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
29LIST_HEAD(, clock_device) clock_devices =
30	LIST_HEAD_INITIALIZER(clock_devices);
31
32void
33clock_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
43uint32_t
44clock_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
77int
78clock_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
94int
95clock_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
111void
112clock_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
126uint32_t *
127clock_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
140uint32_t
141clock_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
169uint32_t
170clock_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
181int
182clock_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
210int
211clock_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
222void
223clock_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
249void
250clock_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
261void
262clock_enable_idx(int node, int idx)
263{
264	clock_do_enable_idx(node, idx, 1);
265}
266
267void
268clock_enable(int node, const char *name)
269{
270	clock_do_enable(node, name, 1);
271}
272
273void
274clock_disable_idx(int node, int idx)
275{
276	clock_do_enable_idx(node, idx, 0);
277}
278
279void
280clock_disable(int node, const char *name)
281{
282	clock_do_enable(node, name, 0);
283}
284
285void
286clock_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
338LIST_HEAD(, reset_device) reset_devices =
339	LIST_HEAD_INITIALIZER(reset_devices);
340
341void
342reset_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
352void
353reset_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
367uint32_t *
368reset_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
381void
382reset_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
408void
409reset_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
420void
421reset_assert_idx(int node, int idx)
422{
423	reset_do_assert_idx(node, idx, 1);
424}
425
426void
427reset_assert(int node, const char *name)
428{
429	reset_do_assert(node, name, 1);
430}
431
432void
433reset_deassert_idx(int node, int idx)
434{
435	reset_do_assert_idx(node, idx, 0);
436}
437
438void
439reset_deassert(int node, const char *name)
440{
441	reset_do_assert(node, name, 0);
442}
443