1 /***************************************************************************//**
2 * @file
3 * @brief Power Manager API definition.
4 *******************************************************************************
5 * # License
6 * <b>Copyright 2019 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 #ifndef SL_POWER_MANAGER_H
32 #define SL_POWER_MANAGER_H
33
34 #ifndef SL_POWER_MANAGER_DEBUG
35 #include "sl_power_manager_config.h"
36 #endif
37 #include "em_device.h"
38 #include "em_core.h"
39 #include "sl_slist.h"
40 #include "sl_status.h"
41 #include "sl_sleeptimer.h"
42 #include "sl_enum.h"
43 #include "em_emu.h"
44
45 #include <stdbool.h>
46 #include <stdint.h>
47
48 #ifdef __cplusplus
49 extern "C" {
50 #endif
51
52 /***************************************************************************//**
53 * @addtogroup power_manager Power Manager
54 *
55 * @details Power manager is a platform level software module that manages
56 * the system's energy modes. Its main purpose is to transition the system to a
57 * low energy mode when the processor has nothing to execute. The energy mode the
58 * system will transition to is determined each time the system goes to sleep
59 * using requirements. These requirements are set by the different software modules
60 * (drivers, stacks, application code, etc...). Power manager also ensures a
61 * strict control of some power hungry resources such as the high frequency
62 * external oscillator (normally called HFXO). Power manager also
63 * offers a notification mechanism through which any piece of software module can be
64 * notified of energy mode transitions through callbacks.
65 *
66 * @note Sleep Driver is deprecated. Use Power Manager for all sleep-related
67 * operations. See <a href="https://www.silabs.com/documents/
68 * public/application-notes/
69 * an1358-migrating-from-sleep-driver-to-power-manager.pdf">AN1358:
70 * Migrating from Sleep Driver to Power Manager</a> for information on how
71 * to migrate from Sleep Driver to Power Manager.
72 *
73 * @details
74 * ## Initialization
75 *
76 * Power manager must be initialized prior to any call to power manager API.
77 * If sl_system is used, only sl_system_init() must be called, otherwise
78 * sl_power_manager_init() must be called manually. Note that power manager
79 * must be initialized after the clock(s), when initialized manually, as the
80 * power manager check which oscillators are used during the initialization phase.
81 *
82 * ## Add and remove requirements
83 *
84 * The drivers should add and remove energy mode requirements, at runtime, on the
85 * lowest energy mode for them depending on their state. When calling
86 * sl_power_manager_sleep(), the lowest possible Energy mode will be automatically
87 * selected.
88 *
89 * It is possible to add and remove requirements from ISR. If a specific energy mode
90 * is required in the ISR, but not required to generate the interrupt, a requirement
91 * on the energy mode can be added from the ISR. It is guaranteed that the associated
92 * clock will be active once sl_power_manager_add_requirement() returns. The EM
93 * requirement can be also be removed from an ISR.
94 *
95 * ## Subscribe to events
96 *
97 * It possible to get notified when the system transition from a power level to
98 * another power level. This can allow to do some operations depending on which level
99 * the system goes, such as saving/restoring context.
100 *
101 * ## Sleep
102 *
103 * When the software has no more operation and only need to wait for an event, the
104 * software must call sl_power_manager_sleep(). This is automatically done when the
105 * kernel is present, but it needs to be called from the super loop in a baremetal
106 * project.
107 *
108 * ## Query callback functions
109 *
110 * ### Is OK to sleep
111 *
112 * Between the time `sl_power_manager_sleep` is called and the MCU is really put
113 * in a lower Energy mode, it is possible that an ISR occur and require the system
114 * to resume at that time instead of sleeping. So a callback is called in a critical
115 * section to validate that the MCU can go to sleep.
116 *
117 * In case of an application that runs on an RTOS, the RTOS will take care of determining
118 * if it is ok to sleep. In case of a baremetal application, the function `sl_power_manager_is_ok_to_sleep()`
119 * will be generated automatically by Simplicity Studio's wizard.
120 * The function will look at multiple software modules from the SDK to take a decision.
121 * The application can contribute to the decision by defining the function `app_is_ok_to_sleep()`.
122 * If any of the software modules (including the application via `app_is_ok_to_sleep()`) return false,
123 * the process of entering in sleep will be aborted.
124 *
125 * ### Sleep on ISR exit
126 *
127 * When the system enters sleep, the only way to wake it up is via an interrupt or
128 * exception. By default, power manager will assume that when an interrupt
129 * occurs and the corresponding ISR has been executed, the system must not go back
130 * to sleep. However, in the case where all the processing related to this interrupt
131 * is performed in the ISR, it is possible to go back to sleep by using this hook.
132 *
133 * In case of an application that runs on an RTOS, the RTOS will take care of determining
134 * if the system can go back to sleep on ISR exit. Power manager will ensure the system resumes
135 * its operations as soon as a task is resumed, posted or that its delay expires.
136 * In case of a baremetal application, the function `sl_power_manager_sleep_on_isr_exit()` will be generated
137 * automatically by Simplicity Studio's wizard. The function will look at multiple software modules from the SDK
138 * to take a decision. The application can contribute to the decision by defining the
139 * function `app_sleep_on_isr_exit()`.
140 * The generated function will take a decision based on the value returned by the different software modules
141 * (including the application via `app_sleep_on_isr_exit()`):
142 *
143 * `SL_POWER_MANAGER_IGNORE`: if the software module did not cause the system wakeup and/or doesn't want to contribute to the decision.
144 * `SL_POWER_MANAGER_SLEEP`: if the software module did cause the system wakeup, but the system should go back to sleep.
145 * `SL_POWER_MANAGER_WAKEUP`: if the software module did cause the system wakeup, and the system should not go back to sleep.
146 *
147 * If any software module returned `SL_POWER_MANAGER_SLEEP` and none returned `SL_POWER_MANAGER_WAKEUP`,
148 * the system will go back to sleep. Any other combination will cause the system not to go back to sleep.
149 *
150 * ### Debugging feature
151 *
152 * By setting the configuration define SL_POWER_MANAGER_DEBUG to 1, it is possible
153 * to record the requirements currently set and their owner. It is possible to print
154 * at any time a table that lists all the added requirements and their owner. This
155 * table can be printed by caling the function
156 * sl_power_manager_debug_print_em_requirements().
157 * Make sure to add the following define
158 * ```
159 * #define CURRENT_MODULE_NAME "<Module printable name here>"
160 * ```
161 * to any application code source file that adds and removes requirements.
162 *
163 * ## Usage Example
164 *
165 * ```
166 * #define EM_EVENT_MASK_ALL (SL_POWER_MANAGER_EVENT_TRANSITION_ENTERING_EM0 \
167 * | SL_POWER_MANAGER_EVENT_TRANSITION_LEAVING_EM0 \
168 * | SL_POWER_MANAGER_EVENT_TRANSITION_ENTERING_EM1 \
169 * | SL_POWER_MANAGER_EVENT_TRANSITION_LEAVING_EM1 \
170 * | SL_POWER_MANAGER_EVENT_TRANSITION_ENTERING_EM2 \
171 * | SL_POWER_MANAGER_EVENT_TRANSITION_LEAVING_EM2 \
172 * | SL_POWER_MANAGER_EVENT_TRANSITION_ENTERING_EM3 \
173 * | SL_POWER_MANAGER_EVENT_TRANSITION_LEAVING_EM3)
174 *
175 * sl_power_manager_em_transition_event_handle_t event_handle;
176 * sl_power_manager_em_transition_event_info_t event_info = {
177 * .event_mask = EM_EVENT_MASK_ALL,
178 * .on_event = my_events_callback,
179 * }
180 *
181 * void main(void)
182 * {
183 * // Initialize power manager; not needed if sl_system_init() is used.
184 * sl_power_manager_init();
185 *
186 * // Limit sleep level to EM1
187 * sl_power_manager_add_em_requirement(SL_POWER_MANAGER_EM1);
188 *
189 * // Subscribe to all event types; get notified for every power transition.
190 * sl_power_manager_subscribe_em_transition_event(&event_handle, &event_info);
191 * while (1) {
192 * // Actions
193 * [...]
194 * if (completed) {
195 * // Remove energy mode requirement, can go to EM2 or EM3 now, depending on the configuration
196 * sl_power_manager_remove_em_requirement(SL_POWER_MANAGER_EM1);
197 * }
198 *
199 * // Sleep to lowest possible energy mode; This call is not needed when using the kernel.
200 * sl_power_manager_sleep();
201 * // Will resume after an interrupt or exception
202 * }
203 * }
204 *
205 * void my_events_callback(sl_power_manager_em_t from,
206 * sl_power_manager_em_t to)
207 * {
208 * printf("Event:%s-%s\r\n", string_lookup_table[from], string_lookup_table[to]);
209 * }
210 * ```
211 *
212 * @{
213 ******************************************************************************/
214
215 // -----------------------------------------------------------------------------
216 // Defines
217
218 // Current module name for debugging features
219 #ifndef CURRENT_MODULE_NAME
220 #define CURRENT_MODULE_NAME "Anonymous"
221 #endif
222
223 // Power transition events
224 #define SL_POWER_MANAGER_EVENT_TRANSITION_ENTERING_EM0 (1 << 0)
225 #define SL_POWER_MANAGER_EVENT_TRANSITION_LEAVING_EM0 (1 << 1)
226 #define SL_POWER_MANAGER_EVENT_TRANSITION_ENTERING_EM1 (1 << 2)
227 #define SL_POWER_MANAGER_EVENT_TRANSITION_LEAVING_EM1 (1 << 3)
228 #define SL_POWER_MANAGER_EVENT_TRANSITION_ENTERING_EM2 (1 << 4)
229 #define SL_POWER_MANAGER_EVENT_TRANSITION_LEAVING_EM2 (1 << 5)
230 #define SL_POWER_MANAGER_EVENT_TRANSITION_ENTERING_EM3 (1 << 6)
231 #define SL_POWER_MANAGER_EVENT_TRANSITION_LEAVING_EM3 (1 << 7)
232 /// @endcond
233
234 // -----------------------------------------------------------------------------
235 // Data Types
236
237 /// @brief Energy modes
238 typedef enum {
239 SL_POWER_MANAGER_EM0 = 0, ///< Run Mode (Energy Mode 0)
240 SL_POWER_MANAGER_EM1, ///< Sleep Mode (Energy Mode 1)
241 SL_POWER_MANAGER_EM2, ///< Deep Sleep Mode (Energy Mode 2)
242 SL_POWER_MANAGER_EM3, ///< Stop Mode (Energy Mode 3)
243 SL_POWER_MANAGER_EM4, ///< Shutoff Mode (Energy Mode 4)
244 } sl_power_manager_em_t;
245
246 /// @brief Mask of all the event(s) to listen to.
247 typedef uint32_t sl_power_manager_em_transition_event_t;
248
249 /***************************************************************************//**
250 * Typedef for the user supplied callback function which is called when
251 * an energy mode transition occurs.
252 *
253 * @param from Energy mode we are leaving.
254 * @param to Energy mode we are entering.
255 ******************************************************************************/
256 typedef void (*sl_power_manager_em_transition_on_event_t)(sl_power_manager_em_t from,
257 sl_power_manager_em_t to);
258
259 /// @brief Struct representing energy mode transition event information
260 typedef struct {
261 const sl_power_manager_em_transition_event_t event_mask; ///< Mask of the transitions on which the callback should be called.
262 const sl_power_manager_em_transition_on_event_t on_event; ///< Function that must be called when the event occurs.
263 } sl_power_manager_em_transition_event_info_t;
264
265 /// @brief Struct representing energy mode transition event handle
266 typedef struct {
267 sl_slist_node_t node; ///< List node.
268 sl_power_manager_em_transition_event_info_t *info; ///< Handle event info.
269 } sl_power_manager_em_transition_event_handle_t;
270
271 /// On ISR Exit Hook answer
SL_ENUM(sl_power_manager_on_isr_exit_t)272 SL_ENUM(sl_power_manager_on_isr_exit_t) {
273 SL_POWER_MANAGER_IGNORE = (1UL << 0UL), ///< The module did not trigger an ISR and it doesn't want to contribute to the decision
274 SL_POWER_MANAGER_SLEEP = (1UL << 1UL), ///< The module was the one that caused the system wakeup and the system SHOULD go back to sleep
275 SL_POWER_MANAGER_WAKEUP = (1UL << 2UL), ///< The module was the one that caused the system wakeup and the system MUST NOT go back to sleep
276 };
277
278 // -----------------------------------------------------------------------------
279 // Internal Prototypes only to be used by Power Manager module
280 void sli_power_manager_update_em_requirement(sl_power_manager_em_t em,
281 bool add);
282
283 // To make sure that we are able to optimize out the string argument when the
284 // debug feature is disable, we use a pre-processor macro resulting in a no-op.
285 // We also make sure to always have a definition for the function regardless if
286 // the debug feature is enable or not for binary compatibility.
287 #if (SL_POWER_MANAGER_DEBUG == 1)
288 void sli_power_manager_debug_log_em_requirement(sl_power_manager_em_t em,
289 bool add,
290 const char *name);
291 #else
292 #define sli_power_manager_debug_log_em_requirement(em, add, name) /* no-op */
293 #endif
294
295 // -----------------------------------------------------------------------------
296 // Prototypes
297
298 /***************************************************************************//**
299 * Initialize Power Manager module.
300 * @return Status code
301 ******************************************************************************/
302 sl_status_t sl_power_manager_init(void);
303
304 /***************************************************************************//**
305 * Sleep at the lowest allowed energy mode.
306 *
307 * @note Must not be called from ISR
308 * @par
309 * @note This function will expect and call a callback with the following
310 * signature: `bool sl_power_manager_is_ok_to_sleep(void)`
311 *
312 * @note This function can be used to cancel a sleep action and handle the
313 * possible race condition where an ISR that would cause a wakeup is
314 * triggered right after the decision to call sl_power_manager_sleep()
315 * has been made.
316 *
317 * Usage example:
318 *
319 * ```c
320 * void main(void)
321 * {
322 * sl_power_manager_init();
323 * while (1) {
324 * tick();
325 * sl_power_manager_sleep();
326 * }
327 * }
328 * ```
329 ******************************************************************************/
330 void sl_power_manager_sleep(void);
331
332 /***************************************************************************//**
333 * Adds requirement on given energy mode.
334 *
335 * @param em Energy mode to add the requirement to:
336 * - ::SL_POWER_MANAGER_EM1
337 * - ::SL_POWER_MANAGER_EM2
338 ******************************************************************************/
sl_power_manager_add_em_requirement(sl_power_manager_em_t em)339 __STATIC_INLINE void sl_power_manager_add_em_requirement(sl_power_manager_em_t em)
340 {
341 #if (SL_POWER_MANAGER_LOWEST_EM_ALLOWED != 1)
342 CORE_DECLARE_IRQ_STATE;
343
344 CORE_ENTER_CRITICAL();
345 sli_power_manager_update_em_requirement(em, true);
346
347 sli_power_manager_debug_log_em_requirement(em, true, (const char *)CURRENT_MODULE_NAME);
348 CORE_EXIT_CRITICAL();
349 #else
350 (void)em;
351 #endif
352 }
353
354 /***************************************************************************//**
355 * Removes requirement on given energy mode.
356 *
357 * @param em Energy mode to remove the requirement to:
358 * - ::SL_POWER_MANAGER_EM1
359 * - ::SL_POWER_MANAGER_EM2
360 ******************************************************************************/
sl_power_manager_remove_em_requirement(sl_power_manager_em_t em)361 __STATIC_INLINE void sl_power_manager_remove_em_requirement(sl_power_manager_em_t em)
362 {
363 #if (SL_POWER_MANAGER_LOWEST_EM_ALLOWED != 1)
364 CORE_DECLARE_IRQ_STATE;
365
366 CORE_ENTER_CRITICAL();
367 sli_power_manager_update_em_requirement(em, false);
368
369 sli_power_manager_debug_log_em_requirement(em, false, (const char *)CURRENT_MODULE_NAME);
370 CORE_EXIT_CRITICAL();
371 #else
372 (void)em;
373 #endif
374 }
375
376 /***************************************************************************//**
377 * Registers a callback to be called on given Energy Mode transition(s).
378 *
379 * @param event_handle Event handle (no initialization needed).
380 *
381 * @param event_info Event info structure that contains the event mask and the
382 * callback that must be called.
383 *
384 * @note Adding and removing requirement(s) from a callback on a transition event
385 * is not supported.
386 *
387 * @note The parameters passed must be persistent, meaning that they need to survive
388 * until the callback fires.
389 *
390 * Usage example:
391 *
392 * ```c
393 * #define EM_EVENT_MASK_ALL ( SL_POWER_MANAGER_EVENT_TRANSITION_ENTERING_EM0 \
394 * | SL_POWER_MANAGER_EVENT_TRANSITION_LEAVING_EM0 \
395 * | SL_POWER_MANAGER_EVENT_TRANSITION_ENTERING_EM1 \
396 * | SL_POWER_MANAGER_EVENT_TRANSITION_LEAVING_EM1 \
397 * | SL_POWER_MANAGER_EVENT_TRANSITION_ENTERING_EM2 \
398 * | SL_POWER_MANAGER_EVENT_TRANSITION_LEAVING_EM2 \
399 * | SL_POWER_MANAGER_EVENT_TRANSITION_ENTERING_EM3 \
400 * | SL_POWER_MANAGER_EVENT_TRANSITION_LEAVING_EM3)
401 *
402 * sl_power_manager_em_transition_event_handle_t event_handle;
403 * sl_power_manager_em_transition_event_info_t event_info = {
404 * .event_mask = EM_EVENT_MASK_ALL,
405 * .on_event = my_callback,
406 * };
407 *
408 * void my_callback(sl_power_manager_em_t from,
409 * sl_power_manager_em_t to)
410 * {
411 * [...]
412 * }
413 *
414 * void main(void)
415 * {
416 * sl_power_manager_init();
417 * sl_power_manager_subscribe_em_transition_event(&event_handle, &event_info);
418 * }
419 * ```
420 ******************************************************************************/
421 void sl_power_manager_subscribe_em_transition_event(sl_power_manager_em_transition_event_handle_t *event_handle,
422 const sl_power_manager_em_transition_event_info_t *event_info);
423
424 /***************************************************************************//**
425 * Unregisters an event callback handle on Energy mode transition.
426 *
427 * @param event_handle Event handle which must be unregistered (must have been
428 * registered previously).
429 *
430 * @note An EFM_ASSERT is thrown if the handle is not found.
431 ******************************************************************************/
432 void sl_power_manager_unsubscribe_em_transition_event(sl_power_manager_em_transition_event_handle_t *event_handle);
433
434 /***************************************************************************//**
435 * Get configurable overhead value for early restore time in Sleeptimer ticks
436 * when a schedule wake-up is set.
437 *
438 * @return Current overhead value for early restore time.
439 *
440 * @note This function will return 0 in case SL_POWER_MANAGER_LOWEST_EM_ALLOWED
441 * config is set to EM1.
442 ******************************************************************************/
443 int32_t sl_power_manager_schedule_wakeup_get_restore_overhead_tick(void);
444
445 /***************************************************************************//**
446 * Set configurable overhead value for early restore time in Sleeptimer ticks
447 * used for schedule wake-up.
448 * Must be called after initialization else the value will be overwritten.
449 *
450 * @param overhead_tick Overhead value to set for early restore time.
451 *
452 * @note The overhead value can also be negative to remove time from the restore
453 * process.
454 *
455 * @note This function will do nothing when SL_POWER_MANAGER_LOWEST_EM_ALLOWED
456 * config is set to EM1.
457 ******************************************************************************/
458 void sl_power_manager_schedule_wakeup_set_restore_overhead_tick(int32_t overhead_tick);
459
460 /***************************************************************************//**
461 * Get configurable minimum off-time value for schedule wake-up in Sleeptimer
462 * ticks.
463 *
464 * @return Current minimum off-time value for schedule wake-up.
465 *
466 * @note Turning on external high frequency clock, such as HFXO, requires more
467 * energy since we must supply higher current for the wake-up.
468 * Therefore, when an 'external high frequency clock enable' is scheduled
469 * in 'x' time, there is a threshold 'x' value where turning off the clock
470 * is not worthwhile since the energy consumed by taking into account the
471 * wake-up will be greater than if we just keep the clock on until the next
472 * scheduled clock enabled. This threshold value is what we refer as the
473 * minimum off-time.
474 *
475 * @note This function will return 0 in case SL_POWER_MANAGER_LOWEST_EM_ALLOWED
476 * config is set to EM1.
477 ******************************************************************************/
478 uint32_t sl_power_manager_schedule_wakeup_get_minimum_offtime_tick(void);
479
480 /***************************************************************************//**
481 * Set configurable minimum off-time value for schedule wake-up in Sleeptimer
482 * ticks.
483 *
484 * @param minimum_offtime_tick minimum off-time value to set for schedule
485 * wake-up.
486 *
487 * @note Turning on external high frequency clock, such as HFXO, requires more
488 * energy since we must supply higher current for the wake-up.
489 * Therefore, when an 'external high frequency clock enable' is scheduled
490 * in 'x' time, there is a threshold 'x' value where turning off the clock
491 * is not worthwhile since the energy consumed by taking into account the
492 * wake-up will be greater than if we just keep the clock on until the next
493 * scheduled clock enabled. This threshold value is what we refer as the
494 * minimum off-time.
495 *
496 * @note This function will do nothing when SL_POWER_MANAGER_LOWEST_EM_ALLOWED
497 * config is set to EM1.
498 ******************************************************************************/
499 void sl_power_manager_schedule_wakeup_set_minimum_offtime_tick(uint32_t minimum_offtime_tick);
500
501 #if defined(EMU_VSCALE_PRESENT)
502 /***************************************************************************//**
503 * Enable or disable fast wake-up in EM2 and EM3
504 *
505 * @note Will also update the wake up time from EM2 to EM0.
506 *
507 * @note This function will do nothing when SL_POWER_MANAGER_LOWEST_EM_ALLOWED
508 * config is set to EM1.
509 ******************************************************************************/
510 void sl_power_manager_em23_voltage_scaling_enable_fast_wakeup(bool enable);
511 #endif
512
513 /**************************************************************************//**
514 * Determines if the HFXO interrupt was part of the last wake-up and/or if
515 * the HFXO early wakeup expired during the last ISR
516 * and if it was the only timer to expire in that period.
517 *
518 * @return true if power manager sleep can return to sleep,
519 * false otherwise.
520 *
521 * @note This function will always return false in case
522 * SL_POWER_MANAGER_LOWEST_EM_ALLOWED config is set to EM1, since we will
523 * never sleep at a lower level than EM1.
524 *****************************************************************************/
525 bool sl_power_manager_is_latest_wakeup_internal(void);
526
527 /** @} (end addtogroup power_manager) */
528
529 #ifdef __cplusplus
530 }
531 #endif
532
533 #endif // SL_POWER_MANAGER_H
534