/******************************************************************************
 * @file     clock.c
 * @brief    CLOCK firmware functions.
 * @version  V4.0.0
 * @date     30-July-2026
 *
 * @note
 * Copyright (C) 2022 Spintrol Electronic Technology (Shanghai) Co., Ltd.. All rights reserved.
 *
 * @attention
 * THIS SOFTWARE JUST PROVIDES CUSTOMERS WITH CODING INFORMATION REGARDING
 * THEIR PRODUCTS, WHICH AIMS AT SAVING TIME FOR THEM. SPINTROL SHALL NOT BE
 * LIABLE FOR THE USE OF THE SOFTWARE. SPINTROL DOES NOT GUARANTEE THE
 * CORRECTNESS OF THIS SOFTWARE AND RESERVES THE RIGHT TO MODIFY THE SOFTWARE
 * WITHOUT NOTIFICATION.
 *
 ******************************************************************************/


#include "clock.h"


#if defined(__CC_ARM)
    #pragma push
    #pragma O1
#endif




/******************************************************************************
 * @brief      PLL VCO frequency trimming
 *
 * @param[in]  none
 *
 * @return     none
 *
 * @note       The PLL is calibrated when factory-out, and usually do not need 
 *             to call this function!!!
 *
 ******************************************************************************/
#if defined(__ICCARM__)
#pragma optimize=low
#endif
ErrorStatus CLOCK_TrimPLL(void)
{
    uint32_t u32VCOTrim;
    volatile uint32_t u32TimeOut = 0;

    /* Enable PLL */
    CLOCK->PLLCTL0.bit.EN       = PLLCTL0_BIT_EN_ENABLE;
    /* Enable open-loop mode for VCO trimming */
    CLOCK->PLLCTL0.bit.OPENLOOP = PLLCTL0_BIT_OPENLOOP_OPEN_LOOP;
    /* Enable PLL frequency calibration */
    CLOCK->PLLCTL0.bit.FCALEN   = PLLCTL0_BIT_FCALEN_ENABLE;

    /* VCO frequency trimming */
    for (u32VCOTrim = 0; u32VCOTrim < 8; u32VCOTrim++)
    {
        /* Set VCO trim value */
        CLOCK->PLLCTL0.bit.VCOTRIM = u32VCOTrim;

        /* Enable PLL frequency calibration time window */
        CLOCK->PLLCTL0.bit.FCALWIN = PLLCTL0_BIT_FCALWIN_ENABLE;

        /* Wait VCOFREQ signal valid */
        for (u32TimeOut = 100000UL; u32TimeOut > 0U; u32TimeOut--)
        {
            if (CLOCK->CLKSTS.bit.VCOFREQVLD != CLKSTS_BIT_VCOFREQVLD_INVALID)
            {
                break;
            }
        }

        if (u32TimeOut == 0)
        {
            return ERROR;
        }

        /* If VCO frequency is too fast, finish calibration */
        if (CLOCK->CLKSTS.bit.VCOFREQ == CLKSTS_BIT_VCOFREQ_TOO_FAST)
        {
            break;
        }

        /* Disable PLL frequency calibration time window */
        CLOCK->PLLCTL0.bit.FCALWIN = PLLCTL0_BIT_FCALWIN_DISABLE;
    }

    /* Restore to default value */
    CLOCK->PLLCTL0.bit.FCALEN   = PLLCTL0_BIT_FCALEN_DISABLE;
    CLOCK->PLLCTL0.bit.OPENLOOP = PLLCTL0_BIT_OPENLOOP_CLOSE_LOOP;
    CLOCK->PLLCTL0.bit.EN       = PLLCTL0_BIT_EN_DISABLE;

    return SUCCESS;
}




/******************************************************************************
 * @brief      Configure chip clock system
 *
 * @param[in]  u32PLLRef  :  Reference clock selection defined by CLOCK_RefSelEnum
 *
 * @param[in]  u32HCLKFreq:  HCLK frequency in Hz
 *
 * @return     none
 *
 * @note       In this function:
 *             - Select PLL as system clock source
 *             - HCLK frequency equal to the system clock frequency
 *             - PCLK, SSPCLK and I2CCLK frequency is limited to less than 50MHz
 *             - Select PLL as periheral clock source
 *             - Peripheral clock freqency (except SSP and I2C) equal to system clock
 *
 * @example    (1)  CLOCK_Init(CLOCK_FROM_XTAL6MHZ, 50000000);
 *             (2)  CLOCK_Init(CLOCK_FROM_RCO32MHZ, 200000000);
 *
 ******************************************************************************/
#if defined(__ICCARM__)
#pragma optimize=low
#endif
ErrorStatus CLOCK_Init(CLOCK_RefSelEnum u32PLLRef, uint32_t u32HCLKFreq)
{
    ErrorStatus Status = SUCCESS;
    uint32_t u32PCLKDiv;
    volatile uint32_t u32TimeOut;

    if (u32PLLRef == CLOCK_FROM_RCO32MHZ)
    {
        /* Enable RCO0 */
        CLOCK->RCO0CTL.all |= RCO0CTL_ALL_EN_ENABLE;

        /* Wait RCO0 ready */
        for (u32TimeOut = 10000UL; u32TimeOut > 0U; u32TimeOut--)
        {
            if (CLOCK->CLKSTS.bit.RCO0RDY != CLKSTS_BIT_RCO0RDY_NOT_READY)
            {
                break;
            }
        }

        if (u32TimeOut == 0)
        {
            return ERROR;
        }

        /* The HCLK frequency is 32Mhz */
        if (u32HCLKFreq != 32000000UL)
        {
            /* Select RCO as reference */
            Status = CLOCK_ConfigurePLL(0, 32000000, u32HCLKFreq);
        }

    }
    else
    {
        /* Set the pin channel to XO */
        GPIO_SetPinChannel(GPIO_16, GPIO16_XIN);
        GPIO_SetPinChannel(GPIO_17, GPIO17_XIO);

        /* Enable XO */
        CLOCK->XOCTL.all =  XOCTL_ALL_PRECNT_(0xFFU)                     /* Set pre-counted target value            */
                              | XOCTL_ALL_FASTEN_ENABLE                  /* Enable noise injection for fast startup */
                              | XOCTL_ALL_EXTRFB_DISABLE;                /* Use internal feedback resistor          */

        CLOCK->XOCTL.bit.EN = XOCTL_BIT_EN_ENABLE;

        /* Wait XO ready */
        for (u32TimeOut = 4000000UL; u32TimeOut > 0U; u32TimeOut--)
        {
            if (CLOCK->CLKSTS.bit.XORDY != CLKSTS_BIT_XORDY_NOT_READY)
            {
                break;
            }
        }

        if (u32TimeOut == 0)
        {
            return ERROR;
        }

        if (u32PLLRef != u32HCLKFreq)
        {
            /* Select XO as reference */
            Status = CLOCK_ConfigurePLL(1, u32PLLRef, u32HCLKFreq);
        }

    }

    if (Status == SUCCESS)
    {
        u32PCLKDiv = u32HCLKFreq / 50000000;
        if ((u32PCLKDiv * 50000000) != u32HCLKFreq)
        {
            u32PCLKDiv += 1;
        }

        /* Update PCLK, SSPCLK, I2CCLK dividing ratio */
        CLOCK->PCLKCTL.bit.DIV    = (u32PCLKDiv - 1);
        CLOCK->SSPCLKCTL.bit.DIV  = (u32PCLKDiv - 1);
        CLOCK->I2CCLKCTL.bit.DIV  = (u32PCLKDiv - 1);

        /* Set HCLK dividing ratio to 1 */
        CLOCK->HCLKCTL.bit.DIV    = 0;

        if (u32PLLRef == u32HCLKFreq)
        {
            /* Select XO clock as system clock */
            CLOCK->SYSCLK0CTL.bit.SRC = SYSCLK0CTL_BIT_SRC_XO;
            CLOCK->SYSCLK1CTL.bit.SRC = SYSCLK0CTL_BIT_SRC_XO;
        }
        else if ((u32PLLRef == CLOCK_FROM_RCO32MHZ) && (u32HCLKFreq == 32000000UL))
        {
            /* Select PLL clock as system clock */
            CLOCK->SYSCLK0CTL.bit.SRC = SYSCLK0CTL_BIT_SRC_RCO0;
            CLOCK->SYSCLK1CTL.bit.SRC = SYSCLK0CTL_BIT_SRC_RCO0;
        }
        else
        {
            /* Select PLL clock as system clock */
            CLOCK->SYSCLK0CTL.bit.SRC = SYSCLK0CTL_BIT_SRC_PLL;
            CLOCK->SYSCLK1CTL.bit.SRC = SYSCLK1CTL_BIT_SRC_PLL;
        }


        /* Update system information */
        SysInfo.u32HCLK = u32HCLKFreq;
        SysInfo.u32PCLK = u32HCLKFreq / u32PCLKDiv;

        /* Configure peripheral clock dividing ratio */
        CLOCK->ADCCLKCTL.bit.DIV  = 0;
        CLOCK->PWMCLKCTL.bit.DIV  = 0;
        CLOCK->ECAPCLKCTL.bit.DIV = 0;
        CLOCK->TMR0CLKCTL.bit.DIV = 0;
        CLOCK->TMR1CLKCTL.bit.DIV = 0;
        CLOCK->TMR2CLKCTL.bit.DIV = 0;
        CLOCK->UARTCLKCTL.bit.DIV = 0;

        /* Configure Watchdog clock source - use default setting */
        CLOCK->WDT0CLKCTL.bit.SRC = WDT0CLKCTL_BIT_SRC_RCO0;
        CLOCK->WDT1CLKCTL.bit.SRC = WDT1CLKCTL_BIT_SRC_RCO1;

        /* Configure Watchdog clock dividing ratio - use default setting */
        CLOCK->WDT0CLKCTL.bit.DIV = 0xF;
        CLOCK->WDT1CLKCTL.bit.DIV = 0x0;
    }
    
    /* Clear PLL unlock and detect error flag */
    SYSTEM_ClearResetEventStatus(RESET_EVENT_PLL_UNLOCK);
    SYSTEM_ClearResetEventStatus(RESET_EVENT_CLK_DETECT_ERROR);

    /* Check PLL status */
    if ((SYSTEM_GetResetEventStatus(RESET_EVENT_PLL_UNLOCK) != 0) || 
        (SYSTEM_GetResetEventStatus(RESET_EVENT_CLK_DETECT_ERROR) != 0))
    {
        return ERROR;
    }
    
    /* Update clock information to Sysinfo */
    if (u32PLLRef == CLOCK_FROM_RCO32MHZ)
    {
        CLOCK_UpdateInfo(0);
    }
    else
    {
        CLOCK_UpdateInfo(u32PLLRef);
    }
    return Status;
}




/******************************************************************************
 * @brief      Configure chip clock system with RCO0 as reference
 *
 * @param[in]  u32HCLKFreq:  HCLK frequency in Hz
 *
 * @return     none
 *
 * @note       In this function:
 *             - Select PLL as system clock source
 *             - HCLK frequency equal to the system clock frequency
 *             - PCLK, SSPCLK and I2CCLK frequency is limited to less than 50MHz
 *             - Select PLL as periheral clock source
 *             - Peripheral clock freqency (except SSP and I2C) equal to system clock
 *
 * @example    (1)  CLOCK_InitWithRCO(50000000);
 *             (2)  CLOCK_InitWithRCO(200000000);
 *
 ******************************************************************************/
#if defined(__ICCARM__)
#pragma optimize=low
#endif
ErrorStatus CLOCK_InitWithRCO(uint32_t u32HCLKFreq)
{
    ErrorStatus Status = SUCCESS;
    uint32_t u32PCLKDiv;
    volatile uint32_t u32TimeOut;

    /* Enable RCO0 */
    CLOCK->RCO0CTL.all |= RCO0CTL_ALL_EN_ENABLE;

    /* Wait RCO0 ready */
    for (u32TimeOut = 10000UL; u32TimeOut > 0U; u32TimeOut--)
    {
        if (CLOCK->CLKSTS.bit.RCO0RDY != CLKSTS_BIT_RCO0RDY_NOT_READY)
        {
            break;
        }
    }

    if (u32TimeOut == 0)
    {
        return ERROR;
    }

    /* The HCLK frequency is 32Mhz */
    if (u32HCLKFreq != 32000000UL)
    {
        /* Configure PLL clock */
        Status = CLOCK_ConfigurePLL(0, 32000000UL, u32HCLKFreq);
    }

    if (Status == SUCCESS)
    {
        u32PCLKDiv = u32HCLKFreq / 50000000;
        if ((u32PCLKDiv * 50000000) != u32HCLKFreq)
        {
            u32PCLKDiv += 1;
        }

        /* Update PCLK, SSPCLK, I2CCLK dividing ratio */
        CLOCK->PCLKCTL.bit.DIV    = (u32PCLKDiv - 1);
        CLOCK->SSPCLKCTL.bit.DIV  = (u32PCLKDiv - 1);
        CLOCK->I2CCLKCTL.bit.DIV  = (u32PCLKDiv - 1);

        /* Set HCLK dividing ratio to 1 */
        CLOCK->HCLKCTL.bit.DIV    = 0;    /* HCLK Frequency = System clock frequency */

        /* The HCLK frequency is 32Mhz */
        if (u32HCLKFreq == 32000000UL)
        {
            /* Select RCO0 clock as system clock */
            CLOCK->SYSCLK0CTL.bit.SRC = SYSCLK0CTL_BIT_SRC_RCO0;
            CLOCK->SYSCLK1CTL.bit.SRC = SYSCLK1CTL_BIT_SRC_RCO0;
        }
        else
        {
            /* Select PLL clock as system clock */
            CLOCK->SYSCLK0CTL.bit.SRC = SYSCLK0CTL_BIT_SRC_PLL;
            CLOCK->SYSCLK1CTL.bit.SRC = SYSCLK1CTL_BIT_SRC_PLL;
        }

        /* Update system information */
        SysInfo.u32HCLK = u32HCLKFreq;
        SysInfo.u32PCLK = u32HCLKFreq / u32PCLKDiv;

        /* Configure peripheral clock dividing ratio */
        CLOCK->ADCCLKCTL.bit.DIV  = 0;
        CLOCK->PWMCLKCTL.bit.DIV  = 0;
        CLOCK->ECAPCLKCTL.bit.DIV = 0;
        CLOCK->TMR0CLKCTL.bit.DIV = 0;
        CLOCK->TMR1CLKCTL.bit.DIV = 0;
        CLOCK->TMR2CLKCTL.bit.DIV = 0;
        CLOCK->UARTCLKCTL.bit.DIV = 0;

        /* Configure Watchdog clock source - use default setting */
        CLOCK->WDT0CLKCTL.bit.SRC = WDT0CLKCTL_BIT_SRC_RCO0;
        CLOCK->WDT1CLKCTL.bit.SRC = WDT1CLKCTL_BIT_SRC_RCO1;

        /* Configure Watchdog clock dividing ratio - use default setting */
        CLOCK->WDT0CLKCTL.bit.DIV = 0xF;
        CLOCK->WDT1CLKCTL.bit.DIV = 0x0;
    }
    
    /* Clear PLL unlock and detect error flag */
    SYSTEM_ClearResetEventStatus(RESET_EVENT_PLL_UNLOCK);
    SYSTEM_ClearResetEventStatus(RESET_EVENT_CLK_DETECT_ERROR);

    /* Check PLL status */
    if ((SYSTEM_GetResetEventStatus(RESET_EVENT_PLL_UNLOCK) != 0) || 
        (SYSTEM_GetResetEventStatus(RESET_EVENT_CLK_DETECT_ERROR) != 0))
    {
        return ERROR;
    }

    /* Update clock information to Sysinfo */
    CLOCK_UpdateInfo(0);
    
    return Status;
}




/******************************************************************************
 * @brief      Configure chip clock system with XO as reference
 *
 * @param[in]  u32XOFreq  :  PLL reference clock
 * @param[in]  u32HCLKFreq:   CPU frequency (HCLK) in Hz
 *
 * @return     SUCCESS or ERROR
 *
 *
 * @note       In this function:
 *             - Select PLL as system clock source for CPU and peripherals
 *             - CPU and peripheral clock frequency equal to system clock
 *             - Select RCO as watchdog 0 clock source
 *             - Select ROSC as watchdog 1 clock source
 *
 * @example    (1)  CLOCK_InitWithXO(60000000, 50000000);
 *             (2)  CLOCK_InitWithXO(320000000, 100000000);
 *
 ******************************************************************************/
#if defined(__ICCARM__)
#pragma optimize=low
#endif
ErrorStatus CLOCK_InitWithXO(uint32_t u32XOFreq, uint32_t u32HCLKFreq)
{
    ErrorStatus Status = SUCCESS;
    uint32_t u32PCLKDiv;
    volatile uint32_t u32TimeOut;

    /* Set the pin channel to XO */
    GPIO_SetPinChannel(GPIO_16, GPIO16_XIN);
    GPIO_SetPinChannel(GPIO_17, GPIO17_XIO);

    /* Enable XO */
    CLOCK->XOCTL.all =  XOCTL_ALL_PRECNT_(0xFFU)                     /* Set pre-counted target value            */
                          | XOCTL_ALL_FASTEN_ENABLE                  /* Enable noise injection for fast startup */
                          | XOCTL_ALL_EXTRFB_DISABLE;                /* Use internal feedback resistor          */

    CLOCK->XOCTL.bit.EN = XOCTL_BIT_EN_ENABLE;

    /* Wait XO ready */
    for (u32TimeOut = 4000000UL; u32TimeOut > 0U; u32TimeOut--)
    {
        if (CLOCK->CLKSTS.bit.XORDY != CLKSTS_BIT_XORDY_NOT_READY)
        {
            break;
        }
    }

    if (u32TimeOut == 0)
    {
        return ERROR;
    }
        

    /* The HCLK frequency is not u32XOFreq */
    if (u32XOFreq != u32HCLKFreq)
    {
        /* Select XO as reference */
        Status = CLOCK_ConfigurePLL(1, u32XOFreq, u32HCLKFreq);
    }

    if (Status == SUCCESS)
    {
        u32PCLKDiv = u32HCLKFreq / 50000000;
        if ((u32PCLKDiv * 50000000) != u32HCLKFreq)
        {
            u32PCLKDiv += 1;
        }

        /* Update PCLK, SSPCLK, I2CCLK dividing ratio */
        CLOCK->PCLKCTL.bit.DIV    = (u32PCLKDiv - 1);
        CLOCK->SSPCLKCTL.bit.DIV  = (u32PCLKDiv - 1);
        CLOCK->I2CCLKCTL.bit.DIV  = (u32PCLKDiv - 1);

        /* Set HCLK dividing ratio to 1 */
        CLOCK->HCLKCTL.bit.DIV    = 0;

        if (u32XOFreq == u32HCLKFreq)
        {
            /* Select XO clock as system clock */
            CLOCK->SYSCLK0CTL.bit.SRC = SYSCLK0CTL_BIT_SRC_XO;
            CLOCK->SYSCLK1CTL.bit.SRC = SYSCLK0CTL_BIT_SRC_XO;
        }
        else
        {
            /* Select PLL clock as system clock */
            CLOCK->SYSCLK0CTL.bit.SRC = SYSCLK0CTL_BIT_SRC_PLL;
            CLOCK->SYSCLK1CTL.bit.SRC = SYSCLK1CTL_BIT_SRC_PLL;
        }

        /* Update system information */
        SysInfo.u32HCLK = u32HCLKFreq;
        SysInfo.u32PCLK = u32HCLKFreq / u32PCLKDiv;

        /* Configure peripheral clock dividing ratio */
        CLOCK->ADCCLKCTL.bit.DIV  = 0;
        CLOCK->PWMCLKCTL.bit.DIV  = 0;
        CLOCK->ECAPCLKCTL.bit.DIV = 0;
        CLOCK->TMR0CLKCTL.bit.DIV = 0;
        CLOCK->TMR1CLKCTL.bit.DIV = 0;
        CLOCK->TMR2CLKCTL.bit.DIV = 0;
        CLOCK->UARTCLKCTL.bit.DIV = 0;

        /* Configure Watchdog clock source - use default setting */
        CLOCK->WDT0CLKCTL.bit.SRC = WDT0CLKCTL_BIT_SRC_RCO0;
        CLOCK->WDT1CLKCTL.bit.SRC = WDT1CLKCTL_BIT_SRC_RCO1;

        /* Configure Watchdog clock dividing ratio - use default setting */
        CLOCK->WDT0CLKCTL.bit.DIV = 0xF;
        CLOCK->WDT1CLKCTL.bit.DIV = 0x0;
    }
    
    /* Clear PLL unlock and detect error flag */
    SYSTEM_ClearResetEventStatus(RESET_EVENT_PLL_UNLOCK);
    SYSTEM_ClearResetEventStatus(RESET_EVENT_CLK_DETECT_ERROR);

    /* Check PLL status */
    if ((SYSTEM_GetResetEventStatus(RESET_EVENT_PLL_UNLOCK) != 0) || 
        (SYSTEM_GetResetEventStatus(RESET_EVENT_CLK_DETECT_ERROR) != 0))
    {
        return ERROR;
    }
    
    /* Update clock information to Sysinfo */
    CLOCK_UpdateInfo(u32XOFreq);
    
    return Status;
}




/******************************************************************************
 * @brief      Configure PLL clock
 *
 * @param[in]  u8RefClkFromXO:  Reference clock selection
 *                               - \ref 1 -> clock source(reference) is from external crystal clock
 *                               - \ref 0 -> clock source(reference) is from internal RCO(32MHz)
 * @param[in]  u32RefClkHz   :  Reference clock frequency (Hz)
 * @param[in]  u32PLLClk     :  PLL output clock frequency (Hz)
 *
 * @return     SUCCESS or ERROR
 *
 ******************************************************************************/
#if defined(__ICCARM__)
#pragma optimize=low
#endif
ErrorStatus CLOCK_ConfigurePLL(uint8_t u8RefClkFromXO, uint32_t u32RefClkHz, uint32_t u32PLLClk)
{
    volatile uint32_t u32TimeOut;
    uint32_t u32NOUT, u32NIN, u32NFB;
    uint32_t u32VCOTrim;
    uint64_t u64NFB;

    /* Check PLL frequency */
    if (u32PLLClk > PLL_MAX_FREQ)
    {
        return ERROR;
    }

    /* NIN number */
    u32NIN = u32RefClkHz / 8000000;
    if ((u32RefClkHz % 8000000) != 0)
    {
        u32NIN++;
    }

    /* NOUT number (VCO range is [400 : 600] MHz) */
    if (u32PLLClk > 150000000U)
    {
        u32NOUT = 3U;      /* (150 : 200] MHz */
    }
    else if (u32PLLClk > 100000000U)
    {
        u32NOUT = 4U;      /* (100 : 150] MHz    */
    }
    else if (u32PLLClk > 75000000U)
    {
        u32NOUT = 6U;      /* (75 : 100] MHz  */
    }
    else if (u32PLLClk > 50000000U)
    {
        u32NOUT = 8U;      /* (50 : 75] MHz  */
    }
    else if (u32PLLClk > 37500000U)
    {
        u32NOUT = 12U;     /* (37.5 : 50] MHz  */
    }
    else if (u32PLLClk >= 25000000U)
    {
        u32NOUT = 16U;     /* [25 : 37.5] MHz */
    }
    else
    {
        return ERROR;
    }

    /* NFB Number */
    u64NFB = (uint64_t)u32PLLClk * u32NOUT * u32NIN * 65536;
    u32NFB = (uint32_t)(u64NFB / (uint64_t)u32RefClkHz);

    /* Set NIN, NOUT and NFB */
    CLOCK->PLLCTL1.bit.NIN  = u32NIN;
    CLOCK->PLLCTL1.bit.NOUT = (u32NOUT - 1);
    CLOCK->PLLCTL1.bit.NFB  = u32NFB;

    /* Set ICP */
    u32VCOTrim = CLOCK->PLLCTL0.bit.VCOTRIM;
    CLOCK->PLLCTL0.bit.ICP = (96 * u32NFB) / (14 - u32VCOTrim) / 65536 / 100;

    /* Select PLL reference clock */
    CLOCK->PLLCTL0.bit.RCLKSELXO = u8RefClkFromXO;

    /* Enable PLL and output */
    CLOCK->PLLCTL0.all |= (PLLCTL0_ALL_EN_ENABLE | PLLCTL0_ALL_OE_ENABLE);

    /* Wait PLL clock ready */
    for (u32TimeOut = 100000UL; u32TimeOut > 0U; u32TimeOut--)
    {
        if (CLOCK->CLKSTS.bit.PLLRDY != CLKSTS_BIT_PLLRDY_NOT_READY)
        {
            break;
        }
    }

    if (u32TimeOut == 0)
    {
        return ERROR;
    }

    /* Clear CLKIC */
    CLOCK->CLKIC.bit.CLKINT = 1;
    CLOCK->CLKIC.bit.CLKDETERR = 1;
    CLOCK->CLKIC.bit.PLLUNLOCK = 1;
    return SUCCESS;
}




/******************************************************************************
 * @brief      Configure PLL clock with reference from RCO0
 *
 * @param[in]  u32PLLClk:  PLL clock output frequency (Hz)
 *
 * @return     SUCCESS or ERROR
 *
 ******************************************************************************/
#if defined(__ICCARM__)
#pragma optimize=low
#endif
ErrorStatus CLOCK_ConfigurePLLWithRCO(uint32_t u32PLLClk)
{
    volatile uint32_t u32TimeOut;
    uint32_t u32NOUT, u32NIN, u32NFB;
    uint32_t u32VCOTrim;
    u32TimeOut = 10000;

    /* Enable RCO0 */
    CLOCK->RCO0CTL.all |= RCO0CTL_ALL_EN_ENABLE;
    for (u32TimeOut = 10000UL; u32TimeOut > 0U; u32TimeOut--)
    {
        if (CLOCK->CLKSTS.bit.RCO0RDY != CLKSTS_BIT_RCO0RDY_NOT_READY)
        {
            break;
        }
    }

    if (u32TimeOut == 0)
    {
        return ERROR;
    }

    /* NIN number */
    u32NIN = 4;

    /* NOUT number (VCO range is [400 : 600] MHz) */
    if (u32PLLClk > 150000000U)
    {
        u32NOUT = 3U;      /* (150 : 200] MHz */
    }
    else if (u32PLLClk > 100000000U)
    {
        u32NOUT = 4U;      /* (100 : 150] MHz    */
    }
    else if (u32PLLClk > 75000000U)
    {
        u32NOUT = 6U;      /* (75 : 100] MHz  */
    }
    else if (u32PLLClk > 50000000U)
    {
        u32NOUT = 8U;      /* (50 : 75] MHz  */
    }
    else if (u32PLLClk > 37500000U)
    {
        u32NOUT = 12U;     /* (37.5 : 50] MHz  */
    }
    else if (u32PLLClk >= 25000000U)
    {
        u32NOUT = 16U;     /* [25 : 37.5] MHz */
    }
    else
    {
        return ERROR;
    }

    /* NFB Number */
    u32NFB = u32PLLClk * u32NOUT / 25 * 128 / 625;

    /* Set NIN, NOUT and NFB */
    CLOCK->PLLCTL1.bit.NIN  = u32NIN;
    CLOCK->PLLCTL1.bit.NOUT = (u32NOUT - 1);
    CLOCK->PLLCTL1.bit.NFB  = u32NFB;

    /* Set ICP */
    u32VCOTrim = CLOCK->PLLCTL0.bit.VCOTRIM;
    CLOCK->PLLCTL0.bit.ICP = (96 * u32NFB) / (14 - u32VCOTrim) / 65536 / 100;

    /* Select RCO0 as reference clock */
    CLOCK->PLLCTL0.bit.RCLKSELXO = PLLCTL0_BIT_RCLKSELXO_RCO0;

    /* Enable PLL and output */
    CLOCK->PLLCTL0.all |= (PLLCTL0_ALL_EN_ENABLE | PLLCTL0_ALL_OE_ENABLE);

    /* Wait PLL clock ready */
    for (u32TimeOut = 100000UL; u32TimeOut > 0U; u32TimeOut--)
    {
        if (CLOCK->CLKSTS.bit.PLLRDY != CLKSTS_BIT_PLLRDY_NOT_READY)
        {
            break;
        }
    }

    if (u32TimeOut == 0)
    {
        return ERROR;
    }

    /* Clear CLKIC */
    CLOCK->CLKIC.bit.CLKINT = 1;
    CLOCK->CLKIC.bit.CLKDETERR = 1;
    CLOCK->CLKIC.bit.PLLUNLOCK = 1;
    return SUCCESS;
}




/******************************************************************************
 * @brief      Update clock related information
 *
 * @param[in]  u32XOFreq :  XO clock frequency
 *                          Can be any value if XO clock is not used
 *
 * @return     none
 *
 ******************************************************************************/
#if defined(__ICCARM__)
#pragma optimize=low
#endif
void CLOCK_UpdateInfo(uint32_t u32XOFreq)
{
    float f32PLLFreq;
    uint32_t u32NFB;
    uint32_t u32NOUT;

    SysInfo.u32SRCCLK[0] = __RCO0_CLOCK;
    SysInfo.u32SRCCLK[1] = __RCO1_CLOCK;
    SysInfo.u32SRCCLK[2] = u32XOFreq;
    if (CLOCK->PLLCTL0.all & PLLCTL0_ALL_RCLKSELXO_Msk) 
    {
        f32PLLFreq = u32XOFreq / 65536.0;
    }
    else
    {
        f32PLLFreq = __RCO0_CLOCK / 65536.0;
    }

    if (CLOCK->PLLCTL1.all & PLLCTL1_ALL_NIN_Msk)
    {
        f32PLLFreq = f32PLLFreq / READ_FIELD(CLOCK->PLLCTL1.all, PLLCTL1_ALL_NIN_Msk, PLLCTL1_ALL_NIN_Pos);
    }

    /* Feedback dividing ratio */
    u32NFB = READ_FIELD(CLOCK->PLLCTL1.all, PLLCTL1_ALL_NFB_Msk, PLLCTL1_ALL_NFB_Pos);

    /* VCO to digital clock dividing ratio */
    u32NOUT = READ_FIELD(CLOCK->PLLCTL1.all, PLLCTL1_ALL_NOUT_Msk, PLLCTL1_ALL_NOUT_Pos);

    f32PLLFreq = f32PLLFreq * (u32NFB / (u32NOUT + 1));

    SysInfo.u32SRCCLK[3] = (uint32_t)f32PLLFreq;
    SysInfo.u32SYSCLK0 = SysInfo.u32SRCCLK[CLOCK_GetSystemClock0Source()];
    SysInfo.u32SYSCLK1 = SysInfo.u32SRCCLK[CLOCK_GetSystemClock1Source()];
    SysInfo.u32WDT0CLK = SysInfo.u32SRCCLK[CLOCK_GetWDT0ClockSource()];
    SysInfo.u32WDT1CLK = SysInfo.u32SRCCLK[CLOCK_GetWDT1ClockSource()];
    SysInfo.u32HCLK = SysInfo.u32SYSCLK0/(READ_FIELD(CLOCK->HCLKCTL.bit.DIV, HCLKCTL_ALL_DIV_Msk, HCLKCTL_ALL_DIV_Pos)+1);
    SysInfo.u32PCLK = SysInfo.u32SYSCLK1/(READ_FIELD(CLOCK->PCLKCTL.bit.DIV, PCLKCTL_ALL_DIV_Msk, PCLKCTL_ALL_DIV_Pos)+1);
}




/******************************************************************************
 * @brief      Enable the clock of the selected peripheral module
 *
 * @param[in]  eModule:  The peripheral module defined by CLOCK_ModuleEnum
 *
 * @return     none
 *
 * @example    CLOCK_EnableModule(ADC_MODULE);
 *
 ******************************************************************************/
#if defined(__ICCARM__)
#pragma optimize=low
#endif
void CLOCK_EnableModule(CLOCK_ModuleEnum eModule)
{
    if (eModule == SSP_MODULE)
    {
        CLOCK->SSPCLKCTL.bit.EN = 1;
    }
    else
    {
        CLOCK_MODULECTL(eModule)->bit.EN = 1;
    }
}




/******************************************************************************
 * @brief      Disable the clock of the selected peripheral module
 *
 * @param[in]  eModule:  The peripheral module defined by CLOCK_ModuleEnum
 *
 * @return     none
 *
 * @example    CLOCK_DisableModule(ADC_MODULE);
 *
 ******************************************************************************/
#if defined(__ICCARM__)
#pragma optimize=low
#endif
void CLOCK_DisableModule(CLOCK_ModuleEnum eModule)
{
    if (eModule == SSP_MODULE)
    {
        CLOCK->SSPCLKCTL.bit.EN = 0;
    }
    else
    {
        CLOCK_MODULECTL(eModule)->bit.EN = 0;
    }
}




/******************************************************************************
 * @brief      Set the clock dividing ratio of the selected peripheral module
 *
 * @param[in]  eModule:  The peripheral module defined by CLOCK_ModuleEnum
 * @param[in]  u32Div :  Clock dividing ratio value
 *
 * @return     none
 *
 * @example    CLOCK_SetModuleDiv(PWM_MODULE, 2);
 *             CLOCK_SetModuleDiv(ADC_MODULE, 4);
 *
 ******************************************************************************/
#if defined(__ICCARM__)
#pragma optimize=low
#endif
void CLOCK_SetModuleDiv(CLOCK_ModuleEnum eModule, uint32_t u32Div)
{
    if (eModule == SSP_MODULE)
    {
        CLOCK->SSPCLKCTL.bit.DIV = (u32Div - 1);
    }
    else
    {
        CLOCK_MODULECTL(eModule)->bit.DIV = (u32Div - 1);
    }
}




/******************************************************************************
 * @brief      Get the clock dividing ratio of the selected peripheral module
 *
 * @param[in]  eModule:  The peripheral module defined by CLOCK_ModuleEnum
 *
 * @return     Clock dividing ratio value range from 1~64
 *
 ******************************************************************************/
#if defined(__ICCARM__)
#pragma optimize=low
#endif
uint32_t CLOCK_GetModuleDiv(CLOCK_ModuleEnum eModule)
{
    uint32_t u32Div;

    if (eModule == SSP_MODULE)
    {
        u32Div = CLOCK->SSPCLKCTL.bit.DIV;
    }
    else
    {
        u32Div = CLOCK_MODULECTL(eModule)->bit.DIV;
    }

    return (u32Div + 1);
}




/******************************************************************************
 * @brief      Get the clock frequency (Hz) of the selected peripheral module
 *
 * @param[in]  eModule:  The peripheral module defined by CLOCK_ModuleEnum
 *
 * @return     Clock frequency in Hz
 *
 * @note       This function can only excute correctly after CLOCK_Init()
 *
 ******************************************************************************/
#if defined(__ICCARM__)
#pragma optimize=low
#endif
uint32_t CLOCK_GetModuleClock(CLOCK_ModuleEnum eModule)
{
    uint32_t u32Div, u32Clk;

    u32Div = CLOCK_GetModuleDiv(eModule);

    if (eModule == WDT0_MODULE)
    {
        u32Clk = SysInfo.u32WDT0CLK / u32Div;
    }
    else if (eModule == WDT1_MODULE)
    {
        u32Clk = SysInfo.u32WDT1CLK / u32Div;
    }
    else
    {
        if (eModule == UART_MODULE || eModule == SSP_MODULE ||
                    eModule == I2C_MODULE )
        {
            u32Clk      = SysInfo.u32SYSCLK1 / u32Div;
        }
        else
        {
            u32Clk      = SysInfo.u32SYSCLK0 / u32Div;
        }
    }

    return u32Clk;
}


#if defined(__CC_ARM)
    #pragma pop
#endif


/******************* Copyright (C) 2022 Spintrol Electronic Technology (Shanghai) Co., Ltd. ***** END OF FILE ****/
