stm32/powerctrl: Add sleep RCC semaphore management for WB55 MCUs.

Signed-off-by: Andrew Leech <andrew.leech@planetinnovation.com.au>
This commit is contained in:
Andrew Leech 2022-03-23 17:44:10 +11:00 committed by Damien George
parent 2ec101e609
commit 2eca86e8fa
4 changed files with 123 additions and 8 deletions

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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();