em_emu.c 19 KB

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