/*
 * Copyright 2022-2023, 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 NVALMAX (0x100U)
#define PVALMAX (0x20U)
#define MVALMAX (0x10000U)

#define PLL_MAX_N_DIV 0x100U

/*--------------------------------------------------------------------------
!!! If required these #defines can be moved to chip library file
----------------------------------------------------------------------------*/

#define PLL_NDIV_VAL_P (0U) /* NDIV is in bits  7:0 */
#define PLL_NDIV_VAL_M (0xFFUL << PLL_NDIV_VAL_P)
#define PLL_MDIV_VAL_P (0U) /* MDIV is in bits  15:0 */
#define PLL_MDIV_VAL_M (0xFFFFULL << PLL_MDIV_VAL_P)
#define PLL_PDIV_VAL_P (0U) /* PDIV is in bits  4:0 */
#define PLL_PDIV_VAL_M (0x1FUL << PLL_PDIV_VAL_P)

#define PLL_MIN_CCO_FREQ_MHZ (275000000U)
#define PLL_MAX_CCO_FREQ_MHZ (550000000U)
#define PLL_LOWER_IN_LIMIT   (32000U)     /*!<  Minimum PLL input rate */
#define PLL_HIGHER_IN_LIMIT  (150000000U) /*!<  Maximum PLL input rate */
#define PLL_MIN_IN_SSMODE    (3000000U)
#define PLL_MAX_IN_SSMODE \
    (100000000U) /*!<  Not find the value in UM, Just use the maximum frequency which device support */

/* PLL NDIV reg */
#define PLL_NDIV_VAL_SET(value) (((unsigned long)(value) << PLL_NDIV_VAL_P) & PLL_NDIV_VAL_M)
/* PLL MDIV reg */
#define PLL_MDIV_VAL_SET(value) (((unsigned long long)(value) << PLL_MDIV_VAL_P) & PLL_MDIV_VAL_M)
/* PLL PDIV reg */
#define PLL_PDIV_VAL_SET(value) (((unsigned long)(value) << PLL_PDIV_VAL_P) & PLL_PDIV_VAL_M)

/* PLL SSCG control1 */
#define PLL_SSCG_MD_FRACT_P 0U
#define PLL_SSCG_MD_INT_P   25U
#define PLL_SSCG_MD_FRACT_M (0x1FFFFFFUL << PLL_SSCG_MD_FRACT_P)
#define PLL_SSCG_MD_INT_M   ((uint64_t)0xFFUL << PLL_SSCG_MD_INT_P)

#define PLL_SSCG_MD_FRACT_SET(value) (((uint64_t)(value) << PLL_SSCG_MD_FRACT_P) & PLL_SSCG_MD_FRACT_M)
#define PLL_SSCG_MD_INT_SET(value)   (((uint64_t)(value) << PLL_SSCG_MD_INT_P) & PLL_SSCG_MD_INT_M)

/*******************************************************************************
 * Variables
 ******************************************************************************/

/** 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. */
volatile static uint32_t s_Ext_Clk_Freq = 16000000U;
/*! @brief External XTAL32K clock frequency. */
volatile static uint32_t s_Xtal32_Freq = 32768U;
/*! @brief SAI MCLK clock frequency. */
volatile static uint32_t s_Sai_Mclk_Freq[2] = {0U};
/*! @brief SAI TX BCLK clock frequency. */
volatile static uint32_t s_Sai_Tx_Bclk_Freq[2] = {0U};
/*! @brief SAI RX BCLK clock frequency. */
volatile static uint32_t s_Sai_Rx_Bclk_Freq[2] = {0U};

/*! @brief external UPLL clock frequency. */
static uint32_t s_extUpllFreq = 0U;

/*******************************************************************************
 * Prototypes
 ******************************************************************************/
/* Get FRO 12M Clk */
static uint32_t CLOCK_GetFro12MFreq(void);
/* Get CLK 1M Clk */
static uint32_t CLOCK_GetClk1MFreq(void);
/* Get HF FRO Clk */
static uint32_t CLOCK_GetFroHfFreq(void);
/* Get CLK 48M Clk */
static uint32_t CLOCK_GetClk48MFreq(void);
/* Get CLK 144M Clk */
static uint32_t CLOCK_GetClk144MFreq(void);
/* Get CLK 16K Clk */
static uint32_t CLOCK_GetClk16KFreq(uint32_t id);
/* Get EXT OSC Clk */
static uint32_t CLOCK_GetExtClkFreq(void);
/* Get OSC 32K Clk */
static uint32_t CLOCK_GetOsc32KFreq(uint32_t id);
/* Get Systick Clk */
static uint32_t CLOCK_GetSystickClkFreq(uint32_t id);
/* Get CLOCK OUT Clk */
static uint32_t CLOCK_GetClockOutClkFreq(void);
/* Get LP_OSC Clk */
static uint32_t CLOCK_GetLposcFreq(void);

/* Find SELP, SELI, and SELR values for raw M value, max M = MVALMAX */
static void pllFindSel(uint32_t M, uint32_t *pSelP, uint32_t *pSelI, uint32_t *pSelR);
/* Get predivider (N) from PLL0 NDIV setting */
static uint32_t findPll0PreDiv(void);
/* Get predivider (N) from PLL1 NDIV setting */
static uint32_t findPll1PreDiv(void);
/* Get postdivider (P) from PLL0 PDIV setting */
static uint32_t findPll0PostDiv(void);
/* Get postdivider (P) from PLL1 PDIV setting */
static uint32_t findPll1PostDiv(void);
/* Get multiplier (M) from PLL0 MDIV and SSCG settings */
static float findPll0MMult(void);
/* Get multiplier (M) from PLL1 MDIV and SSCG settings */
static float findPll1MMult(void);
/* 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 useSS);
/* Set PLL0 output based on desired output rate */
static pll_error_t CLOCK_GetPllConfigInternal(uint32_t finHz, uint32_t foutHz, pll_setup_t *pSetup, bool useSS);
/* Get PLL input clock rate from setup structure */
static uint32_t CLOCK_GetPLLInClockRateFromSetup(pll_setup_t *pSetup);
/* Get predivider (N) from setup structure */
static uint32_t findPllPreDivFromSetup(pll_setup_t *pSetup);
/* Get postdivider (P) from setup structure */
static uint32_t findPllPostDivFromSetup(pll_setup_t *pSetup);
/* Get multiplier (M) from setup structure */
static float findPllMMultFromSetup(pll_setup_t *pSetup);

/*******************************************************************************
 * Code
 ******************************************************************************/

/**
 * @brief   Initialize the Core clock to given frequency (48 or 144 MHz).
 * This function turns on FIRC and select the given frequency as the source of fro_hf
 * @param   iFreq   : Desired frequency (must be one of CLK_FRO_48MHZ or CLK_FRO_144MHZ)
 * @return  returns success or fail status.
 */
status_t CLOCK_SetupFROHFClocking(uint32_t iFreq)
{
    if ((iFreq != 48000000U) && (iFreq != 144000000U))
    {
        return kStatus_Fail;
    }

    /* Select 48MHz or 144MHz for FIRC clock */
    SCG0->FIRCCFG = SCG_FIRCCFG_RANGE((iFreq == 48000000U) ? 0 : 1);

    /* Unlock FIRCCSR */
    SCG0->FIRCCSR &= ~SCG_FIRCCSR_LK_MASK;

    /* Enable FIRC 48 MHz clock for peripheral use */
    SCG0->FIRCCSR |= SCG_FIRCCSR_FIRC_SCLK_PERIPH_EN_MASK;
    /* Enable FIRC 144 MHz clock for peripheral use */
    SCG0->FIRCCSR |= SCG_FIRCCSR_FIRC_FCLK_PERIPH_EN_MASK;

    /* Enable FIRC */
    SCG0->FIRCCSR |= SCG_FIRCCSR_FIRCEN_MASK;

    /* Wait for FIRC clock to be valid. */
    while ((SCG0->FIRCCSR & SCG_FIRCCSR_FIRCVLD_MASK) == 0U)
    {
    }

    return kStatus_Success;
}

/**
 * @brief   Initialize the external osc clock to given frequency.
 * @param   iFreq   : Desired frequency (must be equal to exact rate in Hz)
 * @return  returns success or fail status.
 */
status_t CLOCK_SetupExtClocking(uint32_t iFreq)
{
    uint8_t range = 0U;

    if ((iFreq >= 16000000U) && (iFreq < 20000000U))
    {
        range = 0U;
    }
    else if ((iFreq >= 20000000U) && (iFreq < 30000000U))
    {
        range = 1U;
    }
    else if ((iFreq >= 30000000U) && (iFreq < 50000000U))
    {
        range = 2U;
    }
    else if ((iFreq >= 50000000U) && (iFreq < 66000000U))
    {
        range = 3U;
    }
    else
    {
        return kStatus_InvalidArgument;
    }

    /* If clock is used by system, return error. */
    if ((SCG0->SOSCCSR & SCG_SOSCCSR_SOSCSEL_MASK) != 0U)
    {
        return (status_t)kStatus_SCG_Busy;
    }

    /* If configure register is locked, return error. */
    if ((SCG0->SOSCCSR & SCG_SOSCCSR_LK_MASK) != 0U)
    {
        return kStatus_ReadOnly;
    }

    /* De-initializes the SCG SOSC */
    SCG0->SOSCCSR = SCG_SOSCCSR_SOSCERR_MASK;

    /* Enable LDO */
    SCG0->LDOCSR |= SCG_LDOCSR_LDOEN_MASK;

    /* Select SOSC source (internal crystal oscillator) and Configure SOSC range */
    SCG0->SOSCCFG = SCG_SOSCCFG_EREFS_MASK | SCG_SOSCCFG_RANGE(range);

    /* Unlock SOSCCSR */
    SCG0->SOSCCSR &= ~SCG_SOSCCSR_LK_MASK;

    /* Enable SOSC clock monitor and Enable SOSC */
    SCG0->SOSCCSR |= (SCG_SOSCCSR_SOSCCM_MASK | SCG_SOSCCSR_SOSCEN_MASK);

    /* Wait for SOSC clock to be valid. */
    while ((SCG0->SOSCCSR & SCG_SOSCCSR_SOSCVLD_MASK) == 0U)
    {
    }

    s_Ext_Clk_Freq = iFreq;

    return kStatus_Success;
}

/**
 * @brief   Initialize the external reference clock to given frequency.
 * @param   iFreq   : Desired frequency (must be equal to exact rate in Hz)
 * @return  returns success or fail status.
 */
status_t CLOCK_SetupExtRefClocking(uint32_t iFreq)
{
    uint8_t range = 0U;

    if ((iFreq >= 16000000U) && (iFreq < 20000000U))
    {
        range = 0U;
    }
    else if ((iFreq >= 20000000U) && (iFreq < 30000000U))
    {
        range = 1U;
    }
    else if ((iFreq >= 30000000U) && (iFreq < 50000000U))
    {
        range = 2U;
    }
    else if ((iFreq >= 50000000U) && (iFreq < 66000000U))
    {
        range = 3U;
    }
    else
    {
        return kStatus_InvalidArgument;
    }

    /* If clock is used by system, return error. */
    if ((SCG0->SOSCCSR & SCG_SOSCCSR_SOSCSEL_MASK) != 0U)
    {
        return (status_t)kStatus_SCG_Busy;
    }

    /* If configure register is locked, return error. */
    if ((SCG0->SOSCCSR & SCG_SOSCCSR_LK_MASK) != 0U)
    {
        return kStatus_ReadOnly;
    }

    /* De-initializes the SCG SOSC */
    SCG0->SOSCCSR = SCG_SOSCCSR_SOSCERR_MASK;

    /* Enable LDO */
    SCG0->LDOCSR |= SCG_LDOCSR_LDOEN_MASK;

    /* Select SOSC source (external reference clock)*/
    SCG0->SOSCCFG &= ~SCG_SOSCCFG_EREFS_MASK;

    /*Configure SOSC range */
    SCG0->SOSCCFG |= SCG_SOSCCFG_RANGE(range);

    /* Unlock SOSCCSR */
    SCG0->SOSCCSR &= ~SCG_SOSCCSR_LK_MASK;

    /* Enable SOSC clock monitor and Enable SOSC */
    SCG0->SOSCCSR |= (SCG_SOSCCSR_SOSCCM_MASK | SCG_SOSCCSR_SOSCEN_MASK);

    /* Wait for SOSC clock to be valid. */
    while ((SCG0->SOSCCSR & SCG_SOSCCSR_SOSCVLD_MASK) == 0U)
    {
    }

    s_Ext_Clk_Freq = iFreq;

    return kStatus_Success;
}

/**
 * @brief   Initialize the OSC 32K.
 * @param   id   : OSC 32 kHz output clock to specified modules
 * @return  returns success or fail status.
 */
status_t CLOCK_SetupOsc32KClocking(uint32_t id)
{
    /* Enable LDO */
    SCG0->LDOCSR |= SCG_LDOCSR_LDOEN_MASK | SCG_LDOCSR_VOUT_OK_MASK;

    VBAT0->OSCCTLA =
        (VBAT0->OSCCTLA & ~(VBAT_OSCCTLA_MODE_EN_MASK | VBAT_OSCCTLA_CAP_SEL_EN_MASK | VBAT_OSCCTLA_OSC_EN_MASK)) |
        VBAT_OSCCTLA_MODE_EN(0x0) | VBAT_OSCCTLA_CAP_SEL_EN_MASK | VBAT_OSCCTLA_OSC_EN_MASK;
    VBAT0->OSCCTLB = VBAT_OSCCTLB_INVERSE(0xFFF7E);
    /* Wait for STATUSA[OSC_RDY] to set. */
    while ((VBAT0->STATUSA & VBAT_STATUSA_OSC_RDY_MASK) == 0U)
    {
    }
    VBAT0->OSCLCKA = VBAT_OSCLCKA_LOCK_MASK;
    VBAT0->OSCLCKB &= ~VBAT_OSCLCKA_LOCK_MASK;

    VBAT0->OSCCLKE |= VBAT_OSCCLKE_CLKE(id);

    /* De-initializes the SCG ROSC */
    SCG0->ROSCCSR = SCG_ROSCCSR_ROSCERR_MASK;

    /* Unlock ROSCCSR */
    SCG0->ROSCCSR &= ~SCG_ROSCCSR_LK_MASK;

    /* Enable SOSC clock monitor and Enable ROSC */
    SCG0->ROSCCSR |= SCG_ROSCCSR_ROSCCM_MASK;

    /* Wait for ROSC clock to be valid. */
    while ((SCG0->ROSCCSR & SCG_ROSCCSR_ROSCVLD_MASK) == 0U)
    {
    }

    s_Xtal32_Freq = 32768U;

    return kStatus_Success;
}

/**
 * @brief   Initialize the CLK16K clock.
 * @param   id   : CLK 16 kHz output clock to specified modules
 * @return  returns success or fail status.
 */
status_t CLOCK_SetupClk16KClocking(uint32_t id)
{
    VBAT0->FROCTLA |= VBAT_FROCTLA_FRO_EN_MASK;
    VBAT0->FROCTLB &= ~VBAT_FROCTLB_INVERSE_MASK;

    VBAT0->FROLCKA |= VBAT_FROLCKA_LOCK_MASK;
    VBAT0->FROLCKB &= ~VBAT_FROLCKB_LOCK_MASK;

    VBAT0->FROCLKE |= VBAT_FROCLKE_CLKE(id);

    return kStatus_Success;
}

/**
 * @brief   Setup FROHF trim.
 * @param   config   : FROHF trim value
 * @return  returns success or fail status.
 */
status_t CLOCK_FROHFTrimConfig(firc_trim_config_t config)
{
    SCG0->FIRCTCFG = SCG_FIRCTCFG_TRIMDIV(config.trimDiv) | SCG_FIRCTCFG_TRIMSRC(config.trimSrc);

    if (kSCG_FircTrimNonUpdate == config.trimMode)
    {
        SCG0->FIRCSTAT = SCG_FIRCSTAT_TRIMFINE(config.trimFine);
    }

    /* Set trim mode. */
    SCG0->FIRCCSR = (uint32_t)config.trimMode;

    if ((SCG0->FIRCCSR & SCG_FIRCCSR_FIRCERR_MASK) == SCG_FIRCCSR_FIRCERR_MASK)
    {
        return (status_t)kStatus_Fail;
    }

    return (status_t)kStatus_Success;
}

/**
 * @brief   Setup FRO 12M trim.
 * @param   config   : FRO 12M trim value
 * @return  returns success or fail status.
 */
status_t CLOCK_FRO12MTrimConfig(sirc_trim_config_t config)
{
    SCG0->SIRCTCFG = SCG_SIRCTCFG_TRIMDIV(config.trimDiv) | SCG_SIRCTCFG_TRIMSRC(config.trimSrc);

    if (kSCG_SircTrimNonUpdate == config.trimMode)
    {
        SCG0->SIRCSTAT = SCG_SIRCSTAT_CCOTRIM(config.cltrim);
        SCG0->SIRCSTAT = SCG_SIRCSTAT_CCOTRIM(config.ccotrim);
    }

    /* Set trim mode. */
    SCG0->SIRCCSR = (uint32_t)config.trimMode;

    if ((SCG0->SIRCCSR & SCG_SIRCCSR_SIRCERR_MASK) == SCG_SIRCCSR_SIRCERR_MASK)
    {
        return (status_t)kStatus_Fail;
    }

    return (status_t)kStatus_Success;
}

/*!
 * @brief Sets the system OSC monitor mode.
 *
 * This function sets the system OSC monitor mode. The mode can be disabled,
 * it can generate an interrupt when the error is disabled, or reset when the error is detected.
 *
 * @param mode Monitor mode to set.
 */
void CLOCK_SetSysOscMonitorMode(scg_sosc_monitor_mode_t mode)
{
    uint32_t reg = SCG0->SOSCCSR;

    reg &= ~(SCG_SOSCCSR_SOSCCM_MASK | SCG_SOSCCSR_SOSCCMRE_MASK);

    reg |= (uint32_t)mode;

    SCG0->SOSCCSR = reg;
}

/*!
 * @brief Sets the ROSC monitor mode.
 *
 * This function sets the ROSC monitor mode. The mode can be disabled,
 * it can generate an interrupt when the error is disabled, or reset when the error is detected.
 *
 * @param mode Monitor mode to set.
 */
void CLOCK_SetRoscMonitorMode(scg_rosc_monitor_mode_t mode)
{
    uint32_t reg = SCG0->ROSCCSR;

    reg &= ~(SCG_ROSCCSR_ROSCCM_MASK | SCG_ROSCCSR_ROSCCMRE_MASK);

    reg |= (uint32_t)mode;

    SCG0->ROSCCSR = reg;
}

/*!
 * @brief Sets the UPLL monitor mode.
 *
 * This function sets the UPLL monitor mode. The mode can be disabled,
 * it can generate an interrupt when the error is disabled, or reset when the error is detected.
 *
 * @param mode Monitor mode to set.
 */
void CLOCK_SetUpllMonitorMode(scg_upll_monitor_mode_t mode)
{
    uint32_t reg = SCG0->UPLLCSR;

    reg &= ~(SCG_UPLLCSR_UPLLCM_MASK | SCG_UPLLCSR_UPLLCMRE_MASK);

    reg |= (uint32_t)mode;

    SCG0->UPLLCSR = reg;
}

/*!
 * @brief Sets the PLL0 monitor mode.
 *
 * This function sets the PLL0 monitor mode. The mode can be disabled,
 * it can generate an interrupt when the error is disabled, or reset when the error is detected.
 *
 * @param mode Monitor mode to set.
 */
void CLOCK_SetPll0MonitorMode(scg_pll0_monitor_mode_t mode)
{
    uint32_t reg = SCG0->APLLCSR;

    reg &= ~(SCG_APLLCSR_APLLCM_MASK | SCG_APLLCSR_APLLCMRE_MASK);

    reg |= (uint32_t)mode;

    SCG0->APLLCSR = reg;
}

/*!
 * @brief Sets the PLL1 monitor mode.
 *
 * This function sets the PLL1 monitor mode. The mode can be disabled,
 * it can generate an interrupt when the error is disabled, or reset when the error is detected.
 *
 * @param mode Monitor mode to set.
 */
void CLOCK_SetPll1MonitorMode(scg_pll1_monitor_mode_t mode)
{
    uint32_t reg = SCG0->SPLLCSR;

    reg &= ~(SCG_SPLLCSR_SPLLCM_MASK | SCG_SPLLCSR_SPLLCMRE_MASK);

    reg |= (uint32_t)mode;

    SCG0->SPLLCSR = reg;
}

/*!
 * @brief	Set the additional number of wait-states added to account for the ratio of system clock period to flash access
 * time during full speed power mode.
 * @param	system_freq_hz	: Input frequency
 * @param	mode	        : Active run mode (voltage level).
 * @return	success or fail status
 */
status_t CLOCK_SetFLASHAccessCyclesForFreq(uint32_t system_freq_hz, run_mode_t mode)
{
    uint32_t num_wait_states_added = 3UL; /* Default 3 additional wait states */
    switch ((uint32_t)mode)
    {
        case (uint32_t)kMD_Mode:
        {
            if (system_freq_hz > 50000000U)
            {
                return kStatus_Fail;
            }
            if (system_freq_hz > 24000000U)
            {
                num_wait_states_added = 1U;
            }
            else
            {
                num_wait_states_added = 0U;
            }
            break;
        }
        case (uint32_t)kSD_Mode:
        {
            if (system_freq_hz > 100000000U)
            {
                return kStatus_Fail;
            }
            if (system_freq_hz > 64000000U)
            {
                num_wait_states_added = 2U;
            }
            else if (system_freq_hz > 36000000U)
            {
                num_wait_states_added = 1U;
            }
            else
            {
                num_wait_states_added = 0U;
            }
            break;
        }
        case (uint32_t)kOD_Mode:
        {
            if (system_freq_hz > 150000000U)
            {
                return kStatus_Fail;
            }
            if (system_freq_hz > 100000000U)
            {
                num_wait_states_added = 3U;
            }
            else if (system_freq_hz > 64000000U)
            {
                num_wait_states_added = 2U;
            }
            else if (system_freq_hz > 36000000U)
            {
                num_wait_states_added = 1U;
            }
            else
            {
                num_wait_states_added = 0U;
            }
            break;
        }
        default:
            num_wait_states_added = 0U;
            break;
    }

    /* additional wait-states are added */
    FMU0->FCTRL = (FMU0->FCTRL & 0xFFFFFFF0UL) | (num_wait_states_added & 0xFUL);

    return kStatus_Success;
}

/*!
 * @brief Config 32k Crystal Oscillator.
 *
 * @param base VBAT peripheral base address.
 * @param config The pointer to the structure \ref vbat_osc_config_t.
 */
void VBAT_SetOscConfig(VBAT_Type *base, const vbat_osc_config_t *config)
{
    uint32_t tmp32;

    if (config->enableCrystalOscillatorBypass == true)
    {
        base->OSCCTLA |= VBAT_OSCCTLA_OSC_BYP_EN_MASK;
        while ((VBAT0->STATUSA & VBAT_STATUSA_OSC_RDY_MASK) == 0U)
        {
        }
    }
    else
    {
        tmp32 = base->OSCCTLA;

        if (config != NULL)
        {
            if (config->enableInternalCapBank)
            {
                tmp32 &= ~(VBAT_OSCCTLA_EXTAL_CAP_SEL_MASK | VBAT_OSCCTLA_XTAL_CAP_SEL_MASK);
                tmp32 |= VBAT_OSCCTLA_EXTAL_CAP_SEL(config->extalCap) | VBAT_OSCCTLA_XTAL_CAP_SEL(config->xtalCap);
                tmp32 |= VBAT_OSCCTLA_CAP_SEL_EN_MASK;
            }
            else
            {
                /* Disable the internal capacitance bank. */
                tmp32 &= ~VBAT_OSCCTLA_CAP_SEL_EN_MASK;
            }

            tmp32 &= ~(VBAT_OSCCTLA_COARSE_AMP_GAIN_MASK);
            tmp32 |= VBAT_OSCCTLA_COARSE_AMP_GAIN(config->coarseAdjustment);
        }
        base->OSCCTLA = tmp32;
        while ((VBAT0->STATUSA & VBAT_STATUSA_OSC_RDY_MASK) == 0U)
        {
        }
    }
}

/* Clock Selection for IP */
/**
 * brief   Configure the clock selection muxes.
 * param   connection  : Clock to be configured.
 * return  Nothing
 */
void CLOCK_AttachClk(clock_attach_id_t connection)
{
    uint16_t mux;
    uint8_t sel;
    uint16_t item;
    uint32_t tmp32 = (uint32_t)connection;
    uint32_t i;
    volatile uint32_t *pClkSel;

    pClkSel = &(SYSCON->SYSTICKCLKSEL0);

    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 = (uint16_t)GET_ID_ITEM_MUX(item);
                sel = (uint8_t)GET_ID_ITEM_SEL(item);
                if (mux == CM_SCGRCCRSCSCLKSEL)
                {
                    SCG0->RCCR = (SCG0->RCCR & ~(SCG_RCCR_SCS_MASK)) | SCG_RCCR_SCS(sel);
                    while ((SCG0->CSR & SCG_CSR_SCS_MASK) != SCG_CSR_SCS(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)
{
    uint16_t mux;
    uint32_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->SYSTICKCLKSEL0);

    if (kNONE_to_NONE == attachId)
    {
        return kNONE_to_NONE;
    }

    for (i = 0U; i < 2U; i++)
    {
        mux = (uint16_t)GET_ID_ITEM_MUX(tmp32);
        if (tmp32 != 0UL)
        {
            if (mux == CM_SCGRCCRSCSCLKSEL)
            {
                actualSel = (uint32_t)((SCG0->RCCR & SCG_RCCR_SCS_MASK) >> SCG_RCCR_SCS_SHIFT);
            }
            else
            {
                actualSel = (uint32_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;
}

/* Set IP Clock Divider */
/**
 * brief   Setup peripheral clock dividers.
 * param   div_name    : Clock divider name
 * param divided_by_value: Value to be divided
 * return  Nothing
 */
void CLOCK_SetClkDiv(clock_div_name_t div_name, uint32_t divided_by_value)
{
    volatile uint32_t *pClkDiv;

    pClkDiv = &(SYSCON->SYSTICKCLKDIV[0]);
    /* halt and reset clock dividers */
    ((volatile uint32_t *)pClkDiv)[(uint32_t)div_name] = 0x3UL << 29U;

    if (divided_by_value == 0U) /*!<  halt */
    {
        ((volatile uint32_t *)pClkDiv)[(uint32_t)div_name] = 1UL << 30U;
    }
    else
    {
        ((volatile uint32_t *)pClkDiv)[(uint32_t)div_name] = (divided_by_value - 1U);
    }
}

/* Get IP clock dividers */
/**
 * brief   Get peripheral clock dividers.
 * param   div_name    : Clock divider name
 * return  peripheral clock dividers
 */
uint32_t CLOCK_GetClkDiv(clock_div_name_t div_name)
{
    uint32_t div;
    volatile uint32_t *pClkDiv;

    pClkDiv = &(SYSCON->SYSTICKCLKDIV[0]);

    if ((uint32_t)(((volatile uint32_t *)pClkDiv)[(uint32_t)div_name] & (0x3UL << 29U)) != 0UL)
    {
        div = 0U;
    }
    else
    {
        div = (uint32_t)((((volatile uint32_t *)pClkDiv)[(uint32_t)div_name] & 0xFFU) + 1U);
    }

    return div;
}

/* Halt IP Clock Divider */
/**
 * brief   Setup peripheral clock dividers.
 * param   Halt    : Clock divider name
 * return  Nothing
 */
void CLOCK_HaltClkDiv(clock_div_name_t div_name)
{
    volatile uint32_t *pClkDiv;

    pClkDiv = &(SYSCON->SYSTICKCLKDIV[0]);

    /* halt clock dividers */
    ((volatile uint32_t *)pClkDiv)[(uint32_t)div_name] = 1UL << 30U;

    return;
}

/* enable system clocks */
/**
 * brief   system clocks enable controls.
 * param   mask : system clocks enable value
 * return  Nothing
 */
void CLOCK_SetupClockCtrl(uint32_t mask)
{
    SYSCON->CLOCK_CTRL |= mask;

    return;
}

/* Get IP Clk */
/*! brief  Return Frequency of selected clock
 *  return Frequency of selected clock
 */
uint32_t CLOCK_GetFreq(clock_name_t clockName)
{
    uint32_t freq = 0U;

    switch (clockName)
    {
        case kCLOCK_CoreSysClk:
            freq = CLOCK_GetCoreSysClkFreq();
            break;
        case kCLOCK_BusClk:
            freq = CLOCK_GetCoreSysClkFreq() / ((SYSCON->AHBCLKDIV & 0xffU) + 1U);
            break;
        case kCLOCK_SystickClk0:
            freq = CLOCK_GetSystickClkFreq(0U);
            break;
        case kCLOCK_ClockOut:
            freq = CLOCK_GetClockOutClkFreq();
            break;
        case kCLOCK_Clk1M:
            freq = CLOCK_GetClk1MFreq();
            break;
        case kCLOCK_Fro12M:
            freq = CLOCK_GetFro12MFreq();
            break;
        case kCLOCK_FroHf:
            freq = CLOCK_GetFroHfFreq();
            break;
        case kCLOCK_Clk48M:
            freq = CLOCK_GetClk48MFreq();
            break;
        case kCLOCK_Clk144M:
            freq = CLOCK_GetClk144MFreq();
            break;
        case kCLOCK_Clk16K0:
            freq = CLOCK_GetClk16KFreq((uint32_t)kCLOCK_Clk16KToVbat);
            break;
        case kCLOCK_Clk16K1:
            freq = CLOCK_GetClk16KFreq((uint32_t)kCLOCK_Clk16KToVsys);
            break;
        case kCLOCK_Clk16K2:
            freq = CLOCK_GetClk16KFreq((uint32_t)kCLOCK_Clk16KToWake);
            break;
        case kCLOCK_Clk16K3:
            freq = CLOCK_GetClk16KFreq((uint32_t)kCLOCK_Clk16KToMain);
            break;
        case kCLOCK_ExtClk:
            freq = CLOCK_GetExtClkFreq();
            break;
        case kCLOCK_Osc32K0:
            freq = CLOCK_GetOsc32KFreq((uint32_t)kCLOCK_Osc32kToVbat);
            break;
        case kCLOCK_Osc32K1:
            freq = CLOCK_GetOsc32KFreq((uint32_t)kCLOCK_Osc32kToVsys);
            break;
        case kCLOCK_Osc32K2:
            freq = CLOCK_GetOsc32KFreq((uint32_t)kCLOCK_Osc32kToWake);
            break;
        case kCLOCK_Osc32K3:
            freq = CLOCK_GetOsc32KFreq((uint32_t)kCLOCK_Osc32kToMain);
            break;
        case kCLOCK_Pll0Out:
            freq = CLOCK_GetPll0OutFreq();
            break;
        case kCLOCK_Pll1Out:
            freq = CLOCK_GetPll1OutFreq();
            break;
        case kCLOCK_UsbPllOut:
            // freq = CLOCK_GetPll0OutFreq();
            break;
        case kCLOCK_LpOsc:
            freq = CLOCK_GetLposcFreq();
            break;
        default:
            freq = 0U;
            break;
    }
    return freq;
}

/* Get CTimer Clk */
/*! brief  Return Frequency of CTimer functional Clock
 *  return Frequency of CTimer functional Clock
 */
uint32_t CLOCK_GetCTimerClkFreq(uint32_t id)
{
    uint32_t freq = 0U;

    switch (SYSCON->CTIMERCLKSEL[id])
    {
        case 0U:
            freq = CLOCK_GetClk1MFreq();
            break;
        case 1U:
            freq = CLOCK_GetPll0OutFreq();
            break;
        case 2U:
            freq = CLOCK_GetPll1OutFreq() / (((SYSCON->PLL1CLK0DIV) & 0xffU) + 1U);
            break;
        case 3U:
            freq = CLOCK_GetFroHfFreq();
            break;
        case 4U:
            freq = CLOCK_GetFro12MFreq();
            break;
        case 5U:
            freq = CLOCK_GetSaiMclkFreq(0U);
            break;
        case 6U:
            freq = CLOCK_GetLposcFreq();
            break;
        case 8U:
            freq = CLOCK_GetSaiMclkFreq(1U);
            break;
        case 9U:
            freq = CLOCK_GetSaiTxBclkFreq(0U);
            break;
        case 10U:
            freq = CLOCK_GetSaiRxBclkFreq(0U);
            break;
        case 11U:
            freq = CLOCK_GetSaiTxBclkFreq(1U);
            break;
        case 12U:
            freq = CLOCK_GetSaiRxBclkFreq(1U);
            break;
        default:
            freq = 0U;
            break;
    }

    return freq / ((SYSCON->CTIMERCLKDIV[id] & 0xffU) + 1U);
}

/* Get ADC Clk */
/*! brief  Return Frequency of Adc Clock
 *  return Frequency of Adc.
 */
uint32_t CLOCK_GetAdcClkFreq(uint32_t id)
{
    uint32_t freq = 0U;
    uint32_t div  = 0U;

    switch ((id == 0U) ? (SYSCON->ADC0CLKSEL) : (SYSCON->ADC1CLKSEL))
    {
        case 1U:
            freq = CLOCK_GetPll0OutFreq();
            break;
        case 2U:
            freq = CLOCK_GetFroHfFreq();
            break;
        case 3U:
            freq = CLOCK_GetFro12MFreq();
            break;
        case 4U:
            freq = CLOCK_GetExtClkFreq();
            break;
        case 5U:
            freq = CLOCK_GetPll1OutFreq() / (((SYSCON->PLL1CLK0DIV) & 0xffU) + 1U);
            break;
        case 6U:
            // freq = CLOCK_GetUPllOutFreq();
            break;
        default:
            freq = 0U;
            break;
    }

    div = ((id == 0U) ? ((SYSCON->ADC0CLKDIV & SYSCON_ADC0CLKDIV_DIV_MASK) + 1U) :
                        ((SYSCON->ADC1CLKDIV & SYSCON_ADC1CLKDIV_DIV_MASK) + 1U));

    return freq / div;
}

/* Get LPFLEXCOMM Clk */
/*! brief  Return Frequency of LPFLEXCOMM Clock
 *  return Frequency of LPFLEXCOMM Clock.
 */
uint32_t CLOCK_GetLPFlexCommClkFreq(uint32_t id)
{
    uint32_t freq = 0U;

    switch (SYSCON->FCCLKSEL[id])
    {
        case 1U:
            freq = CLOCK_GetPllClkDivFreq();
            break;
        case 2U:
            freq = CLOCK_GetFro12MFreq();
            break;
        case 3U:
            freq = CLOCK_GetFroHfFreq() / ((SYSCON->FROHFDIV & 0xffU) + 1U);
            break;
        case 4U:
            freq = CLOCK_GetClk1MFreq();
            break;
        case 5U:
            // freq = CLOCK_GetUPllOutFreq();
            break;
        case 6U:
            freq = CLOCK_GetLposcFreq();
            break;
        default:
            freq = 0U;
            break;
    }

    return freq / ((SYSCON->FLEXCOMMCLKDIV[id] & 0xffU) + 1U);
}


/* Get SYSTEM PLL0 Clk */
/*! brief  Return Frequency of PLL0
 *  return Frequency of PLL0
 */
uint32_t CLOCK_GetPll0OutFreq(void)
{
    uint32_t clkRate = 0;
    uint32_t prediv, postdiv;
    float workRate = 0.0F;

    /* Get the input clock frequency of PLL. */
    clkRate = CLOCK_GetPLL0InClockRate();

    /* If PLL0 is work */
    if (CLOCK_IsPLL0Locked() == true)
    {
        prediv  = findPll0PreDiv();
        postdiv = findPll0PostDiv();
        /* Adjust input clock */
        clkRate = clkRate / prediv;
        /* MDEC used for rate */
        workRate = (float)clkRate * (float)findPll0MMult();
        workRate /= (float)postdiv;
    }

    return (uint32_t)workRate;
}

/* Get SYSTEM PLL1 Clk */
/*! brief  Return Frequency of PLL1
 *  return Frequency of PLL1
 */
uint32_t CLOCK_GetPll1OutFreq(void)
{
    uint32_t clkRate = 0;
    uint32_t prediv, postdiv;
    float workRate = 0.0F;

    /* Get the input clock frequency of PLL. */
    clkRate = CLOCK_GetPLL1InClockRate();

    /* If PLL1 is work */
    if (CLOCK_IsPLL1Locked() == true)
    {
        prediv  = findPll1PreDiv();
        postdiv = findPll1PostDiv();
        /* Adjust input clock */
        clkRate = clkRate / prediv;
        /* MDEC used for rate */
        workRate = (float)clkRate * (float)findPll1MMult();
        workRate /= (float)postdiv;
    }

    return (uint32_t)workRate;
}

/* Get PLLClkDiv Clk */
/*! brief  Return Frequency of PLLClkDiv
 *  return Frequency of PLLClkDiv
 */
uint32_t CLOCK_GetPllClkDivFreq(void)
{
    uint32_t freq = 0U;

    switch (SYSCON->PLLCLKDIVSEL)
    {
        case 0U:
            freq = CLOCK_GetPll0OutFreq();
            break;
        case 1U:
            freq = CLOCK_GetPll1OutFreq() / (((SYSCON->PLL1CLK0DIV) & 0xffU) + 1U);
            break;
        default:
            freq = 0U;
            break;
    }

    return freq / ((SYSCON->PLLCLKDIV & SYSCON_PLLCLKDIV_DIV_MASK) + 1U);
}

/*!
 * brief Gets the external UPLL frequency.
 *
 * This function gets the external UPLL frequency in Hz.
 *
 * return The frequency of the external UPLL.
 */
uint32_t CLOCK_GetExtUpllFreq(void)
{
    return s_extUpllFreq;
}

/*!
 * brief Sets the external UPLL frequency.
 *
 * This function sets the external UPLL frequency in Hz.
 * Call this function after the external PLL frequency is changed.
 * Otherwise, the APIs, which are used to get the frequency, may return an incorrect value.
 *
 * param The frequency of external UPLL.
 */
void CLOCK_SetExtUpllFreq(uint32_t freq)
{
    s_extUpllFreq = freq;
}

/* Get I3C function Clk */
/*! brief  Return Frequency of I3C function clock
 *  return Frequency of I3C function Clock
 */
uint32_t CLOCK_GetI3cClkFreq(uint32_t id)
{
    uint32_t freq = 0U;
    uint32_t div  = 0U;

    switch ((id == 0U) ? (SYSCON->I3C0FCLKSEL) : (SYSCON->I3C1FCLKSEL))
    {
        case 1U:
            freq = CLOCK_GetPll0OutFreq();
            break;
        case 3U:
            freq = CLOCK_GetFroHfFreq();
            break;
        case 4U:
            freq = CLOCK_GetClk1MFreq();
            break;
        case 5U:
            freq = CLOCK_GetPll1OutFreq() / (((SYSCON->PLL1CLK0DIV) & 0xffU) + 1U);
            break;
        case 6U:
            // freq = CLOCK_GetUPllOutFreq();
        default:
            freq = 0U;
            break;
    }

    div = ((id == 0U) ? ((SYSCON->I3C0FCLKDIV & SYSCON_I3C0FCLKDIV_DIV_MASK) + 1U) :
                        ((SYSCON->I3C1FCLKDIV & SYSCON_I3C1FCLKDIV_DIV_MASK) + 1U));

    return freq / div;
}

/* Get MICFIL Clk */
/*! brief  Return Frequency of MICFIL
 *  return Frequency of MICFIL
 */
uint32_t CLOCK_GetMicfilClkFreq(void)
{
    uint32_t freq = 0U;

    switch (SYSCON->MICFILFCLKSEL)
    {
        case 0U:
            freq = CLOCK_GetFro12MFreq();
            break;
        case 1U:
            freq = CLOCK_GetPll0OutFreq();
            break;
        case 2U:
            freq = CLOCK_GetExtClkFreq();
            break;
        case 3U:
            freq = CLOCK_GetFroHfFreq();
            break;
        case 4U:
            freq = CLOCK_GetPll1OutFreq() / (((SYSCON->PLL1CLK0DIV) & 0xffU) + 1U);
            break;
        case 5U:
            freq = CLOCK_GetSaiMclkFreq(0U);
            break;
        case 6U:
            // freq = CLOCK_GetUPllOutFreq();
        case 8U:
            freq = CLOCK_GetSaiMclkFreq(1U);
            break;
        default:
            freq = 0U;
            break;
    }

    return freq / ((SYSCON->MICFILFCLKDIV & SYSCON_MICFILFCLKDIV_DIV_MASK) + 1U);
}

/* Get FLEXIO Clk */
/*! brief  Return Frequency of FLEXIO
 *  return Frequency of FLEXIO
 */
uint32_t CLOCK_GetFlexioClkFreq(void)
{
    uint32_t freq = 0U;

    switch (SYSCON->FLEXIOCLKSEL)
    {
        case 1U:
            freq = CLOCK_GetPll0OutFreq();
            break;
        case 2U:
            freq = CLOCK_GetExtClkFreq();
            break;
        case 3U:
            freq = CLOCK_GetFroHfFreq();
            break;
        case 4U:
            freq = CLOCK_GetFro12MFreq();
            break;
        case 5U:
            freq = CLOCK_GetPll1OutFreq() / (((SYSCON->PLL1CLK0DIV) & 0xffU) + 1U);
            break;
        case 6U:
            // freq = CLOCK_GetUPllOutFreq();
        default:
            freq = 0U;
            break;
    }

    return freq / ((SYSCON->FLEXIOCLKDIV & SYSCON_FLEXIOCLKDIV_DIV_MASK) + 1U);
}

/* Get FLEXCAN Clk */
/*! brief  Return Frequency of FLEXCAN
 *  return Frequency of FLEXCAN
 */
uint32_t CLOCK_GetFlexcanClkFreq(uint32_t id)
{
    uint32_t freq = 0U;
    uint32_t div  = 0U;

    switch ((id == 0U) ? (SYSCON->FLEXCAN0CLKSEL) : (SYSCON->FLEXCAN1CLKSEL))
    {
        case 1U:
            freq = CLOCK_GetPll0OutFreq();
            break;
        case 2U:
            freq = CLOCK_GetExtClkFreq();
            break;
        case 3U:
            freq = CLOCK_GetFroHfFreq();
            break;
        case 5U:
            freq = CLOCK_GetPll1OutFreq() / (((SYSCON->PLL1CLK0DIV) & 0xffU) + 1U);
            break;
        case 6U:
            // freq = CLOCK_GetUPllOutFreq();
        default:
            freq = 0U;
            break;
    }

    div = ((id == 0U) ? ((SYSCON->FLEXCAN0CLKDIV & SYSCON_FLEXCAN0CLKDIV_DIV_MASK) + 1U) :
                        ((SYSCON->FLEXCAN1CLKDIV & SYSCON_FLEXCAN1CLKDIV_DIV_MASK) + 1U));

    return freq / div;
}

/* Get EWM0 Clk */
/*! brief  Return Frequency of EWM0
 *  return Frequency of EWM0
 */
uint32_t CLOCK_GetEwm0ClkFreq(void)
{
    uint32_t freq = 0U;

    switch (SYSCON->EWM0CLKSEL)
    {
        case 1U:
            freq = CLOCK_GetClk16KFreq((uint32_t)kCLOCK_Clk16KToWake);
            break;
        case 2U:
            freq = CLOCK_GetOsc32KFreq((uint32_t)kCLOCK_Osc32kToWake);
            break;
        default:
            freq = 0U;
            break;
    }

    return freq;
}

/* Get Watchdog Clk */
/*! brief  Return Frequency of Watchdog
 *  return Frequency of Watchdog
 */
uint32_t CLOCK_GetWdtClkFreq(uint32_t id)
{
    uint32_t freq = 0U;
    uint32_t div  = 0U;

    if (id == 0U)
    {
        freq = CLOCK_GetClk1MFreq();
    }
    else
    {
        switch (SYSCON->WDT1CLKSEL)
        {
            case 0U:
                freq = CLOCK_GetClk16KFreq((uint32_t)kCLOCK_Clk16KToWake);
                break;
            case 1U:
                freq = CLOCK_GetFroHfFreq() / ((SYSCON->FROHFDIV & 0xffU) + 1U);
                break;
            case 2U:
                freq = CLOCK_GetClk1MFreq();
                break;
            case 3U:
                freq = CLOCK_GetClk1MFreq();
                break;
            default:
                freq = 0U;
                break;
        }
    }

    div = ((id == 0U) ? ((SYSCON->WDT0CLKDIV & SYSCON_WDT0CLKDIV_DIV_MASK) + 1U) :
                        ((SYSCON->WDT1CLKDIV & SYSCON_WDT1CLKDIV_DIV_MASK) + 1U));

    return freq / div;
}

/* Get OSTIMER Clk */
/*! brief  Return Frequency of OSTIMER
 *  return Frequency of OSTIMER
 */
uint32_t CLOCK_GetOstimerClkFreq(void)
{
    uint32_t freq = 0U;

    switch (SYSCON->OSTIMERCLKSEL)
    {
        case 0U:
            freq = CLOCK_GetClk16KFreq((uint32_t)kCLOCK_Clk16KToWake);
            break;
        case 1U:
            freq = CLOCK_GetOsc32KFreq((uint32_t)kCLOCK_Osc32kToWake);
            break;
        case 2U:
            freq = CLOCK_GetClk1MFreq();
            break;
        default:
            freq = 0U;
            break;
    }

    return freq;
}

/* Get CMP Function Clk */
/*! brief  Return Frequency of CMP Function
 *  return Frequency of CMP Function
 */
uint32_t CLOCK_GetCmpFClkFreq(uint32_t id)
{
    uint32_t freq = 0U;
    uint32_t div  = 0U;

    switch ((id == 0U) ? (SYSCON->CMP0FCLKSEL) : (SYSCON->CMP1FCLKSEL))
    {
        case 1U:
            freq = CLOCK_GetPll0OutFreq();
            break;
        case 2U:
            freq = CLOCK_GetFroHfFreq();
            break;
        case 3U:
            freq = CLOCK_GetFro12MFreq();
            break;
        case 4U:
            freq = CLOCK_GetExtClkFreq();
            break;
        case 5U:
            freq = CLOCK_GetPll1OutFreq() / (((SYSCON->PLL1CLK0DIV) & 0xffU) + 1U);
            break;
        case 6U:
            // freq = CLOCK_GetUPllOutFreq();
        default:
            freq = 0U;
            break;
    }

    div = ((id == 0U) ? ((SYSCON->CMP0FCLKDIV & SYSCON_CMP0FCLKDIV_DIV_MASK) + 1U) :
                        ((SYSCON->CMP1FCLKDIV & SYSCON_CMP1FCLKDIV_DIV_MASK) + 1U));

    return freq / div;
}

/* Get CMP Round Robin Clk */
/*! brief  Return Frequency of CMP Round Robin
 *  return Frequency of CMP Round Robin
 */
uint32_t CLOCK_GetCmpRRClkFreq(uint32_t id)
{
    uint32_t freq = 0U;
    uint32_t div  = 0U;

    switch ((id == 0U) ? (SYSCON->CMP0RRCLKSEL) : (SYSCON->CMP1RRCLKSEL))
    {
        case 1U:
            freq = CLOCK_GetPll0OutFreq();
            break;
        case 2U:
            freq = CLOCK_GetFroHfFreq();
            break;
        case 3U:
            freq = CLOCK_GetFro12MFreq();
            break;
        case 4U:
            freq = CLOCK_GetExtClkFreq();
            break;
        case 5U:
            freq = CLOCK_GetPll1OutFreq() / (((SYSCON->PLL1CLK0DIV) & 0xffU) + 1U);
            break;
        case 6U:
            // freq = CLOCK_GetUPllOutFreq();
        default:
            freq = 0U;
            break;
    }

    div = ((id == 0U) ? ((SYSCON->CMP0RRCLKDIV & SYSCON_CMP0RRCLKDIV_DIV_MASK) + 1U) : 
              ((SYSCON->CMP1RRCLKDIV & SYSCON_CMP1RRCLKDIV_DIV_MASK) + 1U));

    return freq / div;
}

/* Get UTICK Clk */
/*! brief  Return Frequency of UTICK
 *  return Frequency of UTICK
 */
uint32_t CLOCK_GetUtickClkFreq(void)
{
    uint32_t freq = 0U;
    uint32_t div  = ((SYSCON->UTICKCLKDIV & SYSCON_UTICKCLKDIV_DIV_MASK) + 1U);

    switch (SYSCON->UTICKCLKSEL)
    {
        case 0U:
            freq = CLOCK_GetExtClkFreq();
            break;
        case 1U:
            freq = CLOCK_GetOsc32KFreq((uint32_t)kCLOCK_Osc32kToWake);
            break;
        case 2U:
            freq = CLOCK_GetClk1MFreq();
            break;
        default:
            freq = 0U;
            break;
    }
    return freq / div;
}

/* Get SAI Clk */
/*! brief  Return Frequency of SAI
 *  return Frequency of SAI
 */
uint32_t CLOCK_GetSaiClkFreq(uint32_t id)
{
    uint32_t freq = 0U;
    uint32_t div  = 0U;

    switch ((id == 0U) ? (SYSCON->SAI0CLKSEL) : (SYSCON->SAI1CLKSEL))
    {
        case 1U:
            freq = CLOCK_GetPll0OutFreq();
            break;
        case 2U:
            freq = CLOCK_GetExtClkFreq();
            break;
        case 3U:
            freq = CLOCK_GetFroHfFreq();
            break;
        case 4U:
            freq = CLOCK_GetPll1OutFreq() / (((SYSCON->PLL1CLK0DIV) & 0xffU) + 1U);
            break;
        case 6U:
            // freq = CLOCK_GetUPllOutFreq();
        default:
            freq = 0U;
            break;
    }

    div = ((id == 0U) ? ((SYSCON->SAI0CLKDIV & SYSCON_SAI0CLKDIV_DIV_MASK) + 1U) :
                        ((SYSCON->SAI1CLKDIV & SYSCON_SAI1CLKDIV_DIV_MASK) + 1U));

    return freq / div;
}

/* Get SAI MCLK */
/*! brief  Initialize the SAI MCLK to given frequency.
 *  return Nothing
 */
void CLOCK_SetupSaiMclk(uint32_t id, uint32_t iFreq)
{
    s_Sai_Mclk_Freq[id] = iFreq;

    return;
}

/* Get SAI TX BCLK */
/*! brief  Initialize the SAI TX BCLK to given frequency.
 *  return Nothing
 */
void CLOCK_SetupSaiTxBclk(uint32_t id, uint32_t iFreq)
{
    s_Sai_Tx_Bclk_Freq[id] = iFreq;

    return;
}

/* Get SAI RX BCLK */
/*! brief  Initialize the SAI RX BCLK to given frequency.
 *  return Nothing
 */
void CLOCK_SetupSaiRxBclk(uint32_t id, uint32_t iFreq)
{
    s_Sai_Rx_Bclk_Freq[id] = iFreq;

    return;
}

/* Get SAI MCLK */
/*! brief  Return Frequency of SAI MCLK
 *  return Frequency of SAI MCLK
 */
uint32_t CLOCK_GetSaiMclkFreq(uint32_t id)
{
    return s_Sai_Mclk_Freq[id];
}

/* Get SAI TX BCLK */
/*! brief  Return Frequency of SAI TX BCLK
 *  return Frequency of SAI TX BCLK
 */
uint32_t CLOCK_GetSaiTxBclkFreq(uint32_t id)
{
    return s_Sai_Tx_Bclk_Freq[id];
}

/* Get SAI RX BCLK */
/*! brief  Return Frequency of SAI RX BCLK
 *  return Frequency of SAI RX BCLK
 */
uint32_t CLOCK_GetSaiRxBclkFreq(uint32_t id)
{
    return s_Sai_Rx_Bclk_Freq[id];
}

/* Return System PLL input clock rate */
/*! brief    Return  PLL0 input clock rate
 *  return    PLL0 input clock rate
 */
uint32_t CLOCK_GetPLL0InClockRate(void)
{
    uint32_t clkRate = 0U;

    switch ((SCG0->APLLCTRL & SCG_APLLCTRL_SOURCE_MASK) >> SCG_APLLCTRL_SOURCE_SHIFT)
    {
        case 0x00U:
            clkRate = CLOCK_GetExtClkFreq();
            break;
        case 0x01U:
            clkRate = CLOCK_GetClk48MFreq();
            break;
        case 0x02U:
            clkRate = CLOCK_GetOsc32KFreq((uint32_t)kCLOCK_Osc32kToVbat);
            break;
        default:
            clkRate = 0U;
            break;
    }

    return clkRate;
}

/* Return PLL1 input clock rate */
uint32_t CLOCK_GetPLL1InClockRate(void)
{
    uint32_t clkRate = 0U;

    switch ((SCG0->SPLLCTRL & SCG_SPLLCTRL_SOURCE_MASK) >> SCG_SPLLCTRL_SOURCE_SHIFT)
    {
        case 0x00U:
            clkRate = CLOCK_GetExtClkFreq();
            break;
        case 0x01U:
            clkRate = CLOCK_GetClk48MFreq();
            break;
        case 0x02U:
            clkRate = CLOCK_GetOsc32KFreq((uint32_t)kCLOCK_Osc32kToVbat);
            break;
        default:
            clkRate = 0U;
            break;
    }

    return clkRate;
}

/* Return PLL output clock rate from setup structure */
/*! brief    Return PLL0 output clock rate from setup structure
 *  param    pSetup : Pointer to a PLL setup structure
 *  return   PLL0 output clock rate the setup structure will generate
 */
uint32_t CLOCK_GetPLLOutFromSetup(pll_setup_t *pSetup)
{
    uint32_t clkRate = 0;
    uint32_t prediv, postdiv;
    float workRate = 0.0F;

    /* Get the input clock frequency of PLL. */
    clkRate = CLOCK_GetPLLInClockRateFromSetup(pSetup);

    prediv  = findPllPreDivFromSetup(pSetup);
    postdiv = findPllPostDivFromSetup(pSetup);
    /* Adjust input clock */
    clkRate = clkRate / prediv;
    /* MDEC used for rate */
    workRate = (float)clkRate * (float)findPllMMultFromSetup(pSetup);
    workRate /= (float)postdiv;

    return (uint32_t)workRate;
}

/* 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) == 0UL);

    pll_error_t pllError;

    /* Get PLL Input Clock Rate */
    switch (pControl->inputSource)
    {
        case (uint32_t)kPll_ClkSrcSysOsc:
            inRate = CLOCK_GetExtClkFreq();
            break;
        case (uint32_t)kPll_ClkSrcFirc:
            inRate = CLOCK_GetClk48MFreq();
            break;
        case (uint32_t)kPll_ClkSrcRosc:
            inRate = CLOCK_GetOsc32KFreq((uint32_t)kCLOCK_Osc32kToVbat);
            break;
        default:
            inRate = 0U;
            break;
    }

    /* PLL flag options */
    pllError = CLOCK_GetPllConfig(inRate, pControl->desiredRate, pSetup, useSS);
    pSetup->pllctrl |= (uint32_t)pControl->inputSource;
    if ((useSS) && (pllError == kStatus_PLL_Success))
    {
        /* If using SS mode, then some tweaks are made to the generated setup */
        pSetup->pllsscg[1] |= (uint32_t)pControl->ss_mf | (uint32_t)pControl->ss_mr | (uint32_t)pControl->ss_mc;
        if (pControl->mfDither)
        {
            pSetup->pllsscg[1] |= (1UL << SCG_APLLSSCG1_DITHER_SHIFT);
        }
    }

    return pllError;
}

/* Setup PLL Frequency from pre-calculated value */
/**
 * brief Set PLL0 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_SetPLL0Freq(const pll_setup_t *pSetup)
{
    uint32_t inRate, clkRate, prediv;

    /* Enable LDO */
    SCG0->LDOCSR |= SCG_LDOCSR_LDOEN_MASK;

    /* Power off PLL0 and disable PLL0 clock during setup changes */
    SCG0->APLLCSR &= ~(SCG_APLLCSR_APLLPWREN_MASK | SCG_APLLCSR_APLLCLKEN_MASK);

    /* Write PLL setup data */
    SCG0->APLLCTRL  = pSetup->pllctrl;
    SCG0->APLLNDIV  = pSetup->pllndiv;
    SCG0->APLLNDIV  = pSetup->pllndiv | (1UL << SCG_APLLNDIV_NREQ_SHIFT); /* latch */
    SCG0->APLLPDIV  = pSetup->pllpdiv;
    SCG0->APLLPDIV  = pSetup->pllpdiv | (1UL << SCG_APLLPDIV_PREQ_SHIFT); /* latch */
    SCG0->APLLMDIV  = pSetup->pllmdiv;
    SCG0->APLLMDIV  = pSetup->pllmdiv | (1UL << SCG_APLLMDIV_MREQ_SHIFT); /* latch */
    SCG0->APLLSSCG0 = pSetup->pllsscg[0];
    SCG0->APLLSSCG1 = pSetup->pllsscg[1];

    /* Unlock APLLLOCK_CNFG register */
    SCG0->TRIM_LOCK = 0x5a5a0001;

    /* Configure lock time of APLL stable, value = 500us/x+300, where x is the period of clk_ref (clk_in/N). */
    inRate = CLOCK_GetPLL0InClockRate();
    prediv = findPll0PreDiv();
    /* Adjust input clock */
    clkRate             = inRate / prediv;
    SCG0->APLLLOCK_CNFG = SCG_APLLLOCK_CNFG_LOCK_TIME(clkRate / 2000U + 300U);

    /* Power on PLL0 and enable PLL0 clock */
    SCG0->APLLCSR |= (SCG_APLLCSR_APLLPWREN_MASK | SCG_APLLCSR_APLLCLKEN_MASK);

    /* Wait for APLL lock */
    while (CLOCK_IsPLL0Locked() == false)
    {
    }

    if (pSetup->pllRate != CLOCK_GetPll0OutFreq())
    {
        return kStatus_PLL_OutputError;
    }

    return kStatus_PLL_Success;
}

/* Setup PLL1 Frequency from pre-calculated value */
/**
 * brief Set PLL1 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_SetPLL1Freq(const pll_setup_t *pSetup)
{
    uint32_t inRate, clkRate, prediv;

    /* Enable LDO */
    SCG0->LDOCSR |= SCG_LDOCSR_LDOEN_MASK;

    /* Power off PLL1 and disable PLL1 clock during setup changes */
    SCG0->SPLLCSR &= ~(SCG_SPLLCSR_SPLLPWREN_MASK | SCG_SPLLCSR_SPLLCLKEN_MASK);

    /* Write PLL setup data */
    SCG0->SPLLCTRL  = pSetup->pllctrl;
    SCG0->SPLLNDIV  = pSetup->pllndiv;
    SCG0->SPLLNDIV  = pSetup->pllndiv | (1UL << SCG_SPLLNDIV_NREQ_SHIFT); /* latch */
    SCG0->SPLLPDIV  = pSetup->pllpdiv;
    SCG0->SPLLPDIV  = pSetup->pllpdiv | (1UL << SCG_SPLLPDIV_PREQ_SHIFT); /* latch */
    SCG0->SPLLMDIV  = pSetup->pllmdiv;
    SCG0->SPLLMDIV  = pSetup->pllmdiv | (1UL << SCG_SPLLMDIV_MREQ_SHIFT); /* latch */
    SCG0->SPLLSSCG0 = pSetup->pllsscg[0];
    SCG0->SPLLSSCG1 = pSetup->pllsscg[1];

    /* Unlock SPLLLOCK_CNFG register */
    SCG0->TRIM_LOCK = 0x5a5a0001;

    /* Configure lock time of APLL stable, value = 500μs/x+300, where x is the period of clk_ref (clk_in/N). */
    inRate = CLOCK_GetPLL1InClockRate();
    prediv = findPll1PreDiv();
    /* Adjust input clock */
    clkRate             = inRate / prediv;
    SCG0->SPLLLOCK_CNFG = SCG_SPLLLOCK_CNFG_LOCK_TIME(clkRate / 2000U + 300U);

    /* Power on PLL1 and enable PLL1 clock */
    SCG0->SPLLCSR |= (SCG_SPLLCSR_SPLLPWREN_MASK | SCG_SPLLCSR_SPLLCLKEN_MASK);

    /* Wait for APLL lock */
    while (CLOCK_IsPLL1Locked() == false)
    {
    }

    if (pSetup->pllRate != CLOCK_GetPll1OutFreq())
    {
        return kStatus_PLL_OutputError;
    }

    return kStatus_PLL_Success;
}

/*! @brief Enable the OSTIMER 32k clock.
 *  @return  Nothing
 */
void CLOCK_EnableOstimer32kClock(void)
{
    // PMC->OSEVENTTIMER |= PMC_OSEVENTTIMER_CLOCKENABLE_MASK;
}

/* Get FRO 12M Clk */
/*! brief  Return Frequency of FRO 12MHz
 *  return Frequency of FRO 12MHz
 */
static uint32_t CLOCK_GetFro12MFreq(void)
{
    return ((SCG0->SIRCCSR & SCG_SIRCCSR_SIRC_CLK_PERIPH_EN_MASK) != 0UL) ? 12000000U : 0U;
}

/* Get CLK 1M Clk */
/*! brief  Return Frequency of CLK 1MHz
 *  return Frequency of CLK 1MHz
 */
static uint32_t CLOCK_GetClk1MFreq(void)
{
    return 1000000U;
}

/* Get HF FRO Clk */
/*! brief  Return Frequency of High-Freq output of FRO
 *  return Frequency of High-Freq output of FRO
 */
static uint32_t CLOCK_GetFroHfFreq(void)
{
    uint32_t freq;

    if ((SCG0->FIRCCSR & SCG_FIRCCSR_FIRCEN_MASK) == 0UL)
    {
        freq = 0;
    }
    else if ((SCG0->FIRCCFG & SCG_FIRCCFG_RANGE_MASK) != 0UL)
    {
        freq = 144000000U;
    }
    else
    {
        freq = 48000000U;
    }

    return freq;
}

/* Get CLK 48M Clk */
/*! brief  Return Frequency of CLK 48MHz
 *  return Frequency of CLK 48MHz
 */
static uint32_t CLOCK_GetClk48MFreq(void)
{
    return ((SCG0->FIRCCSR & SCG_FIRCCSR_FIRC_SCLK_PERIPH_EN_MASK) != 0U) ? 48000000U : 0U;
}

/* Get CLK 144M Clk */
/*! brief  Return Frequency of CLK 144MHz
 *  return Frequency of CLK 144MHz
 */
static uint32_t CLOCK_GetClk144MFreq(void)
{
    return ((SCG0->FIRCCSR & SCG_FIRCCSR_FIRC_FCLK_PERIPH_EN_MASK) != 0U) ? 144000000U : 0U;
}

/* Get CLK 16K Clk */
/*! brief  Return Frequency of CLK 16KHz
 *  return Frequency of CLK 16KHz
 */
static uint32_t CLOCK_GetClk16KFreq(uint32_t id)
{
    return ((VBAT0->FROCTLA & VBAT_FROCTLA_FRO_EN_MASK) != 0U) ?
               (((VBAT0->FROCLKE & VBAT_FROCLKE_CLKE(id)) != 0UL) ? 16000U : 0U) :
               0U;
}

/* Get EXT OSC Clk */
/*! brief  Return Frequency of External Clock
 *  return Frequency of External Clock. If no external clock is used returns 0.
 */
static uint32_t CLOCK_GetExtClkFreq(void)
{
    return ((SCG0->SOSCCSR & SCG_SOSCCSR_SOSCVLD_MASK) != 0UL) ? s_Ext_Clk_Freq : 0U;
}

/* Get RTC OSC Clk */
/*! brief  Return Frequency of 32kHz osc
 *  return Frequency of 32kHz osc
 */
static uint32_t CLOCK_GetOsc32KFreq(uint32_t id)
{
    return ((SCG0->ROSCCSR & SCG_ROSCCSR_ROSCVLD_MASK) != 0UL) ?
               (((VBAT0->OSCCLKE & VBAT_OSCCLKE_CLKE(id)) != 0UL) ? s_Xtal32_Freq : 0U) :
               0U;
}

/* Get MAIN Clk */
/*! brief  Return Frequency of Core System
 *  return Frequency of Core System
 */
uint32_t CLOCK_GetCoreSysClkFreq(void)
{
    uint32_t freq = 0U;

    switch ((SCG0->CSR & SCG_CSR_SCS_MASK) >> SCG_CSR_SCS_SHIFT)
    {
        case 1U:
            freq = CLOCK_GetExtClkFreq();
            break;
        case 2U:
            freq = CLOCK_GetFro12MFreq();
            break;
        case 3U:
            freq = CLOCK_GetFroHfFreq();
            break;
        case 4U:
            freq = CLOCK_GetOsc32KFreq((uint32_t)kCLOCK_Osc32kToWake);
            break;
        case 5U:
            freq = CLOCK_GetPll0OutFreq();
            break;
        case 6U:
            freq = CLOCK_GetPll1OutFreq();
            break;
        case 7U:
            // freq = CLOCK_GetUPllOutFreq();
            break;
        default:
            freq = 0U;
            break;
    }

    return freq;
}

/* Get Systick Clk */
/*! brief  Return Frequency of SystickClock
 *  return Frequency of Systick Clock
 */
static uint32_t CLOCK_GetSystickClkFreq(uint32_t id)
{
    uint32_t freq = 0U;

    switch (SYSCON->SYSTICKCLKSEL0)
    {
        case 0U:
            freq = CLOCK_GetCoreSysClkFreq() / (((SYSCON->SYSTICKCLKDIV[id]) & 0xffU) + 1U);
            break;
        case 1U:
            freq = CLOCK_GetClk1MFreq();
            break;
        case 2U:
            freq = CLOCK_GetLposcFreq();
            break;
        default:
            freq = 0U;
            break;
    }

    return freq;
}

/* Get CLOCK OUT Clk */
/*! brief  Return Frequency of ClockOut
 *  return Frequency of ClockOut
 */
static uint32_t CLOCK_GetClockOutClkFreq(void)
{
    uint32_t freq = 0U;

    switch (SYSCON->CLKOUTSEL)
    {
        case 0U:
            freq = CLOCK_GetCoreSysClkFreq();
            break;
        case 1U:
            freq = CLOCK_GetPll0OutFreq();
            break;
        case 2U:
            freq = CLOCK_GetExtClkFreq();
            break;
        case 3U:
            freq = CLOCK_GetFroHfFreq();
            break;
        case 4U:
            freq = CLOCK_GetFro12MFreq();
            break;
        case 5U:
            freq = CLOCK_GetPll1OutFreq() / (((SYSCON->PLL1CLK0DIV) & 0xffU) + 1U);
            break;
        case 6U:
            freq = CLOCK_GetLposcFreq();
            break;
        case 7U:
            // freq = CLOCK_GetUPllOutFreq();
            break;
        default:
            freq = 0U;
            break;
    }
    return freq / ((SYSCON->CLKOUTDIV & 0xffU) + 1U);
}

/* Get LP_OSC Clk */
/*! brief  Return Frequency of LP_OSC
 *  return Frequency of LP_OSC
 */
static uint32_t CLOCK_GetLposcFreq(void)
{
    uint32_t freq = 0U;

    switch ((RTC0->CTRL & RTC_CTRL_CLK_SEL_MASK) >> RTC_CTRL_CLK_SEL_SHIFT)
    {
        case 0U:
            freq = CLOCK_GetClk16KFreq((uint32_t)kCLOCK_Clk16KToVbat);
            break;
        case 1U:
            freq = CLOCK_GetOsc32KFreq((uint32_t)kCLOCK_Osc32kToVbat);
            break;
        default:
            freq = 0U;
            break;
    }

    return freq;
}

/* Find SELP, SELI, and SELR values for raw M value, max M = MVALMAX */
static void pllFindSel(uint32_t M, uint32_t *pSelP, uint32_t *pSelI, uint32_t *pSelR)
{
    uint32_t seli, selp;
    /* bandwidth: compute selP from Multiplier */
    if ((SCG0->APLLCTRL & SCG_APLLCTRL_LIMUPOFF_MASK) == 0UL) /* normal mode */
    {
        selp = (M >> 2U) + 1U;
        if (selp >= 31U)
        {
            selp = 31U;
        }
        *pSelP = selp;

        if (M >= 8000UL)
        {
            seli = 1UL;
        }
        else if (M >= 122UL)
        {
            seli = (uint32_t)(8000UL / M); /*floor(8000/M) */
        }
        else
        {
            seli = 2UL * ((uint32_t)(M / 4UL)) + 3UL; /* 2*floor(M/4) + 3 */
        }

        if (seli >= 63UL)
        {
            seli = 63UL;
        }
        *pSelI = seli;

        *pSelR = 0U;
    }
    else
    {
        /* Note: If the spread spectrum and fractional mode, choose N to ensure 3 MHz < Fin/N < 5 MHz */
        *pSelP = 3U;
        *pSelI = 4U;
        *pSelR = 4U;
    }
}

/* Get predivider (N) from PLL0 NDIV setting */
static uint32_t findPll0PreDiv(void)
{
    uint32_t preDiv = 1UL;

    /* Direct input is not used? */
    if ((SCG0->APLLCTRL & SCG_APLLCTRL_BYPASSPREDIV_MASK) == 0UL)
    {
        preDiv = SCG0->APLLNDIV & SCG_APLLNDIV_NDIV_MASK;
        if (preDiv == 0UL)
        {
            preDiv = 1UL;
        }
    }
    return preDiv;
}

/* Get predivider (N) from PLL1 NDIV setting */
static uint32_t findPll1PreDiv(void)
{
    uint32_t preDiv = 1UL;

    /* Direct input is not used? */
    if ((SCG0->SPLLCTRL & SCG_SPLLCTRL_BYPASSPREDIV_MASK) == 0UL)
    {
        preDiv = SCG0->SPLLNDIV & SCG_SPLLNDIV_NDIV_MASK;
        if (preDiv == 0UL)
        {
            preDiv = 1UL;
        }
    }
    return preDiv;
}

/* Get postdivider (P) from PLL0 PDIV setting */
static uint32_t findPll0PostDiv(void)
{
    uint32_t postDiv = 1UL;

    if ((SCG0->APLLCTRL & SCG_APLLCTRL_BYPASSPOSTDIV_MASK) == 0UL)
    {
        if ((SCG0->APLLCTRL & SCG_APLLCTRL_BYPASSPOSTDIV2_MASK) != 0UL)
        {
            postDiv = SCG0->APLLPDIV & SCG_APLLPDIV_PDIV_MASK;
        }
        else
        {
            postDiv = 2UL * (SCG0->APLLPDIV & SCG_APLLPDIV_PDIV_MASK);
        }
        if (postDiv == 0UL)
        {
            postDiv = 2UL;
        }
    }

    return postDiv;
}

/* Get postdivider (P) from PLL1 PDIV setting. */
static uint32_t findPll1PostDiv(void)
{
    uint32_t postDiv = 1UL;

    if ((SCG0->SPLLCTRL & SCG_SPLLCTRL_BYPASSPOSTDIV_MASK) == 0UL)
    {
        if ((SCG0->SPLLCTRL & SCG_SPLLCTRL_BYPASSPOSTDIV2_MASK) != 0UL)
        {
            postDiv = SCG0->SPLLPDIV & SCG_SPLLPDIV_PDIV_MASK;
        }
        else
        {
            postDiv = 2UL * (SCG0->SPLLPDIV & SCG_SPLLPDIV_PDIV_MASK);
        }
        if (postDiv == 0UL)
        {
            postDiv = 2UL;
        }
    }

    return postDiv;
}

/* Get multiplier (M) from PLL0 SSCG and SEL_EXT settings */
static float findPll0MMult(void)
{
    float mMult = 1.0F;
    float mMult_fract;
    uint32_t mMult_int;

    if ((SCG0->APLLSSCG1 & SCG_APLLSSCG1_SEL_SS_MDIV_MASK) == 0UL)
    {
        mMult = (float)(uint32_t)(SCG0->APLLMDIV & SCG_APLLMDIV_MDIV_MASK);
    }
    else
    {
        mMult_int = ((SCG0->APLLSSCG1 & SCG_APLLSSCG1_SS_MDIV_MSB_MASK) << 7U);
        mMult_int = mMult_int | ((SCG0->APLLSSCG0) >> PLL_SSCG_MD_INT_P);
        mMult_fract =
            ((float)(uint32_t)((SCG0->APLLSSCG0) & PLL_SSCG_MD_FRACT_M) / (float)(uint32_t)(1UL << PLL_SSCG_MD_INT_P));
        mMult = (float)mMult_int + mMult_fract;
    }
    if (0ULL == ((uint64_t)mMult))
    {
        mMult = 1.0F;
    }
    return mMult;
}

/* Get multiplier (M) from PLL1 MDEC. */
static float findPll1MMult(void)
{
    float mMult = 1.0F;
    float mMult_fract;
    uint32_t mMult_int;

    if ((SCG0->SPLLSSCG1 & SCG_SPLLSSCG1_SEL_SS_MDIV_MASK) == 0UL)
    {
        mMult = (float)(uint32_t)(SCG0->SPLLMDIV & SCG_SPLLMDIV_MDIV_MASK);
    }
    else
    {
        mMult_int = ((SCG0->SPLLSSCG1 & SCG_SPLLSSCG1_SS_MDIV_MSB_MASK) << 7U);
        mMult_int = mMult_int | ((SCG0->SPLLSSCG0) >> PLL_SSCG_MD_INT_P);
        mMult_fract =
            ((float)(uint32_t)((SCG0->SPLLSSCG0) & PLL_SSCG_MD_FRACT_M) / (float)(uint32_t)(1UL << PLL_SSCG_MD_INT_P));
        mMult = (float)mMult_int + mMult_fract;
    }
    if (0ULL == ((uint64_t)mMult))
    {
        mMult = 1.0F;
    }
    return mMult;
}

/* Find greatest common divisor between m and n */
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;
}

#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_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 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]) && (useSS == s_UseSSCache[i]))
        {
            /* Hit the target in cache buffer. */
            pSetup->pllctrl    = s_PllSetupCacheStruct[i].pllctrl;
            pSetup->pllndiv    = s_PllSetupCacheStruct[i].pllndiv;
            pSetup->pllmdiv    = s_PllSetupCacheStruct[i].pllmdiv;
            pSetup->pllpdiv    = s_PllSetupCacheStruct[i].pllpdiv;
            pSetup->pllsscg[0] = s_PllSetupCacheStruct[i].pllsscg[0];
            pSetup->pllsscg[1] = s_PllSetupCacheStruct[i].pllsscg[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, 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_UseSSCache[s_PllSetupCacheIdx]  = useSS;

    s_PllSetupCacheStruct[s_PllSetupCacheIdx].pllctrl    = pSetup->pllctrl;
    s_PllSetupCacheStruct[s_PllSetupCacheIdx].pllndiv    = pSetup->pllndiv;
    s_PllSetupCacheStruct[s_PllSetupCacheIdx].pllmdiv    = pSetup->pllmdiv;
    s_PllSetupCacheStruct[s_PllSetupCacheIdx].pllpdiv    = pSetup->pllpdiv;
    s_PllSetupCacheStruct[s_PllSetupCacheIdx].pllsscg[0] = pSetup->pllsscg[0];
    s_PllSetupCacheStruct[s_PllSetupCacheIdx].pllsscg[1] = pSetup->pllsscg[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;
}

/*
 * Set PLL output based on desired output rate.
 * In this function, the it calculates the PLL0 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", "pllndiv", "pllpdiv", "pllmdiv" would updated in this function.
 */
static pll_error_t CLOCK_GetPllConfigInternal(uint32_t finHz, uint32_t foutHz, pll_setup_t *pSetup, bool useSS)
{
    uint32_t nDivOutHz, fccoHz;
    uint32_t pllPreDivider, pllMultiplier, pllPostDivider;
    uint32_t pllDirectInput, pllDirectOutput;
    uint32_t pllSelP, pllSelI, pllSelR, uplimoff;

    /* Baseline parameters (no input or output dividers) */
    pllPreDivider   = 1U; /* 1 implies pre-divider will be disabled */
    pllPostDivider  = 1U; /* 1 implies post-divider will be disabled */
    pllDirectOutput = 1U;

    /* 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 3MHz and 20MHz */
    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 20MHz */
        if (finHz > (PLL_MAX_IN_SSMODE * 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;
        }
        if (finHz > PLL_HIGHER_IN_LIMIT)
        {
            /* Input clock into the PLL cannot be higher than this */
            return kStatus_PLL_InputTooHigh;
        }
    }

    /* 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 */
        /* divide-by-2 divider in the post-divider is always work*/
        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, finHz);

        if (a > PLL_LOWER_IN_LIMIT)
        {
            a = 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);

    /* Find optimal values for filter */
    if (useSS == false)
    {
        /* Will bumping up M by 1 get us closer to the desired CCO frequency? */
        if ((nDivOutHz * ((pllMultiplier * 2U) + 1U)) < (fccoHz * 2U))
        {
            pllMultiplier++;
        }

        /* Setup filtering */
        pllFindSel(pllMultiplier, &pllSelP, &pllSelI, &pllSelR);
        uplimoff = 0U;

        /* Get encoded value for M (mult) and use manual filter, disable SS mode */
        pSetup->pllmdiv = (uint32_t)PLL_MDIV_VAL_SET(pllMultiplier);
        pSetup->pllsscg[1] &= ~SCG_APLLSSCG1_SEL_SS_MDIV_MASK;
    }
    else
    {
        uint64_t fc;

        /* Filtering will be handled by SSC */
        pllSelR  = 0UL;
        pllSelI  = 0UL;
        pllSelP  = 0UL;
        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)(uint32_t)(fccoHz % nDivOutHz) << 25UL) / nDivOutHz;

        /* Set multiplier */
        pSetup->pllsscg[0] = (uint32_t)(PLL_SSCG_MD_INT_SET(pllMultiplier) | PLL_SSCG_MD_FRACT_SET((uint32_t)fc));
        pSetup->pllsscg[1] = (uint32_t)(PLL_SSCG_MD_INT_SET(pllMultiplier) >> 32U) | SCG_APLLSSCG1_SEL_SS_MDIV_MASK;
    }

    /* Get encoded values for N (prediv) and P (postdiv) */
    pSetup->pllndiv = PLL_NDIV_VAL_SET(pllPreDivider);
    pSetup->pllpdiv = PLL_PDIV_VAL_SET(pllPostDivider);

    /* PLL control */
    pSetup->pllctrl = (pllSelR << SCG_APLLCTRL_SELR_SHIFT) |                 /* Filter coefficient */
                      (pllSelI << SCG_APLLCTRL_SELI_SHIFT) |                 /* Filter coefficient */
                      (pllSelP << SCG_APLLCTRL_SELP_SHIFT) |                 /* Filter coefficient */
                      (uplimoff << SCG_APLLCTRL_LIMUPOFF_SHIFT) |            /* SS/fractional mode disabled */
                      (pllDirectInput << SCG_APLLCTRL_BYPASSPREDIV_SHIFT) |  /* Bypass pre-divider? */
                      (pllDirectOutput << SCG_APLLCTRL_BYPASSPOSTDIV_SHIFT); /* Bypass post-divider? */

    return kStatus_PLL_Success;
}

/* Get PLL input clock rate from setup structure */
static uint32_t CLOCK_GetPLLInClockRateFromSetup(pll_setup_t *pSetup)
{
    uint32_t clkRate = 0U;

    switch ((pSetup->pllctrl & SCG_APLLCTRL_SOURCE_MASK) >> SCG_APLLCTRL_SOURCE_SHIFT)
    {
        case 0x00U:
            clkRate = CLOCK_GetExtClkFreq();
            break;
        case 0x01U:
            clkRate = CLOCK_GetClk48MFreq();
            break;
        case 0x02U:
            clkRate = CLOCK_GetOsc32KFreq((uint32_t)kCLOCK_Osc32kToVbat);
            break;
        default:
            clkRate = 0U;
            break;
    }

    return clkRate;
}

/* Get predivider (N) from from setup structure */
static uint32_t findPllPreDivFromSetup(pll_setup_t *pSetup)
{
    uint32_t preDiv = 1UL;

    /* Direct input is not used? */
    if ((pSetup->pllctrl & SCG_APLLCTRL_BYPASSPREDIV_MASK) == 0UL)
    {
        preDiv = pSetup->pllndiv & SCG_APLLNDIV_NDIV_MASK;
        if (preDiv == 0UL)
        {
            preDiv = 1UL;
        }
    }
    return preDiv;
}

/* Get postdivider (P) from from setup structure */
static uint32_t findPllPostDivFromSetup(pll_setup_t *pSetup)
{
    uint32_t postDiv = 1UL;

    if ((pSetup->pllctrl & SCG_APLLCTRL_BYPASSPOSTDIV_MASK) == 0UL)
    {
        if ((pSetup->pllctrl & SCG_APLLCTRL_BYPASSPOSTDIV2_MASK) != 0UL)
        {
            postDiv = pSetup->pllpdiv & SCG_APLLPDIV_PDIV_MASK;
        }
        else
        {
            postDiv = 2UL * (pSetup->pllpdiv & SCG_APLLPDIV_PDIV_MASK);
        }
        if (postDiv == 0UL)
        {
            postDiv = 2UL;
        }
    }

    return postDiv;
}

/* Get multiplier (M) from from setup structure */
static float findPllMMultFromSetup(pll_setup_t *pSetup)
{
    float mMult = 1.0F;
    float mMult_fract;
    uint32_t mMult_int;

    if ((pSetup->pllsscg[1] & SCG_APLLSSCG1_SEL_SS_MDIV_MASK) == 0UL)
    {
        mMult = (float)(uint32_t)(pSetup->pllmdiv & SCG_APLLMDIV_MDIV_MASK);
    }
    else
    {
        mMult_int   = ((pSetup->pllsscg[1] & SCG_APLLSSCG1_SS_MDIV_MSB_MASK) << 7U);
        mMult_int   = mMult_int | ((pSetup->pllsscg[0]) >> PLL_SSCG_MD_INT_P);
        mMult_fract = ((float)(uint32_t)((pSetup->pllsscg[0]) & PLL_SSCG_MD_FRACT_M) /
                       (float)(uint32_t)(1UL << PLL_SSCG_MD_INT_P));
        mMult       = (float)mMult_int + mMult_fract;
    }
    if (0ULL == ((uint64_t)mMult))
    {
        mMult = 1.0F;
    }
    return mMult;
}

/*! brief Enable USB HS PHY PLL clock.
 *
 * This function enables the internal 480MHz USB PHY PLL clock.
 *
 * param src  USB HS PHY PLL clock source.
 * param freq The frequency specified by src.
 * retval true The clock is set successfully.
 * retval false The clock source is invalid to get proper USB HS clock.
 */
bool CLOCK_EnableUsbhsPhyPllClock(clock_usb_phy_src_t src, uint32_t freq)
{
    uint32_t phyPllDiv  = 0U;
    uint16_t multiplier = 0U;
    bool err            = false;

    USBPHY->CTRL_CLR    = USBPHY_CTRL_SFTRST_MASK;
    USBPHY->ANACTRL_SET = USBPHY_ANACTRL_LVI_EN_MASK;
    USBPHY->PLL_SIC_SET = (USBPHY_PLL_SIC_PLL_POWER(1) | USBPHY_PLL_SIC_PLL_REG_ENABLE_MASK);
    if ((480000000UL % freq) != 0UL)
    {
        return false;
    }
    multiplier = (uint16_t)(480000000UL / freq);

    switch (multiplier)
    {
        case 15:
        {
            phyPllDiv = USBPHY_PLL_SIC_PLL_DIV_SEL(0U);
            break;
        }
        case 16:
        {
            phyPllDiv = USBPHY_PLL_SIC_PLL_DIV_SEL(1U);
            break;
        }
        case 20:
        {
            phyPllDiv = USBPHY_PLL_SIC_PLL_DIV_SEL(2U);
            break;
        }
        case 22:
        {
            phyPllDiv = USBPHY_PLL_SIC_PLL_DIV_SEL(3U);
            break;
        }
        case 24:
        {
            phyPllDiv = USBPHY_PLL_SIC_PLL_DIV_SEL(4U);
            break;
        }
        case 25:
        {
            phyPllDiv = USBPHY_PLL_SIC_PLL_DIV_SEL(5U);
            break;
        }
        case 30:
        {
            phyPllDiv = USBPHY_PLL_SIC_PLL_DIV_SEL(6U);
            break;
        }
        case 40:
        {
            phyPllDiv = USBPHY_PLL_SIC_PLL_DIV_SEL(7U);
            break;
        }
        default:
        {
            err = true;
            break;
        }
    }

    if (err)
    {
        return false;
    }

    USBPHY->PLL_SIC = (USBPHY->PLL_SIC & ~(USBPHY_PLL_SIC_PLL_DIV_SEL_MASK)) | phyPllDiv;

    USBPHY->PLL_SIC_CLR = USBPHY_PLL_SIC_PLL_BYPASS_MASK;
    USBPHY->PLL_SIC_SET = (USBPHY_PLL_SIC_PLL_EN_USB_CLKS_MASK);

    USBPHY->CTRL_CLR = USBPHY_CTRL_CLR_CLKGATE_MASK;
    USBPHY->PWD      = 0x0U;

    while (0UL == (USBPHY->PLL_SIC & USBPHY_PLL_SIC_PLL_LOCK_MASK))
    {
    }

    return true;
}

/*! brief Disable USB HS PHY PLL clock.
 *
 * This function disables USB HS PHY PLL clock.
 */
void CLOCK_DisableUsbhsPhyPllClock(void)
{
    USBPHY->CTRL |= USBPHY_CTRL_CLKGATE_MASK; /* Set to 1U to gate clocks */
}

/*! brief Enable USB HS clock.
 * retval true The clock is set successfully.
 * retval false The clock source is invalid to get proper USB HS clock.
 */
bool CLOCK_EnableUsbhsClock(void)
{
    USBHS1__USBC->USBCMD |= USBHS_USBCMD_RST_MASK;
    /* Add a delay between RST and RS so make sure there is a DP pullup sequence*/
    for (uint32_t i = 0; i < 400000U; i++)
    {
        __ASM("nop");
    }
    return true;
}

/**
 * @brief   FIRC Auto Trim With SOF.
 * @return  returns success or fail status.
 */
status_t CLOCK_FIRCAutoTrimWithSOF(void)
{
    /* System OSC Clock Monitor is disabled */
    CLOCK_SetSysOscMonitorMode(kSCG_SysOscMonitorDisable);

    firc_trim_config_t fircAutoTrimConfig = {
        .trimMode = kSCG_FircTrimUpdate,  /* FIRC trim is enabled and trim value update is enabled */
        .trimSrc  = kSCG_FircTrimSrcUsb0, /* Trim source is USB0 start of frame (1kHz) */
        .trimDiv  = 1U,                   /* Divided value */
        .trimCoar = 0U,                   /* Trim value, see Reference Manual for more information */
        .trimFine = 0U,                   /* Trim value, see Reference Manual for more information */
    };

    return CLOCK_FROHFTrimConfig(fircAutoTrimConfig);
}
