184 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright (C) 2014 Samsung Electronics Co., Ltd.4 * Sylwester Nawrocki <s.nawrocki@samsung.com>5 */6 7#include <linux/clk.h>8#include <linux/clk-provider.h>9#include <linux/clk/clk-conf.h>10#include <linux/device.h>11#include <linux/of.h>12#include <linux/printk.h>13#include <linux/slab.h>14 15static int __set_clk_parents(struct device_node *node, bool clk_supplier)16{17 struct of_phandle_args clkspec;18 int index, rc, num_parents;19 struct clk *clk, *pclk;20 21 num_parents = of_count_phandle_with_args(node, "assigned-clock-parents",22 "#clock-cells");23 if (num_parents == -EINVAL)24 pr_err("clk: invalid value of clock-parents property at %pOF\n",25 node);26 27 for (index = 0; index < num_parents; index++) {28 rc = of_parse_phandle_with_args(node, "assigned-clock-parents",29 "#clock-cells", index, &clkspec);30 if (rc < 0) {31 /* skip empty (null) phandles */32 if (rc == -ENOENT)33 continue;34 else35 return rc;36 }37 if (clkspec.np == node && !clk_supplier) {38 of_node_put(clkspec.np);39 return 0;40 }41 pclk = of_clk_get_from_provider(&clkspec);42 of_node_put(clkspec.np);43 if (IS_ERR(pclk)) {44 if (PTR_ERR(pclk) != -EPROBE_DEFER)45 pr_warn("clk: couldn't get parent clock %d for %pOF\n",46 index, node);47 return PTR_ERR(pclk);48 }49 50 rc = of_parse_phandle_with_args(node, "assigned-clocks",51 "#clock-cells", index, &clkspec);52 if (rc < 0)53 goto err;54 if (clkspec.np == node && !clk_supplier) {55 of_node_put(clkspec.np);56 rc = 0;57 goto err;58 }59 clk = of_clk_get_from_provider(&clkspec);60 of_node_put(clkspec.np);61 if (IS_ERR(clk)) {62 if (PTR_ERR(clk) != -EPROBE_DEFER)63 pr_warn("clk: couldn't get assigned clock %d for %pOF\n",64 index, node);65 rc = PTR_ERR(clk);66 goto err;67 }68 69 rc = clk_set_parent(clk, pclk);70 if (rc < 0)71 pr_err("clk: failed to reparent %s to %s: %d\n",72 __clk_get_name(clk), __clk_get_name(pclk), rc);73 clk_put(clk);74 clk_put(pclk);75 }76 return 0;77err:78 clk_put(pclk);79 return rc;80}81 82static int __set_clk_rates(struct device_node *node, bool clk_supplier)83{84 struct of_phandle_args clkspec;85 int rc, count, count_64, index;86 struct clk *clk;87 u64 *rates_64 __free(kfree) = NULL;88 u32 *rates __free(kfree) = NULL;89 90 count = of_property_count_u32_elems(node, "assigned-clock-rates");91 count_64 = of_property_count_u64_elems(node, "assigned-clock-rates-u64");92 if (count_64 > 0) {93 count = count_64;94 rates_64 = kcalloc(count, sizeof(*rates_64), GFP_KERNEL);95 if (!rates_64)96 return -ENOMEM;97 98 rc = of_property_read_u64_array(node,99 "assigned-clock-rates-u64",100 rates_64, count);101 } else if (count > 0) {102 rates = kcalloc(count, sizeof(*rates), GFP_KERNEL);103 if (!rates)104 return -ENOMEM;105 106 rc = of_property_read_u32_array(node, "assigned-clock-rates",107 rates, count);108 } else {109 return 0;110 }111 112 if (rc)113 return rc;114 115 for (index = 0; index < count; index++) {116 unsigned long rate;117 118 if (rates_64)119 rate = rates_64[index];120 else121 rate = rates[index];122 123 if (rate) {124 rc = of_parse_phandle_with_args(node, "assigned-clocks",125 "#clock-cells", index, &clkspec);126 if (rc < 0) {127 /* skip empty (null) phandles */128 if (rc == -ENOENT)129 continue;130 else131 return rc;132 }133 if (clkspec.np == node && !clk_supplier) {134 of_node_put(clkspec.np);135 return 0;136 }137 138 clk = of_clk_get_from_provider(&clkspec);139 of_node_put(clkspec.np);140 if (IS_ERR(clk)) {141 if (PTR_ERR(clk) != -EPROBE_DEFER)142 pr_warn("clk: couldn't get clock %d for %pOF\n",143 index, node);144 return PTR_ERR(clk);145 }146 147 rc = clk_set_rate(clk, rate);148 if (rc < 0)149 pr_err("clk: couldn't set %s clk rate to %lu (%d), current rate: %lu\n",150 __clk_get_name(clk), rate, rc,151 clk_get_rate(clk));152 clk_put(clk);153 }154 }155 return 0;156}157 158/**159 * of_clk_set_defaults() - parse and set assigned clocks configuration160 * @node: device node to apply clock settings for161 * @clk_supplier: true if clocks supplied by @node should also be considered162 *163 * This function parses 'assigned-{clocks/clock-parents/clock-rates}' properties164 * and sets any specified clock parents and rates. The @clk_supplier argument165 * should be set to true if @node may be also a clock supplier of any clock166 * listed in its 'assigned-clocks' or 'assigned-clock-parents' properties.167 * If @clk_supplier is false the function exits returning 0 as soon as it168 * determines the @node is also a supplier of any of the clocks.169 */170int of_clk_set_defaults(struct device_node *node, bool clk_supplier)171{172 int rc;173 174 if (!node)175 return 0;176 177 rc = __set_clk_parents(node, clk_supplier);178 if (rc < 0)179 return rc;180 181 return __set_clk_rates(node, clk_supplier);182}183EXPORT_SYMBOL_GPL(of_clk_set_defaults);184