[PATCH v2 3/3] clk: socfpga: stratix10: register the SP timer clocks early

From: Adrian Ng Ho Yin

Date: Fri Oct 02 2026 - 06:29:04 EST


The DW APB timers are probed through TIMER_OF_DECLARE() from time_init()
and cannot defer, but the clock manager is a platform driver registered
at core_initcall(). The timer's clk_get() therefore fails and the timer
is never brought up.

Register only l4_sp_clk and its parents (boot_clk, main_pll, periph_pll,
main_noc_base_clk, peri_noc_base_clk and noc_free_clk) from
of_clk_init() using CLK_OF_DECLARE_DRIVER(), with every other clock left
as -EPROBE_DEFER. The remaining clocks are still registered when the
platform driver probes, which reuses the early provider.

Signed-off-by: Adrian Ng Ho Yin <adrian.ho.yin.ng@xxxxxxxxxx>
---
drivers/clk/socfpga/clk-s10.c | 133 +++++++++++++++++++++++++++-------
1 file changed, 107 insertions(+), 26 deletions(-)

diff --git a/drivers/clk/socfpga/clk-s10.c b/drivers/clk/socfpga/clk-s10.c
index b4bf4e2d38e1..7eae2dfa408d 100644
--- a/drivers/clk/socfpga/clk-s10.c
+++ b/drivers/clk/socfpga/clk-s10.c
@@ -4,7 +4,9 @@
*/
#include <linux/slab.h>
#include <linux/clk-provider.h>
+#include <linux/io.h>
#include <linux/of.h>
+#include <linux/of_address.h>
#include <linux/platform_device.h>

#include <dt-bindings/clock/stratix10-clock.h>
@@ -188,20 +190,36 @@ static const struct stratix10_pll_clock s10_pll_clks[] = {
0, 0xe4},
};

+/*
+ * The SP timers are probed from time_init() and cannot defer, so their
+ * l4_sp_clk and its parents are registered from of_clk_init(). Everything
+ * else is registered when the platform driver probes.
+ */
+static const struct stratix10_perip_c_clock s10_early_perip_c_clks[] = {
+ { STRATIX10_MAIN_NOC_BASE_CLK, "main_noc_base_clk", "main_pll", NULL, 1, 0, 0x88},
+ { STRATIX10_PERI_NOC_BASE_CLK, "peri_noc_base_clk", "periph_pll", NULL, 1, 0,
+ 0xF8},
+};
+
+static const struct stratix10_perip_cnt_clock s10_early_perip_cnt_clks[] = {
+ { STRATIX10_NOC_FREE_CLK, "noc_free_clk", NULL, noc_free_mux, ARRAY_SIZE(noc_free_mux),
+ 0, 0x4C, 0, 0x3C, 1},
+};
+
+static const struct stratix10_gate_clock s10_early_gate_clks[] = {
+ { STRATIX10_L4_SP_CLK, "l4_sp_clk", NULL, noc_mux, ARRAY_SIZE(noc_mux),
+ CLK_IS_CRITICAL, 0x30, 3, 0x70, 16, 2, 0x3C, 1, 0},
+};
+
static const struct stratix10_perip_c_clock s10_main_perip_c_clks[] = {
{ STRATIX10_MAIN_MPU_BASE_CLK, "main_mpu_base_clk", "main_pll", NULL, 1, 0, 0x84},
- { STRATIX10_MAIN_NOC_BASE_CLK, "main_noc_base_clk", "main_pll", NULL, 1, 0, 0x88},
{ STRATIX10_PERI_MPU_BASE_CLK, "peri_mpu_base_clk", "periph_pll", NULL, 1, 0,
0xF4},
- { STRATIX10_PERI_NOC_BASE_CLK, "peri_noc_base_clk", "periph_pll", NULL, 1, 0,
- 0xF8},
};

static const struct stratix10_perip_cnt_clock s10_main_perip_cnt_clks[] = {
{ STRATIX10_MPU_FREE_CLK, "mpu_free_clk", NULL, mpu_free_mux, ARRAY_SIZE(mpu_free_mux),
0, 0x48, 0, 0, 0},
- { STRATIX10_NOC_FREE_CLK, "noc_free_clk", NULL, noc_free_mux, ARRAY_SIZE(noc_free_mux),
- 0, 0x4C, 0, 0x3C, 1},
{ STRATIX10_MAIN_EMACA_CLK, "main_emaca_clk", "main_noc_base_clk", NULL, 1, 0,
0x50, 0, 0, 0},
{ STRATIX10_MAIN_EMACB_CLK, "main_emacb_clk", "main_noc_base_clk", NULL, 1, 0,
@@ -263,8 +281,6 @@ static const struct stratix10_gate_clock s10_gate_clks[] = {
1, 0x70, 0, 2, 0x3C, 1, 0},
{ STRATIX10_L4_MP_CLK, "l4_mp_clk", NULL, noc_mux, ARRAY_SIZE(noc_mux), 0, 0x30,
2, 0x70, 8, 2, 0x3C, 1, 0},
- { STRATIX10_L4_SP_CLK, "l4_sp_clk", NULL, noc_mux, ARRAY_SIZE(noc_mux), CLK_IS_CRITICAL, 0x30,
- 3, 0x70, 16, 2, 0x3C, 1, 0},
{ STRATIX10_CS_AT_CLK, "cs_at_clk", NULL, noc_mux, ARRAY_SIZE(noc_mux), 0, 0x30,
4, 0x70, 24, 2, 0x3C, 1, 0},
{ STRATIX10_CS_TRACE_CLK, "cs_trace_clk", NULL, noc_mux, ARRAY_SIZE(noc_mux), 0, 0x30,
@@ -382,33 +398,91 @@ static int s10_clk_register_pll(const struct stratix10_pll_clock *clks,
return 0;
}

-static int s10_clkmgr_init(struct platform_device *pdev)
+static struct stratix10_clock_data *s10_clk_data;
+
+static struct stratix10_clock_data *s10_clk_data_alloc(void __iomem *base)
+{
+ struct stratix10_clock_data *clk_data;
+ int i;
+
+ clk_data = kzalloc(struct_size(clk_data, clk_data.hws, STRATIX10_NUM_CLKS),
+ GFP_KERNEL);
+ if (!clk_data)
+ return NULL;
+
+ clk_data->base = base;
+ clk_data->clk_data.num = STRATIX10_NUM_CLKS;
+
+ for (i = 0; i < STRATIX10_NUM_CLKS; i++)
+ clk_data->clk_data.hws[i] = ERR_PTR(-EPROBE_DEFER);
+
+ return clk_data;
+}
+
+static void s10_clk_register_early(struct stratix10_clock_data *clk_data)
+{
+ s10_clk_register_pll(s10_pll_clks, ARRAY_SIZE(s10_pll_clks), clk_data);
+
+ s10_clk_register_c_perip(s10_early_perip_c_clks,
+ ARRAY_SIZE(s10_early_perip_c_clks), clk_data);
+
+ s10_clk_register_cnt_perip(s10_early_perip_cnt_clks,
+ ARRAY_SIZE(s10_early_perip_cnt_clks),
+ clk_data);
+
+ s10_clk_register_gate(s10_early_gate_clks,
+ ARRAY_SIZE(s10_early_gate_clks), clk_data);
+}
+
+static void __init s10_clkmgr_early_init(struct device_node *np)
{
- struct device_node *np = pdev->dev.of_node;
- struct device *dev = &pdev->dev;
struct stratix10_clock_data *clk_data;
void __iomem *base;
- int i, num_clks;

- base = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(base)) {
+ base = of_iomap(np, 0);
+ if (!base) {
pr_err("%s: failed to map clock registers\n", __func__);
- return PTR_ERR(base);
+ return;
}

- num_clks = STRATIX10_NUM_CLKS;
- clk_data = devm_kzalloc(dev, struct_size(clk_data, clk_data.hws,
- num_clks), GFP_KERNEL);
- if (!clk_data)
- return -ENOMEM;
+ clk_data = s10_clk_data_alloc(base);
+ if (!clk_data) {
+ iounmap(base);
+ return;
+ }

- clk_data->base = base;
- clk_data->clk_data.num = num_clks;
+ s10_clk_register_early(clk_data);

- for (i = 0; i < num_clks; i++)
- clk_data->clk_data.hws[i] = ERR_PTR(-ENOENT);
+ if (of_clk_add_hw_provider(np, of_clk_hw_onecell_get, &clk_data->clk_data)) {
+ pr_err("%s: failed to add clock provider\n", __func__);
+ return;
+ }

- s10_clk_register_pll(s10_pll_clks, ARRAY_SIZE(s10_pll_clks), clk_data);
+ s10_clk_data = clk_data;
+}
+
+CLK_OF_DECLARE_DRIVER(stratix10_clkmgr, "intel,stratix10-clkmgr",
+ s10_clkmgr_early_init);
+
+static int s10_clkmgr_init(struct platform_device *pdev)
+{
+ struct stratix10_clock_data *clk_data = s10_clk_data;
+ void __iomem *base;
+ int i;
+
+ if (!clk_data) {
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base)) {
+ pr_err("%s: failed to map clock registers\n", __func__);
+ return PTR_ERR(base);
+ }
+
+ clk_data = s10_clk_data_alloc(base);
+ if (!clk_data)
+ return -ENOMEM;
+
+ s10_clk_register_early(clk_data);
+ }

s10_clk_register_c_perip(s10_main_perip_c_clks,
ARRAY_SIZE(s10_main_perip_c_clks), clk_data);
@@ -420,8 +494,15 @@ static int s10_clkmgr_init(struct platform_device *pdev)
s10_clk_register_gate(s10_gate_clks, ARRAY_SIZE(s10_gate_clks),
clk_data);

- of_clk_add_hw_provider(np, of_clk_hw_onecell_get, &clk_data->clk_data);
- return 0;
+ for (i = 0; i < STRATIX10_NUM_CLKS; i++)
+ if (clk_data->clk_data.hws[i] == ERR_PTR(-EPROBE_DEFER))
+ clk_data->clk_data.hws[i] = ERR_PTR(-ENOENT);
+
+ if (clk_data == s10_clk_data)
+ return 0;
+
+ return of_clk_add_hw_provider(pdev->dev.of_node, of_clk_hw_onecell_get,
+ &clk_data->clk_data);
}

static int s10_clkmgr_probe(struct platform_device *pdev)
--
2.49.GIT