From: Rosen Penev <rosenp@gmail.com>
To: linux-mips@vger.kernel.org,
Thomas Bogendoerfer <tsbogend@alpha.franken.de>,
Stephen Boyd <sboyd@kernel.org>
Cc: Brian Masney <bmasney+clk@redhat.com>,
Jerome Brunet <jbrunet+clk@baylibre.com>,
Alban Bedel <albeu@free.fr>,
Antony Pavlov <antonynpavlov@gmail.com>,
linux-kernel@vger.kernel.org (open list),
linux-clk@vger.kernel.org (open list:COMMON CLK FRAMEWORK)
Subject: [PATCH 2/8] MIPS: ath79: move the bootstrap reference clock to setup
Date: Tue, 3 Nov 2026 21:58:46 -0800 [thread overview]
Message-ID: <20261104055852.152340-3-rosenp@gmail.com> (raw)
In-Reply-To: <20261104055852.152340-1-rosenp@gmail.com>
The qca,ath79-pll binding requires the reference clock as an input, and a
rate in the device tree already took precedence over the 25/40 MHz that
the clock code read from the bootstrap pins. Many AR933x boards leave the
rate out, though, and rely on the pins.
Read the pins in plat_time_init() instead, and register the reference
clock from them only when the device tree gives no rate. The clock code
then takes the DT clock or this one through the clk API and no longer
needs ath79_reset_base. When the device tree does give a rate, warn if
it disagrees with the pins: a wrong rate leaves the CPU, DDR and AHB rates
off, the UART baud rate included.
Signed-off-by: Rosen Penev <rosenp@gmail.com>
---
arch/mips/ath79/clock.c | 71 ++++++++-------------------------
arch/mips/ath79/setup.c | 87 +++++++++++++++++++++++++++++++++++++++++
2 files changed, 104 insertions(+), 54 deletions(-)
diff --git a/arch/mips/ath79/clock.c b/arch/mips/ath79/clock.c
index 8c5eaa4ab6e0..85f747cf37a8 100644
--- a/arch/mips/ath79/clock.c
+++ b/arch/mips/ath79/clock.c
@@ -25,9 +25,6 @@
#include <asm/mach-ath79/ar71xx_regs.h>
#include "common.h"
-#define AR71XX_BASE_FREQ 40000000
-#define AR724X_BASE_FREQ 40000000
-
static struct clk *clks[ATH79_CLK_END];
static struct clk_onecell_data clk_data = {
.clks = clks,
@@ -78,16 +75,12 @@ static struct clk * __init ath79_set_ff_clk(int type, const char *parent,
return clk;
}
-static unsigned long __init ath79_setup_ref_clk(unsigned long rate)
+static unsigned long __init ath79_get_ref_rate(void)
{
- struct clk *clk = clks[ATH79_CLK_REF];
-
- if (clk)
- rate = clk_get_rate(clk);
- else
- clk = ath79_set_clk(ATH79_CLK_REF, rate);
+ if (!clks[ATH79_CLK_REF])
+ panic("ath79: no reference clock");
- return rate;
+ return clk_get_rate(clks[ATH79_CLK_REF]);
}
static void __init ar71xx_clocks_init(void __iomem *pll_base)
@@ -100,7 +93,7 @@ static void __init ar71xx_clocks_init(void __iomem *pll_base)
u32 freq;
u32 div;
- ref_rate = ath79_setup_ref_clk(AR71XX_BASE_FREQ);
+ ref_rate = ath79_get_ref_rate();
pll = __raw_readl(pll_base + AR71XX_PLL_REG_CPU_CONFIG);
@@ -126,7 +119,7 @@ static void __init ar724x_clocks_init(void __iomem *pll_base)
u32 mult, div, ddr_div, ahb_div;
u32 pll;
- ath79_setup_ref_clk(AR71XX_BASE_FREQ);
+ ath79_get_ref_rate();
pll = __raw_readl(pll_base + AR724X_PLL_REG_CPU_CONFIG);
@@ -143,7 +136,6 @@ static void __init ar724x_clocks_init(void __iomem *pll_base)
static void __init ar933x_clocks_init(void __iomem *pll_base)
{
- unsigned long ref_rate;
u32 clock_ctrl;
u32 ref_div;
u32 ninit_mul;
@@ -152,15 +144,8 @@ static void __init ar933x_clocks_init(void __iomem *pll_base)
u32 cpu_div;
u32 ddr_div;
u32 ahb_div;
- u32 t;
- t = ath79_reset_rr(AR933X_RESET_REG_BOOTSTRAP);
- if (t & AR933X_BOOTSTRAP_REF_CLK_40)
- ref_rate = (40 * 1000 * 1000);
- else
- ref_rate = (25 * 1000 * 1000);
-
- ath79_setup_ref_clk(ref_rate);
+ ath79_get_ref_rate();
clock_ctrl = __raw_readl(pll_base + AR933X_PLL_CLOCK_CTRL_REG);
if (clock_ctrl & AR933X_PLL_CLOCK_CTRL_BYPASS) {
@@ -237,18 +222,11 @@ static void __init ar934x_clocks_init(void __iomem *pll_base)
unsigned long ahb_rate;
u32 pll, out_div, ref_div, nint, nfrac, frac, clk_ctrl, postdiv;
u32 cpu_pll, ddr_pll;
- u32 bootstrap;
void __iomem *dpll_base;
dpll_base = ioremap(AR934X_SRIF_BASE, AR934X_SRIF_SIZE);
- bootstrap = ath79_reset_rr(AR934X_RESET_REG_BOOTSTRAP);
- if (bootstrap & AR934X_BOOTSTRAP_REF_CLK_40)
- ref_rate = 40 * 1000 * 1000;
- else
- ref_rate = 25 * 1000 * 1000;
-
- ref_rate = ath79_setup_ref_clk(ref_rate);
+ ref_rate = ath79_get_ref_rate();
pll = __raw_readl(dpll_base + AR934X_SRIF_CPU_DPLL2_REG);
if (pll & AR934X_SRIF_DPLL2_LOCAL_PLL) {
@@ -355,15 +333,8 @@ static void __init qca953x_clocks_init(void __iomem *pll_base)
unsigned long ahb_rate;
u32 pll, out_div, ref_div, nint, frac, clk_ctrl, postdiv;
u32 cpu_pll, ddr_pll;
- u32 bootstrap;
-
- bootstrap = ath79_reset_rr(QCA953X_RESET_REG_BOOTSTRAP);
- if (bootstrap & QCA953X_BOOTSTRAP_REF_CLK_40)
- ref_rate = 40 * 1000 * 1000;
- else
- ref_rate = 25 * 1000 * 1000;
- ref_rate = ath79_setup_ref_clk(ref_rate);
+ ref_rate = ath79_get_ref_rate();
pll = __raw_readl(pll_base + QCA953X_PLL_CPU_CONFIG_REG);
out_div = (pll >> QCA953X_PLL_CPU_CONFIG_OUTDIV_SHIFT) &
@@ -438,15 +409,8 @@ static void __init qca955x_clocks_init(void __iomem *pll_base)
unsigned long ahb_rate;
u32 pll, out_div, ref_div, nint, frac, clk_ctrl, postdiv;
u32 cpu_pll, ddr_pll;
- u32 bootstrap;
- bootstrap = ath79_reset_rr(QCA955X_RESET_REG_BOOTSTRAP);
- if (bootstrap & QCA955X_BOOTSTRAP_REF_CLK_40)
- ref_rate = 40 * 1000 * 1000;
- else
- ref_rate = 25 * 1000 * 1000;
-
- ref_rate = ath79_setup_ref_clk(ref_rate);
+ ref_rate = ath79_get_ref_rate();
pll = __raw_readl(pll_base + QCA955X_PLL_CPU_CONFIG_REG);
out_div = (pll >> QCA955X_PLL_CPU_CONFIG_OUTDIV_SHIFT) &
@@ -521,15 +485,8 @@ static void __init qca956x_clocks_init(void __iomem *pll_base)
unsigned long ahb_rate;
u32 pll, out_div, ref_div, nint, hfrac, lfrac, clk_ctrl, postdiv;
u32 cpu_pll, ddr_pll;
- u32 bootstrap;
-
- bootstrap = ath79_reset_rr(QCA956X_RESET_REG_BOOTSTRAP);
- if (bootstrap & QCA956X_BOOTSTRAP_REF_CLK_40)
- ref_rate = 40 * 1000 * 1000;
- else
- ref_rate = 25 * 1000 * 1000;
- ref_rate = ath79_setup_ref_clk(ref_rate);
+ ref_rate = ath79_get_ref_rate();
pll = __raw_readl(pll_base + QCA956X_PLL_CPU_CONFIG_REG);
out_div = (pll >> QCA956X_PLL_CPU_CONFIG_OUTDIV_SHIFT) &
@@ -610,7 +567,13 @@ static void __init ath79_clocks_init_dt(struct device_node *np)
struct clk *ref_clk;
void __iomem *pll_base;
+ /*
+ * The board's crystal. Without a rate in the device tree, the arch
+ * code registers it from the bootstrap pins.
+ */
ref_clk = of_clk_get(np, 0);
+ if (IS_ERR(ref_clk))
+ ref_clk = clk_get_sys(NULL, "ref");
if (!IS_ERR(ref_clk))
clks[ATH79_CLK_REF] = ref_clk;
diff --git a/arch/mips/ath79/setup.c b/arch/mips/ath79/setup.c
index d9e1cc77ad04..dff0fbe07b04 100644
--- a/arch/mips/ath79/setup.c
+++ b/arch/mips/ath79/setup.c
@@ -15,6 +15,9 @@
#include <linux/memblock.h>
#include <linux/err.h>
#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <linux/of.h>
#include <linux/of_clk.h>
#include <linux/of_fdt.h>
#include <linux/irqchip.h>
@@ -235,6 +238,89 @@ void __init plat_mem_setup(void)
pm_power_off = ath79_halt;
}
+static const struct of_device_id ath79_pll_ids[] __initconst = {
+ { .compatible = "qca,ar7100-pll" },
+ { .compatible = "qca,ar7240-pll" },
+ { .compatible = "qca,ar9130-pll" },
+ { .compatible = "qca,ar9330-pll" },
+ { .compatible = "qca,ar9340-pll" },
+ { .compatible = "qca,qca9530-pll" },
+ { .compatible = "qca,qca9550-pll" },
+ { .compatible = "qca,qca9560-pll" },
+ { /* sentinel */ }
+};
+
+/* the crystal that the bootstrap pins select */
+static unsigned long __init ath79_bootstrap_ref_rate(bool *strapped)
+{
+ unsigned int reg;
+ u32 bit;
+
+ *strapped = true;
+ if (soc_is_ar933x()) {
+ reg = AR933X_RESET_REG_BOOTSTRAP;
+ bit = AR933X_BOOTSTRAP_REF_CLK_40;
+ } else if (soc_is_ar934x()) {
+ reg = AR934X_RESET_REG_BOOTSTRAP;
+ bit = AR934X_BOOTSTRAP_REF_CLK_40;
+ } else if (soc_is_qca953x()) {
+ reg = QCA953X_RESET_REG_BOOTSTRAP;
+ bit = QCA953X_BOOTSTRAP_REF_CLK_40;
+ } else if (soc_is_qca955x()) {
+ reg = QCA955X_RESET_REG_BOOTSTRAP;
+ bit = QCA955X_BOOTSTRAP_REF_CLK_40;
+ } else if (soc_is_qca956x() || soc_is_tp9343()) {
+ reg = QCA956X_RESET_REG_BOOTSTRAP;
+ bit = QCA956X_BOOTSTRAP_REF_CLK_40;
+ } else {
+ /* no bootstrap pin for the crystal on AR71xx, AR724x, AR913x */
+ *strapped = false;
+ return 40000000;
+ }
+
+ return (ath79_reset_rr(reg) & bit) ? 40000000 : 25000000;
+}
+
+/*
+ * The clock code takes the reference clock from the device tree. Many
+ * AR933x boards leave its rate out and rely on the bootstrap pins, so
+ * register it from those for them. Otherwise warn if the device tree
+ * disagrees with the pins, as a wrong rate leaves every derived clock off,
+ * the UART baud rate included.
+ */
+static void __init ath79_setup_ref_clk(void)
+{
+ struct device_node *np, *ref_np;
+ unsigned long rate, strap_rate;
+ struct clk *clk;
+ bool strapped;
+ u32 dt_rate;
+
+ strap_rate = ath79_bootstrap_ref_rate(&strapped);
+
+ np = of_find_matching_node(NULL, ath79_pll_ids);
+ ref_np = np ? of_parse_phandle(np, "clocks", 0) : NULL;
+ of_node_put(np);
+
+ if (ref_np && !of_property_read_u32(ref_np, "clock-frequency", &dt_rate)) {
+ of_node_put(ref_np);
+ rate = dt_rate;
+ if (strapped && rate != strap_rate)
+ pr_warn("ath79: reference clock is %lu Hz in the device tree, but the bootstrap pins select %lu Hz\n",
+ rate, strap_rate);
+ return;
+ }
+ of_node_put(ref_np);
+
+ clk = clk_register_fixed_rate(NULL, "ref", NULL, 0, strap_rate);
+ if (IS_ERR(clk)) {
+ pr_err("ath79: failed to register the reference clock\n");
+ return;
+ }
+
+ clk_register_clkdev(clk, "ref", NULL);
+}
+
void __init plat_time_init(void)
{
struct device_node *np;
@@ -254,6 +340,7 @@ void __init plat_time_init(void)
ath79_reset_wr(AR71XX_RESET_REG_MISC_INT_ENABLE, misc);
}
+ ath79_setup_ref_clk();
of_clk_init(NULL);
np = of_get_cpu_node(0, NULL);
--
2.56.0
next prev parent reply other threads:[~2026-11-04 5:58 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-04 5:58 [PATCH 0/8] clk: ath79: move the PLL clock driver to drivers/clk Rosen Penev
2026-11-04 5:58 ` [PATCH 1/8] MIPS: ath79: apply the QCA956x timer workaround in setup Rosen Penev
2026-11-04 5:58 ` Rosen Penev [this message]
2026-11-04 5:58 ` [PATCH 3/8] clk: ath79: move the ath79 clock driver to drivers/clk Rosen Penev
2026-11-04 5:58 ` [PATCH 4/8] clk: ath79: use FIELD_GET() for the PLL register fields Rosen Penev
2026-11-04 5:58 ` [PATCH 5/8] clk: ath79: convert to clk_hw based registration Rosen Penev
2026-11-04 5:58 ` [PATCH 6/8] clk: ath79: fix the error handling Rosen Penev
2026-11-04 5:58 ` [PATCH 7/8] clk: ath79: give each compatible its own init function Rosen Penev
2026-11-04 5:58 ` [PATCH 8/8] clk: ath79: share the fractional PLL calculation Rosen Penev
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261104055852.152340-3-rosenp@gmail.com \
--to=rosenp@gmail.com \
--cc=albeu@free.fr \
--cc=antonynpavlov@gmail.com \
--cc=bmasney+clk@redhat.com \
--cc=jbrunet+clk@baylibre.com \
--cc=linux-clk@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mips@vger.kernel.org \
--cc=sboyd@kernel.org \
--cc=tsbogend@alpha.franken.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®