mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
kinetis: Add support for MCG_Lite hardware
MCG_Lite is used in many KL parts and is a less advanced clock generator than the full MCG used in the K series. This change lets the MCG_Lite and MCG share the same user facing API, with some configuration differences.
This commit is contained in:
parent
238f91f1bb
commit
dc84ccdfe0
@ -27,7 +27,7 @@ config CPU_FAM_K
|
||||
config CPU_FAM_L
|
||||
bool
|
||||
select CPU_COMMON_KINETIS
|
||||
select HAS_PERIPH_MCG
|
||||
select HAS_PERIPH_MCG_LITE
|
||||
|
||||
config CPU_FAM_W
|
||||
bool
|
||||
@ -126,6 +126,11 @@ config HAS_PERIPH_MCG
|
||||
help
|
||||
Indicates that the cpu uses the Kinetis Multipurpose Clock Generator.
|
||||
|
||||
config HAS_PERIPH_MCG_LITE
|
||||
bool
|
||||
help
|
||||
Indicates that the cpu uses the lite version of the Kinetis Multipurpose Clock Generator.
|
||||
|
||||
config HAS_PERIPH_ICS
|
||||
bool
|
||||
help
|
||||
|
@ -10,11 +10,14 @@ endif
|
||||
# and check FEATURES_USED instead.
|
||||
FEATURES_OPTIONAL += periph_ics
|
||||
FEATURES_OPTIONAL += periph_mcg
|
||||
FEATURES_OPTIONAL += periph_mcg_lite
|
||||
|
||||
ifneq (,$(filter periph_ics,$(FEATURES_USED)))
|
||||
USEMODULE += periph_ics
|
||||
else ifneq (,$(filter periph_mcg,$(FEATURES_USED)))
|
||||
USEMODULE += periph_mcg
|
||||
else ifneq (,$(filter periph_mcg_lite,$(FEATURES_USED)))
|
||||
USEMODULE += periph_mcg_lite
|
||||
endif
|
||||
|
||||
USEMODULE += periph_wdog
|
||||
|
@ -26,6 +26,8 @@ endif
|
||||
|
||||
ifeq (ea,$(CPU_FAM))
|
||||
FEATURES_PROVIDED += periph_ics
|
||||
else ifeq (l,$(CPU_FAM))
|
||||
FEATURES_PROVIDED += periph_mcg_lite
|
||||
else
|
||||
FEATURES_PROVIDED += periph_mcg
|
||||
endif
|
||||
|
@ -7,7 +7,8 @@
|
||||
* details.
|
||||
*/
|
||||
|
||||
#ifdef MODULE_PERIPH_MCG /* please doxygen by hiding dangling references */
|
||||
/* please doxygen by hiding dangling references */
|
||||
#if defined(MODULE_PERIPH_MCG) || defined(MODULE_PERIPH_MCG_LITE)
|
||||
/**
|
||||
* @defgroup cpu_kinetis_mcg Kinetis MCG
|
||||
* @ingroup cpu_kinetis
|
||||
|
@ -532,8 +532,8 @@ typedef struct {
|
||||
uart_type_t type; /**< Hardware module type (KINETIS_UART or KINETIS_LPUART)*/
|
||||
} uart_conf_t;
|
||||
|
||||
#if !defined(KINETIS_HAVE_PLL)
|
||||
#if defined(MCG_C6_PLLS_MASK) || DOXYGEN
|
||||
#if !defined(KINETIS_HAVE_PLL) && defined(MODULE_PERIPH_MCG) \
|
||||
&& defined(MCG_C6_PLLS_MASK) || DOXYGEN
|
||||
/**
|
||||
* @brief Defined to 1 if the MCG in this Kinetis CPU has a PLL
|
||||
*/
|
||||
@ -541,7 +541,19 @@ typedef struct {
|
||||
#else
|
||||
#define KINETIS_HAVE_PLL 0
|
||||
#endif
|
||||
#endif /* !defined(KINETIS_HAVE_PLL) */
|
||||
|
||||
#ifdef MODULE_PERIPH_MCG_LITE
|
||||
/**
|
||||
* @brief Kinetis possible MCG modes
|
||||
*/
|
||||
typedef enum kinetis_mcg_mode {
|
||||
KINETIS_MCG_MODE_LIRC8M = 0, /**< LIRC 8 MHz mode*/
|
||||
KINETIS_MCG_MODE_HIRC = 1, /**< HIRC 48 MHz mode */
|
||||
KINETIS_MCG_MODE_EXT = 2, /**< External clocking mode */
|
||||
KINETIS_MCG_MODE_LIRC2M = 3, /**< LIRC 2 MHz mode */
|
||||
KINETIS_MCG_MODE_NUMOF, /**< Number of possible modes */
|
||||
} kinetis_mcg_mode_t;
|
||||
#endif /* MODULE_PERIPH_MCG_LITE */
|
||||
|
||||
#ifdef MODULE_PERIPH_MCG
|
||||
/**
|
||||
@ -583,6 +595,9 @@ typedef enum {
|
||||
KINETIS_MCG_FLL_FACTOR_2929 = (MCG_C4_DRST_DRS(3) | MCG_C4_DMX32_MASK),
|
||||
} kinetis_mcg_fll_t;
|
||||
|
||||
#endif /* MODULE_PERIPH_MCG */
|
||||
#if defined(MODULE_PERIPH_MCG) || defined(MODULE_PERIPH_MCG_LITE)
|
||||
|
||||
/**
|
||||
* @brief Kinetis FLL external reference clock range settings
|
||||
*/
|
||||
@ -625,6 +640,8 @@ typedef enum {
|
||||
* @note This flag affects the clock frequency of the CPU when using the MCG
|
||||
* in FBI, or BLPI clocking modes.
|
||||
*
|
||||
* @note This flag is ignored on MCG_Lite parts
|
||||
*
|
||||
* - If this flag is set, the fast internal reference clock (up to 4 MHz,
|
||||
* depends on settings) will be routed to the MCGIRCLK internal clock signal.
|
||||
* - If not set, the slow internal reference clock (32 kHz) will be routed to
|
||||
@ -652,6 +669,17 @@ typedef enum {
|
||||
* CPU STOP modes.
|
||||
*/
|
||||
KINETIS_CLOCK_MCGIRCLK_STOP_EN = (1 << 4),
|
||||
/**
|
||||
* @brief Enable MCGPCLK (HIRC) internal clock signal
|
||||
*
|
||||
* This flag corresponds to the HIRCEN bit in the MCG_MC register.
|
||||
*
|
||||
* This clock source is only available on MCG_Lite parts
|
||||
*
|
||||
* - If this flag is set, the MCG will provide MCGPCLK for use by other
|
||||
* peripherals.
|
||||
*/
|
||||
KINETIS_CLOCK_MCGPCLK_EN = (1 << 5),
|
||||
} kinetis_clock_flags_t;
|
||||
|
||||
/**
|
||||
@ -723,6 +751,7 @@ typedef struct {
|
||||
* @see CPU reference manual, OSC_CR[SCxP]
|
||||
*/
|
||||
uint8_t osc_clc;
|
||||
#ifdef MODULE_PERIPH_MCG
|
||||
/**
|
||||
* @brief MCG external reference oscillator selection
|
||||
*
|
||||
@ -733,9 +762,12 @@ typedef struct {
|
||||
* @see CPU reference manual, MCG_C7[OSCSEL]
|
||||
*/
|
||||
uint8_t oscsel;
|
||||
#endif /* MODULE_PERIPH_MCG */
|
||||
/**
|
||||
* @brief Fast internal reference clock divider
|
||||
*
|
||||
* This field is also known as LIRC_DIV1 on MCG_Lite parts.
|
||||
*
|
||||
* The bits will be passed directly to the MCG_SC register without any
|
||||
* transformation, use the MCG_SC_FCRDIV() macro to ensure the proper bit
|
||||
* shift for the chosen setting.
|
||||
@ -743,6 +775,20 @@ typedef struct {
|
||||
* @see CPU reference manual, MCG_SC[FCRDIV]
|
||||
*/
|
||||
uint8_t fcrdiv;
|
||||
#ifdef MODULE_PERIPH_MCG_LITE
|
||||
/**
|
||||
* @brief LIRC second clock divider
|
||||
*
|
||||
* The bits will be passed directly to the MCG_MC register without any
|
||||
* transformation, use the MCG_MC_LIRC_DIV2() macro to ensure the proper bit
|
||||
* shift for the chosen setting.
|
||||
* This divider only affects the MCGIRCLK output, it does not affect the
|
||||
* core frequency when running the MCU in a LIRC clocking mode.
|
||||
*
|
||||
* @see CPU reference manual, MCG_MC[LIRC_DIV2]
|
||||
*/
|
||||
uint8_t lirc_div2;
|
||||
#else
|
||||
/**
|
||||
* @brief FLL ERC divider setting
|
||||
*
|
||||
@ -789,8 +835,9 @@ typedef struct {
|
||||
*/
|
||||
uint8_t pll_vdiv;
|
||||
#endif /* KINETIS_HAVE_PLL */
|
||||
} clock_config_t;
|
||||
#endif /* MODULE_PERIPH_MCG */
|
||||
} clock_config_t;
|
||||
#endif /* MODULE_PERIPH_MCG || MODULE_PERIPH_MCG_LITE */
|
||||
/**
|
||||
* @brief CPU internal function for initializing PORTs
|
||||
*
|
||||
|
@ -283,7 +283,6 @@ static void kinetis_mcg_set_fbe(void)
|
||||
current_mode = KINETIS_MCG_MODE_FBE;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Initialize the FLL Bypassed Low Power Internal Mode.
|
||||
*
|
||||
|
260
cpu/kinetis/periph/mcg_lite.c
Normal file
260
cpu/kinetis/periph/mcg_lite.c
Normal file
@ -0,0 +1,260 @@
|
||||
/*
|
||||
* Copyright (C) 2015 PHYTEC Messtechnik GmbH
|
||||
* Copyright (C) 2017 Eistec AB
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser General
|
||||
* Public License v2.1. See the file LICENSE in the top level directory for more
|
||||
* details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup cpu_kinetis
|
||||
* @ingroup cpu_kinetis_mcg
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Implementation of the Kinetis Multipurpose Clock Generator (Lite version)
|
||||
*
|
||||
* @author Johann Fischer <j.fischer@phytec.de>
|
||||
* @author Joakim Nohlgård <joakim.nohlgard@eistec.se>
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include "periph_conf.h"
|
||||
#include "mcg.h"
|
||||
#include "bit.h"
|
||||
|
||||
/* The CPU is in LIRC8M mode after hardware reset */
|
||||
static kinetis_mcg_mode_t current_mode = KINETIS_MCG_MODE_LIRC8M;
|
||||
|
||||
/**
|
||||
* @brief Enable Oscillator module
|
||||
*/
|
||||
static void kinetis_mcg_enable_osc(void)
|
||||
{
|
||||
/* Configure ERC range for the DCO input. */
|
||||
MCG->C2 = (MCG->C2 & ~MCG_C2_RANGE0_MASK) | clock_config.erc_range;
|
||||
|
||||
#if defined(OSC0)
|
||||
/* Kinetis CPU with OSC module */
|
||||
/* Enable Oscillator */
|
||||
if (clock_config.clock_flags & KINETIS_CLOCK_OSC0_EN) {
|
||||
/* Configure oscillator */
|
||||
OSC0->CR = OSC_CR_ERCLKEN_MASK | OSC_CR_EREFSTEN_MASK | clock_config.osc_clc;
|
||||
bit_set8(&MCG->C2, MCG_C2_EREFS0_SHIFT);
|
||||
|
||||
/* wait for OSC initialization */
|
||||
while ((MCG->S & MCG_S_OSCINIT0_MASK) == 0) {}
|
||||
}
|
||||
else {
|
||||
bit_clear8(&MCG->C2, MCG_C2_EREFS0_SHIFT);
|
||||
}
|
||||
#elif defined(RSIM)
|
||||
/* Kinetis CPU with a radio system integration module which can provide an
|
||||
* oscillator output. */
|
||||
|
||||
/* The CPUs with RSIM (currently only KW41Z, KW31Z, KW21Z) ignore the EREFS0
|
||||
* bit in MCG_C2 because they have no OSC module. These CPUs need to use the
|
||||
* RF oscillator inside the RSIM module if an oscillator is needed. */
|
||||
/* The external reference clock line on these CPUs is permanently connected
|
||||
* to the RSIM clock output, thus the RSIM, instead of the MCG, controls the
|
||||
* external clock source selection. */
|
||||
|
||||
if (clock_config.clock_flags & KINETIS_CLOCK_OSC0_EN) {
|
||||
/* Disable RF oscillator bypass, if it was enabled before */
|
||||
bit_clear32(&RSIM->RF_OSC_CTRL, RSIM_RF_OSC_CTRL_RF_OSC_BYPASS_EN_SHIFT);
|
||||
}
|
||||
else {
|
||||
/* Enable RF oscillator bypass, to use the EXTAL pin as external clock
|
||||
* source without the oscillator circuit */
|
||||
bit_set32(&RSIM->RF_OSC_CTRL, RSIM_RF_OSC_CTRL_RF_OSC_BYPASS_EN_SHIFT);
|
||||
}
|
||||
|
||||
/* Enable RF oscillator circuit */
|
||||
/* Current setting is that the OSC only runs in RUN and WAIT modes, see ref.man. */
|
||||
RSIM->CONTROL = (RSIM->CONTROL & ~RSIM_CONTROL_RF_OSC_EN_MASK) | RSIM_CONTROL_RF_OSC_EN(1);
|
||||
|
||||
/* Wait for oscillator ready signal */
|
||||
while((RSIM->CONTROL & RSIM_CONTROL_RF_OSC_READY_MASK) == 0) {}
|
||||
#endif /* defined OSC0/RSIM */
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize the 32 kHz reference clock (ERCLK32K)
|
||||
*
|
||||
* This will enable the RTC oscillator if enabled in the configuration.
|
||||
*/
|
||||
static void kinetis_mcg_init_erclk32k(void)
|
||||
{
|
||||
/* Enable RTC oscillator if selected */
|
||||
if (clock_config.clock_flags & KINETIS_CLOCK_RTCOSC_EN) {
|
||||
RTC_CLKEN();
|
||||
if (!(RTC->CR & RTC_CR_OSCE_MASK)) {
|
||||
/* Only touch if it was previously not running. The RTC is not reset
|
||||
* by software resets, only by power on reset */
|
||||
RTC->CR = RTC_CR_OSCE_MASK | RTC_CR_SUP_MASK | clock_config.rtc_clc;
|
||||
}
|
||||
}
|
||||
/* Select ERCLK32K source */
|
||||
SIM->SOPT1 = (SIM->SOPT1 & ~SIM_SOPT1_OSC32KSEL_MASK) | clock_config.osc32ksel;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize the MCG internal reference clock (MCGIRCLK)
|
||||
*
|
||||
* This clock signal can be used for directly clocking certain peripherals, and
|
||||
* can be chosen as the MCG output clock (MCGOUTCLK).
|
||||
*/
|
||||
static void kinetis_mcg_init_mcgirclk(void)
|
||||
{
|
||||
/* Configure internal reference clock */
|
||||
/* On MCG_Lite, LIRC is divided first by LIRC_DIV1, controlled by
|
||||
* MCG_SC[FCRDIV], then by LIRC_DIV2, controlled by MCG_MC[LIRC_DIV2] */
|
||||
MCG->SC = (MCG->SC & ~MCG_SC_FCRDIV_MASK) | clock_config.fcrdiv;
|
||||
MCG->MC = (MCG->MC & ~MCG_MC_LIRC_DIV2_MASK) | clock_config.lirc_div2;
|
||||
/* on MCG_Lite, we control the IRCS flag via the mode selection (LIRC2M vs LIRC8M) */
|
||||
|
||||
/* Enable/disable MCGIRCLK */
|
||||
/* MCGIRCLK can be used as an alternate clock source for certain modules */
|
||||
if (clock_config.clock_flags & KINETIS_CLOCK_MCGIRCLK_EN) {
|
||||
bit_set8(&MCG->C1, MCG_C1_IRCLKEN_SHIFT);
|
||||
}
|
||||
else {
|
||||
bit_clear8(&MCG->C1, MCG_C1_IRCLKEN_SHIFT);
|
||||
}
|
||||
|
||||
if (clock_config.clock_flags & KINETIS_CLOCK_MCGIRCLK_STOP_EN) {
|
||||
/* Enable MCGIRCLK during STOP (but only when also IRCLKEN is set) */
|
||||
bit_set8(&MCG->C1, MCG_C1_IREFSTEN_SHIFT);
|
||||
}
|
||||
else {
|
||||
bit_clear8(&MCG->C1, MCG_C1_IREFSTEN_SHIFT);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize the MCG high speed peripheral clock (MCGPCLK)
|
||||
*
|
||||
* This clock signal can be used for directly clocking certain peripherals
|
||||
*/
|
||||
static void kinetis_mcg_init_mcgpclk(void)
|
||||
{
|
||||
if (clock_config.clock_flags & KINETIS_CLOCK_MCGPCLK_EN) {
|
||||
bit_set8(&MCG->MC, MCG_MC_HIRCEN_SHIFT);
|
||||
}
|
||||
else {
|
||||
bit_clear8(&MCG->MC, MCG_MC_HIRCEN_SHIFT);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize HIRC (48 MHz) mode.
|
||||
*/
|
||||
static void kinetis_mcg_set_hirc(void)
|
||||
{
|
||||
/* select HIRC mode */
|
||||
MCG->C1 = (MCG->C1 & ~MCG_C1_CLKS_MASK) | (MCG_C1_CLKS(0));
|
||||
|
||||
/* Wait until HIRC is selected */
|
||||
while ((MCG->S & (MCG_S_CLKST_MASK)) != 0) {}
|
||||
|
||||
current_mode = KINETIS_MCG_MODE_HIRC;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize EXT (external clock) mode.
|
||||
*/
|
||||
static void kinetis_mcg_set_ext(void)
|
||||
{
|
||||
kinetis_mcg_enable_osc();
|
||||
|
||||
/* select EXT mode */
|
||||
MCG->C1 = (MCG->C1 & ~MCG_C1_CLKS_MASK) | (MCG_C1_CLKS(0));
|
||||
|
||||
/* Wait until HIRC is selected */
|
||||
while ((MCG->S & (MCG_S_CLKST_MASK)) != 0) {}
|
||||
|
||||
current_mode = KINETIS_MCG_MODE_HIRC;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize LIRC mode.
|
||||
*
|
||||
* Use @p ircs to select between LIRC8M (8 MHz) and LIRC2M (2 MHz).
|
||||
*
|
||||
* @param[in] lirc8m set to 1 -> LIRC8M, 0 -> LIRC2M
|
||||
*/
|
||||
static void kinetis_mcg_set_lirc(unsigned lirc8m)
|
||||
{
|
||||
uint32_t clkdiv = SIM->CLKDIV1;
|
||||
if ((lirc8m && (current_mode == KINETIS_MCG_MODE_LIRC8M)) ||
|
||||
(!lirc8m && (current_mode == KINETIS_MCG_MODE_LIRC2M))) {
|
||||
/* We can not switch directly between LIRC2M <-> LIRC8M, go via HIRC */
|
||||
/* Set safe clock dividers so we don't run out of specs while switching */
|
||||
SIM->CLKDIV1 = SIM_CLKDIV1_OUTDIV1(3); /* divide clock by 4 => 12 MHz */
|
||||
kinetis_mcg_set_hirc();
|
||||
}
|
||||
|
||||
if (lirc8m) {
|
||||
/* Select 8 MHz mode */
|
||||
bit_set8(&MCG->C2, MCG_C2_IRCS_SHIFT);
|
||||
current_mode = KINETIS_MCG_MODE_LIRC8M;
|
||||
}
|
||||
else {
|
||||
/* Select 2 MHz mode */
|
||||
bit_clear8(&MCG->C2, MCG_C2_IRCS_SHIFT);
|
||||
current_mode = KINETIS_MCG_MODE_LIRC2M;
|
||||
}
|
||||
|
||||
/* select LIRC as clock source */
|
||||
MCG->C1 = (MCG->C1 & ~MCG_C1_CLKS_MASK) | (MCG_C1_CLKS(1));
|
||||
|
||||
/* Wait until LIRC is selected */
|
||||
while ((MCG->S & (MCG_S_CLKST_MASK)) != MCG_S_CLKST(1)) {}
|
||||
|
||||
/* Restore clock divider settings */
|
||||
SIM->CLKDIV1 = clkdiv;
|
||||
}
|
||||
|
||||
int kinetis_mcg_set_mode(kinetis_mcg_mode_t mode)
|
||||
{
|
||||
switch(mode) {
|
||||
case KINETIS_MCG_MODE_LIRC8M:
|
||||
kinetis_mcg_set_lirc(1);
|
||||
break;
|
||||
case KINETIS_MCG_MODE_HIRC:
|
||||
kinetis_mcg_set_hirc();
|
||||
break;
|
||||
case KINETIS_MCG_MODE_EXT:
|
||||
kinetis_mcg_set_ext();
|
||||
break;
|
||||
case KINETIS_MCG_MODE_LIRC2M:
|
||||
kinetis_mcg_set_lirc(0);
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void kinetis_mcg_init(void)
|
||||
{
|
||||
unsigned mask = irq_disable();
|
||||
|
||||
/* Set module clock dividers */
|
||||
SIM->CLKDIV1 = clock_config.clkdiv1;
|
||||
|
||||
kinetis_mcg_init_mcgpclk();
|
||||
|
||||
kinetis_mcg_init_mcgirclk();
|
||||
|
||||
kinetis_mcg_init_erclk32k();
|
||||
|
||||
/* Switch to the selected MCG mode */
|
||||
kinetis_mcg_set_mode(clock_config.default_mode);
|
||||
irq_restore(mask);
|
||||
}
|
||||
|
||||
/** @} */
|
@ -319,8 +319,11 @@ ISR_VECTOR(1) const isr_t vector_cpu[CPU_IRQ_NUMOF] = {
|
||||
[DAC1_IRQn ] = isr_dac1, /* DAC1 interrupt */
|
||||
#endif
|
||||
#ifdef MCG
|
||||
#ifndef MCG_MC_LIRC_DIV2_MASK
|
||||
/* Only on full MCG, not MCG_Lite */
|
||||
[MCG_IRQn ] = isr_mcg, /* MCG Interrupt */
|
||||
#endif
|
||||
#endif /* MCG_MC_LIRC_DIV2_MASK */
|
||||
#endif /* MCG */
|
||||
#ifdef LPTMR0
|
||||
[LPTMR0_IRQn ] = isr_lptmr0, /* LPTimer interrupt */
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user