Skip to content

Commit f214758

Browse files
plappermaulrobimarko
authored andcommitted
realtek: fix stall after restart of otto timer
Once tested this will go upstream. Signed-off-by: Markus Stockhausen <[email protected]> Link: openwrt/openwrt#19468 Signed-off-by: Robert Marko <[email protected]>
1 parent 522294e commit f214758

File tree

1 file changed

+146
-0
lines changed

1 file changed

+146
-0
lines changed
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
From: Markus Stockhausen <[email protected]>
2+
Date: Sat, 19 Jul 2025 18:22:21 +0200
3+
Subject: [PATCH] realtek: fix stall after restart of otto timer
4+
5+
With kernel 6.9 the kernel scheduler has been redesigned. This uncovered
6+
a bug in the realtek timer hardware and a misconception in the driver.
7+
8+
Regarding the driver: Software cannot set the current counter value to
9+
zero directly. This is automatically done when writing a new target value.
10+
Drop function rttm_set_counter(). Additionally do not use stop timer
11+
during normal operation because it acknowledges interrupts. This should
12+
only be done from the interrupt handler. Replace this with disable_timer().
13+
14+
Regarding the hardware: There is a minimal chance that a timer dies if it
15+
is reprogrammed within the 5us before its expiration time. Let's call this
16+
the "critical time window". Work around this issue by introducing a
17+
bounce() function. It restarts the timer directly before the normal
18+
restart functions as follows:
19+
20+
- Stop timer
21+
- Restart timer with a slow frequency.
22+
- Target time will be >5us
23+
- The subsequent normal restart will be outside the critical window
24+
25+
While we are here clarify documentation and double the timer frequency to
26+
6.25 Mhz. This allows for more detailed timestamps.
27+
28+
Signed-off-by: Markus Stockhausen <[email protected]>
29+
---
30+
31+
--- a/drivers/clocksource/timer-rtl-otto.c
32+
+++ b/drivers/clocksource/timer-rtl-otto.c
33+
@@ -25,12 +25,11 @@
34+
35+
/*
36+
* The Otto platform provides multiple 28 bit timers/counters with the following
37+
- * operating logic. If enabled the timer counts up. Per timer one can set a
38+
- * maximum counter value as an end marker. If end marker is reached the timer
39+
- * fires an interrupt. If the timer "overflows" by reaching the end marker or
40+
- * by adding 1 to 0x0fffffff the counter is reset to 0. When this happens and
41+
- * the timer is in operating mode COUNTER it stops. In mode TIMER it will
42+
- * continue to count up.
43+
+ * operating logic. If enabled the timer counts up. Per timer a counter target
44+
+ * value can be set with the minimum being 0x2 and the maximumu being 0xfffffff.
45+
+ * If the the target value is reached the timer is reset to 0. Depending on its
46+
+ * configuration the timer will then fire an interrupt. In case the timer is in
47+
+ * operating mode COUNTER it stops. In mode TIMER it will continue to count up.
48+
*/
49+
#define RTTM_CTRL_COUNTER 0
50+
#define RTTM_CTRL_TIMER BIT(24)
51+
@@ -38,16 +37,15 @@
52+
#define RTTM_BIT_COUNT 28
53+
#define RTTM_MIN_DELTA 8
54+
#define RTTM_MAX_DELTA CLOCKSOURCE_MASK(28)
55+
+#define RTTM_MAX_DIVISOR GENMASK(15, 0)
56+
57+
/*
58+
- * Timers are derived from the LXB clock frequency. Usually this is a fixed
59+
- * multiple of the 25 MHz oscillator. The 930X SOC is an exception from that.
60+
- * Its LXB clock has only dividers and uses the switch PLL of 2.45 GHz as its
61+
- * base. The only meaningful frequencies we can achieve from that are 175.000
62+
- * MHz and 153.125 MHz. The greatest common divisor of all explained possible
63+
- * speeds is 3125000. Pin the timers to this 3.125 MHz reference frequency.
64+
+ * Timers are derived from the lexra bus (LXB) clock frequency. This is 175 MHz
65+
+ * on RTL930x and 200 MHz on the other platforms. With 6.25 MHz choose a common
66+
+ * divisor to have enough range and detail. This even allows to compare the
67+
+ * different platforms more easily.
68+
*/
69+
-#define RTTM_TICKS_PER_SEC 3125000
70+
+#define RTTM_TICKS_PER_SEC 6250000
71+
72+
struct rttm_cs {
73+
struct timer_of to;
74+
@@ -55,11 +53,6 @@ struct rttm_cs {
75+
};
76+
77+
/* Simple internal register functions */
78+
-static inline void rttm_set_counter(void __iomem *base, unsigned int counter)
79+
-{
80+
- iowrite32(counter, base + RTTM_CNT);
81+
-}
82+
-
83+
static inline unsigned int rttm_get_counter(void __iomem *base)
84+
{
85+
return ioread32(base + RTTM_CNT);
86+
@@ -112,6 +105,22 @@ static irqreturn_t rttm_timer_interrupt(
87+
return IRQ_HANDLED;
88+
}
89+
90+
+static void rttm_bounce_timer(void __iomem *base, u32 mode)
91+
+{
92+
+ /*
93+
+ * When a running timer has less than ~5us left, a stop/start sequence
94+
+ * might fail. While the details are unknown the most evident effect is
95+
+ * that the subsequent interrupt will not be fired.
96+
+ *
97+
+ * As a workaround issue an intermediate restart with a very slow
98+
+ * frequency of ~3kHz keeping the target value. So the actual follow
99+
+ * up restart will always be issued outside the critical window.
100+
+ */
101+
+
102+
+ rttm_disable_timer(base);
103+
+ rttm_enable_timer(base, mode, RTTM_MAX_DIVISOR);
104+
+}
105+
+
106+
static void rttm_stop_timer(void __iomem *base)
107+
{
108+
rttm_disable_timer(base);
109+
@@ -120,7 +129,6 @@ static void rttm_stop_timer(void __iomem
110+
111+
static void rttm_start_timer(struct timer_of *to, u32 mode)
112+
{
113+
- rttm_set_counter(to->of_base.base, 0);
114+
rttm_enable_timer(to->of_base.base, mode, to->of_clk.rate / RTTM_TICKS_PER_SEC);
115+
}
116+
117+
@@ -129,7 +137,8 @@ static int rttm_next_event(unsigned long
118+
struct timer_of *to = to_timer_of(clkevt);
119+
120+
RTTM_DEBUG(to->of_base.base);
121+
- rttm_stop_timer(to->of_base.base);
122+
+ rttm_bounce_timer(to->of_base.base, RTTM_CTRL_COUNTER);
123+
+ rttm_disable_timer(to->of_base.base);
124+
rttm_set_period(to->of_base.base, delta);
125+
rttm_start_timer(to, RTTM_CTRL_COUNTER);
126+
127+
@@ -141,7 +150,8 @@ static int rttm_state_oneshot(struct clo
128+
struct timer_of *to = to_timer_of(clkevt);
129+
130+
RTTM_DEBUG(to->of_base.base);
131+
- rttm_stop_timer(to->of_base.base);
132+
+ rttm_bounce_timer(to->of_base.base, RTTM_CTRL_COUNTER);
133+
+ rttm_disable_timer(to->of_base.base);
134+
rttm_set_period(to->of_base.base, RTTM_TICKS_PER_SEC / HZ);
135+
rttm_start_timer(to, RTTM_CTRL_COUNTER);
136+
137+
@@ -153,7 +163,8 @@ static int rttm_state_periodic(struct cl
138+
struct timer_of *to = to_timer_of(clkevt);
139+
140+
RTTM_DEBUG(to->of_base.base);
141+
- rttm_stop_timer(to->of_base.base);
142+
+ rttm_bounce_timer(to->of_base.base, RTTM_CTRL_TIMER);
143+
+ rttm_disable_timer(to->of_base.base);
144+
rttm_set_period(to->of_base.base, RTTM_TICKS_PER_SEC / HZ);
145+
rttm_start_timer(to, RTTM_CTRL_TIMER);
146+

0 commit comments

Comments
 (0)