1778 lines
52 KiB
C
1778 lines
52 KiB
C
|
|
/*
|
||
|
|
* Copyright (c) 2016, Freescale Semiconductor, Inc.
|
||
|
|
* Copyright 2016 - 2019 , NXP
|
||
|
|
* All rights reserved.
|
||
|
|
*
|
||
|
|
*
|
||
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
||
|
|
*/
|
||
|
|
|
||
|
|
#include "fsl_clock.h"
|
||
|
|
#include "fsl_power.h"
|
||
|
|
/*******************************************************************************
|
||
|
|
* Definitions
|
||
|
|
******************************************************************************/
|
||
|
|
/* Component ID definition, used by tools. */
|
||
|
|
#ifndef FSL_COMPONENT_ID
|
||
|
|
#define FSL_COMPONENT_ID "platform.drivers.clock"
|
||
|
|
#endif
|
||
|
|
#define NVALMAX (0x100U)
|
||
|
|
#define PVALMAX (0x20U)
|
||
|
|
#define MVALMAX (0x8000U)
|
||
|
|
|
||
|
|
#define PLL_MAX_N_DIV 0x100U
|
||
|
|
|
||
|
|
#define INDEX_SECTOR_TRIM48 ((uint32_t *)0x01000444U)
|
||
|
|
#define INDEX_SECTOR_TRIM96 ((uint32_t *)0x01000430U)
|
||
|
|
/*--------------------------------------------------------------------------
|
||
|
|
!!! If required these #defines can be moved to chip library file
|
||
|
|
----------------------------------------------------------------------------*/
|
||
|
|
|
||
|
|
#define PLL_SSCG0_MDEC_VAL_P (0U) /* MDEC is in bits 16 downto 0 */
|
||
|
|
#define PLL_SSCG0_MDEC_VAL_M (0x1FFFFUL << PLL_SSCG0_MDEC_VAL_P) /* NDEC is in bits 9 downto 0 */
|
||
|
|
#define PLL_NDEC_VAL_P (0U) /* NDEC is in bits 9:0 */
|
||
|
|
#define PLL_NDEC_VAL_M (0x3FFUL << PLL_NDEC_VAL_P)
|
||
|
|
#define PLL_PDEC_VAL_P (0U) /* PDEC is in bits 6:0 */
|
||
|
|
#define PLL_PDEC_VAL_M (0x7FUL << PLL_PDEC_VAL_P)
|
||
|
|
|
||
|
|
#define PLL_MIN_CCO_FREQ_MHZ (75000000U)
|
||
|
|
#define PLL_MAX_CCO_FREQ_MHZ (150000000U)
|
||
|
|
#define PLL_LOWER_IN_LIMIT (4000U) /*!< Minimum PLL input rate */
|
||
|
|
#define PLL_MIN_IN_SSMODE (2000000U)
|
||
|
|
#define PLL_MAX_IN_SSMODE (4000000U)
|
||
|
|
|
||
|
|
/* Middle of the range values for spread-spectrum */
|
||
|
|
#define PLL_SSCG_MF_FREQ_VALUE 4U
|
||
|
|
#define PLL_SSCG_MC_COMP_VALUE 2U
|
||
|
|
#define PLL_SSCG_MR_DEPTH_VALUE 4U
|
||
|
|
#define PLL_SSCG_DITHER_VALUE 0U
|
||
|
|
|
||
|
|
/* PLL NDEC reg */
|
||
|
|
#define PLL_NDEC_VAL_SET(value) (((unsigned long)(value) << PLL_NDEC_VAL_P) & PLL_NDEC_VAL_M)
|
||
|
|
/* PLL PDEC reg */
|
||
|
|
#define PLL_PDEC_VAL_SET(value) (((unsigned long)(value) << PLL_PDEC_VAL_P) & PLL_PDEC_VAL_M)
|
||
|
|
/* SSCG control0 */
|
||
|
|
#define PLL_SSCG0_MDEC_VAL_SET(value) (((unsigned long)(value) << PLL_SSCG0_MDEC_VAL_P) & PLL_SSCG0_MDEC_VAL_M)
|
||
|
|
|
||
|
|
/* SSCG control1 */
|
||
|
|
#define PLL_SSCG1_MD_FRACT_P 0U
|
||
|
|
#define PLL_SSCG1_MD_INT_P 11U
|
||
|
|
#define PLL_SSCG1_MD_FRACT_M (0x7FFUL << PLL_SSCG1_MD_FRACT_P)
|
||
|
|
#define PLL_SSCG1_MD_INT_M (0xFFUL << PLL_SSCG1_MD_INT_P)
|
||
|
|
|
||
|
|
#define PLL_SSCG1_MD_FRACT_SET(value) (((unsigned long)(value) << PLL_SSCG1_MD_FRACT_P) & PLL_SSCG1_MD_FRACT_M)
|
||
|
|
#define PLL_SSCG1_MD_INT_SET(value) (((unsigned long)(value) << PLL_SSCG1_MD_INT_P) & PLL_SSCG1_MD_INT_M)
|
||
|
|
|
||
|
|
/* Saved value of PLL output rate, computed whenever needed to save run-time
|
||
|
|
computation on each call to retrive the PLL rate. */
|
||
|
|
static uint32_t s_Pll_Freq;
|
||
|
|
|
||
|
|
/* I2S mclk. */
|
||
|
|
static uint32_t s_I2S_Mclk_Freq = 0U;
|
||
|
|
|
||
|
|
/** External clock rate on the CLKIN pin in Hz. If not used,
|
||
|
|
set this to 0. Otherwise, set it to the exact rate in Hz this pin is
|
||
|
|
being driven at. */
|
||
|
|
static const uint32_t s_Ext_Clk_Freq = 0U;
|
||
|
|
|
||
|
|
/*******************************************************************************
|
||
|
|
* Variables
|
||
|
|
******************************************************************************/
|
||
|
|
|
||
|
|
/*******************************************************************************
|
||
|
|
* Prototypes
|
||
|
|
******************************************************************************/
|
||
|
|
/* Find encoded NDEC value for raw N value, max N = NVALMAX */
|
||
|
|
static uint32_t pllEncodeN(uint32_t N);
|
||
|
|
/* Find decoded N value for raw NDEC value */
|
||
|
|
static uint32_t pllDecodeN(uint32_t NDEC);
|
||
|
|
/* Find encoded PDEC value for raw P value, max P = PVALMAX */
|
||
|
|
static uint32_t pllEncodeP(uint32_t P);
|
||
|
|
/* Find decoded P value for raw PDEC value */
|
||
|
|
static uint32_t pllDecodeP(uint32_t PDEC);
|
||
|
|
/* Find encoded MDEC value for raw M value, max M = MVALMAX */
|
||
|
|
static uint32_t pllEncodeM(uint32_t M);
|
||
|
|
/* Find decoded M value for raw MDEC value */
|
||
|
|
static uint32_t pllDecodeM(uint32_t MDEC);
|
||
|
|
/* Find SELP, SELI, and SELR values for raw M value, max M = MVALMAX */
|
||
|
|
static void pllFindSel(uint32_t M, bool bypassFBDIV2, uint32_t *pSelP, uint32_t *pSelI, uint32_t *pSelR);
|
||
|
|
/* Get predivider (N) from PLL NDEC setting */
|
||
|
|
static uint32_t findPllPreDiv(uint32_t ctrlReg, uint32_t nDecReg);
|
||
|
|
/* Get postdivider (P) from PLL PDEC setting */
|
||
|
|
static uint32_t findPllPostDiv(uint32_t ctrlReg, uint32_t pDecReg);
|
||
|
|
/* Get multiplier (M) from PLL MDEC and BYPASS_FBDIV2 settings */
|
||
|
|
static uint32_t findPllMMult(uint32_t ctrlReg, uint32_t mDecReg);
|
||
|
|
/* Get the greatest common divisor */
|
||
|
|
static uint32_t FindGreatestCommonDivisor(uint32_t m, uint32_t n);
|
||
|
|
/* Set PLL output based on desired output rate */
|
||
|
|
static pll_error_t CLOCK_GetPllConfig(
|
||
|
|
uint32_t finHz, uint32_t foutHz, pll_setup_t *pSetup, bool useFeedbackDiv2, bool useSS);
|
||
|
|
/* Update local PLL rate variable */
|
||
|
|
static void CLOCK_GetSystemPLLOutFromSetupUpdate(pll_setup_t *pSetup);
|
||
|
|
|
||
|
|
static const uint8_t wdtFreqLookup[32] = {0, 8, 12, 15, 18, 20, 24, 26, 28, 30, 32, 34, 36, 38, 40, 41,
|
||
|
|
42, 44, 45, 46, 48, 49, 50, 52, 53, 54, 56, 57, 58, 59, 60, 61};
|
||
|
|
/*******************************************************************************
|
||
|
|
* Code
|
||
|
|
******************************************************************************/
|
||
|
|
|
||
|
|
/**
|
||
|
|
* brief Configure the clock selection muxes.
|
||
|
|
* param connection : Clock to be configured.
|
||
|
|
* return Nothing
|
||
|
|
*/
|
||
|
|
void CLOCK_AttachClk(clock_attach_id_t connection)
|
||
|
|
{
|
||
|
|
uint8_t mux;
|
||
|
|
uint8_t sel;
|
||
|
|
uint16_t item;
|
||
|
|
uint32_t tmp32 = (uint32_t)connection;
|
||
|
|
uint32_t i;
|
||
|
|
volatile uint32_t *pClkSel;
|
||
|
|
|
||
|
|
pClkSel = &(SYSCON->MAINCLKSELA);
|
||
|
|
|
||
|
|
if (kNONE_to_NONE != connection)
|
||
|
|
{
|
||
|
|
for (i = 0U; i < 2U; i++)
|
||
|
|
{
|
||
|
|
if (tmp32 == 0U)
|
||
|
|
{
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
item = (uint16_t)GET_ID_ITEM(tmp32);
|
||
|
|
if (item != 0U)
|
||
|
|
{
|
||
|
|
mux = GET_ID_ITEM_MUX(item);
|
||
|
|
sel = GET_ID_ITEM_SEL(item);
|
||
|
|
if (mux == CM_ASYNCAPB)
|
||
|
|
{
|
||
|
|
ASYNC_SYSCON->ASYNCAPBCLKSELA = sel;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
((volatile uint32_t *)pClkSel)[mux] = sel;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
tmp32 = GET_ID_NEXT_ITEM(tmp32); /* pick up next descriptor */
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Return the actual clock attach id */
|
||
|
|
/**
|
||
|
|
* brief Get the actual clock attach id.
|
||
|
|
* This fuction uses the offset in input attach id, then it reads the actual source value in
|
||
|
|
* the register and combine the offset to obtain an actual attach id.
|
||
|
|
* param attachId : Clock attach id to get.
|
||
|
|
* return Clock source value.
|
||
|
|
*/
|
||
|
|
clock_attach_id_t CLOCK_GetClockAttachId(clock_attach_id_t attachId)
|
||
|
|
{
|
||
|
|
uint8_t mux;
|
||
|
|
uint8_t actualSel;
|
||
|
|
uint32_t tmp32 = (uint32_t)attachId;
|
||
|
|
uint32_t i;
|
||
|
|
uint32_t actualAttachId = 0U;
|
||
|
|
uint32_t selector = GET_ID_SELECTOR(tmp32);
|
||
|
|
volatile uint32_t *pClkSel;
|
||
|
|
|
||
|
|
pClkSel = &(SYSCON->MAINCLKSELA);
|
||
|
|
|
||
|
|
if (kNONE_to_NONE == attachId)
|
||
|
|
{
|
||
|
|
return kNONE_to_NONE;
|
||
|
|
}
|
||
|
|
|
||
|
|
for (i = 0U; i < 2U; i++)
|
||
|
|
{
|
||
|
|
mux = GET_ID_ITEM_MUX(tmp32);
|
||
|
|
if (tmp32 != 0UL)
|
||
|
|
{
|
||
|
|
if (mux == CM_ASYNCAPB)
|
||
|
|
{
|
||
|
|
actualSel = (uint8_t)(ASYNC_SYSCON->ASYNCAPBCLKSELA);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
actualSel = (uint8_t)(((volatile uint32_t *)pClkSel)[mux]);
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Consider the combination of two registers */
|
||
|
|
actualAttachId |= CLK_ATTACH_ID(mux, actualSel, i);
|
||
|
|
}
|
||
|
|
tmp32 = GET_ID_NEXT_ITEM(tmp32); /*!< pick up next descriptor */
|
||
|
|
}
|
||
|
|
|
||
|
|
actualAttachId |= selector;
|
||
|
|
|
||
|
|
return (clock_attach_id_t)actualAttachId;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* brief Setup peripheral clock dividers.
|
||
|
|
* param div_name : Clock divider name
|
||
|
|
* param divided_by_value: Value to be divided
|
||
|
|
* param reset : Whether to reset the divider counter.
|
||
|
|
* return Nothing
|
||
|
|
*/
|
||
|
|
void CLOCK_SetClkDiv(clock_div_name_t div_name, uint32_t divided_by_value, bool reset)
|
||
|
|
{
|
||
|
|
volatile uint32_t *pClkDiv;
|
||
|
|
|
||
|
|
pClkDiv = &(SYSCON->SYSTICKCLKDIV);
|
||
|
|
if (reset)
|
||
|
|
{
|
||
|
|
((volatile uint32_t *)pClkDiv)[(uint8_t)div_name] = 1UL << 29U;
|
||
|
|
}
|
||
|
|
if (divided_by_value == 0UL) /* halt */
|
||
|
|
{
|
||
|
|
((volatile uint32_t *)pClkDiv)[(uint8_t)div_name] = 1UL << 30U;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
((volatile uint32_t *)pClkDiv)[(uint8_t)div_name] = (divided_by_value - 1UL);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Set FRO Clocking */
|
||
|
|
/**
|
||
|
|
* brief Initialize the Core clock to given frequency (12, 48 or 96 MHz).
|
||
|
|
* Turns on FRO and uses default CCO, if freq is 12000000, then high speed output is off, else high speed output is
|
||
|
|
* enabled.
|
||
|
|
* param iFreq : Desired frequency (must be one of CLK_FRO_12MHZ or CLK_FRO_48MHZ or CLK_FRO_96MHZ)
|
||
|
|
* return returns success or fail status.
|
||
|
|
*/
|
||
|
|
status_t CLOCK_SetupFROClocking(uint32_t iFreq)
|
||
|
|
{
|
||
|
|
uint32_t usb_adj;
|
||
|
|
if ((iFreq != 12000000U) && (iFreq != 48000000U) && (iFreq != 96000000U))
|
||
|
|
{
|
||
|
|
return kStatus_Fail;
|
||
|
|
}
|
||
|
|
/* Power up the FRO and set this as the base clock */
|
||
|
|
POWER_DisablePD(kPDRUNCFG_PD_FRO_EN);
|
||
|
|
/* back up the value of whether USB adj is selected, in which case we will have a value of 1 else 0 */
|
||
|
|
usb_adj = ((SYSCON->FROCTRL) & SYSCON_FROCTRL_USBCLKADJ_MASK) >> SYSCON_FROCTRL_USBCLKADJ_SHIFT;
|
||
|
|
if (iFreq > 12000000U)
|
||
|
|
{
|
||
|
|
if (iFreq == 96000000U)
|
||
|
|
{
|
||
|
|
SYSCON->FROCTRL = ((SYSCON_FROCTRL_TRIM_MASK | SYSCON_FROCTRL_FREQTRIM_MASK) & *INDEX_SECTOR_TRIM96) |
|
||
|
|
SYSCON_FROCTRL_SEL(1) | SYSCON_FROCTRL_WRTRIM(1) | SYSCON_FROCTRL_USBCLKADJ(usb_adj) |
|
||
|
|
SYSCON_FROCTRL_HSPDCLK(1);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
SYSCON->FROCTRL = ((SYSCON_FROCTRL_TRIM_MASK | SYSCON_FROCTRL_FREQTRIM_MASK) & *INDEX_SECTOR_TRIM48) |
|
||
|
|
SYSCON_FROCTRL_SEL(0) | SYSCON_FROCTRL_WRTRIM(1) | SYSCON_FROCTRL_USBCLKADJ(usb_adj) |
|
||
|
|
SYSCON_FROCTRL_HSPDCLK(1);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
SYSCON->FROCTRL &= ~SYSCON_FROCTRL_HSPDCLK(1);
|
||
|
|
}
|
||
|
|
|
||
|
|
return kStatus_Success;
|
||
|
|
}
|
||
|
|
|
||
|
|
/*! brief Return Frequency of FRO 12MHz
|
||
|
|
* return Frequency of FRO 12MHz
|
||
|
|
*/
|
||
|
|
uint32_t CLOCK_GetFro12MFreq(void)
|
||
|
|
{
|
||
|
|
return ((SYSCON->PDRUNCFG[0] & SYSCON_PDRUNCFG_PDEN_FRO_MASK) != 0UL) ? 0U : 12000000U;
|
||
|
|
}
|
||
|
|
|
||
|
|
/*! brief Return Frequency of External Clock
|
||
|
|
* return Frequency of External Clock. If no external clock is used returns 0.
|
||
|
|
*/
|
||
|
|
uint32_t CLOCK_GetExtClkFreq(void)
|
||
|
|
{
|
||
|
|
return (s_Ext_Clk_Freq);
|
||
|
|
}
|
||
|
|
/*! brief Return Frequency of Watchdog Oscillator
|
||
|
|
* return Frequency of Watchdog Oscillator
|
||
|
|
*/
|
||
|
|
uint32_t CLOCK_GetWdtOscFreq(void)
|
||
|
|
{
|
||
|
|
uint8_t freq_sel, div_sel;
|
||
|
|
if ((SYSCON->PDRUNCFG[(uint32_t)kPDRUNCFG_PD_WDT_OSC >> 8UL] & (1UL << ((uint32_t)kPDRUNCFG_PD_WDT_OSC & 0xffU))) !=
|
||
|
|
0UL)
|
||
|
|
{
|
||
|
|
return 0UL;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
div_sel = (uint8_t)(((SYSCON->WDTOSCCTRL & 0x1fU) + 1UL) << 1U);
|
||
|
|
freq_sel =
|
||
|
|
wdtFreqLookup[((SYSCON->WDTOSCCTRL & SYSCON_WDTOSCCTRL_FREQSEL_MASK) >> SYSCON_WDTOSCCTRL_FREQSEL_SHIFT)];
|
||
|
|
return ((uint32_t)freq_sel * 50000U) / ((uint32_t)div_sel);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Get HF FRO Clk */
|
||
|
|
/*! brief Return Frequency of High-Freq output of FRO
|
||
|
|
* return Frequency of High-Freq output of FRO
|
||
|
|
*/
|
||
|
|
uint32_t CLOCK_GetFroHfFreq(void)
|
||
|
|
{
|
||
|
|
if (((SYSCON->PDRUNCFG[0] & SYSCON_PDRUNCFG_PDEN_FRO_MASK) != 0UL) ||
|
||
|
|
((SYSCON->FROCTRL & SYSCON_FROCTRL_HSPDCLK_MASK) == 0UL))
|
||
|
|
{
|
||
|
|
return 0U;
|
||
|
|
}
|
||
|
|
|
||
|
|
if ((SYSCON->FROCTRL & SYSCON_FROCTRL_SEL_MASK) != 0UL)
|
||
|
|
{
|
||
|
|
return 96000000U;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
return 48000000U;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/*! brief Return Frequency of PLL
|
||
|
|
* return Frequency of PLL
|
||
|
|
*/
|
||
|
|
uint32_t CLOCK_GetPllOutFreq(void)
|
||
|
|
{
|
||
|
|
return s_Pll_Freq;
|
||
|
|
}
|
||
|
|
|
||
|
|
/*! brief Return Frequency of 32kHz osc
|
||
|
|
* return Frequency of 32kHz osc
|
||
|
|
*/
|
||
|
|
uint32_t CLOCK_GetOsc32KFreq(void)
|
||
|
|
{
|
||
|
|
return CLK_RTC_32K_CLK; /* Needs to be corrected to check that RTC Clock is enabled */
|
||
|
|
}
|
||
|
|
/*! brief Return Frequency of Core System
|
||
|
|
* return Frequency of Core System
|
||
|
|
*/
|
||
|
|
uint32_t CLOCK_GetCoreSysClkFreq(void)
|
||
|
|
{
|
||
|
|
uint32_t freq = 0U;
|
||
|
|
|
||
|
|
switch (SYSCON->MAINCLKSELB)
|
||
|
|
{
|
||
|
|
case 0U:
|
||
|
|
if (SYSCON->MAINCLKSELA == 0U)
|
||
|
|
{
|
||
|
|
freq = CLOCK_GetFro12MFreq();
|
||
|
|
}
|
||
|
|
else if (SYSCON->MAINCLKSELA == 1U)
|
||
|
|
{
|
||
|
|
freq = CLOCK_GetExtClkFreq();
|
||
|
|
}
|
||
|
|
else if (SYSCON->MAINCLKSELA == 2U)
|
||
|
|
{
|
||
|
|
freq = CLOCK_GetWdtOscFreq();
|
||
|
|
}
|
||
|
|
else if (SYSCON->MAINCLKSELA == 3U)
|
||
|
|
{
|
||
|
|
freq = CLOCK_GetFroHfFreq();
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
/* Add comment to prevent the case of MISRA C-2012 rule 15.7. */
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
case 2U:
|
||
|
|
freq = CLOCK_GetPllOutFreq();
|
||
|
|
break;
|
||
|
|
|
||
|
|
case 3U:
|
||
|
|
freq = CLOCK_GetOsc32KFreq();
|
||
|
|
break;
|
||
|
|
|
||
|
|
default:
|
||
|
|
assert(false);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
return freq;
|
||
|
|
}
|
||
|
|
/*! brief Return Frequency of I2S MCLK Clock
|
||
|
|
* return Frequency of I2S MCLK Clock
|
||
|
|
*/
|
||
|
|
uint32_t CLOCK_GetI2SMClkFreq(void)
|
||
|
|
{
|
||
|
|
return s_I2S_Mclk_Freq;
|
||
|
|
}
|
||
|
|
|
||
|
|
/*! brief Return Frequency of Asynchronous APB Clock
|
||
|
|
* return Frequency of Asynchronous APB Clock Clock
|
||
|
|
*/
|
||
|
|
uint32_t CLOCK_GetAsyncApbClkFreq(void)
|
||
|
|
{
|
||
|
|
async_clock_src_t clkSrc;
|
||
|
|
uint32_t clkRate;
|
||
|
|
|
||
|
|
clkSrc = CLOCK_GetAsyncApbClkSrc();
|
||
|
|
|
||
|
|
switch (clkSrc)
|
||
|
|
{
|
||
|
|
case kCLOCK_AsyncMainClk:
|
||
|
|
clkRate = CLOCK_GetCoreSysClkFreq();
|
||
|
|
break;
|
||
|
|
case kCLOCK_AsyncFro12Mhz:
|
||
|
|
clkRate = CLK_FRO_12MHZ;
|
||
|
|
break;
|
||
|
|
default:
|
||
|
|
clkRate = 0U;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
return clkRate;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Get FLEXCOMM Clk */
|
||
|
|
/*! brief Return Frequency of Flexcomm functional Clock
|
||
|
|
* return Frequency of Flexcomm functional Clock
|
||
|
|
*/
|
||
|
|
uint32_t CLOCK_GetFlexCommClkFreq(uint32_t id)
|
||
|
|
{
|
||
|
|
uint32_t freq = 0U;
|
||
|
|
|
||
|
|
switch (SYSCON->FXCOMCLKSEL[id])
|
||
|
|
{
|
||
|
|
case 0U:
|
||
|
|
freq = CLOCK_GetFro12MFreq();
|
||
|
|
break;
|
||
|
|
case 1U:
|
||
|
|
freq = CLOCK_GetFroHfFreq();
|
||
|
|
break;
|
||
|
|
case 2U:
|
||
|
|
freq = CLOCK_GetPllOutFreq();
|
||
|
|
break;
|
||
|
|
case 3U:
|
||
|
|
freq = CLOCK_GetI2SMClkFreq();
|
||
|
|
break;
|
||
|
|
case 4U:
|
||
|
|
freq = CLOCK_GetFrgClkFreq();
|
||
|
|
break;
|
||
|
|
|
||
|
|
default:
|
||
|
|
assert(false);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
return freq;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Get ADC Clk */
|
||
|
|
/*! brief Return Frequency of Adc Clock
|
||
|
|
* return Frequency of Adc Clock.
|
||
|
|
*/
|
||
|
|
uint32_t CLOCK_GetAdcClkFreq(void)
|
||
|
|
{
|
||
|
|
uint32_t freq = 0U;
|
||
|
|
|
||
|
|
switch (SYSCON->ADCCLKSEL)
|
||
|
|
{
|
||
|
|
case 0U:
|
||
|
|
freq = CLOCK_GetCoreSysClkFreq();
|
||
|
|
break;
|
||
|
|
case 1U:
|
||
|
|
freq = CLOCK_GetPllOutFreq();
|
||
|
|
break;
|
||
|
|
case 2U:
|
||
|
|
freq = CLOCK_GetFroHfFreq();
|
||
|
|
break;
|
||
|
|
case 7U:
|
||
|
|
freq = 0U;
|
||
|
|
break;
|
||
|
|
default:
|
||
|
|
assert(false);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
return freq / ((SYSCON->ADCCLKDIV & 0xffU) + 1U);
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Get FRG Clk */
|
||
|
|
/*! brief Return Input frequency for the Fractional baud rate generator
|
||
|
|
* return Input Frequency for FRG
|
||
|
|
*/
|
||
|
|
uint32_t CLOCK_GetFRGInputClock(void)
|
||
|
|
{
|
||
|
|
uint32_t freq = 0U;
|
||
|
|
|
||
|
|
switch (SYSCON->FRGCLKSEL)
|
||
|
|
{
|
||
|
|
case 0U:
|
||
|
|
freq = CLOCK_GetCoreSysClkFreq();
|
||
|
|
break;
|
||
|
|
case 1U:
|
||
|
|
freq = CLOCK_GetPllOutFreq();
|
||
|
|
break;
|
||
|
|
case 2U:
|
||
|
|
freq = CLOCK_GetFro12MFreq();
|
||
|
|
break;
|
||
|
|
case 3U:
|
||
|
|
freq = CLOCK_GetFroHfFreq();
|
||
|
|
break;
|
||
|
|
|
||
|
|
default:
|
||
|
|
assert(false);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
return freq;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Get DMIC Clk */
|
||
|
|
/*! brief Return Input frequency for the DMIC
|
||
|
|
* return Input Frequency for DMIC
|
||
|
|
*/
|
||
|
|
uint32_t CLOCK_GetDmicClkFreq(void)
|
||
|
|
{
|
||
|
|
uint32_t freq = 0U;
|
||
|
|
|
||
|
|
switch (SYSCON->DMICCLKSEL)
|
||
|
|
{
|
||
|
|
case 0U:
|
||
|
|
freq = CLOCK_GetFro12MFreq();
|
||
|
|
break;
|
||
|
|
case 1U:
|
||
|
|
freq = CLOCK_GetFroHfFreq();
|
||
|
|
break;
|
||
|
|
case 2U:
|
||
|
|
freq = CLOCK_GetPllOutFreq();
|
||
|
|
break;
|
||
|
|
case 3U:
|
||
|
|
freq = CLOCK_GetI2SMClkFreq();
|
||
|
|
break;
|
||
|
|
case 4U:
|
||
|
|
freq = CLOCK_GetCoreSysClkFreq();
|
||
|
|
break;
|
||
|
|
case 5U:
|
||
|
|
freq = CLOCK_GetWdtOscFreq();
|
||
|
|
break;
|
||
|
|
default:
|
||
|
|
assert(false);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
return freq / ((SYSCON->DMICCLKDIV & 0xffU) + 1U);
|
||
|
|
}
|
||
|
|
|
||
|
|
/*! brief Set output of the Fractional baud rate generator
|
||
|
|
* param freq : Desired output frequency
|
||
|
|
* return Error Code 0 - fail 1 - success
|
||
|
|
*/
|
||
|
|
uint32_t CLOCK_SetFRGClock(uint32_t freq)
|
||
|
|
{
|
||
|
|
uint32_t input = CLOCK_GetFRGInputClock();
|
||
|
|
uint32_t mul;
|
||
|
|
|
||
|
|
if ((freq > 48000000UL) || (freq > input) || (input / freq >= 2UL))
|
||
|
|
{
|
||
|
|
/* FRG output frequency should be less than equal to 48MHz */
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
mul = (uint32_t)((((uint64_t)input - freq) * 256UL) / ((uint64_t)freq));
|
||
|
|
SYSCON->FRGCTRL = (mul << SYSCON_FRGCTRL_MULT_SHIFT) | SYSCON_FRGCTRL_DIV_MASK;
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Get FRG Clk */
|
||
|
|
/*! brief Return Input frequency for the FRG
|
||
|
|
* return Input Frequency for FRG
|
||
|
|
*/
|
||
|
|
uint32_t CLOCK_GetFrgClkFreq(void)
|
||
|
|
{
|
||
|
|
uint32_t freq = 0U;
|
||
|
|
|
||
|
|
if ((SYSCON->FRGCTRL & SYSCON_FRGCTRL_DIV_MASK) == SYSCON_FRGCTRL_DIV_MASK)
|
||
|
|
{
|
||
|
|
freq = (uint32_t)(((uint64_t)CLOCK_GetFRGInputClock() * (SYSCON_FRGCTRL_DIV_MASK + 1U)) /
|
||
|
|
((SYSCON_FRGCTRL_DIV_MASK + 1U) +
|
||
|
|
((SYSCON->FRGCTRL & SYSCON_FRGCTRL_MULT_MASK) >> SYSCON_FRGCTRL_MULT_SHIFT)));
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
freq = 0U;
|
||
|
|
}
|
||
|
|
|
||
|
|
return freq;
|
||
|
|
}
|
||
|
|
|
||
|
|
/*! brief Return Frequency of USB
|
||
|
|
* return Frequency of USB
|
||
|
|
*/
|
||
|
|
uint32_t CLOCK_GetUsbClkFreq(void)
|
||
|
|
{
|
||
|
|
uint32_t freq = 0U;
|
||
|
|
|
||
|
|
if (SYSCON->USBCLKSEL == 0U)
|
||
|
|
{
|
||
|
|
freq = CLOCK_GetFroHfFreq();
|
||
|
|
}
|
||
|
|
else if (SYSCON->USBCLKSEL == 1U)
|
||
|
|
{
|
||
|
|
freq = CLOCK_GetPllOutFreq();
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
/* Add comment to prevent the case of MISRA C-2012 rule 15.7. */
|
||
|
|
}
|
||
|
|
|
||
|
|
return freq / ((SYSCON->USBCLKDIV & 0xffU) + 1U);
|
||
|
|
}
|
||
|
|
|
||
|
|
/*! 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_BusClk:
|
||
|
|
freq = CLOCK_GetCoreSysClkFreq() / ((SYSCON->AHBCLKDIV & 0xffU) + 1U);
|
||
|
|
break;
|
||
|
|
case kCLOCK_FroHf:
|
||
|
|
freq = CLOCK_GetFroHfFreq();
|
||
|
|
break;
|
||
|
|
case kCLOCK_Fro12M:
|
||
|
|
freq = CLOCK_GetFro12MFreq();
|
||
|
|
break;
|
||
|
|
case kCLOCK_PllOut:
|
||
|
|
freq = CLOCK_GetPllOutFreq();
|
||
|
|
break;
|
||
|
|
case kCLOCK_UsbClk:
|
||
|
|
freq = CLOCK_GetUsbClkFreq();
|
||
|
|
break;
|
||
|
|
case kCLOCK_WdtOsc:
|
||
|
|
freq = CLOCK_GetWdtOscFreq();
|
||
|
|
break;
|
||
|
|
case kCLOCK_Frg:
|
||
|
|
freq = CLOCK_GetFrgClkFreq();
|
||
|
|
break;
|
||
|
|
case kCLOCK_AsyncApbClk:
|
||
|
|
freq = CLOCK_GetAsyncApbClkFreq();
|
||
|
|
break;
|
||
|
|
case kCLOCK_FlexI2S:
|
||
|
|
freq = CLOCK_GetI2SMClkFreq();
|
||
|
|
break;
|
||
|
|
default:
|
||
|
|
freq = 0U;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
return freq;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Set the FLASH wait states for the passed frequency */
|
||
|
|
/**
|
||
|
|
* brief Set the flash wait states for the input freuqency.
|
||
|
|
* param iFreq : Input frequency
|
||
|
|
* return Nothing
|
||
|
|
*/
|
||
|
|
void CLOCK_SetFLASHAccessCyclesForFreq(uint32_t iFreq)
|
||
|
|
{
|
||
|
|
if (iFreq <= 12000000U)
|
||
|
|
{
|
||
|
|
CLOCK_SetFLASHAccessCycles(kCLOCK_Flash1Cycle);
|
||
|
|
}
|
||
|
|
else if (iFreq <= 24000000U)
|
||
|
|
{
|
||
|
|
CLOCK_SetFLASHAccessCycles(kCLOCK_Flash2Cycle);
|
||
|
|
}
|
||
|
|
else if (iFreq <= 48000000U)
|
||
|
|
{
|
||
|
|
CLOCK_SetFLASHAccessCycles(kCLOCK_Flash3Cycle);
|
||
|
|
}
|
||
|
|
else if (iFreq <= 72000000U)
|
||
|
|
{
|
||
|
|
CLOCK_SetFLASHAccessCycles(kCLOCK_Flash4Cycle);
|
||
|
|
}
|
||
|
|
else if (iFreq <= 84000000U)
|
||
|
|
{
|
||
|
|
CLOCK_SetFLASHAccessCycles(kCLOCK_Flash5Cycle);
|
||
|
|
}
|
||
|
|
else if (iFreq <= 100000000U)
|
||
|
|
{
|
||
|
|
CLOCK_SetFLASHAccessCycles(kCLOCK_Flash6Cycle);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
CLOCK_SetFLASHAccessCycles(kCLOCK_Flash7Cycle);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Find encoded NDEC value for raw N value, max N = NVALMAX */
|
||
|
|
static uint32_t pllEncodeN(uint32_t N)
|
||
|
|
{
|
||
|
|
uint32_t x, i;
|
||
|
|
|
||
|
|
/* Find NDec */
|
||
|
|
switch (N)
|
||
|
|
{
|
||
|
|
case 0U:
|
||
|
|
x = 0x3FFU;
|
||
|
|
break;
|
||
|
|
|
||
|
|
case 1U:
|
||
|
|
x = 0x302U;
|
||
|
|
break;
|
||
|
|
|
||
|
|
case 2U:
|
||
|
|
x = 0x202U;
|
||
|
|
break;
|
||
|
|
|
||
|
|
default:
|
||
|
|
x = 0x080U;
|
||
|
|
for (i = N; i <= NVALMAX; i++)
|
||
|
|
{
|
||
|
|
x = (((x ^ (x >> 2U) ^ (x >> 3U) ^ (x >> 4U)) & 1U) << 7U) | ((x >> 1U) & 0x7FU);
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
return x & (PLL_NDEC_VAL_M >> PLL_NDEC_VAL_P);
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Find decoded N value for raw NDEC value */
|
||
|
|
static uint32_t pllDecodeN(uint32_t NDEC)
|
||
|
|
{
|
||
|
|
uint32_t n, x, i;
|
||
|
|
|
||
|
|
/* Find NDec */
|
||
|
|
switch (NDEC)
|
||
|
|
{
|
||
|
|
case 0x3FFU:
|
||
|
|
n = 0U;
|
||
|
|
break;
|
||
|
|
|
||
|
|
case 0x302U:
|
||
|
|
n = 1U;
|
||
|
|
break;
|
||
|
|
|
||
|
|
case 0x202U:
|
||
|
|
n = 2U;
|
||
|
|
break;
|
||
|
|
|
||
|
|
default:
|
||
|
|
x = 0x080U;
|
||
|
|
n = 0xFFFFFFFFU;
|
||
|
|
for (i = NVALMAX; i >= 3U; i--)
|
||
|
|
{
|
||
|
|
x = (((x ^ (x >> 2U) ^ (x >> 3U) ^ (x >> 4U)) & 1U) << 7U) | ((x >> 1U) & 0x7FU);
|
||
|
|
if ((x & (PLL_NDEC_VAL_M >> PLL_NDEC_VAL_P)) == NDEC)
|
||
|
|
{
|
||
|
|
/* Decoded value of NDEC */
|
||
|
|
n = i;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
return n;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Find encoded PDEC value for raw P value, max P = PVALMAX */
|
||
|
|
static uint32_t pllEncodeP(uint32_t P)
|
||
|
|
{
|
||
|
|
uint32_t x, i;
|
||
|
|
|
||
|
|
/* Find PDec */
|
||
|
|
switch (P)
|
||
|
|
{
|
||
|
|
case 0U:
|
||
|
|
x = 0x7FU;
|
||
|
|
break;
|
||
|
|
|
||
|
|
case 1U:
|
||
|
|
x = 0x62U;
|
||
|
|
break;
|
||
|
|
|
||
|
|
case 2U:
|
||
|
|
x = 0x42U;
|
||
|
|
break;
|
||
|
|
|
||
|
|
default:
|
||
|
|
x = 0x10U;
|
||
|
|
for (i = P; i <= PVALMAX; i++)
|
||
|
|
{
|
||
|
|
x = (((x ^ (x >> 2U)) & 1U) << 4U) | ((x >> 1U) & 0xFU);
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
return x & (PLL_PDEC_VAL_M >> PLL_PDEC_VAL_P);
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Find decoded P value for raw PDEC value */
|
||
|
|
static uint32_t pllDecodeP(uint32_t PDEC)
|
||
|
|
{
|
||
|
|
uint32_t p, x, i;
|
||
|
|
|
||
|
|
/* Find PDec */
|
||
|
|
switch (PDEC)
|
||
|
|
{
|
||
|
|
case 0x7FU:
|
||
|
|
p = 0U;
|
||
|
|
break;
|
||
|
|
|
||
|
|
case 0x62U:
|
||
|
|
p = 1U;
|
||
|
|
break;
|
||
|
|
|
||
|
|
case 0x42U:
|
||
|
|
p = 2U;
|
||
|
|
break;
|
||
|
|
|
||
|
|
default:
|
||
|
|
x = 0x10U;
|
||
|
|
p = 0xFFFFFFFFU;
|
||
|
|
for (i = PVALMAX; i >= 3U; i--)
|
||
|
|
{
|
||
|
|
x = (((x ^ (x >> 2U)) & 1U) << 4U) | ((x >> 1U) & 0xFU);
|
||
|
|
if ((x & (PLL_PDEC_VAL_M >> PLL_PDEC_VAL_P)) == PDEC)
|
||
|
|
{
|
||
|
|
/* Decoded value of PDEC */
|
||
|
|
p = i;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
return p;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Find encoded MDEC value for raw M value, max M = MVALMAX */
|
||
|
|
static uint32_t pllEncodeM(uint32_t M)
|
||
|
|
{
|
||
|
|
uint32_t i, x;
|
||
|
|
|
||
|
|
/* Find MDec */
|
||
|
|
switch (M)
|
||
|
|
{
|
||
|
|
case 0U:
|
||
|
|
x = 0x1FFFFU;
|
||
|
|
break;
|
||
|
|
|
||
|
|
case 1U:
|
||
|
|
x = 0x18003U;
|
||
|
|
break;
|
||
|
|
|
||
|
|
case 2U:
|
||
|
|
x = 0x10003U;
|
||
|
|
break;
|
||
|
|
|
||
|
|
default:
|
||
|
|
x = 0x04000U;
|
||
|
|
for (i = M; i <= MVALMAX; i++)
|
||
|
|
{
|
||
|
|
x = (((x ^ (x >> 1U)) & 1U) << 14U) | ((x >> 1U) & 0x3FFFU);
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
return x & (PLL_SSCG0_MDEC_VAL_M >> PLL_SSCG0_MDEC_VAL_P);
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Find decoded M value for raw MDEC value */
|
||
|
|
static uint32_t pllDecodeM(uint32_t MDEC)
|
||
|
|
{
|
||
|
|
uint32_t m, i, x;
|
||
|
|
|
||
|
|
/* Find MDec */
|
||
|
|
switch (MDEC)
|
||
|
|
{
|
||
|
|
case 0x1FFFFU:
|
||
|
|
m = 0U;
|
||
|
|
break;
|
||
|
|
|
||
|
|
case 0x18003U:
|
||
|
|
m = 1U;
|
||
|
|
break;
|
||
|
|
|
||
|
|
case 0x10003U:
|
||
|
|
m = 2U;
|
||
|
|
break;
|
||
|
|
|
||
|
|
default:
|
||
|
|
x = 0x04000U;
|
||
|
|
m = 0xFFFFFFFFU;
|
||
|
|
for (i = MVALMAX; i >= 3U; i--)
|
||
|
|
{
|
||
|
|
x = (((x ^ (x >> 1U)) & 1U) << 14U) | ((x >> 1U) & 0x3FFFU);
|
||
|
|
if ((x & (PLL_SSCG0_MDEC_VAL_M >> PLL_SSCG0_MDEC_VAL_P)) == MDEC)
|
||
|
|
{
|
||
|
|
/* Decoded value of MDEC */
|
||
|
|
m = i;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
return m;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Find SELP, SELI, and SELR values for raw M value, max M = MVALMAX */
|
||
|
|
static void pllFindSel(uint32_t M, bool bypassFBDIV2, uint32_t *pSelP, uint32_t *pSelI, uint32_t *pSelR)
|
||
|
|
{
|
||
|
|
/* bandwidth: compute selP from Multiplier */
|
||
|
|
if (M < 60U)
|
||
|
|
{
|
||
|
|
*pSelP = (M >> 1U) + 1U;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
*pSelP = PVALMAX - 1U;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* bandwidth: compute selI from Multiplier */
|
||
|
|
if (M > 16384U)
|
||
|
|
{
|
||
|
|
*pSelI = 1U;
|
||
|
|
}
|
||
|
|
else if (M > 8192U)
|
||
|
|
{
|
||
|
|
*pSelI = 2U;
|
||
|
|
}
|
||
|
|
else if (M > 2048U)
|
||
|
|
{
|
||
|
|
*pSelI = 4U;
|
||
|
|
}
|
||
|
|
else if (M >= 501U)
|
||
|
|
{
|
||
|
|
*pSelI = 8U;
|
||
|
|
}
|
||
|
|
else if (M >= 60U)
|
||
|
|
{
|
||
|
|
*pSelI = 4U * (1024U / (M + 9U));
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
*pSelI = (M & 0x3CU) + 4U;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (*pSelI > ((0x3FUL << SYSCON_SYSPLLCTRL_SELI_SHIFT) >> SYSCON_SYSPLLCTRL_SELI_SHIFT))
|
||
|
|
{
|
||
|
|
*pSelI = ((0x3FUL << SYSCON_SYSPLLCTRL_SELI_SHIFT) >> SYSCON_SYSPLLCTRL_SELI_SHIFT);
|
||
|
|
}
|
||
|
|
|
||
|
|
*pSelR = 0U;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Get predivider (N) from PLL NDEC setting */
|
||
|
|
static uint32_t findPllPreDiv(uint32_t ctrlReg, uint32_t nDecReg)
|
||
|
|
{
|
||
|
|
uint32_t preDiv = 1;
|
||
|
|
|
||
|
|
/* Direct input is not used? */
|
||
|
|
if ((ctrlReg & (1UL << SYSCON_SYSPLLCTRL_DIRECTI_SHIFT)) == 0U)
|
||
|
|
{
|
||
|
|
/* Decode NDEC value to get (N) pre divider */
|
||
|
|
preDiv = pllDecodeN(nDecReg & 0x3FFU);
|
||
|
|
if (preDiv == 0U)
|
||
|
|
{
|
||
|
|
preDiv = 1U;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Adjusted by 1, directi is used to bypass */
|
||
|
|
return preDiv;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Get postdivider (P) from PLL PDEC setting */
|
||
|
|
static uint32_t findPllPostDiv(uint32_t ctrlReg, uint32_t pDecReg)
|
||
|
|
{
|
||
|
|
uint32_t postDiv = 1U;
|
||
|
|
|
||
|
|
/* Direct input is not used? */
|
||
|
|
if ((ctrlReg & SYSCON_SYSPLLCTRL_DIRECTO_MASK) == 0U)
|
||
|
|
{
|
||
|
|
/* Decode PDEC value to get (P) post divider */
|
||
|
|
postDiv = 2U * pllDecodeP(pDecReg & 0x7FU);
|
||
|
|
if (postDiv == 0U)
|
||
|
|
{
|
||
|
|
postDiv = 2U;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Adjusted by 1, directo is used to bypass */
|
||
|
|
return postDiv;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Get multiplier (M) from PLL MDEC and BYPASS_FBDIV2 settings */
|
||
|
|
static uint32_t findPllMMult(uint32_t ctrlReg, uint32_t mDecReg)
|
||
|
|
{
|
||
|
|
uint32_t mMult = 1U;
|
||
|
|
|
||
|
|
/* Decode MDEC value to get (M) multiplier */
|
||
|
|
mMult = pllDecodeM(mDecReg & 0x1FFFFU);
|
||
|
|
|
||
|
|
/* Extra multiply by 2 needed? */
|
||
|
|
if ((ctrlReg & (SYSCON_SYSPLLCTRL_BYPASSCCODIV2_MASK)) == 0U)
|
||
|
|
{
|
||
|
|
mMult = mMult << 1U;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (mMult == 0U)
|
||
|
|
{
|
||
|
|
mMult = 1U;
|
||
|
|
}
|
||
|
|
|
||
|
|
return mMult;
|
||
|
|
}
|
||
|
|
|
||
|
|
static uint32_t FindGreatestCommonDivisor(uint32_t m, uint32_t n)
|
||
|
|
{
|
||
|
|
uint32_t tmp;
|
||
|
|
|
||
|
|
while (n != 0U)
|
||
|
|
{
|
||
|
|
tmp = n;
|
||
|
|
n = m % n;
|
||
|
|
m = tmp;
|
||
|
|
}
|
||
|
|
|
||
|
|
return m;
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
* Set PLL output based on desired output rate.
|
||
|
|
* In this function, the it calculates the PLL setting for output frequency from input clock
|
||
|
|
* frequency. The calculation would cost a few time. So it is not recommaned to use it frequently.
|
||
|
|
* the "pllctrl", "pllndec", "pllpdec", "pllmdec" would updated in this function.
|
||
|
|
*/
|
||
|
|
static pll_error_t CLOCK_GetPllConfigInternal(
|
||
|
|
uint32_t finHz, uint32_t foutHz, pll_setup_t *pSetup, bool useFeedbackDiv2, bool useSS)
|
||
|
|
{
|
||
|
|
uint32_t nDivOutHz, fccoHz, multFccoDiv;
|
||
|
|
uint32_t pllPreDivider, pllMultiplier, pllBypassFBDIV2, pllPostDivider;
|
||
|
|
uint32_t pllDirectInput, pllDirectOutput;
|
||
|
|
uint32_t pllSelP, pllSelI, pllSelR, bandsel, uplimoff;
|
||
|
|
|
||
|
|
/* Baseline parameters (no input or output dividers) */
|
||
|
|
pllPreDivider = 1U; /* 1 implies pre-divider will be disabled */
|
||
|
|
pllPostDivider = 0U; /* 0 implies post-divider will be disabled */
|
||
|
|
pllDirectOutput = 1U;
|
||
|
|
if (useFeedbackDiv2)
|
||
|
|
{
|
||
|
|
/* Using feedback divider for M, so disable bypass */
|
||
|
|
pllBypassFBDIV2 = 0U;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
pllBypassFBDIV2 = 1U;
|
||
|
|
}
|
||
|
|
multFccoDiv = (2U - pllBypassFBDIV2);
|
||
|
|
|
||
|
|
/* Verify output rate parameter */
|
||
|
|
if (foutHz > PLL_MAX_CCO_FREQ_MHZ)
|
||
|
|
{
|
||
|
|
/* Maximum PLL output with post divider=1 cannot go above this frequency */
|
||
|
|
return kStatus_PLL_OutputTooHigh;
|
||
|
|
}
|
||
|
|
if (foutHz < (PLL_MIN_CCO_FREQ_MHZ / (PVALMAX << 1U)))
|
||
|
|
{
|
||
|
|
/* Minmum PLL output with maximum post divider cannot go below this frequency */
|
||
|
|
return kStatus_PLL_OutputTooLow;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* If using SS mode, input clock needs to be between 2MHz and 4MHz */
|
||
|
|
if (useSS)
|
||
|
|
{
|
||
|
|
/* Verify input rate parameter */
|
||
|
|
if (finHz < PLL_MIN_IN_SSMODE)
|
||
|
|
{
|
||
|
|
/* Input clock into the PLL cannot be lower than this */
|
||
|
|
return kStatus_PLL_InputTooLow;
|
||
|
|
}
|
||
|
|
/* PLL input in SS mode must be under 4MHz */
|
||
|
|
pllPreDivider = finHz / ((PLL_MIN_IN_SSMODE + PLL_MAX_IN_SSMODE) / 2U);
|
||
|
|
if (pllPreDivider > NVALMAX)
|
||
|
|
{
|
||
|
|
return kStatus_PLL_InputTooHigh;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
/* Verify input rate parameter */
|
||
|
|
if (finHz < PLL_LOWER_IN_LIMIT)
|
||
|
|
{
|
||
|
|
/* Input clock into the PLL cannot be lower than this */
|
||
|
|
return kStatus_PLL_InputTooLow;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Find the optimal CCO frequency for the output and input that
|
||
|
|
will keep it inside the PLL CCO range. This may require
|
||
|
|
tweaking the post-divider for the PLL. */
|
||
|
|
fccoHz = foutHz;
|
||
|
|
while (fccoHz < PLL_MIN_CCO_FREQ_MHZ)
|
||
|
|
{
|
||
|
|
/* CCO output is less than minimum CCO range, so the CCO output
|
||
|
|
needs to be bumped up and the post-divider is used to bring
|
||
|
|
the PLL output back down. */
|
||
|
|
pllPostDivider++;
|
||
|
|
if (pllPostDivider > PVALMAX)
|
||
|
|
{
|
||
|
|
return kStatus_PLL_OutsideIntLimit;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Target CCO goes up, PLL output goes down */
|
||
|
|
fccoHz = foutHz * (pllPostDivider * 2U);
|
||
|
|
pllDirectOutput = 0U;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Determine if a pre-divider is needed to get the best frequency */
|
||
|
|
if ((finHz > PLL_LOWER_IN_LIMIT) && (fccoHz >= finHz) && (useSS == false))
|
||
|
|
{
|
||
|
|
uint32_t a = FindGreatestCommonDivisor(fccoHz, (multFccoDiv * finHz));
|
||
|
|
|
||
|
|
if (a > 20000U)
|
||
|
|
{
|
||
|
|
a = (multFccoDiv * finHz) / a;
|
||
|
|
if ((a != 0U) && (a < PLL_MAX_N_DIV))
|
||
|
|
{
|
||
|
|
pllPreDivider = a;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Bypass pre-divider hardware if pre-divider is 1 */
|
||
|
|
if (pllPreDivider > 1U)
|
||
|
|
{
|
||
|
|
pllDirectInput = 0U;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
pllDirectInput = 1U;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Determine PLL multipler */
|
||
|
|
nDivOutHz = (finHz / pllPreDivider);
|
||
|
|
pllMultiplier = (fccoHz / nDivOutHz) / multFccoDiv;
|
||
|
|
|
||
|
|
/* Find optimal values for filter */
|
||
|
|
if (useSS == false)
|
||
|
|
{
|
||
|
|
/* Will bumping up M by 1 get us closer to the desired CCO frequency? */
|
||
|
|
if ((nDivOutHz * ((multFccoDiv * pllMultiplier * 2U) + 1U)) < (fccoHz * 2U))
|
||
|
|
{
|
||
|
|
pllMultiplier++;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Setup filtering */
|
||
|
|
pllFindSel(pllMultiplier, (bool)pllBypassFBDIV2, &pllSelP, &pllSelI, &pllSelR);
|
||
|
|
bandsel = 1U;
|
||
|
|
uplimoff = 0U;
|
||
|
|
|
||
|
|
/* Get encoded value for M (mult) and use manual filter, disable SS mode */
|
||
|
|
pSetup->syspllssctrl[0] =
|
||
|
|
(PLL_SSCG0_MDEC_VAL_SET(pllEncodeM(pllMultiplier)) | (1UL << SYSCON_SYSPLLSSCTRL0_SEL_EXT_SHIFT));
|
||
|
|
|
||
|
|
/* Power down SSC, not used */
|
||
|
|
pSetup->syspllssctrl[1] = (1UL << SYSCON_SYSPLLSSCTRL1_PD_SHIFT);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
uint64_t fc;
|
||
|
|
|
||
|
|
/* Filtering will be handled by SSC */
|
||
|
|
pllSelR = 0U;
|
||
|
|
pllSelI = 0U;
|
||
|
|
pllSelP = 0U;
|
||
|
|
bandsel = 0U;
|
||
|
|
uplimoff = 1U;
|
||
|
|
|
||
|
|
/* The PLL multiplier will get very close and slightly under the
|
||
|
|
desired target frequency. A small fractional component can be
|
||
|
|
added to fine tune the frequency upwards to the target. */
|
||
|
|
fc = (((uint64_t)fccoHz % ((uint64_t)multFccoDiv * nDivOutHz)) << 11U) / ((uint64_t)multFccoDiv * nDivOutHz);
|
||
|
|
|
||
|
|
/* MDEC set by SSC */
|
||
|
|
pSetup->syspllssctrl[0U] = 0U;
|
||
|
|
|
||
|
|
/* Set multiplier */
|
||
|
|
pSetup->syspllssctrl[1] = PLL_SSCG1_MD_INT_SET(pllMultiplier) | PLL_SSCG1_MD_FRACT_SET((uint32_t)fc);
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Get encoded values for N (prediv) and P (postdiv) */
|
||
|
|
pSetup->syspllndec = PLL_NDEC_VAL_SET(pllEncodeN(pllPreDivider));
|
||
|
|
pSetup->syspllpdec = PLL_PDEC_VAL_SET(pllEncodeP(pllPostDivider));
|
||
|
|
|
||
|
|
/* PLL control */
|
||
|
|
pSetup->syspllctrl = (pllSelR << SYSCON_SYSPLLCTRL_SELR_SHIFT) | /* Filter coefficient */
|
||
|
|
(pllSelI << SYSCON_SYSPLLCTRL_SELI_SHIFT) | /* Filter coefficient */
|
||
|
|
(pllSelP << SYSCON_SYSPLLCTRL_SELP_SHIFT) | /* Filter coefficient */
|
||
|
|
(0UL << SYSCON_SYSPLLCTRL_BYPASS_SHIFT) | /* PLL bypass mode disabled */
|
||
|
|
(pllBypassFBDIV2 << SYSCON_SYSPLLCTRL_BYPASSCCODIV2_SHIFT) | /* Extra M / 2 divider? */
|
||
|
|
(uplimoff << SYSCON_SYSPLLCTRL_UPLIMOFF_SHIFT) | /* SS/fractional mode disabled */
|
||
|
|
(bandsel << SYSCON_SYSPLLCTRL_BANDSEL_SHIFT) | /* Manual bandwidth selection enabled */
|
||
|
|
(pllDirectInput << SYSCON_SYSPLLCTRL_DIRECTI_SHIFT) | /* Bypass pre-divider? */
|
||
|
|
(pllDirectOutput << SYSCON_SYSPLLCTRL_DIRECTO_SHIFT); /* Bypass post-divider? */
|
||
|
|
|
||
|
|
return kStatus_PLL_Success;
|
||
|
|
}
|
||
|
|
|
||
|
|
#if (defined(CLOCK_USR_CFG_PLL_CONFIG_CACHE_COUNT) && CLOCK_USR_CFG_PLL_CONFIG_CACHE_COUNT)
|
||
|
|
/* Alloct the static buffer for cache. */
|
||
|
|
static pll_setup_t s_PllSetupCacheStruct[CLOCK_USR_CFG_PLL_CONFIG_CACHE_COUNT];
|
||
|
|
static uint32_t s_FinHzCache[CLOCK_USR_CFG_PLL_CONFIG_CACHE_COUNT] = {0};
|
||
|
|
static uint32_t s_FoutHzCache[CLOCK_USR_CFG_PLL_CONFIG_CACHE_COUNT] = {0};
|
||
|
|
static bool s_UseFeedbackDiv2Cache[CLOCK_USR_CFG_PLL_CONFIG_CACHE_COUNT] = {false};
|
||
|
|
static bool s_UseSSCache[CLOCK_USR_CFG_PLL_CONFIG_CACHE_COUNT] = {false};
|
||
|
|
static uint32_t s_PllSetupCacheIdx = 0U;
|
||
|
|
#endif /* CLOCK_USR_CFG_PLL_CONFIG_CACHE_COUNT */
|
||
|
|
|
||
|
|
/*
|
||
|
|
* Calculate the PLL setting values from input clock freq to output freq.
|
||
|
|
*/
|
||
|
|
static pll_error_t CLOCK_GetPllConfig(
|
||
|
|
uint32_t finHz, uint32_t foutHz, pll_setup_t *pSetup, bool useFeedbackDiv2, bool useSS)
|
||
|
|
{
|
||
|
|
pll_error_t retErr;
|
||
|
|
#if (defined(CLOCK_USR_CFG_PLL_CONFIG_CACHE_COUNT) && CLOCK_USR_CFG_PLL_CONFIG_CACHE_COUNT)
|
||
|
|
uint32_t i;
|
||
|
|
|
||
|
|
for (i = 0U; i < CLOCK_USR_CFG_PLL_CONFIG_CACHE_COUNT; i++)
|
||
|
|
{
|
||
|
|
if ((finHz == s_FinHzCache[i]) && (foutHz == s_FoutHzCache[i]) &&
|
||
|
|
(useFeedbackDiv2 == s_UseFeedbackDiv2Cache[i]) && (useSS == s_UseSSCache[i]))
|
||
|
|
{
|
||
|
|
/* Hit the target in cache buffer. */
|
||
|
|
pSetup->syspllctrl = s_PllSetupCacheStruct[i].syspllctrl;
|
||
|
|
pSetup->syspllndec = s_PllSetupCacheStruct[i].syspllndec;
|
||
|
|
pSetup->syspllpdec = s_PllSetupCacheStruct[i].syspllpdec;
|
||
|
|
pSetup->syspllssctrl[0] = s_PllSetupCacheStruct[i].syspllssctrl[0];
|
||
|
|
pSetup->syspllssctrl[1] = s_PllSetupCacheStruct[i].syspllssctrl[1];
|
||
|
|
retErr = kStatus_PLL_Success;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (i < CLOCK_USR_CFG_PLL_CONFIG_CACHE_COUNT)
|
||
|
|
{
|
||
|
|
return retErr;
|
||
|
|
}
|
||
|
|
#endif /* CLOCK_USR_CFG_PLL_CONFIG_CACHE_COUNT */
|
||
|
|
|
||
|
|
retErr = CLOCK_GetPllConfigInternal(finHz, foutHz, pSetup, useFeedbackDiv2, useSS);
|
||
|
|
|
||
|
|
#if (defined(CLOCK_USR_CFG_PLL_CONFIG_CACHE_COUNT) && CLOCK_USR_CFG_PLL_CONFIG_CACHE_COUNT)
|
||
|
|
/* Cache the most recent calulation result into buffer. */
|
||
|
|
s_FinHzCache[s_PllSetupCacheIdx] = finHz;
|
||
|
|
s_FoutHzCache[s_PllSetupCacheIdx] = foutHz;
|
||
|
|
s_UseFeedbackDiv2Cache[s_PllSetupCacheIdx] = useFeedbackDiv2;
|
||
|
|
s_UseSSCache[s_PllSetupCacheIdx] = useSS;
|
||
|
|
|
||
|
|
s_PllSetupCacheStruct[s_PllSetupCacheIdx].syspllctrl = pSetup->syspllctrl;
|
||
|
|
s_PllSetupCacheStruct[s_PllSetupCacheIdx].syspllndec = pSetup->syspllndec;
|
||
|
|
s_PllSetupCacheStruct[s_PllSetupCacheIdx].syspllpdec = pSetup->syspllpdec;
|
||
|
|
s_PllSetupCacheStruct[s_PllSetupCacheIdx].syspllssctrl[0] = pSetup->syspllssctrl[0];
|
||
|
|
s_PllSetupCacheStruct[s_PllSetupCacheIdx].syspllssctrl[1] = pSetup->syspllssctrl[1];
|
||
|
|
/* Update the index for next available buffer. */
|
||
|
|
s_PllSetupCacheIdx = (s_PllSetupCacheIdx + 1U) % CLOCK_USR_CFG_PLL_CONFIG_CACHE_COUNT;
|
||
|
|
#endif /* CLOCK_USR_CFG_PLL_CONFIG_CACHE_COUNT */
|
||
|
|
|
||
|
|
return retErr;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Update local PLL rate variable */
|
||
|
|
static void CLOCK_GetSystemPLLOutFromSetupUpdate(pll_setup_t *pSetup)
|
||
|
|
{
|
||
|
|
s_Pll_Freq = CLOCK_GetSystemPLLOutFromSetup(pSetup);
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Return System PLL input clock rate */
|
||
|
|
/*! brief Return System PLL input clock rate
|
||
|
|
* return System PLL input clock rate
|
||
|
|
*/
|
||
|
|
uint32_t CLOCK_GetSystemPLLInClockRate(void)
|
||
|
|
{
|
||
|
|
uint32_t clkRate = 0U;
|
||
|
|
|
||
|
|
switch ((SYSCON->SYSPLLCLKSEL & SYSCON_SYSPLLCLKSEL_SEL_MASK))
|
||
|
|
{
|
||
|
|
case 0x00U:
|
||
|
|
clkRate = CLK_FRO_12MHZ;
|
||
|
|
break;
|
||
|
|
|
||
|
|
case 0x01U:
|
||
|
|
clkRate = CLOCK_GetExtClkFreq();
|
||
|
|
break;
|
||
|
|
|
||
|
|
case 0x02U:
|
||
|
|
clkRate = CLOCK_GetWdtOscFreq();
|
||
|
|
break;
|
||
|
|
|
||
|
|
case 0x03U:
|
||
|
|
clkRate = CLOCK_GetOsc32KFreq();
|
||
|
|
break;
|
||
|
|
|
||
|
|
default:
|
||
|
|
clkRate = 0U;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
return clkRate;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Return System PLL output clock rate from setup structure */
|
||
|
|
/*! brief Return System PLL output clock rate from setup structure
|
||
|
|
* param pSetup : Pointer to a PLL setup structure
|
||
|
|
* return System PLL output clock rate calculated from the setup structure
|
||
|
|
*/
|
||
|
|
uint32_t CLOCK_GetSystemPLLOutFromSetup(pll_setup_t *pSetup)
|
||
|
|
{
|
||
|
|
uint32_t prediv, postdiv, mMult, inPllRate;
|
||
|
|
uint64_t workRate;
|
||
|
|
|
||
|
|
/* Get the input clock frequency of PLL. */
|
||
|
|
inPllRate = CLOCK_GetSystemPLLInClockRate();
|
||
|
|
|
||
|
|
/*
|
||
|
|
* If the PLL is bypassed, PLL would not be used and the output of PLL module would just be the input clock.
|
||
|
|
*/
|
||
|
|
if ((pSetup->syspllctrl & (SYSCON_SYSPLLCTRL_BYPASS_MASK)) == 0U)
|
||
|
|
{
|
||
|
|
/* PLL is not in bypass mode, get pre-divider, and M divider, post-divider. */
|
||
|
|
/*
|
||
|
|
* 1. Pre-divider
|
||
|
|
* Pre-divider is only available when the DIRECTI is disabled.
|
||
|
|
*/
|
||
|
|
if (0U == (pSetup->syspllctrl & SYSCON_SYSPLLCTRL_DIRECTI_MASK))
|
||
|
|
{
|
||
|
|
prediv = findPllPreDiv(pSetup->syspllctrl, pSetup->syspllndec);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
prediv = 1U; /* The pre-divider is bypassed. */
|
||
|
|
}
|
||
|
|
/* Adjust input clock */
|
||
|
|
inPllRate = inPllRate / prediv;
|
||
|
|
|
||
|
|
/*
|
||
|
|
* 2. M divider
|
||
|
|
* If using the SS, use the multiplier.
|
||
|
|
*/
|
||
|
|
if ((pSetup->syspllssctrl[1] & (SYSCON_SYSPLLSSCTRL1_PD_MASK)) != 0UL)
|
||
|
|
{
|
||
|
|
/* MDEC used for rate */
|
||
|
|
mMult = findPllMMult(pSetup->syspllctrl, pSetup->syspllssctrl[0]);
|
||
|
|
workRate = (uint64_t)inPllRate * (uint64_t)mMult;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
uint64_t fract;
|
||
|
|
|
||
|
|
/* SS multipler used for rate */
|
||
|
|
mMult = (pSetup->syspllssctrl[1] & PLL_SSCG1_MD_INT_M) >> PLL_SSCG1_MD_INT_P;
|
||
|
|
workRate = (uint64_t)inPllRate * (uint64_t)mMult;
|
||
|
|
|
||
|
|
/* Adjust by fractional */
|
||
|
|
fract = ((uint64_t)(pSetup->syspllssctrl[1]) & PLL_SSCG1_MD_FRACT_M) >> PLL_SSCG1_MD_FRACT_P;
|
||
|
|
workRate = workRate + ((inPllRate * fract) / 0x800U);
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
* 3. Post-divider
|
||
|
|
* Post-divider is only available when the DIRECTO is disabled.
|
||
|
|
*/
|
||
|
|
if (0U == (pSetup->syspllctrl & SYSCON_SYSPLLCTRL_DIRECTO_MASK))
|
||
|
|
{
|
||
|
|
postdiv = findPllPostDiv(pSetup->syspllctrl, pSetup->syspllpdec);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
postdiv = 1U; /* The post-divider is bypassed. */
|
||
|
|
}
|
||
|
|
workRate = workRate / ((uint64_t)postdiv);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
/* In bypass mode */
|
||
|
|
workRate = (uint64_t)inPllRate;
|
||
|
|
}
|
||
|
|
|
||
|
|
return (uint32_t)workRate;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Set the current PLL Rate */
|
||
|
|
/*! brief Store the current PLL rate
|
||
|
|
* param rate: Current rate of the PLL
|
||
|
|
* return Nothing
|
||
|
|
**/
|
||
|
|
void CLOCK_SetStoredPLLClockRate(uint32_t rate)
|
||
|
|
{
|
||
|
|
s_Pll_Freq = rate;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Return System PLL output clock rate */
|
||
|
|
/*! brief Return System PLL output clock rate
|
||
|
|
* param recompute : Forces a PLL rate recomputation if true
|
||
|
|
* return System PLL output clock rate
|
||
|
|
* note The PLL rate is cached in the driver in a variable as
|
||
|
|
* the rate computation function can take some time to perform. It
|
||
|
|
* is recommended to use 'false' with the 'recompute' parameter.
|
||
|
|
*/
|
||
|
|
uint32_t CLOCK_GetSystemPLLOutClockRate(bool recompute)
|
||
|
|
{
|
||
|
|
pll_setup_t Setup;
|
||
|
|
uint32_t rate;
|
||
|
|
|
||
|
|
if ((recompute) || (s_Pll_Freq == 0U))
|
||
|
|
{
|
||
|
|
Setup.syspllctrl = SYSCON->SYSPLLCTRL;
|
||
|
|
Setup.syspllndec = SYSCON->SYSPLLNDEC;
|
||
|
|
Setup.syspllpdec = SYSCON->SYSPLLPDEC;
|
||
|
|
Setup.syspllssctrl[0] = SYSCON->SYSPLLSSCTRL0;
|
||
|
|
Setup.syspllssctrl[1] = SYSCON->SYSPLLSSCTRL1;
|
||
|
|
|
||
|
|
CLOCK_GetSystemPLLOutFromSetupUpdate(&Setup);
|
||
|
|
}
|
||
|
|
|
||
|
|
rate = s_Pll_Freq;
|
||
|
|
|
||
|
|
return rate;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Set PLL output based on the passed PLL setup data */
|
||
|
|
/*! brief Set PLL output based on the passed PLL setup data
|
||
|
|
* param pControl : Pointer to populated PLL control structure to generate setup with
|
||
|
|
* param pSetup : Pointer to PLL setup structure to be filled
|
||
|
|
* return PLL_ERROR_SUCCESS on success, or PLL setup error code
|
||
|
|
* note Actual frequency for setup may vary from the desired frequency based on the
|
||
|
|
* accuracy of input clocks, rounding, non-fractional PLL mode, etc.
|
||
|
|
*/
|
||
|
|
pll_error_t CLOCK_SetupPLLData(pll_config_t *pControl, pll_setup_t *pSetup)
|
||
|
|
{
|
||
|
|
uint32_t inRate;
|
||
|
|
bool useSS = (bool)((pControl->flags & PLL_CONFIGFLAG_FORCENOFRACT) == 0U);
|
||
|
|
bool useFbDiv2;
|
||
|
|
|
||
|
|
pll_error_t pllError;
|
||
|
|
|
||
|
|
/* Determine input rate for the PLL */
|
||
|
|
if ((pControl->flags & PLL_CONFIGFLAG_USEINRATE) != 0UL)
|
||
|
|
{
|
||
|
|
inRate = pControl->inputRate;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
inRate = CLOCK_GetSystemPLLInClockRate();
|
||
|
|
}
|
||
|
|
|
||
|
|
if ((pSetup->flags & PLL_SETUPFLAG_USEFEEDBACKDIV2) != 0UL)
|
||
|
|
{
|
||
|
|
useFbDiv2 = true;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
useFbDiv2 = false;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* PLL flag options */
|
||
|
|
pllError = CLOCK_GetPllConfig(inRate, pControl->desiredRate, pSetup, useFbDiv2, useSS);
|
||
|
|
if ((useSS) && (pllError == kStatus_PLL_Success))
|
||
|
|
{
|
||
|
|
/* If using SS mode, then some tweaks are made to the generated setup */
|
||
|
|
pSetup->syspllssctrl[1] |= (uint32_t)pControl->ss_mf | (uint32_t)pControl->ss_mr | (uint32_t)pControl->ss_mc;
|
||
|
|
if (pControl->mfDither)
|
||
|
|
{
|
||
|
|
pSetup->syspllssctrl[1] |= (1UL << SYSCON_SYSPLLSSCTRL1_DITHER_SHIFT);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return pllError;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Set PLL output from PLL setup structure */
|
||
|
|
/*! brief Set PLL output from PLL setup structure (precise frequency)
|
||
|
|
* param pSetup : Pointer to populated PLL setup structure
|
||
|
|
* param flagcfg : Flag configuration for PLL config structure
|
||
|
|
* return PLL_ERROR_SUCCESS on success, or PLL setup error code
|
||
|
|
* note This function will power off the PLL, setup the PLL with the
|
||
|
|
* new setup data, and then optionally powerup the PLL, wait for PLL lock,
|
||
|
|
* and adjust system voltages to the new PLL rate. The function will not
|
||
|
|
* alter any source clocks (ie, main systen clock) that may use the PLL,
|
||
|
|
* so these should be setup prior to and after exiting the function.
|
||
|
|
*/
|
||
|
|
pll_error_t CLOCK_SetupSystemPLLPrec(pll_setup_t *pSetup, uint32_t flagcfg)
|
||
|
|
{
|
||
|
|
/* Power off PLL during setup changes */
|
||
|
|
POWER_EnablePD(kPDRUNCFG_PD_SYS_PLL0);
|
||
|
|
|
||
|
|
pSetup->flags = flagcfg;
|
||
|
|
|
||
|
|
/* Write PLL setup data */
|
||
|
|
SYSCON->SYSPLLCTRL = pSetup->syspllctrl;
|
||
|
|
SYSCON->SYSPLLNDEC = pSetup->syspllndec;
|
||
|
|
SYSCON->SYSPLLNDEC = pSetup->syspllndec | (1UL << SYSCON_SYSPLLNDEC_NREQ_SHIFT); /* latch */
|
||
|
|
SYSCON->SYSPLLPDEC = pSetup->syspllpdec;
|
||
|
|
SYSCON->SYSPLLPDEC = pSetup->syspllpdec | (1UL << SYSCON_SYSPLLPDEC_PREQ_SHIFT); /* latch */
|
||
|
|
SYSCON->SYSPLLSSCTRL0 = pSetup->syspllssctrl[0];
|
||
|
|
SYSCON->SYSPLLSSCTRL0 = pSetup->syspllssctrl[0] | (1UL << SYSCON_SYSPLLSSCTRL0_MREQ_SHIFT); /* latch */
|
||
|
|
SYSCON->SYSPLLSSCTRL1 = pSetup->syspllssctrl[1];
|
||
|
|
SYSCON->SYSPLLSSCTRL1 = pSetup->syspllssctrl[1] | (1UL << SYSCON_SYSPLLSSCTRL1_MDREQ_SHIFT); /* latch */
|
||
|
|
|
||
|
|
/* Flags for lock or power on */
|
||
|
|
if ((pSetup->flags & (PLL_SETUPFLAG_POWERUP | PLL_SETUPFLAG_WAITLOCK)) != 0UL)
|
||
|
|
{
|
||
|
|
/* If turning the PLL back on, perform the following sequence to accelerate PLL lock */
|
||
|
|
uint32_t maxCCO = (1UL << 18U) | 0x5dd2U; /* CCO = 1.6Ghz + MDEC enabled*/
|
||
|
|
uint32_t curSSCTRL = SYSCON->SYSPLLSSCTRL0 & ~(1UL << 17U);
|
||
|
|
|
||
|
|
/* Initialize and power up PLL */
|
||
|
|
SYSCON->SYSPLLSSCTRL0 = maxCCO;
|
||
|
|
POWER_DisablePD(kPDRUNCFG_PD_SYS_PLL0);
|
||
|
|
|
||
|
|
/* Set mreq to activate */
|
||
|
|
SYSCON->SYSPLLSSCTRL0 = maxCCO | (1UL << 17U);
|
||
|
|
|
||
|
|
/* Delay for 72 uSec @ 12Mhz */
|
||
|
|
SDK_DelayAtLeastUs(72U, SDK_DEVICE_MAXIMUM_CPU_CLOCK_FREQUENCY);
|
||
|
|
|
||
|
|
/* clear mreq to prepare for restoring mreq */
|
||
|
|
SYSCON->SYSPLLSSCTRL0 = curSSCTRL;
|
||
|
|
|
||
|
|
/* set original value back and activate */
|
||
|
|
SYSCON->SYSPLLSSCTRL0 = curSSCTRL | (1UL << 17U);
|
||
|
|
|
||
|
|
/* Enable peripheral states by setting low */
|
||
|
|
POWER_DisablePD(kPDRUNCFG_PD_SYS_PLL0);
|
||
|
|
}
|
||
|
|
if ((pSetup->flags & PLL_SETUPFLAG_WAITLOCK) != 0UL)
|
||
|
|
{
|
||
|
|
while (CLOCK_IsSystemPLLLocked() == false)
|
||
|
|
{
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Update current programmed PLL rate var */
|
||
|
|
CLOCK_GetSystemPLLOutFromSetupUpdate(pSetup);
|
||
|
|
|
||
|
|
/* System voltage adjustment, occurs prior to setting main system clock */
|
||
|
|
if ((pSetup->flags & PLL_SETUPFLAG_ADGVOLT) != 0UL)
|
||
|
|
{
|
||
|
|
POWER_SetVoltageForFreq(s_Pll_Freq);
|
||
|
|
}
|
||
|
|
|
||
|
|
return kStatus_PLL_Success;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Setup PLL Frequency from pre-calculated value */
|
||
|
|
/**
|
||
|
|
* brief Set PLL output from PLL setup structure (precise frequency)
|
||
|
|
* param pSetup : Pointer to populated PLL setup structure
|
||
|
|
* return kStatus_PLL_Success on success, or PLL setup error code
|
||
|
|
* note This function will power off the PLL, setup the PLL with the
|
||
|
|
* new setup data, and then optionally powerup the PLL, wait for PLL lock,
|
||
|
|
* and adjust system voltages to the new PLL rate. The function will not
|
||
|
|
* alter any source clocks (ie, main systen clock) that may use the PLL,
|
||
|
|
* so these should be setup prior to and after exiting the function.
|
||
|
|
*/
|
||
|
|
pll_error_t CLOCK_SetPLLFreq(const pll_setup_t *pSetup)
|
||
|
|
{
|
||
|
|
/* Power off PLL during setup changes */
|
||
|
|
POWER_EnablePD(kPDRUNCFG_PD_SYS_PLL0);
|
||
|
|
|
||
|
|
/* Write PLL setup data */
|
||
|
|
SYSCON->SYSPLLCTRL = pSetup->syspllctrl;
|
||
|
|
SYSCON->SYSPLLNDEC = pSetup->syspllndec;
|
||
|
|
SYSCON->SYSPLLNDEC = pSetup->syspllndec | (1UL << SYSCON_SYSPLLNDEC_NREQ_SHIFT); /* latch */
|
||
|
|
SYSCON->SYSPLLPDEC = pSetup->syspllpdec;
|
||
|
|
SYSCON->SYSPLLPDEC = pSetup->syspllpdec | (1UL << SYSCON_SYSPLLPDEC_PREQ_SHIFT); /* latch */
|
||
|
|
SYSCON->SYSPLLSSCTRL0 = pSetup->syspllssctrl[0];
|
||
|
|
SYSCON->SYSPLLSSCTRL0 = pSetup->syspllssctrl[0] | (1UL << SYSCON_SYSPLLSSCTRL0_MREQ_SHIFT); /* latch */
|
||
|
|
SYSCON->SYSPLLSSCTRL1 = pSetup->syspllssctrl[1];
|
||
|
|
SYSCON->SYSPLLSSCTRL1 = pSetup->syspllssctrl[1] | (1UL << SYSCON_SYSPLLSSCTRL1_MDREQ_SHIFT); /* latch */
|
||
|
|
|
||
|
|
/* Flags for lock or power on */
|
||
|
|
if ((pSetup->flags & (PLL_SETUPFLAG_POWERUP | PLL_SETUPFLAG_WAITLOCK)) != 0UL)
|
||
|
|
{
|
||
|
|
/* If turning the PLL back on, perform the following sequence to accelerate PLL lock */
|
||
|
|
uint32_t maxCCO = (1UL << 18U) | 0x5dd2U; /* CCO = 1.6Ghz + MDEC enabled*/
|
||
|
|
uint32_t curSSCTRL = SYSCON->SYSPLLSSCTRL0 & ~(1UL << 17U);
|
||
|
|
|
||
|
|
/* Initialize and power up PLL */
|
||
|
|
SYSCON->SYSPLLSSCTRL0 = maxCCO;
|
||
|
|
POWER_DisablePD(kPDRUNCFG_PD_SYS_PLL0);
|
||
|
|
|
||
|
|
/* Set mreq to activate */
|
||
|
|
SYSCON->SYSPLLSSCTRL0 = maxCCO | (1UL << 17U);
|
||
|
|
|
||
|
|
/* Delay for 72 uSec @ 12Mhz */
|
||
|
|
SDK_DelayAtLeastUs(72U, SDK_DEVICE_MAXIMUM_CPU_CLOCK_FREQUENCY);
|
||
|
|
|
||
|
|
/* clear mreq to prepare for restoring mreq */
|
||
|
|
SYSCON->SYSPLLSSCTRL0 = curSSCTRL;
|
||
|
|
|
||
|
|
/* set original value back and activate */
|
||
|
|
SYSCON->SYSPLLSSCTRL0 = curSSCTRL | (1UL << 17U);
|
||
|
|
|
||
|
|
/* Enable peripheral states by setting low */
|
||
|
|
POWER_DisablePD(kPDRUNCFG_PD_SYS_PLL0);
|
||
|
|
}
|
||
|
|
if ((pSetup->flags & PLL_SETUPFLAG_WAITLOCK) != 0UL)
|
||
|
|
{
|
||
|
|
while (CLOCK_IsSystemPLLLocked() == false)
|
||
|
|
{
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Update current programmed PLL rate var */
|
||
|
|
s_Pll_Freq = pSetup->pllRate;
|
||
|
|
|
||
|
|
return kStatus_PLL_Success;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Set System PLL clock based on the input frequency and multiplier */
|
||
|
|
/*! brief Set PLL output based on the multiplier and input frequency
|
||
|
|
* param multiply_by : multiplier
|
||
|
|
* param input_freq : Clock input frequency of the PLL
|
||
|
|
* return Nothing
|
||
|
|
* note Unlike the Chip_Clock_SetupSystemPLLPrec() function, this
|
||
|
|
* function does not disable or enable PLL power, wait for PLL lock,
|
||
|
|
* or adjust system voltages. These must be done in the application.
|
||
|
|
* The function will not alter any source clocks (ie, main systen clock)
|
||
|
|
* that may use the PLL, so these should be setup prior to and after
|
||
|
|
* exiting the function.
|
||
|
|
*/
|
||
|
|
void CLOCK_SetupSystemPLLMult(uint32_t multiply_by, uint32_t input_freq)
|
||
|
|
{
|
||
|
|
uint32_t cco_freq = input_freq * multiply_by;
|
||
|
|
uint32_t pdec = 1U;
|
||
|
|
uint32_t selr;
|
||
|
|
uint32_t seli;
|
||
|
|
uint32_t selp;
|
||
|
|
uint32_t mdec, ndec;
|
||
|
|
|
||
|
|
uint32_t directo = SYSCON_SYSPLLCTRL_DIRECTO(1);
|
||
|
|
|
||
|
|
while (cco_freq < 75000000U)
|
||
|
|
{
|
||
|
|
multiply_by <<= 1U; /* double value in each iteration */
|
||
|
|
pdec <<= 1U; /* correspondingly double pdec to cancel effect of double msel */
|
||
|
|
cco_freq = input_freq * multiply_by;
|
||
|
|
}
|
||
|
|
selr = 0U;
|
||
|
|
if (multiply_by < 60U)
|
||
|
|
{
|
||
|
|
seli = (multiply_by & 0x3cU) + 4U;
|
||
|
|
selp = (multiply_by >> 1U) + 1U;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
selp = 31U;
|
||
|
|
if (multiply_by > 16384U)
|
||
|
|
{
|
||
|
|
seli = 1U;
|
||
|
|
}
|
||
|
|
else if (multiply_by > 8192U)
|
||
|
|
{
|
||
|
|
seli = 2U;
|
||
|
|
}
|
||
|
|
else if (multiply_by > 2048U)
|
||
|
|
{
|
||
|
|
seli = 4U;
|
||
|
|
}
|
||
|
|
else if (multiply_by >= 501U)
|
||
|
|
{
|
||
|
|
seli = 8U;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
seli = 4U * (1024U / (multiply_by + 9U));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (pdec > 1U)
|
||
|
|
{
|
||
|
|
directo = 0U; /* use post divider */
|
||
|
|
pdec = pdec / 2U; /* Account for minus 1 encoding */
|
||
|
|
/* Translate P value */
|
||
|
|
switch (pdec)
|
||
|
|
{
|
||
|
|
case 1U:
|
||
|
|
pdec = 0x62U; /* 1 * 2 */
|
||
|
|
break;
|
||
|
|
case 2U:
|
||
|
|
pdec = 0x42U; /* 2 * 2 */
|
||
|
|
break;
|
||
|
|
case 4U:
|
||
|
|
pdec = 0x02U; /* 4 * 2 */
|
||
|
|
break;
|
||
|
|
case 8U:
|
||
|
|
pdec = 0x0bU; /* 8 * 2 */
|
||
|
|
break;
|
||
|
|
case 16U:
|
||
|
|
pdec = 0x11U; /* 16 * 2 */
|
||
|
|
break;
|
||
|
|
case 32U:
|
||
|
|
pdec = 0x08U; /* 32 * 2 */
|
||
|
|
break;
|
||
|
|
default:
|
||
|
|
pdec = 0x08U;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
mdec = PLL_SSCG0_MDEC_VAL_SET(pllEncodeM(multiply_by));
|
||
|
|
ndec = 0x302U; /* pre divide by 1 (hardcoded) */
|
||
|
|
|
||
|
|
SYSCON->SYSPLLCTRL = SYSCON_SYSPLLCTRL_BANDSEL(1) | directo | SYSCON_SYSPLLCTRL_BYPASSCCODIV2(1) |
|
||
|
|
(selr << SYSCON_SYSPLLCTRL_SELR_SHIFT) | (seli << SYSCON_SYSPLLCTRL_SELI_SHIFT) |
|
||
|
|
(selp << SYSCON_SYSPLLCTRL_SELP_SHIFT);
|
||
|
|
SYSCON->SYSPLLPDEC = pdec | (1UL << 7U); /* set Pdec value and assert preq */
|
||
|
|
SYSCON->SYSPLLNDEC = ndec | (1UL << 10U); /* set Pdec value and assert preq */
|
||
|
|
SYSCON->SYSPLLSSCTRL0 =
|
||
|
|
(1UL << 18U) | (1UL << 17U) | mdec; /* select non sscg MDEC value, assert mreq and select mdec value */
|
||
|
|
}
|
||
|
|
bool CLOCK_EnableUsbfs0Clock(clock_usb_src_t src, uint32_t freq)
|
||
|
|
{
|
||
|
|
bool ret = true;
|
||
|
|
|
||
|
|
CLOCK_DisableClock(kCLOCK_Usbd0);
|
||
|
|
|
||
|
|
if (kCLOCK_UsbSrcFro == src)
|
||
|
|
{
|
||
|
|
switch (freq)
|
||
|
|
{
|
||
|
|
case 96000000U:
|
||
|
|
CLOCK_SetClkDiv(kCLOCK_DivUsbClk, 2, false); /*!< Div by 2 to get 48MHz, no divider reset */
|
||
|
|
break;
|
||
|
|
case 48000000U:
|
||
|
|
CLOCK_SetClkDiv(kCLOCK_DivUsbClk, 1, false); /*!< Div by 1 to get 48MHz, no divider reset */
|
||
|
|
break;
|
||
|
|
default:
|
||
|
|
ret = false;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
/* Turn ON FRO HF and let it adjust TRIM value based on USB SOF */
|
||
|
|
SYSCON->FROCTRL = (SYSCON->FROCTRL & ~((0x01UL << 15U) | (0xFUL << 26U))) | SYSCON_FROCTRL_HSPDCLK_MASK |
|
||
|
|
SYSCON_FROCTRL_USBCLKADJ_MASK;
|
||
|
|
/* select FRO 96 or 48 MHz */
|
||
|
|
CLOCK_AttachClk(kFRO_HF_to_USB_CLK);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
/*TODO , we only implement FRO as usb clock source*/
|
||
|
|
ret = false;
|
||
|
|
}
|
||
|
|
|
||
|
|
CLOCK_EnableClock(kCLOCK_Usbd0);
|
||
|
|
|
||
|
|
return ret;
|
||
|
|
}
|