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