/***************************************************************************//**
* \file cy_sysclk.c
* \version 1.10
*
* Provides an API implementation of the sysclk driver.
*
********************************************************************************
* \copyright
* Copyright 2016, Cypress Semiconductor Corporation. All rights reserved.
* You may use this file only in accordance with the license, terms, conditions,
* disclaimers, and limitations in the end user license agreement accompanying
* the software package with which this file was provided.
*******************************************************************************/


#include "cy_sysclk.h"
#include "syslib/cy_syslib.h"
#include <math.h>
#include <stdlib.h>

#if defined(__cplusplus)
extern "C" {
#endif /* __cplusplus */

/* # of elements in an array */
#define  CY_SYSCLK_N_ELMTS(a)  (sizeof(a) / sizeof((a)[0]))

/* ========================================================================== */
/* ===========================    ECO SECTION    ============================ */
/* ========================================================================== */
/**
* \addtogroup group_sysclk_eco_funcs
* \{
*/
/*******************************************************************************
* Function Name: Cy_SysClk_EcoConfigure
****************************************************************************//**
*
* This function configures the external crystal oscillator (ECO) trim bits based
* on crystal characteristics. This function should be called only when the ECO is
* disabled.
*
* \param freq Operating frequency of the crystal in Hz.
*
* \param cLoad Crystal load capacitance in pF.
*
* \param esr Effective series resistance of the crystal in ohms.
*
* \param driveLevel Crystal drive level in uW.
*
* \return Error / status code:<br>
* CY_SYSCLK_SUCCESS - ECO configuration completed successfully<br>
* CY_SYSCLK_BAD_PARAM - One or more invalid parameters<br>
* CY_SYSCLK_INVALID_STATE - ECO already enabled
*
* \note
* The following calculations are implemented, generally in floating point:<br>
*   freqMHz = freq / 1,000,000<br>
*   max amplitude Vpp = 1,000 * sqrt(drivelevel / 2 / esr) / 3.14 / freqMHz / cLoad<br>
*   gm_min mA/V = 5 * 4 * 3.14 * 3.14 * freqMhz^2 * cLoad^2 * 4 * esr / 1,000,000,000<br>
*   Number of amplifier sections = INT(gm_min / 4.5)<br>
*   As a result of the above calculations, max amplitude must be >= 0.5, and the
*   number of amplifier sections must be <= 3, otherwise this function returns with
*   a parameter error.<br>
*   atrim = if max amplitude < 0.5 then error<br>
*           else 2 * the following:<br>
*                    max amplitude < 0.6: 0<br>
*                    max amplitude < 0.7: 1<br>
*                    max amplitude < 0.8: 2<br>
*                    max amplitude < 0.9: 3<br>
*                    max amplitude < 1.15: 5<br>
*                    max amplitude < 1.275: 6<br>
*                    max amplitude >= 1.275: 7<br>
*   wdtrim = if max amplitude < 0.5 then error<br>
*            else 2 * the following:<br>
*                     max amplitude < 1.2: INT(5 * max amplitude) - 2<br>
*                     max amplitude >= 1.2: 3<br>
*   gtrim = if number of amplifier sections > 3 then error<br>
*           else the following:<br>
*                number of amplifier sections > 1: number of amplifier sections<br>
*                number of amplifier sections = 1: 0<br>
*                number of amplifier sections < 1: 1<br>
*   rtrim = if gtrim = error then error<br>
*           else the following:<br>
*                freqMHz > 26.8: 0<br>
*                freqMHz > 23.33: 1<br>
*                freqMHz > 16.5: 2<br>
*                freqMHz <= 16.5: 3<br>
*   ftrim = if atrim = error then error<br>
*           else INT(atrim / 2)<br>
*
*******************************************************************************/
cy_en_sysclk_status_t Cy_SysClk_EcoConfigure(uint32_t freq, uint32_t cLoad, uint32_t esr, uint32_t driveLevel)
{
    /* error if ECO is not disabled - any of the 3 enable bits are set */
    cy_en_sysclk_status_t rtnval = CY_SYSCLK_INVALID_STATE;
    if ((SRSS->CLK_ECO_CONFIG & 0xE0000000ul) == 0ul)
    {
        /* calculate intemediate values */
        float32_t freqMHz = (float32_t)freq / 1000000.0f;
        float32_t maxAmplitude =
            (1000.0f * ((float32_t)sqrt((float64_t)((float32_t)driveLevel / (2.0f * (float32_t)esr))))) /
            (3.14f * freqMHz * (float32_t)cLoad);
        float32_t gm_min =
            (788.8f /*5 * 4 * 3.14 * 3.14 * 4*/ * freqMHz * freqMHz * (float32_t)cLoad * (float32_t)cLoad) /
            1000000000.0f;
        uint32_t nAmpSections = (uint32_t)(gm_min / 4.5f);

        /* Error if input parameters cause erroneous intermediate values. */
        rtnval = CY_SYSCLK_BAD_PARAM;
        if ((maxAmplitude >= 0.5f) && (nAmpSections <= 3ul))
        {
            uint32_t atrim, wdtrim, gtrim, rtrim, ftrim, reg;

            atrim = 2ul * ((maxAmplitude < 0.6f) ? 0ul :
                           ((maxAmplitude < 0.7f) ? 1ul :
                            ((maxAmplitude < 0.8f) ? 2ul :
                             ((maxAmplitude < 0.9f) ? 3ul :
                              ((maxAmplitude < 1.15f) ? 5ul :
                               ((maxAmplitude < 1.275f) ? 6ul : 7ul))))));

            wdtrim = 2ul * ((maxAmplitude < 1.2f) ? (uint32_t)(5.0f * maxAmplitude) - 2ul : 3ul);

            gtrim = ((nAmpSections > 1ul) ? nAmpSections :
                     ((nAmpSections == 1ul) ? 0ul : 1ul));

            rtrim = ((freqMHz > 26.8f) ? 0ul :
                     ((freqMHz > 23.33f) ? 1ul :
                      ((freqMHz > 16.5f) ? 2ul : 3ul)));

            ftrim = atrim / 2ul;

            /* update all fields of trim control register with one write, without
               changing the ITRIM field in bits [21:16]:
                 gtrim:  bits [13:12]
                 rtrim:  bits [11:10]
                 ftrim:  bits  [9:8]
                 atrim:  bits  [7:4]
                 wdtrim: bits  [2:0]
            */
            reg = (SRSS->CLK_TRIM_ECO_CTL & ~0x3FFFul);
            reg |= (gtrim  & 3ul)    << 12;
            reg |= (rtrim  & 3ul)    << 10;
            reg |= (ftrim  & 3ul)    << 8;
            reg |= (atrim  & 0x0Ful) << 4;
            reg |= (wdtrim & 7ul);
            SRSS->CLK_TRIM_ECO_CTL = reg;

            rtnval = CY_SYSCLK_SUCCESS;
        } /* if valid parameters */
    } /* if ECO not enabled */

    return (rtnval);
}

/*******************************************************************************
* Function Name: Cy_SysClk_EcoEnable
****************************************************************************//**
*
* Enables the external crystal oscillator (ECO). This function should be called
* after \ref Cy_SysClk_EcoConfigure.
*
* \param timeoutus Amount of time in microseconds to wait for the ECO to lock.
* If a lock does not occur, the ECO is stopped. To avoid waiting for a lock, set
* this parameter to 0.
*
* \return Error / status code:<br>
* CY_SYSCLK_SUCCESS - ECO locked<br>
* CY_SYSCLK_TIMEOUT - ECO timed out and did not lock
* CY_SYSCLK_INVALID_STATE - ECO already enabled
*
*******************************************************************************/
cy_en_sysclk_status_t Cy_SysClk_EcoEnable(uint32_t timeoutus)
{
    cy_en_sysclk_status_t rtnval = CY_SYSCLK_INVALID_STATE;

    /* invalid state error if ECO is already enabled */
    if (_FLD2VAL(SRSS_CLK_ECO_CONFIG_ECO_EN, SRSS->CLK_ECO_CONFIG) == 0ul) /* 1 = enabled */
    {
        /* first set ECO enable */
        SRSS->CLK_ECO_CONFIG |= _VAL2FLD(SRSS_CLK_ECO_CONFIG_ECO_EN, 1ul); /* 1 = enable */

        /* now do the timeout wait for ECO_STATUS, bit ECO_OK */
        for (;
             ((_FLD2VAL(SRSS_CLK_ECO_STATUS_ECO_READY, SRSS->CLK_ECO_STATUS) == 0ul)) &&(timeoutus != 0ul);
             timeoutus--)
        {
            Cy_SysLib_DelayUs(1u);
        }
        rtnval = ((timeoutus == 0ul) ? CY_SYSCLK_TIMEOUT : CY_SYSCLK_SUCCESS);
    }
    return (rtnval);
}
/** \} group_sysclk_eco_funcs */


/* ========================================================================== */
/* ====================    INPUT MULTIPLEXER SECTION    ===================== */
/* ========================================================================== */
/**
* \addtogroup group_sysclk_path_src_funcs
* \{
*/
/*******************************************************************************
* Function Name: Cy_SysClk_ClkPathSetSource
****************************************************************************//**
*
* Configures the source for the specified clock path.
*
* \param clkPath Selects which clock path to configure; 0 is the first clock
* path, which is the FLL.
*
* \param source \ref cy_en_clkpath_in_sources_t
*
* \return \ref cy_en_sysclk_status_t
*
* \note
* If calling this function changes an FLL or PLL input frequency, disable the FLL
* or PLL before calling this function. After calling this function, call the FLL
* or PLL configure function, for example \ref Cy_SysClk_FllConfigure().
*******************************************************************************/
cy_en_sysclk_status_t Cy_SysClk_ClkPathSetSource(uint32_t clkPath, cy_en_clkpath_in_sources_t source)
{
    cy_en_sysclk_status_t retval = CY_SYSCLK_BAD_PARAM;
    if ((clkPath < SRSS_NUM_CLKPATH) &&
        ((source <= CY_SYSCLK_CLKPATH_IN_DSIMUX) ||
         ((CY_SYSCLK_CLKPATH_IN_DSI <= source) && (source <= CY_SYSCLK_CLKPATH_IN_PILO))))
    {
        if (source >= CY_SYSCLK_CLKPATH_IN_DSI)
        {
            SRSS->CLK_DSI_SELECT[clkPath] = _VAL2FLD(SRSS_CLK_DSI_SELECT_DSI_MUX, (uint32_t)source);
            SRSS->CLK_PATH_SELECT[clkPath] = _VAL2FLD(SRSS_CLK_PATH_SELECT_PATH_MUX, (uint32_t)CY_SYSCLK_CLKPATH_IN_DSIMUX);
        }
        else
        {
            SRSS->CLK_PATH_SELECT[clkPath] = _VAL2FLD(SRSS_CLK_PATH_SELECT_PATH_MUX, (uint32_t)source);
        }
        retval = CY_SYSCLK_SUCCESS;
    }
    return (retval);
}

/*******************************************************************************
* Function Name: Cy_SysClk_ClkPathGetSource
****************************************************************************//**
*
* Reports which source is selected for the path mux.
*
* \param clkPath Selects which clock path to report; 0 is the first clock path,
* which is the FLL.
*
* \return \ref cy_en_clkpath_in_sources_t
*
*******************************************************************************/
cy_en_clkpath_in_sources_t Cy_SysClk_ClkPathGetSource(uint32_t clkPath)
{
    CY_ASSERT_L1(clkPath < SRSS_NUM_CLKPATH);
    cy_en_clkpath_in_sources_t rtnval =
        (cy_en_clkpath_in_sources_t )_FLD2VAL(SRSS_CLK_PATH_SELECT_PATH_MUX, SRSS->CLK_PATH_SELECT[clkPath]);
    if (rtnval == CY_SYSCLK_CLKPATH_IN_DSIMUX)
    {
        rtnval = (cy_en_clkpath_in_sources_t)(CY_SYSCLK_CLKPATH_IN_DSI |
                    (_FLD2VAL(SRSS_CLK_DSI_SELECT_DSI_MUX, SRSS->CLK_DSI_SELECT[clkPath])));
    }
    return rtnval;
}
/** \} group_sysclk_path_src_funcs */


/* ========================================================================== */
/* ===========================    FLL SECTION    ============================ */
/* ========================================================================== */
/* min and max FLL output frequencies, in Hz */
#define  CY_SYSCLK_MIN_FLL_CCO_OUTPUT_FREQ  48000000ul
#define  CY_SYSCLK_MIN_FLL_OUTPUT_FREQ     (CY_SYSCLK_MIN_FLL_CCO_OUTPUT_FREQ / 2u)
#define  CY_SYSCLK_MAX_FLL_OUTPUT_FREQ     100000000ul

/**
* \addtogroup group_sysclk_fll_funcs
* \{
*/
/*******************************************************************************
* Function Name: Cy_SysClk_FllConfigure
****************************************************************************//**
*
* Configures the FLL, for best accuracy optimization.
*
* \param inputFreq frequency of input source, in Hz
*
* \param outputFreq Desired FLL output frequency, in Hz. Allowable range is
* 24 MHz to 100 MHz. In all cases, FLL_OUTPUT_DIV must be set; the output divide
* by 2 option is required.
*
* \param outputMode \ref cy_en_fll_pll_output_mode_t
* If output mode is bypass, then the output frequency equals the input source
* frequency regardless of the frequency parameter values.
*
* \return  Error / status code:<br>
* CY_SYSCLK_SUCCESS - FLL successfully configured<br>
* CY_SYSCLK_INVALID_STATE - FLL not configured because it is enabled<br>
* CY_SYSCLK_BAD_PARAM - desired output frequency is out of valid range
*
* \note
* Call this function after changing the FLL input frequency, for example if
* \ref Cy_SysClk_ClkPathSetSource() is called.
* \note
* Do not call this function when the FLL is enabled. If it is, then this function
* returns immediately with an error return value and no register updates.
*******************************************************************************/
cy_en_sysclk_status_t Cy_SysClk_FllConfigure(uint32_t inputFreq, uint32_t outputFreq, cy_en_fll_pll_output_mode_t outputMode)
{
    cy_en_sysclk_status_t returnStatus = CY_SYSCLK_SUCCESS;

    /* check for errors */
    if (_FLD2VAL(SRSS_CLK_FLL_CONFIG_FLL_ENABLE, SRSS->CLK_FLL_CONFIG) != 0u) /* 1 = enabled */
    {
        returnStatus = CY_SYSCLK_INVALID_STATE;
    }
    else if ((outputFreq < CY_SYSCLK_MIN_FLL_OUTPUT_FREQ) || (CY_SYSCLK_MAX_FLL_OUTPUT_FREQ < outputFreq)) /* invalid output frequency */
    {
        returnStatus = CY_SYSCLK_BAD_PARAM;
    }
    else if (((float32_t)outputFreq / (float32_t)inputFreq) < 2.2f) /* check output/input frequency ratio */
    {
        returnStatus = CY_SYSCLK_BAD_PARAM;
    }
    else
    { /* return status is OK */
    }

    /* no error */
    if (returnStatus == CY_SYSCLK_SUCCESS) /* no errors */
    {
        /* If output mode is bypass (input routed directly to output), then done.
           The output frequency equals the input frequency regardless of the
           frequency parameters. */
        if (outputMode != CY_SYSCLK_FLLPLL_OUTPUT_INPUT)
        {
            cy_stc_fll_manual_config_t config;
            uint32_t ccoFreq;
            bool wcoSource = ((Cy_SysClk_ClkPathGetSource(0ul/*FLL*/) == CY_SYSCLK_CLKPATH_IN_WCO) ? true : false);

            config.outputMode = outputMode;
            /* 1. Output division by 2 is always required. */
            config.enableOutputDiv = (bool)(1ul);
            /* 2. Compute the target CCO frequency from the target output frequency and output division. */
            ccoFreq = outputFreq * ((uint32_t)(config.enableOutputDiv) + 1ul);
            /* 3. Compute the CCO range value from the CCO frequency */
            config.ccoRange = ((ccoFreq >= 150339200ul) ? CY_SYSCLK_FLL_CCO_RANGE4 :
                               ((ccoFreq >= 113009380ul) ? CY_SYSCLK_FLL_CCO_RANGE3 :
                                ((ccoFreq >=  84948700ul) ? CY_SYSCLK_FLL_CCO_RANGE2 :
                                 ((ccoFreq >=  63855600ul) ? CY_SYSCLK_FLL_CCO_RANGE1 : CY_SYSCLK_FLL_CCO_RANGE0))));
            {
                /* constants indexed by ccoRange */
                const float32_t trimSteps[] = {0.0011034f, 0.001102f, 0.0011f, 0.0011f, 0.00117062f};
                const float32_t fMargin[] = {43600000.0f, 58100000.0f, 77200000.0f, 103000000.0f, 132000000.0f};

            /* 4. Compute the FLL reference divider value.
                  refDiv is a constant if the WCO is the FLL source, otherwise the formula is
                  refDiv = ROUNDUP((1 / (trimStep[ccoRange] / 2)) * (inputFreq / ccoFreq)) */
                config.refDiv = wcoSource ? 19u : ((uint16_t)ceilf((float32_t)inputFreq /
                                                      ((trimSteps[config.ccoRange] / 2.0f) * (float32_t)ccoFreq)));
            /* 5. Compute the FLL multiplier value.
                  Formula is fllMult = ccoFreq / (inputFreq / refDiv) */
                config.fllMult = CY_SYSCLK_DIV_ROUNDUP(ccoFreq, CY_SYSCLK_DIV_ROUND(inputFreq, config.refDiv));
            /* 6. Compute the lock tolerance.
                  Formula is lock tolerance = 1.5 * fllMult * (((1 + CCO accuracy) / (1 - source clock accuracy)) - 1)
                  We assume CCO accuracy is 0.25%.
                  We assume the source clock accuracy = 1%. This is the accuracy of the IMO.
                  Therefore the formula is lock tolerance = 1.5 * fllMult * 0.012626 = 0.018939 * fllMult */
                config.lockTolerance = (uint16_t)ceilf((float32_t)(config.fllMult) * 0.018939f);
            /* 7. Compute the CCO igain and pgain. */
                {
                    /* intermediate parameters */
                    float32_t kcco = (trimSteps[config.ccoRange] * fMargin[config.ccoRange]) / 1000.0f;
                    float32_t ki_p = (0.85f / (kcco * ((float32_t)(config.refDiv) / (float32_t)inputFreq))) / 1000.0f;

                    /* igain and pgain bitfield values correspond to: 1/256, 1/128, ..., 4, 8 */
                    const float32_t gains[] = {0.00390625f, 0.0078125f, 0.015625f, 0.03125f, 0.0625f, 0.125f, 0.25f,
                                               0.5f, 1.0f, 2.0f, 4.0f, 8.0f};

                    /* find the largest IGAIN value that is less than or equal to ki_p */
                    for(config.igain = CY_SYSCLK_N_ELMTS(gains) - 1ul;
                        (gains[config.igain] > ki_p) && (config.igain != 0ul); config.igain--){}
                    /* decrement igain if the WCO is the FLL source */
                    if (wcoSource && (config.igain > 0u))
                    {
                        config.igain--;
                    }
                    /* then find the largest PGAIN value that is less than or equal to ki_p - gains[igain] */
                    for(config.pgain = CY_SYSCLK_N_ELMTS(gains) - 1ul;
                        (gains[config.pgain] > (ki_p - gains[config.igain])) && (config.pgain != 0ul);
                        config.pgain--){}
                    /* decrement pgain if the WCO is the FLL source */
                    if (wcoSource && (config.pgain > 0u))
                    {
                        config.pgain--;
                    }
                }
            /* 8. Compute the CCO_FREQ bits in CLK_FLL_CONFIG4 register. */
                config.cco_Freq = (uint16_t)
                    (floor(log((float32_t)ccoFreq / fMargin[config.ccoRange]) /
                           log(1.0f + trimSteps[config.ccoRange])));
            }
            /* 9. Compute the settling count, using a 1-usec settling time.
                  Use a constant if the WCO is the FLL source. */
            {
                float32_t ttref   = (float32_t)config.refDiv / ((float32_t)inputFreq / 1000.0f);
                float32_t testval = 6000.0f / (float32_t)outputFreq;
                float32_t divval  = ceil((float32_t)inputFreq * 0.000001f);
                float32_t altval  = ceil((divval / ttref) + 1.0f);
                config.settlingCount = (uint16)(wcoSource ? 200u : 
                                                ((ttref > testval) ? divval :
                                                 ((divval > altval) ? divval : altval)));
            }
            /* configure FLL based on calculated values */
            returnStatus = Cy_SysClk_FllManualConfigure(&config);
        } /* if not bypass output mode */

        else
        { /* bypass mode */
            /* update CLK_FLL_CONFIG3 register with divide by 2 parameter */
            CY_SYSCLK_CLR_SET(SRSS->CLK_FLL_CONFIG3, SRSS_CLK_FLL_CONFIG3_BYPASS_SEL, (uint32_t)outputMode);
        }
    } /* if no error */

    return (returnStatus);
}

/*******************************************************************************
* Function Name: Cy_SysClk_FllManualConfigure
****************************************************************************//**
*
* Manually configures the FLL based on user inputs.
*
* \param config \ref cy_stc_fll_manual_config_t
*
* \return  Error / status code:<br>
* CY_SYSCLK_SUCCESS - FLL successfully configured<br>
* CY_SYSCLK_INVALID_STATE - FLL not configured because it is enabled
*
* \note
* Call this function after changing the FLL input frequency, for example if
* \ref Cy_SysClk_ClkPathSetSource() is called.
* \note
* Do not call this function when the FLL is enabled. If it is, then this function
* returns immediately with an error return value and no register updates.
*******************************************************************************/
cy_en_sysclk_status_t Cy_SysClk_FllManualConfigure(const cy_stc_fll_manual_config_t *config)
{
    cy_en_sysclk_status_t returnStatus = CY_SYSCLK_SUCCESS;

    CY_ASSERT_L1(config != NULL);
    /* check for errors */
    if (_FLD2VAL(SRSS_CLK_FLL_CONFIG_FLL_ENABLE, SRSS->CLK_FLL_CONFIG) != 0u) /* 1 = enabled */
    {
        returnStatus = CY_SYSCLK_INVALID_STATE;
    }
    else
    { /* return status is OK */
    }

    /* no error */
    if (returnStatus == CY_SYSCLK_SUCCESS) /* no errors */
    {
        /* update CLK_FLL_CONFIG register with 2 parameters; FLL_ENABLE is already 0 */
        /* asserts just check for bitfield overflow */
        CY_ASSERT_L1(config->fllMult <= (SRSS_CLK_FLL_CONFIG_FLL_MULT_Msk >> SRSS_CLK_FLL_CONFIG_FLL_MULT_Pos));
        uint32_t reg = _VAL2FLD(SRSS_CLK_FLL_CONFIG_FLL_MULT, config->fllMult);
        /* no assert check for enableOutputDiv, because it's a type boolean */
        SRSS->CLK_FLL_CONFIG = reg | _VAL2FLD(SRSS_CLK_FLL_CONFIG_FLL_OUTPUT_DIV, (uint32_t)(config->enableOutputDiv));

        /* update CLK_FLL_CONFIG2 register with 2 parameters */
        /* asserts just check for bitfield overflow */
        CY_ASSERT_L1(config->refDiv <= (SRSS_CLK_FLL_CONFIG2_FLL_REF_DIV_Msk >> SRSS_CLK_FLL_CONFIG2_FLL_REF_DIV_Pos));
        CY_ASSERT_L1(config->lockTolerance <= (SRSS_CLK_FLL_CONFIG2_LOCK_TOL_Msk >> SRSS_CLK_FLL_CONFIG2_LOCK_TOL_Pos));
        reg = _VAL2FLD(SRSS_CLK_FLL_CONFIG2_FLL_REF_DIV, config->refDiv);
        SRSS->CLK_FLL_CONFIG2 = reg | _VAL2FLD(SRSS_CLK_FLL_CONFIG2_LOCK_TOL, config->lockTolerance);

        /* update CLK_FLL_CONFIG3 register with 4 parameters */
        /* asserts just check for bitfield overflow */
        CY_ASSERT_L1(config->igain <= (SRSS_CLK_FLL_CONFIG3_FLL_LF_IGAIN_Msk >> SRSS_CLK_FLL_CONFIG3_FLL_LF_IGAIN_Pos));
        CY_ASSERT_L1(config->pgain <= (SRSS_CLK_FLL_CONFIG3_FLL_LF_PGAIN_Msk >> SRSS_CLK_FLL_CONFIG3_FLL_LF_PGAIN_Pos));
        CY_ASSERT_L1(config->settlingCount <= (SRSS_CLK_FLL_CONFIG3_SETTLING_COUNT_Msk >> SRSS_CLK_FLL_CONFIG3_SETTLING_COUNT_Pos));
        reg  = _VAL2FLD(SRSS_CLK_FLL_CONFIG3_FLL_LF_IGAIN, config->igain);
        reg |= _VAL2FLD(SRSS_CLK_FLL_CONFIG3_FLL_LF_PGAIN, config->pgain);
        reg |= _VAL2FLD(SRSS_CLK_FLL_CONFIG3_SETTLING_COUNT, config->settlingCount);
        SRSS->CLK_FLL_CONFIG3 = reg | _VAL2FLD(SRSS_CLK_FLL_CONFIG3_BYPASS_SEL, (uint32_t)(config->outputMode));

        /* update CLK_FLL_CONFIG4 register with 1 parameter; preserve other bits */
        /* asserts just check for bitfield overflow */
        CY_ASSERT_L1(config->ccoRange <= (SRSS_CLK_FLL_CONFIG4_CCO_RANGE_Msk >> SRSS_CLK_FLL_CONFIG4_CCO_RANGE_Pos));
        CY_ASSERT_L1(config->cco_Freq <= (SRSS_CLK_FLL_CONFIG4_CCO_FREQ_Msk >> SRSS_CLK_FLL_CONFIG4_CCO_FREQ_Pos));
        CY_SYSCLK_CLR_SET(SRSS->CLK_FLL_CONFIG4, SRSS_CLK_FLL_CONFIG4_CCO_RANGE, (uint32_t)(config->ccoRange));
        CY_SYSCLK_CLR_SET(SRSS->CLK_FLL_CONFIG4, SRSS_CLK_FLL_CONFIG4_CCO_FREQ, (uint32_t)(config->cco_Freq));
    } /* if no error */

    return (returnStatus);
}

/*******************************************************************************
* Function Name: Cy_SysClk_FllGetConfiguration
****************************************************************************//**
*
* Reports the FLL configuration settings.
*
* \param config \ref cy_stc_fll_manual_config_t
*
*******************************************************************************/
void Cy_SysClk_FllGetConfiguration(cy_stc_fll_manual_config_t *config)
{
    CY_ASSERT_L1(config != NULL);
    /* read 2 parameters from CLK_FLL_CONFIG register */
    uint32_t tempReg = SRSS->CLK_FLL_CONFIG;
    config->fllMult         = _FLD2VAL(SRSS_CLK_FLL_CONFIG_FLL_MULT, tempReg);
    config->enableOutputDiv = (bool)_FLD2VAL(SRSS_CLK_FLL_CONFIG_FLL_OUTPUT_DIV, tempReg);
    /* read 2 parameters from CLK_FLL_CONFIG2 register */
    tempReg = SRSS->CLK_FLL_CONFIG2;
    config->refDiv          = _FLD2VAL(SRSS_CLK_FLL_CONFIG2_FLL_REF_DIV, tempReg);
    config->lockTolerance   = _FLD2VAL(SRSS_CLK_FLL_CONFIG2_LOCK_TOL, tempReg);
    /* read 4 parameters from CLK_FLL_CONFIG3 register */
    tempReg = SRSS->CLK_FLL_CONFIG3;
    config->igain           = _FLD2VAL(SRSS_CLK_FLL_CONFIG3_FLL_LF_IGAIN, tempReg);
    config->pgain           = _FLD2VAL(SRSS_CLK_FLL_CONFIG3_FLL_LF_PGAIN, tempReg);
    config->settlingCount   = _FLD2VAL(SRSS_CLK_FLL_CONFIG3_SETTLING_COUNT, tempReg);
    config->outputMode      = (cy_en_fll_pll_output_mode_t)_FLD2VAL(SRSS_CLK_FLL_CONFIG3_BYPASS_SEL, tempReg);
    /* read 1 parameter from CLK_FLL_CONFIG4 register */
    config->ccoRange        = (cy_en_fll_cco_ranges_t)_FLD2VAL(SRSS_CLK_FLL_CONFIG4_CCO_RANGE, SRSS->CLK_FLL_CONFIG4);
}

/*******************************************************************************
* Function Name: Cy_SysClk_FllEnable
****************************************************************************//**
*
* Enables the FLL. The FLL should be configured before calling this function.
*
* \param timeoutus amount of time in micro seconds to wait for FLL to lock.
* If lock doesn't occur, FLL is stopped. To avoid waiting for lock set this to 0,
* and manually check for lock using \ref Cy_SysClk_FllLocked.
*
* \return Error / status code:<br>
* CY_SYSCLK_SUCCESS - FLL successfully enabled<br>
* CY_SYSCLK_TIMEOUT - Timeout waiting for FLL lock
*
* \note
* While waiting for the FLL to lock, the FLL bypass mode is set to \ref CY_SYSCLK_FLLPLL_OUTPUT_INPUT.
* After the FLL is locked, the FLL bypass mdoe is then set to \ref CY_SYSCLK_FLLPLL_OUTPUT_OUTPUT.
*******************************************************************************/
cy_en_sysclk_status_t Cy_SysClk_FllEnable(uint32_t timeoutus)
{
    cy_en_sysclk_status_t rtnval;
    bool nonZeroTimeout = (timeoutus != 0ul);

    /* first set the CCO enable bit */
    SRSS->CLK_FLL_CONFIG4 |= _VAL2FLD(SRSS_CLK_FLL_CONFIG4_CCO_ENABLE, 1ul); /* 1 = enable */

    /* Wait until CCO is ready */
    for (; (_FLD2VAL(SRSS_CLK_FLL_STATUS_CCO_READY, SRSS->CLK_FLL_STATUS) == 0ul) &&
           (timeoutus != 0ul);
         timeoutus--)
    {
        Cy_SysLib_DelayUs(1u);
    }

    /* Set the FLL bypass mode to 2 */
    CY_SYSCLK_CLR_SET(SRSS->CLK_FLL_CONFIG3, SRSS_CLK_FLL_CONFIG3_BYPASS_SEL, (uint32_t)CY_SYSCLK_FLLPLL_OUTPUT_INPUT);

    /* Set the FLL enable bit, if CCO is ready */
    if ((!nonZeroTimeout) || (nonZeroTimeout && (timeoutus != 0ul)))
    {
        SRSS->CLK_FLL_CONFIG |= _VAL2FLD(SRSS_CLK_FLL_CONFIG_FLL_ENABLE, 1ul); /* 1 = enable */
    }

    /* now do the timeout wait for FLL_STATUS, bit LOCKED */
    for (; (_FLD2VAL(SRSS_CLK_FLL_STATUS_LOCKED, SRSS->CLK_FLL_STATUS) == 0ul) &&
           (timeoutus != 0ul);
         timeoutus--)
    {
        Cy_SysLib_DelayUs(1u);
    }

    /* If lock doesn't occur, FLL is stopped. */
    if (nonZeroTimeout && (timeoutus == 0ul))
    {
        (void)Cy_SysClk_FllDisable();
    }
    else
    { /* Lock occurred; we need to clear the unlock occurred bit.
         Do so by writing a 1 to it. */
        SRSS->CLK_FLL_STATUS = _VAL2FLD(SRSS_CLK_FLL_STATUS_UNLOCK_OCCURRED, 1ul);
        /* Set the FLL bypass mode to 3 */
        CY_SYSCLK_CLR_SET(SRSS->CLK_FLL_CONFIG3, SRSS_CLK_FLL_CONFIG3_BYPASS_SEL,
                          (uint32_t)CY_SYSCLK_FLLPLL_OUTPUT_OUTPUT);
    }

    rtnval = ((timeoutus == 0ul) ? CY_SYSCLK_TIMEOUT : CY_SYSCLK_SUCCESS);
    return rtnval;
}
/** \} group_sysclk_fll_funcs */


/* ========================================================================== */
/* ===========================    PLL SECTION    ============================ */
/* ========================================================================== */
/** \cond INTERNAL */
/* PLL OUTPUT_DIV bitfield allowable range */
#define MIN_OUTPUT_DIV     2UL
#define MAX_OUTPUT_DIV    16UL

/* PLL REFERENCE_DIV bitfield allowable range */
#define MIN_REF_DIV        1UL
#define MAX_REF_DIV       18UL

/* PLL FEEDBACK_DIV bitfield allowable ranges, LF and normal modes */
#define MIN_FB_DIV_LF     19UL
#define MAX_FB_DIV_LF     56UL
#define MIN_FB_DIV_NORM   22UL
#define MAX_FB_DIV_NORM  112UL
/* PLL FEEDBACK_DIV bitfield allowable range selection */
#define MIN_FB_DIV ((config->lfMode) ? MIN_FB_DIV_LF : MIN_FB_DIV_NORM)
#define MAX_FB_DIV ((config->lfMode) ? MAX_FB_DIV_LF : MAX_FB_DIV_NORM)

/* PLL Fvco range allowable ranges, LF and normal modes */
#define MIN_FVCO_LF   170000000UL
#define MAX_FVCO_LF   200000000UL
#define MIN_FVCO_NORM 200000000UL
#define MAX_FVCO_NORM 400000000UL
/* PLL Fvco range selection */
#define MIN_FVCO ((config->lfMode) ? MIN_FVCO_LF : MIN_FVCO_NORM)
#define MAX_FVCO ((config->lfMode) ? MAX_FVCO_LF : MAX_FVCO_NORM)

/* PLL input and output frequency limits */
#define MIN_IN_FREQ    4000000UL
#define MAX_IN_FREQ   64000000UL
#define MIN_OUT_FREQ ((config->lfMode) ? (MIN_FVCO_LF / MAX_OUTPUT_DIV) : (MIN_FVCO_NORM / MAX_OUTPUT_DIV))
#define MAX_OUT_FREQ CY_HF_CLK_MAX_FREQ
/** \endcond */

/**
* \addtogroup group_sysclk_pll_funcs
* \{
*/
/*******************************************************************************
* Function Name: Cy_SysClk_PllConfigure
****************************************************************************//**
*
* Configures a given PLL.
* The configuration formula used is:
*   Fout = pll_clk * (P / Q / div_out), where:
*     Fout is the desired output frequency
*     pll_clk is the frequency of the input source
*     P is the feedback divider. Its value is in bitfield FEEDBACK_DIV.
*     Q is the reference divider. Its value is in bitfield REFERENCE_DIV.
*     div_out is the reference divider. Its value is in bitfield OUTPUT_DIV.
*
* \param clkPath Selects which PLL to configure. 1 is the first PLL; 0 is invalid.
*
* \param config \ref cy_stc_pll_config_t
*
* \return  Error / status code:<br>
* CY_SYSCLK_SUCCESS - PLL successfully configured<br>
* CY_SYSCLK_INVALID_STATE - PLL not configured because it is enabled<br>
* CY_SYSCLK_BAD_PARAM - invalid clock path number, or input or desired output frequency is out of valid range
*
* \note
* Call this function after changing the PLL input frequency, for example if
* \ref Cy_SysClk_ClkPathSetSource() is called.
* \note
* Do not call this function when the PLL is enabled. If it is, then this function
* returns immediately with an error return value and no register updates.
*******************************************************************************/
cy_en_sysclk_status_t Cy_SysClk_PllConfigure(uint32_t clkPath, const cy_stc_pll_config_t *config)
{
    cy_en_sysclk_status_t returnStatus = CY_SYSCLK_SUCCESS;

    /* check for error */
    if ((clkPath == 0ul) || (clkPath > SRSS_NUM_PLL)) /* invalid clock path number */
    {
        returnStatus = CY_SYSCLK_BAD_PARAM;
    }
    else if (_FLD2VAL(SRSS_CLK_PLL_CONFIG_ENABLE, SRSS->CLK_PLL_CONFIG[clkPath - 1ul]) != 0u) /* 1 = enabled */
    {
        returnStatus = CY_SYSCLK_INVALID_STATE;
    }
    /* invalid input frequency */
    else if (((config->inputFreq) < MIN_IN_FREQ) || (MAX_IN_FREQ < (config->inputFreq)))
    {
        returnStatus = CY_SYSCLK_BAD_PARAM;
    }
    /* invalid output frequency */
    else if (((config->outputFreq) < MIN_OUT_FREQ) || (MAX_OUT_FREQ < (config->outputFreq)))
    {
        returnStatus = CY_SYSCLK_BAD_PARAM;
    }
    else
    { /* returnStatus is OK */
    }

    /* no errors */
    if (returnStatus == CY_SYSCLK_SUCCESS)
    {
        cy_stc_pll_manual_config_t manualConfig;
        manualConfig.feedbackDiv = 0ul;
        manualConfig.referenceDiv = 0ul;
        manualConfig.outputDiv = 0ul;

        /* If output mode is bypass (input routed directly to output), then done.
           The output frequency equals the input frequency regardless of the
           frequency parameters. */
        if (config->outputMode != CY_SYSCLK_FLLPLL_OUTPUT_INPUT)
        {
            /* for each possible value of OUTPUT_DIV and REFERENCE_DIV (Q), try
               to find a value for FEEDBACK_DIV (P) that gives an output frequency
               as close as possible to the desired output frequency. */
            uint32_t p, q, out;
            uint32_t foutBest = 0ul; /* to ensure at least one pass through the for loops below */

            /* REFERENCE_DIV (Q) selection */
            for (q = MIN_REF_DIV; (q <= MAX_REF_DIV) && (foutBest != (config->outputFreq)); q++)
            {
                /* FEEDBACK_DIV (P) selection */
                for (p = MIN_FB_DIV; (p <= MAX_FB_DIV) && (foutBest != (config->outputFreq)); p++)
                {
                    /* Calculate the intermediate Fvco, and make sure that it's in range. */
                    uint32_t fvco = (uint32_t)(((uint64_t)(config->inputFreq) * (uint64_t)p) / (uint64_t)q);
                    if ((MIN_FVCO <= fvco) && (fvco <= MAX_FVCO))
                    {
                        /* OUTPUT_DIV selection */
                        for (out = MIN_OUTPUT_DIV; (out <= MAX_OUTPUT_DIV) && (foutBest != (config->outputFreq)); out++)
                        {
                            /* Calculate what output frequency will actually be produced. 
                               If it's closer to the target than what we have so far, then save it. */
                            uint32_t fout = ((p * config->inputFreq) / q) / out;
                            if ((uint32_t)abs((int32_t)fout - (int32_t)(config->outputFreq)) <
                                (uint32_t)abs((int32_t)foutBest - (int32_t)(config->outputFreq)))
                            {
                                foutBest = fout;
                                manualConfig.feedbackDiv  = p;
                                manualConfig.referenceDiv = q;
                                manualConfig.outputDiv    = out;
                            }
                        }
                    }
                }
            }
            /* exit loops if foutBest equals outputFreq */
        } /* if not bypass output mode */

        /* configure PLL based on calculated values */
        manualConfig.lfMode     = config->lfMode;
        manualConfig.outputMode = config->outputMode;
        returnStatus = Cy_SysClk_PllManualConfigure(clkPath, &manualConfig);

    } /* if no error */

    return (returnStatus);
}

/*******************************************************************************
* Function Name: Cy_SysClk_PllManualConfigure
****************************************************************************//**
*
* Manually configures a PLL based on user inputs.
*
* \param clkPath Selects which PLL to configure. 1 is the first PLL; 0 is invalid.
*
* \param config \ref cy_stc_pll_manual_config_t
*
* \return  Error / status code:<br>
* CY_SYSCLK_SUCCESS - PLL successfully configured<br>
* CY_SYSCLK_INVALID_STATE - PLL not configured because it is enabled<br>
* CY_SYSCLK_BAD_PARAM - invalid clock path number
*
* \note
* Call this function after changing the PLL input frequency, for example if
* \ref Cy_SysClk_ClkPathSetSource() is called.
* \note
* Do not call this function when the PLL is enabled. If it is, then this function
* returns immediately with an error return value and no register updates.
*******************************************************************************/
cy_en_sysclk_status_t Cy_SysClk_PllManualConfigure(uint32_t clkPath, const cy_stc_pll_manual_config_t *config)
{
    cy_en_sysclk_status_t returnStatus = CY_SYSCLK_SUCCESS;

    /* check for errors */
    if ((clkPath == 0ul) || (clkPath > SRSS_NUM_PLL)) /* invalid clock path number */
    {
        returnStatus = CY_SYSCLK_BAD_PARAM;
    }
    else if (_FLD2VAL(SRSS_CLK_PLL_CONFIG_ENABLE, SRSS->CLK_PLL_CONFIG[clkPath - 1ul]) != 0u) /* 1 = enabled */
    {
        returnStatus = CY_SYSCLK_INVALID_STATE;
    }
    /* valid divider bitfield values */
    else if ((config->outputDiv    < MIN_OUTPUT_DIV) || (MAX_OUTPUT_DIV < config->outputDiv)    ||
             (config->referenceDiv <    MIN_REF_DIV) || (MAX_REF_DIV    < config->referenceDiv) ||
             (config->feedbackDiv  < (config->lfMode ? MIN_FB_DIV_LF : MIN_FB_DIV))             ||
             ((config->lfMode ? MAX_FB_DIV_LF : MAX_FB_DIV) < config->feedbackDiv))
    {
         returnStatus = CY_SYSCLK_BAD_PARAM;
    }
    else
    { /* returnStatus is OK */
    }

    /* no errors */
    if (returnStatus == CY_SYSCLK_SUCCESS)
    {
        clkPath--; /* to correctly access PLL config registers structure */
        /* If output mode is bypass (input routed directly to output), then done.
           The output frequency equals the input frequency regardless of the frequency parameters. */
        if (config->outputMode != CY_SYSCLK_FLLPLL_OUTPUT_INPUT)
        {
            SRSS->CLK_PLL_CONFIG[clkPath] =
                _VAL2FLD(SRSS_CLK_PLL_CONFIG_FEEDBACK_DIV,  (uint32_t)(config->feedbackDiv))  |
                _VAL2FLD(SRSS_CLK_PLL_CONFIG_REFERENCE_DIV, (uint32_t)(config->referenceDiv)) |
                _VAL2FLD(SRSS_CLK_PLL_CONFIG_OUTPUT_DIV,    (uint32_t)(config->outputDiv))    |
                _VAL2FLD(SRSS_CLK_PLL_CONFIG_PLL_LF_MODE,   (uint32_t)(config->lfMode));
        }

        CY_SYSCLK_CLR_SET(SRSS->CLK_PLL_CONFIG[clkPath], SRSS_CLK_PLL_CONFIG_BYPASS_SEL, (uint32_t)config->outputMode);
    } /* if no error */

    return (returnStatus);
}

/*******************************************************************************
* Function Name: Cy_SysClk_PllGetConfiguration
****************************************************************************//**
*
* Reports configuration settings for a PLL.
*
* \param clkPath Selects which PLL to report. 1 is the first PLL; 0 is invalid.
*
* \param config \ref cy_stc_pll_manual_config_t
*
* \return  Error / status code:<br>
* CY_SYSCLK_SUCCESS - PLL data successfully reported<br>
* CY_SYSCLK_BAD_PARAM - invalid clock path number
*
*******************************************************************************/
cy_en_sysclk_status_t Cy_SysClk_PllGetConfiguration(uint32_t clkPath, cy_stc_pll_manual_config_t *config)
{
    cy_en_sysclk_status_t rtnval = CY_SYSCLK_BAD_PARAM;
    if ((clkPath != 0ul) && (clkPath <= SRSS_NUM_PLL))
    {
        uint32_t tempReg = SRSS->CLK_PLL_CONFIG[clkPath - 1ul];
        config->feedbackDiv  = (uint8_t)_FLD2VAL(SRSS_CLK_PLL_CONFIG_FEEDBACK_DIV, tempReg);
        config->referenceDiv = (uint8_t)_FLD2VAL(SRSS_CLK_PLL_CONFIG_REFERENCE_DIV, tempReg);
        config->outputDiv    = (uint8_t)_FLD2VAL(SRSS_CLK_PLL_CONFIG_OUTPUT_DIV, tempReg);
        config->lfMode       = (bool)_FLD2VAL(SRSS_CLK_PLL_CONFIG_OUTPUT_DIV, tempReg);
        config->outputMode   = (cy_en_fll_pll_output_mode_t)_FLD2VAL(SRSS_CLK_PLL_CONFIG_BYPASS_SEL, tempReg);
        rtnval = CY_SYSCLK_SUCCESS;
    }
    return (rtnval);
}

/*******************************************************************************
* Function Name: Cy_SysClk_PllEnable
****************************************************************************//**
*
* Enables the PLL. The PLL should be configured before calling this function.
*
* \param clkPath Selects which PLL to enable. 1 is the first PLL; 0 is invalid.
*
* \param timeoutus amount of time in microseconds to wait for the PLL to lock.
* If lock doesn't occur, PLL is stopped. To avoid waiting for lock set this to 0,
* and manually check for lock using \ref Cy_SysClk_PllLocked.
*
* \return Error / status code:<br>
* CY_SYSCLK_SUCCESS - PLL successfully enabled<br>
* CY_SYSCLK_TIMEOUT - Timeout waiting for PLL lock<br>
* CY_SYSCLK_BAD_PARAM - invalid clock path number
*
*******************************************************************************/
cy_en_sysclk_status_t Cy_SysClk_PllEnable(uint32_t clkPath, uint32_t timeoutus)
{
    cy_en_sysclk_status_t rtnval = CY_SYSCLK_BAD_PARAM;
    if ((clkPath != 0ul) && (clkPath <= SRSS_NUM_PLL))
    {
        clkPath--; /* to correctly access PLL config and status registers structures */
        /* first set the PLL enable bit */
        SRSS->CLK_PLL_CONFIG[clkPath] |= _VAL2FLD(SRSS_CLK_PLL_CONFIG_ENABLE, 1ul); /* 1 = enable */

        /* now do the timeout wait for PLL_STATUS, bit LOCKED */
        for (; (_FLD2VAL(SRSS_CLK_PLL_STATUS_LOCKED, SRSS->CLK_PLL_STATUS[clkPath]) == 0ul) &&
               (timeoutus != 0ul);
             timeoutus--)
        {
            Cy_SysLib_DelayUs(1u);
        }
        rtnval = ((timeoutus == 0ul) ? CY_SYSCLK_TIMEOUT : CY_SYSCLK_SUCCESS);
    }
    return (rtnval);
}
/** \} group_sysclk_pll_funcs */


/* ========================================================================== */
/* ====================    Clock Measurement section    ===================== */
/* ========================================================================== */

/* Cy_SysClk_StartClkMeasurementCounters() input parameter saved for use later in other functions. */
static uint32_t clk1Count1;

/* These variables act as locks to prevent collisions between clock measurement and entry into
   DeepSleep mode. See Cy_SysClk_DeepSleep(). */
static bool clkCounting = false;
static bool preventCounting = false;

/**
* \addtogroup group_sysclk_calclk_funcs
* \{
*/
/*******************************************************************************
* Function Name: Cy_SysClk_StartClkMeasurementCounters
****************************************************************************//**
*
* Assigns clocks to the clock measurement counters, and starts counting. The counters
* let you measure a clock frequency using another clock as a reference. There are two
* counters.<br>
* One counter (counter1), which is clocked by clock1, is loaded with an initial value
* and counts down to zero.<br>
* The second counter (counter2), which is clocked by clock2, counts up until the first
* counter reaches zero.<br>
* Either clock1 or clock2 can be a reference clock; the other clock becomes the
* measured clock. The reference clock frequency is always known.<br>
* After calling this function, call \ref Cy_SysClk_ClkMeasurementCountersDone()
* to determine when counting is done, that is, counter1 has counted down to zero.
* Then call \ref Cy_SysClk_ClkMeasurementCountersGetFreq() to calculate the frequency
* of the measured clock.
*
* \param clock1 The clock for counter1
*
* \param count1 The initial value for counter1, from which counter1 counts down to zero.
*
* \param clock2 The clock for counter2
*
* \return Error / status code:<br>
* CY_SYSCLK_INVALID_STATE if already doing a measurement<br>
* CY_SYSCLK_BAD_PARAM if invalid clock input parameter<br>
* else CY_SYSCLK_SUCCESS
*
* \note The counters are both 24-bit, so the maximum value of count1 is 0xFFFFFF.
* If clock2 frequency is greater than clock1, make sure that count1 is low enough
* that counter2 does not overflow before counter1 reaches zero.
* \note The time to complete a measurement is count1 / clock1 frequency.
* \note The clocks for both counters must have a nonzero frequency, or
* \ref Cy_SysClk_ClkMeasurementCountersGetFreq() incorrectly reports the result of the
* previous  measurement.
* \note Do not enter a device low power mode (Sleep, Deep Sleep) while doing a measurement;
* the measured clock frequency may not be accurate.
*
*******************************************************************************/
cy_en_sysclk_status_t Cy_SysClk_StartClkMeasurementCounters(cy_en_meas_clks_t clock1, uint32_t count1, cy_en_meas_clks_t clock2)
{
    cy_en_sysclk_status_t rtnval = CY_SYSCLK_INVALID_STATE;

    if ((!preventCounting) /* don't start a measurement if about to enter DeepSleep mode */  ||
        (_FLD2VAL(SRSS_CLK_CAL_CNT1_CAL_COUNTER_DONE, SRSS->CLK_CAL_CNT1) != 0ul/*1 = done*/))
    {
        /* Connect the indicated clocks to the respective counters.

           if clock1 is a slow clock,
             select it in SRSS_CLK_OUTPUT_SLOW.SLOW_SEL0, and SRSS_CLK_OUTPUT_FAST.FAST_SEL0 = SLOW_SEL0
           else if clock1 is a fast clock,
             select it in SRSS_CLK_OUTPUT_FAST.FAST_SEL0,
           else error, do nothing and return.

           if clock2 is a slow clock,
             select it in SRSS_CLK_OUTPUT_SLOW.SLOW_SEL1, and SRSS_CLK_OUTPUT_FAST.FAST_SEL1 = SLOW_SEL1
           else if clock2 is a fast clock,
             select it in SRSS_CLK_OUTPUT_FAST.FAST_SEL1,
           else error, do nothing and return.
        */
        rtnval = CY_SYSCLK_BAD_PARAM;
        if ((clock1 < CY_SYSCLK_MEAS_CLK_LAST_CLK) && (clock2 < CY_SYSCLK_MEAS_CLK_LAST_CLK) &&
            (count1 <= (SRSS_CLK_CAL_CNT1_CAL_COUNTER1_Msk >> SRSS_CLK_CAL_CNT1_CAL_COUNTER1_Pos)))
        {
            /* Disallow entry into DeepSleep mode while counting. */
            clkCounting = true;

            if (clock1 < CY_SYSCLK_MEAS_CLK_FAST_CLKS)
            { /* slow clock */
                SRSS->CLK_OUTPUT_SLOW = _CLR_SET_FLD32U(SRSS->CLK_OUTPUT_SLOW, SRSS_CLK_OUTPUT_SLOW_SLOW_SEL0, (uint32_t)clock1);
                SRSS->CLK_OUTPUT_FAST = _CLR_SET_FLD32U(SRSS->CLK_OUTPUT_FAST, SRSS_CLK_OUTPUT_FAST_FAST_SEL0, 7ul/*slow_sel0 output*/);
            }
            else
            { /* fast clock */
                if (clock1 < CY_SYSCLK_MEAS_CLK_PATH_CLKS)
                { /* ECO, EXT, ALTHF */
                    SRSS->CLK_OUTPUT_FAST = _CLR_SET_FLD32U(SRSS->CLK_OUTPUT_FAST, SRSS_CLK_OUTPUT_FAST_FAST_SEL0, (uint32_t)clock1);
                }
                else
                { /* PATH or CLKHF */
                    SRSS->CLK_OUTPUT_FAST = _CLR_SET_FLD32U(SRSS->CLK_OUTPUT_FAST, SRSS_CLK_OUTPUT_FAST_FAST_SEL0,
                                                            (((uint32_t)clock1 >> 8) & 0x0Ful) /*use enum bits [11:8]*/);
                    if (clock1 < CY_SYSCLK_MEAS_CLK_CLKHFS)
                    { /* PATH select */
                        SRSS->CLK_OUTPUT_FAST = _CLR_SET_FLD32U(SRSS->CLK_OUTPUT_FAST, SRSS_CLK_OUTPUT_FAST_PATH_SEL0,
                                                                ((uint32_t)clock1 & 0x0Ful) /*use enum bits [3:0]*/);
                    }
                    else
                    { /* CLKHF select */
                        SRSS->CLK_OUTPUT_FAST = _CLR_SET_FLD32U(SRSS->CLK_OUTPUT_FAST, SRSS_CLK_OUTPUT_FAST_HFCLK_SEL0,
                                                                ((uint32_t)clock1 & 0x0Ful) /*use enum bits [3:0]*/);
                    }
                }
            } /* clock1 fast clock */

            if (clock2 < CY_SYSCLK_MEAS_CLK_FAST_CLKS)
            { /* slow clock */
                SRSS->CLK_OUTPUT_SLOW = _CLR_SET_FLD32U(SRSS->CLK_OUTPUT_SLOW, SRSS_CLK_OUTPUT_SLOW_SLOW_SEL1, (uint32_t)clock2);
                SRSS->CLK_OUTPUT_FAST = _CLR_SET_FLD32U(SRSS->CLK_OUTPUT_FAST, SRSS_CLK_OUTPUT_FAST_FAST_SEL1, 7ul/*slow_sel1 output*/);
            }
            else
            { /* fast clock */
                if (clock2 < CY_SYSCLK_MEAS_CLK_PATH_CLKS)
                { /* ECO, EXT, ALTHF */
                    SRSS->CLK_OUTPUT_FAST = _CLR_SET_FLD32U(SRSS->CLK_OUTPUT_FAST, SRSS_CLK_OUTPUT_FAST_FAST_SEL1, (uint32_t)clock2);
                }
                else
                { /* PATH or CLKHF */
                    SRSS->CLK_OUTPUT_FAST = _CLR_SET_FLD32U(SRSS->CLK_OUTPUT_FAST, SRSS_CLK_OUTPUT_FAST_FAST_SEL1,
                                                            (((uint32_t)clock2 >> 8) & 0x0Ful) /*use enum bits [11:8]*/);
                    if (clock2 < CY_SYSCLK_MEAS_CLK_CLKHFS)
                    { /* PATH select */
                        SRSS->CLK_OUTPUT_FAST = _CLR_SET_FLD32U(SRSS->CLK_OUTPUT_FAST, SRSS_CLK_OUTPUT_FAST_PATH_SEL1,
                                                                ((uint32_t)clock2 & 0x0Ful) /*use enum bits [3:0]*/);
                    }
                    else
                    { /* CLKHF select */
                        SRSS->CLK_OUTPUT_FAST = _CLR_SET_FLD32U(SRSS->CLK_OUTPUT_FAST, SRSS_CLK_OUTPUT_FAST_HFCLK_SEL1,
                                                                ((uint32_t)clock2 & 0x0Ful) /*use enum bits [3:0]*/);
                    }
                }
            } /* clock2 fast clock */

            rtnval = CY_SYSCLK_SUCCESS;

            /* Save this input parameter for use later, in other functions.
               No error checking is done on this parameter.*/
            clk1Count1 = count1;

            /* Counting starts when counter1 is written with a nonzero value. */
            SRSS->CLK_CAL_CNT1 = clk1Count1;
        } /* if (clock1 < CY_SYSCLK_MEAS_CLK_LAST_CLK && clock2 < CY_SYSCLK_MEAS_CLK_LAST_CLK) */
    } /* if (not done) */
    return (rtnval);
}

/*******************************************************************************
* Function Name: Cy_SysClk_ClkMeasurementCountersGetFreq
****************************************************************************//**
*
* Calculates the frequency of the indicated measured clock (clock1 or clock2).<br>
* If clock1 is the measured clock, its frequency is:<br>
* clock1 frequency = (count1 / count2) * clock2 frequency<br>
* If clock2 is the measured clock, its frequency is:<br>
* clock2 frequency = (count2 / count1) * clock1 frequency<br>
* Call this function only after counting is done; see \ref Cy_SysClk_ClkMeasurementCountersDone().
*
* \param measuredClock False (0) if the measured clock is clock1, true (nonzero)
* if the measured clock is clock2.
*
* \param refClkFreq The reference clock frequency (clock1 or clock2).
*
* \return The frequency of the measured clock, in Hz.
*
*******************************************************************************/
uint32_t Cy_SysClk_ClkMeasurementCountersGetFreq(bool measuredClock, uint32_t refClkFreq)
{
    volatile uint64_t rtnval = (uint64_t)_FLD2VAL(SRSS_CLK_CAL_CNT2_CAL_COUNTER2, SRSS->CLK_CAL_CNT2);

    /* Done counting; allow entry into DeepSleep mode. */
    clkCounting = false;

    if (!measuredClock)
    { /* clock1 is the measured clock */
        if (rtnval != 0u) /* avoid divide by zero */
        {
            rtnval = CY_SYSCLK_DIV_ROUND((uint64_t)clk1Count1 * (uint64_t)refClkFreq, rtnval);
        }
    }
    else
    { /* clock2 is the measured clock */
        rtnval = CY_SYSCLK_DIV_ROUND(rtnval * (uint64_t)refClkFreq, (uint64_t)clk1Count1 );
    }
    return ((uint32_t)rtnval);
}
/** \} group_sysclk_calclk_funcs */


/* ========================================================================== */
/* ==========================    TRIM SECTION    ============================ */
/* ========================================================================== */
/**
* \addtogroup group_sysclk_trim_funcs
* \{
*/

/*******************************************************************************
* Function Name: Cy_SysClk_IloTrim
****************************************************************************//**
*
* Trims the ILO to be as close to 32,768 Hz as possible.
*
* \param iloFreq current ILO frequency. Call \ref Cy_SysClk_StartClkMeasurementCounters
* and other measurement functions to obtain the current frequency of the ILO.
*
* \return Change in trim value; 0 if done, that is, no change in trim value.
*
* \note The watchdog timer (WDT) must be unlocked before calling this function.
*******************************************************************************/
/** \cond INTERNAL */
/* target frequency */
#define CY_SYSCLK_ILO_TARGET_FREQ  32768u
/** \endcond */

int32_t Cy_SysClk_IloTrim(uint32_t iloFreq)
{
    /* Nominal trim step size is 1.5% of "the frequency". Using the target frequency. */
    const uint32_t trimStep = CY_SYSCLK_DIV_ROUND((uint32_t)CY_SYSCLK_ILO_TARGET_FREQ * 15ul, 1000ul);

    uint32_t newTrim = 0ul;
    uint32_t curTrim = 0ul;

    /* Do nothing if iloFreq is already within one trim step from the target */
    uint32_t diff = (uint32_t)abs((int32_t)iloFreq - (int32_t)CY_SYSCLK_ILO_TARGET_FREQ);
    if (diff >= trimStep)
    {
        curTrim = _FLD2VAL(SRSS_CLK_TRIM_ILO_CTL_ILO_FTRIM, SRSS->CLK_TRIM_ILO_CTL);
        if (iloFreq > CY_SYSCLK_ILO_TARGET_FREQ)
        { /* iloFreq is too high. Reduce the trim value */
            newTrim = curTrim - CY_SYSCLK_DIV_ROUND(iloFreq - CY_SYSCLK_ILO_TARGET_FREQ, trimStep);
        }
        else
        { /* iloFreq too low. Increase the trim value. */
            newTrim = curTrim + CY_SYSCLK_DIV_ROUND(CY_SYSCLK_ILO_TARGET_FREQ - iloFreq, trimStep);
        }

        /* Update the trim value */
        CY_SYSCLK_CLR_SET(SRSS->CLK_TRIM_ILO_CTL, SRSS_CLK_TRIM_ILO_CTL_ILO_FTRIM, newTrim);
    }
    return (int32_t)(curTrim - newTrim);
}

/*******************************************************************************
* Function Name: Cy_SysClk_PiloTrim
****************************************************************************//**
*
* Trims the PILO to be as close to 32,768 Hz as possible.
*
* \param piloFreq current PILO frequency. Call \ref Cy_SysClk_StartClkMeasurementCounters
* and other measurement functions to obtain the current frequency of the PILO.
*
* \return Change in trim value; 0 if done, that is, no change in trim value.
*
*******************************************************************************/
/** \cond INTERNAL */
/* target frequency */
#define CY_SYSCLK_PILO_TARGET_FREQ  32768ul
/* nominal trim step size */
#define CY_SYSCLK_PILO_TRIM_STEP        5ul
/** \endcond */

int32_t Cy_SysClk_PiloTrim(uint32_t piloFreq)
{
    uint32_t newTrim = 0ul;
    uint32_t curTrim = 0ul;

    /* Do nothing if piloFreq is already within one trim step from the target */
    uint32_t diff = (uint32_t)abs((int32_t)piloFreq - (int32_t)CY_SYSCLK_PILO_TARGET_FREQ);
    if (diff >= CY_SYSCLK_PILO_TRIM_STEP)
    {
        curTrim = Cy_SysClk_PiloGetTrim();
        if (piloFreq > CY_SYSCLK_PILO_TARGET_FREQ)
        { /* piloFreq too high. Decrease the trim value. */
            newTrim = curTrim - CY_SYSCLK_DIV_ROUND(piloFreq - CY_SYSCLK_PILO_TARGET_FREQ, CY_SYSCLK_PILO_TRIM_STEP);
            if ((int32_t)newTrim < 0) /* limit underflow */
            {
                newTrim = 0;
            }
        }
        else
        { /* piloFreq too low. Increase the trim value. */
            newTrim = curTrim + CY_SYSCLK_DIV_ROUND(CY_SYSCLK_PILO_TARGET_FREQ - piloFreq, CY_SYSCLK_PILO_TRIM_STEP);
            if (newTrim >= SRSS_CLK_PILO_CONFIG_PILO_FFREQ_Msk) /* limit overflow */
            {
                newTrim = SRSS_CLK_PILO_CONFIG_PILO_FFREQ_Msk;
            }
        }
        Cy_SysClk_PiloSetTrim(newTrim);
    }

    return (int32_t)(curTrim - newTrim);
}
/** \} group_sysclk_trim_funcs */


/* ========================================================================== */
/* ======================    POWER MANAGEMENT SECTION    ==================== */
/* ========================================================================== */
/**
* \addtogroup group_sysclk_pm_funcs
* \{
*/
/** \cond INTERNAL */
/* timeout count for use in function Cy_SysClk_DeepSleepCallback() is sufficiently large for ~1 second at 100 MHz */
#define TIMEOUTK 5000000ul
/** \endcond */

/*******************************************************************************
* Function Name: Cy_SysClk_DeepSleepCallback
****************************************************************************//**
*
* This is a callback function for System Power Management (SysPm), for entering
* DeepSleep mode. This function does the following tests:<br>
* FLL and PLL:<br>
* 1. If the FLL or a PLL have the ECO as a source (see \ref Cy_SysClk_ClkPathGetSource),
*    and is enabled, changes the source to IMO.<br>
* 2. On wakeup, timeout-waits for FLL and PLL to regain their frequency locks,
*    if enabled.<br>
* Frequency measurement: Prevents entry into DeepSleep mode if currently counting;
* see \ref Cy_SysClk_StartClkMeasurementCounters. Does not allow start of counting
* if already indicated that entry into DeepSleep mode is OK.<br>
*
* This function can be called during execution of \ref Cy_SysPm_DeepSleep.
* To do so, register this function as a callback before calling
* \ref Cy_SysPm_DeepSleep - specify \ref CY_SYSPM_DEEPSLEEP as the callback
* type and call \ref Cy_SysPm_RegisterCallback.
*
* \note If the FLL or PLL source is the ECO, this function must be registered, and
* it must be the last callback function that is registered. This is needed to allow the
* ECO to stabilize before reconnecting it to the FLL or PLL.
*
* \param callbackParams
* structure with the syspm callback parameters,
* see \ref cy_stc_syspm_callback_params_t.
*
* \return Error / status code; see \ref cy_en_syspm_status_t. Pass if not doing
* a clock measurement, otherwise Fail. Timeout if timeout waiting for FLL or a PLL
* to regain its frequency lock.
*
*******************************************************************************/
cy_en_syspm_status_t Cy_SysClk_DeepSleepCallback(cy_stc_syspm_callback_params_t *callbackParams)
{
    /* bitmapped paths and roots that may be affected by FLL or PLL being sourced by ECO */
    static uint16_t changedSourcePaths;

    cy_en_syspm_status_t rtnval = CY_SYSPM_SUCCESS;

    CY_ASSERT_L1(callbackParams != NULL);

    /* Entry into DeepSleep mode tests */
    if (callbackParams->mode == CY_SYSPM_CHECK_READY)
    {
        /* Don't allow entry into DeepSleep mode if currently measuring a frequency. */
        if (clkCounting)
        {
            rtnval = CY_SYSPM_FAIL;
        }
        else 
        { /* Indicating that we can go into DeepSleep. Before doing so ... */
            uint32_t fllpll; /* 0 = FLL, all other values = a PLL */

            /* initialize record of changed paths */
            changedSourcePaths = 0u;

            /* for FLL and each PLL, */
            for (fllpll = 0ul; fllpll < (SRSS_NUM_PLL + 1ul); fllpll++)
            {
                /* If FLL or PLL is enabled, */
                if (0ul != ((fllpll == 0ul) ? (_FLD2VAL(SRSS_CLK_FLL_CONFIG_FLL_ENABLE, SRSS->CLK_FLL_CONFIG)) : 
                                              (_FLD2VAL(SRSS_CLK_PLL_CONFIG_ENABLE, SRSS->CLK_PLL_CONFIG[fllpll]))))
                {
                    /* and the FLL or PLL has ECO as a source, */
                    if (Cy_SysClk_ClkPathGetSource(fllpll) == CY_SYSCLK_CLKPATH_IN_ECO)
                    {
                        /* Change this FLL or PLL source to IMO */
                        (void)Cy_SysClk_ClkPathSetSource(fllpll, CY_SYSCLK_CLKPATH_IN_IMO);
                        /* keep a record that this FLL or PLL's source was changed from ECO */
                        changedSourcePaths |= (uint16_t)(1u << fllpll);
                    }
                }
            }
            /* Set the FLL bypass mode to 2 */
            CY_SYSCLK_CLR_SET(SRSS->CLK_FLL_CONFIG3, SRSS_CLK_FLL_CONFIG3_BYPASS_SEL, (uint32_t)CY_SYSCLK_FLLPLL_OUTPUT_INPUT);
            /* Also, prevent starting a new clock measurement until after we've come back from DeepSleep. */
            preventCounting = true;
        }
    }

    /* After return from DeepSleep, for each FLL and PLL, if needed, restore the source to ECO.
       And block until the FLL or PLL has regained its frequency locks. */
    else if (callbackParams->mode == CY_SYSPM_AFTER_TRANSITION)
    {
        /* if any FLL/PLL was sourced by the ECO, timeout wait for the ECO to become fully stabilized again. */
        if (changedSourcePaths != 0u)
        {
            uint32_t timeout;
            /* Cy_SysClk_EcoGetStatus()return value 2ul = fully stabilized */
            for (timeout = TIMEOUTK; (timeout != 0ul) && (Cy_SysClk_EcoGetStatus() != 2ul); timeout--){}
            if (timeout != 0ul)
            {
                rtnval = CY_SYSPM_TIMEOUT;
            }
            else
            {
                /* for FLL and each PLL, */
                uint32_t fllpll; /* 0 = FLL, all other values = a PLL */
                for (fllpll = 0ul; fllpll < (SRSS_NUM_PLL + 1ul); fllpll++)
                {
                    /* If FLL or PLL is enabled, */
                    if (0ul !=
                        ((fllpll == 0ul) ? (_FLD2VAL(SRSS_CLK_FLL_CONFIG_FLL_ENABLE, SRSS->CLK_FLL_CONFIG)) : 
                                           (_FLD2VAL(SRSS_CLK_PLL_CONFIG_ENABLE, SRSS->CLK_PLL_CONFIG[fllpll]))))
                    {
                        /* check the record that this FLL or PLL's source was changed from ECO */
                        if ((changedSourcePaths & (uint16_t)(1u << fllpll)) != 0u)
                        {
                            /* Change this FLL or PLL source back to ECO */
                            (void)Cy_SysClk_ClkPathSetSource(fllpll, CY_SYSCLK_CLKPATH_IN_ECO);
                        }
                        { /* Timeout wait for FLL or PLL to regain lock. */
                            uint32_t timout;
                            for (timout = TIMEOUTK; timout != 0ul; timout--)
                            {
                                if (true == ((fllpll == 0ul) ? Cy_SysClk_FllLocked() : Cy_SysClk_PllLocked(fllpll)))
                                {
                                    break;
                                }
                            }
                            if (timout != 0ul)
                            {
                                rtnval = CY_SYSPM_TIMEOUT;
                            }
                        }
                    }
                }
            }
        }
        
        /* Set the FLL bypass mode to 3 */
        CY_SYSCLK_CLR_SET(SRSS->CLK_FLL_CONFIG3, SRSS_CLK_FLL_CONFIG3_BYPASS_SEL,
                          (uint32_t)CY_SYSCLK_FLLPLL_OUTPUT_OUTPUT);
        /* Coming back from DeepSleep; we can allow clock measurement. */
        preventCounting = false;
    }

    /* No other modes need be checked. */
    else
    {
    }

    return rtnval;
}
/** \} group_sysclk_pm_funcs */


/* ========================================================================== */
/* ===========================    WCO SECTION    ============================ */
/* ========================================================================== */
/**
* \addtogroup group_sysclk_wco_funcs
* \{
*/
#if (SRSS_WCOCSV_PRESENT != 0) || defined(CY_DOXYGEN)
/*******************************************************************************
* Function Name: Cy_SysClk_WcoConfigureCsv
****************************************************************************//**
*
* Configure the WCO clock supervisor (CSV).
*
* \param config \ref cy_stc_wco_csv_config_t
*
* \note
* If loss detection is enabled, writes to other register bits are ignored.
* Therefore loss detection is disabled before writing the config structure
* members to the CTL register. Note that one of the config structure members is
* an enable bit.
*******************************************************************************/
void Cy_SysClk_WcoConfigureCsv(const cy_stc_wco_csv_config_t *config)
{
    CY_ASSERT_L1(config != NULL);
    CY_ASSERT_L3(config->SupervisorClock <= CY_SYSCLK_WCO_CSV_SUPERVISOR_PILO);
    CY_ASSERT_L3(config->LossWindow      <= CY_SYSCLK_CSV_LOSS_512_CYCLES);
    CY_ASSERT_L3(config->LossAction      <= CY_SYSCLK_CSV_ERROR_FAULT_RESET);

    /* First clear all bits, including the enable bit; disable loss detection. */
    SRSS->CLK_CSV_WCO_CTL = 0ul;
    /* Then write the structure elements (which include an enable bit) to the register. */
    SRSS->CLK_CSV_WCO_CTL = _VAL2FLD(SRSS_CLK_CSV_WCO_CTL_CSV_MUX, (uint32_t)config->SupervisorClock)      |
                            _VAL2FLD(SRSS_CLK_CSV_WCO_CTL_CSV_LOSS_WINDOW, (uint32_t)(config->LossWindow)) |
                            _VAL2FLD(SRSS_CLK_CSV_WCO_CTL_CSV_LOSS_ACTION, (uint32_t)(config->LossAction)) |
                            _VAL2FLD(SRSS_CLK_CSV_WCO_CTL_CSV_LOSS_EN, config->enableLossDetection);
}
#endif /* (SRSS_WCOCSV_PRESENT != 0) || defined(CY_DOXYGEN) */
/** \} group_sysclk_wco_funcs */


/* ========================================================================== */
/* =========================    clkHf[n] SECTION    ========================= */
/* ========================================================================== */
/**
* \addtogroup group_sysclk_clk_hf_funcs
* \{
*/
#if (SRSS_MASK_HFCSV != 0)  || defined(CY_DOXYGEN)
/*******************************************************************************
* Function Name: Cy_SysClk_ClkHfConfigureCsv
****************************************************************************//**
*
* Configures the clkHf clock supervisor (CSV).
*
* \param clkHf selects which clkHf CSV to configure.
*
* \param config \ref cy_stc_clkhf_csv_config_t
*
* \return Error / status code: CY_SYSCLK_INVALID_STATE if clkHf CSV is not present
* in the device, else CY_SYSCLK_SUCCESS
*
* \note
* If loss detection is enabled, writes to other register bits are ignored.
* Therefore loss detection is disabled before writing the config structure
* members to the CTL register. Note that one of the config structure members is
* an enable bit.
*******************************************************************************/
cy_en_sysclk_status_t Cy_SysClk_ClkHfConfigureCsv(uint32_t clkHf, const cy_stc_clkhf_csv_config_t *config)
{
    CY_ASSERT_L1(clkHf < SRSS_NUM_HFROOT);
    CY_ASSERT_L1(config != NULL);
    CY_ASSERT_L3(config->SupervisorClock <= CY_SYSCLK_CLKHF_CSV_SUPERVISOR_ALTHF);
    CY_ASSERT_L3(config->FrequencyAction <= CY_SYSCLK_CSV_ERROR_FAULT_RESET);
    CY_ASSERT_L3(config->LossWindow      <= CY_SYSCLK_CSV_LOSS_512_CYCLES);
    CY_ASSERT_L3(config->LossAction      <= CY_SYSCLK_CSV_ERROR_FAULT_RESET);

    /* First update the limit bits; this can be done regardless of enable state. */
    SRSS->CLK_CSV[clkHf].HF_LIMIT = _VAL2FLD(CLK_CSV_HF_LIMIT_UPPER_LIMIT, config->FrequencyUpperLimit) |
                                    _VAL2FLD(CLK_CSV_HF_LIMIT_LOWER_LIMIT, config->FrequencyLowerLimit);
    /* Then clear all CTL register bits, including the enable bit; disable loss detection. */
    SRSS->CLK_CSV[clkHf].HF_CTL = 0ul;
    /* Finally, write the structure elements (which include an enable bit) to the CTL register. */
    SRSS->CLK_CSV[clkHf].HF_CTL = _VAL2FLD(CLK_CSV_HF_CTL_CSV_LOSS_EN, config->enableLossDetection)             |
                                  _VAL2FLD(CLK_CSV_HF_CTL_CSV_LOSS_ACTION, (uint32_t)(config->LossAction))      |
                                  _VAL2FLD(CLK_CSV_HF_CTL_CSV_FREQ_EN, config->enableFrequencyFaultDetection)   |
                                  _VAL2FLD(CLK_CSV_HF_CTL_CSV_FREQ_ACTION, (uint32_t)(config->FrequencyAction)) |
                                  _VAL2FLD(CLK_CSV_HF_CTL_CSV_LOSS_WINDOW, (uint32_t)(config->LossWindow))      |
                                  _VAL2FLD(CLK_CSV_HF_CTL_CSV_MUX, (uint32_t)(config->SupervisorClock))         |
                                  _VAL2FLD(CLK_CSV_HF_CTL_CSV_FREQ_WINDOW, config->SupervisingWindow);
    return CY_SYSCLK_SUCCESS; /* placeholder */
}
#endif /* (SRSS_MASK_HFCSV != 0)  || defined(CY_DOXYGEN) */
/** \} group_sysclk_clk_hf_funcs */


/* ========================================================================== */
/* =====================    clk_peripherals SECTION    ====================== */
/* ========================================================================== */
/**
* \addtogroup group_sysclk_clk_peripheral_funcs
* \{
*/
/*******************************************************************************
* Function Name: Cy_SysClk_PeriphGetFrequency
****************************************************************************//**
*
* Reports the frequency of the output of a given peripheral divider.
*
* \param dividerType specifies which type of divider to use; \ref cy_en_divider_types_t
*
* \param dividerNum specifies which divider of the selected type to configure
*
* \return The frequency, in Hz.
*
* \note
* The reported frequency may be zero, which indicates unknown. This happens if
* the source input is clk_ext, ECO, clk_althf, dsi_out, or clk_altlf.
*
*******************************************************************************/
uint32_t Cy_SysClk_PeriphGetFrequency(cy_en_divider_types_t dividerType, uint32_t dividerNum)
{
    uint32_t rtnval = 0ul; /* 0 = unknown frequency */

    CY_ASSERT_L1(((dividerType == CY_SYSCLK_DIV_8_BIT)    && (dividerNum < PERI_DIV_8_NR))    ||
                 ((dividerType == CY_SYSCLK_DIV_16_BIT)   && (dividerNum < PERI_DIV_16_NR))   ||
                 ((dividerType == CY_SYSCLK_DIV_16_5_BIT) && (dividerNum < PERI_DIV_16_5_NR)) ||
                 ((dividerType == CY_SYSCLK_DIV_24_5_BIT) && (dividerNum < PERI_DIV_24_5_NR)));

    /* FLL or PLL configuration parameters */
    union
    {
        cy_stc_fll_manual_config_t fll;
        struct
        {
            uint8_t feedbackDiv;
            uint8_t referenceDiv;
            uint8_t outputDiv;
        } pll;
    } fllpll = {0ul};

    /* variables holding intermediate clock sources and dividers */
    cy_en_fll_pll_output_mode_t mode = CY_SYSCLK_FLLPLL_OUTPUT_AUTO; /* FLL or PLL mode; n/a for direct */
    bool                     locked = 0;      /* FLL or PLL lock status; n/a for direct */
    cy_en_clkpath_in_sources_t  source;       /* source input for path (FLL, PLL, or direct) */
    uint32_t                 source_freq;     /* source clock frequency, in Hz */
    cy_en_clkhf_in_sources_t path;            /* source input for root 0 (clkHf[0]) */
    uint32_t                 path_freq = 0ul; /* path (FLL, PLL, or direct) frequency, in Hz */
    uint32_t                 root_div;        /* root prescaler (1/2/4/8) */
    uint32_t                 clkHf0_div;      /* clkHf[0] predivider to clk_peri */

    /* clk_peri divider to selected peripheral clock */
    struct
    {
        uint32_t integer;
        uint32_t frac;
    } clkdiv = {0ul, 0ul};

    /* Start by finding the source input for root 0 (clkHf[0]) */
    path = Cy_SysClk_ClkHfGetSource(0ul);

    if (path == CY_SYSCLK_CLKHF_IN_CLKPATH0) /* FLL? (always path 0) */
    {
        Cy_SysClk_FllGetConfiguration(&fllpll.fll);
        source = Cy_SysClk_ClkPathGetSource(0ul);
        mode = fllpll.fll.outputMode;
        locked = Cy_SysClk_FllLocked();
    }
    else if ((uint32_t)path <= (uint32_t)SRSS_NUM_PLL) /* PLL? (always path 1 - N)*/
    {
        cy_stc_pll_manual_config_t config;
        (void)Cy_SysClk_PllGetConfiguration((uint32_t)path, &config);
        fllpll.pll.feedbackDiv  = config.feedbackDiv;
        fllpll.pll.referenceDiv = config.referenceDiv;
        fllpll.pll.outputDiv    = config.outputDiv;
        mode = config.outputMode;
        source = Cy_SysClk_ClkPathGetSource((uint32_t)path);
        locked = Cy_SysClk_PllLocked((uint32_t)path);
    }
    else /* assume clk_path < SRSS_NUM_CLKPATH */
    { /* Direct select path. Use PLL function to get the source. */
        source = Cy_SysClk_ClkPathGetSource((uint32_t)path);
    }

    /* get the frequency of the source, i.e., the path mux input */
    switch(source)
    {
        case CY_SYSCLK_CLKPATH_IN_IMO: /* IMO frequency is fixed at 8 MHz */
            source_freq = 8000000ul; /*Hz*/
            break;
        case CY_SYSCLK_CLKPATH_IN_ILO: /* ILO and WCO frequencies are nominally 32.768 kHz */
        case CY_SYSCLK_CLKPATH_IN_WCO:
            source_freq = 32768ul; /*Hz*/
            break;
        default:
            /* don't know the frequency of clk_ext, ECO, clk_althf, dsi_out, or clk_altlf */
            source_freq = 0ul; /* unknown frequency */
            break;
    }
    if (source_freq != 0ul)
    {
        /* Calculate the path frequency */
        if (path == CY_SYSCLK_CLKHF_IN_CLKPATH0) /* FLL? (always path 0) */
        {
            path_freq = source_freq; /* for bypass mode */
            /* if not bypass mode, apply the dividers calculation */
            if ((mode == CY_SYSCLK_FLLPLL_OUTPUT_OUTPUT) || ((mode != CY_SYSCLK_FLLPLL_OUTPUT_INPUT) && (locked != 0ul)))
            {
                /* Ffll_out = Ffll_clk * FLL_MULT / FLL_REF_DIV / (FLL_OUTPUT_DIV + 1), where:
                 *  FLL_MULT, REFERENCE_DIV, and OUTPUT_DIV are FLL configuration register bitfields
                 * Check for possible divide by 0.
                 */
                if (fllpll.fll.refDiv != 0ul)
                {
                    path_freq = (uint32_t)(((uint64_t)path_freq * (uint64_t)fllpll.fll.fllMult) /
                                           (uint64_t)fllpll.fll.refDiv) /
                                ((uint32_t)(fllpll.fll.enableOutputDiv) + 1ul);
                }
                else
                {
                    path_freq = 0ul; /* error, one of the divisors is 0 */
                }
            }
        }
        else if ((uint32_t)path <= (uint32_t)SRSS_NUM_PLL) /* PLL? (always path 1 - N)*/
        {
            path_freq = source_freq; /* for bypass mode */
            /* if not bypass mode, apply the dividers calculation */
            if ((mode == CY_SYSCLK_FLLPLL_OUTPUT_OUTPUT) || ((mode != CY_SYSCLK_FLLPLL_OUTPUT_INPUT) && (locked != 0ul)))
            {
                /* Fpll_out = Fpll_clk * FEEDBACK_DIV / REFERENCE_DIV / OUTPUT_DIV, where:
                 *  FEEDBACK_DIV, REFERENCE_DIV, and OUTPUT_DIV are PLL configuration register bitfields
                 * Check for possible divide by 0.
                 */
                if ((fllpll.pll.referenceDiv != 0ul) && (fllpll.pll.outputDiv != 0ul))
                {
                    path_freq = (uint32_t)(((uint64_t)source_freq * (uint64_t)fllpll.pll.feedbackDiv) /
                                           (uint64_t)fllpll.pll.referenceDiv) /
                                (uint32_t)fllpll.pll.outputDiv;
                }
                else
                {
                    path_freq = 0ul; /* error, one of the divisors is 0 */
                }
            }
        }
        else /* assume clk_path < SRSS_NUM_CLKPATH */
        { /* direct select path */
            path_freq = source_freq;
        }

        /* get the prescaler value for root 0, or clkHf[0]: 1/2/4/8 */
        root_div = 1ul << (uint32_t)Cy_SysClk_ClkHfGetDivider(0ul);

        /* get the predivider value for clkHf[0] to clk_peri */
        clkHf0_div = (uint32_t)Cy_SysClk_ClkPeriGetDivider() + 1ul;

        /* get the divider value for clk_peri to the selected peripheral clock */
        switch(dividerType)
        {
            case CY_SYSCLK_DIV_8_BIT:
            case CY_SYSCLK_DIV_16_BIT:
                clkdiv.integer = (uint32_t)Cy_SysClk_PeriphGetDivider(dividerType, dividerNum);
                /* frac = 0 means it's an integer divider */
                break;
            case CY_SYSCLK_DIV_16_5_BIT:
            case CY_SYSCLK_DIV_24_5_BIT:
                (void)Cy_SysClk_PeriphGetFracDivider(dividerType, dividerNum, &clkdiv.integer, &clkdiv.frac);
                break;
            default:
                break;
        }
        /* Divide the path input frequency down, and return the result.
           Stepping through the following code shows the frequency at each stage.
        */
        rtnval = path_freq / root_div; /* clkHf[0] frequency */
        rtnval /= clkHf0_div; /* clk_peri frequency */
        /* For fractional dividers, the divider is (int + 1) + frac/32.
         * Use the fractional value to round the divider to the nearest integer.
         */
        rtnval /= (clkdiv.integer + 1ul + ((clkdiv.frac >= 16ul) ? 1ul : 0ul)); /* peripheral divider output frequency */
    }

    return rtnval;
}
/** \} group_sysclk_clk_peripheral_funcs */

#if defined(__cplusplus)
}
#endif /* __cplusplus */


/* [] END OF FILE */
