971 lines
29 KiB
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();
|
|
}
|