Skip to content

RK3326: dw_mmc: survive an SDIO card that stops responding - #3251

Closed
pkilar wants to merge 1 commit into
ROCKNIX:nextfrom
pkilar:rk3326-dw-mmc-dead-bus
Closed

RK3326: dw_mmc: survive an SDIO card that stops responding#3251
pkilar wants to merge 1 commit into
ROCKNIX:nextfrom
pkilar:rk3326-dw-mmc-dead-bus

Conversation

@pkilar

@pkilar pkilar commented Aug 30, 2026

Copy link
Copy Markdown

Summary

  • What is the goal of this PR? Stop an unresponsive SDIO card from taking the whole machine down.

Two independent problems in dw_mmc.c. Both were hit on a Gusgu H7 (RK3326) when its RK915 SDIO wifi chip stops responding under load, but neither is specific to that chip — any SDIO card that goes silent mid-transfer reaches the same two paths.

1. A TXDR/RXDR interrupt storm wedges the machine.

TXDR/RXDR ask for the FIFO to be serviced by the CPU and are only meaningful for PIO. dw_mci_submit_data_dma() masks them for the duration of a DMA transfer, so DMA itself is covered — but nothing masks them once a transfer is over. They are enabled by the INTMASK written in dw_mci_probe(), again by dw_mci_runtime_resume(), and re-enabled by every PIO transfer in dw_mci_submit_data(); the only thing that ever clears them is the next DMA transfer. An SDIO card driver that mixes CMD52 (PIO) with block transfers therefore leaves them enabled for long stretches with host->sg == NULL, and in that state the handler can only clear the latch.

Harmless while the FIFO condition is transient. Not harmless when the card dies: the condition persists, clearing the latch achieves nothing, and the level-triggered line is re-raised immediately.

Per-irq tracing on mmc@ff380000 caught the count advancing by ~697,000 between two consecutive samples, with every other interrupt on that CPU frozen:

IRQTRACE 344 dwmci: 56:164736147  vop=11531 adc=24734 ser=418 gpio=10946
IRQTRACE 345 dwmci: 56:165433415  vop=11531 adc=24734 ser=418 gpio=10946

All device interrupts on this board are affine to CPU0, so the display stops (drm vblank timeouts), the SARADC stops, the network dies, and the machine needs a power cycle. The fix masks the bit when it fires with nothing to service; dw_mci_submit_data() re-enables it for the next PIO transfer.

2. A write to a dead card never times out.

dw_mci_set_drto() is armed only for reads, and the hardware data timeout does not cover the write data phase, so mmc_wait_for_req() waits forever:

task:rk915_tx  state:D
  wait_for_completion / mmc_wait_for_req_done / mmc_io_rw_extended
  / sdio_memcpy_toio / tx_thread [rk915]
task:rk915_rx  state:D
  __mmc_claim_host / sdio_claim_host / rk915_serias_read [rk915]

The second thread is merely queued behind the first on the host lock. Nothing errors, so the card driver's own recovery never runs and the module cannot be unloaded. The fix arms the timer for writes as well.

Testing

  • How was this tested? Built and run on a Gusgu H7 (RK3326), where the failure is reproducible by driving sustained traffic through the RK915 SDIO wifi until the chip stops responding.
  • Test results: Before, the board hard-locks (display frozen, all CPU0 interrupts starved) and needs a power cycle; the D-state task pair above never unblocks. After, the stuck FIFO interrupt is masked with a single ratelimited message, the write errors out on the data-read timeout instead of hanging forever, the card driver's recovery runs, and the machine stays up. eMMC and SD behaviour on the same board is unchanged across normal use — boot, game loading, and storage writes.

Additional Context

  • Where to focus review: part 2 is the riskier half. Arming dw_mci_set_drto() for writes introduces a timeout where there previously was none, so a legitimately slow write could now be cut short if the timeout is too tight. The value comes from TMOUT, which dw_mci_set_data_timeout() already programs per request from data->timeout_ns, so it should track whatever the card asked for — but this is the assumption to check.
  • This is scoped as an RK3326/patches/linux patch, so it does not affect other families. It is not RK3326-specific in nature; if it holds up here it may be worth wider application.
  • I only have one RK3326 device, so the "no regression on normal eMMC/SD" claim above rests on a single board. Testing on other RK3326 handhelds would be valuable before this lands.

AI Usage

Did you use AI tools to help write this code? YES — Claude Code was used throughout the investigation (per-irq tracing, reading the interrupt paths, narrowing down which hunks were actually load-bearing) and to draft the patch and this description. The interrupt counts and task traces quoted above are captured from the hardware, not reconstructed.

Two independent problems, both observed on a Gusgu H7 (RK3326) when its RK915
SDIO wifi chip stops responding under load, but neither is specific to that
chip - any SDIO card that goes silent mid-transfer reaches the same two paths.

1. A TXDR/RXDR interrupt storm wedges the machine.

   TXDR/RXDR ask for the FIFO to be serviced by the CPU and are only meaningful
   for PIO. dw_mci_submit_data_dma() masks them for the duration of a DMA
   transfer, but nothing masks them once a transfer is over, so an SDIO card
   driver that mixes CMD52 (PIO) with block transfers leaves them enabled for
   long stretches with host->sg == NULL. In that state the handler can only
   clear the latch - the underlying FIFO condition persists and the
   level-triggered line is re-raised immediately.

   Per-irq tracing on mmc@ff380000 caught the count advancing by ~697,000
   between two consecutive samples, with every other interrupt on that CPU
   frozen. All device interrupts on this board are affine to CPU0, so the
   display stops (drm vblank timeouts), the SARADC stops, the network dies, and
   the machine needs a power cycle.

   Mask the bit when it fires with nothing to service; dw_mci_submit_data()
   re-enables it for the next PIO transfer.

2. A write to a dead card never times out.

   dw_mci_set_drto() is armed only for reads, and the hardware data timeout does
   not cover the write data phase, so mmc_wait_for_req() waits forever. Nothing
   errors, the card driver's own recovery never runs, and the module cannot be
   unloaded. Arm it for writes as well; the timeout value comes from TMOUT,
   which dw_mci_set_data_timeout() already programs per request from
   data->timeout_ns.

Full traces and reasoning are in the patch header.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01985tp15STZZvPFMfnVBGnm
@pkilar pkilar closed this Sep 4, 2026
@pkilar
pkilar deleted the rk3326-dw-mmc-dead-bus branch September 4, 2026 21:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants