mirror of
https://github.com/apache/nuttx.git
synced 2026-10-07 22:35:22 +00:00
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 <ricardopaul@geotab.com>
This commit is contained in:
parent
8374dc60bd
commit
7e3d6066df
3 changed files with 72 additions and 5 deletions
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue