/* ======================================================================
 *   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     Eth_Irq.c
 *
 *  \brief    This file contains the ISR implementation of ETH MCAL driver
 *
 */

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

#include "Eth_Cfg.h"
#include "Eth.h"
#include "EthIf_Cbk.h"
#include "Eth_Irq.h"
#if  (ETH_ENABLE_MII_API == STD_ON)
#include "EthTrcv.h" 
#endif
#if (STD_ON == ETH_DEV_ERROR_DETECT)
#include "Det.h"
#endif
#include "Dem.h"
#include "SchM_Eth.h"
#include "Eth_Priv.h"
#include "cpsw_priv.h"
#include "cpsw_stats.h"
#include "cpsw_cpts.h"
#include "cpsw.h"
#include "cpsw_mdio.h"
#include "hw_cpsw_ss.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"

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

/* None */

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

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

#if  (ETH_ENABLE_MII_API == STD_ON)
static void Eth_miiIndication(uint32 regVal, uint16 dataVal);
#endif

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

/* None */

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

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


#if  (ETH_ENABLE_MII_API == STD_ON)
static void
    Eth_miiIndication(uint32 regVal, uint16 dataVal)
{
    uint32 command = 0U;
    uint8  regIdx =0U, phyAddr =0U;

    command = HW_GET_FIELD(regVal, MDIO_USER_GROUP_USER_ACCESS_REG_WRITE);
    regIdx = (uint8) HW_GET_FIELD(regVal, 
	                             MDIO_USER_GROUP_USER_ACCESS_REG_REGADR);
    phyAddr = (uint8) HW_GET_FIELD(regVal, 
	                             MDIO_USER_GROUP_USER_ACCESS_REG_PHYADR);

    if (MDIO_USER_GROUP_USER_ACCESS_REG_WRITE == command)
    {
        EthTrcv_WriteMiiIndication(gEthDrvObj.ctrlIdx, phyAddr, regIdx);
    }
    else
    {
        EthTrcv_ReadMiiIndication(gEthDrvObj.ctrlIdx, phyAddr, regIdx,
                                  dataVal);
    }

    return;
}
#endif

/* ========================================================================== */
/*                          Function Definitions                              */
/* ========================================================================== */
#if  (ETH_ENABLE_RX_INTERRUPT == STD_ON)
/**
 *  \brief      This is the ISR function for receive interrupt of ETH
 *              controller 0.
 *              The function shall clear the interrupt and read the frames of
 *              all filled receive buffers.
 *              The function passes each received frame to the Ethernet
 *              interface using the callback
 *              function EthIf_RxIndication.
 *
 *  \param[in]  void
 *
 *  \context    ISR
 */
/* Design :  */
/*
 * Requirements : SWS_Eth_00109, SWS_Eth_00110, SWS_Eth_00111, SWS_Eth_00112,
 *                SWS_Eth_00113
 */

#if (ETH_ISR_TYPE == ETH_ISR_CAT1 || ETH_ISR_TYPE == ETH_ISR_VOID)
FUNC(void, ETH_CODE_ISR) Eth_RxIrqHdlr_0(void)
#else
ISR(Eth_RxIrqHdlr_0)
#endif
{
    uint32         rxIntFlags   = 0U, threshIntFlags =0U;
    uint32         channelNum = 0U, channelMask = 0U;
#if (STD_ON == ETH_DEV_ERROR_DETECT)
    uint32         cp;
#endif  /* #if (STD_ON == ETH_DEV_ERROR_DETECT) */

    /* Read the Rx interrupt cause from WR_C0_RX_STAT */
    rxIntFlags = CPSWChIntrStatus(gEthDrvObj.baseAddr,
                                  CPSW_CH_INTR_RX);
#if (STD_ON == ETH_DEV_ERROR_DETECT)
    if (ETH_STATE_INIT != gEthDrvStatus)
    {
        /*
         * If a crash occurs while interrupts were pending and the core is
         * restored (no system wide reset) we can end up here since the CPSW
         * ISRs are hooked before the driver has a chance to reset the HW.
         */
        while (0U != rxIntFlags)
        {
            channelMask = 0x1U << channelNum;
            /* Clear interrupts for this channel */
            CPSWCpdmaReadRxCP(gEthDrvObj.baseAddr, channelNum, &cp);
            CPSWCpdmaXferCpWrite(gEthDrvObj.baseAddr,
                                 ETH_XFER_MODE_RX, channelNum, cp);
            /* Clear the channel flag for the channel just handled */
            rxIntFlags &= ~channelMask;
            channelNum++;
        }
        (void) Det_ReportError(
            ETH_MODULE_ID,
            ETH_INSTANCE_ID,
            ETH_SID_RX_IRQ_HDLR,
            ETH_E_UNINIT);
    }
    else
#endif
    {
        /* Handle both Rx threshold and paced Rx interrupts */
        threshIntFlags = CPSWChIntrStatus(gEthDrvObj.baseAddr,
                                            CPSW_CH_INTR_RX_THR);
        rxIntFlags |= threshIntFlags;

        /* Look for receive interrupts from across all CPDMA Rx Channels */
        while (0U != rxIntFlags)
        {
            channelMask = 0x1U << channelNum;

            if ((uint32) 0U != (uint32) (rxIntFlags & channelMask))
            {
                EthRxBuffDescProcess(gEthDrvObj.ctrlIdx, channelNum);
            }

            /* Clear the channel flag for the channel just handled */
            rxIntFlags &= ~channelMask;
            channelNum++;
        }
        if (0U != threshIntFlags)
        {
            /* All outstanding RX_THRESH interrupts were handled */
            CPSWCpdmaWriteEoiVector(gEthDrvObj.baseAddr, 
			                        CPSW_WR_INTR_LINE_RX_THR);
        }
    }

    /* Write the EOI register */
    CPSWCpdmaWriteEoiVector(gEthDrvObj.baseAddr, CPSW_WR_INTR_LINE_RX);

}
#endif

#if  (ETH_ENABLE_TX_INTERRUPT == STD_ON)
/**
 *  \brief      This is the ISR function for for transmit interrupt of ETH
 *              controller 0.
 *              This function shall clear the interrupt and check all filled
 *              transmit buffers for
 *              successful transmission. The function issues transmit
 *              confirmation for each
 *              transmitted frame using the callback function
 *              EthIf_TxConfirmation if requested by
 *              the previous call of Eth_Transmit service.
 *
 *  \param[in]  void
 *
 *  \context    ISR
 */
/* Design : */
/*
 * Requirements : SWS_Eth_00114, SWS_Eth_00115, SWS_Eth_00116, SWS_Eth_00117,
 *                SWS_Eth_00118
 */

#if (ETH_ISR_TYPE == ETH_ISR_CAT1 || ETH_ISR_TYPE == ETH_ISR_VOID)
void Eth_TxIrqHdlr_0(void)
#else

ISR(Eth_TxIrqHdlr_0)
#endif
{
    uint32         txIntFlags   = 0U;
    uint32         channelNum = 0U, channelMask = 0U;
#if (STD_ON == ETH_DEV_ERROR_DETECT)
    uint32         cp;
#endif  /* #if (STD_ON == ETH_DEV_ERROR_DETECT) */

    /* Read the Rx interrupt cause from WR_C0_RX_STAT */
    txIntFlags = CPSWChIntrStatus(gEthDrvObj.baseAddr,
                                  CPSW_CH_INTR_TX);
#if (STD_ON == ETH_DEV_ERROR_DETECT)
    if (ETH_STATE_INIT != gEthDrvStatus)
    {
      
        while (0U != txIntFlags)
        {
            channelMask = 0x1U << channelNum;
            /* Clear interrupts for this channel */
            CPSWCpdmaReadTxCP(gEthDrvObj.baseAddr, channelNum, &cp);
            CPSWCpdmaXferCpWrite(gEthDrvObj.baseAddr,
                                 ETH_XFER_MODE_TX, channelNum, cp);
            /* Clear the channel flag for the channel just handled */
            txIntFlags &= ~channelMask;
            channelNum++;
        }
        (void) Det_ReportError(ETH_MODULE_ID, ETH_INSTANCE_ID,
                               ETH_SID_TX_IRQ_HDLR, ETH_E_UNINIT);
    }
    else
#endif
    {
        /* Look for transmit interrupts from across all CPDMA Tx Channels */
        while (0U != txIntFlags)
        {
            channelMask = 0x1U << channelNum;

            if ((uint32) 0U != (uint32) (txIntFlags & channelMask))
            {
                EthTxBuffDescProcess(gEthDrvObj.ctrlIdx, channelNum);
            }

            /* Clear the channel flag for the channel just handled */
            txIntFlags &= ~channelMask;
            channelNum++;
        }
    }

    /* Write the EOI register */
    CPSWCpdmaWriteEoiVector(gEthDrvObj.baseAddr, CPSW_WR_INTR_LINE_TX);

}
#endif

/**
 *  \brief      This is the ISR function for for miscellaneous interrupts of ETH
 *              controller 0.
 *              This function is an ISR for combination of interrupts like time
 *              sync event, statistics, host error and MDIO interrupts.
 *
 *  \param[in]  void
 *
 *  \context    ISR
 */

#if (ETH_ISR_TYPE == ETH_ISR_CAT1 || ETH_ISR_TYPE == ETH_ISR_VOID)
FUNC(void, ETH_CODE_ISR) Eth_MiscIrqHdlr_0(void)
#else
ISR(Eth_MiscIrqHdlr_0)
#endif
{
    uint32 intFlags = 0;

    /* Read the Misc interrupt cause from WR_C0_MISC_STAT */
    intFlags = CPSWMiscIntrStatus(gEthDrvObj.baseAddr);

    /* Look for Statistics Interrupt */
    if (0U !=
             (uint32) HW_GET_FIELD(intFlags, CPSW_SS_MISC_STATUS_STAT_PEND))
    {
#if (ETH_STATS_INTERRUPT == STD_ON)
        /* Update the driver statistics structure */
      /*  EthStatsUpdate(gEthDrvObj.baseAddr, &(gEthDrvObj.statsObj));*/
#endif
    }
    /* Look for CPTS Event Interrupt */
    else if (0U != (uint32) HW_GET_FIELD(intFlags, 
	                                    CPSW_SS_MISC_STATUS_EVNT_PEND))
    {
#if (ETH_GLOBALTIMESUPPORT_API == STD_ON)
        /* Check till event is returned*/
        while ( 1U == Eth_cptsEventPendStatus(gEthDrvObj.cptsObj.cpswBaseAddr) )
        {   /* Call the CPTS module to handle all CPTS event interrupts */
            Eth_cptsHandleEvents( &(gEthDrvObj.cptsObj) );
        }
#endif
    }
    /* Look for CPDMA Host Error Interrupt */
    else if (0U !=
             (uint32) HW_GET_FIELD(intFlags, CPSW_SS_MISC_STATUS_HOST_PEND))
    {

#if (ETH_E_HARDWARE_ERROR != ETH_DEM_NO_EVENT)
        Dem_SetEventStatus(ETH_E_HARDWARE_ERROR, DEM_EVENT_STATUS_FAILED);
#endif
    }
    else if (0U !=
             (uint32) HW_GET_FIELD(intFlags,
                                   CPSW_SS_MISC_STATUS_MDIO_LINKINT))
    {
        /* Link interrupt not handled */
    }
    else
    {
        if (0U !=
            (uint32) HW_GET_FIELD(intFlags, CPSW_SS_MISC_STATUS_MDIO_USERINT))
        {
#if  (ETH_ENABLE_MII_API == STD_ON)
            /* Store the data if the read is acknowledged */
            uint16 dataVal = 0U;
            uint32 regVal = 0U;
			uint32 phy = 0U;
			uint32 offset = 0U;
			phy = HW_RD_REG32(gEthDrvObj.baseAddr + MDIO_USER_INT_MASKED_REG);
			if((phy & MDIO_USER_INT_MASKED_REG_MAX) == MDIO_USER_INT_MASKED_REG_MAX)
			{
				offset = MDIO_USER_GROUP_USER_OFFSET;
			}
            regVal = CPSWMdioRegRead(gEthDrvObj.baseAddr, 
			                       MDIO_USER_GROUP_USER_ACCESS_REG + offset);
            if (MDIO_USER_GROUP_USER_ACCESS_REG_ACK_PASS ==
                         (uint32) HW_GET_FIELD(regVal, 
						         MDIO_USER_GROUP_USER_ACCESS_REG_ACK))
            {
                dataVal = (uint16) HW_GET_FIELD(regVal, 
				                  MDIO_USER_GROUP_USER_ACCESS_REG_DATA);
            }
            else
            {
                /*
                 * Nothing to do. As no way to indicate error to
                 * Transceiver.
                 */
                dataVal = 0U;
            }

            /* Indicate command read/write completion to TRCV */
            Eth_miiIndication(regVal, dataVal); 
#endif
            MDIOUsrIntrClr(gEthDrvObj.baseAddr);
        }
    }

    /* Write the EOI register */
    CPSWCpdmaWriteEoiVector(gEthDrvObj.baseAddr, CPSW_WR_INTR_LINE_MISC);
}

#if  (ETH_ENABLE_RX_INTERRUPT == STD_ON)
/**
 *  @b HwIntRxThres
 *  @n
 *      CPSW Receive Threshold ISR.
 *
 *  \param[in]  void
 *
 *  \retval
 *      void
 */

#if (ETH_ISR_TYPE == ETH_ISR_CAT1 || ETH_ISR_TYPE == ETH_ISR_VOID)
FUNC(void, ETH_CODE_ISR) Eth_RxThreshIrqHdlr_0(void)
#else
ISR(Eth_RxThreshIrqHdlr_0)
#endif
{
    uint32         rxIntFlags   = 0U, threshIntFlags =0U;
    uint32         channelNum = 0U, channelMask =0U;
#if (STD_ON == ETH_DEV_ERROR_DETECT)
    uint32         cp;
#endif  /* #if (STD_ON == ETH_DEV_ERROR_DETECT) */

    /* Read the RX_THRESH interrupt cause from WR_C0_RX_THRESH_STAT */
    threshIntFlags = CPSWChIntrStatus(gEthDrvObj.baseAddr,
                                  CPSW_CH_INTR_RX_THR);
#if (STD_ON == ETH_DEV_ERROR_DETECT)
    if (ETH_STATE_INIT != gEthDrvStatus)
    {
        /*
         * If a crash occurs while interrupts were pending and the core is
         * restored (no system wide reset) we can end up here since the CPSW
         * ISRs are hooked before the driver has a chance to reset the HW.
         */
        while (0U != threshIntFlags)
        {
            channelMask = 0x1U << channelNum;
            /* Clear interrupts for this channel */
            CPSWCpdmaReadRxCP(gEthDrvObj.baseAddr, channelNum, &cp);
            CPSWCpdmaXferCpWrite(gEthDrvObj.baseAddr,
                                     ETH_XFER_MODE_RX, channelNum, cp);
            /* Clear the channel flag for the channel just handled */
            threshIntFlags &= ~channelMask;
            channelNum++;
        }
        (void) Det_ReportError(
            ETH_MODULE_ID,
            ETH_INSTANCE_ID,
            ETH_SID_TX_IRQ_HDLR,
            ETH_E_UNINIT);
    }
    else
#endif
    {
        /*
         * Look for receive threshold interrupts from across all CPDMA Rx
         * Channels.
         */
        rxIntFlags = CPSWChIntrStatus(gEthDrvObj.baseAddr,
                                            CPSW_CH_INTR_RX);
        while (0U != threshIntFlags)
        {
            channelMask = 0x1U << channelNum;

            if ((uint32) 0U != (uint32) (rxIntFlags & channelMask))
            {
                EthRxBuffDescProcess(gEthDrvObj.ctrlIdx, channelNum);
            }

            /* Clear the channel flags for the channel just handled */
            threshIntFlags &= ~channelMask;
            rxIntFlags &= ~channelMask;
            channelNum++;
        }

        /* Write the EOI register for RX_PULSE interrupts if there are none left */
        if (0U == rxIntFlags)
        {
            /* All RX_PULSE interrupts were handled as RX_THRESH interrupts */
            CPSWCpdmaWriteEoiVector(gEthDrvObj.baseAddr, CPSW_WR_INTR_LINE_RX);
        }
    }

    /* Write the EOI register */
    CPSWCpdmaWriteEoiVector(gEthDrvObj.baseAddr, CPSW_WR_INTR_LINE_RX_THR);
}
#endif

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

