/******************************************************************************
 * $Revision: 423 $
 * $Date:: 2017-04-07 16:03:30 +0900#$
 *****************************************************************************/
/* __DISCLAIMER_START__                                                      */
/******************************************************************************
* (c)2017, Cypress Semiconductor Corporation
* or a subsidiary of Cypress Semiconductor Corporation. All rights
* reserved.
*
* This software, including source code, documentation and related
* materials ( "Software" ), is owned by Cypress Semiconductor
* Corporation or one of its subsidiaries ( "Cypress" ) and is protected by
* and subject to worldwide patent protection (United States and foreign),
* United States copyright laws and international treaty provisions.
* Therefore, you may use this Software only as provided in the license
* agreement accompanying the software package from which you
* obtained this Software ( "EULA" ).
*
* If no EULA applies, Cypress hereby grants you a personal, nonexclusive,
* non-transferable license to copy, modify, and compile the
* Software source code solely for use in connection with Cypress' s
* integrated circuit products. Any reproduction, modification, translation,
* compilation, or representation of this Software except as specified
* above is prohibited without the express written permission of Cypress.
*
* Disclaimer: THIS SOFTWARE IS PROVIDED AS-IS, WITH NO
* WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING,
* BUT NOT LIMITED TO, NONINFRINGEMENT, IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE. Cypress reserves the right to make
* changes to the Software without notice. Cypress does not assume any
* liability arising out of the application or use of the Software or any
* product or circuit described in the Software. Cypress does not
* authorize its products for use in any products where a malfunction or
* failure of the Cypress product may reasonably be expected to result in
* significant property damage, injury or death ( "High Risk Product" ). By
* including Cypress' s product in a High Risk Product, the manufacturer
* of such system or application assumes all risk of such use and in doing
* so agrees to indemnify Cypress against all liability.
******************************************************************************/
/* __DISCLAIMER_END__                                                        */
/*****************************************************************************/
/** \file sysctrl.c
 **
 ** System Controller functions
 **
 ** History:
 **   - 2014-06-04  0.01  HS  Initial version for Traveo
 *****************************************************************************/

/*****************************************************************************/
/* Include files                                                             */
/*****************************************************************************/
#include "common_include.h"

/*****************************************************************************/
/* Local pre-processor symbols/macros ('define')                             */
/*****************************************************************************/

/*****************************************************************************/
/* Global variable definitions (declared in header file with 'extern')       */
/*****************************************************************************/

/*****************************************************************************/
/* Local type definitions ('typedef')                                        */
/*****************************************************************************/

/*****************************************************************************/
/* Local prototyping                                                         */
/*****************************************************************************/
static uint32_t GetClockSourceFrequencyHz(en_sys_ctrl_clock_source_t enClockSourceId);

/*****************************************************************************/
/* Local variable definitions ('static')                                     */
/*****************************************************************************/

/*****************************************************************************/
/* Local function prototypes ('static')                                      */
/*****************************************************************************/

/*****************************************************************************/
/* Function implementation - global ('extern') and local ('static')          */
/*****************************************************************************/

/**
 *****************************************************************************
 ** \addtogroup SyscGroup
 *****************************************************************************/
/*! @{ */

/**
 *****************************************************************************
 ** \brief Get current frequency of a selected source clock signal.
 **
 ** The function will read the APPLIED profile to obtain the currently
 ** effective settings. The application must ensure that there is no state
 ** transition ongoing which might result in invalid values.
 **
 ** \param [in]  enSourceClockId  Clock signal identifier
 **
 ** \return Frequency of selected clock signal in Hertz, 0 if clock is off or unknown.
 *****************************************************************************/
uint32_t SysCtrl_GetSourceClockFrequencyHz(en_sys_ctrl_source_clock_t enSourceClockId)
{
    stc_sys_ctrl_pll_settings_t stcPllSettings;
    uint32_t u32RetVal = 0;

    switch (enSourceClockId)
    {
        case SysCtrlSourceClockSlowRcClock:
            /* Return frequency if oscillator is enabled */
            u32RetVal = (SYSC0_APPCKSRER_SCROSCEN == 1) ? MCU_FREQ_CLK_SRC_HZ : 0;
            break;
            
        case SysCtrlSourceClockRcClock:
            /* Return frequency if oscillator is enabled */
            u32RetVal = (SYSC0_APPCKSRER_CROSCEN == 1) ? MCU_FREQ_CLK_RC_HZ : 0;
            return MCU_FREQ_CLK_RC_HZ;

        case SysCtrlSourceClockSubClock:
            /* Return frequency if oscillator is enabled */
            u32RetVal = (SYSC0_APPCKSRER_SOSCEN == 1) ? MCU_FREQ_CLK_SUB_HZ : 0;
            break;

        case SysCtrlSourceClockMainClock:
            /* Return frequency if oscillator is enabled */
            u32RetVal = (SYSC0_APPCKSRER_MOSCEN == 1) ? MCU_FREQ_CLK_MAIN_HZ : 0;
            break;

#ifdef SYSC0_APPPLL0CNTR
        case SysCtrlSourceClockMainPll0Clock:
            /* Return frequency if oscillator is enabled */
            if (SYSC0_APPCKSRER_PLL0EN == 1)
            {
                stcPllSettings.enInputClk = (en_sys_ctrl_pll_input_clk_t)SYSC0_APPPLL0CNTR_PLL0ISEL;
                stcPllSettings.u8DivN = SYSC0_APPPLL0CNTR_PLL0DIVN;
                stcPllSettings.enDivL = (en_sys_ctrl_clock_prescaler_type_b_t) SYSC0_APPPLL0CNTR_PLL0DIVL;
                stcPllSettings.enDivM = (en_sys_ctrl_clock_prescaler_type_b_t) SYSC0_APPPLL0CNTR_PLL0DIVM;
                u32RetVal = SysCtrl_CalculatePllFrequency(&stcPllSettings, FALSE);
            }
            else
            {
                u32RetVal = 0;
            }
        break;
#endif
#ifdef SYSC0_APPSSCG0CNTR0
        case SysCtrlSourceClockSscgPll0Clock:
            /* Return frequency if oscillator is enabled */
            if (SYSC0_APPCKSRER_SSCG0EN == 1)
            {
                stcPllSettings.enInputClk = (en_sys_ctrl_pll_input_clk_t)SYSC0_APPSSCG0CNTR0_SSCG0ISEL;
                stcPllSettings.u8DivN = SYSC0_APPSSCG0CNTR0_SSCG0DIVN;
                stcPllSettings.enDivL = (en_sys_ctrl_clock_prescaler_type_b_t) SYSC0_APPSSCG0CNTR0_SSCG0DIVL;
                stcPllSettings.enDivM = (en_sys_ctrl_clock_prescaler_type_b_t) SYSC0_APPSSCG0CNTR0_SSCG0DIVM;
                u32RetVal = SysCtrl_CalculatePllFrequency(&stcPllSettings, FALSE);
            }
            else
            {
                u32RetVal = 0;
            }
            break;
#endif
        default:
            u32RetVal = 0;
        break;
    }

    return u32RetVal;
}


/**
 *****************************************************************************
 ** \brief Get current frequency of a selected distributed clock signal
 **        (i.e. derived from a source clock)
 **
 ** \param [in]  #enDistributedClockId Distributed clock signal identifier
 **
 ** \return Frequency of selected clock signal, 0 if clock is off,
 **         depending on external (unknown) clock or enDistributedClockId invalid
 *****************************************************************************/
uint32_t SysCtrl_GetDistributedClockFrequencyHz(en_sys_ctrl_distributed_clock_t enDistributedClockId)
{
    uint32_t u32SystemClockHz;
    uint32_t u32LCP0AClockHz;
    uint32_t u32RetVal = 0;     /* default value in case of error */
    uint32_t u32Pll0ClockHz;
    
    /* Determine system base clock frequency */
    u32SystemClockHz = GetClockSourceFrequencyHz((en_sys_ctrl_clock_source_t)SYSC1_APPCKSELR0_CD0CSL);
    u32Pll0ClockHz = SysCtrl_GetSourceClockFrequencyHz(SysCtrlSourceClockMainPll0Clock);

    /* Divide system base clock by current divider */
    u32SystemClockHz /= (SYSC1_APPCKDIVR0_SYSDIV + 1);

    /* Calculate requested distributed clock frequency */
    switch (enDistributedClockId)
    {
        case SysCtrlDistributedClockLCP0A:
            if (SYSC1_APPCKSELR0_LCP0ACSL== 0)
            {
                /* CD0 clock */
                u32LCP0AClockHz = u32SystemClockHz / (SYSC1_APPCKDIVR3_LCP0ADIV + 1);
            }
            else
            {
                /* PLL0 clock */
                u32LCP0AClockHz = u32Pll0ClockHz / (SYSC1_APPCKDIVR3_LCP0ADIV + 1);
            }

            u32RetVal = (SYSC1_APPCKER0_ENCLKLCP0A == 0) ? 0 : u32LCP0AClockHz;
            break;

        default:
            break;
    }

    return u32RetVal;
}


/**
 *****************************************************************************
 ** \brief Calculate either the PLL VCO or the effective PLL output frequency.
 **
 ** \param [in] pstcPllSettings         Pointer to PLL settings
 ** \param [in] bCalculateVcoFrequency if TRUE, return value is VCO frequency (without output divider DIVM)
 **                                    if FALSE, return value is PLL frequency as it will be used by the system
 **
 ** \return Frequency in Hertz (0 if DIVL and/or DIVM are zero)
 *****************************************************************************/
uint32_t SysCtrl_CalculatePllFrequency(const stc_sys_ctrl_pll_settings_t* pstcPllSettings,
                                       boolean_t bCalculateVcoFrequency)
{
    uint32_t u32EffDivN;
    uint32_t u32EffDivL;
    uint32_t u32EffDivM;
    uint32_t u32EffSourceFreq;

    u32EffDivN = pstcPllSettings->u8DivN;
    u32EffDivL = (pstcPllSettings->enDivL == 0) ? 1 : pstcPllSettings->enDivL * 2;
    u32EffDivM = (pstcPllSettings->enDivM == 0) ? 1 : pstcPllSettings->enDivM * 2;
    u32EffSourceFreq = (pstcPllSettings->enInputClk == SysCtrlPllInputClkMain) ? MCU_FREQ_CLK_MAIN_HZ : MCU_FREQ_CLK_RC_HZ;

    /* Prevent division by 0 */
    if ((u32EffDivL != 0) && (u32EffDivM != 0))
    {
        if (TRUE == bCalculateVcoFrequency)
        {
            /* Calculate VCO output frequency */
            return (u32EffSourceFreq * u32EffDivN) / u32EffDivL;
        }
        else
        {
            /* Calculate PLL output frequency */
            return (u32EffSourceFreq * u32EffDivN) / (u32EffDivL * u32EffDivM);
        }
    }
    else
    {
        return 0;
    }
}

/**
 *****************************************************************************
 ** \brief Get clock source frequency
 **
 ** \param [in] enClockSourceId  See #en_sys_ctrl_clock_source_t
 **
 ** \return Frequency of selected clock signal, 0 if clock is off
 *****************************************************************************/
static uint32_t GetClockSourceFrequencyHz(en_sys_ctrl_clock_source_t enClockSourceId)
{
    uint32_t u32SystemClockHz;
    
    switch (enClockSourceId)
    {
        case SysCtrlClockSourceRcClock:
            u32SystemClockHz = SysCtrl_GetSourceClockFrequencyHz(SysCtrlSourceClockRcClock);
            break;
        case SysCtrlClockSourceMainClock:
            u32SystemClockHz = SysCtrl_GetSourceClockFrequencyHz(SysCtrlSourceClockMainClock);
            break;
        case SysCtrlClockSourceMainPll0Clock:
            u32SystemClockHz = SysCtrl_GetSourceClockFrequencyHz(SysCtrlSourceClockMainPll0Clock);
            break;
        case SysCtrlClockSourceSscgPll0Clock:
            u32SystemClockHz = SysCtrl_GetSourceClockFrequencyHz(SysCtrlSourceClockSscgPll0Clock);
            break;
        default:
            u32SystemClockHz = 0;
            break;
    }

    return u32SystemClockHz;
}

/*****************************************************************************/
/* EOF (not truncated)                                                       */
/*****************************************************************************/
