/*            
 *            Copyright (c) 1998-2000 by NETsilicon Inc.
 *
 *  This software is copyrighted by and is the sole property of 
 *  NETsilicon.  All rights, title, ownership, or other interests
 *  in the software remain the property of NETsilicon.  This
 *  software may only be used in accordance with the corresponding
 *  license agreement.  Any unauthorized use, duplication, transmission,  
 *  distribution, or disclosure of this software is expressly forbidden.
 *
 *  This Copyright notice may not be removed or modified without prior
 *  written consent of NETsilicon.
 *
 *  NETsilicon, reserves the right to modify this software
 *  without notice.
 *
 *  NETsilicon
 *  411 Waverley Oaks Road                  USA 781.647.1234
 *  Suite 227                               http://www.netsilicon.com
 *  Waltham, MA 02452                       AmericaSales@netsilicon.com
 *
 *************************************************************************
 *
 *     Module Name: eth_watchdog.c
 *         Version: 1.00
 *   Original Date: 10-28-2002
 *          Author: Julia Rakhvalska
 *        Language: Ansi C
 * Compile Options: 
 * Compile defines: 
 *       Libraries: 
 *    Link Options: 
 *
 *    Entry Points: eth_reset1_rx()
 *                  eth_reset2_rx()
 *                  eth_watchdog()
 *                  eth_reset_tx()
 *
 * Description.
 * =======================================================================
 * This file contains routines that clear Ethernet lockup conditions. 
 *                                                                        
 *                                                                        
 * Edit Date/Ver   Edit Description
 * ==============  ===================================================
 * 09-24-04  CHG    Put 20us delay back into eth_reset_rx() routine. 
 *                  The description of the errata says it's needed.
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "Npttypes.h"
#include "reg_def.h"
#include "Dma_def.h"
#include "NA_ISR.h"
#include "Efe_def.h"
#include "eth.h"
#include "std.h"
#include "sysClock.h"


/*** external definitions */
extern fb_buffer_desc_t eth_tx_buffer_descriptors[];  /* transmit DMA buffer descriptors */

/* constants used by the receive watchdog */
#define ETH_CHECK_DSIER  0xdf000000
#define ETH_CHECK_EGSR   0xf3000000
#define ETH_EGSR_RXBR    0x02000000

/*
 *
 *  Function: unsigned long delay20us(void)
 *
 *  Description:
 *
 *      This routine implements 20 us delay.
 *      The delay guaranteed to be >= than 20 us.
 *
 *  Parameters:
 *
 *      none
 *
 *  Return Values:
 *
 *      unused
 *
 */
static unsigned long delay20us(void)

{
    unsigned long clockRate;
    //int pllValue = (*NCC_GEN).pllcr.bits.pllcnt;
    int pllValue = narm_read_reg (NARM_GPLR_REG, NARM_GPLR_ADDR, pllcnt);
    int cyclesPerIteration, cyclesToExecute;
    int numberIterations, iteration;
    int dummyReturnValue = 0;
   
/*
 * Determine our clock rate from the PLL setting and our crystal speed.
 */  

    clockRate = NAgetSysClkFreq();
    cyclesToExecute = (clockRate * 20) / 1000000;

/*
 * Now figure out how many times we should execute the loop.  I got these numbers
 * by looking at the code the compiler produced for the loop.  There were 9 add/sub
 * instructions and 1 branch.  If cache is on, then the each pass in the loop should
 * take about 12 cycles.  If cache is off, then its hard to tell how many cycles each
 * iteration will take.  It depends upon the type of memory used.  We can
 * guesstimate that it will take about 3 times as long though.
 */
    //if ((*NCC_GEN).scr.bits.cache)
    if (narm_read_reg (NARM_GSCR_REG, NARM_GSCR_ADDR, cache))
    {
        cyclesPerIteration = 12;
    }
    else
    {
        cyclesPerIteration = 36;
    }
    numberIterations = cyclesToExecute / cyclesPerIteration;

/*
 * This is the delay loop.  The dummyReturnValue is there so that the compiler
 * does not optimize the loop away.
 */
    for (iteration = 0; iteration < numberIterations; iteration++)
    {
        dummyReturnValue += iteration + cyclesToExecute;
    }

    return dummyReturnValue;
}






/*
 *
 *  Function: void eth_reset2_rx(void)
 *
 *  Description:
 *
 *      Part 2 of Ethernet receive lockup reset. 
 *      Resets buffer descriptors and enables Receive Fifo, 
 *      DMA in EGCR, and all receive DMA channels 
 *
 *  Parameters:
 *
 *      none
 *
 *  Return Values:
 *
 *      none
 *
 */
void eth_reset_rx(void)
{
    volatile WORD32 dma_status;
    int  channel;
    volatile unsigned long *dsr_addr   = NARM_DSIER1A_ADDR;
    volatile unsigned long *dcr_addr   = NARM_DCR1A_ADDR;

    /* Disable Receive Fifo, DMA in EGCR, and all receive DMA channels */
    narm_write_reg (NARM_EGCR_REG, NARM_EGCR_ADDR, erxdma, 0);
    narm_write_reg (NARM_EGCR_REG, NARM_EGCR_ADDR, erx, 0);

    delay20us();                        /* dshort delay */


    /* acknolege receive DMA interrupts and enable NRIP interrupt */
    for (channel = 0; channel < ETH_RX_CHANNELS; channel++)
    {
        narm_write_reg (NARM_DCR_REG, dcr_addr, ce, 0);
        dma_status = (*dsr_addr & DMA_IP_MASK) | DMA_IE_MASK;
        *dsr_addr  = dma_status;

        dcr_addr += NARM_DCR1B_ADDR - NARM_DCR1A_ADDR;
        dsr_addr += NARM_DSIER1B_ADDR - NARM_DSIER1A_ADDR;
    }
    /* reset DMA buffer descriptors and */
    eth_reset_rx_buffers_descriptors();

    /* increment reset count */
    eth_stats.receiverResets++;

    /* Enable Receive Fifo, DMA in EGCR, and all receive DMA channels */
    dcr_addr   = NARM_DCR1A_ADDR;
    for (channel = 0; channel < ETH_RX_CHANNELS; channel++)
    {
        narm_write_reg (NARM_DCR_REG, dcr_addr, ce, 1);
        dcr_addr += NARM_DCR1B_ADDR - NARM_DCR1A_ADDR;
    }
    narm_write_reg (NARM_EGCR_REG, NARM_EGCR_ADDR, erx, 1);
    narm_write_reg (NARM_EGCR_REG, NARM_EGCR_ADDR, erxdma, 1);
  
}

static int RX_RESET_FROM_WATCHDOG = 0; /* debug count */

/*
 *
 *  Function: void eth_watchdog(void)
 *
 *  Description:
 *
 *      This routine detects en Ethernet receiver lockup condition.
 *
 *  Parameters:
 *
 *      none
 *
 *  Return Values:
 *
 *      none
 *
 */
void eth_watchdog(void)
{
    static unsigned int packet_count = 0;
    static int check_again = 0;
    int channel, new_packet_cnt = 0;
    static void *last_tx_queue_head = NULL;
    static int last_xmitted_cnt = 0;
    static int tx_lockup_cnt = 0;
    int max_tx_lockup_cnt;
    void *tx_queue_head;

#define MAX_100_LOCKUP_CNT 1
#define MAX_10_LOCKUP_CNT  10
     
    if (eth_datap->state != ETH_ENABLED)
        return;

    /****** Transmit watchdog **********/

    /* Get The head of transmit queue */
    
    tx_queue_head = eth_get_tx_queue_head();
    max_tx_lockup_cnt = eth_datap->efe_mii_100 ? MAX_100_LOCKUP_CNT: MAX_10_LOCKUP_CNT;

    /* If there are packets to transmit and the head of transmit 
       queue did not change from the last_tx_queue_head, 
       and transmitted packet count did not change, reset the 
       transmitter 
    */
    if (tx_queue_head && tx_queue_head == last_tx_queue_head &&
        last_xmitted_cnt == eth_datap->xmitted_buffer_cnt)
    {
        if (++tx_lockup_cnt >= max_tx_lockup_cnt)
        {
            tx_lockup_cnt = 0;
            eth_task_wake(ETH_TX_TASK, ETH_TX_LOCKUP_FLAG);
        }
    }
    else
        tx_lockup_cnt = 0;

    last_tx_queue_head = tx_queue_head;              /* update last_tx_queue_head */
    last_xmitted_cnt = eth_datap->xmitted_buffer_cnt;

    /****** Receive watchdog **********/

    /* Calculate number of received packets */
    for (channel = 0; channel < ETH_RX_CHANNELS; channel++)
        new_packet_cnt += eth_stats.received[channel];
    
    /* If received new packets, no lockup */ 
    if (packet_count != new_packet_cnt)
    {
        packet_count = new_packet_cnt;
        check_again = 0;
        return;
    }
    /* There are no NCIP, ECIP,or CAIP interrupts pending, 
       RXFDB indicates a full word available in the FIFO, 
       RXBR is set, and RXSKIP is not set - lockup condition
    */
    if ((*NARM_DSIER1A_ADDR & ETH_CHECK_DSIER) == 0 &&       
        (*NARM_DSIER1B_ADDR & ETH_CHECK_DSIER) == 0 &&
        (*NARM_DSIER1C_ADDR & ETH_CHECK_DSIER) == 0 &&
        (*NARM_DSIER1D_ADDR & ETH_CHECK_DSIER) == 0 &&
        (*NARM_EGSR_ADDR & ETH_CHECK_EGSR) == ETH_EGSR_RXBR)
    {
        if (check_again == 0)
        {
             check_again = 1;                /* andcheck again */
            return;
        }
        /* Receiver has locked up - reset it */
 
        RX_RESET_FROM_WATCHDOG++;
        check_again = 0;
        
        /* Wake up the receive task to finish the lockup recet */
        eth_task_wake(ETH_RX_TASK, ETH_WATCHDOG_FLAG);
    }
}

/*
 *
 *  Function: void eth_reset_tx(void)
 *
 *  Description:
 *
 *      Transmit lockup reset.
 *      Disables transmit FIFO and transmit DMA, resets DMA buffer 
 *      descriptors and enables transmit FIFO and transmit DMA.
 *
 *  Parameters:
 *
 *      none
 *
 *  Return Values:
 *
 *      none
 *
 */
void eth_reset_tx(void)
{
    int index, cnt = 0;
    volatile WORD32 dma_status;
    use_critical;
    
    critical;

    /* increment reset count */
    eth_stats.transmitterResets++;
   
    /* acknolege transmit DMA interrupts*/
    dma_status = *NARM_DSIER2_ADDR;
    *NARM_DSIER2_ADDR = dma_status;  

    /* disable transmit FIFO and DMA */
    narm_write_reg (NARM_DCR_REG, NARM_DCR2_ADDR,  ce, 0);
    narm_write_reg (NARM_EGCR_REG, NARM_EGCR_ADDR, etxdma, 0);
    narm_write_reg (NARM_EGCR_REG, NARM_EGCR_ADDR, etx, 0);

    /* Count number of packets in a DMA ring */
    index = eth_datap->tx_bd_tail;

    while (index != eth_datap->tx_bd_head)
    {
        if (eth_tx_buffer_descriptors[index].lower_word.bits.last)
            cnt++;

        if (eth_tx_buffer_descriptors[index].lower_word.bits.wrap)
            index = 0;
        else 
            index++;
    }
    /* reset transmit buffer descriptors */
    eth_reset_tx_buffers_descriptors();

    /* enable transmit FIFO and DMA */
    narm_write_reg (NARM_EGCR_REG, NARM_EGCR_ADDR, etxdma, 1);
    narm_write_reg (NARM_EGCR_REG, NARM_EGCR_ADDR, etx, 1);
    narm_write_reg (NARM_DCR_REG, NARM_DCR2_ADDR,  ce, 1);

    /* Free dropped transmit packets */
    for (index = 0; index < cnt; index++)
        eth_restart();
    
    normal;
}
