2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 * Copyright (c) 2018 Emmanuel Vadot <manu@freebsd.org>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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.
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
31 #include <sys/cdefs.h>
32 __FBSDID("$FreeBSD$");
34 #include <sys/param.h>
35 #include <sys/systm.h>
38 #include <dev/extres/clk/clk.h>
40 #include <arm64/rockchip/clk/rk_clk_pll.h>
42 #include "clkdev_if.h"
44 struct rk_clk_pll_sc {
55 struct rk_clk_pll_rate *rates;
56 struct rk_clk_pll_rate *frac_rates;
59 #define WRITE4(_clk, off, val) \
60 CLKDEV_WRITE_4(clknode_get_device(_clk), off, val)
61 #define READ4(_clk, off, val) \
62 CLKDEV_READ_4(clknode_get_device(_clk), off, val)
63 #define DEVICE_LOCK(_clk) \
64 CLKDEV_DEVICE_LOCK(clknode_get_device(_clk))
65 #define DEVICE_UNLOCK(_clk) \
66 CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk))
68 #define RK_CLK_PLL_MASK_SHIFT 16
70 /* #define dprintf(format, arg...) printf("%s:(%s)" format, __func__, clknode_get_name(clk), arg) */
71 #define dprintf(format, arg...)
74 rk_clk_pll_set_gate(struct clknode *clk, bool enable)
76 struct rk_clk_pll_sc *sc;
79 sc = clknode_get_softc(clk);
81 if ((sc->flags & RK_CLK_PLL_HAVE_GATE) == 0)
84 dprintf("%sabling gate\n", enable ? "En" : "Dis");
86 val |= 1 << sc->gate_shift;
87 dprintf("sc->gate_shift: %x\n", sc->gate_shift);
88 val |= (1 << sc->gate_shift) << RK_CLK_PLL_MASK_SHIFT;
89 dprintf("Write: gate_offset=%x, val=%x\n", sc->gate_offset, val);
91 WRITE4(clk, sc->gate_offset, val);
97 #define RK3328_CLK_PLL_FBDIV_OFFSET 0
98 #define RK3328_CLK_PLL_FBDIV_SHIFT 0
99 #define RK3328_CLK_PLL_FBDIV_MASK 0xFFF
101 #define RK3328_CLK_PLL_POSTDIV1_OFFSET 0
102 #define RK3328_CLK_PLL_POSTDIV1_SHIFT 12
103 #define RK3328_CLK_PLL_POSTDIV1_MASK 0x7000
105 #define RK3328_CLK_PLL_DSMPD_OFFSET 4
106 #define RK3328_CLK_PLL_DSMPD_SHIFT 12
107 #define RK3328_CLK_PLL_DSMPD_MASK 0x1000
109 #define RK3328_CLK_PLL_REFDIV_OFFSET 4
110 #define RK3328_CLK_PLL_REFDIV_SHIFT 0
111 #define RK3328_CLK_PLL_REFDIV_MASK 0x3F
113 #define RK3328_CLK_PLL_POSTDIV2_OFFSET 4
114 #define RK3328_CLK_PLL_POSTDIV2_SHIFT 6
115 #define RK3328_CLK_PLL_POSTDIV2_MASK 0x1C0
117 #define RK3328_CLK_PLL_FRAC_OFFSET 8
118 #define RK3328_CLK_PLL_FRAC_SHIFT 0
119 #define RK3328_CLK_PLL_FRAC_MASK 0xFFFFFF
121 #define RK3328_CLK_PLL_LOCK_MASK 0x400
123 #define RK3328_CLK_PLL_MODE_SLOW 0
124 #define RK3328_CLK_PLL_MODE_NORMAL 1
125 #define RK3328_CLK_PLL_MODE_MASK 0x1
128 rk3328_clk_pll_init(struct clknode *clk, device_t dev)
130 struct rk_clk_pll_sc *sc;
132 sc = clknode_get_softc(clk);
134 clknode_init_parent_idx(clk, 0);
140 rk3328_clk_pll_recalc(struct clknode *clk, uint64_t *freq)
142 struct rk_clk_pll_sc *sc;
144 uint32_t dsmpd, refdiv, fbdiv;
145 uint32_t postdiv1, postdiv2, frac;
146 uint32_t raw1, raw2, raw3;
148 sc = clknode_get_softc(clk);
152 READ4(clk, sc->base_offset, &raw1);
153 READ4(clk, sc->base_offset + 4, &raw2);
154 READ4(clk, sc->base_offset + 8, &raw3);
156 fbdiv = (raw1 & RK3328_CLK_PLL_FBDIV_MASK) >> RK3328_CLK_PLL_FBDIV_SHIFT;
157 postdiv1 = (raw1 & RK3328_CLK_PLL_POSTDIV1_MASK) >> RK3328_CLK_PLL_POSTDIV1_SHIFT;
159 dsmpd = (raw2 & RK3328_CLK_PLL_DSMPD_MASK) >> RK3328_CLK_PLL_DSMPD_SHIFT;
160 refdiv = (raw2 & RK3328_CLK_PLL_REFDIV_MASK) >> RK3328_CLK_PLL_REFDIV_SHIFT;
161 postdiv2 = (raw2 & RK3328_CLK_PLL_POSTDIV2_MASK) >> RK3328_CLK_PLL_POSTDIV2_SHIFT;
163 frac = (raw3 & RK3328_CLK_PLL_FRAC_MASK) >> RK3328_CLK_PLL_FRAC_SHIFT;
167 rate = *freq * fbdiv / refdiv;
169 /* Fractional mode */
172 frac_rate = *freq * frac / refdiv;
173 rate += frac_rate >> 24;
176 *freq = rate / postdiv1 / postdiv2;
185 rk3328_clk_pll_set_freq(struct clknode *clk, uint64_t fparent, uint64_t *fout,
186 int flags, int *stop)
188 struct rk_clk_pll_rate *rates;
189 struct rk_clk_pll_sc *sc;
193 sc = clknode_get_softc(clk);
197 else if (sc->frac_rates)
198 rates = sc->frac_rates;
202 for (; rates->freq; rates++) {
203 if (rates->freq == *fout)
206 if (rates->freq == 0) {
213 /* Setting to slow mode during frequency change */
214 reg = (RK3328_CLK_PLL_MODE_MASK << sc->mode_shift) <<
215 RK_CLK_PLL_MASK_SHIFT;
216 dprintf("Set PLL_MODEREG to %x\n", reg);
217 WRITE4(clk, sc->mode_reg, reg);
219 /* Setting postdiv1 and fbdiv */
220 reg = (rates->postdiv1 << RK3328_CLK_PLL_POSTDIV1_SHIFT) |
221 (rates->fbdiv << RK3328_CLK_PLL_FBDIV_SHIFT);
222 reg |= (RK3328_CLK_PLL_POSTDIV1_MASK | RK3328_CLK_PLL_FBDIV_MASK) << 16;
223 dprintf("Set PLL_CON0 to %x\n", reg);
224 WRITE4(clk, sc->base_offset, reg);
226 /* Setting dsmpd, postdiv2 and refdiv */
227 reg = (rates->dsmpd << RK3328_CLK_PLL_DSMPD_SHIFT) |
228 (rates->postdiv2 << RK3328_CLK_PLL_POSTDIV2_SHIFT) |
229 (rates->refdiv << RK3328_CLK_PLL_REFDIV_SHIFT);
230 reg |= (RK3328_CLK_PLL_DSMPD_MASK |
231 RK3328_CLK_PLL_POSTDIV2_MASK |
232 RK3328_CLK_PLL_REFDIV_MASK) << RK_CLK_PLL_MASK_SHIFT;
233 dprintf("Set PLL_CON1 to %x\n", reg);
234 WRITE4(clk, sc->base_offset + 0x4, reg);
237 READ4(clk, sc->base_offset + 0x8, ®);
238 reg &= ~RK3328_CLK_PLL_FRAC_MASK;
239 reg |= rates->frac << RK3328_CLK_PLL_FRAC_SHIFT;
240 dprintf("Set PLL_CON2 to %x\n", reg);
241 WRITE4(clk, sc->base_offset + 0x8, reg);
244 for (timeout = 1000; timeout; timeout--) {
245 READ4(clk, sc->base_offset + 0x4, ®);
246 if ((reg & RK3328_CLK_PLL_LOCK_MASK) == 0)
251 /* Set back to normal mode */
252 reg = (RK3328_CLK_PLL_MODE_NORMAL << sc->mode_shift);
253 reg |= (RK3328_CLK_PLL_MODE_MASK << sc->mode_shift) <<
254 RK_CLK_PLL_MASK_SHIFT;
255 dprintf("Set PLL_MODEREG to %x\n", reg);
256 WRITE4(clk, sc->mode_reg, reg);
264 static clknode_method_t rk3328_clk_pll_clknode_methods[] = {
265 /* Device interface */
266 CLKNODEMETHOD(clknode_init, rk3328_clk_pll_init),
267 CLKNODEMETHOD(clknode_set_gate, rk_clk_pll_set_gate),
268 CLKNODEMETHOD(clknode_recalc_freq, rk3328_clk_pll_recalc),
269 CLKNODEMETHOD(clknode_set_freq, rk3328_clk_pll_set_freq),
273 DEFINE_CLASS_1(rk3328_clk_pll_clknode, rk3328_clk_pll_clknode_class,
274 rk3328_clk_pll_clknode_methods, sizeof(struct rk_clk_pll_sc), clknode_class);
277 rk3328_clk_pll_register(struct clkdom *clkdom, struct rk_clk_pll_def *clkdef)
280 struct rk_clk_pll_sc *sc;
282 clk = clknode_create(clkdom, &rk3328_clk_pll_clknode_class,
287 sc = clknode_get_softc(clk);
289 sc->base_offset = clkdef->base_offset;
290 sc->gate_offset = clkdef->gate_offset;
291 sc->gate_shift = clkdef->gate_shift;
292 sc->mode_reg = clkdef->mode_reg;
293 sc->mode_shift = clkdef->mode_shift;
294 sc->flags = clkdef->flags;
295 sc->rates = clkdef->rates;
296 sc->frac_rates = clkdef->frac_rates;
298 clknode_register(clkdom, clk);
303 #define RK3399_CLK_PLL_FBDIV_OFFSET 0
304 #define RK3399_CLK_PLL_FBDIV_SHIFT 0
305 #define RK3399_CLK_PLL_FBDIV_MASK 0xFFF
307 #define RK3399_CLK_PLL_POSTDIV2_OFFSET 4
308 #define RK3399_CLK_PLL_POSTDIV2_SHIFT 12
309 #define RK3399_CLK_PLL_POSTDIV2_MASK 0x7000
311 #define RK3399_CLK_PLL_POSTDIV1_OFFSET 4
312 #define RK3399_CLK_PLL_POSTDIV1_SHIFT 8
313 #define RK3399_CLK_PLL_POSTDIV1_MASK 0x700
315 #define RK3399_CLK_PLL_REFDIV_OFFSET 4
316 #define RK3399_CLK_PLL_REFDIV_SHIFT 0
317 #define RK3399_CLK_PLL_REFDIV_MASK 0x3F
319 #define RK3399_CLK_PLL_FRAC_OFFSET 8
320 #define RK3399_CLK_PLL_FRAC_SHIFT 0
321 #define RK3399_CLK_PLL_FRAC_MASK 0xFFFFFF
323 #define RK3399_CLK_PLL_DSMPD_OFFSET 0xC
324 #define RK3399_CLK_PLL_DSMPD_SHIFT 3
325 #define RK3399_CLK_PLL_DSMPD_MASK 0x8
327 #define RK3399_CLK_PLL_LOCK_OFFSET 8
328 #define RK3399_CLK_PLL_LOCK_MASK 0x400
330 #define RK3399_CLK_PLL_MODE_OFFSET 0xC
331 #define RK3399_CLK_PLL_MODE_MASK 0x300
332 #define RK3399_CLK_PLL_MODE_SLOW 0
333 #define RK3399_CLK_PLL_MODE_NORMAL 1
334 #define RK3399_CLK_PLL_MODE_DEEPSLOW 2
335 #define RK3399_CLK_PLL_MODE_SHIFT 8
337 #define RK3399_CLK_PLL_WRITE_MASK 0xFFFF0000
340 rk3399_clk_pll_init(struct clknode *clk, device_t dev)
342 struct rk_clk_pll_sc *sc;
345 sc = clknode_get_softc(clk);
347 /* Setting to normal mode */
348 reg = RK3399_CLK_PLL_MODE_NORMAL << RK3399_CLK_PLL_MODE_SHIFT;
349 reg |= RK3399_CLK_PLL_MODE_MASK << RK_CLK_PLL_MASK_SHIFT;
350 WRITE4(clk, sc->base_offset + RK3399_CLK_PLL_MODE_OFFSET,
351 reg | RK3399_CLK_PLL_WRITE_MASK);
353 clknode_init_parent_idx(clk, 0);
359 rk3399_clk_pll_recalc(struct clknode *clk, uint64_t *freq)
361 struct rk_clk_pll_sc *sc;
362 uint32_t dsmpd, refdiv, fbdiv;
363 uint32_t postdiv1, postdiv2, fracdiv;
364 uint32_t con1, con2, con3, con4;
367 sc = clknode_get_softc(clk);
370 READ4(clk, sc->base_offset, &con1);
371 READ4(clk, sc->base_offset + 4, &con2);
372 READ4(clk, sc->base_offset + 8, &con3);
373 READ4(clk, sc->base_offset + 0xC, &con4);
376 dprintf("con0: %x\n", con1);
377 dprintf("con1: %x\n", con2);
378 dprintf("con2: %x\n", con3);
379 dprintf("con3: %x\n", con4);
381 fbdiv = (con1 & RK3399_CLK_PLL_FBDIV_MASK) >> RK3399_CLK_PLL_FBDIV_SHIFT;
383 postdiv1 = (con2 & RK3399_CLK_PLL_POSTDIV1_MASK) >> RK3399_CLK_PLL_POSTDIV1_SHIFT;
384 postdiv2 = (con2 & RK3399_CLK_PLL_POSTDIV2_MASK) >> RK3399_CLK_PLL_POSTDIV2_SHIFT;
385 refdiv = (con2 & RK3399_CLK_PLL_REFDIV_MASK) >> RK3399_CLK_PLL_REFDIV_SHIFT;
387 fracdiv = (con3 & RK3399_CLK_PLL_FRAC_MASK) >> RK3399_CLK_PLL_FRAC_SHIFT;
390 dsmpd = (con4 & RK3399_CLK_PLL_DSMPD_MASK) >> RK3399_CLK_PLL_DSMPD_SHIFT;
392 dprintf("fbdiv: %d\n", fbdiv);
393 dprintf("postdiv1: %d\n", postdiv1);
394 dprintf("postdiv2: %d\n", postdiv2);
395 dprintf("refdiv: %d\n", refdiv);
396 dprintf("fracdiv: %d\n", fracdiv);
397 dprintf("dsmpd: %d\n", dsmpd);
399 dprintf("parent freq=%lu\n", *freq);
402 /* Fractional mode */
403 foutvco = *freq / refdiv * (fbdiv + fracdiv);
406 foutvco = *freq / refdiv * fbdiv;
408 dprintf("foutvco: %lu\n", foutvco);
410 *freq = foutvco / postdiv1 / postdiv2;
411 dprintf("freq: %lu\n", *freq);
417 rk3399_clk_pll_set_freq(struct clknode *clk, uint64_t fparent, uint64_t *fout,
418 int flags, int *stop)
420 struct rk_clk_pll_rate *rates;
421 struct rk_clk_pll_sc *sc;
425 sc = clknode_get_softc(clk);
429 else if (sc->frac_rates)
430 rates = sc->frac_rates;
434 for (; rates->freq; rates++) {
435 if (rates->freq == *fout)
438 if (rates->freq == 0) {
445 /* Set to slow mode during frequency change */
446 reg = RK3399_CLK_PLL_MODE_SLOW << RK3399_CLK_PLL_MODE_SHIFT;
447 reg |= RK3399_CLK_PLL_MODE_MASK << RK_CLK_PLL_MASK_SHIFT;
448 WRITE4(clk, sc->base_offset + 0xC, reg);
451 reg = rates->fbdiv << RK3399_CLK_PLL_FBDIV_SHIFT;
452 reg |= RK3399_CLK_PLL_FBDIV_MASK << RK_CLK_PLL_MASK_SHIFT;
453 WRITE4(clk, sc->base_offset, reg);
455 /* Setting postdiv1, postdiv2 and refdiv */
456 reg = rates->postdiv1 << RK3399_CLK_PLL_POSTDIV1_SHIFT;
457 reg |= rates->postdiv2 << RK3399_CLK_PLL_POSTDIV2_SHIFT;
458 reg |= rates->refdiv << RK3399_CLK_PLL_REFDIV_SHIFT;
459 reg |= (RK3399_CLK_PLL_POSTDIV1_MASK | RK3399_CLK_PLL_POSTDIV2_MASK |
460 RK3399_CLK_PLL_REFDIV_MASK) << RK_CLK_PLL_MASK_SHIFT;
461 WRITE4(clk, sc->base_offset + 0x4, reg);
464 READ4(clk, sc->base_offset + 0x8, ®);
465 reg &= ~RK3399_CLK_PLL_FRAC_MASK;
466 reg |= rates->frac << RK3399_CLK_PLL_FRAC_SHIFT;
467 WRITE4(clk, sc->base_offset + 0x8, reg | RK3399_CLK_PLL_WRITE_MASK);
470 reg = rates->dsmpd << RK3399_CLK_PLL_DSMPD_SHIFT;
471 reg |= RK3399_CLK_PLL_DSMPD_MASK << RK_CLK_PLL_MASK_SHIFT;
472 WRITE4(clk, sc->base_offset + 0xC, reg);
475 for (timeout = 1000; timeout; timeout--) {
476 READ4(clk, sc->base_offset + RK3399_CLK_PLL_LOCK_OFFSET, ®);
477 if ((reg & RK3399_CLK_PLL_LOCK_MASK) == 0)
482 /* Set back to normal mode */
483 reg = RK3399_CLK_PLL_MODE_NORMAL << RK3399_CLK_PLL_MODE_SHIFT;
484 reg |= RK3399_CLK_PLL_MODE_MASK << RK_CLK_PLL_MASK_SHIFT;
485 WRITE4(clk, sc->base_offset + 0xC, reg);
493 static clknode_method_t rk3399_clk_pll_clknode_methods[] = {
494 /* Device interface */
495 CLKNODEMETHOD(clknode_init, rk3399_clk_pll_init),
496 CLKNODEMETHOD(clknode_set_gate, rk_clk_pll_set_gate),
497 CLKNODEMETHOD(clknode_recalc_freq, rk3399_clk_pll_recalc),
498 CLKNODEMETHOD(clknode_set_freq, rk3399_clk_pll_set_freq),
502 DEFINE_CLASS_1(rk3399_clk_pll_clknode, rk3399_clk_pll_clknode_class,
503 rk3399_clk_pll_clknode_methods, sizeof(struct rk_clk_pll_sc), clknode_class);
506 rk3399_clk_pll_register(struct clkdom *clkdom, struct rk_clk_pll_def *clkdef)
509 struct rk_clk_pll_sc *sc;
511 clk = clknode_create(clkdom, &rk3399_clk_pll_clknode_class,
516 sc = clknode_get_softc(clk);
518 sc->base_offset = clkdef->base_offset;
519 sc->gate_offset = clkdef->gate_offset;
520 sc->gate_shift = clkdef->gate_shift;
521 sc->flags = clkdef->flags;
522 sc->rates = clkdef->rates;
523 sc->frac_rates = clkdef->frac_rates;
525 clknode_register(clkdom, clk);