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
pull/892/head
Daniele Lacamera 2026-09-15 08:41:46 +02:00
parent 63f1ce7872
commit 4e01a45e3b
1 changed files with 44 additions and 15 deletions

View File

@ -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;
}