Skip to content

Commit a105ec2

Browse files
plappermaulgregkh
authored andcommitted
clocksource/drivers/timer-rtl-otto: Work around dying timers
[ Upstream commit e7a2510 ] The OpenWrt distribution has switched from kernel longterm 6.6 to 6.12. Reports show that devices with the Realtek Otto switch platform die during operation and are rebooted by the watchdog. Sorting out other possible reasons the Otto timer is to blame. The platform currently consists of 4 targets with different hardware revisions. It is not 100% clear which devices and revisions are affected. Analysis shows: A more aggressive sched/deadline handling leads to more timer starts with small intervals. This increases the bug chances. See https://marc.info/?l=linux-kernel&m=175276556023276&w=2 Focusing on the real issue a hardware limitation on some devices was found. There is a minimal chance that a timer ends without firing an interrupt if it is reprogrammed within the 5us before its expiration time. Work around this issue by introducing a bounce() function. It restarts the timer directly before the normal restart functions as follows: - Stop timer - Restart timer with a slow frequency. - Target time will be >5us - The subsequent normal restart is outside the critical window Downstream has already tested and confirmed a patch. See openwrt/openwrt#19468 https://forum.openwrt.org/t/support-for-rtl838x-based-managed-switches/57875/3788 Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de> Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org> Tested-by: Stephen Howell <howels@allthatwemight.be> Tested-by: Bjørn Mork <bjorn@mork.no> Link: https://lore.kernel.org/r/20250804080328.2609287-2-markus.stockhausen@gmx.de Signed-off-by: Sasha Levin <sashal@kernel.org>
1 parent a654de8 commit a105ec2

File tree

1 file changed

+20
-0
lines changed

1 file changed

+20
-0
lines changed

drivers/clocksource/timer-rtl-otto.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
#define RTTM_BIT_COUNT 28
3939
#define RTTM_MIN_DELTA 8
4040
#define RTTM_MAX_DELTA CLOCKSOURCE_MASK(28)
41+
#define RTTM_MAX_DIVISOR GENMASK(15, 0)
4142

4243
/*
4344
* Timers are derived from the LXB clock frequency. Usually this is a fixed
@@ -112,6 +113,22 @@ static irqreturn_t rttm_timer_interrupt(int irq, void *dev_id)
112113
return IRQ_HANDLED;
113114
}
114115

116+
static void rttm_bounce_timer(void __iomem *base, u32 mode)
117+
{
118+
/*
119+
* When a running timer has less than ~5us left, a stop/start sequence
120+
* might fail. While the details are unknown the most evident effect is
121+
* that the subsequent interrupt will not be fired.
122+
*
123+
* As a workaround issue an intermediate restart with a very slow
124+
* frequency of ~3kHz keeping the target counter (>=8). So the follow
125+
* up restart will always be issued outside the critical window.
126+
*/
127+
128+
rttm_disable_timer(base);
129+
rttm_enable_timer(base, mode, RTTM_MAX_DIVISOR);
130+
}
131+
115132
static void rttm_stop_timer(void __iomem *base)
116133
{
117134
rttm_disable_timer(base);
@@ -129,6 +146,7 @@ static int rttm_next_event(unsigned long delta, struct clock_event_device *clkev
129146
struct timer_of *to = to_timer_of(clkevt);
130147

131148
RTTM_DEBUG(to->of_base.base);
149+
rttm_bounce_timer(to->of_base.base, RTTM_CTRL_COUNTER);
132150
rttm_stop_timer(to->of_base.base);
133151
rttm_set_period(to->of_base.base, delta);
134152
rttm_start_timer(to, RTTM_CTRL_COUNTER);
@@ -141,6 +159,7 @@ static int rttm_state_oneshot(struct clock_event_device *clkevt)
141159
struct timer_of *to = to_timer_of(clkevt);
142160

143161
RTTM_DEBUG(to->of_base.base);
162+
rttm_bounce_timer(to->of_base.base, RTTM_CTRL_COUNTER);
144163
rttm_stop_timer(to->of_base.base);
145164
rttm_set_period(to->of_base.base, RTTM_TICKS_PER_SEC / HZ);
146165
rttm_start_timer(to, RTTM_CTRL_COUNTER);
@@ -153,6 +172,7 @@ static int rttm_state_periodic(struct clock_event_device *clkevt)
153172
struct timer_of *to = to_timer_of(clkevt);
154173

155174
RTTM_DEBUG(to->of_base.base);
175+
rttm_bounce_timer(to->of_base.base, RTTM_CTRL_TIMER);
156176
rttm_stop_timer(to->of_base.base);
157177
rttm_set_period(to->of_base.base, RTTM_TICKS_PER_SEC / HZ);
158178
rttm_start_timer(to, RTTM_CTRL_TIMER);

0 commit comments

Comments
 (0)