1*4fb05369Sryo /* $NetBSD: rk_cru_pll.c,v 1.6 2022/08/23 05:39:06 ryo Exp $ */
26726462dSjmcneill
36726462dSjmcneill /*-
46726462dSjmcneill * Copyright (c) 2018 Jared McNeill <jmcneill@invisible.ca>
56726462dSjmcneill * All rights reserved.
66726462dSjmcneill *
76726462dSjmcneill * Redistribution and use in source and binary forms, with or without
86726462dSjmcneill * modification, are permitted provided that the following conditions
96726462dSjmcneill * are met:
106726462dSjmcneill * 1. Redistributions of source code must retain the above copyright
116726462dSjmcneill * notice, this list of conditions and the following disclaimer.
126726462dSjmcneill * 2. Redistributions in binary form must reproduce the above copyright
136726462dSjmcneill * notice, this list of conditions and the following disclaimer in the
146726462dSjmcneill * documentation and/or other materials provided with the distribution.
156726462dSjmcneill *
166726462dSjmcneill * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
176726462dSjmcneill * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
186726462dSjmcneill * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
196726462dSjmcneill * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
206726462dSjmcneill * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
216726462dSjmcneill * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
226726462dSjmcneill * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
236726462dSjmcneill * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
246726462dSjmcneill * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
256726462dSjmcneill * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
266726462dSjmcneill * SUCH DAMAGE.
276726462dSjmcneill */
286726462dSjmcneill
296726462dSjmcneill #include <sys/cdefs.h>
30*4fb05369Sryo __KERNEL_RCSID(0, "$NetBSD: rk_cru_pll.c,v 1.6 2022/08/23 05:39:06 ryo Exp $");
316726462dSjmcneill
326726462dSjmcneill #include <sys/param.h>
336726462dSjmcneill #include <sys/bus.h>
346726462dSjmcneill
356726462dSjmcneill #include <dev/clk/clk_backend.h>
366726462dSjmcneill
376726462dSjmcneill #include <arm/rockchip/rk_cru.h>
386726462dSjmcneill
396726462dSjmcneill #define PLL_CON0 0x00
406726462dSjmcneill #define PLL_BYPASS __BIT(15)
416726462dSjmcneill #define PLL_POSTDIV1 __BITS(14,12)
426726462dSjmcneill #define PLL_FBDIV __BITS(11,0)
436726462dSjmcneill
446726462dSjmcneill #define PLL_CON1 0x04
456726462dSjmcneill #define PLL_PDSEL __BIT(15)
466726462dSjmcneill #define PLL_PD1 __BIT(14)
476726462dSjmcneill #define PLL_PD0 __BIT(13)
486726462dSjmcneill #define PLL_DSMPD __BIT(12)
496726462dSjmcneill #define PLL_LOCK __BIT(10)
506726462dSjmcneill #define PLL_POSTDIV2 __BITS(8,6)
516726462dSjmcneill #define PLL_REFDIV __BITS(5,0)
526726462dSjmcneill
536726462dSjmcneill #define PLL_CON2 0x08
546726462dSjmcneill #define PLL_FOUT4PHASEPD __BIT(27)
556726462dSjmcneill #define PLL_FOUTVCOPD __BIT(26)
566726462dSjmcneill #define PLL_FOUTPOSTDIVPD __BIT(25)
576726462dSjmcneill #define PLL_DACPD __BIT(24)
586726462dSjmcneill #define PLL_FRACDIV __BITS(23,0)
596726462dSjmcneill
6001470923Sjmcneill #define PLL_CON3 0x0c
61*4fb05369Sryo #define PLL_CON6 0x18
6201470923Sjmcneill
636726462dSjmcneill #define PLL_WRITE_MASK 0xffff0000 /* for CON0 and CON1 */
646726462dSjmcneill
6501470923Sjmcneill /* RK3288 CON0 */
6601470923Sjmcneill #define RK3288_CLKR __BITS(13,8)
6701470923Sjmcneill #define RK3288_CLKOD __BITS(3,0)
6801470923Sjmcneill /* RK3288 CON1 */
6901470923Sjmcneill #define RK3288_LOCK __BIT(31)
7001470923Sjmcneill #define RK3288_CLKF __BITS(12,0)
7101470923Sjmcneill /* RK3288 CON2 */
7201470923Sjmcneill #define RK3288_BWADJ __BITS(11,0)
7301470923Sjmcneill /* RK3288 CON3 */
7401470923Sjmcneill #define RK3288_BYPASS __BIT(0)
756726462dSjmcneill
76*4fb05369Sryo #define RK3588_PLLCON0_M __BITS(9,0)
77*4fb05369Sryo #define RK3588_PLLCON1_P __BITS(5,0)
78*4fb05369Sryo #define RK3588_PLLCON1_S __BITS(8,6)
79*4fb05369Sryo #define RK3588_PLLCON2_K __BITS(15,0)
80*4fb05369Sryo #define RK3588_PLLCON1_PWRDOWN __BIT(13)
81*4fb05369Sryo #define RK3588_PLLCON6_LOCK __BIT(15)
82*4fb05369Sryo
83*4fb05369Sryo #define PLL_MODE_SLOW 0x0
84*4fb05369Sryo #define PLL_MODE_NORM 0x1
85*4fb05369Sryo
866726462dSjmcneill u_int
rk_cru_pll_get_rate(struct rk_cru_softc * sc,struct rk_cru_clk * clk)876726462dSjmcneill rk_cru_pll_get_rate(struct rk_cru_softc *sc,
886726462dSjmcneill struct rk_cru_clk *clk)
896726462dSjmcneill {
906726462dSjmcneill struct rk_cru_pll *pll = &clk->u.pll;
916726462dSjmcneill struct clk *clkp, *clkp_parent;
926726462dSjmcneill u_int foutvco, foutpostdiv;
936726462dSjmcneill
946726462dSjmcneill KASSERT(clk->type == RK_CRU_PLL);
956726462dSjmcneill
966726462dSjmcneill clkp = &clk->base;
976726462dSjmcneill clkp_parent = clk_get_parent(clkp);
986726462dSjmcneill if (clkp_parent == NULL)
996726462dSjmcneill return 0;
1006726462dSjmcneill
1016726462dSjmcneill const u_int fref = clk_get_rate(clkp_parent);
1026726462dSjmcneill if (fref == 0)
1036726462dSjmcneill return 0;
1046726462dSjmcneill
1056726462dSjmcneill const uint32_t con0 = CRU_READ(sc, pll->con_base + PLL_CON0);
1066726462dSjmcneill const uint32_t con1 = CRU_READ(sc, pll->con_base + PLL_CON1);
1076726462dSjmcneill const uint32_t con2 = CRU_READ(sc, pll->con_base + PLL_CON2);
10801470923Sjmcneill const uint32_t con3 = CRU_READ(sc, pll->con_base + PLL_CON3);
1096726462dSjmcneill
11001470923Sjmcneill if ((pll->flags & RK_PLL_RK3288) != 0) {
11101470923Sjmcneill if ((con3 & RK3288_BYPASS) != 0) {
11201470923Sjmcneill return fref;
11301470923Sjmcneill }
11401470923Sjmcneill
11501470923Sjmcneill const u_int nr = __SHIFTOUT(con0, RK3288_CLKR) + 1;
11601470923Sjmcneill const u_int no = __SHIFTOUT(con0, RK3288_CLKOD) + 1;
11701470923Sjmcneill const u_int nf = __SHIFTOUT(con1, RK3288_CLKF) + 1;
11801470923Sjmcneill
11901470923Sjmcneill const uint64_t tmp = (uint64_t)fref * nf / nr / no;
12001470923Sjmcneill
12101470923Sjmcneill return (u_int)tmp;
122*4fb05369Sryo } else if ((pll->flags & RK_PLL_RK3588) != 0) {
123*4fb05369Sryo const uint64_t m = __SHIFTOUT(con0, RK3588_PLLCON0_M);
124*4fb05369Sryo const uint64_t p = __SHIFTOUT(con1, RK3588_PLLCON1_P);
125*4fb05369Sryo const uint64_t s = __SHIFTOUT(con1, RK3588_PLLCON1_S);
126*4fb05369Sryo const uint64_t k = __SHIFTOUT(con2, RK3588_PLLCON2_K);
127*4fb05369Sryo
128*4fb05369Sryo uint64_t tmp = (uint64_t)fref * m;
129*4fb05369Sryo if (p != 0)
130*4fb05369Sryo tmp /= p;
131*4fb05369Sryo if (k != 0 && p != 0)
132*4fb05369Sryo tmp += ((uint64_t)fref * k) / (p * 65535);
133*4fb05369Sryo tmp >>= s;
134*4fb05369Sryo return (u_int)tmp;
13501470923Sjmcneill } else {
1366726462dSjmcneill const u_int postdiv1 = __SHIFTOUT(con0, PLL_POSTDIV1);
1376726462dSjmcneill const u_int fbdiv = __SHIFTOUT(con0, PLL_FBDIV);
1386726462dSjmcneill const u_int dsmpd = __SHIFTOUT(con1, PLL_DSMPD);
1396726462dSjmcneill const u_int refdiv = __SHIFTOUT(con1, PLL_REFDIV);
1406726462dSjmcneill const u_int postdiv2 = __SHIFTOUT(con1, PLL_POSTDIV2);
1416726462dSjmcneill const u_int fracdiv = __SHIFTOUT(con2, PLL_FRACDIV);
1426726462dSjmcneill
1436726462dSjmcneill if (dsmpd == 1) {
1446726462dSjmcneill /* integer mode */
1456726462dSjmcneill foutvco = fref / refdiv * fbdiv;
1466726462dSjmcneill } else {
1476726462dSjmcneill /* fractional mode */
148e70658e1Sjmcneill foutvco = fref / refdiv * fbdiv + ((fref * fracdiv) >> 24);
1496726462dSjmcneill }
1506726462dSjmcneill foutpostdiv = foutvco / postdiv1 / postdiv2;
1516726462dSjmcneill
1526726462dSjmcneill return foutpostdiv;
1536726462dSjmcneill }
15401470923Sjmcneill }
1556726462dSjmcneill
1566726462dSjmcneill int
rk_cru_pll_set_rate(struct rk_cru_softc * sc,struct rk_cru_clk * clk,u_int rate)1576726462dSjmcneill rk_cru_pll_set_rate(struct rk_cru_softc *sc,
1586726462dSjmcneill struct rk_cru_clk *clk, u_int rate)
1596726462dSjmcneill {
1606726462dSjmcneill struct rk_cru_pll *pll = &clk->u.pll;
1616726462dSjmcneill const struct rk_cru_pll_rate *pll_rate = NULL;
1626726462dSjmcneill uint32_t val;
1636726462dSjmcneill int retry;
1646726462dSjmcneill
1656726462dSjmcneill KASSERT(clk->type == RK_CRU_PLL);
1666726462dSjmcneill
1676726462dSjmcneill if (pll->rates == NULL || rate == 0 || !HAS_GRF(sc))
1686726462dSjmcneill return EIO;
1696726462dSjmcneill
1706726462dSjmcneill for (int i = 0; i < pll->nrates; i++)
1716726462dSjmcneill if (pll->rates[i].rate == rate) {
1726726462dSjmcneill pll_rate = &pll->rates[i];
1736726462dSjmcneill break;
1746726462dSjmcneill }
1756726462dSjmcneill if (pll_rate == NULL)
1766726462dSjmcneill return EINVAL;
1776726462dSjmcneill
178*4fb05369Sryo if ((pll->flags & RK_PLL_RK3288) != 0) {
179*4fb05369Sryo /* XXX TODO */
180*4fb05369Sryo KASSERT(false);
181*4fb05369Sryo } else if ((pll->flags & RK_PLL_RK3588) != 0) {
182*4fb05369Sryo bool muxed = false;
18301470923Sjmcneill
184*4fb05369Sryo /* into SLOW mode */
185*4fb05369Sryo if (__SHIFTOUT(CRU_READ(sc, pll->mode_reg), pll->mode_mask) ==
186*4fb05369Sryo PLL_MODE_NORM) {
187*4fb05369Sryo CRU_WRITE(sc, pll->mode_reg,
188*4fb05369Sryo pll->mode_mask << 16 |
189*4fb05369Sryo __SHIFTIN(PLL_MODE_SLOW, pll->mode_mask));
190*4fb05369Sryo muxed = true;
191*4fb05369Sryo }
192*4fb05369Sryo
193*4fb05369Sryo /* power down */
194*4fb05369Sryo CRU_WRITE(sc, pll->con_base + PLL_CON1,
195*4fb05369Sryo RK3588_PLLCON1_PWRDOWN << 16 |
196*4fb05369Sryo __SHIFTIN(1, RK3588_PLLCON1_PWRDOWN));
197*4fb05369Sryo
198*4fb05369Sryo /* update m,p,s,k */
199*4fb05369Sryo CRU_WRITE(sc, pll->con_base + PLL_CON0,
200*4fb05369Sryo RK3588_PLLCON0_M << 16 |
201*4fb05369Sryo __SHIFTIN(pll_rate->m, RK3588_PLLCON0_M));
202*4fb05369Sryo CRU_WRITE(sc, pll->con_base + PLL_CON1,
203*4fb05369Sryo RK3588_PLLCON1_P << 16 |
204*4fb05369Sryo RK3588_PLLCON1_S << 16 |
205*4fb05369Sryo __SHIFTIN(pll_rate->p, RK3588_PLLCON1_P) |
206*4fb05369Sryo __SHIFTIN(pll_rate->s, RK3588_PLLCON1_S));
207*4fb05369Sryo CRU_WRITE(sc, pll->con_base + PLL_CON2,
208*4fb05369Sryo RK3588_PLLCON2_K << 16 |
209*4fb05369Sryo __SHIFTIN(pll_rate->k, RK3588_PLLCON2_K));
210*4fb05369Sryo
211*4fb05369Sryo /* power up */
212*4fb05369Sryo CRU_WRITE(sc, pll->con_base + PLL_CON1,
213*4fb05369Sryo RK3588_PLLCON1_PWRDOWN << 16 |
214*4fb05369Sryo __SHIFTIN(0, RK3588_PLLCON1_PWRDOWN));
215*4fb05369Sryo
216*4fb05369Sryo /* wait */
217*4fb05369Sryo for (retry = 1000; retry > 0; retry--) {
218*4fb05369Sryo if (CRU_READ(sc, pll->con_base + PLL_CON6) &
219*4fb05369Sryo pll->lock_mask) {
220*4fb05369Sryo break;
221*4fb05369Sryo }
222*4fb05369Sryo delay(1);
223*4fb05369Sryo }
224*4fb05369Sryo if (retry == 0)
225*4fb05369Sryo device_printf(sc->sc_dev,
226*4fb05369Sryo "WARNING: %s failed to lock\n", clk->base.name);
227*4fb05369Sryo
228*4fb05369Sryo /* into NORM mode */
229*4fb05369Sryo if (muxed) {
230*4fb05369Sryo CRU_WRITE(sc, pll->mode_reg,
231*4fb05369Sryo pll->mode_mask << 16 |
232*4fb05369Sryo __SHIFTIN(PLL_MODE_NORM, pll->mode_mask));
233*4fb05369Sryo }
234*4fb05369Sryo } else {
2356726462dSjmcneill CRU_WRITE(sc, pll->con_base + PLL_CON0,
2366726462dSjmcneill __SHIFTIN(pll_rate->postdiv1, PLL_POSTDIV1) |
2376726462dSjmcneill __SHIFTIN(pll_rate->fbdiv, PLL_FBDIV) |
2386726462dSjmcneill PLL_WRITE_MASK);
2396726462dSjmcneill
2406726462dSjmcneill CRU_WRITE(sc, pll->con_base + PLL_CON1,
2416726462dSjmcneill __SHIFTIN(pll_rate->dsmpd, PLL_DSMPD) |
2426726462dSjmcneill __SHIFTIN(pll_rate->postdiv2, PLL_POSTDIV2) |
2436726462dSjmcneill __SHIFTIN(pll_rate->refdiv, PLL_REFDIV) |
2446726462dSjmcneill PLL_WRITE_MASK);
2456726462dSjmcneill
2466726462dSjmcneill val = CRU_READ(sc, pll->con_base + PLL_CON2);
2476726462dSjmcneill val &= ~PLL_FRACDIV;
2486726462dSjmcneill val |= __SHIFTIN(pll_rate->fracdiv, PLL_FRACDIV);
2496726462dSjmcneill CRU_WRITE(sc, pll->con_base + PLL_CON2, val);
2506726462dSjmcneill
2516726462dSjmcneill /* Set PLL work mode to normal */
2526726462dSjmcneill const uint32_t write_mask = pll->mode_mask << 16;
2536726462dSjmcneill const uint32_t write_val = pll->mode_mask;
2546726462dSjmcneill CRU_WRITE(sc, pll->mode_reg, write_mask | write_val);
2556726462dSjmcneill
256a515ac42Sjmcneill syscon_lock(sc->sc_grf);
2576726462dSjmcneill for (retry = 1000; retry > 0; retry--) {
258*4fb05369Sryo if (syscon_read_4(sc->sc_grf,
259*4fb05369Sryo sc->sc_grf_soc_status) & pll->lock_mask)
2606726462dSjmcneill break;
2616726462dSjmcneill delay(1);
2626726462dSjmcneill }
263a515ac42Sjmcneill syscon_unlock(sc->sc_grf);
264a515ac42Sjmcneill
2656726462dSjmcneill if (retry == 0)
266*4fb05369Sryo device_printf(sc->sc_dev,
267*4fb05369Sryo "WARNING: %s failed to lock\n", clk->base.name);
268*4fb05369Sryo }
2696726462dSjmcneill
2706726462dSjmcneill return 0;
2716726462dSjmcneill }
2726726462dSjmcneill
2736726462dSjmcneill const char *
rk_cru_pll_get_parent(struct rk_cru_softc * sc,struct rk_cru_clk * clk)2746726462dSjmcneill rk_cru_pll_get_parent(struct rk_cru_softc *sc,
2756726462dSjmcneill struct rk_cru_clk *clk)
2766726462dSjmcneill {
2776726462dSjmcneill struct rk_cru_pll *pll = &clk->u.pll;
2786726462dSjmcneill
2796726462dSjmcneill KASSERT(clk->type == RK_CRU_PLL);
2806726462dSjmcneill
2818c7bec30Sjmcneill return pll->parents[0];
2826726462dSjmcneill }
283