Index: head/sys/arm64/rockchip/clk/rk_clk_pll.c =================================================================== --- head/sys/arm64/rockchip/clk/rk_clk_pll.c (revision 333314) +++ head/sys/arm64/rockchip/clk/rk_clk_pll.c (revision 333315) @@ -1,203 +1,273 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2018 Emmanuel Vadot * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include "clkdev_if.h" struct rk_clk_pll_sc { uint32_t base_offset; uint32_t gate_offset; uint32_t gate_shift; uint32_t flags; + + struct rk_clk_pll_rate *rates; + struct rk_clk_pll_rate *frac_rates; }; #define WRITE4(_clk, off, val) \ CLKDEV_WRITE_4(clknode_get_device(_clk), off, val) #define READ4(_clk, off, val) \ CLKDEV_READ_4(clknode_get_device(_clk), off, val) #define DEVICE_LOCK(_clk) \ CLKDEV_DEVICE_LOCK(clknode_get_device(_clk)) #define DEVICE_UNLOCK(_clk) \ CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk)) -#define RK_CLK_PLL_DSMPD_OFFSET 4 -#define RK_CLK_PLL_DSMPD_SHIFT 12 -#define RK_CLK_PLL_DSMPD_MASK 0x1000 - -#define RK_CLK_PLL_REFDIV_OFFSET 4 -#define RK_CLK_PLL_REFDIV_SHIFT 0 -#define RK_CLK_PLL_REFDIV_MASK 0x3F - #define RK_CLK_PLL_FBDIV_OFFSET 0 #define RK_CLK_PLL_FBDIV_SHIFT 0 #define RK_CLK_PLL_FBDIV_MASK 0xFFF #define RK_CLK_PLL_POSTDIV1_OFFSET 0 #define RK_CLK_PLL_POSTDIV1_SHIFT 12 #define RK_CLK_PLL_POSTDIV1_MASK 0x7000 +#define RK_CLK_PLL_DSMPD_OFFSET 4 +#define RK_CLK_PLL_DSMPD_SHIFT 12 +#define RK_CLK_PLL_DSMPD_MASK 0x1000 + +#define RK_CLK_PLL_REFDIV_OFFSET 4 +#define RK_CLK_PLL_REFDIV_SHIFT 0 +#define RK_CLK_PLL_REFDIV_MASK 0x3F + #define RK_CLK_PLL_POSTDIV2_OFFSET 4 #define RK_CLK_PLL_POSTDIV2_SHIFT 6 #define RK_CLK_PLL_POSTDIV2_MASK 0x1C0 #define RK_CLK_PLL_FRAC_OFFSET 8 #define RK_CLK_PLL_FRAC_SHIFT 0 #define RK_CLK_PLL_FRAC_MASK 0xFFFFFF +#define RK_CLK_PLL_LOCK_MASK 0x400 + +#define RK_CLK_PLL_WRITE_MASK 0xFFFF0000 + static int rk_clk_pll_init(struct clknode *clk, device_t dev) { struct rk_clk_pll_sc *sc; sc = clknode_get_softc(clk); clknode_init_parent_idx(clk, 0); return (0); } static int rk_clk_pll_set_gate(struct clknode *clk, bool enable) { struct rk_clk_pll_sc *sc; uint32_t val; sc = clknode_get_softc(clk); if ((sc->flags & RK_CLK_PLL_HAVE_GATE) == 0) return (0); DEVICE_LOCK(clk); READ4(clk, sc->gate_offset, &val); if (enable) val &= ~(1 << sc->gate_shift); else val |= 1 << sc->gate_shift; WRITE4(clk, sc->gate_offset, val | RK_CLK_PLL_MASK); DEVICE_UNLOCK(clk); return (0); } static int rk_clk_pll_recalc(struct clknode *clk, uint64_t *freq) { struct rk_clk_pll_sc *sc; - uint64_t foutvco; + uint64_t rate; uint32_t dsmpd, refdiv, fbdiv; uint32_t postdiv1, postdiv2, frac; - uint32_t raw1, raw2; + uint32_t raw1, raw2, raw3; sc = clknode_get_softc(clk); DEVICE_LOCK(clk); READ4(clk, sc->base_offset, &raw1); READ4(clk, sc->base_offset + 4, &raw2); + READ4(clk, sc->base_offset + 8, &raw3); - READ4(clk, sc->base_offset + RK_CLK_PLL_DSMPD_OFFSET, &dsmpd); - dsmpd = (dsmpd & RK_CLK_PLL_DSMPD_MASK) >> RK_CLK_PLL_DSMPD_SHIFT; + fbdiv = (raw1 & RK_CLK_PLL_FBDIV_MASK) >> RK_CLK_PLL_FBDIV_SHIFT; + postdiv1 = (raw1 & RK_CLK_PLL_POSTDIV1_MASK) >> RK_CLK_PLL_POSTDIV1_SHIFT; - READ4(clk, sc->base_offset + RK_CLK_PLL_REFDIV_OFFSET, &refdiv); - refdiv = (refdiv & RK_CLK_PLL_REFDIV_MASK) >> RK_CLK_PLL_REFDIV_SHIFT; + dsmpd = (raw2 & RK_CLK_PLL_DSMPD_MASK) >> RK_CLK_PLL_DSMPD_SHIFT; + refdiv = (raw2 & RK_CLK_PLL_REFDIV_MASK) >> RK_CLK_PLL_REFDIV_SHIFT; + postdiv2 = (raw2 & RK_CLK_PLL_POSTDIV2_MASK) >> RK_CLK_PLL_POSTDIV2_SHIFT; - READ4(clk, sc->base_offset + RK_CLK_PLL_FBDIV_OFFSET, &fbdiv); - fbdiv = (fbdiv & RK_CLK_PLL_FBDIV_MASK) >> RK_CLK_PLL_FBDIV_SHIFT; + frac = (raw3 & RK_CLK_PLL_FRAC_MASK) >> RK_CLK_PLL_FRAC_SHIFT; - READ4(clk, sc->base_offset + RK_CLK_PLL_POSTDIV1_OFFSET, &postdiv1); - postdiv1 = (postdiv1 & RK_CLK_PLL_POSTDIV1_MASK) >> RK_CLK_PLL_POSTDIV1_SHIFT; - - READ4(clk, sc->base_offset + RK_CLK_PLL_POSTDIV2_OFFSET, &postdiv2); - postdiv2 = (postdiv2 & RK_CLK_PLL_POSTDIV2_MASK) >> RK_CLK_PLL_POSTDIV2_SHIFT; - - READ4(clk, sc->base_offset + RK_CLK_PLL_FRAC_OFFSET, &frac); - frac = (frac & RK_CLK_PLL_FRAC_MASK) >> RK_CLK_PLL_FRAC_SHIFT; - DEVICE_UNLOCK(clk); + rate = *freq * fbdiv / refdiv; if (dsmpd == 0) { /* Fractional mode */ - foutvco = *freq / refdiv * (fbdiv + frac / 224); - } else { - /* Integer mode */ + uint64_t frac_rate; - foutvco = *freq / refdiv * fbdiv; + frac_rate = *freq * frac / refdiv; + rate += frac_rate >> 24; } - *freq = foutvco / postdiv1 / postdiv2; + *freq = rate / postdiv1 / postdiv2; + if (*freq % 2) + *freq = *freq + 1; + return (0); } +static int +rk_clk_pll_set_freq(struct clknode *clk, uint64_t fparent, uint64_t *fout, + int flags, int *stop) +{ + struct rk_clk_pll_rate *rates; + struct rk_clk_pll_sc *sc; + uint32_t reg; + int timeout; + + sc = clknode_get_softc(clk); + + if (sc->rates) + rates = sc->rates; + else if (sc->frac_rates) + rates = sc->frac_rates; + else + return (EINVAL); + + for (; rates->freq; rates++) { + if (rates->freq == *fout) + break; + } + if (rates->freq == 0) { + *stop = 1; + return (EINVAL); + } + + DEVICE_LOCK(clk); + + /* Setting postdiv1 and fbdiv */ + READ4(clk, sc->base_offset, ®); + reg &= ~(RK_CLK_PLL_POSTDIV1_MASK | RK_CLK_PLL_FBDIV_MASK); + reg |= rates->postdiv1 << RK_CLK_PLL_POSTDIV1_SHIFT; + reg |= rates->fbdiv << RK_CLK_PLL_FBDIV_SHIFT; + WRITE4(clk, sc->base_offset, reg | RK_CLK_PLL_WRITE_MASK); + + /* Setting dsmpd, postdiv2 and refdiv */ + READ4(clk, sc->base_offset + 0x4, ®); + reg &= ~(RK_CLK_PLL_DSMPD_MASK | RK_CLK_PLL_POSTDIV2_MASK | + RK_CLK_PLL_REFDIV_MASK); + reg |= rates->dsmpd << RK_CLK_PLL_DSMPD_SHIFT; + reg |= rates->postdiv2 << RK_CLK_PLL_POSTDIV2_SHIFT; + reg |= rates->refdiv << RK_CLK_PLL_REFDIV_SHIFT; + WRITE4(clk, sc->base_offset + 0x4, reg | RK_CLK_PLL_WRITE_MASK); + + /* Setting frac */ + READ4(clk, sc->base_offset + 0x8, ®); + reg &= ~RK_CLK_PLL_FRAC_MASK; + reg |= rates->frac << RK_CLK_PLL_FRAC_SHIFT; + WRITE4(clk, sc->base_offset + 0x8, reg); + + /* Reading lock */ + for (timeout = 1000; timeout; timeout--) { + READ4(clk, sc->base_offset + 0x4, ®); + if ((reg & RK_CLK_PLL_LOCK_MASK) == 0) + break; + DELAY(1); + } + + DEVICE_UNLOCK(clk); + + *stop = 1; + return (0); +} + static clknode_method_t rk_clk_pll_clknode_methods[] = { /* Device interface */ CLKNODEMETHOD(clknode_init, rk_clk_pll_init), CLKNODEMETHOD(clknode_set_gate, rk_clk_pll_set_gate), CLKNODEMETHOD(clknode_recalc_freq, rk_clk_pll_recalc), + CLKNODEMETHOD(clknode_set_freq, rk_clk_pll_set_freq), CLKNODEMETHOD_END }; DEFINE_CLASS_1(rk_clk_pll_clknode, rk_clk_pll_clknode_class, rk_clk_pll_clknode_methods, sizeof(struct rk_clk_pll_sc), clknode_class); int rk_clk_pll_register(struct clkdom *clkdom, struct rk_clk_pll_def *clkdef) { struct clknode *clk; struct rk_clk_pll_sc *sc; clk = clknode_create(clkdom, &rk_clk_pll_clknode_class, &clkdef->clkdef); if (clk == NULL) return (1); sc = clknode_get_softc(clk); sc->base_offset = clkdef->base_offset; sc->gate_offset = clkdef->gate_offset; sc->gate_shift = clkdef->gate_shift; sc->flags = clkdef->flags; + sc->rates = clkdef->rates; + sc->frac_rates = clkdef->frac_rates; clknode_register(clkdom, clk); return (0); } Index: head/sys/arm64/rockchip/clk/rk_clk_pll.h =================================================================== --- head/sys/arm64/rockchip/clk/rk_clk_pll.h (revision 333314) +++ head/sys/arm64/rockchip/clk/rk_clk_pll.h (revision 333315) @@ -1,52 +1,65 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright 2018 Emmanuel Vadot * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _RK_CLK_PLL_H_ #define _RK_CLK_PLL_H_ #include +struct rk_clk_pll_rate { + uint32_t freq; + uint32_t refdiv; + uint32_t fbdiv; + uint32_t postdiv1; + uint32_t postdiv2; + uint32_t dsmpd; + uint32_t frac; +}; + struct rk_clk_pll_def { struct clknode_init_def clkdef; uint32_t base_offset; uint32_t gate_offset; uint32_t gate_shift; uint32_t flags; + + struct rk_clk_pll_rate *rates; + struct rk_clk_pll_rate *frac_rates; }; #define RK_CLK_PLL_HAVE_GATE 0x1 #define RK_CLK_PLL_MASK 0xFFFF0000 int rk_clk_pll_register(struct clkdom *clkdom, struct rk_clk_pll_def *clkdef); #endif /* _RK_CLK_PLL_H_ */