1 /***************************************************************************//**
2  * @file
3  * @brief Power Manager HAL 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 "em_device.h"
32 #if defined(_SILICON_LABS_32B_SERIES_2)
33 
34 #include "em_emu.h"
35 #include "em_cmu.h"
36 #include "sl_assert.h"
37 #include "sl_power_manager.h"
38 #include "sli_power_manager.h"
39 #include "sli_power_manager_private.h"
40 #include "sl_sleeptimer.h"
41 #include "sli_sleeptimer.h"
42 #include "sl_power_manager_config.h"
43 
44 #if defined(_SILICON_LABS_32B_SERIES_2_CONFIG_2)
45 #include "em_iadc.h"
46 #include <stdlib.h>
47 #endif
48 
49 #if defined(SL_COMPONENT_CATALOG_PRESENT)
50 #include "sl_component_catalog.h"
51 #endif
52 
53 #if !defined(SL_CATALOG_POWER_MANAGER_NO_DEEPSLEEP_PRESENT) \
54   && !defined(SL_CATALOG_POWER_MANAGER_DEEPSLEEP_BLOCKING_HFXO_RESTORE_PRESENT)
55 #include "sli_hfxo_manager.h"
56 #endif
57 
58 #include <stdbool.h>
59 
60 /*******************************************************************************
61  *********************************   DEFINES   *********************************
62  ******************************************************************************/
63 
64 // Time required by the hardware to come out of EM2 in microseconds.
65 // This value includes HW startup, emlib and sleepdrv execution time.
66 // Voltage scaling, HFXO startup and HFXO steady times are excluded from
67 // this because they are handled separately. RTCCSYNC time is also
68 // excluded and it is handled by RTCCSYNC code itself.
69 #if (_SILICON_LABS_32B_SERIES_2_CONFIG == 1)
70 #define EM2_WAKEUP_PROCESS_TIME_OVERHEAD_US (31u)
71 #else // (_SILICON_LABS_32B_SERIES_2_CONFIG == 2),
72 #define EM2_WAKEUP_PROCESS_TIME_OVERHEAD_US (31u)
73 #endif
74 
75 // DPLL Locking delay related defines
76 #define DPLL_COARSECOUNT_VALUE  (5u)
77 
78 // Time it takes to upscale voltage after EM2 in microseconds.
79 // This value represents the time for scaling from VSCALE0 to VSCALE2.
80 #define EM2_WAKEUP_VSCALE_OVERHEAD_US (64u)
81 
82 // Default time value in microseconds required to wake-up the hfxo oscillator.
83 #define HFXO_WAKE_UP_TIME_DEFAULT_VALUE_US  (400u)
84 
85 // high frequency oscillator wake-up time margin for possible variation
86 // A shift by 3 will be like a division by 8, so a percentage of 12.5%.
87 #define HFXO_START_UP_TIME_OVERHEAD_LOG2   3
88 
89 // Default time value in microseconds for the HFXO minimum off time.
90 #define HFXO_MINIMUM_OFFTIME_DEFAULT_VALUE_US  (400u)
91 
92 #if defined(SL_CATALOG_POWER_MANAGER_DEEPSLEEP_BLOCKING_HFXO_RESTORE_PRESENT)
93 // Table size of HFXO wake-up time measurement
94 #define HFXO_WAKE_UP_TIME_TABLE_SIZE  10
95 #endif
96 
97 // Defines for hidden HFXO0 DBGSTATUS register and STARTUPDONE flag
98 #define HFXO0_DBGSTATUS  (*(volatile uint32_t *)(HFXO0_BASE + 0x05C))
99 #define HFXO_DBGSTATUS_STARTUPDONE                  (0x1UL << 1)                               /**< Startup Done Status                         */
100 #define _HFXO_DBGSTATUS_STARTUPDONE_SHIFT           1                                          /**< Shift value for HFXO_STARTUPDONE            */
101 #define _HFXO_DBGSTATUS_STARTUPDONE_MASK            0x2UL                                      /**< Bit mask for HFXO_STARTUPDONE               */
102 
103 /*******************************************************************************
104  *******************************  MACROS   *************************************
105  ******************************************************************************/
106 
107 /*******************************************************************************
108 * DPLL lock time can be approximately calculated by the equation:
109 *   COARSECOUNT * (M + 1) * Tref
110 * Where
111 *   - COARSECOUNT is calibration value in a hidden register. Its default value
112 *     is 5 and should not change with calibration.
113 *   - M is one the DPLL configuration parameter.
114 *   - Tref is the reference clock period.
115 *******************************************************************************/
116 #define DPLL_LOCKING_DELAY_US_FUNCTION(M, freq_ref) \
117   (uint32_t)(((uint64_t)(DPLL_COARSECOUNT_VALUE * ((M) +1)) * 1000000 + ((freq_ref) - 1)) / (freq_ref))
118 
119 /*******************************************************************************
120  ***************************  LOCAL VARIABLES   ********************************
121  ******************************************************************************/
122 
123 #if !defined(SL_CATALOG_POWER_MANAGER_NO_DEEPSLEEP_PRESENT)
124 // Variables to save the relevant clock registers.
125 uint32_t cmu_em01_grpA_clock_register;
126 #if defined(_CMU_EM01GRPBCLKCTRL_CLKSEL_MASK)
127 uint32_t cmu_em01_grpB_clock_register;
128 #endif
129 #if defined(_CMU_EM01GRPCCLKCTRL_CLKSEL_MASK)
130 uint32_t cmu_em01_grpC_clock_register;
131 #endif
132 #if defined(_CMU_DPLLREFCLKCTRL_CLKSEL_MASK)
133 uint32_t cmu_dpll_ref_clock_register;
134 #endif
135 
136 uint32_t cmu_sys_clock_register;
137 
138 // Time in ticks required for the general wake-up process.
139 static uint32_t process_wakeup_overhead_tick = 0;
140 
141 #if defined(EMU_VSCALE_PRESENT)
142 static bool is_fast_wakeup_enabled = true;
143 #endif
144 
145 static bool is_hf_x_oscillator_used = false;
146 static bool is_dpll_used = false;
147 static bool is_entering_deepsleep = false;
148 
149 static bool is_hf_x_oscillator_already_started = false;
150 
151 #if defined(SL_CATALOG_POWER_MANAGER_DEEPSLEEP_BLOCKING_HFXO_RESTORE_PRESENT)
152 static uint32_t hf_x_oscillator_wakeup_time_tc_inital = 0;
153 
154 static uint32_t hfxo_wakeup_time_table[HFXO_WAKE_UP_TIME_TABLE_SIZE];
155 static uint8_t hfxo_wakeup_time_table_index = 0;
156 static uint32_t hfxo_wakeup_time_sum_average = 0;
157 
158 // Time in ticks required for HFXO start-up after wake-up from sleep.
159 static uint32_t hfxo_wakeup_time_tick = 0;
160 #endif
161 #endif
162 
163 /***************************************************************************//**
164  * Do some hardware initialization if necessary.
165  ******************************************************************************/
sli_power_manager_init_hardware(void)166 void sli_power_manager_init_hardware(void)
167 {
168   // Initializes EMU (voltage scaling in EM2/3)
169 #if defined(EMU_VSCALE_EM01_PRESENT)
170   EMU_EM01Init_TypeDef em01_init = EMU_EM01INIT_DEFAULT;
171 
172   EMU_EM01Init(&em01_init);
173 #endif
174 
175 #if !defined(SL_CATALOG_POWER_MANAGER_NO_DEEPSLEEP_PRESENT)
176 #if defined(EMU_VSCALE_PRESENT)
177 #if defined(SL_POWER_MANAGER_CONFIG_VOLTAGE_SCALING_FAST_WAKEUP)
178 #if (SL_POWER_MANAGER_CONFIG_VOLTAGE_SCALING_FAST_WAKEUP == 0)
179   sli_power_manager_em23_voltage_scaling_enable_fast_wakeup(false);
180 #else
181   sli_power_manager_em23_voltage_scaling_enable_fast_wakeup(true);
182 #endif
183 #else
184   sli_power_manager_em23_voltage_scaling_enable_fast_wakeup(false);
185 #endif
186 #endif
187 
188   // Get the current HF oscillator for the SYSCLK
189   cmu_sys_clock_register = CMU->SYSCLKCTRL & _CMU_SYSCLKCTRL_CLKSEL_MASK;
190 #if defined(_CMU_DPLLREFCLKCTRL_CLKSEL_MASK)
191   cmu_dpll_ref_clock_register = CMU->DPLLREFCLKCTRL & _CMU_DPLLREFCLKCTRL_CLKSEL_MASK;
192 #endif
193 
194 #if defined(CMU_CLKEN0_DPLL0)
195   CMU->CLKEN0_SET = CMU_CLKEN0_HFXO0;
196 
197   CMU->CLKEN0_SET = CMU_CLKEN0_DPLL0;
198 #endif
199 
200   is_dpll_used = ((DPLL0->STATUS & _DPLL_STATUS_ENS_MASK) != 0);
201 
202   is_hf_x_oscillator_used = ((cmu_sys_clock_register == CMU_SYSCLKCTRL_CLKSEL_HFXO)
203                              || ((CMU->EM01GRPACLKCTRL & _CMU_EM01GRPACLKCTRL_CLKSEL_MASK) == CMU_EM01GRPACLKCTRL_CLKSEL_HFXO));
204 
205 #if defined(_SILICON_LABS_32B_SERIES_2_CONFIG_1)
206   is_hf_x_oscillator_used = is_hf_x_oscillator_used || ((CMU->RADIOCLKCTRL & _CMU_RADIOCLKCTRL_EN_MASK) != 0);
207 #endif
208 
209 #if defined(CMU_EM01GRPBCLKCTRL_CLKSEL_HFXO)
210   is_hf_x_oscillator_used = is_hf_x_oscillator_used || ((CMU->EM01GRPBCLKCTRL & _CMU_EM01GRPBCLKCTRL_CLKSEL_MASK) == CMU_EM01GRPBCLKCTRL_CLKSEL_HFXO);
211 #endif
212 
213 #if defined(CMU_EM01GRPCCLKCTRL_CLKSEL_HFXO)
214   is_hf_x_oscillator_used = is_hf_x_oscillator_used || ((CMU->EM01GRPCCLKCTRL & _CMU_EM01GRPCCLKCTRL_CLKSEL_MASK) == CMU_EM01GRPCCLKCTRL_CLKSEL_HFXO);
215 #endif
216 
217 #if defined(SL_CATALOG_POWER_MANAGER_DEEPSLEEP_BLOCKING_HFXO_RESTORE_PRESENT)
218   // Set HFXO wakeup time to conservative default value
219   hfxo_wakeup_time_tick = sli_power_manager_convert_delay_us_to_tick(HFXO_WAKE_UP_TIME_DEFAULT_VALUE_US);
220   for (uint8_t i = 0; i < HFXO_WAKE_UP_TIME_TABLE_SIZE; i++) {
221     hfxo_wakeup_time_table[i] = hfxo_wakeup_time_tick;
222     hfxo_wakeup_time_sum_average += hfxo_wakeup_time_tick;
223   }
224 #endif
225 
226   if (is_dpll_used && !is_hf_x_oscillator_used) {
227     is_hf_x_oscillator_used = is_hf_x_oscillator_used || ((CMU->DPLLREFCLKCTRL & _CMU_DPLLREFCLKCTRL_CLKSEL_MASK) == _CMU_DPLLREFCLKCTRL_CLKSEL_HFXO);
228   }
229 
230   // Calculate DPLL locking delay from its configuration
231   if (is_dpll_used) {
232     uint32_t freq = 0;
233 
234     switch (CMU->DPLLREFCLKCTRL & _CMU_DPLLREFCLKCTRL_CLKSEL_MASK) {
235       case _CMU_DPLLREFCLKCTRL_CLKSEL_HFXO:
236         freq = SystemHFXOClockGet();
237         break;
238 
239       case _CMU_DPLLREFCLKCTRL_CLKSEL_LFXO:
240         freq = SystemLFXOClockGet();
241         break;
242 
243       case _CMU_DPLLREFCLKCTRL_CLKSEL_CLKIN0:
244         freq = SystemCLKIN0Get();
245         break;
246 
247       default:
248         EFM_ASSERT(false);
249         break;
250     }
251     if (freq > 0) { // Avoid division by 0
252       // Add DPLL Locking delay
253       process_wakeup_overhead_tick += sli_power_manager_convert_delay_us_to_tick(DPLL_LOCKING_DELAY_US_FUNCTION((DPLL0->CFG1 & _DPLL_CFG1_M_MASK) >> _DPLL_CFG1_M_SHIFT, freq));
254     }
255   }
256 
257   process_wakeup_overhead_tick += sli_power_manager_convert_delay_us_to_tick(EM2_WAKEUP_PROCESS_TIME_OVERHEAD_US);
258 
259 #endif
260 }
261 
262 /***************************************************************************//**
263  * Enable or disable fast wake-up in EM2 and EM3.
264  ******************************************************************************/
sli_power_manager_em23_voltage_scaling_enable_fast_wakeup(bool enable)265 void sli_power_manager_em23_voltage_scaling_enable_fast_wakeup(bool enable)
266 {
267 #if (defined(EMU_VSCALE_PRESENT) && !defined(SL_CATALOG_POWER_MANAGER_NO_DEEPSLEEP_PRESENT))
268 
269   if (enable == is_fast_wakeup_enabled) {
270     return;
271   }
272 
273   EMU_EM23Init_TypeDef em23_init = EMU_EM23INIT_DEFAULT;
274 
275   // Enable/disable EMU voltage scaling in EM2/3
276   if (enable) {
277     em23_init.vScaleEM23Voltage = emuVScaleEM23_FastWakeup;
278   } else {
279     em23_init.vScaleEM23Voltage = emuVScaleEM23_LowPower;
280   }
281 
282   EMU_EM23Init(&em23_init);
283 
284   // Calculate and add voltage scaling wake-up delays in ticks
285   if (enable) {
286     // Remove voltage scaling delay if it was added before
287     process_wakeup_overhead_tick -= sli_power_manager_convert_delay_us_to_tick(EM2_WAKEUP_VSCALE_OVERHEAD_US);
288   } else {
289     // Add voltage scaling delay if it was not added before
290     process_wakeup_overhead_tick += sli_power_manager_convert_delay_us_to_tick(EM2_WAKEUP_VSCALE_OVERHEAD_US);
291   }
292 
293   is_fast_wakeup_enabled = enable;
294 #else
295   (void)enable;
296 #endif
297 }
298 
299 #if !defined(SL_CATALOG_POWER_MANAGER_NO_DEEPSLEEP_PRESENT)
300 /***************************************************************************//**
301  * Save the CMU HF clock select state, oscillator enable, and voltage scaling.
302  ******************************************************************************/
sli_power_manager_save_states(void)303 void sli_power_manager_save_states(void)
304 {
305   // Save HF clock sources
306   cmu_em01_grpA_clock_register = CMU->EM01GRPACLKCTRL & _CMU_EM01GRPACLKCTRL_CLKSEL_MASK;
307 #if defined(_CMU_EM01GRPBCLKCTRL_CLKSEL_MASK)
308   cmu_em01_grpB_clock_register = CMU->EM01GRPBCLKCTRL & _CMU_EM01GRPBCLKCTRL_CLKSEL_MASK;
309 #endif
310 #if defined(_CMU_EM01GRPCCLKCTRL_CLKSEL_MASK)
311   cmu_em01_grpC_clock_register = CMU->EM01GRPCCLKCTRL & _CMU_EM01GRPCCLKCTRL_CLKSEL_MASK;
312 #endif
313 
314   EMU_Save();
315 }
316 #endif
317 
318 #if !defined(SL_CATALOG_POWER_MANAGER_NO_DEEPSLEEP_PRESENT)
319 /***************************************************************************//**
320  * Handle pre-sleep operations if any are necessary, like manually disabling
321  * oscillators, change clock settings, etc.
322  ******************************************************************************/
SL_CODE_CLASSIFY(SL_CODE_COMPONENT_POWER_MANAGER,SL_CODE_CLASS_TIME_CRITICAL)323 SL_CODE_CLASSIFY(SL_CODE_COMPONENT_POWER_MANAGER, SL_CODE_CLASS_TIME_CRITICAL)
324 void EMU_EM23PresleepHook(void)
325 {
326   // Change the HF Clocks to be on FSRCO before sleep
327   if (is_entering_deepsleep) {
328     is_entering_deepsleep = false;
329 
330     CMU->SYSCLKCTRL = (CMU->SYSCLKCTRL & ~_CMU_SYSCLKCTRL_CLKSEL_MASK) | _CMU_SYSCLKCTRL_CLKSEL_FSRCO;
331     // Switch the HF Clocks oscillator's to FSRCO before deepsleep
332     CMU->EM01GRPACLKCTRL = (CMU->EM01GRPACLKCTRL  & ~_CMU_EM01GRPACLKCTRL_CLKSEL_MASK) | _CMU_EM01GRPACLKCTRL_CLKSEL_FSRCO;
333 #if defined(_CMU_EM01GRPBCLKCTRL_CLKSEL_MASK)
334     CMU->EM01GRPBCLKCTRL = (CMU->EM01GRPBCLKCTRL  & ~_CMU_EM01GRPBCLKCTRL_CLKSEL_MASK) | _CMU_EM01GRPBCLKCTRL_CLKSEL_FSRCO;
335 #endif
336 #if defined(_CMU_EM01GRPCCLKCTRL_CLKSEL_MASK)
337     CMU->EM01GRPCCLKCTRL = (CMU->EM01GRPCCLKCTRL  & ~_CMU_EM01GRPCCLKCTRL_CLKSEL_MASK) | _CMU_EM01GRPCCLKCTRL_CLKSEL_FSRCO;
338 #endif
339     // Disable DPLL before deepsleep
340 #if (_DPLL_IPVERSION_IPVERSION_DEFAULT >= 1)
341 #if defined(_CMU_DPLLREFCLKCTRL_CLKSEL_MASK)
342     if (is_dpll_used) {
343       DPLL0->EN_CLR = DPLL_EN_EN;
344       while ((DPLL0->EN & _DPLL_EN_DISABLING_MASK) != 0) {
345         // Wait for DPLL to be disabled.
346       }
347     }
348 #endif
349 #endif
350 
351     SystemCoreClockUpdate();
352   }
353   // Clear HFXO IEN RDY before entering sleep to prevent HFXO HW requests from waking up the system
354   HFXO0->IEN_CLR = HFXO_IEN_RDY;
355 }
356 #endif
357 
358 #if !defined(SL_CATALOG_POWER_MANAGER_NO_DEEPSLEEP_PRESENT)
359 /***************************************************************************//**
360  * Handle post-sleep operations. The idea is to start HFXO ASAP when we know we
361  * will need it.
362  *
363  * @note In case HFXO is already started when we wake-up (ENS flag is up),
364  *       the hidden flag STARTUPDONE is check to see if the HFXO was just
365  *       enabled or not. If HFXO is enabled automatically following the wake-up,
366  *       the STARTUPDONE flag will not yet be up, and it's an indication that
367  *       we can still process to the HFXO restore time measurement.
368  ******************************************************************************/
EMU_EM23PostsleepHook(void)369 void EMU_EM23PostsleepHook(void)
370 {
371   // Re enable HFXO IEN RDY since it was disabled in EMU_EM23PresleepHook
372   HFXO0->IEN_SET = HFXO_IEN_RDY;
373 
374   // Poke sleeptimer to determine if power manager's timer expired before the
375   // ISR handler executes.
376   // Also, check if HFXO is used.
377   if (is_hf_x_oscillator_used
378       && sli_sleeptimer_hal_is_int_status_set(SLEEPTIMER_EVENT_COMP)
379       && sli_sleeptimer_is_power_manager_timer_next_to_expire()) {
380     // Check if HFXO is already running and has finished its startup.
381     // If yes, don't do the HFXO restore time measurement.
382     if (((HFXO0->STATUS & _HFXO_STATUS_ENS_MASK) != 0
383          && (HFXO0_DBGSTATUS & _HFXO_DBGSTATUS_STARTUPDONE_MASK) != 0)
384         || (HFXO0->STATUS & _HFXO_STATUS_RDY_MASK) != 0) {
385 #if !defined(SL_CATALOG_POWER_MANAGER_DEEPSLEEP_BLOCKING_HFXO_RESTORE_PRESENT)
386       // Force-enable HFXO in case the HFXO on-demand request would be removed
387       // before we finish the restore process.
388       HFXO0->CTRL_SET = HFXO_CTRL_FORCEEN;
389 #endif
390       return;
391     }
392 
393     // Start measure HFXO restore time.
394     is_hf_x_oscillator_already_started = true;
395 
396 #if defined(SL_CATALOG_POWER_MANAGER_DEEPSLEEP_BLOCKING_HFXO_RESTORE_PRESENT)
397     hf_x_oscillator_wakeup_time_tc_inital = sl_sleeptimer_get_tick_count();
398 
399     // Switch SYSCLK to HFXO to measure restore time
400     CMU->SYSCLKCTRL = (CMU->SYSCLKCTRL & ~_CMU_SYSCLKCTRL_CLKSEL_MASK) | cmuSelect_HFXO;
401     SystemCoreClockUpdate();
402 #else
403     sli_hfxo_manager_begin_startup_measurement();
404 
405     // Force enable HFXO to measure restore time
406     HFXO0->CTRL_SET = HFXO_CTRL_FORCEEN;
407 #endif
408   }
409 }
410 #endif
411 
412 #if !defined(SL_CATALOG_POWER_MANAGER_NO_DEEPSLEEP_PRESENT)
413 /***************************************************************************//**
414  * Handle pre-deepsleep operations if any are necessary, like manually disabling
415  * oscillators, change clock settings, etc.
416  ******************************************************************************/
sli_power_manager_handle_pre_deepsleep_operations(void)417 void sli_power_manager_handle_pre_deepsleep_operations(void)
418 {
419   is_entering_deepsleep = true;
420 }
421 #endif
422 
423 #if !defined(SL_CATALOG_POWER_MANAGER_NO_DEEPSLEEP_PRESENT)
424 /***************************************************************************//**
425  * Handle post-sleep operations if any are necessary, like manually enabling
426  * oscillators, change clock settings, etc.
427  ******************************************************************************/
sli_power_manager_restore_high_freq_accuracy_clk(void)428 void sli_power_manager_restore_high_freq_accuracy_clk(void)
429 {
430   if (!is_hf_x_oscillator_used) {
431     return;
432   }
433 
434   // For the cases where it's not started from an early wake up
435   // And if HFXO is not already running.
436   if (!is_hf_x_oscillator_already_started) {
437     // Check if HFXO is already running and has finished its startup.
438     // If yes, don't do the HFXO restore time measurement.
439     if (((HFXO0->STATUS & _HFXO_STATUS_ENS_MASK) != 0
440          && (HFXO0_DBGSTATUS & _HFXO_DBGSTATUS_STARTUPDONE_MASK) != 0)
441         || (HFXO0->STATUS & _HFXO_STATUS_RDY_MASK) != 0) {
442 #if !defined(SL_CATALOG_POWER_MANAGER_DEEPSLEEP_BLOCKING_HFXO_RESTORE_PRESENT)
443       // Force-enable HFXO in case the HFXO on-demand request would be removed
444       // before we finish the restore process.
445       HFXO0->CTRL_SET = HFXO_CTRL_FORCEEN;
446 #endif
447       return;
448     }
449 
450 #if defined(SL_CATALOG_POWER_MANAGER_DEEPSLEEP_BLOCKING_HFXO_RESTORE_PRESENT)
451     hf_x_oscillator_wakeup_time_tc_inital = sl_sleeptimer_get_tick_count();
452 
453     // Switch SYSCLK to HFXO to measure restore time
454     CMU->SYSCLKCTRL = (CMU->SYSCLKCTRL & ~_CMU_SYSCLKCTRL_CLKSEL_MASK) | cmuSelect_HFXO;
455     SystemCoreClockUpdate();
456 #else
457     // Start measure HFXO restore time
458     sli_hfxo_manager_begin_startup_measurement();
459 
460     // Force enable HFXO to measure restore time
461     HFXO0->CTRL_SET = HFXO_CTRL_FORCEEN;
462 #endif
463   }
464 
465 #if defined(SL_CATALOG_POWER_MANAGER_DEEPSLEEP_BLOCKING_HFXO_RESTORE_PRESENT)
466   uint32_t current_time = sl_sleeptimer_get_tick_count() - hf_x_oscillator_wakeup_time_tc_inital;
467   // Calculate average for HFXO restore time
468   hfxo_wakeup_time_sum_average -= (int32_t)hfxo_wakeup_time_table[hfxo_wakeup_time_table_index] - (int32_t)current_time;
469   hfxo_wakeup_time_table[hfxo_wakeup_time_table_index] = current_time;
470   hfxo_wakeup_time_tick = ((hfxo_wakeup_time_sum_average + (HFXO_WAKE_UP_TIME_TABLE_SIZE - 1) ) / HFXO_WAKE_UP_TIME_TABLE_SIZE);
471 
472   // Update index of wakeup time table
473   hfxo_wakeup_time_table_index++;
474   hfxo_wakeup_time_table_index %= HFXO_WAKE_UP_TIME_TABLE_SIZE;
475 #endif
476 
477   is_hf_x_oscillator_already_started = false;
478 }
479 #endif
480 
481 #if !defined(SL_CATALOG_POWER_MANAGER_NO_DEEPSLEEP_PRESENT)
482 /***************************************************************************//**
483  * Checks if HF accuracy clocks is fully restored and, if needed, waits for it.
484  *
485  * @param wait  True, to wait for HF accuracy clocks to be ready
486  *              False, otherwise.
487  *
488  * @return True, if HFXO ready.
489  *         False, otherwise.
490  ******************************************************************************/
sli_power_manager_is_high_freq_accuracy_clk_ready(bool wait)491 bool sli_power_manager_is_high_freq_accuracy_clk_ready(bool wait)
492 {
493   if (!is_hf_x_oscillator_used) {
494     return true;
495   }
496 
497 #if defined(SL_CATALOG_POWER_MANAGER_DEEPSLEEP_BLOCKING_HFXO_RESTORE_PRESENT)
498   (void)wait;
499   return true;
500 #else
501   return sli_hfxo_manager_is_hfxo_ready(wait);
502 #endif
503 }
504 #endif
505 
506 #if !defined(SL_CATALOG_POWER_MANAGER_NO_DEEPSLEEP_PRESENT)
507 /***************************************************************************//**
508  * Restore CMU HF clock select state, oscillator enable, and voltage scaling.
509  ******************************************************************************/
sli_power_manager_restore_states(void)510 void sli_power_manager_restore_states(void)
511 {
512   // Restore specific EMU saved contexts
513   EMU_Restore();
514 
515   // Restore DPLL after deepsleep
516 #if (_DPLL_IPVERSION_IPVERSION_DEFAULT >= 1)
517 #if defined(_CMU_DPLLREFCLKCTRL_CLKSEL_MASK)
518   if (is_dpll_used) {
519     DPLL0->EN_SET = DPLL_EN_EN;
520     while ((DPLL0->STATUS & _DPLL_STATUS_RDY_MASK) == 0U) {
521       // Wait for DPLL to be ready.
522     }
523   }
524 #endif
525 #endif
526 
527   // Restore SYSCLK to what it was before the deepsleep
528   CMU->SYSCLKCTRL = (CMU->SYSCLKCTRL & ~_CMU_SYSCLKCTRL_CLKSEL_MASK) | cmu_sys_clock_register;
529 
530   // Restore the HF Clocks to what they were before deepsleep
531   CMU->EM01GRPACLKCTRL = (CMU->EM01GRPACLKCTRL & ~_CMU_EM01GRPACLKCTRL_CLKSEL_MASK) | cmu_em01_grpA_clock_register;
532 #if defined(_CMU_EM01GRPBCLKCTRL_CLKSEL_MASK)
533   CMU->EM01GRPBCLKCTRL = (CMU->EM01GRPBCLKCTRL & ~_CMU_EM01GRPBCLKCTRL_CLKSEL_MASK) | cmu_em01_grpB_clock_register;
534 #endif
535 #if defined(_CMU_EM01GRPCCLKCTRL_CLKSEL_MASK)
536   CMU->EM01GRPCCLKCTRL = (CMU->EM01GRPCCLKCTRL & ~_CMU_EM01GRPCCLKCTRL_CLKSEL_MASK) | cmu_em01_grpC_clock_register;
537 #endif
538 
539   // Remove FORCEEN on HFXO
540   if (is_hf_x_oscillator_used) {
541     HFXO0->CTRL_CLR = HFXO_CTRL_FORCEEN;
542   }
543 
544   SystemCoreClockUpdate();
545 }
546 #endif
547 
548 /***************************************************************************//**
549  * Applies energy mode.
550  *
551  * @param em  Energy mode to apply:
552  *            SL_POWER_MANAGER_EM0
553  *            SL_POWER_MANAGER_EM1
554  *            SL_POWER_MANAGER_EM2
555  *
556  * @note EMU_EnterEM2() and EMU_EnterEM3() has the parameter 'restore' set to
557  *       true in the Power Manager. When set to true, the parameter 'restore'
558  *       allows the EMU driver to save and restore oscillators, clocks and
559  *       voltage scaling. When the processor returns from EM2 or EM3, its
560  *       execution resumes in a clean and stable state.
561  ******************************************************************************/
sli_power_manager_apply_em(sl_power_manager_em_t em)562 void sli_power_manager_apply_em(sl_power_manager_em_t em)
563 {
564 #if defined(SL_CATALOG_POWER_MANAGER_NO_DEEPSLEEP_PRESENT)
565   // Perform required actions according to energy mode
566   switch (em) {
567     case SL_POWER_MANAGER_EM1:
568     case SL_POWER_MANAGER_EM2:
569     case SL_POWER_MANAGER_EM3:
570 #if (SL_EMLIB_CORE_ENABLE_INTERRUPT_DISABLED_TIMING == 1)
571       // when measuring interrupt disabled time, we don't
572       // want to count the time spent in sleep
573       sl_cycle_counter_pause();
574 #endif
575       EMU_EnterEM1();
576 #if (SL_EMLIB_CORE_ENABLE_INTERRUPT_DISABLED_TIMING == 1)
577       sl_cycle_counter_resume();
578 #endif
579       break;
580 
581     default:
582       EFM_ASSERT(false);
583       break;
584   }
585 #else
586   // Perform required actions according to energy mode
587   switch (em) {
588     case SL_POWER_MANAGER_EM1:
589 #if (SL_EMLIB_CORE_ENABLE_INTERRUPT_DISABLED_TIMING == 1)
590       // when measuring interrupt disabled time, we don't
591       // want to count the time spent in sleep
592       sl_cycle_counter_pause();
593 #endif
594       EMU_EnterEM1();
595 #if (SL_EMLIB_CORE_ENABLE_INTERRUPT_DISABLED_TIMING == 1)
596       sl_cycle_counter_resume();
597 #endif
598       break;
599 
600     case SL_POWER_MANAGER_EM2:
601       EMU_EnterEM2(false);
602       break;
603 
604     case SL_POWER_MANAGER_EM3:
605       EMU_EnterEM3(false);
606       break;
607 
608     default:
609       EFM_ASSERT(false);
610       break;
611   }
612 #endif // SL_CATALOG_POWER_MANAGER_NO_DEEPSLEEP_PRESENT
613 }
614 
615 #if !defined(SL_CATALOG_POWER_MANAGER_NO_DEEPSLEEP_PRESENT)
616 /*******************************************************************************
617  * Returns the default minimum offtime for HFXO.
618  ******************************************************************************/
sli_power_manager_get_default_high_frequency_minimum_offtime(void)619 uint32_t sli_power_manager_get_default_high_frequency_minimum_offtime(void)
620 {
621   return sli_power_manager_convert_delay_us_to_tick(HFXO_MINIMUM_OFFTIME_DEFAULT_VALUE_US);
622 }
623 #endif
624 
625 /*******************************************************************************
626  * Gets the delay associated the wake-up process from EM23.
627  *
628  * @return Delay for the complete wake-up process with full restore.
629  ******************************************************************************/
sli_power_manager_get_wakeup_process_time_overhead(void)630 uint32_t sli_power_manager_get_wakeup_process_time_overhead(void)
631 {
632 #if !defined(SL_CATALOG_POWER_MANAGER_NO_DEEPSLEEP_PRESENT)
633   uint32_t delay = 0;
634 
635   // Add HFXO start-up delay if applicable
636   if (is_hf_x_oscillator_used) {
637 #if defined(SL_CATALOG_POWER_MANAGER_DEEPSLEEP_BLOCKING_HFXO_RESTORE_PRESENT)
638     delay = hfxo_wakeup_time_tick;
639 #else
640     delay = sli_hfxo_manager_get_startup_time();
641 #endif
642     delay += delay >> HFXO_START_UP_TIME_OVERHEAD_LOG2;
643   }
644 
645   // Add all additional overhead wake-up delays (DPLL, VSCALE, general wake-up process)
646   delay += process_wakeup_overhead_tick;
647 
648   return delay;
649 #else
650   return 0;
651 #endif
652 }
653 
654 #if !defined(SL_CATALOG_POWER_MANAGER_NO_DEEPSLEEP_PRESENT)
655 /***************************************************************************//**
656  * Informs the power manager module that the high accuracy/high frequency clock
657  * is used.
658  ******************************************************************************/
sli_power_manager_set_high_accuracy_hf_clock_as_used(void)659 void sli_power_manager_set_high_accuracy_hf_clock_as_used(void)
660 {
661   is_hf_x_oscillator_used = true;
662 }
663 #endif
664 
665 #if !defined(SL_CATALOG_POWER_MANAGER_NO_DEEPSLEEP_PRESENT)
666 /*******************************************************************************
667  * Restores the Low Frequency clocks according to what LF oscillators are used.
668  *
669  * @note On series 2, the on-demand will enable automatically the oscillators
670  *       used when coming from sleep.
671  ******************************************************************************/
sli_power_manager_low_frequency_restore(void)672 void sli_power_manager_low_frequency_restore(void)
673 {
674   // Nothing to do as on-demand feature will enable the LF oscillators automatically.
675 }
676 
677 /***************************************************************************//**
678  * Informs the power manager if the high accuracy/high frequency clock
679  * is used, prior to scheduling an early clock restore.
680  *
681  * @return true if HFXO is used, else false.
682  ******************************************************************************/
sli_power_manager_is_high_freq_accuracy_clk_used(void)683 bool sli_power_manager_is_high_freq_accuracy_clk_used(void)
684 {
685   return is_hf_x_oscillator_used;
686 }
687 #endif
688 
689 #if defined(SL_CATALOG_POWER_MANAGER_NO_DEEPSLEEP_PRESENT)
690 /*******************************************************************************
691  * HAL hook function for pre EM1HCLKDIV sleep.
692  ******************************************************************************/
sli_power_manager_em1hclkdiv_presleep_operations(void)693 void sli_power_manager_em1hclkdiv_presleep_operations(void)
694 {
695   // No operations to do before EM1HCLKDIV sleep on series 2 devices
696 }
697 
698 /*******************************************************************************
699  * HAL hook function for post EM1HCLKDIV sleep.
700  ******************************************************************************/
sli_power_manager_em1hclkdiv_postsleep_operations(void)701 void sli_power_manager_em1hclkdiv_postsleep_operations(void)
702 {
703   // No operations to do before EM1HCLKDIV sleep on series 2 devices
704 }
705 #endif
706 #endif
707