xref: /linux/drivers/clk/clk-conf.c (revision 3a39d672e7f48b8d6b91a09afa4b55352773b4b5)
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