From f75a3d96e1f33c1b8737018843b7bd5fc207b9d4 Mon Sep 17 00:00:00 2001 From: Alexey Charkov Date: Thu, 23 Jul 2026 19:04:27 +0400 Subject: clk: rockchip: pll: fix overflow and drop manual two's complement on RK3588/RK3576 The TRM defines the fractional PLL adjustment coefficient as a signed two's complement number, 16 bits wide, so store and use it as such to avoid confusion and enable proper signed arithmetics, promoting operands with automatic sign extension. This also fixes an overflow when the current rk3588_pll_get_rate calculates the fractional component in 32 bits before assigning it to a 64-bit holding variable, given that OSC_HZ is 24000000U and k can be from -32768 to 32767. Fixes: b851c006a150 ("clk: rockchip: pll: Add pll_rk3588 type for rk3588") Reviewed-by: Quentin Schulz Signed-off-by: Alexey Charkov Link: https://patch.msgid.link/20260723-rk3588-fracpll-v2-5-382d113f3a80@flipper.net Signed-off-by: Quentin Schulz --- arch/arm/include/asm/arch-rockchip/clock.h | 8 +++--- drivers/clk/rockchip/clk_pll.c | 43 +++++++++++++----------------- 2 files changed, 23 insertions(+), 28 deletions(-) diff --git a/arch/arm/include/asm/arch-rockchip/clock.h b/arch/arm/include/asm/arch-rockchip/clock.h index 95b08bfd046..8d3819778eb 100644 --- a/arch/arm/include/asm/arch-rockchip/clock.h +++ b/arch/arm/include/asm/arch-rockchip/clock.h @@ -101,10 +101,10 @@ struct rockchip_pll_rate_table { unsigned int dsmpd; unsigned int frac; /* for RK3588 */ - unsigned int m; - unsigned int p; - unsigned int s; - unsigned int k; + unsigned int m; /* main divider, 10 bit unsigned */ + unsigned int p; /* pre-divider, 6 bit unsigned */ + unsigned int s; /* scaler, 3 bit unsigned */ + s16 k; /* fractional part, 16 bit two's complement */ }; enum rockchip_pll_type { diff --git a/drivers/clk/rockchip/clk_pll.c b/drivers/clk/rockchip/clk_pll.c index 94d30ccb142..f677525bba2 100644 --- a/drivers/clk/rockchip/clk_pll.c +++ b/drivers/clk/rockchip/clk_pll.c @@ -167,22 +167,34 @@ rockchip_pll_clk_set_by_auto(ulong fin_hz, return rate_table; } +/* + * 2250 MHz <= Fvco <= 4500 MHz + * For Fvco > 3 GHz: period jitter +-1% frac PLL, +-0.75% int PLL + * For Fvco < 3 GHz: period jitter +-2% frac PLL, +-1.50% int PLL + * Fvco = ((m + k / 65536) * Fin) / p + * Fout = ((m + k / 65536) * Fin) / (p * 2^s) + * Fref = Fin / p + * -32768 <= k <= 32767 (only available in frac PLLs, not int PLLs) + */ static void rockchip_rk3588_pll_k_get(struct rockchip_pll_rate_table *rate_table, u64 fref, u64 fvco) { u64 ffrac; - u32 k = 0; + int k; ffrac = fvco - (rate_table->m) * fref; k = ffrac * 65536 / fref; if (k > 32767) { + /* + * The requested rate is closer to the next integer multiplier + * m, so pick it and use a negative fractional coefficient k + */ rate_table->m += 1; ffrac = (rate_table->m) * fref - fvco; /* * Round up to avoid overshooting requested rate for negative k */ - k = DIV64_U64_ROUND_UP(ffrac * 65536, fref); - k = ~k + 1; + k = -(int)DIV64_U64_ROUND_UP(ffrac * 65536, fref); } rate_table->k = k; } @@ -549,9 +561,10 @@ static int rk3588_pll_set_rate(struct rockchip_pll_clock *pll, static ulong rk3588_pll_get_rate(struct rockchip_pll_clock *pll, void __iomem *base, ulong pll_id) { - u32 m, p, s, k; + u32 m, p, s; u32 con = 0, shift, mode; - u64 rate, postdiv; + u64 rate; + s16 k; con = readl(base + pll->mode_offset); shift = pll->mode_shift; @@ -578,25 +591,7 @@ static ulong rk3588_pll_get_rate(struct rockchip_pll_clock *pll, rate = OSC_HZ / p; rate *= m; - if (k & BIT(15)) { - /* fractional mode */ - u64 frac_rate64; - - k = (~(k - 1)) & RK3588_PLLCON2_K_MASK; - frac_rate64 = OSC_HZ * k; - postdiv = p; - postdiv *= 65536; - do_div(frac_rate64, postdiv); - rate -= frac_rate64; - } else { - /* fractional mode */ - u64 frac_rate64 = OSC_HZ * k; - - postdiv = p; - postdiv *= 65536; - do_div(frac_rate64, postdiv); - rate += frac_rate64; - } + rate += div_s64((s64)OSC_HZ * k, p * 65536); rate = rate >> s; return rate; case RKCLK_PLL_MODE_DEEP: -- cgit v1.2.3