From 7a72bc05dcc3a51e85ae531749e6270bf9b9212d Mon Sep 17 00:00:00 2001 From: Elaine Zhang Date: Thu, 3 Nov 2022 17:49:30 +0800 Subject: [PATCH] clk: rockchip: rk3588: fix up the frac pll calculation rk3588 frac pll: FFVCO = ((m + k / 65536) * FFIN) / p FFOUT = ((m + k / 65536) * FFIN) / (p * 2s) k is the original code, but the K[15:0] is complement code (6'b1000_0000_0000_0000 <= K[15:0] <= 16'b0111_1111_1111_1111), need to be converted. Signed-off-by: Elaine Zhang Change-Id: I107d31d910d260c83891d5b6e927f119761d6fba --- drivers/clk/rockchip/clk-pll.c | 172 ++++++++++++++++++++++++------ drivers/clk/rockchip/clk-rk3588.c | 6 +- 2 files changed, 141 insertions(+), 37 deletions(-) diff --git a/drivers/clk/rockchip/clk-pll.c b/drivers/clk/rockchip/clk-pll.c index 91a067401967..8008ee92be41 100644 --- a/drivers/clk/rockchip/clk-pll.c +++ b/drivers/clk/rockchip/clk-pll.c @@ -332,6 +332,64 @@ rockchip_rk3066_pll_clk_set_by_auto(struct rockchip_clk_pll *pll, return rate_table; } +static u32 +rockchip_rk3588_pll_frac_get(u32 m, u32 p, u32 s, u64 fin_hz, u64 fvco) +{ + u64 fref, fout, ffrac; + u32 k = 0; + + fref = fin_hz / p; + ffrac = fvco - (m * fref); + fout = ffrac * 65536; + k = fout / fref; + if (k > 32767) { + fref = fin_hz / p; + ffrac = ((m + 1) * fref) - fvco; + fout = ffrac * 65536; + k = ((fout * 10 / fref) + 7) / 10; + if (k > 32767) + k = 0; + else + k = ~k + 1; + } + return k; +} + +static struct rockchip_pll_rate_table * +rockchip_rk3588_pll_frac_by_auto(unsigned long fin_hz, unsigned long fout_hz) +{ + struct rockchip_pll_rate_table *rate_table = rk_pll_rate_table_get(); + u64 fvco_min = 2250 * MHZ, fvco_max = 4500 * MHZ; + u32 p, m, s, k; + u64 fvco; + + for (s = 0; s <= 6; s++) { + fvco = (u64)fout_hz << 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) && (fvco < (m + 1) * fin_hz / p)) { + k = rockchip_rk3588_pll_frac_get(m, p, s, + (u64)fin_hz, + fvco); + if (!k) + continue; + rate_table->p = p; + rate_table->s = s; + rate_table->k = k; + if (k > 32767) + rate_table->m = m + 1; + else + rate_table->m = m; + return rate_table; + } + } + } + } + return NULL; +} + static struct rockchip_pll_rate_table * rockchip_rk3588_pll_clk_set_by_auto(struct rockchip_clk_pll *pll, unsigned long fin_hz, @@ -341,7 +399,7 @@ rockchip_rk3588_pll_clk_set_by_auto(struct rockchip_clk_pll *pll, u64 fvco_min = 2250 * MHZ, fvco_max = 4500 * MHZ; u64 fout_min = 37 * MHZ, fout_max = 4500 * MHZ; u32 p, m, s; - u64 fvco, fref, fout, ffrac; + u64 fvco; if (fin_hz == 0 || fout_hz == 0 || fout_hz == fin_hz) return NULL; @@ -368,26 +426,11 @@ rockchip_rk3588_pll_clk_set_by_auto(struct rockchip_clk_pll *pll, } pr_err("CANNOT FIND Fout by auto,fout = %lu\n", fout_hz); } else { - for (s = 0; s <= 6; s++) { - fvco = (u64)fout_hz << 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) && (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); + rate_table = rockchip_rk3588_pll_frac_by_auto(fin_hz, fout_hz); + if (!rate_table) + pr_err("CANNOT FIND Fout by auto,fout = %lu\n", fout_hz); + else + return rate_table; } return NULL; } @@ -1345,7 +1388,17 @@ static unsigned long rockchip_rk3588_pll_recalc_rate(struct clk_hw *hw, rate64 *= cur.m; do_div(rate64, cur.p); - if (cur.k) { + if (cur.k & BIT(15)) { + /* fractional mode */ + u64 frac_rate64; + + cur.k = (~(cur.k - 1)) & RK3588_PLLCON2_K_MASK; + frac_rate64 = prate * cur.k; + postdiv = cur.p; + postdiv *= 65536; + do_div(frac_rate64, postdiv); + rate64 -= frac_rate64; + } else { /* fractional mode */ u64 frac_rate64 = prate * cur.k; @@ -1516,7 +1569,7 @@ int rockchip_pll_clk_compensation(struct clk *clk, int ppm) { struct clk *parent = clk_get_parent(clk); struct rockchip_clk_pll *pll; - static u32 frac, fbdiv; + static u32 frac, fbdiv, s, p; bool negative; u32 pllcon, pllcon0, pllcon2, fbdiv_mask, frac_mask, frac_shift; u64 fracdiv, m, n; @@ -1567,11 +1620,6 @@ int rockchip_pll_clk_compensation(struct clk *clk, int ppm) negative = !!(ppm & BIT(31)); ppm = negative ? ~ppm + 1 : ppm; - if (!frac) { - frac = readl_relaxed(pll->reg_base + pllcon2) & frac_mask; - fbdiv = readl_relaxed(pll->reg_base + pllcon0) & fbdiv_mask; - } - switch (pll->type) { case pll_rk3036: case pll_rk3328: @@ -1583,6 +1631,10 @@ int rockchip_pll_clk_compensation(struct clk *clk, int ppm) * 1 << 24 1 << 24 1000000 * */ + if (!frac) { + frac = readl_relaxed(pll->reg_base + pllcon2) & frac_mask; + fbdiv = readl_relaxed(pll->reg_base + pllcon0) & fbdiv_mask; + } m = div64_u64((uint64_t)frac * ppm, 1000000); n = div64_u64((uint64_t)ppm << 24, 1000000) * fbdiv; @@ -1597,13 +1649,65 @@ int rockchip_pll_clk_compensation(struct clk *clk, int ppm) writel_relaxed(pllcon, pll->reg_base + pllcon2); break; case pll_rk3588: - m = div64_u64((uint64_t)frac * ppm, 100000); - n = div64_u64((uint64_t)ppm * 65535 * fbdiv, 100000); + if (!fbdiv) { + frac = readl_relaxed(pll->reg_base + pllcon2) & frac_mask; + fbdiv = readl_relaxed(pll->reg_base + pllcon0) & fbdiv_mask; + } + if (!frac) { + pllcon = readl_relaxed(pll->reg_base + RK3588_PLLCON(1)); + s = ((pllcon >> RK3588_PLLCON1_S_SHIFT) + & RK3588_PLLCON1_S_MASK); + p = ((pllcon >> RK3588_PLLCON1_P_SHIFT) + & RK3588_PLLCON1_P_MASK); + m = div64_u64((uint64_t)clk_get_rate(clk) * ppm, 24000000); + n = div64_u64((uint64_t)m * 65536 * p * (1 << s), 1000000); - fracdiv = negative ? frac - (div64_u64(m + n, 10)) : frac + (div64_u64(m + n, 10)); - - if (!frac || fracdiv > frac_mask) - return -EINVAL; + if (n > 32767) + return -EINVAL; + fracdiv = negative ? ~n + 1 : n; + } else if (frac & BIT(15)) { + frac = (~(frac - 1)) & RK3588_PLLCON2_K_MASK; + m = div64_u64((uint64_t)frac * ppm, 100000); + n = div64_u64((uint64_t)ppm * 65536 * fbdiv, 100000); + if (negative) { + fracdiv = frac + (div64_u64(m + n, 10)); + if (fracdiv > 32767) + return -EINVAL; + fracdiv = ~fracdiv + 1; + } else { + s = div64_u64(m + n, 10); + if (frac >= s) { + fracdiv = frac - s; + if (fracdiv > 32767) + return -EINVAL; + fracdiv = ~fracdiv + 1; + } else { + fracdiv = s - frac; + if (fracdiv > 32767) + return -EINVAL; + } + } + } else { + m = div64_u64((uint64_t)frac * ppm, 100000); + n = div64_u64((uint64_t)ppm * 65536 * fbdiv, 100000); + if (!negative) { + fracdiv = frac + (div64_u64(m + n, 10)); + if (fracdiv > 32767) + return -EINVAL; + } else { + s = div64_u64(m + n, 10); + if (frac >= s) { + fracdiv = frac - s; + if (fracdiv > 32767) + return -EINVAL; + } else { + fracdiv = s - frac; + if (fracdiv > 32767) + return -EINVAL; + fracdiv = ~fracdiv + 1; + } + } + } writel_relaxed(HIWORD_UPDATE(fracdiv, frac_mask, frac_shift), pll->reg_base + pllcon2); diff --git a/drivers/clk/rockchip/clk-rk3588.c b/drivers/clk/rockchip/clk-rk3588.c index 28c23e962c57..4b03505cfb90 100644 --- a/drivers/clk/rockchip/clk-rk3588.c +++ b/drivers/clk/rockchip/clk-rk3588.c @@ -79,16 +79,16 @@ static struct rockchip_pll_rate_table rk3588_pll_rates[] = { RK3588_PLL_RATE(1008000000, 2, 336, 2, 0), RK3588_PLL_RATE(1000000000, 3, 500, 2, 0), RK3588_PLL_RATE(983040000, 4, 655, 2, 23592), - RK3588_PLL_RATE(955520000, 3, 477, 2, 49806), + RK3588_PLL_RATE(955520000, 3, 478, 2, 49807), RK3588_PLL_RATE(903168000, 6, 903, 2, 11009), RK3588_PLL_RATE(900000000, 2, 300, 2, 0), RK3588_PLL_RATE(816000000, 2, 272, 2, 0), RK3588_PLL_RATE(786432000, 2, 262, 2, 9437), RK3588_PLL_RATE(786000000, 1, 131, 2, 0), - RK3588_PLL_RATE(785560000, 3, 392, 2, 51117), + RK3588_PLL_RATE(785560000, 3, 393, 2, 51119), RK3588_PLL_RATE(722534400, 8, 963, 2, 24850), RK3588_PLL_RATE(600000000, 2, 200, 2, 0), - RK3588_PLL_RATE(594000000, 2, 198, 2, 0), + RK3588_PLL_RATE(594000000, 1, 99, 2, 0), RK3588_PLL_RATE(408000000, 2, 272, 3, 0), RK3588_PLL_RATE(312000000, 2, 208, 3, 0), RK3588_PLL_RATE(216000000, 2, 288, 4, 0),