xref: /linux/drivers/clk/clk-conf.c (revision 2885c3b2a3da7902314fa1d0a5b603eeea7c7302)
186be408bSSylwester Nawrocki /*
286be408bSSylwester Nawrocki  * Copyright (C) 2014 Samsung Electronics Co., Ltd.
386be408bSSylwester Nawrocki  * Sylwester Nawrocki <s.nawrocki@samsung.com>
486be408bSSylwester Nawrocki  *
586be408bSSylwester Nawrocki  * This program is free software; you can redistribute it and/or modify
686be408bSSylwester Nawrocki  * it under the terms of the GNU General Public License version 2 as
786be408bSSylwester Nawrocki  * published by the Free Software Foundation.
886be408bSSylwester Nawrocki  */
986be408bSSylwester Nawrocki 
1086be408bSSylwester Nawrocki #include <linux/clk.h>
1186be408bSSylwester Nawrocki #include <linux/clk-provider.h>
1286be408bSSylwester Nawrocki #include <linux/clk/clk-conf.h>
1386be408bSSylwester Nawrocki #include <linux/device.h>
1486be408bSSylwester Nawrocki #include <linux/of.h>
1586be408bSSylwester Nawrocki #include <linux/printk.h>
1686be408bSSylwester Nawrocki 
1786be408bSSylwester Nawrocki static int __set_clk_parents(struct device_node *node, bool clk_supplier)
1886be408bSSylwester Nawrocki {
1986be408bSSylwester Nawrocki 	struct of_phandle_args clkspec;
2086be408bSSylwester Nawrocki 	int index, rc, num_parents;
2186be408bSSylwester Nawrocki 	struct clk *clk, *pclk;
2286be408bSSylwester Nawrocki 
2386be408bSSylwester Nawrocki 	num_parents = of_count_phandle_with_args(node, "assigned-clock-parents",
2486be408bSSylwester Nawrocki 						 "#clock-cells");
2586be408bSSylwester Nawrocki 	if (num_parents == -EINVAL)
2686be408bSSylwester Nawrocki 		pr_err("clk: invalid value of clock-parents property at %s\n",
2786be408bSSylwester Nawrocki 		       node->full_name);
2886be408bSSylwester Nawrocki 
2986be408bSSylwester Nawrocki 	for (index = 0; index < num_parents; index++) {
3086be408bSSylwester Nawrocki 		rc = of_parse_phandle_with_args(node, "assigned-clock-parents",
3186be408bSSylwester Nawrocki 					"#clock-cells",	index, &clkspec);
3286be408bSSylwester Nawrocki 		if (rc < 0) {
3386be408bSSylwester Nawrocki 			/* skip empty (null) phandles */
3486be408bSSylwester Nawrocki 			if (rc == -ENOENT)
3586be408bSSylwester Nawrocki 				continue;
3686be408bSSylwester Nawrocki 			else
3786be408bSSylwester Nawrocki 				return rc;
3886be408bSSylwester Nawrocki 		}
3986be408bSSylwester Nawrocki 		if (clkspec.np == node && !clk_supplier)
4086be408bSSylwester Nawrocki 			return 0;
41306c342fSStephen Boyd 		pclk = of_clk_get_from_provider(&clkspec);
4286be408bSSylwester Nawrocki 		if (IS_ERR(pclk)) {
4386be408bSSylwester Nawrocki 			pr_warn("clk: couldn't get parent clock %d for %s\n",
4486be408bSSylwester Nawrocki 				index, node->full_name);
4586be408bSSylwester Nawrocki 			return PTR_ERR(pclk);
4686be408bSSylwester Nawrocki 		}
4786be408bSSylwester Nawrocki 
4886be408bSSylwester Nawrocki 		rc = of_parse_phandle_with_args(node, "assigned-clocks",
4986be408bSSylwester Nawrocki 					"#clock-cells", index, &clkspec);
5086be408bSSylwester Nawrocki 		if (rc < 0)
5186be408bSSylwester Nawrocki 			goto err;
5286be408bSSylwester Nawrocki 		if (clkspec.np == node && !clk_supplier) {
5386be408bSSylwester Nawrocki 			rc = 0;
5486be408bSSylwester Nawrocki 			goto err;
5586be408bSSylwester Nawrocki 		}
56306c342fSStephen Boyd 		clk = of_clk_get_from_provider(&clkspec);
576ba19bf0SDan Carpenter 		if (IS_ERR(clk)) {
5886be408bSSylwester Nawrocki 			pr_warn("clk: couldn't get parent clock %d for %s\n",
5986be408bSSylwester Nawrocki 				index, node->full_name);
606ba19bf0SDan Carpenter 			rc = PTR_ERR(clk);
6186be408bSSylwester Nawrocki 			goto err;
6286be408bSSylwester Nawrocki 		}
6386be408bSSylwester Nawrocki 
6486be408bSSylwester Nawrocki 		rc = clk_set_parent(clk, pclk);
6586be408bSSylwester Nawrocki 		if (rc < 0)
6686be408bSSylwester Nawrocki 			pr_err("clk: failed to reparent %s to %s: %d\n",
6786be408bSSylwester Nawrocki 			       __clk_get_name(clk), __clk_get_name(pclk), rc);
6886be408bSSylwester Nawrocki 		clk_put(clk);
6986be408bSSylwester Nawrocki 		clk_put(pclk);
7086be408bSSylwester Nawrocki 	}
7186be408bSSylwester Nawrocki 	return 0;
7286be408bSSylwester Nawrocki err:
7386be408bSSylwester Nawrocki 	clk_put(pclk);
7486be408bSSylwester Nawrocki 	return rc;
7586be408bSSylwester Nawrocki }
7686be408bSSylwester Nawrocki 
7786be408bSSylwester Nawrocki static int __set_clk_rates(struct device_node *node, bool clk_supplier)
7886be408bSSylwester Nawrocki {
7986be408bSSylwester Nawrocki 	struct of_phandle_args clkspec;
8086be408bSSylwester Nawrocki 	struct property	*prop;
8186be408bSSylwester Nawrocki 	const __be32 *cur;
8286be408bSSylwester Nawrocki 	int rc, index = 0;
8386be408bSSylwester Nawrocki 	struct clk *clk;
8486be408bSSylwester Nawrocki 	u32 rate;
8586be408bSSylwester Nawrocki 
8686be408bSSylwester Nawrocki 	of_property_for_each_u32(node, "assigned-clock-rates", prop, cur, rate) {
8786be408bSSylwester Nawrocki 		if (rate) {
8886be408bSSylwester Nawrocki 			rc = of_parse_phandle_with_args(node, "assigned-clocks",
8986be408bSSylwester Nawrocki 					"#clock-cells",	index, &clkspec);
9086be408bSSylwester Nawrocki 			if (rc < 0) {
9186be408bSSylwester Nawrocki 				/* skip empty (null) phandles */
9286be408bSSylwester Nawrocki 				if (rc == -ENOENT)
9386be408bSSylwester Nawrocki 					continue;
9486be408bSSylwester Nawrocki 				else
9586be408bSSylwester Nawrocki 					return rc;
9686be408bSSylwester Nawrocki 			}
9786be408bSSylwester Nawrocki 			if (clkspec.np == node && !clk_supplier)
9886be408bSSylwester Nawrocki 				return 0;
9986be408bSSylwester Nawrocki 
100306c342fSStephen Boyd 			clk = of_clk_get_from_provider(&clkspec);
10186be408bSSylwester Nawrocki 			if (IS_ERR(clk)) {
10286be408bSSylwester Nawrocki 				pr_warn("clk: couldn't get clock %d for %s\n",
10386be408bSSylwester Nawrocki 					index, node->full_name);
10486be408bSSylwester Nawrocki 				return PTR_ERR(clk);
10586be408bSSylwester Nawrocki 			}
10686be408bSSylwester Nawrocki 
10786be408bSSylwester Nawrocki 			rc = clk_set_rate(clk, rate);
10886be408bSSylwester Nawrocki 			if (rc < 0)
109*2885c3b2SChanwoo Choi 				pr_err("clk: couldn't set %s clk rate to %d (%d), current rate: %ld\n",
110*2885c3b2SChanwoo Choi 				       __clk_get_name(clk), rate, rc,
111*2885c3b2SChanwoo Choi 				       clk_get_rate(clk));
11286be408bSSylwester Nawrocki 			clk_put(clk);
11386be408bSSylwester Nawrocki 		}
11486be408bSSylwester Nawrocki 		index++;
11586be408bSSylwester Nawrocki 	}
11686be408bSSylwester Nawrocki 	return 0;
11786be408bSSylwester Nawrocki }
11886be408bSSylwester Nawrocki 
11986be408bSSylwester Nawrocki /**
12086be408bSSylwester Nawrocki  * of_clk_set_defaults() - parse and set assigned clocks configuration
12186be408bSSylwester Nawrocki  * @node: device node to apply clock settings for
12286be408bSSylwester Nawrocki  * @clk_supplier: true if clocks supplied by @node should also be considered
12386be408bSSylwester Nawrocki  *
12486be408bSSylwester Nawrocki  * This function parses 'assigned-{clocks/clock-parents/clock-rates}' properties
12586be408bSSylwester Nawrocki  * and sets any specified clock parents and rates. The @clk_supplier argument
12686be408bSSylwester Nawrocki  * should be set to true if @node may be also a clock supplier of any clock
12786be408bSSylwester Nawrocki  * listed in its 'assigned-clocks' or 'assigned-clock-parents' properties.
12886be408bSSylwester Nawrocki  * If @clk_supplier is false the function exits returnning 0 as soon as it
12986be408bSSylwester Nawrocki  * determines the @node is also a supplier of any of the clocks.
13086be408bSSylwester Nawrocki  */
13186be408bSSylwester Nawrocki int of_clk_set_defaults(struct device_node *node, bool clk_supplier)
13286be408bSSylwester Nawrocki {
13386be408bSSylwester Nawrocki 	int rc;
13486be408bSSylwester Nawrocki 
13586be408bSSylwester Nawrocki 	if (!node)
13686be408bSSylwester Nawrocki 		return 0;
13786be408bSSylwester Nawrocki 
13886be408bSSylwester Nawrocki 	rc = __set_clk_parents(node, clk_supplier);
13986be408bSSylwester Nawrocki 	if (rc < 0)
14086be408bSSylwester Nawrocki 		return rc;
14186be408bSSylwester Nawrocki 
14286be408bSSylwester Nawrocki 	return __set_clk_rates(node, clk_supplier);
14386be408bSSylwester Nawrocki }
144b11a6facSSylwester Nawrocki EXPORT_SYMBOL_GPL(of_clk_set_defaults);
145