From e31127497c7cc35ddc121eef8918736539dbf80f Mon Sep 17 00:00:00 2001 From: Markus Stockhausen Date: Fri, 19 Sep 2025 15:35:05 -0400 Subject: [PATCH] realtek: timer: replace downstream with upstream patches The fixes for the dying timers were finally accepted upstream. Signed-off-by: Markus Stockhausen Link: https://github.com/openwrt/openwrt/pull/20097 Signed-off-by: Robert Marko --- ...er-rtl-otto-work-around-dying-timers.patch | 103 ++++++++++++ ...r-rtl-otto-drop-set-counter-function.patch | 51 ++++++ ...tto-do-not-interfere-with-interrupts.patch | 60 +++++++ ...4-timer-otto-fix-stall-after-restart.patch | 146 ------------------ 4 files changed, 214 insertions(+), 146 deletions(-) create mode 100644 target/linux/realtek/patches-6.12/020-01-v6.18-timer-rtl-otto-work-around-dying-timers.patch create mode 100644 target/linux/realtek/patches-6.12/020-02-v6.18-timer-rtl-otto-drop-set-counter-function.patch create mode 100644 target/linux/realtek/patches-6.12/020-03-v6.18-timer-rtl-otto-do-not-interfere-with-interrupts.patch delete mode 100644 target/linux/realtek/patches-6.12/304-timer-otto-fix-stall-after-restart.patch diff --git a/target/linux/realtek/patches-6.12/020-01-v6.18-timer-rtl-otto-work-around-dying-timers.patch b/target/linux/realtek/patches-6.12/020-01-v6.18-timer-rtl-otto-work-around-dying-timers.patch new file mode 100644 index 00000000000..19bbc384a2f --- /dev/null +++ b/target/linux/realtek/patches-6.12/020-01-v6.18-timer-rtl-otto-work-around-dying-timers.patch @@ -0,0 +1,103 @@ +From 9f146b3e0b9e098cf36ebe42b4aa69270113c6bf Mon Sep 17 00:00:00 2001 +From: Markus Stockhausen +Date: Mon, 4 Aug 2025 04:03:25 -0400 +Subject: clocksource/drivers/timer-rtl-otto: Work around dying timers +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +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 + +Tested-by: Stephen Howell +Tested-by: Bjørn Mork +Signed-off-by: Markus Stockhausen +Link: https://lore.kernel.org/r/20250804080328.2609287-2-markus.stockhausen@gmx.de +Signed-off-by: Daniel Lezcano +--- + drivers/clocksource/timer-rtl-otto.c | 20 ++++++++++++++++++++ + 1 file changed, 20 insertions(+) + +--- a/drivers/clocksource/timer-rtl-otto.c ++++ b/drivers/clocksource/timer-rtl-otto.c +@@ -38,6 +38,7 @@ + #define RTTM_BIT_COUNT 28 + #define RTTM_MIN_DELTA 8 + #define RTTM_MAX_DELTA CLOCKSOURCE_MASK(28) ++#define RTTM_MAX_DIVISOR GENMASK(15, 0) + + /* + * Timers are derived from the LXB clock frequency. Usually this is a fixed +@@ -112,6 +113,22 @@ static irqreturn_t rttm_timer_interrupt( + return IRQ_HANDLED; + } + ++static void rttm_bounce_timer(void __iomem *base, u32 mode) ++{ ++ /* ++ * When a running timer has less than ~5us left, a stop/start sequence ++ * might fail. While the details are unknown the most evident effect is ++ * that the subsequent interrupt will not be fired. ++ * ++ * As a workaround issue an intermediate restart with a very slow ++ * frequency of ~3kHz keeping the target counter (>=8). So the follow ++ * up restart will always be issued outside the critical window. ++ */ ++ ++ rttm_disable_timer(base); ++ rttm_enable_timer(base, mode, RTTM_MAX_DIVISOR); ++} ++ + static void rttm_stop_timer(void __iomem *base) + { + rttm_disable_timer(base); +@@ -129,6 +146,7 @@ static int rttm_next_event(unsigned long + struct timer_of *to = to_timer_of(clkevt); + + RTTM_DEBUG(to->of_base.base); ++ rttm_bounce_timer(to->of_base.base, RTTM_CTRL_COUNTER); + rttm_stop_timer(to->of_base.base); + rttm_set_period(to->of_base.base, delta); + rttm_start_timer(to, RTTM_CTRL_COUNTER); +@@ -141,6 +159,7 @@ static int rttm_state_oneshot(struct clo + struct timer_of *to = to_timer_of(clkevt); + + RTTM_DEBUG(to->of_base.base); ++ rttm_bounce_timer(to->of_base.base, RTTM_CTRL_COUNTER); + rttm_stop_timer(to->of_base.base); + rttm_set_period(to->of_base.base, RTTM_TICKS_PER_SEC / HZ); + rttm_start_timer(to, RTTM_CTRL_COUNTER); +@@ -153,6 +172,7 @@ static int rttm_state_periodic(struct cl + struct timer_of *to = to_timer_of(clkevt); + + RTTM_DEBUG(to->of_base.base); ++ rttm_bounce_timer(to->of_base.base, RTTM_CTRL_TIMER); + rttm_stop_timer(to->of_base.base); + rttm_set_period(to->of_base.base, RTTM_TICKS_PER_SEC / HZ); + rttm_start_timer(to, RTTM_CTRL_TIMER); diff --git a/target/linux/realtek/patches-6.12/020-02-v6.18-timer-rtl-otto-drop-set-counter-function.patch b/target/linux/realtek/patches-6.12/020-02-v6.18-timer-rtl-otto-drop-set-counter-function.patch new file mode 100644 index 00000000000..5ee2f07f273 --- /dev/null +++ b/target/linux/realtek/patches-6.12/020-02-v6.18-timer-rtl-otto-drop-set-counter-function.patch @@ -0,0 +1,51 @@ +From 85e27f218121bdaa8e8afd68674262aa154d2cb4 Mon Sep 17 00:00:00 2001 +From: Markus Stockhausen +Date: Mon, 4 Aug 2025 04:03:26 -0400 +Subject: clocksource/drivers/timer-rtl-otto: Drop set_counter function +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The current counter value is a read only register. It will be +reset when writing a new target timer value with rttm_set_period(). +rttm_set_counter() is essentially a noop. Drop it. + +While this makes rttm_start_timer() and rttm_enable_timer() the +same functions keep both to make the established abstraction layers +for register and control functions active. + +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 + +Tested-by: Stephen Howell +Tested-by: Bjørn Mork +Signed-off-by: Markus Stockhausen +Link: https://lore.kernel.org/r/20250804080328.2609287-3-markus.stockhausen@gmx.de +Signed-off-by: Daniel Lezcano +--- + drivers/clocksource/timer-rtl-otto.c | 6 ------ + 1 file changed, 6 deletions(-) + +--- a/drivers/clocksource/timer-rtl-otto.c ++++ b/drivers/clocksource/timer-rtl-otto.c +@@ -56,11 +56,6 @@ struct rttm_cs { + }; + + /* Simple internal register functions */ +-static inline void rttm_set_counter(void __iomem *base, unsigned int counter) +-{ +- iowrite32(counter, base + RTTM_CNT); +-} +- + static inline unsigned int rttm_get_counter(void __iomem *base) + { + return ioread32(base + RTTM_CNT); +@@ -137,7 +132,6 @@ static void rttm_stop_timer(void __iomem + + static void rttm_start_timer(struct timer_of *to, u32 mode) + { +- rttm_set_counter(to->of_base.base, 0); + rttm_enable_timer(to->of_base.base, mode, to->of_clk.rate / RTTM_TICKS_PER_SEC); + } + diff --git a/target/linux/realtek/patches-6.12/020-03-v6.18-timer-rtl-otto-do-not-interfere-with-interrupts.patch b/target/linux/realtek/patches-6.12/020-03-v6.18-timer-rtl-otto-do-not-interfere-with-interrupts.patch new file mode 100644 index 00000000000..c806a217ab2 --- /dev/null +++ b/target/linux/realtek/patches-6.12/020-03-v6.18-timer-rtl-otto-do-not-interfere-with-interrupts.patch @@ -0,0 +1,60 @@ +From add0d895aa6f66320f9b1d901b66259f4308af04 Mon Sep 17 00:00:00 2001 +From: Markus Stockhausen +Date: Mon, 4 Aug 2025 04:03:27 -0400 +Subject: clocksource/drivers/timer-rtl-otto: Do not interfere with interrupts +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +During normal operation the timers are reprogrammed including an +interrupt acknowledgement. This has no effect as the whole timer +is setup from scratch afterwards. Especially in an interrupt this +has already been done by rttm_timer_interrupt(). + +Change the behaviour as follows: + +- Use rttm_disable_timer() during reprogramming +- Keep rttm_stop_timer() for all other use cases. + +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 + +Tested-by: Stephen Howell +Tested-by: Bjørn Mork +Signed-off-by: Markus Stockhausen +Link: https://lore.kernel.org/r/20250804080328.2609287-4-markus.stockhausen@gmx.de +Signed-off-by: Daniel Lezcano +--- + drivers/clocksource/timer-rtl-otto.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +--- a/drivers/clocksource/timer-rtl-otto.c ++++ b/drivers/clocksource/timer-rtl-otto.c +@@ -141,7 +141,7 @@ static int rttm_next_event(unsigned long + + RTTM_DEBUG(to->of_base.base); + rttm_bounce_timer(to->of_base.base, RTTM_CTRL_COUNTER); +- rttm_stop_timer(to->of_base.base); ++ rttm_disable_timer(to->of_base.base); + rttm_set_period(to->of_base.base, delta); + rttm_start_timer(to, RTTM_CTRL_COUNTER); + +@@ -154,7 +154,7 @@ static int rttm_state_oneshot(struct clo + + RTTM_DEBUG(to->of_base.base); + rttm_bounce_timer(to->of_base.base, RTTM_CTRL_COUNTER); +- rttm_stop_timer(to->of_base.base); ++ rttm_disable_timer(to->of_base.base); + rttm_set_period(to->of_base.base, RTTM_TICKS_PER_SEC / HZ); + rttm_start_timer(to, RTTM_CTRL_COUNTER); + +@@ -167,7 +167,7 @@ static int rttm_state_periodic(struct cl + + RTTM_DEBUG(to->of_base.base); + rttm_bounce_timer(to->of_base.base, RTTM_CTRL_TIMER); +- rttm_stop_timer(to->of_base.base); ++ rttm_disable_timer(to->of_base.base); + rttm_set_period(to->of_base.base, RTTM_TICKS_PER_SEC / HZ); + rttm_start_timer(to, RTTM_CTRL_TIMER); + diff --git a/target/linux/realtek/patches-6.12/304-timer-otto-fix-stall-after-restart.patch b/target/linux/realtek/patches-6.12/304-timer-otto-fix-stall-after-restart.patch deleted file mode 100644 index 72cfd703781..00000000000 --- a/target/linux/realtek/patches-6.12/304-timer-otto-fix-stall-after-restart.patch +++ /dev/null @@ -1,146 +0,0 @@ -From: Markus Stockhausen -Date: Sat, 19 Jul 2025 18:22:21 +0200 -Subject: [PATCH] realtek: fix stall after restart of otto timer - -With kernel 6.9 the kernel scheduler has been redesigned. This uncovered -a bug in the realtek timer hardware and a misconception in the driver. - -Regarding the driver: Software cannot set the current counter value to -zero directly. This is automatically done when writing a new target value. -Drop function rttm_set_counter(). Additionally do not use stop timer -during normal operation because it acknowledges interrupts. This should -only be done from the interrupt handler. Replace this with disable_timer(). - -Regarding the hardware: There is a minimal chance that a timer dies if it -is reprogrammed within the 5us before its expiration time. Let's call this -the "critical time window". 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 will be outside the critical window - -While we are here clarify documentation and double the timer frequency to -6.25 Mhz. This allows for more detailed timestamps. - -Signed-off-by: Markus Stockhausen ---- - ---- a/drivers/clocksource/timer-rtl-otto.c -+++ b/drivers/clocksource/timer-rtl-otto.c -@@ -25,12 +25,11 @@ - - /* - * The Otto platform provides multiple 28 bit timers/counters with the following -- * operating logic. If enabled the timer counts up. Per timer one can set a -- * maximum counter value as an end marker. If end marker is reached the timer -- * fires an interrupt. If the timer "overflows" by reaching the end marker or -- * by adding 1 to 0x0fffffff the counter is reset to 0. When this happens and -- * the timer is in operating mode COUNTER it stops. In mode TIMER it will -- * continue to count up. -+ * operating logic. If enabled the timer counts up. Per timer a counter target -+ * value can be set with the minimum being 0x2 and the maximumu being 0xfffffff. -+ * If the the target value is reached the timer is reset to 0. Depending on its -+ * configuration the timer will then fire an interrupt. In case the timer is in -+ * operating mode COUNTER it stops. In mode TIMER it will continue to count up. - */ - #define RTTM_CTRL_COUNTER 0 - #define RTTM_CTRL_TIMER BIT(24) -@@ -38,16 +37,15 @@ - #define RTTM_BIT_COUNT 28 - #define RTTM_MIN_DELTA 8 - #define RTTM_MAX_DELTA CLOCKSOURCE_MASK(28) -+#define RTTM_MAX_DIVISOR GENMASK(15, 0) - - /* -- * Timers are derived from the LXB clock frequency. Usually this is a fixed -- * multiple of the 25 MHz oscillator. The 930X SOC is an exception from that. -- * Its LXB clock has only dividers and uses the switch PLL of 2.45 GHz as its -- * base. The only meaningful frequencies we can achieve from that are 175.000 -- * MHz and 153.125 MHz. The greatest common divisor of all explained possible -- * speeds is 3125000. Pin the timers to this 3.125 MHz reference frequency. -+ * Timers are derived from the lexra bus (LXB) clock frequency. This is 175 MHz -+ * on RTL930x and 200 MHz on the other platforms. With 6.25 MHz choose a common -+ * divisor to have enough range and detail. This even allows to compare the -+ * different platforms more easily. - */ --#define RTTM_TICKS_PER_SEC 3125000 -+#define RTTM_TICKS_PER_SEC 6250000 - - struct rttm_cs { - struct timer_of to; -@@ -55,11 +53,6 @@ struct rttm_cs { - }; - - /* Simple internal register functions */ --static inline void rttm_set_counter(void __iomem *base, unsigned int counter) --{ -- iowrite32(counter, base + RTTM_CNT); --} -- - static inline unsigned int rttm_get_counter(void __iomem *base) - { - return ioread32(base + RTTM_CNT); -@@ -112,6 +105,22 @@ static irqreturn_t rttm_timer_interrupt( - return IRQ_HANDLED; - } - -+static void rttm_bounce_timer(void __iomem *base, u32 mode) -+{ -+ /* -+ * When a running timer has less than ~5us left, a stop/start sequence -+ * might fail. While the details are unknown the most evident effect is -+ * that the subsequent interrupt will not be fired. -+ * -+ * As a workaround issue an intermediate restart with a very slow -+ * frequency of ~3kHz keeping the target value. So the actual follow -+ * up restart will always be issued outside the critical window. -+ */ -+ -+ rttm_disable_timer(base); -+ rttm_enable_timer(base, mode, RTTM_MAX_DIVISOR); -+} -+ - static void rttm_stop_timer(void __iomem *base) - { - rttm_disable_timer(base); -@@ -120,7 +129,6 @@ static void rttm_stop_timer(void __iomem - - static void rttm_start_timer(struct timer_of *to, u32 mode) - { -- rttm_set_counter(to->of_base.base, 0); - rttm_enable_timer(to->of_base.base, mode, to->of_clk.rate / RTTM_TICKS_PER_SEC); - } - -@@ -129,7 +137,8 @@ static int rttm_next_event(unsigned long - struct timer_of *to = to_timer_of(clkevt); - - RTTM_DEBUG(to->of_base.base); -- rttm_stop_timer(to->of_base.base); -+ rttm_bounce_timer(to->of_base.base, RTTM_CTRL_COUNTER); -+ rttm_disable_timer(to->of_base.base); - rttm_set_period(to->of_base.base, delta); - rttm_start_timer(to, RTTM_CTRL_COUNTER); - -@@ -141,7 +150,8 @@ static int rttm_state_oneshot(struct clo - struct timer_of *to = to_timer_of(clkevt); - - RTTM_DEBUG(to->of_base.base); -- rttm_stop_timer(to->of_base.base); -+ rttm_bounce_timer(to->of_base.base, RTTM_CTRL_COUNTER); -+ rttm_disable_timer(to->of_base.base); - rttm_set_period(to->of_base.base, RTTM_TICKS_PER_SEC / HZ); - rttm_start_timer(to, RTTM_CTRL_COUNTER); - -@@ -153,7 +163,8 @@ static int rttm_state_periodic(struct cl - struct timer_of *to = to_timer_of(clkevt); - - RTTM_DEBUG(to->of_base.base); -- rttm_stop_timer(to->of_base.base); -+ rttm_bounce_timer(to->of_base.base, RTTM_CTRL_TIMER); -+ rttm_disable_timer(to->of_base.base); - rttm_set_period(to->of_base.base, RTTM_TICKS_PER_SEC / HZ); - rttm_start_timer(to, RTTM_CTRL_TIMER); - -- 2.47.3