xref: /netbsd-src/sys/dev/fdt/fdt_clock.c (revision deb6f0161a9109e7de9b519dc8dfb9478668dcdd)
1 /* $NetBSD: fdt_clock.c,v 1.6 2018/09/09 07:21:18 aymeric Exp $ */
2 
3 /*-
4  * Copyright (c) 2015 Jared D. McNeill <jmcneill@invisible.ca>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: fdt_clock.c,v 1.6 2018/09/09 07:21:18 aymeric Exp $");
31 
32 #include <sys/param.h>
33 #include <sys/bus.h>
34 #include <sys/kmem.h>
35 #include <sys/queue.h>
36 
37 #include <libfdt.h>
38 #include <dev/fdt/fdtvar.h>
39 
40 #include <dev/clk/clk_backend.h>
41 
42 struct fdtbus_clock_controller {
43 	device_t cc_dev;
44 	int cc_phandle;
45 	const struct fdtbus_clock_controller_func *cc_funcs;
46 
47 	LIST_ENTRY(fdtbus_clock_controller) cc_next;
48 };
49 
50 static LIST_HEAD(, fdtbus_clock_controller) fdtbus_clock_controllers =
51     LIST_HEAD_INITIALIZER(fdtbus_clock_controller);
52 
53 int
54 fdtbus_register_clock_controller(device_t dev, int phandle,
55     const struct fdtbus_clock_controller_func *funcs)
56 {
57 	struct fdtbus_clock_controller *cc;
58 
59 	cc = kmem_alloc(sizeof(*cc), KM_SLEEP);
60 	cc->cc_dev = dev;
61 	cc->cc_phandle = phandle;
62 	cc->cc_funcs = funcs;
63 
64 	LIST_INSERT_HEAD(&fdtbus_clock_controllers, cc, cc_next);
65 
66 	fdtbus_clock_assign(phandle);
67 
68 	return 0;
69 }
70 
71 static struct fdtbus_clock_controller *
72 fdtbus_get_clock_controller(int phandle)
73 {
74 	struct fdtbus_clock_controller *cc;
75 
76 	LIST_FOREACH(cc, &fdtbus_clock_controllers, cc_next) {
77 		if (cc->cc_phandle == phandle)
78 			return cc;
79 	}
80 
81 	return NULL;
82 }
83 
84 static struct clk *
85 fdtbus_clock_get_index_prop(int phandle, u_int index, const char *prop)
86 {
87 	struct fdtbus_clock_controller *cc;
88 	struct clk *clk = NULL;
89 	const u_int *p;
90 	u_int n, clock_cells;
91 	int len, resid;
92 
93 	p = fdtbus_get_prop(phandle, prop, &len);
94 	if (p == NULL)
95 		return NULL;
96 
97 	for (n = 0, resid = len; resid > 0; n++) {
98 		const int cc_phandle =
99 		    fdtbus_get_phandle_from_native(be32toh(p[0]));
100 		if (of_getprop_uint32(cc_phandle, "#clock-cells", &clock_cells))
101 			break;
102 		if (n == index) {
103 			cc = fdtbus_get_clock_controller(cc_phandle);
104 			if (cc == NULL)
105 				break;
106 			clk = cc->cc_funcs->decode(cc->cc_dev, cc_phandle,
107 			    clock_cells > 0 ? &p[1] : NULL, clock_cells * 4);
108 			break;
109 		}
110 		resid -= (clock_cells + 1) * 4;
111 		p += clock_cells + 1;
112 	}
113 
114 	return clk;
115 }
116 
117 struct clk *
118 fdtbus_clock_get_index(int phandle, u_int index)
119 {
120 	return fdtbus_clock_get_index_prop(phandle, index, "clocks");
121 }
122 
123 static struct clk *
124 fdtbus_clock_get_prop(int phandle, const char *clkname, const char *prop)
125 {
126 	struct clk *clk = NULL;
127 	const char *p;
128 	u_int index;
129 	int len, resid;
130 
131 	p = fdtbus_get_prop(phandle, prop, &len);
132 	if (p == NULL)
133 		return NULL;
134 
135 	for (index = 0, resid = len; resid > 0; index++) {
136 		if (strcmp(p, clkname) == 0) {
137 			clk = fdtbus_clock_get_index(phandle, index);
138 			break;
139 		}
140 		resid -= strlen(p);
141 		p += strlen(p) + 1;
142 	}
143 
144 	return clk;
145 }
146 
147 static u_int
148 fdtbus_clock_count_prop(int phandle, const char *prop)
149 {
150 	u_int n, clock_cells;
151 	int len, resid;
152 
153 	const u_int *p = fdtbus_get_prop(phandle, prop, &len);
154 	if (p == NULL)
155 		return 0;
156 
157 	for (n = 0, resid = len; resid > 0; n++) {
158 		const int cc_phandle =
159 		    fdtbus_get_phandle_from_native(be32toh(p[0]));
160 		if (of_getprop_uint32(cc_phandle, "#clock-cells", &clock_cells))
161 			break;
162 		resid -= (clock_cells + 1) * 4;
163 		p += clock_cells + 1;
164 	}
165 
166 	return n;
167 }
168 
169 struct clk *
170 fdtbus_clock_get(int phandle, const char *clkname)
171 {
172 	return fdtbus_clock_get_prop(phandle, clkname, "clock-names");
173 }
174 
175 /*
176  * Search the DT for a clock by "clock-output-names" property.
177  *
178  * This should only be used by clk backends. Not for use by ordinary
179  * clock consumers!
180  */
181 struct clk *
182 fdtbus_clock_byname(const char *clkname)
183 {
184 	struct fdtbus_clock_controller *cc;
185 	u_int len, resid, index, clock_cells;
186 	const char *p;
187 
188 	LIST_FOREACH(cc, &fdtbus_clock_controllers, cc_next) {
189 		if (!of_hasprop(cc->cc_phandle, "clock-output-names"))
190 			continue;
191 		p = fdtbus_get_prop(cc->cc_phandle, "clock-output-names", &len);
192 		for (index = 0, resid = len; resid > 0; index++) {
193 			if (strcmp(p, clkname) == 0) {
194 				if (of_getprop_uint32(cc->cc_phandle, "#clock-cells", &clock_cells))
195 					break;
196 				const u_int index_raw = htobe32(index);
197 				return cc->cc_funcs->decode(cc->cc_dev,
198 				    cc->cc_phandle,
199 				    clock_cells > 0 ? &index_raw : NULL,
200 				    clock_cells > 0 ? 4 : 0);
201 			}
202 			resid -= strlen(p) + 1;
203 			p += strlen(p) + 1;
204 		}
205 	}
206 
207 	return NULL;
208 }
209 
210 /*
211  * Apply assigned clock parents and rates.
212  *
213  * This is automatically called by fdtbus_register_clock_controller, so clock
214  * drivers likely don't need to call this directly.
215  */
216 void
217 fdtbus_clock_assign(int phandle)
218 {
219 	u_int index, rates_len;
220 	struct clk *clk, *clk_parent;
221 	int error;
222 
223 	const u_int *rates = fdtbus_get_prop(phandle, "assigned-clock-rates", &rates_len);
224 	if (rates == NULL)
225 		rates_len = 0;
226 
227 	const u_int nclocks = fdtbus_clock_count_prop(phandle, "assigned-clocks");
228 	const u_int nparents = fdtbus_clock_count_prop(phandle, "assigned-clock-parents");
229 	const u_int nrates = rates_len / sizeof(*rates);
230 
231 	for (index = 0; index < nclocks; index++) {
232 		clk = fdtbus_clock_get_index_prop(phandle, index, "assigned-clocks");
233 		if (clk == NULL) {
234 			aprint_debug("clk: assigned clock (%u) not found, skipping...\n", index);
235 			continue;
236 		}
237 
238 		if (index < nparents) {
239 			clk_parent = fdtbus_clock_get_index_prop(phandle, index, "assigned-clock-parents");
240 			if (clk_parent != NULL) {
241 				error = clk_set_parent(clk, clk_parent);
242 				if (error != 0) {
243 					aprint_error("clk: failed to set %s parent to %s, error %d\n",
244 					    clk->name, clk_parent->name, error);
245 				}
246 			} else {
247 				aprint_debug("clk: failed to set %s parent (not found)\n", clk->name);
248 			}
249 		}
250 
251 		if (index < nrates) {
252 			const u_int rate = be32toh(rates[index]);
253 			if (rate != 0) {
254 				error = clk_set_rate(clk, rate);
255 				if (error != 0)
256 					aprint_error("clk: failed to set %s rate to %u Hz, error %d\n",
257 					    clk->name, rate, error);
258 			}
259 		}
260 	}
261 }
262