/* ======================================================================
 *   Copyright (C) 2022 Texas Instruments Incorporated
 *
 *   All rights reserved. Property of Texas Instruments Incorporated.
 *   Restricted rights to use, duplicate or disclose this code are
 *   granted through contract.
 *
 *   The program may not be used without the written permission
 *   of Texas Instruments Incorporated or against the terms and conditions
 *   stipulated in the agreement under which this program has been
 *   supplied.
 * ==================================================================== */

/**
 *   \file  cpsw_cpts.c
 *
 *   \brief
 *          CPTS module implementation
 *
 */

/* ========================================================================== */
/*                             Include Files                                  */
/* ========================================================================== */

#include "Eth_Cfg.h"
#include "string.h"
#include "Std_Types.h"

/* There are static inline functions in hw_types.h file. Map them as well */
#define ETH_START_SEC_CODE
/* MISRAC_2012_R.20.1
  * "Reason - This is the format to use for specifying memory sections " */
#include "Eth_MemMap.h"
#include "hw_types.h"
#define ETH_STOP_SEC_CODE
/* MISRAC_2012_R.20.1
  * "Reason - This is the format to use for specifying memory sections " */
#include "Eth_MemMap.h"

#include "SchM_Eth.h"

#include "Eth_Priv.h"
#include "cpsw_priv.h"
#include "cpsw.h"
#include "cpsw_cpts.h"
#include "hw_cpsw_ss.h"
#include "hw_cpsw_port.h"

/* This module's header */
#include "hw_cpsw_cpts.h"

/*---------------------------------------------------------------------------*\
|                             Extern Declarations                             |
\*---------------------------------------------------------------------------*/


/*---------------------------------------------------------------------------*\
|                            Local Macros/Defines                             |
\*---------------------------------------------------------------------------*/

/*---------------------------------------------------------------------------*\
|                            Local Typedefs/Enums                             |
\*---------------------------------------------------------------------------*/


/*---------------------------------------------------------------------------*\
|                         Local Function Declarations                         |
\*---------------------------------------------------------------------------*/

static void Eth_cptsConfigPort(Eth_CptsStateObj *pCptsStateObj, uint32 portNum);
static inline void Eth_cptsEventQueueInit(Eth_CptsEventQueue *pCPTSEventQueue);
static Std_ReturnType Eth_cptsHwInit(Eth_CptsStateObj *pCptsStateObj);
static Std_ReturnType Eth_cptsHwDeInit(Eth_CptsStateObj *pCptsStateObj);
static void Eth_cptsClearAllCPTSEvents(Eth_CptsStateObj *pCptsStateObj);
static uint64 Eth_cptsGetTimestamp(const Eth_CptsStateObj *pCptsStateObj, 
                                                    uint32 lowerTimeStamp);
static uint64 Eth_cptsHwTimeCyclesRead (Eth_CptsStateObj *pCptsStateObj);
static void Eth_cptsTimestampPush(Eth_CptsStateObj *pCptsStateObj);
static void Eth_cptsGetEvent(Eth_CptsStateObj *pCptsStateObj,
                             Eth_CptsEvent **ppEvent);
static void Eth_cptsReleaseEvent(Eth_CptsStateObj *pCptsStateObj, 
                                 Eth_CptsEvent *pEvent);
static void Eth_cptsEventEnqueue(Eth_CptsEventQueue *pCPTSEventQueue, 
                                 Eth_CptsEvent *pCPTSEvent);
static void Eth_cptsClearSWQueueEvents(Eth_CptsStateObj *pCptsStateObj);
static void Eth_cptsClearHWFifoEvents(Eth_CptsStateObj *pCptsStateObj);
static void Eth_cptsDisableCPTSInterrupt(Eth_CptsStateObj *pCptsStateObj);
static void Eth_cptsEnableCPTSInterrupt(Eth_CptsStateObj *pCptsStateObj);
static void Eth_cptsCounterInit(Eth_CptsStateObj* pCptsStateObj);
static inline uint64 Eth_cptsCyc2ns(Eth_CptsTimeCounter *tc, uint64 cycles);
static Eth_CptsEvent *Eth_cptsEventDequeue(Eth_CptsEventQueue *pCPTSEventQueue);
static Eth_CptsEvent *Eth_cptsEventExtract(Eth_CptsEventQueue *pCPTSEventQueue,
                                            const Eth_CptsEvent *pEventTemplate);
static Std_ReturnType Eth_cptsLookupEvent(Eth_CptsStateObj *pCptsStateObj,
                                  const Eth_CptsEvent *pEventTemplate,
                                  Eth_CptsEvent **ppEvent);

static void Eth_cptsFindShiftMult(Eth_CptsStateObj *pCptsStateObj);
static void Eth_cptsClearStaleEvents(Eth_CptsStateObj *pCptsStateObj);


/*---------------------------------------------------------------------------*\
|                         Local Variable Declarations                         |
\*---------------------------------------------------------------------------*/

#define ETH_START_SEC_VAR_INIT_32
/* MISRAC_2012_R.20.1
  * "Reason - This is the format to use for specifying memory sections " */
#include "Eth_MemMap.h"
/*
 * Data to keep track of open references to the CPTS structure
 * This will be used in case of both ports used simultaneously.
 */
static uint32 gCptsRefCount = 0U;
#define ETH_STOP_SEC_VAR_INIT_32
/* MISRAC_2012_R.20.1
  * "Reason - This is the format to use for specifying memory sections " */
#include "Eth_MemMap.h"

#define ETH_START_SEC_VAR_NO_INIT_UNSPECIFIED
/* MISRAC_2012_R.20.1
  * "Reason - This is the format to use for specifying memory sections " */
#include "Eth_MemMap.h"

/* Max number of events we can buffer in software */
#define ETH_CPTS_EVENT_CNT (32U)
/* Memory to be used for the CPTS event SW queue */

    static Eth_CptsEvent gCPTSEvents[ETH_CPTS_EVENT_CNT];

/*---------------------------------------------------------------------------*\
|                         Global Variable Declarations                        |
\*---------------------------------------------------------------------------*/

#if (CPTS_DEBUG_ENABLED == STD_ON)
 Eth_CptsStats gCptsStats;
#endif

#define ETH_STOP_SEC_VAR_NO_INIT_UNSPECIFIED
/* MISRAC_2012_R.20.1
  * "Reason - This is the format to use for specifying memory sections " */
#include "Eth_MemMap.h"

#define ETH_START_SEC_CODE
/* MISRAC_2012_R.20.1
  * "Reason - This is the format to use for specifying memory sections " */
#include "Eth_MemMap.h"
/*---------------------------------------------------------------------------*\
|                          Global Function Definitions                        |
\*---------------------------------------------------------------------------*/

 Std_ReturnType Eth_cptsInit(
    uint32 baseAddr, Eth_CptsStateObj *pCptsStateObj,
                            const Eth_CptsConfigType *pCptsConfig, uint8 portIdx)
{
    Std_ReturnType retVal;

    /* Increment the reference count */
    gCptsRefCount++;

    if ((uint32)1U == gCptsRefCount)
    {
        if (((void *)NULL != (pCptsConfig)))
        {
            /* Zero init the Eth_CptsStateObj structure */
            memset((void *)pCptsStateObj, 0, (size_t) sizeof(Eth_CptsStateObj));

    #if (CPTS_DEBUG_ENABLED == STD_ON)
            memset(&gCptsStats, 0, sizeof(Eth_CptsStats));
    #endif

            pCptsStateObj->portNum = portIdx;

            /* Process the packet */
            (void) memcpy(&pCptsStateObj->cptsCfg, pCptsConfig, 
			                        sizeof (Eth_CptsConfigType));

            /* Initialize the free event queue */
            Eth_cptsEventQueueInit(&pCptsStateObj->freeQueue);

            /* Initialize the active event queue */
            Eth_cptsEventQueueInit(&pCptsStateObj->activeQueue);

            pCptsStateObj->cpswBaseAddr = baseAddr;

            /* Set initial values for state fields */
            pCptsStateObj->upperTimeStamp              = 0U;
            pCptsStateObj->checkForMisalignment        = 0U;
            pCptsStateObj->tsPushInFIFO                = 0U;

            /* Save the input frequency value */
            pCptsStateObj->cptsInputFreq          = pCptsConfig->cptsInputFreq;

            /* Enable the CPTS Module */
            HW_WR_FIELD32( baseAddr + CPSW_CPTS_CONTROL_REG, 
			                              CPSW_CPTS_CONTROL_REG_CPTS_EN,
                                          CPSW_CPTS_CONTROL_REG_CPTS_EN_MAX);
										  										  
			HW_WR_FIELD32( baseAddr + CPSW_CPTS_CONTROL_REG, 
			                              CPSW_CPTS_CONTROL_REG_TSTAMP_EN,
                                          CPSW_CPTS_CONTROL_REG_TSTAMP_EN_MAX);
										  
			HW_WR_FIELD32( baseAddr + CPSW_CPTS_CONTROL_REG, 
			                              CPSW_CPTS_CONTROL_REG_MODE,
                                          CPSW_CPTS_CONTROL_REG_MODE_MAX);
										  
            /* Enable the CPTS interrupt by setting the enable bit */
            Eth_cptsEnableCPTSInterrupt(pCptsStateObj);

            /* Enable the EVNT misc interrupt*/
            CPSWMiscIntrEnable(baseAddr,
                         (uint32) CPSW_SS_MISC_EN_EVT_PEND);

            /* Init CPTS HW module with user provided configuration */
            (void)Eth_cptsHwInit(pCptsStateObj);

            Eth_cptsCounterInit(pCptsStateObj);

            retVal = (Std_ReturnType) E_OK;
        }
        else
        {
            retVal = (Std_ReturnType) E_NOT_OK;
        }
    }
    else
    {
        /*
         * If reference count at entry was non-zero, CPDMA was already opened,
         * so return error.
         */
        retVal = (Std_ReturnType) E_NOT_OK;
    }

    return retVal;
}

void Eth_cptsDeInit(Eth_CptsStateObj * pCptsStateObj)
{
    /* Enable the EVNT misc interrupt in the wrapper module */
    CPSWMiscIntrDisable(pCptsStateObj->cpswBaseAddr,
                 (uint32) CPSW_SS_MISC_EN_EVT_PEND);

    /* Disable the CPTS Module */
    HW_WR_FIELD32( pCptsStateObj->cpswBaseAddr + CPSW_CPTS_CONTROL_REG,
                                            CPSW_CPTS_CONTROL_REG_CPTS_EN, 0U);
    memset(pCptsStateObj, 0 , sizeof(Eth_CptsStateObj));
    /* Stop counter and time sync events */
    (void)Eth_cptsHwDeInit(pCptsStateObj);

    gCptsRefCount--;

    return;
}

void Eth_cptsHandleEvents(Eth_CptsStateObj* pCptsStateObj)
{
    Eth_CptsEvent event;

    uint32 lowerTimeStamp = 0U;
    uint32 eventFields = 0U;
#if (CPTS_DEBUG_ENABLED == STD_ON)
    gCptsStats.eventHandleCnt++;
#endif

    /* Clear out any active events in the HW FIFO */

    /* Upon interrupt, read the CPTS_EVENT_LOW and CPTS_EVENT_HIGH registers 
	   values. */
   eventFields    = HW_RD_REG32( pCptsStateObj->cpswBaseAddr + 
                                 CPSW_CPTS_EVENT_1_REG);
   
   lowerTimeStamp = HW_RD_REG32( pCptsStateObj->cpswBaseAddr + 
                                 CPSW_CPTS_EVENT_0_REG);

    /*
     * Set the EVENT_POP field (bit 0) of the CPTS_EVENT_POP register to pop the
     * previously read value off of the event FIFO.
     */
    HW_WR_FIELD32( pCptsStateObj->cpswBaseAddr + CPSW_CPTS_EVENT_POP_REG, 
	                                   CPSW_CPTS_EVENT_POP_REG_EVENT_POP, 1U);

    event.eventType     =  (Eth_CptsEventType) HW_GET_FIELD( eventFields, 
	                                     CPSW_CPTS_EVENT_1_REG_EVENT_TYPE);

    event.messageType   =  (Eth_CptsMessageType) HW_GET_FIELD( eventFields, 
	                                     CPSW_CPTS_EVENT_1_REG_MESSAGE_TYPE);
    event.sequenceId    =  (uint16) HW_GET_FIELD( eventFields, 
	                                      CPSW_CPTS_EVENT_1_REG_SEQUENCE_ID);
    event.portNumber    =  (uint16) HW_GET_FIELD( eventFields, 
	                                      CPSW_CPTS_EVENT_1_REG_PORT_NUMBER);

    if (event.eventType == CPTS_EVENT_TIME_STAMP_ROLLOVER)
    {
       /* Lower 32-bits in hardware rolled over, increment upper 32-bits by 1 */
        pCptsStateObj->upperTimeStamp += 1U;

        /* Mark that misalignement checking is now required */
        pCptsStateObj->checkForMisalignment = 1U;

    #if (CPTS_DEBUG_ENABLED == STD_ON)
        gCptsStats.tsRolloverEventCount++;
    #endif
    }
    else if (event.eventType == CPTS_EVENT_TIME_STAMP_HALF_ROLLOVER)
    {
        /* Mark that misalignment checking is not required */
        pCptsStateObj->checkForMisalignment = 0U;

    #if (CPTS_DEBUG_ENABLED == STD_ON)
        gCptsStats.tsHalfRolloverEventCount++;
    #endif
    }
    else
    {
        /* Get a free Event from the freeEvent queue */
        Eth_CptsEvent *pEvent = Eth_cptsEventDequeue(&pCptsStateObj->freeQueue);

        /* Check to make sure an event was available - if not, this current 
		  event gets dropped */
        if (NULL != pEvent)
        {
            /* Get the event information */
            pEvent->eventType   = event.eventType;
            pEvent->messageType = event.messageType;
            pEvent->sequenceId  = event.sequenceId;
            pEvent->portNumber  = event.portNumber;

            switch(pEvent->eventType)
            {
                case CPTS_EVENT_TIME_STAMP_PUSH:
                {
              /* Clear the flag that indicates a TS_PUSH event is in the FIFO */
                    pCptsStateObj->tsPushInFIFO = 0U;

                    /* These fields are meaningless for this event type */
                    pEvent->portNumber  = 0U;
                    pEvent->sequenceId  = 0U;
                    pEvent->messageType = CPTS_MESSAGE_INVALID;
    #if (CPTS_DEBUG_ENABLED == STD_ON)
                    gCptsStats.tsSwPushEventCount++;
    #endif
                    break;
                }

                case CPTS_EVENT_HARDWARE_TIME_STAMP_PUSH:
                {
                    /* Get the port number which for HW PUSH events gives the
      					HW_PUSH number (1-4) */
                    event.portNumber    =  (uint16) HW_GET_FIELD( eventFields, 
					                       CPSW_CPTS_EVENT_1_REG_PORT_NUMBER);

                    /* These fields are meaningless for this event type */
                    pEvent->sequenceId  = 0U;
                    pEvent->messageType = CPTS_MESSAGE_INVALID;
    #if (CPTS_DEBUG_ENABLED == STD_ON)
                    gCptsStats.tsHwPushEventCount[pEvent->portNumber]++;
    #endif
                    break;
                }

                case CPTS_EVENT_ETHERNET_RECEIVE:
                {
                    /* For Ethernet events, get the messageType, sequence ID, 
					    and port number */
                pEvent->messageType   =  (Eth_CptsMessageType) HW_GET_FIELD( 
					           eventFields, CPSW_CPTS_EVENT_1_REG_MESSAGE_TYPE);
							   
                pEvent->sequenceId  =  (uint16) HW_GET_FIELD( eventFields, 
				                       CPSW_CPTS_EVENT_1_REG_SEQUENCE_ID);
								   
                pEvent->portNumber  =  (uint16) HW_GET_FIELD( eventFields, 
				                         CPSW_CPTS_EVENT_1_REG_PORT_NUMBER);


    #if (CPTS_DEBUG_ENABLED == STD_ON)
                    gCptsStats.tsEthernetRxEventCount++;
    #endif
                    break;
                }

                case CPTS_EVENT_ETHERNET_TRANSMIT:
                {
                    /* For Ethernet events, get the messageType, sequence ID, 
					    and port number */
                pEvent->messageType   = (Eth_CptsMessageType) 
					                    HW_GET_FIELD( eventFields, 
										CPSW_CPTS_EVENT_1_REG_MESSAGE_TYPE);
										
                pEvent->sequenceId    =  (uint16) HW_GET_FIELD( eventFields, 
				                         CPSW_CPTS_EVENT_1_REG_SEQUENCE_ID);
                pEvent->portNumber    =  (uint16) HW_GET_FIELD( eventFields, 
				                         CPSW_CPTS_EVENT_1_REG_PORT_NUMBER);
    #if (CPTS_DEBUG_ENABLED == STD_ON)
                    gCptsStats.tsEthernetTxEventCount++;
    #endif
                    break;
                }

                default:
                {
                    /* Release the event since it isn't needed by anyone */
                    Eth_cptsReleaseEvent(pCptsStateObj, pEvent);
                    pEvent = NULL;

                    break;
                }
            }

            if (NULL != pEvent)
            {
                /* Calculate the full 64-bit timestamp */
                pEvent->timeStamp = Eth_cptsGetTimestamp(pCptsStateObj, 
				                                       lowerTimeStamp);
                /*
                 * The event should be handled by the registered stack, so
                 * put it into the active event queue
                 */
                Eth_cptsEventEnqueue(&pCptsStateObj->activeQueue, pEvent);
            }
        }
#if (CPTS_DEBUG_ENABLED == STD_ON)
        else
        {
            gCptsStats.unhandledEventCount++;
        }
#endif
    }
#if (CPTS_DEBUG_ENABLED == STD_ON)
    gCptsStats.eventProcessed[gCptsStats.eventHandleCnt%255U]++;
#endif

}

 Std_ReturnType Eth_cptsCounterReadEthEvent(
   Eth_CptsStateObj *pCptsStateObj, uint64 *nsec,Eth_CptsEvent *pEeventTemplate)
{
    Std_ReturnType retVal = (Std_ReturnType) E_OK;
    uint64         timeStamp = 0U;
    Eth_CptsEvent     *pEvent = NULL;
    Eth_CptsTimeCounter *tc = &pCptsStateObj->timeCounter;

    SchM_Enter_Eth_ETH_EXCLUSIVE_AREA_0();

    if (0U != Eth_cptsEventPendStatus(pCptsStateObj->cpswBaseAddr))
    {
        /*
         * Pop the event into SW queue to be returned to when
         * Eth_GetIngressTimeStamp is called from EthIf_RxIndication
         */
        Eth_cptsHandleEvents(pCptsStateObj);
    }

    retVal = Eth_cptsLookupEvent(&gEthDrvObj.cptsObj,
                                 pEeventTemplate, &pEvent);
    if ( (Std_ReturnType) E_OK == retVal )
    {
        timeStamp = pEvent->timeStamp;

        Eth_cptsReleaseEvent(&gEthDrvObj.cptsObj, pEvent);

        /* convert to nanoseconds: */
        *nsec = Eth_cptsCyc2ns(tc, timeStamp);
    }

    SchM_Exit_Eth_ETH_EXCLUSIVE_AREA_0();

    return retVal;
}

 Std_ReturnType Eth_cptsCounterRead(
    Eth_CptsStateObj *pCptsStateObj, uint64 *nsec)
{
    Std_ReturnType retVal = (Std_ReturnType) E_OK;
    Eth_CptsTimeCounter *tc = &pCptsStateObj->timeCounter;
    uint64 currCycle = 0U;

    /* read cycle counter: */
    currCycle = Eth_cptsHwTimeCyclesRead(pCptsStateObj);

    if (0U != currCycle)
    {
        /* convert to nanoseconds: */
        *nsec = Eth_cptsCyc2ns(tc, currCycle);
    }
    else
    {
        retVal = (Std_ReturnType) E_NOT_OK;
    }

    return retVal;
}

 void Eth_cptsGetSysTime(Eth_CptsStateObj *pCptsStateObj,
                        uint64 *nsec,
                        Eth_TimeStampType *pTimeStamp)
{
    Eth_CptsTimeCounter *tc = &pCptsStateObj->timeCounter;
    uint64 currSec48bit, deltaSec48bit, temp64bit;
    uint32 remainder = 0U;

    /* Apply time sync correction and rate ratio */
    *nsec = (uint64)((float32)*nsec * tc->rateRatio);

    if (0U == *nsec)
    {
        pTimeStamp->nanoseconds = 0U;
        pTimeStamp->seconds = 0U;
        pTimeStamp->secondsHi = 0U;
    }
    else
    {
        currSec48bit = *nsec / (uint64)NSEC_PER_SEC;
        remainder = (uint32)(*nsec % (uint64)NSEC_PER_SEC);

        pTimeStamp->nanoseconds = remainder;

        temp64bit = ((uint64)tc->deltaTimeStamp.secondsHi << 32U);
        deltaSec48bit =  temp64bit + (uint64)tc->deltaTimeStamp.seconds;

        if ((boolean)TRUE == tc->deltaSign)
        {
            currSec48bit += deltaSec48bit;
            pTimeStamp->nanoseconds += tc->deltaTimeStamp.nanoseconds;
            if (pTimeStamp->nanoseconds >= NSEC_PER_SEC)
            {
                currSec48bit++;
                pTimeStamp->nanoseconds -= NSEC_PER_SEC;
            }

        }
        else
        {
            /* Delta negative */
            if (tc->deltaTimeStamp.nanoseconds > pTimeStamp->nanoseconds)
            {
                tc->deltaTimeStamp.nanoseconds += NSEC_PER_SEC;
                deltaSec48bit--;
            }

            if ( currSec48bit < deltaSec48bit)
            {
             /* It should never come here, it will if set correction is called
              * with negative delta with offset lesser than current time */
                currSec48bit = 0U;
                pTimeStamp->nanoseconds = 0U;
            }
            else
            {
                currSec48bit = currSec48bit - deltaSec48bit;

                pTimeStamp->nanoseconds =
                       pTimeStamp->nanoseconds - tc->deltaTimeStamp.nanoseconds;
            }
        }

        pTimeStamp->secondsHi = (uint16)
		                  ((currSec48bit & TIME_SYNC_SECONDSHIGH_MASK) >> 32U );
        pTimeStamp->seconds   = (uint32)(currSec48bit & TIME_SYNC_SECONDS_MASK);
    }
}

/**
 * converts system time (of format Eth_TimeStampType) to nanoseconds
 */
uint64 Eth_cptsSysTime2ns(Eth_TimeStampType *pTimeStamp)
{
    uint64 nsec;

    /* We dont consider secondsHi part of nSec computation here as it would
     * quickly overflow nsec */
    nsec = ((uint64)pTimeStamp->seconds * (uint64)NSEC_PER_SEC);
    nsec = nsec + (uint64)pTimeStamp->nanoseconds;

    return nsec;
}

float32 Eth_cptsGetRateRatio
                            (Eth_TimeIntDiffType *pIngressTsDelta,
                            Eth_TimeIntDiffType *pOriginTsDelta)
{
    Eth_TimeStampType *pIngressTs, *pOriginTs;
    uint64 ingressTimeStampNsec, originTimeStampNsec;
    float32 rateRatio = (float32)0U;

    pIngressTs = &pIngressTsDelta->diff;
    pOriginTs = &pOriginTsDelta->diff;

    ingressTimeStampNsec = Eth_cptsSysTime2ns(pIngressTs);
    originTimeStampNsec = Eth_cptsSysTime2ns(pOriginTs);

    if (0U != ingressTimeStampNsec)
    {
        rateRatio = (float32)((float64)originTimeStampNsec/(float64)ingressTimeStampNsec);
    }

    return rateRatio;
}


 void Eth_cptsTimeStampLoad(
    Eth_CptsStateObj *pCptsStateObj, uint64 timeStamp)
{
    uint32 upperTimeStamp = (uint32) (timeStamp >> 32U);
    uint32 lowerTimeStamp = (uint32) (timeStamp & 0xFFFFFFFFU);

    SchM_Enter_Eth_ETH_EXCLUSIVE_AREA_0();

    Eth_cptsDisableCPTSInterrupt(pCptsStateObj);

    /* Get rid of all old events since the timestamp base will change */
    Eth_cptsClearAllCPTSEvents(pCptsStateObj);

    /* Now set the timestamp value (lower 32-bit in HW and upper 32-bit in 
	   module state) */
    HW_WR_REG32( pCptsStateObj->cpswBaseAddr + CPSW_CPTS_TS_LOAD_VAL_REG, 
	                                                      lowerTimeStamp);
    HW_WR_REG32( pCptsStateObj->cpswBaseAddr + CPSW_CPTS_TS_LOAD_EN_REG, 0x1);

    pCptsStateObj->upperTimeStamp = upperTimeStamp;

    Eth_cptsEnableCPTSInterrupt(pCptsStateObj);

    SchM_Exit_Eth_ETH_EXCLUSIVE_AREA_0();

    return;
}

uint32 Eth_cptsEventPendStatus(uint32 baseAddr)
{
    /*
     * Check pending event interrupt
     * Note - To check multiple events, application should wait for an amount
     * of time greater than four CPTS_RFT_CLK periods plus four MAIN_CLK periods
     */
    uint32 regVal = HW_RD_REG32( baseAddr + CPSW_CPTS_INTSTAT_RAW_REG);

    return ( HW_GET_FIELD (regVal, CPSW_CPTS_INTSTAT_RAW_REG_TS_PEND_RAW) );
}


void Eth_cptsFindComputeOffsetDelta (Eth_CptsStateObj *pCptsStateObj,
                                     Eth_TimeIntDiffType *pNewIntDiff)
{
    uint64 currDeltaSec48bit=0U, newDeltaSec48bit= 0U, newSec48bit=0U, temp64bit =0U;
    Eth_TimeStampType *pNewOffsetTs = &pNewIntDiff->diff;
    Eth_CptsTimeCounter *tc = &pCptsStateObj->timeCounter;
    Eth_TimeStampType *pCurrDeltaTs = &tc->deltaTimeStamp;


    /* Convert nanoseconds to system time */
    temp64bit = pCurrDeltaTs->secondsHi;
    currDeltaSec48bit =  (uint64)(temp64bit << 32U) + 
	                     (uint64)pCurrDeltaTs->seconds;

    temp64bit = pNewOffsetTs->secondsHi;
    newSec48bit =  (uint64)(temp64bit << 32U) + (uint64)pNewOffsetTs->seconds;

    if (pNewIntDiff->sign == (boolean)FALSE)
    {
        if (currDeltaSec48bit >= newSec48bit )
        {
            tc->deltaSign = (boolean)TRUE; /* positive */
            if (currDeltaSec48bit == newSec48bit)
            {
                newDeltaSec48bit = 0U;
                if (pCurrDeltaTs->nanoseconds >= pNewOffsetTs->nanoseconds)
                {
                    tc->deltaTimeStamp.nanoseconds =
                        pCurrDeltaTs->nanoseconds - pNewOffsetTs->nanoseconds;
                }
                else
                {
                    tc->deltaSign = (boolean) FALSE; /* Negative */
                    tc->deltaTimeStamp.nanoseconds =
                        pNewOffsetTs->nanoseconds - pCurrDeltaTs->nanoseconds;
                }
            }
            else
            {
                if (pCurrDeltaTs->nanoseconds < pNewOffsetTs->nanoseconds)
                {
                    pCurrDeltaTs->nanoseconds += NSEC_PER_SEC;
                    currDeltaSec48bit--;
                }
                newDeltaSec48bit = currDeltaSec48bit - newSec48bit;
                tc->deltaTimeStamp.nanoseconds =
                          pCurrDeltaTs->nanoseconds - pNewOffsetTs->nanoseconds;
            }
        }
        else
        {
            tc->deltaSign = (boolean)FALSE; /* negative */
            if (pNewOffsetTs->nanoseconds < pCurrDeltaTs->nanoseconds)
            {
                pNewOffsetTs->nanoseconds += NSEC_PER_SEC;
                newSec48bit--;
            }
            newDeltaSec48bit = newSec48bit - currDeltaSec48bit;
            tc->deltaTimeStamp.nanoseconds =
                                pNewOffsetTs->nanoseconds - pCurrDeltaTs->nanoseconds;
        }

        tc->deltaTimeStamp.secondsHi = (uint16)
		              ((newDeltaSec48bit & TIME_SYNC_SECONDSHIGH_MASK) >> 32U );
        tc->deltaTimeStamp.seconds   = (uint32)
		                           ( newDeltaSec48bit & TIME_SYNC_SECONDS_MASK);

    }
    else
    {
        newDeltaSec48bit = currDeltaSec48bit + newSec48bit;
        tc->deltaTimeStamp.nanoseconds =
                     pCurrDeltaTs->nanoseconds + pNewIntDiff->diff.nanoseconds;
    }

    tc->deltaTimeStamp.secondsHi = (uint16)
	                 ( (newDeltaSec48bit & TIME_SYNC_SECONDSHIGH_MASK) >> 32U );
    tc->deltaTimeStamp.seconds   = (uint32)
	                   ( newDeltaSec48bit & TIME_SYNC_SECONDS_MASK);

}

/*---------------------------------------------------------------------------*\
|                           Static Function Definitions                        |
\*---------------------------------------------------------------------------*/

/* Init the buffer descriptor queue */
static inline void Eth_cptsEventQueueInit(
    Eth_CptsEventQueue *pCPTSEventQueue)
{
    pCPTSEventQueue->pHead = NULL;
    pCPTSEventQueue->pTail = NULL;
    pCPTSEventQueue->count = 0U;
}


 static void Eth_cptsTimestampPush(Eth_CptsStateObj *pCptsStateObj)
{
    /* Do SW timestamp push */

    /* check is TS PUSH already in flight */
    if (0U == pCptsStateObj->tsPushInFIFO)
    {
        pCptsStateObj->tsPushInFIFO = 1U;
        HW_WR_FIELD32( pCptsStateObj->cpswBaseAddr + CPSW_CPTS_TS_PUSH_REG, 
		                                    CPSW_CPTS_TS_PUSH_REG_TS_PUSH, 1U);
    }

    return;
}

 static void Eth_cptsConfigPort(
    Eth_CptsStateObj *pCptsStateObj, uint32 portNum)
{
    uint32 portCtrlReg = 0U, portMsgTypeReg = 0U;
    Eth_CptsConfigType  *pCptsCfg = &pCptsStateObj->cptsCfg;

   portCtrlReg  = pCptsStateObj->cpswBaseAddr + CPSW_ETH_PN_TS_CTL_REG + 
														CPSW_PN_OFFSET(portNum);
   portMsgTypeReg = pCptsStateObj->cpswBaseAddr + CPSW_ETH_PN_TS_SEQ_LTYPE_REG + 
														CPSW_PN_OFFSET(portNum);


    /* Enable all message types */
    if ((uint32)ETH_CPTS_MESSAGE_ALL == pCptsCfg->msgTypeCfg)
    {
        HW_WR_FIELD32( portCtrlReg, CPSW_ETH_PN_TS_CTL_REG_TS_MSG_TYPE_EN,
                                 CPSW_ETH_PN_TS_CTL_REG_TS_MSG_TYPE_EN_MAX );
    }
    else
    {
        HW_WR_FIELD32( portCtrlReg, CPSW_ETH_PN_TS_CTL_REG_TS_MSG_TYPE_EN,
                        pCptsCfg->msgTypeCfg);
    }

    /* Make sure Sequence ID offset is correct */
    HW_WR_FIELD32( portMsgTypeReg, CPSW_ETH_PN_TS_SEQ_LTYPE_REG_TS_SEQ_ID_OFFSET,
                    CPSW_P_TS_SEQ_ID_OFFSET );

    /* Enable Annex F of 1588v2 */
    HW_WR_FIELD32( portCtrlReg, CPSW_ETH_PN_TS_CTL_REG_TS_TX_ANNEX_F_EN, 1U);

    if (ETH_CPTS_VLAN_TYPE_NONE != pCptsCfg->vlanType)
    {

        /* Configure VLAN LTYPE1 */
    HW_WR_FIELD32( portCtrlReg, CPSW_ETH_PN_TS_CTL_REG_TS_RX_VLAN_LTYPE1_EN, 1U);
    HW_WR_FIELD32( portCtrlReg, CPSW_ETH_PN_TS_CTL_REG_TS_TX_VLAN_LTYPE1_EN, 1U);
	  
    if (ETH_CPTS_VLAN_TYPE_STACKED_TAGS == pCptsCfg->vlanType)
    {
            /* Configure VLAN LTYPE2 */
    HW_WR_FIELD32( portCtrlReg, CPSW_ETH_PN_TS_CTL_REG_TS_RX_VLAN_LTYPE2_EN, 1U);
    HW_WR_FIELD32( portCtrlReg, CPSW_ETH_PN_TS_CTL_REG_TS_TX_VLAN_LTYPE2_EN, 1U);
    }
    }
}

static Std_ReturnType Eth_cptsHwInit(
    Eth_CptsStateObj *pCptsStateObj)
{
    Std_ReturnType retVal = (Std_ReturnType) E_NOT_OK;
    Eth_CptsConfigType  *pCptsCfg = &pCptsStateObj->cptsCfg;
	uint8 portIdx = pCptsStateObj->portNum;

    /*
     * Set the TS LTYPE to the IEEE 1588 Annex F value.*/
    HW_WR_FIELD32( pCptsStateObj->cpswBaseAddr + CPSW_ETH_PN_TS_VLAN_LTYPE_REG +
								  CPSW_PN_OFFSET(portIdx),
                                  CPSW_ETH_PN_TS_VLAN_LTYPE_REG_TS_VLAN_LTYPE1, 
				                                         CPSW_ETHTYPE_PTP1588);
    /*
     * Set the VLAN LTYPE to the standard 802.1Q VLAN Ethertype. 
     */
    if (ETH_CPTS_VLAN_TYPE_NONE != pCptsCfg->vlanType)
    {
        HW_WR_FIELD32( pCptsStateObj->cpswBaseAddr + CPSW_VLAN_LTYPE_REG, 
                                    CPSW_VLAN_LTYPE_REG_VLAN_LTYPE_INNER,
                                              CPSW_VLAN_LTYPE_VLAN_INNER);
        if (ETH_CPTS_VLAN_TYPE_STACKED_TAGS == pCptsCfg->vlanType)
        {
            HW_WR_FIELD32( pCptsStateObj->cpswBaseAddr + CPSW_VLAN_LTYPE_REG,
                                        CPSW_VLAN_LTYPE_REG_VLAN_LTYPE_OUTER,
                                                  CPSW_VLAN_LTYPE_VLAN_OUTER);
        }
    }

    Eth_cptsConfigPort(pCptsStateObj, pCptsStateObj->portNum);

    /* Init the event queue using memory provided by the stack/app */
    {
        uint32 i;
        Eth_CptsEvent *pCPTSEvents = gCPTSEvents;

        /* Clear the entire events array, add to free queue */
        for (i = 0U; i < ETH_CPTS_EVENT_CNT; i++)
        {
         Eth_cptsReleaseEvent(pCptsStateObj, &pCPTSEvents[i]);
        }
    }

    retVal = (Std_ReturnType) E_OK;

    return retVal;
}

 static Std_ReturnType Eth_cptsHwDeInit(
    Eth_CptsStateObj *pCptsStateObj)
{
    Std_ReturnType retVal = (Std_ReturnType) E_NOT_OK;

    Eth_CptsEvent *pEvent = NULL_PTR;

    Eth_cptsDisableCPTSInterrupt(pCptsStateObj);

    /* Clear out any active SW queue events */
    Eth_cptsClearSWQueueEvents(pCptsStateObj);

    /* Clear out all the inactive SW queue events */
    pEvent = Eth_cptsEventDequeue(&pCptsStateObj->freeQueue);
    while (pEvent != NULL)
    {
        memset(pEvent, 0, sizeof(Eth_CptsEvent));
        pEvent = Eth_cptsEventDequeue(&pCptsStateObj->freeQueue);
    }

    Eth_cptsEnableCPTSInterrupt(pCptsStateObj);

    retVal = (Std_ReturnType) E_OK;

    return retVal;
}


static void Eth_cptsGetEvent(
    Eth_CptsStateObj *pCptsStateObj, Eth_CptsEvent **ppEvent)
{
    /* If cpts no active events, NULL would be returned */
    *ppEvent = Eth_cptsEventDequeue(&pCptsStateObj->activeQueue);
}


static Std_ReturnType Eth_cptsLookupEvent(
    Eth_CptsStateObj *pCptsStateObj, const Eth_CptsEvent *pEventTemplate, 
	                                             Eth_CptsEvent **ppEvent)
{
    Std_ReturnType retVal = (Std_ReturnType) E_OK;

    *ppEvent = Eth_cptsEventExtract(&pCptsStateObj->activeQueue, pEventTemplate);

    if (*ppEvent == NULL)
    {
        retVal = (Std_ReturnType) E_NOT_OK;;

        /* TODO recheck this after IP team confirmation about scenario where
         * CPTS capturing Eth event but CPDMA dropping packet (no B.D) so event
         * never gets consumed by stack (via getIngressTs) */
        if ( ETH_CPTS_EVENT_CNT == pCptsStateObj->activeQueue.count )
        {
            Eth_cptsClearStaleEvents(pCptsStateObj);
        }
    }

    return retVal;
}

 static void Eth_cptsReleaseEvent(
    Eth_CptsStateObj *pCptsStateObj, Eth_CptsEvent *pEvent)
{
    /* Clear the event contents */
    memset(pEvent, 0, sizeof(Eth_CptsEvent));

    Eth_cptsEventEnqueue(&pCptsStateObj->freeQueue, pEvent);

    return;
}

 static void Eth_cptsClearAllCPTSEvents(Eth_CptsStateObj *pCptsStateObj)
{
    Eth_cptsDisableCPTSInterrupt(pCptsStateObj);

    /* Clear out any active events in the SW queue */
    Eth_cptsClearSWQueueEvents(pCptsStateObj);

    /* Clear out any active events in the HW FIFO */
    Eth_cptsClearHWFifoEvents(pCptsStateObj);

    Eth_cptsEnableCPTSInterrupt(pCptsStateObj);

    return;
}

/* Remove a buffer descriptor from the head of the descriptor queue */
static Eth_CptsEvent* Eth_cptsEventDequeue(
    Eth_CptsEventQueue *pCPTSEventQueue)
{
    Eth_CptsEvent *pCPTSEvent = NULL;

    if (NULL != pCPTSEventQueue)
    {
        /*
         * This should happen in critical section. Calling function should
         * call with interrupts disabled.
         */

        pCPTSEvent = pCPTSEventQueue->pHead;

        if (NULL != pCPTSEvent)
        {
            pCPTSEventQueue->count--;

            if (0U != pCPTSEventQueue->count)
            {
                pCPTSEventQueue->pHead = pCPTSEvent->pNextEvent;
            }
            else
            {
                pCPTSEventQueue->pHead = NULL;
            }
            pCPTSEvent->pNextEvent = NULL;
            /* Check if queue is now empty */
            if (NULL == pCPTSEventQueue->pHead)
            {
                pCPTSEventQueue->pTail = NULL;
            }
        }

    }
    return (pCPTSEvent);
}

/* Add a buffer descriptor to the tail of the descriptor queue */
 static void Eth_cptsEventEnqueue(
    Eth_CptsEventQueue *pCPTSEventQueue, Eth_CptsEvent *pCPTSEvent)
{
    if ( (NULL != pCPTSEvent) && (NULL != pCPTSEventQueue))
    {
        pCPTSEvent->pNextEvent = NULL;

        if (NULL == pCPTSEventQueue->pHead)
        {
            /* Queue is empty, make head point to new event */
            pCPTSEventQueue->pHead = pCPTSEvent;
        }
        else
        {
            /* Queue is not empty, make previous tail point to new event */
            pCPTSEventQueue->pTail->pNextEvent = pCPTSEvent;
        }

        /* Make tail of queue point to new event */
        pCPTSEventQueue->pTail = pCPTSEvent;
        pCPTSEventQueue->count++;
    }
}

/* Lookup and extract an event from the queue */
 static Eth_CptsEvent *Eth_cptsEventExtract(
    Eth_CptsEventQueue *pCPTSEventQueue, const Eth_CptsEvent *pEventTemplate)
{
    Eth_CptsEvent *pCPTSEvent = NULL;

    if (NULL != pCPTSEventQueue)
    {
        uint32 matchFound         = 0U;
        Eth_CptsEvent *currentEvent    = NULL;
        Eth_CptsEvent *prevEvent       = NULL;

        currentEvent = pCPTSEventQueue->pHead;

        while (NULL != currentEvent)
        {
            Eth_CptsEvent *pEvent = currentEvent;
            /* Check if current event type matches template event type */
            if ( (pEventTemplate->eventType == CPTS_EVENT_ETHERNET_RECEIVE) ||
                 (pEventTemplate->eventType == CPTS_EVENT_ETHERNET_TRANSMIT) )
            {
                /* Ethernet events match by event type, message type, and seq ID */
                if ((pEventTemplate->eventType == pEvent->eventType) &&
                    (pEventTemplate->messageType == pEvent->messageType) &&
                    (pEventTemplate->sequenceId == pEvent->sequenceId))
                {
                    matchFound = 1U;
                }
            }
            else
            {
                /* All other events match by event type only */
                if (pEventTemplate->eventType == pEvent->eventType)
                {
                    matchFound = 1U;
                }
            }

            if (1U == matchFound)
            {
                break;
            }

            prevEvent = currentEvent;
            currentEvent = currentEvent->pNextEvent;
        }

        if (matchFound != 0U)
        {
            /* The currentEvent should be removed from the queue */
            if (currentEvent == pCPTSEventQueue->pHead)
            {
                /* Removing head (and also tail if only one element in queue) */
                pCPTSEventQueue->pHead = currentEvent->pNextEvent;
            }
            else if (currentEvent == pCPTSEventQueue->pTail)
            {
                /* Removing tail (from queue with two or more elements) */
                prevEvent->pNextEvent = NULL;
                pCPTSEventQueue->pTail = prevEvent;
            }
            else
            {
                /* Removing from middle of queue (from queue with 3 or more entries) */
                prevEvent->pNextEvent = currentEvent->pNextEvent;
            }
            /* Decrement the event queue count */
            pCPTSEventQueue->count--;

            /* Disconnect event to be returned from others */
            currentEvent->pNextEvent = NULL;
            pCPTSEvent = currentEvent;
        }
#if (CPTS_DEBUG_ENABLED == STD_ON)
        else
        {
            gCptsStats.matchNotFound++;
        }
#endif
    }

    return (pCPTSEvent);
}

/* Calculate the full timestamp from the lower 32-bits and the upper
   32-bits in the state */
static uint64 Eth_cptsGetTimestamp(
    const Eth_CptsStateObj *pCptsStateObj, uint32 lowerTimeStamp)
{
    uint64 retTimeStamp = (uint64) pCptsStateObj->upperTimeStamp;

    /* If misalignment check is required, check it and adjust the upper 32-bits */
    if (1U == pCptsStateObj->checkForMisalignment)
    {
        if (0U != (uint32)(lowerTimeStamp & (uint32) 0x80000000U) )
        {
            retTimeStamp -= 1U;
        }
    }

    /* Shift upper 32-bit to top of timestamp return value and add in lower 32-bits */
    retTimeStamp = (retTimeStamp << 32U) + (uint64) lowerTimeStamp;

    return retTimeStamp;
}

/* Clear all the active events*/
static void Eth_cptsClearSWQueueEvents(
    Eth_CptsStateObj *pCptsStateObj)
{
    Eth_CptsEvent *pEvent = NULL;

    /* Clear out any active events in the SW queue */
    Eth_cptsGetEvent(pCptsStateObj, &pEvent);
    while (pEvent != NULL)
    {
        Eth_cptsReleaseEvent(pCptsStateObj,pEvent);
        Eth_cptsGetEvent(pCptsStateObj, &pEvent);
    }
}

 static void Eth_cptsClearHWFifoEvents(
    Eth_CptsStateObj *pCptsStateObj)
{
    uint32 intStatus = HW_RD_REG32( pCptsStateObj->cpswBaseAddr 
	                                +CPSW_CPTS_INTSTAT_RAW_REG);

    /* Clear out any active events in the HW FIFO */
    while (0U != (intStatus & 0x1U))
    {
        /*
         * Set the EVENT_POP field (bit 0) of the CPTS_EVENT_POP register to pop the
         * previously read value off of the event FIFO.
         */
        HW_WR_FIELD32( pCptsStateObj->cpswBaseAddr + CPSW_CPTS_EVENT_POP_REG, 
		               CPSW_CPTS_EVENT_POP_REG_EVENT_POP, 1U);
        intStatus = HW_RD_REG32( pCptsStateObj->cpswBaseAddr + 
		               CPSW_CPTS_INTSTAT_RAW_REG);
    }
}

 static void Eth_cptsDisableCPTSInterrupt(
    Eth_CptsStateObj *pCptsStateObj)
{
    /* Mask the interrupts */
    HW_WR_FIELD32( pCptsStateObj->cpswBaseAddr + CPSW_CPTS_INT_ENABLE_REG,
                          	CPSW_CPTS_INT_ENABLE_REG_TS_PEND_EN, 0U);
}

 static void Eth_cptsEnableCPTSInterrupt(
    Eth_CptsStateObj *pCptsStateObj)
{
    /* Unmask the interrupts */
    HW_WR_FIELD32( pCptsStateObj->cpswBaseAddr + CPSW_CPTS_INT_ENABLE_REG, 
	                                CPSW_CPTS_INT_ENABLE_REG_TS_PEND_EN, 
									CPSW_CPTS_INT_ENABLE_REG_TS_PEND_EN_MAX);
}

 static void Eth_cptsCounterInit(
    Eth_CptsStateObj* pCptsStateObj)
{
    Eth_CptsTimeCounter *tc = &pCptsStateObj->timeCounter;

    Eth_cptsFindShiftMult(pCptsStateObj);

    memset (&tc->deltaTimeStamp, 0, sizeof (Eth_TimeStampType));
    tc->deltaSign = (boolean)TRUE; /* positive */

    /* Initially set the rate ration to one */
    tc->rateRatio = (float32)1U;
}

 static void Eth_cptsFindShiftMult(
    Eth_CptsStateObj *pCptsStateObj)
{
    float32 period = (float32) ((float32)1.0f/(float32)pCptsStateObj->cptsInputFreq);
    uint32 mult = 1U;

    period = period * (float32) NSEC_PER_SEC;

    while (period < (float32) 1)
    {
        period *= (float32)mult;
        mult *= (uint32)10U;
    }

    pCptsStateObj->timeCounter.mult = (uint32) (period * (float32) 100000U);
    pCptsStateObj->timeCounter.shift = mult * 100000U;

}

static uint64 Eth_cptsHwTimeCyclesRead(
    Eth_CptsStateObj *pCptsStateObj)
{
    uint64 timeStamp = 0U;
    Eth_CptsEvent     eventTemplate;
    Eth_CptsEvent     *pEvent = NULL;
    uint32 forceRetries = 50U;
    Std_ReturnType retVal;

    SchM_Enter_Eth_ETH_EXCLUSIVE_AREA_0();

    /*
     * Clear all pending events - scenario where CPTS capturing Eth event but
     * CPDMA dropping packet (no B.D) so event never gets consumed by stack
     * (via getIngressTs)
     */
    if ( ETH_CPTS_EVENT_CNT == pCptsStateObj->activeQueue.count )
    {
        Eth_cptsClearStaleEvents(pCptsStateObj);
       /* Check till event is returned*/
        while ( 1U == Eth_cptsEventPendStatus(pCptsStateObj->cpswBaseAddr) )
        {
            Eth_cptsHandleEvents(pCptsStateObj);
        }
    }

    Eth_cptsTimestampPush(pCptsStateObj);

    /*
     * We handle TS_PUSH event by ourself instead of in ISR.
     * DO NOT ENABLE interrupts till event is handled.
     */
    while (0U != forceRetries)
    {
        /* Check till event is returned*/
        if ( 1U == Eth_cptsEventPendStatus(gEthDrvObj.cptsObj.cpswBaseAddr) )
        {
            break;
        }
#if (CPTS_DEBUG_ENABLED == STD_ON)
        gCptsStats.eventPushWait++;
#endif
        forceRetries--;
    }

    if ( 0U < forceRetries )
    {
        Eth_cptsHandleEvents(&gEthDrvObj.cptsObj);

        eventTemplate.eventType = CPTS_EVENT_TIME_STAMP_PUSH;
        retVal = Eth_cptsLookupEvent(&gEthDrvObj.cptsObj, &eventTemplate, &pEvent);
        if ( (Std_ReturnType) E_OK == retVal )
        {
            timeStamp = pEvent->timeStamp;
            Eth_cptsReleaseEvent(&gEthDrvObj.cptsObj, pEvent);
        }
    }
    else
    {
        /* This event is missed maybe due to HW FIFO being full, remove lock */
        pCptsStateObj->tsPushInFIFO = 0U;
#if (CPTS_DEBUG_ENABLED == STD_ON)
        gCptsStats.getTimeFail++;
#endif
    }

    SchM_Exit_Eth_ETH_EXCLUSIVE_AREA_0();

    return timeStamp;
}


/* converts nanoseconds to counter cycles */
 uint64 Eth_cptsSysns2Cycles(
    uint64 nsec, Eth_CptsTimeCounter *tc)
{
    uint64 cycles = (uint64) nsec;

    cycles = cycles * (uint64)tc->shift;
    cycles = (cycles/(uint64)tc->mult);

    return cycles;
}


/* converts cycle counter cycles to nanoseconds */
 static inline uint64 Eth_cptsCyc2ns(
    Eth_CptsTimeCounter *tc, uint64 cycles)
{
    uint64 ns = 0U;

    ns = (uint64) ((uint64)cycles * (uint64)tc->mult);
    ns = (ns / (uint64) tc->shift);

    return ns;
}

static void
    Eth_cptsClearStaleEvents(Eth_CptsStateObj *pCptsStateObj)
{
    uint32 i = 0U;
    Eth_CptsEvent *pEvent = NULL_PTR;

    /* Clear out few active events in the SW queue to make space*/
    for (i = 0U; i < (ETH_CPTS_EVENT_CNT/2U); i++)
    {
        pEvent = NULL;

        Eth_cptsGetEvent(pCptsStateObj, &pEvent);

        if (pEvent == NULL)
        {
            break;
        }

        Eth_cptsReleaseEvent(pCptsStateObj,pEvent);
    }
}


#define ETH_STOP_SEC_CODE
#include "Eth_MemMap.h"
/*---------------------------------------------------------------------------*\
|                                 End of File                                 |
\*---------------------------------------------------------------------------*/
