Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 26 additions & 1 deletion cpu/kinetis_common/include/mcg.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
* @defgroup cpu_kinetis_common_mcg Kinetis MCG
* @ingroup cpu_kinetis_common
* @brief Implementation of the Kinetis Multipurpose Clock Generator
* (MCG) driver.
* (MCG) driver
*
* Please add mcg.h in cpu conf.h
* and MCG configuration to periph_conf.h
*
Expand All @@ -37,6 +38,30 @@
* - BLPE -> FEE
* - BLPI -> FEE
*
* \dot
* digraph states {
* layout=dot
* nodesep=0.5
* {rank=same Reset [shape=none] FEI FEE}
* {rank=same FBI FBE}
* {rank=same BLPI BLPE}
* Reset -> FEI
* FEI -> FEE [dir="both"]
* FEI -> FBE [dir="both"]
* FEI -> FBI [dir="both"]
* FEE -> FBI [dir="both"]
* FEE -> FBE [dir="both"]
* FBI -> FBE [dir="both"]
* FBI -> BLPI [dir="both"]
* FBE -> BLPE [dir="both"]
* PBE
* PEE
* FBE -> PBE [dir="both"]
* BLPE -> PBE [dir="both"]
* PBE -> PEE [dir="both"]
* }
* \enddot
*
* ### MCG Configuration Examples (for periph_conf.h) ###
*
* Example for FEI Mode (MCGOUTCLK = 20MHz ... 25MHz):
Expand Down
188 changes: 64 additions & 124 deletions cpu/kinetis_common/periph/mcg.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/*
* 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
Expand All @@ -14,7 +15,8 @@
* @brief Implementation of the Kinetis Multipurpose Clock Generator
*
* @author Johann Fischer <j.fischer@phytec.de>
* @}
* @author Joakim Nohlgård <joakim.nohlgard@eistec.se>
*
*/

#include <stdint.h>
Expand All @@ -23,8 +25,24 @@

#if KINETIS_CPU_USE_MCG

/* MCG neighbor modes matrix */
static uint8_t mcg_pm[8] = {0x02, 0x15, 0x12, 0x20, 0xe6, 0xd8, 0xb0, 0xf0};
#if defined(MCG_C6_PLLS_MASK)
#define KINETIS_HAVE_PLL 1
#else
#define KINETIS_HAVE_PLL 0
#endif

/* Pathfinding for the clocking modes, this table lists the next mode in the
* chain when moving from mode <first> to mode <second> */
static const uint8_t mcg_mode_routing[8][8] = {
{0, 1, 1, 1, 1, 1, 1, 1}, /* from PEE */
{0, 1, 2, 4, 4, 4, 4, 4}, /* from PBE */
{1, 1, 2, 4, 4, 4, 4, 4}, /* from BLPE */
{5, 5, 5, 3, 5, 5, 5, 5}, /* from BLPI */
{1, 1, 2, 5, 4, 5, 6, 7}, /* from FBE */
{4, 4, 4, 3, 4, 5, 6, 7}, /* from FBI */
{4, 4, 4, 5, 4, 5, 6, 7}, /* from FEE */
{4, 4, 4, 5, 4, 5, 6, 7}, /* from FEI */
};

static uint8_t current_mode = KINETIS_MCG_FEI;

Expand Down Expand Up @@ -97,6 +115,7 @@ static uint8_t current_mode = KINETIS_MCG_FEI;
#error "KINETIS_MCG_ERC_RANGE not defined in periph_conf.h"
#endif

#if KINETIS_HAVE_PLL
#ifndef KINETIS_MCG_PLL_PRDIV
#error "KINETIS_MCG_PLL_PRDIV not defined in periph_conf.h"
#endif
Expand All @@ -118,6 +137,9 @@ static inline void kinetis_mcg_disable_pll(void)
MCG->C5 = (uint8_t)0;
MCG->C6 = (uint8_t)0;
}
#else
static inline void kinetis_mcg_disable_pll(void) {}
#endif /* KINETIS_HAVE_PLL */

/**
* @brief Set Frequency Locked Loop (FLL) factor.
Expand Down Expand Up @@ -161,7 +183,7 @@ static void kinetis_mcg_enable_osc(void)
MCG->C2 |= (uint8_t)(MCG_C2_EREFS0_MASK);

/* wait fo OSC initialization */
while ((MCG->S & MCG_S_OSCINIT0_MASK) == 0);
while ((MCG->S & MCG_S_OSCINIT0_MASK) == 0) {}
}
}

Expand All @@ -182,10 +204,10 @@ static void kinetis_mcg_set_fei(void)
MCG->C2 = (uint8_t)0;

/* source of the FLL reference clock shall be internal reference clock */
while ((MCG->S & MCG_S_IREFST_MASK) == 0);
while ((MCG->S & MCG_S_IREFST_MASK) == 0) {}

/* Wait until output of the FLL is selected */
while (MCG->S & (MCG_S_CLKST_MASK));
while (MCG->S & (MCG_S_CLKST_MASK)) {}

kinetis_mcg_disable_pll();
current_mode = KINETIS_MCG_FEI;
Expand All @@ -207,7 +229,7 @@ static void kinetis_mcg_set_fee(void)
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));
while ((MCG->S & MCG_S_CLKST_MASK) != MCG_S_CLKST(0)) {}

kinetis_mcg_disable_pll();
current_mode = KINETIS_MCG_FEE;
Expand Down Expand Up @@ -237,10 +259,10 @@ static void kinetis_mcg_set_fbi(void)
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));
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);
while ((MCG->S & MCG_S_IREFST_MASK) == 0) {}

kinetis_mcg_disable_pll();
current_mode = KINETIS_MCG_FBI;
Expand All @@ -266,7 +288,7 @@ static void kinetis_mcg_set_fbe(void)
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));
while ((MCG->S & MCG_S_CLKST_MASK) != MCG_S_CLKST(2)) {}

kinetis_mcg_disable_pll();
current_mode = KINETIS_MCG_FBE;
Expand Down Expand Up @@ -299,6 +321,7 @@ static void kinetis_mcg_set_blpe(void)
current_mode = KINETIS_MCG_BLPE;
}

#if KINETIS_HAVE_PLL
/**
* @brief Initialize the PLL Bypassed External Mode.
*
Expand All @@ -315,7 +338,7 @@ static void kinetis_mcg_set_pbe(void)
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));
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);
Expand All @@ -330,10 +353,10 @@ static void kinetis_mcg_set_pbe(void)
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);
while ((MCG->S & MCG_S_PLLST_MASK) == 0) {}

/* Wait until PLL locked */
while ((MCG->S & MCG_S_LOCK0_MASK) == 0);
while ((MCG->S & MCG_S_LOCK0_MASK) == 0) {}

current_mode = KINETIS_MCG_PBE;
}
Expand All @@ -350,135 +373,52 @@ 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));
while ((MCG->S & MCG_S_CLKST_MASK) != MCG_S_CLKST(3)) {}

current_mode = KINETIS_MCG_PEE;
}

#endif /* KINETIS_HAVE_PLL */

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();
while (current_mode != mode) {
switch(mcg_mode_routing[current_mode][mode]) {
#if KINETIS_HAVE_PLL
case KINETIS_MCG_PEE:
kinetis_mcg_set_pee();
return 0;
}

if (current_mode == KINETIS_MCG_BLPE) {
/* set PBE -> PEE */
break;
case KINETIS_MCG_PBE:
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();
break;
#endif /* KINETIS_HAVE_PLL */
case KINETIS_MCG_BLPE:
kinetis_mcg_set_blpe();
return 0;
}

break;

case KINETIS_MCG_BLPI:
if (current_mode == KINETIS_MCG_FEE) {
/* set FBI -> BLPI */
kinetis_mcg_set_fbi();
break;
case KINETIS_MCG_BLPI:
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 */
break;
case KINETIS_MCG_FBE:
kinetis_mcg_set_fbe();
kinetis_mcg_set_fee();
return 0;
}

if (current_mode == KINETIS_MCG_BLPI) {
/* set FBI -> FEE */
break;
case KINETIS_MCG_FBI:
kinetis_mcg_set_fbi();
break;
case KINETIS_MCG_FEE:
kinetis_mcg_set_fee();
return 0;
}

break;

default:
break;
break;
case KINETIS_MCG_FEI:
kinetis_mcg_set_fei();
break;
default:
return -1;
}
}

return -1;
return 0;
}

#endif /* KINETIS_CPU_USE_MCG */
/** @} */