blob: 52051bc05a9347d3e8ea56e54834e9428a263610 [file] [log] [blame]
/* ----------------------------------------------------------------------------
* SAM Software Package License
* ----------------------------------------------------------------------------
* Copyright (c) 2015, Atmel Corporation
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the disclaimer below.
*
* Atmel's name may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
* DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* ----------------------------------------------------------------------------
*/
/** \addtogroup pmc_module Working with PMC
* \section Purpose
* The PMC driver provides the Interface for configuration the Power Management
* Controller (PMC).
*
* \section Usage
* <ul>
* <li> Enable & disable peripherals using pmc_enable_peripheral() and
* pmc_enable_all_peripherals() or pmc_disable_peripheral() and
* pmc_disable_all_peripherals().
* <li> Get & set maximum frequency clock for giving peripheral using
* pmc_get_peri_max_freq() and pmc_set_peri_max_clock().
* <li> Get Peripheral Status for the given peripheral using pmc_is_periph_enabled()
* <li> Select clocks's source using pmc_select_external_crystal(),
* pmc_select_internal_crystal(), pmc_select_external_osc() and pmc_select_internal_osc().
* <li> Switch MCK using pmc_switch_mck_to_pll(), pmc_switch_mck_to_main() and
* pmc_switch_mck_to_slck().
* <li> Config PLL using pmc_set_pll_a() and pmc_disable_pll_a().
* </li>
* </ul>
* For more accurate information, please look at the PMC section of the
* Datasheet.
*
* Related files :\n
* \ref pmc.c\n
* \ref pmc.h\n
*/
/*@{*/
/*@}*/
/**
* \file
*
* Implementation of PIO (Parallel Input/Output) controller.
*
*/
/*----------------------------------------------------------------------------
* Headers
*----------------------------------------------------------------------------*/
#include "chip.h"
#include "board.h"
#include "peripherals/pmc.h"
#include "trace.h"
#include <assert.h>
/*----------------------------------------------------------------------------
* Variables
*----------------------------------------------------------------------------*/
static uint32_t _pmc_mck = 0;
/*----------------------------------------------------------------------------
* Private functions
*----------------------------------------------------------------------------*/
static void _pmc_compute_mck(void)
{
uint32_t clk = 0;
uint32_t mckr = PMC->PMC_MCKR;
uint32_t css = mckr & PMC_MCKR_CSS_Msk;
switch (css) {
case PMC_MCKR_CSS_SLOW_CLK:
clk = pmc_get_slow_clock();
break;
case PMC_MCKR_CSS_MAIN_CLK:
clk = pmc_get_main_clock();
break;
case PMC_MCKR_CSS_PLLA_CLK:
clk = pmc_get_plla_clock();
break;
case PMC_MCKR_CSS_UPLL_CLK:
clk = pmc_get_upll_clock();
break;
default:
/* should never get here... */
break;
}
uint32_t pres = mckr & PMC_MCKR_PRES_Msk;
switch (pres) {
case PMC_MCKR_PRES_CLOCK:
break;
case PMC_MCKR_PRES_CLOCK_DIV2:
clk >>= 1;
break;
case PMC_MCKR_PRES_CLOCK_DIV4:
clk >>= 2;
break;
case PMC_MCKR_PRES_CLOCK_DIV8:
clk >>= 3;
break;
case PMC_MCKR_PRES_CLOCK_DIV16:
clk >>= 4;
break;
case PMC_MCKR_PRES_CLOCK_DIV32:
clk >>= 5;
break;
case PMC_MCKR_PRES_CLOCK_DIV64:
clk >>= 6;
break;
default:
/* should never get here... */
break;
}
uint32_t mdiv = mckr & PMC_MCKR_MDIV_Msk;
switch (mdiv) {
case PMC_MCKR_MDIV_EQ_PCK:
break;
case PMC_MCKR_MDIV_PCK_DIV2:
clk >>= 1; // divide by 2
break;
case PMC_MCKR_MDIV_PCK_DIV4:
clk >>= 2; // divide by 4
break;
case PMC_MCKR_MDIV_PCK_DIV3:
clk /= 3; // divide by 3
break;
default:
/* should never get here... */
break;
}
_pmc_mck = clk;
}
static uint32_t _pmc_get_pck_clock(uint32_t index)
{
uint32_t clk = 0;
uint32_t pck = PMC->PMC_PCK[index];
switch (pck & PMC_PCK_CSS_Msk) {
case PMC_PCK_CSS_SLOW_CLK:
clk = pmc_get_slow_clock();
break;
case PMC_PCK_CSS_MAIN_CLK:
clk = pmc_get_main_clock();
break;
case PMC_PCK_CSS_PLLA_CLK:
clk = pmc_get_plla_clock();
break;
case PMC_PCK_CSS_UPLL_CLK:
clk = pmc_get_upll_clock();
break;
case PMC_PCK_CSS_MCK_CLK:
clk = pmc_get_master_clock();
break;
#ifdef CONFIG_HAVE_PMC_AUDIO_CLOCK
case PMC_PCK_CSS_AUDIO_CLK:
clk = pmc_get_audio_pmc_clock();
break;
#endif
}
uint32_t prescaler = (pck & PMC_PCK_PRES_Msk) >> PMC_PCK_PRES_Pos;
return clk / (prescaler + 1);
}
static bool _pmc_get_system_clock_bits(enum _pmc_system_clock clock,
uint32_t *scer, uint32_t* scdr, uint32_t *scsr)
{
uint32_t e, d, s;
switch (clock)
{
#ifdef PMC_SCDR_PCK
case PMC_SYSTEM_CLOCK_PCK:
e = 0;
d = PMC_SCDR_PCK;
s = PMC_SCSR_PCK;
break;
#endif
case PMC_SYSTEM_CLOCK_DDR:
e = PMC_SCER_DDRCK;
d = PMC_SCDR_DDRCK;
s = PMC_SCSR_DDRCK;
break;
case PMC_SYSTEM_CLOCK_LCD:
e = PMC_SCER_LCDCK;
d = PMC_SCDR_LCDCK;
s = PMC_SCSR_LCDCK;
break;
#ifdef PMC_SCER_SMDCK
case PMC_SYSTEM_CLOCK_SMD:
e = PMC_SCER_SMDCK;
d = PMC_SCDR_SMDCK;
s = PMC_SCSR_SMDCK;
break;
#endif
case PMC_SYSTEM_CLOCK_UHP:
e = PMC_SCER_UHP;
d = PMC_SCDR_UHP;
s = PMC_SCSR_UHP;
break;
case PMC_SYSTEM_CLOCK_UDP:
e = PMC_SCER_UDP;
d = PMC_SCDR_UDP;
s = PMC_SCSR_UDP;
break;
case PMC_SYSTEM_CLOCK_PCK0:
e = PMC_SCER_PCK0;
d = PMC_SCDR_PCK0;
s = PMC_SCSR_PCK0;
break;
case PMC_SYSTEM_CLOCK_PCK1:
e = PMC_SCER_PCK1;
d = PMC_SCDR_PCK1;
s = PMC_SCSR_PCK1;
break;
case PMC_SYSTEM_CLOCK_PCK2:
e = PMC_SCER_PCK2;
d = PMC_SCDR_PCK2;
s = PMC_SCSR_PCK2;
break;
#ifdef PMC_SCER_ISCCK
case PMC_SYSTEM_CLOCK_ISC:
e = PMC_SCER_ISCCK;
d = PMC_SCDR_ISCCK;
s = PMC_SCSR_ISCCK;
break;
#endif
default:
return false;
}
if (scer) {
if (e)
*scer = e;
else
return false;
}
if (scdr) {
if (d)
*scdr = d;
else
return false;
}
if (scsr) {
if (s)
*scsr = s;
else
return false;
}
return true;
}
/*----------------------------------------------------------------------------
* Exported functions (General)
*----------------------------------------------------------------------------*/
uint32_t pmc_get_master_clock(void)
{
if (!_pmc_mck) {
_pmc_compute_mck();
}
return _pmc_mck;
}
uint32_t pmc_get_slow_clock(void)
{
if (SCKC->SCKC_CR & SCKC_CR_OSCSEL)
return SLOW_CLOCK_INT_OSC; /* on-chip slow clock RC */
else
return BOARD_SLOW_CLOCK_EXT_OSC; /* external crystal */
}
uint32_t pmc_get_main_clock(void)
{
if (PMC->CKGR_MOR & CKGR_MOR_MOSCSEL)
return MAIN_CLOCK_INT_OSC; /* on-chip main clock RC */
else
return BOARD_MAIN_CLOCK_EXT_OSC; /* external crystal */
}
uint32_t pmc_get_plla_clock(void)
{
uint32_t pllaclk, pllar, pllmula, plldiva;
if (PMC->CKGR_MOR & CKGR_MOR_MOSCSEL)
pllaclk = MAIN_CLOCK_INT_OSC; /* on-chip main clock RC */
else
pllaclk = BOARD_MAIN_CLOCK_EXT_OSC; /* external crystal */
pllar = PMC->CKGR_PLLAR;
pllmula = (pllar & CKGR_PLLAR_MULA_Msk) >> CKGR_PLLAR_MULA_Pos;
plldiva = (pllar & CKGR_PLLAR_DIVA_Msk) >> CKGR_PLLAR_DIVA_Pos;
if (plldiva == 0 || pllmula == 0) {
pllaclk = 0;
} else {
pllaclk = pllaclk * (pllmula + 1) / plldiva;
if (PMC->PMC_MCKR & PMC_MCKR_PLLADIV2)
pllaclk >>= 1;
}
return pllaclk;
}
uint32_t pmc_get_processor_clock(void)
{
uint32_t procclk, mdiv;
procclk = pmc_get_master_clock();
mdiv = PMC->PMC_MCKR & PMC_MCKR_MDIV_Msk;
switch (mdiv) {
case PMC_MCKR_MDIV_EQ_PCK:
break;
case PMC_MCKR_MDIV_PCK_DIV2:
procclk <<= 1; // multiply by 2
break;
case PMC_MCKR_MDIV_PCK_DIV3:
procclk *= 3; // multiply by 3
break;
case PMC_MCKR_MDIV_PCK_DIV4:
procclk <<= 2; // multiply by 4
break;
default:
/* should never get here... */
break;
}
return procclk;
}
void pmc_select_external_crystal(void)
{
int return_to_sclock = 0;
if (PMC->PMC_MCKR == PMC_MCKR_CSS(PMC_MCKR_CSS_SLOW_CLK)) {
pmc_switch_mck_to_main();
return_to_sclock = 1;
}
/* switch from internal RC 32kHz to external OSC 32 kHz */
SCKC->SCKC_CR = (SCKC->SCKC_CR & ~SCKC_CR_OSCSEL) | SCKC_CR_OSCSEL_XTAL;
/* Wait 5 slow clock cycles for internal resynchronization */
volatile int count;
for (count = 0; count < 0x1000; count++);
/* Switch to slow clock again if needed */
if (return_to_sclock)
pmc_switch_mck_to_slck();
}
void pmc_select_internal_crystal(void)
{
int return_to_sclock = 0;
if (PMC->PMC_MCKR == PMC_MCKR_CSS(PMC_MCKR_CSS_SLOW_CLK)) {
pmc_switch_mck_to_main();
return_to_sclock = 1;
}
/* switch from extenal OSC 32kHz to internal RC 32 kHz */
/* switch slow clock source to internal OSC 32 kHz */
SCKC->SCKC_CR = (SCKC->SCKC_CR & ~SCKC_CR_OSCSEL) | SCKC_CR_OSCSEL_RC;
/* Wait 5 slow clock cycles for internal resynchronization */
volatile int count;
for (count = 0; count < 0x1000; count++);
/* Switch to slow clock again if needed */
if (return_to_sclock)
pmc_switch_mck_to_slck();
}
void pmc_select_external_osc(void)
{
/* Enable external osc 12 MHz when needed */
if ((PMC->CKGR_MOR & CKGR_MOR_MOSCXTEN) != CKGR_MOR_MOSCXTEN) {
PMC->CKGR_MOR |= CKGR_MOR_MOSCXTST(18) | CKGR_MOR_MOSCXTEN | CKGR_MOR_KEY_PASSWD;
/* Wait Main Oscillator ready */
while(!(PMC->PMC_SR & PMC_SR_MOSCXTS));
}
/* Return if external osc had been selected */
if ((PMC->CKGR_MOR & CKGR_MOR_MOSCSEL) == CKGR_MOR_MOSCSEL)
return;
/* switch MAIN clock to external OSC 12 MHz */
PMC->CKGR_MOR |= CKGR_MOR_MOSCSEL | CKGR_MOR_KEY_PASSWD;
/* wait for the command to be taken into account */
while ((PMC->CKGR_MOR & CKGR_MOR_MOSCSEL) != CKGR_MOR_MOSCSEL);
/* wait MAIN clock status change for external OSC 12 MHz selection */
while (!(PMC->PMC_SR & PMC_SR_MOSCSELS));
/* disable internal RC 12 MHz to save power */
pmc_disable_internal_osc();
}
void pmc_disable_external_osc(void)
{
/* disable external OSC 12 MHz */
PMC->CKGR_MOR = (PMC->CKGR_MOR & ~CKGR_MOR_MOSCXTEN) | CKGR_MOR_KEY_PASSWD;
}
void pmc_select_internal_osc(void)
{
#ifdef CKGR_MOR_MOSCRCEN
/* Enable internal RC 12 MHz when needed */
if ((PMC->CKGR_MOR & CKGR_MOR_MOSCRCEN) != CKGR_MOR_MOSCRCEN) {
PMC->CKGR_MOR |= CKGR_MOR_MOSCRCEN | CKGR_MOR_KEY_PASSWD;
/* Wait internal 12 MHz RC Startup Time for clock stabilization */
while (!(PMC->PMC_SR & PMC_SR_MOSCRCS));
}
#endif
/* switch MAIN clock to internal RC 12 MHz */
PMC->CKGR_MOR = (PMC->CKGR_MOR & ~CKGR_MOR_MOSCSEL) | CKGR_MOR_KEY_PASSWD;
/* in case where MCK is running on MAIN CLK */
while (!(PMC->PMC_SR & PMC_SR_MCKRDY));
/* disable external OSC 12 MHz to save power*/
pmc_disable_external_osc();
}
void pmc_disable_internal_osc(void)
{
#ifdef CKGR_MOR_MOSCRCEN
/* disable internal RC 12 MHz */
PMC->CKGR_MOR = (PMC->CKGR_MOR & ~CKGR_MOR_MOSCRCEN) | CKGR_MOR_KEY_PASSWD;
#endif
}
void pmc_switch_mck_to_pll(void)
{
/* Select PLL as input clock for PCK and MCK */
PMC->PMC_MCKR = (PMC->PMC_MCKR & ~PMC_MCKR_CSS_Msk) | PMC_MCKR_CSS_PLLA_CLK;
while (!(PMC->PMC_SR & PMC_SR_MCKRDY));
_pmc_mck = 0;
}
void pmc_switch_mck_to_upll(void)
{
/* Select UPLL as input clock for PCK and MCK */
PMC->PMC_MCKR = (PMC->PMC_MCKR & ~PMC_MCKR_CSS_Msk) | PMC_MCKR_CSS_UPLL_CLK;
while (!(PMC->PMC_SR & PMC_SR_MCKRDY));
_pmc_mck = 0;
}
void pmc_switch_mck_to_main(void)
{
/* Select Main Oscillator as input clock for PCK and MCK */
PMC->PMC_MCKR = (PMC->PMC_MCKR & ~PMC_MCKR_CSS_Msk) | PMC_PCK_CSS_MAIN_CLK;
while (!(PMC->PMC_SR & PMC_SR_MCKRDY));
_pmc_mck = 0;
}
void pmc_switch_mck_to_slck(void)
{
/* Select Slow Clock as input clock for PCK and MCK */
PMC->PMC_MCKR = (PMC->PMC_MCKR & ~PMC_MCKR_CSS_Msk) | PMC_PCK_CSS_SLOW_CLK;
while (!(PMC->PMC_SR & PMC_SR_MCKRDY));
_pmc_mck = 0;
}
void pmc_set_mck_prescaler(uint32_t prescaler)
{
/* Change MCK Prescaler divider in PMC_MCKR register */
PMC->PMC_MCKR = (PMC->PMC_MCKR & ~PMC_MCKR_PRES_Msk) | prescaler;
while (!(PMC->PMC_SR & PMC_SR_MCKRDY));
}
void pmc_set_mck_plla_div(uint32_t divider)
{
if ((PMC->PMC_MCKR & PMC_MCKR_PLLADIV2) == PMC_MCKR_PLLADIV2) {
if (divider == 0) {
PMC->PMC_MCKR = (PMC->PMC_MCKR & ~PMC_MCKR_PLLADIV2);
}
} else {
if (divider == PMC_MCKR_PLLADIV2) {
PMC->PMC_MCKR = (PMC->PMC_MCKR | PMC_MCKR_PLLADIV2);
}
}
while (!(PMC->PMC_SR & PMC_SR_MCKRDY));
}
void pmc_set_mck_h32mxdiv(uint32_t divider)
{
if ((PMC->PMC_MCKR & PMC_MCKR_H32MXDIV) == PMC_MCKR_H32MXDIV_H32MXDIV2) {
if (divider == PMC_MCKR_H32MXDIV_H32MXDIV1) {
PMC->PMC_MCKR = (PMC->PMC_MCKR & ~PMC_MCKR_H32MXDIV);
}
} else {
if (divider == PMC_MCKR_H32MXDIV_H32MXDIV2) {
PMC->PMC_MCKR = (PMC->PMC_MCKR | PMC_MCKR_H32MXDIV_H32MXDIV2);
}
}
while (!(PMC->PMC_SR & PMC_SR_MCKRDY));
}
void pmc_set_mck_divider(uint32_t divider)
{
/* change MCK Prescaler divider in PMC_MCKR register */
PMC->PMC_MCKR = (PMC->PMC_MCKR & ~PMC_MCKR_MDIV_Msk) | divider;
while (!(PMC->PMC_SR & PMC_SR_MCKRDY));
}
void pmc_set_plla(uint32_t pll, uint32_t cpcr)
{
PMC->CKGR_PLLAR = pll;
PMC->PMC_PLLICPR = cpcr;
if ((pll & CKGR_PLLAR_DIVA_Msk) != CKGR_PLLAR_DIVA_0) {
while (!(PMC->PMC_SR & PMC_SR_LOCKA));
}
}
void pmc_disable_plla(void)
{
PMC->CKGR_PLLAR = (PMC->CKGR_PLLAR & ~CKGR_PLLAR_MULA_Msk) | CKGR_PLLAR_MULA(0);
}
void pmc_enable_system_clock(enum _pmc_system_clock clock)
{
uint32_t scer, scsr;
if (!_pmc_get_system_clock_bits(clock, &scer, NULL, &scsr))
return;
PMC->PMC_SCER |= scer;
while (!(PMC->PMC_SCSR & scsr));
}
void pmc_disable_system_clock(enum _pmc_system_clock clock)
{
uint32_t scdr, scsr;
if (!_pmc_get_system_clock_bits(clock, NULL, &scdr, &scsr))
return;
PMC->PMC_SCDR |= scdr;
while (PMC->PMC_SCSR & scsr);
}
#ifdef CONFIG_HAVE_PMC_FAST_STARTUP
void pmc_set_fast_startup_mode(uint32_t startup_mode)
{
PMC->PMC_FSMR = startup_mode;
}
void pmc_set_fast_startup_polarity(uint32_t high_level, uint32_t low_level)
{
PMC->PMC_FSPR &= ~low_level;
PMC->PMC_FSPR |= high_level;
}
#endif /* CONFIG_HAVE_PMC_FAST_STARTUP */
void pmc_set_custom_pck_mck(struct pck_mck_cfg *cfg)
{
pmc_switch_mck_to_slck();
if (cfg->ext12m)
pmc_select_external_osc();
else
pmc_select_internal_osc();
pmc_switch_mck_to_main();
if (cfg->ext32k)
pmc_select_external_crystal();
else
pmc_select_internal_crystal();
pmc_set_mck_prescaler(cfg->pck_pres);
pmc_set_mck_divider(cfg->mck_div);
pmc_set_mck_plla_div(cfg->plla_div2 ? PMC_MCKR_PLLADIV2 : 0);
if (cfg->plla_mul > 0) {
pmc_disable_plla();
uint32_t tmp = CKGR_PLLAR_ONE |
CKGR_PLLAR_PLLACOUNT(0x3F) |
CKGR_PLLAR_OUTA(0x0) |
CKGR_PLLAR_MULA(cfg->plla_mul) |
CKGR_PLLAR_DIVA(cfg->plla_div);
pmc_set_plla(tmp, PMC_PLLICPR_IPLL_PLLA(0x3));
} else {
pmc_disable_plla();
}
if (cfg->h32mxdiv2)
pmc_set_mck_h32mxdiv(PMC_MCKR_H32MXDIV_H32MXDIV2);
else
pmc_set_mck_h32mxdiv(PMC_MCKR_H32MXDIV_H32MXDIV1);
switch (cfg->pck_input) {
case PMC_MCKR_CSS_PLLA_CLK:
pmc_switch_mck_to_pll();
break;
case PMC_MCKR_CSS_UPLL_CLK:
pmc_switch_mck_to_upll();
break;
case PMC_MCKR_CSS_SLOW_CLK:
pmc_switch_mck_to_slck();
pmc_disable_internal_osc();
pmc_disable_external_osc();
break;
}
}
/*----------------------------------------------------------------------------
* Exported functions (Peripherals)
*----------------------------------------------------------------------------*/
void pmc_enable_peripheral(uint32_t id)
{
assert(id > 1 && id < ID_PERIPH_COUNT);
PMC->PMC_PCR = PMC_PCR_PID(id);
volatile uint32_t pcr = PMC->PMC_PCR;
PMC->PMC_PCR = pcr | PMC_PCR_CMD | PMC_PCR_EN;
}
void pmc_disable_peripheral(uint32_t id)
{
assert(id > 1 && id < ID_PERIPH_COUNT);
PMC->PMC_PCR = PMC_PCR_PID(id);
volatile uint32_t pcr = PMC->PMC_PCR;
PMC->PMC_PCR = PMC_PCR_CMD | (pcr & ~PMC_PCR_EN);
}
uint32_t pmc_is_peripheral_enabled(uint32_t id)
{
assert(id > 1 && id < ID_PERIPH_COUNT);
PMC->PMC_PCR = PMC_PCR_PID(id);
volatile uint32_t pcr = PMC->PMC_PCR;
return !!(pcr & PMC_PCR_EN);
}
uint32_t pmc_get_peripheral_clock(uint32_t id)
{
assert(id > 1 && id < ID_PERIPH_COUNT);
uint32_t div = get_peripheral_clock_divider(id);
if (div)
return pmc_get_master_clock() / div;
return 0;
}
void pmc_disable_all_peripherals(void)
{
int i;
for (i = 2; i < ID_PERIPH_COUNT; i++)
pmc_disable_peripheral(i);
}
/*----------------------------------------------------------------------------
* Exported functions (PCK0-2)
*----------------------------------------------------------------------------*/
void pmc_configure_pck0(uint32_t clock_source, uint32_t prescaler)
{
pmc_disable_pck0();
PMC->PMC_PCK[0] = (clock_source & PMC_PCK_CSS_Msk) | PMC_PCK_PRES(prescaler);
}
void pmc_enable_pck0(void)
{
PMC->PMC_SCER = PMC_SCER_PCK0;
while (!(PMC->PMC_SR & PMC_SR_PCKRDY0));
}
void pmc_disable_pck0(void)
{
PMC->PMC_SCDR = PMC_SCDR_PCK0;
while (PMC->PMC_SCSR & PMC_SCSR_PCK0);
}
uint32_t pmc_get_pck0_clock(void)
{
return _pmc_get_pck_clock(0);
}
void pmc_configure_pck1(uint32_t clock_source, uint32_t prescaler)
{
pmc_disable_pck1();
PMC->PMC_PCK[1] = (clock_source & PMC_PCK_CSS_Msk) | PMC_PCK_PRES(prescaler);
}
void pmc_enable_pck1(void)
{
PMC->PMC_SCER = PMC_SCER_PCK1;
while (!(PMC->PMC_SR & PMC_SR_PCKRDY1));
}
void pmc_disable_pck1(void)
{
PMC->PMC_SCDR = PMC_SCDR_PCK1;
while (PMC->PMC_SCSR & PMC_SCSR_PCK1);
}
uint32_t pmc_get_pck1_clock(void)
{
return _pmc_get_pck_clock(1);
}
void pmc_configure_pck2(uint32_t clock_source, uint32_t prescaler)
{
pmc_disable_pck2();
PMC->PMC_PCK[2] = (clock_source & PMC_PCK_CSS_Msk) | PMC_PCK_PRES(prescaler);
}
void pmc_enable_pck2(void)
{
PMC->PMC_SCER = PMC_SCER_PCK2;
while (!(PMC->PMC_SR & PMC_SR_PCKRDY2));
}
void pmc_disable_pck2(void)
{
PMC->PMC_SCDR = PMC_SCDR_PCK2;
while (PMC->PMC_SCSR & PMC_SCSR_PCK2);
}
uint32_t pmc_get_pck2_clock(void)
{
return _pmc_get_pck_clock(2);
}
/*----------------------------------------------------------------------------
* Exported functions (UPLL)
*----------------------------------------------------------------------------*/
void pmc_enable_upll_clock(void)
{
/* enable 480Mhz UPLL */
PMC->CKGR_UCKR = CKGR_UCKR_UPLLEN | CKGR_UCKR_UPLLCOUNT(0x3)
| CKGR_UCKR_BIASCOUNT(0x1);
/* wait until UPLL is locked */
while (!(PMC->PMC_SR & PMC_SR_LOCKU));
}
void pmc_disable_upll_clock(void)
{
PMC->CKGR_UCKR &= ~CKGR_UCKR_UPLLEN;
}
uint32_t pmc_get_upll_clock(void)
{
#ifdef SFR_UTMICKTRIM_FREQ_Msk
uint32_t clktrim = SFR->SFR_UTMICKTRIM & SFR_UTMICKTRIM_FREQ_Msk;
switch (clktrim) {
case SFR_UTMICKTRIM_FREQ_16:
return 30 * BOARD_MAIN_CLOCK_EXT_OSC;
case SFR_UTMICKTRIM_FREQ_24:
return 20 * BOARD_MAIN_CLOCK_EXT_OSC;
default:
return 40 * BOARD_MAIN_CLOCK_EXT_OSC;
}
#else
return 40 * BOARD_MAIN_CLOCK_EXT_OSC;
#endif
}
void pmc_enable_upll_bias(void)
{
PMC->CKGR_UCKR |= CKGR_UCKR_BIASEN;
}
void pmc_disable_upll_bias(void)
{
PMC->CKGR_UCKR &= ~CKGR_UCKR_BIASEN;
}
/*----------------------------------------------------------------------------
* Exported functions (Generated clocks)
*----------------------------------------------------------------------------*/
#ifdef CONFIG_HAVE_PMC_GENERATED_CLOCKS
void pmc_configure_gck(uint32_t id, uint32_t clock_source, uint32_t div)
{
assert(id > 1 && id < ID_PERIPH_COUNT);
assert(!(clock_source & ~PMC_PCR_GCKCSS_Msk));
assert(!(div << PMC_PCR_GCKDIV_Pos & ~PMC_PCR_GCKDIV_Msk));
pmc_disable_gck(id);
PMC->PMC_PCR = PMC_PCR_PID(id);
volatile uint32_t pcr = PMC->PMC_PCR;
PMC->PMC_PCR = pcr | (clock_source & PMC_PCR_GCKCSS_Msk) | PMC_PCR_CMD
| PMC_PCR_GCKDIV(div);
}
void pmc_enable_gck(uint32_t id)
{
assert(id > 1 && id < ID_PERIPH_COUNT);
PMC->PMC_PCR = PMC_PCR_PID(id);
volatile uint32_t pcr = PMC->PMC_PCR;
PMC->PMC_PCR = pcr | PMC_PCR_CMD | PMC_PCR_GCKEN;
while (!(PMC->PMC_SR & PMC_SR_GCKRDY));
}
void pmc_disable_gck(uint32_t id)
{
assert(id > 1 && id < ID_PERIPH_COUNT);
PMC->PMC_PCR = PMC_PCR_PID(id);
volatile uint32_t pcr = PMC->PMC_PCR;
PMC->PMC_PCR = PMC_PCR_CMD | (pcr & ~PMC_PCR_GCKEN);
}
uint32_t pmc_get_gck_clock(uint32_t id)
{
uint32_t clk = 0;
assert(id > 1 && id < ID_PERIPH_COUNT);
PMC->PMC_PCR = PMC_PCR_PID(id);
volatile uint32_t pcr = PMC->PMC_PCR;
switch (pcr & PMC_PCR_GCKCSS_Msk) {
case PMC_PCR_GCKCSS_SLOW_CLK:
clk = pmc_get_slow_clock();
break;
case PMC_PCR_GCKCSS_MAIN_CLK:
clk = pmc_get_main_clock();
break;
case PMC_PCR_GCKCSS_PLLA_CLK:
clk = pmc_get_plla_clock();
break;
case PMC_PCR_GCKCSS_UPLL_CLK:
clk = pmc_get_upll_clock();
break;
case PMC_PCR_GCKCSS_MCK_CLK:
clk = pmc_get_master_clock();
break;
#ifdef CONFIG_HAVE_PMC_AUDIO_CLOCK
case PMC_PCR_GCKCSS_AUDIO_CLK:
clk = pmc_get_audio_pmc_clock();
break;
#endif
}
uint32_t div = (pcr & PMC_PCR_GCKDIV_Msk) >> PMC_PCR_GCKDIV_Pos;
return ROUND_INT_DIV(clk, div + 1);
}
#endif /* CONFIG_HAVE_PMC_GENERATED_CLOCKS */
/*----------------------------------------------------------------------------
* Exported functions (Audio PLL)
*----------------------------------------------------------------------------*/
#ifdef CONFIG_HAVE_PMC_AUDIO_CLOCK
void pmc_configure_audio(struct _pmc_audio_cfg *cfg)
{
/* reset audio clock */
PMC->PMC_AUDIO_PLL0 &= ~PMC_AUDIO_PLL0_RESETN;
PMC->PMC_AUDIO_PLL0 |= PMC_AUDIO_PLL0_RESETN;
/* configure values */
uint32_t pll0 = PMC->PMC_AUDIO_PLL0;
pll0 &= ~PMC_AUDIO_PLL0_ND_Msk;
pll0 |= cfg->nd << PMC_AUDIO_PLL0_ND_Pos;
pll0 &= ~PMC_AUDIO_PLL0_QDPMC_Msk;
pll0 |= cfg->qdpmc << PMC_AUDIO_PLL0_QDPMC_Pos;
PMC->PMC_AUDIO_PLL0 = pll0;
uint32_t pll1 = PMC->PMC_AUDIO_PLL1;
pll1 &= ~PMC_AUDIO_PLL1_DIV_Msk;
pll1 |= cfg->div << PMC_AUDIO_PLL1_DIV_Pos;
pll1 &= ~PMC_AUDIO_PLL1_FRACR_Msk;
pll1 |= cfg->fracr << PMC_AUDIO_PLL1_FRACR_Pos;
pll1 &= ~PMC_AUDIO_PLL1_QDAUDIO_Msk;
pll1 |= cfg->qdaudio << PMC_AUDIO_PLL1_QDAUDIO_Pos;
PMC->PMC_AUDIO_PLL1 = pll1;
}
void pmc_enable_audio(bool pmc_clock, bool pad_clock)
{
uint32_t bits = PMC_AUDIO_PLL0_PLLEN | PMC_AUDIO_PLL0_RESETN;
uint32_t nbits = 0;
if (pad_clock)
bits |= PMC_AUDIO_PLL0_PADEN;
else
nbits |= PMC_AUDIO_PLL0_PADEN;
if (pmc_clock)
bits |= PMC_AUDIO_PLL0_PMCEN;
else
nbits |= PMC_AUDIO_PLL0_PMCEN;
PMC->PMC_AUDIO_PLL0 = (PMC->PMC_AUDIO_PLL0 & ~nbits) | bits;
}
void pmc_disable_audio()
{
uint32_t nbits = PMC_AUDIO_PLL0_PLLEN | PMC_AUDIO_PLL0_RESETN |
PMC_AUDIO_PLL0_PADEN | PMC_AUDIO_PLL0_PMCEN;
PMC->PMC_AUDIO_PLL0 &= ~nbits;
}
uint32_t pmc_get_audio_pmc_clock(void)
{
uint32_t pll0 = PMC->PMC_AUDIO_PLL0;
uint32_t pll1 = PMC->PMC_AUDIO_PLL1;
uint32_t nd = (pll0 & PMC_AUDIO_PLL0_ND_Msk) >> PMC_AUDIO_PLL0_ND_Pos;
uint32_t fracr = (pll1 & PMC_AUDIO_PLL1_FRACR_Msk) >> PMC_AUDIO_PLL1_FRACR_Pos;
uint32_t qdpmc = (pll0 & PMC_AUDIO_PLL0_QDPMC_Msk) >> PMC_AUDIO_PLL0_QDPMC_Pos;
uint64_t clk = BOARD_MAIN_CLOCK_EXT_OSC;
clk *= ((nd + 1) << 22) + fracr;
clk /= 1 << 22;
clk /= qdpmc + 1;
return (uint32_t)clk;
}
uint32_t pmc_get_audio_pad_clock(void)
{
uint32_t pll0 = PMC->PMC_AUDIO_PLL0;
uint32_t pll1 = PMC->PMC_AUDIO_PLL1;
uint32_t nd = (pll0 & PMC_AUDIO_PLL0_ND_Msk) >> PMC_AUDIO_PLL0_ND_Pos;
uint32_t fracr = (pll1 & PMC_AUDIO_PLL1_FRACR_Msk) >> PMC_AUDIO_PLL1_FRACR_Pos;
uint32_t qdaudio = (pll1 & PMC_AUDIO_PLL1_QDAUDIO_Msk) >> PMC_AUDIO_PLL1_QDAUDIO_Pos;
if (qdaudio == 0)
return 0;
uint32_t div = (pll1 & PMC_AUDIO_PLL1_DIV_Msk) >> PMC_AUDIO_PLL1_DIV_Pos;
if (div != 2 && div != 3)
return 0;
uint64_t clk = BOARD_MAIN_CLOCK_EXT_OSC;
clk *= ((nd + 1) << 22) + fracr;
clk /= 1 << 22;
clk /= div * qdaudio;
return (uint32_t)clk;
}
#endif /* CONFIG_HAVE_PMC_AUDIO_CLOCK */