summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexey Charkov <alchark@flipper.net>2026-07-23 19:04:27 +0400
committerQuentin Schulz <u-boot@0leil.net>2026-08-12 16:55:03 +0200
commitf75a3d96e1f33c1b8737018843b7bd5fc207b9d4 (patch)
treef48a1660fc06ea9aaf170981b72786e5171663d7
parent3bc88868c237b7b40d87d931474c5739f76b2d13 (diff)
downloadu-boot-f75a3d96e1f33c1b8737018843b7bd5fc207b9d4.tar.gz
u-boot-f75a3d96e1f33c1b8737018843b7bd5fc207b9d4.zip
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 <quentin.schulz@cherry.de> Signed-off-by: Alexey Charkov <alchark@flipper.net> Link: https://patch.msgid.link/20260723-rk3588-fracpll-v2-5-382d113f3a80@flipper.net Signed-off-by: Quentin Schulz <u-boot@0leil.net>
-rw-r--r--arch/arm/include/asm/arch-rockchip/clock.h8
-rw-r--r--drivers/clk/rockchip/clk_pll.c43
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: