From: Enric Balletbo i Serra <eballetb@redhat.com>
To: Daniel Lezcano <daniel.lezcano@kernel.org>,
Thomas Gleixner <tglx@kernel.org>,
Maxime Coquelin <mcoquelin.stm32@gmail.com>,
Alexandre Torgue <alexandre.torgue@foss.st.com>
Cc: linux-kernel@vger.kernel.org,
linux-arm-kernel@lists.infradead.org,
linux-rt-devel@lists.linux.dev,
Ghennadi Procopciuc <ghennadi.procopciuc@oss.nxp.com>,
NXP S32 Linux Team <s32@nxp.com>,
stable@vger.kernel.org,
linux-stm32@st-md-mailman.stormreply.com,
Enric Balletbo i Serra <eballetb@redhat.com>
Subject: [PATCH v3 3/3] clocksource/drivers/nxp-stm: Arm the compare channel from the state callbacks
Date: Thu, 05 Nov 2026 09:18:34 +0100 [thread overview]
Message-ID: <20261105-nxp-stm-fixes-v3-3-8129b912e74b@redhat.com> (raw)
In-Reply-To: <20261105-nxp-stm-fixes-v3-0-8129b912e74b@redhat.com>
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@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
next prev parent reply other threads:[~2026-11-05 8:18 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-05 8:18 [PATCH v3 0/3] Fix NXP STM clockevent bugs causing lockups Enric Balletbo i Serra
2026-11-05 8:18 ` [PATCH v3 1/3] clocksource/drivers/nxp-stm: Fix max_delta overflow on 64-bit platforms Enric Balletbo i Serra
2026-11-05 8:18 ` [PATCH v3 2/3] clocksource/drivers/nxp-stm: Fix overshoot detection in set_next_event Enric Balletbo i Serra
2026-11-05 8:18 ` Enric Balletbo i Serra [this message]
2026-11-06 8:42 ` [PATCH v3 0/3] Fix NXP STM clockevent bugs causing lockups Khristine Andreea Barbulescu
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=20261105-nxp-stm-fixes-v3-3-8129b912e74b@redhat.com \
--to=eballetb@redhat.com \
--cc=alexandre.torgue@foss.st.com \
--cc=daniel.lezcano@kernel.org \
--cc=ghennadi.procopciuc@oss.nxp.com \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-rt-devel@lists.linux.dev \
--cc=linux-stm32@st-md-mailman.stormreply.com \
--cc=mcoquelin.stm32@gmail.com \
--cc=s32@nxp.com \
--cc=stable@vger.kernel.org \
--cc=tglx@kernel.org \
/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®