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 <[email protected]>
Signed-off-by: Alexey Charkov <[email protected]>
---
 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 95b08bfd046f..8d3819778eb1 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 94d30ccb1425..f677525bba22 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:

-- 
2.54.0

Reply via email to