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