Message ID | 20230130145749.177515-7-jagan@edgeble.ai |
---|---|
State | Accepted |
Commit | b851c006a15032e535f80c78509491a42f86a1aa |
Delegated to: | Kever Yang |
Headers | show |
Series | arm: Add Rockchip RK3588 support | expand |
On 2023/1/30 22:57, Jagan Teki wrote: > Add RK3588 pll set and get rate clock support. > > Signed-off-by: Elaine Zhang <zhangqing@rock-chips.com> > Signed-off-by: Jagan Teki <jagan@edgeble.ai> Reviewed-by: Kever Yang <kever.yang@rock-chips.com> Thanks, - Kever > --- > arch/arm/include/asm/arch-rockchip/clock.h | 24 ++ > drivers/clk/rockchip/clk_pll.c | 267 ++++++++++++++++++++- > 2 files changed, 288 insertions(+), 3 deletions(-) > > diff --git a/arch/arm/include/asm/arch-rockchip/clock.h b/arch/arm/include/asm/arch-rockchip/clock.h > index 566bdcc4fa..90e66c7da0 100644 > --- a/arch/arm/include/asm/arch-rockchip/clock.h > +++ b/arch/arm/include/asm/arch-rockchip/clock.h > @@ -22,6 +22,14 @@ enum { > ROCKCHIP_SYSCON_PMUSGRF, > ROCKCHIP_SYSCON_CIC, > ROCKCHIP_SYSCON_MSCH, > + ROCKCHIP_SYSCON_USBGRF, > + ROCKCHIP_SYSCON_PCIE30_PHY_GRF, > + ROCKCHIP_SYSCON_PHP_GRF, > + ROCKCHIP_SYSCON_PIPE_PHY0_GRF, > + ROCKCHIP_SYSCON_PIPE_PHY1_GRF, > + ROCKCHIP_SYSCON_PIPE_PHY2_GRF, > + ROCKCHIP_SYSCON_VOP_GRF, > + ROCKCHIP_SYSCON_VO_GRF, > }; > > /* Standard Rockchip clock numbers */ > @@ -61,6 +69,15 @@ enum rk_clk_id { > .frac = _frac, \ > } > > +#define RK3588_PLL_RATE(_rate, _p, _m, _s, _k) \ > +{ \ > + .rate = _rate##U, \ > + .p = _p, \ > + .m = _m, \ > + .s = _s, \ > + .k = _k, \ > +} > + > struct rockchip_pll_rate_table { > unsigned long rate; > unsigned int nr; > @@ -74,6 +91,11 @@ struct rockchip_pll_rate_table { > unsigned int postdiv2; > unsigned int dsmpd; > unsigned int frac; > + /* for RK3588 */ > + unsigned int m; > + unsigned int p; > + unsigned int s; > + unsigned int k; > }; > > enum rockchip_pll_type { > @@ -82,6 +104,7 @@ enum rockchip_pll_type { > pll_rk3328, > pll_rk3366, > pll_rk3399, > + pll_rk3588, > }; > > struct rockchip_pll_clock { > @@ -171,5 +194,6 @@ int rockchip_get_clk(struct udevice **devp); > * Return: 0 success, or error value > */ > int rockchip_reset_bind(struct udevice *pdev, u32 reg_offset, u32 reg_number); > +int rockchip_get_scmi_clk(struct udevice **devp); > > #endif > diff --git a/drivers/clk/rockchip/clk_pll.c b/drivers/clk/rockchip/clk_pll.c > index 09b97cf57a..d657ef38f3 100644 > --- a/drivers/clk/rockchip/clk_pll.c > +++ b/drivers/clk/rockchip/clk_pll.c > @@ -45,6 +45,10 @@ enum { > > #define MIN_FOUTVCO_FREQ (800 * MHZ) > #define MAX_FOUTVCO_FREQ (2000 * MHZ) > +#define RK3588_VCO_MIN_HZ (2250UL * MHZ) > +#define RK3588_VCO_MAX_HZ (4500UL * MHZ) > +#define RK3588_FOUT_MIN_HZ (37UL * MHZ) > +#define RK3588_FOUT_MAX_HZ (4500UL * MHZ) > > int gcd(int m, int n) > { > @@ -164,6 +168,65 @@ rockchip_pll_clk_set_by_auto(ulong fin_hz, > return rate_table; > } > > +static struct rockchip_pll_rate_table * > +rk3588_pll_clk_set_by_auto(unsigned long fin_hz, > + unsigned long fout_hz) > +{ > + struct rockchip_pll_rate_table *rate_table = &rockchip_auto_table; > + u32 p, m, s; > + ulong fvco, fref, fout, ffrac; > + > + if (fin_hz == 0 || fout_hz == 0 || fout_hz == fin_hz) > + return NULL; > + > + if (fout_hz > RK3588_FOUT_MAX_HZ || fout_hz < RK3588_FOUT_MIN_HZ) > + return NULL; > + > + if (fin_hz / MHZ * MHZ == fin_hz && fout_hz / MHZ * MHZ == fout_hz) { > + for (s = 0; s <= 6; s++) { > + fvco = fout_hz << s; > + if (fvco < RK3588_VCO_MIN_HZ || > + fvco > RK3588_VCO_MAX_HZ) > + continue; > + for (p = 2; p <= 4; p++) { > + for (m = 64; m <= 1023; m++) { > + if (fvco == m * fin_hz / p) { > + rate_table->p = p; > + rate_table->m = m; > + rate_table->s = s; > + rate_table->k = 0; > + return rate_table; > + } > + } > + } > + } > + pr_err("CANNOT FIND Fout by auto,fout = %lu\n", fout_hz); > + } else { > + for (s = 0; s <= 6; s++) { > + fvco = fout_hz << s; > + if (fvco < RK3588_VCO_MIN_HZ || > + fvco > RK3588_VCO_MAX_HZ) > + continue; > + for (p = 1; p <= 4; p++) { > + for (m = 64; m <= 1023; m++) { > + if ((fvco >= m * fin_hz / p) && (fvco < (m + 1) * fin_hz / p)) { > + rate_table->p = p; > + rate_table->m = m; > + rate_table->s = s; > + fref = fin_hz / p; > + ffrac = fvco - (m * fref); > + fout = ffrac * 65536; > + rate_table->k = fout / fref; > + return rate_table; > + } > + } > + } > + } > + pr_err("CANNOT FIND Fout by auto,fout = %lu\n", fout_hz); > + } > + return NULL; > +} > + > static const struct rockchip_pll_rate_table * > rockchip_get_pll_settings(struct rockchip_pll_clock *pll, ulong rate) > { > @@ -174,10 +237,14 @@ rockchip_get_pll_settings(struct rockchip_pll_clock *pll, ulong rate) > break; > rate_table++; > } > - if (rate_table->rate != rate) > - return rockchip_pll_clk_set_by_auto(24 * MHZ, rate); > - else > + if (rate_table->rate != rate) { > + if (pll->type == pll_rk3588) > + return rk3588_pll_clk_set_by_auto(24 * MHZ, rate); > + else > + return rockchip_pll_clk_set_by_auto(24 * MHZ, rate); > + } else { > return rate_table; > + } > } > > static int rk3036_pll_set_rate(struct rockchip_pll_clock *pll, > @@ -296,6 +363,192 @@ static ulong rk3036_pll_get_rate(struct rockchip_pll_clock *pll, > } > } > > +#define RK3588_PLLCON(i) ((i) * 0x4) > +#define RK3588_PLLCON0_M_MASK 0x3ff << 0 > +#define RK3588_PLLCON0_M_SHIFT 0 > +#define RK3588_PLLCON1_P_MASK 0x3f << 0 > +#define RK3588_PLLCON1_P_SHIFT 0 > +#define RK3588_PLLCON1_S_MASK 0x7 << 6 > +#define RK3588_PLLCON1_S_SHIFT 6 > +#define RK3588_PLLCON2_K_MASK 0xffff > +#define RK3588_PLLCON2_K_SHIFT 0 > +#define RK3588_PLLCON1_PWRDOWN BIT(13) > +#define RK3588_PLLCON6_LOCK_STATUS BIT(15) > +#define RK3588_B0PLL_CLKSEL_CON(i) ((i) * 0x4 + 0x50000 + 0x300) > +#define RK3588_B1PLL_CLKSEL_CON(i) ((i) * 0x4 + 0x52000 + 0x300) > +#define RK3588_LPLL_CLKSEL_CON(i) ((i) * 0x4 + 0x58000 + 0x300) > +#define RK3588_CORE_DIV_MASK 0x1f > +#define RK3588_CORE_L02_DIV_SHIFT 0 > +#define RK3588_CORE_L13_DIV_SHIFT 7 > +#define RK3588_CORE_B02_DIV_SHIFT 8 > +#define RK3588_CORE_B13_DIV_SHIFT 0 > + > +static int rk3588_pll_set_rate(struct rockchip_pll_clock *pll, > + void __iomem *base, ulong pll_id, > + ulong drate) > +{ > + const struct rockchip_pll_rate_table *rate; > + > + rate = rockchip_get_pll_settings(pll, drate); > + if (!rate) { > + printf("%s unsupported rate\n", __func__); > + return -EINVAL; > + } > + > + debug("%s: rate settings for %lu p: %d, m: %d, s: %d, k: %d\n", > + __func__, rate->rate, rate->p, rate->m, rate->s, rate->k); > + > + /* > + * When power on or changing PLL setting, > + * we must force PLL into slow mode to ensure output stable clock. > + */ > + if (pll_id == 3) > + rk_clrsetreg(base + 0x84c, 0x1 << 1, 0x1 << 1); > + > + rk_clrsetreg(base + pll->mode_offset, > + pll->mode_mask << pll->mode_shift, > + RKCLK_PLL_MODE_SLOW << pll->mode_shift); > + if (pll_id == 0) > + rk_clrsetreg(base + RK3588_B0PLL_CLKSEL_CON(0), > + pll->mode_mask << 6, > + RKCLK_PLL_MODE_SLOW << 6); > + else if (pll_id == 1) > + rk_clrsetreg(base + RK3588_B1PLL_CLKSEL_CON(0), > + pll->mode_mask << 6, > + RKCLK_PLL_MODE_SLOW << 6); > + else if (pll_id == 2) > + rk_clrsetreg(base + RK3588_LPLL_CLKSEL_CON(5), > + pll->mode_mask << 14, > + RKCLK_PLL_MODE_SLOW << 14); > + > + /* Power down */ > + rk_setreg(base + pll->con_offset + RK3588_PLLCON(1), > + RK3588_PLLCON1_PWRDOWN); > + > + rk_clrsetreg(base + pll->con_offset, > + RK3588_PLLCON0_M_MASK, > + (rate->m << RK3588_PLLCON0_M_SHIFT)); > + rk_clrsetreg(base + pll->con_offset + RK3588_PLLCON(1), > + (RK3588_PLLCON1_P_MASK | > + RK3588_PLLCON1_S_MASK), > + (rate->p << RK3588_PLLCON1_P_SHIFT | > + rate->s << RK3588_PLLCON1_S_SHIFT)); > + if (rate->k) { > + rk_clrsetreg(base + pll->con_offset + RK3588_PLLCON(2), > + RK3588_PLLCON2_K_MASK, > + rate->k << RK3588_PLLCON2_K_SHIFT); > + } > + /* Power up */ > + rk_clrreg(base + pll->con_offset + RK3588_PLLCON(1), > + RK3588_PLLCON1_PWRDOWN); > + > + /* waiting for pll lock */ > + while (!(readl(base + pll->con_offset + RK3588_PLLCON(6)) & > + RK3588_PLLCON6_LOCK_STATUS)) { > + udelay(1); > + debug("%s: wait pll lock, pll_id=%ld\n", __func__, pll_id); > + } > + > + rk_clrsetreg(base + pll->mode_offset, pll->mode_mask << pll->mode_shift, > + RKCLK_PLL_MODE_NORMAL << pll->mode_shift); > + if (pll_id == 0) { > + rk_clrsetreg(base + RK3588_B0PLL_CLKSEL_CON(0), > + pll->mode_mask << 6, > + 2 << 6); > + rk_clrsetreg(base + RK3588_B0PLL_CLKSEL_CON(0), > + RK3588_CORE_DIV_MASK << RK3588_CORE_B02_DIV_SHIFT, > + 0 << RK3588_CORE_B02_DIV_SHIFT); > + rk_clrsetreg(base + RK3588_B0PLL_CLKSEL_CON(1), > + RK3588_CORE_DIV_MASK << RK3588_CORE_B13_DIV_SHIFT, > + 0 << RK3588_CORE_B13_DIV_SHIFT); > + } else if (pll_id == 1) { > + rk_clrsetreg(base + RK3588_B1PLL_CLKSEL_CON(0), > + pll->mode_mask << 6, > + 2 << 6); > + rk_clrsetreg(base + RK3588_B1PLL_CLKSEL_CON(0), > + RK3588_CORE_DIV_MASK << RK3588_CORE_B02_DIV_SHIFT, > + 0 << RK3588_CORE_B02_DIV_SHIFT); > + rk_clrsetreg(base + RK3588_B1PLL_CLKSEL_CON(1), > + RK3588_CORE_DIV_MASK << RK3588_CORE_B13_DIV_SHIFT, > + 0 << RK3588_CORE_B13_DIV_SHIFT); > + } else if (pll_id == 2) { > + rk_clrsetreg(base + RK3588_LPLL_CLKSEL_CON(5), > + pll->mode_mask << 14, > + 2 << 14); > + rk_clrsetreg(base + RK3588_LPLL_CLKSEL_CON(6), > + RK3588_CORE_DIV_MASK << RK3588_CORE_L13_DIV_SHIFT, > + 0 << RK3588_CORE_L13_DIV_SHIFT); > + rk_clrsetreg(base + RK3588_LPLL_CLKSEL_CON(6), > + RK3588_CORE_DIV_MASK << RK3588_CORE_L02_DIV_SHIFT, > + 0 << RK3588_CORE_L02_DIV_SHIFT); > + rk_clrsetreg(base + RK3588_LPLL_CLKSEL_CON(7), > + RK3588_CORE_DIV_MASK << RK3588_CORE_L13_DIV_SHIFT, > + 0 << RK3588_CORE_L13_DIV_SHIFT); > + rk_clrsetreg(base + RK3588_LPLL_CLKSEL_CON(7), > + RK3588_CORE_DIV_MASK << RK3588_CORE_L02_DIV_SHIFT, > + 0 << RK3588_CORE_L02_DIV_SHIFT); > + } > + > + if (pll_id == 3) > + rk_clrsetreg(base + 0x84c, 0x1 << 1, 0); > + > + debug("PLL at %p: con0=%x con1= %x con2= %x mode= %x\n", > + pll, readl(base + pll->con_offset), > + readl(base + pll->con_offset + 0x4), > + readl(base + pll->con_offset + 0x8), > + readl(base + pll->mode_offset)); > + > + return 0; > +} > + > +static ulong rk3588_pll_get_rate(struct rockchip_pll_clock *pll, > + void __iomem *base, ulong pll_id) > +{ > + u32 m, p, s, k; > + u32 con = 0, shift, mode; > + u64 rate, postdiv; > + > + con = readl(base + pll->mode_offset); > + shift = pll->mode_shift; > + if (pll_id == 8) > + mode = RKCLK_PLL_MODE_NORMAL; > + else > + mode = (con & (pll->mode_mask << shift)) >> shift; > + switch (mode) { > + case RKCLK_PLL_MODE_SLOW: > + return OSC_HZ; > + case RKCLK_PLL_MODE_NORMAL: > + /* normal mode */ > + con = readl(base + pll->con_offset); > + m = (con & RK3588_PLLCON0_M_MASK) >> > + RK3588_PLLCON0_M_SHIFT; > + con = readl(base + pll->con_offset + RK3588_PLLCON(1)); > + p = (con & RK3588_PLLCON1_P_MASK) >> > + RK3036_PLLCON0_FBDIV_SHIFT; > + s = (con & RK3588_PLLCON1_S_MASK) >> > + RK3588_PLLCON1_S_SHIFT; > + con = readl(base + pll->con_offset + RK3588_PLLCON(2)); > + k = (con & RK3588_PLLCON2_K_MASK) >> > + RK3588_PLLCON2_K_SHIFT; > + > + rate = OSC_HZ / p; > + rate *= m; > + if (k) { > + /* fractional mode */ > + u64 frac_rate64 = OSC_HZ * k; > + > + postdiv = p * 65536; > + do_div(frac_rate64, postdiv); > + rate += frac_rate64; > + } > + rate = rate >> s; > + return rate; > + case RKCLK_PLL_MODE_DEEP: > + default: > + return 32768; > + } > +} > + > ulong rockchip_pll_get_rate(struct rockchip_pll_clock *pll, > void __iomem *base, > ulong pll_id) > @@ -311,6 +564,10 @@ ulong rockchip_pll_get_rate(struct rockchip_pll_clock *pll, > pll->mode_mask = PLL_RK3328_MODE_MASK; > rate = rk3036_pll_get_rate(pll, base, pll_id); > break; > + case pll_rk3588: > + pll->mode_mask = PLL_MODE_MASK; > + rate = rk3588_pll_get_rate(pll, base, pll_id); > + break; > default: > printf("%s: Unknown pll type for pll clk %ld\n", > __func__, pll_id); > @@ -336,6 +593,10 @@ int rockchip_pll_set_rate(struct rockchip_pll_clock *pll, > pll->mode_mask = PLL_RK3328_MODE_MASK; > ret = rk3036_pll_set_rate(pll, base, pll_id, drate); > break; > + case pll_rk3588: > + pll->mode_mask = PLL_MODE_MASK; > + ret = rk3588_pll_set_rate(pll, base, pll_id, drate); > + break; > default: > printf("%s: Unknown pll type for pll clk %ld\n", > __func__, pll_id);
diff --git a/arch/arm/include/asm/arch-rockchip/clock.h b/arch/arm/include/asm/arch-rockchip/clock.h index 566bdcc4fa..90e66c7da0 100644 --- a/arch/arm/include/asm/arch-rockchip/clock.h +++ b/arch/arm/include/asm/arch-rockchip/clock.h @@ -22,6 +22,14 @@ enum { ROCKCHIP_SYSCON_PMUSGRF, ROCKCHIP_SYSCON_CIC, ROCKCHIP_SYSCON_MSCH, + ROCKCHIP_SYSCON_USBGRF, + ROCKCHIP_SYSCON_PCIE30_PHY_GRF, + ROCKCHIP_SYSCON_PHP_GRF, + ROCKCHIP_SYSCON_PIPE_PHY0_GRF, + ROCKCHIP_SYSCON_PIPE_PHY1_GRF, + ROCKCHIP_SYSCON_PIPE_PHY2_GRF, + ROCKCHIP_SYSCON_VOP_GRF, + ROCKCHIP_SYSCON_VO_GRF, }; /* Standard Rockchip clock numbers */ @@ -61,6 +69,15 @@ enum rk_clk_id { .frac = _frac, \ } +#define RK3588_PLL_RATE(_rate, _p, _m, _s, _k) \ +{ \ + .rate = _rate##U, \ + .p = _p, \ + .m = _m, \ + .s = _s, \ + .k = _k, \ +} + struct rockchip_pll_rate_table { unsigned long rate; unsigned int nr; @@ -74,6 +91,11 @@ struct rockchip_pll_rate_table { unsigned int postdiv2; unsigned int dsmpd; unsigned int frac; + /* for RK3588 */ + unsigned int m; + unsigned int p; + unsigned int s; + unsigned int k; }; enum rockchip_pll_type { @@ -82,6 +104,7 @@ enum rockchip_pll_type { pll_rk3328, pll_rk3366, pll_rk3399, + pll_rk3588, }; struct rockchip_pll_clock { @@ -171,5 +194,6 @@ int rockchip_get_clk(struct udevice **devp); * Return: 0 success, or error value */ int rockchip_reset_bind(struct udevice *pdev, u32 reg_offset, u32 reg_number); +int rockchip_get_scmi_clk(struct udevice **devp); #endif diff --git a/drivers/clk/rockchip/clk_pll.c b/drivers/clk/rockchip/clk_pll.c index 09b97cf57a..d657ef38f3 100644 --- a/drivers/clk/rockchip/clk_pll.c +++ b/drivers/clk/rockchip/clk_pll.c @@ -45,6 +45,10 @@ enum { #define MIN_FOUTVCO_FREQ (800 * MHZ) #define MAX_FOUTVCO_FREQ (2000 * MHZ) +#define RK3588_VCO_MIN_HZ (2250UL * MHZ) +#define RK3588_VCO_MAX_HZ (4500UL * MHZ) +#define RK3588_FOUT_MIN_HZ (37UL * MHZ) +#define RK3588_FOUT_MAX_HZ (4500UL * MHZ) int gcd(int m, int n) { @@ -164,6 +168,65 @@ rockchip_pll_clk_set_by_auto(ulong fin_hz, return rate_table; } +static struct rockchip_pll_rate_table * +rk3588_pll_clk_set_by_auto(unsigned long fin_hz, + unsigned long fout_hz) +{ + struct rockchip_pll_rate_table *rate_table = &rockchip_auto_table; + u32 p, m, s; + ulong fvco, fref, fout, ffrac; + + if (fin_hz == 0 || fout_hz == 0 || fout_hz == fin_hz) + return NULL; + + if (fout_hz > RK3588_FOUT_MAX_HZ || fout_hz < RK3588_FOUT_MIN_HZ) + return NULL; + + if (fin_hz / MHZ * MHZ == fin_hz && fout_hz / MHZ * MHZ == fout_hz) { + for (s = 0; s <= 6; s++) { + fvco = fout_hz << s; + if (fvco < RK3588_VCO_MIN_HZ || + fvco > RK3588_VCO_MAX_HZ) + continue; + for (p = 2; p <= 4; p++) { + for (m = 64; m <= 1023; m++) { + if (fvco == m * fin_hz / p) { + rate_table->p = p; + rate_table->m = m; + rate_table->s = s; + rate_table->k = 0; + return rate_table; + } + } + } + } + pr_err("CANNOT FIND Fout by auto,fout = %lu\n", fout_hz); + } else { + for (s = 0; s <= 6; s++) { + fvco = fout_hz << s; + if (fvco < RK3588_VCO_MIN_HZ || + fvco > RK3588_VCO_MAX_HZ) + continue; + for (p = 1; p <= 4; p++) { + for (m = 64; m <= 1023; m++) { + if ((fvco >= m * fin_hz / p) && (fvco < (m + 1) * fin_hz / p)) { + rate_table->p = p; + rate_table->m = m; + rate_table->s = s; + fref = fin_hz / p; + ffrac = fvco - (m * fref); + fout = ffrac * 65536; + rate_table->k = fout / fref; + return rate_table; + } + } + } + } + pr_err("CANNOT FIND Fout by auto,fout = %lu\n", fout_hz); + } + return NULL; +} + static const struct rockchip_pll_rate_table * rockchip_get_pll_settings(struct rockchip_pll_clock *pll, ulong rate) { @@ -174,10 +237,14 @@ rockchip_get_pll_settings(struct rockchip_pll_clock *pll, ulong rate) break; rate_table++; } - if (rate_table->rate != rate) - return rockchip_pll_clk_set_by_auto(24 * MHZ, rate); - else + if (rate_table->rate != rate) { + if (pll->type == pll_rk3588) + return rk3588_pll_clk_set_by_auto(24 * MHZ, rate); + else + return rockchip_pll_clk_set_by_auto(24 * MHZ, rate); + } else { return rate_table; + } } static int rk3036_pll_set_rate(struct rockchip_pll_clock *pll, @@ -296,6 +363,192 @@ static ulong rk3036_pll_get_rate(struct rockchip_pll_clock *pll, } } +#define RK3588_PLLCON(i) ((i) * 0x4) +#define RK3588_PLLCON0_M_MASK 0x3ff << 0 +#define RK3588_PLLCON0_M_SHIFT 0 +#define RK3588_PLLCON1_P_MASK 0x3f << 0 +#define RK3588_PLLCON1_P_SHIFT 0 +#define RK3588_PLLCON1_S_MASK 0x7 << 6 +#define RK3588_PLLCON1_S_SHIFT 6 +#define RK3588_PLLCON2_K_MASK 0xffff +#define RK3588_PLLCON2_K_SHIFT 0 +#define RK3588_PLLCON1_PWRDOWN BIT(13) +#define RK3588_PLLCON6_LOCK_STATUS BIT(15) +#define RK3588_B0PLL_CLKSEL_CON(i) ((i) * 0x4 + 0x50000 + 0x300) +#define RK3588_B1PLL_CLKSEL_CON(i) ((i) * 0x4 + 0x52000 + 0x300) +#define RK3588_LPLL_CLKSEL_CON(i) ((i) * 0x4 + 0x58000 + 0x300) +#define RK3588_CORE_DIV_MASK 0x1f +#define RK3588_CORE_L02_DIV_SHIFT 0 +#define RK3588_CORE_L13_DIV_SHIFT 7 +#define RK3588_CORE_B02_DIV_SHIFT 8 +#define RK3588_CORE_B13_DIV_SHIFT 0 + +static int rk3588_pll_set_rate(struct rockchip_pll_clock *pll, + void __iomem *base, ulong pll_id, + ulong drate) +{ + const struct rockchip_pll_rate_table *rate; + + rate = rockchip_get_pll_settings(pll, drate); + if (!rate) { + printf("%s unsupported rate\n", __func__); + return -EINVAL; + } + + debug("%s: rate settings for %lu p: %d, m: %d, s: %d, k: %d\n", + __func__, rate->rate, rate->p, rate->m, rate->s, rate->k); + + /* + * When power on or changing PLL setting, + * we must force PLL into slow mode to ensure output stable clock. + */ + if (pll_id == 3) + rk_clrsetreg(base + 0x84c, 0x1 << 1, 0x1 << 1); + + rk_clrsetreg(base + pll->mode_offset, + pll->mode_mask << pll->mode_shift, + RKCLK_PLL_MODE_SLOW << pll->mode_shift); + if (pll_id == 0) + rk_clrsetreg(base + RK3588_B0PLL_CLKSEL_CON(0), + pll->mode_mask << 6, + RKCLK_PLL_MODE_SLOW << 6); + else if (pll_id == 1) + rk_clrsetreg(base + RK3588_B1PLL_CLKSEL_CON(0), + pll->mode_mask << 6, + RKCLK_PLL_MODE_SLOW << 6); + else if (pll_id == 2) + rk_clrsetreg(base + RK3588_LPLL_CLKSEL_CON(5), + pll->mode_mask << 14, + RKCLK_PLL_MODE_SLOW << 14); + + /* Power down */ + rk_setreg(base + pll->con_offset + RK3588_PLLCON(1), + RK3588_PLLCON1_PWRDOWN); + + rk_clrsetreg(base + pll->con_offset, + RK3588_PLLCON0_M_MASK, + (rate->m << RK3588_PLLCON0_M_SHIFT)); + rk_clrsetreg(base + pll->con_offset + RK3588_PLLCON(1), + (RK3588_PLLCON1_P_MASK | + RK3588_PLLCON1_S_MASK), + (rate->p << RK3588_PLLCON1_P_SHIFT | + rate->s << RK3588_PLLCON1_S_SHIFT)); + if (rate->k) { + rk_clrsetreg(base + pll->con_offset + RK3588_PLLCON(2), + RK3588_PLLCON2_K_MASK, + rate->k << RK3588_PLLCON2_K_SHIFT); + } + /* Power up */ + rk_clrreg(base + pll->con_offset + RK3588_PLLCON(1), + RK3588_PLLCON1_PWRDOWN); + + /* waiting for pll lock */ + while (!(readl(base + pll->con_offset + RK3588_PLLCON(6)) & + RK3588_PLLCON6_LOCK_STATUS)) { + udelay(1); + debug("%s: wait pll lock, pll_id=%ld\n", __func__, pll_id); + } + + rk_clrsetreg(base + pll->mode_offset, pll->mode_mask << pll->mode_shift, + RKCLK_PLL_MODE_NORMAL << pll->mode_shift); + if (pll_id == 0) { + rk_clrsetreg(base + RK3588_B0PLL_CLKSEL_CON(0), + pll->mode_mask << 6, + 2 << 6); + rk_clrsetreg(base + RK3588_B0PLL_CLKSEL_CON(0), + RK3588_CORE_DIV_MASK << RK3588_CORE_B02_DIV_SHIFT, + 0 << RK3588_CORE_B02_DIV_SHIFT); + rk_clrsetreg(base + RK3588_B0PLL_CLKSEL_CON(1), + RK3588_CORE_DIV_MASK << RK3588_CORE_B13_DIV_SHIFT, + 0 << RK3588_CORE_B13_DIV_SHIFT); + } else if (pll_id == 1) { + rk_clrsetreg(base + RK3588_B1PLL_CLKSEL_CON(0), + pll->mode_mask << 6, + 2 << 6); + rk_clrsetreg(base + RK3588_B1PLL_CLKSEL_CON(0), + RK3588_CORE_DIV_MASK << RK3588_CORE_B02_DIV_SHIFT, + 0 << RK3588_CORE_B02_DIV_SHIFT); + rk_clrsetreg(base + RK3588_B1PLL_CLKSEL_CON(1), + RK3588_CORE_DIV_MASK << RK3588_CORE_B13_DIV_SHIFT, + 0 << RK3588_CORE_B13_DIV_SHIFT); + } else if (pll_id == 2) { + rk_clrsetreg(base + RK3588_LPLL_CLKSEL_CON(5), + pll->mode_mask << 14, + 2 << 14); + rk_clrsetreg(base + RK3588_LPLL_CLKSEL_CON(6), + RK3588_CORE_DIV_MASK << RK3588_CORE_L13_DIV_SHIFT, + 0 << RK3588_CORE_L13_DIV_SHIFT); + rk_clrsetreg(base + RK3588_LPLL_CLKSEL_CON(6), + RK3588_CORE_DIV_MASK << RK3588_CORE_L02_DIV_SHIFT, + 0 << RK3588_CORE_L02_DIV_SHIFT); + rk_clrsetreg(base + RK3588_LPLL_CLKSEL_CON(7), + RK3588_CORE_DIV_MASK << RK3588_CORE_L13_DIV_SHIFT, + 0 << RK3588_CORE_L13_DIV_SHIFT); + rk_clrsetreg(base + RK3588_LPLL_CLKSEL_CON(7), + RK3588_CORE_DIV_MASK << RK3588_CORE_L02_DIV_SHIFT, + 0 << RK3588_CORE_L02_DIV_SHIFT); + } + + if (pll_id == 3) + rk_clrsetreg(base + 0x84c, 0x1 << 1, 0); + + debug("PLL at %p: con0=%x con1= %x con2= %x mode= %x\n", + pll, readl(base + pll->con_offset), + readl(base + pll->con_offset + 0x4), + readl(base + pll->con_offset + 0x8), + readl(base + pll->mode_offset)); + + return 0; +} + +static ulong rk3588_pll_get_rate(struct rockchip_pll_clock *pll, + void __iomem *base, ulong pll_id) +{ + u32 m, p, s, k; + u32 con = 0, shift, mode; + u64 rate, postdiv; + + con = readl(base + pll->mode_offset); + shift = pll->mode_shift; + if (pll_id == 8) + mode = RKCLK_PLL_MODE_NORMAL; + else + mode = (con & (pll->mode_mask << shift)) >> shift; + switch (mode) { + case RKCLK_PLL_MODE_SLOW: + return OSC_HZ; + case RKCLK_PLL_MODE_NORMAL: + /* normal mode */ + con = readl(base + pll->con_offset); + m = (con & RK3588_PLLCON0_M_MASK) >> + RK3588_PLLCON0_M_SHIFT; + con = readl(base + pll->con_offset + RK3588_PLLCON(1)); + p = (con & RK3588_PLLCON1_P_MASK) >> + RK3036_PLLCON0_FBDIV_SHIFT; + s = (con & RK3588_PLLCON1_S_MASK) >> + RK3588_PLLCON1_S_SHIFT; + con = readl(base + pll->con_offset + RK3588_PLLCON(2)); + k = (con & RK3588_PLLCON2_K_MASK) >> + RK3588_PLLCON2_K_SHIFT; + + rate = OSC_HZ / p; + rate *= m; + if (k) { + /* fractional mode */ + u64 frac_rate64 = OSC_HZ * k; + + postdiv = p * 65536; + do_div(frac_rate64, postdiv); + rate += frac_rate64; + } + rate = rate >> s; + return rate; + case RKCLK_PLL_MODE_DEEP: + default: + return 32768; + } +} + ulong rockchip_pll_get_rate(struct rockchip_pll_clock *pll, void __iomem *base, ulong pll_id) @@ -311,6 +564,10 @@ ulong rockchip_pll_get_rate(struct rockchip_pll_clock *pll, pll->mode_mask = PLL_RK3328_MODE_MASK; rate = rk3036_pll_get_rate(pll, base, pll_id); break; + case pll_rk3588: + pll->mode_mask = PLL_MODE_MASK; + rate = rk3588_pll_get_rate(pll, base, pll_id); + break; default: printf("%s: Unknown pll type for pll clk %ld\n", __func__, pll_id); @@ -336,6 +593,10 @@ int rockchip_pll_set_rate(struct rockchip_pll_clock *pll, pll->mode_mask = PLL_RK3328_MODE_MASK; ret = rk3036_pll_set_rate(pll, base, pll_id, drate); break; + case pll_rk3588: + pll->mode_mask = PLL_MODE_MASK; + ret = rk3588_pll_set_rate(pll, base, pll_id, drate); + break; default: printf("%s: Unknown pll type for pll clk %ld\n", __func__, pll_id);