added prescaler for 16 bit pwm in LPC1347 target

Fork of mbed-dev by mbed official

Revision:
50:a417edff4437
Parent:
0:9b334a45a8ff
Child:
144:ef7eb2e8f9f7
diff -r 57ac6e3cdfd3 -r a417edff4437 targets/hal/TARGET_Silicon_Labs/TARGET_EFM32/emlib/src/em_emu.c
--- a/targets/hal/TARGET_Silicon_Labs/TARGET_EFM32/emlib/src/em_emu.c	Wed Jan 13 12:45:11 2016 +0000
+++ b/targets/hal/TARGET_Silicon_Labs/TARGET_EFM32/emlib/src/em_emu.c	Fri Jan 15 07:45:16 2016 +0000
@@ -1,10 +1,10 @@
 /***************************************************************************//**
  * @file em_emu.c
  * @brief Energy Management Unit (EMU) Peripheral API
- * @version 3.20.12
+ * @version 4.2.1
  *******************************************************************************
  * @section License
- * <b>(C) Copyright 2014 Silicon Labs, http://www.silabs.com</b>
+ * <b>(C) Copyright 2015 Silicon Labs, http://www.silabs.com</b>
  *******************************************************************************
  *
  * Permission is granted to anyone to use this software for any purpose,
@@ -30,6 +30,7 @@
  *
  ******************************************************************************/
 
+#include <limits.h>
 
 #include "em_emu.h"
 #if defined( EMU_PRESENT ) && ( EMU_COUNT > 0 )
@@ -67,32 +68,50 @@
 
 /** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
 /* Fix for errata EMU_E107 - non-WIC interrupt masks. */
-#if defined(_EFM32_GECKO_FAMILY)
-  #define ERRATA_FIX_EMU_E107_EN
-  #define NON_WIC_INT_MASK_0    (~(0x0dfc0323U))
-  #define NON_WIC_INT_MASK_1    (~(0x0U))
-#elif defined(_EFM32_TINY_FAMILY)
-  #define ERRATA_FIX_EMU_E107_EN
-  #define NON_WIC_INT_MASK_0    (~(0x001be323U))
-  #define NON_WIC_INT_MASK_1    (~(0x0U))
-#elif defined(_EFM32_GIANT_FAMILY)
-  #define ERRATA_FIX_EMU_E107_EN
-  #define NON_WIC_INT_MASK_0    (~(0xff020e63U))
-  #define NON_WIC_INT_MASK_1    (~(0x00000046U))
-#elif defined(_EFM32_WONDER_FAMILY)
-  #define ERRATA_FIX_EMU_E107_EN
-  #define NON_WIC_INT_MASK_0    (~(0xff020e63U))
-  #define NON_WIC_INT_MASK_1    (~(0x00000046U))
+#if defined( _EFM32_GECKO_FAMILY )
+#define ERRATA_FIX_EMU_E107_EN
+#define NON_WIC_INT_MASK_0    (~(0x0dfc0323U))
+#define NON_WIC_INT_MASK_1    (~(0x0U))
+
+#elif defined( _EFM32_TINY_FAMILY )
+#define ERRATA_FIX_EMU_E107_EN
+#define NON_WIC_INT_MASK_0    (~(0x001be323U))
+#define NON_WIC_INT_MASK_1    (~(0x0U))
+
+#elif defined( _EFM32_GIANT_FAMILY )
+#define ERRATA_FIX_EMU_E107_EN
+#define NON_WIC_INT_MASK_0    (~(0xff020e63U))
+#define NON_WIC_INT_MASK_1    (~(0x00000046U))
+
+#elif defined( _EFM32_WONDER_FAMILY )
+#define ERRATA_FIX_EMU_E107_EN
+#define NON_WIC_INT_MASK_0    (~(0xff020e63U))
+#define NON_WIC_INT_MASK_1    (~(0x00000046U))
+
 #else
 /* Zero Gecko and future families are not affected by errata EMU_E107 */
 #endif
 
 /* Fix for errata EMU_E108 - High Current Consumption on EM4 Entry. */
-#if defined(_EFM32_HAPPY_FAMILY)
+#if defined( _EFM32_HAPPY_FAMILY )
 #define ERRATA_FIX_EMU_E108_EN
 #endif
 /** @endcond */
 
+
+#if defined( _EMU_DCDCCTRL_MASK )
+/* DCDCTODVDD output range min/max */
+#define PWRCFG_DCDCTODVDD_VMIN          1200
+#define PWRCFG_DCDCTODVDD_VMAX          3000
+typedef enum
+{
+  errataFixDcdcHsInit,
+  errataFixDcdcHsTrimSet,
+  errataFixDcdcHsLnWaitDone
+} errataFixDcdcHs_TypeDef;
+errataFixDcdcHs_TypeDef errataFixDcdcHsState = errataFixDcdcHsInit;
+#endif
+
 /*******************************************************************************
  **************************   LOCAL VARIABLES   ********************************
  ******************************************************************************/
@@ -108,6 +127,14 @@
  * for oscillator control).
  */
 static uint32_t cmuStatus;
+#if defined( _CMU_HFCLKSTATUS_RESETVALUE )
+static uint16_t cmuHfclkStatus;
+#endif
+#if defined( _EMU_DCDCCTRL_MASK )
+static uint16_t dcdcMaxCurrent_mA;
+static uint16_t dcdcOutput_mVout;
+#endif
+
 /** @endcond */
 
 
@@ -121,11 +148,10 @@
  * @brief
  *   Restore oscillators and core clock after having been in EM2 or EM3.
  ******************************************************************************/
-static void EMU_Restore(void)
+static void emuRestore(void)
 {
   uint32_t oscEnCmd;
   uint32_t cmuLocked;
-  uint32_t statusClkSelMask;
 
   /* Although we could use the CMU API for most of the below handling, we */
   /* would like this function to be as efficient as possible. */
@@ -149,52 +175,91 @@
 #endif
   CMU->OSCENCMD = oscEnCmd;
 
-  statusClkSelMask =
-    (CMU_STATUS_HFRCOSEL |
-     CMU_STATUS_HFXOSEL |
-     CMU_STATUS_LFRCOSEL |
-#if defined( CMU_STATUS_USHFRCODIV2SEL )
-     CMU_STATUS_USHFRCODIV2SEL |
-#endif
-     CMU_STATUS_LFXOSEL);
 
+#if defined( _CMU_HFCLKSTATUS_RESETVALUE )
   /* Restore oscillator used for clocking core */
-  switch (cmuStatus & statusClkSelMask)
+  switch (cmuHfclkStatus & _CMU_HFCLKSTATUS_SELECTED_MASK)
   {
-  case CMU_STATUS_LFRCOSEL:
-    /* Wait for LFRCO to stabilize */
-    while (!(CMU->STATUS & CMU_STATUS_LFRCORDY))
-      ;
-    CMU->CMD = CMU_CMD_HFCLKSEL_LFRCO;
-    break;
+    case CMU_HFCLKSTATUS_SELECTED_LFRCO:
+      /* HFRCO could only be selected if the autostart HFXO feature is not
+       * enabled, otherwise the HFXO would be started and selected automatically.
+       * Note: this error hook helps catching erroneous oscillator configurations,
+       * when the AUTOSTARTSELEM0EM1 is set in CMU_HFXOCTRL. */
+      if (!(CMU->HFXOCTRL & CMU_HFXOCTRL_AUTOSTARTSELEM0EM1))
+      {
+        /* Wait for LFRCO to stabilize */
+        while (!(CMU->STATUS & CMU_STATUS_LFRCORDY))
+          ;
+        CMU->HFCLKSEL = CMU_HFCLKSEL_HF_LFRCO;
+      }
+      else
+      {
+        EFM_ASSERT(0);
+      }
+      break;
+
+    case CMU_HFCLKSTATUS_SELECTED_LFXO:
+      /* Wait for LFXO to stabilize */
+      while (!(CMU->STATUS & CMU_STATUS_LFXORDY))
+        ;
+      CMU->HFCLKSEL = CMU_HFCLKSEL_HF_LFXO;
+      break;
+
+    case CMU_HFCLKSTATUS_SELECTED_HFXO:
+      /* Wait for HFXO to stabilize */
+      while (!(CMU->STATUS & CMU_STATUS_HFXORDY))
+        ;
+      CMU->HFCLKSEL = CMU_HFCLKSEL_HF_HFXO;
+      break;
 
-  case CMU_STATUS_LFXOSEL:
-    /* Wait for LFXO to stabilize */
-    while (!(CMU->STATUS & CMU_STATUS_LFXORDY))
-      ;
-    CMU->CMD = CMU_CMD_HFCLKSEL_LFXO;
-    break;
+    default: /* CMU_HFCLKSTATUS_SELECTED_HFRCO */
+      /* If core clock was HFRCO core clock, it is automatically restored to */
+      /* state prior to entering energy mode. No need for further action. */
+      break;
+  }
+#else
+  switch (cmuStatus & (CMU_STATUS_HFRCOSEL
+                      | CMU_STATUS_HFXOSEL
+                      | CMU_STATUS_LFRCOSEL
+#if defined( CMU_STATUS_USHFRCODIV2SEL )
+                      | CMU_STATUS_USHFRCODIV2SEL
+#endif
+                      | CMU_STATUS_LFXOSEL))
+  {
+    case CMU_STATUS_LFRCOSEL:
+      /* Wait for LFRCO to stabilize */
+      while (!(CMU->STATUS & CMU_STATUS_LFRCORDY))
+        ;
+      CMU->CMD = CMU_CMD_HFCLKSEL_LFRCO;
+      break;
 
-  case CMU_STATUS_HFXOSEL:
-    /* Wait for HFXO to stabilize */
-    while (!(CMU->STATUS & CMU_STATUS_HFXORDY))
-      ;
-    CMU->CMD = CMU_CMD_HFCLKSEL_HFXO;
-    break;
+    case CMU_STATUS_LFXOSEL:
+      /* Wait for LFXO to stabilize */
+      while (!(CMU->STATUS & CMU_STATUS_LFXORDY))
+        ;
+      CMU->CMD = CMU_CMD_HFCLKSEL_LFXO;
+      break;
+
+    case CMU_STATUS_HFXOSEL:
+      /* Wait for HFXO to stabilize */
+      while (!(CMU->STATUS & CMU_STATUS_HFXORDY))
+        ;
+      CMU->CMD = CMU_CMD_HFCLKSEL_HFXO;
+      break;
 
 #if defined( CMU_STATUS_USHFRCODIV2SEL )
-  case CMU_STATUS_USHFRCODIV2SEL:
-    /* Wait for USHFRCO to stabilize */
-    while (!(CMU->STATUS & CMU_STATUS_USHFRCORDY))
-      ;
-    CMU->CMD = _CMU_CMD_HFCLKSEL_USHFRCODIV2;
-    break;
+    case CMU_STATUS_USHFRCODIV2SEL:
+      /* Wait for USHFRCO to stabilize */
+      while (!(CMU->STATUS & CMU_STATUS_USHFRCORDY))
+        ;
+      CMU->CMD = _CMU_CMD_HFCLKSEL_USHFRCODIV2;
+      break;
 #endif
 
-  default: /* CMU_STATUS_HFRCOSEL */
-    /* If core clock was HFRCO core clock, it is automatically restored to */
-    /* state prior to entering energy mode. No need for further action. */
-    break;
+    default: /* CMU_STATUS_HFRCOSEL */
+      /* If core clock was HFRCO core clock, it is automatically restored to */
+      /* state prior to entering energy mode. No need for further action. */
+      break;
   }
 
   /* If HFRCO was disabled before entering Energy Mode, turn it off again */
@@ -203,7 +268,7 @@
   {
     CMU->OSCENCMD = CMU_OSCENCMD_HFRCODIS;
   }
-
+#endif
   /* Restore CMU register locking */
   if (cmuLocked)
   {
@@ -212,27 +277,34 @@
 }
 
 
+#if defined( ERRATA_FIX_EMU_E107_EN )
 /* Get enable conditions for errata EMU_E107 fix. */
-#if defined(ERRATA_FIX_EMU_E107_EN)
 static __INLINE bool getErrataFixEmuE107En(void)
 {
-  /* SYSTEM_ChipRevisionGet could have been used here, but we would like a faster implementation in this case. */
+  /* SYSTEM_ChipRevisionGet could have been used here, but we would like a
+   * faster implementation in this case.
+   */
   uint16_t majorMinorRev;
 
   /* CHIP MAJOR bit [3:0] */
-  majorMinorRev = (((ROMTABLE->PID0 & _ROMTABLE_PID0_REVMAJOR_MASK) >> _ROMTABLE_PID0_REVMAJOR_SHIFT) << 8);
+  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);
+  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);
+  majorMinorRev |= (ROMTABLE->PID3 & _ROMTABLE_PID3_REVMINORLSB_MASK)
+                   >> _ROMTABLE_PID3_REVMINORLSB_SHIFT;
 
-#if defined(_EFM32_GECKO_FAMILY)
+#if defined( _EFM32_GECKO_FAMILY )
   return (majorMinorRev <= 0x0103);
-#elif defined(_EFM32_TINY_FAMILY)
+#elif defined( _EFM32_TINY_FAMILY )
   return (majorMinorRev <= 0x0102);
-#elif defined(_EFM32_GIANT_FAMILY)
+#elif defined( _EFM32_GIANT_FAMILY )
   return (majorMinorRev <= 0x0103) || (majorMinorRev == 0x0204);
-#elif defined(_EFM32_WONDER_FAMILY)
+#elif defined( _EFM32_WONDER_FAMILY )
   return (majorMinorRev == 0x0100);
 #else
   /* Zero Gecko and future families are not affected by errata EMU_E107 */
@@ -241,6 +313,50 @@
 }
 #endif
 
+
+#if defined( _EMU_DCDCCTRL_MASK )
+/* LP prepare / LN restore P/NFET count */
+static void maxCurrentUpdate(void);
+#define DCDC_LP_PFET_CNT        7
+#define DCDC_LP_NFET_CNT        15
+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;
+    maxCurrentUpdate();
+  }
+  else
+  {
+    EMU->DCDCMISCCTRL = emuDcdcMiscCtrlReg;
+    maxCurrentUpdate();
+  }
+}
+
+void dcdcHsFixLnBlock(void)
+{
+#define EMU_DCDCSTATUS  (* (volatile uint32_t *)(EMU_BASE + 0x7C))
+  if (errataFixDcdcHsState == errataFixDcdcHsTrimSet)
+  {
+    /* Wait for LNRUNNING */
+    if ((EMU->DCDCCTRL & ~_EMU_DCDCCTRL_DCDCMODE_MASK) == EMU_DCDCCTRL_DCDCMODE_LOWNOISE)
+    {
+      while (!(EMU_DCDCSTATUS & (0x1 << 16)));
+    }
+    errataFixDcdcHsState = errataFixDcdcHsLnWaitDone;
+  }
+}
+#endif
+
+
 /** @endcond */
 
 
@@ -281,6 +397,11 @@
  *   If a debugger is attached, the AUXHFRCO will not be disabled if enabled
  *   upon entering EM2. It will thus remain enabled when returning to EM0
  *   regardless of the @p restore parameter.
+ * @par
+ *   If HFXO autostart and select is enabled by using CMU_HFXOAutostartEnable(),
+ *   the starting and selecting of the core clocks will be identical to the user
+ *   independently of the value of the @p restore parameter when waking up on
+ *   the wakeup sources corresponding to the autostart and select setting.
  *
  * @param[in] restore
  *   @li true - restore oscillators and clocks, see function details.
@@ -291,7 +412,7 @@
  ******************************************************************************/
 void EMU_EnterEM2(bool restore)
 {
-#if defined(ERRATA_FIX_EMU_E107_EN)
+#if defined( ERRATA_FIX_EMU_E107_EN )
   bool errataFixEmuE107En;
   uint32_t nonWicIntEn[2];
 #endif
@@ -299,13 +420,16 @@
   /* Auto-update CMU status just in case before entering energy mode. */
   /* This variable is normally kept up-to-date by the CMU API. */
   cmuStatus = CMU->STATUS;
+#if defined( _CMU_HFCLKSTATUS_RESETVALUE )
+  cmuHfclkStatus = (uint16_t)(CMU->HFCLKSTATUS);
+#endif
 
-  /* Enter Cortex-M3 deep sleep mode */
+  /* Enter Cortex deep sleep mode */
   SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
 
   /* Fix for errata EMU_E107 - store non-WIC interrupt enable flags.
      Disable the enabled non-WIC interrupts. */
-#if defined(ERRATA_FIX_EMU_E107_EN)
+#if defined( ERRATA_FIX_EMU_E107_EN )
   errataFixEmuE107En = getErrataFixEmuE107En();
   if (errataFixEmuE107En)
   {
@@ -318,10 +442,19 @@
   }
 #endif
 
+#if defined( _EMU_DCDCCTRL_MASK )
+  dcdcFetCntSet(true);
+  dcdcHsFixLnBlock();
+#endif
+
   __WFI();
 
+#if defined( _EMU_DCDCCTRL_MASK )
+  dcdcFetCntSet(false);
+#endif
+
   /* Fix for errata EMU_E107 - restore state of non-WIC interrupt enable flags. */
-#if defined(ERRATA_FIX_EMU_E107_EN)
+#if defined( ERRATA_FIX_EMU_E107_EN )
   if (errataFixEmuE107En)
   {
     NVIC->ISER[0] = nonWicIntEn[0];
@@ -334,12 +467,17 @@
   /* Restore oscillators/clocks if specified */
   if (restore)
   {
-    EMU_Restore();
+    emuRestore();
   }
   /* If not restoring, and original clock was not HFRCO, we have to */
   /* update CMSIS core clock variable since core clock has changed */
   /* to using HFRCO. */
+#if defined( _CMU_HFCLKSTATUS_RESETVALUE )
+  else if ((cmuHfclkStatus & _CMU_HFCLKSTATUS_SELECTED_MASK)
+           != CMU_HFCLKSTATUS_SELECTED_HFRCO)
+#else
   else if (!(cmuStatus & CMU_STATUS_HFRCOSEL))
+#endif
   {
     SystemCoreClockUpdate();
   }
@@ -392,7 +530,7 @@
 {
   uint32_t cmuLocked;
 
-#if defined(ERRATA_FIX_EMU_E107_EN)
+#if defined( ERRATA_FIX_EMU_E107_EN )
   bool errataFixEmuE107En;
   uint32_t nonWicIntEn[2];
 #endif
@@ -400,6 +538,9 @@
   /* Auto-update CMU status just in case before entering energy mode. */
   /* This variable is normally kept up-to-date by the CMU API. */
   cmuStatus = CMU->STATUS;
+#if defined( _CMU_HFCLKSTATUS_RESETVALUE )
+  cmuHfclkStatus = (uint16_t)(CMU->HFCLKSTATUS);
+#endif
 
   /* CMU registers may be locked */
   cmuLocked = CMU->LOCK & CMU_LOCK_LOCKKEY_LOCKED;
@@ -414,12 +555,12 @@
     CMU_Lock();
   }
 
-  /* Enter Cortex-M3 deep sleep mode */
+  /* Enter Cortex deep sleep mode */
   SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
 
   /* Fix for errata EMU_E107 - store non-WIC interrupt enable flags.
      Disable the enabled non-WIC interrupts. */
-#if defined(ERRATA_FIX_EMU_E107_EN)
+#if defined( ERRATA_FIX_EMU_E107_EN )
   errataFixEmuE107En = getErrataFixEmuE107En();
   if (errataFixEmuE107En)
   {
@@ -433,10 +574,19 @@
   }
 #endif
 
+#if defined( _EMU_DCDCCTRL_MASK )
+  dcdcFetCntSet(true);
+  dcdcHsFixLnBlock();
+#endif
+
   __WFI();
 
+#if defined( _EMU_DCDCCTRL_MASK )
+  dcdcFetCntSet(false);
+#endif
+
   /* Fix for errata EMU_E107 - restore state of non-WIC interrupt enable flags. */
-#if defined(ERRATA_FIX_EMU_E107_EN)
+#if defined( ERRATA_FIX_EMU_E107_EN )
   if (errataFixEmuE107En)
   {
     NVIC->ISER[0] = nonWicIntEn[0];
@@ -449,12 +599,17 @@
   /* Restore oscillators/clocks if specified */
   if (restore)
   {
-    EMU_Restore();
+    emuRestore();
   }
   /* If not restoring, and original clock was not HFRCO, we have to */
   /* update CMSIS core clock variable since core clock has changed */
   /* to using HFRCO. */
+#if defined( _CMU_HFCLKSTATUS_RESETVALUE )
+  else if ((cmuHfclkStatus & _CMU_HFCLKSTATUS_SELECTED_MASK)
+           != CMU_HFCLKSTATUS_SELECTED_HFRCO)
+#else
   else if (!(cmuStatus & CMU_STATUS_HFRCOSEL))
+#endif
   {
     SystemCoreClockUpdate();
   }
@@ -471,27 +626,46 @@
 void EMU_EnterEM4(void)
 {
   int i;
-  uint32_t em4seq2;
-  uint32_t em4seq3;
 
-  em4seq2 = (EMU->CTRL & ~_EMU_CTRL_EM4CTRL_MASK) | (2 << _EMU_CTRL_EM4CTRL_SHIFT);
-  em4seq3 = (EMU->CTRL & ~_EMU_CTRL_EM4CTRL_MASK) | (3 << _EMU_CTRL_EM4CTRL_SHIFT);
+#if defined( _EMU_EM4CTRL_EM4ENTRY_SHIFT )
+  uint32_t em4seq2 = (EMU->EM4CTRL & ~_EMU_EM4CTRL_EM4ENTRY_MASK)
+                     | (2 << _EMU_EM4CTRL_EM4ENTRY_SHIFT);
+  uint32_t em4seq3 = (EMU->EM4CTRL & ~_EMU_EM4CTRL_EM4ENTRY_MASK)
+                     | (3 << _EMU_EM4CTRL_EM4ENTRY_SHIFT);
+#else
+  uint32_t em4seq2 = (EMU->CTRL & ~_EMU_CTRL_EM4CTRL_MASK)
+                     | (2 << _EMU_CTRL_EM4CTRL_SHIFT);
+  uint32_t em4seq3 = (EMU->CTRL & ~_EMU_CTRL_EM4CTRL_MASK)
+                     | (3 << _EMU_CTRL_EM4CTRL_SHIFT);
+#endif
 
   /* Make sure register write lock is disabled */
   EMU_Unlock();
 
-#if defined(ERRATA_FIX_EMU_E108_EN)
+#if defined( ERRATA_FIX_EMU_E108_EN )
   /* Fix for errata EMU_E108 - High Current Consumption on EM4 Entry. */
   __disable_irq();
   *(volatile uint32_t *)0x400C80E4 = 0;
 #endif
 
+#if defined( _EMU_DCDCCTRL_MASK )
+  dcdcFetCntSet(true);
+  dcdcHsFixLnBlock();
+#endif
+
   for (i = 0; i < 4; i++)
   {
+#if defined( _EMU_EM4CTRL_EM4ENTRY_SHIFT )
+    EMU->EM4CTRL = em4seq2;
+    EMU->EM4CTRL = em4seq3;
+  }
+  EMU->EM4CTRL = em4seq2;
+#else
     EMU->CTRL = em4seq2;
     EMU->CTRL = em4seq3;
   }
   EMU->CTRL = em4seq2;
+#endif
 }
 
 
@@ -502,7 +676,7 @@
  * @param[in] blocks
  *   Specifies a logical OR of bits indicating memory blocks to power down.
  *   Bit 0 selects block 1, bit 1 selects block 2, etc. Memory block 0 cannot
- *   be disabled. Please refer to the EFM32 reference manual for available
+ *   be disabled. Please refer to the reference manual for available
  *   memory blocks for a device.
  *
  * @note
@@ -512,10 +686,28 @@
  ******************************************************************************/
 void EMU_MemPwrDown(uint32_t blocks)
 {
-#if defined(_EMU_MEMCTRL_RESETVALUE)
-  EFM_ASSERT(blocks <= _EMU_MEMCTRL_MASK);
+#if defined( _EMU_MEMCTRL_POWERDOWN_MASK )
+  EFM_ASSERT(blocks <= (_EMU_MEMCTRL_POWERDOWN_MASK
+                        >> _EMU_MEMCTRL_POWERDOWN_SHIFT));
+  EMU->MEMCTRL = blocks;
 
+#elif defined( _EMU_MEMCTRL_RAMPOWERDOWN_MASK )       \
+      && defined( _EMU_MEMCTRL_RAMHPOWERDOWN_MASK )   \
+      && defined( _EMU_MEMCTRL_SEQRAMPOWERDOWN_MASK )
+  EFM_ASSERT((blocks & (_EMU_MEMCTRL_RAMPOWERDOWN_MASK
+                        | _EMU_MEMCTRL_RAMHPOWERDOWN_MASK
+                        | _EMU_MEMCTRL_SEQRAMPOWERDOWN_MASK))
+             == blocks);
   EMU->MEMCTRL = blocks;
+
+#elif defined( _EMU_MEMCTRL_RAMPOWERDOWN_MASK )
+  EFM_ASSERT((blocks & _EMU_MEMCTRL_RAMPOWERDOWN_MASK) == blocks);
+  EMU->MEMCTRL = blocks;
+
+#elif defined( _EMU_RAM0CTRL_RAMPOWERDOWN_MASK )
+  EFM_ASSERT((blocks & _EMU_RAM0CTRL_RAMPOWERDOWN_MASK) == blocks);
+  EMU->RAM0CTRL = blocks;
+
 #else
   (void)blocks;
 #endif
@@ -545,10 +737,12 @@
 {
   /* Fetch current configuration */
   cmuStatus = CMU->STATUS;
+#if defined( _CMU_HFCLKSTATUS_RESETVALUE )
+  cmuHfclkStatus = (uint16_t)(CMU->HFCLKSTATUS);
+#endif
 }
 
 
-#if defined( _EMU_CTRL_EMVREG_MASK ) || defined( _EMU_CTRL_EM23VREG_MASK )
 /***************************************************************************//**
  * @brief
  *   Update EMU module with Energy Mode 2 and 3 configuration
@@ -559,15 +753,18 @@
 void EMU_EM23Init(EMU_EM23Init_TypeDef *em23Init)
 {
 #if defined( _EMU_CTRL_EMVREG_MASK )
-  EMU->CTRL = em23Init->em23Vreg ? (EMU->CTRL | EMU_CTRL_EMVREG) : (EMU->CTRL & ~EMU_CTRL_EMVREG);
+  EMU->CTRL = em23Init->em23VregFullEn ? (EMU->CTRL | EMU_CTRL_EMVREG)
+                                         : (EMU->CTRL & ~EMU_CTRL_EMVREG);
 #elif defined( _EMU_CTRL_EM23VREG_MASK )
-  EMU->CTRL = em23Init->em23Vreg ? (EMU->CTRL | EMU_CTRL_EM23VREG) : (EMU->CTRL & ~EMU_CTRL_EM23VREG);
+  EMU->CTRL = em23Init->em23VregFullEn ? (EMU->CTRL | EMU_CTRL_EM23VREG)
+                                         : (EMU->CTRL & ~EMU_CTRL_EM23VREG);
+#else
+  (void)em23Init;
 #endif
 }
-#endif
 
 
-#if defined( _EMU_EM4CONF_MASK )
+#if defined( _EMU_EM4CONF_MASK ) || defined( _EMU_EM4CTRL_MASK )
 /***************************************************************************//**
  * @brief
  *   Update EMU module with Energy Mode 4 configuration
@@ -577,30 +774,49 @@
  ******************************************************************************/
 void EMU_EM4Init(EMU_EM4Init_TypeDef *em4Init)
 {
+#if defined( _EMU_EM4CONF_MASK )
+  /* Init for platforms with EMU->EM4CONF register */
   uint32_t em4conf = EMU->EM4CONF;
 
   /* Clear fields that will be reconfigured */
-  em4conf &= ~(
-    _EMU_EM4CONF_LOCKCONF_MASK |
-    _EMU_EM4CONF_OSC_MASK |
-    _EMU_EM4CONF_BURTCWU_MASK |
-    _EMU_EM4CONF_VREGEN_MASK);
+  em4conf &= ~(_EMU_EM4CONF_LOCKCONF_MASK
+               | _EMU_EM4CONF_OSC_MASK
+               | _EMU_EM4CONF_BURTCWU_MASK
+               | _EMU_EM4CONF_VREGEN_MASK);
 
   /* Configure new settings */
-  em4conf |= (
-    (em4Init->lockConfig << _EMU_EM4CONF_LOCKCONF_SHIFT) |
-    (em4Init->osc) |
-    (em4Init->buRtcWakeup << _EMU_EM4CONF_BURTCWU_SHIFT) |
-    (em4Init->vreg << _EMU_EM4CONF_VREGEN_SHIFT));
+  em4conf |= (em4Init->lockConfig << _EMU_EM4CONF_LOCKCONF_SHIFT)
+             | (em4Init->osc)
+             | (em4Init->buRtcWakeup << _EMU_EM4CONF_BURTCWU_SHIFT)
+             | (em4Init->vreg << _EMU_EM4CONF_VREGEN_SHIFT);
 
   /* Apply configuration. Note that lock can be set after this stage. */
   EMU->EM4CONF = em4conf;
+
+#elif defined( _EMU_EM4CTRL_MASK )
+  /* Init for platforms with EMU->EM4CTRL register */
+
+  uint32_t em4ctrl = EMU->EM4CTRL;
+
+  em4ctrl &= ~(_EMU_EM4CTRL_RETAINLFXO_MASK
+               | _EMU_EM4CTRL_RETAINLFRCO_MASK
+               | _EMU_EM4CTRL_RETAINULFRCO_MASK
+               | _EMU_EM4CTRL_EM4STATE_MASK
+               | _EMU_EM4CTRL_EM4IORETMODE_MASK);
+
+     em4ctrl |= (em4Init->retainLfxo     ? EMU_EM4CTRL_RETAINLFXO : 0)
+                | (em4Init->retainLfrco  ? EMU_EM4CTRL_RETAINLFRCO : 0)
+                | (em4Init->retainUlfrco ? EMU_EM4CTRL_RETAINULFRCO : 0)
+                | (em4Init->em4State     ? EMU_EM4CTRL_EM4STATE_EM4H : 0)
+                | (em4Init->pinRetentionMode);
+
+  EMU->EM4CTRL = em4ctrl;
+#endif
 }
 #endif
 
 
 #if defined( BU_PRESENT )
-
 /***************************************************************************//**
  * @brief
  *   Configure Backup Power Domain settings
@@ -613,16 +829,15 @@
   uint32_t reg;
 
   /* Set power connection configuration */
-  reg = EMU->PWRCONF & ~(
-    _EMU_PWRCONF_PWRRES_MASK|
-    _EMU_PWRCONF_VOUTSTRONG_MASK|
-    _EMU_PWRCONF_VOUTMED_MASK|
-    _EMU_PWRCONF_VOUTWEAK_MASK);
+  reg = 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));
+  reg |= bupdInit->resistor
+         | (bupdInit->voutStrong << _EMU_PWRCONF_VOUTSTRONG_SHIFT)
+         | (bupdInit->voutMed    << _EMU_PWRCONF_VOUTMED_SHIFT)
+         | (bupdInit->voutWeak   << _EMU_PWRCONF_VOUTWEAK_SHIFT);
 
   EMU->PWRCONF = reg;
 
@@ -637,18 +852,17 @@
   EMU->BUACT = reg;
 
   /* Set power control configuration */
-  reg = EMU->BUCTRL & ~(
-    _EMU_BUCTRL_PROBE_MASK|
-    _EMU_BUCTRL_BODCAL_MASK|
-    _EMU_BUCTRL_STATEN_MASK|
-    _EMU_BUCTRL_EN_MASK);
+  reg = EMU->BUCTRL & ~(_EMU_BUCTRL_PROBE_MASK
+                        | _EMU_BUCTRL_BODCAL_MASK
+                        | _EMU_BUCTRL_STATEN_MASK
+                        | _EMU_BUCTRL_EN_MASK);
 
   /* Note use of ->enable to both enable BUPD, use BU_VIN pin input and
      release reset */
-  reg |= (bupdInit->probe|
-         (bupdInit->bodCal          << _EMU_BUCTRL_BODCAL_SHIFT)|
-         (bupdInit->statusPinEnable << _EMU_BUCTRL_STATEN_SHIFT)|
-         (bupdInit->enable          << _EMU_BUCTRL_EN_SHIFT));
+  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;
@@ -657,7 +871,7 @@
   EMU_BUPinEnable(bupdInit->enable);
 
   /* If enable is true, release BU reset, if not keep reset asserted */
-  BITBAND_Peripheral(&(RMU->CTRL), _RMU_CTRL_BURSTEN_SHIFT, !bupdInit->enable);
+  BUS_RegBitWrite(&(RMU->CTRL), _RMU_CTRL_BURSTEN_SHIFT, !bupdInit->enable);
 }
 
 
@@ -671,16 +885,19 @@
  ******************************************************************************/
 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;
+    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;
   }
 }
 
@@ -695,22 +912,894 @@
  ******************************************************************************/
 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;
+    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( _EMU_DCDCCTRL_MASK )
+
+/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
+
+/***************************************************************************//**
+ * @brief
+ *   Load DCDC calibration constants from DI page. Const means calibration
+ *   data that does not change depending on other configuration parameters.
+ *
+ * @return
+ *   False if calibration registers are locked
+ ******************************************************************************/
+static bool ConstCalibrationLoad(void)
+{
+  uint32_t val;
+  volatile uint32_t *reg;
+
+  /* DI calib data in flash */
+  volatile uint32_t* const diCal_EMU_DCDCLNFREQCTRL =  (volatile uint32_t *)(0x0FE08038);
+  volatile uint32_t* const diCal_EMU_DCDCLNVCTRL =     (volatile uint32_t *)(0x0FE08040);
+  volatile uint32_t* const diCal_EMU_DCDCLPCTRL =      (volatile uint32_t *)(0x0FE08048);
+  volatile uint32_t* const diCal_EMU_DCDCLPVCTRL =     (volatile uint32_t *)(0x0FE08050);
+  volatile uint32_t* const diCal_EMU_DCDCTRIM0 =       (volatile uint32_t *)(0x0FE08058);
+  volatile uint32_t* const diCal_EMU_DCDCTRIM1 =       (volatile uint32_t *)(0x0FE08060);
+
+  if (DEVINFO->DCDCLPVCTRL0 != UINT_MAX)
+  {
+    val = *(diCal_EMU_DCDCLNFREQCTRL + 1);
+    reg = (volatile uint32_t *)*diCal_EMU_DCDCLNFREQCTRL;
+    *reg = val;
+
+    val = *(diCal_EMU_DCDCLNVCTRL + 1);
+    reg = (volatile uint32_t *)*diCal_EMU_DCDCLNVCTRL;
+    *reg = val;
+
+    val = *(diCal_EMU_DCDCLPCTRL + 1);
+    reg = (volatile uint32_t *)*diCal_EMU_DCDCLPCTRL;
+    *reg = val;
+
+    val = *(diCal_EMU_DCDCLPVCTRL + 1);
+    reg = (volatile uint32_t *)*diCal_EMU_DCDCLPVCTRL;
+    *reg = val;
+
+    val = *(diCal_EMU_DCDCTRIM0 + 1);
+    reg = (volatile uint32_t *)*diCal_EMU_DCDCTRIM0;
+    *reg = val;
+
+    val = *(diCal_EMU_DCDCTRIM1 + 1);
+    reg = (volatile uint32_t *)*diCal_EMU_DCDCTRIM1;
+    *reg = val;
+
+    return true;
+  }
+  EFM_ASSERT(false);
+  /* Return when assertions are disabled */
+  return false;
+}
+
+
+/***************************************************************************//**
+ * @brief
+ *   Set recommended and validated current optimization settings
+ *
+ ******************************************************************************/
+void ValidatedConfigSet(void)
+{
+#define EMU_DCDCSMCTRL  (* (volatile uint32_t *)(EMU_BASE + 0x44))
+
+  uint32_t dcdcTiming;
+  SYSTEM_PartFamily_TypeDef family;
+  SYSTEM_ChipRevision_TypeDef rev;
+
+  /* Enable duty cycling of the bias */
+  EMU->DCDCLPCTRL |= EMU_DCDCLPCTRL_LPVREFDUTYEN;
+
+  /* Set low-noise RCO for EFM32 and EFR32 */
+#if defined( _EFR_DEVICE )
+  /* 7MHz is recommended for all EFR32 parts with DCDC */
+  EMU->DCDCLNFREQCTRL = (EMU->DCDCLNFREQCTRL & ~_EMU_DCDCLNFREQCTRL_RCOBAND_MASK)
+                          | (EMU_DcdcLnRcoBand_7MHz << _EMU_DCDCLNFREQCTRL_RCOBAND_SHIFT);
+#else
+  /* 3MHz is recommended for all EFM32 parts with DCDC */
+  EMU->DCDCLNFREQCTRL = (EMU->DCDCLNFREQCTRL & ~_EMU_DCDCLNFREQCTRL_RCOBAND_MASK)
+                          | (EMU_DcdcLnRcoBand_3MHz << _EMU_DCDCLNFREQCTRL_RCOBAND_SHIFT);
+#endif
+
+  EMU->DCDCTIMING &= ~_EMU_DCDCTIMING_DUTYSCALE_MASK;
+
+  family = SYSTEM_GetFamily();
+  SYSTEM_ChipRevisionGet(&rev);
+  if ((((family >= systemPartFamilyMighty1P)
+         && (family <= systemPartFamilyFlex1V))
+       || (family == systemPartFamilyEfm32Pearl1B)
+       || (family == systemPartFamilyEfm32Jade1B))
+      && ((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;
   }
 }
 
+
+/***************************************************************************//**
+ * @brief
+ *   Calculate and update EMU->DCDCMISCCTRL for maximum DCDC current based
+ *   on the slice configuration and user set maximum.
+ ******************************************************************************/
+static void maxCurrentUpdate(void)
+{
+  uint32_t lncLimImSel;
+  uint32_t lpcLimImSel;
+  uint32_t pFetCnt;
+
+  pFetCnt = (EMU->DCDCMISCCTRL & _EMU_DCDCMISCCTRL_PFETCNT_MASK)
+             >> _EMU_DCDCMISCCTRL_PFETCNT_SHIFT;
+
+  /* Equation from Reference Manual section 11.5.20, in the register
+     field description for LNCLIMILIMSEL and LPCLIMILIMSEL. */
+  lncLimImSel = (dcdcMaxCurrent_mA / (5 * (pFetCnt + 1))) - 1;
+  /* 80mA as recommended in Application Note AN0948 */
+  lpcLimImSel = (80 / (5 * (pFetCnt + 1))) - 1;
+
+  lncLimImSel <<= _EMU_DCDCMISCCTRL_LNCLIMILIMSEL_SHIFT;
+  lpcLimImSel <<= _EMU_DCDCMISCCTRL_LPCLIMILIMSEL_SHIFT;
+  EMU->DCDCMISCCTRL = (EMU->DCDCMISCCTRL & ~(_EMU_DCDCMISCCTRL_LNCLIMILIMSEL_MASK
+                                             | _EMU_DCDCMISCCTRL_LPCLIMILIMSEL_MASK))
+                       | (lncLimImSel | lpcLimImSel);
+}
+
+
+/***************************************************************************//**
+ * @brief
+ *   Set static variable that holds the user set maximum current. Update
+ *   DCDC configuration.
+ *
+ * @param[in] mAmaxCurrent
+ *   Maximum allowed current drawn by the DCDC from VREGVDD in mA.
+ ******************************************************************************/
+static void maxCurrentSet(uint32_t mAmaxCurrent)
+{
+  dcdcMaxCurrent_mA = mAmaxCurrent;
+  maxCurrentUpdate();
+}
+
+
+/***************************************************************************//**
+ * @brief
+ *   Load EMU_DCDCLPCTRL_LPCMPHYSSEL depending on LP bias, LP feedback
+ *   attenuation and DEVINFOREV.
+ *
+ * @param[in] attSet
+ *   LP feedback attenuation.
+ * @param[in] lpCmpBias
+ *   lpCmpBias selection
+ ******************************************************************************/
+static bool LpCmpHystCalibrationLoad(bool lpAttenuation, uint32_t lpCmpBias)
+{
+  uint8_t devinfoRev;
+  uint32_t lpcmpHystSel;
+
+  /* Get calib data revision */
+  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;
+    }
+  }
+  /* devinfoRev >= 4
+     Load LPCMPBIAS indexed calibration data */
+  else
+  {
+    lpcmpHystSel = DEVINFO->DCDCLPCMPHYSSEL1;
+    switch (lpCmpBias)
+    {
+      case _EMU_DCDCMISCCTRL_LPCMPBIAS_BIAS0:
+        lpcmpHystSel = (lpcmpHystSel & _DEVINFO_DCDCLPCMPHYSSEL1_LPCMPHYSSELLPCMPBIAS0_MASK)
+                        >> _DEVINFO_DCDCLPCMPHYSSEL1_LPCMPHYSSELLPCMPBIAS0_SHIFT;
+        break;
+
+      case _EMU_DCDCMISCCTRL_LPCMPBIAS_BIAS1:
+        lpcmpHystSel = (lpcmpHystSel & _DEVINFO_DCDCLPCMPHYSSEL1_LPCMPHYSSELLPCMPBIAS1_MASK)
+                        >> _DEVINFO_DCDCLPCMPHYSSEL1_LPCMPHYSSELLPCMPBIAS1_SHIFT;
+        break;
+
+      case _EMU_DCDCMISCCTRL_LPCMPBIAS_BIAS2:
+        lpcmpHystSel = (lpcmpHystSel & _DEVINFO_DCDCLPCMPHYSSEL1_LPCMPHYSSELLPCMPBIAS2_MASK)
+                        >> _DEVINFO_DCDCLPCMPHYSSEL1_LPCMPHYSSELLPCMPBIAS2_SHIFT;
+        break;
+
+      case _EMU_DCDCMISCCTRL_LPCMPBIAS_BIAS3:
+        lpcmpHystSel = (lpcmpHystSel & _DEVINFO_DCDCLPCMPHYSSEL1_LPCMPHYSSELLPCMPBIAS3_MASK)
+                        >> _DEVINFO_DCDCLPCMPHYSSEL1_LPCMPHYSSELLPCMPBIAS3_SHIFT;
+        break;
+
+      default:
+        EFM_ASSERT(false);
+        /* Return when assertions are disabled */
+        return false;
+    }
+  }
+
+  /* Make sure the sel value is within the field range. */
+  lpcmpHystSel <<= _EMU_DCDCLPCTRL_LPCMPHYSSEL_SHIFT;
+  if (lpcmpHystSel & ~_EMU_DCDCLPCTRL_LPCMPHYSSEL_MASK)
+  {
+    EFM_ASSERT(false);
+    /* Return when assertions are disabled */
+    return false;
+  }
+  EMU->DCDCLPCTRL = (EMU->DCDCLPCTRL & ~_EMU_DCDCLPCTRL_LPCMPHYSSEL_MASK) | lpcmpHystSel;
+
+  return true;
+}
+
+
+/** @endcond */
+
+/***************************************************************************//**
+ * @brief
+ *   Set DCDC regulator operating mode
+ *
+ * @param[in] dcdcMode
+ *   DCDC mode
+ ******************************************************************************/
+void EMU_DCDCModeSet(EMU_DcdcMode_TypeDef dcdcMode)
+{
+  while(EMU->DCDCSYNC & EMU_DCDCSYNC_DCDCCTRLBUSY);
+  BUS_RegBitWrite(&EMU->DCDCCLIMCTRL, _EMU_DCDCCLIMCTRL_BYPLIMEN_SHIFT, dcdcMode == emuDcdcMode_Bypass ? 0 : 1);
+  EMU->DCDCCTRL = (EMU->DCDCCTRL & ~_EMU_DCDCCTRL_DCDCMODE_MASK) | dcdcMode;
+}
+
+
+/***************************************************************************//**
+ * @brief
+ *   Configure DCDC regulator
+ *
+ * @note
+ *   Use the function EMU_DCDCPowerDown() to if the power circuit is configured
+ *   for NODCDC as decribed in Section 11.3.4.3 in the Reference Manual.
+ *
+ * @param[in] dcdcInit
+ *   DCDC initialization structure
+ *
+ * @return
+ *   True if initialization parameters are valid
+ ******************************************************************************/
+bool EMU_DCDCInit(EMU_DCDCInit_TypeDef *dcdcInit)
+{
+  uint32_t lpCmpBiasSel;
+
+  /* Set external power configuration. This enables writing to the other
+     DCDC registers. */
+  EMU->PWRCFG = dcdcInit->powerConfig;
+
+  /* EMU->PWRCFG is write-once and POR reset only. Check that
+     we could set the desired power configuration. */
+  if ((EMU->PWRCFG & _EMU_PWRCFG_PWRCFG_MASK) != dcdcInit->powerConfig)
+  {
+    /* If this assert triggers unexpectedly, please power cycle the
+       kit to reset the power configuration. */
+    EFM_ASSERT(false);
+    /* Return when assertions are disabled */
+    return false;
+  }
+
+  /* Load DCDC calibration data from the DI page */
+  ConstCalibrationLoad();
+
+  /* Check current parameters */
+  EFM_ASSERT(dcdcInit->maxCurrent_mA <= 200);
+  EFM_ASSERT(dcdcInit->em01LoadCurrent_mA <= dcdcInit->maxCurrent_mA);
+
+  /* DCDC low-noise supports max 200mA */
+  if (dcdcInit->dcdcMode == emuDcdcMode_LowNoise)
+  {
+    EFM_ASSERT(dcdcInit->em01LoadCurrent_mA <= 200);
+  }
+
+  /* EM2, 3 and 4 current above 100uA is not supported */
+  EFM_ASSERT(dcdcInit->em234LoadCurrent_uA <= 100);
+
+  /* Decode LP comparator bias for EM0/1 and EM2/3 */
+  lpCmpBiasSel  = EMU_DCDCMISCCTRL_LPCMPBIAS_BIAS1;
+  if (dcdcInit->em234LoadCurrent_uA <= 10)
+  {
+    lpCmpBiasSel  = EMU_DCDCMISCCTRL_LPCMPBIAS_BIAS0;
+  }
+
+  /* Set DCDC low-power mode comparator bias selection */
+  EMU->DCDCMISCCTRL = (EMU->DCDCMISCCTRL & ~(_EMU_DCDCMISCCTRL_LPCMPBIAS_MASK
+                                             | _EMU_DCDCMISCCTRL_LNFORCECCM_MASK))
+                       | ((uint32_t)lpCmpBiasSel
+                          | (uint32_t)dcdcInit->lnTransientMode);
+
+  /* Set recommended and validated current optimization settings */
+  ValidatedConfigSet();
+
+  /* Set the maximum current that the DCDC can draw from the power source */
+  maxCurrentSet(dcdcInit->maxCurrent_mA);
+
+  /* Optimize LN slice based on given load current estimate */
+  EMU_DCDCOptimizeSlice(dcdcInit->em01LoadCurrent_mA);
+
+  /* Set DCDC output voltage */
+  dcdcOutput_mVout = dcdcInit->mVout;
+  if (!EMU_DCDCOutputVoltageSet(dcdcOutput_mVout, true, true))
+  {
+    EFM_ASSERT(false);
+    /* Return when assertions are disabled */
+    return false;
+  }
+
+  /* Set EM0 DCDC operating mode. Output voltage set in EMU_DCDCOutputVoltageSet()
+     above takes effect if mode is changed from bypass here. */
+  EMU_DCDCModeSet(dcdcInit->dcdcMode);
+
+  /* Select analog peripheral power supply */
+  BUS_RegBitWrite(&EMU->PWRCTRL, _EMU_PWRCTRL_ANASW_SHIFT, dcdcInit->anaPeripheralPower ? 1 : 0);
+
+  return true;
+}
+
+
+/***************************************************************************//**
+ * @brief
+ *   Set DCDC output voltage
+ *
+ * @param[in] mV
+ *   Target DCDC output voltage in mV
+ *
+ * @return
+ *   True if the mV parameter is valid
+ ******************************************************************************/
+bool EMU_DCDCOutputVoltageSet(uint32_t mV,
+                              bool setLpVoltage,
+                              bool setLnVoltage)
+{
+#if defined( _DEVINFO_DCDCLNVCTRL0_3V0LNATT1_MASK )
+
+  bool validOutVoltage;
+  uint8_t lnMode;
+  bool attSet;
+  uint32_t attMask;
+  uint32_t vrefLow = 0;
+  uint32_t vrefHigh = 0;
+  uint32_t vrefVal = 0;
+  uint32_t mVlow = 0;
+  uint32_t mVhigh = 0;
+  uint32_t vrefShift;
+  uint32_t lpcmpBias;
+  volatile uint32_t* ctrlReg;
+
+  /* Check that the set voltage is within valid range.
+     Voltages are obtained from the datasheet. */
+  validOutVoltage = false;
+  if ((EMU->PWRCFG & _EMU_PWRCFG_PWRCFG_MASK) == EMU_PWRCFG_PWRCFG_DCDCTODVDD)
+  {
+    validOutVoltage = ((mV >= PWRCFG_DCDCTODVDD_VMIN)
+                       && (mV <= PWRCFG_DCDCTODVDD_VMAX));
+  }
+
+  if (!validOutVoltage)
+  {
+    EFM_ASSERT(false);
+    /* Return when assertions are disabled */
+    return false;
+  }
+
+  /* Populate both LP and LN registers, set control reg pointer and VREF shift. */
+  for (lnMode = 0; lnMode <= 1; lnMode++)
+  {
+    if (((lnMode == 0) && !setLpVoltage)
+        || ((lnMode == 1) && !setLnVoltage))
+    {
+      continue;
+    }
+
+    ctrlReg   = (lnMode ? &EMU->DCDCLNVCTRL : &EMU->DCDCLPVCTRL);
+    vrefShift = (lnMode ? _EMU_DCDCLNVCTRL_LNVREF_SHIFT
+                        : _EMU_DCDCLPVCTRL_LPVREF_SHIFT);
+
+    /* Set attenuation to use */
+    attSet = (mV > 1800);
+    if (attSet)
+    {
+      mVlow = 1800;
+      mVhigh = 3000;
+      attMask = (lnMode ? EMU_DCDCLNVCTRL_LNATT : EMU_DCDCLPVCTRL_LPATT);
+    }
+    else
+    {
+      mVlow = 1200;
+      mVhigh = 1800;
+      attMask = 0;
+    }
+
+    /* Get 2-point calib data from DEVINFO, calculate trimming and set voltege */
+    if (lnMode)
+    {
+      /* Set low-noise DCDC output voltage tuning */
+      if (attSet)
+      {
+        vrefLow  = DEVINFO->DCDCLNVCTRL0;
+        vrefHigh = (vrefLow & _DEVINFO_DCDCLNVCTRL0_3V0LNATT1_MASK)
+                   >> _DEVINFO_DCDCLNVCTRL0_3V0LNATT1_SHIFT;
+        vrefLow  = (vrefLow & _DEVINFO_DCDCLNVCTRL0_1V8LNATT1_MASK)
+                   >> _DEVINFO_DCDCLNVCTRL0_1V8LNATT1_SHIFT;
+      }
+      else
+      {
+        vrefLow  = DEVINFO->DCDCLNVCTRL0;
+        vrefHigh = (vrefLow & _DEVINFO_DCDCLNVCTRL0_1V8LNATT0_MASK)
+                   >> _DEVINFO_DCDCLNVCTRL0_1V8LNATT0_SHIFT;
+        vrefLow  = (vrefLow & _DEVINFO_DCDCLNVCTRL0_1V2LNATT0_MASK)
+                   >> _DEVINFO_DCDCLNVCTRL0_1V2LNATT0_SHIFT;
+      }
+    }
+    else
+    {
+      /* Set low-power DCDC output voltage tuning */
+
+      /* Get LPCMPBIAS and make sure masks are not overlayed */
+      lpcmpBias = EMU->DCDCMISCCTRL & _EMU_DCDCMISCCTRL_LPCMPBIAS_MASK;
+      EFM_ASSERT(!(_EMU_DCDCMISCCTRL_LPCMPBIAS_MASK & attMask));
+      switch (attMask | lpcmpBias)
+      {
+        case EMU_DCDCLPVCTRL_LPATT | EMU_DCDCMISCCTRL_LPCMPBIAS_BIAS0:
+          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 EMU_DCDCLPVCTRL_LPATT | EMU_DCDCMISCCTRL_LPCMPBIAS_BIAS1:
+          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 EMU_DCDCLPVCTRL_LPATT | EMU_DCDCMISCCTRL_LPCMPBIAS_BIAS2:
+          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 EMU_DCDCLPVCTRL_LPATT | EMU_DCDCMISCCTRL_LPCMPBIAS_BIAS3:
+          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 EMU_DCDCMISCCTRL_LPCMPBIAS_BIAS0:
+          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 EMU_DCDCMISCCTRL_LPCMPBIAS_BIAS1:
+          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 EMU_DCDCMISCCTRL_LPCMPBIAS_BIAS2:
+          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 EMU_DCDCMISCCTRL_LPCMPBIAS_BIAS3:
+          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;
+      }
+
+      /* Load LP comparator hysteresis calibration */
+      if(!(LpCmpHystCalibrationLoad(attSet, lpcmpBias >> _EMU_DCDCMISCCTRL_LPCMPBIAS_SHIFT)))
+      {
+        EFM_ASSERT(false);
+        /* Return when assertions are disabled */
+        return false;
+      }
+    } /* Low-nise / low-power mode */
+
+
+    /* Check for valid 2-point trim values */
+    if ((vrefLow == 0xFF) && (vrefHigh == 0xFF))
+    {
+      EFM_ASSERT(false);
+      /* Return when assertions are disabled */
+      return false;
+    }
+
+    /* Calculate and set voltage trim */
+    vrefVal = ((mV - mVlow) * (vrefHigh - vrefLow))  / (mVhigh - mVlow);
+    vrefVal += vrefLow;
+
+    /* Range check */
+    if ((vrefVal > vrefHigh) || (vrefVal < vrefLow))
+    {
+      EFM_ASSERT(false);
+      /* Return when assertions are disabled */
+      return false;
+    }
+
+    /* Update DCDCLNVCTRL/DCDCLPVCTRL */
+    *ctrlReg = (vrefVal << vrefShift) | attMask;
+  }
+#endif
+  return true;
+}
+
+
+/***************************************************************************//**
+ * @brief
+ *   Optimize DCDC slice count based on the estimated average load current
+ *   in EM0
+ *
+ * @param[in] mAEm0LoadCurrent
+ *   Estimated average EM0 load current in mA.
+ ******************************************************************************/
+void EMU_DCDCOptimizeSlice(uint32_t mAEm0LoadCurrent)
+{
+  uint32_t sliceCount = 0;
+  uint32_t rcoBand = (EMU->DCDCLNFREQCTRL & _EMU_DCDCLNFREQCTRL_RCOBAND_MASK)
+                      >> _EMU_DCDCLNFREQCTRL_RCOBAND_SHIFT;
+
+  /* Set recommended slice count */
+  if ((EMU->DCDCMISCCTRL & _EMU_DCDCMISCCTRL_LNFORCECCM_MASK) && (rcoBand >= EMU_DcdcLnRcoBand_5MHz))
+  {
+    if (mAEm0LoadCurrent < 20)
+    {
+      sliceCount = 4;
+    }
+    else if ((mAEm0LoadCurrent >= 20) && (mAEm0LoadCurrent < 40))
+    {
+      sliceCount = 8;
+    }
+    else
+    {
+      sliceCount = 16;
+    }
+  }
+  else if ((!(EMU->DCDCMISCCTRL & _EMU_DCDCMISCCTRL_LNFORCECCM_MASK)) && (rcoBand <= EMU_DcdcLnRcoBand_4MHz))
+  {
+    if (mAEm0LoadCurrent < 10)
+    {
+      sliceCount = 4;
+    }
+    else if ((mAEm0LoadCurrent >= 10) && (mAEm0LoadCurrent < 20))
+    {
+      sliceCount = 8;
+    }
+    else
+    {
+      sliceCount = 16;
+    }
+  }
+  else if ((EMU->DCDCMISCCTRL & _EMU_DCDCMISCCTRL_LNFORCECCM_MASK) && (rcoBand <= EMU_DcdcLnRcoBand_4MHz))
+  {
+    if (mAEm0LoadCurrent < 40)
+    {
+      sliceCount = 8;
+    }
+    else
+    {
+      sliceCount = 16;
+    }
+  }
+  else
+  {
+    /* This configuration is not recommended. EMU_DCDCInit() applies a recommended
+       configuration. */
+    EFM_ASSERT(false);
+  }
+
+  /* The selected silices are PSLICESEL + 1 */
+  sliceCount--;
+
+  /* Apply slice count to both N and P slice */
+  sliceCount = (sliceCount << _EMU_DCDCMISCCTRL_PFETCNT_SHIFT
+                | sliceCount << _EMU_DCDCMISCCTRL_NFETCNT_SHIFT);
+  EMU->DCDCMISCCTRL = (EMU->DCDCMISCCTRL & ~(_EMU_DCDCMISCCTRL_PFETCNT_MASK
+                                             | _EMU_DCDCMISCCTRL_NFETCNT_MASK))
+                      | sliceCount;
+
+  /* Update current limit configuration as it depends on the slice configuration. */
+  maxCurrentUpdate();
+}
+
+/***************************************************************************//**
+ * @brief
+ *   Set DCDC Low-noise RCO band.
+ *
+ * @param[in] band
+ *   RCO band to set.
+ ******************************************************************************/
+void EMU_DCDCLnRcoBandSet(EMU_DcdcLnRcoBand_TypeDef band)
+{
+  EMU->DCDCLNFREQCTRL = (EMU->DCDCLNFREQCTRL & ~_EMU_DCDCLNFREQCTRL_RCOBAND_MASK)
+                         | (band << _EMU_DCDCLNFREQCTRL_RCOBAND_SHIFT);
+}
+
+/***************************************************************************//**
+ * @brief
+ *   Power off the DCDC regulator.
+ *
+ * @details
+ *   This function powers off the DCDC controller. This function should only be
+ *   used if the external power circuit is wired for no DCDC. If the external power
+ *   circuit is wired for DCDC usage, then use EMU_DCDCInit() and set the
+ *   DCDC in bypass mode to disable DCDC.
+ *
+ * @return
+ *   Return false if the DCDC could not be disabled.
+ ******************************************************************************/
+bool EMU_DCDCPowerOff(void)
+{
+  /* Set power configuration to hard bypass */
+  EMU->PWRCFG = 0xF;
+  if ((EMU->PWRCFG & _EMU_PWRCFG_PWRCFG_MASK) != 0xF)
+  {
+    EFM_ASSERT(false);
+    /* Return when assertions are disabled */
+    return false;
+  }
+
+  /* Set DCDC to OFF and disable LP in EM2/3/4 */
+  EMU->DCDCCTRL = EMU_DCDCCTRL_DCDCMODE_OFF;
+  return true;
+}
 #endif
 
 
+#if defined( EMU_STATUS_VMONRDY )
+/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
+__STATIC_INLINE uint32_t vmonMilliVoltToCoarseThreshold(int mV)
+{
+  return (mV - 1200) / 200;
+}
+
+__STATIC_INLINE uint32_t vmonMilliVoltToFineThreshold(int mV, uint32_t coarseThreshold)
+{
+  return (mV - 1200 - (coarseThreshold * 200)) / 20;
+}
+/** @endcond */
+
+/***************************************************************************//**
+ * @brief
+ *   Initialize VMON channel.
+ *
+ * @details
+ *   Initialize a VMON channel without hysteresis. If the channel supports
+ *   separate rise and fall triggers, both thresholds will be set to the same
+ *   value.
+ *
+ * @param[in] vmonInit
+ *   VMON initialization struct
+ ******************************************************************************/
+void EMU_VmonInit(EMU_VmonInit_TypeDef *vmonInit)
+{
+  uint32_t thresholdCoarse, thresholdFine;
+  EFM_ASSERT((vmonInit->threshold >= 1200) && (vmonInit->threshold <= 3980));
+
+  thresholdCoarse = vmonMilliVoltToCoarseThreshold(vmonInit->threshold);
+  thresholdFine = vmonMilliVoltToFineThreshold(vmonInit->threshold, thresholdCoarse);
+
+  switch(vmonInit->channel)
+  {
+  case emuVmonChannel_AVDD:
+    EMU->VMONAVDDCTRL = (thresholdCoarse << _EMU_VMONAVDDCTRL_RISETHRESCOARSE_SHIFT)
+                      | (thresholdFine << _EMU_VMONAVDDCTRL_RISETHRESFINE_SHIFT)
+                      | (thresholdCoarse << _EMU_VMONAVDDCTRL_FALLTHRESCOARSE_SHIFT)
+                      | (thresholdFine << _EMU_VMONAVDDCTRL_FALLTHRESFINE_SHIFT)
+                      | (vmonInit->riseWakeup ? EMU_VMONAVDDCTRL_RISEWU : 0)
+                      | (vmonInit->fallWakeup ? EMU_VMONAVDDCTRL_FALLWU : 0)
+                      | (vmonInit->enable     ? EMU_VMONAVDDCTRL_EN     : 0);
+    break;
+  case emuVmonChannel_ALTAVDD:
+    EMU->VMONALTAVDDCTRL = (thresholdCoarse << _EMU_VMONALTAVDDCTRL_THRESCOARSE_SHIFT)
+                         | (thresholdFine << _EMU_VMONALTAVDDCTRL_THRESFINE_SHIFT)
+                         | (vmonInit->riseWakeup ? EMU_VMONALTAVDDCTRL_RISEWU : 0)
+                         | (vmonInit->fallWakeup ? EMU_VMONALTAVDDCTRL_FALLWU : 0)
+                         | (vmonInit->enable     ? EMU_VMONALTAVDDCTRL_EN     : 0);
+    break;
+  case emuVmonChannel_DVDD:
+    EMU->VMONDVDDCTRL = (thresholdCoarse << _EMU_VMONDVDDCTRL_THRESCOARSE_SHIFT)
+                      | (thresholdFine << _EMU_VMONDVDDCTRL_THRESFINE_SHIFT)
+                      | (vmonInit->riseWakeup ? EMU_VMONDVDDCTRL_RISEWU : 0)
+                      | (vmonInit->fallWakeup ? EMU_VMONDVDDCTRL_FALLWU : 0)
+                      | (vmonInit->enable     ? EMU_VMONDVDDCTRL_EN     : 0);
+    break;
+  case emuVmonChannel_IOVDD0:
+    EMU->VMONIO0CTRL = (thresholdCoarse << _EMU_VMONIO0CTRL_THRESCOARSE_SHIFT)
+                     | (thresholdFine << _EMU_VMONIO0CTRL_THRESFINE_SHIFT)
+                     | (vmonInit->retDisable ? EMU_VMONIO0CTRL_RETDIS : 0)
+                     | (vmonInit->riseWakeup ? EMU_VMONIO0CTRL_RISEWU : 0)
+                     | (vmonInit->fallWakeup ? EMU_VMONIO0CTRL_FALLWU : 0)
+                     | (vmonInit->enable     ? EMU_VMONIO0CTRL_EN     : 0);
+    break;
+  default:
+    EFM_ASSERT(false);
+    return;
+  }
+}
+
+/***************************************************************************//**
+ * @brief
+ *   Initialize VMON channel with hysteresis (separate rise and fall triggers).
+ *
+ * @details
+ *   Initialize a VMON channel which supports hysteresis. The AVDD channel is
+ *   the only channel to support separate rise and fall triggers.
+ *
+ * @param[in] vmonInit
+ *   VMON Hysteresis initialization struct
+ ******************************************************************************/
+void EMU_VmonHystInit(EMU_VmonHystInit_TypeDef *vmonInit)
+{
+  uint32_t riseThresholdCoarse, riseThresholdFine, fallThresholdCoarse, fallThresholdFine;
+  /* VMON supports voltages between 1200 mV and 3980 mV (inclusive) in 20 mV increments */
+  EFM_ASSERT((vmonInit->riseThreshold >= 1200) && (vmonInit->riseThreshold < 4000));
+  EFM_ASSERT((vmonInit->fallThreshold >= 1200) && (vmonInit->fallThreshold < 4000));
+  /* Fall threshold has to be lower than rise threshold */
+  EFM_ASSERT(vmonInit->fallThreshold <= vmonInit->riseThreshold);
+
+  riseThresholdCoarse = vmonMilliVoltToCoarseThreshold(vmonInit->riseThreshold);
+  riseThresholdFine = vmonMilliVoltToFineThreshold(vmonInit->riseThreshold, riseThresholdCoarse);
+  fallThresholdCoarse = vmonMilliVoltToCoarseThreshold(vmonInit->fallThreshold);
+  fallThresholdFine = vmonMilliVoltToFineThreshold(vmonInit->fallThreshold, fallThresholdCoarse);
+
+  switch(vmonInit->channel)
+  {
+  case emuVmonChannel_AVDD:
+    EMU->VMONAVDDCTRL = (riseThresholdCoarse << _EMU_VMONAVDDCTRL_RISETHRESCOARSE_SHIFT)
+                      | (riseThresholdFine << _EMU_VMONAVDDCTRL_RISETHRESFINE_SHIFT)
+                      | (fallThresholdCoarse << _EMU_VMONAVDDCTRL_FALLTHRESCOARSE_SHIFT)
+                      | (fallThresholdFine << _EMU_VMONAVDDCTRL_FALLTHRESFINE_SHIFT)
+                      | (vmonInit->riseWakeup ? EMU_VMONAVDDCTRL_RISEWU : 0)
+                      | (vmonInit->fallWakeup ? EMU_VMONAVDDCTRL_FALLWU : 0)
+                      | (vmonInit->enable     ? EMU_VMONAVDDCTRL_EN     : 0);
+    break;
+  default:
+    EFM_ASSERT(false);
+    return;
+  }
+}
+
+/***************************************************************************//**
+ * @brief
+ *   Enable or disable a VMON channel
+ *
+ * @param[in] channel
+ *   VMON channel to enable/disable
+ *
+ * @param[in] enable
+ *   Whether to enable or disable
+ ******************************************************************************/
+void EMU_VmonEnable(EMU_VmonChannel_TypeDef channel, bool enable)
+{
+  uint32_t volatile * reg;
+  uint32_t bit;
+
+  switch(channel)
+  {
+  case emuVmonChannel_AVDD:
+    reg = &(EMU->VMONAVDDCTRL);
+    bit = _EMU_VMONAVDDCTRL_EN_SHIFT;
+    break;
+  case emuVmonChannel_ALTAVDD:
+    reg = &(EMU->VMONALTAVDDCTRL);
+    bit = _EMU_VMONALTAVDDCTRL_EN_SHIFT;
+    break;
+  case emuVmonChannel_DVDD:
+    reg = &(EMU->VMONDVDDCTRL);
+    bit = _EMU_VMONDVDDCTRL_EN_SHIFT;
+    break;
+  case emuVmonChannel_IOVDD0:
+    reg = &(EMU->VMONIO0CTRL);
+    bit = _EMU_VMONIO0CTRL_EN_SHIFT;
+    break;
+  default:
+    EFM_ASSERT(false);
+    return;
+  }
+
+  BUS_RegBitWrite(reg, bit, enable);
+}
+
+/***************************************************************************//**
+ * @brief
+ *   Get the status of a voltage monitor channel.
+ *
+ * @param[in] channel
+ *   VMON channel to get status for
+ *
+ * @return
+ *   Status of the selected VMON channel. True if channel is triggered.
+ ******************************************************************************/
+bool EMU_VmonChannelStatusGet(EMU_VmonChannel_TypeDef channel)
+{
+  uint32_t bit;
+  switch(channel)
+  {
+  case emuVmonChannel_AVDD:
+    bit = _EMU_STATUS_VMONAVDD_SHIFT;
+    break;
+  case emuVmonChannel_ALTAVDD:
+    bit = _EMU_STATUS_VMONALTAVDD_SHIFT;
+    break;
+  case emuVmonChannel_DVDD:
+    bit = _EMU_STATUS_VMONDVDD_SHIFT;
+    break;
+  case emuVmonChannel_IOVDD0:
+    bit = _EMU_STATUS_VMONIO0_SHIFT;
+    break;
+  default:
+    EFM_ASSERT(false);
+    bit = 0;
+  }
+
+  return BUS_RegBitRead(&EMU->STATUS, bit);
+}
+#endif /* EMU_STATUS_VMONRDY */
+
 /** @} (end addtogroup EMU) */
 /** @} (end addtogroup EM_Library) */
 #endif /* __EM_EMU_H */