[PATCH 8/8] clk: ath79: share the fractional PLL calculation
From: Rosen Penev
Date: Wed Nov 04 2026 - 01:04:04 EST
AR934x, QCA953x and QCA955x compute their CPU and DDR PLL rates with the
same formula, ref / ref_div * (nint + nfrac / frac) / 2^out_div, written
three different ways. Use one helper for all of them, with 64-bit
intermediates.
That fixes the QCA955x DDR PLL rate, whose nfrac * ref product could
overflow 32 bits: with a 40 MHz reference clock it did for any nfrac
above 107, and with 25 MHz for any above 171. The other rates are
unchanged, as checked against the old code with random register values.
Signed-off-by: Rosen Penev <rosenp@xxxxxxxxx>
---
drivers/clk/clk-ath79.c | 47 +++++++++++++----------------------------
1 file changed, 15 insertions(+), 32 deletions(-)
diff --git a/drivers/clk/clk-ath79.c b/drivers/clk/clk-ath79.c
index 8086b4f6378c..f9a8c9b9aa24 100644
--- a/drivers/clk/clk-ath79.c
+++ b/drivers/clk/clk-ath79.c
@@ -10,6 +10,7 @@
#include <linux/bitfield.h>
#include <linux/kernel.h>
+#include <linux/math64.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/err.h>
@@ -21,7 +22,6 @@
#include <linux/slab.h>
#include <dt-bindings/clock/ath79-clk.h>
-#include <asm/div64.h>
#include "clk-ath79.h"
@@ -189,24 +189,17 @@ static int __init ar933x_clocks_init(void __iomem *pll_base)
ref_div * out_div * ahb_div);
}
-static u32 __init ar934x_get_pll_freq(u32 ref, u32 ref_div, u32 nint, u32 nfrac,
- u32 frac, u32 out_div)
+/* a fractional PLL: ref / ref_div * (nint + nfrac / frac) / 2^out_div */
+static unsigned long __init ath79_pll_rate(unsigned long ref, u32 ref_div,
+ u32 nint, u32 nfrac, u32 frac,
+ u32 out_div)
{
- u64 t;
- u32 ret;
+ unsigned long rate;
- t = ref;
- t *= nint;
- do_div(t, ref_div);
- ret = t;
+ rate = div_u64((u64)ref * nint, ref_div);
+ rate += div_u64((u64)ref * nfrac, ref_div * frac);
- t = ref;
- t *= nfrac;
- do_div(t, ref_div * frac);
- ret += t;
-
- ret /= (1 << out_div);
- return ret;
+ return rate >> out_div;
}
static int __init ar934x_clocks_init(void __iomem *pll_base)
@@ -243,8 +236,7 @@ static int __init ar934x_clocks_init(void __iomem *pll_base)
frac = 1 << 6;
}
- cpu_pll = ar934x_get_pll_freq(ref_rate, ref_div, nint,
- nfrac, frac, out_div);
+ cpu_pll = ath79_pll_rate(ref_rate, ref_div, nint, nfrac, frac, out_div);
pll = __raw_readl(dpll_base + AR934X_SRIF_DDR_DPLL2_REG);
if (pll & AR934X_SRIF_DPLL2_LOCAL_PLL) {
@@ -263,8 +255,7 @@ static int __init ar934x_clocks_init(void __iomem *pll_base)
frac = 1 << 10;
}
- ddr_pll = ar934x_get_pll_freq(ref_rate, ref_div, nint,
- nfrac, frac, out_div);
+ ddr_pll = ath79_pll_rate(ref_rate, ref_div, nint, nfrac, frac, out_div);
iounmap(dpll_base);
@@ -325,9 +316,7 @@ static int __init qca953x_clocks_init(void __iomem *pll_base)
nint = FIELD_GET(QCA953X_PLL_CPU_CONFIG_NINT, pll);
frac = FIELD_GET(QCA953X_PLL_CPU_CONFIG_NFRAC, pll);
- cpu_pll = nint * ref_rate / ref_div;
- cpu_pll += frac * (ref_rate >> 6) / ref_div;
- cpu_pll /= (1 << out_div);
+ cpu_pll = ath79_pll_rate(ref_rate, ref_div, nint, frac, 1 << 6, out_div);
pll = __raw_readl(pll_base + QCA953X_PLL_DDR_CONFIG_REG);
out_div = FIELD_GET(QCA953X_PLL_DDR_CONFIG_OUTDIV, pll);
@@ -335,9 +324,7 @@ static int __init qca953x_clocks_init(void __iomem *pll_base)
nint = FIELD_GET(QCA953X_PLL_DDR_CONFIG_NINT, pll);
frac = FIELD_GET(QCA953X_PLL_DDR_CONFIG_NFRAC, pll);
- ddr_pll = nint * ref_rate / ref_div;
- ddr_pll += frac * (ref_rate >> 6) / (ref_div << 4);
- ddr_pll /= (1 << out_div);
+ ddr_pll = ath79_pll_rate(ref_rate, ref_div, nint, frac, 1 << 10, out_div);
clk_ctrl = __raw_readl(pll_base + QCA953X_PLL_CLK_CTRL_REG);
@@ -388,9 +375,7 @@ static int __init qca955x_clocks_init(void __iomem *pll_base)
nint = FIELD_GET(QCA955X_PLL_CPU_CONFIG_NINT, pll);
frac = FIELD_GET(QCA955X_PLL_CPU_CONFIG_NFRAC, pll);
- cpu_pll = nint * ref_rate / ref_div;
- cpu_pll += frac * ref_rate / (ref_div * (1 << 6));
- cpu_pll /= (1 << out_div);
+ cpu_pll = ath79_pll_rate(ref_rate, ref_div, nint, frac, 1 << 6, out_div);
pll = __raw_readl(pll_base + QCA955X_PLL_DDR_CONFIG_REG);
out_div = FIELD_GET(QCA955X_PLL_DDR_CONFIG_OUTDIV, pll);
@@ -398,9 +383,7 @@ static int __init qca955x_clocks_init(void __iomem *pll_base)
nint = FIELD_GET(QCA955X_PLL_DDR_CONFIG_NINT, pll);
frac = FIELD_GET(QCA955X_PLL_DDR_CONFIG_NFRAC, pll);
- ddr_pll = nint * ref_rate / ref_div;
- ddr_pll += frac * ref_rate / (ref_div * (1 << 10));
- ddr_pll /= (1 << out_div);
+ ddr_pll = ath79_pll_rate(ref_rate, ref_div, nint, frac, 1 << 10, out_div);
clk_ctrl = __raw_readl(pll_base + QCA955X_PLL_CLK_CTRL_REG);
--
2.56.0