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