public inbox for linux-arm-kernel@lists.infradead.org
 help / color / mirror / Atom feed
* [PATCH v1 resend 0/7] Timer driver module support
@ 2026-03-27 18:05 Daniel Lezcano
  2026-03-27 18:05 ` [PATCH resend v1 5/7] clocksource/drivers/rockchip: Use the TIMER_PDEV_DECLARE() macro Daniel Lezcano
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: Daniel Lezcano @ 2026-03-27 18:05 UTC (permalink / raw)
  To: daniel.lezcano, tglx, zhipeng.wang_1
  Cc: shawnguo, jstultz, linux-kernel, Matthias Brugger,
	AngeloGioacchino Del Regno,
	moderated list:ARM/Mediatek SoC support,
	moderated list:ARM/Mediatek SoC support

Converting the timer driver modules requires a particular care
because, depending on the platform, that may be not supported.

A previous study showed we are safe regarding how the module refcount
is held and if THIS_MODULE is set for the clockevent and the
clocksource when they are registered.

It won't be possible to unload a module if a clockevent is registered.

It will be possible to unload a module if only a clocksource is
registered and it is not the current one.

However platforms without architected timers may need the timer driver
to be initialized very early and others can be initialized later. The
former can not be a module and the init function receives a
device_node pointer, there is no device associated and devres is not
used. That results in a lot of rollbacking code where usually it is
where we find bug and resource leaks. The latter can be converted to a
module and uses a module_platform_driver(), thus the init function is
a probe function receiving a struct platform_device pointer parameter.

We end up with two approaches and duplicate code for the init
functions. This is not optimal.

Finally, we have the driver having to be initialized very early on
some platforms and be built as a module on other platforms, resulting
on having two init functions co-existing in the same driver.

This series provides what is needed to move to the same probe function
for early init, builtin and module timers.

A new macro is introduced: TIMER_PDEV_DECLARE() and a new Kconfig
option is added CONFIG_EARLY_TIMER. TIMER_PDEV_DECLARE() will have
different behavior depending on the context:

 - The driver is a module and CONFIG_EARLY_TIMER=no
   --> the driver is a module

 - The driver is builtin and CONFIG_EARLY_TIMER=no
   --> the driver is loaded later

 - The driver is builtin or a module but CONFIG_EARLY_TIMER=yes
   --> the driver is initialized through the timer-probe function

The different timer driver framework functions have their __init
sections removed and the symbols exported in order to be compatible
with the drivers converted into modules.

The series provides a couple of drivers changed. The Mediatek as a
recent requested target which is only compiled-tested. The Rockchip
timer which was tested on a rk3588 in the three different
configurations.

Daniel Lezcano (7):
  clocksource/drivers/timer-probe: Create a platform_device before the
    framework is initialized
  drivers/clocksource/rockchip: Use the TIMER_PDEV_DECLARE() macro
  clocksource/drivers/mmio: Make the code compatible with modules
  clocksource/drivers/timer-of: Make the code compatible with modules
  clocksource/drivers/timer-probe: Add the module support for the
    TIMER_PDEV_DECLARE() macro
  clocksource/drivers/rockchip: Add rockchip timer module support
  clocksource/drivers/mediatek: Convert to module support

 drivers/clocksource/Kconfig          |   7 +-
 drivers/clocksource/mmio.c           |  11 ++-
 drivers/clocksource/timer-mediatek.c |  29 ++++++--
 drivers/clocksource/timer-of.c       |  24 ++++---
 drivers/clocksource/timer-of.h       |   5 +-
 drivers/clocksource/timer-probe.c    |  69 ++++++++++++++++--
 drivers/clocksource/timer-rockchip.c | 101 ++++++++++-----------------
 include/asm-generic/vmlinux.lds.h    |  10 +++
 include/linux/clocksource.h          |  31 ++++++++
 9 files changed, 194 insertions(+), 93 deletions(-)

-- 
2.43.0



^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH resend v1 5/7] clocksource/drivers/rockchip: Use the TIMER_PDEV_DECLARE() macro
  2026-03-27 18:05 [PATCH v1 resend 0/7] Timer driver module support Daniel Lezcano
@ 2026-03-27 18:05 ` Daniel Lezcano
  2026-03-27 18:05 ` [PATCH resend v1 6/7] clocksource/drivers/rockchip: Add rockchip timer module support Daniel Lezcano
  2026-03-27 18:05 ` [PATCH resend v1 7/7] clocksource/drivers/mediatek: Convert to " Daniel Lezcano
  2 siblings, 0 replies; 4+ messages in thread
From: Daniel Lezcano @ 2026-03-27 18:05 UTC (permalink / raw)
  To: daniel.lezcano, tglx, zhipeng.wang_1
  Cc: shawnguo, jstultz, linux-kernel, Heiko Stuebner,
	moderated list:ARM/Rockchip SoC support,
	open list:ARM/Rockchip SoC support

The previous changes introduced the TIMER_PDEV_DECLARE() macro which
allows to use the platform driver to initialize a timer driver with
the benefit of having the devres to rollback automatically in case of
error.

Use this macro and change the function to rely on the devm_ variants,
allowing to cleanup the code.

Signed-off-by: Daniel Lezcano <daniel.lezcano@kernel.org>
---
 drivers/clocksource/timer-rockchip.c | 99 ++++++++++------------------
 1 file changed, 34 insertions(+), 65 deletions(-)

diff --git a/drivers/clocksource/timer-rockchip.c b/drivers/clocksource/timer-rockchip.c
index 540a16667145..486bbffba464 100644
--- a/drivers/clocksource/timer-rockchip.c
+++ b/drivers/clocksource/timer-rockchip.c
@@ -124,18 +124,18 @@ static u64 notrace rk_timer_sched_read(void)
 	return ~readl_relaxed(rk_clksrc->base + TIMER_CURRENT_VALUE0);
 }
 
-static int __init
-rk_timer_probe(struct rk_timer *timer, struct device_node *np)
+static int rk_timer_init(struct rk_timer *timer, struct device *dev)
 {
+	struct device_node *np = dev->of_node;
 	struct clk *timer_clk;
 	struct clk *pclk;
-	int ret = -EINVAL, irq;
+	int irq;
 	u32 ctrl_reg = TIMER_CONTROL_REG3288;
 
-	timer->base = of_iomap(np, 0);
-	if (!timer->base) {
+	timer->base = devm_of_iomap(dev, np, 0, NULL);
+	if (IS_ERR(timer->base)) {
 		pr_err("Failed to get base address for '%s'\n", TIMER_NAME);
-		return -ENXIO;
+		return PTR_ERR(timer->base);
 	}
 
 	if (of_device_is_compatible(np, "rockchip,rk3399-timer"))
@@ -143,31 +143,17 @@ rk_timer_probe(struct rk_timer *timer, struct device_node *np)
 
 	timer->ctrl = timer->base + ctrl_reg;
 
-	pclk = of_clk_get_by_name(np, "pclk");
+	pclk = devm_clk_get_enabled(dev, "pclk");
 	if (IS_ERR(pclk)) {
-		ret = PTR_ERR(pclk);
 		pr_err("Failed to get pclk for '%s'\n", TIMER_NAME);
-		goto out_unmap;
-	}
-
-	ret = clk_prepare_enable(pclk);
-	if (ret) {
-		pr_err("Failed to enable pclk for '%s'\n", TIMER_NAME);
-		goto out_unmap;
+		return PTR_ERR(pclk);
 	}
 	timer->pclk = pclk;
 
-	timer_clk = of_clk_get_by_name(np, "timer");
+	timer_clk = devm_clk_get_enabled(dev, "timer");
 	if (IS_ERR(timer_clk)) {
-		ret = PTR_ERR(timer_clk);
 		pr_err("Failed to get timer clock for '%s'\n", TIMER_NAME);
-		goto out_timer_clk;
-	}
-
-	ret = clk_prepare_enable(timer_clk);
-	if (ret) {
-		pr_err("Failed to enable timer clock\n");
-		goto out_timer_clk;
+		return PTR_ERR(timer_clk);
 	}
 	timer->clk = timer_clk;
 
@@ -175,47 +161,32 @@ rk_timer_probe(struct rk_timer *timer, struct device_node *np)
 
 	irq = irq_of_parse_and_map(np, 0);
 	if (!irq) {
-		ret = -EINVAL;
 		pr_err("Failed to map interrupts for '%s'\n", TIMER_NAME);
-		goto out_irq;
+		return -EINVAL;
 	}
 	timer->irq = irq;
 
 	rk_timer_interrupt_clear(timer);
 	rk_timer_disable(timer);
-	return 0;
-
-out_irq:
-	clk_disable_unprepare(timer_clk);
-out_timer_clk:
-	clk_disable_unprepare(pclk);
-out_unmap:
-	iounmap(timer->base);
-
-	return ret;
-}
 
-static void __init rk_timer_cleanup(struct rk_timer *timer)
-{
-	clk_disable_unprepare(timer->clk);
-	clk_disable_unprepare(timer->pclk);
-	iounmap(timer->base);
+	return 0;
 }
 
-static int __init rk_clkevt_init(struct device_node *np)
+static int rk_clkevt_init(struct platform_device *pdev)
 {
+	struct device *dev = &pdev->dev;
 	struct clock_event_device *ce;
 	int ret = -EINVAL;
 
-	rk_clkevt = kzalloc_obj(struct rk_clkevt);
+	rk_clkevt = devm_kzalloc(dev, sizeof(*rk_clkevt), GFP_KERNEL);
 	if (!rk_clkevt) {
 		ret = -ENOMEM;
 		goto out;
 	}
 
-	ret = rk_timer_probe(&rk_clkevt->timer, np);
+	ret = rk_timer_init(&rk_clkevt->timer, dev);
 	if (ret)
-		goto out_probe;
+		goto out;
 
 	ce = &rk_clkevt->ce;
 	ce->name = TIMER_NAME;
@@ -233,36 +204,33 @@ static int __init rk_clkevt_init(struct device_node *np)
 	if (ret) {
 		pr_err("Failed to initialize '%s': %d\n",
 			TIMER_NAME, ret);
-		goto out_irq;
+		goto out;
 	}
 
 	clockevents_config_and_register(&rk_clkevt->ce,
 					rk_clkevt->timer.freq, 1, UINT_MAX);
 	return 0;
 
-out_irq:
-	rk_timer_cleanup(&rk_clkevt->timer);
-out_probe:
-	kfree(rk_clkevt);
 out:
 	/* Leave rk_clkevt not NULL to prevent future init */
 	rk_clkevt = ERR_PTR(ret);
 	return ret;
 }
 
-static int __init rk_clksrc_init(struct device_node *np)
+static int rk_clksrc_init(struct platform_device *pdev)
 {
+	struct device *dev = &pdev->dev;
 	int ret = -EINVAL;
 
-	rk_clksrc = kzalloc_obj(struct rk_timer);
+	rk_clksrc = devm_kzalloc(dev, sizeof(*rk_clksrc), GFP_KERNEL);
 	if (!rk_clksrc) {
 		ret = -ENOMEM;
 		goto out;
 	}
 
-	ret = rk_timer_probe(rk_clksrc, np);
+	ret = rk_timer_init(rk_clksrc, dev);
 	if (ret)
-		goto out_probe;
+		goto out;
 
 	rk_timer_update_counter(UINT_MAX, rk_clksrc);
 	rk_timer_enable(rk_clksrc, 0);
@@ -272,33 +240,34 @@ static int __init rk_clksrc_init(struct device_node *np)
 		clocksource_mmio_readl_down);
 	if (ret) {
 		pr_err("Failed to register clocksource\n");
-		goto out_clocksource;
+		goto out;
 	}
 
 	sched_clock_register(rk_timer_sched_read, 32, rk_clksrc->freq);
 	return 0;
 
-out_clocksource:
-	rk_timer_cleanup(rk_clksrc);
-out_probe:
-	kfree(rk_clksrc);
 out:
 	/* Leave rk_clksrc not NULL to prevent future init */
 	rk_clksrc = ERR_PTR(ret);
 	return ret;
 }
 
-static int __init rk_timer_init(struct device_node *np)
+static int rk_timer_probe(struct platform_device *pdev)
 {
 	if (!rk_clkevt)
-		return rk_clkevt_init(np);
+		return rk_clkevt_init(pdev);
 
 	if (!rk_clksrc)
-		return rk_clksrc_init(np);
+		return rk_clksrc_init(pdev);
 
 	pr_err("Too many timer definitions for '%s'\n", TIMER_NAME);
 	return -EINVAL;
 }
 
-TIMER_OF_DECLARE(rk3288_timer, "rockchip,rk3288-timer", rk_timer_init);
-TIMER_OF_DECLARE(rk3399_timer, "rockchip,rk3399-timer", rk_timer_init);
+static const struct of_device_id rk_timer_match_table[] = {
+	{ .compatible = "rockchip,rk3288-timer" },
+	{ .compatible = "rockchip,rk3399-timer" },
+	{ /* sentinel */ }
+};
+
+TIMER_PDEV_DECLARE(rk_timer, rk_timer_probe, NULL, rk_timer_match_table);
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCH resend v1 6/7] clocksource/drivers/rockchip: Add rockchip timer module support
  2026-03-27 18:05 [PATCH v1 resend 0/7] Timer driver module support Daniel Lezcano
  2026-03-27 18:05 ` [PATCH resend v1 5/7] clocksource/drivers/rockchip: Use the TIMER_PDEV_DECLARE() macro Daniel Lezcano
@ 2026-03-27 18:05 ` Daniel Lezcano
  2026-03-27 18:05 ` [PATCH resend v1 7/7] clocksource/drivers/mediatek: Convert to " Daniel Lezcano
  2 siblings, 0 replies; 4+ messages in thread
From: Daniel Lezcano @ 2026-03-27 18:05 UTC (permalink / raw)
  To: daniel.lezcano, tglx, zhipeng.wang_1
  Cc: shawnguo, jstultz, linux-kernel, Heiko Stuebner,
	moderated list:ARM/Rockchip SoC support,
	open list:ARM/Rockchip SoC support

Now the TIMER_PDEV_DECLARE() allows the driver to be compiled as a
module. Add the MODULE_DESCRIPTION and the MODULE_LICENSE left for the
one converting the driver as a module.

Signed-off-by: Daniel Lezcano <daniel.lezcano@kernel.org>
---
 drivers/clocksource/timer-rockchip.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/clocksource/timer-rockchip.c b/drivers/clocksource/timer-rockchip.c
index 486bbffba464..61433b295882 100644
--- a/drivers/clocksource/timer-rockchip.c
+++ b/drivers/clocksource/timer-rockchip.c
@@ -271,3 +271,5 @@ static const struct of_device_id rk_timer_match_table[] = {
 };
 
 TIMER_PDEV_DECLARE(rk_timer, rk_timer_probe, NULL, rk_timer_match_table);
+MODULE_DESCRIPTION("Rockchip timer driver");
+MODULE_LICENSE("GPL");
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCH resend v1 7/7] clocksource/drivers/mediatek: Convert to module support
  2026-03-27 18:05 [PATCH v1 resend 0/7] Timer driver module support Daniel Lezcano
  2026-03-27 18:05 ` [PATCH resend v1 5/7] clocksource/drivers/rockchip: Use the TIMER_PDEV_DECLARE() macro Daniel Lezcano
  2026-03-27 18:05 ` [PATCH resend v1 6/7] clocksource/drivers/rockchip: Add rockchip timer module support Daniel Lezcano
@ 2026-03-27 18:05 ` Daniel Lezcano
  2 siblings, 0 replies; 4+ messages in thread
From: Daniel Lezcano @ 2026-03-27 18:05 UTC (permalink / raw)
  To: daniel.lezcano, tglx, zhipeng.wang_1
  Cc: shawnguo, jstultz, linux-kernel, Matthias Brugger,
	AngeloGioacchino Del Regno,
	moderated list:ARM/Mediatek SoC support,
	moderated list:ARM/Mediatek SoC support

Now the TIMER_PDEV_DECLARE() allows the driver to be compiled as a
module. Add the MODULE_DESCRIPTION and the MODULE_LICENSE left for the
one converting the driver as a module.

Signed-off-by: Daniel Lezcano <daniel.lezcano@kernel.org>
---
 drivers/clocksource/timer-mediatek.c | 29 ++++++++++++++++++++++------
 1 file changed, 23 insertions(+), 6 deletions(-)

diff --git a/drivers/clocksource/timer-mediatek.c b/drivers/clocksource/timer-mediatek.c
index 7bcb4a3f26fb..f5de5f397730 100644
--- a/drivers/clocksource/timer-mediatek.c
+++ b/drivers/clocksource/timer-mediatek.c
@@ -215,8 +215,7 @@ static irqreturn_t mtk_gpt_interrupt(int irq, void *dev_id)
 	return IRQ_HANDLED;
 }
 
-static void
-__init mtk_gpt_setup(struct timer_of *to, u8 timer, u8 option)
+static void mtk_gpt_setup(struct timer_of *to, u8 timer, u8 option)
 {
 	writel(GPT_CTRL_CLEAR | GPT_CTRL_DISABLE,
 	       timer_of_base(to) + GPT_CTRL_REG(timer));
@@ -281,7 +280,7 @@ static struct timer_of to = {
 	},
 };
 
-static int __init mtk_syst_init(struct device_node *node)
+static int mtk_syst_init(struct device_node *node)
 {
 	int ret;
 
@@ -302,7 +301,7 @@ static int __init mtk_syst_init(struct device_node *node)
 	return 0;
 }
 
-static int __init mtk_gpt_init(struct device_node *node)
+static int mtk_gpt_init(struct device_node *node)
 {
 	int ret;
 
@@ -337,5 +336,23 @@ static int __init mtk_gpt_init(struct device_node *node)
 
 	return 0;
 }
-TIMER_OF_DECLARE(mtk_mt6577, "mediatek,mt6577-timer", mtk_gpt_init);
-TIMER_OF_DECLARE(mtk_mt6765, "mediatek,mt6765-timer", mtk_syst_init);
+
+static int mtk_timer_probe(struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	int (*probe_func)(struct device_node *node);
+
+	probe_func = of_device_get_match_data(&pdev->dev);
+
+	return probe_func(np);
+}
+
+static const struct of_device_id mtk_timer_match_table[] = {
+	{ .compatible =  "mediatek,mt6577-timer", .data = mtk_gpt_init },
+	{ .compatible =  "mediatek,mt6765-timer", .data = mtk_syst_init },
+	{ /* sentinel */ }
+};
+
+TIMER_PDEV_DECLARE(mtk_timer, mtk_timer_probe, NULL, mtk_timer_match_table);
+MODULE_DESCRIPTION("Mediatek timer driver");
+MODULE_LICENSE("GPL");
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2026-03-27 18:06 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-27 18:05 [PATCH v1 resend 0/7] Timer driver module support Daniel Lezcano
2026-03-27 18:05 ` [PATCH resend v1 5/7] clocksource/drivers/rockchip: Use the TIMER_PDEV_DECLARE() macro Daniel Lezcano
2026-03-27 18:05 ` [PATCH resend v1 6/7] clocksource/drivers/rockchip: Add rockchip timer module support Daniel Lezcano
2026-03-27 18:05 ` [PATCH resend v1 7/7] clocksource/drivers/mediatek: Convert to " Daniel Lezcano

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox