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