--- /dev/null
+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
+ */