/**
* @file icss_ethdriver.c
*
* @brief Contains the EMAC interface for enabling/disabling and managing interrupts
*
* @details  Main file which contains the interface for enabling/disabling and managing interrupts
 * through the EMAC interface. As of now only one interrupt..the main RX interrupt using four priority
 * queues is managed through this interface
*
* \par
* Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/
* \par
*/

/* ========================================================================== */
/*                             Include Files                                  */
/* ========================================================================== */
#include <mdio.h>
#include "icss_switch_emac.h"
#include "icss_nimu_eth.h"
#include "icss.h"
#include "icss_ethdriver.h"
#include "icss_stormControl.h"

#include <ti/sysbios/interfaces/IHwi.h>
#include <ti/sysbios/family/arm/a8/intcps/Hwi.h>
/* ========================================================================== */
/*                            Global Variables                                */
/* ========================================================================== */

Hwi_Handle HwiRxInt;
/** @brief Handle to our EMAC instance */
Handle 	        hEMAC = 0;
/** @brief Statistics to account for packets dropped because of memory squeeze */
Uint32         memory_squeeze_error = 0;
/** @brief Link change call back routine */
link_change_notification_t link_change_notification_routine = NULL;
/** @brief Switch Initial Configuration structure */
SwitchInitConfig            switchCfg = {0};
/** @brief Switch statistics structures */
extern hostStatistics hostStats[NUM_PORTS];

/* ========================================================================== */
/*                           Macros & Typedefs                                */
/* ========================================================================== */
/**
* @def disableInterrupts()
*      Macro to disable interrupts. Reuse compiler(-specific) intrinsics
*/
#define disableInterrupts() _disable_interrupts()
/**
* @def restoreInterrupts(key)
*      Macro to restore all interrupts. Reuse compiler(-specific) intrinsics
*/
#define restoreInterrupts(key) _restore_interrupts(key)
/**
* @def LINK_INT_CLR_REG
*      Macro to clear Link Interrupt
*/
#define LINK_INT_CLR_REG  0x4a320284		// todo: get from standard PRU headers

/* ========================================================================== */
/*                            Local Variables                                */
/* ========================================================================== */
/** @brief Handle to Device Info */
static PDINFO   *pPDI = 0;
/** @brief CSL returned error due to software */
static Uint32   csl_errors = 0;
/** @brief EMAC fatal error */
static Uint32   emac_fatal_error = 0;
/** @brief Maximum lenght of packet theat can be transmitted */
static Uint32   PktMTU;
/** @brief EMAC packet place holder to handle tx packets */
static EMAC_Pkt    send_Pkt;
/** @brief Duplex settings for both ports */
volatile Uint8 duplex[NUM_PORTS] = {0};
/** @brief Speed settings for both ports */
volatile Uint8 speed[NUM_PORTS] = {0};
/** @brief Link status for both PHYS */
static Uint8 linkUp[SWITCH_NUM_MAC_PORTS];
/** @brief Callback to be registered for Port0 Link Status change */
static icssCallBack_t port0ISRCall;
/** @brief Callback to be registered for Port1 Link Status change */
static icssCallBack_t port1ISRCall;
/** @brief Callback to be registered to process protocol specific packets */
static icssCallBack_t periodicProtTask;
/** @brief User specific data HW Port 0 Link ISR*/
static void *port0ISRUser;
/** @brief User specific data HW Port 1 link ISR */
static void *port1ISRUser;



/* ========================================================================== */
/*                          Function Definitions                              */
/* ========================================================================== */
/**
* @internal
* @brief Registering Interrupts and Enabling global interrupts
*
* @param none
*
* @retval none
*/
static void InterruptInit(void);
/**
* @internal
* @brief De-registering the interrupts and disabling global interrupts
*
* @param none
*
* @retval none
*/
static void InterruptEnd(void);
/**
* @internal
* @brief Clears Link interrupt for Port0
*
* @param none
*
* @retval none
*/
inline void ClearLink0ISR(void)
{
	HWREG(LINK_INT_CLR_REG) = 0x200;
}
/**
* @internal
* @brief Clears Link interrupt for Port1
*
* @param none
*
* @retval none
*/
inline void ClearLink1ISR(void)
{
	HWREG(LINK_INT_CLR_REG) = 0x200000;
}

/**********************************************************************
 *************************** EMAC Functions ***************************
 **********************************************************************/
/**
* @internal
* @brief This function disables the EMAC interrupts
*
* @param none
*
* @retval none
*/
void DisableEMACInterrupts()
{
    Uint32 key;  
    key = disableInterrupts();      

	IntSetup intSetup;

	Hwi_disableInterrupt(ICSS_RX_PULSE_INTNUM);

    intSetup.intVectId = ICSS_LINK0_STAT_INTNUM;
	Interrupt_disable(&intSetup);

	intSetup.intVectId = ICSS_LINK1_STAT_INTNUM;
	Interrupt_disable(&intSetup);
    restoreInterrupts(key);
}

/**
* @internal
* @brief This function enables the EMAC interrupts
*
* @param none
*
* @retval none
*/
static void EnableEMACInterrupts()
{
    Uint32 key = 0;
            
    key = disableInterrupts();

    IntSetup intSetup;
    
    Hwi_enableInterrupt(ICSS_RX_PULSE_INTNUM);

  	intSetup.intVectId = ICSS_LINK0_STAT_INTNUM;
    Interrupt_enable(&intSetup);

	intSetup.intVectId = ICSS_LINK1_STAT_INTNUM;
	Interrupt_enable(&intSetup);

	restoreInterrupts(key);
}

/**
* @internal
* @brief Function called by CSL to pass the received packet to the driver
*
* @param hApplication  Driver's handle
* @param cslPkt  EMAC_Pkt type packet passed by CSL
*
* @retval EMAC_Pkt pointer of a packet with buffer to replenish CSL
*/
static EMAC_Pkt *RxPacket(Handle hApplication, EMAC_Pkt *cslPkt)
{
	PBM_Handle  hPkt;

	if( (Uint32)hApplication != ICSS_HANDLE_VALUE )
    {
	    return NULL;
    }

	hPkt = PBM_alloc(PktMTU + 4 + PKT_PREPAD);
	if (hPkt)
	{
        /* Remove the 4 byte header from the packet. Not sure as to why the EMAC
         * is adding an extra 4bytes to the header. This is causing stack to
         * fail checksum validations on IPv6 packets. So, remove the extra 4 bytes
         * tagged by the EMAC.
         */
		PBM_setValidLen((PBM_Handle) cslPkt->AppPrivate, (cslPkt->ValidLen));
	    PBMQ_enq( &pPDI->PBMQ_rx, (PBM_Handle) cslPkt->AppPrivate );

        /* Notify NDK stack of pending Rx Ethernet packet */
		STKEVENT_signal( pPDI->hEvent, STKEVENT_ETHERNET, 1 );

        PBM_setDataOffset( hPkt, PKT_PREPAD );
		cslPkt->AppPrivate  = (Uint32)hPkt;
		cslPkt->pDataBuffer = PBM_getDataBuffer(hPkt);
		cslPkt->BufferLen   = PBM_getBufferLen(hPkt);
		cslPkt->DataOffset = PBM_getDataOffset(hPkt);

		return( cslPkt );
	}

	/* Increment the statistics to account for packets dropped because
	 * of memory squeeze 
     */
	memory_squeeze_error++;
	return cslPkt;
}

/**
* @internal
* @brief Populate the configuration structures with the switch's default initialization parameters
*
* @param none
*
* @retval none
*/
static void SetupEmacInitConfig(void)
{
    SwitchInitConfig    *iCfg = NULL;
    Int32               i, j;
	EMAC_AddrConfig     *addrCfg;

    /* Allocate Switch configuration structure */
    mmZeroInit(&switchCfg,sizeof(SwitchInitConfig));

    iCfg = &switchCfg;

    /* Program default MAC SL config */
    for (i=0; i < SWITCH_NUM_MAC_PORTS; i++)
    {

    	iCfg->macInitCfg[i].PhyMask = 1<<mac_configuration.phyAddr[i];
    	iCfg->macInitCfg[i].phyNum = mac_configuration.phyAddr[i];
	    if(0 == i)
	    {
			iCfg->macInitCfg[i].MLinkMask = 0x0;
	    }
	    else
	    {
			iCfg->macInitCfg[i].MLinkMask = 0x1;
	    }
	    /* end of else block covering settings for port 2 */
        iCfg->macInitCfg[i].macModeFlags |=  EMAC_CONFIG_MODEFLG_FULLDUPLEX ;
        iCfg->macInitCfg[i].mdioModeFlags |= MDIO_MODEFLG_AUTONEG;
    }

    /* Program Default ALE Configuration */
    iCfg->aleCfg.aleModeFlags |= EMAC_CONFIG_ALE_ENABLE
                                 | EMAC_CONFIG_ALE_CLRTABLE;

    /* Ale Port Config */
    for(i=0; i < SWITCH_NUM_PORTS; i++)
    {
        iCfg->alePortCfg[i].bcastLimit = SWITCH_DEFAULT_BCASTLIMIT;
        iCfg->alePortCfg[i].mcastLimit = SWITCH_DEFAULT_MCASTLIMIT;
    }

    iCfg->switchCfg.mdioBusFrequency = SWITCH_DEFAULT_MDIOBUSFREQ;
    iCfg->switchCfg.mdioClockFrequency = SWITCH_DEFAULT_MDIOCLOCKFREQ;

	/* We count in 100ms Ticks */
	switchCfg.aleTicks = (ALE_AGE_OUT_TIME / 100);
	switchCfg.aleTimerActive        = 0;

    /* Add 4 bytes for VLAN header if configuring for VLANs */
    switchCfg.PktMTU                = 1518; //rxcrc is present

    switchCfg.pfcbRxPacket           = &RxPacket;
    switchCfg.TotalNumOfMacAddrs     = 1;

	/* Allocate mem for and configure MAC address */
    switchCfg.MacAddr = (EMAC_AddrConfig **)
     		       mmAlloc(switchCfg.TotalNumOfMacAddrs * sizeof(EMAC_AddrConfig *));

	for ( j=0; j<switchCfg.TotalNumOfMacAddrs; j++ )
    {
		switchCfg.MacAddr[j] = (EMAC_AddrConfig *)mmAlloc(sizeof(EMAC_AddrConfig));

		SocMACAddrGet(INTERFACE_MAC, &(switchCfg.MacAddr[j]->Addr[0]));

		info_printf("EDRV: MAC: %02x-%02x-%02x-%02x-%02x-%02x\n",
				switchCfg.MacAddr[j]->Addr[0], switchCfg.MacAddr[j]->Addr[1], switchCfg.MacAddr[j]->Addr[2],
				switchCfg.MacAddr[j]->Addr[3], switchCfg.MacAddr[j]->Addr[4], switchCfg.MacAddr[j]->Addr[5]);
	}

	addrCfg = switchCfg.MacAddr[0];
	/* Copy the MAC Address into the PDI Structure */
 	mmCopy ((void *)&pPDI->bMacAddr[0], (void *)&addrCfg->Addr[0], 6);


    return;
}

/**
* @internal
* @brief Initializes the device MAC address to use.Not used Currently
*
* @param none
*
* @retval Whether mac address has been initialized 1 - true 0 - false
*/
uint HwPktInit()
{
	/* Initialize our MAC address */

	    return (1);

}
/**
* @internal
* @brief none
*
* @param none
*
* @retval none
*
*/
void HwPktShutdown()
{
}

/**
* @internal
* @brief Opens and configures EMAC. Configures Interrupts
*
* @param pi  PDINFO structure pointer.
*
* @retval Success(0) or failure(Error Codes defined)
*/
uint HwPktOpen( PDINFO *pi )
{
	Uint32              i;

    pPDI = pi;

    /* Initialize EMAC setup  */
    SetupEmacInitConfig();

    InterruptInit();
	
	PktMTU = switchCfg.PktMTU;

	/* Initialize packet structure for Rx */
	PacketInit();

    i = EMACOpen( 1, (Handle)ICSS_HANDLE_VALUE, &switchCfg, &hEMAC );

    if( i )
	{
    	info_printf("EMAC OPEN Returned error \n");
		csl_errors++;
		return i;
	}

	/* Wait for link to come up */
    pi->TxFree = 0;

    EnableEMACInterrupts();

    return 0;
}
/**
* @internal
* @brief Closes EMAC and disables interrupts.
*
* @param pi  PDINFO structure pointer.
*
* @retval none
*/
void HwPktClose( PDINFO *pi )
{
	Uint32 i;

	DisableEMACInterrupts();

    InterruptEnd();

    i = EMACClose( hEMAC );

	if( i )
	{
		csl_errors++;
		info_printf("EMAC Close Returned error %08x\n",i);
    }

    hEMAC = 0;
}
/**
* @internal
* @brief Routine to send out a packet.
*
* @param pi  PDINFO structure pointer.
*
* @retval none
*/
void HwPktTxNext( PDINFO *pi )
{
	PBM_Handle          hPkt;
	EMAC_Pkt*		    csl_send_pkt;
	Uint32              i;

	/* Checking for any queued packets to be transmitted */
	if( !(hPkt = PBMQ_deq(&pi->PBMQ_tx)) )
	{
		pi->TxFree = 1;
		return;
	}

	csl_send_pkt = &send_Pkt;
	csl_send_pkt->AppPrivate 	= (Uint32)hPkt;
	csl_send_pkt->pDataBuffer	= PBM_getDataBuffer(hPkt);
	csl_send_pkt->BufferLen  	= PBM_getBufferLen(hPkt);
	csl_send_pkt->Flags      	= EMAC_PKT_FLAGS_SOP | EMAC_PKT_FLAGS_EOP;
	csl_send_pkt->ValidLen   	= PBM_getValidLen(hPkt);
	csl_send_pkt->DataOffset 	= PBM_getDataOffset(hPkt);
	csl_send_pkt->PktChannel 	= SWITCH_DEFAULT_TX_CHANNEL;
	csl_send_pkt->PktLength  	= PBM_getValidLen(hPkt);
	csl_send_pkt->PktFrags   	= 1;

	i = EMACSendPacket(hEMAC, csl_send_pkt);

	if( i )
	{
		csl_errors++;
		PBM_free( (PBM_Handle)hPkt );
		return;
	}

	PBM_free( (PBM_Handle)hPkt );
	/* Packet has been transmitted */
	return;
}
/**
* @internal
* @brief Low level driver Ioctl interface. This interface can be used for
*  ALE configuration,control,statistics
*
* @param pi  PDINFO structure pointer.
* @param cmd Ioctl command.
* @param pBuf Ioctl buffer with commands and params to set/get  configuration from hardware.
* @param size Size of Ioctl buffer
*
* @retval none
*/
Uint32 HwPktIoctl(PDINFO *pi, uint cmd, void* param, uint size)
{
    Uint32  retVal = 0;

    (void)pi;

    switch(cmd)
    {
        case ICSS_IOCTL_SWITCH_CMD:
        {
            retVal = EMACSetConfig(hEMAC, param, size);
            if( retVal != SWITCH_SUCCESS)
			{
			   info_printf("Error configuring the EMAC Hardware: %d \n", retVal);
				return (1U<<31);
			}
            break;
        }
	    case ICSS_IOCTL_STAT_CMD:
	    {
		    /* handle STATs related IOCTL here */
		    StatsCmd *statsCommand = (StatsCmd*) param;
		    switch(statsCommand->opcode)
		    {
			    case ICSS_IOCTL_STAT_CLEAR:
                {
                	EMACClearStatistics(hEMAC,statsCommand->portNum);
				    break;
                }
			    case ICSS_IOCTL_STAT_GET:
			    {
                    EMACGetStatistics(hEMAC, &(statsCommand->statsObj),statsCommand->portNum);
				    break;
			    }
    		    default:
                {
				    break;
                }
		    }
            retVal = SWITCH_SUCCESS;
           break;
        }
        default:
        {
			return (1U<<31);
        }
	}
    return retVal;
}
/**
* @internal
* @brief This function is called at least every 100ms, faster in a
*      polling environment. The fTimerTick flag is set only when
*      called on a 100ms event.
*
* @param pi  PDINFO structure pointer.
* @param fTimerTick Flag for timer, set when called on a 100ms event..
*
* @retval none
*/
void _HwPktPoll( PDINFO *pi, uint fTimerTick )
{
    uint            mdioStatus;
    SwitchDevice*   devPtr = (SwitchDevice *)hEMAC;
    int             i;
    static Uint32   aleTickCount = 0;

    if( fTimerTick )
    {
    	/*Different protocols require periodic processing*/
    	if(periodicProtTask) {
    		periodicProtTask(NULL, NULL);
    	}
    	ICSSALECounter();
    	/*Reset the credit values used for Storm prevention*/
    	if(ifStormPreventionEnabled(HWPORT0) || ifStormPreventionEnabled(HWPORT1)) {
    		resetStormPreventionCounter();
    	}

	    if( (0u != devPtr->Config.aleTimerActive) && (0 != devPtr->Config.aleTicks) )
	    {
			/* aleTickCount can become greater if timeout period is changed in between */
			if( (++aleTickCount) >=  devPtr->Config.aleTicks)
			{
				/* age out now */
				ICSSALEAgeOutNow();
				aleTickCount = 0;
			}
	    }        

        llEnter();

        /* Signal the MDIO */
		for(i=0;i<SWITCH_NUM_MAC_PORTS;i++)
		{
			CpgmacObj *gmacObject  = &devPtr->cpgmacMac[i];
			Int32 tickChange;
			SwitchMacStatus macStat;

			tickChange = ICSSMDIOTic(&gmacObject->PhyDev,&mdioStatus);

			if(tickChange == 1)
			{
				ICSSUpdatePhystatus(gmacObject, i);
				macStat.PhyDuplex = gmacObject->PhyDuplex;
				macStat.PhyLinked =  gmacObject->PhyLinked;
				macStat.PhyNum = gmacObject->PhyNum;
				macStat.PhySpeed = gmacObject->PhySpeed;
				ICSSControlCb(devPtr, SWITCH_PHY_STATUS_UPDATE, &macStat, &devPtr->switchPort[i]);
			}
			if ((mdioStatus == MDIO_EVENT_LINKDOWN)  || (mdioStatus ==  MDIO_EVENT_LINKUP))
			{
				if(mdioStatus != MDIO_EVENT_LINKDOWN)
				{
					/* Now that we have a link, send any queued packets */
					while( !pi->TxFree )
						HwPktTxNext( pi );
				}


			}
	    }        
        llExit();
    }
}

/**
* @internal
* @brief EMAC Packet Receive API
*
* @param none
*
* @retval none
*/
void HwIntRx(Int32 queueNum)
{
	Uint32 i;

	if ( hEMAC )
    {
#ifdef DEBUG_STATS
        raweth_stat.ethRxCnt++;
#endif
        i = EMACRxServiceCheck(hEMAC,queueNum);
        if( i )
		{
		    if (i == SWITCH_ERR_MACFATAL)
		        emac_fatal_error++;
		    else
		        csl_errors++;
		}
    }
    return;
}

/**
* @internal
* @brief Link change status interrupt for Port 0
*  	   calls a user callback if defined to provide link info to stack
*
* @param none
*
* @retval none
*/
void Link0ISR(void *args) {
	// FW seems we only get phy link state here - MDIO state machine not completed for other state info yet
	// need to defer that info to other (slow) API
	
	linkUp[HWPORT0] = (UInt8)PhyLinkStatusGet(ICSS_MDIO_BASEADDR, mac_configuration.phyAddr[0], 3);

	updatePHYStatus(HWPORT0);

	if(linkUp[HWPORT0]) {
		ICSSIoctl((enum t_portNum)HWPORT0, PORT_ENABLE);
	} else {
		ICSSIoctl((enum t_portNum)HWPORT0, PORT_DISABLE);
		/*Count number of link breaks*/
		hostStats[HWPORT0].linkBreak++;
	}

	/*Protocol specific processing*/
	if(port0ISRCall != NULL) {
		port0ISRCall((void *)linkUp[HWPORT0], port0ISRUser);
	}

	/*clear interrupt in MDIO*/
	HWREG(ICSS_MDIO_BASEADDR + ICSS_MDIO_LINKINT_RAW_MASK_OFFSET) = 0x01;
	/*clear PRU-ICSS INTC interrupt*/
	ClearLink0ISR();
}

/**
* @internal
* @brief Link change status interrupt for Port 1
*
* @param none
*
* @retval none
*/
void Link1ISR(void *args) {

	

	linkUp[HWPORT1] = (UInt8)PhyLinkStatusGet(ICSS_MDIO_BASEADDR, mac_configuration.phyAddr[1], 3);

	updatePHYStatus(HWPORT1);

	if(linkUp[HWPORT1]) {
		ICSSIoctl((enum t_portNum)HWPORT1, PORT_ENABLE);
		/*Update flags in memory*/
		/*Stores 10/100/HD status in PRU memory. Not required for PN TODO*/
		//updatePHYStatus(HWPORT1);
		//UARTprintf(5, "Port 1 UP\n");		// ICEv1 link debug
	} else {
		ICSSIoctl((enum t_portNum)HWPORT1, PORT_DISABLE);
		/*Count number of link breaks*/
		hostStats[HWPORT1].linkBreak++;
		//UARTprintf(5, "Port 1 DOWN\n");		// ICEv1 link debug
	}

	/*Protocol specific processing*/
	if(port1ISRCall != NULL) {
		port1ISRCall((void *)linkUp[HWPORT1], port1ISRUser);
	}

	/*clear interrupt in MDIO*/
	HWREG(ICSS_MDIO_BASEADDR + ICSS_MDIO_LINKINT_RAW_MASK_OFFSET) = 0x02;
	/*clear PRU-ICSS INTC interrupt*/
	ClearLink1ISR();
}

// todo: extend with link speed and duplex
/**
* @brief Function to fetch the link status of a port
*
* @param port	port number
*
* @retval 1 if link is up and 0 if link is down
*/
Uint8 GetLinkStatus(Uint32 port)
{
	if(port == ANYPORT)
	{
		if(SWITCH_NUM_MAC_PORTS > 1)
			return (linkUp[PORT1-1] || linkUp[PORT2-1]);
		else
			return (linkUp[PORT1-1]);
	}
	else
	{
		if (port > SWITCH_NUM_MAC_PORTS)
			return 0;
	}
	return linkUp[port-1];
}

/**
* @internal
* @brief De-registering the interrupts and disabling global interrupts
*
* @param none
*
* @retval none
*/
void InterruptEnd(void)
{
    IntSetup intSetup;

    Hwi_delete(&HwiRxInt);

    intSetup.intVectId = ICSS_LINK0_STAT_INTNUM;
    Interrupt_delete(&intSetup);

    intSetup.intVectId = ICSS_LINK1_STAT_INTNUM;
    Interrupt_delete(&intSetup);
}

/**
* @internal
* @brief Registering Interrupts and Enabling global interrupts
*
* @param none
*
* @retval none
*/
void InterruptInit(void)
{	   
    static UInt32 cookie = 0;
    
    cookie = disableInterrupts();
    
    IntSetup    hwi_intSetup;
    Uint32      retVal;

	Hwi_Params hwiParams;
    Error_Block eb;

    Hwi_Params_init(&hwiParams);
    Error_init(&eb);
   
   // setup Receive Frame ISR
    hwiParams.arg = 1;        // just an ID
    hwiParams.eventId = ICSS_RX_PULSE_INTNUM;
    hwiParams.maskSetting = Hwi_MaskingOption_SELF;
    hwiParams.enableInt = FALSE;		// enable later
    hwiParams.priority = ICSS_RX_PULSE_INTNUM-5;	

    HwiRxInt = Hwi_create(ICSS_RX_PULSE_INTNUM, (IHwi_FuncPtr)RxInterruptHandler, &hwiParams, &eb);
	Hwi_enableInterrupt(ICSS_RX_PULSE_INTNUM);
	
    if (Error_check(&eb)) {
        // handle the error
    	return;
    }

    mmZeroInit(&hwi_intSetup, sizeof(IntSetup));

    hwi_intSetup.intVectId =  ICSS_LINK0_STAT_INTNUM;
    hwi_intSetup.sysEvtCount = 1;
    hwi_intSetup.sysEvtId[0] = 0;
    hwi_intSetup.pCallbackFxn = &Link0ISR;
    hwi_intSetup.pCallbackArg = 0;
    hwi_intSetup.bEnable = 0;

    retVal = Interrupt_add(&hwi_intSetup);
    if(retVal != 0)
    	info_printf("Error setting up Link Status 0 Interrupts \n");

    mmZeroInit(&hwi_intSetup, sizeof(IntSetup));

    hwi_intSetup.intVectId =  ICSS_LINK1_STAT_INTNUM;
    hwi_intSetup.sysEvtCount = 1;
    hwi_intSetup.sysEvtId[0] = 0;
    hwi_intSetup.pCallbackFxn = &Link1ISR;
    hwi_intSetup.pCallbackArg = 0;
    hwi_intSetup.bEnable = 0;

    retVal = Interrupt_add(&hwi_intSetup);
    if(retVal != 0)
    	info_printf("Error setting up Link Status 1 Interrupts \n");

    restoreInterrupts(cookie);
}

/**
* @brief Callback function to process protocol specific handler for link status ISR
*
* @param callBack	Callback function pointer
* @param userArg	user specific parameter
*
* @retval none
*/
void RegisterPort0ISRCallback(icssCallBack_t callBack, void *userArg)
{
	port0ISRCall = callBack;
	port0ISRUser = userArg;
}
/**
* @brief Callback function to process protocol specific handler for link status ISR
*
* @param callBack	Callback function pointer
* @param userArg	user specific parameter
*
* @retval none
*/
void RegisterPort1ISRCallback(icssCallBack_t callBack, void *userArg)
{
	port1ISRCall = callBack;
	port1ISRUser = userArg;
}
/**
* @brief  EthdriverRegisterLinkChangeNotification routine allows the
*			registration of a notification routine called by the driver on a
*			link change on an ethernet port
*
* @param routine Pointer to the registration routine
*
* @retval none
*/
void EthdriverRegisterLinkChangeNotification(link_change_notification_t routine)
{
	if (NULL != routine)
  	{
    	link_change_notification_routine = routine;
  	}
}
/**
* @brief Function to register callback function to process periodic protocol processing
*
* @param callBack	Callback function pointer
*
* @retval none
*/
void RegisterPeriodicCallback(icssCallBack_t callBack)
{
	periodicProtTask = callBack;
}
/**
* @brief Poll PHY and find if link settings have been modified, set variable in memory
*
* @param portNum Port Number. 0/1
*
* @retval none
*/
void updatePHYStatus(Uint8 portNum) {

	volatile Uint32 *portStatusPtr;
	volatile Uint32 portStatus;
	volatile unsigned short RegStatus;

	portStatusPtr = (Uint32*)(PRU_SHARED_DRAM_BASE_ADDRESS + PORT_STATUS_OFFSET);
	portStatus = *(portStatusPtr);

	if(linkUp[portNum]) {

		/*Read PHY, do it multiple times*/
		MDIOPhyRegRead(ICSS_MDIO_BASEADDR, mac_configuration.phyAddr[portNum], PHYSTS_OFFSET, &RegStatus);
		MDIOPhyRegRead(ICSS_MDIO_BASEADDR, mac_configuration.phyAddr[portNum], PHYSTS_OFFSET, &RegStatus);


		if(switch_configuration.halfDuplexEnable) {
			/*set flags for HD*/
			if(!(RegStatus & HALF_DUPLEX_BITMASK)) {
				duplex[portNum] = HALF_DUPLEX_TYPE;
			} else {
				duplex[portNum] = FULL_DUPLEX_TYPE;
			}
		}

		if(RegStatus & PHY_SPEED_BITMASK) {
			HWREG(DATARAM1_PHYS_BASE + PHY_SPEED_OFFSET) = Ten_Mbps;
		} else {
			HWREG(DATARAM1_PHYS_BASE + PHY_SPEED_OFFSET) = Hundread_Mbps;
		}

		/*Set Link Up Flag*/
		portStatus |= ((portNum + 1) * 2);

	} else {
		/*Clear Link Up Flag*/
		portStatus &= ~((portNum + 1) * 2);
	}

	portStatus |= ((duplex[HWPORT0] | duplex[HWPORT1]) & 0x1);


	/*write back*/
	*(portStatusPtr) = portStatus;
}
/**
* @brief returns the PHY speed status for a port
*
* @param portNum Port Number. 0/1
*
* @retval Status. 0 means 10mbps and 1 means 100 mbps. 2 means undefined.
*/
Uint8 getSpeedStatus(Uint8 portNum) {
	volatile unsigned int *statusPtr;

	/*Link down*/
	if(!linkUp[portNum]) {
		/*always returns default value*/
		return 2;
	}

	statusPtr = (unsigned int*)(DATARAM1_PHYS_BASE + PHY_SPEED_OFFSET);
	if(Ten_Mbps == (*statusPtr)) {
		return 0;
	} else {
		return 1;
	}
}
/**
* @brief returns the PHY duplex status for a port
*
* @param portNum Port Number. 0/1
*
* @retval Status. 1 means Half Duplex and 0 means Full Duplex. 2 means not defined
*/
Uint8 getDuplexStatus(Uint8 portNum) {
	volatile unsigned int *statusPtr;

	/*Link down*/
	if(!linkUp[portNum]) {
		/*always returns default value*/
		return 2;
	}

	statusPtr = (unsigned int*)(PRU_SHARED_DRAM_BASE_ADDRESS + PORT_STATUS_OFFSET);

	return ((*statusPtr) & PORT_IS_HD_MASK);
}
/**
* @brief API to Configure MDI/X Mode of PHY
*
* @param mdiState	Callback function pointer
*                   MDIO_ENABLE_FORCE_MDI
*                   MDIO_ENABLE_FORCE_MDIX
*                   MDIO_ENABLE_AUTO_MDIX
* @param portNum	HWPORT0(0)
*                   HWPORT1(1)
*
* @retval none
*/
void SetPhyMDIX(Uint8 mdiState,Uint8 portNum)
{
	volatile Uint16 regStatus;

	MDIOPhyRegRead(ICSS_MDIO_BASEADDR, mac_configuration.phyAddr[portNum], PHY_CNTRL_REG, &regStatus);
	MDIOPhyRegRead(ICSS_MDIO_BASEADDR, mac_configuration.phyAddr[portNum], PHY_CNTRL_REG, &regStatus);
	switch(mdiState)
	{
		case MDIO_ENABLE_FORCE_MDI:
			regStatus &= ~(NWAY_AUTOMDIX_ENABLE);
			regStatus &= ~(NWAY_FORCEMDIX_ENABLE);
			break;
		case MDIO_ENABLE_FORCE_MDIX:
			regStatus &= ~(NWAY_AUTOMDIX_ENABLE);
			regStatus |= NWAY_FORCEMDIX_ENABLE;
			break;
		case MDIO_ENABLE_AUTO_MDIX:
		default:
			regStatus |= NWAY_AUTOMDIX_ENABLE;
			break;
	}
	MDIOPhyRegWrite(ICSS_MDIO_BASEADDR, mac_configuration.phyAddr[portNum], PHY_CNTRL_REG,regStatus);

}
/**
* @brief API to Change Phy Configuration
*
* @param phyConfig	PHY_CONFIG_AUTONEG
*                   PHY_CONFIG_100FD
*                   PHY_CONFIG_10FD
*                   PHY_CONFIG_100HD
* @param portNum	HWPORT0(0)
*                   HWPORT1(1)
*
* @retval none
*/
void SetPhyConfig(Uint8 phyConfig,Uint8 portNum)
{
	EMACUpdatePhyConfig(phyConfig,portNum);
}
/**
* @brief  API to Enable Power Saving mode in PHY
*
* @param portNum	Callback function pointer
*                   HWPORT0(0)
*                   HWPORT1(1)
*
* @retval none
*/
void EnablePhyPowerSaveMode(Uint8 portNum)
{
	volatile Uint16 regStatus;

	MDIOPhyRegRead(ICSS_MDIO_BASEADDR, mac_configuration.phyAddr[portNum], PHY_SCR_REG, &regStatus);
	MDIOPhyRegRead(ICSS_MDIO_BASEADDR, mac_configuration.phyAddr[portNum], PHY_SCR_REG, &regStatus);
	regStatus |= MII_PSMODE_ENABLE;

	MDIOPhyRegWrite(ICSS_MDIO_BASEADDR, mac_configuration.phyAddr[portNum], PHY_SCR_REG,regStatus);
}
/**
* @brief API to force Power Saving mode in PHY
*
* @param phyPowerMode	MDIO_POWERMODE_NORMAL
*                   MDIO_POWERMODE_DOWN
*                   MDIO_POWERMODE_ACTIVE_SLEEP
*                   MDIO_POWERMODE_PASSIVE_SLEEP
* @param portNum	HWPORT0(0)
*                   HWPORT1(1)
*
* @retval none
*/
void SetPhyPowerSaveMode(Uint8 phyPowerMode,Uint8 portNum)
{
	volatile Uint16 regStatus;

	MDIOPhyRegRead(ICSS_MDIO_BASEADDR, mac_configuration.phyAddr[portNum], PHY_SCR_REG, &regStatus);
	MDIOPhyRegRead(ICSS_MDIO_BASEADDR, mac_configuration.phyAddr[portNum], PHY_SCR_REG, &regStatus);
	switch(phyPowerMode)
	{
		case MDIO_POWERMODE_NORMAL:
			regStatus &= (~(MII_PSMODE_BIT1) & ~(MII_PSMODE_BIT2));
			break;
		case MDIO_POWERMODE_DOWN:
			regStatus &= ~(MII_PSMODE_BIT2);
			regStatus |= MII_PSMODE_BIT1;
			break;
		case MDIO_POWERMODE_ACTIVE_SLEEP:
			regStatus &= ~(MII_PSMODE_BIT1);
			regStatus |= MII_PSMODE_BIT2;
			break;
		case MDIO_POWERMODE_PASSIVE_SLEEP:
			regStatus |= (MII_PSMODE_BIT2 |MII_PSMODE_BIT1);
		default:
			regStatus &= (~(MII_PSMODE_BIT1) & ~(MII_PSMODE_BIT2));
			break;
	}
	MDIOPhyRegWrite(ICSS_MDIO_BASEADDR, mac_configuration.phyAddr[portNum], PHY_SCR_REG,regStatus);
}

