ofw_clock.c revision 1.4
1/*	$OpenBSD: ofw_clock.c,v 1.4 2016/08/22 19:28:27 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
94void
95clock_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
109uint32_t *
110clock_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
123int
124clock_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
155uint32_t
156clock_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
184uint32_t
185clock_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
196int
197clock_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 0;
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
225int
226clock_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 0;
233
234	return clock_set_frequency_idx(node, idx, freq);
235}
236
237void
238clock_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
264void
265clock_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
276void
277clock_enable_idx(int node, int idx)
278{
279	clock_do_enable_idx(node, idx, 1);
280}
281
282void
283clock_enable(int node, const char *name)
284{
285	clock_do_enable(node, name, 1);
286}
287
288void
289clock_disable_idx(int node, int idx)
290{
291	clock_do_enable_idx(node, idx, 0);
292}
293
294void
295clock_disable(int node, const char *name)
296{
297	clock_do_enable(node, name, 0);
298}
299
300/*
301 * Reset functionality.
302 */
303
304LIST_HEAD(, reset_device) reset_devices =
305	LIST_HEAD_INITIALIZER(reset_devices);
306
307void
308reset_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
318void
319reset_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	rd->rd_reset(rd->rd_cookie, &cells[1], assert);
330}
331
332uint32_t *
333reset_next_reset(uint32_t *cells)
334{
335	uint32_t phandle = cells[0];
336	int node, ncells;
337
338	node = OF_getnodebyphandle(phandle);
339	if (node == 0)
340		return NULL;
341
342	ncells = OF_getpropint(node, "#reset-cells", 0);
343	return cells + ncells + 1;
344}
345
346int
347reset_index(int node, const char *reset)
348{
349	char *names;
350	char *name;
351	char *end;
352	int idx = 0;
353	int len;
354
355	if (reset == NULL)
356		return 0;
357
358	len = OF_getproplen(node, "reset-names");
359	if (len <= 0)
360		return -1;
361
362	names = malloc(len, M_TEMP, M_WAITOK);
363	OF_getprop(node, "reset-names", names, len);
364	end = names + len;
365	name = names;
366	while (name < end) {
367		if (strcmp(name, reset) == 0) {
368			free(names, M_TEMP, len);
369			return idx;
370		}
371		name += strlen(name) + 1;
372		idx++;
373	}
374	free(names, M_TEMP, len);
375	return -1;
376}
377
378void
379reset_do_assert_idx(int node, int idx, int assert)
380{
381	uint32_t *resets;
382	uint32_t *reset;
383	int len;
384
385	len = OF_getproplen(node, "resets");
386	if (len <= 0)
387		return;
388
389	resets = malloc(len, M_TEMP, M_WAITOK);
390	OF_getpropintarray(node, "resets", resets, len);
391
392	reset = resets;
393	while (reset && resets < resets + (len / sizeof(uint32_t))) {
394		if (idx <= 0)
395			reset_assert_cells(reset, assert);
396		if (idx == 0)
397			break;
398		reset = reset_next_reset(reset);
399		idx--;
400	}
401
402	free(resets, M_TEMP, len);
403}
404
405void
406reset_do_assert(int node, const char *name, int assert)
407{
408	int idx;
409
410	idx = reset_index(node, name);
411	if (idx == -1)
412		return;
413
414	reset_do_assert_idx(node, idx, assert);
415}
416
417void
418reset_assert(int node, const char *name)
419{
420	reset_do_assert(node, name, 1);
421}
422
423void
424reset_deassert(int node, const char *name)
425{
426	reset_do_assert(node, name, 0);
427}
428