Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

clocksource/drivers/timer-rtl-otto: Work around dying timers

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
https://github.com/openwrt/openwrt/pull/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

authored by

Markus Stockhausen and committed by
Daniel Lezcano
e7a25106 0494fc34

+20
+20
drivers/clocksource/timer-rtl-otto.c
··· 38 38 #define RTTM_BIT_COUNT 28 39 39 #define RTTM_MIN_DELTA 8 40 40 #define RTTM_MAX_DELTA CLOCKSOURCE_MASK(28) 41 + #define RTTM_MAX_DIVISOR GENMASK(15, 0) 41 42 42 43 /* 43 44 * Timers are derived from the LXB clock frequency. Usually this is a fixed ··· 113 112 return IRQ_HANDLED; 114 113 } 115 114 115 + static void rttm_bounce_timer(void __iomem *base, u32 mode) 116 + { 117 + /* 118 + * When a running timer has less than ~5us left, a stop/start sequence 119 + * might fail. While the details are unknown the most evident effect is 120 + * that the subsequent interrupt will not be fired. 121 + * 122 + * As a workaround issue an intermediate restart with a very slow 123 + * frequency of ~3kHz keeping the target counter (>=8). So the follow 124 + * up restart will always be issued outside the critical window. 125 + */ 126 + 127 + rttm_disable_timer(base); 128 + rttm_enable_timer(base, mode, RTTM_MAX_DIVISOR); 129 + } 130 + 116 131 static void rttm_stop_timer(void __iomem *base) 117 132 { 118 133 rttm_disable_timer(base); ··· 146 129 struct timer_of *to = to_timer_of(clkevt); 147 130 148 131 RTTM_DEBUG(to->of_base.base); 132 + rttm_bounce_timer(to->of_base.base, RTTM_CTRL_COUNTER); 149 133 rttm_stop_timer(to->of_base.base); 150 134 rttm_set_period(to->of_base.base, delta); 151 135 rttm_start_timer(to, RTTM_CTRL_COUNTER); ··· 159 141 struct timer_of *to = to_timer_of(clkevt); 160 142 161 143 RTTM_DEBUG(to->of_base.base); 144 + rttm_bounce_timer(to->of_base.base, RTTM_CTRL_COUNTER); 162 145 rttm_stop_timer(to->of_base.base); 163 146 rttm_set_period(to->of_base.base, RTTM_TICKS_PER_SEC / HZ); 164 147 rttm_start_timer(to, RTTM_CTRL_COUNTER); ··· 172 153 struct timer_of *to = to_timer_of(clkevt); 173 154 174 155 RTTM_DEBUG(to->of_base.base); 156 + rttm_bounce_timer(to->of_base.base, RTTM_CTRL_TIMER); 175 157 rttm_stop_timer(to->of_base.base); 176 158 rttm_set_period(to->of_base.base, RTTM_TICKS_PER_SEC / HZ); 177 159 rttm_start_timer(to, RTTM_CTRL_TIMER);