xref: /openbsd-src/sys/dev/ofw/ofw_clock.c (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
1 /*	$OpenBSD: ofw_clock.c,v 1.7 2016/08/27 16:50:40 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
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
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
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 void
95 clock_enable_cells(uint32_t *cells, int on)
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_enable)
106 		cd->cd_enable(cd->cd_cookie, &cells[1], on);
107 }
108 
109 uint32_t *
110 clock_next_clock(uint32_t *cells)
111 {
112 	uint32_t phandle = cells[0];
113 	int node, ncells;
114 
115 	node = OF_getnodebyphandle(phandle);
116 	if (node == 0)
117 		return NULL;
118 
119 	ncells = OF_getpropint(node, "#clock-cells", 0);
120 	return cells + ncells + 1;
121 }
122 
123 int
124 clock_index(int node, const char *clock)
125 {
126 	char *names;
127 	char *name;
128 	char *end;
129 	int idx = 0;
130 	int len;
131 
132 	if (clock == NULL)
133 		return 0;
134 
135 	len = OF_getproplen(node, "clock-names");
136 	if (len <= 0)
137 		return -1;
138 
139 	names = malloc(len, M_TEMP, M_WAITOK);
140 	OF_getprop(node, "clock-names", names, len);
141 	end = names + len;
142 	name = names;
143 	while (name < end) {
144 		if (strcmp(name, clock) == 0) {
145 			free(names, M_TEMP, len);
146 			return idx;
147 		}
148 		name += strlen(name) + 1;
149 		idx++;
150 	}
151 	free(names, M_TEMP, len);
152 	return -1;
153 }
154 
155 uint32_t
156 clock_get_frequency_idx(int node, int idx)
157 {
158 	uint32_t *clocks;
159 	uint32_t *clock;
160 	uint32_t freq = 0;
161 	int len;
162 
163 	len = OF_getproplen(node, "clocks");
164 	if (len <= 0)
165 		return 0;
166 
167 	clocks = malloc(len, M_TEMP, M_WAITOK);
168 	OF_getpropintarray(node, "clocks", clocks, len);
169 
170 	clock = clocks;
171 	while (clock && clock < clocks + (len / sizeof(uint32_t))) {
172 		if (idx == 0) {
173 			freq = clock_get_frequency_cells(clock);
174 			break;
175 		}
176 		clock = clock_next_clock(clock);
177 		idx--;
178 	}
179 
180 	free(clocks, M_TEMP, len);
181 	return freq;
182 }
183 
184 uint32_t
185 clock_get_frequency(int node, const char *name)
186 {
187 	int idx;
188 
189 	idx = clock_index(node, name);
190 	if (idx == -1)
191 		return 0;
192 
193 	return clock_get_frequency_idx(node, idx);
194 }
195 
196 int
197 clock_set_frequency_idx(int node, int idx, uint32_t freq)
198 {
199 	uint32_t *clocks;
200 	uint32_t *clock;
201 	int rv = -1;
202 	int len;
203 
204 	len = OF_getproplen(node, "clocks");
205 	if (len <= 0)
206 		return -1;
207 
208 	clocks = malloc(len, M_TEMP, M_WAITOK);
209 	OF_getpropintarray(node, "clocks", clocks, len);
210 
211 	clock = clocks;
212 	while (clock && clock < clocks + (len / sizeof(uint32_t))) {
213 		if (idx == 0) {
214 			rv = clock_set_frequency_cells(clock, freq);
215 			break;
216 		}
217 		clock = clock_next_clock(clock);
218 		idx--;
219 	}
220 
221 	free(clocks, M_TEMP, len);
222 	return rv;
223 }
224 
225 int
226 clock_set_frequency(int node, const char *name, uint32_t freq)
227 {
228 	int idx;
229 
230 	idx = clock_index(node, name);
231 	if (idx == -1)
232 		return -1;
233 
234 	return clock_set_frequency_idx(node, idx, freq);
235 }
236 
237 void
238 clock_do_enable_idx(int node, int idx, int on)
239 {
240 	uint32_t *clocks;
241 	uint32_t *clock;
242 	int len;
243 
244 	len = OF_getproplen(node, "clocks");
245 	if (len <= 0)
246 		return;
247 
248 	clocks = malloc(len, M_TEMP, M_WAITOK);
249 	OF_getpropintarray(node, "clocks", clocks, len);
250 
251 	clock = clocks;
252 	while (clock && clock < clocks + (len / sizeof(uint32_t))) {
253 		if (idx <= 0)
254 			clock_enable_cells(clock, on);
255 		if (idx == 0)
256 			break;
257 		clock = clock_next_clock(clock);
258 		idx--;
259 	}
260 
261 	free(clocks, M_TEMP, len);
262 }
263 
264 void
265 clock_do_enable(int node, const char *name, int on)
266 {
267 	int idx;
268 
269 	idx = clock_index(node, name);
270 	if (idx == -1)
271 		return;
272 
273 	clock_do_enable_idx(node, idx, on);
274 }
275 
276 void
277 clock_enable_idx(int node, int idx)
278 {
279 	clock_do_enable_idx(node, idx, 1);
280 }
281 
282 void
283 clock_enable(int node, const char *name)
284 {
285 	clock_do_enable(node, name, 1);
286 }
287 
288 void
289 clock_disable_idx(int node, int idx)
290 {
291 	clock_do_enable_idx(node, idx, 0);
292 }
293 
294 void
295 clock_disable(int node, const char *name)
296 {
297 	clock_do_enable(node, name, 0);
298 }
299 
300 /*
301  * Reset functionality.
302  */
303 
304 LIST_HEAD(, reset_device) reset_devices =
305 	LIST_HEAD_INITIALIZER(reset_devices);
306 
307 void
308 reset_register(struct reset_device *rd)
309 {
310 	rd->rd_cells = OF_getpropint(rd->rd_node, "#reset-cells", 0);
311 	rd->rd_phandle = OF_getpropint(rd->rd_node, "phandle", 0);
312 	if (rd->rd_phandle == 0)
313 		return;
314 
315 	LIST_INSERT_HEAD(&reset_devices, rd, rd_list);
316 }
317 
318 void
319 reset_assert_cells(uint32_t *cells, int assert)
320 {
321 	struct reset_device *rd;
322 	uint32_t phandle = cells[0];
323 
324 	LIST_FOREACH(rd, &reset_devices, rd_list) {
325 		if (rd->rd_phandle == phandle)
326 			break;
327 	}
328 
329 	if (rd && rd->rd_reset)
330 		rd->rd_reset(rd->rd_cookie, &cells[1], assert);
331 }
332 
333 uint32_t *
334 reset_next_reset(uint32_t *cells)
335 {
336 	uint32_t phandle = cells[0];
337 	int node, ncells;
338 
339 	node = OF_getnodebyphandle(phandle);
340 	if (node == 0)
341 		return NULL;
342 
343 	ncells = OF_getpropint(node, "#reset-cells", 0);
344 	return cells + ncells + 1;
345 }
346 
347 int
348 reset_index(int node, const char *reset)
349 {
350 	char *names;
351 	char *name;
352 	char *end;
353 	int idx = 0;
354 	int len;
355 
356 	if (reset == NULL)
357 		return 0;
358 
359 	len = OF_getproplen(node, "reset-names");
360 	if (len <= 0)
361 		return -1;
362 
363 	names = malloc(len, M_TEMP, M_WAITOK);
364 	OF_getprop(node, "reset-names", names, len);
365 	end = names + len;
366 	name = names;
367 	while (name < end) {
368 		if (strcmp(name, reset) == 0) {
369 			free(names, M_TEMP, len);
370 			return idx;
371 		}
372 		name += strlen(name) + 1;
373 		idx++;
374 	}
375 	free(names, M_TEMP, len);
376 	return -1;
377 }
378 
379 void
380 reset_do_assert_idx(int node, int idx, int assert)
381 {
382 	uint32_t *resets;
383 	uint32_t *reset;
384 	int len;
385 
386 	len = OF_getproplen(node, "resets");
387 	if (len <= 0)
388 		return;
389 
390 	resets = malloc(len, M_TEMP, M_WAITOK);
391 	OF_getpropintarray(node, "resets", resets, len);
392 
393 	reset = resets;
394 	while (reset && reset < resets + (len / sizeof(uint32_t))) {
395 		if (idx <= 0)
396 			reset_assert_cells(reset, assert);
397 		if (idx == 0)
398 			break;
399 		reset = reset_next_reset(reset);
400 		idx--;
401 	}
402 
403 	free(resets, M_TEMP, len);
404 }
405 
406 void
407 reset_do_assert(int node, const char *name, int assert)
408 {
409 	int idx;
410 
411 	idx = reset_index(node, name);
412 	if (idx == -1)
413 		return;
414 
415 	reset_do_assert_idx(node, idx, assert);
416 }
417 
418 void
419 reset_assert_idx(int node, int idx)
420 {
421 	reset_do_assert_idx(node, idx, 1);
422 }
423 
424 void
425 reset_assert(int node, const char *name)
426 {
427 	reset_do_assert(node, name, 1);
428 }
429 
430 void
431 reset_deassert_idx(int node, int idx)
432 {
433 	reset_do_assert_idx(node, idx, 0);
434 }
435 
436 void
437 reset_deassert(int node, const char *name)
438 {
439 	reset_do_assert(node, name, 0);
440 }
441