1 /*
2 * Copyright (c) 2022 STMicroelectronics
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6 #include <zephyr/kernel.h>
7 #include <zephyr/pm/pm.h>
8 #include <soc.h>
9 #include <zephyr/init.h>
10 #include <zephyr/arch/common/pm_s2ram.h>
11 #include <zephyr/drivers/timer/system_timer.h>
12
13 #include <stm32wbaxx_ll_bus.h>
14 #include <stm32wbaxx_ll_cortex.h>
15 #include <stm32wbaxx_ll_pwr.h>
16 #include <stm32wbaxx_ll_icache.h>
17 #include <stm32wbaxx_ll_rcc.h>
18 #include <stm32wbaxx_ll_system.h>
19 #include <clock_control/clock_stm32_ll_common.h>
20
21 #ifdef CONFIG_BT_STM32WBA
22 #include "scm.h"
23 #endif
24
25 #include <zephyr/logging/log.h>
26
27 LOG_MODULE_DECLARE(soc, CONFIG_SOC_LOG_LEVEL);
28
29 void stm32_power_init(void);
30
disable_cache(void)31 static void disable_cache(void)
32 {
33 /* Disabling ICACHE */
34 LL_ICACHE_Disable();
35 while (LL_ICACHE_IsEnabled() == 1U) {
36 }
37
38 /* Wait until ICACHE_SR.BUSYF is cleared */
39 while (LL_ICACHE_IsActiveFlag_BUSY() == 1U) {
40 }
41
42 /* Wait until ICACHE_SR.BSYENDF is set */
43 while (LL_ICACHE_IsActiveFlag_BSYEND() == 0U) {
44 }
45 }
46
set_mode_stop(uint8_t substate_id)47 static void set_mode_stop(uint8_t substate_id)
48 {
49
50 LL_PWR_ClearFlag_STOP();
51 LL_RCC_ClearResetFlags();
52
53 /* Erratum 2.2.15:
54 * Disabling ICACHE is required before entering stop mode
55 */
56 disable_cache();
57
58 #ifdef CONFIG_BT_STM32WBA
59 scm_setwaitstates(LP);
60 #endif
61 /* Set SLEEPDEEP bit of Cortex System Control Register */
62 LL_LPM_EnableDeepSleep();
63
64 while (LL_PWR_IsActiveFlag_ACTVOS() == 0) {
65 }
66
67 switch (substate_id) {
68 case 1: /* enter STOP0 mode */
69 LL_PWR_SetPowerMode(LL_PWR_MODE_STOP0);
70 break;
71 case 2: /* enter STOP1 mode */
72 LL_PWR_SetPowerMode(LL_PWR_MODE_STOP1);
73 break;
74 default:
75 LOG_DBG("Unsupported power state substate-id %u", substate_id);
76 break;
77 }
78 }
79
80 #if defined(CONFIG_PM_S2RAM)
suspend_to_ram(void)81 static int suspend_to_ram(void)
82 {
83 LL_LPM_EnableDeepSleep();
84
85 while (LL_PWR_IsActiveFlag_ACTVOS() == 0) {
86 }
87
88 /* Select mode entry : WFE or WFI and enter the CPU selected mode */
89 k_cpu_idle();
90
91 return 0;
92 }
93
set_mode_suspend_to_ram(void)94 static void set_mode_suspend_to_ram(void)
95 {
96 /* Enable SRAM full retention */
97 LL_PWR_SetSRAM1SBRetention(LL_PWR_SRAM1_SB_FULL_RETENTION);
98 LL_PWR_SetSRAM2SBRetention(LL_PWR_SRAM2_SB_FULL_RETENTION);
99
100 /* Enable RTC wakeup
101 * This configures an internal pin that generates an event to wakeup the system
102 */
103 LL_PWR_EnableWakeUpPin(LL_PWR_WAKEUP_PIN7);
104 LL_PWR_SetWakeUpPinSignal3Selection(LL_PWR_WAKEUP_PIN7);
105
106 /* Clear flags */
107 LL_PWR_ClearFlag_SB();
108 LL_PWR_ClearFlag_WU();
109 LL_RCC_ClearResetFlags();
110
111 disable_cache();
112
113 /* Select standby mode */
114 LL_PWR_SetPowerMode(LL_PWR_MODE_STANDBY);
115
116 /* Save context and enter Standby mode */
117 arch_pm_s2ram_suspend(suspend_to_ram);
118
119 /* Execution is restored at this point after wake up */
120 /* Restore system clock as soon as we exit standby mode */
121 stm32_clock_control_standby_exit();
122 }
123 #endif
124
125 /* Invoke Low Power/System Off specific Tasks */
pm_state_set(enum pm_state state,uint8_t substate_id)126 void pm_state_set(enum pm_state state, uint8_t substate_id)
127 {
128 switch (state) {
129 case PM_STATE_SUSPEND_TO_IDLE:
130 set_mode_stop(substate_id);
131
132 /* Select mode entry : WFE or WFI and enter the CPU selected mode */
133 k_cpu_idle();
134
135 break;
136 #if defined(CONFIG_PM_S2RAM)
137 case PM_STATE_SUSPEND_TO_RAM:
138 set_mode_suspend_to_ram();
139 break;
140 #endif
141 default:
142 LOG_DBG("Unsupported power state %u", state);
143 return;
144 }
145 }
146
147 /* Handle SOC specific activity after Low Power Mode Exit */
pm_state_exit_post_ops(enum pm_state state,uint8_t substate_id)148 void pm_state_exit_post_ops(enum pm_state state, uint8_t substate_id)
149 {
150 #ifdef CONFIG_BT_STM32WBA
151 if (LL_PWR_IsActiveFlag_STOP() == 1U) {
152 scm_setup();
153 } else {
154 scm_setwaitstates(RUN);
155 }
156 #endif
157
158 switch (state) {
159 case PM_STATE_SUSPEND_TO_IDLE:
160 if (substate_id <= 2) {
161 /* Erratum 2.2.15:
162 * Enable ICACHE when exiting stop mode
163 */
164 LL_ICACHE_SetMode(LL_ICACHE_1WAY);
165 LL_ICACHE_Enable();
166 while (LL_ICACHE_IsEnabled() == 0U) {
167 }
168
169 LL_LPM_DisableSleepOnExit();
170 LL_LPM_EnableSleep();
171 } else {
172 LOG_DBG("Unsupported power substate-id %u",
173 substate_id);
174 }
175 break;
176 case PM_STATE_SUSPEND_TO_RAM:
177 #if defined(CONFIG_PM_S2RAM)
178 stm32wba_init();
179 stm32_power_init();
180
181 LL_LPM_DisableSleepOnExit();
182 LL_LPM_EnableSleep();
183 #else
184 LOG_DBG("Suspend to RAM needs CONFIG_PM_S2RAM to be enabled");
185 #endif
186 break;
187 case PM_STATE_STANDBY:
188 __fallthrough;
189 case PM_STATE_SUSPEND_TO_DISK:
190 __fallthrough;
191 default:
192 LOG_DBG("Unsupported power state %u", state);
193 break;
194 }
195
196 /* When BLE is enabled, clock restoration is performed by SCM */
197 #if !defined(CONFIG_BT_STM32WBA)
198 stm32_clock_control_init(NULL);
199 #endif
200
201 /*
202 * System is now in active mode.
203 * Reenable interrupts which were disabled
204 * when OS started idling code.
205 */
206 irq_unlock(0);
207 }
208
209 /* Initialize STM32 Power */
stm32_power_init(void)210 void stm32_power_init(void)
211 {
212
213 #ifdef CONFIG_BT_STM32WBA
214 scm_init();
215 #endif
216
217 LL_AHB4_GRP1_EnableClock(LL_AHB4_GRP1_PERIPH_PWR);
218
219 #ifdef CONFIG_DEBUG
220 LL_DBGMCU_EnableDBGStandbyMode();
221 LL_DBGMCU_APB7_GRP1_FreezePeriph(LL_DBGMCU_APB7_GRP1_RTC_STOP);
222 LL_DBGMCU_APB7_GRP1_FreezePeriph(LL_DBGMCU_APB7_GRP1_LPTIM1_STOP);
223 #else
224 LL_DBGMCU_DisableDBGStandbyMode();
225 #endif
226
227 /* Enabling Ultra Low power mode */
228 LL_PWR_EnableUltraLowPowerMode();
229
230 LL_FLASH_EnableSleepPowerDown();
231 }
232