From 2eca86e8fa15e50bf9f928b1df6a925b49cdd142 Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Wed, 23 Mar 2022 17:44:10 +1100 Subject: [PATCH] stm32/powerctrl: Add sleep RCC semaphore management for WB55 MCUs. Signed-off-by: Andrew Leech --- ports/stm32/boards/stm32wbxx_hal_conf_base.h | 38 ++++++++++++ ports/stm32/powerctrl.c | 64 ++++++++++++++++++++ ports/stm32/powerctrlboot.c | 18 +++--- ports/stm32/rfcore.c | 11 ++++ 4 files changed, 123 insertions(+), 8 deletions(-) diff --git a/ports/stm32/boards/stm32wbxx_hal_conf_base.h b/ports/stm32/boards/stm32wbxx_hal_conf_base.h index b03ad26864..25eb4b93ec 100644 --- a/ports/stm32/boards/stm32wbxx_hal_conf_base.h +++ b/ports/stm32/boards/stm32wbxx_hal_conf_base.h @@ -42,6 +42,7 @@ #include "stm32wbxx_hal_uart.h" #include "stm32wbxx_hal_usart.h" #include "stm32wbxx_ll_adc.h" +#include "stm32wbxx_ll_hsem.h" #include "stm32wbxx_ll_lpuart.h" #include "stm32wbxx_ll_rtc.h" #include "stm32wbxx_ll_usart.h" @@ -79,4 +80,41 @@ // HAL parameter assertions are disabled #define assert_param(expr) ((void)0) +// Hardware Semaphores - ref: AN5289 + +// Used to prevent conflicts after standby / sleep. +// Each CPUs takes this semaphore at standby wakeup until conflicting elements are restored. +// Note: this is used in WB55 reference examples, but not listed in AN5289 Rev 6 +#define CFG_HW_PWR_STANDBY_SEMID 10 + +// Ensures that CPU2 does not update the BLE persistent data in SRAM2 when CPU1 is reading them +#define CFG_HW_THREAD_NVM_SRAM_SEMID 9 + +// Ensures that CPU2 does not update the Thread persistent data in SRAM2 when CPU1 is reading them +#define CFG_HW_BLE_NVM_SRAM_SEMID 8 + +// Used by CPU2 to prevent CPU1 from writing/erasing data in Flash memory +#define CFG_HW_BLOCK_FLASH_REQ_BY_CPU2_SEMID 7 + +// Used by CPU1 to prevent CPU2 from writing/erasing data in Flash memory +#define CFG_HW_BLOCK_FLASH_REQ_BY_CPU1_SEMID 6 + +// Used to manage the CLK48 clock configuration (RCC_CRRCR, RCC_CCIPR) +#define CFG_HW_CLK48_CONFIG_SEMID 5 + +// Used to manage the entry Stop Mode procedure +#define CFG_HW_ENTRY_STOP_MODE_SEMID 4 + +// Used to access the RCC (RCC_CR, RCC_EXTCFGR, RCC_CFGR, RCC_SMPSCR) +#define CFG_HW_RCC_SEMID 3 + +// Used to access the FLASH (all registers) +#define CFG_HW_FLASH_SEMID 2 + +// Used to access the PKA (all registers) +#define CFG_HW_PKA_SEMID 1 + +// Used to access the RNG (all registers) +#define CFG_HW_RNG_SEMID 0 + #endif // MICROPY_INCLUDED_STM32WBXX_HAL_CONF_BASE_H diff --git a/ports/stm32/powerctrl.c b/ports/stm32/powerctrl.c index 2f5e78f346..3439b0d1fa 100644 --- a/ports/stm32/powerctrl.c +++ b/ports/stm32/powerctrl.c @@ -676,6 +676,58 @@ int powerctrl_set_sysclk(uint32_t sysclk, uint32_t ahb, uint32_t apb1, uint32_t return 0; } +static void powerctrl_switch_on_HSI(void) { + LL_RCC_HSI_Enable(); + while (!LL_RCC_HSI_IsReady()) { + } + LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_HSI); + LL_RCC_SetSMPSClockSource(LL_RCC_SMPS_CLKSOURCE_HSI); + while (LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_HSI) { + } + return; +} + +static void powerctrl_low_power_prep_wb55() { + // See WB55 specific documentation in AN5289 Rev 6, and in particular, Figure 6. + while (LL_HSEM_1StepLock(HSEM, CFG_HW_RCC_SEMID)) { + } + if (!LL_HSEM_1StepLock(HSEM, CFG_HW_ENTRY_STOP_MODE_SEMID)) { + if (LL_PWR_IsActiveFlag_C2DS() || LL_PWR_IsActiveFlag_C2SB()) { + // Release ENTRY_STOP_MODE semaphore + LL_HSEM_ReleaseLock(HSEM, CFG_HW_ENTRY_STOP_MODE_SEMID, 0); + + powerctrl_switch_on_HSI(); + } + } else { + powerctrl_switch_on_HSI(); + } + // Release RCC semaphore + LL_HSEM_ReleaseLock(HSEM, CFG_HW_RCC_SEMID, 0); +} + +static void powerctrl_low_power_exit_wb55() { + // Ensure the HSE/HSI clock configuration is correct so core2 can wake properly again. + // See WB55 specific documentation in AN5289 Rev 6, and in particular, Figure 7. + LL_HSEM_ReleaseLock(HSEM, CFG_HW_ENTRY_STOP_MODE_SEMID, 0); + // Acquire RCC semaphore before adjusting clocks. + while (LL_HSEM_1StepLock(HSEM, CFG_HW_RCC_SEMID)) { + } + + if (LL_RCC_GetSysClkSource() == LL_RCC_SYS_CLKSOURCE_STATUS_HSI) { + // Restore the clock configuration of the application + LL_RCC_HSE_Enable(); + __HAL_FLASH_SET_LATENCY(FLASH_LATENCY_1); + while (!LL_RCC_HSE_IsReady()) { + } + LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_HSE); + while (LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_HSE) { + } + } + + // Release RCC semaphore + LL_HSEM_ReleaseLock(HSEM, CFG_HW_RCC_SEMID, 0); +} + #endif #endif // !defined(STM32F0) && !defined(STM32L0) && !defined(STM32L4) @@ -729,6 +781,10 @@ void powerctrl_enter_stop_mode(void) { } #endif + #if defined(STM32WB) + powerctrl_low_power_prep_wb55(); + #endif + #if defined(STM32F7) HAL_PWR_EnterSTOPMode((PWR_CR1_LPDS | PWR_CR1_LPUDS | PWR_CR1_FPDS | PWR_CR1_UDEN), PWR_STOPENTRY_WFI); #else @@ -762,6 +818,10 @@ void powerctrl_enter_stop_mode(void) { } #endif + #if defined(STM32WB) + powerctrl_low_power_exit_wb55(); + #endif + #if !defined(STM32L4) // enable clock __HAL_RCC_HSE_CONFIG(MICROPY_HW_RCC_HSE_STATE); @@ -977,6 +1037,10 @@ void powerctrl_enter_standby_mode(void) { DBGMCU->CR = 0; #endif + #if defined(STM32WB) + powerctrl_low_power_prep_wb55(); + #endif + // enter standby mode HAL_PWR_EnterSTANDBYMode(); // we never return; MCU is reset on exit from standby diff --git a/ports/stm32/powerctrlboot.c b/ports/stm32/powerctrlboot.c index 61d48ffe5c..b78fcae4b4 100644 --- a/ports/stm32/powerctrlboot.c +++ b/ports/stm32/powerctrlboot.c @@ -298,21 +298,20 @@ void SystemClock_Config(void) { } #elif defined(STM32WB) -#include "stm32wbxx_ll_hsem.h" - -// This semaphore protected access to the CLK48 configuration. -// CPU1 should hold this semaphore while the USB peripheral is in use. -// See AN5289 and https://github.com/micropython/micropython/issues/6316. -#define CLK48_SEMID (5) - void SystemClock_Config(void) { + while (LL_HSEM_1StepLock(HSEM, CFG_HW_RCC_SEMID)) { + } + // Enable the 32MHz external oscillator RCC->CR |= RCC_CR_HSEON; while (!(RCC->CR & RCC_CR_HSERDY)) { } // Prevent CPU2 from disabling CLK48. - while (LL_HSEM_1StepLock(HSEM, CLK48_SEMID)) { + // This semaphore protected access to the CLK48 configuration. + // CPU1 should hold this semaphore while the USB peripheral is in use. + // See AN5289 and https://github.com/micropython/micropython/issues/6316. + while (LL_HSEM_1StepLock(HSEM, CFG_HW_CLK48_CONFIG_SEMID)) { } // Use HSE and the PLL to get a 64MHz SYSCLK @@ -349,6 +348,9 @@ void SystemClock_Config(void) { SystemCoreClockUpdate(); powerctrl_config_systick(); + + // Release RCC semaphore + LL_HSEM_ReleaseLock(HSEM, CFG_HW_RCC_SEMID, 0); } #elif defined(STM32WL) diff --git a/ports/stm32/rfcore.c b/ports/stm32/rfcore.c index 55d6d17ad1..20b2c5a7e6 100644 --- a/ports/stm32/rfcore.c +++ b/ports/stm32/rfcore.c @@ -534,12 +534,23 @@ void rfcore_init(void) { // Ensure LSE is running rtc_init_finalise(); + // In case we're waking from deepsleep, enforce core synchronisation + __HAL_RCC_HSEM_CLK_ENABLE(); + while (LL_HSEM_1StepLock(HSEM, CFG_HW_PWR_STANDBY_SEMID)) { + } + // Select LSE as RF wakeup source RCC->CSR = (RCC->CSR & ~RCC_CSR_RFWKPSEL) | 1 << RCC_CSR_RFWKPSEL_Pos; // Initialise IPCC and shared memory structures ipcc_init(IRQ_PRI_SDIO); + // When the device is out of standby, it is required to use the EXTI mechanism to wakeup CPU2 + LL_C2_EXTI_EnableEvent_32_63(LL_EXTI_LINE_41); + LL_EXTI_EnableRisingTrig_32_63(LL_EXTI_LINE_41); + + LL_HSEM_ReleaseLock(HSEM, CFG_HW_PWR_STANDBY_SEMID, 0); + // Boot the second core __SEV(); __WFE();