MCUXpresso_LPC845/devices/LPC845/drivers/fsl_clock.c

696 lines
19 KiB
C

/*
* Copyright 2017-2019 NXP
* All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "fsl_clock.h"
/*******************************************************************************
* Definitions
******************************************************************************/
/* Component ID definition, used by tools. */
#ifndef FSL_COMPONENT_ID
#define FSL_COMPONENT_ID "platform.drivers.clock"
#endif
#define SYSPLL_MIN_INPUT_FREQ_HZ (10000000U) /*!< Minimum PLL input rate */
#define SYSPLL_MAX_INPUT_FREQ_HZ (25000000U) /*!< Maximum PLL input rate */
#define SYSPLL_MAX_OUTPUT_FREQ_HZ (100000000U) /*!< Maximum PLL output rate */
#define SYSPLL_MIN_FCCO_FREQ_HZ (156000000U) /*!< Maximum FCCO output rate */
#define SYSPLL_MAX_FCCO_FREQ_HZ (320000000U) /*!< Maximum FCCO output rate */
#define SYSOSC_BOUNDARY_FREQ_HZ (15000000U) /*!< boundary frequency value */
/* External clock rate.
* Either external clk in rate or system oscillator frequency.
*/
volatile uint32_t g_Ext_Clk_Freq = 0U;
/** watch dog oscillator rate in Hz.*/
volatile uint32_t g_Wdt_Osc_Freq = 0U;
/*******************************************************************************
* Variables
******************************************************************************/
/*******************************************************************************
* Prototypes
******************************************************************************/
/*
* @brief select post divider for system pll according to the target frequency.
* @param outFreq: Value to be output
* @return post divider
*/
static uint32_t findSyestemPllPsel(uint32_t outFreq);
/*
* @brief Get FRG input clock frequency.
* @param fractional clock register base address.
* @return input clock frequency.
*/
static uint32_t CLOCK_GetFRGInputClkFreq(uint32_t *base);
/*
* @brief Update clock source.
* @param base clock register base address.
* @param mask clock source update enable bit mask value.
*/
static void CLOCK_UpdateClkSrc(volatile uint32_t *base, uint32_t mask);
/*******************************************************************************
* Code
******************************************************************************/
static uint32_t CLOCK_GetFRGInputClkFreq(uint32_t *base)
{
uint32_t sel = CLK_FRG_SEL_REG_MAP(base) & SYSCON_FRG_FRGCLKSEL_SEL_MASK;
if (sel == 0U)
{
return CLOCK_GetFroFreq();
}
else if (sel == 1U)
{
return CLOCK_GetMainClkFreq();
}
else
{
return CLOCK_GetSystemPLLFreq();
}
}
static bool CLOCK_SetFRGClkFreq(uint32_t *base, uint32_t freq)
{
assert(freq);
uint32_t input = CLOCK_GetFRGInputClkFreq(base);
uint32_t mul;
if ((freq > input) || (input / freq >= 2U))
{
return false;
}
mul = (uint32_t)(((uint64_t)((uint64_t)input - freq) << 8U) / ((uint64_t)freq));
CLK_FRG_DIV_REG_MAP(base) = SYSCON_FRG_FRGDIV_DIV_MASK;
CLK_FRG_MUL_REG_MAP(base) = SYSCON_FRG_FRGMULT_MULT(mul);
return true;
}
static void CLOCK_UpdateClkSrc(volatile uint32_t *base, uint32_t mask)
{
assert(base);
*base &= ~mask;
*base |= mask;
while ((*base & mask) == 0U)
{
}
}
/*! brief Set FRG0 output frequency.
* param freq, target output frequency,freq < input and (input / freq) < 2 should be satisfy.
* retval true - successfully, false - input argument is invalid.
*
*/
bool CLOCK_SetFRG0ClkFreq(uint32_t freq)
{
return CLOCK_SetFRGClkFreq(((uint32_t *)(uint32_t)(&SYSCON->FRG[0U])), freq);
}
/*! brief Set FRG1 output frequency.
* param freq, target output frequency,freq < input and (input / freq) < 2 should be satisfy.
* retval true - successfully, false - input argument is invalid.
*
*/
bool CLOCK_SetFRG1ClkFreq(uint32_t freq)
{
return CLOCK_SetFRGClkFreq(((uint32_t *)(uint32_t)(&SYSCON->FRG[1U])), freq);
}
/*! brief Return Frequency of FRG0 Clock.
* return Frequency of FRG0 Clock.
*/
uint32_t CLOCK_GetFRG0ClkFreq(void)
{
uint32_t temp;
temp = CLOCK_GetFRGInputClkFreq((uint32_t *)(uint32_t)(&SYSCON->FRG[0U])) << 8U;
return (uint32_t)((uint64_t)(temp) / (((uint64_t)SYSCON->FRG[0U].FRGMULT & SYSCON_FRG_FRGMULT_MULT_MASK) + 256ULL));
}
/*! brief Return Frequency of FRG1 Clock.
* return Frequency of FRG1 Clock.
*/
uint32_t CLOCK_GetFRG1ClkFreq(void)
{
uint32_t temp;
temp = (CLOCK_GetFRGInputClkFreq((uint32_t *)(uint32_t)(&SYSCON->FRG[1U])) << 8U);
return (uint32_t)(((uint64_t)temp) / (((uint64_t)SYSCON->FRG[1U].FRGMULT & SYSCON_FRG_FRGMULT_MULT_MASK) + 256ULL));
}
/*! brief Return Frequency of Main Clock.
* return Frequency of Main Clock.
*/
uint32_t CLOCK_GetMainClkFreq(void)
{
uint32_t freq = 0U;
if ((SYSCON->MAINCLKPLLSEL & SYSCON_MAINCLKPLLSEL_SEL_MASK) == 1U)
{
return CLOCK_GetSystemPLLFreq();
}
switch (SYSCON->MAINCLKSEL)
{
case 0U:
freq = CLOCK_GetFroFreq();
break;
case 1U:
freq = CLOCK_GetExtClkFreq();
break;
case 2U:
freq = CLOCK_GetWdtOscFreq();
break;
case 3U:
freq = CLOCK_GetFroFreq() >> 1U;
break;
default:
freq = 0U;
break;
}
return freq;
}
/*! brief Return Frequency of FRO.
* return Frequency of FRO.
*/
uint32_t CLOCK_GetFroFreq(void)
{
uint32_t froOscSel = SYSCON->FROOSCCTRL & 3U;
uint32_t froOscFreq = 0U;
if (froOscSel == 0U)
{
froOscFreq = 18000000U;
}
else if (froOscSel == 1U)
{
froOscFreq = 24000000U;
}
else
{
froOscFreq = 30000000U;
}
if (((SYSCON->FROOSCCTRL & SYSCON_FROOSCCTRL_FRO_DIRECT_MASK) >> SYSCON_FROOSCCTRL_FRO_DIRECT_SHIFT) == 0U)
{
/* need to check the FAIM low power boot value */
froOscFreq /= ((*((volatile uint32_t *)(CLOCK_FAIM_BASE)) & 0x2U) != 0UL) ? 16U : 2U;
}
return froOscFreq;
}
/*! brief Return Frequency of ClockOut
* return Frequency of ClockOut
*/
uint32_t CLOCK_GetClockOutClkFreq(void)
{
uint32_t divider = SYSCON->CLKOUTDIV & 0xffU, freq = 0U;
switch (SYSCON->CLKOUTSEL)
{
case 0U:
freq = CLOCK_GetFroFreq();
break;
case 1U:
freq = CLOCK_GetMainClkFreq();
break;
case 2U:
freq = CLOCK_GetSystemPLLFreq();
break;
case 3U:
freq = CLOCK_GetExtClkFreq();
break;
case 4U:
freq = CLOCK_GetWdtOscFreq();
break;
default:
freq = 0U;
break;
}
return divider == 0U ? 0U : (freq / divider);
}
/*! brief Return Frequency of UART0
* return Frequency of UART0
*/
uint32_t CLOCK_GetUart0ClkFreq(void)
{
uint32_t freq = 0U;
switch (SYSCON->FCLKSEL[0])
{
case 0U:
freq = CLOCK_GetFroFreq();
break;
case 1U:
freq = CLOCK_GetMainClkFreq();
break;
case 2U:
freq = CLOCK_GetFRG0ClkFreq();
break;
case 3U:
freq = CLOCK_GetFRG1ClkFreq();
break;
case 4U:
freq = CLOCK_GetFroFreq() >> 1U;
break;
default:
freq = 0U;
break;
}
return freq;
}
/*! brief Return Frequency of UART1
* return Frequency of UART1
*/
uint32_t CLOCK_GetUart1ClkFreq(void)
{
uint32_t freq = 0U;
switch (SYSCON->FCLKSEL[1])
{
case 0U:
freq = CLOCK_GetFroFreq();
break;
case 1U:
freq = CLOCK_GetMainClkFreq();
break;
case 2U:
freq = CLOCK_GetFRG0ClkFreq();
break;
case 3U:
freq = CLOCK_GetFRG1ClkFreq();
break;
case 4U:
freq = CLOCK_GetFroFreq() >> 1U;
break;
default:
freq = 0U;
break;
}
return freq;
}
/*! brief Return Frequency of UART2
* return Frequency of UART2
*/
uint32_t CLOCK_GetUart2ClkFreq(void)
{
uint32_t freq = 0U;
switch (SYSCON->FCLKSEL[2])
{
case 0U:
freq = CLOCK_GetFroFreq();
break;
case 1U:
freq = CLOCK_GetMainClkFreq();
break;
case 2U:
freq = CLOCK_GetFRG0ClkFreq();
break;
case 3U:
freq = CLOCK_GetFRG1ClkFreq();
break;
case 4U:
freq = CLOCK_GetFroFreq() >> 1U;
break;
default:
freq = 0U;
break;
}
return freq;
}
/*! brief Return Frequency of UART3
* return Frequency of UART3
*/
uint32_t CLOCK_GetUart3ClkFreq(void)
{
uint32_t freq = 0U;
switch (SYSCON->FCLKSEL[3])
{
case 0U:
freq = CLOCK_GetFroFreq();
break;
case 1U:
freq = CLOCK_GetMainClkFreq();
break;
case 2U:
freq = CLOCK_GetFRG0ClkFreq();
break;
case 3U:
freq = CLOCK_GetFRG1ClkFreq();
break;
case 4U:
freq = CLOCK_GetFroFreq() >> 1U;
break;
default:
freq = 0U;
break;
}
return freq;
}
/*! brief Return Frequency of UART4
* return Frequency of UART4
*/
uint32_t CLOCK_GetUart4ClkFreq(void)
{
uint32_t freq = 0U;
switch (SYSCON->FCLKSEL[4])
{
case 0U:
freq = CLOCK_GetFroFreq();
break;
case 1U:
freq = CLOCK_GetMainClkFreq();
break;
case 2U:
freq = CLOCK_GetFRG0ClkFreq();
break;
case 3U:
freq = CLOCK_GetFRG1ClkFreq();
break;
case 4U:
freq = CLOCK_GetFroFreq() >> 1U;
break;
default:
freq = 0U;
break;
}
return freq;
}
/*! brief Return Frequency of selected clock
* return Frequency of selected clock
*/
uint32_t CLOCK_GetFreq(clock_name_t clockName)
{
uint32_t freq;
switch (clockName)
{
case kCLOCK_CoreSysClk:
freq = CLOCK_GetCoreSysClkFreq();
break;
case kCLOCK_MainClk:
freq = CLOCK_GetMainClkFreq();
break;
case kCLOCK_Fro:
freq = CLOCK_GetFroFreq();
break;
case kCLOCK_FroDiv:
freq = CLOCK_GetFroFreq() >> 1U;
break;
case kCLOCK_ExtClk:
freq = CLOCK_GetExtClkFreq();
break;
case kCLOCK_WdtOsc:
freq = CLOCK_GetWdtOscFreq();
break;
case kCLOCK_PllOut:
freq = CLOCK_GetSystemPLLFreq();
break;
case kCLOCK_Frg0:
freq = CLOCK_GetFRG0ClkFreq();
break;
case kCLOCK_Frg1:
freq = CLOCK_GetFRG1ClkFreq();
break;
default:
freq = 0U;
break;
}
return freq;
}
/*! brief Return System PLL input clock rate
* return System PLL input clock rate
*/
uint32_t CLOCK_GetSystemPLLInClockRate(void)
{
uint32_t freq = 0U;
switch ((SYSCON->SYSPLLCLKSEL))
{
/* source from fro div clock */
case 0x03U:
freq = CLOCK_GetFroFreq() >> 1U;
break;
/* source from the fro clock */
case 0x00U:
freq = CLOCK_GetFroFreq();
break;
/* source from external clock in */
case 0x01U:
freq = CLOCK_GetExtClkFreq();
break;
/* source from watchdog oscillator */
case 0x02U:
freq = CLOCK_GetWdtOscFreq();
break;
default:
freq = 0U;
break;
}
return freq;
}
static uint32_t findSyestemPllPsel(uint32_t outFreq)
{
uint32_t pSel = 0U;
if (outFreq > (SYSPLL_MIN_FCCO_FREQ_HZ >> 1U))
{
pSel = 0U;
}
else if (outFreq > (SYSPLL_MIN_FCCO_FREQ_HZ >> 2U))
{
pSel = 1U;
}
else if (outFreq > (SYSPLL_MIN_FCCO_FREQ_HZ >> 3U))
{
pSel = 2U;
}
else
{
pSel = 3U;
}
return pSel;
}
/*! brief System PLL initialize.
* param config System PLL configurations.
*/
void CLOCK_InitSystemPll(const clock_sys_pll_t *config)
{
assert(config->targetFreq <= SYSPLL_MAX_OUTPUT_FREQ_HZ);
uint32_t mSel = 0U, pSel = 0U, inputFreq = 0U;
uint32_t syspllclkseltmp;
/* Power off PLL during setup changes */
SYSCON->PDRUNCFG |= SYSCON_PDRUNCFG_SYSPLL_PD_MASK;
/*set system pll clock source select register */
syspllclkseltmp = (SYSCON->SYSPLLCLKSEL & (~SYSCON_SYSPLLCLKSEL_SEL_MASK)) | (uint32_t)(config->src);
SYSCON->SYSPLLCLKSEL |= syspllclkseltmp;
/* system pll clock source update */
CLOCK_UpdateClkSrc((volatile uint32_t *)(&(SYSCON->SYSPLLCLKUEN)), SYSCON_SYSPLLCLKSEL_SEL_MASK);
inputFreq = CLOCK_GetSystemPLLInClockRate();
assert(inputFreq != 0U);
/* calucate the feedback divider value and post divider value*/
mSel = config->targetFreq / inputFreq;
pSel = findSyestemPllPsel(config->targetFreq);
/* configure PSEL and MSEL */
SYSCON->SYSPLLCTRL = (SYSCON->SYSPLLCTRL & (~(SYSCON_SYSPLLCTRL_MSEL_MASK | SYSCON_SYSPLLCTRL_PSEL_MASK))) |
SYSCON_SYSPLLCTRL_MSEL(mSel == 0U ? 0U : (mSel - 1U)) | SYSCON_SYSPLLCTRL_PSEL(pSel);
/* Power up PLL after setup changes */
SYSCON->PDRUNCFG &= ~SYSCON_PDRUNCFG_SYSPLL_PD_MASK;
/* wait pll lock */
while ((SYSCON->SYSPLLSTAT & SYSCON_SYSPLLSTAT_LOCK_MASK) == 0U)
{
}
}
/*! brief Init external CLK IN, select the CLKIN as the external clock source.
* param clkInFreq external clock in frequency.
*/
void CLOCK_InitExtClkin(uint32_t clkInFreq)
{
/* remove the pull up and pull down resistors in the IOCON */
IOCON->PIO[IOCON_INDEX_PIO0_1] &= ~IOCON_PIO_MODE_MASK;
/* enable the 1 bit functions for CLKIN */
SWM0->PINENABLE0 &= ~SWM_PINENABLE0_CLKIN_MASK;
/* enable the external clk in */
SYSCON->EXTCLKSEL |= SYSCON_EXTCLKSEL_SEL_MASK;
/* record the external clock rate */
g_Ext_Clk_Freq = clkInFreq;
}
/*! brief XTALIN init function
* system oscillator is bypassed, sys_osc_clk is fed driectly from the XTALIN.
* param xtalInFreq XTALIN frequency value
* return Frequency of PLL
*/
void CLOCK_InitXtalin(uint32_t xtalInFreq)
{
/* remove the pull up and pull down resistors in the IOCON */
IOCON->PIO[IOCON_INDEX_PIO0_8] &= ~IOCON_PIO_MODE_MASK;
/* enable the 1 bit functions for XTALIN and XTALOUT */
SWM0->PINENABLE0 &= ~SWM_PINENABLE0_XTALIN_MASK;
/* system osc configure */
SYSCON->SYSOSCCTRL |= SYSCON_SYSOSCCTRL_BYPASS_MASK;
/* external clock select */
SYSCON->EXTCLKSEL &= ~SYSCON_EXTCLKSEL_SEL_MASK;
/* enable system osc power first */
SYSCON->PDRUNCFG &= ~SYSCON_PDRUNCFG_SYSOSC_PD_MASK;
/* software delay 500USs */
SDK_DelayAtLeastUs(500U, SDK_DEVICE_MAXIMUM_CPU_CLOCK_FREQUENCY);
/* record the external clock rate */
g_Ext_Clk_Freq = xtalInFreq;
}
/*! brief Init SYS OSC
* param oscFreq oscillator frequency value.
*/
void CLOCK_InitSysOsc(uint32_t oscFreq)
{
uint32_t sysoscctrltmp;
/* remove the pull up and pull down resistors in the IOCON */
IOCON->PIO[IOCON_INDEX_PIO0_9] &= ~IOCON_PIO_MODE_MASK;
IOCON->PIO[IOCON_INDEX_PIO0_8] &= ~IOCON_PIO_MODE_MASK;
/* enable the 1 bit functions for XTALIN and XTALOUT */
SWM0->PINENABLE0 &= ~(SWM_PINENABLE0_XTALIN_MASK | SWM_PINENABLE0_XTALOUT_MASK);
/* system osc configure */
sysoscctrltmp = (SYSCON->SYSOSCCTRL & (~(SYSCON_SYSOSCCTRL_BYPASS_MASK | SYSCON_SYSOSCCTRL_FREQRANGE_MASK))) |
(oscFreq > SYSOSC_BOUNDARY_FREQ_HZ ? SYSCON_SYSOSCCTRL_FREQRANGE_MASK : 0U);
SYSCON->SYSOSCCTRL |= sysoscctrltmp;
/* external clock select */
SYSCON->EXTCLKSEL &= ~SYSCON_EXTCLKSEL_SEL_MASK;
/* enable system osc power first */
SYSCON->PDRUNCFG &= ~SYSCON_PDRUNCFG_SYSOSC_PD_MASK;
/* software delay 500USs */
SDK_DelayAtLeastUs(500U, SDK_DEVICE_MAXIMUM_CPU_CLOCK_FREQUENCY);
/* record the external clock rate */
g_Ext_Clk_Freq = oscFreq;
}
/*! brief Init watch dog OSC
* Any setting of the FREQSEL bits will yield a Fclkana value within 40% of the
* listed frequency value. The watchdog oscillator is the clock source with the lowest power
* consumption. If accurate timing is required, use the FRO or system oscillator.
* The frequency of the watchdog oscillator is undefined after reset. The watchdog
* oscillator frequency must be programmed by writing to the WDTOSCCTRL register before
* using the watchdog oscillator.
* Watchdog osc output frequency = wdtOscFreq / wdtOscDiv, should in range 9.3KHZ to 2.3MHZ.
* param wdtOscFreq watch dog analog part output frequency, reference _wdt_analog_output_freq.
* param wdtOscDiv watch dog analog part output frequency divider, shoule be a value >= 2U and multiple of 2
*/
void CLOCK_InitWdtOsc(clock_wdt_analog_freq_t wdtOscFreq, uint32_t wdtOscDiv)
{
assert(wdtOscDiv >= 2U);
uint32_t wdtOscCtrl = SYSCON->WDTOSCCTRL;
wdtOscCtrl &= ~(SYSCON_WDTOSCCTRL_DIVSEL_MASK | SYSCON_WDTOSCCTRL_FREQSEL_MASK);
wdtOscCtrl |= SYSCON_WDTOSCCTRL_DIVSEL((wdtOscDiv >> 1U) - 1U) |
SYSCON_WDTOSCCTRL_FREQSEL(CLK_WDT_OSC_GET_REG((uint32_t)wdtOscFreq));
SYSCON->WDTOSCCTRL = wdtOscCtrl;
/* power up watchdog oscillator */
SYSCON->PDRUNCFG &= ~SYSCON_PDRUNCFG_WDTOSC_PD_MASK;
/* update watch dog oscillator value */
g_Wdt_Osc_Freq = CLK_WDT_OSC_GET_FREQ(wdtOscFreq) / wdtOscDiv;
}
/*! brief Set main clock reference source.
* param src, reference clock_main_clk_src_t to set the main clock source.
*/
void CLOCK_SetMainClkSrc(clock_main_clk_src_t src)
{
uint32_t mainMux = CLK_MAIN_CLK_MUX_GET_MUX(src), mainPreMux = CLK_MAIN_CLK_MUX_GET_PRE_MUX(src);
if (((SYSCON->MAINCLKSEL & SYSCON_MAINCLKSEL_SEL_MASK) != mainPreMux) && (mainMux == 0U))
{
SYSCON->MAINCLKSEL = (SYSCON->MAINCLKSEL & (~SYSCON_MAINCLKSEL_SEL_MASK)) | SYSCON_MAINCLKSEL_SEL(mainPreMux);
CLOCK_UpdateClkSrc((volatile uint32_t *)(&(SYSCON->MAINCLKUEN)), SYSCON_MAINCLKUEN_ENA_MASK);
}
if ((SYSCON->MAINCLKPLLSEL & SYSCON_MAINCLKPLLSEL_SEL_MASK) != mainMux)
{
SYSCON->MAINCLKPLLSEL =
(SYSCON->MAINCLKPLLSEL & (~SYSCON_MAINCLKPLLSEL_SEL_MASK)) | SYSCON_MAINCLKPLLSEL_SEL(mainMux);
CLOCK_UpdateClkSrc((volatile uint32_t *)(&(SYSCON->MAINCLKPLLUEN)), SYSCON_MAINCLKPLLUEN_ENA_MASK);
}
}
/*! brief Set FRO clock source
* param src, please reference _clock_fro_src definition.
*
*/
void CLOCK_SetFroOutClkSrc(clock_fro_src_t src)
{
if ((uint32_t)src != (SYSCON->FROOSCCTRL & SYSCON_FROOSCCTRL_FRO_DIRECT_MASK))
{
SYSCON->FROOSCCTRL = (SYSCON->FROOSCCTRL & (~SYSCON_FROOSCCTRL_FRO_DIRECT_MASK)) | (uint32_t)src;
/* Update clock source */
CLOCK_UpdateClkSrc((volatile uint32_t *)(&(SYSCON->FRODIRECTCLKUEN)), SYSCON_FRODIRECTCLKUEN_ENA_MASK);
}
}