/***************************************************************************//**
 * @file em_cmu.c
 * @brief Clock management unit (CMU) Peripheral API
 * @version 5.2.2
 *******************************************************************************
 * # License
 * <b>Copyright 2016 Silicon Laboratories, Inc. http://www.silabs.com</b>
 *******************************************************************************
 *
 * Permission is granted to anyone to use this software for any purpose,
 * including commercial applications, and to alter it and redistribute it
 * freely, subject to the following restrictions:
 *
 * 1. The origin of this software must not be misrepresented; you must not
 *    claim that you wrote the original software.
 * 2. Altered source versions must be plainly marked as such, and must not be
 *    misrepresented as being the original software.
 * 3. This notice may not be removed or altered from any source distribution.
 *
 * DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Silicon Labs has no
 * obligation to support this Software. Silicon Labs is providing the
 * Software "AS IS", with no express or implied warranties of any kind,
 * including, but not limited to, any implied warranties of merchantability
 * or fitness for any particular purpose or warranties against infringement
 * of any proprietary rights of a third party.
 *
 * Silicon Labs will not be liable for any consequential, incidental, or
 * special damages, or any other relief, or for any claim by any third party,
 * arising from your use of this Software.
 *
 ******************************************************************************/

#include "em_cmu.h"
#if defined(CMU_PRESENT)

#include <stddef.h>
#include <limits.h>
#include "em_assert.h"
#include "em_bus.h"
#include "em_emu.h"
#include "em_cmu.h"
#include "em_system.h"
#include "em_common.h"

/***************************************************************************//**
 * @addtogroup emlib
 * @{
 ******************************************************************************/

/***************************************************************************//**
 * @addtogroup CMU
 * @brief Clock management unit (CMU) Peripheral API
 * @details
 *  This module contains functions to control the CMU peripheral of Silicon
 *  Labs 32-bit MCUs and SoCs. The CMU controls oscillators and clocks.
 * @{
 ******************************************************************************/

/*******************************************************************************
 ******************************   DEFINES   ************************************
 ******************************************************************************/

/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */

#if defined(_SILICON_LABS_32B_SERIES_1)
/** Maximum allowed core frequency when using 0 wait-states on flash access. */
#define CMU_MAX_FREQ_0WS    26000000
/** Maximum allowed core frequency when using 1 wait-states on flash access */
#define CMU_MAX_FREQ_1WS    40000000
/** Maximum allowed core frequency when using 2 wait-states on flash access */
#define CMU_MAX_FREQ_2WS    54000000
/** Maximum allowed core frequency when using 3 wait-states on flash access */
#define CMU_MAX_FREQ_3WS    72000000
#elif defined(_SILICON_LABS_32B_SERIES_0)
/** Maximum allowed core frequency when using 0 wait-states on flash access. */
#define CMU_MAX_FREQ_0WS    16000000
/** Maximum allowed core frequency when using 1 wait-states on flash access */
#define CMU_MAX_FREQ_1WS    32000000
#else
#error "Max Flash wait-state frequencies are not defined for this platform."
#endif

/** Maximum frequency for HFLE interface */
#if defined(CMU_CTRL_HFLE)
/** Maximum HFLE frequency for series 0 EFM32 and EZR32 Wonder Gecko. */
#if defined(_SILICON_LABS_32B_SERIES_0) \
  && (defined(_EFM32_WONDER_FAMILY)     \
  || defined(_EZR32_WONDER_FAMILY))
#define CMU_MAX_FREQ_HFLE                       24000000
/** Maximum HFLE frequency for other series 0 parts with maximum core clock
    higher than 32MHz. */
#elif defined(_SILICON_LABS_32B_SERIES_0) \
  && (defined(_EFM32_GIANT_FAMILY)        \
  || defined(_EZR32_LEOPARD_FAMILY))
#define CMU_MAX_FREQ_HFLE                       maxFreqHfle()
#endif
#elif defined(CMU_CTRL_WSHFLE)
/** Maximum HFLE frequency for series 1 parts */
#define CMU_MAX_FREQ_HFLE                       32000000
#endif

#if defined(CMU_STATUS_HFXOSHUNTOPTRDY)
#define HFXO_TUNING_READY_FLAGS  (CMU_STATUS_HFXOPEAKDETRDY | CMU_STATUS_HFXOSHUNTOPTRDY)
#define HFXO_TUNING_MODE_AUTO    (_CMU_HFXOCTRL_PEAKDETSHUNTOPTMODE_AUTOCMD)
#define HFXO_TUNING_MODE_CMD     (_CMU_HFXOCTRL_PEAKDETSHUNTOPTMODE_CMD)
#elif defined(CMU_STATUS_HFXOPEAKDETRDY)
#define HFXO_TUNING_READY_FLAGS  (CMU_STATUS_HFXOPEAKDETRDY)
#define HFXO_TUNING_MODE_AUTO    (_CMU_HFXOCTRL_PEAKDETMODE_AUTOCMD)
#define HFXO_TUNING_MODE_CMD     (_CMU_HFXOCTRL_PEAKDETMODE_CMD)
#endif

#if defined(CMU_HFXOCTRL_MODE_EXTCLK)
/** HFXO external clock mode is renamed from EXTCLK to DIGEXTCLK. */
#define CMU_HFXOCTRL_MODE_DIGEXTCLK     CMU_HFXOCTRL_MODE_EXTCLK
#endif

#if defined(_EMU_CMD_EM01VSCALE0_MASK)
#define VSCALE_DEFAULT    (EMU_VScaleGet())
#else
#define VSCALE_DEFAULT    0
#endif

/*******************************************************************************
 **************************   LOCAL VARIABLES   ********************************
 ******************************************************************************/

#if defined(_CMU_AUXHFRCOCTRL_FREQRANGE_MASK)
static CMU_AUXHFRCOFreq_TypeDef auxHfrcoFreq = cmuAUXHFRCOFreq_19M0Hz;
#endif
#if defined(_CMU_STATUS_HFXOSHUNTOPTRDY_MASK)
#define HFXO_INVALID_TRIM   (~_CMU_HFXOTRIMSTATUS_MASK)
#endif

#if defined(CMU_OSCENCMD_DPLLEN)
/** Table of HFRCOCTRL values and their associated min/max frequencies and
    optional band enumerator. */
static const struct hfrcoCtrlTableElement{
  uint32_t              minFreq;
  uint32_t              maxFreq;
  uint32_t              value;
  CMU_HFRCOFreq_TypeDef band;
} hfrcoCtrlTable[] =
{
  // minFreq  maxFreq   HFRCOCTRL value  band
  {  860000, 1050000, 0xBC601F00, cmuHFRCOFreq_1M0Hz       },
  { 1050000, 1280000, 0xBC611F35, (CMU_HFRCOFreq_TypeDef)0 },
  { 1280000, 1480000, 0xBCA21F35, (CMU_HFRCOFreq_TypeDef)0 },
  { 1480000, 1800000, 0xAD231F35, (CMU_HFRCOFreq_TypeDef)0 },
  { 1800000, 2110000, 0xBA601F00, cmuHFRCOFreq_2M0Hz       },
  { 2110000, 2560000, 0xBA611F35, (CMU_HFRCOFreq_TypeDef)0 },
  { 2560000, 2970000, 0xBAA21F35, (CMU_HFRCOFreq_TypeDef)0 },
  { 2970000, 3600000, 0xAB231F35, (CMU_HFRCOFreq_TypeDef)0 },
  { 3600000, 4220000, 0xB8601F00, cmuHFRCOFreq_4M0Hz       },
  { 4220000, 5120000, 0xB8611F35, (CMU_HFRCOFreq_TypeDef)0 },
  { 5120000, 5930000, 0xB8A21F35, (CMU_HFRCOFreq_TypeDef)0 },
  { 5930000, 7520000, 0xA9231F00, cmuHFRCOFreq_7M0Hz       },
  { 7520000, 9520000, 0x99241F35, (CMU_HFRCOFreq_TypeDef)0 },
  { 9520000, 11800000, 0x99251F35, (CMU_HFRCOFreq_TypeDef)0 },
  { 11800000, 14400000, 0x99261F00, cmuHFRCOFreq_13M0Hz      },
  { 14400000, 17200000, 0x99271F00, cmuHFRCOFreq_16M0Hz      },
  { 17200000, 19700000, 0x99481F00, cmuHFRCOFreq_19M0Hz      },
  { 19700000, 23800000, 0x99491F35, (CMU_HFRCOFreq_TypeDef)0 },
  { 23800000, 28700000, 0x994A1F00, cmuHFRCOFreq_26M0Hz      },
  { 28700000, 34800000, 0x996B1F00, cmuHFRCOFreq_32M0Hz      },
#if defined(_DEVINFO_HFRCOCAL16_MASK)
  { 34800000, 42800000, 0x996C1F00, cmuHFRCOFreq_38M0Hz      },
  { 42800000, 51600000, 0x996D1F00, cmuHFRCOFreq_48M0Hz      },
  { 51600000, 60500000, 0x998E1F00, cmuHFRCOFreq_56M0Hz      },
  { 60500000, 72000000, 0xA98F1F00, cmuHFRCOFreq_64M0Hz      }
#else
  { 34800000, 40000000, 0x996C1F00, cmuHFRCOFreq_38M0Hz      }
#endif
};

#define HFRCOCTRLTABLE_ENTRIES (sizeof(hfrcoCtrlTable) \
                                / sizeof(struct hfrcoCtrlTableElement))
#endif // CMU_OSCENCMD_DPLLEN

#if defined(_SILICON_LABS_32B_SERIES_1) && defined(_EMU_STATUS_VSCALE_MASK)
/* Devices with Voltage Scaling needs extra handling of wait states. */
static const struct flashWsTableElement{
  uint32_t maxFreq;
  uint8_t  vscale;
  uint8_t  ws;
} flashWsTable[] =
{
#if (_SILICON_LABS_GECKO_INTERNAL_SDID == 100)
  { 18000000, 0, 0 },  /* 0 wait states at max frequency 18 MHz and 1.2V */
  { 36000000, 0, 1 },  /* 1 wait states at max frequency 36 MHz and 1.2V */
  { 54000000, 0, 2 },  /* 2 wait states at max frequency 54 MHz and 1.2V */
  { 72000000, 0, 3 },  /* 3 wait states at max frequency 72 MHz and 1.2V */
  { 7000000, 2, 0 },   /* 0 wait states at max frequency 7 MHz and 1.0V */
  { 14000000, 2, 1 },  /* 1 wait states at max frequency 14 MHz and 1.0V */
  { 21000000, 2, 2 },  /* 2 wait states at max frequency 21 MHz and 1.0V */
#else
  { 25000000, 0, 0 },  /* 0 wait states at max frequency 25 MHz and 1.2V */
  { 40000000, 0, 1 },  /* 1 wait states at max frequency 40 MHz and 1.2V */
  { 7000000, 2, 0 },   /* 0 wait states at max frequency 7 MHz and 1.0V */
  { 14000000, 2, 1 },  /* 1 wait states at max frequency 14 MHz and 1.0V */
  { 21000000, 2, 2 },  /* 2 wait states at max frequency 21 MHz and 1.0V */
#endif
};

#define FLASH_WS_TABLE_ENTRIES (sizeof(flashWsTable) / sizeof(flashWsTable[0]))
#endif

#if defined(_CMU_USHFRCOCTRL_FREQRANGE_MASK) \
  || defined(_CMU_USHFRCOTUNE_MASK)
#ifndef EFM32_USHFRCO_STARTUP_FREQ
#define EFM32_USHFRCO_STARTUP_FREQ        (48000000UL)
#endif

static uint32_t ushfrcoFreq = EFM32_USHFRCO_STARTUP_FREQ;
#endif

/*******************************************************************************
 **************************   LOCAL PROTOTYPES   *******************************
 ******************************************************************************/
#if defined(_CMU_HFRCOCTRL_FREQRANGE_MASK)
static uint32_t CMU_HFRCODevinfoGet(CMU_HFRCOFreq_TypeDef freq);
#endif

#if defined(_CMU_USHFRCOCTRL_FREQRANGE_MASK)
static uint32_t CMU_USHFRCODevinfoGet(CMU_USHFRCOFreq_TypeDef freq);
#endif

/** @endcond */

/*******************************************************************************
 **************************   LOCAL FUNCTIONS   ********************************
 ******************************************************************************/

/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */

#if defined(_SILICON_LABS_32B_SERIES_0) \
  && (defined(_EFM32_GIANT_FAMILY)      \
  || defined(_EZR32_LEOPARD_FAMILY))
/***************************************************************************//**
 * @brief
 *   Return max allowed frequency for low energy peripherals.
 ******************************************************************************/
static uint32_t maxFreqHfle(void)
{
  uint16_t majorMinorRev;

  switch (SYSTEM_GetFamily()) {
    case systemPartFamilyEfm32Leopard:
    case systemPartFamilyEzr32Leopard:
      /* CHIP MAJOR bit [5: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 (majorMinorRev >= 0x0204) {
        return 24000000;
      } else {
        return 32000000;
      }

    case systemPartFamilyEfm32Giant:
      return 32000000;

    default:
      /* Invalid device family. */
      EFM_ASSERT(false);
      return 0;
  }
}
#endif

#if defined(CMU_MAX_FREQ_HFLE)

/* Unified definitions for HFLE wait-state and prescaler fields. */
#if defined(CMU_CTRL_HFLE)
#define _GENERIC_HFLE_WS_MASK           _CMU_CTRL_HFLE_MASK
#define _GENERIC_HFLE_WS_SHIFT          _CMU_CTRL_HFLE_SHIFT
#define GENERIC_HFLE_PRESC_REG          CMU->HFCORECLKDIV
#define _GENERIC_HFLE_PRESC_MASK        _CMU_HFCORECLKDIV_HFCORECLKLEDIV_MASK
#define _GENERIC_HFLE_PRESC_SHIFT       _CMU_HFCORECLKDIV_HFCORECLKLEDIV_SHIFT
#elif defined(CMU_CTRL_WSHFLE)
#define _GENERIC_HFLE_WS_MASK           _CMU_CTRL_WSHFLE_MASK
#define _GENERIC_HFLE_WS_SHIFT          _CMU_CTRL_WSHFLE_SHIFT
#define GENERIC_HFLE_PRESC_REG          CMU->HFPRESC
#define _GENERIC_HFLE_PRESC_MASK        _CMU_HFPRESC_HFCLKLEPRESC_MASK
#define _GENERIC_HFLE_PRESC_SHIFT       _CMU_HFPRESC_HFCLKLEPRESC_SHIFT
#endif

/***************************************************************************//**
 * @brief
 *   Set HFLE wait-states and HFCLKLE prescaler.
 *
 * @param[in] maxLeFreq
 *   Max LE frequency
 ******************************************************************************/
static void setHfLeConfig(uint32_t hfFreq)
{
  unsigned int hfleWs;
  uint32_t hflePresc;

  /* Check for 1 bit fields. BUS_RegBitWrite() below are going to fail if the
     fields are changed to more than 1 bit. */
  EFM_ASSERT((_GENERIC_HFLE_WS_MASK >> _GENERIC_HFLE_WS_SHIFT) == 0x1);

  /* - Enable HFLE wait-state if to allow access to LE peripherals when HFBUSCLK is
       above maxLeFreq.
     - Set HFLE prescaler. Allowed HFLE clock frequency is maxLeFreq. */

  hfleWs = 1;
  if (hfFreq <= CMU_MAX_FREQ_HFLE) {
    hfleWs = 0;
    hflePresc = 0;
  } else if (hfFreq <= (2 * CMU_MAX_FREQ_HFLE)) {
    hflePresc = 1;
  } else {
    hflePresc = 2;
  }
  BUS_RegBitWrite(&CMU->CTRL, _GENERIC_HFLE_WS_SHIFT, hfleWs);
  GENERIC_HFLE_PRESC_REG = (GENERIC_HFLE_PRESC_REG & ~_GENERIC_HFLE_PRESC_MASK)
                           | (hflePresc << _GENERIC_HFLE_PRESC_SHIFT);
}

#if defined(_CMU_CTRL_HFLE_MASK)
/***************************************************************************//**
 * @brief
 *   Get HFLE wait-state configuration.
 *
 * @return
 *   Current wait-state configuration.
 ******************************************************************************/
static uint32_t getHfLeConfig(void)
{
  uint32_t ws = BUS_RegBitRead(&CMU->CTRL, _GENERIC_HFLE_WS_SHIFT);
  return ws;
}
#endif
#endif

/***************************************************************************//**
 * @brief
 *   Get the AUX clock frequency. Used by MSC flash programming and LESENSE,
 *   by default also as debug clock.
 *
 * @return
 *   AUX Frequency in Hz
 ******************************************************************************/
static uint32_t auxClkGet(void)
{
  uint32_t ret;

#if defined(_CMU_AUXHFRCOCTRL_FREQRANGE_MASK)
  ret = auxHfrcoFreq;

#elif defined(_CMU_AUXHFRCOCTRL_BAND_MASK)
  /* All series 0 families except EFM32G */
  switch (CMU->AUXHFRCOCTRL & _CMU_AUXHFRCOCTRL_BAND_MASK) {
    case CMU_AUXHFRCOCTRL_BAND_1MHZ:
      if ( SYSTEM_GetProdRev() >= 19 ) {
        ret = 1200000;
      } else {
        ret = 1000000;
      }
      break;

    case CMU_AUXHFRCOCTRL_BAND_7MHZ:
      if ( SYSTEM_GetProdRev() >= 19 ) {
        ret = 6600000;
      } else {
        ret = 7000000;
      }
      break;

    case CMU_AUXHFRCOCTRL_BAND_11MHZ:
      ret = 11000000;
      break;

    case CMU_AUXHFRCOCTRL_BAND_14MHZ:
      ret = 14000000;
      break;

    case CMU_AUXHFRCOCTRL_BAND_21MHZ:
      ret = 21000000;
      break;

#if defined(_CMU_AUXHFRCOCTRL_BAND_28MHZ)
    case CMU_AUXHFRCOCTRL_BAND_28MHZ:
      ret = 28000000;
      break;
#endif

    default:
      EFM_ASSERT(0);
      ret = 0;
      break;
  }

#else
  /* Gecko has a fixed 14Mhz AUXHFRCO clock */
  ret = 14000000;

#endif

  return ret;
}

#if defined (_CMU_ADCCTRL_ADC0CLKSEL_HFSRCCLK) \
  || defined (_CMU_ADCCTRL_ADC1CLKSEL_HFSRCCLK)
/***************************************************************************//**
 * @brief
 *   Get the HFSRCCLK frequency.
 *
 * @return
 *   HFSRCCLK Frequency in Hz
 ******************************************************************************/
static uint32_t hfSrcClkGet(void)
{
  uint32_t ret;

  ret = SystemHFClockGet();
  return ret * (1U + ((CMU->HFPRESC & _CMU_HFPRESC_PRESC_MASK)
                      >> _CMU_HFPRESC_PRESC_SHIFT));
}
#endif

/***************************************************************************//**
 * @brief
 *   Get the Debug Trace clock frequency
 *
 * @return
 *   Debug Trace frequency in Hz
 ******************************************************************************/
static uint32_t dbgClkGet(void)
{
  uint32_t ret;
  CMU_Select_TypeDef clk;

  /* Get selected clock source */
  clk = CMU_ClockSelectGet(cmuClock_DBG);

  switch (clk) {
    case cmuSelect_HFCLK:
      ret = SystemHFClockGet();
      break;

    case cmuSelect_AUXHFRCO:
      ret = auxClkGet();
      break;

    default:
      EFM_ASSERT(0);
      ret = 0;
      break;
  }
  return ret;
}

#if defined(_CMU_ADCCTRL_MASK)
/***************************************************************************//**
 * @brief
 *   Get the ADC n asynchronous clock frequency
 *
 * @return
 *   ADC n asynchronous frequency in Hz
 ******************************************************************************/
static uint32_t adcAsyncClkGet(uint32_t adc)
{
  uint32_t ret;
  CMU_Select_TypeDef clk;

  /* Get selected clock source */
  switch (adc) {
    case 0:
      clk = CMU_ClockSelectGet(cmuClock_ADC0ASYNC);
      break;

#if defined(_CMU_ADCCTRL_ADC1CLKSEL_MASK)
    case 1:
      clk = CMU_ClockSelectGet(cmuClock_ADC1ASYNC);
      break;
#endif

    default:
      EFM_ASSERT(0);
      return 0;
  }

  switch (clk) {
    case cmuSelect_Disabled:
      ret = 0;
      break;

    case cmuSelect_AUXHFRCO:
      ret = auxClkGet();
      break;

    case cmuSelect_HFXO:
      ret = SystemHFXOClockGet();
      break;

    case cmuSelect_HFSRCCLK:
      ret = hfSrcClkGet();
      break;

    default:
      EFM_ASSERT(0);
      ret = 0;
      break;
  }
  return ret;
}
#endif

#if defined(_CMU_SDIOCTRL_MASK)
/***************************************************************************//**
 * @brief
 *   Get the SDIO reference clock frequency
 *
 * @return
 *   SDIO reference clock frequency in Hz
 ******************************************************************************/
static uint32_t sdioRefClkGet(void)
{
  uint32_t ret;
  CMU_Select_TypeDef clk;

  /* Get selected clock source */
  clk = CMU_ClockSelectGet(cmuClock_SDIOREF);

  switch (clk) {
    case cmuSelect_HFRCO:
      ret = SystemHfrcoFreq;
      break;

    case cmuSelect_HFXO:
      ret = SystemHFXOClockGet();
      break;

    case cmuSelect_AUXHFRCO:
      ret = auxClkGet();
      break;

    case cmuSelect_USHFRCO:
      ret = ushfrcoFreq;
      break;

    default:
      EFM_ASSERT(0);
      ret = 0;
      break;
  }
  return ret;
}
#endif

#if defined(_CMU_QSPICTRL_MASK)
/***************************************************************************//**
 * @brief
 *   Get the QSPI n reference clock frequency
 *
 * @return
 *   QSPI n reference clock frequency in Hz
 ******************************************************************************/
static uint32_t qspiRefClkGet(uint32_t qspi)
{
  uint32_t ret;
  CMU_Select_TypeDef clk;

  /* Get selected clock source */
  switch (qspi) {
    case 0:
      clk = CMU_ClockSelectGet(cmuClock_QSPI0REF);
      break;

    default:
      EFM_ASSERT(0);
      return 0;
  }

  switch (clk) {
    case cmuSelect_HFRCO:
      ret = SystemHfrcoFreq;
      break;

    case cmuSelect_HFXO:
      ret = SystemHFXOClockGet();
      break;

    case cmuSelect_AUXHFRCO:
      ret = auxClkGet();
      break;

    case cmuSelect_USHFRCO:
      ret = ushfrcoFreq;
      break;

    default:
      EFM_ASSERT(0);
      ret = 0;
      break;
  }
  return ret;
}
#endif

#if defined(USBR_CLOCK_PRESENT)
/***************************************************************************//**
 * @brief
 *   Get the USB rate clock frequency
 *
 * @return
 *   USB rate clock frequency in Hz
 ******************************************************************************/
static uint32_t usbRateClkGet(void)
{
  uint32_t ret;
  CMU_Select_TypeDef clk;

  clk = CMU_ClockSelectGet(cmuClock_USBR);

  switch (clk) {
    case cmuSelect_USHFRCO:
      ret = ushfrcoFreq;
      break;

    case cmuSelect_HFXO:
      ret = SystemHFXOClockGet();
      break;

    case cmuSelect_HFXOX2:
      ret = 2u * SystemHFXOClockGet();
      break;

    case cmuSelect_HFRCO:
      ret = SystemHfrcoFreq;
      break;

    case cmuSelect_LFXO:
      ret = SystemLFXOClockGet();
      break;

    case cmuSelect_LFRCO:
      ret = SystemLFRCOClockGet();
      break;

    default:
      EFM_ASSERT(0);
      ret = 0;
      break;
  }
  return ret;
}
#endif

/***************************************************************************//**
 * @brief
 *   Configure flash access wait states in order to support given core clock
 *   frequency.
 *
 * @param[in] coreFreq
 *   Core clock frequency to configure flash wait-states for
 *
 * @param[in] vscale
 *   Voltage Scale level. Supported levels are 0 and 2 where 0 is the default.
 ******************************************************************************/
static void flashWaitStateControl(uint32_t coreFreq, int vscale)
{
  uint32_t mode;
  bool mscLocked;
#if defined(MSC_READCTRL_MODE_WS0SCBTP)
  bool scbtpEn;   /* Suppressed Conditional Branch Target Prefetch setting. */
#endif
  (void) vscale;  /* vscale parameter is only used on some devices */

  /* Make sure the MSC is unlocked */
  mscLocked = MSC->LOCK;
  MSC->LOCK = MSC_UNLOCK_CODE;

  /* Get mode and SCBTP enable */
  mode = MSC->READCTRL & _MSC_READCTRL_MODE_MASK;
#if defined(MSC_READCTRL_MODE_WS0SCBTP)
  /* Devices with MODE and SCBTP in same register field */
  switch (mode) {
    case MSC_READCTRL_MODE_WS0:
    case MSC_READCTRL_MODE_WS1:
#if defined(MSC_READCTRL_MODE_WS2)
    case MSC_READCTRL_MODE_WS2:
#endif
      scbtpEn = false;
      break;

    default: /* WSxSCBTP */
      scbtpEn = true;
      break;
  }

  /* Set mode based on the core clock frequency and SCBTP enable */
  if (false) {
  }
#if defined(MSC_READCTRL_MODE_WS2)
  else if (coreFreq > CMU_MAX_FREQ_1WS) {
    mode = (scbtpEn ? MSC_READCTRL_MODE_WS2SCBTP : MSC_READCTRL_MODE_WS2);
  }
#endif
  else if ((coreFreq <= CMU_MAX_FREQ_1WS) && (coreFreq > CMU_MAX_FREQ_0WS)) {
    mode = (scbtpEn ? MSC_READCTRL_MODE_WS1SCBTP : MSC_READCTRL_MODE_WS1);
  } else {
    mode = (scbtpEn ? MSC_READCTRL_MODE_WS0SCBTP : MSC_READCTRL_MODE_WS0);
  }

#elif defined(_SILICON_LABS_32B_SERIES_1) && defined(_EMU_STATUS_VSCALE_MASK)

  /* These devices have specific requirements on the supported flash wait state
   * depending on frequency and voltage scale level. */
  uint32_t i;
  for (i = 0; i < FLASH_WS_TABLE_ENTRIES; i++) {
    if ((flashWsTable[i].vscale == vscale)
        && (coreFreq <= flashWsTable[i].maxFreq)) {
      break; // found matching entry
    }
  }

  if (i == FLASH_WS_TABLE_ENTRIES) {
    EFM_ASSERT(false);
    mode = 3; // worst case flash wait state for unsupported cases
  } else {
    mode = flashWsTable[i].ws;
  }
  mode = mode << _MSC_READCTRL_MODE_SHIFT;

#else
  /* Devices where MODE and SCBTP are in separate fields and where the device
   * either does not support voltage scale or where the voltage scale does
   * not impact flash wait state configuration. */
  if (coreFreq <= CMU_MAX_FREQ_0WS) {
    mode = 0;
  } else if (coreFreq <= CMU_MAX_FREQ_1WS) {
    mode = 1;
  }
#if defined(MSC_READCTRL_MODE_WS2)
  else if (coreFreq <= CMU_MAX_FREQ_2WS) {
    mode = 2;
  }
#endif
#if defined(MSC_READCTRL_MODE_WS3)
  else if (coreFreq <= CMU_MAX_FREQ_3WS) {
    mode = 3;
  }
#endif
  mode = mode << _MSC_READCTRL_MODE_SHIFT;

#endif

  /* BUS_RegMaskedWrite cannot be used here as it would temporarily set the
     mode field to WS0 */
  MSC->READCTRL = (MSC->READCTRL & ~_MSC_READCTRL_MODE_MASK) | mode;

  if (mscLocked) {
    MSC->LOCK = 0;
  }
}

/***************************************************************************//**
 * @brief
 *   Configure flash access wait states to most conservative setting for
 *   this target. Retain SCBTP (Suppressed Conditional Branch Target Prefetch)
 *   setting.
 ******************************************************************************/
static void flashWaitStateMax(void)
{
  flashWaitStateControl(SystemMaxCoreClockGet(), 0);
}

#if defined(_MSC_RAMCTRL_RAMWSEN_MASK)
/***************************************************************************//**
 * @brief
 *   Configure RAM access wait states in order to support given core clock
 *   frequency.
 *
 * @param[in] coreFreq
 *   Core clock frequency to configure RAM wait-states for
 *
 * @param[in] vscale
 *   Voltage Scale level. Supported levels are 0 and 2 where 0 is the default.
 ******************************************************************************/
static void setRamWaitState(uint32_t coreFreq, int vscale)
{
  uint32_t limit = 38000000;
  if (vscale == 2) {
    limit = 16000000;
  }

  if (coreFreq > limit) {
    BUS_RegMaskedSet(&MSC->RAMCTRL, (MSC_RAMCTRL_RAMWSEN
                                     | MSC_RAMCTRL_RAM1WSEN
                                     | MSC_RAMCTRL_RAM2WSEN));
  } else {
    BUS_RegMaskedClear(&MSC->RAMCTRL, (MSC_RAMCTRL_RAMWSEN
                                       | MSC_RAMCTRL_RAM1WSEN
                                       | MSC_RAMCTRL_RAM2WSEN));
  }
}
#endif

#if defined(_MSC_CTRL_WAITMODE_MASK)
/***************************************************************************//**
 * @brief
 *   Configure wait state for peripheral accesses over the bus to support
 *   given bus clock frequency.
 *
 * @param[in] busFreq
 *   peripheral bus clock frequency to configure wait-states for
 *
 * @param[in] vscale
 *   The voltage scale to configure wait-states for. Expected values are
 *   0 or 2.
 *
 *   @li 0 = 1.2 V (VSCALE2)
 *   @li 2 = 1.0 V (VSCALE0)
 * ******************************************************************************/
static void setBusWaitState(uint32_t busFreq, int vscale)
{
  if ((busFreq > 50000000) && (vscale == 0)) {
    BUS_RegMaskedSet(&MSC->CTRL, MSC_CTRL_WAITMODE_WS1);
  } else {
    BUS_RegMaskedClear(&MSC->CTRL, MSC_CTRL_WAITMODE_WS1);
  }
}
#endif

/***************************************************************************//**
 * @brief
 *   Configure various wait states necessary to switch to a certain frequency
 *   and a certain voltage scale.
 *
 * @details
 *   This function will setup the necessary flash, bus and RAM wait states.
 *   Updating the wait state configuration must be done before
 *   increasing the clock frequency, and it must be done after decreasing the
 *   clock frequency. Updating the wait state configuration must be done before
 *   core voltage is decreased, and it must be done after a core voltage is
 *   increased.
 *
 * @param[in] coreFreq
 *   Core clock frequency to configure wait-states for.
 *
 * @param[in] vscale
 *   The voltage scale to configure wait-states for. Expected values are
 *   0 or 2, higher number is lower voltage.
 *
 *   @li 0 = 1.2 V (VSCALE2)
 *   @li 2 = 1.0 V (VSCALE0)
 *
 ******************************************************************************/
void CMU_UpdateWaitStates(uint32_t freq, int vscale)
{
  flashWaitStateControl(freq, vscale);
#if defined(_MSC_RAMCTRL_RAMWSEN_MASK)
  setRamWaitState(freq, vscale);
#endif
#if defined(_MSC_CTRL_WAITMODE_MASK)
  setBusWaitState(freq, vscale);
#endif
}

#if defined(_CMU_HFXOSTEADYSTATECTRL_REGISHUPPER_MASK)
/***************************************************************************//**
 * @brief
 *   Return upper value for CMU_HFXOSTEADYSTATECTRL_REGISH
 ******************************************************************************/
static uint32_t getRegIshUpperVal(uint32_t steadyStateRegIsh)
{
  uint32_t regIshUpper;
  const uint32_t upperMax = _CMU_HFXOSTEADYSTATECTRL_REGISHUPPER_MASK
                            >> _CMU_HFXOSTEADYSTATECTRL_REGISHUPPER_SHIFT;
  /* Add 3 as specified in register description for CMU_HFXOSTEADYSTATECTRL_REGISHUPPER. */
  regIshUpper = SL_MIN(steadyStateRegIsh + 3, upperMax);
  regIshUpper <<= _CMU_HFXOSTEADYSTATECTRL_REGISHUPPER_SHIFT;
  return regIshUpper;
}
#endif

#if defined(_CMU_HFXOCTRL_MASK)
/***************************************************************************//**
 * @brief
 *   Get the HFXO tuning mode
 *
 * @return
 *   The current HFXO tuning mode from the HFXOCTRL register.
 ******************************************************************************/
__STATIC_INLINE uint32_t getHfxoTuningMode(void)
{
#if defined(_CMU_HFXOCTRL_PEAKDETSHUNTOPTMODE_MASK)
  return (CMU->HFXOCTRL & _CMU_HFXOCTRL_PEAKDETSHUNTOPTMODE_MASK);
#else
  return (CMU->HFXOCTRL & _CMU_HFXOCTRL_PEAKDETMODE_MASK);
#endif
}

/***************************************************************************//**
 * @brief
 *   Set the HFXO tuning mode
 *
 * @param[in] mode
 *   the new HFXO tuning mode, this can be HFXO_TUNING_MODE_AUTO or
 *   HFXO_TUNING_MODE_CMD.
 ******************************************************************************/
__STATIC_INLINE void setHfxoTuningMode(uint32_t mode)
{
#if defined(_CMU_HFXOCTRL_PEAKDETSHUNTOPTMODE_MASK)
  CMU->HFXOCTRL = (CMU->HFXOCTRL & ~_CMU_HFXOCTRL_PEAKDETSHUNTOPTMODE_MASK) | mode;
#else
  CMU->HFXOCTRL = (CMU->HFXOCTRL & ~_CMU_HFXOCTRL_PEAKDETMODE_MASK) | mode;
#endif
}
#endif

/***************************************************************************//**
 * @brief
 *   Get the LFnCLK frequency based on current configuration.
 *
 * @param[in] lfClkBranch
 *   Selected LF branch
 *
 * @return
 *   The LFnCLK frequency in Hz. If no LFnCLK is selected (disabled), 0 is
 *   returned.
 ******************************************************************************/
static uint32_t lfClkGet(CMU_Clock_TypeDef lfClkBranch)
{
  uint32_t sel;
  uint32_t ret = 0;

  switch (lfClkBranch) {
    case cmuClock_LFA:
    case cmuClock_LFB:
#if defined(_CMU_LFCCLKEN0_MASK)
    case cmuClock_LFC:
#endif
#if defined(_CMU_LFECLKSEL_MASK)
    case cmuClock_LFE:
#endif
      break;

    default:
      EFM_ASSERT(0);
      break;
  }

  sel = CMU_ClockSelectGet(lfClkBranch);

  /* Get clock select field */
  switch (lfClkBranch) {
    case cmuClock_LFA:
#if defined(_CMU_LFCLKSEL_MASK)
      sel = (CMU->LFCLKSEL & _CMU_LFCLKSEL_LFA_MASK) >> _CMU_LFCLKSEL_LFA_SHIFT;
#elif defined(_CMU_LFACLKSEL_MASK)
      sel = (CMU->LFACLKSEL & _CMU_LFACLKSEL_LFA_MASK) >> _CMU_LFACLKSEL_LFA_SHIFT;
#else
      EFM_ASSERT(0);
#endif
      break;

    case cmuClock_LFB:
#if defined(_CMU_LFCLKSEL_MASK)
      sel = (CMU->LFCLKSEL & _CMU_LFCLKSEL_LFB_MASK) >> _CMU_LFCLKSEL_LFB_SHIFT;
#elif defined(_CMU_LFBCLKSEL_MASK)
      sel = (CMU->LFBCLKSEL & _CMU_LFBCLKSEL_LFB_MASK) >> _CMU_LFBCLKSEL_LFB_SHIFT;
#else
      EFM_ASSERT(0);
#endif
      break;

#if defined(_CMU_LFCCLKEN0_MASK)
    case cmuClock_LFC:
#if defined(_CMU_LFCLKSEL_LFC_MASK)
      sel = (CMU->LFCLKSEL & _CMU_LFCLKSEL_LFC_MASK) >> _CMU_LFCLKSEL_LFC_SHIFT;
#elif defined(_CMU_LFCCLKSEL_LFC_MASK)
      sel = (CMU->LFCCLKSEL & _CMU_LFCCLKSEL_LFC_MASK) >> _CMU_LFCCLKSEL_LFC_SHIFT;
#else
      EFM_ASSERT(0);
#endif
      break;
#endif

#if defined(_CMU_LFECLKSEL_MASK)
    case cmuClock_LFE:
      sel = (CMU->LFECLKSEL & _CMU_LFECLKSEL_LFE_MASK) >> _CMU_LFECLKSEL_LFE_SHIFT;
      break;
#endif

    default:
      EFM_ASSERT(0);
      break;
  }

  /* Get clock frequency */
#if defined(_CMU_LFCLKSEL_MASK)
  switch (sel) {
    case _CMU_LFCLKSEL_LFA_LFRCO:
      ret = SystemLFRCOClockGet();
      break;

    case _CMU_LFCLKSEL_LFA_LFXO:
      ret = SystemLFXOClockGet();
      break;

#if defined(_CMU_LFCLKSEL_LFA_HFCORECLKLEDIV2)
    case _CMU_LFCLKSEL_LFA_HFCORECLKLEDIV2:
#if defined(CMU_MAX_FREQ_HFLE)
      /* HFLE bit is or'ed by hardware with HFCORECLKLEDIV to reduce the
       * frequency of CMU_HFCORECLKLEDIV2. */
      ret = SystemCoreClockGet() / (1U << (getHfLeConfig() + 1));
#else
      ret = SystemCoreClockGet() / 2U;
#endif
      break;
#endif

    case _CMU_LFCLKSEL_LFA_DISABLED:
      ret = 0;
#if defined(CMU_LFCLKSEL_LFAE)
      /* Check LF Extended bit setting for LFA or LFB ULFRCO clock */
      if ((lfClkBranch == cmuClock_LFA) || (lfClkBranch == cmuClock_LFB)) {
        if (CMU->LFCLKSEL >> (lfClkBranch == cmuClock_LFA
                              ? _CMU_LFCLKSEL_LFAE_SHIFT
                              : _CMU_LFCLKSEL_LFBE_SHIFT)) {
          ret = SystemULFRCOClockGet();
        }
      }
#endif
      break;

    default:
      EFM_ASSERT(0);
      ret = 0U;
      break;
  }
#endif /* _CMU_LFCLKSEL_MASK */

#if defined(_CMU_LFACLKSEL_MASK)
  switch (sel) {
    case _CMU_LFACLKSEL_LFA_LFRCO:
      ret = SystemLFRCOClockGet();
      break;

    case _CMU_LFACLKSEL_LFA_LFXO:
      ret = SystemLFXOClockGet();
      break;

    case _CMU_LFACLKSEL_LFA_ULFRCO:
      ret = SystemULFRCOClockGet();
      break;

#if defined(CMU_LFACLKSEL_LFA_PLFRCO)
    case _CMU_LFACLKSEL_LFA_PLFRCO:
      ret = SystemLFRCOClockGet();
      break;
#endif

#if defined(_CMU_LFACLKSEL_LFA_HFCLKLE)
    case _CMU_LFACLKSEL_LFA_HFCLKLE:
      ret = SystemCoreClockGet()
            / CMU_Log2ToDiv(((CMU->HFPRESC & _CMU_HFPRESC_HFCLKLEPRESC_MASK)
                             >> _CMU_HFPRESC_HFCLKLEPRESC_SHIFT) + 1);
      break;
#elif defined(_CMU_LFBCLKSEL_LFB_HFCLKLE)
    case _CMU_LFBCLKSEL_LFB_HFCLKLE:
      ret = SystemCoreClockGet()
            / CMU_Log2ToDiv(((CMU->HFPRESC & _CMU_HFPRESC_HFCLKLEPRESC_MASK)
                             >> _CMU_HFPRESC_HFCLKLEPRESC_SHIFT) + 1);
      break;
#endif

    case _CMU_LFACLKSEL_LFA_DISABLED:
      ret = 0;
      break;
  }
#endif

  return ret;
}

/***************************************************************************//**
 * @brief
 *   Wait for ongoing sync of register(s) to low frequency domain to complete.
 *
 * @param[in] mask
 *   Bitmask corresponding to SYNCBUSY register defined bits, indicating
 *   registers that must complete any ongoing synchronization.
 ******************************************************************************/
__STATIC_INLINE void syncReg(uint32_t mask)
{
  /* Avoid deadlock if modifying the same register twice when freeze mode is */
  /* activated. */
  if (CMU->FREEZE & CMU_FREEZE_REGFREEZE) {
    return;
  }

  /* Wait for any pending previous write operation to have been completed */
  /* in low frequency domain */
  while (CMU->SYNCBUSY & mask) {
  }
}

#if defined(USBC_CLOCK_PRESENT)
/***************************************************************************//**
 * @brief
 *   Get the USBC frequency
 *
 * @return
 *   USBC frequency in Hz
 ******************************************************************************/
static uint32_t usbCClkGet(void)
{
  uint32_t ret;
  CMU_Select_TypeDef clk;

  /* Get selected clock source */
  clk = CMU_ClockSelectGet(cmuClock_USBC);

  switch (clk) {
    case cmuSelect_LFXO:
      ret = SystemLFXOClockGet();
      break;
    case cmuSelect_LFRCO:
      ret = SystemLFRCOClockGet();
      break;
#if defined (_CMU_USHFRCOCTRL_MASK)
    case cmuSelect_USHFRCO:
      ret = ushfrcoFreq;
      break;
#endif
    case cmuSelect_HFCLK:
      ret = SystemHFClockGet();
      break;
    default:
      /* Clock is not enabled */
      ret = 0;
      break;
  }
  return ret;
}
#endif

/** @endcond */

/*******************************************************************************
 **************************   GLOBAL FUNCTIONS   *******************************
 ******************************************************************************/

#if defined(_CMU_AUXHFRCOCTRL_BAND_MASK)
/***************************************************************************//**
 * @brief
 *   Get AUXHFRCO band in use.
 *
 * @return
 *   AUXHFRCO band in use.
 ******************************************************************************/
CMU_AUXHFRCOBand_TypeDef CMU_AUXHFRCOBandGet(void)
{
  return (CMU_AUXHFRCOBand_TypeDef)((CMU->AUXHFRCOCTRL
                                     & _CMU_AUXHFRCOCTRL_BAND_MASK)
                                    >> _CMU_AUXHFRCOCTRL_BAND_SHIFT);
}
#endif /* _CMU_AUXHFRCOCTRL_BAND_MASK */

#if defined(_CMU_AUXHFRCOCTRL_BAND_MASK)
/***************************************************************************//**
 * @brief
 *   Set AUXHFRCO band and the tuning value based on the value in the
 *   calibration table made during production.
 *
 * @param[in] band
 *   AUXHFRCO band to activate.
 ******************************************************************************/
void CMU_AUXHFRCOBandSet(CMU_AUXHFRCOBand_TypeDef band)
{
  uint32_t tuning;

  /* Read tuning value from calibration table */
  switch (band) {
    case cmuAUXHFRCOBand_1MHz:
      tuning = (DEVINFO->AUXHFRCOCAL0 & _DEVINFO_AUXHFRCOCAL0_BAND1_MASK)
               >> _DEVINFO_AUXHFRCOCAL0_BAND1_SHIFT;
      break;

    case cmuAUXHFRCOBand_7MHz:
      tuning = (DEVINFO->AUXHFRCOCAL0 & _DEVINFO_AUXHFRCOCAL0_BAND7_MASK)
               >> _DEVINFO_AUXHFRCOCAL0_BAND7_SHIFT;
      break;

    case cmuAUXHFRCOBand_11MHz:
      tuning = (DEVINFO->AUXHFRCOCAL0 & _DEVINFO_AUXHFRCOCAL0_BAND11_MASK)
               >> _DEVINFO_AUXHFRCOCAL0_BAND11_SHIFT;
      break;

    case cmuAUXHFRCOBand_14MHz:
      tuning = (DEVINFO->AUXHFRCOCAL0 & _DEVINFO_AUXHFRCOCAL0_BAND14_MASK)
               >> _DEVINFO_AUXHFRCOCAL0_BAND14_SHIFT;
      break;

    case cmuAUXHFRCOBand_21MHz:
      tuning = (DEVINFO->AUXHFRCOCAL1 & _DEVINFO_AUXHFRCOCAL1_BAND21_MASK)
               >> _DEVINFO_AUXHFRCOCAL1_BAND21_SHIFT;
      break;

#if defined(_CMU_AUXHFRCOCTRL_BAND_28MHZ)
    case cmuAUXHFRCOBand_28MHz:
      tuning = (DEVINFO->AUXHFRCOCAL1 & _DEVINFO_AUXHFRCOCAL1_BAND28_MASK)
               >> _DEVINFO_AUXHFRCOCAL1_BAND28_SHIFT;
      break;
#endif

    default:
      EFM_ASSERT(0);
      return;
  }

  /* Set band/tuning */
  CMU->AUXHFRCOCTRL = (CMU->AUXHFRCOCTRL
                       & ~(_CMU_AUXHFRCOCTRL_BAND_MASK
                           | _CMU_AUXHFRCOCTRL_TUNING_MASK))
                      | (band << _CMU_AUXHFRCOCTRL_BAND_SHIFT)
                      | (tuning << _CMU_AUXHFRCOCTRL_TUNING_SHIFT);
}
#endif /* _CMU_AUXHFRCOCTRL_BAND_MASK */

#if defined(_CMU_AUXHFRCOCTRL_FREQRANGE_MASK)
/**************************************************************************//**
 * @brief
 *   Get the AUXHFRCO frequency calibration word in DEVINFO
 *
 * @param[in] freq
 *   Frequency in Hz
 *
 * @return
 *   AUXHFRCO calibration word for a given frequency
 *****************************************************************************/
static uint32_t CMU_AUXHFRCODevinfoGet(CMU_AUXHFRCOFreq_TypeDef freq)
{
  switch (freq) {
    /* 1, 2 and 4MHz share the same calibration word */
    case cmuAUXHFRCOFreq_1M0Hz:
    case cmuAUXHFRCOFreq_2M0Hz:
    case cmuAUXHFRCOFreq_4M0Hz:
      return DEVINFO->AUXHFRCOCAL0;

    case cmuAUXHFRCOFreq_7M0Hz:
      return DEVINFO->AUXHFRCOCAL3;

    case cmuAUXHFRCOFreq_13M0Hz:
      return DEVINFO->AUXHFRCOCAL6;

    case cmuAUXHFRCOFreq_16M0Hz:
      return DEVINFO->AUXHFRCOCAL7;

    case cmuAUXHFRCOFreq_19M0Hz:
      return DEVINFO->AUXHFRCOCAL8;

    case cmuAUXHFRCOFreq_26M0Hz:
      return DEVINFO->AUXHFRCOCAL10;

    case cmuAUXHFRCOFreq_32M0Hz:
      return DEVINFO->AUXHFRCOCAL11;

    case cmuAUXHFRCOFreq_38M0Hz:
      return DEVINFO->AUXHFRCOCAL12;

#if defined(DEVINFO_AUXHFRCOCAL14)
    case cmuAUXHFRCOFreq_48M0Hz:
      return DEVINFO->AUXHFRCOCAL13;

    case cmuAUXHFRCOFreq_50M0Hz:
      return DEVINFO->AUXHFRCOCAL14;
#endif

    default: /* cmuAUXHFRCOFreq_UserDefined */
      return 0;
  }
}
#endif /* _CMU_AUXHFRCOCTRL_FREQRANGE_MASK */

#if defined(_CMU_AUXHFRCOCTRL_FREQRANGE_MASK)
/***************************************************************************//**
 * @brief
 *   Get current AUXHFRCO frequency.
 *
 * @return
 *   AUXHFRCO frequency
 ******************************************************************************/
CMU_AUXHFRCOFreq_TypeDef CMU_AUXHFRCOBandGet(void)
{
  return auxHfrcoFreq;
}
#endif /* _CMU_AUXHFRCOCTRL_FREQRANGE_MASK */

#if defined(_CMU_AUXHFRCOCTRL_FREQRANGE_MASK)
/***************************************************************************//**
 * @brief
 *   Set AUXHFRCO calibration for the selected target frequency.
 *
 * @param[in] setFreq
 *   AUXHFRCO frequency to set
 ******************************************************************************/
void CMU_AUXHFRCOBandSet(CMU_AUXHFRCOFreq_TypeDef setFreq)
{
  uint32_t freqCal;

  /* Get DEVINFO index, set global auxHfrcoFreq */
  freqCal = CMU_AUXHFRCODevinfoGet(setFreq);
  EFM_ASSERT((freqCal != 0) && (freqCal != UINT_MAX));
  auxHfrcoFreq = setFreq;

  /* Wait for any previous sync to complete, and then set calibration data
     for the selected frequency.  */
  while (BUS_RegBitRead(&CMU->SYNCBUSY, _CMU_SYNCBUSY_AUXHFRCOBSY_SHIFT)) ;

  /* Set divider in AUXHFRCOCTRL for 1, 2 and 4MHz */
  switch (setFreq) {
    case cmuAUXHFRCOFreq_1M0Hz:
      freqCal = (freqCal & ~_CMU_AUXHFRCOCTRL_CLKDIV_MASK)
                | CMU_AUXHFRCOCTRL_CLKDIV_DIV4;
      break;

    case cmuAUXHFRCOFreq_2M0Hz:
      freqCal = (freqCal & ~_CMU_AUXHFRCOCTRL_CLKDIV_MASK)
                | CMU_AUXHFRCOCTRL_CLKDIV_DIV2;
      break;

    case cmuAUXHFRCOFreq_4M0Hz:
      freqCal = (freqCal & ~_CMU_AUXHFRCOCTRL_CLKDIV_MASK)
                | CMU_AUXHFRCOCTRL_CLKDIV_DIV1;
      break;

    default:
      break;
  }
  CMU->AUXHFRCOCTRL = freqCal;
}
#endif /* _CMU_AUXHFRCOCTRL_FREQRANGE_MASK */

/***************************************************************************//**
 * @brief
 *   Calibrate clock.
 *
 * @details
 *   Run a calibration for HFCLK against a selectable reference clock. Please
 *   refer to the reference manual, CMU chapter, for further details.
 *
 * @note
 *   This function will not return until calibration measurement is completed.
 *
 * @param[in] HFCycles
 *   The number of HFCLK cycles to run calibration. Increasing this number
 *   increases precision, but the calibration will take more time.
 *
 * @param[in] ref
 *   The reference clock used to compare HFCLK with.
 *
 * @return
 *   The number of ticks the reference clock after HFCycles ticks on the HF
 *   clock.
 ******************************************************************************/
uint32_t CMU_Calibrate(uint32_t HFCycles, CMU_Osc_TypeDef ref)
{
  EFM_ASSERT(HFCycles <= (_CMU_CALCNT_CALCNT_MASK >> _CMU_CALCNT_CALCNT_SHIFT));

  /* Set reference clock source */
  switch (ref) {
    case cmuOsc_LFXO:
      CMU->CALCTRL = CMU_CALCTRL_UPSEL_LFXO;
      break;

    case cmuOsc_LFRCO:
      CMU->CALCTRL = CMU_CALCTRL_UPSEL_LFRCO;
      break;

    case cmuOsc_HFXO:
      CMU->CALCTRL = CMU_CALCTRL_UPSEL_HFXO;
      break;

    case cmuOsc_HFRCO:
      CMU->CALCTRL = CMU_CALCTRL_UPSEL_HFRCO;
      break;

    case cmuOsc_AUXHFRCO:
      CMU->CALCTRL = CMU_CALCTRL_UPSEL_AUXHFRCO;
      break;

#if defined (_CMU_USHFRCOCTRL_MASK)
    case cmuOsc_USHFRCO:
      CMU->CALCTRL = CMU_CALCTRL_UPSEL_USHFRCO;
      break;
#endif

    default:
      EFM_ASSERT(0);
      return 0;
  }

  /* Set top value */
  CMU->CALCNT = HFCycles;

  /* Start calibration */
  CMU->CMD = CMU_CMD_CALSTART;

#if defined(CMU_STATUS_CALRDY)
  /* Wait until calibration completes */
  while (!BUS_RegBitRead(&CMU->STATUS, _CMU_STATUS_CALRDY_SHIFT)) {
  }
#else
  /* Wait until calibration completes */
  while (BUS_RegBitRead(&CMU->STATUS, _CMU_STATUS_CALBSY_SHIFT)) {
  }
#endif

  return CMU->CALCNT;
}

#if defined(_CMU_CALCTRL_UPSEL_MASK) && defined(_CMU_CALCTRL_DOWNSEL_MASK)
/***************************************************************************//**
 * @brief
 *   Configure clock calibration
 *
 * @details
 *   Configure a calibration for a selectable clock source against another
 *   selectable reference clock.
 *   Refer to the reference manual, CMU chapter, for further details.
 *
 * @note
 *   After configuration, a call to CMU_CalibrateStart() is required, and
 *   the resulting calibration value can be read out with the
 *   CMU_CalibrateCountGet() function call.
 *
 * @param[in] downCycles
 *   The number of downSel clock cycles to run calibration. Increasing this
 *   number increases precision, but the calibration will take more time.
 *
 * @param[in] downSel
 *   The clock which will be counted down downCycles
 *
 * @param[in] upSel
 *   The reference clock, the number of cycles generated by this clock will
 *   be counted and added up, the result can be given with the
 *   CMU_CalibrateCountGet() function call.
 ******************************************************************************/
void CMU_CalibrateConfig(uint32_t downCycles, CMU_Osc_TypeDef downSel,
                         CMU_Osc_TypeDef upSel)
{
  /* Keep untouched configuration settings */
  uint32_t calCtrl = CMU->CALCTRL
                     & ~(_CMU_CALCTRL_UPSEL_MASK | _CMU_CALCTRL_DOWNSEL_MASK);

  /* 20 bits of precision to calibration count register */
  EFM_ASSERT(downCycles <= (_CMU_CALCNT_CALCNT_MASK >> _CMU_CALCNT_CALCNT_SHIFT));

  /* Set down counting clock source - down counter */
  switch (downSel) {
    case cmuOsc_LFXO:
      calCtrl |= CMU_CALCTRL_DOWNSEL_LFXO;
      break;

    case cmuOsc_LFRCO:
      calCtrl |= CMU_CALCTRL_DOWNSEL_LFRCO;
      break;

    case cmuOsc_HFXO:
      calCtrl |= CMU_CALCTRL_DOWNSEL_HFXO;
      break;

    case cmuOsc_HFRCO:
      calCtrl |= CMU_CALCTRL_DOWNSEL_HFRCO;
      break;

    case cmuOsc_AUXHFRCO:
      calCtrl |= CMU_CALCTRL_DOWNSEL_AUXHFRCO;
      break;

#if defined (_CMU_USHFRCOCTRL_MASK)
    case cmuOsc_USHFRCO:
      calCtrl |= CMU_CALCTRL_DOWNSEL_USHFRCO;
      break;
#endif

    default:
      EFM_ASSERT(0);
      break;
  }

  /* Set top value to be counted down by the downSel clock */
  CMU->CALCNT = downCycles;

  /* Set reference clock source - up counter */
  switch (upSel) {
    case cmuOsc_LFXO:
      calCtrl |= CMU_CALCTRL_UPSEL_LFXO;
      break;

    case cmuOsc_LFRCO:
      calCtrl |= CMU_CALCTRL_UPSEL_LFRCO;
      break;

    case cmuOsc_HFXO:
      calCtrl |= CMU_CALCTRL_UPSEL_HFXO;
      break;

    case cmuOsc_HFRCO:
      calCtrl |= CMU_CALCTRL_UPSEL_HFRCO;
      break;

    case cmuOsc_AUXHFRCO:
      calCtrl |= CMU_CALCTRL_UPSEL_AUXHFRCO;
      break;

#if defined (_CMU_USHFRCOCTRL_MASK)
    case cmuOsc_USHFRCO:
      calCtrl |= CMU_CALCTRL_UPSEL_USHFRCO;
      break;
#endif

    default:
      EFM_ASSERT(0);
      break;
  }

  CMU->CALCTRL = calCtrl;
}
#endif

/***************************************************************************//**
 * @brief
 *    Get calibration count register
 * @note
 *    If continuous calibrartion mode is active, calibration busy will almost
 *    always be off, and we just need to read the value, where the normal case
 *    would be that this function call has been triggered by the CALRDY
 *    interrupt flag.
 * @return
 *    Calibration count, the number of UPSEL clocks (see CMU_CalibrateConfig)
 *    in the period of DOWNSEL oscillator clock cycles configured by a previous
 *    write operation to CMU->CALCNT
 ******************************************************************************/
uint32_t CMU_CalibrateCountGet(void)
{
  /* Wait until calibration completes, UNLESS continuous calibration mode is  */
  /* active */
#if defined(CMU_CALCTRL_CONT)
  if (!BUS_RegBitRead(&CMU->CALCTRL, _CMU_CALCTRL_CONT_SHIFT)) {
#if defined(CMU_STATUS_CALRDY)
    /* Wait until calibration completes */
    while (!BUS_RegBitRead(&CMU->STATUS, _CMU_STATUS_CALRDY_SHIFT)) {
    }
#else
    /* Wait until calibration completes */
    while (BUS_RegBitRead(&CMU->STATUS, _CMU_STATUS_CALBSY_SHIFT)) {
    }
#endif
  }
#else
  while (BUS_RegBitRead(&CMU->STATUS, _CMU_STATUS_CALBSY_SHIFT)) {
  }
#endif
  return CMU->CALCNT;
}

/***************************************************************************//**
 * @brief
 *   Get clock divisor/prescaler.
 *
 * @param[in] clock
 *   Clock point to get divisor/prescaler for. Notice that not all clock points
 *   have a divisor/prescaler. Please refer to CMU overview in reference manual.
 *
 * @return
 *   The current clock point divisor/prescaler. 1 is returned
 *   if @p clock specifies a clock point without a divisor/prescaler.
 ******************************************************************************/
CMU_ClkDiv_TypeDef CMU_ClockDivGet(CMU_Clock_TypeDef clock)
{
#if defined(_SILICON_LABS_32B_SERIES_1)
  return 1 + (uint32_t)CMU_ClockPrescGet(clock);

#elif defined(_SILICON_LABS_32B_SERIES_0)
  uint32_t           divReg;
  CMU_ClkDiv_TypeDef ret;

  /* Get divisor reg id */
  divReg = (clock >> CMU_DIV_REG_POS) & CMU_DIV_REG_MASK;

  switch (divReg) {
#if defined(_CMU_CTRL_HFCLKDIV_MASK)
    case CMU_HFCLKDIV_REG:
      ret = 1 + ((CMU->CTRL & _CMU_CTRL_HFCLKDIV_MASK)
                 >> _CMU_CTRL_HFCLKDIV_SHIFT);
      break;
#endif

    case CMU_HFPERCLKDIV_REG:
      ret = (CMU_ClkDiv_TypeDef)((CMU->HFPERCLKDIV
                                  & _CMU_HFPERCLKDIV_HFPERCLKDIV_MASK)
                                 >> _CMU_HFPERCLKDIV_HFPERCLKDIV_SHIFT);
      ret = CMU_Log2ToDiv(ret);
      break;

    case CMU_HFCORECLKDIV_REG:
      ret = (CMU_ClkDiv_TypeDef)((CMU->HFCORECLKDIV
                                  & _CMU_HFCORECLKDIV_HFCORECLKDIV_MASK)
                                 >> _CMU_HFCORECLKDIV_HFCORECLKDIV_SHIFT);
      ret = CMU_Log2ToDiv(ret);
      break;

    case CMU_LFAPRESC0_REG:
      switch (clock) {
        case cmuClock_RTC:
          ret = (CMU_ClkDiv_TypeDef)((CMU->LFAPRESC0 & _CMU_LFAPRESC0_RTC_MASK)
                                     >> _CMU_LFAPRESC0_RTC_SHIFT);
          ret = CMU_Log2ToDiv(ret);
          break;

#if defined(_CMU_LFAPRESC0_LETIMER0_MASK)
        case cmuClock_LETIMER0:
          ret = (CMU_ClkDiv_TypeDef)((CMU->LFAPRESC0 & _CMU_LFAPRESC0_LETIMER0_MASK)
                                     >> _CMU_LFAPRESC0_LETIMER0_SHIFT);
          ret = CMU_Log2ToDiv(ret);
          break;
#endif

#if defined(_CMU_LFAPRESC0_LCD_MASK)
        case cmuClock_LCDpre:
          ret = (CMU_ClkDiv_TypeDef)(((CMU->LFAPRESC0 & _CMU_LFAPRESC0_LCD_MASK)
                                      >> _CMU_LFAPRESC0_LCD_SHIFT)
                                     + CMU_DivToLog2(cmuClkDiv_16));
          ret = CMU_Log2ToDiv(ret);
          break;
#endif

#if defined(_CMU_LFAPRESC0_LESENSE_MASK)
        case cmuClock_LESENSE:
          ret = (CMU_ClkDiv_TypeDef)((CMU->LFAPRESC0 & _CMU_LFAPRESC0_LESENSE_MASK)
                                     >> _CMU_LFAPRESC0_LESENSE_SHIFT);
          ret = CMU_Log2ToDiv(ret);
          break;
#endif

        default:
          EFM_ASSERT(0);
          ret = cmuClkDiv_1;
          break;
      }
      break;

    case CMU_LFBPRESC0_REG:
      switch (clock) {
#if defined(_CMU_LFBPRESC0_LEUART0_MASK)
        case cmuClock_LEUART0:
          ret = (CMU_ClkDiv_TypeDef)((CMU->LFBPRESC0 & _CMU_LFBPRESC0_LEUART0_MASK)
                                     >> _CMU_LFBPRESC0_LEUART0_SHIFT);
          ret = CMU_Log2ToDiv(ret);
          break;
#endif

#if defined(_CMU_LFBPRESC0_LEUART1_MASK)
        case cmuClock_LEUART1:
          ret = (CMU_ClkDiv_TypeDef)((CMU->LFBPRESC0 & _CMU_LFBPRESC0_LEUART1_MASK)
                                     >> _CMU_LFBPRESC0_LEUART1_SHIFT);
          ret = CMU_Log2ToDiv(ret);
          break;
#endif

        default:
          EFM_ASSERT(0);
          ret = cmuClkDiv_1;
          break;
      }
      break;

    default:
      EFM_ASSERT(0);
      ret = cmuClkDiv_1;
      break;
  }

  return ret;
#endif
}

/***************************************************************************//**
 * @brief
 *   Set clock divisor/prescaler.
 *
 * @note
 *   If setting a LF clock prescaler, synchronization into the low frequency
 *   domain is required. If the same register is modified before a previous
 *   update has completed, this function will stall until the previous
 *   synchronization has completed. Please refer to CMU_FreezeEnable() for
 *   a suggestion on how to reduce stalling time in some use cases.
 *
 * @param[in] clock
 *   Clock point to set divisor/prescaler for. Notice that not all clock points
 *   have a divisor/prescaler, please refer to CMU overview in the reference
 *   manual.
 *
 * @param[in] div
 *   The clock divisor to use (<= cmuClkDiv_512).
 ******************************************************************************/
void CMU_ClockDivSet(CMU_Clock_TypeDef clock, CMU_ClkDiv_TypeDef div)
{
#if defined(_SILICON_LABS_32B_SERIES_1)
  CMU_ClockPrescSet(clock, (CMU_ClkPresc_TypeDef)(div - 1));

#elif defined(_SILICON_LABS_32B_SERIES_0)
  uint32_t freq;
  uint32_t divReg;

  /* Get divisor reg id */
  divReg = (clock >> CMU_DIV_REG_POS) & CMU_DIV_REG_MASK;

  switch (divReg) {
#if defined(_CMU_CTRL_HFCLKDIV_MASK)
    case CMU_HFCLKDIV_REG:
      EFM_ASSERT((div >= cmuClkDiv_1) && (div <= cmuClkDiv_8));

      /* Configure worst case wait states for flash access before setting divisor */
      flashWaitStateMax();

      /* Set divider */
      CMU->CTRL = (CMU->CTRL & ~_CMU_CTRL_HFCLKDIV_MASK)
                  | ((div - 1) << _CMU_CTRL_HFCLKDIV_SHIFT);

      /* Update CMSIS core clock variable */
      /* (The function will update the global variable) */
      freq = SystemCoreClockGet();

      /* Optimize flash access wait state setting for current core clk */
      CMU_UpdateWaitStates(freq, VSCALE_DEFAULT);
      break;
#endif

    case CMU_HFPERCLKDIV_REG:
      EFM_ASSERT((div >= cmuClkDiv_1) && (div <= cmuClkDiv_512));
      /* Convert to correct scale */
      div = CMU_DivToLog2(div);
      CMU->HFPERCLKDIV = (CMU->HFPERCLKDIV & ~_CMU_HFPERCLKDIV_HFPERCLKDIV_MASK)
                         | (div << _CMU_HFPERCLKDIV_HFPERCLKDIV_SHIFT);
      break;

    case CMU_HFCORECLKDIV_REG:
      EFM_ASSERT((div >= cmuClkDiv_1) && (div <= cmuClkDiv_512));

      /* Configure worst case wait states for flash access before setting divisor */
      flashWaitStateMax();

#if defined(CMU_MAX_FREQ_HFLE)
      setHfLeConfig(SystemHFClockGet() / div);
#endif

      /* Convert to correct scale */
      div = CMU_DivToLog2(div);

      CMU->HFCORECLKDIV = (CMU->HFCORECLKDIV
                           & ~_CMU_HFCORECLKDIV_HFCORECLKDIV_MASK)
                          | (div << _CMU_HFCORECLKDIV_HFCORECLKDIV_SHIFT);

      /* Update CMSIS core clock variable */
      /* (The function will update the global variable) */
      freq = SystemCoreClockGet();

      /* Optimize wait state setting for current core clk */
      CMU_UpdateWaitStates(freq, VSCALE_DEFAULT);
      break;

    case CMU_LFAPRESC0_REG:
      switch (clock) {
        case cmuClock_RTC:
          EFM_ASSERT(div <= cmuClkDiv_32768);

          /* LF register about to be modified require sync. busy check */
          syncReg(CMU_SYNCBUSY_LFAPRESC0);

          /* Convert to correct scale */
          div = CMU_DivToLog2(div);

          CMU->LFAPRESC0 = (CMU->LFAPRESC0 & ~_CMU_LFAPRESC0_RTC_MASK)
                           | (div << _CMU_LFAPRESC0_RTC_SHIFT);
          break;

#if defined(_CMU_LFAPRESC0_LETIMER0_MASK)
        case cmuClock_LETIMER0:
          EFM_ASSERT(div <= cmuClkDiv_32768);

          /* LF register about to be modified require sync. busy check */
          syncReg(CMU_SYNCBUSY_LFAPRESC0);

          /* Convert to correct scale */
          div = CMU_DivToLog2(div);

          CMU->LFAPRESC0 = (CMU->LFAPRESC0 & ~_CMU_LFAPRESC0_LETIMER0_MASK)
                           | (div << _CMU_LFAPRESC0_LETIMER0_SHIFT);
          break;
#endif

#if defined(LCD_PRESENT)
        case cmuClock_LCDpre:
          EFM_ASSERT((div >= cmuClkDiv_16) && (div <= cmuClkDiv_128));

          /* LF register about to be modified require sync. busy check */
          syncReg(CMU_SYNCBUSY_LFAPRESC0);

          /* Convert to correct scale */
          div = CMU_DivToLog2(div);

          CMU->LFAPRESC0 = (CMU->LFAPRESC0 & ~_CMU_LFAPRESC0_LCD_MASK)
                           | ((div - CMU_DivToLog2(cmuClkDiv_16))
                              << _CMU_LFAPRESC0_LCD_SHIFT);
          break;
#endif /* defined(LCD_PRESENT) */

#if defined(LESENSE_PRESENT)
        case cmuClock_LESENSE:
          EFM_ASSERT(div <= cmuClkDiv_8);

          /* LF register about to be modified require sync. busy check */
          syncReg(CMU_SYNCBUSY_LFAPRESC0);

          /* Convert to correct scale */
          div = CMU_DivToLog2(div);

          CMU->LFAPRESC0 = (CMU->LFAPRESC0 & ~_CMU_LFAPRESC0_LESENSE_MASK)
                           | (div << _CMU_LFAPRESC0_LESENSE_SHIFT);
          break;
#endif /* defined(LESENSE_PRESENT) */

        default:
          EFM_ASSERT(0);
          break;
      }
      break;

    case CMU_LFBPRESC0_REG:
      switch (clock) {
#if defined(_CMU_LFBPRESC0_LEUART0_MASK)
        case cmuClock_LEUART0:
          EFM_ASSERT(div <= cmuClkDiv_8);

          /* LF register about to be modified require sync. busy check */
          syncReg(CMU_SYNCBUSY_LFBPRESC0);

          /* Convert to correct scale */
          div = CMU_DivToLog2(div);

          CMU->LFBPRESC0 = (CMU->LFBPRESC0 & ~_CMU_LFBPRESC0_LEUART0_MASK)
                           | (((uint32_t)div) << _CMU_LFBPRESC0_LEUART0_SHIFT);
          break;
#endif

#if defined(_CMU_LFBPRESC0_LEUART1_MASK)
        case cmuClock_LEUART1:
          EFM_ASSERT(div <= cmuClkDiv_8);

          /* LF register about to be modified require sync. busy check */
          syncReg(CMU_SYNCBUSY_LFBPRESC0);

          /* Convert to correct scale */
          div = CMU_DivToLog2(div);

          CMU->LFBPRESC0 = (CMU->LFBPRESC0 & ~_CMU_LFBPRESC0_LEUART1_MASK)
                           | (((uint32_t)div) << _CMU_LFBPRESC0_LEUART1_SHIFT);
          break;
#endif

        default:
          EFM_ASSERT(0);
          break;
      }
      break;

    default:
      EFM_ASSERT(0);
      break;
  }
#endif
}

/***************************************************************************//**
 * @brief
 *   Enable/disable a clock.
 *
 * @details
 *   In general, module clocking is disabled after a reset. If a module
 *   clock is disabled, the registers of that module are not accessible and
 *   reading from such registers may return undefined values. Writing to
 *   registers of clock disabled modules have no effect. One should normally
 *   avoid accessing module registers of a module with a disabled clock.
 *
 * @note
 *   If enabling/disabling a LF clock, synchronization into the low frequency
 *   domain is required. If the same register is modified before a previous
 *   update has completed, this function will stall until the previous
 *   synchronization has completed. Please refer to CMU_FreezeEnable() for
 *   a suggestion on how to reduce stalling time in some use cases.
 *
 * @param[in] clock
 *   The clock to enable/disable. Notice that not all defined clock
 *   points have separate enable/disable control, please refer to CMU overview
 *   in reference manual.
 *
 * @param[in] enable
 *   @li true - enable specified clock.
 *   @li false - disable specified clock.
 ******************************************************************************/
void CMU_ClockEnable(CMU_Clock_TypeDef clock, bool enable)
{
  volatile uint32_t *reg;
  uint32_t          bit;
  uint32_t          sync = 0;

  /* Identify enable register */
  switch ((clock >> CMU_EN_REG_POS) & CMU_EN_REG_MASK) {
#if defined(_CMU_CTRL_HFPERCLKEN_MASK)
    case CMU_CTRL_EN_REG:
      reg = &CMU->CTRL;
      break;
#endif

#if defined(_CMU_HFCORECLKEN0_MASK)
    case CMU_HFCORECLKEN0_EN_REG:
      reg = &CMU->HFCORECLKEN0;
#if defined(CMU_MAX_FREQ_HFLE)
      setHfLeConfig(CMU_ClockFreqGet(cmuClock_HFLE));
#endif
      break;
#endif

#if defined(_CMU_HFBUSCLKEN0_MASK)
    case CMU_HFBUSCLKEN0_EN_REG:
      reg = &CMU->HFBUSCLKEN0;
      break;
#endif

#if defined(_CMU_HFPERCLKDIV_MASK)
    case CMU_HFPERCLKDIV_EN_REG:
      reg = &CMU->HFPERCLKDIV;
      break;
#endif

    case CMU_HFPERCLKEN0_EN_REG:
      reg = &CMU->HFPERCLKEN0;
      break;

#if defined(_CMU_HFPERCLKEN1_MASK)
    case CMU_HFPERCLKEN1_EN_REG:
      reg = &CMU->HFPERCLKEN1;
      break;
#endif

    case CMU_LFACLKEN0_EN_REG:
      reg  = &CMU->LFACLKEN0;
      sync = CMU_SYNCBUSY_LFACLKEN0;
      break;

    case CMU_LFBCLKEN0_EN_REG:
      reg  = &CMU->LFBCLKEN0;
      sync = CMU_SYNCBUSY_LFBCLKEN0;
      break;

#if defined(_CMU_LFCCLKEN0_MASK)
    case CMU_LFCCLKEN0_EN_REG:
      reg = &CMU->LFCCLKEN0;
      sync = CMU_SYNCBUSY_LFCCLKEN0;
      break;
#endif

#if defined(_CMU_LFECLKEN0_MASK)
    case CMU_LFECLKEN0_EN_REG:
      reg  = &CMU->LFECLKEN0;
      sync = CMU_SYNCBUSY_LFECLKEN0;
      break;
#endif

#if defined(_CMU_SDIOCTRL_MASK)
    case CMU_SDIOREF_EN_REG:
      reg = &CMU->SDIOCTRL;
      enable = !enable;
      break;
#endif

#if defined(_CMU_QSPICTRL_MASK)
    case CMU_QSPI0REF_EN_REG:
      reg = &CMU->QSPICTRL;
      enable = !enable;
      break;
#endif
#if defined(_CMU_USBCTRL_MASK)
    case CMU_USBRCLK_EN_REG:
      reg = &CMU->USBCTRL;
      break;
#endif

    case CMU_PCNT_EN_REG:
      reg = &CMU->PCNTCTRL;
      break;

    default: /* Cannot enable/disable clock point */
      EFM_ASSERT(0);
      return;
  }

  /* Get bit position used to enable/disable */
  bit = (clock >> CMU_EN_BIT_POS) & CMU_EN_BIT_MASK;

  /* LF synchronization required? */
  if (sync) {
    syncReg(sync);
  }

  /* Set/clear bit as requested */
  BUS_RegBitWrite(reg, bit, enable);
}

/***************************************************************************//**
 * @brief
 *   Get clock frequency for a clock point.
 *
 * @param[in] clock
 *   Clock point to fetch frequency for.
 *
 * @return
 *   The current frequency in Hz.
 ******************************************************************************/
uint32_t CMU_ClockFreqGet(CMU_Clock_TypeDef clock)
{
  uint32_t ret;

  switch (clock & (CMU_CLK_BRANCH_MASK << CMU_CLK_BRANCH_POS)) {
    case (CMU_HF_CLK_BRANCH << CMU_CLK_BRANCH_POS):
      ret = SystemHFClockGet();
      break;

    case (CMU_HFPER_CLK_BRANCH << CMU_CLK_BRANCH_POS):
      ret = SystemHFClockGet();
      /* Calculate frequency after HFPER divider. */
#if defined(_CMU_HFPERCLKDIV_HFPERCLKDIV_MASK)
      ret >>= (CMU->HFPERCLKDIV & _CMU_HFPERCLKDIV_HFPERCLKDIV_MASK)
              >> _CMU_HFPERCLKDIV_HFPERCLKDIV_SHIFT;
#endif
#if defined(_CMU_HFPERPRESC_PRESC_MASK)
      ret /= 1U + ((CMU->HFPERPRESC & _CMU_HFPERPRESC_PRESC_MASK)
                   >> _CMU_HFPERPRESC_PRESC_SHIFT);
#endif
      break;

#if defined(_SILICON_LABS_32B_SERIES_1)
#if defined(CRYPTO_PRESENT)     \
      || defined(LDMA_PRESENT)  \
      || defined(GPCRC_PRESENT) \
      || defined(PRS_PRESENT)   \
      || defined(GPIO_PRESENT)
    case (CMU_HFBUS_CLK_BRANCH << CMU_CLK_BRANCH_POS):
      ret = SystemHFClockGet();
      break;
#endif

    case (CMU_HFCORE_CLK_BRANCH << CMU_CLK_BRANCH_POS):
      ret = SystemHFClockGet();
      ret /= 1U + ((CMU->HFCOREPRESC & _CMU_HFCOREPRESC_PRESC_MASK)
                   >> _CMU_HFCOREPRESC_PRESC_SHIFT);
      break;

    case (CMU_HFEXP_CLK_BRANCH << CMU_CLK_BRANCH_POS):
      ret = SystemHFClockGet();
      ret /= 1U + ((CMU->HFEXPPRESC & _CMU_HFEXPPRESC_PRESC_MASK)
                   >> _CMU_HFEXPPRESC_PRESC_SHIFT);
      break;
#endif

#if defined(_SILICON_LABS_32B_SERIES_0)
#if defined(AES_PRESENT)      \
      || defined(DMA_PRESENT) \
      || defined(EBI_PRESENT) \
      || defined(USB_PRESENT)
    case (CMU_HFCORE_CLK_BRANCH << CMU_CLK_BRANCH_POS):
    {
      ret = SystemCoreClockGet();
    } break;
#endif
#endif

    case (CMU_LFA_CLK_BRANCH << CMU_CLK_BRANCH_POS):
      ret = lfClkGet(cmuClock_LFA);
      break;

#if defined(_CMU_LFACLKEN0_RTC_MASK)
    case (CMU_RTC_CLK_BRANCH << CMU_CLK_BRANCH_POS):
      ret = lfClkGet(cmuClock_LFA);
      ret >>= (CMU->LFAPRESC0 & _CMU_LFAPRESC0_RTC_MASK)
              >> _CMU_LFAPRESC0_RTC_SHIFT;
      break;
#endif

#if defined(_CMU_LFECLKEN0_RTCC_MASK)
    case (CMU_RTCC_CLK_BRANCH << CMU_CLK_BRANCH_POS):
      ret = lfClkGet(cmuClock_LFE);
      break;
#endif

#if defined(_CMU_LFACLKEN0_LETIMER0_MASK)
    case (CMU_LETIMER0_CLK_BRANCH << CMU_CLK_BRANCH_POS):
      ret = lfClkGet(cmuClock_LFA);
#if defined(_SILICON_LABS_32B_SERIES_0)
      ret >>= (CMU->LFAPRESC0 & _CMU_LFAPRESC0_LETIMER0_MASK)
              >> _CMU_LFAPRESC0_LETIMER0_SHIFT;
#else
      ret /= CMU_Log2ToDiv((CMU->LFAPRESC0 & _CMU_LFAPRESC0_LETIMER0_MASK)
                           >> _CMU_LFAPRESC0_LETIMER0_SHIFT);
#endif
      break;
#endif

#if defined(_CMU_LFACLKEN0_LCD_MASK)
    case (CMU_LCDPRE_CLK_BRANCH << CMU_CLK_BRANCH_POS):
      ret = lfClkGet(cmuClock_LFA);
#if defined(_SILICON_LABS_32B_SERIES_0)
      ret >>= ((CMU->LFAPRESC0 & _CMU_LFAPRESC0_LCD_MASK)
               >> _CMU_LFAPRESC0_LCD_SHIFT)
              + CMU_DivToLog2(cmuClkDiv_16);
#else
      ret /= CMU_Log2ToDiv((CMU->LFAPRESC0 & _CMU_LFAPRESC0_LCD_MASK)
                           >> _CMU_LFAPRESC0_LCD_SHIFT);
#endif
      break;

#if defined(_CMU_LCDCTRL_MASK)
    case (CMU_LCD_CLK_BRANCH << CMU_CLK_BRANCH_POS):
      ret = lfClkGet(cmuClock_LFA);
      ret >>= (CMU->LFAPRESC0 & _CMU_LFAPRESC0_LCD_MASK)
              >> _CMU_LFAPRESC0_LCD_SHIFT;
      ret /= 1U + ((CMU->LCDCTRL & _CMU_LCDCTRL_FDIV_MASK)
                   >> _CMU_LCDCTRL_FDIV_SHIFT);
      break;
#endif
#endif

#if defined(_CMU_LFACLKEN0_LESENSE_MASK)
    case (CMU_LESENSE_CLK_BRANCH << CMU_CLK_BRANCH_POS):
      ret = lfClkGet(cmuClock_LFA);
      ret >>= (CMU->LFAPRESC0 & _CMU_LFAPRESC0_LESENSE_MASK)
              >> _CMU_LFAPRESC0_LESENSE_SHIFT;
      break;
#endif

    case (CMU_LFB_CLK_BRANCH << CMU_CLK_BRANCH_POS):
      ret = lfClkGet(cmuClock_LFB);
      break;

#if defined(_CMU_LFBCLKEN0_LEUART0_MASK)
    case (CMU_LEUART0_CLK_BRANCH << CMU_CLK_BRANCH_POS):
      ret = lfClkGet(cmuClock_LFB);
#if defined(_SILICON_LABS_32B_SERIES_0)
      ret >>= (CMU->LFBPRESC0 & _CMU_LFBPRESC0_LEUART0_MASK)
              >> _CMU_LFBPRESC0_LEUART0_SHIFT;
#else
      ret /= CMU_Log2ToDiv((CMU->LFBPRESC0 & _CMU_LFBPRESC0_LEUART0_MASK)
                           >> _CMU_LFBPRESC0_LEUART0_SHIFT);
#endif
      break;
#endif

#if defined(_CMU_LFBCLKEN0_LEUART1_MASK)
    case (CMU_LEUART1_CLK_BRANCH << CMU_CLK_BRANCH_POS):
      ret = lfClkGet(cmuClock_LFB);
#if defined(_SILICON_LABS_32B_SERIES_0)
      ret >>= (CMU->LFBPRESC0 & _CMU_LFBPRESC0_LEUART1_MASK)
              >> _CMU_LFBPRESC0_LEUART1_SHIFT;
#else
      ret /= CMU_Log2ToDiv((CMU->LFBPRESC0 & _CMU_LFBPRESC0_LEUART1_MASK)
                           >> _CMU_LFBPRESC0_LEUART1_SHIFT);
#endif
      break;
#endif

#if defined(_CMU_LFBCLKEN0_CSEN_MASK)
    case (CMU_CSEN_LF_CLK_BRANCH << CMU_CLK_BRANCH_POS):
      ret = lfClkGet(cmuClock_LFB);
      ret /= CMU_Log2ToDiv(((CMU->LFBPRESC0 & _CMU_LFBPRESC0_CSEN_MASK)
                            >> _CMU_LFBPRESC0_CSEN_SHIFT) + 4);
      break;
#endif

#if defined(_SILICON_LABS_32B_SERIES_1)
    case (CMU_LFE_CLK_BRANCH << CMU_CLK_BRANCH_POS):
      ret = lfClkGet(cmuClock_LFE);
      break;
#endif

    case (CMU_DBG_CLK_BRANCH << CMU_CLK_BRANCH_POS):
      ret = dbgClkGet();
      break;

    case (CMU_AUX_CLK_BRANCH << CMU_CLK_BRANCH_POS):
      ret = auxClkGet();
      break;

#if defined(USBC_CLOCK_PRESENT)
    case (CMU_USBC_CLK_BRANCH << CMU_CLK_BRANCH_POS):
      ret = usbCClkGet();
      break;
#endif

#if defined(_CMU_ADCCTRL_ADC0CLKSEL_MASK)
    case (CMU_ADC0ASYNC_CLK_BRANCH << CMU_CLK_BRANCH_POS):
      ret = adcAsyncClkGet(0);
#if defined(_CMU_ADCCTRL_ADC0CLKDIV_MASK)
      ret /= 1U + ((CMU->ADCCTRL & _CMU_ADCCTRL_ADC0CLKDIV_MASK)
                   >> _CMU_ADCCTRL_ADC0CLKDIV_SHIFT);
#endif
      break;
#endif

#if defined(_CMU_ADCCTRL_ADC1CLKSEL_MASK)
    case (CMU_ADC1ASYNC_CLK_BRANCH << CMU_CLK_BRANCH_POS):
      ret = adcAsyncClkGet(1);
#if defined(_CMU_ADCCTRL_ADC1CLKDIV_MASK)
      ret /= 1U + ((CMU->ADCCTRL & _CMU_ADCCTRL_ADC1CLKDIV_MASK)
                   >> _CMU_ADCCTRL_ADC1CLKDIV_SHIFT);
#endif
      break;
#endif

#if defined(_CMU_SDIOCTRL_SDIOCLKSEL_MASK)
    case (CMU_SDIOREF_CLK_BRANCH << CMU_CLK_BRANCH_POS):
      ret = sdioRefClkGet();
      break;
#endif

#if defined(_CMU_QSPICTRL_QSPI0CLKSEL_MASK)
    case (CMU_QSPI0REF_CLK_BRANCH << CMU_CLK_BRANCH_POS):
      ret = qspiRefClkGet(0);
      break;
#endif

#if defined(USBR_CLOCK_PRESENT)
    case (CMU_USBR_CLK_BRANCH << CMU_CLK_BRANCH_POS):
      ret = usbRateClkGet();
      break;
#endif

    default:
      EFM_ASSERT(0);
      ret = 0;
      break;
  }

  return ret;
}

#if defined(_SILICON_LABS_32B_SERIES_1)
/***************************************************************************//**
 * @brief
 *   Get clock prescaler.
 *
 * @param[in] clock
 *   Clock point to get the prescaler for. Notice that not all clock points
 *   have a prescaler. Please refer to CMU overview in reference manual.
 *
 * @return
 *   The prescaler value of the current clock point. 0 is returned
 *   if @p clock specifies a clock point without a prescaler.
 ******************************************************************************/
uint32_t CMU_ClockPrescGet(CMU_Clock_TypeDef clock)
{
  uint32_t  prescReg;
  uint32_t  ret;

  /* Get prescaler register id. */
  prescReg = (clock >> CMU_PRESC_REG_POS) & CMU_PRESC_REG_MASK;

  switch (prescReg) {
    case CMU_HFPRESC_REG:
      ret = (CMU->HFPRESC & _CMU_HFPRESC_PRESC_MASK)
            >> _CMU_HFPRESC_PRESC_SHIFT;
      break;

    case CMU_HFEXPPRESC_REG:
      ret = (CMU->HFEXPPRESC & _CMU_HFEXPPRESC_PRESC_MASK)
            >> _CMU_HFEXPPRESC_PRESC_SHIFT;
      break;

    case CMU_HFCLKLEPRESC_REG:
      ret = (CMU->HFPRESC & _CMU_HFPRESC_HFCLKLEPRESC_MASK)
            >> _CMU_HFPRESC_HFCLKLEPRESC_SHIFT;
      break;

    case CMU_HFPERPRESC_REG:
      ret = (CMU->HFPERPRESC & _CMU_HFPERPRESC_PRESC_MASK)
            >> _CMU_HFPERPRESC_PRESC_SHIFT;
      break;

    case CMU_HFCOREPRESC_REG:
      ret = (CMU->HFCOREPRESC & _CMU_HFCOREPRESC_PRESC_MASK)
            >> _CMU_HFCOREPRESC_PRESC_SHIFT;
      break;

    case CMU_LFAPRESC0_REG:
      switch (clock) {
#if defined(_CMU_LFAPRESC0_LETIMER0_MASK)
        case cmuClock_LETIMER0:
          ret = (CMU->LFAPRESC0 & _CMU_LFAPRESC0_LETIMER0_MASK)
                >> _CMU_LFAPRESC0_LETIMER0_SHIFT;
          /* Convert the exponent to prescaler value. */
          ret = CMU_Log2ToDiv(ret) - 1U;
          break;
#endif

#if defined(_CMU_LFAPRESC0_LESENSE_MASK)
        case cmuClock_LESENSE:
          ret = (CMU->LFAPRESC0 & _CMU_LFAPRESC0_LESENSE_MASK)
                >> _CMU_LFAPRESC0_LESENSE_SHIFT;
          /* Convert the exponent to prescaler value. */
          ret = CMU_Log2ToDiv(ret) - 1U;
          break;
#endif

#if defined(_CMU_LFAPRESC0_LETIMER1_MASK)
        case cmuClock_LETIMER1:
          ret = (CMU->LFAPRESC0 & _CMU_LFAPRESC0_LETIMER1_MASK)
                >> _CMU_LFAPRESC0_LETIMER1_SHIFT;
          ret = CMU_Log2ToDiv(ret) - 1U;
          break;
#endif

#if defined(_CMU_LFAPRESC0_LCD_MASK)
        case cmuClock_LCD:
        case cmuClock_LCDpre:
          ret = (CMU->LFAPRESC0 & _CMU_LFAPRESC0_LCD_MASK)
                >> _CMU_LFAPRESC0_LCD_SHIFT;
          ret = CMU_Log2ToDiv(ret) - 1U;
          break;
#endif

#if defined(_CMU_LFAPRESC0_RTC_MASK)
        case cmuClock_RTC:
          ret = (CMU->LFAPRESC0 & _CMU_LFAPRESC0_RTC_MASK)
                >> _CMU_LFAPRESC0_RTC_SHIFT;
          ret = CMU_Log2ToDiv(ret) - 1U;
          break;
#endif

        default:
          EFM_ASSERT(0);
          ret = 0U;
          break;
      }
      break;

    case CMU_LFBPRESC0_REG:
      switch (clock) {
#if defined(_CMU_LFBPRESC0_LEUART0_MASK)
        case cmuClock_LEUART0:
          ret = (CMU->LFBPRESC0 & _CMU_LFBPRESC0_LEUART0_MASK)
                >> _CMU_LFBPRESC0_LEUART0_SHIFT;
          /* Convert the exponent to prescaler value. */
          ret = CMU_Log2ToDiv(ret) - 1U;
          break;
#endif

#if defined(_CMU_LFBPRESC0_LEUART1_MASK)
        case cmuClock_LEUART1:
          ret = (CMU->LFBPRESC0 & _CMU_LFBPRESC0_LEUART1_MASK)
                >> _CMU_LFBPRESC0_LEUART1_SHIFT;
          /* Convert the exponent to prescaler value. */
          ret = CMU_Log2ToDiv(ret) - 1U;
          break;
#endif

#if defined(_CMU_LFBPRESC0_CSEN_MASK)
        case cmuClock_CSEN_LF:
          ret = (CMU->LFBPRESC0 & _CMU_LFBPRESC0_CSEN_MASK)
                >> _CMU_LFBPRESC0_CSEN_SHIFT;
          /* Convert the exponent to prescaler value. */
          ret = CMU_Log2ToDiv(ret + 4) - 1U;
          break;
#endif

        default:
          EFM_ASSERT(0);
          ret = 0U;
          break;
      }
      break;

    case CMU_LFEPRESC0_REG:
      switch (clock) {
#if defined(RTCC_PRESENT)
        case cmuClock_RTCC:
          /* No need to compute with LFEPRESC0_RTCC - DIV1 is the only  */
          /* allowed value. Convert the exponent to prescaler value.    */
          ret = _CMU_LFEPRESC0_RTCC_DIV1;
          break;

        default:
          EFM_ASSERT(0);
          ret = 0U;
          break;
#endif
      }
      break;

    case CMU_ADCASYNCDIV_REG:
      switch (clock) {
#if defined(_CMU_ADCCTRL_ADC0CLKDIV_MASK)
        case cmuClock_ADC0ASYNC:
          ret = (CMU->ADCCTRL & _CMU_ADCCTRL_ADC0CLKDIV_MASK)
                >> _CMU_ADCCTRL_ADC0CLKDIV_SHIFT;
          break;
#endif
#if defined(_CMU_ADCCTRL_ADC1CLKDIV_MASK)
        case cmuClock_ADC1ASYNC:
          ret = (CMU->ADCCTRL & _CMU_ADCCTRL_ADC1CLKDIV_MASK)
                >> _CMU_ADCCTRL_ADC1CLKDIV_SHIFT;
          break;
#endif
        default:
          EFM_ASSERT(0);
          ret = 0U;
          break;
      }
      break;

    default:
      EFM_ASSERT(0);
      ret = 0U;
      break;
  }

  return ret;
}
#endif

#if defined(_SILICON_LABS_32B_SERIES_1)
/***************************************************************************//**
 * @brief
 *   Set clock prescaler.
 *
 * @note
 *   If setting a LF clock prescaler, synchronization into the low frequency
 *   domain is required. If the same register is modified before a previous
 *   update has completed, this function will stall until the previous
 *   synchronization has completed. Please refer to CMU_FreezeEnable() for
 *   a suggestion on how to reduce stalling time in some use cases.
 *
 * @param[in] clock
 *   Clock point to set prescaler for. Notice that not all clock points
 *   have a prescaler, please refer to CMU overview in the reference manual.
 *
 * @param[in] presc
 *   The clock prescaler to use.
 ******************************************************************************/
void CMU_ClockPrescSet(CMU_Clock_TypeDef clock, CMU_ClkPresc_TypeDef presc)
{
  uint32_t freq;
  uint32_t prescReg;

  /* Get divisor reg id */
  prescReg = (clock >> CMU_PRESC_REG_POS) & CMU_PRESC_REG_MASK;

  switch (prescReg) {
    case CMU_HFPRESC_REG:
      EFM_ASSERT(presc < 32U);

      /* Configure worst case wait-states for flash and HFLE. */
      flashWaitStateMax();
      setHfLeConfig(CMU_MAX_FREQ_HFLE + 1);

      CMU->HFPRESC = (CMU->HFPRESC & ~_CMU_HFPRESC_PRESC_MASK)
                     | (presc << _CMU_HFPRESC_PRESC_SHIFT);

      /* Update CMSIS core clock variable (this function updates the global variable).
         Optimize flash and HFLE wait states. */
      freq = SystemCoreClockGet();
      CMU_UpdateWaitStates(freq, VSCALE_DEFAULT);

      setHfLeConfig(CMU_ClockFreqGet(cmuClock_HFLE));
      break;

    case CMU_HFEXPPRESC_REG:
      EFM_ASSERT(presc < 32U);

      CMU->HFEXPPRESC = (CMU->HFEXPPRESC & ~_CMU_HFEXPPRESC_PRESC_MASK)
                        | (presc << _CMU_HFEXPPRESC_PRESC_SHIFT);
      break;

    case CMU_HFCLKLEPRESC_REG:
#if defined (CMU_HFPRESC_HFCLKLEPRESC_DIV8)
      EFM_ASSERT(presc < 3U);
#else
      EFM_ASSERT(presc < 2U);
#endif

      /* Specifies the clock divider for HFCLKLE. This clock divider must be set
       * high enough for the divided clock frequency to be at or below the max
       * frequency allowed for the HFCLKLE clock. */
      CMU->HFPRESC = (CMU->HFPRESC & ~_CMU_HFPRESC_HFCLKLEPRESC_MASK)
                     | (presc << _CMU_HFPRESC_HFCLKLEPRESC_SHIFT);
      break;

    case CMU_HFPERPRESC_REG:
      EFM_ASSERT(presc < 512U);

      CMU->HFPERPRESC = (CMU->HFPERPRESC & ~_CMU_HFPERPRESC_PRESC_MASK)
                        | (presc << _CMU_HFPERPRESC_PRESC_SHIFT);
      break;

    case CMU_HFCOREPRESC_REG:
      EFM_ASSERT(presc < 512U);

      /* Configure worst case wait-states for flash and HFLE. */
      flashWaitStateMax();
      setHfLeConfig(CMU_MAX_FREQ_HFLE + 1);

      CMU->HFCOREPRESC = (CMU->HFCOREPRESC & ~_CMU_HFCOREPRESC_PRESC_MASK)
                         | (presc << _CMU_HFCOREPRESC_PRESC_SHIFT);

      /* Update CMSIS core clock variable (this function updates the global variable).
         Optimize flash and HFLE wait states. */
      freq = SystemCoreClockGet();
      CMU_UpdateWaitStates(freq, VSCALE_DEFAULT);
      setHfLeConfig(CMU_ClockFreqGet(cmuClock_HFLE));
      break;

    case CMU_LFAPRESC0_REG:
      switch (clock) {
#if defined(RTC_PRESENT)
        case cmuClock_RTC:
          EFM_ASSERT(presc <= 32768U);

          /* Convert prescaler value to DIV exponent scale. */
          presc = CMU_PrescToLog2(presc);

          /* LF register about to be modified require sync. Busy check. */
          syncReg(CMU_SYNCBUSY_LFAPRESC0);

          CMU->LFAPRESC0 = (CMU->LFAPRESC0 & ~_CMU_LFAPRESC0_RTC_MASK)
                           | (presc << _CMU_LFAPRESC0_RTC_SHIFT);
          break;
#endif

#if defined(RTCC_PRESENT)
        case cmuClock_RTCC:
#if defined(_CMU_LFEPRESC0_RTCC_MASK)
          /* DIV1 is the only accepted value. */
          EFM_ASSERT(presc <= 0U);

          /* LF register about to be modified require sync. Busy check.. */
          syncReg(CMU_SYNCBUSY_LFEPRESC0);

          CMU->LFEPRESC0 = (CMU->LFEPRESC0 & ~_CMU_LFEPRESC0_RTCC_MASK)
                           | (presc << _CMU_LFEPRESC0_RTCC_SHIFT);
#else
          EFM_ASSERT(presc <= 32768U);

          /* Convert prescaler value to DIV exponent scale. */
          presc = CMU_PrescToLog2(presc);

          /* LF register about to be modified require sync. Busy check. */
          syncReg(CMU_SYNCBUSY_LFAPRESC0);

          CMU->LFAPRESC0 = (CMU->LFAPRESC0 & ~_CMU_LFAPRESC0_RTCC_MASK)
                           | (presc << _CMU_LFAPRESC0_RTCC_SHIFT);
#endif
          break;
#endif

#if defined(_CMU_LFAPRESC0_LETIMER0_MASK)
        case cmuClock_LETIMER0:
          EFM_ASSERT(presc <= 32768U);

          /* Convert prescaler value to DIV exponent scale. */
          presc = CMU_PrescToLog2(presc);

          /* LF register about to be modified require sync. Busy check. */
          syncReg(CMU_SYNCBUSY_LFAPRESC0);

          CMU->LFAPRESC0 = (CMU->LFAPRESC0 & ~_CMU_LFAPRESC0_LETIMER0_MASK)
                           | (presc << _CMU_LFAPRESC0_LETIMER0_SHIFT);
          break;
#endif

#if defined(_CMU_LFAPRESC0_LESENSE_MASK)
        case cmuClock_LESENSE:
          EFM_ASSERT(presc <= 8);

          /* Convert prescaler value to DIV exponent scale. */
          presc = CMU_PrescToLog2(presc);

          /* LF register about to be modified require sync. Busy check. */
          syncReg(CMU_SYNCBUSY_LFAPRESC0);

          CMU->LFAPRESC0 = (CMU->LFAPRESC0 & ~_CMU_LFAPRESC0_LESENSE_MASK)
                           | (presc << _CMU_LFAPRESC0_LESENSE_SHIFT);
          break;
#endif

#if defined(_CMU_LFAPRESC0_LCD_MASK)
        case cmuClock_LCDpre:
        case cmuClock_LCD:
        {
          EFM_ASSERT(presc <= 32768U);

          /* Convert prescaler value to DIV exponent scale. */
          presc = CMU_PrescToLog2(presc);

          /* LF register about to be modified require sync. Busy check. */
          syncReg(CMU_SYNCBUSY_LFAPRESC0);

          CMU->LFAPRESC0 = (CMU->LFAPRESC0 & ~_CMU_LFAPRESC0_LCD_MASK)
                           | (presc << _CMU_LFAPRESC0_LCD_SHIFT);
        } break;
#endif

        default:
          EFM_ASSERT(0);
          break;
      }
      break;

    case CMU_LFBPRESC0_REG:
      switch (clock) {
#if defined(_CMU_LFBPRESC0_LEUART0_MASK)
        case cmuClock_LEUART0:
          EFM_ASSERT(presc <= 8U);

          /* Convert prescaler value to DIV exponent scale. */
          presc = CMU_PrescToLog2(presc);

          /* LF register about to be modified require sync. Busy check. */
          syncReg(CMU_SYNCBUSY_LFBPRESC0);

          CMU->LFBPRESC0 = (CMU->LFBPRESC0 & ~_CMU_LFBPRESC0_LEUART0_MASK)
                           | (presc << _CMU_LFBPRESC0_LEUART0_SHIFT);
          break;
#endif

#if defined(_CMU_LFBPRESC0_LEUART1_MASK)
        case cmuClock_LEUART1:
          EFM_ASSERT(presc <= 8U);

          /* Convert prescaler value to DIV exponent scale. */
          presc = CMU_PrescToLog2(presc);

          /* LF register about to be modified require sync. Busy check. */
          syncReg(CMU_SYNCBUSY_LFBPRESC0);

          CMU->LFBPRESC0 = (CMU->LFBPRESC0 & ~_CMU_LFBPRESC0_LEUART1_MASK)
                           | (presc << _CMU_LFBPRESC0_LEUART1_SHIFT);
          break;
#endif

#if defined(_CMU_LFBPRESC0_CSEN_MASK)
        case cmuClock_CSEN_LF:
          EFM_ASSERT((presc <= 127U) && (presc >= 15U));

          /* Convert prescaler value to DIV exponent scale.
           * DIV16 is the lowest supported prescaler. */
          presc = CMU_PrescToLog2(presc) - 4;

          /* LF register about to be modified require sync. Busy check. */
          syncReg(CMU_SYNCBUSY_LFBPRESC0);

          CMU->LFBPRESC0 = (CMU->LFBPRESC0 & ~_CMU_LFBPRESC0_CSEN_MASK)
                           | (presc << _CMU_LFBPRESC0_CSEN_SHIFT);
          break;
#endif

        default:
          EFM_ASSERT(0);
          break;
      }
      break;

    case CMU_LFEPRESC0_REG:
      switch (clock) {
#if defined(_CMU_LFEPRESC0_RTCC_MASK)
        case cmuClock_RTCC:
          EFM_ASSERT(presc <= 0U);

          /* LF register about to be modified require sync. Busy check. */
          syncReg(CMU_SYNCBUSY_LFEPRESC0);

          CMU->LFEPRESC0 = (CMU->LFEPRESC0 & ~_CMU_LFEPRESC0_RTCC_MASK)
                           | (presc << _CMU_LFEPRESC0_RTCC_SHIFT);
          break;
#endif

        default:
          EFM_ASSERT(0);
          break;
      }
      break;

    case CMU_ADCASYNCDIV_REG:
      switch (clock) {
#if defined(_CMU_ADCCTRL_ADC0CLKDIV_MASK)
        case cmuClock_ADC0ASYNC:
          EFM_ASSERT(presc <= 3);
          CMU->ADCCTRL = (CMU->ADCCTRL & ~_CMU_ADCCTRL_ADC0CLKDIV_MASK)
                         | (presc << _CMU_ADCCTRL_ADC0CLKDIV_SHIFT);
          break;
#endif

#if defined(_CMU_ADCCTRL_ADC1CLKDIV_MASK)
        case cmuClock_ADC1ASYNC:
          EFM_ASSERT(presc <= 3);
          CMU->ADCCTRL = (CMU->ADCCTRL & ~_CMU_ADCCTRL_ADC1CLKDIV_MASK)
                         | (presc << _CMU_ADCCTRL_ADC1CLKDIV_SHIFT);
          break;
#endif
        default:
          EFM_ASSERT(0);
          break;
      }
      break;

    default:
      EFM_ASSERT(0);
      break;
  }
}
#endif

/***************************************************************************//**
 * @brief
 *   Get currently selected reference clock used for a clock branch.
 *
 * @param[in] clock
 *   Clock branch to fetch selected ref. clock for. One of:
 *   @li #cmuClock_HF
 *   @li #cmuClock_LFA
 *   @li #cmuClock_LFB @if _CMU_LFCLKSEL_LFAE_ULFRCO
 *   @li #cmuClock_LFC
 *   @endif            @if _SILICON_LABS_32B_SERIES_1
 *   @li #cmuClock_LFE
 *   @endif
 *   @li #cmuClock_DBG @if DOXYDOC_USB_PRESENT
 *   @li #cmuClock_USBC
 *   @endif
 *
 * @return
 *   Reference clock used for clocking selected branch, #cmuSelect_Error if
 *   invalid @p clock provided.
 ******************************************************************************/
CMU_Select_TypeDef CMU_ClockSelectGet(CMU_Clock_TypeDef clock)
{
  CMU_Select_TypeDef ret = cmuSelect_Disabled;
  uint32_t selReg;

  selReg = (clock >> CMU_SEL_REG_POS) & CMU_SEL_REG_MASK;

  switch (selReg) {
    case CMU_HFCLKSEL_REG:
#if defined(_CMU_HFCLKSTATUS_MASK)
      switch (CMU->HFCLKSTATUS & _CMU_HFCLKSTATUS_SELECTED_MASK) {
        case CMU_HFCLKSTATUS_SELECTED_LFXO:
          ret = cmuSelect_LFXO;
          break;

        case CMU_HFCLKSTATUS_SELECTED_LFRCO:
          ret = cmuSelect_LFRCO;
          break;

        case CMU_HFCLKSTATUS_SELECTED_HFXO:
          ret = cmuSelect_HFXO;
          break;

        default:
          ret = cmuSelect_HFRCO;
          break;
      }
#else
      switch (CMU->STATUS
              & (CMU_STATUS_HFRCOSEL
                 | CMU_STATUS_HFXOSEL
                 | CMU_STATUS_LFRCOSEL
#if defined(CMU_STATUS_USHFRCODIV2SEL)
                 | CMU_STATUS_USHFRCODIV2SEL
#endif
                 | CMU_STATUS_LFXOSEL)) {
        case CMU_STATUS_LFXOSEL:
          ret = cmuSelect_LFXO;
          break;

        case CMU_STATUS_LFRCOSEL:
          ret = cmuSelect_LFRCO;
          break;

        case CMU_STATUS_HFXOSEL:
          ret = cmuSelect_HFXO;
          break;

#if defined(CMU_STATUS_USHFRCODIV2SEL)
        case CMU_STATUS_USHFRCODIV2SEL:
          ret = cmuSelect_USHFRCODIV2;
          break;
#endif

        default:
          ret = cmuSelect_HFRCO;
          break;
      }
#endif
      break;

#if defined(_CMU_LFCLKSEL_MASK) || defined(_CMU_LFACLKSEL_MASK)
    case CMU_LFACLKSEL_REG:
#if defined(_CMU_LFCLKSEL_MASK)
      switch (CMU->LFCLKSEL & _CMU_LFCLKSEL_LFA_MASK) {
        case CMU_LFCLKSEL_LFA_LFRCO:
          ret = cmuSelect_LFRCO;
          break;

        case CMU_LFCLKSEL_LFA_LFXO:
          ret = cmuSelect_LFXO;
          break;

#if defined(CMU_LFCLKSEL_LFA_HFCORECLKLEDIV2)
        case CMU_LFCLKSEL_LFA_HFCORECLKLEDIV2:
          ret = cmuSelect_HFCLKLE;
          break;
#endif

        default:
#if defined(CMU_LFCLKSEL_LFAE)
          if (CMU->LFCLKSEL & _CMU_LFCLKSEL_LFAE_MASK) {
            ret = cmuSelect_ULFRCO;
            break;
          }
#else
          ret = cmuSelect_Disabled;
#endif
          break;
      }

#elif defined(_CMU_LFACLKSEL_MASK)
      switch (CMU->LFACLKSEL & _CMU_LFACLKSEL_LFA_MASK) {
        case CMU_LFACLKSEL_LFA_LFRCO:
          ret = cmuSelect_LFRCO;
          break;

        case CMU_LFACLKSEL_LFA_LFXO:
          ret = cmuSelect_LFXO;
          break;

        case CMU_LFACLKSEL_LFA_ULFRCO:
          ret = cmuSelect_ULFRCO;
          break;

#if defined(_CMU_LFACLKSEL_LFA_HFCLKLE)
        case CMU_LFACLKSEL_LFA_HFCLKLE:
          ret = cmuSelect_HFCLKLE;
          break;
#endif

#if defined(CMU_LFACLKSEL_LFA_PLFRCO)
        case CMU_LFACLKSEL_LFA_PLFRCO:
          ret = cmuSelect_PLFRCO;
          break;
#endif

        default:
          ret = cmuSelect_Disabled;
          break;
      }
#endif
      break;
#endif /* _CMU_LFCLKSEL_MASK || _CMU_LFACLKSEL_MASK */

#if defined(_CMU_LFCLKSEL_MASK) || defined(_CMU_LFBCLKSEL_MASK)
    case CMU_LFBCLKSEL_REG:
#if defined(_CMU_LFCLKSEL_MASK)
      switch (CMU->LFCLKSEL & _CMU_LFCLKSEL_LFB_MASK) {
        case CMU_LFCLKSEL_LFB_LFRCO:
          ret = cmuSelect_LFRCO;
          break;

        case CMU_LFCLKSEL_LFB_LFXO:
          ret = cmuSelect_LFXO;
          break;

#if defined(CMU_LFCLKSEL_LFB_HFCORECLKLEDIV2)
        case CMU_LFCLKSEL_LFB_HFCORECLKLEDIV2:
          ret = cmuSelect_HFCLKLE;
          break;
#endif

#if defined(CMU_LFCLKSEL_LFB_HFCLKLE)
        case CMU_LFCLKSEL_LFB_HFCLKLE:
          ret = cmuSelect_HFCLKLE;
          break;
#endif

        default:
#if defined(CMU_LFCLKSEL_LFBE)
          if (CMU->LFCLKSEL & _CMU_LFCLKSEL_LFBE_MASK) {
            ret = cmuSelect_ULFRCO;
            break;
          }
#else
          ret = cmuSelect_Disabled;
#endif
          break;
      }

#elif defined(_CMU_LFBCLKSEL_MASK)
      switch (CMU->LFBCLKSEL & _CMU_LFBCLKSEL_LFB_MASK) {
        case CMU_LFBCLKSEL_LFB_LFRCO:
          ret = cmuSelect_LFRCO;
          break;

        case CMU_LFBCLKSEL_LFB_LFXO:
          ret = cmuSelect_LFXO;
          break;

        case CMU_LFBCLKSEL_LFB_ULFRCO:
          ret = cmuSelect_ULFRCO;
          break;

        case CMU_LFBCLKSEL_LFB_HFCLKLE:
          ret = cmuSelect_HFCLKLE;
          break;

#if defined(CMU_LFBCLKSEL_LFB_PLFRCO)
        case CMU_LFBCLKSEL_LFB_PLFRCO:
          ret = cmuSelect_PLFRCO;
          break;
#endif

        default:
          ret = cmuSelect_Disabled;
          break;
      }
#endif
      break;
#endif /* _CMU_LFCLKSEL_MASK || _CMU_LFBCLKSEL_MASK */

#if defined(_CMU_LFCLKSEL_LFC_MASK)
    case CMU_LFCCLKSEL_REG:
      switch (CMU->LFCLKSEL & _CMU_LFCLKSEL_LFC_MASK) {
        case CMU_LFCLKSEL_LFC_LFRCO:
          ret = cmuSelect_LFRCO;
          break;

        case CMU_LFCLKSEL_LFC_LFXO:
          ret = cmuSelect_LFXO;
          break;

        default:
          ret = cmuSelect_Disabled;
          break;
      }
      break;
#endif

#if defined(_CMU_LFECLKSEL_LFE_MASK)
    case CMU_LFECLKSEL_REG:
      switch (CMU->LFECLKSEL & _CMU_LFECLKSEL_LFE_MASK) {
        case CMU_LFECLKSEL_LFE_LFRCO:
          ret = cmuSelect_LFRCO;
          break;

        case CMU_LFECLKSEL_LFE_LFXO:
          ret = cmuSelect_LFXO;
          break;

        case CMU_LFECLKSEL_LFE_ULFRCO:
          ret = cmuSelect_ULFRCO;
          break;

#if defined (_CMU_LFECLKSEL_LFE_HFCLKLE)
        case CMU_LFECLKSEL_LFE_HFCLKLE:
          ret = cmuSelect_HFCLKLE;
          break;
#endif

#if defined(CMU_LFECLKSEL_LFE_PLFRCO)
        case CMU_LFECLKSEL_LFE_PLFRCO:
          ret = cmuSelect_PLFRCO;
          break;
#endif

        default:
          ret = cmuSelect_Disabled;
          break;
      }
      break;
#endif /* CMU_LFECLKSEL_REG */

    case CMU_DBGCLKSEL_REG:
#if defined(_CMU_DBGCLKSEL_DBG_MASK)
      switch (CMU->DBGCLKSEL & _CMU_DBGCLKSEL_DBG_MASK) {
        case CMU_DBGCLKSEL_DBG_HFCLK:
          ret = cmuSelect_HFCLK;
          break;

        case CMU_DBGCLKSEL_DBG_AUXHFRCO:
          ret = cmuSelect_AUXHFRCO;
          break;
      }

#elif defined(_CMU_CTRL_DBGCLK_MASK)
      switch (CMU->CTRL & _CMU_CTRL_DBGCLK_MASK) {
        case CMU_CTRL_DBGCLK_AUXHFRCO:
          ret = cmuSelect_AUXHFRCO;
          break;

        case CMU_CTRL_DBGCLK_HFCLK:
          ret = cmuSelect_HFCLK;
          break;
      }
#else
      ret = cmuSelect_AUXHFRCO;
#endif
      break;

#if defined(USBC_CLOCK_PRESENT)
    case CMU_USBCCLKSEL_REG:
      switch (CMU->STATUS
              & (CMU_STATUS_USBCLFXOSEL
#if defined(_CMU_STATUS_USBCHFCLKSEL_MASK)
                 | CMU_STATUS_USBCHFCLKSEL
#endif
#if defined(_CMU_STATUS_USBCUSHFRCOSEL_MASK)
                 | CMU_STATUS_USBCUSHFRCOSEL
#endif
                 | CMU_STATUS_USBCLFRCOSEL)) {
#if defined(_CMU_STATUS_USBCHFCLKSEL_MASK)
        case CMU_STATUS_USBCHFCLKSEL:
          ret = cmuSelect_HFCLK;
          break;
#endif

#if defined(_CMU_STATUS_USBCUSHFRCOSEL_MASK)
        case CMU_STATUS_USBCUSHFRCOSEL:
          ret = cmuSelect_USHFRCO;
          break;
#endif

        case CMU_STATUS_USBCLFXOSEL:
          ret = cmuSelect_LFXO;
          break;

        case CMU_STATUS_USBCLFRCOSEL:
          ret = cmuSelect_LFRCO;
          break;

        default:
          ret = cmuSelect_Disabled;
          break;
      }
      break;
#endif

#if defined(_CMU_ADCCTRL_ADC0CLKSEL_MASK)
    case CMU_ADC0ASYNCSEL_REG:
      switch (CMU->ADCCTRL & _CMU_ADCCTRL_ADC0CLKSEL_MASK) {
        case CMU_ADCCTRL_ADC0CLKSEL_DISABLED:
          ret = cmuSelect_Disabled;
          break;

        case CMU_ADCCTRL_ADC0CLKSEL_AUXHFRCO:
          ret = cmuSelect_AUXHFRCO;
          break;

        case CMU_ADCCTRL_ADC0CLKSEL_HFXO:
          ret = cmuSelect_HFXO;
          break;

        case CMU_ADCCTRL_ADC0CLKSEL_HFSRCCLK:
          ret = cmuSelect_HFSRCCLK;
          break;
      }
      break;
#endif

#if defined(_CMU_ADCCTRL_ADC1CLKSEL_MASK)
    case CMU_ADC1ASYNCSEL_REG:
      switch (CMU->ADCCTRL & _CMU_ADCCTRL_ADC1CLKSEL_MASK) {
        case CMU_ADCCTRL_ADC1CLKSEL_DISABLED:
          ret = cmuSelect_Disabled;
          break;

        case CMU_ADCCTRL_ADC1CLKSEL_AUXHFRCO:
          ret = cmuSelect_AUXHFRCO;
          break;

        case CMU_ADCCTRL_ADC1CLKSEL_HFXO:
          ret = cmuSelect_HFXO;
          break;

        case CMU_ADCCTRL_ADC1CLKSEL_HFSRCCLK:
          ret = cmuSelect_HFSRCCLK;
          break;
      }
      break;
#endif

#if defined(_CMU_SDIOCTRL_SDIOCLKSEL_MASK)
    case CMU_SDIOREFSEL_REG:
      switch (CMU->SDIOCTRL & _CMU_SDIOCTRL_SDIOCLKSEL_MASK) {
        case CMU_SDIOCTRL_SDIOCLKSEL_HFRCO:
          ret = cmuSelect_HFRCO;
          break;

        case CMU_SDIOCTRL_SDIOCLKSEL_HFXO:
          ret = cmuSelect_HFXO;
          break;

        case CMU_SDIOCTRL_SDIOCLKSEL_AUXHFRCO:
          ret = cmuSelect_AUXHFRCO;
          break;

        case CMU_SDIOCTRL_SDIOCLKSEL_USHFRCO:
          ret = cmuSelect_USHFRCO;
          break;
      }
      break;
#endif

#if defined(_CMU_QSPICTRL_QSPI0CLKSEL_MASK)
    case CMU_QSPI0REFSEL_REG:
      switch (CMU->QSPICTRL & _CMU_QSPICTRL_QSPI0CLKSEL_MASK) {
        case CMU_QSPICTRL_QSPI0CLKSEL_HFRCO:
          ret = cmuSelect_HFRCO;
          break;

        case CMU_QSPICTRL_QSPI0CLKSEL_HFXO:
          ret = cmuSelect_HFXO;
          break;

        case CMU_QSPICTRL_QSPI0CLKSEL_AUXHFRCO:
          ret = cmuSelect_AUXHFRCO;
          break;

        case CMU_QSPICTRL_QSPI0CLKSEL_USHFRCO:
          ret = cmuSelect_USHFRCO;
          break;
      }
      break;
#endif

#if defined(_CMU_USBCTRL_USBCLKSEL_MASK)
    case CMU_USBRCLKSEL_REG:
      switch (CMU->USBCTRL & _CMU_USBCTRL_USBCLKSEL_MASK) {
        case CMU_USBCTRL_USBCLKSEL_USHFRCO:
          ret = cmuSelect_USHFRCO;
          break;

        case CMU_USBCTRL_USBCLKSEL_HFXO:
          ret = cmuSelect_HFXO;
          break;

        case CMU_USBCTRL_USBCLKSEL_HFXOX2:
          ret = cmuSelect_HFXOX2;
          break;

        case CMU_USBCTRL_USBCLKSEL_HFRCO:
          ret = cmuSelect_HFRCO;
          break;

        case CMU_USBCTRL_USBCLKSEL_LFXO:
          ret = cmuSelect_LFXO;
          break;

        case CMU_USBCTRL_USBCLKSEL_LFRCO:
          ret = cmuSelect_LFRCO;
          break;
      }
      break;
#endif

    default:
      EFM_ASSERT(0);
      ret = cmuSelect_Error;
      break;
  }

  return ret;
}

/***************************************************************************//**
 * @brief
 *   Select reference clock/oscillator used for a clock branch.
 *
 * @details
 *   Notice that if a selected reference is not enabled prior to selecting its
 *   use, it will be enabled, and this function will wait for the selected
 *   oscillator to be stable. It will however NOT be disabled if another
 *   reference clock is selected later.
 *
 *   This feature is particularly important if selecting a new reference
 *   clock for the clock branch clocking the core, otherwise the system
 *   may halt.
 *
 * @param[in] clock
 *   Clock branch to select reference clock for. One of:
 *   @li #cmuClock_HF
 *   @li #cmuClock_LFA
 *   @li #cmuClock_LFB
 *   @if _CMU_LFCCLKEN0_MASK
 *   @li #cmuClock_LFC
 *   @endif
 *   @if _CMU_LFECLKEN0_MASK
 *   @li #cmuClock_LFE
 *   @endif
 *   @li #cmuClock_DBG
 *   @if _CMU_CMD_USBCLKSEL_MASK
 *   @li #cmuClock_USBC
 *   @endif
 *   @if _CMU_USBCTRL_MASK
 *   @li #cmuClock_USBR
 *   @endif
 *
 * @param[in] ref
 *   Reference selected for clocking, please refer to reference manual for
 *   for details on which reference is available for a specific clock branch.
 *   @li #cmuSelect_HFRCO
 *   @li #cmuSelect_LFRCO
 *   @li #cmuSelect_HFXO
 *   @if _CMU_HFXOCTRL_HFXOX2EN_MASK
 *   @li #cmuSelect_HFXOX2
 *   @endif
 *   @li #cmuSelect_LFXO
 *   @li #cmuSelect_HFCLKLE
 *   @li #cmuSelect_AUXHFRCO
 *   @if _CMU_USHFRCOCTRL_MASK
 *   @li #cmuSelect_USHFRCO
 *   @endif
 *   @li #cmuSelect_HFCLK
 *   @ifnot DOXYDOC_EFM32_GECKO_FAMILY
 *   @li #cmuSelect_ULFRCO
 *   @li #cmuSelect_PLFRCO
 *   @endif
 ******************************************************************************/
void CMU_ClockSelectSet(CMU_Clock_TypeDef clock, CMU_Select_TypeDef ref)
{
  uint32_t              select = cmuOsc_HFRCO;
  CMU_Osc_TypeDef       osc    = cmuOsc_HFRCO;
  uint32_t              freq;
  uint32_t              tmp;
  uint32_t              selRegId;
#if defined(_SILICON_LABS_32B_SERIES_1)
  volatile uint32_t     *selReg = NULL;
#endif
#if defined(CMU_LFCLKSEL_LFAE_ULFRCO)
  uint32_t              lfExtended = 0;
#endif

#if defined(_EMU_CMD_EM01VSCALE0_MASK)
  uint32_t              vScaleFrequency = 0; /* Use default */

  /* Start voltage upscaling before clock is set. */
  if (clock == cmuClock_HF) {
    if (ref == cmuSelect_HFXO) {
      vScaleFrequency = SystemHFXOClockGet();
    } else if ((ref == cmuSelect_HFRCO)
               && (CMU_HFRCOBandGet() > CMU_VSCALEEM01_LOWPOWER_VOLTAGE_CLOCK_MAX)) {
      vScaleFrequency = CMU_HFRCOBandGet();
    }
    if (vScaleFrequency != 0) {
      EMU_VScaleEM01ByClock(vScaleFrequency, false);
    }
  }
#endif

  selRegId = (clock >> CMU_SEL_REG_POS) & CMU_SEL_REG_MASK;

  switch (selRegId) {
    case CMU_HFCLKSEL_REG:
      switch (ref) {
        case cmuSelect_LFXO:
#if defined(_SILICON_LABS_32B_SERIES_1)
          select = CMU_HFCLKSEL_HF_LFXO;
#elif defined(_SILICON_LABS_32B_SERIES_0)
          select = CMU_CMD_HFCLKSEL_LFXO;
#endif
          osc = cmuOsc_LFXO;
          break;

        case cmuSelect_LFRCO:
#if defined(_SILICON_LABS_32B_SERIES_1)
          select = CMU_HFCLKSEL_HF_LFRCO;
#elif defined(_SILICON_LABS_32B_SERIES_0)
          select = CMU_CMD_HFCLKSEL_LFRCO;
#endif
          osc = cmuOsc_LFRCO;
          break;

        case cmuSelect_HFXO:
#if defined(CMU_HFCLKSEL_HF_HFXO)
          select = CMU_HFCLKSEL_HF_HFXO;
#elif defined(CMU_CMD_HFCLKSEL_HFXO)
          select = CMU_CMD_HFCLKSEL_HFXO;
#endif
          osc = cmuOsc_HFXO;
#if defined(CMU_MAX_FREQ_HFLE)
          /* Set 1 HFLE wait-state until the new HFCLKLE frequency is known.
             This is known after 'select' is written below. */
          setHfLeConfig(CMU_MAX_FREQ_HFLE + 1);
#endif
#if defined(CMU_CTRL_HFXOBUFCUR_BOOSTABOVE32MHZ)
          /* Adjust HFXO buffer current for frequencies above 32MHz */
          if (SystemHFXOClockGet() > 32000000) {
            CMU->CTRL = (CMU->CTRL & ~_CMU_CTRL_HFXOBUFCUR_MASK)
                        | CMU_CTRL_HFXOBUFCUR_BOOSTABOVE32MHZ;
          } else {
            CMU->CTRL = (CMU->CTRL & ~_CMU_CTRL_HFXOBUFCUR_MASK)
                        | CMU_CTRL_HFXOBUFCUR_BOOSTUPTO32MHZ;
          }
#endif
          break;

        case cmuSelect_HFRCO:
#if defined(_SILICON_LABS_32B_SERIES_1)
          select = CMU_HFCLKSEL_HF_HFRCO;
#elif defined(_SILICON_LABS_32B_SERIES_0)
          select = CMU_CMD_HFCLKSEL_HFRCO;
#endif
          osc = cmuOsc_HFRCO;
#if defined(CMU_MAX_FREQ_HFLE)
          /* Set 1 HFLE wait-state until the new HFCLKLE frequency is known.
             This is known after 'select' is written below. */
          setHfLeConfig(CMU_MAX_FREQ_HFLE + 1);
#endif
          break;

#if defined(CMU_CMD_HFCLKSEL_USHFRCODIV2)
        case cmuSelect_USHFRCODIV2:
          select = CMU_CMD_HFCLKSEL_USHFRCODIV2;
          osc = cmuOsc_USHFRCO;
          break;
#endif

#if defined(CMU_LFCLKSEL_LFAE_ULFRCO) || defined(CMU_LFACLKSEL_LFA_ULFRCO)
        case cmuSelect_ULFRCO:
          /* ULFRCO cannot be used as HFCLK  */
          EFM_ASSERT(0);
          return;
#endif

        default:
          EFM_ASSERT(0);
          return;
      }

      /* Ensure selected oscillator is enabled, waiting for it to stabilize */
      CMU_OscillatorEnable(osc, true, true);

      /* Configure worst case wait states for flash access before selecting */
      flashWaitStateMax();

#if defined(_EMU_CMD_EM01VSCALE0_MASK)
      /* Wait for voltage upscaling to complete before clock is set. */
      if (vScaleFrequency != 0) {
        EMU_VScaleWait();
      }
#endif

      /* Switch to selected oscillator */
#if defined(_CMU_HFCLKSEL_MASK)
      CMU->HFCLKSEL = select;
#else
      CMU->CMD = select;
#endif
#if defined(CMU_MAX_FREQ_HFLE)
      /* Update HFLE configuration after 'select' is set.
         Note that the HFCLKLE clock is connected differently on planform 1 and 2 */
      setHfLeConfig(CMU_ClockFreqGet(cmuClock_HFLE));
#endif

      /* Update CMSIS core clock variable */
      /* (The function will update the global variable) */
      freq = SystemCoreClockGet();

      /* Optimize flash access wait state setting for currently selected core clk */
      CMU_UpdateWaitStates(freq, VSCALE_DEFAULT);

#if defined(_EMU_CMD_EM01VSCALE0_MASK)
      /* Keep EMU module informed on source HF clock frequency. This will apply voltage
         downscaling after clock is set if downscaling is configured. */
      if (vScaleFrequency == 0) {
        EMU_VScaleEM01ByClock(0, true);
      }
#endif
      break;

#if defined(_SILICON_LABS_32B_SERIES_1)
    case CMU_LFACLKSEL_REG:
      selReg = (selReg == NULL) ? &CMU->LFACLKSEL : selReg;
#if !defined(_CMU_LFACLKSEL_LFA_HFCLKLE)
      /* HFCLKCLE can not be used as LFACLK */
      EFM_ASSERT(ref != cmuSelect_HFCLKLE);
#endif
      /* Fall through and select clock source */

#if defined(_CMU_LFCCLKSEL_MASK)
    case CMU_LFCCLKSEL_REG:
      selReg = (selReg == NULL) ? &CMU->LFCCLKSEL : selReg;
#if !defined(_CMU_LFCCLKSEL_LFC_HFCLKLE)
      /* HFCLKCLE can not be used as LFCCLK */
      EFM_ASSERT(ref != cmuSelect_HFCLKLE);
#endif
#endif
    /* Fall through and select clock source */

    case CMU_LFECLKSEL_REG:
      selReg = (selReg == NULL) ? &CMU->LFECLKSEL : selReg;
#if !defined(_CMU_LFECLKSEL_LFE_HFCLKLE)
      /* HFCLKCLE can not be used as LFECLK */
      EFM_ASSERT(ref != cmuSelect_HFCLKLE);
#endif
    /* Fall through and select clock source */

    case CMU_LFBCLKSEL_REG:
      selReg = (selReg == NULL) ? &CMU->LFBCLKSEL : selReg;
      switch (ref) {
        case cmuSelect_Disabled:
          tmp = _CMU_LFACLKSEL_LFA_DISABLED;
          break;

        case cmuSelect_LFXO:
          /* Ensure selected oscillator is enabled, waiting for it to stabilize */
          CMU_OscillatorEnable(cmuOsc_LFXO, true, true);
          tmp = _CMU_LFACLKSEL_LFA_LFXO;
          break;

        case cmuSelect_LFRCO:
          /* Ensure selected oscillator is enabled, waiting for it to stabilize */
          CMU_OscillatorEnable(cmuOsc_LFRCO, true, true);
          tmp = _CMU_LFACLKSEL_LFA_LFRCO;
          break;

        case cmuSelect_HFCLKLE:
          /* Ensure correct HFLE wait-states and enable HFCLK to LE */
          setHfLeConfig(SystemCoreClockGet());
          BUS_RegBitWrite(&CMU->HFBUSCLKEN0, _CMU_HFBUSCLKEN0_LE_SHIFT, 1);
          tmp = _CMU_LFBCLKSEL_LFB_HFCLKLE;
          break;

        case cmuSelect_ULFRCO:
          /* ULFRCO is always on, there is no need to enable it. */
          tmp = _CMU_LFACLKSEL_LFA_ULFRCO;
          break;

#if defined(_CMU_STATUS_PLFRCOENS_MASK)
        case cmuSelect_PLFRCO:
          /* Ensure selected oscillator is enabled, waiting for it to stabilize */
          CMU_OscillatorEnable(cmuOsc_PLFRCO, true, true);
          tmp = _CMU_LFACLKSEL_LFA_PLFRCO;
          break;
#endif

        default:
          EFM_ASSERT(0);
          return;
      }
      *selReg = tmp;
      break;

#elif defined(_SILICON_LABS_32B_SERIES_0)
    case CMU_LFACLKSEL_REG:
    case CMU_LFBCLKSEL_REG:
      switch (ref) {
        case cmuSelect_Disabled:
          tmp = _CMU_LFCLKSEL_LFA_DISABLED;
          break;

        case cmuSelect_LFXO:
          /* Ensure selected oscillator is enabled, waiting for it to stabilize */
          CMU_OscillatorEnable(cmuOsc_LFXO, true, true);
          tmp = _CMU_LFCLKSEL_LFA_LFXO;
          break;

        case cmuSelect_LFRCO:
          /* Ensure selected oscillator is enabled, waiting for it to stabilize */
          CMU_OscillatorEnable(cmuOsc_LFRCO, true, true);
          tmp = _CMU_LFCLKSEL_LFA_LFRCO;
          break;

        case cmuSelect_HFCLKLE:
#if defined(CMU_MAX_FREQ_HFLE)
          /* Set HFLE wait-state and divider */
          freq = SystemCoreClockGet();
          setHfLeConfig(freq);
#endif
          /* Ensure HFCORE to LE clocking is enabled */
          BUS_RegBitWrite(&CMU->HFCORECLKEN0, _CMU_HFCORECLKEN0_LE_SHIFT, 1);
          tmp = _CMU_LFCLKSEL_LFA_HFCORECLKLEDIV2;
          break;

#if defined(CMU_LFCLKSEL_LFAE_ULFRCO)
        case cmuSelect_ULFRCO:
          /* ULFRCO is always enabled */
          tmp = _CMU_LFCLKSEL_LFA_DISABLED;
          lfExtended = 1;
          break;
#endif

        default:
          /* Illegal clock source for LFA/LFB selected */
          EFM_ASSERT(0);
          return;
      }

      /* Apply select */
      if (selRegId == CMU_LFACLKSEL_REG) {
#if defined(_CMU_LFCLKSEL_LFAE_MASK)
        CMU->LFCLKSEL = (CMU->LFCLKSEL
                         & ~(_CMU_LFCLKSEL_LFA_MASK | _CMU_LFCLKSEL_LFAE_MASK))
                        | (tmp << _CMU_LFCLKSEL_LFA_SHIFT)
                        | (lfExtended << _CMU_LFCLKSEL_LFAE_SHIFT);
#else
        CMU->LFCLKSEL = (CMU->LFCLKSEL & ~_CMU_LFCLKSEL_LFA_MASK)
                        | (tmp << _CMU_LFCLKSEL_LFA_SHIFT);
#endif
      } else {
#if defined(_CMU_LFCLKSEL_LFBE_MASK)
        CMU->LFCLKSEL = (CMU->LFCLKSEL
                         & ~(_CMU_LFCLKSEL_LFB_MASK | _CMU_LFCLKSEL_LFBE_MASK))
                        | (tmp << _CMU_LFCLKSEL_LFB_SHIFT)
                        | (lfExtended << _CMU_LFCLKSEL_LFBE_SHIFT);
#else
        CMU->LFCLKSEL = (CMU->LFCLKSEL & ~_CMU_LFCLKSEL_LFB_MASK)
                        | (tmp << _CMU_LFCLKSEL_LFB_SHIFT);
#endif
      }
      break;

#if defined(_CMU_LFCLKSEL_LFC_MASK)
    case CMU_LFCCLKSEL_REG:
      switch (ref) {
        case cmuSelect_Disabled:
          tmp = _CMU_LFCLKSEL_LFA_DISABLED;
          break;

        case cmuSelect_LFXO:
          /* Ensure selected oscillator is enabled, waiting for it to stabilize */
          CMU_OscillatorEnable(cmuOsc_LFXO, true, true);
          tmp = _CMU_LFCLKSEL_LFC_LFXO;
          break;

        case cmuSelect_LFRCO:
          /* Ensure selected oscillator is enabled, waiting for it to stabilize */
          CMU_OscillatorEnable(cmuOsc_LFRCO, true, true);
          tmp = _CMU_LFCLKSEL_LFC_LFRCO;
          break;

        default:
          /* Illegal clock source for LFC selected */
          EFM_ASSERT(0);
          return;
      }

      /* Apply select */
      CMU->LFCLKSEL = (CMU->LFCLKSEL & ~_CMU_LFCLKSEL_LFC_MASK)
                      | (tmp << _CMU_LFCLKSEL_LFC_SHIFT);
      break;
#endif
#endif

#if defined(_CMU_DBGCLKSEL_DBG_MASK) || defined(CMU_CTRL_DBGCLK)
    case CMU_DBGCLKSEL_REG:
      switch (ref) {
#if defined(_CMU_DBGCLKSEL_DBG_MASK)
        case cmuSelect_AUXHFRCO:
          /* Select AUXHFRCO as debug clock */
          CMU->DBGCLKSEL = CMU_DBGCLKSEL_DBG_AUXHFRCO;
          break;

        case cmuSelect_HFCLK:
          /* Select divided HFCLK as debug clock */
          CMU->DBGCLKSEL = CMU_DBGCLKSEL_DBG_HFCLK;
          break;
#endif

#if defined(CMU_CTRL_DBGCLK)
        case cmuSelect_AUXHFRCO:
          /* Select AUXHFRCO as debug clock */
          CMU->CTRL = (CMU->CTRL & ~(_CMU_CTRL_DBGCLK_MASK))
                      | CMU_CTRL_DBGCLK_AUXHFRCO;
          break;

        case cmuSelect_HFCLK:
          /* Select divided HFCLK as debug clock */
          CMU->CTRL = (CMU->CTRL & ~(_CMU_CTRL_DBGCLK_MASK))
                      | CMU_CTRL_DBGCLK_HFCLK;
          break;
#endif

        default:
          /* Illegal clock source for debug selected */
          EFM_ASSERT(0);
          return;
      }
      break;
#endif

#if defined(USBC_CLOCK_PRESENT)
    case CMU_USBCCLKSEL_REG:
      switch (ref) {
        case cmuSelect_LFXO:
          /* Select LFXO as clock source for USB, can only be used in sleep mode */
          /* Ensure selected oscillator is enabled, waiting for it to stabilize */
          CMU_OscillatorEnable(cmuOsc_LFXO, true, true);

          /* Switch oscillator */
          CMU->CMD = CMU_CMD_USBCCLKSEL_LFXO;

          /* Wait until clock is activated */
          while ((CMU->STATUS & CMU_STATUS_USBCLFXOSEL) == 0) {
          }
          break;

        case cmuSelect_LFRCO:
          /* Select LFRCO as clock source for USB, can only be used in sleep mode */
          /* Ensure selected oscillator is enabled, waiting for it to stabilize */
          CMU_OscillatorEnable(cmuOsc_LFRCO, true, true);

          /* Switch oscillator */
          CMU->CMD = CMU_CMD_USBCCLKSEL_LFRCO;

          /* Wait until clock is activated */
          while ((CMU->STATUS & CMU_STATUS_USBCLFRCOSEL) == 0) {
          }
          break;

#if defined(CMU_STATUS_USBCHFCLKSEL)
        case cmuSelect_HFCLK:
          /* Select undivided HFCLK as clock source for USB */
          /* Oscillator must already be enabled to avoid a core lockup */
          CMU->CMD = CMU_CMD_USBCCLKSEL_HFCLKNODIV;
          /* Wait until clock is activated */
          while ((CMU->STATUS & CMU_STATUS_USBCHFCLKSEL) == 0) {
          }
          break;
#endif

#if defined(CMU_CMD_USBCCLKSEL_USHFRCO)
        case cmuSelect_USHFRCO:
          /* Select USHFRCO as clock source for USB */
          /* Ensure selected oscillator is enabled, waiting for it to stabilize */
          CMU_OscillatorEnable(cmuOsc_USHFRCO, true, true);

          /* Switch oscillator */
          CMU->CMD = CMU_CMD_USBCCLKSEL_USHFRCO;

          /* Wait until clock is activated */
          while ((CMU->STATUS & CMU_STATUS_USBCUSHFRCOSEL) == 0) {
          }
          break;
#endif

        default:
          /* Illegal clock source for USB */
          EFM_ASSERT(0);
          return;
      }
      break;
#endif

#if defined(_CMU_ADCCTRL_ADC0CLKSEL_MASK)
    case CMU_ADC0ASYNCSEL_REG:
      switch (ref) {
        case cmuSelect_Disabled:
          tmp = _CMU_ADCCTRL_ADC0CLKSEL_DISABLED;
          break;

        case cmuSelect_AUXHFRCO:
          /* Ensure selected oscillator is enabled, waiting for it to stabilize */
          CMU_OscillatorEnable(cmuOsc_AUXHFRCO, true, true);
          tmp = _CMU_ADCCTRL_ADC0CLKSEL_AUXHFRCO;
          break;

        case cmuSelect_HFXO:
          /* Ensure selected oscillator is enabled, waiting for it to stabilize */
          CMU_OscillatorEnable(cmuOsc_HFXO, true, true);
          tmp = _CMU_ADCCTRL_ADC0CLKSEL_HFXO;
          break;

        case cmuSelect_HFSRCCLK:
          tmp = _CMU_ADCCTRL_ADC0CLKSEL_HFSRCCLK;
          break;

        default:
          /* Illegal clock source for ADC0ASYNC selected */
          EFM_ASSERT(0);
          return;
      }

      /* Apply select */
      CMU->ADCCTRL = (CMU->ADCCTRL & ~_CMU_ADCCTRL_ADC0CLKSEL_MASK)
                     | (tmp << _CMU_ADCCTRL_ADC0CLKSEL_SHIFT);
      break;
#endif

#if defined(_CMU_ADCCTRL_ADC1CLKSEL_MASK)
    case CMU_ADC1ASYNCSEL_REG:
      switch (ref) {
        case cmuSelect_Disabled:
          tmp = _CMU_ADCCTRL_ADC1CLKSEL_DISABLED;
          break;

        case cmuSelect_AUXHFRCO:
          /* Ensure selected oscillator is enabled, waiting for it to stabilize */
          CMU_OscillatorEnable(cmuOsc_AUXHFRCO, true, true);
          tmp = _CMU_ADCCTRL_ADC1CLKSEL_AUXHFRCO;
          break;

        case cmuSelect_HFXO:
          /* Ensure selected oscillator is enabled, waiting for it to stabilize */
          CMU_OscillatorEnable(cmuOsc_HFXO, true, true);
          tmp = _CMU_ADCCTRL_ADC1CLKSEL_HFXO;
          break;

        case cmuSelect_HFSRCCLK:
          tmp = _CMU_ADCCTRL_ADC1CLKSEL_HFSRCCLK;
          break;

        default:
          /* Illegal clock source for ADC1ASYNC selected */
          EFM_ASSERT(0);
          return;
      }

      /* Apply select */
      CMU->ADCCTRL = (CMU->ADCCTRL & ~_CMU_ADCCTRL_ADC1CLKSEL_MASK)
                     | (tmp << _CMU_ADCCTRL_ADC1CLKSEL_SHIFT);
      break;
#endif

#if defined(_CMU_SDIOCTRL_SDIOCLKSEL_MASK)
    case CMU_SDIOREFSEL_REG:
      switch (ref) {
        case cmuSelect_HFRCO:
          /* Ensure selected oscillator is enabled, waiting for it to stabilize */
          CMU_OscillatorEnable(cmuOsc_HFRCO, true, true);
          tmp = _CMU_SDIOCTRL_SDIOCLKSEL_HFRCO;
          break;

        case cmuSelect_HFXO:
          /* Ensure selected oscillator is enabled, waiting for it to stabilize */
          CMU_OscillatorEnable(cmuOsc_HFXO, true, true);
          tmp = _CMU_SDIOCTRL_SDIOCLKSEL_HFXO;
          break;

        case cmuSelect_AUXHFRCO:
          /* Ensure selected oscillator is enabled, waiting for it to stabilize */
          CMU_OscillatorEnable(cmuOsc_AUXHFRCO, true, true);
          tmp = _CMU_SDIOCTRL_SDIOCLKSEL_AUXHFRCO;
          break;

        case cmuSelect_USHFRCO:
          /* Ensure selected oscillator is enabled, waiting for it to stabilize */
          CMU_OscillatorEnable(cmuOsc_USHFRCO, true, true);
          tmp = _CMU_SDIOCTRL_SDIOCLKSEL_USHFRCO;
          break;

        default:
          /* Illegal clock source for SDIOREF selected */
          EFM_ASSERT(0);
          return;
      }

      /* Apply select */
      CMU->SDIOCTRL = (CMU->SDIOCTRL & ~_CMU_SDIOCTRL_SDIOCLKSEL_MASK)
                      | (tmp << _CMU_SDIOCTRL_SDIOCLKSEL_SHIFT);
      break;
#endif

#if defined(_CMU_QSPICTRL_QSPI0CLKSEL_MASK)
    case CMU_QSPI0REFSEL_REG:
      switch (ref) {
        case cmuSelect_HFRCO:
          /* Ensure selected oscillator is enabled, waiting for it to stabilize */
          CMU_OscillatorEnable(cmuOsc_HFRCO, true, true);
          tmp = _CMU_QSPICTRL_QSPI0CLKSEL_HFRCO;
          break;

        case cmuSelect_HFXO:
          /* Ensure selected oscillator is enabled, waiting for it to stabilize */
          CMU_OscillatorEnable(cmuOsc_HFXO, true, true);
          tmp = _CMU_QSPICTRL_QSPI0CLKSEL_HFXO;
          break;

        case cmuSelect_AUXHFRCO:
          /* Ensure selected oscillator is enabled, waiting for it to stabilize */
          CMU_OscillatorEnable(cmuOsc_AUXHFRCO, true, true);
          tmp = _CMU_QSPICTRL_QSPI0CLKSEL_AUXHFRCO;
          break;

        case cmuSelect_USHFRCO:
          /* Ensure selected oscillator is enabled, waiting for it to stabilize */
          CMU_OscillatorEnable(cmuOsc_USHFRCO, true, true);
          tmp = _CMU_QSPICTRL_QSPI0CLKSEL_USHFRCO;
          break;

        default:
          /* Illegal clock source for QSPI0REF selected */
          EFM_ASSERT(0);
          return;
      }

      /* Apply select */
      CMU->QSPICTRL = (CMU->QSPICTRL & ~_CMU_QSPICTRL_QSPI0CLKSEL_MASK)
                      | (tmp << _CMU_QSPICTRL_QSPI0CLKSEL_SHIFT);
      break;
#endif

#if defined(_CMU_USBCTRL_USBCLKSEL_MASK)
    case CMU_USBRCLKSEL_REG:
      switch (ref) {
        case cmuSelect_USHFRCO:
          /* Ensure selected oscillator is enabled, waiting for it to stabilize */
          CMU_OscillatorEnable(cmuOsc_USHFRCO, true, true);
          tmp = _CMU_USBCTRL_USBCLKSEL_USHFRCO;
          break;

        case cmuSelect_HFXO:
          /* Ensure selected oscillator is enabled, waiting for it to stabilize */
          CMU_OscillatorEnable(cmuOsc_HFXO, true, true);
          tmp = _CMU_USBCTRL_USBCLKSEL_HFXO;
          break;

        case cmuSelect_HFXOX2:
          /* Only allowed for HFXO frequencies up to 25 MHz */
          EFM_ASSERT(SystemHFXOClockGet() <= 25000000u);

          /* Enable HFXO X2 */
          CMU->HFXOCTRL |= CMU_HFXOCTRL_HFXOX2EN;

          /* Ensure selected oscillator is enabled, waiting for it to stabilize */
          CMU_OscillatorEnable(cmuOsc_HFXO, true, true);

          tmp = _CMU_USBCTRL_USBCLKSEL_HFXOX2;
          break;

        case cmuSelect_HFRCO:
          /* Ensure selected oscillator is enabled, waiting for it to stabilize */
          CMU_OscillatorEnable(cmuOsc_HFRCO, true, true);
          tmp = _CMU_USBCTRL_USBCLKSEL_HFRCO;
          break;

        case cmuSelect_LFXO:
          /* Ensure selected oscillator is enabled, waiting for it to stabilize */
          CMU_OscillatorEnable(cmuOsc_LFXO, true, true);
          tmp = _CMU_USBCTRL_USBCLKSEL_LFXO;
          break;

        case cmuSelect_LFRCO:
          /* Ensure selected oscillator is enabled, waiting for it to stabilize */
          CMU_OscillatorEnable(cmuOsc_LFRCO, true, true);
          tmp = _CMU_USBCTRL_USBCLKSEL_LFRCO;
          break;

        default:
          /* Illegal clock source for USBR selected */
          EFM_ASSERT(0);
          return;
      }

      /* Apply select */
      CMU->USBCTRL = (CMU->USBCTRL & ~_CMU_USBCTRL_USBCLKSEL_MASK)
                     | (tmp << _CMU_USBCTRL_USBCLKSEL_SHIFT);
      break;
#endif

    default:
      EFM_ASSERT(0);
      break;
  }
}

#if defined(CMU_OSCENCMD_DPLLEN)
/**************************************************************************//**
 * @brief
 *   Lock the DPLL to a given frequency.
 *
 *   The frequency is given by: Fout = Fref * (N+1) / (M+1).
 *
 * @note
 *   This function does not check if the given N & M values will actually
 *   produce the desired target frequency.
 *   Any peripheral running off HFRCO should be switched to HFRCODIV2 prior to
 *   calling this function to avoid over-clocking.
 *
 * @param[in] init
 *    DPLL setup parameters.
 *
 * @return
 *   Returns false on invalid target frequency or DPLL locking error.
 *****************************************************************************/
bool CMU_DPLLLock(CMU_DPLLInit_TypeDef *init)
{
  int index = 0;
  unsigned int i;
  bool hfrcoDiv2 = false;
  uint32_t hfrcoCtrlVal, lockStatus, sysFreq;

  EFM_ASSERT(init->frequency >= hfrcoCtrlTable[0].minFreq);
  EFM_ASSERT(init->frequency
             <= hfrcoCtrlTable[HFRCOCTRLTABLE_ENTRIES - 1].maxFreq);
  EFM_ASSERT(init->n >= 32);
  EFM_ASSERT(init->n <= (_CMU_DPLLCTRL1_N_MASK >> _CMU_DPLLCTRL1_N_SHIFT));
  EFM_ASSERT(init->m <= (_CMU_DPLLCTRL1_M_MASK >> _CMU_DPLLCTRL1_M_SHIFT));
  EFM_ASSERT(init->ssInterval  <= (_CMU_HFRCOSS_SSINV_MASK
                                   >> _CMU_HFRCOSS_SSINV_SHIFT));
  EFM_ASSERT(init->ssAmplitude <= (_CMU_HFRCOSS_SSAMP_MASK
                                   >> _CMU_HFRCOSS_SSAMP_SHIFT));

#if defined(_EMU_STATUS_VSCALE_MASK)
  if ((EMU_VScaleGet() == emuVScaleEM01_LowPower)
      && (init->frequency > CMU_VSCALEEM01_LOWPOWER_VOLTAGE_CLOCK_MAX)) {
    EFM_ASSERT(false);
    return false;
  }
#endif

  // Find correct HFRCO band, and retrieve a HFRCOCTRL value.
  for (i = 0; i < HFRCOCTRLTABLE_ENTRIES; i++) {
    if ((init->frequency    >= hfrcoCtrlTable[i].minFreq)
        && (init->frequency <= hfrcoCtrlTable[i].maxFreq)) {
      index = i;                            // Correct band found
      break;
    }
  }
  if (index == HFRCOCTRLTABLE_ENTRIES) {
    EFM_ASSERT(false);
    return false;                           // Target frequency out of spec.
  }
  hfrcoCtrlVal = hfrcoCtrlTable[index].value;

  // Check if we have a calibrated HFRCOCTRL.TUNING value in device DI page.
  if (hfrcoCtrlTable[index].band != (CMU_HFRCOFreq_TypeDef)0) {
    uint32_t tuning;

    tuning = (CMU_HFRCODevinfoGet(hfrcoCtrlTable[index].band)
              & _CMU_HFRCOCTRL_TUNING_MASK)
             >> _CMU_HFRCOCTRL_TUNING_SHIFT;

    // When HFRCOCTRL.FINETUNINGEN is enabled, the center frequency
    // of the band shifts down by 5.8%. We subtract 9 to compensate.
    if (tuning > 9) {
      tuning -= 9;
    } else {
      tuning = 0;
    }

    hfrcoCtrlVal |= tuning << _CMU_HFRCOCTRL_TUNING_SHIFT;
  }

  // Update CMSIS frequency SystemHfrcoFreq value.
  SystemHfrcoFreq = init->frequency;

  // Set max wait-states while changing core clock.
  if (CMU_ClockSelectGet(cmuClock_HF) == cmuSelect_HFRCO) {
    flashWaitStateMax();
  }

  // Update HFLE configuration before updating HFRCO, use new DPLL frequency.
  if (CMU_ClockSelectGet(cmuClock_HF) == cmuSelect_HFRCO) {
    setHfLeConfig(init->frequency);

    // Switch to HFRCO/2 before setting DPLL to avoid over-clocking.
    hfrcoDiv2 = (CMU->HFCLKSTATUS & _CMU_HFCLKSTATUS_SELECTED_MASK)
                == CMU_HFCLKSTATUS_SELECTED_HFRCODIV2;
    CMU->HFCLKSEL = CMU_HFCLKSEL_HF_HFRCODIV2;
  }

  CMU->OSCENCMD  = CMU_OSCENCMD_DPLLDIS;
  while ((CMU->STATUS & (CMU_STATUS_DPLLENS | CMU_STATUS_DPLLRDY)) != 0) ;
  CMU->IFC       = CMU_IFC_DPLLRDY | CMU_IFC_DPLLLOCKFAILLOW
                   | CMU_IFC_DPLLLOCKFAILHIGH;
  CMU->DPLLCTRL1 = (init->n   << _CMU_DPLLCTRL1_N_SHIFT)
                   | (init->m << _CMU_DPLLCTRL1_M_SHIFT);
  CMU->HFRCOCTRL = hfrcoCtrlVal;
  CMU->DPLLCTRL  = (init->refClk        << _CMU_DPLLCTRL_REFSEL_SHIFT)
                   | (init->autoRecover << _CMU_DPLLCTRL_AUTORECOVER_SHIFT)
                   | (init->edgeSel     << _CMU_DPLLCTRL_EDGESEL_SHIFT)
                   | (init->lockMode    << _CMU_DPLLCTRL_MODE_SHIFT);
  CMU->OSCENCMD  = CMU_OSCENCMD_DPLLEN;
  while ((lockStatus = (CMU->IF & (CMU_IF_DPLLRDY
                                   | CMU_IF_DPLLLOCKFAILLOW
                                   | CMU_IF_DPLLLOCKFAILHIGH))) == 0) ;

  if ((CMU_ClockSelectGet(cmuClock_HF) == cmuSelect_HFRCO)
      && (hfrcoDiv2 == false)) {
    CMU->HFCLKSEL = CMU_HFCLKSEL_HF_HFRCO;
  }

  // If HFRCO is selected as HF clock, optimize flash access wait-state
  // configuration for this frequency and update CMSIS core clock variable.
  if (CMU_ClockSelectGet(cmuClock_HF) == cmuSelect_HFRCO) {
    // Call SystemCoreClockGet() to update CMSIS core clock variable.
    sysFreq = SystemCoreClockGet();
    EFM_ASSERT(sysFreq <= init->frequency);
    EFM_ASSERT(sysFreq <= SystemHfrcoFreq);
    EFM_ASSERT(init->frequency == SystemHfrcoFreq);
    CMU_UpdateWaitStates(sysFreq, VSCALE_DEFAULT);
  }

  // Reduce HFLE frequency if possible.
  setHfLeConfig(CMU_ClockFreqGet(cmuClock_HFLE));

#if defined(_EMU_CMD_EM01VSCALE0_MASK)
  // Update voltage scaling.
  EMU_VScaleEM01ByClock(0, true);
#endif

  if (lockStatus == CMU_IF_DPLLRDY) {
    return true;
  }
  return false;
}
#endif // CMU_OSCENCMD_DPLLEN

/**************************************************************************//**
 * @brief
 *   CMU low frequency register synchronization freeze control.
 *
 * @details
 *   Some CMU registers requires synchronization into the low frequency (LF)
 *   domain. The freeze feature allows for several such registers to be
 *   modified before passing them to the LF domain simultaneously (which
 *   takes place when the freeze mode is disabled).
 *
 *   Another usage scenario of this feature, is when using an API (such
 *   as the CMU API) for modifying several bit fields consecutively in the
 *   same register. If freeze mode is enabled during this sequence, stalling
 *   can be avoided.
 *
 * @note
 *   When enabling freeze mode, this function will wait for all current
 *   ongoing CMU synchronization to LF domain to complete (Normally
 *   synchronization will not be in progress.) However for this reason, when
 *   using freeze mode, modifications of registers requiring LF synchronization
 *   should be done within one freeze enable/disable block to avoid unecessary
 *   stalling.
 *
 * @param[in] enable
 *   @li true - enable freeze, modified registers are not propagated to the
 *       LF domain
 *   @li false - disable freeze, modified registers are propagated to LF
 *       domain
 *****************************************************************************/
void CMU_FreezeEnable(bool enable)
{
  if (enable) {
    /* Wait for any ongoing LF synchronization to complete. This is just to */
    /* protect against the rare case when a user                            */
    /* - modifies a register requiring LF sync                              */
    /* - then enables freeze before LF sync completed                       */
    /* - then modifies the same register again                              */
    /* since modifying a register while it is in sync progress should be    */
    /* avoided.                                                             */
    while (CMU->SYNCBUSY) {
    }

    CMU->FREEZE = CMU_FREEZE_REGFREEZE;
  } else {
    CMU->FREEZE = 0;
  }
}

#if defined(_CMU_HFRCOCTRL_BAND_MASK)
/***************************************************************************//**
 * @brief
 *   Get HFRCO band in use.
 *
 * @return
 *   HFRCO band in use.
 ******************************************************************************/
CMU_HFRCOBand_TypeDef CMU_HFRCOBandGet(void)
{
  return (CMU_HFRCOBand_TypeDef)((CMU->HFRCOCTRL & _CMU_HFRCOCTRL_BAND_MASK)
                                 >> _CMU_HFRCOCTRL_BAND_SHIFT);
}
#endif /* _CMU_HFRCOCTRL_BAND_MASK */

#if defined(_CMU_HFRCOCTRL_BAND_MASK)
/***************************************************************************//**
 * @brief
 *   Set HFRCO band and the tuning value based on the value in the calibration
 *   table made during production.
 *
 * @param[in] band
 *   HFRCO band to activate.
 ******************************************************************************/
void CMU_HFRCOBandSet(CMU_HFRCOBand_TypeDef band)
{
  uint32_t           tuning;
  uint32_t           freq;
  CMU_Select_TypeDef osc;

  /* Read tuning value from calibration table */
  switch (band) {
    case cmuHFRCOBand_1MHz:
      tuning = (DEVINFO->HFRCOCAL0 & _DEVINFO_HFRCOCAL0_BAND1_MASK)
               >> _DEVINFO_HFRCOCAL0_BAND1_SHIFT;
      break;

    case cmuHFRCOBand_7MHz:
      tuning = (DEVINFO->HFRCOCAL0 & _DEVINFO_HFRCOCAL0_BAND7_MASK)
               >> _DEVINFO_HFRCOCAL0_BAND7_SHIFT;
      break;

    case cmuHFRCOBand_11MHz:
      tuning = (DEVINFO->HFRCOCAL0 & _DEVINFO_HFRCOCAL0_BAND11_MASK)
               >> _DEVINFO_HFRCOCAL0_BAND11_SHIFT;
      break;

    case cmuHFRCOBand_14MHz:
      tuning = (DEVINFO->HFRCOCAL0 & _DEVINFO_HFRCOCAL0_BAND14_MASK)
               >> _DEVINFO_HFRCOCAL0_BAND14_SHIFT;
      break;

    case cmuHFRCOBand_21MHz:
      tuning = (DEVINFO->HFRCOCAL1 & _DEVINFO_HFRCOCAL1_BAND21_MASK)
               >> _DEVINFO_HFRCOCAL1_BAND21_SHIFT;
      break;

#if defined(_CMU_HFRCOCTRL_BAND_28MHZ)
    case cmuHFRCOBand_28MHz:
      tuning = (DEVINFO->HFRCOCAL1 & _DEVINFO_HFRCOCAL1_BAND28_MASK)
               >> _DEVINFO_HFRCOCAL1_BAND28_SHIFT;
      break;
#endif

    default:
      EFM_ASSERT(0);
      return;
  }

  /* If HFRCO is used for core clock, we have to consider flash access WS. */
  osc = CMU_ClockSelectGet(cmuClock_HF);
  if (osc == cmuSelect_HFRCO) {
    /* Configure worst case wait states for flash access before setting divider */
    flashWaitStateMax();
  }

  /* Set band/tuning */
  CMU->HFRCOCTRL = (CMU->HFRCOCTRL
                    & ~(_CMU_HFRCOCTRL_BAND_MASK | _CMU_HFRCOCTRL_TUNING_MASK))
                   | (band << _CMU_HFRCOCTRL_BAND_SHIFT)
                   | (tuning << _CMU_HFRCOCTRL_TUNING_SHIFT);

  /* If HFRCO is used for core clock, optimize flash WS */
  if (osc == cmuSelect_HFRCO) {
    /* Call SystemCoreClockGet() to update CMSIS core clock variable. */
    freq = SystemCoreClockGet();
    CMU_UpdateWaitStates(freq, VSCALE_DEFAULT);
  }

#if defined(CMU_MAX_FREQ_HFLE)
  /* Reduce HFLE frequency if possible. */
  setHfLeConfig(CMU_ClockFreqGet(cmuClock_HFLE));
#endif
}
#endif /* _CMU_HFRCOCTRL_BAND_MASK */

#if defined(_CMU_HFRCOCTRL_FREQRANGE_MASK)
/**************************************************************************//**
 * @brief
 *   Get the HFRCO frequency calibration word in DEVINFO
 *
 * @param[in] freq
 *   Frequency in Hz
 *
 * @return
 *   HFRCO calibration word for a given frequency
 *****************************************************************************/
static uint32_t CMU_HFRCODevinfoGet(CMU_HFRCOFreq_TypeDef freq)
{
  switch (freq) {
    /* 1, 2 and 4MHz share the same calibration word */
    case cmuHFRCOFreq_1M0Hz:
    case cmuHFRCOFreq_2M0Hz:
    case cmuHFRCOFreq_4M0Hz:
      return DEVINFO->HFRCOCAL0;

    case cmuHFRCOFreq_7M0Hz:
      return DEVINFO->HFRCOCAL3;

    case cmuHFRCOFreq_13M0Hz:
      return DEVINFO->HFRCOCAL6;

    case cmuHFRCOFreq_16M0Hz:
      return DEVINFO->HFRCOCAL7;

    case cmuHFRCOFreq_19M0Hz:
      return DEVINFO->HFRCOCAL8;

    case cmuHFRCOFreq_26M0Hz:
      return DEVINFO->HFRCOCAL10;

    case cmuHFRCOFreq_32M0Hz:
      return DEVINFO->HFRCOCAL11;

    case cmuHFRCOFreq_38M0Hz:
      return DEVINFO->HFRCOCAL12;

#if defined(_DEVINFO_HFRCOCAL16_MASK)
    case cmuHFRCOFreq_48M0Hz:
      return DEVINFO->HFRCOCAL13;

    case cmuHFRCOFreq_56M0Hz:
      return DEVINFO->HFRCOCAL14;

    case cmuHFRCOFreq_64M0Hz:
      return DEVINFO->HFRCOCAL15;

    case cmuHFRCOFreq_72M0Hz:
      return DEVINFO->HFRCOCAL16;
#endif

    default: /* cmuHFRCOFreq_UserDefined */
      return 0;
  }
}

/***************************************************************************//**
 * @brief
 *   Get current HFRCO frequency.
 *
 * @return
 *   HFRCO frequency
 ******************************************************************************/
CMU_HFRCOFreq_TypeDef CMU_HFRCOBandGet(void)
{
  return (CMU_HFRCOFreq_TypeDef)SystemHfrcoFreq;
}

/***************************************************************************//**
 * @brief
 *   Set HFRCO calibration for the selected target frequency.
 *
 * @param[in] setFreq
 *   HFRCO frequency to set
 ******************************************************************************/
void CMU_HFRCOBandSet(CMU_HFRCOFreq_TypeDef setFreq)
{
  uint32_t freqCal;
  uint32_t sysFreq;
  uint32_t prevFreq;

  /* Get DEVINFO index, set CMSIS frequency SystemHfrcoFreq */
  freqCal = CMU_HFRCODevinfoGet(setFreq);
  EFM_ASSERT((freqCal != 0) && (freqCal != UINT_MAX));
  prevFreq = SystemHfrcoFreq;
  SystemHfrcoFreq = (uint32_t)setFreq;

  /* Set max wait-states while changing core clock */
  if (CMU_ClockSelectGet(cmuClock_HF) == cmuSelect_HFRCO) {
    flashWaitStateMax();
  }

  /* Wait for any previous sync to complete, and then set calibration data
     for the selected frequency.  */
  while (BUS_RegBitRead(&CMU->SYNCBUSY, _CMU_SYNCBUSY_HFRCOBSY_SHIFT)) ;

  /* Check for valid calibration data */
  EFM_ASSERT(freqCal != UINT_MAX);

  /* Set divider in HFRCOCTRL for 1, 2 and 4MHz */
  switch (setFreq) {
    case cmuHFRCOFreq_1M0Hz:
      freqCal = (freqCal & ~_CMU_HFRCOCTRL_CLKDIV_MASK)
                | CMU_HFRCOCTRL_CLKDIV_DIV4;
      break;

    case cmuHFRCOFreq_2M0Hz:
      freqCal = (freqCal & ~_CMU_HFRCOCTRL_CLKDIV_MASK)
                | CMU_HFRCOCTRL_CLKDIV_DIV2;
      break;

    case cmuHFRCOFreq_4M0Hz:
      freqCal = (freqCal & ~_CMU_HFRCOCTRL_CLKDIV_MASK)
                | CMU_HFRCOCTRL_CLKDIV_DIV1;
      break;

    default:
      break;
  }

  /* Update HFLE configuration before updating HFRCO.
     Use the new set frequency. */
  if (CMU_ClockSelectGet(cmuClock_HF) == cmuSelect_HFRCO) {
    /* setFreq is worst-case as dividers may reduce the HFLE frequency. */
    setHfLeConfig(setFreq);
  }

  if (setFreq > prevFreq) {
#if defined(_EMU_CMD_EM01VSCALE0_MASK)
    /* When increasing frequency we need to voltage scale before the change */
    EMU_VScaleEM01ByClock(setFreq, true);
#endif
  }

  CMU->HFRCOCTRL = freqCal;

  /* If HFRCO is selected as HF clock, optimize flash access wait-state configuration
     for this frequency and update CMSIS core clock variable. */
  if (CMU_ClockSelectGet(cmuClock_HF) == cmuSelect_HFRCO) {
    /* Call SystemCoreClockGet() to update CMSIS core clock variable. */
    sysFreq = SystemCoreClockGet();
    EFM_ASSERT(sysFreq <= (uint32_t)setFreq);
    EFM_ASSERT(sysFreq <= SystemHfrcoFreq);
    EFM_ASSERT(setFreq == SystemHfrcoFreq);
    CMU_UpdateWaitStates(sysFreq, VSCALE_DEFAULT);
  }

  /* Reduce HFLE frequency if possible. */
  setHfLeConfig(CMU_ClockFreqGet(cmuClock_HFLE));

  if (setFreq <= prevFreq) {
#if defined(_EMU_CMD_EM01VSCALE0_MASK)
    /* When decreasing frequency we need to voltage scale after the change */
    EMU_VScaleEM01ByClock(0, true);
#endif
  }
}
#endif /* _CMU_HFRCOCTRL_FREQRANGE_MASK */

#if defined(_CMU_HFRCOCTRL_SUDELAY_MASK)
/***************************************************************************//**
 * @brief
 *   Get the HFRCO startup delay.
 *
 * @details
 *   Please refer to the reference manual for further details.
 *
 * @return
 *   The startup delay in use.
 ******************************************************************************/
uint32_t CMU_HFRCOStartupDelayGet(void)
{
  return (CMU->HFRCOCTRL & _CMU_HFRCOCTRL_SUDELAY_MASK)
         >> _CMU_HFRCOCTRL_SUDELAY_SHIFT;
}

/***************************************************************************//**
 * @brief
 *   Set the HFRCO startup delay.
 *
 * @details
 *   Please refer to the reference manual for further details.
 *
 * @param[in] delay
 *   The startup delay to set (<= 31).
 ******************************************************************************/
void CMU_HFRCOStartupDelaySet(uint32_t delay)
{
  EFM_ASSERT(delay <= 31);

  delay &= _CMU_HFRCOCTRL_SUDELAY_MASK >> _CMU_HFRCOCTRL_SUDELAY_SHIFT;
  CMU->HFRCOCTRL = (CMU->HFRCOCTRL & ~(_CMU_HFRCOCTRL_SUDELAY_MASK))
                   | (delay << _CMU_HFRCOCTRL_SUDELAY_SHIFT);
}
#endif

#if defined(_CMU_USHFRCOCTRL_FREQRANGE_MASK)
/**************************************************************************//**
 * @brief
 *   Get the USHFRCO frequency calibration word in DEVINFO
 *
 * @param[in] freq
 *   Frequency in Hz
 *
 * @return
 *   USHFRCO calibration word for a given frequency
 *****************************************************************************/
static uint32_t CMU_USHFRCODevinfoGet(CMU_USHFRCOFreq_TypeDef freq)
{
  switch (freq) {
    case cmuUSHFRCOFreq_16M0Hz:
      return DEVINFO->USHFRCOCAL7;

    case cmuUSHFRCOFreq_32M0Hz:
      return DEVINFO->USHFRCOCAL11;

    case cmuUSHFRCOFreq_48M0Hz:
      return DEVINFO->USHFRCOCAL13;

    case cmuUSHFRCOFreq_50M0Hz:
      return DEVINFO->USHFRCOCAL14;

    default: /* cmuUSHFRCOFreq_UserDefined */
      return 0;
  }
}

/***************************************************************************//**
 * @brief
 *   Get current USHFRCO frequency.
 *
 * @return
 *   HFRCO frequency
 ******************************************************************************/
CMU_USHFRCOFreq_TypeDef CMU_USHFRCOBandGet(void)
{
  return (CMU_USHFRCOFreq_TypeDef) ushfrcoFreq;
}

/***************************************************************************//**
 * @brief
 *   Set USHFRCO calibration for the selected target frequency.
 *
 * @param[in] setFreq
 *   USHFRCO frequency to set
 ******************************************************************************/
void CMU_USHFRCOBandSet(CMU_USHFRCOFreq_TypeDef setFreq)
{
  uint32_t freqCal;

  /* Get DEVINFO calibration values */
  freqCal = CMU_USHFRCODevinfoGet(setFreq);
  EFM_ASSERT((freqCal != 0) && (freqCal != UINT_MAX));
  ushfrcoFreq = (uint32_t)setFreq;

  /* Wait for any previous sync to complete, and then set calibration data
     for the selected frequency.  */
  while (BUS_RegBitRead(&CMU->SYNCBUSY, _CMU_SYNCBUSY_USHFRCOBSY_SHIFT)) ;

  CMU->USHFRCOCTRL = freqCal;
}
#endif /* _CMU_USHFRCOCTRL_FREQRANGE_MASK  */

#if defined(_CMU_HFXOCTRL_AUTOSTARTEM0EM1_MASK)
/***************************************************************************//**
 * @brief
 *   Enable or disable HFXO autostart
 *
 * @param[in] userSel
 *   Additional user specified enable bit.
 *
 * @param[in] enEM0EM1Start
 *   If true, HFXO is automatically started upon entering EM0/EM1 entry from
 *   EM2/EM3. HFXO selection has to be handled by the user.
 *   If false, HFXO is not started automatically when entering EM0/EM1.
 *
 * @param[in] enEM0EM1StartSel
 *   If true, HFXO is automatically started and immediately selected upon
 *   entering EM0/EM1 entry from EM2/EM3. Note that this option stalls the use of
 *   HFSRCCLK until HFXO becomes ready.
 *   If false, HFXO is not started or selected automatically when entering
 *   EM0/EM1.
 ******************************************************************************/
void CMU_HFXOAutostartEnable(uint32_t userSel,
                             bool enEM0EM1Start,
                             bool enEM0EM1StartSel)
{
  uint32_t hfxoFreq;
  uint32_t hfxoCtrl;

  /* Mask supported enable bits. */
#if defined(_CMU_HFXOCTRL_AUTOSTARTRDYSELRAC_MASK)
  userSel &= _CMU_HFXOCTRL_AUTOSTARTRDYSELRAC_MASK;
#else
  userSel = 0;
#endif

  hfxoCtrl = CMU->HFXOCTRL & ~(userSel
                               | _CMU_HFXOCTRL_AUTOSTARTEM0EM1_MASK
                               | _CMU_HFXOCTRL_AUTOSTARTSELEM0EM1_MASK);

  hfxoCtrl |= userSel
              | (enEM0EM1Start ? CMU_HFXOCTRL_AUTOSTARTEM0EM1 : 0)
              | (enEM0EM1StartSel ? CMU_HFXOCTRL_AUTOSTARTSELEM0EM1 : 0);

  /* Set wait-states for HFXO if automatic start and select is configured. */
  if (userSel || enEM0EM1StartSel) {
    hfxoFreq = SystemHFXOClockGet();
    CMU_UpdateWaitStates(hfxoFreq, VSCALE_DEFAULT);
    setHfLeConfig(hfxoFreq);
  }

  /* Update HFXOCTRL after wait-states are updated as HF may automatically switch
     to HFXO when automatic select is enabled . */
  CMU->HFXOCTRL = hfxoCtrl;
}
#endif

/**************************************************************************//**
 * @brief
 *   Set HFXO control registers
 *
 * @note
 *   HFXO configuration should be obtained from a configuration tool,
 *   app note or xtal datasheet. This function disables the HFXO to ensure
 *   a valid state before update.
 *
 * @param[in] hfxoInit
 *    HFXO setup parameters
 *****************************************************************************/
void CMU_HFXOInit(const CMU_HFXOInit_TypeDef *hfxoInit)
{
  /* Do not disable HFXO if it is currently selected as HF/Core clock */
  EFM_ASSERT(CMU_ClockSelectGet(cmuClock_HF) != cmuSelect_HFXO);

  /* HFXO must be disabled before reconfiguration */
  CMU_OscillatorEnable(cmuOsc_HFXO, false, true);

#if defined(_SILICON_LABS_32B_SERIES_1) && (_SILICON_LABS_GECKO_INTERNAL_SDID >= 100)
  uint32_t tmp = CMU_HFXOCTRL_MODE_XTAL;

  switch (hfxoInit->mode) {
    case cmuOscMode_Crystal:
      tmp = CMU_HFXOCTRL_MODE_XTAL;
      break;
    case cmuOscMode_External:
      tmp = CMU_HFXOCTRL_MODE_DIGEXTCLK;
      break;
    case cmuOscMode_AcCoupled:
      tmp = CMU_HFXOCTRL_MODE_ACBUFEXTCLK;
      break;
    default:
      EFM_ASSERT(false); /* Unsupported configuration */
  }

  /* HFXO Doubler can only be enabled on crystals up to max 25 MHz */
  if (SystemHFXOClockGet() <= 25000000) {
    tmp |= CMU_HFXOCTRL_HFXOX2EN;
  }

  CMU->HFXOCTRL = (CMU->HFXOCTRL & ~(_CMU_HFXOCTRL_MODE_MASK
                                     | _CMU_HFXOCTRL_HFXOX2EN_MASK))
                  | tmp;

  /* Set tuning for startup and steady state */
  CMU->HFXOSTARTUPCTRL = (hfxoInit->ctuneStartup << _CMU_HFXOSTARTUPCTRL_CTUNE_SHIFT)
                         | (hfxoInit->xoCoreBiasTrimStartup << _CMU_HFXOSTARTUPCTRL_IBTRIMXOCORE_SHIFT);

  CMU->HFXOSTEADYSTATECTRL = (CMU->HFXOSTEADYSTATECTRL & ~(_CMU_HFXOSTEADYSTATECTRL_CTUNE_MASK
                                                           | _CMU_HFXOSTEADYSTATECTRL_IBTRIMXOCORE_MASK))
                             | (hfxoInit->ctuneSteadyState << _CMU_HFXOSTEADYSTATECTRL_CTUNE_SHIFT)
                             | (hfxoInit->xoCoreBiasTrimSteadyState << _CMU_HFXOSTEADYSTATECTRL_IBTRIMXOCORE_SHIFT);

  /* Set timeouts */
  CMU->HFXOTIMEOUTCTRL = (hfxoInit->timeoutPeakDetect << _CMU_HFXOTIMEOUTCTRL_PEAKDETTIMEOUT_SHIFT)
                         | (hfxoInit->timeoutSteady << _CMU_HFXOTIMEOUTCTRL_STEADYTIMEOUT_SHIFT)
                         | (hfxoInit->timeoutStartup << _CMU_HFXOTIMEOUTCTRL_STARTUPTIMEOUT_SHIFT);

#elif defined(_CMU_HFXOCTRL_MASK)
  /* Verify that the deprecated autostart fields are not used,
   * @ref CMU_HFXOAutostartEnable must be used instead. */
  EFM_ASSERT(!(hfxoInit->autoStartEm01
               || hfxoInit->autoSelEm01
               || hfxoInit->autoStartSelOnRacWakeup));

  uint32_t tmp = CMU_HFXOCTRL_MODE_XTAL;

  /* AC coupled external clock not supported */
  EFM_ASSERT(hfxoInit->mode != cmuOscMode_AcCoupled);
  if (hfxoInit->mode == cmuOscMode_External) {
    tmp = CMU_HFXOCTRL_MODE_DIGEXTCLK;
  }

  /* Apply control settings */
  CMU->HFXOCTRL = (CMU->HFXOCTRL & ~_CMU_HFXOCTRL_MODE_MASK)
                  | tmp;
  BUS_RegBitWrite(&CMU->HFXOCTRL, _CMU_HFXOCTRL_LOWPOWER_SHIFT, hfxoInit->lowPowerMode);

  /* Set XTAL tuning parameters */

#if defined(_CMU_HFXOCTRL1_PEAKDETTHR_MASK)
  /* Set peak detection threshold */
  CMU->HFXOCTRL1 = (CMU->HFXOCTRL1 & ~_CMU_HFXOCTRL1_PEAKDETTHR_MASK)
                   | (hfxoInit->thresholdPeakDetect << _CMU_HFXOCTRL1_PEAKDETTHR_SHIFT);
#endif
  /* Set tuning for startup and steady state */
  CMU->HFXOSTARTUPCTRL = (hfxoInit->ctuneStartup << _CMU_HFXOSTARTUPCTRL_CTUNE_SHIFT)
                         | (hfxoInit->xoCoreBiasTrimStartup << _CMU_HFXOSTARTUPCTRL_IBTRIMXOCORE_SHIFT);

  CMU->HFXOSTEADYSTATECTRL = (CMU->HFXOSTEADYSTATECTRL & ~(_CMU_HFXOSTEADYSTATECTRL_CTUNE_MASK
                                                           | _CMU_HFXOSTEADYSTATECTRL_IBTRIMXOCORE_MASK
                                                           | _CMU_HFXOSTEADYSTATECTRL_REGISH_MASK
                                                           | _CMU_HFXOSTEADYSTATECTRL_REGISHUPPER_MASK))
                             | (hfxoInit->ctuneSteadyState << _CMU_HFXOSTEADYSTATECTRL_CTUNE_SHIFT)
                             | (hfxoInit->xoCoreBiasTrimSteadyState << _CMU_HFXOSTEADYSTATECTRL_IBTRIMXOCORE_SHIFT)
                             | (hfxoInit->regIshSteadyState << _CMU_HFXOSTEADYSTATECTRL_REGISH_SHIFT)
                             | getRegIshUpperVal(hfxoInit->regIshSteadyState);

  /* Set timeouts */
  CMU->HFXOTIMEOUTCTRL = (hfxoInit->timeoutPeakDetect << _CMU_HFXOTIMEOUTCTRL_PEAKDETTIMEOUT_SHIFT)
                         | (hfxoInit->timeoutSteady << _CMU_HFXOTIMEOUTCTRL_STEADYTIMEOUT_SHIFT)
                         | (hfxoInit->timeoutStartup << _CMU_HFXOTIMEOUTCTRL_STARTUPTIMEOUT_SHIFT)
                         | (hfxoInit->timeoutShuntOptimization << _CMU_HFXOTIMEOUTCTRL_SHUNTOPTTIMEOUT_SHIFT);

#else
  CMU->CTRL = (CMU->CTRL & ~(_CMU_CTRL_HFXOTIMEOUT_MASK
                             | _CMU_CTRL_HFXOBOOST_MASK
                             | _CMU_CTRL_HFXOMODE_MASK
                             | _CMU_CTRL_HFXOGLITCHDETEN_MASK))
              | (hfxoInit->timeout << _CMU_CTRL_HFXOTIMEOUT_SHIFT)
              | (hfxoInit->boost << _CMU_CTRL_HFXOBOOST_SHIFT)
              | (hfxoInit->mode << _CMU_CTRL_HFXOMODE_SHIFT)
              | (hfxoInit->glitchDetector ? CMU_CTRL_HFXOGLITCHDETEN : 0);
#endif
}

/***************************************************************************//**
 * @brief
 *   Get the LCD framerate divisor (FDIV) setting.
 *
 * @return
 *   The LCD framerate divisor.
 ******************************************************************************/
uint32_t CMU_LCDClkFDIVGet(void)
{
#if defined(LCD_PRESENT) && defined(_CMU_LCDCTRL_MASK)
  return (CMU->LCDCTRL & _CMU_LCDCTRL_FDIV_MASK) >> _CMU_LCDCTRL_FDIV_SHIFT;
#else
  return 0;
#endif /* defined(LCD_PRESENT) */
}

/***************************************************************************//**
 * @brief
 *   Set the LCD framerate divisor (FDIV) setting.
 *
 * @note
 *   The FDIV field (CMU LCDCTRL register) should only be modified while the
 *   LCD module is clock disabled (CMU LFACLKEN0.LCD bit is 0). This function
 *   will NOT modify FDIV if the LCD module clock is enabled. Please refer to
 *   CMU_ClockEnable() for disabling/enabling LCD clock.
 *
 * @param[in] div
 *   The FDIV setting to use.
 ******************************************************************************/
void CMU_LCDClkFDIVSet(uint32_t div)
{
#if defined(LCD_PRESENT) && defined(_CMU_LCDCTRL_MASK)
  EFM_ASSERT(div <= cmuClkDiv_128);

  /* Do not allow modification if LCD clock enabled */
  if (CMU->LFACLKEN0 & CMU_LFACLKEN0_LCD) {
    return;
  }

  div        <<= _CMU_LCDCTRL_FDIV_SHIFT;
  div         &= _CMU_LCDCTRL_FDIV_MASK;
  CMU->LCDCTRL = (CMU->LCDCTRL & ~_CMU_LCDCTRL_FDIV_MASK) | div;
#else
  (void)div;  /* Unused parameter */
#endif /* defined(LCD_PRESENT) */
}

/**************************************************************************//**
 * @brief
 *   Set LFXO control registers
 *
 * @note
 *   LFXO configuration should be obtained from a configuration tool,
 *   app note or xtal datasheet. This function disables the LFXO to ensure
 *   a valid state before update.
 *
 * @param[in] lfxoInit
 *    LFXO setup parameters
 *****************************************************************************/
void CMU_LFXOInit(const CMU_LFXOInit_TypeDef *lfxoInit)
{
  /* Do not disable LFXO if it is currently selected as HF/Core clock */
  EFM_ASSERT(CMU_ClockSelectGet(cmuClock_HF) != cmuSelect_LFXO);

  /* LFXO must be disabled before reconfiguration */
  CMU_OscillatorEnable(cmuOsc_LFXO, false, false);

#if defined(_CMU_LFXOCTRL_MASK)
  BUS_RegMaskedWrite(&CMU->LFXOCTRL,
                     _CMU_LFXOCTRL_TUNING_MASK
                     | _CMU_LFXOCTRL_GAIN_MASK
                     | _CMU_LFXOCTRL_TIMEOUT_MASK
                     | _CMU_LFXOCTRL_MODE_MASK,
                     (lfxoInit->ctune << _CMU_LFXOCTRL_TUNING_SHIFT)
                     | (lfxoInit->gain << _CMU_LFXOCTRL_GAIN_SHIFT)
                     | (lfxoInit->timeout << _CMU_LFXOCTRL_TIMEOUT_SHIFT)
                     | (lfxoInit->mode << _CMU_LFXOCTRL_MODE_SHIFT));
#else
  bool cmuBoost  = (lfxoInit->boost & 0x2);
  BUS_RegMaskedWrite(&CMU->CTRL,
                     _CMU_CTRL_LFXOTIMEOUT_MASK
                     | _CMU_CTRL_LFXOBOOST_MASK
                     | _CMU_CTRL_LFXOMODE_MASK,
                     (lfxoInit->timeout << _CMU_CTRL_LFXOTIMEOUT_SHIFT)
                     | ((cmuBoost ? 1 : 0) << _CMU_CTRL_LFXOBOOST_SHIFT)
                     | (lfxoInit->mode << _CMU_CTRL_LFXOMODE_SHIFT));
#endif

#if defined(_EMU_AUXCTRL_REDLFXOBOOST_MASK)
  bool emuReduce = (lfxoInit->boost & 0x1);
  BUS_RegBitWrite(&EMU->AUXCTRL, _EMU_AUXCTRL_REDLFXOBOOST_SHIFT, emuReduce ? 1 : 0);
#endif
}

/***************************************************************************//**
 * @brief
 *   Enable/disable oscillator.
 *
 * @note
 *   WARNING: When this function is called to disable either cmuOsc_LFXO or
 *   cmuOsc_HFXO the LFXOMODE or HFXOMODE fields of the CMU_CTRL register
 *   are reset to the reset value. I.e. if external clock sources are selected
 *   in either LFXOMODE or HFXOMODE fields, the configuration will be cleared
 *   and needs to be reconfigured if needed later.
 *
 * @param[in] osc
 *   The oscillator to enable/disable.
 *
 * @param[in] enable
 *   @li true - enable specified oscillator.
 *   @li false - disable specified oscillator.
 *
 * @param[in] wait
 *   Only used if @p enable is true.
 *   @li true - wait for oscillator start-up time to timeout before returning.
 *   @li false - do not wait for oscillator start-up time to timeout before
 *     returning.
 ******************************************************************************/
void CMU_OscillatorEnable(CMU_Osc_TypeDef osc, bool enable, bool wait)
{
  uint32_t rdyBitPos;
#if defined(_SILICON_LABS_32B_SERIES_1)
  uint32_t ensBitPos;
#endif
#if defined(_CMU_STATUS_HFXOPEAKDETRDY_MASK)
  uint32_t hfxoTrimStatus;
#endif

  uint32_t enBit;
  uint32_t disBit;

  switch (osc) {
    case cmuOsc_HFRCO:
      enBit  = CMU_OSCENCMD_HFRCOEN;
      disBit = CMU_OSCENCMD_HFRCODIS;
      rdyBitPos = _CMU_STATUS_HFRCORDY_SHIFT;
#if defined(_SILICON_LABS_32B_SERIES_1)
      ensBitPos = _CMU_STATUS_HFRCOENS_SHIFT;
#endif
      break;

    case cmuOsc_HFXO:
      enBit  = CMU_OSCENCMD_HFXOEN;
      disBit = CMU_OSCENCMD_HFXODIS;
      rdyBitPos = _CMU_STATUS_HFXORDY_SHIFT;
#if defined(_SILICON_LABS_32B_SERIES_1)
      ensBitPos = _CMU_STATUS_HFXOENS_SHIFT;
#endif
      break;

    case cmuOsc_AUXHFRCO:
      enBit  = CMU_OSCENCMD_AUXHFRCOEN;
      disBit = CMU_OSCENCMD_AUXHFRCODIS;
      rdyBitPos = _CMU_STATUS_AUXHFRCORDY_SHIFT;
#if defined(_SILICON_LABS_32B_SERIES_1)
      ensBitPos = _CMU_STATUS_AUXHFRCOENS_SHIFT;
#endif
      break;

    case cmuOsc_LFRCO:
      enBit  = CMU_OSCENCMD_LFRCOEN;
      disBit = CMU_OSCENCMD_LFRCODIS;
      rdyBitPos = _CMU_STATUS_LFRCORDY_SHIFT;
#if defined(_SILICON_LABS_32B_SERIES_1)
      ensBitPos = _CMU_STATUS_LFRCOENS_SHIFT;
#endif
      break;

    case cmuOsc_LFXO:
      enBit  = CMU_OSCENCMD_LFXOEN;
      disBit = CMU_OSCENCMD_LFXODIS;
      rdyBitPos = _CMU_STATUS_LFXORDY_SHIFT;
#if defined(_SILICON_LABS_32B_SERIES_1)
      ensBitPos = _CMU_STATUS_LFXOENS_SHIFT;
#endif
      break;

#if defined(_CMU_STATUS_USHFRCOENS_MASK)
    case cmuOsc_USHFRCO:
      enBit  = CMU_OSCENCMD_USHFRCOEN;
      disBit = CMU_OSCENCMD_USHFRCODIS;
      rdyBitPos = _CMU_STATUS_USHFRCORDY_SHIFT;
#if defined(_SILICON_LABS_32B_SERIES_1)
      ensBitPos = _CMU_STATUS_USHFRCOENS_SHIFT;
#endif
      break;
#endif

#if defined(_CMU_STATUS_PLFRCOENS_MASK)
    case cmuOsc_PLFRCO:
      enBit  = CMU_OSCENCMD_PLFRCOEN;
      disBit = CMU_OSCENCMD_PLFRCODIS;
      rdyBitPos = _CMU_STATUS_PLFRCORDY_SHIFT;
      ensBitPos = _CMU_STATUS_PLFRCOENS_SHIFT;
      break;
#endif

    default:
      /* Undefined clock source or cmuOsc_ULFRCO. ULFRCO is always enabled,
         and cannot be disabled. Ie. the definition of cmuOsc_ULFRCO is primarely
         intended for information: the ULFRCO is always on.  */
      EFM_ASSERT(0);
      return;
  }

  if (enable) {
 #if defined(_CMU_HFXOCTRL_MASK)
    bool firstHfxoEnable = false;

    /* Enabling the HFXO for the first time requires special handling. We use the
     * PEAKDETSHUTOPTMODE field of the HFXOCTRL register to see if this is the
     * first time the HFXO is enabled. */
    if ((osc == cmuOsc_HFXO) && (getHfxoTuningMode() == HFXO_TUNING_MODE_AUTO)) {
      /* REGPWRSEL must be set to DVDD before the HFXO can be enabled. */
#if defined(_EMU_PWRCTRL_REGPWRSEL_MASK)
      EFM_ASSERT(EMU->PWRCTRL & EMU_PWRCTRL_REGPWRSEL_DVDD);
#endif

      firstHfxoEnable = true;
      /* First time we enable an external clock we should switch to CMD mode to make sure that
       * we only do SCO and not PDA tuning. */
      if ((CMU->HFXOCTRL & (_CMU_HFXOCTRL_MODE_MASK)) == CMU_HFXOCTRL_MODE_DIGEXTCLK) {
        setHfxoTuningMode(HFXO_TUNING_MODE_CMD);
      }
    }
#endif
    CMU->OSCENCMD = enBit;

#if defined(_SILICON_LABS_32B_SERIES_1)
    /* Always wait for ENS to go high */
    while (!BUS_RegBitRead(&CMU->STATUS, ensBitPos)) {
    }
#endif

    /* Wait for clock to become ready after enable */
    if (wait) {
      while (!BUS_RegBitRead(&CMU->STATUS, rdyBitPos)) ;
#if defined(_SILICON_LABS_32B_SERIES_1)
      if ((osc == cmuOsc_HFXO) && firstHfxoEnable) {
        if ((CMU->HFXOCTRL & _CMU_HFXOCTRL_MODE_MASK) == CMU_HFXOCTRL_MODE_DIGEXTCLK) {
#if defined(CMU_CMD_HFXOSHUNTOPTSTART)
          /* External clock mode should only do shunt current optimization. */
          CMU_OscillatorTuningOptimize(cmuOsc_HFXO, cmuHFXOTuningMode_ShuntCommand, true);
#endif
        } else {
          /* Wait for peak detection and shunt current optimization to complete. */
          CMU_OscillatorTuningWait(cmuOsc_HFXO, cmuHFXOTuningMode_Auto);
        }

        /* Disable the HFXO again to apply the trims. Apply trim from HFXOTRIMSTATUS
           when disabled. */
        hfxoTrimStatus = CMU_OscillatorTuningGet(cmuOsc_HFXO);
        CMU_OscillatorEnable(cmuOsc_HFXO, false, true);
        CMU_OscillatorTuningSet(cmuOsc_HFXO, hfxoTrimStatus);

        /* Restart in CMD mode. */
        CMU->OSCENCMD = enBit;
        while (!BUS_RegBitRead(&CMU->STATUS, rdyBitPos)) ;
      }
#endif
    }
  } else {
    CMU->OSCENCMD = disBit;

#if defined(_SILICON_LABS_32B_SERIES_1)
    /* Always wait for ENS to go low */
    while (BUS_RegBitRead(&CMU->STATUS, ensBitPos)) {
    }
#endif
  }
}

/***************************************************************************//**
 * @brief
 *   Get oscillator frequency tuning setting.
 *
 * @param[in] osc
 *   Oscillator to get tuning value for, one of:
 *   @li #cmuOsc_LFRCO
 *   @li #cmuOsc_HFRCO @if _CMU_USHFRCOCTRL_TUNING_MASK
 *   @li #cmuOsc_USHFRCO
 *   @endif
 *   @li #cmuOsc_AUXHFRCO
 *   @li #cmuOsc_HFXO if CMU_HFXOCTRL_PEAKDETSHUNTOPTMODE is defined
 *
 * @return
 *   The oscillator frequency tuning setting in use.
 ******************************************************************************/
uint32_t CMU_OscillatorTuningGet(CMU_Osc_TypeDef osc)
{
  uint32_t ret;

  switch (osc) {
    case cmuOsc_LFRCO:
      ret = (CMU->LFRCOCTRL & _CMU_LFRCOCTRL_TUNING_MASK)
            >> _CMU_LFRCOCTRL_TUNING_SHIFT;
      break;

    case cmuOsc_HFRCO:
      ret = (CMU->HFRCOCTRL & _CMU_HFRCOCTRL_TUNING_MASK)
            >> _CMU_HFRCOCTRL_TUNING_SHIFT;
      break;

#if defined (_CMU_USHFRCOCTRL_TUNING_MASK)
    case cmuOsc_USHFRCO:
      ret = (CMU->USHFRCOCTRL & _CMU_USHFRCOCTRL_TUNING_MASK)
            >> _CMU_USHFRCOCTRL_TUNING_SHIFT;
      break;
#endif

    case cmuOsc_AUXHFRCO:
      ret = (CMU->AUXHFRCOCTRL & _CMU_AUXHFRCOCTRL_TUNING_MASK)
            >> _CMU_AUXHFRCOCTRL_TUNING_SHIFT;
      break;

#if defined(_CMU_HFXOCTRL_PEAKDETSHUNTOPTMODE_MASK)
    case cmuOsc_HFXO:
      ret = CMU->HFXOTRIMSTATUS & (_CMU_HFXOTRIMSTATUS_IBTRIMXOCORE_MASK
#if defined(_CMU_HFXOTRIMSTATUS_REGISH_MASK)
                                   | _CMU_HFXOTRIMSTATUS_REGISH_MASK
#endif
                                   );
      break;
#endif

    default:
      EFM_ASSERT(0);
      ret = 0;
      break;
  }

  return ret;
}

/***************************************************************************//**
 * @brief
 *   Set the oscillator frequency tuning control.
 *
 * @note
 *   Oscillator tuning is done during production, and the tuning value is
 *   automatically loaded after a reset. Changing the tuning value from the
 *   calibrated value is for more advanced use. Certain oscillators also have
 *   build-in tuning optimization.
 *
 * @param[in] osc
 *   Oscillator to set tuning value for, one of:
 *   @li #cmuOsc_LFRCO
 *   @li #cmuOsc_HFRCO @if _CMU_USHFRCOCTRL_TUNING_MASK
 *   @li #cmuOsc_USHFRCO
 *   @endif
 *   @li #cmuOsc_AUXHFRCO
 *   @li #cmuOsc_HFXO if PEAKDETSHUNTOPTMODE is available. Note that CMD mode is set.
 *
 * @param[in] val
 *   The oscillator frequency tuning setting to use.
 ******************************************************************************/
void CMU_OscillatorTuningSet(CMU_Osc_TypeDef osc, uint32_t val)
{
#if defined(_CMU_HFXOSTEADYSTATECTRL_REGISH_MASK)
  uint32_t regIshUpper;
#endif

  switch (osc) {
    case cmuOsc_LFRCO:
      EFM_ASSERT(val <= (_CMU_LFRCOCTRL_TUNING_MASK
                         >> _CMU_LFRCOCTRL_TUNING_SHIFT));
      val &= (_CMU_LFRCOCTRL_TUNING_MASK >> _CMU_LFRCOCTRL_TUNING_SHIFT);
#if defined(_SILICON_LABS_32B_SERIES_1)
      while (BUS_RegBitRead(&CMU->SYNCBUSY, _CMU_SYNCBUSY_LFRCOBSY_SHIFT)) ;
#endif
      CMU->LFRCOCTRL = (CMU->LFRCOCTRL & ~(_CMU_LFRCOCTRL_TUNING_MASK))
                       | (val << _CMU_LFRCOCTRL_TUNING_SHIFT);
      break;

    case cmuOsc_HFRCO:
      EFM_ASSERT(val <= (_CMU_HFRCOCTRL_TUNING_MASK
                         >> _CMU_HFRCOCTRL_TUNING_SHIFT));
      val &= (_CMU_HFRCOCTRL_TUNING_MASK >> _CMU_HFRCOCTRL_TUNING_SHIFT);
#if defined(_SILICON_LABS_32B_SERIES_1)
      while (BUS_RegBitRead(&CMU->SYNCBUSY, _CMU_SYNCBUSY_HFRCOBSY_SHIFT)) {
      }
#endif
      CMU->HFRCOCTRL = (CMU->HFRCOCTRL & ~(_CMU_HFRCOCTRL_TUNING_MASK))
                       | (val << _CMU_HFRCOCTRL_TUNING_SHIFT);
      break;

#if defined (_CMU_USHFRCOCTRL_TUNING_MASK)
    case cmuOsc_USHFRCO:
      EFM_ASSERT(val <= (_CMU_USHFRCOCTRL_TUNING_MASK
                         >> _CMU_USHFRCOCTRL_TUNING_SHIFT));
      val &= (_CMU_USHFRCOCTRL_TUNING_MASK >> _CMU_USHFRCOCTRL_TUNING_SHIFT);
#if defined(_SILICON_LABS_32B_SERIES_1)
      while (BUS_RegBitRead(&CMU->SYNCBUSY, _CMU_SYNCBUSY_USHFRCOBSY_SHIFT)) {
      }
#endif
      CMU->USHFRCOCTRL = (CMU->USHFRCOCTRL & ~(_CMU_USHFRCOCTRL_TUNING_MASK))
                         | (val << _CMU_USHFRCOCTRL_TUNING_SHIFT);
      break;
#endif

    case cmuOsc_AUXHFRCO:
      EFM_ASSERT(val <= (_CMU_AUXHFRCOCTRL_TUNING_MASK
                         >> _CMU_AUXHFRCOCTRL_TUNING_SHIFT));
      val &= (_CMU_AUXHFRCOCTRL_TUNING_MASK >> _CMU_AUXHFRCOCTRL_TUNING_SHIFT);
#if defined(_SILICON_LABS_32B_SERIES_1)
      while (BUS_RegBitRead(&CMU->SYNCBUSY, _CMU_SYNCBUSY_AUXHFRCOBSY_SHIFT)) {
      }
#endif
      CMU->AUXHFRCOCTRL = (CMU->AUXHFRCOCTRL & ~(_CMU_AUXHFRCOCTRL_TUNING_MASK))
                          | (val << _CMU_AUXHFRCOCTRL_TUNING_SHIFT);
      break;

#if defined(_CMU_HFXOCTRL_PEAKDETSHUNTOPTMODE_MASK)
    case cmuOsc_HFXO:

      /* Do set PEAKDETSHUNTOPTMODE or HFXOSTEADYSTATECTRL if HFXO is enabled */
      EFM_ASSERT(!(CMU->STATUS & CMU_STATUS_HFXOENS));

      /* Switch to command mode. Automatic SCO and PDA calibration is not done
         at the next enable. Set user REGISH, REGISHUPPER and IBTRIMXOCORE. */
      CMU->HFXOCTRL = (CMU->HFXOCTRL & ~_CMU_HFXOCTRL_PEAKDETSHUNTOPTMODE_MASK)
                      | CMU_HFXOCTRL_PEAKDETSHUNTOPTMODE_CMD;

#if defined(_CMU_HFXOSTEADYSTATECTRL_REGISH_MASK)
      regIshUpper = getRegIshUpperVal((val & _CMU_HFXOSTEADYSTATECTRL_REGISH_MASK)
                                      >> _CMU_HFXOSTEADYSTATECTRL_REGISH_SHIFT);
      CMU->HFXOSTEADYSTATECTRL = (CMU->HFXOSTEADYSTATECTRL
                                  & ~(_CMU_HFXOSTEADYSTATECTRL_IBTRIMXOCORE_MASK
                                      | _CMU_HFXOSTEADYSTATECTRL_REGISH_MASK
                                      | _CMU_HFXOSTEADYSTATECTRL_REGISHUPPER_MASK))
                                 | val
                                 | regIshUpper;
#else
      CMU->HFXOSTEADYSTATECTRL = (CMU->HFXOSTEADYSTATECTRL
                                  & ~_CMU_HFXOSTEADYSTATECTRL_IBTRIMXOCORE_MASK)
                                 | val;
#endif

      break;
#endif

    default:
      EFM_ASSERT(0);
      break;
  }
}

#if defined(_CMU_HFXOCTRL_PEAKDETSHUNTOPTMODE_MASK) || defined(_CMU_HFXOCTRL_PEAKDETMODE_MASK)
/***************************************************************************//**
 * @brief
 *   Wait for oscillator tuning optimization.
 *
 * @param[in] osc
 *   Oscillator to set tuning value for, one of:
 *   @li #cmuOsc_HFXO
 *
 * @param[in] mode
 *   Tuning optimization mode.
 *
 * @return
 *   Returns false on invalid parameters or oscillator error status.
 ******************************************************************************/
bool CMU_OscillatorTuningWait(CMU_Osc_TypeDef osc,
                              CMU_HFXOTuningMode_TypeDef mode)
{
  uint32_t waitFlags;
  EFM_ASSERT(osc == cmuOsc_HFXO);

  /* Currently implemented for HFXO with PEAKDETSHUNTOPTMODE only */
  (void)osc;

  if (getHfxoTuningMode() == HFXO_TUNING_MODE_AUTO) {
    waitFlags = HFXO_TUNING_READY_FLAGS;
  } else {
    /* Set wait flags for each command and wait */
    switch (mode) {
#if defined(_CMU_STATUS_HFXOSHUNTOPTRDY_MASK)
      case cmuHFXOTuningMode_ShuntCommand:
        waitFlags = CMU_STATUS_HFXOSHUNTOPTRDY;
        break;
#endif
      case cmuHFXOTuningMode_Auto:
        waitFlags = HFXO_TUNING_READY_FLAGS;
        break;

#if defined(CMU_CMD_HFXOSHUNTOPTSTART)
      case cmuHFXOTuningMode_PeakShuntCommand:
        waitFlags = HFXO_TUNING_READY_FLAGS;
        break;
#endif

      default:
        waitFlags = _CMU_STATUS_MASK;
        EFM_ASSERT(false);
    }
  }
  while ((CMU->STATUS & waitFlags) != waitFlags) ;

#if defined(CMU_IF_HFXOPEAKDETERR)
  /* Check error flags */
  if (waitFlags & CMU_STATUS_HFXOPEAKDETRDY) {
    return (CMU->IF & CMU_IF_HFXOPEAKDETERR ? true : false);
  }
#endif
  return true;
}

/***************************************************************************//**
 * @brief
 *   Start and optionally wait for oscillator tuning optimization.
 *
 * @param[in] osc
 *   Oscillator to set tuning value for, one of:
 *   @li #cmuOsc_HFXO
 *
 * @param[in] mode
 *   Tuning optimization mode.
 *
 * @param[in] wait
 *   Wait for tuning optimization to complete.
 *   true - wait for tuning optimization to complete.
 *   false - return without waiting.
 *
 * @return
 *   Returns false on invalid parameters or oscillator error status.
 ******************************************************************************/
bool CMU_OscillatorTuningOptimize(CMU_Osc_TypeDef osc,
                                  CMU_HFXOTuningMode_TypeDef mode,
                                  bool wait)
{
  switch (osc) {
    case cmuOsc_HFXO:
      if (mode) {
#if defined(CMU_IF_HFXOPEAKDETERR)
        /* Clear error flag before command write */
        CMU->IFC = CMU_IFC_HFXOPEAKDETERR;
#endif
        CMU->CMD = mode;
      }
      if (wait) {
        return CMU_OscillatorTuningWait(osc, mode);
      }
      break;

    default:
      EFM_ASSERT(false);
  }
  return true;
}
#endif

/**************************************************************************//**
 * @brief
 *   Determine if currently selected PCNTn clock used is external or LFBCLK.
 *
 * @param[in] instance
 *   PCNT instance number to get currently selected clock source for.
 *
 * @return
 *   @li true - selected clock is external clock.
 *   @li false - selected clock is LFBCLK.
 *****************************************************************************/
bool CMU_PCNTClockExternalGet(unsigned int instance)
{
  uint32_t setting;

  switch (instance) {
#if defined(_CMU_PCNTCTRL_PCNT0CLKEN_MASK)
    case 0:
      setting = CMU->PCNTCTRL & CMU_PCNTCTRL_PCNT0CLKSEL_PCNT0S0;
      break;

#if defined(_CMU_PCNTCTRL_PCNT1CLKEN_MASK)
    case 1:
      setting = CMU->PCNTCTRL & CMU_PCNTCTRL_PCNT1CLKSEL_PCNT1S0;
      break;

#if defined(_CMU_PCNTCTRL_PCNT2CLKEN_MASK)
    case 2:
      setting = CMU->PCNTCTRL & CMU_PCNTCTRL_PCNT2CLKSEL_PCNT2S0;
      break;
#endif
#endif
#endif

    default:
      setting = 0;
      break;
  }
  return (setting ? true : false);
}

/**************************************************************************//**
 * @brief
 *   Select PCNTn clock.
 *
 * @param[in] instance
 *   PCNT instance number to set selected clock source for.
 *
 * @param[in] external
 *   Set to true to select external clock, false to select LFBCLK.
 *****************************************************************************/
void CMU_PCNTClockExternalSet(unsigned int instance, bool external)
{
#if defined(PCNT_PRESENT)
  uint32_t setting = 0;

  EFM_ASSERT(instance < PCNT_COUNT);

  if (external) {
    setting = 1;
  }

  BUS_RegBitWrite(&(CMU->PCNTCTRL), (instance * 2) + 1, setting);

#else
  (void)instance;  /* Unused parameter */
  (void)external;  /* Unused parameter */
#endif
}

#if defined(_CMU_USHFRCOCONF_BAND_MASK)
/***************************************************************************//**
 * @brief
 *   Get USHFRCO band in use.
 *
 * @return
 *   USHFRCO band in use.
 ******************************************************************************/
CMU_USHFRCOBand_TypeDef CMU_USHFRCOBandGet(void)
{
  return (CMU_USHFRCOBand_TypeDef)((CMU->USHFRCOCONF
                                    & _CMU_USHFRCOCONF_BAND_MASK)
                                   >> _CMU_USHFRCOCONF_BAND_SHIFT);
}
#endif

#if defined(_CMU_USHFRCOCONF_BAND_MASK)
/***************************************************************************//**
 * @brief
 *   Set USHFRCO band to use.
 *
 * @param[in] band
 *   USHFRCO band to activate.
 ******************************************************************************/
void CMU_USHFRCOBandSet(CMU_USHFRCOBand_TypeDef band)
{
  uint32_t           tuning;
  uint32_t           fineTuning;
  CMU_Select_TypeDef osc;

  /* Cannot switch band if USHFRCO is already selected as HF clock. */
  osc = CMU_ClockSelectGet(cmuClock_HF);
  EFM_ASSERT((CMU_USHFRCOBandGet() != band) && (osc != cmuSelect_USHFRCO));

  /* Read tuning value from calibration table */
  switch (band) {
    case cmuUSHFRCOBand_24MHz:
      tuning = (DEVINFO->USHFRCOCAL0 & _DEVINFO_USHFRCOCAL0_BAND24_TUNING_MASK)
               >> _DEVINFO_USHFRCOCAL0_BAND24_TUNING_SHIFT;
      fineTuning = (DEVINFO->USHFRCOCAL0
                    & _DEVINFO_USHFRCOCAL0_BAND24_FINETUNING_MASK)
                   >> _DEVINFO_USHFRCOCAL0_BAND24_FINETUNING_SHIFT;
      ushfrcoFreq = 24000000UL;
      break;

    case cmuUSHFRCOBand_48MHz:
      tuning = (DEVINFO->USHFRCOCAL0 & _DEVINFO_USHFRCOCAL0_BAND48_TUNING_MASK)
               >> _DEVINFO_USHFRCOCAL0_BAND48_TUNING_SHIFT;
      fineTuning = (DEVINFO->USHFRCOCAL0
                    & _DEVINFO_USHFRCOCAL0_BAND48_FINETUNING_MASK)
                   >> _DEVINFO_USHFRCOCAL0_BAND48_FINETUNING_SHIFT;
      /* Enable the clock divider before switching the band from 24 to 48MHz */
      BUS_RegBitWrite(&CMU->USHFRCOCONF, _CMU_USHFRCOCONF_USHFRCODIV2DIS_SHIFT, 0);
      ushfrcoFreq = 48000000UL;
      break;

    default:
      EFM_ASSERT(0);
      return;
  }

  /* Set band and tuning */
  CMU->USHFRCOCONF = (CMU->USHFRCOCONF & ~_CMU_USHFRCOCONF_BAND_MASK)
                     | (band << _CMU_USHFRCOCONF_BAND_SHIFT);
  CMU->USHFRCOCTRL = (CMU->USHFRCOCTRL & ~_CMU_USHFRCOCTRL_TUNING_MASK)
                     | (tuning << _CMU_USHFRCOCTRL_TUNING_SHIFT);
  CMU->USHFRCOTUNE = (CMU->USHFRCOTUNE & ~_CMU_USHFRCOTUNE_FINETUNING_MASK)
                     | (fineTuning << _CMU_USHFRCOTUNE_FINETUNING_SHIFT);

  /* Disable the clock divider after switching the band from 48 to 24MHz */
  if (band == cmuUSHFRCOBand_24MHz) {
    BUS_RegBitWrite(&CMU->USHFRCOCONF, _CMU_USHFRCOCONF_USHFRCODIV2DIS_SHIFT, 1);
  }
}
#endif

/** @} (end addtogroup CMU) */
/** @} (end addtogroup emlib) */
#endif /* __EM_CMU_H */
