/**
* @file icss_switch_emac.c
*
* @brief Contains routines that implement the EMAC interface
*
* \par
* Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/
* \par
*/
/* ========================================================================== */
/*                             Include Files                                  */
/* ========================================================================== */
#include "icss_switch_emac.h"
#include "icss_ioctl.h"
#include "icss_statistics.h"
#include "icss_stormControl.h"
#include <ti/ndk/inc/stkmain.h>
#include <ti/ndk/inc/os/osif.h>
#include "icss_nimu_eth.h"
#include "icss.h"
#include "osdrv_tlk110.h"

/* ========================================================================== */
/*                            Global Variables                                */
/* ========================================================================== */
/** Flag to indicate if the EMAC is already opened.                        */
static Uint32           openFlag = 0;
/** Local copy of the EMAC device instance                                 */
static SwitchDevice      localDev;
#ifdef DEBUG_STATS
RAWETH_STAT raweth_stat;
#endif
/**Variable containing packet*/
EMAC_Pkt pktRx;
/**External variable. Holder for Statistics measured on host side*/
extern hostStatistics hostStats[NUM_PORTS];
/**External variable. Holder for PRU Statistics*/
extern pruStatistics pruStats[NUM_PORTS];

/**
* @internal
* @brief Update our local copy of the statistics
*
* @param pd pointer to EMAC object
*
* @retval none
*
* @pre EMAC peripheral instance must be opened
*/
static void EmacUpdateStats(SwitchDevice *pd);

/**
* @internal
* @brief The function initialize an EMAC_Pkt to receive packets
*
* @param none
*
* @retval none
*/
void PacketInit()
{
	PBM_Handle hPkt;
	hPkt = PBM_alloc(1522);
	/* Couldn't allocate memory, return error */
	if (hPkt == NULL)
		return ;
	PBM_setDataOffset( hPkt, 0 );
	pktRx.AppPrivate   = (Uint32)hPkt;
	pktRx.pDataBuffer  = PBM_getDataBuffer(hPkt);
	pktRx.BufferLen    = PBM_getBufferLen(hPkt);
	pktRx.DataOffset = PBM_getDataOffset(pktRx.AppPrivate);
}

/**
* @internal
* @brief The function free Rx packet buffer
*
* @param none
*
* @retval none
*/
void PacketShutDown()
{
	PBM_free( (PBM_Handle)pktRx.AppPrivate);
}

/**
* @internal
* @brief Update our local copy of the statistics
*
* @param pd pointer to EMAC object
*
* @retval none
*
* @pre EMAC peripheral instance must be opened
*/
static void EmacUpdateStats( SwitchDevice *pd)
{
    int	i;
    ConsolidateStats();
	pd->Stats[0].txUcast += (hostStats[0].txUcast + hostStats[1].txUcast);
	pd->Stats[0].txBcast += (hostStats[0].txBcast + hostStats[1].txBcast);
	pd->Stats[0].txMcast += (hostStats[0].txMcast + hostStats[1].txMcast);
	pd->Stats[0].txGoodFrames += (pd->Stats[0].txUcast + pd->Stats[0].txBcast + pd->Stats[0].txMcast);
	pd->Stats[0].txNetOctets += (hostStats[0].txOctets + hostStats[1].txOctets);
	pd->Stats[0].rxUcast+= (hostStats[0].rxUcast + hostStats[1].rxUcast);
	pd->Stats[0].rxBcast += (hostStats[0].rxBcast + hostStats[1].rxBcast);
	pd->Stats[0].rxMcast += (hostStats[0].rxMcast + hostStats[1].rxMcast);
	pd->Stats[0].rxGoodFrames += (pd->Stats[0].rxUcast + pd->Stats[0].rxBcast + pd->Stats[0].rxMcast);										//NA
	pd->Stats[0].rxNetOctets += (hostStats[0].rxOctets + hostStats[1].rxOctets);
	pd->Stats[0].rxUnknownProtocol += (hostStats[0].rxUnknownProtocol + hostStats[1].rxUnknownProtocol);
	pd->Stats[0].droppedPackets = 0;

    for( i = 0; i < SWITCH_NUM_MAC_PORTS; i++) {

		pd->Stats[i + 1].txUcast += pruStats[i].txUcast;
		pd->Stats[i + 1].txBcast += pruStats[i].txBcast;
		pd->Stats[i + 1].txMcast += pruStats[i].txMcast;
		pd->Stats[i + 1].txGoodFrames += (pruStats[i].txUcast + pruStats[i].txBcast \
				                         + pruStats[i].txMcast);
		pd->Stats[i + 1].txNetOctets += pruStats[i].txOctets;

		pd->Stats[i + 1].rxUcast += pruStats[i].rxUcast;
		pd->Stats[i + 1].rxBcast += pruStats[i].rxBcast;
		pd->Stats[i + 1].rxMcast += pruStats[i].rxMcast;

		pd->Stats[i + 1].rxGoodFrames += (pruStats[i].rxUcast + pruStats[i].rxBcast \
									      + pruStats[i].rxMcast);
		pd->Stats[i + 1].rxNetOctets += pruStats[i].rxOctets;
		pd->Stats[i + 1].rxErrorFrames += (pruStats[i].rxCRCFrames + pruStats[i].rxOverSizedFrames + pruStats[i].rxUnderSizedFrames
											+ pruStats[i].rxMisAlignmentFrames);
		pd->Stats[i+1].rxUnknownProtocol +=hostStats[i].rxUnknownProtocol;
		pd->Stats[i + 1].droppedPackets += (pruStats[i].stormPrevCounter + pruStats[i].droppedPackets);

		pd->Stats[i + 1].lateColl 	+= pruStats[i].lateColl;
		pd->Stats[i + 1].excessColl 	+= pruStats[i].excessColl;
		pd->Stats[i + 1].singleColl 	+= pruStats[i].singleColl;
		pd->Stats[i + 1].multiColl 	+= pruStats[i].multiColl;
		pd->Stats[i + 1].defTx 		+= pruStats[i].defTx;
		pd->Stats[i + 1].macRxError 	+= (pruStats[i].macRxError + pruStats[i].SFDError);

	}


	PurgeStats();
}


/**********************************************************************
 ***************** Switch Configuration Functions *********************
 **********************************************************************/


/**
* @internal
* @brief Configures the MAC port settings.
*
* @param hPort       pointer to port object
*
* @retval none
*
*/
static void ICSSSetMacCfg(SwitchPort* hPort)
{
    SwitchInitConfig*   switchCfg = &localDev.Config;
    Uint32 portNo = 0,macNo = 0;
    SwitchMacConfig*    lpMacConfign = 0;

    portNo = hPort->portNum;
    if (portNo > 0)
    	macNo =  portNo - 1;

    if(portNo > 2)//only GMAC port 0 and port 1 are valid
    {
        /* error condition */
    	info_printf("Error in SetMacCfg\n");
    }
    else
    {
        lpMacConfign = &switchCfg->macInitCfg[macNo];
        localDev.PktMTU = (switchCfg->PktMTU);

        /* Configure the MDIO params, i.e. speed, duplex */

        if(lpMacConfign->mdioModeFlags & MDIO_MODEFLG_NOPHY)
            lpMacConfign->phyMode = NWAY_NOPHY;
        else if(lpMacConfign->mdioModeFlags & MDIO_MODEFLG_AUTONEG)
        {
            /* If this MAC port is marked for internal PHY loopback,
             * configure the MDIO parameters appropriately.
             */
            if(lpMacConfign->mdioModeFlags & MDIO_MODEFLG_LOOPBACK)
                lpMacConfign->phyMode = NWAY_FD100|NWAY_FD10|NWAY_HD100|NWAY_HD10|NWAY_AUTO;
            else
            	lpMacConfign->phyMode = NWAY_AUTO|NWAY_FD100|NWAY_FD10|NWAY_HD100|NWAY_HD10;
            }
        else
        {
            if(lpMacConfign->mdioModeFlags & MDIO_MODEFLG_HD10)
                lpMacConfign->phyMode = NWAY_HD10;
            if(lpMacConfign->mdioModeFlags & MDIO_MODEFLG_FD10)
                lpMacConfign->phyMode |= NWAY_FD10;
            if(lpMacConfign->mdioModeFlags & MDIO_MODEFLG_HD100)
                lpMacConfign->phyMode |= NWAY_HD100;
            if(lpMacConfign->mdioModeFlags & MDIO_MODEFLG_FD100)
                lpMacConfign->phyMode |= NWAY_FD100;
        }
    }

    return;
}

/**
* @internal
* @brief Configures Address lookup engine (ALE) of EMAC.
*
* @param hPort       pointer to port object
*
* @retval none
*
*/
static void ICSSSetAleCfg(void)
{
    SwitchInitConfig*   icssCfg = &localDev.Config;
    /* Local Pointer to Ale Configuration */
    AleConfig*          lpAleConfig = &icssCfg->aleCfg;

	if (lpAleConfig->aleModeFlags & EMAC_CONFIG_ALE_CLRTABLE)
		ICSSALEClearTable();
	if (lpAleConfig->aleModeFlags & EMAC_CONFIG_ALE_AGEOUTNOW)
		ICSSALEAgeOutNow();

    /* Clear out toggle bits*/
    lpAleConfig->aleModeFlags &= ~(EMAC_CONFIG_ALE_CLRTABLE
                                 | EMAC_CONFIG_ALE_AGEOUTNOW);

}
/**
* @internal
* @brief This function configures the Tx flow control abilities of a port.
*
* @param enable              Boolean flag to enable/disable Tx rate control
* @param port                port number on which this is being configured
* @param type                Type of data(Multicast/Broadcast) this applies to.
* @param noOfPkts            Rate to configure
*
* @retval none
*
*/
static void ICSSRateLimitTx(Uint32 enable, Uint32 port,
                                typeOfData type, Uint32 noOfPkts)
{
    if(0u != enable)
    {
        /**CHECK : assumption is ALE_PRESCALE set for 100micros */
        if( (BROADCAST == type) || (MULTICAST == type))
        {
        	setCreditValue(noOfPkts,port);
        	enableStormPrevention(port);
        	initStormPreventionTable();
        }
    }/* end of if enable conditional block */
    else
    {
    	disableStormPrevention(port);
    }

    return;
}

/**
* @internal
* @brief This function configures the MACCONTROL register according to the user
*  			defined PHY speed, duplex link settings
*
* @param gmacObject          GMAC Object whose PHY needs to be updated
* @param instanceNum         MAC port identifier
*
* @retval Always returns SWITCH_SUCCESS
*
*/
Uint32 ICSSUpdatePhystatus(CpgmacObj* gmacObject , Uint32 instanceNum)
{
    SwitchInitConfig*   icssCfg = &localDev.Config;
	Uint32              setPhyMode;
	PHY_DEVICE*         PhyDev =  &gmacObject->PhyDev;

    setPhyMode = icssCfg->macInitCfg[instanceNum].phyMode;

  	if(0u != (setPhyMode & NWAY_NOPHY))
   	{
        /* No Phy condition, always linked */
      	gmacObject->PhyLinked = 1;
      	gmacObject->PhySpeed = 1;
      	gmacObject->PhyDuplex = 1;
      	gmacObject->PhyNum = 0xFFFFFFFF;  /* No Phy */

    }
	else
	{
		gmacObject->PhyLinked = 1;

		if(0u != (setPhyMode & NWAY_LPBK))
		{
			gmacObject->PhyLinked = ICSSMDIOGetLoopback(PhyDev);
		}
		else
		{
			gmacObject->PhyLinked = ICSSMDIOGetLinked(PhyDev);
		}

		if (0u != (gmacObject->PhyLinked) )
		{
			/*  Retreive Duplex and Speed and the Phy Number  */
			if (0u != (setPhyMode & NWAY_LPBK))
			{
				gmacObject->PhyDuplex = 1;
			}
			else
			{
				gmacObject->PhyDuplex = ICSSMDIOGetDuplex(PhyDev);
			}

			gmacObject->PhySpeed  = ICSSMDIOGetSpeed(PhyDev);

			if (gmacObject->PhySpeed != 2)
			{
				gmacObject->PhySpeed = (gmacObject->PhySpeed >> 10);
			}

			gmacObject->PhyNum =  ICSSMDIOGetPhyNum(PhyDev);
	    }
	}

	return (SWITCH_SUCCESS);
}

/**
* @internal
* @brief This function is called to update the local copy of device configuration
*  			with the updated Phy status and parameters
*
* @param devPtr              device pointer to be updated
* @param cmd                 Type of update being received. Currently, only one
*                            supported, i.e. SWITCH_PHY_STATUS_UPDATE
* @param cmdArg              Command argument, pointer to the updated link parameters
* @param param               Pointer to MAC port object to update.
*
* @retval Always returns SWITCH_SUCCESS
*
*/
void ICSSControlCb(SwitchDevice* devPtr,Uint32 cmd,Ptr cmdArg, Ptr param)
{
    static Uint32 linkspeed[2]= {0};
	SwitchPort *hPort = (SwitchPort*)param;
	SwitchMacStatus *macStat;

    switch (cmd)
	{
        case SWITCH_PHY_STATUS_UPDATE:
        {
		    macStat = (SwitchMacStatus*)cmdArg;

			devPtr->Config.linkStatus[hPort->portNum] = macStat->PhyLinked;
			if(0u != (macStat->PhyLinked) )
			{
			    devPtr->Config.linkSpeed[hPort->portNum] =
                        (macStat->PhySpeed == 2) ? 1000000000 :((macStat->PhySpeed)?100000000 : 10000000);
				devPtr->Config.linkMode[hPort->portNum]  = ((macStat->PhyDuplex) ? 2 : 1);

				if(linkspeed[hPort->portNum]!= devPtr->Config.linkSpeed[hPort->portNum])
                {
                    if(devPtr->Config.linkSpeed[hPort->portNum]== 1000000000)
                    {
                        devPtr->Config.macInitCfg[hPort->portNum].macModeFlags |= EMAC_CONFIG_MODEFLG_GIGABIT;
                    }
                    else
                    {
                        devPtr->Config.macInitCfg[hPort->portNum].macModeFlags &= ~EMAC_CONFIG_MODEFLG_GIGABIT;
                    }

                    if(macStat->PhyDuplex == 1)
                        devPtr->Config.macInitCfg[hPort->portNum].macModeFlags |= EMAC_CONFIG_MODEFLG_FULLDUPLEX;
                    else
                        devPtr->Config.macInitCfg[hPort->portNum].macModeFlags &= ~EMAC_CONFIG_MODEFLG_FULLDUPLEX;
                    ICSSSetMacCfg(hPort);
                }

                linkspeed[hPort->portNum]= devPtr->Config.linkSpeed[hPort->portNum];
            }
			else
			{
				if(  (0 == devPtr->Config.linkStatus[0] ) && ( 0 == devPtr->Config.linkStatus[1]) )
				{
				    devPtr->Config.linkSpeed[hPort->portNum] = 100000000;
                    devPtr->Config.linkMode[hPort->portNum]  = 1;
				}
			}

			break;
        }
		default:
        {
			break;
        }
	}
}

/**
* @internal
* @brief This function configures the appropriate registers with updated PHY
*  			settings and notifies a callback function to update the same in our local
*  			configuration copy.
*
* @param gmacObject          GMAC Object whose PHY needs to be updated
* @param instanceNum         MAC port identifier

* @retval none
*
*/
static void ICSSSetPhyMode(CpgmacObj* gmacObject,Uint32 instanceNum)
{
    SwitchInitConfig*   switchCfg = &localDev.Config;
    Uint32 setPhyMode,PhyMode;
    SwitchMacConfig * macInitCfg =  &switchCfg->macInitCfg[instanceNum];
    SwitchMacStatus macStat;
    unsigned char disableAutoMDIX = 0;

    setPhyMode = macInitCfg->phyMode;
    PhyMode = 0;

    if(0u != (setPhyMode & NWAY_AUTO))
    {
	    PhyMode |= NWAY_AUTO;
    }
    if(0u!= (setPhyMode & NWAY_FD10))
    {
	    PhyMode |= NWAY_FD10;
	    disableAutoMDIX = 1;
    }
    if(0u != (setPhyMode & NWAY_FD100))
    {
	    PhyMode |= NWAY_FD100;
	    disableAutoMDIX = 1;
    }
    if(0u != (setPhyMode & NWAY_HD10 ))
    {
	    PhyMode |= NWAY_HD10;
	    disableAutoMDIX = 1;
    }
    if(0u != (setPhyMode & NWAY_HD100))
    {
	    PhyMode |= NWAY_HD100;
	    disableAutoMDIX = 1;
    }

    if(0u !=(setPhyMode & NWAY_LPBK ))
    {
	    PhyMode |= NWAY_LPBK;
    }
    if(0u != (setPhyMode & NWAY_AUTOMDIX ))
    {
	    PhyMode |= NWAY_AUTOMDIX;
    }

    ICSSMDIOSetPhyMode(&gmacObject->PhyDev, PhyMode);

    ICSSUpdatePhystatus(gmacObject,instanceNum);
    //fix for TLK110 MDIX issue
    if(disableAutoMDIX && (0u == (setPhyMode & NWAY_AUTO)))
    {
    	if(mac_configuration.tlk110Flag ==1)
    		tlkDisableAutoMDIX(gmacObject->PhyDev.miibase,mac_configuration.phyAddr[instanceNum]);
    	else
    		tlkEnableAutoMDIX(gmacObject->PhyDev.miibase,mac_configuration.phyAddr[instanceNum]);
    }
    else
    	tlkEnableAutoMDIX(gmacObject->PhyDev.miibase,mac_configuration.phyAddr[instanceNum]);

  	/**CHECK: notify status change thru callback here */
  	macStat.PhyDuplex = gmacObject->PhyDuplex;
  	macStat.PhyLinked =  gmacObject->PhyLinked;
  	macStat.PhyNum = gmacObject->PhyNum;
  	macStat.PhySpeed = gmacObject->PhySpeed;

  	ICSSControlCb(&localDev, SWITCH_PHY_STATUS_UPDATE, &macStat, &localDev.switchPort[instanceNum]);
}


/**
* @internal
* @brief This function opens and sets up a MAC port for communication.
*
* @param hPort               Handle to the Port object to setup
*
* @retval none
*
*/
static void ICSSMacOpen(SwitchPort *hPort)
{
    SwitchInitConfig*	switchCfg = &localDev.Config;
    SwitchConfig*		icssCfg = &switchCfg->switchCfg;
    Uint32 				instId = hPort->portNum -1 ;//PRU-ICSS:port0-host,port1&2-MII
    SwitchMacConfig*	macInitCfg = &switchCfg->macInitCfg[instId];
    CpgmacObj*			gmacObject = &localDev.cpgmacMac[instId];

    ICSSSetMacCfg(hPort);

    /*Kick Start MDIO , auto-negn */
    memset(&gmacObject->PhyDev, 0, sizeof(PHY_DEVICE));

    ICSSMDIOInit(&gmacObject->PhyDev, ICSS_MDIO_BASEADDR, instId,macInitCfg->PhyMask,
              macInitCfg->MLinkMask, 0,macInitCfg->phyNum, 0,
              icssCfg->mdioBusFrequency, icssCfg->mdioClockFrequency,0);
	if(mac_configuration.tlk110Flag ==1)
		tlkNLPFix(ICSS_MDIO_BASEADDR,macInitCfg->phyNum);

    /* set the Phy to given mode based on Config Params */
    ICSSSetPhyMode(gmacObject,instId);
}

/**
* @internal
* @brief This function opens and sets up the port for communication.
*
* @param hPort               Handle to the Port object to setup
*
* @retval 	SWITCH_SUCCESS      port open successful
*			error value         port open failed
*
*/
static int ICSSPortOpen(SwitchPort *hPort)
{
    if(hPort->portNum > 0)
    {
    	 /* Open physical ports */
    	ICSSMacOpen(hPort);
    }

    return (SWITCH_SUCCESS);
}

/**
* @internal
* @brief  Opens the EMAC peripheral at the given physical index and initializes
*     		it to an embryonic state.
* @details	   The calling application must supply a operating configuration that
*     		includes a callback function table. Data from this config structure is
*     		copied into the device's internal instance structure so the structure
*     		may be discarded after EMACOpen() returns. In order to change an item
*     		in the configuration, the EMAC device must be closed and then
*     		re-opened with the new configuration.
*
*     			The application layer may pass in an hApplication callback handle,
*     		that will be supplied by the EMAC device when making calls to the
*     		application callback functions.
*
*     		An EMAC device handle is written to phEMAC. This handle must be saved
*     		by the caller and then passed to other EMAC device functions.
*
*     		The default receive filter prevents normal packets from being received
*     		until the receive filter is specified by calling EMAC_receiveFilter().
*
*     		A device reset is achieved by calling EMACClose() followed by EMACOpen().
*
*     		The function returns zero on success, or an error code on failure.
*
*     		Possible error codes include:
*       		SWITCH_ERR_DEV_ALREADY_OPEN   - The device is already open
*       		SWITCH_INVALID_PARAM   - A calling parameter is invalid
*
* @param physicalIndex   physical index
* @param hApplication    application handle
* @param pEMACConfig     EMAC's configuration structure
* @param phEMAC          handle to the EMAC device
*
* @retval 	Success (0)
*			SWITCH_INVALID_PARAM   - A calling parameter is invalid
*			SWITCH_ERR_DEV_ALREADY_OPEN   - The device is already open
*
*/
Uint32 EMACOpen(int physicalIndex, Handle hApplication,
                 SwitchInitConfig *pEMACConfig, Handle *phEMAC)
{
    int              i, index;
	EMAC_AddrConfig  *tmp;

    /* We only handle a single instance */
    if (physicalIndex != 1)
        return (SWITCH_INVALID_PARAM);

    /* If the device is already open, return an error */
    if (openFlag)
        return( SWITCH_ERR_DEV_ALREADY_OPEN );

    /*
     *  Validate the supplied configuration structure
     */
    if (!pEMACConfig || !phEMAC)
        return (SWITCH_INVALID_PARAM);

    /* MAC address must be supplied and not a multicast address */
	for (index=0; (Uint8)index<(pEMACConfig->TotalNumOfMacAddrs); index++)
	{
		tmp = pEMACConfig->MacAddr[index];
    	if(tmp->Addr[0] & 1 )
        	return( SWITCH_INVALID_PARAM );
	    for (i = 0; (i < 6) && (!(tmp->Addr[i])); i++)
    	if (i == 6)
        	return (SWITCH_INVALID_PARAM);
	}

    /* Call back functions must be supplied */
    if (!pEMACConfig->pfcbRxPacket)

        return (SWITCH_INVALID_PARAM);

    /* Default everything in our instance structure to zero */
    memset(&localDev, 0, sizeof(SwitchDevice));

    /* Set the hApplication and RxFilter */
    localDev.hApplication = hApplication;

    /* Setup the new configuration */
    localDev.Config = *pEMACConfig;

    /* Open all ports */
    for(i=0;i<SWITCH_NUM_PORTS; i++)
    {
	    localDev.switchPort[i].portNum = i;
	    localDev.switchPort[i].chNum = i;
	    {
	    	ICSSPortOpen(&localDev.switchPort[i]);
	    }

    }/* end of for loop over PORTS */

#ifdef USE_RX_TASK
    if(CreateRXtask())
    	return (SWITCH_INTERNAL_FAILURE);
#endif
    if(mac_configuration.tlk110Flag ==1)
    	tlkMDIXFixinit(ICSS_MDIO_BASEADDR);
    ICSSALEInit();

    ICSSSetAleCfg();

    /* Activate the ALE ageout timer here */
    localDev.Config.aleTimerActive = 1;

	/* Validate the device handle */
    localDev.DevMagic = EMAC_DEVMAGIC;

    /* Set the open flag */
    openFlag = 1;

    /* Give a handle back to the caller */
    *phEMAC = &localDev;

    /* Return Success */
    return( SWITCH_SUCCESS );
}

/**
* @internal
* @brief Closed the EMAC peripheral indicated by the supplied instance handle.
*     		When called, the EMAC device will shutdown both send and receive
*     		operations, and free all pending transmit and receive packets.
* @details    The function returns zero on success, or an error code on failure.
*     			Possible error code include:
*         		SWITCH_INVALID_PARAM   - A calling parameter is invalid
*
* @param hEMAC   handle to opened the EMAC device
*
* @retval 	Success (0)
*			SWITCH_INVALID_PARAM   - A calling parameter is invalid
* @pre      EMACOpen function must be called before calling this API.
*
*/
Uint32 EMACClose(Handle hEMAC)
{
	SwitchDevice    *pd = (SwitchDevice *)hEMAC;

    /* Validate our handle */
    if (!pd || pd->DevMagic != EMAC_DEVMAGIC)
        return (SWITCH_INVALID_PARAM);

    /* De-Activate the ALE ageout timer here */
    pd->Config.aleTimerActive = 0;

    /* Invalidate the EMAC handle */
    pd->DevMagic = 0;

    /* Clear the open flag */
    openFlag = 0;

#ifdef USE_RX_TASK
	DeleteRXtask();
#endif
    /*Creates the task for TLK 110 Fix*/
    if(mac_configuration.tlk110Flag ==1)
    	tlkMDIXFixDeinit();
	PacketShutDown();

    /* Exit with interrupts still disabled in the wrapper */
    return (SWITCH_SUCCESS);
}

/**
* @internal
* @brief 	Called to get the current device statistics. The statistics structure
*     		contains a collection of event counts for various packet sent and
*     		receive properties. Reading the statistics also clears the current
*     		statistic counters, so the values read represent a delta from the last
*     		call.
* @details  The statistics information is copied into the structure pointed to
*     			by the pStatistics argument.
*     			The function returns zero on success, or an error code on failure.
*     			Possible error code include:
*      			SWITCH_INVALID_PARAM   - A calling parameter is invalid
*
* @param  hEMAC       handle to the opened EMAC device
* @param  pStatistics Get the device statistics
*
* @retval 	Success (0)
*			SWITCH_INVALID_PARAM   - A calling parameter is invalid
* @pre      EMAC peripheral instance must be opened before calling this API
*
*/
Uint32 EMACGetStatistics(Handle hEMAC, IcssSwitchStatistics *pStatistics,Uint8 portNo)
{
    SwitchDevice  *pd = (SwitchDevice *)hEMAC;

    /* Validate our handle */
    if(!pd || pd->DevMagic != EMAC_DEVMAGIC || !pStatistics)
        return (SWITCH_INVALID_PARAM);

    if(portNo > SWITCH_NUM_MAC_PORTS)
    	return (SWITCH_INVALID_PORT);

    /* Update the stats */
    EmacUpdateStats(pd);

    /* Copy the updated stats to the application */
   *pStatistics = pd->Stats[portNo];

    return (0);
}

/**
* @internal
* @brief 	Called to clear the current device statistics for a particular port
*
* @param  hEMAC       handle to the opened EMAC device
* @param  portNo      port to clear statistics
*
* @retval 	Success (0)
*			SWITCH_INVALID_PARAM   - A calling parameter is invalid
* @pre      EMAC peripheral instance must be opened before calling this API
*
*/
Uint32 EMACClearStatistics(Handle hEMAC,Uint8 portNo)
{
	SwitchDevice  *pd = (SwitchDevice *)hEMAC;

	/* Validate our handle */
	if(!pd || pd->DevMagic != EMAC_DEVMAGIC)
		return (SWITCH_INVALID_PARAM);

	if(portNo > SWITCH_NUM_MAC_PORTS)
			return (SWITCH_INVALID_PORT);

	EmacUpdateStats(pd);

	memset(&pd->Stats[portNo],0,sizeof(IcssSwitchStatistics));

	return (0);
}


/**
* @internal
* @brief 	This function is called to perform various get/set operations on the
*     		EMAC ALE table, statistics counters etc.
*
* @param  hEMAC       handle to the opened EMAC device
* @param  pBuf        pointer to the buffer that holds the configuration
                      command and any data required.
* @param  size        size of the buffer
*
* @retval 	Success (0)
*			SWITCH_INVALID_PARAM   - A calling parameter is invalid
* @pre      EMAC peripheral instance must be opened before calling this API
*
*/
Uint32 EMACSetConfig(Handle hEMAC, void* pBuf, Uint32 size)
{
    SwitchDevice*       pd = (SwitchDevice *)hEMAC;
    SwitchInitConfig*   cfgPtr  = &pd->Config;
    SwitchConsCmd*      swConsCommand = NULL;
    Int32               ret_val = SWITCH_SUCCESS;
    Uint32 port = 0;

    /* Validate our handle */
    if (!pd || pd->DevMagic != EMAC_DEVMAGIC || !size || !pBuf)
        return (SWITCH_INVALID_PARAM);

    swConsCommand = (SwitchConsCmd *)pBuf;

    switch(swConsCommand->opcode)
    {
        case ICSS_IOCTL_RATELIMIT:
        {
            PORT_CMD *lpPortCmd = &swConsCommand->CmdType.PortCmd;

            if(lpPortCmd->EnDis < 2)
            {
				if( (lpPortCmd->port < 3) && (lpPortCmd->ml < 2) &&
                        (lpPortCmd->value < 2) && (lpPortCmd->bl < 256))
				{
					ICSSRateLimitTx( lpPortCmd->EnDis, lpPortCmd->port,
												((typeOfData)lpPortCmd->ml), lpPortCmd->bl);
				}
				else
				{
					info_printf("RATELIMIT:Invalid Arguments\n");
				}
			}
			else
			{
				info_printf("RATELIMIT:Invalid Arguments\n");
			}
			break;
		}
        case ICSS_IOCTL_SET_PORT_CONFIG:
        {
            PORT_CMD *lpPortCmd = &swConsCommand->CmdType.PortCmd;
            Uint32 fault = 0u;

            if( (lpPortCmd->port < 2) && (lpPortCmd->value < 1001) && (lpPortCmd->EnDis < 2))
            {
                switch(lpPortCmd->value)
                {
                    case 0:
                    {
                        if(pd->cpgmacMac[lpPortCmd->port].intPhy == 0)
                        {
                            cfgPtr->macInitCfg[lpPortCmd->port].phyMode = NWAY_AUTO|NWAY_FD1000|NWAY_FD100|
                                                                          NWAY_FD10|NWAY_HD100|NWAY_HD10;
                        }
                        else
                        {
                            cfgPtr->macInitCfg[lpPortCmd->port].phyMode = NWAY_FD100|NWAY_FD10|NWAY_HD100|NWAY_HD10|NWAY_AUTO;
                        }

                        break;
                    }
                    case 10:
                    {
                        if(lpPortCmd->EnDis ==1)
                        {
                            cfgPtr->macInitCfg[lpPortCmd->port].phyMode = NWAY_FD10;
                        }
                        else
                        {
                            if(lpPortCmd->EnDis ==0)
                            {
                                cfgPtr->macInitCfg[lpPortCmd->port].phyMode = NWAY_HD10;
                            }
                            else
                            {
                                cfgPtr->macInitCfg[lpPortCmd->port].phyMode = NWAY_HD10|NWAY_FD10;
                            }
                        }

                        break;
                    }
                    case 100:
                    {
                        if(lpPortCmd->EnDis ==1)
                        {
                            cfgPtr->macInitCfg[lpPortCmd->port].phyMode = NWAY_FD100;
                        }
                        else
                        {
                            if(lpPortCmd->EnDis ==0)
                            {
                                cfgPtr->macInitCfg[lpPortCmd->port].phyMode = NWAY_HD100;
                            }
                            else
                            {
                                cfgPtr->macInitCfg[lpPortCmd->port].phyMode = NWAY_HD100|NWAY_FD100;
                            }
                         }

                         break;
                    }
				    default:
                    {
				        break;
                    }
                }/* end of switch construct  */

                if(!fault)
                {
                	ICSSSetPhyMode(&pd->cpgmacMac[lpPortCmd->port],lpPortCmd->port);
                }
            }
            else
            {
            	info_printf("SET_PORT_CONFIG:Invalid Arguments\n");
            }

            break;
        }
        case ICSS_IOCTL_GET_PORT_CONFIG:

        {
            PORT_CMD *lpPortCmd = &swConsCommand->CmdType.PortCmd;

            if(lpPortCmd->port == 0)		/*HOST Port*/
            {
                port  = lpPortCmd->port;
                lpPortCmd->ml    = port;
                lpPortCmd->port  = 1;
                lpPortCmd->value = pd->Config.linkSpeed[0];
                lpPortCmd->EnDis = 1;
                SocMACAddrGet(port, lpPortCmd->add);
                lpPortCmd->phyMode = 0;			// No AutoNeg status for Host port
            }
            else if(lpPortCmd->port < 3)
            {
                port  = lpPortCmd->port;
                lpPortCmd->ml    = port - 1;
                lpPortCmd->port  = pd->cpgmacMac[port - 1].PhyLinked;
                lpPortCmd->value = pd->Config.linkSpeed[port - 1];
                lpPortCmd->EnDis = pd->Config.linkMode[port - 1];
                SocMACAddrGet(port, lpPortCmd->add);
                lpPortCmd->phyMode = cfgPtr->macInitCfg[port - 1].phyMode;
            }
            else
            {
                info_printf("GET_PORT_CONFIG:Invalid Arguments\n");
            }

            break;

        }
        default:
        {
            break;
        }
    }/* end of switch construct */

    swConsCommand->ret_type = ret_val;

    return ret_val;

}

/**
* @internal
* @brief 	Sends a Ethernet data packet out the EMAC device. On a non-error return,
*     		the EMAC device takes ownership of the packet. The packet is returned
*     		to the application's free pool once it has been transmitted.
*
* @param  hEMAC       handle to the opened EMAC device
* @param  pPkt        EMAC packet structure
*
* @retval 	Success (0)
*			SWITCH_INVALID_PARAM   - A calling parameter is invalid
*			SWITCH_ERR_BADPACKET - The packet structure is invalid
* @pre      EMAC peripheral instance must be opened and get a packet
*     		 buffer from private queue
*
*/
Uint32 EMACSendPacket(Handle hEMAC, EMAC_Pkt *pPkt)
{
    SwitchDevice    *pd = (SwitchDevice *)hEMAC;
    Uint32          fragcnt,pktlen;
    EMAC_Pkt        *pPktLast;
    Uint32 txPort;

    /* Validate our handle */
    if (!pd || pd->DevMagic != EMAC_DEVMAGIC || !pPkt)
        return (SWITCH_INVALID_PARAM);

    /* Do some packet validation */
    if (!(pPkt->Flags & EMAC_PKT_FLAGS_SOP))
        return (SWITCH_ERR_BADPACKET);


    if (pPkt->PktLength < 14 || pPkt->PktLength > pd->PktMTU)
        return (SWITCH_ERR_BADPACKET);

    /* Count the number of frags in this packet */
    fragcnt = 1;
    pktlen  = pPkt->PktLength;

    pPktLast = pPkt;
    while (!(pPktLast->Flags & EMAC_PKT_FLAGS_EOP))
    {
        if (!pPktLast->pNext)
            return (SWITCH_INVALID_PARAM);
        pktlen -= pPktLast->ValidLen;
        pPktLast = pPktLast->pNext;
        fragcnt++;
        info_printf("FW fragmented packet\n\r");
        /* At this point we can't have another SOP */
        if (pPktLast->Flags & EMAC_PKT_FLAGS_SOP)
            return (SWITCH_INVALID_PARAM);
    }

    /* Make sure PktLength and ValidLen agree */
    if (pktlen != pPktLast->ValidLen)
        return (SWITCH_ERR_BADPACKET);

    /* The frag count must be correct */
    if (fragcnt != pPkt->PktFrags)
        return (SWITCH_ERR_BADPACKET);

    /* Now pad for 60 byte min size. We only pad the last fragment */
    if (pPkt->PktLength < 60)
    {
        pktlen = 60 - pPkt->PktLength;
        pPkt->PktLength = 60;
        pPktLast->ValidLen += pktlen;
    }

	txPort = PORT0;
    int ret = TxPacket ((const Uint8 *)(pPkt->pDataBuffer + pPkt->DataOffset), txPort, QUEPRIO4, pPkt->PktLength);
	if(ret != 0)
	{
#ifdef DEBUG_STATS
		raweth_stat.retryTxCnt++; // error in send!
#endif
		ret = TxPacket ((const Uint8 *)(pPkt->pDataBuffer + pPkt->DataOffset), txPort, QUEPRIO4, pPkt->PktLength);
		if(ret != 0)
    	{
#ifdef DEBUG_STATS
    		raweth_stat.notGoCnt++;
#endif
			return (SWITCH_ERR_TX_OUT_OF_BD);			// indicate packet didn't get send to stack
		}
	}
#ifdef DEBUG_STATS
 	raweth_stat.ethTxCnt++;
#endif
    return (0);
}

/**
* @internal
* @brief 	This function should be called every time there is an EMAC device Rx
*     		interrupt to process the packet
* @details  Note that the application has the responsibility for mapping the
*     		physical device index to the correct EMAC_serviceCheck() function. If
*     		more than one EMAC device is on the same interrupt, the function must be
*     		called for each device.
*
* @param  hEMAC       handle to the opened EMAC device
*
* @retval 	Success (0)
*			SWITCH_INVALID_PARAM   - A calling parameter is invalid
*			SWITCH_ERR_MACFATAL  - Fatal error in the MAC - Call EMACClose()
* @pre     EMACOpen function must be called before calling this API.
*
*/
Uint32 EMACRxServiceCheck(Handle hEMAC,Int32 prioQueue)
{
	SwitchDevice    *pd = (SwitchDevice *)hEMAC;
    EMAC_Pkt    *pPkt;
    Int32		packetLength;	// for PRU-ICSS switch
    Uint32      tmp;
    Int32		morePkts;
    Int32		port;

	/* Validate our handle */
	if (!pd || pd->DevMagic != EMAC_DEVMAGIC)
		return (SWITCH_INVALID_PARAM);
#ifdef DEBUG_STATS
	raweth_stat.cslRxCnt++;
#endif
    for (tmp = 1; tmp; )
    {
		pPkt = &pktRx;

		packetLength = RxPktGet((Uint32)(pPkt->pDataBuffer + pPkt->DataOffset),prioQueue, &port, &morePkts);

		if (pPkt && (packetLength > 0))		// process packets with valid length only, MAXMTU checked in driver
		{
			/* Fill in the necessary packet header fields */
			pPkt->ValidLen = pPkt->PktLength = packetLength & 0xFFFF;
			pPkt->PktChannel = port;
			pPkt->PktFrags = 1;

			/* Pass the packet to the application */
			(*localDev.Config.pfcbRxPacket)
										   (pd->hApplication,pPkt);
		} else
		{
			return SWITCH_ERR_RX_BUFFER_ALLOC_FAIL;
		}

		if(morePkts == 0)
			tmp = 0;			// exit if there are no more packets

    }

    return(0);

}
/**
* @internal
* @brief 	Function to Update the PHY configuration
*
* @param  phyConfig  	PHY_CONFIG_AUTONEG
*                		PHY_CONFIG_100FD
*                		PHY_CONFIG_10FD
*                		PHY_CONFIG_100HD
*                		PHY_CONFIG_10HD
* @param   portNum 		HWPORT0(0)
*                 		HWPORT1(1)
*
* @retval 	none
*
*/
void EMACUpdatePhyConfig(Uint8 phyConfig,Uint8 portNum)
{
	SwitchMacConfig*	switchMacObject = &(localDev.Config.macInitCfg[portNum]);
	CpgmacObj*			gmacObj = &(localDev.cpgmacMac[portNum]);
     switch(phyConfig)
     {
         case PHY_CONFIG_AUTONEG:
         {
        	switchMacObject->phyMode = NWAY_FD100|NWAY_FD10|NWAY_HD100|NWAY_HD10|NWAY_AUTO;
            break;
         }
         case PHY_CONFIG_100FD:
         {
        	 switchMacObject->phyMode = NWAY_FD100;
             break;
         }
         case PHY_CONFIG_10FD:
         {
        	 switchMacObject->phyMode = NWAY_FD10;
              break;
         }
         case PHY_CONFIG_100HD:
         {
        	 switchMacObject->phyMode = NWAY_HD100;
              break;
         }
         case PHY_CONFIG_10HD:
         {
        	 switchMacObject->phyMode = NWAY_HD10;
              break;
         }
		 default:
         {
        	 switchMacObject->phyMode = NWAY_FD100|NWAY_FD10|NWAY_HD100|NWAY_HD10|NWAY_AUTO;
		     break;
         }
     }
	 ICSSSetPhyMode(gmacObj,portNum);
}

