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