/***************************************************************************//** * @file em_cmu.c * @brief Clock management unit (CMU) Peripheral API * @version 5.2.2 ******************************************************************************* * # License * Copyright 2016 Silicon Laboratories, Inc. http://www.silabs.com ******************************************************************************* * * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. * * DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Silicon Labs has no * obligation to support this Software. Silicon Labs is providing the * Software "AS IS", with no express or implied warranties of any kind, * including, but not limited to, any implied warranties of merchantability * or fitness for any particular purpose or warranties against infringement * of any proprietary rights of a third party. * * Silicon Labs will not be liable for any consequential, incidental, or * special damages, or any other relief, or for any claim by any third party, * arising from your use of this Software. * ******************************************************************************/ #include "em_cmu.h" #if defined(CMU_PRESENT) #include #include #include "em_assert.h" #include "em_bus.h" #include "em_emu.h" #include "em_cmu.h" #include "em_system.h" #include "em_common.h" /***************************************************************************//** * @addtogroup emlib * @{ ******************************************************************************/ /***************************************************************************//** * @addtogroup CMU * @brief Clock management unit (CMU) Peripheral API * @details * This module contains functions to control the CMU peripheral of Silicon * Labs 32-bit MCUs and SoCs. The CMU controls oscillators and clocks. * @{ ******************************************************************************/ /******************************************************************************* ****************************** DEFINES ************************************ ******************************************************************************/ /** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */ #if defined(_SILICON_LABS_32B_SERIES_1) /** Maximum allowed core frequency when using 0 wait-states on flash access. */ #define CMU_MAX_FREQ_0WS 26000000 /** Maximum allowed core frequency when using 1 wait-states on flash access */ #define CMU_MAX_FREQ_1WS 40000000 /** Maximum allowed core frequency when using 2 wait-states on flash access */ #define CMU_MAX_FREQ_2WS 54000000 /** Maximum allowed core frequency when using 3 wait-states on flash access */ #define CMU_MAX_FREQ_3WS 72000000 #elif defined(_SILICON_LABS_32B_SERIES_0) /** Maximum allowed core frequency when using 0 wait-states on flash access. */ #define CMU_MAX_FREQ_0WS 16000000 /** Maximum allowed core frequency when using 1 wait-states on flash access */ #define CMU_MAX_FREQ_1WS 32000000 #else #error "Max Flash wait-state frequencies are not defined for this platform." #endif /** Maximum frequency for HFLE interface */ #if defined(CMU_CTRL_HFLE) /** Maximum HFLE frequency for series 0 EFM32 and EZR32 Wonder Gecko. */ #if defined(_SILICON_LABS_32B_SERIES_0) \ && (defined(_EFM32_WONDER_FAMILY) \ || defined(_EZR32_WONDER_FAMILY)) #define CMU_MAX_FREQ_HFLE 24000000 /** Maximum HFLE frequency for other series 0 parts with maximum core clock higher than 32MHz. */ #elif defined(_SILICON_LABS_32B_SERIES_0) \ && (defined(_EFM32_GIANT_FAMILY) \ || defined(_EZR32_LEOPARD_FAMILY)) #define CMU_MAX_FREQ_HFLE maxFreqHfle() #endif #elif defined(CMU_CTRL_WSHFLE) /** Maximum HFLE frequency for series 1 parts */ #define CMU_MAX_FREQ_HFLE 32000000 #endif #if defined(CMU_STATUS_HFXOSHUNTOPTRDY) #define HFXO_TUNING_READY_FLAGS (CMU_STATUS_HFXOPEAKDETRDY | CMU_STATUS_HFXOSHUNTOPTRDY) #define HFXO_TUNING_MODE_AUTO (_CMU_HFXOCTRL_PEAKDETSHUNTOPTMODE_AUTOCMD) #define HFXO_TUNING_MODE_CMD (_CMU_HFXOCTRL_PEAKDETSHUNTOPTMODE_CMD) #elif defined(CMU_STATUS_HFXOPEAKDETRDY) #define HFXO_TUNING_READY_FLAGS (CMU_STATUS_HFXOPEAKDETRDY) #define HFXO_TUNING_MODE_AUTO (_CMU_HFXOCTRL_PEAKDETMODE_AUTOCMD) #define HFXO_TUNING_MODE_CMD (_CMU_HFXOCTRL_PEAKDETMODE_CMD) #endif #if defined(CMU_HFXOCTRL_MODE_EXTCLK) /** HFXO external clock mode is renamed from EXTCLK to DIGEXTCLK. */ #define CMU_HFXOCTRL_MODE_DIGEXTCLK CMU_HFXOCTRL_MODE_EXTCLK #endif #if defined(_EMU_CMD_EM01VSCALE0_MASK) #define VSCALE_DEFAULT (EMU_VScaleGet()) #else #define VSCALE_DEFAULT 0 #endif /******************************************************************************* ************************** LOCAL VARIABLES ******************************** ******************************************************************************/ #if defined(_CMU_AUXHFRCOCTRL_FREQRANGE_MASK) static CMU_AUXHFRCOFreq_TypeDef auxHfrcoFreq = cmuAUXHFRCOFreq_19M0Hz; #endif #if defined(_CMU_STATUS_HFXOSHUNTOPTRDY_MASK) #define HFXO_INVALID_TRIM (~_CMU_HFXOTRIMSTATUS_MASK) #endif #if defined(CMU_OSCENCMD_DPLLEN) /** Table of HFRCOCTRL values and their associated min/max frequencies and optional band enumerator. */ static const struct hfrcoCtrlTableElement{ uint32_t minFreq; uint32_t maxFreq; uint32_t value; CMU_HFRCOFreq_TypeDef band; } hfrcoCtrlTable[] = { // minFreq maxFreq HFRCOCTRL value band { 860000, 1050000, 0xBC601F00, cmuHFRCOFreq_1M0Hz }, { 1050000, 1280000, 0xBC611F35, (CMU_HFRCOFreq_TypeDef)0 }, { 1280000, 1480000, 0xBCA21F35, (CMU_HFRCOFreq_TypeDef)0 }, { 1480000, 1800000, 0xAD231F35, (CMU_HFRCOFreq_TypeDef)0 }, { 1800000, 2110000, 0xBA601F00, cmuHFRCOFreq_2M0Hz }, { 2110000, 2560000, 0xBA611F35, (CMU_HFRCOFreq_TypeDef)0 }, { 2560000, 2970000, 0xBAA21F35, (CMU_HFRCOFreq_TypeDef)0 }, { 2970000, 3600000, 0xAB231F35, (CMU_HFRCOFreq_TypeDef)0 }, { 3600000, 4220000, 0xB8601F00, cmuHFRCOFreq_4M0Hz }, { 4220000, 5120000, 0xB8611F35, (CMU_HFRCOFreq_TypeDef)0 }, { 5120000, 5930000, 0xB8A21F35, (CMU_HFRCOFreq_TypeDef)0 }, { 5930000, 7520000, 0xA9231F00, cmuHFRCOFreq_7M0Hz }, { 7520000, 9520000, 0x99241F35, (CMU_HFRCOFreq_TypeDef)0 }, { 9520000, 11800000, 0x99251F35, (CMU_HFRCOFreq_TypeDef)0 }, { 11800000, 14400000, 0x99261F00, cmuHFRCOFreq_13M0Hz }, { 14400000, 17200000, 0x99271F00, cmuHFRCOFreq_16M0Hz }, { 17200000, 19700000, 0x99481F00, cmuHFRCOFreq_19M0Hz }, { 19700000, 23800000, 0x99491F35, (CMU_HFRCOFreq_TypeDef)0 }, { 23800000, 28700000, 0x994A1F00, cmuHFRCOFreq_26M0Hz }, { 28700000, 34800000, 0x996B1F00, cmuHFRCOFreq_32M0Hz }, #if defined(_DEVINFO_HFRCOCAL16_MASK) { 34800000, 42800000, 0x996C1F00, cmuHFRCOFreq_38M0Hz }, { 42800000, 51600000, 0x996D1F00, cmuHFRCOFreq_48M0Hz }, { 51600000, 60500000, 0x998E1F00, cmuHFRCOFreq_56M0Hz }, { 60500000, 72000000, 0xA98F1F00, cmuHFRCOFreq_64M0Hz } #else { 34800000, 40000000, 0x996C1F00, cmuHFRCOFreq_38M0Hz } #endif }; #define HFRCOCTRLTABLE_ENTRIES (sizeof(hfrcoCtrlTable) \ / sizeof(struct hfrcoCtrlTableElement)) #endif // CMU_OSCENCMD_DPLLEN #if defined(_SILICON_LABS_32B_SERIES_1) && defined(_EMU_STATUS_VSCALE_MASK) /* Devices with Voltage Scaling needs extra handling of wait states. */ static const struct flashWsTableElement{ uint32_t maxFreq; uint8_t vscale; uint8_t ws; } flashWsTable[] = { #if (_SILICON_LABS_GECKO_INTERNAL_SDID == 100) { 18000000, 0, 0 }, /* 0 wait states at max frequency 18 MHz and 1.2V */ { 36000000, 0, 1 }, /* 1 wait states at max frequency 36 MHz and 1.2V */ { 54000000, 0, 2 }, /* 2 wait states at max frequency 54 MHz and 1.2V */ { 72000000, 0, 3 }, /* 3 wait states at max frequency 72 MHz and 1.2V */ { 7000000, 2, 0 }, /* 0 wait states at max frequency 7 MHz and 1.0V */ { 14000000, 2, 1 }, /* 1 wait states at max frequency 14 MHz and 1.0V */ { 21000000, 2, 2 }, /* 2 wait states at max frequency 21 MHz and 1.0V */ #else { 25000000, 0, 0 }, /* 0 wait states at max frequency 25 MHz and 1.2V */ { 40000000, 0, 1 }, /* 1 wait states at max frequency 40 MHz and 1.2V */ { 7000000, 2, 0 }, /* 0 wait states at max frequency 7 MHz and 1.0V */ { 14000000, 2, 1 }, /* 1 wait states at max frequency 14 MHz and 1.0V */ { 21000000, 2, 2 }, /* 2 wait states at max frequency 21 MHz and 1.0V */ #endif }; #define FLASH_WS_TABLE_ENTRIES (sizeof(flashWsTable) / sizeof(flashWsTable[0])) #endif #if defined(_CMU_USHFRCOCTRL_FREQRANGE_MASK) \ || defined(_CMU_USHFRCOTUNE_MASK) #ifndef EFM32_USHFRCO_STARTUP_FREQ #define EFM32_USHFRCO_STARTUP_FREQ (48000000UL) #endif static uint32_t ushfrcoFreq = EFM32_USHFRCO_STARTUP_FREQ; #endif /******************************************************************************* ************************** LOCAL PROTOTYPES ******************************* ******************************************************************************/ #if defined(_CMU_HFRCOCTRL_FREQRANGE_MASK) static uint32_t CMU_HFRCODevinfoGet(CMU_HFRCOFreq_TypeDef freq); #endif #if defined(_CMU_USHFRCOCTRL_FREQRANGE_MASK) static uint32_t CMU_USHFRCODevinfoGet(CMU_USHFRCOFreq_TypeDef freq); #endif /** @endcond */ /******************************************************************************* ************************** LOCAL FUNCTIONS ******************************** ******************************************************************************/ /** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */ #if defined(_SILICON_LABS_32B_SERIES_0) \ && (defined(_EFM32_GIANT_FAMILY) \ || defined(_EZR32_LEOPARD_FAMILY)) /***************************************************************************//** * @brief * Return max allowed frequency for low energy peripherals. ******************************************************************************/ static uint32_t maxFreqHfle(void) { uint16_t majorMinorRev; switch (SYSTEM_GetFamily()) { case systemPartFamilyEfm32Leopard: case systemPartFamilyEzr32Leopard: /* CHIP MAJOR bit [5:0] */ majorMinorRev = (((ROMTABLE->PID0 & _ROMTABLE_PID0_REVMAJOR_MASK) >> _ROMTABLE_PID0_REVMAJOR_SHIFT) << 8); /* CHIP MINOR bit [7:4] */ majorMinorRev |= (((ROMTABLE->PID2 & _ROMTABLE_PID2_REVMINORMSB_MASK) >> _ROMTABLE_PID2_REVMINORMSB_SHIFT) << 4); /* CHIP MINOR bit [3:0] */ majorMinorRev |= ((ROMTABLE->PID3 & _ROMTABLE_PID3_REVMINORLSB_MASK) >> _ROMTABLE_PID3_REVMINORLSB_SHIFT); if (majorMinorRev >= 0x0204) { return 24000000; } else { return 32000000; } case systemPartFamilyEfm32Giant: return 32000000; default: /* Invalid device family. */ EFM_ASSERT(false); return 0; } } #endif #if defined(CMU_MAX_FREQ_HFLE) /* Unified definitions for HFLE wait-state and prescaler fields. */ #if defined(CMU_CTRL_HFLE) #define _GENERIC_HFLE_WS_MASK _CMU_CTRL_HFLE_MASK #define _GENERIC_HFLE_WS_SHIFT _CMU_CTRL_HFLE_SHIFT #define GENERIC_HFLE_PRESC_REG CMU->HFCORECLKDIV #define _GENERIC_HFLE_PRESC_MASK _CMU_HFCORECLKDIV_HFCORECLKLEDIV_MASK #define _GENERIC_HFLE_PRESC_SHIFT _CMU_HFCORECLKDIV_HFCORECLKLEDIV_SHIFT #elif defined(CMU_CTRL_WSHFLE) #define _GENERIC_HFLE_WS_MASK _CMU_CTRL_WSHFLE_MASK #define _GENERIC_HFLE_WS_SHIFT _CMU_CTRL_WSHFLE_SHIFT #define GENERIC_HFLE_PRESC_REG CMU->HFPRESC #define _GENERIC_HFLE_PRESC_MASK _CMU_HFPRESC_HFCLKLEPRESC_MASK #define _GENERIC_HFLE_PRESC_SHIFT _CMU_HFPRESC_HFCLKLEPRESC_SHIFT #endif /***************************************************************************//** * @brief * Set HFLE wait-states and HFCLKLE prescaler. * * @param[in] maxLeFreq * Max LE frequency ******************************************************************************/ static void setHfLeConfig(uint32_t hfFreq) { unsigned int hfleWs; uint32_t hflePresc; /* Check for 1 bit fields. BUS_RegBitWrite() below are going to fail if the fields are changed to more than 1 bit. */ EFM_ASSERT((_GENERIC_HFLE_WS_MASK >> _GENERIC_HFLE_WS_SHIFT) == 0x1); /* - Enable HFLE wait-state if to allow access to LE peripherals when HFBUSCLK is above maxLeFreq. - Set HFLE prescaler. Allowed HFLE clock frequency is maxLeFreq. */ hfleWs = 1; if (hfFreq <= CMU_MAX_FREQ_HFLE) { hfleWs = 0; hflePresc = 0; } else if (hfFreq <= (2 * CMU_MAX_FREQ_HFLE)) { hflePresc = 1; } else { hflePresc = 2; } BUS_RegBitWrite(&CMU->CTRL, _GENERIC_HFLE_WS_SHIFT, hfleWs); GENERIC_HFLE_PRESC_REG = (GENERIC_HFLE_PRESC_REG & ~_GENERIC_HFLE_PRESC_MASK) | (hflePresc << _GENERIC_HFLE_PRESC_SHIFT); } #if defined(_CMU_CTRL_HFLE_MASK) /***************************************************************************//** * @brief * Get HFLE wait-state configuration. * * @return * Current wait-state configuration. ******************************************************************************/ static uint32_t getHfLeConfig(void) { uint32_t ws = BUS_RegBitRead(&CMU->CTRL, _GENERIC_HFLE_WS_SHIFT); return ws; } #endif #endif /***************************************************************************//** * @brief * Get the AUX clock frequency. Used by MSC flash programming and LESENSE, * by default also as debug clock. * * @return * AUX Frequency in Hz ******************************************************************************/ static uint32_t auxClkGet(void) { uint32_t ret; #if defined(_CMU_AUXHFRCOCTRL_FREQRANGE_MASK) ret = auxHfrcoFreq; #elif defined(_CMU_AUXHFRCOCTRL_BAND_MASK) /* All series 0 families except EFM32G */ switch (CMU->AUXHFRCOCTRL & _CMU_AUXHFRCOCTRL_BAND_MASK) { case CMU_AUXHFRCOCTRL_BAND_1MHZ: if ( SYSTEM_GetProdRev() >= 19 ) { ret = 1200000; } else { ret = 1000000; } break; case CMU_AUXHFRCOCTRL_BAND_7MHZ: if ( SYSTEM_GetProdRev() >= 19 ) { ret = 6600000; } else { ret = 7000000; } break; case CMU_AUXHFRCOCTRL_BAND_11MHZ: ret = 11000000; break; case CMU_AUXHFRCOCTRL_BAND_14MHZ: ret = 14000000; break; case CMU_AUXHFRCOCTRL_BAND_21MHZ: ret = 21000000; break; #if defined(_CMU_AUXHFRCOCTRL_BAND_28MHZ) case CMU_AUXHFRCOCTRL_BAND_28MHZ: ret = 28000000; break; #endif default: EFM_ASSERT(0); ret = 0; break; } #else /* Gecko has a fixed 14Mhz AUXHFRCO clock */ ret = 14000000; #endif return ret; } #if defined (_CMU_ADCCTRL_ADC0CLKSEL_HFSRCCLK) \ || defined (_CMU_ADCCTRL_ADC1CLKSEL_HFSRCCLK) /***************************************************************************//** * @brief * Get the HFSRCCLK frequency. * * @return * HFSRCCLK Frequency in Hz ******************************************************************************/ static uint32_t hfSrcClkGet(void) { uint32_t ret; ret = SystemHFClockGet(); return ret * (1U + ((CMU->HFPRESC & _CMU_HFPRESC_PRESC_MASK) >> _CMU_HFPRESC_PRESC_SHIFT)); } #endif /***************************************************************************//** * @brief * Get the Debug Trace clock frequency * * @return * Debug Trace frequency in Hz ******************************************************************************/ static uint32_t dbgClkGet(void) { uint32_t ret; CMU_Select_TypeDef clk; /* Get selected clock source */ clk = CMU_ClockSelectGet(cmuClock_DBG); switch (clk) { case cmuSelect_HFCLK: ret = SystemHFClockGet(); break; case cmuSelect_AUXHFRCO: ret = auxClkGet(); break; default: EFM_ASSERT(0); ret = 0; break; } return ret; } #if defined(_CMU_ADCCTRL_MASK) /***************************************************************************//** * @brief * Get the ADC n asynchronous clock frequency * * @return * ADC n asynchronous frequency in Hz ******************************************************************************/ static uint32_t adcAsyncClkGet(uint32_t adc) { uint32_t ret; CMU_Select_TypeDef clk; /* Get selected clock source */ switch (adc) { case 0: clk = CMU_ClockSelectGet(cmuClock_ADC0ASYNC); break; #if defined(_CMU_ADCCTRL_ADC1CLKSEL_MASK) case 1: clk = CMU_ClockSelectGet(cmuClock_ADC1ASYNC); break; #endif default: EFM_ASSERT(0); return 0; } switch (clk) { case cmuSelect_Disabled: ret = 0; break; case cmuSelect_AUXHFRCO: ret = auxClkGet(); break; case cmuSelect_HFXO: ret = SystemHFXOClockGet(); break; case cmuSelect_HFSRCCLK: ret = hfSrcClkGet(); break; default: EFM_ASSERT(0); ret = 0; break; } return ret; } #endif #if defined(_CMU_SDIOCTRL_MASK) /***************************************************************************//** * @brief * Get the SDIO reference clock frequency * * @return * SDIO reference clock frequency in Hz ******************************************************************************/ static uint32_t sdioRefClkGet(void) { uint32_t ret; CMU_Select_TypeDef clk; /* Get selected clock source */ clk = CMU_ClockSelectGet(cmuClock_SDIOREF); switch (clk) { case cmuSelect_HFRCO: ret = SystemHfrcoFreq; break; case cmuSelect_HFXO: ret = SystemHFXOClockGet(); break; case cmuSelect_AUXHFRCO: ret = auxClkGet(); break; case cmuSelect_USHFRCO: ret = ushfrcoFreq; break; default: EFM_ASSERT(0); ret = 0; break; } return ret; } #endif #if defined(_CMU_QSPICTRL_MASK) /***************************************************************************//** * @brief * Get the QSPI n reference clock frequency * * @return * QSPI n reference clock frequency in Hz ******************************************************************************/ static uint32_t qspiRefClkGet(uint32_t qspi) { uint32_t ret; CMU_Select_TypeDef clk; /* Get selected clock source */ switch (qspi) { case 0: clk = CMU_ClockSelectGet(cmuClock_QSPI0REF); break; default: EFM_ASSERT(0); return 0; } switch (clk) { case cmuSelect_HFRCO: ret = SystemHfrcoFreq; break; case cmuSelect_HFXO: ret = SystemHFXOClockGet(); break; case cmuSelect_AUXHFRCO: ret = auxClkGet(); break; case cmuSelect_USHFRCO: ret = ushfrcoFreq; break; default: EFM_ASSERT(0); ret = 0; break; } return ret; } #endif #if defined(USBR_CLOCK_PRESENT) /***************************************************************************//** * @brief * Get the USB rate clock frequency * * @return * USB rate clock frequency in Hz ******************************************************************************/ static uint32_t usbRateClkGet(void) { uint32_t ret; CMU_Select_TypeDef clk; clk = CMU_ClockSelectGet(cmuClock_USBR); switch (clk) { case cmuSelect_USHFRCO: ret = ushfrcoFreq; break; case cmuSelect_HFXO: ret = SystemHFXOClockGet(); break; case cmuSelect_HFXOX2: ret = 2u * SystemHFXOClockGet(); break; case cmuSelect_HFRCO: ret = SystemHfrcoFreq; break; case cmuSelect_LFXO: ret = SystemLFXOClockGet(); break; case cmuSelect_LFRCO: ret = SystemLFRCOClockGet(); break; default: EFM_ASSERT(0); ret = 0; break; } return ret; } #endif /***************************************************************************//** * @brief * Configure flash access wait states in order to support given core clock * frequency. * * @param[in] coreFreq * Core clock frequency to configure flash wait-states for * * @param[in] vscale * Voltage Scale level. Supported levels are 0 and 2 where 0 is the default. ******************************************************************************/ static void flashWaitStateControl(uint32_t coreFreq, int vscale) { uint32_t mode; bool mscLocked; #if defined(MSC_READCTRL_MODE_WS0SCBTP) bool scbtpEn; /* Suppressed Conditional Branch Target Prefetch setting. */ #endif (void) vscale; /* vscale parameter is only used on some devices */ /* Make sure the MSC is unlocked */ mscLocked = MSC->LOCK; MSC->LOCK = MSC_UNLOCK_CODE; /* Get mode and SCBTP enable */ mode = MSC->READCTRL & _MSC_READCTRL_MODE_MASK; #if defined(MSC_READCTRL_MODE_WS0SCBTP) /* Devices with MODE and SCBTP in same register field */ switch (mode) { case MSC_READCTRL_MODE_WS0: case MSC_READCTRL_MODE_WS1: #if defined(MSC_READCTRL_MODE_WS2) case MSC_READCTRL_MODE_WS2: #endif scbtpEn = false; break; default: /* WSxSCBTP */ scbtpEn = true; break; } /* Set mode based on the core clock frequency and SCBTP enable */ if (false) { } #if defined(MSC_READCTRL_MODE_WS2) else if (coreFreq > CMU_MAX_FREQ_1WS) { mode = (scbtpEn ? MSC_READCTRL_MODE_WS2SCBTP : MSC_READCTRL_MODE_WS2); } #endif else if ((coreFreq <= CMU_MAX_FREQ_1WS) && (coreFreq > CMU_MAX_FREQ_0WS)) { mode = (scbtpEn ? MSC_READCTRL_MODE_WS1SCBTP : MSC_READCTRL_MODE_WS1); } else { mode = (scbtpEn ? MSC_READCTRL_MODE_WS0SCBTP : MSC_READCTRL_MODE_WS0); } #elif defined(_SILICON_LABS_32B_SERIES_1) && defined(_EMU_STATUS_VSCALE_MASK) /* These devices have specific requirements on the supported flash wait state * depending on frequency and voltage scale level. */ uint32_t i; for (i = 0; i < FLASH_WS_TABLE_ENTRIES; i++) { if ((flashWsTable[i].vscale == vscale) && (coreFreq <= flashWsTable[i].maxFreq)) { break; // found matching entry } } if (i == FLASH_WS_TABLE_ENTRIES) { EFM_ASSERT(false); mode = 3; // worst case flash wait state for unsupported cases } else { mode = flashWsTable[i].ws; } mode = mode << _MSC_READCTRL_MODE_SHIFT; #else /* Devices where MODE and SCBTP are in separate fields and where the device * either does not support voltage scale or where the voltage scale does * not impact flash wait state configuration. */ if (coreFreq <= CMU_MAX_FREQ_0WS) { mode = 0; } else if (coreFreq <= CMU_MAX_FREQ_1WS) { mode = 1; } #if defined(MSC_READCTRL_MODE_WS2) else if (coreFreq <= CMU_MAX_FREQ_2WS) { mode = 2; } #endif #if defined(MSC_READCTRL_MODE_WS3) else if (coreFreq <= CMU_MAX_FREQ_3WS) { mode = 3; } #endif mode = mode << _MSC_READCTRL_MODE_SHIFT; #endif /* BUS_RegMaskedWrite cannot be used here as it would temporarily set the mode field to WS0 */ MSC->READCTRL = (MSC->READCTRL & ~_MSC_READCTRL_MODE_MASK) | mode; if (mscLocked) { MSC->LOCK = 0; } } /***************************************************************************//** * @brief * Configure flash access wait states to most conservative setting for * this target. Retain SCBTP (Suppressed Conditional Branch Target Prefetch) * setting. ******************************************************************************/ static void flashWaitStateMax(void) { flashWaitStateControl(SystemMaxCoreClockGet(), 0); } #if defined(_MSC_RAMCTRL_RAMWSEN_MASK) /***************************************************************************//** * @brief * Configure RAM access wait states in order to support given core clock * frequency. * * @param[in] coreFreq * Core clock frequency to configure RAM wait-states for * * @param[in] vscale * Voltage Scale level. Supported levels are 0 and 2 where 0 is the default. ******************************************************************************/ static void setRamWaitState(uint32_t coreFreq, int vscale) { uint32_t limit = 38000000; if (vscale == 2) { limit = 16000000; } if (coreFreq > limit) { BUS_RegMaskedSet(&MSC->RAMCTRL, (MSC_RAMCTRL_RAMWSEN | MSC_RAMCTRL_RAM1WSEN | MSC_RAMCTRL_RAM2WSEN)); } else { BUS_RegMaskedClear(&MSC->RAMCTRL, (MSC_RAMCTRL_RAMWSEN | MSC_RAMCTRL_RAM1WSEN | MSC_RAMCTRL_RAM2WSEN)); } } #endif #if defined(_MSC_CTRL_WAITMODE_MASK) /***************************************************************************//** * @brief * Configure wait state for peripheral accesses over the bus to support * given bus clock frequency. * * @param[in] busFreq * peripheral bus clock frequency to configure wait-states for * * @param[in] vscale * The voltage scale to configure wait-states for. Expected values are * 0 or 2. * * @li 0 = 1.2 V (VSCALE2) * @li 2 = 1.0 V (VSCALE0) * ******************************************************************************/ static void setBusWaitState(uint32_t busFreq, int vscale) { if ((busFreq > 50000000) && (vscale == 0)) { BUS_RegMaskedSet(&MSC->CTRL, MSC_CTRL_WAITMODE_WS1); } else { BUS_RegMaskedClear(&MSC->CTRL, MSC_CTRL_WAITMODE_WS1); } } #endif /***************************************************************************//** * @brief * Configure various wait states necessary to switch to a certain frequency * and a certain voltage scale. * * @details * This function will setup the necessary flash, bus and RAM wait states. * Updating the wait state configuration must be done before * increasing the clock frequency, and it must be done after decreasing the * clock frequency. Updating the wait state configuration must be done before * core voltage is decreased, and it must be done after a core voltage is * increased. * * @param[in] coreFreq * Core clock frequency to configure wait-states for. * * @param[in] vscale * The voltage scale to configure wait-states for. Expected values are * 0 or 2, higher number is lower voltage. * * @li 0 = 1.2 V (VSCALE2) * @li 2 = 1.0 V (VSCALE0) * ******************************************************************************/ void CMU_UpdateWaitStates(uint32_t freq, int vscale) { flashWaitStateControl(freq, vscale); #if defined(_MSC_RAMCTRL_RAMWSEN_MASK) setRamWaitState(freq, vscale); #endif #if defined(_MSC_CTRL_WAITMODE_MASK) setBusWaitState(freq, vscale); #endif } #if defined(_CMU_HFXOSTEADYSTATECTRL_REGISHUPPER_MASK) /***************************************************************************//** * @brief * Return upper value for CMU_HFXOSTEADYSTATECTRL_REGISH ******************************************************************************/ static uint32_t getRegIshUpperVal(uint32_t steadyStateRegIsh) { uint32_t regIshUpper; const uint32_t upperMax = _CMU_HFXOSTEADYSTATECTRL_REGISHUPPER_MASK >> _CMU_HFXOSTEADYSTATECTRL_REGISHUPPER_SHIFT; /* Add 3 as specified in register description for CMU_HFXOSTEADYSTATECTRL_REGISHUPPER. */ regIshUpper = SL_MIN(steadyStateRegIsh + 3, upperMax); regIshUpper <<= _CMU_HFXOSTEADYSTATECTRL_REGISHUPPER_SHIFT; return regIshUpper; } #endif #if defined(_CMU_HFXOCTRL_MASK) /***************************************************************************//** * @brief * Get the HFXO tuning mode * * @return * The current HFXO tuning mode from the HFXOCTRL register. ******************************************************************************/ __STATIC_INLINE uint32_t getHfxoTuningMode(void) { #if defined(_CMU_HFXOCTRL_PEAKDETSHUNTOPTMODE_MASK) return (CMU->HFXOCTRL & _CMU_HFXOCTRL_PEAKDETSHUNTOPTMODE_MASK); #else return (CMU->HFXOCTRL & _CMU_HFXOCTRL_PEAKDETMODE_MASK); #endif } /***************************************************************************//** * @brief * Set the HFXO tuning mode * * @param[in] mode * the new HFXO tuning mode, this can be HFXO_TUNING_MODE_AUTO or * HFXO_TUNING_MODE_CMD. ******************************************************************************/ __STATIC_INLINE void setHfxoTuningMode(uint32_t mode) { #if defined(_CMU_HFXOCTRL_PEAKDETSHUNTOPTMODE_MASK) CMU->HFXOCTRL = (CMU->HFXOCTRL & ~_CMU_HFXOCTRL_PEAKDETSHUNTOPTMODE_MASK) | mode; #else CMU->HFXOCTRL = (CMU->HFXOCTRL & ~_CMU_HFXOCTRL_PEAKDETMODE_MASK) | mode; #endif } #endif /***************************************************************************//** * @brief * Get the LFnCLK frequency based on current configuration. * * @param[in] lfClkBranch * Selected LF branch * * @return * The LFnCLK frequency in Hz. If no LFnCLK is selected (disabled), 0 is * returned. ******************************************************************************/ static uint32_t lfClkGet(CMU_Clock_TypeDef lfClkBranch) { uint32_t sel; uint32_t ret = 0; switch (lfClkBranch) { case cmuClock_LFA: case cmuClock_LFB: #if defined(_CMU_LFCCLKEN0_MASK) case cmuClock_LFC: #endif #if defined(_CMU_LFECLKSEL_MASK) case cmuClock_LFE: #endif break; default: EFM_ASSERT(0); break; } sel = CMU_ClockSelectGet(lfClkBranch); /* Get clock select field */ switch (lfClkBranch) { case cmuClock_LFA: #if defined(_CMU_LFCLKSEL_MASK) sel = (CMU->LFCLKSEL & _CMU_LFCLKSEL_LFA_MASK) >> _CMU_LFCLKSEL_LFA_SHIFT; #elif defined(_CMU_LFACLKSEL_MASK) sel = (CMU->LFACLKSEL & _CMU_LFACLKSEL_LFA_MASK) >> _CMU_LFACLKSEL_LFA_SHIFT; #else EFM_ASSERT(0); #endif break; case cmuClock_LFB: #if defined(_CMU_LFCLKSEL_MASK) sel = (CMU->LFCLKSEL & _CMU_LFCLKSEL_LFB_MASK) >> _CMU_LFCLKSEL_LFB_SHIFT; #elif defined(_CMU_LFBCLKSEL_MASK) sel = (CMU->LFBCLKSEL & _CMU_LFBCLKSEL_LFB_MASK) >> _CMU_LFBCLKSEL_LFB_SHIFT; #else EFM_ASSERT(0); #endif break; #if defined(_CMU_LFCCLKEN0_MASK) case cmuClock_LFC: #if defined(_CMU_LFCLKSEL_LFC_MASK) sel = (CMU->LFCLKSEL & _CMU_LFCLKSEL_LFC_MASK) >> _CMU_LFCLKSEL_LFC_SHIFT; #elif defined(_CMU_LFCCLKSEL_LFC_MASK) sel = (CMU->LFCCLKSEL & _CMU_LFCCLKSEL_LFC_MASK) >> _CMU_LFCCLKSEL_LFC_SHIFT; #else EFM_ASSERT(0); #endif break; #endif #if defined(_CMU_LFECLKSEL_MASK) case cmuClock_LFE: sel = (CMU->LFECLKSEL & _CMU_LFECLKSEL_LFE_MASK) >> _CMU_LFECLKSEL_LFE_SHIFT; break; #endif default: EFM_ASSERT(0); break; } /* Get clock frequency */ #if defined(_CMU_LFCLKSEL_MASK) switch (sel) { case _CMU_LFCLKSEL_LFA_LFRCO: ret = SystemLFRCOClockGet(); break; case _CMU_LFCLKSEL_LFA_LFXO: ret = SystemLFXOClockGet(); break; #if defined(_CMU_LFCLKSEL_LFA_HFCORECLKLEDIV2) case _CMU_LFCLKSEL_LFA_HFCORECLKLEDIV2: #if defined(CMU_MAX_FREQ_HFLE) /* HFLE bit is or'ed by hardware with HFCORECLKLEDIV to reduce the * frequency of CMU_HFCORECLKLEDIV2. */ ret = SystemCoreClockGet() / (1U << (getHfLeConfig() + 1)); #else ret = SystemCoreClockGet() / 2U; #endif break; #endif case _CMU_LFCLKSEL_LFA_DISABLED: ret = 0; #if defined(CMU_LFCLKSEL_LFAE) /* Check LF Extended bit setting for LFA or LFB ULFRCO clock */ if ((lfClkBranch == cmuClock_LFA) || (lfClkBranch == cmuClock_LFB)) { if (CMU->LFCLKSEL >> (lfClkBranch == cmuClock_LFA ? _CMU_LFCLKSEL_LFAE_SHIFT : _CMU_LFCLKSEL_LFBE_SHIFT)) { ret = SystemULFRCOClockGet(); } } #endif break; default: EFM_ASSERT(0); ret = 0U; break; } #endif /* _CMU_LFCLKSEL_MASK */ #if defined(_CMU_LFACLKSEL_MASK) switch (sel) { case _CMU_LFACLKSEL_LFA_LFRCO: ret = SystemLFRCOClockGet(); break; case _CMU_LFACLKSEL_LFA_LFXO: ret = SystemLFXOClockGet(); break; case _CMU_LFACLKSEL_LFA_ULFRCO: ret = SystemULFRCOClockGet(); break; #if defined(CMU_LFACLKSEL_LFA_PLFRCO) case _CMU_LFACLKSEL_LFA_PLFRCO: ret = SystemLFRCOClockGet(); break; #endif #if defined(_CMU_LFACLKSEL_LFA_HFCLKLE) case _CMU_LFACLKSEL_LFA_HFCLKLE: ret = SystemCoreClockGet() / CMU_Log2ToDiv(((CMU->HFPRESC & _CMU_HFPRESC_HFCLKLEPRESC_MASK) >> _CMU_HFPRESC_HFCLKLEPRESC_SHIFT) + 1); break; #elif defined(_CMU_LFBCLKSEL_LFB_HFCLKLE) case _CMU_LFBCLKSEL_LFB_HFCLKLE: ret = SystemCoreClockGet() / CMU_Log2ToDiv(((CMU->HFPRESC & _CMU_HFPRESC_HFCLKLEPRESC_MASK) >> _CMU_HFPRESC_HFCLKLEPRESC_SHIFT) + 1); break; #endif case _CMU_LFACLKSEL_LFA_DISABLED: ret = 0; break; } #endif return ret; } /***************************************************************************//** * @brief * Wait for ongoing sync of register(s) to low frequency domain to complete. * * @param[in] mask * Bitmask corresponding to SYNCBUSY register defined bits, indicating * registers that must complete any ongoing synchronization. ******************************************************************************/ __STATIC_INLINE void syncReg(uint32_t mask) { /* Avoid deadlock if modifying the same register twice when freeze mode is */ /* activated. */ if (CMU->FREEZE & CMU_FREEZE_REGFREEZE) { return; } /* Wait for any pending previous write operation to have been completed */ /* in low frequency domain */ while (CMU->SYNCBUSY & mask) { } } #if defined(USBC_CLOCK_PRESENT) /***************************************************************************//** * @brief * Get the USBC frequency * * @return * USBC frequency in Hz ******************************************************************************/ static uint32_t usbCClkGet(void) { uint32_t ret; CMU_Select_TypeDef clk; /* Get selected clock source */ clk = CMU_ClockSelectGet(cmuClock_USBC); switch (clk) { case cmuSelect_LFXO: ret = SystemLFXOClockGet(); break; case cmuSelect_LFRCO: ret = SystemLFRCOClockGet(); break; #if defined (_CMU_USHFRCOCTRL_MASK) case cmuSelect_USHFRCO: ret = ushfrcoFreq; break; #endif case cmuSelect_HFCLK: ret = SystemHFClockGet(); break; default: /* Clock is not enabled */ ret = 0; break; } return ret; } #endif /** @endcond */ /******************************************************************************* ************************** GLOBAL FUNCTIONS ******************************* ******************************************************************************/ #if defined(_CMU_AUXHFRCOCTRL_BAND_MASK) /***************************************************************************//** * @brief * Get AUXHFRCO band in use. * * @return * AUXHFRCO band in use. ******************************************************************************/ CMU_AUXHFRCOBand_TypeDef CMU_AUXHFRCOBandGet(void) { return (CMU_AUXHFRCOBand_TypeDef)((CMU->AUXHFRCOCTRL & _CMU_AUXHFRCOCTRL_BAND_MASK) >> _CMU_AUXHFRCOCTRL_BAND_SHIFT); } #endif /* _CMU_AUXHFRCOCTRL_BAND_MASK */ #if defined(_CMU_AUXHFRCOCTRL_BAND_MASK) /***************************************************************************//** * @brief * Set AUXHFRCO band and the tuning value based on the value in the * calibration table made during production. * * @param[in] band * AUXHFRCO band to activate. ******************************************************************************/ void CMU_AUXHFRCOBandSet(CMU_AUXHFRCOBand_TypeDef band) { uint32_t tuning; /* Read tuning value from calibration table */ switch (band) { case cmuAUXHFRCOBand_1MHz: tuning = (DEVINFO->AUXHFRCOCAL0 & _DEVINFO_AUXHFRCOCAL0_BAND1_MASK) >> _DEVINFO_AUXHFRCOCAL0_BAND1_SHIFT; break; case cmuAUXHFRCOBand_7MHz: tuning = (DEVINFO->AUXHFRCOCAL0 & _DEVINFO_AUXHFRCOCAL0_BAND7_MASK) >> _DEVINFO_AUXHFRCOCAL0_BAND7_SHIFT; break; case cmuAUXHFRCOBand_11MHz: tuning = (DEVINFO->AUXHFRCOCAL0 & _DEVINFO_AUXHFRCOCAL0_BAND11_MASK) >> _DEVINFO_AUXHFRCOCAL0_BAND11_SHIFT; break; case cmuAUXHFRCOBand_14MHz: tuning = (DEVINFO->AUXHFRCOCAL0 & _DEVINFO_AUXHFRCOCAL0_BAND14_MASK) >> _DEVINFO_AUXHFRCOCAL0_BAND14_SHIFT; break; case cmuAUXHFRCOBand_21MHz: tuning = (DEVINFO->AUXHFRCOCAL1 & _DEVINFO_AUXHFRCOCAL1_BAND21_MASK) >> _DEVINFO_AUXHFRCOCAL1_BAND21_SHIFT; break; #if defined(_CMU_AUXHFRCOCTRL_BAND_28MHZ) case cmuAUXHFRCOBand_28MHz: tuning = (DEVINFO->AUXHFRCOCAL1 & _DEVINFO_AUXHFRCOCAL1_BAND28_MASK) >> _DEVINFO_AUXHFRCOCAL1_BAND28_SHIFT; break; #endif default: EFM_ASSERT(0); return; } /* Set band/tuning */ CMU->AUXHFRCOCTRL = (CMU->AUXHFRCOCTRL & ~(_CMU_AUXHFRCOCTRL_BAND_MASK | _CMU_AUXHFRCOCTRL_TUNING_MASK)) | (band << _CMU_AUXHFRCOCTRL_BAND_SHIFT) | (tuning << _CMU_AUXHFRCOCTRL_TUNING_SHIFT); } #endif /* _CMU_AUXHFRCOCTRL_BAND_MASK */ #if defined(_CMU_AUXHFRCOCTRL_FREQRANGE_MASK) /**************************************************************************//** * @brief * Get the AUXHFRCO frequency calibration word in DEVINFO * * @param[in] freq * Frequency in Hz * * @return * AUXHFRCO calibration word for a given frequency *****************************************************************************/ static uint32_t CMU_AUXHFRCODevinfoGet(CMU_AUXHFRCOFreq_TypeDef freq) { switch (freq) { /* 1, 2 and 4MHz share the same calibration word */ case cmuAUXHFRCOFreq_1M0Hz: case cmuAUXHFRCOFreq_2M0Hz: case cmuAUXHFRCOFreq_4M0Hz: return DEVINFO->AUXHFRCOCAL0; case cmuAUXHFRCOFreq_7M0Hz: return DEVINFO->AUXHFRCOCAL3; case cmuAUXHFRCOFreq_13M0Hz: return DEVINFO->AUXHFRCOCAL6; case cmuAUXHFRCOFreq_16M0Hz: return DEVINFO->AUXHFRCOCAL7; case cmuAUXHFRCOFreq_19M0Hz: return DEVINFO->AUXHFRCOCAL8; case cmuAUXHFRCOFreq_26M0Hz: return DEVINFO->AUXHFRCOCAL10; case cmuAUXHFRCOFreq_32M0Hz: return DEVINFO->AUXHFRCOCAL11; case cmuAUXHFRCOFreq_38M0Hz: return DEVINFO->AUXHFRCOCAL12; #if defined(DEVINFO_AUXHFRCOCAL14) case cmuAUXHFRCOFreq_48M0Hz: return DEVINFO->AUXHFRCOCAL13; case cmuAUXHFRCOFreq_50M0Hz: return DEVINFO->AUXHFRCOCAL14; #endif default: /* cmuAUXHFRCOFreq_UserDefined */ return 0; } } #endif /* _CMU_AUXHFRCOCTRL_FREQRANGE_MASK */ #if defined(_CMU_AUXHFRCOCTRL_FREQRANGE_MASK) /***************************************************************************//** * @brief * Get current AUXHFRCO frequency. * * @return * AUXHFRCO frequency ******************************************************************************/ CMU_AUXHFRCOFreq_TypeDef CMU_AUXHFRCOBandGet(void) { return auxHfrcoFreq; } #endif /* _CMU_AUXHFRCOCTRL_FREQRANGE_MASK */ #if defined(_CMU_AUXHFRCOCTRL_FREQRANGE_MASK) /***************************************************************************//** * @brief * Set AUXHFRCO calibration for the selected target frequency. * * @param[in] setFreq * AUXHFRCO frequency to set ******************************************************************************/ void CMU_AUXHFRCOBandSet(CMU_AUXHFRCOFreq_TypeDef setFreq) { uint32_t freqCal; /* Get DEVINFO index, set global auxHfrcoFreq */ freqCal = CMU_AUXHFRCODevinfoGet(setFreq); EFM_ASSERT((freqCal != 0) && (freqCal != UINT_MAX)); auxHfrcoFreq = setFreq; /* Wait for any previous sync to complete, and then set calibration data for the selected frequency. */ while (BUS_RegBitRead(&CMU->SYNCBUSY, _CMU_SYNCBUSY_AUXHFRCOBSY_SHIFT)) ; /* Set divider in AUXHFRCOCTRL for 1, 2 and 4MHz */ switch (setFreq) { case cmuAUXHFRCOFreq_1M0Hz: freqCal = (freqCal & ~_CMU_AUXHFRCOCTRL_CLKDIV_MASK) | CMU_AUXHFRCOCTRL_CLKDIV_DIV4; break; case cmuAUXHFRCOFreq_2M0Hz: freqCal = (freqCal & ~_CMU_AUXHFRCOCTRL_CLKDIV_MASK) | CMU_AUXHFRCOCTRL_CLKDIV_DIV2; break; case cmuAUXHFRCOFreq_4M0Hz: freqCal = (freqCal & ~_CMU_AUXHFRCOCTRL_CLKDIV_MASK) | CMU_AUXHFRCOCTRL_CLKDIV_DIV1; break; default: break; } CMU->AUXHFRCOCTRL = freqCal; } #endif /* _CMU_AUXHFRCOCTRL_FREQRANGE_MASK */ /***************************************************************************//** * @brief * Calibrate clock. * * @details * Run a calibration for HFCLK against a selectable reference clock. Please * refer to the reference manual, CMU chapter, for further details. * * @note * This function will not return until calibration measurement is completed. * * @param[in] HFCycles * The number of HFCLK cycles to run calibration. Increasing this number * increases precision, but the calibration will take more time. * * @param[in] ref * The reference clock used to compare HFCLK with. * * @return * The number of ticks the reference clock after HFCycles ticks on the HF * clock. ******************************************************************************/ uint32_t CMU_Calibrate(uint32_t HFCycles, CMU_Osc_TypeDef ref) { EFM_ASSERT(HFCycles <= (_CMU_CALCNT_CALCNT_MASK >> _CMU_CALCNT_CALCNT_SHIFT)); /* Set reference clock source */ switch (ref) { case cmuOsc_LFXO: CMU->CALCTRL = CMU_CALCTRL_UPSEL_LFXO; break; case cmuOsc_LFRCO: CMU->CALCTRL = CMU_CALCTRL_UPSEL_LFRCO; break; case cmuOsc_HFXO: CMU->CALCTRL = CMU_CALCTRL_UPSEL_HFXO; break; case cmuOsc_HFRCO: CMU->CALCTRL = CMU_CALCTRL_UPSEL_HFRCO; break; case cmuOsc_AUXHFRCO: CMU->CALCTRL = CMU_CALCTRL_UPSEL_AUXHFRCO; break; #if defined (_CMU_USHFRCOCTRL_MASK) case cmuOsc_USHFRCO: CMU->CALCTRL = CMU_CALCTRL_UPSEL_USHFRCO; break; #endif default: EFM_ASSERT(0); return 0; } /* Set top value */ CMU->CALCNT = HFCycles; /* Start calibration */ CMU->CMD = CMU_CMD_CALSTART; #if defined(CMU_STATUS_CALRDY) /* Wait until calibration completes */ while (!BUS_RegBitRead(&CMU->STATUS, _CMU_STATUS_CALRDY_SHIFT)) { } #else /* Wait until calibration completes */ while (BUS_RegBitRead(&CMU->STATUS, _CMU_STATUS_CALBSY_SHIFT)) { } #endif return CMU->CALCNT; } #if defined(_CMU_CALCTRL_UPSEL_MASK) && defined(_CMU_CALCTRL_DOWNSEL_MASK) /***************************************************************************//** * @brief * Configure clock calibration * * @details * Configure a calibration for a selectable clock source against another * selectable reference clock. * Refer to the reference manual, CMU chapter, for further details. * * @note * After configuration, a call to CMU_CalibrateStart() is required, and * the resulting calibration value can be read out with the * CMU_CalibrateCountGet() function call. * * @param[in] downCycles * The number of downSel clock cycles to run calibration. Increasing this * number increases precision, but the calibration will take more time. * * @param[in] downSel * The clock which will be counted down downCycles * * @param[in] upSel * The reference clock, the number of cycles generated by this clock will * be counted and added up, the result can be given with the * CMU_CalibrateCountGet() function call. ******************************************************************************/ void CMU_CalibrateConfig(uint32_t downCycles, CMU_Osc_TypeDef downSel, CMU_Osc_TypeDef upSel) { /* Keep untouched configuration settings */ uint32_t calCtrl = CMU->CALCTRL & ~(_CMU_CALCTRL_UPSEL_MASK | _CMU_CALCTRL_DOWNSEL_MASK); /* 20 bits of precision to calibration count register */ EFM_ASSERT(downCycles <= (_CMU_CALCNT_CALCNT_MASK >> _CMU_CALCNT_CALCNT_SHIFT)); /* Set down counting clock source - down counter */ switch (downSel) { case cmuOsc_LFXO: calCtrl |= CMU_CALCTRL_DOWNSEL_LFXO; break; case cmuOsc_LFRCO: calCtrl |= CMU_CALCTRL_DOWNSEL_LFRCO; break; case cmuOsc_HFXO: calCtrl |= CMU_CALCTRL_DOWNSEL_HFXO; break; case cmuOsc_HFRCO: calCtrl |= CMU_CALCTRL_DOWNSEL_HFRCO; break; case cmuOsc_AUXHFRCO: calCtrl |= CMU_CALCTRL_DOWNSEL_AUXHFRCO; break; #if defined (_CMU_USHFRCOCTRL_MASK) case cmuOsc_USHFRCO: calCtrl |= CMU_CALCTRL_DOWNSEL_USHFRCO; break; #endif default: EFM_ASSERT(0); break; } /* Set top value to be counted down by the downSel clock */ CMU->CALCNT = downCycles; /* Set reference clock source - up counter */ switch (upSel) { case cmuOsc_LFXO: calCtrl |= CMU_CALCTRL_UPSEL_LFXO; break; case cmuOsc_LFRCO: calCtrl |= CMU_CALCTRL_UPSEL_LFRCO; break; case cmuOsc_HFXO: calCtrl |= CMU_CALCTRL_UPSEL_HFXO; break; case cmuOsc_HFRCO: calCtrl |= CMU_CALCTRL_UPSEL_HFRCO; break; case cmuOsc_AUXHFRCO: calCtrl |= CMU_CALCTRL_UPSEL_AUXHFRCO; break; #if defined (_CMU_USHFRCOCTRL_MASK) case cmuOsc_USHFRCO: calCtrl |= CMU_CALCTRL_UPSEL_USHFRCO; break; #endif default: EFM_ASSERT(0); break; } CMU->CALCTRL = calCtrl; } #endif /***************************************************************************//** * @brief * Get calibration count register * @note * If continuous calibrartion mode is active, calibration busy will almost * always be off, and we just need to read the value, where the normal case * would be that this function call has been triggered by the CALRDY * interrupt flag. * @return * Calibration count, the number of UPSEL clocks (see CMU_CalibrateConfig) * in the period of DOWNSEL oscillator clock cycles configured by a previous * write operation to CMU->CALCNT ******************************************************************************/ uint32_t CMU_CalibrateCountGet(void) { /* Wait until calibration completes, UNLESS continuous calibration mode is */ /* active */ #if defined(CMU_CALCTRL_CONT) if (!BUS_RegBitRead(&CMU->CALCTRL, _CMU_CALCTRL_CONT_SHIFT)) { #if defined(CMU_STATUS_CALRDY) /* Wait until calibration completes */ while (!BUS_RegBitRead(&CMU->STATUS, _CMU_STATUS_CALRDY_SHIFT)) { } #else /* Wait until calibration completes */ while (BUS_RegBitRead(&CMU->STATUS, _CMU_STATUS_CALBSY_SHIFT)) { } #endif } #else while (BUS_RegBitRead(&CMU->STATUS, _CMU_STATUS_CALBSY_SHIFT)) { } #endif return CMU->CALCNT; } /***************************************************************************//** * @brief * Get clock divisor/prescaler. * * @param[in] clock * Clock point to get divisor/prescaler for. Notice that not all clock points * have a divisor/prescaler. Please refer to CMU overview in reference manual. * * @return * The current clock point divisor/prescaler. 1 is returned * if @p clock specifies a clock point without a divisor/prescaler. ******************************************************************************/ CMU_ClkDiv_TypeDef CMU_ClockDivGet(CMU_Clock_TypeDef clock) { #if defined(_SILICON_LABS_32B_SERIES_1) return 1 + (uint32_t)CMU_ClockPrescGet(clock); #elif defined(_SILICON_LABS_32B_SERIES_0) uint32_t divReg; CMU_ClkDiv_TypeDef ret; /* Get divisor reg id */ divReg = (clock >> CMU_DIV_REG_POS) & CMU_DIV_REG_MASK; switch (divReg) { #if defined(_CMU_CTRL_HFCLKDIV_MASK) case CMU_HFCLKDIV_REG: ret = 1 + ((CMU->CTRL & _CMU_CTRL_HFCLKDIV_MASK) >> _CMU_CTRL_HFCLKDIV_SHIFT); break; #endif case CMU_HFPERCLKDIV_REG: ret = (CMU_ClkDiv_TypeDef)((CMU->HFPERCLKDIV & _CMU_HFPERCLKDIV_HFPERCLKDIV_MASK) >> _CMU_HFPERCLKDIV_HFPERCLKDIV_SHIFT); ret = CMU_Log2ToDiv(ret); break; case CMU_HFCORECLKDIV_REG: ret = (CMU_ClkDiv_TypeDef)((CMU->HFCORECLKDIV & _CMU_HFCORECLKDIV_HFCORECLKDIV_MASK) >> _CMU_HFCORECLKDIV_HFCORECLKDIV_SHIFT); ret = CMU_Log2ToDiv(ret); break; case CMU_LFAPRESC0_REG: switch (clock) { case cmuClock_RTC: ret = (CMU_ClkDiv_TypeDef)((CMU->LFAPRESC0 & _CMU_LFAPRESC0_RTC_MASK) >> _CMU_LFAPRESC0_RTC_SHIFT); ret = CMU_Log2ToDiv(ret); break; #if defined(_CMU_LFAPRESC0_LETIMER0_MASK) case cmuClock_LETIMER0: ret = (CMU_ClkDiv_TypeDef)((CMU->LFAPRESC0 & _CMU_LFAPRESC0_LETIMER0_MASK) >> _CMU_LFAPRESC0_LETIMER0_SHIFT); ret = CMU_Log2ToDiv(ret); break; #endif #if defined(_CMU_LFAPRESC0_LCD_MASK) case cmuClock_LCDpre: ret = (CMU_ClkDiv_TypeDef)(((CMU->LFAPRESC0 & _CMU_LFAPRESC0_LCD_MASK) >> _CMU_LFAPRESC0_LCD_SHIFT) + CMU_DivToLog2(cmuClkDiv_16)); ret = CMU_Log2ToDiv(ret); break; #endif #if defined(_CMU_LFAPRESC0_LESENSE_MASK) case cmuClock_LESENSE: ret = (CMU_ClkDiv_TypeDef)((CMU->LFAPRESC0 & _CMU_LFAPRESC0_LESENSE_MASK) >> _CMU_LFAPRESC0_LESENSE_SHIFT); ret = CMU_Log2ToDiv(ret); break; #endif default: EFM_ASSERT(0); ret = cmuClkDiv_1; break; } break; case CMU_LFBPRESC0_REG: switch (clock) { #if defined(_CMU_LFBPRESC0_LEUART0_MASK) case cmuClock_LEUART0: ret = (CMU_ClkDiv_TypeDef)((CMU->LFBPRESC0 & _CMU_LFBPRESC0_LEUART0_MASK) >> _CMU_LFBPRESC0_LEUART0_SHIFT); ret = CMU_Log2ToDiv(ret); break; #endif #if defined(_CMU_LFBPRESC0_LEUART1_MASK) case cmuClock_LEUART1: ret = (CMU_ClkDiv_TypeDef)((CMU->LFBPRESC0 & _CMU_LFBPRESC0_LEUART1_MASK) >> _CMU_LFBPRESC0_LEUART1_SHIFT); ret = CMU_Log2ToDiv(ret); break; #endif default: EFM_ASSERT(0); ret = cmuClkDiv_1; break; } break; default: EFM_ASSERT(0); ret = cmuClkDiv_1; break; } return ret; #endif } /***************************************************************************//** * @brief * Set clock divisor/prescaler. * * @note * If setting a LF clock prescaler, synchronization into the low frequency * domain is required. If the same register is modified before a previous * update has completed, this function will stall until the previous * synchronization has completed. Please refer to CMU_FreezeEnable() for * a suggestion on how to reduce stalling time in some use cases. * * @param[in] clock * Clock point to set divisor/prescaler for. Notice that not all clock points * have a divisor/prescaler, please refer to CMU overview in the reference * manual. * * @param[in] div * The clock divisor to use (<= cmuClkDiv_512). ******************************************************************************/ void CMU_ClockDivSet(CMU_Clock_TypeDef clock, CMU_ClkDiv_TypeDef div) { #if defined(_SILICON_LABS_32B_SERIES_1) CMU_ClockPrescSet(clock, (CMU_ClkPresc_TypeDef)(div - 1)); #elif defined(_SILICON_LABS_32B_SERIES_0) uint32_t freq; uint32_t divReg; /* Get divisor reg id */ divReg = (clock >> CMU_DIV_REG_POS) & CMU_DIV_REG_MASK; switch (divReg) { #if defined(_CMU_CTRL_HFCLKDIV_MASK) case CMU_HFCLKDIV_REG: EFM_ASSERT((div >= cmuClkDiv_1) && (div <= cmuClkDiv_8)); /* Configure worst case wait states for flash access before setting divisor */ flashWaitStateMax(); /* Set divider */ CMU->CTRL = (CMU->CTRL & ~_CMU_CTRL_HFCLKDIV_MASK) | ((div - 1) << _CMU_CTRL_HFCLKDIV_SHIFT); /* Update CMSIS core clock variable */ /* (The function will update the global variable) */ freq = SystemCoreClockGet(); /* Optimize flash access wait state setting for current core clk */ CMU_UpdateWaitStates(freq, VSCALE_DEFAULT); break; #endif case CMU_HFPERCLKDIV_REG: EFM_ASSERT((div >= cmuClkDiv_1) && (div <= cmuClkDiv_512)); /* Convert to correct scale */ div = CMU_DivToLog2(div); CMU->HFPERCLKDIV = (CMU->HFPERCLKDIV & ~_CMU_HFPERCLKDIV_HFPERCLKDIV_MASK) | (div << _CMU_HFPERCLKDIV_HFPERCLKDIV_SHIFT); break; case CMU_HFCORECLKDIV_REG: EFM_ASSERT((div >= cmuClkDiv_1) && (div <= cmuClkDiv_512)); /* Configure worst case wait states for flash access before setting divisor */ flashWaitStateMax(); #if defined(CMU_MAX_FREQ_HFLE) setHfLeConfig(SystemHFClockGet() / div); #endif /* Convert to correct scale */ div = CMU_DivToLog2(div); CMU->HFCORECLKDIV = (CMU->HFCORECLKDIV & ~_CMU_HFCORECLKDIV_HFCORECLKDIV_MASK) | (div << _CMU_HFCORECLKDIV_HFCORECLKDIV_SHIFT); /* Update CMSIS core clock variable */ /* (The function will update the global variable) */ freq = SystemCoreClockGet(); /* Optimize wait state setting for current core clk */ CMU_UpdateWaitStates(freq, VSCALE_DEFAULT); break; case CMU_LFAPRESC0_REG: switch (clock) { case cmuClock_RTC: EFM_ASSERT(div <= cmuClkDiv_32768); /* LF register about to be modified require sync. busy check */ syncReg(CMU_SYNCBUSY_LFAPRESC0); /* Convert to correct scale */ div = CMU_DivToLog2(div); CMU->LFAPRESC0 = (CMU->LFAPRESC0 & ~_CMU_LFAPRESC0_RTC_MASK) | (div << _CMU_LFAPRESC0_RTC_SHIFT); break; #if defined(_CMU_LFAPRESC0_LETIMER0_MASK) case cmuClock_LETIMER0: EFM_ASSERT(div <= cmuClkDiv_32768); /* LF register about to be modified require sync. busy check */ syncReg(CMU_SYNCBUSY_LFAPRESC0); /* Convert to correct scale */ div = CMU_DivToLog2(div); CMU->LFAPRESC0 = (CMU->LFAPRESC0 & ~_CMU_LFAPRESC0_LETIMER0_MASK) | (div << _CMU_LFAPRESC0_LETIMER0_SHIFT); break; #endif #if defined(LCD_PRESENT) case cmuClock_LCDpre: EFM_ASSERT((div >= cmuClkDiv_16) && (div <= cmuClkDiv_128)); /* LF register about to be modified require sync. busy check */ syncReg(CMU_SYNCBUSY_LFAPRESC0); /* Convert to correct scale */ div = CMU_DivToLog2(div); CMU->LFAPRESC0 = (CMU->LFAPRESC0 & ~_CMU_LFAPRESC0_LCD_MASK) | ((div - CMU_DivToLog2(cmuClkDiv_16)) << _CMU_LFAPRESC0_LCD_SHIFT); break; #endif /* defined(LCD_PRESENT) */ #if defined(LESENSE_PRESENT) case cmuClock_LESENSE: EFM_ASSERT(div <= cmuClkDiv_8); /* LF register about to be modified require sync. busy check */ syncReg(CMU_SYNCBUSY_LFAPRESC0); /* Convert to correct scale */ div = CMU_DivToLog2(div); CMU->LFAPRESC0 = (CMU->LFAPRESC0 & ~_CMU_LFAPRESC0_LESENSE_MASK) | (div << _CMU_LFAPRESC0_LESENSE_SHIFT); break; #endif /* defined(LESENSE_PRESENT) */ default: EFM_ASSERT(0); break; } break; case CMU_LFBPRESC0_REG: switch (clock) { #if defined(_CMU_LFBPRESC0_LEUART0_MASK) case cmuClock_LEUART0: EFM_ASSERT(div <= cmuClkDiv_8); /* LF register about to be modified require sync. busy check */ syncReg(CMU_SYNCBUSY_LFBPRESC0); /* Convert to correct scale */ div = CMU_DivToLog2(div); CMU->LFBPRESC0 = (CMU->LFBPRESC0 & ~_CMU_LFBPRESC0_LEUART0_MASK) | (((uint32_t)div) << _CMU_LFBPRESC0_LEUART0_SHIFT); break; #endif #if defined(_CMU_LFBPRESC0_LEUART1_MASK) case cmuClock_LEUART1: EFM_ASSERT(div <= cmuClkDiv_8); /* LF register about to be modified require sync. busy check */ syncReg(CMU_SYNCBUSY_LFBPRESC0); /* Convert to correct scale */ div = CMU_DivToLog2(div); CMU->LFBPRESC0 = (CMU->LFBPRESC0 & ~_CMU_LFBPRESC0_LEUART1_MASK) | (((uint32_t)div) << _CMU_LFBPRESC0_LEUART1_SHIFT); break; #endif default: EFM_ASSERT(0); break; } break; default: EFM_ASSERT(0); break; } #endif } /***************************************************************************//** * @brief * Enable/disable a clock. * * @details * In general, module clocking is disabled after a reset. If a module * clock is disabled, the registers of that module are not accessible and * reading from such registers may return undefined values. Writing to * registers of clock disabled modules have no effect. One should normally * avoid accessing module registers of a module with a disabled clock. * * @note * If enabling/disabling a LF clock, synchronization into the low frequency * domain is required. If the same register is modified before a previous * update has completed, this function will stall until the previous * synchronization has completed. Please refer to CMU_FreezeEnable() for * a suggestion on how to reduce stalling time in some use cases. * * @param[in] clock * The clock to enable/disable. Notice that not all defined clock * points have separate enable/disable control, please refer to CMU overview * in reference manual. * * @param[in] enable * @li true - enable specified clock. * @li false - disable specified clock. ******************************************************************************/ void CMU_ClockEnable(CMU_Clock_TypeDef clock, bool enable) { volatile uint32_t *reg; uint32_t bit; uint32_t sync = 0; /* Identify enable register */ switch ((clock >> CMU_EN_REG_POS) & CMU_EN_REG_MASK) { #if defined(_CMU_CTRL_HFPERCLKEN_MASK) case CMU_CTRL_EN_REG: reg = &CMU->CTRL; break; #endif #if defined(_CMU_HFCORECLKEN0_MASK) case CMU_HFCORECLKEN0_EN_REG: reg = &CMU->HFCORECLKEN0; #if defined(CMU_MAX_FREQ_HFLE) setHfLeConfig(CMU_ClockFreqGet(cmuClock_HFLE)); #endif break; #endif #if defined(_CMU_HFBUSCLKEN0_MASK) case CMU_HFBUSCLKEN0_EN_REG: reg = &CMU->HFBUSCLKEN0; break; #endif #if defined(_CMU_HFPERCLKDIV_MASK) case CMU_HFPERCLKDIV_EN_REG: reg = &CMU->HFPERCLKDIV; break; #endif case CMU_HFPERCLKEN0_EN_REG: reg = &CMU->HFPERCLKEN0; break; #if defined(_CMU_HFPERCLKEN1_MASK) case CMU_HFPERCLKEN1_EN_REG: reg = &CMU->HFPERCLKEN1; break; #endif case CMU_LFACLKEN0_EN_REG: reg = &CMU->LFACLKEN0; sync = CMU_SYNCBUSY_LFACLKEN0; break; case CMU_LFBCLKEN0_EN_REG: reg = &CMU->LFBCLKEN0; sync = CMU_SYNCBUSY_LFBCLKEN0; break; #if defined(_CMU_LFCCLKEN0_MASK) case CMU_LFCCLKEN0_EN_REG: reg = &CMU->LFCCLKEN0; sync = CMU_SYNCBUSY_LFCCLKEN0; break; #endif #if defined(_CMU_LFECLKEN0_MASK) case CMU_LFECLKEN0_EN_REG: reg = &CMU->LFECLKEN0; sync = CMU_SYNCBUSY_LFECLKEN0; break; #endif #if defined(_CMU_SDIOCTRL_MASK) case CMU_SDIOREF_EN_REG: reg = &CMU->SDIOCTRL; enable = !enable; break; #endif #if defined(_CMU_QSPICTRL_MASK) case CMU_QSPI0REF_EN_REG: reg = &CMU->QSPICTRL; enable = !enable; break; #endif #if defined(_CMU_USBCTRL_MASK) case CMU_USBRCLK_EN_REG: reg = &CMU->USBCTRL; break; #endif case CMU_PCNT_EN_REG: reg = &CMU->PCNTCTRL; break; default: /* Cannot enable/disable clock point */ EFM_ASSERT(0); return; } /* Get bit position used to enable/disable */ bit = (clock >> CMU_EN_BIT_POS) & CMU_EN_BIT_MASK; /* LF synchronization required? */ if (sync) { syncReg(sync); } /* Set/clear bit as requested */ BUS_RegBitWrite(reg, bit, enable); } /***************************************************************************//** * @brief * Get clock frequency for a clock point. * * @param[in] clock * Clock point to fetch frequency for. * * @return * The current frequency in Hz. ******************************************************************************/ uint32_t CMU_ClockFreqGet(CMU_Clock_TypeDef clock) { uint32_t ret; switch (clock & (CMU_CLK_BRANCH_MASK << CMU_CLK_BRANCH_POS)) { case (CMU_HF_CLK_BRANCH << CMU_CLK_BRANCH_POS): ret = SystemHFClockGet(); break; case (CMU_HFPER_CLK_BRANCH << CMU_CLK_BRANCH_POS): ret = SystemHFClockGet(); /* Calculate frequency after HFPER divider. */ #if defined(_CMU_HFPERCLKDIV_HFPERCLKDIV_MASK) ret >>= (CMU->HFPERCLKDIV & _CMU_HFPERCLKDIV_HFPERCLKDIV_MASK) >> _CMU_HFPERCLKDIV_HFPERCLKDIV_SHIFT; #endif #if defined(_CMU_HFPERPRESC_PRESC_MASK) ret /= 1U + ((CMU->HFPERPRESC & _CMU_HFPERPRESC_PRESC_MASK) >> _CMU_HFPERPRESC_PRESC_SHIFT); #endif break; #if defined(_SILICON_LABS_32B_SERIES_1) #if defined(CRYPTO_PRESENT) \ || defined(LDMA_PRESENT) \ || defined(GPCRC_PRESENT) \ || defined(PRS_PRESENT) \ || defined(GPIO_PRESENT) case (CMU_HFBUS_CLK_BRANCH << CMU_CLK_BRANCH_POS): ret = SystemHFClockGet(); break; #endif case (CMU_HFCORE_CLK_BRANCH << CMU_CLK_BRANCH_POS): ret = SystemHFClockGet(); ret /= 1U + ((CMU->HFCOREPRESC & _CMU_HFCOREPRESC_PRESC_MASK) >> _CMU_HFCOREPRESC_PRESC_SHIFT); break; case (CMU_HFEXP_CLK_BRANCH << CMU_CLK_BRANCH_POS): ret = SystemHFClockGet(); ret /= 1U + ((CMU->HFEXPPRESC & _CMU_HFEXPPRESC_PRESC_MASK) >> _CMU_HFEXPPRESC_PRESC_SHIFT); break; #endif #if defined(_SILICON_LABS_32B_SERIES_0) #if defined(AES_PRESENT) \ || defined(DMA_PRESENT) \ || defined(EBI_PRESENT) \ || defined(USB_PRESENT) case (CMU_HFCORE_CLK_BRANCH << CMU_CLK_BRANCH_POS): { ret = SystemCoreClockGet(); } break; #endif #endif case (CMU_LFA_CLK_BRANCH << CMU_CLK_BRANCH_POS): ret = lfClkGet(cmuClock_LFA); break; #if defined(_CMU_LFACLKEN0_RTC_MASK) case (CMU_RTC_CLK_BRANCH << CMU_CLK_BRANCH_POS): ret = lfClkGet(cmuClock_LFA); ret >>= (CMU->LFAPRESC0 & _CMU_LFAPRESC0_RTC_MASK) >> _CMU_LFAPRESC0_RTC_SHIFT; break; #endif #if defined(_CMU_LFECLKEN0_RTCC_MASK) case (CMU_RTCC_CLK_BRANCH << CMU_CLK_BRANCH_POS): ret = lfClkGet(cmuClock_LFE); break; #endif #if defined(_CMU_LFACLKEN0_LETIMER0_MASK) case (CMU_LETIMER0_CLK_BRANCH << CMU_CLK_BRANCH_POS): ret = lfClkGet(cmuClock_LFA); #if defined(_SILICON_LABS_32B_SERIES_0) ret >>= (CMU->LFAPRESC0 & _CMU_LFAPRESC0_LETIMER0_MASK) >> _CMU_LFAPRESC0_LETIMER0_SHIFT; #else ret /= CMU_Log2ToDiv((CMU->LFAPRESC0 & _CMU_LFAPRESC0_LETIMER0_MASK) >> _CMU_LFAPRESC0_LETIMER0_SHIFT); #endif break; #endif #if defined(_CMU_LFACLKEN0_LCD_MASK) case (CMU_LCDPRE_CLK_BRANCH << CMU_CLK_BRANCH_POS): ret = lfClkGet(cmuClock_LFA); #if defined(_SILICON_LABS_32B_SERIES_0) ret >>= ((CMU->LFAPRESC0 & _CMU_LFAPRESC0_LCD_MASK) >> _CMU_LFAPRESC0_LCD_SHIFT) + CMU_DivToLog2(cmuClkDiv_16); #else ret /= CMU_Log2ToDiv((CMU->LFAPRESC0 & _CMU_LFAPRESC0_LCD_MASK) >> _CMU_LFAPRESC0_LCD_SHIFT); #endif break; #if defined(_CMU_LCDCTRL_MASK) case (CMU_LCD_CLK_BRANCH << CMU_CLK_BRANCH_POS): ret = lfClkGet(cmuClock_LFA); ret >>= (CMU->LFAPRESC0 & _CMU_LFAPRESC0_LCD_MASK) >> _CMU_LFAPRESC0_LCD_SHIFT; ret /= 1U + ((CMU->LCDCTRL & _CMU_LCDCTRL_FDIV_MASK) >> _CMU_LCDCTRL_FDIV_SHIFT); break; #endif #endif #if defined(_CMU_LFACLKEN0_LESENSE_MASK) case (CMU_LESENSE_CLK_BRANCH << CMU_CLK_BRANCH_POS): ret = lfClkGet(cmuClock_LFA); ret >>= (CMU->LFAPRESC0 & _CMU_LFAPRESC0_LESENSE_MASK) >> _CMU_LFAPRESC0_LESENSE_SHIFT; break; #endif case (CMU_LFB_CLK_BRANCH << CMU_CLK_BRANCH_POS): ret = lfClkGet(cmuClock_LFB); break; #if defined(_CMU_LFBCLKEN0_LEUART0_MASK) case (CMU_LEUART0_CLK_BRANCH << CMU_CLK_BRANCH_POS): ret = lfClkGet(cmuClock_LFB); #if defined(_SILICON_LABS_32B_SERIES_0) ret >>= (CMU->LFBPRESC0 & _CMU_LFBPRESC0_LEUART0_MASK) >> _CMU_LFBPRESC0_LEUART0_SHIFT; #else ret /= CMU_Log2ToDiv((CMU->LFBPRESC0 & _CMU_LFBPRESC0_LEUART0_MASK) >> _CMU_LFBPRESC0_LEUART0_SHIFT); #endif break; #endif #if defined(_CMU_LFBCLKEN0_LEUART1_MASK) case (CMU_LEUART1_CLK_BRANCH << CMU_CLK_BRANCH_POS): ret = lfClkGet(cmuClock_LFB); #if defined(_SILICON_LABS_32B_SERIES_0) ret >>= (CMU->LFBPRESC0 & _CMU_LFBPRESC0_LEUART1_MASK) >> _CMU_LFBPRESC0_LEUART1_SHIFT; #else ret /= CMU_Log2ToDiv((CMU->LFBPRESC0 & _CMU_LFBPRESC0_LEUART1_MASK) >> _CMU_LFBPRESC0_LEUART1_SHIFT); #endif break; #endif #if defined(_CMU_LFBCLKEN0_CSEN_MASK) case (CMU_CSEN_LF_CLK_BRANCH << CMU_CLK_BRANCH_POS): ret = lfClkGet(cmuClock_LFB); ret /= CMU_Log2ToDiv(((CMU->LFBPRESC0 & _CMU_LFBPRESC0_CSEN_MASK) >> _CMU_LFBPRESC0_CSEN_SHIFT) + 4); break; #endif #if defined(_SILICON_LABS_32B_SERIES_1) case (CMU_LFE_CLK_BRANCH << CMU_CLK_BRANCH_POS): ret = lfClkGet(cmuClock_LFE); break; #endif case (CMU_DBG_CLK_BRANCH << CMU_CLK_BRANCH_POS): ret = dbgClkGet(); break; case (CMU_AUX_CLK_BRANCH << CMU_CLK_BRANCH_POS): ret = auxClkGet(); break; #if defined(USBC_CLOCK_PRESENT) case (CMU_USBC_CLK_BRANCH << CMU_CLK_BRANCH_POS): ret = usbCClkGet(); break; #endif #if defined(_CMU_ADCCTRL_ADC0CLKSEL_MASK) case (CMU_ADC0ASYNC_CLK_BRANCH << CMU_CLK_BRANCH_POS): ret = adcAsyncClkGet(0); #if defined(_CMU_ADCCTRL_ADC0CLKDIV_MASK) ret /= 1U + ((CMU->ADCCTRL & _CMU_ADCCTRL_ADC0CLKDIV_MASK) >> _CMU_ADCCTRL_ADC0CLKDIV_SHIFT); #endif break; #endif #if defined(_CMU_ADCCTRL_ADC1CLKSEL_MASK) case (CMU_ADC1ASYNC_CLK_BRANCH << CMU_CLK_BRANCH_POS): ret = adcAsyncClkGet(1); #if defined(_CMU_ADCCTRL_ADC1CLKDIV_MASK) ret /= 1U + ((CMU->ADCCTRL & _CMU_ADCCTRL_ADC1CLKDIV_MASK) >> _CMU_ADCCTRL_ADC1CLKDIV_SHIFT); #endif break; #endif #if defined(_CMU_SDIOCTRL_SDIOCLKSEL_MASK) case (CMU_SDIOREF_CLK_BRANCH << CMU_CLK_BRANCH_POS): ret = sdioRefClkGet(); break; #endif #if defined(_CMU_QSPICTRL_QSPI0CLKSEL_MASK) case (CMU_QSPI0REF_CLK_BRANCH << CMU_CLK_BRANCH_POS): ret = qspiRefClkGet(0); break; #endif #if defined(USBR_CLOCK_PRESENT) case (CMU_USBR_CLK_BRANCH << CMU_CLK_BRANCH_POS): ret = usbRateClkGet(); break; #endif default: EFM_ASSERT(0); ret = 0; break; } return ret; } #if defined(_SILICON_LABS_32B_SERIES_1) /***************************************************************************//** * @brief * Get clock prescaler. * * @param[in] clock * Clock point to get the prescaler for. Notice that not all clock points * have a prescaler. Please refer to CMU overview in reference manual. * * @return * The prescaler value of the current clock point. 0 is returned * if @p clock specifies a clock point without a prescaler. ******************************************************************************/ uint32_t CMU_ClockPrescGet(CMU_Clock_TypeDef clock) { uint32_t prescReg; uint32_t ret; /* Get prescaler register id. */ prescReg = (clock >> CMU_PRESC_REG_POS) & CMU_PRESC_REG_MASK; switch (prescReg) { case CMU_HFPRESC_REG: ret = (CMU->HFPRESC & _CMU_HFPRESC_PRESC_MASK) >> _CMU_HFPRESC_PRESC_SHIFT; break; case CMU_HFEXPPRESC_REG: ret = (CMU->HFEXPPRESC & _CMU_HFEXPPRESC_PRESC_MASK) >> _CMU_HFEXPPRESC_PRESC_SHIFT; break; case CMU_HFCLKLEPRESC_REG: ret = (CMU->HFPRESC & _CMU_HFPRESC_HFCLKLEPRESC_MASK) >> _CMU_HFPRESC_HFCLKLEPRESC_SHIFT; break; case CMU_HFPERPRESC_REG: ret = (CMU->HFPERPRESC & _CMU_HFPERPRESC_PRESC_MASK) >> _CMU_HFPERPRESC_PRESC_SHIFT; break; case CMU_HFCOREPRESC_REG: ret = (CMU->HFCOREPRESC & _CMU_HFCOREPRESC_PRESC_MASK) >> _CMU_HFCOREPRESC_PRESC_SHIFT; break; case CMU_LFAPRESC0_REG: switch (clock) { #if defined(_CMU_LFAPRESC0_LETIMER0_MASK) case cmuClock_LETIMER0: ret = (CMU->LFAPRESC0 & _CMU_LFAPRESC0_LETIMER0_MASK) >> _CMU_LFAPRESC0_LETIMER0_SHIFT; /* Convert the exponent to prescaler value. */ ret = CMU_Log2ToDiv(ret) - 1U; break; #endif #if defined(_CMU_LFAPRESC0_LESENSE_MASK) case cmuClock_LESENSE: ret = (CMU->LFAPRESC0 & _CMU_LFAPRESC0_LESENSE_MASK) >> _CMU_LFAPRESC0_LESENSE_SHIFT; /* Convert the exponent to prescaler value. */ ret = CMU_Log2ToDiv(ret) - 1U; break; #endif #if defined(_CMU_LFAPRESC0_LETIMER1_MASK) case cmuClock_LETIMER1: ret = (CMU->LFAPRESC0 & _CMU_LFAPRESC0_LETIMER1_MASK) >> _CMU_LFAPRESC0_LETIMER1_SHIFT; ret = CMU_Log2ToDiv(ret) - 1U; break; #endif #if defined(_CMU_LFAPRESC0_LCD_MASK) case cmuClock_LCD: case cmuClock_LCDpre: ret = (CMU->LFAPRESC0 & _CMU_LFAPRESC0_LCD_MASK) >> _CMU_LFAPRESC0_LCD_SHIFT; ret = CMU_Log2ToDiv(ret) - 1U; break; #endif #if defined(_CMU_LFAPRESC0_RTC_MASK) case cmuClock_RTC: ret = (CMU->LFAPRESC0 & _CMU_LFAPRESC0_RTC_MASK) >> _CMU_LFAPRESC0_RTC_SHIFT; ret = CMU_Log2ToDiv(ret) - 1U; break; #endif default: EFM_ASSERT(0); ret = 0U; break; } break; case CMU_LFBPRESC0_REG: switch (clock) { #if defined(_CMU_LFBPRESC0_LEUART0_MASK) case cmuClock_LEUART0: ret = (CMU->LFBPRESC0 & _CMU_LFBPRESC0_LEUART0_MASK) >> _CMU_LFBPRESC0_LEUART0_SHIFT; /* Convert the exponent to prescaler value. */ ret = CMU_Log2ToDiv(ret) - 1U; break; #endif #if defined(_CMU_LFBPRESC0_LEUART1_MASK) case cmuClock_LEUART1: ret = (CMU->LFBPRESC0 & _CMU_LFBPRESC0_LEUART1_MASK) >> _CMU_LFBPRESC0_LEUART1_SHIFT; /* Convert the exponent to prescaler value. */ ret = CMU_Log2ToDiv(ret) - 1U; break; #endif #if defined(_CMU_LFBPRESC0_CSEN_MASK) case cmuClock_CSEN_LF: ret = (CMU->LFBPRESC0 & _CMU_LFBPRESC0_CSEN_MASK) >> _CMU_LFBPRESC0_CSEN_SHIFT; /* Convert the exponent to prescaler value. */ ret = CMU_Log2ToDiv(ret + 4) - 1U; break; #endif default: EFM_ASSERT(0); ret = 0U; break; } break; case CMU_LFEPRESC0_REG: switch (clock) { #if defined(RTCC_PRESENT) case cmuClock_RTCC: /* No need to compute with LFEPRESC0_RTCC - DIV1 is the only */ /* allowed value. Convert the exponent to prescaler value. */ ret = _CMU_LFEPRESC0_RTCC_DIV1; break; default: EFM_ASSERT(0); ret = 0U; break; #endif } break; case CMU_ADCASYNCDIV_REG: switch (clock) { #if defined(_CMU_ADCCTRL_ADC0CLKDIV_MASK) case cmuClock_ADC0ASYNC: ret = (CMU->ADCCTRL & _CMU_ADCCTRL_ADC0CLKDIV_MASK) >> _CMU_ADCCTRL_ADC0CLKDIV_SHIFT; break; #endif #if defined(_CMU_ADCCTRL_ADC1CLKDIV_MASK) case cmuClock_ADC1ASYNC: ret = (CMU->ADCCTRL & _CMU_ADCCTRL_ADC1CLKDIV_MASK) >> _CMU_ADCCTRL_ADC1CLKDIV_SHIFT; break; #endif default: EFM_ASSERT(0); ret = 0U; break; } break; default: EFM_ASSERT(0); ret = 0U; break; } return ret; } #endif #if defined(_SILICON_LABS_32B_SERIES_1) /***************************************************************************//** * @brief * Set clock prescaler. * * @note * If setting a LF clock prescaler, synchronization into the low frequency * domain is required. If the same register is modified before a previous * update has completed, this function will stall until the previous * synchronization has completed. Please refer to CMU_FreezeEnable() for * a suggestion on how to reduce stalling time in some use cases. * * @param[in] clock * Clock point to set prescaler for. Notice that not all clock points * have a prescaler, please refer to CMU overview in the reference manual. * * @param[in] presc * The clock prescaler to use. ******************************************************************************/ void CMU_ClockPrescSet(CMU_Clock_TypeDef clock, CMU_ClkPresc_TypeDef presc) { uint32_t freq; uint32_t prescReg; /* Get divisor reg id */ prescReg = (clock >> CMU_PRESC_REG_POS) & CMU_PRESC_REG_MASK; switch (prescReg) { case CMU_HFPRESC_REG: EFM_ASSERT(presc < 32U); /* Configure worst case wait-states for flash and HFLE. */ flashWaitStateMax(); setHfLeConfig(CMU_MAX_FREQ_HFLE + 1); CMU->HFPRESC = (CMU->HFPRESC & ~_CMU_HFPRESC_PRESC_MASK) | (presc << _CMU_HFPRESC_PRESC_SHIFT); /* Update CMSIS core clock variable (this function updates the global variable). Optimize flash and HFLE wait states. */ freq = SystemCoreClockGet(); CMU_UpdateWaitStates(freq, VSCALE_DEFAULT); setHfLeConfig(CMU_ClockFreqGet(cmuClock_HFLE)); break; case CMU_HFEXPPRESC_REG: EFM_ASSERT(presc < 32U); CMU->HFEXPPRESC = (CMU->HFEXPPRESC & ~_CMU_HFEXPPRESC_PRESC_MASK) | (presc << _CMU_HFEXPPRESC_PRESC_SHIFT); break; case CMU_HFCLKLEPRESC_REG: #if defined (CMU_HFPRESC_HFCLKLEPRESC_DIV8) EFM_ASSERT(presc < 3U); #else EFM_ASSERT(presc < 2U); #endif /* Specifies the clock divider for HFCLKLE. This clock divider must be set * high enough for the divided clock frequency to be at or below the max * frequency allowed for the HFCLKLE clock. */ CMU->HFPRESC = (CMU->HFPRESC & ~_CMU_HFPRESC_HFCLKLEPRESC_MASK) | (presc << _CMU_HFPRESC_HFCLKLEPRESC_SHIFT); break; case CMU_HFPERPRESC_REG: EFM_ASSERT(presc < 512U); CMU->HFPERPRESC = (CMU->HFPERPRESC & ~_CMU_HFPERPRESC_PRESC_MASK) | (presc << _CMU_HFPERPRESC_PRESC_SHIFT); break; case CMU_HFCOREPRESC_REG: EFM_ASSERT(presc < 512U); /* Configure worst case wait-states for flash and HFLE. */ flashWaitStateMax(); setHfLeConfig(CMU_MAX_FREQ_HFLE + 1); CMU->HFCOREPRESC = (CMU->HFCOREPRESC & ~_CMU_HFCOREPRESC_PRESC_MASK) | (presc << _CMU_HFCOREPRESC_PRESC_SHIFT); /* Update CMSIS core clock variable (this function updates the global variable). Optimize flash and HFLE wait states. */ freq = SystemCoreClockGet(); CMU_UpdateWaitStates(freq, VSCALE_DEFAULT); setHfLeConfig(CMU_ClockFreqGet(cmuClock_HFLE)); break; case CMU_LFAPRESC0_REG: switch (clock) { #if defined(RTC_PRESENT) case cmuClock_RTC: EFM_ASSERT(presc <= 32768U); /* Convert prescaler value to DIV exponent scale. */ presc = CMU_PrescToLog2(presc); /* LF register about to be modified require sync. Busy check. */ syncReg(CMU_SYNCBUSY_LFAPRESC0); CMU->LFAPRESC0 = (CMU->LFAPRESC0 & ~_CMU_LFAPRESC0_RTC_MASK) | (presc << _CMU_LFAPRESC0_RTC_SHIFT); break; #endif #if defined(RTCC_PRESENT) case cmuClock_RTCC: #if defined(_CMU_LFEPRESC0_RTCC_MASK) /* DIV1 is the only accepted value. */ EFM_ASSERT(presc <= 0U); /* LF register about to be modified require sync. Busy check.. */ syncReg(CMU_SYNCBUSY_LFEPRESC0); CMU->LFEPRESC0 = (CMU->LFEPRESC0 & ~_CMU_LFEPRESC0_RTCC_MASK) | (presc << _CMU_LFEPRESC0_RTCC_SHIFT); #else EFM_ASSERT(presc <= 32768U); /* Convert prescaler value to DIV exponent scale. */ presc = CMU_PrescToLog2(presc); /* LF register about to be modified require sync. Busy check. */ syncReg(CMU_SYNCBUSY_LFAPRESC0); CMU->LFAPRESC0 = (CMU->LFAPRESC0 & ~_CMU_LFAPRESC0_RTCC_MASK) | (presc << _CMU_LFAPRESC0_RTCC_SHIFT); #endif break; #endif #if defined(_CMU_LFAPRESC0_LETIMER0_MASK) case cmuClock_LETIMER0: EFM_ASSERT(presc <= 32768U); /* Convert prescaler value to DIV exponent scale. */ presc = CMU_PrescToLog2(presc); /* LF register about to be modified require sync. Busy check. */ syncReg(CMU_SYNCBUSY_LFAPRESC0); CMU->LFAPRESC0 = (CMU->LFAPRESC0 & ~_CMU_LFAPRESC0_LETIMER0_MASK) | (presc << _CMU_LFAPRESC0_LETIMER0_SHIFT); break; #endif #if defined(_CMU_LFAPRESC0_LESENSE_MASK) case cmuClock_LESENSE: EFM_ASSERT(presc <= 8); /* Convert prescaler value to DIV exponent scale. */ presc = CMU_PrescToLog2(presc); /* LF register about to be modified require sync. Busy check. */ syncReg(CMU_SYNCBUSY_LFAPRESC0); CMU->LFAPRESC0 = (CMU->LFAPRESC0 & ~_CMU_LFAPRESC0_LESENSE_MASK) | (presc << _CMU_LFAPRESC0_LESENSE_SHIFT); break; #endif #if defined(_CMU_LFAPRESC0_LCD_MASK) case cmuClock_LCDpre: case cmuClock_LCD: { EFM_ASSERT(presc <= 32768U); /* Convert prescaler value to DIV exponent scale. */ presc = CMU_PrescToLog2(presc); /* LF register about to be modified require sync. Busy check. */ syncReg(CMU_SYNCBUSY_LFAPRESC0); CMU->LFAPRESC0 = (CMU->LFAPRESC0 & ~_CMU_LFAPRESC0_LCD_MASK) | (presc << _CMU_LFAPRESC0_LCD_SHIFT); } break; #endif default: EFM_ASSERT(0); break; } break; case CMU_LFBPRESC0_REG: switch (clock) { #if defined(_CMU_LFBPRESC0_LEUART0_MASK) case cmuClock_LEUART0: EFM_ASSERT(presc <= 8U); /* Convert prescaler value to DIV exponent scale. */ presc = CMU_PrescToLog2(presc); /* LF register about to be modified require sync. Busy check. */ syncReg(CMU_SYNCBUSY_LFBPRESC0); CMU->LFBPRESC0 = (CMU->LFBPRESC0 & ~_CMU_LFBPRESC0_LEUART0_MASK) | (presc << _CMU_LFBPRESC0_LEUART0_SHIFT); break; #endif #if defined(_CMU_LFBPRESC0_LEUART1_MASK) case cmuClock_LEUART1: EFM_ASSERT(presc <= 8U); /* Convert prescaler value to DIV exponent scale. */ presc = CMU_PrescToLog2(presc); /* LF register about to be modified require sync. Busy check. */ syncReg(CMU_SYNCBUSY_LFBPRESC0); CMU->LFBPRESC0 = (CMU->LFBPRESC0 & ~_CMU_LFBPRESC0_LEUART1_MASK) | (presc << _CMU_LFBPRESC0_LEUART1_SHIFT); break; #endif #if defined(_CMU_LFBPRESC0_CSEN_MASK) case cmuClock_CSEN_LF: EFM_ASSERT((presc <= 127U) && (presc >= 15U)); /* Convert prescaler value to DIV exponent scale. * DIV16 is the lowest supported prescaler. */ presc = CMU_PrescToLog2(presc) - 4; /* LF register about to be modified require sync. Busy check. */ syncReg(CMU_SYNCBUSY_LFBPRESC0); CMU->LFBPRESC0 = (CMU->LFBPRESC0 & ~_CMU_LFBPRESC0_CSEN_MASK) | (presc << _CMU_LFBPRESC0_CSEN_SHIFT); break; #endif default: EFM_ASSERT(0); break; } break; case CMU_LFEPRESC0_REG: switch (clock) { #if defined(_CMU_LFEPRESC0_RTCC_MASK) case cmuClock_RTCC: EFM_ASSERT(presc <= 0U); /* LF register about to be modified require sync. Busy check. */ syncReg(CMU_SYNCBUSY_LFEPRESC0); CMU->LFEPRESC0 = (CMU->LFEPRESC0 & ~_CMU_LFEPRESC0_RTCC_MASK) | (presc << _CMU_LFEPRESC0_RTCC_SHIFT); break; #endif default: EFM_ASSERT(0); break; } break; case CMU_ADCASYNCDIV_REG: switch (clock) { #if defined(_CMU_ADCCTRL_ADC0CLKDIV_MASK) case cmuClock_ADC0ASYNC: EFM_ASSERT(presc <= 3); CMU->ADCCTRL = (CMU->ADCCTRL & ~_CMU_ADCCTRL_ADC0CLKDIV_MASK) | (presc << _CMU_ADCCTRL_ADC0CLKDIV_SHIFT); break; #endif #if defined(_CMU_ADCCTRL_ADC1CLKDIV_MASK) case cmuClock_ADC1ASYNC: EFM_ASSERT(presc <= 3); CMU->ADCCTRL = (CMU->ADCCTRL & ~_CMU_ADCCTRL_ADC1CLKDIV_MASK) | (presc << _CMU_ADCCTRL_ADC1CLKDIV_SHIFT); break; #endif default: EFM_ASSERT(0); break; } break; default: EFM_ASSERT(0); break; } } #endif /***************************************************************************//** * @brief * Get currently selected reference clock used for a clock branch. * * @param[in] clock * Clock branch to fetch selected ref. clock for. One of: * @li #cmuClock_HF * @li #cmuClock_LFA * @li #cmuClock_LFB @if _CMU_LFCLKSEL_LFAE_ULFRCO * @li #cmuClock_LFC * @endif @if _SILICON_LABS_32B_SERIES_1 * @li #cmuClock_LFE * @endif * @li #cmuClock_DBG @if DOXYDOC_USB_PRESENT * @li #cmuClock_USBC * @endif * * @return * Reference clock used for clocking selected branch, #cmuSelect_Error if * invalid @p clock provided. ******************************************************************************/ CMU_Select_TypeDef CMU_ClockSelectGet(CMU_Clock_TypeDef clock) { CMU_Select_TypeDef ret = cmuSelect_Disabled; uint32_t selReg; selReg = (clock >> CMU_SEL_REG_POS) & CMU_SEL_REG_MASK; switch (selReg) { case CMU_HFCLKSEL_REG: #if defined(_CMU_HFCLKSTATUS_MASK) switch (CMU->HFCLKSTATUS & _CMU_HFCLKSTATUS_SELECTED_MASK) { case CMU_HFCLKSTATUS_SELECTED_LFXO: ret = cmuSelect_LFXO; break; case CMU_HFCLKSTATUS_SELECTED_LFRCO: ret = cmuSelect_LFRCO; break; case CMU_HFCLKSTATUS_SELECTED_HFXO: ret = cmuSelect_HFXO; break; default: ret = cmuSelect_HFRCO; break; } #else switch (CMU->STATUS & (CMU_STATUS_HFRCOSEL | CMU_STATUS_HFXOSEL | CMU_STATUS_LFRCOSEL #if defined(CMU_STATUS_USHFRCODIV2SEL) | CMU_STATUS_USHFRCODIV2SEL #endif | CMU_STATUS_LFXOSEL)) { case CMU_STATUS_LFXOSEL: ret = cmuSelect_LFXO; break; case CMU_STATUS_LFRCOSEL: ret = cmuSelect_LFRCO; break; case CMU_STATUS_HFXOSEL: ret = cmuSelect_HFXO; break; #if defined(CMU_STATUS_USHFRCODIV2SEL) case CMU_STATUS_USHFRCODIV2SEL: ret = cmuSelect_USHFRCODIV2; break; #endif default: ret = cmuSelect_HFRCO; break; } #endif break; #if defined(_CMU_LFCLKSEL_MASK) || defined(_CMU_LFACLKSEL_MASK) case CMU_LFACLKSEL_REG: #if defined(_CMU_LFCLKSEL_MASK) switch (CMU->LFCLKSEL & _CMU_LFCLKSEL_LFA_MASK) { case CMU_LFCLKSEL_LFA_LFRCO: ret = cmuSelect_LFRCO; break; case CMU_LFCLKSEL_LFA_LFXO: ret = cmuSelect_LFXO; break; #if defined(CMU_LFCLKSEL_LFA_HFCORECLKLEDIV2) case CMU_LFCLKSEL_LFA_HFCORECLKLEDIV2: ret = cmuSelect_HFCLKLE; break; #endif default: #if defined(CMU_LFCLKSEL_LFAE) if (CMU->LFCLKSEL & _CMU_LFCLKSEL_LFAE_MASK) { ret = cmuSelect_ULFRCO; break; } #else ret = cmuSelect_Disabled; #endif break; } #elif defined(_CMU_LFACLKSEL_MASK) switch (CMU->LFACLKSEL & _CMU_LFACLKSEL_LFA_MASK) { case CMU_LFACLKSEL_LFA_LFRCO: ret = cmuSelect_LFRCO; break; case CMU_LFACLKSEL_LFA_LFXO: ret = cmuSelect_LFXO; break; case CMU_LFACLKSEL_LFA_ULFRCO: ret = cmuSelect_ULFRCO; break; #if defined(_CMU_LFACLKSEL_LFA_HFCLKLE) case CMU_LFACLKSEL_LFA_HFCLKLE: ret = cmuSelect_HFCLKLE; break; #endif #if defined(CMU_LFACLKSEL_LFA_PLFRCO) case CMU_LFACLKSEL_LFA_PLFRCO: ret = cmuSelect_PLFRCO; break; #endif default: ret = cmuSelect_Disabled; break; } #endif break; #endif /* _CMU_LFCLKSEL_MASK || _CMU_LFACLKSEL_MASK */ #if defined(_CMU_LFCLKSEL_MASK) || defined(_CMU_LFBCLKSEL_MASK) case CMU_LFBCLKSEL_REG: #if defined(_CMU_LFCLKSEL_MASK) switch (CMU->LFCLKSEL & _CMU_LFCLKSEL_LFB_MASK) { case CMU_LFCLKSEL_LFB_LFRCO: ret = cmuSelect_LFRCO; break; case CMU_LFCLKSEL_LFB_LFXO: ret = cmuSelect_LFXO; break; #if defined(CMU_LFCLKSEL_LFB_HFCORECLKLEDIV2) case CMU_LFCLKSEL_LFB_HFCORECLKLEDIV2: ret = cmuSelect_HFCLKLE; break; #endif #if defined(CMU_LFCLKSEL_LFB_HFCLKLE) case CMU_LFCLKSEL_LFB_HFCLKLE: ret = cmuSelect_HFCLKLE; break; #endif default: #if defined(CMU_LFCLKSEL_LFBE) if (CMU->LFCLKSEL & _CMU_LFCLKSEL_LFBE_MASK) { ret = cmuSelect_ULFRCO; break; } #else ret = cmuSelect_Disabled; #endif break; } #elif defined(_CMU_LFBCLKSEL_MASK) switch (CMU->LFBCLKSEL & _CMU_LFBCLKSEL_LFB_MASK) { case CMU_LFBCLKSEL_LFB_LFRCO: ret = cmuSelect_LFRCO; break; case CMU_LFBCLKSEL_LFB_LFXO: ret = cmuSelect_LFXO; break; case CMU_LFBCLKSEL_LFB_ULFRCO: ret = cmuSelect_ULFRCO; break; case CMU_LFBCLKSEL_LFB_HFCLKLE: ret = cmuSelect_HFCLKLE; break; #if defined(CMU_LFBCLKSEL_LFB_PLFRCO) case CMU_LFBCLKSEL_LFB_PLFRCO: ret = cmuSelect_PLFRCO; break; #endif default: ret = cmuSelect_Disabled; break; } #endif break; #endif /* _CMU_LFCLKSEL_MASK || _CMU_LFBCLKSEL_MASK */ #if defined(_CMU_LFCLKSEL_LFC_MASK) case CMU_LFCCLKSEL_REG: switch (CMU->LFCLKSEL & _CMU_LFCLKSEL_LFC_MASK) { case CMU_LFCLKSEL_LFC_LFRCO: ret = cmuSelect_LFRCO; break; case CMU_LFCLKSEL_LFC_LFXO: ret = cmuSelect_LFXO; break; default: ret = cmuSelect_Disabled; break; } break; #endif #if defined(_CMU_LFECLKSEL_LFE_MASK) case CMU_LFECLKSEL_REG: switch (CMU->LFECLKSEL & _CMU_LFECLKSEL_LFE_MASK) { case CMU_LFECLKSEL_LFE_LFRCO: ret = cmuSelect_LFRCO; break; case CMU_LFECLKSEL_LFE_LFXO: ret = cmuSelect_LFXO; break; case CMU_LFECLKSEL_LFE_ULFRCO: ret = cmuSelect_ULFRCO; break; #if defined (_CMU_LFECLKSEL_LFE_HFCLKLE) case CMU_LFECLKSEL_LFE_HFCLKLE: ret = cmuSelect_HFCLKLE; break; #endif #if defined(CMU_LFECLKSEL_LFE_PLFRCO) case CMU_LFECLKSEL_LFE_PLFRCO: ret = cmuSelect_PLFRCO; break; #endif default: ret = cmuSelect_Disabled; break; } break; #endif /* CMU_LFECLKSEL_REG */ case CMU_DBGCLKSEL_REG: #if defined(_CMU_DBGCLKSEL_DBG_MASK) switch (CMU->DBGCLKSEL & _CMU_DBGCLKSEL_DBG_MASK) { case CMU_DBGCLKSEL_DBG_HFCLK: ret = cmuSelect_HFCLK; break; case CMU_DBGCLKSEL_DBG_AUXHFRCO: ret = cmuSelect_AUXHFRCO; break; } #elif defined(_CMU_CTRL_DBGCLK_MASK) switch (CMU->CTRL & _CMU_CTRL_DBGCLK_MASK) { case CMU_CTRL_DBGCLK_AUXHFRCO: ret = cmuSelect_AUXHFRCO; break; case CMU_CTRL_DBGCLK_HFCLK: ret = cmuSelect_HFCLK; break; } #else ret = cmuSelect_AUXHFRCO; #endif break; #if defined(USBC_CLOCK_PRESENT) case CMU_USBCCLKSEL_REG: switch (CMU->STATUS & (CMU_STATUS_USBCLFXOSEL #if defined(_CMU_STATUS_USBCHFCLKSEL_MASK) | CMU_STATUS_USBCHFCLKSEL #endif #if defined(_CMU_STATUS_USBCUSHFRCOSEL_MASK) | CMU_STATUS_USBCUSHFRCOSEL #endif | CMU_STATUS_USBCLFRCOSEL)) { #if defined(_CMU_STATUS_USBCHFCLKSEL_MASK) case CMU_STATUS_USBCHFCLKSEL: ret = cmuSelect_HFCLK; break; #endif #if defined(_CMU_STATUS_USBCUSHFRCOSEL_MASK) case CMU_STATUS_USBCUSHFRCOSEL: ret = cmuSelect_USHFRCO; break; #endif case CMU_STATUS_USBCLFXOSEL: ret = cmuSelect_LFXO; break; case CMU_STATUS_USBCLFRCOSEL: ret = cmuSelect_LFRCO; break; default: ret = cmuSelect_Disabled; break; } break; #endif #if defined(_CMU_ADCCTRL_ADC0CLKSEL_MASK) case CMU_ADC0ASYNCSEL_REG: switch (CMU->ADCCTRL & _CMU_ADCCTRL_ADC0CLKSEL_MASK) { case CMU_ADCCTRL_ADC0CLKSEL_DISABLED: ret = cmuSelect_Disabled; break; case CMU_ADCCTRL_ADC0CLKSEL_AUXHFRCO: ret = cmuSelect_AUXHFRCO; break; case CMU_ADCCTRL_ADC0CLKSEL_HFXO: ret = cmuSelect_HFXO; break; case CMU_ADCCTRL_ADC0CLKSEL_HFSRCCLK: ret = cmuSelect_HFSRCCLK; break; } break; #endif #if defined(_CMU_ADCCTRL_ADC1CLKSEL_MASK) case CMU_ADC1ASYNCSEL_REG: switch (CMU->ADCCTRL & _CMU_ADCCTRL_ADC1CLKSEL_MASK) { case CMU_ADCCTRL_ADC1CLKSEL_DISABLED: ret = cmuSelect_Disabled; break; case CMU_ADCCTRL_ADC1CLKSEL_AUXHFRCO: ret = cmuSelect_AUXHFRCO; break; case CMU_ADCCTRL_ADC1CLKSEL_HFXO: ret = cmuSelect_HFXO; break; case CMU_ADCCTRL_ADC1CLKSEL_HFSRCCLK: ret = cmuSelect_HFSRCCLK; break; } break; #endif #if defined(_CMU_SDIOCTRL_SDIOCLKSEL_MASK) case CMU_SDIOREFSEL_REG: switch (CMU->SDIOCTRL & _CMU_SDIOCTRL_SDIOCLKSEL_MASK) { case CMU_SDIOCTRL_SDIOCLKSEL_HFRCO: ret = cmuSelect_HFRCO; break; case CMU_SDIOCTRL_SDIOCLKSEL_HFXO: ret = cmuSelect_HFXO; break; case CMU_SDIOCTRL_SDIOCLKSEL_AUXHFRCO: ret = cmuSelect_AUXHFRCO; break; case CMU_SDIOCTRL_SDIOCLKSEL_USHFRCO: ret = cmuSelect_USHFRCO; break; } break; #endif #if defined(_CMU_QSPICTRL_QSPI0CLKSEL_MASK) case CMU_QSPI0REFSEL_REG: switch (CMU->QSPICTRL & _CMU_QSPICTRL_QSPI0CLKSEL_MASK) { case CMU_QSPICTRL_QSPI0CLKSEL_HFRCO: ret = cmuSelect_HFRCO; break; case CMU_QSPICTRL_QSPI0CLKSEL_HFXO: ret = cmuSelect_HFXO; break; case CMU_QSPICTRL_QSPI0CLKSEL_AUXHFRCO: ret = cmuSelect_AUXHFRCO; break; case CMU_QSPICTRL_QSPI0CLKSEL_USHFRCO: ret = cmuSelect_USHFRCO; break; } break; #endif #if defined(_CMU_USBCTRL_USBCLKSEL_MASK) case CMU_USBRCLKSEL_REG: switch (CMU->USBCTRL & _CMU_USBCTRL_USBCLKSEL_MASK) { case CMU_USBCTRL_USBCLKSEL_USHFRCO: ret = cmuSelect_USHFRCO; break; case CMU_USBCTRL_USBCLKSEL_HFXO: ret = cmuSelect_HFXO; break; case CMU_USBCTRL_USBCLKSEL_HFXOX2: ret = cmuSelect_HFXOX2; break; case CMU_USBCTRL_USBCLKSEL_HFRCO: ret = cmuSelect_HFRCO; break; case CMU_USBCTRL_USBCLKSEL_LFXO: ret = cmuSelect_LFXO; break; case CMU_USBCTRL_USBCLKSEL_LFRCO: ret = cmuSelect_LFRCO; break; } break; #endif default: EFM_ASSERT(0); ret = cmuSelect_Error; break; } return ret; } /***************************************************************************//** * @brief * Select reference clock/oscillator used for a clock branch. * * @details * Notice that if a selected reference is not enabled prior to selecting its * use, it will be enabled, and this function will wait for the selected * oscillator to be stable. It will however NOT be disabled if another * reference clock is selected later. * * This feature is particularly important if selecting a new reference * clock for the clock branch clocking the core, otherwise the system * may halt. * * @param[in] clock * Clock branch to select reference clock for. One of: * @li #cmuClock_HF * @li #cmuClock_LFA * @li #cmuClock_LFB * @if _CMU_LFCCLKEN0_MASK * @li #cmuClock_LFC * @endif * @if _CMU_LFECLKEN0_MASK * @li #cmuClock_LFE * @endif * @li #cmuClock_DBG * @if _CMU_CMD_USBCLKSEL_MASK * @li #cmuClock_USBC * @endif * @if _CMU_USBCTRL_MASK * @li #cmuClock_USBR * @endif * * @param[in] ref * Reference selected for clocking, please refer to reference manual for * for details on which reference is available for a specific clock branch. * @li #cmuSelect_HFRCO * @li #cmuSelect_LFRCO * @li #cmuSelect_HFXO * @if _CMU_HFXOCTRL_HFXOX2EN_MASK * @li #cmuSelect_HFXOX2 * @endif * @li #cmuSelect_LFXO * @li #cmuSelect_HFCLKLE * @li #cmuSelect_AUXHFRCO * @if _CMU_USHFRCOCTRL_MASK * @li #cmuSelect_USHFRCO * @endif * @li #cmuSelect_HFCLK * @ifnot DOXYDOC_EFM32_GECKO_FAMILY * @li #cmuSelect_ULFRCO * @li #cmuSelect_PLFRCO * @endif ******************************************************************************/ void CMU_ClockSelectSet(CMU_Clock_TypeDef clock, CMU_Select_TypeDef ref) { uint32_t select = cmuOsc_HFRCO; CMU_Osc_TypeDef osc = cmuOsc_HFRCO; uint32_t freq; uint32_t tmp; uint32_t selRegId; #if defined(_SILICON_LABS_32B_SERIES_1) volatile uint32_t *selReg = NULL; #endif #if defined(CMU_LFCLKSEL_LFAE_ULFRCO) uint32_t lfExtended = 0; #endif #if defined(_EMU_CMD_EM01VSCALE0_MASK) uint32_t vScaleFrequency = 0; /* Use default */ /* Start voltage upscaling before clock is set. */ if (clock == cmuClock_HF) { if (ref == cmuSelect_HFXO) { vScaleFrequency = SystemHFXOClockGet(); } else if ((ref == cmuSelect_HFRCO) && (CMU_HFRCOBandGet() > CMU_VSCALEEM01_LOWPOWER_VOLTAGE_CLOCK_MAX)) { vScaleFrequency = CMU_HFRCOBandGet(); } if (vScaleFrequency != 0) { EMU_VScaleEM01ByClock(vScaleFrequency, false); } } #endif selRegId = (clock >> CMU_SEL_REG_POS) & CMU_SEL_REG_MASK; switch (selRegId) { case CMU_HFCLKSEL_REG: switch (ref) { case cmuSelect_LFXO: #if defined(_SILICON_LABS_32B_SERIES_1) select = CMU_HFCLKSEL_HF_LFXO; #elif defined(_SILICON_LABS_32B_SERIES_0) select = CMU_CMD_HFCLKSEL_LFXO; #endif osc = cmuOsc_LFXO; break; case cmuSelect_LFRCO: #if defined(_SILICON_LABS_32B_SERIES_1) select = CMU_HFCLKSEL_HF_LFRCO; #elif defined(_SILICON_LABS_32B_SERIES_0) select = CMU_CMD_HFCLKSEL_LFRCO; #endif osc = cmuOsc_LFRCO; break; case cmuSelect_HFXO: #if defined(CMU_HFCLKSEL_HF_HFXO) select = CMU_HFCLKSEL_HF_HFXO; #elif defined(CMU_CMD_HFCLKSEL_HFXO) select = CMU_CMD_HFCLKSEL_HFXO; #endif osc = cmuOsc_HFXO; #if defined(CMU_MAX_FREQ_HFLE) /* Set 1 HFLE wait-state until the new HFCLKLE frequency is known. This is known after 'select' is written below. */ setHfLeConfig(CMU_MAX_FREQ_HFLE + 1); #endif #if defined(CMU_CTRL_HFXOBUFCUR_BOOSTABOVE32MHZ) /* Adjust HFXO buffer current for frequencies above 32MHz */ if (SystemHFXOClockGet() > 32000000) { CMU->CTRL = (CMU->CTRL & ~_CMU_CTRL_HFXOBUFCUR_MASK) | CMU_CTRL_HFXOBUFCUR_BOOSTABOVE32MHZ; } else { CMU->CTRL = (CMU->CTRL & ~_CMU_CTRL_HFXOBUFCUR_MASK) | CMU_CTRL_HFXOBUFCUR_BOOSTUPTO32MHZ; } #endif break; case cmuSelect_HFRCO: #if defined(_SILICON_LABS_32B_SERIES_1) select = CMU_HFCLKSEL_HF_HFRCO; #elif defined(_SILICON_LABS_32B_SERIES_0) select = CMU_CMD_HFCLKSEL_HFRCO; #endif osc = cmuOsc_HFRCO; #if defined(CMU_MAX_FREQ_HFLE) /* Set 1 HFLE wait-state until the new HFCLKLE frequency is known. This is known after 'select' is written below. */ setHfLeConfig(CMU_MAX_FREQ_HFLE + 1); #endif break; #if defined(CMU_CMD_HFCLKSEL_USHFRCODIV2) case cmuSelect_USHFRCODIV2: select = CMU_CMD_HFCLKSEL_USHFRCODIV2; osc = cmuOsc_USHFRCO; break; #endif #if defined(CMU_LFCLKSEL_LFAE_ULFRCO) || defined(CMU_LFACLKSEL_LFA_ULFRCO) case cmuSelect_ULFRCO: /* ULFRCO cannot be used as HFCLK */ EFM_ASSERT(0); return; #endif default: EFM_ASSERT(0); return; } /* Ensure selected oscillator is enabled, waiting for it to stabilize */ CMU_OscillatorEnable(osc, true, true); /* Configure worst case wait states for flash access before selecting */ flashWaitStateMax(); #if defined(_EMU_CMD_EM01VSCALE0_MASK) /* Wait for voltage upscaling to complete before clock is set. */ if (vScaleFrequency != 0) { EMU_VScaleWait(); } #endif /* Switch to selected oscillator */ #if defined(_CMU_HFCLKSEL_MASK) CMU->HFCLKSEL = select; #else CMU->CMD = select; #endif #if defined(CMU_MAX_FREQ_HFLE) /* Update HFLE configuration after 'select' is set. Note that the HFCLKLE clock is connected differently on planform 1 and 2 */ setHfLeConfig(CMU_ClockFreqGet(cmuClock_HFLE)); #endif /* Update CMSIS core clock variable */ /* (The function will update the global variable) */ freq = SystemCoreClockGet(); /* Optimize flash access wait state setting for currently selected core clk */ CMU_UpdateWaitStates(freq, VSCALE_DEFAULT); #if defined(_EMU_CMD_EM01VSCALE0_MASK) /* Keep EMU module informed on source HF clock frequency. This will apply voltage downscaling after clock is set if downscaling is configured. */ if (vScaleFrequency == 0) { EMU_VScaleEM01ByClock(0, true); } #endif break; #if defined(_SILICON_LABS_32B_SERIES_1) case CMU_LFACLKSEL_REG: selReg = (selReg == NULL) ? &CMU->LFACLKSEL : selReg; #if !defined(_CMU_LFACLKSEL_LFA_HFCLKLE) /* HFCLKCLE can not be used as LFACLK */ EFM_ASSERT(ref != cmuSelect_HFCLKLE); #endif /* Fall through and select clock source */ #if defined(_CMU_LFCCLKSEL_MASK) case CMU_LFCCLKSEL_REG: selReg = (selReg == NULL) ? &CMU->LFCCLKSEL : selReg; #if !defined(_CMU_LFCCLKSEL_LFC_HFCLKLE) /* HFCLKCLE can not be used as LFCCLK */ EFM_ASSERT(ref != cmuSelect_HFCLKLE); #endif #endif /* Fall through and select clock source */ case CMU_LFECLKSEL_REG: selReg = (selReg == NULL) ? &CMU->LFECLKSEL : selReg; #if !defined(_CMU_LFECLKSEL_LFE_HFCLKLE) /* HFCLKCLE can not be used as LFECLK */ EFM_ASSERT(ref != cmuSelect_HFCLKLE); #endif /* Fall through and select clock source */ case CMU_LFBCLKSEL_REG: selReg = (selReg == NULL) ? &CMU->LFBCLKSEL : selReg; switch (ref) { case cmuSelect_Disabled: tmp = _CMU_LFACLKSEL_LFA_DISABLED; break; case cmuSelect_LFXO: /* Ensure selected oscillator is enabled, waiting for it to stabilize */ CMU_OscillatorEnable(cmuOsc_LFXO, true, true); tmp = _CMU_LFACLKSEL_LFA_LFXO; break; case cmuSelect_LFRCO: /* Ensure selected oscillator is enabled, waiting for it to stabilize */ CMU_OscillatorEnable(cmuOsc_LFRCO, true, true); tmp = _CMU_LFACLKSEL_LFA_LFRCO; break; case cmuSelect_HFCLKLE: /* Ensure correct HFLE wait-states and enable HFCLK to LE */ setHfLeConfig(SystemCoreClockGet()); BUS_RegBitWrite(&CMU->HFBUSCLKEN0, _CMU_HFBUSCLKEN0_LE_SHIFT, 1); tmp = _CMU_LFBCLKSEL_LFB_HFCLKLE; break; case cmuSelect_ULFRCO: /* ULFRCO is always on, there is no need to enable it. */ tmp = _CMU_LFACLKSEL_LFA_ULFRCO; break; #if defined(_CMU_STATUS_PLFRCOENS_MASK) case cmuSelect_PLFRCO: /* Ensure selected oscillator is enabled, waiting for it to stabilize */ CMU_OscillatorEnable(cmuOsc_PLFRCO, true, true); tmp = _CMU_LFACLKSEL_LFA_PLFRCO; break; #endif default: EFM_ASSERT(0); return; } *selReg = tmp; break; #elif defined(_SILICON_LABS_32B_SERIES_0) case CMU_LFACLKSEL_REG: case CMU_LFBCLKSEL_REG: switch (ref) { case cmuSelect_Disabled: tmp = _CMU_LFCLKSEL_LFA_DISABLED; break; case cmuSelect_LFXO: /* Ensure selected oscillator is enabled, waiting for it to stabilize */ CMU_OscillatorEnable(cmuOsc_LFXO, true, true); tmp = _CMU_LFCLKSEL_LFA_LFXO; break; case cmuSelect_LFRCO: /* Ensure selected oscillator is enabled, waiting for it to stabilize */ CMU_OscillatorEnable(cmuOsc_LFRCO, true, true); tmp = _CMU_LFCLKSEL_LFA_LFRCO; break; case cmuSelect_HFCLKLE: #if defined(CMU_MAX_FREQ_HFLE) /* Set HFLE wait-state and divider */ freq = SystemCoreClockGet(); setHfLeConfig(freq); #endif /* Ensure HFCORE to LE clocking is enabled */ BUS_RegBitWrite(&CMU->HFCORECLKEN0, _CMU_HFCORECLKEN0_LE_SHIFT, 1); tmp = _CMU_LFCLKSEL_LFA_HFCORECLKLEDIV2; break; #if defined(CMU_LFCLKSEL_LFAE_ULFRCO) case cmuSelect_ULFRCO: /* ULFRCO is always enabled */ tmp = _CMU_LFCLKSEL_LFA_DISABLED; lfExtended = 1; break; #endif default: /* Illegal clock source for LFA/LFB selected */ EFM_ASSERT(0); return; } /* Apply select */ if (selRegId == CMU_LFACLKSEL_REG) { #if defined(_CMU_LFCLKSEL_LFAE_MASK) CMU->LFCLKSEL = (CMU->LFCLKSEL & ~(_CMU_LFCLKSEL_LFA_MASK | _CMU_LFCLKSEL_LFAE_MASK)) | (tmp << _CMU_LFCLKSEL_LFA_SHIFT) | (lfExtended << _CMU_LFCLKSEL_LFAE_SHIFT); #else CMU->LFCLKSEL = (CMU->LFCLKSEL & ~_CMU_LFCLKSEL_LFA_MASK) | (tmp << _CMU_LFCLKSEL_LFA_SHIFT); #endif } else { #if defined(_CMU_LFCLKSEL_LFBE_MASK) CMU->LFCLKSEL = (CMU->LFCLKSEL & ~(_CMU_LFCLKSEL_LFB_MASK | _CMU_LFCLKSEL_LFBE_MASK)) | (tmp << _CMU_LFCLKSEL_LFB_SHIFT) | (lfExtended << _CMU_LFCLKSEL_LFBE_SHIFT); #else CMU->LFCLKSEL = (CMU->LFCLKSEL & ~_CMU_LFCLKSEL_LFB_MASK) | (tmp << _CMU_LFCLKSEL_LFB_SHIFT); #endif } break; #if defined(_CMU_LFCLKSEL_LFC_MASK) case CMU_LFCCLKSEL_REG: switch (ref) { case cmuSelect_Disabled: tmp = _CMU_LFCLKSEL_LFA_DISABLED; break; case cmuSelect_LFXO: /* Ensure selected oscillator is enabled, waiting for it to stabilize */ CMU_OscillatorEnable(cmuOsc_LFXO, true, true); tmp = _CMU_LFCLKSEL_LFC_LFXO; break; case cmuSelect_LFRCO: /* Ensure selected oscillator is enabled, waiting for it to stabilize */ CMU_OscillatorEnable(cmuOsc_LFRCO, true, true); tmp = _CMU_LFCLKSEL_LFC_LFRCO; break; default: /* Illegal clock source for LFC selected */ EFM_ASSERT(0); return; } /* Apply select */ CMU->LFCLKSEL = (CMU->LFCLKSEL & ~_CMU_LFCLKSEL_LFC_MASK) | (tmp << _CMU_LFCLKSEL_LFC_SHIFT); break; #endif #endif #if defined(_CMU_DBGCLKSEL_DBG_MASK) || defined(CMU_CTRL_DBGCLK) case CMU_DBGCLKSEL_REG: switch (ref) { #if defined(_CMU_DBGCLKSEL_DBG_MASK) case cmuSelect_AUXHFRCO: /* Select AUXHFRCO as debug clock */ CMU->DBGCLKSEL = CMU_DBGCLKSEL_DBG_AUXHFRCO; break; case cmuSelect_HFCLK: /* Select divided HFCLK as debug clock */ CMU->DBGCLKSEL = CMU_DBGCLKSEL_DBG_HFCLK; break; #endif #if defined(CMU_CTRL_DBGCLK) case cmuSelect_AUXHFRCO: /* Select AUXHFRCO as debug clock */ CMU->CTRL = (CMU->CTRL & ~(_CMU_CTRL_DBGCLK_MASK)) | CMU_CTRL_DBGCLK_AUXHFRCO; break; case cmuSelect_HFCLK: /* Select divided HFCLK as debug clock */ CMU->CTRL = (CMU->CTRL & ~(_CMU_CTRL_DBGCLK_MASK)) | CMU_CTRL_DBGCLK_HFCLK; break; #endif default: /* Illegal clock source for debug selected */ EFM_ASSERT(0); return; } break; #endif #if defined(USBC_CLOCK_PRESENT) case CMU_USBCCLKSEL_REG: switch (ref) { case cmuSelect_LFXO: /* Select LFXO as clock source for USB, can only be used in sleep mode */ /* Ensure selected oscillator is enabled, waiting for it to stabilize */ CMU_OscillatorEnable(cmuOsc_LFXO, true, true); /* Switch oscillator */ CMU->CMD = CMU_CMD_USBCCLKSEL_LFXO; /* Wait until clock is activated */ while ((CMU->STATUS & CMU_STATUS_USBCLFXOSEL) == 0) { } break; case cmuSelect_LFRCO: /* Select LFRCO as clock source for USB, can only be used in sleep mode */ /* Ensure selected oscillator is enabled, waiting for it to stabilize */ CMU_OscillatorEnable(cmuOsc_LFRCO, true, true); /* Switch oscillator */ CMU->CMD = CMU_CMD_USBCCLKSEL_LFRCO; /* Wait until clock is activated */ while ((CMU->STATUS & CMU_STATUS_USBCLFRCOSEL) == 0) { } break; #if defined(CMU_STATUS_USBCHFCLKSEL) case cmuSelect_HFCLK: /* Select undivided HFCLK as clock source for USB */ /* Oscillator must already be enabled to avoid a core lockup */ CMU->CMD = CMU_CMD_USBCCLKSEL_HFCLKNODIV; /* Wait until clock is activated */ while ((CMU->STATUS & CMU_STATUS_USBCHFCLKSEL) == 0) { } break; #endif #if defined(CMU_CMD_USBCCLKSEL_USHFRCO) case cmuSelect_USHFRCO: /* Select USHFRCO as clock source for USB */ /* Ensure selected oscillator is enabled, waiting for it to stabilize */ CMU_OscillatorEnable(cmuOsc_USHFRCO, true, true); /* Switch oscillator */ CMU->CMD = CMU_CMD_USBCCLKSEL_USHFRCO; /* Wait until clock is activated */ while ((CMU->STATUS & CMU_STATUS_USBCUSHFRCOSEL) == 0) { } break; #endif default: /* Illegal clock source for USB */ EFM_ASSERT(0); return; } break; #endif #if defined(_CMU_ADCCTRL_ADC0CLKSEL_MASK) case CMU_ADC0ASYNCSEL_REG: switch (ref) { case cmuSelect_Disabled: tmp = _CMU_ADCCTRL_ADC0CLKSEL_DISABLED; break; case cmuSelect_AUXHFRCO: /* Ensure selected oscillator is enabled, waiting for it to stabilize */ CMU_OscillatorEnable(cmuOsc_AUXHFRCO, true, true); tmp = _CMU_ADCCTRL_ADC0CLKSEL_AUXHFRCO; break; case cmuSelect_HFXO: /* Ensure selected oscillator is enabled, waiting for it to stabilize */ CMU_OscillatorEnable(cmuOsc_HFXO, true, true); tmp = _CMU_ADCCTRL_ADC0CLKSEL_HFXO; break; case cmuSelect_HFSRCCLK: tmp = _CMU_ADCCTRL_ADC0CLKSEL_HFSRCCLK; break; default: /* Illegal clock source for ADC0ASYNC selected */ EFM_ASSERT(0); return; } /* Apply select */ CMU->ADCCTRL = (CMU->ADCCTRL & ~_CMU_ADCCTRL_ADC0CLKSEL_MASK) | (tmp << _CMU_ADCCTRL_ADC0CLKSEL_SHIFT); break; #endif #if defined(_CMU_ADCCTRL_ADC1CLKSEL_MASK) case CMU_ADC1ASYNCSEL_REG: switch (ref) { case cmuSelect_Disabled: tmp = _CMU_ADCCTRL_ADC1CLKSEL_DISABLED; break; case cmuSelect_AUXHFRCO: /* Ensure selected oscillator is enabled, waiting for it to stabilize */ CMU_OscillatorEnable(cmuOsc_AUXHFRCO, true, true); tmp = _CMU_ADCCTRL_ADC1CLKSEL_AUXHFRCO; break; case cmuSelect_HFXO: /* Ensure selected oscillator is enabled, waiting for it to stabilize */ CMU_OscillatorEnable(cmuOsc_HFXO, true, true); tmp = _CMU_ADCCTRL_ADC1CLKSEL_HFXO; break; case cmuSelect_HFSRCCLK: tmp = _CMU_ADCCTRL_ADC1CLKSEL_HFSRCCLK; break; default: /* Illegal clock source for ADC1ASYNC selected */ EFM_ASSERT(0); return; } /* Apply select */ CMU->ADCCTRL = (CMU->ADCCTRL & ~_CMU_ADCCTRL_ADC1CLKSEL_MASK) | (tmp << _CMU_ADCCTRL_ADC1CLKSEL_SHIFT); break; #endif #if defined(_CMU_SDIOCTRL_SDIOCLKSEL_MASK) case CMU_SDIOREFSEL_REG: switch (ref) { case cmuSelect_HFRCO: /* Ensure selected oscillator is enabled, waiting for it to stabilize */ CMU_OscillatorEnable(cmuOsc_HFRCO, true, true); tmp = _CMU_SDIOCTRL_SDIOCLKSEL_HFRCO; break; case cmuSelect_HFXO: /* Ensure selected oscillator is enabled, waiting for it to stabilize */ CMU_OscillatorEnable(cmuOsc_HFXO, true, true); tmp = _CMU_SDIOCTRL_SDIOCLKSEL_HFXO; break; case cmuSelect_AUXHFRCO: /* Ensure selected oscillator is enabled, waiting for it to stabilize */ CMU_OscillatorEnable(cmuOsc_AUXHFRCO, true, true); tmp = _CMU_SDIOCTRL_SDIOCLKSEL_AUXHFRCO; break; case cmuSelect_USHFRCO: /* Ensure selected oscillator is enabled, waiting for it to stabilize */ CMU_OscillatorEnable(cmuOsc_USHFRCO, true, true); tmp = _CMU_SDIOCTRL_SDIOCLKSEL_USHFRCO; break; default: /* Illegal clock source for SDIOREF selected */ EFM_ASSERT(0); return; } /* Apply select */ CMU->SDIOCTRL = (CMU->SDIOCTRL & ~_CMU_SDIOCTRL_SDIOCLKSEL_MASK) | (tmp << _CMU_SDIOCTRL_SDIOCLKSEL_SHIFT); break; #endif #if defined(_CMU_QSPICTRL_QSPI0CLKSEL_MASK) case CMU_QSPI0REFSEL_REG: switch (ref) { case cmuSelect_HFRCO: /* Ensure selected oscillator is enabled, waiting for it to stabilize */ CMU_OscillatorEnable(cmuOsc_HFRCO, true, true); tmp = _CMU_QSPICTRL_QSPI0CLKSEL_HFRCO; break; case cmuSelect_HFXO: /* Ensure selected oscillator is enabled, waiting for it to stabilize */ CMU_OscillatorEnable(cmuOsc_HFXO, true, true); tmp = _CMU_QSPICTRL_QSPI0CLKSEL_HFXO; break; case cmuSelect_AUXHFRCO: /* Ensure selected oscillator is enabled, waiting for it to stabilize */ CMU_OscillatorEnable(cmuOsc_AUXHFRCO, true, true); tmp = _CMU_QSPICTRL_QSPI0CLKSEL_AUXHFRCO; break; case cmuSelect_USHFRCO: /* Ensure selected oscillator is enabled, waiting for it to stabilize */ CMU_OscillatorEnable(cmuOsc_USHFRCO, true, true); tmp = _CMU_QSPICTRL_QSPI0CLKSEL_USHFRCO; break; default: /* Illegal clock source for QSPI0REF selected */ EFM_ASSERT(0); return; } /* Apply select */ CMU->QSPICTRL = (CMU->QSPICTRL & ~_CMU_QSPICTRL_QSPI0CLKSEL_MASK) | (tmp << _CMU_QSPICTRL_QSPI0CLKSEL_SHIFT); break; #endif #if defined(_CMU_USBCTRL_USBCLKSEL_MASK) case CMU_USBRCLKSEL_REG: switch (ref) { case cmuSelect_USHFRCO: /* Ensure selected oscillator is enabled, waiting for it to stabilize */ CMU_OscillatorEnable(cmuOsc_USHFRCO, true, true); tmp = _CMU_USBCTRL_USBCLKSEL_USHFRCO; break; case cmuSelect_HFXO: /* Ensure selected oscillator is enabled, waiting for it to stabilize */ CMU_OscillatorEnable(cmuOsc_HFXO, true, true); tmp = _CMU_USBCTRL_USBCLKSEL_HFXO; break; case cmuSelect_HFXOX2: /* Only allowed for HFXO frequencies up to 25 MHz */ EFM_ASSERT(SystemHFXOClockGet() <= 25000000u); /* Enable HFXO X2 */ CMU->HFXOCTRL |= CMU_HFXOCTRL_HFXOX2EN; /* Ensure selected oscillator is enabled, waiting for it to stabilize */ CMU_OscillatorEnable(cmuOsc_HFXO, true, true); tmp = _CMU_USBCTRL_USBCLKSEL_HFXOX2; break; case cmuSelect_HFRCO: /* Ensure selected oscillator is enabled, waiting for it to stabilize */ CMU_OscillatorEnable(cmuOsc_HFRCO, true, true); tmp = _CMU_USBCTRL_USBCLKSEL_HFRCO; break; case cmuSelect_LFXO: /* Ensure selected oscillator is enabled, waiting for it to stabilize */ CMU_OscillatorEnable(cmuOsc_LFXO, true, true); tmp = _CMU_USBCTRL_USBCLKSEL_LFXO; break; case cmuSelect_LFRCO: /* Ensure selected oscillator is enabled, waiting for it to stabilize */ CMU_OscillatorEnable(cmuOsc_LFRCO, true, true); tmp = _CMU_USBCTRL_USBCLKSEL_LFRCO; break; default: /* Illegal clock source for USBR selected */ EFM_ASSERT(0); return; } /* Apply select */ CMU->USBCTRL = (CMU->USBCTRL & ~_CMU_USBCTRL_USBCLKSEL_MASK) | (tmp << _CMU_USBCTRL_USBCLKSEL_SHIFT); break; #endif default: EFM_ASSERT(0); break; } } #if defined(CMU_OSCENCMD_DPLLEN) /**************************************************************************//** * @brief * Lock the DPLL to a given frequency. * * The frequency is given by: Fout = Fref * (N+1) / (M+1). * * @note * This function does not check if the given N & M values will actually * produce the desired target frequency. * Any peripheral running off HFRCO should be switched to HFRCODIV2 prior to * calling this function to avoid over-clocking. * * @param[in] init * DPLL setup parameters. * * @return * Returns false on invalid target frequency or DPLL locking error. *****************************************************************************/ bool CMU_DPLLLock(CMU_DPLLInit_TypeDef *init) { int index = 0; unsigned int i; bool hfrcoDiv2 = false; uint32_t hfrcoCtrlVal, lockStatus, sysFreq; EFM_ASSERT(init->frequency >= hfrcoCtrlTable[0].minFreq); EFM_ASSERT(init->frequency <= hfrcoCtrlTable[HFRCOCTRLTABLE_ENTRIES - 1].maxFreq); EFM_ASSERT(init->n >= 32); EFM_ASSERT(init->n <= (_CMU_DPLLCTRL1_N_MASK >> _CMU_DPLLCTRL1_N_SHIFT)); EFM_ASSERT(init->m <= (_CMU_DPLLCTRL1_M_MASK >> _CMU_DPLLCTRL1_M_SHIFT)); EFM_ASSERT(init->ssInterval <= (_CMU_HFRCOSS_SSINV_MASK >> _CMU_HFRCOSS_SSINV_SHIFT)); EFM_ASSERT(init->ssAmplitude <= (_CMU_HFRCOSS_SSAMP_MASK >> _CMU_HFRCOSS_SSAMP_SHIFT)); #if defined(_EMU_STATUS_VSCALE_MASK) if ((EMU_VScaleGet() == emuVScaleEM01_LowPower) && (init->frequency > CMU_VSCALEEM01_LOWPOWER_VOLTAGE_CLOCK_MAX)) { EFM_ASSERT(false); return false; } #endif // Find correct HFRCO band, and retrieve a HFRCOCTRL value. for (i = 0; i < HFRCOCTRLTABLE_ENTRIES; i++) { if ((init->frequency >= hfrcoCtrlTable[i].minFreq) && (init->frequency <= hfrcoCtrlTable[i].maxFreq)) { index = i; // Correct band found break; } } if (index == HFRCOCTRLTABLE_ENTRIES) { EFM_ASSERT(false); return false; // Target frequency out of spec. } hfrcoCtrlVal = hfrcoCtrlTable[index].value; // Check if we have a calibrated HFRCOCTRL.TUNING value in device DI page. if (hfrcoCtrlTable[index].band != (CMU_HFRCOFreq_TypeDef)0) { uint32_t tuning; tuning = (CMU_HFRCODevinfoGet(hfrcoCtrlTable[index].band) & _CMU_HFRCOCTRL_TUNING_MASK) >> _CMU_HFRCOCTRL_TUNING_SHIFT; // When HFRCOCTRL.FINETUNINGEN is enabled, the center frequency // of the band shifts down by 5.8%. We subtract 9 to compensate. if (tuning > 9) { tuning -= 9; } else { tuning = 0; } hfrcoCtrlVal |= tuning << _CMU_HFRCOCTRL_TUNING_SHIFT; } // Update CMSIS frequency SystemHfrcoFreq value. SystemHfrcoFreq = init->frequency; // Set max wait-states while changing core clock. if (CMU_ClockSelectGet(cmuClock_HF) == cmuSelect_HFRCO) { flashWaitStateMax(); } // Update HFLE configuration before updating HFRCO, use new DPLL frequency. if (CMU_ClockSelectGet(cmuClock_HF) == cmuSelect_HFRCO) { setHfLeConfig(init->frequency); // Switch to HFRCO/2 before setting DPLL to avoid over-clocking. hfrcoDiv2 = (CMU->HFCLKSTATUS & _CMU_HFCLKSTATUS_SELECTED_MASK) == CMU_HFCLKSTATUS_SELECTED_HFRCODIV2; CMU->HFCLKSEL = CMU_HFCLKSEL_HF_HFRCODIV2; } CMU->OSCENCMD = CMU_OSCENCMD_DPLLDIS; while ((CMU->STATUS & (CMU_STATUS_DPLLENS | CMU_STATUS_DPLLRDY)) != 0) ; CMU->IFC = CMU_IFC_DPLLRDY | CMU_IFC_DPLLLOCKFAILLOW | CMU_IFC_DPLLLOCKFAILHIGH; CMU->DPLLCTRL1 = (init->n << _CMU_DPLLCTRL1_N_SHIFT) | (init->m << _CMU_DPLLCTRL1_M_SHIFT); CMU->HFRCOCTRL = hfrcoCtrlVal; CMU->DPLLCTRL = (init->refClk << _CMU_DPLLCTRL_REFSEL_SHIFT) | (init->autoRecover << _CMU_DPLLCTRL_AUTORECOVER_SHIFT) | (init->edgeSel << _CMU_DPLLCTRL_EDGESEL_SHIFT) | (init->lockMode << _CMU_DPLLCTRL_MODE_SHIFT); CMU->OSCENCMD = CMU_OSCENCMD_DPLLEN; while ((lockStatus = (CMU->IF & (CMU_IF_DPLLRDY | CMU_IF_DPLLLOCKFAILLOW | CMU_IF_DPLLLOCKFAILHIGH))) == 0) ; if ((CMU_ClockSelectGet(cmuClock_HF) == cmuSelect_HFRCO) && (hfrcoDiv2 == false)) { CMU->HFCLKSEL = CMU_HFCLKSEL_HF_HFRCO; } // If HFRCO is selected as HF clock, optimize flash access wait-state // configuration for this frequency and update CMSIS core clock variable. if (CMU_ClockSelectGet(cmuClock_HF) == cmuSelect_HFRCO) { // Call SystemCoreClockGet() to update CMSIS core clock variable. sysFreq = SystemCoreClockGet(); EFM_ASSERT(sysFreq <= init->frequency); EFM_ASSERT(sysFreq <= SystemHfrcoFreq); EFM_ASSERT(init->frequency == SystemHfrcoFreq); CMU_UpdateWaitStates(sysFreq, VSCALE_DEFAULT); } // Reduce HFLE frequency if possible. setHfLeConfig(CMU_ClockFreqGet(cmuClock_HFLE)); #if defined(_EMU_CMD_EM01VSCALE0_MASK) // Update voltage scaling. EMU_VScaleEM01ByClock(0, true); #endif if (lockStatus == CMU_IF_DPLLRDY) { return true; } return false; } #endif // CMU_OSCENCMD_DPLLEN /**************************************************************************//** * @brief * CMU low frequency register synchronization freeze control. * * @details * Some CMU registers requires synchronization into the low frequency (LF) * domain. The freeze feature allows for several such registers to be * modified before passing them to the LF domain simultaneously (which * takes place when the freeze mode is disabled). * * Another usage scenario of this feature, is when using an API (such * as the CMU API) for modifying several bit fields consecutively in the * same register. If freeze mode is enabled during this sequence, stalling * can be avoided. * * @note * When enabling freeze mode, this function will wait for all current * ongoing CMU synchronization to LF domain to complete (Normally * synchronization will not be in progress.) However for this reason, when * using freeze mode, modifications of registers requiring LF synchronization * should be done within one freeze enable/disable block to avoid unecessary * stalling. * * @param[in] enable * @li true - enable freeze, modified registers are not propagated to the * LF domain * @li false - disable freeze, modified registers are propagated to LF * domain *****************************************************************************/ void CMU_FreezeEnable(bool enable) { if (enable) { /* Wait for any ongoing LF synchronization to complete. This is just to */ /* protect against the rare case when a user */ /* - modifies a register requiring LF sync */ /* - then enables freeze before LF sync completed */ /* - then modifies the same register again */ /* since modifying a register while it is in sync progress should be */ /* avoided. */ while (CMU->SYNCBUSY) { } CMU->FREEZE = CMU_FREEZE_REGFREEZE; } else { CMU->FREEZE = 0; } } #if defined(_CMU_HFRCOCTRL_BAND_MASK) /***************************************************************************//** * @brief * Get HFRCO band in use. * * @return * HFRCO band in use. ******************************************************************************/ CMU_HFRCOBand_TypeDef CMU_HFRCOBandGet(void) { return (CMU_HFRCOBand_TypeDef)((CMU->HFRCOCTRL & _CMU_HFRCOCTRL_BAND_MASK) >> _CMU_HFRCOCTRL_BAND_SHIFT); } #endif /* _CMU_HFRCOCTRL_BAND_MASK */ #if defined(_CMU_HFRCOCTRL_BAND_MASK) /***************************************************************************//** * @brief * Set HFRCO band and the tuning value based on the value in the calibration * table made during production. * * @param[in] band * HFRCO band to activate. ******************************************************************************/ void CMU_HFRCOBandSet(CMU_HFRCOBand_TypeDef band) { uint32_t tuning; uint32_t freq; CMU_Select_TypeDef osc; /* Read tuning value from calibration table */ switch (band) { case cmuHFRCOBand_1MHz: tuning = (DEVINFO->HFRCOCAL0 & _DEVINFO_HFRCOCAL0_BAND1_MASK) >> _DEVINFO_HFRCOCAL0_BAND1_SHIFT; break; case cmuHFRCOBand_7MHz: tuning = (DEVINFO->HFRCOCAL0 & _DEVINFO_HFRCOCAL0_BAND7_MASK) >> _DEVINFO_HFRCOCAL0_BAND7_SHIFT; break; case cmuHFRCOBand_11MHz: tuning = (DEVINFO->HFRCOCAL0 & _DEVINFO_HFRCOCAL0_BAND11_MASK) >> _DEVINFO_HFRCOCAL0_BAND11_SHIFT; break; case cmuHFRCOBand_14MHz: tuning = (DEVINFO->HFRCOCAL0 & _DEVINFO_HFRCOCAL0_BAND14_MASK) >> _DEVINFO_HFRCOCAL0_BAND14_SHIFT; break; case cmuHFRCOBand_21MHz: tuning = (DEVINFO->HFRCOCAL1 & _DEVINFO_HFRCOCAL1_BAND21_MASK) >> _DEVINFO_HFRCOCAL1_BAND21_SHIFT; break; #if defined(_CMU_HFRCOCTRL_BAND_28MHZ) case cmuHFRCOBand_28MHz: tuning = (DEVINFO->HFRCOCAL1 & _DEVINFO_HFRCOCAL1_BAND28_MASK) >> _DEVINFO_HFRCOCAL1_BAND28_SHIFT; break; #endif default: EFM_ASSERT(0); return; } /* If HFRCO is used for core clock, we have to consider flash access WS. */ osc = CMU_ClockSelectGet(cmuClock_HF); if (osc == cmuSelect_HFRCO) { /* Configure worst case wait states for flash access before setting divider */ flashWaitStateMax(); } /* Set band/tuning */ CMU->HFRCOCTRL = (CMU->HFRCOCTRL & ~(_CMU_HFRCOCTRL_BAND_MASK | _CMU_HFRCOCTRL_TUNING_MASK)) | (band << _CMU_HFRCOCTRL_BAND_SHIFT) | (tuning << _CMU_HFRCOCTRL_TUNING_SHIFT); /* If HFRCO is used for core clock, optimize flash WS */ if (osc == cmuSelect_HFRCO) { /* Call SystemCoreClockGet() to update CMSIS core clock variable. */ freq = SystemCoreClockGet(); CMU_UpdateWaitStates(freq, VSCALE_DEFAULT); } #if defined(CMU_MAX_FREQ_HFLE) /* Reduce HFLE frequency if possible. */ setHfLeConfig(CMU_ClockFreqGet(cmuClock_HFLE)); #endif } #endif /* _CMU_HFRCOCTRL_BAND_MASK */ #if defined(_CMU_HFRCOCTRL_FREQRANGE_MASK) /**************************************************************************//** * @brief * Get the HFRCO frequency calibration word in DEVINFO * * @param[in] freq * Frequency in Hz * * @return * HFRCO calibration word for a given frequency *****************************************************************************/ static uint32_t CMU_HFRCODevinfoGet(CMU_HFRCOFreq_TypeDef freq) { switch (freq) { /* 1, 2 and 4MHz share the same calibration word */ case cmuHFRCOFreq_1M0Hz: case cmuHFRCOFreq_2M0Hz: case cmuHFRCOFreq_4M0Hz: return DEVINFO->HFRCOCAL0; case cmuHFRCOFreq_7M0Hz: return DEVINFO->HFRCOCAL3; case cmuHFRCOFreq_13M0Hz: return DEVINFO->HFRCOCAL6; case cmuHFRCOFreq_16M0Hz: return DEVINFO->HFRCOCAL7; case cmuHFRCOFreq_19M0Hz: return DEVINFO->HFRCOCAL8; case cmuHFRCOFreq_26M0Hz: return DEVINFO->HFRCOCAL10; case cmuHFRCOFreq_32M0Hz: return DEVINFO->HFRCOCAL11; case cmuHFRCOFreq_38M0Hz: return DEVINFO->HFRCOCAL12; #if defined(_DEVINFO_HFRCOCAL16_MASK) case cmuHFRCOFreq_48M0Hz: return DEVINFO->HFRCOCAL13; case cmuHFRCOFreq_56M0Hz: return DEVINFO->HFRCOCAL14; case cmuHFRCOFreq_64M0Hz: return DEVINFO->HFRCOCAL15; case cmuHFRCOFreq_72M0Hz: return DEVINFO->HFRCOCAL16; #endif default: /* cmuHFRCOFreq_UserDefined */ return 0; } } /***************************************************************************//** * @brief * Get current HFRCO frequency. * * @return * HFRCO frequency ******************************************************************************/ CMU_HFRCOFreq_TypeDef CMU_HFRCOBandGet(void) { return (CMU_HFRCOFreq_TypeDef)SystemHfrcoFreq; } /***************************************************************************//** * @brief * Set HFRCO calibration for the selected target frequency. * * @param[in] setFreq * HFRCO frequency to set ******************************************************************************/ void CMU_HFRCOBandSet(CMU_HFRCOFreq_TypeDef setFreq) { uint32_t freqCal; uint32_t sysFreq; uint32_t prevFreq; /* Get DEVINFO index, set CMSIS frequency SystemHfrcoFreq */ freqCal = CMU_HFRCODevinfoGet(setFreq); EFM_ASSERT((freqCal != 0) && (freqCal != UINT_MAX)); prevFreq = SystemHfrcoFreq; SystemHfrcoFreq = (uint32_t)setFreq; /* Set max wait-states while changing core clock */ if (CMU_ClockSelectGet(cmuClock_HF) == cmuSelect_HFRCO) { flashWaitStateMax(); } /* Wait for any previous sync to complete, and then set calibration data for the selected frequency. */ while (BUS_RegBitRead(&CMU->SYNCBUSY, _CMU_SYNCBUSY_HFRCOBSY_SHIFT)) ; /* Check for valid calibration data */ EFM_ASSERT(freqCal != UINT_MAX); /* Set divider in HFRCOCTRL for 1, 2 and 4MHz */ switch (setFreq) { case cmuHFRCOFreq_1M0Hz: freqCal = (freqCal & ~_CMU_HFRCOCTRL_CLKDIV_MASK) | CMU_HFRCOCTRL_CLKDIV_DIV4; break; case cmuHFRCOFreq_2M0Hz: freqCal = (freqCal & ~_CMU_HFRCOCTRL_CLKDIV_MASK) | CMU_HFRCOCTRL_CLKDIV_DIV2; break; case cmuHFRCOFreq_4M0Hz: freqCal = (freqCal & ~_CMU_HFRCOCTRL_CLKDIV_MASK) | CMU_HFRCOCTRL_CLKDIV_DIV1; break; default: break; } /* Update HFLE configuration before updating HFRCO. Use the new set frequency. */ if (CMU_ClockSelectGet(cmuClock_HF) == cmuSelect_HFRCO) { /* setFreq is worst-case as dividers may reduce the HFLE frequency. */ setHfLeConfig(setFreq); } if (setFreq > prevFreq) { #if defined(_EMU_CMD_EM01VSCALE0_MASK) /* When increasing frequency we need to voltage scale before the change */ EMU_VScaleEM01ByClock(setFreq, true); #endif } CMU->HFRCOCTRL = freqCal; /* If HFRCO is selected as HF clock, optimize flash access wait-state configuration for this frequency and update CMSIS core clock variable. */ if (CMU_ClockSelectGet(cmuClock_HF) == cmuSelect_HFRCO) { /* Call SystemCoreClockGet() to update CMSIS core clock variable. */ sysFreq = SystemCoreClockGet(); EFM_ASSERT(sysFreq <= (uint32_t)setFreq); EFM_ASSERT(sysFreq <= SystemHfrcoFreq); EFM_ASSERT(setFreq == SystemHfrcoFreq); CMU_UpdateWaitStates(sysFreq, VSCALE_DEFAULT); } /* Reduce HFLE frequency if possible. */ setHfLeConfig(CMU_ClockFreqGet(cmuClock_HFLE)); if (setFreq <= prevFreq) { #if defined(_EMU_CMD_EM01VSCALE0_MASK) /* When decreasing frequency we need to voltage scale after the change */ EMU_VScaleEM01ByClock(0, true); #endif } } #endif /* _CMU_HFRCOCTRL_FREQRANGE_MASK */ #if defined(_CMU_HFRCOCTRL_SUDELAY_MASK) /***************************************************************************//** * @brief * Get the HFRCO startup delay. * * @details * Please refer to the reference manual for further details. * * @return * The startup delay in use. ******************************************************************************/ uint32_t CMU_HFRCOStartupDelayGet(void) { return (CMU->HFRCOCTRL & _CMU_HFRCOCTRL_SUDELAY_MASK) >> _CMU_HFRCOCTRL_SUDELAY_SHIFT; } /***************************************************************************//** * @brief * Set the HFRCO startup delay. * * @details * Please refer to the reference manual for further details. * * @param[in] delay * The startup delay to set (<= 31). ******************************************************************************/ void CMU_HFRCOStartupDelaySet(uint32_t delay) { EFM_ASSERT(delay <= 31); delay &= _CMU_HFRCOCTRL_SUDELAY_MASK >> _CMU_HFRCOCTRL_SUDELAY_SHIFT; CMU->HFRCOCTRL = (CMU->HFRCOCTRL & ~(_CMU_HFRCOCTRL_SUDELAY_MASK)) | (delay << _CMU_HFRCOCTRL_SUDELAY_SHIFT); } #endif #if defined(_CMU_USHFRCOCTRL_FREQRANGE_MASK) /**************************************************************************//** * @brief * Get the USHFRCO frequency calibration word in DEVINFO * * @param[in] freq * Frequency in Hz * * @return * USHFRCO calibration word for a given frequency *****************************************************************************/ static uint32_t CMU_USHFRCODevinfoGet(CMU_USHFRCOFreq_TypeDef freq) { switch (freq) { case cmuUSHFRCOFreq_16M0Hz: return DEVINFO->USHFRCOCAL7; case cmuUSHFRCOFreq_32M0Hz: return DEVINFO->USHFRCOCAL11; case cmuUSHFRCOFreq_48M0Hz: return DEVINFO->USHFRCOCAL13; case cmuUSHFRCOFreq_50M0Hz: return DEVINFO->USHFRCOCAL14; default: /* cmuUSHFRCOFreq_UserDefined */ return 0; } } /***************************************************************************//** * @brief * Get current USHFRCO frequency. * * @return * HFRCO frequency ******************************************************************************/ CMU_USHFRCOFreq_TypeDef CMU_USHFRCOBandGet(void) { return (CMU_USHFRCOFreq_TypeDef) ushfrcoFreq; } /***************************************************************************//** * @brief * Set USHFRCO calibration for the selected target frequency. * * @param[in] setFreq * USHFRCO frequency to set ******************************************************************************/ void CMU_USHFRCOBandSet(CMU_USHFRCOFreq_TypeDef setFreq) { uint32_t freqCal; /* Get DEVINFO calibration values */ freqCal = CMU_USHFRCODevinfoGet(setFreq); EFM_ASSERT((freqCal != 0) && (freqCal != UINT_MAX)); ushfrcoFreq = (uint32_t)setFreq; /* Wait for any previous sync to complete, and then set calibration data for the selected frequency. */ while (BUS_RegBitRead(&CMU->SYNCBUSY, _CMU_SYNCBUSY_USHFRCOBSY_SHIFT)) ; CMU->USHFRCOCTRL = freqCal; } #endif /* _CMU_USHFRCOCTRL_FREQRANGE_MASK */ #if defined(_CMU_HFXOCTRL_AUTOSTARTEM0EM1_MASK) /***************************************************************************//** * @brief * Enable or disable HFXO autostart * * @param[in] userSel * Additional user specified enable bit. * * @param[in] enEM0EM1Start * If true, HFXO is automatically started upon entering EM0/EM1 entry from * EM2/EM3. HFXO selection has to be handled by the user. * If false, HFXO is not started automatically when entering EM0/EM1. * * @param[in] enEM0EM1StartSel * If true, HFXO is automatically started and immediately selected upon * entering EM0/EM1 entry from EM2/EM3. Note that this option stalls the use of * HFSRCCLK until HFXO becomes ready. * If false, HFXO is not started or selected automatically when entering * EM0/EM1. ******************************************************************************/ void CMU_HFXOAutostartEnable(uint32_t userSel, bool enEM0EM1Start, bool enEM0EM1StartSel) { uint32_t hfxoFreq; uint32_t hfxoCtrl; /* Mask supported enable bits. */ #if defined(_CMU_HFXOCTRL_AUTOSTARTRDYSELRAC_MASK) userSel &= _CMU_HFXOCTRL_AUTOSTARTRDYSELRAC_MASK; #else userSel = 0; #endif hfxoCtrl = CMU->HFXOCTRL & ~(userSel | _CMU_HFXOCTRL_AUTOSTARTEM0EM1_MASK | _CMU_HFXOCTRL_AUTOSTARTSELEM0EM1_MASK); hfxoCtrl |= userSel | (enEM0EM1Start ? CMU_HFXOCTRL_AUTOSTARTEM0EM1 : 0) | (enEM0EM1StartSel ? CMU_HFXOCTRL_AUTOSTARTSELEM0EM1 : 0); /* Set wait-states for HFXO if automatic start and select is configured. */ if (userSel || enEM0EM1StartSel) { hfxoFreq = SystemHFXOClockGet(); CMU_UpdateWaitStates(hfxoFreq, VSCALE_DEFAULT); setHfLeConfig(hfxoFreq); } /* Update HFXOCTRL after wait-states are updated as HF may automatically switch to HFXO when automatic select is enabled . */ CMU->HFXOCTRL = hfxoCtrl; } #endif /**************************************************************************//** * @brief * Set HFXO control registers * * @note * HFXO configuration should be obtained from a configuration tool, * app note or xtal datasheet. This function disables the HFXO to ensure * a valid state before update. * * @param[in] hfxoInit * HFXO setup parameters *****************************************************************************/ void CMU_HFXOInit(const CMU_HFXOInit_TypeDef *hfxoInit) { /* Do not disable HFXO if it is currently selected as HF/Core clock */ EFM_ASSERT(CMU_ClockSelectGet(cmuClock_HF) != cmuSelect_HFXO); /* HFXO must be disabled before reconfiguration */ CMU_OscillatorEnable(cmuOsc_HFXO, false, true); #if defined(_SILICON_LABS_32B_SERIES_1) && (_SILICON_LABS_GECKO_INTERNAL_SDID >= 100) uint32_t tmp = CMU_HFXOCTRL_MODE_XTAL; switch (hfxoInit->mode) { case cmuOscMode_Crystal: tmp = CMU_HFXOCTRL_MODE_XTAL; break; case cmuOscMode_External: tmp = CMU_HFXOCTRL_MODE_DIGEXTCLK; break; case cmuOscMode_AcCoupled: tmp = CMU_HFXOCTRL_MODE_ACBUFEXTCLK; break; default: EFM_ASSERT(false); /* Unsupported configuration */ } /* HFXO Doubler can only be enabled on crystals up to max 25 MHz */ if (SystemHFXOClockGet() <= 25000000) { tmp |= CMU_HFXOCTRL_HFXOX2EN; } CMU->HFXOCTRL = (CMU->HFXOCTRL & ~(_CMU_HFXOCTRL_MODE_MASK | _CMU_HFXOCTRL_HFXOX2EN_MASK)) | tmp; /* Set tuning for startup and steady state */ CMU->HFXOSTARTUPCTRL = (hfxoInit->ctuneStartup << _CMU_HFXOSTARTUPCTRL_CTUNE_SHIFT) | (hfxoInit->xoCoreBiasTrimStartup << _CMU_HFXOSTARTUPCTRL_IBTRIMXOCORE_SHIFT); CMU->HFXOSTEADYSTATECTRL = (CMU->HFXOSTEADYSTATECTRL & ~(_CMU_HFXOSTEADYSTATECTRL_CTUNE_MASK | _CMU_HFXOSTEADYSTATECTRL_IBTRIMXOCORE_MASK)) | (hfxoInit->ctuneSteadyState << _CMU_HFXOSTEADYSTATECTRL_CTUNE_SHIFT) | (hfxoInit->xoCoreBiasTrimSteadyState << _CMU_HFXOSTEADYSTATECTRL_IBTRIMXOCORE_SHIFT); /* Set timeouts */ CMU->HFXOTIMEOUTCTRL = (hfxoInit->timeoutPeakDetect << _CMU_HFXOTIMEOUTCTRL_PEAKDETTIMEOUT_SHIFT) | (hfxoInit->timeoutSteady << _CMU_HFXOTIMEOUTCTRL_STEADYTIMEOUT_SHIFT) | (hfxoInit->timeoutStartup << _CMU_HFXOTIMEOUTCTRL_STARTUPTIMEOUT_SHIFT); #elif defined(_CMU_HFXOCTRL_MASK) /* Verify that the deprecated autostart fields are not used, * @ref CMU_HFXOAutostartEnable must be used instead. */ EFM_ASSERT(!(hfxoInit->autoStartEm01 || hfxoInit->autoSelEm01 || hfxoInit->autoStartSelOnRacWakeup)); uint32_t tmp = CMU_HFXOCTRL_MODE_XTAL; /* AC coupled external clock not supported */ EFM_ASSERT(hfxoInit->mode != cmuOscMode_AcCoupled); if (hfxoInit->mode == cmuOscMode_External) { tmp = CMU_HFXOCTRL_MODE_DIGEXTCLK; } /* Apply control settings */ CMU->HFXOCTRL = (CMU->HFXOCTRL & ~_CMU_HFXOCTRL_MODE_MASK) | tmp; BUS_RegBitWrite(&CMU->HFXOCTRL, _CMU_HFXOCTRL_LOWPOWER_SHIFT, hfxoInit->lowPowerMode); /* Set XTAL tuning parameters */ #if defined(_CMU_HFXOCTRL1_PEAKDETTHR_MASK) /* Set peak detection threshold */ CMU->HFXOCTRL1 = (CMU->HFXOCTRL1 & ~_CMU_HFXOCTRL1_PEAKDETTHR_MASK) | (hfxoInit->thresholdPeakDetect << _CMU_HFXOCTRL1_PEAKDETTHR_SHIFT); #endif /* Set tuning for startup and steady state */ CMU->HFXOSTARTUPCTRL = (hfxoInit->ctuneStartup << _CMU_HFXOSTARTUPCTRL_CTUNE_SHIFT) | (hfxoInit->xoCoreBiasTrimStartup << _CMU_HFXOSTARTUPCTRL_IBTRIMXOCORE_SHIFT); CMU->HFXOSTEADYSTATECTRL = (CMU->HFXOSTEADYSTATECTRL & ~(_CMU_HFXOSTEADYSTATECTRL_CTUNE_MASK | _CMU_HFXOSTEADYSTATECTRL_IBTRIMXOCORE_MASK | _CMU_HFXOSTEADYSTATECTRL_REGISH_MASK | _CMU_HFXOSTEADYSTATECTRL_REGISHUPPER_MASK)) | (hfxoInit->ctuneSteadyState << _CMU_HFXOSTEADYSTATECTRL_CTUNE_SHIFT) | (hfxoInit->xoCoreBiasTrimSteadyState << _CMU_HFXOSTEADYSTATECTRL_IBTRIMXOCORE_SHIFT) | (hfxoInit->regIshSteadyState << _CMU_HFXOSTEADYSTATECTRL_REGISH_SHIFT) | getRegIshUpperVal(hfxoInit->regIshSteadyState); /* Set timeouts */ CMU->HFXOTIMEOUTCTRL = (hfxoInit->timeoutPeakDetect << _CMU_HFXOTIMEOUTCTRL_PEAKDETTIMEOUT_SHIFT) | (hfxoInit->timeoutSteady << _CMU_HFXOTIMEOUTCTRL_STEADYTIMEOUT_SHIFT) | (hfxoInit->timeoutStartup << _CMU_HFXOTIMEOUTCTRL_STARTUPTIMEOUT_SHIFT) | (hfxoInit->timeoutShuntOptimization << _CMU_HFXOTIMEOUTCTRL_SHUNTOPTTIMEOUT_SHIFT); #else CMU->CTRL = (CMU->CTRL & ~(_CMU_CTRL_HFXOTIMEOUT_MASK | _CMU_CTRL_HFXOBOOST_MASK | _CMU_CTRL_HFXOMODE_MASK | _CMU_CTRL_HFXOGLITCHDETEN_MASK)) | (hfxoInit->timeout << _CMU_CTRL_HFXOTIMEOUT_SHIFT) | (hfxoInit->boost << _CMU_CTRL_HFXOBOOST_SHIFT) | (hfxoInit->mode << _CMU_CTRL_HFXOMODE_SHIFT) | (hfxoInit->glitchDetector ? CMU_CTRL_HFXOGLITCHDETEN : 0); #endif } /***************************************************************************//** * @brief * Get the LCD framerate divisor (FDIV) setting. * * @return * The LCD framerate divisor. ******************************************************************************/ uint32_t CMU_LCDClkFDIVGet(void) { #if defined(LCD_PRESENT) && defined(_CMU_LCDCTRL_MASK) return (CMU->LCDCTRL & _CMU_LCDCTRL_FDIV_MASK) >> _CMU_LCDCTRL_FDIV_SHIFT; #else return 0; #endif /* defined(LCD_PRESENT) */ } /***************************************************************************//** * @brief * Set the LCD framerate divisor (FDIV) setting. * * @note * The FDIV field (CMU LCDCTRL register) should only be modified while the * LCD module is clock disabled (CMU LFACLKEN0.LCD bit is 0). This function * will NOT modify FDIV if the LCD module clock is enabled. Please refer to * CMU_ClockEnable() for disabling/enabling LCD clock. * * @param[in] div * The FDIV setting to use. ******************************************************************************/ void CMU_LCDClkFDIVSet(uint32_t div) { #if defined(LCD_PRESENT) && defined(_CMU_LCDCTRL_MASK) EFM_ASSERT(div <= cmuClkDiv_128); /* Do not allow modification if LCD clock enabled */ if (CMU->LFACLKEN0 & CMU_LFACLKEN0_LCD) { return; } div <<= _CMU_LCDCTRL_FDIV_SHIFT; div &= _CMU_LCDCTRL_FDIV_MASK; CMU->LCDCTRL = (CMU->LCDCTRL & ~_CMU_LCDCTRL_FDIV_MASK) | div; #else (void)div; /* Unused parameter */ #endif /* defined(LCD_PRESENT) */ } /**************************************************************************//** * @brief * Set LFXO control registers * * @note * LFXO configuration should be obtained from a configuration tool, * app note or xtal datasheet. This function disables the LFXO to ensure * a valid state before update. * * @param[in] lfxoInit * LFXO setup parameters *****************************************************************************/ void CMU_LFXOInit(const CMU_LFXOInit_TypeDef *lfxoInit) { /* Do not disable LFXO if it is currently selected as HF/Core clock */ EFM_ASSERT(CMU_ClockSelectGet(cmuClock_HF) != cmuSelect_LFXO); /* LFXO must be disabled before reconfiguration */ CMU_OscillatorEnable(cmuOsc_LFXO, false, false); #if defined(_CMU_LFXOCTRL_MASK) BUS_RegMaskedWrite(&CMU->LFXOCTRL, _CMU_LFXOCTRL_TUNING_MASK | _CMU_LFXOCTRL_GAIN_MASK | _CMU_LFXOCTRL_TIMEOUT_MASK | _CMU_LFXOCTRL_MODE_MASK, (lfxoInit->ctune << _CMU_LFXOCTRL_TUNING_SHIFT) | (lfxoInit->gain << _CMU_LFXOCTRL_GAIN_SHIFT) | (lfxoInit->timeout << _CMU_LFXOCTRL_TIMEOUT_SHIFT) | (lfxoInit->mode << _CMU_LFXOCTRL_MODE_SHIFT)); #else bool cmuBoost = (lfxoInit->boost & 0x2); BUS_RegMaskedWrite(&CMU->CTRL, _CMU_CTRL_LFXOTIMEOUT_MASK | _CMU_CTRL_LFXOBOOST_MASK | _CMU_CTRL_LFXOMODE_MASK, (lfxoInit->timeout << _CMU_CTRL_LFXOTIMEOUT_SHIFT) | ((cmuBoost ? 1 : 0) << _CMU_CTRL_LFXOBOOST_SHIFT) | (lfxoInit->mode << _CMU_CTRL_LFXOMODE_SHIFT)); #endif #if defined(_EMU_AUXCTRL_REDLFXOBOOST_MASK) bool emuReduce = (lfxoInit->boost & 0x1); BUS_RegBitWrite(&EMU->AUXCTRL, _EMU_AUXCTRL_REDLFXOBOOST_SHIFT, emuReduce ? 1 : 0); #endif } /***************************************************************************//** * @brief * Enable/disable oscillator. * * @note * WARNING: When this function is called to disable either cmuOsc_LFXO or * cmuOsc_HFXO the LFXOMODE or HFXOMODE fields of the CMU_CTRL register * are reset to the reset value. I.e. if external clock sources are selected * in either LFXOMODE or HFXOMODE fields, the configuration will be cleared * and needs to be reconfigured if needed later. * * @param[in] osc * The oscillator to enable/disable. * * @param[in] enable * @li true - enable specified oscillator. * @li false - disable specified oscillator. * * @param[in] wait * Only used if @p enable is true. * @li true - wait for oscillator start-up time to timeout before returning. * @li false - do not wait for oscillator start-up time to timeout before * returning. ******************************************************************************/ void CMU_OscillatorEnable(CMU_Osc_TypeDef osc, bool enable, bool wait) { uint32_t rdyBitPos; #if defined(_SILICON_LABS_32B_SERIES_1) uint32_t ensBitPos; #endif #if defined(_CMU_STATUS_HFXOPEAKDETRDY_MASK) uint32_t hfxoTrimStatus; #endif uint32_t enBit; uint32_t disBit; switch (osc) { case cmuOsc_HFRCO: enBit = CMU_OSCENCMD_HFRCOEN; disBit = CMU_OSCENCMD_HFRCODIS; rdyBitPos = _CMU_STATUS_HFRCORDY_SHIFT; #if defined(_SILICON_LABS_32B_SERIES_1) ensBitPos = _CMU_STATUS_HFRCOENS_SHIFT; #endif break; case cmuOsc_HFXO: enBit = CMU_OSCENCMD_HFXOEN; disBit = CMU_OSCENCMD_HFXODIS; rdyBitPos = _CMU_STATUS_HFXORDY_SHIFT; #if defined(_SILICON_LABS_32B_SERIES_1) ensBitPos = _CMU_STATUS_HFXOENS_SHIFT; #endif break; case cmuOsc_AUXHFRCO: enBit = CMU_OSCENCMD_AUXHFRCOEN; disBit = CMU_OSCENCMD_AUXHFRCODIS; rdyBitPos = _CMU_STATUS_AUXHFRCORDY_SHIFT; #if defined(_SILICON_LABS_32B_SERIES_1) ensBitPos = _CMU_STATUS_AUXHFRCOENS_SHIFT; #endif break; case cmuOsc_LFRCO: enBit = CMU_OSCENCMD_LFRCOEN; disBit = CMU_OSCENCMD_LFRCODIS; rdyBitPos = _CMU_STATUS_LFRCORDY_SHIFT; #if defined(_SILICON_LABS_32B_SERIES_1) ensBitPos = _CMU_STATUS_LFRCOENS_SHIFT; #endif break; case cmuOsc_LFXO: enBit = CMU_OSCENCMD_LFXOEN; disBit = CMU_OSCENCMD_LFXODIS; rdyBitPos = _CMU_STATUS_LFXORDY_SHIFT; #if defined(_SILICON_LABS_32B_SERIES_1) ensBitPos = _CMU_STATUS_LFXOENS_SHIFT; #endif break; #if defined(_CMU_STATUS_USHFRCOENS_MASK) case cmuOsc_USHFRCO: enBit = CMU_OSCENCMD_USHFRCOEN; disBit = CMU_OSCENCMD_USHFRCODIS; rdyBitPos = _CMU_STATUS_USHFRCORDY_SHIFT; #if defined(_SILICON_LABS_32B_SERIES_1) ensBitPos = _CMU_STATUS_USHFRCOENS_SHIFT; #endif break; #endif #if defined(_CMU_STATUS_PLFRCOENS_MASK) case cmuOsc_PLFRCO: enBit = CMU_OSCENCMD_PLFRCOEN; disBit = CMU_OSCENCMD_PLFRCODIS; rdyBitPos = _CMU_STATUS_PLFRCORDY_SHIFT; ensBitPos = _CMU_STATUS_PLFRCOENS_SHIFT; break; #endif default: /* Undefined clock source or cmuOsc_ULFRCO. ULFRCO is always enabled, and cannot be disabled. Ie. the definition of cmuOsc_ULFRCO is primarely intended for information: the ULFRCO is always on. */ EFM_ASSERT(0); return; } if (enable) { #if defined(_CMU_HFXOCTRL_MASK) bool firstHfxoEnable = false; /* Enabling the HFXO for the first time requires special handling. We use the * PEAKDETSHUTOPTMODE field of the HFXOCTRL register to see if this is the * first time the HFXO is enabled. */ if ((osc == cmuOsc_HFXO) && (getHfxoTuningMode() == HFXO_TUNING_MODE_AUTO)) { /* REGPWRSEL must be set to DVDD before the HFXO can be enabled. */ #if defined(_EMU_PWRCTRL_REGPWRSEL_MASK) EFM_ASSERT(EMU->PWRCTRL & EMU_PWRCTRL_REGPWRSEL_DVDD); #endif firstHfxoEnable = true; /* First time we enable an external clock we should switch to CMD mode to make sure that * we only do SCO and not PDA tuning. */ if ((CMU->HFXOCTRL & (_CMU_HFXOCTRL_MODE_MASK)) == CMU_HFXOCTRL_MODE_DIGEXTCLK) { setHfxoTuningMode(HFXO_TUNING_MODE_CMD); } } #endif CMU->OSCENCMD = enBit; #if defined(_SILICON_LABS_32B_SERIES_1) /* Always wait for ENS to go high */ while (!BUS_RegBitRead(&CMU->STATUS, ensBitPos)) { } #endif /* Wait for clock to become ready after enable */ if (wait) { while (!BUS_RegBitRead(&CMU->STATUS, rdyBitPos)) ; #if defined(_SILICON_LABS_32B_SERIES_1) if ((osc == cmuOsc_HFXO) && firstHfxoEnable) { if ((CMU->HFXOCTRL & _CMU_HFXOCTRL_MODE_MASK) == CMU_HFXOCTRL_MODE_DIGEXTCLK) { #if defined(CMU_CMD_HFXOSHUNTOPTSTART) /* External clock mode should only do shunt current optimization. */ CMU_OscillatorTuningOptimize(cmuOsc_HFXO, cmuHFXOTuningMode_ShuntCommand, true); #endif } else { /* Wait for peak detection and shunt current optimization to complete. */ CMU_OscillatorTuningWait(cmuOsc_HFXO, cmuHFXOTuningMode_Auto); } /* Disable the HFXO again to apply the trims. Apply trim from HFXOTRIMSTATUS when disabled. */ hfxoTrimStatus = CMU_OscillatorTuningGet(cmuOsc_HFXO); CMU_OscillatorEnable(cmuOsc_HFXO, false, true); CMU_OscillatorTuningSet(cmuOsc_HFXO, hfxoTrimStatus); /* Restart in CMD mode. */ CMU->OSCENCMD = enBit; while (!BUS_RegBitRead(&CMU->STATUS, rdyBitPos)) ; } #endif } } else { CMU->OSCENCMD = disBit; #if defined(_SILICON_LABS_32B_SERIES_1) /* Always wait for ENS to go low */ while (BUS_RegBitRead(&CMU->STATUS, ensBitPos)) { } #endif } } /***************************************************************************//** * @brief * Get oscillator frequency tuning setting. * * @param[in] osc * Oscillator to get tuning value for, one of: * @li #cmuOsc_LFRCO * @li #cmuOsc_HFRCO @if _CMU_USHFRCOCTRL_TUNING_MASK * @li #cmuOsc_USHFRCO * @endif * @li #cmuOsc_AUXHFRCO * @li #cmuOsc_HFXO if CMU_HFXOCTRL_PEAKDETSHUNTOPTMODE is defined * * @return * The oscillator frequency tuning setting in use. ******************************************************************************/ uint32_t CMU_OscillatorTuningGet(CMU_Osc_TypeDef osc) { uint32_t ret; switch (osc) { case cmuOsc_LFRCO: ret = (CMU->LFRCOCTRL & _CMU_LFRCOCTRL_TUNING_MASK) >> _CMU_LFRCOCTRL_TUNING_SHIFT; break; case cmuOsc_HFRCO: ret = (CMU->HFRCOCTRL & _CMU_HFRCOCTRL_TUNING_MASK) >> _CMU_HFRCOCTRL_TUNING_SHIFT; break; #if defined (_CMU_USHFRCOCTRL_TUNING_MASK) case cmuOsc_USHFRCO: ret = (CMU->USHFRCOCTRL & _CMU_USHFRCOCTRL_TUNING_MASK) >> _CMU_USHFRCOCTRL_TUNING_SHIFT; break; #endif case cmuOsc_AUXHFRCO: ret = (CMU->AUXHFRCOCTRL & _CMU_AUXHFRCOCTRL_TUNING_MASK) >> _CMU_AUXHFRCOCTRL_TUNING_SHIFT; break; #if defined(_CMU_HFXOCTRL_PEAKDETSHUNTOPTMODE_MASK) case cmuOsc_HFXO: ret = CMU->HFXOTRIMSTATUS & (_CMU_HFXOTRIMSTATUS_IBTRIMXOCORE_MASK #if defined(_CMU_HFXOTRIMSTATUS_REGISH_MASK) | _CMU_HFXOTRIMSTATUS_REGISH_MASK #endif ); break; #endif default: EFM_ASSERT(0); ret = 0; break; } return ret; } /***************************************************************************//** * @brief * Set the oscillator frequency tuning control. * * @note * Oscillator tuning is done during production, and the tuning value is * automatically loaded after a reset. Changing the tuning value from the * calibrated value is for more advanced use. Certain oscillators also have * build-in tuning optimization. * * @param[in] osc * Oscillator to set tuning value for, one of: * @li #cmuOsc_LFRCO * @li #cmuOsc_HFRCO @if _CMU_USHFRCOCTRL_TUNING_MASK * @li #cmuOsc_USHFRCO * @endif * @li #cmuOsc_AUXHFRCO * @li #cmuOsc_HFXO if PEAKDETSHUNTOPTMODE is available. Note that CMD mode is set. * * @param[in] val * The oscillator frequency tuning setting to use. ******************************************************************************/ void CMU_OscillatorTuningSet(CMU_Osc_TypeDef osc, uint32_t val) { #if defined(_CMU_HFXOSTEADYSTATECTRL_REGISH_MASK) uint32_t regIshUpper; #endif switch (osc) { case cmuOsc_LFRCO: EFM_ASSERT(val <= (_CMU_LFRCOCTRL_TUNING_MASK >> _CMU_LFRCOCTRL_TUNING_SHIFT)); val &= (_CMU_LFRCOCTRL_TUNING_MASK >> _CMU_LFRCOCTRL_TUNING_SHIFT); #if defined(_SILICON_LABS_32B_SERIES_1) while (BUS_RegBitRead(&CMU->SYNCBUSY, _CMU_SYNCBUSY_LFRCOBSY_SHIFT)) ; #endif CMU->LFRCOCTRL = (CMU->LFRCOCTRL & ~(_CMU_LFRCOCTRL_TUNING_MASK)) | (val << _CMU_LFRCOCTRL_TUNING_SHIFT); break; case cmuOsc_HFRCO: EFM_ASSERT(val <= (_CMU_HFRCOCTRL_TUNING_MASK >> _CMU_HFRCOCTRL_TUNING_SHIFT)); val &= (_CMU_HFRCOCTRL_TUNING_MASK >> _CMU_HFRCOCTRL_TUNING_SHIFT); #if defined(_SILICON_LABS_32B_SERIES_1) while (BUS_RegBitRead(&CMU->SYNCBUSY, _CMU_SYNCBUSY_HFRCOBSY_SHIFT)) { } #endif CMU->HFRCOCTRL = (CMU->HFRCOCTRL & ~(_CMU_HFRCOCTRL_TUNING_MASK)) | (val << _CMU_HFRCOCTRL_TUNING_SHIFT); break; #if defined (_CMU_USHFRCOCTRL_TUNING_MASK) case cmuOsc_USHFRCO: EFM_ASSERT(val <= (_CMU_USHFRCOCTRL_TUNING_MASK >> _CMU_USHFRCOCTRL_TUNING_SHIFT)); val &= (_CMU_USHFRCOCTRL_TUNING_MASK >> _CMU_USHFRCOCTRL_TUNING_SHIFT); #if defined(_SILICON_LABS_32B_SERIES_1) while (BUS_RegBitRead(&CMU->SYNCBUSY, _CMU_SYNCBUSY_USHFRCOBSY_SHIFT)) { } #endif CMU->USHFRCOCTRL = (CMU->USHFRCOCTRL & ~(_CMU_USHFRCOCTRL_TUNING_MASK)) | (val << _CMU_USHFRCOCTRL_TUNING_SHIFT); break; #endif case cmuOsc_AUXHFRCO: EFM_ASSERT(val <= (_CMU_AUXHFRCOCTRL_TUNING_MASK >> _CMU_AUXHFRCOCTRL_TUNING_SHIFT)); val &= (_CMU_AUXHFRCOCTRL_TUNING_MASK >> _CMU_AUXHFRCOCTRL_TUNING_SHIFT); #if defined(_SILICON_LABS_32B_SERIES_1) while (BUS_RegBitRead(&CMU->SYNCBUSY, _CMU_SYNCBUSY_AUXHFRCOBSY_SHIFT)) { } #endif CMU->AUXHFRCOCTRL = (CMU->AUXHFRCOCTRL & ~(_CMU_AUXHFRCOCTRL_TUNING_MASK)) | (val << _CMU_AUXHFRCOCTRL_TUNING_SHIFT); break; #if defined(_CMU_HFXOCTRL_PEAKDETSHUNTOPTMODE_MASK) case cmuOsc_HFXO: /* Do set PEAKDETSHUNTOPTMODE or HFXOSTEADYSTATECTRL if HFXO is enabled */ EFM_ASSERT(!(CMU->STATUS & CMU_STATUS_HFXOENS)); /* Switch to command mode. Automatic SCO and PDA calibration is not done at the next enable. Set user REGISH, REGISHUPPER and IBTRIMXOCORE. */ CMU->HFXOCTRL = (CMU->HFXOCTRL & ~_CMU_HFXOCTRL_PEAKDETSHUNTOPTMODE_MASK) | CMU_HFXOCTRL_PEAKDETSHUNTOPTMODE_CMD; #if defined(_CMU_HFXOSTEADYSTATECTRL_REGISH_MASK) regIshUpper = getRegIshUpperVal((val & _CMU_HFXOSTEADYSTATECTRL_REGISH_MASK) >> _CMU_HFXOSTEADYSTATECTRL_REGISH_SHIFT); CMU->HFXOSTEADYSTATECTRL = (CMU->HFXOSTEADYSTATECTRL & ~(_CMU_HFXOSTEADYSTATECTRL_IBTRIMXOCORE_MASK | _CMU_HFXOSTEADYSTATECTRL_REGISH_MASK | _CMU_HFXOSTEADYSTATECTRL_REGISHUPPER_MASK)) | val | regIshUpper; #else CMU->HFXOSTEADYSTATECTRL = (CMU->HFXOSTEADYSTATECTRL & ~_CMU_HFXOSTEADYSTATECTRL_IBTRIMXOCORE_MASK) | val; #endif break; #endif default: EFM_ASSERT(0); break; } } #if defined(_CMU_HFXOCTRL_PEAKDETSHUNTOPTMODE_MASK) || defined(_CMU_HFXOCTRL_PEAKDETMODE_MASK) /***************************************************************************//** * @brief * Wait for oscillator tuning optimization. * * @param[in] osc * Oscillator to set tuning value for, one of: * @li #cmuOsc_HFXO * * @param[in] mode * Tuning optimization mode. * * @return * Returns false on invalid parameters or oscillator error status. ******************************************************************************/ bool CMU_OscillatorTuningWait(CMU_Osc_TypeDef osc, CMU_HFXOTuningMode_TypeDef mode) { uint32_t waitFlags; EFM_ASSERT(osc == cmuOsc_HFXO); /* Currently implemented for HFXO with PEAKDETSHUNTOPTMODE only */ (void)osc; if (getHfxoTuningMode() == HFXO_TUNING_MODE_AUTO) { waitFlags = HFXO_TUNING_READY_FLAGS; } else { /* Set wait flags for each command and wait */ switch (mode) { #if defined(_CMU_STATUS_HFXOSHUNTOPTRDY_MASK) case cmuHFXOTuningMode_ShuntCommand: waitFlags = CMU_STATUS_HFXOSHUNTOPTRDY; break; #endif case cmuHFXOTuningMode_Auto: waitFlags = HFXO_TUNING_READY_FLAGS; break; #if defined(CMU_CMD_HFXOSHUNTOPTSTART) case cmuHFXOTuningMode_PeakShuntCommand: waitFlags = HFXO_TUNING_READY_FLAGS; break; #endif default: waitFlags = _CMU_STATUS_MASK; EFM_ASSERT(false); } } while ((CMU->STATUS & waitFlags) != waitFlags) ; #if defined(CMU_IF_HFXOPEAKDETERR) /* Check error flags */ if (waitFlags & CMU_STATUS_HFXOPEAKDETRDY) { return (CMU->IF & CMU_IF_HFXOPEAKDETERR ? true : false); } #endif return true; } /***************************************************************************//** * @brief * Start and optionally wait for oscillator tuning optimization. * * @param[in] osc * Oscillator to set tuning value for, one of: * @li #cmuOsc_HFXO * * @param[in] mode * Tuning optimization mode. * * @param[in] wait * Wait for tuning optimization to complete. * true - wait for tuning optimization to complete. * false - return without waiting. * * @return * Returns false on invalid parameters or oscillator error status. ******************************************************************************/ bool CMU_OscillatorTuningOptimize(CMU_Osc_TypeDef osc, CMU_HFXOTuningMode_TypeDef mode, bool wait) { switch (osc) { case cmuOsc_HFXO: if (mode) { #if defined(CMU_IF_HFXOPEAKDETERR) /* Clear error flag before command write */ CMU->IFC = CMU_IFC_HFXOPEAKDETERR; #endif CMU->CMD = mode; } if (wait) { return CMU_OscillatorTuningWait(osc, mode); } break; default: EFM_ASSERT(false); } return true; } #endif /**************************************************************************//** * @brief * Determine if currently selected PCNTn clock used is external or LFBCLK. * * @param[in] instance * PCNT instance number to get currently selected clock source for. * * @return * @li true - selected clock is external clock. * @li false - selected clock is LFBCLK. *****************************************************************************/ bool CMU_PCNTClockExternalGet(unsigned int instance) { uint32_t setting; switch (instance) { #if defined(_CMU_PCNTCTRL_PCNT0CLKEN_MASK) case 0: setting = CMU->PCNTCTRL & CMU_PCNTCTRL_PCNT0CLKSEL_PCNT0S0; break; #if defined(_CMU_PCNTCTRL_PCNT1CLKEN_MASK) case 1: setting = CMU->PCNTCTRL & CMU_PCNTCTRL_PCNT1CLKSEL_PCNT1S0; break; #if defined(_CMU_PCNTCTRL_PCNT2CLKEN_MASK) case 2: setting = CMU->PCNTCTRL & CMU_PCNTCTRL_PCNT2CLKSEL_PCNT2S0; break; #endif #endif #endif default: setting = 0; break; } return (setting ? true : false); } /**************************************************************************//** * @brief * Select PCNTn clock. * * @param[in] instance * PCNT instance number to set selected clock source for. * * @param[in] external * Set to true to select external clock, false to select LFBCLK. *****************************************************************************/ void CMU_PCNTClockExternalSet(unsigned int instance, bool external) { #if defined(PCNT_PRESENT) uint32_t setting = 0; EFM_ASSERT(instance < PCNT_COUNT); if (external) { setting = 1; } BUS_RegBitWrite(&(CMU->PCNTCTRL), (instance * 2) + 1, setting); #else (void)instance; /* Unused parameter */ (void)external; /* Unused parameter */ #endif } #if defined(_CMU_USHFRCOCONF_BAND_MASK) /***************************************************************************//** * @brief * Get USHFRCO band in use. * * @return * USHFRCO band in use. ******************************************************************************/ CMU_USHFRCOBand_TypeDef CMU_USHFRCOBandGet(void) { return (CMU_USHFRCOBand_TypeDef)((CMU->USHFRCOCONF & _CMU_USHFRCOCONF_BAND_MASK) >> _CMU_USHFRCOCONF_BAND_SHIFT); } #endif #if defined(_CMU_USHFRCOCONF_BAND_MASK) /***************************************************************************//** * @brief * Set USHFRCO band to use. * * @param[in] band * USHFRCO band to activate. ******************************************************************************/ void CMU_USHFRCOBandSet(CMU_USHFRCOBand_TypeDef band) { uint32_t tuning; uint32_t fineTuning; CMU_Select_TypeDef osc; /* Cannot switch band if USHFRCO is already selected as HF clock. */ osc = CMU_ClockSelectGet(cmuClock_HF); EFM_ASSERT((CMU_USHFRCOBandGet() != band) && (osc != cmuSelect_USHFRCO)); /* Read tuning value from calibration table */ switch (band) { case cmuUSHFRCOBand_24MHz: tuning = (DEVINFO->USHFRCOCAL0 & _DEVINFO_USHFRCOCAL0_BAND24_TUNING_MASK) >> _DEVINFO_USHFRCOCAL0_BAND24_TUNING_SHIFT; fineTuning = (DEVINFO->USHFRCOCAL0 & _DEVINFO_USHFRCOCAL0_BAND24_FINETUNING_MASK) >> _DEVINFO_USHFRCOCAL0_BAND24_FINETUNING_SHIFT; ushfrcoFreq = 24000000UL; break; case cmuUSHFRCOBand_48MHz: tuning = (DEVINFO->USHFRCOCAL0 & _DEVINFO_USHFRCOCAL0_BAND48_TUNING_MASK) >> _DEVINFO_USHFRCOCAL0_BAND48_TUNING_SHIFT; fineTuning = (DEVINFO->USHFRCOCAL0 & _DEVINFO_USHFRCOCAL0_BAND48_FINETUNING_MASK) >> _DEVINFO_USHFRCOCAL0_BAND48_FINETUNING_SHIFT; /* Enable the clock divider before switching the band from 24 to 48MHz */ BUS_RegBitWrite(&CMU->USHFRCOCONF, _CMU_USHFRCOCONF_USHFRCODIV2DIS_SHIFT, 0); ushfrcoFreq = 48000000UL; break; default: EFM_ASSERT(0); return; } /* Set band and tuning */ CMU->USHFRCOCONF = (CMU->USHFRCOCONF & ~_CMU_USHFRCOCONF_BAND_MASK) | (band << _CMU_USHFRCOCONF_BAND_SHIFT); CMU->USHFRCOCTRL = (CMU->USHFRCOCTRL & ~_CMU_USHFRCOCTRL_TUNING_MASK) | (tuning << _CMU_USHFRCOCTRL_TUNING_SHIFT); CMU->USHFRCOTUNE = (CMU->USHFRCOTUNE & ~_CMU_USHFRCOTUNE_FINETUNING_MASK) | (fineTuning << _CMU_USHFRCOTUNE_FINETUNING_SHIFT); /* Disable the clock divider after switching the band from 48 to 24MHz */ if (band == cmuUSHFRCOBand_24MHz) { BUS_RegBitWrite(&CMU->USHFRCOCONF, _CMU_USHFRCOCONF_USHFRCODIV2DIS_SHIFT, 1); } } #endif /** @} (end addtogroup CMU) */ /** @} (end addtogroup emlib) */ #endif /* __EM_CMU_H */