1 /*
2  * Copyright (c) 2023 Google LLC
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <clock_control/clock_stm32_ll_common.h>
8 #include <soc.h>
9 
10 #include <stm32f4xx_ll_bus.h>
11 #include <stm32f4xx_ll_cortex.h>
12 #include <stm32f4xx_ll_pwr.h>
13 #include <stm32f4xx.h>
14 
15 #include <zephyr/drivers/clock_control/stm32_clock_control.h>
16 #include <zephyr/drivers/counter.h>
17 #include <zephyr/drivers/interrupt_controller/gic.h>
18 #include <zephyr/kernel.h>
19 #include <zephyr/logging/log.h>
20 #include <zephyr/pm/pm.h>
21 #include <zephyr/init.h>
22 
23 LOG_MODULE_DECLARE(soc, CONFIG_SOC_LOG_LEVEL);
24 
25 BUILD_ASSERT(DT_SAME_NODE(DT_CHOSEN(zephyr_cortex_m_idle_timer), DT_NODELABEL(rtc)),
26 		"STM32Fx series needs RTC as an additional IDLE timer for power management");
27 
pm_state_set(enum pm_state state,uint8_t substate_id)28 void pm_state_set(enum pm_state state, uint8_t substate_id)
29 {
30 	ARG_UNUSED(substate_id);
31 
32 	switch (state) {
33 	case PM_STATE_SUSPEND_TO_IDLE:
34 		LL_LPM_DisableEventOnPend();
35 		LL_PWR_ClearFlag_WU();
36 		/* According to datasheet (DS11139 Rev 8,Table 38.), wakeup with regulator in
37 		 * low-power mode takes typically 8us, max 13us more time than with the main
38 		 * regulator. We are using RTC as a wakeup source, which has a tick 62,5us.
39 		 * It means we have to add significant margin to the exit-latency anyway,
40 		 * so it is worth always using the low-power regulator.
41 		 */
42 		LL_PWR_SetPowerMode(LL_PWR_MODE_STOP_LPREGU);
43 		LL_LPM_EnableDeepSleep();
44 
45 		k_cpu_idle();
46 
47 		break;
48 	default:
49 		LOG_DBG("Unsupported power state %u", state);
50 		break;
51 	}
52 }
53 
pm_state_exit_post_ops(enum pm_state state,uint8_t substate_id)54 void pm_state_exit_post_ops(enum pm_state state, uint8_t substate_id)
55 {
56 	ARG_UNUSED(substate_id);
57 
58 	switch (state) {
59 	case PM_STATE_SUSPEND_TO_IDLE:
60 		LL_LPM_DisableSleepOnExit();
61 		LL_LPM_EnableSleep();
62 
63 		/* Restore the clock setup. */
64 		stm32_clock_control_init(NULL);
65 		break;
66 	default:
67 		LOG_DBG("Unsupported power substate-id %u", state);
68 		break;
69 	}
70 
71 	/*
72 	 * System is now in active mode. Reenable interrupts which were
73 	 * disabled when OS started idling code.
74 	 */
75 	irq_unlock(0);
76 }
77 
stm32_power_init(void)78 void stm32_power_init(void)
79 {
80 	/* Enable Power clock. It should by done by default, but make sure to
81 	 * enable it for all STM32F4x chips.
82 	 */
83 	LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_PWR);
84 }
85