1 /*
2 * Copyright (c) 2021 Linaro Limited
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
11 #include <stm32u5xx_ll_utils.h>
12 #include <stm32u5xx_ll_bus.h>
13 #include <stm32u5xx_ll_cortex.h>
14 #include <stm32u5xx_ll_pwr.h>
15 #include <stm32u5xx_ll_icache.h>
16 #include <stm32u5xx_ll_rcc.h>
17 #include <stm32u5xx_ll_system.h>
18 #include <clock_control/clock_stm32_ll_common.h>
19
20 #include <zephyr/logging/log.h>
21 LOG_MODULE_DECLARE(soc, CONFIG_SOC_LOG_LEVEL);
22
23 /* select MSI as wake-up system clock if configured, HSI otherwise */
24 #if STM32_SYSCLK_SRC_MSI
25 #define RCC_STOP_WAKEUPCLOCK_SELECTED LL_RCC_STOP_WAKEUPCLOCK_MSI
26 #else
27 #define RCC_STOP_WAKEUPCLOCK_SELECTED LL_RCC_STOP_WAKEUPCLOCK_HSI
28 #endif
29
30 #ifdef CONFIG_STM32_STOP3_LP_MODE
pwr_stop3_isr(const struct device * dev)31 static void pwr_stop3_isr(const struct device *dev)
32 {
33 ARG_UNUSED(dev);
34
35 /* Clear all wake-up flags */
36 LL_PWR_ClearFlag_WU();
37 }
38
disable_cache(void)39 static void disable_cache(void)
40 {
41 /* Disabling ICACHE */
42 LL_ICACHE_Disable();
43 while (LL_ICACHE_IsEnabled() == 1U) {
44 }
45
46 /* Wait until ICACHE_SR.BUSYF is cleared */
47 while (LL_ICACHE_IsActiveFlag_BUSY() == 1U) {
48 }
49
50 /* Wait until ICACHE_SR.BSYENDF is set */
51 while (LL_ICACHE_IsActiveFlag_BSYEND() == 0U) {
52 }
53 }
54 #endif
55
set_mode_stop(uint8_t substate_id)56 void set_mode_stop(uint8_t substate_id)
57 {
58 /* ensure the proper wake-up system clock */
59 LL_RCC_SetClkAfterWakeFromStop(RCC_STOP_WAKEUPCLOCK_SELECTED);
60
61 switch (substate_id) {
62 case 1: /* enter STOP0 mode */
63 LL_PWR_SetPowerMode(LL_PWR_STOP0_MODE);
64 break;
65 case 2: /* enter STOP1 mode */
66 LL_PWR_SetPowerMode(LL_PWR_STOP1_MODE);
67 break;
68 case 3: /* enter STOP2 mode */
69 LL_PWR_SetPowerMode(LL_PWR_STOP2_MODE);
70 break;
71 #ifdef CONFIG_STM32_STOP3_LP_MODE
72 case 4: /* enter STOP3 mode */
73
74 LL_PWR_SetSRAM2SBRetention(LL_PWR_SRAM2_SB_FULL_RETENTION);
75 /* Enable RTC wakeup
76 * This configures an internal pin that generates an event to wakeup the system
77 */
78 LL_PWR_EnableWakeUpPin(LL_PWR_WAKEUP_PIN7);
79 LL_PWR_SetWakeUpPinSignal3Selection(LL_PWR_WAKEUP_PIN7);
80
81 /* Clear flags */
82 LL_PWR_ClearFlag_SB();
83 LL_PWR_ClearFlag_WU();
84
85 disable_cache();
86
87 LL_PWR_SetPowerMode(LL_PWR_STOP3_MODE);
88 break;
89 #endif
90 default:
91 LOG_DBG("Unsupported power state substate-id %u", substate_id);
92 break;
93 }
94 }
95
set_mode_standby(uint8_t substate_id)96 void set_mode_standby(uint8_t substate_id)
97 {
98 ARG_UNUSED(substate_id);
99 /* Select standby mode */
100 LL_PWR_SetPowerMode(LL_PWR_STANDBY_MODE);
101 }
102
103 /* Invoke Low Power/System Off specific Tasks */
pm_state_set(enum pm_state state,uint8_t substate_id)104 void pm_state_set(enum pm_state state, uint8_t substate_id)
105 {
106 switch (state) {
107 case PM_STATE_SUSPEND_TO_IDLE:
108 set_mode_stop(substate_id);
109 break;
110 case PM_STATE_STANDBY:
111 /* To be tested */
112 set_mode_standby(substate_id);
113 break;
114 default:
115 LOG_DBG("Unsupported power state %u", state);
116 return;
117 }
118
119 /* Set SLEEPDEEP bit of Cortex System Control Register */
120 LL_LPM_EnableDeepSleep();
121
122 /* Select mode entry : WFE or WFI and enter the CPU selected mode */
123 k_cpu_idle();
124 }
125
126 /* Handle SOC specific activity after Low Power Mode Exit */
pm_state_exit_post_ops(enum pm_state state,uint8_t substate_id)127 void pm_state_exit_post_ops(enum pm_state state, uint8_t substate_id)
128 {
129 switch (state) {
130 case PM_STATE_SUSPEND_TO_IDLE:
131 if (substate_id <= 3) {
132 LL_LPM_DisableSleepOnExit();
133 LL_LPM_EnableSleep();
134 #ifdef CONFIG_STM32_STOP3_LP_MODE
135 } else if (substate_id == 4) {
136 stm32_clock_control_standby_exit();
137
138 LL_ICACHE_SetMode(LL_ICACHE_1WAY);
139 LL_ICACHE_Enable();
140 while (LL_ICACHE_IsEnabled() == 0U) {
141 }
142
143 LL_LPM_DisableSleepOnExit();
144 LL_LPM_EnableSleep();
145 #endif
146 } else {
147 LOG_DBG("Unsupported power substate-id %u",
148 substate_id);
149 }
150 case PM_STATE_STANDBY:
151 /* To be tested */
152 LL_LPM_EnableSleep();
153 case PM_STATE_SUSPEND_TO_RAM:
154 __fallthrough;
155 case PM_STATE_SUSPEND_TO_DISK:
156 __fallthrough;
157 default:
158 LOG_DBG("Unsupported power state %u", state);
159 break;
160 }
161 /* need to restore the clock */
162 stm32_clock_control_init(NULL);
163
164 /*
165 * System is now in active mode.
166 * Reenable interrupts which were disabled
167 * when OS started idling code.
168 */
169 irq_unlock(0);
170 }
171
172 /* Initialize STM32 Power */
stm32_power_init(void)173 void stm32_power_init(void)
174 {
175
176 /* enable Power clock */
177 LL_AHB3_GRP1_EnableClock(LL_AHB3_GRP1_PERIPH_PWR);
178
179 #ifdef CONFIG_STM32_STOP3_LP_MODE
180 IRQ_CONNECT(PWR_S3WU_IRQn, 0,
181 pwr_stop3_isr, 0, 0);
182 irq_enable(PWR_S3WU_IRQn);
183 #endif
184 }
185