]> git.ipfire.org Git - thirdparty/openwrt.git/commitdiff
realtek: timer: replace downstream with upstream patches 20097/head
authorMarkus Stockhausen <markus.stockhausen@gmx.de>
Fri, 19 Sep 2025 19:35:05 +0000 (15:35 -0400)
committerRobert Marko <robimarko@gmail.com>
Sat, 20 Sep 2025 10:49:51 +0000 (12:49 +0200)
The fixes for the dying timers were finally accepted upstream.

Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
Link: https://github.com/openwrt/openwrt/pull/20097
Signed-off-by: Robert Marko <robimarko@gmail.com>
target/linux/realtek/patches-6.12/020-01-v6.18-timer-rtl-otto-work-around-dying-timers.patch [new file with mode: 0644]
target/linux/realtek/patches-6.12/020-02-v6.18-timer-rtl-otto-drop-set-counter-function.patch [new file with mode: 0644]
target/linux/realtek/patches-6.12/020-03-v6.18-timer-rtl-otto-do-not-interfere-with-interrupts.patch [new file with mode: 0644]
target/linux/realtek/patches-6.12/304-timer-otto-fix-stall-after-restart.patch [deleted file]

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 (file)
index 0000000..19bbc38
--- /dev/null
@@ -0,0 +1,103 @@
+From 9f146b3e0b9e098cf36ebe42b4aa69270113c6bf Mon Sep 17 00:00:00 2001
+From: Markus Stockhausen <markus.stockhausen@gmx.de>
+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 <howels@allthatwemight.be>
+Tested-by: Bjørn Mork <bjorn@mork.no>
+Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
+Link: https://lore.kernel.org/r/20250804080328.2609287-2-markus.stockhausen@gmx.de
+Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
+---
+ 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 (file)
index 0000000..5ee2f07
--- /dev/null
@@ -0,0 +1,51 @@
+From 85e27f218121bdaa8e8afd68674262aa154d2cb4 Mon Sep 17 00:00:00 2001
+From: Markus Stockhausen <markus.stockhausen@gmx.de>
+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 <howels@allthatwemight.be>
+Tested-by: Bjørn Mork <bjorn@mork.no>
+Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
+Link: https://lore.kernel.org/r/20250804080328.2609287-3-markus.stockhausen@gmx.de
+Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
+---
+ 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 (file)
index 0000000..c806a21
--- /dev/null
@@ -0,0 +1,60 @@
+From add0d895aa6f66320f9b1d901b66259f4308af04 Mon Sep 17 00:00:00 2001
+From: Markus Stockhausen <markus.stockhausen@gmx.de>
+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 <howels@allthatwemight.be>
+Tested-by: Bjørn Mork <bjorn@mork.no>
+Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
+Link: https://lore.kernel.org/r/20250804080328.2609287-4-markus.stockhausen@gmx.de
+Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
+---
+ 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 (file)
index 72cfd70..0000000
+++ /dev/null
@@ -1,146 +0,0 @@
-From: Markus Stockhausen <markus.stockhausen@gmx.de>
-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 <markus.stockhausen@gmx.de>
----
-
---- 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);