1 /***************************************************************************//**
2 * @file
3 * @brief Power Manager common API implementation.
4 *******************************************************************************
5 * # License
6 * <b>Copyright 2024 Silicon Laboratories Inc. www.silabs.com</b>
7 *******************************************************************************
8 *
9 * SPDX-License-Identifier: Zlib
10 *
11 * The licensor of this software is Silicon Laboratories Inc.
12 *
13 * This software is provided 'as-is', without any express or implied
14 * warranty. In no event will the authors be held liable for any damages
15 * arising from the use of this software.
16 *
17 * Permission is granted to anyone to use this software for any purpose,
18 * including commercial applications, and to alter it and redistribute it
19 * freely, subject to the following restrictions:
20 *
21 * 1. The origin of this software must not be misrepresented; you must not
22 * claim that you wrote the original software. If you use this software
23 * in a product, an acknowledgment in the product documentation would be
24 * appreciated but is not required.
25 * 2. Altered source versions must be plainly marked as such, and must not be
26 * misrepresented as being the original software.
27 * 3. This notice may not be removed or altered from any source distribution.
28 *
29 ******************************************************************************/
30
31 #include "sl_power_manager.h"
32 #include "sl_power_manager_config.h"
33 #include "sli_power_manager.h"
34 #include "sli_clock_manager.h"
35 #include "sl_assert.h"
36 #include "sl_atomic.h"
37 #include "sl_clock_manager.h"
38
39 #if defined(SL_COMPONENT_CATALOG_PRESENT)
40 #include "sl_component_catalog.h"
41 #endif
42
43 #include "em_device.h"
44 #if !defined(_SILICON_LABS_32B_SERIES_3)
45 #include "em_emu.h"
46 #else
47 #include "sli_power_manager_execution_modes_private.h"
48 #endif
49
50 #include <stdlib.h>
51 #include <stdint.h>
52 #include <string.h>
53
54 /*******************************************************************************
55 *************************** LOCAL VARIABLES ********************************
56 ******************************************************************************/
57
58 // Events subscribers lists
59 static sl_slist_node_t *power_manager_em_transition_event_list = NULL;
60
61 /*******************************************************************************
62 ************************** GLOBAL FUNCTIONS *******************************
63 ******************************************************************************/
64
65 /***************************************************************************//**
66 * Last-chance check before sleep.
67 *
68 * @return True, if the system should actually sleep.
69 * False, if not.
70 *
71 * @note This is the fallback implementation of the callback, it can be
72 * overridden by the application or other components.
73 ******************************************************************************/
sl_power_manager_is_ok_to_sleep(void)74 __WEAK bool sl_power_manager_is_ok_to_sleep(void)
75 {
76 return true;
77 }
78
79 /***************************************************************************//**
80 * Check if the MCU can sleep after an interrupt.
81 *
82 * @return True, if the system can sleep after the interrupt.
83 * False, otherwise.
84 *
85 * @note This is the fallback implementation of the callback, it can be
86 * overridden by the application or other components.
87 ******************************************************************************/
sl_power_manager_sleep_on_isr_exit(void)88 __WEAK bool sl_power_manager_sleep_on_isr_exit(void)
89 {
90 return false;
91 }
92
93 /***************************************************************************//**
94 * Enable or disable fast wake-up in EM2 and EM3
95 *
96 * @note Will also update the wake up time from EM2 to EM0.
97 *
98 * @note This function will do nothing when a project contains the
99 * power_manager_no_deepsleep component, which configures the
100 * lowest energy mode as EM1.
101 ******************************************************************************/
sl_power_manager_em23_voltage_scaling_enable_fast_wakeup(bool enable)102 void sl_power_manager_em23_voltage_scaling_enable_fast_wakeup(bool enable)
103 {
104 #if (defined(EMU_VSCALE_PRESENT) && !defined(SL_CATALOG_POWER_MANAGER_NO_DEEPSLEEP_PRESENT))
105 CORE_DECLARE_IRQ_STATE;
106
107 CORE_ENTER_CRITICAL();
108
109 sli_power_manager_em23_voltage_scaling_enable_fast_wakeup(enable);
110
111 CORE_EXIT_CRITICAL();
112 #else
113 (void)enable;
114 #endif
115 }
116
117 /***************************************************************************//**
118 * Registers a callback to be called on given Energy Mode transition(s).
119 *
120 * @note Adding/Removing requirement(s) from the callback is not supported.
121 ******************************************************************************/
sl_power_manager_subscribe_em_transition_event(sl_power_manager_em_transition_event_handle_t * event_handle,const sl_power_manager_em_transition_event_info_t * event_info)122 void sl_power_manager_subscribe_em_transition_event(sl_power_manager_em_transition_event_handle_t *event_handle,
123 const sl_power_manager_em_transition_event_info_t *event_info)
124 {
125 CORE_DECLARE_IRQ_STATE;
126
127 event_handle->info = event_info;
128 CORE_ENTER_CRITICAL();
129 sl_slist_push(&power_manager_em_transition_event_list, &event_handle->node);
130 CORE_EXIT_CRITICAL();
131 }
132
133 /***************************************************************************//**
134 * Unregisters an event callback handle on Energy mode transition.
135 ******************************************************************************/
sl_power_manager_unsubscribe_em_transition_event(sl_power_manager_em_transition_event_handle_t * event_handle)136 void sl_power_manager_unsubscribe_em_transition_event(sl_power_manager_em_transition_event_handle_t *event_handle)
137 {
138 CORE_DECLARE_IRQ_STATE;
139
140 CORE_ENTER_CRITICAL();
141 sl_slist_remove(&power_manager_em_transition_event_list, &event_handle->node);
142 CORE_EXIT_CRITICAL();
143 }
144
145 /***************************************************************************//**
146 * Initializes energy mode transition list.
147 ******************************************************************************/
sli_power_manager_em_transition_event_list_init(void)148 void sli_power_manager_em_transition_event_list_init(void)
149 {
150 sl_slist_init(&power_manager_em_transition_event_list);
151 }
152
153 /***************************************************************************//**
154 * Notify subscribers about energy mode transition.
155 ******************************************************************************/
sli_power_manager_notify_em_transition(sl_power_manager_em_t from,sl_power_manager_em_t to)156 void sli_power_manager_notify_em_transition(sl_power_manager_em_t from,
157 sl_power_manager_em_t to)
158 {
159 sl_power_manager_em_transition_event_handle_t *handle;
160 sl_power_manager_em_transition_event_t transition = 0;
161
162 switch (to) {
163 case SL_POWER_MANAGER_EM0:
164 transition = SL_POWER_MANAGER_EVENT_TRANSITION_ENTERING_EM0;
165 break;
166
167 case SL_POWER_MANAGER_EM1:
168 transition = SL_POWER_MANAGER_EVENT_TRANSITION_ENTERING_EM1;
169 break;
170
171 case SL_POWER_MANAGER_EM2:
172 transition = SL_POWER_MANAGER_EVENT_TRANSITION_ENTERING_EM2;
173 break;
174
175 #if !defined(SL_CATALOG_POWER_MANAGER_ARM_SLEEP_ON_EXIT)
176 case SL_POWER_MANAGER_EM3:
177 transition = SL_POWER_MANAGER_EVENT_TRANSITION_ENTERING_EM3;
178 break;
179 #endif
180
181 default:
182 EFM_ASSERT(0);
183 }
184
185 switch (from) {
186 case SL_POWER_MANAGER_EM0:
187 transition |= SL_POWER_MANAGER_EVENT_TRANSITION_LEAVING_EM0;
188 break;
189
190 case SL_POWER_MANAGER_EM1:
191 transition |= SL_POWER_MANAGER_EVENT_TRANSITION_LEAVING_EM1;
192 break;
193
194 case SL_POWER_MANAGER_EM2:
195 transition |= SL_POWER_MANAGER_EVENT_TRANSITION_LEAVING_EM2;
196 break;
197
198 #if !defined(SL_CATALOG_POWER_MANAGER_ARM_SLEEP_ON_EXIT)
199 case SL_POWER_MANAGER_EM3:
200 transition |= SL_POWER_MANAGER_EVENT_TRANSITION_LEAVING_EM3;
201 break;
202 #endif
203
204 default:
205 EFM_ASSERT(0);
206 }
207
208 SL_SLIST_FOR_EACH_ENTRY(power_manager_em_transition_event_list, handle, sl_power_manager_em_transition_event_handle_t, node) {
209 if ((handle->info->event_mask & transition) > 0) {
210 handle->info->on_event(from, to);
211 }
212 }
213 }
214