xref: /netbsd-src/sys/dev/fdt/fdt_clock.c (revision 47f97eb335756fcafa173cde555234581ae52810)
1*47f97eb3Sjmcneill /* $NetBSD: fdt_clock.c,v 1.10 2019/11/09 23:28:26 jmcneill Exp $ */
2fda513fdSjmcneill 
3fda513fdSjmcneill /*-
4fda513fdSjmcneill  * Copyright (c) 2015 Jared D. McNeill <jmcneill@invisible.ca>
5fda513fdSjmcneill  * All rights reserved.
6fda513fdSjmcneill  *
7fda513fdSjmcneill  * Redistribution and use in source and binary forms, with or without
8fda513fdSjmcneill  * modification, are permitted provided that the following conditions
9fda513fdSjmcneill  * are met:
10fda513fdSjmcneill  * 1. Redistributions of source code must retain the above copyright
11fda513fdSjmcneill  *    notice, this list of conditions and the following disclaimer.
12fda513fdSjmcneill  * 2. Redistributions in binary form must reproduce the above copyright
13fda513fdSjmcneill  *    notice, this list of conditions and the following disclaimer in the
14fda513fdSjmcneill  *    documentation and/or other materials provided with the distribution.
15fda513fdSjmcneill  *
16fda513fdSjmcneill  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17fda513fdSjmcneill  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18fda513fdSjmcneill  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19fda513fdSjmcneill  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20fda513fdSjmcneill  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21fda513fdSjmcneill  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22fda513fdSjmcneill  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23fda513fdSjmcneill  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24fda513fdSjmcneill  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25fda513fdSjmcneill  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26fda513fdSjmcneill  * SUCH DAMAGE.
27fda513fdSjmcneill  */
28fda513fdSjmcneill 
29fda513fdSjmcneill #include <sys/cdefs.h>
30*47f97eb3Sjmcneill __KERNEL_RCSID(0, "$NetBSD: fdt_clock.c,v 1.10 2019/11/09 23:28:26 jmcneill Exp $");
31fda513fdSjmcneill 
32fda513fdSjmcneill #include <sys/param.h>
33fda513fdSjmcneill #include <sys/bus.h>
34fda513fdSjmcneill #include <sys/kmem.h>
35233c5d85Sjmcneill #include <sys/queue.h>
36fda513fdSjmcneill 
37fda513fdSjmcneill #include <libfdt.h>
38fda513fdSjmcneill #include <dev/fdt/fdtvar.h>
39fda513fdSjmcneill 
408baf3a21Sjmcneill #include <dev/clk/clk_backend.h>
418baf3a21Sjmcneill 
42fda513fdSjmcneill struct fdtbus_clock_controller {
43fda513fdSjmcneill 	device_t cc_dev;
44fda513fdSjmcneill 	int cc_phandle;
45fda513fdSjmcneill 	const struct fdtbus_clock_controller_func *cc_funcs;
46fda513fdSjmcneill 
47233c5d85Sjmcneill 	LIST_ENTRY(fdtbus_clock_controller) cc_next;
48fda513fdSjmcneill };
49fda513fdSjmcneill 
50233c5d85Sjmcneill static LIST_HEAD(, fdtbus_clock_controller) fdtbus_clock_controllers =
51233c5d85Sjmcneill     LIST_HEAD_INITIALIZER(fdtbus_clock_controller);
52fda513fdSjmcneill 
53fda513fdSjmcneill int
fdtbus_register_clock_controller(device_t dev,int phandle,const struct fdtbus_clock_controller_func * funcs)54fda513fdSjmcneill fdtbus_register_clock_controller(device_t dev, int phandle,
55fda513fdSjmcneill     const struct fdtbus_clock_controller_func *funcs)
56fda513fdSjmcneill {
57fda513fdSjmcneill 	struct fdtbus_clock_controller *cc;
58fda513fdSjmcneill 
59fda513fdSjmcneill 	cc = kmem_alloc(sizeof(*cc), KM_SLEEP);
60fda513fdSjmcneill 	cc->cc_dev = dev;
61fda513fdSjmcneill 	cc->cc_phandle = phandle;
62fda513fdSjmcneill 	cc->cc_funcs = funcs;
63fda513fdSjmcneill 
64233c5d85Sjmcneill 	LIST_INSERT_HEAD(&fdtbus_clock_controllers, cc, cc_next);
65fda513fdSjmcneill 
668baf3a21Sjmcneill 	fdtbus_clock_assign(phandle);
678baf3a21Sjmcneill 
68fda513fdSjmcneill 	return 0;
69fda513fdSjmcneill }
70fda513fdSjmcneill 
71fda513fdSjmcneill static struct fdtbus_clock_controller *
fdtbus_get_clock_controller(int phandle)72fda513fdSjmcneill fdtbus_get_clock_controller(int phandle)
73fda513fdSjmcneill {
74fda513fdSjmcneill 	struct fdtbus_clock_controller *cc;
75fda513fdSjmcneill 
76233c5d85Sjmcneill 	LIST_FOREACH(cc, &fdtbus_clock_controllers, cc_next) {
77233c5d85Sjmcneill 		if (cc->cc_phandle == phandle)
78fda513fdSjmcneill 			return cc;
79fda513fdSjmcneill 	}
80fda513fdSjmcneill 
81fda513fdSjmcneill 	return NULL;
82fda513fdSjmcneill }
83fda513fdSjmcneill 
848baf3a21Sjmcneill static struct clk *
fdtbus_clock_get_index_prop(int phandle,u_int index,const char * prop)858baf3a21Sjmcneill fdtbus_clock_get_index_prop(int phandle, u_int index, const char *prop)
86fda513fdSjmcneill {
87fda513fdSjmcneill 	struct fdtbus_clock_controller *cc;
88fda513fdSjmcneill 	struct clk *clk = NULL;
898baf3a21Sjmcneill 	const u_int *p;
90fda513fdSjmcneill 	u_int n, clock_cells;
91fda513fdSjmcneill 	int len, resid;
92fda513fdSjmcneill 
938baf3a21Sjmcneill 	p = fdtbus_get_prop(phandle, prop, &len);
948baf3a21Sjmcneill 	if (p == NULL)
95fda513fdSjmcneill 		return NULL;
96fda513fdSjmcneill 
97fda513fdSjmcneill 	for (n = 0, resid = len; resid > 0; n++) {
98fda513fdSjmcneill 		const int cc_phandle =
99fda513fdSjmcneill 		    fdtbus_get_phandle_from_native(be32toh(p[0]));
100fda513fdSjmcneill 		if (of_getprop_uint32(cc_phandle, "#clock-cells", &clock_cells))
101fda513fdSjmcneill 			break;
102fda513fdSjmcneill 		if (n == index) {
103fda513fdSjmcneill 			cc = fdtbus_get_clock_controller(cc_phandle);
104fda513fdSjmcneill 			if (cc == NULL)
1058baf3a21Sjmcneill 				break;
10651b425efSaymeric 			clk = cc->cc_funcs->decode(cc->cc_dev, cc_phandle,
107fda513fdSjmcneill 			    clock_cells > 0 ? &p[1] : NULL, clock_cells * 4);
108fda513fdSjmcneill 			break;
109fda513fdSjmcneill 		}
110fda513fdSjmcneill 		resid -= (clock_cells + 1) * 4;
111fda513fdSjmcneill 		p += clock_cells + 1;
112fda513fdSjmcneill 	}
113fda513fdSjmcneill 
114fda513fdSjmcneill 	return clk;
115fda513fdSjmcneill }
116fda513fdSjmcneill 
117fda513fdSjmcneill struct clk *
fdtbus_clock_get_index(int phandle,u_int index)1188baf3a21Sjmcneill fdtbus_clock_get_index(int phandle, u_int index)
1198baf3a21Sjmcneill {
1208baf3a21Sjmcneill 	return fdtbus_clock_get_index_prop(phandle, index, "clocks");
1218baf3a21Sjmcneill }
1228baf3a21Sjmcneill 
1238baf3a21Sjmcneill static struct clk *
fdtbus_clock_get_prop(int phandle,const char * clkname,const char * prop)1248baf3a21Sjmcneill fdtbus_clock_get_prop(int phandle, const char *clkname, const char *prop)
125fda513fdSjmcneill {
126fda513fdSjmcneill 	u_int index;
12721aceb10Sjakllsch 	int err;
128fda513fdSjmcneill 
12921aceb10Sjakllsch 	err = fdtbus_get_index(phandle, prop, clkname, &index);
13021aceb10Sjakllsch 	if (err != 0)
131fda513fdSjmcneill 		return NULL;
132fda513fdSjmcneill 
13321aceb10Sjakllsch 	return fdtbus_clock_get_index(phandle, index);
134fda513fdSjmcneill }
1359a2e3e05Sjmcneill 
1368fc8ad9aSjmcneill u_int
fdtbus_clock_count(int phandle,const char * prop)1378fc8ad9aSjmcneill fdtbus_clock_count(int phandle, const char *prop)
1388baf3a21Sjmcneill {
1398baf3a21Sjmcneill 	u_int n, clock_cells;
1408baf3a21Sjmcneill 	int len, resid;
1418baf3a21Sjmcneill 
1428baf3a21Sjmcneill 	const u_int *p = fdtbus_get_prop(phandle, prop, &len);
1438baf3a21Sjmcneill 	if (p == NULL)
1448baf3a21Sjmcneill 		return 0;
1458baf3a21Sjmcneill 
1468baf3a21Sjmcneill 	for (n = 0, resid = len; resid > 0; n++) {
1478baf3a21Sjmcneill 		const int cc_phandle =
1488baf3a21Sjmcneill 		    fdtbus_get_phandle_from_native(be32toh(p[0]));
1498baf3a21Sjmcneill 		if (of_getprop_uint32(cc_phandle, "#clock-cells", &clock_cells))
1508baf3a21Sjmcneill 			break;
1518baf3a21Sjmcneill 		resid -= (clock_cells + 1) * 4;
1528baf3a21Sjmcneill 		p += clock_cells + 1;
1538baf3a21Sjmcneill 	}
1548baf3a21Sjmcneill 
1558baf3a21Sjmcneill 	return n;
1568baf3a21Sjmcneill }
1578baf3a21Sjmcneill 
1588baf3a21Sjmcneill struct clk *
fdtbus_clock_get(int phandle,const char * clkname)1598baf3a21Sjmcneill fdtbus_clock_get(int phandle, const char *clkname)
1608baf3a21Sjmcneill {
1618baf3a21Sjmcneill 	return fdtbus_clock_get_prop(phandle, clkname, "clock-names");
1628baf3a21Sjmcneill }
1638baf3a21Sjmcneill 
164*47f97eb3Sjmcneill int
fdtbus_clock_enable(int phandle,const char * clkname,bool required)165*47f97eb3Sjmcneill fdtbus_clock_enable(int phandle, const char *clkname, bool required)
166*47f97eb3Sjmcneill {
167*47f97eb3Sjmcneill 	struct clk *clk;
168*47f97eb3Sjmcneill 
169*47f97eb3Sjmcneill 	clk = fdtbus_clock_get(phandle, clkname);
170*47f97eb3Sjmcneill 	if (clk == NULL)
171*47f97eb3Sjmcneill 		return required ? ENOENT : 0;
172*47f97eb3Sjmcneill 
173*47f97eb3Sjmcneill 	return clk_enable(clk);
174*47f97eb3Sjmcneill }
175*47f97eb3Sjmcneill 
176*47f97eb3Sjmcneill int
fdtbus_clock_enable_index(int phandle,u_int index,bool required)177*47f97eb3Sjmcneill fdtbus_clock_enable_index(int phandle, u_int index, bool required)
178*47f97eb3Sjmcneill {
179*47f97eb3Sjmcneill 	struct clk *clk;
180*47f97eb3Sjmcneill 
181*47f97eb3Sjmcneill 	clk = fdtbus_clock_get_index(phandle, index);
182*47f97eb3Sjmcneill 	if (clk == NULL)
183*47f97eb3Sjmcneill 		return required ? ENOENT : 0;
184*47f97eb3Sjmcneill 
185*47f97eb3Sjmcneill 	return clk_enable(clk);
186*47f97eb3Sjmcneill }
187*47f97eb3Sjmcneill 
1889a2e3e05Sjmcneill /*
1899a2e3e05Sjmcneill  * Search the DT for a clock by "clock-output-names" property.
1909a2e3e05Sjmcneill  *
1919a2e3e05Sjmcneill  * This should only be used by clk backends. Not for use by ordinary
1929a2e3e05Sjmcneill  * clock consumers!
1939a2e3e05Sjmcneill  */
1949a2e3e05Sjmcneill struct clk *
fdtbus_clock_byname(const char * clkname)1959a2e3e05Sjmcneill fdtbus_clock_byname(const char *clkname)
1969a2e3e05Sjmcneill {
1979a2e3e05Sjmcneill 	struct fdtbus_clock_controller *cc;
19821aceb10Sjakllsch 	u_int index, clock_cells;
19921aceb10Sjakllsch 	int err;
2009a2e3e05Sjmcneill 
201233c5d85Sjmcneill 	LIST_FOREACH(cc, &fdtbus_clock_controllers, cc_next) {
20221aceb10Sjakllsch 		err = fdtbus_get_index(cc->cc_phandle, "clock-output-names", clkname, &index);
20321aceb10Sjakllsch 		if (err != 0)
2049a2e3e05Sjmcneill 			continue;
2059a2e3e05Sjmcneill 		if (of_getprop_uint32(cc->cc_phandle, "#clock-cells", &clock_cells))
20621aceb10Sjakllsch 			continue;
2079a2e3e05Sjmcneill 		const u_int index_raw = htobe32(index);
2089a2e3e05Sjmcneill 		return cc->cc_funcs->decode(cc->cc_dev,
20951b425efSaymeric 		    cc->cc_phandle,
2109a2e3e05Sjmcneill 		    clock_cells > 0 ? &index_raw : NULL,
2119a2e3e05Sjmcneill 		    clock_cells > 0 ? 4 : 0);
2129a2e3e05Sjmcneill 	}
2139a2e3e05Sjmcneill 
2149a2e3e05Sjmcneill 	return NULL;
2159a2e3e05Sjmcneill }
2168baf3a21Sjmcneill 
2178baf3a21Sjmcneill /*
2188baf3a21Sjmcneill  * Apply assigned clock parents and rates.
2198baf3a21Sjmcneill  *
2208baf3a21Sjmcneill  * This is automatically called by fdtbus_register_clock_controller, so clock
2218baf3a21Sjmcneill  * drivers likely don't need to call this directly.
2228baf3a21Sjmcneill  */
2238baf3a21Sjmcneill void
fdtbus_clock_assign(int phandle)2248baf3a21Sjmcneill fdtbus_clock_assign(int phandle)
2258baf3a21Sjmcneill {
2268baf3a21Sjmcneill 	u_int index, rates_len;
2278baf3a21Sjmcneill 	struct clk *clk, *clk_parent;
2288baf3a21Sjmcneill 	int error;
2298baf3a21Sjmcneill 
2308baf3a21Sjmcneill 	const u_int *rates = fdtbus_get_prop(phandle, "assigned-clock-rates", &rates_len);
2318baf3a21Sjmcneill 	if (rates == NULL)
2328baf3a21Sjmcneill 		rates_len = 0;
2338baf3a21Sjmcneill 
2348fc8ad9aSjmcneill 	const u_int nclocks = fdtbus_clock_count(phandle, "assigned-clocks");
2358fc8ad9aSjmcneill 	const u_int nparents = fdtbus_clock_count(phandle, "assigned-clock-parents");
2368be04e5fSjmcneill 	const u_int nrates = rates_len / sizeof(*rates);
2378baf3a21Sjmcneill 
2388baf3a21Sjmcneill 	for (index = 0; index < nclocks; index++) {
2398baf3a21Sjmcneill 		clk = fdtbus_clock_get_index_prop(phandle, index, "assigned-clocks");
2408baf3a21Sjmcneill 		if (clk == NULL) {
2418baf3a21Sjmcneill 			aprint_debug("clk: assigned clock (%u) not found, skipping...\n", index);
2428baf3a21Sjmcneill 			continue;
2438baf3a21Sjmcneill 		}
2448baf3a21Sjmcneill 
2458be04e5fSjmcneill 		if (index < nparents) {
2468baf3a21Sjmcneill 			clk_parent = fdtbus_clock_get_index_prop(phandle, index, "assigned-clock-parents");
2478baf3a21Sjmcneill 			if (clk_parent != NULL) {
2488baf3a21Sjmcneill 				error = clk_set_parent(clk, clk_parent);
2498baf3a21Sjmcneill 				if (error != 0) {
2508baf3a21Sjmcneill 					aprint_error("clk: failed to set %s parent to %s, error %d\n",
2518baf3a21Sjmcneill 					    clk->name, clk_parent->name, error);
2528baf3a21Sjmcneill 				}
2538be04e5fSjmcneill 			} else {
2548be04e5fSjmcneill 				aprint_debug("clk: failed to set %s parent (not found)\n", clk->name);
2558be04e5fSjmcneill 			}
2568baf3a21Sjmcneill 		}
2578baf3a21Sjmcneill 
2588be04e5fSjmcneill 		if (index < nrates) {
2598be04e5fSjmcneill 			const u_int rate = be32toh(rates[index]);
2608baf3a21Sjmcneill 			if (rate != 0) {
2618baf3a21Sjmcneill 				error = clk_set_rate(clk, rate);
2628baf3a21Sjmcneill 				if (error != 0)
2638baf3a21Sjmcneill 					aprint_error("clk: failed to set %s rate to %u Hz, error %d\n",
2648baf3a21Sjmcneill 					    clk->name, rate, error);
2658baf3a21Sjmcneill 			}
2668baf3a21Sjmcneill 		}
2678baf3a21Sjmcneill 	}
2688baf3a21Sjmcneill }
269