openblt/Target/Demo/ARMCM3_EFM32_Olimex_EM32G88.../Boot/lib/efm32lib/src/efm32_emu.c

500 lines
18 KiB
C

/***************************************************************************//**
* @file
* @brief Energy Management Unit (EMU) Peripheral API for EFM32
* @author Energy Micro AS
* @version 2.3.2
*******************************************************************************
* @section License
* <b>(C) Copyright 2010 Energy Micro AS, http://www.energymicro.com</b>
*******************************************************************************
*
* This source code is the property of Energy Micro AS. The source and compiled
* code may only be used on Energy Micro "EFM32" microcontrollers.
*
* This copyright notice may not be removed from the source code nor changed.
*
* DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Energy Micro AS has no
* obligation to support this Software. Energy Micro AS is providing the
* Software "AS IS", with no express or implied warranties of any kind,
* including, but not limited to, any implied warranties of merchantability
* or fitness for any particular purpose or warranties against infringement
* of any proprietary rights of a third party.
*
* Energy Micro AS will not be liable for any consequential, incidental, or
* special damages, or any other relief, or for any claim by any third party,
* arising from your use of this Software.
*
******************************************************************************/
#include "efm32_emu.h"
#include "efm32_cmu.h"
#include "efm32_assert.h"
/***************************************************************************//**
* @addtogroup EFM32_Library
* @{
******************************************************************************/
/***************************************************************************//**
* @addtogroup EMU
* @brief Energy Management Unit (EMU) Peripheral API for EFM32
* @{
******************************************************************************/
/* Consistency check, since restoring assumes similar bitpositions in */
/* CMU OSCENCMD and STATUS regs */
#if (CMU_STATUS_AUXHFRCOENS != CMU_OSCENCMD_AUXHFRCOEN)
#error Conflict in AUXHFRCOENS and AUXHFRCOEN bitpositions
#endif
#if (CMU_STATUS_HFXOENS != CMU_OSCENCMD_HFXOEN)
#error Conflict in HFXOENS and HFXOEN bitpositions
#endif
#if (CMU_STATUS_LFRCOENS != CMU_OSCENCMD_LFRCOEN)
#error Conflict in LFRCOENS and LFRCOEN bitpositions
#endif
#if (CMU_STATUS_LFXOENS != CMU_OSCENCMD_LFXOEN)
#error Conflict in LFXOENS and LFXOEN bitpositions
#endif
/*******************************************************************************
************************** LOCAL VARIABLES ********************************
******************************************************************************/
/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
/**
* CMU configured oscillator selection and oscillator enable status. When a
* user configures oscillators, this varaiable shall shadow the configuration.
* It is used by the EMU module in order to be able to restore the oscillator
* config after having been in certain energy modes (since HW may automatically
* alter config when going into an energy mode). It is the responsibility of
* the CMU module to keep it up-to-date (or a user if not using the CMU API
* for oscillator control).
*/
static uint16_t cmuStatus;
/** @endcond */
/*******************************************************************************
************************** LOCAL FUNCTIONS ********************************
******************************************************************************/
/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
/***************************************************************************//**
* @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;
}
/* If HFRCO was disabled before entering Energy Mode, turn it off again */
/* as it is automatically enabled by wake up */
if ( ! (cmuStatus & CMU_STATUS_HFRCOENS) )
{
CMU->OSCENCMD = CMU_OSCENCMD_HFRCODIS;
}
/* Restore CMU register locking */
if (cmuLocked)
{
CMU_Lock();
}
}
/** @endcond */
/*******************************************************************************
************************** GLOBAL FUNCTIONS *******************************
******************************************************************************/
/***************************************************************************//**
* @brief
* Enter energy mode 2 (EM2).
*
* @details
* When entering EM2, the high frequency clocks are disabled, ie HFXO, HFRCO
* and AUXHFRCO (for AUXHFRCO, see exception note below). 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 EM2.
*
* However, prior to entering EM2, the core may have been using another
* oscillator than HFRCO. The @p restore parameter gives the user the option
* to restore all HF oscillators according to state prior to entering EM2,
* 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 the HFXO oscillator, which has been
* disabled during EM2 mode, 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 is re-enabled by this function, and NOT used to clock the core,
* this function will not wait for HFXO to stabilize. This must be considered
* by the application if trying to use features relying on that oscillator
* upon return.
* @par
* If a debugger is attached, the AUXHFRCO will not be disabled if enabled
* upon entering EM2. 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_EnterEM2(bool restore)
{
/* 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);
/* 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();
}
}
/***************************************************************************//**
* @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();
}
}
/***************************************************************************//**
* @brief
* Enter energy mode 4 (EM4).
*
* @note
* Only a power on reset or external reset pin can wake the device from EM4.
******************************************************************************/
void EMU_EnterEM4(void)
{
int i;
/* Make sure register write lock is disabled */
EMU->LOCK = EMU_LOCK_LOCKKEY_UNLOCK;
for (i = 0; i < 4; i++)
{
EMU->CTRL = (2 << _EMU_CTRL_EM4CTRL_SHIFT);
EMU->CTRL = (3 << _EMU_CTRL_EM4CTRL_SHIFT);
}
EMU->CTRL = (2 << _EMU_CTRL_EM4CTRL_SHIFT);
}
/***************************************************************************//**
* @brief
* Power down memory block.
*
* @param[in] blocks
* Specifies a logical OR of bits indicating memory blocks to power down.
* Bit 0 selects block 1, bit 1 selects block 2, etc. Memory block 0 cannot
* be disabled. Please refer to the EFM32 reference manual for available
* memory blocks for a device.
*
* @note
* Only a reset can make the specified memory block(s) available for use
* after having been powered down. Function will be void for devices not
* supporting this feature.
******************************************************************************/
void EMU_MemPwrDown(uint32_t blocks)
{
#if defined(_EMU_MEMCTRL_RESETVALUE)
EFM_ASSERT(blocks <= _EMU_MEMCTRL_MASK);
EMU->MEMCTRL = blocks;
#else
(void)blocks;
#endif
}
/***************************************************************************//**
* @brief
* Update EMU module with CMU oscillator selection/enable status.
*
* @details
* When entering EM2 and EM3, the HW may change the core clock oscillator
* used, as well as disabling some oscillators. The user may optionally select
* to restore the oscillators after waking up from EM2 and EM3 through the
* SW API.
*
* However, in order to support this in a safe way, the EMU module must
* be kept up-to-date on the actual selected configuration. The CMU
* module must keep the EMU module up-to-date.
*
* This function is mainly intended for internal use by the CMU module,
* but if the applications changes oscillator configurations without
* using the CMU API, this function can be used to keep the EMU module
* up-to-date.
******************************************************************************/
void EMU_UpdateOscConfig(void)
{
/* Fetch current configuration */
cmuStatus = (uint16_t)(CMU->STATUS);
}
#if defined(_EFM32_GIANT_FAMILY)
/***************************************************************************//**
* @brief
* Update EMU module with Energy Mode 4 configuration
*
* @param[in] em4init
* Energy Mode 4 configuration structure
******************************************************************************/
void EMU_EM4Init(EMU_EM4Init_TypeDef *em4init)
{
uint32_t em4conf = EMU->EM4CONF;
/* Clear fields that will be reconfigured */
em4conf &= ~(
_EMU_EM4CONF_LOCKCONF_MASK|
_EMU_EM4CONF_OSC_MASK|
_EMU_EM4CONF_BURTCWU_MASK|
_EMU_EM4CONF_VREGEN_MASK);
/* Configure new settings */
em4conf |= (
(em4init->lockConfig << _EMU_EM4CONF_LOCKCONF_SHIFT)|
(em4init->osc)|
(em4init->buRtcWakeup << _EMU_EM4CONF_BURTCWU_SHIFT)|
(em4init->vreg << _EMU_EM4CONF_VREGEN_SHIFT));
/* Apply configuration. Note that lock can be set after this stage. */
EMU->EM4CONF = em4conf;
}
/***************************************************************************//**
* @brief
* Configure BackUp Power Domain settings
*
* @note
* stig note to self: Touches RMU->CTRL BUPD?
*
* @param[in] bupdInit
* Backup power domain initialization structure
******************************************************************************/
void EMU_BUPDInit(EMU_BUPDInit_TypeDef *bupdInit)
{
uint32_t reg;
EFM_ASSERT(bupdInit->inactiveThresRange < 4);
EFM_ASSERT(bupdInit->inactiveThreshold < 4);
EFM_ASSERT(bupdInit->activeThresRange < 4);
EFM_ASSERT(bupdInit->activeThreshold < 4);
/* Set power connection configuration */
reg = EMU->PWRCONF & ~(
_EMU_PWRCONF_PWRRES_MASK|
_EMU_PWRCONF_VOUTSTRONG_MASK|
_EMU_PWRCONF_VOUTMED_MASK|
_EMU_PWRCONF_VOUTWEAK_MASK);
reg |= (bupdInit->resistor|
(bupdInit->voutStrong << _EMU_PWRCONF_VOUTSTRONG_SHIFT)|
(bupdInit->voutMed << _EMU_PWRCONF_VOUTMED_SHIFT)|
(bupdInit->voutWeak << _EMU_PWRCONF_VOUTWEAK_SHIFT));
EMU->PWRCONF = reg;
/* Set backup domain inactive mode configuration */
reg = EMU->BUINACT & ~(
_EMU_BUINACT_PWRCON_MASK|
_EMU_BUINACT_BUENRANGE_MASK|
_EMU_BUINACT_BUENTHRES_MASK);
reg |= (bupdInit->inactivePower|
(bupdInit->inactiveThresRange << _EMU_BUINACT_BUENRANGE_SHIFT)|
(bupdInit->inactiveThreshold << _EMU_BUINACT_BUENTHRES_SHIFT));
EMU->BUINACT = reg;
/* Set backup domain active mode configuration */
reg = EMU->BUACT & ~(
_EMU_BUACT_PWRCON_MASK|
_EMU_BUACT_BUEXRANGE_MASK|
_EMU_BUACT_BUEXTHRES_MASK);
reg |= (bupdInit->activePower|
(bupdInit->activeThresRange << _EMU_BUACT_BUEXRANGE_SHIFT)|
(bupdInit->activeThreshold << _EMU_BUACT_BUEXTHRES_SHIFT));
EMU->BUACT = reg;
/* Set power control configuration */
reg = EMU->BUCTRL & ~(
_EMU_BUCTRL_PROBE_MASK|
_EMU_BUCTRL_BODCAL_MASK|
_EMU_BUCTRL_STATEN_MASK|
_EMU_BUCTRL_EN_MASK);
/* Note use of ->enable to both enable BUPD, use BU_VIN pin input and
release reset */
reg |= (bupdInit->probe|
(bupdInit->bodCal << _EMU_BUCTRL_BODCAL_SHIFT)|
(bupdInit->statusPinEnable << _EMU_BUCTRL_STATEN_SHIFT)|
(bupdInit->enable << _EMU_BUCTRL_EN_SHIFT));
/* Enable configuration */
EMU->BUCTRL = reg;
/* If enable is true, enable BU_VIN input power pin, if not disable it */
EMU_BUPinEnable(bupdInit->enable);
/* If enable is true, release BU reset, if not keep reset asserted */
BITBAND_Peripheral(&(RMU->CTRL), _RMU_CTRL_BURSTEN_SHIFT, !bupdInit->enable);
}
#endif
/** @} (end addtogroup EMU) */
/** @} (end addtogroup EFM32_Library) */