[PATCH rtw-next v7 3/4] wifi: rtw88: sdio: Fix unhandled RX request interrupt storm
From: Alastair D'Silva
Date: Fri Oct 30 2026 - 23:36:06 EST
On 3081-based SDIO chips (observed on RTL8821CS; RTL8822BS and RTL8822CS
are inferred to be affected as they share the 3081 architecture), clearing
REG_SDIO_HISR_RX_REQUEST requires both a software W1C acknowledge and an
empty RX FIFO. Masking this bit out before writing back to HISR prevents
the software acknowledge from happening, leaving the bit asserted even
after the FIFO is drained and causing an infinite interrupt storm loop
that locks up the system.
8051-based chips instead clear this bit in hardware once the RX buffer is
empty without a W1C writeback, and software must not clear it.
Split rtw_sdio_handle_interrupt() into separate 8051 and 3081 handlers.
For 3081, disable interrupts via HIMR, acknowledge all pending status
bits via W1C writeback (preserving the existing full status word
writeback used on non-RTL8723BS chips, now including
REG_SDIO_HISR_RX_REQUEST), service pending events, and re-enable
interrupts under irq_lock if still running.
If unread packets remain in the FIFO when interrupts are re-enabled (or
if new packets arrived), REG_SDIO_HISR_RX_REQUEST remains asserted and
re-triggers the IRQ line. 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 | 73 ++++++++++++++++++-----
1 file changed, 57 insertions(+), 16 deletions(-)
diff --git a/drivers/net/wireless/realtek/rtw88/sdio.c b/drivers/net/wireless/realtek/rtw88/sdio.c
index 253467250ee6..75a5f5a96169 100644
--- a/drivers/net/wireless/realtek/rtw88/sdio.c
+++ b/drivers/net/wireless/realtek/rtw88/sdio.c
@@ -1481,6 +1481,58 @@ 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);
+
+ /*
+ * Acknowledge all pending status bits via W1C writeback. Unlike 8051,
+ * 3081 requires REG_SDIO_HISR_RX_REQUEST to be explicitly cleared by
+ * software.
+ */
+ 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 or if
+ * unread data remains in the RX FIFO (e.g. if the 64KB budget was
+ * reached or an RX error occurred). On 3081 hardware, clearing
+ * REG_SDIO_HISR_RX_REQUEST requires both the W1C writeback above and
+ * an empty RX FIFO, so the bit remains asserted as long as unread
+ * data is in the buffer and re-triggers the interrupt when unmasked.
+ */
+ 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);
@@ -1499,24 +1551,13 @@ static void rtw_sdio_handle_interrupt(struct sdio_func *sdio_func)
rtwsdio->irq_thread = current;
hisr = rtw_read32(rtwdev, REG_SDIO_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);
+ if (hisr) {
+ if (rtw_chip_wcpu_8051(rtwdev))
+ rtw_sdio_handle_interrupt_8051(rtwdev, hisr);
+ else
+ rtw_sdio_handle_interrupt_3081(rtwdev, hisr);
}
- /*
- * 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);
-
rtwsdio->irq_thread = NULL;
}
--
2.53.0