1 /*
2  * Copyright (c) 2021 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 
11 #include <stm32l5xx_ll_utils.h>
12 #include <stm32l5xx_ll_bus.h>
13 #include <stm32l5xx_ll_cortex.h>
14 #include <stm32l5xx_ll_pwr.h>
15 #include <stm32l5xx_ll_rcc.h>
16 #include <stm32l5xx_ll_system.h>
17 #include <clock_control/clock_stm32_ll_common.h>
18 #include <zephyr/drivers/clock_control/stm32_clock_control.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 /* Invoke Low Power/System Off specific Tasks */
pm_state_set(enum pm_state state,uint8_t substate_id)31 void pm_state_set(enum pm_state state, uint8_t substate_id)
32 {
33 	if (state != PM_STATE_SUSPEND_TO_IDLE) {
34 		LOG_DBG("Unsupported power state %u", state);
35 		return;
36 	}
37 
38 	/* ensure the proper wake-up system clock */
39 	LL_RCC_SetClkAfterWakeFromStop(RCC_STOP_WAKEUPCLOCK_SELECTED);
40 
41 	switch (substate_id) {
42 	case 1: /* this corresponds to the STOP0 mode: */
43 		/* enter STOP0 mode */
44 		LL_PWR_SetPowerMode(LL_PWR_MODE_STOP0);
45 		break;
46 	case 2: /* this corresponds to the STOP1 mode: */
47 		/* enter STOP1 mode */
48 		LL_PWR_SetPowerMode(LL_PWR_MODE_STOP1);
49 		break;
50 	case 3: /* this corresponds to the STOP2 mode: */
51 #ifdef PWR_CR1_RRSTP
52 		LL_PWR_DisableSRAM3Retention();
53 #endif /* PWR_CR1_RRSTP */
54 		/* enter STOP2 mode */
55 		LL_PWR_SetPowerMode(LL_PWR_MODE_STOP2);
56 		break;
57 	default:
58 		LOG_DBG("Unsupported power state substate-id %u",
59 			substate_id);
60 		break;
61 	}
62 
63 	LL_LPM_EnableDeepSleep();
64 	/* enter SLEEP mode : WFE or WFI */
65 	k_cpu_idle();
66 }
67 
68 /* Handle SOC specific activity after Low Power Mode Exit */
pm_state_exit_post_ops(enum pm_state state,uint8_t substate_id)69 void pm_state_exit_post_ops(enum pm_state state, uint8_t substate_id)
70 {
71 	if (state != PM_STATE_SUSPEND_TO_IDLE) {
72 		LOG_DBG("Unsupported power substate-id %u", state);
73 	} else {
74 		switch (substate_id) {
75 		case 1:	/* STOP0 */
76 			__fallthrough;
77 		case 2:	/* STOP1 */
78 			__fallthrough;
79 		case 3:	/* STOP2 */
80 			LL_LPM_DisableSleepOnExit();
81 			LL_LPM_EnableSleep();
82 			break;
83 		default:
84 			LOG_DBG("Unsupported power substate-id %u",
85 				substate_id);
86 			break;
87 		}
88 		/* need to restore the clock */
89 		stm32_clock_control_init(NULL);
90 	}
91 
92 	/*
93 	 * System is now in active mode.
94 	 * Reenable interrupts which were disabled
95 	 * when OS started idling code.
96 	 */
97 	irq_unlock(0);
98 }
99 
100 /* Initialize STM32 Power */
stm32_power_init(void)101 void stm32_power_init(void)
102 {
103 
104 	/* enable Power clock */
105 	LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_PWR);
106 }
107