2588 lines
94 KiB
C
2588 lines
94 KiB
C
/***************************************************************************//**
|
|
* @file em_emu.c
|
|
* @brief Energy Management Unit (EMU) Peripheral API
|
|
* @version 5.2.2
|
|
*******************************************************************************
|
|
* # License
|
|
* <b>Copyright 2016 Silicon Laboratories, Inc. http://www.silabs.com</b>
|
|
*******************************************************************************
|
|
*
|
|
* Permission is granted to anyone to use this software for any purpose,
|
|
* including commercial applications, and to alter it and redistribute it
|
|
* freely, subject to the following restrictions:
|
|
*
|
|
* 1. The origin of this software must not be misrepresented; you must not
|
|
* claim that you wrote the original software.
|
|
* 2. Altered source versions must be plainly marked as such, and must not be
|
|
* misrepresented as being the original software.
|
|
* 3. This notice may not be removed or altered from any source distribution.
|
|
*
|
|
* DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Silicon Labs has no
|
|
* obligation to support this Software. Silicon Labs 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.
|
|
*
|
|
* Silicon Labs 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 <limits.h>
|
|
|
|
#include "em_emu.h"
|
|
#if defined(EMU_PRESENT) && (EMU_COUNT > 0)
|
|
|
|
#include "em_cmu.h"
|
|
#include "em_system.h"
|
|
#include "em_common.h"
|
|
#include "em_assert.h"
|
|
|
|
/***************************************************************************//**
|
|
* @addtogroup emlib
|
|
* @{
|
|
******************************************************************************/
|
|
|
|
/***************************************************************************//**
|
|
* @addtogroup EMU
|
|
* @brief Energy Management Unit (EMU) Peripheral API
|
|
* @details
|
|
* This module contains functions to control the EMU peripheral of Silicon
|
|
* Labs 32-bit MCUs and SoCs. The EMU handles the different low energy modes
|
|
* in Silicon Labs microcontrollers.
|
|
* @{
|
|
******************************************************************************/
|
|
|
|
/* 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
|
|
|
|
/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
|
|
#if defined(_SILICON_LABS_32B_SERIES_0)
|
|
/* Fix for errata EMU_E107 - non-WIC interrupt masks.
|
|
* Zero Gecko and future families are not affected by errata EMU_E107 */
|
|
#if defined(_EFM32_GECKO_FAMILY)
|
|
#define ERRATA_FIX_EMU_E107_EN
|
|
#define NON_WIC_INT_MASK_0 (~(0x0dfc0323U))
|
|
#define NON_WIC_INT_MASK_1 (~(0x0U))
|
|
|
|
#elif defined(_EFM32_TINY_FAMILY)
|
|
#define ERRATA_FIX_EMU_E107_EN
|
|
#define NON_WIC_INT_MASK_0 (~(0x001be323U))
|
|
#define NON_WIC_INT_MASK_1 (~(0x0U))
|
|
|
|
#elif defined(_EFM32_GIANT_FAMILY)
|
|
#define ERRATA_FIX_EMU_E107_EN
|
|
#define NON_WIC_INT_MASK_0 (~(0xff020e63U))
|
|
#define NON_WIC_INT_MASK_1 (~(0x00000046U))
|
|
|
|
#elif defined(_EFM32_WONDER_FAMILY)
|
|
#define ERRATA_FIX_EMU_E107_EN
|
|
#define NON_WIC_INT_MASK_0 (~(0xff020e63U))
|
|
#define NON_WIC_INT_MASK_1 (~(0x00000046U))
|
|
|
|
#endif
|
|
#endif
|
|
|
|
/* Fix for errata EMU_E108 - High Current Consumption on EM4 Entry. */
|
|
#if defined(_SILICON_LABS_32B_SERIES_0) && defined(_EFM32_HAPPY_FAMILY)
|
|
#define ERRATA_FIX_EMU_E108_EN
|
|
#endif
|
|
|
|
/* Fix for errata EMU_E208 - Occasional Full Reset After Exiting EM4H */
|
|
#if defined(_SILICON_LABS_GECKO_INTERNAL_SDID_80)
|
|
#define ERRATA_FIX_EMU_E208_EN
|
|
#endif
|
|
|
|
/* Enable FETCNT tuning errata fix */
|
|
#if defined(_SILICON_LABS_GECKO_INTERNAL_SDID_80)
|
|
#define ERRATA_FIX_DCDC_FETCNT_SET_EN
|
|
#endif
|
|
|
|
/* Enable LN handshake errata fix */
|
|
#if defined(_SILICON_LABS_GECKO_INTERNAL_SDID_80)
|
|
#define ERRATA_FIX_DCDC_LNHS_BLOCK_EN
|
|
typedef enum {
|
|
errataFixDcdcHsInit,
|
|
errataFixDcdcHsTrimSet,
|
|
errataFixDcdcHsBypassLn,
|
|
errataFixDcdcHsLnWaitDone
|
|
} errataFixDcdcHs_TypeDef;
|
|
static errataFixDcdcHs_TypeDef errataFixDcdcHsState = errataFixDcdcHsInit;
|
|
#endif
|
|
|
|
/* Used to figure out if a memory address is inside or outside of a RAM block.
|
|
* A memory address is inside a RAM block if the address is greater than the
|
|
* RAM block address. */
|
|
#define ADDRESS_NOT_IN_BLOCK(addr, block) ((addr) <= (block))
|
|
|
|
/* RAM Block layout for various device families. Note that some devices
|
|
* have special layout in RAM0 and some devices have a special RAM block
|
|
* at the end of their block layout. */
|
|
#if defined(_SILICON_LABS_GECKO_INTERNAL_SDID_84)
|
|
#define RAM1_BLOCKS 2
|
|
#define RAM1_BLOCK_SIZE 0x10000 // 64 kB blocks
|
|
#define RAM2_BLOCKS 1
|
|
#define RAM2_BLOCK_SIZE 0x800 // 2 kB block
|
|
#elif defined(_SILICON_LABS_GECKO_INTERNAL_SDID_89)
|
|
#define RAM0_BLOCKS 2
|
|
#define RAM0_BLOCK_SIZE 0x4000
|
|
#define RAM1_BLOCKS 2
|
|
#define RAM1_BLOCK_SIZE 0x4000 // 16 kB blocks
|
|
#define RAM2_BLOCKS 1
|
|
#define RAM2_BLOCK_SIZE 0x800 // 2 kB block
|
|
#elif defined(_SILICON_LABS_GECKO_INTERNAL_SDID_95)
|
|
#define RAM0_BLOCKS 1
|
|
#define RAM0_BLOCK_SIZE 0x4000 // 16 kB block
|
|
#define RAM1_BLOCKS 1
|
|
#define RAM1_BLOCK_SIZE 0x4000 // 16 kB block
|
|
#define RAM2_BLOCKS 1
|
|
#define RAM2_BLOCK_SIZE 0x800 // 2 kB block
|
|
#elif defined(_SILICON_LABS_32B_SERIES_0) && defined(_EFM32_GIANT_FAMILY)
|
|
#define RAM0_BLOCKS 4
|
|
#define RAM0_BLOCK_SIZE 0x8000 // 32 kB blocks
|
|
#elif defined(_SILICON_LABS_32B_SERIES_0) && defined(_EFM32_GECKO_FAMILY)
|
|
#define RAM0_BLOCKS 4
|
|
#define RAM0_BLOCK_SIZE 0x1000 // 4 kB blocks
|
|
#elif defined(_SILICON_LABS_32B_SERIES_1) && defined(_EFM32_GIANT_FAMILY)
|
|
#define RAM0_BLOCKS 8
|
|
#define RAM0_BLOCK_SIZE 0x4000 // 16 kB blocks
|
|
#define RAM1_BLOCKS 8
|
|
#define RAM1_BLOCK_SIZE 0x4000 // 16 kB blocks
|
|
#define RAM2_BLOCKS 4
|
|
#define RAM2_BLOCK_SIZE 0x10000 // 64 kB blocks
|
|
#endif
|
|
|
|
#if defined(_SILICON_LABS_32B_SERIES_0)
|
|
/* RAM_MEM_END on Gecko devices have a value larger than the SRAM_SIZE */
|
|
#define RAM0_END (SRAM_BASE + SRAM_SIZE - 1)
|
|
#else
|
|
#define RAM0_END RAM_MEM_END
|
|
#endif
|
|
|
|
#if defined(CMU_STATUS_HFXOSHUNTOPTRDY)
|
|
#define HFXO_STATUS_READY_FLAGS (CMU_STATUS_HFXOPEAKDETRDY | CMU_STATUS_HFXOSHUNTOPTRDY)
|
|
#elif defined(CMU_STATUS_HFXOPEAKDETRDY)
|
|
#define HFXO_STATUS_READY_FLAGS (CMU_STATUS_HFXOPEAKDETRDY)
|
|
#endif
|
|
|
|
/** @endcond */
|
|
|
|
#if defined(_EMU_DCDCCTRL_MASK)
|
|
/* DCDCTODVDD output range min/max */
|
|
#if !defined(PWRCFG_DCDCTODVDD_VMIN)
|
|
#define PWRCFG_DCDCTODVDD_VMIN 1800
|
|
#endif
|
|
#if !defined(PWRCFG_DCDCTODVDD_VMAX)
|
|
#define PWRCFG_DCDCTODVDD_VMAX 3000
|
|
#endif
|
|
#endif
|
|
|
|
/*******************************************************************************
|
|
*************************** LOCAL VARIABLES ********************************
|
|
******************************************************************************/
|
|
|
|
/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
|
|
|
|
/* Static user configuration */
|
|
#if defined(_EMU_DCDCCTRL_MASK)
|
|
static uint16_t dcdcMaxCurrent_mA;
|
|
static uint16_t dcdcEm01LoadCurrent_mA;
|
|
static EMU_DcdcLnReverseCurrentControl_TypeDef dcdcReverseCurrentControl;
|
|
#endif
|
|
#if defined(_EMU_CMD_EM01VSCALE0_MASK)
|
|
static EMU_EM01Init_TypeDef vScaleEM01Config = { false };
|
|
#endif
|
|
/** @endcond */
|
|
|
|
/*******************************************************************************
|
|
************************** LOCAL FUNCTIONS ********************************
|
|
******************************************************************************/
|
|
|
|
/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
|
|
|
|
#if defined(_EMU_CMD_EM01VSCALE0_MASK)
|
|
/* Convert from level to EM0 and 1 command bit */
|
|
__STATIC_INLINE uint32_t vScaleEM01Cmd(EMU_VScaleEM01_TypeDef level)
|
|
{
|
|
return EMU_CMD_EM01VSCALE0 << (_EMU_STATUS_VSCALE_VSCALE0 - (uint32_t)level);
|
|
}
|
|
#endif
|
|
|
|
/***************************************************************************//**
|
|
* @brief
|
|
* Save/restore/update oscillator, core clock and voltage scaling configuration on
|
|
* EM2 or EM3 entry/exit.
|
|
*
|
|
* @details
|
|
* Hardware may automatically change oscillator and voltage scaling configuration
|
|
* when going into or out of an energy mode. Static data in this function keeps track of
|
|
* such configuration bits and is used to restore state if needed.
|
|
*
|
|
******************************************************************************/
|
|
typedef enum {
|
|
emState_Save, /* Save EMU and CMU state */
|
|
emState_Restore, /* Restore and unlock */
|
|
} emState_TypeDef;
|
|
|
|
static void emState(emState_TypeDef action)
|
|
{
|
|
uint32_t oscEnCmd;
|
|
uint32_t cmuLocked;
|
|
static uint32_t cmuStatus;
|
|
static CMU_Select_TypeDef hfClock;
|
|
#if defined(_EMU_CMD_EM01VSCALE0_MASK)
|
|
static uint8_t vScaleStatus;
|
|
#endif
|
|
|
|
/* Save or update state */
|
|
if (action == emState_Save) {
|
|
/* Save configuration. */
|
|
cmuStatus = CMU->STATUS;
|
|
hfClock = CMU_ClockSelectGet(cmuClock_HF);
|
|
#if defined(_EMU_CMD_EM01VSCALE0_MASK)
|
|
/* Save vscale */
|
|
EMU_VScaleWait();
|
|
vScaleStatus = (uint8_t)((EMU->STATUS & _EMU_STATUS_VSCALE_MASK)
|
|
>> _EMU_STATUS_VSCALE_SHIFT);
|
|
#endif
|
|
} else if (action == emState_Restore) { /* Restore state */
|
|
/* Apply saved configuration. */
|
|
#if defined(_EMU_CMD_EM01VSCALE0_MASK)
|
|
/* Restore EM0 and 1 voltage scaling level. EMU_VScaleWait() is called later,
|
|
just before HF clock select is set. */
|
|
EMU->CMD = vScaleEM01Cmd((EMU_VScaleEM01_TypeDef)vScaleStatus);
|
|
#endif
|
|
|
|
/* CMU registers may be locked */
|
|
cmuLocked = CMU->LOCK & CMU_LOCK_LOCKKEY_LOCKED;
|
|
CMU_Unlock();
|
|
|
|
/* AUXHFRCO are automatically disabled (except if using debugger). */
|
|
/* HFRCO, USHFRCO and HFXO are automatically disabled. */
|
|
/* LFRCO/LFXO may be disabled by SW in EM3. */
|
|
/* Restore according to status prior to entering energy mode. */
|
|
oscEnCmd = 0;
|
|
oscEnCmd |= ((cmuStatus & CMU_STATUS_HFRCOENS) ? CMU_OSCENCMD_HFRCOEN : 0);
|
|
oscEnCmd |= ((cmuStatus & CMU_STATUS_AUXHFRCOENS) ? CMU_OSCENCMD_AUXHFRCOEN : 0);
|
|
oscEnCmd |= ((cmuStatus & CMU_STATUS_LFRCOENS) ? CMU_OSCENCMD_LFRCOEN : 0);
|
|
oscEnCmd |= ((cmuStatus & CMU_STATUS_HFXOENS) ? CMU_OSCENCMD_HFXOEN : 0);
|
|
oscEnCmd |= ((cmuStatus & CMU_STATUS_LFXOENS) ? CMU_OSCENCMD_LFXOEN : 0);
|
|
#if defined(_CMU_STATUS_USHFRCOENS_MASK)
|
|
oscEnCmd |= ((cmuStatus & CMU_STATUS_USHFRCOENS) ? CMU_OSCENCMD_USHFRCOEN : 0);
|
|
#endif
|
|
CMU->OSCENCMD = oscEnCmd;
|
|
|
|
#if defined(_EMU_STATUS_VSCALE_MASK)
|
|
/* Wait for upscale to complete and then restore selected clock */
|
|
EMU_VScaleWait();
|
|
#endif
|
|
|
|
if (hfClock != cmuSelect_HFRCO) {
|
|
CMU_ClockSelectSet(cmuClock_HF, hfClock);
|
|
}
|
|
|
|
/* 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();
|
|
}
|
|
}
|
|
}
|
|
|
|
#if defined(ERRATA_FIX_EMU_E107_EN)
|
|
/* Get enable conditions for errata EMU_E107 fix. */
|
|
__STATIC_INLINE bool getErrataFixEmuE107En(void)
|
|
{
|
|
/* SYSTEM_ChipRevisionGet could have been used here, but we would like a
|
|
* faster implementation in this case.
|
|
*/
|
|
uint16_t majorMinorRev;
|
|
|
|
/* CHIP MAJOR bit [3:0] */
|
|
majorMinorRev = ((ROMTABLE->PID0 & _ROMTABLE_PID0_REVMAJOR_MASK)
|
|
>> _ROMTABLE_PID0_REVMAJOR_SHIFT)
|
|
<< 8;
|
|
/* CHIP MINOR bit [7:4] */
|
|
majorMinorRev |= ((ROMTABLE->PID2 & _ROMTABLE_PID2_REVMINORMSB_MASK)
|
|
>> _ROMTABLE_PID2_REVMINORMSB_SHIFT)
|
|
<< 4;
|
|
/* CHIP MINOR bit [3:0] */
|
|
majorMinorRev |= (ROMTABLE->PID3 & _ROMTABLE_PID3_REVMINORLSB_MASK)
|
|
>> _ROMTABLE_PID3_REVMINORLSB_SHIFT;
|
|
|
|
#if defined(_EFM32_GECKO_FAMILY)
|
|
return (majorMinorRev <= 0x0103);
|
|
#elif defined(_EFM32_TINY_FAMILY)
|
|
return (majorMinorRev <= 0x0102);
|
|
#elif defined(_EFM32_GIANT_FAMILY)
|
|
return (majorMinorRev <= 0x0103) || (majorMinorRev == 0x0204);
|
|
#elif defined(_EFM32_WONDER_FAMILY)
|
|
return (majorMinorRev == 0x0100);
|
|
#else
|
|
/* Zero Gecko and future families are not affected by errata EMU_E107 */
|
|
return false;
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
/* LP prepare / LN restore P/NFET count */
|
|
#define DCDC_LP_PFET_CNT 7
|
|
#define DCDC_LP_NFET_CNT 7
|
|
#if defined(ERRATA_FIX_DCDC_FETCNT_SET_EN)
|
|
static void currentLimitersUpdate(void);
|
|
static void dcdcFetCntSet(bool lpModeSet)
|
|
{
|
|
uint32_t tmp;
|
|
static uint32_t emuDcdcMiscCtrlReg;
|
|
|
|
if (lpModeSet) {
|
|
emuDcdcMiscCtrlReg = EMU->DCDCMISCCTRL;
|
|
tmp = EMU->DCDCMISCCTRL
|
|
& ~(_EMU_DCDCMISCCTRL_PFETCNT_MASK | _EMU_DCDCMISCCTRL_NFETCNT_MASK);
|
|
tmp |= (DCDC_LP_PFET_CNT << _EMU_DCDCMISCCTRL_PFETCNT_SHIFT)
|
|
| (DCDC_LP_NFET_CNT << _EMU_DCDCMISCCTRL_NFETCNT_SHIFT);
|
|
EMU->DCDCMISCCTRL = tmp;
|
|
currentLimitersUpdate();
|
|
} else {
|
|
EMU->DCDCMISCCTRL = emuDcdcMiscCtrlReg;
|
|
currentLimitersUpdate();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if defined(ERRATA_FIX_DCDC_LNHS_BLOCK_EN)
|
|
static void dcdcHsFixLnBlock(void)
|
|
{
|
|
#define EMU_DCDCSTATUS (*(volatile uint32_t *)(EMU_BASE + 0x7C))
|
|
if ((errataFixDcdcHsState == errataFixDcdcHsTrimSet)
|
|
|| (errataFixDcdcHsState == errataFixDcdcHsBypassLn)) {
|
|
/* Wait for LNRUNNING */
|
|
if ((EMU->DCDCCTRL & _EMU_DCDCCTRL_DCDCMODE_MASK) == EMU_DCDCCTRL_DCDCMODE_LOWNOISE) {
|
|
while (!(EMU_DCDCSTATUS & (0x1 << 16))) ;
|
|
}
|
|
errataFixDcdcHsState = errataFixDcdcHsLnWaitDone;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if defined(_EMU_CTRL_EM23VSCALE_MASK)
|
|
/* Configure EMU and CMU for EM2 and 3 voltage downscale */
|
|
static void vScaleDownEM23Setup(void)
|
|
{
|
|
uint32_t hfSrcClockFrequency;
|
|
|
|
EMU_VScaleEM23_TypeDef scaleEM23Voltage =
|
|
(EMU_VScaleEM23_TypeDef)((EMU->CTRL & _EMU_CTRL_EM23VSCALE_MASK)
|
|
>> _EMU_CTRL_EM23VSCALE_SHIFT);
|
|
|
|
EMU_VScaleEM01_TypeDef currentEM01Voltage =
|
|
(EMU_VScaleEM01_TypeDef)((EMU->STATUS & _EMU_STATUS_VSCALE_MASK)
|
|
>> _EMU_STATUS_VSCALE_SHIFT);
|
|
|
|
/* Wait until previous scaling is done. */
|
|
EMU_VScaleWait();
|
|
|
|
/* Inverse coding. */
|
|
if ((uint32_t)scaleEM23Voltage > (uint32_t)currentEM01Voltage) {
|
|
/* Set safe clock and wait-states. */
|
|
if (scaleEM23Voltage == emuVScaleEM23_LowPower) {
|
|
hfSrcClockFrequency = CMU_ClockDivGet(cmuClock_HF) * CMU_ClockFreqGet(cmuClock_HF);
|
|
/* Set default low power voltage HFRCO band as HF clock. */
|
|
if (hfSrcClockFrequency > CMU_VSCALEEM01_LOWPOWER_VOLTAGE_CLOCK_MAX) {
|
|
CMU_HFRCOBandSet(cmuHFRCOFreq_19M0Hz);
|
|
}
|
|
CMU_ClockSelectSet(cmuClock_HF, cmuSelect_HFRCO);
|
|
} else {
|
|
/* Other voltage scaling levels are not currently supported. */
|
|
EFM_ASSERT(false);
|
|
}
|
|
} else {
|
|
/* Same voltage or hardware will scale to min(EMU_CTRL_EM23VSCALE, EMU_STATUS_VSCALE) */
|
|
}
|
|
}
|
|
#endif
|
|
/** @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.
|
|
* @par
|
|
* If HFXO autostart and select is enabled by using CMU_HFXOAutostartEnable(),
|
|
* the starting and selecting of the core clocks will be identical to the user
|
|
* independently of the value of the @p restore parameter when waking up on
|
|
* the wakeup sources corresponding to the autostart and select setting.
|
|
* @par
|
|
* If voltage scaling is supported, the restore parameter is true and the EM0
|
|
* voltage scaling level is set higher than the EM2 level, then the EM0 level is
|
|
* also restored.
|
|
*
|
|
* @param[in] restore
|
|
* @li true - save and restore oscillators, clocks and voltage scaling, see
|
|
* function details.
|
|
* @li false - do not save and 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)
|
|
{
|
|
#if defined(ERRATA_FIX_EMU_E107_EN)
|
|
bool errataFixEmuE107En;
|
|
uint32_t nonWicIntEn[2];
|
|
#endif
|
|
|
|
/* Only save EMU and CMU state if restored on wake-up. */
|
|
if (restore) {
|
|
emState(emState_Save);
|
|
}
|
|
|
|
#if defined(_EMU_CTRL_EM23VSCALE_MASK)
|
|
vScaleDownEM23Setup();
|
|
#endif
|
|
|
|
/* Enter Cortex deep sleep mode */
|
|
SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
|
|
|
|
/* Fix for errata EMU_E107 - store non-WIC interrupt enable flags.
|
|
Disable the enabled non-WIC interrupts. */
|
|
#if defined(ERRATA_FIX_EMU_E107_EN)
|
|
errataFixEmuE107En = getErrataFixEmuE107En();
|
|
if (errataFixEmuE107En) {
|
|
nonWicIntEn[0] = NVIC->ISER[0] & NON_WIC_INT_MASK_0;
|
|
NVIC->ICER[0] = nonWicIntEn[0];
|
|
#if (NON_WIC_INT_MASK_1 != (~(0x0U)))
|
|
nonWicIntEn[1] = NVIC->ISER[1] & NON_WIC_INT_MASK_1;
|
|
NVIC->ICER[1] = nonWicIntEn[1];
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
#if defined(ERRATA_FIX_DCDC_FETCNT_SET_EN)
|
|
dcdcFetCntSet(true);
|
|
#endif
|
|
#if defined(ERRATA_FIX_DCDC_LNHS_BLOCK_EN)
|
|
dcdcHsFixLnBlock();
|
|
#endif
|
|
|
|
__WFI();
|
|
|
|
#if defined(ERRATA_FIX_DCDC_FETCNT_SET_EN)
|
|
dcdcFetCntSet(false);
|
|
#endif
|
|
|
|
/* Fix for errata EMU_E107 - restore state of non-WIC interrupt enable flags. */
|
|
#if defined(ERRATA_FIX_EMU_E107_EN)
|
|
if (errataFixEmuE107En) {
|
|
NVIC->ISER[0] = nonWicIntEn[0];
|
|
#if (NON_WIC_INT_MASK_1 != (~(0x0U)))
|
|
NVIC->ISER[1] = nonWicIntEn[1];
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
/* Restore oscillators/clocks and voltage scaling if supported. */
|
|
if (restore) {
|
|
emState(emState_Restore);
|
|
} else {
|
|
/* If not restoring, and original clock was not HFRCO, we have to */
|
|
/* update CMSIS core clock variable since HF clock has changed */
|
|
/* to HFRCO. */
|
|
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.
|
|
* @par
|
|
* If voltage scaling is supported, the restore parameter is true and the EM0
|
|
* voltage scaling level is set higher than the EM3 level, then the EM0 level is
|
|
* also restored.
|
|
*
|
|
* @param[in] restore
|
|
* @li true - save and restore oscillators, clocks and voltage scaling, see
|
|
* function details.
|
|
* @li false - do not save and 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;
|
|
|
|
#if defined(ERRATA_FIX_EMU_E107_EN)
|
|
bool errataFixEmuE107En;
|
|
uint32_t nonWicIntEn[2];
|
|
#endif
|
|
|
|
/* Only save EMU and CMU state if restored on wake-up. */
|
|
if (restore) {
|
|
emState(emState_Save);
|
|
}
|
|
|
|
#if defined(_EMU_CTRL_EM23VSCALE_MASK)
|
|
vScaleDownEM23Setup();
|
|
#endif
|
|
|
|
/* 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 deep sleep mode */
|
|
SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
|
|
|
|
/* Fix for errata EMU_E107 - store non-WIC interrupt enable flags.
|
|
Disable the enabled non-WIC interrupts. */
|
|
#if defined(ERRATA_FIX_EMU_E107_EN)
|
|
errataFixEmuE107En = getErrataFixEmuE107En();
|
|
if (errataFixEmuE107En) {
|
|
nonWicIntEn[0] = NVIC->ISER[0] & NON_WIC_INT_MASK_0;
|
|
NVIC->ICER[0] = nonWicIntEn[0];
|
|
#if (NON_WIC_INT_MASK_1 != (~(0x0U)))
|
|
nonWicIntEn[1] = NVIC->ISER[1] & NON_WIC_INT_MASK_1;
|
|
NVIC->ICER[1] = nonWicIntEn[1];
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
#if defined(ERRATA_FIX_DCDC_FETCNT_SET_EN)
|
|
dcdcFetCntSet(true);
|
|
#endif
|
|
#if defined(ERRATA_FIX_DCDC_LNHS_BLOCK_EN)
|
|
dcdcHsFixLnBlock();
|
|
#endif
|
|
|
|
__WFI();
|
|
|
|
#if defined(ERRATA_FIX_DCDC_FETCNT_SET_EN)
|
|
dcdcFetCntSet(false);
|
|
#endif
|
|
|
|
/* Fix for errata EMU_E107 - restore state of non-WIC interrupt enable flags. */
|
|
#if defined(ERRATA_FIX_EMU_E107_EN)
|
|
if (errataFixEmuE107En) {
|
|
NVIC->ISER[0] = nonWicIntEn[0];
|
|
#if (NON_WIC_INT_MASK_1 != (~(0x0U)))
|
|
NVIC->ISER[1] = nonWicIntEn[1];
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
/* Restore oscillators/clocks and voltage scaling if supported. */
|
|
if (restore) {
|
|
emState(emState_Restore);
|
|
} else {
|
|
/* If not restoring, and original clock was not HFRCO, we have to */
|
|
/* update CMSIS core clock variable since HF clock has changed */
|
|
/* to HFRCO. */
|
|
SystemCoreClockUpdate();
|
|
}
|
|
}
|
|
|
|
/***************************************************************************//**
|
|
* @brief
|
|
* Save CMU HF clock select state, oscillator enable and voltage scaling
|
|
* (if available) before @ref EMU_EnterEM2() or @ref EMU_EnterEM3() are called
|
|
* with the restore parameter set to false. Calling this function is
|
|
* equivalent to calling @ref EMU_EnterEM2() or @ref EMU_EnterEM3() with the
|
|
* restore parameter set to true, but it allows the state to be saved without
|
|
* going to sleep. The state can be restored manually by calling
|
|
* @ref EMU_Restore().
|
|
******************************************************************************/
|
|
void EMU_Save(void)
|
|
{
|
|
emState(emState_Save);
|
|
}
|
|
|
|
/***************************************************************************//**
|
|
* @brief
|
|
* Restore CMU HF clock select state, oscillator enable and voltage scaling
|
|
* (if available) after @ref EMU_EnterEM2() or @ref EMU_EnterEM3() are called
|
|
* with the restore parameter set to false. Calling this function is
|
|
* equivalent to calling @ref EMU_EnterEM2() or @ref EMU_EnterEM3() with the
|
|
* restore parameter set to true, but it allows the application to evaluate the
|
|
* wakeup reason before restoring state.
|
|
******************************************************************************/
|
|
void EMU_Restore(void)
|
|
{
|
|
emState(emState_Restore);
|
|
}
|
|
|
|
/***************************************************************************//**
|
|
* @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;
|
|
|
|
#if defined(_EMU_EM4CTRL_EM4ENTRY_SHIFT)
|
|
uint32_t em4seq2 = (EMU->EM4CTRL & ~_EMU_EM4CTRL_EM4ENTRY_MASK)
|
|
| (2 << _EMU_EM4CTRL_EM4ENTRY_SHIFT);
|
|
uint32_t em4seq3 = (EMU->EM4CTRL & ~_EMU_EM4CTRL_EM4ENTRY_MASK)
|
|
| (3 << _EMU_EM4CTRL_EM4ENTRY_SHIFT);
|
|
#else
|
|
uint32_t em4seq2 = (EMU->CTRL & ~_EMU_CTRL_EM4CTRL_MASK)
|
|
| (2 << _EMU_CTRL_EM4CTRL_SHIFT);
|
|
uint32_t em4seq3 = (EMU->CTRL & ~_EMU_CTRL_EM4CTRL_MASK)
|
|
| (3 << _EMU_CTRL_EM4CTRL_SHIFT);
|
|
#endif
|
|
|
|
/* Make sure register write lock is disabled */
|
|
EMU_Unlock();
|
|
|
|
#if defined(_EMU_EM4CTRL_MASK)
|
|
if ((EMU->EM4CTRL & _EMU_EM4CTRL_EM4STATE_MASK) == EMU_EM4CTRL_EM4STATE_EM4S) {
|
|
uint32_t dcdcMode = EMU->DCDCCTRL & _EMU_DCDCCTRL_DCDCMODE_MASK;
|
|
if (dcdcMode == EMU_DCDCCTRL_DCDCMODE_LOWNOISE
|
|
|| dcdcMode == EMU_DCDCCTRL_DCDCMODE_LOWPOWER) {
|
|
/* DCDC is not supported in EM4S so we switch DCDC to bypass mode before
|
|
* entering EM4S */
|
|
EMU_DCDCModeSet(emuDcdcMode_Bypass);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if defined(_EMU_EM4CTRL_MASK) && defined(ERRATA_FIX_EMU_E208_EN)
|
|
if (EMU->EM4CTRL & EMU_EM4CTRL_EM4STATE_EM4H) {
|
|
/* Fix for errata EMU_E208 - Occasional Full Reset After Exiting EM4H.
|
|
* Full description of errata fix can be found in the errata document. */
|
|
__disable_irq();
|
|
*(volatile uint32_t *)(EMU_BASE + 0x190) = 0x0000ADE8UL;
|
|
*(volatile uint32_t *)(EMU_BASE + 0x198) |= (0x1UL << 7);
|
|
*(volatile uint32_t *)(EMU_BASE + 0x88) |= (0x1UL << 8);
|
|
}
|
|
#endif
|
|
|
|
#if defined(ERRATA_FIX_EMU_E108_EN)
|
|
/* Fix for errata EMU_E108 - High Current Consumption on EM4 Entry. */
|
|
__disable_irq();
|
|
*(volatile uint32_t *)0x400C80E4 = 0;
|
|
#endif
|
|
|
|
#if defined(ERRATA_FIX_DCDC_FETCNT_SET_EN)
|
|
dcdcFetCntSet(true);
|
|
#endif
|
|
#if defined(ERRATA_FIX_DCDC_LNHS_BLOCK_EN)
|
|
dcdcHsFixLnBlock();
|
|
#endif
|
|
|
|
for (i = 0; i < 4; i++) {
|
|
#if defined(_EMU_EM4CTRL_EM4ENTRY_SHIFT)
|
|
EMU->EM4CTRL = em4seq2;
|
|
EMU->EM4CTRL = em4seq3;
|
|
}
|
|
EMU->EM4CTRL = em4seq2;
|
|
#else
|
|
EMU->CTRL = em4seq2;
|
|
EMU->CTRL = em4seq3;
|
|
}
|
|
EMU->CTRL = em4seq2;
|
|
#endif
|
|
}
|
|
|
|
#if defined(_EMU_EM4CTRL_MASK)
|
|
/***************************************************************************//**
|
|
* @brief
|
|
* Enter energy mode 4 hibernate (EM4H).
|
|
*
|
|
* @note
|
|
* Retention of clocks and GPIO in EM4 can be configured using
|
|
* @ref EMU_EM4Init before calling this function.
|
|
******************************************************************************/
|
|
void EMU_EnterEM4H(void)
|
|
{
|
|
BUS_RegBitWrite(&EMU->EM4CTRL, _EMU_EM4CTRL_EM4STATE_SHIFT, 1);
|
|
EMU_EnterEM4();
|
|
}
|
|
|
|
/***************************************************************************//**
|
|
* @brief
|
|
* Enter energy mode 4 shutoff (EM4S).
|
|
*
|
|
* @note
|
|
* Retention of clocks and GPIO in EM4 can be configured using
|
|
* @ref EMU_EM4Init before calling this function.
|
|
******************************************************************************/
|
|
void EMU_EnterEM4S(void)
|
|
{
|
|
BUS_RegBitWrite(&EMU->EM4CTRL, _EMU_EM4CTRL_EM4STATE_SHIFT, 0);
|
|
EMU_EnterEM4();
|
|
}
|
|
#endif
|
|
|
|
/***************************************************************************//**
|
|
* @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 reference manual for available
|
|
* memory blocks for a device.
|
|
*
|
|
* @note
|
|
* Only a POR reset can power up the specified memory block(s) after powerdown.
|
|
*
|
|
* @deprecated
|
|
* This function is deprecated, use @ref EMU_RamPowerDown() instead which
|
|
* maps a user provided memory range into RAM blocks to power down.
|
|
******************************************************************************/
|
|
void EMU_MemPwrDown(uint32_t blocks)
|
|
{
|
|
#if defined(_EMU_MEMCTRL_MASK)
|
|
EMU->MEMCTRL = blocks & _EMU_MEMCTRL_MASK;
|
|
#elif defined(_EMU_RAM0CTRL_MASK)
|
|
EMU->RAM0CTRL = blocks & _EMU_RAM0CTRL_MASK;
|
|
#else
|
|
(void)blocks;
|
|
#endif
|
|
}
|
|
|
|
/***************************************************************************//**
|
|
* @brief
|
|
* Power down RAM memory blocks.
|
|
*
|
|
* @details
|
|
* This function will power down all the RAM blocks that are within a given
|
|
* range. The RAM block layout is different between device families, so this
|
|
* function can be used in a generic way to power down a RAM memory region
|
|
* which is known to be unused.
|
|
*
|
|
* This function will only power down blocks which are completely enclosed
|
|
* by the memory range given by [start, end).
|
|
*
|
|
* Here is an example of how to power down all RAM blocks except the first
|
|
* one. The first RAM block is special in that it cannot be powered down
|
|
* by the hardware. The size of this first RAM block is device specific
|
|
* see the reference manual to find the RAM block sizes.
|
|
*
|
|
* @code
|
|
* EMU_RamPowerDown(SRAM_BASE, SRAM_BASE + SRAM_SIZE);
|
|
* @endcode
|
|
*
|
|
* @note
|
|
* Only a POR reset can power up the specified memory block(s) after powerdown.
|
|
*
|
|
* @param[in] start
|
|
* The start address of the RAM region to power down. This address is
|
|
* inclusive.
|
|
*
|
|
* @param[in] end
|
|
* The end address of the RAM region to power down. This address is
|
|
* exclusive. If this parameter is 0, then all RAM blocks contained in the
|
|
* region from start to the upper RAM address will be powered down.
|
|
******************************************************************************/
|
|
void EMU_RamPowerDown(uint32_t start, uint32_t end)
|
|
{
|
|
uint32_t mask = 0;
|
|
|
|
if (end == 0) {
|
|
end = SRAM_BASE + SRAM_SIZE;
|
|
}
|
|
|
|
// Check to see if something in RAM0 can be powered down
|
|
if (end > RAM0_END) {
|
|
#if defined(_SILICON_LABS_GECKO_INTERNAL_SDID_84) // EFM32xG12 and EFR32xG12
|
|
// Block 0 is 16 kB and cannot be powered off
|
|
mask |= ADDRESS_NOT_IN_BLOCK(start, 0x20004000) << 0; // Block 1, 16 kB
|
|
mask |= ADDRESS_NOT_IN_BLOCK(start, 0x20008000) << 1; // Block 2, 16 kB
|
|
mask |= ADDRESS_NOT_IN_BLOCK(start, 0x2000C000) << 2; // Block 3, 16 kB
|
|
mask |= ADDRESS_NOT_IN_BLOCK(start, 0x20010000) << 3; // Block 4, 64 kB
|
|
#elif defined(_SILICON_LABS_GECKO_INTERNAL_SDID_80) // EFM32xG1 and EFR32xG1
|
|
// Block 0 is 4 kB and cannot be powered off
|
|
mask |= ADDRESS_NOT_IN_BLOCK(start, 0x20001000) << 0; // Block 1, 4 kB
|
|
mask |= ADDRESS_NOT_IN_BLOCK(start, 0x20002000) << 1; // Block 2, 8 kB
|
|
mask |= ADDRESS_NOT_IN_BLOCK(start, 0x20004000) << 2; // Block 3, 8 kB
|
|
mask |= ADDRESS_NOT_IN_BLOCK(start, 0x20006000) << 3; // Block 4, 7 kB
|
|
#elif defined(RAM0_BLOCKS)
|
|
// These platforms have equally sized RAM blocks
|
|
for (int i = 1; i < RAM0_BLOCKS; i++) {
|
|
mask |= ADDRESS_NOT_IN_BLOCK(start, RAM_MEM_BASE + (i * RAM0_BLOCK_SIZE)) << (i - 1);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// Power down the selected blocks
|
|
#if defined(_EMU_MEMCTRL_MASK)
|
|
EMU->MEMCTRL = EMU->MEMCTRL | mask;
|
|
#elif defined(_EMU_RAM0CTRL_MASK)
|
|
EMU->RAM0CTRL = EMU->RAM0CTRL | mask;
|
|
#else
|
|
// These devices are unable to power down RAM blocks
|
|
(void) mask;
|
|
(void) start;
|
|
#endif
|
|
|
|
#if defined(RAM1_MEM_END)
|
|
mask = 0;
|
|
if (end > RAM1_MEM_END) {
|
|
for (int i = 0; i < RAM1_BLOCKS; i++) {
|
|
mask |= ADDRESS_NOT_IN_BLOCK(start, RAM1_MEM_BASE + (i * RAM1_BLOCK_SIZE)) << i;
|
|
}
|
|
}
|
|
EMU->RAM1CTRL |= mask;
|
|
#endif
|
|
|
|
#if defined(RAM2_MEM_END)
|
|
mask = 0;
|
|
if (end > RAM2_MEM_END) {
|
|
for (int i = 0; i < RAM2_BLOCKS; i++) {
|
|
mask |= ADDRESS_NOT_IN_BLOCK(start, RAM2_MEM_BASE + (i * RAM2_BLOCK_SIZE)) << i;
|
|
}
|
|
}
|
|
EMU->RAM2CTRL |= mask;
|
|
#endif
|
|
}
|
|
|
|
#if defined(_EMU_EM23PERNORETAINCTRL_MASK)
|
|
/***************************************************************************//**
|
|
* @brief
|
|
* Set EM2 3 peripheral retention control.
|
|
*
|
|
* @param[in] periMask
|
|
* Peripheral select mask. Use | operator to select multiple peripheral, for example
|
|
* @ref emuPeripheralRetention_LEUART0 | @ref emuPeripheralRetention_VDAC0.
|
|
* @param[in] enable
|
|
* Peripheral retention enable (true) or disable (false).
|
|
*
|
|
*
|
|
* @note
|
|
* Only peripheral retention disable is currently supported. Peripherals are
|
|
* enabled by default, and can only be disabled.
|
|
******************************************************************************/
|
|
void EMU_PeripheralRetention(EMU_PeripheralRetention_TypeDef periMask, bool enable)
|
|
{
|
|
EFM_ASSERT(!enable);
|
|
EMU->EM23PERNORETAINCTRL = periMask & emuPeripheralRetention_ALL;
|
|
}
|
|
#endif
|
|
|
|
/***************************************************************************//**
|
|
* @brief
|
|
* Update EMU module with CMU oscillator selection/enable status.
|
|
*
|
|
* @deprecated
|
|
* Oscillator status is saved in @ref EMU_EnterEM2() and @ref EMU_EnterEM3().
|
|
******************************************************************************/
|
|
void EMU_UpdateOscConfig(void)
|
|
{
|
|
emState(emState_Save);
|
|
}
|
|
|
|
#if defined(_EMU_CMD_EM01VSCALE0_MASK)
|
|
/***************************************************************************//**
|
|
* @brief
|
|
* Voltage scale in EM0 and 1 by clock frequency.
|
|
*
|
|
* @param[in] clockFrequency
|
|
* Use CMSIS HF clock if 0, or override to custom clock. Providing a
|
|
* custom clock frequency is required if using a non-standard HFXO
|
|
* frequency.
|
|
* @param[in] wait
|
|
* Wait for scaling to complete.
|
|
*
|
|
* @note
|
|
* This function is primarily needed by the @ref CMU module.
|
|
******************************************************************************/
|
|
void EMU_VScaleEM01ByClock(uint32_t clockFrequency, bool wait)
|
|
{
|
|
uint32_t hfSrcClockFrequency;
|
|
uint32_t hfPresc = 1U + ((CMU->HFPRESC & _CMU_HFPRESC_PRESC_MASK)
|
|
>> _CMU_HFPRESC_PRESC_SHIFT);
|
|
|
|
/* VSCALE frequency is HFSRCCLK */
|
|
if (clockFrequency == 0) {
|
|
hfSrcClockFrequency = SystemHFClockGet() * hfPresc;
|
|
} else {
|
|
hfSrcClockFrequency = clockFrequency;
|
|
}
|
|
|
|
/* Apply EM0 and 1 voltage scaling command. */
|
|
if (vScaleEM01Config.vScaleEM01LowPowerVoltageEnable
|
|
&& (hfSrcClockFrequency < CMU_VSCALEEM01_LOWPOWER_VOLTAGE_CLOCK_MAX)) {
|
|
EMU_VScaleEM01(emuVScaleEM01_LowPower, wait);
|
|
} else {
|
|
EMU_VScaleEM01(emuVScaleEM01_HighPerformance, wait);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if defined(_EMU_CMD_EM01VSCALE0_MASK)
|
|
/***************************************************************************//**
|
|
* @brief
|
|
* Force voltage scaling in EM0 and 1 to a specific voltage level.
|
|
*
|
|
* @param[in] voltage
|
|
* Target VSCALE voltage level.
|
|
* @param[in] wait
|
|
* Wait for scaling to complate.
|
|
*
|
|
* @note
|
|
* This function is useful for upscaling before programming Flash from @ref MSC,
|
|
* and downscaling after programming is done. Flash programming is only supported
|
|
* at @ref emuVScaleEM01_HighPerformance.
|
|
*
|
|
* @note
|
|
* This function ignores @ref vScaleEM01LowPowerVoltageEnable set from @ref
|
|
* EMU_EM01Init().
|
|
******************************************************************************/
|
|
void EMU_VScaleEM01(EMU_VScaleEM01_TypeDef voltage, bool wait)
|
|
{
|
|
uint32_t hfSrcClockFrequency;
|
|
uint32_t hfPresc = 1U + ((CMU->HFPRESC & _CMU_HFPRESC_PRESC_MASK)
|
|
>> _CMU_HFPRESC_PRESC_SHIFT);
|
|
uint32_t hfFreq = SystemHFClockGet();
|
|
EMU_VScaleEM01_TypeDef current = EMU_VScaleGet();
|
|
|
|
if (current == voltage) {
|
|
/* Voltage is already at correct level. */
|
|
return;
|
|
}
|
|
|
|
hfSrcClockFrequency = hfFreq * hfPresc;
|
|
|
|
if (voltage == emuVScaleEM01_LowPower) {
|
|
EFM_ASSERT(hfSrcClockFrequency <= CMU_VSCALEEM01_LOWPOWER_VOLTAGE_CLOCK_MAX);
|
|
/* Update wait states before scaling down voltage */
|
|
CMU_UpdateWaitStates(hfFreq, emuVScaleEM01_LowPower);
|
|
}
|
|
|
|
EMU->CMD = vScaleEM01Cmd(voltage);
|
|
|
|
if (voltage == emuVScaleEM01_HighPerformance) {
|
|
/* Update wait states after scaling up voltage */
|
|
CMU_UpdateWaitStates(hfFreq, emuVScaleEM01_HighPerformance);
|
|
}
|
|
|
|
if (wait) {
|
|
EMU_VScaleWait();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if defined(_EMU_CMD_EM01VSCALE0_MASK)
|
|
/***************************************************************************//**
|
|
* @brief
|
|
* Update EMU module with Energy Mode 0 and 1 configuration
|
|
*
|
|
* @param[in] em01Init
|
|
* Energy Mode 0 and 1 configuration structure
|
|
******************************************************************************/
|
|
void EMU_EM01Init(const EMU_EM01Init_TypeDef *em01Init)
|
|
{
|
|
vScaleEM01Config.vScaleEM01LowPowerVoltageEnable =
|
|
em01Init->vScaleEM01LowPowerVoltageEnable;
|
|
EMU_VScaleEM01ByClock(0, true);
|
|
}
|
|
#endif
|
|
|
|
/***************************************************************************//**
|
|
* @brief
|
|
* Update EMU module with Energy Mode 2 and 3 configuration
|
|
*
|
|
* @param[in] em23Init
|
|
* Energy Mode 2 and 3 configuration structure
|
|
******************************************************************************/
|
|
void EMU_EM23Init(const EMU_EM23Init_TypeDef *em23Init)
|
|
{
|
|
#if defined(_EMU_CTRL_EMVREG_MASK)
|
|
EMU->CTRL = em23Init->em23VregFullEn ? (EMU->CTRL | EMU_CTRL_EMVREG)
|
|
: (EMU->CTRL & ~EMU_CTRL_EMVREG);
|
|
#elif defined(_EMU_CTRL_EM23VREG_MASK)
|
|
EMU->CTRL = em23Init->em23VregFullEn ? (EMU->CTRL | EMU_CTRL_EM23VREG)
|
|
: (EMU->CTRL & ~EMU_CTRL_EM23VREG);
|
|
#else
|
|
(void)em23Init;
|
|
#endif
|
|
|
|
#if defined(_EMU_CTRL_EM23VSCALE_MASK)
|
|
EMU->CTRL = (EMU->CTRL & ~_EMU_CTRL_EM23VSCALE_MASK)
|
|
| (em23Init->vScaleEM23Voltage << _EMU_CTRL_EM23VSCALE_SHIFT);
|
|
#endif
|
|
}
|
|
|
|
#if defined(_EMU_EM4CONF_MASK) || defined(_EMU_EM4CTRL_MASK)
|
|
/***************************************************************************//**
|
|
* @brief
|
|
* Update EMU module with Energy Mode 4 configuration
|
|
*
|
|
* @param[in] em4Init
|
|
* Energy Mode 4 configuration structure
|
|
******************************************************************************/
|
|
void EMU_EM4Init(const EMU_EM4Init_TypeDef *em4Init)
|
|
{
|
|
#if defined(_EMU_EM4CONF_MASK)
|
|
/* Init for platforms with EMU->EM4CONF register */
|
|
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;
|
|
|
|
#elif defined(_EMU_EM4CTRL_MASK)
|
|
/* Init for platforms with EMU->EM4CTRL register */
|
|
|
|
uint32_t em4ctrl = EMU->EM4CTRL;
|
|
|
|
em4ctrl &= ~(_EMU_EM4CTRL_RETAINLFXO_MASK
|
|
| _EMU_EM4CTRL_RETAINLFRCO_MASK
|
|
| _EMU_EM4CTRL_RETAINULFRCO_MASK
|
|
| _EMU_EM4CTRL_EM4STATE_MASK
|
|
| _EMU_EM4CTRL_EM4IORETMODE_MASK);
|
|
|
|
em4ctrl |= (em4Init->retainLfxo ? EMU_EM4CTRL_RETAINLFXO : 0)
|
|
| (em4Init->retainLfrco ? EMU_EM4CTRL_RETAINLFRCO : 0)
|
|
| (em4Init->retainUlfrco ? EMU_EM4CTRL_RETAINULFRCO : 0)
|
|
| (em4Init->em4State ? EMU_EM4CTRL_EM4STATE_EM4H : 0)
|
|
| (em4Init->pinRetentionMode);
|
|
|
|
EMU->EM4CTRL = em4ctrl;
|
|
#endif
|
|
|
|
#if defined(_EMU_CTRL_EM4HVSCALE_MASK)
|
|
EMU->CTRL = (EMU->CTRL & ~_EMU_CTRL_EM4HVSCALE_MASK)
|
|
| (em4Init->vScaleEM4HVoltage << _EMU_CTRL_EM4HVSCALE_SHIFT);
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
#if defined(BU_PRESENT)
|
|
/***************************************************************************//**
|
|
* @brief
|
|
* Configure Backup Power Domain settings
|
|
*
|
|
* @param[in] bupdInit
|
|
* Backup power domain initialization structure
|
|
******************************************************************************/
|
|
void EMU_BUPDInit(const EMU_BUPDInit_TypeDef *bupdInit)
|
|
{
|
|
uint32_t reg;
|
|
|
|
/* 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);
|
|
reg |= (bupdInit->inactivePower);
|
|
EMU->BUINACT = reg;
|
|
|
|
/* Set backup domain active mode configuration */
|
|
reg = EMU->BUACT & ~(_EMU_BUACT_PWRCON_MASK);
|
|
reg |= (bupdInit->activePower);
|
|
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 */
|
|
BUS_RegBitWrite(&(RMU->CTRL), _RMU_CTRL_BURSTEN_SHIFT, !bupdInit->enable);
|
|
}
|
|
|
|
/***************************************************************************//**
|
|
* @brief
|
|
* Configure Backup Power Domain BOD Threshold value
|
|
* @note
|
|
* These values are precalibrated
|
|
* @param[in] mode Active or Inactive mode
|
|
* @param[in] value
|
|
******************************************************************************/
|
|
void EMU_BUThresholdSet(EMU_BODMode_TypeDef mode, uint32_t value)
|
|
{
|
|
EFM_ASSERT(value < 8);
|
|
EFM_ASSERT(value <= (_EMU_BUACT_BUEXTHRES_MASK >> _EMU_BUACT_BUEXTHRES_SHIFT));
|
|
|
|
switch (mode) {
|
|
case emuBODMode_Active:
|
|
EMU->BUACT = (EMU->BUACT & ~_EMU_BUACT_BUEXTHRES_MASK)
|
|
| (value << _EMU_BUACT_BUEXTHRES_SHIFT);
|
|
break;
|
|
case emuBODMode_Inactive:
|
|
EMU->BUINACT = (EMU->BUINACT & ~_EMU_BUINACT_BUENTHRES_MASK)
|
|
| (value << _EMU_BUINACT_BUENTHRES_SHIFT);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/***************************************************************************//**
|
|
* @brief
|
|
* Configure Backup Power Domain BOD Threshold Range
|
|
* @note
|
|
* These values are precalibrated
|
|
* @param[in] mode Active or Inactive mode
|
|
* @param[in] value
|
|
******************************************************************************/
|
|
void EMU_BUThresRangeSet(EMU_BODMode_TypeDef mode, uint32_t value)
|
|
{
|
|
EFM_ASSERT(value < 4);
|
|
EFM_ASSERT(value <= (_EMU_BUACT_BUEXRANGE_MASK >> _EMU_BUACT_BUEXRANGE_SHIFT));
|
|
|
|
switch (mode) {
|
|
case emuBODMode_Active:
|
|
EMU->BUACT = (EMU->BUACT & ~_EMU_BUACT_BUEXRANGE_MASK)
|
|
| (value << _EMU_BUACT_BUEXRANGE_SHIFT);
|
|
break;
|
|
case emuBODMode_Inactive:
|
|
EMU->BUINACT = (EMU->BUINACT & ~_EMU_BUINACT_BUENRANGE_MASK)
|
|
| (value << _EMU_BUINACT_BUENRANGE_SHIFT);
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
|
|
#if defined(_EMU_DCDCCTRL_MASK)
|
|
/* Translate fields with different names across platform generations to common names. */
|
|
#if defined(_EMU_DCDCMISCCTRL_LPCMPBIAS_MASK)
|
|
#define _GENERIC_DCDCMISCCTRL_LPCMPBIASEM234H_MASK _EMU_DCDCMISCCTRL_LPCMPBIAS_MASK
|
|
#define _GENERIC_DCDCMISCCTRL_LPCMPBIASEM234H_SHIFT _EMU_DCDCMISCCTRL_LPCMPBIAS_SHIFT
|
|
#elif defined(_EMU_DCDCMISCCTRL_LPCMPBIASEM234H_MASK)
|
|
#define _GENERIC_DCDCMISCCTRL_LPCMPBIASEM234H_MASK _EMU_DCDCMISCCTRL_LPCMPBIASEM234H_MASK
|
|
#define _GENERIC_DCDCMISCCTRL_LPCMPBIASEM234H_SHIFT _EMU_DCDCMISCCTRL_LPCMPBIASEM234H_SHIFT
|
|
#endif
|
|
#if defined(_EMU_DCDCLPCTRL_LPCMPHYSSEL_MASK)
|
|
#define _GENERIC_DCDCLPCTRL_LPCMPHYSSELEM234H_MASK _EMU_DCDCLPCTRL_LPCMPHYSSEL_MASK
|
|
#define _GENERIC_DCDCLPCTRL_LPCMPHYSSELEM234H_SHIFT _EMU_DCDCLPCTRL_LPCMPHYSSEL_SHIFT
|
|
#elif defined(_EMU_DCDCLPCTRL_LPCMPHYSSELEM234H_MASK)
|
|
#define _GENERIC_DCDCLPCTRL_LPCMPHYSSELEM234H_MASK _EMU_DCDCLPCTRL_LPCMPHYSSELEM234H_MASK
|
|
#define _GENERIC_DCDCLPCTRL_LPCMPHYSSELEM234H_SHIFT _EMU_DCDCLPCTRL_LPCMPHYSSELEM234H_SHIFT
|
|
#endif
|
|
|
|
/* Internal DCDC trim modes. */
|
|
typedef enum {
|
|
dcdcTrimMode_EM234H_LP = 0,
|
|
#if defined(_EMU_DCDCLPEM01CFG_LPCMPBIASEM01_MASK)
|
|
dcdcTrimMode_EM01_LP,
|
|
#endif
|
|
dcdcTrimMode_LN,
|
|
} dcdcTrimMode_TypeDef;
|
|
|
|
/***************************************************************************//**
|
|
* @brief
|
|
* Load DCDC calibration constants from DI page. Const means calibration
|
|
* data that does not change depending on other configuration parameters.
|
|
*
|
|
* @return
|
|
* False if calibration registers are locked
|
|
******************************************************************************/
|
|
static bool dcdcConstCalibrationLoad(void)
|
|
{
|
|
#if defined(_SILICON_LABS_GECKO_INTERNAL_SDID_80)
|
|
uint32_t val;
|
|
volatile uint32_t *reg;
|
|
|
|
/* DI calib data in flash */
|
|
volatile uint32_t* const diCal_EMU_DCDCLNFREQCTRL = (volatile uint32_t *)(0x0FE08038);
|
|
volatile uint32_t* const diCal_EMU_DCDCLNVCTRL = (volatile uint32_t *)(0x0FE08040);
|
|
volatile uint32_t* const diCal_EMU_DCDCLPCTRL = (volatile uint32_t *)(0x0FE08048);
|
|
volatile uint32_t* const diCal_EMU_DCDCLPVCTRL = (volatile uint32_t *)(0x0FE08050);
|
|
volatile uint32_t* const diCal_EMU_DCDCTRIM0 = (volatile uint32_t *)(0x0FE08058);
|
|
volatile uint32_t* const diCal_EMU_DCDCTRIM1 = (volatile uint32_t *)(0x0FE08060);
|
|
|
|
if (DEVINFO->DCDCLPVCTRL0 != UINT_MAX) {
|
|
val = *(diCal_EMU_DCDCLNFREQCTRL + 1);
|
|
reg = (volatile uint32_t *)*diCal_EMU_DCDCLNFREQCTRL;
|
|
*reg = val;
|
|
|
|
val = *(diCal_EMU_DCDCLNVCTRL + 1);
|
|
reg = (volatile uint32_t *)*diCal_EMU_DCDCLNVCTRL;
|
|
*reg = val;
|
|
|
|
val = *(diCal_EMU_DCDCLPCTRL + 1);
|
|
reg = (volatile uint32_t *)*diCal_EMU_DCDCLPCTRL;
|
|
*reg = val;
|
|
|
|
val = *(diCal_EMU_DCDCLPVCTRL + 1);
|
|
reg = (volatile uint32_t *)*diCal_EMU_DCDCLPVCTRL;
|
|
*reg = val;
|
|
|
|
val = *(diCal_EMU_DCDCTRIM0 + 1);
|
|
reg = (volatile uint32_t *)*diCal_EMU_DCDCTRIM0;
|
|
*reg = val;
|
|
|
|
val = *(diCal_EMU_DCDCTRIM1 + 1);
|
|
reg = (volatile uint32_t *)*diCal_EMU_DCDCTRIM1;
|
|
*reg = val;
|
|
|
|
return true;
|
|
}
|
|
EFM_ASSERT(false);
|
|
/* Return when assertions are disabled */
|
|
return false;
|
|
|
|
#else
|
|
return true;
|
|
#endif
|
|
}
|
|
|
|
/***************************************************************************//**
|
|
* @brief
|
|
* Set recommended and validated current optimization and timing settings
|
|
*
|
|
******************************************************************************/
|
|
static void dcdcValidatedConfigSet(void)
|
|
{
|
|
/* Disable LP mode hysterysis in the state machine control */
|
|
#define EMU_DCDCMISCCTRL_LPCMPHYSDIS (0x1UL << 1)
|
|
/* Comparator threshold on the high side */
|
|
#define EMU_DCDCMISCCTRL_LPCMPHYSHI (0x1UL << 2)
|
|
#define EMU_DCDCSMCTRL (*(volatile uint32_t *)(EMU_BASE + 0x44))
|
|
|
|
uint32_t lnForceCcm;
|
|
|
|
#if defined(_SILICON_LABS_GECKO_INTERNAL_SDID_80)
|
|
uint32_t dcdcTiming;
|
|
SYSTEM_ChipRevision_TypeDef rev;
|
|
#endif
|
|
|
|
/* Enable duty cycling of the bias */
|
|
EMU->DCDCLPCTRL |= EMU_DCDCLPCTRL_LPVREFDUTYEN;
|
|
|
|
/* Set low-noise RCO for LNFORCECCM configuration
|
|
* LNFORCECCM is default 1 for EFR32
|
|
* LNFORCECCM is default 0 for EFM32
|
|
*/
|
|
lnForceCcm = BUS_RegBitRead(&EMU->DCDCMISCCTRL, _EMU_DCDCMISCCTRL_LNFORCECCM_SHIFT);
|
|
if (lnForceCcm) {
|
|
/* 7MHz is recommended for LNFORCECCM = 1 */
|
|
EMU_DCDCLnRcoBandSet(emuDcdcLnRcoBand_7MHz);
|
|
} else {
|
|
/* 3MHz is recommended for LNFORCECCM = 0 */
|
|
EMU_DCDCLnRcoBandSet(emuDcdcLnRcoBand_3MHz);
|
|
}
|
|
|
|
#if defined(_SILICON_LABS_GECKO_INTERNAL_SDID_80)
|
|
EMU->DCDCTIMING &= ~_EMU_DCDCTIMING_DUTYSCALE_MASK;
|
|
EMU->DCDCMISCCTRL |= EMU_DCDCMISCCTRL_LPCMPHYSDIS
|
|
| EMU_DCDCMISCCTRL_LPCMPHYSHI;
|
|
|
|
SYSTEM_ChipRevisionGet(&rev);
|
|
if ((rev.major == 1)
|
|
&& (rev.minor < 3)
|
|
&& (errataFixDcdcHsState == errataFixDcdcHsInit)) {
|
|
/* LPCMPWAITDIS = 1 */
|
|
EMU_DCDCSMCTRL |= 1;
|
|
|
|
dcdcTiming = EMU->DCDCTIMING;
|
|
dcdcTiming &= ~(_EMU_DCDCTIMING_LPINITWAIT_MASK
|
|
| _EMU_DCDCTIMING_LNWAIT_MASK
|
|
| _EMU_DCDCTIMING_BYPWAIT_MASK);
|
|
|
|
dcdcTiming |= ((180 << _EMU_DCDCTIMING_LPINITWAIT_SHIFT)
|
|
| (12 << _EMU_DCDCTIMING_LNWAIT_SHIFT)
|
|
| (180 << _EMU_DCDCTIMING_BYPWAIT_SHIFT));
|
|
EMU->DCDCTIMING = dcdcTiming;
|
|
|
|
errataFixDcdcHsState = errataFixDcdcHsTrimSet;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/***************************************************************************//**
|
|
* @brief
|
|
* Compute current limiters:
|
|
* LNCLIMILIMSEL: LN current limiter threshold
|
|
* LPCLIMILIMSEL: LP current limiter threshold
|
|
* DCDCZDETCTRL: zero detector limiter threshold
|
|
******************************************************************************/
|
|
static void currentLimitersUpdate(void)
|
|
{
|
|
uint32_t lncLimSel;
|
|
uint32_t zdetLimSel;
|
|
uint32_t pFetCnt;
|
|
uint16_t maxReverseCurrent_mA;
|
|
|
|
/* 80mA as recommended peak in Application Note AN0948.
|
|
The peak current is the average current plus 50% of the current ripple.
|
|
Hence, a 14mA average current is recommended in LP mode. Since LP PFETCNT is also
|
|
a constant, we get lpcLimImSel = 1. The following calculation is provided
|
|
for documentation only. */
|
|
const uint32_t lpcLim = (((14 + 40) + ((14 + 40) / 2))
|
|
/ (5 * (DCDC_LP_PFET_CNT + 1)))
|
|
- 1;
|
|
const uint32_t lpcLimSel = lpcLim << _EMU_DCDCMISCCTRL_LPCLIMILIMSEL_SHIFT;
|
|
|
|
/* Get enabled PFETs */
|
|
pFetCnt = (EMU->DCDCMISCCTRL & _EMU_DCDCMISCCTRL_PFETCNT_MASK)
|
|
>> _EMU_DCDCMISCCTRL_PFETCNT_SHIFT;
|
|
|
|
/* Compute LN current limiter threshold from nominal user input current and
|
|
LN PFETCNT as described in the register description for
|
|
EMU_DCDCMISCCTRL_LNCLIMILIMSEL. */
|
|
lncLimSel = (((dcdcMaxCurrent_mA + 40) + ((dcdcMaxCurrent_mA + 40) / 2))
|
|
/ (5 * (pFetCnt + 1)))
|
|
- 1;
|
|
|
|
/* Saturate the register field value */
|
|
lncLimSel = SL_MIN(lncLimSel,
|
|
_EMU_DCDCMISCCTRL_LNCLIMILIMSEL_MASK
|
|
>> _EMU_DCDCMISCCTRL_LNCLIMILIMSEL_SHIFT);
|
|
|
|
lncLimSel <<= _EMU_DCDCMISCCTRL_LNCLIMILIMSEL_SHIFT;
|
|
|
|
/* Check for overflow */
|
|
EFM_ASSERT((lncLimSel & ~_EMU_DCDCMISCCTRL_LNCLIMILIMSEL_MASK) == 0x0);
|
|
EFM_ASSERT((lpcLimSel & ~_EMU_DCDCMISCCTRL_LPCLIMILIMSEL_MASK) == 0x0);
|
|
|
|
EMU->DCDCMISCCTRL = (EMU->DCDCMISCCTRL & ~(_EMU_DCDCMISCCTRL_LNCLIMILIMSEL_MASK
|
|
| _EMU_DCDCMISCCTRL_LPCLIMILIMSEL_MASK))
|
|
| (lncLimSel | lpcLimSel);
|
|
|
|
/* Compute reverse current limit threshold for the zero detector from user input
|
|
maximum reverse current and LN PFETCNT as described in the register description
|
|
for EMU_DCDCZDETCTRL_ZDETILIMSEL. */
|
|
if (dcdcReverseCurrentControl >= 0) {
|
|
/* If dcdcReverseCurrentControl < 0, then EMU_DCDCZDETCTRL_ZDETILIMSEL is "don't care" */
|
|
maxReverseCurrent_mA = (uint16_t)dcdcReverseCurrentControl;
|
|
|
|
zdetLimSel = ( ((maxReverseCurrent_mA + 40) + ((maxReverseCurrent_mA + 40) / 2))
|
|
/ ((2 * (pFetCnt + 1)) + ((pFetCnt + 1) / 2)) );
|
|
/* Saturate the register field value */
|
|
zdetLimSel = SL_MIN(zdetLimSel,
|
|
_EMU_DCDCZDETCTRL_ZDETILIMSEL_MASK
|
|
>> _EMU_DCDCZDETCTRL_ZDETILIMSEL_SHIFT);
|
|
|
|
zdetLimSel <<= _EMU_DCDCZDETCTRL_ZDETILIMSEL_SHIFT;
|
|
|
|
/* Check for overflow */
|
|
EFM_ASSERT((zdetLimSel & ~_EMU_DCDCZDETCTRL_ZDETILIMSEL_MASK) == 0x0);
|
|
|
|
EMU->DCDCZDETCTRL = (EMU->DCDCZDETCTRL & ~_EMU_DCDCZDETCTRL_ZDETILIMSEL_MASK)
|
|
| zdetLimSel;
|
|
}
|
|
}
|
|
|
|
/***************************************************************************//**
|
|
* @brief
|
|
* Set static variables that hold the user set maximum peak current
|
|
* and reverse current. Update limiters.
|
|
*
|
|
* @param[in] maxCurrent_mA
|
|
* Set the maximum peak current that the DCDC can draw from the power source.
|
|
* @param[in] reverseCurrentControl
|
|
* Reverse current control as defined by
|
|
* @ref EMU_DcdcLnReverseCurrentControl_TypeDef. Positive values have unit mA.
|
|
******************************************************************************/
|
|
static void userCurrentLimitsSet(uint32_t maxCurrent_mA,
|
|
EMU_DcdcLnReverseCurrentControl_TypeDef reverseCurrentControl)
|
|
{
|
|
dcdcMaxCurrent_mA = maxCurrent_mA;
|
|
dcdcReverseCurrentControl = reverseCurrentControl;
|
|
}
|
|
|
|
/***************************************************************************//**
|
|
* @brief
|
|
* Set DCDC low noise compensator control register
|
|
*
|
|
* @param[in] comp
|
|
* Low-noise mode compensator trim setpoint
|
|
******************************************************************************/
|
|
static void compCtrlSet(EMU_DcdcLnCompCtrl_TypeDef comp)
|
|
{
|
|
switch (comp) {
|
|
case emuDcdcLnCompCtrl_1u0F:
|
|
EMU->DCDCLNCOMPCTRL = 0x57204077UL;
|
|
break;
|
|
|
|
case emuDcdcLnCompCtrl_4u7F:
|
|
EMU->DCDCLNCOMPCTRL = 0xB7102137UL;
|
|
break;
|
|
|
|
default:
|
|
EFM_ASSERT(false);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/***************************************************************************//**
|
|
* @brief
|
|
* Load EMU_DCDCLPCTRL_LPCMPHYSSEL depending on LP bias, LP feedback
|
|
* attenuation and DEVINFOREV.
|
|
*
|
|
* @param[in] lpAttenuation
|
|
* LP feedback attenuation.
|
|
* @param[in] lpCmpBias
|
|
* lpCmpBias selection.
|
|
* @param[in] trimMode
|
|
* DCDC trim mode.
|
|
******************************************************************************/
|
|
static bool lpCmpHystCalibrationLoad(bool lpAttenuation,
|
|
uint8_t lpCmpBias,
|
|
dcdcTrimMode_TypeDef trimMode)
|
|
{
|
|
uint32_t lpcmpHystSel;
|
|
|
|
/* Get calib data revision */
|
|
#if defined(_SILICON_LABS_GECKO_INTERNAL_SDID_80)
|
|
uint8_t devinfoRev = SYSTEM_GetDevinfoRev();
|
|
|
|
/* Load LPATT indexed calibration data */
|
|
if (devinfoRev < 4)
|
|
#else
|
|
/* Format change not present of newer families. */
|
|
if (false)
|
|
#endif
|
|
{
|
|
lpcmpHystSel = DEVINFO->DCDCLPCMPHYSSEL0;
|
|
|
|
if (lpAttenuation) {
|
|
lpcmpHystSel = (lpcmpHystSel & _DEVINFO_DCDCLPCMPHYSSEL0_LPCMPHYSSELLPATT1_MASK)
|
|
>> _DEVINFO_DCDCLPCMPHYSSEL0_LPCMPHYSSELLPATT1_SHIFT;
|
|
} else {
|
|
lpcmpHystSel = (lpcmpHystSel & _DEVINFO_DCDCLPCMPHYSSEL0_LPCMPHYSSELLPATT0_MASK)
|
|
>> _DEVINFO_DCDCLPCMPHYSSEL0_LPCMPHYSSELLPATT0_SHIFT;
|
|
}
|
|
} else {
|
|
/* devinfoRev >= 4: load LPCMPBIAS indexed calibration data */
|
|
lpcmpHystSel = DEVINFO->DCDCLPCMPHYSSEL1;
|
|
switch (lpCmpBias) {
|
|
case 0:
|
|
lpcmpHystSel = (lpcmpHystSel & _DEVINFO_DCDCLPCMPHYSSEL1_LPCMPHYSSELLPCMPBIAS0_MASK)
|
|
>> _DEVINFO_DCDCLPCMPHYSSEL1_LPCMPHYSSELLPCMPBIAS0_SHIFT;
|
|
break;
|
|
|
|
case 1:
|
|
lpcmpHystSel = (lpcmpHystSel & _DEVINFO_DCDCLPCMPHYSSEL1_LPCMPHYSSELLPCMPBIAS1_MASK)
|
|
>> _DEVINFO_DCDCLPCMPHYSSEL1_LPCMPHYSSELLPCMPBIAS1_SHIFT;
|
|
break;
|
|
|
|
case 2:
|
|
lpcmpHystSel = (lpcmpHystSel & _DEVINFO_DCDCLPCMPHYSSEL1_LPCMPHYSSELLPCMPBIAS2_MASK)
|
|
>> _DEVINFO_DCDCLPCMPHYSSEL1_LPCMPHYSSELLPCMPBIAS2_SHIFT;
|
|
break;
|
|
|
|
case 3:
|
|
lpcmpHystSel = (lpcmpHystSel & _DEVINFO_DCDCLPCMPHYSSEL1_LPCMPHYSSELLPCMPBIAS3_MASK)
|
|
>> _DEVINFO_DCDCLPCMPHYSSEL1_LPCMPHYSSELLPCMPBIAS3_SHIFT;
|
|
break;
|
|
|
|
default:
|
|
EFM_ASSERT(false);
|
|
/* Return when assertions are disabled */
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/* Set trims */
|
|
if (trimMode == dcdcTrimMode_EM234H_LP) {
|
|
/* Make sure the sel value is within the field range. */
|
|
lpcmpHystSel <<= _GENERIC_DCDCLPCTRL_LPCMPHYSSELEM234H_SHIFT;
|
|
if (lpcmpHystSel & ~_GENERIC_DCDCLPCTRL_LPCMPHYSSELEM234H_MASK) {
|
|
EFM_ASSERT(false);
|
|
/* Return when assertions are disabled */
|
|
return false;
|
|
}
|
|
EMU->DCDCLPCTRL = (EMU->DCDCLPCTRL & ~_GENERIC_DCDCLPCTRL_LPCMPHYSSELEM234H_MASK) | lpcmpHystSel;
|
|
}
|
|
|
|
#if defined(_EMU_DCDCLPEM01CFG_LPCMPHYSSELEM01_MASK)
|
|
if (trimMode == dcdcTrimMode_EM01_LP) {
|
|
/* Make sure the sel value is within the field range. */
|
|
lpcmpHystSel <<= _EMU_DCDCLPEM01CFG_LPCMPHYSSELEM01_SHIFT;
|
|
if (lpcmpHystSel & ~_EMU_DCDCLPEM01CFG_LPCMPHYSSELEM01_MASK) {
|
|
EFM_ASSERT(false);
|
|
/* Return when assertions are disabled */
|
|
return false;
|
|
}
|
|
EMU->DCDCLPEM01CFG = (EMU->DCDCLPEM01CFG & ~_EMU_DCDCLPEM01CFG_LPCMPHYSSELEM01_MASK) | lpcmpHystSel;
|
|
}
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
|
|
/***************************************************************************//**
|
|
* @brief
|
|
* Load LPVREF low and high from DEVINFO.
|
|
*
|
|
* @param[out] vrefL
|
|
* LPVREF low from DEVINFO.
|
|
* @param[out] vrefH
|
|
* LPVREF high from DEVINFO.
|
|
* @param[in] lpAttenuation
|
|
* LP feedback attenuation.
|
|
* @param[in] lpcmpBias
|
|
* lpcmpBias to lookup in DEVINFO.
|
|
******************************************************************************/
|
|
static void lpGetDevinfoVrefLowHigh(uint32_t *vrefL,
|
|
uint32_t *vrefH,
|
|
bool lpAttenuation,
|
|
uint8_t lpcmpBias)
|
|
{
|
|
uint32_t vrefLow = 0;
|
|
uint32_t vrefHigh = 0;
|
|
|
|
/* Find VREF high and low in DEVINFO indexed by LPCMPBIAS (lpcmpBias)
|
|
and LPATT (lpAttenuation) */
|
|
uint32_t switchVal = (lpcmpBias << 8) | (lpAttenuation ? 1 : 0);
|
|
switch (switchVal) {
|
|
case ((0 << 8) | 1):
|
|
vrefLow = DEVINFO->DCDCLPVCTRL2;
|
|
vrefHigh = (vrefLow & _DEVINFO_DCDCLPVCTRL2_3V0LPATT1LPCMPBIAS0_MASK)
|
|
>> _DEVINFO_DCDCLPVCTRL2_3V0LPATT1LPCMPBIAS0_SHIFT;
|
|
vrefLow = (vrefLow & _DEVINFO_DCDCLPVCTRL2_1V8LPATT1LPCMPBIAS0_MASK)
|
|
>> _DEVINFO_DCDCLPVCTRL2_1V8LPATT1LPCMPBIAS0_SHIFT;
|
|
break;
|
|
|
|
case ((1 << 8) | 1):
|
|
vrefLow = DEVINFO->DCDCLPVCTRL2;
|
|
vrefHigh = (vrefLow & _DEVINFO_DCDCLPVCTRL2_3V0LPATT1LPCMPBIAS1_MASK)
|
|
>> _DEVINFO_DCDCLPVCTRL2_3V0LPATT1LPCMPBIAS1_SHIFT;
|
|
vrefLow = (vrefLow & _DEVINFO_DCDCLPVCTRL2_1V8LPATT1LPCMPBIAS1_MASK)
|
|
>> _DEVINFO_DCDCLPVCTRL2_1V8LPATT1LPCMPBIAS1_SHIFT;
|
|
break;
|
|
|
|
case ((2 << 8) | 1):
|
|
vrefLow = DEVINFO->DCDCLPVCTRL3;
|
|
vrefHigh = (vrefLow & _DEVINFO_DCDCLPVCTRL3_3V0LPATT1LPCMPBIAS2_MASK)
|
|
>> _DEVINFO_DCDCLPVCTRL3_3V0LPATT1LPCMPBIAS2_SHIFT;
|
|
vrefLow = (vrefLow & _DEVINFO_DCDCLPVCTRL3_1V8LPATT1LPCMPBIAS2_MASK)
|
|
>> _DEVINFO_DCDCLPVCTRL3_1V8LPATT1LPCMPBIAS2_SHIFT;
|
|
break;
|
|
|
|
case ((3 << 8) | 1):
|
|
vrefLow = DEVINFO->DCDCLPVCTRL3;
|
|
vrefHigh = (vrefLow & _DEVINFO_DCDCLPVCTRL3_3V0LPATT1LPCMPBIAS3_MASK)
|
|
>> _DEVINFO_DCDCLPVCTRL3_3V0LPATT1LPCMPBIAS3_SHIFT;
|
|
vrefLow = (vrefLow & _DEVINFO_DCDCLPVCTRL3_1V8LPATT1LPCMPBIAS3_MASK)
|
|
>> _DEVINFO_DCDCLPVCTRL3_1V8LPATT1LPCMPBIAS3_SHIFT;
|
|
break;
|
|
|
|
case ((0 << 8) | 0):
|
|
vrefLow = DEVINFO->DCDCLPVCTRL0;
|
|
vrefHigh = (vrefLow & _DEVINFO_DCDCLPVCTRL0_1V8LPATT0LPCMPBIAS0_MASK)
|
|
>> _DEVINFO_DCDCLPVCTRL0_1V8LPATT0LPCMPBIAS0_SHIFT;
|
|
vrefLow = (vrefLow & _DEVINFO_DCDCLPVCTRL0_1V2LPATT0LPCMPBIAS0_MASK)
|
|
>> _DEVINFO_DCDCLPVCTRL0_1V2LPATT0LPCMPBIAS0_SHIFT;
|
|
break;
|
|
|
|
case ((1 << 8) | 0):
|
|
vrefLow = DEVINFO->DCDCLPVCTRL0;
|
|
vrefHigh = (vrefLow & _DEVINFO_DCDCLPVCTRL0_1V8LPATT0LPCMPBIAS1_MASK)
|
|
>> _DEVINFO_DCDCLPVCTRL0_1V8LPATT0LPCMPBIAS1_SHIFT;
|
|
vrefLow = (vrefLow & _DEVINFO_DCDCLPVCTRL0_1V2LPATT0LPCMPBIAS1_MASK)
|
|
>> _DEVINFO_DCDCLPVCTRL0_1V2LPATT0LPCMPBIAS1_SHIFT;
|
|
break;
|
|
|
|
case ((2 << 8) | 0):
|
|
vrefLow = DEVINFO->DCDCLPVCTRL1;
|
|
vrefHigh = (vrefLow & _DEVINFO_DCDCLPVCTRL1_1V8LPATT0LPCMPBIAS2_MASK)
|
|
>> _DEVINFO_DCDCLPVCTRL1_1V8LPATT0LPCMPBIAS2_SHIFT;
|
|
vrefLow = (vrefLow & _DEVINFO_DCDCLPVCTRL1_1V2LPATT0LPCMPBIAS2_MASK)
|
|
>> _DEVINFO_DCDCLPVCTRL1_1V2LPATT0LPCMPBIAS2_SHIFT;
|
|
break;
|
|
|
|
case ((3 << 8) | 0):
|
|
vrefLow = DEVINFO->DCDCLPVCTRL1;
|
|
vrefHigh = (vrefLow & _DEVINFO_DCDCLPVCTRL1_1V8LPATT0LPCMPBIAS3_MASK)
|
|
>> _DEVINFO_DCDCLPVCTRL1_1V8LPATT0LPCMPBIAS3_SHIFT;
|
|
vrefLow = (vrefLow & _DEVINFO_DCDCLPVCTRL1_1V2LPATT0LPCMPBIAS3_MASK)
|
|
>> _DEVINFO_DCDCLPVCTRL1_1V2LPATT0LPCMPBIAS3_SHIFT;
|
|
break;
|
|
|
|
default:
|
|
EFM_ASSERT(false);
|
|
break;
|
|
}
|
|
*vrefL = vrefLow;
|
|
*vrefH = vrefHigh;
|
|
}
|
|
|
|
/** @endcond */
|
|
|
|
/***************************************************************************//**
|
|
* @brief
|
|
* Set DCDC regulator operating mode
|
|
*
|
|
* @param[in] dcdcMode
|
|
* DCDC mode
|
|
******************************************************************************/
|
|
void EMU_DCDCModeSet(EMU_DcdcMode_TypeDef dcdcMode)
|
|
{
|
|
uint32_t currentDcdcMode;
|
|
|
|
/* Wait for any previous write sync to complete and read DCDC mode. */
|
|
while (EMU->DCDCSYNC & EMU_DCDCSYNC_DCDCCTRLBUSY) ;
|
|
currentDcdcMode = (EMU->DCDCCTRL & _EMU_DCDCCTRL_DCDCMODE_MASK);
|
|
|
|
/* Enable bypass current limiter when not in bypass mode to prevent
|
|
excessive current between VREGVDD and DVDD supplies when reentering bypass mode. */
|
|
if (currentDcdcMode != EMU_DCDCCTRL_DCDCMODE_BYPASS) {
|
|
BUS_RegBitWrite(&EMU->DCDCCLIMCTRL, _EMU_DCDCCLIMCTRL_BYPLIMEN_SHIFT, 1);
|
|
}
|
|
|
|
if ((EMU_DcdcMode_TypeDef)currentDcdcMode == dcdcMode) {
|
|
/* Mode already set. If already in bypass, make sure bypass current limiter
|
|
is disabled. */
|
|
if (dcdcMode == emuDcdcMode_Bypass) {
|
|
BUS_RegBitWrite(&EMU->DCDCCLIMCTRL, _EMU_DCDCCLIMCTRL_BYPLIMEN_SHIFT, 0);
|
|
}
|
|
return;
|
|
}
|
|
|
|
#if defined(_SILICON_LABS_GECKO_INTERNAL_SDID_80)
|
|
|
|
/* Fix for errata DCDC_E203 */
|
|
if ((currentDcdcMode == EMU_DCDCCTRL_DCDCMODE_BYPASS)
|
|
&& (dcdcMode == emuDcdcMode_LowNoise)) {
|
|
errataFixDcdcHsState = errataFixDcdcHsBypassLn;
|
|
}
|
|
|
|
#else
|
|
|
|
/* Fix for errata DCDC_E204 */
|
|
if (((currentDcdcMode == EMU_DCDCCTRL_DCDCMODE_OFF) || (currentDcdcMode == EMU_DCDCCTRL_DCDCMODE_BYPASS))
|
|
&& ((dcdcMode == emuDcdcMode_LowPower) || (dcdcMode == emuDcdcMode_LowNoise))) {
|
|
/* Always start in LOWNOISE mode and then switch to LOWPOWER mode once LOWNOISE startup is complete. */
|
|
EMU_IntClear(EMU_IFC_DCDCLNRUNNING);
|
|
while (EMU->DCDCSYNC & EMU_DCDCSYNC_DCDCCTRLBUSY) ;
|
|
EMU->DCDCCTRL = (EMU->DCDCCTRL & ~_EMU_DCDCCTRL_DCDCMODE_MASK) | EMU_DCDCCTRL_DCDCMODE_LOWNOISE;
|
|
while (!(EMU_IntGet() & EMU_IF_DCDCLNRUNNING)) ;
|
|
}
|
|
#endif
|
|
|
|
/* Set user requested mode. */
|
|
while (EMU->DCDCSYNC & EMU_DCDCSYNC_DCDCCTRLBUSY) ;
|
|
EMU->DCDCCTRL = (EMU->DCDCCTRL & ~_EMU_DCDCCTRL_DCDCMODE_MASK) | dcdcMode;
|
|
|
|
/* Disable bypass current limiter after bypass mode is entered.
|
|
Enable the limiter if any other mode is entered. */
|
|
while (EMU->DCDCSYNC & EMU_DCDCSYNC_DCDCCTRLBUSY) ;
|
|
BUS_RegBitWrite(&EMU->DCDCCLIMCTRL, _EMU_DCDCCLIMCTRL_BYPLIMEN_SHIFT, dcdcMode == emuDcdcMode_Bypass ? 0 : 1);
|
|
}
|
|
|
|
/***************************************************************************//**
|
|
* @brief
|
|
* Set DCDC LN regulator conduction mode
|
|
*
|
|
* @param[in] conductionMode
|
|
* DCDC LN conduction mode.
|
|
* @param[in] rcoDefaultSet
|
|
* The default DCDC RCO band for the conductionMode will be used if true.
|
|
* Otherwise the current RCO configuration is used.
|
|
******************************************************************************/
|
|
void EMU_DCDCConductionModeSet(EMU_DcdcConductionMode_TypeDef conductionMode, bool rcoDefaultSet)
|
|
{
|
|
EMU_DcdcMode_TypeDef currentDcdcMode
|
|
= (EMU_DcdcMode_TypeDef)(EMU->DCDCCTRL & _EMU_DCDCCTRL_DCDCMODE_MASK);
|
|
EMU_DcdcLnRcoBand_TypeDef rcoBand
|
|
= (EMU_DcdcLnRcoBand_TypeDef)((EMU->DCDCLNFREQCTRL & _EMU_DCDCLNFREQCTRL_RCOBAND_MASK)
|
|
>> _EMU_DCDCLNFREQCTRL_RCOBAND_SHIFT);
|
|
|
|
/* Set bypass mode and wait for bypass mode to settle before
|
|
EMU_DCDCMISCCTRL_LNFORCECCM is set. Restore current DCDC mode. */
|
|
EMU_IntClear(EMU_IFC_DCDCINBYPASS);
|
|
EMU_DCDCModeSet(emuDcdcMode_Bypass);
|
|
while (EMU->DCDCSYNC & EMU_DCDCSYNC_DCDCCTRLBUSY) ;
|
|
while (!(EMU_IntGet() & EMU_IF_DCDCINBYPASS)) ;
|
|
if (conductionMode == emuDcdcConductionMode_DiscontinuousLN) {
|
|
EMU->DCDCMISCCTRL &= ~EMU_DCDCMISCCTRL_LNFORCECCM;
|
|
if (rcoDefaultSet) {
|
|
EMU_DCDCLnRcoBandSet(emuDcdcLnRcoBand_3MHz);
|
|
} else {
|
|
/* emuDcdcConductionMode_DiscontinuousLN supports up to 4MHz LN RCO. */
|
|
EFM_ASSERT(rcoBand <= emuDcdcLnRcoBand_4MHz);
|
|
}
|
|
} else {
|
|
EMU->DCDCMISCCTRL |= EMU_DCDCMISCCTRL_LNFORCECCM;
|
|
if (rcoDefaultSet) {
|
|
EMU_DCDCLnRcoBandSet(emuDcdcLnRcoBand_7MHz);
|
|
}
|
|
}
|
|
EMU_DCDCModeSet(currentDcdcMode);
|
|
/* Update slice configuration as it depends on conduction mode and RCO band. */
|
|
EMU_DCDCOptimizeSlice(dcdcEm01LoadCurrent_mA);
|
|
}
|
|
|
|
/***************************************************************************//**
|
|
* @brief
|
|
* Configure DCDC regulator
|
|
*
|
|
* @note
|
|
* If the power circuit is configured for NODCDC as described in Section
|
|
* 11.3.4.3 of the Reference Manual, do not call this function. Instead call
|
|
* EMU_DCDCPowerOff().
|
|
*
|
|
* @param[in] dcdcInit
|
|
* DCDC initialization structure
|
|
*
|
|
* @return
|
|
* True if initialization parameters are valid
|
|
******************************************************************************/
|
|
bool EMU_DCDCInit(const EMU_DCDCInit_TypeDef *dcdcInit)
|
|
{
|
|
uint32_t lpCmpBiasSelEM234H;
|
|
|
|
#if defined(_EMU_PWRCFG_MASK)
|
|
/* Set external power configuration. This enables writing to the other
|
|
DCDC registers. */
|
|
EMU->PWRCFG = EMU_PWRCFG_PWRCFG_DCDCTODVDD;
|
|
|
|
/* EMU->PWRCFG is write-once and POR reset only. Check that
|
|
we could set the desired power configuration. */
|
|
if ((EMU->PWRCFG & _EMU_PWRCFG_PWRCFG_MASK) != EMU_PWRCFG_PWRCFG_DCDCTODVDD) {
|
|
/* If this assert triggers unexpectedly, please power cycle the
|
|
kit to reset the power configuration. */
|
|
EFM_ASSERT(false);
|
|
/* Return when assertions are disabled */
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
/* Load DCDC calibration data from the DI page */
|
|
dcdcConstCalibrationLoad();
|
|
|
|
/* Check current parameters */
|
|
EFM_ASSERT(dcdcInit->maxCurrent_mA <= 200);
|
|
EFM_ASSERT(dcdcInit->em01LoadCurrent_mA <= dcdcInit->maxCurrent_mA);
|
|
EFM_ASSERT(dcdcInit->reverseCurrentControl <= 200);
|
|
|
|
if (dcdcInit->dcdcMode == emuDcdcMode_LowNoise) {
|
|
/* DCDC low-noise supports max 200mA */
|
|
EFM_ASSERT(dcdcInit->em01LoadCurrent_mA <= 200);
|
|
}
|
|
#if (_SILICON_LABS_GECKO_INTERNAL_SDID != 80)
|
|
else if (dcdcInit->dcdcMode == emuDcdcMode_LowPower) {
|
|
/* Up to 10mA is supported for EM01-LP mode. */
|
|
EFM_ASSERT(dcdcInit->em01LoadCurrent_mA <= 10);
|
|
}
|
|
#endif
|
|
|
|
/* EM2/3/4 current above 10mA is not supported */
|
|
EFM_ASSERT(dcdcInit->em234LoadCurrent_uA <= 10000);
|
|
|
|
if (dcdcInit->em234LoadCurrent_uA < 75) {
|
|
lpCmpBiasSelEM234H = 0;
|
|
} else if (dcdcInit->em234LoadCurrent_uA < 500) {
|
|
lpCmpBiasSelEM234H = 1 << _GENERIC_DCDCMISCCTRL_LPCMPBIASEM234H_SHIFT;
|
|
} else if (dcdcInit->em234LoadCurrent_uA < 2500) {
|
|
lpCmpBiasSelEM234H = 2 << _GENERIC_DCDCMISCCTRL_LPCMPBIASEM234H_SHIFT;
|
|
} else {
|
|
lpCmpBiasSelEM234H = 3 << _GENERIC_DCDCMISCCTRL_LPCMPBIASEM234H_SHIFT;
|
|
}
|
|
|
|
/* ==== THESE NEXT STEPS ARE STRONGLY ORDER DEPENDENT ==== */
|
|
|
|
/* Set DCDC low-power mode comparator bias selection */
|
|
|
|
/* 1. Set DCDC low-power mode comparator bias selection and forced CCM
|
|
=> Updates DCDCMISCCTRL_LNFORCECCM */
|
|
EMU->DCDCMISCCTRL = (EMU->DCDCMISCCTRL & ~(_GENERIC_DCDCMISCCTRL_LPCMPBIASEM234H_MASK
|
|
| _EMU_DCDCMISCCTRL_LNFORCECCM_MASK))
|
|
| ((uint32_t)lpCmpBiasSelEM234H
|
|
| (dcdcInit->reverseCurrentControl >= 0
|
|
? EMU_DCDCMISCCTRL_LNFORCECCM : 0));
|
|
#if defined(_EMU_DCDCLPEM01CFG_LPCMPBIASEM01_MASK)
|
|
/* Only 10mA EM01-LP current is supported */
|
|
EMU->DCDCLPEM01CFG = (EMU->DCDCLPEM01CFG & ~_EMU_DCDCLPEM01CFG_LPCMPBIASEM01_MASK)
|
|
| EMU_DCDCLPEM01CFG_LPCMPBIASEM01_BIAS3;
|
|
#endif
|
|
|
|
/* 2. Set recommended and validated current optimization settings
|
|
<= Depends on LNFORCECCM
|
|
=> Updates DCDCLNFREQCTRL_RCOBAND */
|
|
dcdcValidatedConfigSet();
|
|
|
|
/* 3. Updated static currents and limits user data.
|
|
Limiters are updated in EMU_DCDCOptimizeSlice() */
|
|
userCurrentLimitsSet(dcdcInit->maxCurrent_mA,
|
|
dcdcInit->reverseCurrentControl);
|
|
dcdcEm01LoadCurrent_mA = dcdcInit->em01LoadCurrent_mA;
|
|
|
|
/* 4. Optimize LN slice based on given user input load current
|
|
<= Depends on DCDCMISCCTRL_LNFORCECCM and DCDCLNFREQCTRL_RCOBAND
|
|
<= Depends on dcdcInit->maxCurrent_mA and dcdcInit->reverseCurrentControl
|
|
=> Updates DCDCMISCCTRL_P/NFETCNT
|
|
=> Updates DCDCMISCCTRL_LNCLIMILIMSEL and DCDCMISCCTRL_LPCLIMILIMSEL
|
|
=> Updates DCDCZDETCTRL_ZDETILIMSEL */
|
|
EMU_DCDCOptimizeSlice(dcdcInit->em01LoadCurrent_mA);
|
|
|
|
/* ======================================================= */
|
|
|
|
/* Set DCDC low noise mode compensator control register. */
|
|
compCtrlSet(dcdcInit->dcdcLnCompCtrl);
|
|
|
|
/* Set DCDC output voltage */
|
|
if (!EMU_DCDCOutputVoltageSet(dcdcInit->mVout, true, true)) {
|
|
EFM_ASSERT(false);
|
|
/* Return when assertions are disabled */
|
|
return false;
|
|
}
|
|
|
|
#if (_SILICON_LABS_GECKO_INTERNAL_SDID == 80)
|
|
/* Select analog peripheral power supply. This must be done before
|
|
DCDC mode is set for all EFM32xG1 and EFR32xG1 devices. */
|
|
BUS_RegBitWrite(&EMU->PWRCTRL,
|
|
_EMU_PWRCTRL_ANASW_SHIFT,
|
|
dcdcInit->anaPeripheralPower ? 1 : 0);
|
|
#endif
|
|
|
|
#if defined(_EMU_PWRCTRL_REGPWRSEL_MASK)
|
|
/* Select DVDD as input to the digital regulator. The switch to DVDD will take
|
|
effect once the DCDC output is stable. */
|
|
EMU->PWRCTRL |= EMU_PWRCTRL_REGPWRSEL_DVDD;
|
|
#endif
|
|
|
|
/* Set EM0 DCDC operating mode. Output voltage set in
|
|
EMU_DCDCOutputVoltageSet() above takes effect if mode
|
|
is changed from bypass/off mode. */
|
|
EMU_DCDCModeSet(dcdcInit->dcdcMode);
|
|
|
|
#if (_SILICON_LABS_GECKO_INTERNAL_SDID != 80)
|
|
/* Select analog peripheral power supply. This must be done after
|
|
DCDC mode is set for all devices other than EFM32xG1 and EFR32xG1. */
|
|
BUS_RegBitWrite(&EMU->PWRCTRL,
|
|
_EMU_PWRCTRL_ANASW_SHIFT,
|
|
dcdcInit->anaPeripheralPower ? 1 : 0);
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
|
|
/***************************************************************************//**
|
|
* @brief
|
|
* Set DCDC output voltage
|
|
*
|
|
* @param[in] mV
|
|
* Target DCDC output voltage in mV
|
|
*
|
|
* @return
|
|
* True if the mV parameter is valid
|
|
******************************************************************************/
|
|
bool EMU_DCDCOutputVoltageSet(uint32_t mV,
|
|
bool setLpVoltage,
|
|
bool setLnVoltage)
|
|
{
|
|
#if defined(_DEVINFO_DCDCLNVCTRL0_3V0LNATT1_MASK)
|
|
|
|
#define DCDC_TRIM_MODES ((uint8_t)dcdcTrimMode_LN + 1)
|
|
bool validOutVoltage;
|
|
bool attenuationSet;
|
|
uint32_t mVlow = 0;
|
|
uint32_t mVhigh = 0;
|
|
uint32_t mVdiff;
|
|
uint32_t vrefVal[DCDC_TRIM_MODES] = { 0 };
|
|
uint32_t vrefLow[DCDC_TRIM_MODES] = { 0 };
|
|
uint32_t vrefHigh[DCDC_TRIM_MODES] = { 0 };
|
|
uint8_t lpcmpBias[DCDC_TRIM_MODES] = { 0 };
|
|
|
|
/* Check that the set voltage is within valid range.
|
|
Voltages are obtained from the datasheet. */
|
|
validOutVoltage = ((mV >= PWRCFG_DCDCTODVDD_VMIN)
|
|
&& (mV <= PWRCFG_DCDCTODVDD_VMAX));
|
|
|
|
if (!validOutVoltage) {
|
|
EFM_ASSERT(false);
|
|
/* Return when assertions are disabled */
|
|
return false;
|
|
}
|
|
|
|
/* Set attenuation to use and low/high range. */
|
|
attenuationSet = (mV > 1800);
|
|
if (attenuationSet) {
|
|
mVlow = 1800;
|
|
mVhigh = 3000;
|
|
mVdiff = mVhigh - mVlow;
|
|
} else {
|
|
mVlow = 1200;
|
|
mVhigh = 1800;
|
|
mVdiff = mVhigh - mVlow;
|
|
}
|
|
|
|
/* Get 2-point calib data from DEVINFO */
|
|
|
|
/* LN mode */
|
|
if (attenuationSet) {
|
|
vrefLow[dcdcTrimMode_LN] = DEVINFO->DCDCLNVCTRL0;
|
|
vrefHigh[dcdcTrimMode_LN] = (vrefLow[dcdcTrimMode_LN] & _DEVINFO_DCDCLNVCTRL0_3V0LNATT1_MASK)
|
|
>> _DEVINFO_DCDCLNVCTRL0_3V0LNATT1_SHIFT;
|
|
vrefLow[dcdcTrimMode_LN] = (vrefLow[dcdcTrimMode_LN] & _DEVINFO_DCDCLNVCTRL0_1V8LNATT1_MASK)
|
|
>> _DEVINFO_DCDCLNVCTRL0_1V8LNATT1_SHIFT;
|
|
} else {
|
|
vrefLow[dcdcTrimMode_LN] = DEVINFO->DCDCLNVCTRL0;
|
|
vrefHigh[dcdcTrimMode_LN] = (vrefLow[dcdcTrimMode_LN] & _DEVINFO_DCDCLNVCTRL0_1V8LNATT0_MASK)
|
|
>> _DEVINFO_DCDCLNVCTRL0_1V8LNATT0_SHIFT;
|
|
vrefLow[dcdcTrimMode_LN] = (vrefLow[dcdcTrimMode_LN] & _DEVINFO_DCDCLNVCTRL0_1V2LNATT0_MASK)
|
|
>> _DEVINFO_DCDCLNVCTRL0_1V2LNATT0_SHIFT;
|
|
}
|
|
|
|
/* LP EM234H mode */
|
|
lpcmpBias[dcdcTrimMode_EM234H_LP] = (EMU->DCDCMISCCTRL & _GENERIC_DCDCMISCCTRL_LPCMPBIASEM234H_MASK)
|
|
>> _GENERIC_DCDCMISCCTRL_LPCMPBIASEM234H_SHIFT;
|
|
lpGetDevinfoVrefLowHigh(&vrefLow[dcdcTrimMode_EM234H_LP],
|
|
&vrefHigh[dcdcTrimMode_EM234H_LP],
|
|
attenuationSet,
|
|
lpcmpBias[dcdcTrimMode_EM234H_LP]);
|
|
|
|
#if defined(_EMU_DCDCLPEM01CFG_LPCMPBIASEM01_MASK)
|
|
/* LP EM01 mode */
|
|
lpcmpBias[dcdcTrimMode_EM01_LP] = (EMU->DCDCLPEM01CFG & _EMU_DCDCLPEM01CFG_LPCMPBIASEM01_MASK)
|
|
>> _EMU_DCDCLPEM01CFG_LPCMPBIASEM01_SHIFT;
|
|
lpGetDevinfoVrefLowHigh(&vrefLow[dcdcTrimMode_EM01_LP],
|
|
&vrefHigh[dcdcTrimMode_EM01_LP],
|
|
attenuationSet,
|
|
lpcmpBias[dcdcTrimMode_EM01_LP]);
|
|
#endif
|
|
|
|
/* Calculate output voltage trims */
|
|
vrefVal[dcdcTrimMode_LN] = ((mV - mVlow) * (vrefHigh[dcdcTrimMode_LN] - vrefLow[dcdcTrimMode_LN]))
|
|
/ mVdiff;
|
|
vrefVal[dcdcTrimMode_LN] += vrefLow[dcdcTrimMode_LN];
|
|
|
|
vrefVal[dcdcTrimMode_EM234H_LP] = ((mV - mVlow) * (vrefHigh[dcdcTrimMode_EM234H_LP] - vrefLow[dcdcTrimMode_EM234H_LP]))
|
|
/ mVdiff;
|
|
vrefVal[dcdcTrimMode_EM234H_LP] += vrefLow[dcdcTrimMode_EM234H_LP];
|
|
|
|
#if defined(_EMU_DCDCLPEM01CFG_LPCMPBIASEM01_MASK)
|
|
vrefVal[dcdcTrimMode_EM01_LP] = ((mV - mVlow) * (vrefHigh[dcdcTrimMode_EM01_LP] - vrefLow[dcdcTrimMode_EM01_LP]))
|
|
/ mVdiff;
|
|
vrefVal[dcdcTrimMode_EM01_LP] += vrefLow[dcdcTrimMode_EM01_LP];
|
|
#endif
|
|
|
|
/* Range checks */
|
|
if ((vrefVal[dcdcTrimMode_LN] > vrefHigh[dcdcTrimMode_LN])
|
|
|| (vrefVal[dcdcTrimMode_LN] < vrefLow[dcdcTrimMode_LN])
|
|
#if defined(_EMU_DCDCLPEM01CFG_LPCMPBIASEM01_MASK)
|
|
|| (vrefVal[dcdcTrimMode_EM01_LP] > vrefHigh[dcdcTrimMode_EM01_LP])
|
|
|| (vrefVal[dcdcTrimMode_EM01_LP] < vrefLow[dcdcTrimMode_EM01_LP])
|
|
#endif
|
|
|| (vrefVal[dcdcTrimMode_EM234H_LP] > vrefHigh[dcdcTrimMode_EM234H_LP])
|
|
|| (vrefVal[dcdcTrimMode_EM234H_LP] < vrefLow[dcdcTrimMode_EM234H_LP])) {
|
|
EFM_ASSERT(false);
|
|
/* Return when assertions are disabled */
|
|
return false;
|
|
}
|
|
|
|
/* Update output voltage tuning for LN and LP modes. */
|
|
if (setLnVoltage) {
|
|
EMU->DCDCLNVCTRL = (EMU->DCDCLNVCTRL & ~(_EMU_DCDCLNVCTRL_LNVREF_MASK | _EMU_DCDCLNVCTRL_LNATT_MASK))
|
|
| (vrefVal[dcdcTrimMode_LN] << _EMU_DCDCLNVCTRL_LNVREF_SHIFT)
|
|
| (attenuationSet ? EMU_DCDCLNVCTRL_LNATT : 0);
|
|
}
|
|
|
|
if (setLpVoltage) {
|
|
/* Load LP EM234H comparator hysteresis calibration */
|
|
if (!(lpCmpHystCalibrationLoad(attenuationSet, lpcmpBias[dcdcTrimMode_EM234H_LP], dcdcTrimMode_EM234H_LP))) {
|
|
EFM_ASSERT(false);
|
|
/* Return when assertions are disabled */
|
|
return false;
|
|
}
|
|
|
|
#if defined(_EMU_DCDCLPEM01CFG_LPCMPBIASEM01_MASK)
|
|
/* Load LP EM234H comparator hysteresis calibration */
|
|
if (!(lpCmpHystCalibrationLoad(attenuationSet, lpcmpBias[dcdcTrimMode_EM01_LP], dcdcTrimMode_EM01_LP))) {
|
|
EFM_ASSERT(false);
|
|
/* Return when assertions are disabled */
|
|
return false;
|
|
}
|
|
|
|
/* LP VREF is that max of trims for EM01 and EM234H. */
|
|
vrefVal[dcdcTrimMode_EM234H_LP] = SL_MAX(vrefVal[dcdcTrimMode_EM234H_LP], vrefVal[dcdcTrimMode_EM01_LP]);
|
|
#endif
|
|
|
|
/* Don't exceed max available code as specified in the reference manual for EMU_DCDCLPVCTRL. */
|
|
vrefVal[dcdcTrimMode_EM234H_LP] = SL_MIN(vrefVal[dcdcTrimMode_EM234H_LP], 0xE7U);
|
|
EMU->DCDCLPVCTRL = (EMU->DCDCLPVCTRL & ~(_EMU_DCDCLPVCTRL_LPVREF_MASK | _EMU_DCDCLPVCTRL_LPATT_MASK))
|
|
| (vrefVal[dcdcTrimMode_EM234H_LP] << _EMU_DCDCLPVCTRL_LPVREF_SHIFT)
|
|
| (attenuationSet ? EMU_DCDCLPVCTRL_LPATT : 0);
|
|
}
|
|
#endif
|
|
return true;
|
|
}
|
|
|
|
/***************************************************************************//**
|
|
* @brief
|
|
* Optimize DCDC slice count based on the estimated average load current
|
|
* in EM0
|
|
*
|
|
* @param[in] em0LoadCurrent_mA
|
|
* Estimated average EM0 load current in mA.
|
|
******************************************************************************/
|
|
void EMU_DCDCOptimizeSlice(uint32_t em0LoadCurrent_mA)
|
|
{
|
|
uint32_t sliceCount = 0;
|
|
uint32_t rcoBand = (EMU->DCDCLNFREQCTRL & _EMU_DCDCLNFREQCTRL_RCOBAND_MASK)
|
|
>> _EMU_DCDCLNFREQCTRL_RCOBAND_SHIFT;
|
|
|
|
/* Set recommended slice count */
|
|
if ((EMU->DCDCMISCCTRL & _EMU_DCDCMISCCTRL_LNFORCECCM_MASK) && (rcoBand >= emuDcdcLnRcoBand_5MHz)) {
|
|
if (em0LoadCurrent_mA < 20) {
|
|
sliceCount = 4;
|
|
} else if ((em0LoadCurrent_mA >= 20) && (em0LoadCurrent_mA < 40)) {
|
|
sliceCount = 8;
|
|
} else {
|
|
sliceCount = 16;
|
|
}
|
|
} else if ((!(EMU->DCDCMISCCTRL & _EMU_DCDCMISCCTRL_LNFORCECCM_MASK)) && (rcoBand <= emuDcdcLnRcoBand_4MHz)) {
|
|
if (em0LoadCurrent_mA < 10) {
|
|
sliceCount = 4;
|
|
} else if ((em0LoadCurrent_mA >= 10) && (em0LoadCurrent_mA < 20)) {
|
|
sliceCount = 8;
|
|
} else {
|
|
sliceCount = 16;
|
|
}
|
|
} else if ((EMU->DCDCMISCCTRL & _EMU_DCDCMISCCTRL_LNFORCECCM_MASK) && (rcoBand <= emuDcdcLnRcoBand_4MHz)) {
|
|
if (em0LoadCurrent_mA < 40) {
|
|
sliceCount = 8;
|
|
} else {
|
|
sliceCount = 16;
|
|
}
|
|
} else {
|
|
/* This configuration is not recommended. EMU_DCDCInit() applies a recommended
|
|
configuration. */
|
|
EFM_ASSERT(false);
|
|
}
|
|
|
|
/* The selected slices are PSLICESEL + 1 */
|
|
sliceCount--;
|
|
|
|
/* Apply slice count to both N and P slice */
|
|
sliceCount = (sliceCount << _EMU_DCDCMISCCTRL_PFETCNT_SHIFT
|
|
| sliceCount << _EMU_DCDCMISCCTRL_NFETCNT_SHIFT);
|
|
EMU->DCDCMISCCTRL = (EMU->DCDCMISCCTRL & ~(_EMU_DCDCMISCCTRL_PFETCNT_MASK
|
|
| _EMU_DCDCMISCCTRL_NFETCNT_MASK))
|
|
| sliceCount;
|
|
|
|
/* Update current limiters */
|
|
currentLimitersUpdate();
|
|
}
|
|
|
|
/***************************************************************************//**
|
|
* @brief
|
|
* Set DCDC Low-noise RCO band.
|
|
*
|
|
* @param[in] band
|
|
* RCO band to set.
|
|
******************************************************************************/
|
|
void EMU_DCDCLnRcoBandSet(EMU_DcdcLnRcoBand_TypeDef band)
|
|
{
|
|
uint32_t forcedCcm;
|
|
forcedCcm = BUS_RegBitRead(&EMU->DCDCMISCCTRL, _EMU_DCDCMISCCTRL_LNFORCECCM_SHIFT);
|
|
|
|
/* DCM mode supports up to 4MHz LN RCO. */
|
|
EFM_ASSERT((!forcedCcm && band <= emuDcdcLnRcoBand_4MHz) || forcedCcm);
|
|
|
|
EMU->DCDCLNFREQCTRL = (EMU->DCDCLNFREQCTRL & ~_EMU_DCDCLNFREQCTRL_RCOBAND_MASK)
|
|
| (band << _EMU_DCDCLNFREQCTRL_RCOBAND_SHIFT);
|
|
|
|
/* Update slice configuration as this depends on the RCO band. */
|
|
EMU_DCDCOptimizeSlice(dcdcEm01LoadCurrent_mA);
|
|
}
|
|
|
|
/***************************************************************************//**
|
|
* @brief
|
|
* Power off the DCDC regulator.
|
|
*
|
|
* @details
|
|
* This function powers off the DCDC controller. This function should only be
|
|
* used if the external power circuit is wired for no DCDC. If the external power
|
|
* circuit is wired for DCDC usage, then use EMU_DCDCInit() and set the
|
|
* DCDC in bypass mode to disable DCDC.
|
|
*
|
|
* @return
|
|
* Return false if the DCDC could not be disabled.
|
|
******************************************************************************/
|
|
bool EMU_DCDCPowerOff(void)
|
|
{
|
|
bool dcdcModeSet;
|
|
|
|
#if defined(_EMU_PWRCFG_MASK)
|
|
/* Set DCDCTODVDD only to enable write access to EMU->DCDCCTRL */
|
|
EMU->PWRCFG = EMU_PWRCFG_PWRCFG_DCDCTODVDD;
|
|
#endif
|
|
|
|
/* Select DVDD as input to the digital regulator */
|
|
#if defined(EMU_PWRCTRL_IMMEDIATEPWRSWITCH)
|
|
EMU->PWRCTRL |= EMU_PWRCTRL_REGPWRSEL_DVDD | EMU_PWRCTRL_IMMEDIATEPWRSWITCH;
|
|
#elif defined(EMU_PWRCTRL_REGPWRSEL_DVDD)
|
|
EMU->PWRCTRL |= EMU_PWRCTRL_REGPWRSEL_DVDD;
|
|
#endif
|
|
|
|
/* Set DCDC to OFF and disable LP in EM2/3/4. Verify that the required
|
|
mode could be set. */
|
|
while (EMU->DCDCSYNC & EMU_DCDCSYNC_DCDCCTRLBUSY) ;
|
|
EMU->DCDCCTRL = EMU_DCDCCTRL_DCDCMODE_OFF;
|
|
|
|
dcdcModeSet = (EMU->DCDCCTRL == EMU_DCDCCTRL_DCDCMODE_OFF);
|
|
EFM_ASSERT(dcdcModeSet);
|
|
|
|
return dcdcModeSet;
|
|
}
|
|
#endif
|
|
|
|
#if defined(EMU_STATUS_VMONRDY)
|
|
/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
|
|
|
|
/***************************************************************************//**
|
|
* @brief
|
|
* Get calibrated threshold value.
|
|
*
|
|
* @details
|
|
* All VMON channels have two calibration fields in the DI page that
|
|
* describes the threshold at 1.86V and 2.98V. This function will convert
|
|
* the uncalibrated input voltage threshold in millivolts into a calibrated
|
|
* threshold.
|
|
*
|
|
* @param[in] channel
|
|
* VMON channel
|
|
*
|
|
* @param[in] threshold
|
|
* Desired threshold in millivolts.
|
|
*
|
|
* @return
|
|
* Calibrated threshold value to use. First digit of return value is placed
|
|
* in the "fine" register fields while the next digits are placed in the
|
|
* "coarse" register fields.
|
|
******************************************************************************/
|
|
static uint32_t vmonCalibratedThreshold(EMU_VmonChannel_TypeDef channel,
|
|
int threshold)
|
|
{
|
|
uint32_t tLow;
|
|
uint32_t tHigh;
|
|
uint32_t calReg;
|
|
|
|
/* Get calibration values for 1.86V and 2.98V */
|
|
switch (channel) {
|
|
case emuVmonChannel_AVDD:
|
|
calReg = DEVINFO->VMONCAL0;
|
|
tLow = (10 * ((calReg & _DEVINFO_VMONCAL0_AVDD1V86THRESCOARSE_MASK)
|
|
>> _DEVINFO_VMONCAL0_AVDD1V86THRESCOARSE_SHIFT))
|
|
+ ((calReg & _DEVINFO_VMONCAL0_AVDD1V86THRESFINE_MASK)
|
|
>> _DEVINFO_VMONCAL0_AVDD1V86THRESFINE_SHIFT);
|
|
tHigh = (10 * ((calReg & _DEVINFO_VMONCAL0_AVDD2V98THRESCOARSE_MASK)
|
|
>> _DEVINFO_VMONCAL0_AVDD2V98THRESCOARSE_SHIFT))
|
|
+ ((calReg & _DEVINFO_VMONCAL0_AVDD2V98THRESFINE_MASK)
|
|
>> _DEVINFO_VMONCAL0_AVDD2V98THRESFINE_SHIFT);
|
|
break;
|
|
case emuVmonChannel_ALTAVDD:
|
|
calReg = DEVINFO->VMONCAL0;
|
|
tLow = (10 * ((calReg & _DEVINFO_VMONCAL0_ALTAVDD1V86THRESCOARSE_MASK)
|
|
>> _DEVINFO_VMONCAL0_ALTAVDD1V86THRESCOARSE_SHIFT))
|
|
+ ((calReg & _DEVINFO_VMONCAL0_ALTAVDD1V86THRESFINE_MASK)
|
|
>> _DEVINFO_VMONCAL0_ALTAVDD1V86THRESFINE_SHIFT);
|
|
tHigh = (10 * ((calReg & _DEVINFO_VMONCAL0_ALTAVDD2V98THRESCOARSE_MASK)
|
|
>> _DEVINFO_VMONCAL0_ALTAVDD2V98THRESCOARSE_SHIFT))
|
|
+ ((calReg & _DEVINFO_VMONCAL0_ALTAVDD2V98THRESFINE_MASK)
|
|
>> _DEVINFO_VMONCAL0_ALTAVDD2V98THRESFINE_SHIFT);
|
|
break;
|
|
case emuVmonChannel_DVDD:
|
|
calReg = DEVINFO->VMONCAL1;
|
|
tLow = (10 * ((calReg & _DEVINFO_VMONCAL1_DVDD1V86THRESCOARSE_MASK)
|
|
>> _DEVINFO_VMONCAL1_DVDD1V86THRESCOARSE_SHIFT))
|
|
+ ((calReg & _DEVINFO_VMONCAL1_DVDD1V86THRESFINE_MASK)
|
|
>> _DEVINFO_VMONCAL1_DVDD1V86THRESFINE_SHIFT);
|
|
tHigh = (10 * ((calReg & _DEVINFO_VMONCAL1_DVDD2V98THRESCOARSE_MASK)
|
|
>> _DEVINFO_VMONCAL1_DVDD2V98THRESCOARSE_SHIFT))
|
|
+ ((calReg & _DEVINFO_VMONCAL1_DVDD2V98THRESFINE_MASK)
|
|
>> _DEVINFO_VMONCAL1_DVDD2V98THRESFINE_SHIFT);
|
|
break;
|
|
case emuVmonChannel_IOVDD0:
|
|
calReg = DEVINFO->VMONCAL1;
|
|
tLow = (10 * ((calReg & _DEVINFO_VMONCAL1_IO01V86THRESCOARSE_MASK)
|
|
>> _DEVINFO_VMONCAL1_IO01V86THRESCOARSE_SHIFT))
|
|
+ ((calReg & _DEVINFO_VMONCAL1_IO01V86THRESFINE_MASK)
|
|
>> _DEVINFO_VMONCAL1_IO01V86THRESFINE_SHIFT);
|
|
tHigh = (10 * ((calReg & _DEVINFO_VMONCAL1_IO02V98THRESCOARSE_MASK)
|
|
>> _DEVINFO_VMONCAL1_IO02V98THRESCOARSE_SHIFT))
|
|
+ ((calReg & _DEVINFO_VMONCAL1_IO02V98THRESFINE_MASK)
|
|
>> _DEVINFO_VMONCAL1_IO02V98THRESFINE_SHIFT);
|
|
break;
|
|
default:
|
|
EFM_ASSERT(false);
|
|
return threshold;
|
|
}
|
|
|
|
if (tHigh <= tLow) {
|
|
/* Uncalibrated device guard */
|
|
return threshold;
|
|
}
|
|
|
|
/* Calculate threshold.
|
|
*
|
|
* Note that volt is used in the reference manual, however we are interested
|
|
* in millivolt results. We also increase the precision of Va and Vb in the
|
|
* calculation instead of using floating points.
|
|
*/
|
|
uint32_t va = (1120 * 100) / (tHigh - tLow);
|
|
uint32_t vb = (1860 * 100) - (va * tLow);
|
|
/* Round threshold to nearest integer value. */
|
|
return ((threshold * 100) - vb + (va / 2)) / va;
|
|
}
|
|
|
|
/** @endcond */
|
|
|
|
/***************************************************************************//**
|
|
* @brief
|
|
* Initialize VMON channel.
|
|
*
|
|
* @details
|
|
* Initialize a VMON channel without hysteresis. If the channel supports
|
|
* separate rise and fall triggers, both thresholds will be set to the same
|
|
* value. The threshold will be converted to a register field value based
|
|
* on calibration values from the DI page.
|
|
*
|
|
* @param[in] vmonInit
|
|
* VMON initialization struct
|
|
******************************************************************************/
|
|
void EMU_VmonInit(const EMU_VmonInit_TypeDef *vmonInit)
|
|
{
|
|
uint32_t thresholdCoarse, thresholdFine;
|
|
uint32_t threshold;
|
|
|
|
EFM_ASSERT((vmonInit->threshold >= 1620) && (vmonInit->threshold <= 3400));
|
|
|
|
threshold = vmonCalibratedThreshold(vmonInit->channel, vmonInit->threshold);
|
|
thresholdFine = threshold % 10;
|
|
thresholdCoarse = threshold / 10;
|
|
|
|
/* Saturate threshold to max values. */
|
|
if (thresholdCoarse > 0xF) {
|
|
thresholdCoarse = 0xF;
|
|
thresholdFine = 9;
|
|
}
|
|
|
|
switch (vmonInit->channel) {
|
|
case emuVmonChannel_AVDD:
|
|
EMU->VMONAVDDCTRL = (thresholdCoarse << _EMU_VMONAVDDCTRL_RISETHRESCOARSE_SHIFT)
|
|
| (thresholdFine << _EMU_VMONAVDDCTRL_RISETHRESFINE_SHIFT)
|
|
| (thresholdCoarse << _EMU_VMONAVDDCTRL_FALLTHRESCOARSE_SHIFT)
|
|
| (thresholdFine << _EMU_VMONAVDDCTRL_FALLTHRESFINE_SHIFT)
|
|
| (vmonInit->riseWakeup ? EMU_VMONAVDDCTRL_RISEWU : 0)
|
|
| (vmonInit->fallWakeup ? EMU_VMONAVDDCTRL_FALLWU : 0)
|
|
| (vmonInit->enable ? EMU_VMONAVDDCTRL_EN : 0);
|
|
break;
|
|
case emuVmonChannel_ALTAVDD:
|
|
EMU->VMONALTAVDDCTRL = (thresholdCoarse << _EMU_VMONALTAVDDCTRL_THRESCOARSE_SHIFT)
|
|
| (thresholdFine << _EMU_VMONALTAVDDCTRL_THRESFINE_SHIFT)
|
|
| (vmonInit->riseWakeup ? EMU_VMONALTAVDDCTRL_RISEWU : 0)
|
|
| (vmonInit->fallWakeup ? EMU_VMONALTAVDDCTRL_FALLWU : 0)
|
|
| (vmonInit->enable ? EMU_VMONALTAVDDCTRL_EN : 0);
|
|
break;
|
|
case emuVmonChannel_DVDD:
|
|
EMU->VMONDVDDCTRL = (thresholdCoarse << _EMU_VMONDVDDCTRL_THRESCOARSE_SHIFT)
|
|
| (thresholdFine << _EMU_VMONDVDDCTRL_THRESFINE_SHIFT)
|
|
| (vmonInit->riseWakeup ? EMU_VMONDVDDCTRL_RISEWU : 0)
|
|
| (vmonInit->fallWakeup ? EMU_VMONDVDDCTRL_FALLWU : 0)
|
|
| (vmonInit->enable ? EMU_VMONDVDDCTRL_EN : 0);
|
|
break;
|
|
case emuVmonChannel_IOVDD0:
|
|
EMU->VMONIO0CTRL = (thresholdCoarse << _EMU_VMONIO0CTRL_THRESCOARSE_SHIFT)
|
|
| (thresholdFine << _EMU_VMONIO0CTRL_THRESFINE_SHIFT)
|
|
| (vmonInit->retDisable ? EMU_VMONIO0CTRL_RETDIS : 0)
|
|
| (vmonInit->riseWakeup ? EMU_VMONIO0CTRL_RISEWU : 0)
|
|
| (vmonInit->fallWakeup ? EMU_VMONIO0CTRL_FALLWU : 0)
|
|
| (vmonInit->enable ? EMU_VMONIO0CTRL_EN : 0);
|
|
break;
|
|
default:
|
|
EFM_ASSERT(false);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/***************************************************************************//**
|
|
* @brief
|
|
* Initialize VMON channel with hysteresis (separate rise and fall triggers).
|
|
*
|
|
* @details
|
|
* Initialize a VMON channel which supports hysteresis. The AVDD channel is
|
|
* the only channel to support separate rise and fall triggers. The rise and
|
|
* fall thresholds will be converted to a register field value based on
|
|
* calibration values from the DI page.
|
|
*
|
|
* @param[in] vmonInit
|
|
* VMON Hysteresis initialization struct
|
|
******************************************************************************/
|
|
void EMU_VmonHystInit(const EMU_VmonHystInit_TypeDef *vmonInit)
|
|
{
|
|
uint32_t riseThreshold;
|
|
uint32_t fallThreshold;
|
|
|
|
/* VMON supports voltages between 1620 mV and 3400 mV (inclusive) */
|
|
EFM_ASSERT((vmonInit->riseThreshold >= 1620) && (vmonInit->riseThreshold <= 3400));
|
|
EFM_ASSERT((vmonInit->fallThreshold >= 1620) && (vmonInit->fallThreshold <= 3400));
|
|
/* Fall threshold has to be lower than rise threshold */
|
|
EFM_ASSERT(vmonInit->fallThreshold <= vmonInit->riseThreshold);
|
|
|
|
riseThreshold = vmonCalibratedThreshold(vmonInit->channel, vmonInit->riseThreshold);
|
|
fallThreshold = vmonCalibratedThreshold(vmonInit->channel, vmonInit->fallThreshold);
|
|
|
|
switch (vmonInit->channel) {
|
|
case emuVmonChannel_AVDD:
|
|
EMU->VMONAVDDCTRL = ((riseThreshold / 10) << _EMU_VMONAVDDCTRL_RISETHRESCOARSE_SHIFT)
|
|
| ((riseThreshold % 10) << _EMU_VMONAVDDCTRL_RISETHRESFINE_SHIFT)
|
|
| ((fallThreshold / 10) << _EMU_VMONAVDDCTRL_FALLTHRESCOARSE_SHIFT)
|
|
| ((fallThreshold % 10) << _EMU_VMONAVDDCTRL_FALLTHRESFINE_SHIFT)
|
|
| (vmonInit->riseWakeup ? EMU_VMONAVDDCTRL_RISEWU : 0)
|
|
| (vmonInit->fallWakeup ? EMU_VMONAVDDCTRL_FALLWU : 0)
|
|
| (vmonInit->enable ? EMU_VMONAVDDCTRL_EN : 0);
|
|
break;
|
|
default:
|
|
EFM_ASSERT(false);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/***************************************************************************//**
|
|
* @brief
|
|
* Enable or disable a VMON channel
|
|
*
|
|
* @param[in] channel
|
|
* VMON channel to enable/disable
|
|
*
|
|
* @param[in] enable
|
|
* Whether to enable or disable
|
|
******************************************************************************/
|
|
void EMU_VmonEnable(EMU_VmonChannel_TypeDef channel, bool enable)
|
|
{
|
|
uint32_t volatile * reg;
|
|
uint32_t bit;
|
|
|
|
switch (channel) {
|
|
case emuVmonChannel_AVDD:
|
|
reg = &(EMU->VMONAVDDCTRL);
|
|
bit = _EMU_VMONAVDDCTRL_EN_SHIFT;
|
|
break;
|
|
case emuVmonChannel_ALTAVDD:
|
|
reg = &(EMU->VMONALTAVDDCTRL);
|
|
bit = _EMU_VMONALTAVDDCTRL_EN_SHIFT;
|
|
break;
|
|
case emuVmonChannel_DVDD:
|
|
reg = &(EMU->VMONDVDDCTRL);
|
|
bit = _EMU_VMONDVDDCTRL_EN_SHIFT;
|
|
break;
|
|
case emuVmonChannel_IOVDD0:
|
|
reg = &(EMU->VMONIO0CTRL);
|
|
bit = _EMU_VMONIO0CTRL_EN_SHIFT;
|
|
break;
|
|
default:
|
|
EFM_ASSERT(false);
|
|
return;
|
|
}
|
|
|
|
BUS_RegBitWrite(reg, bit, enable);
|
|
}
|
|
|
|
/***************************************************************************//**
|
|
* @brief
|
|
* Get the status of a voltage monitor channel.
|
|
*
|
|
* @param[in] channel
|
|
* VMON channel to get status for
|
|
*
|
|
* @return
|
|
* Status of the selected VMON channel. True if channel is triggered.
|
|
******************************************************************************/
|
|
bool EMU_VmonChannelStatusGet(EMU_VmonChannel_TypeDef channel)
|
|
{
|
|
uint32_t bit;
|
|
switch (channel) {
|
|
case emuVmonChannel_AVDD:
|
|
bit = _EMU_STATUS_VMONAVDD_SHIFT;
|
|
break;
|
|
case emuVmonChannel_ALTAVDD:
|
|
bit = _EMU_STATUS_VMONALTAVDD_SHIFT;
|
|
break;
|
|
case emuVmonChannel_DVDD:
|
|
bit = _EMU_STATUS_VMONDVDD_SHIFT;
|
|
break;
|
|
case emuVmonChannel_IOVDD0:
|
|
bit = _EMU_STATUS_VMONIO0_SHIFT;
|
|
break;
|
|
default:
|
|
EFM_ASSERT(false);
|
|
bit = 0;
|
|
}
|
|
|
|
return BUS_RegBitRead(&EMU->STATUS, bit);
|
|
}
|
|
#endif /* EMU_STATUS_VMONRDY */
|
|
|
|
#if defined(_SILICON_LABS_GECKO_INTERNAL_SDID_80)
|
|
/***************************************************************************//**
|
|
* @brief
|
|
* Adjust the bias refresh rate
|
|
*
|
|
* @details
|
|
* This function is only meant to be used under high-temperature operation on
|
|
* EFR32xG1 and EFM32xG1 devices. Adjusting the bias mode will
|
|
* increase the typical current consumption. See application note 1027
|
|
* and errata documents for further details.
|
|
*
|
|
* @param [in] mode
|
|
* The new bias refresh rate
|
|
******************************************************************************/
|
|
void EMU_SetBiasMode(EMU_BiasMode_TypeDef mode)
|
|
{
|
|
#define EMU_TESTLOCK (*(volatile uint32_t *) (EMU_BASE + 0x190))
|
|
#define EMU_BIASCONF (*(volatile uint32_t *) (EMU_BASE + 0x164))
|
|
#define EMU_BIASTESTCTRL (*(volatile uint32_t *) (EMU_BASE + 0x19C))
|
|
#define CMU_ULFRCOCTRL (*(volatile uint32_t *) (CMU_BASE + 0x03C))
|
|
|
|
uint32_t freq = 0x2u;
|
|
bool emuTestLocked = false;
|
|
|
|
if (mode == emuBiasMode_1KHz) {
|
|
freq = 0x0u;
|
|
}
|
|
|
|
if (EMU_TESTLOCK == 0x1u) {
|
|
emuTestLocked = true;
|
|
EMU_TESTLOCK = 0xADE8u;
|
|
}
|
|
|
|
if (mode == emuBiasMode_Continuous) {
|
|
EMU_BIASCONF &= ~0x74u;
|
|
} else {
|
|
EMU_BIASCONF |= 0x74u;
|
|
}
|
|
|
|
EMU_BIASTESTCTRL |= 0x8u;
|
|
CMU_ULFRCOCTRL = (CMU_ULFRCOCTRL & ~0xC00u)
|
|
| ((freq & 0x3u) << 10u);
|
|
EMU_BIASTESTCTRL &= ~0x8u;
|
|
|
|
if (emuTestLocked) {
|
|
EMU_TESTLOCK = 0u;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/** @} (end addtogroup EMU) */
|
|
/** @} (end addtogroup emlib) */
|
|
#endif /* __EM_EMU_H */
|