/* fa31x.c -- NETGEAR FA311/FA312 PCI Adapter Driver */
/* fa31x.c: Version 2.20       07/16/2001
 *    
 *       NETGEAR FA311/FA312 PCI Adaper Driver for Linux
 *
 *       This program is free software; you can redistribute it and/or
 *       modify it under the terms of the GNU General Public License
 *       as published by the Free Software Foundation; either version 2
 *       of the License, or (at your option) any later version.
 *
 *       This program is distributed in the hope that it will be useful,
 *       but WITHOUT ANY WARRANTY; without even the implied warranty of
 *       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *       GNU General Public License for more details.
 */
static const char *version = "fa31x.c: NETGEAR FA311/FA312 PCI Adapter v2.20\n";

/* Generic Kernel Module/Driver Headers */

#ifdef MODVERSIONS
#include
#endif

#include
#include

#include

#include
#include          /* IO stuff */
#include
#include

/* Ethernet Driver Specific Headers */

#include
#include
#include
#include
#include

#include "fa31x.h"

/* Macros */

/*
 * SWAP_BUS_TO_CPU_XX and SWAP_CPU_TO_BUS_XX macros swap 16 and 32 bit values
 * between the PCI bus' little endian byte-order and the CPU's native
 * byte-order.
 */
#ifdef __BIG_ENDIAN
#define BUS_TO_CPU_SWAP_16(X)    swap_16(X)
#define BUS_TO_CPU_SWAP_32(X)    swap_32(X)
#define CPU_TO_BUS_SWAP_16(X)    swap_16(X)
#define CPU_TO_BUS_SWAP_32(X)    swap_32(X)
#define CPU_TO_NET_SWAP_16(X)    ((u16)(X))
#define CPU_TO_NET_SWAP_32(X)    ((u32)(X))
#define NET_TO_CPU_SWAP_16(X)    ((u16)(X))
#define NET_TO_CPU_SWAP_32(X)    ((u32)(X))
#else
#define BUS_TO_CPU_SWAP_32(X)    ((u32)(X))
#define BUS_TO_CPU_SWAP_16(X)    ((u16)(X))
#define CPU_TO_BUS_SWAP_32(X)    ((u32)(X))
#define CPU_TO_BUS_SWAP_16(X)    ((u16)(X))
#define CPU_TO_NET_SWAP_16(X)    swap_16(X)
#define CPU_TO_NET_SWAP_32(X)    swap_32(X)
#define NET_TO_CPU_SWAP_16(X)    swap_16(X)
#define NET_TO_CPU_SWAP_32(X)    swap_32(X)
#endif

/* Macros translate addresses between PCI bus and CPU */
#define BUS_TO_CPU_ADDR_XLATE(X)  BUS_TO_CPU_SWAP_32(bus_to_virt(X))
#define CPU_TO_BUS_ADDR_XLATE(X)  CPU_TO_BUS_SWAP_32(virt_to_bus(X))

/* Macros to read/write 32/16 bit data to/from FA311/FA312 device registers. */
#define DP_REG32_WRITE(reg, val)  io_write_32 (iobase+(reg),            \
                                               CPU_TO_BUS_SWAP_32(val))
#define DP_REG32_READ(reg)        BUS_TO_CPU_SWAP_32(io_read_32 (iobase+(reg)))
#define DP_REG16_WRITE(reg, val)  io_write_16 (iobase+(reg),            \
                                               CPU_TO_BUS_SWAP_16(val))
#define DP_REG16_READ(reg)        BUS_TO_CPU_SWAP_16(io_read_16 (iobase+(reg)))
#define DP_REG32_SET(reg, val)    DP_REG32_WRITE(reg,DP_REG32_READ(reg)|(val))
#define DP_REG32_CLR(reg, val)    DP_REG32_WRITE(reg,DP_REG32_READ(reg)&~(val))
#define DP_REG16_SET(reg, val)    DP_REG16_WRITE(reg,DP_REG16_READ(reg)|(val))
#define DP_REG16_CLR(reg, val)    DP_REG16_WRITE(reg,DP_REG16_READ(reg)&~(val))

/* Debug Macros */
#define DP_DEBUG_PROBE            0x00000001
#define DP_DEBUG_OPEN             0x00000002
#define DP_DEBUG_CLOSE            0x00000004
#define DP_DEBUG_IOCTL            0x00000008
#define DP_DEBUG_TX               0x00000010
#define DP_DEBUG_RX               0x00000020
#define DP_DEBUG_MC               0x00000040
#define DP_DEBUG_ANEG             0x00000080
#define DP_DEBUG_INTR             0x00000100
#define DP_DEBUG_LOAD             0x00000200
#define DP_DEBUG_UNLOAD           0x00000400

#ifdef DEBUG
u32     dp_debug_level=DEBUG;
#define DP_DEBUG(level, X)  if (level & dp_debug_level) printk X
#else
#define DP_DEBUG(level, X)
#endif

/* data types */
typedef u32    status;                /* return status */
typedef volatile u8 * virt_addr;      /* CPU virtual address */
typedef volatile u8 * bus_addr;       /* BUS physical address */
typedef u8     bool;

#define OK        0                   /* status: OK */
#define ERROR    -1                   /* status: ERROR */
#define    TRUE      1
#define FALSE     0

/* Default Driver Parameters */
#define DP_DEFAULT_TXQ_SIZE         10
#define DP_DEFAULT_RXQ_SIZE         30
#define DP_RX_COPY_THRESHOLD        128 /* upper limit for rx packet copy */
#define DP_POLYNOMIAL               0x04C11DB6

/* Alignment and packet sizes */
#define ETH_CRC_LEN                 4
#define ETH_MAX_PKT_SIZE            (ETH_FRAME_LEN + ETH_CRC_LEN)
#define DP_ALIGN                    4     /* word alignment */

/* Driver private descriptor macros */
#define DP_DESC_SKBPTR              0x0c  /* SKB pointer offset */
#define DP_QUEUE_ELE_SIZE           (DP_DESC_SIZE + 4)
#define DP_QUEUE_ELE_NEXT(q)                                            \
        q->cur_desc_addr = DP_QUEUE_ELE_NEXT_GET(q, q->cur_desc_addr)
#define DP_QUEUE_ELE_NEXT_GET(q, desc_addr)                                    \
        ((desc_addr) == (q)->last_desc_addr) ? (q)->first_desc_addr :        \
                                           (desc_addr) + DP_QUEUE_ELE_SIZE

/* Macros to get/set the values of the descriptor fields */
#define DP_DESC_LNK_GET(ptr)     *(u32 *)((virt_addr)ptr + DP_DESC_LNK)
#define DP_DESC_CMDSTS_GET(ptr)  *(u32 *)((virt_addr)ptr + DP_DESC_CMDSTS)
#define DP_DESC_BUFPTR_GET(ptr)  *(u32 *)((virt_addr)ptr + DP_DESC_BUFPTR)
#define DP_DESC_SKBPTR_GET(ptr)  *(u32 *)((virt_addr)ptr + DP_DESC_SKBPTR)

#define DP_DESC_LNK_SET(ptr, val)      DP_DESC_LNK_GET(ptr) = (val)
#define DP_DESC_CMDSTS_SET(ptr, val)   DP_DESC_CMDSTS_GET(ptr) = (val)
#define DP_DESC_BUFPTR_SET(ptr,val)    DP_DESC_BUFPTR_GET(ptr) = (val)
#define DP_DESC_SKBPTR_SET(ptr,val)    DP_DESC_SKBPTR_GET(ptr) = (val)

/*
 * Macros to get/set the values of descriptor fields with
 * appropriate address and byte order translations
 */
#define DP_DESC_LNK_XLATE_GET(p)    BUS_TO_CPU_ADDR_XLATE(DP_DESC_LNK_GET(p))
#define DP_DESC_CMDSTS_XLATE_GET(p)    BUS_TO_CPU_SWAP_32(DP_DESC_CMDSTS_GET(p))
#define DP_DESC_BUFPTR_XLATE_GET(p) BUS_TO_CPU_ADDR_XLATE(DP_DESC_BUFPTR_GET(p))

#define DP_DESC_LNK_XLATE_SET(p, v)                                    \
        DP_DESC_LNK_SET(p, CPU_TO_BUS_ADDR_XLATE(v))
#define DP_DESC_CMDSTS_XLATE_SET(p, v)                                 \
        DP_DESC_CMDSTS_SET(p, CPU_TO_BUS_SWAP_32(v))
#define DP_DESC_BUFPTR_XLATE_SET(p,v)                                  \
        DP_DESC_BUFPTR_SET(p, CPU_TO_BUS_ADDR_XLATE(v))


/* Descriptor queue */
struct fa31x_queue {
    virt_addr  first_desc_addr;  /* descriptor array address */
    virt_addr  last_desc_addr;   /* last descriptor address */
    virt_addr  cur_desc_addr;    /* current descriptor address */
    virt_addr  qbuf;             /* allocated queue buffer */
    u16        count;            /* number of elements */
};

/* Queue types -- qtype */
#define DP_QUEUE_TYPE_TX    1    /* Transmit queue */
#define DP_QUEUE_TYPE_RX    2    /* Receive queue */

/* Device private data */
struct fa31x_priv {
    struct device *         next;        /* Next fa31xdevice */
    struct fa31x_queue    tx_queue;    /* Transmit Descriptor Queue */
    struct fa31x_queue    rx_queue;    /* Receive Descriptor Queue */
    struct enet_statistics  stats;       /* MIB data */
};

static struct device *    fa31x_dev_list;        /* List of fa31xdevices */

/* Linux Network Driver interface routines */
extern int fa31x_probe (struct device *dev);
static int fa31x_open (struct device *dev);
static int fa31x_close (struct device *dev);
static int fa31x_start_xmit (struct sk_buff *skb, struct device *dev);
static void fa31x_set_multicast_list (struct device *dev);
static int fa31x_ioctl (struct device *dev, struct ifreq *rq, int cmd);
static void fa31x_interrupt (int irq, void *dev_id, struct pt_regs *regs);
static struct enet_statistics *fa31x_get_stats (struct device *dev);

/* Support Functions */
/*static u16 swap_16 (u16 us);*/
/*static u32 swap_32 (u32 ui);*/
static u32 io_read_32 (u16 io_port);
static void io_write_32 (u16 io_port, u32 data);
static u16 io_read_16 (u16 io_port);
/*static void io_write_16 (u16 io_port, u16 data);*/

/* Driver Private Routines */
static void fa31x_mac_address_set (u32 iobase, char *mac_addr);
static void fa31x_mac_address_get (u32 iobase, char *mac_addr);
static status fa31x_dev_reset (u32 iobase);
static status fa31x_queue_create (struct fa31x_queue *q, int count, int qtype);
static status fa31x_queue_delete (struct fa31x_queue *q);
static virt_addr fa31x_tx_desc_get (struct device *dev);
static virt_addr fa31x_rx_desc_get (struct device *dev);
static status fa31x_phy_setup (struct device *dev);
static int fa31x_crc (char * mc_addr);
static void fa31x_tx_skb_reclaim (struct device *dev, virt_addr desc_addr);
static void  ReadEEWord(u16 iobase,u32 wordOffset,u16* pEeData);


/* Driver Debug Routines */
#ifdef DEBUG
static void fa31x_regs_info (u16 iobase);
#endif

/*
 * fa31x_probe - enumerate the PCI bus for instances of FA311/FA312
 *
 * This routine enumerates FA311/FA312 ethernet devices on the PCI bus, and
 * registers each FA311/FA312 device found.
 */
int
fa31x_probe (struct device *dev)
{
    int  dev_count;
    u8   bus;
    u8   func;
    u8   irq;
    u32  iobase;
    u32  version;
   
    if (! pcibios_present())
        return -ENODEV;        /* No such device */

    dev_count=0;
    while (pcibios_find_device (PCI_VENDOR_ID_NS, PCI_VENDOR_ID_NS_83815,
                                dev_count, &bus, &func)
           == PCIBIOS_SUCCESSFUL) {

        /* Allocate, name the device and add to ethernet device list */
        dev = init_etherdev(dev, sizeof (struct fa31x_priv));

        if (dev == NULL) {
            printk (KERN_INFO "%s: Failed to create device struct\n",
                    DP_DRV_NAME);
            break;
        }
           
        /* Read PCI Configuration Registers */
        pcibios_read_config_byte (bus, func, PCI_INTERRUPT_LINE, &irq);
        pcibios_read_config_dword (bus, func, PCI_BASE_ADDRESS_0, &iobase);
        iobase &= PCI_BASE_ADDRESS_IO_MASK;

        /* Put the device in a quiescent state */
        if (fa31x_dev_reset (iobase) != OK) {
            printk (KERN_INFO "%s: Device Reset failed.\n",
                    DP_DRV_NAME);
            kfree (dev);
            continue;             /* Try the next device */
        }
        DP_REG32_WRITE(DP_CCSR, 0x00008000);
    DP_REG32_WRITE(DP_WCSR, 0x00000000);  
        /* Get the ethernet address */
        fa31x_mac_address_get (iobase, dev->dev_addr);

        /* If address is not set, set up a default one */
        /* XXX needed only for pre-release versions of the hardware */
        if ((dev->dev_addr[0] == 0) && (dev->dev_addr[1] == 0) &&
            (dev->dev_addr[2] == 0) && (dev->dev_addr[3] == 0) &&
            (dev->dev_addr[4] == 0) && (dev->dev_addr[5] == 0)) {
            u32   random = jiffies;
            u8 *  ptr = (u8 *) &random;

            dev->dev_addr[0] = 0x08; /* National's Ethernet ID 0 */
//            dev->dev_addr[1] = 0x00; /* National's Ethernet ID 1 */
 //           dev->dev_addr[2] = 0x17; /* National's Ethernet ID 2 */
            dev->dev_addr[3] = *ptr++;
            dev->dev_addr[4] = *ptr++;
            dev->dev_addr[5] = *ptr;
        }
 
        /* initialize the device data */
        dev->base_addr = iobase;
        dev->irq       = irq;
        dev->open      = fa31x_open;
        dev->stop      = fa31x_close;
        dev->get_stats = fa31x_get_stats;
        dev->do_ioctl  = fa31x_ioctl;
        dev->hard_start_xmit    = fa31x_start_xmit;
        dev->set_multicast_list = fa31x_set_multicast_list;

        ether_setup (dev);        /* initialize generic fields */

        /* reserve IO region */
        request_region (iobase, FA31x_PCI_IO_SIZE, dev->name);

        /* Display board info */
        version = DP_REG32_READ(DP_SRR);
        printk (KERN_INFO "%s: bus=%d func=%d io=0x%x irq=%d ver=%d.%d\n",
                dev->name, bus, func, iobase, irq,
                (version & DP_SRR_MAJ) >> DP_SRR_MAJ_SHF,
                (version & DP_SRR_MIN));
   

        /* If address is not set, set up a default one */
        /* XXX needed only for pre-release versions of the hardware */
        if ((dev->dev_addr[0] == 0) && (dev->dev_addr[1] == 0) &&
            (dev->dev_addr[2] == 0) && (dev->dev_addr[3] == 0) &&
            (dev->dev_addr[4] == 0) && (dev->dev_addr[5] == 0)) {
            u32   random = jiffies;
            u8 *  ptr = (u8 *) &random;

            dev->dev_addr[0] = 0x08; /* National's Ethernet ID 0 */
            dev->dev_addr[1] = 0x00; /* National's Ethernet ID 1 */
            dev->dev_addr[2] = 0x17; /* National's Ethernet ID 2 */
            dev->dev_addr[3] = *ptr++;
            dev->dev_addr[4] = *ptr++;
            dev->dev_addr[5] = *ptr;
        }
 


        printk (KERN_INFO "%s: ethernet addr=%02x:%02x:%02x:%02x:%02x:%02x\n",
                dev->name,
                dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2],
                dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]);



        /* Chain the device */
        ((struct fa31x_priv *)(dev->priv))->next = fa31x_dev_list;
        fa31x_dev_list = dev;

#ifdef DEBUG
        printk (KERN_INFO "%s: DebugInfo: dev=0x%x priv=0x%x "
                "&dp_debug_level=0x%x\n",
                dev->name, (u32)dev, (u32)dev->priv, (u32)&dp_debug_level);
#endif

        /* Update Counters */
        dev_count++;
        dev=NULL;
    }

    return dev_count ? OK : -ENODEV;
}

/* fa31x_open - open and initialize a device */
static int
fa31x_open (struct device *dev)
{
    u32     iobase = dev->base_addr;
    struct fa31x_priv* priv   = dev->priv;
   
    /* Reset the device -- paranoia! */
    if (fa31x_dev_reset (iobase) != OK)
        return -EAGAIN;

    /* Allocate Tx and Rx queues */
    if (fa31x_queue_create (&priv->tx_queue, DP_DEFAULT_TXQ_SIZE,
                              DP_QUEUE_TYPE_TX) != OK) {
        printk (KERN_INFO "%s: Failed to create tx queue\n", dev->name);
        return -EAGAIN;
    }

    if (fa31x_queue_create (&priv->rx_queue, DP_DEFAULT_RXQ_SIZE,
                              DP_QUEUE_TYPE_RX) != OK) {
        fa31x_queue_delete (&priv->tx_queue);
        printk (KERN_INFO "%s: Failed to create rx queue\n", dev->name);
        return -EAGAIN;
    }

    /* Install the Tx and Rx queues on the device */
    DP_REG32_WRITE (DP_TXDP, virt_to_bus(priv->tx_queue.first_desc_addr));
    DP_REG32_WRITE (DP_RXDP, virt_to_bus(priv->rx_queue.first_desc_addr));

    DP_DEBUG (DP_DEBUG_OPEN,
              (KERN_INFO "dp: setting TXDP=0x%x RXDP=0x%x\n",
               (u32)virt_to_bus (priv->tx_queue.first_desc_addr),
               (u32)virt_to_bus (priv->rx_queue.first_desc_addr)));

    /* Install interrupt vector */
    if (request_irq (dev->irq, &fa31x_interrupt, SA_SHIRQ,
                     dev->name, dev) != OK) {
        fa31x_queue_delete (&priv->tx_queue);
        fa31x_queue_delete (&priv->rx_queue);
        return -EAGAIN;
    }

    /* Setup phy capabilities */
    if (fa31x_phy_setup (dev) != OK) {
        printk (KERN_INFO "%s: Warning PHY setup did not complete. Check cable.\n",
                dev->name);
    }
   
    /* Setup transmit control */
    DP_REG32_WRITE (DP_TXCFG, (DP_TXCFG_DRTH_SET(0x30) |
                               DP_TXCFG_FLTH_SET(0x10) |
                               DP_TXCFG_MXDMA_32       |
                               DP_TXCFG_ATP));

    DP_DEBUG (DP_DEBUG_OPEN,
              (KERN_INFO "dp: TXCFG set to 0x%x\n",
               (DP_TXCFG_DRTH_SET(0x30) | DP_TXCFG_FLTH_SET(0x10) |
                DP_TXCFG_MXDMA_32       | DP_TXCFG_ATP)));
   
    /* Setup receive control */
    DP_REG32_WRITE (DP_RXCFG, (DP_RXCFG_DRTH_SET(0x08) |
                               DP_RXCFG_MXDMA_32));

    DP_DEBUG (DP_DEBUG_OPEN,
              (KERN_INFO "dp: RXCFG set to 0x%x\n",
               (DP_RXCFG_DRTH_SET(0x08) | DP_RXCFG_MXDMA_32)));

    /* Setup the ethernet address */
    fa31x_mac_address_set (iobase, dev->dev_addr);

    /* Receive perfect match and broadcast packets */
    DP_REG32_WRITE (DP_RFCR, 0);
    DP_REG32_WRITE (DP_RFCR, (DP_RFCR_AAB  | /* all broadcast pkts */
                              DP_RFCR_APM  | /* perfect match pkts */
                              DP_RFCR_RFEN));

    DP_DEBUG (DP_DEBUG_OPEN,
              (KERN_INFO "dp: RFCR set to 0x%x\n",
               DP_RFCR_RFEN | DP_RFCR_APM | DP_RFCR_AAB));
                   
    /* Turn on device interrupts */
    DP_REG32_WRITE (DP_IMR, (DP_IMR_RXOK  | DP_IMR_MIB   |
                             DP_IMR_RTABT | DP_IMR_RMABT |
                             DP_IMR_SSERR | DP_IMR_PHY));
    DP_REG32_WRITE (DP_IER, DP_IER_IE);

    /* Enable Tx/Rx */
    DP_REG32_WRITE (DP_CR, DP_CR_TXE | DP_CR_RXE);

    /* Increment module reference count */
    MOD_INC_USE_COUNT;

    return OK;
}

/* fa31x_close - close a device, and reclaim resources */
static int
fa31x_close (struct device *dev)
{
    u32                     iobase = dev->base_addr;
    struct fa31x_priv* priv   = dev->priv;

    /* Stop the Tx/Rx */
    /* XXX Do we need to do this ??? */
    DP_REG32_WRITE (DP_CR, (DP_CR_TXD | DP_CR_RXD));

    /* Reset the device */
    fa31x_dev_reset (iobase);
   
    /* Uninstall the interrupt vector */
    free_irq (dev->irq, dev);

    /* Free the Tx and Rx queues */
    fa31x_queue_delete (&priv->tx_queue);
    fa31x_queue_delete (&priv->rx_queue);

    /* Decrement module reference count */
    MOD_DEC_USE_COUNT;
   
    return OK;
}

/*
 * fa31x_start_xmit - transmit an ethernet packet.
 *
 * This routine writes to a tx descriptor, sets the ownership bit of the
 * CMDSTS, and signals the chip
 */
static int
fa31x_start_xmit (struct sk_buff *skb, struct device *dev)
{
    u32        iobase = dev->base_addr;
    u32        cmdsts;
    virt_addr  tx_desc;
    struct enet_statistics *stats_p;

    if (skb->len > ETH_MAX_PKT_SIZE)
        return ERROR;

    tx_desc = fa31x_tx_desc_get(dev);
    if (tx_desc == NULL) {
        set_bit (0, &dev->tbusy);
        DP_REG32_SET (DP_IMR, DP_IMR_TXOK | DP_IMR_TXIDLE);
        tx_desc = fa31x_tx_desc_get (dev);
    }

    stats_p = &((struct fa31x_priv *)(dev->priv))->stats;

    /* Update tx_desc to point to SKB data, set CMDSTS, and signal the chip */
    if (tx_desc != NULL) {

        /* Reclaim SKBs from the TX queue */
        if (DP_DESC_SKBPTR_GET (tx_desc))
            fa31x_tx_skb_reclaim (dev, tx_desc);

        /* update statistics of the previous transmit */
        cmdsts = DP_DESC_CMDSTS_XLATE_GET (tx_desc);

                if(DP_REG32_READ(DP_ISR) & DP_ISR_TXERR) {  /*modified by Pam 2001/7/16 */

        /* if (cmdsts & DP_DESC_CMDSTS_TX_ERRORS) { */

            stats_p->tx_errors++;
           
            /* Update individual counters */

            stats_p->collisions += DP_DESC_CMDSTS_TX_COLLISIONS_GET(cmdsts);

            if (cmdsts & DP_DESC_CMDSTS_TX_TXA) /* tx aborted */
                stats_p->tx_packets--;
           
            if (cmdsts & DP_DESC_CMDSTS_TX_TFU) /* fifo errors */
                stats_p->tx_fifo_errors++;

            if (cmdsts & DP_DESC_CMDSTS_TX_CRS) /* lost carrier */
                stats_p->tx_carrier_errors++;

            if (cmdsts & DP_DESC_CMDSTS_TX_OWC) /* out of window collisions */
                stats_p->tx_window_errors++;
        }
       
        /* Update the descriptor */
        DP_DESC_CMDSTS_XLATE_SET (tx_desc, DP_DESC_CMDSTS_OWN|skb->len);
        DP_DESC_BUFPTR_XLATE_SET (tx_desc, skb->data);
        DP_DESC_SKBPTR_SET (tx_desc, (u32)skb);
       
        dev->trans_start = jiffies;
        DP_REG32_SET (DP_CR, DP_CR_TXE);
        DP_DEBUG (DP_DEBUG_TX,
                  (KERN_INFO "Tx: tx_desc=0x%x ", (u32)tx_desc));
        stats_p->tx_packets++;
    } else
        stats_p->tx_dropped++;

    return OK;
}

/*
 * fa31x_start_receive - receive the data from a Rx Queue descriptor
 *
 * This routine receives the data from Rx queue as long as it gets a valid
 * rx_descriptor and resets the descriptor's CMDSTS field back to the Buffer
 * size, and updates the BUFPTR and SKBPTR fields to the newly allocated SKB.
 */
static int
fa31x_start_receive (struct device *dev)
{
    u32         cmdsts;
    int         len;
    bool        do_copy;
    virt_addr   rx_desc;
    struct sk_buff *cur_skb;
    struct sk_buff *new_skb;
    struct sk_buff *rx_skb;
    struct enet_statistics *stats_p;
   
    stats_p = &((struct fa31x_priv *)(dev->priv))->stats;
   
    for (rx_desc = fa31x_rx_desc_get(dev);
         (rx_desc != NULL);
         rx_desc = fa31x_rx_desc_get(dev)) {
       
        DP_DEBUG (DP_DEBUG_RX,
                  (KERN_INFO "Rx: rx_desc=0x%x, CMDSTS = 0x%x",
                   (u32)rx_desc, DP_DESC_CMDSTS_XLATE_GET(rx_desc)));

        cmdsts = DP_DESC_CMDSTS_XLATE_GET (rx_desc);

        /* Send the packet to the stack if no errors */
        if ((cmdsts & DP_DESC_CMDSTS_RX_ERRORS) == 0) {

            len = (cmdsts & DP_DESC_CMDSTS_SIZE) - ETH_CRC_LEN;

            /*
             * Allocate a new SKB
             * small data packets less than DP_RX_COPY_THRESHOLD are copied
             * into the new SKB, other allocate one to replace the current SKB.
             */
            if (len < DP_RX_COPY_THRESHOLD) {
                do_copy = TRUE;
                new_skb = alloc_skb (len + 2, GFP_ATOMIC);
            } else {
                do_copy = FALSE;
                new_skb = alloc_skb (ETH_MAX_PKT_SIZE, GFP_ATOMIC);
            }
       
            if (new_skb) {
                cur_skb = (struct sk_buff *) DP_DESC_SKBPTR_GET (rx_desc);

                if (do_copy) {
                    /* Copy data from current SKB and send the new SKB up */
                    rx_skb = new_skb;
                    skb_reserve (rx_skb, 2);
                    memcpy (skb_put(rx_skb, len), cur_skb->data, len);
                } else {
                    /* Replace the the current SKB with the new SKB */
                    rx_skb = cur_skb;
                    DP_DESC_BUFPTR_XLATE_SET (rx_desc, new_skb->data);
                    DP_DESC_SKBPTR_SET (rx_desc, (u32) new_skb);
                    (void) skb_put(rx_skb, len);
                }

                /* update the SKB and set it up */
                rx_skb->dev = dev;
                rx_skb->protocol = eth_type_trans (rx_skb, dev);

                netif_rx (rx_skb);

                dev->last_rx = jiffies;
                stats_p->rx_packets++;
            } else
                stats_p->rx_dropped++; /* no resources */
            if (cmdsts & DP_DESC_CMDSTS_RX_DEST_MC)
                stats_p->multicast++;
        }
        else {
            stats_p->rx_errors++; /* bad packet */

            /* Update individual counters */
            if (cmdsts & (DP_DESC_CMDSTS_RX_RUNT |
                          DP_DESC_CMDSTS_RX_LONG))
                stats_p->rx_length_errors++;

            if (cmdsts & DP_DESC_CMDSTS_RX_CRCE)
                stats_p->rx_crc_errors++;

            if (cmdsts & DP_DESC_CMDSTS_RX_FAE)
                stats_p->rx_frame_errors++;

            if (cmdsts & DP_DESC_CMDSTS_RX_RXO)
                stats_p->rx_fifo_errors++;
        }

        /* Cleanup the descriptor and make available for reception */
        DP_DESC_CMDSTS_XLATE_SET (rx_desc, ETH_MAX_PKT_SIZE);
    }
    return OK;
}

/* fa31x_get_stats - get current device statistics */
static struct enet_statistics *
fa31x_get_stats (struct device *dev)
{
    return &((struct fa31x_priv *)(dev->priv))->stats;
}

/* fa31x_set_multicast_list - sets multicast, & promiscuous mode */
static void
fa31x_set_multicast_list (struct device *dev)
{
    u32      iobase = dev->base_addr;
    u16   hash_table[32];
    u32      rfcr_flags;
    int   i;
    struct dev_mc_list * mc_list;
   

    /* default RFCR mode */
    rfcr_flags = DP_RFCR_APM | DP_RFCR_AAB | DP_RFCR_RFEN;

    /* Setup promiscuous mode */
    if (dev->flags & IFF_PROMISC) {
        DP_DEBUG (DP_DEBUG_OPEN,
                  (KERN_INFO "IFF_PROMISC\n"));
        rfcr_flags = (DP_RFCR_AAU | DP_RFCR_AAM | DP_RFCR_AAB |
                      DP_RFCR_RFEN);
           
    } else if (dev->flags & IFF_ALLMULTI) {

        /* Receive all multicast packets */
        DP_DEBUG (DP_DEBUG_OPEN,
                  (KERN_INFO "IFF_ALLMULTI\n"));
        rfcr_flags |= DP_RFCR_AAM;
    } else {

        /* Setup to receive programmed multicast packets */
        memset (hash_table, 0, 32);
        for (i=0, mc_list=dev->mc_list; mc_list && i < dev->mc_count;
             i++, mc_list = mc_list->next) {
            DP_DEBUG (DP_DEBUG_OPEN,
                      (KERN_INFO "mc_addr=%p\n", mc_list->dmi_addr));
            set_bit (fa31x_crc((char *)mc_list->dmi_addr) & 0x1ff, hash_table);
        }

        /* install the hash table */
        for (i=0; i<32; i++) {
            DP_REG32_WRITE (DP_RFCR, DP_RFCR_RFADDR_FMEM_LO + i*2);
            DP_REG32_WRITE (DP_RFDR, (u32) hash_table[i]);
        }

        rfcr_flags |= DP_RFCR_MHEN;
    }

    DP_REG32_WRITE (DP_RFCR, 0);
    DP_REG32_WRITE (DP_RFCR, rfcr_flags);
    DP_DEBUG (DP_DEBUG_OPEN,
              (KERN_INFO "MC Setup RFCR flags=0x%x\n", rfcr_flags));

    return;
}

/* fa31x_crc - computer CRC for hash table entries */
static int
fa31x_crc (char * mc_addr)
{
    u32    crc;
    u8    cur_byte;
    u8    msb;
    u8    byte, bit;

    crc = ~0;
    for (byte=0; byte<6; byte++) {
        cur_byte = *mc_addr++;
        for (bit=0; bit<8; bit++) {
            msb = crc >> 31;
            crc <<= 1;
            if (msb ^ (cur_byte & 1)) {
                crc ^= DP_POLYNOMIAL;
                crc |= 1;
            }
            cur_byte >>= 1;
        }
    }
    crc >>= 23;

    return (crc);
}

/* fa31x_interrupt - handle driver specific ioctls */
static int
fa31x_ioctl (struct device *dev, struct ifreq *rq, int cmd)
{
    return -EOPNOTSUPP;
}

/* 02/15/2000 */
static u32 PreviousLinkStatus=0;
/* fa31x_interrupt - handle the interrupts */
static void
fa31x_interrupt (int irq, void *dev_id, struct pt_regs *regs)
{
    struct device * dev = dev_id;
    u16  iobase = dev->base_addr;
    u32  reg_isr;
    u32  CurrentLinkStatus;
   
    CurrentLinkStatus= DP_REG32_READ(DP_PHYSTS);
#define HAL_PHY_SPEED10   0x00000002
    if(PreviousLinkStatus!=CurrentLinkStatus)
    {
        if(CurrentLinkStatus & HAL_PHY_SPEED10)
        {
            DP_REG32_WRITE(DP_PHY_PAGE,0x0001);
            DP_REG32_WRITE(DP_PHY_EXTCFG,0x0000);
            DP_REG32_WRITE(DP_PHY_PAGE,0x0000);
        }
               else
               {
            DP_REG32_WRITE(DP_PHY_PAGE,0x0001);
                        DP_REG32_WRITE(DP_PHY_EXTCFG,0x0010);
                        DP_REG32_WRITE(DP_PHY_PAGE,0x0000);

        }
        PreviousLinkStatus=CurrentLinkStatus;
    }

    reg_isr = DP_REG32_READ (DP_ISR);
   
    DP_DEBUG (DP_DEBUG_IOCTL,
              (KERN_INFO "%s: intr_status=0x%x\n", dev->name,
               reg_isr));
    if (reg_isr & DP_ISR_RXOK)
        fa31x_start_receive (dev);

    if (reg_isr & (DP_ISR_TXOK | DP_ISR_TXIDLE)) {
        DP_REG32_CLR (DP_IMR, (DP_IMR_TXOK|DP_IMR_TXIDLE));
        clear_bit (0, &dev->tbusy);
        mark_bh (NET_BH);
    }
}

/* fa31x_mac_address_set - set the ethernet address */
static void
fa31x_mac_address_set (u32 iobase, char *mac_addr)
{
    u16 *    mac_addr_ptr;
    int i;

    for (i=0, mac_addr_ptr = (u16 *)mac_addr; i<3; i++, mac_addr_ptr++) {
        DP_REG32_WRITE (DP_RFCR, DP_RFCR_RFADDR_PMATCH1 + i*2);
        DP_REG32_WRITE (DP_RFDR, CPU_TO_BUS_SWAP_16 (*mac_addr_ptr));
    }
}

/* fa31x_mac_address_get - get the ethernet address */
static void
fa31x_mac_address_get (u32 iobase, char *mac_addr)
{
    u16 *  mac_addr_ptr=(u16 *)mac_addr;
    int    i;
   u16 mask;
   u16 word1 = 0;
   u16 word2 = 0;
   u16 word3 = 0;
   u16 word4 = 0;
   u16 nicAddress[ 3 ];

   //
   // Read 16 bit words 6 - 9 from the EEProm.  They contain the hardwares MAC
   // address in a rather cryptic format.
   //
   ReadEEWord( iobase,
           0x06,
           &word1 );
   ReadEEWord( iobase,
           0x07,
           &word2 );
   ReadEEWord( iobase,
           0x08,
           &word3 );
   ReadEEWord( iobase,
           0x09,
           &word4 );

   //
   // Decode the cryptic format into what we can use a word at a time.
   //
   nicAddress[ 0 ] = word1 & 1;
   nicAddress[ 1 ] = word2 & 1;
   nicAddress[ 2 ] = word3 & 1;

   i = 15;
   mask = 0x2;
   while ( i-- ) {
      if ( word2 & 0x8000 ) {
     nicAddress[ 0 ] |= mask;
      }
      word2 = word2 << 1;
      mask = mask << 1;
   }
//printk("word0=%x\n",nicAddress[0]);
  *mac_addr_ptr=nicAddress[0];
  mac_addr_ptr++;


   i = 15;
   mask = 0x2;
   while ( i-- ) {
      if ( word3 & 0x8000 ) {
     nicAddress[ 1 ] |= mask;
      }
      word3 = word3 << 1;
      mask = mask << 1;
   }
//printk("word1 =%x\n",nicAddress[1]);

  *mac_addr_ptr=nicAddress[1];
 mac_addr_ptr++;

   i = 15;
   mask = 0x2;
   while ( i-- ) {
      if ( word4 & 0x8000 ) {
     nicAddress[ 2 ] |= mask;
      }
      word4 = word4 << 1;
      mask = mask << 1;
   }
//printk("word2 =%x\n",nicAddress[2]);

  *mac_addr_ptr=nicAddress[2];

}

/* fa31x_dev_reset - soft reset FA311/FA312 */
static status
fa31x_dev_reset (u32 iobase)
{
    int  timeout = 100;     /* XXX cleanup: use macro */
   
    DP_REG32_WRITE (DP_CR, DP_CR_RST);
    while (timeout--) {
        if (DP_REG32_READ (DP_ISR) == 0x03008000) {
            return (OK);
        }
        udelay (5);
    }
    return ERROR;
}

/*
 * fa31x_queue_create - create a circular queue of descriptors
 *
 * This routine allocates a descriptor buffer array aligned  on a word
 * boundary, initializes and links the array to make a circular queue.
 */
static status
fa31x_queue_create (struct fa31x_queue *q, int count, int qtype)
{
    virt_addr     desc_addr;
    int           i;
    struct sk_buff *skb;
   
    /* allocate the desc buffer array */
    q->qbuf = (virt_addr) kmalloc (DP_QUEUE_ELE_SIZE * count + DP_ALIGN,
                                   GFP_DMA);
    if (q->qbuf == (virt_addr) NULL)
        return ERROR;

    memset ((char *)q->qbuf, 0, DP_QUEUE_ELE_SIZE * count + DP_ALIGN);

    /* Adjust alignment and Initialize queue data */
    q->cur_desc_addr =
        q->first_desc_addr =
        (virt_addr)(((u32)q->qbuf + DP_ALIGN) & ~(DP_ALIGN - 1));
    q->last_desc_addr = q->first_desc_addr + ((count -1) * DP_QUEUE_ELE_SIZE);
    q->count = count;

    /* Initialize each buffer descriptor, and link them into circular queue */
    for (i=0, desc_addr=q->first_desc_addr; i
         i++, desc_addr+=DP_QUEUE_ELE_SIZE) {
        DP_DESC_LNK_XLATE_SET (desc_addr, desc_addr + DP_QUEUE_ELE_SIZE);

        /* Update the size, BUFPTR, and SKBPTR fields for RX descriptors */
        if (qtype == DP_QUEUE_TYPE_RX) {
            skb = alloc_skb (ETH_MAX_PKT_SIZE, GFP_ATOMIC);
            if (skb == NULL) {
                fa31x_queue_delete (q);
                return (ERROR);
            }
            DP_DESC_CMDSTS_XLATE_SET (desc_addr, ETH_MAX_PKT_SIZE);
            DP_DESC_BUFPTR_XLATE_SET (desc_addr, skb->data);
            DP_DESC_SKBPTR_SET (desc_addr, (u32) skb);
        }
    }
    /* Make the queue circular */
    DP_DESC_LNK_XLATE_SET (q->last_desc_addr, q->first_desc_addr);

    return OK;
}

/* fa31x_queue_delete - frees an allocated descriptor queue */
static status
fa31x_queue_delete (struct fa31x_queue *q)
{
    int       i;
    virt_addr desc_addr;
    struct sk_buff *skb;
       
    /* Free all SKBs in the queue */
    for (i=0, desc_addr=q->first_desc_addr;
         (i < q->count);
         i++, desc_addr += DP_QUEUE_ELE_SIZE) {
        skb = (struct sk_buff *) DP_DESC_SKBPTR_GET (desc_addr);
        if (skb != NULL)
            dev_kfree_skb (skb);
    }

    /* Free the queue buffer */
    kfree ((char *)q->qbuf);
   
    return (OK);
}

/*
 * fa31x_tx_desc_get - get a valid transmit descriptor
 *
 * This routine returns the current descriptor from the tx_queue if driver is
 * the owner, else returns NULL
 */
static virt_addr
fa31x_tx_desc_get (struct device *dev)
{
    struct fa31x_queue *   q;
    virt_addr                desc_addr = NULL;

    q = &((struct fa31x_priv *)(dev->priv))->tx_queue;

    /* Check if we own the descriptor */
    if  ((DP_DESC_CMDSTS_XLATE_GET (q->cur_desc_addr) & DP_DESC_CMDSTS_OWN)
         == 0) {
        desc_addr = q->cur_desc_addr;
        DP_QUEUE_ELE_NEXT (q);                 /* Move to the next element */
    }

    return desc_addr;
}

/* fa31x_tx_skb_reclaim - reclaim SKBs in transmitted descriptors */
static void
fa31x_tx_skb_reclaim (struct device *dev, virt_addr desc_addr)
{
    struct sk_buff *       skb;
    struct fa31x_queue * q;
   
    /* Reclaim buffers from all descriptors we own. */
    q = &((struct fa31x_priv *)(dev->priv))->tx_queue;
    while (((DP_DESC_CMDSTS_XLATE_GET(desc_addr) & DP_DESC_CMDSTS_OWN) == 0) &&
           ((skb=(struct sk_buff *) DP_DESC_SKBPTR_GET(desc_addr)) != NULL)) {
        dev_kfree_skb (skb);
        DP_DESC_SKBPTR_SET (desc_addr, 0);
        desc_addr = DP_QUEUE_ELE_NEXT_GET (q, desc_addr);
    }
}

/*
 * fa31x_rx_desc_get - get a valid receive descriptor
 *
 * This routine returns the current descriptor from the rx_queue if driver is
 * the owner, else returns NULL
 */
static virt_addr
fa31x_rx_desc_get (struct device *dev)
{
    struct fa31x_queue *   q;
    virt_addr                desc_addr = NULL;

    q = &((struct fa31x_priv *)(dev->priv))->rx_queue;

    /* Check if we own the descriptor */
    if  (DP_DESC_CMDSTS_XLATE_GET (q->cur_desc_addr) & DP_DESC_CMDSTS_OWN) {
        desc_addr = q->cur_desc_addr;
        DP_QUEUE_ELE_NEXT(q);                 /* Move to the next element */
    }

    return desc_addr;
}

/* fa31x_phy_setup - reset and setup the PHY device */
int link_speed=0;

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
MODULE_PARM(link_speed, "i");
#endif

static status
fa31x_phy_setup (struct device *dev)
{
    u32  iobase = dev->base_addr;
    u32  dp_cfg_val;
    u16  phy_status;
    u16  timeout;
    u32  lnk_status;
    u32  lnk_check = 0;

    dp_cfg_val = DP_REG32_READ(DP_CFG);
    dp_cfg_val &= 0xFFFF1FFF;               /* clear auto-negotiation select */
    dp_cfg_val |= (DP_CFG_PESEL         |   /* parity error detect */
                   DP_CFG_PAUSE_ADV     |   /* pause capable */
                   DP_CFG_PINT_ACEN     |   /* phy intr auto clear */
                   0x00040000);             /* phy config */

    switch (link_speed) {
    case 1: /* Force 10Mb Half duplex */
        dp_cfg_val |= DP_CFG_ANEG_SEL_10_HD;
        break;

    case 2: /* Force 100Mb Half duplex */
        dp_cfg_val |= DP_CFG_ANEG_SEL_100_HD;
        break;

    case 3: /* Force 10Mb Full duplex */
        dp_cfg_val |= DP_CFG_ANEG_SEL_10_FD;
        break;

    case 4: /* Force 100Mb Full duplex */
        dp_cfg_val |= DP_CFG_ANEG_SEL_100_FD;
        break;

    default:
    printk (KERN_INFO "Usage: insmod fa31x link_speed=n (n=0...4)");
        printk (KERN_INFO "Force to 10/100 full/half autonegotiate mode)");
    case 0: /* Autonegotiate 10/100 full/half */
        dp_cfg_val |= DP_CFG_ANEG_SEL_AUTO;
        break;
    }

    DP_REG32_WRITE (DP_CFG, dp_cfg_val | DP_CFG_PHY_RST);
    udelay (500);

    DP_REG32_WRITE (DP_CFG, dp_cfg_val);
    for (timeout=10000; timeout; timeout--) {

        lnk_status = DP_REG32_READ (DP_CFG);

        switch (link_speed) {
        case 1: /* Force 10Mb Half duplex */
            if (!(lnk_status & (DP_CFG_SPEED100 | DP_CFG_FDUP))) {
                lnk_check = 1;
                break;
            }
            udelay (500);
            break;

        case 2: /* Force 100Mb Half duplex */
            if ((lnk_status & (DP_CFG_SPEED100 | DP_CFG_FDUP))
                == DP_CFG_SPEED100) {
                lnk_check = 1;
                break;
            }
            udelay (500);
            break;

        case 3: /* Force 10Mb Full duplex */
            if ((lnk_status & (DP_CFG_ANEG_DN | DP_CFG_SPEED100))
                == DP_CFG_ANEG_DN) {
                lnk_check = 1;
                break;
            }
            udelay (500);
            break;

        case 4: /* Force 100Mb Full duplex */
            if ((lnk_status & (DP_CFG_ANEG_DN | DP_CFG_SPEED100))
                == (DP_CFG_ANEG_DN | DP_CFG_SPEED100)) {
                lnk_check = 1;
                break;
            }
            udelay (500);
            break;

        case 0: /* Autonegotiate 10/100 full/half */
        default:
            if (DP_REG32_READ (DP_CFG) & DP_CFG_ANEG_DN) {
                lnk_check = 1;
                break;
            }
            udelay (500);
        }

        if (lnk_check)
            break;
    }

    if (timeout == 0) {
        DP_DEBUG (DP_DEBUG_ANEG, (KERN_INFO "Phy Autonegotiate Failed\n"));
        return (ERROR);
    }
    DP_REG32_WRITE (DP_PHY_PAGE, DP_PHY_PAGE_VAL);
    DP_REG32_WRITE (DP_PHYCR, DP_PHYCR_PMDCSR_VAL);
    DP_REG32_WRITE (DP_PHY_TDATA, DP_PHY_TDATA_VAL);
    DP_REG32_WRITE (DP_PHY_DSPCFG, DP_PHY_DSPCFG_VAL);
    DP_REG32_WRITE (DP_PHY_SDCFG, DP_PHY_SDCFG_VAL);
    DP_REG32_WRITE (DP_PHY_PAGE, (u16) 0x0000);

                       
    phy_status = DP_REG16_READ (DP_PHYSTS);
    printk (KERN_INFO "%s: speed=%d duplex=%s link=%s\n",
            dev->name,
            (phy_status & DP_PHYSTS_SPEED_10) ? 10 : 100,
            (phy_status & DP_PHYSTS_FDX)? "full" : "half",
            (phy_status & DP_PHYSTS_LNK_VALID) ? "up" : "down");

    return (OK);
}

#if 0
/* swap_16 - swap a 16 bit value between little endian & big endian byteorder */
static u16 swap_16 (u16 us)
{
    return ((us << 8) & 0xff00) | ((us >> 8) & 0x00ff);
}
#endif

#if 0 /* Not used */
/* swap_32 - swap a 32 bit value between little endian & big endian byteorder */
static u32 swap_32 (u32 ui)
{
    return ((swap_16 (ui & 0x0000ffff) << 16) | swap_16 (ui >> 16));
}
#endif

/* io_read_16 - read the 16 bit value stored at given io address */
static u16 io_read_16 (u16 io_port)
{
    return inw (io_port);
}

#if 0 /* Not used */
/* io_write_16 - write the 16 bit data at given io address */
static void io_write_16 (u16 io_port, u16 data)
{
    outw (data, io_port);
}
#endif

/* io_read_32 - read the 32 bit value stored at given io address*/
static u32 io_read_32 (u16 io_port)
{
    return inl (io_port);
}

/* io_write_32 - write the 32 bit data at given io address */
static void io_write_32 (u16 io_port, u32 data)
{
    outl (data, io_port);
}

#ifdef DEBUG
/* fa31x_desc_info - print info on descriptors
 *
 * This routine displays values of the elements of fa31x_queue;
 * if option is -1, prints info on all the descs and their field vals;
 * and in all other cases prints info on the  specified descriptor.
 */

static void fa31x_desc_info (struct fa31x_queue * q, int option)
{
    virt_addr    desc_addr;
    u32          i;

    /* Display info header */
    printk ( KERN_DEBUG "fa31x_desc_info(): q=0x%x, "
             "first_desc_addr=0x%x, last_desc_addr=0x%x cur_desc_addr=0x%x\n",
             (u32)q,  (u32)q->first_desc_addr,
             (u32)q->last_desc_addr, (u32)q->cur_desc_addr);

    /* Print requested element info */
    switch (option) {
    case -1:                                 /* all elements */
        desc_addr = q->first_desc_addr;
        for (i=0; icount; i++) {
            printk (KERN_DEBUG "desc_addr=0x%x link=0x%x "
                    "cmdsts=0x%x bufptr=0x%x\n",
                    (u32)desc_addr, DP_DESC_LNK_GET (desc_addr),
                    DP_DESC_CMDSTS_GET (desc_addr),
                    DP_DESC_BUFPTR_GET (desc_addr));
            desc_addr += DP_QUEUE_ELE_SIZE;
        }
        break;
       
    default:                                 /* a specific element */
        if (option > q->count) {
            printk (KERN_DEBUG "Can't print info for ele=%d when count=%d\n",
                    option, q->count);
        }
        else {
            desc_addr = q->first_desc_addr + (DP_QUEUE_ELE_SIZE * (option - 1));
            printk (KERN_DEBUG "ele=%d desc_addr=0x%x link=0x%x "
                    "cmdsts=0x%x bufptr=0x%x\n",
                    option, (u32)desc_addr, DP_DESC_LNK_GET (desc_addr),
                    DP_DESC_CMDSTS_GET (desc_addr),
                    DP_DESC_BUFPTR_GET (desc_addr));
        }
    }
}


/* fa31x_regs_info - prints values of registers */
static void fa31x_regs_info (u16 iobase)
{
    printk (KERN_INFO "dp(0x%x): CFG=0x%x IMR=0x%x IER=0x%x\n",
            iobase, DP_REG32_READ (DP_CFG), DP_REG32_READ (DP_IMR),
            DP_REG32_READ (DP_IER));
    printk (KERN_INFO " ++ TXDP=0x%x TXCFG=0x%x RXDP=0x%x RXCFG=0x%x\n",
            DP_REG32_READ (DP_TXDP), DP_REG32_READ (DP_TXCFG),
            DP_REG32_READ (DP_RXDP), DP_REG32_READ (DP_RXCFG));
}
#endif

#ifdef MODULE

/*
 * init_module - Initialize the driver module
 *
 * This routine is called to initialize the driver when the module in
 * installed into a running kernel via insmod.
 *
 */

int
init_module (void)
{
    printk (KERN_INFO "%s", version);
    return fa31x_probe (NULL);
}

/*
 * cleanup_module - Deinstall the driver module.
 *
 * This routine is called when removing the driver module from a running
 * kernel via rmmod.
 *
 */

void
cleanup_module (void)
{
    struct device *cur_dev;

    cur_dev=fa31x_dev_list;
    while (cur_dev) {
        fa31x_dev_list = ((struct fa31x_priv *)(cur_dev->priv))->next;
        unregister_netdev (cur_dev);
        release_region (cur_dev->base_addr, FA31x_PCI_IO_SIZE);
        kfree (cur_dev);
        cur_dev = fa31x_dev_list;
    }
}
#endif /* MODULE */

static void  ReadEEWord(u16 iobase,u32 wordOffset,u16* pEeData)
{
#define DP_MEAR 0x08
#define EECLK  0x00000004
#define EECS   0x00000008
#define EEDI   0x00000001
#define EEDO   0x00000002

u32 i = 9;
u32 mask = 0x100;
u32 command = 0x180 | wordOffset;
u32 data;
u32 temp;
   //
   // Start the sequence with Chip Select 0 for a 4 microsecond cycle.
   //
   DP_REG32_WRITE (DP_MEAR, 0);
   udelay( 2 );
   DP_REG32_WRITE (DP_MEAR, EECLK );
   udelay( 2 );
   //
   // Write out the read command, one bit at a time, insuring that each transition
   // of the clock lasts at least 2 microseconds.
   //
   while ( i-- ) {
      data = ( command & mask ) ? EECS | EEDI : EECS;
      DP_REG32_WRITE(DP_MEAR, data );
      udelay( 2 );
      DP_REG32_WRITE(DP_MEAR, data | EECLK );
      udelay( 2 );
      mask = mask >> 1;
   }
   //
   // Read in the 16 bits of data, one bit at a time, insuring that each
   // transition of the clock lasts at least 2 microseconds.
   //
   i = 16;
   mask = 0x8000;
   data = 0;
   while ( i--) {
      DP_REG32_WRITE(DP_MEAR,  EECS );
      udelay( 2 );
      DP_REG32_WRITE(DP_MEAR, EECS | EECLK );
      udelay( 2 );
      temp = DP_REG32_READ(DP_MEAR);
      if ( temp & EEDO ) {
     data |= mask;
      }
    mask = mask >> 1;
  }
  *pEeData = ( u16 )data;
}




/*
 * Local variables:
 *
 * for Redhat 5.2/6.0/6.x
 compile-command: "gcc -D__KERNEL__ -DMODULE -Wall -Wstrict-prototypes -O -m486 -c fa31x.c"
 * for Redhat 7.0
 compile-command: "kgcc -D__KERNEL__ -DMODULE -Wall -Wstrict-prototypes -O -m486 -c fa31x.c -I/usr/src/linux/include"
 *  version-control: t
 *  kept-new-versions: 5
 * End:
 */