/***************************************************************************//**
 * @file
 * @brief Energy Management Unit (EMU) Peripheral API
 *******************************************************************************
 * # License
 * <b>Copyright 2018 Silicon Laboratories Inc. www.silabs.com</b>
 *******************************************************************************
 *
 * 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 <limits.h>

#include "em_emu.h"
#if defined(EMU_PRESENT) && (EMU_COUNT > 0)

#include "sl_assert.h"
#include "em_cmu.h"
#include "em_gpio.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_6)
#define RAM0_BLOCKS           32U
#define RAM0_BLOCK_SIZE   0x4000U // 16 kB blocks
#elif defined(_SILICON_LABS_32B_SERIES_2_CONFIG_8) \
  || defined(_SILICON_LABS_32B_SERIES_2_CONFIG_9)
#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(_GPIO_IF_EM4WU_MASK)
#if defined(_CMU_CLKEN0_GPIO_SHIFT)
  if (CMU->CLKEN0 & CMU_CLKEN0_GPIO) {
    // Clear all EM4WU interrupts before entering sleep
    GPIO_IntClear(_GPIO_IF_EM4WU_MASK);
  }
#else
  GPIO_IntClear(_GPIO_IF_EM4WU_MASK);
#endif // defined(_CMU_CLKEN0_GPIO_SHIFT)
#endif // defined(_GPIO_IF_EM4WU_MASK)

#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

#if defined(_GPIO_IF_EM4WU_MASK)
#if defined(_CMU_CLKEN0_GPIO_SHIFT)
  if (CMU->CLKEN0 & CMU_CLKEN0_GPIO) {
    // Clear all EM4WU interrupts before entering sleep
    GPIO_IntClear(_GPIO_IF_EM4WU_MASK);
  }
#else
  GPIO_IntClear(_GPIO_IF_EM4WU_MASK);
#endif // defined(_CMU_CLKEN0_GPIO_SHIFT)
#endif // defined(_GPIO_IF_EM4WU_MASK)

  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.
 *   Device which is configured in Boost DC-DC mode can not enter EM4.
 ******************************************************************************/
void EMU_EnterEM4(void)
{
  /* Device with Boost DC-DC cannot enter EM4 because Boost DC-DC module does not
   * have BYPASS switch so DC-DC converter can not be set to bypass mode. */
#if (defined(_SILICON_LABS_DCDC_FEATURE) \
  && (_SILICON_LABS_DCDC_FEATURE == _SILICON_LABS_DCDC_FEATURE_DCDC_BOOST))
  EFM_ASSERT(false);
#endif

#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();

#if defined(_GPIO_IF_EM4WU_MASK)
#if defined(_CMU_CLKEN0_GPIO_SHIFT)
  if (CMU->CLKEN0 & CMU_CLKEN0_GPIO) {
    // Clear all EM4WU interrupts before entering sleep
    GPIO_IntClear(_GPIO_IF_EM4WU_MASK);
  }
#else
  GPIO_IntClear(_GPIO_IF_EM4WU_MASK);
#endif // defined(_CMU_CLKEN0_GPIO_SHIFT)
#endif // defined(_GPIO_IF_EM4WU_MASK)

  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)
  /* If EM4ERR flag in DCDC->IF is set, mean that device cannot enter EM4, device will be suspended in this assertion */
  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 power down blocks from start to the end of RAM. For xg27,
 *   it will shut off 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
 *   The specified memory block(s) will stay off until a call
 *   to EMU_RamPowerUp() is done.
 *
 * @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. Except for xg27, It can only
 *   have two values: 0 or more than RAM0_END. Any other valid RAM address
 *   will just do nothing without any error or indication that nothing happened.
 *   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 defined(_SILICON_LABS_32B_SERIES_2)
  if (end >= RAM0_END) {
#if 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_6)  \
    || defined(_SILICON_LABS_32B_SERIES_2_CONFIG_8) \
    || defined(_SILICON_LABS_32B_SERIES_2_CONFIG_9)
    // 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
  }
#if defined(_SILICON_LABS_32B_SERIES_2_CONFIG_7)
  else if (end > 0x20006000UL) {
    // 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
  }
#endif
#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

#if defined(_DCDC_CTRL_DVDDBSTPRG_MASK)
  BUS_RegMaskedWrite(&DCDC->CTRL,
                     _DCDC_CTRL_DVDDBSTPRG_MASK,
                     ((uint32_t)dcdcBoostInit->outputVoltage << _DCDC_CTRL_DVDDBSTPRG_SHIFT));
#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;
  }
}

#if defined(_DCDC_CTRL_DVDDBSTPRG_MASK)
/***************************************************************************//**
 * @brief
 *   Set DCDC Boost output voltage.
 *
 * @param[in] boostOutputVoltage
 *   Boost output voltage.
 ******************************************************************************/
void EMU_DCDCBoostOutputVoltageSet(const EMU_DcdcBoostOutputVoltage_TypeDef boostOutputVoltage)
{
  bool dcdcLocked = false;

  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->CTRL,
                     _DCDC_CTRL_DVDDBSTPRG_MASK,
                     ((uint32_t)boostOutputVoltage << _DCDC_CTRL_DVDDBSTPRG_SHIFT));

  if (dcdcLocked) {
    EMU_DCDCLock();
  }

  EMU_DCDCUpdatedHook();
}
#endif
#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.
 ******************************************************************************/
SL_WEAK 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

#if defined(_EMU_CTRL_HDREGEM2EXITCLIM_MASK)
/***************************************************************************//**
 * @brief
 *   Set to enable HDREG EM2 Exit current limit.
 *
 * @details
 *   Limit HDREG max current drawn on EM2 exit by temporarily adjusting its
 *   output trim so current is pulled from DECOUPLE cap.
 *
 * @param[in] enable
 *   True to enable HDREG EM2 Exit current limit.
 ******************************************************************************/
void EMU_HDRegEM2ExitCurrentLimitEnable(bool enable)
{
  if (enable) {
    EMU->CTRL_SET = EMU_CTRL_HDREGEM2EXITCLIM;
  } else {
    EMU->CTRL_CLR = EMU_CTRL_HDREGEM2EXITCLIM;
  }
}
#endif

#if defined(_EMU_CTRL_HDREGSTOPGEAR_MASK)
/***************************************************************************//**
 * @brief
 *   Set the HDREG max current capability limit.
 *
 * @param[in] current
 *   HDREG max current capability limit.
 ******************************************************************************/
void EMU_HDRegStopGearSet(EMU_HdregStopGearILmt_TypeDef current)
{
  EMU->CTRL =  ((current << _EMU_CTRL_HDREGSTOPGEAR_SHIFT) \
                & _EMU_CTRL_HDREGSTOPGEAR_MASK) | (EMU->CTRL & ~_EMU_CTRL_HDREGSTOPGEAR_MASK);
}
#endif
/** @} (end addtogroup emu) */
#endif /* __EM_EMU_H */
