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');