* [PATCH rtw-next v8 1/4] wifi: rtw88: sdio: Handle allocation and read errors in rtw_sdio_rxfifo_recv
2026-11-05 2:56 [PATCH rtw-next v8 0/4] wifi: rtw88: sdio: Fix unhandled RX request interrupt storm Alastair D'Silva
@ 2026-11-05 2:56 ` Alastair D'Silva
2026-11-05 2:56 ` [PATCH rtw-next v8 2/4] wifi: rtw88: sdio: Track running state and cancel TX worker on stop Alastair D'Silva
` (2 subsequent siblings)
3 siblings, 0 replies; 6+ messages in thread
From: Alastair D'Silva @ 2026-11-05 2:56 UTC (permalink / raw)
To: linux-wireless, Ping-Ke Shih, Kalle Valo
Cc: Luka Gejak, Martin Blumenstingl, Jernej Skrabec, Ulf Hansson,
linux-kernel, Alastair D'Silva, stable
When dev_alloc_skb() or rtw_sdio_read_port() fails, rtw_sdio_rxfifo_recv()
returns without reading data. Because it returned void, the caller still
counted rx_len against the loop budget while leaving data stranded in the
FIFO.
Return an error code from rtw_sdio_rxfifo_recv() on failure and break out
of the receive loop in the caller immediately.
Fixes: 65371a3f14e7 ("wifi: rtw88: sdio: Add HCI implementation for SDIO based chipsets")
Cc: stable@vger.kernel.org
Assisted-by: LLM
Acked-by: Ping-Ke Shih <pkshih@realtek.com>
Reviewed-by: Luka Gejak <luka.gejak@linux.dev>
Signed-off-by: Alastair D'Silva <alastair@d-silva.org>
---
drivers/net/wireless/realtek/rtw88/sdio.c | 17 +++++++++++------
1 file changed, 11 insertions(+), 6 deletions(-)
diff --git a/drivers/net/wireless/realtek/rtw88/sdio.c b/drivers/net/wireless/realtek/rtw88/sdio.c
index ecd4d751c05a5..172aa7ad4732a 100644
--- a/drivers/net/wireless/realtek/rtw88/sdio.c
+++ b/drivers/net/wireless/realtek/rtw88/sdio.c
@@ -1348,7 +1348,7 @@ static void rtw_sdio_rx_skb(struct rtw_dev *rtwdev, struct sk_buff *skb,
ieee80211_rx_irqsafe(rtwdev->hw, skb);
}
-static void rtw_sdio_rxfifo_recv(struct rtw_dev *rtwdev, u32 rx_len)
+static int rtw_sdio_rxfifo_recv(struct rtw_dev *rtwdev, u32 rx_len)
{
struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
const struct rtw_chip_info *chip = rtwdev->chip;
@@ -1365,12 +1365,12 @@ static void rtw_sdio_rxfifo_recv(struct rtw_dev *rtwdev, u32 rx_len)
skb = dev_alloc_skb(bufsz);
if (!skb)
- return;
+ return -ENOMEM;
ret = rtw_sdio_read_port(rtwdev, skb->data, bufsz);
if (ret) {
dev_kfree_skb_any(skb);
- return;
+ return ret;
}
while (true) {
@@ -1379,7 +1379,7 @@ static void rtw_sdio_rxfifo_recv(struct rtw_dev *rtwdev, u32 rx_len)
&pkt_stat, &rx_status);
if (ret) {
dev_kfree_skb_any(skb);
- return;
+ return ret;
}
pkt_offset = pkt_desc_sz + pkt_stat.drv_info_sz +
@@ -1393,7 +1393,7 @@ static void rtw_sdio_rxfifo_recv(struct rtw_dev *rtwdev, u32 rx_len)
"oversized RX packet: %u, %u bytes left\n",
pkt_offset + pkt_stat.pkt_len, rx_len);
dev_kfree_skb_any(skb);
- return;
+ return -EINVAL;
}
if ((curr_pkt_len + pkt_desc_sz) >= rx_len) {
@@ -1423,11 +1423,14 @@ static void rtw_sdio_rxfifo_recv(struct rtw_dev *rtwdev, u32 rx_len)
skb_reserve(skb, curr_pkt_len);
rx_len -= curr_pkt_len;
}
+
+ return 0;
}
static void rtw_sdio_rx_isr(struct rtw_dev *rtwdev)
{
u32 rx_len, hisr, total_rx_bytes = 0;
+ int ret;
do {
if (rtw_chip_wcpu_8051(rtwdev))
@@ -1438,7 +1441,9 @@ static void rtw_sdio_rx_isr(struct rtw_dev *rtwdev)
if (!rx_len)
break;
- rtw_sdio_rxfifo_recv(rtwdev, rx_len);
+ ret = rtw_sdio_rxfifo_recv(rtwdev, rx_len);
+ if (ret)
+ break;
total_rx_bytes += rx_len;
--
2.53.0
^ permalink raw reply [flat|nested] 6+ messages in thread* [PATCH rtw-next v8 2/4] wifi: rtw88: sdio: Track running state and cancel TX worker on stop
2026-11-05 2:56 [PATCH rtw-next v8 0/4] wifi: rtw88: sdio: Fix unhandled RX request interrupt storm Alastair D'Silva
2026-11-05 2:56 ` [PATCH rtw-next v8 1/4] wifi: rtw88: sdio: Handle allocation and read errors in rtw_sdio_rxfifo_recv Alastair D'Silva
@ 2026-11-05 2:56 ` Alastair D'Silva
2026-11-06 14:46 ` Luka Gejak
2026-11-05 2:56 ` [PATCH rtw-next v8 3/4] wifi: rtw88: sdio: Fix unhandled RX request interrupt storm Alastair D'Silva
2026-11-05 2:57 ` [PATCH rtw-next v8 4/4] wifi: rtw88: sdio: Split rtw_sdio_rx_isr into 8051 and 3081 variants Alastair D'Silva
3 siblings, 1 reply; 6+ messages in thread
From: Alastair D'Silva @ 2026-11-05 2:56 UTC (permalink / raw)
To: linux-wireless, Ping-Ke Shih, Kalle Valo
Cc: Luka Gejak, Martin Blumenstingl, Jernej Skrabec, Ulf Hansson,
linux-kernel, Alastair D'Silva, stable
When rtw_sdio_stop() disables interrupts, in-flight interrupt handlers or
delayed TX work may still run against a powered-down device.
Track the operational state in rtwsdio->running with an irq_lock mutex
(similar to PCI), check it at the entry of rtw_sdio_handle_interrupt()
and rtw_sdio_tx_handler(), serialize state transitions in rtw_sdio_start()
and rtw_sdio_stop() under sdio_claim_host() and irq_lock, cancel the TX
worker synchronously in rtw_sdio_stop(), and purge any remaining frames in
rtwsdio->tx_queue[] while waking any stopped AC queues.
Fixes: 28eaf7971a20 ("wifi: rtw88: sdio: add TX back-pressure and retry on page starvation")
Cc: stable@vger.kernel.org
Assisted-by: LLM
Signed-off-by: Alastair D'Silva <alastair@d-silva.org>
---
drivers/net/wireless/realtek/rtw88/sdio.c | 46 ++++++++++++++++++++++-
drivers/net/wireless/realtek/rtw88/sdio.h | 3 ++
2 files changed, 47 insertions(+), 2 deletions(-)
diff --git a/drivers/net/wireless/realtek/rtw88/sdio.c b/drivers/net/wireless/realtek/rtw88/sdio.c
index 172aa7ad4732a..e318fefe5b5e7 100644
--- a/drivers/net/wireless/realtek/rtw88/sdio.c
+++ b/drivers/net/wireless/realtek/rtw88/sdio.c
@@ -941,6 +941,8 @@ static void rtw_sdio_init(struct rtw_dev *rtwdev)
{
struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
+ mutex_init(&rtwsdio->irq_lock);
+
if (rtw_is_8723bs(rtwdev))
rtwsdio->irq_mask = REG_SDIO_HIMR_RX_REQUEST;
else
@@ -1054,6 +1056,7 @@ static int rtw_sdio_8723bs_check_rqpn(struct rtw_dev *rtwdev)
static int rtw_sdio_start(struct rtw_dev *rtwdev)
{
+ struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
u32 clear;
if (rtw_is_8723bs(rtwdev)) {
@@ -1069,20 +1072,49 @@ static int rtw_sdio_start(struct rtw_dev *rtwdev)
rtw_sdio_enable_rx_aggregation(rtwdev);
+ sdio_claim_host(rtwsdio->sdio_func);
+
if (rtw_is_8723bs(rtwdev)) {
clear = rtw_read32(rtwdev, REG_SDIO_HISR) & RTW_SDIO_HISR_CLEAR_MASK;
if (clear)
rtw_write32(rtwdev, REG_SDIO_HISR, clear);
}
- rtw_sdio_enable_interrupt(rtwdev);
+ scoped_guard(mutex, &rtwsdio->irq_lock) {
+ rtwsdio->running = true;
+ rtw_sdio_enable_interrupt(rtwdev);
+ }
+
+ sdio_release_host(rtwsdio->sdio_func);
return 0;
}
static void rtw_sdio_stop(struct rtw_dev *rtwdev)
{
- rtw_sdio_disable_interrupt(rtwdev);
+ struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
+ enum rtw_tx_queue_type queue;
+ int i;
+
+ sdio_claim_host(rtwsdio->sdio_func);
+ scoped_guard(mutex, &rtwsdio->irq_lock) {
+ rtwsdio->running = false;
+ rtw_sdio_disable_interrupt(rtwdev);
+ }
+ sdio_release_host(rtwsdio->sdio_func);
+
+ cancel_delayed_work_sync(&rtwsdio->tx_handler_data->work);
+
+ for (i = 0; i < RTK_MAX_TX_QUEUE_NUM; i++)
+ ieee80211_purge_tx_queue(rtwdev->hw, &rtwsdio->tx_queue[i]);
+
+ for (i = 0; i < IEEE80211_NUM_ACS; i++) {
+ queue = rtw_tx_ac_to_hwq(i);
+ if (rtwsdio->tx_queue_stopped[queue]) {
+ rtwsdio->tx_queue_stopped[queue] = false;
+ ieee80211_wake_queue(rtwdev->hw, i);
+ }
+ }
}
static void rtw_sdio_deep_ps_enter(struct rtw_dev *rtwdev)
@@ -1477,6 +1509,11 @@ static void rtw_sdio_handle_interrupt(struct sdio_func *sdio_func)
rtwdev = hw->priv;
rtwsdio = (struct rtw_sdio *)rtwdev->priv;
+ scoped_guard(mutex, &rtwsdio->irq_lock) {
+ if (!rtwsdio->running)
+ return;
+ }
+
rtwsdio->irq_thread = current;
hisr = rtw_read32(rtwdev, REG_SDIO_HISR);
@@ -1710,6 +1747,11 @@ static void rtw_sdio_tx_handler(struct work_struct *work)
rtwdev = work_data->rtwdev;
rtwsdio = (struct rtw_sdio *)rtwdev->priv;
+ scoped_guard(mutex, &rtwsdio->irq_lock) {
+ if (!rtwsdio->running)
+ return;
+ }
+
if (!rtw_fw_feature_check(&rtwdev->fw, FW_FEATURE_TX_WAKE))
rtw_sdio_deep_ps_leave(rtwdev);
diff --git a/drivers/net/wireless/realtek/rtw88/sdio.h b/drivers/net/wireless/realtek/rtw88/sdio.h
index 6e7e6009744b0..64266b54f595d 100644
--- a/drivers/net/wireless/realtek/rtw88/sdio.h
+++ b/drivers/net/wireless/realtek/rtw88/sdio.h
@@ -168,6 +168,9 @@ struct rtw_sdio {
bool sdio3_bus_mode;
void *irq_thread;
+ bool running;
+ /* protect running state and enabling interrupts */
+ struct mutex irq_lock;
struct workqueue_struct *txwq;
struct rtw_sdio_work_data *tx_handler_data;
--
2.53.0
^ permalink raw reply [flat|nested] 6+ messages in thread* Re: [PATCH rtw-next v8 2/4] wifi: rtw88: sdio: Track running state and cancel TX worker on stop
2026-11-05 2:56 ` [PATCH rtw-next v8 2/4] wifi: rtw88: sdio: Track running state and cancel TX worker on stop Alastair D'Silva
@ 2026-11-06 14:46 ` Luka Gejak
0 siblings, 0 replies; 6+ messages in thread
From: Luka Gejak @ 2026-11-06 14:46 UTC (permalink / raw)
To: Alastair D'Silva, linux-wireless, Ping-Ke Shih, Kalle Valo
Cc: Martin Blumenstingl, Jernej Skrabec, Ulf Hansson, linux-kernel,
Alastair D'Silva, stable
Hi Alastair,
Thanks for the new version. Two comments below.
November 5, 2026 at 3:56 AM, "Alastair D'Silva" <alastair@d-silva.org> wrote:
> diff --git a/drivers/net/wireless/realtek/rtw88/sdio.c b/drivers/net/wireless/realtek/rtw88/sdio.c
> index 172aa7ad4732a..e318fefe5b5e7 100644
> --- a/drivers/net/wireless/realtek/rtw88/sdio.c
> +++ b/drivers/net/wireless/realtek/rtw88/sdio.c
[ ... ]
> static void rtw_sdio_stop(struct rtw_dev *rtwdev)
> {
[ ... ]
> + cancel_delayed_work_sync(&rtwsdio->tx_handler_data->work);
> +
> + for (i = 0; i < RTK_MAX_TX_QUEUE_NUM; i++)
> + ieee80211_purge_tx_queue(rtwdev->hw, &rtwsdio->tx_queue[i]);
ieee80211_purge_tx_queue() takes the frames off with __skb_dequeue(), so
without the queue lock. rtw_sdio_tx_write() appends with skb_queue_tail()
and does not look at running, so as far as I can see both can run on the
same queue at once.
That seems possible on the firmware recovery path, where the device is
stopped while mac80211 is still sending, since ieee80211_restart_hw()
only comes afterwards:
rtw_fw_recovery_work()
__fw_recovery_work()
rtw_enter_ips()
rtw_core_stop()
rtw_power_off()
rtw_hci_stop()
rtw_sdio_stop()
ieee80211_restart_hw()
rtw_core_stop() clears RTW_FLAG_RUNNING, but it does not cancel
rtwdev->tx_work, and __rtw_tx_work() does not check the flag. So a
tx_work that is already pushing frames can append while the purge walks
the queue, and the list and qlen get corrupted. On SDIO this path is
reachable since dc16902cad9c ("wifi: rtw88: ps: Trigger firmware
recovery when failing to leave LPS state"). If I read it right, that is
the only rtw_fw_recovery() caller there, because rtw_fw_c2h_cmd_isr() is
only called from pci.c.
A frame appended just after the purge also stays in tx_queue[] and goes
out after the restart with the TX descriptor from before it, like with
the rearm in v7.
Freeing with skb_dequeue() and ieee80211_free_txskb() in a loop would
keep the list intact. Maybe a second pass in rtw_sdio_start(), before
running is set, could drop the late frames too.
> + for (i = 0; i < IEEE80211_NUM_ACS; i++) {
> + queue = rtw_tx_ac_to_hwq(i);
> + if (rtwsdio->tx_queue_stopped[queue]) {
> + rtwsdio->tx_queue_stopped[queue] = false;
> + ieee80211_wake_queue(rtwdev->hw, i);
> + }
> + }
> }
When the 8723bs back pressure has stopped an AC, mac80211 marks its txqs
with IEEE80211_TXQ_DIRTY the next time a dequeue is tried. On the
recovery path above, this wake runs before ieee80211_restart_hw() sets
in_reconfig. So if I read it right, the wake_txqs tasklet clears the flag
and calls rtw_ops_wake_tx_queue(), which returns early because
RTW_FLAG_RUNNING is already clear. After the restart those txqs only move
again once another frame is queued on them.
PCI does this reset in rtw_pci_start() since 6ac7db1cea43 ("wifi: rtw88:
pci: wake the TX queues after restart_hw and interface up"), through
rtw_pci_reset_tx_queue_state(). On restart_hw that runs while in_reconfig
is set and the SUSPEND reason still holds the queues, so the marked txqs
are woken at the end of the reconfig instead. I think moving the reset
and the wake to rtw_sdio_start() would work here too. It could also take
tx_queue[].lock there, like the 8723bs stop and wake helpers.
Best regards,
Luka Gejak
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH rtw-next v8 3/4] wifi: rtw88: sdio: Fix unhandled RX request interrupt storm
2026-11-05 2:56 [PATCH rtw-next v8 0/4] wifi: rtw88: sdio: Fix unhandled RX request interrupt storm Alastair D'Silva
2026-11-05 2:56 ` [PATCH rtw-next v8 1/4] wifi: rtw88: sdio: Handle allocation and read errors in rtw_sdio_rxfifo_recv Alastair D'Silva
2026-11-05 2:56 ` [PATCH rtw-next v8 2/4] wifi: rtw88: sdio: Track running state and cancel TX worker on stop Alastair D'Silva
@ 2026-11-05 2:56 ` Alastair D'Silva
2026-11-05 2:57 ` [PATCH rtw-next v8 4/4] wifi: rtw88: sdio: Split rtw_sdio_rx_isr into 8051 and 3081 variants Alastair D'Silva
3 siblings, 0 replies; 6+ messages in thread
From: Alastair D'Silva @ 2026-11-05 2:56 UTC (permalink / raw)
To: linux-wireless, Ping-Ke Shih, Kalle Valo
Cc: Luka Gejak, Martin Blumenstingl, Jernej Skrabec, Ulf Hansson,
linux-kernel, Alastair D'Silva, stable
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@vger.kernel.org
Assisted-by: LLM
Signed-off-by: Alastair D'Silva <alastair@d-silva.org>
---
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 e318fefe5b5e7..eb1b60a517fb4 100644
--- a/drivers/net/wireless/realtek/rtw88/sdio.c
+++ b/drivers/net/wireless/realtek/rtw88/sdio.c
@@ -1499,6 +1499,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);
@@ -1517,24 +1569,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
^ permalink raw reply [flat|nested] 6+ messages in thread* [PATCH rtw-next v8 4/4] wifi: rtw88: sdio: Split rtw_sdio_rx_isr into 8051 and 3081 variants
2026-11-05 2:56 [PATCH rtw-next v8 0/4] wifi: rtw88: sdio: Fix unhandled RX request interrupt storm Alastair D'Silva
` (2 preceding siblings ...)
2026-11-05 2:56 ` [PATCH rtw-next v8 3/4] wifi: rtw88: sdio: Fix unhandled RX request interrupt storm Alastair D'Silva
@ 2026-11-05 2:57 ` Alastair D'Silva
3 siblings, 0 replies; 6+ messages in thread
From: Alastair D'Silva @ 2026-11-05 2:57 UTC (permalink / raw)
To: linux-wireless, Ping-Ke Shih, Kalle Valo
Cc: Luka Gejak, Martin Blumenstingl, Jernej Skrabec, Ulf Hansson,
linux-kernel, Alastair D'Silva
Split rtw_sdio_rx_isr() into separate 8051 and 3081 variants to be
clear. The two differences between the architectures are:
- Register size: REG_SDIO_RX0_REQ_LEN is 16-bit on 8051, 32-bit on 3081.
- HISR behavior: 8051 re-reads REG_SDIO_HISR on each iteration, while
3081 drains based on rx_len without re-reading HISR.
This is a refactoring without any logic changes.
Assisted-by: LLM
Acked-by: Ping-Ke Shih <pkshih@realtek.com>
Reviewed-by: Luka Gejak <luka.gejak@linux.dev>
Signed-off-by: Alastair D'Silva <alastair@d-silva.org>
---
drivers/net/wireless/realtek/rtw88/sdio.c | 58 +++++++++++++----------
1 file changed, 33 insertions(+), 25 deletions(-)
diff --git a/drivers/net/wireless/realtek/rtw88/sdio.c b/drivers/net/wireless/realtek/rtw88/sdio.c
index eb1b60a517fb4..64d9f6a41660a 100644
--- a/drivers/net/wireless/realtek/rtw88/sdio.c
+++ b/drivers/net/wireless/realtek/rtw88/sdio.c
@@ -1459,17 +1459,13 @@ static int rtw_sdio_rxfifo_recv(struct rtw_dev *rtwdev, u32 rx_len)
return 0;
}
-static void rtw_sdio_rx_isr(struct rtw_dev *rtwdev)
+static void rtw_sdio_rx_isr_8051(struct rtw_dev *rtwdev)
{
u32 rx_len, hisr, total_rx_bytes = 0;
int ret;
do {
- if (rtw_chip_wcpu_8051(rtwdev))
- rx_len = rtw_read16(rtwdev, REG_SDIO_RX0_REQ_LEN);
- else
- rx_len = rtw_read32(rtwdev, REG_SDIO_RX0_REQ_LEN);
-
+ rx_len = rtw_read16(rtwdev, REG_SDIO_RX0_REQ_LEN);
if (!rx_len)
break;
@@ -1479,33 +1475,45 @@ static void rtw_sdio_rx_isr(struct rtw_dev *rtwdev)
total_rx_bytes += rx_len;
- if (rtw_chip_wcpu_8051(rtwdev)) {
- /* Stop if no more RX requests are pending, even if
- * rx_len could be greater than zero in the next
- * iteration. This is needed because the RX buffer may
- * already contain data while either HW or FW are not
- * done filling that buffer yet. Still reading the
- * buffer can result in packets where
- * rtw_rx_pkt_stat.pkt_len is zero or points beyond the
- * end of the buffer.
- */
- hisr = rtw_read32(rtwdev, REG_SDIO_HISR);
- } else {
- /* RTW_WCPU_3081 chips have improved hardware or
- * firmware and can use rx_len unconditionally.
- */
- hisr = REG_SDIO_HISR_RX_REQUEST;
- }
+ /*
+ * Stop if no more RX requests are pending, even if
+ * rx_len could be greater than zero in the next
+ * iteration. This is needed because the RX buffer may
+ * already contain data while either HW or FW are not
+ * done filling that buffer yet. Still reading the
+ * buffer can result in packets where
+ * rtw_rx_pkt_stat.pkt_len is zero or points beyond the
+ * end of the buffer.
+ */
+ hisr = rtw_read32(rtwdev, REG_SDIO_HISR);
} while (total_rx_bytes < SZ_64K && hisr & REG_SDIO_HISR_RX_REQUEST);
}
+static void rtw_sdio_rx_isr_3081(struct rtw_dev *rtwdev)
+{
+ u32 rx_len, total_rx_bytes = 0;
+ int ret;
+
+ do {
+ rx_len = rtw_read32(rtwdev, REG_SDIO_RX0_REQ_LEN);
+ if (!rx_len)
+ break;
+
+ ret = rtw_sdio_rxfifo_recv(rtwdev, rx_len);
+ if (ret)
+ break;
+
+ total_rx_bytes += rx_len;
+ } while (total_rx_bytes < SZ_64K);
+}
+
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);
+ rtw_sdio_rx_isr_8051(rtwdev);
}
/*
@@ -1535,7 +1543,7 @@ static void rtw_sdio_handle_interrupt_3081(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)
- rtw_sdio_rx_isr(rtwdev);
+ rtw_sdio_rx_isr_3081(rtwdev);
/*
* Unmasking HIMR re-asserts the IRQ line if new packets arrived or if
--
2.53.0
^ permalink raw reply [flat|nested] 6+ messages in thread