[Linux-stm32] [PATCH v3 3/3] clocksource/drivers/nxp-stm: Arm the compare channel from the state callbacks
Enric Balletbo i Serra
eballetb at redhat.com
Thu Nov 5 09:18:34 CET 2026
set_next_event() disables the compare channel, programs the comparator
and enables the channel again after the overshoot check. The trailing
channel-enable write is a posted write landing after the check: if the
counter passes the programmed value before the enable takes effect, the
match is missed. No min_delta can close that window.
Keep the channel armed for the whole ONESHOT operation instead, and
manage it from the state callbacks, like the HPET driver does:
- set_state_oneshot() and set_state_periodic() enable the channel;
a stale pending interrupt flag is cleared before enabling.
set_state_oneshot_stopped() and set_state_shutdown() disable it.
- set_next_event() now only writes the comparator, covered by the
overshoot check.
- The interrupt handler reprograms the comparator only in periodic
mode; in ONESHOT mode the stale value stays armed until the core
programs a new event or stops the channel.
Fixes: cec32ac75827 ("clocksource/drivers/nxp-timer: Add the System Timer Module for the s32gx platforms")
Assisted-by: Claude:claude-opus-4-6
Signed-off-by: Enric Balletbo i Serra <eballetb at redhat.com>
---
drivers/clocksource/timer-nxp-stm.c | 52 +++++++++++++++++++++++--------------
1 file changed, 32 insertions(+), 20 deletions(-)
diff --git a/drivers/clocksource/timer-nxp-stm.c b/drivers/clocksource/timer-nxp-stm.c
index 81527745b7e2..8ad99863bdff 100644
--- a/drivers/clocksource/timer-nxp-stm.c
+++ b/drivers/clocksource/timer-nxp-stm.c
@@ -254,14 +254,27 @@ static int nxp_stm_clockevent_shutdown(struct clock_event_device *ced)
return 0;
}
+static int nxp_stm_clockevent_set_oneshot(struct clock_event_device *ced)
+{
+ struct stm_timer *stm_timer = ced_to_stm(ced);
+
+ /*
+ * Clear a stale interrupt flag so that enabling the channel
+ * cannot fire an event from a previous match.
+ */
+ writel(STM_CIR_CIF, STM_CIR0(stm_timer->base));
+
+ nxp_stm_clockevent_enable(stm_timer);
+
+ return 0;
+}
+
static int nxp_stm_clockevent_set_next_event(unsigned long delta, struct clock_event_device *ced)
{
struct stm_timer *stm_timer = ced_to_stm(ced);
u32 val;
s32 res;
- nxp_stm_clockevent_disable(stm_timer);
-
stm_timer->delta = delta;
val = nxp_stm_clockevent_read_counter(stm_timer) + delta;
@@ -288,18 +301,16 @@ static int nxp_stm_clockevent_set_next_event(unsigned long delta, struct clock_e
*/
res = (s32)(val - nxp_stm_clockevent_read_counter(stm_timer));
- if (res < STM_MIN_CYCLES)
- return -ETIME;
-
- nxp_stm_clockevent_enable(stm_timer);
-
- return 0;
+ return res < STM_MIN_CYCLES ? -ETIME : 0;
}
static int nxp_stm_clockevent_set_periodic(struct clock_event_device *ced)
{
struct stm_timer *stm_timer = ced_to_stm(ced);
+ writel(STM_CIR_CIF, STM_CIR0(stm_timer->base));
+ nxp_stm_clockevent_enable(stm_timer);
+
return nxp_stm_clockevent_set_next_event(stm_timer->rate, ced);
}
@@ -327,6 +338,8 @@ static int nxp_stm_clockevent_per_cpu_init(struct device *dev, struct stm_timer
stm_timer->ced.name = name;
stm_timer->ced.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
stm_timer->ced.set_state_shutdown = nxp_stm_clockevent_shutdown;
+ stm_timer->ced.set_state_oneshot = nxp_stm_clockevent_set_oneshot;
+ stm_timer->ced.set_state_oneshot_stopped = nxp_stm_clockevent_shutdown;
stm_timer->ced.set_state_periodic = nxp_stm_clockevent_set_periodic;
stm_timer->ced.set_next_event = nxp_stm_clockevent_set_next_event;
stm_timer->ced.suspend = nxp_stm_clockevent_suspend;
@@ -384,19 +397,18 @@ static irqreturn_t nxp_stm_module_interrupt(int irq, void *dev_id)
writel(STM_CIR_CIF, STM_CIR0(stm_timer->base));
/*
- * Update STM_CMP value using the counter value
+ * The STM hardware has no periodic mode: the comparator stays
+ * armed after a match, so periodic mode is emulated by
+ * reprogramming the comparator here. In ONESHOT mode the stale
+ * comparator value stays armed instead; the clockevents core
+ * either programs a new event or stops the channel long before
+ * the counter can wrap around to the stale value (~32.2s at
+ * 133 MHz).
*/
- val = nxp_stm_clockevent_read_counter(stm_timer) + stm_timer->delta;
-
- writel(val, STM_CMP0(stm_timer->base));
-
- /*
- * stm hardware doesn't support oneshot, it will generate an
- * interrupt and start the counter again so software needs to
- * disable the timer to stop the counter loop in ONESHOT mode.
- */
- if (likely(clockevent_state_oneshot(ced)))
- nxp_stm_clockevent_disable(stm_timer);
+ if (clockevent_state_periodic(ced)) {
+ val = nxp_stm_clockevent_read_counter(stm_timer) + stm_timer->delta;
+ writel(val, STM_CMP0(stm_timer->base));
+ }
ced->event_handler(ced);
--
2.56.0
More information about the Linux-stm32
mailing list