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