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