From 576c564ec3bb60e571c705a71907d7c0c039e6c0 Mon Sep 17 00:00:00 2001 From: Enlin Mu Date: Thu, 6 Nov 2025 10:18:30 +0800 Subject: [PATCH 01/13] clocksource/drivers/sprd: Enable register for timer counter from 32 bit to 64 bit Using 32 bit for suspend compensation, the max compensation time is 36 hours(working clock is 32k).In some IOT devices, the suspend time may be long, even exceeding 36 hours. Therefore, a 64 bit timer counter is needed for counting. Signed-off-by: Enlin Mu Signed-off-by: Daniel Lezcano Reviewed-by: Baolin Wang Link: https://patch.msgid.link/20251106021830.34846-1-enlin.mu@linux.dev --- drivers/clocksource/timer-sprd.c | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/drivers/clocksource/timer-sprd.c b/drivers/clocksource/timer-sprd.c index 430cb99d8d79..2c07dd2af760 100644 --- a/drivers/clocksource/timer-sprd.c +++ b/drivers/clocksource/timer-sprd.c @@ -30,6 +30,7 @@ #define TIMER_VALUE_SHDW_HI 0x1c #define TIMER_VALUE_LO_MASK GENMASK(31, 0) +#define TIMER_VALUE_HI_MASK GENMASK(31, 0) static void sprd_timer_enable(void __iomem *base, u32 flag) { @@ -162,15 +163,26 @@ static struct timer_of suspend_to = { static u64 sprd_suspend_timer_read(struct clocksource *cs) { - return ~(u64)readl_relaxed(timer_of_base(&suspend_to) + - TIMER_VALUE_SHDW_LO) & cs->mask; + u32 lo, hi; + + do { + hi = readl_relaxed(timer_of_base(&suspend_to) + + TIMER_VALUE_SHDW_HI); + lo = readl_relaxed(timer_of_base(&suspend_to) + + TIMER_VALUE_SHDW_LO); + } while (hi != readl_relaxed(timer_of_base(&suspend_to) + TIMER_VALUE_SHDW_HI)); + + return ~(((u64)hi << 32) | lo); } static int sprd_suspend_timer_enable(struct clocksource *cs) { - sprd_timer_update_counter(timer_of_base(&suspend_to), - TIMER_VALUE_LO_MASK); - sprd_timer_enable(timer_of_base(&suspend_to), TIMER_CTL_PERIOD_MODE); + writel_relaxed(TIMER_VALUE_LO_MASK, + timer_of_base(&suspend_to) + TIMER_LOAD_LO); + writel_relaxed(TIMER_VALUE_HI_MASK, + timer_of_base(&suspend_to) + TIMER_LOAD_HI); + sprd_timer_enable(timer_of_base(&suspend_to), + TIMER_CTL_PERIOD_MODE|TIMER_CTL_64BIT_WIDTH); return 0; } @@ -186,7 +198,7 @@ static struct clocksource suspend_clocksource = { .read = sprd_suspend_timer_read, .enable = sprd_suspend_timer_enable, .disable = sprd_suspend_timer_disable, - .mask = CLOCKSOURCE_MASK(32), + .mask = CLOCKSOURCE_MASK(64), .flags = CLOCK_SOURCE_IS_CONTINUOUS | CLOCK_SOURCE_SUSPEND_NONSTOP, }; From 640594a04f119338019b0aeed70c7301216595b3 Mon Sep 17 00:00:00 2001 From: Stephen Eta Zhou Date: Sun, 25 May 2025 16:43:28 +0800 Subject: [PATCH 02/13] clocksource/drivers/timer-sp804: Fix read_current_timer() issue when clock source is not registered MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Register a valid read_current_timer() function for the SP804 timer on ARM32. On ARM32 platforms, when the SP804 timer is selected as the clocksource, the driver does not register a valid read_current_timer() function. As a result, features that rely on this API—such as rdseed—consistently return incorrect values. To fix this, a delay_timer structure is registered during the SP804 driver's initialization. The read_current_timer() function is implemented using the existing sp804_read() logic, and the timer frequency is reused from the already-initialized clocksource. Signed-off-by: Stephen Eta Zhou Signed-off-by: Daniel Lezcano Link: https://patch.msgid.link/20250525-sp804-fix-read_current_timer-v4-1-87a9201fa4ec@gmail.com --- drivers/clocksource/timer-sp804.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/drivers/clocksource/timer-sp804.c b/drivers/clocksource/timer-sp804.c index cd1916c05325..e82a95ea4724 100644 --- a/drivers/clocksource/timer-sp804.c +++ b/drivers/clocksource/timer-sp804.c @@ -21,6 +21,10 @@ #include #include +#ifdef CONFIG_ARM +#include +#endif + #include "timer-sp.h" /* Hisilicon 64-bit timer(a variant of ARM SP804) */ @@ -102,6 +106,23 @@ static u64 notrace sp804_read(void) return ~readl_relaxed(sched_clkevt->value); } +#ifdef CONFIG_ARM +static struct delay_timer delay; +static unsigned long sp804_read_delay_timer_read(void) +{ + return sp804_read(); +} + +static void sp804_register_delay_timer(int freq) +{ + delay.freq = freq; + delay.read_current_timer = sp804_read_delay_timer_read; + register_current_timer_delay(&delay); +} +#else +static inline void sp804_register_delay_timer(int freq) {} +#endif + static int __init sp804_clocksource_and_sched_clock_init(void __iomem *base, const char *name, struct clk *clk, @@ -114,6 +135,8 @@ static int __init sp804_clocksource_and_sched_clock_init(void __iomem *base, if (rate < 0) return -EINVAL; + sp804_register_delay_timer(rate); + clkevt = sp804_clkevt_get(base); writel(0, clkevt->ctrl); @@ -318,6 +341,7 @@ static int __init sp804_of_init(struct device_node *np, struct sp804_timer *time if (ret) goto err; } + initialized = true; return 0; From 2ba8e2aae1324704565a7d4d66f199d056c9e3c6 Mon Sep 17 00:00:00 2001 From: Haotian Zhang Date: Thu, 30 Oct 2025 17:07:10 +0800 Subject: [PATCH 03/13] clocksource/drivers/ralink: Fix resource leaks in init error path The ralink_systick_init() function does not release all acquired resources on its error paths. If irq_of_parse_and_map() or a subsequent call fails, the previously created I/O memory mapping and IRQ mapping are leaked. Add goto-based error handling labels to ensure that all allocated resources are correctly freed. Fixes: 1f2acc5a8a0a ("MIPS: ralink: Add support for systick timer found on newer ralink SoC") Signed-off-by: Haotian Zhang Signed-off-by: Daniel Lezcano Link: https://patch.msgid.link/20251030090710.1603-1-vulab@iscas.ac.cn --- drivers/clocksource/timer-ralink.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/clocksource/timer-ralink.c b/drivers/clocksource/timer-ralink.c index 6ecdb4228f76..68434d9ed910 100644 --- a/drivers/clocksource/timer-ralink.c +++ b/drivers/clocksource/timer-ralink.c @@ -130,14 +130,15 @@ static int __init ralink_systick_init(struct device_node *np) systick.dev.irq = irq_of_parse_and_map(np, 0); if (!systick.dev.irq) { pr_err("%pOFn: request_irq failed", np); - return -EINVAL; + ret = -EINVAL; + goto err_iounmap; } ret = clocksource_mmio_init(systick.membase + SYSTICK_COUNT, np->name, SYSTICK_FREQ, 301, 16, clocksource_mmio_readl_up); if (ret) - return ret; + goto err_free_irq; clockevents_register_device(&systick.dev); @@ -145,6 +146,12 @@ static int __init ralink_systick_init(struct device_node *np) np, systick.dev.mult, systick.dev.shift); return 0; + +err_free_irq: + irq_dispose_mapping(systick.dev.irq); +err_iounmap: + iounmap(systick.membase); + return ret; } TIMER_OF_DECLARE(systick, "ralink,cevt-systick", ralink_systick_init); From 6b38a8b31e2c5c2c3fd5f9848850788c190f216d Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 17 Oct 2025 07:50:39 +0200 Subject: [PATCH 04/13] clocksource/drivers/stm: Fix double deregistration on probe failure The purpose of the devm_add_action_or_reset() helper is to call the action function in case adding an action ever fails so drop the clock source deregistration from the error path to avoid deregistering twice. Fixes: cec32ac75827 ("clocksource/drivers/nxp-timer: Add the System Timer Module for the s32gx platforms") Signed-off-by: Johan Hovold Signed-off-by: Daniel Lezcano Cc: Daniel Lezcano Link: https://patch.msgid.link/20251017055039.7307-1-johan@kernel.org --- drivers/clocksource/timer-nxp-stm.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/clocksource/timer-nxp-stm.c b/drivers/clocksource/timer-nxp-stm.c index bbc40623728f..16d52167e949 100644 --- a/drivers/clocksource/timer-nxp-stm.c +++ b/drivers/clocksource/timer-nxp-stm.c @@ -208,10 +208,8 @@ static int __init nxp_stm_clocksource_init(struct device *dev, struct stm_timer return ret; ret = devm_add_action_or_reset(dev, devm_clocksource_unregister, stm_timer); - if (ret) { - clocksource_unregister(&stm_timer->cs); + if (ret) return ret; - } stm_sched_clock = stm_timer; From 62524f285c11d6e6168ad31b586143755b27b2e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niklas=20S=C3=B6derlund?= Date: Thu, 16 Oct 2025 20:20:22 +0200 Subject: [PATCH 05/13] clocksource/drivers/sh_cmt: Always leave device running after probe MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The CMT device can be used as both a clocksource and a clockevent provider. The driver tries to be smart and power itself on and off, as well as enabling and disabling its clock when it's not in operation. This behavior is slightly altered if the CMT is used as an early platform device in which case the device is left powered on after probe, but the clock is still enabled and disabled at runtime. This has worked for a long time, but recent improvements in PREEMPT_RT and PROVE_LOCKING have highlighted an issue. As the CMT registers itself as a clockevent provider, clockevents_register_device(), it needs to use raw spinlocks internally as this is the context of which the clockevent framework interacts with the CMT driver. However in the context of holding a raw spinlock the CMT driver can't really manage its power state or clock with calls to pm_runtime_*() and clk_*() as these calls end up in other platform drivers using regular spinlocks to control power and clocks. This mix of spinlock contexts trips a lockdep warning. ============================= [ BUG: Invalid wait context ] 6.17.0-rc3-arm64-renesas-03071-gb3c4f4122b28-dirty #21 Not tainted ----------------------------- swapper/1/0 is trying to lock: ffff00000898d180 (&dev->power.lock){-...}-{3:3}, at: __pm_runtime_resume+0x38/0x88 ccree e6601000.crypto: ARM CryptoCell 630P Driver: HW version 0xAF400001/0xDCC63000, Driver version 5.0 other info that might help us debug this: ccree e6601000.crypto: ARM ccree device initialized context-{5:5} 2 locks held by swapper/1/0: #0: ffff80008173c298 (tick_broadcast_lock){-...}-{2:2}, at: __tick_broadcast_oneshot_control+0xa4/0x3a8 #1: ffff0000089a5858 (&ch->lock){....}-{2:2} usbcore: registered new interface driver usbhid , at: sh_cmt_start+0x30/0x364 stack backtrace: CPU: 1 UID: 0 PID: 0 Comm: swapper/1 Not tainted 6.17.0-rc3-arm64-renesas-03071-gb3c4f4122b28-dirty #21 PREEMPT Hardware name: Renesas Salvator-X 2nd version board based on r8a77965 (DT) Call trace: show_stack+0x14/0x1c (C) dump_stack_lvl+0x6c/0x90 dump_stack+0x14/0x1c __lock_acquire+0x904/0x1584 lock_acquire+0x220/0x34c _raw_spin_lock_irqsave+0x58/0x80 __pm_runtime_resume+0x38/0x88 sh_cmt_start+0x54/0x364 sh_cmt_clock_event_set_oneshot+0x64/0xb8 clockevents_switch_state+0xfc/0x13c tick_broadcast_set_event+0x30/0xa4 __tick_broadcast_oneshot_control+0x1e0/0x3a8 tick_broadcast_oneshot_control+0x30/0x40 cpuidle_enter_state+0x40c/0x680 cpuidle_enter+0x30/0x40 do_idle+0x1f4/0x26c cpu_startup_entry+0x34/0x40 secondary_start_kernel+0x11c/0x13c __secondary_switched+0x74/0x78 For non-PREEMPT_RT builds this is not really an issue, but for PREEMPT_RT builds where normal spinlocks can sleep this might be an issue. Be cautious and always leave the power and clock running after probe. Signed-off-by: Niklas Söderlund Signed-off-by: Daniel Lezcano Tested-by: Geert Uytterhoeven Link: https://patch.msgid.link/20251016182022.1837417-1-niklas.soderlund+renesas@ragnatech.se --- drivers/clocksource/sh_cmt.c | 36 +++--------------------------------- 1 file changed, 3 insertions(+), 33 deletions(-) diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c index 385eb94bbe7c..791b298c995b 100644 --- a/drivers/clocksource/sh_cmt.c +++ b/drivers/clocksource/sh_cmt.c @@ -355,14 +355,6 @@ static int sh_cmt_enable(struct sh_cmt_channel *ch) dev_pm_syscore_device(&ch->cmt->pdev->dev, true); - /* enable clock */ - ret = clk_enable(ch->cmt->clk); - if (ret) { - dev_err(&ch->cmt->pdev->dev, "ch%u: cannot enable clock\n", - ch->index); - goto err0; - } - /* make sure channel is disabled */ sh_cmt_start_stop_ch(ch, 0); @@ -384,19 +376,12 @@ static int sh_cmt_enable(struct sh_cmt_channel *ch) if (ret || sh_cmt_read_cmcnt(ch)) { dev_err(&ch->cmt->pdev->dev, "ch%u: cannot clear CMCNT\n", ch->index); - ret = -ETIMEDOUT; - goto err1; + return -ETIMEDOUT; } /* enable channel */ sh_cmt_start_stop_ch(ch, 1); return 0; - err1: - /* stop clock */ - clk_disable(ch->cmt->clk); - - err0: - return ret; } static void sh_cmt_disable(struct sh_cmt_channel *ch) @@ -407,9 +392,6 @@ static void sh_cmt_disable(struct sh_cmt_channel *ch) /* disable interrupts in CMT block */ sh_cmt_write_cmcsr(ch, 0); - /* stop clock */ - clk_disable(ch->cmt->clk); - dev_pm_syscore_device(&ch->cmt->pdev->dev, false); } @@ -583,8 +565,6 @@ static int sh_cmt_start_clocksource(struct sh_cmt_channel *ch) int ret = 0; unsigned long flags; - pm_runtime_get_sync(&ch->cmt->pdev->dev); - raw_spin_lock_irqsave(&ch->lock, flags); if (!(ch->flags & (FLAG_CLOCKEVENT | FLAG_CLOCKSOURCE))) @@ -619,8 +599,6 @@ static void sh_cmt_stop_clocksource(struct sh_cmt_channel *ch) sh_cmt_disable(ch); raw_spin_unlock_irqrestore(&ch->lock, flags); - - pm_runtime_put(&ch->cmt->pdev->dev); } static int sh_cmt_start_clockevent(struct sh_cmt_channel *ch) @@ -630,10 +608,8 @@ static int sh_cmt_start_clockevent(struct sh_cmt_channel *ch) raw_spin_lock_irqsave(&ch->lock, flags); - if (!(ch->flags & (FLAG_CLOCKEVENT | FLAG_CLOCKSOURCE))) { - pm_runtime_get_sync(&ch->cmt->pdev->dev); + if (!(ch->flags & (FLAG_CLOCKEVENT | FLAG_CLOCKSOURCE))) ret = sh_cmt_enable(ch); - } if (ret) goto out; @@ -656,10 +632,8 @@ static void sh_cmt_stop_clockevent(struct sh_cmt_channel *ch) ch->flags &= ~FLAG_CLOCKEVENT; - if (f && !(ch->flags & (FLAG_CLOCKEVENT | FLAG_CLOCKSOURCE))) { + if (f && !(ch->flags & (FLAG_CLOCKEVENT | FLAG_CLOCKSOURCE))) sh_cmt_disable(ch); - pm_runtime_put(&ch->cmt->pdev->dev); - } /* adjust the timeout to maximum if only clocksource left */ if (ch->flags & FLAG_CLOCKSOURCE) @@ -1134,8 +1108,6 @@ static int sh_cmt_setup(struct sh_cmt_device *cmt, struct platform_device *pdev) mask &= ~(1 << hwidx); } - clk_disable(cmt->clk); - platform_set_drvdata(pdev, cmt); return 0; @@ -1183,8 +1155,6 @@ static int sh_cmt_probe(struct platform_device *pdev) out: if (cmt->has_clockevent || cmt->has_clocksource) pm_runtime_irq_safe(&pdev->dev); - else - pm_runtime_idle(&pdev->dev); return 0; } From b452d2c97eeccbf9c7ac5b3d2d9e80bf6d8a23db Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 17 Oct 2025 07:49:43 +0200 Subject: [PATCH 06/13] clocksource/drivers/nxp-stm: Fix section mismatches Platform drivers can be probed after their init sections have been discarded (e.g. on probe deferral or manual rebind through sysfs) so the probe function must not live in init. Device managed resource actions similarly cannot be discarded. The "_probe" suffix of the driver structure name prevents modpost from warning about this so replace it to catch any similar future issues. Fixes: cec32ac75827 ("clocksource/drivers/nxp-timer: Add the System Timer Module for the s32gx platforms") Signed-off-by: Johan Hovold Signed-off-by: Daniel Lezcano Cc: stable@vger.kernel.org # 6.16 Cc: Daniel Lezcano Link: https://patch.msgid.link/20251017054943.7195-1-johan@kernel.org --- drivers/clocksource/timer-nxp-stm.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/clocksource/timer-nxp-stm.c b/drivers/clocksource/timer-nxp-stm.c index 16d52167e949..c320d764b12e 100644 --- a/drivers/clocksource/timer-nxp-stm.c +++ b/drivers/clocksource/timer-nxp-stm.c @@ -177,15 +177,15 @@ static void nxp_stm_clocksource_resume(struct clocksource *cs) nxp_stm_clocksource_enable(cs); } -static void __init devm_clocksource_unregister(void *data) +static void devm_clocksource_unregister(void *data) { struct stm_timer *stm_timer = data; clocksource_unregister(&stm_timer->cs); } -static int __init nxp_stm_clocksource_init(struct device *dev, struct stm_timer *stm_timer, - const char *name, void __iomem *base, struct clk *clk) +static int nxp_stm_clocksource_init(struct device *dev, struct stm_timer *stm_timer, + const char *name, void __iomem *base, struct clk *clk) { int ret; @@ -296,9 +296,9 @@ static void nxp_stm_clockevent_resume(struct clock_event_device *ced) nxp_stm_module_get(stm_timer); } -static int __init nxp_stm_clockevent_per_cpu_init(struct device *dev, struct stm_timer *stm_timer, - const char *name, void __iomem *base, int irq, - struct clk *clk, int cpu) +static int nxp_stm_clockevent_per_cpu_init(struct device *dev, struct stm_timer *stm_timer, + const char *name, void __iomem *base, int irq, + struct clk *clk, int cpu) { stm_timer->base = base; stm_timer->rate = clk_get_rate(clk); @@ -386,7 +386,7 @@ static irqreturn_t nxp_stm_module_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } -static int __init nxp_stm_timer_probe(struct platform_device *pdev) +static int nxp_stm_timer_probe(struct platform_device *pdev) { struct stm_timer *stm_timer; struct device *dev = &pdev->dev; @@ -482,14 +482,14 @@ static const struct of_device_id nxp_stm_of_match[] = { }; MODULE_DEVICE_TABLE(of, nxp_stm_of_match); -static struct platform_driver nxp_stm_probe = { +static struct platform_driver nxp_stm_driver = { .probe = nxp_stm_timer_probe, .driver = { .name = "nxp-stm", .of_match_table = nxp_stm_of_match, }, }; -module_platform_driver(nxp_stm_probe); +module_platform_driver(nxp_stm_driver); MODULE_DESCRIPTION("NXP System Timer Module driver"); MODULE_LICENSE("GPL"); From 6aa10f0e2ef9eba1955be6a9d0a8eaecf6bdb7ae Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 11 Nov 2025 16:32:24 +0100 Subject: [PATCH 07/13] clocksource/drivers/arm_arch_timer_mmio: Prevent driver unbind Clockevents cannot be deregistered so suppress the bind attributes to prevent the driver from being unbound and releasing the underlying resources after registration. Fixes: 4891f01527bb ("clocksource/drivers/arm_arch_timer: Add standalone MMIO driver") Signed-off-by: Johan Hovold Signed-off-by: Daniel Lezcano Acked-by: Marc Zyngier Link: https://patch.msgid.link/20251111153226.579-2-johan@kernel.org --- drivers/clocksource/arm_arch_timer_mmio.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/clocksource/arm_arch_timer_mmio.c b/drivers/clocksource/arm_arch_timer_mmio.c index ebe1987d651e..d10362692fdd 100644 --- a/drivers/clocksource/arm_arch_timer_mmio.c +++ b/drivers/clocksource/arm_arch_timer_mmio.c @@ -426,6 +426,7 @@ static struct platform_driver arch_timer_mmio_drv = { .driver = { .name = "arch-timer-mmio", .of_match_table = arch_timer_mmio_of_table, + .suppress_bind_attrs = true, }, .probe = arch_timer_mmio_probe, }; @@ -434,6 +435,7 @@ builtin_platform_driver(arch_timer_mmio_drv); static struct platform_driver arch_timer_mmio_acpi_drv = { .driver = { .name = "gtdt-arm-mmio-timer", + .suppress_bind_attrs = true, }, .probe = arch_timer_mmio_probe, }; From e25f964cf414dafa6bee5c9c2c0b1d1fb041dc92 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 11 Nov 2025 16:32:25 +0100 Subject: [PATCH 08/13] clocksource/drivers/nxp-pit: Prevent driver unbind The driver does not support unbinding (e.g. as clockevents cannot be deregistered) so suppress the bind attributes to prevent the driver from being unbound and rebound after registration (and disabling the timer when reprobing fails). Even if the driver can currently only be built-in, also switch to builtin_platform_driver() to prevent it from being unloaded should modular builds ever be enabled. Fixes: bee33f22d7c3 ("clocksource/drivers/nxp-pit: Add NXP Automotive s32g2 / s32g3 support") Signed-off-by: Johan Hovold Signed-off-by: Daniel Lezcano Link: https://patch.msgid.link/20251111153226.579-3-johan@kernel.org --- drivers/clocksource/timer-nxp-pit.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/clocksource/timer-nxp-pit.c b/drivers/clocksource/timer-nxp-pit.c index 2d0a3554b6bf..d1740f18f718 100644 --- a/drivers/clocksource/timer-nxp-pit.c +++ b/drivers/clocksource/timer-nxp-pit.c @@ -374,9 +374,10 @@ static struct platform_driver nxp_pit_driver = { .driver = { .name = "nxp-pit", .of_match_table = pit_timer_of_match, + .suppress_bind_attrs = true, }, .probe = pit_timer_probe, }; -module_platform_driver(nxp_pit_driver); +builtin_platform_driver(nxp_pit_driver); TIMER_OF_DECLARE(vf610, "fsl,vf610-pit", pit_timer_init); From 6a2416892e8942f5e2bfe9b85c0164f410a53a2d Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 11 Nov 2025 16:32:26 +0100 Subject: [PATCH 09/13] clocksource/drivers/nxp-stm: Prevent driver unbind Clockevents cannot be deregistered so suppress the bind attributes to prevent the driver from being unbound and releasing the underlying resources after registration. Even if the driver can currently only be built-in, also switch to builtin_platform_driver() to prevent it from being unloaded should modular builds ever be enabled. Fixes: cec32ac75827 ("clocksource/drivers/nxp-timer: Add the System Timer Module for the s32gx platforms") Signed-off-by: Johan Hovold Signed-off-by: Daniel Lezcano Link: https://patch.msgid.link/20251111153226.579-4-johan@kernel.org --- drivers/clocksource/timer-nxp-stm.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/clocksource/timer-nxp-stm.c b/drivers/clocksource/timer-nxp-stm.c index c320d764b12e..1ab907233f48 100644 --- a/drivers/clocksource/timer-nxp-stm.c +++ b/drivers/clocksource/timer-nxp-stm.c @@ -487,9 +487,10 @@ static struct platform_driver nxp_stm_driver = { .driver = { .name = "nxp-stm", .of_match_table = nxp_stm_of_match, + .suppress_bind_attrs = true, }, }; -module_platform_driver(nxp_stm_driver); +builtin_platform_driver(nxp_stm_driver); MODULE_DESCRIPTION("NXP System Timer Module driver"); MODULE_LICENSE("GPL"); From 627f3f3716a3591f5e6a6bd124c95eef85444080 Mon Sep 17 00:00:00 2001 From: Enlin Mu Date: Fri, 7 Nov 2025 14:33:47 +0800 Subject: [PATCH 10/13] clocksource/drivers/rda: Add sched_clock_register for RDA8810PL SoC The current system log timestamp accuracy is tick based, which can not meet the usage requirements and needs to reach nanoseconds. Therefore, the sched_clock_register function needs to be added. [ dlezcano: Fixed typos ] Signed-off-by: Enlin Mu Signed-off-by: Daniel Lezcano Link: https://patch.msgid.link/20251107063347.3692-1-enlin.mu@linux.dev --- drivers/clocksource/timer-rda.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/clocksource/timer-rda.c b/drivers/clocksource/timer-rda.c index fd1199c189bf..0be8e05970e2 100644 --- a/drivers/clocksource/timer-rda.c +++ b/drivers/clocksource/timer-rda.c @@ -13,6 +13,7 @@ #include #include +#include #include "timer-of.h" @@ -153,7 +154,7 @@ static struct timer_of rda_ostimer_of = { }, }; -static u64 rda_hwtimer_read(struct clocksource *cs) +static u64 rda_hwtimer_clocksource_read(void) { void __iomem *base = timer_of_base(&rda_ostimer_of); u32 lo, hi; @@ -167,6 +168,11 @@ static u64 rda_hwtimer_read(struct clocksource *cs) return ((u64)hi << 32) | lo; } +static u64 rda_hwtimer_read(struct clocksource *cs) +{ + return rda_hwtimer_clocksource_read(); +} + static struct clocksource rda_hwtimer_clocksource = { .name = "rda-timer", .rating = 400, @@ -185,6 +191,7 @@ static int __init rda_timer_init(struct device_node *np) return ret; clocksource_register_hz(&rda_hwtimer_clocksource, rate); + sched_clock_register(rda_hwtimer_clocksource_read, 64, rate); clockevents_config_and_register(&rda_ostimer_of.clkevt, rate, 0x2, UINT_MAX); From ed92a968a967042a7c7eb4c938e640b4deb79fe2 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 11 Nov 2025 16:45:16 +0100 Subject: [PATCH 11/13] clocksource/drivers/stm32-lp: Drop unused module alias The driver cannot be built as a module so drop the unused platform module alias. Note that platform aliases are not needed for OF probing should it ever become possible to build the driver as a module. Signed-off-by: Johan Hovold Signed-off-by: Daniel Lezcano Link: https://patch.msgid.link/20251111154516.1698-1-johan@kernel.org --- drivers/clocksource/timer-stm32-lp.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/clocksource/timer-stm32-lp.c b/drivers/clocksource/timer-stm32-lp.c index c2a699f5c1dd..3d804128c765 100644 --- a/drivers/clocksource/timer-stm32-lp.c +++ b/drivers/clocksource/timer-stm32-lp.c @@ -289,5 +289,4 @@ static struct platform_driver stm32_clkevent_lp_driver = { }; module_platform_driver(stm32_clkevent_lp_driver); -MODULE_ALIAS("platform:stm32-lptimer-timer"); MODULE_DESCRIPTION("STMicroelectronics STM32 clockevent low power driver"); From 40caba2bd027ab57c196b690e4e7f3c1746acb96 Mon Sep 17 00:00:00 2001 From: Hao-Wen Ting Date: Wed, 26 Nov 2025 14:01:09 +0800 Subject: [PATCH 12/13] dt-bindings: timer: Add Realtek SYSTIMER The Realtek SYSTIMER (System Timer) is a 64-bit global hardware counter operating at a fixed 1MHz frequency. Thanks to its compare match interrupt capability, the timer natively supports oneshot mode for tick broadcast functionality. Signed-off-by: Hao-Wen Ting Signed-off-by: Daniel Lezcano Reviewed-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20251126060110.198330-2-haowen.ting@realtek.com --- .../timer/realtek,rtd1625-systimer.yaml | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 Documentation/devicetree/bindings/timer/realtek,rtd1625-systimer.yaml diff --git a/Documentation/devicetree/bindings/timer/realtek,rtd1625-systimer.yaml b/Documentation/devicetree/bindings/timer/realtek,rtd1625-systimer.yaml new file mode 100644 index 000000000000..e08d3d2d306b --- /dev/null +++ b/Documentation/devicetree/bindings/timer/realtek,rtd1625-systimer.yaml @@ -0,0 +1,47 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/timer/realtek,rtd1625-systimer.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Realtek System Timer + +maintainers: + - Hao-Wen Ting + +description: + The Realtek SYSTIMER (System Timer) is a 64-bit global hardware counter operating + at a fixed 1MHz frequency. Thanks to its compare match interrupt capability, + the timer natively supports oneshot mode for tick broadcast functionality. + +properties: + compatible: + oneOf: + - const: realtek,rtd1625-systimer + - items: + - const: realtek,rtd1635-systimer + - const: realtek,rtd1625-systimer + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + +required: + - compatible + - reg + - interrupts + +additionalProperties: false + +examples: + - | + #include + + timer@89420 { + compatible = "realtek,rtd1635-systimer", + "realtek,rtd1625-systimer"; + reg = <0x89420 0x18>; + interrupts = ; + }; From d1780dce9575072303b9c574614b72b5c8c5c44c Mon Sep 17 00:00:00 2001 From: Hao-Wen Ting Date: Wed, 26 Nov 2025 14:01:10 +0800 Subject: [PATCH 13/13] clocksource/drivers: Add Realtek system timer driver Add a system timer driver for Realtek SoCs. This driver registers the 1 MHz global hardware counter on Realtek platforms as a clock event device. Since this hardware counter starts counting automatically after SoC power-on, no clock initialization is required. Because the counter does not stop or get affected by CPU power down, and it supports oneshot mode, it is typically used as a tick broadcast timer. Signed-off-by: Hao-Wen Ting Signed-off-by: Daniel Lezcano Acked-by: Thomas Gleixner Link: https://patch.msgid.link/20251126060110.198330-3-haowen.ting@realtek.com --- MAINTAINERS | 5 + drivers/clocksource/Kconfig | 11 ++ drivers/clocksource/Makefile | 1 + drivers/clocksource/timer-realtek.c | 150 ++++++++++++++++++++++++++++ 4 files changed, 167 insertions(+) create mode 100644 drivers/clocksource/timer-realtek.c diff --git a/MAINTAINERS b/MAINTAINERS index 3da2c26a796b..b72b9873b90e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -21669,6 +21669,11 @@ S: Maintained F: Documentation/devicetree/bindings/spi/realtek,rtl9301-snand.yaml F: drivers/spi/spi-realtek-rtl-snand.c +REALTEK SYSTIMER DRIVER +M: Hao-Wen Ting +S: Maintained +F: drivers/clocksource/timer-realtek.c + REALTEK WIRELESS DRIVER (rtlwifi family) M: Ping-Ke Shih L: linux-wireless@vger.kernel.org diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index ffcd23668763..aa59e5b13351 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -782,4 +782,15 @@ config NXP_STM_TIMER Enables the support for NXP System Timer Module found in the s32g NXP platform series. +config RTK_SYSTIMER + bool "Realtek SYSTIMER support" + depends on ARM || ARM64 + depends on ARCH_REALTEK || COMPILE_TEST + select TIMER_OF + help + This option enables the driver that registers the global 1 MHz hardware + counter as a clock event device on Realtek SoCs. Make sure to enable + this option only when building for a Realtek platform or for compilation + testing. + endmenu diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index ec4452ee958f..b46376af6b49 100644 --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile @@ -95,3 +95,4 @@ obj-$(CONFIG_CLKSRC_LOONGSON1_PWM) += timer-loongson1-pwm.o obj-$(CONFIG_EP93XX_TIMER) += timer-ep93xx.o obj-$(CONFIG_RALINK_TIMER) += timer-ralink.o obj-$(CONFIG_NXP_STM_TIMER) += timer-nxp-stm.o +obj-$(CONFIG_RTK_SYSTIMER) += timer-realtek.o diff --git a/drivers/clocksource/timer-realtek.c b/drivers/clocksource/timer-realtek.c new file mode 100644 index 000000000000..4f0439de9939 --- /dev/null +++ b/drivers/clocksource/timer-realtek.c @@ -0,0 +1,150 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2025 Realtek Semiconductor Corp. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include "timer-of.h" + +#define ENBL 1 +#define DSBL 0 + +#define SYSTIMER_RATE 1000000 +#define SYSTIMER_MIN_DELTA 0x64 +#define SYSTIMER_MAX_DELTA ULONG_MAX + +/* SYSTIMER Register Offset (RTK Internal Use) */ +#define TS_LW_OFST 0x0 +#define TS_HW_OFST 0x4 +#define TS_CMP_VAL_LW_OFST 0x8 +#define TS_CMP_VAL_HW_OFST 0xC +#define TS_CMP_CTRL_OFST 0x10 +#define TS_CMP_STAT_OFST 0x14 + +/* SYSTIMER CMP CTRL REG Mask */ +#define TS_CMP_EN_MASK 0x1 +#define TS_WR_EN0_MASK 0x2 + +static void __iomem *systimer_base; + +static u64 rtk_ts64_read(void) +{ + u32 low, high; + u64 ts; + + /* Caution: Read LSB word (TS_LW_OFST) first then MSB (TS_HW_OFST) */ + low = readl(systimer_base + TS_LW_OFST); + high = readl(systimer_base + TS_HW_OFST); + ts = ((u64)high << 32) | low; + + return ts; +} + +static void rtk_cmp_value_write(u64 value) +{ + u32 high, low; + + low = value & 0xFFFFFFFF; + high = value >> 32; + + writel(high, systimer_base + TS_CMP_VAL_HW_OFST); + writel(low, systimer_base + TS_CMP_VAL_LW_OFST); +} + +static inline void rtk_cmp_en_write(bool cmp_en) +{ + u32 val; + + val = TS_WR_EN0_MASK; + if (cmp_en == ENBL) + val |= TS_CMP_EN_MASK; + + writel(val, systimer_base + TS_CMP_CTRL_OFST); +} + +static int rtk_syst_clkevt_next_event(unsigned long cycles, struct clock_event_device *clkevt) +{ + u64 cmp_val; + + rtk_cmp_en_write(DSBL); + cmp_val = rtk_ts64_read(); + + /* Set CMP value to current timestamp plus delta_us */ + rtk_cmp_value_write(cmp_val + cycles); + rtk_cmp_en_write(ENBL); + return 0; +} + +static irqreturn_t rtk_ts_match_intr_handler(int irq, void *dev_id) +{ + struct clock_event_device *clkevt = dev_id; + void __iomem *reg_base; + u32 val; + + /* Disable TS CMP Match */ + rtk_cmp_en_write(DSBL); + + /* Clear TS CMP INTR */ + reg_base = systimer_base + TS_CMP_STAT_OFST; + val = readl(reg_base) & TS_CMP_EN_MASK; + writel(val | TS_CMP_EN_MASK, reg_base); + clkevt->event_handler(clkevt); + + return IRQ_HANDLED; +} + +static int rtk_syst_shutdown(struct clock_event_device *clkevt) +{ + void __iomem *reg_base; + u64 cmp_val = 0; + + /* Disable TS CMP Match */ + rtk_cmp_en_write(DSBL); + /* Set compare value to 0 */ + rtk_cmp_value_write(cmp_val); + + /* Clear TS CMP INTR */ + reg_base = systimer_base + TS_CMP_STAT_OFST; + writel(TS_CMP_EN_MASK, reg_base); + return 0; +} + +static struct timer_of rtk_timer_to = { + .flags = TIMER_OF_IRQ | TIMER_OF_BASE, + + .clkevt = { + .name = "rtk-clkevt", + .rating = 300, + .cpumask = cpu_possible_mask, + .features = CLOCK_EVT_FEAT_DYNIRQ | + CLOCK_EVT_FEAT_ONESHOT, + .set_next_event = rtk_syst_clkevt_next_event, + .set_state_oneshot = rtk_syst_shutdown, + .set_state_shutdown = rtk_syst_shutdown, + }, + + .of_irq = { + .flags = IRQF_TIMER | IRQF_IRQPOLL, + .handler = rtk_ts_match_intr_handler, + }, +}; + +static int __init rtk_systimer_init(struct device_node *node) +{ + int ret; + + ret = timer_of_init(node, &rtk_timer_to); + if (ret) + return ret; + + systimer_base = timer_of_base(&rtk_timer_to); + clockevents_config_and_register(&rtk_timer_to.clkevt, SYSTIMER_RATE, + SYSTIMER_MIN_DELTA, SYSTIMER_MAX_DELTA); + + return 0; +} + +TIMER_OF_DECLARE(rtk_systimer, "realtek,rtd1625-systimer", rtk_systimer_init);