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