1 /***************************************************************************//**
2 * @file
3 * @brief Energy Management Unit (EMU) Peripheral API
4 * @author Energy Micro AS
5 * @version 3.0.0
6 *******************************************************************************
7 * @section License
8 * <b>(C) Copyright 2012 Energy Micro AS, http://www.energymicro.com</b>
9 *******************************************************************************
10 *
11 * Permission is granted to anyone to use this software for any purpose,
12 * including commercial applications, and to alter it and redistribute it
13 * freely, subject to the following restrictions:
14 *
15 * 1. The origin of this software must not be misrepresented; you must not
16 * claim that you wrote the original software.
17 * 2. Altered source versions must be plainly marked as such, and must not be
18 * misrepresented as being the original software.
19 * 3. This notice may not be removed or altered from any source distribution.
20 *
21 * DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Energy Micro AS has no
22 * obligation to support this Software. Energy Micro AS is providing the
23 * Software "AS IS", with no express or implied warranties of any kind,
24 * including, but not limited to, any implied warranties of merchantability
25 * or fitness for any particular purpose or warranties against infringement
26 * of any proprietary rights of a third party.
27 *
28 * Energy Micro AS will not be liable for any consequential, incidental, or
29 * special damages, or any other relief, or for any claim by any third party,
30 * arising from your use of this Software.
31 *
32 ******************************************************************************/
33 #include "em_emu.h"
34 #include "em_cmu.h"
35 #include "em_assert.h"
36
37 /***************************************************************************//**
38 * @addtogroup EM_Library
39 * @{
40 ******************************************************************************/
41
42 /***************************************************************************//**
43 * @addtogroup EMU
44 * @brief Energy Management Unit (EMU) Peripheral API
45 * @{
46 ******************************************************************************/
47
48 /* Consistency check, since restoring assumes similar bitpositions in */
49 /* CMU OSCENCMD and STATUS regs */
50 #if (CMU_STATUS_AUXHFRCOENS != CMU_OSCENCMD_AUXHFRCOEN)
51 #error Conflict in AUXHFRCOENS and AUXHFRCOEN bitpositions
52 #endif
53 #if (CMU_STATUS_HFXOENS != CMU_OSCENCMD_HFXOEN)
54 #error Conflict in HFXOENS and HFXOEN bitpositions
55 #endif
56 #if (CMU_STATUS_LFRCOENS != CMU_OSCENCMD_LFRCOEN)
57 #error Conflict in LFRCOENS and LFRCOEN bitpositions
58 #endif
59 #if (CMU_STATUS_LFXOENS != CMU_OSCENCMD_LFXOEN)
60 #error Conflict in LFXOENS and LFXOEN bitpositions
61 #endif
62
63 /*******************************************************************************
64 ************************** LOCAL VARIABLES ********************************
65 ******************************************************************************/
66
67 /** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
68 /**
69 * CMU configured oscillator selection and oscillator enable status. When a
70 * user configures oscillators, this varaiable shall shadow the configuration.
71 * It is used by the EMU module in order to be able to restore the oscillator
72 * config after having been in certain energy modes (since HW may automatically
73 * alter config when going into an energy mode). It is the responsibility of
74 * the CMU module to keep it up-to-date (or a user if not using the CMU API
75 * for oscillator control).
76 */
77 static uint16_t cmuStatus;
78 /** @endcond */
79
80
81 /*******************************************************************************
82 ************************** LOCAL FUNCTIONS ********************************
83 ******************************************************************************/
84
85 /** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
86
87 /***************************************************************************//**
88 * @brief
89 * Restore oscillators and core clock after having been in EM2 or EM3.
90 ******************************************************************************/
EMU_Restore(void)91 static void EMU_Restore(void)
92 {
93 uint32_t cmuLocked;
94
95 /* Although we could use the CMU API for most of the below handling, we */
96 /* would like this function to be as efficient as possible. */
97
98 /* CMU registers may be locked */
99 cmuLocked = CMU->LOCK & CMU_LOCK_LOCKKEY_LOCKED;
100 CMU_Unlock();
101
102 /* AUXHFRCO was automatically disabled (except if using debugger). */
103 /* HFXO was automatically disabled. */
104 /* LFRCO/LFXO were possibly disabled by SW in EM3. */
105 /* Restore according to status prior to entering EM. */
106 CMU->OSCENCMD = cmuStatus & (CMU_STATUS_AUXHFRCOENS |
107 CMU_STATUS_HFXOENS |
108 CMU_STATUS_LFRCOENS |
109 CMU_STATUS_LFXOENS);
110
111 /* Restore oscillator used for clocking core */
112 switch (cmuStatus & (CMU_STATUS_HFXOSEL | CMU_STATUS_HFRCOSEL |
113 CMU_STATUS_LFXOSEL | CMU_STATUS_LFRCOSEL))
114 {
115 case CMU_STATUS_LFRCOSEL:
116 /* Wait for LFRCO to stabilize */
117 while (!(CMU->STATUS & CMU_STATUS_LFRCORDY))
118 ;
119 CMU->CMD = CMU_CMD_HFCLKSEL_LFRCO;
120 break;
121
122 case CMU_STATUS_LFXOSEL:
123 /* Wait for LFXO to stabilize */
124 while (!(CMU->STATUS & CMU_STATUS_LFXORDY))
125 ;
126 CMU->CMD = CMU_CMD_HFCLKSEL_LFXO;
127 break;
128
129 case CMU_STATUS_HFXOSEL:
130 /* Wait for HFXO to stabilize */
131 while (!(CMU->STATUS & CMU_STATUS_HFXORDY))
132 ;
133 CMU->CMD = CMU_CMD_HFCLKSEL_HFXO;
134 break;
135
136 default: /* CMU_STATUS_HFRCOSEL */
137 /* If core clock was HFRCO core clock, it is automatically restored to */
138 /* state prior to entering energy mode. No need for further action. */
139 break;
140 }
141
142 /* If HFRCO was disabled before entering Energy Mode, turn it off again */
143 /* as it is automatically enabled by wake up */
144 if ( ! (cmuStatus & CMU_STATUS_HFRCOENS) )
145 {
146 CMU->OSCENCMD = CMU_OSCENCMD_HFRCODIS;
147 }
148
149 /* Restore CMU register locking */
150 if (cmuLocked)
151 {
152 CMU_Lock();
153 }
154 }
155
156 /** @endcond */
157
158
159 /*******************************************************************************
160 ************************** GLOBAL FUNCTIONS *******************************
161 ******************************************************************************/
162
163 /***************************************************************************//**
164 * @brief
165 * Enter energy mode 2 (EM2).
166 *
167 * @details
168 * When entering EM2, the high frequency clocks are disabled, ie HFXO, HFRCO
169 * and AUXHFRCO (for AUXHFRCO, see exception note below). When re-entering
170 * EM0, HFRCO is re-enabled and the core will be clocked by the configured
171 * HFRCO band. This ensures a quick wakeup from EM2.
172 *
173 * However, prior to entering EM2, the core may have been using another
174 * oscillator than HFRCO. The @p restore parameter gives the user the option
175 * to restore all HF oscillators according to state prior to entering EM2,
176 * as well as the clock used to clock the core. This restore procedure is
177 * handled by SW. However, since handled by SW, it will not be restored
178 * before completing the interrupt function(s) waking up the core!
179 *
180 * @note
181 * If restoring core clock to use the HFXO oscillator, which has been
182 * disabled during EM2 mode, this function will stall until the oscillator
183 * has stabilized. Stalling time can be reduced by adding interrupt
184 * support detecting stable oscillator, and an asynchronous switch to the
185 * original oscillator. See CMU documentation. Such a feature is however
186 * outside the scope of the implementation in this function.
187 * @par
188 * If HFXO is re-enabled by this function, and NOT used to clock the core,
189 * this function will not wait for HFXO to stabilize. This must be considered
190 * by the application if trying to use features relying on that oscillator
191 * upon return.
192 * @par
193 * If a debugger is attached, the AUXHFRCO will not be disabled if enabled
194 * upon entering EM2. It will thus remain enabled when returning to EM0
195 * regardless of the @p restore parameter.
196 *
197 * @param[in] restore
198 * @li true - restore oscillators and clocks, see function details.
199 * @li false - do not restore oscillators and clocks, see function details.
200 * @par
201 * The @p restore option should only be used if all clock control is done
202 * via the CMU API.
203 ******************************************************************************/
EMU_EnterEM2(bool restore)204 void EMU_EnterEM2(bool restore)
205 {
206 /* Auto-update CMU status just in case before entering energy mode. */
207 /* This variable is normally kept up-to-date by the CMU API. */
208 cmuStatus = (uint16_t)(CMU->STATUS);
209
210 /* Enter Cortex-M3 deep sleep mode */
211 SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
212 __WFI();
213
214 /* Restore oscillators/clocks if specified */
215 if (restore)
216 {
217 EMU_Restore();
218 }
219 /* If not restoring, and original clock was not HFRCO, we have to */
220 /* update CMSIS core clock variable since core clock has changed */
221 /* to using HFRCO. */
222 else if (!(cmuStatus & CMU_STATUS_HFRCOSEL))
223 {
224 SystemCoreClockUpdate();
225 }
226 }
227
228
229 /***************************************************************************//**
230 * @brief
231 * Enter energy mode 3 (EM3).
232 *
233 * @details
234 * When entering EM3, the high frequency clocks are disabled by HW, ie HFXO,
235 * HFRCO and AUXHFRCO (for AUXHFRCO, see exception note below). In addition,
236 * the low frequency clocks, ie LFXO and LFRCO are disabled by SW. When
237 * re-entering EM0, HFRCO is re-enabled and the core will be clocked by the
238 * configured HFRCO band. This ensures a quick wakeup from EM3.
239 *
240 * However, prior to entering EM3, the core may have been using another
241 * oscillator than HFRCO. The @p restore parameter gives the user the option
242 * to restore all HF/LF oscillators according to state prior to entering EM3,
243 * as well as the clock used to clock the core. This restore procedure is
244 * handled by SW. However, since handled by SW, it will not be restored
245 * before completing the interrupt function(s) waking up the core!
246 *
247 * @note
248 * If restoring core clock to use an oscillator other than HFRCO, this
249 * function will stall until the oscillator has stabilized. Stalling time
250 * can be reduced by adding interrupt support detecting stable oscillator,
251 * and an asynchronous switch to the original oscillator. See CMU
252 * documentation. Such a feature is however outside the scope of the
253 * implementation in this function.
254 * @par
255 * If HFXO/LFXO/LFRCO are re-enabled by this function, and NOT used to clock
256 * the core, this function will not wait for those oscillators to stabilize.
257 * This must be considered by the application if trying to use features
258 * relying on those oscillators upon return.
259 * @par
260 * If a debugger is attached, the AUXHFRCO will not be disabled if enabled
261 * upon entering EM3. It will thus remain enabled when returning to EM0
262 * regardless of the @p restore parameter.
263 *
264 * @param[in] restore
265 * @li true - restore oscillators and clocks, see function details.
266 * @li false - do not restore oscillators and clocks, see function details.
267 * @par
268 * The @p restore option should only be used if all clock control is done
269 * via the CMU API.
270 ******************************************************************************/
EMU_EnterEM3(bool restore)271 void EMU_EnterEM3(bool restore)
272 {
273 uint32_t cmuLocked;
274
275 /* Auto-update CMU status just in case before entering energy mode. */
276 /* This variable is normally kept up-to-date by the CMU API. */
277 cmuStatus = (uint16_t)(CMU->STATUS);
278
279 /* CMU registers may be locked */
280 cmuLocked = CMU->LOCK & CMU_LOCK_LOCKKEY_LOCKED;
281 CMU_Unlock();
282
283 /* Disable LF oscillators */
284 CMU->OSCENCMD = CMU_OSCENCMD_LFXODIS | CMU_OSCENCMD_LFRCODIS;
285
286 /* Restore CMU register locking */
287 if (cmuLocked)
288 {
289 CMU_Lock();
290 }
291
292 /* Enter Cortex-M3 deep sleep mode */
293 SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
294 __WFI();
295
296 /* Restore oscillators/clocks if specified */
297 if (restore)
298 {
299 EMU_Restore();
300 }
301 /* If not restoring, and original clock was not HFRCO, we have to */
302 /* update CMSIS core clock variable since core clock has changed */
303 /* to using HFRCO. */
304 else if (!(cmuStatus & CMU_STATUS_HFRCOSEL))
305 {
306 SystemCoreClockUpdate();
307 }
308 }
309
310
311 /***************************************************************************//**
312 * @brief
313 * Enter energy mode 4 (EM4).
314 *
315 * @note
316 * Only a power on reset or external reset pin can wake the device from EM4.
317 ******************************************************************************/
EMU_EnterEM4(void)318 void EMU_EnterEM4(void)
319 {
320 int i;
321
322 /* Make sure register write lock is disabled */
323 EMU->LOCK = EMU_LOCK_LOCKKEY_UNLOCK;
324
325 for (i = 0; i < 4; i++)
326 {
327 EMU->CTRL = (2 << _EMU_CTRL_EM4CTRL_SHIFT);
328 EMU->CTRL = (3 << _EMU_CTRL_EM4CTRL_SHIFT);
329 }
330 EMU->CTRL = (2 << _EMU_CTRL_EM4CTRL_SHIFT);
331 }
332
333
334 /***************************************************************************//**
335 * @brief
336 * Power down memory block.
337 *
338 * @param[in] blocks
339 * Specifies a logical OR of bits indicating memory blocks to power down.
340 * Bit 0 selects block 1, bit 1 selects block 2, etc. Memory block 0 cannot
341 * be disabled. Please refer to the EFM32 reference manual for available
342 * memory blocks for a device.
343 *
344 * @note
345 * Only a reset can make the specified memory block(s) available for use
346 * after having been powered down. Function will be void for devices not
347 * supporting this feature.
348 ******************************************************************************/
EMU_MemPwrDown(uint32_t blocks)349 void EMU_MemPwrDown(uint32_t blocks)
350 {
351 #if defined(_EMU_MEMCTRL_RESETVALUE)
352 EFM_ASSERT(blocks <= _EMU_MEMCTRL_MASK);
353
354 EMU->MEMCTRL = blocks;
355 #else
356 (void)blocks;
357 #endif
358 }
359
360
361 /***************************************************************************//**
362 * @brief
363 * Update EMU module with CMU oscillator selection/enable status.
364 *
365 * @details
366 * When entering EM2 and EM3, the HW may change the core clock oscillator
367 * used, as well as disabling some oscillators. The user may optionally select
368 * to restore the oscillators after waking up from EM2 and EM3 through the
369 * SW API.
370 *
371 * However, in order to support this in a safe way, the EMU module must
372 * be kept up-to-date on the actual selected configuration. The CMU
373 * module must keep the EMU module up-to-date.
374 *
375 * This function is mainly intended for internal use by the CMU module,
376 * but if the applications changes oscillator configurations without
377 * using the CMU API, this function can be used to keep the EMU module
378 * up-to-date.
379 ******************************************************************************/
EMU_UpdateOscConfig(void)380 void EMU_UpdateOscConfig(void)
381 {
382 /* Fetch current configuration */
383 cmuStatus = (uint16_t)(CMU->STATUS);
384 }
385
386
387 #if defined(_EFM32_GIANT_FAMILY)
388 /***************************************************************************//**
389 * @brief
390 * Update EMU module with Energy Mode 4 configuration
391 *
392 * @param[in] em4init
393 * Energy Mode 4 configuration structure
394 ******************************************************************************/
EMU_EM4Init(EMU_EM4Init_TypeDef * em4init)395 void EMU_EM4Init(EMU_EM4Init_TypeDef *em4init)
396 {
397 uint32_t em4conf = EMU->EM4CONF;
398
399 /* Clear fields that will be reconfigured */
400 em4conf &= ~(
401 _EMU_EM4CONF_LOCKCONF_MASK|
402 _EMU_EM4CONF_OSC_MASK|
403 _EMU_EM4CONF_BURTCWU_MASK|
404 _EMU_EM4CONF_VREGEN_MASK);
405
406 /* Configure new settings */
407 em4conf |= (
408 (em4init->lockConfig << _EMU_EM4CONF_LOCKCONF_SHIFT)|
409 (em4init->osc)|
410 (em4init->buRtcWakeup << _EMU_EM4CONF_BURTCWU_SHIFT)|
411 (em4init->vreg << _EMU_EM4CONF_VREGEN_SHIFT));
412
413 /* Apply configuration. Note that lock can be set after this stage. */
414 EMU->EM4CONF = em4conf;
415 }
416
417
418 /***************************************************************************//**
419 * @brief
420 * Configure Backup Power Domain settings
421 *
422 * @param[in] bupdInit
423 * Backup power domain initialization structure
424 ******************************************************************************/
EMU_BUPDInit(EMU_BUPDInit_TypeDef * bupdInit)425 void EMU_BUPDInit(EMU_BUPDInit_TypeDef *bupdInit)
426 {
427 uint32_t reg;
428
429 /* Set power connection configuration */
430 reg = EMU->PWRCONF & ~(
431 _EMU_PWRCONF_PWRRES_MASK|
432 _EMU_PWRCONF_VOUTSTRONG_MASK|
433 _EMU_PWRCONF_VOUTMED_MASK|
434 _EMU_PWRCONF_VOUTWEAK_MASK);
435
436 reg |= (bupdInit->resistor|
437 (bupdInit->voutStrong << _EMU_PWRCONF_VOUTSTRONG_SHIFT)|
438 (bupdInit->voutMed << _EMU_PWRCONF_VOUTMED_SHIFT)|
439 (bupdInit->voutWeak << _EMU_PWRCONF_VOUTWEAK_SHIFT));
440
441 EMU->PWRCONF = reg;
442
443 /* Set backup domain inactive mode configuration */
444 reg = EMU->BUINACT & ~(_EMU_BUINACT_PWRCON_MASK);
445 reg |= (bupdInit->inactivePower);
446 EMU->BUINACT = reg;
447
448 /* Set backup domain active mode configuration */
449 reg = EMU->BUACT & ~(_EMU_BUACT_PWRCON_MASK);
450 reg |= (bupdInit->activePower);
451 EMU->BUACT = reg;
452
453 /* Set power control configuration */
454 reg = EMU->BUCTRL & ~(
455 _EMU_BUCTRL_PROBE_MASK|
456 _EMU_BUCTRL_BODCAL_MASK|
457 _EMU_BUCTRL_STATEN_MASK|
458 _EMU_BUCTRL_EN_MASK);
459
460 /* Note use of ->enable to both enable BUPD, use BU_VIN pin input and
461 release reset */
462 reg |= (bupdInit->probe|
463 (bupdInit->bodCal << _EMU_BUCTRL_BODCAL_SHIFT)|
464 (bupdInit->statusPinEnable << _EMU_BUCTRL_STATEN_SHIFT)|
465 (bupdInit->enable << _EMU_BUCTRL_EN_SHIFT));
466
467 /* Enable configuration */
468 EMU->BUCTRL = reg;
469
470 /* If enable is true, enable BU_VIN input power pin, if not disable it */
471 EMU_BUPinEnable(bupdInit->enable);
472
473 /* If enable is true, release BU reset, if not keep reset asserted */
474 BITBAND_Peripheral(&(RMU->CTRL), _RMU_CTRL_BURSTEN_SHIFT, !bupdInit->enable);
475 }
476
477
478 /***************************************************************************//**
479 * @brief
480 * Configure Backup Power Domain BOD Threshold value
481 * @note
482 * These values are precalibrated
483 * @param[in] mode Active or Inactive mode
484 * @param[in] value
485 ******************************************************************************/
EMU_BUThresholdSet(EMU_BODMode_TypeDef mode,uint32_t value)486 void EMU_BUThresholdSet(EMU_BODMode_TypeDef mode, uint32_t value)
487 {
488 EFM_ASSERT(value<4);
489
490 switch(mode)
491 {
492 case emuBODMode_Active:
493 EMU->BUACT = (EMU->BUACT & ~(_EMU_BUACT_BUEXTHRES_MASK))|(value<<_EMU_BUACT_BUEXTHRES_SHIFT);
494 break;
495 case emuBODMode_Inactive:
496 EMU->BUINACT = (EMU->BUINACT & ~(_EMU_BUINACT_BUENTHRES_MASK))|(value<<_EMU_BUINACT_BUENTHRES_SHIFT);
497 break;
498 }
499 }
500
501
502 /***************************************************************************//**
503 * @brief
504 * Configure Backup Power Domain BOD Threshold Range
505 * @note
506 * These values are precalibrated
507 * @param[in] mode Active or Inactive mode
508 * @param[in] value
509 ******************************************************************************/
EMU_BUThresRangeSet(EMU_BODMode_TypeDef mode,uint32_t value)510 void EMU_BUThresRangeSet(EMU_BODMode_TypeDef mode, uint32_t value)
511 {
512 EFM_ASSERT(value<4);
513
514 switch(mode)
515 {
516 case emuBODMode_Active:
517 EMU->BUACT = (EMU->BUACT & ~(_EMU_BUACT_BUEXRANGE_MASK))|(value<<_EMU_BUACT_BUEXRANGE_SHIFT);
518 break;
519 case emuBODMode_Inactive:
520 EMU->BUINACT = (EMU->BUINACT & ~(_EMU_BUINACT_BUENRANGE_MASK))|(value<<_EMU_BUINACT_BUENRANGE_SHIFT);
521 break;
522 }
523 }
524
525 #endif
526
527
528 /** @} (end addtogroup EMU) */
529 /** @} (end addtogroup EM_Library) */
530