From f21475839f10eec2ad48a2a7b24db1867793efbc Mon Sep 17 00:00:00 2001 From: Markus Stockhausen Date: Sat, 19 Jul 2025 16:14:28 -0400 Subject: [PATCH] realtek: fix stall after restart of otto timer Once tested this will go upstream. Signed-off-by: Markus Stockhausen Link: https://github.com/openwrt/openwrt/pull/19468 Signed-off-by: Robert Marko --- ...4-timer-otto-fix-stall-after-restart.patch | 146 ++++++++++++++++++ 1 file changed, 146 insertions(+) create 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/304-timer-otto-fix-stall-after-restart.patch b/target/linux/realtek/patches-6.12/304-timer-otto-fix-stall-after-restart.patch new file mode 100644 index 00000000000..72cfd703781 --- /dev/null +++ b/target/linux/realtek/patches-6.12/304-timer-otto-fix-stall-after-restart.patch @@ -0,0 +1,146 @@ +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.2