xref: /openbsd/sys/dev/ofw/ofw_clock.c (revision fd4119f1)
1 /*	$OpenBSD: ofw_clock.c,v 1.11 2018/08/05 21:05:17 kettenis Exp $	*/
2 /*
3  * Copyright (c) 2016 Mark Kettenis
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <sys/types.h>
19 #include <sys/systm.h>
20 #include <sys/malloc.h>
21 
22 #include <dev/ofw/openfirm.h>
23 #include <dev/ofw/ofw_clock.h>
24 
25 /*
26  * Clock functionality.
27  */
28 
29 LIST_HEAD(, clock_device) clock_devices =
30 	LIST_HEAD_INITIALIZER(clock_devices);
31 
32 void
clock_register(struct clock_device * cd)33 clock_register(struct clock_device *cd)
34 {
35 	cd->cd_cells = OF_getpropint(cd->cd_node, "#clock-cells", 0);
36 	cd->cd_phandle = OF_getpropint(cd->cd_node, "phandle", 0);
37 	if (cd->cd_phandle == 0)
38 		return;
39 
40 	LIST_INSERT_HEAD(&clock_devices, cd, cd_list);
41 }
42 
43 uint32_t
clock_get_frequency_cells(uint32_t * cells)44 clock_get_frequency_cells(uint32_t *cells)
45 {
46 	struct clock_device *cd;
47 	uint32_t phandle = cells[0];
48 	int node;
49 
50 	LIST_FOREACH(cd, &clock_devices, cd_list) {
51 		if (cd->cd_phandle == phandle)
52 			break;
53 	}
54 
55 	if (cd && cd->cd_get_frequency)
56 		return cd->cd_get_frequency(cd->cd_cookie, &cells[1]);
57 
58 	node = OF_getnodebyphandle(phandle);
59 	if (node == 0)
60 		return 0;
61 
62 	if (OF_is_compatible(node, "fixed-clock"))
63 		return OF_getpropint(node, "clock-frequency", 0);
64 
65 	if (OF_is_compatible(node, "fixed-factor-clock")) {
66 		uint32_t mult, div, freq;
67 
68 		mult = OF_getpropint(node, "clock-mult", 1);
69 		div = OF_getpropint(node, "clock-div", 1);
70 		freq = clock_get_frequency(node, NULL);
71 		return (freq * mult) / div;
72 	}
73 
74 	return 0;
75 }
76 
77 int
clock_set_frequency_cells(uint32_t * cells,uint32_t freq)78 clock_set_frequency_cells(uint32_t *cells, uint32_t freq)
79 {
80 	struct clock_device *cd;
81 	uint32_t phandle = cells[0];
82 
83 	LIST_FOREACH(cd, &clock_devices, cd_list) {
84 		if (cd->cd_phandle == phandle)
85 			break;
86 	}
87 
88 	if (cd && cd->cd_set_frequency)
89 		return cd->cd_set_frequency(cd->cd_cookie, &cells[1], freq);
90 
91 	return -1;
92 }
93 
94 int
clock_set_parent_cells(uint32_t * cells,uint32_t * pcells)95 clock_set_parent_cells(uint32_t *cells, uint32_t *pcells)
96 {
97 	struct clock_device *cd;
98 	uint32_t phandle = cells[0];
99 
100 	LIST_FOREACH(cd, &clock_devices, cd_list) {
101 		if (cd->cd_phandle == phandle)
102 			break;
103 	}
104 
105 	if (cd && cd->cd_set_parent)
106 		return cd->cd_set_parent(cd->cd_cookie, &cells[1], pcells);
107 
108 	return -1;
109 }
110 
111 void
clock_enable_cells(uint32_t * cells,int on)112 clock_enable_cells(uint32_t *cells, int on)
113 {
114 	struct clock_device *cd;
115 	uint32_t phandle = cells[0];
116 
117 	LIST_FOREACH(cd, &clock_devices, cd_list) {
118 		if (cd->cd_phandle == phandle)
119 			break;
120 	}
121 
122 	if (cd && cd->cd_enable)
123 		cd->cd_enable(cd->cd_cookie, &cells[1], on);
124 }
125 
126 uint32_t *
clock_next_clock(uint32_t * cells)127 clock_next_clock(uint32_t *cells)
128 {
129 	uint32_t phandle = cells[0];
130 	int node, ncells;
131 
132 	node = OF_getnodebyphandle(phandle);
133 	if (node == 0)
134 		return NULL;
135 
136 	ncells = OF_getpropint(node, "#clock-cells", 0);
137 	return cells + ncells + 1;
138 }
139 
140 uint32_t
clock_get_frequency_idx(int node,int idx)141 clock_get_frequency_idx(int node, int idx)
142 {
143 	uint32_t *clocks;
144 	uint32_t *clock;
145 	uint32_t freq = 0;
146 	int len;
147 
148 	len = OF_getproplen(node, "clocks");
149 	if (len <= 0)
150 		return 0;
151 
152 	clocks = malloc(len, M_TEMP, M_WAITOK);
153 	OF_getpropintarray(node, "clocks", clocks, len);
154 
155 	clock = clocks;
156 	while (clock && clock < clocks + (len / sizeof(uint32_t))) {
157 		if (idx == 0) {
158 			freq = clock_get_frequency_cells(clock);
159 			break;
160 		}
161 		clock = clock_next_clock(clock);
162 		idx--;
163 	}
164 
165 	free(clocks, M_TEMP, len);
166 	return freq;
167 }
168 
169 uint32_t
clock_get_frequency(int node,const char * name)170 clock_get_frequency(int node, const char *name)
171 {
172 	int idx;
173 
174 	idx = OF_getindex(node, name, "clock-names");
175 	if (idx == -1)
176 		return 0;
177 
178 	return clock_get_frequency_idx(node, idx);
179 }
180 
181 int
clock_set_frequency_idx(int node,int idx,uint32_t freq)182 clock_set_frequency_idx(int node, int idx, uint32_t freq)
183 {
184 	uint32_t *clocks;
185 	uint32_t *clock;
186 	int rv = -1;
187 	int len;
188 
189 	len = OF_getproplen(node, "clocks");
190 	if (len <= 0)
191 		return -1;
192 
193 	clocks = malloc(len, M_TEMP, M_WAITOK);
194 	OF_getpropintarray(node, "clocks", clocks, len);
195 
196 	clock = clocks;
197 	while (clock && clock < clocks + (len / sizeof(uint32_t))) {
198 		if (idx == 0) {
199 			rv = clock_set_frequency_cells(clock, freq);
200 			break;
201 		}
202 		clock = clock_next_clock(clock);
203 		idx--;
204 	}
205 
206 	free(clocks, M_TEMP, len);
207 	return rv;
208 }
209 
210 int
clock_set_frequency(int node,const char * name,uint32_t freq)211 clock_set_frequency(int node, const char *name, uint32_t freq)
212 {
213 	int idx;
214 
215 	idx = OF_getindex(node, name, "clock-names");
216 	if (idx == -1)
217 		return -1;
218 
219 	return clock_set_frequency_idx(node, idx, freq);
220 }
221 
222 void
clock_do_enable_idx(int node,int idx,int on)223 clock_do_enable_idx(int node, int idx, int on)
224 {
225 	uint32_t *clocks;
226 	uint32_t *clock;
227 	int len;
228 
229 	len = OF_getproplen(node, "clocks");
230 	if (len <= 0)
231 		return;
232 
233 	clocks = malloc(len, M_TEMP, M_WAITOK);
234 	OF_getpropintarray(node, "clocks", clocks, len);
235 
236 	clock = clocks;
237 	while (clock && clock < clocks + (len / sizeof(uint32_t))) {
238 		if (idx <= 0)
239 			clock_enable_cells(clock, on);
240 		if (idx == 0)
241 			break;
242 		clock = clock_next_clock(clock);
243 		idx--;
244 	}
245 
246 	free(clocks, M_TEMP, len);
247 }
248 
249 void
clock_do_enable(int node,const char * name,int on)250 clock_do_enable(int node, const char *name, int on)
251 {
252 	int idx;
253 
254 	idx = OF_getindex(node, name, "clock-names");
255 	if (idx == -1)
256 		return;
257 
258 	clock_do_enable_idx(node, idx, on);
259 }
260 
261 void
clock_enable_idx(int node,int idx)262 clock_enable_idx(int node, int idx)
263 {
264 	clock_do_enable_idx(node, idx, 1);
265 }
266 
267 void
clock_enable(int node,const char * name)268 clock_enable(int node, const char *name)
269 {
270 	clock_do_enable(node, name, 1);
271 }
272 
273 void
clock_disable_idx(int node,int idx)274 clock_disable_idx(int node, int idx)
275 {
276 	clock_do_enable_idx(node, idx, 0);
277 }
278 
279 void
clock_disable(int node,const char * name)280 clock_disable(int node, const char *name)
281 {
282 	clock_do_enable(node, name, 0);
283 }
284 
285 void
clock_set_assigned(int node)286 clock_set_assigned(int node)
287 {
288 	uint32_t *clocks, *parents, *rates;
289 	uint32_t *clock, *parent, *rate;
290 	int clen, plen, rlen;
291 
292 	clen = OF_getproplen(node, "assigned-clocks");
293 	plen = OF_getproplen(node, "assigned-clock-parents");
294 	rlen = OF_getproplen(node, "assigned-clock-rates");
295 
296 	if (clen <= 0 || (plen <= 0 && rlen <= 0))
297 		return;
298 
299 	clock = clocks = malloc(clen, M_TEMP, M_WAITOK);
300 	OF_getpropintarray(node, "assigned-clocks", clocks, clen);
301 
302 	parent = parents = NULL;
303 	if (plen > 0) {
304 		parent = parents = malloc(plen, M_TEMP, M_WAITOK);
305 		OF_getpropintarray(node, "assigned-clock-parents", parents, plen);
306 	}
307 	rate = rates = NULL;
308 	if (rlen > 0) {
309 		rate = rates = malloc(rlen, M_TEMP, M_WAITOK);
310 		OF_getpropintarray(node, "assigned-clock-rates", rates, rlen);
311 	}
312 
313 	while (clock && clock < clocks + (clen / sizeof(uint32_t))) {
314 		if (parent && parent < parents + (plen / sizeof(uint32_t)))
315 			if (*parent != 0)
316 				clock_set_parent_cells(clock, parent);
317 
318 		if (rate && rate < rates + (rlen / sizeof(uint32_t)))
319 			if (*rate != 0)
320 				clock_set_frequency_cells(clock, *rate);
321 
322 		clock = clock_next_clock(clock);
323 		if (parent)
324 			parent = clock_next_clock(parent);
325 		if (rate)
326 			rate++;
327 	}
328 
329 	free(clocks, M_TEMP, clen);
330 	free(parents, M_TEMP, plen);
331 	free(rates, M_TEMP, rlen);
332 }
333 
334 /*
335  * Reset functionality.
336  */
337 
338 LIST_HEAD(, reset_device) reset_devices =
339 	LIST_HEAD_INITIALIZER(reset_devices);
340 
341 void
reset_register(struct reset_device * rd)342 reset_register(struct reset_device *rd)
343 {
344 	rd->rd_cells = OF_getpropint(rd->rd_node, "#reset-cells", 0);
345 	rd->rd_phandle = OF_getpropint(rd->rd_node, "phandle", 0);
346 	if (rd->rd_phandle == 0)
347 		return;
348 
349 	LIST_INSERT_HEAD(&reset_devices, rd, rd_list);
350 }
351 
352 void
reset_assert_cells(uint32_t * cells,int assert)353 reset_assert_cells(uint32_t *cells, int assert)
354 {
355 	struct reset_device *rd;
356 	uint32_t phandle = cells[0];
357 
358 	LIST_FOREACH(rd, &reset_devices, rd_list) {
359 		if (rd->rd_phandle == phandle)
360 			break;
361 	}
362 
363 	if (rd && rd->rd_reset)
364 		rd->rd_reset(rd->rd_cookie, &cells[1], assert);
365 }
366 
367 uint32_t *
reset_next_reset(uint32_t * cells)368 reset_next_reset(uint32_t *cells)
369 {
370 	uint32_t phandle = cells[0];
371 	int node, ncells;
372 
373 	node = OF_getnodebyphandle(phandle);
374 	if (node == 0)
375 		return NULL;
376 
377 	ncells = OF_getpropint(node, "#reset-cells", 0);
378 	return cells + ncells + 1;
379 }
380 
381 void
reset_do_assert_idx(int node,int idx,int assert)382 reset_do_assert_idx(int node, int idx, int assert)
383 {
384 	uint32_t *resets;
385 	uint32_t *reset;
386 	int len;
387 
388 	len = OF_getproplen(node, "resets");
389 	if (len <= 0)
390 		return;
391 
392 	resets = malloc(len, M_TEMP, M_WAITOK);
393 	OF_getpropintarray(node, "resets", resets, len);
394 
395 	reset = resets;
396 	while (reset && reset < resets + (len / sizeof(uint32_t))) {
397 		if (idx <= 0)
398 			reset_assert_cells(reset, assert);
399 		if (idx == 0)
400 			break;
401 		reset = reset_next_reset(reset);
402 		idx--;
403 	}
404 
405 	free(resets, M_TEMP, len);
406 }
407 
408 void
reset_do_assert(int node,const char * name,int assert)409 reset_do_assert(int node, const char *name, int assert)
410 {
411 	int idx;
412 
413 	idx = OF_getindex(node, name, "reset-names");
414 	if (idx == -1)
415 		return;
416 
417 	reset_do_assert_idx(node, idx, assert);
418 }
419 
420 void
reset_assert_idx(int node,int idx)421 reset_assert_idx(int node, int idx)
422 {
423 	reset_do_assert_idx(node, idx, 1);
424 }
425 
426 void
reset_assert(int node,const char * name)427 reset_assert(int node, const char *name)
428 {
429 	reset_do_assert(node, name, 1);
430 }
431 
432 void
reset_deassert_idx(int node,int idx)433 reset_deassert_idx(int node, int idx)
434 {
435 	reset_do_assert_idx(node, idx, 0);
436 }
437 
438 void
reset_deassert(int node,const char * name)439 reset_deassert(int node, const char *name)
440 {
441 	reset_do_assert(node, name, 0);
442 }
443