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