1
0
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:
Joakim Nohlgård 2018-04-24 18:36:22 +02:00 committed by Benjamin Valentin
parent 238f91f1bb
commit dc84ccdfe0
8 changed files with 328 additions and 8 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
*

View File

@ -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.
*

View 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);
}
/** @} */

View File

@ -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