1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/cpu/kinetis_common/mcg.c
Johann F c0628a3058 cpu/kinetis_common: initial import for kinetis_common
add peripheral drivers for Freescale Kinetis MCUs:
    adc driver
    cpuid driver
    gpio driver
    hwtimer_arch driver (hwtimer used Low Power Timer)
    i2c driver (master mode only)
    mcg driver
    pwm driver
    random_rnga driver
    random_rngb driver
    rtc driver
    spi driver
    timer driver (timer used Periodic Interrupt Timer)
    uart driver
  add doc.txt (configuration examples)

  random_rnga: Update RNGA driver in preparation for RNGB driver.
  random_rngb: Add RNGB driver.
  spi: refactor SPI to work for multiple CTARS, add spi_acquire, spi_release
  gpio: Add gpio_irq_enable, gpio_irq_disable. Refactor GPIO.
  gpio: Add gpio_irq_enable, gpio_irq_disable.
  gpio: Refactor ISR functions to work with all GPIOs (0-31) and all ports (PORTA-PORTH)
  adc: Refactor ADC, add calibration and scaling.
    Added integer scaling of results in adc_map.
    Handle precision setting in adc_init.
    Set ADC clock divider depending on module clock.
    Add ADC_1 as a possible device.
    Add ADC calibration procedure according to K60 ref manual.
    Handle ADC pins which are not part of the pin function mux.
  Signed-off-by: Joakim Gebart <joakim.gebart@eistec.se>
2015-02-04 14:50:54 +01:00

485 lines
13 KiB
C

/*
* Copyright (C) 2015 PHYTEC Messtechnik GmbH
*
* 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_common_mcg
* @{
*
* @file
* @brief Implementation of the Kinetis Multipurpose Clock Generator
*
* @author Johann Fischer <j.fischer@phytec.de>
* @}
*/
#include <stdint.h>
#include "mcg.h"
#include "periph_conf.h"
#if KINETIS_CPU_USE_MCG
/* MCG neighbor modes matrix */
static uint8_t mcg_pm[8] = {0x02, 0x15, 0x12, 0x20, 0xe6, 0xd8, 0xb0, 0xf0};
static uint8_t current_mode = KINETIS_MCG_FEI;
#define KINETIS_MCG_FLL_FACTOR_640 0
#define KINETIS_MCG_FLL_FACTOR_732 (MCG_C4_DRST_DRS(0) | MCG_C4_DMX32_MASK)
#define KINETIS_MCG_FLL_FACTOR_1280 (MCG_C4_DRST_DRS(1))
#define KINETIS_MCG_FLL_FACTOR_1464 (MCG_C4_DRST_DRS(1) | MCG_C4_DMX32_MASK)
#define KINETIS_MCG_FLL_FACTOR_1920 (MCG_C4_DRST_DRS(2))
#define KINETIS_MCG_FLL_FACTOR_2197 (MCG_C4_DRST_DRS(2) | MCG_C4_DMX32_MASK)
#define KINETIS_MCG_FLL_FACTOR_2560 (MCG_C4_DRST_DRS(3))
#define KINETIS_MCG_FLL_FACTOR_2929 (MCG_C4_DRST_DRS(3) | MCG_C4_DMX32_MASK)
#ifndef KINETIS_MCG_DCO_RANGE
#define KINETIS_MCG_DCO_RANGE (48000000U)
#endif
/* Default DCO_RANGE should be defined in periph_conf.h */
#if (KINETIS_MCG_DCO_RANGE == 24000000U)
#define KINETIS_MCG_FLL_FACTOR_FEI KINETIS_MCG_FLL_FACTOR_640
#define KINETIS_MCG_FLL_FACTOR_FEE KINETIS_MCG_FLL_FACTOR_732
#elif (KINETIS_MCG_DCO_RANGE == 48000000U)
#define KINETIS_MCG_FLL_FACTOR_FEI KINETIS_MCG_FLL_FACTOR_1280
#define KINETIS_MCG_FLL_FACTOR_FEE KINETIS_MCG_FLL_FACTOR_1464
#elif (KINETIS_MCG_DCO_RANGE == 72000000U)
#define KINETIS_MCG_FLL_FACTOR_FEI KINETIS_MCG_FLL_FACTOR_1920
#define KINETIS_MCG_FLL_FACTOR_FEE KINETIS_MCG_FLL_FACTOR_2197
#elif (KINETIS_MCG_DCO_RANGE == 96000000U)
#define KINETIS_MCG_FLL_FACTOR_FEI KINETIS_MCG_FLL_FACTOR_2560
#define KINETIS_MCG_FLL_FACTOR_FEE KINETIS_MCG_FLL_FACTOR_2929
#else
#error "KINETIS_MCG_DCO_RANGE is wrong"
#endif
#ifndef KINETIS_MCG_USE_FAST_IRC
#define KINETIS_MCG_USE_FAST_IRC 0
#endif
#if (KINETIS_MCG_USE_ERC == 0)
#define KINETIS_MCG_USE_ERC 0
#define KINETIS_MCG_USE_PLL 0
#define KINETIS_MCG_ERC_FREQ 0
#define KINETIS_MCG_ERC_FRDIV 0
#define KINETIS_MCG_ERC_RANGE 0
#define KINETIS_MCG_ERC_OSCILLATOR 0
#endif
#if (KINETIS_MCG_USE_PLL == 0)
#define KINETIS_MCG_PLL_PRDIV 0
#define KINETIS_MCG_PLL_VDIV0 0
#define KINETIS_MCG_PLL_FREQ 0
#endif
#ifndef KINETIS_MCG_OSC_CLC
#define KINETIS_MCG_OSC_CLC 0
#endif
#ifndef KINETIS_MCG_ERC_OSCILLATOR
#error "KINETIS_MCG_ERC_OSCILLATOR not defined in periph_conf.h"
#endif
#ifndef KINETIS_MCG_ERC_FREQ
#error "KINETIS_MCG_ERC_FREQ not defined in periph_conf.h"
#endif
#ifndef KINETIS_MCG_ERC_FRDIV
#error "KINETIS_MCG_ERC_FRDIV not defined in periph_conf.h"
#endif
#ifndef KINETIS_MCG_ERC_RANGE
#error "KINETIS_MCG_ERC_RANGE not defined in periph_conf.h"
#endif
#ifndef KINETIS_MCG_PLL_PRDIV
#error "KINETIS_MCG_PLL_PRDIV not defined in periph_conf.h"
#endif
#ifndef KINETIS_MCG_PLL_VDIV0
#error "KINETIS_MCG_PLL_VDIV0 not defined in periph_conf.h"
#endif
#ifndef KINETIS_MCG_PLL_FREQ
#error "KINETIS_MCG_PLL_FREQ not defined in periph_conf.h"
#endif
/**
* @brief Disable Phase Locked Loop (PLL)
*
*/
static inline void kinetis_mcg_disable_pll(void)
{
MCG->C5 = (uint8_t)0;
MCG->C6 = (uint8_t)0;
}
/**
* @brief Set Frequency Locked Loop (FLL) factor.
*
* The FLL will lock the DCO frequency to the FLL factor.
* FLL factor will be selected by DRST_DRS and DMX32 bits
* and depends on KINETIS_MCG_DCO_RANGE value.
*/
static inline void kinetis_mcg_set_fll_factor(uint8_t factor)
{
MCG->C4 &= ~(uint8_t)(MCG_C4_DMX32_MASK | MCG_C4_DRST_DRS_MASK);
MCG->C4 |= (uint8_t)(factor);
}
/**
* @brief Enable Oscillator module
*
* The module settings depend on KINETIS_MCG_ERC_RANGE
* KINETIS_MCG_ERC_OSCILLATOR values.
*/
static void kinetis_mcg_enable_osc(void)
{
if (KINETIS_MCG_ERC_RANGE == 1) {
/* select high frequency range and oscillator clock */
MCG->C2 = (uint8_t)(MCG_C2_RANGE0(1));
}
else if (KINETIS_MCG_ERC_RANGE == 2) {
/* select very high frequency range and osciallor clock */
MCG->C2 = (uint8_t)(MCG_C2_RANGE0(2));
}
else {
/* select low frequency range and osciallor clock */
MCG->C2 = (uint8_t)(MCG_C2_RANGE0(0));
}
OSC0->CR = (uint8_t)(OSC_CR_ERCLKEN_MASK | OSC_CR_EREFSTEN_MASK
| (KINETIS_MCG_OSC_CLC & 0xf));
/* Enable Oscillator */
if (KINETIS_MCG_ERC_OSCILLATOR) {
MCG->C2 |= (uint8_t)(MCG_C2_EREFS0_MASK);
/* wait fo OSC initialization */
while ((MCG->S & MCG_S_OSCINIT0_MASK) == 0);
}
}
/**
* @brief Initialize the FLL Engaged Internal Mode.
*
* MCGOUTCLK is derived from the FLL clock.
* Clock source is the 32kHz slow Internal Reference Clock.
* The FLL loop will lock the DCO frequency to the FLL-Factor.
*/
static void kinetis_mcg_set_fei(void)
{
kinetis_mcg_set_fll_factor(KINETIS_MCG_FLL_FACTOR_FEI);
/* enable and select slow internal reference clock */
MCG->C1 = (uint8_t)(MCG_C1_CLKS(0) | MCG_C1_IREFS_MASK);
/* set to defaults */
MCG->C2 = (uint8_t)0;
/* source of the FLL reference clock shall be internal reference clock */
while ((MCG->S & MCG_S_IREFST_MASK) == 0);
/* Wait until output of the FLL is selected */
while (MCG->S & (MCG_S_CLKST_MASK));
kinetis_mcg_disable_pll();
current_mode = KINETIS_MCG_FEI;
}
/**
* @brief Initialize the FLL Engaged External Mode.
*
* MCGOUTCLK is derived from the FLL clock.
* Clock source is the external reference clock (IRC or oscillator).
* The FLL loop will lock the DCO frequency to the FLL-Factor.
*/
static void kinetis_mcg_set_fee(void)
{
kinetis_mcg_enable_osc();
kinetis_mcg_set_fll_factor(KINETIS_MCG_FLL_FACTOR_FEE);
/* select external reference clock and divide factor */
MCG->C1 = (uint8_t)(MCG_C1_CLKS(0) | MCG_C1_FRDIV(KINETIS_MCG_ERC_FRDIV));
/* Wait until output of FLL is selected */
while ((MCG->S & MCG_S_CLKST_MASK) != MCG_S_CLKST(0));
kinetis_mcg_disable_pll();
current_mode = KINETIS_MCG_FEE;
}
/**
* @brief Initialize the FLL Bypassed Internal Mode.
*
* MCGOUTCLK is derived from 32kHz IRC or 4MHz IRC.
* FLL output is not used.
* FLL clock source is internal 32kHz IRC.
* The FLL loop will lock the DCO frequency to the FLL-Factor.
* Next useful mode: BLPI or FEI.
*/
static void kinetis_mcg_set_fbi(void)
{
/* select IRC source */
if (KINETIS_MCG_USE_FAST_IRC) {
MCG->C2 = (uint8_t)(MCG_C2_IRCS_MASK);
}
else {
MCG->C2 = (uint8_t)(0);
}
kinetis_mcg_set_fll_factor(KINETIS_MCG_FLL_FACTOR_FEI);
/* enable and select slow internal reference clock */
MCG->C1 = (uint8_t)(MCG_C1_CLKS(1) | MCG_C1_IREFS_MASK);
/* Wait until output of IRC is selected */
while ((MCG->S & MCG_S_CLKST_MASK) != MCG_S_CLKST(1));
/* source of the FLL reference clock shall be internal reference clock */
while ((MCG->S & MCG_S_IREFST_MASK) == 0);
kinetis_mcg_disable_pll();
current_mode = KINETIS_MCG_FBI;
}
/**
* @brief Initialize the FLL Bypassed External Mode.
*
* MCGOUTCLK is derived from external reference clock (oscillator).
* FLL output is not used.
* Clock source is the external reference clock (oscillator).
* The FLL loop will lock the DCO frequency to the FLL-Factor.
*/
static void kinetis_mcg_set_fbe(void)
{
kinetis_mcg_enable_osc();
kinetis_mcg_set_fll_factor(KINETIS_MCG_FLL_FACTOR_FEE);
/* FLL is not disabled in bypass mode */
MCG->C2 &= ~(uint8_t)(MCG_C2_LP_MASK);
/* select external reference clock and divide factor */
MCG->C1 = (uint8_t)(MCG_C1_CLKS(2) | MCG_C1_FRDIV(KINETIS_MCG_ERC_FRDIV));
/* Wait until ERC is selected */
while ((MCG->S & MCG_S_CLKST_MASK) != MCG_S_CLKST(2));
kinetis_mcg_disable_pll();
current_mode = KINETIS_MCG_FBE;
}
/**
* @brief Initialize the FLL Bypassed Low Power Internal Mode.
*
* MCGOUTCLK is derived from IRC.
* FLL and PLL are disable.
* Previous and next allowed mode is FBI.
*/
static void kinetis_mcg_set_blpi(void)
{
MCG->C2 |= (uint8_t)(MCG_C2_LP_MASK);
current_mode = KINETIS_MCG_BLPI;
}
/**
* @brief Initialize the FLL Bypassed Low Power External Mode.
*
* MCGOUTCLK is derived from ERC.
* FLL and PLL are disable.
* Previous and next allowed mode: FBE or PBE.
*/
static void kinetis_mcg_set_blpe(void)
{
MCG->C2 |= (uint8_t)(MCG_C2_LP_MASK);
current_mode = KINETIS_MCG_BLPE;
}
/**
* @brief Initialize the PLL Bypassed External Mode.
*
* MCGOUTCLK is derived from external reference clock (oscillator).
* PLL output is not used.
* Clock source is the external reference clock (oscillator).
* The PLL loop will locks to VDIV times the frequency
* corresponding by PRDIV.
* Previous allowed mode are FBE or BLPE.
*/
static void kinetis_mcg_set_pbe(void)
{
/* select external reference clock and divide factor */
MCG->C1 = (uint8_t)(MCG_C1_CLKS(2) | MCG_C1_FRDIV(KINETIS_MCG_ERC_FRDIV));
/* Wait until ERC is selected */
while ((MCG->S & MCG_S_CLKST_MASK) != MCG_S_CLKST(2));
/* PLL is not disabled in bypass mode */
MCG->C2 &= ~(uint8_t)(MCG_C2_LP_MASK);
/* set external reference devider */
MCG->C5 = (uint8_t)(MCG_C5_PRDIV0(KINETIS_MCG_PLL_PRDIV));
/* set external reference devider */
MCG->C6 = (uint8_t)(MCG_C6_VDIV0(KINETIS_MCG_PLL_VDIV0));
/* select PLL */
MCG->C6 |= (uint8_t)(MCG_C6_PLLS_MASK);
/* Wait until the source of the PLLS clock is PLL */
while ((MCG->S & MCG_S_PLLST_MASK) == 0);
/* Wait until PLL locked */
while ((MCG->S & MCG_S_LOCK0_MASK) == 0);
current_mode = KINETIS_MCG_PBE;
}
/**
* @brief Initialize the PLL Engaged External Mode.
*
* MCGOUTCLK is derived from PLL.
* PLL output is used.
* Previous and next allowed mode is PBE.
*/
static void kinetis_mcg_set_pee(void)
{
MCG->C1 &= ~(uint8_t)(MCG_C1_CLKS_MASK);
/* Wait until output of the PLL is selected */
while ((MCG->S & MCG_S_CLKST_MASK) != MCG_S_CLKST(3));
current_mode = KINETIS_MCG_PEE;
}
int kinetis_mcg_set_mode(kinetis_mcg_mode_t mode)
{
if (mode > KINETIS_MCG_FEI) {
return -1;
}
if (mcg_pm[current_mode] & (1 << mode)) {
if (mode == KINETIS_MCG_FEI) {
kinetis_mcg_set_fei();
}
if (mode == KINETIS_MCG_FBI) {
kinetis_mcg_set_fbi();
}
if (mode == KINETIS_MCG_FEE) {
kinetis_mcg_set_fee();
}
if (mode == KINETIS_MCG_FBE) {
kinetis_mcg_set_fbe();
}
if (mode == KINETIS_MCG_BLPI) {
kinetis_mcg_set_blpi();
}
if (mode == KINETIS_MCG_BLPE) {
kinetis_mcg_set_blpe();
}
if (mode == KINETIS_MCG_PBE) {
kinetis_mcg_set_pbe();
}
if (mode == KINETIS_MCG_PEE) {
kinetis_mcg_set_pee();
}
return 0;
}
switch (mode) {
case KINETIS_MCG_PEE:
/* cppcheck-suppress duplicateExpression */
if (!(KINETIS_MCG_USE_ERC || KINETIS_MCG_USE_PLL)) {
return -1;
}
if (current_mode == KINETIS_MCG_FEI) {
/* set FBE -> PBE -> PEE */
kinetis_mcg_set_fbe();
kinetis_mcg_set_pbe();
kinetis_mcg_set_pee();
return 0;
}
if (current_mode == KINETIS_MCG_BLPE) {
/* set PBE -> PEE */
kinetis_mcg_set_pbe();
kinetis_mcg_set_pee();
return 0;
}
break;
case KINETIS_MCG_BLPE:
if (!KINETIS_MCG_USE_ERC) {
return -1;
}
if (current_mode == KINETIS_MCG_PEE) {
/* set PBE -> BLPE */
kinetis_mcg_set_pbe();
kinetis_mcg_set_blpe();
return 0;
}
if (current_mode == KINETIS_MCG_FEE) {
/* set FBE -> BLPE */
kinetis_mcg_set_fbe();
kinetis_mcg_set_blpe();
return 0;
}
break;
case KINETIS_MCG_BLPI:
if (current_mode == KINETIS_MCG_FEE) {
/* set FBI -> BLPI */
kinetis_mcg_set_fbi();
kinetis_mcg_set_blpi();
return 0;
}
break;
case KINETIS_MCG_FEE:
if (!KINETIS_MCG_USE_ERC) {
return -1;
}
if (current_mode == KINETIS_MCG_BLPE) {
/* set FBE -> FEE */
kinetis_mcg_set_fbe();
kinetis_mcg_set_fee();
return 0;
}
if (current_mode == KINETIS_MCG_BLPI) {
/* set FBI -> FEE */
kinetis_mcg_set_fbi();
kinetis_mcg_set_fee();
return 0;
}
break;
default:
break;
}
return -1;
}
#endif /* KINETIS_CPU_USE_MCG */