[PATCH rtw-next v5 3/4] wifi: rtw88: sdio: Fix unhandled RX request interrupt storm
From: Alastair D'Silva
Date: Sun Oct 11 2026 - 18:13:55 EST
On 3081-based SDIO chips (e.g. RTL8821CS), hardware does not automatically
clear REG_SDIO_HISR_RX_REQUEST when the RX buffer is empty. Masking this
bit out before writing back to HISR prevents it from being acknowledged,
causing an infinite interrupt storm loop that locks up the system.
8051-based chips instead rely on hardware to clear this bit automatically
once the buffer is empty, and software must not clear it.
Split rtw_sdio_handle_interrupt() into separate 8051 and 3081 handlers.
For 3081, disable interrupts via HIMR, acknowledge pending status bits
via W1C writeback, service pending events, and re-enable interrupts under
irq_lock if still running. For 8051 chips, preserve the existing behavior.
Fixes: 65371a3f14e7 ("wifi: rtw88: sdio: Add HCI implementation for SDIO based chipsets")
Cc: stable@xxxxxxxxxxxxxxx
Assisted-by: LLM
Signed-off-by: Alastair D'Silva <alastair@xxxxxxxxxxx>
---
drivers/net/wireless/realtek/rtw88/sdio.c | 62 +++++++++++++++++------
1 file changed, 46 insertions(+), 16 deletions(-)
diff --git a/drivers/net/wireless/realtek/rtw88/sdio.c b/drivers/net/wireless/realtek/rtw88/sdio.c
index 7d4f17e96648..9fbd7654ada4 100644
--- a/drivers/net/wireless/realtek/rtw88/sdio.c
+++ b/drivers/net/wireless/realtek/rtw88/sdio.c
@@ -1486,6 +1486,45 @@ static void rtw_sdio_rx_isr(struct rtw_dev *rtwdev)
} while (total_rx_bytes < SZ_64K && hisr & REG_SDIO_HISR_RX_REQUEST);
}
+static void rtw_sdio_handle_interrupt_8051(struct rtw_dev *rtwdev, u32 hisr)
+{
+ if (hisr & REG_SDIO_HISR_TXERR)
+ rtw_sdio_tx_err_isr(rtwdev);
+ if (hisr & REG_SDIO_HISR_RX_REQUEST) {
+ hisr &= ~REG_SDIO_HISR_RX_REQUEST;
+ rtw_sdio_rx_isr(rtwdev);
+ }
+
+ /*
+ * RTL8723BS keeps raising the interrupt after resume if undefined
+ * status bits are written back, so acknowledge only the bits this
+ * driver defines. Other chips keep the existing behaviour.
+ */
+ if (rtw_is_8723bs(rtwdev))
+ hisr &= RTW_SDIO_HISR_CLEAR_MASK;
+
+ rtw_write32(rtwdev, REG_SDIO_HISR, hisr);
+}
+
+static void rtw_sdio_handle_interrupt_3081(struct rtw_dev *rtwdev, u32 hisr)
+{
+ struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
+
+ rtw_sdio_disable_interrupt(rtwdev);
+ rtw_write32(rtwdev, REG_SDIO_HISR, hisr);
+
+ if (hisr & REG_SDIO_HISR_TXERR)
+ rtw_sdio_tx_err_isr(rtwdev);
+ if (hisr & REG_SDIO_HISR_RX_REQUEST)
+ rtw_sdio_rx_isr(rtwdev);
+
+ /* Unmasking HIMR re-asserts the IRQ line if new packets arrived */
+ scoped_guard(mutex, &rtwsdio->irq_lock) {
+ if (rtwsdio->running)
+ rtw_sdio_enable_interrupt(rtwdev);
+ }
+}
+
static void rtw_sdio_handle_interrupt(struct sdio_func *sdio_func)
{
struct ieee80211_hw *hw = sdio_get_drvdata(sdio_func);
@@ -1504,24 +1543,15 @@ static void rtw_sdio_handle_interrupt(struct sdio_func *sdio_func)
rtwsdio->irq_thread = current;
hisr = rtw_read32(rtwdev, REG_SDIO_HISR);
+ if (!hisr)
+ goto out;
- if (hisr & REG_SDIO_HISR_TXERR)
- rtw_sdio_tx_err_isr(rtwdev);
- if (hisr & REG_SDIO_HISR_RX_REQUEST) {
- hisr &= ~REG_SDIO_HISR_RX_REQUEST;
- rtw_sdio_rx_isr(rtwdev);
- }
-
- /*
- * RTL8723BS keeps raising the interrupt after resume if undefined
- * status bits are written back, so acknowledge only the bits this
- * driver defines. Other chips keep the existing behaviour.
- */
- if (rtw_is_8723bs(rtwdev))
- hisr &= RTW_SDIO_HISR_CLEAR_MASK;
-
- rtw_write32(rtwdev, REG_SDIO_HISR, hisr);
+ if (rtw_chip_wcpu_8051(rtwdev))
+ rtw_sdio_handle_interrupt_8051(rtwdev, hisr);
+ else
+ rtw_sdio_handle_interrupt_3081(rtwdev, hisr);
+out:
rtwsdio->irq_thread = NULL;
}
--
2.53.0