From 20cd3a3f094047294810c49f9060e674a0fdaa2e Mon Sep 17 00:00:00 2001 From: Luca Burelli Date: Wed, 23 Oct 2024 18:08:05 +0200 Subject: [PATCH] drivers/flash/mcux: fix flash_read() operation on LPC55S36 As other targets in the LPC55xxx series, the LPC55S36 has a Flash controller that raises ECC errors when reading erased pages directly. To avoid this, there is special code for this platform that calls the HAL FLASH_IsFlashAreaReadable() function. However, this in turn calls a function at an hardcoded address in ROM that _always_ causes an instruction fault, making the situation worse. This patch reworks the read operation to use the FLASH_Read() HAL function for this target to gracefully handle error conditions and properly emulate accesses to erased pages. The preprocessor is required since some targets do not define the FLASH_Read() function. Fixes: #80325 Signed-off-by: Luca Burelli --- drivers/flash/soc_flash_mcux.c | 48 +++++++++++++++++++++++----------- 1 file changed, 33 insertions(+), 15 deletions(-) diff --git a/drivers/flash/soc_flash_mcux.c b/drivers/flash/soc_flash_mcux.c index bcde5bcfc8878d..88b0387f623295 100644 --- a/drivers/flash/soc_flash_mcux.c +++ b/drivers/flash/soc_flash_mcux.c @@ -55,7 +55,7 @@ LOG_MODULE_REGISTER(flash_mcux); #define SOC_NV_FLASH_NODE DT_INST(0, soc_nv_flash) -#if defined(CONFIG_CHECK_BEFORE_READING) && !defined(CONFIG_SOC_LPC55S36) +#if defined(CONFIG_CHECK_BEFORE_READING) && !defined(CONFIG_SOC_LPC55S36) #define FMC_STATUS_FAIL FLASH_INT_CLR_ENABLE_FAIL_MASK #define FMC_STATUS_ERR FLASH_INT_CLR_ENABLE_ERR_MASK #define FMC_STATUS_DONE FLASH_INT_CLR_ENABLE_DONE_MASK @@ -205,35 +205,53 @@ static int flash_mcux_read(const struct device *dev, off_t offset, addr = offset + priv->pflash_block_base; #ifdef CONFIG_CHECK_BEFORE_READING + /* + * Ensure the area is readable, since a direct access may cause faults + * on erased or otherwise unreadable pages. Emulate erased pages, + * return other errors. + */ #ifdef CONFIG_SOC_LPC55S36 - /* Validates the given address range is loaded in the flash hiding region. */ - rc = FLASH_IsFlashAreaReadable(&priv->config, addr, len); - if (rc != kStatus_FLASH_Success) { - rc = -EIO; - } else { - /* Check whether the flash is erased ("len" and "addr" must be word-aligned). */ + /* On LPC55S36, use a HAL function to safely copy from Flash. */ + rc = FLASH_Read(&priv->config, addr, data, len); + switch (rc) { + case kStatus_FLASH_Success: + rc = 0; + break; + case kStatus_FLASH_EccError: + /* Check id the ECC issue is due to the Flash being erased + * ("addr" and "len" must be word-aligned for this call). + */ rc = FLASH_VerifyErase(&priv->config, ((addr + 0x3) & ~0x3), ((len + 0x3) & ~0x3)); if (rc == kStatus_FLASH_Success) { rc = -ENODATA; } else { - rc = 0; + rc = -EIO; } + break; + default: + rc = -EIO; + break; } - #else + #else /* CONFIG_SOC_LPC55S36 */ + /* On all other targets, check if the Flash area is readable. + * If so, copy data from it directly. + */ rc = is_area_readable(addr, len); - #endif /* CONFIG_SOC_LPC55S36 */ -#endif /* CONFIG_CHECK_BEFORE_READING */ - if (!rc) { memcpy(data, (void *) addr, len); } -#ifdef CONFIG_CHECK_BEFORE_READING - else if (rc == -ENODATA) { + #endif /* CONFIG_SOC_LPC55S36 */ + + if (rc == -ENODATA) { /* Erased area, return dummy data as an erased page. */ memset(data, 0xFF, len); rc = 0; } -#endif +#else /* CONFIG_CHECK_BEFORE_READING */ + /* No safety checks, directly copy the memory mapped data. */ + memcpy(data, (void *) addr, len); +#endif /* CONFIG_CHECK_BEFORE_READING */ + return rc; }