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