1ebafb63dSStephen Boyd // SPDX-License-Identifier: GPL-2.0
286be408bSSylwester Nawrocki /*
386be408bSSylwester Nawrocki * Copyright (C) 2014 Samsung Electronics Co., Ltd.
486be408bSSylwester Nawrocki * Sylwester Nawrocki <s.nawrocki@samsung.com>
586be408bSSylwester Nawrocki */
686be408bSSylwester Nawrocki
786be408bSSylwester Nawrocki #include <linux/clk.h>
886be408bSSylwester Nawrocki #include <linux/clk-provider.h>
986be408bSSylwester Nawrocki #include <linux/clk/clk-conf.h>
1086be408bSSylwester Nawrocki #include <linux/device.h>
1186be408bSSylwester Nawrocki #include <linux/of.h>
1286be408bSSylwester Nawrocki #include <linux/printk.h>
13*965e0637SPeng Fan #include <linux/slab.h>
1486be408bSSylwester Nawrocki
__set_clk_parents(struct device_node * node,bool clk_supplier)1586be408bSSylwester Nawrocki static int __set_clk_parents(struct device_node *node, bool clk_supplier)
1686be408bSSylwester Nawrocki {
1786be408bSSylwester Nawrocki struct of_phandle_args clkspec;
1886be408bSSylwester Nawrocki int index, rc, num_parents;
1986be408bSSylwester Nawrocki struct clk *clk, *pclk;
2086be408bSSylwester Nawrocki
2186be408bSSylwester Nawrocki num_parents = of_count_phandle_with_args(node, "assigned-clock-parents",
2286be408bSSylwester Nawrocki "#clock-cells");
2386be408bSSylwester Nawrocki if (num_parents == -EINVAL)
2416673931SRob Herring pr_err("clk: invalid value of clock-parents property at %pOF\n",
2516673931SRob Herring node);
2686be408bSSylwester Nawrocki
2786be408bSSylwester Nawrocki for (index = 0; index < num_parents; index++) {
2886be408bSSylwester Nawrocki rc = of_parse_phandle_with_args(node, "assigned-clock-parents",
2986be408bSSylwester Nawrocki "#clock-cells", index, &clkspec);
3086be408bSSylwester Nawrocki if (rc < 0) {
3186be408bSSylwester Nawrocki /* skip empty (null) phandles */
3286be408bSSylwester Nawrocki if (rc == -ENOENT)
3386be408bSSylwester Nawrocki continue;
3486be408bSSylwester Nawrocki else
3586be408bSSylwester Nawrocki return rc;
3686be408bSSylwester Nawrocki }
3727a6e1b0SClément Léger if (clkspec.np == node && !clk_supplier) {
3827a6e1b0SClément Léger of_node_put(clkspec.np);
3986be408bSSylwester Nawrocki return 0;
4027a6e1b0SClément Léger }
41306c342fSStephen Boyd pclk = of_clk_get_from_provider(&clkspec);
4227a6e1b0SClément Léger of_node_put(clkspec.np);
4386be408bSSylwester Nawrocki if (IS_ERR(pclk)) {
44e2d79333SGeert Uytterhoeven if (PTR_ERR(pclk) != -EPROBE_DEFER)
4516673931SRob Herring pr_warn("clk: couldn't get parent clock %d for %pOF\n",
4616673931SRob Herring index, node);
4786be408bSSylwester Nawrocki return PTR_ERR(pclk);
4886be408bSSylwester Nawrocki }
4986be408bSSylwester Nawrocki
5086be408bSSylwester Nawrocki rc = of_parse_phandle_with_args(node, "assigned-clocks",
5186be408bSSylwester Nawrocki "#clock-cells", index, &clkspec);
5286be408bSSylwester Nawrocki if (rc < 0)
5386be408bSSylwester Nawrocki goto err;
5486be408bSSylwester Nawrocki if (clkspec.np == node && !clk_supplier) {
5527a6e1b0SClément Léger of_node_put(clkspec.np);
5686be408bSSylwester Nawrocki rc = 0;
5786be408bSSylwester Nawrocki goto err;
5886be408bSSylwester Nawrocki }
59306c342fSStephen Boyd clk = of_clk_get_from_provider(&clkspec);
6027a6e1b0SClément Léger of_node_put(clkspec.np);
616ba19bf0SDan Carpenter if (IS_ERR(clk)) {
62e2d79333SGeert Uytterhoeven if (PTR_ERR(clk) != -EPROBE_DEFER)
6316673931SRob Herring pr_warn("clk: couldn't get assigned clock %d for %pOF\n",
6416673931SRob Herring index, node);
656ba19bf0SDan Carpenter rc = PTR_ERR(clk);
6686be408bSSylwester Nawrocki goto err;
6786be408bSSylwester Nawrocki }
6886be408bSSylwester Nawrocki
6986be408bSSylwester Nawrocki rc = clk_set_parent(clk, pclk);
7086be408bSSylwester Nawrocki if (rc < 0)
7186be408bSSylwester Nawrocki pr_err("clk: failed to reparent %s to %s: %d\n",
7286be408bSSylwester Nawrocki __clk_get_name(clk), __clk_get_name(pclk), rc);
7386be408bSSylwester Nawrocki clk_put(clk);
7486be408bSSylwester Nawrocki clk_put(pclk);
7586be408bSSylwester Nawrocki }
7686be408bSSylwester Nawrocki return 0;
7786be408bSSylwester Nawrocki err:
7886be408bSSylwester Nawrocki clk_put(pclk);
7986be408bSSylwester Nawrocki return rc;
8086be408bSSylwester Nawrocki }
8186be408bSSylwester Nawrocki
__set_clk_rates(struct device_node * node,bool clk_supplier)8286be408bSSylwester Nawrocki static int __set_clk_rates(struct device_node *node, bool clk_supplier)
8386be408bSSylwester Nawrocki {
8486be408bSSylwester Nawrocki struct of_phandle_args clkspec;
85*965e0637SPeng Fan int rc, count, count_64, index;
8686be408bSSylwester Nawrocki struct clk *clk;
87*965e0637SPeng Fan u64 *rates_64 __free(kfree) = NULL;
88*965e0637SPeng Fan u32 *rates __free(kfree) = NULL;
8986be408bSSylwester Nawrocki
90*965e0637SPeng Fan count = of_property_count_u32_elems(node, "assigned-clock-rates");
91*965e0637SPeng Fan count_64 = of_property_count_u64_elems(node, "assigned-clock-rates-u64");
92*965e0637SPeng Fan if (count_64 > 0) {
93*965e0637SPeng Fan count = count_64;
94*965e0637SPeng Fan rates_64 = kcalloc(count, sizeof(*rates_64), GFP_KERNEL);
95*965e0637SPeng Fan if (!rates_64)
96*965e0637SPeng Fan return -ENOMEM;
97*965e0637SPeng Fan
98*965e0637SPeng Fan rc = of_property_read_u64_array(node,
99*965e0637SPeng Fan "assigned-clock-rates-u64",
100*965e0637SPeng Fan rates_64, count);
101*965e0637SPeng Fan } else if (count > 0) {
102*965e0637SPeng Fan rates = kcalloc(count, sizeof(*rates), GFP_KERNEL);
103*965e0637SPeng Fan if (!rates)
104*965e0637SPeng Fan return -ENOMEM;
105*965e0637SPeng Fan
106*965e0637SPeng Fan rc = of_property_read_u32_array(node, "assigned-clock-rates",
107*965e0637SPeng Fan rates, count);
108*965e0637SPeng Fan } else {
109*965e0637SPeng Fan return 0;
110*965e0637SPeng Fan }
111*965e0637SPeng Fan
112*965e0637SPeng Fan if (rc)
113*965e0637SPeng Fan return rc;
114*965e0637SPeng Fan
115*965e0637SPeng Fan for (index = 0; index < count; index++) {
116*965e0637SPeng Fan unsigned long rate;
117*965e0637SPeng Fan
118*965e0637SPeng Fan if (rates_64)
119*965e0637SPeng Fan rate = rates_64[index];
120*965e0637SPeng Fan else
121*965e0637SPeng Fan rate = rates[index];
122*965e0637SPeng Fan
12386be408bSSylwester Nawrocki if (rate) {
12486be408bSSylwester Nawrocki rc = of_parse_phandle_with_args(node, "assigned-clocks",
12586be408bSSylwester Nawrocki "#clock-cells", index, &clkspec);
12686be408bSSylwester Nawrocki if (rc < 0) {
12786be408bSSylwester Nawrocki /* skip empty (null) phandles */
12886be408bSSylwester Nawrocki if (rc == -ENOENT)
12986be408bSSylwester Nawrocki continue;
13086be408bSSylwester Nawrocki else
13186be408bSSylwester Nawrocki return rc;
13286be408bSSylwester Nawrocki }
13327a6e1b0SClément Léger if (clkspec.np == node && !clk_supplier) {
13427a6e1b0SClément Léger of_node_put(clkspec.np);
13586be408bSSylwester Nawrocki return 0;
13627a6e1b0SClément Léger }
13786be408bSSylwester Nawrocki
138306c342fSStephen Boyd clk = of_clk_get_from_provider(&clkspec);
13927a6e1b0SClément Léger of_node_put(clkspec.np);
14086be408bSSylwester Nawrocki if (IS_ERR(clk)) {
141e2d79333SGeert Uytterhoeven if (PTR_ERR(clk) != -EPROBE_DEFER)
14216673931SRob Herring pr_warn("clk: couldn't get clock %d for %pOF\n",
14316673931SRob Herring index, node);
14486be408bSSylwester Nawrocki return PTR_ERR(clk);
14586be408bSSylwester Nawrocki }
14686be408bSSylwester Nawrocki
14786be408bSSylwester Nawrocki rc = clk_set_rate(clk, rate);
14886be408bSSylwester Nawrocki if (rc < 0)
149*965e0637SPeng Fan pr_err("clk: couldn't set %s clk rate to %lu (%d), current rate: %lu\n",
1502885c3b2SChanwoo Choi __clk_get_name(clk), rate, rc,
1512885c3b2SChanwoo Choi clk_get_rate(clk));
15286be408bSSylwester Nawrocki clk_put(clk);
15386be408bSSylwester Nawrocki }
15486be408bSSylwester Nawrocki }
15586be408bSSylwester Nawrocki return 0;
15686be408bSSylwester Nawrocki }
15786be408bSSylwester Nawrocki
15886be408bSSylwester Nawrocki /**
15986be408bSSylwester Nawrocki * of_clk_set_defaults() - parse and set assigned clocks configuration
16086be408bSSylwester Nawrocki * @node: device node to apply clock settings for
16186be408bSSylwester Nawrocki * @clk_supplier: true if clocks supplied by @node should also be considered
16286be408bSSylwester Nawrocki *
16386be408bSSylwester Nawrocki * This function parses 'assigned-{clocks/clock-parents/clock-rates}' properties
16486be408bSSylwester Nawrocki * and sets any specified clock parents and rates. The @clk_supplier argument
16586be408bSSylwester Nawrocki * should be set to true if @node may be also a clock supplier of any clock
16686be408bSSylwester Nawrocki * listed in its 'assigned-clocks' or 'assigned-clock-parents' properties.
167004cbb47SShailendra Verma * If @clk_supplier is false the function exits returning 0 as soon as it
16886be408bSSylwester Nawrocki * determines the @node is also a supplier of any of the clocks.
16986be408bSSylwester Nawrocki */
of_clk_set_defaults(struct device_node * node,bool clk_supplier)17086be408bSSylwester Nawrocki int of_clk_set_defaults(struct device_node *node, bool clk_supplier)
17186be408bSSylwester Nawrocki {
17286be408bSSylwester Nawrocki int rc;
17386be408bSSylwester Nawrocki
17486be408bSSylwester Nawrocki if (!node)
17586be408bSSylwester Nawrocki return 0;
17686be408bSSylwester Nawrocki
17786be408bSSylwester Nawrocki rc = __set_clk_parents(node, clk_supplier);
17886be408bSSylwester Nawrocki if (rc < 0)
17986be408bSSylwester Nawrocki return rc;
18086be408bSSylwester Nawrocki
18186be408bSSylwester Nawrocki return __set_clk_rates(node, clk_supplier);
18286be408bSSylwester Nawrocki }
183b11a6facSSylwester Nawrocki EXPORT_SYMBOL_GPL(of_clk_set_defaults);
184