| /***************************************************************************//** |
| * @file em_cmu.c |
| * @brief Clock management unit (CMU) Peripheral API |
| * @version 5.6.0 |
| ******************************************************************************* |
| * # License |
| * <b>Copyright 2018 Silicon Laboratories, Inc. www.silabs.com</b> |
| ******************************************************************************* |
| * |
| * Permission is granted to anyone to use this software for any purpose, |
| * including commercial applications, and to alter it and redistribute it |
| * freely, subject to the following restrictions: |
| * |
| * 1. The origin of this software must not be misrepresented; you must not |
| * claim that you wrote the original software. |
| * 2. Altered source versions must be plainly marked as such, and must not be |
| * misrepresented as being the original software. |
| * 3. This notice may not be removed or altered from any source distribution. |
| * |
| * DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Silicon Labs has no |
| * obligation to support this Software. Silicon Labs is providing the |
| * Software "AS IS", with no express or implied warranties of any kind, |
| * including, but not limited to, any implied warranties of merchantability |
| * or fitness for any particular purpose or warranties against infringement |
| * of any proprietary rights of a third party. |
| * |
| * Silicon Labs will not be liable for any consequential, incidental, or |
| * special damages, or any other relief, or for any claim by any third party, |
| * arising from your use of this Software. |
| * |
| ******************************************************************************/ |
| |
| #include "em_cmu.h" |
| #if defined(CMU_PRESENT) |
| |
| #include <stddef.h> |
| #include <limits.h> |
| #include "em_assert.h" |
| #include "em_bus.h" |
| #include "em_cmu.h" |
| #include "em_common.h" |
| #include "em_emu.h" |
| #include "em_gpio.h" |
| #include "em_system.h" |
| |
| /***************************************************************************//** |
| * @addtogroup emlib |
| * @{ |
| ******************************************************************************/ |
| |
| /***************************************************************************//** |
| * @addtogroup CMU |
| * @brief Clock management unit (CMU) Peripheral API |
| * @details |
| * This module contains functions to control the CMU peripheral of Silicon |
| * Labs 32-bit MCUs and SoCs. The CMU controls oscillators and clocks. |
| * @{ |
| ******************************************************************************/ |
| |
| #if defined(_SILICON_LABS_32B_SERIES_2) |
| |
| /** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */ |
| |
| /******************************************************************************* |
| ****************************** DEFINES ************************************ |
| ******************************************************************************/ |
| |
| // Maximum allowed core frequency vs. wait-states on flash accesses. |
| #define CMU_MAX_FLASHREAD_FREQ_0WS 39000000UL |
| #define CMU_MAX_FLASHREAD_FREQ_1WS 80000000UL |
| |
| // Maximum allowed core frequency vs. wait-states on sram accesses. |
| #define CMU_MAX_SRAM_FREQ_0WS 50000000UL |
| #define CMU_MAX_SRAM_FREQ_1WS 80000000UL |
| |
| // Maximum allowed PCLK frequency. |
| #define CMU_MAX_PCLK_FREQ 50000000UL |
| |
| /******************************************************************************* |
| ************************** LOCAL VARIABLES ******************************** |
| ******************************************************************************/ |
| |
| // Table of HFRCOCAL values and their associated min/max frequencies and |
| // optional band enumerator. |
| static const struct hfrcoCalTableElement{ |
| uint32_t minFreq; |
| uint32_t maxFreq; |
| uint32_t value; |
| CMU_HFRCODPLLFreq_TypeDef band; |
| } hfrcoCalTable[] = |
| |
| // TODO: Get confirmation on min/max freq limits |
| |
| { |
| // minFreq maxFreq HFRCOCAL value band |
| { 860000UL, 1050000UL, 0x82401F00UL, cmuHFRCODPLLFreq_1M0Hz }, |
| { 1050000UL, 1280000UL, 0xA2411F3AUL, (CMU_HFRCODPLLFreq_TypeDef)0 }, |
| { 1280000UL, 1480000UL, 0xA2421F3AUL, (CMU_HFRCODPLLFreq_TypeDef)0 }, |
| { 1480000UL, 1800000UL, 0xB6439F3AUL, (CMU_HFRCODPLLFreq_TypeDef)0 }, |
| { 1800000UL, 2110000UL, 0x81401F00UL, cmuHFRCODPLLFreq_2M0Hz }, |
| { 2110000UL, 2560000UL, 0xA1411F3AUL, (CMU_HFRCODPLLFreq_TypeDef)0 }, |
| { 2560000UL, 2970000UL, 0xA1421F3AUL, (CMU_HFRCODPLLFreq_TypeDef)0 }, |
| { 2970000UL, 3600000UL, 0xB5439F3AUL, (CMU_HFRCODPLLFreq_TypeDef)0 }, |
| { 3600000UL, 4220000UL, 0x80401F00UL, cmuHFRCODPLLFreq_4M0Hz }, |
| { 4220000UL, 5120000UL, 0xA0411F3AUL, (CMU_HFRCODPLLFreq_TypeDef)0 }, |
| { 5120000UL, 5930000UL, 0xA0421F3AUL, (CMU_HFRCODPLLFreq_TypeDef)0 }, |
| { 5930000UL, 7520000UL, 0xB4439F00UL, cmuHFRCODPLLFreq_7M0Hz }, |
| { 7520000UL, 9520000UL, 0xB4449F3AUL, (CMU_HFRCODPLLFreq_TypeDef)0 }, |
| { 9520000UL, 11800000UL, 0xB4459F3AUL, (CMU_HFRCODPLLFreq_TypeDef)0 }, |
| { 11800000UL, 14400000UL, 0xB4669F00UL, cmuHFRCODPLLFreq_13M0Hz }, |
| { 14400000UL, 17200000UL, 0xB4679F00UL, cmuHFRCODPLLFreq_16M0Hz }, |
| { 17200000UL, 19700000UL, 0xA8689F00UL, cmuHFRCODPLLFreq_19M0Hz }, |
| { 19700000UL, 23800000UL, 0xB8899F3AUL, (CMU_HFRCODPLLFreq_TypeDef)0 }, |
| { 23800000UL, 28700000UL, 0xB88A9F00UL, cmuHFRCODPLLFreq_26M0Hz }, |
| { 28700000UL, 34800000UL, 0xB8AB9F00UL, cmuHFRCODPLLFreq_32M0Hz }, |
| { 34800000UL, 42800000UL, 0xA8CC9F00UL, cmuHFRCODPLLFreq_38M0Hz }, |
| { 42800000UL, 51600000UL, 0xACED9F00UL, cmuHFRCODPLLFreq_48M0Hz }, |
| { 51600000UL, 60500000UL, 0xBCEE9F00UL, cmuHFRCODPLLFreq_56M0Hz }, |
| { 60500000UL, 72600000UL, 0xBCEF9F00UL, cmuHFRCODPLLFreq_64M0Hz }, |
| { 72600000UL, 80000000UL, 0xCCF09F00UL, cmuHFRCODPLLFreq_80M0Hz } |
| }; |
| |
| #define HFRCOCALTABLE_ENTRIES (sizeof(hfrcoCalTable) \ |
| / sizeof(struct hfrcoCalTableElement)) |
| |
| /******************************************************************************* |
| ************************** LOCAL PROTOTYPES ******************************* |
| ******************************************************************************/ |
| |
| static void dpllRefClkGet(uint32_t *freq, CMU_Select_TypeDef *sel); |
| static void em01GrpaClkGet(uint32_t *freq, CMU_Select_TypeDef *sel); |
| static void em23GrpaClkGet(uint32_t *freq, CMU_Select_TypeDef *sel); |
| static void em4GrpaClkGet(uint32_t *freq, CMU_Select_TypeDef *sel); |
| static uint32_t HFRCODPLLDevinfoGet(CMU_HFRCODPLLFreq_TypeDef freq); |
| static uint32_t HFRCOEM23DevinfoGet(CMU_HFRCOEM23Freq_TypeDef freq); |
| static void iadcClkGet(uint32_t *freq, CMU_Select_TypeDef *sel); |
| static void pclkDivMax(void); |
| static void pclkDivOptimize(void); |
| static void rtccClkGet(uint32_t *freq, CMU_Select_TypeDef *sel); |
| static void traceClkGet(uint32_t *freq, CMU_Select_TypeDef *sel); |
| static void waitStateMax(void); |
| static void waitStateSet(uint32_t coreFreq); |
| static void wdog0ClkGet(uint32_t *freq, CMU_Select_TypeDef *sel); |
| static void wdog1ClkGet(uint32_t *freq, CMU_Select_TypeDef *sel); |
| |
| /** @endcond */ |
| |
| /******************************************************************************* |
| ************************** GLOBAL FUNCTIONS ******************************* |
| ******************************************************************************/ |
| |
| /***************************************************************************//** |
| * @brief |
| * Calibrate an oscillator. |
| * |
| * @details |
| * Run a calibration of a selectable reference clock againt HCLK. Please |
| * refer to the reference manual, CMU chapter, for further details. |
| * |
| * @note |
| * This function will not return until calibration measurement is completed. |
| * |
| * @param[in] cycles |
| * The number of HCLK cycles to run calibration. Increasing this number |
| * increases precision, but the calibration will take more time. |
| * |
| * @param[in] ref |
| * The reference clock used to compare against HCLK. |
| * |
| * @return |
| * The number of ticks the selected reference clock ticked while running |
| * cycles ticks of the HCLK clock. |
| ******************************************************************************/ |
| uint32_t CMU_Calibrate(uint32_t cycles, CMU_Select_TypeDef ref) |
| { |
| // Check for cycle count overflow |
| EFM_ASSERT(cycles <= (_CMU_CALCTRL_CALTOP_MASK |
| >> _CMU_CALCTRL_CALTOP_SHIFT)); |
| |
| CMU_CalibrateConfig(cycles, cmuSelect_HCLK, ref); |
| CMU_CalibrateStart(); |
| return CMU_CalibrateCountGet(); |
| } |
| |
| /***************************************************************************//** |
| * @brief |
| * Configure clock calibration. |
| * |
| * @details |
| * Configure a calibration for a selectable clock source against another |
| * selectable reference clock. |
| * Refer to the reference manual, CMU chapter, for further details. |
| * |
| * @note |
| * After configuration, a call to @ref CMU_CalibrateStart() is required, and |
| * the resulting calibration value can be read with the |
| * @ref CMU_CalibrateCountGet() function call. |
| * |
| * @param[in] downCycles |
| * The number of downSel clock cycles to run calibration. Increasing this |
| * number increases precision, but the calibration will take more time. |
| * |
| * @param[in] downSel |
| * The clock which will be counted down downCycles cycles. |
| * |
| * @param[in] upSel |
| * The reference clock, the number of cycles generated by this clock will |
| * be counted and added up, the result can be given with the |
| * @ref CMU_CalibrateCountGet() function call. |
| ******************************************************************************/ |
| void CMU_CalibrateConfig(uint32_t downCycles, CMU_Select_TypeDef downSel, |
| CMU_Select_TypeDef upSel) |
| { |
| // Keep untouched configuration settings |
| uint32_t calCtrl = CMU->CALCTRL |
| & ~(_CMU_CALCTRL_UPSEL_MASK |
| | _CMU_CALCTRL_DOWNSEL_MASK |
| | _CMU_CALCTRL_CALTOP_MASK); |
| |
| // Check for cycle count overflow |
| EFM_ASSERT(downCycles <= (_CMU_CALCTRL_CALTOP_MASK |
| >> _CMU_CALCTRL_CALTOP_SHIFT)); |
| calCtrl |= downCycles; |
| |
| // Set down counting clock source selector |
| switch (downSel) { |
| case cmuSelect_HCLK: |
| calCtrl |= CMU_CALCTRL_DOWNSEL_HCLK; |
| break; |
| |
| case cmuSelect_PRS: |
| calCtrl |= CMU_CALCTRL_DOWNSEL_PRS; |
| break; |
| |
| case cmuSelect_HFXO: |
| calCtrl |= CMU_CALCTRL_DOWNSEL_HFXO; |
| break; |
| |
| case cmuSelect_LFXO: |
| calCtrl |= CMU_CALCTRL_DOWNSEL_LFXO; |
| break; |
| |
| case cmuSelect_HFRCODPLL: |
| calCtrl |= CMU_CALCTRL_DOWNSEL_HFRCODPLL; |
| break; |
| |
| case cmuSelect_HFRCOEM23: |
| calCtrl |= CMU_CALCTRL_DOWNSEL_HFRCOEM23; |
| break; |
| |
| case cmuSelect_FSRCO: |
| calCtrl |= CMU_CALCTRL_DOWNSEL_FSRCO; |
| break; |
| |
| case cmuSelect_LFRCO: |
| calCtrl |= CMU_CALCTRL_DOWNSEL_LFRCO; |
| break; |
| |
| case cmuSelect_ULFRCO: |
| calCtrl |= CMU_CALCTRL_DOWNSEL_ULFRCO; |
| break; |
| |
| case cmuSelect_Disabled: |
| break; |
| |
| default: |
| EFM_ASSERT(false); |
| break; |
| } |
| |
| // Set up counting clock source selector |
| switch (upSel) { |
| case cmuSelect_PRS: |
| calCtrl |= CMU_CALCTRL_UPSEL_PRS; |
| break; |
| |
| case cmuSelect_HFXO: |
| calCtrl |= CMU_CALCTRL_UPSEL_HFXO; |
| break; |
| |
| case cmuSelect_LFXO: |
| calCtrl |= CMU_CALCTRL_UPSEL_LFXO; |
| break; |
| |
| case cmuSelect_HFRCODPLL: |
| calCtrl |= CMU_CALCTRL_UPSEL_HFRCODPLL; |
| break; |
| |
| case cmuSelect_HFRCOEM23: |
| calCtrl |= CMU_CALCTRL_UPSEL_HFRCOEM23; |
| break; |
| |
| case cmuSelect_FSRCO: |
| calCtrl |= CMU_CALCTRL_UPSEL_FSRCO; |
| break; |
| |
| case cmuSelect_LFRCO: |
| calCtrl |= CMU_CALCTRL_UPSEL_LFRCO; |
| break; |
| |
| case cmuSelect_ULFRCO: |
| calCtrl |= CMU_CALCTRL_UPSEL_ULFRCO; |
| break; |
| |
| case cmuSelect_Disabled: |
| break; |
| |
| default: |
| EFM_ASSERT(false); |
| break; |
| } |
| |
| CMU->CALCTRL = calCtrl; |
| } |
| |
| /***************************************************************************//** |
| * @brief |
| * Get calibration count value. |
| * |
| * @note |
| * If continuous calibrartion mode is active, calibration busy will almost |
| * always be off, and we just need to read the value, where the normal case |
| * would be that this function call has been triggered by the CALRDY |
| * interrupt flag. |
| * |
| * @return |
| * Calibration count, the number of UPSEL clocks (see @ref CMU_CalibrateConfig()) |
| * in the period of DOWNSEL oscillator clock cycles configured by a previous |
| * write operation to CMU->CALCNT. |
| ******************************************************************************/ |
| uint32_t CMU_CalibrateCountGet(void) |
| { |
| // Wait until calibration completes, UNLESS continuous calibration mode is on |
| if ((CMU->CALCTRL & CMU_CALCTRL_CONT) == 0UL) { |
| // Wait until calibration completes |
| while ((CMU->STATUS & CMU_STATUS_CALRDY) == 0UL) { |
| } |
| } |
| return CMU->CALCNT; |
| } |
| |
| /***************************************************************************//** |
| * @brief |
| * Direct a clock to a GPIO pin. |
| * |
| * @param[in] clkNo |
| * Selects between CLKOUT0, CLKOUT1 or CLKOUT2 outputs. Use values 0,1or 2. |
| * |
| * @param[in] sel |
| * Select clock source. |
| * |
| * @param[in] clkDiv |
| * Select a clock divisor (1..32). Only applicable when cmuSelect_EXPCLK is |
| * slexted as clock source. |
| * |
| * @param[in] port |
| * GPIO port. |
| * |
| * @param[in] pin |
| * GPIO pin. |
| * |
| * @note |
| * Refer to the reference manual and the datasheet for details on which |
| * GPIO port/pins that are available. |
| ******************************************************************************/ |
| void CMU_ClkOutPinConfig(uint32_t clkNo, |
| CMU_Select_TypeDef sel, |
| CMU_ClkDiv_TypeDef clkDiv, |
| GPIO_Port_TypeDef port, |
| unsigned int pin) |
| { |
| uint32_t tmp = 0U, mask; |
| |
| EFM_ASSERT(clkNo <= 2U); |
| EFM_ASSERT(clkDiv <= 32U); |
| EFM_ASSERT(port <= 3U); |
| EFM_ASSERT(pin <= 15U); |
| |
| switch (sel) { |
| case cmuSelect_Disabled: |
| tmp = CMU_EXPORTCLKCTRL_CLKOUTSEL0_DISABLED; |
| break; |
| |
| case cmuSelect_FSRCO: |
| tmp = CMU_EXPORTCLKCTRL_CLKOUTSEL0_FSRCO; |
| break; |
| |
| case cmuSelect_HFXO: |
| tmp = CMU_EXPORTCLKCTRL_CLKOUTSEL0_HFXO; |
| break; |
| |
| case cmuSelect_HFRCODPLL: |
| tmp = CMU_EXPORTCLKCTRL_CLKOUTSEL0_HFRCODPLL; |
| break; |
| |
| case cmuSelect_HFRCOEM23: |
| tmp = CMU_EXPORTCLKCTRL_CLKOUTSEL0_HFRCOEM23; |
| break; |
| |
| case cmuSelect_EXPCLK: |
| tmp = CMU_EXPORTCLKCTRL_CLKOUTSEL0_HFEXPCLK; |
| break; |
| |
| case cmuSelect_LFXO: |
| tmp = CMU_EXPORTCLKCTRL_CLKOUTSEL0_LFXO; |
| break; |
| |
| case cmuSelect_LFRCO: |
| tmp = CMU_EXPORTCLKCTRL_CLKOUTSEL0_LFRCO; |
| break; |
| |
| case cmuSelect_ULFRCO: |
| tmp = CMU_EXPORTCLKCTRL_CLKOUTSEL0_ULFRCO; |
| break; |
| |
| case cmuSelect_HCLK: |
| tmp = CMU_EXPORTCLKCTRL_CLKOUTSEL0_HCLK; |
| break; |
| |
| default: |
| EFM_ASSERT(false); |
| break; |
| } |
| |
| mask = _CMU_EXPORTCLKCTRL_CLKOUTSEL0_MASK |
| << (clkNo * _CMU_EXPORTCLKCTRL_CLKOUTSEL1_SHIFT); |
| tmp <<= clkNo * _CMU_EXPORTCLKCTRL_CLKOUTSEL1_SHIFT; |
| |
| if (sel == cmuSelect_EXPCLK) { |
| tmp |= (clkDiv - 1U) << _CMU_EXPORTCLKCTRL_PRESC_SHIFT; |
| mask |= _CMU_EXPORTCLKCTRL_PRESC_MASK; |
| } |
| |
| CMU->EXPORTCLKCTRL = (CMU->EXPORTCLKCTRL & ~mask) | tmp; |
| |
| if (sel == cmuSelect_Disabled) { |
| GPIO->CMUROUTE_CLR.ROUTEEN = GPIO_CMU_ROUTEEN_CLKOUT0PEN << clkNo; |
| GPIO_PinModeSet(port, pin, gpioModeDisabled, 0); |
| } else { |
| GPIO->CMUROUTE_SET.ROUTEEN = GPIO_CMU_ROUTEEN_CLKOUT0PEN << clkNo; |
| if (clkNo == 0U) { |
| GPIO->CMUROUTE.CLKOUT0ROUTE = (port << _GPIO_CMU_CLKOUT0ROUTE_PORT_SHIFT) |
| | (pin << _GPIO_CMU_CLKOUT0ROUTE_PIN_SHIFT); |
| } else if (clkNo == 1) { |
| GPIO->CMUROUTE.CLKOUT1ROUTE = (port << _GPIO_CMU_CLKOUT1ROUTE_PORT_SHIFT) |
| | (pin << _GPIO_CMU_CLKOUT1ROUTE_PIN_SHIFT); |
| } else { |
| GPIO->CMUROUTE.CLKOUT2ROUTE = (port << _GPIO_CMU_CLKOUT2ROUTE_PORT_SHIFT) |
| | (pin << _GPIO_CMU_CLKOUT2ROUTE_PIN_SHIFT); |
| } |
| GPIO_PinModeSet(port, pin, gpioModePushPull, 0); |
| } |
| } |
| |
| /***************************************************************************//** |
| * @brief |
| * Get clock divisor. |
| * |
| * @param[in] clock |
| * Clock point to get divisor for. Notice that not all clock points |
| * have a divisors. Please refer to CMU overview in reference manual. |
| * |
| * @return |
| * The current clock point divisor. 1 is returned |
| * if @p clock specifies a clock point without divisor. |
| ******************************************************************************/ |
| CMU_ClkDiv_TypeDef CMU_ClockDivGet(CMU_Clock_TypeDef clock) |
| { |
| uint32_t ret = 0U; |
| |
| switch (clock) { |
| case cmuClock_HCLK: |
| case cmuClock_CORE: |
| ret = (CMU->SYSCLKCTRL & _CMU_SYSCLKCTRL_HCLKPRESC_MASK) |
| >> _CMU_SYSCLKCTRL_HCLKPRESC_SHIFT; |
| if (ret == 2U ) { // Unused value, illegal prescaler |
| EFM_ASSERT(false); |
| } |
| break; |
| |
| case cmuClock_EXPCLK: |
| ret = (CMU->EXPORTCLKCTRL & _CMU_EXPORTCLKCTRL_PRESC_MASK) |
| >> _CMU_EXPORTCLKCTRL_PRESC_SHIFT; |
| break; |
| |
| case cmuClock_PCLK: |
| ret = (CMU->SYSCLKCTRL & _CMU_SYSCLKCTRL_PCLKPRESC_MASK) |
| >> _CMU_SYSCLKCTRL_PCLKPRESC_SHIFT; |
| break; |
| |
| default: |
| EFM_ASSERT(false); |
| break; |
| } |
| return 1U + ret; |
| } |
| |
| /***************************************************************************//** |
| * @brief |
| * Set clock divisor. |
| * |
| * @param[in] clock |
| * Clock point to set divisor for. Notice that not all clock points |
| * have a divisor, please refer to CMU overview in the reference |
| * manual. |
| * |
| * @param[in] div |
| * The clock divisor to use. |
| ******************************************************************************/ |
| void CMU_ClockDivSet(CMU_Clock_TypeDef clock, CMU_ClkDiv_TypeDef div) |
| { |
| switch (clock) { |
| case cmuClock_HCLK: |
| case cmuClock_CORE: |
| EFM_ASSERT((div == 1U) || (div == 2U) || (div == 4U)); |
| |
| // Set max wait-states and PCLK divisor while changing core clock |
| waitStateMax(); |
| pclkDivMax(); |
| |
| // Set new divisor |
| CMU->SYSCLKCTRL = (CMU->SYSCLKCTRL & ~_CMU_SYSCLKCTRL_HCLKPRESC_MASK) |
| | ((div - 1U) << _CMU_SYSCLKCTRL_HCLKPRESC_SHIFT); |
| |
| // Update CMSIS core clock variable and set optimum wait-states |
| CMU_UpdateWaitStates(SystemCoreClockGet(), 0); |
| |
| // Set optimal PCLK divisor |
| pclkDivOptimize(); |
| break; |
| |
| case cmuClock_EXPCLK: |
| EFM_ASSERT((div >= 1U) && (div <= 32U)); |
| CMU->EXPORTCLKCTRL = (CMU->EXPORTCLKCTRL & ~_CMU_EXPORTCLKCTRL_PRESC_MASK) |
| | ((div - 1U) << _CMU_EXPORTCLKCTRL_PRESC_SHIFT); |
| break; |
| |
| case cmuClock_PCLK: |
| EFM_ASSERT((div == 1U) || (div == 2U)); |
| CMU->SYSCLKCTRL = (CMU->SYSCLKCTRL & ~_CMU_SYSCLKCTRL_PCLKPRESC_MASK) |
| | ((div - 1U) << _CMU_SYSCLKCTRL_PCLKPRESC_SHIFT); |
| break; |
| |
| default: |
| EFM_ASSERT(false); |
| break; |
| } |
| } |
| |
| /***************************************************************************//** |
| * @brief |
| * Get clock frequency for a clock point. |
| * |
| * @param[in] clock |
| * Clock point to fetch frequency for. |
| * |
| * @return |
| * The current frequency in Hz. |
| ******************************************************************************/ |
| uint32_t CMU_ClockFreqGet(CMU_Clock_TypeDef clock) |
| { |
| uint32_t ret = 0U; |
| |
| switch (clock) { |
| case cmuClock_SYSCLK: |
| ret = SystemSYSCLKGet(); |
| break; |
| |
| case cmuClock_CORE: |
| case cmuClock_HCLK: |
| case cmuClock_LDMA: |
| case cmuClock_GPCRC: |
| ret = SystemHCLKGet(); |
| break; |
| |
| case cmuClock_EXPCLK: |
| ret = SystemSYSCLKGet() / CMU_ClockDivGet(cmuClock_EXPCLK); |
| break; |
| |
| case cmuClock_I2C1: |
| case cmuClock_PRS: |
| case cmuClock_PCLK: |
| case cmuClock_GPIO: |
| case cmuClock_USART0: |
| case cmuClock_USART1: |
| case cmuClock_USART2: |
| ret = SystemHCLKGet() / CMU_ClockDivGet(cmuClock_PCLK); |
| break; |
| |
| case cmuClock_I2C0: |
| case cmuClock_LSPCLK: |
| ret = SystemHCLKGet() / CMU_ClockDivGet(cmuClock_PCLK) / 2U; |
| break; |
| |
| case cmuClock_IADC0: |
| case cmuClock_IADCCLK: |
| iadcClkGet(&ret, NULL); |
| break; |
| |
| case cmuClock_TIMER0: |
| case cmuClock_TIMER1: |
| case cmuClock_TIMER2: |
| case cmuClock_TIMER3: |
| case cmuClock_EM01GRPACLK: |
| em01GrpaClkGet(&ret, NULL); |
| break; |
| |
| case cmuClock_SYSTICK: |
| case cmuClock_LETIMER0: |
| case cmuClock_EM23GRPACLK: |
| em23GrpaClkGet(&ret, NULL); |
| break; |
| |
| case cmuClock_BURTC: |
| case cmuClock_EM4GRPACLK: |
| em4GrpaClkGet(&ret, NULL); |
| break; |
| |
| case cmuClock_WDOG0: |
| case cmuClock_WDOG0CLK: |
| wdog0ClkGet(&ret, NULL); |
| break; |
| |
| case cmuClock_WDOG1: |
| case cmuClock_WDOG1CLK: |
| wdog1ClkGet(&ret, NULL); |
| break; |
| |
| case cmuClock_DPLLREFCLK: |
| dpllRefClkGet(&ret, NULL); |
| break; |
| |
| case cmuClock_TRACECLK: |
| traceClkGet(&ret, NULL); |
| break; |
| |
| case cmuClock_RTCC: |
| case cmuClock_RTCCCLK: |
| rtccClkGet(&ret, NULL); |
| break; |
| |
| default: |
| EFM_ASSERT(false); |
| break; |
| } |
| return ret; |
| } |
| |
| /***************************************************************************//** |
| * @brief |
| * Get currently selected reference clock used for a clock branch. |
| * |
| * @param[in] clock |
| * Clock branch to fetch selected ref. clock for. |
| * |
| * @return |
| * Reference clock used for clocking selected branch, #cmuSelect_Error if |
| * invalid @p clock provided. |
| ******************************************************************************/ |
| CMU_Select_TypeDef CMU_ClockSelectGet(CMU_Clock_TypeDef clock) |
| { |
| CMU_Select_TypeDef ret = cmuSelect_Error; |
| |
| switch (clock) { |
| // ----------------------------------------------------------------------------- |
| case cmuClock_SYSCLK: |
| switch (CMU->SYSCLKCTRL & _CMU_SYSCLKCTRL_CLKSEL_MASK) { |
| case _CMU_SYSCLKCTRL_CLKSEL_HFRCODPLL: |
| ret = cmuSelect_HFRCODPLL; |
| break; |
| |
| case _CMU_SYSCLKCTRL_CLKSEL_HFXO: |
| ret = cmuSelect_HFXO; |
| break; |
| |
| case _CMU_SYSCLKCTRL_CLKSEL_CLKIN0: |
| ret = cmuSelect_CLKIN0; |
| break; |
| |
| case _CMU_SYSCLKCTRL_CLKSEL_FSRCO: |
| ret = cmuSelect_FSRCO; |
| break; |
| |
| default: |
| ret = cmuSelect_Error; |
| EFM_ASSERT(false); |
| break; |
| } |
| break; |
| |
| // ----------------------------------------------------------------------------- |
| case cmuClock_IADC0: |
| case cmuClock_IADCCLK: |
| iadcClkGet(NULL, &ret); |
| break; |
| |
| // ----------------------------------------------------------------------------- |
| case cmuClock_TIMER0: |
| case cmuClock_TIMER1: |
| case cmuClock_TIMER2: |
| case cmuClock_TIMER3: |
| case cmuClock_EM01GRPACLK: |
| em01GrpaClkGet(NULL, &ret); |
| break; |
| |
| // ----------------------------------------------------------------------------- |
| case cmuClock_SYSTICK: |
| case cmuClock_LETIMER0: |
| case cmuClock_EM23GRPACLK: |
| em23GrpaClkGet(NULL, &ret); |
| break; |
| |
| // ----------------------------------------------------------------------------- |
| case cmuClock_BURTC: |
| case cmuClock_EM4GRPACLK: |
| em4GrpaClkGet(NULL, &ret); |
| break; |
| |
| // ----------------------------------------------------------------------------- |
| case cmuClock_WDOG0: |
| case cmuClock_WDOG0CLK: |
| wdog0ClkGet(NULL, &ret); |
| break; |
| |
| // ----------------------------------------------------------------------------- |
| case cmuClock_WDOG1: |
| case cmuClock_WDOG1CLK: |
| wdog1ClkGet(NULL, &ret); |
| break; |
| |
| // ----------------------------------------------------------------------------- |
| case cmuClock_DPLLREFCLK: |
| dpllRefClkGet(NULL, &ret); |
| break; |
| |
| // ----------------------------------------------------------------------------- |
| case cmuClock_TRACECLK: |
| traceClkGet(NULL, &ret); |
| break; |
| |
| // ----------------------------------------------------------------------------- |
| case cmuClock_RTCC: |
| case cmuClock_RTCCCLK: |
| rtccClkGet(NULL, &ret); |
| break; |
| |
| // ----------------------------------------------------------------------------- |
| default: |
| EFM_ASSERT(false); |
| break; |
| } |
| return ret; |
| } |
| |
| /***************************************************************************//** |
| * @brief |
| * Select reference clock/oscillator used for a clock branch. |
| * |
| * @param[in] clock |
| * Clock branch to select reference clock for. |
| * |
| * @param[in] ref |
| * Reference selected for clocking, please refer to reference manual for |
| * for details on which reference is available for a specific clock branch. |
| ******************************************************************************/ |
| void CMU_ClockSelectSet(CMU_Clock_TypeDef clock, CMU_Select_TypeDef ref) |
| { |
| uint32_t tmp = 0U; |
| bool oscForceEnStatus = false; |
| |
| switch (clock) { |
| // ----------------------------------------------------------------------------- |
| case cmuClock_SYSCLK: |
| switch (ref) { |
| case cmuSelect_HFRCODPLL: |
| tmp = CMU_SYSCLKCTRL_CLKSEL_HFRCODPLL; |
| // Make sure HFRCO0 is enabled and ready |
| oscForceEnStatus = (HFRCO0->CTRL & HFRCO_CTRL_DISONDEMAND) != 0; |
| HFRCO0->CTRL_SET = HFRCO_CTRL_FORCEEN; |
| while ((HFRCO0->STATUS & HFRCO_STATUS_RDY) == 0) { |
| } |
| break; |
| |
| case cmuSelect_HFXO: |
| tmp = CMU_SYSCLKCTRL_CLKSEL_HFXO; |
| // Make sure HFXO is enabled and ready |
| oscForceEnStatus = (HFXO0->CTRL & HFXO_CTRL_DISONDEMAND) != 0; |
| HFXO0->CTRL_SET = HFXO_CTRL_FORCEEN; |
| while ((HFXO0->STATUS & HFXO_STATUS_RDY) == 0) { |
| } |
| break; |
| |
| case cmuSelect_CLKIN0: |
| tmp = CMU_SYSCLKCTRL_CLKSEL_CLKIN0; |
| break; |
| |
| case cmuSelect_FSRCO: |
| tmp = CMU_SYSCLKCTRL_CLKSEL_FSRCO; |
| break; |
| |
| default: |
| EFM_ASSERT(false); |
| break; |
| } |
| |
| // Set max wait-states and PCLK divisor while changing core clock |
| waitStateMax(); |
| pclkDivMax(); |
| |
| // Switch to selected oscillator |
| CMU->SYSCLKCTRL = (CMU->SYSCLKCTRL & ~_CMU_SYSCLKCTRL_CLKSEL_MASK) | tmp; |
| |
| // Update CMSIS core clock variable and set optimum wait-states |
| CMU_UpdateWaitStates(SystemCoreClockGet(), 0); |
| |
| // Set optimal PCLK divisor |
| pclkDivOptimize(); |
| |
| if (oscForceEnStatus == false) { |
| switch (ref) { |
| case cmuSelect_HFRCODPLL: |
| HFRCO0->CTRL_CLR = HFRCO_CTRL_FORCEEN; |
| break; |
| |
| case cmuSelect_HFXO: |
| HFXO0->CTRL_CLR = HFXO_CTRL_FORCEEN; |
| break; |
| |
| default: |
| break; |
| } |
| } |
| break; |
| |
| // ----------------------------------------------------------------------------- |
| case cmuClock_IADC0: |
| case cmuClock_IADCCLK: |
| switch (ref) { |
| case cmuSelect_EM01GRPACLK: |
| tmp = CMU_IADCCLKCTRL_CLKSEL_EM01GRPACLK; |
| break; |
| |
| case cmuSelect_HFRCOEM23: |
| tmp = CMU_IADCCLKCTRL_CLKSEL_HFRCOEM23; |
| break; |
| |
| case cmuSelect_FSRCO: |
| tmp = CMU_IADCCLKCTRL_CLKSEL_FSRCO; |
| break; |
| |
| default: |
| EFM_ASSERT(false); |
| break; |
| } |
| CMU->IADCCLKCTRL = (CMU->IADCCLKCTRL & ~_CMU_IADCCLKCTRL_CLKSEL_MASK) |
| | tmp; |
| break; |
| |
| // ----------------------------------------------------------------------------- |
| case cmuClock_TIMER0: |
| case cmuClock_TIMER1: |
| case cmuClock_TIMER2: |
| case cmuClock_TIMER3: |
| case cmuClock_EM01GRPACLK: |
| switch (ref) { |
| case cmuSelect_HFRCODPLL: |
| tmp = CMU_EM01GRPACLKCTRL_CLKSEL_HFRCODPLL; |
| break; |
| |
| case cmuSelect_HFXO: |
| tmp = CMU_EM01GRPACLKCTRL_CLKSEL_HFXO; |
| break; |
| |
| case cmuSelect_HFRCOEM23: |
| tmp = CMU_EM01GRPACLKCTRL_CLKSEL_HFRCOEM23; |
| break; |
| |
| case cmuSelect_FSRCO: |
| tmp = CMU_EM01GRPACLKCTRL_CLKSEL_FSRCO; |
| break; |
| |
| default: |
| EFM_ASSERT(false); |
| break; |
| } |
| CMU->EM01GRPACLKCTRL = (CMU->EM01GRPACLKCTRL |
| & ~_CMU_EM01GRPACLKCTRL_CLKSEL_MASK) | tmp; |
| break; |
| |
| // ----------------------------------------------------------------------------- |
| case cmuClock_SYSTICK: |
| case cmuClock_LETIMER0: |
| case cmuClock_EM23GRPACLK: |
| switch (ref) { |
| case cmuSelect_LFRCO: |
| tmp = CMU_EM23GRPACLKCTRL_CLKSEL_LFRCO; |
| break; |
| |
| case cmuSelect_LFXO: |
| tmp = CMU_EM23GRPACLKCTRL_CLKSEL_LFXO; |
| break; |
| |
| case cmuSelect_ULFRCO: |
| tmp = CMU_EM23GRPACLKCTRL_CLKSEL_ULFRCO; |
| break; |
| |
| default: |
| EFM_ASSERT(false); |
| break; |
| } |
| CMU->EM23GRPACLKCTRL = (CMU->EM23GRPACLKCTRL |
| & ~_CMU_EM23GRPACLKCTRL_CLKSEL_MASK) | tmp; |
| break; |
| |
| // ----------------------------------------------------------------------------- |
| case cmuClock_BURTC: |
| case cmuClock_EM4GRPACLK: |
| switch (ref) { |
| case cmuSelect_LFRCO: |
| tmp = CMU_EM4GRPACLKCTRL_CLKSEL_LFRCO; |
| break; |
| |
| case cmuSelect_LFXO: |
| tmp = CMU_EM4GRPACLKCTRL_CLKSEL_LFXO; |
| break; |
| |
| case cmuSelect_ULFRCO: |
| tmp = CMU_EM4GRPACLKCTRL_CLKSEL_ULFRCO; |
| break; |
| |
| default: |
| EFM_ASSERT(false); |
| break; |
| } |
| CMU->EM4GRPACLKCTRL = (CMU->EM4GRPACLKCTRL |
| & ~_CMU_EM4GRPACLKCTRL_CLKSEL_MASK) | tmp; |
| break; |
| |
| // ----------------------------------------------------------------------------- |
| case cmuClock_WDOG0: |
| case cmuClock_WDOG0CLK: |
| switch (ref) { |
| case cmuSelect_LFRCO: |
| tmp = CMU_WDOG0CLKCTRL_CLKSEL_LFRCO; |
| break; |
| |
| case cmuSelect_LFXO: |
| tmp = CMU_WDOG0CLKCTRL_CLKSEL_LFXO; |
| break; |
| |
| case cmuSelect_ULFRCO: |
| tmp = CMU_WDOG0CLKCTRL_CLKSEL_ULFRCO; |
| break; |
| |
| case cmuSelect_HCLKDIV1024: |
| tmp = CMU_WDOG0CLKCTRL_CLKSEL_HCLKDIV1024; |
| break; |
| |
| default: |
| EFM_ASSERT(false); |
| break; |
| } |
| CMU->WDOG0CLKCTRL = (CMU->WDOG0CLKCTRL & ~_CMU_WDOG0CLKCTRL_CLKSEL_MASK) |
| | tmp; |
| break; |
| |
| // ----------------------------------------------------------------------------- |
| case cmuClock_WDOG1: |
| case cmuClock_WDOG1CLK: |
| switch (ref) { |
| case cmuSelect_LFRCO: |
| tmp = CMU_WDOG1CLKCTRL_CLKSEL_LFRCO; |
| break; |
| |
| case cmuSelect_LFXO: |
| tmp = CMU_WDOG1CLKCTRL_CLKSEL_LFXO; |
| break; |
| |
| case cmuSelect_ULFRCO: |
| tmp = CMU_WDOG1CLKCTRL_CLKSEL_ULFRCO; |
| break; |
| |
| case cmuSelect_HCLKDIV1024: |
| tmp = CMU_WDOG1CLKCTRL_CLKSEL_HCLKDIV1024; |
| break; |
| |
| default: |
| EFM_ASSERT(false); |
| break; |
| } |
| CMU->WDOG1CLKCTRL = (CMU->WDOG1CLKCTRL & ~_CMU_WDOG1CLKCTRL_CLKSEL_MASK) |
| | tmp; |
| break; |
| |
| // ----------------------------------------------------------------------------- |
| case cmuClock_DPLLREFCLK: |
| switch (ref) { |
| case cmuSelect_HFXO: |
| tmp = CMU_DPLLREFCLKCTRL_CLKSEL_HFXO; |
| break; |
| |
| case cmuSelect_LFXO: |
| tmp = CMU_DPLLREFCLKCTRL_CLKSEL_LFXO; |
| break; |
| |
| case cmuSelect_CLKIN0: |
| tmp = CMU_DPLLREFCLKCTRL_CLKSEL_CLKIN0; |
| break; |
| |
| case cmuSelect_Disabled: |
| tmp = CMU_DPLLREFCLKCTRL_CLKSEL_DISABLED; |
| break; |
| |
| default: |
| EFM_ASSERT(false); |
| break; |
| } |
| CMU->DPLLREFCLKCTRL = (CMU->DPLLREFCLKCTRL |
| & ~_CMU_DPLLREFCLKCTRL_CLKSEL_MASK) | tmp; |
| break; |
| |
| // ----------------------------------------------------------------------------- |
| case cmuClock_TRACECLK: |
| switch (ref) { |
| case cmuSelect_PCLK: |
| tmp = CMU_TRACECLKCTRL_CLKSEL_PCLK; |
| break; |
| |
| case cmuSelect_HCLK: |
| tmp = CMU_TRACECLKCTRL_CLKSEL_HCLK; |
| break; |
| |
| case cmuSelect_HFRCOEM23: |
| tmp = CMU_TRACECLKCTRL_CLKSEL_HFRCOEM23; |
| break; |
| |
| default: |
| EFM_ASSERT(false); |
| break; |
| } |
| CMU->TRACECLKCTRL = (CMU->TRACECLKCTRL & ~_CMU_TRACECLKCTRL_CLKSEL_MASK) |
| | tmp; |
| break; |
| |
| // ----------------------------------------------------------------------------- |
| case cmuClock_RTCC: |
| case cmuClock_RTCCCLK: |
| switch (ref) { |
| case cmuSelect_LFRCO: |
| tmp = CMU_RTCCCLKCTRL_CLKSEL_LFRCO; |
| break; |
| |
| case cmuSelect_LFXO: |
| tmp = CMU_RTCCCLKCTRL_CLKSEL_LFXO; |
| break; |
| |
| case cmuSelect_ULFRCO: |
| tmp = CMU_RTCCCLKCTRL_CLKSEL_ULFRCO; |
| break; |
| |
| default: |
| EFM_ASSERT(false); |
| break; |
| } |
| CMU->RTCCCLKCTRL = (CMU->RTCCCLKCTRL & ~_CMU_RTCCCLKCTRL_CLKSEL_MASK) |
| | tmp; |
| break; |
| |
| // ----------------------------------------------------------------------------- |
| default: |
| EFM_ASSERT(false); |
| break; |
| } |
| } |
| |
| /**************************************************************************//** |
| * @brief |
| * Lock the DPLL to a given frequency. |
| * The frequency is given by: Fout = Fref * (N+1) / (M+1). |
| * |
| * @note |
| * This function does not check if the given N & M values will actually |
| * produce the desired target frequency. @n |
| * N & M limitations: @n |
| * 300 < N <= 4095 @n |
| * 0 <= M <= 4095 @n |
| * Any peripheral running off HFRCODPLL should be switched to a lower |
| * frequency clock (if possible) prior to calling this function to avoid |
| * over-clocking. |
| * |
| * @param[in] init |
| * DPLL setup parameter struct. |
| * |
| * @return |
| * Returns false on invalid target frequency or DPLL locking error. |
| *****************************************************************************/ |
| bool CMU_DPLLLock(const CMU_DPLLInit_TypeDef *init) |
| { |
| int index = 0; |
| unsigned int i; |
| bool hclkDivIncreased = false; |
| uint32_t hfrcoCalVal, lockStatus, hclkDiv = 0, sysFreq; |
| |
| EFM_ASSERT(init->frequency >= hfrcoCalTable[0].minFreq); |
| EFM_ASSERT(init->frequency |
| <= hfrcoCalTable[HFRCOCALTABLE_ENTRIES - 1U].maxFreq); |
| |
| EFM_ASSERT(init->n > 300U); |
| EFM_ASSERT(init->n <= (_DPLL_CFG1_N_MASK >> _DPLL_CFG1_N_SHIFT)); |
| EFM_ASSERT(init->m <= (_DPLL_CFG1_M_MASK >> _DPLL_CFG1_M_SHIFT)); |
| |
| // Find correct HFRCODPLL band, and retrieve a HFRCOCAL value. |
| for (i = 0; i < HFRCOCALTABLE_ENTRIES; i++) { |
| if ((init->frequency >= hfrcoCalTable[i].minFreq) |
| && (init->frequency <= hfrcoCalTable[i].maxFreq)) { |
| index = (int)i; // Correct band found |
| break; |
| } |
| } |
| if ((uint32_t)index == HFRCOCALTABLE_ENTRIES) { |
| EFM_ASSERT(false); |
| return false; // Target frequency out of spec. |
| } |
| hfrcoCalVal = hfrcoCalTable[index].value; |
| |
| // Check if we have a calibrated HFRCOCAL.TUNING value in device DI page. |
| if (hfrcoCalTable[index].band != (CMU_HFRCODPLLFreq_TypeDef)0) { |
| uint32_t tuning; |
| |
| tuning = (HFRCODPLLDevinfoGet(hfrcoCalTable[index].band) |
| & _HFRCO_CAL_TUNING_MASK) |
| >> _HFRCO_CAL_TUNING_SHIFT; |
| hfrcoCalVal |= tuning << _HFRCO_CAL_TUNING_SHIFT; |
| } |
| |
| // Update CMSIS HFRCODPLL frequency. |
| SystemHFRCODPLLClockSet(init->frequency); |
| |
| if (CMU_ClockSelectGet(cmuClock_SYSCLK) == cmuSelect_HFRCODPLL) { |
| // Set max wait-states and PCLK divisor while changing core clock |
| waitStateMax(); |
| pclkDivMax(); |
| |
| // Increase HCLK divider value (if possible) while locking DPLL to |
| // avoid over-clocking. |
| hclkDiv = CMU_ClockDivGet(cmuClock_HCLK); |
| hclkDivIncreased = true; |
| if (hclkDiv == 1U) { |
| CMU_ClockDivSet(cmuClock_HCLK, 2U); |
| } else if (hclkDiv == 2U) { |
| CMU_ClockDivSet(cmuClock_HCLK, 4U); |
| } else { |
| hclkDivIncreased = false; |
| } |
| } |
| |
| // Make sure DPLL is disabled before configuring |
| DPLL0->EN_CLR = DPLL_EN_EN; |
| while ((DPLL0->STATUS & (DPLL_STATUS_ENS | DPLL_STATUS_RDY)) != 0UL) { |
| } |
| DPLL0->IF_CLR = DPLL_IF_LOCK | DPLL_IF_LOCKFAILLOW | DPLL_IF_LOCKFAILHIGH; |
| DPLL0->CFG1 = ((uint32_t)init->n << _DPLL_CFG1_N_SHIFT) |
| | ((uint32_t)init->m << _DPLL_CFG1_M_SHIFT); |
| HFRCO0->CAL = hfrcoCalVal; |
| CMU_ClockSelectSet(cmuClock_DPLLREFCLK, init->refClk); |
| DPLL0->CFG = ((init->autoRecover ? 1UL : 0UL) << _DPLL_CFG_AUTORECOVER_SHIFT) |
| | ((init->ditherEn ? 1UL : 0UL) << _DPLL_CFG_DITHEN_SHIFT) |
| | ((uint32_t)init->edgeSel << _DPLL_CFG_EDGESEL_SHIFT) |
| | ((uint32_t)init->lockMode << _DPLL_CFG_MODE_SHIFT); |
| // Lock DPLL |
| DPLL0->EN_SET = DPLL_EN_EN; |
| while ((lockStatus = (DPLL0->IF & (DPLL_IF_LOCK |
| | DPLL_IF_LOCKFAILLOW |
| | DPLL_IF_LOCKFAILHIGH))) == 0UL) { |
| } |
| |
| if (CMU_ClockSelectGet(cmuClock_SYSCLK) == cmuSelect_HFRCODPLL) { |
| if (hclkDivIncreased) { |
| // Restore original HCLK divider |
| CMU_ClockDivSet(cmuClock_HCLK, hclkDiv); |
| } |
| |
| // Call @ref SystemCoreClockGet() to update CMSIS core clock variable. |
| sysFreq = SystemCoreClockGet(); |
| EFM_ASSERT(sysFreq <= init->frequency); |
| EFM_ASSERT(sysFreq <= SystemHFRCODPLLClockGet()); |
| EFM_ASSERT(init->frequency == SystemHFRCODPLLClockGet()); |
| |
| // Set optimal wait-states and PCLK divisor |
| CMU_UpdateWaitStates(sysFreq, 0); |
| pclkDivOptimize(); |
| } |
| |
| if (lockStatus == DPLL_IF_LOCK) { |
| return true; |
| } |
| return false; |
| } |
| |
| /***************************************************************************//** |
| * @brief |
| * Get HFRCODPLL band in use. |
| * |
| * @return |
| * HFRCODPLL band in use. |
| ******************************************************************************/ |
| CMU_HFRCODPLLFreq_TypeDef CMU_HFRCODPLLBandGet(void) |
| { |
| return (CMU_HFRCODPLLFreq_TypeDef)SystemHFRCODPLLClockGet(); |
| } |
| |
| /***************************************************************************//** |
| * @brief |
| * Set HFRCODPLL band and the tuning value based on the value in the |
| * calibration table made during production. |
| * |
| * @param[in] freq |
| * HFRCODPLL frequency band to activate. |
| ******************************************************************************/ |
| void CMU_HFRCODPLLBandSet(CMU_HFRCODPLLFreq_TypeDef freq) |
| { |
| uint32_t freqCal, sysFreq; |
| |
| // Get calibration data from DEVINFO |
| freqCal = HFRCODPLLDevinfoGet(freq); |
| EFM_ASSERT((freqCal != 0UL) && (freqCal != UINT_MAX)); |
| |
| // Make sure DPLL is disabled before configuring |
| if (DPLL0->EN_CLR == DPLL_EN_EN) { |
| DPLL0->EN_CLR = DPLL_EN_EN; |
| while ((DPLL0->STATUS & (DPLL_STATUS_ENS | DPLL_STATUS_RDY)) != 0UL) { |
| } |
| } |
| |
| // Update CMSIS HFRCODPLL frequency. |
| SystemHFRCODPLLClockSet(freq); |
| |
| // Set max wait-states and PCLK divisor while changing core clock |
| if (CMU_ClockSelectGet(cmuClock_SYSCLK) == cmuSelect_HFRCODPLL) { |
| waitStateMax(); |
| pclkDivMax(); |
| } |
| |
| // Set divider for 1, 2 and 4MHz bands |
| freqCal &= ~_HFRCO_CAL_CLKDIV_MASK; |
| switch (freq) { |
| case cmuHFRCODPLLFreq_1M0Hz: |
| freqCal |= HFRCO_CAL_CLKDIV_DIV4; |
| break; |
| |
| case cmuHFRCODPLLFreq_2M0Hz: |
| freqCal |= HFRCO_CAL_CLKDIV_DIV2; |
| break; |
| |
| default: |
| break; |
| } |
| |
| // Activate new band selection |
| HFRCO0->CAL = freqCal; |
| |
| // If HFRCODPLL is selected as SYSCLK (and HCLK), optimize flash access |
| // wait-state configuration and PCLK divisor for this frequency. |
| if (CMU_ClockSelectGet(cmuClock_SYSCLK) == cmuSelect_HFRCODPLL) { |
| // Call @ref SystemCoreClockGet() to update CMSIS core clock variable. |
| sysFreq = SystemCoreClockGet(); |
| EFM_ASSERT(sysFreq <= (uint32_t)freq); |
| CMU_UpdateWaitStates(sysFreq, 0); |
| pclkDivOptimize(); |
| } |
| } |
| |
| /***************************************************************************//** |
| * @brief |
| * Get HFRCOEM23 band in use. |
| * |
| * @return |
| * HFRCOEM23 band in use. |
| ******************************************************************************/ |
| CMU_HFRCOEM23Freq_TypeDef CMU_HFRCOEM23BandGet(void) |
| { |
| return (CMU_HFRCOEM23Freq_TypeDef)SystemHFRCOEM23ClockGet(); |
| } |
| |
| /***************************************************************************//** |
| * @brief |
| * Set HFRCOEM23 band and the tuning value based on the value in the |
| * calibration table made during production. |
| * |
| * @param[in] freq |
| * HFRCOEM23 frequency band to activate. |
| ******************************************************************************/ |
| void CMU_HFRCOEM23BandSet(CMU_HFRCOEM23Freq_TypeDef freq) |
| { |
| uint32_t freqCal; |
| |
| // Get calibration data from DEVINFO |
| freqCal = HFRCOEM23DevinfoGet(freq); |
| EFM_ASSERT((freqCal != 0UL) && (freqCal != UINT_MAX)); |
| |
| // Set divider for 1, 2 and 4MHz bands |
| freqCal &= ~_HFRCO_CAL_CLKDIV_MASK; |
| switch (freq) { |
| case cmuHFRCOEM23Freq_1M0Hz: |
| freqCal |= HFRCO_CAL_CLKDIV_DIV4; |
| break; |
| |
| case cmuHFRCOEM23Freq_2M0Hz: |
| freqCal |= HFRCO_CAL_CLKDIV_DIV2; |
| break; |
| |
| default: |
| break; |
| } |
| |
| // Activate new band selection |
| HFRCOEM23->CAL = freqCal; |
| } |
| |
| /**************************************************************************//** |
| * @brief |
| * Initialize all 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) |
| { |
| // Check all initialization structure members which may overflow target |
| // bitfield. |
| EFM_ASSERT(hfxoInit->timeoutCbLsb |
| <= (_HFXO_XTALCFG_TIMEOUTCBLSB_MASK |
| >> _HFXO_XTALCFG_TIMEOUTCBLSB_SHIFT)); |
| EFM_ASSERT(hfxoInit->timeoutSteadyFirstLock |
| <= (_HFXO_XTALCFG_TIMEOUTSTEADY_MASK |
| >> _HFXO_XTALCFG_TIMEOUTSTEADY_SHIFT)); |
| EFM_ASSERT(hfxoInit->timeoutSteady |
| <= (_HFXO_XTALCFG_TIMEOUTSTEADY_MASK |
| >> _HFXO_XTALCFG_TIMEOUTSTEADY_SHIFT)); |
| EFM_ASSERT(hfxoInit->ctuneXoStartup |
| <= (_HFXO_XTALCFG_CTUNEXOSTARTUP_MASK |
| >> _HFXO_XTALCFG_CTUNEXOSTARTUP_SHIFT)); |
| EFM_ASSERT(hfxoInit->ctuneXiStartup |
| <= (_HFXO_XTALCFG_CTUNEXISTARTUP_MASK |
| >> _HFXO_XTALCFG_CTUNEXISTARTUP_SHIFT)); |
| EFM_ASSERT(hfxoInit->coreBiasStartup |
| <= (_HFXO_XTALCFG_COREBIASSTARTUP_MASK |
| >> _HFXO_XTALCFG_COREBIASSTARTUP_SHIFT)); |
| EFM_ASSERT(hfxoInit->imCoreBiasStartup |
| <= (_HFXO_XTALCFG_COREBIASSTARTUPI_MASK |
| >> _HFXO_XTALCFG_COREBIASSTARTUPI_SHIFT)); |
| EFM_ASSERT(hfxoInit->coreDegenAna |
| <= (_HFXO_XTALCTRL_COREDGENANA_MASK |
| >> _HFXO_XTALCTRL_COREDGENANA_SHIFT)); |
| EFM_ASSERT(hfxoInit->ctuneFixAna |
| <= (_HFXO_XTALCTRL_CTUNEFIXANA_MASK |
| >> _HFXO_XTALCTRL_CTUNEFIXANA_SHIFT)); |
| EFM_ASSERT(hfxoInit->mode |
| <= (_HFXO_CFG_MODE_MASK >> _HFXO_CFG_MODE_SHIFT)); |
| |
| // Do not disable HFXO if it is currently selected as core clock |
| EFM_ASSERT(CMU_ClockSelectGet(cmuClock_SYSCLK) != cmuSelect_HFXO); |
| |
| // Unlock register interface |
| HFXO0->LOCK = HFXO_LOCK_LOCKKEY_UNLOCK; |
| |
| // Disable HFXO |
| HFXO0->CTRL_SET = HFXO_CTRL_DISONDEMAND; |
| HFXO0->CTRL_CLR = HFXO_CTRL_FORCEEN; |
| while ((HFXO0->STATUS & _HFXO_STATUS_ENS_MASK) != 0U) { |
| } |
| |
| // Configure HFXO as specified in initialization struct, use |
| // timeoutSteadyFirstLock as TIMEOUTSTEADY value |
| HFXO0->XTALCFG = |
| (hfxoInit->timeoutCbLsb << _HFXO_XTALCFG_TIMEOUTCBLSB_SHIFT) |
| | (hfxoInit->timeoutSteadyFirstLock |
| << _HFXO_XTALCFG_TIMEOUTSTEADY_SHIFT) |
| | (hfxoInit->ctuneXoStartup << _HFXO_XTALCFG_CTUNEXOSTARTUP_SHIFT) |
| | (hfxoInit->ctuneXiStartup << _HFXO_XTALCFG_CTUNEXISTARTUP_SHIFT) |
| | (hfxoInit->coreBiasStartup << _HFXO_XTALCFG_COREBIASSTARTUP_SHIFT) |
| | (hfxoInit->imCoreBiasStartup << _HFXO_XTALCFG_COREBIASSTARTUPI_SHIFT); |
| |
| HFXO0->XTALCTRL = |
| (hfxoInit->coreDegenAna << _HFXO_XTALCTRL_COREDGENANA_SHIFT) |
| | (hfxoInit->ctuneFixAna << _HFXO_XTALCTRL_CTUNEFIXANA_SHIFT) |
| | (hfxoInit->ctuneXoAna << _HFXO_XTALCTRL_CTUNEXOANA_SHIFT) |
| | (hfxoInit->ctuneXiAna << _HFXO_XTALCTRL_CTUNEXIANA_SHIFT) |
| | (hfxoInit->coreBiasAna << _HFXO_XTALCTRL_COREBIASANA_SHIFT); |
| |
| HFXO0->CFG = (HFXO0->CFG & ~(_HFXO_CFG_SQBUFSCHTRGANA_MASK |
| | _HFXO_CFG_ENXIDCBIASANA_MASK |
| | _HFXO_CFG_MODE_MASK)) |
| | ((hfxoInit->mode == cmuHfxoOscMode_Crystal) |
| ? 0 : HFXO_CFG_SQBUFSCHTRGANA) |
| | (hfxoInit->enXiDcBiasAna << _HFXO_CFG_ENXIDCBIASANA_SHIFT) |
| | (hfxoInit->mode << _HFXO_CFG_MODE_SHIFT); |
| |
| if (hfxoInit->mode == cmuHfxoOscMode_Crystal) { |
| // Lock HFXO with FORCEEN bit set and DISONDEMAND bit cleared |
| HFXO0->CTRL = (HFXO0->CTRL & ~(_HFXO_CTRL_FORCEXO2GNDANA_MASK |
| | _HFXO_CTRL_FORCEXI2GNDANA_MASK |
| | _HFXO_CTRL_DISONDEMAND_MASK |
| | _HFXO_CTRL_FORCEEN_MASK)) |
| | (hfxoInit->forceXo2GndAna << _HFXO_CTRL_FORCEXO2GNDANA_SHIFT) |
| | (hfxoInit->forceXi2GndAna << _HFXO_CTRL_FORCEXI2GNDANA_SHIFT) |
| | HFXO_CTRL_FORCEEN; |
| |
| // Wait for HFXO lock and core bias algorithm to complete |
| while ((HFXO0->STATUS & (HFXO_STATUS_RDY | HFXO_STATUS_COREBIASOPTRDY |
| | HFXO_STATUS_ENS | HFXO_STATUS_FSMLOCK)) |
| != (HFXO_STATUS_RDY | HFXO_STATUS_COREBIASOPTRDY | HFXO_STATUS_ENS |
| | HFXO_STATUS_FSMLOCK)) { |
| } |
| |
| // We must set DISONDEMAND to be able enter new values for use on subsequent |
| // locks |
| HFXO0->CTRL_SET = HFXO_CTRL_DISONDEMAND; |
| while ((HFXO0->STATUS & HFXO_STATUS_FSMLOCK) != 0) { |
| } |
| |
| // Set new TIMEOUTSTEADY value for use on subsequent locks |
| HFXO0->XTALCFG = (HFXO0->XTALCFG & ~_HFXO_XTALCFG_TIMEOUTSTEADY_MASK) |
| | (hfxoInit->timeoutSteady |
| << _HFXO_XTALCFG_TIMEOUTSTEADY_SHIFT); |
| |
| // Skip core bias algorithm on subsequent locks |
| HFXO0->XTALCTRL_SET = HFXO_XTALCTRL_SKIPCOREBIASOPT; |
| |
| if (hfxoInit->disOnDemand == false) { |
| HFXO0->CTRL_CLR = HFXO_CTRL_DISONDEMAND; |
| } |
| |
| if (hfxoInit->forceEn == false) { |
| HFXO0->CTRL_CLR = HFXO_CTRL_FORCEEN; |
| } |
| } else { |
| // Lock HFXO in EXTERNAL SINE mode |
| HFXO0->CTRL = (HFXO0->CTRL & ~(_HFXO_CTRL_FORCEXO2GNDANA_MASK |
| | _HFXO_CTRL_FORCEXI2GNDANA_MASK |
| | _HFXO_CTRL_DISONDEMAND_MASK |
| | _HFXO_CTRL_FORCEEN_MASK)) |
| | (hfxoInit->forceXo2GndAna << _HFXO_CTRL_FORCEXO2GNDANA_SHIFT) |
| | (hfxoInit->forceXi2GndAna << _HFXO_CTRL_FORCEXI2GNDANA_SHIFT) |
| | (hfxoInit->disOnDemand << _HFXO_CTRL_DISONDEMAND_SHIFT) |
| | (hfxoInit->forceEn << _HFXO_CTRL_FORCEEN_SHIFT); |
| } |
| |
| if (hfxoInit->regLock) { |
| HFXO0->LOCK = ~HFXO_LOCK_LOCKKEY_UNLOCK; |
| } |
| } |
| |
| /**************************************************************************//** |
| * @brief |
| * Initialize LFXO control registers. |
| * |
| * @note |
| * LFXO configuration should be obtained from a configuration tool, |
| * app note or xtal datasheet. This function disables the LFXO to ensure |
| * a valid state before update. |
| * |
| * @param[in] lfxoInit |
| * LFXO setup parameters |
| *****************************************************************************/ |
| void CMU_LFXOInit(const CMU_LFXOInit_TypeDef *lfxoInit) |
| { |
| EFM_ASSERT(lfxoInit->timeout |
| <= (_LFXO_CFG_TIMEOUT_MASK >> _LFXO_CFG_TIMEOUT_SHIFT)); |
| EFM_ASSERT(lfxoInit->mode |
| <= (_LFXO_CFG_MODE_MASK >> _LFXO_CFG_MODE_SHIFT)); |
| EFM_ASSERT(lfxoInit->gain |
| <= (_LFXO_CAL_GAIN_MASK >> _LFXO_CAL_GAIN_SHIFT)); |
| EFM_ASSERT(lfxoInit->capTune |
| <= (_LFXO_CAL_CAPTUNE_MASK >> _LFXO_CAL_CAPTUNE_SHIFT)); |
| |
| // Unlock register interface |
| LFXO->LOCK = LFXO_LOCK_LOCKKEY_UNLOCK; |
| |
| // Disable LFXO |
| LFXO->CTRL_SET = LFXO_CTRL_DISONDEMAND; |
| LFXO->CTRL_CLR = LFXO_CTRL_FORCEEN; |
| while ((LFXO->STATUS & _LFXO_STATUS_ENS_MASK) != 0U) { |
| } |
| |
| // Configure LFXO as specified |
| LFXO->CAL = (lfxoInit->gain << _LFXO_CAL_GAIN_SHIFT) |
| | (lfxoInit->capTune << _LFXO_CAL_CAPTUNE_SHIFT); |
| |
| LFXO->CFG = (lfxoInit->timeout << _LFXO_CFG_TIMEOUT_SHIFT) |
| | (lfxoInit->mode << _LFXO_CFG_MODE_SHIFT) |
| | (lfxoInit->highAmplitudeEn << _LFXO_CFG_HIGHAMPL_SHIFT) |
| | (lfxoInit->agcEn << _LFXO_CFG_AGC_SHIFT); |
| |
| LFXO->CTRL = (lfxoInit->failDetEM4WUEn << _LFXO_CTRL_FAILDETEM4WUEN_SHIFT) |
| | (lfxoInit->failDetEn << _LFXO_CTRL_FAILDETEN_SHIFT) |
| | (lfxoInit->disOnDemand << _LFXO_CTRL_DISONDEMAND_SHIFT) |
| | (lfxoInit->forceEn << _LFXO_CTRL_FORCEEN_SHIFT); |
| |
| if (lfxoInit->regLock) { |
| LFXO->LOCK = ~LFXO_LOCK_LOCKKEY_UNLOCK; |
| } |
| } |
| |
| /***************************************************************************//** |
| * @brief |
| * Get oscillator frequency tuning setting. |
| * |
| * @param[in] osc |
| * Oscillator to get tuning value for. |
| * |
| * @return |
| * The oscillator frequency tuning setting in use. |
| ******************************************************************************/ |
| uint32_t CMU_OscillatorTuningGet(CMU_Osc_TypeDef osc) |
| { |
| uint32_t ret = 0U; |
| |
| switch (osc) { |
| case cmuOsc_LFRCO: |
| ret = (LFRCO->CAL & _LFRCO_CAL_FREQTRIM_MASK) |
| >> _LFRCO_CAL_FREQTRIM_SHIFT; |
| break; |
| |
| case cmuOsc_HFRCODPLL: |
| ret = (HFRCO0->CAL & _HFRCO_CAL_TUNING_MASK) >> _HFRCO_CAL_TUNING_SHIFT; |
| break; |
| |
| case cmuOsc_HFRCOEM23: |
| ret = (HFRCOEM23->CAL & _HFRCO_CAL_TUNING_MASK) |
| >> _HFRCO_CAL_TUNING_SHIFT; |
| break; |
| |
| default: |
| EFM_ASSERT(false); |
| break; |
| } |
| |
| return ret; |
| } |
| |
| /***************************************************************************//** |
| * @brief |
| * Set the oscillator frequency tuning control. |
| * |
| * @note |
| * Oscillator tuning is done during production, and the tuning value is |
| * automatically loaded after a reset. Changing the tuning value from the |
| * calibrated value is for more advanced use. Certain oscillators also have |
| * build-in tuning optimization. |
| * |
| * @param[in] osc |
| * Oscillator to set tuning value for. |
| * |
| * @param[in] val |
| * The oscillator frequency tuning setting to use. |
| ******************************************************************************/ |
| void CMU_OscillatorTuningSet(CMU_Osc_TypeDef osc, uint32_t val) |
| { |
| switch (osc) { |
| case cmuOsc_LFRCO: |
| EFM_ASSERT(val <= (_LFRCO_CAL_FREQTRIM_MASK |
| >> _LFRCO_CAL_FREQTRIM_SHIFT)); |
| val &= _LFRCO_CAL_FREQTRIM_MASK >> _LFRCO_CAL_FREQTRIM_SHIFT; |
| LFRCO->CAL = (LFRCO->CAL & ~_LFRCO_CAL_FREQTRIM_MASK) |
| | (val << _LFRCO_CAL_FREQTRIM_SHIFT); |
| break; |
| |
| case cmuOsc_HFRCODPLL: |
| EFM_ASSERT(val <= (_HFRCO_CAL_TUNING_MASK >> _HFRCO_CAL_TUNING_SHIFT)); |
| val &= _HFRCO_CAL_TUNING_MASK >> _HFRCO_CAL_TUNING_SHIFT; |
| while ((HFRCO0->STATUS & HFRCO_STATUS_SYNCBUSY) != 0UL) { |
| } |
| HFRCO0->CAL = (HFRCO0->CAL & ~_HFRCO_CAL_TUNING_MASK) |
| | (val << _HFRCO_CAL_TUNING_SHIFT); |
| break; |
| |
| case cmuOsc_HFRCOEM23: |
| EFM_ASSERT(val <= (_HFRCO_CAL_TUNING_MASK >> _HFRCO_CAL_TUNING_SHIFT)); |
| val &= _HFRCO_CAL_TUNING_MASK >> _HFRCO_CAL_TUNING_SHIFT; |
| while ((HFRCOEM23->STATUS & HFRCO_STATUS_SYNCBUSY) != 0UL) { |
| } |
| HFRCOEM23->CAL = (HFRCOEM23->CAL & ~_HFRCO_CAL_TUNING_MASK) |
| | (val << _HFRCO_CAL_TUNING_SHIFT); |
| break; |
| |
| default: |
| EFM_ASSERT(false); |
| break; |
| } |
| } |
| |
| /***************************************************************************//** |
| * @brief |
| * Configure wait state settings necessary to switch to a given core clock |
| * frequency. |
| * |
| * @details |
| * This function will setup the necessary flash 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. |
| * |
| * @param[in] freq |
| * Core clock frequency to configure wait-states for. |
| ******************************************************************************/ |
| void CMU_UpdateWaitStates(uint32_t freq, int vscale) |
| { |
| (void)vscale; |
| waitStateSet(freq); |
| } |
| |
| /** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */ |
| |
| /******************************************************************************* |
| ************************** LOCAL FUNCTIONS ******************************** |
| ******************************************************************************/ |
| |
| /***************************************************************************//** |
| * @brief |
| * Get calibrated HFRCODPLL tuning value from Device information (DI) page |
| * for a given frequency. Calibration value is not available for all frequency |
| * bands. |
| * |
| * @param[in] freq |
| * HFRCODPLL frequency band |
| ******************************************************************************/ |
| static uint32_t HFRCODPLLDevinfoGet(CMU_HFRCODPLLFreq_TypeDef freq) |
| { |
| uint32_t ret = 0U; |
| |
| switch (freq) { |
| // 1, 2 and 4MHz share the same calibration word |
| case cmuHFRCODPLLFreq_1M0Hz: |
| case cmuHFRCODPLLFreq_2M0Hz: |
| case cmuHFRCODPLLFreq_4M0Hz: |
| ret = DEVINFO->HFRCODPLLCAL[0].HFRCODPLLCAL; |
| break; |
| |
| case cmuHFRCODPLLFreq_7M0Hz: |
| ret = DEVINFO->HFRCODPLLCAL[3].HFRCODPLLCAL; |
| break; |
| |
| case cmuHFRCODPLLFreq_13M0Hz: |
| ret = DEVINFO->HFRCODPLLCAL[6].HFRCODPLLCAL; |
| break; |
| |
| case cmuHFRCODPLLFreq_16M0Hz: |
| ret = DEVINFO->HFRCODPLLCAL[7].HFRCODPLLCAL; |
| break; |
| |
| case cmuHFRCODPLLFreq_19M0Hz: |
| ret = DEVINFO->HFRCODPLLCAL[8].HFRCODPLLCAL; |
| break; |
| |
| case cmuHFRCODPLLFreq_26M0Hz: |
| ret = DEVINFO->HFRCODPLLCAL[10].HFRCODPLLCAL; |
| break; |
| |
| case cmuHFRCODPLLFreq_32M0Hz: |
| ret = DEVINFO->HFRCODPLLCAL[11].HFRCODPLLCAL; |
| break; |
| |
| case cmuHFRCODPLLFreq_38M0Hz: |
| ret = DEVINFO->HFRCODPLLCAL[12].HFRCODPLLCAL; |
| break; |
| |
| case cmuHFRCODPLLFreq_48M0Hz: |
| ret = DEVINFO->HFRCODPLLCAL[13].HFRCODPLLCAL; |
| break; |
| |
| case cmuHFRCODPLLFreq_56M0Hz: |
| ret = DEVINFO->HFRCODPLLCAL[14].HFRCODPLLCAL; |
| break; |
| |
| case cmuHFRCODPLLFreq_64M0Hz: |
| ret = DEVINFO->HFRCODPLLCAL[15].HFRCODPLLCAL; |
| break; |
| |
| case cmuHFRCODPLLFreq_80M0Hz: |
| ret = DEVINFO->HFRCODPLLCAL[16].HFRCODPLLCAL; |
| break; |
| |
| case cmuHFRCODPLLFreq_UserDefined: |
| break; |
| |
| default: |
| EFM_ASSERT(false); |
| break; |
| } |
| return ret; |
| } |
| |
| /***************************************************************************//** |
| * @brief |
| * Get calibrated HFRCOEM23 tuning value from Device information (DI) page |
| * for a given frequency. Calibration value is not available for all frequency |
| * bands. |
| * |
| * @param[in] freq |
| * HFRCOEM23 frequency band |
| ******************************************************************************/ |
| static uint32_t HFRCOEM23DevinfoGet(CMU_HFRCOEM23Freq_TypeDef freq) |
| { |
| uint32_t ret = 0U; |
| |
| switch (freq) { |
| // 1, 2 and 4MHz share the same calibration word |
| case cmuHFRCOEM23Freq_1M0Hz: |
| case cmuHFRCOEM23Freq_2M0Hz: |
| case cmuHFRCOEM23Freq_4M0Hz: |
| ret = DEVINFO->HFRCOEM23CAL[0].HFRCOEM23CAL; |
| break; |
| |
| case cmuHFRCOEM23Freq_13M0Hz: |
| ret = DEVINFO->HFRCOEM23CAL[6].HFRCOEM23CAL; |
| break; |
| |
| case cmuHFRCOEM23Freq_16M0Hz: |
| ret = DEVINFO->HFRCOEM23CAL[7].HFRCOEM23CAL; |
| break; |
| |
| case cmuHFRCOEM23Freq_19M0Hz: |
| ret = DEVINFO->HFRCOEM23CAL[8].HFRCOEM23CAL; |
| break; |
| |
| case cmuHFRCOEM23Freq_26M0Hz: |
| ret = DEVINFO->HFRCOEM23CAL[10].HFRCOEM23CAL; |
| break; |
| |
| case cmuHFRCOEM23Freq_32M0Hz: |
| ret = DEVINFO->HFRCOEM23CAL[11].HFRCOEM23CAL; |
| break; |
| |
| case cmuHFRCOEM23Freq_40M0Hz: |
| ret = DEVINFO->HFRCOEM23CAL[12].HFRCOEM23CAL; |
| break; |
| |
| case cmuHFRCOEM23Freq_UserDefined: |
| break; |
| |
| default: |
| EFM_ASSERT(false); |
| break; |
| } |
| return ret; |
| } |
| |
| /***************************************************************************//** |
| * @brief |
| * Get selected oscillator and frequency for @ref cmuClock_DPLLREFCLK |
| * clock tree. |
| * |
| * @param[out] freq |
| * The frequency. |
| * |
| * @param[out] sel |
| * The selected oscillator. |
| ******************************************************************************/ |
| static void dpllRefClkGet(uint32_t *freq, CMU_Select_TypeDef *sel) |
| { |
| uint32_t f = 0U; |
| CMU_Select_TypeDef s; |
| |
| switch (CMU->DPLLREFCLKCTRL & _CMU_DPLLREFCLKCTRL_CLKSEL_MASK) { |
| case _CMU_DPLLREFCLKCTRL_CLKSEL_HFXO: |
| f = SystemHFXOClockGet(); |
| s = cmuSelect_HFXO; |
| break; |
| |
| case _CMU_DPLLREFCLKCTRL_CLKSEL_LFXO: |
| f = SystemLFXOClockGet(); |
| s = cmuSelect_LFXO; |
| break; |
| |
| case _CMU_DPLLREFCLKCTRL_CLKSEL_CLKIN0: |
| f = SystemCLKIN0Get(); |
| s = cmuSelect_CLKIN0; |
| break; |
| |
| case _CMU_DPLLREFCLKCTRL_CLKSEL_DISABLED: |
| s = cmuSelect_Disabled; |
| break; |
| |
| default: |
| s = cmuSelect_Error; |
| EFM_ASSERT(false); |
| break; |
| } |
| |
| if (freq != NULL) { |
| *freq = f; |
| } |
| if (sel != NULL) { |
| *sel = s; |
| } |
| } |
| |
| /***************************************************************************//** |
| * @brief |
| * Get selected oscillator and frequency for @ref cmuClock_EM01GRPACLK |
| * clock tree. |
| * |
| * @param[out] freq |
| * The frequency. |
| * |
| * @param[out] sel |
| * The selected oscillator. |
| ******************************************************************************/ |
| static void em01GrpaClkGet(uint32_t *freq, CMU_Select_TypeDef *sel) |
| { |
| uint32_t f = 0U; |
| CMU_Select_TypeDef s; |
| |
| switch (CMU->EM01GRPACLKCTRL & _CMU_EM01GRPACLKCTRL_CLKSEL_MASK) { |
| case _CMU_EM01GRPACLKCTRL_CLKSEL_HFRCODPLL: |
| f = SystemHFRCODPLLClockGet(); |
| s = cmuSelect_HFRCODPLL; |
| break; |
| |
| case _CMU_EM01GRPACLKCTRL_CLKSEL_HFXO: |
| f = SystemHFXOClockGet(); |
| s = cmuSelect_HFXO; |
| break; |
| |
| case _CMU_EM01GRPACLKCTRL_CLKSEL_HFRCOEM23: |
| f = SystemHFRCOEM23ClockGet(); |
| s = cmuSelect_HFRCOEM23; |
| break; |
| |
| case _CMU_EM01GRPACLKCTRL_CLKSEL_FSRCO: |
| f = SystemFSRCOClockGet(); |
| s = cmuSelect_FSRCO; |
| break; |
| |
| default: |
| s = cmuSelect_Error; |
| EFM_ASSERT(false); |
| break; |
| } |
| |
| if (freq != NULL) { |
| *freq = f; |
| } |
| if (sel != NULL) { |
| *sel = s; |
| } |
| } |
| |
| /***************************************************************************//** |
| * @brief |
| * Get selected oscillator and frequency for @ref cmuClock_EM23GRPACLK |
| * clock tree. |
| * |
| * @param[out] freq |
| * The frequency. |
| * |
| * @param[out] sel |
| * The selected oscillator. |
| ******************************************************************************/ |
| static void em23GrpaClkGet(uint32_t *freq, CMU_Select_TypeDef *sel) |
| { |
| uint32_t f = 0U; |
| CMU_Select_TypeDef s; |
| |
| switch (CMU->EM23GRPACLKCTRL & _CMU_EM23GRPACLKCTRL_CLKSEL_MASK) { |
| case _CMU_EM23GRPACLKCTRL_CLKSEL_LFRCO: |
| f = SystemLFRCOClockGet(); |
| s = cmuSelect_LFRCO; |
| break; |
| |
| case _CMU_EM23GRPACLKCTRL_CLKSEL_LFXO: |
| f = SystemLFXOClockGet(); |
| s = cmuSelect_LFXO; |
| break; |
| |
| case _CMU_EM23GRPACLKCTRL_CLKSEL_ULFRCO: |
| f = SystemULFRCOClockGet(); |
| s = cmuSelect_ULFRCO; |
| break; |
| |
| default: |
| s = cmuSelect_Error; |
| EFM_ASSERT(false); |
| break; |
| } |
| |
| if (freq != NULL) { |
| *freq = f; |
| } |
| if (sel != NULL) { |
| *sel = s; |
| } |
| } |
| |
| /***************************************************************************//** |
| * @brief |
| * Get selected oscillator and frequency for @ref cmuClock_EM4GRPACLK |
| * clock tree. |
| * |
| * @param[out] freq |
| * The frequency. |
| * |
| * @param[out] sel |
| * The selected oscillator. |
| ******************************************************************************/ |
| static void em4GrpaClkGet(uint32_t *freq, CMU_Select_TypeDef *sel) |
| { |
| uint32_t f = 0U; |
| CMU_Select_TypeDef s; |
| |
| switch (CMU->EM4GRPACLKCTRL & _CMU_EM4GRPACLKCTRL_CLKSEL_MASK) { |
| case _CMU_EM4GRPACLKCTRL_CLKSEL_LFRCO: |
| f = SystemLFRCOClockGet(); |
| s = cmuSelect_LFRCO; |
| break; |
| |
| case _CMU_EM4GRPACLKCTRL_CLKSEL_LFXO: |
| f = SystemLFXOClockGet(); |
| s = cmuSelect_LFXO; |
| break; |
| |
| case _CMU_EM4GRPACLKCTRL_CLKSEL_ULFRCO: |
| f = SystemULFRCOClockGet(); |
| s = cmuSelect_ULFRCO; |
| break; |
| |
| default: |
| s = cmuSelect_Error; |
| EFM_ASSERT(false); |
| break; |
| } |
| |
| if (freq != NULL) { |
| *freq = f; |
| } |
| if (sel != NULL) { |
| *sel = s; |
| } |
| } |
| |
| /***************************************************************************//** |
| * @brief |
| * Get selected oscillator and frequency for @ref cmuClock_IADCCLK |
| * clock tree. |
| * |
| * @param[out] freq |
| * The frequency. |
| * |
| * @param[out] sel |
| * The selected oscillator. |
| ******************************************************************************/ |
| static void iadcClkGet(uint32_t *freq, CMU_Select_TypeDef *sel) |
| { |
| uint32_t f = 0U; |
| CMU_Select_TypeDef s; |
| |
| switch (CMU->IADCCLKCTRL & _CMU_IADCCLKCTRL_CLKSEL_MASK) { |
| case _CMU_IADCCLKCTRL_CLKSEL_EM01GRPACLK: |
| em01GrpaClkGet(&f, NULL); |
| s = cmuSelect_EM01GRPACLK; |
| break; |
| |
| case _CMU_IADCCLKCTRL_CLKSEL_HFRCOEM23: |
| f = SystemHFRCOEM23ClockGet(); |
| s = cmuSelect_HFRCOEM23; |
| break; |
| |
| case _CMU_IADCCLKCTRL_CLKSEL_FSRCO: |
| f = SystemFSRCOClockGet(); |
| s = cmuSelect_FSRCO; |
| break; |
| |
| default: |
| s = cmuSelect_Error; |
| EFM_ASSERT(false); |
| break; |
| } |
| |
| if (freq != NULL) { |
| *freq = f; |
| } |
| if (sel != NULL) { |
| *sel = s; |
| } |
| } |
| |
| /***************************************************************************//** |
| * @brief |
| * Set maximum allowed divisor for @ref cmuClock_PCLK clock tree. |
| ******************************************************************************/ |
| static void pclkDivMax(void) |
| { |
| // Set largest divisor for PCLK clock tree |
| CMU_ClockDivSet(cmuClock_PCLK, 2U); |
| } |
| |
| /***************************************************************************//** |
| * @brief |
| * Set @ref cmuClock_PCLK clock tree divisor to achieve highest possible |
| * frequency and still be within spec. |
| ******************************************************************************/ |
| static void pclkDivOptimize(void) |
| { |
| CMU_ClkDiv_TypeDef div = 2U; |
| |
| if (CMU_ClockFreqGet(cmuClock_HCLK) <= CMU_MAX_PCLK_FREQ) { |
| div = 1U; |
| } |
| CMU_ClockDivSet(cmuClock_PCLK, div); |
| } |
| |
| /***************************************************************************//** |
| * @brief |
| * Get selected oscillator and frequency for @ref cmuClock_RTCCCLK |
| * clock tree. |
| * |
| * @param[out] freq |
| * The frequency. |
| * |
| * @param[out] sel |
| * The selected oscillator. |
| ******************************************************************************/ |
| static void rtccClkGet(uint32_t *freq, CMU_Select_TypeDef *sel) |
| { |
| uint32_t f = 0U; |
| CMU_Select_TypeDef s; |
| |
| switch (CMU->RTCCCLKCTRL & _CMU_RTCCCLKCTRL_CLKSEL_MASK) { |
| case _CMU_RTCCCLKCTRL_CLKSEL_LFRCO: |
| f = SystemLFRCOClockGet(); |
| s = cmuSelect_LFRCO; |
| break; |
| |
| case _CMU_RTCCCLKCTRL_CLKSEL_LFXO: |
| f = SystemLFXOClockGet(); |
| s = cmuSelect_LFXO; |
| break; |
| |
| case _CMU_RTCCCLKCTRL_CLKSEL_ULFRCO: |
| f = SystemULFRCOClockGet(); |
| s = cmuSelect_ULFRCO; |
| break; |
| |
| default: |
| s = cmuSelect_Error; |
| EFM_ASSERT(false); |
| break; |
| } |
| |
| if (freq != NULL) { |
| *freq = f; |
| } |
| if (sel != NULL) { |
| *sel = s; |
| } |
| } |
| |
| /***************************************************************************//** |
| * @brief |
| * Get selected oscillator and frequency for @ref cmuClock_TRACECLK |
| * clock tree. |
| * |
| * @param[out] freq |
| * The frequency. |
| * |
| * @param[out] sel |
| * The selected oscillator. |
| ******************************************************************************/ |
| static void traceClkGet(uint32_t *freq, CMU_Select_TypeDef *sel) |
| { |
| uint32_t f = 0U; |
| CMU_Select_TypeDef s; |
| |
| switch (CMU->TRACECLKCTRL & _CMU_TRACECLKCTRL_CLKSEL_MASK) { |
| case _CMU_TRACECLKCTRL_CLKSEL_PCLK: |
| f = SystemHCLKGet() / CMU_ClockDivGet(cmuClock_PCLK); |
| s = cmuSelect_PCLK; |
| break; |
| |
| case _CMU_TRACECLKCTRL_CLKSEL_HCLK: |
| f = SystemHCLKGet(); |
| s = cmuSelect_HCLK; |
| break; |
| |
| case _CMU_TRACECLKCTRL_CLKSEL_HFRCOEM23: |
| f = SystemHFRCOEM23ClockGet(); |
| s = cmuSelect_HFRCOEM23; |
| break; |
| |
| default: |
| s = cmuSelect_Error; |
| EFM_ASSERT(false); |
| break; |
| } |
| |
| if (freq != NULL) { |
| *freq = f; |
| } |
| if (sel != NULL) { |
| *sel = s; |
| } |
| } |
| |
| /***************************************************************************//** |
| * @brief |
| * Set wait-states to values valid for maximum allowable core clock frequency. |
| ******************************************************************************/ |
| static void waitStateMax(void) |
| { |
| waitStateSet(SystemMaxCoreClockGet()); |
| } |
| |
| /***************************************************************************//** |
| * @brief |
| * Set wait-state settings valid for a given core clock frequency. |
| * |
| * @param[out] coreFreq |
| * Core clock frequency. |
| ******************************************************************************/ |
| static void waitStateSet(uint32_t coreFreq) |
| { |
| uint32_t mode; |
| bool mscLocked; |
| |
| // Make sure the MSC is unlocked |
| mscLocked = (MSC->STATUS & _MSC_STATUS_REGLOCK_MASK) |
| == MSC_STATUS_REGLOCK_LOCKED; |
| MSC->LOCK = MSC_LOCK_LOCKKEY_UNLOCK; |
| |
| // Get current flash read setting |
| mode = MSC->READCTRL & ~_MSC_READCTRL_MODE_MASK; |
| |
| // Set new mode based on the core clock frequency |
| if (coreFreq <= CMU_MAX_FLASHREAD_FREQ_0WS) { |
| mode |= MSC_READCTRL_MODE_WS0; |
| } else { |
| mode |= MSC_READCTRL_MODE_WS1; |
| } |
| MSC->READCTRL = mode; |
| |
| // Get current sram read setting |
| mode = SYSCFG->DMEM0RAMCTRL & ~_SYSCFG_DMEM0RAMCTRL_RAMWSEN_MASK; |
| |
| // Set new mode based on the core clock frequency |
| if (coreFreq > CMU_MAX_SRAM_FREQ_0WS) { |
| mode |= 1 << _SYSCFG_DMEM0RAMCTRL_RAMWSEN_SHIFT; |
| } |
| SYSCFG->DMEM0RAMCTRL = mode; |
| |
| if (mscLocked) { |
| MSC->LOCK = MSC_LOCK_LOCKKEY_LOCK; |
| } |
| } |
| |
| /***************************************************************************//** |
| * @brief |
| * Get selected oscillator and frequency for @ref cmuClock_WDOG0CLK |
| * clock tree. |
| * |
| * @param[out] freq |
| * The frequency. |
| * |
| * @param[out] sel |
| * The selected oscillator. |
| ******************************************************************************/ |
| static void wdog0ClkGet(uint32_t *freq, CMU_Select_TypeDef *sel) |
| { |
| uint32_t f = 0U; |
| CMU_Select_TypeDef s; |
| |
| switch (CMU->WDOG0CLKCTRL & _CMU_WDOG0CLKCTRL_CLKSEL_MASK) { |
| case _CMU_WDOG0CLKCTRL_CLKSEL_LFRCO: |
| f = SystemLFRCOClockGet(); |
| s = cmuSelect_LFRCO; |
| break; |
| |
| case _CMU_WDOG0CLKCTRL_CLKSEL_LFXO: |
| f = SystemLFXOClockGet(); |
| s = cmuSelect_LFXO; |
| break; |
| |
| case _CMU_WDOG0CLKCTRL_CLKSEL_ULFRCO: |
| f = SystemULFRCOClockGet(); |
| s = cmuSelect_ULFRCO; |
| break; |
| |
| case _CMU_WDOG0CLKCTRL_CLKSEL_HCLKDIV1024: |
| f = SystemHCLKGet() / 1024U; |
| s = cmuSelect_HCLKDIV1024; |
| break; |
| |
| default: |
| s = cmuSelect_Error; |
| EFM_ASSERT(false); |
| break; |
| } |
| |
| if (freq != NULL) { |
| *freq = f; |
| } |
| if (sel != NULL) { |
| *sel = s; |
| } |
| } |
| |
| /***************************************************************************//** |
| * @brief |
| * Get selected oscillator and frequency for @ref cmuClock_WDOG1CLK |
| * clock tree. |
| * |
| * @param[out] freq |
| * The frequency. |
| * |
| * @param[out] sel |
| * The selected oscillator. |
| ******************************************************************************/ |
| static void wdog1ClkGet(uint32_t *freq, CMU_Select_TypeDef *sel) |
| { |
| uint32_t f = 0U; |
| CMU_Select_TypeDef s; |
| |
| switch (CMU->WDOG1CLKCTRL & _CMU_WDOG1CLKCTRL_CLKSEL_MASK) { |
| case _CMU_WDOG1CLKCTRL_CLKSEL_LFRCO: |
| f = SystemLFRCOClockGet(); |
| s = cmuSelect_LFRCO; |
| break; |
| |
| case _CMU_WDOG1CLKCTRL_CLKSEL_LFXO: |
| f = SystemLFXOClockGet(); |
| s = cmuSelect_LFXO; |
| break; |
| |
| case _CMU_WDOG1CLKCTRL_CLKSEL_ULFRCO: |
| f = SystemULFRCOClockGet(); |
| s = cmuSelect_ULFRCO; |
| break; |
| |
| case _CMU_WDOG1CLKCTRL_CLKSEL_HCLKDIV1024: |
| f = SystemHCLKGet() / 1024U; |
| s = cmuSelect_HCLKDIV1024; |
| break; |
| |
| default: |
| s = cmuSelect_Error; |
| EFM_ASSERT(false); |
| break; |
| } |
| |
| if (freq != NULL) { |
| *freq = f; |
| } |
| if (sel != NULL) { |
| *sel = s; |
| } |
| } |
| |
| /** @endcond */ |
| |
| #else // defined(_SILICON_LABS_32B_SERIES_2) |
| |
| /******************************************************************************* |
| ****************************** DEFINES ************************************ |
| ******************************************************************************/ |
| |
| /** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */ |
| |
| #if defined(_SILICON_LABS_32B_SERIES_0) |
| /** The maximum allowed core frequency when using 0 wait-states on flash access. */ |
| #define CMU_MAX_FREQ_0WS 16000000 |
| /** The maximum allowed core frequency when using 1 wait-states on flash access */ |
| #define CMU_MAX_FREQ_1WS 32000000 |
| |
| #elif (_SILICON_LABS_GECKO_INTERNAL_SDID == 80) |
| // EFR32xG1x and EFM32xG1x |
| #define CMU_MAX_FREQ_0WS_1V2 25000000 |
| #define CMU_MAX_FREQ_1WS_1V2 40000000 |
| |
| #elif (_SILICON_LABS_GECKO_INTERNAL_SDID == 84) |
| // EFR32xG12x and EFM32xG12x |
| #define CMU_MAX_FREQ_0WS_1V2 25000000 |
| #define CMU_MAX_FREQ_1WS_1V2 40000000 |
| #define CMU_MAX_FREQ_0WS_1V1 21330000 |
| #define CMU_MAX_FREQ_1WS_1V1 32000000 |
| #define CMU_MAX_FREQ_0WS_1V0 7000000 |
| #define CMU_MAX_FREQ_1WS_1V0 14000000 |
| #define CMU_MAX_FREQ_2WS_1V0 21000000 |
| |
| #elif (_SILICON_LABS_GECKO_INTERNAL_SDID == 89) |
| // EFR32xG13x and EFM32xG13x |
| #define CMU_MAX_FREQ_0WS_1V2 25000000 |
| #define CMU_MAX_FREQ_1WS_1V2 40000000 |
| #define CMU_MAX_FREQ_0WS_1V0 7000000 |
| #define CMU_MAX_FREQ_1WS_1V0 14000000 |
| #define CMU_MAX_FREQ_2WS_1V0 21000000 |
| |
| #elif (_SILICON_LABS_GECKO_INTERNAL_SDID == 95) |
| // EFR32xG14x and EFM32xG14x |
| #define CMU_MAX_FREQ_0WS_1V2 25000000 |
| #define CMU_MAX_FREQ_1WS_1V2 40000000 |
| #define CMU_MAX_FREQ_0WS_1V0 7000000 |
| #define CMU_MAX_FREQ_1WS_1V0 14000000 |
| #define CMU_MAX_FREQ_2WS_1V0 21000000 |
| |
| #elif (_SILICON_LABS_GECKO_INTERNAL_SDID == 100) |
| // EFM32GG11x |
| #define CMU_MAX_FREQ_0WS_1V2 18000000 |
| #define CMU_MAX_FREQ_1WS_1V2 36000000 |
| #define CMU_MAX_FREQ_2WS_1V2 54000000 |
| #define CMU_MAX_FREQ_3WS_1V2 72000000 |
| #define CMU_MAX_FREQ_0WS_1V0 7000000 |
| #define CMU_MAX_FREQ_1WS_1V0 14000000 |
| #define CMU_MAX_FREQ_2WS_1V0 21000000 |
| |
| #elif (_SILICON_LABS_GECKO_INTERNAL_SDID == 103) |
| // EFM32TG11x |
| #define CMU_MAX_FREQ_0WS_1V2 25000000 |
| #define CMU_MAX_FREQ_1WS_1V2 48000000 |
| #define CMU_MAX_FREQ_0WS_1V0 10000000 |
| #define CMU_MAX_FREQ_1WS_1V0 21000000 |
| #define CMU_MAX_FREQ_2WS_1V0 21000000 |
| |
| #else |
| #error "Max Flash wait-state frequencies are not defined for this platform." |
| #endif |
| |
| /** The maximum frequency for the HFLE interface. */ |
| #if defined(CMU_CTRL_HFLE) |
| /** The maximum HFLE frequency for series 0 EFM32 and EZR32 Wonder Gecko. */ |
| #if defined(_SILICON_LABS_32B_SERIES_0) \ |
| && (defined(_EFM32_WONDER_FAMILY) \ |
| || defined(_EZR32_WONDER_FAMILY)) |
| #define CMU_MAX_FREQ_HFLE 24000000UL |
| /** The maximum HFLE frequency for other series 0 parts with maximum core clock |
| higher than 32 MHz. */ |
| #elif defined(_SILICON_LABS_32B_SERIES_0) \ |
| && (defined(_EFM32_GIANT_FAMILY) \ |
| || defined(_EZR32_LEOPARD_FAMILY)) |
| #define CMU_MAX_FREQ_HFLE maxFreqHfle() |
| #endif |
| #elif defined(CMU_CTRL_WSHFLE) |
| /** The maximum HFLE frequency for series 1 parts. */ |
| #define CMU_MAX_FREQ_HFLE 32000000UL |
| #endif |
| |
| #if defined(CMU_STATUS_HFXOSHUNTOPTRDY) |
| #define HFXO_TUNING_READY_FLAGS (CMU_STATUS_HFXOPEAKDETRDY | CMU_STATUS_HFXOSHUNTOPTRDY) |
| #define HFXO_TUNING_MODE_AUTO (_CMU_HFXOCTRL_PEAKDETSHUNTOPTMODE_AUTOCMD) |
| #define HFXO_TUNING_MODE_CMD (_CMU_HFXOCTRL_PEAKDETSHUNTOPTMODE_CMD) |
| #elif defined(CMU_STATUS_HFXOPEAKDETRDY) |
| #define HFXO_TUNING_READY_FLAGS (CMU_STATUS_HFXOPEAKDETRDY) |
| #define HFXO_TUNING_MODE_AUTO (_CMU_HFXOCTRL_PEAKDETMODE_AUTOCMD) |
| #define HFXO_TUNING_MODE_CMD (_CMU_HFXOCTRL_PEAKDETMODE_CMD) |
| #endif |
| |
| #if defined(CMU_HFXOCTRL_MODE_EXTCLK) |
| /** HFXO external clock mode is renamed from EXTCLK to DIGEXTCLK. */ |
| #define CMU_HFXOCTRL_MODE_DIGEXTCLK CMU_HFXOCTRL_MODE_EXTCLK |
| #endif |
| |
| #if defined(_EMU_CMD_EM01VSCALE0_MASK) |
| #define VSCALE_DEFAULT (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) |
| /** A table of HFRCOCTRL values and their associated minimum/maximum frequencies and |
| an optional band enumerator. */ |
| static const struct hfrcoCtrlTableElement{ |
| uint32_t minFreq; |
| uint32_t maxFreq; |
| uint32_t value; |
| CMU_HFRCOFreq_TypeDef band; |
| } hfrcoCtrlTable[] = |
| { |
| // minFreq maxFreq HFRCOCTRL value band |
| { 860000UL, 1050000UL, 0xBC601F00UL, cmuHFRCOFreq_1M0Hz }, |
| { 1050000UL, 1280000UL, 0xBC611F35UL, (CMU_HFRCOFreq_TypeDef)0 }, |
| { 1280000UL, 1480000UL, 0xBCA21F35UL, (CMU_HFRCOFreq_TypeDef)0 }, |
| { 1480000UL, 1800000UL, 0xAD231F35UL, (CMU_HFRCOFreq_TypeDef)0 }, |
| { 1800000UL, 2110000UL, 0xBA601F00UL, cmuHFRCOFreq_2M0Hz }, |
| { 2110000UL, 2560000UL, 0xBA611F35UL, (CMU_HFRCOFreq_TypeDef)0 }, |
| { 2560000UL, 2970000UL, 0xBAA21F35UL, (CMU_HFRCOFreq_TypeDef)0 }, |
| { 2970000UL, 3600000UL, 0xAB231F35UL, (CMU_HFRCOFreq_TypeDef)0 }, |
| { 3600000UL, 4220000UL, 0xB8601F00UL, cmuHFRCOFreq_4M0Hz }, |
| { 4220000UL, 5120000UL, 0xB8611F35UL, (CMU_HFRCOFreq_TypeDef)0 }, |
| { 5120000UL, 5930000UL, 0xB8A21F35UL, (CMU_HFRCOFreq_TypeDef)0 }, |
| { 5930000UL, 7520000UL, 0xA9231F00UL, cmuHFRCOFreq_7M0Hz }, |
| { 7520000UL, 9520000UL, 0x99241F35UL, (CMU_HFRCOFreq_TypeDef)0 }, |
| { 9520000UL, 11800000UL, 0x99251F35UL, (CMU_HFRCOFreq_TypeDef)0 }, |
| { 11800000UL, 14400000UL, 0x99261F00UL, cmuHFRCOFreq_13M0Hz }, |
| { 14400000UL, 17200000UL, 0x99271F00UL, cmuHFRCOFreq_16M0Hz }, |
| { 17200000UL, 19700000UL, 0x99481F00UL, cmuHFRCOFreq_19M0Hz }, |
| { 19700000UL, 23800000UL, 0x99491F35UL, (CMU_HFRCOFreq_TypeDef)0 }, |
| { 23800000UL, 28700000UL, 0x994A1F00UL, cmuHFRCOFreq_26M0Hz }, |
| { 28700000UL, 34800000UL, 0x996B1F00UL, cmuHFRCOFreq_32M0Hz }, |
| #if defined(_SILICON_LABS_GECKO_INTERNAL_SDID_84) \ |
| || defined(_SILICON_LABS_GECKO_INTERNAL_SDID_89) \ |
| || defined(_SILICON_LABS_GECKO_INTERNAL_SDID_95) |
| { 34800000UL, 40000000UL, 0x996C1F00UL, cmuHFRCOFreq_38M0Hz } |
| #elif defined(_SILICON_LABS_GECKO_INTERNAL_SDID_100) |
| { 34800000UL, 42800000UL, 0x996C1F00UL, cmuHFRCOFreq_38M0Hz }, |
| { 42800000UL, 51600000UL, 0x996D1F00UL, cmuHFRCOFreq_48M0Hz }, |
| { 51600000UL, 60500000UL, 0x998E1F00UL, cmuHFRCOFreq_56M0Hz }, |
| { 60500000UL, 72000000UL, 0xA98F1F00UL, cmuHFRCOFreq_64M0Hz } |
| #elif defined(_SILICON_LABS_GECKO_INTERNAL_SDID_103) |
| { 34800000UL, 42800000UL, 0x996C1F00UL, cmuHFRCOFreq_38M0Hz }, |
| { 42800000UL, 48000000UL, 0x996D1F00UL, cmuHFRCOFreq_48M0Hz } |
| #else |
| #error "HFRCOCTRL values not set for this platform." |
| #endif |
| }; |
| |
| #define HFRCOCTRLTABLE_ENTRIES (sizeof(hfrcoCtrlTable) \ |
| / sizeof(struct hfrcoCtrlTableElement)) |
| #endif // CMU_OSCENCMD_DPLLEN |
| |
| #if defined(_SILICON_LABS_32B_SERIES_1) && defined(_EMU_STATUS_VSCALE_MASK) |
| /* Devices with Voltage Scaling needs extra handling of wait states. */ |
| static const struct flashWsTableElement{ |
| uint32_t maxFreq; |
| uint8_t vscale; |
| uint8_t ws; |
| } flashWsTable[] = |
| { |
| #if (_SILICON_LABS_GECKO_INTERNAL_SDID == 100) |
| { CMU_MAX_FREQ_0WS_1V2, 0, 0 }, /* 0 wait states at max frequency 18 MHz and 1.2V */ |
| { CMU_MAX_FREQ_1WS_1V2, 0, 1 }, /* 1 wait states at max frequency 36 MHz and 1.2V */ |
| { CMU_MAX_FREQ_2WS_1V2, 0, 2 }, /* 2 wait states at max frequency 54 MHz and 1.2V */ |
| { CMU_MAX_FREQ_3WS_1V2, 0, 3 }, /* 3 wait states at max frequency 72 MHz and 1.2V */ |
| { CMU_MAX_FREQ_0WS_1V0, 2, 0 }, /* 0 wait states at max frequency 7 MHz and 1.0V */ |
| { CMU_MAX_FREQ_1WS_1V0, 2, 1 }, /* 1 wait states at max frequency 14 MHz and 1.0V */ |
| { CMU_MAX_FREQ_2WS_1V0, 2, 2 }, /* 2 wait states at max frequency 21 MHz and 1.0V */ |
| #else |
| { CMU_MAX_FREQ_0WS_1V2, 0, 0 }, /* 0 wait states at 1.2V */ |
| { CMU_MAX_FREQ_1WS_1V2, 0, 1 }, /* 1 wait states at 1.2V */ |
| { CMU_MAX_FREQ_0WS_1V0, 2, 0 }, /* 0 wait states at 1.0V */ |
| { CMU_MAX_FREQ_1WS_1V0, 2, 1 }, /* 1 wait states at 1.0V */ |
| { CMU_MAX_FREQ_2WS_1V0, 2, 2 }, /* 2 wait states at 1.0V */ |
| #endif |
| }; |
| |
| #define FLASH_WS_TABLE_ENTRIES (sizeof(flashWsTable) / sizeof(flashWsTable[0])) |
| #endif |
| |
| #if defined(_CMU_USHFRCOCTRL_FREQRANGE_MASK) \ |
| || defined(_CMU_USHFRCOTUNE_MASK) |
| #ifndef EFM32_USHFRCO_STARTUP_FREQ |
| #define EFM32_USHFRCO_STARTUP_FREQ (48000000UL) |
| #endif |
| |
| static uint32_t ushfrcoFreq = EFM32_USHFRCO_STARTUP_FREQ; |
| #endif |
| |
| /******************************************************************************* |
| ************************** LOCAL PROTOTYPES ******************************* |
| ******************************************************************************/ |
| #if defined(_CMU_HFRCOCTRL_FREQRANGE_MASK) |
| static uint32_t CMU_HFRCODevinfoGet(CMU_HFRCOFreq_TypeDef freq); |
| #endif |
| |
| #if defined(_CMU_USHFRCOCTRL_FREQRANGE_MASK) |
| static uint32_t CMU_USHFRCODevinfoGet(CMU_USHFRCOFreq_TypeDef freq); |
| #endif |
| |
| static void hfperClkSafePrescaler(void); |
| static void hfperClkOptimizedPrescaler(void); |
| |
| /** @endcond */ |
| |
| /******************************************************************************* |
| ************************** LOCAL FUNCTIONS ******************************** |
| ******************************************************************************/ |
| |
| /** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */ |
| |
| #if defined(_SILICON_LABS_32B_SERIES_0) \ |
| && (defined(_EFM32_GIANT_FAMILY) \ |
| || defined(_EZR32_LEOPARD_FAMILY)) |
| /***************************************************************************//** |
| * @brief |
| * Return maximum allowed frequency for low energy peripherals. |
| ******************************************************************************/ |
| static uint32_t maxFreqHfle(void) |
| { |
| uint16_t majorMinorRev; |
| |
| switch (SYSTEM_GetFamily()) { |
| case systemPartFamilyEfm32Leopard: |
| case systemPartFamilyEzr32Leopard: |
| /* CHIP MAJOR bit [5:0] */ |
| majorMinorRev = (((ROMTABLE->PID0 & _ROMTABLE_PID0_REVMAJOR_MASK) |
| >> _ROMTABLE_PID0_REVMAJOR_SHIFT) << 8); |
| /* CHIP MINOR bit [7:4] */ |
| majorMinorRev |= (((ROMTABLE->PID2 & _ROMTABLE_PID2_REVMINORMSB_MASK) |
| >> _ROMTABLE_PID2_REVMINORMSB_SHIFT) << 4); |
| /* CHIP MINOR bit [3:0] */ |
| majorMinorRev |= ((ROMTABLE->PID3 & _ROMTABLE_PID3_REVMINORLSB_MASK) |
| >> _ROMTABLE_PID3_REVMINORLSB_SHIFT); |
| |
| if (majorMinorRev >= 0x0204) { |
| return 24000000; |
| } else { |
| return 32000000; |
| } |
| |
| case systemPartFamilyEfm32Giant: |
| return 32000000; |
| |
| default: |
| /* Invalid device family. */ |
| EFM_ASSERT(false); |
| return 0; |
| } |
| } |
| #endif |
| |
| #if defined(CMU_MAX_FREQ_HFLE) |
| |
| /* Unified definitions for the HFLE wait-state and prescaler fields. */ |
| #if defined(CMU_CTRL_HFLE) |
| #define _GENERIC_HFLE_WS_MASK _CMU_CTRL_HFLE_MASK |
| #define _GENERIC_HFLE_WS_SHIFT _CMU_CTRL_HFLE_SHIFT |
| #define GENERIC_HFLE_PRESC_REG CMU->HFCORECLKDIV |
| #define _GENERIC_HFLE_PRESC_MASK _CMU_HFCORECLKDIV_HFCORECLKLEDIV_MASK |
| #define _GENERIC_HFLE_PRESC_SHIFT _CMU_HFCORECLKDIV_HFCORECLKLEDIV_SHIFT |
| #elif defined(CMU_CTRL_WSHFLE) |
| #define _GENERIC_HFLE_WS_MASK _CMU_CTRL_WSHFLE_MASK |
| #define _GENERIC_HFLE_WS_SHIFT _CMU_CTRL_WSHFLE_SHIFT |
| #define GENERIC_HFLE_PRESC_REG CMU->HFPRESC |
| #define _GENERIC_HFLE_PRESC_MASK _CMU_HFPRESC_HFCLKLEPRESC_MASK |
| #define _GENERIC_HFLE_PRESC_SHIFT _CMU_HFPRESC_HFCLKLEPRESC_SHIFT |
| #endif |
| |
| /***************************************************************************//** |
| * @brief |
| * Set HFLE wait-states and HFCLKLE prescaler. |
| * |
| * @param[in] maxLeFreq |
| * The maximum LE frequency. |
| ******************************************************************************/ |
| static void setHfLeConfig(uint32_t hfFreq) |
| { |
| unsigned int hfleWs; |
| uint32_t hflePresc; |
| |
| /* Check for 1 bit fields. @ref BUS_RegBitWrite() below are going to fail if the |
| fields are changed to more than 1 bit. */ |
| EFM_ASSERT((_GENERIC_HFLE_WS_MASK >> _GENERIC_HFLE_WS_SHIFT) == 0x1U); |
| |
| /* - Enable HFLE wait-state to allow access to LE peripherals when HFBUSCLK is |
| above maxLeFreq. |
| - Set HFLE prescaler. Allowed HFLE clock frequency is maxLeFreq. */ |
| |
| hfleWs = 1; |
| if (hfFreq <= CMU_MAX_FREQ_HFLE) { |
| hfleWs = 0; |
| hflePresc = 0; |
| } else if (hfFreq <= (2UL * CMU_MAX_FREQ_HFLE)) { |
| hflePresc = 1; |
| } else { |
| hflePresc = 2; |
| } |
| BUS_RegBitWrite(&CMU->CTRL, _GENERIC_HFLE_WS_SHIFT, hfleWs); |
| GENERIC_HFLE_PRESC_REG = (GENERIC_HFLE_PRESC_REG & ~_GENERIC_HFLE_PRESC_MASK) |
| | (hflePresc << _GENERIC_HFLE_PRESC_SHIFT); |
| } |
| |
| #if defined(_CMU_CTRL_HFLE_MASK) |
| /***************************************************************************//** |
| * @brief |
| * Get HFLE wait-state configuration. |
| * |
| * @return |
| * The current wait-state configuration. |
| ******************************************************************************/ |
| static uint32_t getHfLeConfig(void) |
| { |
| uint32_t ws = BUS_RegBitRead(&CMU->CTRL, _GENERIC_HFLE_WS_SHIFT); |
| return ws; |
| } |
| #endif |
| #endif |
| |
| /***************************************************************************//** |
| * @brief |
| * Get the AUX clock frequency. Used by MSC flash programming and LESENSE, |
| * by default also as a debug clock. |
| * |
| * @return |
| * AUX Frequency in Hz. |
| ******************************************************************************/ |
| static uint32_t auxClkGet(void) |
| { |
| uint32_t ret; |
| |
| #if defined(_CMU_AUXHFRCOCTRL_FREQRANGE_MASK) |
| ret = (uint32_t)auxHfrcoFreq; |
| |
| #elif defined(_CMU_AUXHFRCOCTRL_BAND_MASK) |
| /* All series 0 families except EFM32G */ |
| switch (CMU->AUXHFRCOCTRL & _CMU_AUXHFRCOCTRL_BAND_MASK) { |
| case CMU_AUXHFRCOCTRL_BAND_1MHZ: |
| if ( SYSTEM_GetProdRev() >= 19 ) { |
| ret = 1200000; |
| } else { |
| ret = 1000000; |
| } |
| break; |
| |
| case CMU_AUXHFRCOCTRL_BAND_7MHZ: |
| if ( SYSTEM_GetProdRev() >= 19 ) { |
| ret = 6600000; |
| } else { |
| ret = 7000000; |
| } |
| break; |
| |
| case CMU_AUXHFRCOCTRL_BAND_11MHZ: |
| ret = 11000000; |
| break; |
| |
| case CMU_AUXHFRCOCTRL_BAND_14MHZ: |
| ret = 14000000; |
| break; |
| |
| case CMU_AUXHFRCOCTRL_BAND_21MHZ: |
| ret = 21000000; |
| break; |
| |
| #if defined(_CMU_AUXHFRCOCTRL_BAND_28MHZ) |
| case CMU_AUXHFRCOCTRL_BAND_28MHZ: |
| ret = 28000000; |
| break; |
| #endif |
| |
| default: |
| ret = 0; |
| EFM_ASSERT(false); |
| break; |
| } |
| |
| #else |
| /* Gecko has a fixed 14 MHz AUXHFRCO clock. */ |
| ret = 14000000; |
| |
| #endif |
| |
| return ret; |
| } |
| |
| #if defined (_CMU_ADCCTRL_ADC0CLKSEL_HFSRCCLK) \ |
| || defined (_CMU_ADCCTRL_ADC1CLKSEL_HFSRCCLK) |
| /***************************************************************************//** |
| * @brief |
| * Get the HFSRCCLK frequency. |
| * |
| * @return |
| * HFSRCCLK Frequency in Hz. |
| ******************************************************************************/ |
| static uint32_t hfSrcClkGet(void) |
| { |
| uint32_t ret; |
| |
| ret = SystemHFClockGet(); |
| return ret * (1U + ((CMU->HFPRESC & _CMU_HFPRESC_PRESC_MASK) |
| >> _CMU_HFPRESC_PRESC_SHIFT)); |
| } |
| #endif |
| |
| /***************************************************************************//** |
| * @brief |
| * Get the Debug Trace clock frequency. |
| * |
| * @return |
| * Debug Trace frequency in Hz. |
| ******************************************************************************/ |
| static uint32_t dbgClkGet(void) |
| { |
| uint32_t ret; |
| CMU_Select_TypeDef clk; |
| |
| /* Get selected clock source */ |
| clk = CMU_ClockSelectGet(cmuClock_DBG); |
| |
| switch (clk) { |
| case cmuSelect_HFCLK: |
| ret = SystemHFClockGet(); |
| break; |
| |
| case cmuSelect_AUXHFRCO: |
| ret = auxClkGet(); |
| break; |
| |
| default: |
| ret = 0; |
| EFM_ASSERT(false); |
| break; |
| } |
| return ret; |
| } |
| |
| #if defined(_CMU_ADCCTRL_MASK) |
| /***************************************************************************//** |
| * @brief |
| * Get the ADC n asynchronous clock frequency. |
| * |
| * @return |
| * ADC n asynchronous frequency in Hz. |
| ******************************************************************************/ |
| static uint32_t adcAsyncClkGet(uint32_t adc) |
| { |
| uint32_t ret; |
| CMU_Select_TypeDef clk; |
| |
| /* Get the selected clock source. */ |
| switch (adc) { |
| case 0: |
| clk = CMU_ClockSelectGet(cmuClock_ADC0ASYNC); |
| break; |
| |
| #if defined(_CMU_ADCCTRL_ADC1CLKSEL_MASK) |
| case 1: |
| clk = CMU_ClockSelectGet(cmuClock_ADC1ASYNC); |
| break; |
| #endif |
| |
| default: |
| EFM_ASSERT(false); |
| return 0; |
| } |
| |
| switch (clk) { |
| case cmuSelect_Disabled: |
| ret = 0; |
| break; |
| |
| case cmuSelect_AUXHFRCO: |
| ret = auxClkGet(); |
| break; |
| |
| case cmuSelect_HFXO: |
| ret = SystemHFXOClockGet(); |
| break; |
| |
| case cmuSelect_HFSRCCLK: |
| ret = hfSrcClkGet(); |
| break; |
| |
| default: |
| ret = 0; |
| EFM_ASSERT(false); |
| break; |
| } |
| return ret; |
| } |
| #endif |
| |
| #if defined(_CMU_SDIOCTRL_MASK) |
| /***************************************************************************//** |
| * @brief |
| * Get the SDIO reference clock frequency. |
| * |
| * @return |
| * SDIO reference clock frequency in Hz. |
| ******************************************************************************/ |
| static uint32_t sdioRefClkGet(void) |
| { |
| uint32_t ret; |
| CMU_Select_TypeDef clk; |
| |
| /* Get the selected clock source. */ |
| clk = CMU_ClockSelectGet(cmuClock_SDIOREF); |
| |
| switch (clk) { |
| case cmuSelect_HFRCO: |
| ret = SystemHfrcoFreq; |
| break; |
| |
| case cmuSelect_HFXO: |
| ret = SystemHFXOClockGet(); |
| break; |
| |
| case cmuSelect_AUXHFRCO: |
| ret = auxClkGet(); |
| break; |
| |
| case cmuSelect_USHFRCO: |
| ret = ushfrcoFreq; |
| break; |
| |
| default: |
| ret = 0; |
| EFM_ASSERT(false); |
| break; |
| } |
| return ret; |
| } |
| #endif |
| |
| #if defined(_CMU_QSPICTRL_MASK) |
| /***************************************************************************//** |
| * @brief |
| * Get the QSPI n reference clock frequency. |
| * |
| * @return |
| * QSPI n reference clock frequency in Hz. |
| ******************************************************************************/ |
| static uint32_t qspiRefClkGet(uint32_t qspi) |
| { |
| uint32_t ret; |
| CMU_Select_TypeDef clk; |
| |
| /* Get the selected clock source. */ |
| switch (qspi) { |
| case 0: |
| clk = CMU_ClockSelectGet(cmuClock_QSPI0REF); |
| break; |
| |
| default: |
| EFM_ASSERT(false); |
| return 0; |
| } |
| |
| switch (clk) { |
| case cmuSelect_HFRCO: |
| ret = SystemHfrcoFreq; |
| break; |
| |
| case cmuSelect_HFXO: |
| ret = SystemHFXOClockGet(); |
| break; |
| |
| case cmuSelect_AUXHFRCO: |
| ret = auxClkGet(); |
| break; |
| |
| case cmuSelect_USHFRCO: |
| ret = ushfrcoFreq; |
| break; |
| |
| default: |
| ret = 0; |
| EFM_ASSERT(false); |
| break; |
| } |
| return ret; |
| } |
| #endif |
| |
| #if defined(USBR_CLOCK_PRESENT) |
| /***************************************************************************//** |
| * @brief |
| * Get the USB rate clock frequency. |
| * |
| * @return |
| * USB rate clock frequency in Hz. |
| ******************************************************************************/ |
| static uint32_t usbRateClkGet(void) |
| { |
| uint32_t ret; |
| CMU_Select_TypeDef clk; |
| |
| clk = CMU_ClockSelectGet(cmuClock_USBR); |
| |
| switch (clk) { |
| case cmuSelect_USHFRCO: |
| ret = ushfrcoFreq; |
| break; |
| |
| case cmuSelect_HFXO: |
| ret = SystemHFXOClockGet(); |
| break; |
| |
| case cmuSelect_HFXOX2: |
| ret = 2u * SystemHFXOClockGet(); |
| break; |
| |
| case cmuSelect_HFRCO: |
| ret = SystemHfrcoFreq; |
| break; |
| |
| case cmuSelect_LFXO: |
| ret = SystemLFXOClockGet(); |
| break; |
| |
| case cmuSelect_LFRCO: |
| ret = SystemLFRCOClockGet(); |
| break; |
| |
| default: |
| ret = 0; |
| EFM_ASSERT(false); |
| break; |
| } |
| return ret; |
| } |
| #endif |
| |
| /***************************************************************************//** |
| * @brief |
| * Configure flash access wait states to support the given core clock |
| * frequency. |
| * |
| * @param[in] coreFreq |
| * The core clock frequency to configure flash wait-states. |
| * |
| * @param[in] vscale |
| * Voltage Scale level. Supported levels are 0 and 2 where 0 is the default. |
| ******************************************************************************/ |
| static void flashWaitStateControl(uint32_t coreFreq, int vscale) |
| { |
| uint32_t mode; |
| #if defined(MSC_READCTRL_MODE_WS0SCBTP) |
| bool scbtpEn; /* Suppressed Conditional Branch Target Prefetch setting. */ |
| #endif |
| (void) vscale; /* vscale parameter is only used on some devices. */ |
| |
| /* Get mode and SCBTP enable. */ |
| mode = MSC->READCTRL & _MSC_READCTRL_MODE_MASK; |
| |
| #if defined(_SILICON_LABS_32B_SERIES_0) |
| #if defined(MSC_READCTRL_MODE_WS0SCBTP) |
| /* Devices with MODE and SCBTP in the same register field. */ |
| switch (mode) { |
| case MSC_READCTRL_MODE_WS0: |
| case MSC_READCTRL_MODE_WS1: |
| #if defined(MSC_READCTRL_MODE_WS2) |
| case MSC_READCTRL_MODE_WS2: |
| #endif |
| scbtpEn = false; |
| break; |
| |
| default: /* WSxSCBTP */ |
| scbtpEn = true; |
| break; |
| } |
| |
| /* Set mode based on the core clock frequency and SCBTP enable. */ |
| if (false) { |
| } |
| #if defined(MSC_READCTRL_MODE_WS2) |
| else if (coreFreq > CMU_MAX_FREQ_1WS) { |
| mode = (scbtpEn ? MSC_READCTRL_MODE_WS2SCBTP : MSC_READCTRL_MODE_WS2); |
| } |
| #endif |
| else if ((coreFreq <= CMU_MAX_FREQ_1WS) && (coreFreq > CMU_MAX_FREQ_0WS)) { |
| mode = (scbtpEn ? MSC_READCTRL_MODE_WS1SCBTP : MSC_READCTRL_MODE_WS1); |
| } else { |
| mode = (scbtpEn ? MSC_READCTRL_MODE_WS0SCBTP : MSC_READCTRL_MODE_WS0); |
| } |
| #else /* defined(MSC_READCTRL_MODE_WS0SCBTP) */ |
| |
| if (coreFreq <= CMU_MAX_FREQ_0WS) { |
| mode = 0; |
| } else if (coreFreq <= CMU_MAX_FREQ_1WS) { |
| mode = 1; |
| } |
| #endif /* defined(MSC_READCTRL_MODE_WS0SCBTP) */ |
| // End defined(_SILICON_LABS_32B_SERIES_0) |
| |
| #elif defined(_SILICON_LABS_32B_SERIES_1) |
| #if defined(_EMU_STATUS_VSCALE_MASK) |
| |
| /* These devices have specific requirements on the supported flash wait state |
| * depending on the frequency and voltage scale level. */ |
| uint32_t i; |
| for (i = 0; i < FLASH_WS_TABLE_ENTRIES; i++) { |
| if ((flashWsTable[i].vscale == (uint8_t)vscale) |
| && (coreFreq <= flashWsTable[i].maxFreq)) { |
| break; // Found a matching entry. |
| } |
| } |
| |
| if (i == FLASH_WS_TABLE_ENTRIES) { |
| mode = 3; // Worst case flash wait state for unsupported cases. |
| EFM_ASSERT(false); |
| } else { |
| mode = flashWsTable[i].ws; |
| } |
| mode = mode << _MSC_READCTRL_MODE_SHIFT; |
| |
| #else |
| /* Devices where MODE and SCBTP are in separate fields and where the device |
| * either does not support voltage scale or where the voltage scale does |
| * not impact the flash wait state configuration. */ |
| if (coreFreq <= CMU_MAX_FREQ_0WS_1V2) { |
| mode = 0; |
| } else if (coreFreq <= CMU_MAX_FREQ_1WS_1V2) { |
| mode = 1; |
| } |
| #if defined(MSC_READCTRL_MODE_WS2) |
| else if (coreFreq <= CMU_MAX_FREQ_2WS) { |
| mode = 2; |
| } |
| #endif |
| #if defined(MSC_READCTRL_MODE_WS3) |
| else if (coreFreq <= CMU_MAX_FREQ_3WS) { |
| mode = 3; |
| } |
| #endif |
| mode = mode << _MSC_READCTRL_MODE_SHIFT; |
| #endif |
| // End defined(_SILICON_LABS_32B_SERIES_1) |
| |
| #else |
| #error "Undefined 32B SERIES!" |
| #endif |
| |
| /* BUS_RegMaskedWrite cannot be used as it would temporarily set the |
| mode field to WS0. */ |
| MSC->READCTRL = (MSC->READCTRL & ~_MSC_READCTRL_MODE_MASK) | mode; |
| } |
| |
| /***************************************************************************//** |
| * @brief |
| * Configure flash access wait states to the most conservative setting for |
| * this target. Retain SCBTP (Suppressed Conditional Branch Target Prefetch) |
| * setting. |
| ******************************************************************************/ |
| static void flashWaitStateMax(void) |
| { |
| /* Make sure the MSC is unlocked */ |
| bool mscLocked = MSC->LOCK != 0UL; |
| MSC->LOCK = MSC_UNLOCK_CODE; |
| |
| flashWaitStateControl(SystemMaxCoreClockGet(), 0); |
| |
| if (mscLocked) { |
| MSC->LOCK = 0; |
| } |
| } |
| |
| #if defined(_MSC_RAMCTRL_RAMWSEN_MASK) |
| /***************************************************************************//** |
| * @brief |
| * Configure RAM access wait states to support the given core clock |
| * frequency. |
| * |
| * @param[in] coreFreq |
| * The core clock frequency to configure RAM wait-states. |
| * |
| * @param[in] vscale |
| * A voltage scale level. Supported levels are 0 and 2 where 0 is the default. |
| ******************************************************************************/ |
| static void setRamWaitState(uint32_t coreFreq, int vscale) |
| { |
| uint32_t limit = 38000000; |
| if (vscale == 2) { |
| limit = 16000000; |
| } |
| |
| if (coreFreq > limit) { |
| BUS_RegMaskedSet(&MSC->RAMCTRL, (MSC_RAMCTRL_RAMWSEN |
| | MSC_RAMCTRL_RAM1WSEN |
| | MSC_RAMCTRL_RAM2WSEN)); |
| } else { |
| BUS_RegMaskedClear(&MSC->RAMCTRL, (MSC_RAMCTRL_RAMWSEN |
| | MSC_RAMCTRL_RAM1WSEN |
| | MSC_RAMCTRL_RAM2WSEN)); |
| } |
| } |
| #endif |
| |
| #if defined(_MSC_CTRL_WAITMODE_MASK) |
| /***************************************************************************//** |
| * @brief |
| * Configure the wait state for peripheral accesses over the bus to support |
| * the given bus clock frequency. |
| * |
| * @param[in] busFreq |
| * A peripheral bus clock frequency to configure wait-states. |
| * |
| * @param[in] vscale |
| * The voltage scale to configure wait-states. Expected values are |
| * 0 or 2. |
| * |
| * @li 0 = 1.2 V (VSCALE2) |
| * @li 2 = 1.0 V (VSCALE0) |
| * ******************************************************************************/ |
| static void setBusWaitState(uint32_t busFreq, int vscale) |
| { |
| if ((busFreq > 50000000) && (vscale == 0)) { |
| BUS_RegMaskedSet(&MSC->CTRL, MSC_CTRL_WAITMODE_WS1); |
| } else { |
| BUS_RegMaskedClear(&MSC->CTRL, MSC_CTRL_WAITMODE_WS1); |
| } |
| } |
| #endif |
| |
| /***************************************************************************//** |
| * @brief |
| * Configure various wait states to switch to a certain frequency |
| * and a certain voltage scale. |
| * |
| * @details |
| * This function will set up the necessary flash, bus, and RAM wait states. |
| * Updating the wait state configuration must be done before |
| * increasing the clock frequency and it must be done after decreasing the |
| * clock frequency. Updating the wait state configuration must be done before |
| * core voltage is decreased and it must be done after a core voltage is |
| * increased. |
| * |
| * @param[in] coreFreq |
| * The core clock frequency to configure wait-states. |
| * |
| * @param[in] vscale |
| * The voltage scale to configure wait-states. Expected values are |
| * 0 or 2, higher number is lower voltage. |
| * |
| * @li 0 = 1.2 V (VSCALE2) |
| * @li 2 = 1.0 V (VSCALE0) |
| * |
| ******************************************************************************/ |
| void CMU_UpdateWaitStates(uint32_t freq, int vscale) |
| { |
| /* Make sure the MSC is unlocked */ |
| bool mscLocked = MSC->LOCK != 0UL; |
| MSC->LOCK = MSC_UNLOCK_CODE; |
| |
| flashWaitStateControl(freq, vscale); |
| #if defined(_MSC_RAMCTRL_RAMWSEN_MASK) |
| setRamWaitState(freq, vscale); |
| #endif |
| #if defined(_MSC_CTRL_WAITMODE_MASK) |
| setBusWaitState(freq, vscale); |
| #endif |
| |
| if (mscLocked) { |
| MSC->LOCK = 0; |
| } |
| } |
| |
| #if defined(_CMU_HFXOSTEADYSTATECTRL_REGISHUPPER_MASK) |
| /***************************************************************************//** |
| * @brief |
| * Return the upper value for CMU_HFXOSTEADYSTATECTRL_REGISH. |
| ******************************************************************************/ |
| static uint32_t getRegIshUpperVal(uint32_t steadyStateRegIsh) |
| { |
| uint32_t regIshUpper; |
| const uint32_t upperMax = _CMU_HFXOSTEADYSTATECTRL_REGISHUPPER_MASK |
| >> _CMU_HFXOSTEADYSTATECTRL_REGISHUPPER_SHIFT; |
| /* Add 3 as specified in the register description for CMU_HFXOSTEADYSTATECTRL_REGISHUPPER. */ |
| regIshUpper = SL_MIN(steadyStateRegIsh + 3UL, upperMax); |
| regIshUpper <<= _CMU_HFXOSTEADYSTATECTRL_REGISHUPPER_SHIFT; |
| return regIshUpper; |
| } |
| #endif |
| |
| #if defined(_CMU_HFXOCTRL_MASK) |
| /***************************************************************************//** |
| * @brief |
| * Get the HFXO tuning mode. |
| * |
| * @return |
| * The current HFXO tuning mode from the HFXOCTRL register. |
| ******************************************************************************/ |
| __STATIC_INLINE uint32_t getHfxoTuningMode(void) |
| { |
| #if defined(_CMU_HFXOCTRL_PEAKDETSHUNTOPTMODE_MASK) |
| return (CMU->HFXOCTRL & _CMU_HFXOCTRL_PEAKDETSHUNTOPTMODE_MASK) |
| >> _CMU_HFXOCTRL_PEAKDETSHUNTOPTMODE_SHIFT; |
| #else |
| return (CMU->HFXOCTRL & _CMU_HFXOCTRL_PEAKDETMODE_MASK) |
| >> _CMU_HFXOCTRL_PEAKDETMODE_SHIFT; |
| #endif |
| } |
| |
| /***************************************************************************//** |
| * @brief |
| * Set the HFXO tuning mode. |
| * |
| * @param[in] mode |
| * The new HFXO tuning mode. This can be HFXO_TUNING_MODE_AUTO or |
| * HFXO_TUNING_MODE_CMD. |
| ******************************************************************************/ |
| __STATIC_INLINE void setHfxoTuningMode(uint32_t mode) |
| { |
| #if defined(_CMU_HFXOCTRL_PEAKDETSHUNTOPTMODE_MASK) |
| CMU->HFXOCTRL = (CMU->HFXOCTRL & ~_CMU_HFXOCTRL_PEAKDETSHUNTOPTMODE_MASK) |
| | (mode << _CMU_HFXOCTRL_PEAKDETSHUNTOPTMODE_SHIFT); |
| #else |
| CMU->HFXOCTRL = (CMU->HFXOCTRL & ~_CMU_HFXOCTRL_PEAKDETMODE_MASK) |
| | (mode << _CMU_HFXOCTRL_PEAKDETMODE_SHIFT); |
| #endif |
| } |
| #endif |
| |
| /***************************************************************************//** |
| * @brief |
| * Get the LFnCLK frequency based on the current configuration. |
| * |
| * @param[in] lfClkBranch |
| * Selected LF branch. |
| * |
| * @return |
| * The LFnCLK frequency in Hz. If no LFnCLK is selected (disabled), 0 is |
| * returned. |
| ******************************************************************************/ |
| static uint32_t lfClkGet(CMU_Clock_TypeDef lfClkBranch) |
| { |
| uint32_t sel; |
| uint32_t ret = 0; |
| |
| switch (lfClkBranch) { |
| case cmuClock_LFA: |
| case cmuClock_LFB: |
| #if defined(_CMU_LFCCLKEN0_MASK) |
| case cmuClock_LFC: |
| #endif |
| #if defined(_CMU_LFECLKSEL_MASK) |
| case cmuClock_LFE: |
| #endif |
| break; |
| |
| default: |
| EFM_ASSERT(false); |
| break; |
| } |
| |
| sel = (uint32_t)CMU_ClockSelectGet(lfClkBranch); |
| |
| /* Get clock select field */ |
| switch (lfClkBranch) { |
| case cmuClock_LFA: |
| #if defined(_CMU_LFCLKSEL_MASK) |
| sel = (CMU->LFCLKSEL & _CMU_LFCLKSEL_LFA_MASK) >> _CMU_LFCLKSEL_LFA_SHIFT; |
| #elif defined(_CMU_LFACLKSEL_MASK) |
| sel = (CMU->LFACLKSEL & _CMU_LFACLKSEL_LFA_MASK) >> _CMU_LFACLKSEL_LFA_SHIFT; |
| #else |
| EFM_ASSERT(false); |
| #endif |
| break; |
| |
| case cmuClock_LFB: |
| #if defined(_CMU_LFCLKSEL_MASK) |
| sel = (CMU->LFCLKSEL & _CMU_LFCLKSEL_LFB_MASK) >> _CMU_LFCLKSEL_LFB_SHIFT; |
| #elif defined(_CMU_LFBCLKSEL_MASK) |
| sel = (CMU->LFBCLKSEL & _CMU_LFBCLKSEL_LFB_MASK) >> _CMU_LFBCLKSEL_LFB_SHIFT; |
| #else |
| EFM_ASSERT(false); |
| #endif |
| break; |
| |
| #if defined(_CMU_LFCCLKEN0_MASK) |
| case cmuClock_LFC: |
| #if defined(_CMU_LFCLKSEL_LFC_MASK) |
| sel = (CMU->LFCLKSEL & _CMU_LFCLKSEL_LFC_MASK) >> _CMU_LFCLKSEL_LFC_SHIFT; |
| #elif defined(_CMU_LFCCLKSEL_LFC_MASK) |
| sel = (CMU->LFCCLKSEL & _CMU_LFCCLKSEL_LFC_MASK) >> _CMU_LFCCLKSEL_LFC_SHIFT; |
| #else |
| EFM_ASSERT(false); |
| #endif |
| break; |
| #endif |
| |
| #if defined(_CMU_LFECLKSEL_MASK) |
| case cmuClock_LFE: |
| sel = (CMU->LFECLKSEL & _CMU_LFECLKSEL_LFE_MASK) >> _CMU_LFECLKSEL_LFE_SHIFT; |
| break; |
| #endif |
| |
| default: |
| EFM_ASSERT(false); |
| break; |
| } |
| |
| /* Get the clock frequency. */ |
| #if defined(_CMU_LFCLKSEL_MASK) |
| switch (sel) { |
| case _CMU_LFCLKSEL_LFA_LFRCO: |
| ret = SystemLFRCOClockGet(); |
| break; |
| |
| case _CMU_LFCLKSEL_LFA_LFXO: |
| ret = SystemLFXOClockGet(); |
| break; |
| |
| #if defined(_CMU_LFCLKSEL_LFA_HFCORECLKLEDIV2) |
| case _CMU_LFCLKSEL_LFA_HFCORECLKLEDIV2: |
| #if defined(CMU_MAX_FREQ_HFLE) |
| /* HFLE bit is or'ed by hardware with HFCORECLKLEDIV to reduce the |
| * frequency of CMU_HFCORECLKLEDIV2. */ |
| ret = SystemCoreClockGet() / (1U << (getHfLeConfig() + 1)); |
| #else |
| ret = SystemCoreClockGet() / 2U; |
| #endif |
| break; |
| #endif |
| |
| case _CMU_LFCLKSEL_LFA_DISABLED: |
| ret = 0; |
| #if defined(CMU_LFCLKSEL_LFAE) |
| /* Check LF Extended bit setting for LFA or LFB ULFRCO clock. */ |
| if ((lfClkBranch == cmuClock_LFA) || (lfClkBranch == cmuClock_LFB)) { |
| if (CMU->LFCLKSEL >> (lfClkBranch == cmuClock_LFA |
| ? _CMU_LFCLKSEL_LFAE_SHIFT |
| : _CMU_LFCLKSEL_LFBE_SHIFT)) { |
| ret = SystemULFRCOClockGet(); |
| } |
| } |
| #endif |
| break; |
| |
| default: |
| ret = 0U; |
| EFM_ASSERT(false); |
| break; |
| } |
| #endif /* _CMU_LFCLKSEL_MASK */ |
| |
| #if defined(_CMU_LFACLKSEL_MASK) |
| switch (sel) { |
| case _CMU_LFACLKSEL_LFA_LFRCO: |
| ret = SystemLFRCOClockGet(); |
| break; |
| |
| case _CMU_LFACLKSEL_LFA_LFXO: |
| ret = SystemLFXOClockGet(); |
| break; |
| |
| case _CMU_LFACLKSEL_LFA_ULFRCO: |
| ret = SystemULFRCOClockGet(); |
| break; |
| |
| #if defined(_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) + 1UL); |
| break; |
| #endif |
| |
| case _CMU_LFACLKSEL_LFA_DISABLED: |
| ret = 0; |
| break; |
| |
| default: |
| ret = 0U; |
| EFM_ASSERT(false); |
| break; |
| } |
| #endif |
| |
| return ret; |
| } |
| |
| /***************************************************************************//** |
| * @brief |
| * Wait for an ongoing sync of register(s) to low-frequency domain to complete. |
| * |
| * @param[in] mask |
| * A bitmask corresponding to SYNCBUSY register defined bits, indicating |
| * registers that must complete any ongoing synchronization. |
| ******************************************************************************/ |
| __STATIC_INLINE void syncReg(uint32_t mask) |
| { |
| /* Avoid a deadlock if modifying the same register twice when freeze mode is */ |
| /* activated. */ |
| if ((CMU->FREEZE & CMU_FREEZE_REGFREEZE) != 0UL) { |
| return; |
| } |
| |
| /* Wait for any pending previous write operation to complete */ |
| /* in low-frequency domain. */ |
| while ((CMU->SYNCBUSY & mask) != 0UL) { |
| } |
| } |
| |
| #if defined(USBC_CLOCK_PRESENT) |
| /***************************************************************************//** |
| * @brief |
| * Get the USBC frequency. |
| * |
| * @return |
| * USBC frequency in Hz. |
| ******************************************************************************/ |
| static uint32_t usbCClkGet(void) |
| { |
| uint32_t ret; |
| CMU_Select_TypeDef clk; |
| |
| /* Get the selected clock source. */ |
| clk = CMU_ClockSelectGet(cmuClock_USBC); |
| |
| switch (clk) { |
| case cmuSelect_LFXO: |
| ret = SystemLFXOClockGet(); |
| break; |
| case cmuSelect_LFRCO: |
| ret = SystemLFRCOClockGet(); |
| break; |
| #if defined (_CMU_USHFRCOCTRL_MASK) |
| case cmuSelect_USHFRCO: |
| ret = ushfrcoFreq; |
| break; |
| #endif |
| case cmuSelect_HFCLK: |
| ret = SystemHFClockGet(); |
| break; |
| default: |
| /* Clock is not enabled */ |
| ret = 0; |
| break; |
| } |
| return ret; |
| } |
| #endif |
| |
| /***************************************************************************//** |
| * @brief |
| * Set HFPER clock tree prescalers to safe values. |
| * |
| * @note |
| * This function applies to EFM32GG11B. There are 3 HFPER clock trees with |
| * these frequency limits: |
| * HFPERCLK (A-tree): 20MHz in VSCALE0 mode, 50MHz in VSCALE2 mode. |
| * HFPERBCLK (B-tree): 20MHz in VSCALE0 mode, 72MHz in VSCALE2 mode. |
| * HFPERCCLK (C-tree): 20MHz in VSCALE0 mode, 50MHz in VSCALE2 mode. |
| ******************************************************************************/ |
| static void hfperClkSafePrescaler(void) |
| { |
| #if defined(_CMU_HFPERPRESC_MASK) && defined(_CMU_HFPERPRESCB_MASK) \ |
| && defined(_CMU_HFPERPRESCC_MASK) |
| // Assuming a max. HFCLK of 72MHz, we need to set prescalers to DIV4. |
| CMU_ClockPrescSet(cmuClock_HFPER, 3U); |
| CMU_ClockPrescSet(cmuClock_HFPERB, 3U); |
| CMU_ClockPrescSet(cmuClock_HFPERC, 3U); |
| #endif |
| } |
| |
| /***************************************************************************//** |
| * @brief |
| * Set HFPER clock tree prescalers to give highest possible clock node |
| * frequency while still beeing within spec. |
| * |
| * @note |
| * This function applies to EFM32GG11B. There are 3 HFPER clock trees with |
| * these frequency limits: |
| * HFPERCLK (A-tree): 20MHz in VSCALE0 mode, 50MHz in VSCALE2 mode. |
| * HFPERBCLK (B-tree): 20MHz in VSCALE0 mode, 72MHz in VSCALE2 mode. |
| * HFPERCCLK (C-tree): 20MHz in VSCALE0 mode, 50MHz in VSCALE2 mode. |
| ******************************************************************************/ |
| static void hfperClkOptimizedPrescaler(void) |
| { |
| #if defined(_CMU_HFPERPRESC_MASK) && defined(_CMU_HFPERPRESCB_MASK) \ |
| && defined(_CMU_HFPERPRESCC_MASK) |
| uint32_t hfClkFreq, divisor; |
| |
| hfClkFreq = SystemHFClockGet(); |
| |
| if ( EMU_VScaleGet() == emuVScaleEM01_LowPower) { |
| divisor = (hfClkFreq + 20000000U - 1U) / 20000000U; // ceil(x) |
| if (divisor > 0U) { |
| divisor--; // Convert to prescaler |
| } |
| CMU_ClockPrescSet(cmuClock_HFPER, divisor); |
| CMU_ClockPrescSet(cmuClock_HFPERB, divisor); |
| CMU_ClockPrescSet(cmuClock_HFPERC, divisor); |
| } else { |
| divisor = (hfClkFreq + 50000000U - 1U) / 50000000U; |
| if (divisor > 0U) { |
| divisor--; |
| } |
| CMU_ClockPrescSet(cmuClock_HFPER, divisor); |
| CMU_ClockPrescSet(cmuClock_HFPERC, divisor); |
| |
| divisor = (hfClkFreq + 72000000U - 1U) / 72000000U; |
| if (divisor > 0U) { |
| divisor--; |
| } |
| CMU_ClockPrescSet(cmuClock_HFPERB, divisor); |
| } |
| #endif |
| } |
| |
| /** @endcond */ |
| |
| /******************************************************************************* |
| ************************** GLOBAL FUNCTIONS ******************************* |
| ******************************************************************************/ |
| |
| #if defined(_CMU_AUXHFRCOCTRL_BAND_MASK) |
| /***************************************************************************//** |
| * @brief |
| * Get the AUXHFRCO band in use. |
| * |
| * @return |
| * AUXHFRCO band in use. |
| ******************************************************************************/ |
| CMU_AUXHFRCOBand_TypeDef CMU_AUXHFRCOBandGet(void) |
| { |
| return (CMU_AUXHFRCOBand_TypeDef)((CMU->AUXHFRCOCTRL |
| & _CMU_AUXHFRCOCTRL_BAND_MASK) |
| >> _CMU_AUXHFRCOCTRL_BAND_SHIFT); |
| } |
| #endif /* _CMU_AUXHFRCOCTRL_BAND_MASK */ |
| |
| #if defined(_CMU_AUXHFRCOCTRL_BAND_MASK) |
| /***************************************************************************//** |
| * @brief |
| * Set the AUXHFRCO band and the tuning value based on the value in the |
| * calibration table made during production. |
| * |
| * @param[in] band |
| * AUXHFRCO band to activate. |
| ******************************************************************************/ |
| void CMU_AUXHFRCOBandSet(CMU_AUXHFRCOBand_TypeDef band) |
| { |
| uint32_t tuning; |
| |
| /* Read a tuning value from the calibration table. */ |
| switch (band) { |
| case cmuAUXHFRCOBand_1MHz: |
| tuning = (DEVINFO->AUXHFRCOCAL0 & _DEVINFO_AUXHFRCOCAL0_BAND1_MASK) |
| >> _DEVINFO_AUXHFRCOCAL0_BAND1_SHIFT; |
| break; |
| |
| case cmuAUXHFRCOBand_7MHz: |
| tuning = (DEVINFO->AUXHFRCOCAL0 & _DEVINFO_AUXHFRCOCAL0_BAND7_MASK) |
| >> _DEVINFO_AUXHFRCOCAL0_BAND7_SHIFT; |
| break; |
| |
| case cmuAUXHFRCOBand_11MHz: |
| tuning = (DEVINFO->AUXHFRCOCAL0 & _DEVINFO_AUXHFRCOCAL0_BAND11_MASK) |
| >> _DEVINFO_AUXHFRCOCAL0_BAND11_SHIFT; |
| break; |
| |
| case cmuAUXHFRCOBand_14MHz: |
| tuning = (DEVINFO->AUXHFRCOCAL0 & _DEVINFO_AUXHFRCOCAL0_BAND14_MASK) |
| >> _DEVINFO_AUXHFRCOCAL0_BAND14_SHIFT; |
| break; |
| |
| case cmuAUXHFRCOBand_21MHz: |
| tuning = (DEVINFO->AUXHFRCOCAL1 & _DEVINFO_AUXHFRCOCAL1_BAND21_MASK) |
| >> _DEVINFO_AUXHFRCOCAL1_BAND21_SHIFT; |
| break; |
| |
| #if defined(_CMU_AUXHFRCOCTRL_BAND_28MHZ) |
| case cmuAUXHFRCOBand_28MHz: |
| tuning = (DEVINFO->AUXHFRCOCAL1 & _DEVINFO_AUXHFRCOCAL1_BAND28_MASK) |
| >> _DEVINFO_AUXHFRCOCAL1_BAND28_SHIFT; |
| break; |
| #endif |
| |
| default: |
| EFM_ASSERT(false); |
| return; |
| } |
| |
| /* Set band/tuning. */ |
| CMU->AUXHFRCOCTRL = (CMU->AUXHFRCOCTRL |
| & ~(_CMU_AUXHFRCOCTRL_BAND_MASK |
| | _CMU_AUXHFRCOCTRL_TUNING_MASK)) |
| | (band << _CMU_AUXHFRCOCTRL_BAND_SHIFT) |
| | (tuning << _CMU_AUXHFRCOCTRL_TUNING_SHIFT); |
| } |
| #endif /* _CMU_AUXHFRCOCTRL_BAND_MASK */ |
| |
| #if defined(_CMU_AUXHFRCOCTRL_FREQRANGE_MASK) |
| /**************************************************************************//** |
| * @brief |
| * Get the AUXHFRCO frequency calibration word in DEVINFO. |
| * |
| * @param[in] freq |
| * Frequency in Hz. |
| * |
| * @return |
| * AUXHFRCO calibration word for a given frequency. |
| *****************************************************************************/ |
| static uint32_t CMU_AUXHFRCODevinfoGet(CMU_AUXHFRCOFreq_TypeDef freq) |
| { |
| switch (freq) { |
| /* 1, 2, and 4 MHz share the same calibration word. */ |
| case cmuAUXHFRCOFreq_1M0Hz: |
| case cmuAUXHFRCOFreq_2M0Hz: |
| case cmuAUXHFRCOFreq_4M0Hz: |
| return DEVINFO->AUXHFRCOCAL0; |
| |
| case cmuAUXHFRCOFreq_7M0Hz: |
| return DEVINFO->AUXHFRCOCAL3; |
| |
| case cmuAUXHFRCOFreq_13M0Hz: |
| return DEVINFO->AUXHFRCOCAL6; |
| |
| case cmuAUXHFRCOFreq_16M0Hz: |
| return DEVINFO->AUXHFRCOCAL7; |
| |
| case cmuAUXHFRCOFreq_19M0Hz: |
| return DEVINFO->AUXHFRCOCAL8; |
| |
| case cmuAUXHFRCOFreq_26M0Hz: |
| return DEVINFO->AUXHFRCOCAL10; |
| |
| case cmuAUXHFRCOFreq_32M0Hz: |
| return DEVINFO->AUXHFRCOCAL11; |
| |
| case cmuAUXHFRCOFreq_38M0Hz: |
| return DEVINFO->AUXHFRCOCAL12; |
| |
| #if defined(_DEVINFO_AUXHFRCOCAL13_MASK) |
| case cmuAUXHFRCOFreq_48M0Hz: |
| return DEVINFO->AUXHFRCOCAL13; |
| #endif |
| #if defined(_DEVINFO_AUXHFRCOCAL14_MASK) |
| case cmuAUXHFRCOFreq_50M0Hz: |
| return DEVINFO->AUXHFRCOCAL14; |
| #endif |
| |
| default: /* cmuAUXHFRCOFreq_UserDefined */ |
| return 0; |
| } |
| } |
| #endif /* _CMU_AUXHFRCOCTRL_FREQRANGE_MASK */ |
| |
| #if defined(_CMU_AUXHFRCOCTRL_FREQRANGE_MASK) |
| /***************************************************************************//** |
| * @brief |
| * Get the current AUXHFRCO frequency. |
| * |
| * @return |
| * AUXHFRCO frequency. |
| ******************************************************************************/ |
| CMU_AUXHFRCOFreq_TypeDef CMU_AUXHFRCOBandGet(void) |
| { |
| return auxHfrcoFreq; |
| } |
| #endif /* _CMU_AUXHFRCOCTRL_FREQRANGE_MASK */ |
| |
| #if defined(_CMU_AUXHFRCOCTRL_FREQRANGE_MASK) |
| /***************************************************************************//** |
| * @brief |
| * Set AUXHFRCO calibration for the selected target frequency. |
| * |
| * @param[in] setFreq |
| * AUXHFRCO frequency to set |
| ******************************************************************************/ |
| void CMU_AUXHFRCOBandSet(CMU_AUXHFRCOFreq_TypeDef setFreq) |
| { |
| uint32_t freqCal; |
| |
| /* Get DEVINFO index and set global auxHfrcoFreq. */ |
| freqCal = CMU_AUXHFRCODevinfoGet(setFreq); |
| EFM_ASSERT((freqCal != 0UL) && (freqCal != UINT_MAX)); |
| auxHfrcoFreq = setFreq; |
| |
| /* Wait for any previous sync to complete, then set calibration data |
| for the selected frequency. */ |
| while (BUS_RegBitRead(&CMU->SYNCBUSY, |
| _CMU_SYNCBUSY_AUXHFRCOBSY_SHIFT) != 0UL) { |
| } |
| |
| /* Set a divider in AUXHFRCOCTRL for 1, 2, and 4 MHz. */ |
| switch (setFreq) { |
| case cmuAUXHFRCOFreq_1M0Hz: |
| freqCal = (freqCal & ~_CMU_AUXHFRCOCTRL_CLKDIV_MASK) |
| | CMU_AUXHFRCOCTRL_CLKDIV_DIV4; |
| break; |
| |
| case cmuAUXHFRCOFreq_2M0Hz: |
| freqCal = (freqCal & ~_CMU_AUXHFRCOCTRL_CLKDIV_MASK) |
| | CMU_AUXHFRCOCTRL_CLKDIV_DIV2; |
| break; |
| |
| case cmuAUXHFRCOFreq_4M0Hz: |
| freqCal = (freqCal & ~_CMU_AUXHFRCOCTRL_CLKDIV_MASK) |
| | CMU_AUXHFRCOCTRL_CLKDIV_DIV1; |
| break; |
| |
| default: |
| break; |
| } |
| CMU->AUXHFRCOCTRL = freqCal; |
| } |
| #endif /* _CMU_AUXHFRCOCTRL_FREQRANGE_MASK */ |
| |
| /***************************************************************************//** |
| * @brief |
| * Calibrate the clock. |
| * |
| * @details |
| * Run a calibration for HFCLK against a selectable reference clock. |
| * See the reference manual, CMU chapter, for more details. |
| * |
| * @note |
| * This function will not return until the calibration measurement is completed. |
| * |
| * @param[in] HFCycles |
| * The number of HFCLK cycles to run the calibration. Increasing this number |
| * increases precision but the calibration will take more time. |
| * |
| * @param[in] ref |
| * The reference clock used to compare HFCLK. |
| * |
| * @return |
| * The number of ticks the reference clock after HFCycles ticks on the HF |
| * clock. |
| ******************************************************************************/ |
| uint32_t CMU_Calibrate(uint32_t HFCycles, CMU_Osc_TypeDef reference) |
| { |
| EFM_ASSERT(HFCycles <= (_CMU_CALCNT_CALCNT_MASK >> _CMU_CALCNT_CALCNT_SHIFT)); |
| |
| /* Set the reference clock source. */ |
| switch (reference) { |
| case cmuOsc_LFXO: |
| CMU->CALCTRL = CMU_CALCTRL_UPSEL_LFXO; |
| break; |
| |
| case cmuOsc_LFRCO: |
| CMU->CALCTRL = CMU_CALCTRL_UPSEL_LFRCO; |
| break; |
| |
| case cmuOsc_HFXO: |
| CMU->CALCTRL = CMU_CALCTRL_UPSEL_HFXO; |
| break; |
| |
| case cmuOsc_HFRCO: |
| CMU->CALCTRL = CMU_CALCTRL_UPSEL_HFRCO; |
| break; |
| |
| case cmuOsc_AUXHFRCO: |
| CMU->CALCTRL = CMU_CALCTRL_UPSEL_AUXHFRCO; |
| break; |
| |
| #if defined (_CMU_USHFRCOCTRL_MASK) |
| case cmuOsc_USHFRCO: |
| CMU->CALCTRL = CMU_CALCTRL_UPSEL_USHFRCO; |
| break; |
| #endif |
| |
| default: |
| EFM_ASSERT(false); |
| return 0; |
| } |
| |
| /* Set the top value. */ |
| CMU->CALCNT = HFCycles; |
| |
| /* Start the calibration. */ |
| CMU->CMD = CMU_CMD_CALSTART; |
| |
| #if defined(CMU_STATUS_CALRDY) |
| /* Wait until calibration completes. */ |
| while (BUS_RegBitRead(&CMU->STATUS, _CMU_STATUS_CALRDY_SHIFT) == 0UL) { |
| } |
| #else |
| /* Wait until calibration completes. */ |
| while (BUS_RegBitRead(&CMU->STATUS, _CMU_STATUS_CALBSY_SHIFT) != 0UL) { |
| } |
| #endif |
| |
| return CMU->CALCNT; |
| } |
| |
| #if defined(_CMU_CALCTRL_UPSEL_MASK) && defined(_CMU_CALCTRL_DOWNSEL_MASK) |
| /***************************************************************************//** |
| * @brief |
| * Configure the clock calibration. |
| * |
| * @details |
| * Configure a calibration for a selectable clock source against another |
| * selectable reference clock. |
| * See the reference manual, CMU chapter, for more details. |
| * |
| * @note |
| * After configuration, a call to @ref CMU_CalibrateStart() is required and |
| * the resulting calibration value can be read out with the |
| * @ref CMU_CalibrateCountGet() function call. |
| * |
| * @param[in] downCycles |
| * The number of downSel clock cycles to run the calibration. Increasing this |
| * number increases precision but the calibration will take more time. |
| * |
| * @param[in] downSel |
| * The clock, which will be counted down downCycles. |
| * |
| * @param[in] upSel |
| * The reference clock; the number of cycles generated by this clock will |
| * be counted and added up and the result can be given with the |
| * @ref CMU_CalibrateCountGet() function call. |
| ******************************************************************************/ |
| void CMU_CalibrateConfig(uint32_t downCycles, CMU_Osc_TypeDef downSel, |
| CMU_Osc_TypeDef upSel) |
| { |
| /* Keep configuration settings untouched. */ |
| uint32_t calCtrl = CMU->CALCTRL |
| & ~(_CMU_CALCTRL_UPSEL_MASK | _CMU_CALCTRL_DOWNSEL_MASK); |
| |
| /* 20 bits of precision to calibration count register. */ |
| EFM_ASSERT(downCycles <= (_CMU_CALCNT_CALCNT_MASK >> _CMU_CALCNT_CALCNT_SHIFT)); |
| |
| /* Set down counting clock source - down counter. */ |
| switch (downSel) { |
| case cmuOsc_LFXO: |
| calCtrl |= CMU_CALCTRL_DOWNSEL_LFXO; |
| break; |
| |
| case cmuOsc_LFRCO: |
| calCtrl |= CMU_CALCTRL_DOWNSEL_LFRCO; |
| break; |
| |
| case cmuOsc_HFXO: |
| calCtrl |= CMU_CALCTRL_DOWNSEL_HFXO; |
| break; |
| |
| case cmuOsc_HFRCO: |
| calCtrl |= CMU_CALCTRL_DOWNSEL_HFRCO; |
| break; |
| |
| case cmuOsc_AUXHFRCO: |
| calCtrl |= CMU_CALCTRL_DOWNSEL_AUXHFRCO; |
| break; |
| |
| #if defined (_CMU_USHFRCOCTRL_MASK) |
| case cmuOsc_USHFRCO: |
| calCtrl |= CMU_CALCTRL_DOWNSEL_USHFRCO; |
| break; |
| #endif |
| |
| default: |
| EFM_ASSERT(false); |
| break; |
| } |
| |
| /* Set the top value to be counted down by the downSel clock. */ |
| CMU->CALCNT = downCycles; |
| |
| /* Set the reference clock source - up counter. */ |
| switch (upSel) { |
| case cmuOsc_LFXO: |
| calCtrl |= CMU_CALCTRL_UPSEL_LFXO; |
| break; |
| |
| case cmuOsc_LFRCO: |
| calCtrl |= CMU_CALCTRL_UPSEL_LFRCO; |
| break; |
| |
| case cmuOsc_HFXO: |
| calCtrl |= CMU_CALCTRL_UPSEL_HFXO; |
| break; |
| |
| case cmuOsc_HFRCO: |
| calCtrl |= CMU_CALCTRL_UPSEL_HFRCO; |
| break; |
| |
| case cmuOsc_AUXHFRCO: |
| calCtrl |= CMU_CALCTRL_UPSEL_AUXHFRCO; |
| break; |
| |
| #if defined (_CMU_USHFRCOCTRL_MASK) |
| case cmuOsc_USHFRCO: |
| calCtrl |= CMU_CALCTRL_UPSEL_USHFRCO; |
| break; |
| #endif |
| |
| default: |
| EFM_ASSERT(false); |
| break; |
| } |
| |
| CMU->CALCTRL = calCtrl; |
| } |
| #endif |
| |
| /***************************************************************************//** |
| * @brief |
| * Get the calibration count register. |
| * @note |
| * If continuous calibration mode is active, calibration busy will almost |
| * always be off and only the value needs to be read. In a normal case, |
| * this function call is triggered by the CALRDY |
| * interrupt flag. |
| * @return |
| * The calibration count, the number of UPSEL clocks (see @ref CMU_CalibrateConfig()) |
| * in the period of DOWNSEL oscillator clock cycles configured by a previous |
| * write operation to CMU->CALCNT. |
| ******************************************************************************/ |
| uint32_t CMU_CalibrateCountGet(void) |
| { |
| /* Wait until calibration completes, UNLESS continuous calibration mode is */ |
| /* active. */ |
| #if defined(CMU_CALCTRL_CONT) |
| if (BUS_RegBitRead(&CMU->CALCTRL, _CMU_CALCTRL_CONT_SHIFT) == 0UL) { |
| #if defined(CMU_STATUS_CALRDY) |
| /* Wait until calibration completes */ |
| while (BUS_RegBitRead(&CMU->STATUS, _CMU_STATUS_CALRDY_SHIFT) == 0UL) { |
| } |
| #else |
| /* Wait until calibration completes */ |
| while (BUS_RegBitRead(&CMU->STATUS, _CMU_STATUS_CALBSY_SHIFT) != 0UL) { |
| } |
| #endif |
| } |
| #else |
| while (BUS_RegBitRead(&CMU->STATUS, _CMU_STATUS_CALBSY_SHIFT) != 0UL) { |
| } |
| #endif |
| return CMU->CALCNT; |
| } |
| |
| /***************************************************************************//** |
| * @brief |
| * Get the clock divisor/prescaler. |
| * |
| * @param[in] clock |
| * A clock point to get the divisor/prescaler for. Notice that not all clock points |
| * have a divisor/prescaler. See the CMU overview in the reference manual. |
| * |
| * @return |
| * The current clock point divisor/prescaler. 1 is returned |
| * if @p clock specifies a clock point without a divisor/prescaler. |
| ******************************************************************************/ |
| CMU_ClkDiv_TypeDef CMU_ClockDivGet(CMU_Clock_TypeDef clock) |
| { |
| #if defined(_SILICON_LABS_32B_SERIES_1) |
| return 1UL + (uint32_t)CMU_ClockPrescGet(clock); |
| |
| #elif defined(_SILICON_LABS_32B_SERIES_0) |
| uint32_t divReg; |
| CMU_ClkDiv_TypeDef ret; |
| |
| /* Get divisor reg ID. */ |
| divReg = (clock >> CMU_DIV_REG_POS) & CMU_DIV_REG_MASK; |
| |
| switch (divReg) { |
| #if defined(_CMU_CTRL_HFCLKDIV_MASK) |
| case CMU_HFCLKDIV_REG: |
| ret = 1 + ((CMU->CTRL & _CMU_CTRL_HFCLKDIV_MASK) |
| >> _CMU_CTRL_HFCLKDIV_SHIFT); |
| break; |
| #endif |
| |
| case CMU_HFPERCLKDIV_REG: |
| ret = (CMU_ClkDiv_TypeDef)((CMU->HFPERCLKDIV |
| & _CMU_HFPERCLKDIV_HFPERCLKDIV_MASK) |
| >> _CMU_HFPERCLKDIV_HFPERCLKDIV_SHIFT); |
| ret = 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: |
| ret = cmuClkDiv_1; |
| EFM_ASSERT(false); |
| break; |
| } |
| break; |
| |
| case CMU_LFBPRESC0_REG: |
| switch (clock) { |
| #if defined(_CMU_LFBPRESC0_LEUART0_MASK) |
| case cmuClock_LEUART0: |
| ret = (CMU_ClkDiv_TypeDef)((CMU->LFBPRESC0 & _CMU_LFBPRESC0_LEUART0_MASK) |
| >> _CMU_LFBPRESC0_LEUART0_SHIFT); |
| ret = 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: |
| ret = cmuClkDiv_1; |
| EFM_ASSERT(false); |
| break; |
| } |
| break; |
| |
| default: |
| ret = cmuClkDiv_1; |
| EFM_ASSERT(false); |
| break; |
| } |
| |
| return ret; |
| #endif |
| } |
| |
| /***************************************************************************//** |
| * @brief |
| * Set the clock divisor/prescaler. |
| * |
| * @note |
| * If setting an LF clock prescaler, synchronization into the low-frequency |
| * domain is required. If the same register is modified before a previous |
| * update has completed, this function will stall until the previous |
| * synchronization has completed. See @ref CMU_FreezeEnable() for |
| * a suggestion on how to reduce the stalling time in some use cases. |
| * |
| * @param[in] clock |
| * Clock point to set divisor/prescaler for. Notice that not all clock points |
| * have a divisor/prescaler. See the CMU overview in the reference |
| * manual. |
| * |
| * @param[in] div |
| * The clock divisor to use (<= cmuClkDiv_512). |
| ******************************************************************************/ |
| void CMU_ClockDivSet(CMU_Clock_TypeDef clock, CMU_ClkDiv_TypeDef div) |
| { |
| #if defined(_SILICON_LABS_32B_SERIES_1) |
| CMU_ClockPrescSet(clock, (CMU_ClkPresc_TypeDef)(div - 1U)); |
| |
| #elif defined(_SILICON_LABS_32B_SERIES_0) |
| uint32_t freq; |
| uint32_t divReg; |
| |
| /* Get the divisor reg ID. */ |
| divReg = (clock >> CMU_DIV_REG_POS) & CMU_DIV_REG_MASK; |
| |
| switch (divReg) { |
| #if defined(_CMU_CTRL_HFCLKDIV_MASK) |
| case CMU_HFCLKDIV_REG: |
| EFM_ASSERT((div >= cmuClkDiv_1) && (div <= cmuClkDiv_8)); |
| |
| /* Configure worst case wait states for flash access before setting divisor. */ |
| flashWaitStateMax(); |
| |
| /* Set the divider. */ |
| CMU->CTRL = (CMU->CTRL & ~_CMU_CTRL_HFCLKDIV_MASK) |
| | ((div - 1) << _CMU_CTRL_HFCLKDIV_SHIFT); |
| |
| /* Update the CMSIS core clock variable. */ |
| /* (The function will update the global variable). */ |
| freq = SystemCoreClockGet(); |
| |
| /* Optimize flash access wait state setting for the current core clk. */ |
| CMU_UpdateWaitStates(freq, (int)VSCALE_DEFAULT); |
| break; |
| #endif |
| |
| case CMU_HFPERCLKDIV_REG: |
| EFM_ASSERT((div >= cmuClkDiv_1) && (div <= cmuClkDiv_512)); |
| /* Convert to the correct scale. */ |
| div = CMU_DivToLog2(div); |
| CMU->HFPERCLKDIV = (CMU->HFPERCLKDIV & ~_CMU_HFPERCLKDIV_HFPERCLKDIV_MASK) |
| | (div << _CMU_HFPERCLKDIV_HFPERCLKDIV_SHIFT); |
| break; |
| |
| case CMU_HFCORECLKDIV_REG: |
| EFM_ASSERT((div >= cmuClkDiv_1) && (div <= cmuClkDiv_512)); |
| |
| /* Configure worst case wait states for flash access before setting the divisor. */ |
| flashWaitStateMax(); |
| |
| #if defined(CMU_MAX_FREQ_HFLE) |
| setHfLeConfig(SystemHFClockGet() / div); |
| #endif |
| |
| /* Convert to the correct scale. */ |
| div = CMU_DivToLog2(div); |
| |
| CMU->HFCORECLKDIV = (CMU->HFCORECLKDIV |
| & ~_CMU_HFCORECLKDIV_HFCORECLKDIV_MASK) |
| | (div << _CMU_HFCORECLKDIV_HFCORECLKDIV_SHIFT); |
| |
| /* Update the CMSIS core clock variable. */ |
| /* (The function will update the global variable). */ |
| freq = SystemCoreClockGet(); |
| |
| /* Optimize wait state setting for the current core clk. */ |
| CMU_UpdateWaitStates(freq, (int)VSCALE_DEFAULT); |
| break; |
| |
| case CMU_LFAPRESC0_REG: |
| switch (clock) { |
| case cmuClock_RTC: |
| EFM_ASSERT(div <= cmuClkDiv_32768); |
| |
| /* LF register about to be modified requires sync. busy check. */ |
| syncReg(CMU_SYNCBUSY_LFAPRESC0); |
| |
| /* Convert to the correct scale. */ |
| div = CMU_DivToLog2(div); |
| |
| CMU->LFAPRESC0 = (CMU->LFAPRESC0 & ~_CMU_LFAPRESC0_RTC_MASK) |
| | (div << _CMU_LFAPRESC0_RTC_SHIFT); |
| break; |
| |
| #if defined(_CMU_LFAPRESC0_LETIMER0_MASK) |
| case cmuClock_LETIMER0: |
| EFM_ASSERT(div <= cmuClkDiv_32768); |
| |
| /* LF register about to be modified requires sync. busy check. */ |
| syncReg(CMU_SYNCBUSY_LFAPRESC0); |
| |
| /* Convert to the correct scale. */ |
| div = CMU_DivToLog2(div); |
| |
| CMU->LFAPRESC0 = (CMU->LFAPRESC0 & ~_CMU_LFAPRESC0_LETIMER0_MASK) |
| | (div << _CMU_LFAPRESC0_LETIMER0_SHIFT); |
| break; |
| #endif |
| |
| #if defined(LCD_PRESENT) |
| case cmuClock_LCDpre: |
| EFM_ASSERT((div >= cmuClkDiv_16) && (div <= cmuClkDiv_128)); |
| |
| /* LF register about to be modified requires sync. busy check. */ |
| syncReg(CMU_SYNCBUSY_LFAPRESC0); |
| |
| /* Convert to the correct scale. */ |
| div = CMU_DivToLog2(div); |
| |
| CMU->LFAPRESC0 = (CMU->LFAPRESC0 & ~_CMU_LFAPRESC0_LCD_MASK) |
| | ((div - CMU_DivToLog2(cmuClkDiv_16)) |
| << _CMU_LFAPRESC0_LCD_SHIFT); |
| break; |
| #endif /* defined(LCD_PRESENT) */ |
| |
| #if defined(LESENSE_PRESENT) |
| case cmuClock_LESENSE: |
| EFM_ASSERT(div <= cmuClkDiv_8); |
| |
| /* LF register about to be modified requires sync. busy check. */ |
| syncReg(CMU_SYNCBUSY_LFAPRESC0); |
| |
| /* Convert to the correct scale. */ |
| div = CMU_DivToLog2(div); |
| |
| CMU->LFAPRESC0 = (CMU->LFAPRESC0 & ~_CMU_LFAPRESC0_LESENSE_MASK) |
| | (div << _CMU_LFAPRESC0_LESENSE_SHIFT); |
| break; |
| #endif /* defined(LESENSE_PRESENT) */ |
| |
| default: |
| EFM_ASSERT(false); |
| break; |
| } |
| break; |
| |
| case CMU_LFBPRESC0_REG: |
| switch (clock) { |
| #if defined(_CMU_LFBPRESC0_LEUART0_MASK) |
| case cmuClock_LEUART0: |
| EFM_ASSERT(div <= cmuClkDiv_8); |
| |
| /* LF register about to be modified requires sync. busy check. */ |
| syncReg(CMU_SYNCBUSY_LFBPRESC0); |
| |
| /* Convert to the correct scale. */ |
| div = CMU_DivToLog2(div); |
| |
| CMU->LFBPRESC0 = (CMU->LFBPRESC0 & ~_CMU_LFBPRESC0_LEUART0_MASK) |
| | (((uint32_t)div) << _CMU_LFBPRESC0_LEUART0_SHIFT); |
| break; |
| #endif |
| |
| #if defined(_CMU_LFBPRESC0_LEUART1_MASK) |
| case cmuClock_LEUART1: |
| EFM_ASSERT(div <= cmuClkDiv_8); |
| |
| /* LF register about to be modified requires sync. busy check. */ |
| syncReg(CMU_SYNCBUSY_LFBPRESC0); |
| |
| /* Convert to the correct scale. */ |
| div = CMU_DivToLog2(div); |
| |
| CMU->LFBPRESC0 = (CMU->LFBPRESC0 & ~_CMU_LFBPRESC0_LEUART1_MASK) |
| | (((uint32_t)div) << _CMU_LFBPRESC0_LEUART1_SHIFT); |
| break; |
| #endif |
| |
| default: |
| EFM_ASSERT(false); |
| break; |
| } |
| break; |
| |
| default: |
| EFM_ASSERT(false); |
| break; |
| } |
| #endif |
| } |
| |
| /***************************************************************************//** |
| * @brief |
| * Enable/disable a clock. |
| * |
| * @details |
| * In general, module clocking is disabled after a reset. If a module |
| * clock is disabled, the registers of that module are not accessible and |
| * reading from such registers may return undefined values. Writing to |
| * registers of clock-disabled modules has no effect. |
| * Avoid accessing module registers of a module with a disabled clock. |
| * |
| * @note |
| * If enabling/disabling an LF clock, synchronization into the low-frequency |
| * domain is required. If the same register is modified before a previous |
| * update has completed, this function will stall until the previous |
| * synchronization has completed. See @ref CMU_FreezeEnable() for |
| * a suggestion on how to reduce the stalling time in some use cases. |
| * |
| * @param[in] clock |
| * The clock to enable/disable. Notice that not all defined clock |
| * points have separate enable/disable control. See the CMU overview |
| * in the reference manual. |
| * |
| * @param[in] enable |
| * @li true - enable specified clock. |
| * @li false - disable specified clock. |
| ******************************************************************************/ |
| void CMU_ClockEnable(CMU_Clock_TypeDef clock, bool enable) |
| { |
| volatile uint32_t *reg; |
| uint32_t bit; |
| uint32_t sync = 0; |
| |
| /* Identify enable register */ |
| switch (((unsigned)clock >> CMU_EN_REG_POS) & CMU_EN_REG_MASK) { |
| #if defined(_CMU_CTRL_HFPERCLKEN_MASK) |
| case CMU_CTRL_EN_REG: |
| reg = &CMU->CTRL; |
| break; |
| #endif |
| |
| #if defined(_CMU_HFCORECLKEN0_MASK) |
| case CMU_HFCORECLKEN0_EN_REG: |
| reg = &CMU->HFCORECLKEN0; |
| #if defined(CMU_MAX_FREQ_HFLE) |
| setHfLeConfig(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 a clock point. */ |
| EFM_ASSERT(false); |
| return; |
| } |
| |
| /* Get the bit position used to enable/disable. */ |
| bit = ((unsigned)clock >> CMU_EN_BIT_POS) & CMU_EN_BIT_MASK; |
| |
| /* LF synchronization required. */ |
| if (sync > 0UL) { |
| syncReg(sync); |
| } |
| |
| /* Set/clear bit as requested. */ |
| BUS_RegBitWrite(reg, bit, (uint32_t)enable); |
| } |
| |
| /***************************************************************************//** |
| * @brief |
| * Get the clock frequency for a clock point. |
| * |
| * @param[in] clock |
| * A clock point to fetch the frequency for. |
| * |
| * @return |
| * The current frequency in Hz. |
| ******************************************************************************/ |
| uint32_t CMU_ClockFreqGet(CMU_Clock_TypeDef clock) |
| { |
| uint32_t ret; |
| |
| switch ((unsigned)clock & (CMU_CLK_BRANCH_MASK << CMU_CLK_BRANCH_POS)) { |
| case (CMU_HF_CLK_BRANCH << CMU_CLK_BRANCH_POS): |
| ret = SystemHFClockGet(); |
| break; |
| |
| case (CMU_HFPER_CLK_BRANCH << CMU_CLK_BRANCH_POS): |
| ret = SystemHFClockGet(); |
| /* Calculate frequency after HFPER divider. */ |
| #if defined(_CMU_HFPERCLKDIV_HFPERCLKDIV_MASK) |
| ret >>= (CMU->HFPERCLKDIV & _CMU_HFPERCLKDIV_HFPERCLKDIV_MASK) |
| >> _CMU_HFPERCLKDIV_HFPERCLKDIV_SHIFT; |
| #endif |
| #if defined(_CMU_HFPERPRESC_PRESC_MASK) |
| ret /= 1U + ((CMU->HFPERPRESC & _CMU_HFPERPRESC_PRESC_MASK) |
| >> _CMU_HFPERPRESC_PRESC_SHIFT); |
| #endif |
| break; |
| |
| #if defined(_CMU_HFPERPRESCB_MASK) |
| case (CMU_HFPERB_CLK_BRANCH << CMU_CLK_BRANCH_POS): |
| ret = SystemHFClockGet(); |
| /* Calculate frequency after HFPERB prescaler. */ |
| ret /= 1U + ((CMU->HFPERPRESCB & _CMU_HFPERPRESCB_PRESC_MASK) |
| >> _CMU_HFPERPRESCB_PRESC_SHIFT); |
| break; |
| #endif |
| |
| #if defined(_CMU_HFPERPRESCC_MASK) |
| case (CMU_HFPERC_CLK_BRANCH << CMU_CLK_BRANCH_POS): |
| ret = SystemHFClockGet(); |
| /* Calculate frequency after HFPERC prescaler. */ |
| ret /= 1U + ((CMU->HFPERPRESCC & _CMU_HFPERPRESCC_PRESC_MASK) |
| >> _CMU_HFPERPRESCC_PRESC_SHIFT); |
| break; |
| #endif |
| |
| #if defined(_SILICON_LABS_32B_SERIES_1) |
| #if defined(CRYPTO_PRESENT) \ |
| || defined(LDMA_PRESENT) \ |
| || defined(GPCRC_PRESENT) \ |
| || defined(PRS_PRESENT) \ |
| || defined(GPIO_PRESENT) |
| case (CMU_HFBUS_CLK_BRANCH << CMU_CLK_BRANCH_POS): |
| ret = SystemHFClockGet(); |
| 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) + 4UL); |
| break; |
| #endif |
| |
| #if defined(CMU_LFCCLKEN0_USB) |
| case (CMU_USBLE_CLK_BRANCH << CMU_CLK_BRANCH_POS): |
| ret = lfClkGet(cmuClock_LFC); |
| break; |
| #endif |
| |
| #if defined(_SILICON_LABS_32B_SERIES_1) |
| case (CMU_LFE_CLK_BRANCH << CMU_CLK_BRANCH_POS): |
| ret = lfClkGet(cmuClock_LFE); |
| break; |
| #endif |
| |
| case (CMU_DBG_CLK_BRANCH << CMU_CLK_BRANCH_POS): |
| ret = dbgClkGet(); |
| break; |
| |
| case (CMU_AUX_CLK_BRANCH << CMU_CLK_BRANCH_POS): |
| ret = auxClkGet(); |
| break; |
| |
| #if defined(USBC_CLOCK_PRESENT) |
| case (CMU_USBC_CLK_BRANCH << CMU_CLK_BRANCH_POS): |
| ret = usbCClkGet(); |
| break; |
| #endif |
| |
| #if defined(_CMU_ADCCTRL_ADC0CLKSEL_MASK) |
| case (CMU_ADC0ASYNC_CLK_BRANCH << CMU_CLK_BRANCH_POS): |
| ret = adcAsyncClkGet(0); |
| #if defined(_CMU_ADCCTRL_ADC0CLKDIV_MASK) |
| ret /= 1U + ((CMU->ADCCTRL & _CMU_ADCCTRL_ADC0CLKDIV_MASK) |
| >> _CMU_ADCCTRL_ADC0CLKDIV_SHIFT); |
| #endif |
| break; |
| #endif |
| |
| #if defined(_CMU_ADCCTRL_ADC1CLKSEL_MASK) |
| case (CMU_ADC1ASYNC_CLK_BRANCH << CMU_CLK_BRANCH_POS): |
| ret = adcAsyncClkGet(1); |
| #if defined(_CMU_ADCCTRL_ADC1CLKDIV_MASK) |
| ret /= 1U + ((CMU->ADCCTRL & _CMU_ADCCTRL_ADC1CLKDIV_MASK) |
| >> _CMU_ADCCTRL_ADC1CLKDIV_SHIFT); |
| #endif |
| break; |
| #endif |
| |
| #if defined(_CMU_SDIOCTRL_SDIOCLKSEL_MASK) |
| case (CMU_SDIOREF_CLK_BRANCH << CMU_CLK_BRANCH_POS): |
| ret = sdioRefClkGet(); |
| break; |
| #endif |
| |
| #if defined(_CMU_QSPICTRL_QSPI0CLKSEL_MASK) |
| case (CMU_QSPI0REF_CLK_BRANCH << CMU_CLK_BRANCH_POS): |
| ret = qspiRefClkGet(0); |
| break; |
| #endif |
| |
| #if defined(USBR_CLOCK_PRESENT) |
| case (CMU_USBR_CLK_BRANCH << CMU_CLK_BRANCH_POS): |
| ret = usbRateClkGet(); |
| break; |
| #endif |
| |
| default: |
| ret = 0; |
| EFM_ASSERT(false); |
| break; |
| } |
| |
| return ret; |
| } |
| |
| #if defined(_SILICON_LABS_32B_SERIES_1) |
| /***************************************************************************//** |
| * @brief |
| * Get the clock prescaler. |
| * |
| * @param[in] clock |
| * A clock point to get the prescaler for. Notice that not all clock points |
| * have a prescaler. See the CMU overview in the reference manual. |
| * |
| * @return |
| * The prescaler value of the current clock point. 0 is returned |
| * if @p clock specifies a clock point without a prescaler. |
| ******************************************************************************/ |
| uint32_t CMU_ClockPrescGet(CMU_Clock_TypeDef clock) |
| { |
| uint32_t prescReg; |
| uint32_t ret; |
| |
| /* Get the prescaler register ID. */ |
| prescReg = ((unsigned)clock >> CMU_PRESC_REG_POS) & CMU_PRESC_REG_MASK; |
| |
| switch (prescReg) { |
| case CMU_HFPRESC_REG: |
| ret = (CMU->HFPRESC & _CMU_HFPRESC_PRESC_MASK) |
| >> _CMU_HFPRESC_PRESC_SHIFT; |
| break; |
| |
| case CMU_HFEXPPRESC_REG: |
| ret = (CMU->HFEXPPRESC & _CMU_HFEXPPRESC_PRESC_MASK) |
| >> _CMU_HFEXPPRESC_PRESC_SHIFT; |
| break; |
| |
| case CMU_HFCLKLEPRESC_REG: |
| ret = (CMU->HFPRESC & _CMU_HFPRESC_HFCLKLEPRESC_MASK) |
| >> _CMU_HFPRESC_HFCLKLEPRESC_SHIFT; |
| break; |
| |
| case CMU_HFPERPRESC_REG: |
| ret = (CMU->HFPERPRESC & _CMU_HFPERPRESC_PRESC_MASK) |
| >> _CMU_HFPERPRESC_PRESC_SHIFT; |
| break; |
| |
| #if defined(_CMU_HFPERPRESCB_MASK) |
| case CMU_HFPERPRESCB_REG: |
| ret = (CMU->HFPERPRESCB & _CMU_HFPERPRESCB_PRESC_MASK) |
| >> _CMU_HFPERPRESCB_PRESC_SHIFT; |
| break; |
| #endif |
| |
| #if defined(_CMU_HFPERPRESCC_MASK) |
| case CMU_HFPERPRESCC_REG: |
| ret = (CMU->HFPERPRESCC & _CMU_HFPERPRESCC_PRESC_MASK) |
| >> _CMU_HFPERPRESCC_PRESC_SHIFT; |
| break; |
| #endif |
| |
| case CMU_HFCOREPRESC_REG: |
| ret = (CMU->HFCOREPRESC & _CMU_HFCOREPRESC_PRESC_MASK) |
| >> _CMU_HFCOREPRESC_PRESC_SHIFT; |
| break; |
| |
| case CMU_LFAPRESC0_REG: |
| switch (clock) { |
| #if defined(_CMU_LFAPRESC0_LETIMER0_MASK) |
| case cmuClock_LETIMER0: |
| ret = (CMU->LFAPRESC0 & _CMU_LFAPRESC0_LETIMER0_MASK) |
| >> _CMU_LFAPRESC0_LETIMER0_SHIFT; |
| /* Convert the exponent to a prescaler value. */ |
| ret = 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 a 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: |
| ret = 0U; |
| EFM_ASSERT(false); |
| break; |
| } |
| break; |
| |
| case CMU_LFBPRESC0_REG: |
| switch (clock) { |
| #if defined(_CMU_LFBPRESC0_LEUART0_MASK) |
| case cmuClock_LEUART0: |
| ret = (CMU->LFBPRESC0 & _CMU_LFBPRESC0_LEUART0_MASK) |
| >> _CMU_LFBPRESC0_LEUART0_SHIFT; |
| /* Convert the exponent to a prescaler value. */ |
| ret = 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 a 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 a prescaler value. */ |
| ret = CMU_Log2ToDiv(ret + 4U) - 1U; |
| break; |
| #endif |
| |
| default: |
| ret = 0U; |
| EFM_ASSERT(false); |
| break; |
| } |
| break; |
| |
| case CMU_LFEPRESC0_REG: |
| switch (clock) { |
| #if defined(RTCC_PRESENT) |
| case cmuClock_RTCC: |
| /* No need to compute with LFEPRESC0_RTCC - DIV1 is the only */ |
| /* allowed value. Convert the exponent to a prescaler value. */ |
| ret = _CMU_LFEPRESC0_RTCC_DIV1; |
| break; |
| |
| default: |
| ret = 0U; |
| EFM_ASSERT(false); |
| break; |
| #endif |
| } |
| break; |
| |
| #if defined(_CMU_ADCCTRL_ADC0CLKDIV_MASK) \ |
| || defined(_CMU_ADCCTRL_ADC1CLKDIV_MASK) |
| case CMU_ADCASYNCDIV_REG: |
| switch (clock) { |
| #if defined(_CMU_ADCCTRL_ADC0CLKDIV_MASK) |
| case cmuClock_ADC0ASYNC: |
| ret = (CMU->ADCCTRL & _CMU_ADCCTRL_ADC0CLKDIV_MASK) |
| >> _CMU_ADCCTRL_ADC0CLKDIV_SHIFT; |
| break; |
| #endif |
| #if defined(_CMU_ADCCTRL_ADC1CLKDIV_MASK) |
| case cmuClock_ADC1ASYNC: |
| ret = (CMU->ADCCTRL & _CMU_ADCCTRL_ADC1CLKDIV_MASK) |
| >> _CMU_ADCCTRL_ADC1CLKDIV_SHIFT; |
| break; |
| #endif |
| default: |
| ret = 0U; |
| EFM_ASSERT(false); |
| break; |
| } |
| break; |
| #endif |
| |
| default: |
| ret = 0U; |
| EFM_ASSERT(false); |
| break; |
| } |
| |
| return ret; |
| } |
| #endif |
| |
| #if defined(_SILICON_LABS_32B_SERIES_1) |
| /***************************************************************************//** |
| * @brief |
| * Set the clock prescaler. |
| * |
| * @note |
| * If setting an LF clock prescaler, synchronization into the low-frequency |
| * domain is required. If the same register is modified before a previous |
| * update has completed, this function will stall until the previous |
| * synchronization has completed. See @ref CMU_FreezeEnable() for |
| * a suggestion on how to reduce the stalling time in some use cases. |
| * |
| * @param[in] clock |
| * A clock point to set the prescaler for. Notice that not all clock points |
| * have a prescaler. See the CMU overview in the reference manual. |
| * |
| * @param[in] presc |
| * The clock prescaler. |
| ******************************************************************************/ |
| void CMU_ClockPrescSet(CMU_Clock_TypeDef clock, CMU_ClkPresc_TypeDef presc) |
| { |
| uint32_t freq; |
| uint32_t prescReg; |
| |
| /* Get the divisor reg ID. */ |
| prescReg = ((unsigned)clock >> CMU_PRESC_REG_POS) & CMU_PRESC_REG_MASK; |
| |
| switch (prescReg) { |
| case CMU_HFPRESC_REG: |
| EFM_ASSERT(presc < 32U); |
| |
| /* Configure worst case wait-states for flash and HFLE, set safe HFPER |
| clock-tree prescalers. */ |
| flashWaitStateMax(); |
| setHfLeConfig(CMU_MAX_FREQ_HFLE + 1UL); |
| hfperClkSafePrescaler(); |
| |
| CMU->HFPRESC = (CMU->HFPRESC & ~_CMU_HFPRESC_PRESC_MASK) |
| | (presc << _CMU_HFPRESC_PRESC_SHIFT); |
| |
| /* Update the CMSIS core clock variable (this function updates the global |
| variable). */ |
| freq = SystemCoreClockGet(); |
| /* Optimize flash and HFLE wait states and set optimized HFPER clock-tree |
| prescalers. */ |
| CMU_UpdateWaitStates(freq, (int)VSCALE_DEFAULT); |
| setHfLeConfig(CMU_ClockFreqGet(cmuClock_HFLE)); |
| hfperClkOptimizedPrescaler(); |
| break; |
| |
| case CMU_HFEXPPRESC_REG: |
| EFM_ASSERT(presc < 32U); |
| |
| CMU->HFEXPPRESC = (CMU->HFEXPPRESC & ~_CMU_HFEXPPRESC_PRESC_MASK) |
| | (presc << _CMU_HFEXPPRESC_PRESC_SHIFT); |
| break; |
| |
| case CMU_HFCLKLEPRESC_REG: |
| #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 maximum |
| * 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; |
| |
| #if defined(_CMU_HFPERPRESCB_MASK) |
| case CMU_HFPERPRESCB_REG: |
| EFM_ASSERT(presc < 512U); |
| CMU->HFPERPRESCB = (CMU->HFPERPRESCB & ~_CMU_HFPERPRESCB_PRESC_MASK) |
| | (presc << _CMU_HFPERPRESCB_PRESC_SHIFT); |
| break; |
| #endif |
| |
| #if defined(_CMU_HFPERPRESCC_MASK) |
| case CMU_HFPERPRESCC_REG: |
| EFM_ASSERT(presc < 512U); |
| CMU->HFPERPRESCC = (CMU->HFPERPRESCC & ~_CMU_HFPERPRESCC_PRESC_MASK) |
| | (presc << _CMU_HFPERPRESCC_PRESC_SHIFT); |
| break; |
| #endif |
| |
| case CMU_HFCOREPRESC_REG: |
| EFM_ASSERT(presc < 512U); |
| |
| /* Configure worst case wait-states for flash and HFLE. */ |
| flashWaitStateMax(); |
| setHfLeConfig(CMU_MAX_FREQ_HFLE + 1UL); |
| |
| CMU->HFCOREPRESC = (CMU->HFCOREPRESC & ~_CMU_HFCOREPRESC_PRESC_MASK) |
| | (presc << _CMU_HFCOREPRESC_PRESC_SHIFT); |
| |
| /* Update the CMSIS core clock variable (this function updates the global variable). |
| Optimize flash and HFLE wait states. */ |
| freq = SystemCoreClockGet(); |
| CMU_UpdateWaitStates(freq, (int)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 the prescaler value to a DIV exponent scale. */ |
| presc = CMU_PrescToLog2(presc); |
| |
| /* LF register about to be modified requires sync. Busy check. */ |
| syncReg(CMU_SYNCBUSY_LFAPRESC0); |
| |
| CMU->LFAPRESC0 = (CMU->LFAPRESC0 & ~_CMU_LFAPRESC0_RTC_MASK) |
| | (presc << _CMU_LFAPRESC0_RTC_SHIFT); |
| break; |
| #endif |
| |
| #if defined(RTCC_PRESENT) |
| case cmuClock_RTCC: |
| #if defined(_CMU_LFEPRESC0_RTCC_MASK) |
| #if defined(_CMU_LFEPRESC0_RTCC_DIV4) |
| EFM_ASSERT(presc <= _CMU_LFEPRESC0_RTCC_DIV4); |
| #elif defined(_CMU_LFEPRESC0_RTCC_DIV2) |
| EFM_ASSERT(presc <= _CMU_LFEPRESC0_RTCC_DIV2); |
| #else |
| EFM_ASSERT(presc <= 0U); |
| #endif |
| |
| /* LF register about to be modified requires sync. Busy check. */ |
| syncReg(CMU_SYNCBUSY_LFEPRESC0); |
| |
| CMU->LFEPRESC0 = (CMU->LFEPRESC0 & ~_CMU_LFEPRESC0_RTCC_MASK) |
| | (presc << _CMU_LFEPRESC0_RTCC_SHIFT); |
| #else |
| EFM_ASSERT(presc <= 32768U); |
| |
| /* Convert the prescaler value to a DIV exponent scale. */ |
| presc = CMU_PrescToLog2(presc); |
| |
| /* LF register about to be modified requires sync. Busy check. */ |
| syncReg(CMU_SYNCBUSY_LFAPRESC0); |
| |
| CMU->LFAPRESC0 = (CMU->LFAPRESC0 & ~_CMU_LFAPRESC0_RTCC_MASK) |
| | (presc << _CMU_LFAPRESC0_RTCC_SHIFT); |
| #endif |
| break; |
| #endif |
| |
| #if defined(_CMU_LFAPRESC0_LETIMER0_MASK) |
| case cmuClock_LETIMER0: |
| EFM_ASSERT(presc <= 32768U); |
| |
| /* Convert the prescaler value to a DIV exponent scale. */ |
| presc = CMU_PrescToLog2(presc); |
| |
| /* LF register about to be modified requires sync. Busy check. */ |
| syncReg(CMU_SYNCBUSY_LFAPRESC0); |
| |
| CMU->LFAPRESC0 = (CMU->LFAPRESC0 & ~_CMU_LFAPRESC0_LETIMER0_MASK) |
| | (presc << _CMU_LFAPRESC0_LETIMER0_SHIFT); |
| break; |
| #endif |
| |
| #if defined(_CMU_LFAPRESC0_LESENSE_MASK) |
| case cmuClock_LESENSE: |
| EFM_ASSERT(presc <= 8U); |
| |
| /* Convert the prescaler value to a DIV exponent scale. */ |
| presc = CMU_PrescToLog2(presc); |
| |
| /* LF register about to be modified requires sync. Busy check. */ |
| syncReg(CMU_SYNCBUSY_LFAPRESC0); |
| |
| CMU->LFAPRESC0 = (CMU->LFAPRESC0 & ~_CMU_LFAPRESC0_LESENSE_MASK) |
| | (presc << _CMU_LFAPRESC0_LESENSE_SHIFT); |
| break; |
| #endif |
| |
| #if defined(_CMU_LFAPRESC0_LCD_MASK) |
| case cmuClock_LCDpre: |
| case cmuClock_LCD: |
| EFM_ASSERT(presc <= 32768U); |
| |
| /* Convert the prescaler value to a DIV exponent scale. */ |
| presc = CMU_PrescToLog2(presc); |
| |
| /* LF register about to be modified requires sync. Busy check. */ |
| syncReg(CMU_SYNCBUSY_LFAPRESC0); |
| |
| CMU->LFAPRESC0 = (CMU->LFAPRESC0 & ~_CMU_LFAPRESC0_LCD_MASK) |
| | (presc << _CMU_LFAPRESC0_LCD_SHIFT); |
| break; |
| #endif |
| |
| default: |
| EFM_ASSERT(false); |
| break; |
| } |
| break; |
| |
| case CMU_LFBPRESC0_REG: |
| switch (clock) { |
| #if defined(_CMU_LFBPRESC0_LEUART0_MASK) |
| case cmuClock_LEUART0: |
| EFM_ASSERT(presc <= 8U); |
| |
| /* Convert the prescaler value to a DIV exponent scale. */ |
| presc = CMU_PrescToLog2(presc); |
| |
| /* LF register about to be modified requires sync. Busy check. */ |
| syncReg(CMU_SYNCBUSY_LFBPRESC0); |
| |
| CMU->LFBPRESC0 = (CMU->LFBPRESC0 & ~_CMU_LFBPRESC0_LEUART0_MASK) |
| | (presc << _CMU_LFBPRESC0_LEUART0_SHIFT); |
| break; |
| #endif |
| |
| #if defined(_CMU_LFBPRESC0_LEUART1_MASK) |
| case cmuClock_LEUART1: |
| EFM_ASSERT(presc <= 8U); |
| |
| /* Convert the prescaler value to a DIV exponent scale. */ |
| presc = CMU_PrescToLog2(presc); |
| |
| /* LF register about to be modified requires sync. Busy check. */ |
| syncReg(CMU_SYNCBUSY_LFBPRESC0); |
| |
| CMU->LFBPRESC0 = (CMU->LFBPRESC0 & ~_CMU_LFBPRESC0_LEUART1_MASK) |
| | (presc << _CMU_LFBPRESC0_LEUART1_SHIFT); |
| break; |
| #endif |
| |
| #if defined(_CMU_LFBPRESC0_CSEN_MASK) |
| case cmuClock_CSEN_LF: |
| EFM_ASSERT((presc <= 127U) && (presc >= 15U)); |
| |
| /* Convert the prescaler value to a DIV exponent scale. |
| * DIV16 is the lowest supported prescaler. */ |
| presc = CMU_PrescToLog2(presc) - 4U; |
| |
| /* LF register about to be modified requires sync. Busy check. */ |
| syncReg(CMU_SYNCBUSY_LFBPRESC0); |
| |
| CMU->LFBPRESC0 = (CMU->LFBPRESC0 & ~_CMU_LFBPRESC0_CSEN_MASK) |
| | (presc << _CMU_LFBPRESC0_CSEN_SHIFT); |
| break; |
| #endif |
| |
| default: |
| EFM_ASSERT(false); |
| break; |
| } |
| break; |
| |
| case CMU_LFEPRESC0_REG: |
| switch (clock) { |
| #if defined(_CMU_LFEPRESC0_RTCC_MASK) |
| case cmuClock_RTCC: |
| #if defined(_CMU_LFEPRESC0_RTCC_DIV4) |
| EFM_ASSERT(presc <= _CMU_LFEPRESC0_RTCC_DIV4); |
| #elif defined(_CMU_LFEPRESC0_RTCC_DIV2) |
| EFM_ASSERT(presc <= _CMU_LFEPRESC0_RTCC_DIV2); |
| #else |
| EFM_ASSERT(presc <= 0U); |
| #endif |
| |
| /* LF register about to be modified requires sync. Busy check. */ |
| syncReg(CMU_SYNCBUSY_LFEPRESC0); |
| |
| CMU->LFEPRESC0 = (CMU->LFEPRESC0 & ~_CMU_LFEPRESC0_RTCC_MASK) |
| | (presc << _CMU_LFEPRESC0_RTCC_SHIFT); |
| break; |
| #endif |
| |
| default: |
| EFM_ASSERT(false); |
| break; |
| } |
| break; |
| |
| #if defined(_CMU_ADCCTRL_ADC0CLKDIV_MASK) \ |
| || defined(_CMU_ADCCTRL_ADC1CLKDIV_MASK) |
| case CMU_ADCASYNCDIV_REG: |
| switch (clock) { |
| #if defined(_CMU_ADCCTRL_ADC0CLKDIV_MASK) |
| case cmuClock_ADC0ASYNC: |
| EFM_ASSERT(presc <= 3); |
| CMU->ADCCTRL = (CMU->ADCCTRL & ~_CMU_ADCCTRL_ADC0CLKDIV_MASK) |
| | (presc << _CMU_ADCCTRL_ADC0CLKDIV_SHIFT); |
| break; |
| #endif |
| |
| #if defined(_CMU_ADCCTRL_ADC1CLKDIV_MASK) |
| case cmuClock_ADC1ASYNC: |
| EFM_ASSERT(presc <= 3); |
| CMU->ADCCTRL = (CMU->ADCCTRL & ~_CMU_ADCCTRL_ADC1CLKDIV_MASK) |
| | (presc << _CMU_ADCCTRL_ADC1CLKDIV_SHIFT); |
| break; |
| #endif |
| default: |
| EFM_ASSERT(false); |
| break; |
| } |
| break; |
| #endif |
| |
| default: |
| EFM_ASSERT(false); |
| break; |
| } |
| } |
| #endif |
| |
| /***************************************************************************//** |
| * @brief |
| * Get the currently selected reference clock used for a clock branch. |
| * |
| * @param[in] clock |
| * Clock branch to fetch selected ref. clock for. One of: |
| * @li #cmuClock_HF |
| * @li #cmuClock_LFA |
| * @li #cmuClock_LFB @if _CMU_LFCLKSEL_LFAE_ULFRCO |
| * @li #cmuClock_LFC |
| * @endif @if _SILICON_LABS_32B_SERIES_1 |
| * @li #cmuClock_LFE |
| * @endif |
| * @li #cmuClock_DBG @if DOXYDOC_USB_PRESENT |
| * @li #cmuClock_USBC |
| * @endif |
| * |
| * @return |
| * The reference clock used for clocking the selected branch, #cmuSelect_Error if |
| * invalid @p clock provided. |
| ******************************************************************************/ |
| CMU_Select_TypeDef CMU_ClockSelectGet(CMU_Clock_TypeDef clock) |
| { |
| CMU_Select_TypeDef ret = cmuSelect_Disabled; |
| uint32_t selReg; |
| |
| selReg = ((unsigned)clock >> CMU_SEL_REG_POS) & CMU_SEL_REG_MASK; |
| |
| switch (selReg) { |
| case CMU_HFCLKSEL_REG: |
| #if defined(_CMU_HFCLKSTATUS_MASK) |
| switch (CMU->HFCLKSTATUS & _CMU_HFCLKSTATUS_SELECTED_MASK) { |
| case CMU_HFCLKSTATUS_SELECTED_LFXO: |
| ret = cmuSelect_LFXO; |
| break; |
| |
| case CMU_HFCLKSTATUS_SELECTED_LFRCO: |
| ret = cmuSelect_LFRCO; |
| break; |
| |
| case CMU_HFCLKSTATUS_SELECTED_HFXO: |
| ret = cmuSelect_HFXO; |
| break; |
| |
| #if defined(CMU_HFCLKSTATUS_SELECTED_HFRCODIV2) |
| case CMU_HFCLKSTATUS_SELECTED_HFRCODIV2: |
| ret = cmuSelect_HFRCODIV2; |
| break; |
| #endif |
| |
| #if defined(CMU_HFCLKSTATUS_SELECTED_CLKIN0) |
| case CMU_HFCLKSTATUS_SELECTED_CLKIN0: |
| ret = cmuSelect_CLKIN0; |
| break; |
| #endif |
| |
| #if defined(CMU_HFCLKSTATUS_SELECTED_USHFRCO) |
| case CMU_HFCLKSTATUS_SELECTED_USHFRCO: |
| ret = cmuSelect_USHFRCO; |
| break; |
| #endif |
| |
| default: |
| ret = cmuSelect_HFRCO; |
| break; |
| } |
| #else |
| switch (CMU->STATUS |
| & (CMU_STATUS_HFRCOSEL |
| | CMU_STATUS_HFXOSEL |
| | CMU_STATUS_LFRCOSEL |
| #if defined(CMU_STATUS_USHFRCODIV2SEL) |
| | CMU_STATUS_USHFRCODIV2SEL |
| #endif |
| | CMU_STATUS_LFXOSEL)) { |
| case CMU_STATUS_LFXOSEL: |
| ret = cmuSelect_LFXO; |
| break; |
| |
| case CMU_STATUS_LFRCOSEL: |
| ret = cmuSelect_LFRCO; |
| break; |
| |
| case CMU_STATUS_HFXOSEL: |
| ret = cmuSelect_HFXO; |
| break; |
| |
| #if defined(CMU_STATUS_USHFRCODIV2SEL) |
| case CMU_STATUS_USHFRCODIV2SEL: |
| ret = cmuSelect_USHFRCODIV2; |
| break; |
| #endif |
| |
| default: |
| ret = cmuSelect_HFRCO; |
| break; |
| } |
| #endif |
| break; |
| |
| #if defined(_CMU_LFCLKSEL_MASK) || defined(_CMU_LFACLKSEL_MASK) |
| case CMU_LFACLKSEL_REG: |
| #if defined(_CMU_LFCLKSEL_MASK) |
| switch (CMU->LFCLKSEL & _CMU_LFCLKSEL_LFA_MASK) { |
| case CMU_LFCLKSEL_LFA_LFRCO: |
| ret = cmuSelect_LFRCO; |
| break; |
| |
| case CMU_LFCLKSEL_LFA_LFXO: |
| ret = cmuSelect_LFXO; |
| break; |
| |
| #if defined(CMU_LFCLKSEL_LFA_HFCORECLKLEDIV2) |
| case CMU_LFCLKSEL_LFA_HFCORECLKLEDIV2: |
| ret = cmuSelect_HFCLKLE; |
| break; |
| #endif |
| |
| default: |
| #if defined(CMU_LFCLKSEL_LFAE) |
| if (CMU->LFCLKSEL & _CMU_LFCLKSEL_LFAE_MASK) { |
| ret = cmuSelect_ULFRCO; |
| break; |
| } |
| #else |
| ret = cmuSelect_Disabled; |
| #endif |
| break; |
| } |
| |
| #elif defined(_CMU_LFACLKSEL_MASK) |
| switch (CMU->LFACLKSEL & _CMU_LFACLKSEL_LFA_MASK) { |
| case CMU_LFACLKSEL_LFA_LFRCO: |
| ret = cmuSelect_LFRCO; |
| break; |
| |
| case CMU_LFACLKSEL_LFA_LFXO: |
| ret = cmuSelect_LFXO; |
| break; |
| |
| case CMU_LFACLKSEL_LFA_ULFRCO: |
| ret = cmuSelect_ULFRCO; |
| break; |
| |
| #if defined(_CMU_LFACLKSEL_LFA_HFCLKLE) |
| case CMU_LFACLKSEL_LFA_HFCLKLE: |
| ret = cmuSelect_HFCLKLE; |
| 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; |
| |
| 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 |
| |
| default: |
| ret = cmuSelect_Disabled; |
| break; |
| } |
| break; |
| #endif /* CMU_LFECLKSEL_REG */ |
| |
| #if defined(_CMU_LFCCLKSEL_LFC_MASK) |
| case CMU_LFCCLKSEL_REG: |
| switch (CMU->LFCCLKSEL & _CMU_LFCCLKSEL_LFC_MASK) { |
| case CMU_LFCCLKSEL_LFC_LFRCO: |
| ret = cmuSelect_LFRCO; |
| break; |
| |
| case CMU_LFCCLKSEL_LFC_LFXO: |
| ret = cmuSelect_LFXO; |
| break; |
| |
| case CMU_LFCCLKSEL_LFC_ULFRCO: |
| ret = cmuSelect_ULFRCO; |
| break; |
| |
| default: |
| ret = cmuSelect_Disabled; |
| break; |
| } |
| break; |
| #endif /* CMU_LFCCLKSEL_REG */ |
| |
| case CMU_DBGCLKSEL_REG: |
| #if defined(_CMU_DBGCLKSEL_DBG_MASK) |
| switch (CMU->DBGCLKSEL & _CMU_DBGCLKSEL_DBG_MASK) { |
| case CMU_DBGCLKSEL_DBG_HFCLK: |
| ret = cmuSelect_HFCLK; |
| break; |
| |
| case CMU_DBGCLKSEL_DBG_AUXHFRCO: |
| ret = cmuSelect_AUXHFRCO; |
| break; |
| |
| default: |
| ret = cmuSelect_Disabled; |
| break; |
| } |
| |
| #elif defined(_CMU_CTRL_DBGCLK_MASK) |
| switch (CMU->CTRL & _CMU_CTRL_DBGCLK_MASK) { |
| case CMU_CTRL_DBGCLK_AUXHFRCO: |
| ret = cmuSelect_AUXHFRCO; |
| break; |
| |
| case CMU_CTRL_DBGCLK_HFCLK: |
| ret = cmuSelect_HFCLK; |
| break; |
| } |
| #else |
| ret = cmuSelect_AUXHFRCO; |
| #endif |
| break; |
| |
| #if defined(USBC_CLOCK_PRESENT) |
| case CMU_USBCCLKSEL_REG: |
| switch (CMU->STATUS |
| & (CMU_STATUS_USBCLFXOSEL |
| #if defined(_CMU_STATUS_USBCHFCLKSEL_MASK) |
| | CMU_STATUS_USBCHFCLKSEL |
| #endif |
| #if defined(_CMU_STATUS_USBCUSHFRCOSEL_MASK) |
| | CMU_STATUS_USBCUSHFRCOSEL |
| #endif |
| | CMU_STATUS_USBCLFRCOSEL)) { |
| #if defined(_CMU_STATUS_USBCHFCLKSEL_MASK) |
| case CMU_STATUS_USBCHFCLKSEL: |
| ret = cmuSelect_HFCLK; |
| break; |
| #endif |
| |
| #if defined(_CMU_STATUS_USBCUSHFRCOSEL_MASK) |
| case CMU_STATUS_USBCUSHFRCOSEL: |
| ret = cmuSelect_USHFRCO; |
| break; |
| #endif |
| |
| case CMU_STATUS_USBCLFXOSEL: |
| ret = cmuSelect_LFXO; |
| break; |
| |
| case CMU_STATUS_USBCLFRCOSEL: |
| ret = cmuSelect_LFRCO; |
| break; |
| |
| default: |
| ret = cmuSelect_Disabled; |
| break; |
| } |
| break; |
| #endif |
| |
| #if defined(_CMU_ADCCTRL_ADC0CLKSEL_MASK) |
| case CMU_ADC0ASYNCSEL_REG: |
| switch (CMU->ADCCTRL & _CMU_ADCCTRL_ADC0CLKSEL_MASK) { |
| case CMU_ADCCTRL_ADC0CLKSEL_DISABLED: |
| ret = cmuSelect_Disabled; |
| break; |
| |
| case CMU_ADCCTRL_ADC0CLKSEL_AUXHFRCO: |
| ret = cmuSelect_AUXHFRCO; |
| break; |
| |
| case CMU_ADCCTRL_ADC0CLKSEL_HFXO: |
| ret = cmuSelect_HFXO; |
| break; |
| |
| case CMU_ADCCTRL_ADC0CLKSEL_HFSRCCLK: |
| ret = cmuSelect_HFSRCCLK; |
| break; |
| |
| default: |
| ret = cmuSelect_Disabled; |
| break; |
| } |
| break; |
| #endif |
| |
| #if defined(_CMU_ADCCTRL_ADC1CLKSEL_MASK) |
| case CMU_ADC1ASYNCSEL_REG: |
| switch (CMU->ADCCTRL & _CMU_ADCCTRL_ADC1CLKSEL_MASK) { |
| case CMU_ADCCTRL_ADC1CLKSEL_DISABLED: |
| ret = cmuSelect_Disabled; |
| break; |
| |
| case CMU_ADCCTRL_ADC1CLKSEL_AUXHFRCO: |
| ret = cmuSelect_AUXHFRCO; |
| break; |
| |
| case CMU_ADCCTRL_ADC1CLKSEL_HFXO: |
| ret = cmuSelect_HFXO; |
| break; |
| |
| case CMU_ADCCTRL_ADC1CLKSEL_HFSRCCLK: |
| ret = cmuSelect_HFSRCCLK; |
| break; |
| } |
| break; |
| #endif |
| |
| #if defined(_CMU_SDIOCTRL_SDIOCLKSEL_MASK) |
| case CMU_SDIOREFSEL_REG: |
| switch (CMU->SDIOCTRL & _CMU_SDIOCTRL_SDIOCLKSEL_MASK) { |
| case CMU_SDIOCTRL_SDIOCLKSEL_HFRCO: |
| ret = cmuSelect_HFRCO; |
| break; |
| |
| case CMU_SDIOCTRL_SDIOCLKSEL_HFXO: |
| ret = cmuSelect_HFXO; |
| break; |
| |
| case CMU_SDIOCTRL_SDIOCLKSEL_AUXHFRCO: |
| ret = cmuSelect_AUXHFRCO; |
| break; |
| |
| case CMU_SDIOCTRL_SDIOCLKSEL_USHFRCO: |
| ret = cmuSelect_USHFRCO; |
| break; |
| } |
| break; |
| #endif |
| |
| #if defined(_CMU_QSPICTRL_QSPI0CLKSEL_MASK) |
| case CMU_QSPI0REFSEL_REG: |
| switch (CMU->QSPICTRL & _CMU_QSPICTRL_QSPI0CLKSEL_MASK) { |
| case CMU_QSPICTRL_QSPI0CLKSEL_HFRCO: |
| ret = cmuSelect_HFRCO; |
| break; |
| |
| case CMU_QSPICTRL_QSPI0CLKSEL_HFXO: |
| ret = cmuSelect_HFXO; |
| break; |
| |
| case CMU_QSPICTRL_QSPI0CLKSEL_AUXHFRCO: |
| ret = cmuSelect_AUXHFRCO; |
| break; |
| |
| case CMU_QSPICTRL_QSPI0CLKSEL_USHFRCO: |
| ret = cmuSelect_USHFRCO; |
| break; |
| } |
| break; |
| #endif |
| |
| #if defined(_CMU_USBCTRL_USBCLKSEL_MASK) |
| case CMU_USBRCLKSEL_REG: |
| switch (CMU->USBCTRL & _CMU_USBCTRL_USBCLKSEL_MASK) { |
| case CMU_USBCTRL_USBCLKSEL_USHFRCO: |
| ret = cmuSelect_USHFRCO; |
| break; |
| |
| case CMU_USBCTRL_USBCLKSEL_HFXO: |
| ret = cmuSelect_HFXO; |
| break; |
| |
| case CMU_USBCTRL_USBCLKSEL_HFXOX2: |
| ret = cmuSelect_HFXOX2; |
| break; |
| |
| case CMU_USBCTRL_USBCLKSEL_HFRCO: |
| ret = cmuSelect_HFRCO; |
| break; |
| |
| case CMU_USBCTRL_USBCLKSEL_LFXO: |
| ret = cmuSelect_LFXO; |
| break; |
| |
| case CMU_USBCTRL_USBCLKSEL_LFRCO: |
| ret = cmuSelect_LFRCO; |
| break; |
| } |
| break; |
| #endif |
| |
| default: |
| ret = cmuSelect_Error; |
| EFM_ASSERT(false); |
| break; |
| } |
| |
| return ret; |
| } |
| |
| /***************************************************************************//** |
| * @brief |
| * Select the reference clock/oscillator used for a clock branch. |
| * |
| * @details |
| * Notice that if a selected reference is not enabled prior to selecting its |
| * use, it will be enabled and this function will wait for the selected |
| * oscillator to be stable. It will however NOT be disabled if another |
| * reference clock is selected later. |
| * |
| * This feature is particularly important if selecting a new reference |
| * clock for the clock branch clocking the core. Otherwise, the system |
| * may halt. |
| * |
| * @param[in] clock |
| * A clock branch to select reference clock for. One of: |
| * @li #cmuClock_HF |
| * @li #cmuClock_LFA |
| * @li #cmuClock_LFB |
| * @if _CMU_LFCCLKEN0_MASK |
| * @li #cmuClock_LFC |
| * @endif |
| * @if _CMU_LFECLKEN0_MASK |
| * @li #cmuClock_LFE |
| * @endif |
| * @li #cmuClock_DBG |
| * @if _CMU_CMD_USBCLKSEL_MASK |
| * @li #cmuClock_USBC |
| * @endif |
| * @if _CMU_USBCTRL_MASK |
| * @li #cmuClock_USBR |
| * @endif |
| * |
| * @param[in] ref |
| * A reference selected for clocking. See the reference manual |
| * for details about references available for a specific clock branch. |
| * @li #cmuSelect_HFRCO |
| * @li #cmuSelect_LFRCO |
| * @li #cmuSelect_HFXO |
| * @if _CMU_HFXOCTRL_HFXOX2EN_MASK |
| * @li #cmuSelect_HFXOX2 |
| * @endif |
| * @li #cmuSelect_LFXO |
| * @li #cmuSelect_HFCLKLE |
| * @li #cmuSelect_AUXHFRCO |
| * @if _CMU_USHFRCOCTRL_MASK |
| * @li #cmuSelect_USHFRCO |
| * @endif |
| * @li #cmuSelect_HFCLK |
| * @ifnot DOXYDOC_EFM32_GECKO_FAMILY |
| * @li #cmuSelect_ULFRCO |
| * @endif |
| ******************************************************************************/ |
| void CMU_ClockSelectSet(CMU_Clock_TypeDef clock, CMU_Select_TypeDef ref) |
| { |
| uint32_t select = (uint32_t)cmuOsc_HFRCO; |
| CMU_Osc_TypeDef osc = cmuOsc_HFRCO; |
| uint32_t freq; |
| uint32_t tmp; |
| uint32_t selRegId; |
| #if defined(_SILICON_LABS_32B_SERIES_1) |
| volatile uint32_t *selReg = NULL; |
| #endif |
| #if defined(CMU_LFCLKSEL_LFAE_ULFRCO) |
| uint32_t lfExtended = 0; |
| #endif |
| |
| #if defined(_EMU_CMD_EM01VSCALE0_MASK) |
| uint32_t vScaleFrequency = 0; /* Use default. */ |
| |
| /* Start voltage upscaling before the clock is set. */ |
| if (clock == cmuClock_HF) { |
| if (ref == cmuSelect_HFXO) { |
| vScaleFrequency = SystemHFXOClockGet(); |
| } else if ((ref == cmuSelect_HFRCO) |
| && ((uint32_t)CMU_HFRCOBandGet() |
| > CMU_VSCALEEM01_LOWPOWER_VOLTAGE_CLOCK_MAX)) { |
| vScaleFrequency = (uint32_t)CMU_HFRCOBandGet(); |
| } else { |
| /* Use the default frequency. */ |
| } |
| if (vScaleFrequency != 0UL) { |
| EMU_VScaleEM01ByClock(vScaleFrequency, false); |
| } |
| } |
| #endif |
| |
| selRegId = ((unsigned)clock >> CMU_SEL_REG_POS) & CMU_SEL_REG_MASK; |
| |
| switch (selRegId) { |
| case CMU_HFCLKSEL_REG: |
| switch (ref) { |
| case cmuSelect_LFXO: |
| #if defined(_SILICON_LABS_32B_SERIES_1) |
| select = CMU_HFCLKSEL_HF_LFXO; |
| #elif defined(_SILICON_LABS_32B_SERIES_0) |
| select = CMU_CMD_HFCLKSEL_LFXO; |
| #endif |
| osc = cmuOsc_LFXO; |
| break; |
| |
| case cmuSelect_LFRCO: |
| #if defined(_SILICON_LABS_32B_SERIES_1) |
| select = CMU_HFCLKSEL_HF_LFRCO; |
| #elif defined(_SILICON_LABS_32B_SERIES_0) |
| select = CMU_CMD_HFCLKSEL_LFRCO; |
| #endif |
| osc = cmuOsc_LFRCO; |
| break; |
| |
| case cmuSelect_HFXO: |
| #if defined(CMU_HFCLKSEL_HF_HFXO) |
| select = CMU_HFCLKSEL_HF_HFXO; |
| #elif defined(CMU_CMD_HFCLKSEL_HFXO) |
| select = CMU_CMD_HFCLKSEL_HFXO; |
| #endif |
| osc = cmuOsc_HFXO; |
| #if defined(CMU_MAX_FREQ_HFLE) |
| /* Set 1 HFLE wait-state until the new HFCLKLE frequency is known. |
| This is known after 'select' is written below. */ |
| setHfLeConfig(CMU_MAX_FREQ_HFLE + 1UL); |
| #endif |
| #if defined(CMU_CTRL_HFXOBUFCUR_BOOSTABOVE32MHZ) |
| /* Adjust HFXO buffer current for frequencies above 32 MHz. */ |
| if (SystemHFXOClockGet() > 32000000) { |
| CMU->CTRL = (CMU->CTRL & ~_CMU_CTRL_HFXOBUFCUR_MASK) |
| | CMU_CTRL_HFXOBUFCUR_BOOSTABOVE32MHZ; |
| } else { |
| CMU->CTRL = (CMU->CTRL & ~_CMU_CTRL_HFXOBUFCUR_MASK) |
| | CMU_CTRL_HFXOBUFCUR_BOOSTUPTO32MHZ; |
| } |
| #endif |
| break; |
| |
| case cmuSelect_HFRCO: |
| #if defined(_SILICON_LABS_32B_SERIES_1) |
| select = CMU_HFCLKSEL_HF_HFRCO; |
| #elif defined(_SILICON_LABS_32B_SERIES_0) |
| select = CMU_CMD_HFCLKSEL_HFRCO; |
| #endif |
| osc = cmuOsc_HFRCO; |
| #if defined(CMU_MAX_FREQ_HFLE) |
| /* Set 1 HFLE wait-state until the new HFCLKLE frequency is known. |
| This is known after 'select' is written below. */ |
| setHfLeConfig(CMU_MAX_FREQ_HFLE + 1UL); |
| #endif |
| break; |
| |
| #if defined(CMU_CMD_HFCLKSEL_USHFRCODIV2) |
| case cmuSelect_USHFRCODIV2: |
| select = CMU_CMD_HFCLKSEL_USHFRCODIV2; |
| osc = cmuOsc_USHFRCO; |
| break; |
| #endif |
| |
| #if defined(CMU_HFCLKSTATUS_SELECTED_HFRCODIV2) |
| case cmuSelect_HFRCODIV2: |
| select = CMU_HFCLKSEL_HF_HFRCODIV2; |
| osc = cmuOsc_HFRCO; |
| break; |
| #endif |
| |
| #if defined(CMU_HFCLKSTATUS_SELECTED_CLKIN0) |
| case cmuSelect_CLKIN0: |
| select = CMU_HFCLKSEL_HF_CLKIN0; |
| osc = cmuOsc_CLKIN0; |
| break; |
| #endif |
| |
| #if defined(CMU_HFCLKSTATUS_SELECTED_USHFRCO) |
| case cmuSelect_USHFRCO: |
| select = CMU_HFCLKSEL_HF_USHFRCO; |
| osc = cmuOsc_USHFRCO; |
| break; |
| #endif |
| |
| #if defined(CMU_LFCLKSEL_LFAE_ULFRCO) || defined(CMU_LFACLKSEL_LFA_ULFRCO) |
| case cmuSelect_ULFRCO: |
| /* ULFRCO cannot be used as HFCLK. */ |
| EFM_ASSERT(false); |
| return; |
| #endif |
| |
| default: |
| EFM_ASSERT(false); |
| return; |
| } |
| |
| /* Ensure that the selected oscillator is enabled, waiting for it to stabilize. */ |
| #if defined(CMU_HFCLKSTATUS_SELECTED_CLKIN0) |
| if (osc != cmuOsc_CLKIN0) { |
| CMU_OscillatorEnable(osc, true, true); |
| } |
| #else |
| CMU_OscillatorEnable(osc, true, true); |
| #endif |
| |
| /* Configure worst case wait-states for flash and set safe HFPER |
| clock-tree prescalers. */ |
| flashWaitStateMax(); |
| hfperClkSafePrescaler(); |
| |
| #if defined(_EMU_CMD_EM01VSCALE0_MASK) |
| /* Wait for voltage upscaling to complete before the clock is set. */ |
| if (vScaleFrequency != 0UL) { |
| EMU_VScaleWait(); |
| } |
| #endif |
| |
| /* Switch to the selected oscillator. */ |
| #if defined(_CMU_HFCLKSEL_MASK) |
| CMU->HFCLKSEL = select; |
| #else |
| CMU->CMD = select; |
| #endif |
| #if defined(CMU_MAX_FREQ_HFLE) |
| /* Update the HFLE configuration after 'select' is set. |
| Note that the HFCLKLE clock is connected differently on platforms 1 and 2. */ |
| setHfLeConfig(CMU_ClockFreqGet(cmuClock_HFLE)); |
| #endif |
| |
| /* Update the CMSIS core clock variable. */ |
| /* (The function will update the global variable). */ |
| freq = SystemCoreClockGet(); |
| |
| /* Optimize flash access wait state setting for the currently selected core clk. */ |
| CMU_UpdateWaitStates(freq, (int)VSCALE_DEFAULT); |
| |
| #if defined(_EMU_CMD_EM01VSCALE0_MASK) |
| /* Keep EMU module informed on the source HF clock frequency. This will apply voltage |
| downscaling after clock is set if downscaling is configured. */ |
| if (vScaleFrequency == 0UL) { |
| EMU_VScaleEM01ByClock(0, true); |
| } |
| #endif |
| /* Set optimized HFPER clock-tree prescalers. */ |
| hfperClkOptimizedPrescaler(); |
| break; |
| |
| #if defined(_SILICON_LABS_32B_SERIES_1) |
| case CMU_LFACLKSEL_REG: |
| selReg = (selReg == NULL) ? &CMU->LFACLKSEL : selReg; |
| #if !defined(_CMU_LFACLKSEL_LFA_HFCLKLE) |
| /* HFCLKCLE can't be used as LFACLK. */ |
| EFM_ASSERT(ref != cmuSelect_HFCLKLE); |
| #endif |
| SL_FALLTHROUGH |
| /* Fall through and select the 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't be used as LFCCLK. */ |
| EFM_ASSERT(ref != cmuSelect_HFCLKLE); |
| #endif |
| SL_FALLTHROUGH |
| #endif |
| /* Fall through and select the clock source. */ |
| |
| case CMU_LFECLKSEL_REG: |
| selReg = (selReg == NULL) ? &CMU->LFECLKSEL : selReg; |
| #if !defined(_CMU_LFECLKSEL_LFE_HFCLKLE) |
| /* HFCLKCLE can't be used as LFECLK. */ |
| EFM_ASSERT(ref != cmuSelect_HFCLKLE); |
| #endif |
| SL_FALLTHROUGH |
| /* Fall through and select the clock source. */ |
| |
| case CMU_LFBCLKSEL_REG: |
| selReg = (selReg == NULL) ? &CMU->LFBCLKSEL : selReg; |
| switch (ref) { |
| case cmuSelect_Disabled: |
| tmp = _CMU_LFACLKSEL_LFA_DISABLED; |
| break; |
| |
| case cmuSelect_LFXO: |
| /* Ensure that thes elected oscillator is enabled, waiting for it to stabilize. */ |
| CMU_OscillatorEnable(cmuOsc_LFXO, true, true); |
| tmp = _CMU_LFACLKSEL_LFA_LFXO; |
| break; |
| |
| case cmuSelect_LFRCO: |
| /* Ensure that the selected oscillator is enabled, waiting for it to stabilize. */ |
| CMU_OscillatorEnable(cmuOsc_LFRCO, true, true); |
| tmp = _CMU_LFACLKSEL_LFA_LFRCO; |
| break; |
| |
| case cmuSelect_HFCLKLE: |
| /* Ensure the correct HFLE wait-states and enable HFCLK to LE.*/ |
| setHfLeConfig(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; |
| |
| default: |
| EFM_ASSERT(false); |
| return; |
| } |
| *selReg = tmp; |
| break; |
| |
| #elif defined(_SILICON_LABS_32B_SERIES_0) |
| case CMU_LFACLKSEL_REG: |
| case CMU_LFBCLKSEL_REG: |
| switch (ref) { |
| case cmuSelect_Disabled: |
| tmp = _CMU_LFCLKSEL_LFA_DISABLED; |
| break; |
| |
| case cmuSelect_LFXO: |
| /* Ensure that the selected oscillator is enabled, waiting for it to stabilize. */ |
| CMU_OscillatorEnable(cmuOsc_LFXO, true, true); |
| tmp = _CMU_LFCLKSEL_LFA_LFXO; |
| break; |
| |
| case cmuSelect_LFRCO: |
| /* Ensure that the selected oscillator is enabled, waiting for it to stabilize. */ |
| CMU_OscillatorEnable(cmuOsc_LFRCO, true, true); |
| tmp = _CMU_LFCLKSEL_LFA_LFRCO; |
| break; |
| |
| case cmuSelect_HFCLKLE: |
| #if defined(CMU_MAX_FREQ_HFLE) |
| /* Set the HFLE wait-state and divider. */ |
| freq = SystemCoreClockGet(); |
| setHfLeConfig(freq); |
| #endif |
| /* Ensure HFCORE to LE clocking is enabled. */ |
| BUS_RegBitWrite(&CMU->HFCORECLKEN0, _CMU_HFCORECLKEN0_LE_SHIFT, 1); |
| tmp = _CMU_LFCLKSEL_LFA_HFCORECLKLEDIV2; |
| break; |
| |
| #if defined(CMU_LFCLKSEL_LFAE_ULFRCO) |
| case cmuSelect_ULFRCO: |
| /* ULFRCO is always enabled. */ |
| tmp = _CMU_LFCLKSEL_LFA_DISABLED; |
| lfExtended = 1; |
| break; |
| #endif |
| |
| default: |
| /* An illegal clock source for LFA/LFB selected. */ |
| EFM_ASSERT(false); |
| return; |
| } |
| |
| /* Apply select. */ |
| if (selRegId == CMU_LFACLKSEL_REG) { |
| #if defined(_CMU_LFCLKSEL_LFAE_MASK) |
| CMU->LFCLKSEL = (CMU->LFCLKSEL |
| & ~(_CMU_LFCLKSEL_LFA_MASK | _CMU_LFCLKSEL_LFAE_MASK)) |
| | (tmp << _CMU_LFCLKSEL_LFA_SHIFT) |
| | (lfExtended << _CMU_LFCLKSEL_LFAE_SHIFT); |
| #else |
| CMU->LFCLKSEL = (CMU->LFCLKSEL & ~_CMU_LFCLKSEL_LFA_MASK) |
| | (tmp << _CMU_LFCLKSEL_LFA_SHIFT); |
| #endif |
| } else { |
| #if defined(_CMU_LFCLKSEL_LFBE_MASK) |
| CMU->LFCLKSEL = (CMU->LFCLKSEL |
| & ~(_CMU_LFCLKSEL_LFB_MASK | _CMU_LFCLKSEL_LFBE_MASK)) |
| | (tmp << _CMU_LFCLKSEL_LFB_SHIFT) |
| | (lfExtended << _CMU_LFCLKSEL_LFBE_SHIFT); |
| #else |
| CMU->LFCLKSEL = (CMU->LFCLKSEL & ~_CMU_LFCLKSEL_LFB_MASK) |
| | (tmp << _CMU_LFCLKSEL_LFB_SHIFT); |
| #endif |
| } |
| break; |
| |
| #if defined(_CMU_LFCLKSEL_LFC_MASK) |
| case CMU_LFCCLKSEL_REG: |
| switch (ref) { |
| case cmuSelect_Disabled: |
| tmp = _CMU_LFCLKSEL_LFA_DISABLED; |
| break; |
| |
| case cmuSelect_LFXO: |
| /* Ensure that the selected oscillator is enabled, waiting for it to stabilize. */ |
| CMU_OscillatorEnable(cmuOsc_LFXO, true, true); |
| tmp = _CMU_LFCLKSEL_LFC_LFXO; |
| break; |
| |
| case cmuSelect_LFRCO: |
| /* Ensure that the selected oscillator is enabled, waiting for it to stabilize. */ |
| CMU_OscillatorEnable(cmuOsc_LFRCO, true, true); |
| tmp = _CMU_LFCLKSEL_LFC_LFRCO; |
| break; |
| |
| default: |
| /* An illegal clock source for LFC selected. */ |
| EFM_ASSERT(false); |
| return; |
| } |
| |
| /* Apply select. */ |
| CMU->LFCLKSEL = (CMU->LFCLKSEL & ~_CMU_LFCLKSEL_LFC_MASK) |
| | (tmp << _CMU_LFCLKSEL_LFC_SHIFT); |
| break; |
| #endif |
| #endif |
| |
| #if defined(_CMU_DBGCLKSEL_DBG_MASK) || defined(CMU_CTRL_DBGCLK) |
| case CMU_DBGCLKSEL_REG: |
| switch (ref) { |
| #if defined(_CMU_DBGCLKSEL_DBG_MASK) |
| case cmuSelect_AUXHFRCO: |
| /* Select AUXHFRCO as a debug clock. */ |
| CMU->DBGCLKSEL = CMU_DBGCLKSEL_DBG_AUXHFRCO; |
| break; |
| |
| case cmuSelect_HFCLK: |
| /* Select divided HFCLK as a debug clock. */ |
| CMU->DBGCLKSEL = CMU_DBGCLKSEL_DBG_HFCLK; |
| break; |
| #endif |
| |
| #if defined(CMU_CTRL_DBGCLK) |
| case cmuSelect_AUXHFRCO: |
| /* Select AUXHFRCO as a debug clock. */ |
| CMU->CTRL = (CMU->CTRL & ~(_CMU_CTRL_DBGCLK_MASK)) |
| | CMU_CTRL_DBGCLK_AUXHFRCO; |
| break; |
| |
| case cmuSelect_HFCLK: |
| /* Select divided HFCLK as a debug clock. */ |
| CMU->CTRL = (CMU->CTRL & ~(_CMU_CTRL_DBGCLK_MASK)) |
| | CMU_CTRL_DBGCLK_HFCLK; |
| break; |
| #endif |
| |
| default: |
| /* An illegal clock source for debug selected. */ |
| EFM_ASSERT(false); |
| return; |
| } |
| break; |
| #endif |
| |
| #if defined(USBC_CLOCK_PRESENT) |
| case CMU_USBCCLKSEL_REG: |
| switch (ref) { |
| case cmuSelect_LFXO: |
| /* Select LFXO as a clock source for USB. It can only be used in sleep mode. */ |
| /* Ensure that the selected oscillator is enabled, waiting for it to stabilize. */ |
| CMU_OscillatorEnable(cmuOsc_LFXO, true, true); |
| |
| /* Switch the oscillator. */ |
| CMU->CMD = CMU_CMD_USBCCLKSEL_LFXO; |
| |
| /* Wait until the clock is activated. */ |
| while ((CMU->STATUS & CMU_STATUS_USBCLFXOSEL) == 0) { |
| } |
| break; |
| |
| case cmuSelect_LFRCO: |
| /* Select LFRCO as a clock source for USB. It can only be used in sleep mode. */ |
| /* Ensure that the selected oscillator is enabled, waiting for it to stabilize. */ |
| CMU_OscillatorEnable(cmuOsc_LFRCO, true, true); |
| |
| /* Switch the oscillator. */ |
| CMU->CMD = CMU_CMD_USBCCLKSEL_LFRCO; |
| |
| /* Wait until the clock is activated. */ |
| while ((CMU->STATUS & CMU_STATUS_USBCLFRCOSEL) == 0) { |
| } |
| break; |
| |
| #if defined(CMU_STATUS_USBCHFCLKSEL) |
| case cmuSelect_HFCLK: |
| /* Select undivided HFCLK as a clock source for USB. */ |
| /* The oscillator must already be enabled to avoid a core lockup. */ |
| CMU->CMD = CMU_CMD_USBCCLKSEL_HFCLKNODIV; |
| /* Wait until the clock is activated. */ |
| while ((CMU->STATUS & CMU_STATUS_USBCHFCLKSEL) == 0) { |
| } |
| break; |
| #endif |
| |
| #if defined(CMU_CMD_USBCCLKSEL_USHFRCO) |
| case cmuSelect_USHFRCO: |
| /* Select USHFRCO as a clock source for USB. */ |
| /* Ensure that the selected oscillator is enabled, waiting for it to stabilize. */ |
| CMU_OscillatorEnable(cmuOsc_USHFRCO, true, true); |
| |
| /* Switch the oscillator. */ |
| CMU->CMD = CMU_CMD_USBCCLKSEL_USHFRCO; |
| |
| /* Wait until the clock is activated. */ |
| while ((CMU->STATUS & CMU_STATUS_USBCUSHFRCOSEL) == 0) { |
| } |
| break; |
| #endif |
| |
| default: |
| /* An illegal clock source for USB. */ |
| EFM_ASSERT(false); |
| return; |
| } |
| break; |
| #endif |
| |
| #if defined(_CMU_ADCCTRL_ADC0CLKSEL_MASK) |
| case CMU_ADC0ASYNCSEL_REG: |
| switch (ref) { |
| case cmuSelect_Disabled: |
| tmp = _CMU_ADCCTRL_ADC0CLKSEL_DISABLED; |
| break; |
| |
| case cmuSelect_AUXHFRCO: |
| /* Ensure that the selected oscillator is enabled, waiting for it to stabilize. */ |
| CMU_OscillatorEnable(cmuOsc_AUXHFRCO, true, true); |
| tmp = _CMU_ADCCTRL_ADC0CLKSEL_AUXHFRCO; |
| break; |
| |
| case cmuSelect_HFXO: |
| /* Ensure that the selected oscillator is enabled, waiting for it to stabilize. */ |
| CMU_OscillatorEnable(cmuOsc_HFXO, true, true); |
| tmp = _CMU_ADCCTRL_ADC0CLKSEL_HFXO; |
| break; |
| |
| case cmuSelect_HFSRCCLK: |
| tmp = _CMU_ADCCTRL_ADC0CLKSEL_HFSRCCLK; |
| break; |
| |
| default: |
| /* An illegal clock source for ADC0ASYNC selected. */ |
| EFM_ASSERT(false); |
| return; |
| } |
| |
| /* Apply select. */ |
| CMU->ADCCTRL = (CMU->ADCCTRL & ~_CMU_ADCCTRL_ADC0CLKSEL_MASK) |
| | (tmp << _CMU_ADCCTRL_ADC0CLKSEL_SHIFT); |
| break; |
| #endif |
| |
| #if defined(_CMU_ADCCTRL_ADC1CLKSEL_MASK) |
| case CMU_ADC1ASYNCSEL_REG: |
| switch (ref) { |
| case cmuSelect_Disabled: |
| tmp = _CMU_ADCCTRL_ADC1CLKSEL_DISABLED; |
| break; |
| |
| case cmuSelect_AUXHFRCO: |
| /* Ensure that the selected oscillator is enabled, waiting for it to stabilize. */ |
| CMU_OscillatorEnable(cmuOsc_AUXHFRCO, true, true); |
| tmp = _CMU_ADCCTRL_ADC1CLKSEL_AUXHFRCO; |
| break; |
| |
| case cmuSelect_HFXO: |
| /* Ensure that the selected oscillator is enabled, waiting for it to stabilize. */ |
| CMU_OscillatorEnable(cmuOsc_HFXO, true, true); |
| tmp = _CMU_ADCCTRL_ADC1CLKSEL_HFXO; |
| break; |
| |
| case cmuSelect_HFSRCCLK: |
| tmp = _CMU_ADCCTRL_ADC1CLKSEL_HFSRCCLK; |
| break; |
| |
| default: |
| /* An illegal clock source for ADC1ASYNC selected. */ |
| EFM_ASSERT(false); |
| return; |
| } |
| |
| /* Apply select. */ |
| CMU->ADCCTRL = (CMU->ADCCTRL & ~_CMU_ADCCTRL_ADC1CLKSEL_MASK) |
| | (tmp << _CMU_ADCCTRL_ADC1CLKSEL_SHIFT); |
| break; |
| #endif |
| |
| #if defined(_CMU_SDIOCTRL_SDIOCLKSEL_MASK) |
| case CMU_SDIOREFSEL_REG: |
| switch (ref) { |
| case cmuSelect_HFRCO: |
| /* Ensure that the selected oscillator is enabled, waiting for it to stabilize. */ |
| CMU_OscillatorEnable(cmuOsc_HFRCO, true, true); |
| tmp = _CMU_SDIOCTRL_SDIOCLKSEL_HFRCO; |
| break; |
| |
| case cmuSelect_HFXO: |
| /* Ensure that the selected oscillator is enabled, waiting for it to stabilize. */ |
| CMU_OscillatorEnable(cmuOsc_HFXO, true, true); |
| tmp = _CMU_SDIOCTRL_SDIOCLKSEL_HFXO; |
| break; |
| |
| case cmuSelect_AUXHFRCO: |
| /* Ensure that the selected oscillator is enabled, waiting for it to stabilize. */ |
| CMU_OscillatorEnable(cmuOsc_AUXHFRCO, true, true); |
| tmp = _CMU_SDIOCTRL_SDIOCLKSEL_AUXHFRCO; |
| break; |
| |
| case cmuSelect_USHFRCO: |
| /* Ensure that the selected oscillator is enabled, waiting for it to stabilize. */ |
| CMU_OscillatorEnable(cmuOsc_USHFRCO, true, true); |
| tmp = _CMU_SDIOCTRL_SDIOCLKSEL_USHFRCO; |
| break; |
| |
| default: |
| /* An illegal clock source for SDIOREF selected. */ |
| EFM_ASSERT(false); |
| return; |
| } |
| |
| /* Apply select. */ |
| CMU->SDIOCTRL = (CMU->SDIOCTRL & ~_CMU_SDIOCTRL_SDIOCLKSEL_MASK) |
| | (tmp << _CMU_SDIOCTRL_SDIOCLKSEL_SHIFT); |
| break; |
| #endif |
| |
| #if defined(_CMU_QSPICTRL_QSPI0CLKSEL_MASK) |
| case CMU_QSPI0REFSEL_REG: |
| switch (ref) { |
| case cmuSelect_HFRCO: |
| /* Ensure that the selected oscillator is enabled, waiting for it to stabilize. */ |
| CMU_OscillatorEnable(cmuOsc_HFRCO, true, true); |
| tmp = _CMU_QSPICTRL_QSPI0CLKSEL_HFRCO; |
| break; |
| |
| case cmuSelect_HFXO: |
| /* Ensure that the selected oscillator is enabled, waiting for it to stabilize. */ |
| CMU_OscillatorEnable(cmuOsc_HFXO, true, true); |
| tmp = _CMU_QSPICTRL_QSPI0CLKSEL_HFXO; |
| break; |
| |
| case cmuSelect_AUXHFRCO: |
| /* Ensure that the selected oscillator is enabled, waiting for it to stabilize. */ |
| CMU_OscillatorEnable(cmuOsc_AUXHFRCO, true, true); |
| tmp = _CMU_QSPICTRL_QSPI0CLKSEL_AUXHFRCO; |
| break; |
| |
| case cmuSelect_USHFRCO: |
| /* Ensure that the selected oscillator is enabled, waiting for it to stabilize. */ |
| CMU_OscillatorEnable(cmuOsc_USHFRCO, true, true); |
| tmp = _CMU_QSPICTRL_QSPI0CLKSEL_USHFRCO; |
| break; |
| |
| default: |
| /* An illegal clock source for QSPI0REF selected. */ |
| EFM_ASSERT(false); |
| return; |
| } |
| |
| /* Apply select. */ |
| CMU->QSPICTRL = (CMU->QSPICTRL & ~_CMU_QSPICTRL_QSPI0CLKSEL_MASK) |
| | (tmp << _CMU_QSPICTRL_QSPI0CLKSEL_SHIFT); |
| break; |
| #endif |
| |
| #if defined(_CMU_USBCTRL_USBCLKSEL_MASK) |
| case CMU_USBRCLKSEL_REG: |
| switch (ref) { |
| case cmuSelect_USHFRCO: |
| /* Ensure that the selected oscillator is enabled, waiting for it to stabilize. */ |
| CMU_OscillatorEnable(cmuOsc_USHFRCO, true, true); |
| tmp = _CMU_USBCTRL_USBCLKSEL_USHFRCO; |
| break; |
| |
| case cmuSelect_HFXO: |
| /* Ensure that the selected oscillator is enabled, waiting for it to stabilize. */ |
| CMU_OscillatorEnable(cmuOsc_HFXO, true, true); |
| tmp = _CMU_USBCTRL_USBCLKSEL_HFXO; |
| break; |
| |
| case cmuSelect_HFXOX2: |
| /* Only allowed for HFXO frequencies up to 25 MHz. */ |
| EFM_ASSERT(SystemHFXOClockGet() <= 25000000u); |
| |
| /* Enable HFXO X2. */ |
| CMU->HFXOCTRL |= CMU_HFXOCTRL_HFXOX2EN; |
| |
| /* Ensure that the selected oscillator is enabled, waiting for it to stabilize. */ |
| CMU_OscillatorEnable(cmuOsc_HFXO, true, true); |
| |
| tmp = _CMU_USBCTRL_USBCLKSEL_HFXOX2; |
| break; |
| |
| case cmuSelect_HFRCO: |
| /* Ensure that the selected oscillator is enabled, waiting for it to stabilize. */ |
| CMU_OscillatorEnable(cmuOsc_HFRCO, true, true); |
| tmp = _CMU_USBCTRL_USBCLKSEL_HFRCO; |
| break; |
| |
| case cmuSelect_LFXO: |
| /* Ensure that the selected oscillator is enabled, waiting for it to stabilize. */ |
| CMU_OscillatorEnable(cmuOsc_LFXO, true, true); |
| tmp = _CMU_USBCTRL_USBCLKSEL_LFXO; |
| break; |
| |
| case cmuSelect_LFRCO: |
| /* Ensure that the selected oscillator is enabled, waiting for it to stabilize. */ |
| CMU_OscillatorEnable(cmuOsc_LFRCO, true, true); |
| tmp = _CMU_USBCTRL_USBCLKSEL_LFRCO; |
| break; |
| |
| default: |
| /* An illegal clock source for USBR selected. */ |
| EFM_ASSERT(false); |
| return; |
| } |
| |
| /* Apply select. */ |
| CMU->USBCTRL = (CMU->USBCTRL & ~_CMU_USBCTRL_USBCLKSEL_MASK) |
| | (tmp << _CMU_USBCTRL_USBCLKSEL_SHIFT); |
| break; |
| #endif |
| |
| default: |
| EFM_ASSERT(false); |
| break; |
| } |
| } |
| |
| #if defined(CMU_OSCENCMD_DPLLEN) |
| /**************************************************************************//** |
| * @brief |
| * Lock the DPLL to a given frequency. |
| * |
| * The frequency is given by: Fout = Fref * (N+1) / (M+1). |
| * |
| * @note |
| * This function does not check if the given N & M values will actually |
| * produce the desired target frequency. @n |
| * N & M limitations: @n |
| * 300 < N <= 4095 @n |
| * 0 <= M <= 4095 @n |
| * Any peripheral running off HFRCO should be switched to HFRCODIV2 prior to |
| * calling this function to avoid over-clocking. |
| * |
| * @param[in] init |
| * DPLL setup parameters. |
| * |
| * @return |
| * Returns false on invalid target frequency or DPLL locking error. |
| *****************************************************************************/ |
| bool CMU_DPLLLock(const CMU_DPLLInit_TypeDef *init) |
| { |
| int index = 0; |
| unsigned int i; |
| bool hfrcoDiv2 = false; |
| uint32_t hfrcoCtrlVal, lockStatus, sysFreq; |
| |
| EFM_ASSERT(init->frequency >= hfrcoCtrlTable[0].minFreq); |
| EFM_ASSERT(init->frequency |
| <= hfrcoCtrlTable[HFRCOCTRLTABLE_ENTRIES - 1U].maxFreq); |
| EFM_ASSERT(init->n > 300U); |
| EFM_ASSERT(init->n <= (_CMU_DPLLCTRL1_N_MASK >> _CMU_DPLLCTRL1_N_SHIFT)); |
| EFM_ASSERT(init->m <= (_CMU_DPLLCTRL1_M_MASK >> _CMU_DPLLCTRL1_M_SHIFT)); |
| EFM_ASSERT(init->ssInterval <= (_CMU_HFRCOSS_SSINV_MASK |
| >> _CMU_HFRCOSS_SSINV_SHIFT)); |
| EFM_ASSERT(init->ssAmplitude <= (_CMU_HFRCOSS_SSAMP_MASK |
| >> _CMU_HFRCOSS_SSAMP_SHIFT)); |
| |
| #if defined(_EMU_STATUS_VSCALE_MASK) |
| if ((EMU_VScaleGet() == emuVScaleEM01_LowPower) |
| && (init->frequency > CMU_VSCALEEM01_LOWPOWER_VOLTAGE_CLOCK_MAX)) { |
| EFM_ASSERT(false); |
| return false; |
| } |
| #endif |
| |
| // Find the correct HFRCO band and retrieve a HFRCOCTRL value. |
| for (i = 0; i < HFRCOCTRLTABLE_ENTRIES; i++) { |
| if ((init->frequency >= hfrcoCtrlTable[i].minFreq) |
| && (init->frequency <= hfrcoCtrlTable[i].maxFreq)) { |
| index = (int)i; // Correct band found |
| break; |
| } |
| } |
| if ((uint32_t)index == HFRCOCTRLTABLE_ENTRIES) { |
| EFM_ASSERT(false); |
| return false; // Target frequency out of spec. |
| } |
| hfrcoCtrlVal = hfrcoCtrlTable[index].value; |
| |
| // Check if a calibrated HFRCOCTRL.TUNING value is in device DI page. |
| if (hfrcoCtrlTable[index].band != (CMU_HFRCOFreq_TypeDef)0) { |
| uint32_t tuning; |
| |
| tuning = (CMU_HFRCODevinfoGet(hfrcoCtrlTable[index].band) |
| & _CMU_HFRCOCTRL_TUNING_MASK) |
| >> _CMU_HFRCOCTRL_TUNING_SHIFT; |
| |
| // When HFRCOCTRL.FINETUNINGEN is enabled, the center frequency |
| // of the band shifts down by 5.8%. 9 is subtracted to compensate. |
| if (tuning > 9UL) { |
| tuning -= 9UL; |
| } else { |
| tuning = 0UL; |
| } |
| |
| hfrcoCtrlVal |= tuning << _CMU_HFRCOCTRL_TUNING_SHIFT; |
| } |
| |
| // Update the CMSIS frequency SystemHfrcoFreq value. |
| SystemHfrcoFreq = init->frequency; |
| |
| // Set maximum wait-states while changing the core clock. |
| if (CMU_ClockSelectGet(cmuClock_HF) == cmuSelect_HFRCO) { |
| flashWaitStateMax(); |
| } |
| |
| // Update the HFLE configuration before updating HFRCO, use new DPLL frequency. |
| if (CMU_ClockSelectGet(cmuClock_HF) == cmuSelect_HFRCO) { |
| setHfLeConfig(init->frequency); |
| |
| // Switch to HFRCO/2 before setting DPLL to avoid over-clocking. |
| 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)) != 0UL) { |
| } |
| CMU->IFC = CMU_IFC_DPLLRDY | CMU_IFC_DPLLLOCKFAILLOW |
| | CMU_IFC_DPLLLOCKFAILHIGH; |
| CMU->DPLLCTRL1 = ((uint32_t)init->n << _CMU_DPLLCTRL1_N_SHIFT) |
| | ((uint32_t)init->m << _CMU_DPLLCTRL1_M_SHIFT); |
| CMU->HFRCOCTRL = hfrcoCtrlVal; |
| CMU->DPLLCTRL = ((uint32_t)init->refClk << _CMU_DPLLCTRL_REFSEL_SHIFT) |
| | ((init->autoRecover ? 1UL : 0UL) |
| << _CMU_DPLLCTRL_AUTORECOVER_SHIFT) |
| | ((uint32_t)init->edgeSel << _CMU_DPLLCTRL_EDGESEL_SHIFT) |
| | ((uint32_t)init->lockMode << _CMU_DPLLCTRL_MODE_SHIFT); |
| CMU->OSCENCMD = CMU_OSCENCMD_DPLLEN; |
| while ((lockStatus = (CMU->IF & (CMU_IF_DPLLRDY |
| | CMU_IF_DPLLLOCKFAILLOW |
| | CMU_IF_DPLLLOCKFAILHIGH))) == 0UL) { |
| } |
| |
| if ((CMU_ClockSelectGet(cmuClock_HF) == cmuSelect_HFRCO) |
| && (hfrcoDiv2 == false)) { |
| CMU->HFCLKSEL = CMU_HFCLKSEL_HF_HFRCO; |
| } |
| |
| // If HFRCO is selected as an HF clock, optimize the flash access wait-state |
| // configuration for this frequency and update the CMSIS core clock variable. |
| if (CMU_ClockSelectGet(cmuClock_HF) == cmuSelect_HFRCO) { |
| // Call @ref SystemCoreClockGet() to update the CMSIS core clock variable. |
| sysFreq = SystemCoreClockGet(); |
| EFM_ASSERT(sysFreq <= init->frequency); |
| EFM_ASSERT(sysFreq <= SystemHfrcoFreq); |
| EFM_ASSERT(init->frequency == SystemHfrcoFreq); |
| CMU_UpdateWaitStates(sysFreq, (int)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 require synchronization into the low-frequency (LF) |
| * domain. The freeze feature allows for several such registers to be |
| * modified before passing them to the LF domain simultaneously (which |
| * takes place when the freeze mode is disabled). |
| * |
| * Another use case for this feature is using an API (such |
| * as the CMU API) for modifying several bit fields consecutively in the |
| * same register. If freeze mode is enabled during this sequence, stalling |
| * can be avoided. |
| * |
| * @note |
| * When enabling freeze mode, this function will wait for all current |
| * ongoing CMU synchronization to LF domain to complete (normally |
| * synchronization will not be in progress.) However, for this reason, when |
| * using freeze mode, modifications of registers requiring LF synchronization |
| * should be done within one freeze enable/disable block to avoid unnecessary |
| * stalling. |
| * |
| * @param[in] enable |
| * @li true - enable freeze, modified registers are not propagated to the |
| * LF domain |
| * @li false - disable freeze, modified registers are propagated to the LF |
| * domain |
| *****************************************************************************/ |
| void CMU_FreezeEnable(bool enable) |
| { |
| if (enable) { |
| /* Wait for any ongoing LF synchronizations to complete. This */ |
| /* protects against the rare case when a user */ |
| /* - modifies a register requiring LF sync */ |
| /* - then enables freeze before LF sync completed */ |
| /* - then modifies the same register again */ |
| /* since modifying a register while it is in sync progress should be */ |
| /* avoided. */ |
| while (CMU->SYNCBUSY != 0UL) { |
| } |
| |
| CMU->FREEZE = CMU_FREEZE_REGFREEZE; |
| } else { |
| CMU->FREEZE = 0; |
| } |
| } |
| |
| #if defined(_CMU_HFRCOCTRL_BAND_MASK) |
| /***************************************************************************//** |
| * @brief |
| * Get HFRCO band in use. |
| * |
| * @return |
| * HFRCO band in use. |
| ******************************************************************************/ |
| CMU_HFRCOBand_TypeDef CMU_HFRCOBandGet(void) |
| { |
| return (CMU_HFRCOBand_TypeDef)((CMU->HFRCOCTRL & _CMU_HFRCOCTRL_BAND_MASK) |
| >> _CMU_HFRCOCTRL_BAND_SHIFT); |
| } |
| #endif /* _CMU_HFRCOCTRL_BAND_MASK */ |
| |
| #if defined(_CMU_HFRCOCTRL_BAND_MASK) |
| /***************************************************************************//** |
| * @brief |
| * Set HFRCO band and the tuning value based on the value in the calibration |
| * table made during production. |
| * |
| * @param[in] band |
| * HFRCO band to activate. |
| ******************************************************************************/ |
| void CMU_HFRCOBandSet(CMU_HFRCOBand_TypeDef band) |
| { |
| uint32_t tuning; |
| uint32_t freq; |
| CMU_Select_TypeDef osc; |
| |
| /* Read the tuning value from the calibration table. */ |
| switch (band) { |
| case cmuHFRCOBand_1MHz: |
| tuning = (DEVINFO->HFRCOCAL0 & _DEVINFO_HFRCOCAL0_BAND1_MASK) |
| >> _DEVINFO_HFRCOCAL0_BAND1_SHIFT; |
| break; |
| |
| case cmuHFRCOBand_7MHz: |
| tuning = (DEVINFO->HFRCOCAL0 & _DEVINFO_HFRCOCAL0_BAND7_MASK) |
| >> _DEVINFO_HFRCOCAL0_BAND7_SHIFT; |
| break; |
| |
| case cmuHFRCOBand_11MHz: |
| tuning = (DEVINFO->HFRCOCAL0 & _DEVINFO_HFRCOCAL0_BAND11_MASK) |
| >> _DEVINFO_HFRCOCAL0_BAND11_SHIFT; |
| break; |
| |
| case cmuHFRCOBand_14MHz: |
| tuning = (DEVINFO->HFRCOCAL0 & _DEVINFO_HFRCOCAL0_BAND14_MASK) |
| >> _DEVINFO_HFRCOCAL0_BAND14_SHIFT; |
| break; |
| |
| case cmuHFRCOBand_21MHz: |
| tuning = (DEVINFO->HFRCOCAL1 & _DEVINFO_HFRCOCAL1_BAND21_MASK) |
| >> _DEVINFO_HFRCOCAL1_BAND21_SHIFT; |
| break; |
| |
| #if defined(_CMU_HFRCOCTRL_BAND_28MHZ) |
| case cmuHFRCOBand_28MHz: |
| tuning = (DEVINFO->HFRCOCAL1 & _DEVINFO_HFRCOCAL1_BAND28_MASK) |
| >> _DEVINFO_HFRCOCAL1_BAND28_SHIFT; |
| break; |
| #endif |
| |
| default: |
| EFM_ASSERT(false); |
| return; |
| } |
| |
| /* If HFRCO is used for the core clock, flash access WS has to be considered. */ |
| osc = CMU_ClockSelectGet(cmuClock_HF); |
| if (osc == cmuSelect_HFRCO) { |
| /* Configure worst case wait states for flash access before setting the divider. */ |
| flashWaitStateMax(); |
| } |
| |
| /* Set band/tuning. */ |
| CMU->HFRCOCTRL = (CMU->HFRCOCTRL |
| & ~(_CMU_HFRCOCTRL_BAND_MASK | _CMU_HFRCOCTRL_TUNING_MASK)) |
| | (band << _CMU_HFRCOCTRL_BAND_SHIFT) |
| | (tuning << _CMU_HFRCOCTRL_TUNING_SHIFT); |
| |
| /* If HFRCO is used for the core clock, optimize flash WS. */ |
| if (osc == cmuSelect_HFRCO) { |
| /* Call @ref SystemCoreClockGet() to update the CMSIS core clock variable. */ |
| freq = SystemCoreClockGet(); |
| CMU_UpdateWaitStates(freq, (int)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 4 MHz share the same calibration word. */ |
| case cmuHFRCOFreq_1M0Hz: |
| case cmuHFRCOFreq_2M0Hz: |
| case cmuHFRCOFreq_4M0Hz: |
| return DEVINFO->HFRCOCAL0; |
| |
| case cmuHFRCOFreq_7M0Hz: |
| return DEVINFO->HFRCOCAL3; |
| |
| case cmuHFRCOFreq_13M0Hz: |
| return DEVINFO->HFRCOCAL6; |
| |
| case cmuHFRCOFreq_16M0Hz: |
| return DEVINFO->HFRCOCAL7; |
| |
| case cmuHFRCOFreq_19M0Hz: |
| return DEVINFO->HFRCOCAL8; |
| |
| case cmuHFRCOFreq_26M0Hz: |
| return DEVINFO->HFRCOCAL10; |
| |
| case cmuHFRCOFreq_32M0Hz: |
| return DEVINFO->HFRCOCAL11; |
| |
| case cmuHFRCOFreq_38M0Hz: |
| return DEVINFO->HFRCOCAL12; |
| |
| #if defined(_DEVINFO_HFRCOCAL13_MASK) |
| case cmuHFRCOFreq_48M0Hz: |
| return DEVINFO->HFRCOCAL13; |
| #endif |
| |
| #if defined(_DEVINFO_HFRCOCAL14_MASK) |
| case cmuHFRCOFreq_56M0Hz: |
| return DEVINFO->HFRCOCAL14; |
| #endif |
| |
| #if defined(_DEVINFO_HFRCOCAL15_MASK) |
| case cmuHFRCOFreq_64M0Hz: |
| return DEVINFO->HFRCOCAL15; |
| #endif |
| |
| #if defined(_DEVINFO_HFRCOCAL16_MASK) |
| case cmuHFRCOFreq_72M0Hz: |
| return DEVINFO->HFRCOCAL16; |
| #endif |
| |
| default: /* cmuHFRCOFreq_UserDefined */ |
| return 0; |
| } |
| } |
| |
| /***************************************************************************//** |
| * @brief |
| * Get the current HFRCO frequency. |
| * |
| * @return |
| * HFRCO frequency. |
| ******************************************************************************/ |
| CMU_HFRCOFreq_TypeDef CMU_HFRCOBandGet(void) |
| { |
| return (CMU_HFRCOFreq_TypeDef)SystemHfrcoFreq; |
| } |
| |
| /***************************************************************************//** |
| * @brief |
| * Set the HFRCO calibration for the selected target frequency. |
| * |
| * @param[in] setFreq |
| * HFRCO frequency to set. |
| ******************************************************************************/ |
| void CMU_HFRCOBandSet(CMU_HFRCOFreq_TypeDef setFreq) |
| { |
| uint32_t freqCal; |
| uint32_t sysFreq; |
| uint32_t prevFreq; |
| |
| /* Get the DEVINFO index and set the CMSIS frequency SystemHfrcoFreq. */ |
| freqCal = CMU_HFRCODevinfoGet(setFreq); |
| EFM_ASSERT((freqCal != 0UL) && (freqCal != UINT_MAX)); |
| prevFreq = SystemHfrcoFreq; |
| SystemHfrcoFreq = (uint32_t)setFreq; |
| |
| /* Set maximum wait-states and set safe HFPER clock-tree prescalers while |
| changing the core clock. */ |
| if (CMU_ClockSelectGet(cmuClock_HF) == cmuSelect_HFRCO) { |
| flashWaitStateMax(); |
| hfperClkSafePrescaler(); |
| } |
| |
| /* Wait for any previous sync to complete and set calibration data |
| for the selected frequency. */ |
| while (BUS_RegBitRead(&CMU->SYNCBUSY, _CMU_SYNCBUSY_HFRCOBSY_SHIFT) != 0UL) { |
| } |
| |
| /* Check for valid calibration data. */ |
| EFM_ASSERT(freqCal != UINT_MAX); |
| |
| /* Set divider in HFRCOCTRL for 1, 2, and 4 MHz. */ |
| switch (setFreq) { |
| case cmuHFRCOFreq_1M0Hz: |
| freqCal = (freqCal & ~_CMU_HFRCOCTRL_CLKDIV_MASK) |
| | CMU_HFRCOCTRL_CLKDIV_DIV4; |
| break; |
| |
| case cmuHFRCOFreq_2M0Hz: |
| freqCal = (freqCal & ~_CMU_HFRCOCTRL_CLKDIV_MASK) |
| | CMU_HFRCOCTRL_CLKDIV_DIV2; |
| break; |
| |
| case cmuHFRCOFreq_4M0Hz: |
| freqCal = (freqCal & ~_CMU_HFRCOCTRL_CLKDIV_MASK) |
| | CMU_HFRCOCTRL_CLKDIV_DIV1; |
| break; |
| |
| default: |
| break; |
| } |
| |
| /* Update HFLE configuration before updating HFRCO. |
| Use the new set frequency. */ |
| if (CMU_ClockSelectGet(cmuClock_HF) == cmuSelect_HFRCO) { |
| /* setFreq is worst-case as dividers may reduce the HFLE frequency. */ |
| setHfLeConfig((uint32_t)setFreq); |
| } |
| |
| if ((uint32_t)setFreq > prevFreq) { |
| #if defined(_EMU_CMD_EM01VSCALE0_MASK) |
| /* When increasing frequency voltage scale must be done before the change. */ |
| EMU_VScaleEM01ByClock((uint32_t)setFreq, true); |
| #endif |
| } |
| |
| CMU->HFRCOCTRL = freqCal; |
| |
| /* If HFRCO is selected as an HF clock, optimize the flash access wait-state configuration |
| for this frequency and update the CMSIS core clock variable. */ |
| if (CMU_ClockSelectGet(cmuClock_HF) == cmuSelect_HFRCO) { |
| /* Call @ref SystemCoreClockGet() to update the CMSIS core clock variable. */ |
| sysFreq = SystemCoreClockGet(); |
| EFM_ASSERT(sysFreq <= (uint32_t)setFreq); |
| EFM_ASSERT(sysFreq <= SystemHfrcoFreq); |
| EFM_ASSERT((uint32_t)setFreq == SystemHfrcoFreq); |
| CMU_UpdateWaitStates(sysFreq, (int)VSCALE_DEFAULT); |
| } |
| |
| /* Reduce HFLE frequency if possible. */ |
| setHfLeConfig(CMU_ClockFreqGet(cmuClock_HFLE)); |
| |
| if ((uint32_t)setFreq <= prevFreq) { |
| #if defined(_EMU_CMD_EM01VSCALE0_MASK) |
| /* When decreasing frequency voltage scale must be done after the change */ |
| EMU_VScaleEM01ByClock(0, true); |
| #endif |
| } |
| if (CMU_ClockSelectGet(cmuClock_HF) == cmuSelect_HFRCO) { |
| /* Set optimized HFPER clock-tree prescalers. */ |
| hfperClkOptimizedPrescaler(); |
| } |
| } |
| #endif /* _CMU_HFRCOCTRL_FREQRANGE_MASK */ |
| |
| #if defined(_CMU_HFRCOCTRL_SUDELAY_MASK) |
| /***************************************************************************//** |
| * @brief |
| * Get the HFRCO startup delay. |
| * |
| * @details |
| * See the reference manual for more details. |
| * |
| * @return |
| * The startup delay in use. |
| ******************************************************************************/ |
| uint32_t CMU_HFRCOStartupDelayGet(void) |
| { |
| return (CMU->HFRCOCTRL & _CMU_HFRCOCTRL_SUDELAY_MASK) |
| >> _CMU_HFRCOCTRL_SUDELAY_SHIFT; |
| } |
| |
| /***************************************************************************//** |
| * @brief |
| * Set the HFRCO startup delay. |
| * |
| * @details |
| * See the reference manual for more details. |
| * |
| * @param[in] delay |
| * The startup delay to set (<= 31). |
| ******************************************************************************/ |
| void CMU_HFRCOStartupDelaySet(uint32_t delay) |
| { |
| EFM_ASSERT(delay <= 31); |
| |
| delay &= _CMU_HFRCOCTRL_SUDELAY_MASK >> _CMU_HFRCOCTRL_SUDELAY_SHIFT; |
| CMU->HFRCOCTRL = (CMU->HFRCOCTRL & ~(_CMU_HFRCOCTRL_SUDELAY_MASK)) |
| | (delay << _CMU_HFRCOCTRL_SUDELAY_SHIFT); |
| } |
| #endif |
| |
| #if defined(_CMU_USHFRCOCTRL_FREQRANGE_MASK) |
| /**************************************************************************//** |
| * @brief |
| * Get the USHFRCO frequency calibration word in DEVINFO. |
| * |
| * @param[in] freq |
| * Frequency in Hz. |
| * |
| * @return |
| * USHFRCO calibration word for a given frequency. |
| *****************************************************************************/ |
| static uint32_t CMU_USHFRCODevinfoGet(CMU_USHFRCOFreq_TypeDef freq) |
| { |
| switch (freq) { |
| case cmuUSHFRCOFreq_16M0Hz: |
| return DEVINFO->USHFRCOCAL7; |
| |
| case cmuUSHFRCOFreq_32M0Hz: |
| return DEVINFO->USHFRCOCAL11; |
| |
| case cmuUSHFRCOFreq_48M0Hz: |
| return DEVINFO->USHFRCOCAL13; |
| |
| case cmuUSHFRCOFreq_50M0Hz: |
| return DEVINFO->USHFRCOCAL14; |
| |
| default: /* cmuUSHFRCOFreq_UserDefined */ |
| return 0; |
| } |
| } |
| |
| /***************************************************************************//** |
| * @brief |
| * Get the current USHFRCO frequency. |
| * |
| * @return |
| * HFRCO frequency. |
| ******************************************************************************/ |
| CMU_USHFRCOFreq_TypeDef CMU_USHFRCOBandGet(void) |
| { |
| return (CMU_USHFRCOFreq_TypeDef) ushfrcoFreq; |
| } |
| |
| /***************************************************************************//** |
| * @brief |
| * Set the USHFRCO calibration for the selected target frequency. |
| * |
| * @param[in] setFreq |
| * USHFRCO frequency to set. |
| ******************************************************************************/ |
| void CMU_USHFRCOBandSet(CMU_USHFRCOFreq_TypeDef setFreq) |
| { |
| uint32_t freqCal; |
| |
| /* Get DEVINFO calibration values. */ |
| freqCal = CMU_USHFRCODevinfoGet(setFreq); |
| EFM_ASSERT((freqCal != 0) && (freqCal != UINT_MAX)); |
| ushfrcoFreq = (uint32_t)setFreq; |
| |
| /* Wait for any previous sync to complete and set calibration data |
| for the selected frequency. */ |
| while (BUS_RegBitRead(&CMU->SYNCBUSY, _CMU_SYNCBUSY_USHFRCOBSY_SHIFT)) ; |
| |
| CMU->USHFRCOCTRL = freqCal; |
| } |
| #endif /* _CMU_USHFRCOCTRL_FREQRANGE_MASK */ |
| |
| #if defined(_CMU_HFXOCTRL_AUTOSTARTEM0EM1_MASK) |
| /***************************************************************************//** |
| * @brief |
| * Enable or disable HFXO autostart. |
| * |
| * @param[in] userSel |
| * Additional user specified enable bit. |
| * |
| * @param[in] enEM0EM1Start |
| * If true, HFXO is automatically started upon entering EM0/EM1 entry from |
| * EM2/EM3. HFXO selection has to be handled by the user. |
| * If false, HFXO is not started automatically when entering EM0/EM1. |
| * |
| * @param[in] enEM0EM1StartSel |
| * If true, HFXO is automatically started and immediately selected upon |
| * entering EM0/EM1 entry from EM2/EM3. Note that this option stalls the use of |
| * HFSRCCLK until HFXO becomes ready. |
| * If false, HFXO is not started or selected automatically when entering |
| * EM0/EM1. |
| ******************************************************************************/ |
| void CMU_HFXOAutostartEnable(uint32_t userSel, |
| bool enEM0EM1Start, |
| bool enEM0EM1StartSel) |
| { |
| uint32_t hfxoFreq; |
| uint32_t hfxoCtrl; |
| |
| #if defined(_EMU_CTRL_EM23VSCALE_MASK) |
| if (enEM0EM1StartSel) { |
| /* Voltage scaling is not compatible with HFXO auto start and select. */ |
| EFM_ASSERT((EMU->CTRL & _EMU_CTRL_EM23VSCALE_MASK) == EMU_CTRL_EM23VSCALE_VSCALE2); |
| } |
| #endif |
| |
| /* Mask supported enable bits. */ |
| #if defined(_CMU_HFXOCTRL_AUTOSTARTRDYSELRAC_MASK) |
| userSel &= _CMU_HFXOCTRL_AUTOSTARTRDYSELRAC_MASK; |
| #else |
| userSel = 0; |
| #endif |
| |
| hfxoCtrl = CMU->HFXOCTRL & ~(userSel |
| | _CMU_HFXOCTRL_AUTOSTARTEM0EM1_MASK |
| | _CMU_HFXOCTRL_AUTOSTARTSELEM0EM1_MASK); |
| |
| hfxoCtrl |= userSel |
| | (enEM0EM1Start ? CMU_HFXOCTRL_AUTOSTARTEM0EM1 : 0UL) |
| | (enEM0EM1StartSel ? CMU_HFXOCTRL_AUTOSTARTSELEM0EM1 : 0UL); |
| |
| hfxoFreq = SystemHFXOClockGet(); |
| #if defined(_EMU_CMD_EM01VSCALE0_MASK) |
| // Update voltage scaling. |
| EMU_VScaleEM01ByClock(hfxoFreq, true); |
| #endif |
| /* Set wait-states for HFXO if automatic start and select is configured. */ |
| if ((userSel > 0UL) || enEM0EM1StartSel) { |
| CMU_UpdateWaitStates(hfxoFreq, (int)VSCALE_DEFAULT); |
| setHfLeConfig(hfxoFreq); |
| } |
| |
| if (enEM0EM1Start || enEM0EM1StartSel) { |
| /* Enable the HFXO once in order to finish first time calibrations. */ |
| CMU_OscillatorEnable(cmuOsc_HFXO, true, true); |
| } |
| |
| /* Update HFXOCTRL after wait-states are updated as HF may automatically switch |
| to HFXO when automatic select is enabled . */ |
| CMU->HFXOCTRL = hfxoCtrl; |
| } |
| #endif |
| |
| /**************************************************************************//** |
| * @brief |
| * Set HFXO control registers. |
| * |
| * @note |
| * HFXO configuration should be obtained from a configuration tool, |
| * app note, or xtal data sheet. This function disables the HFXO to ensure |
| * a valid state before update. |
| * |
| * @param[in] hfxoInit |
| * HFXO setup parameters. |
| *****************************************************************************/ |
| void CMU_HFXOInit(const CMU_HFXOInit_TypeDef *hfxoInit) |
| { |
| /* Do not disable HFXO if it is currently selected as the HF/Core clock. */ |
| EFM_ASSERT(CMU_ClockSelectGet(cmuClock_HF) != cmuSelect_HFXO); |
| |
| /* HFXO must be disabled before reconfiguration. */ |
| CMU_OscillatorEnable(cmuOsc_HFXO, false, true); |
| |
| #if defined(_SILICON_LABS_32B_SERIES_1) \ |
| && (_SILICON_LABS_GECKO_INTERNAL_SDID >= 100) |
| uint32_t tmp = CMU_HFXOCTRL_MODE_XTAL; |
| |
| switch (hfxoInit->mode) { |
| case cmuOscMode_Crystal: |
| tmp = CMU_HFXOCTRL_MODE_XTAL; |
| break; |
| case cmuOscMode_External: |
| tmp = CMU_HFXOCTRL_MODE_DIGEXTCLK; |
| break; |
| case cmuOscMode_AcCoupled: |
| tmp = CMU_HFXOCTRL_MODE_ACBUFEXTCLK; |
| break; |
| default: |
| EFM_ASSERT(false); /* Unsupported configuration */ |
| break; |
| } |
| CMU->HFXOCTRL = (CMU->HFXOCTRL & ~_CMU_HFXOCTRL_MODE_MASK) | tmp; |
| |
| #if defined(CMU_HFXOCTRL_HFXOX2EN) |
| /* HFXO Doubler can only be enabled on crystals up to max 25 MHz. */ |
| tmp = 0; |
| if (SystemHFXOClockGet() <= 25000000) { |
| tmp |= CMU_HFXOCTRL_HFXOX2EN; |
| } |
| |
| CMU->HFXOCTRL = (CMU->HFXOCTRL & ~_CMU_HFXOCTRL_HFXOX2EN_MASK) | tmp; |
| #endif |
| |
| /* Set tuning for startup and steady state. */ |
| CMU->HFXOSTARTUPCTRL = (hfxoInit->ctuneStartup |
| << _CMU_HFXOSTARTUPCTRL_CTUNE_SHIFT) |
| | (hfxoInit->xoCoreBiasTrimStartup |
| << _CMU_HFXOSTARTUPCTRL_IBTRIMXOCORE_SHIFT); |
| |
| CMU->HFXOSTEADYSTATECTRL = (CMU->HFXOSTEADYSTATECTRL |
| & ~(_CMU_HFXOSTEADYSTATECTRL_CTUNE_MASK |
| | _CMU_HFXOSTEADYSTATECTRL_IBTRIMXOCORE_MASK)) |
| | (hfxoInit->ctuneSteadyState |
| << _CMU_HFXOSTEADYSTATECTRL_CTUNE_SHIFT) |
| | (hfxoInit->xoCoreBiasTrimSteadyState |
| << _CMU_HFXOSTEADYSTATECTRL_IBTRIMXOCORE_SHIFT); |
| |
| /* Set timeouts */ |
| CMU->HFXOTIMEOUTCTRL = (hfxoInit->timeoutPeakDetect |
| << _CMU_HFXOTIMEOUTCTRL_PEAKDETTIMEOUT_SHIFT) |
| | (hfxoInit->timeoutSteady |
| << _CMU_HFXOTIMEOUTCTRL_STEADYTIMEOUT_SHIFT) |
| | (hfxoInit->timeoutStartup |
| << _CMU_HFXOTIMEOUTCTRL_STARTUPTIMEOUT_SHIFT); |
| |
| #elif defined(_CMU_HFXOCTRL_MASK) |
| /* Verify that the deprecated autostart fields are not used, |
| * @ref CMU_HFXOAutostartEnable must be used instead. */ |
| EFM_ASSERT(!(hfxoInit->autoStartEm01 |
| || hfxoInit->autoSelEm01 |
| || hfxoInit->autoStartSelOnRacWakeup)); |
| |
| uint32_t tmp = CMU_HFXOCTRL_MODE_XTAL; |
| |
| /* AC coupled external clock not supported. */ |
| EFM_ASSERT(hfxoInit->mode != cmuOscMode_AcCoupled); |
| if (hfxoInit->mode == cmuOscMode_External) { |
| tmp = CMU_HFXOCTRL_MODE_DIGEXTCLK; |
| } |
| |
| /* Apply control settings. */ |
| CMU->HFXOCTRL = (CMU->HFXOCTRL & ~_CMU_HFXOCTRL_MODE_MASK) |
| | tmp; |
| BUS_RegBitWrite(&CMU->HFXOCTRL, |
| _CMU_HFXOCTRL_LOWPOWER_SHIFT, |
| (unsigned)hfxoInit->lowPowerMode); |
| |
| /* Set XTAL tuning parameters. */ |
| |
| #if defined(_CMU_HFXOCTRL1_PEAKDETTHR_MASK) |
| /* Set peak detection threshold. */ |
| CMU->HFXOCTRL1 = (CMU->HFXOCTRL1 & ~_CMU_HFXOCTRL1_PEAKDETTHR_MASK) |
| | (hfxoInit->thresholdPeakDetect |
| << _CMU_HFXOCTRL1_PEAKDETTHR_SHIFT); |
| #endif |
| /* Set tuning for startup and steady state. */ |
| CMU->HFXOSTARTUPCTRL = ((uint32_t)hfxoInit->ctuneStartup |
| << _CMU_HFXOSTARTUPCTRL_CTUNE_SHIFT) |
| | ((uint32_t)hfxoInit->xoCoreBiasTrimStartup |
| << _CMU_HFXOSTARTUPCTRL_IBTRIMXOCORE_SHIFT); |
| |
| CMU->HFXOSTEADYSTATECTRL = (CMU->HFXOSTEADYSTATECTRL |
| & ~(_CMU_HFXOSTEADYSTATECTRL_CTUNE_MASK |
| | _CMU_HFXOSTEADYSTATECTRL_IBTRIMXOCORE_MASK |
| | _CMU_HFXOSTEADYSTATECTRL_REGISH_MASK |
| | _CMU_HFXOSTEADYSTATECTRL_REGISHUPPER_MASK)) |
| | ((uint32_t)hfxoInit->ctuneSteadyState |
| << _CMU_HFXOSTEADYSTATECTRL_CTUNE_SHIFT) |
| | ((uint32_t)hfxoInit->xoCoreBiasTrimSteadyState |
| << _CMU_HFXOSTEADYSTATECTRL_IBTRIMXOCORE_SHIFT) |
| | ((uint32_t)hfxoInit->regIshSteadyState |
| << _CMU_HFXOSTEADYSTATECTRL_REGISH_SHIFT) |
| | getRegIshUpperVal(hfxoInit->regIshSteadyState); |
| |
| /* Set timeouts. */ |
| CMU->HFXOTIMEOUTCTRL = ((uint32_t)hfxoInit->timeoutPeakDetect |
| << _CMU_HFXOTIMEOUTCTRL_PEAKDETTIMEOUT_SHIFT) |
| | ((uint32_t)hfxoInit->timeoutSteady |
| << _CMU_HFXOTIMEOUTCTRL_STEADYTIMEOUT_SHIFT) |
| | ((uint32_t)hfxoInit->timeoutStartup |
| << _CMU_HFXOTIMEOUTCTRL_STARTUPTIMEOUT_SHIFT) |
| | ((uint32_t)hfxoInit->timeoutShuntOptimization |
| << _CMU_HFXOTIMEOUTCTRL_SHUNTOPTTIMEOUT_SHIFT); |
| |
| #else |
| CMU->CTRL = (CMU->CTRL & ~(_CMU_CTRL_HFXOTIMEOUT_MASK |
| | _CMU_CTRL_HFXOBOOST_MASK |
| | _CMU_CTRL_HFXOMODE_MASK |
| | _CMU_CTRL_HFXOGLITCHDETEN_MASK)) |
| | (hfxoInit->timeout << _CMU_CTRL_HFXOTIMEOUT_SHIFT) |
| | (hfxoInit->boost << _CMU_CTRL_HFXOBOOST_SHIFT) |
| | (hfxoInit->mode << _CMU_CTRL_HFXOMODE_SHIFT) |
| | (hfxoInit->glitchDetector ? CMU_CTRL_HFXOGLITCHDETEN : 0); |
| #endif |
| } |
| |
| /***************************************************************************//** |
| * @brief |
| * Get the LCD framerate divisor (FDIV) setting. |
| * |
| * @return |
| * The LCD framerate divisor. |
| ******************************************************************************/ |
| uint32_t CMU_LCDClkFDIVGet(void) |
| { |
| #if defined(LCD_PRESENT) && defined(_CMU_LCDCTRL_MASK) |
| return (CMU->LCDCTRL & _CMU_LCDCTRL_FDIV_MASK) >> _CMU_LCDCTRL_FDIV_SHIFT; |
| #else |
| return 0; |
| #endif /* defined(LCD_PRESENT) */ |
| } |
| |
| /***************************************************************************//** |
| * @brief |
| * Set the LCD framerate divisor (FDIV) setting. |
| * |
| * @note |
| * The FDIV field (CMU LCDCTRL register) should only be modified while the |
| * LCD module is clock disabled (CMU LFACLKEN0.LCD bit is 0). This function |
| * will NOT modify FDIV if the LCD module clock is enabled. See |
| * @ref CMU_ClockEnable() for disabling/enabling LCD clock. |
| * |
| * @param[in] div |
| * The FDIV setting to use. |
| ******************************************************************************/ |
| void CMU_LCDClkFDIVSet(uint32_t div) |
| { |
| #if defined(LCD_PRESENT) && defined(_CMU_LCDCTRL_MASK) |
| EFM_ASSERT(div <= cmuClkDiv_128); |
| |
| /* Do not allow modification if LCD clock enabled. */ |
| if (CMU->LFACLKEN0 & CMU_LFACLKEN0_LCD) { |
| return; |
| } |
| |
| div <<= _CMU_LCDCTRL_FDIV_SHIFT; |
| div &= _CMU_LCDCTRL_FDIV_MASK; |
| CMU->LCDCTRL = (CMU->LCDCTRL & ~_CMU_LCDCTRL_FDIV_MASK) | div; |
| #else |
| (void)div; /* Unused parameter. */ |
| #endif /* defined(LCD_PRESENT) */ |
| } |
| |
| /**************************************************************************//** |
| * @brief |
| * Set LFXO control registers. |
| * |
| * @note |
| * LFXO configuration should be obtained from a configuration tool, |
| * app note, or xtal data sheet. This function disables the LFXO to ensure |
| * a valid state before update. |
| * |
| * @param[in] lfxoInit |
| * LFXO setup parameters. |
| *****************************************************************************/ |
| void CMU_LFXOInit(const CMU_LFXOInit_TypeDef *lfxoInit) |
| { |
| /* Do not disable LFXO if it is currently selected as the HF/Core clock. */ |
| EFM_ASSERT(CMU_ClockSelectGet(cmuClock_HF) != cmuSelect_LFXO); |
| |
| /* 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, |
| ((uint32_t)lfxoInit->ctune << _CMU_LFXOCTRL_TUNING_SHIFT) |
| | ((uint32_t)lfxoInit->gain << _CMU_LFXOCTRL_GAIN_SHIFT) |
| | ((uint32_t)lfxoInit->timeout |
| << _CMU_LFXOCTRL_TIMEOUT_SHIFT) |
| | ((uint32_t)lfxoInit->mode << _CMU_LFXOCTRL_MODE_SHIFT)); |
| #else |
| bool cmuBoost = (lfxoInit->boost & 0x2); |
| BUS_RegMaskedWrite(&CMU->CTRL, |
| _CMU_CTRL_LFXOTIMEOUT_MASK |
| | _CMU_CTRL_LFXOBOOST_MASK |
| | _CMU_CTRL_LFXOMODE_MASK, |
| ((uint32_t)lfxoInit->timeout |
| << _CMU_CTRL_LFXOTIMEOUT_SHIFT) |
| | ((cmuBoost ? 1 : 0) << _CMU_CTRL_LFXOBOOST_SHIFT) |
| | ((uint32_t)lfxoInit->mode << _CMU_CTRL_LFXOMODE_SHIFT)); |
| #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. In other words, if external clock sources are selected |
| * in either LFXOMODE or HFXOMODE fields, the configuration will be cleared |
| * and needs to be reconfigured if needed later. |
| * |
| * @param[in] osc |
| * The oscillator to enable/disable. |
| * |
| * @param[in] enable |
| * @li true - enable specified oscillator. |
| * @li false - disable specified oscillator. |
| * |
| * @param[in] wait |
| * Only used if @p enable is true. |
| * @li true - wait for oscillator start-up time to timeout before returning. |
| * @li false - do not wait for oscillator start-up time to timeout before |
| * returning. |
| ******************************************************************************/ |
| void CMU_OscillatorEnable(CMU_Osc_TypeDef osc, bool enable, bool wait) |
| { |
| uint32_t rdyBitPos; |
| #if defined(_SILICON_LABS_32B_SERIES_1) |
| uint32_t ensBitPos; |
| #endif |
| #if defined(_CMU_STATUS_HFXOPEAKDETRDY_MASK) |
| uint32_t hfxoTrimStatus; |
| #endif |
| |
| uint32_t enBit; |
| uint32_t disBit; |
| |
| switch (osc) { |
| case cmuOsc_HFRCO: |
| enBit = CMU_OSCENCMD_HFRCOEN; |
| disBit = CMU_OSCENCMD_HFRCODIS; |
| rdyBitPos = _CMU_STATUS_HFRCORDY_SHIFT; |
| #if defined(_SILICON_LABS_32B_SERIES_1) |
| ensBitPos = _CMU_STATUS_HFRCOENS_SHIFT; |
| #endif |
| break; |
| |
| case cmuOsc_HFXO: |
| enBit = CMU_OSCENCMD_HFXOEN; |
| disBit = CMU_OSCENCMD_HFXODIS; |
| rdyBitPos = _CMU_STATUS_HFXORDY_SHIFT; |
| #if defined(_SILICON_LABS_32B_SERIES_1) |
| ensBitPos = _CMU_STATUS_HFXOENS_SHIFT; |
| #endif |
| break; |
| |
| case cmuOsc_AUXHFRCO: |
| enBit = CMU_OSCENCMD_AUXHFRCOEN; |
| disBit = CMU_OSCENCMD_AUXHFRCODIS; |
| rdyBitPos = _CMU_STATUS_AUXHFRCORDY_SHIFT; |
| #if defined(_SILICON_LABS_32B_SERIES_1) |
| ensBitPos = _CMU_STATUS_AUXHFRCOENS_SHIFT; |
| #endif |
| break; |
| |
| case cmuOsc_LFRCO: |
| enBit = CMU_OSCENCMD_LFRCOEN; |
| disBit = CMU_OSCENCMD_LFRCODIS; |
| rdyBitPos = _CMU_STATUS_LFRCORDY_SHIFT; |
| #if defined(_SILICON_LABS_32B_SERIES_1) |
| ensBitPos = _CMU_STATUS_LFRCOENS_SHIFT; |
| #endif |
| break; |
| |
| case cmuOsc_LFXO: |
| enBit = CMU_OSCENCMD_LFXOEN; |
| disBit = CMU_OSCENCMD_LFXODIS; |
| rdyBitPos = _CMU_STATUS_LFXORDY_SHIFT; |
| #if defined(_SILICON_LABS_32B_SERIES_1) |
| ensBitPos = _CMU_STATUS_LFXOENS_SHIFT; |
| #endif |
| break; |
| |
| #if defined(_CMU_STATUS_USHFRCOENS_MASK) |
| case cmuOsc_USHFRCO: |
| enBit = CMU_OSCENCMD_USHFRCOEN; |
| disBit = CMU_OSCENCMD_USHFRCODIS; |
| rdyBitPos = _CMU_STATUS_USHFRCORDY_SHIFT; |
| #if defined(_SILICON_LABS_32B_SERIES_1) |
| ensBitPos = _CMU_STATUS_USHFRCOENS_SHIFT; |
| #endif |
| break; |
| #endif |
| |
| default: |
| /* Undefined clock source, cmuOsc_CLKIN0 or cmuOsc_ULFRCO. ULFRCO is always enabled |
| and cannot be disabled. In other words,the definition of cmuOsc_ULFRCO is primarily |
| intended for information: the ULFRCO is always on. */ |
| EFM_ASSERT(false); |
| return; |
| } |
| |
| if (enable) { |
| #if defined(_CMU_HFXOCTRL_MASK) |
| bool firstHfxoEnable = false; |
| |
| /* Enabling the HFXO for the first time requires special handling. |
| * PEAKDETSHUTOPTMODE field of the HFXOCTRL register is used to see if this is the |
| * first time the HFXO is enabled. */ |
| if (osc == cmuOsc_HFXO) { |
| if (getHfxoTuningMode() == HFXO_TUNING_MODE_AUTO) { |
| /* REGPWRSEL must be set to DVDD before the HFXO can be enabled. */ |
| #if defined(_EMU_PWRCTRL_REGPWRSEL_MASK) |
| EFM_ASSERT((EMU->PWRCTRL & EMU_PWRCTRL_REGPWRSEL_DVDD) != 0UL); |
| #endif |
| |
| firstHfxoEnable = true; |
| /* The first time that an external clock is enabled, switch to CMD mode to make sure that |
| * only SCO and not PDA tuning is performed. */ |
| if ((CMU->HFXOCTRL & (_CMU_HFXOCTRL_MODE_MASK)) == CMU_HFXOCTRL_MODE_DIGEXTCLK) { |
| setHfxoTuningMode(HFXO_TUNING_MODE_CMD); |
| } |
| } |
| } |
| #endif |
| CMU->OSCENCMD = enBit; |
| |
| #if defined(_SILICON_LABS_32B_SERIES_1) |
| /* Always wait for ENS to go high. */ |
| while (BUS_RegBitRead(&CMU->STATUS, ensBitPos) == 0UL) { |
| } |
| #endif |
| |
| /* Wait for the clock to become ready after enable. */ |
| if (wait) { |
| while (BUS_RegBitRead(&CMU->STATUS, rdyBitPos) == 0UL) { |
| } |
| #if defined(_SILICON_LABS_32B_SERIES_1) |
| if ((osc == cmuOsc_HFXO) && firstHfxoEnable) { |
| if ((CMU->HFXOCTRL & _CMU_HFXOCTRL_MODE_MASK) |
| == CMU_HFXOCTRL_MODE_DIGEXTCLK) { |
| #if defined(CMU_CMD_HFXOSHUNTOPTSTART) |
| /* External clock mode should only do shunt current optimization. */ |
| (void)CMU_OscillatorTuningOptimize(cmuOsc_HFXO, |
| cmuHFXOTuningMode_ShuntCommand, |
| true); |
| #endif |
| } else { |
| /* Wait for the peak detection and shunt current optimization |
| to complete. */ |
| (void)CMU_OscillatorTuningWait(cmuOsc_HFXO, cmuHFXOTuningMode_Auto); |
| } |
| |
| /* Disable the HFXO again to apply the trims. Apply trim from |
| HFXOTRIMSTATUS when disabled. */ |
| hfxoTrimStatus = CMU_OscillatorTuningGet(cmuOsc_HFXO); |
| CMU_OscillatorEnable(cmuOsc_HFXO, false, true); |
| CMU_OscillatorTuningSet(cmuOsc_HFXO, hfxoTrimStatus); |
| |
| /* Restart in CMD mode. */ |
| CMU->OSCENCMD = enBit; |
| while (BUS_RegBitRead(&CMU->STATUS, rdyBitPos) == 0UL) { |
| } |
| } |
| #endif |
| } |
| } else { |
| CMU->OSCENCMD = disBit; |
| |
| #if defined(_SILICON_LABS_32B_SERIES_1) |
| /* Always wait for ENS to go low. */ |
| while (BUS_RegBitRead(&CMU->STATUS, ensBitPos) != 0UL) { |
| } |
| #endif |
| } |
| } |
| |
| /***************************************************************************//** |
| * @brief |
| * Get the oscillator frequency tuning setting. |
| * |
| * @param[in] osc |
| * An oscillator to get tuning value for, one of the following: |
| * @li #cmuOsc_LFRCO |
| * @li #cmuOsc_HFRCO @if _CMU_USHFRCOCTRL_TUNING_MASK |
| * @li #cmuOsc_USHFRCO |
| * @endif |
| * @li #cmuOsc_AUXHFRCO |
| * @li #cmuOsc_HFXO if CMU_HFXOCTRL_PEAKDETSHUNTOPTMODE is defined |
| * |
| * @return |
| * The oscillator frequency tuning setting in use. |
| ******************************************************************************/ |
| uint32_t CMU_OscillatorTuningGet(CMU_Osc_TypeDef osc) |
| { |
| uint32_t ret; |
| |
| switch (osc) { |
| case cmuOsc_LFRCO: |
| ret = (CMU->LFRCOCTRL & _CMU_LFRCOCTRL_TUNING_MASK) |
| >> _CMU_LFRCOCTRL_TUNING_SHIFT; |
| break; |
| |
| case cmuOsc_HFRCO: |
| ret = (CMU->HFRCOCTRL & _CMU_HFRCOCTRL_TUNING_MASK) |
| >> _CMU_HFRCOCTRL_TUNING_SHIFT; |
| break; |
| |
| #if defined (_CMU_USHFRCOCTRL_TUNING_MASK) |
| case cmuOsc_USHFRCO: |
| ret = (CMU->USHFRCOCTRL & _CMU_USHFRCOCTRL_TUNING_MASK) |
| >> _CMU_USHFRCOCTRL_TUNING_SHIFT; |
| break; |
| #endif |
| |
| case cmuOsc_AUXHFRCO: |
| ret = (CMU->AUXHFRCOCTRL & _CMU_AUXHFRCOCTRL_TUNING_MASK) |
| >> _CMU_AUXHFRCOCTRL_TUNING_SHIFT; |
| break; |
| |
| #if defined(_CMU_HFXOCTRL_PEAKDETSHUNTOPTMODE_MASK) |
| case cmuOsc_HFXO: |
| ret = CMU->HFXOTRIMSTATUS & (_CMU_HFXOTRIMSTATUS_IBTRIMXOCORE_MASK |
| #if defined(_CMU_HFXOTRIMSTATUS_REGISH_MASK) |
| | _CMU_HFXOTRIMSTATUS_REGISH_MASK |
| #endif |
| ); |
| break; |
| #endif |
| |
| default: |
| ret = 0; |
| EFM_ASSERT(false); |
| break; |
| } |
| |
| return ret; |
| } |
| |
| /***************************************************************************//** |
| * @brief |
| * Set the oscillator frequency tuning control. |
| * |
| * @note |
| * Oscillator tuning is done during production and the tuning value is |
| * automatically loaded after reset. Changing the tuning value from the |
| * calibrated value is for more advanced use. Certain oscillators also have |
| * build-in tuning optimization. |
| * |
| * @param[in] osc |
| * An oscillator to set tuning value for, one of the following: |
| * @li #cmuOsc_LFRCO |
| * @li #cmuOsc_HFRCO @if _CMU_USHFRCOCTRL_TUNING_MASK |
| * @li #cmuOsc_USHFRCO |
| * @endif |
| * @li #cmuOsc_AUXHFRCO |
| * @li #cmuOsc_HFXO if PEAKDETSHUNTOPTMODE is available. Note that CMD mode is set. |
| * |
| * @param[in] val |
| * The oscillator frequency tuning setting to use. |
| ******************************************************************************/ |
| void CMU_OscillatorTuningSet(CMU_Osc_TypeDef osc, uint32_t val) |
| { |
| #if defined(_CMU_HFXOSTEADYSTATECTRL_REGISH_MASK) |
| uint32_t regIshUpper; |
| #endif |
| |
| switch (osc) { |
| case cmuOsc_LFRCO: |
| EFM_ASSERT(val <= (_CMU_LFRCOCTRL_TUNING_MASK |
| >> _CMU_LFRCOCTRL_TUNING_SHIFT)); |
| val &= (_CMU_LFRCOCTRL_TUNING_MASK >> _CMU_LFRCOCTRL_TUNING_SHIFT); |
| #if defined(_SILICON_LABS_32B_SERIES_1) |
| while (BUS_RegBitRead(&CMU->SYNCBUSY, |
| _CMU_SYNCBUSY_LFRCOBSY_SHIFT) != 0UL) { |
| } |
| #endif |
| CMU->LFRCOCTRL = (CMU->LFRCOCTRL & ~(_CMU_LFRCOCTRL_TUNING_MASK)) |
| | (val << _CMU_LFRCOCTRL_TUNING_SHIFT); |
| break; |
| |
| case cmuOsc_HFRCO: |
| EFM_ASSERT(val <= (_CMU_HFRCOCTRL_TUNING_MASK |
| >> _CMU_HFRCOCTRL_TUNING_SHIFT)); |
| val &= (_CMU_HFRCOCTRL_TUNING_MASK >> _CMU_HFRCOCTRL_TUNING_SHIFT); |
| #if defined(_SILICON_LABS_32B_SERIES_1) |
| while (BUS_RegBitRead(&CMU->SYNCBUSY, |
| _CMU_SYNCBUSY_HFRCOBSY_SHIFT) != 0UL) { |
| } |
| #endif |
| CMU->HFRCOCTRL = (CMU->HFRCOCTRL & ~(_CMU_HFRCOCTRL_TUNING_MASK)) |
| | (val << _CMU_HFRCOCTRL_TUNING_SHIFT); |
| break; |
| |
| #if defined (_CMU_USHFRCOCTRL_TUNING_MASK) |
| case cmuOsc_USHFRCO: |
| EFM_ASSERT(val <= (_CMU_USHFRCOCTRL_TUNING_MASK |
| >> _CMU_USHFRCOCTRL_TUNING_SHIFT)); |
| val &= (_CMU_USHFRCOCTRL_TUNING_MASK >> _CMU_USHFRCOCTRL_TUNING_SHIFT); |
| #if defined(_SILICON_LABS_32B_SERIES_1) |
| while (BUS_RegBitRead(&CMU->SYNCBUSY, _CMU_SYNCBUSY_USHFRCOBSY_SHIFT)) { |
| } |
| #endif |
| CMU->USHFRCOCTRL = (CMU->USHFRCOCTRL & ~(_CMU_USHFRCOCTRL_TUNING_MASK)) |
| | (val << _CMU_USHFRCOCTRL_TUNING_SHIFT); |
| break; |
| #endif |
| |
| case cmuOsc_AUXHFRCO: |
| EFM_ASSERT(val <= (_CMU_AUXHFRCOCTRL_TUNING_MASK |
| >> _CMU_AUXHFRCOCTRL_TUNING_SHIFT)); |
| val &= (_CMU_AUXHFRCOCTRL_TUNING_MASK >> _CMU_AUXHFRCOCTRL_TUNING_SHIFT); |
| #if defined(_SILICON_LABS_32B_SERIES_1) |
| while (BUS_RegBitRead(&CMU->SYNCBUSY, |
| _CMU_SYNCBUSY_AUXHFRCOBSY_SHIFT) != 0UL) { |
| } |
| #endif |
| CMU->AUXHFRCOCTRL = (CMU->AUXHFRCOCTRL & ~(_CMU_AUXHFRCOCTRL_TUNING_MASK)) |
| | (val << _CMU_AUXHFRCOCTRL_TUNING_SHIFT); |
| break; |
| |
| #if defined(_CMU_HFXOCTRL_PEAKDETSHUNTOPTMODE_MASK) |
| case cmuOsc_HFXO: |
| |
| /* Do set PEAKDETSHUNTOPTMODE or HFXOSTEADYSTATECTRL if HFXO is enabled. */ |
| EFM_ASSERT((CMU->STATUS & CMU_STATUS_HFXOENS) == 0UL); |
| |
| /* Switch to command mode. Automatic SCO and PDA calibration is not done |
| at the next enable. Set user REGISH, REGISHUPPER, and IBTRIMXOCORE. */ |
| CMU->HFXOCTRL = (CMU->HFXOCTRL & ~_CMU_HFXOCTRL_PEAKDETSHUNTOPTMODE_MASK) |
| | CMU_HFXOCTRL_PEAKDETSHUNTOPTMODE_CMD; |
| |
| #if defined(_CMU_HFXOSTEADYSTATECTRL_REGISH_MASK) |
| regIshUpper = getRegIshUpperVal((val & _CMU_HFXOSTEADYSTATECTRL_REGISH_MASK) |
| >> _CMU_HFXOSTEADYSTATECTRL_REGISH_SHIFT); |
| CMU->HFXOSTEADYSTATECTRL = (CMU->HFXOSTEADYSTATECTRL |
| & ~(_CMU_HFXOSTEADYSTATECTRL_IBTRIMXOCORE_MASK |
| | _CMU_HFXOSTEADYSTATECTRL_REGISH_MASK |
| | _CMU_HFXOSTEADYSTATECTRL_REGISHUPPER_MASK)) |
| | val |
| | regIshUpper; |
| #else |
| CMU->HFXOSTEADYSTATECTRL = (CMU->HFXOSTEADYSTATECTRL |
| & ~_CMU_HFXOSTEADYSTATECTRL_IBTRIMXOCORE_MASK) |
| | val; |
| #endif |
| |
| break; |
| #endif |
| |
| default: |
| EFM_ASSERT(false); |
| break; |
| } |
| } |
| |
| #if defined(_CMU_HFXOCTRL_PEAKDETSHUNTOPTMODE_MASK) || defined(_CMU_HFXOCTRL_PEAKDETMODE_MASK) |
| /***************************************************************************//** |
| * @brief |
| * Wait for the oscillator tuning optimization. |
| * |
| * @param[in] osc |
| * An oscillator to set tuning value for, one of the following: |
| * @li #cmuOsc_HFXO |
| * |
| * @param[in] mode |
| * Tuning optimization mode. |
| * |
| * @return |
| * Returns false on invalid parameters or oscillator error status. |
| ******************************************************************************/ |
| bool CMU_OscillatorTuningWait(CMU_Osc_TypeDef osc, |
| CMU_HFXOTuningMode_TypeDef mode) |
| { |
| uint32_t waitFlags; |
| EFM_ASSERT(osc == cmuOsc_HFXO); |
| |
| /* Currently implemented for HFXO with PEAKDETSHUNTOPTMODE only. */ |
| (void)osc; |
| |
| if (getHfxoTuningMode() == HFXO_TUNING_MODE_AUTO) { |
| waitFlags = HFXO_TUNING_READY_FLAGS; |
| } else { |
| /* Set wait flags for each command and wait. */ |
| switch (mode) { |
| #if defined(_CMU_STATUS_HFXOSHUNTOPTRDY_MASK) |
| case cmuHFXOTuningMode_ShuntCommand: |
| waitFlags = CMU_STATUS_HFXOSHUNTOPTRDY; |
| break; |
| #endif |
| case cmuHFXOTuningMode_Auto: |
| waitFlags = HFXO_TUNING_READY_FLAGS; |
| break; |
| |
| #if defined(CMU_CMD_HFXOSHUNTOPTSTART) |
| case cmuHFXOTuningMode_PeakShuntCommand: |
| waitFlags = HFXO_TUNING_READY_FLAGS; |
| break; |
| #endif |
| |
| default: |
| waitFlags = _CMU_STATUS_MASK; |
| EFM_ASSERT(false); |
| break; |
| } |
| } |
| while ((CMU->STATUS & waitFlags) != waitFlags) { |
| } |
| |
| #if defined(CMU_IF_HFXOPEAKDETERR) |
| /* Check error flags. */ |
| if ((waitFlags & CMU_STATUS_HFXOPEAKDETRDY) != 0UL) { |
| return (CMU->IF & CMU_IF_HFXOPEAKDETERR) != 0UL ? true : false; |
| } |
| #endif |
| return true; |
| } |
| |
| /***************************************************************************//** |
| * @brief |
| * Start and optionally wait for the oscillator tuning optimization. |
| * |
| * @param[in] osc |
| * An oscillator to set tuning value for, one of the following: |
| * @li #cmuOsc_HFXO |
| * |
| * @param[in] mode |
| * Tuning optimization mode. |
| * |
| * @param[in] wait |
| * Wait for tuning optimization to complete. |
| * true - wait for tuning optimization to complete. |
| * false - return without waiting. |
| * |
| * @return |
| * Returns false on invalid parameters or oscillator error status. |
| ******************************************************************************/ |
| bool CMU_OscillatorTuningOptimize(CMU_Osc_TypeDef osc, |
| CMU_HFXOTuningMode_TypeDef mode, |
| bool wait) |
| { |
| switch (osc) { |
| case cmuOsc_HFXO: |
| if ((unsigned)mode != 0U) { |
| #if defined(CMU_IF_HFXOPEAKDETERR) |
| /* Clear the error flag before command write. */ |
| CMU->IFC = CMU_IFC_HFXOPEAKDETERR; |
| #endif |
| CMU->CMD = (uint32_t)mode; |
| } |
| if (wait) { |
| return CMU_OscillatorTuningWait(osc, mode); |
| } |
| break; |
| |
| default: |
| EFM_ASSERT(false); |
| break; |
| } |
| return true; |
| } |
| #endif |
| |
| /**************************************************************************//** |
| * @brief |
| * Determine if the currently selected PCNTn clock used is external or LFBCLK. |
| * |
| * @param[in] instance |
| * PCNT instance number to get currently selected clock source for. |
| * |
| * @return |
| * @li true - selected clock is external clock. |
| * @li false - selected clock is LFBCLK. |
| *****************************************************************************/ |
| bool CMU_PCNTClockExternalGet(unsigned int instance) |
| { |
| uint32_t setting; |
| |
| switch (instance) { |
| #if defined(_CMU_PCNTCTRL_PCNT0CLKEN_MASK) |
| case 0: |
| setting = CMU->PCNTCTRL & CMU_PCNTCTRL_PCNT0CLKSEL_PCNT0S0; |
| break; |
| |
| #if defined(_CMU_PCNTCTRL_PCNT1CLKEN_MASK) |
| case 1: |
| setting = CMU->PCNTCTRL & CMU_PCNTCTRL_PCNT1CLKSEL_PCNT1S0; |
| break; |
| |
| #if defined(_CMU_PCNTCTRL_PCNT2CLKEN_MASK) |
| case 2: |
| setting = CMU->PCNTCTRL & CMU_PCNTCTRL_PCNT2CLKSEL_PCNT2S0; |
| break; |
| #endif |
| #endif |
| #endif |
| |
| default: |
| setting = 0; |
| break; |
| } |
| return setting > 0UL ? true : false; |
| } |
| |
| /**************************************************************************//** |
| * @brief |
| * Select the PCNTn clock. |
| * |
| * @param[in] instance |
| * PCNT instance number to set selected clock source for. |
| * |
| * @param[in] external |
| * Set to true to select the external clock, false to select LFBCLK. |
| *****************************************************************************/ |
| void CMU_PCNTClockExternalSet(unsigned int instance, bool external) |
| { |
| #if defined(PCNT_PRESENT) |
| uint32_t setting = 0; |
| |
| EFM_ASSERT(instance < (unsigned)PCNT_COUNT); |
| |
| if (external) { |
| setting = 1; |
| } |
| |
| BUS_RegBitWrite(&(CMU->PCNTCTRL), (instance * 2U) + 1U, setting); |
| |
| #else |
| (void)instance; /* An unused parameter */ |
| (void)external; /* An unused parameter */ |
| #endif |
| } |
| |
| #if defined(_CMU_USHFRCOCONF_BAND_MASK) |
| /***************************************************************************//** |
| * @brief |
| * Get USHFRCO band in use. |
| * |
| * @return |
| * USHFRCO band in use. |
| ******************************************************************************/ |
| CMU_USHFRCOBand_TypeDef CMU_USHFRCOBandGet(void) |
| { |
| return (CMU_USHFRCOBand_TypeDef)((CMU->USHFRCOCONF |
| & _CMU_USHFRCOCONF_BAND_MASK) |
| >> _CMU_USHFRCOCONF_BAND_SHIFT); |
| } |
| #endif |
| |
| #if defined(_CMU_USHFRCOCONF_BAND_MASK) |
| /***************************************************************************//** |
| * @brief |
| * Set the USHFRCO band to use. |
| * |
| * @param[in] band |
| * USHFRCO band to activate. |
| ******************************************************************************/ |
| void CMU_USHFRCOBandSet(CMU_USHFRCOBand_TypeDef band) |
| { |
| uint32_t tuning; |
| uint32_t fineTuning; |
| |
| /* Cannot switch band if USHFRCO is already selected as HF clock. */ |
| EFM_ASSERT(CMU_ClockSelectGet(cmuClock_HF) != cmuSelect_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 48 MHz */ |
| BUS_RegBitWrite(&CMU->USHFRCOCONF, _CMU_USHFRCOCONF_USHFRCODIV2DIS_SHIFT, 0); |
| ushfrcoFreq = 48000000UL; |
| break; |
| |
| default: |
| EFM_ASSERT(false); |
| return; |
| } |
| |
| /* Set band and tuning. */ |
| CMU->USHFRCOCONF = (CMU->USHFRCOCONF & ~_CMU_USHFRCOCONF_BAND_MASK) |
| | (band << _CMU_USHFRCOCONF_BAND_SHIFT); |
| CMU->USHFRCOCTRL = (CMU->USHFRCOCTRL & ~_CMU_USHFRCOCTRL_TUNING_MASK) |
| | (tuning << _CMU_USHFRCOCTRL_TUNING_SHIFT); |
| CMU->USHFRCOTUNE = (CMU->USHFRCOTUNE & ~_CMU_USHFRCOTUNE_FINETUNING_MASK) |
| | (fineTuning << _CMU_USHFRCOTUNE_FINETUNING_SHIFT); |
| |
| /* Disable the clock divider after switching the band from 48 to 24 MHz. */ |
| if (band == cmuUSHFRCOBand_24MHz) { |
| BUS_RegBitWrite(&CMU->USHFRCOCONF, _CMU_USHFRCOCONF_USHFRCODIV2DIS_SHIFT, 1); |
| } |
| } |
| #endif |
| |
| #endif // defined(_SILICON_LABS_32B_SERIES_2) |
| |
| /** @} (end addtogroup CMU) */ |
| /** @} (end addtogroup emlib) */ |
| #endif /* defined(CMU_PRESENT) */ |