Skip to content

Commit cc174cc

Browse files
committed
ZynqMP ZCU102 SD-card boot fixes
1 parent 00459d8 commit cc174cc

5 files changed

Lines changed: 66 additions & 12 deletions

File tree

config/examples/zynqmp_sdcard.config

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ DEBUG_SYMBOLS=1
2323
DEBUG_UART=1
2424
CFLAGS_EXTRA+=-DDEBUG_ZYNQ=1
2525

26+
# Display boot timing data
27+
#BOOT_BENCHMARK?=1
28+
2629
# SD card support - use SDHCI driver
2730
DISK_SDCARD?=1
2831
DISK_EMMC?=0
@@ -38,6 +41,7 @@ NO_XIP=1
3841

3942
# ELF loading support
4043
ELF?=1
44+
#DEBUG_ELF?=1
4145

4246
# Boot Exception Level: leave wolfBoot at EL2 for handoff to Linux (matches
4347
# the standard PetaLinux U-Boot flow and preserves KVM/hypervisor use of
@@ -79,7 +83,9 @@ WOLFBOOT_NO_PARTITIONS=1
7983
CFLAGS_EXTRA+=-DBOOT_PART_A=1
8084
CFLAGS_EXTRA+=-DBOOT_PART_B=2
8185

82-
# Disk read chunk size (512KB)
86+
# Disk read chunk size for firmware loading (update_disk.c). 512KB gives the
87+
# best throughput (~1.4s for 32MB). The SDMA engine handles boundary crossings
88+
# every 4KB (SDHCI_DMA_THRESHOLD default) within each 512KB chunk.
8389
CFLAGS_EXTRA+=-DDISK_BLOCK_SIZE=0x80000
8490

8591
# Linux rootfs is on partition 4. Device naming depends on whether both

docs/Targets.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2689,6 +2689,25 @@ Set the ZCU102 boot mode switches (SW6) for SD card boot:
26892689
| QSPI32 | 0 0 1 0 | on, on, off, on |
26902690
| SD1 | 1 1 1 0 | off, off, off, on |
26912691

2692+
**SDHCI Notes (Arasan controller)**
2693+
2694+
The ZynqMP uses an Arasan SDHCI v3.0 controller. Key considerations:
2695+
2696+
- **SDMA vs PIO**: The PIO (Programmed I/O) multi-block read path has a race condition on
2697+
this controller under compiler optimization (`-Os`/`-O2`). The BRR (Buffer Read Ready)
2698+
flag is re-polled too quickly between blocks, causing stale data reads that corrupt
2699+
firmware images. The default `SDHCI_DMA_THRESHOLD=4096` forces all multi-block reads
2700+
through the SDMA path, which avoids this issue entirely.
2701+
- **HV4E redirect**: The Arasan controller does not support Host Version 4 Enable (HV4E).
2702+
The platform HAL in `hal/zynq.c` transparently redirects SRS22/SRS23 writes to the
2703+
legacy SRS00 register for 32-bit SDMA addressing.
2704+
- **Card detect**: The Arasan controller does not support CDSS/CDTL card detect test
2705+
level. `SDHCI_FORCE_CARD_DETECT` is set in the config since FSBL already booted from
2706+
the same SD card.
2707+
- **`DISK_BLOCK_SIZE`**: Controls the firmware read chunk size in `update_disk.c` (default
2708+
64KB). This determines the per-read size passed to the SDHCI driver. Must be less than
2709+
the SDMA buffer boundary (4KB with the default threshold).
2710+
26922711
**Debug**
26932712

26942713
Enable SDHCI debug output by uncommenting in the config:

include/sdhci.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,16 @@
4040
#define SDHCI_BLOCK_SIZE 512
4141
#endif
4242

43-
/* DMA threshold - minimum transfer size to use DMA mode (default: 512KB) */
43+
/* DMA threshold - minimum transfer size (bytes) to use SDMA instead of PIO.
44+
* Default 4KB: forces SDMA for virtually all multi-block CMD18 reads.
45+
* The PIO multi-block read path has a known race condition on Arasan SDHCI
46+
* controllers (ZynqMP, Versal) where BRR (Buffer Read Ready) is re-checked
47+
* too quickly between blocks under compiler optimization (-Os/-O2), causing
48+
* stale data reads and firmware integrity failures. Using SDMA avoids the
49+
* BRR polling loop entirely.
50+
* Override in target .config if a larger PIO window is acceptable. */
4451
#ifndef SDHCI_DMA_THRESHOLD
45-
#define SDHCI_DMA_THRESHOLD (512U * 1024U)
52+
#define SDHCI_DMA_THRESHOLD (4U * 1024U)
4653
#endif
4754

4855
/* Disk test block address (platform should override) */

src/sdhci.c

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1289,7 +1289,22 @@ static int sdhci_transfer(int dir, uint32_t cmd_index, uint32_t block_addr,
12891289
#endif /* !SDHCI_SDMA_DISABLED */
12901290
}
12911291
else {
1292-
/* Blocking mode - buffer ready flag differs for read vs write */
1292+
/* PIO (Programmed I/O) mode — reads/writes data word-by-word via
1293+
* the SRS08 data port register.
1294+
*
1295+
* CAUTION: On Arasan SDHCI v3.0 (ZynqMP, Versal), multi-block PIO
1296+
* reads (CMD18) have a known race condition under compiler
1297+
* optimization (-Os/-O2). After reading one block, the BRR (Buffer
1298+
* Read Ready) flag in SRS12 may still be set from the previous
1299+
* block when the outer loop re-checks it. The optimized code
1300+
* re-polls so quickly that BRR has not yet auto-cleared, causing
1301+
* the next 512-byte read from SRS08 to return stale/partial data.
1302+
* This corrupts the loaded firmware image.
1303+
*
1304+
* Workaround: Set SDHCI_DMA_THRESHOLD low (default 4KB) so that
1305+
* multi-block reads use SDMA instead of this PIO path. The eMMC
1306+
* path manually clears BRR between blocks (W1C write below),
1307+
* which also avoids the race. */
12931308
uint32_t buf_ready_flag = (dir == SDHCI_DIR_READ) ?
12941309
SDHCI_SRS12_BRR : SDHCI_SRS12_BWR;
12951310

@@ -1531,15 +1546,21 @@ int sdhci_init(void)
15311546
}
15321547

15331548
#ifdef DEBUG_SDHCI
1534-
{
1535-
const char *card_type;
1536-
#ifdef DISK_EMMC
1537-
card_type = "eMMC";
1538-
#else
1539-
card_type = "SD";
1540-
#endif
1541-
wolfBoot_printf("sdhci_init: %s status: %d\n", card_type, status);
1549+
if (status == 0) {
1550+
wolfBoot_printf("SDHCI: DMA (threshold: %dKB, buf boundary: %dKB)\n",
1551+
SDHCI_DMA_THRESHOLD / 1024,
1552+
(4 << ((SDHCI_DMA_BUFF_BOUNDARY >> 12) & 0x7))
1553+
);
15421554
}
1555+
1556+
wolfBoot_printf("SDHCI: %s init, status %d\n",
1557+
#ifdef DISK_EMMC
1558+
"eMMC"
1559+
#else
1560+
"SD"
1561+
#endif
1562+
, status
1563+
);
15431564
#endif
15441565

15451566
return status;

src/update_disk.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,7 @@ void RAMFUNCTION wolfBoot_start(void)
353353
pB_ver_u = (uint32_t)pB_ver;
354354

355355
wolfBoot_printf("Versions, A:%u B:%u\r\n", pA_ver_u, pB_ver_u);
356+
wolfBoot_printf("Load block size: %dKB\r\n", DISK_BLOCK_SIZE / 1024);
356357
max_ver = (pB_ver_u > pA_ver_u) ? pB_ver_u : pA_ver_u;
357358

358359
/* Choose partition with higher version */

0 commit comments

Comments
 (0)