Add LS1028A eSDHC SD card disk boot support

pull/846/merge
David Garske 2026-09-09 09:31:50 -07:00 committed by Daniele Lacamera
parent 8eafe1ef76
commit a8c8c19fe8
6 changed files with 230 additions and 34 deletions

View File

@ -274,6 +274,14 @@ jobs:
config-file: ./config/examples/nxp-ls1028a-tpm.config
make-args: CROSS_COMPILE=aarch64-linux-gnu-
# SD card disk boot via the Freescale eSDHC driver (hal/nxp_esdhc.o)
nxp_ls1028a_sdcard_test:
uses: ./.github/workflows/test-build.yml
with:
arch: aarch64
config-file: ./config/examples/nxp-ls1028a-sdcard.config
make-args: CROSS_COMPILE=aarch64-linux-gnu-
nxp_mcxa_test:
uses: ./.github/workflows/test-build-mcux-sdk-manifests.yml
with:

20
arch.mk
View File

@ -181,6 +181,18 @@ ifeq ($(ARCH),AARCH64)
CFLAGS+=-fno-builtin-printf
endif
# SD card disk boot uses the Freescale eSDHC driver, not the Cadence
# SDHCI one (the shared AARCH64 block below wires update_disk.o).
# hal/nxp_esdhc.c is SD card only, so DISK_EMMC has no driver here:
# reject it rather than silently link the wrong controller driver.
ifeq ($(DISK_EMMC),1)
$(error DISK_EMMC is not supported on nxp_ls1028a (hal/nxp_esdhc.c is SD card only))
endif
ifeq ($(DISK_SDCARD),1)
override DISK_DRIVER=esdhc
OBJS+=hal/nxp_esdhc.o
endif
SPI_TARGET=nxp
endif
@ -1598,10 +1610,16 @@ ifneq ($(filter nxp_t1024 nxp_t1040,$(TARGET)),)
# Disk boot from SD card (eSDHC controller, driver hal/nxp_esdhc.c).
# src/gpt.o is already linked for all PPC targets above. The driver is
# kept out of the size-constrained stage1 loader.
ifneq ($(filter 1,$(DISK_SDCARD) $(DISK_EMMC)),)
# hal/nxp_esdhc.c drives SD cards only: it has no eMMC (CMD1/EXT_CSD)
# initialization, so DISK_EMMC has no driver on this arch.
ifeq ($(DISK_EMMC),1)
$(error DISK_EMMC is not supported on PPC (hal/nxp_esdhc.c is SD card only))
endif
ifeq ($(DISK_SDCARD),1)
CFLAGS+=-D"WOLFBOOT_UPDATE_DISK" -D"MAX_DISKS=1"
UPDATE_OBJS:=src/update_disk.o
OBJS+=src/disk.o
override DISK_DRIVER=esdhc
ifneq ($(STAGE1),1)
OBJS+=hal/nxp_esdhc.o
endif

View File

@ -0,0 +1,88 @@
# NXP LS1028A with SD card disk boot
#
# Same as nxp-ls1028a.config, but the signed application image is loaded
# from the SD card slot (eSDHC1 at 0x02140000) instead of XSPI NOR. The
# card is GPT (or MBR) partitioned; the signed image sits at offset 0 of
# the first two partitions (BOOT_PART_A / BOOT_PART_B, 0-based indexes
# into the partition table - same card layout as the T1040 eSDHC target).
# wolfBoot picks the slot with the higher version, verifies it and boots
# it from DDR (src/update_disk.c).
ARCH=AARCH64
TARGET=nxp_ls1028a
SIGN?=ECC256
HASH?=SHA256
DEBUG?=0
DEBUG_UART?=1
VTOR?=0
CORTEX_M0?=0
NO_ASM?=0
EXT_FLASH?=1
SPI_FLASH?=0
## Force app to be copied into ram
NO_XIP?=1
UART_FLASH?=0
ALLOW_DOWNGRADE?=0
NVM_FLASH_WRITEONCE?=0
WOLFBOOT_VERSION?=0
V?=0
NO_MPU?=0
SPMATH?=1
RAM_CODE?=0
DUALBANK_SWAP?=0
PKA?=0
ELF?=1
# SD card disk boot (Freescale eSDHC driver, hal/nxp_esdhc.c)
DISK_SDCARD=1
# Upper bound for the unauthenticated image size read from disk
WOLFBOOT_RAMBOOT_MAX_SIZE?=0x1000000
# Boot slots: partition table indexes (0-based) and read chunk size
CFLAGS_EXTRA+=-DBOOT_PART_A=0 -DBOOT_PART_B=1
CFLAGS_EXTRA+=-DDISK_BLOCK_SIZE=0x8000
# do_boot keeps the MMU and caches on (ENETC coherency) and cleans from the
# entry point before the jump. Pin the clean length to the RAMBOOT window so
# it covers the area images are loaded into regardless of what
# WOLFBOOT_PARTITION_SIZE is set to. Note this cleans one span from the
# entry point: an ELF or FIT payload that relocates a segment outside this
# window would need its own cache maintenance.
CFLAGS_EXTRA+=-DWOLFBOOT_MMU_FLUSH_APP_SIZE=0x1000000
# eSDHC bring-up trace on the DUART console
#CFLAGS_EXTRA+=-DDEBUG_ESDHC
# NOR Base Address
ARCH_FLASH_OFFSET?=0x20000000
# Flash Sector Size (128 KB)
WOLFBOOT_SECTOR_SIZE=0x20000
# wolfBoot start address
WOLFBOOT_ORIGIN=0x20020000
# wolfBoot partition size
BOOTLOADER_PARTITION_SIZE=0x20000
# Application Partition size. Matches the 16MB SD card slots; the sign
# tool bounds the image against this even though disk boot does not use
# the NOR partitions.
WOLFBOOT_PARTITION_SIZE?=0x1000000
# Location in Flash for Application Partition (unused for disk boot,
# kept for target.h consistency)
WOLFBOOT_PARTITION_BOOT_ADDRESS?=0x20100000
# Load the image to DDR, not the default OCRAM: OCRAM also holds
# wolfBoot's own .data/.bss/.stack and cannot hold a disk-sized image.
WOLFBOOT_LOAD_ADDRESS?=0x80100000
# Location in Flash for Update Partition
WOLFBOOT_PARTITION_UPDATE_ADDRESS?=0x21100000
# Location of temporary sector used during updates
WOLFBOOT_PARTITION_SWAP_ADDRESS?=0x22100000
# DTS (Device Tree). The load address must sit above
# WOLFBOOT_LOAD_ADDRESS + WOLFBOOT_RAMBOOT_MAX_SIZE.
WOLFBOOT_LOAD_DTS_ADDRESS?=0x82000000
WOLFBOOT_DTS_BOOT_ADDRESS?=0x20F00000
WOLFBOOT_DTS_UPDATE_ADDRESS?=0x20F00000

View File

@ -3698,6 +3698,11 @@ The LS1028A is a AARCH64 armv8-a Cortex-A72 processor. Support has been tested w
Example configurations for this target are provided in:
* NXP LS1028A: [/config/examples/nxp-ls1028a.config](/config/examples/nxp-ls1028a.config).
* NXP LS1028A with TPM: [/config/examples/nxp-ls1028a-tpm.config](/config/examples/nxp-ls1028a-tpm.config).
* NXP LS1028A with SD card boot: [/config/examples/nxp-ls1028a-sdcard.config](/config/examples/nxp-ls1028a-sdcard.config).
### LS1028A SD Card Boot (eSDHC)
The LS1028A can load the signed application image from the SD card slot (eSDHC1) using the same Freescale eSDHC driver as the T1040 (`hal/nxp_esdhc.c`, built as its own object). The card layout is identical to the T1040 SD target: GPT (or MBR) partitioned, with the signed image at offset 0 of the first two partitions (`BOOT_PART_A`/`BOOT_PART_B`, 0-based). wolfBoot reads both headers, picks the higher version, loads it to DDR (`WOLFBOOT_LOAD_ADDRESS=0x80100000`), verifies the signature and boots it. The driver reprograms the eSDHC source clock (HWA2) at init, because the NOR-boot RCW leaves it on a source too fast for card identification. Define `DEBUG_ESDHC` (see the config) for controller bring-up trace on the DUART console. Validated on the LS1028ARDB booting a signed image from SD.
### Building wolfBoot for NXP LS1028A

View File

@ -19,7 +19,8 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
*/
/* Freescale/NXP eSDHC block driver for QorIQ (T1040, T1024, T2080).
/* Freescale/NXP eSDHC block driver for QorIQ PPC (T1040, T1024, T2080)
* and Layerscape (LS1028A).
*
* Provides the four entry points src/disk.c expects (disk_init, disk_read,
* disk_write, disk_close) so the disk boot path, and therefore DISK_FS, can
@ -30,9 +31,9 @@
* one XFERTYP register, combines block size and count into BLKATTR, and has
* a watermark register with no standard-SDHCI equivalent.
*
* Compiled as its own object (arch.mk adds hal/nxp_esdhc.o when
* DISK_SDCARD or DISK_EMMC is set); the clock helpers it needs are
* exported by nxp_ppc.h.
* Compiled as its own object (the target's arch.mk block adds
* hal/nxp_esdhc.o and sets DISK_DRIVER=esdhc when DISK_SDCARD is set);
* per-target base address, clocks and byte order are selected below.
*
* Transfers use PIO through DATPORT rather than DMA. On e5500 with the MMU
* enabled a DMA descriptor would need cache maintenance on the destination,
@ -47,7 +48,38 @@
#include "disk.h"
#include "printf.h"
/* Per-target selection. The eSDHC block is the same IP on big-endian QorIQ
* PPC and little-endian Layerscape; the register file follows the
* integration, so a native 32-bit access reads it correctly on both. Only
* the base address, source clock and DATPORT byte order move per target. */
#if defined(TARGET_nxp_ls1028a)
#include "nxp_ls1028a.h"
/* eSDHC1 is the SD card slot; eSDHC2 is eMMC, not supported by this driver. */
#define ESDHC_CTRL_BASE ESDHC_BASE(0)
#define ESDHC_EMODE_SEL ESDHC_PROCTL_EMODE_LE
#ifndef ESDHC_REF_CLK
#define ESDHC_REF_CLK 400000000UL
#endif
/* The block is clocked by hardware-accelerator mux HWA2 (the device tree
* binds mmc@2140000 to QORIQ_CLK_HWACCEL index 1). The NOR-boot RCW leaves
* it on CGA_PLL2 (1.2 GHz), which overruns the card during identification;
* CLKSEL=7 selects CGA_PLL1/3. Set at runtime so SD works whichever RCW
* booted us. hwaccel[idx] = clockgen + 0x20*idx + 0x10, eSDHC is idx 1. */
#define LS1028A_HWA2CSR (CGUCGA_BASE + 0x30)
#define HWA_CLKSEL_MASK 0x78000000U
#define HWA_CLKSEL_SHIFT 27
#define HWA_CLKSEL_ESDHC 7U
#else
/* QorIQ PPC T-series: big-endian core and registers. */
#include "nxp_ppc.h"
#define ESDHC_CTRL_BASE (CCSRBAR + 0x114000)
#define ESDHC_EMODE_SEL ESDHC_PROCTL_EMODE_BE
#endif /* target selection */
#ifdef DEBUG_ESDHC
#define ESDHC_DBG(_f_, ...) wolfBoot_printf(_f_, ##__VA_ARGS__)
@ -55,18 +87,7 @@
#define ESDHC_DBG(_f_, ...) do{}while(0)
#endif
/* ---------------------------------------------------------------------
* Register map. CCSRBAR comes from nxp_ppc.h for the selected target.
*
* The block presents its registers big-endian and the e5500 is big-endian,
* so a native 32-bit access reads them correctly with no swapping. The one
* exception is DATPORT, whose byte order is selected by PROCTL[EMODE] and is
* configured below.
* --------------------------------------------------------------------- */
#ifndef ESDHC_BASE
#define ESDHC_BASE (CCSRBAR + 0x114000)
#endif
#define ESDHC_REG(off) ((volatile uint32_t*)(ESDHC_BASE + (off)))
#define ESDHC_REG(off) ((volatile uint32_t*)(ESDHC_CTRL_BASE + (off)))
#define ESDHC_DSADDR 0x00
#define ESDHC_BLKATTR 0x04
@ -117,11 +138,11 @@
#define ESDHC_PROCTL_DTW_1BIT (0U << 1)
#define ESDHC_PROCTL_DTW_4BIT (1U << 1)
#define ESDHC_PROCTL_DTW_MASK (3U << 1)
/* EMODE selects the byte order of DATPORT. Big-endian mode delivers bytes
* in media order when the word is stored natively by this big-endian core.
* Verified on T1040D4RDB silicon: little-endian mode read every aligned
* 4-byte group byte-reversed (MBR signature came back AA55). */
/* EMODE sets DATPORT byte order; ESDHC_EMODE_SEL picks the mode giving
* media order for a native word store. Silicon-verified: BE on the T1040
* (LE there returned every aligned 4-byte group reversed). */
#define ESDHC_PROCTL_EMODE_BE (0U << 4)
#define ESDHC_PROCTL_EMODE_LE (2U << 4)
#define ESDHC_PROCTL_EMODE_MASK (3U << 4)
/* SYSCTL */
@ -186,6 +207,27 @@ static int g_esdhc_ready;
* 37500000 Hz (600 MHz / 16).
* --------------------------------------------------------------------- */
#if defined(TARGET_nxp_ls1028a)
/* ARM generic timer; hal_init() enables the system counter first. */
static uint64_t esdhc_timebase(void)
{
uint64_t cnt;
__asm__ __volatile__("isb; mrs %0, cntpct_el0" : "=r"(cnt));
return cnt;
}
static uint32_t esdhc_read_tb_hz(void)
{
uint64_t frq;
__asm__ __volatile__("mrs %0, cntfrq_el0" : "=r"(frq));
return (uint32_t)frq;
}
#else
static uint64_t esdhc_timebase(void)
{
uint32_t hi, lo, hi2;
@ -201,6 +243,13 @@ static uint64_t esdhc_timebase(void)
return ((uint64_t)hi << 32) | (uint64_t)lo;
}
static uint32_t esdhc_read_tb_hz(void)
{
return TIMEBASE_HZ;
}
#endif /* target timebase */
/* Timebase frequency, cached by disk_init(). TIMEBASE_HZ reads clock
* registers and divides on every use; the value cannot change at runtime,
* and disk_init() rejects a zero reading before any other driver path can
@ -311,7 +360,11 @@ static int esdhc_send_cmd(uint32_t idx, uint32_t arg, uint32_t xfertyp,
/* Set the SD clock. The divider is SDCLKFS (base 2 prescaler) times DVS. */
static void esdhc_set_clock(uint32_t target_hz)
{
#if defined(TARGET_nxp_ls1028a)
uint32_t base = ESDHC_REF_CLK;
#else
uint32_t base = hal_get_bus_clk();
#endif
uint32_t pre = 2, div = 1, sysctl;
if (target_hz == 0U) {
@ -341,8 +394,9 @@ static void esdhc_set_clock(uint32_t target_hz)
ESDHC_SYSCTL_SDCLKEN;
esdhc_udelay(100);
ESDHC_DBG("esdhc: clock %u Hz (pre %u, div %u)\r\n",
(base / pre) / div, pre, div);
ESDHC_DBG("esdhc: clock %u Hz (pre %u, div %u) SYSCTL %x PROCTL %x\r\n",
(base / pre) / div, pre, div,
*ESDHC_REG(ESDHC_SYSCTL), *ESDHC_REG(ESDHC_PROCTL));
}
@ -376,7 +430,7 @@ static int esdhc_host_init(void)
/* 1-bit bus for identification, and set the data-port byte order. */
proctl = *ESDHC_REG(ESDHC_PROCTL);
proctl &= ~(ESDHC_PROCTL_DTW_MASK | ESDHC_PROCTL_EMODE_MASK);
proctl |= ESDHC_PROCTL_DTW_1BIT | ESDHC_PROCTL_EMODE_BE;
proctl |= ESDHC_PROCTL_DTW_1BIT | ESDHC_EMODE_SEL;
*ESDHC_REG(ESDHC_PROCTL) = proctl;
esdhc_set_clock(400000U);
@ -403,6 +457,7 @@ static int esdhc_send_acmd(uint32_t idx, uint32_t arg, uint32_t xfertyp,
ESDHC_XFERTYP_RSPTYP_48 | ESDHC_XFERTYP_CICEN | ESDHC_XFERTYP_CCCEN,
NULL);
if (ret != 0) {
ESDHC_DBG("esdhc: CMD55 (for ACMD%u) failed\r\n", idx);
return ret;
}
return esdhc_send_cmd(idx, arg, xfertyp, resp);
@ -442,8 +497,10 @@ static int esdhc_card_init(void)
return -1;
}
v2 = 1;
ESDHC_DBG("esdhc: CMD8 ok resp %x\r\n", resp[0]);
}
else {
ESDHC_DBG("esdhc: CMD8 no response (v1 or signalling)\r\n");
/* CMD8 leaves the command line in error state on a v1 card. */
*ESDHC_REG(ESDHC_SYSCTL) = *ESDHC_REG(ESDHC_SYSCTL) |
ESDHC_SYSCTL_RSTC;
@ -529,10 +586,12 @@ static int esdhc_card_init(void)
/* Drain one block from the data port.
*
* PROCTL[EMODE] is set to big-endian above, so a native 32-bit read of
* DATPORT returns the four media bytes already in order and they can be
* stored as-is. Silicon-verified: little-endian mode returned every
* aligned 4-byte group byte-reversed. */
* ESDHC_EMODE_SEL (set per target above) makes a native 32-bit read of
* DATPORT return the four media bytes already in order, so they are
* stored as-is. The correct mode differs by integration and both are
* silicon-verified: big-endian on the big-endian T1040, where
* little-endian returned every aligned 4-byte group reversed, and
* little-endian on the little-endian LS1028A. */
static int esdhc_read_block(uint8_t *buf)
{
uint32_t i, word;
@ -662,6 +721,19 @@ static int esdhc_read_blocks(uint64_t lba, uint32_t count, uint8_t *buf)
* disk.c interface
* --------------------------------------------------------------------- */
#if defined(TARGET_nxp_ls1028a)
/* Route a usable source clock to the eSDHC block (see HWA2CSR above). */
static void esdhc_clock_src_init(void)
{
volatile uint32_t *hwa2 = (volatile uint32_t*)LS1028A_HWA2CSR;
uint32_t val = (*hwa2 & ~HWA_CLKSEL_MASK) |
(HWA_CLKSEL_ESDHC << HWA_CLKSEL_SHIFT);
*hwa2 = val;
ESDHC_DBG("esdhc: HWA2CSR %x\r\n", *hwa2);
}
#endif
int disk_init(int drv)
{
if (drv != 0) {
@ -673,7 +745,10 @@ int disk_init(int drv)
/* Cache the timebase frequency for every delay and timeout below. A
* zero reading means no timeout in this driver could ever expire, so
* fail here and let the caller panic instead of spinning forever. */
g_esdhc_tb_hz = TIMEBASE_HZ;
#if defined(TARGET_nxp_ls1028a)
esdhc_clock_src_init();
#endif
g_esdhc_tb_hz = esdhc_read_tb_hz();
if (g_esdhc_tb_hz == 0U) {
return -1;
}

View File

@ -801,12 +801,14 @@ ifeq ($(DISK_EMMC),1)
CFLAGS+=-D"DISK_EMMC=1"
endif
# Add SDHCI driver if SD card or eMMC is enabled (only add once).
# PPC targets provide their own eSDHC driver (hal/nxp_esdhc.o, added in
# arch.mk), so the Cadence SDHCI driver must not be linked there (its
# disk_* entry points would collide).
# Add the SD/eMMC block driver if SD card or eMMC is enabled (only once).
# DISK_DRIVER selects which one: the Cadence SDHCI driver (src/sdhci.c,
# the default) or the Freescale eSDHC driver (hal/nxp_esdhc.o, added by
# the target's arch.mk block, which also sets DISK_DRIVER=esdhc). Exactly
# one may link: both define the disk_* entry points.
DISK_DRIVER?=cadence
ifneq ($(filter 1,$(DISK_SDCARD) $(DISK_EMMC)),)
ifneq ($(ARCH),PPC)
ifeq ($(DISK_DRIVER),cadence)
OBJS+= src/sdhci.o
endif
endif