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