1 From b1275cdd7456ef811747dfb4f3c46310ddd300cd Mon Sep 17 00:00:00 2001
2 From: Shiji Yang <yangshiji66@outlook.com>
3 Date: Sat, 4 Nov 2023 16:57:58 +0800
4 Subject: wifi: rt2x00: introduce DMA busy check watchdog for rt2800
6 When I tried to fix the watchdog of rt2800, I found that sometimes
7 the watchdog can not reset the hung device. This is because the
8 queue is not completely stuck, it just becomes very slow. The MTK
9 vendor driver for the new chip MT7603/MT7612 has a DMA busy watchdog
10 to detect device hangs by checking DMA busy status. This watchdog
11 implementation is something similar to it. To reduce unnecessary
12 reset, we can check the INT_SOURCE_CSR register together as I found
13 that when the radio hung, the RX/TX coherent interrupt will always
14 stuck at triggered state.
16 The 'watchdog' module parameter has been extended to control all
17 watchdogs(0=disabled, 1=hang watchdog, 2=DMA watchdog, 3=both). This
18 new watchdog function is a slight schedule and it won't affect the
19 transmission speed. So we can turn on it by default. Due to the
20 INT_SOURCE_CSR register is invalid on rt2800 USB NICs, the DMA busy
21 watchdog will be automatically disabled for them.
23 Tested on MT7620 and RT5350.
25 Signed-off-by: Shiji Yang <yangshiji66@outlook.com>
26 Acked-by: Stanislaw Gruszka <stf_xl@wp.pl>
27 Signed-off-by: Kalle Valo <kvalo@kernel.org>
28 Link: https://lore.kernel.org/r/TYAP286MB0315D7462CE08A119A99DE34BCA4A@TYAP286MB0315.JPNP286.PROD.OUTLOOK.COM
30 drivers/net/wireless/ralink/rt2x00/rt2800.h | 4 ++
31 drivers/net/wireless/ralink/rt2x00/rt2800lib.c | 77 ++++++++++++++++++++++----
32 drivers/net/wireless/ralink/rt2x00/rt2x00.h | 3 +
33 3 files changed, 73 insertions(+), 11 deletions(-)
35 --- a/drivers/net/wireless/ralink/rt2x00/rt2800.h
36 +++ b/drivers/net/wireless/ralink/rt2x00/rt2800.h
37 @@ -3194,4 +3194,8 @@ enum rt2800_eeprom_word {
39 #define BCN_TBTT_OFFSET 64
41 +/* Watchdog type mask */
42 +#define RT2800_WATCHDOG_HANG BIT(0)
43 +#define RT2800_WATCHDOG_DMA_BUSY BIT(1)
46 --- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
47 +++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
49 #include "rt2800lib.h"
52 -static bool modparam_watchdog;
53 -module_param_named(watchdog, modparam_watchdog, bool, S_IRUGO);
54 -MODULE_PARM_DESC(watchdog, "Enable watchdog to detect tx/rx hangs and reset hardware if detected");
55 +static unsigned int modparam_watchdog = RT2800_WATCHDOG_DMA_BUSY;
56 +module_param_named(watchdog, modparam_watchdog, uint, 0444);
57 +MODULE_PARM_DESC(watchdog, "Enable watchdog to recover tx/rx hangs.\n"
58 + "\t\t(0=disabled, 1=hang watchdog, 2=DMA watchdog(default), 3=both)");
62 @@ -1261,15 +1262,12 @@ static void rt2800_update_survey(struct
63 chan_survey->time_ext_busy += rt2800_register_read(rt2x00dev, CH_BUSY_STA_SEC);
66 -void rt2800_watchdog(struct rt2x00_dev *rt2x00dev)
67 +static bool rt2800_watchdog_hung(struct rt2x00_dev *rt2x00dev)
69 struct data_queue *queue;
73 - if (test_bit(DEVICE_STATE_SCANNING, &rt2x00dev->flags))
76 rt2800_update_survey(rt2x00dev);
78 queue_for_each(rt2x00dev, queue) {
79 @@ -1297,18 +1295,72 @@ void rt2800_watchdog(struct rt2x00_dev *
83 + if (!hung_tx && !hung_rx)
87 rt2x00_warn(rt2x00dev, "Watchdog TX hung detected\n");
90 rt2x00_warn(rt2x00dev, "Watchdog RX hung detected\n");
92 - if (hung_tx || hung_rx) {
93 - queue_for_each(rt2x00dev, queue)
94 - queue->wd_count = 0;
95 + queue_for_each(rt2x00dev, queue)
96 + queue->wd_count = 0;
101 +static bool rt2800_watchdog_dma_busy(struct rt2x00_dev *rt2x00dev)
103 + bool busy_rx, busy_tx;
104 + u32 reg_cfg = rt2800_register_read(rt2x00dev, WPDMA_GLO_CFG);
105 + u32 reg_int = rt2800_register_read(rt2x00dev, INT_SOURCE_CSR);
107 + if (rt2x00_get_field32(reg_cfg, WPDMA_GLO_CFG_RX_DMA_BUSY) &&
108 + rt2x00_get_field32(reg_int, INT_SOURCE_CSR_RX_COHERENT))
109 + rt2x00dev->rxdma_busy++;
111 + rt2x00dev->rxdma_busy = 0;
113 + if (rt2x00_get_field32(reg_cfg, WPDMA_GLO_CFG_TX_DMA_BUSY) &&
114 + rt2x00_get_field32(reg_int, INT_SOURCE_CSR_TX_COHERENT))
115 + rt2x00dev->txdma_busy++;
117 + rt2x00dev->txdma_busy = 0;
119 + busy_rx = rt2x00dev->rxdma_busy > 30 ? true : false;
120 + busy_tx = rt2x00dev->txdma_busy > 30 ? true : false;
122 + if (!busy_rx && !busy_tx)
126 + rt2x00_warn(rt2x00dev, "Watchdog RX DMA busy detected\n");
129 + rt2x00_warn(rt2x00dev, "Watchdog TX DMA busy detected\n");
131 + rt2x00dev->rxdma_busy = 0;
132 + rt2x00dev->txdma_busy = 0;
137 +void rt2800_watchdog(struct rt2x00_dev *rt2x00dev)
139 + bool reset = false;
141 + if (test_bit(DEVICE_STATE_SCANNING, &rt2x00dev->flags))
144 + if (modparam_watchdog & RT2800_WATCHDOG_DMA_BUSY)
145 + reset = rt2800_watchdog_dma_busy(rt2x00dev);
147 + if (modparam_watchdog & RT2800_WATCHDOG_HANG)
148 + reset = rt2800_watchdog_hung(rt2x00dev) || reset;
151 ieee80211_restart_hw(rt2x00dev->hw);
154 EXPORT_SYMBOL_GPL(rt2800_watchdog);
156 @@ -12016,6 +12068,9 @@ int rt2800_probe_hw(struct rt2x00_dev *r
157 __set_bit(REQUIRE_TASKLET_CONTEXT, &rt2x00dev->cap_flags);
160 + /* USB NICs don't support DMA watchdog as INT_SOURCE_CSR is invalid */
161 + if (rt2x00_is_usb(rt2x00dev))
162 + modparam_watchdog &= ~RT2800_WATCHDOG_DMA_BUSY;
163 if (modparam_watchdog) {
164 __set_bit(CAPABILITY_RESTART_HW, &rt2x00dev->cap_flags);
165 rt2x00dev->link.watchdog_interval = msecs_to_jiffies(100);
166 --- a/drivers/net/wireless/ralink/rt2x00/rt2x00.h
167 +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00.h
168 @@ -926,6 +926,9 @@ struct rt2x00_dev {
172 + /* Rx/Tx DMA busy watchdog counter */
173 + u16 rxdma_busy, txdma_busy;
176 * Timestamp of last received beacon