/*
 * Copyright 2017 - 2020 , NXP
 * All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include "fsl_clock.h"
#include "fsl_power.h"
/*******************************************************************************
 * Definitions
 ******************************************************************************/
/* Component ID definition, used by tools. */
#ifndef FSL_COMPONENT_ID
#define FSL_COMPONENT_ID "platform.drivers.clock"
#endif
#define NVALMAX (0x100U)
#define PVALMAX (0x20U)
#define MVALMAX (0x10000U)

#define PLL_MAX_N_DIV 0x100U

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

#define PLL_SSCG1_MDEC_VAL_P (10U) /* MDEC is in bits  25 downto 10 */
#define PLL_SSCG1_MDEC_VAL_M (0xFFFFULL << PLL_SSCG1_MDEC_VAL_P)
#define PLL_NDEC_VAL_P       (0U) /* NDEC is in bits  9:0 */
#define PLL_NDEC_VAL_M       (0xFFUL << PLL_NDEC_VAL_P)
#define PLL_PDEC_VAL_P       (0U) /*!<  PDEC is in bits 6:0 */
#define PLL_PDEC_VAL_M       (0x1FUL << PLL_PDEC_VAL_P)

#define PLL_MIN_CCO_FREQ_MHZ (275000000U)
#define PLL_MAX_CCO_FREQ_MHZ (550000000U)
#define PLL_LOWER_IN_LIMIT   (2000U)      /*!<  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 NDEC reg */
#define PLL_NDEC_VAL_SET(value) (((unsigned long)(value) << PLL_NDEC_VAL_P) & PLL_NDEC_VAL_M)
/* PLL PDEC reg */
#define PLL_PDEC_VAL_SET(value) (((unsigned long)(value) << PLL_PDEC_VAL_P) & PLL_PDEC_VAL_M)
/* SSCG control1 */
#define PLL_SSCG1_MDEC_VAL_SET(value) (((uint64_t)(value) << PLL_SSCG1_MDEC_VAL_P) & PLL_SSCG1_MDEC_VAL_M)

/* PLL0 SSCG control1 */
#define PLL0_SSCG_MD_FRACT_P 0U
#define PLL0_SSCG_MD_INT_P   25U
#define PLL0_SSCG_MD_FRACT_M (0x1FFFFFFUL << PLL0_SSCG_MD_FRACT_P)
#define PLL0_SSCG_MD_INT_M   ((uint64_t)0xFFUL << PLL0_SSCG_MD_INT_P)

#define PLL0_SSCG_MD_FRACT_SET(value) (((uint64_t)(value) << PLL0_SSCG_MD_FRACT_P) & PLL0_SSCG_MD_FRACT_M)
#define PLL0_SSCG_MD_INT_SET(value)   (((uint64_t)(value) << PLL0_SSCG_MD_INT_P) & PLL0_SSCG_MD_INT_M)

/* Peripheral FLASH_NMPA base address */
#define FLASH_NMPA_BASE (0x3FC00u)
/* Return values from Config (N-2) page of flash */
#define GET_HFXO_TRIM()  (*(uint32_t *)(FLASH_NMPA_BASE + 0xC0U)) // (0x3FCC0)
#define GET_32KXO_TRIM() (*(uint32_t *)(FLASH_NMPA_BASE + 0xC4U)) // (0x3FCC4)

#define XO_SLAVE_EN (1)

/* Saved value of PLL output rate, computed whenever needed to save run-time
   computation on each call to retrive the PLL rate. */
static uint32_t s_Pll0_Freq;
static uint32_t s_Pll1_Freq;

/** External clock rate on the CLKIN pin in Hz. If not used,
    set this to 0. Otherwise, set it to the exact rate in Hz this pin is
    being driven at. */
static uint32_t s_Ext_Clk_Freq  = 16000000U;
static uint32_t s_I2S_Mclk_Freq = 0U;

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

/*******************************************************************************
 * Prototypes
 ******************************************************************************/
/* 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 NDEC setting */
static uint32_t findPll0PreDiv(void);
/* Get predivider (N) from PLL1 NDEC setting */
static uint32_t findPll1PreDiv(void);
/* Get postdivider (P) from PLL0 PDEC setting */
static uint32_t findPll0PostDiv(void);
/* Get postdivider (P) from PLL1 PDEC setting. */
static uint32_t findPll1PostDiv(void);
/* Get multiplier (M) from PLL0 MDEC and SSCG settings */
static float findPll0MMult(void);
/* Get multiplier (M) from PLL1 MDEC. */
static uint32_t 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_GetPll0Config(uint32_t finHz, uint32_t foutHz, pll_setup_t *pSetup, bool useSS);
/* Update local PLL rate variable */
static void CLOCK_GetPLL0OutFromSetupUpdate(pll_setup_t *pSetup);
/* Update local PLL1 rate variable */
static void CLOCK_GetPLL1OutFromSetupUpdate(pll_setup_t *pSetup);
/* Compensate for discontinuity in the capacitor banks */
static uint8_t CLOCK_u8OscCapConvert(uint8_t u8OscCap, uint8_t u8CapBankDiscontinuity);
/* Enables and sets LDO for High Frequency crystal oscillator */
static void CLOCK_SetXtalHfLdo(void);

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

/* 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_RTCOSC32KCLKSEL)
                {
                    PMC->RTCOSC32K = (PMC->RTCOSC32K & ~PMC_RTCOSC32K_SEL_MASK) | PMC_RTCOSC32K_SEL(sel);
                }
                else if (mux == CM_OSTIMERCLKSEL)
                {
                    PMC->OSEVENTTIMER =
                        (PMC->OSEVENTTIMER & ~PMC_OSEVENTTIMER_SELCLOCK_MASK) | PMC_OSEVENTTIMER_SELCLOCK(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_RTCOSC32KCLKSEL)
            {
                actualSel = ((PMC->RTCOSC32K) & PMC_RTCOSC32K_SEL_MASK) >> PMC_RTCOSC32K_SEL_SHIFT;
            }
            else if (mux == CM_OSTIMERCLKSEL)
            {
                actualSel = ((PMC->OSEVENTTIMER) & PMC_OSEVENTTIMER_SELCLOCK_MASK) >> PMC_OSEVENTTIMER_SELCLOCK_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
 * param reset :  Whether to reset the divider counter.
 * return  Nothing
 */
void CLOCK_SetClkDiv(clock_div_name_t div_name, uint32_t divided_by_value, bool reset)
{
    volatile uint32_t *pClkDiv;

    pClkDiv = &(SYSCON->SYSTICKCLKDIV[0]);
    if ((div_name >= kCLOCK_DivFlexFrg0) && (div_name <= kCLOCK_DivFlexFrg7))
    {
        /*!<  Flexcomm Interface function clock = (clock selected via FCCLKSEL) / (1+ MULT /DIV), DIV = 0xFF */
        ((volatile uint32_t *)pClkDiv)[(uint16_t)div_name] =
            SYSCON_FRGCTRL_DIV_MASK | SYSCON_FRGCTRL_MULT(divided_by_value);
    }
    else
    {
        if (reset)
        {
            ((volatile uint32_t *)pClkDiv)[(uint16_t)div_name] = 1UL << 29U;
        }
        if (divided_by_value == 0U) /*!<  halt */
        {
            ((volatile uint32_t *)pClkDiv)[(uint16_t)div_name] = 1UL << 30U;
        }
        else
        {
            ((volatile uint32_t *)pClkDiv)[(uint16_t)div_name] = (divided_by_value - 1U);
        }
    }
}

/* Set FRO Clocking */
/**
 * brief   Initialize the Core clock to given frequency (12, 48 or 96 MHz).
 * Turns on FRO and uses default CCO, if freq is 12000000, then high speed output is off, else high speed output is
 * enabled.
 * param   iFreq   : Desired frequency (must be one of #CLK_FRO_12MHZ or #CLK_FRO_48MHZ or #CLK_FRO_96MHZ)
 * return  returns success or fail status.
 */
status_t CLOCK_SetupFROClocking(uint32_t iFreq)
{
    if ((iFreq != 12000000U) && (iFreq != 96000000U))
    {
        return kStatus_Fail;
    }
    /* Enable Analog Control module */
    SYSCON->PRESETCTRLCLR[2] = SYSCON_PRESETCTRL2_ANACTRL_RST_MASK;
    SYSCON->AHBCLKCTRLSET[2] = SYSCON_AHBCLKCTRL2_ANALOG_CTRL_MASK;

    /* Initialize FRO192 trim */
    CLOCK_FroHfTrim();

    /* Power up the FRO192M */
    POWER_DisablePD(kPDRUNCFG_PD_FRO192M);

    if (iFreq == 96000000U)
    {
        ANACTRL->FRO192M_CTRL |= ANACTRL_FRO192M_CTRL_ENA_96MHZCLK(1);
    }
    /* always enable
    else if (iFreq == 48000000U)
    {
        ANACTRL->FRO192M_CTRL |= ANACTRL_FRO192M_CTRL_ENA_48MHZCLK(1);
    }*/
    else
    {
        ANACTRL->FRO192M_CTRL |= ANACTRL_FRO192M_CTRL_ENA_12MHZCLK(1);
    }
    return kStatus_Success;
}

/* Set the FLASH wait states for the passed frequency */
/**
 * brief    Set the flash wait states for the input freuqency.
 * param    iFreq: Input frequency
 * return    Nothing
 */
typedef struct
{
    uint32_t waitstate;
    uint32_t freqMax;
} WaitStateInterval_t;

/* clang-format off */
/* Wait state if frequency is inferior to the one specified */
static const WaitStateInterval_t IntervalList[] = {
    {0, 11000000},
    {1, 22000000},
    {2, 33000000},
    {3, 44000000},
    {4, 55000000},
    {5, 66000000},
    {6, 77000000},
    {7, 88000000},
    {8, 100000000},
    {9, 115000000},
    {10, 130000000},
    {11, 150000000} /* Maximum allowed frequency (150 MHz) */
};
/* clang-format on */

void CLOCK_SetFLASHAccessCyclesForFreq(uint32_t system_freq_hz)
{
    /* Flash Controller & FMC internal number of Wait States (minus 1) */
    uint32_t num_wait_states = 15UL; /* Default to the maximum number of wait states */

    for (size_t cnt = 0; cnt < (sizeof(IntervalList) / sizeof(WaitStateInterval_t)); cnt++)
    {
        if (system_freq_hz <= IntervalList[cnt].freqMax)
        {
            num_wait_states = IntervalList[cnt].waitstate;
            break;
        }
    }

    FLASH->INTSTAT_CLR = 0xF; /* Clear all status flags */

    FLASH->DATAW[0] = (FLASH->DATAW[0] & 0xFFFFFFF0UL) | (num_wait_states & 0xFUL);

    FLASH->CMD = 0x2; /* CMD_SET_READ_MODE */

    /* Wait until the cmd is completed (without error) */
    while ((FLASH->INTSTAT & FLASH_INTSTAT_DONE_MASK) == 0UL)
    {
    }

    /* Adjust FMC waiting time cycles (num_wait_states) */
    SYSCON->FMCCR = (SYSCON->FMCCR & 0xFFFF0FFFUL) | ((num_wait_states & 0xFUL) << 12UL);
}

/* Set EXT OSC Clk */
/**
 * 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)
{
    if (iFreq >= 32000000U)
    {
        return kStatus_Fail;
    }
    /* Turn on power for crystal 32 MHz */
    POWER_DisablePD(kPDRUNCFG_PD_XTALHF);
    POWER_DisablePD(kPDRUNCFG_PD_LDOXTALHF);
    /* Enable clock_in clock for clock module. */
    SYSCON->CLOCK_CTRL |= SYSCON_CLOCK_CTRL_CLKIN_ENA_MASK;

    s_Ext_Clk_Freq = iFreq;
    return kStatus_Success;
}

/* Set I2S MCLK Clk */
/**
 * brief   Initialize the I2S MCLK 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_SetupI2SMClkClocking(uint32_t iFreq)
{
    s_I2S_Mclk_Freq = iFreq;
    return kStatus_Success;
}

/* Get CLOCK OUT Clk */
/*! brief  Return Frequency of ClockOut
 *  return Frequency of ClockOut
 */
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_GetFro1MFreq();
            break;

        case 5U:
            freq = CLOCK_GetPll1OutFreq();
            break;

        case 6U:
            freq = CLOCK_GetOsc32KFreq();
            break;

        case 7U:
            freq = 0U;
            break;

        default:
            freq = 0U;
            break;
    }
    return freq / ((SYSCON->CLKOUTDIV & 0xffU) + 1U);
}

/* Get CAN Clk */
/*! brief  Return Frequency of Can Clock
 *  return Frequency of Can.
 */
uint32_t CLOCK_GetMCanClkFreq(void)
{
    uint32_t freq = 0U;

    switch (SYSCON->CANCLKSEL)
    {
        case 0U:
            freq = CLOCK_GetCoreSysClkFreq() / ((SYSCON->CANCLKDIV & SYSCON_CANCLKDIV_DIV_MASK) + 1U);
            break;
        case 1U:
            freq = CLOCK_GetFro1MFreq();
            break;
        case 2U:
            freq = CLOCK_GetOsc32KFreq();
            break;
        case 7U:
            freq = 0U;
            break;

        default:
            freq = 0U;
            break;
    }

    return freq;
}

/* 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 == 0) ? (SYSCON->ADC0CLKSEL) : (SYSCON->ADC1CLKSEL))
    {
        case 0U:
            freq = CLOCK_GetCoreSysClkFreq();
            break;
        case 1U:
            freq = CLOCK_GetPll0OutFreq();
            break;
        case 2U:
            freq = CLOCK_GetFroHfFreq();
            break;
        default:
            freq = 0U;
            break;
    }

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

    return freq / div;
}

/* Get USB0 Clk */
/*! brief  Return Frequency of Usb0 Clock
 *  return Frequency of Usb0 Clock.
 */
uint32_t CLOCK_GetUsb0ClkFreq(void)
{
    uint32_t freq = 0U;

    switch (SYSCON->USB0CLKSEL)
    {
        case 0U:
            freq = CLOCK_GetCoreSysClkFreq();
            break;
        case 1U:
            freq = CLOCK_GetPll0OutFreq();
            break;
        case 3U:
            freq = CLOCK_GetFroHfFreq();
            break;
        case 5U:
            freq = CLOCK_GetPll1OutFreq();
            break;
        case 7U:
            freq = 0U;
            break;

        default:
            freq = 0U;
            break;
    }

    return freq / ((SYSCON->USB0CLKDIV & 0xffU) + 1U);
}

/* Get MCLK Clk */
/*! brief  Return Frequency of MClk Clock
 *  return Frequency of MClk Clock.
 */
uint32_t CLOCK_GetMclkClkFreq(void)
{
    uint32_t freq = 0U;

    switch (SYSCON->MCLKCLKSEL)
    {
        case 0U:
            freq = CLOCK_GetFroHfFreq();
            break;
        case 1U:
            freq = CLOCK_GetPll0OutFreq();
            break;
        case 7U:
            freq = 0U;
            break;

        default:
            freq = 0U;
            break;
    }

    return freq / ((SYSCON->MCLKDIV & 0xffU) + 1U);
}

/* Get SCTIMER Clk */
/*! brief  Return Frequency of SCTimer Clock
 *  return Frequency of SCTimer Clock.
 */
uint32_t CLOCK_GetSctClkFreq(void)
{
    uint32_t freq = 0U;

    switch (SYSCON->SCTCLKSEL)
    {
        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_GetPll1OutFreq();
            break;
        case 5U:
            freq = CLOCK_GetI2SMClkFreq();
            break;
        case 7U:
            freq = 0U;
            break;

        default:
            freq = 0U;
            break;
    }

    return freq / ((SYSCON->SCTCLKDIV & 0xffU) + 1U);
}

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

/* Get FRO 1M Clk */
/*! brief  Return Frequency of FRO 1MHz
 *  return Frequency of FRO 1MHz
 */
uint32_t CLOCK_GetFro1MFreq(void)
{
    return ((SYSCON->CLOCK_CTRL & SYSCON_CLOCK_CTRL_FRO1MHZ_CLK_ENA_MASK) != 0UL) ? 1000000U : 0U;
}

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

/* Get WATCH DOG Clk */
/*! brief  Return Frequency of Watchdog
 *  return Frequency of Watchdog
 */
uint32_t CLOCK_GetWdtClkFreq(void)
{
    return CLOCK_GetFro1MFreq() / ((SYSCON->WDTCLKDIV & SYSCON_WDTCLKDIV_DIV_MASK) + 1U);
}

/* Get HF FRO Clk */
/*! brief  Return Frequency of High-Freq output of FRO
 *  return Frequency of High-Freq output of FRO
 */
uint32_t CLOCK_GetFroHfFreq(void)
{
    return ((ANACTRL->FRO192M_CTRL & ANACTRL_FRO192M_CTRL_ENA_96MHZCLK_MASK) != 0UL) ? 96000000U : 0U;
}

/* Get SYSTEM PLL Clk */
/*! brief  Return Frequency of PLL
 *  return Frequency of PLL
 */
uint32_t CLOCK_GetPll0OutFreq(void)
{
    return s_Pll0_Freq;
}

/* Get USB PLL Clk */
/*! brief  Return Frequency of USB PLL
 *  return Frequency of PLL
 */
uint32_t CLOCK_GetPll1OutFreq(void)
{
    return s_Pll1_Freq;
}

/* Get RTC OSC Clk */
/*! brief  Return Frequency of 32kHz osc
 *  return Frequency of 32kHz osc
 */
uint32_t CLOCK_GetOsc32KFreq(void)
{
    return ((0UL == (PMC->PDRUNCFG0 & PMC_PDRUNCFG0_PDEN_FRO32K_MASK)) &&
            (0UL == (PMC->RTCOSC32K & PMC_RTCOSC32K_SEL_MASK))) ?
               CLK_RTC_32K_CLK :
               ((0UL == (PMC->PDRUNCFG0 & PMC_PDRUNCFG0_PDEN_XTAL32K_MASK)) &&
                ((PMC->RTCOSC32K & PMC_RTCOSC32K_SEL_MASK) != 0UL)) ?
               CLK_RTC_32K_CLK :
               0UL;
}

/* Get Flexcomm 32K Clk */
/*! brief  Return Frequency of Flexcomm 32kHz
 *  return Frequency of Flexcomm 32kHz
 */
uint32_t CLOCK_GetFC32KFreq(void)
{
    return ((0UL == (PMC->PDRUNCFG0 & PMC_PDRUNCFG0_PDEN_FRO32K_MASK)) &&
            (0UL == (SYSCON->FC32KCLKSEL & SYSCON_FC32KCLKSEL_FC32KCLKSEL_MASK))) ?
               CLK_RTC_32K_CLK :
               ((0UL == (PMC->PDRUNCFG0 & PMC_PDRUNCFG0_PDEN_XTAL32K_MASK)) &&
                ((SYSCON->FC32KCLKSEL & SYSCON_FC32KCLKSEL_FC32KCLKSEL_MASK) != 0UL)) ?
               CLK_RTC_32K_CLK :
               0UL;
}

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

    switch (SYSCON->MAINCLKSELB)
    {
        case 0U:
            if (SYSCON->MAINCLKSELA == 0U)
            {
                freq = CLOCK_GetFro12MFreq();
            }
            else if (SYSCON->MAINCLKSELA == 1U)
            {
                freq = CLOCK_GetExtClkFreq();
            }
            else if (SYSCON->MAINCLKSELA == 2U)
            {
                freq = CLOCK_GetFro1MFreq();
            }
            else if (SYSCON->MAINCLKSELA == 3U)
            {
                freq = CLOCK_GetFroHfFreq();
            }
            else
            {
                /* Added comments to avoid the violation of MISRA C-2012 rule 15.7 */
            }
            break;
        case 1U:
            freq = CLOCK_GetPll0OutFreq();
            break;
        case 2U:
            freq = CLOCK_GetPll1OutFreq();
            break;

        case 3U:
            freq = CLOCK_GetOsc32KFreq();
            break;

        default:
            freq = 0U;
            break;
    }

    return freq;
}

/* Get I2S MCLK Clk */
/*! brief  Return Frequency of I2S MCLK Clock
 *  return Frequency of I2S MCLK Clock
 */
uint32_t CLOCK_GetI2SMClkFreq(void)
{
    return s_I2S_Mclk_Freq;
}

/* Get PLLClkDiv Clk */
uint32_t CLOCK_GetPllClkDivFreq(void)
{
    uint32_t freq = 0U;

    switch (SYSCON->PLLCLKDIVSEL)
    {
        case 0U:
            freq = CLOCK_GetPll0OutFreq() / ((SYSCON->PLLCLKDIV & SYSCON_PLLCLKDIV_DIV_MASK) + 1U);
            break;
        case 1U:
            freq = CLOCK_GetPll1OutFreq() /
                   ((SYSCON->PLLCLKDIV & SYSCON_PLLCLKDIV_DIV_MASK) + 1U); // register name should be checked
            break;
        default:
            freq = 0U;
            break;
    }
    /* todo: pll_clk_div = pll_clk/div*/
    return freq;
}

/* Get FRG Clk */
uint32_t CLOCK_GetFrgFreq(uint32_t id)
{
    uint32_t freq = 0U;

    switch (SYSCON->FRGCLKSEL[id])
    {
        case 0U:
            freq = CLOCK_GetCoreSysClkFreq();
            break;
        case 1U:
            freq = CLOCK_GetPllClkDivFreq();
            break;
        case 2U:
            freq = CLOCK_GetFroHfFreq() / ((SYSCON->FROHFDIV & 0xffU) + 1U);
            break;
        default:
            freq = 0U;
            break;
    }
    return (uint32_t)(((uint64_t)freq * (SYSCON_FRGCTRL_DIV_MASK + 1U)) /
                      ((SYSCON_FRGCTRL_DIV_MASK + 1U) +
                       ((SYSCON->FRGCTRL[id] & SYSCON_FRGCTRL_MULT_MASK) >> SYSCON_FRGCTRL_MULT_SHIFT)));
}

/* Get FLEXCOMM Clk */
uint32_t CLOCK_GetFlexCommClkFreq(uint32_t id)
{
    uint32_t freq = 0U;

    switch (SYSCON->FCCLKSEL[id])
    {
        case 0U:
            freq = CLOCK_GetCoreSysClkFreq();
            break;
        case 1U:
            freq = CLOCK_GetFrgFreq(id);
            break;
        case 2U:
            freq = CLOCK_GetFro12MFreq();
            break;
        case 3U:
            freq = CLOCK_GetFroHfFreq() / ((SYSCON->FROHFDIV & 0xffU) + 1U);
            break;
        case 4U:
            freq = CLOCK_GetFro1MFreq();
            break;
        case 5U:
            freq = CLOCK_GetI2SMClkFreq();
            break;
        case 6U:
            freq = CLOCK_GetOsc32KFreq();
            break;
        case 7U:
            freq = 0U;
            break;

        default:
            freq = 0U;
            break;
    }

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

/* Get HS_LPSI Clk */
uint32_t CLOCK_GetHsLspiClkFreq(void)
{
    uint32_t freq = 0U;

    switch (SYSCON->HSSPICLKSEL)
    {
        case 0U:
            freq = CLOCK_GetCoreSysClkFreq();
            break;
        case 1U:
            freq = CLOCK_GetPll0OutFreq() / ((SYSCON->PLLCLKDIV & 0xffU) + 1U);
            break;
        case 2U:
            freq = CLOCK_GetFro12MFreq();
            break;
        case 3U:
            freq = CLOCK_GetFroHfFreq() / ((SYSCON->FROHFDIV & 0xffU) + 1U);
            break;
        case 4U:
            freq = CLOCK_GetFro1MFreq();
            break;
        case 6U:
            freq = CLOCK_GetOsc32KFreq();
            break;
        case 7U:
            freq = 0U;
            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_GetCoreSysClkFreq();
            break;
        case 1U:
            freq = CLOCK_GetPll0OutFreq();
            break;
        case 2U:
            freq = CLOCK_GetPll1OutFreq();
            break;
        case 3U:
            freq = CLOCK_GetFroHfFreq();
            break;
        case 4U:
            freq = CLOCK_GetFro1MFreq();
            break;
        case 5U:
            freq = CLOCK_GetI2SMClkFreq();
            break;
        case 6U:
            freq = CLOCK_GetOsc32KFreq();
            break;
        case 7U:
            freq = 0U;
            break;

        default:
            freq = 0U;
            break;
    }

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

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

    switch (SYSCON->SYSTICKCLKSEL0)
    {
        case 0U:
            /*Niobe4mini just has one SYSTICKSEL and SYSTICKDIV register, Fix coverity problem in this way temporarily
             */
            freq = CLOCK_GetCoreSysClkFreq() / (((SYSCON->SYSTICKCLKDIV[0]) & 0xffU) + 1U);
            break;
        case 1U:
            freq = CLOCK_GetFro1MFreq();
            break;
        case 2U:
            freq = CLOCK_GetOsc32KFreq();
            break;
        case 7U:
            freq = 0U;
            break;

        default:
            freq = 0U;
            break;
    }

    return freq;
}

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

    switch (SYSCON->DAC[id].CLKSEL)
    {
        case 0U:
            freq = CLOCK_GetCoreSysClkFreq();
            break;
        case 1U:
            freq = CLOCK_GetPll0OutFreq();
            break;
        case 3U:
            freq = CLOCK_GetFroHfFreq();
            break;
        case 4U:
            freq = CLOCK_GetFro12MFreq();
            break;
        case 5U:
            freq = CLOCK_GetPll1OutFreq();
            break;
        case 6U:
            freq = CLOCK_GetFro1MFreq();
            break;
        default:
            freq = 0U;
            break;
    }

    return freq / ((SYSCON->DAC[id].CLKDIV & SYSCON_DAC_CLKDIV_DIV_MASK) + 1U);
}

/* Get FlexSpi Clk */
/*! brief  Return Frequency of FlexSpi clock
 *  return Frequency of FlexSpi Clock
 */
uint32_t CLOCK_GetFlexSpiClkFreq(void)
{
    uint32_t freq = 0U;

    switch (SYSCON->FLEXSPICLKSEL)
    {
        case 0U:
            freq = CLOCK_GetCoreSysClkFreq();
            break;
        case 1U:
            freq = CLOCK_GetPll0OutFreq();
            break;
        case 3U:
            freq = CLOCK_GetFroHfFreq();
            break;
        case 5U:
            freq = CLOCK_GetPll1OutFreq();
            break;
        case 7U:
            freq = 0U;
            break;
        default:
            freq = 0U;
            break;
    }

    return freq / ((SYSCON->FLEXSPICLKDIV & SYSCON_FLEXSPICLKDIV_DIV_MASK) + 1U);
}

/* Get DMIC Clk */
/*! brief  Return Frequency of DMIC clock
 *  return Frequency of DMIC Clock
 */
uint32_t CLOCK_GetDmicClkFreq(void)
{
    uint32_t freq = 0U;

    switch (SYSCON->DMICFCLKSEL)
    {
        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_GetPll1OutFreq();
            break;
        case 5U:
            freq = CLOCK_GetI2SMClkFreq();
            break;
        default:
            freq = 0U;
            break;
    }

    return freq / ((SYSCON->DMICFCLKDIV & SYSCON_DMICFCLKDIV_DIV_MASK) + 1U);
}

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

    switch (SYSCON->I3CFCLKSEL)
    {
        case 0U:
            freq = CLOCK_GetCoreSysClkFreq();
            break;
        case 1U:
            freq = CLOCK_GetPllClkDivFreq();
            break;
        default:
            freq = 0U;
            break;
    }

    return freq / ((SYSCON->I3CFCLKDIV & SYSCON_I3CFCLKDIV_DIV_MASK) + 1U);
}

/* Get I3C function slow TC Clk */
/*! brief  Return Frequency of I3C function Slow TC clock
 *  return Frequency of I3C function slow TC Clock
 */
uint32_t CLOCK_GetI3cSTCClkFreq(void)
{
    uint32_t freq = 0U;

    switch (SYSCON->I3CFCLKSTCSEL)
    {
        case 0U:
            freq = CLOCK_GetI3cClkFreq();
            break;
        case 1U:
            freq = CLOCK_GetFro1MFreq();
            break;
        default:
            freq = 0U;
            break;
    }

    return freq / ((SYSCON->I3CFCLKSTCDIV & SYSCON_I3CFCLKSTCDIV_DIV_MASK) + 1U);
}

/* Get I3C function slow Clk */
/*! brief  Return Frequency of I3C function Slow clock
 *  return Frequency of I3C function slow Clock
 */
uint32_t CLOCK_GetI3cSClkFreq(void)
{
    uint32_t freq = 0U;

    switch (SYSCON->I3CFCLKSSEL)
    {
        case 0U:
            freq = CLOCK_GetFro1MFreq();
            break;
        default:
            freq = 0U;
            break;
    }

    return freq / ((SYSCON->I3CFCLKSDIV & SYSCON_I3CFCLKSDIV_DIV_MASK) + 1U);
}

/* 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;
    switch (clockName)
    {
        case kCLOCK_CoreSysClk:
            freq = CLOCK_GetCoreSysClkFreq();
            break;
        case kCLOCK_BusClk:
            freq = CLOCK_GetCoreSysClkFreq() / ((SYSCON->AHBCLKDIV & 0xffU) + 1U);
            break;
        case kCLOCK_ClockOut:
            freq = CLOCK_GetClockOutClkFreq();
            break;
        case kCLOCK_Pll1Out:
            freq = CLOCK_GetPll1OutFreq();
            break;
        case kCLOCK_Mclk:
            freq = CLOCK_GetMclkClkFreq();
            break;
        case kCLOCK_FroHf:
            freq = CLOCK_GetFroHfFreq();
            break;
        case kCLOCK_Fro12M:
            freq = CLOCK_GetFro12MFreq();
            break;
        case kCLOCK_Fro1M:
            freq = CLOCK_GetFro1MFreq();
            break;
        case kCLOCK_ExtClk:
            freq = CLOCK_GetExtClkFreq();
            break;
        case kCLOCK_Pll0Out:
            freq = CLOCK_GetPll0OutFreq();
            break;
        case kCLOCK_FlexI2S:
            freq = CLOCK_GetI2SMClkFreq();
            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 ((SYSCON->PLL0SSCG1 & SYSCON_PLL0SSCG1_MDIV_EXT_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 = 0UL;
    }
    else
    {
        /* Note: If the spread spectrum mode, choose N to ensure 3 MHz < Fin/N < 5 MHz */
        *pSelP = 3U;
        *pSelI = 4U;
        *pSelR = 4U;
    }
}

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

    /* Direct input is not used? */
    if ((SYSCON->PLL0CTRL & SYSCON_PLL0CTRL_BYPASSPREDIV_MASK) == 0UL)
    {
        preDiv = SYSCON->PLL0NDEC & SYSCON_PLL0NDEC_NDIV_MASK;
        if (preDiv == 0UL)
        {
            preDiv = 1UL;
        }
    }
    return preDiv;
}

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

    /* Direct input is not used? */
    if ((SYSCON->PLL1CTRL & SYSCON_PLL1CTRL_BYPASSPREDIV_MASK) == 0UL)
    {
        preDiv = SYSCON->PLL1NDEC & SYSCON_PLL1NDEC_NDIV_MASK;
        if (preDiv == 0UL)
        {
            preDiv = 1UL;
        }
    }
    return preDiv;
}

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

    if ((SYSCON->PLL0CTRL & SYSCON_PLL0CTRL_BYPASSPOSTDIV_MASK) == 0UL)
    {
        if ((SYSCON->PLL0CTRL & SYSCON_PLL0CTRL_BYPASSPOSTDIV2_MASK) != 0UL)
        {
            postDiv = SYSCON->PLL0PDEC & SYSCON_PLL0PDEC_PDIV_MASK;
        }
        else
        {
            postDiv = 2UL * (SYSCON->PLL0PDEC & SYSCON_PLL0PDEC_PDIV_MASK);
        }
        if (postDiv == 0UL)
        {
            postDiv = 2UL;
        }
    }
    return postDiv;
}

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

    if ((SYSCON->PLL1CTRL & SYSCON_PLL1CTRL_BYPASSPOSTDIV_MASK) == 0UL)
    {
        if ((SYSCON->PLL1CTRL & SYSCON_PLL1CTRL_BYPASSPOSTDIV2_MASK) != 0UL)
        {
            postDiv = SYSCON->PLL1PDEC & SYSCON_PLL1PDEC_PDIV_MASK;
        }
        else
        {
            postDiv = 2UL * (SYSCON->PLL1PDEC & SYSCON_PLL1PDEC_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 ((SYSCON->PLL0SSCG1 & SYSCON_PLL0SSCG1_SEL_EXT_MASK) != 0UL)
    {
        mMult =
            (float)(uint32_t)((SYSCON->PLL0SSCG1 & SYSCON_PLL0SSCG1_MDIV_EXT_MASK) >> SYSCON_PLL0SSCG1_MDIV_EXT_SHIFT);
    }
    else
    {
        mMult_int   = ((SYSCON->PLL0SSCG1 & SYSCON_PLL0SSCG1_MD_MBS_MASK) << 7U);
        mMult_int   = mMult_int | ((SYSCON->PLL0SSCG0) >> PLL0_SSCG_MD_INT_P);
        mMult_fract = ((float)(uint32_t)((SYSCON->PLL0SSCG0) & PLL0_SSCG_MD_FRACT_M) /
                       (float)(uint32_t)(1UL << PLL0_SSCG_MD_INT_P));
        mMult       = (float)mMult_int + mMult_fract;
    }
    if (mMult == 0.0F)
    {
        mMult = 1.0F;
    }
    return mMult;
}

/* Get multiplier (M) from PLL1 MDEC. */
static uint32_t findPll1MMult(void)
{
    uint32_t mMult = 1UL;

    mMult = SYSCON->PLL1MDEC & SYSCON_PLL1MDEC_MDIV_MASK;

    if (mMult == 0UL)
    {
        mMult = 1UL;
    }

    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;
}

/*
 * Set PLL0 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", "pllndec", "pllpdec", "pllmdec" would updated in this function.
 */
static pll_error_t CLOCK_GetPll0ConfigInternal(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->pllsscg[1] =
            (uint32_t)((PLL_SSCG1_MDEC_VAL_SET(pllMultiplier)) | (1UL << SYSCON_PLL0SSCG1_SEL_EXT_SHIFT));
    }
    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)(PLL0_SSCG_MD_INT_SET(pllMultiplier) | PLL0_SSCG_MD_FRACT_SET((uint32_t)fc));
        pSetup->pllsscg[1] = (uint32_t)(PLL0_SSCG_MD_INT_SET(pllMultiplier) >> 32U);
    }

    /* Get encoded values for N (prediv) and P (postdiv) */
    pSetup->pllndec = PLL_NDEC_VAL_SET(pllPreDivider);
    pSetup->pllpdec = PLL_PDEC_VAL_SET(pllPostDivider);

    /* PLL control */
    pSetup->pllctrl = (pllSelR << SYSCON_PLL0CTRL_SELR_SHIFT) |                  /* Filter coefficient */
                      (pllSelI << SYSCON_PLL0CTRL_SELI_SHIFT) |                  /* Filter coefficient */
                      (pllSelP << SYSCON_PLL0CTRL_SELP_SHIFT) |                  /* Filter coefficient */
                      (0UL << SYSCON_PLL0CTRL_BYPASSPLL_SHIFT) |                 /* PLL bypass mode disabled */
                      (uplimoff << SYSCON_PLL0CTRL_LIMUPOFF_SHIFT) |             /* SS/fractional mode disabled */
                      (pllDirectInput << SYSCON_PLL0CTRL_BYPASSPREDIV_SHIFT) |   /* Bypass pre-divider? */
                      (pllDirectOutput << SYSCON_PLL0CTRL_BYPASSPOSTDIV_SHIFT) | /* Bypass post-divider? */
                      (1UL << SYSCON_PLL0CTRL_CLKEN_SHIFT);                      /* Ensure the PLL clock output */

    return kStatus_PLL_Success;
}

#if (defined(CLOCK_USR_CFG_PLL_CONFIG_CACHE_COUNT) && CLOCK_USR_CFG_PLL_CONFIG_CACHE_COUNT)
/* Alloct the static buffer for cache. */
static pll_setup_t s_PllSetupCacheStruct[CLOCK_USR_CFG_PLL_CONFIG_CACHE_COUNT];
static uint32_t s_FinHzCache[CLOCK_USR_CFG_PLL_CONFIG_CACHE_COUNT]  = {0};
static uint32_t s_FoutHzCache[CLOCK_USR_CFG_PLL_CONFIG_CACHE_COUNT] = {0};
static bool s_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_GetPll0Config(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->pllndec    = s_PllSetupCacheStruct[i].pllndec;
            pSetup->pllpdec    = s_PllSetupCacheStruct[i].pllpdec;
            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_GetPll0ConfigInternal(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].pllndec    = pSetup->pllndec;
    s_PllSetupCacheStruct[s_PllSetupCacheIdx].pllpdec    = pSetup->pllpdec;
    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;
}

/* Update local PLL rate variable */
static void CLOCK_GetPLL0OutFromSetupUpdate(pll_setup_t *pSetup)
{
    s_Pll0_Freq = CLOCK_GetPLL0OutFromSetup(pSetup);
}

/* Update local PLL1 rate variable */
static void CLOCK_GetPLL1OutFromSetupUpdate(pll_setup_t *pSetup)
{
    s_Pll1_Freq = CLOCK_GetPLL1OutFromSetup(pSetup);
}

/* 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 ((SYSCON->PLL0CLKSEL & SYSCON_PLL0CLKSEL_SEL_MASK))
    {
        case 0x00U:
            clkRate = CLK_FRO_12MHZ;
            break;

        case 0x01U:
            clkRate = CLOCK_GetExtClkFreq();
            break;

        case 0x02U:
            clkRate = CLOCK_GetFro1MFreq();
            break;

        case 0x03U:
            clkRate = CLOCK_GetOsc32KFreq();
            break;

        default:
            clkRate = 0U;
            break;
    }

    return clkRate;
}

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

    switch ((SYSCON->PLL1CLKSEL & SYSCON_PLL1CLKSEL_SEL_MASK))
    {
        case 0x00U:
            clkRate = CLK_FRO_12MHZ;
            break;

        case 0x01U:
            clkRate = CLOCK_GetExtClkFreq();
            break;

        case 0x02U:
            clkRate = CLOCK_GetFro1MFreq();
            break;

        case 0x03U:
            clkRate = CLOCK_GetOsc32KFreq();
            break;

        default:
            clkRate = 0U;
            break;
    }

    return clkRate;
}

/* Return PLL0 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_GetPLL0OutFromSetup(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_GetPLL0InClockRate();

    if (((pSetup->pllctrl & SYSCON_PLL0CTRL_BYPASSPLL_MASK) == 0UL) &&
        ((pSetup->pllctrl & SYSCON_PLL0CTRL_CLKEN_MASK) != 0UL) &&
        ((PMC->PDRUNCFG0 & PMC_PDRUNCFG0_PDEN_PLL0_MASK) == 0UL) &&
        ((PMC->PDRUNCFG0 & PMC_PDRUNCFG0_PDEN_PLL0_SSCG_MASK) == 0UL))
    {
        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;
}

/* Return PLL1 output clock rate from setup structure */
/*! brief    Return PLL1 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_GetPLL1OutFromSetup(pll_setup_t *pSetup)
{
    uint32_t clkRate = 0;
    uint32_t prediv, postdiv;
    uint32_t workRate = 0UL;

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

    if (((pSetup->pllctrl & SYSCON_PLL1CTRL_BYPASSPLL_MASK) == 0UL) &&
        ((pSetup->pllctrl & SYSCON_PLL1CTRL_CLKEN_MASK) != 0UL) &&
        ((PMC->PDRUNCFG0 & PMC_PDRUNCFG0_PDEN_PLL1_MASK) == 0UL))
    {
        prediv  = findPll1PreDiv();
        postdiv = findPll1PostDiv();
        /* Adjust input clock */
        clkRate = clkRate / prediv;
        /* MDEC used for rate */
        workRate = clkRate * findPll1MMult();
        workRate /= postdiv;
    }

    return workRate;
}

/* Set the current PLL0 Rate */
/*! brief Store the current PLL rate
 *  param    rate: Current rate of the PLL
 *  return   Nothing
 **/
void CLOCK_SetStoredPLL0ClockRate(uint32_t rate)
{
    s_Pll0_Freq = rate;
}

/* Return PLL0 output clock rate */
/*! brief    Return  PLL0 output clock rate
 *  param    recompute   : Forces a PLL rate recomputation if true
 *  return    PLL0 output clock rate
 *  note The PLL rate is cached in the driver in a variable as
 *  the rate computation function can take some time to perform. It
 *  is recommended to use 'false' with the 'recompute' parameter.
 */
uint32_t CLOCK_GetPLL0OutClockRate(bool recompute)
{
    pll_setup_t Setup;
    uint32_t rate;

    if ((recompute) || (s_Pll0_Freq == 0U))
    {
        Setup.pllctrl    = SYSCON->PLL0CTRL;
        Setup.pllndec    = SYSCON->PLL0NDEC;
        Setup.pllpdec    = SYSCON->PLL0PDEC;
        Setup.pllsscg[0] = SYSCON->PLL0SSCG0;
        Setup.pllsscg[1] = SYSCON->PLL0SSCG1;

        CLOCK_GetPLL0OutFromSetupUpdate(&Setup);
    }

    rate = s_Pll0_Freq;

    return rate;
}

/*! brief    Return  PLL1 output clock rate
 *  param    recompute   : Forces a PLL rate recomputation if true
 *  return    PLL1 output clock rate
 *  note The PLL rate is cached in the driver in a variable as
 *  the rate computation function can take some time to perform. It
 *  is recommended to use 'false' with the 'recompute' parameter.
 */
uint32_t CLOCK_GetPLL1OutClockRate(bool recompute)
{
    pll_setup_t Setup;
    uint32_t rate;

    if ((recompute) || (s_Pll1_Freq == 0U))
    {
        Setup.pllctrl = SYSCON->PLL1CTRL;
        Setup.pllndec = SYSCON->PLL1NDEC;
        Setup.pllpdec = SYSCON->PLL1PDEC;
        Setup.pllmdec = SYSCON->PLL1MDEC;
        CLOCK_GetPLL1OutFromSetupUpdate(&Setup);
    }

    rate = s_Pll1_Freq;

    return rate;
}

/* Set PLL0 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_SetupPLL0Data(pll_config_t *pControl, pll_setup_t *pSetup)
{
    uint32_t inRate;
    bool useSS = (bool)((pControl->flags & PLL_CONFIGFLAG_FORCENOFRACT) == 0UL);

    pll_error_t pllError;

    /* Determine input rate for the PLL */
    if ((pControl->flags & PLL_CONFIGFLAG_USEINRATE) != 0UL)
    {
        inRate = pControl->inputRate;
    }
    else
    {
        inRate = CLOCK_GetPLL0InClockRate();
    }

    /* PLL flag options */
    pllError = CLOCK_GetPll0Config(inRate, pControl->desiredRate, pSetup, useSS);
    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 << SYSCON_PLL0SSCG1_DITHER_SHIFT);
        }
    }

    return pllError;
}

/* Set PLL0 output from PLL setup structure */
/*! brief    Set PLL output from PLL setup structure (precise frequency)
 * param pSetup  : Pointer to populated PLL setup structure
 * param flagcfg : Flag configuration for PLL config structure
 * return    PLL_ERROR_SUCCESS on success, or PLL setup error code
 * note  This function will power off the PLL, setup the PLL with the
 * new setup data, and then optionally powerup the PLL, wait for PLL lock,
 * and adjust system voltages to the new PLL rate. The function will not
 * alter any source clocks (ie, main systen clock) that may use the PLL,
 * so these should be setup prior to and after exiting the function.
 */
pll_error_t CLOCK_SetupPLL0Prec(pll_setup_t *pSetup, uint32_t flagcfg)
{
    uint32_t inRate, clkRate, prediv;

    /* Power off PLL during setup changes */
    POWER_EnablePD(kPDRUNCFG_PD_PLL0);
    POWER_EnablePD(kPDRUNCFG_PD_PLL0_SSCG);

    pSetup->flags = flagcfg;

    /* Write PLL setup data */
    SYSCON->PLL0CTRL  = pSetup->pllctrl;
    SYSCON->PLL0NDEC  = pSetup->pllndec;
    SYSCON->PLL0NDEC  = pSetup->pllndec | (1UL << SYSCON_PLL0NDEC_NREQ_SHIFT); /* latch */
    SYSCON->PLL0PDEC  = pSetup->pllpdec;
    SYSCON->PLL0PDEC  = pSetup->pllpdec | (1UL << SYSCON_PLL0PDEC_PREQ_SHIFT); /* latch */
    SYSCON->PLL0SSCG0 = pSetup->pllsscg[0];
    SYSCON->PLL0SSCG1 = pSetup->pllsscg[1];
    SYSCON->PLL0SSCG1 =
        pSetup->pllsscg[1] | (1UL << SYSCON_PLL0SSCG1_MREQ_SHIFT) | (1UL << SYSCON_PLL0SSCG1_MD_REQ_SHIFT); /* latch */

    POWER_DisablePD(kPDRUNCFG_PD_PLL0);
    POWER_DisablePD(kPDRUNCFG_PD_PLL0_SSCG);

    if ((pSetup->flags & PLL_SETUPFLAG_WAITLOCK) != 0UL)
    {
        if ((SYSCON->PLL0SSCG1 & SYSCON_PLL0SSCG1_MDIV_EXT_MASK) != 0UL) /* normal mode */
        {
            inRate = CLOCK_GetPLL0InClockRate();
            prediv = findPll0PreDiv();
            /* Adjust input clock */
            clkRate = inRate / prediv;
            /* The lock signal is only reliable between fref[2] :100 kHz to 20 MHz. */
            if ((clkRate >= 100000UL) && (clkRate <= 20000000UL))
            {
                while (CLOCK_IsPLL0Locked() == false)
                {
                }
            }
            else
            {
                SDK_DelayAtLeastUs(6000U,
                                   SDK_DEVICE_MAXIMUM_CPU_CLOCK_FREQUENCY); /* software should use a 6 ms time interval
                                                                               to insure the PLL will be stable */
            }
        }
        else /* spread spectrum mode */
        {
            SDK_DelayAtLeastUs(6000U,
                               SDK_DEVICE_MAXIMUM_CPU_CLOCK_FREQUENCY); /* software should use a 6 ms time interval to
                                                                           insure the PLL will be stable */
        }
    }

    /* Update current programmed PLL rate var */
    CLOCK_GetPLL0OutFromSetupUpdate(pSetup);

    /* System voltage adjustment, occurs prior to setting main system clock */
    if ((pSetup->flags & PLL_SETUPFLAG_ADGVOLT) != 0UL)
    {
        POWER_SetVoltageForFreq(s_Pll0_Freq);
    }

    return kStatus_PLL_Success;
}

/* 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;
    /* Power off PLL during setup changes */
    POWER_EnablePD(kPDRUNCFG_PD_PLL0);
    POWER_EnablePD(kPDRUNCFG_PD_PLL0_SSCG);

    /* Write PLL setup data */
    SYSCON->PLL0CTRL  = pSetup->pllctrl;
    SYSCON->PLL0NDEC  = pSetup->pllndec;
    SYSCON->PLL0NDEC  = pSetup->pllndec | (1UL << SYSCON_PLL0NDEC_NREQ_SHIFT); /* latch */
    SYSCON->PLL0PDEC  = pSetup->pllpdec;
    SYSCON->PLL0PDEC  = pSetup->pllpdec | (1UL << SYSCON_PLL0PDEC_PREQ_SHIFT); /* latch */
    SYSCON->PLL0SSCG0 = pSetup->pllsscg[0];
    SYSCON->PLL0SSCG1 = pSetup->pllsscg[1];
    SYSCON->PLL0SSCG1 =
        pSetup->pllsscg[1] | (1UL << SYSCON_PLL0SSCG1_MD_REQ_SHIFT) | (1UL << SYSCON_PLL0SSCG1_MREQ_SHIFT); /* latch */

    POWER_DisablePD(kPDRUNCFG_PD_PLL0);
    POWER_DisablePD(kPDRUNCFG_PD_PLL0_SSCG);

    if ((pSetup->flags & PLL_SETUPFLAG_WAITLOCK) != 0UL)
    {
        if ((SYSCON->PLL0SSCG1 & SYSCON_PLL0SSCG1_MDIV_EXT_MASK) != 0UL) /* normal mode */
        {
            inRate = CLOCK_GetPLL0InClockRate();
            prediv = findPll0PreDiv();
            /* Adjust input clock */
            clkRate = inRate / prediv;
            /* The lock signal is only reliable between fref[2] :100 kHz to 20 MHz. */
            if ((clkRate >= 100000UL) && (clkRate <= 20000000UL))
            {
                while (CLOCK_IsPLL0Locked() == false)
                {
                }
            }
            else
            {
                SDK_DelayAtLeastUs(6000U,
                                   SDK_DEVICE_MAXIMUM_CPU_CLOCK_FREQUENCY); /* software should use a 6 ms time interval
                                                                               to insure the PLL will be stable */
            }
        }
        else /* spread spectrum mode */
        {
            SDK_DelayAtLeastUs(6000U,
                               SDK_DEVICE_MAXIMUM_CPU_CLOCK_FREQUENCY); /* software should use a 6 ms time interval to
                                                                           insure the PLL will be stable */
        }
    }

    /* Update current programmed PLL rate var */
    s_Pll0_Freq = pSetup->pllRate;

    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;
    /* Power off PLL during setup changes */
    POWER_EnablePD(kPDRUNCFG_PD_PLL1);

    /* Write PLL setup data */
    SYSCON->PLL1CTRL = pSetup->pllctrl;
    SYSCON->PLL1NDEC = pSetup->pllndec;
    SYSCON->PLL1NDEC = pSetup->pllndec | (1UL << SYSCON_PLL1NDEC_NREQ_SHIFT); /* latch */
    SYSCON->PLL1PDEC = pSetup->pllpdec;
    SYSCON->PLL1PDEC = pSetup->pllpdec | (1UL << SYSCON_PLL1PDEC_PREQ_SHIFT); /* latch */
    SYSCON->PLL1MDEC = pSetup->pllmdec;
    SYSCON->PLL1MDEC = pSetup->pllmdec | (1UL << SYSCON_PLL1MDEC_MREQ_SHIFT); /* latch */

    POWER_DisablePD(kPDRUNCFG_PD_PLL1);

    if ((pSetup->flags & PLL_SETUPFLAG_WAITLOCK) != 0UL)
    {
        inRate = CLOCK_GetPLL1InClockRate();
        prediv = findPll1PreDiv();
        /* Adjust input clock */
        clkRate = inRate / prediv;
        /* The lock signal is only reliable between fref[2] :100 kHz to 20 MHz. */
        if ((clkRate >= 100000UL) && (clkRate <= 20000000UL))
        {
            while (CLOCK_IsPLL1Locked() == false)
            {
            }
        }
        else
        {
            SDK_DelayAtLeastUs(6000U,
                               SDK_DEVICE_MAXIMUM_CPU_CLOCK_FREQUENCY); /* software should use a 6 ms time interval to
                                                                           insure the PLL will be stable */
        }
    }

    /* Update current programmed PLL rate var */
    s_Pll1_Freq = pSetup->pllRate;

    return kStatus_PLL_Success;
}

/* Set PLL0 clock based on the input frequency and multiplier */
/*! brief    Set PLL0 output based on the multiplier and input frequency
 * param multiply_by : multiplier
 * param input_freq  : Clock input frequency of the PLL
 * return    Nothing
 * note  Unlike the Chip_Clock_SetupSystemPLLPrec() function, this
 * function does not disable or enable PLL power, wait for PLL lock,
 * or adjust system voltages. These must be done in the application.
 * The function will not alter any source clocks (ie, main systen clock)
 * that may use the PLL, so these should be setup prior to and after
 * exiting the function.
 */
void CLOCK_SetupPLL0Mult(uint32_t multiply_by, uint32_t input_freq)
{
    uint32_t cco_freq = input_freq * multiply_by;
    uint32_t pdec     = 1U;
    uint32_t selr;
    uint32_t seli;
    uint32_t selp;
    uint32_t mdec, ndec;

    while (cco_freq < 275000000U)
    {
        multiply_by <<= 1U; /* double value in each iteration */
        pdec <<= 1U;        /* correspondingly double pdec to cancel effect of double msel */
        cco_freq = input_freq * multiply_by;
    }

    selr = 0U;

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

    if (seli >= 63U)
    {
        seli = 63U;
    }

    {
        selp = 31U;
    }

    if (pdec > 1U)
    {
        pdec = pdec / 2U; /* Account for minus 1 encoding */
                          /* Translate P value */
    }

    mdec = (uint32_t)PLL_SSCG1_MDEC_VAL_SET(multiply_by);
    ndec = 0x1U; /* pre divide by 1 (hardcoded) */

    SYSCON->PLL0CTRL = SYSCON_PLL0CTRL_CLKEN_MASK | SYSCON_PLL0CTRL_BYPASSPOSTDIV(0) |
                       SYSCON_PLL0CTRL_BYPASSPOSTDIV2(0) | (selr << SYSCON_PLL0CTRL_SELR_SHIFT) |
                       (seli << SYSCON_PLL0CTRL_SELI_SHIFT) | (selp << SYSCON_PLL0CTRL_SELP_SHIFT);
    SYSCON->PLL0PDEC = pdec | (1UL << SYSCON_PLL0PDEC_PREQ_SHIFT); /* set Pdec value and assert preq */
    SYSCON->PLL0NDEC = ndec | (1UL << SYSCON_PLL0NDEC_NREQ_SHIFT); /* set Pdec value and assert preq */
    SYSCON->PLL0SSCG1 =
        mdec | (1UL << SYSCON_PLL0SSCG1_MREQ_SHIFT); /* select non sscg MDEC value, assert mreq and select mdec value */
}

/* Enable USB DEVICE FULL SPEED clock */
/*! brief Enable USB Device FS clock.
 * param src : clock source
 * param freq: clock frequency
 * Enable USB Device Full Speed clock.
 */
bool CLOCK_EnableUsbfs0DeviceClock(clock_usbfs_src_t src, uint32_t freq)
{
    bool ret = true;

    CLOCK_DisableClock(kCLOCK_Usbd0);

    if (kCLOCK_UsbfsSrcFro == src)
    {
        switch (freq)
        {
            case 96000000U:
                CLOCK_SetClkDiv(kCLOCK_DivUsb0Clk, 2, false); /*!< Div by 2 to get 48MHz, no divider reset */
                break;

            default:
                ret = false;
                break;
        }
        /* Turn ON FRO HF */
        POWER_DisablePD(kPDRUNCFG_PD_FRO192M);
        /* Enable FRO 96MHz output */
        ANACTRL->FRO192M_CTRL = ANACTRL->FRO192M_CTRL | ANACTRL_FRO192M_CTRL_ENA_96MHZCLK_MASK;
        /* Select FRO 96 or 48 MHz */
        CLOCK_AttachClk(kFRO_HF_to_USB0);
    }
    else
    {
        /*!< Configure XTAL32M */
        POWER_DisablePD(kPDRUNCFG_PD_XTALHF);                                 /* Ensure XTAL32M is powered */
        POWER_DisablePD(kPDRUNCFG_PD_LDOXTALHF);                              /* Ensure XTAL32M is powered */
        (void)CLOCK_SetupExtClocking(16000000U);                              /* Enable clk_in clock */
        SYSCON->CLOCK_CTRL |= SYSCON_CLOCK_CTRL_CLKIN_ENA_MASK;               /* Enable clk_in from XTAL32M clock  */
        ANACTRL->XO32M_CTRL |= ANACTRL_XO32M_CTRL_ENABLE_SYSTEM_CLK_OUT_MASK; /* Enable clk_in to system  */

        /*!< Set up PLL1 */
        POWER_DisablePD(kPDRUNCFG_PD_PLL1);
        CLOCK_AttachClk(kEXT_CLK_to_PLL1); /*!< Switch PLL1CLKSEL to EXT_CLK */
        const pll_setup_t pll1Setup = {
            .pllctrl = SYSCON_PLL1CTRL_CLKEN_MASK | SYSCON_PLL1CTRL_SELI(19U) | SYSCON_PLL1CTRL_SELP(9U),
            .pllndec = SYSCON_PLL1NDEC_NDIV(1U),
            .pllpdec = SYSCON_PLL1PDEC_PDIV(5U),
            .pllmdec = SYSCON_PLL1MDEC_MDIV(30U),
            .pllRate = 48000000U,
            .flags   = PLL_SETUPFLAG_WAITLOCK};
        (void)CLOCK_SetPLL1Freq(&pll1Setup);

        CLOCK_SetClkDiv(kCLOCK_DivUsb0Clk, 1U, false);
        CLOCK_AttachClk(kPLL1_to_USB0);
        SDK_DelayAtLeastUs(50U, SDK_DEVICE_MAXIMUM_CPU_CLOCK_FREQUENCY);
    }
    CLOCK_EnableClock(kCLOCK_Usbd0);

    return ret;
}

/* Enable USB HOST FULL SPEED clock */
/*! brief Enable USB HOST FS clock.
 * param src : clock source
 * param freq: clock frequency
 * Enable USB HOST Full Speed clock.
 */
bool CLOCK_EnableUsbfs0HostClock(clock_usbfs_src_t src, uint32_t freq)
{
    bool ret = true;

    if (kCLOCK_UsbfsSrcFro == src)
    {
        switch (freq)
        {
            case 96000000U:
                CLOCK_SetClkDiv(kCLOCK_DivUsb0Clk, 2, false); /*!< Div by 2 to get 48MHz, no divider reset */
                break;

            default:
                ret = false;
                break;
        }
        /* Turn ON FRO HF */
        POWER_DisablePD(kPDRUNCFG_PD_FRO192M);
        /* Enable FRO 96MHz output */
        ANACTRL->FRO192M_CTRL = ANACTRL->FRO192M_CTRL | ANACTRL_FRO192M_CTRL_ENA_96MHZCLK_MASK;
        /* Select FRO 96 MHz */
        CLOCK_AttachClk(kFRO_HF_to_USB0);
    }
    else
    {
        /*!< Configure XTAL32M */
        POWER_DisablePD(kPDRUNCFG_PD_XTALHF);                                 /* Ensure XTAL32M is powered */
        POWER_DisablePD(kPDRUNCFG_PD_LDOXTALHF);                              /* Ensure XTAL32M is powered */
        (void)CLOCK_SetupExtClocking(16000000U);                              /* Enable clk_in clock */
        SYSCON->CLOCK_CTRL |= SYSCON_CLOCK_CTRL_CLKIN_ENA_MASK;               /* Enable clk_in from XTAL32M clock  */
        ANACTRL->XO32M_CTRL |= ANACTRL_XO32M_CTRL_ENABLE_SYSTEM_CLK_OUT_MASK; /* Enable clk_in to system  */

        /*!< Set up PLL1 */
        POWER_DisablePD(kPDRUNCFG_PD_PLL1);
        CLOCK_AttachClk(kEXT_CLK_to_PLL1); /*!< Switch PLL1CLKSEL to EXT_CLK */
        const pll_setup_t pll1Setup = {
            .pllctrl = SYSCON_PLL1CTRL_CLKEN_MASK | SYSCON_PLL1CTRL_SELI(19U) | SYSCON_PLL1CTRL_SELP(9U),
            .pllndec = SYSCON_PLL1NDEC_NDIV(1U),
            .pllpdec = SYSCON_PLL1PDEC_PDIV(5U),
            .pllmdec = SYSCON_PLL1MDEC_MDIV(30U),
            .pllRate = 48000000U,
            .flags   = PLL_SETUPFLAG_WAITLOCK};
        (void)CLOCK_SetPLL1Freq(&pll1Setup);

        CLOCK_SetClkDiv(kCLOCK_DivUsb0Clk, 1U, false);
        CLOCK_AttachClk(kPLL1_to_USB0);
        SDK_DelayAtLeastUs(50U, SDK_DEVICE_MAXIMUM_CPU_CLOCK_FREQUENCY);
    }
    CLOCK_EnableClock(kCLOCK_Usbhmr0);
    CLOCK_EnableClock(kCLOCK_Usbhsl0);

    return ret;
}

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

/* Sets board-specific trim values for High Frequency crystal oscillator */
/*! brief Sets board-specific trim values for High Frequency crystal oscillator.
 * param pi32_hfXtalIecLoadpF_x100 : Load capacitance, pF x 100. For example, 6pF becomes 600, 1.2pF becomes 120
 * param pi32_hfXtalPPcbParCappF_x100 : PCB +ve parasitic capacitance, pF x 100. For example, 6pF becomes 600, 1.2pF
 * becomes 120
 * param pi32_hfXtalNPcbParCappF_x100 : PCB -ve parasitic capacitance, pF x 100. For example, 6pF becomes 600, 1.2pF
 * becomes 120
 * return   none
 * note     Following default Values can be used:
 *          pi32_32MfXtalIecLoadpF_x100 Load capacitance, pF x 100 : 600
 *          pi32_32MfXtalPPcbParCappF_x100 PCB +ve parasitic capacitance, pF x 100 : 20
 *          pi32_32MfXtalNPcbParCappF_x100 PCB -ve parasitic capacitance, pF x 100 : 40
 * Sets board-specific trim values for High Frequency crystal oscillator.
 */
void CLOCK_XtalHfCapabankTrim(int32_t pi32_hfXtalIecLoadpF_x100,
                              int32_t pi32_hfXtalPPcbParCappF_x100,
                              int32_t pi32_hfXtalNPcbParCappF_x100)
{
    uint32_t u32XOTrimValue;
    uint8_t u8IECXinCapCal6pF, u8IECXinCapCal8pF, u8IECXoutCapCal6pF, u8IECXoutCapCal8pF, u8XOSlave;
    int32_t iaXin_x4, ibXin, iaXout_x4, ibXout;
    int32_t iXOCapInpF_x100, iXOCapOutpF_x100;
    uint8_t u8XOCapInCtrl, u8XOCapOutCtrl;
    uint32_t u32RegVal;

    /* Enable and set LDO, if not already done */
    CLOCK_SetXtalHfLdo();
    /* Get Cal values from Flash */
    u32XOTrimValue = GET_HFXO_TRIM();
    /* Check validity and apply */
    if ((u32XOTrimValue & 1) && ((u32XOTrimValue >> 15) & 1))
    {
        /* These fields are 7 bits, unsigned */
        u8IECXinCapCal6pF  = (u32XOTrimValue >> 1) & 0x7f;
        u8IECXinCapCal8pF  = (u32XOTrimValue >> 8) & 0x7f;
        u8IECXoutCapCal6pF = (u32XOTrimValue >> 16) & 0x7f;
        u8IECXoutCapCal8pF = (u32XOTrimValue >> 23) & 0x7f;
        /* This field is 1 bit */
        u8XOSlave = (u32XOTrimValue >> 30) & 0x1;
        /* Linear fit coefficients calculation */
        iaXin_x4  = (int)u8IECXinCapCal8pF - (int)u8IECXinCapCal6pF;
        ibXin     = (int)u8IECXinCapCal6pF - iaXin_x4 * 3;
        iaXout_x4 = (int)u8IECXoutCapCal8pF - (int)u8IECXoutCapCal6pF;
        ibXout    = (int)u8IECXoutCapCal6pF - iaXout_x4 * 3;
    }
    else
    {
        iaXin_x4  = 20;  // gain in LSB/pF
        ibXin     = -9;  // offset in LSB
        iaXout_x4 = 20;  // gain in LSB/pF
        ibXout    = -13; // offset in LSB
        u8XOSlave = 0;
    }
    /* In & out load cap calculation with derating */
    iXOCapInpF_x100 =
        2 * pi32_hfXtalIecLoadpF_x100 - pi32_hfXtalNPcbParCappF_x100 + 39 * (XO_SLAVE_EN - u8XOSlave) - 15;
    iXOCapOutpF_x100 = 2 * pi32_hfXtalIecLoadpF_x100 - pi32_hfXtalPPcbParCappF_x100 - 21;
    /* In & out XO_OSC_CAP_Code_CTRL calculation, with rounding */
    u8XOCapInCtrl  = (uint8_t)(((iXOCapInpF_x100 * iaXin_x4 + ibXin * 400) + 200) / 400);
    u8XOCapOutCtrl = (uint8_t)(((iXOCapOutpF_x100 * iaXout_x4 + ibXout * 400) + 200) / 400);
    /* Read register and clear fields to be written */
    u32RegVal = ANACTRL->XO32M_CTRL;
    u32RegVal &= ~(ANACTRL_XO32M_CTRL_OSC_CAP_IN_MASK | ANACTRL_XO32M_CTRL_OSC_CAP_OUT_MASK);
    /* Configuration of 32 MHz XO output buffers */
#if (XO_SLAVE_EN == 0)
    u32RegVal &= ~(ANACTRL_XO32M_CTRL_SLAVE_MASK | ANACTRL_XO32M_CTRL_ACBUF_PASS_ENABLE_MASK);
#else
    u32RegVal |= ANACTRL_XO32M_CTRL_SLAVE_MASK | ANACTRL_XO32M_CTRL_ACBUF_PASS_ENABLE_MASK;
#endif
    /* XO_OSC_CAP_Code_CTRL to XO_OSC_CAP_Code conversion */
    u32RegVal |= CLOCK_u8OscCapConvert(u8XOCapInCtrl, 13) << ANACTRL_XO32M_CTRL_OSC_CAP_IN_SHIFT;
    u32RegVal |= CLOCK_u8OscCapConvert(u8XOCapOutCtrl, 13) << ANACTRL_XO32M_CTRL_OSC_CAP_OUT_SHIFT;
    /* Write back to register */
    ANACTRL->XO32M_CTRL = u32RegVal;
}

/* Sets board-specific trim values for 32kHz XTAL */
/*! brief Sets board-specific trim values for 32kHz XTAL.
 * param pi32_32kfXtalIecLoadpF_x100 : Load capacitance, pF x 100. For example, 6pF becomes 600, 1.2pF becomes 120
 * param pi32_32kfXtalPPcbParCappF_x100 : PCB +ve parasitic capacitance, pF x 100. For example, 6pF becomes 600, 1.2pF
 * becomes 120
 * param pi32_32kfXtalNPcbParCappF_x100 : PCB -ve parasitic capacitance, pF x 100. For example, 6pF becomes 600, 1.2pF
 * becomes 120
 * return   none
 * note     Following default Values can be used:
 *          pi32_32kfXtalIecLoadpF_x100 Load capacitance, pF x 100 : 600
 *          pi32_32kfXtalPPcbParCappF_x100 PCB +ve parasitic capacitance, pF x 100 : 40
 *          pi32_32kfXtalNPcbParCappF_x100 PCB -ve parasitic capacitance, pF x 100 : 40
 * Sets board-specific trim values for 32kHz XTAL.
 */
void CLOCK_Xtal32khzCapabankTrim(int32_t pi32_32kfXtalIecLoadpF_x100,
                                 int32_t pi32_32kfXtalPPcbParCappF_x100,
                                 int32_t pi32_32kfXtalNPcbParCappF_x100)
{
    uint32_t u32XOTrimValue;
    uint8_t u8IECXinCapCal6pF, u8IECXinCapCal8pF, u8IECXoutCapCal6pF, u8IECXoutCapCal8pF;
    int32_t iaXin_x4, ibXin, iaXout_x4, ibXout;
    int32_t iXOCapInpF_x100, iXOCapOutpF_x100;
    uint8_t u8XOCapInCtrl, u8XOCapOutCtrl;
    uint32_t u32RegVal;
    /* Get Cal values from Flash */
    u32XOTrimValue = GET_32KXO_TRIM();
    /* check validity and apply */
    if ((u32XOTrimValue & 1) && ((u32XOTrimValue >> 15) & 1))
    {
        /* These fields are 7 bits, unsigned */
        u8IECXinCapCal6pF  = (u32XOTrimValue >> 1) & 0x7f;
        u8IECXinCapCal8pF  = (u32XOTrimValue >> 8) & 0x7f;
        u8IECXoutCapCal6pF = (u32XOTrimValue >> 16) & 0x7f;
        u8IECXoutCapCal8pF = (u32XOTrimValue >> 23) & 0x7f;
        /* Linear fit coefficients calculation */
        iaXin_x4  = (int)u8IECXinCapCal8pF - (int)u8IECXinCapCal6pF;
        ibXin     = (int)u8IECXinCapCal6pF - iaXin_x4 * 3;
        iaXout_x4 = (int)u8IECXoutCapCal8pF - (int)u8IECXoutCapCal6pF;
        ibXout    = (int)u8IECXoutCapCal6pF - iaXout_x4 * 3;
    }
    else
    {
        iaXin_x4  = 16; // gain in LSB/pF
        ibXin     = 12; // offset in LSB
        iaXout_x4 = 16; // gain in LSB/pF
        ibXout    = 11; // offset in LSB
    }

    /* In & out load cap calculation with derating */
    iXOCapInpF_x100  = 2 * pi32_32kfXtalIecLoadpF_x100 - pi32_32kfXtalNPcbParCappF_x100 - 130;
    iXOCapOutpF_x100 = 2 * pi32_32kfXtalIecLoadpF_x100 - pi32_32kfXtalPPcbParCappF_x100 - 41;

    /* In & out XO_OSC_CAP_Code_CTRL calculation, with rounding */
    u8XOCapInCtrl  = (uint8_t)(((iXOCapInpF_x100 * iaXin_x4 + ibXin * 400) + 200) / 400);
    u8XOCapOutCtrl = (uint8_t)(((iXOCapOutpF_x100 * iaXout_x4 + ibXout * 400) + 200) / 400);

    /* Read register and clear fields to be written */
    u32RegVal = PMC->XTAL32K;
    u32RegVal &= ~(PMC_XTAL32K_CAPBANKIN_MASK | PMC_XTAL32K_CAPBANKOUT_MASK);

    /* XO_OSC_CAP_Code_CTRL to XO_OSC_CAP_Code conversion */
    u32RegVal |= CLOCK_u8OscCapConvert(u8XOCapInCtrl, 23) << PMC_XTAL32K_CAPBANKIN_SHIFT;
    u32RegVal |= CLOCK_u8OscCapConvert(u8XOCapOutCtrl, 23) << PMC_XTAL32K_CAPBANKOUT_SHIFT;

    /* Write back to register */
    PMC->XTAL32K = u32RegVal;
}

/* Enables and sets LDO for High Frequency crystal oscillator. */
/*! brief Enables and sets LDO for High Frequency crystal oscillator.
 * return   none
 * Sets Enables and sets LDO for High Frequency crystal oscillator.
 */
void CLOCK_SetXtalHfLdo(void)
{
    uint32_t temp;
    const uint32_t u32Mask =
        (ANACTRL_LDO_XO32M_VOUT_MASK | ANACTRL_LDO_XO32M_IBIAS_MASK | ANACTRL_LDO_XO32M_STABMODE_MASK);

    const uint32_t u32Value =
        (ANACTRL_LDO_XO32M_VOUT(0x5) | ANACTRL_LDO_XO32M_IBIAS(0x2) | ANACTRL_LDO_XO32M_STABMODE(0x1));

    /* Enable & set-up XTAL 32 MHz clock LDO */
    temp = ANACTRL->LDO_XO32M;

    if ((temp & u32Mask) != u32Value)
    {
        temp &= ~u32Mask;

        /*
         * Enable the XTAL32M LDO
         * Adjust the output voltage level, 0x5 for 1.1V
         * Adjust the biasing current, 0x2 value
         * Stability configuration, 0x1 default mode
         */
        temp |= u32Value;

        ANACTRL->LDO_XO32M = temp;

        /* Delay for LDO to be up */
        // CLOCK_uDelay(20);
    }

    /* Enable LDO XO32M */
    PMC->PDRUNCFGCLR0 = PMC_PDRUNCFG0_PDEN_LDOXTALHF_MASK;
}

/**
 * @brief
 * @param
 * @return
 */
static uint8_t CLOCK_u8OscCapConvert(uint8_t u8OscCap, uint8_t u8CapBankDiscontinuity)
{
    /* Compensate for discontinuity in the capacitor banks */
    if (u8OscCap < 64)
    {
        if (u8OscCap >= u8CapBankDiscontinuity)
        {
            u8OscCap -= u8CapBankDiscontinuity;
        }
        else
        {
            u8OscCap = 0;
        }
    }
    else
    {
        if (u8OscCap <= (127 - u8CapBankDiscontinuity))
        {
            u8OscCap += u8CapBankDiscontinuity;
        }
        else
        {
            u8OscCap = 127;
        }
    }
    return u8OscCap;
}

/*!
 * brief  Initialize the trim value for FRO HF .
 */
void CLOCK_FroHfTrim(void)
{
    volatile unsigned int Fro192mCtrlEfuse = 0;
    Fro192mCtrlEfuse                       = ANACTRL->FRO192M_CTRL;
    ANACTRL->ANALOG_CTRL_CFG               = ANACTRL->ANALOG_CTRL_CFG | ANACTRL_ANALOG_CTRL_CFG_FRO192M_TRIM_SRC_MASK;
    ANACTRL->FRO192M_CTRL                  = ANACTRL_FRO192M_CTRL_WRTRIM_MASK | Fro192mCtrlEfuse;
}
