From c54f6b4fcf240c0ad04138c4eab3c5183ef7f754 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20Nohlg=C3=A5rd?= Date: Wed, 11 Apr 2018 06:58:46 +0200 Subject: [PATCH] kinetis: Refactor clock generator initialization --- boards/frdm-k22f/include/periph_conf.h | 22 ++- boards/frdm-k64f/include/periph_conf.h | 22 ++- boards/frdm-kw41z/board.c | 6 - boards/frdm-kw41z/include/periph_conf.h | 24 ++- boards/mulle/board.c | 22 --- boards/mulle/include/periph_conf.h | 49 ++--- boards/pba-d-01-kw2x/include/periph_conf.h | 22 ++- boards/teensy31/include/periph_conf.h | 22 ++- cpu/kinetis/include/cpu_conf_kinetis.h | 4 + cpu/kinetis/include/periph_cpu.h | 214 ++++++++++++++++++--- cpu/kinetis/periph/mcg.c | 116 +++++++---- 11 files changed, 361 insertions(+), 162 deletions(-) diff --git a/boards/frdm-k22f/include/periph_conf.h b/boards/frdm-k22f/include/periph_conf.h index e36bbde8d2..d028cb307b 100644 --- a/boards/frdm-k22f/include/periph_conf.h +++ b/boards/frdm-k22f/include/periph_conf.h @@ -41,20 +41,24 @@ static const clock_config_t clock_config = { */ .clkdiv1 = SIM_CLKDIV1_OUTDIV1(0) | SIM_CLKDIV1_OUTDIV2(1) | SIM_CLKDIV1_OUTDIV3(2) | SIM_CLKDIV1_OUTDIV4(2), + .rtc_clc = 0, /* External load caps on the FRDM-K22F board */ + .osc32ksel = SIM_SOPT1_OSC32KSEL(2), + .clock_flags = + KINETIS_CLOCK_OSC0_EN | + KINETIS_CLOCK_RTCOSC_EN | + KINETIS_CLOCK_USE_FAST_IRC | + 0, .default_mode = KINETIS_MCG_MODE_FEE, /* The crystal connected to OSC0 is 8 MHz */ .erc_range = KINETIS_MCG_ERC_RANGE_HIGH, - .fcrdiv = 0, /* Fast IRC divide by 1 => 4 MHz */ - .oscsel = 0, /* Use OSC0 for external clock */ - .clc = 0, /* External load caps on the FRDM-K22F board */ - .fll_frdiv = 0b011, /* Divide by 256 */ + .osc_clc = 0, /* External load caps on the FRDM-K22F board */ + .oscsel = MCG_C7_OSCSEL(0), /* Use OSC0 for external clock */ + .fcrdiv = MCG_SC_FCRDIV(0), /* Fast IRC divide by 1 => 4 MHz */ + .fll_frdiv = MCG_C1_FRDIV(0b011), /* Divide by 256 */ .fll_factor_fei = KINETIS_MCG_FLL_FACTOR_1464, /* FLL freq = 48 MHz */ .fll_factor_fee = KINETIS_MCG_FLL_FACTOR_1920, /* FLL freq = 60 MHz */ - .pll_prdiv = 0b00011, /* Divide by 4 */ - .pll_vdiv = 0b00110, /* Multiply by 30 => PLL freq = 60 MHz */ - .enable_oscillator = true, - .select_fast_irc = true, - .enable_mcgirclk = false, + .pll_prdiv = MCG_C5_PRDIV0(0b00011), /* Divide by 4 */ + .pll_vdiv = MCG_C6_VDIV0(0b00110), /* Multiply by 30 => PLL freq = 60 MHz */ }; #define CLOCK_CORECLOCK (60000000ul) #define CLOCK_BUSCLOCK (CLOCK_CORECLOCK / 2) diff --git a/boards/frdm-k64f/include/periph_conf.h b/boards/frdm-k64f/include/periph_conf.h index d10c23fbfc..09d40c6bd4 100644 --- a/boards/frdm-k64f/include/periph_conf.h +++ b/boards/frdm-k64f/include/periph_conf.h @@ -42,20 +42,24 @@ static const clock_config_t clock_config = { */ .clkdiv1 = SIM_CLKDIV1_OUTDIV1(0) | SIM_CLKDIV1_OUTDIV2(0) | SIM_CLKDIV1_OUTDIV3(2) | SIM_CLKDIV1_OUTDIV4(2), + .rtc_clc = 0, /* External load caps on board */ + .osc32ksel = SIM_SOPT1_OSC32KSEL(2), + .clock_flags = + /* No OSC0_EN, use EXTAL directly without OSC0 */ + KINETIS_CLOCK_RTCOSC_EN | + KINETIS_CLOCK_USE_FAST_IRC | + 0, .default_mode = KINETIS_MCG_MODE_PEE, /* The board has an external RMII (Ethernet) clock which drives the ERC at 50 MHz */ .erc_range = KINETIS_MCG_ERC_RANGE_VERY_HIGH, - .fcrdiv = 0, /* Fast IRC divide by 1 => 4 MHz */ - .oscsel = 0, /* Use EXTAL for external clock */ - .clc = 0, /* External load caps on board */ - .fll_frdiv = 0b111, /* Divide by 1536 => FLL input 32252 Hz */ + .osc_clc = 0, /* External load caps on board */ + .oscsel = MCG_C7_OSCSEL(0), /* Use EXTAL for external clock */ + .fcrdiv = MCG_SC_FCRDIV(0), /* Fast IRC divide by 1 => 4 MHz */ + .fll_frdiv = MCG_C1_FRDIV(0b111), /* Divide by 1536 => FLL input 32252 Hz */ .fll_factor_fei = KINETIS_MCG_FLL_FACTOR_1464, /* FLL freq = 48 MHz */ .fll_factor_fee = KINETIS_MCG_FLL_FACTOR_1920, /* FLL freq = 62.5 MHz */ - .pll_prdiv = 0b10011, /* Divide by 20 */ - .pll_vdiv = 0b00000, /* Multiply by 24 => PLL freq = 60 MHz */ - .enable_oscillator = false, /* Use EXTAL directly without OSC0 */ - .select_fast_irc = true, - .enable_mcgirclk = false, + .pll_prdiv = MCG_C5_PRDIV0(0b10011), /* Divide by 20 */ + .pll_vdiv = MCG_C6_VDIV0(0b00000), /* Multiply by 24 => PLL freq = 60 MHz */ }; #define CLOCK_CORECLOCK (60000000ul) #define CLOCK_BUSCLOCK (CLOCK_CORECLOCK / 1) diff --git a/boards/frdm-kw41z/board.c b/boards/frdm-kw41z/board.c index 31153834be..9251c626a1 100644 --- a/boards/frdm-kw41z/board.c +++ b/boards/frdm-kw41z/board.c @@ -20,18 +20,12 @@ #include "board.h" #include "periph/gpio.h" -#include "periph/rtt.h" void board_init(void) { /* initialize the CPU core */ cpu_init(); -#if MODULE_XTIMER && !(KINETIS_XTIMER_SOURCE_PIT) - /* Start the RTT, used as time base for xtimer when using LPTMR backend */ - rtt_init(); -#endif - /* initialize and turn off LEDs */ gpio_init(LED0_PIN, GPIO_OUT); gpio_set(LED0_PIN); diff --git a/boards/frdm-kw41z/include/periph_conf.h b/boards/frdm-kw41z/include/periph_conf.h index a021cef6de..05feabb1cd 100644 --- a/boards/frdm-kw41z/include/periph_conf.h +++ b/boards/frdm-kw41z/include/periph_conf.h @@ -39,20 +39,30 @@ static const clock_config_t clock_config = { * Flash: 24 MHz */ .clkdiv1 = SIM_CLKDIV1_OUTDIV1(0) | SIM_CLKDIV1_OUTDIV4(1), + /* unsure if this RTC load cap configuration is correct, but it matches the + * settings used by the example code in the NXP provided SDK */ + .rtc_clc = 0, + /* Use the 32 kHz oscillator as ERCLK32K. Note that the values here have a + * different mapping for the KW41Z than the values used in the Kinetis K series */ + .osc32ksel = SIM_SOPT1_OSC32KSEL(0), + .clock_flags = + KINETIS_CLOCK_OSC0_EN | /* Enable RSIM oscillator */ + KINETIS_CLOCK_RTCOSC_EN | + KINETIS_CLOCK_USE_FAST_IRC | + KINETIS_CLOCK_MCGIRCLK_EN | /* Used for LPUART clocking */ + KINETIS_CLOCK_MCGIRCLK_STOP_EN | + 0, /* Using FEI mode by default, the external crystal settings below are only * used if mode is changed to an external mode (PEE, FBE, or FEE) */ .default_mode = KINETIS_MCG_MODE_FEI, /* The crystal connected to RSIM OSC is 32 MHz */ .erc_range = KINETIS_MCG_ERC_RANGE_VERY_HIGH, - .fcrdiv = 0, /* Fast IRC divide by 1 => 4 MHz */ - .oscsel = 0, /* Use RSIM for external clock */ - .clc = 0, /* no load cap configuration */ - .fll_frdiv = 0b101, /* Divide by 1024 */ + .osc_clc = 0, /* no load cap configuration */ + .oscsel = MCG_C7_OSCSEL(0), /* Use RSIM for external clock */ + .fcrdiv = MCG_SC_FCRDIV(0), /* Fast IRC divide by 1 => 4 MHz */ + .fll_frdiv = MCG_C1_FRDIV(0b101), /* Divide by 1024 */ .fll_factor_fei = KINETIS_MCG_FLL_FACTOR_1464, /* FEI FLL freq = 48 MHz */ .fll_factor_fee = KINETIS_MCG_FLL_FACTOR_1280, /* FEE FLL freq = 40 MHz */ - .enable_oscillator = true, /* Use RF module oscillator */ - .select_fast_irc = true, - .enable_mcgirclk = true, /* Used for LPUART clocking */ }; /* Radio xtal frequency, either 32 MHz or 26 MHz */ #define CLOCK_RADIOXTAL (32000000ul) diff --git a/boards/mulle/board.c b/boards/mulle/board.c index cfe0ca4772..b4f548557d 100644 --- a/boards/mulle/board.c +++ b/boards/mulle/board.c @@ -103,28 +103,6 @@ void board_init(void) /* Turn on AVDD for reading voltages */ gpio_set(MULLE_POWER_AVDD); - /* Initialize RTC oscillator as early as possible since we are using it as a - * base clock for the FLL. - * It takes a while to stabilize the oscillator, therefore we do this as - * soon as possible during boot in order to let it stabilize while other - * stuff is initializing. */ - /* If the clock is not stable then the UART will have the wrong baud rate - * for debug prints as well */ - rtt_init(); - - /* Set 32 kHz clock source */ - SIM->SOPT1 = (SIM->SOPT1 & ~(SIM_SOPT1_OSC32KSEL_MASK)) | SIM_SOPT1_OSC32KSEL(2); - - /* At this point we need to wait for 1 ms until the clock is stable. - * Since the clock is not yet stable we can only guess how long we must - * wait. I have tried to make this as short as possible but still being able - * to read the initialization messages written on the UART. - * (If the clock is not stable all UART output is garbled until it has - * stabilized) */ - for (int i = 0; i < 100000; ++i) { - __asm__ volatile("nop\n"); - } - /* initialize the CPU */ cpu_init(); diff --git a/boards/mulle/include/periph_conf.h b/boards/mulle/include/periph_conf.h index e6d16fd435..9b8e667b3e 100644 --- a/boards/mulle/include/periph_conf.h +++ b/boards/mulle/include/periph_conf.h @@ -33,6 +33,18 @@ extern "C" * @name Clock system configuration * @{ */ +/* The crystal on the Mulle is designed for 12.5 pF load capacitance. According + * to the data sheet, the K60 will have a 5 pF parasitic capacitance on the + * XTAL32/EXTAL32 connection. The board traces might give some minor parasitic + * capacitance as well. */ +/* Use the equation + * CL = (C1 * C2) / (C1 + C2) + Cstray + * with C1 == C2: + * C1 = 2 * (CL - Cstray) + */ +/* enable 14pF load capacitor which will yield a crystal load capacitance of 12 pF */ +#define RTC_LOAD_CAP_BITS (RTC_CR_SC8P_MASK | RTC_CR_SC4P_MASK | RTC_CR_SC2P_MASK) + static const clock_config_t clock_config = { /* * This configuration results in the system running from the FLL output with @@ -48,24 +60,28 @@ static const clock_config_t clock_config = { * consumption than using the 16 MHz crystal and the OSC0 module */ .clkdiv1 = SIM_CLKDIV1_OUTDIV1(0) | SIM_CLKDIV1_OUTDIV2(0) | SIM_CLKDIV1_OUTDIV3(1) | SIM_CLKDIV1_OUTDIV4(1), + .rtc_clc = RTC_LOAD_CAP_BITS, + .osc32ksel = SIM_SOPT1_OSC32KSEL(2), + .clock_flags = + /* no OSC0_EN, the RTC module provides the clock input signal for the FLL */ + KINETIS_CLOCK_RTCOSC_EN | + KINETIS_CLOCK_USE_FAST_IRC | + 0, .default_mode = KINETIS_MCG_MODE_FEE, .erc_range = KINETIS_MCG_ERC_RANGE_LOW, /* Input clock is 32768 Hz */ - .fcrdiv = 0, /* Fast IRC divide by 1 => 4 MHz */ - .oscsel = 1, /* Use RTC for external clock */ /* 16 pF capacitors yield ca 10 pF load capacitance as required by the * onboard xtal, not used when OSC0 is disabled */ - .clc = 0b0001, - .fll_frdiv = 0b000, /* Divide by 1 => FLL input 32768 Hz */ + .osc_clc = OSC_CR_SC16P_MASK, + .oscsel = MCG_C7_OSCSEL(1), /* Use RTC for external clock */ + .fcrdiv = MCG_SC_FCRDIV(0), /* Fast IRC divide by 1 => 4 MHz */ + .fll_frdiv = MCG_C1_FRDIV(0b000), /* Divide by 1 => FLL input 32768 Hz */ .fll_factor_fei = KINETIS_MCG_FLL_FACTOR_1464, /* FLL freq = 48 MHz */ .fll_factor_fee = KINETIS_MCG_FLL_FACTOR_1464, /* FLL freq = 48 MHz */ /* PLL is unavailable when using a 32768 Hz source clock, so the * configuration below can only be used if the above config is modified to * use the 16 MHz crystal instead of the RTC. */ - .pll_prdiv = 0b00111, /* Divide by 8 */ - .pll_vdiv = 0b01100, /* Multiply by 36 => PLL freq = 72 MHz */ - .enable_oscillator = false, /* the RTC module provides the clock input signal */ - .select_fast_irc = true, /* Only used for FBI mode */ - .enable_mcgirclk = false, + .pll_prdiv = MCG_C5_PRDIV0(0b00111), /* Divide by 8 */ + .pll_vdiv = MCG_C6_VDIV0(0b01100), /* Multiply by 36 => PLL freq = 72 MHz */ }; #define CLOCK_CORECLOCK (48000000ul) #define CLOCK_BUSCLOCK (CLOCK_CORECLOCK / 1) @@ -382,21 +398,6 @@ static const spi_conf_t spi_config[] = { #define RTT_MAX_VALUE (0xffffffff) #define RTT_FREQUENCY (1) /* in Hz */ -/** - * RTC module crystal load capacitance configuration bits. - */ -/* The crystal on the Mulle is designed for 12.5 pF load capacitance. According - * to the data sheet, the K60 will have a 5 pF parasitic capacitance on the - * XTAL32/EXTAL32 connection. The board traces might give some minor parasitic - * capacitance as well. */ -/* Use the equation - * CL = (C1 * C2) / (C1 + C2) + Cstray - * with C1 == C2: - * C1 = 2 * (CL - Cstray) - */ -/* enable 14pF load capacitor which will yield a crystal load capacitance of 12 pF */ -#define RTC_LOAD_CAP_BITS (RTC_CR_SC8P_MASK | RTC_CR_SC4P_MASK | RTC_CR_SC2P_MASK) - /** @} */ #ifdef __cplusplus diff --git a/boards/pba-d-01-kw2x/include/periph_conf.h b/boards/pba-d-01-kw2x/include/periph_conf.h index e3efa10e6b..ae1f18fe0c 100644 --- a/boards/pba-d-01-kw2x/include/periph_conf.h +++ b/boards/pba-d-01-kw2x/include/periph_conf.h @@ -43,20 +43,24 @@ static const clock_config_t clock_config = { */ .clkdiv1 = SIM_CLKDIV1_OUTDIV1(0) | SIM_CLKDIV1_OUTDIV2(0) | SIM_CLKDIV1_OUTDIV4(1), + .rtc_clc = 0, /* External load caps on the FRDM-K22F board */ + .osc32ksel = SIM_SOPT1_OSC32KSEL(2), + .clock_flags = + /* No OSC0_EN, use modem clock from EXTAL0 */ + KINETIS_CLOCK_RTCOSC_EN | + KINETIS_CLOCK_USE_FAST_IRC | + 0, .default_mode = KINETIS_MCG_MODE_PEE, /* The modem generates a 4 MHz clock signal */ .erc_range = KINETIS_MCG_ERC_RANGE_HIGH, - .fcrdiv = 0, /* Fast IRC divide by 1 => 4 MHz */ - .oscsel = 0, /* Use EXTAL0 for external clock */ - .clc = 0, /* OSC0 is unused*/ - .fll_frdiv = 0b010, /* Divide by 128 */ + .osc_clc = 0, /* OSC0 is unused*/ + .oscsel = MCG_C7_OSCSEL(0), /* Use EXTAL0 for external clock */ + .fcrdiv = MCG_SC_FCRDIV(0), /* Fast IRC divide by 1 => 4 MHz */ + .fll_frdiv = MCG_C1_FRDIV(0b010), /* Divide by 128 */ .fll_factor_fei = KINETIS_MCG_FLL_FACTOR_1464, /* FLL freq = 48 MHz */ .fll_factor_fee = KINETIS_MCG_FLL_FACTOR_1280, /* FLL freq = 40 MHz */ - .pll_prdiv = 0b00001, /* Divide by 2 */ - .pll_vdiv = 0b00000, /* Multiply by 24 => PLL freq = 48 MHz */ - .enable_oscillator = false, /* Use modem clock from EXTAL0 */ - .select_fast_irc = true, - .enable_mcgirclk = false, + .pll_prdiv = MCG_C5_PRDIV0(0b00001), /* Divide by 2 */ + .pll_vdiv = MCG_C6_VDIV0(0b00000), /* Multiply by 24 => PLL freq = 48 MHz */ }; #define CLOCK_CORECLOCK (48000000ul) #define CLOCK_BUSCLOCK (CLOCK_CORECLOCK / 1) diff --git a/boards/teensy31/include/periph_conf.h b/boards/teensy31/include/periph_conf.h index 6c6de07976..7598b985be 100644 --- a/boards/teensy31/include/periph_conf.h +++ b/boards/teensy31/include/periph_conf.h @@ -45,24 +45,28 @@ static const clock_config_t clock_config = { * consumption than using the 16 MHz crystal and the OSC0 module */ .clkdiv1 = SIM_CLKDIV1_OUTDIV1(0) | SIM_CLKDIV1_OUTDIV2(0) | SIM_CLKDIV1_OUTDIV3(1) | SIM_CLKDIV1_OUTDIV4(1), + /* RTC crystal has to be soldered by the user, we can't know the load cap requirements */ + .rtc_clc = 0, + .osc32ksel = SIM_SOPT1_OSC32KSEL(2), + .clock_flags = + KINETIS_CLOCK_RTCOSC_EN | + KINETIS_CLOCK_USE_FAST_IRC | + 0, .default_mode = KINETIS_MCG_MODE_FEE, .erc_range = KINETIS_MCG_ERC_RANGE_LOW, /* Input clock is 32768 Hz */ - .fcrdiv = 0, /* Fast IRC divide by 1 => 4 MHz */ - .oscsel = 1, /* Use RTC for external clock */ /* 16 pF capacitors yield ca 10 pF load capacitance as required by the * onboard xtal, not used when OSC0 is disabled */ - .clc = 0b0001, - .fll_frdiv = 0b000, /* Divide by 1 => FLL input 32768 Hz */ + .osc_clc = OSC_CR_SC16P_MASK, + .oscsel = MCG_C7_OSCSEL(1), /* Use RTC oscillator as external clock */ + .fcrdiv = MCG_SC_FCRDIV(0), /* Fast IRC divide by 1 => 4 MHz */ + .fll_frdiv = MCG_C1_FRDIV(0b000), /* Divide by 1 => FLL input 32768 Hz */ .fll_factor_fei = KINETIS_MCG_FLL_FACTOR_1464, /* FLL freq = 48 MHz */ .fll_factor_fee = KINETIS_MCG_FLL_FACTOR_1464, /* FLL freq = 48 MHz */ /* PLL is unavailable when using a 32768 Hz source clock, so the * configuration below can only be used if the above config is modified to * use the 16 MHz crystal instead of the RTC. */ - .pll_prdiv = 0b00111, /* Divide by 8 */ - .pll_vdiv = 0b01100, /* Multiply by 36 => PLL freq = 72 MHz */ - .enable_oscillator = false, /* the RTC module provides the clock input signal */ - .select_fast_irc = true, /* Only used for FBI mode */ - .enable_mcgirclk = false, + .pll_prdiv = MCG_C5_PRDIV0(0b00111), /* Divide by 8 */ + .pll_vdiv = MCG_C6_VDIV0(0b01100), /* Multiply by 36 => PLL freq = 72 MHz */ }; #define CLOCK_CORECLOCK (48000000ul) #define CLOCK_BUSCLOCK (CLOCK_CORECLOCK / 1) diff --git a/cpu/kinetis/include/cpu_conf_kinetis.h b/cpu/kinetis/include/cpu_conf_kinetis.h index cd5101d692..d874f984de 100644 --- a/cpu/kinetis/include/cpu_conf_kinetis.h +++ b/cpu/kinetis/include/cpu_conf_kinetis.h @@ -124,6 +124,10 @@ extern "C" /** Enable PIT clock gate */ #define PIT_CLKEN() (bit_set32(&SIM->SCGC6, SIM_SCGC6_PIT_SHIFT)) #endif +#ifdef SIM_SCGC6_RTC_SHIFT +/** Enable RTC clock gate */ +#define RTC_CLKEN() (bit_set32(&SIM->SCGC6, SIM_SCGC6_RTC_SHIFT)) +#endif /** @} */ /** diff --git a/cpu/kinetis/include/periph_cpu.h b/cpu/kinetis/include/periph_cpu.h index bba24a9535..e7645edcaa 100644 --- a/cpu/kinetis/include/periph_cpu.h +++ b/cpu/kinetis/include/periph_cpu.h @@ -1,5 +1,6 @@ /* * Copyright (C) 2015-2016 Freie UniversitÀt Berlin + * Copyright (C) 2017-2018 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 @@ -14,6 +15,7 @@ * @brief CPU specific definitions for internal peripheral handling * * @author Hauke Petersen + * @author Joakim NohlgÄrd */ #ifndef PERIPH_CPU_H @@ -426,51 +428,203 @@ typedef enum { KINETIS_MCG_ERC_RANGE_VERY_HIGH = MCG_C2_RANGE0(2), /**< for 8-32 MHz crystal */ } kinetis_mcg_erc_range_t; +/** + * @brief Clock generation configuration flags + * + * @see "Clock distribution -> High-Level device clocking diagram" in every + * Kinetis CPU reference manual + */ +typedef enum { + /** + * @brief Turn on OSC0 oscillator + * + * - If this flag is set, the OSC0 oscillator expects a crystal between + * the pins XTAL0 and EXTAL0, and the OSCCLK internal signal will be + * provided by OSC0. + * - If not set, the EXTAL0 pin will be used directly as the OSCCLK signal. + */ + KINETIS_CLOCK_OSC0_EN = (1 << 0), + /** + * @brief Turn on RTC oscillator + * + * - If this flag is set, the RTC oscillator expects a crystal between + * the pins XTAL32 and EXTAL32. + * - If not set, the EXTAL32 pin can be used as an external clock signal on + * certain CPU models. + */ + KINETIS_CLOCK_RTCOSC_EN = (1 << 1), + /** + * @brief Use the fast internal reference clock as MCGIRCLK signal + * + * This flag corresponds to the IRCS bit in the MCG_C2 register. + * + * @note This flag affects the clock frequency of the CPU when using the MCG + * in FBI, or BLPI clocking modes. + * + * - 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 + * the MCGIRCLK internal clock signal. FBI and BLPI modes will clock the core + * at 32 kHz. + */ + KINETIS_CLOCK_USE_FAST_IRC = (1 << 2), + /** + * @brief Enable MCGIRCLK internal clock signal + * + * This flag corresponds to the IRCLKEN bit in the MCG_C1 register. + * + * - If this flag is set, the MCG will provide MCGIRCLK for use by other + * peripherals. + */ + KINETIS_CLOCK_MCGIRCLK_EN = (1 << 3), + /** + * @brief Enable MCGIRCLK signal during STOP modes + * + * This flag corresponds to the IREFSTEN bit in the MCG_SC register. + * + * - If this flag is set, MCGIRCLK internal clock signal will be available + * for clocking peripherals during CPU STOP modes. + * - If not set, the MCGIRCLK internal clock signal will be stopped during + * CPU STOP modes. + */ + KINETIS_CLOCK_MCGIRCLK_STOP_EN = (1 << 4), +} kinetis_clock_flags_t; + /** * @brief Clock configuration for Kinetis CPUs */ typedef struct { - /** Clock divider bitfield setting, see reference manual for SIM_CLKDIV1 */ + /** + * @brief Clock divider bitfield setting + * + * The value will be written to the SIM_CLKDIV1 hardware register without + * any transformation. Use the SIM_CLKDIV1_OUTDIVx() macros to ensure the + * proper bit shift for the chosen divider settings. + * + * @see CPU reference manual, SIM_CLKDIV1 + */ uint32_t clkdiv1; - /** MCG mode used after initialization, see kinetis_mcg_mode_t */ + /** + * @brief RTC oscillator Capacitor Load Configuration bits + * + * The bits will be passed directly to the RTC_CR register without any + * transformation, i.e. the SC16P bit is (unintuitively) at bit position 10, + * SC8P is at position 11, and so on (see details in the reference manual). + * Use the RTC_CR_SCxP_MASK macros to avoid accidentally reversing the bits + * here. + * + * @see CPU reference manual, RTC_CR[SCxP] + */ + uint32_t rtc_clc; + /** + * @brief ERCLK32K 32 kHz reference selection + * + * The bits will be passed directly to the SIM_SOPT1 register without any + * transformation, use the SIM_SOPT1_OSC32KSEL() macro to ensure the proper + * bit shift for the chosen setting. + * + * This signal is the input clock to the RTC module on some CPUs and an input + * option for the LPTMRx modules. On other CPUs the RTC is clocked directly + * by the RTC oscillator output without passing through this clock multiplexer. + * + * @see CPU reference manual, SIM_SOPT1[OSC32KSEL] + */ + uint32_t osc32ksel; + /** + * @brief Flags which will enable various clocking options at init + * + * @see @ref kinetis_clock_flags_t + */ + unsigned int clock_flags; + /** + * @brief MCG mode used after initialization + * + * @see @ref kinetis_mcg_mode_t + */ kinetis_mcg_mode_t default_mode; - /** ERC range setting, see kinetis_mcg_erc_range_t */ + /** + * @brief ERC range setting + * + * @see @ref kinetis_mcg_erc_range_t + */ kinetis_mcg_erc_range_t erc_range; - /** Fast internal reference clock divider, see reference manual for MCG_SC[FCRDIV] */ - uint8_t fcrdiv; - /** Oscillator selection, see reference manual for MCG_C7[OSCSEL] */ + /** + * @brief OSC0 Capacitor Load Configuration bits + * + * The bits will be passed directly to the OSC_CR register without any + * transformation, i.e. the SC16P bit is (unintuitively) the LSB, SC8P is + * the next bit, and so on (see details in the reference manual). Use the + * OSC_CR_SCxP_MASK macros to avoid accidentally reversing the bits here. + * + * @see CPU reference manual, OSC_CR[SCxP] + */ + uint8_t osc_clc; + /** + * @brief MCG external reference oscillator selection + * + * The bits will be passed directly to the MCG_C7 register without any + * transformation, use the MCG_C7_OSCSEL() macro to ensure the proper bit + * shift for the chosen setting. + * + * @see CPU reference manual, MCG_C7[OSCSEL] + */ uint8_t oscsel; - /** Capacitor Load configuration bits, see reference manual for OSC_CR */ - uint8_t clc; - /** FLL ERC divider setting, see reference manual for MCG_C1[FRDIV] */ + /** + * @brief Fast internal reference clock divider + * + * 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. + * + * @see CPU reference manual, MCG_SC[FCRDIV] + */ + uint8_t fcrdiv; + /** + * @brief FLL ERC divider setting + * + * The bits will be passed directly to the MCG_C1 register without any + * transformation, use the MCG_C1_FRDIV() macro to ensure the proper bit + * shift for the chosen setting. + * + * @see CPU reference manual, MCG_C1[FRDIV] + */ uint8_t fll_frdiv; - /** FLL multiplier when running in FEI mode */ + /** + * @brief FLL multiplier when running in FEI mode + * + * @see @ref kinetis_mcg_fll_t + * @see CPU reference manual, MCG_C4[DMX32, DRST_DRS] + */ kinetis_mcg_fll_t fll_factor_fei; - /** FLL multiplier when running in FEE mode */ + /** + * @brief FLL multiplier when running in FEE mode + * + * @see @ref kinetis_mcg_fll_t + * @see CPU reference manual, MCG_C4[DMX32, DRST_DRS] + */ kinetis_mcg_fll_t fll_factor_fee; #if KINETIS_HAVE_PLL - /** PLL ERC divider setting, see reference manual for MCG_C5[PRDIV] */ + /** + * @brief PLL ERC divider setting + * + * The bits will be passed directly to the MCG_C5 register without any + * transformation, use the MCG_C5_PRDIV0() macro to ensure the proper bit + * shift for the chosen setting. + * + * @see CPU reference manual, MCG_C5[PRDIV0] + */ uint8_t pll_prdiv; - /** PLL VCO divider setting, see reference manual for MCG_C6[VDIV0] */ + /** + * @brief PLL VCO divider setting + * + * The bits will be passed directly to the MCG_C6 register without any + * transformation, use the MCG_C6_VDIV0() macro to ensure the proper bit + * shift for the chosen setting. + * + * @see CPU reference manual, MCG_C6[VDIV0] + */ uint8_t pll_vdiv; #endif /* KINETIS_HAVE_PLL */ - /** - * @brief External reference clock selection - * - * True: Use oscillator circuit with external crystal. - * False: Use external clock signal directly. - */ - bool enable_oscillator; - /** - * @brief Use fast internal reference clock for MCGIRCLK - * - * See reference manual for MCG module and MCG_C2[IRCS] - */ - bool select_fast_irc; - /** - * @brief Enable MCGIRCLK output from MCG for use as alternate clock in some modules - */ - bool enable_mcgirclk; } clock_config_t; /** diff --git a/cpu/kinetis/periph/mcg.c b/cpu/kinetis/periph/mcg.c index 5da62dad07..89544fdcca 100644 --- a/cpu/kinetis/periph/mcg.c +++ b/cpu/kinetis/periph/mcg.c @@ -74,10 +74,9 @@ static void kinetis_mcg_enable_osc(void) #if defined(OSC0) /* Kinetis CPU with OSC module */ /* Enable Oscillator */ - if (clock_config.enable_oscillator) { + if (clock_config.clock_flags & KINETIS_CLOCK_OSC0_EN) { /* Configure oscillator */ - OSC0->CR = (uint8_t)(OSC_CR_ERCLKEN_MASK | OSC_CR_EREFSTEN_MASK | - (clock_config.clc & 0xf)); + 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 */ @@ -97,24 +96,86 @@ static void kinetis_mcg_enable_osc(void) * to the RSIM clock output, thus the RSIM, instead of the MCG, controls the * external clock source selection. */ - /* 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); - - if (clock_config.enable_oscillator) { + 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); - /* Wait for oscillator ready signal */ - while((RSIM->CONTROL & RSIM_CONTROL_RF_OSC_READY_MASK) == 0) {} } 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 */ + if (clock_config.clock_flags & KINETIS_CLOCK_USE_FAST_IRC) { + /* Fast IRC divider setting */ + uint8_t tmp = MCG->SC; + /* Avoid clearing w1c flags during writeback */ + tmp &= ~(MCG_SC_ATMF_MASK | MCG_SC_LOCS0_MASK); + /* Write new FCRDIV setting */ + tmp &= ~MCG_SC_FCRDIV_MASK; + tmp |= clock_config.fcrdiv; + MCG->SC = tmp; + bit_set8(&MCG->C2, MCG_C2_IRCS_SHIFT); + } + else { + bit_clear8(&MCG->C2, MCG_C2_IRCS_SHIFT); + } + /* 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 FLL Engaged Internal Mode. * @@ -416,42 +477,23 @@ void kinetis_mcg_init(void) SIM->CLKDIV1 = clock_config.clkdiv1; /* Select external reference clock source for the FLL */ - MCG->C7 = MCG_C7_OSCSEL(clock_config.oscsel); + MCG->C7 = clock_config.oscsel; /* Set external reference clock divider for the FLL */ - MCG->C1 = (MCG->C1 & ~MCG_C1_FRDIV_MASK) | MCG_C1_FRDIV(clock_config.fll_frdiv); + MCG->C1 = (MCG->C1 & ~MCG_C1_FRDIV_MASK) | clock_config.fll_frdiv; #if KINETIS_HAVE_PLL /* set ERC divider for the PLL */ - MCG->C5 = (uint8_t)(MCG_C5_PRDIV0(clock_config.pll_prdiv)); + MCG->C5 = clock_config.pll_prdiv; /* set PLL VCO divider */ - MCG->C6 = (uint8_t)(MCG_C6_VDIV0(clock_config.pll_vdiv)); + MCG->C6 = clock_config.pll_vdiv; #endif /* KINETIS_HAVE_PLL */ - /* Configure internal reference clock */ - if (clock_config.select_fast_irc) { - /* Fast IRC divider setting */ - uint8_t tmp = MCG->SC; - /* Avoid clearing w1c flags during writeback */ - tmp &= ~(MCG_SC_ATMF_MASK | MCG_SC_LOCS0_MASK); - /* Write new FCRDIV setting */ - tmp &= ~MCG_SC_FCRDIV_MASK; - tmp |= MCG_SC_FCRDIV(clock_config.fcrdiv); - MCG->SC = tmp; - bit_set8(&MCG->C2, MCG_C2_IRCS_SHIFT); - } - else { - bit_clear8(&MCG->C2, MCG_C2_IRCS_SHIFT); - } - /* Enable/disable MCGIRCLK */ - /* MCGIRCLK can be used as an alternate clock source for certain modules */ - if (clock_config.enable_mcgirclk) { - bit_set8(&MCG->C1, MCG_C1_IRCLKEN_SHIFT); - } - else { - bit_clear8(&MCG->C1, MCG_C1_IRCLKEN_SHIFT); - } + 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);