1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/cpu/kinetis/periph/mcg_lite.c
Joakim Nohlgård dc84ccdfe0 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.
2020-10-24 22:12:32 +02:00

261 lines
7.8 KiB
C

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