Le vendredi 22 avril 2022 à 19:09 +0200, Sebastian Reichel a écrit : > From: Elaine Zhang <zhangqing@xxxxxxxxxxxxxx> > > Add RK3588 PLL support including calculation of PLL parameters > for arbitrary frequencies. > > Signed-off-by: Elaine Zhang <zhangqing@xxxxxxxxxxxxxx> > [rebase and partially rewrite code] > Signed-off-by: Sebastian Reichel <sebastian.reichel@xxxxxxxxxxxxx> > --- > drivers/clk/rockchip/clk-pll.c | 287 ++++++++++++++++++++++++++++++++- > drivers/clk/rockchip/clk.h | 18 +++ > 2 files changed, 304 insertions(+), 1 deletion(-) > > diff --git a/drivers/clk/rockchip/clk-pll.c b/drivers/clk/rockchip/clk-pll.c > index f7827b3b7fc1..010e47eb51b8 100644 > --- a/drivers/clk/rockchip/clk-pll.c > +++ b/drivers/clk/rockchip/clk-pll.c > @@ -15,6 +15,7 @@ > #include <linux/iopoll.h> > #include <linux/regmap.h> > #include <linux/clk.h> > +#include <linux/units.h> > #include "clk.h" > > #define PLL_MODE_MASK 0x3 > @@ -47,6 +48,67 @@ struct rockchip_clk_pll { > #define to_rockchip_clk_pll_nb(nb) \ > container_of(nb, struct rockchip_clk_pll, clk_nb) > > +static int > +rockchip_rk3588_get_pll_settings(struct rockchip_clk_pll *pll, > + unsigned long fin_hz, > + unsigned long fout_hz, > + struct rockchip_pll_rate_table *rate_table) > +{ > + u64 fvco_min = 2250 * HZ_PER_MHZ, fvco_max = 4500 * HZ_PER_MHZ; > + u64 fout_min = 37 * HZ_PER_MHZ, fout_max = 4500 * HZ_PER_MHZ; > + u32 p, m, s; > + u64 fvco, fref, fout, ffrac; > + > + if (fin_hz == 0 || fout_hz == 0 || fout_hz == fin_hz) > + return -EINVAL; > + > + if (fout_hz > fout_max || fout_hz < fout_min) > + return -EINVAL; > + > + if (fin_hz / HZ_PER_MHZ * HZ_PER_MHZ == fin_hz && > + fout_hz / HZ_PER_MHZ * HZ_PER_MHZ == fout_hz) { > + for (s = 0; s <= 6; s++) { > + fvco = fout_hz << s; > + if (fvco < fvco_min || fvco > fvco_max) > + 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 0; > + } > + } > + } > + } > + } else { > + fout = (fout_hz / HZ_PER_MHZ) * HZ_PER_MHZ; > + ffrac = (fout_hz % HZ_PER_MHZ); > + for (s = 0; s <= 6; s++) { > + fvco = fout << s; > + if (fvco < fvco_min || fvco > fvco_max) > + continue; > + for (p = 1; 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; > + fref = fin_hz / p; > + fout = (ffrac << s) * 65535; > + rate_table->k = fout / fref; > + return 0; > + } > + } > + } > + } > + } > + > + return -EINVAL; > +} > + > static const struct rockchip_pll_rate_table *rockchip_get_pll_settings( > struct rockchip_clk_pll *pll, unsigned long rate) > { > @@ -68,6 +130,14 @@ static long rockchip_pll_round_rate(struct clk_hw *hw, > const struct rockchip_pll_rate_table *rate_table = pll->rate_table; > int i; > > + if (pll->type == pll_rk3588 || pll->type == pll_rk3588_core) { > + long parent_rate = prate ? *prate : 24 * HZ_PER_MHZ; > + struct rockchip_pll_rate_table pll_settings; > + > + if (rockchip_rk3588_get_pll_settings(pll, parent_rate, drate, &pll_settings) >= 0) > + return pll_settings.rate; > + } > + I was reading some of previous backlog on why these formula have never been mainlines [0]. It looks like so far the statu quo was adopted. But if that was to change, I think this implementation is not aligned with the intent. If my understanding is right, the rate_table[] (if it exists) should be looked up first and the formula use as a fallback. [0] https://patchwork.kernel.org/project/linux-rockchip/patch/1470144852-20708-1-git-send-email-zhengxing@xxxxxxxxxxxxxx/#19548765 > /* Assumming rate_table is in descending order */ > for (i = 0; i < pll->rate_count; i++) { > if (drate >= rate_table[i].rate) > @@ -842,6 +912,212 @@ static const struct clk_ops rockchip_rk3399_pll_clk_ops = { > .init = rockchip_rk3399_pll_init, > }; > > +/** > + * PLL used in RK3588 > + */ > + > +#define RK3588_PLLCON(i) (i * 0x4) > +#define RK3588_PLLCON0_M_MASK 0x3ff > +#define RK3588_PLLCON0_M_SHIFT 0 > +#define RK3588_PLLCON1_P_MASK 0x3f > +#define RK3588_PLLCON1_P_SHIFT 0 > +#define RK3588_PLLCON1_S_MASK 0x7 > +#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) > + > +static int rockchip_rk3588_pll_wait_lock(struct rockchip_clk_pll *pll) > +{ > + u32 pllcon; > + int ret; > + > + /* > + * Lock time typical 250, max 500 input clock cycles @24MHz > + * So define a very safe maximum of 1000us, meaning 24000 cycles. > + */ > + ret = readl_relaxed_poll_timeout(pll->reg_base + RK3588_PLLCON(6), > + pllcon, > + pllcon & RK3588_PLLCON6_LOCK_STATUS, > + 0, 1000); > + if (ret) > + pr_err("%s: timeout waiting for pll to lock\n", __func__); > + > + return ret; > +} > + > +static void rockchip_rk3588_pll_get_params(struct rockchip_clk_pll *pll, > + struct rockchip_pll_rate_table *rate) > +{ > + u32 pllcon; > + > + pllcon = readl_relaxed(pll->reg_base + RK3588_PLLCON(0)); > + rate->m = ((pllcon >> RK3588_PLLCON0_M_SHIFT) & RK3588_PLLCON0_M_MASK); > + > + pllcon = readl_relaxed(pll->reg_base + RK3588_PLLCON(1)); > + rate->p = ((pllcon >> RK3588_PLLCON1_P_SHIFT) & RK3588_PLLCON1_P_MASK); > + rate->s = ((pllcon >> RK3588_PLLCON1_S_SHIFT) & RK3588_PLLCON1_S_MASK); > + > + pllcon = readl_relaxed(pll->reg_base + RK3588_PLLCON(2)); > + rate->k = ((pllcon >> RK3588_PLLCON2_K_SHIFT) & RK3588_PLLCON2_K_MASK); > +} > + > +static unsigned long rockchip_rk3588_pll_recalc_rate(struct clk_hw *hw, unsigned long prate) > +{ > + struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw); > + struct rockchip_pll_rate_table cur; > + u64 rate64 = prate, postdiv; > + > + rockchip_rk3588_pll_get_params(pll, &cur); > + > + rate64 *= cur.m; > + do_div(rate64, cur.p); > + > + if (cur.k) { > + /* fractional mode */ > + u64 frac_rate64 = prate * cur.k; > + > + postdiv = cur.p * 65535; > + do_div(frac_rate64, postdiv); > + rate64 += frac_rate64; > + } > + rate64 = rate64 >> cur.s; > + > + return (unsigned long)rate64; > +} > + > +static int rockchip_rk3588_pll_set_params(struct rockchip_clk_pll *pll, > + const struct rockchip_pll_rate_table *rate) > +{ > + const struct clk_ops *pll_mux_ops = pll->pll_mux_ops; > + struct clk_mux *pll_mux = &pll->pll_mux; > + struct rockchip_pll_rate_table cur; > + int rate_change_remuxed = 0; > + int cur_parent; > + int ret; > + > + pr_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); > + > + rockchip_rk3588_pll_get_params(pll, &cur); > + cur.rate = 0; > + > + if (pll->type == pll_rk3588) { > + cur_parent = pll_mux_ops->get_parent(&pll_mux->hw); > + if (cur_parent == PLL_MODE_NORM) { > + pll_mux_ops->set_parent(&pll_mux->hw, PLL_MODE_SLOW); > + rate_change_remuxed = 1; > + } > + } > + > + /* set pll power down */ > + writel(HIWORD_UPDATE(RK3588_PLLCON1_PWRDOWN, > + RK3588_PLLCON1_PWRDOWN, 0), > + pll->reg_base + RK3399_PLLCON(1)); > + > + /* update pll values */ > + writel_relaxed(HIWORD_UPDATE(rate->m, RK3588_PLLCON0_M_MASK, RK3588_PLLCON0_M_SHIFT), > + pll->reg_base + RK3399_PLLCON(0)); > + > + writel_relaxed(HIWORD_UPDATE(rate->p, RK3588_PLLCON1_P_MASK, RK3588_PLLCON1_P_SHIFT) | > + HIWORD_UPDATE(rate->s, RK3588_PLLCON1_S_MASK, RK3588_PLLCON1_S_SHIFT), > + pll->reg_base + RK3399_PLLCON(1)); > + > + writel_relaxed(HIWORD_UPDATE(rate->k, RK3588_PLLCON2_K_MASK, RK3588_PLLCON2_K_SHIFT), > + pll->reg_base + RK3399_PLLCON(2)); > + > + /* set pll power up */ > + writel(HIWORD_UPDATE(0, RK3588_PLLCON1_PWRDOWN, 0), > + pll->reg_base + RK3588_PLLCON(1)); > + > + /* wait for the pll to lock */ > + ret = rockchip_rk3588_pll_wait_lock(pll); > + if (ret) { > + pr_warn("%s: pll update unsuccessful, trying to restore old params\n", > + __func__); > + rockchip_rk3588_pll_set_params(pll, &cur); > + } > + > + if ((pll->type == pll_rk3588) && rate_change_remuxed) > + pll_mux_ops->set_parent(&pll_mux->hw, PLL_MODE_NORM); > + > + return ret; > +} > + > +static int rockchip_rk3588_pll_set_rate(struct clk_hw *hw, unsigned long drate, > + unsigned long prate) > +{ > + struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw); > + struct rockchip_pll_rate_table rate; > + unsigned long old_rate = rockchip_rk3588_pll_recalc_rate(hw, prate); > + > + pr_debug("%s: changing %s from %lu to %lu with a parent rate of %lu\n", > + __func__, __clk_get_name(hw->clk), old_rate, drate, prate); > + > + if (rockchip_rk3588_get_pll_settings(pll, prate, drate, &rate) < 0) { > + pr_err("%s: Invalid rate : %lu for pll clk %s\n", __func__, > + drate, __clk_get_name(hw->clk)); > + return -EINVAL; > + } > + > + return rockchip_rk3588_pll_set_params(pll, &rate); > +} > + > +static int rockchip_rk3588_pll_enable(struct clk_hw *hw) > +{ > + struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw); > + > + writel(HIWORD_UPDATE(0, RK3588_PLLCON1_PWRDOWN, 0), > + pll->reg_base + RK3588_PLLCON(1)); > + rockchip_rk3588_pll_wait_lock(pll); > + > + return 0; > +} > + > +static void rockchip_rk3588_pll_disable(struct clk_hw *hw) > +{ > + struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw); > + > + writel(HIWORD_UPDATE(RK3588_PLLCON1_PWRDOWN, RK3588_PLLCON1_PWRDOWN, 0), > + pll->reg_base + RK3588_PLLCON(1)); > +} > + > +static int rockchip_rk3588_pll_is_enabled(struct clk_hw *hw) > +{ > + struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw); > + u32 pllcon = readl(pll->reg_base + RK3588_PLLCON(1)); > + > + return !(pllcon & RK3588_PLLCON1_PWRDOWN); > +} > + > +static int rockchip_rk3588_pll_init(struct clk_hw *hw) > +{ > + struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw); > + > + if (!(pll->flags & ROCKCHIP_PLL_SYNC_RATE)) > + return 0; > + > + return 0; > +} > + > +static const struct clk_ops rockchip_rk3588_pll_clk_norate_ops = { > + .recalc_rate = rockchip_rk3588_pll_recalc_rate, > + .enable = rockchip_rk3588_pll_enable, > + .disable = rockchip_rk3588_pll_disable, > + .is_enabled = rockchip_rk3588_pll_is_enabled, > +}; > + > +static const struct clk_ops rockchip_rk3588_pll_clk_ops = { > + .recalc_rate = rockchip_rk3588_pll_recalc_rate, > + .round_rate = rockchip_pll_round_rate, > + .set_rate = rockchip_rk3588_pll_set_rate, > + .enable = rockchip_rk3588_pll_enable, > + .disable = rockchip_rk3588_pll_disable, > + .is_enabled = rockchip_rk3588_pll_is_enabled, > + .init = rockchip_rk3588_pll_init, > +}; > + > /* > * Common registering of pll clocks > */ > @@ -890,7 +1166,8 @@ struct clk *rockchip_clk_register_pll(struct rockchip_clk_provider *ctx, > if (pll_type == pll_rk3036 || > pll_type == pll_rk3066 || > pll_type == pll_rk3328 || > - pll_type == pll_rk3399) > + pll_type == pll_rk3399 || > + pll_type == pll_rk3588) > pll_mux->flags |= CLK_MUX_HIWORD_MASK; > > /* the actual muxing is xin24m, pll-output, xin32k */ > @@ -957,6 +1234,14 @@ struct clk *rockchip_clk_register_pll(struct rockchip_clk_provider *ctx, > else > init.ops = &rockchip_rk3399_pll_clk_ops; > break; > + case pll_rk3588: > + case pll_rk3588_core: > + if (!pll->rate_table) > + init.ops = &rockchip_rk3588_pll_clk_norate_ops; > + else > + init.ops = &rockchip_rk3588_pll_clk_ops; > + init.flags = flags; > + break; > default: > pr_warn("%s: Unknown pll type for pll clk %s\n", > __func__, name); > diff --git a/drivers/clk/rockchip/clk.h b/drivers/clk/rockchip/clk.h > index 6aece7f07a7d..bf7c8d082fde 100644 > --- a/drivers/clk/rockchip/clk.h > +++ b/drivers/clk/rockchip/clk.h > @@ -221,6 +221,8 @@ enum rockchip_pll_type { > pll_rk3066, > pll_rk3328, > pll_rk3399, > + pll_rk3588, > + pll_rk3588_core, > }; > > #define RK3036_PLL_RATE(_rate, _refdiv, _fbdiv, _postdiv1, \ > @@ -253,6 +255,15 @@ enum rockchip_pll_type { > .nb = _nb, \ > } > > +#define RK3588_PLL_RATE(_rate, _p, _m, _s, _k) \ > +{ \ > + .rate = _rate##U, \ > + .p = _p, \ > + .m = _m, \ > + .s = _s, \ > + .k = _k, \ > +} > + > /** > * struct rockchip_clk_provider - information about clock provider > * @reg_base: virtual address for the register base. > @@ -288,6 +299,13 @@ struct rockchip_pll_rate_table { > unsigned int dsmpd; > unsigned int frac; > }; > + struct { > + /* for RK3588 */ > + unsigned int m; > + unsigned int p; > + unsigned int s; > + unsigned int k; > + }; > }; > }; > > -- > 2.35.1 > >