1 /***************************************************************************//**
2  * @file
3  * @brief Power Manager API implementation.
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 #include "sl_power_manager.h"
32 #include "sl_power_manager_config.h"
33 #include "sli_power_manager_private.h"
34 #include "sli_power_manager.h"
35 #include "sli_sleeptimer.h"
36 #include "sl_assert.h"
37 #include "sl_atomic.h"
38 
39 #include <stdlib.h>
40 #include <stdint.h>
41 #include <string.h>
42 
43 /*******************************************************************************
44  *********************************   DEFINES   *********************************
45  ******************************************************************************/
46 
47 #if ((SL_POWER_MANAGER_LOWEST_EM_ALLOWED != 1) \
48   && (SL_POWER_MANAGER_LOWEST_EM_ALLOWED != 2) \
49   && (SL_POWER_MANAGER_LOWEST_EM_ALLOWED != 3))
50 #error "Lowest Energy mode allowed is invalid."
51 #endif
52 
53 // Default overhead value for the wake-up time used for the schedule wake-up
54 // functionality.
55 #define SCHEDULE_WAKEUP_DEFAULT_RESTORE_TIME_OVERHEAD_TICK  0
56 
57 /*******************************************************************************
58  ***************************  LOCAL VARIABLES   ********************************
59  ******************************************************************************/
60 
61 // Initialization flag.
62 static bool is_initialized = false;
63 
64 // Current active energy mode.
65 static sl_power_manager_em_t current_em = SL_POWER_MANAGER_EM0;
66 
67 // Events subscribers lists
68 static sl_slist_node_t *power_manager_em_transition_event_list = NULL;
69 
70 #if (SL_POWER_MANAGER_LOWEST_EM_ALLOWED != 1)
71 // Store the sleeptimer module clock frequency for conversion calculation
72 static uint32_t sleeptimer_frequency;
73 
74 // Table of energy modes counters. Each counter indicates the presence (not zero)
75 // or absence (zero) of requirements on a given energy mode. The table doesn't
76 // contain requirement on EM0.
77 static uint8_t requirement_em_table[SLI_POWER_MANAGER_EM_TABLE_SIZE] = {
78   0,  // EM1 requirement counter
79   0,  // EM2 requirement counter
80 };
81 
82 // Counter variable to save the number of High Accuracy HF clock requirements requested.
83 uint8_t requirement_high_accuracy_hf_clock_counter = 0;
84 
85 #ifdef SLI_DEVICE_SUPPORTS_EM1P
86 // Variable to indicate if the High Accuracy HF clock requirements count is back to zero.
87 bool requirement_high_accuracy_hf_clock_back_to_zero = false;
88 #endif
89 
90 // Saved energy mode we are coming from when waiting for HFXO ready.
91 static sl_power_manager_em_t waiting_clock_restore_from_em = SL_POWER_MANAGER_EM0;
92 
93 // Flag indicating if we are sleeping, waiting for the HF clock restore
94 static bool is_sleeping_waiting_for_clock_restore = false;
95 
96 // Flag indicating if the system states (clocks) are saved and should be restored
97 static bool is_states_saved = false;
98 
99 // Timer that it is used for enabling the clock for the scheduled wakeup
100 static sl_sleeptimer_timer_handle_t clock_wakeup_timer_handle = { 0 };
101 
102 // Store if requirement on EM1 has been added before sleeping;
103 // i.e. only possible if sleeping for less than minimum off time
104 static bool requirement_on_em1_added = false;
105 
106 // Threshold delay in sleeptimer ticks indicating the minimum time required
107 // to make the shut down of external high frequency oscillator worthwhile before
108 // the next synchronous high frequency oscillator requirement. Shorter than this
109 // delay, the power gain of shutting down is invalidated.
110 uint32_t high_frequency_min_offtime_tick = 0;
111 
112 // Store the configuration overhead value in sleeptimer tick to add/remove to the wake-up time.
113 int32_t wakeup_time_config_overhead_tick = 0;
114 
115 static bool is_hf_x_oscillator_not_preserved;
116 
117 // Store if we are currently waiting for HF clock restoration to finish
118 static bool is_actively_waiting_for_clock_restore = false;
119 
120 // Indicates if the clock restore was completed from the HFXO ISR
121 static bool is_restored_from_hfxo_isr = false;
122 static bool is_restored_from_hfxo_isr_internal = false;
123 #endif
124 
125 /*
126  *********************************************************************************************************
127  *                                           HOOK REFERENCES
128  *********************************************************************************************************
129  */
130 
131 bool sl_power_manager_sleep_on_isr_exit(void);
132 
133 // Callback to application after wakeup but before restoring interrupts.
134 // For internal Silicon Labs use only
135 __WEAK void sli_power_manager_on_wakeup(void);
136 
137 // Hook that can be used by the log outputer to suspend transmission of logs
138 // in case it would require energy mode changes while in the sleep loop.
139 __WEAK void sli_power_manager_suspend_log_transmission(void);
140 
141 // Hook that can be used by the log outputer to resume transmission of logs.
142 __WEAK void sli_power_manager_resume_log_transmission(void);
143 
144 // Callback to notify possible transition from EM1P to EM2.
145 // For internal Silicon Labs use only
146 #ifdef SLI_DEVICE_SUPPORTS_EM1P
147 __WEAK void sli_power_manager_em1p_to_em2_notification(void);
148 #endif
149 
150 /***************************************************************************//**
151  * Mandatory callback that allows to cancel sleeping action.
152  ******************************************************************************/
153 bool sl_power_manager_is_ok_to_sleep(void);
154 
155 /*******************************************************************************
156  **************************   LOCAL FUNCTIONS   ********************************
157  ******************************************************************************/
158 
159 #if (SL_POWER_MANAGER_LOWEST_EM_ALLOWED != 1)
160 static sl_power_manager_em_t get_lowest_em(void);
161 
162 static void evaluate_wakeup(sl_power_manager_em_t to);
163 
164 static void update_em1_requirement(bool add);
165 
166 static void on_clock_wakeup_timeout(sl_sleeptimer_timer_handle_t *handle,
167                                     void *data);
168 
169 static void clock_restore_and_wait(void);
170 
171 static void clock_restore(void);
172 #endif
173 
174 static void power_manager_notify_em_transition(sl_power_manager_em_t from,
175                                                sl_power_manager_em_t to);
176 
177 /*******************************************************************************
178  **************************   GLOBAL FUNCTIONS   *******************************
179  ******************************************************************************/
180 
181 /***************************************************************************//**
182  * Initialize Power Manager module.
183  ******************************************************************************/
sl_power_manager_init(void)184 sl_status_t sl_power_manager_init(void)
185 {
186   CORE_DECLARE_IRQ_STATE;
187 
188   CORE_ENTER_CRITICAL();
189   if (!is_initialized) {
190     sl_status_t status = SL_STATUS_OK;
191 
192     // Initialize Sleeptimer module in case not already done.
193     status = sl_sleeptimer_init();
194     if (status != SL_STATUS_OK) {
195       CORE_EXIT_CRITICAL();
196       return status;
197     }
198 
199   #if (SL_POWER_MANAGER_DEBUG == 1)
200     sli_power_manager_debug_init();
201   #endif
202     sl_slist_init(&power_manager_em_transition_event_list);
203 
204 #if (SL_POWER_MANAGER_LOWEST_EM_ALLOWED != 1)
205     // Set the default wake-up overhead value
206     wakeup_time_config_overhead_tick = SCHEDULE_WAKEUP_DEFAULT_RESTORE_TIME_OVERHEAD_TICK;
207 
208     // Get the sleeptimer frequency
209     sleeptimer_frequency = sl_sleeptimer_get_timer_frequency();
210 #endif
211   }
212 
213   // Do all necessary hardware initialization.
214   sli_power_manager_init_hardware();
215 
216 #if (SL_POWER_MANAGER_LOWEST_EM_ALLOWED != 1)
217   // Set the HF minimum offtime in sleeptimer ticks
218   high_frequency_min_offtime_tick = sli_power_manager_get_default_high_frequency_minimum_offtime();
219 #endif
220 
221   is_initialized = true;
222   CORE_EXIT_CRITICAL();
223 
224   return SL_STATUS_OK;
225 }
226 
227 /***************************************************************************//**
228  * Sleep at the lowest allowed energy mode.
229  ******************************************************************************/
sl_power_manager_sleep(void)230 void sl_power_manager_sleep(void)
231 {
232   CORE_DECLARE_IRQ_STATE;
233 
234   CORE_ENTER_CRITICAL();
235 
236   sli_power_manager_suspend_log_transmission();
237 
238   if (sl_power_manager_is_ok_to_sleep() != true) {
239     sli_power_manager_resume_log_transmission();
240     CORE_EXIT_CRITICAL();
241     return;
242   }
243 
244 #if (SL_POWER_MANAGER_LOWEST_EM_ALLOWED != 1)
245   sl_power_manager_em_t lowest_em;
246 
247   // Go to another energy mode (same, higher to lower or lower to higher)
248   do {
249     // Remove any previous EM1 requirement added internally by the power manager itself
250     if (requirement_on_em1_added) {
251       update_em1_requirement(false);
252       requirement_on_em1_added = false;
253     }
254 
255     lowest_em = get_lowest_em();
256     evaluate_wakeup(lowest_em);
257     lowest_em = get_lowest_em();  // Reevaluate as a requirement can be added from evaluate_wakeup()
258 
259     if ((lowest_em >= SL_POWER_MANAGER_EM2)
260         && (is_states_saved == false)) {
261       sli_power_manager_save_states();
262     }
263 
264     // Notify listeners if transition to another energy mode
265     if (lowest_em != current_em) {
266 #ifdef SLI_DEVICE_SUPPORTS_EM1P
267       requirement_high_accuracy_hf_clock_back_to_zero = false;
268 #endif
269       if (is_sleeping_waiting_for_clock_restore == false) {
270         // But only notify if we are not in the process of waiting for the HF oscillators restore.
271         power_manager_notify_em_transition(current_em, lowest_em);
272       }
273       current_em = lowest_em;           // Keep new active energy mode
274     }
275 
276 #ifdef SLI_DEVICE_SUPPORTS_EM1P
277     // Notification for possible transition from EM1P to EM2
278     // For internal Silicon Labs use only
279     if (requirement_high_accuracy_hf_clock_back_to_zero
280         && current_em == SL_POWER_MANAGER_EM2) {
281       requirement_high_accuracy_hf_clock_back_to_zero = false;
282       sli_power_manager_em1p_to_em2_notification();
283     }
284 #endif
285 
286     // Pre-sleep operations if any are necessary
287     if ((lowest_em >= SL_POWER_MANAGER_EM2)
288         && (is_states_saved == false)) {
289       // Only do pre-sleep operations if there is no requirement on High Accuracy Clock.
290       // Else we must not touch the clock tree.
291       if (requirement_high_accuracy_hf_clock_counter == 0) {
292         sli_power_manager_handle_pre_deepsleep_operations();
293         is_hf_x_oscillator_not_preserved = true;
294       }
295       is_states_saved = true;
296     }
297 
298     // Apply lowest reachable energy mode
299     sli_power_manager_apply_em(current_em);
300 
301     // In case we are waiting for the restore from an early wake-up,
302     // we put back the current EM to the one before the early wake-up to do the next notification correctly.
303     if (is_sleeping_waiting_for_clock_restore == true) {
304       current_em = waiting_clock_restore_from_em;
305     }
306 
307     // Notify consumer of wakeup while interrupts are still off
308     // For internal Silicon Labs use only
309     sli_power_manager_on_wakeup();
310 
311     CORE_EXIT_CRITICAL();
312     CORE_ENTER_CRITICAL();
313 
314     // In case the HF restore was completed from the HFXO ISR,
315     // and notification not done elsewhere, do it here
316     if (is_restored_from_hfxo_isr_internal == true) {
317       is_restored_from_hfxo_isr_internal = false;
318       if (current_em == waiting_clock_restore_from_em) {
319         current_em = SL_POWER_MANAGER_EM1;
320         power_manager_notify_em_transition(waiting_clock_restore_from_em, SL_POWER_MANAGER_EM1);
321       }
322     }
323 
324     // Stop the internal power manager sleeptimer.
325     sl_sleeptimer_stop_timer(&clock_wakeup_timer_handle);
326   } while (sl_power_manager_sleep_on_isr_exit() == true);
327 
328 #ifdef SLI_DEVICE_SUPPORTS_EM1P
329   requirement_high_accuracy_hf_clock_back_to_zero = false;
330 #endif
331 
332   if (is_states_saved == true) {
333     is_sleeping_waiting_for_clock_restore = false;
334     // Restore clocks
335     if (is_hf_x_oscillator_not_preserved) {
336       sli_power_manager_restore_high_freq_accuracy_clk();
337       is_hf_x_oscillator_not_preserved = false;
338     }
339     // If possible, go back to sleep in EM1 while waiting for HF accuracy restore
340     while (!sli_power_manager_is_high_freq_accuracy_clk_ready(false)) {
341       sli_power_manager_apply_em(SL_POWER_MANAGER_EM1);
342       CORE_EXIT_CRITICAL();
343       CORE_ENTER_CRITICAL();
344     }
345     sli_power_manager_restore_states();
346     is_states_saved = false;
347   }
348 
349   evaluate_wakeup(SL_POWER_MANAGER_EM0);
350 #else
351   current_em = SL_POWER_MANAGER_EM1;
352 
353   // Notify listeners of transition to EM1
354   power_manager_notify_em_transition(SL_POWER_MANAGER_EM0, SL_POWER_MANAGER_EM1);
355   do {
356     // Apply EM1 energy mode
357     sli_power_manager_apply_em(SL_POWER_MANAGER_EM1);
358 
359     CORE_EXIT_CRITICAL();
360     CORE_ENTER_CRITICAL();
361   } while (sl_power_manager_sleep_on_isr_exit() == true);
362 #endif
363 
364   // Indicate back to EM0
365   power_manager_notify_em_transition(current_em, SL_POWER_MANAGER_EM0);
366   current_em = SL_POWER_MANAGER_EM0;
367 
368   sli_power_manager_resume_log_transmission();
369 
370   CORE_EXIT_CRITICAL();
371 }
372 
373 /***************************************************************************//**
374  * Updates requirement on the given energy mode.
375  *
376  * @param   em    Energy mode. Possible values are:
377  *                SL_POWER_MANAGER_EM1
378  *                SL_POWER_MANAGER_EM2
379  *
380  * @param   add   Flag indicating if requirement is added (true) or removed
381  *                (false).
382  *
383  * @note Need to be call inside a critical section.
384  *
385  * @note This function will do nothing when SL_POWER_MANAGER_LOWEST_EM_ALLOWED
386  *       config is set to EM1.
387  ******************************************************************************/
sli_power_manager_update_em_requirement(sl_power_manager_em_t em,bool add)388 void sli_power_manager_update_em_requirement(sl_power_manager_em_t em,
389                                              bool add)
390 {
391 #if (SL_POWER_MANAGER_LOWEST_EM_ALLOWED != 1)
392   // EM0 is not allowed
393   EFM_ASSERT((em > SL_POWER_MANAGER_EM0) && (em < SL_POWER_MANAGER_EM3));
394 
395   // Cannot increment above 255 (wraparound not allowed)
396   EFM_ASSERT(!((requirement_em_table[em - 1] == UINT8_MAX) && (add == true)));
397   // Cannot decrement below 0 (wraparound not allowed)
398   EFM_ASSERT(!((requirement_em_table[em - 1] == 0) && (add == false)));
399 
400   // Increment (add) or decrement (remove) energy mode counter.
401   requirement_em_table[em - 1] += (add) ? 1 : -1;
402 
403   if (add == true
404       && current_em >= SL_POWER_MANAGER_EM2) {  // if currently sleeping at a level that can require a clock restore; i.e. called from ISR
405     sl_power_manager_em_t lowest_em;
406     // If requirement added when sleeping, restore the clock before continuing the processing.
407     // Retrieve lowest reachable energy mode
408     lowest_em = get_lowest_em();
409 
410     if (lowest_em <= SL_POWER_MANAGER_EM1) {
411       // If new lowest requirement is greater than the current
412       // Restore clock; Everything is restored (HF and LF Clocks), the sleep loop will
413       // shutdown the clocks when returning sleeping
414       clock_restore_and_wait();
415     } else if (current_em == SL_POWER_MANAGER_EM3
416                && lowest_em == SL_POWER_MANAGER_EM2) {
417       // Restore LF clocks if we are transitioning from EM3 to EM2
418       sli_power_manager_low_frequency_restore();
419     }
420 
421     if (current_em != lowest_em) {
422       power_manager_notify_em_transition(current_em, lowest_em);
423       current_em = lowest_em;           // Keep new active energy mode
424     }
425   }
426 #else
427   (void)em;
428   (void)add;
429 #endif
430 }
431 
432 #if defined(SLI_DEVICE_SUPPORTS_EM1P)
433 /***************************************************************************//**
434  * Updates requirement on preservation of High Frequency Clocks settings.
435  *
436  * @param   add   Flag indicating if requirement is added (true) or removed
437  *                (false).
438  ******************************************************************************/
sli_power_manager_update_hf_clock_settings_preservation_requirement(bool add)439 void sli_power_manager_update_hf_clock_settings_preservation_requirement(bool add)
440 {
441 #if (SL_POWER_MANAGER_LOWEST_EM_ALLOWED != 1)
442   CORE_DECLARE_IRQ_STATE;
443 
444   CORE_ENTER_CRITICAL();
445   // Cannot increment above 255 (wraparound not allowed)
446   EFM_ASSERT(!((requirement_high_accuracy_hf_clock_counter == UINT8_MAX) && (add == true)));
447   // Cannot decrement below 0 (wraparound not allowed)
448   EFM_ASSERT(!((requirement_high_accuracy_hf_clock_counter == 0) && (add == false)));
449 
450   // Cannot add requirement if the "normal" clock settings are not currently applied
451   EFM_ASSERT(!((current_em > SL_POWER_MANAGER_EM2) && (add == true)));
452 
453   // Increment (add) or decrement (remove) energy mode counter.
454   requirement_high_accuracy_hf_clock_counter += (add) ? 1 : -1;
455 
456   // Save if the requirement is back to zero.
457   requirement_high_accuracy_hf_clock_back_to_zero = (requirement_high_accuracy_hf_clock_counter == 0) ? true : false;
458 
459   CORE_EXIT_CRITICAL();
460 #else
461   (void)add;
462 #endif
463 }
464 #endif
465 
466 /***************************************************************************//**
467  * Gets the wake-up restore process time.
468  * If we are not in the context of a deepsleep and therefore don't need to
469  * do a restore, the return value is 0.
470  *
471  * @return   Wake-up restore process time.
472  ******************************************************************************/
sli_power_manager_get_restore_delay(void)473 uint32_t sli_power_manager_get_restore_delay(void)
474 {
475 #if (SL_POWER_MANAGER_LOWEST_EM_ALLOWED != 1)
476   uint32_t wakeup_delay = 0;
477   CORE_DECLARE_IRQ_STATE;
478 
479   CORE_ENTER_CRITICAL();
480 
481   // If we are not currently in deepsleep, not need for any clock restore
482   if (current_em <= SL_POWER_MANAGER_EM1) {
483     CORE_EXIT_CRITICAL();
484     return wakeup_delay;
485   }
486 
487   // Get the clock restore delay
488   wakeup_delay = sl_power_manager_schedule_wakeup_get_restore_overhead_tick();
489   wakeup_delay += sli_power_manager_get_wakeup_process_time_overhead();
490 
491   CORE_EXIT_CRITICAL();
492 
493   return wakeup_delay;
494 #else
495   return 0;
496 #endif
497 }
498 
499 /***************************************************************************//**
500  * Initiates the wake-up restore process.
501  ******************************************************************************/
sli_power_manager_initiate_restore(void)502 void sli_power_manager_initiate_restore(void)
503 {
504 #if (SL_POWER_MANAGER_LOWEST_EM_ALLOWED != 1)
505   CORE_DECLARE_IRQ_STATE;
506 
507   CORE_ENTER_CRITICAL();
508 
509   // Start restore process
510   clock_restore();
511 
512   CORE_EXIT_CRITICAL();
513 #endif
514 }
515 
516 /***************************************************************************//**
517  * Registers a callback to be called on given Energy Mode transition(s).
518  *
519  * @note Adding/Removing requirement(s) from the callback is not supported.
520  ******************************************************************************/
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)521 void sl_power_manager_subscribe_em_transition_event(sl_power_manager_em_transition_event_handle_t *event_handle,
522                                                     const sl_power_manager_em_transition_event_info_t *event_info)
523 {
524   CORE_DECLARE_IRQ_STATE;
525 
526   event_handle->info = (sl_power_manager_em_transition_event_info_t *)event_info;
527   CORE_ENTER_CRITICAL();
528   sl_slist_push(&power_manager_em_transition_event_list, &event_handle->node);
529   CORE_EXIT_CRITICAL();
530 }
531 
532 /***************************************************************************//**
533  * Unregisters an event callback handle on Energy mode transition.
534  ******************************************************************************/
sl_power_manager_unsubscribe_em_transition_event(sl_power_manager_em_transition_event_handle_t * event_handle)535 void sl_power_manager_unsubscribe_em_transition_event(sl_power_manager_em_transition_event_handle_t *event_handle)
536 {
537   CORE_DECLARE_IRQ_STATE;
538 
539   CORE_ENTER_CRITICAL();
540   sl_slist_remove(&power_manager_em_transition_event_list, &event_handle->node);
541   CORE_EXIT_CRITICAL();
542 }
543 
544 /***************************************************************************//**
545  * Get configurable overhead value for early restore time in Sleeptimer ticks
546  * when a schedule wake-up is set.
547  *
548  * @return  Current overhead value for early wake-up time.
549  *
550  * @note This function will return 0 in case SL_POWER_MANAGER_LOWEST_EM_ALLOWED
551  *       config is set to EM1.
552  ******************************************************************************/
sl_power_manager_schedule_wakeup_get_restore_overhead_tick(void)553 int32_t sl_power_manager_schedule_wakeup_get_restore_overhead_tick(void)
554 {
555 #if (SL_POWER_MANAGER_LOWEST_EM_ALLOWED != 1)
556   int32_t overhead_tick;
557 
558   sl_atomic_load(overhead_tick, wakeup_time_config_overhead_tick);
559   return overhead_tick;
560 #else
561   return 0;
562 #endif
563 }
564 
565 /***************************************************************************//**
566  * Set configurable overhead value for early restore time in Sleeptimer ticks
567  * used for schedule wake-up.
568  * Must be called after initialization else the value will be overwritten.
569  *
570  * @param overhead_tick Overhead value to set for early restore time.
571  *
572  * @note The overhead value can also be negative to remove time from the restore
573  *       process.
574  *
575  * @note This function will do nothing when SL_POWER_MANAGER_LOWEST_EM_ALLOWED
576  *       config is set to EM1.
577  ******************************************************************************/
sl_power_manager_schedule_wakeup_set_restore_overhead_tick(int32_t overhead_tick)578 void sl_power_manager_schedule_wakeup_set_restore_overhead_tick(int32_t overhead_tick)
579 {
580 #if (SL_POWER_MANAGER_LOWEST_EM_ALLOWED != 1)
581   sl_atomic_store(wakeup_time_config_overhead_tick, overhead_tick);
582 #else
583   (void)overhead_tick;
584 #endif
585 }
586 
587 /***************************************************************************//**
588  * Get configurable minimum off-time value for schedule wake-up in Sleeptimer
589  * ticks.
590  *
591  * @return  Current minimum off-time value for schedule wake-up.
592  *
593  * @note  Turning on external high frequency oscillator, such as HFXO, requires
594  *        more energy since we must supply higher current for the wake-up.
595  *        Therefore, when an 'external high frequency oscillator enable' is
596  *        scheduled in 'x' time, there is a threshold 'x' value where turning
597  *        off the oscillator is not worthwhile since the energy consumed by
598  *        taking into account the wake-up will be greater than if we just keep
599  *        the oscillator on until the next scheduled oscillator enabled. This
600  *        threshold value is what we refer as the minimum off-time.
601  *
602  * @note This function will return 0 in case SL_POWER_MANAGER_LOWEST_EM_ALLOWED
603  *       config is set to EM1.
604  ******************************************************************************/
sl_power_manager_schedule_wakeup_get_minimum_offtime_tick(void)605 uint32_t sl_power_manager_schedule_wakeup_get_minimum_offtime_tick(void)
606 {
607 #if (SL_POWER_MANAGER_LOWEST_EM_ALLOWED != 1)
608   uint32_t offtime_tick;
609 
610   sl_atomic_load(offtime_tick, high_frequency_min_offtime_tick);
611   return offtime_tick;
612 #else
613   return 0;
614 #endif
615 }
616 
617 /***************************************************************************//**
618  * Set configurable minimum off-time value for schedule wake-up in Sleeptimer
619  * ticks.
620  *
621  * @param minimum_offtime_tick  minimum off-time value to set for schedule
622  *                              wake-up.
623  *
624  * @note  Turning on external high frequency oscillator, such as HFXO, requires
625  *        more energy since we must supply higher current for the wake-up.
626  *        Therefore, when an 'external high frequency oscillator enable' is
627  *        scheduled in 'x' time, there is a threshold 'x' value where turning
628  *        off the oscillator is not worthwhile since the energy consumed by
629  *        taking into account the wake-up will be greater than if we just keep
630  *        the oscillator on until the next scheduled oscillator enabled. This
631  *        threshold value is what we refer as the minimum off-time.
632  *
633  * @note This function will do nothing when SL_POWER_MANAGER_LOWEST_EM_ALLOWED
634  *       config is set to EM1.
635  ******************************************************************************/
sl_power_manager_schedule_wakeup_set_minimum_offtime_tick(uint32_t minimum_offtime_tick)636 void sl_power_manager_schedule_wakeup_set_minimum_offtime_tick(uint32_t minimum_offtime_tick)
637 {
638 #if (SL_POWER_MANAGER_LOWEST_EM_ALLOWED != 1)
639   sl_atomic_store(high_frequency_min_offtime_tick, minimum_offtime_tick);
640 #else
641   (void)minimum_offtime_tick;
642 #endif
643 }
644 
645 #if (SL_POWER_MANAGER_LOWEST_EM_ALLOWED != 1)
646 /*******************************************************************************
647  * Converts microseconds time in sleeptimer ticks.
648  ******************************************************************************/
sli_power_manager_convert_delay_us_to_tick(uint32_t time_us)649 uint32_t sli_power_manager_convert_delay_us_to_tick(uint32_t time_us)
650 {
651   return (((time_us * sleeptimer_frequency) + (1000000 - 1)) / 1000000);
652 }
653 #endif
654 
655 /***************************************************************************//**
656  * Last-chance check before sleep.
657  *
658  * @return  True, if the system should actually sleep.
659  *          False, if not.
660  *
661  * @note This is the fallback implementation of the callback, it can be
662  *       overridden by the application or other components.
663  ******************************************************************************/
sl_power_manager_is_ok_to_sleep(void)664 __WEAK bool sl_power_manager_is_ok_to_sleep(void)
665 {
666   return true;
667 }
668 
669 /***************************************************************************//**
670  * Check if the MCU can sleep after an interrupt.
671  *
672  * @return  True, if the system can sleep after the interrupt.
673  *          False, otherwise.
674  *
675  * @note This is the fallback implementation of the callback, it can be
676  *       overridden by the application or other components.
677  ******************************************************************************/
sl_power_manager_sleep_on_isr_exit(void)678 __WEAK bool sl_power_manager_sleep_on_isr_exit(void)
679 {
680   return false;
681 }
682 
683 /**************************************************************************//**
684  * Determines if the HFXO interrupt was part of the last wake-up and/or if
685  * the HFXO early wakeup expired during the last ISR
686  * and if it was the only timer to expire in that period.
687  *
688  * @return true if power manager sleep can return to sleep,
689  *         false otherwise.
690  *
691  * @note This function will always return false in case
692  *       SL_POWER_MANAGER_LOWEST_EM_ALLOWED config is set to EM1, since we will
693  *       never sleep at a lower level than EM1.
694  *****************************************************************************/
sl_power_manager_is_latest_wakeup_internal(void)695 bool sl_power_manager_is_latest_wakeup_internal(void)
696 {
697 #if (SL_POWER_MANAGER_LOWEST_EM_ALLOWED != 1)
698   CORE_DECLARE_IRQ_STATE;
699   bool sleep;
700 
701   CORE_ENTER_CRITICAL();
702   sleep = is_restored_from_hfxo_isr;
703   is_restored_from_hfxo_isr = false;
704   CORE_EXIT_CRITICAL();
705 
706   sleep |= sl_sleeptimer_is_power_manager_early_restore_timer_latest_to_expire();
707   return sleep;
708 #else
709   return false;
710 #endif
711 }
712 
713 /*******************************************************************************
714  **************************   LOCAL FUNCTIONS   ********************************
715  ******************************************************************************/
716 
717 #if (SL_POWER_MANAGER_LOWEST_EM_ALLOWED != 1)
718 /***************************************************************************//**
719  * Get lowest energy mode to apply given the requirements on the different
720  * energy modes.
721  *
722  * @return  Lowest energy mode: EM1, EM2 or EM3.
723  *
724  * @note If no requirement for any energy mode (EM1 and EM2), lowest energy mode
725  * is EM3.
726  ******************************************************************************/
get_lowest_em(void)727 static sl_power_manager_em_t get_lowest_em(void)
728 {
729   uint32_t em_ix;
730   sl_power_manager_em_t em;
731 
732   // Retrieve lowest Energy mode allowed given the requirements
733   for (em_ix = 1; (em_ix < SL_POWER_MANAGER_LOWEST_EM_ALLOWED) && (requirement_em_table[em_ix - 1] == 0); em_ix++) {
734     ;
735   }
736 
737   em = (sl_power_manager_em_t)em_ix;
738 
739   return em;
740 }
741 #endif
742 
743 /***************************************************************************//**
744  * Notify subscribers about energy mode transition.
745  *
746  * @param  from  Energy mode from which CPU comes from.
747  *
748  * @param  to    Energy mode to which CPU is going to.
749  ******************************************************************************/
power_manager_notify_em_transition(sl_power_manager_em_t from,sl_power_manager_em_t to)750 static void power_manager_notify_em_transition(sl_power_manager_em_t from,
751                                                sl_power_manager_em_t to)
752 {
753   sl_power_manager_em_transition_event_handle_t *handle;
754   sl_power_manager_em_transition_event_t transition = 0;
755 
756   switch (to) {
757     case SL_POWER_MANAGER_EM0:
758       transition = SL_POWER_MANAGER_EVENT_TRANSITION_ENTERING_EM0;
759       break;
760 
761     case SL_POWER_MANAGER_EM1:
762       transition = SL_POWER_MANAGER_EVENT_TRANSITION_ENTERING_EM1;
763       break;
764 
765     case SL_POWER_MANAGER_EM2:
766       transition = SL_POWER_MANAGER_EVENT_TRANSITION_ENTERING_EM2;
767       break;
768 
769     case SL_POWER_MANAGER_EM3:
770       transition = SL_POWER_MANAGER_EVENT_TRANSITION_ENTERING_EM3;
771       break;
772 
773     default:
774       EFM_ASSERT(0);
775   }
776 
777   switch (from) {
778     case SL_POWER_MANAGER_EM0:
779       transition |= SL_POWER_MANAGER_EVENT_TRANSITION_LEAVING_EM0;
780       break;
781 
782     case SL_POWER_MANAGER_EM1:
783       transition |= SL_POWER_MANAGER_EVENT_TRANSITION_LEAVING_EM1;
784       break;
785 
786     case SL_POWER_MANAGER_EM2:
787       transition |= SL_POWER_MANAGER_EVENT_TRANSITION_LEAVING_EM2;
788       break;
789 
790     case SL_POWER_MANAGER_EM3:
791       transition |= SL_POWER_MANAGER_EVENT_TRANSITION_LEAVING_EM3;
792       break;
793 
794     default:
795       EFM_ASSERT(0);
796   }
797 
798   SL_SLIST_FOR_EACH_ENTRY(power_manager_em_transition_event_list, handle, sl_power_manager_em_transition_event_handle_t, node) {
799     if ((handle->info->event_mask & transition) > 0) {
800       handle->info->on_event(from, to);
801     }
802   }
803 }
804 
805 /***************************************************************************//**
806  * Evaluates scheduled wakeup and restart timer based on the wakeup time.
807  * If the remaining time is shorter than the wakeup time then add a requirement
808  * on EM1 for avoiding the wakeup delay time.
809  *
810  * @note Must be called in a critical section.
811  ******************************************************************************/
812 #if (SL_POWER_MANAGER_LOWEST_EM_ALLOWED != 1)
evaluate_wakeup(sl_power_manager_em_t to)813 static void evaluate_wakeup(sl_power_manager_em_t to)
814 {
815   sl_status_t status;
816   uint32_t tick_remaining;
817 
818   switch (to) {
819     case SL_POWER_MANAGER_EM0:
820       // Coming back from Sleep.
821       if (requirement_on_em1_added) {
822         update_em1_requirement(false);
823         requirement_on_em1_added = false;
824       }
825       break;
826 
827     case SL_POWER_MANAGER_EM1:
828       // External high frequency clock, such as HFXO, already enabled; No wakeup delay
829       break;
830 
831     case SL_POWER_MANAGER_EM2:
832     case SL_POWER_MANAGER_EM3:
833       // Get the time remaining until the next sleeptimer requiring early wake-up
834       status = sl_sleeptimer_get_remaining_time_of_first_timer(0, &tick_remaining);
835       if (status == SL_STATUS_OK) {
836         if (tick_remaining <= high_frequency_min_offtime_tick) {
837           // Add EM1 requirement if time remaining is to short to be energy efficient
838           // if going back to deepsleep.
839           update_em1_requirement(true);
840           requirement_on_em1_added = true;
841         } else {
842           int32_t wakeup_delay = 0;
843           int32_t cfg_overhead_tick = 0;
844 
845           // Calculate overall wake-up delay.
846           sl_atomic_load(cfg_overhead_tick, wakeup_time_config_overhead_tick);
847           wakeup_delay += cfg_overhead_tick;
848           wakeup_delay += sli_power_manager_get_wakeup_process_time_overhead();
849           EFM_ASSERT(wakeup_delay >= 0);
850           if (tick_remaining <= (uint32_t)wakeup_delay) {
851             // Add EM1 requirement if time remaining is smaller than wake-up delay.
852             update_em1_requirement(true);
853             requirement_on_em1_added = true;
854           } else {
855             // Start internal sleeptimer to do the early wake-up.
856             sl_sleeptimer_restart_timer(&clock_wakeup_timer_handle,
857                                         (tick_remaining - (uint32_t)wakeup_delay),
858                                         on_clock_wakeup_timeout,
859                                         NULL,
860                                         0,
861                                         SLI_SLEEPTIMER_POWER_MANAGER_EARLY_WAKEUP_TIMER_FLAG);
862           }
863         }
864       }
865       break;
866 
867     default:
868       EFM_ASSERT(false);
869   }
870 }
871 #endif
872 
873 #if (SL_POWER_MANAGER_LOWEST_EM_ALLOWED != 1)
874 /***************************************************************************//**
875  * Updates internal EM1 requirement.
876  * We add an internal EM1 requirement when we would usually go into EM2/EM3
877  * but there is not enough time before the next schedule event requiring a
878  * clock restore. So we just go to sleep in EM1.
879  * We remove this internal EM1 requirement next time we wake-up.
880  *
881  * @param   add  true, to add EM1 requirement,
882  *               false, to remove EM1 requirement.
883  *
884  * @note For internal use only.
885  *
886  * @note Need to be call inside a critical section.
887  ******************************************************************************/
update_em1_requirement(bool add)888 static void update_em1_requirement(bool add)
889 {
890   // Cannot increment above 255 (wraparound not allowed)
891   EFM_ASSERT(!((requirement_em_table[SL_POWER_MANAGER_EM1 - 1] == UINT8_MAX) && (add == true)));
892   // Cannot decrement below 0 (wraparound not allowed)
893   EFM_ASSERT(!((requirement_em_table[SL_POWER_MANAGER_EM1 - 1] == 0) && (add == false)));
894 
895 #if (SL_POWER_MANAGER_DEBUG == 1)
896   sli_power_manager_debug_log_em_requirement(SL_POWER_MANAGER_EM1, add, "PM_INTERNAL_EM1_REQUIREMENT");
897 #endif
898 
899   // Increment (add) or decrement (remove) energy mode counter.
900   requirement_em_table[SL_POWER_MANAGER_EM1 - 1] += (add) ? 1 : -1;
901 
902   // In rare occasions a clock restore must be started here:
903   // - An asynchronous event wake-up the system from deepsleep very near the early wake-up event,
904   //   When we re-enter the sleep loop, we delete the internal early wake-up timer, but during
905   //   the evaluation before sleep, it is calculated that not enough time is remains to go to
906   //   deepsleep. In that case, since we deleted the early wake-up timer we must start the
907   //   restore process here.
908   // - A synchronous event is added during an ISR, when we evaluate if the timeout is bigger
909   //   than the clock restore time, it's barely bigger, so no clock restore process is started
910   //   at that time. But when we do the evaluate before sleep, the remaining time is now smaller
911   //   than the clock restore delay. So me must start the restore process here.
912   if (add == true
913       && current_em >= SL_POWER_MANAGER_EM2
914       && is_sleeping_waiting_for_clock_restore == false) {
915     clock_restore();
916   }
917 }
918 #endif
919 
920 #if (SL_POWER_MANAGER_LOWEST_EM_ALLOWED != 1)
921 /***************************************************************************//**
922  * Do clock restore process and wait for it to be completed.
923  ******************************************************************************/
clock_restore_and_wait(void)924 static void clock_restore_and_wait(void)
925 {
926   CORE_DECLARE_IRQ_STATE;
927 
928   CORE_ENTER_CRITICAL();
929   if (is_states_saved == true) {
930     if (is_actively_waiting_for_clock_restore == false) {
931       is_actively_waiting_for_clock_restore = true;
932 
933       // Since we will actively wait for clock restore, we cancel any current non-active wait.
934       is_sleeping_waiting_for_clock_restore = false;
935     }
936 
937     if (is_hf_x_oscillator_not_preserved) {
938       sli_power_manager_restore_high_freq_accuracy_clk();
939       is_hf_x_oscillator_not_preserved = false;
940     }
941 
942     CORE_EXIT_CRITICAL();
943     // We remove the critical section in case HFXO fails to startup and the HFXO Interrupt needs to run to handle the error.
944     sli_power_manager_is_high_freq_accuracy_clk_ready(true);
945     CORE_ENTER_CRITICAL();
946     if (is_actively_waiting_for_clock_restore) {
947       sli_power_manager_restore_states();
948       is_actively_waiting_for_clock_restore = false;
949     }
950 
951     is_states_saved = false;
952   }
953   CORE_EXIT_CRITICAL();
954 }
955 #endif
956 
957 #if (SL_POWER_MANAGER_LOWEST_EM_ALLOWED != 1)
958 /***************************************************************************//**
959  * Start clock restore process.
960  *
961  * @note Need to be call inside a critical section.
962  ******************************************************************************/
clock_restore(void)963 static void clock_restore(void)
964 {
965   // Check if we need to start the clock restore process
966   if (is_states_saved == true) {
967     if (is_hf_x_oscillator_not_preserved) {
968       sli_power_manager_restore_high_freq_accuracy_clk();
969       is_hf_x_oscillator_not_preserved = false;
970     }
971     if (sli_power_manager_is_high_freq_accuracy_clk_ready(false)) {
972       // Do the clock restore if the HF oscillator is already ready
973       sli_power_manager_restore_states();
974       is_states_saved = false;
975 
976       // We do the notification only when the restore is completed.
977       power_manager_notify_em_transition(current_em, SL_POWER_MANAGER_EM1);
978       current_em = SL_POWER_MANAGER_EM1; // Keep new active energy mode
979     } else {
980       // If the HF oscillator is not yet ready, we will go back to sleep while waiting
981       is_sleeping_waiting_for_clock_restore = true;
982 
983       // Save current EM to do the right notification later
984       waiting_clock_restore_from_em = current_em;
985     }
986   }
987 }
988 #endif
989 
990 #if (SL_POWER_MANAGER_LOWEST_EM_ALLOWED != 1)
991 /***************************************************************************//**
992  * Callback for clock enable timer.
993  *
994  * @param   handle  Pointer to sleeptimer handle
995  *
996  * @param   data    Pointer to callback data
997  *
998  * @note We restore the HF clocks and go to EM1 here to be ready in time for the
999  *       Application sleeptimer callback. But no EM1 requirement is added
1000  *       here. Since the time until the Application sleeptimer times out is <=
1001  *       than the wake-up delay, it protects us from going back to sleep lower
1002  *       than EM1. After that, it's up to the Application sleeptimer callback to
1003  *       put a EM1 requirement if still needed.
1004  ******************************************************************************/
on_clock_wakeup_timeout(sl_sleeptimer_timer_handle_t * handle,void * data)1005 static void on_clock_wakeup_timeout(sl_sleeptimer_timer_handle_t *handle,
1006                                     void *data)
1007 {
1008   (void)handle;
1009   (void)data;
1010   CORE_DECLARE_IRQ_STATE;
1011 
1012   CORE_ENTER_CRITICAL();
1013 
1014   if (is_actively_waiting_for_clock_restore) {
1015     // In case we are already actively waiting for HFXO ready in another ISR, just exit
1016     CORE_EXIT_CRITICAL();
1017     return;
1018   }
1019 
1020   // If needed start the clock restore process
1021   clock_restore();
1022 
1023   CORE_EXIT_CRITICAL();
1024 }
1025 #endif
1026 
1027 /***************************************************************************//**
1028  * HFXO ready notification callback for internal use with power manager
1029  *
1030  * @note Will only be used on series 2 devices when HFXO Manager is present.
1031  ******************************************************************************/
sli_hfxo_manager_notify_ready_for_power_manager(void)1032 void sli_hfxo_manager_notify_ready_for_power_manager(void)
1033 {
1034 #if (SL_POWER_MANAGER_LOWEST_EM_ALLOWED != 1)
1035   // Complete HF restore and change current Energy mode
1036   // The notification will be done once back in the sleep loop
1037   if (current_em != SL_POWER_MANAGER_EM0
1038       && is_sleeping_waiting_for_clock_restore == true) {
1039     sli_power_manager_restore_states();
1040     is_sleeping_waiting_for_clock_restore = false;
1041     is_states_saved = false;
1042     is_restored_from_hfxo_isr = true;
1043     is_restored_from_hfxo_isr_internal = true;
1044   }
1045 #endif
1046 }
1047 
1048 #if defined(EMU_VSCALE_PRESENT)
1049 /***************************************************************************//**
1050  * Enable or disable fast wake-up in EM2 and EM3
1051  *
1052  * @note Will also update the wake up time from EM2 to EM0.
1053  *
1054  * @note This function will do nothing when SL_POWER_MANAGER_LOWEST_EM_ALLOWED
1055  *       config is set to EM1.
1056  ******************************************************************************/
sl_power_manager_em23_voltage_scaling_enable_fast_wakeup(bool enable)1057 void sl_power_manager_em23_voltage_scaling_enable_fast_wakeup(bool enable)
1058 {
1059 #if (SL_POWER_MANAGER_LOWEST_EM_ALLOWED != 1)
1060   CORE_DECLARE_IRQ_STATE;
1061 
1062   CORE_ENTER_CRITICAL();
1063 
1064   sli_power_manager_em23_voltage_scaling_enable_fast_wakeup(enable);
1065 
1066   CORE_EXIT_CRITICAL();
1067 #else
1068   (void)enable;
1069 #endif
1070 }
1071 #endif
1072