From e5093f41b22468b1fdc17908f46888d62ed15f47 Mon Sep 17 00:00:00 2001 From: Joakim Andersson Date: Tue, 17 Jun 2025 11:47:42 +0200 Subject: [PATCH] drivers: flash: Fix timeout handling in STM32 flash driver Fix timeout error that can occur in rare case. When the thread writing to flash is pre-emptive it can be scheduled out after reading the status register, but before checking if timeout has expired. In this case it will report timeout without re-checking the status register. When writing a lot to flash, for example a firmware update process then this situation is very likely to occur. Signed-off-by: Joakim Andersson --- drivers/flash/flash_stm32.c | 13 ++++++++++--- drivers/flash/flash_stm32h7x.c | 13 ++++++++++--- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/drivers/flash/flash_stm32.c b/drivers/flash/flash_stm32.c index a778443a982..94fb9363318 100644 --- a/drivers/flash/flash_stm32.c +++ b/drivers/flash/flash_stm32.c @@ -81,7 +81,8 @@ static int flash_stm32_check_status(const struct device *dev) int flash_stm32_wait_flash_idle(const struct device *dev) { - int64_t timeout_time = k_uptime_get() + STM32_FLASH_TIMEOUT; + k_timepoint_t timeout = sys_timepoint_calc(K_MSEC(STM32_FLASH_TIMEOUT)); + bool expired = false; int rc; uint32_t busy_flags; @@ -98,10 +99,16 @@ int flash_stm32_wait_flash_idle(const struct device *dev) #endif while ((FLASH_STM32_REGS(dev)->FLASH_STM32_SR & busy_flags)) { - if (k_uptime_get() > timeout_time) { - LOG_ERR("Timeout! val: %d", STM32_FLASH_TIMEOUT); + if (expired) { + LOG_ERR("Timeout! val: %d ms", STM32_FLASH_TIMEOUT); return -EIO; } + + /* Check if expired, but always read status register one more time. + * If the calling thread is pre-emptive we may have been scheduled out after reading + * the status register, and scheduled back after timeout has expired. + */ + expired = sys_timepoint_expired(timeout); } return 0; diff --git a/drivers/flash/flash_stm32h7x.c b/drivers/flash/flash_stm32h7x.c index 636ec1c8784..e089043cc19 100644 --- a/drivers/flash/flash_stm32h7x.c +++ b/drivers/flash/flash_stm32h7x.c @@ -427,7 +427,8 @@ static int flash_stm32_check_status(const struct device *dev) int flash_stm32_wait_flash_idle(const struct device *dev) { - int64_t timeout_time = k_uptime_get() + STM32H7_FLASH_TIMEOUT; + k_timepoint_t timeout = sys_timepoint_calc(K_MSEC(STM32H7_FLASH_TIMEOUT)); + bool expired = false; int rc; rc = flash_stm32_check_status(dev); @@ -441,10 +442,16 @@ int flash_stm32_wait_flash_idle(const struct device *dev) while (FLASH_STM32_REGS(dev)->SR1 & FLASH_SR_QW) #endif { - if (k_uptime_get() > timeout_time) { - LOG_ERR("Timeout! val: %d", STM32H7_FLASH_TIMEOUT); + if (expired) { + LOG_ERR("Timeout! val: %d ms", STM32H7_FLASH_TIMEOUT); return -EIO; } + + /* Check if expired, but always read status register one more time. + * If the calling thread is pre-emptive we may have been scheduled out after reading + * the status register, and scheduled back after timeout has expired. + */ + expired = sys_timepoint_expired(timeout); } return 0;