From 737bb55f76b610b08298a575a316c010f64d9d3e Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Tue, 18 Aug 2026 09:10:13 +0200 Subject: [PATCH] ls1028a: wait for the NOR program/erase cycle to finish XSPI_IPCMDDONE only says the controller finished driving the sequence on the bus. The device keeps WIP set for the ~ms the cycle takes and ignores Write Enable while it is set, so the per-page WREN this PR added was dropped for pages 2..N and their Page Programs with it. The erase loops polled the same bit with inverted polarity: they exited as soon as WIP read *set*, i.e. once the erase had started, and would spin forever if they missed the busy window. FLASH_READY_MSK is renamed FLASH_SR_WIP_MSK so the sense is not guessable, and both paths now share xspi_wait_ready(). The test device models WIP, so a missing wait shows up as a rejected Write Enable. --- hal/nxp_ls1028a.c | 30 +++++++++++++------- hal/nxp_ls1028a.h | 5 +++- tools/unit-tests/unit-ls1028a-xspi-write.c | 32 ++++++++++++++++++---- 3 files changed, 50 insertions(+), 17 deletions(-) diff --git a/hal/nxp_ls1028a.c b/hal/nxp_ls1028a.c index 30968253..9981d7b0 100644 --- a/hal/nxp_ls1028a.c +++ b/hal/nxp_ls1028a.c @@ -520,6 +520,20 @@ void xspi_read_sr(uint8_t* rxbuf, uint32_t addr, uint32_t len) XSPI_INTR = XSPI_IPCMDDONE; } +/* Block until the NOR device finishes its program/erase cycle. + * XSPI_IPCMDDONE only reports that the controller finished driving the + * sequence on the bus; the device keeps WIP set for the ~ms the cycle + * takes, and ignores Write Enable and further program/erase commands + * until it clears. */ +void xspi_wait_ready(uint32_t addr) +{ + uint8_t status[4] = {0, 0, 0, 0}; + + do { + xspi_read_sr(status, addr, 1); + } while (status[0] & FLASH_SR_WIP_MSK); +} + void xspi_sw_reset(void) { XSPI_SWRESET(); @@ -599,6 +613,10 @@ void xspi_flash_write(uintptr_t address, const uint8_t *data, uint32_t len) XSPI_IPTXFCR = XSPI_IPRCFCR_FLUSH; XSPI_INTR = XSPI_IPCMDDONE; + /* The program cycle is still running: the next iteration's + * Write Enable would be ignored and its Page Program dropped. */ + xspi_wait_ready(address); + len -= size; address += size; } @@ -639,14 +657,10 @@ int hal_flash_erase(uintptr_t address, int len) num_sectors += (len % FLASH_ERASE_SIZE) ? 1 : 0; for (i = 0; i < num_sectors; i++) { - uint8_t status[4] = {0, 0, 0, 0}; - xspi_write_en(address + i * FLASH_ERASE_SIZE); xspi_flash_sec_erase(address + i * FLASH_ERASE_SIZE); - while (!(status[0] & FLASH_READY_MSK)) { - xspi_read_sr(status, 0, 1); - } + xspi_wait_ready(address + i * FLASH_ERASE_SIZE); } xspi_sw_reset(); @@ -685,14 +699,10 @@ int ext_flash_erase(uintptr_t address, int len) num_sectors += (len % FLASH_ERASE_SIZE) ? 1 : 0; for (i = 0; i < num_sectors; i++) { - uint8_t status[4] = {0, 0, 0, 0}; - xspi_write_en(address + i * FLASH_ERASE_SIZE); xspi_flash_sec_erase(address + i * FLASH_ERASE_SIZE); - while (!(status[0] & FLASH_READY_MSK)) { - xspi_read_sr(status, 0, 1); - } + xspi_wait_ready(address + i * FLASH_ERASE_SIZE); } xspi_sw_reset(); diff --git a/hal/nxp_ls1028a.h b/hal/nxp_ls1028a.h index c461ad34..d87360a3 100644 --- a/hal/nxp_ls1028a.h +++ b/hal/nxp_ls1028a.h @@ -463,7 +463,10 @@ #define FLASH_SECTOR_CNT (FLASH_BANK_SIZE / FLASH_ERASE_SIZE) #define FLASH_ERASE_TOUT 60000 /* Flash Erase Timeout (ms) */ #define FLASH_WRITE_TOUT 500 /* Flash Write Timeout (ms) */ -#define FLASH_READY_MSK (0x1 << 0) +/* RDSR (0x05) bit 0 is WIP: 1 while a program or erase is running, + * 0 when the device is ready. The device ignores Write Enable and any + * further program/erase while WIP is set. */ +#define FLASH_SR_WIP_MSK (0x1 << 0) #define MASK_32BIT 0xffffffff /* LUT register helper */ diff --git a/tools/unit-tests/unit-ls1028a-xspi-write.c b/tools/unit-tests/unit-ls1028a-xspi-write.c index 76fde927..56acc78f 100644 --- a/tools/unit-tests/unit-ls1028a-xspi-write.c +++ b/tools/unit-tests/unit-ls1028a-xspi-write.c @@ -82,6 +82,11 @@ static int g_xspi_log_n; /* Emulated NOR flash plus the write-enable latch real NOR keeps. */ static uint8_t g_nor[2 * 128 * 1024]; static int g_wel; +/* RDSR polls remaining before the emulated device reports WIP clear. + * A program or erase arms it; Write Enable and program/erase issued + * while it is armed are ignored, exactly as the device does. */ +#define NOR_BUSY_POLLS 2 +static int g_busy; /* The FlexSPI drains the TX FIFO while the command runs, so the flash * receives the bytes in the order the driver hands them to the FIFO. @@ -108,8 +113,21 @@ static void xspi_emu_start(void) uint32_t len = ipcr1 & 0xFFFF; uint32_t addr = XSPI_IPCR0; uint32_t i; - int e = g_xspi_log_n < XSPI_LOG_MAX ? g_xspi_log_n : XSPI_LOG_MAX - 1; + int e; + /* Status polls are not operations: answer them without taking a + * log slot, so the assertions below stay about WEN/PP/SE order. */ + if (seq == LUT_INDEX_RDSR) { + /* WIP stays set for a few polls after a program or erase */ + XSPI_RFD(0) = g_busy ? FLASH_SR_WIP_MSK : 0; + if (g_busy) + g_busy--; + XSPI_INTR |= XSPI_IPCMDDONE; + g_tfd_stream_len = 0; + return; + } + + e = g_xspi_log_n < XSPI_LOG_MAX ? g_xspi_log_n : XSPI_LOG_MAX - 1; if (g_xspi_log_n < XSPI_LOG_MAX) g_xspi_log_n++; g_xspi_log[e].addr = addr; @@ -118,7 +136,10 @@ static void xspi_emu_start(void) if (seq == LUT_INDEX_WRITE_EN) { g_xspi_log[e].cmd = XSPI_CMD_WEN; - g_wel = 1; + if (g_busy) + g_xspi_log[e].rejected = 1; /* WREN ignored while WIP set */ + else + g_wel = 1; } else if (seq == LUT_INDEX_PP) { g_xspi_log[e].cmd = XSPI_CMD_PP; @@ -135,6 +156,7 @@ static void xspi_emu_start(void) g_tfd_stream[i]; } g_wel = 0; /* the program consumes the latch */ + g_busy = NOR_BUSY_POLLS; } else { g_xspi_log[e].rejected = 1; /* NOR ignores PP without WEN */ @@ -145,15 +167,12 @@ static void xspi_emu_start(void) if (g_wel) { memset(&g_nor[addr], 0xFF, len); g_wel = 0; + g_busy = NOR_BUSY_POLLS; } else { g_xspi_log[e].rejected = 1; } } - else if (seq == LUT_INDEX_RDSR) { - g_xspi_log[e].cmd = XSPI_CMD_RDSR; - XSPI_RFD(0) = FLASH_READY_MSK; /* status: READY */ - } XSPI_INTR |= XSPI_IPCMDDONE; g_tfd_stream_len = 0; /* the FIFO was drained by the command */ @@ -189,6 +208,7 @@ static void setup(void) memset(g_nor, 0xFF, sizeof(g_nor)); g_xspi_log_n = 0; g_wel = 0; + g_busy = 0; g_tfd_stream_len = 0; XSPI_INTR = 0x1 << 6; /* TX FIFO write enable */ }