mac80211: sync some rt2x00 patches with wireless-next
[openwrt/staging/wigyori.git] / package / kernel / mac80211 / patches / rt2x00 / 005-1-v6.8-wifi-rt2x00-introduce-DMA-busy-check-watchdog-for-rt.patch
diff --git a/package/kernel/mac80211/patches/rt2x00/005-1-v6.8-wifi-rt2x00-introduce-DMA-busy-check-watchdog-for-rt.patch b/package/kernel/mac80211/patches/rt2x00/005-1-v6.8-wifi-rt2x00-introduce-DMA-busy-check-watchdog-for-rt.patch
new file mode 100644 (file)
index 0000000..c1c3225
--- /dev/null
@@ -0,0 +1,177 @@
+From b1275cdd7456ef811747dfb4f3c46310ddd300cd Mon Sep 17 00:00:00 2001
+From: Shiji Yang <yangshiji66@outlook.com>
+Date: Sat, 4 Nov 2023 16:57:58 +0800
+Subject: wifi: rt2x00: introduce DMA busy check watchdog for rt2800
+
+When I tried to fix the watchdog of rt2800, I found that sometimes
+the watchdog can not reset the hung device. This is because the
+queue is not completely stuck, it just becomes very slow. The MTK
+vendor driver for the new chip MT7603/MT7612 has a DMA busy watchdog
+to detect device hangs by checking DMA busy status. This watchdog
+implementation is something similar to it. To reduce unnecessary
+reset, we can check the INT_SOURCE_CSR register together as I found
+that when the radio hung, the RX/TX coherent interrupt will always
+stuck at triggered state.
+
+The 'watchdog' module parameter has been extended to control all
+watchdogs(0=disabled, 1=hang watchdog, 2=DMA watchdog, 3=both). This
+new watchdog function is a slight schedule and it won't affect the
+transmission speed. So we can turn on it by default. Due to the
+INT_SOURCE_CSR register is invalid on rt2800 USB NICs, the DMA busy
+watchdog will be automatically disabled for them.
+
+Tested on MT7620 and RT5350.
+
+Signed-off-by: Shiji Yang <yangshiji66@outlook.com>
+Acked-by: Stanislaw Gruszka <stf_xl@wp.pl>
+Signed-off-by: Kalle Valo <kvalo@kernel.org>
+Link: https://lore.kernel.org/r/TYAP286MB0315D7462CE08A119A99DE34BCA4A@TYAP286MB0315.JPNP286.PROD.OUTLOOK.COM
+---
+ drivers/net/wireless/ralink/rt2x00/rt2800.h    |  4 ++
+ drivers/net/wireless/ralink/rt2x00/rt2800lib.c | 77 ++++++++++++++++++++++----
+ drivers/net/wireless/ralink/rt2x00/rt2x00.h    |  3 +
+ 3 files changed, 73 insertions(+), 11 deletions(-)
+
+--- a/drivers/net/wireless/ralink/rt2x00/rt2800.h
++++ b/drivers/net/wireless/ralink/rt2x00/rt2800.h
+@@ -3194,4 +3194,8 @@ enum rt2800_eeprom_word {
+  */
+ #define BCN_TBTT_OFFSET 64
++/* Watchdog type mask */
++#define RT2800_WATCHDOG_HANG          BIT(0)
++#define RT2800_WATCHDOG_DMA_BUSY      BIT(1)
++
+ #endif /* RT2800_H */
+--- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
++++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
+@@ -30,9 +30,10 @@
+ #include "rt2800lib.h"
+ #include "rt2800.h"
+-static bool modparam_watchdog;
+-module_param_named(watchdog, modparam_watchdog, bool, S_IRUGO);
+-MODULE_PARM_DESC(watchdog, "Enable watchdog to detect tx/rx hangs and reset hardware if detected");
++static unsigned int modparam_watchdog = RT2800_WATCHDOG_DMA_BUSY;
++module_param_named(watchdog, modparam_watchdog, uint, 0444);
++MODULE_PARM_DESC(watchdog, "Enable watchdog to recover tx/rx hangs.\n"
++               "\t\t(0=disabled, 1=hang watchdog, 2=DMA watchdog(default), 3=both)");
+ /*
+  * Register access.
+@@ -1261,15 +1262,12 @@ static void rt2800_update_survey(struct
+       chan_survey->time_ext_busy += rt2800_register_read(rt2x00dev, CH_BUSY_STA_SEC);
+ }
+-void rt2800_watchdog(struct rt2x00_dev *rt2x00dev)
++static bool rt2800_watchdog_hung(struct rt2x00_dev *rt2x00dev)
+ {
+       struct data_queue *queue;
+       bool hung_tx = false;
+       bool hung_rx = false;
+-      if (test_bit(DEVICE_STATE_SCANNING, &rt2x00dev->flags))
+-              return;
+-
+       rt2800_update_survey(rt2x00dev);
+       queue_for_each(rt2x00dev, queue) {
+@@ -1297,18 +1295,72 @@ void rt2800_watchdog(struct rt2x00_dev *
+               }
+       }
++      if (!hung_tx && !hung_rx)
++              return false;
++
+       if (hung_tx)
+               rt2x00_warn(rt2x00dev, "Watchdog TX hung detected\n");
+       if (hung_rx)
+               rt2x00_warn(rt2x00dev, "Watchdog RX hung detected\n");
+-      if (hung_tx || hung_rx) {
+-              queue_for_each(rt2x00dev, queue)
+-                      queue->wd_count = 0;
++      queue_for_each(rt2x00dev, queue)
++              queue->wd_count = 0;
++
++      return true;
++}
++
++static bool rt2800_watchdog_dma_busy(struct rt2x00_dev *rt2x00dev)
++{
++      bool busy_rx, busy_tx;
++      u32 reg_cfg = rt2800_register_read(rt2x00dev, WPDMA_GLO_CFG);
++      u32 reg_int = rt2800_register_read(rt2x00dev, INT_SOURCE_CSR);
++
++      if (rt2x00_get_field32(reg_cfg, WPDMA_GLO_CFG_RX_DMA_BUSY) &&
++          rt2x00_get_field32(reg_int, INT_SOURCE_CSR_RX_COHERENT))
++              rt2x00dev->rxdma_busy++;
++      else
++              rt2x00dev->rxdma_busy = 0;
++      if (rt2x00_get_field32(reg_cfg, WPDMA_GLO_CFG_TX_DMA_BUSY) &&
++          rt2x00_get_field32(reg_int, INT_SOURCE_CSR_TX_COHERENT))
++              rt2x00dev->txdma_busy++;
++      else
++              rt2x00dev->txdma_busy = 0;
++
++      busy_rx = rt2x00dev->rxdma_busy > 30 ? true : false;
++      busy_tx = rt2x00dev->txdma_busy > 30 ? true : false;
++
++      if (!busy_rx && !busy_tx)
++              return false;
++
++      if (busy_rx)
++              rt2x00_warn(rt2x00dev, "Watchdog RX DMA busy detected\n");
++
++      if (busy_tx)
++              rt2x00_warn(rt2x00dev, "Watchdog TX DMA busy detected\n");
++
++      rt2x00dev->rxdma_busy = 0;
++      rt2x00dev->txdma_busy = 0;
++
++      return true;
++}
++
++void rt2800_watchdog(struct rt2x00_dev *rt2x00dev)
++{
++      bool reset = false;
++
++      if (test_bit(DEVICE_STATE_SCANNING, &rt2x00dev->flags))
++              return;
++
++      if (modparam_watchdog & RT2800_WATCHDOG_DMA_BUSY)
++              reset = rt2800_watchdog_dma_busy(rt2x00dev);
++
++      if (modparam_watchdog & RT2800_WATCHDOG_HANG)
++              reset = rt2800_watchdog_hung(rt2x00dev) || reset;
++
++      if (reset)
+               ieee80211_restart_hw(rt2x00dev->hw);
+-      }
+ }
+ EXPORT_SYMBOL_GPL(rt2800_watchdog);
+@@ -12016,6 +12068,9 @@ int rt2800_probe_hw(struct rt2x00_dev *r
+               __set_bit(REQUIRE_TASKLET_CONTEXT, &rt2x00dev->cap_flags);
+       }
++      /* USB NICs don't support DMA watchdog as INT_SOURCE_CSR is invalid */
++      if (rt2x00_is_usb(rt2x00dev))
++              modparam_watchdog &= ~RT2800_WATCHDOG_DMA_BUSY;
+       if (modparam_watchdog) {
+               __set_bit(CAPABILITY_RESTART_HW, &rt2x00dev->cap_flags);
+               rt2x00dev->link.watchdog_interval = msecs_to_jiffies(100);
+--- a/drivers/net/wireless/ralink/rt2x00/rt2x00.h
++++ b/drivers/net/wireless/ralink/rt2x00/rt2x00.h
+@@ -926,6 +926,9 @@ struct rt2x00_dev {
+        */
+       u16 beacon_int;
++      /* Rx/Tx DMA busy watchdog counter */
++      u16 rxdma_busy, txdma_busy;
++
+       /**
+        * Timestamp of last received beacon
+        */