/*****************************************************************************
* 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 "naeth.h"
#include "eth.h"
#include "nammu.h"

#include "NA_ISR.h"
#include "tlog.h"
#include "mii.h"
#include "Npttypes.h"
#if	BSP_ETH_WIFI_BRIDGE 
#include "ewb_api.h"
#endif
#include "tx_api.h"

/*
 * Receive DMA Buffer descriptors rings for channels A, B, C, and D
 */
#define ETH_TOTAL_RX_BD (ETH_MAX_RXA_DESCRIPTORS + \
						 ETH_MAX_RXB_DESCRIPTORS + \
						 ETH_MAX_RXC_DESCRIPTORS + \
						 ETH_MAX_RXD_DESCRIPTORS)

#if (PROCESSOR == ns9750) || (PROCESSOR == ns9360) || (PROCESSOR == ns9210) || (PROCESSOR == ns9215)

/* 
 * To avoid any cache coherency issues DMA buffers and buffer descriptors must be aligned to the start of a 
 * cache line and the size must be multiple of the cache line size (32 bytes). Allocating even number of 
 * buffer descriptors 16 bytes each assures the size requirement.
 */
#define ETH_ALIGN_DMA_BUFFER        32
#define ETH_ADJUST_FOR_CACHE(size) 	(((size) + 31) & ~31)

#if (ETH_TOTAL_RX_BD & 1)
#define ETH_TOTAL_RX_BD_FORCACHE	(1 + ETH_TOTAL_RX_BD)
#else
#define ETH_TOTAL_RX_BD_FORCACHE	ETH_TOTAL_RX_BD
#endif

#if defined(NDS_SPECIFIC) || defined(NETOS_GNU_TOOLS)
static fb_buffer_desc_t eth_rx_bd[ETH_TOTAL_RX_BD_FORCACHE] __attribute__ ((aligned(32)));
#else
#pragma alignvar (32)
static fb_buffer_desc_t eth_rx_bd[ETH_TOTAL_RX_BD_FORCACHE];
#endif 

#elif (PROCESSOR == ns7520) || (PROCESSOR == net50)

#define ETH_ALIGN_DMA_BUFFER        4
#define ETH_ADJUST_FOR_CACHE(size) 	size

#define ETH_TOTAL_RX_BD_FORCACHE	ETH_TOTAL_RX_BD
static fb_buffer_desc_t eth_rx_bd[ETH_TOTAL_RX_BD_FORCACHE];

#else
    #error Unsupported Processor
#endif


/*
 * Array Pointers to Receive DMA Buffer descriptor rings 
 * for channels A, B, C, and D
 */
static fb_buffer_desc_t *eth_rx_buffer_descriptors[ETH_RX_CHANNELS] =
{
    &eth_rx_bd[0]
#if ETH_RX_CHANNELS > 1
  , &eth_rx_bd[ETH_MAX_RXA_DESCRIPTORS]
#if ETH_RX_CHANNELS > 2
  , &eth_rx_bd[ETH_MAX_RXA_DESCRIPTORS+ETH_MAX_RXB_DESCRIPTORS]
#if ETH_RX_CHANNELS > 3
  , &eth_rx_bd[ETH_MAX_RXA_DESCRIPTORS+ETH_MAX_RXB_DESCRIPTORS+ETH_MAX_RXC_DESCRIPTORS]
#endif
#endif
#endif
};

/* 
 * Receive Buffers descriptor ring sizes 
 * for channels A, B, C, and D
 */
static int ETH_MAX_RX_DESCRIPTORS[4] =
{
    ETH_MAX_RXA_DESCRIPTORS,
    ETH_MAX_RXB_DESCRIPTORS,
    ETH_MAX_RXC_DESCRIPTORS,
    ETH_MAX_RXD_DESCRIPTORS
};

static unsigned long ETH_RX_MAX_LEN[4] = 
{
    ETH_RXA_MAX_PACKET_LEN,
    ETH_RXB_MAX_PACKET_LEN,
    ETH_RXC_MAX_PACKET_LEN,
    ETH_RXD_MAX_PACKET_LEN
};


#if (PROCESSOR == ns9750) || (PROCESSOR == ns9360) || (PROCESSOR == ns9210) || (PROCESSOR == ns9215)

#define DMA_STATUS_REG	    NARM_EFE_ISR
#define DMA_STATUS_RX_MASK  NARM_EFE_IR_RX_MASK

#elif (PROCESSOR == ns7520) || (PROCESSOR == net50)

#define DMA_STATUS_RX_MASK  (~(NARM_DSIER_NRIP))

static unsigned long volatile * DMA_STATUS_REG[4] = 
    {   NARM_DMA_SIER1A, 
        NARM_DMA_SIER1B, 
        NARM_DMA_SIER1C, 
        NARM_DMA_SIER1D };

static unsigned long volatile * DMA_COLTROL_REG[4] = 
    {   NARM_DMA_CR1A, 
        NARM_DMA_CR1B, 
        NARM_DMA_CR1C, 
        NARM_DMA_CR1D };
#else
    #error Unsupported Processor
#endif

static unsigned long DMA_BYTEOFFSET_ETH_RX[4] = 
    {   DMA_BYTEOFFSET_ETH_RXA, 
        DMA_BYTEOFFSET_ETH_RXB, 
        DMA_BYTEOFFSET_ETH_RXC, 
        DMA_BYTEOFFSET_ETH_RXD
    };


#define RX_INDEX(rxd, channel)	    ((fb_buffer_desc_t *)(rxd) - eth_rx_buffer_descriptors[channel])
#define RX_ADDR(byteoffset, channel) ((fb_buffer_desc_t *)(((char *)eth_rx_buffer_descriptors[channel])+(byteoffset)))
#define RX_NEXT(rxd, channel)	    (ReadRegMask(DMA_FLAG1(rxd), DMA_FLAG1_WRAP) ? eth_rx_buffer_descriptors[channel] : ((fb_buffer_desc_t *)(rxd) + 1))

#define RX_NEXT_CHANNEL(channel) (((channel+1) < ETH_RX_CHANNELS) ? channel+1 : 0)

static void ethRxReset(ethData *eth_datap);
static void ethFillRxBufferDescriptors(ethData *eth_datap, int all);
static void ethUpdateRxStatistics(WORD32 status, ETH_STATS * stats);
static void ethResetRxDescriptor(fb_buffer_desc_t *desc, unsigned int channel, char *buffer);

#define ETH_RX_SHORT_RESET  0x80

#if ETH_LOG_ENABLED
#define EL_SIZE    256
struct {
    DWord op;
    DWord channel;
    DWord status;
    DWord rx_out;
    DWord rx_isr_out;
    DWord rx_idx;
} el_rx[EL_SIZE];
int elindex_rx;
int elwrap_rx;



void EthLogRx(ethData *eth_datap, DWord operation, DWord status, DWord channel)
{
    int level = NASetIRQ(IRQ_DISABLED);
    el_rx[elindex_rx].op = operation;
    el_rx[elindex_rx].status = status;
    el_rx[elindex_rx].channel = channel;
    el_rx[elindex_rx].rx_out = eth_datap->rx_out[channel] - eth_rx_buffer_descriptors[channel];
    el_rx[elindex_rx].rx_isr_out = eth_datap->rx_isr_out[channel] - eth_rx_buffer_descriptors[channel];
    el_rx[elindex_rx].rx_idx = ReadRegMask(DMA_BYTEOFFSET_ETH_RX[channel], DMA_BYTEOFFSET_MASK) / sizeof(fb_buffer_desc_t);
    ++elindex_rx;
    if (elindex_rx >= EL_SIZE)
    {
	elwrap_rx = 1;
	elindex_rx = 0;
    }
    NASetIRQ(level);
}
#endif


void naEthRxIsr(void *context)
{
    ethData *eth_datap = (ethData *)context;
    fb_buffer_desc_t *hw_desc;
    WORD32  dma_status;
    WORD32  channel;
    int cnt, rx_count = 0;
    
    ++eth_datap->eth_stats.rx_ints;
    
#if (PROCESSOR == ns9750) || (PROCESSOR == ns9360) || (PROCESSOR == ns9210) || (PROCESSOR == ns9215)

    dma_status = ReadReg(DMA_STATUS_REG);

    if (dma_status & NARM_EFE_IR_RXOVFL_DATA)
    {
        if ((dma_status & NARM_EFE_IR_RXDONE) == 0)
        {
            /*
             * If the EFE_ISR_RXOVFL_DATA_MASK bit is set, then the receiver front
             * end has apparently locked up.  Set a flag that tells the receiver 
             * thread to reset it.  We check the DONE bits to try to empty the
             * receive rings of packets before we reset the receiver.  Note
             * that once we get into this lockup state, the EFE_ISR_RXOVFL_DATA_MASK
             * interrupt will happen continously until we reset the receiver.
             */
            WriteRegMaskData(NARM_EFE_IER, NARM_EFE_IR_RXOVFL_DATA, 0);
            eth_datap->rx_reset_scheduled = 1;
        }
    }

    /* acknowledge the interrupt*/
    WriteReg(DMA_STATUS_REG, dma_status & DMA_STATUS_RX_MASK);

    if (dma_status & (NARM_EFE_IR_RXNOBUF | NARM_EFE_IR_RXBUFFUL))
    {
        ++eth_datap->eth_stats.rx_no_buffer;
    }

    if (dma_status & (NARM_EFE_IR_RXDONE | NARM_EFE_IR_RXNOBUF | NARM_EFE_IR_RXBUFFUL))
#endif

    for (channel = 0; channel < ETH_RX_CHANNELS; channel++)
    {
    #if (PROCESSOR == ns7520) || (PROCESSOR == net50)
        /* read DMA status register */
        dma_status = ReadReg(DMA_STATUS_REG[channel]);

        if (dma_status & NARM_DSIER_ECIP)                         /* ECIP */
        {
	        panic("Ethernet receive: ECIP Interrupt 1,1");
        }

        if ((dma_status & (NARM_DSIER_NRIP | NARM_DSIER_NRIE)) == (NARM_DSIER_NRIP | NARM_DSIER_NRIE))
        {   /* if NRIP has occurred */
    	    /* disable interrupt for now so that we don't keep getting it */
    	    dma_status &= ~NARM_DSIER_NRIE;
    	    ++eth_datap->eth_stats.rx_nrip;

            eth_datap->rx_reset_scheduled |= 1 << channel;
        }

        /* acknowledge the interrupt*/
        WriteReg(DMA_STATUS_REG[channel], dma_status & DMA_STATUS_RX_MASK);

        /* Received a packet on this channel */
        if (dma_status & NARM_DSIER_NCIP)
        {
            if ((dma_status & NARM_DSIER_NRIP) == 0)
            {
               /* Continue to receive - cancel reset for this channel and 
                  re-enable NRIP interrupt - this must be done after
                  acknowledging the interrupt, otherwise will cause a lockup
                */
                WriteRegMaskData( DMA_STATUS_REG[channel],
        	        NARM_DSIER_NCIP | NARM_DSIER_NRIP |
        	        NARM_DSIER_ECIP | NARM_DSIER_CAIP | NARM_DSIER_NRIE,
        	        NARM_DSIER_NRIE );
                eth_datap->rx_reset_scheduled &= ~(1 << channel);
            }
    #elif (PROCESSOR == ns9750) || (PROCESSOR == ns9360) || (PROCESSOR == ns9210) || (PROCESSOR == ns9215)
        /* Received a packet on this channel */
        if (dma_status & ((NARM_EFE_IR_RXDONEA >> channel) | NARM_EFE_IR_RXNOBUF | NARM_EFE_IR_RXBUFFUL))
        {
    #endif
            EthLogRx(eth_datap, 0x11, dma_status, channel);

            /* We count all descriptors that the dma engine has completed on this channel */
            hw_desc = RX_ADDR(ReadRegMask(DMA_BYTEOFFSET_ETH_RX[channel], DMA_BYTEOFFSET_MASK), channel);
            cnt = hw_desc - eth_datap->rx_isr_out[channel];
            TLog(1, RX_INDEX(hw_desc, channel));

			#if ETH_RX_DMA_STATS
			if (dma_status & NARM_EFE_IR_RXNOBUF)
				++eth_datap->eth_stats.rx_isr_nobuf;
				
			if (dma_status & NARM_EFE_IR_RXBUFFUL)
				++eth_datap->eth_stats.rx_isr_full;
			#endif

            if (cnt < 0)
                cnt += ETH_MAX_RX_DESCRIPTORS[channel];

            if (cnt == 0)
            {
               if (ReadRegMask(DMA_FLAG2(hw_desc), DMA_FLAG2_FULL))
               {
                   cnt = ETH_MAX_RX_DESCRIPTORS[channel];
               #if ETH_RX_DMA_STATS
                   ++eth_datap->eth_stats.rx_all_full[channel];
               #endif                       
               }
            #if ETH_RX_DMA_STATS
               else ++eth_datap->eth_stats.rx_isr_empty[channel];
            #endif
            }
            EthLogRx(eth_datap, 0x12, cnt, channel);

            NA_ASSERT(cnt >= 0 && cnt <= ETH_MAX_RX_DESCRIPTORS[channel]);

            if ((cnt + (int)eth_datap->rx_service_count[channel]) > ETH_MAX_RX_DESCRIPTORS[channel] + 1)
            {
                 cnt = (ETH_MAX_RX_DESCRIPTORS[channel] - eth_datap->rx_service_count[channel]) > 0 ? 
                       ETH_MAX_RX_DESCRIPTORS[channel] - eth_datap->rx_service_count[channel] : 0;
            }
            if (cnt)
            {
                /* Update number of packets per dma channel and calculate 
                   total number of packets for all dma channels */
                eth_datap->rx_service_count[channel] += cnt;
                rx_count += cnt;
            }
#if BSP_ENABLE_ETHERNET_INTERRUPT_FILTER
			{
				// Call an Inteterrupt filter for each received packet 
				fb_buffer_desc_t *desc = eth_datap->rx_isr_out[channel];
				int count = 0;

				do 
				{
					if (ReadRegMask(DMA_FLAG2(desc), DMA_FLAG2_FULL))
						naEthIsrFilter(desc);
					desc = RX_NEXT(desc, channel);
					count++;
				} while (desc != hw_desc && count < ETH_MAX_RX_DESCRIPTORS[channel]);
			}
#endif
            /* Save the descriptor where h/w DMA index */
            eth_datap->rx_isr_out[channel] = hw_desc;
        }
    }  
    
    if (eth_datap->rx_reset_scheduled && rx_count == 0)
    {
        /* Unblock the thread if reset is pending */ 
        rx_count++;
    }
#if ETH_RX_DMA_STATS
	eth_datap->eth_stats.rx_isr_notified += rx_count;
#endif
    if (rx_count)
    {
        /* We'll need to signal receive task to process this channel */
#if	BSP_ETH_WIFI_BRIDGE
        bfNotifyInterfaceIsr(eth_datap->interfaceHandle, rx_count, 0, 0, 0);
#else
        tfNotifyInterfaceIsr(eth_datap->interfaceHandle, rx_count, 0, 0, 0);
#endif
        /* And light the led. */
        naEthLEDMarkActivity();
    }
}

#define VERY_SHORT_PACKET_LENGTH        (30)
int naEthRecvPacket(ttUserInterface      * interfaceHandle,
                                    char TM_FAR * TM_FAR * dataPtr,
	                                int TM_FAR *           dataLength,
	                                ttUserBufferPtr        bufHandlePtr)
{
    WORD32  channel = 0;
    int level, cnt = 0;
    fb_buffer_desc_t *desc, *hw_desc;
    int result = TM_DEV_ERROR;
	/* It is possible for this to get called too early, before
	 * eth_datap is set up */
#if	BSP_ETH_WIFI_BRIDGE
    ethData *eth_datap = bfDeviceGetPointer(interfaceHandle);
#else
    ethData *eth_datap = tfDeviceGetPointer(interfaceHandle);
#endif
    eth_datap->rx_bypass = NULL;

#if ETH_RX_CHANNELS == 1
    level = NASetIRQ(IRQ_DISABLED);
    if ((cnt = eth_datap->rx_service_count[channel]))
        eth_datap->rx_service_count[channel]--;
    NASetIRQ(level);
#else
    /* Choose a receive DMA channel to process */
    channel = eth_datap->rx_present_channel;

    /* If there are still packets on the rx_present_channel, since last 
       looked, continue with this channel 
    */
    if (eth_datap->rx_channel_cnt)
    {
        level = NASetIRQ(IRQ_DISABLED);
        if ((cnt = eth_datap->rx_service_count[channel]))
            eth_datap->rx_service_count[channel]--;
		eth_datap->rx_channel_cnt--;
        NASetIRQ(level);
    }
    /* Otherwise switch the channel. Might end up with the same channel */
    if (cnt == 0) do
    {
       channel = RX_NEXT_CHANNEL(channel);
       level = NASetIRQ(IRQ_DISABLED);

        if ((cnt = eth_datap->rx_service_count[channel]))
        {
            eth_datap->rx_service_count[channel]--;
            eth_datap->rx_channel_cnt = cnt-1;
            eth_datap->rx_present_channel = channel;
			NASetIRQ(level); 
            break;
        }
        else
            NASetIRQ(level); 

    } while (channel != eth_datap->rx_present_channel);
#endif
    /* If cnt == 0, naEthReceive was, probably, called to reset a lockup */
    if (cnt)
    {
        desc = eth_datap->rx_out[channel];
        TLog(2, RX_INDEX(desc, channel));

        if (!ReadRegMask(DMA_FLAG2(desc), DMA_FLAG2_FULL))
        {
            hw_desc = RX_ADDR(ReadRegMask(DMA_BYTEOFFSET_ETH_RX[channel], DMA_BYTEOFFSET_MASK), channel);

            /* 
             * Sometimes the ARM7 DMA system will increment it's DMA index when there is no
             * packet.  If we see that we have received all the full buffers but we are still not
             * caught up to the DMA system, then just skip over the empty buffers.
             */
            if (desc != hw_desc)
            {
            #if ETH_RX_DMA_STATS
                ++eth_datap->eth_stats.dma_skipped[channel];
            #endif
                eth_datap->rx_out[channel] = RX_NEXT(desc, channel);
            }
            else
            {
            #if ETH_RX_DMA_STATS
                ++eth_datap->eth_stats.rx_not_full[channel];
            #endif
            }
        }
        else
        {
            unsigned size = ReadRegMask(DMA_BUFLEN(desc), DMA_BUFLEN_MASK);
            WORD32 dma_status = ReadRegMask(DMA_FLAG2(desc), DMA_FLAG2_STATUS_MASK) >> DMA_FLAG2_STATUS_SHIFT;
            char *buffer, *new_buffer = NULL;
            unsigned int  idx, i;
            ttUserBuffer  new_handle, handle;

        #if (PROCESSOR == ns7520) || (PROCESSOR == net50)
            if (size < VERY_SHORT_PACKET_LENGTH)
            {
                /* Occasionally a really short received packet will lockup the DMA,
                   populating the entire DMA ring with buffer descriptors with 0 status 
                */ 
                WriteRegMaskData(NARM_EGCR, NARM_EGCR_ERXDMA | NARM_EGCR_ERX, 0);
                eth_datap->rx_reset_scheduled |= ETH_RX_SHORT_RESET;
                ++eth_datap->eth_stats.rx_really_short;
                if (dma_status)
                    dma_status &= ~DMA_STATUS_RXOK;
                else
                    ++eth_datap->eth_stats.rx_drop;
            }
        #endif
            ++eth_datap->eth_stats.rx_receive;
            ethUpdateRxStatistics(dma_status, &eth_datap->eth_stats);
               
            #if ETH_RX_DMA_STATS
            if (ReadRegMask(DMA_FLAG1(desc), DMA_FLAG1_WRAP))
    	        ++eth_datap->eth_stats.rx_wrap;
            #endif
           
            idx = RX_INDEX(desc, channel);

        #if (PROCESSOR == ns9750) || (PROCESSOR == ns9360) || (PROCESSOR == ns9210) || (PROCESSOR == ns9215) 
            if (dma_status & DMA_STATUS_RXOK)
        #elif (PROCESSOR == ns7520) || (PROCESSOR == net50)
            /*
             * The ARM7 based chips don't handle dribble errors correctly.  These errors indicate the
             * packet has extra bits at the end.  However, the packet is still okay as long as the
             * CRC is valid.  On ARM7 processor we set the NARM_EGCR_ERXBAD bit (in eth_reset()), and
             * then check for the dribble error bit here.  Packets are valid on the ARM7 if either
             * DMA_STATUS_RXOK is set, or if DMA_STATUS_RXDR is set and there is no CRC error.
             */
            if ((dma_status & DMA_STATUS_RXOK) || ((dma_status & (DMA_STATUS_RXCRC | DMA_STATUS_RXDR)) == DMA_STATUS_RXDR))
        #else
            #error Unsupported processor
        #endif
            {
                ethFilterActionType action = ethFilterActionDiscard;

                buffer = eth_datap->rxPackets[channel][idx].buffer;

                NAAfterDMA(NA_DEVICE_TO_MEMORY_DMA, NULL, buffer, size);

                /* Now we return the packet.  The ethernet header is aligned, so
                 * we must move it up for Treck.  This also fits the requirement
                 * that the received packets must be placed on a 4 byte+2
                 * alignment. 7 shorts (14 bytes) must be moved up 2 bytes.
                 */
                for (i = 7; i; --i)
                    ((unsigned short *)buffer)[i] = ((unsigned short *)buffer)[i-1];

                size -= 2;
                buffer += 2;
                handle = eth_datap->rxPackets[channel][idx].handle;
                eth_datap->eth_stats.bytes_received += size;

                /* naEthKeepThisPacket calls MUX or Bypass filter */
                action = naEthKeepThisPacket(buffer, size, dma_status);

                if (action != ethFilterActionDiscard)
                {
                    new_buffer = tfGetDriverBuffer(&new_handle, 
                                   ETH_ADJUST_FOR_CACHE(ETH_RX_MAX_LEN[channel]), ETH_ALIGN_DMA_BUFFER);
                    if (new_buffer)
                    {
                        eth_datap->rxPackets[channel][idx].handle = new_handle;
                        eth_datap->rxPackets[channel][idx].buffer = new_buffer;
                    
                        ++eth_datap->eth_stats.rec_passed_up;
                        eth_datap->eth_stats.rec_bytes_passed_up += size;
                        if (dma_status & DMA_STATUS_RXMC) 
                           ++eth_datap->eth_stats.multicast_passed_up;
                        
                        if (action == ethFilterActionForward)
                        {
                            *bufHandlePtr = handle;
                            *dataPtr      = buffer;
                            *dataLength   = size;
                            result        = TM_DEV_OKAY;        

                        }
                        else 
                            eth_datap->rx_bypass = handle;
                    }
                    else
                    {
                        ++eth_datap->eth_stats.rec_no_proto_buffer;
                    }
                }
                /* Need to call NABeforeDMA, even if the packet dumped,
                   because the packet data has been touched */
                NABeforeDMA(NA_DEVICE_TO_MEMORY_DMA, NULL, 
                            eth_datap->rxPackets[channel][idx].buffer, 
                            ETH_RX_MAX_LEN[channel]);
                
            } /* dma_status & DMA_STATUS_RXOK */
            else
            {
                if (dma_status)
                    ++eth_datap->eth_stats.rx_drop;
            }

            /* Reset the descriptor to empty to allow reuse. */
            ethResetRxDescriptor(desc, channel, eth_datap->rxPackets[channel][idx].buffer);

        #if (PROCESSOR == ns9750) || (PROCESSOR == ns9360) || (PROCESSOR == ns9210) || (PROCESSOR == ns9215)
            /* Let DMA know we freed the buffer */
            WriteRegMaskData(NARM_EFE_RXFBR, (NARM_EFE_RXFBR_A << channel), MASK_ON);
        #elif (PROCESSOR == ns7520) || (PROCESSOR == net50)
            /* Enable receiver (by clearing NRIP only). */
            WriteRegMaskData( DMA_STATUS_REG[channel],
    	                        NARM_DSIER_NCIP | NARM_DSIER_NRIP |
    	                        NARM_DSIER_ECIP | NARM_DSIER_CAIP,
    	                        NARM_DSIER_NRIP );
        #else
            #error Unsupported Processor
        #endif
            desc = RX_NEXT(desc, channel);

            /* Go to the next buffer descriptor */
            eth_datap->rx_out[channel] = desc;
            if (result == TM_DEV_OKAY)
            {
	            TLog(3, RX_INDEX(desc, channel));
	        }

       
        } /* ReadRegMask(DMA_FLAG2(desc), DMA_FLAG2_FULL) */
    }

    /* Reset DMA if reset is pending and all packets have been processed */
    if (eth_datap->rx_reset_scheduled)
    {
        cnt = 0;

        level = NASetIRQ(IRQ_DISABLED);

        for (channel = 0; channel < ETH_RX_CHANNELS; channel++)
            cnt += eth_datap->rx_service_count[channel];

        if (cnt == 0)
        {
            WriteRegMaskData(NARM_EGCR, NARM_EGCR_ERXDMA | NARM_EGCR_ERX, 0);
            NASetIRQ(level);

            ethRxReset(eth_datap);
        }
        else
            NASetIRQ(level);
    }

    return result;
}

#if (!BSP_USE_ETHERNET_RECV_QUEUE)

int naEthReceive(ttUserInterface interfaceHandle,
	char TM_FAR * TM_FAR * dataPtr,
	int  TM_FAR *          dataLength,
	ttUserBufferPtr        bufHandlePtr
	)
{
    
    return naEthRecvPacket(interfaceHandle, dataPtr, dataLength, bufHandlePtr);
}
#endif



void naEthCheckLink(ethData *eth_datap)
{
    int newLinkState;
    NA_UNUSED_PARAM(eth_datap);
    
    newLinkState = customizeMiiCheckLink();
        
    if (eth_datap->link_state != newLinkState)
    {
    #ifdef ETH_ENABLE_LINK_MONITOR
        naEthSetLinkEvent();
    #else        
        eth_link_changed(eth_datap);
    #endif
        eth_datap->link_state = newLinkState;
    }
}


/*
 *
 *  Function: int naEthResetRxBufferDescriptors(ethData *eth_datap)
 *
 *  Description:
 *
 *      This routine re-initializes receive DMA buffer descriptors to bu full.
 *      It does not change DMA buffer pointers.
 *      It is called from both initialization and receive lockup reset routine.
 *
 *  Parameters:
 *
 *      none
 *
 *  Return Values:
 *
 *     0    - success.
 *
 */

void naEthResetRxBufferDescriptors(ethData *eth_datap)
{
    int i, channel;

    /* re-initialize receive buffer descriptors for all DMA rings */

    for (channel = 0; channel < ETH_RX_CHANNELS; channel++)
    {
        for (i = 0; i < ETH_MAX_RX_DESCRIPTORS[channel];  i++)
        {
            char *buffer = (char *)ReadRegMask(DMA_SBP(&eth_rx_buffer_descriptors[channel][i]), DMA_SBP_MASK);
            ethResetRxDescriptor(&eth_rx_buffer_descriptors[channel][i], channel, buffer);
         }
        /* set WRAP bit in the last buffer descriptor of the DMA ring */
	    WriteRegMaskData(DMA_FLAG1(&eth_rx_buffer_descriptors[channel][ETH_MAX_RX_DESCRIPTORS[channel]-1]), 
                                   DMA_FLAG1_WRAP, DMA_FLAG1_WRAP);
    
        /* reset the software index for channel 0 */
    #if (PROCESSOR == ns9750) || (PROCESSOR == ns9360) || (PROCESSOR == ns9210) || (PROCESSOR == ns9215)
        /* TODO: Why this here?  This needs review, since eth_fill hasn't been called yet! */
            /* Inform receiver that buffer descriptor is available */
	    WriteRegMaskData(NARM_EFE_RXFBR, NARM_EFE_RXFBR_A << channel, NARM_EFE_RXFBR_A << channel);
    #endif

    eth_datap->rx_isr_out[channel] = 
    eth_datap->rx_out[channel] =  eth_rx_buffer_descriptors[channel];
    }
    eth_datap->rx_reset_scheduled = 0;
    eth_datap->rx_present_channel = 0;
    eth_datap->rx_channel_cnt = 0;
}

int naEthInitRxBufferDescriptors(ethData *eth_datap)
{
    int channel;

    ETH_RX_MAX_LEN[ETH_RX_CHANNELS -1] = ETH_RX_MAX_PACKET_LEN;

	NAInvalidateBuffer(eth_rx_bd, sizeof (eth_rx_bd));

    for (channel = 0; channel < ETH_RX_CHANNELS; ++channel) 
	    eth_rx_buffer_descriptors[channel] = NA_UNCACHED(eth_rx_buffer_descriptors[channel]);
    
    /*
     * setup the rx buffers & the rx buffer descriptors for the four receive
     * dma channels
     */

    /* Clear the memory for receive buffer descriptors */
    memset(NA_UNCACHED(eth_rx_bd), 0, sizeof (eth_rx_bd));

#if (PROCESSOR == ns7520) || (PROCESSOR == net50)
	/* Called during reset when enabled. */
    /* Initialize all fields, but the buffer pointers */
    naEthResetRxBufferDescriptors(eth_datap);
#endif

    /* Allocate DMA buffers, and initialize buffer pointers */
    ethFillRxBufferDescriptors(eth_datap, /* all */ 1);
    return 0;
}

/*
 *
 *  Function: void ethFillRxBufferDescriptors(ethData *eth_datap, int all)
 *
 *  Description:
 *
 *      This routine allocates receive DMA buffers.
 *      It is called from both initialization and receive lockup reset routine.
 *      If all is non-zero, all buffers are allocated, otherwise only zero
 *      buffers are allocated.
 *
 *  Parameters:
 *
 *      none
 *
 *  Return Values:
 *
 *      none
 *
 */
static void ethFillRxBufferDescriptors(ethData *eth_datap, int all)
{
#if (PROCESSOR == ns9750) || (PROCESSOR == ns9360) || (PROCESSOR == ns9210) || (PROCESSOR == ns9215)
    /* address of Rx DMA channel A DBDP register */
    volatile unsigned long *bd_addr = NARM_EFE_RXABDPR;
#elif (PROCESSOR == ns7520) || (PROCESSOR == net50)
    volatile unsigned long *bd_addr = NARM_DMA_BDP1A; /* address of DMA channel 1A DBDP register */
#else
    #error Unsupported Processor
#endif
    char *pbuf;
    int i, channel;

    /* Allocate receive buffers for all DMA rings */

    for (channel =0; channel < ETH_RX_CHANNELS; channel++)
    {
	    physical_address_t phys;

        for (i = 0; i < ETH_MAX_RX_DESCRIPTORS[channel];  i++)
        {
	        if (all || ReadRegMask(DMA_SBP(&eth_rx_buffer_descriptors[channel][i]),
				   DMA_SBP_MASK) == 0)
			{ 
		    /* put pointer to data buffer in rx buffer descriptor */

               if ((pbuf = tfGetDriverBuffer(&eth_datap->rxPackets[channel][i].handle,
				                ETH_ADJUST_FOR_CACHE(ETH_RX_MAX_LEN[channel]), ETH_ALIGN_DMA_BUFFER)) == 0)
		            return;

                eth_datap->rxPackets[channel][i].buffer = pbuf;
		        if (!NAVaToPhys((void *)pbuf, &phys))
		            panic_num("Unable to map rx data buffer to physical space!", (unsigned long)pbuf, 0);
		        NABeforeDMA(NA_DEVICE_TO_MEMORY_DMA, NULL, pbuf, ETH_RX_MAX_LEN[channel]);
		        WriteRegMaskData(DMA_SBP(&eth_rx_buffer_descriptors[channel][i]), DMA_SBP_MASK, phys);
	        }
        }

        /* Initialize receive buffer descriptor pointer register with physical
	     * address of receive buffer.
	     */
	    if (!NAVaToPhys(&eth_rx_buffer_descriptors[channel][0], &phys))
	        panic_num("Unable to map eth_rx_buffer_descriptor to physical space!", (int)&eth_rx_buffer_descriptors[channel][0], channel);

        *bd_addr = phys;

        /* Get the address of the next DBDP register for the next channel */
    #if (PROCESSOR == ns9750) || (PROCESSOR == ns9360) || (PROCESSOR == ns9210) || (PROCESSOR == ns9215)
	    ++bd_addr;
    #elif (PROCESSOR == ns7520) || (PROCESSOR == net50)
        bd_addr += NARM_DMA_BDP1B - NARM_DMA_BDP1A;
    #else
        #error Unsupported Processor
    #endif
    }
}

/* 
 * Reset the descriptor to empty to allow reuse. 
 * Leave the buffer pointer ias is.
 */
static void ethResetRxDescriptor(fb_buffer_desc_t *desc, unsigned int channel, char *buffer)
{
    physical_address_t phys;

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

    WriteRegMaskData(DMA_SBP(desc), DMA_SBP_MASK, phys);

#if (PROCESSOR == ns9750) || (PROCESSOR == ns9360) || (PROCESSOR == ns9210) || (PROCESSOR == ns9215)
    WriteRegMaskData(DMA_FLAG1(desc), DMA_FLAG1_LAST, DMA_FLAG1_LAST);
//	    DMA_FLAG1_INTR|DMA_FLAG1_LAST, DMA_FLAG1_INTR|DMA_FLAG1_LAST);
    WriteRegMaskData(DMA_FLAG1(desc),
	    DMA_FLAG1_INTR|DMA_FLAG1_LAST, DMA_FLAG1_INTR|DMA_FLAG1_LAST);
    /* The "last" bit is an enable bit in 9750 */
#elif (PROCESSOR == ns7520) || (PROCESSOR == net50)
    WriteRegMaskData(DMA_FLAG1(desc),
	    DMA_FLAG1_INTR|DMA_FLAG1_LAST, 0);
#else
    #error Unsupported Processor
#endif

    WriteRegMaskData(DMA_FLAG2(desc), DMA_FLAG2_STATUS_MASK, 0);
    WriteRegMaskData(DMA_BUFLEN(desc), DMA_BUFLEN_MASK, ETH_RX_MAX_LEN[channel]);

    /* Make this descriptor available again. */
    WriteRegMaskData(DMA_FLAG2(desc), DMA_FLAG2_FULL, 0);
}

/*
 * Reset DMA if apparently locked up.
 */
#if (PROCESSOR == ns9750) || (PROCESSOR == ns9360) || (PROCESSOR == ns9210) || (PROCESSOR == ns9215)
/*
 * The ns9750/ns9360 Ethernet receivers can lock up and require a reset.  Here is the 
 * description of the problem from the ns9750 Errata.
 *
 * The Ethernet receiver intermittently locks up in 100 Mbps half-duplex applications
 * due to an overflow in the RX data FIFO. The Ethernet Interrupt Status register
 * indicates this condition by setting the RX_OVFL_DATA bit.
 * 
 * Workaround: Reset the RX Ethernet logic (EN_RXOVFL_DATA bit in the Ethernet
 * Interrupt Enable register) when an RX_OVFL_DATA interrupt is generated.
 * The Ethernet driver should use these steps to recover from the stall condition.
 * 
 *     1 Clear the ERX bit in Ethernet General Control Register #1.
 *     2 Clear the ERXDMA bit in Ethernet General Control Register #1.
 *     3 Clear the RXEN bit in MAC Configuration Register #1.
 *     4 Clear the RX_OVFL_DATA bit in the Ethernet Interrupt Status register by
 *       writing a 1 to the bit.
 *     5 If packets were received before the stall condition and are waiting to be
 *       serviced, service them as usual.
 *     6 Reinitialize the Ethernet receive DMA rings.
 *     7 Reload the RX_A Buffer Descriptor Pointer register, RX_B Buffer Descriptor
 *       Pointer register, RX_C Buffer Descriptor Pointer register, and RX_D Buffer
 *       Descriptor Pointer register.
 *     8 Clear the RXINIT bit in the Ethernet General Status register by writing a 1 to
 *       the bit.
 *     9 Set the ERX bit in Ethernet General Control Register #1.
 *     10 Set the ERXINIT bit in Ethernet General Control Register #1.
 *     11 Wait for the RXINIT bit in the Ethernet General Status register to be set.
 *     12 Clear the ERXINIT bit in Ethernet General Control Register #1.
 *     13 Set the ERXDMA bit in Ethernet General Control Register #1.
 *     14 Set the RXEN bit in MAC Configuration Register #1.
 *     
 * The Ethernet receive DMA indexes, for all rings, are reset to zero by the procedure.
 * The Ethernet driver’s internal variables must be updated accordingly before the
 * driver services any packets after executing this procedure.
 * 
 */
static void ethRxReset(ethData *eth_datap)
{
    #define RXINIT_TIMEOUT      (100000)                /* this number is arbitrary */
    WORD32 bufferDescriptorPointer[4];
    unsigned timeoutIndex;

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

/*
 * Save DMA buffer descriptor table pointers.
 */
    bufferDescriptorPointer[0] = ReadReg(NARM_EFE_RXABDPR);
    bufferDescriptorPointer[1] = ReadReg(NARM_EFE_RXBBDPR);
    bufferDescriptorPointer[2] = ReadReg(NARM_EFE_RXCBDPR);
    bufferDescriptorPointer[3] = ReadReg(NARM_EFE_RXDBDPR);
    
/*
 * Turn off Ethernet receiver and Ethernet DMA controller.  This has the
 * side effect of zeroing the DMA buffer descriptor pointers.
 */
    WriteRegMaskData(NARM_EGCR, NARM_EGCR_ERX | NARM_EGCR_ERXDMA, 0);
    WriteRegMaskData(NARM_EMAC1CFR, NARM_EMAC1CFR_RXEN, 0);
 
/*
 * Restore the DMA buffer descriptor pointers.
 */
    WriteReg(NARM_EFE_RXABDPR, bufferDescriptorPointer[0]);
    WriteReg(NARM_EFE_RXBBDPR, bufferDescriptorPointer[1]);
    WriteReg(NARM_EFE_RXCBDPR, bufferDescriptorPointer[2]);
    WriteReg(NARM_EFE_RXDBDPR, bufferDescriptorPointer[3]);
    
/*
 * Reset and refill the buffer descriptors.
 */
    naEthResetRxBufferDescriptors(eth_datap);
    ethFillRxBufferDescriptors(eth_datap, /* all */ 0);

/*
 * Reinitialize the receiver.
 */
    WriteRegMaskData(NARM_EGSR, NARM_EGSR_RXINIT, NARM_EGSR_RXINIT);
    WriteRegMaskData(NARM_EGCR, NARM_EGCR_ERX | NARM_EGCR_ERXINIT, 
                                NARM_EGCR_ERX | NARM_EGCR_ERXINIT);

    /*
     * Wait for Ethernet module to reinitialize.  It should set the RXINIT bit when it has.  This should take
     * no more than 5 microseconds.  This should never fail.  The timeout loop is just precaution.
     */
    for (timeoutIndex = 0; (timeoutIndex < RXINIT_TIMEOUT) && (ReadRegMask(NARM_EGSR, NARM_EGSR_RXINIT) == 0); timeoutIndex++)
    {
    }
    
    if (ReadRegMask(NARM_EGSR, NARM_EGSR_RXINIT) == 0)
    {
        panic("ethRxReset(): NARM_EGSR_RXINIT was never set");
    }
    
/*
 * Turn the Ethernet receiver back on again.
 */
    WriteRegMaskData(NARM_EGCR, NARM_EGCR_ERXINIT| NARM_EGCR_ERXDMA, NARM_EGCR_ERXDMA); 
    WriteRegMaskData(NARM_EMAC1CFR, NARM_EMAC1CFR_RXEN, NARM_EMAC1CFR_RXEN);

    WriteRegMaskData(NARM_EFE_IER, NARM_EFE_IR_RXOVFL_DATA, NARM_EFE_IR_RXOVFL_DATA);
}
#elif (PROCESSOR == ns7520) || (PROCESSOR == net50)
/*
 * This routine resets the Ethernet receiver on the NS7520 and NET50 processors.  The receiver
 * can lockup if the system runs out of buffers.  Here is the description of the lockup 
 * condition and the recovery procedure from section titled "Ethernet receiver considerations" 
 * in the DMA Module chapter of the NS7520 hardware reference.
 *
 * An Ethernet receive FIFO overrun condition can occur (the FIFO becomes full while
 * receiving an Ethernet packet) if insufficient buffers are allocated by the application. If 
 * this condition occurs (signaled by the NRIP bit in the DMA Channel 1 Status register),
 * you must use this procedure to guarantee successful operation:
 *
 *     1 Set the ERXDMA, in the Ethernet General Control register, to zero.
 *     2 SET the ERX bit, in the Ethernet General Control register, to zero.
 *     3 Set the CE bit, in the DMA Control register, to zero.
 *     4 Add new buffers for Ethernet DMA.
 *     5 Restore the DMA Control register.
 *     6 Restore the ERX bit.
 *     7 Restore the ERXDMA bit.
 */
static void ethRxReset(ethData *eth_datap)
{
    int    channel;
    fb_buffer_desc_t *hw_descr;

    /* Disable Receive Fifo, DMA in EGCR, and all receive DMA channels */
    WriteRegMaskData(NARM_EGCR, NARM_EGCR_ERXDMA | NARM_EGCR_ERX, 0);
    /* acknowlege receive DMA interrupts and enable NRIP interrupt */
    for (channel = 0; channel < ETH_RX_CHANNELS; channel++)
    {
	    WriteRegMaskData(DMA_COLTROL_REG[channel], NARM_DMA_CR_CE, 0);
	    WriteReg(DMA_STATUS_REG[channel],
	            ReadRegMask(DMA_STATUS_REG[channel], 
                    NARM_DSIER_NCIP | NARM_DSIER_ECIP | NARM_DSIER_NRIP | NARM_DSIER_CAIP)
	       	      | (NARM_DSIER_NCIE | NARM_DSIER_ECIE | NARM_DSIER_NRIE | NARM_DSIER_CAIE));
    }
    /* increment reset count */
    eth_datap->eth_stats.rx_reset++;

    /* reset DMA buffer descriptors and */
    /* fill empty rx descriptors. */
    naEthResetRxBufferDescriptors(eth_datap);
    ethFillRxBufferDescriptors(eth_datap, /* all */ 0);
   
    for (channel = 0; channel < ETH_RX_CHANNELS; channel++)
    {
#if (PROCESSOR == ns7520)
        if (channel != 0)
#endif
        {
           /* During a lockup reset ns7520 as a rule does not reset 
             hardware DMA index to the beggining of the DMA ring for 
             channels B, C, and D. This needs to be further researched
             TODO: NET+50 needs to be verified.
            */
           hw_descr = RX_ADDR(ReadRegMask(DMA_BYTEOFFSET_ETH_RX[channel], DMA_BYTEOFFSET_MASK), channel);
           eth_datap->rx_isr_out[channel] = eth_datap->rx_out[channel] = hw_descr;
        }
   	    WriteRegMaskData(DMA_COLTROL_REG[channel], NARM_DMA_CR_CE, NARM_DMA_CR_CE);
    }
    /* Enable Receive Fifo, DMA in EGCR, and all receive DMA channels */

    WriteRegMaskData(NARM_EGCR, NARM_EGCR_ERXDMA | NARM_EGCR_ERX,
	                            NARM_EGCR_ERXDMA | NARM_EGCR_ERX);
}
#else
    #error Unsupported processor
#endif

static void ethUpdateRxStatistics(WORD32 status, ETH_STATS * stats)
{
    /* TODO: The Ethernet module in the chip (all but ns7520) collects statistics, which
     * could be used instead, speeding up the driver.
     */
    if (status)
    {
        if (status & DMA_STATUS_RXCE)
	        ++stats->rx_carrierevent;
        if (status & DMA_STATUS_RXDV)
	        ++stats->rx_dataevent;
        if (status & DMA_STATUS_RXOK)
	        ++stats->rx_frames;
        if (status & DMA_STATUS_RXBR)
	        ++stats->rx_broadcast;
        if (status & DMA_STATUS_RXMC)
	        ++stats->rx_multicast;

    #if (PROCESSOR == ns7520) || (PROCESSOR == net50)
        if (status & DMA_STATUS_RXCRC)
	        ++stats->rx_crcerror;

        /* NOTE: For RMII on ns9750_1, the dribble bit should be ignored. */
        if (status & DMA_STATUS_RXDR)
	        ++stats->rx_dribble;

        if (status & DMA_STATUS_RXCV)
	        ++stats->rx_codeviolation;
        if (status & DMA_STATUS_RXLNG)
	        ++stats->rx_long;
        if (status & DMA_STATUS_RXSHT)
	        ++stats->rx_short;
        if (status & DMA_STATUS_RXOVER)
	        ++stats->rx_overrun;
    #endif
    }
}


