mirror of
git://git.openwrt.org/openwrt/openwrt.git
synced 2026-03-07 17:49:29 -05:00
realtek: timer: replace downstream with upstream patches
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>
This commit is contained in:
committed by
Robert Marko
parent
95e04234c1
commit
e31127497c
@@ -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);
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user