162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2014 Samsung Electronics Co., Ltd. 462306a36Sopenharmony_ci * Sylwester Nawrocki <s.nawrocki@samsung.com> 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/clk.h> 862306a36Sopenharmony_ci#include <linux/clk-provider.h> 962306a36Sopenharmony_ci#include <linux/clk/clk-conf.h> 1062306a36Sopenharmony_ci#include <linux/device.h> 1162306a36Sopenharmony_ci#include <linux/of.h> 1262306a36Sopenharmony_ci#include <linux/printk.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_cistatic int __set_clk_parents(struct device_node *node, bool clk_supplier) 1562306a36Sopenharmony_ci{ 1662306a36Sopenharmony_ci struct of_phandle_args clkspec; 1762306a36Sopenharmony_ci int index, rc, num_parents; 1862306a36Sopenharmony_ci struct clk *clk, *pclk; 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci num_parents = of_count_phandle_with_args(node, "assigned-clock-parents", 2162306a36Sopenharmony_ci "#clock-cells"); 2262306a36Sopenharmony_ci if (num_parents == -EINVAL) 2362306a36Sopenharmony_ci pr_err("clk: invalid value of clock-parents property at %pOF\n", 2462306a36Sopenharmony_ci node); 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci for (index = 0; index < num_parents; index++) { 2762306a36Sopenharmony_ci rc = of_parse_phandle_with_args(node, "assigned-clock-parents", 2862306a36Sopenharmony_ci "#clock-cells", index, &clkspec); 2962306a36Sopenharmony_ci if (rc < 0) { 3062306a36Sopenharmony_ci /* skip empty (null) phandles */ 3162306a36Sopenharmony_ci if (rc == -ENOENT) 3262306a36Sopenharmony_ci continue; 3362306a36Sopenharmony_ci else 3462306a36Sopenharmony_ci return rc; 3562306a36Sopenharmony_ci } 3662306a36Sopenharmony_ci if (clkspec.np == node && !clk_supplier) { 3762306a36Sopenharmony_ci of_node_put(clkspec.np); 3862306a36Sopenharmony_ci return 0; 3962306a36Sopenharmony_ci } 4062306a36Sopenharmony_ci pclk = of_clk_get_from_provider(&clkspec); 4162306a36Sopenharmony_ci of_node_put(clkspec.np); 4262306a36Sopenharmony_ci if (IS_ERR(pclk)) { 4362306a36Sopenharmony_ci if (PTR_ERR(pclk) != -EPROBE_DEFER) 4462306a36Sopenharmony_ci pr_warn("clk: couldn't get parent clock %d for %pOF\n", 4562306a36Sopenharmony_ci index, node); 4662306a36Sopenharmony_ci return PTR_ERR(pclk); 4762306a36Sopenharmony_ci } 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci rc = of_parse_phandle_with_args(node, "assigned-clocks", 5062306a36Sopenharmony_ci "#clock-cells", index, &clkspec); 5162306a36Sopenharmony_ci if (rc < 0) 5262306a36Sopenharmony_ci goto err; 5362306a36Sopenharmony_ci if (clkspec.np == node && !clk_supplier) { 5462306a36Sopenharmony_ci of_node_put(clkspec.np); 5562306a36Sopenharmony_ci rc = 0; 5662306a36Sopenharmony_ci goto err; 5762306a36Sopenharmony_ci } 5862306a36Sopenharmony_ci clk = of_clk_get_from_provider(&clkspec); 5962306a36Sopenharmony_ci of_node_put(clkspec.np); 6062306a36Sopenharmony_ci if (IS_ERR(clk)) { 6162306a36Sopenharmony_ci if (PTR_ERR(clk) != -EPROBE_DEFER) 6262306a36Sopenharmony_ci pr_warn("clk: couldn't get assigned clock %d for %pOF\n", 6362306a36Sopenharmony_ci index, node); 6462306a36Sopenharmony_ci rc = PTR_ERR(clk); 6562306a36Sopenharmony_ci goto err; 6662306a36Sopenharmony_ci } 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci rc = clk_set_parent(clk, pclk); 6962306a36Sopenharmony_ci if (rc < 0) 7062306a36Sopenharmony_ci pr_err("clk: failed to reparent %s to %s: %d\n", 7162306a36Sopenharmony_ci __clk_get_name(clk), __clk_get_name(pclk), rc); 7262306a36Sopenharmony_ci clk_put(clk); 7362306a36Sopenharmony_ci clk_put(pclk); 7462306a36Sopenharmony_ci } 7562306a36Sopenharmony_ci return 0; 7662306a36Sopenharmony_cierr: 7762306a36Sopenharmony_ci clk_put(pclk); 7862306a36Sopenharmony_ci return rc; 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistatic int __set_clk_rates(struct device_node *node, bool clk_supplier) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci struct of_phandle_args clkspec; 8462306a36Sopenharmony_ci struct property *prop; 8562306a36Sopenharmony_ci const __be32 *cur; 8662306a36Sopenharmony_ci int rc, index = 0; 8762306a36Sopenharmony_ci struct clk *clk; 8862306a36Sopenharmony_ci u32 rate; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci of_property_for_each_u32(node, "assigned-clock-rates", prop, cur, rate) { 9162306a36Sopenharmony_ci if (rate) { 9262306a36Sopenharmony_ci rc = of_parse_phandle_with_args(node, "assigned-clocks", 9362306a36Sopenharmony_ci "#clock-cells", index, &clkspec); 9462306a36Sopenharmony_ci if (rc < 0) { 9562306a36Sopenharmony_ci /* skip empty (null) phandles */ 9662306a36Sopenharmony_ci if (rc == -ENOENT) 9762306a36Sopenharmony_ci continue; 9862306a36Sopenharmony_ci else 9962306a36Sopenharmony_ci return rc; 10062306a36Sopenharmony_ci } 10162306a36Sopenharmony_ci if (clkspec.np == node && !clk_supplier) { 10262306a36Sopenharmony_ci of_node_put(clkspec.np); 10362306a36Sopenharmony_ci return 0; 10462306a36Sopenharmony_ci } 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci clk = of_clk_get_from_provider(&clkspec); 10762306a36Sopenharmony_ci of_node_put(clkspec.np); 10862306a36Sopenharmony_ci if (IS_ERR(clk)) { 10962306a36Sopenharmony_ci if (PTR_ERR(clk) != -EPROBE_DEFER) 11062306a36Sopenharmony_ci pr_warn("clk: couldn't get clock %d for %pOF\n", 11162306a36Sopenharmony_ci index, node); 11262306a36Sopenharmony_ci return PTR_ERR(clk); 11362306a36Sopenharmony_ci } 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci rc = clk_set_rate(clk, rate); 11662306a36Sopenharmony_ci if (rc < 0) 11762306a36Sopenharmony_ci pr_err("clk: couldn't set %s clk rate to %u (%d), current rate: %lu\n", 11862306a36Sopenharmony_ci __clk_get_name(clk), rate, rc, 11962306a36Sopenharmony_ci clk_get_rate(clk)); 12062306a36Sopenharmony_ci clk_put(clk); 12162306a36Sopenharmony_ci } 12262306a36Sopenharmony_ci index++; 12362306a36Sopenharmony_ci } 12462306a36Sopenharmony_ci return 0; 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci/** 12862306a36Sopenharmony_ci * of_clk_set_defaults() - parse and set assigned clocks configuration 12962306a36Sopenharmony_ci * @node: device node to apply clock settings for 13062306a36Sopenharmony_ci * @clk_supplier: true if clocks supplied by @node should also be considered 13162306a36Sopenharmony_ci * 13262306a36Sopenharmony_ci * This function parses 'assigned-{clocks/clock-parents/clock-rates}' properties 13362306a36Sopenharmony_ci * and sets any specified clock parents and rates. The @clk_supplier argument 13462306a36Sopenharmony_ci * should be set to true if @node may be also a clock supplier of any clock 13562306a36Sopenharmony_ci * listed in its 'assigned-clocks' or 'assigned-clock-parents' properties. 13662306a36Sopenharmony_ci * If @clk_supplier is false the function exits returning 0 as soon as it 13762306a36Sopenharmony_ci * determines the @node is also a supplier of any of the clocks. 13862306a36Sopenharmony_ci */ 13962306a36Sopenharmony_ciint of_clk_set_defaults(struct device_node *node, bool clk_supplier) 14062306a36Sopenharmony_ci{ 14162306a36Sopenharmony_ci int rc; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci if (!node) 14462306a36Sopenharmony_ci return 0; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci rc = __set_clk_parents(node, clk_supplier); 14762306a36Sopenharmony_ci if (rc < 0) 14862306a36Sopenharmony_ci return rc; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci return __set_clk_rates(node, clk_supplier); 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(of_clk_set_defaults); 153