meson_clk.c revision 1.1 1 /* $NetBSD: meson_clk.c,v 1.1 2019/01/19 20:56:03 jmcneill Exp $ */
2
3 /*-
4 * Copyright (c) 2017-2019 Jared McNeill <jmcneill (at) invisible.ca>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: meson_clk.c,v 1.1 2019/01/19 20:56:03 jmcneill Exp $");
31
32 #include <sys/param.h>
33 #include <sys/bus.h>
34 #include <sys/cpu.h>
35 #include <sys/device.h>
36
37 #include <dev/fdt/fdtvar.h>
38
39 #include <dev/clk/clk_backend.h>
40
41 #include <arm/amlogic/meson_clk.h>
42
43 static void *
44 meson_clk_reset_acquire(device_t dev, const void *data, size_t len)
45 {
46 struct meson_clk_softc * const sc = device_private(dev);
47 struct meson_clk_reset *reset;
48
49 if (len != 4)
50 return NULL;
51
52 const u_int reset_id = be32dec(data);
53
54 if (reset_id >= sc->sc_nresets)
55 return NULL;
56
57 reset = &sc->sc_resets[reset_id];
58 if (reset->mask == 0)
59 return NULL;
60
61 return reset;
62 }
63
64 static void
65 meson_clk_reset_release(device_t dev, void *priv)
66 {
67 }
68
69 static int
70 meson_clk_reset_assert(device_t dev, void *priv)
71 {
72 struct meson_clk_softc * const sc = device_private(dev);
73 struct meson_clk_reset * const reset = priv;
74
75 const uint32_t val = CLK_READ(sc, reset->reg);
76 CLK_WRITE(sc, reset->reg, val | reset->mask);
77
78 return 0;
79 }
80
81 static int
82 meson_clk_reset_deassert(device_t dev, void *priv)
83 {
84 struct meson_clk_softc * const sc = device_private(dev);
85 struct meson_clk_reset * const reset = priv;
86
87 const uint32_t val = CLK_READ(sc, reset->reg);
88 CLK_WRITE(sc, reset->reg, val & ~reset->mask);
89
90 return 0;
91 }
92
93 static const struct fdtbus_reset_controller_func meson_clk_fdtreset_funcs = {
94 .acquire = meson_clk_reset_acquire,
95 .release = meson_clk_reset_release,
96 .reset_assert = meson_clk_reset_assert,
97 .reset_deassert = meson_clk_reset_deassert,
98 };
99
100 static struct clk *
101 meson_clk_clock_decode(device_t dev, int cc_phandle, const void *data,
102 size_t len)
103 {
104 struct meson_clk_softc * const sc = device_private(dev);
105 struct meson_clk_clk *clk;
106
107 if (len != 4)
108 return NULL;
109
110 const u_int clock_id = be32dec(data);
111 if (clock_id >= sc->sc_nclks)
112 return NULL;
113
114 clk = &sc->sc_clks[clock_id];
115 if (clk->type == MESON_CLK_UNKNOWN)
116 return NULL;
117
118 return &clk->base;
119 }
120
121 static const struct fdtbus_clock_controller_func meson_clk_fdtclock_funcs = {
122 .decode = meson_clk_clock_decode,
123 };
124
125 static struct clk *
126 meson_clk_clock_get(void *priv, const char *name)
127 {
128 struct meson_clk_softc * const sc = priv;
129 struct meson_clk_clk *clk;
130
131 clk = meson_clk_clock_find(sc, name);
132 if (clk == NULL)
133 return NULL;
134
135 return &clk->base;
136 }
137
138 static void
139 meson_clk_clock_put(void *priv, struct clk *clk)
140 {
141 }
142
143 static u_int
144 meson_clk_clock_get_rate(void *priv, struct clk *clkp)
145 {
146 struct meson_clk_softc * const sc = priv;
147 struct meson_clk_clk *clk = (struct meson_clk_clk *)clkp;
148 struct clk *clkp_parent;
149
150 if (clk->get_rate)
151 return clk->get_rate(sc, clk);
152
153 clkp_parent = clk_get_parent(clkp);
154 if (clkp_parent == NULL) {
155 aprint_error("%s: no parent for %s\n", __func__, clk->base.name);
156 return 0;
157 }
158
159 return clk_get_rate(clkp_parent);
160 }
161
162 static int
163 meson_clk_clock_set_rate(void *priv, struct clk *clkp, u_int rate)
164 {
165 struct meson_clk_softc * const sc = priv;
166 struct meson_clk_clk *clk = (struct meson_clk_clk *)clkp;
167 struct clk *clkp_parent;
168
169 if (clkp->flags & CLK_SET_RATE_PARENT) {
170 clkp_parent = clk_get_parent(clkp);
171 if (clkp_parent == NULL) {
172 aprint_error("%s: no parent for %s\n", __func__, clk->base.name);
173 return ENXIO;
174 }
175 return clk_set_rate(clkp_parent, rate);
176 }
177
178 if (clk->set_rate)
179 return clk->set_rate(sc, clk, rate);
180
181 return ENXIO;
182 }
183
184 static u_int
185 meson_clk_clock_round_rate(void *priv, struct clk *clkp, u_int rate)
186 {
187 struct meson_clk_softc * const sc = priv;
188 struct meson_clk_clk *clk = (struct meson_clk_clk *)clkp;
189 struct clk *clkp_parent;
190
191 if (clkp->flags & CLK_SET_RATE_PARENT) {
192 clkp_parent = clk_get_parent(clkp);
193 if (clkp_parent == NULL) {
194 aprint_error("%s: no parent for %s\n", __func__, clk->base.name);
195 return 0;
196 }
197 return clk_round_rate(clkp_parent, rate);
198 }
199
200 if (clk->round_rate)
201 return clk->round_rate(sc, clk, rate);
202
203 return 0;
204 }
205
206 static int
207 meson_clk_clock_enable(void *priv, struct clk *clkp)
208 {
209 struct meson_clk_softc * const sc = priv;
210 struct meson_clk_clk *clk = (struct meson_clk_clk *)clkp;
211 struct clk *clkp_parent;
212 int error = 0;
213
214 clkp_parent = clk_get_parent(clkp);
215 if (clkp_parent != NULL) {
216 error = clk_enable(clkp_parent);
217 if (error != 0)
218 return error;
219 }
220
221 if (clk->enable)
222 error = clk->enable(sc, clk, 1);
223
224 return error;
225 }
226
227 static int
228 meson_clk_clock_disable(void *priv, struct clk *clkp)
229 {
230 struct meson_clk_softc * const sc = priv;
231 struct meson_clk_clk *clk = (struct meson_clk_clk *)clkp;
232 int error = EINVAL;
233
234 if (clk->enable)
235 error = clk->enable(sc, clk, 0);
236
237 return error;
238 }
239
240 static int
241 meson_clk_clock_set_parent(void *priv, struct clk *clkp,
242 struct clk *clkp_parent)
243 {
244 struct meson_clk_softc * const sc = priv;
245 struct meson_clk_clk *clk = (struct meson_clk_clk *)clkp;
246
247 if (clk->set_parent == NULL)
248 return EINVAL;
249
250 return clk->set_parent(sc, clk, clkp_parent->name);
251 }
252
253 static struct clk *
254 meson_clk_clock_get_parent(void *priv, struct clk *clkp)
255 {
256 struct meson_clk_softc * const sc = priv;
257 struct meson_clk_clk *clk = (struct meson_clk_clk *)clkp;
258 struct meson_clk_clk *clk_parent;
259 const char *parent;
260
261 if (clk->get_parent == NULL)
262 return NULL;
263
264 parent = clk->get_parent(sc, clk);
265 if (parent == NULL)
266 return NULL;
267
268 clk_parent = meson_clk_clock_find(sc, parent);
269 if (clk_parent != NULL)
270 return &clk_parent->base;
271
272 /* No parent in this domain, try FDT */
273 return fdtbus_clock_get(sc->sc_phandle, parent);
274 }
275
276 static const struct clk_funcs meson_clk_clock_funcs = {
277 .get = meson_clk_clock_get,
278 .put = meson_clk_clock_put,
279 .get_rate = meson_clk_clock_get_rate,
280 .set_rate = meson_clk_clock_set_rate,
281 .round_rate = meson_clk_clock_round_rate,
282 .enable = meson_clk_clock_enable,
283 .disable = meson_clk_clock_disable,
284 .set_parent = meson_clk_clock_set_parent,
285 .get_parent = meson_clk_clock_get_parent,
286 };
287
288 struct meson_clk_clk *
289 meson_clk_clock_find(struct meson_clk_softc *sc, const char *name)
290 {
291 for (int i = 0; i < sc->sc_nclks; i++) {
292 if (sc->sc_clks[i].base.name == NULL)
293 continue;
294 if (strcmp(sc->sc_clks[i].base.name, name) == 0)
295 return &sc->sc_clks[i];
296 }
297
298 return NULL;
299 }
300
301 int
302 meson_clk_attach(struct meson_clk_softc *sc, u_int reg_index)
303 {
304 bus_addr_t addr;
305 bus_size_t size;
306 int i;
307
308 if (fdtbus_get_reg(sc->sc_phandle, reg_index, &addr, &size) != 0) {
309 aprint_error(": couldn't get registers\n");
310 return ENXIO;
311 }
312 if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) {
313 aprint_error(": couldn't map registers\n");
314 return ENXIO;
315 }
316
317 sc->sc_clkdom.name = device_xname(sc->sc_dev);
318 sc->sc_clkdom.funcs = &meson_clk_clock_funcs;
319 sc->sc_clkdom.priv = sc;
320 for (i = 0; i < sc->sc_nclks; i++) {
321 sc->sc_clks[i].base.domain = &sc->sc_clkdom;
322 clk_attach(&sc->sc_clks[i].base);
323 }
324
325 fdtbus_register_clock_controller(sc->sc_dev, sc->sc_phandle,
326 &meson_clk_fdtclock_funcs);
327
328 fdtbus_register_reset_controller(sc->sc_dev, sc->sc_phandle,
329 &meson_clk_fdtreset_funcs);
330
331 return 0;
332 }
333
334 void
335 meson_clk_print(struct meson_clk_softc *sc)
336 {
337 struct meson_clk_clk *clk;
338 struct clk *clkp_parent;
339 const char *type;
340 int i;
341
342 for (i = 0; i < sc->sc_nclks; i++) {
343 clk = &sc->sc_clks[i];
344 if (clk->type == MESON_CLK_UNKNOWN)
345 continue;
346
347 clkp_parent = clk_get_parent(&clk->base);
348
349 switch (clk->type) {
350 case MESON_CLK_FIXED: type = "fixed"; break;
351 case MESON_CLK_GATE: type = "gate"; break;
352 case MESON_CLK_MPLL: type = "mpll"; break;
353 case MESON_CLK_PLL: type = "pll"; break;
354 case MESON_CLK_DIV: type = "div"; break;
355 case MESON_CLK_FIXED_FACTOR: type = "fixed-factor"; break;
356 case MESON_CLK_MUX: type = "mux"; break;
357 default: type = "???"; break;
358 }
359
360 aprint_debug_dev(sc->sc_dev,
361 "%3d %-12s %2s %-12s %-7s ",
362 i,
363 clk->base.name,
364 clkp_parent ? "<-" : "",
365 clkp_parent ? clkp_parent->name : "",
366 type);
367 aprint_debug("%10u Hz\n", clk_get_rate(&clk->base));
368 }
369 }
370