text: /***************************************************************************//**
   * @file
   * @brief Clock management unit (CMU) Peripheral API
   *******************************************************************************
   * # License
   * <b>Copyright 2018 Silicon Laboratories Inc. www.silabs.com</b>
   *******************************************************************************
   *
   * SPDX-License-Identifier: Zlib
   *
   * The licensor of this software is Silicon Laboratories Inc.
   *
   * This software is provided 'as-is', without any express or implied
   * warranty. In no event will the authors be held liable for any damages
   * arising from the use of this software.
   *
   * Permission is granted to anyone to use this software for any purpose,
   * including commercial applications, and to alter it and redistribute it
   * freely, subject to the following restrictions:
   *
   * 1. The origin of this software must not be misrepresented; you must not
   *    claim that you wrote the original software. If you use this software
   *    in a product, an acknowledgment in the product documentation would be
   *    appreciated but is not required.
   * 2. Altered source versions must be plainly marked as such, and must not be
   *    misrepresented as being the original software.
   * 3. This notice may not be removed or altered from any source distribution.
   *
   ******************************************************************************/
  
  #include "em_cmu.h"
  #if defined(CMU_PRESENT)
  
  #include <stddef.h>
  #include <limits.h>
  #include "em_assert.h"
  #include "em_bus.h"
  #include "em_cmu.h"
  #include "em_common.h"
  #include "em_emu.h"
  #include "em_gpio.h"
  #include "em_system.h"
  
  /***************************************************************************//**
   * @addtogroup emlib
   * @{
   ******************************************************************************/
  
  /***************************************************************************//**
   * @addtogroup CMU
   * @brief Clock management unit (CMU) Peripheral API
   * @details
   *  This module contains functions for the CMU peripheral of Silicon Labs 32-bit
   *  MCUs and SoCs. The CMU module controls oscillators, clocks gates, clock
   *  multiplexers, pre-scalers, calibration modules and wait-states.
   * @{
   ******************************************************************************/
  
  #if defined(_SILICON_LABS_32B_SERIES_2)
  
  /** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
  
  /*******************************************************************************
   ******************************   DEFINES   ************************************
   ******************************************************************************/
  
  #if defined(_SILICON_LABS_32B_SERIES_2_CONFIG_1)
  // Maximum allowed core frequency vs. wait-states on flash accesses.
  #define CMU_MAX_FLASHREAD_FREQ_0WS        39000000UL
  #define CMU_MAX_FLASHREAD_FREQ_1WS        80000000UL
  
  // Maximum allowed core frequency vs. wait-states on sram accesses.
  #define CMU_MAX_SRAM_FREQ_0WS             50000000UL
  #define CMU_MAX_SRAM_FREQ_1WS             80000000UL
  
  #elif defined(_SILICON_LABS_32B_SERIES_2_CONFIG_2)
  // Maximum allowed core frequency vs. wait-states on flash accesses.
  //#define CMU_MAX_FLASHREAD_FREQ_0WS        40000000UL
  //#define CMU_MAX_FLASHREAD_FREQ_1WS        80000000UL
  
  // Maximum allowed core frequency vs. wait-states and vscale on flash accesses.
  #define CMU_MAX_FREQ_0WS_1V1              40000000UL
  #define CMU_MAX_FREQ_1WS_1V1              80000000UL
  #define CMU_MAX_FREQ_0WS_1V0              80000000UL
  #define CMU_MAX_FREQ_1WS_1V0              80000000UL
  #endif
  
  // Maximum allowed PCLK frequency.
  #define CMU_MAX_PCLK_FREQ                 50000000UL
  
  // Maximum allowed RHCLK frequency.
  #define CMU_MAX_RHCLK_FREQ                40000000UL
  
  #if defined(EMU_VSCALE_PRESENT)
  // This macro is intended to be used as input to CMU_UpdateWaitStates()
  #define VSCALE_DEFAULT    (2 - (int)EMU_VScaleGet())
  #else
  #define VSCALE_DEFAULT    VSCALE_EM01_HIGH_PERFORMANCE
  #endif
  
  /*******************************************************************************
   **************************   LOCAL VARIABLES   ********************************
   ******************************************************************************/
  
  // Table of HFRCOCAL values and their associated min/max frequencies and
  // optional band enumerator.
  static const struct hfrcoCalTableElement{
    uint32_t  minFreq;
    uint32_t  maxFreq;
    uint32_t  value;
    CMU_HFRCODPLLFreq_TypeDef band;
  } hfrcoCalTable[] =
  {
    //  minFreq   maxFreq    HFRCOCAL value  band
    {  900000UL, 1080000UL, 0x82401F00UL, cmuHFRCODPLLFreq_1M0Hz         },
    { 1080000UL, 1300000UL, 0xA2411F3AUL, (CMU_HFRCODPLLFreq_TypeDef)0   },
    { 1300000UL, 1530000UL, 0xA2421F3AUL, (CMU_HFRCODPLLFreq_TypeDef)0   },
    { 1530000UL, 1800000UL, 0xB6439F3AUL, (CMU_HFRCODPLLFreq_TypeDef)0   },
    { 1800000UL, 2150000UL, 0x81401F00UL, cmuHFRCODPLLFreq_2M0Hz         },
    { 2150000UL, 2600000UL, 0xA1411F3AUL, (CMU_HFRCODPLLFreq_TypeDef)0   },
    { 2600000UL, 3050000UL, 0xA1421F3AUL, (CMU_HFRCODPLLFreq_TypeDef)0   },
    { 3050000UL, 3600000UL, 0xB5439F3AUL, (CMU_HFRCODPLLFreq_TypeDef)0   },
    { 3600000UL, 4300000UL, 0x80401F00UL, cmuHFRCODPLLFreq_4M0Hz         },
    { 4300000UL, 5200000UL, 0xA0411F3AUL, (CMU_HFRCODPLLFreq_TypeDef)0   },
    { 5200000UL, 6100000UL, 0xA0421F3AUL, (CMU_HFRCODPLLFreq_TypeDef)0   },
    { 6100000UL, 7800000UL, 0xB4439F00UL, cmuHFRCODPLLFreq_7M0Hz         },
    { 7800000UL, 9800000UL, 0xB4449F3AUL, (CMU_HFRCODPLLFreq_TypeDef)0   },
    { 9800000UL, 11800000UL, 0xB4459F3AUL, (CMU_HFRCODPLLFreq_TypeDef)0  },
    { 11800000UL, 14400000UL, 0xB4669F00UL, cmuHFRCODPLLFreq_13M0Hz      },
    { 14400000UL, 17200000UL, 0xB4679F00UL, cmuHFRCODPLLFreq_16M0Hz      },
    { 17200000UL, 19700000UL, 0xA8689F00UL, cmuHFRCODPLLFreq_19M0Hz      },
    { 19700000UL, 23800000UL, 0xB8899F3AUL, (CMU_HFRCODPLLFreq_TypeDef)0 },
    { 23800000UL, 28700000UL, 0xB88A9F00UL, cmuHFRCODPLLFreq_26M0Hz      },
    { 28700000UL, 34800000UL, 0xB8AB9F00UL, cmuHFRCODPLLFreq_32M0Hz      },
    { 34800000UL, 42800000UL, 0xA8CC9F00UL, cmuHFRCODPLLFreq_38M0Hz      },
    { 42800000UL, 51600000UL, 0xACED9F00UL, cmuHFRCODPLLFreq_48M0Hz      },
    { 51600000UL, 60500000UL, 0xBCEE9F00UL, cmuHFRCODPLLFreq_56M0Hz      },
    { 60500000UL, 72600000UL, 0xBCEF9F00UL, cmuHFRCODPLLFreq_64M0Hz      },
    { 72600000UL, 80000000UL, 0xCCF09F00UL, cmuHFRCODPLLFreq_80M0Hz      }
  };
  
  #define HFRCOCALTABLE_ENTRIES (sizeof(hfrcoCalTable) \
                                 / sizeof(struct hfrcoCalTableElement))
  
  /*******************************************************************************
   **************************   LOCAL PROTOTYPES   *******************************
   ******************************************************************************/
  
  #if defined(_SILICON_LABS_32B_SERIES_2_CONFIG_2)
  static void     em01GrpbClkGet(uint32_t *freq, CMU_Select_TypeDef *sel);
  static void     euart0ClkGet(uint32_t *freq, CMU_Select_TypeDef *sel);
  static void     rhclkPrescMax(void);
  static void     rhclkPrescOptimize(void);
  #endif
  
  #if defined(_SILICON_LABS_32B_SERIES_2_CONFIG_1)
  static uint32_t HFRCOEM23DevinfoGet(CMU_HFRCOEM23Freq_TypeDef freq);
  static void     traceClkGet(uint32_t *freq, CMU_Select_TypeDef *sel);
  static void     wdog1ClkGet(uint32_t *freq, CMU_Select_TypeDef *sel);
  #endif
  
  static void     dpllRefClkGet(uint32_t *freq, CMU_Select_TypeDef *sel);
  static void     em01GrpaClkGet(uint32_t *freq, CMU_Select_TypeDef *sel);
  static void     em23GrpaClkGet(uint32_t *freq, CMU_Select_TypeDef *sel);
  static void     em4GrpaClkGet(uint32_t *freq, CMU_Select_TypeDef *sel);
  static void     flashWaitStateControl(uint32_t coreFreq, int vscale);
  static uint32_t HFRCODPLLDevinfoGet(CMU_HFRCODPLLFreq_TypeDef freq);
  static void     iadcClkGet(uint32_t *freq, CMU_Select_TypeDef *sel);
  static void     pclkDivMax(void);
  static void     pclkDivOptimize(void);
  static void     rtccClkGet(uint32_t *freq, CMU_Select_TypeDef *sel);
  static void     waitStateMax(void);
  static void     wdog0ClkGet(uint32_t *freq, CMU_Select_TypeDef *sel);
  
  /** @endcond */
  
  #if defined(_SILICON_LABS_32B_SERIES_2_CONFIG_2)
  /*******************************************************************************
   **************************   GLOBAL FUNCTIONS   *******************************
   ******************************************************************************/
  
  /***************************************************************************//**
   * @brief
   *   Enable/disable a clock.
   *
   * @details
   *   Module clocks sre disabled after reset. If a module clock is disabled, the
   *   registers of that module are not accessible and accessing such registers
   *   will hardfault the Cortex core.
   *
   * @param[in] clock
   *   The clock to enable/disable.
   *
   * @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 = NULL;
    uint32_t          bit;
  
    /* Identify the enable register. */
    if (((unsigned)clock >> CMU_EN_REG_POS) == CMU_NO_EN_REG) {
      EFM_ASSERT(false);                      /* No enable for this clock. */
    } else if (((unsigned)clock >> CMU_EN_REG_POS) == CMU_CLKEN0_EN_REG) {
      reg = &CMU->CLKEN0;
    } else if (((unsigned)clock >> CMU_EN_REG_POS) == CMU_CLKEN1_EN_REG) {
      reg = &CMU->CLKEN1;
    } else {
      reg = &CMU->CRYPTOACCCLKCTRL;
    }
  
    /* Get the bit position used to enable/disable. */
    bit = ((unsigned)clock >> CMU_EN_BIT_POS) & CMU_EN_BIT_MASK;
  
    /* Set/clear bit as requested. */
    BUS_RegBitWrite(reg, bit, (uint32_t)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 = 0U;
  
    switch (clock) {
      case cmuClock_SYSCLK:
        ret = SystemSYSCLKGet();
        break;
  
      case cmuClock_HCLK:
      case cmuClock_CORE:
      case cmuClock_ICACHE:
      case cmuClock_MSC:
      case cmuClock_LDMA:
      case cmuClock_SMU:
      case cmuClock_CRYPTOACC:
      case cmuClock_RADIOAES:
      case cmuClock_CRYPTOAES:
      case cmuClock_CRYPTOPK:
        ret = SystemHCLKGet();
        break;
  
      case cmuClock_EXPCLK:
        ret = SystemSYSCLKGet() / CMU_ClockDivGet(cmuClock_EXPCLK);
        break;
  
      case cmuClock_PCLK:
      case cmuClock_USART0:
      case cmuClock_USART1:
      case cmuClock_I2C1:
      case cmuClock_PRS:
      case cmuClock_GPIO:
      case cmuClock_GPCRC:
      case cmuClock_LDMAXBAR:
      case cmuClock_SYSCFG:
      case cmuClock_DCDC:
      case cmuClock_BURAM:
      case cmuClock_DPLL0:
        ret = SystemHCLKGet() / CMU_ClockDivGet(cmuClock_PCLK);
        break;
  
      case cmuClock_LSPCLK:
      case cmuClock_I2C0:
      case cmuClock_AMUXCP0:
        ret = SystemHCLKGet() / CMU_ClockDivGet(cmuClock_PCLK) / 2U;
        break;
  
      case cmuClock_TRACECLK:
        ret = SystemSYSCLKGet() / CMU_ClockDivGet(cmuClock_TRACECLK);
        break;
  
      case cmuClock_TIMER0:
      case cmuClock_TIMER1:
      case cmuClock_TIMER2:
      case cmuClock_TIMER3:
      case cmuClock_TIMER4:
      case cmuClock_EM01GRPACLK:
        em01GrpaClkGet(&ret, NULL);
        break;
  
      case cmuClock_PDM:
      case cmuClock_EM01GRPBCLK:
        em01GrpbClkGet(&ret, NULL);
        break;
  
      case cmuClock_EUART0:
      case cmuClock_EUART0CLK:
        euart0ClkGet(&ret, NULL);
        break;
  
      case cmuClock_IADC0:
      case cmuClock_IADCCLK:
        iadcClkGet(&ret, NULL);
        break;
  
      case cmuClock_SYSTICK:
      case cmuClock_LETIMER0:
      case cmuClock_EM23GRPACLK:
        em23GrpaClkGet(&ret, NULL);
        break;
  
      case cmuClock_WDOG0:
      case cmuClock_WDOG0CLK:
        wdog0ClkGet(&ret, NULL);
        break;
  
      case cmuClock_RTCC:
      case cmuClock_RTCCCLK:
        rtccClkGet(&ret, NULL);
        break;
  
      case cmuClock_BURTC:
      case cmuClock_EM4GRPACLK:
        em4GrpaClkGet(&ret, NULL);
        break;
  
      default:
        EFM_ASSERT(false);
        break;
    }
    return ret;
  }
  
  /*******************************************************************************
   **************************   LOCAL FUNCTIONS   ********************************
   ******************************************************************************/
  
  /***************************************************************************//**
   * @brief
   *   Get selected oscillator and frequency for @ref cmuClock_EM01GRPBCLK
   *   clock tree.
   *
   * @param[out] freq
   *   The frequency.
   *
   * @param[out] sel
   *   The selected oscillator.
   ******************************************************************************/
  static void em01GrpbClkGet(uint32_t *freq, CMU_Select_TypeDef *sel)
  {
    uint32_t f = 0U;
    CMU_Select_TypeDef s;
  
    switch (CMU->EM01GRPBCLKCTRL & _CMU_EM01GRPBCLKCTRL_CLKSEL_MASK) {
      case _CMU_EM01GRPBCLKCTRL_CLKSEL_HFRCODPLL:
        f = SystemHFRCODPLLClockGet();
        s = cmuSelect_HFRCODPLL;
        break;
  
      case _CMU_EM01GRPBCLKCTRL_CLKSEL_HFRCODPLLRT:
        f = SystemHFRCODPLLClockGet();
        s = cmuSelect_HFRCODPLLRT;
        break;
  
      case _CMU_EM01GRPBCLKCTRL_CLKSEL_HFXO:
        f = SystemHFXOClockGet();
        s = cmuSelect_HFXO;
        break;
  
      case _CMU_EM01GRPBCLKCTRL_CLKSEL_HFXORT:
        f = SystemHFXOClockGet();
        s = cmuSelect_HFXORT;
        break;
  
      case _CMU_EM01GRPBCLKCTRL_CLKSEL_FSRCO:
        f = SystemFSRCOClockGet();
        s = cmuSelect_FSRCO;
        break;
  
      case _CMU_EM01GRPBCLKCTRL_CLKSEL_CLKIN0:
        f = SystemCLKIN0Get();
        s = cmuSelect_CLKIN0;
        break;
  
      default:
        s = cmuSelect_Error;
        EFM_ASSERT(false);
        break;
    }
  
    if (freq != NULL) {
      *freq = f;
    }
    if (sel != NULL) {
      *sel = s;
    }
  }
  
  /***************************************************************************//**
   * @brief
   *   Get selected oscillator and frequency for @ref cmuClock_EUART0CLK
   *   clock tree.
   *
   * @param[out] freq
   *   The frequency.
   *
   * @param[out] sel
   *   The selected oscillator.
   ******************************************************************************/
  static void euart0ClkGet(uint32_t *freq, CMU_Select_TypeDef *sel)
  {
    switch (CMU->EUART0CLKCTRL & _CMU_EUART0CLKCTRL_CLKSEL_MASK) {
      case _CMU_EUART0CLKCTRL_CLKSEL_EM01GRPACLK:
        em01GrpaClkGet(freq, sel);
        break;
  
      case _CMU_EUART0CLKCTRL_CLKSEL_EM23GRPACLK:
        em23GrpaClkGet(freq, sel);
        break;
  
      default:
        if (freq != NULL) {
          *freq = 0U;
        }
        if (sel != NULL) {
          *sel = cmuSelect_Error;
        }
        EFM_ASSERT(false);
        break;
    }
  }
  
  /***************************************************************************//**
   * @brief
   *   Set maximum allowed prescaler for radio clock tree (RHCLK).
   ******************************************************************************/
  static void rhclkPrescMax(void)
  {
    // Set largest prescaler (DIV2).
    CMU->SYSCLKCTRL_SET = CMU_SYSCLKCTRL_RHCLKPRESC;
  }
  
  /***************************************************************************//**
   * @brief
   *   Set radio clock tree prescaler to achieve highest possible frequency
   *   and still be within spec.
   ******************************************************************************/
  static void rhclkPrescOptimize(void)
  {
    if (CMU_ClockFreqGet(cmuClock_SYSCLK) <= CMU_MAX_RHCLK_FREQ) {
      // Set smallest prescaler (DIV1).
      CMU->SYSCLKCTRL_CLR = CMU_SYSCLKCTRL_RHCLKPRESC;
    }
  }
  
  #endif  // defined(_SILICON_LABS_32B_SERIES_2_CONFIG_2)
  
  #if defined(_SILICON_LABS_32B_SERIES_2_CONFIG_1)
  /*******************************************************************************
   **************************   GLOBAL FUNCTIONS   *******************************
   ******************************************************************************/
  
  /***************************************************************************//**
   * @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 = 0U;
  
    switch (clock) {
      case cmuClock_SYSCLK:
        ret = SystemSYSCLKGet();
        break;
  
      case cmuClock_CORE:
      case cmuClock_HCLK:
      case cmuClock_LDMA:
      case cmuClock_GPCRC:
        ret = SystemHCLKGet();
        break;
  
      case cmuClock_EXPCLK:
        ret = SystemSYSCLKGet() / CMU_ClockDivGet(cmuClock_EXPCLK);
        break;
  
      case cmuClock_I2C1:
      case cmuClock_PRS:
      case cmuClock_PCLK:
      case cmuClock_GPIO:
      case cmuClock_USART0:
      case cmuClock_USART1:
      case cmuClock_USART2:
        ret = SystemHCLKGet() / CMU_ClockDivGet(cmuClock_PCLK);
        break;
  
      case cmuClock_I2C0:
      case cmuClock_LSPCLK:
        ret = SystemHCLKGet() / CMU_ClockDivGet(cmuClock_PCLK) / 2U;
        break;
  
      case cmuClock_IADC0:
      case cmuClock_IADCCLK:
        iadcClkGet(&ret, NULL);
        break;
  
      case cmuClock_TIMER0:
      case cmuClock_TIMER1:
      case cmuClock_TIMER2:
      case cmuClock_TIMER3:
      case cmuClock_EM01GRPACLK:
        em01GrpaClkGet(&ret, NULL);
        break;
  
      case cmuClock_SYSTICK:
      case cmuClock_LETIMER0:
      case cmuClock_EM23GRPACLK:
        em23GrpaClkGet(&ret, NULL);
        break;
  
      case cmuClock_BURTC:
      case cmuClock_EM4GRPACLK:
        em4GrpaClkGet(&ret, NULL);
        break;
  
      case cmuClock_WDOG0:
      case cmuClock_WDOG0CLK:
        wdog0ClkGet(&ret, NULL);
        break;
  
      case cmuClock_WDOG1:
      case cmuClock_WDOG1CLK:
        wdog1ClkGet(&ret, NULL);
        break;
  
      case cmuClock_DPLLREFCLK:
        dpllRefClkGet(&ret, NULL);
        break;
  
      case cmuClock_TRACECLK:
        traceClkGet(&ret, NULL);
        break;
  
      case cmuClock_RTCC:
      case cmuClock_RTCCCLK:
        rtccClkGet(&ret, NULL);
        break;
  
      default:
        EFM_ASSERT(false);
        break;
    }
    return ret;
  }
  
  /***************************************************************************//**
   * @brief
   *   Get HFRCOEM23 band in use.
   *
   * @return
   *   HFRCOEM23 band in use.
   ******************************************************************************/
  CMU_HFRCOEM23Freq_TypeDef CMU_HFRCOEM23BandGet(void)
  {
    return (CMU_HFRCOEM23Freq_TypeDef)SystemHFRCOEM23ClockGet();
  }
  
  /***************************************************************************//**
   * @brief
   *   Set HFRCOEM23 band and the tuning value based on the value in the
   *   calibration table made during production.
   *
   * @param[in] freq
   *   HFRCOEM23 frequency band to activate.
   ******************************************************************************/
  void CMU_HFRCOEM23BandSet(CMU_HFRCOEM23Freq_TypeDef freq)
  {
    uint32_t freqCal;
  
    // Get calibration data from DEVINFO
    freqCal = HFRCOEM23DevinfoGet(freq);
    EFM_ASSERT((freqCal != 0UL) && (freqCal != UINT_MAX));
  
    // Set divider for 1, 2 and 4MHz bands
    freqCal &= ~_HFRCO_CAL_CLKDIV_MASK;
    switch (freq) {
      case cmuHFRCOEM23Freq_1M0Hz:
        freqCal |= HFRCO_CAL_CLKDIV_DIV4;
        break;
  
      case cmuHFRCOEM23Freq_2M0Hz:
        freqCal |= HFRCO_CAL_CLKDIV_DIV2;
        break;
  
      default:
        break;
    }
  
    // Activate new band selection
    HFRCOEM23->CAL = freqCal;
  }
  
  /** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
  
  /*******************************************************************************
   **************************   LOCAL FUNCTIONS   ********************************
   ******************************************************************************/
  
  /***************************************************************************//**
   * @brief
   *   Get calibrated HFRCOEM23 tuning value from Device information (DI) page
   *   for a given frequency. Calibration value is not available for all frequency
   *   bands.
   *
   * @param[in] freq
   *   HFRCOEM23 frequency band
   ******************************************************************************/
  static uint32_t HFRCOEM23DevinfoGet(CMU_HFRCOEM23Freq_TypeDef freq)
  {
    uint32_t ret = 0U;
  
    switch (freq) {
      // 1, 2 and 4MHz share the same calibration word
      case cmuHFRCOEM23Freq_1M0Hz:
      case cmuHFRCOEM23Freq_2M0Hz:
      case cmuHFRCOEM23Freq_4M0Hz:
        ret = DEVINFO->HFRCOEM23CAL[0].HFRCOEM23CAL;
        break;
  
      case cmuHFRCOEM23Freq_13M0Hz:
        ret = DEVINFO->HFRCOEM23CAL[6].HFRCOEM23CAL;
        break;
  
      case cmuHFRCOEM23Freq_16M0Hz:
        ret = DEVINFO->HFRCOEM23CAL[7].HFRCOEM23CAL;
        break;
  
      case cmuHFRCOEM23Freq_19M0Hz:
        ret = DEVINFO->HFRCOEM23CAL[8].HFRCOEM23CAL;
        break;
  
      case cmuHFRCOEM23Freq_26M0Hz:
        ret = DEVINFO->HFRCOEM23CAL[10].HFRCOEM23CAL;
        break;
  
      case cmuHFRCOEM23Freq_32M0Hz:
        ret = DEVINFO->HFRCOEM23CAL[11].HFRCOEM23CAL;
        break;
  
      case cmuHFRCOEM23Freq_40M0Hz:
        ret = DEVINFO->HFRCOEM23CAL[12].HFRCOEM23CAL;
        break;
  
      case cmuHFRCOEM23Freq_UserDefined:
        break;
  
      default:
        EFM_ASSERT(false);
        break;
    }
    return ret;
  }
  
  /***************************************************************************//**
   * @brief
   *   Get selected oscillator and frequency for @ref cmuClock_TRACECLK
   *   clock tree.
   *
   * @param[out] freq
   *   The frequency.
   *
   * @param[out] sel
   *   The selected oscillator.
   ******************************************************************************/
  static void traceClkGet(uint32_t *freq, CMU_Select_TypeDef *sel)
  {
    uint32_t f = 0U;
    CMU_Select_TypeDef s;
  
    switch (CMU->TRACECLKCTRL & _CMU_TRACECLKCTRL_CLKSEL_MASK) {
      case _CMU_TRACECLKCTRL_CLKSEL_HCLK:
        f = SystemHCLKGet();
        s = cmuSelect_HCLK;
        break;
  
      case _CMU_TRACECLKCTRL_CLKSEL_HFRCOEM23:
        f = SystemHFRCOEM23ClockGet();
        s = cmuSelect_HFRCOEM23;
        break;
  
      default:
        s = cmuSelect_Error;
        EFM_ASSERT(false);
        break;
    }
  
    if (freq != NULL) {
      *freq = f;
    }
    if (sel != NULL) {
      *sel = s;
    }
  }
  
  /***************************************************************************//**
   * @brief
   *   Get selected oscillator and frequency for @ref cmuClock_WDOG1CLK
   *   clock tree.
   *
   * @param[out] freq
   *   The frequency.
   *
   * @param[out] sel
   *   The selected oscillator.
   ******************************************************************************/
  static void wdog1ClkGet(uint32_t *freq, CMU_Select_TypeDef *sel)
  {
    uint32_t f = 0U;
    CMU_Select_TypeDef s;
  
    switch (CMU->WDOG1CLKCTRL & _CMU_WDOG1CLKCTRL_CLKSEL_MASK) {
      case _CMU_WDOG1CLKCTRL_CLKSEL_LFRCO:
        f = SystemLFRCOClockGet();
        s = cmuSelect_LFRCO;
        break;
  
      case _CMU_WDOG1CLKCTRL_CLKSEL_LFXO:
        f = SystemLFXOClockGet();
        s = cmuSelect_LFXO;
        break;
  
      case _CMU_WDOG1CLKCTRL_CLKSEL_ULFRCO:
        f = SystemULFRCOClockGet();
        s = cmuSelect_ULFRCO;
        break;
  
      case _CMU_WDOG1CLKCTRL_CLKSEL_HCLKDIV1024:
        f = SystemHCLKGet() / 1024U;
        s = cmuSelect_HCLKDIV1024;
        break;
  
      default:
        s = cmuSelect_Error;
        EFM_ASSERT(false);
        break;
    }
  
    if (freq != NULL) {
      *freq = f;
    }
    if (sel != NULL) {
      *sel = s;
    }
  }
  
  /** @endcond */
  
  #endif  // defined(_SILICON_LABS_32B_SERIES_2_CONFIG_1)
  
  // The following code is common for all SERIES_2 configurations.
  
  /*******************************************************************************
   **************************   GLOBAL FUNCTIONS   *******************************
   ******************************************************************************/
  
  /***************************************************************************//**
   * @brief
   *   Calibrate an oscillator.
   *
   * @details
   *   Run a calibration of a selectable reference clock againt HCLK. Please
   *   refer to the reference manual, CMU chapter, for further details.
   *
   * @note
   *   This function will not return until calibration measurement is completed.
   *
   * @param[in] cycles
   *   The number of HCLK 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 against HCLK.
   *
   * @return
   *   The number of ticks the selected reference clock ticked while running
   *   cycles ticks of the HCLK clock.
   ******************************************************************************/
  uint32_t CMU_Calibrate(uint32_t cycles, CMU_Select_TypeDef ref)
  {
    // Check for cycle count overflow
    EFM_ASSERT(cycles <= (_CMU_CALCTRL_CALTOP_MASK
                          >> _CMU_CALCTRL_CALTOP_SHIFT));
  
    CMU_CalibrateConfig(cycles, cmuSelect_HCLK, ref);
    CMU_CalibrateStart();
    return CMU_CalibrateCountGet();
  }
  
  /***************************************************************************//**
   * @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 @ref CMU_CalibrateStart() is required, and
   *   the resulting calibration value can be read with the
   *   @ref 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 cycles.
   *
   * @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
   *   @ref CMU_CalibrateCountGet() function call.
   ******************************************************************************/
  void CMU_CalibrateConfig(uint32_t downCycles, CMU_Select_TypeDef downSel,
                           CMU_Select_TypeDef upSel)
  {
    // Keep untouched configuration settings
    uint32_t calCtrl = CMU->CALCTRL
                       & ~(_CMU_CALCTRL_UPSEL_MASK
                           | _CMU_CALCTRL_DOWNSEL_MASK
                           | _CMU_CALCTRL_CALTOP_MASK);
  
    // Check for cycle count overflow
    EFM_ASSERT(downCycles <= (_CMU_CALCTRL_CALTOP_MASK
                              >> _CMU_CALCTRL_CALTOP_SHIFT));
    calCtrl |= downCycles;
  
    // Set down counting clock source selector
    switch (downSel) {
      case cmuSelect_HCLK:
        calCtrl |= CMU_CALCTRL_DOWNSEL_HCLK;
        break;
  
      case cmuSelect_PRS:
        calCtrl |= CMU_CALCTRL_DOWNSEL_PRS;
        break;
  
      case cmuSelect_HFXO:
        calCtrl |= CMU_CALCTRL_DOWNSEL_HFXO;
        break;
  
      case cmuSelect_LFXO:
        calCtrl |= CMU_CALCTRL_DOWNSEL_LFXO;
        break;
  
      case cmuSelect_HFRCODPLL:
        calCtrl |= CMU_CALCTRL_DOWNSEL_HFRCODPLL;
        break;
  
  #if defined(HFRCOEM23_PRESENT)
      case cmuSelect_HFRCOEM23:
        calCtrl |= CMU_CALCTRL_DOWNSEL_HFRCOEM23;
        break;
  #endif
  
      case cmuSelect_FSRCO:
        calCtrl |= CMU_CALCTRL_DOWNSEL_FSRCO;
        break;
  
      case cmuSelect_LFRCO:
        calCtrl |= CMU_CALCTRL_DOWNSEL_LFRCO;
        break;
  
      case cmuSelect_ULFRCO:
        calCtrl |= CMU_CALCTRL_DOWNSEL_ULFRCO;
        break;
  
      case cmuSelect_Disabled:
        break;
  
      default:
        EFM_ASSERT(false);
        break;
    }
  
    // Set up counting clock source selector
    switch (upSel) {
      case cmuSelect_PRS:
        calCtrl |= CMU_CALCTRL_UPSEL_PRS;
        break;
  
      case cmuSelect_HFXO:
        calCtrl |= CMU_CALCTRL_UPSEL_HFXO;
        break;
  
      case cmuSelect_LFXO:
        calCtrl |= CMU_CALCTRL_UPSEL_LFXO;
        break;
  
      case cmuSelect_HFRCODPLL:
        calCtrl |= CMU_CALCTRL_UPSEL_HFRCODPLL;
        break;
  
  #if defined(HFRCOEM23_PRESENT)
      case cmuSelect_HFRCOEM23:
        calCtrl |= CMU_CALCTRL_UPSEL_HFRCOEM23;
        break;
  #endif
  
      case cmuSelect_FSRCO:
        calCtrl |= CMU_CALCTRL_UPSEL_FSRCO;
        break;
  
      case cmuSelect_LFRCO:
        calCtrl |= CMU_CALCTRL_UPSEL_LFRCO;
        break;
  
      case cmuSelect_ULFRCO:
        calCtrl |= CMU_CALCTRL_UPSEL_ULFRCO;
        break;
  
      case cmuSelect_Disabled:
        break;
  
      default:
        EFM_ASSERT(false);
        break;
    }
  
    CMU->CALCTRL = calCtrl;
  }
  
  /***************************************************************************//**
   * @brief
   *    Get calibration count value.
   *
   * @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 @ref 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 on
    if ((CMU->CALCTRL & CMU_CALCTRL_CONT) == 0UL) {
      // Wait until calibration completes
      while ((CMU->STATUS & CMU_STATUS_CALRDY) == 0UL) {
      }
    }
    return CMU->CALCNT;
  }
  
  /***************************************************************************//**
   * @brief
   *    Direct a clock to a GPIO pin.
   *
   * @param[in] clkNo
   *   Selects between CLKOUT0, CLKOUT1 or CLKOUT2 outputs. Use values 0,1or 2.
   *
   * @param[in] sel
   *   Select clock source.
   *
   * @param[in] clkDiv
   *   Select a clock divisor (1..32). Only applicable when cmuSelect_EXPCLK is
   *   slexted as clock source.
   *
   * @param[in] port
   *   GPIO port.
   *
   * @param[in] pin
   *   GPIO pin.
   *
   * @note
   *    Refer to the reference manual and the datasheet for details on which
   *    GPIO port/pins that are available.
   ******************************************************************************/
  void CMU_ClkOutPinConfig(uint32_t           clkNo,
                           CMU_Select_TypeDef sel,
                           CMU_ClkDiv_TypeDef clkDiv,
                           GPIO_Port_TypeDef  port,
                           unsigned int       pin)
  {
    uint32_t tmp = 0U, mask;
  
    EFM_ASSERT(clkNo <= 2U);
    EFM_ASSERT(clkDiv <= 32U);
    EFM_ASSERT(port <= 3U);
    EFM_ASSERT(pin <= 15U);
  
    switch (sel) {
      case cmuSelect_Disabled:
        tmp = CMU_EXPORTCLKCTRL_CLKOUTSEL0_DISABLED;
        break;
  
      case cmuSelect_FSRCO:
        tmp = CMU_EXPORTCLKCTRL_CLKOUTSEL0_FSRCO;
        break;
  
      case cmuSelect_HFXO:
        tmp = CMU_EXPORTCLKCTRL_CLKOUTSEL0_HFXO;
        break;
  
      case cmuSelect_HFRCODPLL:
        tmp = CMU_EXPORTCLKCTRL_CLKOUTSEL0_HFRCODPLL;
        break;
  
  #if defined(HFRCOEM23_PRESENT)
      case cmuSelect_HFRCOEM23:
        tmp = CMU_EXPORTCLKCTRL_CLKOUTSEL0_HFRCOEM23;
        break;
  #endif
  
      case cmuSelect_EXPCLK:
        tmp  = CMU_EXPORTCLKCTRL_CLKOUTSEL0_HFEXPCLK;
        break;
  
      case cmuSelect_LFXO:
        tmp = CMU_EXPORTCLKCTRL_CLKOUTSEL0_LFXO;
        break;
  
      case cmuSelect_LFRCO:
        tmp = CMU_EXPORTCLKCTRL_CLKOUTSEL0_LFRCO;
        break;
  
      case cmuSelect_ULFRCO:
        tmp = CMU_EXPORTCLKCTRL_CLKOUTSEL0_ULFRCO;
        break;
  
      case cmuSelect_HCLK:
        tmp = CMU_EXPORTCLKCTRL_CLKOUTSEL0_HCLK;
        break;
  
      default:
        EFM_ASSERT(false);
        break;
    }
  
    mask  = _CMU_EXPORTCLKCTRL_CLKOUTSEL0_MASK
            << (clkNo * _CMU_EXPORTCLKCTRL_CLKOUTSEL1_SHIFT);
    tmp <<= clkNo * _CMU_EXPORTCLKCTRL_CLKOUTSEL1_SHIFT;
  
    if (sel == cmuSelect_EXPCLK) {
      tmp  |= (clkDiv - 1U) << _CMU_EXPORTCLKCTRL_PRESC_SHIFT;
      mask |= _CMU_EXPORTCLKCTRL_PRESC_MASK;
    }
  
    CMU->EXPORTCLKCTRL = (CMU->EXPORTCLKCTRL & ~mask) | tmp;
  
    if (sel == cmuSelect_Disabled) {
      GPIO->CMUROUTE_CLR.ROUTEEN = GPIO_CMU_ROUTEEN_CLKOUT0PEN << clkNo;
      GPIO_PinModeSet(port, pin, gpioModeDisabled, 0);
    } else {
      GPIO->CMUROUTE_SET.ROUTEEN = GPIO_CMU_ROUTEEN_CLKOUT0PEN << clkNo;
      if (clkNo == 0U) {
        GPIO->CMUROUTE.CLKOUT0ROUTE = (port << _GPIO_CMU_CLKOUT0ROUTE_PORT_SHIFT)
                                      | (pin << _GPIO_CMU_CLKOUT0ROUTE_PIN_SHIFT);
      } else if (clkNo == 1) {
        GPIO->CMUROUTE.CLKOUT1ROUTE = (port << _GPIO_CMU_CLKOUT1ROUTE_PORT_SHIFT)
                                      | (pin << _GPIO_CMU_CLKOUT1ROUTE_PIN_SHIFT);
      } else {
        GPIO->CMUROUTE.CLKOUT2ROUTE = (port << _GPIO_CMU_CLKOUT2ROUTE_PORT_SHIFT)
                                      | (pin << _GPIO_CMU_CLKOUT2ROUTE_PIN_SHIFT);
      }
      GPIO_PinModeSet(port, pin, gpioModePushPull, 0);
    }
  }
  
  /***************************************************************************//**
   * @brief
   *   Get clock divisor.
   *
   * @param[in] clock
   *   Clock point to get divisor for. Notice that not all clock points
   *   have a divisors. Please refer to CMU overview in reference manual.
   *
   * @return
   *   The current clock point divisor. 1 is returned
   *   if @p clock specifies a clock point without divisor.
   ******************************************************************************/
  CMU_ClkDiv_TypeDef CMU_ClockDivGet(CMU_Clock_TypeDef clock)
  {
    uint32_t ret = 0U;
  
    switch (clock) {
      case cmuClock_HCLK:
      case cmuClock_CORE:
        ret = (CMU->SYSCLKCTRL & _CMU_SYSCLKCTRL_HCLKPRESC_MASK)
              >> _CMU_SYSCLKCTRL_HCLKPRESC_SHIFT;
  #if defined(_SILICON_LABS_32B_SERIES_2_CONFIG_1)
        if (ret == 2U ) {   // Unused value, illegal prescaler
          EFM_ASSERT(false);
        }
  #elif defined(_SILICON_LABS_32B_SERIES_2_CONFIG_2)
        if (!((ret == _CMU_SYSCLKCTRL_HCLKPRESC_DIV1)
              || (ret == _CMU_SYSCLKCTRL_HCLKPRESC_DIV2)
              || (ret == _CMU_SYSCLKCTRL_HCLKPRESC_DIV4)
              || (ret == _CMU_SYSCLKCTRL_HCLKPRESC_DIV8)
              || (ret == _CMU_SYSCLKCTRL_HCLKPRESC_DIV16))) {
          // Illegal prescaler
          EFM_ASSERT(false);
        }
  #endif
        break;
  
  #if defined(_CMU_TRACECLKCTRL_PRESC_MASK)
      case cmuClock_TRACECLK:
        ret = (CMU->TRACECLKCTRL & _CMU_TRACECLKCTRL_PRESC_MASK)
              >> _CMU_TRACECLKCTRL_PRESC_SHIFT;
        if (!((ret == _CMU_TRACECLKCTRL_PRESC_DIV1)
              || (ret == _CMU_TRACECLKCTRL_PRESC_DIV2)
              || (ret == _CMU_TRACECLKCTRL_PRESC_DIV4))) {
          // Illegal prescaler
          EFM_ASSERT(false);
        }
        break;
  #endif
  
      case cmuClock_EXPCLK:
        ret = (CMU->EXPORTCLKCTRL & _CMU_EXPORTCLKCTRL_PRESC_MASK)
              >> _CMU_EXPORTCLKCTRL_PRESC_SHIFT;
        break;
  
      case cmuClock_PCLK:
        ret = (CMU->SYSCLKCTRL & _CMU_SYSCLKCTRL_PCLKPRESC_MASK)
              >> _CMU_SYSCLKCTRL_PCLKPRESC_SHIFT;
        break;
  
      default:
        break;
    }
    return 1U + ret;
  }
  
  /***************************************************************************//**
   * @brief
   *   Set clock divisor.
   *
   * @param[in] clock
   *   Clock point to set divisor for. Notice that not all clock points
   *   have a divisor, please refer to CMU overview in the reference
   *   manual.
   *
   * @param[in] div
   *   The clock divisor to use.
   ******************************************************************************/
  void CMU_ClockDivSet(CMU_Clock_TypeDef clock, CMU_ClkDiv_TypeDef div)
  {
    switch (clock) {
      case cmuClock_HCLK:
      case cmuClock_CORE:
  #if defined(_SILICON_LABS_32B_SERIES_2_CONFIG_1)
        EFM_ASSERT((div == 1U) || (div == 2U) || (div == 4U));
  #elif defined(_SILICON_LABS_32B_SERIES_2_CONFIG_2)
        EFM_ASSERT((div == 1U) || (div == 2U) || (div == 4U)
                   || (div == 8U) || (div == 16U));
  #endif
  
        // Set max wait-states and PCLK divisor while changing core clock
        waitStateMax();
        pclkDivMax();
  #if defined(_SILICON_LABS_32B_SERIES_2_CONFIG_2)
        // Set largest prescaler for radio clock tree
        rhclkPrescMax();
  #endif
  
        // Set new divisor
        CMU->SYSCLKCTRL = (CMU->SYSCLKCTRL & ~_CMU_SYSCLKCTRL_HCLKPRESC_MASK)
                          | ((div - 1U) << _CMU_SYSCLKCTRL_HCLKPRESC_SHIFT);
  
        // Update CMSIS core clock variable and set optimum wait-states
        CMU_UpdateWaitStates(SystemCoreClockGet(), VSCALE_DEFAULT);
  
        // Set optimal PCLK divisor
        pclkDivOptimize();
  #if defined(_SILICON_LABS_32B_SERIES_2_CONFIG_2)
        // Set optimal RHCLK prescaler
        rhclkPrescOptimize();
  #endif
        break;
  
  #if defined(_CMU_TRACECLKCTRL_PRESC_MASK)
      case cmuClock_TRACECLK:
        EFM_ASSERT((div == 1U) || (div == 2U) || (div == 4U));
        CMU->TRACECLKCTRL = (CMU->TRACECLKCTRL & ~_CMU_TRACECLKCTRL_PRESC_MASK)
                            | ((div - 1U) << _CMU_TRACECLKCTRL_PRESC_SHIFT);
        break;
  #endif
  
      case cmuClock_EXPCLK:
        EFM_ASSERT((div >= 1U) && (div <= 32U));
        CMU->EXPORTCLKCTRL = (CMU->EXPORTCLKCTRL & ~_CMU_EXPORTCLKCTRL_PRESC_MASK)
                             | ((div - 1U) << _CMU_EXPORTCLKCTRL_PRESC_SHIFT);
        break;
  
      case cmuClock_PCLK:
        EFM_ASSERT((div == 1U) || (div == 2U));
        CMU->SYSCLKCTRL = (CMU->SYSCLKCTRL & ~_CMU_SYSCLKCTRL_PCLKPRESC_MASK)
                          | ((div - 1U) << _CMU_SYSCLKCTRL_PCLKPRESC_SHIFT);
        break;
  
      default:
        EFM_ASSERT(false);
        break;
    }
  }
  
  /***************************************************************************//**
   * @brief
   *   Get currently selected reference clock used for a clock branch.
   *
   * @param[in] clock
   *   Clock branch to fetch selected ref. clock for.
   *
   * @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_Error;
  
    switch (clock) {
  // -----------------------------------------------------------------------------
      case cmuClock_SYSCLK:
        switch (CMU->SYSCLKCTRL & _CMU_SYSCLKCTRL_CLKSEL_MASK) {
          case _CMU_SYSCLKCTRL_CLKSEL_HFRCODPLL:
            ret = cmuSelect_HFRCODPLL;
            break;
  
          case _CMU_SYSCLKCTRL_CLKSEL_HFXO:
            ret = cmuSelect_HFXO;
            break;
  
          case _CMU_SYSCLKCTRL_CLKSEL_CLKIN0:
            ret = cmuSelect_CLKIN0;
            break;
  
          case _CMU_SYSCLKCTRL_CLKSEL_FSRCO:
            ret = cmuSelect_FSRCO;
            break;
  
          default:
            ret = cmuSelect_Error;
            EFM_ASSERT(false);
            break;
        }
        break;
  
  // -----------------------------------------------------------------------------
      case cmuClock_IADC0:
      case cmuClock_IADCCLK:
        iadcClkGet(NULL, &ret);
        break;
  
  // -----------------------------------------------------------------------------
      case cmuClock_TIMER0:
      case cmuClock_TIMER1:
      case cmuClock_TIMER2:
      case cmuClock_TIMER3:
      case cmuClock_EM01GRPACLK:
        em01GrpaClkGet(NULL, &ret);
        break;
  
  // -----------------------------------------------------------------------------
      case cmuClock_SYSTICK:
      case cmuClock_LETIMER0:
      case cmuClock_EM23GRPACLK:
        em23GrpaClkGet(NULL, &ret);
        break;
  
  // -----------------------------------------------------------------------------
      case cmuClock_BURTC:
      case cmuClock_EM4GRPACLK:
        em4GrpaClkGet(NULL, &ret);
        break;
  
  #if defined(_CMU_EM01GRPBCLKCTRL_CLKSEL_MASK)
  // -----------------------------------------------------------------------------
      case cmuClock_PDMREF:
      case cmuClock_EM01GRPBCLK:
        em01GrpbClkGet(NULL, &ret);
        break;
  #endif
  
  // -----------------------------------------------------------------------------
      case cmuClock_WDOG0:
      case cmuClock_WDOG0CLK:
        wdog0ClkGet(NULL, &ret);
        break;
  
  #if defined(_CMU_WDOG1CLKCTRL_CLKSEL_MASK)
  // -----------------------------------------------------------------------------
      case cmuClock_WDOG1:
      case cmuClock_WDOG1CLK:
        wdog1ClkGet(NULL, &ret);
        break;
  #endif
  
  // -----------------------------------------------------------------------------
      case cmuClock_DPLLREFCLK:
        dpllRefClkGet(NULL, &ret);
        break;
  
  #if defined(_CMU_TRACECLKCTRL_CLKSEL_MASK)
  // -----------------------------------------------------------------------------
      case cmuClock_TRACECLK:
        traceClkGet(NULL, &ret);
        break;
  #endif
  
  #if defined(_CMU_EUART0CLKCTRL_CLKSEL_MASK)
  // -----------------------------------------------------------------------------
      case cmuClock_EUART0:
      case cmuClock_EUART0CLK:
        euart0ClkGet(NULL, &ret);
        break;
  #endif
  
  // -----------------------------------------------------------------------------
      case cmuClock_RTCC:
      case cmuClock_RTCCCLK:
        rtccClkGet(NULL, &ret);
        break;
  
  // -----------------------------------------------------------------------------
      default:
        EFM_ASSERT(false);
        break;
    }
    return ret;
  }
  
  /***************************************************************************//**
   * @brief
   *   Select reference clock/oscillator used for a clock branch.
   *
   * @param[in] clock
   *   Clock branch to select reference clock for.
   *
   * @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.
   ******************************************************************************/
  void CMU_ClockSelectSet(CMU_Clock_TypeDef clock, CMU_Select_TypeDef ref)
  {
    uint32_t tmp = 0U;
    bool oscForceEnStatus = false;
  
    switch (clock) {
  // -----------------------------------------------------------------------------
      case cmuClock_SYSCLK:
        switch (ref) {
          case cmuSelect_HFRCODPLL:
            tmp = CMU_SYSCLKCTRL_CLKSEL_HFRCODPLL;
            break;
  
          case cmuSelect_HFXO:
            tmp = CMU_SYSCLKCTRL_CLKSEL_HFXO;
  #if defined(_CMU_CLKEN0_MASK)
            CMU->CLKEN0_SET = CMU_CLKEN0_HFXO0;
  #endif
            // Make sure HFXO is enabled.
            oscForceEnStatus = (HFXO0->CTRL & HFXO_CTRL_DISONDEMAND) != 0;
            HFXO0->CTRL_SET = HFXO_CTRL_FORCEEN;
            break;
  
          case cmuSelect_CLKIN0:
            tmp = CMU_SYSCLKCTRL_CLKSEL_CLKIN0;
            break;
  
          case cmuSelect_FSRCO:
            tmp = CMU_SYSCLKCTRL_CLKSEL_FSRCO;
            break;
  
          default:
            EFM_ASSERT(false);
            break;
        }
  
  #if defined(EMU_VSCALE_PRESENT)
        // VSCALE up before changing clock.
        EMU_VScaleEM01(emuVScaleEM01_HighPerformance, true);
  #endif
  
        // Set max wait-states and PCLK divisor while changing core clock.
        waitStateMax();
        pclkDivMax();
  #if defined(_SILICON_LABS_32B_SERIES_2_CONFIG_2)
        // Set largest prescaler for radio clock tree
        rhclkPrescMax();
  #endif
  
        // Switch to selected oscillator.
        CMU->SYSCLKCTRL = (CMU->SYSCLKCTRL & ~_CMU_SYSCLKCTRL_CLKSEL_MASK) | tmp;
  
        // Update CMSIS core clock variable and set optimum wait-states.
        CMU_UpdateWaitStates(SystemCoreClockGet(), VSCALE_DEFAULT);
  
  #if defined(EMU_VSCALE_PRESENT)
        // Check if possible to downscale VSCALE setting.
        EMU_VScaleEM01ByClock(0, true);
  #endif
  
        // Set optimal PCLK divisor
        pclkDivOptimize();
  #if defined(_SILICON_LABS_32B_SERIES_2_CONFIG_2)
        // Set optimal RHCLK prescaler
        rhclkPrescOptimize();
  #endif
  
        if (oscForceEnStatus == false) {
          switch (ref) {
            case cmuSelect_HFXO:
              HFXO0->CTRL_CLR = HFXO_CTRL_FORCEEN;
              break;
  
            default:
              break;
          }
        }
        break;
  
  // -----------------------------------------------------------------------------
      case cmuClock_IADC0:
      case cmuClock_IADCCLK:
        switch (ref) {
          case cmuSelect_EM01GRPACLK:
            tmp = CMU_IADCCLKCTRL_CLKSEL_EM01GRPACLK;
            break;
  
  #if defined(HFRCOEM23_PRESENT)
          case cmuSelect_HFRCOEM23:
            tmp = CMU_IADCCLKCTRL_CLKSEL_HFRCOEM23;
            break;
  #endif
  
          case cmuSelect_FSRCO:
            tmp = CMU_IADCCLKCTRL_CLKSEL_FSRCO;
            break;
  
          default:
            EFM_ASSERT(false);
            break;
        }
        CMU->IADCCLKCTRL = (CMU->IADCCLKCTRL & ~_CMU_IADCCLKCTRL_CLKSEL_MASK)
                           | tmp;
        break;
  
  // -----------------------------------------------------------------------------
      case cmuClock_TIMER0:
      case cmuClock_TIMER1:
      case cmuClock_TIMER2:
      case cmuClock_TIMER3:
      case cmuClock_EM01GRPACLK:
        switch (ref) {
          case cmuSelect_HFRCODPLL:
            tmp = CMU_EM01GRPACLKCTRL_CLKSEL_HFRCODPLL;
            break;
  
          case cmuSelect_HFXO:
            tmp = CMU_EM01GRPACLKCTRL_CLKSEL_HFXO;
            break;
  
  #if defined(HFRCOEM23_PRESENT)
          case cmuSelect_HFRCOEM23:
            tmp = CMU_EM01GRPACLKCTRL_CLKSEL_HFRCOEM23;
            break;
  #endif
  
          case cmuSelect_FSRCO:
            tmp = CMU_EM01GRPACLKCTRL_CLKSEL_FSRCO;
            break;
  
          default:
            EFM_ASSERT(false);
            break;
        }
        CMU->EM01GRPACLKCTRL = (CMU->EM01GRPACLKCTRL
                                & ~_CMU_EM01GRPACLKCTRL_CLKSEL_MASK) | tmp;
        break;
  
  // -----------------------------------------------------------------------------
      case cmuClock_SYSTICK:
      case cmuClock_LETIMER0:
      case cmuClock_EM23GRPACLK:
        switch (ref) {
          case cmuSelect_LFRCO:
            tmp = CMU_EM23GRPACLKCTRL_CLKSEL_LFRCO;
            break;
  
          case cmuSelect_LFXO:
            tmp = CMU_EM23GRPACLKCTRL_CLKSEL_LFXO;
            break;
  
          case cmuSelect_ULFRCO:
            tmp = CMU_EM23GRPACLKCTRL_CLKSEL_ULFRCO;
            break;
  
          default:
            EFM_ASSERT(false);
            break;
        }
        CMU->EM23GRPACLKCTRL = (CMU->EM23GRPACLKCTRL
                                & ~_CMU_EM23GRPACLKCTRL_CLKSEL_MASK) | tmp;
        break;
  
  // -----------------------------------------------------------------------------
      case cmuClock_BURTC:
      case cmuClock_EM4GRPACLK:
        switch (ref) {
          case cmuSelect_LFRCO:
            tmp = CMU_EM4GRPACLKCTRL_CLKSEL_LFRCO;
            break;
  
          case cmuSelect_LFXO:
            tmp = CMU_EM4GRPACLKCTRL_CLKSEL_LFXO;
            break;
  
          case cmuSelect_ULFRCO:
            tmp = CMU_EM4GRPACLKCTRL_CLKSEL_ULFRCO;
            break;
  
          default:
            EFM_ASSERT(false);
            break;
        }
        CMU->EM4GRPACLKCTRL = (CMU->EM4GRPACLKCTRL
                               & ~_CMU_EM4GRPACLKCTRL_CLKSEL_MASK) | tmp;
        break;
  
  #if defined(_CMU_EM01GRPBCLKCTRL_CLKSEL_MASK)
  // -----------------------------------------------------------------------------
      case cmuClock_PDMREF:
      case cmuClock_EM01GRPBCLK:
        switch (ref) {
          case cmuSelect_HFRCODPLL:
            tmp = CMU_EM01GRPBCLKCTRL_CLKSEL_HFRCODPLL;
            break;
  
          case cmuSelect_HFXO:
            tmp = CMU_EM01GRPBCLKCTRL_CLKSEL_HFXO;
            break;
  
          case cmuSelect_FSRCO:
            tmp = CMU_EM01GRPBCLKCTRL_CLKSEL_FSRCO;
            break;
  
          case cmuSelect_CLKIN0:
            tmp = CMU_EM01GRPBCLKCTRL_CLKSEL_CLKIN0;
            break;
  
          case cmuSelect_HFRCODPLLRT:
            tmp = CMU_EM01GRPBCLKCTRL_CLKSEL_HFRCODPLLRT;
            break;
  
          case cmuSelect_HFXORT:
            tmp = CMU_EM01GRPBCLKCTRL_CLKSEL_HFXORT;
            break;
  
          default:
            EFM_ASSERT(false);
            break;
        }
        CMU->EM01GRPBCLKCTRL = (CMU->EM01GRPBCLKCTRL
                                & ~_CMU_EM01GRPBCLKCTRL_CLKSEL_MASK) | tmp;
        break;
  #endif
  
  // -----------------------------------------------------------------------------
      case cmuClock_WDOG0:
      case cmuClock_WDOG0CLK:
        switch (ref) {
          case cmuSelect_LFRCO:
            tmp = CMU_WDOG0CLKCTRL_CLKSEL_LFRCO;
            break;
  
          case cmuSelect_LFXO:
            tmp = CMU_WDOG0CLKCTRL_CLKSEL_LFXO;
            break;
  
          case cmuSelect_ULFRCO:
            tmp = CMU_WDOG0CLKCTRL_CLKSEL_ULFRCO;
            break;
  
          case cmuSelect_HCLKDIV1024:
            tmp = CMU_WDOG0CLKCTRL_CLKSEL_HCLKDIV1024;
            break;
  
          default:
            EFM_ASSERT(false);
            break;
        }
        CMU->WDOG0CLKCTRL = (CMU->WDOG0CLKCTRL & ~_CMU_WDOG0CLKCTRL_CLKSEL_MASK)
                            | tmp;
        break;
  
  #if defined(_CMU_WDOG1CLKCTRL_CLKSEL_MASK)
  // -----------------------------------------------------------------------------
      case cmuClock_WDOG1:
      case cmuClock_WDOG1CLK:
        switch (ref) {
          case cmuSelect_LFRCO:
            tmp = CMU_WDOG1CLKCTRL_CLKSEL_LFRCO;
            break;
  
          case cmuSelect_LFXO:
            tmp = CMU_WDOG1CLKCTRL_CLKSEL_LFXO;
            break;
  
          case cmuSelect_ULFRCO:
            tmp = CMU_WDOG1CLKCTRL_CLKSEL_ULFRCO;
            break;
  
          case cmuSelect_HCLKDIV1024:
            tmp = CMU_WDOG1CLKCTRL_CLKSEL_HCLKDIV1024;
            break;
  
          default:
            EFM_ASSERT(false);
            break;
        }
        CMU->WDOG1CLKCTRL = (CMU->WDOG1CLKCTRL & ~_CMU_WDOG1CLKCTRL_CLKSEL_MASK)
                            | tmp;
        break;
  #endif
  
  // -----------------------------------------------------------------------------
      case cmuClock_DPLLREFCLK:
        switch (ref) {
          case cmuSelect_HFXO:
            tmp = CMU_DPLLREFCLKCTRL_CLKSEL_HFXO;
            break;
  
          case cmuSelect_LFXO:
            tmp = CMU_DPLLREFCLKCTRL_CLKSEL_LFXO;
            break;
  
          case cmuSelect_CLKIN0:
            tmp = CMU_DPLLREFCLKCTRL_CLKSEL_CLKIN0;
            break;
  
          case cmuSelect_Disabled:
            tmp = CMU_DPLLREFCLKCTRL_CLKSEL_DISABLED;
            break;
  
          default:
            EFM_ASSERT(false);
            break;
        }
        CMU->DPLLREFCLKCTRL = (CMU->DPLLREFCLKCTRL
                               & ~_CMU_DPLLREFCLKCTRL_CLKSEL_MASK) | tmp;
        break;
  
  #if defined(_CMU_TRACECLKCTRL_CLKSEL_MASK)
  // -----------------------------------------------------------------------------
      case cmuClock_TRACECLK:
        switch (ref) {
          case cmuSelect_HCLK:
            tmp = CMU_TRACECLKCTRL_CLKSEL_HCLK;
            break;
  
          case cmuSelect_HFRCOEM23:
            tmp = CMU_TRACECLKCTRL_CLKSEL_HFRCOEM23;
            break;
  
          default:
            EFM_ASSERT(false);
            break;
        }
        CMU->TRACECLKCTRL = (CMU->TRACECLKCTRL & ~_CMU_TRACECLKCTRL_CLKSEL_MASK)
                            | tmp;
        break;
  #endif
  
  #if defined(_CMU_EUART0CLKCTRL_CLKSEL_MASK)
  // -----------------------------------------------------------------------------
      case cmuClock_EUART0:
      case cmuClock_EUART0CLK:
        switch (ref) {
          case cmuSelect_EM01GRPACLK:
            tmp = _CMU_EUART0CLKCTRL_CLKSEL_EM01GRPACLK;
            break;
  
          case cmuSelect_EM23GRPACLK:
            tmp = _CMU_EUART0CLKCTRL_CLKSEL_EM23GRPACLK;
            break;
  
          default:
            EFM_ASSERT(false);
            break;
        }
        CMU->EUART0CLKCTRL = (CMU->EUART0CLKCTRL & ~_CMU_EUART0CLKCTRL_CLKSEL_MASK)
                             | tmp;
        break;
  #endif
  
  // -----------------------------------------------------------------------------
      case cmuClock_RTCC:
      case cmuClock_RTCCCLK:
        switch (ref) {
          case cmuSelect_LFRCO:
            tmp = CMU_RTCCCLKCTRL_CLKSEL_LFRCO;
            break;
  
          case cmuSelect_LFXO:
            tmp = CMU_RTCCCLKCTRL_CLKSEL_LFXO;
            break;
  
          case cmuSelect_ULFRCO:
            tmp = CMU_RTCCCLKCTRL_CLKSEL_ULFRCO;
            break;
  
          default:
            EFM_ASSERT(false);
            break;
        }
        CMU->RTCCCLKCTRL = (CMU->RTCCCLKCTRL & ~_CMU_RTCCCLKCTRL_CLKSEL_MASK)
                           | tmp;
        break;
  
  // -----------------------------------------------------------------------------
      default:
        EFM_ASSERT(false);
        break;
    }
  }
  
  /**************************************************************************//**
   * @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. @n
   *   N & M limitations: @n
   *     300 < N <= 4095 @n
   *     0 <= M <= 4095 @n
   *   Any peripheral running off HFRCODPLL should be switched to a lower
   *   frequency clock (if possible) prior to calling this function to avoid
   *   over-clocking.
   *
   * @param[in] init
   *    DPLL setup parameter struct.
   *
   * @return
   *   Returns false on invalid target frequency or DPLL locking error.
   *****************************************************************************/
  bool CMU_DPLLLock(const CMU_DPLLInit_TypeDef *init)
  {
    int index = 0;
    unsigned int i;
    bool hclkDivIncreased = false;
    uint32_t hfrcoCalVal, lockStatus, hclkDiv = 0, sysFreq;
  
    EFM_ASSERT(init->frequency >= hfrcoCalTable[0].minFreq);
    EFM_ASSERT(init->frequency
               <= hfrcoCalTable[HFRCOCALTABLE_ENTRIES - 1U].maxFreq);
  
    EFM_ASSERT(init->n > 300U);
    EFM_ASSERT(init->n <= (_DPLL_CFG1_N_MASK >> _DPLL_CFG1_N_SHIFT));
    EFM_ASSERT(init->m <= (_DPLL_CFG1_M_MASK >> _DPLL_CFG1_M_SHIFT));
  
  #if defined(EMU_VSCALE_PRESENT)
    if ((EMU_VScaleGet() == emuVScaleEM01_LowPower)
        && (init->frequency > CMU_VSCALEEM01_LOWPOWER_VOLTAGE_CLOCK_MAX)) {
      EFM_ASSERT(false);
      return false;
    }
  #endif
  
    // Find correct HFRCODPLL band, and retrieve a HFRCOCAL value.
    for (i = 0; i < HFRCOCALTABLE_ENTRIES; i++) {
      if ((init->frequency    >= hfrcoCalTable[i].minFreq)
          && (init->frequency <= hfrcoCalTable[i].maxFreq)) {
        index = (int)i;                       // Correct band found
        break;
      }
    }
    if ((uint32_t)index == HFRCOCALTABLE_ENTRIES) {
      EFM_ASSERT(false);
      return false;                           // Target frequency out of spec.
    }
    hfrcoCalVal = hfrcoCalTable[index].value;
  
    // Check if we have a calibrated HFRCOCAL.TUNING value in device DI page.
    if (hfrcoCalTable[index].band != (CMU_HFRCODPLLFreq_TypeDef)0) {
      uint32_t tuning;
  
      tuning = (HFRCODPLLDevinfoGet(hfrcoCalTable[index].band)
                & _HFRCO_CAL_TUNING_MASK)
               >> _HFRCO_CAL_TUNING_SHIFT;
      hfrcoCalVal |= tuning << _HFRCO_CAL_TUNING_SHIFT;
    }
  
    // Update CMSIS HFRCODPLL frequency.
    SystemHFRCODPLLClockSet(init->frequency);
  
    if (CMU_ClockSelectGet(cmuClock_SYSCLK) == cmuSelect_HFRCODPLL) {
      // Set max wait-states and PCLK divisor while changing core clock
      waitStateMax();
      pclkDivMax();
  #if defined(_SILICON_LABS_32B_SERIES_2_CONFIG_2)
      // Set largest prescaler for radio clock tree
      rhclkPrescMax();
  #endif
  
      // Increase HCLK divider value (if possible) while locking DPLL to
      // avoid over-clocking.
      hclkDiv = CMU_ClockDivGet(cmuClock_HCLK);
      hclkDivIncreased = true;
      if (hclkDiv == 1U) {
        CMU_ClockDivSet(cmuClock_HCLK, 2U);
      } else if (hclkDiv == 2U) {
        CMU_ClockDivSet(cmuClock_HCLK, 4U);
      } else {
        hclkDivIncreased = false;
      }
    }
  
  #if defined(CMU_CLKEN0_DPLL0)
    CMU->CLKEN0_SET = CMU_CLKEN0_DPLL0 | CMU_CLKEN0_HFRCO0;
  #endif
  
    // Make sure DPLL is disabled before configuring
    DPLL0->EN_CLR = DPLL_EN_EN;
    while ((DPLL0->STATUS & (DPLL_STATUS_ENS | DPLL_STATUS_RDY)) != 0UL) {
    }
    DPLL0->IF_CLR = DPLL_IF_LOCK | DPLL_IF_LOCKFAILLOW | DPLL_IF_LOCKFAILHIGH;
    DPLL0->CFG1   = ((uint32_t)init->n   << _DPLL_CFG1_N_SHIFT)
                    | ((uint32_t)init->m << _DPLL_CFG1_M_SHIFT);
    HFRCO0->CAL = hfrcoCalVal;
    CMU_ClockSelectSet(cmuClock_DPLLREFCLK, init->refClk);
    DPLL0->CFG = ((init->autoRecover ? 1UL : 0UL) << _DPLL_CFG_AUTORECOVER_SHIFT)
                 | ((init->ditherEn ? 1UL : 0UL)  << _DPLL_CFG_DITHEN_SHIFT)
                 | ((uint32_t)init->edgeSel  << _DPLL_CFG_EDGESEL_SHIFT)
                 | ((uint32_t)init->lockMode << _DPLL_CFG_MODE_SHIFT);
    // Lock DPLL
    DPLL0->EN_SET = DPLL_EN_EN;
    while ((lockStatus = (DPLL0->IF & (DPLL_IF_LOCK
                                       | DPLL_IF_LOCKFAILLOW
                                       | DPLL_IF_LOCKFAILHIGH))) == 0UL) {
    }
  
    if (CMU_ClockSelectGet(cmuClock_SYSCLK) == cmuSelect_HFRCODPLL) {
      if (hclkDivIncreased) {
        // Restore original HCLK divider
        CMU_ClockDivSet(cmuClock_HCLK, hclkDiv);
      }
  
      // Call @ref SystemCoreClockGet() to update CMSIS core clock variable.
      sysFreq = SystemCoreClockGet();
      EFM_ASSERT(sysFreq <= init->frequency);
      EFM_ASSERT(sysFreq <= SystemHFRCODPLLClockGet());
      EFM_ASSERT(init->frequency == SystemHFRCODPLLClockGet());
  
      // Set optimal wait-states and PCLK divisor
      CMU_UpdateWaitStates(sysFreq, VSCALE_DEFAULT);
      pclkDivOptimize();
  #if defined(_SILICON_LABS_32B_SERIES_2_CONFIG_2)
      // Set optimal RHCLK prescaler
      rhclkPrescOptimize();
  #endif
    }
  
    if (lockStatus == DPLL_IF_LOCK) {
      return true;
    }
    return false;
  }
  
  /***************************************************************************//**
   * @brief
   *   Get HFRCODPLL band in use.
   *
   * @return
   *   HFRCODPLL band in use.
   ******************************************************************************/
  CMU_HFRCODPLLFreq_TypeDef CMU_HFRCODPLLBandGet(void)
  {
    return (CMU_HFRCODPLLFreq_TypeDef)SystemHFRCODPLLClockGet();
  }
  
  /***************************************************************************//**
   * @brief
   *   Set HFRCODPLL band and the tuning value based on the value in the
   *   calibration table made during production.
   *
   * @param[in] freq
   *   HFRCODPLL frequency band to activate.
   ******************************************************************************/
  void CMU_HFRCODPLLBandSet(CMU_HFRCODPLLFreq_TypeDef freq)
  {
    uint32_t freqCal, sysFreq;
  #if defined(EMU_VSCALE_PRESENT)
    uint32_t prevFreq;
  #endif
  
    // Get calibration data from DEVINFO
    freqCal = HFRCODPLLDevinfoGet(freq);
    EFM_ASSERT((freqCal != 0UL) && (freqCal != UINT_MAX));
  
  #if defined(CMU_CLKEN0_DPLL0)
    CMU->CLKEN0_SET = CMU_CLKEN0_DPLL0 | CMU_CLKEN0_HFRCO0;
  #endif
  
    // Make sure DPLL is disabled before configuring
    if (DPLL0->EN_CLR == DPLL_EN_EN) {
      DPLL0->EN_CLR = DPLL_EN_EN;
      while ((DPLL0->STATUS & (DPLL_STATUS_ENS | DPLL_STATUS_RDY)) != 0UL) {
      }
    }
  
    // Update CMSIS HFRCODPLL frequency.
  #if defined(EMU_VSCALE_PRESENT)
    prevFreq = SystemHFRCODPLLClockGet();
  #endif
    SystemHFRCODPLLClockSet(freq);
  
    // Set max wait-states and PCLK divisor while changing core clock
    if (CMU_ClockSelectGet(cmuClock_SYSCLK) == cmuSelect_HFRCODPLL) {
      waitStateMax();
      pclkDivMax();
  #if defined(_SILICON_LABS_32B_SERIES_2_CONFIG_2)
      // Set largest prescaler for radio clock tree
      rhclkPrescMax();
  #endif
    }
  
    // Set divider for 1, 2 and 4MHz bands
    freqCal &= ~_HFRCO_CAL_CLKDIV_MASK;
    switch (freq) {
      case cmuHFRCODPLLFreq_1M0Hz:
        freqCal |= HFRCO_CAL_CLKDIV_DIV4;
        break;
  
      case cmuHFRCODPLLFreq_2M0Hz:
        freqCal |= HFRCO_CAL_CLKDIV_DIV2;
        break;
  
      default:
        break;
    }
  
  #if defined(EMU_VSCALE_PRESENT)
    if ((uint32_t)freq > prevFreq) {
      /* When increasing frequency voltage scale must be done before the change. */
      EMU_VScaleEM01ByClock((uint32_t)freq, true);
    }
  #endif
  
    // Activate new band selection
    HFRCO0->CAL = freqCal;
  
    // If HFRCODPLL is selected as SYSCLK (and HCLK), optimize flash access
    // wait-state configuration and PCLK divisor for this frequency.
    if (CMU_ClockSelectGet(cmuClock_SYSCLK) == cmuSelect_HFRCODPLL) {
      // Call @ref SystemCoreClockGet() to update CMSIS core clock variable.
      sysFreq = SystemCoreClockGet();
      EFM_ASSERT(sysFreq <= (uint32_t)freq);
      CMU_UpdateWaitStates(sysFreq, VSCALE_DEFAULT);
      pclkDivOptimize();
  #if defined(_SILICON_LABS_32B_SERIES_2_CONFIG_2)
      // Set optimal RHCLK prescaler
      rhclkPrescOptimize();
  #endif
    }
  
  #if defined(EMU_VSCALE_PRESENT)
    if ((uint32_t)freq <= prevFreq) {
      /* When decreasing frequency voltage scale must be done after the change. */
      EMU_VScaleEM01ByClock(0, true);
    }
  #endif
  }
  
  /**************************************************************************//**
   * @brief
   *   Initialize all HFXO control registers.
   *
   * @note
   *   HFXO configuration should be obtained from a configuration tool,
   *   app note or xtal datasheet. This function returns early if HFXO is
   *   already selected as SYSCLK.
   *
   * @param[in] hfxoInit
   *    HFXO setup parameters.
   *****************************************************************************/
  void CMU_HFXOInit(const CMU_HFXOInit_TypeDef *hfxoInit)
  {
    // Check all initialization structure members which may overflow target
    // bitfield.
    EFM_ASSERT(hfxoInit->timeoutCbLsb
               <= (_HFXO_XTALCFG_TIMEOUTCBLSB_MASK
                   >> _HFXO_XTALCFG_TIMEOUTCBLSB_SHIFT));
    EFM_ASSERT(hfxoInit->timeoutSteadyFirstLock
               <= (_HFXO_XTALCFG_TIMEOUTSTEADY_MASK
                   >> _HFXO_XTALCFG_TIMEOUTSTEADY_SHIFT));
    EFM_ASSERT(hfxoInit->timeoutSteady
               <= (_HFXO_XTALCFG_TIMEOUTSTEADY_MASK
                   >> _HFXO_XTALCFG_TIMEOUTSTEADY_SHIFT));
    EFM_ASSERT(hfxoInit->ctuneXoStartup
               <= (_HFXO_XTALCFG_CTUNEXOSTARTUP_MASK
                   >> _HFXO_XTALCFG_CTUNEXOSTARTUP_SHIFT));
    EFM_ASSERT(hfxoInit->ctuneXiStartup
               <= (_HFXO_XTALCFG_CTUNEXISTARTUP_MASK
                   >> _HFXO_XTALCFG_CTUNEXISTARTUP_SHIFT));
    EFM_ASSERT(hfxoInit->coreBiasStartup
               <= (_HFXO_XTALCFG_COREBIASSTARTUP_MASK
                   >> _HFXO_XTALCFG_COREBIASSTARTUP_SHIFT));
    EFM_ASSERT(hfxoInit->imCoreBiasStartup
               <= (_HFXO_XTALCFG_COREBIASSTARTUPI_MASK
                   >> _HFXO_XTALCFG_COREBIASSTARTUPI_SHIFT));
    EFM_ASSERT(hfxoInit->coreDegenAna
               <= (_HFXO_XTALCTRL_COREDGENANA_MASK
                   >> _HFXO_XTALCTRL_COREDGENANA_SHIFT));
    EFM_ASSERT(hfxoInit->ctuneFixAna
               <= (_HFXO_XTALCTRL_CTUNEFIXANA_MASK
                   >> _HFXO_XTALCTRL_CTUNEFIXANA_SHIFT));
    EFM_ASSERT(hfxoInit->mode
               <= (_HFXO_CFG_MODE_MASK >> _HFXO_CFG_MODE_SHIFT));
  
    // Return early if HFXO is already selected as SYSCLK.
    if (CMU_ClockSelectGet(cmuClock_SYSCLK) == cmuSelect_HFXO) {
      return;
    }
  
  #if defined(CMU_CLKEN0_HFXO0)
    // Enable HFXO module clock.
    CMU->CLKEN0_SET = CMU_CLKEN0_HFXO0;
  #endif
  
    // Unlock register interface.
    HFXO0->LOCK = HFXO_LOCK_LOCKKEY_UNLOCK;
  
    // Disable HFXO.
    HFXO0->CTRL_SET = HFXO_CTRL_DISONDEMAND;
    HFXO0->CTRL_CLR = HFXO_CTRL_FORCEEN;
    while ((HFXO0->STATUS & _HFXO_STATUS_ENS_MASK) != 0U) {
    }
  
    // Configure HFXO as specified in initialization struct, use
    // timeoutSteadyFirstLock as TIMEOUTSTEADY value.
    HFXO0->XTALCFG =
      (hfxoInit->timeoutCbLsb        << _HFXO_XTALCFG_TIMEOUTCBLSB_SHIFT)
      | (hfxoInit->timeoutSteadyFirstLock
         << _HFXO_XTALCFG_TIMEOUTSTEADY_SHIFT)
      | (hfxoInit->ctuneXoStartup    << _HFXO_XTALCFG_CTUNEXOSTARTUP_SHIFT)
      | (hfxoInit->ctuneXiStartup    << _HFXO_XTALCFG_CTUNEXISTARTUP_SHIFT)
      | (hfxoInit->coreBiasStartup   << _HFXO_XTALCFG_COREBIASSTARTUP_SHIFT)
      | (hfxoInit->imCoreBiasStartup << _HFXO_XTALCFG_COREBIASSTARTUPI_SHIFT);
  
    HFXO0->XTALCTRL =
      (hfxoInit->coreDegenAna    << _HFXO_XTALCTRL_COREDGENANA_SHIFT)
      | (hfxoInit->ctuneFixAna   << _HFXO_XTALCTRL_CTUNEFIXANA_SHIFT)
      | (hfxoInit->ctuneXoAna    << _HFXO_XTALCTRL_CTUNEXOANA_SHIFT)
      | (hfxoInit->ctuneXiAna    << _HFXO_XTALCTRL_CTUNEXIANA_SHIFT)
      | (hfxoInit->coreBiasAna   << _HFXO_XTALCTRL_COREBIASANA_SHIFT);
  
    HFXO0->CFG = (HFXO0->CFG & ~(_HFXO_CFG_SQBUFSCHTRGANA_MASK
                                 | _HFXO_CFG_ENXIDCBIASANA_MASK
                                 | _HFXO_CFG_MODE_MASK))
                 | ((hfxoInit->mode == cmuHfxoOscMode_Crystal)
                    ? 0 : HFXO_CFG_SQBUFSCHTRGANA)
                 | (hfxoInit->enXiDcBiasAna    << _HFXO_CFG_ENXIDCBIASANA_SHIFT)
                 | (hfxoInit->mode             << _HFXO_CFG_MODE_SHIFT);
  
    if (hfxoInit->mode == cmuHfxoOscMode_Crystal) {
      // Lock HFXO with FORCEEN bit set and DISONDEMAND bit cleared.
      HFXO0->CTRL = (HFXO0->CTRL & ~(_HFXO_CTRL_FORCEXO2GNDANA_MASK
                                     | _HFXO_CTRL_FORCEXI2GNDANA_MASK
                                     | _HFXO_CTRL_DISONDEMAND_MASK
                                     | _HFXO_CTRL_FORCEEN_MASK))
                    | (hfxoInit->forceXo2GndAna << _HFXO_CTRL_FORCEXO2GNDANA_SHIFT)
                    | (hfxoInit->forceXi2GndAna << _HFXO_CTRL_FORCEXI2GNDANA_SHIFT)
                    | HFXO_CTRL_FORCEEN;
  
      // Wait for HFXO lock and core bias algorithm to complete.
      while ((HFXO0->STATUS & (HFXO_STATUS_RDY | HFXO_STATUS_COREBIASOPTRDY
                               | HFXO_STATUS_ENS | HFXO_STATUS_FSMLOCK))
             != (HFXO_STATUS_RDY | HFXO_STATUS_COREBIASOPTRDY | HFXO_STATUS_ENS
                 | HFXO_STATUS_FSMLOCK)) {
      }
  
      // We must set DISONDEMAND to be able to enter new values for use on
      // subsequent locks.
      HFXO0->CTRL_SET = HFXO_CTRL_DISONDEMAND;
  #if defined(HFXO_CMD_MANUALOVERRIDE)
      HFXO0->CMD = HFXO_CMD_MANUALOVERRIDE;
  #endif
      while ((HFXO0->STATUS & HFXO_STATUS_FSMLOCK) != 0) {
      }
  
      // Set new TIMEOUTSTEADY value for use on subsequent locks.
      HFXO0->XTALCFG = (HFXO0->XTALCFG & ~_HFXO_XTALCFG_TIMEOUTSTEADY_MASK)
                       | (hfxoInit->timeoutSteady
                          << _HFXO_XTALCFG_TIMEOUTSTEADY_SHIFT);
  
      // Skip core bias algorithm on subsequent locks.
      HFXO0->XTALCTRL_SET = HFXO_XTALCTRL_SKIPCOREBIASOPT;
  
      if (hfxoInit->disOnDemand == false) {
        HFXO0->CTRL_CLR = HFXO_CTRL_DISONDEMAND;
      }
  
      if (hfxoInit->forceEn == false) {
        HFXO0->CTRL_CLR = HFXO_CTRL_FORCEEN;
      }
    } else {
      // Lock HFXO in EXTERNAL SINE mode.
      HFXO0->CTRL = (HFXO0->CTRL & ~(_HFXO_CTRL_FORCEXO2GNDANA_MASK
                                     | _HFXO_CTRL_FORCEXI2GNDANA_MASK
                                     | _HFXO_CTRL_DISONDEMAND_MASK
                                     | _HFXO_CTRL_FORCEEN_MASK))
                    | (hfxoInit->forceXo2GndAna << _HFXO_CTRL_FORCEXO2GNDANA_SHIFT)
                    | (hfxoInit->forceXi2GndAna << _HFXO_CTRL_FORCEXI2GNDANA_SHIFT)
                    | (hfxoInit->disOnDemand    << _HFXO_CTRL_DISONDEMAND_SHIFT)
                    | (hfxoInit->forceEn        << _HFXO_CTRL_FORCEEN_SHIFT);
    }
  
    if (hfxoInit->regLock) {
      HFXO0->LOCK = ~HFXO_LOCK_LOCKKEY_UNLOCK;
    }
  }
  
  /**************************************************************************//**
   * @brief
   *   Initialize 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)
  {
    EFM_ASSERT(lfxoInit->timeout
               <= (_LFXO_CFG_TIMEOUT_MASK  >> _LFXO_CFG_TIMEOUT_SHIFT));
    EFM_ASSERT(lfxoInit->mode
               <= (_LFXO_CFG_MODE_MASK >> _LFXO_CFG_MODE_SHIFT));
    EFM_ASSERT(lfxoInit->gain
               <= (_LFXO_CAL_GAIN_MASK >> _LFXO_CAL_GAIN_SHIFT));
    EFM_ASSERT(lfxoInit->capTune
               <= (_LFXO_CAL_CAPTUNE_MASK >> _LFXO_CAL_CAPTUNE_SHIFT));
  
    // Max internal capacitance tuning value is 0x4F (20 pF)
    uint8_t ctune = SL_MIN(0x4FU, lfxoInit->capTune);
  
  #if defined(CMU_CLKEN0_LFXO)
    // Enable LFXO module clock.
    CMU->CLKEN0_SET = CMU_CLKEN0_LFXO;
  #endif
  
    // Unlock register interface
    LFXO->LOCK = LFXO_LOCK_LOCKKEY_UNLOCK;
  
    // Disable LFXO
    LFXO->CTRL_SET = LFXO_CTRL_DISONDEMAND;
    LFXO->CTRL_CLR = LFXO_CTRL_FORCEEN;
    while ((LFXO->STATUS & _LFXO_STATUS_ENS_MASK) != 0U) {
    }
  
    // Configure LFXO as specified
    LFXO->CAL = (lfxoInit->gain  << _LFXO_CAL_GAIN_SHIFT)
                | (ctune         << _LFXO_CAL_CAPTUNE_SHIFT);
  
    LFXO->CFG = (lfxoInit->timeout           << _LFXO_CFG_TIMEOUT_SHIFT)
                | (lfxoInit->mode            << _LFXO_CFG_MODE_SHIFT)
                | (lfxoInit->highAmplitudeEn << _LFXO_CFG_HIGHAMPL_SHIFT)
                | (lfxoInit->agcEn           << _LFXO_CFG_AGC_SHIFT);
  
    LFXO->CTRL = (lfxoInit->failDetEM4WUEn   << _LFXO_CTRL_FAILDETEM4WUEN_SHIFT)
                 | (lfxoInit->failDetEn      << _LFXO_CTRL_FAILDETEN_SHIFT)
                 | (lfxoInit->disOnDemand    << _LFXO_CTRL_DISONDEMAND_SHIFT)
                 | (lfxoInit->forceEn        << _LFXO_CTRL_FORCEEN_SHIFT);
  
    if (lfxoInit->regLock) {
      LFXO->LOCK = ~LFXO_LOCK_LOCKKEY_UNLOCK;
    }
  }
  
  #if defined(PLFRCO_PRESENT)
  /**************************************************************************//**
   * @brief
   *   Configure the LFRCO precision.
   *
   * @details
   *   When enabling high precision mode on the LFRCO the hardware will tune
   *   the oscillator automatically using the HFXO as a reference.
   *
   * @param[in] precision
   *    LFRCO precision, this can be either high or default.
   *****************************************************************************/
  void CMU_LFRCOSetPrecision(CMU_Precision_TypeDef precision)
  {
    EFM_ASSERT(38400000 == SystemHFXOClockGet());
  
    CMU->CLKEN0_SET = CMU_CLKEN0_LFRCO;
  
    LFRCO->CTRL = LFRCO_CTRL_DISONDEMAND; // Force disable
    while ((LFRCO->STATUS & LFRCO_STATUS_ENS) != 0U) {
      // Wait for LFRCO to stop
    }
  
    if (precision == cmuPrecisionHigh) {
      LFRCO->CFG = LFRCO_CFG_HIGHPRECEN;
    } else {
      LFRCO->CFG = 0;
    }
    LFRCO->CTRL = _LFRCO_CTRL_RESETVALUE;
  }
  #endif
  
  /***************************************************************************//**
   * @brief
   *   Get oscillator frequency tuning setting.
   *
   * @param[in] osc
   *   Oscillator to get tuning value for.
   *
   * @return
   *   The oscillator frequency tuning setting in use.
   ******************************************************************************/
  uint32_t CMU_OscillatorTuningGet(CMU_Osc_TypeDef osc)
  {
    uint32_t ret = 0U;
  
    switch (osc) {
  #if defined(_SILICON_LABS_32B_SERIES_2_CONFIG_1)
      case cmuOsc_LFRCO:
        ret = (LFRCO->CAL & _LFRCO_CAL_FREQTRIM_MASK)
              >> _LFRCO_CAL_FREQTRIM_SHIFT;
        break;
  #endif
  
      case cmuOsc_HFRCODPLL:
  #if defined(CMU_CLKEN0_HFRCO0)
        CMU->CLKEN0_SET = CMU_CLKEN0_HFRCO0;
  #endif
        ret = (HFRCO0->CAL & _HFRCO_CAL_TUNING_MASK) >> _HFRCO_CAL_TUNING_SHIFT;
        break;
  
  #if defined(HFRCOEM23_PRESENT)
      case cmuOsc_HFRCOEM23:
        ret = (HFRCOEM23->CAL & _HFRCO_CAL_TUNING_MASK)
              >> _HFRCO_CAL_TUNING_SHIFT;
        break;
  #endif
  
      default:
        EFM_ASSERT(false);
        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.
   *
   * @param[in] val
   *   The oscillator frequency tuning setting to use.
   ******************************************************************************/
  void CMU_OscillatorTuningSet(CMU_Osc_TypeDef osc, uint32_t val)
  {
    switch (osc) {
  #if defined(_SILICON_LABS_32B_SERIES_2_CONFIG_1)
      case cmuOsc_LFRCO:
        EFM_ASSERT(val <= (_LFRCO_CAL_FREQTRIM_MASK
                           >> _LFRCO_CAL_FREQTRIM_SHIFT));
        val &= _LFRCO_CAL_FREQTRIM_MASK >> _LFRCO_CAL_FREQTRIM_SHIFT;
        while (LFRCO->SYNCBUSY != 0U) {
        }
        LFRCO->CAL = (LFRCO->CAL & ~_LFRCO_CAL_FREQTRIM_MASK)
                     | (val << _LFRCO_CAL_FREQTRIM_SHIFT);
        break;
  #endif
  
      case cmuOsc_HFRCODPLL:
  #if defined(CMU_CLKEN0_HFRCO0)
        CMU->CLKEN0_SET = CMU_CLKEN0_HFRCO0;
  #endif
        EFM_ASSERT(val <= (_HFRCO_CAL_TUNING_MASK >> _HFRCO_CAL_TUNING_SHIFT));
        val &= _HFRCO_CAL_TUNING_MASK >> _HFRCO_CAL_TUNING_SHIFT;
        while ((HFRCO0->STATUS & HFRCO_STATUS_SYNCBUSY) != 0UL) {
        }
        HFRCO0->CAL = (HFRCO0->CAL & ~_HFRCO_CAL_TUNING_MASK)
                      | (val << _HFRCO_CAL_TUNING_SHIFT);
        break;
  
  #if defined(HFRCOEM23_PRESENT)
      case cmuOsc_HFRCOEM23:
        EFM_ASSERT(val <= (_HFRCO_CAL_TUNING_MASK >> _HFRCO_CAL_TUNING_SHIFT));
        val &= _HFRCO_CAL_TUNING_MASK >> _HFRCO_CAL_TUNING_SHIFT;
        while ((HFRCOEM23->STATUS & HFRCO_STATUS_SYNCBUSY) != 0UL) {
        }
        HFRCOEM23->CAL = (HFRCOEM23->CAL & ~_HFRCO_CAL_TUNING_MASK)
                         | (val << _HFRCO_CAL_TUNING_SHIFT);
        break;
  #endif
  
      default:
        EFM_ASSERT(false);
        break;
    }
  }
  
  /***************************************************************************//**
   * @brief
   *   Configure wait state settings necessary to switch to a given core clock
   *   frequency at a certain voltage scale level.
   *
   * @details
   *   This function will set up the necessary flash 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] freq
   *   The core clock frequency to configure wait-states.
   *
   * @param[in] vscale
   *   The voltage scale to configure wait-states. Expected values are
   *   0 or 1, higher number is lower voltage.
   *   @li 0 = 1.1 V (VSCALE2)
   *   @li 1 = 1.0 V (VSCALE1)
   ******************************************************************************/
  void CMU_UpdateWaitStates(uint32_t freq, int vscale)
  {
    if (vscale > 0) {
      flashWaitStateControl(freq, VSCALE_EM01_LOW_POWER);
    } else {
      flashWaitStateControl(freq, VSCALE_EM01_HIGH_PERFORMANCE);
    }
  }
  
  /*******************************************************************************
   **************************   LOCAL FUNCTIONS   ********************************
   ******************************************************************************/
  
  /***************************************************************************//**
   * @brief
   *   Get selected oscillator and frequency for @ref cmuClock_DPLLREFCLK
   *   clock tree.
   *
   * @param[out] freq
   *   The frequency.
   *
   * @param[out] sel
   *   The selected oscillator.
   ******************************************************************************/
  static void dpllRefClkGet(uint32_t *freq, CMU_Select_TypeDef *sel)
  {
    uint32_t f = 0U;
    CMU_Select_TypeDef s;
  
    switch (CMU->DPLLREFCLKCTRL & _CMU_DPLLREFCLKCTRL_CLKSEL_MASK) {
      case _CMU_DPLLREFCLKCTRL_CLKSEL_HFXO:
        f = SystemHFXOClockGet();
        s = cmuSelect_HFXO;
        break;
  
      case _CMU_DPLLREFCLKCTRL_CLKSEL_LFXO:
        f = SystemLFXOClockGet();
        s = cmuSelect_LFXO;
        break;
  
      case _CMU_DPLLREFCLKCTRL_CLKSEL_CLKIN0:
        f = SystemCLKIN0Get();
        s = cmuSelect_CLKIN0;
        break;
  
      case _CMU_DPLLREFCLKCTRL_CLKSEL_DISABLED:
        s = cmuSelect_Disabled;
        break;
  
      default:
        s = cmuSelect_Error;
        EFM_ASSERT(false);
        break;
    }
  
    if (freq != NULL) {
      *freq = f;
    }
    if (sel != NULL) {
      *sel = s;
    }
  }
  
  /***************************************************************************//**
   * @brief
   *   Get selected oscillator and frequency for @ref cmuClock_EM01GRPACLK
   *   clock tree.
   *
   * @param[out] freq
   *   The frequency.
   *
   * @param[out] sel
   *   The selected oscillator.
   ******************************************************************************/
  static void em01GrpaClkGet(uint32_t *freq, CMU_Select_TypeDef *sel)
  {
    uint32_t f = 0U;
    CMU_Select_TypeDef s;
  
    switch (CMU->EM01GRPACLKCTRL & _CMU_EM01GRPACLKCTRL_CLKSEL_MASK) {
      case _CMU_EM01GRPACLKCTRL_CLKSEL_HFRCODPLL:
        f = SystemHFRCODPLLClockGet();
        s = cmuSelect_HFRCODPLL;
        break;
  
      case _CMU_EM01GRPACLKCTRL_CLKSEL_HFXO:
        f = SystemHFXOClockGet();
        s = cmuSelect_HFXO;
        break;
  
  #if defined(HFRCOEM23_PRESENT)
      case _CMU_EM01GRPACLKCTRL_CLKSEL_HFRCOEM23:
        f = SystemHFRCOEM23ClockGet();
        s = cmuSelect_HFRCOEM23;
        break;
  #endif
  
      case _CMU_EM01GRPACLKCTRL_CLKSEL_FSRCO:
        f = SystemFSRCOClockGet();
        s = cmuSelect_FSRCO;
        break;
  
      default:
        s = cmuSelect_Error;
        EFM_ASSERT(false);
        break;
    }
  
    if (freq != NULL) {
      *freq = f;
    }
    if (sel != NULL) {
      *sel = s;
    }
  }
  
  /***************************************************************************//**
   * @brief
   *   Get selected oscillator and frequency for @ref cmuClock_EM23GRPACLK
   *   clock tree.
   *
   * @param[out] freq
   *   The frequency.
   *
   * @param[out] sel
   *   The selected oscillator.
   ******************************************************************************/
  static void em23GrpaClkGet(uint32_t *freq, CMU_Select_TypeDef *sel)
  {
    uint32_t f = 0U;
    CMU_Select_TypeDef s;
  
    switch (CMU->EM23GRPACLKCTRL & _CMU_EM23GRPACLKCTRL_CLKSEL_MASK) {
      case _CMU_EM23GRPACLKCTRL_CLKSEL_LFRCO:
        f = SystemLFRCOClockGet();
        s = cmuSelect_LFRCO;
        break;
  
      case _CMU_EM23GRPACLKCTRL_CLKSEL_LFXO:
        f = SystemLFXOClockGet();
        s = cmuSelect_LFXO;
        break;
  
      case _CMU_EM23GRPACLKCTRL_CLKSEL_ULFRCO:
        f = SystemULFRCOClockGet();
        s = cmuSelect_ULFRCO;
        break;
  
      default:
        s = cmuSelect_Error;
        EFM_ASSERT(false);
        break;
    }
  
    if (freq != NULL) {
      *freq = f;
    }
    if (sel != NULL) {
      *sel = s;
    }
  }
  
  /***************************************************************************//**
   * @brief
   *   Get selected oscillator and frequency for @ref cmuClock_EM4GRPACLK
   *   clock tree.
   *
   * @param[out] freq
   *   The frequency.
   *
   * @param[out] sel
   *   The selected oscillator.
   ******************************************************************************/
  static void em4GrpaClkGet(uint32_t *freq, CMU_Select_TypeDef *sel)
  {
    uint32_t f = 0U;
    CMU_Select_TypeDef s;
  
    switch (CMU->EM4GRPACLKCTRL & _CMU_EM4GRPACLKCTRL_CLKSEL_MASK) {
      case _CMU_EM4GRPACLKCTRL_CLKSEL_LFRCO:
        f = SystemLFRCOClockGet();
        s = cmuSelect_LFRCO;
        break;
  
      case _CMU_EM4GRPACLKCTRL_CLKSEL_LFXO:
        f = SystemLFXOClockGet();
        s = cmuSelect_LFXO;
        break;
  
      case _CMU_EM4GRPACLKCTRL_CLKSEL_ULFRCO:
        f = SystemULFRCOClockGet();
        s = cmuSelect_ULFRCO;
        break;
  
      default:
        s = cmuSelect_Error;
        EFM_ASSERT(false);
        break;
    }
  
    if (freq != NULL) {
      *freq = f;
    }
    if (sel != NULL) {
      *sel = s;
    }
  }
  
  /***************************************************************************//**
   * @brief
   *   Configure flash access wait states to support the given core clock
   *   frequency and vscale level.
   *
   * @note Current implementation sets wait states depending on frequency only.
   *   This assumes that applications running on Series 2 Config 2 devices
   *   never attemtps to set core frequency above 40MHz at VSCALE1 (1.0V).
   *   Series 2 Config 1 devices does not support vscale.
   *
   * @param[in] coreFreq
   *   The core clock frequency to configure flash wait-states.
   *
   * @param[in] vscale
   *   Voltage Scale level. Supported levels are 0 and 1 where 0 is the default.
   *   @li 0 = 1.1 V (VSCALE2)
   *   @li 1 = 1.0 V (VSCALE1)
   ******************************************************************************/
  static void flashWaitStateControl(uint32_t coreFreq, int vscale)
  {
    (void)vscale;
    uint32_t mode;
    bool mscLocked;
  
  #if defined(CMU_CLKEN1_MSC)
    CMU->CLKEN1_SET = CMU_CLKEN1_MSC;
  #endif
  
  #if defined(_SILICON_LABS_32B_SERIES_2_CONFIG_2)
    coreFreq *= CMU_ClockDivGet(cmuClock_CORE);
  #endif
  
    // Make sure the MSC is unlocked
    mscLocked = (MSC->STATUS & _MSC_STATUS_REGLOCK_MASK)
                == MSC_STATUS_REGLOCK_LOCKED;
    MSC->LOCK = MSC_LOCK_LOCKKEY_UNLOCK;
  
    // Get current flash read setting
    mode = MSC->READCTRL & ~_MSC_READCTRL_MODE_MASK;
  
    // Set new mode based on the core clock frequency
  #if defined(_SILICON_LABS_32B_SERIES_2_CONFIG_1)
    if (coreFreq <= CMU_MAX_FLASHREAD_FREQ_0WS) {
  #else // defined(_SILICON_LABS_32B_SERIES_2_CONFIG_2)
    if ((coreFreq <= CMU_MAX_FREQ_0WS_1V1)
        || (vscale >= 1)) {
  #endif
      mode |= MSC_READCTRL_MODE_WS0;
    } else {
      mode |= MSC_READCTRL_MODE_WS1;
    }
    MSC->READCTRL = mode;
  
  #if defined(_SILICON_LABS_32B_SERIES_2_CONFIG_1)
    // Get current sram read setting
    mode = SYSCFG->DMEM0RAMCTRL & ~_SYSCFG_DMEM0RAMCTRL_RAMWSEN_MASK;
  
    // Set new mode based on the core clock frequency
    if (coreFreq > CMU_MAX_SRAM_FREQ_0WS) {
      mode |= 1 << _SYSCFG_DMEM0RAMCTRL_RAMWSEN_SHIFT;
    }
    SYSCFG->DMEM0RAMCTRL = mode;
  #endif
  
    if (mscLocked) {
      MSC->LOCK = MSC_LOCK_LOCKKEY_LOCK;
    }
  }
  
  /***************************************************************************//**
   * @brief
   *   Get calibrated HFRCODPLL tuning value from Device information (DI) page
   *   for a given frequency. Calibration value is not available for all frequency
   *   bands.
   *
   * @param[in] freq
   *   HFRCODPLL frequency band
   ******************************************************************************/
  static uint32_t HFRCODPLLDevinfoGet(CMU_HFRCODPLLFreq_TypeDef freq)
  {
    uint32_t ret = 0U;
  
    switch (freq) {
      // 1, 2 and 4MHz share the same calibration word
      case cmuHFRCODPLLFreq_1M0Hz:
      case cmuHFRCODPLLFreq_2M0Hz:
      case cmuHFRCODPLLFreq_4M0Hz:
        ret = DEVINFO->HFRCODPLLCAL[0].HFRCODPLLCAL;
        break;
  
      case cmuHFRCODPLLFreq_7M0Hz:
        ret = DEVINFO->HFRCODPLLCAL[3].HFRCODPLLCAL;
        break;
  
      case cmuHFRCODPLLFreq_13M0Hz:
        ret = DEVINFO->HFRCODPLLCAL[6].HFRCODPLLCAL;
        break;
  
      case cmuHFRCODPLLFreq_16M0Hz:
        ret = DEVINFO->HFRCODPLLCAL[7].HFRCODPLLCAL;
        break;
  
      case cmuHFRCODPLLFreq_19M0Hz:
        ret = DEVINFO->HFRCODPLLCAL[8].HFRCODPLLCAL;
        break;
  
      case cmuHFRCODPLLFreq_26M0Hz:
        ret = DEVINFO->HFRCODPLLCAL[10].HFRCODPLLCAL;
        break;
  
      case cmuHFRCODPLLFreq_32M0Hz:
        ret = DEVINFO->HFRCODPLLCAL[11].HFRCODPLLCAL;
        break;
  
      case cmuHFRCODPLLFreq_38M0Hz:
        ret = DEVINFO->HFRCODPLLCAL[12].HFRCODPLLCAL;
        break;
  
      case cmuHFRCODPLLFreq_48M0Hz:
        ret = DEVINFO->HFRCODPLLCAL[13].HFRCODPLLCAL;
        break;
  
      case cmuHFRCODPLLFreq_56M0Hz:
        ret = DEVINFO->HFRCODPLLCAL[14].HFRCODPLLCAL;
        break;
  
      case cmuHFRCODPLLFreq_64M0Hz:
        ret = DEVINFO->HFRCODPLLCAL[15].HFRCODPLLCAL;
        break;
  
      case cmuHFRCODPLLFreq_80M0Hz:
        ret = DEVINFO->HFRCODPLLCAL[16].HFRCODPLLCAL;
        break;
  
      case cmuHFRCODPLLFreq_UserDefined:
        break;
  
      default:
        EFM_ASSERT(false);
        break;
    }
    return ret;
  }
  
  /***************************************************************************//**
   * @brief
   *   Get selected oscillator and frequency for @ref cmuClock_IADCCLK
   *   clock tree.
   *
   * @param[out] freq
   *   The frequency.
   *
   * @param[out] sel
   *   The selected oscillator.
   ******************************************************************************/
  static void iadcClkGet(uint32_t *freq, CMU_Select_TypeDef *sel)
  {
    uint32_t f = 0U;
    CMU_Select_TypeDef s;
  
    switch (CMU->IADCCLKCTRL & _CMU_IADCCLKCTRL_CLKSEL_MASK) {
      case _CMU_IADCCLKCTRL_CLKSEL_EM01GRPACLK:
        em01GrpaClkGet(&f, NULL);
        s = cmuSelect_EM01GRPACLK;
        break;
  
  #if defined(HFRCOEM23_PRESENT)
      case _CMU_IADCCLKCTRL_CLKSEL_HFRCOEM23:
        f = SystemHFRCOEM23ClockGet();
        s = cmuSelect_HFRCOEM23;
        break;
  #endif
  
      case _CMU_IADCCLKCTRL_CLKSEL_FSRCO:
        f = SystemFSRCOClockGet();
        s = cmuSelect_FSRCO;
        break;
  
      default:
        s = cmuSelect_Error;
        EFM_ASSERT(false);
        break;
    }
  
    if (freq != NULL) {
      *freq = f;
    }
    if (sel != NULL) {
      *sel = s;
    }
  }
  
  /***************************************************************************//**
   * @brief
   *   Set maximum allowed divisor for @ref cmuClock_PCLK clock tree.
   ******************************************************************************/
  static void pclkDivMax(void)
  {
    // Set largest divisor for PCLK clock tree.
    CMU_ClockDivSet(cmuClock_PCLK, 2U);
  }
  
  /***************************************************************************//**
   * @brief
   *   Set @ref cmuClock_PCLK clock tree divisor to achieve highest possible
   *  frequency and still be within spec.
   ******************************************************************************/
  static void pclkDivOptimize(void)
  {
    CMU_ClkDiv_TypeDef div = 2U;
  
    if (CMU_ClockFreqGet(cmuClock_HCLK) <= CMU_MAX_PCLK_FREQ) {
      div = 1U;
    }
    CMU_ClockDivSet(cmuClock_PCLK, div);
  }
  
  /***************************************************************************//**
   * @brief
   *   Get selected oscillator and frequency for @ref cmuClock_RTCCCLK
   *   clock tree.
   *
   * @param[out] freq
   *   The frequency.
   *
   * @param[out] sel
   *   The selected oscillator.
   ******************************************************************************/
  static void rtccClkGet(uint32_t *freq, CMU_Select_TypeDef *sel)
  {
    uint32_t f = 0U;
    CMU_Select_TypeDef s;
  
    switch (CMU->RTCCCLKCTRL & _CMU_RTCCCLKCTRL_CLKSEL_MASK) {
      case _CMU_RTCCCLKCTRL_CLKSEL_LFRCO:
        f = SystemLFRCOClockGet();
        s = cmuSelect_LFRCO;
        break;
  
      case _CMU_RTCCCLKCTRL_CLKSEL_LFXO:
        f = SystemLFXOClockGet();
        s = cmuSelect_LFXO;
        break;
  
      case _CMU_RTCCCLKCTRL_CLKSEL_ULFRCO:
        f = SystemULFRCOClockGet();
        s = cmuSelect_ULFRCO;
        break;
  
      default:
        s = cmuSelect_Error;
        EFM_ASSERT(false);
        break;
    }
  
    if (freq != NULL) {
      *freq = f;
    }
    if (sel != NULL) {
      *sel = s;
    }
  }
  
  /***************************************************************************//**
   * @brief
   *   Set wait-states to values valid for maximum allowable core clock frequency.
   ******************************************************************************/
  static void waitStateMax(void)
  {
    flashWaitStateControl(SystemMaxCoreClockGet(), 0);
  }
  
  /***************************************************************************//**
   * @brief
   *   Get selected oscillator and frequency for @ref cmuClock_WDOG0CLK
   *   clock tree.
   *
   * @param[out] freq
   *   The frequency.
   *
   * @param[out] sel
   *   The selected oscillator.
   ******************************************************************************/
  static void wdog0ClkGet(uint32_t *freq, CMU_Select_TypeDef *sel)
  {
    uint32_t f = 0U;
    CMU_Select_TypeDef s;
  
    switch (CMU->WDOG0CLKCTRL & _CMU_WDOG0CLKCTRL_CLKSEL_MASK) {
      case _CMU_WDOG0CLKCTRL_CLKSEL_LFRCO:
        f = SystemLFRCOClockGet();
        s = cmuSelect_LFRCO;
        break;
  
      case _CMU_WDOG0CLKCTRL_CLKSEL_LFXO:
        f = SystemLFXOClockGet();
        s = cmuSelect_LFXO;
        break;
  
      case _CMU_WDOG0CLKCTRL_CLKSEL_ULFRCO:
        f = SystemULFRCOClockGet();
        s = cmuSelect_ULFRCO;
        break;
  
      case _CMU_WDOG0CLKCTRL_CLKSEL_HCLKDIV1024:
        f = SystemHCLKGet() / 1024U;
        s = cmuSelect_HCLKDIV1024;
        break;
  
      default:
        s = cmuSelect_Error;
        EFM_ASSERT(false);
        break;
    }
  
    if (freq != NULL) {
      *freq = f;
    }
    if (sel != NULL) {
      *sel = s;
    }
  }
  
  #else   // defined(_SILICON_LABS_32B_SERIES_2)
  
  /*******************************************************************************
   ******************************   DEFINES   ************************************
   ******************************************************************************/
  
  /** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
  
  #if defined(_SILICON_LABS_32B_SERIES_0)
  /** The maximum allowed core frequency when using 0 wait-states on flash access. */
  #define CMU_MAX_FREQ_0WS        16000000
  /** The maximum allowed core frequency when using 1 wait-states on flash access */
  #define CMU_MAX_FREQ_1WS        32000000
  
  #elif (_SILICON_LABS_GECKO_INTERNAL_SDID == 80)
  // EFR32xG1x and EFM32xG1x
  #define CMU_MAX_FREQ_0WS_1V2    25000000
  #define CMU_MAX_FREQ_1WS_1V2    40000000
  
  #elif (_SILICON_LABS_GECKO_INTERNAL_SDID == 84)
  // EFR32xG12x and EFM32xG12x
  #define CMU_MAX_FREQ_0WS_1V2    25000000
  #define CMU_MAX_FREQ_1WS_1V2    40000000
  #define CMU_MAX_FREQ_0WS_1V1    21330000
  #define CMU_MAX_FREQ_1WS_1V1    32000000
  #define CMU_MAX_FREQ_0WS_1V0     7000000
  #define CMU_MAX_FREQ_1WS_1V0    14000000
  #define CMU_MAX_FREQ_2WS_1V0    20000000
  
  #elif (_SILICON_LABS_GECKO_INTERNAL_SDID == 89)
  // EFR32xG13x and EFM32xG13x
  #define CMU_MAX_FREQ_0WS_1V2    25000000
  #define CMU_MAX_FREQ_1WS_1V2    40000000
  #define CMU_MAX_FREQ_0WS_1V0     7000000
  #define CMU_MAX_FREQ_1WS_1V0    14000000
  #define CMU_MAX_FREQ_2WS_1V0    20000000
  
  #elif (_SILICON_LABS_GECKO_INTERNAL_SDID == 95)
  // EFR32xG14x and EFM32xG14x
  #define CMU_MAX_FREQ_0WS_1V2    25000000
  #define CMU_MAX_FREQ_1WS_1V2    40000000
  #define CMU_MAX_FREQ_0WS_1V0     7000000
  #define CMU_MAX_FREQ_1WS_1V0    14000000
  #define CMU_MAX_FREQ_2WS_1V0    20000000
  
  #elif (_SILICON_LABS_GECKO_INTERNAL_SDID == 100)
  // EFM32GG11x
  #define CMU_MAX_FREQ_0WS_1V2    18000000
  #define CMU_MAX_FREQ_1WS_1V2    36000000
  #define CMU_MAX_FREQ_2WS_1V2    54000000
  #define CMU_MAX_FREQ_3WS_1V2    72000000
  #define CMU_MAX_FREQ_0WS_1V0     7000000
  #define CMU_MAX_FREQ_1WS_1V0    14000000
  #define CMU_MAX_FREQ_2WS_1V0    20000000
  
  #elif (_SILICON_LABS_GECKO_INTERNAL_SDID == 103)
  // EFM32TG11x
  #define CMU_MAX_FREQ_0WS_1V2    25000000
  #define CMU_MAX_FREQ_1WS_1V2    48000000
  #define CMU_MAX_FREQ_0WS_1V0    10000000
  #define CMU_MAX_FREQ_1WS_1V0    21000000
  #define CMU_MAX_FREQ_2WS_1V0    20000000
  
  #elif (_SILICON_LABS_GECKO_INTERNAL_SDID == 106)
  // EFM32GG12x
  #define CMU_MAX_FREQ_0WS_1V2    18000000
  #define CMU_MAX_FREQ_1WS_1V2    36000000
  #define CMU_MAX_FREQ_2WS_1V2    54000000
  #define CMU_MAX_FREQ_3WS_1V2    72000000
  #define CMU_MAX_FREQ_0WS_1V0    7000000
  #define CMU_MAX_FREQ_1WS_1V0    14000000
  #define CMU_MAX_FREQ_2WS_1V0    20000000
  
  #else
  #error "Max Flash wait-state frequencies are not defined for this platform."
  #endif
  
  /** The maximum frequency for the HFLE interface. */
  #if defined(CMU_CTRL_HFLE)
  /** The 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                       24000000UL
  /** The maximum HFLE frequency for other series 0 parts with maximum core clock
      higher than 32 MHz. */
  #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)
  /** The maximum HFLE frequency for series 1 parts. */
  #define CMU_MAX_FREQ_HFLE                       32000000UL
  #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    ((int)EMU_VScaleGet())
  #else
  #define VSCALE_DEFAULT    VSCALE_EM01_HIGH_PERFORMANCE
  #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)
  /** A table of HFRCOCTRL values and their associated minimum/maximum frequencies and
      an 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
    {  860000UL, 1050000UL, 0xBC601F00UL, cmuHFRCOFreq_1M0Hz       },
    { 1050000UL, 1280000UL, 0xBC611F35UL, (CMU_HFRCOFreq_TypeDef)0 },
    { 1280000UL, 1480000UL, 0xBCA21F35UL, (CMU_HFRCOFreq_TypeDef)0 },
    { 1480000UL, 1800000UL, 0xAD231F35UL, (CMU_HFRCOFreq_TypeDef)0 },
    { 1800000UL, 2110000UL, 0xBA601F00UL, cmuHFRCOFreq_2M0Hz       },
    { 2110000UL, 2560000UL, 0xBA611F35UL, (CMU_HFRCOFreq_TypeDef)0 },
    { 2560000UL, 2970000UL, 0xBAA21F35UL, (CMU_HFRCOFreq_TypeDef)0 },
    { 2970000UL, 3600000UL, 0xAB231F35UL, (CMU_HFRCOFreq_TypeDef)0 },
    { 3600000UL, 4220000UL, 0xB8601F00UL, cmuHFRCOFreq_4M0Hz       },
    { 4220000UL, 5120000UL, 0xB8611F35UL, (CMU_HFRCOFreq_TypeDef)0 },
    { 5120000UL, 5930000UL, 0xB8A21F35UL, (CMU_HFRCOFreq_TypeDef)0 },
    { 5930000UL, 7520000UL, 0xA9231F00UL, cmuHFRCOFreq_7M0Hz       },
    { 7520000UL, 9520000UL, 0x99241F35UL, (CMU_HFRCOFreq_TypeDef)0 },
    { 9520000UL, 11800000UL, 0x99251F35UL, (CMU_HFRCOFreq_TypeDef)0 },
    { 11800000UL, 14400000UL, 0x99261F00UL, cmuHFRCOFreq_13M0Hz     },
    { 14400000UL, 17200000UL, 0x99271F00UL, cmuHFRCOFreq_16M0Hz     },
    { 17200000UL, 19700000UL, 0x99481F00UL, cmuHFRCOFreq_19M0Hz     },
    { 19700000UL, 23800000UL, 0x99491F35UL, (CMU_HFRCOFreq_TypeDef)0 },
    { 23800000UL, 28700000UL, 0x994A1F00UL, cmuHFRCOFreq_26M0Hz      },
    { 28700000UL, 34800000UL, 0x996B1F00UL, cmuHFRCOFreq_32M0Hz      },
  #if defined(_SILICON_LABS_GECKO_INTERNAL_SDID_84)  \
    || defined(_SILICON_LABS_GECKO_INTERNAL_SDID_89) \
    || defined(_SILICON_LABS_GECKO_INTERNAL_SDID_95)
    { 34800000UL, 40000000UL, 0x996C1F00UL, cmuHFRCOFreq_38M0Hz      }
  #elif defined(_SILICON_LABS_GECKO_INTERNAL_SDID_100) \
    || defined(_SILICON_LABS_GECKO_INTERNAL_SDID_106)
    { 34800000UL, 42800000UL, 0x996C1F00UL, cmuHFRCOFreq_38M0Hz      },
    { 42800000UL, 51600000UL, 0x996D1F00UL, cmuHFRCOFreq_48M0Hz      },
    { 51600000UL, 60500000UL, 0x998E1F00UL, cmuHFRCOFreq_56M0Hz      },
    { 60500000UL, 72000000UL, 0xA98F1F00UL, cmuHFRCOFreq_64M0Hz      }
  #elif defined(_SILICON_LABS_GECKO_INTERNAL_SDID_103)
    { 34800000UL, 42800000UL, 0x996C1F00UL, cmuHFRCOFreq_38M0Hz      },
    { 42800000UL, 48000000UL, 0x996D1F00UL, cmuHFRCOFreq_48M0Hz      }
  #else
    #error "HFRCOCTRL values not set for this platform."
  #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 || _SILICON_LABS_GECKO_INTERNAL_SDID == 106)
    { CMU_MAX_FREQ_0WS_1V2, 0, 0 },  /* 0 wait states at max frequency 18 MHz and 1.2V */
    { CMU_MAX_FREQ_1WS_1V2, 0, 1 },  /* 1 wait states at max frequency 36 MHz and 1.2V */
    { CMU_MAX_FREQ_2WS_1V2, 0, 2 },  /* 2 wait states at max frequency 54 MHz and 1.2V */
    { CMU_MAX_FREQ_3WS_1V2, 0, 3 },  /* 3 wait states at max frequency 72 MHz and 1.2V */
    { CMU_MAX_FREQ_0WS_1V0, 2, 0 },  /* 0 wait states at max frequency 7 MHz and 1.0V */
    { CMU_MAX_FREQ_1WS_1V0, 2, 1 },  /* 1 wait states at max frequency 14 MHz and 1.0V */
    { CMU_MAX_FREQ_2WS_1V0, 2, 2 },  /* 2 wait states at max frequency 21 MHz and 1.0V */
  #else
    { CMU_MAX_FREQ_0WS_1V2, 0, 0 },  /* 0 wait states at 1.2V */
    { CMU_MAX_FREQ_1WS_1V2, 0, 1 },  /* 1 wait states at 1.2V */
    { CMU_MAX_FREQ_0WS_1V0, 2, 0 },  /* 0 wait states at 1.0V */
    { CMU_MAX_FREQ_1WS_1V0, 2, 1 },  /* 1 wait states at 1.0V */
    { CMU_MAX_FREQ_2WS_1V0, 2, 2 },  /* 2 wait states at 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
  
  static void hfperClkSafePrescaler(void);
  static void hfperClkOptimizedPrescaler(void);
  
  /** @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 maximum 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 the 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 according to wanted HF clock.
   *
   * @param[in] hfFreq
   *   The HF clock frequency we want to use.
   *   This is:
   *     CORE clock on Series0 devices.
   *     HF clock on on Series1 devices.
   ******************************************************************************/
  static void setHfLeConfig(uint32_t hfFreq)
  {
    unsigned int hfleWs;
    uint32_t hflePresc;
  
    /* Check for 1 bit fields. @ref 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) == 0x1U);
  
    /* - Enable HFLE wait-state 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 <= (2UL * 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
   *   The 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 a debug clock.
   *
   * @return
   *   AUX Frequency in Hz.
   ******************************************************************************/
  static uint32_t auxClkGet(void)
  {
    uint32_t ret;
  
  #if defined(_CMU_AUXHFRCOCTRL_FREQRANGE_MASK)
    ret = (uint32_t)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:
        ret = 0;
        EFM_ASSERT(false);
        break;
    }
  
  #else
    /* Gecko has a fixed 14 MHz 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:
        ret = 0;
        EFM_ASSERT(false);
        break;
    }
    return ret;
  }
  
  #if defined(_CMU_ADCCTRL_ADC0CLKSEL_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 the 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(false);
        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:
        ret = 0;
        EFM_ASSERT(false);
        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 the 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:
        ret = 0;
        EFM_ASSERT(false);
        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 the selected clock source. */
    switch (qspi) {
      case 0:
        clk = CMU_ClockSelectGet(cmuClock_QSPI0REF);
        break;
  
      default:
        EFM_ASSERT(false);
        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:
        ret = 0;
        EFM_ASSERT(false);
        break;
    }
    return ret;
  }
  #endif
  
  #if defined(_CMU_PDMCTRL_MASK)
  /***************************************************************************//**
   * @brief
   *   Get the PDM reference clock frequency.
   *
   * @return
   *   PDM reference clock frequency in Hz.
   ******************************************************************************/
  static uint32_t pdmRefClkGet(void)
  {
    uint32_t ret;
    CMU_Select_TypeDef clk;
  
    /* Get the selected clock source. */
    clk = CMU_ClockSelectGet(cmuClock_PDMREF);
  
    switch (clk) {
      case cmuSelect_HFRCO:
        ret = SystemHfrcoFreq;
        break;
  
      case cmuSelect_HFXO:
        ret = SystemHFXOClockGet();
        break;
  
      case cmuSelect_USHFRCO:
        ret = ushfrcoFreq;
        break;
  
      default:
        ret = 0;
        EFM_ASSERT(false);
        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:
        ret = 0;
        EFM_ASSERT(false);
        break;
    }
    return ret;
  }
  #endif
  
  /***************************************************************************//**
   * @brief
   *   Configure flash access wait states to support the given core clock
   *   frequency.
   *
   * @param[in] coreFreq
   *   The core clock frequency to configure flash wait-states.
   *
   * @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;
  #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. */
  
    /* Get mode and SCBTP enable. */
    mode = MSC->READCTRL & _MSC_READCTRL_MODE_MASK;
  
  #if defined(_SILICON_LABS_32B_SERIES_0)
  #if defined(MSC_READCTRL_MODE_WS0SCBTP)
    /* Devices with MODE and SCBTP in the 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);
    }
  #else /* defined(MSC_READCTRL_MODE_WS0SCBTP) */
  
    if (coreFreq <= CMU_MAX_FREQ_0WS) {
      mode = 0;
    } else if (coreFreq <= CMU_MAX_FREQ_1WS) {
      mode = 1;
    }
  #endif /* defined(MSC_READCTRL_MODE_WS0SCBTP) */
  // End defined(_SILICON_LABS_32B_SERIES_0)
  
  #elif defined(_SILICON_LABS_32B_SERIES_1)
  #if defined(_EMU_STATUS_VSCALE_MASK)
  
    /* These devices have specific requirements on the supported flash wait state
     * depending on the frequency and voltage scale level. */
    uint32_t i;
    for (i = 0; i < FLASH_WS_TABLE_ENTRIES; i++) {
      if ((flashWsTable[i].vscale == (uint8_t)vscale)
          && (coreFreq <= flashWsTable[i].maxFreq)) {
        break; // Found a matching entry.
      }
    }
  
    if (i == FLASH_WS_TABLE_ENTRIES) {
      mode = 3; // Worst case flash wait state for unsupported cases.
      EFM_ASSERT(false);
    } 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 the flash wait state configuration. */
    if (coreFreq <= CMU_MAX_FREQ_0WS_1V2) {
      mode = 0;
    } else if (coreFreq <= CMU_MAX_FREQ_1WS_1V2) {
      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
  // End defined(_SILICON_LABS_32B_SERIES_1)
  
  #else
  #error "Undefined 32B SERIES!"
  #endif
  
    /* BUS_RegMaskedWrite cannot be used as it would temporarily set the
       mode field to WS0. */
    MSC->READCTRL = (MSC->READCTRL & ~_MSC_READCTRL_MODE_MASK) | mode;
  }
  
  /***************************************************************************//**
   * @brief
   *   Configure flash access wait states to the most conservative setting for
   *   this target. Retain SCBTP (Suppressed Conditional Branch Target Prefetch)
   *   setting.
   ******************************************************************************/
  static void flashWaitStateMax(void)
  {
    /* Make sure the MSC is unlocked */
    bool mscLocked = MSC->LOCK != 0UL;
    MSC->LOCK = MSC_UNLOCK_CODE;
  
    flashWaitStateControl(SystemMaxCoreClockGet(), 0);
  
    if (mscLocked) {
      MSC->LOCK = 0;
    }
  }
  
  #if defined(_MSC_RAMCTRL_RAMWSEN_MASK)
  /***************************************************************************//**
   * @brief
   *   Configure RAM access wait states to support the given core clock
   *   frequency.
   *
   * @param[in] coreFreq
   *   The core clock frequency to configure RAM wait-states.
   *
   * @param[in] vscale
   *   A 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 the wait state for peripheral accesses over the bus to support
   *   the given bus clock frequency.
   *
   * @param[in] busFreq
   *   A peripheral bus clock frequency to configure wait-states.
   *
   * @param[in] vscale
   *   The voltage scale to configure wait-states. 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
  
  #if defined(PLFRCO_PRESENT)
  static bool deviceHasPlfrco(void)
  {
    SYSTEM_ChipRevision_TypeDef rev;
  
    if (_SILICON_LABS_GECKO_INTERNAL_SDID == 89) {
      // check the xG13 rev and check if it's >= Rev A3
      SYSTEM_ChipRevisionGet(&rev);
      return (rev.major > 1) || (rev.minor >= 3);
    } else {
      return false;
    }
  }
  #endif
  
  /***************************************************************************//**
   * @brief
   *   Configure various wait states to switch to a certain frequency
   *   and a certain voltage scale.
   *
   * @details
   *   This function will set up 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
   *   The core clock frequency to configure wait-states.
   *
   * @param[in] vscale
   *   The voltage scale to configure wait-states. 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)
  {
    /* Make sure the MSC is unlocked */
    bool mscLocked = MSC->LOCK != 0UL;
    MSC->LOCK = MSC_UNLOCK_CODE;
  
    flashWaitStateControl(freq, vscale);
  #if defined(_MSC_RAMCTRL_RAMWSEN_MASK)
    setRamWaitState(freq, vscale);
  #endif
  #if defined(_MSC_CTRL_WAITMODE_MASK)
    setBusWaitState(freq, vscale);
  #endif
  
    if (mscLocked) {
      MSC->LOCK = 0;
    }
  }
  
  #if defined(_CMU_HFXOSTEADYSTATECTRL_REGISHUPPER_MASK)
  /***************************************************************************//**
   * @brief
   *   Return the 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 the register description for CMU_HFXOSTEADYSTATECTRL_REGISHUPPER. */
    regIshUpper = SL_MIN(steadyStateRegIsh + 3UL, 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)
           >> _CMU_HFXOCTRL_PEAKDETSHUNTOPTMODE_SHIFT;
  #else
    return (CMU->HFXOCTRL & _CMU_HFXOCTRL_PEAKDETMODE_MASK)
           >> _CMU_HFXOCTRL_PEAKDETMODE_SHIFT;
  #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 << _CMU_HFXOCTRL_PEAKDETSHUNTOPTMODE_SHIFT);
  #else
    CMU->HFXOCTRL = (CMU->HFXOCTRL & ~_CMU_HFXOCTRL_PEAKDETMODE_MASK)
                    | (mode << _CMU_HFXOCTRL_PEAKDETMODE_SHIFT);
  #endif
  }
  #endif
  
  /***************************************************************************//**
   * @brief
   *   Get the LFnCLK frequency based on the 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(false);
        break;
    }
  
    sel = (uint32_t)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(false);
  #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(false);
  #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(false);
  #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(false);
        break;
    }
  
    /* Get the 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:
        ret = 0U;
        EFM_ASSERT(false);
        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(PLFRCO_PRESENT)
      case _CMU_LFACLKSEL_LFA_PLFRCO:
        ret = SystemLFRCOClockGet();
        break;
  #endif
  
  #if defined(_CMU_LFBCLKSEL_LFB_HFCLKLE)
      case _CMU_LFBCLKSEL_LFB_HFCLKLE:
        ret = SystemHFClockGet()
              / SL_Log2ToDiv(((CMU->HFPRESC & _CMU_HFPRESC_HFCLKLEPRESC_MASK)
                              >> _CMU_HFPRESC_HFCLKLEPRESC_SHIFT) + 1UL);
        break;
  #endif
  
      case _CMU_LFACLKSEL_LFA_DISABLED:
        ret = 0;
        break;
  
      default:
        ret = 0U;
        EFM_ASSERT(false);
        break;
    }
  #endif
  
    return ret;
  }
  
  /***************************************************************************//**
   * @brief
   *   Wait for an ongoing sync of register(s) to low-frequency domain to complete.
   *
   * @param[in] mask
   *   A bitmask corresponding to SYNCBUSY register defined bits, indicating
   *   registers that must complete any ongoing synchronization.
   ******************************************************************************/
  __STATIC_INLINE void syncReg(uint32_t mask)
  {
    /* Avoid a deadlock if modifying the same register twice when freeze mode is */
    /* activated. */
    if ((CMU->FREEZE & CMU_FREEZE_REGFREEZE) != 0UL) {
      return;
    }
  
    /* Wait for any pending previous write operation to complete */
    /* in low-frequency domain. */
    while ((CMU->SYNCBUSY & mask) != 0UL) {
    }
  }
  
  #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 the 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
  
  /***************************************************************************//**
   * @brief
   *   Set HFPER clock tree prescalers to safe values.
   *
   * @note
   *   This function applies to EFM32GG11B. There are 3 HFPER clock trees with
   *   these frequency limits:
   *     HFPERCLK  (A-tree): 20MHz in VSCALE0 mode, 50MHz in VSCALE2 mode.
   *     HFPERBCLK (B-tree): 20MHz in VSCALE0 mode, 72MHz in VSCALE2 mode.
   *     HFPERCCLK (C-tree): 20MHz in VSCALE0 mode, 50MHz in VSCALE2 mode.
   ******************************************************************************/
  static void hfperClkSafePrescaler(void)
  {
  #if defined(_CMU_HFPERPRESC_MASK) && defined(_CMU_HFPERPRESCB_MASK) \
    && defined(_CMU_HFPERPRESCC_MASK)
    // Assuming a max. HFCLK of 72MHz, we need to set prescalers to DIV4.
    CMU_ClockPrescSet(cmuClock_HFPER, 3U);
    CMU_ClockPrescSet(cmuClock_HFPERB, 3U);
    CMU_ClockPrescSet(cmuClock_HFPERC, 3U);
  #endif
  }
  
  /***************************************************************************//**
   * @brief
   *   Set HFPER clock tree prescalers to give highest possible clock node
   *   frequency while still beeing within spec.
   *
   * @note
   *   This function applies to EFM32GG11B. There are 3 HFPER clock trees with
   *   these frequency limits:
   *     HFPERCLK  (A-tree): 20MHz in VSCALE0 mode, 50MHz in VSCALE2 mode.
   *     HFPERBCLK (B-tree): 20MHz in VSCALE0 mode, 72MHz in VSCALE2 mode.
   *     HFPERCCLK (C-tree): 20MHz in VSCALE0 mode, 50MHz in VSCALE2 mode.
   ******************************************************************************/
  static void hfperClkOptimizedPrescaler(void)
  {
  #if defined(_CMU_HFPERPRESC_MASK) && defined(_CMU_HFPERPRESCB_MASK) \
    && defined(_CMU_HFPERPRESCC_MASK)
    uint32_t hfClkFreq, divisor;
  
    hfClkFreq = SystemHFClockGet();
  
    if ( EMU_VScaleGet() == emuVScaleEM01_LowPower) {
      divisor = (hfClkFreq + 20000000U - 1U) / 20000000U; // ceil(x)
      if (divisor > 0U) {
        divisor--;                                        // Convert to prescaler
      }
      CMU_ClockPrescSet(cmuClock_HFPER, divisor);
      CMU_ClockPrescSet(cmuClock_HFPERB, divisor);
      CMU_ClockPrescSet(cmuClock_HFPERC, divisor);
    } else {
      divisor = (hfClkFreq + 50000000U - 1U) / 50000000U;
      if (divisor > 0U) {
        divisor--;
      }
      CMU_ClockPrescSet(cmuClock_HFPER, divisor);
      CMU_ClockPrescSet(cmuClock_HFPERC, divisor);
  
      divisor = (hfClkFreq + 72000000U - 1U) / 72000000U;
      if (divisor > 0U) {
        divisor--;
      }
      CMU_ClockPrescSet(cmuClock_HFPERB, divisor);
    }
  #endif
  }
  
  /** @endcond */
  
  /*******************************************************************************
   **************************   GLOBAL FUNCTIONS   *******************************
   ******************************************************************************/
  
  #if defined(_CMU_AUXHFRCOCTRL_BAND_MASK)
  /***************************************************************************//**
   * @brief
   *   Get the 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 the 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 a tuning value from the 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(false);
        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 4 MHz 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_AUXHFRCOCAL13_MASK)
      case cmuAUXHFRCOFreq_48M0Hz:
        return DEVINFO->AUXHFRCOCAL13;
  #endif
  #if defined(_DEVINFO_AUXHFRCOCAL14_MASK)
      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 the 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 and set global auxHfrcoFreq. */
    freqCal = CMU_AUXHFRCODevinfoGet(setFreq);
    EFM_ASSERT((freqCal != 0UL) && (freqCal != UINT_MAX));
    auxHfrcoFreq = setFreq;
  
    /* Wait for any previous sync to complete, then set calibration data
       for the selected frequency.  */
    while (BUS_RegBitRead(&CMU->SYNCBUSY,
                          _CMU_SYNCBUSY_AUXHFRCOBSY_SHIFT) != 0UL) {
    }
  
    /* Set a divider in AUXHFRCOCTRL for 1, 2, and 4 MHz. */
    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 the clock.
   *
   * @details
   *   Run a calibration for HFCLK against a selectable reference clock.
   *   See the reference manual, CMU chapter, for more details.
   *
   * @note
   *   This function will not return until the calibration measurement is completed.
   *
   * @param[in] HFCycles
   *   The number of HFCLK cycles to run the calibration. Increasing this number
   *   increases precision but the calibration will take more time.
   *
   * @param[in] ref
   *   The reference clock used to compare HFCLK.
   *
   * @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 reference)
  {
    EFM_ASSERT(HFCycles <= (_CMU_CALCNT_CALCNT_MASK >> _CMU_CALCNT_CALCNT_SHIFT));
  
    /* Set the reference clock source. */
    switch (reference) {
      case cmuOsc_LFXO:
        CMU->CALCTRL = CMU_CALCTRL_UPSEL_LFXO;
        break;
  
      case cmuOsc_LFRCO:
        CMU->CALCTRL = CMU_CALCTRL_UPSEL_LFRCO;
        break;
  
  #if defined(PLFRCO_PRESENT)
      case cmuOsc_PLFRCO:
        CMU->CALCTRL = CMU_CALCTRL_UPSEL_PLFRCO;
        break;
  #endif
  
      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(false);
        return 0;
    }
  
    /* Set the top value. */
    CMU->CALCNT = HFCycles;
  
    /* Start the calibration. */
    CMU->CMD = CMU_CMD_CALSTART;
  
  #if defined(CMU_STATUS_CALRDY)
    /* Wait until calibration completes. */
    while (BUS_RegBitRead(&CMU->STATUS, _CMU_STATUS_CALRDY_SHIFT) == 0UL) {
    }
  #else
    /* Wait until calibration completes. */
    while (BUS_RegBitRead(&CMU->STATUS, _CMU_STATUS_CALBSY_SHIFT) != 0UL) {
    }
  #endif
  
    return CMU->CALCNT;
  }
  
  #if defined(_CMU_CALCTRL_UPSEL_MASK) && defined(_CMU_CALCTRL_DOWNSEL_MASK)
  /***************************************************************************//**
   * @brief
   *   Configure the clock calibration.
   *
   * @details
   *   Configure a calibration for a selectable clock source against another
   *   selectable reference clock.
   *   See the reference manual, CMU chapter, for more details.
   *
   * @note
   *   After configuration, a call to @ref CMU_CalibrateStart() is required and
   *   the resulting calibration value can be read out with the
   *   @ref CMU_CalibrateCountGet() function call.
   *
   * @param[in] downCycles
   *   The number of downSel clock cycles to run the 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 and the result can be given with the
   *   @ref CMU_CalibrateCountGet() function call.
   ******************************************************************************/
  void CMU_CalibrateConfig(uint32_t downCycles, CMU_Osc_TypeDef downSel,
                           CMU_Osc_TypeDef upSel)
  {
    /* Keep configuration settings untouched. */
    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;
  
  #if defined(PLFRCO_PRESENT)
      case cmuOsc_PLFRCO:
        calCtrl |= CMU_CALCTRL_DOWNSEL_PLFRCO;
        break;
  #endif
  
      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(false);
        break;
    }
  
    /* Set the top value to be counted down by the downSel clock. */
    CMU->CALCNT = downCycles;
  
    /* Set the 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;
  
  #if defined(PLFRCO_PRESENT)
      case cmuOsc_PLFRCO:
        calCtrl |= CMU_CALCTRL_UPSEL_PLFRCO;
        break;
  #endif
  
      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(false);
        break;
    }
  
    CMU->CALCTRL = calCtrl;
  }
  #endif
  
  /***************************************************************************//**
   * @brief
   *    Get the calibration count register.
   * @note
   *    If continuous calibration mode is active, calibration busy will almost
   *    always be off and only the value needs to be read. In a normal case,
   *    this function call is triggered by the CALRDY
   *    interrupt flag.
   * @return
   *    The calibration count, the number of UPSEL clocks (see @ref 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) == 0UL) {
  #if defined(CMU_STATUS_CALRDY)
      /* Wait until calibration completes */
      while (BUS_RegBitRead(&CMU->STATUS, _CMU_STATUS_CALRDY_SHIFT) == 0UL) {
      }
  #else
      /* Wait until calibration completes */
      while (BUS_RegBitRead(&CMU->STATUS, _CMU_STATUS_CALBSY_SHIFT) != 0UL) {
      }
  #endif
    }
  #else
    while (BUS_RegBitRead(&CMU->STATUS, _CMU_STATUS_CALBSY_SHIFT) != 0UL) {
    }
  #endif
    return CMU->CALCNT;
  }
  
  /***************************************************************************//**
   * @brief
   *   Get the clock divisor/prescaler.
   *
   * @param[in] clock
   *   A clock point to get the divisor/prescaler for. Notice that not all clock points
   *   have a divisor/prescaler. See the CMU overview in the 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 1UL + (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 = SL_Log2ToDiv(ret);
        break;
  
      case CMU_HFCORECLKDIV_REG:
        ret = (CMU_ClkDiv_TypeDef)((CMU->HFCORECLKDIV
                                    & _CMU_HFCORECLKDIV_HFCORECLKDIV_MASK)
                                   >> _CMU_HFCORECLKDIV_HFCORECLKDIV_SHIFT);
        ret = SL_Log2ToDiv(ret);
        break;
  
  #if defined(_CMU_HFCORECLKDIV_HFCORECLKLEDIV_MASK)
      case CMU_HFCORECLKLEDIV_REG:
        ret = (CMU_ClkDiv_TypeDef)((CMU->HFCORECLKDIV
                                    & _CMU_HFCORECLKDIV_HFCORECLKLEDIV_MASK)
                                   >> _CMU_HFCORECLKDIV_HFCORECLKLEDIV_SHIFT);
        ret = SL_Log2ToDiv(ret + 1U);
        break;
  #endif
  
      case CMU_LFAPRESC0_REG:
        switch (clock) {
          case cmuClock_RTC:
            ret = (CMU_ClkDiv_TypeDef)((CMU->LFAPRESC0 & _CMU_LFAPRESC0_RTC_MASK)
                                       >> _CMU_LFAPRESC0_RTC_SHIFT);
            ret = SL_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 = SL_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 = SL_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 = SL_Log2ToDiv(ret);
            break;
  #endif
  
          default:
            ret = cmuClkDiv_1;
            EFM_ASSERT(false);
            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 = SL_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 = SL_Log2ToDiv(ret);
            break;
  #endif
  
          default:
            ret = cmuClkDiv_1;
            EFM_ASSERT(false);
            break;
        }
        break;
  
      default:
        ret = cmuClkDiv_1;
        EFM_ASSERT(false);
        break;
    }
  
    return ret;
  #endif
  }
  
  /***************************************************************************//**
   * @brief
   *   Set the clock divisor/prescaler.
   *
   * @note
   *   If setting an 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. See @ref CMU_FreezeEnable() for
   *   a suggestion on how to reduce the stalling time in some use cases.
   *
   *   HFCLKLE prescaler is automatically modified when peripherals with clock
   *   domain HFBUSCLK is chosen based on the maximum HFLE frequency allowed.
   *
   * @param[in] clock
   *   Clock point to set divisor/prescaler for. Notice that not all clock points
   *   have a divisor/prescaler. See the 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 - 1U));
  
  #elif defined(_SILICON_LABS_32B_SERIES_0)
    uint32_t freq;
    uint32_t divReg;
  
    /* Get the 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 the divider. */
        CMU->CTRL = (CMU->CTRL & ~_CMU_CTRL_HFCLKDIV_MASK)
                    | ((div - 1) << _CMU_CTRL_HFCLKDIV_SHIFT);
  
        /* Update the CMSIS core clock variable. */
        /* (The function will update the global variable). */
        freq = SystemCoreClockGet();
  
        /* Optimize flash access wait state setting for the current core clk. */
        CMU_UpdateWaitStates(freq, VSCALE_DEFAULT);
        break;
  #endif
  
      case CMU_HFPERCLKDIV_REG:
        EFM_ASSERT((div >= cmuClkDiv_1) && (div <= cmuClkDiv_512));
        /* Convert to the correct scale. */
        div = CMU_DivToLog2(div);
        CMU->HFPERCLKDIV = (CMU->HFPERCLKDIV & ~_CMU_HFPERCLKDIV_HFPERCLKDIV_MASK)
                           | (div << _CMU_HFPERCLKDIV_HFPERCLKDIV_SHIFT);
        break;
  
  #if defined(_CMU_HFCORECLKDIV_HFCORECLKLEDIV_MASK)
      case CMU_HFCORECLKLEDIV_REG:
        /*
           This divisor is usually set when changing HF clock to keep HFLE clock
           within safe bounds. This code path ignore these constraints.
         */
        /* Convert to the correct scale. */
        div = CMU_DivToLog2(div) - 1U;
        CMU->HFCORECLKDIV = (CMU->HFCORECLKDIV
                             & ~_CMU_HFCORECLKDIV_HFCORECLKLEDIV_MASK)
                            | (div << _CMU_HFCORECLKDIV_HFCORECLKLEDIV_SHIFT);
        break;
  #endif
  
      case CMU_HFCORECLKDIV_REG:
        EFM_ASSERT((div >= cmuClkDiv_1) && (div <= cmuClkDiv_512));
  
        /* Configure worst case wait states for flash access before setting the divisor. */
        flashWaitStateMax();
  
  #if defined(CMU_MAX_FREQ_HFLE)
        setHfLeConfig(SystemCoreClockGet() / div);
  #endif
  
        /* Convert to the correct scale. */
        div = CMU_DivToLog2(div);
  
        CMU->HFCORECLKDIV = (CMU->HFCORECLKDIV
                             & ~_CMU_HFCORECLKDIV_HFCORECLKDIV_MASK)
                            | (div << _CMU_HFCORECLKDIV_HFCORECLKDIV_SHIFT);
  
        /* Update the CMSIS core clock variable. */
        /* (The function will update the global variable). */
        freq = SystemCoreClockGet();
  
        /* Optimize wait state setting for the current core clk. */
        CMU_UpdateWaitStates(freq, VSCALE_DEFAULT);
  #if defined(CMU_MAX_FREQ_HFLE)
        setHfLeConfig(freq);
  #endif
        break;
  
      case CMU_LFAPRESC0_REG:
        switch (clock) {
          case cmuClock_RTC:
            EFM_ASSERT(div <= cmuClkDiv_32768);
  
            /* LF register about to be modified requires sync. busy check. */
            syncReg(CMU_SYNCBUSY_LFAPRESC0);
  
            /* Convert to the 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 requires sync. busy check. */
            syncReg(CMU_SYNCBUSY_LFAPRESC0);
  
            /* Convert to the 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 requires sync. busy check. */
            syncReg(CMU_SYNCBUSY_LFAPRESC0);
  
            /* Convert to the 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 requires sync. busy check. */
            syncReg(CMU_SYNCBUSY_LFAPRESC0);
  
            /* Convert to the 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(false);
            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 requires sync. busy check. */
            syncReg(CMU_SYNCBUSY_LFBPRESC0);
  
            /* Convert to the 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 requires sync. busy check. */
            syncReg(CMU_SYNCBUSY_LFBPRESC0);
  
            /* Convert to the 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(false);
            break;
        }
        break;
  
      default:
        EFM_ASSERT(false);
        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 has no effect.
   *   Avoid accessing module registers of a module with a disabled clock.
   *
   * @note
   *   If enabling/disabling an 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. See @ref CMU_FreezeEnable() for
   *   a suggestion on how to reduce the stalling time in some use cases.
   *
   *   HFCLKLE prescaler is automatically modified when peripherals with clock
   *   domain HFBUSCLK is chosen based on the maximum HFLE frequency allowed.
   *
   * @param[in] clock
   *   The clock to enable/disable. Notice that not all defined clock
   *   points have separate enable/disable control. See the CMU overview
   *   in the 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 (((unsigned)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(SystemCoreClockGet());
  #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
  #if defined(_CMU_PDMCTRL_MASK)
      case CMU_PDMREF_EN_REG:
        reg = &CMU->PDMCTRL;
        break;
  #endif
  
      case CMU_PCNT_EN_REG:
        reg = &CMU->PCNTCTRL;
        break;
  
      default: /* Cannot enable/disable a clock point. */
        EFM_ASSERT(false);
        return;
    }
  
    /* Get the bit position used to enable/disable. */
    bit = ((unsigned)clock >> CMU_EN_BIT_POS) & CMU_EN_BIT_MASK;
  
    /* LF synchronization required. */
    if (sync > 0UL) {
      syncReg(sync);
    }
  
    /* Set/clear bit as requested. */
    BUS_RegBitWrite(reg, bit, (uint32_t)enable);
  }
  
  /***************************************************************************//**
   * @brief
   *   Get the clock frequency for a clock point.
   *
   * @param[in] clock
   *   A clock point to fetch the frequency for.
   *
   * @return
   *   The current frequency in Hz.
   ******************************************************************************/
  uint32_t CMU_ClockFreqGet(CMU_Clock_TypeDef clock)
  {
    uint32_t ret;
  
    switch ((unsigned)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(_CMU_HFPERPRESCB_MASK)
      case (CMU_HFPERB_CLK_BRANCH << CMU_CLK_BRANCH_POS):
        ret = SystemHFClockGet();
        /* Calculate frequency after HFPERB prescaler. */
        ret /= 1U + ((CMU->HFPERPRESCB & _CMU_HFPERPRESCB_PRESC_MASK)
                     >> _CMU_HFPERPRESCB_PRESC_SHIFT);
        break;
  #endif
  
  #if defined(_CMU_HFPERPRESCC_MASK)
      case (CMU_HFPERC_CLK_BRANCH << CMU_CLK_BRANCH_POS):
        ret = SystemHFClockGet();
        /* Calculate frequency after HFPERC prescaler. */
        ret /= 1U + ((CMU->HFPERPRESCC & _CMU_HFPERPRESCC_PRESC_MASK)
                     >> _CMU_HFPERPRESCC_PRESC_SHIFT);
        break;
  #endif
  
  #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();
  #if defined(_CMU_HFBUSPRESC_MASK)
        ret /= 1U + ((CMU->HFBUSPRESC & _CMU_HFBUSPRESC_MASK)
                     >> _CMU_HFBUSPRESC_PRESC_SHIFT);
  #endif
        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);
        ret >>= (CMU->LFEPRESC0 & _CMU_LFEPRESC0_RTCC_MASK)
                >> _CMU_LFEPRESC0_RTCC_SHIFT;
        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 /= SL_Log2ToDiv((CMU->LFAPRESC0 & _CMU_LFAPRESC0_LETIMER0_MASK)
                            >> _CMU_LFAPRESC0_LETIMER0_SHIFT);
  #endif
        break;
  #endif
  
  #if defined(_CMU_LFACLKEN0_LETIMER1_MASK)
      case (CMU_LETIMER1_CLK_BRANCH << CMU_CLK_BRANCH_POS):
        ret = lfClkGet(cmuClock_LFA);
  #if defined(_SILICON_LABS_32B_SERIES_0)
        ret >>= (CMU->LFAPRESC0 & _CMU_LFAPRESC0_LETIMER1_MASK)
                >> _CMU_LFAPRESC0_LETIMER1_SHIFT;
  #else
        ret /= SL_Log2ToDiv((CMU->LFAPRESC0 & _CMU_LFAPRESC0_LETIMER1_MASK)
                            >> _CMU_LFAPRESC0_LETIMER1_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 /= SL_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 /= SL_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 /= SL_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 /= SL_Log2ToDiv(((CMU->LFBPRESC0 & _CMU_LFBPRESC0_CSEN_MASK)
                             >> _CMU_LFBPRESC0_CSEN_SHIFT) + 4UL);
        break;
  #endif
  
  #if defined(CMU_LFCCLKEN0_USB)
      case (CMU_USBLE_CLK_BRANCH << CMU_CLK_BRANCH_POS):
        ret = lfClkGet(cmuClock_LFC);
        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
  
  #if defined(_CMU_PDMCTRL_PDMCLKSEL_MASK)
      case (CMU_PDMREF_CLK_BRANCH << CMU_CLK_BRANCH_POS):
        ret = pdmRefClkGet();
        break;
  #endif
  
      case (CMU_HFLE_CLK_BRANCH << CMU_CLK_BRANCH_POS):
  #if defined(_CMU_HFCORECLKDIV_HFCORECLKLEDIV_MASK)
        ret = SystemCoreClockGet() / CMU_ClockDivGet(clock);
  #elif defined(_CMU_HFPRESC_HFCLKLEPRESC_MASK)
        ret = SystemHFClockGet() / CMU_ClockDivGet(clock);
  #else
        ret = SystemCoreClockGet() / 2;
  #endif
        break;
  
      default:
        ret = 0;
        EFM_ASSERT(false);
        break;
    }
  
    return ret;
  }
  
  #if defined(_SILICON_LABS_32B_SERIES_1)
  /***************************************************************************//**
   * @brief
   *   Get the clock prescaler.
   *
   * @param[in] clock
   *   A clock point to get the prescaler for. Notice that not all clock points
   *   have a prescaler. See the CMU overview in the 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 the prescaler register ID. */
    prescReg = ((unsigned)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;
        ret = SL_Log2ToDiv(ret + 1U) - 1U;
        break;
  
      case CMU_HFPERPRESC_REG:
        ret = (CMU->HFPERPRESC & _CMU_HFPERPRESC_PRESC_MASK)
              >> _CMU_HFPERPRESC_PRESC_SHIFT;
        break;
  
  #if defined(_CMU_HFPERPRESCB_MASK)
      case CMU_HFPERPRESCB_REG:
        ret = (CMU->HFPERPRESCB & _CMU_HFPERPRESCB_PRESC_MASK)
              >> _CMU_HFPERPRESCB_PRESC_SHIFT;
        break;
  #endif
  
  #if defined(_CMU_HFPERPRESCC_MASK)
      case CMU_HFPERPRESCC_REG:
        ret = (CMU->HFPERPRESCC & _CMU_HFPERPRESCC_PRESC_MASK)
              >> _CMU_HFPERPRESCC_PRESC_SHIFT;
        break;
  #endif
  
      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 a prescaler value. */
            ret = SL_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 a prescaler value. */
            ret = SL_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 = SL_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 = SL_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 = SL_Log2ToDiv(ret) - 1U;
            break;
  #endif
  
          default:
            ret = 0U;
            EFM_ASSERT(false);
            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 a prescaler value. */
            ret = SL_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 a prescaler value. */
            ret = SL_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 a prescaler value. */
            ret = SL_Log2ToDiv(ret + 4U) - 1U;
            break;
  #endif
  
          default:
            ret = 0U;
            EFM_ASSERT(false);
            break;
        }
        break;
  
      case CMU_LFEPRESC0_REG:
        switch (clock) {
  #if defined(RTCC_PRESENT)
          case cmuClock_RTCC:
            ret = (CMU->LFEPRESC0 & _CMU_LFEPRESC0_RTCC_MASK)
                  >> _CMU_LFEPRESC0_RTCC_SHIFT;
            break;
  
          default:
            ret = 0U;
            EFM_ASSERT(false);
            break;
  #endif
        }
        break;
  
  #if defined(_CMU_ADCCTRL_ADC0CLKDIV_MASK) \
        || defined(_CMU_ADCCTRL_ADC1CLKDIV_MASK)
      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:
            ret = 0U;
            EFM_ASSERT(false);
            break;
        }
        break;
  #endif
  #if defined(_CMU_HFBUSPRESC_MASK)
      case CMU_HFBUSPRESC_REG:
        ret = (CMU->HFBUSPRESC & _CMU_HFBUSPRESC_MASK)
              >> _CMU_HFBUSPRESC_PRESC_SHIFT;
        break;
  #endif
      default:
        ret = 0U;
        EFM_ASSERT(false);
        break;
    }
  
    return ret;
  }
  #endif
  
  #if defined(_SILICON_LABS_32B_SERIES_1)
  /***************************************************************************//**
   * @brief
   *   Set the clock prescaler.
   *
   * @note
   *   If setting an 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. See @ref CMU_FreezeEnable() for
   *   a suggestion on how to reduce the stalling time in some use cases.
   *
   *   HFCLKLE prescaler is automatically modified when peripherals with clock
   *   domain HFBUSCLK is chosen based on the maximum HFLE frequency allowed.
   *
   * @param[in] clock
   *   A clock point to set the prescaler for. Notice that not all clock points
   *   have a prescaler. See the CMU overview in the reference manual.
   *
   * @param[in] presc
   *   The clock prescaler.
   ******************************************************************************/
  void CMU_ClockPrescSet(CMU_Clock_TypeDef clock, CMU_ClkPresc_TypeDef presc)
  {
    uint32_t freq;
    uint32_t prescReg;
  
    /* Get the divisor reg ID. */
    prescReg = ((unsigned)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, set safe HFPER
           clock-tree prescalers. */
        flashWaitStateMax();
        setHfLeConfig(CMU_MAX_FREQ_HFLE + 1UL);
        hfperClkSafePrescaler();
  
        CMU->HFPRESC = (CMU->HFPRESC & ~_CMU_HFPRESC_PRESC_MASK)
                       | (presc << _CMU_HFPRESC_PRESC_SHIFT);
  
        /* Update the CMSIS core clock variable (this function updates the global
           variable). */
        freq = SystemCoreClockGet();
        /* Optimize flash and HFLE wait states and set optimized HFPER clock-tree
           prescalers. */
        CMU_UpdateWaitStates(freq, VSCALE_DEFAULT);
        setHfLeConfig(SystemHFClockGet());
        hfperClkOptimizedPrescaler();
        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:
        presc = CMU_DivToLog2(presc);
        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;
  
  #if defined(_CMU_HFPERPRESCB_MASK)
      case CMU_HFPERPRESCB_REG:
        EFM_ASSERT(presc < 512U);
        CMU->HFPERPRESCB = (CMU->HFPERPRESCB & ~_CMU_HFPERPRESCB_PRESC_MASK)
                           | (presc << _CMU_HFPERPRESCB_PRESC_SHIFT);
        break;
  #endif
  
  #if defined(_CMU_HFPERPRESCC_MASK)
      case CMU_HFPERPRESCC_REG:
        EFM_ASSERT(presc < 512U);
        CMU->HFPERPRESCC = (CMU->HFPERPRESCC & ~_CMU_HFPERPRESCC_PRESC_MASK)
                           | (presc << _CMU_HFPERPRESCC_PRESC_SHIFT);
        break;
  #endif
  
      case CMU_HFCOREPRESC_REG:
        EFM_ASSERT(presc < 512U);
  
        /* Configure worst case wait-states for flash. */
        flashWaitStateMax();
  
        CMU->HFCOREPRESC = (CMU->HFCOREPRESC & ~_CMU_HFCOREPRESC_PRESC_MASK)
                           | (presc << _CMU_HFCOREPRESC_PRESC_SHIFT);
  
        /* Update the CMSIS core clock variable (this function updates the global variable).
           Optimize flash and HFLE wait states. */
        freq = SystemCoreClockGet();
        CMU_UpdateWaitStates(freq, VSCALE_DEFAULT);
        break;
  
      case CMU_LFAPRESC0_REG:
        switch (clock) {
  #if defined(RTC_PRESENT)
          case cmuClock_RTC:
            EFM_ASSERT(presc <= 32768U);
  
            /* Convert the prescaler value to a DIV exponent scale. */
            presc = CMU_PrescToLog2(presc);
  
            /* LF register about to be modified requires 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)
  #if defined(_CMU_LFEPRESC0_RTCC_DIV4)
            EFM_ASSERT(presc <= _CMU_LFEPRESC0_RTCC_DIV4);
  #elif defined(_CMU_LFEPRESC0_RTCC_DIV2)
            EFM_ASSERT(presc <= _CMU_LFEPRESC0_RTCC_DIV2);
  #else
            EFM_ASSERT(presc <= 0U);
  #endif
  
            /* LF register about to be modified requires 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 the prescaler value to a DIV exponent scale. */
            presc = CMU_PrescToLog2(presc);
  
            /* LF register about to be modified requires 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 the prescaler value to a DIV exponent scale. */
            presc = CMU_PrescToLog2(presc);
  
            /* LF register about to be modified requires 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_LETIMER1_MASK)
          case cmuClock_LETIMER1:
            EFM_ASSERT(presc <= 32768U);
  
            /* Convert the prescaler value to a DIV exponent scale. */
            presc = CMU_PrescToLog2(presc);
  
            /* LF register about to be modified requires sync. Busy check. */
            syncReg(CMU_SYNCBUSY_LFAPRESC0);
  
            CMU->LFAPRESC0 = (CMU->LFAPRESC0 & ~_CMU_LFAPRESC0_LETIMER1_MASK)
                             | (presc << _CMU_LFAPRESC0_LETIMER1_SHIFT);
            break;
  #endif
  
  #if defined(_CMU_LFAPRESC0_LESENSE_MASK)
          case cmuClock_LESENSE:
            EFM_ASSERT(presc <= 8U);
  
            /* Convert the prescaler value to a DIV exponent scale. */
            presc = CMU_PrescToLog2(presc);
  
            /* LF register about to be modified requires 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 the prescaler value to a DIV exponent scale. */
            presc = CMU_PrescToLog2(presc);
  
            /* LF register about to be modified requires 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(false);
            break;
        }
        break;
  
      case CMU_LFBPRESC0_REG:
        switch (clock) {
  #if defined(_CMU_LFBPRESC0_LEUART0_MASK)
          case cmuClock_LEUART0:
            EFM_ASSERT(presc <= 8U);
  
            /* Convert the prescaler value to a DIV exponent scale. */
            presc = CMU_PrescToLog2(presc);
  
            /* LF register about to be modified requires 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 the prescaler value to a DIV exponent scale. */
            presc = CMU_PrescToLog2(presc);
  
            /* LF register about to be modified requires 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 the prescaler value to a DIV exponent scale.
             * DIV16 is the lowest supported prescaler. */
            presc = CMU_PrescToLog2(presc) - 4U;
  
            /* LF register about to be modified requires 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(false);
            break;
        }
        break;
  
      case CMU_LFEPRESC0_REG:
        switch (clock) {
  #if defined(_CMU_LFEPRESC0_RTCC_MASK)
          case cmuClock_RTCC:
  #if defined(_CMU_LFEPRESC0_RTCC_DIV4)
            EFM_ASSERT(presc <= _CMU_LFEPRESC0_RTCC_DIV4);
  #elif defined(_CMU_LFEPRESC0_RTCC_DIV2)
            EFM_ASSERT(presc <= _CMU_LFEPRESC0_RTCC_DIV2);
  #else
            EFM_ASSERT(presc <= 0U);
  #endif
  
            /* LF register about to be modified requires 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(false);
            break;
        }
        break;
  
  #if defined(_CMU_ADCCTRL_ADC0CLKDIV_MASK) \
        ||  defined(_CMU_ADCCTRL_ADC1CLKDIV_MASK)
      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(false);
            break;
        }
        break;
  #endif
  
  #if defined(_CMU_HFBUSPRESC_MASK)
      case CMU_HFBUSPRESC_REG:
        EFM_ASSERT(presc <= _CMU_HFBUSPRESC_MASK >> _CMU_HFBUSPRESC_PRESC_SHIFT);
        CMU->HFBUSPRESC = (CMU->HFBUSPRESC & ~_CMU_HFBUSPRESC_MASK)
                          | (presc << _CMU_HFBUSPRESC_PRESC_SHIFT);
        break;
  #endif
  
      default:
        EFM_ASSERT(false);
        break;
    }
  }
  #endif
  
  /***************************************************************************//**
   * @brief
   *   Get the 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
   *   The reference clock used for clocking the 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 = ((unsigned)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;
  
  #if defined(CMU_HFCLKSTATUS_SELECTED_HFRCODIV2)
          case CMU_HFCLKSTATUS_SELECTED_HFRCODIV2:
            ret = cmuSelect_HFRCODIV2;
            break;
  #endif
  
  #if defined(CMU_HFCLKSTATUS_SELECTED_CLKIN0)
          case CMU_HFCLKSTATUS_SELECTED_CLKIN0:
            ret = cmuSelect_CLKIN0;
            break;
  #endif
  
  #if defined(CMU_HFCLKSTATUS_SELECTED_USHFRCO)
          case CMU_HFCLKSTATUS_SELECTED_USHFRCO:
            ret = cmuSelect_USHFRCO;
            break;
  #endif
  
          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(PLFRCO_PRESENT)
          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(PLFRCO_PRESENT)
          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(PLFRCO_PRESENT)
          case CMU_LFECLKSEL_LFE_PLFRCO:
            ret = cmuSelect_PLFRCO;
            break;
  #endif
  
          default:
            ret = cmuSelect_Disabled;
            break;
        }
        break;
  #endif /* CMU_LFECLKSEL_REG */
  
  #if defined(_CMU_LFCCLKSEL_LFC_MASK)
      case CMU_LFCCLKSEL_REG:
        switch (CMU->LFCCLKSEL & _CMU_LFCCLKSEL_LFC_MASK) {
          case CMU_LFCCLKSEL_LFC_LFRCO:
            ret = cmuSelect_LFRCO;
            break;
  
          case CMU_LFCCLKSEL_LFC_LFXO:
            ret = cmuSelect_LFXO;
            break;
  
          case CMU_LFCCLKSEL_LFC_ULFRCO:
            ret = cmuSelect_ULFRCO;
            break;
  
          default:
            ret = cmuSelect_Disabled;
            break;
        }
        break;
  #endif /* CMU_LFCCLKSEL_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;
  
          default:
            ret = cmuSelect_Disabled;
            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;
  
          default:
            ret = cmuSelect_Disabled;
            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
  
  #if defined(_CMU_PDMCTRL_PDMCLKSEL_MASK)
      case CMU_PDMREFSEL_REG:
        switch (CMU->PDMCTRL & _CMU_PDMCTRL_PDMCLKSEL_MASK) {
          case CMU_PDMCTRL_PDMCLKSEL_USHFRCO:
            ret = cmuSelect_USHFRCO;
            break;
  
          case CMU_PDMCTRL_PDMCLKSEL_HFXO:
            ret = cmuSelect_HFXO;
            break;
  
          case CMU_PDMCTRL_PDMCLKSEL_HFRCO:
            ret = cmuSelect_HFRCO;
            break;
        }
        break;
  #endif
  
      default:
        ret = cmuSelect_Error;
        EFM_ASSERT(false);
        break;
    }
  
    return ret;
  }
  
  /***************************************************************************//**
   * @brief
   *   Select the 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.
   *
   * @note
   *   HFCLKLE prescaler is automatically modified when peripherals with clock
   *   domain HFBUSCLK is chosen based on the maximum HFLE frequency allowed.
   *
   * @param[in] clock
   *   A 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
   *   A reference selected for clocking. See the reference manual
   *   for details about references 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
   *   @endif
   *   @if CMU_OSCENCMD_PLFRCOEN
   *   @li #cmuSelect_PLFRCO
   *   @endif
   ******************************************************************************/
  void CMU_ClockSelectSet(CMU_Clock_TypeDef clock, CMU_Select_TypeDef ref)
  {
    uint32_t              select = (uint32_t)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 the clock is set. */
    if (clock == cmuClock_HF) {
      if (ref == cmuSelect_HFXO) {
        vScaleFrequency = SystemHFXOClockGet();
      } else if ((ref == cmuSelect_HFRCO)
                 && ((uint32_t)CMU_HFRCOBandGet()
                     > CMU_VSCALEEM01_LOWPOWER_VOLTAGE_CLOCK_MAX)) {
        vScaleFrequency = (uint32_t)CMU_HFRCOBandGet();
      } else {
        /* Use the default frequency. */
      }
      if (vScaleFrequency != 0UL) {
        EMU_VScaleEM01ByClock(vScaleFrequency, false);
      }
    }
  #endif
  
    selRegId = ((unsigned)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 + 1UL);
  #endif
  #if defined(CMU_CTRL_HFXOBUFCUR_BOOSTABOVE32MHZ)
            /* Adjust HFXO buffer current for frequencies above 32 MHz. */
            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 + 1UL);
  #endif
            break;
  
  #if defined(CMU_CMD_HFCLKSEL_USHFRCODIV2)
          case cmuSelect_USHFRCODIV2:
            select = CMU_CMD_HFCLKSEL_USHFRCODIV2;
            osc = cmuOsc_USHFRCO;
            break;
  #endif
  
  #if defined(CMU_HFCLKSTATUS_SELECTED_HFRCODIV2)
          case cmuSelect_HFRCODIV2:
            select = CMU_HFCLKSEL_HF_HFRCODIV2;
            osc = cmuOsc_HFRCO;
            break;
  #endif
  
  #if defined(CMU_HFCLKSTATUS_SELECTED_CLKIN0)
          case cmuSelect_CLKIN0:
            select = CMU_HFCLKSEL_HF_CLKIN0;
            osc = cmuOsc_CLKIN0;
            break;
  #endif
  
  #if defined(CMU_HFCLKSTATUS_SELECTED_USHFRCO)
          case cmuSelect_USHFRCO:
            select = CMU_HFCLKSEL_HF_USHFRCO;
            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(false);
            return;
  #endif
  
          default:
            EFM_ASSERT(false);
            return;
        }
  
        /* Ensure that the selected oscillator is enabled, waiting for it to stabilize. */
  #if defined(CMU_HFCLKSTATUS_SELECTED_CLKIN0)
        if (osc != cmuOsc_CLKIN0) {
          CMU_OscillatorEnable(osc, true, true);
        }
  #else
        CMU_OscillatorEnable(osc, true, true);
  #endif
  
        /* Configure worst case wait-states for flash and set safe HFPER
           clock-tree prescalers. */
        flashWaitStateMax();
        hfperClkSafePrescaler();
  
  #if defined(_EMU_CMD_EM01VSCALE0_MASK)
        /* Wait for voltage upscaling to complete before the clock is set. */
        if (vScaleFrequency != 0UL) {
          EMU_VScaleWait();
        }
  #endif
  
        /* Switch to the selected oscillator. */
  #if defined(_CMU_HFCLKSEL_MASK)
        CMU->HFCLKSEL = select;
  #else
        CMU->CMD = select;
  #endif
        /* Update the CMSIS core clock variable. */
        /* (The function will update the global variable). */
        freq = SystemCoreClockGet();
  
  #if defined(CMU_MAX_FREQ_HFLE)
        /* Update the HFLE configuration after 'select' is set.
           Note that the HFCLKLE clock is connected differently on platforms 1 and 2. */
  #if defined(_SILICON_LABS_32B_SERIES_0)
        setHfLeConfig(freq);
  #else
        setHfLeConfig(SystemHFClockGet());
  #endif
  #endif
  
        /* Optimize flash access wait state setting for the currently selected core clk. */
        CMU_UpdateWaitStates(freq, VSCALE_DEFAULT);
  
  #if defined(_EMU_CMD_EM01VSCALE0_MASK)
        /* Keep EMU module informed on the source HF clock frequency. This will apply voltage
           downscaling after clock is set if downscaling is configured. */
        if (vScaleFrequency == 0UL) {
          EMU_VScaleEM01ByClock(0, true);
        }
  #endif
        /* Set optimized HFPER clock-tree prescalers. */
        hfperClkOptimizedPrescaler();
        break;
  
  #if defined(_SILICON_LABS_32B_SERIES_1)
      case CMU_LFACLKSEL_REG:
        selReg = &CMU->LFACLKSEL;
        /* HFCLKCLE can't be used as LFACLK. */
        EFM_ASSERT(ref != cmuSelect_HFCLKLE);
        SL_FALLTHROUGH
        /* Fall through and select the clock source. */
  
  #if defined(_CMU_LFCCLKSEL_MASK)
      case CMU_LFCCLKSEL_REG:
        selReg = (selReg == NULL) ? &CMU->LFCCLKSEL : selReg;
        /* HFCLKCLE can't be used as LFCCLK. */
        EFM_ASSERT(ref != cmuSelect_HFCLKLE);
        SL_FALLTHROUGH
  #endif
      /* Fall through and select the clock source. */
  
      case CMU_LFECLKSEL_REG:
        selReg = (selReg == NULL) ? &CMU->LFECLKSEL : selReg;
        /* HFCLKCLE can't be used as LFECLK. */
        EFM_ASSERT(ref != cmuSelect_HFCLKLE);
        SL_FALLTHROUGH
      /* Fall through and select the 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 that thes elected oscillator is enabled, waiting for it to stabilize. */
            CMU_OscillatorEnable(cmuOsc_LFXO, true, true);
            tmp = _CMU_LFACLKSEL_LFA_LFXO;
            break;
  
          case cmuSelect_LFRCO:
            /* Ensure that the 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 the correct HFLE wait-states and enable HFCLK to LE.*/
            setHfLeConfig(SystemHFClockGet());
            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(PLFRCO_PRESENT)
          case cmuSelect_PLFRCO:
            CMU_OscillatorEnable(cmuOsc_PLFRCO, true, true);
            tmp = _CMU_LFACLKSEL_LFA_PLFRCO;
            break;
  #endif
  
          default:
            EFM_ASSERT(false);
            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 that the 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 that the 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 the 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:
            /* An illegal clock source for LFA/LFB selected. */
            EFM_ASSERT(false);
            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 that the 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 that the selected oscillator is enabled, waiting for it to stabilize. */
            CMU_OscillatorEnable(cmuOsc_LFRCO, true, true);
            tmp = _CMU_LFCLKSEL_LFC_LFRCO;
            break;
  
          default:
            /* An illegal clock source for LFC selected. */
            EFM_ASSERT(false);
            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 a debug clock. */
            CMU->DBGCLKSEL = CMU_DBGCLKSEL_DBG_AUXHFRCO;
            break;
  
          case cmuSelect_HFCLK:
            /* Select divided HFCLK as a debug clock. */
            CMU->DBGCLKSEL = CMU_DBGCLKSEL_DBG_HFCLK;
            break;
  #endif
  
  #if defined(CMU_CTRL_DBGCLK)
          case cmuSelect_AUXHFRCO:
            /* Select AUXHFRCO as a debug clock. */
            CMU->CTRL = (CMU->CTRL & ~(_CMU_CTRL_DBGCLK_MASK))
                        | CMU_CTRL_DBGCLK_AUXHFRCO;
            break;
  
          case cmuSelect_HFCLK:
            /* Select divided HFCLK as a debug clock. */
            CMU->CTRL = (CMU->CTRL & ~(_CMU_CTRL_DBGCLK_MASK))
                        | CMU_CTRL_DBGCLK_HFCLK;
            break;
  #endif
  
          default:
            /* An illegal clock source for debug selected. */
            EFM_ASSERT(false);
            return;
        }
        break;
  #endif
  
  #if defined(USBC_CLOCK_PRESENT)
      case CMU_USBCCLKSEL_REG:
        switch (ref) {
          case cmuSelect_LFXO:
            /* Select LFXO as a clock source for USB. It can only be used in sleep mode. */
            /* Ensure that the selected oscillator is enabled, waiting for it to stabilize. */
            CMU_OscillatorEnable(cmuOsc_LFXO, true, true);
  
            /* Switch the oscillator. */
            CMU->CMD = CMU_CMD_USBCCLKSEL_LFXO;
  
            /* Wait until the clock is activated. */
            while ((CMU->STATUS & CMU_STATUS_USBCLFXOSEL) == 0) {
            }
            break;
  
          case cmuSelect_LFRCO:
            /* Select LFRCO as a clock source for USB. It can only be used in sleep mode. */
            /* Ensure that the selected oscillator is enabled, waiting for it to stabilize. */
            CMU_OscillatorEnable(cmuOsc_LFRCO, true, true);
  
            /* Switch the oscillator. */
            CMU->CMD = CMU_CMD_USBCCLKSEL_LFRCO;
  
            /* Wait until the clock is activated. */
            while ((CMU->STATUS & CMU_STATUS_USBCLFRCOSEL) == 0) {
            }
            break;
  
  #if defined(CMU_STATUS_USBCHFCLKSEL)
          case cmuSelect_HFCLK:
            /* Select undivided HFCLK as a clock source for USB. */
            /* The oscillator must already be enabled to avoid a core lockup. */
            CMU->CMD = CMU_CMD_USBCCLKSEL_HFCLKNODIV;
            /* Wait until the clock is activated. */
            while ((CMU->STATUS & CMU_STATUS_USBCHFCLKSEL) == 0) {
            }
            break;
  #endif
  
  #if defined(CMU_CMD_USBCCLKSEL_USHFRCO)
          case cmuSelect_USHFRCO:
            /* Select USHFRCO as a clock source for USB. */
            /* Ensure that the selected oscillator is enabled, waiting for it to stabilize. */
            CMU_OscillatorEnable(cmuOsc_USHFRCO, true, true);
  
            /* Switch the oscillator. */
            CMU->CMD = CMU_CMD_USBCCLKSEL_USHFRCO;
  
            /* Wait until the clock is activated. */
            while ((CMU->STATUS & CMU_STATUS_USBCUSHFRCOSEL) == 0) {
            }
            break;
  #endif
  
          default:
            /* An illegal clock source for USB. */
            EFM_ASSERT(false);
            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 that the 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 that the 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:
            /* An illegal clock source for ADC0ASYNC selected. */
            EFM_ASSERT(false);
            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 that the 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 that the 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:
            /* An illegal clock source for ADC1ASYNC selected. */
            EFM_ASSERT(false);
            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 that the 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 that the 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 that the 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 that the selected oscillator is enabled, waiting for it to stabilize. */
            CMU_OscillatorEnable(cmuOsc_USHFRCO, true, true);
            tmp = _CMU_SDIOCTRL_SDIOCLKSEL_USHFRCO;
            break;
  
          default:
            /* An illegal clock source for SDIOREF selected. */
            EFM_ASSERT(false);
            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 that the 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 that the 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 that the 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 that the selected oscillator is enabled, waiting for it to stabilize. */
            CMU_OscillatorEnable(cmuOsc_USHFRCO, true, true);
            tmp = _CMU_QSPICTRL_QSPI0CLKSEL_USHFRCO;
            break;
  
          default:
            /* An illegal clock source for QSPI0REF selected. */
            EFM_ASSERT(false);
            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 that the 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 that the 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 that the 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 that the 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 that the 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 that the selected oscillator is enabled, waiting for it to stabilize. */
            CMU_OscillatorEnable(cmuOsc_LFRCO, true, true);
            tmp = _CMU_USBCTRL_USBCLKSEL_LFRCO;
            break;
  
          default:
            /* An illegal clock source for USBR selected. */
            EFM_ASSERT(false);
            return;
        }
  
        /* Apply select. */
        CMU->USBCTRL = (CMU->USBCTRL & ~_CMU_USBCTRL_USBCLKSEL_MASK)
                       | (tmp << _CMU_USBCTRL_USBCLKSEL_SHIFT);
        break;
  #endif
  
  #if defined(_CMU_PDMCTRL_PDMCLKSEL_MASK)
      case CMU_PDMREFSEL_REG:
        switch (ref) {
          case cmuSelect_USHFRCO:
            /* Ensure that the selected oscillator is enabled, waiting for it to stabilize. */
            CMU_OscillatorEnable(cmuOsc_USHFRCO, true, true);
            tmp = _CMU_PDMCTRL_PDMCLKSEL_USHFRCO;
            break;
  
          case cmuSelect_HFXO:
            /* Ensure that the selected oscillator is enabled, waiting for it to stabilize. */
            CMU_OscillatorEnable(cmuOsc_HFXO, true, true);
            tmp = _CMU_PDMCTRL_PDMCLKSEL_HFXO;
            break;
  
          case cmuSelect_HFRCO:
            /* Ensure that the selected oscillator is enabled, waiting for it to stabilize. */
            CMU_OscillatorEnable(cmuOsc_HFRCO, true, true);
            tmp = _CMU_PDMCTRL_PDMCLKSEL_HFRCO;
            break;
  
          default:
            /* An illegal clock source for PDMREF selected. */
            EFM_ASSERT(false);
            return;
        }
  
        /* Apply select. */
        CMU->PDMCTRL = (CMU->PDMCTRL & ~_CMU_PDMCTRL_PDMCLKSEL_MASK)
                       | (tmp << _CMU_PDMCTRL_PDMCLKSEL_SHIFT);
        break;
  #endif
  
      default:
        EFM_ASSERT(false);
        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. @n
   *   N & M limitations: @n
   *     300 < N <= 4095 @n
   *     0 <= M <= 4095 @n
   *   Any peripheral running off HFRCO should be switched to HFRCODIV2 prior to
   *   calling this function to avoid over-clocking.
   *
   *   HFCLKLE prescaler is automatically modified before updating HFRCO
   *   based on the maximum HFLE frequency allowed.
   *
   * @param[in] init
   *    DPLL setup parameters.
   *
   * @return
   *   Returns false on invalid target frequency or DPLL locking error.
   *****************************************************************************/
  bool CMU_DPLLLock(const CMU_DPLLInit_TypeDef *init)
  {
    int index = 0;
    unsigned int i;
    bool hfrcoDiv2override = false;
    uint32_t hfrcoCtrlVal, lockStatus, sysFreq;
  
    EFM_ASSERT(init->frequency >= hfrcoCtrlTable[0].minFreq);
    EFM_ASSERT(init->frequency
               <= hfrcoCtrlTable[HFRCOCTRLTABLE_ENTRIES - 1U].maxFreq);
    EFM_ASSERT(init->n > 300U);
    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 the 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 = (int)i;                       // Correct band found
        break;
      }
    }
    if ((uint32_t)index == HFRCOCTRLTABLE_ENTRIES) {
      EFM_ASSERT(false);
      return false;                           // Target frequency out of spec.
    }
    hfrcoCtrlVal = hfrcoCtrlTable[index].value;
  
    // Check if a calibrated HFRCOCTRL.TUNING value is 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%. 9 is subtracted to compensate.
      if (tuning > 9UL) {
        tuning -= 9UL;
      } else {
        tuning = 0UL;
      }
  
      hfrcoCtrlVal |= tuning << _CMU_HFRCOCTRL_TUNING_SHIFT;
    }
  
    // Update the CMSIS frequency SystemHfrcoFreq value.
    SystemHfrcoFreq = init->frequency;
  
    // Set maximum wait-states while changing the core clock.
    if (CMU_ClockSelectGet(cmuClock_HF) == cmuSelect_HFRCO) {
      flashWaitStateMax();
    }
  
    // Update the 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.
      hfrcoDiv2override = true;
      CMU->HFCLKSEL = CMU_HFCLKSEL_HF_HFRCODIV2;
    }
  
    CMU->OSCENCMD  = CMU_OSCENCMD_DPLLDIS;
    while ((CMU->STATUS & (CMU_STATUS_DPLLENS | CMU_STATUS_DPLLRDY)) != 0UL) {
    }
    CMU->IFC       = CMU_IFC_DPLLRDY | CMU_IFC_DPLLLOCKFAILLOW
                     | CMU_IFC_DPLLLOCKFAILHIGH;
    CMU->DPLLCTRL1 = ((uint32_t)init->n   << _CMU_DPLLCTRL1_N_SHIFT)
                     | ((uint32_t)init->m << _CMU_DPLLCTRL1_M_SHIFT);
    CMU->HFRCOCTRL = hfrcoCtrlVal;
    CMU->DPLLCTRL  = ((uint32_t)init->refClk << _CMU_DPLLCTRL_REFSEL_SHIFT)
                     | ((init->autoRecover ? 1UL : 0UL)
                        << _CMU_DPLLCTRL_AUTORECOVER_SHIFT)
                     | ((uint32_t)init->edgeSel << _CMU_DPLLCTRL_EDGESEL_SHIFT)
                     | ((uint32_t)init->lockMode << _CMU_DPLLCTRL_MODE_SHIFT);
    CMU->OSCENCMD  = CMU_OSCENCMD_DPLLEN;
    while ((lockStatus = (CMU->IF & (CMU_IF_DPLLRDY
                                     | CMU_IF_DPLLLOCKFAILLOW
                                     | CMU_IF_DPLLLOCKFAILHIGH))) == 0UL) {
    }
  
    // Restore to HFRCO
    if ((CMU_ClockSelectGet(cmuClock_HF) == cmuSelect_HFRCODIV2)
        && (hfrcoDiv2override == true)) {
      CMU->HFCLKSEL = CMU_HFCLKSEL_HF_HFRCO;
    }
  
    // If HFRCO is selected as an HF clock, optimize the flash access wait-state
    // configuration for this frequency and update the CMSIS core clock variable.
    if (CMU_ClockSelectGet(cmuClock_HF) == cmuSelect_HFRCO) {
      // Call @ref SystemCoreClockGet() to update the 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(SystemHFClockGet());
  
  #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 require 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 use case for this feature is 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 unnecessary
   *   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 the LF
   *       domain
   *****************************************************************************/
  void CMU_FreezeEnable(bool enable)
  {
    if (enable) {
      /* Wait for any ongoing LF synchronizations to complete. This */
      /* protects 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 != 0UL) {
      }
  
      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.
   *
   * @note
   *   HFCLKLE prescaler is automatically modified based on the maximum
   *   HFLE frequency allowed.
   *
   * @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 the tuning value from the 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(false);
        return;
    }
  
    /* If HFRCO is used for the core clock, flash access WS has to be considered. */
    osc = CMU_ClockSelectGet(cmuClock_HF);
    if (osc == cmuSelect_HFRCO) {
      /* Configure worst case wait states for flash access before setting the 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 the core clock, optimize flash WS. */
    if (osc == cmuSelect_HFRCO) {
      /* Call @ref SystemCoreClockGet() to update the CMSIS core clock variable. */
      freq = SystemCoreClockGet();
      CMU_UpdateWaitStates(freq, VSCALE_DEFAULT);
    }
  
  #if defined(CMU_MAX_FREQ_HFLE)
    /* Reduce HFLE frequency if possible. */
    setHfLeConfig(SystemCoreClockGet());
  #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 4 MHz 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_HFRCOCAL13_MASK)
      case cmuHFRCOFreq_48M0Hz:
        return DEVINFO->HFRCOCAL13;
  #endif
  
  #if defined(_DEVINFO_HFRCOCAL14_MASK)
      case cmuHFRCOFreq_56M0Hz:
        return DEVINFO->HFRCOCAL14;
  #endif
  
  #if defined(_DEVINFO_HFRCOCAL15_MASK)
      case cmuHFRCOFreq_64M0Hz:
        return DEVINFO->HFRCOCAL15;
  #endif
  
  #if defined(_DEVINFO_HFRCOCAL16_MASK)
      case cmuHFRCOFreq_72M0Hz:
        return DEVINFO->HFRCOCAL16;
  #endif
  
      default: /* cmuHFRCOFreq_UserDefined */
        return 0;
    }
  }
  
  /***************************************************************************//**
   * @brief
   *   Get the current HFRCO frequency.
   *
   * @return
   *   HFRCO frequency.
   ******************************************************************************/
  CMU_HFRCOFreq_TypeDef CMU_HFRCOBandGet(void)
  {
    return (CMU_HFRCOFreq_TypeDef)SystemHfrcoFreq;
  }
  
  /***************************************************************************//**
   * @brief
   *   Set the HFRCO calibration for the selected target frequency.
   *
   * @note
   *   HFCLKLE prescaler is automatically modified based on the maximum
   *   HFLE frequency allowed.
   *
   * @param[in] setFreq
   *   HFRCO frequency to set.
   ******************************************************************************/
  void CMU_HFRCOBandSet(CMU_HFRCOFreq_TypeDef setFreq)
  {
    uint32_t freqCal;
    uint32_t sysFreq;
    uint32_t prevFreq;
  
    /* Get the DEVINFO index and set the CMSIS frequency SystemHfrcoFreq. */
    freqCal = CMU_HFRCODevinfoGet(setFreq);
    EFM_ASSERT((freqCal != 0UL) && (freqCal != UINT_MAX));
    prevFreq = SystemHfrcoFreq;
    SystemHfrcoFreq = (uint32_t)setFreq;
  
    /* Set maximum wait-states and set safe HFPER clock-tree prescalers while
       changing the core clock. */
    if (CMU_ClockSelectGet(cmuClock_HF) == cmuSelect_HFRCO) {
      flashWaitStateMax();
      hfperClkSafePrescaler();
    }
  
    /* Wait for any previous sync to complete and set calibration data
       for the selected frequency.  */
    while (BUS_RegBitRead(&CMU->SYNCBUSY, _CMU_SYNCBUSY_HFRCOBSY_SHIFT) != 0UL) {
    }
  
    /* Check for valid calibration data. */
    EFM_ASSERT(freqCal != UINT_MAX);
  
    /* Set divider in HFRCOCTRL for 1, 2, and 4 MHz. */
    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((uint32_t)setFreq);
    }
  
    if ((uint32_t)setFreq > prevFreq) {
  #if defined(_EMU_CMD_EM01VSCALE0_MASK)
      /* When increasing frequency voltage scale must be done before the change. */
      EMU_VScaleEM01ByClock((uint32_t)setFreq, true);
  #endif
    }
  
    CMU->HFRCOCTRL = freqCal;
  
    /* If HFRCO is selected as an HF clock, optimize the flash access wait-state configuration
       for this frequency and update the CMSIS core clock variable. */
    if (CMU_ClockSelectGet(cmuClock_HF) == cmuSelect_HFRCO) {
      /* Call @ref SystemCoreClockGet() to update the CMSIS core clock variable. */
      sysFreq = SystemCoreClockGet();
      EFM_ASSERT(sysFreq <= (uint32_t)setFreq);
      EFM_ASSERT(sysFreq <= SystemHfrcoFreq);
      EFM_ASSERT((uint32_t)setFreq == SystemHfrcoFreq);
      CMU_UpdateWaitStates(sysFreq, VSCALE_DEFAULT);
    }
  
    /* Reduce HFLE frequency if possible. */
    setHfLeConfig(SystemHFClockGet());
  
    if ((uint32_t)setFreq <= prevFreq) {
  #if defined(_EMU_CMD_EM01VSCALE0_MASK)
      /* When decreasing frequency voltage scale must be done after the change */
      EMU_VScaleEM01ByClock(0, true);
  #endif
    }
    if (CMU_ClockSelectGet(cmuClock_HF) == cmuSelect_HFRCO) {
      /* Set optimized HFPER clock-tree prescalers. */
      hfperClkOptimizedPrescaler();
    }
  }
  #endif /* _CMU_HFRCOCTRL_FREQRANGE_MASK */
  
  #if defined(_CMU_HFRCOCTRL_SUDELAY_MASK)
  /***************************************************************************//**
   * @brief
   *   Get the HFRCO startup delay.
   *
   * @details
   *   See the reference manual for more 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
   *   See the reference manual for more 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 the current USHFRCO frequency.
   *
   * @return
   *   HFRCO frequency.
   ******************************************************************************/
  CMU_USHFRCOFreq_TypeDef CMU_USHFRCOBandGet(void)
  {
    return (CMU_USHFRCOFreq_TypeDef) ushfrcoFreq;
  }
  
  /***************************************************************************//**
   * @brief
   *   Get USHFRCO frequency.
   *
   * @return
   *   USHFRCO frequency.
   ******************************************************************************/
  uint32_t CMU_USHFRCOFreqGet(void)
  {
    return ushfrcoFreq;
  }
  
  /***************************************************************************//**
   * @brief
   *   Set the 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 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. HFCLKLE prescaler is also automatically
   *   modified if userSel is specified.
   *   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;
  
  #if defined(_EMU_CTRL_EM23VSCALE_MASK)
    if (enEM0EM1StartSel) {
      /* Voltage scaling is not compatible with HFXO auto start and select. */
      EFM_ASSERT((EMU->CTRL & _EMU_CTRL_EM23VSCALE_MASK) == EMU_CTRL_EM23VSCALE_VSCALE2);
    }
  #endif
  
    /* 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 : 0UL)
                | (enEM0EM1StartSel ? CMU_HFXOCTRL_AUTOSTARTSELEM0EM1 : 0UL);
  
    hfxoFreq = SystemHFXOClockGet();
  #if defined(_EMU_CMD_EM01VSCALE0_MASK)
    // Update voltage scaling.
    EMU_VScaleEM01ByClock(hfxoFreq, true);
  #endif
    /* Set wait-states for HFXO if automatic start and select is configured. */
    if ((userSel > 0UL) || enEM0EM1StartSel) {
      CMU_UpdateWaitStates(hfxoFreq, VSCALE_DEFAULT);
      setHfLeConfig(hfxoFreq / CMU_ClockDivGet(cmuClock_HF));
    }
  
    if (enEM0EM1Start || enEM0EM1StartSel) {
      /* Enable the HFXO once in order to finish first time calibrations. */
      CMU_OscillatorEnable(cmuOsc_HFXO, true, true);
    }
  
    /* 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 data sheet. 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 the 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 */
        break;
    }
    CMU->HFXOCTRL = (CMU->HFXOCTRL & ~_CMU_HFXOCTRL_MODE_MASK) | tmp;
  
  #if defined(CMU_HFXOCTRL_HFXOX2EN)
    /* HFXO Doubler can only be enabled on crystals up to max 25 MHz. */
    tmp = 0;
    if (SystemHFXOClockGet() <= 25000000) {
      tmp |= CMU_HFXOCTRL_HFXOX2EN;
    }
  
    CMU->HFXOCTRL = (CMU->HFXOCTRL & ~_CMU_HFXOCTRL_HFXOX2EN_MASK) | tmp;
  #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))
                               | (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,
                    (unsigned)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 = ((uint32_t)hfxoInit->ctuneStartup
                            << _CMU_HFXOSTARTUPCTRL_CTUNE_SHIFT)
                           | ((uint32_t)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))
                               | ((uint32_t)hfxoInit->ctuneSteadyState
                                  << _CMU_HFXOSTEADYSTATECTRL_CTUNE_SHIFT)
                               | ((uint32_t)hfxoInit->xoCoreBiasTrimSteadyState
                                  << _CMU_HFXOSTEADYSTATECTRL_IBTRIMXOCORE_SHIFT)
                               | ((uint32_t)hfxoInit->regIshSteadyState
                                  << _CMU_HFXOSTEADYSTATECTRL_REGISH_SHIFT)
                               | getRegIshUpperVal(hfxoInit->regIshSteadyState);
  
    /* Set timeouts. */
    CMU->HFXOTIMEOUTCTRL = ((uint32_t)hfxoInit->timeoutPeakDetect
                            << _CMU_HFXOTIMEOUTCTRL_PEAKDETTIMEOUT_SHIFT)
                           | ((uint32_t)hfxoInit->timeoutSteady
                              << _CMU_HFXOTIMEOUTCTRL_STEADYTIMEOUT_SHIFT)
                           | ((uint32_t)hfxoInit->timeoutStartup
                              << _CMU_HFXOTIMEOUTCTRL_STARTUPTIMEOUT_SHIFT)
                           | ((uint32_t)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. See
   *   @ref 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 data sheet. This function disables the LFXO when
   *   necessary 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 the HF/Core clock. */
    EFM_ASSERT(CMU_ClockSelectGet(cmuClock_HF) != cmuSelect_LFXO);
  
  #if defined(_SILICON_LABS_32B_SERIES_1)
    uint32_t reg = (CMU->LFXOCTRL & ~(_CMU_LFXOCTRL_TUNING_MASK
                                      | _CMU_LFXOCTRL_GAIN_MASK
                                      | _CMU_LFXOCTRL_TIMEOUT_MASK
                                      | _CMU_LFXOCTRL_MODE_MASK))
                   | ((uint32_t)lfxoInit->ctune << _CMU_LFXOCTRL_TUNING_SHIFT)
                   | ((uint32_t)lfxoInit->gain << _CMU_LFXOCTRL_GAIN_SHIFT)
                   | ((uint32_t)lfxoInit->timeout << _CMU_LFXOCTRL_TIMEOUT_SHIFT)
                   | ((uint32_t)lfxoInit->mode << _CMU_LFXOCTRL_MODE_SHIFT);
  
    /* If LFXO already contains the correct configuration then there is no need
     * to stop the oscillator and apply new settings. The LFXO can be running out
     * of reset, in which case we don't want to disable it unless necessary. */
    if (reg != CMU->LFXOCTRL) {
      CMU_OscillatorEnable(cmuOsc_LFXO, false, true);
      CMU->LFXOCTRL = reg;
    }
  #elif defined(_SILICON_LABS_32B_SERIES_0)
    /* LFXO must be disabled before reconfiguration. */
    CMU_OscillatorEnable(cmuOsc_LFXO, false, true);
  
    bool cmuBoost  = (lfxoInit->boost & 0x2);
    BUS_RegMaskedWrite(&CMU->CTRL,
                       _CMU_CTRL_LFXOTIMEOUT_MASK
                       | _CMU_CTRL_LFXOBOOST_MASK
                       | _CMU_CTRL_LFXOMODE_MASK,
                       ((uint32_t)lfxoInit->timeout
                        << _CMU_CTRL_LFXOTIMEOUT_SHIFT)
                       | ((cmuBoost ? 1 : 0) << _CMU_CTRL_LFXOBOOST_SHIFT)
                       | ((uint32_t)lfxoInit->mode << _CMU_CTRL_LFXOMODE_SHIFT));
  #if defined(_EMU_AUXCTRL_REDLFXOBOOST_MASK)
    /* EFM32GG has a "reduce startup boost" field in the EMU */
    bool emuReduce = (lfxoInit->boost & 0x1);
    BUS_RegBitWrite(&EMU->AUXCTRL, _EMU_AUXCTRL_REDLFXOBOOST_SHIFT, emuReduce ? 1 : 0);
  #endif
  #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. In other words, 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(PLFRCO_PRESENT)
      case cmuOsc_PLFRCO:
        if (!deviceHasPlfrco()) {
          while (true) { // PLFRCO is not available
            EFM_ASSERT(false);
          }
        }
        enBit = CMU_OSCENCMD_PLFRCOEN;
        disBit = CMU_OSCENCMD_PLFRCODIS;
        rdyBitPos = _CMU_STATUS_PLFRCORDY_SHIFT;
        ensBitPos = _CMU_STATUS_PLFRCOENS_SHIFT;
        break;
  #endif
  
      default:
        /* Undefined clock source, cmuOsc_CLKIN0 or cmuOsc_ULFRCO. ULFRCO is always enabled
           and cannot be disabled. In other words,the definition of cmuOsc_ULFRCO is primarily
           intended for information: the ULFRCO is always on.  */
        EFM_ASSERT(false);
        return;
    }
  
    if (enable) {
   #if defined(_CMU_HFXOCTRL_MASK)
      bool firstHfxoEnable = false;
  
      /* Enabling the HFXO for the first time requires special handling.
       * PEAKDETSHUTOPTMODE field of the HFXOCTRL register is used to see if this is the
       * first time the HFXO is enabled. */
      if (osc == cmuOsc_HFXO) {
        if (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) != 0UL);
  #endif
  
          firstHfxoEnable = true;
          /* The first time that an external clock is enabled, switch to CMD mode to make sure that
           * only SCO and not PDA tuning is performed. */
          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) == 0UL) {
      }
  #endif
  
      /* Wait for the clock to become ready after enable. */
      if (wait) {
        while (BUS_RegBitRead(&CMU->STATUS, rdyBitPos) == 0UL) {
        }
  #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. */
            (void)CMU_OscillatorTuningOptimize(cmuOsc_HFXO,
                                               cmuHFXOTuningMode_ShuntCommand,
                                               true);
  #endif
          } else {
            /* Wait for the peak detection and shunt current optimization
               to complete. */
            (void)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) == 0UL) {
          }
        }
  #endif
      }
    } else {
      CMU->OSCENCMD = disBit;
  
  #if defined(_SILICON_LABS_32B_SERIES_1)
      /* Always wait for ENS to go low. */
      while ((CMU->STATUS & (0x1 << ensBitPos)) != 0U) {
      }
  
      if (wait) {
        /* Wait for RDY to go low as well. */
        while ((CMU->STATUS & (0x1 << rdyBitPos)) != 0U) {
        }
      }
  #endif
    }
  }
  
  /***************************************************************************//**
   * @brief
   *   Get the oscillator frequency tuning setting.
   *
   * @param[in] osc
   *   An oscillator to get tuning value for, one of the following:
   *   @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:
        ret = 0;
        EFM_ASSERT(false);
        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 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
   *   An oscillator to set tuning value for, one of the following:
   *   @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) != 0UL) {
        }
  #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) != 0UL) {
        }
  #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) != 0UL) {
        }
  #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) == 0UL);
  
        /* 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(false);
        break;
    }
  }
  
  #if defined(_CMU_HFXOCTRL_PEAKDETSHUNTOPTMODE_MASK) || defined(_CMU_HFXOCTRL_PEAKDETMODE_MASK)
  /***************************************************************************//**
   * @brief
   *   Wait for the oscillator tuning optimization.
   *
   * @param[in] osc
   *   An oscillator to set tuning value for, one of the following:
   *   @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);
          break;
      }
    }
    while ((CMU->STATUS & waitFlags) != waitFlags) {
    }
  
  #if defined(CMU_IF_HFXOPEAKDETERR)
    /* Check error flags. */
    if ((waitFlags & CMU_STATUS_HFXOPEAKDETRDY) != 0UL) {
      return (CMU->IF & CMU_IF_HFXOPEAKDETERR) != 0UL ? true : false;
    }
  #endif
    return true;
  }
  
  /***************************************************************************//**
   * @brief
   *   Start and optionally wait for the oscillator tuning optimization.
   *
   * @param[in] osc
   *   An oscillator to set tuning value for, one of the following:
   *   @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 ((unsigned)mode != 0U) {
  #if defined(CMU_IF_HFXOPEAKDETERR)
          /* Clear the error flag before command write. */
          CMU->IFC = CMU_IFC_HFXOPEAKDETERR;
  #endif
          CMU->CMD = (uint32_t)mode;
        }
        if (wait) {
          return CMU_OscillatorTuningWait(osc, mode);
        }
        break;
  
      default:
        EFM_ASSERT(false);
        break;
    }
    return true;
  }
  #endif
  
  /**************************************************************************//**
   * @brief
   *   Determine if the 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 > 0UL ? true : false;
  }
  
  /**************************************************************************//**
   * @brief
   *   Select the PCNTn clock.
   *
   * @param[in] instance
   *   PCNT instance number to set selected clock source for.
   *
   * @param[in] external
   *   Set to true to select the 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 < (unsigned)PCNT_COUNT);
  
    if (external) {
      setting = 1;
    }
  
    BUS_RegBitWrite(&(CMU->PCNTCTRL), (instance * 2U) + 1U, setting);
  
  #else
    (void)instance;  /* An unused parameter */
    (void)external;  /* An 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);
  }
  
  /***************************************************************************//**
   * @brief
   *   Get USHFRCO frequency.
   *
   * @return
   *   USHFRCO frequency.
   ******************************************************************************/
  uint32_t CMU_USHFRCOFreqGet(void)
  {
    return ushfrcoFreq;
  }
  #endif
  
  #if defined(_CMU_USHFRCOCONF_BAND_MASK)
  /***************************************************************************//**
   * @brief
   *   Set the USHFRCO band to use.
   *
   * @param[in] band
   *   USHFRCO band to activate.
   ******************************************************************************/
  void CMU_USHFRCOBandSet(CMU_USHFRCOBand_TypeDef band)
  {
    uint32_t           tuning;
    uint32_t           fineTuning;
  
    /* Cannot switch band if USHFRCO is already selected as HF clock. */
    EFM_ASSERT(CMU_ClockSelectGet(cmuClock_HF) != cmuSelect_USHFRCODIV2);
  
    /* 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 48 MHz */
        BUS_RegBitWrite(&CMU->USHFRCOCONF, _CMU_USHFRCOCONF_USHFRCODIV2DIS_SHIFT, 0);
        ushfrcoFreq = 48000000UL;
        break;
  
      default:
        EFM_ASSERT(false);
        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 24 MHz. */
    if (band == cmuUSHFRCOBand_24MHz) {
      BUS_RegBitWrite(&CMU->USHFRCOCONF, _CMU_USHFRCOCONF_USHFRCODIV2DIS_SHIFT, 1);
    }
  }
  #endif
  
  #endif // defined(_SILICON_LABS_32B_SERIES_2)
  
  /** @} (end addtogroup CMU) */
  /** @} (end addtogroup emlib) */
  #endif /* defined(CMU_PRESENT) */
  