/*****************************************************************************
* Copyright (c) 2005 Digi International Inc., All Rights Reserved
*
* This software contains proprietary and confidential information of Digi
* International Inc.  By accepting transfer of this copy, Recipient agrees
* to retain this software in confidence, to prevent disclosure to others,
* and to make no use of this software other than that for which it was
* delivered.  This is an unpublished copyrighted work of Digi International
* Inc.  Except as permitted by federal law, 17 USC 117, copying is strictly
* prohibited.
*
* Restricted Rights Legend
*
* Use, duplication, or disclosure by the Government is subject to
* restrictions set forth in sub-paragraph (c)(1)(ii) of The Rights in
* Technical Data and Computer Software clause at DFARS 252.227-7031 or
* subparagraphs (c)(1) and (2) of the Commercial Computer Software -
* Restricted Rights at 48 CFR 52.227-19, as applicable.
*
* Digi International Inc. 11001 Bren Road East, Minnetonka, MN 55343
*
*****************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "naeth.h"
#include "eth.h"
#include "nammu.h"

#include "NA_ISR.h"
#include "tlog.h"
#include "eth_interrupt.h"

#ifdef NET_OS_SPECIFIC
#if (PROCESSOR == ns9210) || (PROCESSOR == ns9215)
#include "powersave.h"
#endif
#if	BSP_ETH_WIFI_BRIDGE
#include "ewb_api.h"
#endif
#endif

/*
 * Transmit DMA Buffer descriptor ring
 */
#if (PROCESSOR == ns9750) || (PROCESSOR == ns9360) || (PROCESSOR == ns9210) || (PROCESSOR == ns9215)
/* NOTE: Because this is processor memory, it is uncached. */
static fb_buffer_desc_t *eth_tx_buffer_descriptors = (fb_buffer_desc_t *)NARM_EFE_TXBDR;
#elif (PROCESSOR == ns7520) || (PROCESSOR == net50)
static fb_buffer_desc_t eth_tx_buffer_descriptors[ETH_MAX_TX_DESCRIPTORS];
#else
#error Unsupported Processor
#endif

#define TX_DESCRIPTOR_BASE  (eth_tx_buffer_descriptors)
#define TX_INDEX(txd)	    ((fb_buffer_desc_t *)(txd) - TX_DESCRIPTOR_BASE)
#define TX_ADDR(byteoffset) ((fb_buffer_desc_t *)(((char *)TX_DESCRIPTOR_BASE)+(byteoffset)))
#define TX_NEXT(txd)	    (ReadRegMask(DMA_FLAG1(txd), DMA_FLAG1_WRAP) ? TX_DESCRIPTOR_BASE : ((fb_buffer_desc_t *)(txd) + 1))

#if (PROCESSOR == ns7520) || (PROCESSOR == net50) || (PROCESSOR == net40)
#define ETH_NEXT_TX_PACKET_INDEX(x)     (x == (ETH_MAX_TX_DESCRIPTORS - 1)? 0 : x+1)
#define ETH_PREV_TX_PACKET_INDEX(x)     (x == 0 ? (ETH_MAX_TX_DESCRIPTORS - 1) : x-1)
#endif

/* TODO: This stuff goes in eth_datap. */
/*
 * Transmit DMA Buffer descriptor ring
 */
#if (PROCESSOR == ns9750) || (PROCESSOR == ns9360) || (PROCESSOR == ns9210) || (PROCESSOR == ns9215)
extern fb_buffer_desc_t *eth_tx_buffer_descriptors;
#elif (PROCESSOR == ns7520) || (PROCESSOR == net50)
extern fb_buffer_desc_t eth_tx_buffer_descriptors[ETH_MAX_TX_DESCRIPTORS];
#else
#error Unsupported Processor
#endif

#if (PROCESSOR == ns9750) || (PROCESSOR == ns9360) || (PROCESSOR == ns9210) || (PROCESSOR == ns9215)
#define ETH_TX_CLOSED_MASK      (NARM_EFE_IR_TXERR | NARM_EFE_IR_TXDONE)
#define ETH_TX_HWINDEX_REG      NARM_EFE_TXBDPOR
#define ETH_TX_HWINDEX_MASK     NARM_EFE_TXBDPOR_OFFSET_MASK
#define ETH_TX_HWINDEX_SHIFT    4
#elif (PROCESSOR == ns7520) || (PROCESSOR == net50)
#define ETH_TX_CLOSED_MASK      NARM_DSIER_NCIP
#define ETH_TX_HWINDEX_REG      NARM_DMA_CR2
#define ETH_TX_HWINDEX_MASK     NARM_DMA_CR_INDEX
#define ETH_TX_HWINDEX_SHIFT    3
#else
#error Unsupported Processor
#endif

#if ETH_LOG_ENABLED
#define EL_SIZE    256
struct {
    DWord op;
    DWord channel;
    DWord status;
    DWord tx_in;
    DWord tx_idx;
} el_tx[EL_SIZE];
int elindex_tx;
int elwrap_tx;



void EthLogTx(ethData *eth_datap, DWord operation, DWord status)
{
    int level = NASetIRQ(IRQ_DISABLED);
    el_tx[elindex_tx].op = operation;
    el_tx[elindex_tx].status = status;
    el_tx[elindex_tx].tx_in = eth_datap->tx_in - eth_tx_buffer_descriptors;
    el_tx[elindex_tx].tx_idx = ReadRegMask(DMA_BYTEOFFSET_ETH_TX, DMA_BYTEOFFSET_MASK) / sizeof(fb_buffer_desc_t);
    ++elindex_tx;
    if (elindex_tx >= EL_SIZE)
    {
	elwrap_tx++;
	elindex_tx = 0;
    }
    NASetIRQ(level);
}
#endif

fb_buffer_desc_t * ethNextTxDescr(fb_buffer_desc_t *desc)
{
	return TX_NEXT(desc);
}

/* Function: naEthSend is not used.

    naEthOneScatSend is always used instead,
    even for the chips that don't support scattered send.
    For the chips that don't support scattered send, buffers_per_frame
    is set to 1 for the Treck stack.
*/
int naEthSend(ttUserInterface interfaceHandle,
	char TM_FAR *   dataPtr,
	int             dataLength,
	int             flag)
{
    /* Now we use naEthOneScatSend for speed. */
    (void) interfaceHandle;
    (void) dataPtr;
    (void) dataLength;
    (void) flag;
    return TM_DEV_ERROR;
}

#if (PROCESSOR == ns7520) || (PROCESSOR == net50)
static unsigned int buffers_for_unaligned_data[ETH_MAX_TX_DESCRIPTORS];
#endif

/*
 Function: naEthTransmitPacketBuffer

 Description:

    Set up one DMA buffer of a chained packet for transmission.
    Start the packet transmission if this is the last packet buffer,
    by writing the FULL bit to DMA buffer descriptor of the first buffer
    and restarting DMA.

    Called by naEthOneScatSend and naEthBypassTransmit.
*/

void naEthTransmitPacketBuffer(ethData *eth_datap,
                               fb_buffer_desc_t *desc,
							   fb_buffer_desc_t *first_descr,
                               unsigned char *buffer,
                               unsigned size,
                               WORD32 flags)
{
   physical_address_t phys;
   int idx = TX_INDEX(desc);

   NA_ASSERT(idx >= 0 && idx < ETH_MAX_TX_DESCRIPTORS);

#ifdef NET_OS_SPECIFIC
    #if (PROCESSOR == ns9210) || (PROCESSOR == ns9215)
    naPdSetCatnapFastClock();
    #endif
#endif

   NABeforeDMA(NA_MEMORY_TO_DEVICE_DMA, buffer, NULL, size);

    if (!NAVaToPhys(buffer, &phys))
        panic_num("Unable to map tx buffer to physical space!", (unsigned long)buffer, 0);

    /* Set up descriptor except for FULL bit. */
    WriteRegMaskData(DMA_FLAG1(desc),
                     DMA_FLAG1_INTR|DMA_FLAG1_LAST,
                     DMA_FLAG1_INTR|((flags & ETH_TX_LAST_FLAG) ? DMA_FLAG1_LAST : 0));

    WriteRegMaskData(DMA_SBP(desc), DMA_SBP_MASK, phys);
    WriteRegMaskData(DMA_FLAG2(desc), DMA_FLAG2_STATUS_MASK, 0);
    WriteRegMaskData(DMA_BUFLEN(desc), DMA_BUFLEN_MASK, size);

    /* Save size, which gets lost in transmission, and bypass flag */
    eth_datap->txPackets[idx].size  = size;
    eth_datap->txPackets[idx].flags = flags;

    /* Set FULL bit if this is not 1st buffer */
    if (desc != first_descr)
        WriteRegMaskData(DMA_FLAG2(desc), DMA_FLAG2_FULL, DMA_FLAG2_FULL);

    /* If this is last buffer, initiate the transmission */
    if (flags & ETH_TX_LAST_FLAG)
    {
        desc = TX_NEXT(desc);
        NA_ASSERT(TX_INDEX(desc) >= 0 && TX_INDEX(desc) < ETH_MAX_TX_DESCRIPTORS);

    /* Save start and end of the packet for possible lockup reset */
    #if (PROCESSOR == ns7520) || (PROCESSOR == net50) || (PROCESSOR == net40)
        eth_datap->ethTxPacketStart[eth_datap->ethTxPacketFixHead] = TX_INDEX(first_descr);
        eth_datap->ethTxPacketEnd[eth_datap->ethTxPacketFixHead] = TX_INDEX(desc);
        eth_datap->ethTxPacketFixHead = ETH_NEXT_TX_PACKET_INDEX(eth_datap->ethTxPacketFixHead);
    #endif
#if (BSP_ENABLE_ETHERNET_BYPASS == FALSE)
        /* Forward driver's index */
        eth_datap->tx_in = desc;
#endif

        /* Now mark the first descriptor ready to transmit (Full). */
        WriteRegMaskData(DMA_FLAG2(first_descr), DMA_FLAG2_FULL, DMA_FLAG2_FULL);
        ++eth_datap->eth_stats.tx_frames;

    /* Reenable DMA */
    #if (PROCESSOR == ns9750) || (PROCESSOR == ns9360) || (PROCESSOR == ns9210) || (PROCESSOR == ns9215)
        {
	    WORD32 value;
		fb_buffer_desc_t *descr, *first;

	    if (ReadRegMask(NARM_EFE_ISR, NARM_EFE_IR_TXIDLE))
	    {
	        /* Clear the interrupt status tx idle flag */
	        WriteRegMaskData(NARM_EFE_ISR, NARM_EFE_IR_TXIDLE, NARM_EFE_IR_TXIDLE);

	        /* Recover a stalled DMA buffer */
			value = (ReadRegMask(NARM_EFE_TXBDPOR, NARM_EFE_TXBDPOR_OFFSET_MASK) >> 4) << 4;

			/* If there was an abort error condition in this packet in the buffer, which is not the last in
			   the chain, the DMA offset is pointing to the empty buffer descriptor - the next buffer, after
			   the one with an error. Attempt to recover the empty buffer would cause the transmit lockup.
			   We must find the next full buffer descriptor and use it to recover stalled DMA.
			*/
			first = descr = (fb_buffer_desc_t *) (value +  (WORD32) eth_tx_buffer_descriptors);

			while ( ReadRegMask(DMA_FLAG2(descr), DMA_FLAG2_FULL) == 0)
			{
				descr = TX_NEXT(descr);
				if (descr == first)
					break;
			}
			if (ReadRegMask(DMA_FLAG2(descr), DMA_FLAG2_FULL))
			{
				value = (WORD32) descr - (WORD32) eth_tx_buffer_descriptors;
				WriteReg(NARM_EFE_TXRBDPR, (value >> 2));
			}
	    }
	    /* Clear error conditions and wake up transmitter */
        WriteRegMaskData(NARM_EGCR2, NARM_EGCR2_TCLER | NARM_EGCR2_TKICK, 0);
        WriteRegMaskData(NARM_EGCR2, NARM_EGCR2_TCLER | NARM_EGCR2_TKICK, NARM_EGCR2_TCLER | NARM_EGCR2_TKICK);
        }
    #elif (PROCESSOR == ns7520) || (PROCESSOR == net50)
        /* Enable transmitter by acknowledging NRIP only. */
        /*
         *  Since all of the status bits are write 1 to clear, explicitly
         *  write zeroes to the ones that shouldn't be cleared
         */
        WriteRegMaskData( NARM_DMA_SIER2,
                          NARM_DSIER_NCIP | NARM_DSIER_NRIP |
                              NARM_DSIER_ECIP | NARM_DSIER_CAIP,
                          NARM_DSIER_NRIP );
    #else
        #error Unsupported Processor
    #endif
    } /* flags & ETH_TX_LAST_FLAG */

#ifdef NET_OS_SPECIFIC
    #if (PROCESSOR == ns9210) || (PROCESSOR == ns9215)
    naPdSetCatnapSlowClock();
    #endif
#endif
}

/*
 Function: ethPacketBufferCnt

 Description:

    Calculate number of DMA buffers needed for the transmission
    of the packet.
*/
static unsigned ethPacketBufferCnt(ttUserPacketPtr packetUPtr)
{
    unsigned buffer_cnt = 0;
    ttUserPacketPtr packet_ptr = packetUPtr;

    while(packet_ptr)
    {
    #if (PROCESSOR == ns7520) || (PROCESSOR == net50)
    /*
        If the buffer for an ARM7 processor is not 32 bit aligned,
        use 2 DMA buffers, unless this is the first buffer,
        or its length is < 4.
    */
        if (((unsigned int)packet_ptr->pktuLinkDataPtr & 3) &&
            (packet_ptr->pktuLinkDataLength > 3) &&
            (packet_ptr != packetUPtr))
        buffer_cnt++;
    #endif
        buffer_cnt++;
        packet_ptr = packet_ptr->pktuLinkNextPtr;
    }
    return buffer_cnt;
}


/*
 *  Function: naEthOneScatSend
 *
 *  Description:
 *      Called by the Treck stack to transmit a packet.
 */
int naEthOneScatSend(ttUserInterface interfaceHandle,
	                 ttUserPacketPtr packetUPtr)
{
    unsigned char *buffer;
    unsigned size;
#if	BSP_ETH_WIFI_BRIDGE
    ethData *eth_datap = bfDeviceGetPointer(interfaceHandle);
#else
    ethData *eth_datap = tfDeviceGetPointer(interfaceHandle);
#endif
    fb_buffer_desc_t *desc, *first_desc;
    ttUserPacketPtr packet_ptr = packetUPtr;
    ttUserPacketPtr next_ptr = NULL;
    unsigned buffer_cnt = 0;
    int level;

#if (PROCESSOR == ns7520) || (PROCESSOR == net50)
    unsigned int        non_aligned_bytes;
    unsigned char     * aligned_data_ptr, * new_aligned_data_ptr;
#endif

    NA_ASSERT((int)packetUPtr);

    /* Update statisticks */
    ++eth_datap->eth_stats.tx_transmit;

    /* Calculate number of DMA buffers for this packet */
    buffer_cnt = ethPacketBufferCnt(packetUPtr);
    /* Return an error if there is not enough free DMA buffers for this packet */
    level = NASetIRQ(IRQ_DISABLED);
    if ((eth_datap->tx_submitted + buffer_cnt) >= ETH_TCPIP_SEND_BUFFERS)
    {
        NASetIRQ(level);
        ++eth_datap->eth_stats.tx_full;
        return TM_DEV_ERROR;
    }
    else /* Update submitted count */
    {
        eth_datap->tx_submitted += buffer_cnt;
		desc = first_desc = eth_datap->tx_in;
#if BSP_ENABLE_ETHERNET_BYPASS
		{									// Have to advance a pointer to the end of the packet, because
		unsigned i;							// naEthBypassTransmit can be called in the middle of naEthOneScatSend
		for (i = 0; i < buffer_cnt; i++)
			eth_datap->tx_in = TX_NEXT(eth_datap->tx_in);
		}
#endif
        NASetIRQ(level);
    }

    TLog(15, TX_INDEX(desc));
	NA_ASSERT(TX_INDEX(desc) >= 0 && TX_INDEX(desc) < ETH_MAX_TX_DESCRIPTORS);
	NA_ASSERT(TX_INDEX(eth_datap->tx_in) >= 0 && TX_INDEX(eth_datap->tx_in) < ETH_MAX_TX_DESCRIPTORS);

    do {
        WORD32 flags = 0;
        buffer = packet_ptr->pktuLinkDataPtr;
        size = packet_ptr->pktuLinkDataLength;
    #if (PROCESSOR == ns7520) || (PROCESSOR == net50)
        if(((unsigned int)buffer & 3) && (packet_ptr != packetUPtr))
        {
            unsigned int k;

            /* For the ns7520, the first data buffer in a chain of DMA descriptors can be unaligned */
            /* Subsequent data buffers in the chain of DMA descriptors must be 4 byte aligned       */

            if (size < 4)
            {
                non_aligned_bytes = size;
                aligned_data_ptr = NULL;
                size = 0;
            }
            else
            {
                aligned_data_ptr = (unsigned char *) ((((unsigned int) buffer) + 3) & ~(3));
                non_aligned_bytes = aligned_data_ptr - buffer;
                size -= non_aligned_bytes;

            }
            new_aligned_data_ptr = (unsigned char *)(&buffers_for_unaligned_data[TX_INDEX(desc)]);

            for (k = 0; k < non_aligned_bytes; k++)
            {
                new_aligned_data_ptr[k] = buffer[k];
            }
            buffer = aligned_data_ptr;

            if (size == 0 && packet_ptr->pktuLinkNextPtr == NULL)
            {
                flags = buffer_cnt | ETH_TX_LAST_FLAG;
            }
            naEthTransmitPacketBuffer(eth_datap, desc, first_desc, new_aligned_data_ptr, non_aligned_bytes, flags);

            desc = TX_NEXT(desc);
            NA_ASSERT(TX_INDEX(desc) >= 0 && TX_INDEX(desc) < ETH_MAX_TX_DESCRIPTORS);
        }
    #endif  /* #if (PROCESSOR == ns7520) || (PROCESSOR == net50) */

        next_ptr = packet_ptr->pktuLinkNextPtr;
        if ((next_ptr == NULL) || (next_ptr->pktuLinkDataLength == 0))
        {
            flags = buffer_cnt | ETH_TX_LAST_FLAG;
        }
        naEthTransmitPacketBuffer(eth_datap, desc, first_desc, buffer, size, flags);
        desc = TX_NEXT(desc);
        NA_ASSERT(TX_INDEX(desc) >= 0 && TX_INDEX(desc) < ETH_MAX_TX_DESCRIPTORS);
        packet_ptr = packet_ptr->pktuLinkNextPtr;

    } while ((packet_ptr) && (packet_ptr->pktuLinkDataLength != 0));

    TLog(16, TX_INDEX(desc));

    return TM_DEV_OKAY;
}

#if (PROCESSOR == ns7520) || (PROCESSOR == net50)
/*
 *
 *  Function: void naEthTxReset(ethData *eth_datap)
 *
 *  Description:
 *
 *      This routine is called to reset the Ethernet transmitter when
 *      we sense it has locked up.  The reset procedure as described in
 *      the errata is:
 *
 *      1. Disable the DMA channel by setting the CE bit to 0 in the DMA Control
 *         register.
 *      2. Disable the transmit DMA and transmit FIFO by setting the ETXDMA and
 *         ETX bits to 0 in the Ethernet General Control register.
 *      3. Initialize the buffer descriptors and DMA buffer descriptor pointer.
 *      4. Enable the transmit DMA and transmit FIFO by setting the ETXDMA and
 *         ETX bits to 1 in the Ethernet General Control register.
 *      5. Enable the DMA channel by setting the CE bit to 1 in the DMA Control
 *         register.
 *
 *      In addition, we have to worry about buffers in the transmit DMA ring that
 *      have not been transmitted yet.  The DMA state machine will lose track of
 *      those buffers when we reset it.  The code that loads buffers in the transmit
 *      ring keeps track of where it is in the ring and where packets start and stop.
 *      The transmit ISR keeps track of where the hardware is in the ring.  When
 *      we reset the transmitter, we save any untransmitted buffers and reload them
 *      so they will be transmitted when we restart.
 *
 *  Parameters:
 *
 *      none
 *
 *  Return Values:
 *
 *      none
 *
 */
void naEthTxReset(ethData *eth_datap)
{
    unsigned idx, notCompleteCount = 0;
    volatile WORD32 dma_status;
    WORD32 savedState;
    fb_buffer_desc_t savedDescriptors[ETH_MAX_TX_DESCRIPTORS];
    WORD32  saved_flags[ETH_MAX_TX_DESCRIPTORS];

    /* disable proc. interrupts */
    savedState = ethMaskInterrupts(ETH_MASK_ALL_ETH_INTERRUPTS);

    /* increment reset count */
    eth_datap->eth_stats.transmitterResets++;

    /* acknolege transmit DMA interrupts*/
    dma_status = *NARM_DMA_SIER2;
    *NARM_DMA_SIER2 = dma_status;

    /* disable transmit FIFO and DMA */
    WriteRegMaskData(NARM_DMA_CR2, NARM_DMA_CR_CE, 0);
    WriteRegMaskData(NARM_EGCR, NARM_GCR_ETX, 0);
    WriteRegMaskData(NARM_EGCR, NARM_GCR_ETXDMA, 0);

/*
 * eth_datap->ethTxPacketFixTail indicates the last packet the hardware sent out,
 * and eth_datap->ethTxPacketFixHead indicates the last packet the transmit
 * routine loaded into the transmit DMA ring.  If those are not equal, then we
 * have untransmitted packets.  We have to save those before we reset everything
 * so we can reload them.
 */
    if (eth_datap->ethTxPacketFixTail != eth_datap->ethTxPacketFixHead)
    {
        WORD32 start = eth_datap->ethTxPacketStart[eth_datap->ethTxPacketFixTail];
        /*
         * Calculate end so that it is the index to the buffer just past the last
         * buffer in the last packet.  The for loop below will copy up to but not
         * including this buffer.
         */
        WORD32 end = ETH_PREV_TX_PACKET_INDEX(eth_datap->ethTxPacketFixHead);
        end = eth_datap->ethTxPacketEnd[end];

/*
 * Copy the descriptors that have not been transmitted yet.  Restore the length fields
 * since the DMA engine can screw up the length field of the descriptor it is
 * currently working on.
 */
        for (idx = start; idx != end; idx = ETH_NEXT_TX_PACKET_INDEX(idx))
        {
            saved_flags[notCompleteCount] = eth_datap->txPackets[idx].flags;
            savedDescriptors[notCompleteCount] = eth_tx_buffer_descriptors[idx];
            savedDescriptors[notCompleteCount].word2 &= ~DMA_BUFLEN_MASK;
            savedDescriptors[notCompleteCount++].word2 |= eth_datap->txPackets[idx].size & DMA_BUFLEN_MASK;
        }
    }

/*
 * naEthResetTxBufferDescriptors() resets the transmit buffer descriptors as well as
 * eth_datap->ethTxPacketFixTail, eth_datap->ethTxPacketFixHead.
 */
    naEthResetTxBufferDescriptors(eth_datap);

/*
 * Reload any packets that were not transmitted before.
 */
    if (notCompleteCount != 0)
    {
        int newPacket = TRUE;

        for (idx = 0; idx < notCompleteCount; idx++)
        {
            if (newPacket)
            {
                eth_datap->ethTxPacketStart[eth_datap->ethTxPacketFixHead] = idx;
                newPacket = FALSE;
            }
            eth_tx_buffer_descriptors[idx] = savedDescriptors[idx];
            eth_tx_buffer_descriptors[idx].word2 |= DMA_FLAG2_FULL;
            /* eth_datap->txPackets[].size holds buffer lengths.  The restored buffers are probably
             * in a different location, so update eth_datap->txPackets[].size as we go */
            eth_datap->txPackets[idx].size = savedDescriptors[idx].word2 & DMA_BUFLEN_MASK;
            eth_datap->txPackets[idx].flags = saved_flags[idx];

            if (savedDescriptors[idx].word1 & DMA_FLAG1_LAST)
            {
                newPacket = TRUE;
                eth_datap->ethTxPacketEnd[eth_datap->ethTxPacketFixHead] = ETH_NEXT_TX_PACKET_INDEX(idx);
                eth_datap->ethTxPacketFixHead = ETH_NEXT_TX_PACKET_INDEX(eth_datap->ethTxPacketFixHead);
            }
        }
    }

/*
 * eth_datap->tx_in points to the next tx descriptor the transmit routine will use.  Update that
 * to point to the buffer after the last one we loaded (if any).
 */
    eth_datap->tx_in = &eth_tx_buffer_descriptors[eth_datap->ethTxPacketFixHead];
    /* enable transmit FIFO and DMA */
    WriteRegMaskData(NARM_EGCR, NARM_GCR_ETXDMA, NARM_GCR_ETXDMA);
    WriteRegMaskData(NARM_EGCR, NARM_GCR_ETX, NARM_GCR_ETX);
    WriteRegMaskData(NARM_DMA_CR2, NARM_DMA_CR_CE, NARM_DMA_CR_CE);

    if (notCompleteCount != 0)
    { /* if we reloaded some tx packets, then kick the transmitter */
        WriteRegMaskData( NARM_DMA_SIER2,
                          NARM_DSIER_NCIP | NARM_DSIER_NRIP |
                              NARM_DSIER_ECIP | NARM_DSIER_CAIP,
                          NARM_DSIER_NRIP );
    }
    /* restore processor interrupts */
    ethRestoreInterrupts(savedState);
}

/*
 *
 *  Function: void naEthCheckTransmit(ethData *eth_datap)
 *
 *  Description:
 *
 *      This routine detects an Ethernet transmitter lockup condition.  We look for the
 *      condition described in "Ethernet transmitter considerations" section of the
 *      NS7520 errata.  According to the errata the transmitter is locked up when:
 *
 *       o  TXCOLC or TXAUR bits in the Ethernet Transmitter Status register are
 *          set to 1.
 *       o  Full bit in the current DMA buffer descriptor is set to 1.
 *       o  Status field in the DMA buffer descriptor is set to 0.
 *
 *      We assume the lock up condition has occurred if we see this condition happen twice
 *      in a row when no packets going out.
 *
 *  Parameters:
 *
 *      eth_datap - pointer to ethernet driver context
 *
 *  Return Values:
 *
 *      none
 *
 */
void naEthCheckTransmit(ethData *eth_datap)
{
    fb_buffer_desc_t *tx_desc = TX_ADDR(ReadRegMask(DMA_BYTEOFFSET_ETH_TX, DMA_BYTEOFFSET_MASK));
    static fb_buffer_desc_t *saved_tx_desc = NULL;
    fb_buffer_desc_t *new_saved_tx_desc = NULL;
    static unsigned savedTxIsrCompleted;

    if (   (ReadRegMask(NARM_ETSR, NARM_ETSR_TXAUR) != 0)
        || (ReadRegMask(NARM_ETSR, NARM_ETSR_TXCOLC_MASK) != 0))
    {
        if (   (ReadRegMask(DMA_FLAG2(tx_desc), DMA_FLAG2_FULL) != 0)
            && (ReadRegMask(DMA_FLAG2(tx_desc), DMA_FLAG2_STATUS_MASK) == 0)
            && (eth_datap->txIsrCompleted == savedTxIsrCompleted))
        {
            if (saved_tx_desc == tx_desc)
            {
/*
 * Looks like we have a lockup.  All of the conditions described in the errata have been met
 * twice in a row, and we are still on the same packet for over 800 milliseconds.  Call the
 * reset routine.
 */
	            ++eth_datap->eth_stats.tx_reset;
                naEthTxReset(eth_datap);
            }
            else
            {
/*
 * If we branch here, then we might have a lock up.  Save the current buffer describe and
 * wait until we are called again.  If the hardware is still in the same state and we are
 * still looking at the same packet, then we will reset the transmitter.
 */
                new_saved_tx_desc = tx_desc;
            }
        }

        savedTxIsrCompleted = eth_datap->txIsrCompleted;
        saved_tx_desc = new_saved_tx_desc;
    }
}
#endif /* #if (PROCESSOR == ns7520) || (PROCESSOR == net50) */

/*
 *
 *  Function: naEthTxIsr
 *
 *  Description:
 *
 *  Ethernet transmit DMA interrupt service routine
 */
void naEthTxIsr(void *context)
{
    ethData *eth_datap = (ethData *)context;
    WORD32 dma_status;

    unsigned long  idx, last;
    unsigned long tx_count = 0, bytesSent = 0;
    unsigned long tx_total_count, buffer_cnt = 0;
#if BSP_ENABLE_ETHERNET_BYPASS
    unsigned long tx_bypass_count = 0, bypassBytesSent = 0;
#endif
    unsigned long lastIdx;
#if (PROCESSOR == ns9750) || (PROCESSOR == ns9360) || (PROCESSOR == ns9210) || (PROCESSOR == ns9215)
    unsigned long startIdx;
#endif

    TLog(17, 0);

    ++eth_datap->eth_stats.tx_ints;

#if (PROCESSOR == ns9750) || (PROCESSOR == ns9360) || (PROCESSOR == ns9210) || (PROCESSOR == ns9215)
    /* read EFE interrupt status register */
    /* TxIdle is not cleared; checked in eth_packet_transmit to restart */
    dma_status = ReadRegMask(NARM_EFE_ISR, NARM_EFE_IR_TX_MASK);
    EthLogTx(eth_datap, 0x1, dma_status);

     /* acknowledge the interrupt */
    WriteReg(NARM_EFE_ISR, dma_status);

    if( dma_status & NARM_EFE_IR_TXERR)
    {
       unsigned long flag;
       /* Recover a DMA buffer following an error buffer */

       /* If the previous buffer descriptor did not complete, the Offset register will not
          have the correct value, in particular, if the buffer is a chained buffer.
          - Walk through the remaining BD for the errored packet and clear out the F bits.
          - If the next BD after the errored packet has the F bit set (its ready to go)
            then load the TX Recover Ptr with location of the BD and toggle TCLER.
          - If the next BD after the errored packet does not have the F bit set then do nothing.
            When the next packet is finally ready it should just get kicked off normally.
       */
	    idx = ReadRegMask(NARM_EFE_TXEBDPR, NARM_EFE_TXEBDPR_OFFSET_MASK) >> 2;
		startIdx = idx;

        do
        {
            WriteRegMaskData(DMA_FLAG2(&eth_tx_buffer_descriptors[idx]), DMA_FLAG2_FULL, 0);
            flag = eth_tx_buffer_descriptors[idx].flag;
            if (flag & EFE_WRAP_MASK)
                idx = 0;
            else
                idx++;

			/* once around is enough */
			if (idx == startIdx)
				break;
        } while ((flag & EFE_LAST_MASK) == 0);

        if (ReadRegMask(DMA_FLAG2(&eth_tx_buffer_descriptors[idx]), DMA_FLAG2_FULL))
        {
            WriteRegMaskData(NARM_EFE_TXRBDPR, NARM_EFE_TXRBDPR_OFFSET_MASK, idx << 2);

            /* Clear Error Conditions and wake up transmitter */
            WriteRegMaskData(NARM_EGCR2, NARM_EGCR2_TCLER | NARM_EGCR2_TKICK, 0);
            WriteRegMaskData(NARM_EGCR2, NARM_EGCR2_TCLER | NARM_EGCR2_TKICK, NARM_EGCR2_TCLER | NARM_EGCR2_TKICK);
        }
    }
#elif (PROCESSOR == ns7520) || (PROCESSOR == net50)
    /* read DMA status register */
    dma_status = ReadReg(NARM_DMA_SIER2);

    EthLogTx(eth_datap, 0x1, dma_status);

     /* acknowledge the interrupt */
    WriteReg(NARM_DMA_SIER2, dma_status);

    /* Process Error interrupts */
    if( dma_status & NARM_DSIER_CAIP )           /* channel abort read it then clear it*/
        panic("CAIP in Ethernet transmit interrupt.");

    if (dma_status & NARM_DSIER_ECIP)            /* check for error interrupt */
        panic("ECIP in Ethernet transmit interrupt.");
#else
    #error Unsupported Processor
#endif

    /* Process the buffer closed interrupt */
    if (dma_status & ETH_TX_CLOSED_MASK)
    {
        /*
         * The Treck stack needs to be given an accurate count of the number of packets
         * transmitted, otherwise the stack can hang.  The receive task sometimes
         * transmits packets in response to ones received, and then pends on notification of
         * the transmit complete.  If this ISR does not report the correct number of
         * transmitted packets, then it's possible the receive task will be left waiting
         * on a semaphore, and so the stack will crash.  Treck tech support also says that
         * buffers are not freed until the transmit ISR says the transmit is complete, so
         * underreporting the transmit buffers will cause a memory leak.  This section of
         * code determines the correct number of transmitted packets the old fashion way:
         * by going through the transmit buffer ring and counting them.
         */
        idx = eth_datap->txIsrSavedIdx;
        /*
         * Determine where we are in the transmit ring.  It's okay if more transmits
         * occur after we read this register because we have already acknowledged the
         * interrupt.  If we complete another buffer before we finish this loop, another
         * interrupt will be generated.  We CANNOT simply go through the buffer ring
         * until we encounter a packet with the full bit set since the entire ring may
         * be empty.
         */
        lastIdx = ReadRegMask(ETH_TX_HWINDEX_REG, ETH_TX_HWINDEX_MASK) >> ETH_TX_HWINDEX_SHIFT;

        while (idx != lastIdx)
        {
            do
            {
                volatile fb_buffer_desc_t * desc = &eth_tx_buffer_descriptors[idx];
                WORD32 flags = eth_datap->txPackets[idx].flags;

            #if BSP_ENABLE_ETHERNET_BYPASS
                if (flags & ETH_TX_BYPASS_FLAG)
                    bypassBytesSent += eth_datap->txPackets[idx].size;
                else
            #endif
                    bytesSent += eth_datap->txPackets[idx].size;

                eth_datap->txPackets[idx].size  = 0;
                eth_datap->txPackets[idx].flags = 0;

                last = ReadRegMask(DMA_FLAG1(desc), DMA_FLAG1_LAST);
                if (last)
                {
                    WORD32 status = ReadRegMask(DMA_FLAG2(desc), DMA_FLAG2_STATUS_MASK) >> DMA_FLAG2_STATUS_SHIFT;

                    /* Update error statisticks */
                    eth_datap->eth_stats.tr_colc += status & NARM_ETSR_TXCOLC_MASK;
                    if (status & NARM_ETSR_TXAL) eth_datap->eth_stats.tr_late_collisions++;
                    if (status & NARM_ETSR_TXAED) eth_datap->eth_stats.tr_excess_deferrals++;
                    if (status & NARM_ETSR_TXAEC) eth_datap->eth_stats.tr_excess_collisions++;
                    if (status & NARM_ETSR_TXAUR) eth_datap->eth_stats.tr_under_run++;

                    /*
                     * Increment the packets sent count everytime we encounter
                     * a buffer with the LAST bit set. Count bypass packets separately.
                     */
                #if BSP_ENABLE_ETHERNET_BYPASS
                    if (flags & ETH_TX_BYPASS_FLAG)
                        tx_bypass_count++;
                    else
                #endif
                    {
                        buffer_cnt += flags & ETH_TX_BUFFER_CNT_MASK;
                        tx_count++;
                    }
                }
                if (ReadRegMask(DMA_FLAG1(desc), DMA_FLAG1_WRAP))
                    idx = 0;
                else
                    idx++;

            } while ((last == 0) && (idx != lastIdx));
        }
        /* If transmit activity was detected, light the LED */
        naEthLEDMarkActivity();
        eth_datap->txIsrSavedIdx = idx;
    }
    eth_datap->tx_submitted -= buffer_cnt;
    tx_total_count = tx_count;
#if BSP_ENABLE_ETHERNET_BYPASS
    tx_total_count += tx_bypass_count;
#endif

#if (PROCESSOR == ns7520) || (PROCESSOR == net50) || (PROCESSOR == net40)
/*
 * Update index tail pointer to take into account packets we have completely transmitted.
 */
    for (idx = 0; idx < tx_total_count; idx++)
    {
        eth_datap->ethTxPacketFixTail = ETH_NEXT_TX_PACKET_INDEX(eth_datap->ethTxPacketFixTail);
    }
#endif
    EthLogTx(eth_datap, 0x2, tx_count);

    if (tx_count > 0)
    {
	    /* We'll need to signal transmit complete thread with the
           transmitted packet and byte count (non-bypass packets)
        */
#if	BSP_ETH_WIFI_BRIDGE
        bfNotifyInterfaceIsr(eth_datap->interfaceHandle,
		                     0, tx_count, bytesSent, 0);
#else
        tfNotifyInterfaceIsr(eth_datap->interfaceHandle,
		                     0, tx_count, bytesSent, 0);
#endif
    }
#if BSP_ENABLE_ETHERNET_BYPASS
    if (tx_bypass_count > 0)
    {
	    /* We'll need to signal bypass transmit complete thread with the
           transmitted bypass packet count
        */
        naEthNotifyTxBypass(tx_bypass_count);
        bytesSent += bypassBytesSent;
    }

#endif
    /* Update transmitted count for check for lockup */
    eth_datap->txIsrCompleted += tx_total_count;

    /* Update byte statisticks */
    eth_datap->eth_stats.bytes_transmitted += bytesSent;

    TLog(18, 0);
}

/*
 *
 *  Function: int naEthResetTxBufferDescriptors(ethData *eth_datap)
 *
 *  Description:
 *
 *      This routine re-initializes transsmit DMA buffer descriptors to be empty.
 *      It is called from both initialization and transmit lockup reset routine.
 *
 *  Parameters:
 *
 *      none
 *
 *  Return Values:
 *
 *     0    - success.
 *
 */
void naEthResetTxBufferDescriptors(ethData *eth_datap)
{
#if (PROCESSOR == ns9750) || (PROCESSOR == ns9360) || (PROCESSOR == ns9210) || (PROCESSOR == ns9215)
    WriteReg(NARM_EFE_TXBDPR, 0);
#endif

    /* setup the tx buffer descriptors, all empty */
#if (PROCESSOR == ns9750) || (PROCESSOR == ns9360) || (PROCESSOR == ns9210) || (PROCESSOR == ns9215)
    /*
        ns9750 and ns9360 processors have transmit buffer descriptors in on-chip RAM.
        Write burst does not work on this memory, and as the result memset must not be
        used on this memory. Green Hills memset uses multiple resister access instructions.
        Memcpy also must not be used on this memory.
    */
    {
        int i;
        for (i = 0; i < ETH_MAX_TX_DESCRIPTORS; i++)
        {
            eth_tx_buffer_descriptors[i].src_addr = 0;
            eth_tx_buffer_descriptors[i].buf_len  = 0;
            eth_tx_buffer_descriptors[i].dst_addr = 0;
            eth_tx_buffer_descriptors[i].flag     = 0;
        }
    }
#elif (PROCESSOR == ns7520) || (PROCESSOR == net50)
    /*
        ns7520 and net50 don't have on-chip RAM, transmit buffer descriptors are
        in on-board SDRAM, memset can be used to clear them.
    */
    memset(eth_tx_buffer_descriptors, 0, sizeof(eth_tx_buffer_descriptors[0])*ETH_MAX_TX_DESCRIPTORS);
#else
    #error Unsupported Processor
#endif

    /* set the wrap bit in the last descriptor */
    WriteRegMaskData(DMA_FLAG1(&eth_tx_buffer_descriptors[ETH_MAX_TX_DESCRIPTORS-1]), DMA_FLAG1_WRAP, DMA_FLAG1_WRAP);

#if (PROCESSOR == ns9750) || (PROCESSOR == ns9360) || (PROCESSOR == ns9210) || (PROCESSOR == ns9215)
    /* Reset a DMA buffer descriptor */
    WriteRegMaskData(NARM_EFE_TXRBDPR, NARM_EFE_TXRBDPR_OFFSET_MASK, 0);
#elif (PROCESSOR == ns7520) || (PROCESSOR == net50)
    WriteReg(NARM_DMA_BDP2, (unsigned int)&eth_tx_buffer_descriptors[0]);
#else
    #error Unsupported Processor
#endif

    /* initialize software head and tail indexes */

    eth_datap->tx_in = eth_tx_buffer_descriptors;
    memset(eth_datap->txPackets, 0, sizeof (eth_datap->txPackets));

#if (PROCESSOR == ns7520) || (PROCESSOR == net50) || (PROCESSOR == net40)
    eth_datap->ethTxPacketFixHead = 0;
    eth_datap->ethTxPacketFixTail = 0;
    memset(eth_datap->ethTxPacketStart, 0, sizeof (eth_datap->ethTxPacketStart));
    memset(eth_datap->ethTxPacketEnd, 0, sizeof (eth_datap->ethTxPacketEnd));
#endif
}




