2 * Copyright (c) 2017 Emmanuel Vadot <manu@freebsd.org>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
19 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
21 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
32 #include <sys/param.h>
33 #include <sys/systm.h>
36 #include <dev/extres/clk/clk.h>
38 #include <arm/allwinner/clkng/aw_clk.h>
39 #include <arm/allwinner/clkng/aw_clk_nkmp.h>
41 #include "clkdev_if.h"
44 * clknode for clocks matching the formula :
46 * clk = (clkin * n * k) / (m * p)
50 struct aw_clk_nkmp_sc {
53 struct aw_clk_factor n;
54 struct aw_clk_factor k;
55 struct aw_clk_factor m;
56 struct aw_clk_factor p;
62 uint32_t lock_retries;
63 uint32_t update_shift;
68 #define WRITE4(_clk, off, val) \
69 CLKDEV_WRITE_4(clknode_get_device(_clk), off, val)
70 #define READ4(_clk, off, val) \
71 CLKDEV_READ_4(clknode_get_device(_clk), off, val)
72 #define MODIFY4(_clk, off, clr, set ) \
73 CLKDEV_MODIFY_4(clknode_get_device(_clk), off, clr, set)
74 #define DEVICE_LOCK(_clk) \
75 CLKDEV_DEVICE_LOCK(clknode_get_device(_clk))
76 #define DEVICE_UNLOCK(_clk) \
77 CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk))
80 aw_clk_nkmp_init(struct clknode *clk, device_t dev)
82 struct aw_clk_nkmp_sc *sc;
85 sc = clknode_get_softc(clk);
88 if ((sc->flags & AW_CLK_HAS_MUX) != 0) {
90 READ4(clk, sc->offset, &val);
93 idx = (val & sc->mux_mask) >> sc->mux_shift;
96 clknode_init_parent_idx(clk, idx);
101 aw_clk_nkmp_set_gate(struct clknode *clk, bool enable)
103 struct aw_clk_nkmp_sc *sc;
106 sc = clknode_get_softc(clk);
108 if ((sc->flags & AW_CLK_HAS_GATE) == 0)
112 READ4(clk, sc->offset, &val);
114 val |= (1 << sc->gate_shift);
116 val &= ~(1 << sc->gate_shift);
117 WRITE4(clk, sc->offset, val);
124 aw_clk_nkmp_set_mux(struct clknode *clk, int index)
126 struct aw_clk_nkmp_sc *sc;
129 sc = clknode_get_softc(clk);
131 if ((sc->flags & AW_CLK_HAS_MUX) != 0)
135 READ4(clk, sc->offset, &val);
136 val &= ~(sc->mux_mask >> sc->mux_shift);
137 val |= index << sc->mux_shift;
138 WRITE4(clk, sc->offset, val);
145 aw_clk_nkmp_find_best(struct aw_clk_nkmp_sc *sc, uint64_t fparent, uint64_t *fout,
146 uint32_t *factor_n, uint32_t *factor_k, uint32_t *factor_m, uint32_t *factor_p)
157 for (n = aw_clk_factor_get_min(&sc->n); n <= aw_clk_factor_get_max(&sc->n); ) {
158 for (k = aw_clk_factor_get_min(&sc->k); k <= aw_clk_factor_get_max(&sc->k); ) {
159 for (m = aw_clk_factor_get_min(&sc->m); m <= aw_clk_factor_get_max(&sc->m); ) {
160 for (p = aw_clk_factor_get_min(&sc->p); p <= aw_clk_factor_get_max(&sc->p); ) {
161 cur = (fparent * n * k) / (m * p);
162 if ((*fout - cur) < (*fout - best)) {
171 if ((sc->p.flags & AW_CLK_FACTOR_POWER_OF_TWO) != 0)
176 if ((sc->m.flags & AW_CLK_FACTOR_POWER_OF_TWO) != 0)
181 if ((sc->k.flags & AW_CLK_FACTOR_POWER_OF_TWO) != 0)
186 if ((sc->n.flags & AW_CLK_FACTOR_POWER_OF_TWO) != 0)
196 aw_clk_nkmp_set_freq_scale(struct clknode *clk, struct aw_clk_nkmp_sc *sc,
197 uint32_t factor_n, uint32_t factor_k, uint32_t factor_m, uint32_t factor_p)
199 uint32_t val, n, k, m, p;
203 READ4(clk, sc->offset, &val);
205 n = aw_clk_get_factor(val, &sc->n);
206 k = aw_clk_get_factor(val, &sc->k);
207 m = aw_clk_get_factor(val, &sc->m);
208 p = aw_clk_get_factor(val, &sc->p);
212 val |= aw_clk_factor_get_value(&sc->p, factor_p) << sc->p.shift;
213 WRITE4(clk, sc->offset, val);
219 val |= aw_clk_factor_get_value(&sc->m, factor_m) << sc->m.shift;
220 WRITE4(clk, sc->offset, val);
226 val |= aw_clk_factor_get_value(&sc->n, factor_n) << sc->n.shift;
227 val |= aw_clk_factor_get_value(&sc->k, factor_k) << sc->k.shift;
228 WRITE4(clk, sc->offset, val);
233 val |= aw_clk_factor_get_value(&sc->m, factor_m) << sc->m.shift;
234 WRITE4(clk, sc->offset, val);
240 val |= aw_clk_factor_get_value(&sc->p, factor_p) << sc->p.shift;
241 WRITE4(clk, sc->offset, val);
245 if ((sc->flags & AW_CLK_HAS_LOCK) != 0) {
246 for (retry = 0; retry < sc->lock_retries; retry++) {
247 READ4(clk, sc->offset, &val);
248 if ((val & (1 << sc->lock_shift)) != 0)
258 aw_clk_nkmp_set_freq(struct clknode *clk, uint64_t fparent, uint64_t *fout,
259 int flags, int *stop)
261 struct aw_clk_nkmp_sc *sc;
263 uint32_t val, best_n, best_k, best_m, best_p;
266 sc = clknode_get_softc(clk);
268 best = aw_clk_nkmp_find_best(sc, fparent, fout,
269 &best_n, &best_k, &best_m, &best_p);
270 if ((flags & CLK_SET_DRYRUN) != 0) {
276 if ((best < *fout) &&
277 ((flags & CLK_SET_ROUND_DOWN) != 0)) {
281 if ((best > *fout) &&
282 ((flags & CLK_SET_ROUND_UP) != 0)) {
287 if ((sc->flags & AW_CLK_SCALE_CHANGE) != 0)
288 aw_clk_nkmp_set_freq_scale(clk, sc,
289 best_n, best_k, best_m, best_p);
292 READ4(clk, sc->offset, &val);
297 val |= aw_clk_factor_get_value(&sc->n, best_n) << sc->n.shift;
298 val |= aw_clk_factor_get_value(&sc->k, best_k) << sc->k.shift;
299 val |= aw_clk_factor_get_value(&sc->m, best_m) << sc->m.shift;
300 val |= aw_clk_factor_get_value(&sc->p, best_p) << sc->p.shift;
301 WRITE4(clk, sc->offset, val);
304 if ((sc->flags & AW_CLK_HAS_UPDATE) != 0) {
306 READ4(clk, sc->offset, &val);
307 val |= 1 << sc->update_shift;
308 WRITE4(clk, sc->offset, val);
312 if ((sc->flags & AW_CLK_HAS_LOCK) != 0) {
313 for (retry = 0; retry < sc->lock_retries; retry++) {
314 READ4(clk, sc->offset, &val);
315 if ((val & (1 << sc->lock_shift)) != 0)
329 aw_clk_nkmp_recalc(struct clknode *clk, uint64_t *freq)
331 struct aw_clk_nkmp_sc *sc;
332 uint32_t val, m, n, k, p;
334 sc = clknode_get_softc(clk);
337 READ4(clk, sc->offset, &val);
340 n = aw_clk_get_factor(val, &sc->n);
341 k = aw_clk_get_factor(val, &sc->k);
342 m = aw_clk_get_factor(val, &sc->m);
343 p = aw_clk_get_factor(val, &sc->p);
345 *freq = (*freq * n * k) / (m * p);
350 static clknode_method_t aw_nkmp_clknode_methods[] = {
351 /* Device interface */
352 CLKNODEMETHOD(clknode_init, aw_clk_nkmp_init),
353 CLKNODEMETHOD(clknode_set_gate, aw_clk_nkmp_set_gate),
354 CLKNODEMETHOD(clknode_set_mux, aw_clk_nkmp_set_mux),
355 CLKNODEMETHOD(clknode_recalc_freq, aw_clk_nkmp_recalc),
356 CLKNODEMETHOD(clknode_set_freq, aw_clk_nkmp_set_freq),
360 DEFINE_CLASS_1(aw_nkmp_clknode, aw_nkmp_clknode_class, aw_nkmp_clknode_methods,
361 sizeof(struct aw_clk_nkmp_sc), clknode_class);
364 aw_clk_nkmp_register(struct clkdom *clkdom, struct aw_clk_nkmp_def *clkdef)
367 struct aw_clk_nkmp_sc *sc;
369 clk = clknode_create(clkdom, &aw_nkmp_clknode_class, &clkdef->clkdef);
373 sc = clknode_get_softc(clk);
375 sc->offset = clkdef->offset;
377 sc->n.shift = clkdef->n.shift;
378 sc->n.width = clkdef->n.width;
379 sc->n.mask = ((1 << clkdef->n.width) - 1) << sc->n.shift;
380 sc->n.value = clkdef->n.value;
381 sc->n.flags = clkdef->n.flags;
383 sc->k.shift = clkdef->k.shift;
384 sc->k.width = clkdef->k.width;
385 sc->k.mask = ((1 << clkdef->k.width) - 1) << sc->k.shift;
386 sc->k.value = clkdef->k.value;
387 sc->k.flags = clkdef->k.flags;
389 sc->m.shift = clkdef->m.shift;
390 sc->m.width = clkdef->m.width;
391 sc->m.mask = ((1 << clkdef->m.width) - 1) << sc->m.shift;
392 sc->m.value = clkdef->m.value;
393 sc->m.flags = clkdef->m.flags;
395 sc->p.shift = clkdef->p.shift;
396 sc->p.width = clkdef->p.width;
397 sc->p.mask = ((1 << clkdef->p.width) - 1) << sc->p.shift;
398 sc->p.value = clkdef->p.value;
399 sc->p.flags = clkdef->p.flags;
401 sc->mux_shift = clkdef->mux_shift;
402 sc->mux_mask = ((1 << clkdef->mux_width) - 1) << sc->mux_shift;
404 sc->gate_shift = clkdef->gate_shift;
405 sc->lock_shift = clkdef->lock_shift;
406 sc->lock_retries = clkdef->lock_retries;
407 sc->update_shift = clkdef->update_shift;
408 sc->flags = clkdef->flags;
410 clknode_register(clkdom, clk);