/**
 * cpsw_ethdriver.c
 * 
 *
 * Copyright (c) 2012 Texas Instruments Incorporated ALL RIGHTS RESERVED
 *
*/

#include <xdc/std.h>
#include <ti/ndk/inc/stkmain.h>
#include <stdio.h>

#include "cpsw_common.h"
#include "cpsw_nimu_eth.h"
#include "cpsw_impl.h"
#include "cpsw_miimdio.h"
#include "cpsw_plfm.h"
#include "cpsw_ethdriver.h"
#include "evmAM335x.h"
#include "osdrv_ndkdeviceconfig.h"

#ifdef UIA_DEBUG
#include <xdc/runtime/Log.h>
#include <ti/uia/events/UIABenchmark.h>
#endif
/**********************************************************************
 ****************** Global Variables / Definitions ********************
 **********************************************************************/
/* use compiler(-specific) intrinsics */
#define CPSW_DISABLE_INTERRUPTS() _disable_interrupts()
#define CPSW_RESTORE_INTERRUPTS(key) _restore_interrupts(key)

#ifdef EXTERNAL_MEMORY
#define     EXTMEM                      0x80000000
#else
#define     INTMEM						0x402f0000
#endif
#define     CPSW_PKT_MAX                     4
#define 	CPSW_PKT_MTU_DEFAULT			 1518

Handle           				        cpsw_hEMAC = 0;          /* Handle to our EMAC instance */
static      CPSW_PDINFO					    *pPDI = 0;          /* Handle to Device Info */
static      Uint32                      PktMTU;
// todo: commentato perchè ridefinito alla riga 65
//static      UINT8                       bMacAddr[6];        /* Device MAC address */


/* Array of initialized EMAC packet buffers to handle incoming packets */
static CPSW_Pkt rcv_pkt[CPSW_PKT_MAX];

/* Array of EMAC packet placeholders to handle tx packets */
static CPSW_Pkt    send_pkt[CPSW_PKT_MAX];

#ifdef DEBUG_STATS
Uint32         cpsw_memory_squeeze_error = 0;
static      Uint32 					    csl_errors = 0;     /* CSL returned error due to software */
static      Uint32					    emac_fatal_error = 0;    /* EMAC fatal error */
#endif

/* Switch Configuration structure       */
Cpsw3gInitConfig            cpsw3gCfg = {0};

IntSetup   cpsw_hwi_intSetup;

Uint8 bMacAddr[] = {0x00,0x01,0x02,0x03,0x04,0x05};

/* Defines the num of ports, set using boardType */
UInt8 cpsw_num_ports;
UInt8 cpsw_num_mac_ports;


/**
 * @brief
 *  CPSW_Pkt Queue Data Structure
 *
 * @details
 *  Maintains the count of queued elements and head and tail to CPSW_Pkt queue.
 */
typedef struct cpsw_pkt_queue {
    /**
     * @brief   Number of packets in queue
     */
    Uint32                Count;

    /**
     * @brief   Pointer to first packet
     */
    CPSW_Pkt          *pHead;

    /**
     * @brief   Pointer to last packet
     */
    CPSW_Pkt          *pTail;
} CPSW_PKT_QUEUE;

static CPSW_PKT_QUEUE     RxQueue;      /* Receive Queue */
static CPSW_PKT_QUEUE     TxQueue;      /* Transmit Queue */

/* Call Back routine for link Notification */
cpsw_link_change_notification_t cpsw_link_change_notification_routine = NULL;
extern cpsw_callback_ret_t cpsw_tx_packet_exception_callback = NULL;

void     CPSW3G_getMAC( UINT8 *pMacAddr);

static CPSW_Pkt *Queue_pop( CPSW_PKT_QUEUE *pq );
static void Queue_push( CPSW_PKT_QUEUE *pq, CPSW_Pkt *pPktHdr );

static void Interrupt_init(void);
static void Interrupt_end(void);

/**********************************************************************
 *************************** EMAC Functions ***************************
 **********************************************************************/

/**
 *  @b Disable_EMAC_Interrupts
 *  @n
 *      This function disables the EMAC interrupts
 *      on A8/C674.
 *
 *  @retval
 *      None.
 */
static void Disable_EMAC_Interrupts()
{
    Uint32 key;  
    key = CPSW_DISABLE_INTERRUPTS();

	IntSetup intSetup; 
    intSetup.intVectId = CPSW_RX_PULSE_INTNUM;
    Interrupt_disable(&intSetup);

    intSetup.intVectId = CPSW_TX_PULSE_INTNUM;
    Interrupt_disable(&intSetup);
    
    CPSW_RESTORE_INTERRUPTS(key);
}

/**
 *  @b Enable_EMAC_Interrupts
 *  @n
 *      This function enables the EMAC interrupts
 *      on A8/C674.
 *
 *  @retval
 *      None.
 */
static void Enable_EMAC_Interrupts()
{
    Uint32 key = 0;
            
    key = CPSW_DISABLE_INTERRUPTS();

	IntSetup intSetup; 
	intSetup.intVectId = CPSW_RX_PULSE_INTNUM;
    Interrupt_enable(&intSetup);

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

    CPSW_RESTORE_INTERRUPTS(key);
}

/**
 *  @b Queue_pop
 *  @n
 *      Dequeues a packet from CPSW_Pkt queue.
 *
 *  @param[in]  pq
 *      Packet queue of type PKT_QUEUE .
 *
 *  @retval
 *      CPSW_Pkt popped from the queue.
 */
static CPSW_Pkt *Queue_pop( CPSW_PKT_QUEUE *pq )
{
	CPSW_Pkt *pPktHdr = NULL;
    Uint32 key = 0;

    key = CPSW_DISABLE_INTERRUPTS();

    pPktHdr = pq->pHead;

    if( pPktHdr)
    {
        pq->pHead = pPktHdr->pNext;
        pq->Count--;
        pPktHdr->pPrev = pPktHdr->pNext = 0;
    }
   	CPSW_RESTORE_INTERRUPTS(key);

    return( pPktHdr );
}


/**
 *  @b Queue_push
 *  @n
 *      Enqueues a packet in CPSW_Pkt queue.
 *
 *  @param[in]  pq
 *      Packet queue of type PKT_QUEUE .
 *  @param[in]  pPktHdr
 *      CPSW_Pkt type packet to push.
 *
 *  @retval
 *      void
 */
static void Queue_push( CPSW_PKT_QUEUE *pq, CPSW_Pkt *pPktHdr )
{
    Uint32 key = 0;
    key = CPSW_DISABLE_INTERRUPTS();

    pPktHdr->pNext = 0;

    if( !pq->pHead ) 
    {
        /* Queue is empty - Initialize it with this one packet */
        pq->pHead = pPktHdr;
        pq->pTail = pPktHdr;
    }
    else 
    {
        /* Queue is not empty - Push onto end */
        pq->pTail->pNext = pPktHdr;
        pq->pTail        = pPktHdr;
    }
    pq->Count++;

	CPSW_RESTORE_INTERRUPTS(key);
}


/**
 *  @b GetPacket
 *  @n
 *      Returns a packet from the RxQueue.
 *
 *  @param[in]  hApplication
 *      Driver's handle.
 *
 *  @retval
 *		CPSW_Pkt pointer of a packet with buffer to replenish CSL.
 */
static CPSW_Pkt *GetPacket( Handle hApplication )
{
	CPSW_Pkt *pPkt = NULL;

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

    pPkt = Queue_pop(&RxQueue);

    if( pPkt ) 
	{
        pPkt->DataOffset = PBM_getDataOffset(pPkt->AppPrivate);
    }

#ifdef EXTERNAL_MEMORY
	/* Clean the cache for external addesses */
	if( (UINT32)(PBM_getDataBuffer(pPkt->AppPrivate)) & EXTMEM )
        OEMCacheClean( (void *)(PBM_getDataBuffer(pPkt->AppPrivate)), PBM_getBufferLen(pPkt->AppPrivate) );
#else
	/* Clean the cache for internal addesses */
	if( (UINT32)(PBM_getDataBuffer(pPkt->AppPrivate)) & INTMEM )
	    OEMCacheClean( (void *)(PBM_getDataBuffer(pPkt->AppPrivate)), PBM_getBufferLen(pPkt->AppPrivate) );
#endif

    return( pPkt );
}

/**
 *  @b FreePacket
 *  @n
 *      Called by CSL to free the buffer.
 *
 *  @param[in]  hApplication
 *      Driver's handle.
 *  @param[in]  cslPkt
 *      CPSW_Pkt type packet passed by CSL.
 *
 *  @retval
 *      void
 */
static void FreePacket( Handle hApplication, CPSW_Pkt *cslPkt )
{
    if( (Uint32)hApplication != CPSW_HANDLE_VALUE )
    {
        return;
    }

    PBM_free( (PBM_Handle)cslPkt->AppPrivate );
    Queue_push( &TxQueue, cslPkt );
}

/**
 *  @b RxPacket
 *  @n
 *      Function called by CSL to pass the received packet to the driver.
 *
 *  @param[in]  hApplication
 *      Driver's handle.
 *  @param[in]  cslPkt
 *      CPSW_Pkt type packet passed by CSL.
 *
 *  @retval
 *		CPSW_Pkt pointer of a packet with buffer to replenish CSL.
 */
static CPSW_Pkt *RxPacket(Handle hApplication, CPSW_Pkt *cslPkt)
{
	PBM_Handle  hPkt;

	if( (Uint32)hApplication != CPSW_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 - 4));
	    PBM_setIFRx((PBM_Handle) cslPkt->AppPrivate, pPDI->hEther );

	    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);

#ifdef EXTERNAL_MEMORY
		/* Clean the cache for external addesses */
		 if( (UINT32)(PBM_getDataBuffer(cslPkt->AppPrivate)) & EXTMEM )
		   OEMCacheClean( (void *)(PBM_getDataBuffer(cslPkt->AppPrivate)), PBM_getBufferLen(cslPkt->AppPrivate) );
#else
			/* Clean the cache for internal addesses */
				 if( (UINT32)(PBM_getDataBuffer(cslPkt->AppPrivate)) & INTMEM )
				   OEMCacheClean( (void *)(PBM_getDataBuffer(cslPkt->AppPrivate)), PBM_getBufferLen(cslPkt->AppPrivate) );
#endif
		return( cslPkt );
	}

#ifdef DEBUG_STATS
	/* Increment the statistics to account for packets dropped because
	 * of memory squeeze 
     */
	cpsw_memory_squeeze_error++;
#endif
	return cslPkt;
}

/**
 *  @b Setup_CpswInitConfig
 *  @n
 *     Populate the configuration structures with 
 *     the switch's default initialization parameters.
 *
 *  @param[in]  
 *      void
 *
 *  @retval
 *      void
 */
static void Setup_CpswInitConfig(void)
{
    Cpsw3gInitConfig    *iCfg = NULL;
    Int32               i, j;
    CPSW_AddrConfig     *addrCfg;

    /* Allocate Switch configuration structure */
    mmZeroInit(&cpsw3gCfg,sizeof(Cpsw3gInitConfig));

    iCfg = &cpsw3gCfg;

    /* Configure the Number of Rx, Tx channels */
    iCfg->dmaInitCfg.numChannels[CPSW_NET_CH_DIR_TX] = CPSW3G_INITPARAM_TXNUMCHAN;
    iCfg->dmaInitCfg.numChannels[CPSW_NET_CH_DIR_RX] = CPSW3G_INITPARAM_RXNUMCHAN;

    /* Initialize Tx,Rx channels with number of BDs to be allocated for
     * each of them.
     */
    for(i=0; i<iCfg->dmaInitCfg.numChannels[CPSW_NET_CH_DIR_TX]; i++)
    {
        Cpsw3gChInfo *txChCfg = &cpsw3gCfg.chInfo[CPSW_NET_CH_DIR_TX][i];
        
        txChCfg->chNum = i;
        txChCfg->chDir = CPSW_NET_CH_DIR_TX;
        txChCfg->numBD = CPSW3G_INITPARAM_TXNUMBDS;
    }
    for(i=0; i<iCfg->dmaInitCfg.numChannels[CPSW_NET_CH_DIR_RX]; i++)
    {
        Cpsw3gChInfo *rxChCfg = &cpsw3gCfg.chInfo[CPSW_NET_CH_DIR_RX][i];

        rxChCfg->chNum = i;
        rxChCfg->chDir = CPSW_NET_CH_DIR_RX;
        /* Configure the Buffer size for plain ethernet packets. VLANs disabled by
         * default. If using VLANs make sure to add in 4 bytes to the buffer size 
         * below to accomodate VLAN header.
         */
        rxChCfg->bufSize = 1522;//1522=1500+14+4+4
        rxChCfg->numBD = CPSW3G_INITPARAM_RXNUMBDS;
    }


    /* Program Default ALE Configuration */
    iCfg->aleCfg.aleModeFlags = CPSW_CONFIG_ALE_ENABLE
                                 | CPSW_CONFIG_ALE_CLRTABLE;

    /** CHECK: Set this to correct value.Set Ratelimit counter for 1msec*/
    iCfg->aleCfg.alePrescale =  CPSW3G_DEFAULT_ALEPRESCALE;

    /* Ale Port Config */

     for(i=0; i < cpsw_num_ports; i++)
           iCfg->alePortCfg[i].portState = CPSW_ALE_PORT_STATE_FWD;



    /*CPSW3G default config*/
    iCfg->cpswCfg.cpswStatModeFlags = CPSW_CONFIG_STAT_ENABLE;

    iCfg->cpswCfg.mdioBusFrequency = MDIO_FREQ_INPUT;
    iCfg->cpswCfg.mdioClockFrequency = MDIO_FREQ_OUTPUT;
    iCfg->Mib64CntMsec = CPSW3G_MIB_TIMER_TIMEOUT;
    
    /*Starterware implementation */
    iCfg->baseConfig.ss_base = CPSW0_SS_REGS;
    iCfg->baseConfig.mdio_base = CPSW0_MDIO_REGS;
    iCfg->baseConfig.wrpr_base = CPSW0_WR_REGS;
    iCfg->baseConfig.cpdma_base = CPSW0_CPDMA_REGS;
    iCfg->baseConfig.ale_base = CPSW0_ALE_REGS;
    iCfg->baseConfig.stat_base = CPSW0_STAT_REGS;
    iCfg->baseConfig.cppi_ram_base = CPSW0_CPPI_RAM_REGS;
    iCfg->baseConfig.host_port_base = CPSW0_PORT_0_REGS;
    iCfg->baseConfig.port[PORT_1].sliver_base = CPSW0_SLIVER_1_REGS;
    iCfg->baseConfig.port[PORT_1].port_base = CPSW0_PORT_1_REGS;
    iCfg->baseConfig.port[PORT_2].port_base = CPSW0_PORT_2_REGS;
    iCfg->baseConfig.port[PORT_2].sliver_base = CPSW0_SLIVER_2_REGS;

    /* Program default MAC SL config */
    for (i=0; i < cpsw_num_mac_ports; i++)
    {
    	iCfg->baseConfig.port[i].phy_addr = mac_configuration.phyAddr[i];
    	/* parse the default speed/duplex params. Use Auto Neg*/
    	iCfg->macInitCfg[i].mdioModeFlags |= CPSW_MDIO_MODEFLG_AUTONEG;

	    if(0 == i)
	    {
			//program phy_mask for port 1
			iCfg->macInitCfg[i].PhyMask = 1<<(iCfg->baseConfig.port[i].phy_addr);
			iCfg->macInitCfg[i].MLinkMask = 0x0;
		}
	    else
	    {
			/* program phy_mask for port 2 */
        	iCfg->macInitCfg[i].PhyMask = 1<<(iCfg->baseConfig.port[i].phy_addr);/*corresponding to address 2 */
	        iCfg->macInitCfg[i].MLinkMask = 0x1;
	    }/* end of else block covering settings for port 1 */
	    iCfg->macInitCfg[i].macModeFlags |= mac_configuration.macModeFlags;
    }/* end of for loop over CPSW3G_NUM_MAC_PORTS */

	/* We count in 100ms Ticks */
	cpsw3gCfg.aleTicks = (CPSW_ALE_AGE_OUT_TIME / CPSW_ALE_TICK_PERIOD);
	cpsw3gCfg.aleTimerActive        = 0;

	cpsw3gCfg.CoreNum               = 0;

    /* Add 4 bytes for VLAN header if configuring for VLANs */
	cpsw3gCfg.PktMTU                = CPSW_PKT_MTU_DEFAULT; //rxcrc is present
	cpsw3gCfg.DescBase              = CPSW_DESC_BASE_CPPI;
    /* Configure the callback functions */
    cpsw3gCfg.pfcbGetPacket          = &GetPacket;
    cpsw3gCfg.pfcbFreePacket         = &FreePacket;
    cpsw3gCfg.pfcbRxPacket           = &RxPacket;

	cpsw3gCfg.TotalNumOfMacAddrs     = CPSW_NUMBER_OF_MAC_ADDRESS;

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

	for ( j=0; j<cpsw3gCfg.TotalNumOfMacAddrs; j++ )
    {
		cpsw3gCfg.MacAddr[j] = (CPSW_AddrConfig *)mmAlloc(sizeof(CPSW_AddrConfig));
	}

    /* Generate a default MAC address to use */
    for( j=0; (Uint8)j<(cpsw3gCfg.TotalNumOfMacAddrs); j++ )
    {
		addrCfg = cpsw3gCfg.MacAddr[j];
		addrCfg->ChannelNum = 0;
		if( j>=3 && j<6 )
			addrCfg->ChannelNum = 1;
		if( j>=6 && j<9 )
			addrCfg->ChannelNum = 2;
   		for( i=0; i<6; i++ )
        {
		 	addrCfg->Addr[i] = j * 0x10 + i ;
		}
	}

	addrCfg = cpsw3gCfg.MacAddr[0];


    /*  Check if CPSW3G_getMac had returned a valid MAC address to use. If not,
     *  use the default generated above.
     */
    for( i=0; (i<6) && (!(bMacAddr[i])); i++ );
    if( i==6 )
    {
        mmCopy( (void *)&pPDI->bMacAddr[0], (void *)&addrCfg->Addr[0], 6 );
    }
    else
    {
        mmCopy( (void *)&addrCfg->Addr[0], (void *)&bMacAddr[0], 6 );
    }

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

    return;
}

/**
 *  @b CpswHwPktInit
 *  @n
 *      Initializes the device MAC address to use. 
 *
 *  @param[in]  void
 *
 *  @retval
 *      Success -   1
 *  @retval
 *      Error   -   0
 */
uint CpswHwPktInit()
{
    /* Initialize our MAC address */
    mmZeroInit(bMacAddr, 6);

    /**
     * Get our MAC address
     */
    CPSW3G_getMAC( bMacAddr);

            
    return (1);
}

/**
 *  @b CpswHwPktShutdown
 *  @n
 *      Void
 *
 *  @param[in]  void
 *
 *  @retval
 *       void
 */
void CpswHwPktShutdown()
{
}

/**
 *  @b CpswHwPktOpen
 *  @n
 *      Opens and configures EMAC. Configures Interrupts, SGMII,
 *      and send and receive queues.
 *
 *  @param[in]  pi
 *      PDINFO structure pointer.
 *
 *  @retval
 *      Success -   0
 *  @retval
 *      Error   -   >0
 */
uint CpswHwPktOpen( CPSW_PDINFO *pi )
{
	Uint32              i;
    PBM_Handle          hPkt;

    pPDI = pi;

    /* Initialize EMAC setup  */
    Setup_CpswInitConfig();

	memset(&RxQueue, 0, sizeof(CPSW_PKT_QUEUE));//mmZeroInit( &RxQueue, sizeof(PKT_QUEUE) );
    memset(&TxQueue, 0, sizeof(CPSW_PKT_QUEUE)); //mmZeroInit( &TxQueue, sizeof(PKT_QUEUE) );
   
	PktMTU                  = cpsw3gCfg.PktMTU;

    /* Queue up some initialized receive buffers */
	for( i=0; i<CPSW_PKT_MAX; i++ )
	{
		hPkt = PBM_alloc(PktMTU + 4 + PKT_PREPAD);
        /* Couldnt allocate memory, return error */
		if (hPkt == NULL)
			return 1;
        PBM_setDataOffset( hPkt, PKT_PREPAD );
		rcv_pkt[i].AppPrivate   = (Uint32)hPkt;
		rcv_pkt[i].pDataBuffer  = PBM_getDataBuffer(hPkt);
		rcv_pkt[i].BufferLen    = PBM_getBufferLen(hPkt);
        Queue_push( &RxQueue, &rcv_pkt[i] );
    }

    /* Queue up some transmit buffers */
	for( i=0; i<CPSW_PKT_MAX; i++ )
	{
        Queue_push( &TxQueue, &send_pkt[i] );
    }

    i = CPSW_open( 1, (Handle)CPSW_HANDLE_VALUE, &cpsw3gCfg, &cpsw_hEMAC );

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

    /* Init internal bookkeeping fields */
    pi->OldMCastCnt = 0;

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

    Interrupt_init();
    Enable_EMAC_Interrupts();

    return 0;
}

/**
 *  @b CpswHwPktClose
 *  @n
 *      Closes EMAC and disables interrupts.
 *
 *  @param[in]  pi
 *      PDINFO structure pointer.
 *
 *  @retval
 *      void
 */
void CpswHwPktClose( CPSW_PDINFO *pi )
{
	Uint32 i;
    (void)pi;
    Disable_EMAC_Interrupts(); 

	Interrupt_end();

	i = CPSW_close( cpsw_hEMAC );
	if( i )
	{
#ifdef DEBUG_STATS
		csl_errors++;
#endif
		info_printf("EMAC Close Returned error %08x\n",i);
    }

	cpsw_hEMAC = 0;
}

/**
 *  @b CpswHwPktTxNext
 *  @n
 *      Routine to send out a packet.
 *
 *  @param[in]  pi
 *      PDINFO structure pointer.
 *
 *  @retval
 *      void
 */
void CpswHwPktTxNext( CPSW_PDINFO *pi )
{

    register UINT8      *buffer;
    register uint       length;

    PBM_Handle          hPkt;
    CPSW_Pkt*		    csl_send_pkt;
	Uint32              i,key=0;

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

#ifdef EXTERNAL_MEMORY
    buffer = PBM_getDataBuffer(hPkt) + PBM_getDataOffset(hPkt);
    length = PBM_getValidLen(hPkt);

    /* Clean the cache for external addesses */
    if( (UINT32)buffer & EXTMEM )
        OEMCacheClean( (void *)buffer, length );
#else
    buffer = PBM_getDataBuffer(hPkt) + PBM_getDataOffset(hPkt);
    length = PBM_getValidLen(hPkt);

    /* Clean the cache for internal addesses */
    if( (UINT32)buffer & INTMEM )
        OEMCacheClean( (void *)buffer, length );
#endif

	csl_send_pkt = Queue_pop( &TxQueue );

	if( csl_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      	= CPDMA_BUF_DESC_SOP | CPDMA_BUF_DESC_EOP;
	    csl_send_pkt->ValidLen   	= PBM_getValidLen(hPkt);
	    csl_send_pkt->DataOffset 	= PBM_getDataOffset(hPkt);

        /* We only use one Tx DMA channel  now. If QOS/Priority has to be
         * supported, identify the channel number based on vlan/priority tag or
         * any other field from the header fields of the ethernet frame. 
         */        
	    csl_send_pkt->PktChannel 	= CPSW3G_DEFAULT_TX_CHANNEL;
	    csl_send_pkt->PktLength  	= PBM_getValidLen(hPkt);
	    csl_send_pkt->PktFrags   	= 1;

			
        key = CPSW_DISABLE_INTERRUPTS();
		i = CPSW_sendPacket(cpsw_hEMAC, csl_send_pkt);
	    CPSW_RESTORE_INTERRUPTS(key);

		if( i )
		{
#ifdef DEBUG_STATS
			csl_errors++;
#endif
			info_printf("CPSW_sendPacket() returned error %08x\n",i);

			/* Free the packet as the packet did not go on the wire*/
			PBM_free( (PBM_Handle)csl_send_pkt->AppPrivate );

			Queue_push( &TxQueue, csl_send_pkt );
        }

		/* Packet has been transmitted */
		return;
	}

	/* Free the packet as the packet did not go on the wire. */
	PBM_free((PBM_Handle)hPkt);
#ifdef DEBUG_STATS
	cpsw_raweth_stat.notGoCnt++;
#endif
	return;
}

/**
 *  @b CpswHwPktIoctl
 *  @n
 *  Low level driver Ioctl interface. This interface can be used for
 *  ALE configuration,control,statistics 
 *
 *  @param[in]  pi
 *      PDINFO structure pointer.
 *  @param[in]  cmd
 *      Ioctl command.
 *  @param[in]  pBuf
 *      Ioctl buffer with commands and params to set/get 
 *      configuration from hardware.
 *  @param[in]  size
 *      Size of Ioctl buffer.
 *
 *  @retval
 *      void
 */
Uint32 CpswHwPktIoctl(CPSW_PDINFO *pi, uint cmd, void* param, uint size)
{
    Uint32  retVal = 0;

    (void)pi;

    switch(cmd)
    {
        case CPSW3G_IOCTL_SWITCH_CMD:
        {
            retVal = CPSW_setConfig(cpsw_hEMAC, param, size);

            if( retVal != CPSW3G_SUCCESS)
            {
            	info_printf("Error configuring the EMAC Hardware: %d \n", retVal);
                //return -1;
				return (1U<<31); 
            }

            break;
        }
	    case CPSW3G_IOCTL_STAT_CMD:
	    {
		    /* handle STATs related IOCTL here */
		    CpswStatsCmd *statsCommand = (CpswStatsCmd*) param;

		    switch(statsCommand->opcode)
		    {
			    case CPSW3G_IOCTL_STAT_CLEAR:
                {
				    break;
                }
			    case CPSW3G_IOCTL_STAT_GET:
			    {
			    	CPSW_getStatistics(cpsw_hEMAC, &(((Cpsw3gDevice *)cpsw_hEMAC)->Stats));
			    	statsCommand->statsObj = ((Cpsw3gDevice *)cpsw_hEMAC)->Stats;
				    break;                    
			    }
    		    default:
                {
				    break;
                }
		    }

            retVal = CPSW3G_SUCCESS;

            break;
        }
        default:
        {
            /* driver doesnt recognize this command. */
            //return -1;
			return (1U<<31);
        }
	}

    return retVal;
}

/**
 *  @b Cpsw_HwPktPoll
 *  @n
 *      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[in]  pi
 *      PDINFO structure pointer.
 *  @param[in]  fTimerTick
 *      Flag for timer, set when called on a 100ms event.
 *
 *  @retval
 *      void
 */
void Cpsw_HwPktPoll( CPSW_PDINFO *pi, uint fTimerTick )
{
    uint            mdioStatus,linkStatus;
    Cpsw3gDevice*   devPtr = (Cpsw3gDevice *)cpsw_hEMAC;
    int             i;
    static Uint32   aleTickCount = 0;
    Uint32 key = 0;

    (void)pi;

    if( fTimerTick )
    {
	    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 */
		    	CPSWALEAgeOutNow(devPtr->Config.baseConfig.ale_base);
			    aleTickCount = 0;
		    }
	    }        

        llEnter();
        key = CPSW_DISABLE_INTERRUPTS();
        
        /* Signal the MDIO */
		for(i=0;i<cpsw_num_mac_ports;i++)
		{
            /* check for port in forward state, not NO_PHY mode */		   
			if ((0u != (CPSWALEPortStateGetAny(devPtr->Config.baseConfig.ale_base , i)))  &&
		     (0u == (devPtr->Config.macInitCfg[i].phyMode & NWAY_NOPHY)))
		    {
                CpgmacObj *gmacObject  = &devPtr->cpgmacMac[i];
			    Int32 tickChange;
			    CpgmacMacStatus macStat;

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

			    if(tickChange == 1)
			    {
			        cpsw3g_Update_Phystatus(gmacObject, i);
				    macStat.PhyDuplex = gmacObject->PhyDuplex;
  				    macStat.PhyLinked =  gmacObject->PhyLinked;
  				    macStat.PhyNum = gmacObject->PhyNum;
  				    macStat.PhySpeed = gmacObject->PhySpeed;
				    cpsw3g_ControlCb(devPtr, CPSW3G_PHY_STATUS_UPDATE, &macStat, &devPtr->cpsw3gPort[i]);
		        }
			    if ((mdioStatus == MDIO_EVENT_LINKDOWN)  || (mdioStatus ==  MDIO_EVENT_LINKUP))
			    {
			        if(mdioStatus == MDIO_EVENT_LINKDOWN)
				    {
					    linkStatus = MDIO_LINKSTATUS_NOLINK; 
				    }
				    else
				    {
				        linkStatus = ((gmacObject->PhySpeed + 1) << 1) + gmacObject->PhyDuplex ;

                        /* Now that we have a link, send any queued packets */
                        while( !pi->TxFree )
                        	CpswHwPktTxNext( pi );
				    }
			        /* Call back to Application */
				    if (NULL != cpsw_link_change_notification_routine)
				    {
				      /* call link change notification routine registered by the user
				       */
				      cpsw_link_change_notification_routine(linkStatus, i);
				    }
			    }
		    }
	    }        

        CPSW_RESTORE_INTERRUPTS(key);
        llExit();
    }
}

/**
 *  @b Cpsw_HwIntRx
 *  @n
 *      EMAC Receive ISR.
 *
 *  @param[in]  void
 *
 *  @retval
 *      void
 */
void Cpsw_HwIntRx(void *a)
{
#ifdef UIA_DEBUG
	Log_write1(UIABenchmark_start, (xdc_IArg)"Rx_Interrupt");
#endif

	if ( cpsw_hEMAC ) {
        CPSW_RxServiceCheck(cpsw_hEMAC);
        /* write the EOI register */
		CPSW_rxEoiWrite();
    }

#ifdef UIA_DEBUG
	Log_write1(UIABenchmark_stop, (xdc_IArg)"Rx_Interrupt");
#endif
    return;
}

/**
 *  @b Cpsw_HwIntTx
 *  @n
 *      EMAC Transmit ISR.
 *
 *  @param[in]  void
 *
 *  @retval
 *      void
 */
void Cpsw_HwIntTx(void *a)
{
#ifdef UIA_DEBUG
	Log_write1(UIABenchmark_start, (xdc_IArg)"Tx_Interrupt");
#endif

	if ( cpsw_hEMAC ){
      CPSW_TxServiceCheck(cpsw_hEMAC);
      /* write the EOI register */
	  CPSW_txEoiWrite();
    }

#ifdef UIA_DEBUG
	Log_write1(UIABenchmark_stop, (xdc_IArg)"Tx_Interrupt");
#endif
   return;
}

/**
 *  @b Interrupt_end
 *  @n
 *      Deregistering the interrupts and disabling global interrupts.
 *
 *  @param[in]  void
 *
 *  @retval
 *      void
 */
void Interrupt_end(void)
{
    IntSetup intSetup;

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

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

/**
 *  @b Interrupt_init
 *  @n
 *      Registering Interrupts and Enabling global interrupts.
 *
 *  @param[in]  void
 *
 *  @retval
 *      void
 */
void Interrupt_init(void)
{	   
    static UInt32 cookie = 0;
    
    cookie = CPSW_DISABLE_INTERRUPTS();
    

	IntSetup    cpsw_hwi_intSetup;
    Uint32      retVal;

    mmZeroInit(&cpsw_hwi_intSetup, sizeof(IntSetup));

    cpsw_hwi_intSetup.intVectId =  CPSW_RX_PULSE_INTNUM;
    cpsw_hwi_intSetup.sysEvtCount = 1;
    cpsw_hwi_intSetup.sysEvtId[0] = 0;
	
    cpsw_hwi_intSetup.pCallbackFxn = &Cpsw_HwIntRx;
    cpsw_hwi_intSetup.pCallbackArg = 0;
    cpsw_hwi_intSetup.bEnable = 0;

    retVal = Interrupt_add(&cpsw_hwi_intSetup);
    if(retVal != 0)
    	info_printf("Error setting up Rx Interrupts \n");

    cpsw_hwi_intSetup.intVectId = CPSW_TX_PULSE_INTNUM;
    cpsw_hwi_intSetup.sysEvtCount = 1;
    cpsw_hwi_intSetup.sysEvtId[0] = 0;

    cpsw_hwi_intSetup.pCallbackFxn = &Cpsw_HwIntTx;
    cpsw_hwi_intSetup.pCallbackArg = 0;
    cpsw_hwi_intSetup.bEnable = 0;

    retVal = Interrupt_add(&cpsw_hwi_intSetup);
    if(retVal != 0)
    	info_printf("Error setting up Tx Interrupts \n");

    /* Restore global interrupts */
    CPSW_RESTORE_INTERRUPTS(cookie);
}

void CPSW3G_getMAC( Uint8 *pMacAddr)
{
	 Uint8 Addr[6];
	 /* Get the MAC address */
	 EVMMACAddrGet(0, Addr);

	 int temp = 0;
	 for(temp = 0; temp < LEN_MAC_ADDRESS; temp++)
	  {
	   	 bMacAddr[temp] = Addr[(LEN_MAC_ADDRESS - 1) - temp];
	  }

    info_printf("Using MAC Address: %02x-%02x-%02x-%02x-%02x-%02x\n",
            bMacAddr[0], bMacAddr[1], bMacAddr[2],
            bMacAddr[3], bMacAddr[4], bMacAddr[5]);

    if((Uint8*)0 != pMacAddr)
	{
    	/* We fill in the pointers here.*/
    	mmCopy( pMacAddr, bMacAddr, 6u );
	}
}

/**
 * @ingroup cpsw_register_link_change_notification
 *
 * The @ref cpsw_register_link_change_notification 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
 */
void
cpsw_register_link_change_notification(cpsw_link_change_notification_t routine)
{
  if (NULL != routine)
  {
    cpsw_link_change_notification_routine = routine;
  }
}


void cpsw_register_tx_exception_callback(cpsw_callback_ret_t callBack)
{
	cpsw_tx_packet_exception_callback = callBack;
}
