This is an automated email from the ASF dual-hosted git repository.

acassis pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git

commit 7e3d6066df4e186fd18290bf6f390eebee3c97f9
Author: Ricardo Maurizio Paul <[email protected]>
AuthorDate: Mon Oct 5 14:44:04 2026 +0200

    arch/arm/stm32h5: start the ICACHE from a clean state and bound its waits.
    
    A bootloader may hand over with the ICACHE enabled, and nothing waits for
    the invalidate that runs when the ICACHE is disabled.  The driver also
    polled BUSYF without a bound, so a stuck flag would hang the boot, and it
    ignored an invalidate that never finished.
    
    - stm32_enable_icache(): on first use, disable and invalidate the ICACHE
      before the associativity and region registers are written (they are
      only writable while EN=0), and wait for any pending invalidate before
      enabling it (RM0481 8.4.5).
    - stm32_disable_icache(): wait for the invalidate that EN=0 starts and
      clear BSYENDF and ERRF.
    - Bound every BUSYF wait with STM32_ICACHE_BUSY_TIMEOUT.  RM0481 gives no
      invalidate duration, so the value is a margin, not a measured limit.
    - stm32_enable_icache() now returns OK or -ETIMEDOUT instead of void.  On
      a timeout the ICACHE is left disabled: with BUSYF stuck it would not
      cache anything anyway (RM0481 8.4.5).  Existing callers ignore the result
      and keep working.
    - __start: when CONFIG_STM32_ICACHE is not set, disable an ICACHE left on
      by a bootloader, so reads of the OTP and UID cannot fault.  On an
      STM32H563 with the ICACHE left enabled this way, a 16-bit read of the
      UID raised a precise bus fault and up_progmem_write() failed its
      read-back check with -EIO.
    
    Assisted-by: Claude:claude-sonnet-5-5
    Signed-off-by: Ricardo Maurizio Paul <[email protected]>
---
 arch/arm/src/stm32h5/stm32_icache.c | 45 ++++++++++++++++++++++++++++++++++++-
 arch/arm/src/stm32h5/stm32_icache.h | 18 +++++++++++----
 arch/arm/src/stm32h5/stm32_start.c  | 14 ++++++++++++
 3 files changed, 72 insertions(+), 5 deletions(-)

diff --git a/arch/arm/src/stm32h5/stm32_icache.c 
b/arch/arm/src/stm32h5/stm32_icache.c
index c6837213bbc..625c5dee787 100644
--- a/arch/arm/src/stm32h5/stm32_icache.c
+++ b/arch/arm/src/stm32h5/stm32_icache.c
@@ -248,6 +248,26 @@ static void stm32_icache_setup_region(struct 
stm32_icache_region region)
 }
 #endif
 
+static bool stm32_icache_wait_invalidate(void)
+{
+  uint32_t timeout = STM32_ICACHE_BUSY_TIMEOUT;
+
+  /* Wait for a running invalidate (after reset, CACHEINV or EN=0) to end;
+   * the ICACHE should not be enabled before (RM0481 8.4.5).  Returns false
+   * on timeout.
+   */
+
+  while ((getreg32(STM32_ICACHE_SR) & ICACHE_SR_BUSYF) != 0)
+    {
+      if (--timeout == 0)
+        {
+          return false;
+        }
+    }
+
+  return true;
+}
+
 static void stm32_icache_mpu_setup(void)
 {
   /* Non-cacheable, execute-never.  stm32_mpuinitialize() enabled the MPU. */
@@ -362,6 +382,14 @@ void stm32_disable_icache(void)
   regval = getreg32(STM32_ICACHE_CR);
   regval &= ~(ICACHE_CR_EN);
   putreg32(regval, STM32_ICACHE_CR);
+
+  /* Disabling the ICACHE starts a full invalidate, wait for it to finish.
+   * The ICACHE is disabled whether or not the wait times out, so there is
+   * nothing to report: stm32_enable_icache() waits again before enabling.
+   */
+
+  stm32_icache_wait_invalidate();
+  putreg32(ICACHE_FCR_CBSYENDF | ICACHE_FCR_CERRF, STM32_ICACHE_FCR);
 }
 
 bool stm32_icache_enabled(void)
@@ -369,21 +397,36 @@ bool stm32_icache_enabled(void)
   return (getreg32(STM32_ICACHE_CR) & ICACHE_CR_EN) != 0;
 }
 
-void stm32_enable_icache(void)
+int stm32_enable_icache(void)
 {
   uint32_t regval;
 
   if (icache1.initialized != true)
     {
+      /* A bootloader may have left the ICACHE enabled.  Disable it, which
+       * also invalidates it: WAYSEL and the region registers can only be
+       * written while EN=0 (RM0481 8.4.4, 8.4.7).
+       */
+
+      stm32_disable_icache();
+
       stm32_icache_initialize();
       icache1.initialized = true;
     }
 
+  /* If the invalidate times out, leave the ICACHE disabled */
+
+  if (!stm32_icache_wait_invalidate())
+    {
+      return -ETIMEDOUT;
+    }
+
   /* Enable the ICACHE */
 
   regval = getreg32(STM32_ICACHE_CR);
   regval |= ICACHE_CR_EN;
   putreg32(regval, STM32_ICACHE_CR);
+  return OK;
 }
 
 void stm32_invalidate_icache(void)
diff --git a/arch/arm/src/stm32h5/stm32_icache.h 
b/arch/arm/src/stm32h5/stm32_icache.h
index 07b85969889..50824dae38c 100644
--- a/arch/arm/src/stm32h5/stm32_icache.h
+++ b/arch/arm/src/stm32h5/stm32_icache.h
@@ -38,6 +38,12 @@
  * Pre-processor Definitions
  ****************************************************************************/
 
+/* Bound on the BUSYF polling loops.  RM0481 gives no invalidate duration, so
+ * this is a margin, not a measured limit.
+ */
+
+#define STM32_ICACHE_BUSY_TIMEOUT 100000
+
 #ifndef __ASSEMBLY__
 
 #undef EXTERN
@@ -89,23 +95,27 @@ size_t stm32_get_icache_size(void);
  * Name: stm32_enable_icache
  *
  * Description:
- *   Initializes the STM32H5 ICACHE
+ *   Initializes (on first call) and enables the STM32H5 ICACHE.  The first
+ *   call disables and invalidates it first, in case a bootloader left it
+ *   enabled, and maps the uncacheable flash areas with the MPU.
  *
  * Input Parameters:
  *   None
  *
  * Returned Value:
- *   None
+ *   OK, or -ETIMEDOUT if the invalidate did not complete; the ICACHE is
+ *   then left disabled.
  *
  ****************************************************************************/
 
-void stm32_enable_icache(void);
+int stm32_enable_icache(void);
 
 /****************************************************************************
  * Name: stm32_disable_icache
  *
  * Description:
- *   Disables the STM32H5 ICACHE.
+ *   Disables the STM32H5 ICACHE and waits, for a bounded time, for the
+ *   invalidate that this starts.
  *
  * Input Parameters:
  *   None
diff --git a/arch/arm/src/stm32h5/stm32_start.c 
b/arch/arm/src/stm32h5/stm32_start.c
index d5384b05172..403125ae88c 100644
--- a/arch/arm/src/stm32h5/stm32_start.c
+++ b/arch/arm/src/stm32h5/stm32_start.c
@@ -240,6 +240,20 @@ void __start(void)
 
 #ifdef CONFIG_STM32_ICACHE
   stm32_enable_icache();
+#else
+  /* Disable an ICACHE left enabled by a bootloader: the OTP and RO flash
+   * areas cannot be read through it (RM0481 7.3.2).
+   */
+
+  if ((getreg32(STM32_ICACHE_CR) & ICACHE_CR_EN) != 0)
+    {
+      uint32_t regval;
+
+      regval = getreg32(STM32_ICACHE_CR);
+      regval &= ~(ICACHE_CR_EN);
+      putreg32(regval, STM32_ICACHE_CR);
+    }
+
 #endif
   showprogress('G');
 

Reply via email to