1*fd4119f1Skettenis /* $OpenBSD: ofw_clock.c,v 1.11 2018/08/05 21:05:17 kettenis Exp $ */
2ea25a064Skettenis /*
3ea25a064Skettenis * Copyright (c) 2016 Mark Kettenis
4ea25a064Skettenis *
5ea25a064Skettenis * Permission to use, copy, modify, and distribute this software for any
6ea25a064Skettenis * purpose with or without fee is hereby granted, provided that the above
7ea25a064Skettenis * copyright notice and this permission notice appear in all copies.
8ea25a064Skettenis *
9ea25a064Skettenis * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10ea25a064Skettenis * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11ea25a064Skettenis * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12ea25a064Skettenis * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13ea25a064Skettenis * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14ea25a064Skettenis * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15ea25a064Skettenis * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16ea25a064Skettenis */
17ea25a064Skettenis
18ea25a064Skettenis #include <sys/types.h>
19ea25a064Skettenis #include <sys/systm.h>
20ea25a064Skettenis #include <sys/malloc.h>
21ea25a064Skettenis
22ea25a064Skettenis #include <dev/ofw/openfirm.h>
23ea25a064Skettenis #include <dev/ofw/ofw_clock.h>
24ea25a064Skettenis
258f989825Skettenis /*
268f989825Skettenis * Clock functionality.
278f989825Skettenis */
288f989825Skettenis
29ea25a064Skettenis LIST_HEAD(, clock_device) clock_devices =
30ea25a064Skettenis LIST_HEAD_INITIALIZER(clock_devices);
31ea25a064Skettenis
32ea25a064Skettenis void
clock_register(struct clock_device * cd)33ea25a064Skettenis clock_register(struct clock_device *cd)
34ea25a064Skettenis {
35ea25a064Skettenis cd->cd_cells = OF_getpropint(cd->cd_node, "#clock-cells", 0);
36ea25a064Skettenis cd->cd_phandle = OF_getpropint(cd->cd_node, "phandle", 0);
37ea25a064Skettenis if (cd->cd_phandle == 0)
38ea25a064Skettenis return;
39ea25a064Skettenis
40ea25a064Skettenis LIST_INSERT_HEAD(&clock_devices, cd, cd_list);
41ea25a064Skettenis }
42ea25a064Skettenis
43ea25a064Skettenis uint32_t
clock_get_frequency_cells(uint32_t * cells)44ea25a064Skettenis clock_get_frequency_cells(uint32_t *cells)
45ea25a064Skettenis {
46ea25a064Skettenis struct clock_device *cd;
47ea25a064Skettenis uint32_t phandle = cells[0];
48ea25a064Skettenis int node;
49ea25a064Skettenis
50ea25a064Skettenis LIST_FOREACH(cd, &clock_devices, cd_list) {
51ea25a064Skettenis if (cd->cd_phandle == phandle)
52ea25a064Skettenis break;
53ea25a064Skettenis }
54ea25a064Skettenis
55ea25a064Skettenis if (cd && cd->cd_get_frequency)
56ea25a064Skettenis return cd->cd_get_frequency(cd->cd_cookie, &cells[1]);
57ea25a064Skettenis
58ea25a064Skettenis node = OF_getnodebyphandle(phandle);
59ea25a064Skettenis if (node == 0)
60ea25a064Skettenis return 0;
61ea25a064Skettenis
62ea25a064Skettenis if (OF_is_compatible(node, "fixed-clock"))
63ea25a064Skettenis return OF_getpropint(node, "clock-frequency", 0);
64ea25a064Skettenis
65ea25a064Skettenis if (OF_is_compatible(node, "fixed-factor-clock")) {
66ea25a064Skettenis uint32_t mult, div, freq;
67ea25a064Skettenis
68ea25a064Skettenis mult = OF_getpropint(node, "clock-mult", 1);
69ea25a064Skettenis div = OF_getpropint(node, "clock-div", 1);
70ea25a064Skettenis freq = clock_get_frequency(node, NULL);
71ea25a064Skettenis return (freq * mult) / div;
72ea25a064Skettenis }
73ea25a064Skettenis
74ea25a064Skettenis return 0;
75ea25a064Skettenis }
76ea25a064Skettenis
7763d47f0eSkettenis int
clock_set_frequency_cells(uint32_t * cells,uint32_t freq)7863d47f0eSkettenis clock_set_frequency_cells(uint32_t *cells, uint32_t freq)
7963d47f0eSkettenis {
8063d47f0eSkettenis struct clock_device *cd;
8163d47f0eSkettenis uint32_t phandle = cells[0];
8263d47f0eSkettenis
8363d47f0eSkettenis LIST_FOREACH(cd, &clock_devices, cd_list) {
8463d47f0eSkettenis if (cd->cd_phandle == phandle)
8563d47f0eSkettenis break;
8663d47f0eSkettenis }
8763d47f0eSkettenis
8863d47f0eSkettenis if (cd && cd->cd_set_frequency)
8963d47f0eSkettenis return cd->cd_set_frequency(cd->cd_cookie, &cells[1], freq);
9063d47f0eSkettenis
9163d47f0eSkettenis return -1;
9263d47f0eSkettenis }
9363d47f0eSkettenis
94634b49deSpatrick int
clock_set_parent_cells(uint32_t * cells,uint32_t * pcells)95634b49deSpatrick clock_set_parent_cells(uint32_t *cells, uint32_t *pcells)
96634b49deSpatrick {
97634b49deSpatrick struct clock_device *cd;
98634b49deSpatrick uint32_t phandle = cells[0];
99634b49deSpatrick
100634b49deSpatrick LIST_FOREACH(cd, &clock_devices, cd_list) {
101634b49deSpatrick if (cd->cd_phandle == phandle)
102634b49deSpatrick break;
103634b49deSpatrick }
104634b49deSpatrick
105634b49deSpatrick if (cd && cd->cd_set_parent)
106f70b9bfaSkettenis return cd->cd_set_parent(cd->cd_cookie, &cells[1], pcells);
107634b49deSpatrick
108634b49deSpatrick return -1;
109634b49deSpatrick }
110634b49deSpatrick
111ea25a064Skettenis void
clock_enable_cells(uint32_t * cells,int on)1122c898219Skettenis clock_enable_cells(uint32_t *cells, int on)
113ea25a064Skettenis {
114ea25a064Skettenis struct clock_device *cd;
115ea25a064Skettenis uint32_t phandle = cells[0];
116ea25a064Skettenis
117ea25a064Skettenis LIST_FOREACH(cd, &clock_devices, cd_list) {
118ea25a064Skettenis if (cd->cd_phandle == phandle)
119ea25a064Skettenis break;
120ea25a064Skettenis }
121ea25a064Skettenis
122ea25a064Skettenis if (cd && cd->cd_enable)
1232c898219Skettenis cd->cd_enable(cd->cd_cookie, &cells[1], on);
124ea25a064Skettenis }
125ea25a064Skettenis
126ea25a064Skettenis uint32_t *
clock_next_clock(uint32_t * cells)127ea25a064Skettenis clock_next_clock(uint32_t *cells)
128ea25a064Skettenis {
129ea25a064Skettenis uint32_t phandle = cells[0];
130ea25a064Skettenis int node, ncells;
131ea25a064Skettenis
132ea25a064Skettenis node = OF_getnodebyphandle(phandle);
133ea25a064Skettenis if (node == 0)
134ea25a064Skettenis return NULL;
135ea25a064Skettenis
136ea25a064Skettenis ncells = OF_getpropint(node, "#clock-cells", 0);
137ea25a064Skettenis return cells + ncells + 1;
138ea25a064Skettenis }
139ea25a064Skettenis
140ea25a064Skettenis uint32_t
clock_get_frequency_idx(int node,int idx)141ea25a064Skettenis clock_get_frequency_idx(int node, int idx)
142ea25a064Skettenis {
143ea25a064Skettenis uint32_t *clocks;
144ea25a064Skettenis uint32_t *clock;
145ea25a064Skettenis uint32_t freq = 0;
146ea25a064Skettenis int len;
147ea25a064Skettenis
148ea25a064Skettenis len = OF_getproplen(node, "clocks");
149ea25a064Skettenis if (len <= 0)
150ea25a064Skettenis return 0;
151ea25a064Skettenis
152ea25a064Skettenis clocks = malloc(len, M_TEMP, M_WAITOK);
153ea25a064Skettenis OF_getpropintarray(node, "clocks", clocks, len);
154ea25a064Skettenis
155ea25a064Skettenis clock = clocks;
156ea25a064Skettenis while (clock && clock < clocks + (len / sizeof(uint32_t))) {
157ea25a064Skettenis if (idx == 0) {
158ea25a064Skettenis freq = clock_get_frequency_cells(clock);
159ea25a064Skettenis break;
160ea25a064Skettenis }
161ea25a064Skettenis clock = clock_next_clock(clock);
162ea25a064Skettenis idx--;
163ea25a064Skettenis }
164ea25a064Skettenis
165ea25a064Skettenis free(clocks, M_TEMP, len);
166ea25a064Skettenis return freq;
167ea25a064Skettenis }
168ea25a064Skettenis
169ea25a064Skettenis uint32_t
clock_get_frequency(int node,const char * name)170ea25a064Skettenis clock_get_frequency(int node, const char *name)
171ea25a064Skettenis {
172ea25a064Skettenis int idx;
173ea25a064Skettenis
17450946bcbSkettenis idx = OF_getindex(node, name, "clock-names");
175ea25a064Skettenis if (idx == -1)
176ea25a064Skettenis return 0;
177ea25a064Skettenis
178ea25a064Skettenis return clock_get_frequency_idx(node, idx);
179ea25a064Skettenis }
180ea25a064Skettenis
18163d47f0eSkettenis int
clock_set_frequency_idx(int node,int idx,uint32_t freq)18263d47f0eSkettenis clock_set_frequency_idx(int node, int idx, uint32_t freq)
18363d47f0eSkettenis {
18463d47f0eSkettenis uint32_t *clocks;
18563d47f0eSkettenis uint32_t *clock;
18663d47f0eSkettenis int rv = -1;
18763d47f0eSkettenis int len;
18863d47f0eSkettenis
18963d47f0eSkettenis len = OF_getproplen(node, "clocks");
19063d47f0eSkettenis if (len <= 0)
191bafc0b43Skettenis return -1;
19263d47f0eSkettenis
19363d47f0eSkettenis clocks = malloc(len, M_TEMP, M_WAITOK);
19463d47f0eSkettenis OF_getpropintarray(node, "clocks", clocks, len);
19563d47f0eSkettenis
19663d47f0eSkettenis clock = clocks;
19763d47f0eSkettenis while (clock && clock < clocks + (len / sizeof(uint32_t))) {
19863d47f0eSkettenis if (idx == 0) {
19963d47f0eSkettenis rv = clock_set_frequency_cells(clock, freq);
20063d47f0eSkettenis break;
20163d47f0eSkettenis }
20263d47f0eSkettenis clock = clock_next_clock(clock);
20363d47f0eSkettenis idx--;
20463d47f0eSkettenis }
20563d47f0eSkettenis
20663d47f0eSkettenis free(clocks, M_TEMP, len);
20763d47f0eSkettenis return rv;
20863d47f0eSkettenis }
20963d47f0eSkettenis
21063d47f0eSkettenis int
clock_set_frequency(int node,const char * name,uint32_t freq)21163d47f0eSkettenis clock_set_frequency(int node, const char *name, uint32_t freq)
21263d47f0eSkettenis {
21363d47f0eSkettenis int idx;
21463d47f0eSkettenis
21550946bcbSkettenis idx = OF_getindex(node, name, "clock-names");
21663d47f0eSkettenis if (idx == -1)
217bafc0b43Skettenis return -1;
21863d47f0eSkettenis
21963d47f0eSkettenis return clock_set_frequency_idx(node, idx, freq);
22063d47f0eSkettenis }
22163d47f0eSkettenis
222ea25a064Skettenis void
clock_do_enable_idx(int node,int idx,int on)2232c898219Skettenis clock_do_enable_idx(int node, int idx, int on)
224ea25a064Skettenis {
225ea25a064Skettenis uint32_t *clocks;
226ea25a064Skettenis uint32_t *clock;
227ea25a064Skettenis int len;
228ea25a064Skettenis
229ea25a064Skettenis len = OF_getproplen(node, "clocks");
230ea25a064Skettenis if (len <= 0)
231ea25a064Skettenis return;
232ea25a064Skettenis
233ea25a064Skettenis clocks = malloc(len, M_TEMP, M_WAITOK);
234ea25a064Skettenis OF_getpropintarray(node, "clocks", clocks, len);
235ea25a064Skettenis
236ea25a064Skettenis clock = clocks;
237ea25a064Skettenis while (clock && clock < clocks + (len / sizeof(uint32_t))) {
2382c898219Skettenis if (idx <= 0)
2392c898219Skettenis clock_enable_cells(clock, on);
2402c898219Skettenis if (idx == 0)
241ea25a064Skettenis break;
242ea25a064Skettenis clock = clock_next_clock(clock);
243ea25a064Skettenis idx--;
244ea25a064Skettenis }
245ea25a064Skettenis
246ea25a064Skettenis free(clocks, M_TEMP, len);
247ea25a064Skettenis }
248ea25a064Skettenis
249ea25a064Skettenis void
clock_do_enable(int node,const char * name,int on)2502c898219Skettenis clock_do_enable(int node, const char *name, int on)
251ea25a064Skettenis {
252ea25a064Skettenis int idx;
253ea25a064Skettenis
25450946bcbSkettenis idx = OF_getindex(node, name, "clock-names");
255ea25a064Skettenis if (idx == -1)
256ea25a064Skettenis return;
257ea25a064Skettenis
2582c898219Skettenis clock_do_enable_idx(node, idx, on);
2592c898219Skettenis }
2602c898219Skettenis
2612c898219Skettenis void
clock_enable_idx(int node,int idx)2622c898219Skettenis clock_enable_idx(int node, int idx)
2632c898219Skettenis {
2642c898219Skettenis clock_do_enable_idx(node, idx, 1);
2652c898219Skettenis }
2662c898219Skettenis
2672c898219Skettenis void
clock_enable(int node,const char * name)2682c898219Skettenis clock_enable(int node, const char *name)
2692c898219Skettenis {
2702c898219Skettenis clock_do_enable(node, name, 1);
2712c898219Skettenis }
2722c898219Skettenis
2732c898219Skettenis void
clock_disable_idx(int node,int idx)2742c898219Skettenis clock_disable_idx(int node, int idx)
2752c898219Skettenis {
2762c898219Skettenis clock_do_enable_idx(node, idx, 0);
2772c898219Skettenis }
2782c898219Skettenis
2792c898219Skettenis void
clock_disable(int node,const char * name)2802c898219Skettenis clock_disable(int node, const char *name)
2812c898219Skettenis {
2822c898219Skettenis clock_do_enable(node, name, 0);
283ea25a064Skettenis }
2848f989825Skettenis
285634b49deSpatrick void
clock_set_assigned(int node)286634b49deSpatrick clock_set_assigned(int node)
287634b49deSpatrick {
288634b49deSpatrick uint32_t *clocks, *parents, *rates;
289634b49deSpatrick uint32_t *clock, *parent, *rate;
290634b49deSpatrick int clen, plen, rlen;
291634b49deSpatrick
292634b49deSpatrick clen = OF_getproplen(node, "assigned-clocks");
293634b49deSpatrick plen = OF_getproplen(node, "assigned-clock-parents");
294634b49deSpatrick rlen = OF_getproplen(node, "assigned-clock-rates");
295634b49deSpatrick
296634b49deSpatrick if (clen <= 0 || (plen <= 0 && rlen <= 0))
297634b49deSpatrick return;
298634b49deSpatrick
299634b49deSpatrick clock = clocks = malloc(clen, M_TEMP, M_WAITOK);
300634b49deSpatrick OF_getpropintarray(node, "assigned-clocks", clocks, clen);
301634b49deSpatrick
302634b49deSpatrick parent = parents = NULL;
303634b49deSpatrick if (plen > 0) {
304634b49deSpatrick parent = parents = malloc(plen, M_TEMP, M_WAITOK);
305634b49deSpatrick OF_getpropintarray(node, "assigned-clock-parents", parents, plen);
306634b49deSpatrick }
307634b49deSpatrick rate = rates = NULL;
308634b49deSpatrick if (rlen > 0) {
309634b49deSpatrick rate = rates = malloc(rlen, M_TEMP, M_WAITOK);
310634b49deSpatrick OF_getpropintarray(node, "assigned-clock-rates", rates, rlen);
311634b49deSpatrick }
312634b49deSpatrick
313634b49deSpatrick while (clock && clock < clocks + (clen / sizeof(uint32_t))) {
314*fd4119f1Skettenis if (parent && parent < parents + (plen / sizeof(uint32_t)))
315634b49deSpatrick if (*parent != 0)
316634b49deSpatrick clock_set_parent_cells(clock, parent);
317634b49deSpatrick
318634b49deSpatrick if (rate && rate < rates + (rlen / sizeof(uint32_t)))
319634b49deSpatrick if (*rate != 0)
320634b49deSpatrick clock_set_frequency_cells(clock, *rate);
321634b49deSpatrick
322634b49deSpatrick clock = clock_next_clock(clock);
323634b49deSpatrick if (parent)
324634b49deSpatrick parent = clock_next_clock(parent);
325634b49deSpatrick if (rate)
326634b49deSpatrick rate++;
327634b49deSpatrick }
328634b49deSpatrick
329634b49deSpatrick free(clocks, M_TEMP, clen);
330634b49deSpatrick free(parents, M_TEMP, plen);
331634b49deSpatrick free(rates, M_TEMP, rlen);
332634b49deSpatrick }
333634b49deSpatrick
3348f989825Skettenis /*
3358f989825Skettenis * Reset functionality.
3368f989825Skettenis */
3378f989825Skettenis
3388f989825Skettenis LIST_HEAD(, reset_device) reset_devices =
3398f989825Skettenis LIST_HEAD_INITIALIZER(reset_devices);
3408f989825Skettenis
3418f989825Skettenis void
reset_register(struct reset_device * rd)3428f989825Skettenis reset_register(struct reset_device *rd)
3438f989825Skettenis {
3448f989825Skettenis rd->rd_cells = OF_getpropint(rd->rd_node, "#reset-cells", 0);
3458f989825Skettenis rd->rd_phandle = OF_getpropint(rd->rd_node, "phandle", 0);
3468f989825Skettenis if (rd->rd_phandle == 0)
3478f989825Skettenis return;
3488f989825Skettenis
3498f989825Skettenis LIST_INSERT_HEAD(&reset_devices, rd, rd_list);
3508f989825Skettenis }
3518f989825Skettenis
3528f989825Skettenis void
reset_assert_cells(uint32_t * cells,int assert)3538f989825Skettenis reset_assert_cells(uint32_t *cells, int assert)
3548f989825Skettenis {
3558f989825Skettenis struct reset_device *rd;
3568f989825Skettenis uint32_t phandle = cells[0];
3578f989825Skettenis
3588f989825Skettenis LIST_FOREACH(rd, &reset_devices, rd_list) {
3598f989825Skettenis if (rd->rd_phandle == phandle)
3608f989825Skettenis break;
3618f989825Skettenis }
3628f989825Skettenis
36318bdb117Skettenis if (rd && rd->rd_reset)
3648f989825Skettenis rd->rd_reset(rd->rd_cookie, &cells[1], assert);
3658f989825Skettenis }
3668f989825Skettenis
3678f989825Skettenis uint32_t *
reset_next_reset(uint32_t * cells)3688f989825Skettenis reset_next_reset(uint32_t *cells)
3698f989825Skettenis {
3708f989825Skettenis uint32_t phandle = cells[0];
3718f989825Skettenis int node, ncells;
3728f989825Skettenis
3738f989825Skettenis node = OF_getnodebyphandle(phandle);
3748f989825Skettenis if (node == 0)
3758f989825Skettenis return NULL;
3768f989825Skettenis
3778f989825Skettenis ncells = OF_getpropint(node, "#reset-cells", 0);
3788f989825Skettenis return cells + ncells + 1;
3798f989825Skettenis }
3808f989825Skettenis
3818f989825Skettenis void
reset_do_assert_idx(int node,int idx,int assert)3828f989825Skettenis reset_do_assert_idx(int node, int idx, int assert)
3838f989825Skettenis {
3848f989825Skettenis uint32_t *resets;
3858f989825Skettenis uint32_t *reset;
3868f989825Skettenis int len;
3878f989825Skettenis
3888f989825Skettenis len = OF_getproplen(node, "resets");
3898f989825Skettenis if (len <= 0)
3908f989825Skettenis return;
3918f989825Skettenis
3928f989825Skettenis resets = malloc(len, M_TEMP, M_WAITOK);
3938f989825Skettenis OF_getpropintarray(node, "resets", resets, len);
3948f989825Skettenis
3958f989825Skettenis reset = resets;
39618bdb117Skettenis while (reset && reset < resets + (len / sizeof(uint32_t))) {
3978f989825Skettenis if (idx <= 0)
3988f989825Skettenis reset_assert_cells(reset, assert);
3998f989825Skettenis if (idx == 0)
4008f989825Skettenis break;
4018f989825Skettenis reset = reset_next_reset(reset);
4028f989825Skettenis idx--;
4038f989825Skettenis }
4048f989825Skettenis
4058f989825Skettenis free(resets, M_TEMP, len);
4068f989825Skettenis }
4078f989825Skettenis
4088f989825Skettenis void
reset_do_assert(int node,const char * name,int assert)4098f989825Skettenis reset_do_assert(int node, const char *name, int assert)
4108f989825Skettenis {
4118f989825Skettenis int idx;
4128f989825Skettenis
41350946bcbSkettenis idx = OF_getindex(node, name, "reset-names");
4148f989825Skettenis if (idx == -1)
4158f989825Skettenis return;
4168f989825Skettenis
4178f989825Skettenis reset_do_assert_idx(node, idx, assert);
4188f989825Skettenis }
4198f989825Skettenis
4208f989825Skettenis void
reset_assert_idx(int node,int idx)42151fc57a5Skettenis reset_assert_idx(int node, int idx)
42251fc57a5Skettenis {
42351fc57a5Skettenis reset_do_assert_idx(node, idx, 1);
42451fc57a5Skettenis }
42551fc57a5Skettenis
42651fc57a5Skettenis void
reset_assert(int node,const char * name)4278f989825Skettenis reset_assert(int node, const char *name)
4288f989825Skettenis {
4298f989825Skettenis reset_do_assert(node, name, 1);
4308f989825Skettenis }
4318f989825Skettenis
4328f989825Skettenis void
reset_deassert_idx(int node,int idx)43351fc57a5Skettenis reset_deassert_idx(int node, int idx)
43451fc57a5Skettenis {
43551fc57a5Skettenis reset_do_assert_idx(node, idx, 0);
43651fc57a5Skettenis }
43751fc57a5Skettenis
43851fc57a5Skettenis void
reset_deassert(int node,const char * name)4398f989825Skettenis reset_deassert(int node, const char *name)
4408f989825Skettenis {
4418f989825Skettenis reset_do_assert(node, name, 0);
4428f989825Skettenis }
443