1 /*
2  * Copyright (c) 2018, Piotr Mienkowski
3  * Copyright (c) 2023, Antmicro <www.antmicro.com>
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 #include <zephyr/kernel.h>
8 #include <zephyr/logging/log.h>
9 #include <zephyr/pm/pm.h>
10 #include <sl_power_manager.h>
11 #include <sl_hfxo_manager.h>
12 #include <sli_hfxo_manager.h>
13 
14 LOG_MODULE_DECLARE(soc, CONFIG_SOC_LOG_LEVEL);
15 
16 /*
17  * Power state map:
18  * PM_STATE_RUNTIME_IDLE: EM1 Sleep
19  * PM_STATE_SUSPEND_TO_IDLE: EM2 Deep Sleep
20  * PM_STATE_STANDBY: EM3 Stop
21  * PM_STATE_SOFT_OFF: EM4
22  */
23 
24 
25 /* Invoke Low Power/System Off specific Tasks */
pm_state_set(enum pm_state state,uint8_t substate_id)26 void pm_state_set(enum pm_state state, uint8_t substate_id)
27 {
28 	ARG_UNUSED(substate_id);
29 	sl_power_manager_em_t energy_mode = SL_POWER_MANAGER_EM0;
30 
31 	LOG_DBG("SoC entering power state %d", state);
32 
33 	switch (state) {
34 	case PM_STATE_RUNTIME_IDLE:
35 		energy_mode = SL_POWER_MANAGER_EM1;
36 		break;
37 	case PM_STATE_SUSPEND_TO_IDLE:
38 		energy_mode = SL_POWER_MANAGER_EM2;
39 		break;
40 	case PM_STATE_STANDBY:
41 		energy_mode = SL_POWER_MANAGER_EM3;
42 		break;
43 	case PM_STATE_SOFT_OFF:
44 		energy_mode = SL_POWER_MANAGER_EM4;
45 		break;
46 	default:
47 		LOG_DBG("Unsupported power state %d", state);
48 		break;
49 	}
50 
51 	LOG_DBG("Entry to energy mode %d", energy_mode);
52 
53 	if (energy_mode == SL_POWER_MANAGER_EM4) {
54 		sl_power_manager_enter_em4();
55 	} else if (energy_mode != SL_POWER_MANAGER_EM0) {
56 		/* Calling the tracing and hook functions provided in arch_cpu_idle(). */
57 #if defined(CONFIG_TRACING)
58 		sys_trace_idle();
59 #endif
60 #if CONFIG_ARM_ON_ENTER_CPU_IDLE_PREPARE_HOOK
61 		z_arm_on_enter_cpu_idle_prepare();
62 #endif
63 
64 		sl_power_manager_add_em_requirement(energy_mode);
65 		sl_power_manager_sleep();
66 		sl_power_manager_remove_em_requirement(energy_mode);
67 	}
68 
69 	LOG_DBG("Exit from energy mode %d", energy_mode);
70 }
71 
pm_state_exit_post_ops(enum pm_state state,uint8_t substate_id)72 void pm_state_exit_post_ops(enum pm_state state, uint8_t substate_id)
73 {
74 	ARG_UNUSED(state);
75 	ARG_UNUSED(substate_id);
76 }
77 
78 /* This function is called by sl_power_manager_sleep() after it has set the PRIMASK. */
sl_power_manager_is_ok_to_sleep(void)79 bool sl_power_manager_is_ok_to_sleep(void)
80 {
81 	/* FIXME: When this function is entered the Kernel has disabled
82 	 * interrupts using BASEPRI register. This is incorrect as it prevents
83 	 * waking up from any interrupt which priority is not 0. Work around the
84 	 * issue and disable interrupts using PRIMASK register as recommended
85 	 * by ARM.
86 	 */
87 	/* Set BASEPRI to 0. */
88 	irq_unlock(0);
89 
90 	return true;
91 }
92 
93 /* This function is called by sl_power_manager_sleep() right after it was woken up from WFI. */
sli_power_manager_on_wakeup(void)94 void sli_power_manager_on_wakeup(void)
95 {
96 #if defined(HFXO_MANAGER_SLEEPTIMER_SYSRTC_INTEGRATION_ON)
97 	/* Handle the HFXO IRQ as soon as possible to retrieve the startup time. */
98 	sl_hfxo_manager_irq_handler();
99 #endif
100 	/* Forces a clock restore before handling interrupts. */
101 	sl_power_manager_add_em_requirement(SL_POWER_MANAGER_EM1);
102 	sl_power_manager_remove_em_requirement(SL_POWER_MANAGER_EM1);
103 }
104 
105 /**
106  * Some SiLabs blobs, such as RAIL, call directly into sl_power_manager, and
107  * for that they had to include sl_power_manager.h during build. Some of those
108  * blobs have been compiled with -DSL_POWER_MANAGER_DEBUG=1, making inlined
109  * functions from that header to rely on
110  * sli_power_manager_debug_log_em_requirement() callback.
111  *
112  * This is irrespective of whether *we* enable SL_POWER_MANAGER_DEBUG when
113  * compiling sl_power_manager code as part of Zephyr build.
114  *
115  * Therefore, we provide sli_power_manager_debug_log_em_requirement()
116  * definition here just to satisfy those blobs. It will also be used if we
117  * attempt to build sl_power_manager with SL_POWER_MANAGER_DEBUG enabled.
118  *
119  * @note Please keep this at the end of the file.
120  */
121 
122 #ifdef sli_power_manager_debug_log_em_requirement
123 #undef sli_power_manager_debug_log_em_requirement
124 #endif
125 
sli_power_manager_debug_init(void)126 void sli_power_manager_debug_init(void)
127 {
128 }
129 
sli_power_manager_debug_log_em_requirement(sl_power_manager_em_t em,bool add,const char * name)130 void sli_power_manager_debug_log_em_requirement(
131 	sl_power_manager_em_t em, bool add, const char *name)
132 {
133 	LOG_DBG("Set PM requirement em=%d add=%s name=%s",
134 			(int)em, add ? "true" : "false", name);
135 }
136