/***************************************************************************//** * @brief * Restore oscillators and core clock after having been in EM2 or EM3. ******************************************************************************/ static void EMU_Restore(void) { uint32_t cmuLocked; /* Although we could use the CMU API for most of the below handling, we */ /* would like this function to be as efficient as possible. */ /* CMU registers may be locked */ cmuLocked = CMU->LOCK & CMU_LOCK_LOCKKEY_LOCKED; CMU_Unlock(); /* AUXHFRCO was automatically disabled (except if using debugger). */ /* HFXO was automatically disabled. */ /* LFRCO/LFXO were possibly disabled by SW in EM3. */ /* Restore according to status prior to entering EM. */ CMU->OSCENCMD = cmuStatus & (CMU_STATUS_AUXHFRCOENS | CMU_STATUS_HFXOENS | CMU_STATUS_LFRCOENS | CMU_STATUS_LFXOENS); /* Restore oscillator used for clocking core */ switch (cmuStatus & (CMU_STATUS_HFXOSEL | CMU_STATUS_HFRCOSEL | CMU_STATUS_LFXOSEL | CMU_STATUS_LFRCOSEL)) { case CMU_STATUS_LFRCOSEL: /* Wait for LFRCO to stabilize */ while (!(CMU->STATUS & CMU_STATUS_LFRCORDY)) ; CMU->CMD = CMU_CMD_HFCLKSEL_LFRCO; break; case CMU_STATUS_LFXOSEL: /* Wait for LFXO to stabilize */ while (!(CMU->STATUS & CMU_STATUS_LFXORDY)) ; CMU->CMD = CMU_CMD_HFCLKSEL_LFXO; break; case CMU_STATUS_HFXOSEL: /* Wait for HFXO to stabilize */ while (!(CMU->STATUS & CMU_STATUS_HFXORDY)) ; CMU->CMD = CMU_CMD_HFCLKSEL_HFXO; break; default: /* CMU_STATUS_HFRCOSEL */ /* If core clock was HFRCO core clock, it is automatically restored to */ /* state prior to entering energy mode. No need for further action. */ break; } /* Restore CMU register locking */ if (cmuLocked) { CMU_Lock(); } }
/***************************************************************************//** * @brief * Enter energy mode 3 (EM3). * * @details * When entering EM3, the high frequency clocks are disabled by HW, ie HFXO, * HFRCO and AUXHFRCO (for AUXHFRCO, see exception note below). In addition, * the low frequency clocks, ie LFXO and LFRCO are disabled by SW. When * re-entering EM0, HFRCO is re-enabled and the core will be clocked by the * configured HFRCO band. This ensures a quick wakeup from EM3. * * However, prior to entering EM3, the core may have been using another * oscillator than HFRCO. The @p restore parameter gives the user the option * to restore all HF/LF oscillators according to state prior to entering EM3, * as well as the clock used to clock the core. This restore procedure is * handled by SW. However, since handled by SW, it will not be restored * before completing the interrupt function(s) waking up the core! * * @note * If restoring core clock to use an oscillator other than HFRCO, this * function will stall until the oscillator has stabilized. Stalling time * can be reduced by adding interrupt support detecting stable oscillator, * and an asynchronous switch to the original oscillator. See CMU * documentation. Such a feature is however outside the scope of the * implementation in this function. * @par * If HFXO/LFXO/LFRCO are re-enabled by this function, and NOT used to clock * the core, this function will not wait for those oscillators to stabilize. * This must be considered by the application if trying to use features * relying on those oscillators upon return. * @par * If a debugger is attached, the AUXHFRCO will not be disabled if enabled * upon entering EM3. It will thus remain enabled when returning to EM0 * regardless of the @p restore parameter. * * @param[in] restore * @li true - restore oscillators and clocks, see function details. * @li false - do not restore oscillators and clocks, see function details. * @par * The @p restore option should only be used if all clock control is done * via the CMU API. ******************************************************************************/ void EMU_EnterEM3(bool restore) { uint32_t cmuLocked; /* Auto-update CMU status just in case before entering energy mode. */ /* This variable is normally kept up-to-date by the CMU API. */ cmuStatus = (uint16_t)(CMU->STATUS); /* CMU registers may be locked */ cmuLocked = CMU->LOCK & CMU_LOCK_LOCKKEY_LOCKED; CMU_Unlock(); /* Disable LF oscillators */ CMU->OSCENCMD = CMU_OSCENCMD_LFXODIS | CMU_OSCENCMD_LFRCODIS; /* Restore CMU register locking */ if (cmuLocked) { CMU_Lock(); } /* Enter Cortex-M3 deep sleep mode */ SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk; __WFI(); /* Restore oscillators/clocks if specified */ if (restore) { EMU_Restore(); } /* If not restoring, and original clock was not HFRCO, we have to */ /* update CMSIS core clock variable since core clock has changed */ /* to using HFRCO. */ else if (!(cmuStatus & CMU_STATUS_HFRCOSEL)) { SystemCoreClockUpdate(); } }