/* ======================================================================
 *   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_stats.c
 *
 *   \brief CPSW Statistics gathering and processing functionality
 */

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

#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 "cpsw_priv.h"
#include "cpsw.h"
#include "cpsw_stats.h"
#include "hw_cpsw_stats.h"

/* ========================================================================== */
/*                           Macros & Typedefs                                */
/* ========================================================================== */

/* None */

/* ========================================================================== */
/*                         Structures and Enums                               */
/* ========================================================================== */

/* None */

/* ========================================================================== */
/*                 Internal Function Declarations                             */
/* ========================================================================== */

/**
 * \brief Clear statistics from stitistics structure
 */
static void EthStatsClear(uint32 baseAddr, Eth_StatsObj *pStatsObj);

/* ========================================================================== */
/*                            Global Variables                                */
/* ========================================================================== */

#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 Stats structure
 * This will be used in case of both ports used simultaneously.
 */
static boolean gStatsRef = FALSE;
#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"

/* ========================================================================== */
/*                          Function Definitions                              */
/* ========================================================================== */

#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"
Std_ReturnType EthStatsInit( uint32 baseAddr,
                                    Eth_StatsObj *pStatsObj,
                                    uint8 portIdx)
{
    Std_ReturnType retVal = (Std_ReturnType) E_NOT_OK;

    if (NULL != pStatsObj)
    {
        retVal = (Std_ReturnType) E_OK;
        /* Increment the reference count */
        if (gStatsRef == FALSE)
        {
			gStatsRef = TRUE;
            /* Zero init the Eth_StatsObj structure */
            (void) memset((void *) &(pStatsObj->stats), 0,
                          sizeof (pStatsObj->stats));
            if ((uint32) 1U == pStatsObj->enableStatistics)
            {
                /* Enable statistics gathering for each port */
                CPSWStatsEnable(baseAddr, (uint32) 0U, (uint32) TRUE);
                CPSWStatsEnable(baseAddr, (uint32) portIdx, (uint32) TRUE);
#if (ETH_STATS_INTERRUPT == STD_ON)
                /* Enable the STATS interrupt by setting the mask bit */
                CPSWCpdmaDmaIntrEnable(baseAddr, CPSW_STAT_INT);

                /* Enable the STATS interrupt*/
                CPSWMiscIntrEnable(baseAddr,
                    (uint32) CPSW_SS_MISC_STATUS_STAT_PEND_MASK);
#endif
            }
        }
        else
        {
            /*
             * If reference count at entry was non-zero, Stats was already
             * opened, so do nothing.
             */
        }

    }
    else
    {
        retVal = (Std_ReturnType) E_NOT_OK;
    }

    return retVal;
}


void EthStatsDeInit( uint32 baseAddr, Eth_StatsObj *pStatsObj, uint8 portIdx)
{

    /* Decrement reference counter and return if still non-zero */

    if (gStatsRef == TRUE)
    {
		gStatsRef = FALSE;
        EthStatsClear(baseAddr, pStatsObj);

		CPSWStatsEnable(baseAddr, (uint32) 0, (uint32) FALSE);
        CPSWStatsEnable(baseAddr, (uint32) portIdx, (uint32) FALSE);

#if (ETH_STATS_INTERRUPT == STD_ON)
    /* Disable the STATS interrupt by setting the mask bit */
        CPSWCpdmaDmaIntrDisable(baseAddr, CPSW_STAT_INT);

        /* Enable the STATS interrupt in the wrapper module */
        CPSWMiscIntrDisable(
            baseAddr,
            (uint32) CPSW_SS_MISC_STATUS_STAT_PEND_MASK);
#endif
    }

}

void EthStatsUpdate(uint32 baseAddr, Eth_StatsObj *pStatsObj, uint8 portNum)
{
    volatile uint32 *pStatRegs = NULL_PTR;
    uint32          *pStatAddr = NULL_PTR;
    uint32           statsAddr = 0U, statval = 0U;

    statsAddr = (baseAddr + CPSW_STAT_1_RXGOODFRAMES + 
													CPSW_STAT_OFFSET(portNum));
 
    pStatRegs = (volatile uint32 *) statsAddr;

    pStatAddr = (uint32 *) ((&pStatsObj->stats));

    while ((uint32) pStatRegs <= (uint32) (baseAddr + 
	                CPSW_STAT_1_TX_MEMORY_PROTECT_ERROR + 
					CPSW_STAT_OFFSET(portNum)))
    {
        statval    = *pStatRegs;
        *pStatRegs = statval;

        *pStatAddr += statval;

        pStatRegs++;
        pStatAddr++;
    }

    return;
}

/* CPSW IOCTL implementations */

    static void EthStatsClear( uint32 baseAddr, Eth_StatsObj *pStatsObj)
{
    volatile uint32 *pStatRegs = NULL_PTR;
    uint32          *pStatAddr = NULL_PTR;
    uint32           statsAddr = 0U;

    statsAddr = (baseAddr + CPSW_STAT_0_RXGOODFRAMES);
    pStatRegs = (volatile uint32 *) (statsAddr);
    pStatAddr = (uint32 *) ((void *)(&pStatsObj->stats));

    while ((uint32) pStatRegs <= (uint32) (baseAddr + 
	        CPSW_STAT_1_TX_MEMORY_PROTECT_ERROR))
    {
        /* Write to decrement to zero */
        *pStatRegs = 0xFFFFFFFFU;
        /* Clear the software accumulation structure */
        *pStatAddr = (uint32) 0U;

        pStatRegs++;
        pStatAddr++;
    }
}

 void EthGetStats(uint32 baseAddr,
                                       Eth_StatsObj  *pStatsObj,
                                       Eth_StatsType *pStatistics,
									   uint8 portNum)
{
    /* Update the stats */
    EthStatsUpdate(baseAddr, pStatsObj, portNum);

    /* Copy the updated stats to the application */
    (void) memcpy(pStatistics,
                  &(pStatsObj->stats),
                  sizeof (Eth_StatsType));

    return;
}

#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"
