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