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