blob: 04a62ce0188c201fd952cd223670ae6826728842 [file] [log] [blame]
/*
* Copyright 2017 - 2019 NXP
* All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "fsl_common.h"
#include "fsl_clock.h"
/*******************************************************************************
* Definitions
******************************************************************************/
/* Component ID definition, used by tools. */
#ifndef FSL_COMPONENT_ID
#define FSL_COMPONENT_ID "platform.drivers.clock"
#endif
/*! @brief SSCG PLL FLITER range value */
#define SSCG_PLL1_FILTER_RANGE (35000000U)
/*******************************************************************************
* Prototypes
******************************************************************************/
/*******************************************************************************
* Variables
******************************************************************************/
/*******************************************************************************
* Code
******************************************************************************/
/*!
* brief Gets the clock frequency for a specific clock name.
*
* This function checks the current clock configurations and then calculates
* the clock frequency for a specific clock name defined in clock_name_t.
*
* param clockName Clock names defined in clock_name_t
* return Clock frequency value in hertz
*/
uint32_t CLOCK_GetFreq(clock_name_t clockName)
{
uint32_t freq;
switch (clockName)
{
case kCLOCK_CoreM4Clk:
freq = CLOCK_GetCoreM4Freq();
break;
case kCLOCK_AxiClk:
freq = CLOCK_GetAxiFreq();
break;
case kCLOCK_AhbClk:
freq = CLOCK_GetAhbFreq();
break;
case kCLOCK_IpgClk:
freq = CLOCK_GetAhbFreq();
break;
default:
freq = 0U;
break;
}
return freq;
}
/*!
* brief Get the CCM Cortex M4 core frequency.
*
* return Clock frequency; If the clock is invalid, returns 0.
*/
uint32_t CLOCK_GetCoreM4Freq(void)
{
uint32_t freq;
uint32_t pre = CLOCK_GetRootPreDivider(kCLOCK_RootM4);
uint32_t post = CLOCK_GetRootPostDivider(kCLOCK_RootM4);
switch (CLOCK_GetRootMux(kCLOCK_RootM4))
{
case (uint32_t)kCLOCK_M4RootmuxOsc25m:
freq = OSC25M_CLK_FREQ;
break;
case (uint32_t)kCLOCK_M4RootmuxSysPll2Div5:
freq = CLOCK_GetPllFreq(kCLOCK_SystemPll2Ctrl) / 5U;
break;
case (uint32_t)kCLOCK_M4RootmuxSysPll2Div4:
freq = CLOCK_GetPllFreq(kCLOCK_SystemPll2Ctrl) / 4U;
break;
case (uint32_t)kCLOCK_M4RootmuxSysPll1Div3:
freq = CLOCK_GetPllFreq(kCLOCK_SystemPll1Ctrl) / 3U;
break;
case (uint32_t)kCLOCK_M4RootmuxSysPll1:
freq = CLOCK_GetPllFreq(kCLOCK_SystemPll1Ctrl);
break;
case (uint32_t)kCLOCK_M4RootmuxAudioPll1:
freq = CLOCK_GetPllFreq(kCLOCK_AudioPll1Ctrl);
break;
case (uint32_t)kCLOCK_M4RootmuxVideoPll1:
freq = CLOCK_GetPllFreq(kCLOCK_VideoPll1Ctrl);
break;
case (uint32_t)kCLOCK_M4RootmuxSysPll3:
freq = CLOCK_GetPllFreq(kCLOCK_SystemPll3Ctrl);
break;
default:
assert(false);
break;
}
return freq / pre / post;
}
/*!
* brief Get the CCM Axi bus frequency.
*
* return Clock frequency; If the clock is invalid, returns 0.
*/
uint32_t CLOCK_GetAxiFreq(void)
{
uint32_t freq;
uint32_t pre = CLOCK_GetRootPreDivider(kCLOCK_RootAxi);
uint32_t post = CLOCK_GetRootPostDivider(kCLOCK_RootAxi);
switch (CLOCK_GetRootMux(kCLOCK_RootAxi))
{
case (uint32_t)kCLOCK_AxiRootmuxOsc25m:
freq = OSC25M_CLK_FREQ;
break;
case (uint32_t)kCLOCK_AxiRootmuxSysPll2Div3:
freq = CLOCK_GetPllFreq(kCLOCK_SystemPll2Ctrl) / 3U;
break;
case (uint32_t)kCLOCK_AxiRootmuxSysPll2Div4:
freq = CLOCK_GetPllFreq(kCLOCK_SystemPll2Ctrl) / 4U;
break;
case (uint32_t)kCLOCK_AxiRootmuxSysPll2:
freq = CLOCK_GetPllFreq(kCLOCK_SystemPll2Ctrl);
break;
case (uint32_t)kCLOCK_AxiRootmuxAudioPll1:
freq = CLOCK_GetPllFreq(kCLOCK_AudioPll1Ctrl);
break;
case (uint32_t)kCLOCK_AxiRootmuxVideoPll1:
freq = CLOCK_GetPllFreq(kCLOCK_VideoPll1Ctrl);
break;
case (uint32_t)kCLOCK_AxiRootmuxSysPll1Div8:
freq = CLOCK_GetPllFreq(kCLOCK_SystemPll1Ctrl) / 8U;
break;
case (uint32_t)kCLOCK_AxiRootmuxSysPll1:
freq = CLOCK_GetPllFreq(kCLOCK_SystemPll1Ctrl);
break;
default:
assert(false);
break;
}
return freq / pre / post;
}
/*!
* brief Get the CCM Ahb bus frequency.
*
* return Clock frequency; If the clock is invalid, returns 0.
*/
uint32_t CLOCK_GetAhbFreq(void)
{
uint32_t freq;
uint32_t pre = CLOCK_GetRootPreDivider(kCLOCK_RootAhb);
uint32_t post = CLOCK_GetRootPostDivider(kCLOCK_RootAhb);
switch (CLOCK_GetRootMux(kCLOCK_RootAhb))
{
case (uint32_t)kCLOCK_AhbRootmuxOsc25m:
freq = OSC25M_CLK_FREQ;
break;
case (uint32_t)kCLOCK_AhbRootmuxSysPll1Div6:
freq = CLOCK_GetPllFreq(kCLOCK_SystemPll1Ctrl) / 6U;
break;
case (uint32_t)kCLOCK_AhbRootmuxSysPll1Div2:
freq = CLOCK_GetPllFreq(kCLOCK_SystemPll1Ctrl) / 2U;
break;
case (uint32_t)kCLOCK_AhbRootmuxSysPll1:
freq = CLOCK_GetPllFreq(kCLOCK_SystemPll1Ctrl);
break;
case (uint32_t)kCLOCK_AhbRootmuxSysPll2Div8:
freq = CLOCK_GetPllFreq(kCLOCK_SystemPll2Ctrl) / 8U;
break;
case (uint32_t)kCLOCK_AhbRootmuxSysPll3:
freq = CLOCK_GetPllFreq(kCLOCK_SystemPll3Ctrl);
break;
case (uint32_t)kCLOCK_AhbRootmuxAudioPll1:
freq = CLOCK_GetPllFreq(kCLOCK_AudioPll1Ctrl);
break;
case (uint32_t)kCLOCK_AhbRootmuxVideoPll1:
freq = CLOCK_GetPllFreq(kCLOCK_VideoPll1Ctrl);
break;
default:
assert(false);
break;
}
return freq / pre / post;
}
/*!
* brief Gets PLL reference clock frequency.
*
* param type fractional pll type.
* return Clock frequency
*/
uint32_t CLOCK_GetPllRefClkFreq(clock_pll_ctrl_t ctrl)
{
uint32_t refClkFreq = 0U;
uint8_t clkSel = 0U;
if (ctrl <= kCLOCK_ArmPllCtrl)
{
clkSel = (uint8_t)CCM_BIT_FIELD_EXTRACTION(CCM_ANALOG_TUPLE_REG(CCM_ANALOG, ctrl),
CCM_ANALOG_AUDIO_PLL1_CFG0_PLL_REFCLK_SEL_MASK,
CCM_ANALOG_AUDIO_PLL1_CFG0_PLL_REFCLK_SEL_SHIFT);
}
else
{
clkSel = (uint8_t)(CCM_ANALOG_TUPLE_REG(CCM_ANALOG, ctrl) & CCM_ANALOG_SYS_PLL1_CFG0_PLL_REFCLK_SEL_MASK);
}
switch (clkSel)
{
case (uint8_t)kANALOG_PllRefOsc25M:
refClkFreq = OSC25M_CLK_FREQ /
(CCM_BIT_FIELD_EXTRACTION(XTALOSC->OSC25M_CTL_CFG, XTALOSC_OSC25M_CTL_CFG_OSC_DIV_MASK,
XTALOSC_OSC25M_CTL_CFG_OSC_DIV_SHIFT) +
1U);
break;
case (uint8_t)kANALOG_PllRefOsc27M:
refClkFreq = OSC27M_CLK_FREQ /
(CCM_BIT_FIELD_EXTRACTION(XTALOSC->OSC27M_CTL_CFG, XTALOSC_OSC27M_CTL_CFG_OSC_DIV_MASK,
XTALOSC_OSC27M_CTL_CFG_OSC_DIV_SHIFT) +
1U);
break;
case (uint8_t)kANALOG_PllRefOscHdmiPhy27M:
refClkFreq = HDMI_PHY_27M_FREQ;
break;
case (uint8_t)kANALOG_PllRefClkPN:
refClkFreq = CLKPN_FREQ;
break;
default:
assert(false);
break;
}
return refClkFreq;
}
/*!
* brief Gets PLL clock frequency.
*
* param type fractional pll type.
* return Clock frequency
*/
uint32_t CLOCK_GetPllFreq(clock_pll_ctrl_t pll)
{
uint32_t pllFreq = 0U;
uint32_t pllRefFreq = 0U;
bool sscgPll1Bypass = false;
bool sscgPll2Bypass = false;
bool fracPllBypass = false;
pllRefFreq = CLOCK_GetPllRefClkFreq(pll);
switch (pll)
{
/* SSCG PLL frequency */
case kCLOCK_SystemPll1Ctrl:
sscgPll1Bypass = CLOCK_IsPllBypassed(CCM_ANALOG, kCLOCK_SysPll1InternalPll1BypassCtrl);
sscgPll2Bypass = CLOCK_IsPllBypassed(CCM_ANALOG, kCLOCK_SysPll1InternalPll2BypassCtrl);
break;
case kCLOCK_SystemPll2Ctrl:
sscgPll1Bypass = CLOCK_IsPllBypassed(CCM_ANALOG, kCLOCK_SysPll2InternalPll1BypassCtrl);
sscgPll2Bypass = CLOCK_IsPllBypassed(CCM_ANALOG, kCLOCK_SysPll2InternalPll2BypassCtrl);
break;
case kCLOCK_SystemPll3Ctrl:
sscgPll1Bypass = CLOCK_IsPllBypassed(CCM_ANALOG, kCLOCK_SysPll3InternalPll1BypassCtrl);
sscgPll2Bypass = CLOCK_IsPllBypassed(CCM_ANALOG, kCLOCK_SysPll3InternalPll2BypassCtrl);
break;
case kCLOCK_VideoPll2Ctrl:
sscgPll1Bypass = CLOCK_IsPllBypassed(CCM_ANALOG, kCLOCK_VideoPll2InternalPll1BypassCtrl);
sscgPll2Bypass = CLOCK_IsPllBypassed(CCM_ANALOG, kCLOCK_VideoPll2InternalPll2BypassCtrl);
break;
case kCLOCK_DramPllCtrl:
sscgPll1Bypass = CLOCK_IsPllBypassed(CCM_ANALOG, kCLOCK_DramPllInternalPll1BypassCtrl);
sscgPll2Bypass = CLOCK_IsPllBypassed(CCM_ANALOG, kCLOCK_DramPllInternalPll2BypassCtrl);
break;
case kCLOCK_AudioPll1Ctrl:
fracPllBypass = CLOCK_IsPllBypassed(CCM_ANALOG, kCLOCK_AudioPll1BypassCtrl);
break;
case kCLOCK_AudioPll2Ctrl:
fracPllBypass = CLOCK_IsPllBypassed(CCM_ANALOG, kCLOCK_AudioPll2BypassCtrl);
break;
case kCLOCK_VideoPll1Ctrl:
fracPllBypass = CLOCK_IsPllBypassed(CCM_ANALOG, kCLOCK_VideoPll1BypassCtrl);
break;
case kCLOCK_GpuPllCtrl:
fracPllBypass = CLOCK_IsPllBypassed(CCM_ANALOG, kCLOCK_GpuPLLPwrBypassCtrl);
break;
case kCLOCK_VpuPllCtrl:
fracPllBypass = CLOCK_IsPllBypassed(CCM_ANALOG, kCLOCK_VpuPllPwrBypassCtrl);
break;
case kCLOCK_ArmPllCtrl:
fracPllBypass = CLOCK_IsPllBypassed(CCM_ANALOG, kCLOCK_ArmPllPwrBypassCtrl);
break;
default:
assert(false);
break;
}
if (pll <= kCLOCK_ArmPllCtrl)
{
if (fracPllBypass)
{
pllFreq = pllRefFreq;
}
else
{
pllFreq = CLOCK_GetFracPllFreq(CCM_ANALOG, pll, pllRefFreq);
}
}
else
{
if (sscgPll2Bypass)
{
/* if PLL2 is bypass, return reference clock directly */
pllFreq = pllRefFreq;
}
else
{
pllFreq = CLOCK_GetSSCGPllFreq(CCM_ANALOG, pll, pllRefFreq, sscgPll1Bypass);
}
}
return pllFreq;
}
/*!
* brief Initializes the ANALOG ARM PLL.
*
* param config Pointer to the configuration structure(see ref ccm_analog_frac_pll_config_t enumeration).
*
* note This function can't detect whether the Arm PLL has been enabled and
* used by some IPs.
*/
void CLOCK_InitArmPll(const ccm_analog_frac_pll_config_t *config)
{
assert(config);
/* Disable PLL bypass */
CLOCK_SetPllBypass(CCM_ANALOG, kCLOCK_ArmPllPwrBypassCtrl, false);
/* Fractional pll configuration */
CLOCK_InitFracPll(CCM_ANALOG, config, kCLOCK_ArmPllCtrl);
/* Enable and power up PLL clock. */
CLOCK_EnableAnalogClock(CCM_ANALOG, kCLOCK_ArmPllClke);
/* Wait for PLL to be locked. */
while (!CLOCK_IsPllLocked(CCM_ANALOG, kCLOCK_ArmPllCtrl))
{
}
}
/*!
* brief De-initialize the ARM PLL.
*/
void CLOCK_DeinitArmPll(void)
{
CLOCK_PowerDownPll(CCM_ANALOG, kCLOCK_ArmPllCtrl);
}
/*!
* brief Initializes the ANALOG AUDIO PLL1.
*
* param config Pointer to the configuration structure(see ref ccm_analog_frac_pll_config_t enumeration).
*
* note This function can't detect whether the AUDIO PLL has been enabled and
* used by some IPs.
*/
void CLOCK_InitAudioPll1(const ccm_analog_frac_pll_config_t *config)
{
assert(config);
/* Disable PLL bypass */
CLOCK_SetPllBypass(CCM_ANALOG, kCLOCK_AudioPll1BypassCtrl, false);
/* Fractional pll configuration */
CLOCK_InitFracPll(CCM_ANALOG, config, kCLOCK_AudioPll1Ctrl);
/* Enable and power up PLL clock. */
CLOCK_EnableAnalogClock(CCM_ANALOG, kCLOCK_AudioPll1Clke);
/* Wait for PLL to be locked. */
while (!CLOCK_IsPllLocked(CCM_ANALOG, kCLOCK_AudioPll1Ctrl))
{
}
}
/*!
* brief De-initialize the Audio PLL1.
*/
void CLOCK_DeinitAudioPll1(void)
{
CLOCK_PowerDownPll(CCM_ANALOG, kCLOCK_AudioPll1Ctrl);
}
/*!
* brief Initializes the ANALOG AUDIO PLL2.
*
* param config Pointer to the configuration structure(see ref ccm_analog_frac_pll_config_t enumeration).
*
* note This function can't detect whether the AUDIO PLL has been enabled and
* used by some IPs.
*/
void CLOCK_InitAudioPll2(const ccm_analog_frac_pll_config_t *config)
{
assert(config);
/* Disable PLL bypass */
CLOCK_SetPllBypass(CCM_ANALOG, kCLOCK_AudioPll2BypassCtrl, false);
/* Fractional pll configuration */
CLOCK_InitFracPll(CCM_ANALOG, config, kCLOCK_AudioPll2Ctrl);
/* Enable and power up PLL clock. */
CLOCK_EnableAnalogClock(CCM_ANALOG, kCLOCK_AudioPll2Clke);
/* Wait for PLL to be locked. */
while (!CLOCK_IsPllLocked(CCM_ANALOG, kCLOCK_AudioPll2Ctrl))
{
}
}
/*!
* brief De-initialize the Audio PLL2.
*/
void CLOCK_DeinitAudioPll2(void)
{
CLOCK_PowerDownPll(CCM_ANALOG, kCLOCK_AudioPll2Ctrl);
}
/*!
* brief Initializes the ANALOG VIDEO PLL1.
*
* param config Pointer to the configuration structure(see ref ccm_analog_frac_pll_config_t enumeration).
*
*/
void CLOCK_InitVideoPll1(const ccm_analog_frac_pll_config_t *config)
{
assert(config);
/* Disable PLL bypass */
CLOCK_SetPllBypass(CCM_ANALOG, kCLOCK_VideoPll1BypassCtrl, false);
/* Fractional pll configuration */
CLOCK_InitFracPll(CCM_ANALOG, config, kCLOCK_VideoPll1Ctrl);
/* Enable and power up PLL clock. */
CLOCK_EnableAnalogClock(CCM_ANALOG, kCLOCK_VideoPll1Clke);
/* Wait for PLL to be locked. */
while (!CLOCK_IsPllLocked(CCM_ANALOG, kCLOCK_VideoPll1Ctrl))
{
}
}
/*!
* brief De-initialize the Video PLL1.
*/
void CLOCK_DeinitVideoPll1(void)
{
CLOCK_PowerDownPll(CCM_ANALOG, kCLOCK_VideoPll1Ctrl);
}
/*!
* brief Initializes the ANALOG SYS PLL1.
*
* param config Pointer to the configuration structure(see ref ccm_analog_sscg_pll_config_t enumeration).
*
* note This function can't detect whether the SYS PLL has been enabled and
* used by some IPs.
*/
void CLOCK_InitSysPll1(const ccm_analog_sscg_pll_config_t *config)
{
assert(config);
/* SSCG PLL configuration */
CLOCK_InitSSCGPll(CCM_ANALOG, config, kCLOCK_SystemPll1Ctrl);
/* Disable PLL bypass */
CLOCK_SetPllBypass(CCM_ANALOG, kCLOCK_SysPll1InternalPll1BypassCtrl, false);
CLOCK_SetPllBypass(CCM_ANALOG, kCLOCK_SysPll1InternalPll2BypassCtrl, false);
/* Enable and power up PLL clock. */
CLOCK_EnableAnalogClock(CCM_ANALOG, kCLOCK_SystemPll1Clke);
/* Wait for PLL to be locked. */
while (!CLOCK_IsPllLocked(CCM_ANALOG, kCLOCK_SystemPll1Ctrl))
{
}
}
/*!
* brief De-initialize the System PLL1.
*/
void CLOCK_DeinitSysPll1(void)
{
CLOCK_PowerDownPll(CCM_ANALOG, kCLOCK_SystemPll1Ctrl);
}
/*!
* brief Initializes the ANALOG SYS PLL2.
*
* param config Pointer to the configuration structure(see ref ccm_analog_sscg_pll_config_t enumeration).
*
* note This function can't detect whether the SYS PLL has been enabled and
* used by some IPs.
*/
void CLOCK_InitSysPll2(const ccm_analog_sscg_pll_config_t *config)
{
assert(config);
/* SSCG PLL configuration */
CLOCK_InitSSCGPll(CCM_ANALOG, config, kCLOCK_SystemPll2Ctrl);
/* Disable PLL bypass */
CLOCK_SetPllBypass(CCM_ANALOG, kCLOCK_SysPll2InternalPll1BypassCtrl, false);
CLOCK_SetPllBypass(CCM_ANALOG, kCLOCK_SysPll2InternalPll2BypassCtrl, false);
/* Enable and power up PLL clock. */
CLOCK_EnableAnalogClock(CCM_ANALOG, kCLOCK_SystemPll2Clke);
/* Wait for PLL to be locked. */
while (!CLOCK_IsPllLocked(CCM_ANALOG, kCLOCK_SystemPll2Ctrl))
{
}
}
/*!
* brief De-initialize the System PLL2.
*/
void CLOCK_DeinitSysPll2(void)
{
CLOCK_PowerDownPll(CCM_ANALOG, kCLOCK_SystemPll2Ctrl);
}
/*!
* brief Initializes the ANALOG SYS PLL3.
*
* param config Pointer to the configuration structure(see ref ccm_analog_sscg_pll_config_t enumeration).
*
* note This function can't detect whether the SYS PLL has been enabled and
* used by some IPs.
*/
void CLOCK_InitSysPll3(const ccm_analog_sscg_pll_config_t *config)
{
assert(config);
/* SSCG PLL configuration */
CLOCK_InitSSCGPll(CCM_ANALOG, config, kCLOCK_SystemPll3Ctrl);
/* Disable PLL bypass */
CLOCK_SetPllBypass(CCM_ANALOG, kCLOCK_SysPll3InternalPll1BypassCtrl, false);
CLOCK_SetPllBypass(CCM_ANALOG, kCLOCK_SysPll3InternalPll2BypassCtrl, false);
/* Enable and power up PLL clock. */
CLOCK_EnableAnalogClock(CCM_ANALOG, kCLOCK_SystemPll3Clke);
/* Wait for PLL to be locked. */
while (!CLOCK_IsPllLocked(CCM_ANALOG, kCLOCK_SystemPll3Ctrl))
{
}
}
/*!
* brief De-initialize the System PLL3.
*/
void CLOCK_DeinitSysPll3(void)
{
CLOCK_PowerDownPll(CCM_ANALOG, kCLOCK_SystemPll3Ctrl);
}
/*!
* brief Initializes the ANALOG DDR PLL.
*
* param config Pointer to the configuration structure(see ref ccm_analog_sscg_pll_config_t enumeration).
*
* note This function can't detect whether the DDR PLL has been enabled and
* used by some IPs.
*/
void CLOCK_InitDramPll(const ccm_analog_sscg_pll_config_t *config)
{
assert(config);
/* init SSCG pll */
CLOCK_InitSSCGPll(CCM_ANALOG, config, kCLOCK_DramPllCtrl);
/* Disable PLL bypass */
CLOCK_SetPllBypass(CCM_ANALOG, kCLOCK_DramPllInternalPll1BypassCtrl, false);
CLOCK_SetPllBypass(CCM_ANALOG, kCLOCK_DramPllInternalPll2BypassCtrl, false);
/* Enable and power up PLL clock. */
CLOCK_EnableAnalogClock(CCM_ANALOG, kCLOCK_DramPllClke);
/* make sure DDR is release from reset, DDR1 should be assigned to special domain first */
/* trigger the DDR1 power up */
GPC->PU_PGC_SW_PUP_REQ |= GPC_PU_PGC_SW_PUP_REQ_DDR1_SW_PUP_REQ_MASK;
/* release DDR1 from reset status */
SRC->DDRC2_RCR = (SRC->DDRC2_RCR & (~(SRC_DDRC2_RCR_DDRC1_PHY_PWROKIN_MASK | SRC_DDRC2_RCR_DDRC1_PHY_RESET_MASK |
SRC_DDRC2_RCR_DDRC2_CORE_RST_MASK | SRC_DDRC2_RCR_DDRC2_PRST_MASK))) |
SRC_DDRC2_RCR_DOM_EN_MASK | SRC_DDRC2_RCR_DOMAIN3_MASK | SRC_DDRC2_RCR_DOMAIN2_MASK |
SRC_DDRC2_RCR_DOMAIN1_MASK | SRC_DDRC2_RCR_DOMAIN0_MASK;
while (!CLOCK_IsPllLocked(CCM_ANALOG, kCLOCK_DramPllCtrl))
{
}
}
/*!
* brief De-initialize the Dram PLL.
*/
void CLOCK_DeinitDramPll(void)
{
CLOCK_PowerDownPll(CCM_ANALOG, kCLOCK_DramPllCtrl);
}
/*!
* brief Initializes the ANALOG VIDEO PLL2.
*
* param config Pointer to the configuration structure(see ref ccm_analog_sscg_pll_config_t enumeration).
*
* note This function can't detect whether the VIDEO PLL has been enabled and
* used by some IPs.
*/
void CLOCK_InitVideoPll2(const ccm_analog_sscg_pll_config_t *config)
{
assert(config);
/* init SSCG pll */
CLOCK_InitSSCGPll(CCM_ANALOG, config, kCLOCK_VideoPll2Ctrl);
/* Disable PLL bypass */
CLOCK_SetPllBypass(CCM_ANALOG, kCLOCK_VideoPll2InternalPll1BypassCtrl, false);
CLOCK_SetPllBypass(CCM_ANALOG, kCLOCK_VideoPll2InternalPll2BypassCtrl, false);
/* Enable and power up PLL clock. */
CLOCK_EnableAnalogClock(CCM_ANALOG, kCLOCK_VideoPll2Clke);
/* Wait for PLL to be locked. */
while (!CLOCK_IsPllLocked(CCM_ANALOG, kCLOCK_VideoPll2Ctrl))
{
}
}
/*!
* brief De-initialize the Video PLL2.
*/
void CLOCK_DeinitVideoPll2(void)
{
CLOCK_PowerDownPll(CCM_ANALOG, kCLOCK_VideoPll2Ctrl);
}
/*!
* brief Initializes the ANALOG Fractional PLL.
*
* param base CCM ANALOG base address.
* param config Pointer to the configuration structure(see ref ccm_analog_frac_pll_config_t enumeration).
* param type fractional pll type.
*
*/
void CLOCK_InitFracPll(CCM_ANALOG_Type *base, const ccm_analog_frac_pll_config_t *config, clock_pll_ctrl_t type)
{
assert(config);
assert((config->refDiv != 0U) && (config->outDiv != 0U));
assert((config->outDiv % 2) == 0U);
assert(type <= kCLOCK_ArmPllCtrl);
uint32_t fracCfg0 = CCM_ANALOG_TUPLE_REG_OFF(base, type, 0U) | CCM_ANALOG_AUDIO_PLL1_CFG0_PLL_PD_MASK;
uint32_t fracCfg1 = CCM_ANALOG_TUPLE_REG_OFF(base, type, 4U);
/* power down the fractional PLL first */
CCM_ANALOG_TUPLE_REG_OFF(base, type, 0U) = fracCfg0;
CCM_ANALOG_TUPLE_REG_OFF(base, type, 0U) =
(fracCfg0 &
(~(CCM_ANALOG_AUDIO_PLL1_CFG0_PLL_OUTPUT_DIV_VAL_MASK | CCM_ANALOG_AUDIO_PLL1_CFG0_PLL_REFCLK_DIV_VAL_MASK |
CCM_ANALOG_AUDIO_PLL1_CFG0_PLL_REFCLK_SEL_MASK | CCM_ANALOG_AUDIO_PLL1_CFG0_PLL_NEWDIV_VAL_MASK))) |
(CCM_ANALOG_AUDIO_PLL1_CFG0_PLL_OUTPUT_DIV_VAL((uint32_t)(config->outDiv) / 2U - 1U)) |
(CCM_ANALOG_AUDIO_PLL1_CFG0_PLL_REFCLK_DIV_VAL((uint32_t)(config->refDiv) - 1U)) |
CCM_ANALOG_AUDIO_PLL1_CFG0_PLL_REFCLK_SEL(config->refSel);
CCM_ANALOG_TUPLE_REG_OFF(base, type, 4U) =
(fracCfg1 &
(~(CCM_ANALOG_AUDIO_PLL1_CFG1_PLL_INT_DIV_CTL_MASK | CCM_ANALOG_AUDIO_PLL1_CFG1_PLL_FRAC_DIV_CTL_MASK))) |
CCM_ANALOG_AUDIO_PLL1_CFG1_PLL_INT_DIV_CTL(config->intDiv) |
CCM_ANALOG_AUDIO_PLL1_CFG1_PLL_FRAC_DIV_CTL(config->fractionDiv);
/* NEW_DIV_VAL */
CCM_ANALOG_TUPLE_REG_OFF(base, type, 0U) |= CCM_ANALOG_AUDIO_PLL1_CFG0_PLL_NEWDIV_VAL_MASK;
/* power up the fractional pll */
CCM_ANALOG_TUPLE_REG_OFF(base, type, 0U) &= ~CCM_ANALOG_AUDIO_PLL1_CFG0_PLL_PD_MASK;
/* need to check NEW_DIV_ACK */
while ((CCM_ANALOG_TUPLE_REG_OFF(base, type, 0U) & CCM_ANALOG_AUDIO_PLL1_CFG0_PLL_NEWDIV_ACK_MASK) == 0U)
{
}
}
/*!
* brief Gets the ANALOG Fractional PLL clock frequency.
*
* param base CCM_ANALOG base pointer.
* param type fractional pll type.
* param fractional pll reference clock frequency
*
* return Clock frequency
*/
uint32_t CLOCK_GetFracPllFreq(CCM_ANALOG_Type *base, clock_pll_ctrl_t type, uint32_t refClkFreq)
{
assert(type <= kCLOCK_ArmPllCtrl);
uint32_t fracCfg0 = CCM_ANALOG_TUPLE_REG_OFF(base, type, 0U);
uint32_t fracCfg1 = CCM_ANALOG_TUPLE_REG_OFF(base, type, 4U);
uint64_t fracClk = 0U;
uint8_t refDiv = (uint8_t)CCM_BIT_FIELD_EXTRACTION(fracCfg0, CCM_ANALOG_AUDIO_PLL1_CFG0_PLL_REFCLK_DIV_VAL_MASK,
CCM_ANALOG_AUDIO_PLL1_CFG0_PLL_REFCLK_DIV_VAL_SHIFT);
uint8_t outDiv = (uint8_t)CCM_BIT_FIELD_EXTRACTION(fracCfg0, CCM_ANALOG_AUDIO_PLL1_CFG0_PLL_OUTPUT_DIV_VAL_MASK,
CCM_ANALOG_AUDIO_PLL1_CFG0_PLL_OUTPUT_DIV_VAL_SHIFT);
uint32_t fracDiv = CCM_BIT_FIELD_EXTRACTION(fracCfg1, CCM_ANALOG_AUDIO_PLL1_CFG1_PLL_FRAC_DIV_CTL_MASK,
CCM_ANALOG_AUDIO_PLL1_CFG1_PLL_FRAC_DIV_CTL_SHIFT);
uint8_t intDiv = (uint8_t)CCM_BIT_FIELD_EXTRACTION(fracCfg1, CCM_ANALOG_AUDIO_PLL1_CFG1_PLL_INT_DIV_CTL_MASK,
CCM_ANALOG_AUDIO_PLL1_CFG1_PLL_INT_DIV_CTL_SHIFT);
refClkFreq /= (uint32_t)refDiv + 1UL;
fracClk = (uint64_t)refClkFreq * 8U * (1U + intDiv) + (((uint64_t)refClkFreq * 8U * fracDiv) >> 24U);
return (uint32_t)(fracClk / (((uint64_t)outDiv + 1U) * 2U));
}
/*!
* brief Initializes the ANALOG SSCG PLL.
*
* param base CCM ANALOG base address
* param config Pointer to the configuration structure(see ref ccm_analog_sscg_pll_config_t enumeration).
* param type sscg pll type
*
*/
void CLOCK_InitSSCGPll(CCM_ANALOG_Type *base, const ccm_analog_sscg_pll_config_t *config, clock_pll_ctrl_t type)
{
assert(config);
assert(config->refDiv1 != 0U);
assert(config->refDiv2 != 0U);
assert(config->outDiv != 0U);
assert(config->loopDivider1 != 0U);
assert(config->loopDivider2 != 0U);
assert(type >= kCLOCK_SystemPll1Ctrl);
uint32_t sscgCfg0 = CCM_ANALOG_TUPLE_REG_OFF(base, type, 0U) | CCM_ANALOG_SYS_PLL1_CFG0_PLL_PD_MASK;
uint32_t sscgCfg2 = CCM_ANALOG_TUPLE_REG_OFF(base, type, 8U);
uint32_t pll1Filter = 0U;
/* power down the SSCG PLL first */
CCM_ANALOG_TUPLE_REG_OFF(base, type, 0U) = sscgCfg0;
/* pll mux configuration */
CCM_ANALOG_TUPLE_REG_OFF(base, type, 0U) =
(sscgCfg0 & (~CCM_ANALOG_SYS_PLL1_CFG0_PLL_REFCLK_SEL_MASK)) | config->refSel;
/* reserve CFG1, spread spectrum */
/* match the PLL1 input clock range with PLL filter range */
if ((CLOCK_GetPllRefClkFreq(type) / (config->refDiv1)) > SSCG_PLL1_FILTER_RANGE)
{
pll1Filter = 1U;
}
/* divider configuration */
CCM_ANALOG_TUPLE_REG_OFF(base, type, 8U) =
(sscgCfg2 &
(~(CCM_ANALOG_SYS_PLL1_CFG2_PLL_OUTPUT_DIV_VAL_MASK | CCM_ANALOG_SYS_PLL1_CFG2_PLL_FEEDBACK_DIVF2_MASK |
CCM_ANALOG_SYS_PLL1_CFG2_PLL_FEEDBACK_DIVF1_MASK | CCM_ANALOG_SYS_PLL1_CFG2_PLL_REF_DIVR2_MASK |
CCM_ANALOG_SYS_PLL1_CFG2_PLL_REF_DIVR1_MASK))) |
CCM_ANALOG_SYS_PLL1_CFG2_PLL_OUTPUT_DIV_VAL((uint32_t)(config->outDiv) - 1U) |
CCM_ANALOG_SYS_PLL1_CFG2_PLL_FEEDBACK_DIVF2(config->loopDivider2 - 1U) |
CCM_ANALOG_SYS_PLL1_CFG2_PLL_FEEDBACK_DIVF1(config->loopDivider1 - 1U) |
CCM_ANALOG_SYS_PLL1_CFG2_PLL_REF_DIVR2((uint32_t)(config->refDiv2) - 1U) |
CCM_ANALOG_SYS_PLL1_CFG2_PLL_REF_DIVR1((uint32_t)(config->refDiv1) - 1U) | pll1Filter;
/* power up the SSCG PLL */
CCM_ANALOG_TUPLE_REG_OFF(base, type, 0U) &= ~CCM_ANALOG_SYS_PLL1_CFG0_PLL_PD_MASK;
}
/*!
* brief Get the ANALOG SSCG PLL clock frequency.
*
* param base CCM ANALOG base address.
* param type sscg pll type
* param pll1Bypass pll1 bypass flag
*
* return Clock frequency
*/
uint32_t CLOCK_GetSSCGPllFreq(CCM_ANALOG_Type *base, clock_pll_ctrl_t type, uint32_t refClkFreq, bool pll1Bypass)
{
assert(type >= kCLOCK_SystemPll1Ctrl);
uint32_t sscgCfg1 = CCM_ANALOG_TUPLE_REG_OFF(base, type, 4U);
uint32_t sscgCfg2 = CCM_ANALOG_TUPLE_REG_OFF(base, type, 8U);
uint64_t pll2InputClock = 0U;
uint8_t refDiv1 = (uint8_t)CCM_BIT_FIELD_EXTRACTION(sscgCfg2, CCM_ANALOG_SYS_PLL1_CFG2_PLL_REF_DIVR1_MASK,
CCM_ANALOG_SYS_PLL1_CFG2_PLL_REF_DIVR1_SHIFT) +
1U;
uint8_t refDiv2 = (uint8_t)CCM_BIT_FIELD_EXTRACTION(sscgCfg2, CCM_ANALOG_SYS_PLL1_CFG2_PLL_REF_DIVR2_MASK,
CCM_ANALOG_SYS_PLL1_CFG2_PLL_REF_DIVR2_SHIFT) +
1U;
uint8_t divf1 = (uint8_t)CCM_BIT_FIELD_EXTRACTION(sscgCfg2, CCM_ANALOG_SYS_PLL1_CFG2_PLL_FEEDBACK_DIVF1_MASK,
CCM_ANALOG_SYS_PLL1_CFG2_PLL_FEEDBACK_DIVF1_SHIFT) +
1U;
uint8_t divf2 = (uint8_t)CCM_BIT_FIELD_EXTRACTION(sscgCfg2, CCM_ANALOG_SYS_PLL1_CFG2_PLL_FEEDBACK_DIVF2_MASK,
CCM_ANALOG_SYS_PLL1_CFG2_PLL_FEEDBACK_DIVF2_SHIFT) +
1U;
uint8_t outDiv = (uint8_t)CCM_BIT_FIELD_EXTRACTION(sscgCfg2, CCM_ANALOG_SYS_PLL1_CFG2_PLL_OUTPUT_DIV_VAL_MASK,
CCM_ANALOG_SYS_PLL1_CFG2_PLL_OUTPUT_DIV_VAL_SHIFT) +
1U;
refClkFreq /= refDiv1;
if (pll1Bypass)
{
pll2InputClock = refClkFreq;
}
else if ((sscgCfg1 & CCM_ANALOG_SYS_PLL1_CFG1_PLL_SSE_MASK) != 0U)
{
pll2InputClock = (uint64_t)refClkFreq * 8U * divf1 / refDiv2;
}
else
{
pll2InputClock = (uint64_t)refClkFreq * 2U * divf1 / refDiv2;
}
return (uint32_t)(pll2InputClock * divf2 / outDiv);
}
/*!
* brief Set root clock divider
* Note: The PRE and POST dividers in this function are the actually divider, software will map it to register value
*
* param ccmRootClk Root control (see ref clock_root_control_t enumeration)
* param pre Pre divider value (1-8)
* param post Post divider value (1-64)
*/
void CLOCK_SetRootDivider(clock_root_control_t ccmRootClk, uint32_t pre, uint32_t post)
{
assert((pre <= 8U) && (pre != 0U));
assert((post <= 64U) && (post != 0U));
CCM_REG(ccmRootClk) = (CCM_REG(ccmRootClk) & (~(CCM_TARGET_ROOT_PRE_PODF_MASK | CCM_TARGET_ROOT_POST_PODF_MASK))) |
CCM_TARGET_ROOT_PRE_PODF(pre - 1U) | CCM_TARGET_ROOT_POST_PODF(post - 1U);
}
/*!
* brief Update clock root in one step, for dynamical clock switching
* Note: The PRE and POST dividers in this function are the actually divider, software will map it to register value
*
* param ccmRootClk Root control (see ref clock_root_control_t enumeration)
* param root mux value (see ref _ccm_rootmux_xxx enumeration)
* param pre Pre divider value (0-7, divider=n+1)
* param post Post divider value (0-63, divider=n+1)
*/
void CLOCK_UpdateRoot(clock_root_control_t ccmRootClk, uint32_t mux, uint32_t pre, uint32_t post)
{
assert((pre <= 8U) && (pre != 0U));
assert((post <= 64U) && (post != 0U));
CCM_REG(ccmRootClk) =
(CCM_REG(ccmRootClk) &
(~(CCM_TARGET_ROOT_MUX_MASK | CCM_TARGET_ROOT_PRE_PODF_MASK | CCM_TARGET_ROOT_POST_PODF_MASK))) |
CCM_TARGET_ROOT_MUX(mux) | CCM_TARGET_ROOT_PRE_PODF(pre - 1U) | CCM_TARGET_ROOT_POST_PODF(post - 1U);
}
/*!
* brief OSC25M init
*
* param config osc configuration
*/
void CLOCK_InitOSC25M(const osc_config_t *config)
{
assert(config);
assert(config->oscDiv != 0U);
XTALOSC->OSC25M_CTL_CFG =
(XTALOSC->OSC25M_CTL_CFG & (~(XTALOSC_OSC25M_CTL_CFG_OSC_DIV_MASK | XTALOSC_OSC25M_CTL_CFG_OSC_BYPSS_MASK))) |
XTALOSC_OSC25M_CTL_CFG_OSC_DIV((uint32_t)(config->oscDiv) - 1U) |
XTALOSC_OSC25M_CTL_CFG_OSC_BYPSS(config->oscMode);
CLOCK_EnableAnalogClock(CCM_ANALOG, kCLOCK_OSC25MClke);
}
/*!
* brief OSC25M deinit
*
*/
void CLOCK_DeinitOSC25M(void)
{
CLOCK_DisableAnalogClock(CCM_ANALOG, kCLOCK_OSC25MClke);
}
/*!
* brief OSC27M init
*
*/
void CLOCK_InitOSC27M(const osc_config_t *config)
{
assert(config);
assert(config->oscDiv != 0U);
XTALOSC->OSC27M_CTL_CFG =
(XTALOSC->OSC27M_CTL_CFG & (~(XTALOSC_OSC27M_CTL_CFG_OSC_DIV_MASK | XTALOSC_OSC27M_CTL_CFG_OSC_BYPSS_MASK))) |
XTALOSC_OSC27M_CTL_CFG_OSC_DIV((uint32_t)(config->oscDiv) - 1U) |
XTALOSC_OSC27M_CTL_CFG_OSC_BYPSS(config->oscMode);
CLOCK_EnableAnalogClock(CCM_ANALOG, kCLOCK_OSC27MClke);
}
/*!
* brief OSC27M deinit
*
* param config osc configuration
*/
void CLOCK_DeinitOSC27M(void)
{
CLOCK_DisableAnalogClock(CCM_ANALOG, kCLOCK_OSC27MClke);
}
/*!
* brief Enable CCGR clock gate and root clock gate for each module
* User should set specific gate for each module according to the description
* of the table of system clocks, gating and override in CCM chapter of
* reference manual. Take care of that one module may need to set more than
* one clock gate.
*
* param ccmGate Gate control for each module (see ref clock_ip_name_t enumeration).
*/
void CLOCK_EnableClock(clock_ip_name_t ccmGate)
{
uint32_t ccgr = CCM_TUPLE_CCGR(ccmGate);
uint32_t rootClk = CCM_TUPLE_ROOT(ccmGate);
CCM_REG_SET(ccgr) = (uint32_t)kCLOCK_ClockNeededAll;
/* if root clock is 0xFFFFU, then skip enable root clock */
if (rootClk != 0xFFFFU)
{
CCM_REG_SET(rootClk) = CCM_TARGET_ROOT_SET_ENABLE_MASK;
}
}
/*!
* brief Disable CCGR clock gate for the each module
* User should set specific gate for each module according to the description
* of the table of system clocks, gating and override in CCM chapter of
* reference manual. Take care of that one module may need to set more than
* one clock gate.
*
* param ccmGate Gate control for each module (see ref clock_ip_name_t enumeration).
*/
void CLOCK_DisableClock(clock_ip_name_t ccmGate)
{
uint32_t ccgr = CCM_TUPLE_CCGR(ccmGate);
uint32_t rootClk = CCM_TUPLE_ROOT(ccmGate);
CCM_REG(ccgr) = (uint32_t)kCLOCK_ClockNotNeeded;
/* if root clock is 0xFFFFU, then skip disable root clock */
if (rootClk != 0xFFFFU)
{
CCM_REG_CLR(rootClk) = CCM_TARGET_ROOT_CLR_ENABLE_MASK;
}
}