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