From 4e01a45e3bae688602339436dfa5e16ffbd923b2 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Tue, 15 Sep 2026 08:41:46 +0200 Subject: [PATCH] F-12881: p1021: check NAND program/erase status and ECC results The write/erase loops discarded the JEDEC status byte from MDR and hal_flash_command ignored the PAR (uncorrectable ECC) and FCT (FCM timeout) bits, so failed programs, erases and uncorrectable reads all reported success. Check P/WPS in the status byte and stop the loop, and fail the command on PAR/FCT. The small-page FCM sequence now ends with the status command (CM3+RSW) so MDR holds a valid status like the large-page path; not verified on real P1021 hardware. config_io_pin now uses a single masked store for CPDIR/ CPPAR (F-12880): the clear-then-set pair could drop a concurrent update to another pin in the same register. F-12880, F-12881, F-12882 --- hal/nxp_p1021.c | 59 ++++++++++++++++++++++++++++++++++++------------- 1 file changed, 44 insertions(+), 15 deletions(-) diff --git a/hal/nxp_p1021.c b/hal/nxp_p1021.c index 774be3bc..8052d65a 100644 --- a/hal/nxp_p1021.c +++ b/hal/nxp_p1021.c @@ -336,6 +336,10 @@ enum elbc_amask_sizes { #define NAND_CMD_READSTART 0x30 /* Extended command for large page devices */ +/* NAND device status byte (JEDEC), returned in MDR by the RS/RSW ops */ +#define NAND_STATUS_WPS (1 << 1) /* write protect status */ +#define NAND_STATUS_FAIL (1 << 3) /* P: program/erase fail */ + /* DDR */ /* DDR3: 512MB, 333.333 MHz (666.667 MT/s) */ @@ -689,6 +693,15 @@ static int hal_flash_command(uint8_t iswrite) if (!(ltesr & ELBC_LTESR_CC)) { ret = -1; } + else if (ltesr & ELBC_LTESR_FCT) { + /* a CW/RSW wait timed out: the device never became ready */ + ret = -1; + } + else if (iswrite == 0 && (ltesr & ELBC_LTESR_PAR)) { + /* uncorrectable ECC error during the FCM read: the data in the + * FCM buffer cannot be trusted */ + ret = -1; + } /* clear interrupt */ set32(ELBC_LTESR, ltesr & ELBC_NAND_MASK); @@ -1063,18 +1076,18 @@ static void config_io_pin(uint8_t port, uint8_t pin, int dir, int open_drain, pin_2bit_dir = (uint32_t)(dir << (NUM_OF_PINS - (pin % (NUM_OF_PINS / 2) + 1) * 2)); - /* Setup the direction */ + /* Setup the direction: one masked store - a clear-then-set pair + * would drop a concurrent update to another pin in the same + * register */ tmp_val = (pin > (NUM_OF_PINS / 2) - 1) ? get32(GUTS_CPDIR2(port)) : get32(GUTS_CPDIR1(port)); if (pin > (NUM_OF_PINS / 2) - 1) { - set32(GUTS_CPDIR2(port), ~pin_2bit_mask & tmp_val); - set32(GUTS_CPDIR2(port), pin_2bit_dir | tmp_val); + set32(GUTS_CPDIR2(port), (~pin_2bit_mask & tmp_val) | pin_2bit_dir); } else { - set32(GUTS_CPDIR1(port), ~pin_2bit_mask & tmp_val); - set32(GUTS_CPDIR1(port), pin_2bit_dir | tmp_val); + set32(GUTS_CPDIR1(port), (~pin_2bit_mask & tmp_val) | pin_2bit_dir); } /* Calculate pin location for 1bit mask */ @@ -1089,21 +1102,21 @@ static void config_io_pin(uint8_t port, uint8_t pin, int dir, int open_drain, set32(GUTS_CPODR(port), ~pin_1bit_mask & tmp_val); } - /* Setup the assignment */ + /* Setup the assignment: one masked store (same reason as the + * direction write above) */ tmp_val = (pin > (NUM_OF_PINS/2) - 1) ? get32(GUTS_CPPAR2(port)): get32(GUTS_CPPAR1(port)); pin_2bit_assign = (uint32_t)(assign << (NUM_OF_PINS - (pin % (NUM_OF_PINS / 2) + 1) * 2)); - /* Clear and set 2 bits mask */ if (pin > (NUM_OF_PINS/2) - 1) { - set32(GUTS_CPPAR2(port), ~pin_2bit_mask & tmp_val); - set32(GUTS_CPPAR2(port), pin_2bit_assign | tmp_val); + set32(GUTS_CPPAR2(port), (~pin_2bit_mask & tmp_val) | + pin_2bit_assign); } else { - set32(GUTS_CPPAR1(port), ~pin_2bit_mask & tmp_val); - set32(GUTS_CPPAR1(port), pin_2bit_assign | tmp_val); + set32(GUTS_CPPAR1(port), (~pin_2bit_mask & tmp_val) | + pin_2bit_assign); } } @@ -1641,13 +1654,19 @@ int ext_flash_write(uintptr_t address, const uint8_t *data, int len) page_size = 512; set32(ELBC_FCR, ELBC_FCR_CMD(0, NAND_CMD_READA) | ELBC_FCR_CMD(1, NAND_CMD_PAGE_PROG2) | - ELBC_FCR_CMD(2, NAND_CMD_PAGE_PROG1)); + ELBC_FCR_CMD(2, NAND_CMD_PAGE_PROG1) | + ELBC_FCR_CMD(3, NAND_CMD_STATUS)); + /* the CM3+RSW pair issues the status command after the program + * execute and waits for it, so MDR holds the page status like the + * large page path */ set32(ELBC_FIR, ELBC_FIR_OP(0, ELBC_FIR_OP_CW0) | ELBC_FIR_OP(1, ELBC_FIR_OP_CM2) | ELBC_FIR_OP(2, ELBC_FIR_OP_CA) | ELBC_FIR_OP(3, ELBC_FIR_OP_PA) | ELBC_FIR_OP(4, ELBC_FIR_OP_WB) | - ELBC_FIR_OP(5, ELBC_FIR_OP_CW1)); + ELBC_FIR_OP(5, ELBC_FIR_OP_CW1) | + ELBC_FIR_OP(6, ELBC_FIR_OP_CM3) | + ELBC_FIR_OP(7, ELBC_FIR_OP_RSW)); #endif (void)block_size; /* not used - shown for reference */ @@ -1690,7 +1709,12 @@ int ext_flash_write(uintptr_t address, const uint8_t *data, int len) wolfBoot_printf("write page %d, col %d, status %x\n", page, col, status); #endif - (void)status; + /* P (program fail) or WPS (write protect) set: the page did not + * program. Stop; retrying the same page fails the same way. */ + if (status & (NAND_STATUS_FAIL | NAND_STATUS_WPS)) { + ret = -1; + break; + } address += write_size; pos += write_size; data += write_size; @@ -1851,7 +1875,12 @@ int ext_flash_erase(uintptr_t address, int len) #ifdef DEBUG_EXT_FLASH wolfBoot_printf("erase page %d, status %x\n", page, status); #endif - (void)status; + /* P (erase fail) or WPS (write protect) set: the block did not + * erase. Stop; erasing the same block fails the same way. */ + if (status & (NAND_STATUS_FAIL | NAND_STATUS_WPS)) { + ret = -1; + break; + } address += block_size; len -= block_size; }