Skip to content

SDHCI: fix silent read failures and unbootable warm reset with UHS-I cards - #853

Open
dgarske wants to merge 1 commit into
wolfSSL:masterfrom
dgarske:zynqmp_sdhci_reset
Open

SDHCI: fix silent read failures and unbootable warm reset with UHS-I cards#853
dgarske wants to merge 1 commit into
wolfSSL:masterfrom
dgarske:zynqmp_sdhci_reset

Conversation

@dgarske

@dgarske dgarske commented Aug 11, 2026

Copy link
Copy Markdown
Member

Found on a ZCU102 SD-boot target. Two related problems.

1. A failed SDMA transfer was reported as success

sdhci_transfer() sets status = -1 when the Transfer Complete wait times out, but the post-transfer error check then ran unconditionally and CMD12's result overwrote it. A wolfBoot-side wait timeout does not necessarily set an SRS12 error bit, so the function returned 0 after moving no data. The caller used a partially filled buffer, which surfaced later as an image integrity failure rather than the I/O error it was -- and with anti-rollback, the good lower-versioned slot was refused too, leaving nothing bootable.

CMD12 is still issued on failure to leave the bus sane, but no longer clears the error. update_disk.c also broke its load loop on ret <= 0 while only erroring on ret < 0, and never checked the loop reached fw_size; short reads are now reported as I/O with byte counts.

2. A card left in UHS-I is unusable until power is removed

An OS that negotiates UHS-I leaves the card at 1.8V. Per the SD spec a card only returns to 3.3V when VDD is removed -- CMD0 does not do it -- and on many boards the card supply is a fixed rail software cannot switch. After a warm reset the card is still at 1.8V while the bootloader comes up at 3.3V. The command path tolerates that, so init and isolated reads work; sustained transfers do not.

It cannot be detected up front: the warm reset clears the controller registers, so the inherited V18SE bit is gone before the bootloader runs (an early-sample implementation was tried on hardware and never triggered), and the corruption is marginal enough that a short probe read usually succeeds. So recovery is driven from an actual data failure -- switch the host to meet the card and retry, at most once per boot. A cold boot never reaches it. This restores signaling the card is already using, so no CMD11 voltage switch is involved.

Evidence

SD1 0xFF170000 Linux leaves wolfBoot at failure
0x3C Host Control 2 0x008B = V18SE + SDR104 0x0001 = SDR25, V18SE clear
0x2C Clock Control 0x0007 undivided 0x0207 divider 2

Building with SDHCI_FORCE_SINGLE_BLOCK_READ removes both SDMA and CMD18, leaving plain CMD17 PIO. It still failed, with error SRS12: 0x00208041 -- Data CRC Error. Command completes, card sends data, data arrives corrupt: not a DMA bug, not a multi-block bug.

Ruled out by measurement: SMMU blocking DMA (SCR0 = 0x00200001, in bypass); a missing controller reset (HRS00_SWR maps to a real SRA write, readback polls the real bit); software power-cycling the card (10 ms with bus power dropped changed nothing -- VDD is not switchable here).

Testing

New regression test drives a multi-block SDMA read whose Transfer Complete never arrives, with a clean SRS12 and a succeeding CMD12. Verified to fail without the fix (returned 0) and pass with it; a positive control confirms the mock can succeed.

unit-sdhci-dma-error       100%: Checks: 2, Failures: 0, Errors: 0
unit-sdhci-response-bits   100%: Checks: 1, Failures: 0, Errors: 0
unit-sdhci-disk-unaligned  100%: Checks: 4, Failures: 0, Errors: 0

Hardware, ZCU102 (zynqmp_sdcard_ramdisk.config, RSA4096/SHA3), cross-built and flashed to QSPI:

cold boot warm reboot
before OK, 3607 ms PANIC, reported as an integrity failure
after OK, 3607 ms, recovery inert OK, 3628 ms via the 1.8V retry

The recovery costs one failed transfer before engaging. The cleanest long-term fix is for the OS to return the card to 3.3V/idle on shutdown; this makes the bootloader survive an OS that does not.

@dgarske dgarske self-assigned this Aug 11, 2026
Copilot AI lite review requested due to automatic review settings August 11, 2026 20:31

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes SDHCI-related silent read failures that could surface later as integrity failures, and adds a warm-reset recovery path for SD cards left in UHS-I 1.8V signaling by a prior stage. This improves boot reliability on affected targets (e.g., ZCU102) and adds regression coverage for the SDMA timeout case.

Changes:

  • Preserve earlier transfer failures in sdhci_transfer() so CMD12 / busy-wait results can’t overwrite timeouts and falsely report success.
  • Treat short reads during disk image load as an I/O error (with byte counts) instead of letting truncated images reach integrity checks.
  • Add an SD-card warm-reset recovery retry that switches host signaling to 1.8V after an actual data failure, plus a new unit test for SDMA timeout reporting.

Reviewed changes

Copilot reviewed 5 out of 6 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
tools/unit-tests/unit-sdhci-dma-error.c New regression test ensuring SDMA timeout is reported as error (and positive control for success path).
tools/unit-tests/Makefile Adds the new unit test binary to the unit test build/run list.
src/update_disk.c Flags ret == 0 / short reads as errors and reports byte counts to avoid misleading integrity failures.
src/sdhci.c Preserves SDMA timeout errors across CMD12, and adds UHS-I warm-reset recovery retry after failed reads.
include/sdhci.h Adds SDHCI_SRS15_V18SE bit definition for Host Control 2’s 1.8V signaling enable.
.gitignore Ignores the newly built unit test binary.
Suppressed comments (3)

src/sdhci.c:376

  • sdhci_uhs_recover() is specific to SD UHS-I warm-reset recovery. If it is compiled for eMMC-only configurations it will be unused (triggering -Werror) and, if accidentally called, would apply an SD-specific recovery sequence and log message. Wrap the helper in #ifdef DISK_SDCARD to keep it out of eMMC-only builds.
/* Recover a card that a previous stage left in UHS-I 1.8V signaling.
 *
 * A card that negotiated UHS-I only returns to 3.3V when VDD is removed --

src/sdhci.c:1843

  • The UHS-I warm-reset recovery retry should only be compiled/enabled for SD-card builds. Without a DISK_SDCARD guard, eMMC-only builds will fail to compile once sdhci_uhs_recover() is SD-only, and even today the retry path is semantically wrong for eMMC.
        if (status != 0 && sdhci_uhs_recover() == 0) {
            continue; /* retry this chunk with matched signaling */
        }

src/sdhci.c:418

  • Close the #ifdef DISK_SDCARD guard for sdhci_uhs_recover() so the rest of the file is not unintentionally made SD-only.
}


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/sdhci.c
Comment on lines 78 to +80
static uint32_t g_rca = 0; /* SD Card Relative Address */
/* Set once sdhci_uhs_recover() has switched the host to 1.8V signaling. */
static int g_uhs_recovered = 0;
Comment on lines +52 to +55
uint64_t hal_get_timer_us(void)
{
return 0;
}
@dgarske
dgarske force-pushed the zynqmp_sdhci_reset branch from 6e7d46b to fd18573 Compare August 11, 2026 21:35
@dgarske
dgarske force-pushed the zynqmp_sdhci_reset branch from fd18573 to 38ec1e1 Compare August 11, 2026 22:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants