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