/*
 * Copyright (c) 2017 , NXP
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * o Redistributions of source code must retain the above copyright notice, this list
 *   of conditions and the following disclaimer.
 *
 * o Redistributions in binary form must reproduce the above copyright notice, this
 *   list of conditions and the following disclaimer in the documentation and/or
 *   other materials provided with the distribution.
 *
 * o Neither the name of the copyright holder nor the names of its
 *   contributors may be used to endorse or promote products derived from this
 *   software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

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

#define USB_NVALMAX (0x4U)
#define USB_PVALMAX (0x8U)
#define USB_MVALMAX (0x100U)

#define PLL_MAX_N_DIV 0x100U
#define USB_PLL_MAX_N_DIV 0x100U

#define INDEX_SECTOR_TRIM48 ((uint32_t *)0x01000448U) /* Need to be corrected if have new index */
#define INDEX_SECTOR_TRIM96 ((uint32_t *)0x0100044CU)
/*--------------------------------------------------------------------------
!!! If required these #defines can be moved to chip library file
----------------------------------------------------------------------------*/

#define PLL_MDEC_VAL_P (0U)                          /*!<  MDEC is in bits  16 downto 0 */
#define PLL_MDEC_VAL_M (0x1FFFFUL << PLL_MDEC_VAL_P) /*!<  NDEC is in bits  9 downto 0 */
#define PLL_NDEC_VAL_P (0U)                          /*!<  NDEC is in bits  9:0 */
#define PLL_NDEC_VAL_M (0x3FFUL << PLL_NDEC_VAL_P)
#define PLL_PDEC_VAL_P (0U) /*!<  PDEC is in bits 6:0 */
#define PLL_PDEC_VAL_M (0x7FUL << PLL_PDEC_VAL_P)

#define PLL_MIN_CCO_FREQ_MHZ (275000000U)
#define PLL_MAX_CCO_FREQ_MHZ (550000000U)
#define PLL_LOWER_IN_LIMIT (4000U) /*!<  Minimum PLL input rate */
#define PLL_MIN_IN_SSMODE (2000000U)
#define PLL_MAX_IN_SSMODE (4000000U)

/*!<  Middle of the range values for spread-spectrum */
#define PLL_SSCG_MF_FREQ_VALUE 4U
#define PLL_SSCG_MC_COMP_VALUE 2U
#define PLL_SSCG_MR_DEPTH_VALUE 4U
#define PLL_SSCG_DITHER_VALUE 0U

/*!<  USB PLL CCO MAX AND MIN FREQ */
#define USB_PLL_MIN_CCO_FREQ_MHZ (156000000U)
#define USB_PLL_MAX_CCO_FREQ_MHZ (320000000U)
#define USB_PLL_LOWER_IN_LIMIT (1000000U) /*!<  Minimum PLL input rate */

#define USB_PLL_MSEL_VAL_P (0U) /*!<  MSEL is in bits  7 downto 0 */
#define USB_PLL_MSEL_VAL_M (0xFFU)
#define USB_PLL_PSEL_VAL_P (8U) /*!<  PDEC is in bits 9:8 */
#define USB_PLL_PSEL_VAL_M (0x3U)
#define USB_PLL_NSEL_VAL_P (10U) /*!<  NDEC is in bits  11:10 */
#define USB_PLL_NSEL_VAL_M (0x3U)

/*!<  SWITCH USB POSTDIVIDER FOR REGITSER WRITING */
#define SWITCH_USB_PSEL(x) ((x == 0x0U) ? 0x1U : (x == 0x1U) ? 0x02U : (x == 0x2U) ? 0x4U : (x == 3U) ? 0x8U : 0U)

/*!<  SYS PLL NDEC reg */
#define PLL_NDEC_VAL_SET(value) (((unsigned long)(value) << PLL_NDEC_VAL_P) & PLL_NDEC_VAL_M)
/*!<  SYS PLL PDEC reg */
#define PLL_PDEC_VAL_SET(value) (((unsigned long)(value) << PLL_PDEC_VAL_P) & PLL_PDEC_VAL_M)
/*!<  SYS PLL MDEC reg */
#define PLL_MDEC_VAL_SET(value) (((unsigned long)(value) << PLL_MDEC_VAL_P) & PLL_MDEC_VAL_M)

/*!<  SYS PLL NSEL reg */
#define USB_PLL_NSEL_VAL_SET(value) (((unsigned long)(value)&USB_PLL_NSEL_VAL_M) << USB_PLL_NSEL_VAL_P)
/*!<  SYS PLL PSEL reg */
#define USB_PLL_PSEL_VAL_SET(value) (((unsigned long)(value)&USB_PLL_PSEL_VAL_M) << USB_PLL_PSEL_VAL_P)
/*!<  SYS PLL MSEL reg */
#define USB_PLL_MSEL_VAL_SET(value) (((unsigned long)(value)&USB_PLL_MSEL_VAL_M) << USB_PLL_MSEL_VAL_P)

/* Saved value of PLL output rate, computed whenever needed to save run-time
   computation on each call to retrive the PLL rate. */
static uint32_t s_Pll_Freq;
static uint32_t s_Usb_Pll_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. */
const uint32_t g_I2S_Mclk_Freq = 0U;
const uint32_t g_Ext_Clk_Freq = 12000000U;

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

/*******************************************************************************
 * Prototypes
 ******************************************************************************/
/* Find encoded NDEC value for raw N value, max N = NVALMAX */
static uint32_t pllEncodeN(uint32_t N);
/* Find decoded N value for raw NDEC value */
static uint32_t pllDecodeN(uint32_t NDEC);
/* Find encoded PDEC value for raw P value, max P = PVALMAX */
 static uint32_t pllEncodeP(uint32_t P);
/* Find decoded P value for raw PDEC value */
static uint32_t pllDecodeP(uint32_t PDEC);
/* Find encoded MDEC value for raw M value, max M = MVALMAX */
static uint32_t pllEncodeM(uint32_t M);
/* Find decoded M value for raw MDEC value */
static uint32_t pllDecodeM(uint32_t MDEC);
/* Find SELP, SELI, and SELR values for raw M value, max M = MVALMAX */
static void pllFindSel(uint32_t M, uint32_t *pSelP, uint32_t *pSelI, uint32_t *pSelR);
/* Get predivider (N) from PLL NDEC setting */
static uint32_t findPllPreDiv(uint32_t ctrlReg, uint32_t nDecReg);
/* Get postdivider (P) from PLL PDEC setting */
static uint32_t findPllPostDiv(uint32_t ctrlReg, uint32_t pDecReg);
/* Get multiplier (M) from PLL MDEC and BYPASS_FBDIV2 settings */
static uint32_t findPllMMult(uint32_t ctrlReg, uint32_t mDecReg);
/* Convert the binary to fractional part */
//static double Binary2Fractional(uint32_t binaryPart);
/* Calculate the powerTimes' power of 2 */
static uint32_t power2Cal(uint32_t powerTimes);
/* 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);

/* Update local PLL rate variable */
static void CLOCK_GetPLL0OutFromSetupUpdate(pll_setup_t *pSetup);

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

/* Clock Selection for IP */
void CLOCK_AttachClk(clock_attach_id_t connection)
{
    uint8_t mux;
    uint8_t sel;
    uint16_t item;
    uint32_t i;
    volatile uint32_t *pClkSel;

    pClkSel = &(SYSCON->SYSTICKCLKSELX[0]);

    if (connection != kNONE_to_NONE)
    {
        for (i = 0U; i < 2U; i++)
        {
            if (connection == 0U)
            {
                break;
            }
            item = (uint16_t)GET_ID_ITEM(connection);
            if (item)
            {
                mux = GET_ID_ITEM_MUX(item);
                sel = GET_ID_ITEM_SEL(item);
                if (mux == CM_RTCOSC32KCLKSEL)
                {
                    PMC->RTCOSC32K |= sel;
                }
                else
                {
                    pClkSel[mux] = sel;
                }
            }
            connection = GET_ID_NEXT_ITEM(connection); /* pick up next descriptor */
        }
    }
}

/* Return the actual clock attach id */
clock_attach_id_t CLOCK_GetClockAttachId(clock_attach_id_t attachId)
{
    uint8_t mux;
    uint8_t actualSel;
    uint32_t i;
    uint32_t actualAttachId = 0U;
    uint32_t selector = GET_ID_SELECTOR(attachId);
    volatile uint32_t *pClkSel;

    pClkSel = &(SYSCON->SYSTICKCLKSELX[0]);

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

    for (i = 0U; i < 2U; i++)
    {
        mux = GET_ID_ITEM_MUX(attachId);
        if (attachId)
        {
            if (mux == CM_RTCOSC32KCLKSEL)
            {
                actualSel = PMC->RTCOSC32K;
            }
            else
            {
                actualSel = pClkSel[mux];
            }
            
            /* Consider the combination of two registers */
            actualAttachId |= CLK_ATTACH_ID(mux, actualSel, i);
        }
        attachId = GET_ID_NEXT_ITEM(attachId); /*!<  pick up next descriptor */
    }
		
    /* Clean unmeaningful selection */
    if((GET_ID_ITEM_SEL(actualAttachId >> 12U) != (selector >> 24U)) && GET_ID_NEXT_ITEM(actualAttachId))
    {
        CLR_ID_ITEM_SEL(actualAttachId);
    }
    actualAttachId |= selector;

    return (clock_attach_id_t)actualAttachId;
}

/* Set IP Clock Divider */
void CLOCK_SetClkDiv(clock_div_name_t div_name, uint32_t divided_by_value, bool reset)
{
    volatile uint32_t *pClkDiv;

    pClkDiv = &(SYSCON->SYSTICKCLKDIV0);
    if (reset)
    {
        pClkDiv[div_name] = 1U << 29U;
    }
    if (divided_by_value == 0U) /*!<  halt */
    {
        pClkDiv[div_name] = 1U << 30U;
    }
    else
    {
        pClkDiv[div_name] = (divided_by_value - 1U);
    }
}

/* Set RTC 1KHz Clock Divider */
void CLOCK_SetRtc1khzClkDiv(uint32_t divided_by_value)
{
    PMC->RTCOSC32K |= (((divided_by_value - 28U) << PMC_RTCOSC32K_CLK1KHZDIV_SHIFT) | PMC_RTCOSC32K_CLK1KHZDIV_MASK);
}

/* Set RTC 1KHz Clock Divider */
void CLOCK_SetRtc1hzClkDiv(uint32_t divided_by_value)
{
    if (divided_by_value == 0U) /*!<  halt */
    {
        PMC->RTCOSC32K |= (1U << PMC_RTCOSC32K_CLK1HZDIVHALT_SHIFT);
    }
    else
    {
        PMC->RTCOSC32K |=
            (((divided_by_value - 31744U) << PMC_RTCOSC32K_CLK1HZDIV_SHIFT) | PMC_RTCOSC32K_CLK1HZDIV_MASK);
    }
}

/* Set FRO Clocking */
status_t CLOCK_SetupFROClocking(uint32_t iFreq)
{
    if ((iFreq != 12000000U) && (iFreq != 48000000U) && (iFreq != 96000000U))
    {
        return kStatus_Fail;
    }
    /* Enable Analog Control module */
    SYSCON->PRESETCTRLCLR[2] = (1U << SYSCON_PRESETCTRL2_ANALOG_CTRL_RST_SHIFT);
    SYSCON->AHBCLKCTRLSET[2] = SYSCON_AHBCLKCTRL2_ANALOG_CTRL_MASK;
    /* Power up the FRO192M */
    POWER_DisablePD(kPDRUNCFG_PD_FRO192M);

    if (iFreq == 96000000U)
    {
        ANACTRL->FRO192M_CTRL |= ANACTRL_FRO192M_CTRL_ENA_96MHZCLK(1);
    }
    else if (iFreq == 48000000U)
    {
        ANACTRL->FRO192M_CTRL |= ANACTRL_FRO192M_CTRL_ENA_48MHZCLK(1);
    }
    else
    {
        ANACTRL->FRO192M_CTRL |= ANACTRL_FRO192M_CTRL_ENA_12MHZCLK(1);
    }
    return 0U;
}

/* Get CLOCK OUT Clk */
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:
            break;
    }
    return freq / ((SYSCON->CLKOUTDIV & 0xffU) + 1U);
}

/* Get ADC Clk */
uint32_t CLOCK_GetAdcClkFreq(void)
{
    uint32_t freq = 0U;

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

        default:
            break;
    }

    return freq / ((SYSCON->ADCCLKDIV & SYSCON_ADCCLKDIV_DIV_MASK) + 1U);
}

/* Get USB0 Clk */
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:
            break;
    }

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

/* Get USB1 Clk */
uint32_t CLOCK_GetUsb1ClkFreq(void)
{
    uint32_t freq = 0U;

    switch (SYSCON->USB1CLKSEL)
    {
        case 0U:
            freq = CLOCK_GetCoreSysClkFreq();
            break;
        case 1U:
            freq = CLOCK_GetPll0OutFreq();
            break;
        case 2U:
            freq = CLOCK_GetExtClkFreq();
            break;
        case 5U:
            freq = CLOCK_GetPll1OutFreq();
            break;
        case 7U:
            freq = 0U;
            break;

        default:
            break;
    }

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

/* Get MCLK Clk */
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:
            break;
    }

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

/* Get SCTIMER Clk */
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 5U:
            freq = CLOCK_GetI2SMClkFreq();
            break;
        case 7U:
            freq = 0U;
            break;

        default:
            break;
    }

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

/* Get SDIO Clk */
uint32_t CLOCK_GetSdioClkFreq(void)
{
    uint32_t freq = 0U;

    switch (SYSCON->SDIOCLKSEL)
    {
        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:
            break;
    }

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

/* Get FRO 12M Clk */
uint32_t CLOCK_GetFro12MFreq(void)
{
    return (PMC->PDRUNCFG0 & PMC_PDRUNCFG0_PDEN_FRO192M_MASK) ?
               0 : (ANACTRL->FRO192M_CTRL & ANACTRL_FRO192M_CTRL_ENA_12MHZCLK_MASK) ? 12000000U : 0U;
}

/* Get FRO 1M Clk */
uint32_t CLOCK_GetFro1MFreq(void)
{
    return 1000000U;
}

/* Get EXT OSC Clk */
uint32_t CLOCK_GetExtClkFreq(void)
{
    return g_Ext_Clk_Freq;
}

/* Get WATCH DOG Clk */
uint32_t CLOCK_GetWdtOscFreq(void)
{
    return CLOCK_GetFro1MFreq(); /* Needs to be corrected to check that WDT Clock is enabled */
}

/* Get HF FRO Clk */
uint32_t CLOCK_GetFroHfFreq(void)
{
    return (PMC->PDRUNCFG0 & PMC_PDSLEEPCFG0_PDEN_FRO192M_MASK) ?
               0 :
               ~(ANACTRL->FRO192M_CTRL & ANACTRL_FRO192M_CTRL_ENA_96MHZCLK_MASK) ? 0U : 96000000U;
}

/* Get SYSTEM PLL Clk */
uint32_t CLOCK_GetPll0OutFreq(void)
{
    return s_Pll_Freq;
}

/* Get USB PLL Clk */
uint32_t CLOCK_GetPll1OutFreq(void)
{
    return s_Usb_Pll_Freq;
}

/* Get RTC OSC Clk */
uint32_t CLOCK_GetOsc32KFreq(void)
{
    return CLK_RTC_32K_CLK; /* Needs to be corrected to check that RTC Clock is enabled */
}

/* Get MAIN Clk */
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
            {
            }
            break;
        case 1U:
            freq = CLOCK_GetPll0OutFreq();
            break;
        case 2U:
            freq = CLOCK_GetPll1OutFreq();
            break;

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

        default:
            break;
    }

    return freq;
}

/* Get I2S MCLK Clk */
uint32_t CLOCK_GetI2SMClkFreq(void)
{
    return g_I2S_Mclk_Freq;
}

/* Get Cap Touch Clk */
uint32_t CLOCK_GetCaptClkFreq(void)
{
    uint32_t freq = 0U;

    if (SYSCON->CAPTCLKSEL == 0U)
    {
        freq = CLOCK_GetFro12MFreq();
    }
    else if (SYSCON->CAPTCLKSEL == 1U)
    {
        freq = CLOCK_GetFro1MFreq();
    }
    else
    {
        freq = 0U;
    }
    
    return freq / ((SYSCON->CAPTCLKDIV & SYSCON_CAPTCLKDIV_DIV_MASK) + 1U);
}

/* Get FLEXCOMM input clock */
uint32_t CLOCK_GetFlexCommInputClock(uint32_t id)
{
    uint32_t freq = 0U;

    switch (SYSCON->FCCLKSELX[id])
    {
        case 0U:
            freq = CLOCK_GetCoreSysClkFreq();
            break;
        case 1U:
            freq = CLOCK_GetPll0OutFreq() / ((SYSCON->PLL0CLKDIV & 0xffU) + 1U);
            break;
        case 2U:
            freq = CLOCK_GetFro12MFreq();
            break;
        case 3U:
            freq = CLOCK_GetFroHfFreq() / ((SYSCON->FRO96MDIV & 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:
            break;
    }

    return freq;
}

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

    freq = CLOCK_GetFlexCommInputClock(id);
    return freq / (1 + (SYSCON->FLEXFRGXCTRL[id] & SYSCON_FLEXFRG0CTRL_MULT_MASK) / ((SYSCON->FLEXFRGXCTRL[id] & SYSCON_FLEXFRG0CTRL_DIV_MASK) + 1U));
}

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

    switch (SYSCON->HSLSPICLKSEL)
    {
        case 0U:
            freq = CLOCK_GetCoreSysClkFreq();
            break;
        case 1U:
            freq = CLOCK_GetPll0OutFreq() / ((SYSCON->PLL0CLKDIV & 0xffU) + 1U);
            break;
        case 2U:
            freq = CLOCK_GetFro12MFreq();
            break;
        case 3U:
            freq = CLOCK_GetFroHfFreq();
            break;
        case 4U:
            freq = CLOCK_GetFro1MFreq();
            break;
        case 6U:
            freq = CLOCK_GetOsc32KFreq();
            break;
        case 7U:
            freq = 0U;
            break;

        default:
            break;
    }

    return freq;
}

/* Get CTimer Clk */
uint32_t CLOCK_GetCTimerClkFreq(uint32_t id)
{
    uint32_t freq = 0U;

    switch (SYSCON->CTIMERCLKSELX[id])
    {
        case 0U:
            freq = CLOCK_GetCoreSysClkFreq();
            break;
        case 1U:
            freq = CLOCK_GetPll0OutFreq();
            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:
            break;
    }

    return freq;
}

/* Get Systick Clk */
uint32_t CLOCK_GetSystickClkFreq(uint32_t id)
{
    volatile uint32_t *pSystickClkDiv;
    pSystickClkDiv = &(SYSCON->SYSTICKCLKDIV0);
    uint32_t freq = 0U;

    switch (SYSCON->SYSTICKCLKSELX[id])
    {
        case 0U:
            freq = CLOCK_GetCoreSysClkFreq() / ((pSystickClkDiv[id] & 0xffU) + 1U);
            break;
        case 1U:
            freq = CLOCK_GetFro1MFreq();
            break;
        case 2U:
            freq = CLOCK_GetOsc32KFreq();
            break;
        case 7U:
            freq = 0U;
            break;

        default:
            break;
    }

    return freq;
}

/* Set FlexComm Clock */
uint32_t CLOCK_SetFlexCommClock(uint32_t id, uint32_t freq)
{
    uint32_t input = CLOCK_GetFlexCommClkFreq(id);
    uint32_t mul;

    if ((freq > 48000000) || (freq > input) || (input / freq >= 2))
    {
        /* FRG output frequency should be less than equal to 48MHz */
        return 0;
    }
    else
    {
        mul = ((uint64_t)(input - freq) * 256) / ((uint64_t)freq);
        SYSCON->FLEXFRGXCTRL[id] = (mul << 8U) | 0xFFU;
        return 1;
    }
}

/* Get IP Clk */
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_Adc:
            freq = CLOCK_GetAdcClkFreq();
            break;
        case kCLOCK_Usb0:
            freq = CLOCK_GetUsb0ClkFreq();
            break;
        case kCLOCK_Usb1:
            freq = CLOCK_GetUsb1ClkFreq();
            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_ExtClk:
            freq = CLOCK_GetExtClkFreq();
            break;
        case kCLOCK_Pll0Out:
            freq = CLOCK_GetPll0OutFreq();
            break;
        case kCLOCK_WdtOsc:
            freq = CLOCK_GetWdtOscFreq();
            break;
        case kCLOCK_Sct:
            freq = CLOCK_GetSctClkFreq();
            break;
        case kCLOCK_SDio:
            freq = CLOCK_GetSdioClkFreq();
            break;
        case kCLOCK_FlexI2S:
            freq = CLOCK_GetI2SMClkFreq();
            break;
        case kCLOCK_Flexcomm0:
            freq = CLOCK_GetFlexCommClkFreq(0U);
            break;
        case kCLOCK_Flexcomm1:
            freq = CLOCK_GetFlexCommClkFreq(1U);
            break;
        case kCLOCK_Flexcomm2:
            freq = CLOCK_GetFlexCommClkFreq(2U);
            break;
        case kCLOCK_Flexcomm3:
            freq = CLOCK_GetFlexCommClkFreq(3U);
            break;
        case kCLOCK_Flexcomm4:
            freq = CLOCK_GetFlexCommClkFreq(4U);
            break;
        case kCLOCK_Flexcomm5:
            freq = CLOCK_GetFlexCommClkFreq(5U);
            break;
        case kCLOCK_Flexcomm6:
            freq = CLOCK_GetFlexCommClkFreq(6U);
            break;
        case kCLOCK_Flexcomm7:
            freq = CLOCK_GetFlexCommClkFreq(7U);
            break;
        case kCLOCK_HsLspi:
            freq = CLOCK_GetHsLspiClkFreq();
            break;
        case kCLOCK_CTmier0:
            freq = CLOCK_GetCTimerClkFreq(0U);
            break;
        case kCLOCK_CTmier1:
            freq = CLOCK_GetCTimerClkFreq(1U);
            break;
        case kCLOCK_CTmier2:
            freq = CLOCK_GetCTimerClkFreq(2U);
            break;
        case kCLOCK_CTmier3:
            freq = CLOCK_GetCTimerClkFreq(3U);
            break;
        case kCLOCK_CTmier4:
            freq = CLOCK_GetCTimerClkFreq(4U);
            break;
        case kCLOCK_Systick0:
            freq = CLOCK_GetSystickClkFreq(0U);
            break;
        case kCLOCK_Systick1:
            freq = CLOCK_GetSystickClkFreq(1U);
            break;
        case kCLOCK_Capt:
            freq = CLOCK_GetCaptClkFreq();
            break;
        default:
            freq = 0U;
            break;
    }
    return freq;
}

/* Find encoded NDEC value for raw N value, max N = NVALMAX */
static uint32_t pllEncodeN(uint32_t N)
{
   uint32_t x, i;

   /* Find NDec */
   switch (N)
   {
       case 0U:
           x = 0x3FFU;
           break;

       case 1U:
           x = 0x302U;
           break;

       case 2U:
           x = 0x202U;
           break;

       default:
           x = 0x080U;
           for (i = N; i <= NVALMAX; i++)
           {
               x = (((x ^ (x >> 2U) ^ (x >> 3U) ^ (x >> 4U)) & 1U) << 7U) | ((x >> 1U) & 0x7FU);
           }
           break;
   }

   return x & (PLL_NDEC_VAL_M >> PLL_NDEC_VAL_P);
}

/* Find decoded N value for raw NDEC value */
static uint32_t pllDecodeN(uint32_t NDEC)
{
   uint32_t n, x, i;

   /* Find NDec */
   switch (NDEC)
   {
       case 0x3FFU:
           n = 0U;
           break;

       case 0x302U:
           n = 1U;
           break;

       case 0x202U:
           n = 2U;
           break;

       default:
           x = 0x080U;
           n = 0xFFFFFFFFU;
           for (i = NVALMAX; ((i >= 3U) && (n == 0xFFFFFFFFU)); i--)
           {
               x = (((x ^ (x >> 2U) ^ (x >> 3U) ^ (x >> 4U)) & 1U) << 7U) | ((x >> 1U) & 0x7FU);
               if ((x & (PLL_NDEC_VAL_M >> PLL_NDEC_VAL_P)) == NDEC)
               {
                   /* Decoded value of NDEC */
                   n = i;
               }
           }
           break;
   }

   return n;
}

/* Find encoded PDEC value for raw P value, max P = PVALMAX */
static uint32_t pllEncodeP(uint32_t P)
{
   uint32_t x, i;

   /* Find PDec */
   switch (P)
   {
       case 0U:
           x = 0x7FU;
           break;

       case 1U:
           x = 0x62U;
           break;

       case 2U:
           x = 0x42U;
           break;

       default:
           x = 0x10U;
           for (i = P; i <= PVALMAX; i++)
           {
               x = (((x ^ (x >> 2U)) & 1U) << 4U) | ((x >> 1U) & 0xFU);
           }
           break;
   }

   return x & (PLL_PDEC_VAL_M >> PLL_PDEC_VAL_P);
}

/* Find decoded P value for raw PDEC value */
static uint32_t pllDecodeP(uint32_t PDEC)
{
   uint32_t p, x, i;

   /* Find PDec */
   switch (PDEC)
   {
       case 0x7FU:
           p = 0U;
           break;

       case 0x62U:
           p = 1U;
           break;

       case 0x42U:
           p = 2U;
           break;

       default:
           x = 0x10U;
           p = 0xFFFFFFFFU;
           for (i = PVALMAX; ((i >= 3U) && (p == 0xFFFFFFFFU)); i--)
           {
               x = (((x ^ (x >> 2U)) & 1U) << 4U) | ((x >> 1U) & 0xFU);
               if ((x & (PLL_PDEC_VAL_M >> PLL_PDEC_VAL_P)) == PDEC)
               {
                   /* Decoded value of PDEC */
                   p = i;
               }
           }
           break;
   }

   return p;
}

/* Find encoded MDEC value for raw M value, max M = MVALMAX */
static uint32_t pllEncodeM(uint32_t M)
{
   uint32_t i, x;

   /* Find MDec */
   switch (M)
   {
       case 0U:
           x = 0x1FFFFU;
           break;

       case 1U:
           x = 0x18003U;
           break;

       case 2U:
           x = 0x10003U;
           break;

       default:
           x = 0x04000U;
           for (i = M; i <= MVALMAX; i++)
           {
               x = (((x ^ (x >> 1U)) & 1U) << 14U) | ((x >> 1U) & 0x3FFFU);
           }
           break;
   }

   return x & (PLL_MDEC_VAL_M >> PLL_MDEC_VAL_P);
}

/* Find decoded M value for raw MDEC value */
static uint32_t pllDecodeM(uint32_t MDEC)
{
   uint32_t m, i, x;

   /* Find MDec */
   switch (MDEC)
   {
       case 0x1FFFFU:
           m = 0U;
           break;

       case 0x18003U:
           m = 1U;
           break;

       case 0x10003U:
           m = 2U;
           break;

       default:
           x = 0x04000U;
           m = 0xFFFFFFFFU;
           for (i = MVALMAX; ((i >= 3U) && (m == 0xFFFFFFFFU)); i--)
           {
               x = (((x ^ (x >> 1U)) & 1) << 14U) | ((x >> 1U) & 0x3FFFU);
               if ((x & (PLL_MDEC_VAL_M >> PLL_MDEC_VAL_P)) == MDEC)
               {
                   /* Decoded value of MDEC */
                   m = i;
               }
           }
           break;
   }

   return m;
}

/* 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)
{
   /* bandwidth: compute selP from Multiplier */
   if (M < 60U)
   {
       *pSelP = (M >> 1U) + 1U;
   }
   else
   {
       *pSelP = PVALMAX - 1U;
   }

   /* bandwidth: compute selI from Multiplier */
   if (M > 16384U)
   {
       *pSelI = 1U;
   }
   else if (M > 8192U)
   {
       *pSelI = 2U;
   }
   else if (M > 2048U)
   {
       *pSelI = 4U;
   }
   else if (M >= 501U)
   {
       *pSelI = 8U;
   }
   else if (M >= 60U)
   {
       *pSelI = 4U * (1024U / (M + 9U));
   }
   else
   {
       *pSelI = (M & 0x3CU) + 4U;
   }

   if (*pSelI > ((0x3FUL << SYSCON_PLL0CTRL_SELI_SHIFT) >> SYSCON_PLL0CTRL_SELI_SHIFT))
   {
       *pSelI = ((0x3FUL << SYSCON_PLL0CTRL_SELI_SHIFT) >> SYSCON_PLL0CTRL_SELI_SHIFT);
   }

   *pSelR = 0U;
}

/* Get predivider (N) from PLL NDEC setting */
static uint32_t findPllPreDiv(uint32_t ctrlReg, uint32_t nDecReg)
{
   uint32_t preDiv = 1;

   /* Direct input is not used? */
   if ((ctrlReg & (1UL << SYSCON_PLL0CTRL_BYPASSPREDIV_SHIFT)) == 0U)
   {
       /* Decode NDEC value to get (N) pre divider */
       preDiv = pllDecodeN(nDecReg & 0x3FFU);
       if (preDiv == 0U)
       {
           preDiv = 1U;
       }
   }

   /* Adjusted by 1, directi is used to bypass */
   return preDiv;
}

/* Get postdivider (P) from PLL PDEC setting */
static uint32_t findPllPostDiv(uint32_t ctrlReg, uint32_t pDecReg)
{
   uint32_t postDiv = 1U;

   /* Direct input is not used? */
   if ((ctrlReg & SYSCON_PLL0CTRL_BYPASSPOSTDIV_MASK) == 0U)
   {
       /* Decode PDEC value to get (P) post divider */
       postDiv = 2U * pllDecodeP(pDecReg & 0x7FU);
       if (postDiv == 0U)
       {
           postDiv = 2U;
       }
   }

   /* Adjusted by 1, directo is used to bypass */
   return postDiv;
}

/* Get multiplier (M) from PLL MDEC and BYPASS_FBDIV2 settings */
static uint32_t findPllMMult(uint32_t ctrlReg, uint32_t mDecReg)
{
   uint32_t mMult = 1U;

   /* Decode MDEC value to get (M) multiplier */
   mMult = pllDecodeM(mDecReg & 0x1FFFFU);

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

   return mMult;
}

/* Calculate the powerTimes' power of 2 */
static uint32_t power2Cal(uint32_t powerTimes)
{
   if (powerTimes == 0)
       return 1;
   return 2 * power2Cal(powerTimes - 1);
}

/* Convert the binary to fractional part */
//static double Binary2Fractional(uint32_t binaryPart)
// {
//    double fractional = 0;
//    for (uint32_t i = 0; i <= 14; i++)
//    {
//        fractional += (double)((binaryPart >> i) & 0x1U) / (double)power2Cal(15 - i);
//    }
//    return fractional;
// }

/* 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 PLL output based on desired output rate.
* In this function, the it calculates the PLL setting for output frequency from input clock
* frequency. The calculation would cost a few time. So it is not recommaned to use it frequently.
* the "pllctrl", "pllndec", "pllpdec", "pllmdec" would updated in this function.
*/
static pll_error_t CLOCK_GetPllConfigInternal(
   uint32_t finHz, uint32_t foutHz, pll_setup_t *pSetup)
{
   uint32_t nDivOutHz, fccoHz, multFccoDiv;
   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 = 0U; /* 0 implies post-divider will be disabled */
   pllDirectOutput = 1U;
   multFccoDiv = 2U;

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

   /* Verify input rate parameter */
   if (finHz < PLL_LOWER_IN_LIMIT)
   {
       /* Input clock into the PLL cannot be lower than this */
       return kStatus_PLL_InputTooLow;
   }

   /* Find the optimal CCO frequency for the output and input that
      will keep it inside the PLL CCO range. This may require
      tweaking the post-divider for the PLL. */
   fccoHz = foutHz;
   while (fccoHz < PLL_MIN_CCO_FREQ_MHZ)
   {
       /* CCO output is less than minimum CCO range, so the CCO output
          needs to be bumped up and the post-divider is used to bring
          the PLL output back down. */
       pllPostDivider++;
       if (pllPostDivider > PVALMAX)
       {
           return kStatus_PLL_OutsideIntLimit;
       }

       /* Target CCO goes up, PLL output goes down */
       fccoHz = foutHz * (pllPostDivider * 2U);
       pllDirectOutput = 0U;
   }

   /* Determine if a pre-divider is needed to get the best frequency */
   if ((finHz > PLL_LOWER_IN_LIMIT) && (fccoHz >= finHz))
   {
       uint32_t a = FindGreatestCommonDivisor(fccoHz, (multFccoDiv * finHz));

       if (a > 20000U)
       {
           a = (multFccoDiv * finHz) / a;
           if ((a != 0U) && (a < PLL_MAX_N_DIV))
           {
               pllPreDivider = a;
           }
       }
   }

   /* Bypass pre-divider hardware if pre-divider is 1 */
   if (pllPreDivider > 1U)
   {
       pllDirectInput = 0U;
   }
   else
   {
       pllDirectInput = 1U;
   }

   /* Determine PLL multipler */
   nDivOutHz = (finHz / pllPreDivider);
   pllMultiplier = (fccoHz / nDivOutHz) / multFccoDiv;

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

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

   /* Get encoded value for M (mult) and use manual filter, disable SS mode */
   pSetup->pllmdec =
       PLL_MDEC_VAL_SET(pllEncodeM(pllMultiplier)) ;

   /* Get encoded values for N (prediv) and P (postdiv) */
   pSetup->pllndec = PLL_NDEC_VAL_SET(pllEncodeN(pllPreDivider));
   pSetup->pllpdec = PLL_PDEC_VAL_SET(pllEncodeP(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 */
                        (0 << 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? */

   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. */
pll_setup_t gPllSetupCacheStruct[CLOCK_USR_CFG_PLL_CONFIG_CACHE_COUNT];
uint32_t gFinHzCache[CLOCK_USR_CFG_PLL_CONFIG_CACHE_COUNT] = {0};
uint32_t gFoutHzCache[CLOCK_USR_CFG_PLL_CONFIG_CACHE_COUNT] = {0};
uint32_t gPllSetupCacheIdx = 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)
{
   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 == gFinHzCache[i]) && (foutHz == gFoutHzCache[i]) )
       {
           /* Hit the target in cache buffer. */
           pSetup->pllctrl = gPllSetupCacheStruct[i].pllctrl;
           pSetup->pllndec = gPllSetupCacheStruct[i].pllndec;
           pSetup->pllpdec = gPllSetupCacheStruct[i].pllpdec;
           pSetup->pllmdec = gPllSetupCacheStruct[i].pllmdec;
           retErr = kStatus_PLL_Success;
       }
   }

   if (i < CLOCK_USR_CFG_PLL_CONFIG_CACHE_COUNT)
   {
       return retErr;
   }
#endif /* CLOCK_USR_CFG_PLL_CONFIG_CACHE_COUNT */

   /* No cache or did not hit the cache. */
   retErr = CLOCK_GetPllConfigInternal(finHz, foutHz, pSetup);

#if (defined(CLOCK_USR_CFG_PLL_CONFIG_CACHE_COUNT) && CLOCK_USR_CFG_PLL_CONFIG_CACHE_COUNT)
   if (kStatus_PLL_Success == retErr)
   {
       /* Cache the most recent calulation result into buffer. */
       gFinHzCache[gPllSetupCacheIdx] = finHz;
       gFoutHzCache[gPllSetupCacheIdx] = foutHz;

       gPllSetupCacheStruct[gPllSetupCacheIdx].pllctrl = pSetup->pllctrl;
       gPllSetupCacheStruct[gPllSetupCacheIdx].pllndec = pSetup->pllndec;
       gPllSetupCacheStruct[gPllSetupCacheIdx].pllpdec = pSetup->pllpdec;
       gPllSetupCacheStruct[gPllSetupCacheIdx].pllmdec = pSetup->pllmdec;
       /* Update the index for next available buffer. */
       gPllSetupCacheIdx = (gPllSetupCacheIdx + 1U) % CLOCK_USR_CFG_PLL_CONFIG_CACHE_COUNT;
   }
#endif /* CLOCK_USR_CFG_PLL_CONFIG_CACHE_COUNT */

   return retErr;
}

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


/* Return System PLL 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_GetWdtOscFreq();
           break;

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

       default:
           clkRate = 0U;
           break;
   }

   return clkRate;
}


/* Return System PLL output clock rate from setup structure */
uint32_t CLOCK_GetPLL0OutFromSetup(pll_setup_t *pSetup)
{
   uint32_t prediv, postdiv, mMult, inPllRate;
   uint64_t workRate;

   inPllRate = CLOCK_GetPLL0InClockRate();
   /* If the PLL is bypassed, PLL would not be used and the output of PLL module would just be the input clock*/
   if ((pSetup->pllctrl & (SYSCON_PLL0CTRL_BYPASSPLL_MASK)) == 0U)
   {
       /* PLL is not in bypass mode, get pre-divider, and M divider, post-divider. */
       /*
        * 1. Pre-divider
        * Pre-divider is only available when the DIRECTI is disabled.
        */
       if (0U == (pSetup->pllctrl & SYSCON_PLL0CTRL_BYPASSPREDIV_MASK))
       {
           prediv = findPllPreDiv(pSetup->pllctrl, pSetup->pllndec);
       }
       else
       {
           prediv = 1U; /* The pre-divider is bypassed. */
       }
       /*
        * 2. Post-divider
        * Post-divider is only available when the DIRECTO is disabled.
        */
       if (0U == (pSetup->pllctrl & SYSCON_PLL0CTRL_BYPASSPOSTDIV_MASK))
       {
           postdiv = findPllPostDiv(pSetup->pllctrl, pSetup->pllpdec);
       }
       else
       {
           postdiv = 1U;           /* The post-divider is bypassed. */
       }
       /* Adjust input clock */
       inPllRate = inPllRate / prediv;

       /* MDEC used for rate */
       mMult = findPllMMult(pSetup->pllctrl, pSetup->pllmdec);
       workRate = (uint64_t)inPllRate * (uint64_t)mMult;

       workRate = workRate / ((uint64_t)postdiv);
       workRate = workRate * 2U; /* SYS PLL hardware cco is divide by 2 before to M-DIVIDER*/

   }
   else
   {
       /* In bypass mode */
       workRate = (uint64_t)inPllRate;
   }

   return (uint32_t)workRate;
}

/* Set the current PLL Rate */
void CLOCK_SetStoredPLLClockRate(uint32_t rate)
{
   s_Pll_Freq = rate;
}

/* Set the current Usb PLL Rate */
void CLOCK_SetStoredUsbPLLClockRate(uint32_t rate)
{
   s_Usb_Pll_Freq = rate;
}

/* Return System PLL output clock rate */
uint32_t CLOCK_GetPLL0OutClockRate(bool recompute)
{
   pll_setup_t Setup;
   uint32_t rate;

   if ((recompute) || (s_Pll_Freq == 0U))
   {
       Setup.pllctrl = SYSCON->PLL0CTRL;
       Setup.pllndec = SYSCON->PLL0NDEC;
       Setup.pllpdec = SYSCON->PLL0PDEC;
       //Setup.pllmdec = SYSCON->PLL0MDEC;

       CLOCK_GetPLL0OutFromSetupUpdate(&Setup);
   }

   rate = s_Pll_Freq;

   return rate;
}

/* Set PLL output based on the passed PLL setup data */
pll_error_t CLOCK_SetupPLLData(pll_config_t *pControl, pll_setup_t *pSetup)
{
   uint32_t inRate;
   pll_error_t pllError;

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

   /* PLL flag options */
   pllError = CLOCK_GetPllConfig(inRate, pControl->desiredRate, pSetup);
   pSetup->pllRate = pControl->desiredRate;
   return pllError;
}

/* Set PLL output from PLL setup structure */
pll_error_t CLOCK_SetupPLL0Prec(pll_setup_t *pSetup, uint32_t flagcfg)
{
   if ((SYSCON->PLL0CLKSEL & SYSCON_PLL0CLKSEL_SEL_MASK) == 0x01U)
   {
      /* Turn on the ext clock if system pll input select clk_in */
      //CLOCK_Enable_SysOsc(true);
   }
   /* Enable power for PLLs */
   //POWER_SetPLL();
   /* Power off PLL during setup changes */
   POWER_EnablePD(kPDRUNCFG_PD_PLL0);
   /*!< Set FLASH waitstates for core */
   //CLOCK_SetFLASHAccessCyclesForFreq(pSetup->pllRate);
   pSetup->flags = flagcfg;

   /* Write PLL setup data */
   SYSCON->PLL0CTRL = pSetup->pllctrl;
   SYSCON->PLL0NDEC = pSetup->pllndec;
   SYSCON->PLL0NDEC = pSetup->pllndec | (1U << SYSCON_PLL0NDEC_NREQ_SHIFT); /* latch */
   SYSCON->PLL0PDEC = pSetup->pllpdec;
   SYSCON->PLL0PDEC = pSetup->pllpdec | (1U << SYSCON_PLL0PDEC_PREQ_SHIFT); /* latch */
   //SYSCON->PLL0MDEC = pSetup->pllmdec;
   //SYSCON->PLL0MDEC = pSetup->pllmdec | (1U << SYSCON_PLL0MDEC_MREQ_SHIFT); /* latch */

   /* Flags for lock or power on */
   if ((pSetup->flags & (PLL_SETUPFLAG_POWERUP | PLL_SETUPFLAG_WAITLOCK)) != 0U)
   {
       /* If turning the PLL back on, perform the following sequence to accelerate PLL lock */
       volatile uint32_t delayX;
       //uint32_t maxCCO = (1U << 18U) | 0x5dd2U; /* CCO = 1.6Ghz + MDEC enabled*/
       //uint32_t curSSCTRL = SYSCON->PLL0MDEC & ~(1U << 17U);

       /* Initialize  and power up PLL */
       //SYSCON->PLL0MDEC = maxCCO;
       POWER_DisablePD(kPDRUNCFG_PD_PLL0);

       /* Set mreq to activate */
       //SYSCON->PLL0MDEC = maxCCO | (1U << 17U);

       /* Delay for 72 uSec @ 12Mhz */
       for (delayX = 0U; delayX < 172U; ++delayX)
       {
       }

       /* clear mreq to prepare for restoring mreq */
       //SYSCON->PLL0MDEC = curSSCTRL;

       /* set original value back and activate */
       //SYSCON->PLL0MDEC = curSSCTRL | (1U << 17U);

       /* Enable peripheral states by setting low */
       POWER_DisablePD(kPDRUNCFG_PD_PLL0);
   }
   if ((pSetup->flags & PLL_SETUPFLAG_WAITLOCK) != 0U)
   {
       while (CLOCK_IsPLL0Locked() == false)
       {
       }
   }

   /* 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) != 0U)
   {
       //POWER_SetVoltageForFreq(s_Pll_Freq);
   }

   return kStatus_PLL_Success;
}

/* Setup PLL Frequency from pre-calculated value */
pll_error_t CLOCK_SetPLLFreq(const pll_setup_t *pSetup)
{
   if ((SYSCON->PLL0CLKSEL & SYSCON_PLL0CLKSEL_SEL_MASK) == 0x01U)
   {
      /* Turn on the ext clock if system pll input select clk_in */
      //CLOCK_Enable_SysOsc(true);
   }
   /* Enable power VD3 for PLLs */
   //POWER_SetPLL();
   /* Power off PLL during setup changes */
   POWER_EnablePD(kPDRUNCFG_PD_PLL0);

   /* Write PLL setup data */
   SYSCON->PLL0CTRL = pSetup->pllctrl;
   SYSCON->PLL0NDEC = pSetup->pllndec;
   SYSCON->PLL0NDEC = pSetup->pllndec | (1U << SYSCON_PLL0NDEC_NREQ_SHIFT); /* latch */
   SYSCON->PLL0PDEC = pSetup->pllpdec;
   SYSCON->PLL0PDEC = pSetup->pllpdec | (1U << SYSCON_PLL0PDEC_PREQ_SHIFT); /* latch */
   //SYSCON->PLL0MDEC = pSetup->pllmdec;
   //SYSCON->PLL0MDEC = pSetup->pllmdec | (1U << SYSCON_PLL0MDEC_MREQ_SHIFT); /* latch */

   /* Flags for lock or power on */
   if ((pSetup->flags & (PLL_SETUPFLAG_POWERUP | PLL_SETUPFLAG_WAITLOCK)) != 0)
   {
       /* If turning the PLL back on, perform the following sequence to accelerate PLL lock */
       volatile uint32_t delayX;
       //uint32_t maxCCO = (1U << 18U) | 0x5dd2U; /* CCO = 1.6Ghz + MDEC enabled*/
       //uint32_t curSSCTRL = SYSCON->PLL0MDEC & ~(1U << 17U);

       /* Initialize  and power up PLL */
       //SYSCON->PLL0MDEC = maxCCO;
       POWER_DisablePD(kPDRUNCFG_PD_PLL0);

       /* Set mreq to activate */
       //SYSCON->PLL0MDEC = maxCCO | (1U << 17U);

       /* Delay for 72 uSec @ 12Mhz */
       for (delayX = 0U; delayX < 172U; ++delayX)
       {
       }

       /* clear mreq to prepare for restoring mreq */
       //SYSCON->PLL0MDEC = curSSCTRL;

       /* set original value back and activate */
       //SYSCON->PLL0MDEC = curSSCTRL | (1U << 17U);

       /* Enable peripheral states by setting low */
       POWER_DisablePD(kPDRUNCFG_PD_PLL0);
   }
   if ((pSetup->flags & PLL_SETUPFLAG_WAITLOCK) != 0U)
   {
       while (CLOCK_IsPLL0Locked() == false)
       {
       }
   }

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

   return kStatus_PLL_Success;
}

/* Set System PLL clock based on the input frequency and multiplier */
void CLOCK_SetupPLL0Mult(uint32_t multiply_by, uint32_t input_freq)
{

}

/* 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_CLK);
   }
   else
   {
       /*Set the USB PLL as the Usb0 CLK*/
       POWER_DisablePD(kPDRUNCFG_PD_PLL1);

       //usb_pll_setup_t pll_setup = { 0x3FU, 0x01U, 0x03U, false, false, false, 12000000U };

       //CLOCK_SetUsbPLLFreq(&pll_setup);
       CLOCK_SetClkDiv(kCLOCK_DivUsb0Clk,1U, false);
       CLOCK_AttachClk(kPLL1_to_USB0_CLK);
       uint32_t delay = 100000;
       while (delay --)
       {
           __asm("nop");
       }
   }
   CLOCK_EnableClock(kCLOCK_Usbd0);
   CLOCK_EnableClock(kCLOCK_UsbRam1);

   return ret;
}

/* Enable USB HOST FULL SPEED clock */
bool CLOCK_EnableUsbfs0HostClock(clock_usbfs_src_t src, uint32_t freq)
{
   bool ret = true;

   CLOCK_DisableClock(kCLOCK_Usbhmr0);
   CLOCK_DisableClock(kCLOCK_Usbhsl0);

   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_CLK);
   }
   else
   {
       /*Set the USB PLL as the Usb0 CLK*/
       POWER_DisablePD(kPDRUNCFG_PD_PLL1);

       //usb_pll_setup_t pll_setup = { 0x3FU, 0x01U, 0x03U, false, false, false, 12000000U };

       //CLOCK_SetUsbPLLFreq(&pll_setup);
       CLOCK_SetClkDiv(kCLOCK_DivUsb0Clk,1U, false);
       CLOCK_AttachClk(kPLL1_to_USB0_CLK);
       uint32_t delay = 100000;
       while (delay --)
       {
           __asm("nop");
       }
   }
   CLOCK_EnableClock(kCLOCK_Usbhmr0);
   CLOCK_EnableClock(kCLOCK_Usbhsl0);
   CLOCK_EnableClock(kCLOCK_UsbRam1);

   return ret;
}

/* Enable USB DEVICE HIGH SPEED clock */
bool CLOCK_EnableUsbhs0DeviceClock(void)
{
   bool ret = true;
   uint32_t delay;
   
   CLOCK_DisableClock(kCLOCK_Usbd1);
   /* Power on the VD5 for USB PHY */
   //POWER_SetUsbPhy();

   /* Turn on power for crystal 32 MHz */
   POWER_DisablePD(kPDRUNCFG_PD_XTAL32M);
   /* Enable XO32 MHz output to USB HS PLL, HS USB only use this clock  */
   ANACTRL->XO32M_CTRL = ANACTRL->XO32M_CTRL | ANACTRL_XO32M_CTRL_ENABLE_PLL_USB_OUT_MASK;

   delay = 100000;
   while (delay --)
   {
      __asm("nop");
   }
   /* Enable USB1D and USB1RAM */
   CLOCK_EnableClock(kCLOCK_Usbd1);
   CLOCK_EnableClock(kCLOCK_UsbRam1);
   POWER_DisablePD(kPDRUNCFG_PD_USB1_PHY); /* Turn on power for USB PHY */
   return ret;
}


/* Enable USB HOST HIGH SPEED clock */
bool CLOCK_EnableUsbhs0HostClock(void)
{
   bool ret = true;
   uint32_t delay;

   CLOCK_DisableClock(kCLOCK_Usbh1);
   /* Power on the VD5 for USB PHY */
   //POWER_SetUsbPhy();

   /* Turn on power for crystal 32 MHz */
   POWER_DisablePD(kPDRUNCFG_PD_XTAL32M);
   /* Enable XO32 MHz output to USB HS PLL, HS USB only use this clock  */
   ANACTRL->XO32M_CTRL = ANACTRL->XO32M_CTRL | ANACTRL_XO32M_CTRL_ENABLE_PLL_USB_OUT_MASK;
   
   delay = 100000;
   while (delay --)
   {
       __asm("nop");
   }
   /* Enable USBh1 and USB1RAM */
   CLOCK_EnableClock(kCLOCK_Usbh1);
   CLOCK_EnableClock(kCLOCK_UsbRam1);
   POWER_DisablePD(kPDRUNCFG_PD_USB1_PHY); /* Turn on power for USB PHY */
   return ret;
}
