From 43ef398614a765f04271680bbc46511bfbc71dba Mon Sep 17 00:00:00 2001 From: Dawid Niedzwiecki Date: Tue, 7 Nov 2023 08:49:45 +0100 Subject: [PATCH] pm: add power management for stm32f4x Add soc power management for the STM32F4x chips. One low power state is added supported by all chips from the family - the Stop mode with voltage regulator in low-power mode. The Stop mode for STM32F chips has to work with the IDLE timer - CORTEX_M_SYSTICK_IDLE_TIMER, because PLL and HSI are disabled in the Stop mode (Systick is not clocked). The only possible wakeup source is RTC, which works as a IDLE timer for the Systick. The exit latency may need to be adjusted per system, depending on the system tick frequency and other variables. Signed-off-by: Dawid Niedzwiecki --- dts/arm/st/f4/stm32f4.dtsi | 18 +++- soc/arm/st_stm32/stm32f4/CMakeLists.txt | 4 + .../st_stm32/stm32f4/Kconfig.defconfig.series | 4 + soc/arm/st_stm32/stm32f4/Kconfig.series | 1 + soc/arm/st_stm32/stm32f4/power.c | 89 +++++++++++++++++++ 5 files changed, 115 insertions(+), 1 deletion(-) create mode 100644 soc/arm/st_stm32/stm32f4/power.c diff --git a/dts/arm/st/f4/stm32f4.dtsi b/dts/arm/st/f4/stm32f4.dtsi index 36cbd9c8fed..f53c9cdf228 100644 --- a/dts/arm/st/f4/stm32f4.dtsi +++ b/dts/arm/st/f4/stm32f4.dtsi @@ -28,10 +28,26 @@ #address-cells = <1>; #size-cells = <0>; - cpu@0 { + cpu0: cpu@0 { device_type = "cpu"; compatible = "arm,cortex-m4f"; reg = <0>; + cpu-power-states = <&stop>; + }; + + power-states { + stop: stop { + compatible = "zephyr,power-state"; + power-state-name = "suspend-to-idle"; + /* It is really hard to establish these numbers precisely. + * We are basing on RTC as a wakeup source with 62,5us tick. + * It requires a proper margin. Additionally, sys_clock_announce + * works within system tick boundaries (100us by default), + * which also introduces some shift. + */ + min-residency-us = <400>; + exit-latency-us = <300>; + }; }; }; diff --git a/soc/arm/st_stm32/stm32f4/CMakeLists.txt b/soc/arm/st_stm32/stm32f4/CMakeLists.txt index e02052e3946..021708b9d02 100644 --- a/soc/arm/st_stm32/stm32f4/CMakeLists.txt +++ b/soc/arm/st_stm32/stm32f4/CMakeLists.txt @@ -6,3 +6,7 @@ zephyr_sources( ) set(SOC_LINKER_SCRIPT ${ZEPHYR_BASE}/include/zephyr/arch/arm/cortex_m/scripts/linker.ld CACHE INTERNAL "") + +zephyr_sources_ifdef(CONFIG_PM + power.c + ) diff --git a/soc/arm/st_stm32/stm32f4/Kconfig.defconfig.series b/soc/arm/st_stm32/stm32f4/Kconfig.defconfig.series index 14dea5bf4d2..28ed9cc3a55 100644 --- a/soc/arm/st_stm32/stm32f4/Kconfig.defconfig.series +++ b/soc/arm/st_stm32/stm32f4/Kconfig.defconfig.series @@ -17,4 +17,8 @@ config TASK_WDT_HW_FALLBACK_DELAY depends on TASK_WDT_HW_FALLBACK default 200 +config PM + select COUNTER + select COUNTER_RTC_STM32_SUBSECONDS + endif # SOC_SERIES_STM32F4X diff --git a/soc/arm/st_stm32/stm32f4/Kconfig.series b/soc/arm/st_stm32/stm32f4/Kconfig.series index 6b8fdf80c7b..a4e65c97784 100644 --- a/soc/arm/st_stm32/stm32f4/Kconfig.series +++ b/soc/arm/st_stm32/stm32f4/Kconfig.series @@ -13,5 +13,6 @@ config SOC_SERIES_STM32F4X select HAS_STM32CUBE select CPU_HAS_ARM_MPU select HAS_SWO + select HAS_PM help Enable support for STM32F4 MCU series diff --git a/soc/arm/st_stm32/stm32f4/power.c b/soc/arm/st_stm32/stm32f4/power.c new file mode 100644 index 00000000000..02b645ee9f1 --- /dev/null +++ b/soc/arm/st_stm32/stm32f4/power.c @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2023 Google LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +LOG_MODULE_DECLARE(soc, CONFIG_SOC_LOG_LEVEL); + +BUILD_ASSERT(DT_SAME_NODE(DT_CHOSEN(zephyr_cortex_m_idle_timer), DT_NODELABEL(rtc)), + "STM32Fx series needs RTC as an additional IDLE timer for power management"); + +void pm_state_set(enum pm_state state, uint8_t substate_id) +{ + ARG_UNUSED(substate_id); + + switch (state) { + case PM_STATE_SUSPEND_TO_IDLE: + LL_LPM_DisableEventOnPend(); + LL_PWR_ClearFlag_WU(); + /* According to datasheet (DS11139 Rev 8,Table 38.), wakeup with regulator in + * low-power mode takes typically 8us, max 13us more time than with the main + * regulator. We are using RTC as a wakeup source, which has a tick 62,5us. + * It means we have to add significant margin to the exit-latency anyway, + * so it is worth always using the low-power regulator. + */ + LL_PWR_SetPowerMode(LL_PWR_MODE_STOP_LPREGU); + LL_LPM_EnableDeepSleep(); + + k_cpu_idle(); + + break; + default: + LOG_DBG("Unsupported power state %u", state); + break; + } +} + +void pm_state_exit_post_ops(enum pm_state state, uint8_t substate_id) +{ + ARG_UNUSED(substate_id); + + switch (state) { + case PM_STATE_SUSPEND_TO_IDLE: + LL_LPM_DisableSleepOnExit(); + LL_LPM_EnableSleep(); + + /* Restore the clock setup. */ + stm32_clock_control_init(NULL); + break; + default: + LOG_DBG("Unsupported power substate-id %u", state); + break; + } + + /* + * System is now in active mode. Reenable interrupts which were + * disabled when OS started idling code. + */ + irq_unlock(0); +} + +static int stm32_power_init(void) +{ + /* Enable Power clock. It should by done by default, but make sure to + * enable it for all STM32F4x chips. + */ + LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_PWR); + + /* Enabling debug during STOP mode is done by the common STM32 configuration */ + return 0; +} + +SYS_INIT(stm32_power_init, PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);