Skip to content

Commit 85318a9

Browse files
HoZHelcfriedt
authored andcommitted
soc: st: stm32: Provide PM support for STM32WB0x
Provide PM support, specifically suspend-to-ram, for STM32WB0x. Enable STM32_RADIO_TIMER Kconfig parameter when PM is set. Signed-off-by: Ali Hozhabri <[email protected]>
1 parent bdb41c0 commit 85318a9

File tree

7 files changed

+290
-2
lines changed

7 files changed

+290
-2
lines changed

drivers/timer/Kconfig.stm32wb0_radio_timer

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
config STM32WB0_RADIO_TIMER
77
bool "STM32 Radio Timer for WB0x"
8-
default y if BT
8+
default y if PM || BT
99
depends on SOC_SERIES_STM32WB0X
1010
select USE_STM32_HAL_RADIO_TIMER
1111
select HAS_STM32LIB

drivers/timer/stm32wb0_radio_timer.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ LOG_MODULE_REGISTER(radio_timer_driver);
3232
BUILD_ASSERT(DT_NODE_HAS_STATUS(DT_NODELABEL(clk_lsi), disabled),
3333
"LSI is not supported yet");
3434

35+
#if (defined(CONFIG_SOC_STM32WB06XX) || defined(CONFIG_SOC_STM32WB06XX)) && defined(CONFIG_PM)
36+
#error "PM is not supported yet for WB06/WB07"
37+
#endif /* (CONFIG_SOC_STM32WB06XX || CONFIG_SOC_STM32WB06XX) && CONFIG_PM */
38+
3539
/* This value is only valid for the LSE with a frequency of 32768 Hz.
3640
* The implementation for the LSI will be done in the future.
3741
*/

dts/arm/st/wb0/stm32wb0.dtsi

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,24 @@
3333
cpu0: cpu@0 {
3434
device_type = "cpu";
3535
compatible = "arm,cortex-m0+";
36+
cpu-power-states = <&deepstop>;
3637
reg = <0>;
3738
};
39+
40+
power-states {
41+
deepstop: state0 {
42+
compatible = "zephyr,power-state";
43+
power-state-name = "suspend-to-ram";
44+
substate-id = <0>;
45+
46+
/* One milisecond seems reasonable for now. */
47+
min-residency-us = <1000>;
48+
49+
/* exit-latency-us property is not needed
50+
* since the same concept is applied by the hardware automatically.
51+
*/
52+
};
53+
};
3854
};
3955

4056
sram0: memory@20000000 {

soc/st/stm32/stm32wb0x/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
zephyr_include_directories(${ZEPHYR_BASE}/drivers)
44

55
zephyr_sources(soc.c)
6+
zephyr_sources_ifdef(CONFIG_PM power.c s2ram_marking.S)
67
zephyr_include_directories(.)
78

89
zephyr_linker_sources(RAM_SECTIONS ram_sections.ld)

soc/st/stm32/stm32wb0x/Kconfig

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# STMicroelectronics STM32W0 MCU series
1+
# STMicroelectronics STM32WB0 MCU series
22

33
# Copyright (c) 2024 STMicroelectronics
44
# SPDX-License-Identifier: Apache-2.0
@@ -9,8 +9,16 @@ config SOC_SERIES_STM32WB0X
99
select CPU_CORTEX_M_HAS_VTOR
1010
select CPU_CORTEX_M_HAS_SYSTICK
1111
select CPU_HAS_ARM_MPU
12+
select HAS_PM
1213
select HAS_STM32CUBE
1314
select SOC_EARLY_INIT_HOOK
1415
# WB0x has a ROM bootloader executed at reset,
1516
# which makes the following option required
1617
select INIT_ARCH_HW_AT_BOOT
18+
# STM32WB0 series only supports suspend-to-RAM as low-power mode.
19+
# Make sure that S2RAM support is enabled if Power Management is
20+
# selected by the user. PM_DEVICE is also required because devices
21+
# need to be reinitialized after resuming. CUSTOM_MARKING is enabled
22+
# to use SoC-specific hardware registers for resume detection.
23+
select PM_DEVICE if PM
24+
select HAS_PM_S2RAM_CUSTOM_MARKING

soc/st/stm32/stm32wb0x/power.c

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
/*
2+
* Copyright (c) 2025 STMicroelectronics.
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
/*
7+
* STM32WB0 Deepstop implementation for Power Management framework
8+
*
9+
* TODO:
10+
* - document the control flow on PM transitions
11+
* - assertions around system configuration
12+
* (e.g., valid slow clock selected, RTC enabled, ...)
13+
* - ...
14+
*/
15+
#include <zephyr/kernel.h>
16+
#include <zephyr/pm/pm.h>
17+
#include <zephyr/sys_clock.h>
18+
#include <zephyr/init.h>
19+
#include <zephyr/arch/common/pm_s2ram.h>
20+
21+
/* Private headers in zephyr/drivers/... */
22+
#include <clock_control/clock_stm32_ll_common.h>
23+
24+
#include <soc.h>
25+
#include <stm32_ll_pwr.h>
26+
#include <stm32_ll_rcc.h>
27+
#include <stm32_ll_cortex.h>
28+
#include <stm32_ll_system.h>
29+
30+
#include <zephyr/logging/log.h>
31+
LOG_MODULE_DECLARE(soc, CONFIG_SOC_LOG_LEVEL);
32+
33+
#if defined(CONFIG_SOC_STM32WB05XX) || defined(CONFIG_SOC_STM32WB09XX)
34+
#define HAS_GPIO_RETENTION 1
35+
#else
36+
#define HAS_GPIO_RETENTION 0
37+
#endif /* CONFIG_SOC_STM32WB05XX || CONFIG_SOC_STM32WB09XX */
38+
39+
/* System-level state managed by PM callbacks
40+
* Things that need to be preserved across Deepstop, but
41+
* have no associated driver to backup and restore them.
42+
*/
43+
#define STM32WB0_SRAM DT_CHOSEN(zephyr_sram)
44+
#define STM32WB0_BL_STACK_SIZE (20 * 4) /* in bytes */
45+
#define STM32WB0_BL_STACK_TOP ((void *)(DT_REG_ADDR(STM32WB0_SRAM) + DT_REG_SIZE(STM32WB0_SRAM) \
46+
- STM32WB0_BL_STACK_SIZE))
47+
48+
static uint8_t bl_stk_area_backup[STM32WB0_BL_STACK_SIZE];
49+
static uint32_t rcc_apb1enr_vr, rcc_ahbenr_vr;
50+
51+
/* Callback for arch_pm_s2ram_suspend */
52+
static int suspend_system_to_deepstop(void)
53+
{
54+
/* Enable SLEEPDEEP to allow entry in Deepstop */
55+
LL_LPM_EnableDeepSleep();
56+
57+
/* Complete all memory transactions */
58+
__DSB();
59+
60+
61+
/* Attempt entry in Deepstop */
62+
__WFI();
63+
64+
/* Make sure no meaningful instruction is
65+
* executed during the two cycles latency
66+
* it takes to power-gate the CPU.
67+
*/
68+
__NOP();
69+
__NOP();
70+
71+
/* This code is reached only if the device did not
72+
* enter Deepstop mode (e.g., because an interrupt
73+
* became pending during preparatory work).
74+
* Disable SLEEPDEEP and return the appropriate error.
75+
*/
76+
LL_LPM_EnableSleep();
77+
78+
return -EBUSY;
79+
}
80+
81+
/* Backup system state to save and configure power
82+
* controller before entry in Deepstop mode
83+
*/
84+
static void prepare_for_deepstop_entry(void)
85+
{
86+
/* DEEPSTOP2 configuration is performed in familiy-wide code
87+
* instead of here (see `soc/st/stm32/common/soc_config.c`).
88+
* RAMRET configuration is performed once during SoC init,
89+
* since it is retained across Deepstop (see `soc.c`).
90+
*/
91+
92+
/* Save the clock configuration. */
93+
rcc_apb1enr_vr = RCC->APB1ENR;
94+
rcc_ahbenr_vr = RCC->AHBENR;
95+
96+
/* Clear wakeup reason flags (which inhibit Deepstop) */
97+
LL_PWR_ClearWakeupSource(LL_PWR_WAKEUP_ALL);
98+
LL_SYSCFG_PWRC_ClearIT(LL_SYSCFG_PWRC_WKUP);
99+
LL_PWR_ClearDeepstopSeqFlag();
100+
LL_PWR_EnableWU_EWBLEHCPU();
101+
102+
#if HAS_GPIO_RETENTION
103+
/* Enable GPIO state retention in Deepstop if available.
104+
* Do not enable this if low-power mode debugging has been
105+
* enabled via Kconfig, because it prevents the debugger
106+
* from staying connected to the SoC.
107+
*/
108+
if (!IS_ENABLED(CONFIG_STM32_ENABLE_DEBUG_SLEEP_STOP)) {
109+
LL_PWR_EnableGPIORET();
110+
LL_PWR_EnableDBGRET();
111+
}
112+
#endif /* HAS_GPIO_RETENTION */
113+
114+
/* The STM32WB0 bootloader uses the end of SRAM as stack.
115+
* Since it is executed on every reset, including wakeup
116+
* from Deepstop, any data placed at the end of SRAM would
117+
* be corrupted.
118+
* Backup these words for later restoration to avoid data
119+
* corruption. A much better solution would be to mark this part
120+
* of SRAM as unusable, but no easy solution was found to
121+
* achieve this.
122+
*/
123+
memcpy(bl_stk_area_backup, STM32WB0_BL_STACK_TOP, STM32WB0_BL_STACK_SIZE);
124+
}
125+
126+
/* Restore SoC-level configuration lost in Deepstop
127+
* This function must be called right after wakeup.
128+
*/
129+
static void post_resume_configuration(void)
130+
{
131+
__ASSERT_NO_MSG(LL_PWR_GetDeepstopSeqFlag() == 1);
132+
133+
/* VTOR has been reset to its default value: restore it.
134+
* (Note that RAM_VR.AppBase was filled during SoC init)
135+
*/
136+
SCB->VTOR = RAM_VR.AppBase;
137+
138+
/* Restore the clock configuration. */
139+
RCC->AHBENR = rcc_ahbenr_vr;
140+
RCC->APB1ENR = rcc_apb1enr_vr;
141+
142+
/* Restore bootloader stack area */
143+
memcpy(STM32WB0_BL_STACK_TOP, bl_stk_area_backup, STM32WB0_BL_STACK_SIZE);
144+
}
145+
146+
/* Power Management subsystem callbacks */
147+
void pm_state_set(enum pm_state state, uint8_t substate_id)
148+
{
149+
/* Ignore substate: STM32WB0 has only one low-power mode */
150+
ARG_UNUSED(substate_id);
151+
152+
if (state != PM_STATE_SUSPEND_TO_RAM) {
153+
154+
/* Deepstop is a suspend-to-RAM state.
155+
* Something is wrong if a different
156+
* power state has been requested.
157+
*/
158+
LOG_ERR("Unsupported power state %u", state);
159+
}
160+
161+
prepare_for_deepstop_entry();
162+
163+
/* Select Deepstop low-power mode and suspend system */
164+
LL_PWR_SetPowerMode(LL_PWR_MODE_DEEPSTOP);
165+
166+
if (arch_pm_s2ram_suspend(suspend_system_to_deepstop) >= 0) {
167+
168+
/* Restore system configuration only if the SoC actually
169+
* entered Deepstop - otherwise, no state has been lost
170+
* and it would be a waste of time to do so.
171+
*/
172+
post_resume_configuration();
173+
}
174+
}
175+
176+
void pm_state_exit_post_ops(enum pm_state state, uint8_t substate_id)
177+
{
178+
ARG_UNUSED(state);
179+
ARG_UNUSED(substate_id);
180+
181+
/* We restore system state in @ref{post_resume_configuration}.
182+
* The only thing we may have to do is release GPIO retention,
183+
* which we have not done yet because we wanted the driver to
184+
* restore all configuration first.
185+
* We also need to enable IRQs to fullfill the API contract.
186+
*/
187+
#if HAS_GPIO_RETENTION
188+
LL_PWR_DisableGPIORET();
189+
LL_PWR_DisableDBGRET();
190+
#endif /* HAS_GPIO_RETENTION */
191+
192+
__enable_irq();
193+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
* Copyright (c) 2025 STMicroelectronics
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
/**
8+
* STM32WB0-specific support code for suspend-to-RAM
9+
*/
10+
11+
#include <zephyr/toolchain.h>
12+
#include <offsets_short.h>
13+
#include <zephyr/arch/cpu.h>
14+
#include <zephyr/arch/common/pm_s2ram.h>
15+
16+
/* Read RCC and PWRC base from Device Tree */
17+
#include <zephyr/devicetree.h>
18+
19+
#define STM32WB0_RCC_CSR (DT_REG_ADDR(DT_NODELABEL(rcc)) + 0x94)
20+
#define STM32WB0_PWR_BASE DT_REG_ADDR(DT_NODELABEL(pwrc))
21+
#define STM32WB0_PWR_SR1 0x10
22+
#define STM32WB0_PWR_SR3 0x38
23+
24+
_ASM_FILE_PROLOGUE
25+
26+
GTEXT(pm_s2ram_mark_set)
27+
SECTION_FUNC(TEXT, pm_s2ram_mark_set)
28+
/*
29+
* Managed by hardware - nothing to do here.
30+
*/
31+
bx lr
32+
33+
/**
34+
* @brief Check whether SoC is waking up from Deepstop
35+
* @returns 1 if SoC is waking up from Deepstop, 0 otherwise.
36+
* @note Registers are cleared by hardware upon reset, or
37+
* the SoC PM code layer upon entry in Deepstop, so
38+
* this function does not clear the registers.
39+
*/
40+
GTEXT(pm_s2ram_mark_check_and_clear)
41+
SECTION_FUNC(TEXT, pm_s2ram_mark_check_and_clear)
42+
/*
43+
* Check for Deepstop exit on wakeup event:
44+
* - RCC_CSR is zero
45+
* - PWRC.EXTSRR has bit DEEPSTOPF set
46+
* (optional; RCC_CSR check suffices)
47+
* - Either PWRC_SR1 or PWRC_SR3 is non-zero
48+
*
49+
* Note that we don't have to clear any register since
50+
* they are automatically updated on reset/wake-up.
51+
*/
52+
ldr r0, =STM32WB0_RCC_CSR
53+
ldr r0, [r0]
54+
cmp r0, #0
55+
bne not_deepstop_wakeup
56+
ldr r0, =STM32WB0_PWR_BASE
57+
ldr r1, [r0, #STM32WB0_PWR_SR1]
58+
ldr r0, [r0, #STM32WB0_PWR_SR3]
59+
orrs r0, r0, r1
60+
beq not_deepstop_wakeup
61+
/* All conditions met: this is a wakeup from Deepstop. */
62+
movs r0, #1
63+
bx lr
64+
not_deepstop_wakeup:
65+
movs r0, #0
66+
bx lr

0 commit comments

Comments
 (0)