diff --git a/drivers/counter/counter_ll_stm32_rtc.c b/drivers/counter/counter_ll_stm32_rtc.c index cab26b1eeea..754efade348 100644 --- a/drivers/counter/counter_ll_stm32_rtc.c +++ b/drivers/counter/counter_ll_stm32_rtc.c @@ -185,7 +185,7 @@ static void rtc_stm32_irq_config(const struct device *dev); static int rtc_stm32_start(const struct device *dev) { -#if defined(CONFIG_SOC_SERIES_STM32WBAX) +#if defined(CONFIG_SOC_SERIES_STM32WBAX) || defined(CONFIG_SOC_SERIES_STM32U5X) const struct device *const clk = DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE); const struct rtc_stm32_config *cfg = dev->config; @@ -208,7 +208,7 @@ static int rtc_stm32_start(const struct device *dev) static int rtc_stm32_stop(const struct device *dev) { -#if defined(CONFIG_SOC_SERIES_STM32WBAX) +#if defined(CONFIG_SOC_SERIES_STM32WBAX) || defined(CONFIG_SOC_SERIES_STM32U5X) const struct device *const clk = DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE); const struct rtc_stm32_config *cfg = dev->config; diff --git a/drivers/timer/Kconfig.stm32_lptim b/drivers/timer/Kconfig.stm32_lptim index 31bddc07409..f8f89740bee 100644 --- a/drivers/timer/Kconfig.stm32_lptim +++ b/drivers/timer/Kconfig.stm32_lptim @@ -59,16 +59,18 @@ config STM32_LPTIM_TICK_FREQ_RATIO_OVERRIDE This options allows to override this check config STM32_LPTIM_STDBY_TIMER - bool "Use an additional timer while entering Standby mode" + bool default $(dt_chosen_enabled,$(DT_CHOSEN_STDBY_TIMER)) depends on COUNTER depends on TICKLESS_KERNEL select EXPERIMENTAL help + Use an additional timer while entering Standby mode. There are chips e.g. STM32WBAX family that use LPTIM as a system timer, but LPTIM is not clocked in standby mode. These chips usually have another timer that is not stopped, but it has lower frequency e.g. RTC, thus it can't be used as a main system timer. + Same approach is used on STM32U5 and STOP3 mode. Use the Standby timer for timeout (wakeup) when the system is entering Standby state. diff --git a/drivers/timer/stm32_lptim_timer.c b/drivers/timer/stm32_lptim_timer.c index f0b234f0997..c12b97007ba 100644 --- a/drivers/timer/stm32_lptim_timer.c +++ b/drivers/timer/stm32_lptim_timer.c @@ -204,7 +204,22 @@ void sys_clock_set_timeout(int32_t ticks, bool idle) next = pm_policy_next_state(CURRENT_CPU, ticks); - if ((next != NULL) && idle && (next->state == PM_STATE_SUSPEND_TO_RAM)) { + /* Check if STANBY or STOP3 is requested */ + timeout_stdby = false; + if ((next != NULL) && idle) { +#ifdef CONFIG_PM_S2RAM + if (next->state == PM_STATE_SUSPEND_TO_RAM) { + timeout_stdby = true; + } +#endif +#ifdef CONFIG_STM32_STOP3_LP_MODE + if ((next->state == PM_STATE_SUSPEND_TO_IDLE) && (next->substate_id == 4)) { + timeout_stdby = true; + } +#endif + } + + if (timeout_stdby) { uint64_t timeout_us = ((uint64_t)ticks * USEC_PER_SEC) / CONFIG_SYS_CLOCK_TICKS_PER_SEC; @@ -215,8 +230,6 @@ void sys_clock_set_timeout(int32_t ticks, bool idle) .flags = 0, }; - timeout_stdby = true; - /* Set the alarm using timer that runs the standby. * Needed rump-up/setting time, lower accurency etc. should be * included in the exit-latency in the power state definition. diff --git a/dts/arm/st/u5/stm32u5.dtsi b/dts/arm/st/u5/stm32u5.dtsi index 4a7f2a1fe43..3b2d7e87d5f 100644 --- a/dts/arm/st/u5/stm32u5.dtsi +++ b/dts/arm/st/u5/stm32u5.dtsi @@ -65,6 +65,13 @@ substate-id = <3>; min-residency-us = <900>; }; + /omit-if-no-ref/ stop3: state3 { + compatible = "zephyr,power-state"; + power-state-name = "suspend-to-idle"; + substate-id = <4>; + min-residency-us = <200000>; + exit-latency-us = <130>; + }; }; }; diff --git a/soc/st/stm32/stm32u5x/Kconfig b/soc/st/stm32/stm32u5x/Kconfig index a2e4145abc3..8408bc1c4d2 100644 --- a/soc/st/stm32/stm32u5x/Kconfig +++ b/soc/st/stm32/stm32u5x/Kconfig @@ -3,6 +3,8 @@ # Copyright (c) 2021 Linaro Limited # SPDX-License-Identifier: Apache-2.0 +DT_CHOSEN_STDBY_TIMER := st,lptim-stdby-timer + config SOC_SERIES_STM32U5X select ARM select CPU_CORTEX_M33 @@ -15,3 +17,16 @@ config SOC_SERIES_STM32U5X select HAS_STM32CUBE select HAS_PM select HAS_POWEROFF + +config STM32_STOP3_LP_MODE + bool + default $(dt_path_enabled,/cpus/power-states/state3) && $(dt_chosen_enabled,$(DT_CHOSEN_STDBY_TIMER)) + help + Enable support for STM32 STOP3 low-power mode. + Based on the Cortex-M33 Deepsleep mode combined with peripheral clock gating. + All clocks in the core domain are stopped. + The PLL, MSIS, MSIK, HSI16, and HSE oscillators are disabled. + All SRAMs and register contents are preserved. + SRAMs can be totally or partially switched off to further reduce consumption. + GPIOs are left floating and additional pull-up or pull-down can be applied + via PWR registers. diff --git a/soc/st/stm32/stm32u5x/Kconfig.defconfig b/soc/st/stm32/stm32u5x/Kconfig.defconfig index 354cc6cd0ab..61771d8951e 100644 --- a/soc/st/stm32/stm32u5x/Kconfig.defconfig +++ b/soc/st/stm32/stm32u5x/Kconfig.defconfig @@ -10,4 +10,26 @@ rsource "Kconfig.defconfig.stm32u5*" config ROM_START_OFFSET default 0x400 if BOOTLOADER_MCUBOOT +if STM32_STOP3_LP_MODE + +config COUNTER + default y + +config COUNTER_RTC_STM32_SUBSECONDS + default y + +config STM32_LPTIM_STDBY_TIMER + default y + +config TICKLESS_KERNEL + default y + +config COUNTER_RTC_STM32_SAVE_VALUE_BETWEEN_RESETS + default y + +config IDLE_STACK_SIZE + default 512 + +endif #STM32_USE_STOP3 + endif # SOC_SERIES_STM32U5X diff --git a/soc/st/stm32/stm32u5x/power.c b/soc/st/stm32/stm32u5x/power.c index a3214f93d96..076c33f9170 100644 --- a/soc/st/stm32/stm32u5x/power.c +++ b/soc/st/stm32/stm32u5x/power.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -26,6 +27,32 @@ LOG_MODULE_DECLARE(soc, CONFIG_SOC_LOG_LEVEL); #define RCC_STOP_WAKEUPCLOCK_SELECTED LL_RCC_STOP_WAKEUPCLOCK_HSI #endif +#ifdef CONFIG_STM32_STOP3_LP_MODE +static void pwr_stop3_isr(const struct device *dev) +{ + ARG_UNUSED(dev); + + /* Clear all wake-up flags */ + LL_PWR_ClearFlag_WU(); +} + +static void disable_cache(void) +{ + /* Disabling ICACHE */ + LL_ICACHE_Disable(); + while (LL_ICACHE_IsEnabled() == 1U) { + } + + /* Wait until ICACHE_SR.BUSYF is cleared */ + while (LL_ICACHE_IsActiveFlag_BUSY() == 1U) { + } + + /* Wait until ICACHE_SR.BSYENDF is set */ + while (LL_ICACHE_IsActiveFlag_BSYEND() == 0U) { + } +} +#endif + void set_mode_stop(uint8_t substate_id) { /* ensure the proper wake-up system clock */ @@ -41,6 +68,25 @@ void set_mode_stop(uint8_t substate_id) case 3: /* enter STOP2 mode */ LL_PWR_SetPowerMode(LL_PWR_STOP2_MODE); break; +#ifdef CONFIG_STM32_STOP3_LP_MODE + case 4: /* enter STOP3 mode */ + + LL_PWR_SetSRAM2SBRetention(LL_PWR_SRAM2_SB_FULL_RETENTION); + /* Enable RTC wakeup + * This configures an internal pin that generates an event to wakeup the system + */ + LL_PWR_EnableWakeUpPin(LL_PWR_WAKEUP_PIN7); + LL_PWR_SetWakeUpPinSignal3Selection(LL_PWR_WAKEUP_PIN7); + + /* Clear flags */ + LL_PWR_ClearFlag_SB(); + LL_PWR_ClearFlag_WU(); + + disable_cache(); + + LL_PWR_SetPowerMode(LL_PWR_STOP3_MODE); + break; +#endif default: LOG_DBG("Unsupported power state substate-id %u", substate_id); break; @@ -85,6 +131,18 @@ void pm_state_exit_post_ops(enum pm_state state, uint8_t substate_id) if (substate_id <= 3) { LL_LPM_DisableSleepOnExit(); LL_LPM_EnableSleep(); +#ifdef CONFIG_STM32_STOP3_LP_MODE + } else if (substate_id == 4) { + stm32_clock_control_standby_exit(); + + LL_ICACHE_SetMode(LL_ICACHE_1WAY); + LL_ICACHE_Enable(); + while (LL_ICACHE_IsEnabled() == 0U) { + } + + LL_LPM_DisableSleepOnExit(); + LL_LPM_EnableSleep(); +#endif } else { LOG_DBG("Unsupported power substate-id %u", substate_id); @@ -118,6 +176,12 @@ static int stm32_power_init(void) /* enable Power clock */ LL_AHB3_GRP1_EnableClock(LL_AHB3_GRP1_PERIPH_PWR); +#ifdef CONFIG_STM32_STOP3_LP_MODE + IRQ_CONNECT(PWR_S3WU_IRQn, 0, + pwr_stop3_isr, 0, 0); + irq_enable(PWR_S3WU_IRQn); +#endif + return 0; }