xref: /netbsd-src/sys/arch/arm/rockchip/rk_cru_pll.c (revision 4fb053697f9d68002b95c174d5a1bc8eedbd6977)
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