/*!
* @file     Mcu_Rcm.c
*
* @version  1.0.0
*
* @brief    The underlying driver code of the RCM module.
*
* @attention
*
* Project Name      : AUTOSAR MCAL
*
* Platform          : Arm
*
* Autosar Standard  : Classic Platform 4.4.0
*
* Compiled Version  : G32A10xx_MCAL_1_0_0_01-Otc-25
*
* Copyright (C) 2026 Geehy Semiconductor
*
* You may not use this file except in compliance with the
* GEEHY COPYRIGHT NOTICE (Geehy Semiconductor Software License Agreement).
*
* The program is only for reference, which is distributed in the hope
* that it will be usefull and instructional for customers to develop
* their software. Unless required by applicable law or agreed to in
* writing, the program is distributed on an "AS IS" BASIS, WITHOUT
* ANY WARRANTY OR CONDITIONS OF ANY KIND, either express or implied.
* See the Geehy Semiconductor Software License Agreement for the governing permissions
* and limitations under the License.
*/

/*
* MISRA-C:2012 compliance checks
*
*      Mcu_Rcm_c_MISRA_REF1
*          Breaks the required Rule-1.1 of MISRA 2012 guidelines,
*          The program shall contain no violations of the standard C syntax and constraints, and shall not exceed the implementation's translation limits
*
*      Mcu_Rcm_c_MISRA_REF2
*          Breaks the required Rule-10.3 of MISRA 2012 guidelines,
*          The value of an expression shall not be assigned to an object with a narrower essential type or of a different essential type category.
*
*/

#ifdef __cplusplus
extern "C"{
#endif

#include "Mcu_RcmTypes.h"
#include "Mcu_RcmReg.h"
	
/** @addtogroup G32A10xx_StdPeriphDriver
  @{
*/

/** @addtogroup MCU_Driver
  * @brief MCU driver modules
  @{
*/

/** @defgroup MCU_Macros
  @{
*/
/*******************************************************************************
                    VENDOR ID & AUTOSAR VERSION INFO
*******************************************************************************/
/** Vendor ID (vendorId) of the dedicated implementation of this module according to the AUTOSAR vendor list. */
#define MCU_RCM_VENDOR_ID                              0x00C1U
/** Major version number of AUTOSAR release on which the appropriate implementation is based on. */
#define MCU_RCM_AR_RELEASE_MAJOR_VERSION               0x04U
/** Minor version number of AUTOSAR release on which the appropriate implementation is based on. */
#define MCU_RCM_AR_RELEASE_MINOR_VERSION               0x04U
/** Revision version number of AUTOSAR release on which the appropriate implementation is based on. */
#define MCU_RCM_AR_RELEASE_REVISION_VERSION            0x00U
/** Major version number of the vendor specific implementation of the module. The numbering is vendor specific. */ 
#define MCU_RCM_SW_MAJOR_VERSION                       0x01U
/** Minor version number of the vendor specific implementation of the module. The numbering is vendor specific. */
#define MCU_RCM_SW_MINOR_VERSION                       0x00U
/** Patch level version number of the vendor specific implementation of the module. The numbering is vendor specific. */
#define MCU_RCM_SW_PATCH_VERSION                       0x00U

/**@} end of group MCU_Macros */
/*******************************************************************************
                               LOCAL FUNCTIONS
*******************************************************************************/
#define MCU_START_SEC_CODE
#include "Mcu_MemMap.h"

/** @defgroup MCU_Fuctions
  @{
*/

/*!
* @brief       Configures HSE as system clock source
* 
* @param       clockSourceConfig   Clock configuration structure
* 
* @retval      STATUS_SUCCESS      Operation successful
* @retval      STATUS_ERROR        Operation failed
* 
* @note        Safety switching protocol:
*              1. Checks if HSE is current system clock
*              2. If disabling active HSE clock: returns error
*              3. Switches to HSI as temporary clock
*              4. Reconfigures HSE and waits for stabilization
*              5. Switches back to HSE
*
*/
static Mcu_Rcm_StatusType Mcu_Rcm_ConfigHseClockSource(const Mcu_Rcm_ConfigType * clockSourceConfig)
{
    Mcu_Rcm_StatusType returnCode = STATUS_SUCCESS;

    /* Check whether the current system clock source is SOSC */
    if (RCM_SYSCLK_SEL_HSE == Mcu_Rcm_ReadSYSCLKSource())
    {
        if (clockSourceConfig->hseClockCfg.enableHseClock == CLK_DISABLE)
        {
            /* Can't disable SOSC, it's used as system clock source. */
            returnCode = STATUS_ERROR;
        }
        else
        {
            /* Transition to a temporary system clock source. */
            Mcu_Rcm_SetHSITrim(16);

            /* Enable HSI */
            Mcu_Rcm_EnableHSI();
            
            if ((uint8)SUCCESS == Mcu_Rcm_WaitHSIReady())
            {
                returnCode = STATUS_SUCCESS;
                /* select HSI as SYSCLK */
                Mcu_Rcm_ConfigSYSCLK(RCM_SYSCLK_SEL_HSI);
            }
            else
            {
                returnCode = STATUS_ERROR;
            }
        }
    }
    else
    {
        /* nothing */
    }

    if (returnCode == STATUS_SUCCESS)
    {
#ifdef MCU_RCM_ISR_NOTIFICATION
        Mcu_Rcm_ConfigHSE(RCM_HSE_CLOSE);
        
        Mcu_Rcm_EnableInterrupt(RCM_HSE_READY_INT_IRQ);
#endif
        while (Mcu_Rcm_WaitHSEReady() == (uint8)ERROR)
        {
            /* nothing */
        }
        
        /* select HSE as SYSCLK */
        Mcu_Rcm_ConfigSYSCLK(RCM_SYSCLK_SEL_HSE);
    }
    else
    {
        /* nothing */
    }

    return returnCode;
}



/*!
* @brief       Configures HSI as system clock source
* 
* @param       clockSourceConfig   Clock configuration structure
* 
* @retval      STATUS_SUCCESS      Operation successful
* @retval      STATUS_ERROR        Operation failed
* 
* @note        Safety switching protocol:
*              1. Checks if HSI is current system clock
*              2. If disabling active HSI clock: returns error
*              3. Switches to HSE as temporary clock
*              4. Reconfigures HSI and waits for stabilization
*              5. Switches back to HSI
*              Uses HSI trimming for frequency calibration
*/
static Mcu_Rcm_StatusType Mcu_Rcm_ConfigHsiClockSource(const Mcu_Rcm_ConfigType * clockSourceConfig)
{
    Mcu_Rcm_StatusType returnCode = STATUS_SUCCESS;

    /* Check whether the current system clock source is SOSC */
    if (RCM_SYSCLK_SEL_HSI == Mcu_Rcm_ReadSYSCLKSource())
    {
        if (clockSourceConfig->hsiClockCfg.enableHsiClock == CLK_DISABLE)
        {
            /* Can't disable SOSC, it's used as system clock source. */
            returnCode = STATUS_ERROR;
        }
        else
        {
            Mcu_Rcm_ConfigHSE(RCM_HSE_OPEN);
            
            if ((uint8)SUCCESS == Mcu_Rcm_WaitHSEReady())
            {
                returnCode = STATUS_SUCCESS;
                /* select HSI as SYSCLK */
                Mcu_Rcm_ConfigSYSCLK(RCM_SYSCLK_SEL_HSE);
            }
            else
            {
                returnCode = STATUS_ERROR;
            }
        }
    }
    else
    {
        /* nothing */
    }

    if (returnCode == STATUS_SUCCESS)
    {
#ifdef MCU_RCM_ISR_NOTIFICATION
        Mcu_Rcm_DisableHSI();
        
        Mcu_Rcm_EnableInterrupt(RCM_HSI_READY_INT_IRQ);
#endif
        while (Mcu_Rcm_WaitHSIReady() == (uint8)ERROR)
        {
            /* nothing */
        }
        
        Mcu_Rcm_ConfigSYSCLK(RCM_SYSCLK_SEL_HSI);
    }
    else
    {
        /* nothing */
    }

    return returnCode;
}



/*!
* @brief       Configures SPLL as system clock source
* 
* @param       clockSourceConfig   Clock configuration structure
* 
* @retval      STATUS_SUCCESS      Operation successful
* @retval      STATUS_ERROR        Operation failed
* 
* @note        Safety switching protocol:
*              1. Checks if PLL is current system clock
*              2. If disabling active PLL clock: returns error
*              3. Switches to HSI as temporary clock
*              4. Reconfigures PLL and waits for lock
*              5. Switches back to PLL
*              PLL requires stable reference clock (HSE/HSI)
*/
static Mcu_Rcm_StatusType Mcu_Rcm_ConfigSpllClockSource(const Mcu_Rcm_ConfigType * clockSourceConfig)
{
    Mcu_Rcm_StatusType returnCode = STATUS_SUCCESS;

    /* Check whether the current system clock source is SOSC */
    if (RCM_SYSCLK_SEL_PLL == Mcu_Rcm_ReadSYSCLKSource())
    {
        if (clockSourceConfig->pllClockCfg.enablePllClock == CLK_DISABLE)
        {
            /* Can't disable SOSC, it's used as system clock source. */
            returnCode = STATUS_ERROR;
        }
        else
        {
            /* Transition to a temporary system clock source. */
            Mcu_Rcm_SetHSITrim(16);

            /* Enable HSI */
            Mcu_Rcm_EnableHSI();
            
            if ((uint8)SUCCESS == Mcu_Rcm_WaitHSIReady())
            {
                returnCode = STATUS_SUCCESS;
                /* select HSI as SYSCLK */
                Mcu_Rcm_ConfigSYSCLK(RCM_SYSCLK_SEL_HSI);
            }
            else
            {
                returnCode = STATUS_ERROR;
            }
        }
    }
    else
    {
        /* nothing */
    }

    if (returnCode == STATUS_SUCCESS)
    {
#ifdef MCU_RCM_ISR_NOTIFICATION
        Mcu_Rcm_EnableInterrupt(RCM_PLL_READY_INT_IRQ);
#endif
        Mcu_Rcm_ConfigSYSCLK(RCM_SYSCLK_SEL_PLL);
    }
    else
    {
        /* nothing */
    }

    return returnCode;
}

/*******************************************************************************
                              GLOBAL FUNCTION
*******************************************************************************/
/*!
* @brief       rcm init function
* 
* @param       clockSourceConfig   Clock configuration structure
* 
* @retval      STATUS_SUCCESS      Operation successful
* @retval      STATUS_ERROR        Operation failed
* 
* @note        none
*
*/
FUNC(Mcu_Rcm_StatusType, MCU_CODE) Mcu_Rcm_Init(const Mcu_Rcm_ConfigType * clockSourceConfig)
{
    Mcu_Rcm_StatusType returnCode = STATUS_SUCCESS;
    
    if (CLK_DISABLE == clockSourceConfig->hsiClockCfg.enableHsiClock)
    {
        Mcu_Rcm_DisableHSI();
    }
    else
    {
        /* set HSI trimming value */
        Mcu_Rcm_SetHSITrim(clockSourceConfig->hsiClockCfg.hsiTrim);
        /* Enable HSI */
        Mcu_Rcm_EnableHSI();
    }
    
    if (CLK_DISABLE == clockSourceConfig->hseClockCfg.enableHseClock)
    {
        Mcu_Rcm_ConfigHSE(RCM_HSE_CLOSE);
    }
    else
    {
        Mcu_Rcm_ConfigHSE(RCM_HSE_OPEN);
    }
    
    if (CLK_DISABLE == clockSourceConfig->pllClockCfg.enablePllClock)
    {
        Mcu_Rcm_DisablePLL();
    }
    else
    {
        /* Disable PLL */
        Mcu_Rcm_DisablePLL();
        
        /* Config PLL source and multiplication factor */
        Mcu_Rcm_ConfigPLL(clockSourceConfig->pllClockCfg.pllSelect,
                      clockSourceConfig->pllClockCfg.pllMf);
        Mcu_Rcm_ConfigClkDiv(clockSourceConfig->pllClockCfg.pllDiv);
        
        /* Enable PLL */
        Mcu_Rcm_EnablePLL();
        
        /* Wait until Pll is ready */
        while (REG_BIT_GET32(RCM_CTRL1_REG_ADDR, RCM_CTRL1_PLLRDYFLG_MASK) == (uint8)0U)
        {
            /* nothing */
        }
    }

    switch (clockSourceConfig->clockSource)
    {
        case HSECLK_CLK:
            returnCode = Mcu_Rcm_ConfigHseClockSource(clockSourceConfig);
            break;
        case HSICLK_CLK:
            returnCode = Mcu_Rcm_ConfigHsiClockSource(clockSourceConfig);
            break;
        case PLLCLK_CLK:
            returnCode = Mcu_Rcm_ConfigSpllClockSource(clockSourceConfig);
            break;
        default:
            /* nothing */
            break;
    }
    
#ifdef MCU_ERR_ISR_NOTIFICATION
    Mcu_Rcm_EnableInterrupt(RCM_FREQ_UNDER_ERR_IRQ);
    Mcu_Rcm_EnableInterrupt(RCM_FREQ_OVER_ERR_IRQ);
    Mcu_Rcm_EnableInterrupt(RCM_CLOCK_LOST_ERR_IRQ);
#endif
    
    return returnCode;
}
    

/*!
* @brief       Reads system reset flags
* 
* @return      Raw reset flags value (bitmask)
* 
* @note        Performs read-clear mechanism:
*              1. Reads RCM_CSTS register
*              2. Masks with 0xBEC00000 to get relevant flags
*/
FUNC(Mcu_RawResetType, MCU_CODE) Mcu_Rcm_ReadResetFlag(void)
{
    Mcu_RawResetType RawReset = 0;
    
    RawReset = REG_READ32(RCM_CSTS_REG_ADDR) & 0xBEC00000U;

    return RawReset;
}



/*!
* @brief       clears flag register
* 
* @return      void
* 
*              1. If any flags set, writes RCM_CSTS[RSTFLGCLR] to clear
*              2. Returns original flag values
*/
FUNC(void, MCU_CODE) Mcu_Rcm_ClearResetFlag(void)
{
    REG_BIT_SET32(RCM_CSTS_REG_ADDR,RCM_CSTS_RSTFLGCLR_MASK);
}

 
/*!
* @brief       Reads current PLL lock status
* 
* @return      PLL status (MCU_PLL_STATUS_LOCKED/UNLOCKED)
* 
* @note        Checks RCM_CTRL1[PLLRDYFLG] bit
* @note        Does NOT initiate PLL lock sequence
* @note        Status reflects actual hardware state at read time
*/
FUNC(Mcu_PllStatusType, MCU_CODE) Mcu_Rcm_GetPllState(void)
{
    Mcu_PllStatusType PllStatus = MCU_PLL_UNLOCKED;
    uint32 regResult = ((uint32)REG_READ32(RCM_CTRL1_REG_ADDR) & (uint32)RCM_CTRL1_PLLRDYFLG_MASK) >> RCM_CTRL1_PLLRDYFLG_SHIFT;

    if (regResult == (uint32)MCU_PLL_UNLOCKED)
    {
        PllStatus = MCU_PLL_UNLOCKED;
    }
    else if (regResult == (uint32)MCU_PLL_LOCKED)
    {
        PllStatus = MCU_PLL_LOCKED;
    }
    else
    {
        PllStatus = MCU_PLL_STATUS_UNDEFINED;
    }
    
    return PllStatus;
}



/*!
* @brief       Triggers Cortex-M4 system reset
* 
* @param       None
* 
* @note        Uses ARM System Control Block (SCB) registers:
*              1. Writes 0x5FA to VECTKEY field (write access key)
*              2. Sets SYSRESETREQ bit to request reset
*
*/
FUNC(void, MCU_CODE) Mcu_Rcm_CM4SysReset(void)
{
    uint32 scbReg = (uint32)((uint32)0x5FAU << (uint32)SCB_AIRCR_VECTKEY_Pos)| (uint32)SCB_AIRCR_SYSRESETREQ_Msk;  
    /*** @warning Mcu_Rcm_c_MISRA_REF1 [C] Left operand of '->' must be a pointer to a 'struct' or 'union' object. */
    /*** @warning Mcu_Rcm_c_MISRA_REF1 [C] Left operand of assignment must be an lvalue (it must designate an object). */
    /*** @warning Mcu_Rcm_c_MISRA_REF2 A non-constant expression of 'essentially unsigned' type (%1s) is being converted to signed type, '%2s' on assignment. */
    SCB->AIRCR = scbReg;
}


/*!
* @brief       Configures HSE
*
* @param       state:   state of the HSE
*                       This parameter can be one of the following values:
*                       RCM_HSE_OPEN: turn ON the HSE oscillator
*                       RCM_HSE_BYPASS: HSE oscillator bypassed with external clock
*
* @retval      None
*
* @note        HSE can not be stopped if it is used directly or through the PLL as system clock
*/
FUNC(void, MCU_CODE) Mcu_Rcm_ConfigHSE(Mcu_Rcm_HseSelType state)
{
    REG_BIT_CLEAR32(RCM_CTRL1_REG_ADDR,RCM_CTRL1_HSEEN_MASK);

    REG_BIT_CLEAR32(RCM_CTRL1_REG_ADDR,RCM_CTRL1_HSEBCFG_MASK);

    if (state == RCM_HSE_OPEN)
    {
        REG_BIT_SET32(RCM_CTRL1_REG_ADDR,RCM_CTRL1_HSEEN_MASK);
    }
    else if (state == RCM_HSE_BYPASS)
    {
        REG_BIT_SET32(RCM_CTRL1_REG_ADDR,RCM_CTRL1_HSEBCFG_MASK);
        REG_BIT_SET32(RCM_CTRL1_REG_ADDR,RCM_CTRL1_HSEEN_MASK);
    }
    else
    {
        /* nothing */
    }
}

/*!
* @brief       Waits for HSE be ready
*
* @param       None
*
* @retval      SUCCESS     HSE oscillator is stable and ready to use
*              ERROR       HSE oscillator not yet ready
*/
FUNC(uint8, MCU_CODE) Mcu_Rcm_WaitHSEReady(void)
{
    uint32 cnt = 0;
    uint8 returnVal = ERROR;

    for (cnt = 0; cnt < (uint32)MCU_HSE_STARTUP_TIMEOUT; cnt++)
    {
        if (((REG_BIT_GET32(RCM_CTRL1_REG_ADDR, RCM_CTRL1_HSERDYFLG_MASK)) >> RCM_CTRL1_HSERDYFLG_SHIFT) == (uint32)BIT_SET)
        {
            returnVal = SUCCESS;
            break;
        }
        else
        {
            /* nothing */
        }
    }

    return returnVal;
}


/*!
* @brief       Waits for HSE be ready
*
* @param       None
*
* @retval      SUCCESS     HSE oscillator is stable and ready to use
*              ERROR       HSE oscillator not yet ready
*/
FUNC(uint8, MCU_CODE) Mcu_Rcm_WaitHSIReady(void)
{
    uint32 cnt = 0;
    uint8 returnVal = ERROR;

    for (cnt = 0; cnt < (uint32)MCU_HSE_STARTUP_TIMEOUT; cnt++)
    {
        if (((REG_BIT_GET32(RCM_CTRL1_REG_ADDR, RCM_CTRL1_HSIRDYFLG_MASK)) >> RCM_CTRL1_HSIRDYFLG_SHIFT) == (uint32)BIT_SET)
        {
            returnVal = SUCCESS;
            break;
        }
        else
        {
            /* nothing */
        }
    }

    return returnVal;
}


/*!
* @brief       Set HSI trimming value
*
* @param        hsiTrim:    HSI trimming value
*                          This parameter must be a number between 0 and 0x1F.
*
* @retval      None
*/
FUNC(void, MCU_CODE) Mcu_Rcm_SetHSITrim(uint8 hsiTrim)
{
    REG_RMW32(RCM_CTRL1_REG_ADDR,RCM_CTRL1_HSITRM_MASK,((uint32)hsiTrim << RCM_CTRL1_HSITRM_SHIFT));
}

/*!
* @brief       Enable HSI
*
* @param       None
*
* @note        HSI can not be stopped if it is used directly or through the PLL as system clock
*/
FUNC(void, MCU_CODE) Mcu_Rcm_EnableHSI(void)
{
    REG_BIT_SET32(RCM_CTRL1_REG_ADDR,RCM_CTRL1_HSIEN_MASK);
}

/*!
* @brief       Disable HSI
*
* @param       None
*
* @note        HSI can not be stopped if it is used directly or through the PLL as system clock
*/
FUNC(void, MCU_CODE) Mcu_Rcm_DisableHSI(void)
{
    REG_BIT_CLEAR32(RCM_CTRL1_REG_ADDR,RCM_CTRL1_HSIEN_MASK);
}



/*!
* @brief       Enable HSI14 ADC
*
* @param       None
*
*/
FUNC(void, MCU_CODE) Mcu_Rcm_OperationHSI14ADC(Mcu_Rcm_ClockEnType hsi14ADC)
{
    if (CLK_DISABLE == hsi14ADC)
    {
        REG_BIT_CLEAR32(RCM_CTRL2_REG_ADDR, RCM_CTRL2_HSI14TO_MASK);
    }
    else
    {
        REG_BIT_SET32(RCM_CTRL2_REG_ADDR, RCM_CTRL2_HSI14TO_MASK);
    }
}


/*!
* @brief       Enable LSI
*
* @param       None
*
* @note        LSI can not be stopped if the IWDT is running
*/
FUNC(uint8, MCU_CODE) Mcu_Rcm_OperationLSI(Mcu_Rcm_ClockEnType lsiEna)
{
    uint32 cnt = 0;
    uint8 reVal = ERROR;
    
    if (CLK_ENABLE == lsiEna)
    {
#ifdef MCU_RCM_ISR_NOTIFICATION
        Mcu_Rcm_EnableInterrupt(RCM_LSI_READY_INT_IRQ);
#endif
        REG_BIT_SET32(RCM_CSTS_REG_ADDR, RCM_CSTS_LSIEN_MASK);

        for (cnt = 0; cnt < (uint32)MCU_HSE_STARTUP_TIMEOUT; cnt++)
        {
            if (0U != (REG_BIT_GET32(RCM_CSTS_REG_ADDR, RCM_CSTS_LSIRDYFLG_MASK)))
            {
                reVal = SUCCESS;
            }
            else
            {
                /* nothing */
            }
        }
    }
    else
    {
        REG_BIT_CLEAR32(RCM_CSTS_REG_ADDR, RCM_CSTS_LSIEN_MASK);
        reVal = SUCCESS;
    }

    return reVal;
}

/*!
* @brief       Configures the PLL clock source and multiplication factor
*
* @param       pllSelect:   PLL entry clock source select
*                           This parameter can be one of the following values:
*                           RCM_PLL_SEL_HSI_DIV2: HSI clock divided by 2 selected as PLL clock source
*                           RCM_PLL_SEL_HSE: HSE/CLKDIV1 selected as PLL clock entry
*
* @param       pllMf:       PLL multiplication factor
*                           This parameter can be RCM_PLLMF_x where x:[2,16]
*
*/
FUNC(void, MCU_CODE) Mcu_Rcm_ConfigPLL(Mcu_Rcm_PllSelType pllSelect, Mcu_Rcm_PllmfType pllMf)
{
    if (RCM_PLL_SEL_HSE == pllSelect)
    {
        REG_BIT_SET32(RCM_CFG1_REG_ADDR,RCM_CFG1_PLLSRCSEL_MASK);
    }
    else
    {
        REG_BIT_CLEAR32(RCM_CFG1_REG_ADDR,RCM_CFG1_PLLSRCSEL_MASK);
    }
    REG_RMW32(RCM_CFG1_REG_ADDR,RCM_CFG1_PLLMULCFG_MASK,(uint32)pllMf << RCM_CFG1_PLLMULCFG_SHIFT);
}

/*!
* @brief       Enables PLL
*
* @param       None
*
* @note        The PLL can not be disabled if it is used as system clock
*/
FUNC(void, MCU_CODE) Mcu_Rcm_EnablePLL(void)
{
    REG_BIT_SET32(RCM_CTRL1_REG_ADDR, RCM_CTRL1_PLLEN_MASK);
}

/*!
* @brief       Disable PLL
*
* @param       None
*
* @note        The PLL can not be disabled if it is used as system clock
*/
FUNC(void, MCU_CODE) Mcu_Rcm_DisablePLL(void)
{
    REG_BIT_CLEAR32(RCM_CTRL1_REG_ADDR, RCM_CTRL1_PLLEN_MASK);
}

/*!
* @brief       Configures the CLK division factor
*
* @param       state:   specifies the PLLDIVCFG clock division factor.
*                       This parameter can be RCM_CLK_Divx where x:[1,16]
*
* @note        This function must be used only when the PLL is disabled
*/
FUNC(void, MCU_CODE) Mcu_Rcm_ConfigClkDiv(Mcu_Rcm_ClkDivType state)
{
    REG_BIT_SET32(RCM_CFG2_REG_ADDR,(uint32)state);
}



/*!
* @brief        Enable Clock Security System
*
* @param        None
*
*/
FUNC(void, MCU_CODE) Mcu_Rcm_OperationCCS(Mcu_Rcm_ClockEnType ccsEna)
{
    if (CLK_ENABLE == ccsEna)
    {
        REG_BIT_SET32(RCM_CTRL1_REG_ADDR, RCM_CTRL1_CSSEN_MASK);
    }
    else
    {
        REG_BIT_CLEAR32(RCM_CTRL1_REG_ADDR, RCM_CTRL1_CSSEN_MASK);
    }
}

/*!
* @brief       Selects clock ouput source
*
* @param       cocClock:  specifies the clock source to output
*                         This parameter can be one of the following values:
*                         RCM_COC_NO_CLOCK:     No clock selected.
*                         RCM_COC_HSI14:        HSI14 oscillator clock selected.
*                         RCM_COC_LSI:          LSI oscillator clock selected.
*                         RCM_COC_LSE:          LSE oscillator clock selected.
*                         RCM_COC_SYSCLK:       System clock selected.
*                         RCM_COC_HSI:          HSI oscillator clock selected.
*                         RCM_COC_HSE:          HSE oscillator clock selected.
*                         RCM_COC_PLLCLK_DIV_2: PLL clock divided by 2 selected.
*
*/
void Mcu_Rcm_ConfigCOC(Mcu_Rcm_CocClkType cocClock)
{
    REG_RMW32(RCM_CFG1_REG_ADDR,RCM_CFG1_MCOSEL_MASK,
             ((uint32)cocClock << RCM_CFG1_MCOSEL_SHIFT));
}

/*!
* @brief       Configures the system clock
*
* @param       sysClkSelect:   specifies the clock source used as system clock
*                              This parameter can be one of the following values:
*                              RCM_SYSCLK_SEL_HSI:    HSI selected as system clock source
*                              RCM_SYSCLK_SEL_HSE:    HSE selected as system clock source
*                              RCM_SYSCLK_SEL_PLL:    PLL selected as system clock source
*
*/
FUNC(void, MCU_CODE) Mcu_Rcm_ConfigSYSCLK(Mcu_Rcm_SyscSelType sysClkSelect)
{
    REG_RMW32(RCM_CFG1_REG_ADDR,RCM_CFG1_SCLKSEL_MASK,
             ((uint32)sysClkSelect << RCM_CFG1_SCLKSEL_SHIFT));
}

/*!
* @brief       returns the clock source used as system clock
*
* @param       None
*
* @retval      The clock source used as system clock
*/
FUNC(Mcu_Rcm_SyscSelType, MCU_CODE) Mcu_Rcm_ReadSYSCLKSource(void)
{
    Mcu_Rcm_SyscSelType sysClock = RCM_SYSCLK_SEL_HSI;
    uint32 regResult = (REG_READ32(RCM_CFG1_REG_ADDR) & (uint32)RCM_CFG1_SCLKSELSTS_MASK) >> RCM_CFG1_SCLKSELSTS_SHIFT;
    
    if (regResult == (uint32)RCM_SYSCLK_SEL_HSI)
    {
        sysClock = RCM_SYSCLK_SEL_HSI;
    }
    else if (regResult == (uint32)RCM_SYSCLK_SEL_HSE)
    {
        sysClock = RCM_SYSCLK_SEL_HSE;
    }
    else
    {
        sysClock = RCM_SYSCLK_SEL_PLL;
    }

    return sysClock;
}

#if (MCU_INITCLK_ENABLED == STD_ON)
/*!
* @brief       Configures the AHB clock
*
* @param       AHBDiv:   AHB divider number. This clock is derived from the system clock (SYSCLK)
*                        This parameter can be one of the following values:
*                        RCM_SYSCLK_DIV_1:   AHB clock = SYSCLK
*                        RCM_SYSCLK_DIV_2:   AHB clock = SYSCLK/2
*                        RCM_SYSCLK_DIV_4:   AHB clock = SYSCLK/4
*                        RCM_SYSCLK_DIV_8:   AHB clock = SYSCLK/8
*                        RCM_SYSCLK_DIV_16:  AHB clock = SYSCLK/16
*                        RCM_SYSCLK_DIV_64:  AHB clock = SYSCLK/64
*                        RCM_SYSCLK_DIV_128: AHB clock = SYSCLK/128
*                        RCM_SYSCLK_DIV_256: AHB clock = SYSCLK/256
*                        RCM_SYSCLK_DIV_512: AHB clock = SYSCLK/512
*
*/
FUNC(void, MCU_CODE) Mcu_Rcm_ConfigAHB(Mcu_Rcm_AHBDivType AHBDiv)
{
    REG_RMW32(RCM_CFG1_REG_ADDR,RCM_CFG1_AHBPSC_MASK,
             ((uint32)AHBDiv << RCM_CFG1_AHBPSC_SHIFT));
}

/*!
* @brief       Configures the APB clock
*
* @param       APBDiv:   defines the APB clock divider. This clock is derived from the AHB clock (HCLK)
*                        This parameter can be one of the following values:
*                        RCM_HCLK_DIV_1:  APB clock = HCLK
*                        RCM_HCLK_DIV_2:  APB clock = HCLK/2
*                        RCM_HCLK_DIV_4:  APB clock = HCLK/4
*                        RCM_HCLK_DIV_8:  APB clock = HCLK/8
*                        RCM_HCLK_DIV_16: APB clock = HCLK/16
*
*/
FUNC(void, MCU_CODE) Mcu_Rcm_ConfigAPB(Mcu_Rcm_APBDivType APBDiv)
{
    REG_RMW32(RCM_CFG1_REG_ADDR,RCM_CFG1_APB1PSC_MASK,
             ((uint32)APBDiv << RCM_CFG1_APB1PSC_SHIFT));
}


/*!
* @brief       Enable HSI14
*
* @param       None
*
*/
FUNC(uint8, MCU_CODE) Mcu_Rcm_OperationHSI14(Mcu_Rcm_Hsi14ClockCfgType hsi14ClkCfg)
{
    uint32 cnt = 0;
    uint8 reVal = ERROR;
    
    if (CLK_ENABLE == hsi14ClkCfg.enableHsi14Clock)
    {
        REG_RMW32(
            RCM_CTRL2_REG_ADDR, 
            RCM_CTRL2_HSI14TRM_MASK,
            ((uint32)hsi14ClkCfg.hsi14Trim << RCM_CTRL2_HSI14TRM_SHIFT) 
        );
#ifdef MCU_RCM_ISR_NOTIFICATION
        Mcu_Rcm_EnableInterrupt(RCM_LSI_READY_INT_IRQ);
#endif
        REG_BIT_SET32(RCM_CTRL2_REG_ADDR, RCM_CTRL2_HSI14EN_MASK);

        for (cnt = 0; cnt < (uint32)MCU_HSE_STARTUP_TIMEOUT; cnt++)
        {
            if (0U != (REG_BIT_GET32(RCM_CTRL2_REG_ADDR, RCM_CTRL2_HSI14RDFLG_MASK)))
            {
                reVal = SUCCESS;
            }
            else
            {
                /* nothing */
            }
        }
    }
    else
    {
        REG_BIT_CLEAR32(RCM_CTRL2_REG_ADDR, RCM_CTRL2_HSI14EN_MASK);
        reVal = SUCCESS;
    }

    return reVal;
}

/*!
* @brief       Configures clock source for USART/CAN peripherals
* 
* @param       cfg3Sel    Peripheral selection (USART1/USART2/CANFD)
* @param       clk        Clock source selection
* 
* @return      None
* 
*/
FUNC(void, MCU_CODE) Mcu_Rcm_ConfigUsartOrCanClk(Mcu_Rcm_Cfg3SelType cfg3Sel,
                                                 Mcu_Rcm_Cfg3ClkType clk)
{
    if (RCM_USART1_SEL == cfg3Sel)
    {
        REG_RMW32(RCM_CFG3_REG_ADDR,RCM_CFG3_USART1SEL_MASK,
                 (((uint32)clk & 0x3U) << RCM_CFG3_USART1SEL_SHIFT));
    }
    else if (RCM_USART2_SEL == cfg3Sel)
    {
        REG_RMW32(RCM_CFG3_REG_ADDR,RCM_CFG3_USART2SEL_MASK,
                 (((uint32)clk & 0xCU) << RCM_CFG3_USART1SEL_SHIFT));
    }
    else if (RCM_CANFD_SEL == cfg3Sel)
    {
        REG_RMW32(RCM_CFG3_REG_ADDR,RCM_CFG3_CANFDSEL_MASK,
                 (((uint32)clk & 0x1000U) << RCM_CFG3_USART1SEL_SHIFT));
    }
    else
    {
        /* nothing */
    }
}


/*!
* @brief       Configures Real-Time Clock (RTC) subsystem
* 
* @param       rtcCfg     RTC configuration structure
* 
* @return      None
* 
*/
FUNC(void, MCU_CODE) Mcu_Rcm_ConfigRTC(Mcu_Rcm_RtcClockCfgType rtcCfg)
{
    if (CLK_ENABLE == rtcCfg.enable)
    {
        REG_BIT_CLEAR32(RCM_RTCCTRL_REG_ADDR, RCM_RTCCTRL_RTCRST_MASK);
        REG_BIT_SET32(RCM_RTCCTRL_REG_ADDR, RCM_RTCCTRL_RTCCLKEN_MASK);
        REG_RMW32(RCM_RTCCTRL_REG_ADDR,RCM_RTCCTRL_RTCSRCSEL_MASK,
                 ((uint32)rtcCfg.rtcClk << RCM_RTCCTRL_RTCSRCSEL_SHIFT));
    }
    else
    {
        REG_BIT_SET32(RCM_RTCCTRL_REG_ADDR, RCM_RTCCTRL_RTCRST_MASK);
    }
}
#endif



/*!
* @brief       Read frequency of SYSCLK
*
* @param       None
*
* @retval      Return frequency of SYSCLK
*/
FUNC(uint32, MCU_CODE) Mcu_Rcm_ReadSYSCLKFreq(void)
{
    uint32 sysClock, pllMull, plldiv, pllSource, regValCFG1, regValCFG2;
    
    uint32 regVal = REG_READ32(RCM_CFG1_REG_ADDR);
    sysClock = (regVal & RCM_CFG1_SCLKSEL_MASK) >> RCM_CFG1_SCLKSEL_SHIFT;

    switch (sysClock)
    {
        case (uint32)RCM_SYSCLK_SEL_HSI:
            sysClock = HSI_VALUE;
            break;

        case (uint32)RCM_SYSCLK_SEL_HSE:
            sysClock = HSE_VALUE;
            break;

        case (uint32)RCM_SYSCLK_SEL_PLL:
        {
            regValCFG1 = REG_READ32(RCM_CFG1_REG_ADDR);
            pllMull = ((regValCFG1 & RCM_CFG1_PLLMULCFG_MASK) >> RCM_CFG1_PLLMULCFG_SHIFT) + 2U;

            pllSource = (regValCFG1 & RCM_CFG1_PLLSRCSEL_MASK) >> RCM_CFG1_PLLSRCSEL_SHIFT;

            {
                regValCFG2 = REG_READ32(RCM_CFG2_REG_ADDR);
                plldiv = ((regValCFG2 & RCM_CFG2_PLLDIVCFG_MASK) >> RCM_CFG2_PLLDIVCFG_SHIFT) + 1U;
            }

            if (pllSource == (uint32)0x00U)
            {
                sysClock = (HSI_VALUE >> 1U) * pllMull;
            }
            else
            {
                sysClock = ((uint32)HSE_VALUE / plldiv) * pllMull;
            }
            break;
        }

        default:
            sysClock = HSI_VALUE;
            break;
    }

    return sysClock;
}

/*!
* @brief       Read frequency of HCLK(AHB)
*
* @param       None
*
* @retval      Return frequency of HCLK
*/
FUNC(uint32, MCU_CODE) Mcu_Rcm_ReadHCLKFreq(void)
{
    uint32 divider;
    uint32 sysClk, hclk;
    const uint32 AHBPrescTable[16] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 6, 7, 8, 9};
    uint32 regVal = REG_READ32(RCM_CFG1_REG_ADDR);
    uint32 ahbpsc = (regVal & RCM_CFG1_AHBPSC_MASK) >> RCM_CFG1_AHBPSC_SHIFT;

    sysClk = Mcu_Rcm_ReadSYSCLKFreq();
    divider = AHBPrescTable[ahbpsc];
    
    hclk = sysClk >> divider;

    return hclk;
}

/*!
* @brief       Read frequency of PCLK
*
* @param       None
*
* @retval      pclk   return frequency of PCLK
*/
FUNC(uint32, MCU_CODE) Mcu_Rcm_ReadPCLKFreq(void)
{
    uint32 hclk, pclk, divider;
    const uint32 APBPrescTable[8] = {(uint32)0, (uint32)0, (uint32)0, (uint32)0, 
                                     (uint32)1, (uint32)2, (uint32)3, (uint32)4};
    uint32 regVal = REG_READ32(RCM_CFG1_REG_ADDR);
    uint32 apbpsc = (regVal & RCM_CFG1_APB1PSC_MASK) >> RCM_CFG1_APB1PSC_SHIFT;

    hclk = Mcu_Rcm_ReadHCLKFreq();
    divider = APBPrescTable[apbpsc];
    
    pclk = hclk >> divider;

    return pclk;

}

/*!
* @brief       Read frequency of USART1 CLK
*
* @param       None
*
* @retval      Return frequency of USART1 CLK
*/
FUNC(uint32, MCU_CODE) Mcu_Rcm_ReadUSART1CLKFreq(void)
{
    uint32 usart1Clk = 0;
    uint32 usart1Sel = 0;
    
    uint32 regVal = REG_READ32(RCM_CFG3_REG_ADDR);
    usart1Sel = (regVal & RCM_CFG3_USART1SEL_MASK) >> RCM_CFG3_USART1SEL_SHIFT;

    if (usart1Sel == 0x00U)
    {
        usart1Clk = Mcu_Rcm_ReadPCLKFreq();
    }
    else if (usart1Sel == 0x01U)
    {
        usart1Clk = Mcu_Rcm_ReadSYSCLKFreq();
    }
    else if (usart1Sel == 0x02U)
    {
        usart1Clk = LSE_VALUE;
    }
    else
    {
        usart1Clk = HSI_VALUE;
    }

    return usart1Clk;
}



/*!
* @brief       Controls peripheral clock gating
* 
* @param       state     Clock state (ENABLE/DISABLE)
* @param       mode      Bus domain (AHB/APB1/APB2)
* @param       Periph    Peripheral bitmask identifier
* 
*/
FUNC(void, MCU_CODE) Mcu_Rcm_OperationPeriphClock(uint32 state,
                                                  Mcu_Rcm_PeriphSelType mode,
                                                  uint32 Periph)
{
    uint32 targetReg = 0;
    
    switch (mode)
    {
        case RCM_AHB_PERIPH:
            targetReg = RCM_AHBCLKEN_REG_ADDR;
            break;
        case RCM_APB1_PERIPH:
            targetReg = RCM_APBCLKEN1_REG_ADDR;
            break;
        case RCM_APB2_PERIPH:
            targetReg = RCM_APBCLKEN2_REG_ADDR;
            break;
        default:
            {
                /* nothing */
            }
            break;
    }

    if ((uint32)0 == targetReg)
    {
        /* nothing */
    }
    else if ((uint32)ENABLE == state)
    {
        REG_BIT_SET32(targetReg, (uint32)Periph);
    }
    else
    {
        REG_BIT_CLEAR32(targetReg, (uint32)Periph);
    }

}


/*!
* @brief       Controls peripheral reset lines
* 
* @param       state     Reset state (ENABLE=assert, DISABLE=deassert)
* @param       mode      Bus domain (AHB/APB1/APB2/RTC)
* @param       Periph    Peripheral reset bitmask
* 
*/
FUNC(void, MCU_CODE) Mcu_Rcm_OperationPeriphReset(uint32 state,
                                                  Mcu_Rcm_PeriphSelType mode,
                                                  Mcu_Rcm_ModeResetType Periph)
{
    uint32 targetReg = 0;
    
    switch (mode)
    {
        case RCM_AHB_PERIPH:
            targetReg = RCM_AHBRST_REG_ADDR;
            break;
        case RCM_APB1_PERIPH:
            targetReg = RCM_APBRST1_REG_ADDR;
            break;
        case RCM_APB2_PERIPH:
            targetReg = RCM_APBRST2_REG_ADDR;
            break;
        case RCM_RTC_PERIPH:
            targetReg = RCM_RTCCTRL_REG_ADDR;
            break;
        default:
            {
                /* nothing */
            }
            break;
    }
    
    if ((uint32)0 == targetReg)
    {
        /* nothing */
    }
    else if ((uint32)ENABLE == state)
    {
        REG_BIT_SET32(targetReg, (uint32)Periph);
    }
    else
    {
        REG_BIT_CLEAR32(targetReg, (uint32)Periph);
    }

}


/*!
* @brief       Enables specific clock system interrupts
* 
* @param       interrupt   Interrupt type identifier
* 
* @return      None
* 
*/
FUNC(void, MCU_CODE) Mcu_Rcm_EnableInterrupt(Mcu_Rcm_IrqType interrupt)
{
    switch (interrupt)
    {
        case RCM_FREQ_UNDER_ERR_IRQ:
            REG_BIT_SET32(RCM_INT2_REG_ADDR, RCM_INT2_FLLIE_MASK);
        break;

        case RCM_FREQ_OVER_ERR_IRQ:
            REG_BIT_SET32(RCM_INT2_REG_ADDR, RCM_INT2_FHHIE_MASK);
            break;

        case RCM_CLOCK_LOST_ERR_IRQ:
            REG_BIT_SET32(RCM_INT2_REG_ADDR, RCM_INT2_LOCIE_MASK);
            break;

        case RCM_LSI_READY_INT_IRQ:
            REG_BIT_SET32(RCM_INT1_REG_ADDR, RCM_INT1_LSIRDYEN_MASK);
            break;

        case RCM_HSI_READY_INT_IRQ:
            REG_BIT_SET32(RCM_INT1_REG_ADDR, RCM_INT1_HSIRDYEN_MASK);
            break;

        case RCM_HSE_READY_INT_IRQ:
            REG_BIT_SET32(RCM_INT1_REG_ADDR, RCM_INT1_HSERDYEN_MASK);
            break;

        case RCM_PLL_READY_INT_IRQ:
            REG_BIT_SET32(RCM_INT1_REG_ADDR, RCM_INT1_PLLRDYEN_MASK);
            break;

        case RCM_HSI14_READY_INT_IRQ:
            REG_BIT_SET32(RCM_INT1_REG_ADDR, RCM_INT1_HSI14RDYEN_MASK);
            break;

        default:
            /* no operation */
            break;
    }
}


/*!
* @brief       Disables specific clock system interrupts
* 
* @param       interrupt   Interrupt type identifier
* 
*/
FUNC(void, MCU_CODE) Mcu_Rcm_DisableInterrupt(Mcu_Rcm_IrqType interrupt)
{
    if (RCM_FREQ_UNDER_ERR_IRQ == interrupt)
    {
        REG_BIT_CLEAR32(RCM_INT2_REG_ADDR, RCM_INT2_FLLIE_MASK);
    }
    else if (RCM_FREQ_OVER_ERR_IRQ == interrupt)
    {
        REG_BIT_CLEAR32(RCM_INT2_REG_ADDR, RCM_INT2_FHHIE_MASK);
    }
    else if (RCM_CLOCK_LOST_ERR_IRQ == interrupt)
    {
        REG_BIT_CLEAR32(RCM_INT2_REG_ADDR, RCM_INT2_LOCIE_MASK);
    }
    else if (RCM_LSI_READY_INT_IRQ == interrupt)
    {
        REG_BIT_CLEAR32(RCM_INT1_REG_ADDR, RCM_INT1_LSIRDYEN_MASK);
    }
    else if (RCM_HSI_READY_INT_IRQ == interrupt)
    {
        REG_BIT_CLEAR32(RCM_INT1_REG_ADDR, RCM_INT1_HSIRDYEN_MASK);
    }
    else if (RCM_HSE_READY_INT_IRQ == interrupt)
    {
        REG_BIT_CLEAR32(RCM_INT1_REG_ADDR, RCM_INT1_HSERDYEN_MASK);
    }
    else if (RCM_PLL_READY_INT_IRQ == interrupt)
    {
        REG_BIT_CLEAR32(RCM_INT1_REG_ADDR, RCM_INT1_PLLRDYEN_MASK);
    }
    else if (RCM_HSI14_READY_INT_IRQ == interrupt)
    {
        REG_BIT_CLEAR32(RCM_INT1_REG_ADDR, RCM_INT1_HSI14RDYEN_MASK);
    }
    else
    {
        /* nothing */
    }
}

#if (MCU_CMU_MODE_ENABLE == STD_ON)
/*!
* @brief       
* 
* @param       
* 
* @return      None
* 
*/
FUNC(void, MCU_CODE) Mcu_Cmu_ConfigReferenceClock(Mcu_Cmu_ClkDivType cmuRefPre)
{
    REG_RMW32(RCM_CFG4_REG_ADDR,RCM_CFG4_REFPRE_MASK,
             ((uint32)cmuRefPre << RCM_CFG4_REFPRE_SHIFT));
}


/*!
* @brief       
* 
* @param       
* 
* @return      None
* 
*/
FUNC(void, MCU_CODE) Mcu_Cmu_MeasureClockSelect(Mcu_Cmu_ClockSelectType selectClock)
{
    if (selectClock == CMU_MEASURE_HSI)
    {
        REG_BIT_CLEAR32(RCM_CFG4_REG_ADDR, RCM_CFG4_CLK_SEL_MASK);
    }
    else
    {
        REG_BIT_SET32(RCM_CFG4_REG_ADDR, RCM_CFG4_CLK_SEL_MASK);
    }
}


/*!
* @brief       
* 
* @param       
* 
* @return      None
* 
*/
FUNC(void, MCU_CODE) Mcu_Cmu_LOCResetEnable(uint8 status)
{
    if (status == DISABLE)
    {
        REG_BIT_CLEAR32(RCM_INT2_REG_ADDR, RCM_INT2_SLOCEN_MASK);
    }
    else
    {
        REG_BIT_SET32(RCM_INT2_REG_ADDR, RCM_INT2_SLOCEN_MASK);
    }
}


/*!
* @brief       
* 
* @param       
* 
* @return      None
* 
*/
FUNC(void, MCU_CODE) Mcu_Cmu_ReferenceClockCounts(uint8 count)
{
    REG_RMW32(RCM_REF_CNT_REG_ADDR,RCM_REF_CNT_MASK,
                 ((uint32)count << RCM_REF_CNT_SHIFT));
}


/*!
* @brief       
* 
* @param       
* 
* @return      None
* 
*/
FUNC(void, MCU_CODE) Mcu_Cmu_Enable(uint8 status)
{
    if (status == DISABLE)
    {
        REG_BIT_CLEAR32(RCM_CFG4_REG_ADDR, RCM_CFG4_CMU_EN_MASK);
    }
    else
    {
        REG_BIT_SET32(RCM_CFG4_REG_ADDR, RCM_CFG4_CMU_EN_MASK);
    }
}


/*!
* @brief       
* 
* @param       
* 
* @return      None
* 
*/
FUNC(void, MCU_CODE) Mcu_Cmu_InterruptEnable(Mcu_Cmu_InterrputType intSelet,uint8 status)
{
    if (status == DISABLE)
    {
        REG_BIT_CLEAR32(RCM_INT2_REG_ADDR, intSelet);
    }
    else
    {
        REG_BIT_SET32(RCM_INT2_REG_ADDR, intSelet);
    }
}


/*!
* @brief       
* 
* @param       
* 
* @return      None
* 
*/
FUNC(uint16, MCU_CODE) Mcu_Cmu_GetMeasValue(void)
{
    return REG_READ32(RCM_MEAS_REG_ADDR);
}


#endif

/**@} end of group MCU_Fuctions */

#define MCU_STOP_SEC_CODE
#include "Mcu_MemMap.h"

#ifdef __cplusplus
}
#endif

/**@} end of group MCU_Driver */
/**@} end of group G32A10xx_StdPeriphDriver */
