/***************************************************************************//**
* @file
* @brief Energy Management Unit (EMU) Peripheral API
*******************************************************************************
* # License
* Copyright 2018 Silicon Laboratories Inc. www.silabs.com
*******************************************************************************
*
* SPDX-License-Identifier: Zlib
*
* The licensor of this software is Silicon Laboratories Inc.
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* 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. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 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.
*
******************************************************************************/
#include
#include "em_emu.h"
#if defined(EMU_PRESENT) && (EMU_COUNT > 0)
#include "sl_assert.h"
#include "em_cmu.h"
#include "sl_common.h"
#include "em_core.h"
#include "em_system.h"
#include "em_ramfunc.h"
#if defined(SL_CATALOG_METRIC_EM23_WAKE_PRESENT)
#include "sli_metric_em23_wake.h"
#include "sli_metric_em23_wake_config.h"
#endif
#if defined(SL_CATALOG_METRIC_EM4_WAKE_PRESENT)
#include "sli_metric_em4_wake.h"
#endif
#if defined(SYSCFG_PRESENT)
#include "em_syscfg.h"
#endif
/* Consistency check, since restoring assumes similar bit positions in */
/* CMU OSCENCMD and STATUS regs. */
#if defined(CMU_STATUS_AUXHFRCOENS) && (CMU_STATUS_AUXHFRCOENS != CMU_OSCENCMD_AUXHFRCOEN)
#error Conflict in AUXHFRCOENS and AUXHFRCOEN bitpositions
#endif
#if defined(CMU_STATUS_HFXOENS) && (CMU_STATUS_HFXOENS != CMU_OSCENCMD_HFXOEN)
#error Conflict in HFXOENS and HFXOEN bitpositions
#endif
#if defined(CMU_STATUS_LFRCOENS) && (CMU_STATUS_LFRCOENS != CMU_OSCENCMD_LFRCOEN)
#error Conflict in LFRCOENS and LFRCOEN bitpositions
#endif
#if defined(CMU_STATUS_LFXOENS) && (CMU_STATUS_LFXOENS != CMU_OSCENCMD_LFXOEN)
#error Conflict in LFXOENS and LFXOEN bitpositions
#endif
/*******************************************************************************
****************************** DEFINES ************************************
******************************************************************************/
#if defined(_SILICON_LABS_32B_SERIES_0)
/* Fix for errata EMU_E107 - non-WIC interrupt masks. */
#if defined(_EFM32_GECKO_FAMILY)
#define ERRATA_FIX_EMU_E107_ENABLE
#define NON_WIC_INT_MASK_0 (~(0x0dfc0323U))
#define NON_WIC_INT_MASK_1 (~(0x0U))
#elif defined(_EFM32_TINY_FAMILY)
#define ERRATA_FIX_EMU_E107_ENABLE
#define NON_WIC_INT_MASK_0 (~(0x001be323U))
#define NON_WIC_INT_MASK_1 (~(0x0U))
#elif defined(_EFM32_GIANT_FAMILY)
#define ERRATA_FIX_EMU_E107_ENABLE
#define NON_WIC_INT_MASK_0 (~(0xff020e63U))
#define NON_WIC_INT_MASK_1 (~(0x00000046U))
#elif defined(_EFM32_WONDER_FAMILY)
#define ERRATA_FIX_EMU_E107_ENABLE
#define NON_WIC_INT_MASK_0 (~(0xff020e63U))
#define NON_WIC_INT_MASK_1 (~(0x00000046U))
#elif defined(_EFM32_ZERO_FAMILY)
#define ERRATA_FIX_EMU_E107_ENABLE
#define NON_WIC_INT_MASK_0 (~(0x00005c6bU))
#define NON_WIC_INT_MASK_1 (~(0x00000000U))
#elif defined(_EFM32_HAPPY_FAMILY)
#define ERRATA_FIX_EMU_E107_ENABLE
#define NON_WIC_INT_MASK_0 (~(0x00085c6bU))
#define NON_WIC_INT_MASK_1 (~(0x00000000U))
#endif
#endif
#if defined(_SILICON_LABS_GECKO_INTERNAL_SDID_74) \
|| (defined(_SILICON_LABS_32B_SERIES_0) \
&& (defined(_EFM32_HAPPY_FAMILY) || defined(_EFM32_ZERO_FAMILY)))
// Fix for errata EMU_E110 - Potential Hard Fault when Exiting EM2.
#define ERRATA_FIX_EMU_E110_ENABLE
#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_ENABLE
#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_ENABLE
#endif
/* Enable FETCNT tuning errata fix. */
#if defined(_SILICON_LABS_GECKO_INTERNAL_SDID_80)
#define ERRATA_FIX_DCDC_FETCNT_SET_ENABLE
#endif
/* Enable LN handshake errata fix. */
#if defined(_SILICON_LABS_GECKO_INTERNAL_SDID_80)
#define ERRATA_FIX_DCDC_LNHS_BLOCK_ENABLE
typedef enum {
errataFixDcdcHsInit,
errataFixDcdcHsTrimSet,
errataFixDcdcHsBypassLn,
errataFixDcdcHsLnWaitDone
} errataFixDcdcHs_TypeDef;
static errataFixDcdcHs_TypeDef errataFixDcdcHsState = errataFixDcdcHsInit;
#endif
/* Fix for errata for EFM32GG11 and EFM32TG11. If a device is entering EM4S
* while powering the analog peripherals from DVDD, firmware must switch
* over to powering the analog peripherals from AVDD and delay the EM4S entry
* with 30 us. */
#if defined(_SILICON_LABS_GECKO_INTERNAL_SDID_100) \
|| defined(_SILICON_LABS_GECKO_INTERNAL_SDID_103)
#define ERRATA_FIX_EM4S_DELAY_ENTRY
#endif
#if defined(_SILICON_LABS_32B_SERIES_1) \
&& !defined(_SILICON_LABS_GECKO_INTERNAL_SDID_80) \
&& !defined(ERRATA_FIX_EMU_E220_DECBOD_IGNORE)
/* EMU_E220 DECBOD Errata fix. DECBOD Reset can occur
* during voltage scaling after EM2/3 wakeup. */
#define ERRATA_FIX_EMU_E220_DECBOD_ENABLE
#define EMU_PORBOD (*(volatile uint32_t *) (EMU_BASE + 0x14C))
#define EMU_PORBOD_GMC_CALIB_DISABLE (0x1UL << 31)
#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) ? 1UL : 0UL)
/* 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 2U
#define RAM1_BLOCK_SIZE 0x10000U // 64 kB blocks
#define RAM2_BLOCKS 1U
#define RAM2_BLOCK_SIZE 0x800U // 2 kB block
#elif defined(_SILICON_LABS_GECKO_INTERNAL_SDID_89)
#define RAM0_BLOCKS 2U
#define RAM0_BLOCK_SIZE 0x4000U
#define RAM1_BLOCKS 2U
#define RAM1_BLOCK_SIZE 0x4000U // 16 kB blocks
#define RAM2_BLOCKS 1U
#define RAM2_BLOCK_SIZE 0x800U // 2 kB block
#elif defined(_SILICON_LABS_GECKO_INTERNAL_SDID_95)
#define RAM0_BLOCKS 1U
#define RAM0_BLOCK_SIZE 0x4000U // 16 kB block
#define RAM1_BLOCKS 1U
#define RAM1_BLOCK_SIZE 0x4000U // 16 kB block
#define RAM2_BLOCKS 1U
#define RAM2_BLOCK_SIZE 0x800U // 2 kB block
#elif defined(_SILICON_LABS_GECKO_INTERNAL_SDID_103)
#define RAM0_BLOCKS 4U
#define RAM0_BLOCK_SIZE 0x2000U // 8 kB blocks
#elif defined(_SILICON_LABS_32B_SERIES_0) && defined(_EFM32_GIANT_FAMILY)
#define RAM0_BLOCKS 4U
#define RAM0_BLOCK_SIZE 0x8000U // 32 kB blocks
#elif defined(_SILICON_LABS_32B_SERIES_0) && defined(_EFM32_GECKO_FAMILY)
#define RAM0_BLOCKS 4U
#define RAM0_BLOCK_SIZE 0x1000U // 4 kB blocks
#elif defined(_SILICON_LABS_GECKO_INTERNAL_SDID_100)
#define RAM0_BLOCKS 8U
#define RAM0_BLOCK_SIZE 0x4000U // 16 kB blocks
#define RAM1_BLOCKS 8U
#define RAM1_BLOCK_SIZE 0x4000U // 16 kB blocks
#define RAM2_BLOCKS 4U
#define RAM2_BLOCK_SIZE 0x10000U // 64 kB blocks
#elif defined(_SILICON_LABS_GECKO_INTERNAL_SDID_106)
#define RAM0_BLOCKS 4U
#define RAM0_BLOCK_SIZE 0x4000U // 16 kB blocks
#define RAM1_BLOCKS 4U
#define RAM1_BLOCK_SIZE 0x4000U // 16 kB blocks
#define RAM2_BLOCKS 4U
#define RAM2_BLOCK_SIZE 0x4000U // 16 kB blocks
#elif defined(_SILICON_LABS_32B_SERIES_2_CONFIG_1)
#define RAM0_BLOCKS 6U
#define RAM0_BLOCK_SIZE 0x4000U // 16 kB blocks
#elif defined(_SILICON_LABS_32B_SERIES_2_CONFIG_3)
#define RAM0_BLOCKS 4U
#define RAM0_BLOCK_SIZE 0x4000U // 16 kB blocks
#elif defined(_SILICON_LABS_32B_SERIES_2_CONFIG_4)
#define RAM0_BLOCKS 16U
#define RAM0_BLOCK_SIZE 0x4000U // 16 kB blocks
#elif defined(_SILICON_LABS_32B_SERIES_2_CONFIG_5)
#define RAM0_BLOCKS 16U
#define RAM0_BLOCK_SIZE 0x8000U // 32 kB blocks
#elif defined(_SILICON_LABS_32B_SERIES_2_CONFIG_8)
#define RAM0_BLOCKS 16U
#define RAM0_BLOCK_SIZE 0x4000U // 16 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
#if defined(EMU_SERIES1_DCDC_BUCK_PRESENT)
#if !defined(PWRCFG_DCDCTODVDD_VMIN)
/** DCDCTODVDD output range maximum. */
#define PWRCFG_DCDCTODVDD_VMIN 1800U
#endif
#if !defined(PWRCFG_DCDCTODVDD_VMAX)
/** DCDCTODVDD output range minimum. */
#define PWRCFG_DCDCTODVDD_VMAX 3000U
#endif
#endif
#if defined(ERRATA_FIX_DCDC_FETCNT_SET_ENABLE) || defined(EMU_SERIES1_DCDC_BUCK_PRESENT)
#define DCDC_LP_PFET_CNT 7
#define DCDC_LP_NFET_CNT 7
#endif
#if defined(ERRATA_FIX_DCDC_LNHS_BLOCK_ENABLE)
#define EMU_DCDCSTATUS (*(volatile uint32_t *)(EMU_BASE + 0x7C))
#endif
#if defined(EMU_SERIES1_DCDC_BUCK_PRESENT)
/* 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
/* Disable LP mode hysteresis 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))
#define DCDC_TRIM_MODES ((uint8_t)dcdcTrimMode_LN + 1)
#endif
#if defined(EMU_SERIES2_DCDC_BUCK_PRESENT) \
|| defined(EMU_SERIES2_DCDC_BOOST_PRESENT)
/* EMU DCDC MODE set timeout. */
#define EMU_DCDC_MODE_SET_TIMEOUT 1000000
#endif
#if defined(_SILICON_LABS_GECKO_INTERNAL_SDID_80)
#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))
#endif
#if defined(_EMU_TEMP_TEMP_MASK)
/* As the energy mode at which a temperature measurement was taken at is
* not known, the chosen constant for the TEMPCO calculation is midway between
* the EM0/EM1 constant and the EM2/EM3/EM4 constant.
*/
#define EMU_TEMPCO_CONST (0.273f)
#endif
#define EMU_EM4_ENTRY_WAIT_LOOPS 200
/*******************************************************************************
*************************** LOCAL VARIABLES ********************************
******************************************************************************/
/* Static user configuration. */
#if defined(EMU_SERIES1_DCDC_BUCK_PRESENT)
static uint16_t dcdcMaxCurrent_mA;
static uint16_t dcdcEm01LoadCurrent_mA;
static EMU_DcdcLnReverseCurrentControl_TypeDef dcdcReverseCurrentControl;
#endif
#if defined(EMU_VSCALE_EM01_PRESENT)
static EMU_EM01Init_TypeDef vScaleEM01Config = { false };
#endif
/*******************************************************************************
************************** LOCAL FUNCTIONS ********************************
******************************************************************************/
#if defined(EMU_VSCALE_EM01_PRESENT)
/* Convert from level to EM0/1 command bit */
__STATIC_INLINE uint32_t vScaleEM01Cmd(EMU_VScaleEM01_TypeDef level)
{
#if defined(_SILICON_LABS_32B_SERIES_2)
return EMU_CMD_EM01VSCALE1 << ((uint32_t)level - _EMU_STATUS_VSCALE_VSCALE1);
#else
return EMU_CMD_EM01VSCALE0 << (_EMU_STATUS_VSCALE_VSCALE0 - (uint32_t)level);
#endif
}
#endif
#if defined(_SILICON_LABS_GECKO_INTERNAL_SDID_205) \
|| defined(ERRATA_FIX_EMU_E110_ENABLE)
SL_RAMFUNC_DECLARATOR static void __attribute__ ((noinline)) ramWFI(void);
SL_RAMFUNC_DEFINITION_BEGIN
static void __attribute__ ((noinline)) ramWFI(void)
{
#if defined(_SILICON_LABS_GECKO_INTERNAL_SDID_205)
__WFI(); // Enter EM2 or EM3
if (CoreDebug->DHCSR & CoreDebug_DHCSR_C_DEBUGEN_Msk) {
for (volatile int i = 0; i < 6; i++) {
} // Dummy wait loop ...
}
#else
__WFI(); // Enter EM2 or EM3
#if defined(__GNUC__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Warray-bounds"
#endif
*(volatile uint32_t*)4; // Clear faulty read data after wakeup
#if defined(__GNUC__)
#pragma GCC diagnostic pop
#endif
#endif
}
SL_RAMFUNC_DEFINITION_END
#endif
#if defined(ERRATA_FIX_EMU_E220_DECBOD_ENABLE)
SL_RAMFUNC_DECLARATOR static void __attribute__ ((noinline)) ramWFI(void);
SL_RAMFUNC_DEFINITION_BEGIN
static void __attribute__ ((noinline)) ramWFI(void)
{
/* Second part of EMU_E220 DECBOD Errata fix. Calibration needs to be disabled
* quickly when coming out of EM2/EM3. Ram execution is needed to meet timing.
* Calibration is re-enabled after voltage scaling completes. */
uint32_t temp = EMU_PORBOD | EMU_PORBOD_GMC_CALIB_DISABLE;
__WFI();
EMU_PORBOD = temp;
}
SL_RAMFUNC_DEFINITION_END
#endif
#if (_SILICON_LABS_32B_SERIES < 2)
/***************************************************************************//**
* @brief
* Save/restore/update oscillator, core clock and voltage scaling configuration on
* EM2 or EM3 entry/exit.
*
* @details
* Hardware may automatically change the oscillator and the 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_VSCALE_PRESENT)
static uint8_t vScaleStatus;
static uint32_t hfrcoCtrl;
#endif
/* Save or update state. */
if (action == emState_Save) {
/* Save configuration. */
cmuStatus = CMU->STATUS;
hfClock = CMU_ClockSelectGet(cmuClock_HF);
#if defined(EMU_VSCALE_PRESENT)
/* Save vscale. */
EMU_VScaleWait();
vScaleStatus = (uint8_t)((EMU->STATUS & _EMU_STATUS_VSCALE_MASK)
>> _EMU_STATUS_VSCALE_SHIFT);
hfrcoCtrl = CMU->HFRCOCTRL;
#endif
} else { /* Restore state. */
/* Apply saved configuration. */
#if defined(EMU_VSCALE_PRESENT)
#if defined(_SILICON_LABS_32B_SERIES_1)
if (EMU_LDOStatusGet() == true)
/* Restore voltage scaling level if LDO regulator is on. */
#endif
{
/* Restore EM0 and 1 voltage scaling level.
@ref 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();
#if defined(_CMU_OSCENCMD_MASK)
/* 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) != 0U
? CMU_OSCENCMD_HFRCOEN : 0U;
oscEnCmd |= (cmuStatus & CMU_STATUS_AUXHFRCOENS) != 0U
? CMU_OSCENCMD_AUXHFRCOEN : 0U;
oscEnCmd |= (cmuStatus & CMU_STATUS_LFRCOENS) != 0U
? CMU_OSCENCMD_LFRCOEN : 0U;
oscEnCmd |= (cmuStatus & CMU_STATUS_HFXOENS) != 0U
? CMU_OSCENCMD_HFXOEN : 0U;
oscEnCmd |= (cmuStatus & CMU_STATUS_LFXOENS) != 0U
? CMU_OSCENCMD_LFXOEN : 0U;
#if defined(_CMU_STATUS_USHFRCOENS_MASK)
oscEnCmd |= (cmuStatus & CMU_STATUS_USHFRCOENS) != 0U
? CMU_OSCENCMD_USHFRCOEN : 0U;
#endif
CMU->OSCENCMD = oscEnCmd;
#endif
#if defined(_EMU_STATUS_VSCALE_MASK)
/* Wait for upscale to complete and then restore selected clock. */
EMU_VScaleWait();
if ((EMU->CTRL & _EMU_CTRL_EM23VSCALEAUTOWSEN_MASK) != 0U) {
/* Restore HFRCO frequency which was automatically adjusted by hardware. */
while ((CMU->SYNCBUSY & CMU_SYNCBUSY_HFRCOBSY) != 0U) {
}
CMU->HFRCOCTRL = hfrcoCtrl;
if (hfClock == cmuSelect_HFRCO) {
/* Optimize wait state after EM2/EM3 wakeup because hardware has
* modified them. */
CMU_UpdateWaitStates(SystemHfrcoFreq, (int)EMU_VScaleGet());
}
}
#endif
switch (hfClock) {
case cmuSelect_LFXO:
CMU_CLOCK_SELECT_SET(HF, LFXO);
break;
case cmuSelect_LFRCO:
CMU_CLOCK_SELECT_SET(HF, LFRCO);
break;
case cmuSelect_HFXO:
CMU_CLOCK_SELECT_SET(HF, HFXO);
break;
#if defined(CMU_CMD_HFCLKSEL_USHFRCODIV2)
case cmuSelect_USHFRCODIV2:
CMU_CLOCK_SELECT_SET(HF, USHFRCODIV2);
break;
#endif
#if defined(CMU_HFCLKSTATUS_SELECTED_HFRCODIV2)
case cmuSelect_HFRCODIV2:
CMU_CLOCK_SELECT_SET(HF, HFRCODIV2);
break;
#endif
#if defined(CMU_HFCLKSTATUS_SELECTED_CLKIN0)
case cmuSelect_CLKIN0:
CMU_CLOCK_SELECT_SET(HF, CLKIN0);
break;
#endif
#if defined(CMU_HFCLKSTATUS_SELECTED_USHFRCO)
case cmuSelect_USHFRCO:
CMU_CLOCK_SELECT_SET(HF, USHFRCO);
break;
#endif
}
#if defined(_CMU_OSCENCMD_MASK)
/* 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) == 0U) {
CMU->OSCENCMD = CMU_OSCENCMD_HFRCODIS;
}
#endif
/* Restore CMU register locking */
if (cmuLocked != 0U) {
CMU_Lock();
}
}
}
#endif
#if defined(ERRATA_FIX_EMU_E107_ENABLE)
/* Get enable conditions for errata EMU_E107 fix. */
__STATIC_INLINE bool getErrataFixEmuE107En(void)
{
#if defined(_EFM32_HAPPY_FAMILY) \
|| defined(_EFM32_TINY_FAMILY) \
|| defined(_EFM32_WONDER_FAMILY) \
|| defined(_EFM32_ZERO_FAMILY)
// all revisions have the errata
return true;
#else
/* SYSTEM_ChipRevisionGet() could have been used here, but a faster implementation
* would be needed 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)
// all GECKO revisions except Revision E have the errata
return (majorMinorRev <= 0x0103);
#elif defined(_EFM32_GIANT_FAMILY)
// all LEOPARD GECKO (Major = 0x01 Or 0x02) revisions have the errata
// all GIANT GECKO (Major = 0x01) revisions except Revision E have the errata
return (majorMinorRev <= 0x0103) || (majorMinorRev == 0x0204) || (majorMinorRev == 0x0205);
#else
/* Invalid configuration. */
EFM_ASSERT(false);
/* Return when assertions are disabled. */
return false;
#endif
#endif /* #if defined(_EFM32_ZERO_FAMILY) || defined(_EFM32_HAPPY_FAMILY) #else */
}
#endif /* #if defined(ERRATA_FIX_EMU_E107_ENABLE) */
#if defined(ERRATA_FIX_EMU_E110_ENABLE)
/* Get enable conditions for errata EMU_E110 fix. */
__STATIC_INLINE bool getErrataFixEmuE110En(void)
{
/* SYSTEM_ChipRevisionGet() could have been used here, but a faster implementation
* would be needed 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(_SILICON_LABS_32B_SERIES_0) && defined(_EFM32_ZERO_FAMILY)
return (majorMinorRev == 0x0100);
#elif defined(_SILICON_LABS_32B_SERIES_0) && defined(_EFM32_HAPPY_FAMILY)
return ((majorMinorRev == 0x0100 || majorMinorRev == 0x0101));
#elif defined(_SILICON_LABS_32B_SERIES_0) && defined(_EFM32_WONDER_FAMILY)
return (majorMinorRev == 0x0100);
#elif defined(_SILICON_LABS_32B_SERIES_0) && defined(_EFM32_GIANT_FAMILY)
return (majorMinorRev == 0x0204);
#else
/* Invalid configuration. */
EFM_ASSERT(false);
/* Return when assertions are disabled. */
return false;
#endif
}
#endif /* #if defined(ERRATA_FIX_EMU_E110_ENABLE) */
/* LP prepare / LN restore P/NFET count. */
#if defined(ERRATA_FIX_DCDC_FETCNT_SET_ENABLE)
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_ENABLE)
static void dcdcHsFixLnBlock(void)
{
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) && defined(EMU_CTRL_EM23VSCALEAUTOWSEN)
/* Configure EMU and CMU for EM2 and 3 voltage downscale. */
static void vScaleDownEM23Setup(void)
{
#if defined(_SILICON_LABS_32B_SERIES_1)
if (EMU_LDOStatusGet() == false) {
/* Skip voltage scaling if the LDO regulator is turned off. */
return;
}
#endif
/* Wait until previous scaling is done. */
EMU_VScaleWait();
uint32_t em23vs = (EMU->CTRL & _EMU_CTRL_EM23VSCALE_MASK) >> _EMU_CTRL_EM23VSCALE_SHIFT;
uint32_t em01vs = (EMU->STATUS & _EMU_STATUS_VSCALE_MASK) >> _EMU_STATUS_VSCALE_SHIFT;
/* Inverse coding. */
if (em23vs > em01vs) {
EMU->CTRL |= EMU_CTRL_EM23VSCALEAUTOWSEN;
#if defined(_MSC_RAMCTRL_RAMWSEN_MASK)
/* Set RAM wait states for safe EM2 wakeup. */
BUS_RegMaskedSet(&MSC->RAMCTRL, (MSC_RAMCTRL_RAMWSEN
| MSC_RAMCTRL_RAM1WSEN
| MSC_RAMCTRL_RAM2WSEN));
#endif
} else {
EMU->CTRL &= ~EMU_CTRL_EM23VSCALEAUTOWSEN;
}
}
/* Handle automatic HFRCO adjustment that may have occurred during EM2/EM3. */
static void vScaleAfterWakeup(void)
{
if ((EMU->CTRL & EMU_CTRL_EM23VSCALEAUTOWSEN) != 0U) {
/* The hardware may have updated the HFRCOCTRL register during EM2/EM3
* entry if voltage scaling in EM2/EM3 is enabled. The hardware would
* then update the HFRCO frequency to 19 MHz automatically. */
uint32_t freqRange = (CMU->HFRCOCTRL & _CMU_HFRCOCTRL_FREQRANGE_MASK)
>> _CMU_HFRCOCTRL_FREQRANGE_SHIFT;
if (freqRange == 0x08U) {
SystemHfrcoFreq = 19000000;
}
}
}
#endif
#if defined(_SILICON_LABS_32B_SERIES_2_CONFIG_2) || defined(_SILICON_LABS_32B_SERIES_2_CONFIG_7)
typedef enum {
dpllState_Save, /* Save DPLL state. */
dpllState_Restore, /* Restore DPLL. */
} dpllState_TypeDef;
/***************************************************************************//**
* @brief
* Save or restore DPLL state.
*
* @param[in] action
* Value to indicate saving DPLL state or restoring its state.
*
* @note
* The function is used in EMU_Save() and EMU_Restore() to handle the
* DPLL state before entering EM2 or EM3 and after exiting EM2 or EM3.
* The function is required for the EFR32xG22 and EFR32xG27 families.
* On those families devices, the DPLL is disabled automatically when
* entering EM2, EM3. But exiting EM2, EM3 won't re-enable automatically
* the DPLL. Hence, the software needs to re-enable the DPLL upon EM2/3
* exit.
******************************************************************************/
static void dpllState(dpllState_TypeDef action)
{
CMU_ClkDiv_TypeDef div;
static uint32_t dpllRefClk = CMU_DPLLREFCLKCTRL_CLKSEL_DISABLED;
if (action == dpllState_Save) {
dpllRefClk = CMU_DPLLREFCLKCTRL_CLKSEL_DISABLED;
CMU->CLKEN0_SET = CMU_CLKEN0_DPLL0;
if (DPLL0->EN == DPLL_EN_EN) {
/* DPLL is in use, save reference clock selection. */
dpllRefClk = CMU->DPLLREFCLKCTRL;
}
} else { /* Restore */
if ((dpllRefClk != CMU_DPLLREFCLKCTRL_CLKSEL_DISABLED)
&& (DPLL0->EN != DPLL_EN_EN)) {
/* Restore DPLL reference clock selection. */
CMU->DPLLREFCLKCTRL = dpllRefClk;
/* Only wait for DPLL lock if HFRCODPLL is used as SYSCLK. */
if (CMU_ClockSelectGet(cmuClock_SYSCLK) == cmuSelect_HFRCODPLL) {
/* Set HCLK prescaler to safe value to avoid overclocking while locking. */
div = CMU_ClockDivGet(cmuClock_HCLK);
if (div == 1U) {
CMU_ClockDivSet(cmuClock_HCLK, 2U);
}
/* Relock DPLL and wait for ready. */
DPLL0->IF_CLR = DPLL_IF_LOCK | DPLL_IF_LOCKFAILLOW | DPLL_IF_LOCKFAILHIGH;
DPLL0->EN_SET = DPLL_EN_EN;
while ((DPLL0->IF & DPLL_IF_LOCK) == 0U) {
}
/* Restore HCLK prescaler. */
if (div == 1U) {
CMU_ClockDivSet(cmuClock_HCLK, 1U);
}
} else {
/* Relock DPLL and exit without waiting for ready. */
DPLL0->EN_SET = DPLL_EN_EN;
}
}
}
}
#endif
/*******************************************************************************
************************** GLOBAL FUNCTIONS *******************************
******************************************************************************/
/***************************************************************************//**
* @addtogroup emu EMU - Energy Management Unit
* @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.
* @{
******************************************************************************/
#if defined(EMU_VSCALE_EM01_PRESENT)
/***************************************************************************//**
* @brief
* Update the 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 the 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_VSCALE_PRESENT)
EMU->CTRL = (EMU->CTRL & ~_EMU_CTRL_EM23VSCALE_MASK)
| ((uint32_t)em23Init->vScaleEM23Voltage << _EMU_CTRL_EM23VSCALE_SHIFT);
#if defined(CMU_HFXOCTRL_AUTOSTARTSELEM0EM1)
if (em23Init->vScaleEM23Voltage == emuVScaleEM23_LowPower) {
/* Voltage scaling is not compatible with HFXO auto start and select. */
EFM_ASSERT((CMU->HFXOCTRL & CMU_HFXOCTRL_AUTOSTARTSELEM0EM1) == 0U);
}
#endif
#endif
}
/***************************************************************************//**
* @brief
* Energy mode 2/3 pre-sleep hook function.
*
* @details
* This function is called by EMU_EnterEM2() and EMU_EnterEM3() functions
* just prior to execution of the WFI instruction. The function implementation
* does not perform anything, but it is SL_WEAK so that it can be re-
* implemented in application code if actions are needed.
******************************************************************************/
SL_WEAK void EMU_EM23PresleepHook(void)
{
}
/***************************************************************************//**
* @brief
* EFP's Energy mode 2/3 pre-sleep hook function.
*
* @details
* This function is similar to @ref EMU_EM23PresleepHook() but is reserved
* for EFP usage.
*
* @note
* The function is primarily meant to be used in systems with EFP circuitry.
* (EFP = Energy Friendly Pmic (PMIC = Power Management IC)).
* In such systems there is a need to drive certain signals to EFP pins to
* notify about energy mode transitions.
******************************************************************************/
SL_WEAK void EMU_EFPEM23PresleepHook(void)
{
}
/***************************************************************************//**
* @brief
* Energy mode 2/3 post-sleep hook function.
*
* @details
* This function is called by EMU_EnterEM2() and EMU_EnterEM3() functions
* just after wakeup from the WFI instruction. The function implementation
* does not perform anything, but it is SL_WEAK so that it can be re-
* implemented in application code if actions are needed.
******************************************************************************/
SL_WEAK void EMU_EM23PostsleepHook(void)
{
}
/***************************************************************************//**
* @brief
* EFP's Energy mode 2/3 post-sleep hook function.
*
* @details
* This function is similar to @ref EMU_EM23PostsleepHook() but is reserved
* for EFP usage.
*
* @note
* The function is primarily meant to be used in systems with EFP circuitry.
* (EFP = Energy Friendly Pmic (PMIC = Power Management IC)).
* In such systems there is a need to drive certain signals to EFP pins to
* notify about energy mode transitions.
******************************************************************************/
SL_WEAK void EMU_EFPEM23PostsleepHook(void)
{
}
/***************************************************************************//**
* @brief
* Enter energy mode 2 (EM2).
*
* @details
* When entering EM2, high-frequency clocks are disabled, i.e., 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.
* @note
* If ERRATA_FIX_EMU_E110_ENABLE is active, the core's SLEEPONEXIT feature
* can not be used.
* @note
* This function is incompatible with the Power Manager module. When the
* Power Manager module is present, it must be the one deciding at which
* EM level the device sleeps to ensure the application properly works. Using
* both at the same time could lead to undefined behavior in the application.
* @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 automatic starting and selecting of the core clocks will be done,
* regardless 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.
* @par
* On Series 2 Config 2 devices (EFRxG22), this function will also relock the
* DPLL if the DPLL is used and @p restore is true.
*
* Note that the hardware will automatically update the HFRCO frequency in the
* case where voltage scaling is used in EM2/EM3 and not in EM0/EM1. When the
* restore argument to this function is true then software will restore the
* original HFRCO frequency after EM2/EM3 wake up. If the restore argument is
* false then the HFRCO frequency is 19 MHz when coming out of EM2/EM3 and
* all wait states are at a safe value.
*
* @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(SLI_METRIC_EM2_HOOK)
sli_metric_em23_wake_init(SLI_INIT_EM2_WAKE);
#endif
#if defined(ERRATA_FIX_EMU_E107_ENABLE)
bool errataFixEmuE107En;
uint32_t nonWicIntEn[2];
#endif
#if defined(ERRATA_FIX_EMU_E110_ENABLE)
bool errataFixEmuE110En;
#endif
#if defined(_SILICON_LABS_32B_SERIES_2_CONFIG_2) || defined(_SILICON_LABS_32B_SERIES_2_CONFIG_7)
if (restore) {
dpllState(dpllState_Save);
}
#endif
#if (_SILICON_LABS_32B_SERIES < 2)
/* Only save EMU and CMU state if restored on wake-up. */
if (restore) {
emState(emState_Save);
}
#endif
#if defined(_EMU_CTRL_EM23VSCALE_MASK) && defined(EMU_CTRL_EM23VSCALEAUTOWSEN)
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_ENABLE)
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_ENABLE)
dcdcFetCntSet(true);
#endif
#if defined(ERRATA_FIX_DCDC_LNHS_BLOCK_ENABLE)
dcdcHsFixLnBlock();
#endif
EMU_EM23PresleepHook();
EMU_EFPEM23PresleepHook();
#if defined(_SILICON_LABS_GECKO_INTERNAL_SDID_205) \
|| defined(ERRATA_FIX_EMU_E110_ENABLE)
#if defined(ERRATA_FIX_EMU_E110_ENABLE)
errataFixEmuE110En = getErrataFixEmuE110En();
if (errataFixEmuE110En) {
#endif
CORE_CRITICAL_SECTION(ramWFI(); )
#if defined(ERRATA_FIX_EMU_E110_ENABLE)
} else {
__WFI();
}
#endif
#elif defined(ERRATA_FIX_EMU_E220_DECBOD_ENABLE)
// Apply errata fix if voltage scaling in EM2 is used.
if ((EMU->CTRL & EMU_CTRL_EM23VSCALEAUTOWSEN) != 0U) {
CORE_CRITICAL_SECTION(ramWFI(); )
} else {
__WFI();
}
#else
__WFI();
#endif
EMU_EFPEM23PostsleepHook();
EMU_EM23PostsleepHook();
#if defined(ERRATA_FIX_DCDC_FETCNT_SET_ENABLE)
dcdcFetCntSet(false);
#endif
/* Fix for errata EMU_E107 - restore state of non-WIC interrupt enable flags. */
#if defined(ERRATA_FIX_EMU_E107_ENABLE)
if (errataFixEmuE107En) {
NVIC->ISER[0] = nonWicIntEn[0];
#if (NON_WIC_INT_MASK_1 != (~(0x0U)))
NVIC->ISER[1] = nonWicIntEn[1];
#endif
}
#endif
#if (_SILICON_LABS_32B_SERIES < 2)
/* Restore oscillators/clocks and voltage scaling if supported. */
if (restore) {
emState(emState_Restore);
}
#if defined(_EMU_CTRL_EM23VSCALE_MASK) && defined(EMU_CTRL_EM23VSCALEAUTOWSEN)
else {
vScaleAfterWakeup();
}
#if defined(ERRATA_FIX_EMU_E220_DECBOD_ENABLE)
/* Third part of EMU_E220 DECBOD Errata fix. Calibration needs to be enabled
* after voltage scaling completes. */
EMU_PORBOD &= ~(EMU_PORBOD_GMC_CALIB_DISABLE);
#endif
#endif
#endif
#if defined(_SILICON_LABS_32B_SERIES_2_CONFIG_2) || defined(_SILICON_LABS_32B_SERIES_2_CONFIG_7)
if (restore) {
dpllState(dpllState_Restore);
}
#endif
if (!restore) {
/* If not restoring, and the original clock was not HFRCO, the CMSIS */
/* core clock variable must be updated 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 hardware, i.e., HFXO,
* HFRCO, and AUXHFRCO (for AUXHFRCO, see exception note below). In addition,
* the low-frequency clocks, i.e., LFXO and LFRCO are disabled by software. 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 an
* oscillator other 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 software. However, since it is handled by software, 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. This feature is, however, outside the scope of the
* implementation in this function.
* @note
* If ERRATA_FIX_EMU_E110_ENABLE is active, the core's SLEEPONEXIT feature
* can't be used.
* @note
* This function is incompatible with the Power Manager module. When the
* Power Manager module is present, it must be the one deciding at which
* EM level the device sleeps to ensure the application properly works. Using
* both at the same time could lead to undefined behavior in the application.
* @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, therefore, 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.
* @par
* On Series 2 Config 2 devices (EFRxG22), this function will also relock the
* DPLL if the DPLL is used and @p restore is true.
*
* @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)
{
#if defined(SLI_METRIC_EM3_HOOK)
sli_metric_em23_wake_init(SLI_INIT_EM3_WAKE);
#endif
#if defined(ERRATA_FIX_EMU_E107_ENABLE)
bool errataFixEmuE107En;
uint32_t nonWicIntEn[2];
#endif
#if defined(ERRATA_FIX_EMU_E110_ENABLE)
bool errataFixEmuE110En;
#endif
#if defined(_SILICON_LABS_32B_SERIES_2_CONFIG_2) || defined(_SILICON_LABS_32B_SERIES_2_CONFIG_7)
if (restore) {
dpllState(dpllState_Save);
}
#endif
#if (_SILICON_LABS_32B_SERIES < 2)
/* Only save EMU and CMU state if restored on wake-up. */
if (restore) {
emState(emState_Save);
}
#endif
#if defined(_EMU_CTRL_EM23VSCALE_MASK) && defined(EMU_CTRL_EM23VSCALEAUTOWSEN)
vScaleDownEM23Setup();
#endif
#if defined(_CMU_OSCENCMD_MASK)
uint32_t cmuLocked;
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 != 0U) {
CMU_Lock();
}
#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_ENABLE)
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_ENABLE)
dcdcFetCntSet(true);
#endif
#if defined(ERRATA_FIX_DCDC_LNHS_BLOCK_ENABLE)
dcdcHsFixLnBlock();
#endif
EMU_EM23PresleepHook();
#if defined(_SILICON_LABS_GECKO_INTERNAL_SDID_205) \
|| defined(ERRATA_FIX_EMU_E110_ENABLE)
#if defined(ERRATA_FIX_EMU_E110_ENABLE)
errataFixEmuE110En = getErrataFixEmuE110En();
if (errataFixEmuE110En) {
#endif
CORE_CRITICAL_SECTION(ramWFI(); )
#if defined(ERRATA_FIX_EMU_E110_ENABLE)
} else {
__WFI();
}
#endif
#elif defined(ERRATA_FIX_EMU_E220_DECBOD_ENABLE)
// Apply errata fix if voltage scaling in EM2 is used.
if ((EMU->CTRL & EMU_CTRL_EM23VSCALEAUTOWSEN) != 0U) {
CORE_CRITICAL_SECTION(ramWFI(); )
} else {
__WFI();
}
#else
__WFI();
#endif
EMU_EM23PostsleepHook();
#if defined(ERRATA_FIX_DCDC_FETCNT_SET_ENABLE)
dcdcFetCntSet(false);
#endif
/* Fix for errata EMU_E107 - restore state of non-WIC interrupt enable flags. */
#if defined(ERRATA_FIX_EMU_E107_ENABLE)
if (errataFixEmuE107En) {
NVIC->ISER[0] = nonWicIntEn[0];
#if (NON_WIC_INT_MASK_1 != (~(0x0U)))
NVIC->ISER[1] = nonWicIntEn[1];
#endif
}
#endif
#if (_SILICON_LABS_32B_SERIES < 2)
/* Restore oscillators/clocks and voltage scaling if supported. */
if (restore) {
emState(emState_Restore);
}
#if defined(_EMU_CTRL_EM23VSCALE_MASK) && defined(EMU_CTRL_EM23VSCALEAUTOWSEN)
else {
vScaleAfterWakeup();
}
#if defined(ERRATA_FIX_EMU_E220_DECBOD_ENABLE)
/* Third part of EMU_E220 DECBOD Errata fix. Calibration needs to be enabled
* after voltage scaling completes. */
EMU_PORBOD &= ~(EMU_PORBOD_GMC_CALIB_DISABLE);
#endif
#endif
#endif
#if defined(_SILICON_LABS_32B_SERIES_2_CONFIG_2) || defined(_SILICON_LABS_32B_SERIES_2_CONFIG_7)
if (restore) {
dpllState(dpllState_Restore);
}
#endif
if (!restore) {
/* If not restoring, and the original clock was not HFRCO, the CMSIS */
/* core clock variable must be updated since HF clock has changed */
/* to HFRCO. */
SystemCoreClockUpdate();
}
}
/***************************************************************************//**
* @brief
* Save the 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)
{
#if (_SILICON_LABS_32B_SERIES < 2)
emState(emState_Save);
#endif
#if defined(_SILICON_LABS_32B_SERIES_2_CONFIG_2) || defined(_SILICON_LABS_32B_SERIES_2_CONFIG_7)
dpllState(dpllState_Save);
#endif
}
/***************************************************************************//**
* @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)
{
#if (_SILICON_LABS_32B_SERIES < 2)
emState(emState_Restore);
#endif
#if defined(_SILICON_LABS_32B_SERIES_2_CONFIG_2) || defined(_SILICON_LABS_32B_SERIES_2_CONFIG_7)
dpllState(dpllState_Restore);
#endif
}
#if defined(_EMU_EM4CONF_MASK) || defined(_EMU_EM4CTRL_MASK)
/***************************************************************************//**
* @brief
* Update the 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)
/* Initialization 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
| _EMU_EM4CONF_BUBODRSTDIS_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)
| (em4Init->buBodRstDis << _EMU_EM4CONF_BUBODRSTDIS_SHIFT);
/* Apply configuration. Note that lock can be set after this stage. */
EMU->EM4CONF = em4conf;
#elif defined(_EMU_EM4CTRL_EM4STATE_MASK)
/* Initialization for platforms with EMU->EM4CTRL register and EM4H and EM4S. */
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 : 0U)
| (em4Init->retainLfrco ? EMU_EM4CTRL_RETAINLFRCO : 0U)
| (em4Init->retainUlfrco ? EMU_EM4CTRL_RETAINULFRCO : 0U)
| (em4Init->em4State == emuEM4Hibernate
? EMU_EM4CTRL_EM4STATE_EM4H : 0U)
| ((uint32_t)em4Init->pinRetentionMode);
EMU->EM4CTRL = em4ctrl;
#elif defined(_EMU_EM4CTRL_MASK)
EMU->EM4CTRL = (EMU->EM4CTRL & ~_EMU_EM4CTRL_EM4IORETMODE_MASK)
| (uint32_t)em4Init->pinRetentionMode;
#endif
#if defined(_EMU_CTRL_EM4HVSCALE_MASK)
EMU->CTRL = (EMU->CTRL & ~_EMU_CTRL_EM4HVSCALE_MASK)
| ((uint32_t)em4Init->vScaleEM4HVoltage << _EMU_CTRL_EM4HVSCALE_SHIFT);
#endif
}
#endif
/***************************************************************************//**
* @brief
* Energy mode 4 pre-sleep hook function.
*
* @details
* This function is called by @ref EMU_EnterEM4() just prior to the sequence
* of writes to put the device in EM4. The function implementation does not
* perform anything, but it is SL_WEAK so that it can be re-implemented in
* application code if actions are needed.
******************************************************************************/
SL_WEAK void EMU_EM4PresleepHook(void)
{
}
/***************************************************************************//**
* @brief
* EFP's Energy mode 4 pre-sleep hook function.
*
* @details
* This function is similar to @ref EMU_EM4PresleepHook() but is reserved for
* EFP usage.
*
* @note
* The function is primarily meant to be used in systems with EFP circuitry.
* (EFP = Energy Friendly Pmic (PMIC = Power Management IC)).
* In such systems there is a need to drive certain signals to EFP pins to
* notify about energy mode transitions.
******************************************************************************/
SL_WEAK void EMU_EFPEM4PresleepHook(void)
{
}
/***************************************************************************//**
* @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)
{
#if defined(SL_CATALOG_METRIC_EM4_WAKE_PRESENT)
sli_metric_em4_wake_init();
#endif
int i;
#if defined(_EMU_EM4CTRL_EM4ENTRY_SHIFT)
uint32_t em4seq2 = (EMU->EM4CTRL & ~_EMU_EM4CTRL_EM4ENTRY_MASK)
| (2U << _EMU_EM4CTRL_EM4ENTRY_SHIFT);
uint32_t em4seq3 = (EMU->EM4CTRL & ~_EMU_EM4CTRL_EM4ENTRY_MASK)
| (3U << _EMU_EM4CTRL_EM4ENTRY_SHIFT);
#else
uint32_t em4seq2 = (EMU->CTRL & ~_EMU_CTRL_EM4CTRL_MASK)
| (2U << _EMU_CTRL_EM4CTRL_SHIFT);
uint32_t em4seq3 = (EMU->CTRL & ~_EMU_CTRL_EM4CTRL_MASK)
| (3U << _EMU_CTRL_EM4CTRL_SHIFT);
#endif
/* Make sure that the register write lock is disabled. */
EMU_Unlock();
#if defined(_SILICON_LABS_GECKO_INTERNAL_SDID_80)
/* The DCDC is not supported in EM4S. EFM32xG1 and EFR32xG1 devices should
* switch to bypass mode before entering EM4S. Other devices handle this
* automatically at the hardware level. */
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) {
EMU_DCDCModeSet(emuDcdcMode_Bypass);
}
}
#endif
#if defined(_DCDC_IF_EM4ERR_MASK)
/* Make sure DCDC Mode is not modified, from this point forward,
* by another code section. */
CORE_DECLARE_IRQ_STATE;
CORE_ENTER_CRITICAL();
/* Workaround for bug that may cause a Hard Fault on EM4 entry */
CMU_CLOCK_SELECT_SET(SYSCLK, FSRCO);
/* The buck DC-DC is available in all energy modes except for EM4.
* The DC-DC converter must first be turned off and switched over to bypass mode. */
#if defined(EMU_SERIES1_DCDC_BUCK_PRESENT) \
|| (defined(EMU_SERIES2_DCDC_BUCK_PRESENT) \
|| defined(EMU_SERIES2_DCDC_BOOST_PRESENT))
EMU_DCDCModeSet(emuDcdcMode_Bypass);
#endif
#endif
#if defined(_EMU_EM4CTRL_MASK) && defined(ERRATA_FIX_EMU_E208_ENABLE)
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 + 0x190UL) = 0x0000ADE8UL;
*(volatile uint32_t *)(EMU_BASE + 0x198UL) |= (0x1UL << 7);
*(volatile uint32_t *)(EMU_BASE + 0x88UL) |= (0x1UL << 8);
}
#endif
#if defined(ERRATA_FIX_EMU_E108_ENABLE)
/* 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_ENABLE)
dcdcFetCntSet(true);
#endif
#if defined(ERRATA_FIX_DCDC_LNHS_BLOCK_ENABLE)
dcdcHsFixLnBlock();
#endif
#if defined(ERRATA_FIX_EM4S_DELAY_ENTRY)
/* Fix for errata where firmware must clear ANASW and delay EM4S entry by 30 us. */
if ((EMU->EM4CTRL & _EMU_EM4CTRL_EM4STATE_MASK) == EMU_EM4CTRL_EM4STATE_EM4S) {
if ((EMU->PWRCTRL & _EMU_PWRCTRL_ANASW_MASK) == EMU_PWRCTRL_ANASW_DVDD) {
BUS_RegMaskedClear(&EMU->PWRCTRL, _EMU_PWRCTRL_ANASW_MASK);
/* Switch to 1 MHz HFRCO. This delays enough to meet the 30 us requirement
* before entering EM4. */
uint32_t freqCal = (DEVINFO->HFRCOCAL0 & ~_CMU_HFRCOCTRL_CLKDIV_MASK)
| CMU_HFRCOCTRL_CLKDIV_DIV4;
while ((CMU->SYNCBUSY & CMU_SYNCBUSY_HFRCOBSY) != 0UL) {
}
CMU->HFRCOCTRL = freqCal;
CMU->OSCENCMD = CMU_OSCENCMD_HFRCOEN;
while ((CMU->STATUS & CMU_STATUS_HFRCORDY) == 0U) {
}
CMU->HFCLKSEL = CMU_HFCLKSEL_HF_HFRCO;
__NOP();
}
}
#endif
EMU_EM4PresleepHook();
EMU_EFPEM4PresleepHook();
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(_DCDC_IF_EM4ERR_MASK)
EFM_ASSERT((DCDC->IF & _DCDC_IF_EM4ERR_MASK) == 0);
CORE_EXIT_CRITICAL();
#endif
}
/***************************************************************************//**
* @brief
* Enter energy mode 4 (EM4).
*
* @details
* This function waits after the EM4 entry request to make sure the CPU
* is properly shutdown or the EM4 entry failed.
*
* @note
* Only a power on reset or external reset pin can wake the device from EM4.
******************************************************************************/
void EMU_EnterEM4Wait(void)
{
EMU_EnterEM4();
// The EM4 entry waiting loop should take 4 cycles by loop minimally (Compiler dependent).
// We would then wait for (EMU_EM4_ENTRY_WAIT_LOOPS * 4) clock cycles.
for (uint16_t i = 0; i < EMU_EM4_ENTRY_WAIT_LOOPS; i++) {
__NOP();
}
}
#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)
{
#if defined(_EMU_EM4CTRL_EM4STATE_MASK)
BUS_RegBitWrite(&EMU->EM4CTRL, _EMU_EM4CTRL_EM4STATE_SHIFT, 1);
#endif
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)
{
#if defined(_EMU_EM4CTRL_EM4STATE_MASK)
BUS_RegBitWrite(&EMU->EM4CTRL, _EMU_EM4CTRL_EM4STATE_SHIFT, 0);
#endif
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, and so on. Memory block 0 cannot
* be disabled. See the reference manual for available
* memory blocks for a device.
*
* @note
* Only a POR reset can power up the specified memory block(s) after power down.
*
* @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).
*
* This is an example 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 the 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 reset can power up the specified memory block(s) after power down
* on a series 0 device. The specified memory block(s) will stay off
* until a call to EMU_RamPowerUp() is done on series 1/2.
*
* @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, 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;
(void) start;
if (end == 0U) {
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, 0x20004000UL) << 0; // Block 1, 16 kB
mask |= ADDRESS_NOT_IN_BLOCK(start, 0x20008000UL) << 1; // Block 2, 16 kB
mask |= ADDRESS_NOT_IN_BLOCK(start, 0x2000C000UL) << 2; // Block 3, 16 kB
mask |= ADDRESS_NOT_IN_BLOCK(start, 0x20010000UL) << 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, 0x20001000UL) << 0; // Block 1, 4 kB
mask |= ADDRESS_NOT_IN_BLOCK(start, 0x20002000UL) << 1; // Block 2, 8 kB
mask |= ADDRESS_NOT_IN_BLOCK(start, 0x20004000UL) << 2; // Block 3, 8 kB
mask |= ADDRESS_NOT_IN_BLOCK(start, 0x20006000UL) << 3; // Block 4, 7 kB
#elif defined(_SILICON_LABS_32B_SERIES_2_CONFIG_2)
// Lynx has 2 blocks. We do no shut off block 0 because we dont want to disable all RAM0
mask |= ADDRESS_NOT_IN_BLOCK(start, 0x20006000UL) << 1; // Block 1, 8 kB
#elif defined(_SILICON_LABS_32B_SERIES_2_CONFIG_7)
// Leopard has 3 blocks. We do no shut off block 0 because we dont want to disable all RAM0
mask |= ADDRESS_NOT_IN_BLOCK(start, 0x20006000UL) << 1; // Block 1, 8 kB
mask |= ADDRESS_NOT_IN_BLOCK(start, 0x20008000UL) << 2; // Block 2, 32 kB
#elif defined(_SILICON_LABS_32B_SERIES_2_CONFIG_8)
// These platforms have equally-sized RAM blocks and block 0 can be powered down but should not.
// This condition happens when the block 0 disable bit flag is available in the retention control register.
for (unsigned i = 1; i < RAM0_BLOCKS; i++) {
mask |= ADDRESS_NOT_IN_BLOCK(start, RAM_MEM_BASE + (i * RAM0_BLOCK_SIZE)) << (i);
}
#elif defined(RAM0_BLOCKS)
// These platforms have equally-sized RAM blocks and block 0 cannot be powered down.
for (unsigned i = 1; i < RAM0_BLOCKS; i++) {
mask |= ADDRESS_NOT_IN_BLOCK(start, RAM_MEM_BASE + (i * RAM0_BLOCK_SIZE)) << (i - 1U);
}
#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;
#elif defined(_SILICON_LABS_32B_SERIES_2)
#if defined(CMU_CLKEN0_SYSCFG)
CMU->CLKEN0_SET = CMU_CLKEN0_SYSCFG;
#endif
SYSCFG_maskDmem0RetnCtrl(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 (unsigned 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 (unsigned i = 0; i < RAM2_BLOCKS; i++) {
mask |= ADDRESS_NOT_IN_BLOCK(start, RAM2_MEM_BASE + (i * RAM2_BLOCK_SIZE)) << i;
}
}
EMU->RAM2CTRL |= mask;
#endif
}
/***************************************************************************//**
* @brief
* Power up all available RAM memory blocks.
*
* @details
* This function will power up all the RAM blocks on a device, this means
* that the RAM blocks are retained in EM2/EM3. Note that this functionality
* is not supported on Series 0 devices. Only a reset will power up the RAM
* blocks on a series 0 device.
******************************************************************************/
void EMU_RamPowerUp(void)
{
#if defined(_EMU_RAM0CTRL_MASK)
EMU->RAM0CTRL = 0x0UL;
#endif
#if defined(_EMU_RAM1CTRL_MASK)
EMU->RAM1CTRL = 0x0UL;
#endif
#if defined(_EMU_RAM2CTRL_MASK)
EMU->RAM2CTRL = 0x0UL;
#endif
#if defined(_SYSCFG_DMEM0RETNCTRL_MASK)
#if defined(CMU_CLKEN0_SYSCFG)
CMU->CLKEN0_SET = CMU_CLKEN0_SYSCFG;
#endif
SYSCFG_zeroDmem0RetnCtrl();
#endif
}
#if defined(_EMU_EM23PERNORETAINCTRL_MASK)
/***************************************************************************//**
* @brief
* Set EM2 3 peripheral retention control.
*
* @param[in] periMask
* A peripheral select mask. Use | operator to select multiple peripherals, 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 = (uint32_t)periMask
& (uint32_t)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)
{
#if (_SILICON_LABS_32B_SERIES < 2)
emState(emState_Save);
#endif
}
#if defined(_SILICON_LABS_32B_SERIES_2) && defined(EMU_VSCALE_EM01_PRESENT)
/***************************************************************************//**
* @brief
* Energy mode 01 voltage scaling hook function.
*
* @param[in] voltage
* Voltage scaling level requested.
*
* @details
* This function is called by EMU_VScaleEM01 to let EFP know that voltage scaling
* is requested.
******************************************************************************/
SL_WEAK void EMU_EFPEM01VScale(EMU_VScaleEM01_TypeDef voltage)
{
(void)voltage;
}
#endif
#if defined(EMU_VSCALE_EM01_PRESENT)
/***************************************************************************//**
* @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.
******************************************************************************/
void EMU_VScaleEM01ByClock(uint32_t clockFrequency, bool wait)
{
uint32_t hfSrcClockFrequency;
#if defined(_SILICON_LABS_32B_SERIES_1)
if (EMU_LDOStatusGet() == false) {
/* Skip voltage scaling if the LDO regulator is turned off. */
return;
}
#endif
/* VSCALE frequency is HFSRCCLK. */
if (clockFrequency == 0U) {
#if defined(_SILICON_LABS_32B_SERIES_2)
hfSrcClockFrequency = SystemSYSCLKGet();
#else
uint32_t hfPresc = 1U + ((CMU->HFPRESC & _CMU_HFPRESC_PRESC_MASK)
>> _CMU_HFPRESC_PRESC_SHIFT);
hfSrcClockFrequency = SystemHFClockGet() * hfPresc;
#endif
} 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_VSCALE_EM01_PRESENT)
/***************************************************************************//**
* @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 complete.
*
* @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 vScaleEM01LowPowerVoltageEnable set from @ref
* EMU_EM01Init().
******************************************************************************/
void EMU_VScaleEM01(EMU_VScaleEM01_TypeDef voltage, bool wait)
{
uint32_t hfFreq;
uint32_t hfSrcClockFrequency;
#if defined(_SILICON_LABS_32B_SERIES_1)
if (EMU_LDOStatusGet() == false) {
/* Skip voltage scaling if the LDO regulator is turned off. */
return;
}
#endif
if (EMU_VScaleGet() == voltage) {
/* Voltage is already at the correct level. */
return;
}
#if defined(_SILICON_LABS_32B_SERIES_2)
(void)wait;
CORE_DECLARE_IRQ_STATE;
hfFreq = SystemSYSCLKGet();
hfSrcClockFrequency = hfFreq;
if (voltage == emuVScaleEM01_LowPower) {
EFM_ASSERT(hfSrcClockFrequency <= CMU_VSCALEEM01_LOWPOWER_VOLTAGE_CLOCK_MAX);
/* Update wait states before scaling down voltage. */
CMU_UpdateWaitStates(hfFreq, VSCALE_EM01_LOW_POWER);
}
CORE_ENTER_CRITICAL();
EMU->IF_CLR = EMU_IF_VSCALEDONE;
EMU_EFPEM01VScale(voltage);
EMU->CMD = vScaleEM01Cmd(voltage);
// Note that VSCALEDONE interrupt flag must be used instead of VSCALEBUSY
// because hardware does not set the VSCALEBUSY flag immediately.
while (((EMU->IF & EMU_IF_VSCALEDONE) == 0U)
&& ((EMU->STATUS & EMU_STATUS_VSCALEFAILED) == 0U)) {
EFM_ASSERT((EMU->STATUS & EMU_STATUS_VSCALEFAILED) == 0U);
// Wait for VSCALE completion.
// SRAM accesses will fault the core while scaling.
}
CORE_EXIT_CRITICAL();
#else
uint32_t hfPresc = 1U + ((CMU->HFPRESC & _CMU_HFPRESC_PRESC_MASK)
>> _CMU_HFPRESC_PRESC_SHIFT);
hfFreq = SystemHFClockGet();
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, VSCALE_EM01_LOW_POWER);
}
EMU->CMD = vScaleEM01Cmd(voltage);
if (wait) {
EMU_VScaleWait();
}
#endif
if (voltage == emuVScaleEM01_HighPerformance) {
/* Update wait states after scaling up voltage. */
CMU_UpdateWaitStates(hfFreq, VSCALE_EM01_HIGH_PERFORMANCE);
}
}
#endif
#if defined(BU_PRESENT) && defined(_SILICON_LABS_32B_SERIES_0)
/***************************************************************************//**
* @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 the 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 the backup domain inactive mode configuration. */
reg = EMU->BUINACT & ~(_EMU_BUINACT_PWRCON_MASK);
reg |= (bupdInit->inactivePower);
EMU->BUINACT = reg;
/* Set the backup domain active mode configuration. */
reg = EMU->BUACT & ~(_EMU_BUACT_PWRCON_MASK);
reg |= (bupdInit->activePower);
EMU->BUACT = reg;
/* Set the power control configuration */
reg = EMU->BUCTRL & ~(_EMU_BUCTRL_PROBE_MASK
| _EMU_BUCTRL_BODCAL_MASK
| _EMU_BUCTRL_STATEN_MASK
| _EMU_BUCTRL_EN_MASK);
/* Note the use of ->enable to 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 the 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 the 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
#if defined(BU_PRESENT) && defined(_SILICON_LABS_32B_SERIES_1)
/***************************************************************************//**
* @brief
* Configure Backup Power Domain settings.
*
* @param[in] buInit
* Backup power domain initialization structure.
******************************************************************************/
void EMU_BUInit(const EMU_BUInit_TypeDef *buInit)
{
uint32_t reg = 0;
/* Set the backup power configuration. */
reg |= (buInit->disMaxComp << _EMU_BUCTRL_DISMAXCOMP_SHIFT);
reg |= (uint32_t)(buInit->inactivePwrCon);
reg |= (uint32_t)(buInit->activePwrCon);
reg |= (uint32_t)(buInit->pwrRes);
reg |= (uint32_t)(buInit->voutRes);
reg |= (buInit->buVinProbeEn << _EMU_BUCTRL_BUVINPROBEEN_SHIFT);
reg |= (buInit->staEn << _EMU_BUCTRL_STATEN_SHIFT);
reg |= (buInit->enable << _EMU_BUCTRL_EN_SHIFT);
EMU->BUCTRL = reg;
}
#endif
#if defined(_EMU_BUCTRL_DISMAXCOMP_MASK)
/***************************************************************************//**
* @brief
* Disable Main Backup Power Domain comparator.
*
* @param[in] disableMainBuComparator
* True to disable main BU comparator.
******************************************************************************/
void EMU_BUDisMaxCompSet(bool disableMainBuComparator)
{
uint32_t reg;
reg = EMU->BUCTRL & ~(_EMU_BUCTRL_DISMAXCOMP_MASK);
reg |= (disableMainBuComparator << _EMU_BUCTRL_DISMAXCOMP_SHIFT);
EMU->BUCTRL = reg;
}
#endif
#if defined(_EMU_BUCTRL_BUINACTPWRCON_MASK)
/***************************************************************************//**
* @brief
* Configure power connection configuration when not in Backup mode.
*
* @param[in] inactPwrCon
* Inactive power configuration.
******************************************************************************/
void EMU_BUBuInactPwrConSet(EMU_BUBuInactPwrCon_TypeDef inactPwrCon)
{
uint32_t reg;
reg = EMU->BUCTRL & ~(_EMU_BUCTRL_BUINACTPWRCON_MASK);
reg |= (uint32_t)(inactPwrCon);
EMU->BUCTRL = reg;
}
#endif
#if defined(_EMU_BUCTRL_BUACTPWRCON_MASK)
/***************************************************************************//**
* @brief
* Configure power connection configuration when in Backup mode.
*
* @param[in] actPwrCon
* Active power configuration.
******************************************************************************/
void EMU_BUBuActPwrConSet(EMU_BUBuActPwrCon_TypeDef actPwrCon)
{
uint32_t reg;
reg = EMU->BUCTRL & ~(_EMU_BUCTRL_BUACTPWRCON_MASK);
reg |= (uint32_t)(actPwrCon);
EMU->BUCTRL = reg;
}
#endif
#if defined(_EMU_BUCTRL_PWRRES_MASK)
/***************************************************************************//**
* @brief
* Power domain resistor selection.
*
* @param[in] pwrRes
* Resistor selection.
******************************************************************************/
void EMU_BUPwrResSet(EMU_BUPwrRes_TypeDef pwrRes)
{
uint32_t reg;
reg = EMU->BUCTRL & ~(_EMU_BUCTRL_PWRRES_MASK);
reg |= (uint32_t)(pwrRes);
EMU->BUCTRL = reg;
}
#endif
#if defined(_EMU_BUCTRL_VOUTRES_MASK)
/***************************************************************************//**
* @brief
* B_VOUT resistor select.
*
* @param[in] resistorSel
* Resistor selection.
******************************************************************************/
void EMU_BUVoutResSet(EMU_BUVoutRes_TypeDef resistorSel)
{
uint32_t reg;
reg = EMU->BUCTRL & ~(_EMU_BUCTRL_VOUTRES_MASK);
reg |= (uint32_t)(resistorSel);
EMU->BUCTRL = reg;
}
#endif
#if defined(_EMU_BUCTRL_BUVINPROBEEN_MASK)
/***************************************************************************//**
* @brief
* Enable BU_VIN probing
*
* @param[in] enable
* True to enable BU_VIN probing. False to disable.
******************************************************************************/
void EMU_BUBuVinProbeEnSet(bool enable)
{
uint32_t reg;
reg = EMU->BUCTRL & ~(_EMU_BUCTRL_BUVINPROBEEN_MASK);
reg |= (enable << _EMU_BUCTRL_BUVINPROBEEN_SHIFT);
EMU->BUCTRL = reg;
}
#endif
#if defined(_EMU_BUCTRL_STATEN_MASK)
/***************************************************************************//**
* @brief
* Enable backup mode status export.
*
* @param[in] enable
* True to enable status export. False to disable.
******************************************************************************/
void EMU_BUStatEnSet(bool enable)
{
uint32_t reg;
reg = EMU->BUCTRL & ~(_EMU_BUCTRL_STATEN_MASK);
reg |= (enable << _EMU_BUCTRL_STATEN_SHIFT);
EMU->BUCTRL = reg;
}
#endif
#if defined(_EMU_BUCTRL_EN_MASK)
/***************************************************************************//**
* @brief
* Enable backup mode.
*
* @param[in] enable
* True to enable backup mode. False to disable.
******************************************************************************/
void EMU_BUEnableSet(bool enable)
{
uint32_t reg;
reg = EMU->BUCTRL & ~(_EMU_BUCTRL_EN_MASK);
reg |= (enable << _EMU_BUCTRL_EN_SHIFT);
EMU->BUCTRL = reg;
}
#endif
#if defined(EMU_SERIES1_DCDC_BUCK_PRESENT)
/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
/* Internal DCDC trim modes. */
typedef enum {
dcdcTrimMode_EM234H_LP = 0,
#if defined(_EMU_DCDCLPEM01CFG_LPCMPBIASEM01_MASK)
dcdcTrimMode_EM01_LP,
#endif
dcdcTrimMode_LN,
} dcdcTrimMode_TypeDef;
/** @endcond */
/***************************************************************************//**
* @brief
* Load DCDC calibration constants from the DI page. A constant means that 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 calibration 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)
{
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 != 0U) {
/* 7 MHz is recommended for LNFORCECCM = 1. */
EMU_DCDCLnRcoBandSet(emuDcdcLnRcoBand_7MHz);
} else {
/* 3 MHz 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;
/* 80 mA as recommended peak in Application Note AN0948.
The peak current is the average current plus 50% of the current ripple.
Hence, a 14 mA average current is recommended in LP mode. Since LP PFETCNT is also
a constant, 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 the LN current limiter threshold from the nominal user input current and
LN PFETCNT as described in the register description for
EMU_DCDCMISCCTRL_LNCLIMILIMSEL. */
lncLimSel = ((((uint32_t)dcdcMaxCurrent_mA + 40U)
+ (((uint32_t)dcdcMaxCurrent_mA + 40U) / 2U))
/ (5U * (pFetCnt + 1U)))
- 1U;
/* 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) == 0x0U);
EFM_ASSERT((lpcLimSel & ~_EMU_DCDCMISCCTRL_LPCLIMILIMSEL_MASK) == 0x0U);
EMU->DCDCMISCCTRL = (EMU->DCDCMISCCTRL & ~(_EMU_DCDCMISCCTRL_LNCLIMILIMSEL_MASK
| _EMU_DCDCMISCCTRL_LPCLIMILIMSEL_MASK))
| (lncLimSel | lpcLimSel);
/* Compute the reverse current limit threshold for the zero detector from the user input
maximum reverse current and LN PFETCNT as described in the register description
for EMU_DCDCZDETCTRL_ZDETILIMSEL. */
if (dcdcReverseCurrentControl >= 0) {
/* If dcdcReverseCurrentControl < 0, EMU_DCDCZDETCTRL_ZDETILIMSEL is "don't care". */
maxReverseCurrent_mA = (uint16_t)dcdcReverseCurrentControl;
zdetLimSel = ((((uint32_t)maxReverseCurrent_mA + 40U)
+ (((uint32_t)maxReverseCurrent_mA + 40U) / 2U))
/ ((2U * (pFetCnt + 1U)) + ((pFetCnt + 1U) / 2U)));
/* 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) == 0x0U);
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 the 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 = (uint16_t)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;
#if !defined(_SILICON_LABS_GECKO_INTERNAL_SDID_80)
(void)lpAttenuation;
#endif
/* Get calibration data revision. */
#if defined(_SILICON_LABS_GECKO_INTERNAL_SDID_80)
uint8_t devinfoRev = SYSTEM_GetDevinfoRev();
/* Load LPATT indexed calibration data. */
if (devinfoRev < 4) {
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
#endif
{
/* 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) != 0U) {
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) != 0U) {
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 look up 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 = ((uint32_t)lpcmpBias << 8) | (lpAttenuation ? 1U : 0U);
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;
}
/***************************************************************************//**
* @brief
* Configure the DCDC regulator.
*
* @note
* Do not call this function if the power circuit is configured for NODCDC as
* described in the Power Configurations section of the Reference Manual.
* Instead, call @ref EMU_DCDCPowerOff().
*
* @param[in] dcdcInit
* The 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 the 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
the desired power configuration was set. */
if ((EMU->PWRCFG & _EMU_PWRCFG_PWRCFG_MASK) != EMU_PWRCFG_PWRCFG_DCDCTODVDD) {
/* If this assert triggers unexpectedly, 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. */
(void)dcdcConstCalibrationLoad();
/* Check current parameters */
EFM_ASSERT(dcdcInit->maxCurrent_mA <= 200U);
EFM_ASSERT(dcdcInit->em01LoadCurrent_mA <= dcdcInit->maxCurrent_mA);
EFM_ASSERT(dcdcInit->reverseCurrentControl <= 200);
if (dcdcInit->dcdcMode == emuDcdcMode_LowNoise) {
/* DCDC low-noise supports max 200 mA. */
EFM_ASSERT(dcdcInit->em01LoadCurrent_mA <= 200U);
}
#if (_SILICON_LABS_GECKO_INTERNAL_SDID != 80)
else if (dcdcInit->dcdcMode == emuDcdcMode_LowPower) {
/* Up to 10 mA is supported for EM01-LP mode. */
EFM_ASSERT(dcdcInit->em01LoadCurrent_mA <= 10U);
}
#endif
else {
/* No need to check the EM01 load limit. */
}
/* EM2/3/4 current above 10 mA is not supported. */
EFM_ASSERT(dcdcInit->em234LoadCurrent_uA <= 10000U);
if (dcdcInit->em234LoadCurrent_uA < 75U) {
lpCmpBiasSelEM234H = 0;
} else if (dcdcInit->em234LoadCurrent_uA < 500U) {
lpCmpBiasSelEM234H = 1U << _GENERIC_DCDCMISCCTRL_LPCMPBIASEM234H_SHIFT;
} else if (dcdcInit->em234LoadCurrent_uA < 2500U) {
lpCmpBiasSelEM234H = 2U << _GENERIC_DCDCMISCCTRL_LPCMPBIASEM234H_SHIFT;
} else {
lpCmpBiasSelEM234H = 3U << _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 : 0U));
#if defined(_EMU_DCDCLPEM01CFG_LPCMPBIASEM01_MASK)
/* Only 10 mA 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 */
dcdcEm01LoadCurrent_mA = dcdcInit->em01LoadCurrent_mA;
dcdcValidatedConfigSet();
/* 3. Updated static currents and limits user data.
Limiters are updated in @ref EMU_DCDCOptimizeSlice(). */
userCurrentLimitsSet(dcdcInit->maxCurrent_mA,
dcdcInit->reverseCurrentControl);
/* 4. Optimize LN slice based on the 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
@ref 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 the 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
== emuDcdcAnaPeripheralPower_DCDC ? 1U : 0U);
#endif
return true;
}
/***************************************************************************//**
* @brief
* Set DCDC regulator operating mode.
*
* @param[in] dcdcMode
* DCDC mode.
******************************************************************************/
void EMU_DCDCModeSet(EMU_DcdcMode_TypeDef dcdcMode)
{
bool dcdcLocked;
uint32_t currentDcdcMode;
dcdcLocked = (EMU->PWRLOCK == EMU_PWRLOCK_LOCKKEY_LOCKED);
EMU_PowerUnlock();
/* Wait for any previous write sync to complete and read DCDC mode. */
while ((EMU->DCDCSYNC & EMU_DCDCSYNC_DCDCCTRLBUSY) != 0U) {
}
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 the 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;
}
#endif // (_SILICON_LABS_GECKO_INTERNAL_SDID_80)
#if defined(_SILICON_LABS_GECKO_INTERNAL_SDID_84)
/* 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. Switch to LOWPOWER mode once LOWNOISE startup is complete. */
EMU_IntClear(EMU_IFC_DCDCLNRUNNING);
while ((EMU->DCDCSYNC & EMU_DCDCSYNC_DCDCCTRLBUSY) != 0U) {
}
EMU->DCDCCTRL = (EMU->DCDCCTRL & ~_EMU_DCDCCTRL_DCDCMODE_MASK) | EMU_DCDCCTRL_DCDCMODE_LOWNOISE;
while ((EMU_IntGet() & EMU_IF_DCDCLNRUNNING) == 0U) {
}
}
#endif // (_SILICON_LABS_GECKO_INTERNAL_SDID_84)
/* Set user-requested mode. */
while ((EMU->DCDCSYNC & EMU_DCDCSYNC_DCDCCTRLBUSY) != 0UL) {
}
EMU->DCDCCTRL = (EMU->DCDCCTRL & ~_EMU_DCDCCTRL_DCDCMODE_MASK)
| (uint32_t)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) != 0U) {
}
BUS_RegBitWrite(&EMU->DCDCCLIMCTRL,
_EMU_DCDCCLIMCTRL_BYPLIMEN_SHIFT,
dcdcMode == emuDcdcMode_Bypass ? 0U : 1U);
if (dcdcLocked) {
EMU_PowerLock();
}
}
#if defined(EMU_DCDCCTRL_DCDCMODEEM23)
/***************************************************************************//**
* @brief
* Set DCDC Mode EM23 operating mode.
*
* @param[in] dcdcModeEM23
* DCDC mode EM23.
******************************************************************************/
void EMU_DCDCModeEM23Set(EMU_DcdcModeEM23_TypeDef dcdcModeEM23)
{
bool dcdcLocked;
dcdcLocked = (EMU->PWRLOCK == EMU_PWRLOCK_LOCKKEY_LOCKED);
EMU_PowerUnlock();
/* Set user-requested mode. */
while ((EMU->DCDCSYNC & EMU_DCDCSYNC_DCDCCTRLBUSY) != 0UL) {
}
EMU->DCDCCTRL = (EMU->DCDCCTRL & ~_EMU_DCDCCTRL_DCDCMODEEM23_MASK)
| (uint32_t)dcdcModeEM23;
if (dcdcLocked) {
EMU_PowerLock();
}
}
#endif
/***************************************************************************//**
* @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, use @ref 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) != 0U) {
}
EMU->DCDCCTRL = EMU_DCDCCTRL_DCDCMODE_OFF;
dcdcModeSet = (EMU->DCDCCTRL == EMU_DCDCCTRL_DCDCMODE_OFF);
EFM_ASSERT(dcdcModeSet);
return dcdcModeSet;
}
/***************************************************************************//**
* @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)((uint32_t)
(EMU->DCDCCTRL & _EMU_DCDCCTRL_DCDCMODE_MASK));
EMU_DcdcLnRcoBand_TypeDef rcoBand
= (EMU_DcdcLnRcoBand_TypeDef)((uint32_t)
((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) != 0U) {
}
while ((EMU_IntGet() & EMU_IF_DCDCINBYPASS) == 0U) {
}
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
* Set the DCDC output voltage.
*
* @note
* The DCDC is not characterized for the entire valid output voltage range.
* For that reason an upper limit of 3.0V output voltage is enforced.
*
* @param[in] mV
* Target DCDC output voltage in mV.
*
* @param[in] setLpVoltage
* Update LP voltage
*
* @param[in] setLnVoltage
* Update LN voltage
*
* @return
* True if the mV parameter is valid.
******************************************************************************/
bool EMU_DCDCOutputVoltageSet(uint32_t mV,
bool setLpVoltage,
bool setLnVoltage)
{
#if defined(_DEVINFO_DCDCLNVCTRL0_3V0LNATT1_MASK)
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 data sheet. */
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 > 1800U;
if (attenuationSet) {
mVlow = 1800;
mVhigh = 3000;
mVdiff = mVhigh - mVlow;
} else {
mVlow = 1200;
mVhigh = 1800;
mVdiff = mVhigh - mVlow;
}
/* Get 2-point calibration 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] = (uint8_t)
((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] = (uint8_t)
((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 : 0U);
}
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 maximum of trims for EM01 and EM234H. */
vrefVal[dcdcTrimMode_EM234H_LP] = SL_MAX(vrefVal[dcdcTrimMode_EM234H_LP], vrefVal[dcdcTrimMode_EM01_LP]);
#endif
/* Don't exceed the maximum 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 : 0U);
}
#endif
return true;
}
/***************************************************************************//**
* @brief
* Optimize the DCDC slice count based on the estimated average load current
* in EM0.
*
* @param[in] em0LoadCurrentmA
* Estimated average EM0 load current in mA.
******************************************************************************/
void EMU_DCDCOptimizeSlice(uint32_t em0LoadCurrentmA)
{
uint32_t sliceCount = 0;
uint32_t rcoBand = (EMU->DCDCLNFREQCTRL & _EMU_DCDCLNFREQCTRL_RCOBAND_MASK)
>> _EMU_DCDCLNFREQCTRL_RCOBAND_SHIFT;
/* Set the recommended slice count. */
if (((EMU->DCDCMISCCTRL & _EMU_DCDCMISCCTRL_LNFORCECCM_MASK) != 0U)
&& (rcoBand >= (uint32_t)emuDcdcLnRcoBand_5MHz)) {
if (em0LoadCurrentmA < 20U) {
sliceCount = 4;
} else if ((em0LoadCurrentmA >= 20U) && (em0LoadCurrentmA < 40U)) {
sliceCount = 8;
} else {
sliceCount = 16;
}
} else if (((EMU->DCDCMISCCTRL & _EMU_DCDCMISCCTRL_LNFORCECCM_MASK) == 0U)
&& (rcoBand <= (uint32_t)emuDcdcLnRcoBand_4MHz)) {
if (em0LoadCurrentmA < 10U) {
sliceCount = 4;
} else if ((em0LoadCurrentmA >= 10U) && (em0LoadCurrentmA < 20U)) {
sliceCount = 8;
} else {
sliceCount = 16;
}
} else if (((EMU->DCDCMISCCTRL & _EMU_DCDCMISCCTRL_LNFORCECCM_MASK) != 0U)
&& (rcoBand <= (uint32_t)emuDcdcLnRcoBand_4MHz)) {
if (em0LoadCurrentmA < 40U) {
sliceCount = 8;
} else {
sliceCount = 16;
}
} else {
/* This configuration is not recommended. @ref 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 the 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 4 MHz LN RCO. */
EFM_ASSERT(((forcedCcm == 0U) && band <= emuDcdcLnRcoBand_4MHz)
|| (forcedCcm != 0U));
EMU->DCDCLNFREQCTRL = (EMU->DCDCLNFREQCTRL & ~_EMU_DCDCLNFREQCTRL_RCOBAND_MASK)
| ((uint32_t)band << _EMU_DCDCLNFREQCTRL_RCOBAND_SHIFT);
/* Update slice configuration as this depends on the RCO band. */
EMU_DCDCOptimizeSlice(dcdcEm01LoadCurrent_mA);
}
#endif /* EMU_SERIES1_DCDC_BUCK_PRESENT */
#if defined(EMU_SERIES2_DCDC_BOOST_PRESENT)
/***************************************************************************//**
* @brief
* Configure the DCDC Boost regulator.
*
* @param[in] dcdcBoostInit
* The DCDC initialization structure.
*
* @return
* True if initialization parameters are valid.
******************************************************************************/
bool EMU_DCDCBoostInit(const EMU_DCDCBoostInit_TypeDef *dcdcBoostInit)
{
bool dcdcLocked;
CMU->CLKEN0_SET = CMU_CLKEN0_DCDC;
#if defined(_DCDC_EN_EN_MASK)
DCDC->EN_SET = DCDC_EN_EN;
#endif
dcdcLocked = ((DCDC->LOCKSTATUS & DCDC_LOCKSTATUS_LOCK) != 0);
EMU_DCDCUnlock();
#if defined(_DCDC_SYNCBUSY_MASK)
EMU_DCDCSync(_DCDC_SYNCBUSY_MASK);
#endif
DCDC->BSTCTRL = (DCDC->BSTCTRL & ~(_DCDC_BSTCTRL_IPKTMAXCTRL_MASK))
| ((uint32_t)dcdcBoostInit->tonMax << _DCDC_BSTCTRL_IPKTMAXCTRL_SHIFT);
DCDC->BSTEM01CTRL = ((uint32_t)dcdcBoostInit->driveSpeedEM01 << _DCDC_BSTEM01CTRL_DRVSPEED_SHIFT)
| ((uint32_t)dcdcBoostInit->peakCurrentEM01 << _DCDC_BSTEM01CTRL_IPKVAL_SHIFT);
DCDC->BSTEM23CTRL = ((uint32_t)dcdcBoostInit->driveSpeedEM23 << _DCDC_BSTEM23CTRL_DRVSPEED_SHIFT)
| ((uint32_t)dcdcBoostInit->peakCurrentEM23 << _DCDC_BSTEM23CTRL_IPKVAL_SHIFT);
EMU_BoostExternalShutdownEnable(dcdcBoostInit->externalShutdownEn);
EMU_DCDCModeSet(emuDcdcMode_Regulation);
if (dcdcLocked) {
EMU_DCDCLock();
}
EMU_DCDCUpdatedHook();
return true;
}
/***************************************************************************//**
* @brief
* Set EM01 mode Boost Peak Current setting.
*
* @param[in] boostPeakCurrentEM01
* Boost Peak load current coefficient in EM01 mode.
******************************************************************************/
void EMU_EM01BoostPeakCurrentSet(const EMU_DcdcBoostEM01PeakCurrent_TypeDef boostPeakCurrentEM01)
{
bool dcdcLocked = false;
bool dcdcClkWasEnabled = false;
dcdcClkWasEnabled = ((CMU->CLKEN0 & CMU_CLKEN0_DCDC) != 0);
CMU->CLKEN0_SET = CMU_CLKEN0_DCDC;
dcdcLocked = ((DCDC->LOCKSTATUS & DCDC_LOCKSTATUS_LOCK) != 0);
EMU_DCDCUnlock();
/* Wait for synchronization before writing new value */
#if defined(_DCDC_SYNCBUSY_MASK)
EMU_DCDCSync(_DCDC_SYNCBUSY_MASK);
#endif
BUS_RegMaskedWrite(&DCDC->BSTEM01CTRL,
_DCDC_BSTEM01CTRL_IPKVAL_MASK,
((uint32_t)boostPeakCurrentEM01 << _DCDC_BSTEM01CTRL_IPKVAL_SHIFT));
if (dcdcLocked) {
EMU_DCDCLock();
}
if (!dcdcClkWasEnabled) {
CMU->CLKEN0_CLR = CMU_CLKEN0_DCDC;
}
EMU_DCDCUpdatedHook();
}
/***************************************************************************//**
* @brief
* Enable/disable Boost External Shutdown Mode.
*
* @param[in] enable
* The boost DC-DC converter can be activated or deactivated
* from a dedicated BOOST_EN pin on the device if enable is true.
******************************************************************************/
void EMU_BoostExternalShutdownEnable(bool enable)
{
if (enable) {
EMU->BOOSTCTRL_CLR = EMU_BOOSTCTRL_BOOSTENCTRL;
} else {
EMU->BOOSTCTRL_SET = EMU_BOOSTCTRL_BOOSTENCTRL;
}
}
#endif /* EMU_SERIES2_DCDC_BOOST_PRESENT */
#if defined(EMU_SERIES2_DCDC_BUCK_PRESENT) \
|| defined(EMU_SERIES2_DCDC_BOOST_PRESENT)
/***************************************************************************//**
* @brief
* Indicate that the DCDC peripheral bus clock enable has changed allowing
* RAIL to react accordingly.
*
* @details
* This function is called after DCDC has been enabled or disabled.
* The function implementation does not perform anything, but it is SL_WEAK
* so that it can use the RAIL version if needed.
******************************************************************************/
SL_WEAK void EMU_DCDCUpdatedHook(void)
{
}
/***************************************************************************//**
* @brief
* Set DCDC regulator operating mode.
*
* @param[in] dcdcMode
* DCDC mode.
* @return
* Returns the status of the DCDC mode set operation.
* @verbatim
* SL_STATUS_OK - Operation completed successfully.
* SL_STATUS_TIMEOUT - Operation EMU DCDC set mode timeout.
* @endverbatim
******************************************************************************/
sl_status_t EMU_DCDCModeSet(EMU_DcdcMode_TypeDef dcdcMode)
{
bool dcdcLocked;
uint32_t currentDcdcMode;
sl_status_t error = SL_STATUS_OK;
uint32_t timeout = 0;
CMU->CLKEN0_SET = CMU_CLKEN0_DCDC;
#if defined(_DCDC_EN_EN_MASK)
DCDC->EN_SET = DCDC_EN_EN;
#endif
dcdcLocked = ((DCDC->LOCKSTATUS & DCDC_LOCKSTATUS_LOCK) != 0);
EMU_DCDCUnlock();
if (dcdcMode == emuDcdcMode_Bypass) {
#if defined(_DCDC_SYNCBUSY_MASK)
#if defined(_DCDC_SYNCBUSY_CTRL_MASK)
EMU_DCDCSync(DCDC_SYNCBUSY_CTRL);
#else
EMU_DCDCSync(_DCDC_SYNCBUSY_MASK);
#endif
#endif
currentDcdcMode = (DCDC->CTRL & _DCDC_CTRL_MODE_MASK) >> _DCDC_CTRL_MODE_SHIFT;
if (currentDcdcMode != emuDcdcMode_Bypass) {
/* Switch to BYPASS mode if it is not the current mode */
DCDC->CTRL_CLR = DCDC_CTRL_MODE;
while (((DCDC->STATUS & DCDC_STATUS_BYPSW) == 0U) && (timeout < EMU_DCDC_MODE_SET_TIMEOUT)) {
/* Wait for BYPASS switch enable. */
timeout++;
}
if (timeout >= EMU_DCDC_MODE_SET_TIMEOUT) {
error = SL_STATUS_TIMEOUT;
}
}
#if defined(_DCDC_EN_EN_MASK)
DCDC->EN_CLR = DCDC_EN_EN;
#endif
} else {
while (((DCDC->STATUS & DCDC_STATUS_VREGIN) != 0U) && (timeout < EMU_DCDC_MODE_SET_TIMEOUT)) {
/* Wait for VREGIN voltage to rise above threshold. */
timeout++;
}
if (timeout >= EMU_DCDC_MODE_SET_TIMEOUT) {
error = SL_STATUS_TIMEOUT;
} else {
DCDC->IF_CLR = DCDC_IF_REGULATION;
DCDC->CTRL_SET = DCDC_CTRL_MODE;
timeout = 0;
while (((DCDC->IF & DCDC_IF_REGULATION) == 0U) && (timeout < EMU_DCDC_MODE_SET_TIMEOUT)) {
/* Wait for DCDC to complete it's startup. */
timeout++;
}
if (timeout >= EMU_DCDC_MODE_SET_TIMEOUT) {
error = SL_STATUS_TIMEOUT;
}
}
}
if (dcdcLocked) {
EMU_DCDCLock();
}
EMU_DCDCUpdatedHook();
return error;
}
#endif /* EMU_SERIES2_DCDC_BUCK_PRESENT || EMU_SERIES2_DCDC_BOOST_PRESENT */
#if defined(EMU_SERIES2_DCDC_BUCK_PRESENT)
/***************************************************************************//**
* @brief
* Configure the DCDC regulator.
*
* @param[in] dcdcInit
* The DCDC initialization structure.
*
* @return
* True if initialization parameters are valid.
******************************************************************************/
bool EMU_DCDCInit(const EMU_DCDCInit_TypeDef *dcdcInit)
{
bool dcdcLocked;
CMU->CLKEN0_SET = CMU_CLKEN0_DCDC;
#if defined(_DCDC_EN_EN_MASK)
DCDC->EN_SET = DCDC_EN_EN;
#endif
dcdcLocked = ((DCDC->LOCKSTATUS & DCDC_LOCKSTATUS_LOCK) != 0);
EMU_DCDCUnlock();
EMU->VREGVDDCMPCTRL = ((uint32_t)dcdcInit->cmpThreshold
<< _EMU_VREGVDDCMPCTRL_THRESSEL_SHIFT)
| EMU_VREGVDDCMPCTRL_VREGINCMPEN;
#if defined(_DCDC_SYNCBUSY_MASK)
#if defined(_DCDC_SYNCBUSY_CTRL_MASK)
EMU_DCDCSync(DCDC_SYNCBUSY_CTRL | DCDC_SYNCBUSY_EM01CTRL0 | DCDC_SYNCBUSY_EM23CTRL0);
#else
EMU_DCDCSync(_DCDC_SYNCBUSY_MASK);
#endif
#endif
#if defined(_DCDC_CTRL_DCMONLYEN_MASK)
DCDC->CTRL = (DCDC->CTRL & ~(_DCDC_CTRL_IPKTMAXCTRL_MASK
| _DCDC_CTRL_DCMONLYEN_MASK))
| ((uint32_t)dcdcInit->tonMax << _DCDC_CTRL_IPKTMAXCTRL_SHIFT)
| ((uint32_t)(dcdcInit->dcmOnlyEn ? 1U : 0U) << _DCDC_CTRL_DCMONLYEN_SHIFT);
#else
DCDC->CTRL = (DCDC->CTRL & ~(_DCDC_CTRL_IPKTMAXCTRL_MASK))
| ((uint32_t)dcdcInit->tonMax << _DCDC_CTRL_IPKTMAXCTRL_SHIFT);
#endif
DCDC->EM01CTRL0 = ((uint32_t)dcdcInit->driveSpeedEM01 << _DCDC_EM01CTRL0_DRVSPEED_SHIFT)
| ((uint32_t)dcdcInit->peakCurrentEM01 << _DCDC_EM01CTRL0_IPKVAL_SHIFT);
DCDC->EM23CTRL0 = ((uint32_t)dcdcInit->driveSpeedEM23 << _DCDC_EM23CTRL0_DRVSPEED_SHIFT)
| ((uint32_t)dcdcInit->peakCurrentEM23 << _DCDC_EM23CTRL0_IPKVAL_SHIFT);
EMU_DCDCModeSet(dcdcInit->mode);
if (dcdcLocked) {
EMU_DCDCLock();
}
EMU_DCDCUpdatedHook();
return true;
}
/***************************************************************************//**
* @brief
* Power off the DCDC regulator.
*
* @return
* Returns true.
******************************************************************************/
bool EMU_DCDCPowerOff(void)
{
EMU_DCDCModeSet(emuDcdcMode_Bypass);
return true;
}
/***************************************************************************//**
* @brief
* Set EMO1 mode Peak Current setting.
*
* @param[in] peakCurrentEM01
* Peak load current coefficient in EM01 mode.
******************************************************************************/
void EMU_EM01PeakCurrentSet(const EMU_DcdcPeakCurrent_TypeDef peakCurrentEM01)
{
bool dcdcLocked = false;
bool dcdcClkWasEnabled = false;
dcdcClkWasEnabled = ((CMU->CLKEN0 & CMU_CLKEN0_DCDC) != 0);
CMU->CLKEN0_SET = CMU_CLKEN0_DCDC;
#if defined(_DCDC_EN_EN_MASK)
bool dcdcWasEnabled = ((DCDC->EN & DCDC_EN_EN) != 0);
DCDC->EN_SET = DCDC_EN_EN;
#endif
dcdcLocked = ((DCDC->LOCKSTATUS & DCDC_LOCKSTATUS_LOCK) != 0);
EMU_DCDCUnlock();
/* Wait for synchronization before writing new value */
#if defined(_DCDC_SYNCBUSY_MASK)
#if defined(_DCDC_SYNCBUSY_EM01CTRL0_MASK)
EMU_DCDCSync(DCDC_SYNCBUSY_EM01CTRL0);
#else
EMU_DCDCSync(_DCDC_SYNCBUSY_MASK);
#endif
#endif
BUS_RegMaskedWrite(&DCDC->EM01CTRL0,
_DCDC_EM01CTRL0_IPKVAL_MASK,
((uint32_t)peakCurrentEM01 << _DCDC_EM01CTRL0_IPKVAL_SHIFT));
#if defined(_DCDC_EN_EN_MASK)
if (!dcdcWasEnabled) {
DCDC->EN_CLR = DCDC_EN_EN;
}
#endif
if (dcdcLocked) {
EMU_DCDCLock();
}
if (!dcdcClkWasEnabled) {
CMU->CLKEN0_CLR = CMU_CLKEN0_DCDC;
}
EMU_DCDCUpdatedHook();
}
#if defined(_DCDC_PFMXCTRL_IPKVAL_MASK)
/***************************************************************************//**
* @brief
* Set PFMX mode Peak Current setting.
*
* @param[in] value
* Peak load current coefficient in PFMX mode.
******************************************************************************/
void EMU_DCDCSetPFMXModePeakCurrent(uint32_t value)
{
bool dcdcLocked = false;
bool dcdcClkWasEnabled = false;
/* Verification that the parameter is in range. */
/* if not, restrict value to maximum allowed. */
EFM_ASSERT(value <= (_DCDC_PFMXCTRL_IPKVAL_MASK >> _DCDC_PFMXCTRL_IPKVAL_SHIFT));
if (value > (_DCDC_PFMXCTRL_IPKVAL_MASK >> _DCDC_PFMXCTRL_IPKVAL_SHIFT)) {
value = (_DCDC_PFMXCTRL_IPKVAL_MASK >> _DCDC_PFMXCTRL_IPKVAL_SHIFT);
}
dcdcClkWasEnabled = ((CMU->CLKEN0 & CMU_CLKEN0_DCDC) != 0);
CMU->CLKEN0_SET = CMU_CLKEN0_DCDC;
dcdcLocked = ((DCDC->LOCKSTATUS & DCDC_LOCKSTATUS_LOCK) != 0);
EMU_DCDCUnlock();
#if defined(_DCDC_SYNCBUSY_MASK)
/* Wait for synchronization before writing new value */
EMU_DCDCSync(DCDC_SYNCBUSY_PFMXCTRL);
#endif
DCDC->PFMXCTRL = ((DCDC->PFMXCTRL & ~_DCDC_PFMXCTRL_IPKVAL_MASK)
| value << _DCDC_PFMXCTRL_IPKVAL_SHIFT);
if (dcdcLocked) {
EMU_DCDCLock();
}
if (!dcdcClkWasEnabled) {
CMU->CLKEN0_CLR = CMU_CLKEN0_DCDC;
}
EMU_DCDCUpdatedHook();
}
#endif /* _DCDC_PFMXCTRL_IPKVAL_MASK */
#if defined(_DCDC_PFMXCTRL_IPKTMAXCTRL_MASK)
/***************************************************************************//**
* @brief
* Set Ton_max timeout control.
*
* @param[in] value
* Maximum time for peak current detection.
******************************************************************************/
void EMU_DCDCSetPFMXTimeoutMaxCtrl(EMU_DcdcTonMaxTimeout_TypeDef value)
{
bool dcdcLocked = false;
bool dcdcClkWasEnabled = false;
dcdcClkWasEnabled = ((CMU->CLKEN0 & CMU_CLKEN0_DCDC) != 0);
CMU->CLKEN0_SET = CMU_CLKEN0_DCDC;
dcdcLocked = ((DCDC->LOCKSTATUS & DCDC_LOCKSTATUS_LOCK) != 0);
EMU_DCDCUnlock();
#if defined(_DCDC_SYNCBUSY_MASK)
/* Wait for synchronization before writing new value */
EMU_DCDCSync(DCDC_SYNCBUSY_PFMXCTRL);
#endif
DCDC->PFMXCTRL = ((DCDC->PFMXCTRL & ~_DCDC_PFMXCTRL_IPKTMAXCTRL_MASK)
| value << _DCDC_PFMXCTRL_IPKTMAXCTRL_SHIFT);
if (dcdcLocked) {
EMU_DCDCLock();
}
if (!dcdcClkWasEnabled) {
CMU->CLKEN0_CLR = CMU_CLKEN0_DCDC;
}
EMU_DCDCUpdatedHook();
}
#endif /* _DCDC_PFMXCTRL_IPKTMAXCTRL_MASK */
#endif /* EMU_SERIES2_DCDC_BUCK_PRESENT */
#if defined(EMU_STATUS_VMONRDY)
/***************************************************************************//**
* @brief
* Get the calibrated threshold value.
*
* @details
* All VMON channels have two calibration fields in the DI page that
* describes the threshold at 1.86 V and 2.98 V. This function will convert
* the uncalibrated input voltage threshold in millivolts into a calibrated
* threshold.
*
* @param[in] channel
* A VMON channel.
*
* @param[in] threshold
* A desired threshold in millivolts.
*
* @return
* A calibrated threshold value to use. The first digit of the 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 tDiff = 0;
uint32_t tLow = 0;
uint32_t tHigh = 0;
uint32_t calReg;
/* Get calibration values for 1.86 V and 2.98 V */
switch (channel) {
case emuVmonChannel_AVDD:
calReg = DEVINFO->VMONCAL0;
tLow = (10U * ((calReg & _DEVINFO_VMONCAL0_AVDD1V86THRESCOARSE_MASK)
>> _DEVINFO_VMONCAL0_AVDD1V86THRESCOARSE_SHIFT))
+ ((calReg & _DEVINFO_VMONCAL0_AVDD1V86THRESFINE_MASK)
>> _DEVINFO_VMONCAL0_AVDD1V86THRESFINE_SHIFT);
tHigh = (10U * ((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 = (10U * ((calReg & _DEVINFO_VMONCAL0_ALTAVDD1V86THRESCOARSE_MASK)
>> _DEVINFO_VMONCAL0_ALTAVDD1V86THRESCOARSE_SHIFT))
+ ((calReg & _DEVINFO_VMONCAL0_ALTAVDD1V86THRESFINE_MASK)
>> _DEVINFO_VMONCAL0_ALTAVDD1V86THRESFINE_SHIFT);
tHigh = (10U * ((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 = (10U * ((calReg & _DEVINFO_VMONCAL1_DVDD1V86THRESCOARSE_MASK)
>> _DEVINFO_VMONCAL1_DVDD1V86THRESCOARSE_SHIFT))
+ ((calReg & _DEVINFO_VMONCAL1_DVDD1V86THRESFINE_MASK)
>> _DEVINFO_VMONCAL1_DVDD1V86THRESFINE_SHIFT);
tHigh = (10U * ((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 = (10U * ((calReg & _DEVINFO_VMONCAL1_IO01V86THRESCOARSE_MASK)
>> _DEVINFO_VMONCAL1_IO01V86THRESCOARSE_SHIFT))
+ ((calReg & _DEVINFO_VMONCAL1_IO01V86THRESFINE_MASK)
>> _DEVINFO_VMONCAL1_IO01V86THRESFINE_SHIFT);
tHigh = (10U * ((calReg & _DEVINFO_VMONCAL1_IO02V98THRESCOARSE_MASK)
>> _DEVINFO_VMONCAL1_IO02V98THRESCOARSE_SHIFT))
+ ((calReg & _DEVINFO_VMONCAL1_IO02V98THRESFINE_MASK)
>> _DEVINFO_VMONCAL1_IO02V98THRESFINE_SHIFT);
break;
#if defined(_EMU_VMONIO1CTRL_EN_MASK)
case emuVmonChannel_IOVDD1:
calReg = DEVINFO->VMONCAL2;
tLow = (10U * ((calReg & _DEVINFO_VMONCAL2_IO11V86THRESCOARSE_MASK)
>> _DEVINFO_VMONCAL2_IO11V86THRESCOARSE_SHIFT))
+ ((calReg & _DEVINFO_VMONCAL2_IO11V86THRESFINE_MASK)
>> _DEVINFO_VMONCAL2_IO11V86THRESFINE_SHIFT);
tHigh = (10U * ((calReg & _DEVINFO_VMONCAL2_IO12V98THRESCOARSE_MASK)
>> _DEVINFO_VMONCAL2_IO12V98THRESCOARSE_SHIFT))
+ ((calReg & _DEVINFO_VMONCAL2_IO12V98THRESFINE_MASK)
>> _DEVINFO_VMONCAL2_IO12V98THRESFINE_SHIFT);
break;
#endif
#if defined(_EMU_VMONBUVDDCTRL_EN_MASK)
case emuVmonChannel_BUVDD:
calReg = DEVINFO->VMONCAL2;
tLow = (10U * ((calReg & _DEVINFO_VMONCAL2_BUVDD1V86THRESCOARSE_MASK)
>> _DEVINFO_VMONCAL2_BUVDD1V86THRESCOARSE_SHIFT))
+ ((calReg & _DEVINFO_VMONCAL2_BUVDD1V86THRESFINE_MASK)
>> _DEVINFO_VMONCAL2_BUVDD1V86THRESFINE_SHIFT);
tHigh = (10U * ((calReg & _DEVINFO_VMONCAL2_BUVDD2V98THRESCOARSE_MASK)
>> _DEVINFO_VMONCAL2_BUVDD2V98THRESCOARSE_SHIFT))
+ ((calReg & _DEVINFO_VMONCAL2_BUVDD2V98THRESFINE_MASK)
>> _DEVINFO_VMONCAL2_BUVDD2V98THRESFINE_SHIFT);
break;
#endif
default:
EFM_ASSERT(false);
break;
}
tDiff = tHigh - tLow;
if (tDiff > 0) {
/* Calculate threshold.
*
* Note that volt is used in the reference manual. However, the results
* should be in millivolts. The precision of Va and Vb are increased in the
* calculation instead of using floating points.
*/
uint32_t va = (1120U * 100U) / (tDiff);
uint32_t vb = (1860U * 100U) - (va * tLow);
// If (tHigh - tLow) is large, Va could be zero. Caught by CSTAT.
if (va != 0) {
/* Round the threshold to the nearest integer value. */
return (((uint32_t)threshold * 100U) - vb + (va / 2U)) / va;
}
}
/* Uncalibrated device guard. */
return (uint32_t)threshold;
}
/***************************************************************************//**
* @brief
* Initialize a 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
* The VMON initialization structure.
******************************************************************************/
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 % 10U;
thresholdCoarse = threshold / 10U;
/* Saturate the threshold to maximum values. */
if (thresholdCoarse > 0xFU) {
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 : 0U)
| (vmonInit->fallWakeup ? EMU_VMONAVDDCTRL_FALLWU : 0U)
| (vmonInit->enable ? EMU_VMONAVDDCTRL_EN : 0U);
break;
case emuVmonChannel_ALTAVDD:
EMU->VMONALTAVDDCTRL = (thresholdCoarse << _EMU_VMONALTAVDDCTRL_THRESCOARSE_SHIFT)
| (thresholdFine << _EMU_VMONALTAVDDCTRL_THRESFINE_SHIFT)
| (vmonInit->riseWakeup ? EMU_VMONALTAVDDCTRL_RISEWU : 0U)
| (vmonInit->fallWakeup ? EMU_VMONALTAVDDCTRL_FALLWU : 0U)
| (vmonInit->enable ? EMU_VMONALTAVDDCTRL_EN : 0U);
break;
case emuVmonChannel_DVDD:
EMU->VMONDVDDCTRL = (thresholdCoarse << _EMU_VMONDVDDCTRL_THRESCOARSE_SHIFT)
| (thresholdFine << _EMU_VMONDVDDCTRL_THRESFINE_SHIFT)
| (vmonInit->riseWakeup ? EMU_VMONDVDDCTRL_RISEWU : 0U)
| (vmonInit->fallWakeup ? EMU_VMONDVDDCTRL_FALLWU : 0U)
| (vmonInit->enable ? EMU_VMONDVDDCTRL_EN : 0U);
break;
case emuVmonChannel_IOVDD0:
EMU->VMONIO0CTRL = (thresholdCoarse << _EMU_VMONIO0CTRL_THRESCOARSE_SHIFT)
| (thresholdFine << _EMU_VMONIO0CTRL_THRESFINE_SHIFT)
| (vmonInit->retDisable ? EMU_VMONIO0CTRL_RETDIS : 0U)
| (vmonInit->riseWakeup ? EMU_VMONIO0CTRL_RISEWU : 0U)
| (vmonInit->fallWakeup ? EMU_VMONIO0CTRL_FALLWU : 0U)
| (vmonInit->enable ? EMU_VMONIO0CTRL_EN : 0U);
break;
#if defined(_EMU_VMONIO1CTRL_EN_MASK)
case emuVmonChannel_IOVDD1:
EMU->VMONIO1CTRL = (thresholdCoarse << _EMU_VMONIO1CTRL_THRESCOARSE_SHIFT)
| (thresholdFine << _EMU_VMONIO1CTRL_THRESFINE_SHIFT)
| (vmonInit->retDisable ? EMU_VMONIO1CTRL_RETDIS : 0U)
| (vmonInit->riseWakeup ? EMU_VMONIO1CTRL_RISEWU : 0U)
| (vmonInit->fallWakeup ? EMU_VMONIO1CTRL_FALLWU : 0U)
| (vmonInit->enable ? EMU_VMONIO1CTRL_EN : 0U);
break;
#endif
#if defined(_EMU_VMONBUVDDCTRL_EN_MASK)
case emuVmonChannel_BUVDD:
EMU->VMONBUVDDCTRL = (thresholdCoarse << _EMU_VMONBUVDDCTRL_THRESCOARSE_SHIFT)
| (thresholdFine << _EMU_VMONBUVDDCTRL_THRESFINE_SHIFT)
| (vmonInit->riseWakeup ? EMU_VMONBUVDDCTRL_RISEWU : 0U)
| (vmonInit->fallWakeup ? EMU_VMONBUVDDCTRL_FALLWU : 0U)
| (vmonInit->enable ? EMU_VMONBUVDDCTRL_EN : 0U);
break;
#endif
default:
EFM_ASSERT(false);
return;
}
}
/***************************************************************************//**
* @brief
* Initialize a 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 the
* calibration values from the DI page.
*
* @param[in] vmonInit
* The VMON hysteresis initialization structure.
******************************************************************************/
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));
/* The 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 / 10U) << _EMU_VMONAVDDCTRL_RISETHRESCOARSE_SHIFT)
| ((riseThreshold % 10U) << _EMU_VMONAVDDCTRL_RISETHRESFINE_SHIFT)
| ((fallThreshold / 10U) << _EMU_VMONAVDDCTRL_FALLTHRESCOARSE_SHIFT)
| ((fallThreshold % 10U) << _EMU_VMONAVDDCTRL_FALLTHRESFINE_SHIFT)
| (vmonInit->riseWakeup ? EMU_VMONAVDDCTRL_RISEWU : 0U)
| (vmonInit->fallWakeup ? EMU_VMONAVDDCTRL_FALLWU : 0U)
| (vmonInit->enable ? EMU_VMONAVDDCTRL_EN : 0U);
break;
default:
EFM_ASSERT(false);
return;
}
}
/***************************************************************************//**
* @brief
* Enable or disable a VMON channel.
*
* @param[in] channel
* A VMON channel to enable/disable.
*
* @param[in] enable
* Indicates 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;
#if defined(_EMU_VMONIO1CTRL_EN_MASK)
case emuVmonChannel_IOVDD1:
reg = &(EMU->VMONIO1CTRL);
bit = _EMU_VMONIO1CTRL_EN_SHIFT;
break;
#endif
#if defined(_EMU_VMONBUVDDCTRL_EN_MASK)
case emuVmonChannel_BUVDD:
reg = &(EMU->VMONBUVDDCTRL);
bit = _EMU_VMONBUVDDCTRL_EN_SHIFT;
break;
#endif
default:
EFM_ASSERT(false);
return;
}
BUS_RegBitWrite(reg, bit, (uint32_t)enable);
}
/***************************************************************************//**
* @brief
* Get the status of a voltage monitor channel.
*
* @param[in] channel
* A VMON channel to get the status for.
*
* @return
* A status of the selected VMON channel. True if the 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;
#if defined(_EMU_VMONIO1CTRL_EN_MASK)
case emuVmonChannel_IOVDD1:
bit = _EMU_STATUS_VMONIO1_SHIFT;
break;
#endif
#if defined(_EMU_VMONBUVDDCTRL_EN_MASK)
case emuVmonChannel_BUVDD:
bit = _EMU_STATUS_VMONBUVDD_SHIFT;
break;
#endif
default:
bit = 0;
EFM_ASSERT(false);
break;
}
return BUS_RegBitRead(&EMU->STATUS, bit) != 0U;
}
#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 more details.
*
* @param [in] mode
* The new bias refresh rate.
******************************************************************************/
void EMU_SetBiasMode(EMU_BiasMode_TypeDef mode)
{
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
#if defined(_EMU_TEMP_TEMP_MASK)
/***************************************************************************//**
* @brief
* Get temperature in degrees Celsius
*
* @return
* Temperature in degrees Celsius
******************************************************************************/
float EMU_TemperatureGet(void)
{
#if defined(_EMU_TEMP_TEMPLSB_MASK)
return ((float) ((EMU->TEMP & (_EMU_TEMP_TEMP_MASK | _EMU_TEMP_TEMPLSB_MASK) )
>> _EMU_TEMP_TEMPLSB_SHIFT)
) / 4.0f - EMU_TEMP_ZERO_C_IN_KELVIN;
#else
uint32_t val1;
uint32_t val2;
float tempCo;
uint32_t diTemp, diEmu;
// Calculate calibration temp based on DI page values
diTemp = ((DEVINFO->CAL & _DEVINFO_CAL_TEMP_MASK) >> _DEVINFO_CAL_TEMP_SHIFT);
diEmu = ((DEVINFO->EMUTEMP & _DEVINFO_EMUTEMP_EMUTEMPROOM_MASK) >> _DEVINFO_EMUTEMP_EMUTEMPROOM_SHIFT);
tempCo = EMU_TEMPCO_CONST + (diEmu / 100.0f);
// Read temperature twice to ensure a stable value
do {
val1 = (EMU->TEMP & _EMU_TEMP_TEMP_MASK)
>> _EMU_TEMP_TEMP_SHIFT;
val2 = (EMU->TEMP & _EMU_TEMP_TEMP_MASK)
>> _EMU_TEMP_TEMP_SHIFT;
} while (val1 != val2);
return diTemp + tempCo * ((int) diEmu - (int) val1);
#endif
}
#endif // defined(_EMU_TEMP_TEMP_MASK)
#if defined(EMU_CTRL_EFPDIRECTMODEEN)
/***************************************************************************//**
* @brief
* Enable/disable EFP Direct Mode.
*
* @param[in] enable
* True to enable direct mode.
******************************************************************************/
void EMU_EFPDirectModeEnable(bool enable)
{
if (enable) {
EMU->CTRL_SET = EMU_CTRL_EFPDIRECTMODEEN;
} else {
EMU->CTRL_CLR = EMU_CTRL_EFPDIRECTMODEEN;
}
}
#endif
#if defined(EMU_CTRL_EFPDRVDECOUPLE)
/***************************************************************************//**
* @brief
* Set to enable EFP to drive Decouple voltage.
*
* @details
* Once set, internal LDO will be disabled, and the EMU will control EFP for
* voltage-scaling. Note that because this bit disables the internal LDO
* powering the core, it should not be set until after EFP's DECOUPLE output has
* been configured and enabled.
*
* @param[in] enable
* True to enable EFP to drive Decouple voltage.
******************************************************************************/
void EMU_EFPDriveDecoupleSet(bool enable)
{
if (enable) {
EMU->CTRL_SET = EMU_CTRL_EFPDRVDECOUPLE;
} else {
EMU->CTRL_CLR = EMU_CTRL_EFPDRVDECOUPLE;
}
}
#endif
#if defined(EMU_CTRL_EFPDRVDVDD)
/***************************************************************************//**
* @brief
* Set to enable EFP to drive DVDD voltage.
*
* @details
* Set this if EFP's DCDC output is powering DVDD supply. This mode assumes that
* internal DCDC is not being used.
*
* @param[in] enable
* True to enable EFP to drive DVDD voltage.
******************************************************************************/
void EMU_EFPDriveDvddSet(bool enable)
{
if (enable) {
EMU->CTRL_SET = EMU_CTRL_EFPDRVDVDD;
} else {
EMU->CTRL_CLR = EMU_CTRL_EFPDRVDVDD;
}
}
#endif
/** @} (end addtogroup emu) */
#endif /* __EM_EMU_H */