nuttx/arch/arm/src/efm32/efm32_clockconfig.c

971 lines
29 KiB
C

/****************************************************************************
* arch/arm/src/efm32/efm32_clockconfig.c
*
* Copyright (C) 2014 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name NuttX nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <arch/board/board.h>
#include <stdint.h>
#include <stdbool.h>
#include <assert.h>
#include "up_arch.h"
#include "chip.h"
#include "itm_syslog.h"
#include "efm32_gpio.h"
#include "chip/efm32_msc.h"
#include "chip/efm32_cmu.h"
#include "chip/efm32_gpio.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* BOARD Configuration ******************************************************/
/* Pre-scalers not currently implemented */
#if defined(CONFIG_EFM32_EFM32GG) && defined(BOARD_HFCLKDIV) && BOARD_HFCLKDIV != 0
# error HFCLK divisor not yet supported
#endif
#if defined(BOARD_HFCORECLKDIV) && BOARD_HFCORECLKDIV != 0
# error HFCORECLK divisor not yet supported
#endif
#if defined(BOARD_HFPERCLKDIV) && BOARD_HFPERCLKDIV != 0
# error HFPERCLK divisor not yet supported
#endif
#ifdef BOARD_LFACLK_ULFRCO
# define BOARD_LFA_ULFCO_ENABLE true
#else
# define BOARD_LFA_ULFCO_ENABLE false
#endif
#ifdef BOARD_LFBCLK_ULFRCO
# define BOARD_LFB_ULFCO_ENABLE true
#else
# define BOARD_LFB_ULFCO_ENABLE false
#endif
#ifndef CONFIG_EFM32_LECLOCK
# if ( ( BOARD_LFACLKSEL != _CMU_LFCLKSEL_LFA_DISABLED ) || \
( BOARD_LFBCLKSEL != _CMU_LFCLKSEL_LFB_DISABLED ) || \
( defined ( CONFIG_EFM32_RTC_BURTC ) ) || \
( defined ( CONFIG_EFM32_LEUART0 ) ) || \
( defined ( CONFIG_EFM32_LEUART1 ) ) \
)
# define CONFIG_EFM32_LECLOCK
# endif
#endif
/****************************************************************************
* Public Data
****************************************************************************/
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: efm32_synchronize
*
* Description:
* Wait for ongoing sync of register(s) to low frequency domain to
* complete.
*
* Input Parameters:
* bitset - Bitset corresponding to SYNCBUSY register defined bits,
* indicating registers that must complete any ongoing
* synchronization.
*
* Returned Value:
* None
*
****************************************************************************/
static inline void efm32_synchronize(uint32_t bitset)
{
/* Avoid deadlock if modifying a register again after freeze mode is
* activated.
*/
if ((getreg32(EFM32_CMU_FREEZE) & CMU_FREEZE_REGFREEZE) == 0)
{
/* Wait for any pending previous write operation to complete */
while ((getreg32(EFM32_CMU_SYNCBUSY) & bitset) != 0);
}
}
/****************************************************************************
* Name: efm32_statuswait
*
* Description:
* Wait for ongoing CMU status bit(s) to become set
*
* Input Parameters:
* bitset - Bitset corresponding to STATUS register defined bits,
* indicating events that we are waiting for.
*
* Returned Value:
* None
*
****************************************************************************/
static inline void efm32_statuswait(uint32_t bitset)
{
/* Wait for clock to stabilize if requested */
while ((getreg32(EFM32_CMU_STATUS) & bitset) == 0);
}
/****************************************************************************
* Name: efm32_enable_XXX
*
* Description:
* Enable specific oscillators
*
****************************************************************************/
static void efm32_enable_lfrco(void)
{
/* Enable the LFRCO */
putreg32(CMU_OSCENCMD_LFRCOEN, EFM32_CMU_OSCENCMD);
efm32_statuswait(CMU_STATUS_LFRCORDY);
}
static void efm32_enable_lfxo(void)
{
/* Enable the LFXO */
putreg32(CMU_OSCENCMD_LFXOEN, EFM32_CMU_OSCENCMD);
efm32_statuswait(CMU_STATUS_LFXORDY);
}
static inline void efm32_enable_hfrco(void)
{
/* Enable the HFRCO */
putreg32(CMU_OSCENCMD_HFRCOEN, EFM32_CMU_OSCENCMD);
efm32_statuswait(CMU_STATUS_HFRCORDY);
}
static void efm32_enable_hfxo(void)
{
/* Enable the HFXO */
putreg32(CMU_OSCENCMD_HFXOEN, EFM32_CMU_OSCENCMD);
efm32_statuswait(CMU_STATUS_HFXORDY);
}
static inline void efm32_enable_auxhfrco(void)
{
/* Enable the HFXO */
putreg32(CMU_OSCENCMD_AUXHFRCOEN, EFM32_CMU_OSCENCMD);
efm32_statuswait(CMU_STATUS_AUXHFRCORDY);
}
/****************************************************************************
* Name: efm32_enable_leclocking
*
* Description:
* Enable HFCORE clocking to the LE
*
****************************************************************************/
static void efm32_enable_leclocking(void)
{
uint32_t regval;
regval = getreg32(EFM32_CMU_HFCORECLKEN0);
regval |= CMU_HFCORECLKEN0_LE;
putreg32(regval, EFM32_CMU_HFCORECLKEN0);
}
/****************************************************************************
* Name: efm32_maxwaitstates
*
* Description:
* Configure flash access wait states to most maximum number of wait
* states, preserving the SCBTP setting.
*
****************************************************************************/
static void efm32_maxwaitstates(void)
{
uint32_t regval;
uint32_t mode;
/* Get the READCTRL register content and mask out the mode setting */
regval = getreg32(EFM32_MSC_READCTRL);
mode = regval & _MSC_READCTRL_MODE_MASK;
regval &= ~_MSC_READCTRL_MODE_MASK;
/* SCBTP mode? */
if (mode == MSC_READCTRL_MODE_WS0SCBTP
|| mode == MSC_READCTRL_MODE_WS1SCBTP
#ifdef MSC_READCTRL_MODE_WS2SCBTP
|| mode == MSC_READCTRL_MODE_WS2SCBTP
#endif
)
{
/* Yes.. select the mximum number of wait states with SCBTP */
regval |= MSC_READCTRL_MODE_WSMAXSCBTP;
}
else
{
/* No.. select the mximum number of wait states without SCBTP */
regval |= MSC_READCTRL_MODE_WSMAX;
}
/* And save the update READCTRL register */
putreg32(regval, EFM32_MSC_READCTRL);
}
/****************************************************************************
* Name: efm32_setwaitstates
*
* Description:
* Configure the optimal number of flash access wait states, preserving
* the SCBTP setting.
*
****************************************************************************/
static void efm32_setwaitstates(uint32_t hfcoreclk)
{
uint32_t regval;
uint32_t mode;
bool scbtp;
/* SCBTP mode? */
regval = getreg32(EFM32_MSC_READCTRL);
mode = regval & _MSC_READCTRL_MODE_MASK;
scbtp = (mode == MSC_READCTRL_MODE_WS0SCBTP
|| mode == MSC_READCTRL_MODE_WS1SCBTP
#ifdef MSC_READCTRL_MODE_WS2SCBTP
|| mode == MSC_READCTRL_MODE_WS2SCBTP
#endif
);
/* Select the number of wait states based on the HFCORECLK frequency */
regval &= ~_MSC_READCTRL_MODE_MASK;
/* We can't do more than 2 wait states in any configuration */
#ifdef MSC_READCTRL_MODE_WS2
if (hfcoreclk > CMU_MAX_FREQ_2WS)
{
PANIC();
}
else
#endif
/* Check if we can use 2 wait states */
if (hfcoreclk > CMU_MAX_FREQ_1WS)
{
#ifdef MSC_READCTRL_MODE_WS2
/* Yes.. select 2 wait states */
regval |= (scbtp ? MSC_READCTRL_MODE_WS2SCBTP : MSC_READCTRL_MODE_WS2);
#else
/* No.. this MCU does not support 2 wait states */
PANIC();
#endif
}
/* Check if we can use 1 wait states */
else if (hfcoreclk > CMU_MAX_FREQ_0WS)
{
/* Yes.. select 1 wait state */
regval |= (scbtp ? MSC_READCTRL_MODE_WS1SCBTP : MSC_READCTRL_MODE_WS1);
}
/* Check if we can use no wait states */
else
{
/* Select no wait states */
regval |= (scbtp ? MSC_READCTRL_MODE_WS0SCBTP : MSC_READCTRL_MODE_WS0);
}
/* And save the update READCTRL register */
putreg32(regval, EFM32_MSC_READCTRL);
}
/****************************************************************************
* Name: efm32_hfclk_config
*
* Description:
* Configure the High Frequency Clock, HFCLK.
*
* HFCLK is the selected High Frequency Clock. This clock is used by the
* CMU and drives the two prescalers that generate HFCORECLK and HFPERCLK.
* The HFCLK can be driven by a high-frequency oscillator (HFRCO or HFXO)
* or one of the low-frequency oscillators (LFRCO or LFXO). By default the
* HFRCO is selected. To change the selected HFCLK write to HFCLKSEL in
* CMU_CMD. The HFCLK is running in EM0 and EM1.
*
* HFCLK can optionally be divided down by setting HFCLKDIV in CMU_CTRL to
* a non-zero value. This divides down HFCLK to all high frequency
* components except the USB Core and is typically used to save energy in
* USB applications where the system is not required to run at 48 MHz.
* Combined with the HFCORECLK and HFPERCLK prescalers the HFCLK divider
* also allows for more flexible clock division.
*
****************************************************************************/
static inline uint32_t efm32_hfclk_config(uint32_t hfclksel, uint32_t hfclkdiv)
{
uint32_t frequency;
#ifdef CMU_CTRL_HFLE
uint32_t regval;
#endif
/* The HFRCO oscillator is selected by hardware as the clock source for
* HFCLK when the device starts up . After reset, the HFRCO frequency is
* 14 MHz.
*
* First enable the oscillator and wait for the oscillator to become ready
* before switching the clock source. This way, the system continues to run
* on the HFRCO until the oscillator has timed out and provides a reliable
* clock.
*/
switch (hfclksel)
{
case _CMU_CMD_HFCLKSEL_LFRCO:
{
frequency = BOARD_LFRCO_FREQUENCY;
efm32_enable_lfrco();
}
break;
case _CMU_CMD_HFCLKSEL_LFXO:
{
frequency = BOARD_LFXO_FREQUENCY;
efm32_enable_lfxo();
}
break;
case _CMU_CMD_HFCLKSEL_HFRCO:
{
frequency = BOARD_HFRCO_FREQUENCY;
efm32_enable_hfrco();
}
break;
case _CMU_CMD_HFCLKSEL_HFXO:
{
frequency = BOARD_HFXO_FREQUENCY;
#ifdef CMU_CTRL_HFLE
#if BOARD_HFXO_FREQUENCY > CMU_MAX_FREQ_HFLE
/* Adjust HFXO buffer current for high crystal frequencies, enable HFLE
* for frequencies above CMU_MAX_FREQ_HFLE.
*
* We must also have HFLE enabled to access some LE peripherals >= 32MHz.
*/
regval = getreg32(EFM32_CMU_CTRL);
regval &= ~_CMU_CTRL_HFXOBUFCUR_MASK;
regval |= CMU_CTRL_HFXOBUFCUR_BOOSTABOVE32MHZ | CMU_CTRL_HFLE;
putreg32(regval, EFM32_CMU_CTRL);
/* Set DIV4 factor for peripheral clock if HFCORE clock for LE is
* enabled.
*/
if ((getreg32(EFM32_CMU_HFCORECLKEN0) & CMU_HFCORECLKEN0_LE) != 0)
{
regval = getreg32(EFM32_CMU_HFCORECLKDIV);
regval |= CMU_HFCORECLKDIV_HFCORECLKLEDIV_DIV4;
putreg32(regval, EFM32_CMU_HFCORECLKDIV);
}
#else
/* No boost... no HFLE */
regval = getreg32(EFM32_CMU_CTRL);
regval &= ~(_CMU_CTRL_HFXOBUFCUR_MASK | CMU_CTRL_HFLE);
regval |= CMU_CTRL_HFXOBUFCUR_BOOSTUPTO32MHZ;
putreg32(regval, EFM32_CMU_CTRL);
#endif
#endif
/* Enable the HFXO */
efm32_enable_hfxo();
}
break;
#ifdef CONFIG_DEBUG
default:
PANIC();
#endif
}
/* Set the maximum number of FLASH wait states before selecting the new
* HFCLK source.
*/
efm32_maxwaitstates();
/* Switch to selected oscillator */
putreg32(hfclksel << _CMU_CMD_HFCLKSEL_SHIFT, EFM32_CMU_CMD);
/* Now select the optimal number of FLASH wait states */
efm32_setwaitstates(frequency);
return frequency;
}
/****************************************************************************
* Name: efm32_hfcoreclk_config
*
* Description:
* Configure the High Frequency Core Clock, HFCORECLK.
*
* HFCORECLK is a prescaled version of HFCLK. This clock drives the Core
* Modules, which consists of the CPU and modules that are tightly coupled
* to the CPU, e.g. MSC, DMA etc. This also includes the interface to the
* Low Energy Peripherals. Some of the modules that are driven by this
* clock can be clock gated completely when not in use. This is done by
* clearing the clock enable bit for the specific module in
* CMU_HFCORECLKEN0. The frequency of HFCORECLK is set using the
* CMU_HFCORECLKDIV register. The setting can be changed dynamically and
* the new setting takes effect immediately.
*
* The USB Core clock (USBC) is always undivided regardless of the
* HFCLKDIV setting. When the USB Core is active this clock must be
* switched to a 32 kHz clock (LFRCO or LFXO) when entering EM2. The USB
* Core uses this clock for monitoring the USB bus. The switch is done by
* writing USBCCLKSEL in CMU_CMD. The currently active clock can be
* checked by reading CMU_STATUS. The clock switch can take up to 1.5 32
* kHz cycle (45 us). To avoid polling the clock selection status when
* switching switching from 32 kHz to HFCLK when coming up from EM2 the
* USBCHFCLKSEL interrupt can be used. EM3 is not supported when the USB
* is active.
*
****************************************************************************/
#ifdef CONFIG_EFM32_LECLOCK
uint32_t efm32_coreleclk_config(int frequency)
{
#ifdef CMU_CTRL_HFLE
uint32_t regval;
/* Check if the core frequency is higher than CMU_MAX_FREQ_HFLE */
if (frequency > CMU_MAX_FREQ_HFLE)
{
/* Enable HFLE */
regval = getreg32(EFM32_CMU_CTRL);
regval |= CMU_CTRL_HFLE;
putreg32(regval, EFM32_CMU_CTRL);
/* Enable DIV4 factor for peripheral clock */
regval = getreg32(EFM32_CMU_HFCORECLKDIV);
regval |= CMU_HFCORECLKDIV_HFCORECLKLEDIV_DIV4;
putreg32(regval, EFM32_CMU_HFCORECLKDIV);
frequency /= 4;
}
#else
frequency /= 2;
#endif
/* Enable core clocking to the LE */
efm32_enable_leclocking();
return frequency;
}
#else
# define efm32_coreleclk_config 0
#endif
/****************************************************************************
* Name: efm32_hfcoreclk_config
*
* Description:
* Configure the High Frequency Core Clock, HFCORECLK.
*
* HFCORECLK is a prescaled version of HFCLK. This clock drives the Core
* Modules, which consists of the CPU and modules that are tightly coupled
* to the CPU, e.g. MSC, DMA etc. This also includes the interface to the
* Low Energy Peripherals. Some of the modules that are driven by this
* clock can be clock gated completely when not in use. This is done by
* clearing the clock enable bit for the specific module in
* CMU_HFCORECLKEN0. The frequency of HFCORECLK is set using the
* CMU_HFCORECLKDIV register. The setting can be changed dynamically and
* the new setting takes effect immediately.
*
* The USB Core clock (USBC) is always undivided regardless of the
* HFCLKDIV setting. When the USB Core is active this clock must be
* switched to a 32 kHz clock (LFRCO or LFXO) when entering EM2. The USB
* Core uses this clock for monitoring the USB bus. The switch is done by
* writing USBCCLKSEL in CMU_CMD. The currently active clock can be
* checked by reading CMU_STATUS. The clock switch can take up to 1.5 32
* kHz cycle (45 us). To avoid polling the clock selection status when
* switching switching from 32 kHz to HFCLK when coming up from EM2 the
* USBCHFCLKSEL interrupt can be used. EM3 is not supported when the USB
* is active.
*
****************************************************************************/
static inline uint32_t efm32_hfcoreclk_config(uint32_t hfcoreclkdiv,
uint32_t hfclk)
{
/* REVISIT: Divider not currently used */
return hfclk;
}
/****************************************************************************
* Name: efm32_hfperclk_config
*
* Description:
* Configure the High Frequency Peripheral Clock, HFPERCLK.
*
* Like HFCORECLK, HFPERCLK can also be a prescaled version of HFCLK. This
* clock drives the High-Frequency Peripherals. All the peripherals that
* are driven by this clock can be clock gated completely when not in use.
* This is done by clearing the clock enable bit for the specific
* peripheral in CMU_HFPERCLKEN0. The frequency of HFPERCLK is set using
* the CMU_HFPERCLKDIV register. The setting can be changed dynamically
* and the new setting takes effect immediately.
*
****************************************************************************/
static inline uint32_t efm32_hfperclk_config(uint32_t hfperclkdiv,
uint32_t hfclk)
{
uint32_t regval;
unsigned int divider;
DEBUGASSERT(hfperclkdiv <= _CMU_HFPERCLKDIV_HFPERCLKDIV_HFCLK512);
/* Set the divider and enable the HFPERCLK */
regval = (hfperclkdiv << _CMU_HFPERCLKDIV_HFPERCLKDIV_SHIFT) |
CMU_HFPERCLKDIV_HFPERCLKEN;
putreg32(regval, EFM32_CMU_HFPERCLKDIV);
/* The value of hfperclkdiv is log2 of the arithmetic divisor:
* 0->1, 1->2, 2->4, 3->8, ... 9->512.
*/
divider = 1 << hfperclkdiv;
return hfclk / divider;
}
/****************************************************************************
* Name: efm32_lfaclk_config
*
* Description:
* Configure the Low Frequency A Clock, LFACLK.
*
* LFACLK is the selected clock for the Low Energy A Peripherals. There
* are four selectable sources for LFACLK: LFRCO, LFXO, HFCORECLK/2 and
* ULFRCO. In addition, the LFACLK can be disabled. From reset, the
* LFACLK source is set to LFRCO. However, note that the LFRCO is disabled
* from reset. The selection is configured using the LFA field in
* CMU_LFCLKSEL. The HFCORECLK/2 setting allows the Low Energy A
* Peripherals to be used as high-frequency peripherals.
*
* Each Low Energy Peripheral that is clocked by LFACLK has its own
* prescaler setting and enable bit. The prescaler settings are configured
* using CMU_LFAPRESC0 and the clock enable bits can be found in
* CMU_LFACLKEN0. Notice that the LCD has an additional high resolution
* prescaler for Frame Rate Control, configured by FDIV in CMU_LCDCTRL.
* When operating in oversampling mode, the pulse counters are clocked by
* LFACLK. This is configured for each pulse counter (n) individually by
* setting PCNTnCLKSEL in CMU_PCNTCTRL.
*
****************************************************************************/
static inline uint32_t efm32_lfaclk_config(uint32_t lfaclksel, bool ulfrco,
uint32_t hfcoreclk)
{
uint32_t lfaclk;
uint32_t regval;
/* ULFRCO is a special case */
if (ulfrco)
{
/* ULFRCO is always enabled */
lfaclksel = _CMU_LFCLKSEL_LFA_DISABLED;
lfaclk = BOARD_ULFRCO_FREQUNCY;
}
else
{
/* Enable the oscillator source */
switch (lfaclksel)
{
default:
case _CMU_LFCLKSEL_LFA_DISABLED:
{
lfaclk = 0;
}
break;
case CMU_LFCLKSEL_LFA_LFRCO:
{
efm32_enable_lfrco();
lfaclk = BOARD_LFRCO_FREQUENCY;
}
break;
case _CMU_LFCLKSEL_LFA_LFXO:
{
efm32_enable_lfxo();
lfaclk = BOARD_LFXO_FREQUENCY;
}
break;
case _CMU_LFCLKSEL_LFA_HFCORECLKLEDIV2:
{
lfaclk = hfcoreclk >> 1;
}
break;
}
}
/* Enable the LFA clock in the LFCLKSEL register */
regval = getreg32(EFM32_CMU_LFCLKSEL);
#ifdef CMU_LFCLKSEL_LFAE
regval &= ~_CMU_LFCLKSEL_LFAE_MASK;
#endif
regval &= ~_CMU_LFCLKSEL_LFA_MASK;
regval |= (lfaclksel << _CMU_LFCLKSEL_LFA_SHIFT);
#ifdef CMU_LFCLKSEL_LFAE_ULFRCO
regval |= ((uint32_t)ulfrco << _CMU_LFCLKSEL_LFAE_SHIFT);
#endif
putreg32(regval, EFM32_CMU_LFCLKSEL);
return lfaclk;
}
/****************************************************************************
* Name: efm32_lfbclk_config
*
* Description:
* Configure the Low Frequency B Clock, LFBCLK.
*
* LFBCLK is the selected clock for the Low Energy B Peripherals. There
* are four selectable sources for LFBCLK: LFRCO, LFXO, HFCORECLK/2 and
* ULFRCO. In addition, the LFBCLK can be disabled. From reset, the LFBCLK
* source is set to LFRCO. However, note that the LFRCO is disabled from
* reset. The selection is configured using the LFB field in CMU_LFCLKSEL.
* The HFCORECLK/2 setting allows the Low Energy B Peripherals to be used
* as high-frequency peripherals.
*
* Each Low Energy Peripheral that is clocked by LFBCLK has its own
* prescaler setting and enable bit. The prescaler settings are
* configured using CMU_LFBPRESC0 and the clock enable bits can be found
* in CMU_LFBCLKEN0.
*
****************************************************************************/
static inline uint32_t efm32_lfbclk_config(uint32_t lfbclksel, bool ulfrco,
uint32_t hfcoreclk)
{
uint32_t lfbclk;
uint32_t regval;
/* ULFRCO is a special case */
if (ulfrco)
{
/* ULFRCO is always enabled */
lfbclksel = _CMU_LFCLKSEL_LFB_DISABLED;
lfbclk = BOARD_ULFRCO_FREQUNCY;
}
else
{
/* Enable the oscillator source */
switch (lfbclksel)
{
default:
case _CMU_LFCLKSEL_LFB_DISABLED:
{
lfbclk = 0;
}
break;
case CMU_LFCLKSEL_LFB_LFRCO:
{
efm32_enable_lfrco();
}
break;
case _CMU_LFCLKSEL_LFB_LFXO:
{
efm32_enable_lfxo();
lfbclk = BOARD_LFXO_FREQUENCY;
}
break;
case _CMU_LFCLKSEL_LFB_HFCORECLKLEDIV2:
{
lfbclk = hfcoreclk >> 1;
}
break;
}
}
/* Enable the LFB clock in the LFCLKSEL register */
regval = getreg32(EFM32_CMU_LFCLKSEL);
#ifdef CMU_LFCLKSEL_LFBE
regval &= ~_CMU_LFCLKSEL_LFBE_MASK;
#endif
regval &= ~_CMU_LFCLKSEL_LFB_MASK;
regval |= (lfbclksel << _CMU_LFCLKSEL_LFB_SHIFT);
#ifdef CMU_LFCLKSEL_LFBE_ULFRCO
regval |= ((uint32_t)ulfrco << _CMU_LFCLKSEL_LFBE_SHIFT);
#endif
putreg32(regval, EFM32_CMU_LFCLKSEL);
return lfbclk;
}
/****************************************************************************
* Name: efm32_pcntclk_config
*
* Description:
* Configure the Pulse Counter n Clock, PCNTnCLK.
*
* Each available pulse counter is driven by its own clock, PCNTnCLK where
* n is the pulse counter instance number. Each pulse counter can be
* configured to use an external pin (PCNTn_S0) or LFACLK as PCNTnCLK.
*
****************************************************************************/
static inline void efm32_pcntclk_config(void)
{
/* REVISIT: Not yet implemented */
}
/****************************************************************************
* Name: efm32_wdogclk_config
*
* Description:
* Configure the Watchdog Timer Clock, WDOGCLK.
*
* The Watchdog Timer (WDOG) can be configured to use one of three
* different clock sources: LFRCO, LFXO or ULFRCO. ULFRCO (Ultra Low
* Frequency RC Oscillator) is a separate 1 kHz RC oscillator that also
* runs in EM3.
*
****************************************************************************/
static inline void efm32_wdogclk_config(void)
{
/* REVISIT: Not yet implemented */
}
/****************************************************************************
* Name: efm32_auxclk_config
*
* Description:
* Configure the Auxiliary Clock, AUXCLK.
*
* AUXCLK is a 1-28 MHz clock driven by a separate RC oscillator, AUXHFRCO.
* This clock is used for flash programming, and Serial Wire Output (SWO),
* and LESENSE operation. During flash programming, or if needed by
* LESENSE, this clock will be active. If the AUXHFRCO has not been
* enabled explicitly by software, the MSC or LESENSE module will
* automatically start and stop it. The AUXHFRCO is enabled by writing a 1
* to AUXHFRCOEN in CMU_OSCENCMD. This explicit enabling is required when
* SWO is used.
*
****************************************************************************/
static inline void efm32_auxclk_config(void)
{
/* REVISIT: Not yet implemented */
}
/****************************************************************************
* Name: efm32_gpioclock
*
* Description:
* Enable clocking to the GPIO
*
****************************************************************************/
static inline void efm32_gpioclock(void)
{
uint32_t regval;
/* Enable clocking to the GPIO be setting the GPIO bit in the High
* Frequency Peripheral Clock Enable.
*/
regval = getreg32(EFM32_CMU_HFPERCLKEN0);
regval |= CMU_HFPERCLKEN0_GPIO;
putreg32(regval, EFM32_CMU_HFPERCLKEN0);
}
/****************************************************************************
* Name: efm32_itm_syslog
*
* Description:
* Enable Serial wire output pin, configure debug clocking, and enable
* ITM syslog support.
*
****************************************************************************/
#if defined(CONFIG_SYSLOG) || defined(CONFIG_ARMV7M_ITMSYSLOG)
static inline void efm32_itm_syslog(void)
{
int regval;
/* Enable Serial wire output pin
*
* Set location and enable output on the pin. All pin configuration
* information must be provided in the board.h header file.
*/
regval = getreg32(EFM32_GPIO_ROUTE);
regval &= ~_GPIO_ROUTE_SWLOCATION_MASK;
regval |= GPIO_ROUTE_SWOPEN;
regval |= ((uint32_t)BOARD_SWOPORT_LOCATION << _GPIO_ROUTE_SWLOCATION_SHIFT);
putreg32(regval, EFM32_GPIO_ROUTE);
/* Enable output on pin */
efm32_configgpio(BOARD_GPIO_SWOPORT);
/* Enable debug clock AUXHFRCO */
efm32_enable_auxhfrco();
/* Then perform ARMv7-M ITM SYSLOG initialization */
itm_syslog_initialize();
}
#else
# define efm32_itm_syslog()
#endif
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: efm32_clockconfig
*
* Description:
* Called to initialize the EFM32 chip. This does whatever setup is
* needed to put the MCU in a usable state. This includes the
* initialization of clocking using the settings in board.h.
*
****************************************************************************/
void efm32_clockconfig(void)
{
uint32_t hfclk;
uint32_t hfcoreclk;
uint32_t hfperclk;
uint32_t coreleclk;
uint32_t lfaclk;
uint32_t lfbclk;
/* Enable clocks and set dividers as determined by the board.h header file */
hfclk = efm32_hfclk_config(BOARD_HFCLKSEL, BOARD_HFCLKDIV);
hfcoreclk = efm32_hfcoreclk_config(BOARD_HFCORECLKDIV, hfclk);
hfperclk = efm32_hfperclk_config(BOARD_HFPERCLKDIV, hfclk);
coreleclk = efm32_coreleclk_config(hfclk);
lfaclk = efm32_lfaclk_config(BOARD_LFACLKSEL, BOARD_LFA_ULFCO_ENABLE, hfcoreclk);
lfbclk = efm32_lfbclk_config(BOARD_LFBCLKSEL, BOARD_LFB_ULFCO_ENABLE, hfcoreclk);
efm32_pcntclk_config();
efm32_wdogclk_config();
efm32_auxclk_config();
UNUSED(hfperclk);
UNUSED(coreleclk);
UNUSED(lfaclk);
UNUSED(lfbclk);
/* Enable clocking of the GPIO ports */
efm32_gpioclock();
/* Enable Serial wire output pin, configure debug clocking, and enable ITM
* syslog support.
*/
efm32_itm_syslog();
}