2 * Copyright (c) 2017 Emmanuel Vadot <manu@freebsd.org>
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
18 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
20 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
21 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
31 #include <sys/param.h>
32 #include <sys/systm.h>
35 #include <dev/extres/clk/clk.h>
37 #include <arm/allwinner/clkng/aw_clk.h>
38 #include <arm/allwinner/clkng/aw_clk_nkmp.h>
40 #include "clkdev_if.h"
43 * clknode for clocks matching the formula :
45 * clk = (clkin * n * k) / (m * p)
49 struct aw_clk_nkmp_sc {
52 struct aw_clk_factor n;
53 struct aw_clk_factor k;
54 struct aw_clk_factor m;
55 struct aw_clk_factor p;
61 uint32_t lock_retries;
62 uint32_t update_shift;
67 #define WRITE4(_clk, off, val) \
68 CLKDEV_WRITE_4(clknode_get_device(_clk), off, val)
69 #define READ4(_clk, off, val) \
70 CLKDEV_READ_4(clknode_get_device(_clk), off, val)
71 #define MODIFY4(_clk, off, clr, set ) \
72 CLKDEV_MODIFY_4(clknode_get_device(_clk), off, clr, set)
73 #define DEVICE_LOCK(_clk) \
74 CLKDEV_DEVICE_LOCK(clknode_get_device(_clk))
75 #define DEVICE_UNLOCK(_clk) \
76 CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk))
79 aw_clk_nkmp_init(struct clknode *clk, device_t dev)
81 struct aw_clk_nkmp_sc *sc;
84 sc = clknode_get_softc(clk);
87 if ((sc->flags & AW_CLK_HAS_MUX) != 0) {
89 READ4(clk, sc->offset, &val);
92 idx = (val & sc->mux_mask) >> sc->mux_shift;
95 clknode_init_parent_idx(clk, idx);
100 aw_clk_nkmp_set_gate(struct clknode *clk, bool enable)
102 struct aw_clk_nkmp_sc *sc;
105 sc = clknode_get_softc(clk);
107 if ((sc->flags & AW_CLK_HAS_GATE) == 0)
111 READ4(clk, sc->offset, &val);
113 val |= (1 << sc->gate_shift);
115 val &= ~(1 << sc->gate_shift);
116 WRITE4(clk, sc->offset, val);
123 aw_clk_nkmp_set_mux(struct clknode *clk, int index)
125 struct aw_clk_nkmp_sc *sc;
128 sc = clknode_get_softc(clk);
130 if ((sc->flags & AW_CLK_HAS_MUX) == 0)
134 READ4(clk, sc->offset, &val);
135 val &= ~sc->mux_mask;
136 val |= index << sc->mux_shift;
137 WRITE4(clk, sc->offset, val);
144 aw_clk_nkmp_find_best(struct aw_clk_nkmp_sc *sc, uint64_t fparent, uint64_t *fout,
145 uint32_t *factor_n, uint32_t *factor_k, uint32_t *factor_m, uint32_t *factor_p)
156 for (n = aw_clk_factor_get_min(&sc->n); n <= aw_clk_factor_get_max(&sc->n); ) {
157 for (k = aw_clk_factor_get_min(&sc->k); k <= aw_clk_factor_get_max(&sc->k); ) {
158 for (m = aw_clk_factor_get_min(&sc->m); m <= aw_clk_factor_get_max(&sc->m); ) {
159 for (p = aw_clk_factor_get_min(&sc->p); p <= aw_clk_factor_get_max(&sc->p); ) {
160 cur = (fparent * n * k) / (m * p);
161 if ((*fout - cur) < (*fout - best)) {
170 if ((sc->p.flags & AW_CLK_FACTOR_POWER_OF_TWO) != 0)
175 if ((sc->m.flags & AW_CLK_FACTOR_POWER_OF_TWO) != 0)
180 if ((sc->k.flags & AW_CLK_FACTOR_POWER_OF_TWO) != 0)
185 if ((sc->n.flags & AW_CLK_FACTOR_POWER_OF_TWO) != 0)
195 aw_clk_nkmp_set_freq_scale(struct clknode *clk, struct aw_clk_nkmp_sc *sc,
196 uint32_t factor_n, uint32_t factor_k, uint32_t factor_m, uint32_t factor_p)
198 uint32_t val, n, k, m, p;
202 READ4(clk, sc->offset, &val);
204 n = aw_clk_get_factor(val, &sc->n);
205 k = aw_clk_get_factor(val, &sc->k);
206 m = aw_clk_get_factor(val, &sc->m);
207 p = aw_clk_get_factor(val, &sc->p);
211 val |= aw_clk_factor_get_value(&sc->p, factor_p) << sc->p.shift;
212 WRITE4(clk, sc->offset, val);
218 val |= aw_clk_factor_get_value(&sc->m, factor_m) << sc->m.shift;
219 WRITE4(clk, sc->offset, val);
225 val |= aw_clk_factor_get_value(&sc->n, factor_n) << sc->n.shift;
226 val |= aw_clk_factor_get_value(&sc->k, factor_k) << sc->k.shift;
227 WRITE4(clk, sc->offset, val);
232 val |= aw_clk_factor_get_value(&sc->m, factor_m) << sc->m.shift;
233 WRITE4(clk, sc->offset, val);
239 val |= aw_clk_factor_get_value(&sc->p, factor_p) << sc->p.shift;
240 WRITE4(clk, sc->offset, val);
244 if ((sc->flags & AW_CLK_HAS_LOCK) != 0) {
245 for (retry = 0; retry < sc->lock_retries; retry++) {
246 READ4(clk, sc->offset, &val);
247 if ((val & (1 << sc->lock_shift)) != 0)
257 aw_clk_nkmp_set_freq(struct clknode *clk, uint64_t fparent, uint64_t *fout,
258 int flags, int *stop)
260 struct aw_clk_nkmp_sc *sc;
262 uint32_t val, best_n, best_k, best_m, best_p;
265 sc = clknode_get_softc(clk);
267 best = aw_clk_nkmp_find_best(sc, fparent, fout,
268 &best_n, &best_k, &best_m, &best_p);
269 if ((flags & CLK_SET_DRYRUN) != 0) {
275 if ((best < *fout) &&
276 ((flags & CLK_SET_ROUND_DOWN) != 0)) {
280 if ((best > *fout) &&
281 ((flags & CLK_SET_ROUND_UP) != 0)) {
286 if ((sc->flags & AW_CLK_SCALE_CHANGE) != 0)
287 aw_clk_nkmp_set_freq_scale(clk, sc,
288 best_n, best_k, best_m, best_p);
291 READ4(clk, sc->offset, &val);
296 val |= aw_clk_factor_get_value(&sc->n, best_n) << sc->n.shift;
297 val |= aw_clk_factor_get_value(&sc->k, best_k) << sc->k.shift;
298 val |= aw_clk_factor_get_value(&sc->m, best_m) << sc->m.shift;
299 val |= aw_clk_factor_get_value(&sc->p, best_p) << sc->p.shift;
300 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);
313 if ((sc->flags & AW_CLK_HAS_LOCK) != 0) {
314 for (retry = 0; retry < sc->lock_retries; retry++) {
315 READ4(clk, sc->offset, &val);
316 if ((val & (1 << sc->lock_shift)) != 0)
330 aw_clk_nkmp_recalc(struct clknode *clk, uint64_t *freq)
332 struct aw_clk_nkmp_sc *sc;
333 uint32_t val, m, n, k, p;
335 sc = clknode_get_softc(clk);
338 READ4(clk, sc->offset, &val);
341 n = aw_clk_get_factor(val, &sc->n);
342 k = aw_clk_get_factor(val, &sc->k);
343 m = aw_clk_get_factor(val, &sc->m);
344 p = aw_clk_get_factor(val, &sc->p);
346 *freq = (*freq * n * k) / (m * p);
351 static clknode_method_t aw_nkmp_clknode_methods[] = {
352 /* Device interface */
353 CLKNODEMETHOD(clknode_init, aw_clk_nkmp_init),
354 CLKNODEMETHOD(clknode_set_gate, aw_clk_nkmp_set_gate),
355 CLKNODEMETHOD(clknode_set_mux, aw_clk_nkmp_set_mux),
356 CLKNODEMETHOD(clknode_recalc_freq, aw_clk_nkmp_recalc),
357 CLKNODEMETHOD(clknode_set_freq, aw_clk_nkmp_set_freq),
361 DEFINE_CLASS_1(aw_nkmp_clknode, aw_nkmp_clknode_class, aw_nkmp_clknode_methods,
362 sizeof(struct aw_clk_nkmp_sc), clknode_class);
365 aw_clk_nkmp_register(struct clkdom *clkdom, struct aw_clk_nkmp_def *clkdef)
368 struct aw_clk_nkmp_sc *sc;
370 clk = clknode_create(clkdom, &aw_nkmp_clknode_class, &clkdef->clkdef);
374 sc = clknode_get_softc(clk);
376 sc->offset = clkdef->offset;
378 sc->n.shift = clkdef->n.shift;
379 sc->n.width = clkdef->n.width;
380 sc->n.mask = ((1 << clkdef->n.width) - 1) << sc->n.shift;
381 sc->n.value = clkdef->n.value;
382 sc->n.flags = clkdef->n.flags;
384 sc->k.shift = clkdef->k.shift;
385 sc->k.width = clkdef->k.width;
386 sc->k.mask = ((1 << clkdef->k.width) - 1) << sc->k.shift;
387 sc->k.value = clkdef->k.value;
388 sc->k.flags = clkdef->k.flags;
390 sc->m.shift = clkdef->m.shift;
391 sc->m.width = clkdef->m.width;
392 sc->m.mask = ((1 << clkdef->m.width) - 1) << sc->m.shift;
393 sc->m.value = clkdef->m.value;
394 sc->m.flags = clkdef->m.flags;
396 sc->p.shift = clkdef->p.shift;
397 sc->p.width = clkdef->p.width;
398 sc->p.mask = ((1 << clkdef->p.width) - 1) << sc->p.shift;
399 sc->p.value = clkdef->p.value;
400 sc->p.flags = clkdef->p.flags;
402 sc->mux_shift = clkdef->mux_shift;
403 sc->mux_mask = ((1 << clkdef->mux_width) - 1) << sc->mux_shift;
405 sc->gate_shift = clkdef->gate_shift;
406 sc->lock_shift = clkdef->lock_shift;
407 sc->lock_retries = clkdef->lock_retries;
408 sc->update_shift = clkdef->update_shift;
409 sc->flags = clkdef->flags;
411 clknode_register(clkdom, clk);