RK3326: dw_mmc: survive an SDIO card that stops responding - #3251
Closed
pkilar wants to merge 1 commit into
Closed
Conversation
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
This was referenced Aug 30, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
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 indw_mci_probe(), again bydw_mci_runtime_resume(), and re-enabled by every PIO transfer indw_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 withhost->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@ff380000caught 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. 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, sommc_wait_for_req()waits forever: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
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
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 fromTMOUT, whichdw_mci_set_data_timeout()already programs per request fromdata->timeout_ns, so it should track whatever the card asked for — but this is the assumption to check.RK3326/patches/linuxpatch, 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.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.