summaryrefslogtreecommitdiffstats
path: root/drivers/net/ethernet/i825xx
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/ethernet/i825xx')
-rw-r--r--drivers/net/ethernet/i825xx/3c505.c1673
-rw-r--r--drivers/net/ethernet/i825xx/3c505.h292
-rw-r--r--drivers/net/ethernet/i825xx/3c507.c939
-rw-r--r--drivers/net/ethernet/i825xx/3c523.c1312
-rw-r--r--drivers/net/ethernet/i825xx/3c523.h355
-rw-r--r--drivers/net/ethernet/i825xx/3c527.c1661
-rw-r--r--drivers/net/ethernet/i825xx/3c527.h81
-rw-r--r--drivers/net/ethernet/i825xx/82596.c1632
-rw-r--r--drivers/net/ethernet/i825xx/Kconfig183
-rw-r--r--drivers/net/ethernet/i825xx/Makefile20
-rw-r--r--drivers/net/ethernet/i825xx/eepro.c1822
-rw-r--r--drivers/net/ethernet/i825xx/eexpress.c1720
-rw-r--r--drivers/net/ethernet/i825xx/eexpress.h179
-rw-r--r--drivers/net/ethernet/i825xx/ether1.c1094
-rw-r--r--drivers/net/ethernet/i825xx/ether1.h280
-rw-r--r--drivers/net/ethernet/i825xx/lasi_82596.c238
-rw-r--r--drivers/net/ethernet/i825xx/lib82596.c1412
-rw-r--r--drivers/net/ethernet/i825xx/lp486e.c1339
-rw-r--r--drivers/net/ethernet/i825xx/ni52.c1346
-rw-r--r--drivers/net/ethernet/i825xx/ni52.h310
-rw-r--r--drivers/net/ethernet/i825xx/sni_82596.c186
-rw-r--r--drivers/net/ethernet/i825xx/sun3_82586.c1213
-rw-r--r--drivers/net/ethernet/i825xx/sun3_82586.h318
-rw-r--r--drivers/net/ethernet/i825xx/znet.c924
24 files changed, 20529 insertions, 0 deletions
diff --git a/drivers/net/ethernet/i825xx/3c505.c b/drivers/net/ethernet/i825xx/3c505.c
new file mode 100644
index 00000000000..40e1a175fce
--- /dev/null
+++ b/drivers/net/ethernet/i825xx/3c505.c
@@ -0,0 +1,1673 @@
+/*
+ * Linux Ethernet device driver for the 3Com Etherlink Plus (3C505)
+ * By Craig Southeren, Juha Laiho and Philip Blundell
+ *
+ * 3c505.c This module implements an interface to the 3Com
+ * Etherlink Plus (3c505) Ethernet card. Linux device
+ * driver interface reverse engineered from the Linux 3C509
+ * device drivers. Some 3C505 information gleaned from
+ * the Crynwr packet driver. Still this driver would not
+ * be here without 3C505 technical reference provided by
+ * 3Com.
+ *
+ * $Id: 3c505.c,v 1.10 1996/04/16 13:06:27 phil Exp $
+ *
+ * Authors: Linux 3c505 device driver by
+ * Craig Southeren, <craigs@ineluki.apana.org.au>
+ * Final debugging by
+ * Andrew Tridgell, <tridge@nimbus.anu.edu.au>
+ * Auto irq/address, tuning, cleanup and v1.1.4+ kernel mods by
+ * Juha Laiho, <jlaiho@ichaos.nullnet.fi>
+ * Linux 3C509 driver by
+ * Donald Becker, <becker@super.org>
+ * (Now at <becker@scyld.com>)
+ * Crynwr packet driver by
+ * Krishnan Gopalan and Gregg Stefancik,
+ * Clemson University Engineering Computer Operations.
+ * Portions of the code have been adapted from the 3c505
+ * driver for NCSA Telnet by Bruce Orchard and later
+ * modified by Warren Van Houten and krus@diku.dk.
+ * 3C505 technical information provided by
+ * Terry Murphy, of 3Com Network Adapter Division
+ * Linux 1.3.0 changes by
+ * Alan Cox <Alan.Cox@linux.org>
+ * More debugging, DMA support, currently maintained by
+ * Philip Blundell <philb@gnu.org>
+ * Multicard/soft configurable dma channel/rev 2 hardware support
+ * by Christopher Collins <ccollins@pcug.org.au>
+ * Ethtool support (jgarzik), 11/17/2001
+ */
+
+#define DRV_NAME "3c505"
+#define DRV_VERSION "1.10a"
+
+
+/* Theory of operation:
+ *
+ * The 3c505 is quite an intelligent board. All communication with it is done
+ * by means of Primary Command Blocks (PCBs); these are transferred using PIO
+ * through the command register. The card has 256k of on-board RAM, which is
+ * used to buffer received packets. It might seem at first that more buffers
+ * are better, but in fact this isn't true. From my tests, it seems that
+ * more than about 10 buffers are unnecessary, and there is a noticeable
+ * performance hit in having more active on the card. So the majority of the
+ * card's memory isn't, in fact, used. Sadly, the card only has one transmit
+ * buffer and, short of loading our own firmware into it (which is what some
+ * drivers resort to) there's nothing we can do about this.
+ *
+ * We keep up to 4 "receive packet" commands active on the board at a time.
+ * When a packet comes in, so long as there is a receive command active, the
+ * board will send us a "packet received" PCB and then add the data for that
+ * packet to the DMA queue. If a DMA transfer is not already in progress, we
+ * set one up to start uploading the data. We have to maintain a list of
+ * backlogged receive packets, because the card may decide to tell us about
+ * a newly-arrived packet at any time, and we may not be able to start a DMA
+ * transfer immediately (ie one may already be going on). We can't NAK the
+ * PCB, because then it would throw the packet away.
+ *
+ * Trying to send a PCB to the card at the wrong moment seems to have bad
+ * effects. If we send it a transmit PCB while a receive DMA is happening,
+ * it will just NAK the PCB and so we will have wasted our time. Worse, it
+ * sometimes seems to interrupt the transfer. The majority of the low-level
+ * code is protected by one huge semaphore -- "busy" -- which is set whenever
+ * it probably isn't safe to do anything to the card. The receive routine
+ * must gain a lock on "busy" before it can start a DMA transfer, and the
+ * transmit routine must gain a lock before it sends the first PCB to the card.
+ * The send_pcb() routine also has an internal semaphore to protect it against
+ * being re-entered (which would be disastrous) -- this is needed because
+ * several things can happen asynchronously (re-priming the receiver and
+ * asking the card for statistics, for example). send_pcb() will also refuse
+ * to talk to the card at all if a DMA upload is happening. The higher-level
+ * networking code will reschedule a later retry if some part of the driver
+ * is blocked. In practice, this doesn't seem to happen very often.
+ */
+
+/* This driver may now work with revision 2.x hardware, since all the read
+ * operations on the HCR have been removed (we now keep our own softcopy).
+ * But I don't have an old card to test it on.
+ *
+ * This has had the bad effect that the autoprobe routine is now a bit
+ * less friendly to other devices. However, it was never very good.
+ * before, so I doubt it will hurt anybody.
+ */
+
+/* The driver is a mess. I took Craig's and Juha's code, and hacked it firstly
+ * to make it more reliable, and secondly to add DMA mode. Many things could
+ * probably be done better; the concurrency protection is particularly awful.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/interrupt.h>
+#include <linux/errno.h>
+#include <linux/in.h>
+#include <linux/ioport.h>
+#include <linux/spinlock.h>
+#include <linux/ethtool.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+#include <linux/gfp.h>
+
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/init.h>
+
+#include "3c505.h"
+
+/*********************************************************
+ *
+ * define debug messages here as common strings to reduce space
+ *
+ *********************************************************/
+
+#define filename __FILE__
+
+#define timeout_msg "*** timeout at %s:%s (line %d) ***\n"
+#define TIMEOUT_MSG(lineno) \
+ pr_notice(timeout_msg, filename, __func__, (lineno))
+
+#define invalid_pcb_msg "*** invalid pcb length %d at %s:%s (line %d) ***\n"
+#define INVALID_PCB_MSG(len) \
+ pr_notice(invalid_pcb_msg, (len), filename, __func__, __LINE__)
+
+#define search_msg "%s: Looking for 3c505 adapter at address %#x..."
+
+#define stilllooking_msg "still looking..."
+
+#define found_msg "found.\n"
+
+#define notfound_msg "not found (reason = %d)\n"
+
+#define couldnot_msg "%s: 3c505 not found\n"
+
+/*********************************************************
+ *
+ * various other debug stuff
+ *
+ *********************************************************/
+
+#ifdef ELP_DEBUG
+static int elp_debug = ELP_DEBUG;
+#else
+static int elp_debug;
+#endif
+#define debug elp_debug
+
+/*
+ * 0 = no messages (well, some)
+ * 1 = messages when high level commands performed
+ * 2 = messages when low level commands performed
+ * 3 = messages when interrupts received
+ */
+
+/*****************************************************************
+ *
+ * List of I/O-addresses we try to auto-sense
+ * Last element MUST BE 0!
+ *****************************************************************/
+
+static int addr_list[] __initdata = {0x300, 0x280, 0x310, 0};
+
+/* Dma Memory related stuff */
+
+static unsigned long dma_mem_alloc(int size)
+{
+ int order = get_order(size);
+ return __get_dma_pages(GFP_KERNEL, order);
+}
+
+
+/*****************************************************************
+ *
+ * Functions for I/O (note the inline !)
+ *
+ *****************************************************************/
+
+static inline unsigned char inb_status(unsigned int base_addr)
+{
+ return inb(base_addr + PORT_STATUS);
+}
+
+static inline int inb_command(unsigned int base_addr)
+{
+ return inb(base_addr + PORT_COMMAND);
+}
+
+static inline void outb_control(unsigned char val, struct net_device *dev)
+{
+ outb(val, dev->base_addr + PORT_CONTROL);
+ ((elp_device *)(netdev_priv(dev)))->hcr_val = val;
+}
+
+#define HCR_VAL(x) (((elp_device *)(netdev_priv(x)))->hcr_val)
+
+static inline void outb_command(unsigned char val, unsigned int base_addr)
+{
+ outb(val, base_addr + PORT_COMMAND);
+}
+
+static inline unsigned int backlog_next(unsigned int n)
+{
+ return (n + 1) % BACKLOG_SIZE;
+}
+
+/*****************************************************************
+ *
+ * useful functions for accessing the adapter
+ *
+ *****************************************************************/
+
+/*
+ * use this routine when accessing the ASF bits as they are
+ * changed asynchronously by the adapter
+ */
+
+/* get adapter PCB status */
+#define GET_ASF(addr) \
+ (get_status(addr)&ASF_PCB_MASK)
+
+static inline int get_status(unsigned int base_addr)
+{
+ unsigned long timeout = jiffies + 10*HZ/100;
+ register int stat1;
+ do {
+ stat1 = inb_status(base_addr);
+ } while (stat1 != inb_status(base_addr) && time_before(jiffies, timeout));
+ if (time_after_eq(jiffies, timeout))
+ TIMEOUT_MSG(__LINE__);
+ return stat1;
+}
+
+static inline void set_hsf(struct net_device *dev, int hsf)
+{
+ elp_device *adapter = netdev_priv(dev);
+ unsigned long flags;
+
+ spin_lock_irqsave(&adapter->lock, flags);
+ outb_control((HCR_VAL(dev) & ~HSF_PCB_MASK) | hsf, dev);
+ spin_unlock_irqrestore(&adapter->lock, flags);
+}
+
+static bool start_receive(struct net_device *, pcb_struct *);
+
+static inline void adapter_reset(struct net_device *dev)
+{
+ unsigned long timeout;
+ elp_device *adapter = netdev_priv(dev);
+ unsigned char orig_hcr = adapter->hcr_val;
+
+ outb_control(0, dev);
+
+ if (inb_status(dev->base_addr) & ACRF) {
+ do {
+ inb_command(dev->base_addr);
+ timeout = jiffies + 2*HZ/100;
+ while (time_before_eq(jiffies, timeout) && !(inb_status(dev->base_addr) & ACRF));
+ } while (inb_status(dev->base_addr) & ACRF);
+ set_hsf(dev, HSF_PCB_NAK);
+ }
+ outb_control(adapter->hcr_val | ATTN | DIR, dev);
+ mdelay(10);
+ outb_control(adapter->hcr_val & ~ATTN, dev);
+ mdelay(10);
+ outb_control(adapter->hcr_val | FLSH, dev);
+ mdelay(10);
+ outb_control(adapter->hcr_val & ~FLSH, dev);
+ mdelay(10);
+
+ outb_control(orig_hcr, dev);
+ if (!start_receive(dev, &adapter->tx_pcb))
+ pr_err("%s: start receive command failed\n", dev->name);
+}
+
+/* Check to make sure that a DMA transfer hasn't timed out. This should
+ * never happen in theory, but seems to occur occasionally if the card gets
+ * prodded at the wrong time.
+ */
+static inline void check_3c505_dma(struct net_device *dev)
+{
+ elp_device *adapter = netdev_priv(dev);
+ if (adapter->dmaing && time_after(jiffies, adapter->current_dma.start_time + 10)) {
+ unsigned long flags, f;
+ pr_err("%s: DMA %s timed out, %d bytes left\n", dev->name,
+ adapter->current_dma.direction ? "download" : "upload",
+ get_dma_residue(dev->dma));
+ spin_lock_irqsave(&adapter->lock, flags);
+ adapter->dmaing = 0;
+ adapter->busy = 0;
+
+ f=claim_dma_lock();
+ disable_dma(dev->dma);
+ release_dma_lock(f);
+
+ if (adapter->rx_active)
+ adapter->rx_active--;
+ outb_control(adapter->hcr_val & ~(DMAE | TCEN | DIR), dev);
+ spin_unlock_irqrestore(&adapter->lock, flags);
+ }
+}
+
+/* Primitive functions used by send_pcb() */
+static inline bool send_pcb_slow(unsigned int base_addr, unsigned char byte)
+{
+ unsigned long timeout;
+ outb_command(byte, base_addr);
+ for (timeout = jiffies + 5*HZ/100; time_before(jiffies, timeout);) {
+ if (inb_status(base_addr) & HCRE)
+ return false;
+ }
+ pr_warning("3c505: send_pcb_slow timed out\n");
+ return true;
+}
+
+static inline bool send_pcb_fast(unsigned int base_addr, unsigned char byte)
+{
+ unsigned int timeout;
+ outb_command(byte, base_addr);
+ for (timeout = 0; timeout < 40000; timeout++) {
+ if (inb_status(base_addr) & HCRE)
+ return false;
+ }
+ pr_warning("3c505: send_pcb_fast timed out\n");
+ return true;
+}
+
+/* Check to see if the receiver needs restarting, and kick it if so */
+static inline void prime_rx(struct net_device *dev)
+{
+ elp_device *adapter = netdev_priv(dev);
+ while (adapter->rx_active < ELP_RX_PCBS && netif_running(dev)) {
+ if (!start_receive(dev, &adapter->itx_pcb))
+ break;
+ }
+}
+
+/*****************************************************************
+ *
+ * send_pcb
+ * Send a PCB to the adapter.
+ *
+ * output byte to command reg --<--+
+ * wait until HCRE is non zero |
+ * loop until all bytes sent -->--+
+ * set HSF1 and HSF2 to 1
+ * output pcb length
+ * wait until ASF give ACK or NAK
+ * set HSF1 and HSF2 to 0
+ *
+ *****************************************************************/
+
+/* This can be quite slow -- the adapter is allowed to take up to 40ms
+ * to respond to the initial interrupt.
+ *
+ * We run initially with interrupts turned on, but with a semaphore set
+ * so that nobody tries to re-enter this code. Once the first byte has
+ * gone through, we turn interrupts off and then send the others (the
+ * timeout is reduced to 500us).
+ */
+
+static bool send_pcb(struct net_device *dev, pcb_struct * pcb)
+{
+ int i;
+ unsigned long timeout;
+ elp_device *adapter = netdev_priv(dev);
+ unsigned long flags;
+
+ check_3c505_dma(dev);
+
+ if (adapter->dmaing && adapter->current_dma.direction == 0)
+ return false;
+
+ /* Avoid contention */
+ if (test_and_set_bit(1, &adapter->send_pcb_semaphore)) {
+ if (elp_debug >= 3) {
+ pr_debug("%s: send_pcb entered while threaded\n", dev->name);
+ }
+ return false;
+ }
+ /*
+ * load each byte into the command register and
+ * wait for the HCRE bit to indicate the adapter
+ * had read the byte
+ */
+ set_hsf(dev, 0);
+
+ if (send_pcb_slow(dev->base_addr, pcb->command))
+ goto abort;
+
+ spin_lock_irqsave(&adapter->lock, flags);
+
+ if (send_pcb_fast(dev->base_addr, pcb->length))
+ goto sti_abort;
+
+ for (i = 0; i < pcb->length; i++) {
+ if (send_pcb_fast(dev->base_addr, pcb->data.raw[i]))
+ goto sti_abort;
+ }
+
+ outb_control(adapter->hcr_val | 3, dev); /* signal end of PCB */
+ outb_command(2 + pcb->length, dev->base_addr);
+
+ /* now wait for the acknowledgement */
+ spin_unlock_irqrestore(&adapter->lock, flags);
+
+ for (timeout = jiffies + 5*HZ/100; time_before(jiffies, timeout);) {
+ switch (GET_ASF(dev->base_addr)) {
+ case ASF_PCB_ACK:
+ adapter->send_pcb_semaphore = 0;
+ return true;
+
+ case ASF_PCB_NAK:
+#ifdef ELP_DEBUG
+ pr_debug("%s: send_pcb got NAK\n", dev->name);
+#endif
+ goto abort;
+ }
+ }
+
+ if (elp_debug >= 1)
+ pr_debug("%s: timeout waiting for PCB acknowledge (status %02x)\n",
+ dev->name, inb_status(dev->base_addr));
+ goto abort;
+
+ sti_abort:
+ spin_unlock_irqrestore(&adapter->lock, flags);
+ abort:
+ adapter->send_pcb_semaphore = 0;
+ return false;
+}
+
+
+/*****************************************************************
+ *
+ * receive_pcb
+ * Read a PCB from the adapter
+ *
+ * wait for ACRF to be non-zero ---<---+
+ * input a byte |
+ * if ASF1 and ASF2 were not both one |
+ * before byte was read, loop --->---+
+ * set HSF1 and HSF2 for ack
+ *
+ *****************************************************************/
+
+static bool receive_pcb(struct net_device *dev, pcb_struct * pcb)
+{
+ int i, j;
+ int total_length;
+ int stat;
+ unsigned long timeout;
+ unsigned long flags;
+
+ elp_device *adapter = netdev_priv(dev);
+
+ set_hsf(dev, 0);
+
+ /* get the command code */
+ timeout = jiffies + 2*HZ/100;
+ while (((stat = get_status(dev->base_addr)) & ACRF) == 0 && time_before(jiffies, timeout));
+ if (time_after_eq(jiffies, timeout)) {
+ TIMEOUT_MSG(__LINE__);
+ return false;
+ }
+ pcb->command = inb_command(dev->base_addr);
+
+ /* read the data length */
+ timeout = jiffies + 3*HZ/100;
+ while (((stat = get_status(dev->base_addr)) & ACRF) == 0 && time_before(jiffies, timeout));
+ if (time_after_eq(jiffies, timeout)) {
+ TIMEOUT_MSG(__LINE__);
+ pr_info("%s: status %02x\n", dev->name, stat);
+ return false;
+ }
+ pcb->length = inb_command(dev->base_addr);
+
+ if (pcb->length > MAX_PCB_DATA) {
+ INVALID_PCB_MSG(pcb->length);
+ adapter_reset(dev);
+ return false;
+ }
+ /* read the data */
+ spin_lock_irqsave(&adapter->lock, flags);
+ for (i = 0; i < MAX_PCB_DATA; i++) {
+ for (j = 0; j < 20000; j++) {
+ stat = get_status(dev->base_addr);
+ if (stat & ACRF)
+ break;
+ }
+ pcb->data.raw[i] = inb_command(dev->base_addr);
+ if ((stat & ASF_PCB_MASK) == ASF_PCB_END || j >= 20000)
+ break;
+ }
+ spin_unlock_irqrestore(&adapter->lock, flags);
+ if (i >= MAX_PCB_DATA) {
+ INVALID_PCB_MSG(i);
+ return false;
+ }
+ if (j >= 20000) {
+ TIMEOUT_MSG(__LINE__);
+ return false;
+ }
+ /* the last "data" byte was really the length! */
+ total_length = pcb->data.raw[i];
+
+ /* safety check total length vs data length */
+ if (total_length != (pcb->length + 2)) {
+ if (elp_debug >= 2)
+ pr_warning("%s: mangled PCB received\n", dev->name);
+ set_hsf(dev, HSF_PCB_NAK);
+ return false;
+ }
+
+ if (pcb->command == CMD_RECEIVE_PACKET_COMPLETE) {
+ if (test_and_set_bit(0, (void *) &adapter->busy)) {
+ if (backlog_next(adapter->rx_backlog.in) == adapter->rx_backlog.out) {
+ set_hsf(dev, HSF_PCB_NAK);
+ pr_warning("%s: PCB rejected, transfer in progress and backlog full\n", dev->name);
+ pcb->command = 0;
+ return true;
+ } else {
+ pcb->command = 0xff;
+ }
+ }
+ }
+ set_hsf(dev, HSF_PCB_ACK);
+ return true;
+}
+
+/******************************************************
+ *
+ * queue a receive command on the adapter so we will get an
+ * interrupt when a packet is received.
+ *
+ ******************************************************/
+
+static bool start_receive(struct net_device *dev, pcb_struct * tx_pcb)
+{
+ bool status;
+ elp_device *adapter = netdev_priv(dev);
+
+ if (elp_debug >= 3)
+ pr_debug("%s: restarting receiver\n", dev->name);
+ tx_pcb->command = CMD_RECEIVE_PACKET;
+ tx_pcb->length = sizeof(struct Rcv_pkt);
+ tx_pcb->data.rcv_pkt.buf_seg
+ = tx_pcb->data.rcv_pkt.buf_ofs = 0; /* Unused */
+ tx_pcb->data.rcv_pkt.buf_len = 1600;
+ tx_pcb->data.rcv_pkt.timeout = 0; /* set timeout to zero */
+ status = send_pcb(dev, tx_pcb);
+ if (status)
+ adapter->rx_active++;
+ return status;
+}
+
+/******************************************************
+ *
+ * extract a packet from the adapter
+ * this routine is only called from within the interrupt
+ * service routine, so no cli/sti calls are needed
+ * note that the length is always assumed to be even
+ *
+ ******************************************************/
+
+static void receive_packet(struct net_device *dev, int len)
+{
+ int rlen;
+ elp_device *adapter = netdev_priv(dev);
+ void *target;
+ struct sk_buff *skb;
+ unsigned long flags;
+
+ rlen = (len + 1) & ~1;
+ skb = dev_alloc_skb(rlen + 2);
+
+ if (!skb) {
+ pr_warning("%s: memory squeeze, dropping packet\n", dev->name);
+ target = adapter->dma_buffer;
+ adapter->current_dma.target = NULL;
+ /* FIXME: stats */
+ return;
+ }
+
+ skb_reserve(skb, 2);
+ target = skb_put(skb, rlen);
+ if ((unsigned long)(target + rlen) >= MAX_DMA_ADDRESS) {
+ adapter->current_dma.target = target;
+ target = adapter->dma_buffer;
+ } else {
+ adapter->current_dma.target = NULL;
+ }
+
+ /* if this happens, we die */
+ if (test_and_set_bit(0, (void *) &adapter->dmaing))
+ pr_err("%s: rx blocked, DMA in progress, dir %d\n",
+ dev->name, adapter->current_dma.direction);
+
+ adapter->current_dma.direction = 0;
+ adapter->current_dma.length = rlen;
+ adapter->current_dma.skb = skb;
+ adapter->current_dma.start_time = jiffies;
+
+ outb_control(adapter->hcr_val | DIR | TCEN | DMAE, dev);
+
+ flags=claim_dma_lock();
+ disable_dma(dev->dma);
+ clear_dma_ff(dev->dma);
+ set_dma_mode(dev->dma, 0x04); /* dma read */
+ set_dma_addr(dev->dma, isa_virt_to_bus(target));
+ set_dma_count(dev->dma, rlen);
+ enable_dma(dev->dma);
+ release_dma_lock(flags);
+
+ if (elp_debug >= 3) {
+ pr_debug("%s: rx DMA transfer started\n", dev->name);
+ }
+
+ if (adapter->rx_active)
+ adapter->rx_active--;
+
+ if (!adapter->busy)
+ pr_warning("%s: receive_packet called, busy not set.\n", dev->name);
+}
+
+/******************************************************
+ *
+ * interrupt handler
+ *
+ ******************************************************/
+
+static irqreturn_t elp_interrupt(int irq, void *dev_id)
+{
+ int len;
+ int dlen;
+ int icount = 0;
+ struct net_device *dev = dev_id;
+ elp_device *adapter = netdev_priv(dev);
+ unsigned long timeout;
+
+ spin_lock(&adapter->lock);
+
+ do {
+ /*
+ * has a DMA transfer finished?
+ */
+ if (inb_status(dev->base_addr) & DONE) {
+ if (!adapter->dmaing)
+ pr_warning("%s: phantom DMA completed\n", dev->name);
+
+ if (elp_debug >= 3)
+ pr_debug("%s: %s DMA complete, status %02x\n", dev->name,
+ adapter->current_dma.direction ? "tx" : "rx",
+ inb_status(dev->base_addr));
+
+ outb_control(adapter->hcr_val & ~(DMAE | TCEN | DIR), dev);
+ if (adapter->current_dma.direction) {
+ dev_kfree_skb_irq(adapter->current_dma.skb);
+ } else {
+ struct sk_buff *skb = adapter->current_dma.skb;
+ if (skb) {
+ if (adapter->current_dma.target) {
+ /* have already done the skb_put() */
+ memcpy(adapter->current_dma.target, adapter->dma_buffer, adapter->current_dma.length);
+ }
+ skb->protocol = eth_type_trans(skb,dev);
+ dev->stats.rx_bytes += skb->len;
+ netif_rx(skb);
+ }
+ }
+ adapter->dmaing = 0;
+ if (adapter->rx_backlog.in != adapter->rx_backlog.out) {
+ int t = adapter->rx_backlog.length[adapter->rx_backlog.out];
+ adapter->rx_backlog.out = backlog_next(adapter->rx_backlog.out);
+ if (elp_debug >= 2)
+ pr_debug("%s: receiving backlogged packet (%d)\n", dev->name, t);
+ receive_packet(dev, t);
+ } else {
+ adapter->busy = 0;
+ }
+ } else {
+ /* has one timed out? */
+ check_3c505_dma(dev);
+ }
+
+ /*
+ * receive a PCB from the adapter
+ */
+ timeout = jiffies + 3*HZ/100;
+ while ((inb_status(dev->base_addr) & ACRF) != 0 && time_before(jiffies, timeout)) {
+ if (receive_pcb(dev, &adapter->irx_pcb)) {
+ switch (adapter->irx_pcb.command)
+ {
+ case 0:
+ break;
+ /*
+ * received a packet - this must be handled fast
+ */
+ case 0xff:
+ case CMD_RECEIVE_PACKET_COMPLETE:
+ /* if the device isn't open, don't pass packets up the stack */
+ if (!netif_running(dev))
+ break;
+ len = adapter->irx_pcb.data.rcv_resp.pkt_len;
+ dlen = adapter->irx_pcb.data.rcv_resp.buf_len;
+ if (adapter->irx_pcb.data.rcv_resp.timeout != 0) {
+ pr_err("%s: interrupt - packet not received correctly\n", dev->name);
+ } else {
+ if (elp_debug >= 3) {
+ pr_debug("%s: interrupt - packet received of length %i (%i)\n",
+ dev->name, len, dlen);
+ }
+ if (adapter->irx_pcb.command == 0xff) {
+ if (elp_debug >= 2)
+ pr_debug("%s: adding packet to backlog (len = %d)\n",
+ dev->name, dlen);
+ adapter->rx_backlog.length[adapter->rx_backlog.in] = dlen;
+ adapter->rx_backlog.in = backlog_next(adapter->rx_backlog.in);
+ } else {
+ receive_packet(dev, dlen);
+ }
+ if (elp_debug >= 3)
+ pr_debug("%s: packet received\n", dev->name);
+ }
+ break;
+
+ /*
+ * 82586 configured correctly
+ */
+ case CMD_CONFIGURE_82586_RESPONSE:
+ adapter->got[CMD_CONFIGURE_82586] = 1;
+ if (elp_debug >= 3)
+ pr_debug("%s: interrupt - configure response received\n", dev->name);
+ break;
+
+ /*
+ * Adapter memory configuration
+ */
+ case CMD_CONFIGURE_ADAPTER_RESPONSE:
+ adapter->got[CMD_CONFIGURE_ADAPTER_MEMORY] = 1;
+ if (elp_debug >= 3)
+ pr_debug("%s: Adapter memory configuration %s.\n", dev->name,
+ adapter->irx_pcb.data.failed ? "failed" : "succeeded");
+ break;
+
+ /*
+ * Multicast list loading
+ */
+ case CMD_LOAD_MULTICAST_RESPONSE:
+ adapter->got[CMD_LOAD_MULTICAST_LIST] = 1;
+ if (elp_debug >= 3)
+ pr_debug("%s: Multicast address list loading %s.\n", dev->name,
+ adapter->irx_pcb.data.failed ? "failed" : "succeeded");
+ break;
+
+ /*
+ * Station address setting
+ */
+ case CMD_SET_ADDRESS_RESPONSE:
+ adapter->got[CMD_SET_STATION_ADDRESS] = 1;
+ if (elp_debug >= 3)
+ pr_debug("%s: Ethernet address setting %s.\n", dev->name,
+ adapter->irx_pcb.data.failed ? "failed" : "succeeded");
+ break;
+
+
+ /*
+ * received board statistics
+ */
+ case CMD_NETWORK_STATISTICS_RESPONSE:
+ dev->stats.rx_packets += adapter->irx_pcb.data.netstat.tot_recv;
+ dev->stats.tx_packets += adapter->irx_pcb.data.netstat.tot_xmit;
+ dev->stats.rx_crc_errors += adapter->irx_pcb.data.netstat.err_CRC;
+ dev->stats.rx_frame_errors += adapter->irx_pcb.data.netstat.err_align;
+ dev->stats.rx_fifo_errors += adapter->irx_pcb.data.netstat.err_ovrrun;
+ dev->stats.rx_over_errors += adapter->irx_pcb.data.netstat.err_res;
+ adapter->got[CMD_NETWORK_STATISTICS] = 1;
+ if (elp_debug >= 3)
+ pr_debug("%s: interrupt - statistics response received\n", dev->name);
+ break;
+
+ /*
+ * sent a packet
+ */
+ case CMD_TRANSMIT_PACKET_COMPLETE:
+ if (elp_debug >= 3)
+ pr_debug("%s: interrupt - packet sent\n", dev->name);
+ if (!netif_running(dev))
+ break;
+ switch (adapter->irx_pcb.data.xmit_resp.c_stat) {
+ case 0xffff:
+ dev->stats.tx_aborted_errors++;
+ pr_info("%s: transmit timed out, network cable problem?\n", dev->name);
+ break;
+ case 0xfffe:
+ dev->stats.tx_fifo_errors++;
+ pr_info("%s: transmit timed out, FIFO underrun\n", dev->name);
+ break;
+ }
+ netif_wake_queue(dev);
+ break;
+
+ /*
+ * some unknown PCB
+ */
+ default:
+ pr_debug("%s: unknown PCB received - %2.2x\n",
+ dev->name, adapter->irx_pcb.command);
+ break;
+ }
+ } else {
+ pr_warning("%s: failed to read PCB on interrupt\n", dev->name);
+ adapter_reset(dev);
+ }
+ }
+
+ } while (icount++ < 5 && (inb_status(dev->base_addr) & (ACRF | DONE)));
+
+ prime_rx(dev);
+
+ /*
+ * indicate no longer in interrupt routine
+ */
+ spin_unlock(&adapter->lock);
+ return IRQ_HANDLED;
+}
+
+
+/******************************************************
+ *
+ * open the board
+ *
+ ******************************************************/
+
+static int elp_open(struct net_device *dev)
+{
+ elp_device *adapter = netdev_priv(dev);
+ int retval;
+
+ if (elp_debug >= 3)
+ pr_debug("%s: request to open device\n", dev->name);
+
+ /*
+ * make sure we actually found the device
+ */
+ if (adapter == NULL) {
+ pr_err("%s: Opening a non-existent physical device\n", dev->name);
+ return -EAGAIN;
+ }
+ /*
+ * disable interrupts on the board
+ */
+ outb_control(0, dev);
+
+ /*
+ * clear any pending interrupts
+ */
+ inb_command(dev->base_addr);
+ adapter_reset(dev);
+
+ /*
+ * no receive PCBs active
+ */
+ adapter->rx_active = 0;
+
+ adapter->busy = 0;
+ adapter->send_pcb_semaphore = 0;
+ adapter->rx_backlog.in = 0;
+ adapter->rx_backlog.out = 0;
+
+ spin_lock_init(&adapter->lock);
+
+ /*
+ * install our interrupt service routine
+ */
+ if ((retval = request_irq(dev->irq, elp_interrupt, 0, dev->name, dev))) {
+ pr_err("%s: could not allocate IRQ%d\n", dev->name, dev->irq);
+ return retval;
+ }
+ if ((retval = request_dma(dev->dma, dev->name))) {
+ free_irq(dev->irq, dev);
+ pr_err("%s: could not allocate DMA%d channel\n", dev->name, dev->dma);
+ return retval;
+ }
+ adapter->dma_buffer = (void *) dma_mem_alloc(DMA_BUFFER_SIZE);
+ if (!adapter->dma_buffer) {
+ pr_err("%s: could not allocate DMA buffer\n", dev->name);
+ free_dma(dev->dma);
+ free_irq(dev->irq, dev);
+ return -ENOMEM;
+ }
+ adapter->dmaing = 0;
+
+ /*
+ * enable interrupts on the board
+ */
+ outb_control(CMDE, dev);
+
+ /*
+ * configure adapter memory: we need 10 multicast addresses, default==0
+ */
+ if (elp_debug >= 3)
+ pr_debug("%s: sending 3c505 memory configuration command\n", dev->name);
+ adapter->tx_pcb.command = CMD_CONFIGURE_ADAPTER_MEMORY;
+ adapter->tx_pcb.data.memconf.cmd_q = 10;
+ adapter->tx_pcb.data.memconf.rcv_q = 20;
+ adapter->tx_pcb.data.memconf.mcast = 10;
+ adapter->tx_pcb.data.memconf.frame = 20;
+ adapter->tx_pcb.data.memconf.rcv_b = 20;
+ adapter->tx_pcb.data.memconf.progs = 0;
+ adapter->tx_pcb.length = sizeof(struct Memconf);
+ adapter->got[CMD_CONFIGURE_ADAPTER_MEMORY] = 0;
+ if (!send_pcb(dev, &adapter->tx_pcb))
+ pr_err("%s: couldn't send memory configuration command\n", dev->name);
+ else {
+ unsigned long timeout = jiffies + TIMEOUT;
+ while (adapter->got[CMD_CONFIGURE_ADAPTER_MEMORY] == 0 && time_before(jiffies, timeout));
+ if (time_after_eq(jiffies, timeout))
+ TIMEOUT_MSG(__LINE__);
+ }
+
+
+ /*
+ * configure adapter to receive broadcast messages and wait for response
+ */
+ if (elp_debug >= 3)
+ pr_debug("%s: sending 82586 configure command\n", dev->name);
+ adapter->tx_pcb.command = CMD_CONFIGURE_82586;
+ adapter->tx_pcb.data.configure = NO_LOOPBACK | RECV_BROAD;
+ adapter->tx_pcb.length = 2;
+ adapter->got[CMD_CONFIGURE_82586] = 0;
+ if (!send_pcb(dev, &adapter->tx_pcb))
+ pr_err("%s: couldn't send 82586 configure command\n", dev->name);
+ else {
+ unsigned long timeout = jiffies + TIMEOUT;
+ while (adapter->got[CMD_CONFIGURE_82586] == 0 && time_before(jiffies, timeout));
+ if (time_after_eq(jiffies, timeout))
+ TIMEOUT_MSG(__LINE__);
+ }
+
+ /* enable burst-mode DMA */
+ /* outb(0x1, dev->base_addr + PORT_AUXDMA); */
+
+ /*
+ * queue receive commands to provide buffering
+ */
+ prime_rx(dev);
+ if (elp_debug >= 3)
+ pr_debug("%s: %d receive PCBs active\n", dev->name, adapter->rx_active);
+
+ /*
+ * device is now officially open!
+ */
+
+ netif_start_queue(dev);
+ return 0;
+}
+
+
+/******************************************************
+ *
+ * send a packet to the adapter
+ *
+ ******************************************************/
+
+static netdev_tx_t send_packet(struct net_device *dev, struct sk_buff *skb)
+{
+ elp_device *adapter = netdev_priv(dev);
+ unsigned long target;
+ unsigned long flags;
+
+ /*
+ * make sure the length is even and no shorter than 60 bytes
+ */
+ unsigned int nlen = (((skb->len < 60) ? 60 : skb->len) + 1) & (~1);
+
+ if (test_and_set_bit(0, (void *) &adapter->busy)) {
+ if (elp_debug >= 2)
+ pr_debug("%s: transmit blocked\n", dev->name);
+ return false;
+ }
+
+ dev->stats.tx_bytes += nlen;
+
+ /*
+ * send the adapter a transmit packet command. Ignore segment and offset
+ * and make sure the length is even
+ */
+ adapter->tx_pcb.command = CMD_TRANSMIT_PACKET;
+ adapter->tx_pcb.length = sizeof(struct Xmit_pkt);
+ adapter->tx_pcb.data.xmit_pkt.buf_ofs
+ = adapter->tx_pcb.data.xmit_pkt.buf_seg = 0; /* Unused */
+ adapter->tx_pcb.data.xmit_pkt.pkt_len = nlen;
+
+ if (!send_pcb(dev, &adapter->tx_pcb)) {
+ adapter->busy = 0;
+ return false;
+ }
+ /* if this happens, we die */
+ if (test_and_set_bit(0, (void *) &adapter->dmaing))
+ pr_debug("%s: tx: DMA %d in progress\n", dev->name, adapter->current_dma.direction);
+
+ adapter->current_dma.direction = 1;
+ adapter->current_dma.start_time = jiffies;
+
+ if ((unsigned long)(skb->data + nlen) >= MAX_DMA_ADDRESS || nlen != skb->len) {
+ skb_copy_from_linear_data(skb, adapter->dma_buffer, nlen);
+ memset(adapter->dma_buffer+skb->len, 0, nlen-skb->len);
+ target = isa_virt_to_bus(adapter->dma_buffer);
+ }
+ else {
+ target = isa_virt_to_bus(skb->data);
+ }
+ adapter->current_dma.skb = skb;
+
+ flags=claim_dma_lock();
+ disable_dma(dev->dma);
+ clear_dma_ff(dev->dma);
+ set_dma_mode(dev->dma, 0x48); /* dma memory -> io */
+ set_dma_addr(dev->dma, target);
+ set_dma_count(dev->dma, nlen);
+ outb_control(adapter->hcr_val | DMAE | TCEN, dev);
+ enable_dma(dev->dma);
+ release_dma_lock(flags);
+
+ if (elp_debug >= 3)
+ pr_debug("%s: DMA transfer started\n", dev->name);
+
+ return true;
+}
+
+/*
+ * The upper layer thinks we timed out
+ */
+
+static void elp_timeout(struct net_device *dev)
+{
+ int stat;
+
+ stat = inb_status(dev->base_addr);
+ pr_warning("%s: transmit timed out, lost %s?\n", dev->name,
+ (stat & ACRF) ? "interrupt" : "command");
+ if (elp_debug >= 1)
+ pr_debug("%s: status %#02x\n", dev->name, stat);
+ dev->trans_start = jiffies; /* prevent tx timeout */
+ dev->stats.tx_dropped++;
+ netif_wake_queue(dev);
+}
+
+/******************************************************
+ *
+ * start the transmitter
+ * return 0 if sent OK, else return 1
+ *
+ ******************************************************/
+
+static netdev_tx_t elp_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ unsigned long flags;
+ elp_device *adapter = netdev_priv(dev);
+
+ spin_lock_irqsave(&adapter->lock, flags);
+ check_3c505_dma(dev);
+
+ if (elp_debug >= 3)
+ pr_debug("%s: request to send packet of length %d\n", dev->name, (int) skb->len);
+
+ netif_stop_queue(dev);
+
+ /*
+ * send the packet at skb->data for skb->len
+ */
+ if (!send_packet(dev, skb)) {
+ if (elp_debug >= 2) {
+ pr_debug("%s: failed to transmit packet\n", dev->name);
+ }
+ spin_unlock_irqrestore(&adapter->lock, flags);
+ return NETDEV_TX_BUSY;
+ }
+ if (elp_debug >= 3)
+ pr_debug("%s: packet of length %d sent\n", dev->name, (int) skb->len);
+
+ prime_rx(dev);
+ spin_unlock_irqrestore(&adapter->lock, flags);
+ netif_start_queue(dev);
+ return NETDEV_TX_OK;
+}
+
+/******************************************************
+ *
+ * return statistics on the board
+ *
+ ******************************************************/
+
+static struct net_device_stats *elp_get_stats(struct net_device *dev)
+{
+ elp_device *adapter = netdev_priv(dev);
+
+ if (elp_debug >= 3)
+ pr_debug("%s: request for stats\n", dev->name);
+
+ /* If the device is closed, just return the latest stats we have,
+ - we cannot ask from the adapter without interrupts */
+ if (!netif_running(dev))
+ return &dev->stats;
+
+ /* send a get statistics command to the board */
+ adapter->tx_pcb.command = CMD_NETWORK_STATISTICS;
+ adapter->tx_pcb.length = 0;
+ adapter->got[CMD_NETWORK_STATISTICS] = 0;
+ if (!send_pcb(dev, &adapter->tx_pcb))
+ pr_err("%s: couldn't send get statistics command\n", dev->name);
+ else {
+ unsigned long timeout = jiffies + TIMEOUT;
+ while (adapter->got[CMD_NETWORK_STATISTICS] == 0 && time_before(jiffies, timeout));
+ if (time_after_eq(jiffies, timeout)) {
+ TIMEOUT_MSG(__LINE__);
+ return &dev->stats;
+ }
+ }
+
+ /* statistics are now up to date */
+ return &dev->stats;
+}
+
+
+static void netdev_get_drvinfo(struct net_device *dev,
+ struct ethtool_drvinfo *info)
+{
+ strcpy(info->driver, DRV_NAME);
+ strcpy(info->version, DRV_VERSION);
+ sprintf(info->bus_info, "ISA 0x%lx", dev->base_addr);
+}
+
+static u32 netdev_get_msglevel(struct net_device *dev)
+{
+ return debug;
+}
+
+static void netdev_set_msglevel(struct net_device *dev, u32 level)
+{
+ debug = level;
+}
+
+static const struct ethtool_ops netdev_ethtool_ops = {
+ .get_drvinfo = netdev_get_drvinfo,
+ .get_msglevel = netdev_get_msglevel,
+ .set_msglevel = netdev_set_msglevel,
+};
+
+/******************************************************
+ *
+ * close the board
+ *
+ ******************************************************/
+
+static int elp_close(struct net_device *dev)
+{
+ elp_device *adapter = netdev_priv(dev);
+
+ if (elp_debug >= 3)
+ pr_debug("%s: request to close device\n", dev->name);
+
+ netif_stop_queue(dev);
+
+ /* Someone may request the device statistic information even when
+ * the interface is closed. The following will update the statistics
+ * structure in the driver, so we'll be able to give current statistics.
+ */
+ (void) elp_get_stats(dev);
+
+ /*
+ * disable interrupts on the board
+ */
+ outb_control(0, dev);
+
+ /*
+ * release the IRQ
+ */
+ free_irq(dev->irq, dev);
+
+ free_dma(dev->dma);
+ free_pages((unsigned long) adapter->dma_buffer, get_order(DMA_BUFFER_SIZE));
+
+ return 0;
+}
+
+
+/************************************************************
+ *
+ * Set multicast list
+ * num_addrs==0: clear mc_list
+ * num_addrs==-1: set promiscuous mode
+ * num_addrs>0: set mc_list
+ *
+ ************************************************************/
+
+static void elp_set_mc_list(struct net_device *dev)
+{
+ elp_device *adapter = netdev_priv(dev);
+ struct netdev_hw_addr *ha;
+ int i;
+ unsigned long flags;
+
+ if (elp_debug >= 3)
+ pr_debug("%s: request to set multicast list\n", dev->name);
+
+ spin_lock_irqsave(&adapter->lock, flags);
+
+ if (!(dev->flags & (IFF_PROMISC | IFF_ALLMULTI))) {
+ /* send a "load multicast list" command to the board, max 10 addrs/cmd */
+ /* if num_addrs==0 the list will be cleared */
+ adapter->tx_pcb.command = CMD_LOAD_MULTICAST_LIST;
+ adapter->tx_pcb.length = 6 * netdev_mc_count(dev);
+ i = 0;
+ netdev_for_each_mc_addr(ha, dev)
+ memcpy(adapter->tx_pcb.data.multicast[i++],
+ ha->addr, 6);
+ adapter->got[CMD_LOAD_MULTICAST_LIST] = 0;
+ if (!send_pcb(dev, &adapter->tx_pcb))
+ pr_err("%s: couldn't send set_multicast command\n", dev->name);
+ else {
+ unsigned long timeout = jiffies + TIMEOUT;
+ while (adapter->got[CMD_LOAD_MULTICAST_LIST] == 0 && time_before(jiffies, timeout));
+ if (time_after_eq(jiffies, timeout)) {
+ TIMEOUT_MSG(__LINE__);
+ }
+ }
+ if (!netdev_mc_empty(dev))
+ adapter->tx_pcb.data.configure = NO_LOOPBACK | RECV_BROAD | RECV_MULTI;
+ else /* num_addrs == 0 */
+ adapter->tx_pcb.data.configure = NO_LOOPBACK | RECV_BROAD;
+ } else
+ adapter->tx_pcb.data.configure = NO_LOOPBACK | RECV_PROMISC;
+ /*
+ * configure adapter to receive messages (as specified above)
+ * and wait for response
+ */
+ if (elp_debug >= 3)
+ pr_debug("%s: sending 82586 configure command\n", dev->name);
+ adapter->tx_pcb.command = CMD_CONFIGURE_82586;
+ adapter->tx_pcb.length = 2;
+ adapter->got[CMD_CONFIGURE_82586] = 0;
+ if (!send_pcb(dev, &adapter->tx_pcb))
+ {
+ spin_unlock_irqrestore(&adapter->lock, flags);
+ pr_err("%s: couldn't send 82586 configure command\n", dev->name);
+ }
+ else {
+ unsigned long timeout = jiffies + TIMEOUT;
+ spin_unlock_irqrestore(&adapter->lock, flags);
+ while (adapter->got[CMD_CONFIGURE_82586] == 0 && time_before(jiffies, timeout));
+ if (time_after_eq(jiffies, timeout))
+ TIMEOUT_MSG(__LINE__);
+ }
+}
+
+/************************************************************
+ *
+ * A couple of tests to see if there's 3C505 or not
+ * Called only by elp_autodetect
+ ************************************************************/
+
+static int __init elp_sense(struct net_device *dev)
+{
+ int addr = dev->base_addr;
+ const char *name = dev->name;
+ byte orig_HSR;
+
+ if (!request_region(addr, ELP_IO_EXTENT, "3c505"))
+ return -ENODEV;
+
+ orig_HSR = inb_status(addr);
+
+ if (elp_debug > 0)
+ pr_debug(search_msg, name, addr);
+
+ if (orig_HSR == 0xff) {
+ if (elp_debug > 0)
+ pr_cont(notfound_msg, 1);
+ goto out;
+ }
+
+ /* Wait for a while; the adapter may still be booting up */
+ if (elp_debug > 0)
+ pr_cont(stilllooking_msg);
+
+ if (orig_HSR & DIR) {
+ /* If HCR.DIR is up, we pull it down. HSR.DIR should follow. */
+ outb(0, dev->base_addr + PORT_CONTROL);
+ msleep(300);
+ if (inb_status(addr) & DIR) {
+ if (elp_debug > 0)
+ pr_cont(notfound_msg, 2);
+ goto out;
+ }
+ } else {
+ /* If HCR.DIR is down, we pull it up. HSR.DIR should follow. */
+ outb(DIR, dev->base_addr + PORT_CONTROL);
+ msleep(300);
+ if (!(inb_status(addr) & DIR)) {
+ if (elp_debug > 0)
+ pr_cont(notfound_msg, 3);
+ goto out;
+ }
+ }
+ /*
+ * It certainly looks like a 3c505.
+ */
+ if (elp_debug > 0)
+ pr_cont(found_msg);
+
+ return 0;
+out:
+ release_region(addr, ELP_IO_EXTENT);
+ return -ENODEV;
+}
+
+/*************************************************************
+ *
+ * Search through addr_list[] and try to find a 3C505
+ * Called only by eplus_probe
+ *************************************************************/
+
+static int __init elp_autodetect(struct net_device *dev)
+{
+ int idx = 0;
+
+ /* if base address set, then only check that address
+ otherwise, run through the table */
+ if (dev->base_addr != 0) { /* dev->base_addr == 0 ==> plain autodetect */
+ if (elp_sense(dev) == 0)
+ return dev->base_addr;
+ } else
+ while ((dev->base_addr = addr_list[idx++])) {
+ if (elp_sense(dev) == 0)
+ return dev->base_addr;
+ }
+
+ /* could not find an adapter */
+ if (elp_debug > 0)
+ pr_debug(couldnot_msg, dev->name);
+
+ return 0; /* Because of this, the layer above will return -ENODEV */
+}
+
+static const struct net_device_ops elp_netdev_ops = {
+ .ndo_open = elp_open,
+ .ndo_stop = elp_close,
+ .ndo_get_stats = elp_get_stats,
+ .ndo_start_xmit = elp_start_xmit,
+ .ndo_tx_timeout = elp_timeout,
+ .ndo_set_rx_mode = elp_set_mc_list,
+ .ndo_change_mtu = eth_change_mtu,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_validate_addr = eth_validate_addr,
+};
+
+/******************************************************
+ *
+ * probe for an Etherlink Plus board at the specified address
+ *
+ ******************************************************/
+
+/* There are three situations we need to be able to detect here:
+
+ * a) the card is idle
+ * b) the card is still booting up
+ * c) the card is stuck in a strange state (some DOS drivers do this)
+ *
+ * In case (a), all is well. In case (b), we wait 10 seconds to see if the
+ * card finishes booting, and carry on if so. In case (c), we do a hard reset,
+ * loop round, and hope for the best.
+ *
+ * This is all very unpleasant, but hopefully avoids the problems with the old
+ * probe code (which had a 15-second delay if the card was idle, and didn't
+ * work at all if it was in a weird state).
+ */
+
+static int __init elplus_setup(struct net_device *dev)
+{
+ elp_device *adapter = netdev_priv(dev);
+ int i, tries, tries1, okay;
+ unsigned long timeout;
+ unsigned long cookie = 0;
+ int err = -ENODEV;
+
+ /*
+ * setup adapter structure
+ */
+
+ dev->base_addr = elp_autodetect(dev);
+ if (!dev->base_addr)
+ return -ENODEV;
+
+ adapter->send_pcb_semaphore = 0;
+
+ for (tries1 = 0; tries1 < 3; tries1++) {
+ outb_control((adapter->hcr_val | CMDE) & ~DIR, dev);
+ /* First try to write just one byte, to see if the card is
+ * responding at all normally.
+ */
+ timeout = jiffies + 5*HZ/100;
+ okay = 0;
+ while (time_before(jiffies, timeout) && !(inb_status(dev->base_addr) & HCRE));
+ if ((inb_status(dev->base_addr) & HCRE)) {
+ outb_command(0, dev->base_addr); /* send a spurious byte */
+ timeout = jiffies + 5*HZ/100;
+ while (time_before(jiffies, timeout) && !(inb_status(dev->base_addr) & HCRE));
+ if (inb_status(dev->base_addr) & HCRE)
+ okay = 1;
+ }
+ if (!okay) {
+ /* Nope, it's ignoring the command register. This means that
+ * either it's still booting up, or it's died.
+ */
+ pr_err("%s: command register wouldn't drain, ", dev->name);
+ if ((inb_status(dev->base_addr) & 7) == 3) {
+ /* If the adapter status is 3, it *could* still be booting.
+ * Give it the benefit of the doubt for 10 seconds.
+ */
+ pr_cont("assuming 3c505 still starting\n");
+ timeout = jiffies + 10*HZ;
+ while (time_before(jiffies, timeout) && (inb_status(dev->base_addr) & 7));
+ if (inb_status(dev->base_addr) & 7) {
+ pr_err("%s: 3c505 failed to start\n", dev->name);
+ } else {
+ okay = 1; /* It started */
+ }
+ } else {
+ /* Otherwise, it must just be in a strange
+ * state. We probably need to kick it.
+ */
+ pr_cont("3c505 is sulking\n");
+ }
+ }
+ for (tries = 0; tries < 5 && okay; tries++) {
+
+ /*
+ * Try to set the Ethernet address, to make sure that the board
+ * is working.
+ */
+ adapter->tx_pcb.command = CMD_STATION_ADDRESS;
+ adapter->tx_pcb.length = 0;
+ cookie = probe_irq_on();
+ if (!send_pcb(dev, &adapter->tx_pcb)) {
+ pr_err("%s: could not send first PCB\n", dev->name);
+ probe_irq_off(cookie);
+ continue;
+ }
+ if (!receive_pcb(dev, &adapter->rx_pcb)) {
+ pr_err("%s: could not read first PCB\n", dev->name);
+ probe_irq_off(cookie);
+ continue;
+ }
+ if ((adapter->rx_pcb.command != CMD_ADDRESS_RESPONSE) ||
+ (adapter->rx_pcb.length != 6)) {
+ pr_err("%s: first PCB wrong (%d, %d)\n", dev->name,
+ adapter->rx_pcb.command, adapter->rx_pcb.length);
+ probe_irq_off(cookie);
+ continue;
+ }
+ goto okay;
+ }
+ /* It's broken. Do a hard reset to re-initialise the board,
+ * and try again.
+ */
+ pr_info("%s: resetting adapter\n", dev->name);
+ outb_control(adapter->hcr_val | FLSH | ATTN, dev);
+ outb_control(adapter->hcr_val & ~(FLSH | ATTN), dev);
+ }
+ pr_err("%s: failed to initialise 3c505\n", dev->name);
+ goto out;
+
+ okay:
+ if (dev->irq) { /* Is there a preset IRQ? */
+ int rpt = probe_irq_off(cookie);
+ if (dev->irq != rpt) {
+ pr_warning("%s: warning, irq %d configured but %d detected\n", dev->name, dev->irq, rpt);
+ }
+ /* if dev->irq == probe_irq_off(cookie), all is well */
+ } else /* No preset IRQ; just use what we can detect */
+ dev->irq = probe_irq_off(cookie);
+ switch (dev->irq) { /* Legal, sane? */
+ case 0:
+ pr_err("%s: IRQ probe failed: check 3c505 jumpers.\n",
+ dev->name);
+ goto out;
+ case 1:
+ case 6:
+ case 8:
+ case 13:
+ pr_err("%s: Impossible IRQ %d reported by probe_irq_off().\n",
+ dev->name, dev->irq);
+ goto out;
+ }
+ /*
+ * Now we have the IRQ number so we can disable the interrupts from
+ * the board until the board is opened.
+ */
+ outb_control(adapter->hcr_val & ~CMDE, dev);
+
+ /*
+ * copy Ethernet address into structure
+ */
+ for (i = 0; i < 6; i++)
+ dev->dev_addr[i] = adapter->rx_pcb.data.eth_addr[i];
+
+ /* find a DMA channel */
+ if (!dev->dma) {
+ if (dev->mem_start) {
+ dev->dma = dev->mem_start & 7;
+ }
+ else {
+ pr_warning("%s: warning, DMA channel not specified, using default\n", dev->name);
+ dev->dma = ELP_DMA;
+ }
+ }
+
+ /*
+ * print remainder of startup message
+ */
+ pr_info("%s: 3c505 at %#lx, irq %d, dma %d, addr %pM, ",
+ dev->name, dev->base_addr, dev->irq, dev->dma, dev->dev_addr);
+ /*
+ * read more information from the adapter
+ */
+
+ adapter->tx_pcb.command = CMD_ADAPTER_INFO;
+ adapter->tx_pcb.length = 0;
+ if (!send_pcb(dev, &adapter->tx_pcb) ||
+ !receive_pcb(dev, &adapter->rx_pcb) ||
+ (adapter->rx_pcb.command != CMD_ADAPTER_INFO_RESPONSE) ||
+ (adapter->rx_pcb.length != 10)) {
+ pr_cont("not responding to second PCB\n");
+ }
+ pr_cont("rev %d.%d, %dk\n", adapter->rx_pcb.data.info.major_vers,
+ adapter->rx_pcb.data.info.minor_vers, adapter->rx_pcb.data.info.RAM_sz);
+
+ /*
+ * reconfigure the adapter memory to better suit our purposes
+ */
+ adapter->tx_pcb.command = CMD_CONFIGURE_ADAPTER_MEMORY;
+ adapter->tx_pcb.length = 12;
+ adapter->tx_pcb.data.memconf.cmd_q = 8;
+ adapter->tx_pcb.data.memconf.rcv_q = 8;
+ adapter->tx_pcb.data.memconf.mcast = 10;
+ adapter->tx_pcb.data.memconf.frame = 10;
+ adapter->tx_pcb.data.memconf.rcv_b = 10;
+ adapter->tx_pcb.data.memconf.progs = 0;
+ if (!send_pcb(dev, &adapter->tx_pcb) ||
+ !receive_pcb(dev, &adapter->rx_pcb) ||
+ (adapter->rx_pcb.command != CMD_CONFIGURE_ADAPTER_RESPONSE) ||
+ (adapter->rx_pcb.length != 2)) {
+ pr_err("%s: could not configure adapter memory\n", dev->name);
+ }
+ if (adapter->rx_pcb.data.configure) {
+ pr_err("%s: adapter configuration failed\n", dev->name);
+ }
+
+ dev->netdev_ops = &elp_netdev_ops;
+ dev->watchdog_timeo = 10*HZ;
+ dev->ethtool_ops = &netdev_ethtool_ops; /* local */
+
+ dev->mem_start = dev->mem_end = 0;
+
+ err = register_netdev(dev);
+ if (err)
+ goto out;
+
+ return 0;
+out:
+ release_region(dev->base_addr, ELP_IO_EXTENT);
+ return err;
+}
+
+#ifndef MODULE
+struct net_device * __init elplus_probe(int unit)
+{
+ struct net_device *dev = alloc_etherdev(sizeof(elp_device));
+ int err;
+ if (!dev)
+ return ERR_PTR(-ENOMEM);
+
+ sprintf(dev->name, "eth%d", unit);
+ netdev_boot_setup_check(dev);
+
+ err = elplus_setup(dev);
+ if (err) {
+ free_netdev(dev);
+ return ERR_PTR(err);
+ }
+ return dev;
+}
+
+#else
+static struct net_device *dev_3c505[ELP_MAX_CARDS];
+static int io[ELP_MAX_CARDS];
+static int irq[ELP_MAX_CARDS];
+static int dma[ELP_MAX_CARDS];
+module_param_array(io, int, NULL, 0);
+module_param_array(irq, int, NULL, 0);
+module_param_array(dma, int, NULL, 0);
+MODULE_PARM_DESC(io, "EtherLink Plus I/O base address(es)");
+MODULE_PARM_DESC(irq, "EtherLink Plus IRQ number(s) (assigned)");
+MODULE_PARM_DESC(dma, "EtherLink Plus DMA channel(s)");
+
+int __init init_module(void)
+{
+ int this_dev, found = 0;
+
+ for (this_dev = 0; this_dev < ELP_MAX_CARDS; this_dev++) {
+ struct net_device *dev = alloc_etherdev(sizeof(elp_device));
+ if (!dev)
+ break;
+
+ dev->irq = irq[this_dev];
+ dev->base_addr = io[this_dev];
+ if (dma[this_dev]) {
+ dev->dma = dma[this_dev];
+ } else {
+ dev->dma = ELP_DMA;
+ pr_warning("3c505.c: warning, using default DMA channel,\n");
+ }
+ if (io[this_dev] == 0) {
+ if (this_dev) {
+ free_netdev(dev);
+ break;
+ }
+ pr_notice("3c505.c: module autoprobe not recommended, give io=xx.\n");
+ }
+ if (elplus_setup(dev) != 0) {
+ pr_warning("3c505.c: Failed to register card at 0x%x.\n", io[this_dev]);
+ free_netdev(dev);
+ break;
+ }
+ dev_3c505[this_dev] = dev;
+ found++;
+ }
+ if (!found)
+ return -ENODEV;
+ return 0;
+}
+
+void __exit cleanup_module(void)
+{
+ int this_dev;
+
+ for (this_dev = 0; this_dev < ELP_MAX_CARDS; this_dev++) {
+ struct net_device *dev = dev_3c505[this_dev];
+ if (dev) {
+ unregister_netdev(dev);
+ release_region(dev->base_addr, ELP_IO_EXTENT);
+ free_netdev(dev);
+ }
+ }
+}
+
+#endif /* MODULE */
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/ethernet/i825xx/3c505.h b/drivers/net/ethernet/i825xx/3c505.h
new file mode 100644
index 00000000000..04df2a9002b
--- /dev/null
+++ b/drivers/net/ethernet/i825xx/3c505.h
@@ -0,0 +1,292 @@
+/*****************************************************************
+ *
+ * defines for 3Com Etherlink Plus adapter
+ *
+ *****************************************************************/
+
+#define ELP_DMA 6
+#define ELP_RX_PCBS 4
+#define ELP_MAX_CARDS 4
+
+/*
+ * I/O register offsets
+ */
+#define PORT_COMMAND 0x00 /* read/write, 8-bit */
+#define PORT_STATUS 0x02 /* read only, 8-bit */
+#define PORT_AUXDMA 0x02 /* write only, 8-bit */
+#define PORT_DATA 0x04 /* read/write, 16-bit */
+#define PORT_CONTROL 0x06 /* read/write, 8-bit */
+
+#define ELP_IO_EXTENT 0x10 /* size of used IO registers */
+
+/*
+ * host control registers bits
+ */
+#define ATTN 0x80 /* attention */
+#define FLSH 0x40 /* flush data register */
+#define DMAE 0x20 /* DMA enable */
+#define DIR 0x10 /* direction */
+#define TCEN 0x08 /* terminal count interrupt enable */
+#define CMDE 0x04 /* command register interrupt enable */
+#define HSF2 0x02 /* host status flag 2 */
+#define HSF1 0x01 /* host status flag 1 */
+
+/*
+ * combinations of HSF flags used for PCB transmission
+ */
+#define HSF_PCB_ACK HSF1
+#define HSF_PCB_NAK HSF2
+#define HSF_PCB_END (HSF2|HSF1)
+#define HSF_PCB_MASK (HSF2|HSF1)
+
+/*
+ * host status register bits
+ */
+#define HRDY 0x80 /* data register ready */
+#define HCRE 0x40 /* command register empty */
+#define ACRF 0x20 /* adapter command register full */
+/* #define DIR 0x10 direction - same as in control register */
+#define DONE 0x08 /* DMA done */
+#define ASF3 0x04 /* adapter status flag 3 */
+#define ASF2 0x02 /* adapter status flag 2 */
+#define ASF1 0x01 /* adapter status flag 1 */
+
+/*
+ * combinations of ASF flags used for PCB reception
+ */
+#define ASF_PCB_ACK ASF1
+#define ASF_PCB_NAK ASF2
+#define ASF_PCB_END (ASF2|ASF1)
+#define ASF_PCB_MASK (ASF2|ASF1)
+
+/*
+ * host aux DMA register bits
+ */
+#define DMA_BRST 0x01 /* DMA burst */
+
+/*
+ * maximum amount of data allowed in a PCB
+ */
+#define MAX_PCB_DATA 62
+
+/*****************************************************************
+ *
+ * timeout value
+ * this is a rough value used for loops to stop them from
+ * locking up the whole machine in the case of failure or
+ * error conditions
+ *
+ *****************************************************************/
+
+#define TIMEOUT 300
+
+/*****************************************************************
+ *
+ * PCB commands
+ *
+ *****************************************************************/
+
+enum {
+ /*
+ * host PCB commands
+ */
+ CMD_CONFIGURE_ADAPTER_MEMORY = 0x01,
+ CMD_CONFIGURE_82586 = 0x02,
+ CMD_STATION_ADDRESS = 0x03,
+ CMD_DMA_DOWNLOAD = 0x04,
+ CMD_DMA_UPLOAD = 0x05,
+ CMD_PIO_DOWNLOAD = 0x06,
+ CMD_PIO_UPLOAD = 0x07,
+ CMD_RECEIVE_PACKET = 0x08,
+ CMD_TRANSMIT_PACKET = 0x09,
+ CMD_NETWORK_STATISTICS = 0x0a,
+ CMD_LOAD_MULTICAST_LIST = 0x0b,
+ CMD_CLEAR_PROGRAM = 0x0c,
+ CMD_DOWNLOAD_PROGRAM = 0x0d,
+ CMD_EXECUTE_PROGRAM = 0x0e,
+ CMD_SELF_TEST = 0x0f,
+ CMD_SET_STATION_ADDRESS = 0x10,
+ CMD_ADAPTER_INFO = 0x11,
+ NUM_TRANSMIT_CMDS,
+
+ /*
+ * adapter PCB commands
+ */
+ CMD_CONFIGURE_ADAPTER_RESPONSE = 0x31,
+ CMD_CONFIGURE_82586_RESPONSE = 0x32,
+ CMD_ADDRESS_RESPONSE = 0x33,
+ CMD_DOWNLOAD_DATA_REQUEST = 0x34,
+ CMD_UPLOAD_DATA_REQUEST = 0x35,
+ CMD_RECEIVE_PACKET_COMPLETE = 0x38,
+ CMD_TRANSMIT_PACKET_COMPLETE = 0x39,
+ CMD_NETWORK_STATISTICS_RESPONSE = 0x3a,
+ CMD_LOAD_MULTICAST_RESPONSE = 0x3b,
+ CMD_CLEAR_PROGRAM_RESPONSE = 0x3c,
+ CMD_DOWNLOAD_PROGRAM_RESPONSE = 0x3d,
+ CMD_EXECUTE_RESPONSE = 0x3e,
+ CMD_SELF_TEST_RESPONSE = 0x3f,
+ CMD_SET_ADDRESS_RESPONSE = 0x40,
+ CMD_ADAPTER_INFO_RESPONSE = 0x41
+};
+
+/* Definitions for the PCB data structure */
+
+/* Data units */
+typedef unsigned char byte;
+typedef unsigned short int word;
+typedef unsigned long int dword;
+
+/* Data structures */
+struct Memconf {
+ word cmd_q,
+ rcv_q,
+ mcast,
+ frame,
+ rcv_b,
+ progs;
+};
+
+struct Rcv_pkt {
+ word buf_ofs,
+ buf_seg,
+ buf_len,
+ timeout;
+};
+
+struct Xmit_pkt {
+ word buf_ofs,
+ buf_seg,
+ pkt_len;
+};
+
+struct Rcv_resp {
+ word buf_ofs,
+ buf_seg,
+ buf_len,
+ pkt_len,
+ timeout,
+ status;
+ dword timetag;
+};
+
+struct Xmit_resp {
+ word buf_ofs,
+ buf_seg,
+ c_stat,
+ status;
+};
+
+
+struct Netstat {
+ dword tot_recv,
+ tot_xmit;
+ word err_CRC,
+ err_align,
+ err_res,
+ err_ovrrun;
+};
+
+
+struct Selftest {
+ word error;
+ union {
+ word ROM_cksum;
+ struct {
+ word ofs, seg;
+ } RAM;
+ word i82586;
+ } failure;
+};
+
+struct Info {
+ byte minor_vers,
+ major_vers;
+ word ROM_cksum,
+ RAM_sz,
+ free_ofs,
+ free_seg;
+};
+
+struct Memdump {
+ word size,
+ off,
+ seg;
+};
+
+/*
+Primary Command Block. The most important data structure. All communication
+between the host and the adapter is done with these. (Except for the actual
+Ethernet data, which has different packaging.)
+*/
+typedef struct {
+ byte command;
+ byte length;
+ union {
+ struct Memconf memconf;
+ word configure;
+ struct Rcv_pkt rcv_pkt;
+ struct Xmit_pkt xmit_pkt;
+ byte multicast[10][6];
+ byte eth_addr[6];
+ byte failed;
+ struct Rcv_resp rcv_resp;
+ struct Xmit_resp xmit_resp;
+ struct Netstat netstat;
+ struct Selftest selftest;
+ struct Info info;
+ struct Memdump memdump;
+ byte raw[62];
+ } data;
+} pcb_struct;
+
+/* These defines for 'configure' */
+#define RECV_STATION 0x00
+#define RECV_BROAD 0x01
+#define RECV_MULTI 0x02
+#define RECV_PROMISC 0x04
+#define NO_LOOPBACK 0x00
+#define INT_LOOPBACK 0x08
+#define EXT_LOOPBACK 0x10
+
+/*****************************************************************
+ *
+ * structure to hold context information for adapter
+ *
+ *****************************************************************/
+
+#define DMA_BUFFER_SIZE 1600
+#define BACKLOG_SIZE 4
+
+typedef struct {
+ volatile short got[NUM_TRANSMIT_CMDS]; /* flags for
+ command completion */
+ pcb_struct tx_pcb; /* PCB for foreground sending */
+ pcb_struct rx_pcb; /* PCB for foreground receiving */
+ pcb_struct itx_pcb; /* PCB for background sending */
+ pcb_struct irx_pcb; /* PCB for background receiving */
+
+ void *dma_buffer;
+
+ struct {
+ unsigned int length[BACKLOG_SIZE];
+ unsigned int in;
+ unsigned int out;
+ } rx_backlog;
+
+ struct {
+ unsigned int direction;
+ unsigned int length;
+ struct sk_buff *skb;
+ void *target;
+ unsigned long start_time;
+ } current_dma;
+
+ /* flags */
+ unsigned long send_pcb_semaphore;
+ unsigned long dmaing;
+ unsigned long busy;
+
+ unsigned int rx_active; /* number of receive PCBs */
+ volatile unsigned char hcr_val; /* what we think the HCR contains */
+ spinlock_t lock; /* Interrupt v tx lock */
+} elp_device;
diff --git a/drivers/net/ethernet/i825xx/3c507.c b/drivers/net/ethernet/i825xx/3c507.c
new file mode 100644
index 00000000000..1e945551c14
--- /dev/null
+++ b/drivers/net/ethernet/i825xx/3c507.c
@@ -0,0 +1,939 @@
+/* 3c507.c: An EtherLink16 device driver for Linux. */
+/*
+ Written 1993,1994 by Donald Becker.
+
+ Copyright 1993 United States Government as represented by the
+ Director, National Security Agency.
+
+ This software may be used and distributed according to the terms
+ of the GNU General Public License, incorporated herein by reference.
+
+ The author may be reached as becker@scyld.com, or C/O
+ Scyld Computing Corporation
+ 410 Severn Ave., Suite 210
+ Annapolis MD 21403
+
+
+ Thanks go to jennings@Montrouge.SMR.slb.com ( Patrick Jennings)
+ and jrs@world.std.com (Rick Sladkey) for testing and bugfixes.
+ Mark Salazar <leslie@access.digex.net> made the changes for cards with
+ only 16K packet buffers.
+
+ Things remaining to do:
+ Verify that the tx and rx buffers don't have fencepost errors.
+ Move the theory of operation and memory map documentation.
+ The statistics need to be updated correctly.
+*/
+
+#define DRV_NAME "3c507"
+#define DRV_VERSION "1.10a"
+#define DRV_RELDATE "11/17/2001"
+
+static const char version[] =
+ DRV_NAME ".c:v" DRV_VERSION " " DRV_RELDATE " Donald Becker (becker@scyld.com)\n";
+
+/*
+ Sources:
+ This driver wouldn't have been written with the availability of the
+ Crynwr driver source code. It provided a known-working implementation
+ that filled in the gaping holes of the Intel documentation. Three cheers
+ for Russ Nelson.
+
+ Intel Microcommunications Databook, Vol. 1, 1990. It provides just enough
+ info that the casual reader might think that it documents the i82586 :-<.
+*/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/string.h>
+#include <linux/spinlock.h>
+#include <linux/ethtool.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/if_ether.h>
+#include <linux/skbuff.h>
+#include <linux/init.h>
+#include <linux/bitops.h>
+
+#include <asm/dma.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+
+/* use 0 for production, 1 for verification, 2..7 for debug */
+#ifndef NET_DEBUG
+#define NET_DEBUG 1
+#endif
+static unsigned int net_debug = NET_DEBUG;
+#define debug net_debug
+
+
+/*
+ Details of the i82586.
+
+ You'll really need the databook to understand the details of this part,
+ but the outline is that the i82586 has two separate processing units.
+ Both are started from a list of three configuration tables, of which only
+ the last, the System Control Block (SCB), is used after reset-time. The SCB
+ has the following fields:
+ Status word
+ Command word
+ Tx/Command block addr.
+ Rx block addr.
+ The command word accepts the following controls for the Tx and Rx units:
+ */
+
+#define CUC_START 0x0100
+#define CUC_RESUME 0x0200
+#define CUC_SUSPEND 0x0300
+#define RX_START 0x0010
+#define RX_RESUME 0x0020
+#define RX_SUSPEND 0x0030
+
+/* The Rx unit uses a list of frame descriptors and a list of data buffer
+ descriptors. We use full-sized (1518 byte) data buffers, so there is
+ a one-to-one pairing of frame descriptors to buffer descriptors.
+
+ The Tx ("command") unit executes a list of commands that look like:
+ Status word Written by the 82586 when the command is done.
+ Command word Command in lower 3 bits, post-command action in upper 3
+ Link word The address of the next command.
+ Parameters (as needed).
+
+ Some definitions related to the Command Word are:
+ */
+#define CMD_EOL 0x8000 /* The last command of the list, stop. */
+#define CMD_SUSP 0x4000 /* Suspend after doing cmd. */
+#define CMD_INTR 0x2000 /* Interrupt after doing cmd. */
+
+enum commands {
+ CmdNOp = 0, CmdSASetup = 1, CmdConfigure = 2, CmdMulticastList = 3,
+ CmdTx = 4, CmdTDR = 5, CmdDump = 6, CmdDiagnose = 7};
+
+/* Information that need to be kept for each board. */
+struct net_local {
+ int last_restart;
+ ushort rx_head;
+ ushort rx_tail;
+ ushort tx_head;
+ ushort tx_cmd_link;
+ ushort tx_reap;
+ ushort tx_pkts_in_ring;
+ spinlock_t lock;
+ void __iomem *base;
+};
+
+/*
+ Details of the EtherLink16 Implementation
+ The 3c507 is a generic shared-memory i82586 implementation.
+ The host can map 16K, 32K, 48K, or 64K of the 64K memory into
+ 0x0[CD][08]0000, or all 64K into 0xF[02468]0000.
+ */
+
+/* Offsets from the base I/O address. */
+#define SA_DATA 0 /* Station address data, or 3Com signature. */
+#define MISC_CTRL 6 /* Switch the SA_DATA banks, and bus config bits. */
+#define RESET_IRQ 10 /* Reset the latched IRQ line. */
+#define SIGNAL_CA 11 /* Frob the 82586 Channel Attention line. */
+#define ROM_CONFIG 13
+#define MEM_CONFIG 14
+#define IRQ_CONFIG 15
+#define EL16_IO_EXTENT 16
+
+/* The ID port is used at boot-time to locate the ethercard. */
+#define ID_PORT 0x100
+
+/* Offsets to registers in the mailbox (SCB). */
+#define iSCB_STATUS 0x8
+#define iSCB_CMD 0xA
+#define iSCB_CBL 0xC /* Command BLock offset. */
+#define iSCB_RFA 0xE /* Rx Frame Area offset. */
+
+/* Since the 3c507 maps the shared memory window so that the last byte is
+ at 82586 address FFFF, the first byte is at 82586 address 0, 16K, 32K, or
+ 48K corresponding to window sizes of 64K, 48K, 32K and 16K respectively.
+ We can account for this be setting the 'SBC Base' entry in the ISCP table
+ below for all the 16 bit offset addresses, and also adding the 'SCB Base'
+ value to all 24 bit physical addresses (in the SCP table and the TX and RX
+ Buffer Descriptors).
+ -Mark
+ */
+#define SCB_BASE ((unsigned)64*1024 - (dev->mem_end - dev->mem_start))
+
+/*
+ What follows in 'init_words[]' is the "program" that is downloaded to the
+ 82586 memory. It's mostly tables and command blocks, and starts at the
+ reset address 0xfffff6. This is designed to be similar to the EtherExpress,
+ thus the unusual location of the SCB at 0x0008.
+
+ Even with the additional "don't care" values, doing it this way takes less
+ program space than initializing the individual tables, and I feel it's much
+ cleaner.
+
+ The databook is particularly useless for the first two structures, I had
+ to use the Crynwr driver as an example.
+
+ The memory setup is as follows:
+ */
+
+#define CONFIG_CMD 0x0018
+#define SET_SA_CMD 0x0024
+#define SA_OFFSET 0x002A
+#define IDLELOOP 0x30
+#define TDR_CMD 0x38
+#define TDR_TIME 0x3C
+#define DUMP_CMD 0x40
+#define DIAG_CMD 0x48
+#define SET_MC_CMD 0x4E
+#define DUMP_DATA 0x56 /* A 170 byte buffer for dump and Set-MC into. */
+
+#define TX_BUF_START 0x0100
+#define NUM_TX_BUFS 5
+#define TX_BUF_SIZE (1518+14+20+16) /* packet+header+TBD */
+
+#define RX_BUF_START 0x2000
+#define RX_BUF_SIZE (1518+14+18) /* packet+header+RBD */
+#define RX_BUF_END (dev->mem_end - dev->mem_start)
+
+#define TX_TIMEOUT (HZ/20)
+
+/*
+ That's it: only 86 bytes to set up the beast, including every extra
+ command available. The 170 byte buffer at DUMP_DATA is shared between the
+ Dump command (called only by the diagnostic program) and the SetMulticastList
+ command.
+
+ To complete the memory setup you only have to write the station address at
+ SA_OFFSET and create the Tx & Rx buffer lists.
+
+ The Tx command chain and buffer list is setup as follows:
+ A Tx command table, with the data buffer pointing to...
+ A Tx data buffer descriptor. The packet is in a single buffer, rather than
+ chaining together several smaller buffers.
+ A NoOp command, which initially points to itself,
+ And the packet data.
+
+ A transmit is done by filling in the Tx command table and data buffer,
+ re-writing the NoOp command, and finally changing the offset of the last
+ command to point to the current Tx command. When the Tx command is finished,
+ it jumps to the NoOp, when it loops until the next Tx command changes the
+ "link offset" in the NoOp. This way the 82586 never has to go through the
+ slow restart sequence.
+
+ The Rx buffer list is set up in the obvious ring structure. We have enough
+ memory (and low enough interrupt latency) that we can avoid the complicated
+ Rx buffer linked lists by alway associating a full-size Rx data buffer with
+ each Rx data frame.
+
+ I current use four transmit buffers starting at TX_BUF_START (0x0100), and
+ use the rest of memory, from RX_BUF_START to RX_BUF_END, for Rx buffers.
+
+ */
+
+static unsigned short init_words[] = {
+ /* System Configuration Pointer (SCP). */
+ 0x0000, /* Set bus size to 16 bits. */
+ 0,0, /* pad words. */
+ 0x0000,0x0000, /* ISCP phys addr, set in init_82586_mem(). */
+
+ /* Intermediate System Configuration Pointer (ISCP). */
+ 0x0001, /* Status word that's cleared when init is done. */
+ 0x0008,0,0, /* SCB offset, (skip, skip) */
+
+ /* System Control Block (SCB). */
+ 0,0xf000|RX_START|CUC_START, /* SCB status and cmd. */
+ CONFIG_CMD, /* Command list pointer, points to Configure. */
+ RX_BUF_START, /* Rx block list. */
+ 0,0,0,0, /* Error count: CRC, align, buffer, overrun. */
+
+ /* 0x0018: Configure command. Change to put MAC data with packet. */
+ 0, CmdConfigure, /* Status, command. */
+ SET_SA_CMD, /* Next command is Set Station Addr. */
+ 0x0804, /* "4" bytes of config data, 8 byte FIFO. */
+ 0x2e40, /* Magic values, including MAC data location. */
+ 0, /* Unused pad word. */
+
+ /* 0x0024: Setup station address command. */
+ 0, CmdSASetup,
+ SET_MC_CMD, /* Next command. */
+ 0xaa00,0xb000,0x0bad, /* Station address (to be filled in) */
+
+ /* 0x0030: NOP, looping back to itself. Point to first Tx buffer to Tx. */
+ 0, CmdNOp, IDLELOOP, 0 /* pad */,
+
+ /* 0x0038: A unused Time-Domain Reflectometer command. */
+ 0, CmdTDR, IDLELOOP, 0,
+
+ /* 0x0040: An unused Dump State command. */
+ 0, CmdDump, IDLELOOP, DUMP_DATA,
+
+ /* 0x0048: An unused Diagnose command. */
+ 0, CmdDiagnose, IDLELOOP,
+
+ /* 0x004E: An empty set-multicast-list command. */
+ 0, CmdMulticastList, IDLELOOP, 0,
+};
+
+/* Index to functions, as function prototypes. */
+
+static int el16_probe1(struct net_device *dev, int ioaddr);
+static int el16_open(struct net_device *dev);
+static netdev_tx_t el16_send_packet(struct sk_buff *skb,
+ struct net_device *dev);
+static irqreturn_t el16_interrupt(int irq, void *dev_id);
+static void el16_rx(struct net_device *dev);
+static int el16_close(struct net_device *dev);
+static void el16_tx_timeout (struct net_device *dev);
+
+static void hardware_send_packet(struct net_device *dev, void *buf, short length, short pad);
+static void init_82586_mem(struct net_device *dev);
+static const struct ethtool_ops netdev_ethtool_ops;
+static void init_rx_bufs(struct net_device *);
+
+static int io = 0x300;
+static int irq;
+static int mem_start;
+
+
+/* Check for a network adaptor of this type, and return '0' iff one exists.
+ If dev->base_addr == 0, probe all likely locations.
+ If dev->base_addr == 1, always return failure.
+ If dev->base_addr == 2, (detachable devices only) allocate space for the
+ device and return success.
+ */
+
+struct net_device * __init el16_probe(int unit)
+{
+ struct net_device *dev = alloc_etherdev(sizeof(struct net_local));
+ static const unsigned ports[] = { 0x300, 0x320, 0x340, 0x280, 0};
+ const unsigned *port;
+ int err = -ENODEV;
+
+ if (!dev)
+ return ERR_PTR(-ENODEV);
+
+ if (unit >= 0) {
+ sprintf(dev->name, "eth%d", unit);
+ netdev_boot_setup_check(dev);
+ io = dev->base_addr;
+ irq = dev->irq;
+ mem_start = dev->mem_start & 15;
+ }
+
+ if (io > 0x1ff) /* Check a single specified location. */
+ err = el16_probe1(dev, io);
+ else if (io != 0)
+ err = -ENXIO; /* Don't probe at all. */
+ else {
+ for (port = ports; *port; port++) {
+ err = el16_probe1(dev, *port);
+ if (!err)
+ break;
+ }
+ }
+
+ if (err)
+ goto out;
+ err = register_netdev(dev);
+ if (err)
+ goto out1;
+ return dev;
+out1:
+ free_irq(dev->irq, dev);
+ iounmap(((struct net_local *)netdev_priv(dev))->base);
+ release_region(dev->base_addr, EL16_IO_EXTENT);
+out:
+ free_netdev(dev);
+ return ERR_PTR(err);
+}
+
+static const struct net_device_ops netdev_ops = {
+ .ndo_open = el16_open,
+ .ndo_stop = el16_close,
+ .ndo_start_xmit = el16_send_packet,
+ .ndo_tx_timeout = el16_tx_timeout,
+ .ndo_change_mtu = eth_change_mtu,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_validate_addr = eth_validate_addr,
+};
+
+static int __init el16_probe1(struct net_device *dev, int ioaddr)
+{
+ static unsigned char init_ID_done;
+ int i, irq, irqval, retval;
+ struct net_local *lp;
+
+ if (init_ID_done == 0) {
+ ushort lrs_state = 0xff;
+ /* Send the ID sequence to the ID_PORT to enable the board(s). */
+ outb(0x00, ID_PORT);
+ for(i = 0; i < 255; i++) {
+ outb(lrs_state, ID_PORT);
+ lrs_state <<= 1;
+ if (lrs_state & 0x100)
+ lrs_state ^= 0xe7;
+ }
+ outb(0x00, ID_PORT);
+ init_ID_done = 1;
+ }
+
+ if (!request_region(ioaddr, EL16_IO_EXTENT, DRV_NAME))
+ return -ENODEV;
+
+ if ((inb(ioaddr) != '*') || (inb(ioaddr + 1) != '3') ||
+ (inb(ioaddr + 2) != 'C') || (inb(ioaddr + 3) != 'O')) {
+ retval = -ENODEV;
+ goto out;
+ }
+
+ pr_info("%s: 3c507 at %#x,", dev->name, ioaddr);
+
+ /* We should make a few more checks here, like the first three octets of
+ the S.A. for the manufacturer's code. */
+
+ irq = inb(ioaddr + IRQ_CONFIG) & 0x0f;
+
+ irqval = request_irq(irq, el16_interrupt, 0, DRV_NAME, dev);
+ if (irqval) {
+ pr_cont("\n");
+ pr_err("3c507: unable to get IRQ %d (irqval=%d).\n", irq, irqval);
+ retval = -EAGAIN;
+ goto out;
+ }
+
+ /* We've committed to using the board, and can start filling in *dev. */
+ dev->base_addr = ioaddr;
+
+ outb(0x01, ioaddr + MISC_CTRL);
+ for (i = 0; i < 6; i++)
+ dev->dev_addr[i] = inb(ioaddr + i);
+ pr_cont(" %pM", dev->dev_addr);
+
+ if (mem_start)
+ net_debug = mem_start & 7;
+
+#ifdef MEM_BASE
+ dev->mem_start = MEM_BASE;
+ dev->mem_end = dev->mem_start + 0x10000;
+#else
+ {
+ int base;
+ int size;
+ char mem_config = inb(ioaddr + MEM_CONFIG);
+ if (mem_config & 0x20) {
+ size = 64*1024;
+ base = 0xf00000 + (mem_config & 0x08 ? 0x080000
+ : ((mem_config & 3) << 17));
+ } else {
+ size = ((mem_config & 3) + 1) << 14;
+ base = 0x0c0000 + ( (mem_config & 0x18) << 12);
+ }
+ dev->mem_start = base;
+ dev->mem_end = base + size;
+ }
+#endif
+
+ dev->if_port = (inb(ioaddr + ROM_CONFIG) & 0x80) ? 1 : 0;
+ dev->irq = inb(ioaddr + IRQ_CONFIG) & 0x0f;
+
+ pr_cont(", IRQ %d, %sternal xcvr, memory %#lx-%#lx.\n", dev->irq,
+ dev->if_port ? "ex" : "in", dev->mem_start, dev->mem_end-1);
+
+ if (net_debug)
+ pr_debug("%s", version);
+
+ lp = netdev_priv(dev);
+ spin_lock_init(&lp->lock);
+ lp->base = ioremap(dev->mem_start, RX_BUF_END);
+ if (!lp->base) {
+ pr_err("3c507: unable to remap memory\n");
+ retval = -EAGAIN;
+ goto out1;
+ }
+
+ dev->netdev_ops = &netdev_ops;
+ dev->watchdog_timeo = TX_TIMEOUT;
+ dev->ethtool_ops = &netdev_ethtool_ops;
+ dev->flags &= ~IFF_MULTICAST; /* Multicast doesn't work */
+ return 0;
+out1:
+ free_irq(dev->irq, dev);
+out:
+ release_region(ioaddr, EL16_IO_EXTENT);
+ return retval;
+}
+
+static int el16_open(struct net_device *dev)
+{
+ /* Initialize the 82586 memory and start it. */
+ init_82586_mem(dev);
+
+ netif_start_queue(dev);
+ return 0;
+}
+
+
+static void el16_tx_timeout (struct net_device *dev)
+{
+ struct net_local *lp = netdev_priv(dev);
+ int ioaddr = dev->base_addr;
+ void __iomem *shmem = lp->base;
+
+ if (net_debug > 1)
+ pr_debug("%s: transmit timed out, %s? ", dev->name,
+ readw(shmem + iSCB_STATUS) & 0x8000 ? "IRQ conflict" :
+ "network cable problem");
+ /* Try to restart the adaptor. */
+ if (lp->last_restart == dev->stats.tx_packets) {
+ if (net_debug > 1)
+ pr_cont("Resetting board.\n");
+ /* Completely reset the adaptor. */
+ init_82586_mem (dev);
+ lp->tx_pkts_in_ring = 0;
+ } else {
+ /* Issue the channel attention signal and hope it "gets better". */
+ if (net_debug > 1)
+ pr_cont("Kicking board.\n");
+ writew(0xf000 | CUC_START | RX_START, shmem + iSCB_CMD);
+ outb (0, ioaddr + SIGNAL_CA); /* Issue channel-attn. */
+ lp->last_restart = dev->stats.tx_packets;
+ }
+ dev->trans_start = jiffies; /* prevent tx timeout */
+ netif_wake_queue (dev);
+}
+
+
+static netdev_tx_t el16_send_packet (struct sk_buff *skb,
+ struct net_device *dev)
+{
+ struct net_local *lp = netdev_priv(dev);
+ int ioaddr = dev->base_addr;
+ unsigned long flags;
+ short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
+ unsigned char *buf = skb->data;
+
+ netif_stop_queue (dev);
+
+ spin_lock_irqsave (&lp->lock, flags);
+
+ dev->stats.tx_bytes += length;
+ /* Disable the 82586's input to the interrupt line. */
+ outb (0x80, ioaddr + MISC_CTRL);
+
+ hardware_send_packet (dev, buf, skb->len, length - skb->len);
+
+ /* Enable the 82586 interrupt input. */
+ outb (0x84, ioaddr + MISC_CTRL);
+
+ spin_unlock_irqrestore (&lp->lock, flags);
+
+ dev_kfree_skb (skb);
+
+ /* You might need to clean up and record Tx statistics here. */
+
+ return NETDEV_TX_OK;
+}
+
+/* The typical workload of the driver:
+ Handle the network interface interrupts. */
+static irqreturn_t el16_interrupt(int irq, void *dev_id)
+{
+ struct net_device *dev = dev_id;
+ struct net_local *lp;
+ int ioaddr, status, boguscount = 0;
+ ushort ack_cmd = 0;
+ void __iomem *shmem;
+
+ if (dev == NULL) {
+ pr_err("net_interrupt(): irq %d for unknown device.\n", irq);
+ return IRQ_NONE;
+ }
+
+ ioaddr = dev->base_addr;
+ lp = netdev_priv(dev);
+ shmem = lp->base;
+
+ spin_lock(&lp->lock);
+
+ status = readw(shmem+iSCB_STATUS);
+
+ if (net_debug > 4) {
+ pr_debug("%s: 3c507 interrupt, status %4.4x.\n", dev->name, status);
+ }
+
+ /* Disable the 82586's input to the interrupt line. */
+ outb(0x80, ioaddr + MISC_CTRL);
+
+ /* Reap the Tx packet buffers. */
+ while (lp->tx_pkts_in_ring) {
+ unsigned short tx_status = readw(shmem+lp->tx_reap);
+ if (!(tx_status & 0x8000)) {
+ if (net_debug > 5)
+ pr_debug("Tx command incomplete (%#x).\n", lp->tx_reap);
+ break;
+ }
+ /* Tx unsuccessful or some interesting status bit set. */
+ if (!(tx_status & 0x2000) || (tx_status & 0x0f3f)) {
+ dev->stats.tx_errors++;
+ if (tx_status & 0x0600) dev->stats.tx_carrier_errors++;
+ if (tx_status & 0x0100) dev->stats.tx_fifo_errors++;
+ if (!(tx_status & 0x0040)) dev->stats.tx_heartbeat_errors++;
+ if (tx_status & 0x0020) dev->stats.tx_aborted_errors++;
+ dev->stats.collisions += tx_status & 0xf;
+ }
+ dev->stats.tx_packets++;
+ if (net_debug > 5)
+ pr_debug("Reaped %x, Tx status %04x.\n" , lp->tx_reap, tx_status);
+ lp->tx_reap += TX_BUF_SIZE;
+ if (lp->tx_reap > RX_BUF_START - TX_BUF_SIZE)
+ lp->tx_reap = TX_BUF_START;
+
+ lp->tx_pkts_in_ring--;
+ /* There is always more space in the Tx ring buffer now. */
+ netif_wake_queue(dev);
+
+ if (++boguscount > 10)
+ break;
+ }
+
+ if (status & 0x4000) { /* Packet received. */
+ if (net_debug > 5)
+ pr_debug("Received packet, rx_head %04x.\n", lp->rx_head);
+ el16_rx(dev);
+ }
+
+ /* Acknowledge the interrupt sources. */
+ ack_cmd = status & 0xf000;
+
+ if ((status & 0x0700) != 0x0200 && netif_running(dev)) {
+ if (net_debug)
+ pr_debug("%s: Command unit stopped, status %04x, restarting.\n",
+ dev->name, status);
+ /* If this ever occurs we should really re-write the idle loop, reset
+ the Tx list, and do a complete restart of the command unit.
+ For now we rely on the Tx timeout if the resume doesn't work. */
+ ack_cmd |= CUC_RESUME;
+ }
+
+ if ((status & 0x0070) != 0x0040 && netif_running(dev)) {
+ /* The Rx unit is not ready, it must be hung. Restart the receiver by
+ initializing the rx buffers, and issuing an Rx start command. */
+ if (net_debug)
+ pr_debug("%s: Rx unit stopped, status %04x, restarting.\n",
+ dev->name, status);
+ init_rx_bufs(dev);
+ writew(RX_BUF_START,shmem+iSCB_RFA);
+ ack_cmd |= RX_START;
+ }
+
+ writew(ack_cmd,shmem+iSCB_CMD);
+ outb(0, ioaddr + SIGNAL_CA); /* Issue channel-attn. */
+
+ /* Clear the latched interrupt. */
+ outb(0, ioaddr + RESET_IRQ);
+
+ /* Enable the 82586's interrupt input. */
+ outb(0x84, ioaddr + MISC_CTRL);
+ spin_unlock(&lp->lock);
+ return IRQ_HANDLED;
+}
+
+static int el16_close(struct net_device *dev)
+{
+ struct net_local *lp = netdev_priv(dev);
+ int ioaddr = dev->base_addr;
+ void __iomem *shmem = lp->base;
+
+ netif_stop_queue(dev);
+
+ /* Flush the Tx and disable Rx. */
+ writew(RX_SUSPEND | CUC_SUSPEND,shmem+iSCB_CMD);
+ outb(0, ioaddr + SIGNAL_CA);
+
+ /* Disable the 82586's input to the interrupt line. */
+ outb(0x80, ioaddr + MISC_CTRL);
+
+ /* We always physically use the IRQ line, so we don't do free_irq(). */
+
+ /* Update the statistics here. */
+
+ return 0;
+}
+
+/* Initialize the Rx-block list. */
+static void init_rx_bufs(struct net_device *dev)
+{
+ struct net_local *lp = netdev_priv(dev);
+ void __iomem *write_ptr;
+ unsigned short SCB_base = SCB_BASE;
+
+ int cur_rxbuf = lp->rx_head = RX_BUF_START;
+
+ /* Initialize each Rx frame + data buffer. */
+ do { /* While there is room for one more. */
+
+ write_ptr = lp->base + cur_rxbuf;
+
+ writew(0x0000,write_ptr); /* Status */
+ writew(0x0000,write_ptr+=2); /* Command */
+ writew(cur_rxbuf + RX_BUF_SIZE,write_ptr+=2); /* Link */
+ writew(cur_rxbuf + 22,write_ptr+=2); /* Buffer offset */
+ writew(0x0000,write_ptr+=2); /* Pad for dest addr. */
+ writew(0x0000,write_ptr+=2);
+ writew(0x0000,write_ptr+=2);
+ writew(0x0000,write_ptr+=2); /* Pad for source addr. */
+ writew(0x0000,write_ptr+=2);
+ writew(0x0000,write_ptr+=2);
+ writew(0x0000,write_ptr+=2); /* Pad for protocol. */
+
+ writew(0x0000,write_ptr+=2); /* Buffer: Actual count */
+ writew(-1,write_ptr+=2); /* Buffer: Next (none). */
+ writew(cur_rxbuf + 0x20 + SCB_base,write_ptr+=2);/* Buffer: Address low */
+ writew(0x0000,write_ptr+=2);
+ /* Finally, the number of bytes in the buffer. */
+ writew(0x8000 + RX_BUF_SIZE-0x20,write_ptr+=2);
+
+ lp->rx_tail = cur_rxbuf;
+ cur_rxbuf += RX_BUF_SIZE;
+ } while (cur_rxbuf <= RX_BUF_END - RX_BUF_SIZE);
+
+ /* Terminate the list by setting the EOL bit, and wrap the pointer to make
+ the list a ring. */
+ write_ptr = lp->base + lp->rx_tail + 2;
+ writew(0xC000,write_ptr); /* Command, mark as last. */
+ writew(lp->rx_head,write_ptr+2); /* Link */
+}
+
+static void init_82586_mem(struct net_device *dev)
+{
+ struct net_local *lp = netdev_priv(dev);
+ short ioaddr = dev->base_addr;
+ void __iomem *shmem = lp->base;
+
+ /* Enable loopback to protect the wire while starting up,
+ and hold the 586 in reset during the memory initialization. */
+ outb(0x20, ioaddr + MISC_CTRL);
+
+ /* Fix the ISCP address and base. */
+ init_words[3] = SCB_BASE;
+ init_words[7] = SCB_BASE;
+
+ /* Write the words at 0xfff6 (address-aliased to 0xfffff6). */
+ memcpy_toio(lp->base + RX_BUF_END - 10, init_words, 10);
+
+ /* Write the words at 0x0000. */
+ memcpy_toio(lp->base, init_words + 5, sizeof(init_words) - 10);
+
+ /* Fill in the station address. */
+ memcpy_toio(lp->base+SA_OFFSET, dev->dev_addr, ETH_ALEN);
+
+ /* The Tx-block list is written as needed. We just set up the values. */
+ lp->tx_cmd_link = IDLELOOP + 4;
+ lp->tx_head = lp->tx_reap = TX_BUF_START;
+
+ init_rx_bufs(dev);
+
+ /* Start the 586 by releasing the reset line, but leave loopback. */
+ outb(0xA0, ioaddr + MISC_CTRL);
+
+ /* This was time consuming to track down: you need to give two channel
+ attention signals to reliably start up the i82586. */
+ outb(0, ioaddr + SIGNAL_CA);
+
+ {
+ int boguscnt = 50;
+ while (readw(shmem+iSCB_STATUS) == 0)
+ if (--boguscnt == 0) {
+ pr_warning("%s: i82586 initialization timed out with status %04x, cmd %04x.\n",
+ dev->name, readw(shmem+iSCB_STATUS), readw(shmem+iSCB_CMD));
+ break;
+ }
+ /* Issue channel-attn -- the 82586 won't start. */
+ outb(0, ioaddr + SIGNAL_CA);
+ }
+
+ /* Disable loopback and enable interrupts. */
+ outb(0x84, ioaddr + MISC_CTRL);
+ if (net_debug > 4)
+ pr_debug("%s: Initialized 82586, status %04x.\n", dev->name,
+ readw(shmem+iSCB_STATUS));
+}
+
+static void hardware_send_packet(struct net_device *dev, void *buf, short length, short pad)
+{
+ struct net_local *lp = netdev_priv(dev);
+ short ioaddr = dev->base_addr;
+ ushort tx_block = lp->tx_head;
+ void __iomem *write_ptr = lp->base + tx_block;
+ static char padding[ETH_ZLEN];
+
+ /* Set the write pointer to the Tx block, and put out the header. */
+ writew(0x0000,write_ptr); /* Tx status */
+ writew(CMD_INTR|CmdTx,write_ptr+=2); /* Tx command */
+ writew(tx_block+16,write_ptr+=2); /* Next command is a NoOp. */
+ writew(tx_block+8,write_ptr+=2); /* Data Buffer offset. */
+
+ /* Output the data buffer descriptor. */
+ writew((pad + length) | 0x8000,write_ptr+=2); /* Byte count parameter. */
+ writew(-1,write_ptr+=2); /* No next data buffer. */
+ writew(tx_block+22+SCB_BASE,write_ptr+=2); /* Buffer follows the NoOp command. */
+ writew(0x0000,write_ptr+=2); /* Buffer address high bits (always zero). */
+
+ /* Output the Loop-back NoOp command. */
+ writew(0x0000,write_ptr+=2); /* Tx status */
+ writew(CmdNOp,write_ptr+=2); /* Tx command */
+ writew(tx_block+16,write_ptr+=2); /* Next is myself. */
+
+ /* Output the packet at the write pointer. */
+ memcpy_toio(write_ptr+2, buf, length);
+ if (pad)
+ memcpy_toio(write_ptr+length+2, padding, pad);
+
+ /* Set the old command link pointing to this send packet. */
+ writew(tx_block,lp->base + lp->tx_cmd_link);
+ lp->tx_cmd_link = tx_block + 20;
+
+ /* Set the next free tx region. */
+ lp->tx_head = tx_block + TX_BUF_SIZE;
+ if (lp->tx_head > RX_BUF_START - TX_BUF_SIZE)
+ lp->tx_head = TX_BUF_START;
+
+ if (net_debug > 4) {
+ pr_debug("%s: 3c507 @%x send length = %d, tx_block %3x, next %3x.\n",
+ dev->name, ioaddr, length, tx_block, lp->tx_head);
+ }
+
+ /* Grimly block further packets if there has been insufficient reaping. */
+ if (++lp->tx_pkts_in_ring < NUM_TX_BUFS)
+ netif_wake_queue(dev);
+}
+
+static void el16_rx(struct net_device *dev)
+{
+ struct net_local *lp = netdev_priv(dev);
+ void __iomem *shmem = lp->base;
+ ushort rx_head = lp->rx_head;
+ ushort rx_tail = lp->rx_tail;
+ ushort boguscount = 10;
+ short frame_status;
+
+ while ((frame_status = readw(shmem+rx_head)) < 0) { /* Command complete */
+ void __iomem *read_frame = lp->base + rx_head;
+ ushort rfd_cmd = readw(read_frame+2);
+ ushort next_rx_frame = readw(read_frame+4);
+ ushort data_buffer_addr = readw(read_frame+6);
+ void __iomem *data_frame = lp->base + data_buffer_addr;
+ ushort pkt_len = readw(data_frame);
+
+ if (rfd_cmd != 0 || data_buffer_addr != rx_head + 22 ||
+ (pkt_len & 0xC000) != 0xC000) {
+ pr_err("%s: Rx frame at %#x corrupted, "
+ "status %04x cmd %04x next %04x "
+ "data-buf @%04x %04x.\n",
+ dev->name, rx_head, frame_status, rfd_cmd,
+ next_rx_frame, data_buffer_addr, pkt_len);
+ } else if ((frame_status & 0x2000) == 0) {
+ /* Frame Rxed, but with error. */
+ dev->stats.rx_errors++;
+ if (frame_status & 0x0800) dev->stats.rx_crc_errors++;
+ if (frame_status & 0x0400) dev->stats.rx_frame_errors++;
+ if (frame_status & 0x0200) dev->stats.rx_fifo_errors++;
+ if (frame_status & 0x0100) dev->stats.rx_over_errors++;
+ if (frame_status & 0x0080) dev->stats.rx_length_errors++;
+ } else {
+ /* Malloc up new buffer. */
+ struct sk_buff *skb;
+
+ pkt_len &= 0x3fff;
+ skb = dev_alloc_skb(pkt_len+2);
+ if (skb == NULL) {
+ pr_err("%s: Memory squeeze, dropping packet.\n",
+ dev->name);
+ dev->stats.rx_dropped++;
+ break;
+ }
+
+ skb_reserve(skb,2);
+
+ /* 'skb->data' points to the start of sk_buff data area. */
+ memcpy_fromio(skb_put(skb,pkt_len), data_frame + 10, pkt_len);
+
+ skb->protocol=eth_type_trans(skb,dev);
+ netif_rx(skb);
+ dev->stats.rx_packets++;
+ dev->stats.rx_bytes += pkt_len;
+ }
+
+ /* Clear the status word and set End-of-List on the rx frame. */
+ writew(0,read_frame);
+ writew(0xC000,read_frame+2);
+ /* Clear the end-of-list on the prev. RFD. */
+ writew(0x0000,lp->base + rx_tail + 2);
+
+ rx_tail = rx_head;
+ rx_head = next_rx_frame;
+ if (--boguscount == 0)
+ break;
+ }
+
+ lp->rx_head = rx_head;
+ lp->rx_tail = rx_tail;
+}
+
+static void netdev_get_drvinfo(struct net_device *dev,
+ struct ethtool_drvinfo *info)
+{
+ strcpy(info->driver, DRV_NAME);
+ strcpy(info->version, DRV_VERSION);
+ sprintf(info->bus_info, "ISA 0x%lx", dev->base_addr);
+}
+
+static u32 netdev_get_msglevel(struct net_device *dev)
+{
+ return debug;
+}
+
+static void netdev_set_msglevel(struct net_device *dev, u32 level)
+{
+ debug = level;
+}
+
+static const struct ethtool_ops netdev_ethtool_ops = {
+ .get_drvinfo = netdev_get_drvinfo,
+ .get_msglevel = netdev_get_msglevel,
+ .set_msglevel = netdev_set_msglevel,
+};
+
+#ifdef MODULE
+static struct net_device *dev_3c507;
+module_param(io, int, 0);
+module_param(irq, int, 0);
+MODULE_PARM_DESC(io, "EtherLink16 I/O base address");
+MODULE_PARM_DESC(irq, "(ignored)");
+
+int __init init_module(void)
+{
+ if (io == 0)
+ pr_notice("3c507: You should not use auto-probing with insmod!\n");
+ dev_3c507 = el16_probe(-1);
+ return IS_ERR(dev_3c507) ? PTR_ERR(dev_3c507) : 0;
+}
+
+void __exit
+cleanup_module(void)
+{
+ struct net_device *dev = dev_3c507;
+ unregister_netdev(dev);
+ free_irq(dev->irq, dev);
+ iounmap(((struct net_local *)netdev_priv(dev))->base);
+ release_region(dev->base_addr, EL16_IO_EXTENT);
+ free_netdev(dev);
+}
+#endif /* MODULE */
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/ethernet/i825xx/3c523.c b/drivers/net/ethernet/i825xx/3c523.c
new file mode 100644
index 00000000000..d70d3df4c98
--- /dev/null
+++ b/drivers/net/ethernet/i825xx/3c523.c
@@ -0,0 +1,1312 @@
+/*
+ net-3-driver for the 3c523 Etherlink/MC card (i82586 Ethernet chip)
+
+
+ This is an extension to the Linux operating system, and is covered by the
+ same GNU General Public License that covers that work.
+
+ Copyright 1995, 1996 by Chris Beauregard (cpbeaure@undergrad.math.uwaterloo.ca)
+
+ This is basically Michael Hipp's ni52 driver, with a new probing
+ algorithm and some minor changes to the 82586 CA and reset routines.
+ Thanks a lot Michael for a really clean i82586 implementation! Unless
+ otherwise documented in ni52.c, any bugs are mine.
+
+ Contrary to the Ethernet-HOWTO, this isn't based on the 3c507 driver in
+ any way. The ni52 is a lot easier to modify.
+
+ sources:
+ ni52.c
+
+ Crynwr packet driver collection was a great reference for my first
+ attempt at this sucker. The 3c507 driver also helped, until I noticed
+ that ni52.c was a lot nicer.
+
+ EtherLink/MC: Micro Channel Ethernet Adapter Technical Reference
+ Manual, courtesy of 3Com CardFacts, documents the 3c523-specific
+ stuff. Information on CardFacts is found in the Ethernet HOWTO.
+ Also see <a href="http://www.3com.com/">
+
+ Microprocessor Communications Support Chips, T.J. Byers, ISBN
+ 0-444-01224-9, has a section on the i82586. It tells you just enough
+ to know that you really don't want to learn how to program the chip.
+
+ The original device probe code was stolen from ps2esdi.c
+
+ Known Problems:
+ Since most of the code was stolen from ni52.c, you'll run across the
+ same bugs in the 0.62 version of ni52.c, plus maybe a few because of
+ the 3c523 idiosynchacies. The 3c523 has 16K of RAM though, so there
+ shouldn't be the overrun problem that the 8K ni52 has.
+
+ This driver is for a 16K adapter. It should work fine on the 64K
+ adapters, but it will only use one of the 4 banks of RAM. Modifying
+ this for the 64K version would require a lot of heinous bank
+ switching, which I'm sure not interested in doing. If you try to
+ implement a bank switching version, you'll basically have to remember
+ what bank is enabled and do a switch every time you access a memory
+ location that's not current. You'll also have to remap pointers on
+ the driver side, because it only knows about 16K of the memory.
+ Anyone desperate or masochistic enough to try?
+
+ It seems to be stable now when multiple transmit buffers are used. I
+ can't see any performance difference, but then I'm working on a 386SX.
+
+ Multicast doesn't work. It doesn't even pretend to work. Don't use
+ it. Don't compile your kernel with multicast support. I don't know
+ why.
+
+ Features:
+ This driver is useable as a loadable module. If you try to specify an
+ IRQ or a IO address (via insmod 3c523.o irq=xx io=0xyyy), it will
+ search the MCA slots until it finds a 3c523 with the specified
+ parameters.
+
+ This driver does support multiple ethernet cards when used as a module
+ (up to MAX_3C523_CARDS, the default being 4)
+
+ This has been tested with both BNC and TP versions, internal and
+ external transceivers. Haven't tested with the 64K version (that I
+ know of).
+
+ History:
+ Jan 1st, 1996
+ first public release
+ Feb 4th, 1996
+ update to 1.3.59, incorporated multicast diffs from ni52.c
+ Feb 15th, 1996
+ added shared irq support
+ Apr 1999
+ added support for multiple cards when used as a module
+ added option to disable multicast as is causes problems
+ Ganesh Sittampalam <ganesh.sittampalam@magdalen.oxford.ac.uk>
+ Stuart Adamson <stuart.adamson@compsoc.net>
+ Nov 2001
+ added support for ethtool (jgarzik)
+
+ $Header: /fsys2/home/chrisb/linux-1.3.59-MCA/drivers/net/RCS/3c523.c,v 1.1 1996/02/05 01:53:46 chrisb Exp chrisb $
+ */
+
+#define DRV_NAME "3c523"
+#define DRV_VERSION "17-Nov-2001"
+
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/skbuff.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/mca-legacy.h>
+#include <linux/ethtool.h>
+#include <linux/bitops.h>
+#include <linux/jiffies.h>
+
+#include <asm/uaccess.h>
+#include <asm/processor.h>
+#include <asm/io.h>
+
+#include "3c523.h"
+
+/*************************************************************************/
+#define DEBUG /* debug on */
+#define SYSBUSVAL 0 /* 1 = 8 Bit, 0 = 16 bit - 3c523 only does 16 bit */
+#undef ELMC_MULTICAST /* Disable multicast support as it is somewhat seriously broken at the moment */
+
+#define make32(ptr16) (p->memtop + (short) (ptr16) )
+#define make24(ptr32) ((char *) (ptr32) - p->base)
+#define make16(ptr32) ((unsigned short) ((unsigned long) (ptr32) - (unsigned long) p->memtop ))
+
+/*************************************************************************/
+/*
+ Tables to which we can map values in the configuration registers.
+ */
+static int irq_table[] __initdata = {
+ 12, 7, 3, 9
+};
+
+static int csr_table[] __initdata = {
+ 0x300, 0x1300, 0x2300, 0x3300
+};
+
+static int shm_table[] __initdata = {
+ 0x0c0000, 0x0c8000, 0x0d0000, 0x0d8000
+};
+
+/******************* how to calculate the buffers *****************************
+
+
+ * IMPORTANT NOTE: if you configure only one NUM_XMIT_BUFFS, the driver works
+ * --------------- in a different (more stable?) mode. Only in this mode it's
+ * possible to configure the driver with 'NO_NOPCOMMANDS'
+
+sizeof(scp)=12; sizeof(scb)=16; sizeof(iscp)=8;
+sizeof(scp)+sizeof(iscp)+sizeof(scb) = 36 = INIT
+sizeof(rfd) = 24; sizeof(rbd) = 12;
+sizeof(tbd) = 8; sizeof(transmit_cmd) = 16;
+sizeof(nop_cmd) = 8;
+
+ * if you don't know the driver, better do not change this values: */
+
+#define RECV_BUFF_SIZE 1524 /* slightly oversized */
+#define XMIT_BUFF_SIZE 1524 /* slightly oversized */
+#define NUM_XMIT_BUFFS 1 /* config for both, 8K and 16K shmem */
+#define NUM_RECV_BUFFS_8 4 /* config for 8K shared mem */
+#define NUM_RECV_BUFFS_16 9 /* config for 16K shared mem */
+
+#if (NUM_XMIT_BUFFS == 1)
+#define NO_NOPCOMMANDS /* only possible with NUM_XMIT_BUFFS=1 */
+#endif
+
+/**************************************************************************/
+
+#define DELAY(x) { mdelay(32 * x); }
+
+/* a much shorter delay: */
+#define DELAY_16(); { udelay(16) ; }
+
+/* wait for command with timeout: */
+#define WAIT_4_SCB_CMD() { int i; \
+ for(i=0;i<1024;i++) { \
+ if(!p->scb->cmd) break; \
+ DELAY_16(); \
+ if(i == 1023) { \
+ pr_warning("%s:%d: scb_cmd timed out .. resetting i82586\n",\
+ dev->name,__LINE__); \
+ elmc_id_reset586(); } } }
+
+static irqreturn_t elmc_interrupt(int irq, void *dev_id);
+static int elmc_open(struct net_device *dev);
+static int elmc_close(struct net_device *dev);
+static netdev_tx_t elmc_send_packet(struct sk_buff *, struct net_device *);
+static struct net_device_stats *elmc_get_stats(struct net_device *dev);
+static void elmc_timeout(struct net_device *dev);
+#ifdef ELMC_MULTICAST
+static void set_multicast_list(struct net_device *dev);
+#endif
+static const struct ethtool_ops netdev_ethtool_ops;
+
+/* helper-functions */
+static int init586(struct net_device *dev);
+static int check586(struct net_device *dev, unsigned long where, unsigned size);
+static void alloc586(struct net_device *dev);
+static void startrecv586(struct net_device *dev);
+static void *alloc_rfa(struct net_device *dev, void *ptr);
+static void elmc_rcv_int(struct net_device *dev);
+static void elmc_xmt_int(struct net_device *dev);
+static void elmc_rnr_int(struct net_device *dev);
+
+struct priv {
+ unsigned long base;
+ char *memtop;
+ unsigned long mapped_start; /* Start of ioremap */
+ volatile struct rfd_struct *rfd_last, *rfd_top, *rfd_first;
+ volatile struct scp_struct *scp; /* volatile is important */
+ volatile struct iscp_struct *iscp; /* volatile is important */
+ volatile struct scb_struct *scb; /* volatile is important */
+ volatile struct tbd_struct *xmit_buffs[NUM_XMIT_BUFFS];
+#if (NUM_XMIT_BUFFS == 1)
+ volatile struct transmit_cmd_struct *xmit_cmds[2];
+ volatile struct nop_cmd_struct *nop_cmds[2];
+#else
+ volatile struct transmit_cmd_struct *xmit_cmds[NUM_XMIT_BUFFS];
+ volatile struct nop_cmd_struct *nop_cmds[NUM_XMIT_BUFFS];
+#endif
+ volatile int nop_point, num_recv_buffs;
+ volatile char *xmit_cbuffs[NUM_XMIT_BUFFS];
+ volatile int xmit_count, xmit_last;
+ volatile int slot;
+};
+
+#define elmc_attn586() {elmc_do_attn586(dev->base_addr,ELMC_CTRL_INTE);}
+#define elmc_reset586() {elmc_do_reset586(dev->base_addr,ELMC_CTRL_INTE);}
+
+/* with interrupts disabled - this will clear the interrupt bit in the
+ 3c523 control register, and won't put it back. This effectively
+ disables interrupts on the card. */
+#define elmc_id_attn586() {elmc_do_attn586(dev->base_addr,0);}
+#define elmc_id_reset586() {elmc_do_reset586(dev->base_addr,0);}
+
+/*************************************************************************/
+/*
+ Do a Channel Attention on the 3c523. This is extremely board dependent.
+ */
+static void elmc_do_attn586(int ioaddr, int ints)
+{
+ /* the 3c523 requires a minimum of 500 ns. The delays here might be
+ a little too large, and hence they may cut the performance of the
+ card slightly. If someone who knows a little more about Linux
+ timing would care to play with these, I'd appreciate it. */
+
+ /* this bit masking stuff is crap. I'd rather have separate
+ registers with strobe triggers for each of these functions. <sigh>
+ Ya take what ya got. */
+
+ outb(ELMC_CTRL_RST | 0x3 | ELMC_CTRL_CA | ints, ioaddr + ELMC_CTRL);
+ DELAY_16(); /* > 500 ns */
+ outb(ELMC_CTRL_RST | 0x3 | ints, ioaddr + ELMC_CTRL);
+}
+
+/*************************************************************************/
+/*
+ Reset the 82586 on the 3c523. Also very board dependent.
+ */
+static void elmc_do_reset586(int ioaddr, int ints)
+{
+ /* toggle the RST bit low then high */
+ outb(0x3 | ELMC_CTRL_LBK, ioaddr + ELMC_CTRL);
+ DELAY_16(); /* > 500 ns */
+ outb(ELMC_CTRL_RST | ELMC_CTRL_LBK | 0x3, ioaddr + ELMC_CTRL);
+
+ elmc_do_attn586(ioaddr, ints);
+}
+
+/**********************************************
+ * close device
+ */
+
+static int elmc_close(struct net_device *dev)
+{
+ netif_stop_queue(dev);
+ elmc_id_reset586(); /* the hard way to stop the receiver */
+ free_irq(dev->irq, dev);
+ return 0;
+}
+
+/**********************************************
+ * open device
+ */
+
+static int elmc_open(struct net_device *dev)
+{
+ int ret;
+
+ elmc_id_attn586(); /* disable interrupts */
+
+ ret = request_irq(dev->irq, elmc_interrupt, IRQF_SHARED,
+ dev->name, dev);
+ if (ret) {
+ pr_err("%s: couldn't get irq %d\n", dev->name, dev->irq);
+ elmc_id_reset586();
+ return ret;
+ }
+ alloc586(dev);
+ init586(dev);
+ startrecv586(dev);
+ netif_start_queue(dev);
+ return 0; /* most done by init */
+}
+
+/**********************************************
+ * Check to see if there's an 82586 out there.
+ */
+
+static int __init check586(struct net_device *dev, unsigned long where, unsigned size)
+{
+ struct priv *p = netdev_priv(dev);
+ char *iscp_addrs[2];
+ int i = 0;
+
+ p->base = (unsigned long) isa_bus_to_virt((unsigned long)where) + size - 0x01000000;
+ p->memtop = isa_bus_to_virt((unsigned long)where) + size;
+ p->scp = (struct scp_struct *)(p->base + SCP_DEFAULT_ADDRESS);
+ memset((char *) p->scp, 0, sizeof(struct scp_struct));
+ p->scp->sysbus = SYSBUSVAL; /* 1 = 8Bit-Bus, 0 = 16 Bit */
+
+ iscp_addrs[0] = isa_bus_to_virt((unsigned long)where);
+ iscp_addrs[1] = (char *) p->scp - sizeof(struct iscp_struct);
+
+ for (i = 0; i < 2; i++) {
+ p->iscp = (struct iscp_struct *) iscp_addrs[i];
+ memset((char *) p->iscp, 0, sizeof(struct iscp_struct));
+
+ p->scp->iscp = make24(p->iscp);
+ p->iscp->busy = 1;
+
+ elmc_id_reset586();
+
+ /* reset586 does an implicit CA */
+
+ /* apparently, you sometimes have to kick the 82586 twice... */
+ elmc_id_attn586();
+ DELAY(1);
+
+ if (p->iscp->busy) { /* i82586 clears 'busy' after successful init */
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/******************************************************************
+ * set iscp at the right place, called by elmc_probe and open586.
+ */
+
+static void alloc586(struct net_device *dev)
+{
+ struct priv *p = netdev_priv(dev);
+
+ elmc_id_reset586();
+ DELAY(2);
+
+ p->scp = (struct scp_struct *) (p->base + SCP_DEFAULT_ADDRESS);
+ p->scb = (struct scb_struct *) isa_bus_to_virt(dev->mem_start);
+ p->iscp = (struct iscp_struct *) ((char *) p->scp - sizeof(struct iscp_struct));
+
+ memset((char *) p->iscp, 0, sizeof(struct iscp_struct));
+ memset((char *) p->scp, 0, sizeof(struct scp_struct));
+
+ p->scp->iscp = make24(p->iscp);
+ p->scp->sysbus = SYSBUSVAL;
+ p->iscp->scb_offset = make16(p->scb);
+
+ p->iscp->busy = 1;
+ elmc_id_reset586();
+ elmc_id_attn586();
+
+ DELAY(2);
+
+ if (p->iscp->busy)
+ pr_err("%s: Init-Problems (alloc).\n", dev->name);
+
+ memset((char *) p->scb, 0, sizeof(struct scb_struct));
+}
+
+/*****************************************************************/
+
+static int elmc_getinfo(char *buf, int slot, void *d)
+{
+ int len = 0;
+ struct net_device *dev = d;
+
+ if (dev == NULL)
+ return len;
+
+ len += sprintf(buf + len, "Revision: 0x%x\n",
+ inb(dev->base_addr + ELMC_REVISION) & 0xf);
+ len += sprintf(buf + len, "IRQ: %d\n", dev->irq);
+ len += sprintf(buf + len, "IO Address: %#lx-%#lx\n", dev->base_addr,
+ dev->base_addr + ELMC_IO_EXTENT);
+ len += sprintf(buf + len, "Memory: %#lx-%#lx\n", dev->mem_start,
+ dev->mem_end - 1);
+ len += sprintf(buf + len, "Transceiver: %s\n", dev->if_port ?
+ "External" : "Internal");
+ len += sprintf(buf + len, "Device: %s\n", dev->name);
+ len += sprintf(buf + len, "Hardware Address: %pM\n",
+ dev->dev_addr);
+
+ return len;
+} /* elmc_getinfo() */
+
+static const struct net_device_ops netdev_ops = {
+ .ndo_open = elmc_open,
+ .ndo_stop = elmc_close,
+ .ndo_get_stats = elmc_get_stats,
+ .ndo_start_xmit = elmc_send_packet,
+ .ndo_tx_timeout = elmc_timeout,
+#ifdef ELMC_MULTICAST
+ .ndo_set_rx_mode = set_multicast_list,
+#endif
+ .ndo_change_mtu = eth_change_mtu,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_validate_addr = eth_validate_addr,
+};
+
+/*****************************************************************/
+
+static int __init do_elmc_probe(struct net_device *dev)
+{
+ static int slot;
+ int base_addr = dev->base_addr;
+ int irq = dev->irq;
+ u_char status = 0;
+ u_char revision = 0;
+ int i = 0;
+ unsigned int size = 0;
+ int retval;
+ struct priv *pr = netdev_priv(dev);
+
+ if (MCA_bus == 0) {
+ return -ENODEV;
+ }
+ /* search through the slots for the 3c523. */
+ slot = mca_find_adapter(ELMC_MCA_ID, 0);
+ while (slot != -1) {
+ status = mca_read_stored_pos(slot, 2);
+
+ dev->irq=irq_table[(status & ELMC_STATUS_IRQ_SELECT) >> 6];
+ dev->base_addr=csr_table[(status & ELMC_STATUS_CSR_SELECT) >> 1];
+
+ /*
+ If we're trying to match a specified irq or IO address,
+ we'll reject a match unless it's what we're looking for.
+ Also reject it if the card is already in use.
+ */
+
+ if ((irq && irq != dev->irq) ||
+ (base_addr && base_addr != dev->base_addr)) {
+ slot = mca_find_adapter(ELMC_MCA_ID, slot + 1);
+ continue;
+ }
+ if (!request_region(dev->base_addr, ELMC_IO_EXTENT, DRV_NAME)) {
+ slot = mca_find_adapter(ELMC_MCA_ID, slot + 1);
+ continue;
+ }
+
+ /* found what we're looking for... */
+ break;
+ }
+
+ /* we didn't find any 3c523 in the slots we checked for */
+ if (slot == MCA_NOTFOUND)
+ return (base_addr || irq) ? -ENXIO : -ENODEV;
+
+ mca_set_adapter_name(slot, "3Com 3c523 Etherlink/MC");
+ mca_set_adapter_procfn(slot, (MCA_ProcFn) elmc_getinfo, dev);
+
+ /* if we get this far, adapter has been found - carry on */
+ pr_info("%s: 3c523 adapter found in slot %d\n", dev->name, slot + 1);
+
+ /* Now we extract configuration info from the card.
+ The 3c523 provides information in two of the POS registers, but
+ the second one is only needed if we want to tell the card what IRQ
+ to use. I suspect that whoever sets the thing up initially would
+ prefer we don't screw with those things.
+
+ Note that we read the status info when we found the card...
+
+ See 3c523.h for more details.
+ */
+
+ /* revision is stored in the first 4 bits of the revision register */
+ revision = inb(dev->base_addr + ELMC_REVISION) & 0xf;
+
+ /* according to docs, we read the interrupt and write it back to
+ the IRQ select register, since the POST might not configure the IRQ
+ properly. */
+ switch (dev->irq) {
+ case 3:
+ mca_write_pos(slot, 3, 0x04);
+ break;
+ case 7:
+ mca_write_pos(slot, 3, 0x02);
+ break;
+ case 9:
+ mca_write_pos(slot, 3, 0x08);
+ break;
+ case 12:
+ mca_write_pos(slot, 3, 0x01);
+ break;
+ }
+
+ pr->slot = slot;
+
+ pr_info("%s: 3Com 3c523 Rev 0x%x at %#lx\n", dev->name, (int) revision,
+ dev->base_addr);
+
+ /* Determine if we're using the on-board transceiver (i.e. coax) or
+ an external one. The information is pretty much useless, but I
+ guess it's worth brownie points. */
+ dev->if_port = (status & ELMC_STATUS_DISABLE_THIN);
+
+ /* The 3c523 has a 24K chunk of memory. The first 16K is the
+ shared memory, while the last 8K is for the EtherStart BIOS ROM.
+ Which we don't care much about here. We'll just tell Linux that
+ we're using 16K. MCA won't permit address space conflicts caused
+ by not mapping the other 8K. */
+ dev->mem_start = shm_table[(status & ELMC_STATUS_MEMORY_SELECT) >> 3];
+
+ /* We're using MCA, so it's a given that the information about memory
+ size is correct. The Crynwr drivers do something like this. */
+
+ elmc_id_reset586(); /* seems like a good idea before checking it... */
+
+ size = 0x4000; /* check for 16K mem */
+ if (!check586(dev, dev->mem_start, size)) {
+ pr_err("%s: memprobe, Can't find memory at 0x%lx!\n", dev->name,
+ dev->mem_start);
+ retval = -ENODEV;
+ goto err_out;
+ }
+ dev->mem_end = dev->mem_start + size; /* set mem_end showed by 'ifconfig' */
+
+ pr->memtop = isa_bus_to_virt(dev->mem_start) + size;
+ pr->base = (unsigned long) isa_bus_to_virt(dev->mem_start) + size - 0x01000000;
+ alloc586(dev);
+
+ elmc_id_reset586(); /* make sure it doesn't generate spurious ints */
+
+ /* set number of receive-buffs according to memsize */
+ pr->num_recv_buffs = NUM_RECV_BUFFS_16;
+
+ /* dump all the assorted information */
+ pr_info("%s: IRQ %d, %sternal xcvr, memory %#lx-%#lx.\n", dev->name,
+ dev->irq, dev->if_port ? "ex" : "in",
+ dev->mem_start, dev->mem_end - 1);
+
+ /* The hardware address for the 3c523 is stored in the first six
+ bytes of the IO address. */
+ for (i = 0; i < 6; i++)
+ dev->dev_addr[i] = inb(dev->base_addr + i);
+
+ pr_info("%s: hardware address %pM\n",
+ dev->name, dev->dev_addr);
+
+ dev->netdev_ops = &netdev_ops;
+ dev->watchdog_timeo = HZ;
+ dev->ethtool_ops = &netdev_ethtool_ops;
+
+ /* note that we haven't actually requested the IRQ from the kernel.
+ That gets done in elmc_open(). I'm not sure that's such a good idea,
+ but it works, so I'll go with it. */
+
+#ifndef ELMC_MULTICAST
+ dev->flags&=~IFF_MULTICAST; /* Multicast doesn't work */
+#endif
+
+ retval = register_netdev(dev);
+ if (retval)
+ goto err_out;
+
+ return 0;
+err_out:
+ mca_set_adapter_procfn(slot, NULL, NULL);
+ release_region(dev->base_addr, ELMC_IO_EXTENT);
+ return retval;
+}
+
+#ifdef MODULE
+static void cleanup_card(struct net_device *dev)
+{
+ mca_set_adapter_procfn(((struct priv *)netdev_priv(dev))->slot,
+ NULL, NULL);
+ release_region(dev->base_addr, ELMC_IO_EXTENT);
+}
+#else
+struct net_device * __init elmc_probe(int unit)
+{
+ struct net_device *dev = alloc_etherdev(sizeof(struct priv));
+ int err;
+
+ if (!dev)
+ return ERR_PTR(-ENOMEM);
+
+ sprintf(dev->name, "eth%d", unit);
+ netdev_boot_setup_check(dev);
+
+ err = do_elmc_probe(dev);
+ if (err)
+ goto out;
+ return dev;
+out:
+ free_netdev(dev);
+ return ERR_PTR(err);
+}
+#endif
+
+/**********************************************
+ * init the chip (elmc-interrupt should be disabled?!)
+ * needs a correct 'allocated' memory
+ */
+
+static int init586(struct net_device *dev)
+{
+ void *ptr;
+ unsigned long s;
+ int i, result = 0;
+ struct priv *p = netdev_priv(dev);
+ volatile struct configure_cmd_struct *cfg_cmd;
+ volatile struct iasetup_cmd_struct *ias_cmd;
+ volatile struct tdr_cmd_struct *tdr_cmd;
+ volatile struct mcsetup_cmd_struct *mc_cmd;
+ struct netdev_hw_addr *ha;
+ int num_addrs = netdev_mc_count(dev);
+
+ ptr = (void *) ((char *) p->scb + sizeof(struct scb_struct));
+
+ cfg_cmd = (struct configure_cmd_struct *) ptr; /* configure-command */
+ cfg_cmd->cmd_status = 0;
+ cfg_cmd->cmd_cmd = CMD_CONFIGURE | CMD_LAST;
+ cfg_cmd->cmd_link = 0xffff;
+
+ cfg_cmd->byte_cnt = 0x0a; /* number of cfg bytes */
+ cfg_cmd->fifo = 0x08; /* fifo-limit (8=tx:32/rx:64) */
+ cfg_cmd->sav_bf = 0x40; /* hold or discard bad recv frames (bit 7) */
+ cfg_cmd->adr_len = 0x2e; /* addr_len |!src_insert |pre-len |loopback */
+ cfg_cmd->priority = 0x00;
+ cfg_cmd->ifs = 0x60;
+ cfg_cmd->time_low = 0x00;
+ cfg_cmd->time_high = 0xf2;
+ cfg_cmd->promisc = 0;
+ if (dev->flags & (IFF_ALLMULTI | IFF_PROMISC))
+ cfg_cmd->promisc = 1;
+ cfg_cmd->carr_coll = 0x00;
+
+ p->scb->cbl_offset = make16(cfg_cmd);
+
+ p->scb->cmd = CUC_START; /* cmd.-unit start */
+ elmc_id_attn586();
+
+ s = jiffies; /* warning: only active with interrupts on !! */
+ while (!(cfg_cmd->cmd_status & STAT_COMPL)) {
+ if (time_after(jiffies, s + 30*HZ/100))
+ break;
+ }
+
+ if ((cfg_cmd->cmd_status & (STAT_OK | STAT_COMPL)) != (STAT_COMPL | STAT_OK)) {
+ pr_warning("%s (elmc): configure command failed: %x\n", dev->name, cfg_cmd->cmd_status);
+ return 1;
+ }
+ /*
+ * individual address setup
+ */
+ ias_cmd = (struct iasetup_cmd_struct *) ptr;
+
+ ias_cmd->cmd_status = 0;
+ ias_cmd->cmd_cmd = CMD_IASETUP | CMD_LAST;
+ ias_cmd->cmd_link = 0xffff;
+
+ memcpy((char *) &ias_cmd->iaddr, (char *) dev->dev_addr, ETH_ALEN);
+
+ p->scb->cbl_offset = make16(ias_cmd);
+
+ p->scb->cmd = CUC_START; /* cmd.-unit start */
+ elmc_id_attn586();
+
+ s = jiffies;
+ while (!(ias_cmd->cmd_status & STAT_COMPL)) {
+ if (time_after(jiffies, s + 30*HZ/100))
+ break;
+ }
+
+ if ((ias_cmd->cmd_status & (STAT_OK | STAT_COMPL)) != (STAT_OK | STAT_COMPL)) {
+ pr_warning("%s (elmc): individual address setup command failed: %04x\n",
+ dev->name, ias_cmd->cmd_status);
+ return 1;
+ }
+ /*
+ * TDR, wire check .. e.g. no resistor e.t.c
+ */
+ tdr_cmd = (struct tdr_cmd_struct *) ptr;
+
+ tdr_cmd->cmd_status = 0;
+ tdr_cmd->cmd_cmd = CMD_TDR | CMD_LAST;
+ tdr_cmd->cmd_link = 0xffff;
+ tdr_cmd->status = 0;
+
+ p->scb->cbl_offset = make16(tdr_cmd);
+
+ p->scb->cmd = CUC_START; /* cmd.-unit start */
+ elmc_attn586();
+
+ s = jiffies;
+ while (!(tdr_cmd->cmd_status & STAT_COMPL)) {
+ if (time_after(jiffies, s + 30*HZ/100)) {
+ pr_warning("%s: %d Problems while running the TDR.\n", dev->name, __LINE__);
+ result = 1;
+ break;
+ }
+ }
+
+ if (!result) {
+ DELAY(2); /* wait for result */
+ result = tdr_cmd->status;
+
+ p->scb->cmd = p->scb->status & STAT_MASK;
+ elmc_id_attn586(); /* ack the interrupts */
+
+ if (result & TDR_LNK_OK) {
+ /* empty */
+ } else if (result & TDR_XCVR_PRB) {
+ pr_warning("%s: TDR: Transceiver problem!\n", dev->name);
+ } else if (result & TDR_ET_OPN) {
+ pr_warning("%s: TDR: No correct termination %d clocks away.\n", dev->name, result & TDR_TIMEMASK);
+ } else if (result & TDR_ET_SRT) {
+ if (result & TDR_TIMEMASK) /* time == 0 -> strange :-) */
+ pr_warning("%s: TDR: Detected a short circuit %d clocks away.\n", dev->name, result & TDR_TIMEMASK);
+ } else {
+ pr_warning("%s: TDR: Unknown status %04x\n", dev->name, result);
+ }
+ }
+ /*
+ * ack interrupts
+ */
+ p->scb->cmd = p->scb->status & STAT_MASK;
+ elmc_id_attn586();
+
+ /*
+ * alloc nop/xmit-cmds
+ */
+#if (NUM_XMIT_BUFFS == 1)
+ for (i = 0; i < 2; i++) {
+ p->nop_cmds[i] = (struct nop_cmd_struct *) ptr;
+ p->nop_cmds[i]->cmd_cmd = CMD_NOP;
+ p->nop_cmds[i]->cmd_status = 0;
+ p->nop_cmds[i]->cmd_link = make16((p->nop_cmds[i]));
+ ptr = (char *) ptr + sizeof(struct nop_cmd_struct);
+ }
+ p->xmit_cmds[0] = (struct transmit_cmd_struct *) ptr; /* transmit cmd/buff 0 */
+ ptr = (char *) ptr + sizeof(struct transmit_cmd_struct);
+#else
+ for (i = 0; i < NUM_XMIT_BUFFS; i++) {
+ p->nop_cmds[i] = (struct nop_cmd_struct *) ptr;
+ p->nop_cmds[i]->cmd_cmd = CMD_NOP;
+ p->nop_cmds[i]->cmd_status = 0;
+ p->nop_cmds[i]->cmd_link = make16((p->nop_cmds[i]));
+ ptr = (char *) ptr + sizeof(struct nop_cmd_struct);
+ p->xmit_cmds[i] = (struct transmit_cmd_struct *) ptr; /*transmit cmd/buff 0 */
+ ptr = (char *) ptr + sizeof(struct transmit_cmd_struct);
+ }
+#endif
+
+ ptr = alloc_rfa(dev, (void *) ptr); /* init receive-frame-area */
+
+ /*
+ * Multicast setup
+ */
+
+ if (num_addrs) {
+ /* I don't understand this: do we really need memory after the init? */
+ int len = ((char *) p->iscp - (char *) ptr - 8) / 6;
+ if (len <= 0) {
+ pr_err("%s: Ooooops, no memory for MC-Setup!\n", dev->name);
+ } else {
+ if (len < num_addrs) {
+ num_addrs = len;
+ pr_warning("%s: Sorry, can only apply %d MC-Address(es).\n",
+ dev->name, num_addrs);
+ }
+ mc_cmd = (struct mcsetup_cmd_struct *) ptr;
+ mc_cmd->cmd_status = 0;
+ mc_cmd->cmd_cmd = CMD_MCSETUP | CMD_LAST;
+ mc_cmd->cmd_link = 0xffff;
+ mc_cmd->mc_cnt = num_addrs * 6;
+ i = 0;
+ netdev_for_each_mc_addr(ha, dev)
+ memcpy((char *) mc_cmd->mc_list[i++],
+ ha->addr, 6);
+ p->scb->cbl_offset = make16(mc_cmd);
+ p->scb->cmd = CUC_START;
+ elmc_id_attn586();
+ s = jiffies;
+ while (!(mc_cmd->cmd_status & STAT_COMPL)) {
+ if (time_after(jiffies, s + 30*HZ/100))
+ break;
+ }
+ if (!(mc_cmd->cmd_status & STAT_COMPL)) {
+ pr_warning("%s: Can't apply multicast-address-list.\n", dev->name);
+ }
+ }
+ }
+ /*
+ * alloc xmit-buffs / init xmit_cmds
+ */
+ for (i = 0; i < NUM_XMIT_BUFFS; i++) {
+ p->xmit_cbuffs[i] = (char *) ptr; /* char-buffs */
+ ptr = (char *) ptr + XMIT_BUFF_SIZE;
+ p->xmit_buffs[i] = (struct tbd_struct *) ptr; /* TBD */
+ ptr = (char *) ptr + sizeof(struct tbd_struct);
+ if ((void *) ptr > (void *) p->iscp) {
+ pr_err("%s: not enough shared-mem for your configuration!\n", dev->name);
+ return 1;
+ }
+ memset((char *) (p->xmit_cmds[i]), 0, sizeof(struct transmit_cmd_struct));
+ memset((char *) (p->xmit_buffs[i]), 0, sizeof(struct tbd_struct));
+ p->xmit_cmds[i]->cmd_status = STAT_COMPL;
+ p->xmit_cmds[i]->cmd_cmd = CMD_XMIT | CMD_INT;
+ p->xmit_cmds[i]->tbd_offset = make16((p->xmit_buffs[i]));
+ p->xmit_buffs[i]->next = 0xffff;
+ p->xmit_buffs[i]->buffer = make24((p->xmit_cbuffs[i]));
+ }
+
+ p->xmit_count = 0;
+ p->xmit_last = 0;
+#ifndef NO_NOPCOMMANDS
+ p->nop_point = 0;
+#endif
+
+ /*
+ * 'start transmitter' (nop-loop)
+ */
+#ifndef NO_NOPCOMMANDS
+ p->scb->cbl_offset = make16(p->nop_cmds[0]);
+ p->scb->cmd = CUC_START;
+ elmc_id_attn586();
+ WAIT_4_SCB_CMD();
+#else
+ p->xmit_cmds[0]->cmd_link = 0xffff;
+ p->xmit_cmds[0]->cmd_cmd = CMD_XMIT | CMD_LAST | CMD_INT;
+#endif
+
+ return 0;
+}
+
+/******************************************************
+ * This is a helper routine for elmc_rnr_int() and init586().
+ * It sets up the Receive Frame Area (RFA).
+ */
+
+static void *alloc_rfa(struct net_device *dev, void *ptr)
+{
+ volatile struct rfd_struct *rfd = (struct rfd_struct *) ptr;
+ volatile struct rbd_struct *rbd;
+ int i;
+ struct priv *p = netdev_priv(dev);
+
+ memset((char *) rfd, 0, sizeof(struct rfd_struct) * p->num_recv_buffs);
+ p->rfd_first = rfd;
+
+ for (i = 0; i < p->num_recv_buffs; i++) {
+ rfd[i].next = make16(rfd + (i + 1) % p->num_recv_buffs);
+ }
+ rfd[p->num_recv_buffs - 1].last = RFD_SUSP; /* RU suspend */
+
+ ptr = (void *) (rfd + p->num_recv_buffs);
+
+ rbd = (struct rbd_struct *) ptr;
+ ptr = (void *) (rbd + p->num_recv_buffs);
+
+ /* clr descriptors */
+ memset((char *) rbd, 0, sizeof(struct rbd_struct) * p->num_recv_buffs);
+
+ for (i = 0; i < p->num_recv_buffs; i++) {
+ rbd[i].next = make16((rbd + (i + 1) % p->num_recv_buffs));
+ rbd[i].size = RECV_BUFF_SIZE;
+ rbd[i].buffer = make24(ptr);
+ ptr = (char *) ptr + RECV_BUFF_SIZE;
+ }
+
+ p->rfd_top = p->rfd_first;
+ p->rfd_last = p->rfd_first + p->num_recv_buffs - 1;
+
+ p->scb->rfa_offset = make16(p->rfd_first);
+ p->rfd_first->rbd_offset = make16(rbd);
+
+ return ptr;
+}
+
+
+/**************************************************
+ * Interrupt Handler ...
+ */
+
+static irqreturn_t
+elmc_interrupt(int irq, void *dev_id)
+{
+ struct net_device *dev = dev_id;
+ unsigned short stat;
+ struct priv *p;
+
+ if (!netif_running(dev)) {
+ /* The 3c523 has this habit of generating interrupts during the
+ reset. I'm not sure if the ni52 has this same problem, but it's
+ really annoying if we haven't finished initializing it. I was
+ hoping all the elmc_id_* commands would disable this, but I
+ might have missed a few. */
+
+ elmc_id_attn586(); /* ack inter. and disable any more */
+ return IRQ_HANDLED;
+ } else if (!(ELMC_CTRL_INT & inb(dev->base_addr + ELMC_CTRL))) {
+ /* wasn't this device */
+ return IRQ_NONE;
+ }
+ /* reading ELMC_CTRL also clears the INT bit. */
+
+ p = netdev_priv(dev);
+
+ while ((stat = p->scb->status & STAT_MASK))
+ {
+ p->scb->cmd = stat;
+ elmc_attn586(); /* ack inter. */
+
+ if (stat & STAT_CX) {
+ /* command with I-bit set complete */
+ elmc_xmt_int(dev);
+ }
+ if (stat & STAT_FR) {
+ /* received a frame */
+ elmc_rcv_int(dev);
+ }
+#ifndef NO_NOPCOMMANDS
+ if (stat & STAT_CNA) {
+ /* CU went 'not ready' */
+ if (netif_running(dev)) {
+ pr_warning("%s: oops! CU has left active state. stat: %04x/%04x.\n",
+ dev->name, (int) stat, (int) p->scb->status);
+ }
+ }
+#endif
+
+ if (stat & STAT_RNR) {
+ /* RU went 'not ready' */
+
+ if (p->scb->status & RU_SUSPEND) {
+ /* special case: RU_SUSPEND */
+
+ WAIT_4_SCB_CMD();
+ p->scb->cmd = RUC_RESUME;
+ elmc_attn586();
+ } else {
+ pr_warning("%s: Receiver-Unit went 'NOT READY': %04x/%04x.\n",
+ dev->name, (int) stat, (int) p->scb->status);
+ elmc_rnr_int(dev);
+ }
+ }
+ WAIT_4_SCB_CMD(); /* wait for ack. (elmc_xmt_int can be faster than ack!!) */
+ if (p->scb->cmd) { /* timed out? */
+ break;
+ }
+ }
+ return IRQ_HANDLED;
+}
+
+/*******************************************************
+ * receive-interrupt
+ */
+
+static void elmc_rcv_int(struct net_device *dev)
+{
+ int status;
+ unsigned short totlen;
+ struct sk_buff *skb;
+ struct rbd_struct *rbd;
+ struct priv *p = netdev_priv(dev);
+
+ for (; (status = p->rfd_top->status) & STAT_COMPL;) {
+ rbd = (struct rbd_struct *) make32(p->rfd_top->rbd_offset);
+
+ if (status & STAT_OK) { /* frame received without error? */
+ if ((totlen = rbd->status) & RBD_LAST) { /* the first and the last buffer? */
+ totlen &= RBD_MASK; /* length of this frame */
+ rbd->status = 0;
+ skb = (struct sk_buff *) dev_alloc_skb(totlen + 2);
+ if (skb != NULL) {
+ skb_reserve(skb, 2); /* 16 byte alignment */
+ skb_put(skb,totlen);
+ skb_copy_to_linear_data(skb, (char *) p->base+(unsigned long) rbd->buffer,totlen);
+ skb->protocol = eth_type_trans(skb, dev);
+ netif_rx(skb);
+ dev->stats.rx_packets++;
+ dev->stats.rx_bytes += totlen;
+ } else {
+ dev->stats.rx_dropped++;
+ }
+ } else {
+ pr_warning("%s: received oversized frame.\n", dev->name);
+ dev->stats.rx_dropped++;
+ }
+ } else { /* frame !(ok), only with 'save-bad-frames' */
+ pr_warning("%s: oops! rfd-error-status: %04x\n", dev->name, status);
+ dev->stats.rx_errors++;
+ }
+ p->rfd_top->status = 0;
+ p->rfd_top->last = RFD_SUSP;
+ p->rfd_last->last = 0; /* delete RU_SUSP */
+ p->rfd_last = p->rfd_top;
+ p->rfd_top = (struct rfd_struct *) make32(p->rfd_top->next); /* step to next RFD */
+ }
+}
+
+/**********************************************************
+ * handle 'Receiver went not ready'.
+ */
+
+static void elmc_rnr_int(struct net_device *dev)
+{
+ struct priv *p = netdev_priv(dev);
+
+ dev->stats.rx_errors++;
+
+ WAIT_4_SCB_CMD(); /* wait for the last cmd */
+ p->scb->cmd = RUC_ABORT; /* usually the RU is in the 'no resource'-state .. abort it now. */
+ elmc_attn586();
+ WAIT_4_SCB_CMD(); /* wait for accept cmd. */
+
+ alloc_rfa(dev, (char *) p->rfd_first);
+ startrecv586(dev); /* restart RU */
+
+ pr_warning("%s: Receive-Unit restarted. Status: %04x\n", dev->name, p->scb->status);
+
+}
+
+/**********************************************************
+ * handle xmit - interrupt
+ */
+
+static void elmc_xmt_int(struct net_device *dev)
+{
+ int status;
+ struct priv *p = netdev_priv(dev);
+
+ status = p->xmit_cmds[p->xmit_last]->cmd_status;
+ if (!(status & STAT_COMPL)) {
+ pr_warning("%s: strange .. xmit-int without a 'COMPLETE'\n", dev->name);
+ }
+ if (status & STAT_OK) {
+ dev->stats.tx_packets++;
+ dev->stats.collisions += (status & TCMD_MAXCOLLMASK);
+ } else {
+ dev->stats.tx_errors++;
+ if (status & TCMD_LATECOLL) {
+ pr_warning("%s: late collision detected.\n", dev->name);
+ dev->stats.collisions++;
+ } else if (status & TCMD_NOCARRIER) {
+ dev->stats.tx_carrier_errors++;
+ pr_warning("%s: no carrier detected.\n", dev->name);
+ } else if (status & TCMD_LOSTCTS) {
+ pr_warning("%s: loss of CTS detected.\n", dev->name);
+ } else if (status & TCMD_UNDERRUN) {
+ dev->stats.tx_fifo_errors++;
+ pr_warning("%s: DMA underrun detected.\n", dev->name);
+ } else if (status & TCMD_MAXCOLL) {
+ pr_warning("%s: Max. collisions exceeded.\n", dev->name);
+ dev->stats.collisions += 16;
+ }
+ }
+
+#if (NUM_XMIT_BUFFS != 1)
+ if ((++p->xmit_last) == NUM_XMIT_BUFFS) {
+ p->xmit_last = 0;
+ }
+#endif
+
+ netif_wake_queue(dev);
+}
+
+/***********************************************************
+ * (re)start the receiver
+ */
+
+static void startrecv586(struct net_device *dev)
+{
+ struct priv *p = netdev_priv(dev);
+
+ p->scb->rfa_offset = make16(p->rfd_first);
+ p->scb->cmd = RUC_START;
+ elmc_attn586(); /* start cmd. */
+ WAIT_4_SCB_CMD(); /* wait for accept cmd. (no timeout!!) */
+}
+
+/******************************************************
+ * timeout
+ */
+
+static void elmc_timeout(struct net_device *dev)
+{
+ struct priv *p = netdev_priv(dev);
+ /* COMMAND-UNIT active? */
+ if (p->scb->status & CU_ACTIVE) {
+ pr_debug("%s: strange ... timeout with CU active?!?\n", dev->name);
+ pr_debug("%s: X0: %04x N0: %04x N1: %04x %d\n", dev->name,
+ (int)p->xmit_cmds[0]->cmd_status,
+ (int)p->nop_cmds[0]->cmd_status,
+ (int)p->nop_cmds[1]->cmd_status, (int)p->nop_point);
+ p->scb->cmd = CUC_ABORT;
+ elmc_attn586();
+ WAIT_4_SCB_CMD();
+ p->scb->cbl_offset = make16(p->nop_cmds[p->nop_point]);
+ p->scb->cmd = CUC_START;
+ elmc_attn586();
+ WAIT_4_SCB_CMD();
+ netif_wake_queue(dev);
+ } else {
+ pr_debug("%s: xmitter timed out, try to restart! stat: %04x\n",
+ dev->name, p->scb->status);
+ pr_debug("%s: command-stats: %04x %04x\n", dev->name,
+ p->xmit_cmds[0]->cmd_status, p->xmit_cmds[1]->cmd_status);
+ elmc_close(dev);
+ elmc_open(dev);
+ }
+}
+
+/******************************************************
+ * send frame
+ */
+
+static netdev_tx_t elmc_send_packet(struct sk_buff *skb, struct net_device *dev)
+{
+ int len;
+ int i;
+#ifndef NO_NOPCOMMANDS
+ int next_nop;
+#endif
+ struct priv *p = netdev_priv(dev);
+
+ netif_stop_queue(dev);
+
+ len = (ETH_ZLEN < skb->len) ? skb->len : ETH_ZLEN;
+
+ if (len != skb->len)
+ memset((char *) p->xmit_cbuffs[p->xmit_count], 0, ETH_ZLEN);
+ skb_copy_from_linear_data(skb, (char *) p->xmit_cbuffs[p->xmit_count], skb->len);
+
+#if (NUM_XMIT_BUFFS == 1)
+#ifdef NO_NOPCOMMANDS
+ p->xmit_buffs[0]->size = TBD_LAST | len;
+ for (i = 0; i < 16; i++) {
+ p->scb->cbl_offset = make16(p->xmit_cmds[0]);
+ p->scb->cmd = CUC_START;
+ p->xmit_cmds[0]->cmd_status = 0;
+ elmc_attn586();
+ if (!i) {
+ dev_kfree_skb(skb);
+ }
+ WAIT_4_SCB_CMD();
+ if ((p->scb->status & CU_ACTIVE)) { /* test it, because CU sometimes doesn't start immediately */
+ break;
+ }
+ if (p->xmit_cmds[0]->cmd_status) {
+ break;
+ }
+ if (i == 15) {
+ pr_warning("%s: Can't start transmit-command.\n", dev->name);
+ }
+ }
+#else
+ next_nop = (p->nop_point + 1) & 0x1;
+ p->xmit_buffs[0]->size = TBD_LAST | len;
+
+ p->xmit_cmds[0]->cmd_link = p->nop_cmds[next_nop]->cmd_link
+ = make16((p->nop_cmds[next_nop]));
+ p->xmit_cmds[0]->cmd_status = p->nop_cmds[next_nop]->cmd_status = 0;
+
+ p->nop_cmds[p->nop_point]->cmd_link = make16((p->xmit_cmds[0]));
+ p->nop_point = next_nop;
+ dev_kfree_skb(skb);
+#endif
+#else
+ p->xmit_buffs[p->xmit_count]->size = TBD_LAST | len;
+ if ((next_nop = p->xmit_count + 1) == NUM_XMIT_BUFFS) {
+ next_nop = 0;
+ }
+ p->xmit_cmds[p->xmit_count]->cmd_status = 0;
+ p->xmit_cmds[p->xmit_count]->cmd_link = p->nop_cmds[next_nop]->cmd_link
+ = make16((p->nop_cmds[next_nop]));
+ p->nop_cmds[next_nop]->cmd_status = 0;
+ p->nop_cmds[p->xmit_count]->cmd_link = make16((p->xmit_cmds[p->xmit_count]));
+ p->xmit_count = next_nop;
+ if (p->xmit_count != p->xmit_last)
+ netif_wake_queue(dev);
+ dev_kfree_skb(skb);
+#endif
+ return NETDEV_TX_OK;
+}
+
+/*******************************************
+ * Someone wanna have the statistics
+ */
+
+static struct net_device_stats *elmc_get_stats(struct net_device *dev)
+{
+ struct priv *p = netdev_priv(dev);
+ unsigned short crc, aln, rsc, ovrn;
+
+ crc = p->scb->crc_errs; /* get error-statistic from the ni82586 */
+ p->scb->crc_errs -= crc;
+ aln = p->scb->aln_errs;
+ p->scb->aln_errs -= aln;
+ rsc = p->scb->rsc_errs;
+ p->scb->rsc_errs -= rsc;
+ ovrn = p->scb->ovrn_errs;
+ p->scb->ovrn_errs -= ovrn;
+
+ dev->stats.rx_crc_errors += crc;
+ dev->stats.rx_fifo_errors += ovrn;
+ dev->stats.rx_frame_errors += aln;
+ dev->stats.rx_dropped += rsc;
+
+ return &dev->stats;
+}
+
+/********************************************************
+ * Set MC list ..
+ */
+
+#ifdef ELMC_MULTICAST
+static void set_multicast_list(struct net_device *dev)
+{
+ if (!dev->start) {
+ /* without a running interface, promiscuous doesn't work */
+ return;
+ }
+ dev->start = 0;
+ alloc586(dev);
+ init586(dev);
+ startrecv586(dev);
+ dev->start = 1;
+}
+#endif
+
+static void netdev_get_drvinfo(struct net_device *dev,
+ struct ethtool_drvinfo *info)
+{
+ strcpy(info->driver, DRV_NAME);
+ strcpy(info->version, DRV_VERSION);
+ sprintf(info->bus_info, "MCA 0x%lx", dev->base_addr);
+}
+
+static const struct ethtool_ops netdev_ethtool_ops = {
+ .get_drvinfo = netdev_get_drvinfo,
+};
+
+#ifdef MODULE
+
+/* Increase if needed ;) */
+#define MAX_3C523_CARDS 4
+
+static struct net_device *dev_elmc[MAX_3C523_CARDS];
+static int irq[MAX_3C523_CARDS];
+static int io[MAX_3C523_CARDS];
+module_param_array(irq, int, NULL, 0);
+module_param_array(io, int, NULL, 0);
+MODULE_PARM_DESC(io, "EtherLink/MC I/O base address(es)");
+MODULE_PARM_DESC(irq, "EtherLink/MC IRQ number(s)");
+MODULE_LICENSE("GPL");
+
+int __init init_module(void)
+{
+ int this_dev,found = 0;
+
+ /* Loop until we either can't find any more cards, or we have MAX_3C523_CARDS */
+ for(this_dev=0; this_dev<MAX_3C523_CARDS; this_dev++) {
+ struct net_device *dev = alloc_etherdev(sizeof(struct priv));
+ if (!dev)
+ break;
+ dev->irq=irq[this_dev];
+ dev->base_addr=io[this_dev];
+ if (do_elmc_probe(dev) == 0) {
+ dev_elmc[this_dev] = dev;
+ found++;
+ continue;
+ }
+ free_netdev(dev);
+ if (io[this_dev]==0)
+ break;
+ pr_warning("3c523.c: No 3c523 card found at io=%#x\n",io[this_dev]);
+ }
+
+ if(found==0) {
+ if (io[0]==0)
+ pr_notice("3c523.c: No 3c523 cards found\n");
+ return -ENXIO;
+ } else return 0;
+}
+
+void __exit cleanup_module(void)
+{
+ int this_dev;
+ for (this_dev=0; this_dev<MAX_3C523_CARDS; this_dev++) {
+ struct net_device *dev = dev_elmc[this_dev];
+ if (dev) {
+ unregister_netdev(dev);
+ cleanup_card(dev);
+ free_netdev(dev);
+ }
+ }
+}
+
+#endif /* MODULE */
diff --git a/drivers/net/ethernet/i825xx/3c523.h b/drivers/net/ethernet/i825xx/3c523.h
new file mode 100644
index 00000000000..6956441687b
--- /dev/null
+++ b/drivers/net/ethernet/i825xx/3c523.h
@@ -0,0 +1,355 @@
+#ifndef _3c523_INCLUDE_
+#define _3c523_INCLUDE_
+/*
+ This is basically a hacked version of ni52.h, for the 3c523
+ Etherlink/MC.
+*/
+
+/*
+ * Intel i82586 Ethernet definitions
+ *
+ * This is an extension to the Linux operating system, and is covered by the
+ * same GNU General Public License that covers that work.
+ *
+ * Copyright 1995 by Chris Beauregard (cpbeaure@undergrad.math.uwaterloo.ca)
+ *
+ * See 3c523.c for details.
+ *
+ * $Header: /home/chrisb/linux-1.2.13-3c523/drivers/net/RCS/3c523.h,v 1.6 1996/01/20 05:09:00 chrisb Exp chrisb $
+ */
+
+/*
+ * where to find the System Configuration Pointer (SCP)
+ */
+#define SCP_DEFAULT_ADDRESS 0xfffff4
+
+
+/*
+ * System Configuration Pointer Struct
+ */
+
+struct scp_struct
+{
+ unsigned short zero_dum0; /* has to be zero */
+ unsigned char sysbus; /* 0=16Bit,1=8Bit */
+ unsigned char zero_dum1; /* has to be zero for 586 */
+ unsigned short zero_dum2;
+ unsigned short zero_dum3;
+ char *iscp; /* pointer to the iscp-block */
+};
+
+
+/*
+ * Intermediate System Configuration Pointer (ISCP)
+ */
+struct iscp_struct
+{
+ unsigned char busy; /* 586 clears after successful init */
+ unsigned char zero_dummy; /* hast to be zero */
+ unsigned short scb_offset; /* pointeroffset to the scb_base */
+ char *scb_base; /* base-address of all 16-bit offsets */
+};
+
+/*
+ * System Control Block (SCB)
+ */
+struct scb_struct
+{
+ unsigned short status; /* status word */
+ unsigned short cmd; /* command word */
+ unsigned short cbl_offset; /* pointeroffset, command block list */
+ unsigned short rfa_offset; /* pointeroffset, receive frame area */
+ unsigned short crc_errs; /* CRC-Error counter */
+ unsigned short aln_errs; /* alignmenterror counter */
+ unsigned short rsc_errs; /* Resourceerror counter */
+ unsigned short ovrn_errs; /* OVerrunerror counter */
+};
+
+/*
+ * possible command values for the command word
+ */
+#define RUC_MASK 0x0070 /* mask for RU commands */
+#define RUC_NOP 0x0000 /* NOP-command */
+#define RUC_START 0x0010 /* start RU */
+#define RUC_RESUME 0x0020 /* resume RU after suspend */
+#define RUC_SUSPEND 0x0030 /* suspend RU */
+#define RUC_ABORT 0x0040 /* abort receiver operation immediately */
+
+#define CUC_MASK 0x0700 /* mask for CU command */
+#define CUC_NOP 0x0000 /* NOP-command */
+#define CUC_START 0x0100 /* start execution of 1. cmd on the CBL */
+#define CUC_RESUME 0x0200 /* resume after suspend */
+#define CUC_SUSPEND 0x0300 /* Suspend CU */
+#define CUC_ABORT 0x0400 /* abort command operation immediately */
+
+#define ACK_MASK 0xf000 /* mask for ACK command */
+#define ACK_CX 0x8000 /* acknowledges STAT_CX */
+#define ACK_FR 0x4000 /* ack. STAT_FR */
+#define ACK_CNA 0x2000 /* ack. STAT_CNA */
+#define ACK_RNR 0x1000 /* ack. STAT_RNR */
+
+/*
+ * possible status values for the status word
+ */
+#define STAT_MASK 0xf000 /* mask for cause of interrupt */
+#define STAT_CX 0x8000 /* CU finished cmd with its I bit set */
+#define STAT_FR 0x4000 /* RU finished receiving a frame */
+#define STAT_CNA 0x2000 /* CU left active state */
+#define STAT_RNR 0x1000 /* RU left ready state */
+
+#define CU_STATUS 0x700 /* CU status, 0=idle */
+#define CU_SUSPEND 0x100 /* CU is suspended */
+#define CU_ACTIVE 0x200 /* CU is active */
+
+#define RU_STATUS 0x70 /* RU status, 0=idle */
+#define RU_SUSPEND 0x10 /* RU suspended */
+#define RU_NOSPACE 0x20 /* RU no resources */
+#define RU_READY 0x40 /* RU is ready */
+
+/*
+ * Receive Frame Descriptor (RFD)
+ */
+struct rfd_struct
+{
+ unsigned short status; /* status word */
+ unsigned short last; /* Bit15,Last Frame on List / Bit14,suspend */
+ unsigned short next; /* linkoffset to next RFD */
+ unsigned short rbd_offset; /* pointeroffset to RBD-buffer */
+ unsigned char dest[6]; /* ethernet-address, destination */
+ unsigned char source[6]; /* ethernet-address, source */
+ unsigned short length; /* 802.3 frame-length */
+ unsigned short zero_dummy; /* dummy */
+};
+
+#define RFD_LAST 0x8000 /* last: last rfd in the list */
+#define RFD_SUSP 0x4000 /* last: suspend RU after */
+#define RFD_ERRMASK 0x0fe1 /* status: errormask */
+#define RFD_MATCHADD 0x0002 /* status: Destinationaddress !matches IA */
+#define RFD_RNR 0x0200 /* status: receiver out of resources */
+
+/*
+ * Receive Buffer Descriptor (RBD)
+ */
+struct rbd_struct
+{
+ unsigned short status; /* status word,number of used bytes in buff */
+ unsigned short next; /* pointeroffset to next RBD */
+ char *buffer; /* receive buffer address pointer */
+ unsigned short size; /* size of this buffer */
+ unsigned short zero_dummy; /* dummy */
+};
+
+#define RBD_LAST 0x8000 /* last buffer */
+#define RBD_USED 0x4000 /* this buffer has data */
+#define RBD_MASK 0x3fff /* size-mask for length */
+
+/*
+ * Statusvalues for Commands/RFD
+ */
+#define STAT_COMPL 0x8000 /* status: frame/command is complete */
+#define STAT_BUSY 0x4000 /* status: frame/command is busy */
+#define STAT_OK 0x2000 /* status: frame/command is ok */
+
+/*
+ * Action-Commands
+ */
+#define CMD_NOP 0x0000 /* NOP */
+#define CMD_IASETUP 0x0001 /* initial address setup command */
+#define CMD_CONFIGURE 0x0002 /* configure command */
+#define CMD_MCSETUP 0x0003 /* MC setup command */
+#define CMD_XMIT 0x0004 /* transmit command */
+#define CMD_TDR 0x0005 /* time domain reflectometer (TDR) command */
+#define CMD_DUMP 0x0006 /* dump command */
+#define CMD_DIAGNOSE 0x0007 /* diagnose command */
+
+/*
+ * Action command bits
+ */
+#define CMD_LAST 0x8000 /* indicates last command in the CBL */
+#define CMD_SUSPEND 0x4000 /* suspend CU after this CB */
+#define CMD_INT 0x2000 /* generate interrupt after execution */
+
+/*
+ * NOP - command
+ */
+struct nop_cmd_struct
+{
+ unsigned short cmd_status; /* status of this command */
+ unsigned short cmd_cmd; /* the command itself (+bits) */
+ unsigned short cmd_link; /* offsetpointer to next command */
+};
+
+/*
+ * IA Setup command
+ */
+struct iasetup_cmd_struct
+{
+ unsigned short cmd_status;
+ unsigned short cmd_cmd;
+ unsigned short cmd_link;
+ unsigned char iaddr[6];
+};
+
+/*
+ * Configure command
+ */
+struct configure_cmd_struct
+{
+ unsigned short cmd_status;
+ unsigned short cmd_cmd;
+ unsigned short cmd_link;
+ unsigned char byte_cnt; /* size of the config-cmd */
+ unsigned char fifo; /* fifo/recv monitor */
+ unsigned char sav_bf; /* save bad frames (bit7=1)*/
+ unsigned char adr_len; /* adr_len(0-2),al_loc(3),pream(4-5),loopbak(6-7)*/
+ unsigned char priority; /* lin_prio(0-2),exp_prio(4-6),bof_metd(7) */
+ unsigned char ifs; /* inter frame spacing */
+ unsigned char time_low; /* slot time low */
+ unsigned char time_high; /* slot time high(0-2) and max. retries(4-7) */
+ unsigned char promisc; /* promisc-mode(0) , et al (1-7) */
+ unsigned char carr_coll; /* carrier(0-3)/collision(4-7) stuff */
+ unsigned char fram_len; /* minimal frame len */
+ unsigned char dummy; /* dummy */
+};
+
+/*
+ * Multicast Setup command
+ */
+struct mcsetup_cmd_struct
+{
+ unsigned short cmd_status;
+ unsigned short cmd_cmd;
+ unsigned short cmd_link;
+ unsigned short mc_cnt; /* number of bytes in the MC-List */
+ unsigned char mc_list[0][6]; /* pointer to 6 bytes entries */
+};
+
+/*
+ * transmit command
+ */
+struct transmit_cmd_struct
+{
+ unsigned short cmd_status;
+ unsigned short cmd_cmd;
+ unsigned short cmd_link;
+ unsigned short tbd_offset; /* pointeroffset to TBD */
+ unsigned char dest[6]; /* destination address of the frame */
+ unsigned short length; /* user defined: 802.3 length / Ether type */
+};
+
+#define TCMD_ERRMASK 0x0fa0
+#define TCMD_MAXCOLLMASK 0x000f
+#define TCMD_MAXCOLL 0x0020
+#define TCMD_HEARTBEAT 0x0040
+#define TCMD_DEFERRED 0x0080
+#define TCMD_UNDERRUN 0x0100
+#define TCMD_LOSTCTS 0x0200
+#define TCMD_NOCARRIER 0x0400
+#define TCMD_LATECOLL 0x0800
+
+struct tdr_cmd_struct
+{
+ unsigned short cmd_status;
+ unsigned short cmd_cmd;
+ unsigned short cmd_link;
+ unsigned short status;
+};
+
+#define TDR_LNK_OK 0x8000 /* No link problem identified */
+#define TDR_XCVR_PRB 0x4000 /* indicates a transceiver problem */
+#define TDR_ET_OPN 0x2000 /* open, no correct termination */
+#define TDR_ET_SRT 0x1000 /* TDR detected a short circuit */
+#define TDR_TIMEMASK 0x07ff /* mask for the time field */
+
+/*
+ * Transmit Buffer Descriptor (TBD)
+ */
+struct tbd_struct
+{
+ unsigned short size; /* size + EOF-Flag(15) */
+ unsigned short next; /* pointeroffset to next TBD */
+ char *buffer; /* pointer to buffer */
+};
+
+#define TBD_LAST 0x8000 /* EOF-Flag, indicates last buffer in list */
+
+/*************************************************************************/
+/*
+Verbatim from the Crynwyr stuff:
+
+ The 3c523 responds with adapter code 0x6042 at slot
+registers xxx0 and xxx1. The setup register is at xxx2 and
+contains the following bits:
+
+0: card enable
+2,1: csr address select
+ 00 = 0300
+ 01 = 1300
+ 10 = 2300
+ 11 = 3300
+4,3: shared memory address select
+ 00 = 0c0000
+ 01 = 0c8000
+ 10 = 0d0000
+ 11 = 0d8000
+5: set to disable on-board thinnet
+7,6: (read-only) shows selected irq
+ 00 = 12
+ 01 = 7
+ 10 = 3
+ 11 = 9
+
+The interrupt-select register is at xxx3 and uses one bit per irq.
+
+0: int 12
+1: int 7
+2: int 3
+3: int 9
+
+ Again, the documentation stresses that the setup register
+should never be written. The interrupt-select register may be
+written with the value corresponding to bits 7.6 in
+the setup register to insure corret setup.
+*/
+
+/* Offsets from the base I/O address. */
+#define ELMC_SA 0 /* first 6 bytes are IEEE network address */
+#define ELMC_CTRL 6 /* control & status register */
+#define ELMC_REVISION 7 /* revision register, first 4 bits only */
+#define ELMC_IO_EXTENT 8
+
+/* these are the bit selects for the port register 2 */
+#define ELMC_STATUS_ENABLED 0x01
+#define ELMC_STATUS_CSR_SELECT 0x06
+#define ELMC_STATUS_MEMORY_SELECT 0x18
+#define ELMC_STATUS_DISABLE_THIN 0x20
+#define ELMC_STATUS_IRQ_SELECT 0xc0
+
+/* this is the card id used in the detection code. You might recognize
+it from @6042.adf */
+#define ELMC_MCA_ID 0x6042
+
+/*
+ The following define the bits for the control & status register
+
+ The bank select registers can be used if more than 16K of memory is
+ on the card. For some stupid reason, bank 3 is the one for the
+ bottom 16K, and the card defaults to bank 0. So we have to set the
+ bank to 3 before the card will even think of operating. To get bank
+ 3, set BS0 and BS1 to high (of course...)
+*/
+#define ELMC_CTRL_BS0 0x01 /* RW bank select */
+#define ELMC_CTRL_BS1 0x02 /* RW bank select */
+#define ELMC_CTRL_INTE 0x04 /* RW interrupt enable, assert high */
+#define ELMC_CTRL_INT 0x08 /* R interrupt active, assert high */
+/*#define ELMC_CTRL_* 0x10*/ /* reserved */
+#define ELMC_CTRL_LBK 0x20 /* RW loopback enable, assert high */
+#define ELMC_CTRL_CA 0x40 /* RW channel attention, assert high */
+#define ELMC_CTRL_RST 0x80 /* RW 82586 reset, assert low */
+
+/* some handy compound bits */
+
+/* normal operation should have bank 3 and RST high, ints enabled */
+#define ELMC_NORMAL (ELMC_CTRL_INTE|ELMC_CTRL_RST|0x3)
+
+#endif /* _3c523_INCLUDE_ */
diff --git a/drivers/net/ethernet/i825xx/3c527.c b/drivers/net/ethernet/i825xx/3c527.c
new file mode 100644
index 00000000000..474b5e71a53
--- /dev/null
+++ b/drivers/net/ethernet/i825xx/3c527.c
@@ -0,0 +1,1661 @@
+/* 3c527.c: 3Com Etherlink/MC32 driver for Linux 2.4 and 2.6.
+ *
+ * (c) Copyright 1998 Red Hat Software Inc
+ * Written by Alan Cox.
+ * Further debugging by Carl Drougge.
+ * Initial SMP support by Felipe W Damasio <felipewd@terra.com.br>
+ * Heavily modified by Richard Procter <rnp@paradise.net.nz>
+ *
+ * Based on skeleton.c written 1993-94 by Donald Becker and ne2.c
+ * (for the MCA stuff) written by Wim Dumon.
+ *
+ * Thanks to 3Com for making this possible by providing me with the
+ * documentation.
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#define DRV_NAME "3c527"
+#define DRV_VERSION "0.7-SMP"
+#define DRV_RELDATE "2003/09/21"
+
+static const char *version =
+DRV_NAME ".c:v" DRV_VERSION " " DRV_RELDATE " Richard Procter <rnp@paradise.net.nz>\n";
+
+/**
+ * DOC: Traps for the unwary
+ *
+ * The diagram (Figure 1-1) and the POS summary disagree with the
+ * "Interrupt Level" section in the manual.
+ *
+ * The manual contradicts itself when describing the minimum number
+ * buffers in the 'configure lists' command.
+ * My card accepts a buffer config of 4/4.
+ *
+ * Setting the SAV BP bit does not save bad packets, but
+ * only enables RX on-card stats collection.
+ *
+ * The documentation in places seems to miss things. In actual fact
+ * I've always eventually found everything is documented, it just
+ * requires careful study.
+ *
+ * DOC: Theory Of Operation
+ *
+ * The 3com 3c527 is a 32bit MCA bus mastering adapter with a large
+ * amount of on board intelligence that housekeeps a somewhat dumber
+ * Intel NIC. For performance we want to keep the transmit queue deep
+ * as the card can transmit packets while fetching others from main
+ * memory by bus master DMA. Transmission and reception are driven by
+ * circular buffer queues.
+ *
+ * The mailboxes can be used for controlling how the card traverses
+ * its buffer rings, but are used only for initial setup in this
+ * implementation. The exec mailbox allows a variety of commands to
+ * be executed. Each command must complete before the next is
+ * executed. Primarily we use the exec mailbox for controlling the
+ * multicast lists. We have to do a certain amount of interesting
+ * hoop jumping as the multicast list changes can occur in interrupt
+ * state when the card has an exec command pending. We defer such
+ * events until the command completion interrupt.
+ *
+ * A copy break scheme (taken from 3c59x.c) is employed whereby
+ * received frames exceeding a configurable length are passed
+ * directly to the higher networking layers without incuring a copy,
+ * in what amounts to a time/space trade-off.
+ *
+ * The card also keeps a large amount of statistical information
+ * on-board. In a perfect world, these could be used safely at no
+ * cost. However, lacking information to the contrary, processing
+ * them without races would involve so much extra complexity as to
+ * make it unworthwhile to do so. In the end, a hybrid SW/HW
+ * implementation was made necessary --- see mc32_update_stats().
+ *
+ * DOC: Notes
+ *
+ * It should be possible to use two or more cards, but at this stage
+ * only by loading two copies of the same module.
+ *
+ * The on-board 82586 NIC has trouble receiving multiple
+ * back-to-back frames and so is likely to drop packets from fast
+ * senders.
+**/
+
+#include <linux/module.h>
+
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/if_ether.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/mca-legacy.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/skbuff.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/wait.h>
+#include <linux/ethtool.h>
+#include <linux/completion.h>
+#include <linux/bitops.h>
+#include <linux/semaphore.h>
+
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#include "3c527.h"
+
+MODULE_LICENSE("GPL");
+
+/*
+ * The name of the card. Is used for messages and in the requests for
+ * io regions, irqs and dma channels
+ */
+static const char* cardname = DRV_NAME;
+
+/* use 0 for production, 1 for verification, >2 for debug */
+#ifndef NET_DEBUG
+#define NET_DEBUG 2
+#endif
+
+static unsigned int mc32_debug = NET_DEBUG;
+
+/* The number of low I/O ports used by the ethercard. */
+#define MC32_IO_EXTENT 8
+
+/* As implemented, values must be a power-of-2 -- 4/8/16/32 */
+#define TX_RING_LEN 32 /* Typically the card supports 37 */
+#define RX_RING_LEN 8 /* " " " */
+
+/* Copy break point, see above for details.
+ * Setting to > 1512 effectively disables this feature. */
+#define RX_COPYBREAK 200 /* Value from 3c59x.c */
+
+/* Issue the 82586 workaround command - this is for "busy lans", but
+ * basically means for all lans now days - has a performance (latency)
+ * cost, but best set. */
+static const int WORKAROUND_82586=1;
+
+/* Pointers to buffers and their on-card records */
+struct mc32_ring_desc
+{
+ volatile struct skb_header *p;
+ struct sk_buff *skb;
+};
+
+/* Information that needs to be kept for each board. */
+struct mc32_local
+{
+ int slot;
+
+ u32 base;
+ volatile struct mc32_mailbox *rx_box;
+ volatile struct mc32_mailbox *tx_box;
+ volatile struct mc32_mailbox *exec_box;
+ volatile struct mc32_stats *stats; /* Start of on-card statistics */
+ u16 tx_chain; /* Transmit list start offset */
+ u16 rx_chain; /* Receive list start offset */
+ u16 tx_len; /* Transmit list count */
+ u16 rx_len; /* Receive list count */
+
+ u16 xceiver_desired_state; /* HALTED or RUNNING */
+ u16 cmd_nonblocking; /* Thread is uninterested in command result */
+ u16 mc_reload_wait; /* A multicast load request is pending */
+ u32 mc_list_valid; /* True when the mclist is set */
+
+ struct mc32_ring_desc tx_ring[TX_RING_LEN]; /* Host Transmit ring */
+ struct mc32_ring_desc rx_ring[RX_RING_LEN]; /* Host Receive ring */
+
+ atomic_t tx_count; /* buffers left */
+ atomic_t tx_ring_head; /* index to tx en-queue end */
+ u16 tx_ring_tail; /* index to tx de-queue end */
+
+ u16 rx_ring_tail; /* index to rx de-queue end */
+
+ struct semaphore cmd_mutex; /* Serialises issuing of execute commands */
+ struct completion execution_cmd; /* Card has completed an execute command */
+ struct completion xceiver_cmd; /* Card has completed a tx or rx command */
+};
+
+/* The station (ethernet) address prefix, used for a sanity check. */
+#define SA_ADDR0 0x02
+#define SA_ADDR1 0x60
+#define SA_ADDR2 0xAC
+
+struct mca_adapters_t {
+ unsigned int id;
+ char *name;
+};
+
+static const struct mca_adapters_t mc32_adapters[] = {
+ { 0x0041, "3COM EtherLink MC/32" },
+ { 0x8EF5, "IBM High Performance Lan Adapter" },
+ { 0x0000, NULL }
+};
+
+
+/* Macros for ring index manipulations */
+static inline u16 next_rx(u16 rx) { return (rx+1)&(RX_RING_LEN-1); };
+static inline u16 prev_rx(u16 rx) { return (rx-1)&(RX_RING_LEN-1); };
+
+static inline u16 next_tx(u16 tx) { return (tx+1)&(TX_RING_LEN-1); };
+
+
+/* Index to functions, as function prototypes. */
+static int mc32_probe1(struct net_device *dev, int ioaddr);
+static int mc32_command(struct net_device *dev, u16 cmd, void *data, int len);
+static int mc32_open(struct net_device *dev);
+static void mc32_timeout(struct net_device *dev);
+static netdev_tx_t mc32_send_packet(struct sk_buff *skb,
+ struct net_device *dev);
+static irqreturn_t mc32_interrupt(int irq, void *dev_id);
+static int mc32_close(struct net_device *dev);
+static struct net_device_stats *mc32_get_stats(struct net_device *dev);
+static void mc32_set_multicast_list(struct net_device *dev);
+static void mc32_reset_multicast_list(struct net_device *dev);
+static const struct ethtool_ops netdev_ethtool_ops;
+
+static void cleanup_card(struct net_device *dev)
+{
+ struct mc32_local *lp = netdev_priv(dev);
+ unsigned slot = lp->slot;
+ mca_mark_as_unused(slot);
+ mca_set_adapter_name(slot, NULL);
+ free_irq(dev->irq, dev);
+ release_region(dev->base_addr, MC32_IO_EXTENT);
+}
+
+/**
+ * mc32_probe - Search for supported boards
+ * @unit: interface number to use
+ *
+ * Because MCA bus is a real bus and we can scan for cards we could do a
+ * single scan for all boards here. Right now we use the passed in device
+ * structure and scan for only one board. This needs fixing for modules
+ * in particular.
+ */
+
+struct net_device *__init mc32_probe(int unit)
+{
+ struct net_device *dev = alloc_etherdev(sizeof(struct mc32_local));
+ static int current_mca_slot = -1;
+ int i;
+ int err;
+
+ if (!dev)
+ return ERR_PTR(-ENOMEM);
+
+ if (unit >= 0)
+ sprintf(dev->name, "eth%d", unit);
+
+ /* Do not check any supplied i/o locations.
+ POS registers usually don't fail :) */
+
+ /* MCA cards have POS registers.
+ Autodetecting MCA cards is extremely simple.
+ Just search for the card. */
+
+ for(i = 0; (mc32_adapters[i].name != NULL); i++) {
+ current_mca_slot =
+ mca_find_unused_adapter(mc32_adapters[i].id, 0);
+
+ if(current_mca_slot != MCA_NOTFOUND) {
+ if(!mc32_probe1(dev, current_mca_slot))
+ {
+ mca_set_adapter_name(current_mca_slot,
+ mc32_adapters[i].name);
+ mca_mark_as_used(current_mca_slot);
+ err = register_netdev(dev);
+ if (err) {
+ cleanup_card(dev);
+ free_netdev(dev);
+ dev = ERR_PTR(err);
+ }
+ return dev;
+ }
+
+ }
+ }
+ free_netdev(dev);
+ return ERR_PTR(-ENODEV);
+}
+
+static const struct net_device_ops netdev_ops = {
+ .ndo_open = mc32_open,
+ .ndo_stop = mc32_close,
+ .ndo_start_xmit = mc32_send_packet,
+ .ndo_get_stats = mc32_get_stats,
+ .ndo_set_rx_mode = mc32_set_multicast_list,
+ .ndo_tx_timeout = mc32_timeout,
+ .ndo_change_mtu = eth_change_mtu,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_validate_addr = eth_validate_addr,
+};
+
+/**
+ * mc32_probe1 - Check a given slot for a board and test the card
+ * @dev: Device structure to fill in
+ * @slot: The MCA bus slot being used by this card
+ *
+ * Decode the slot data and configure the card structures. Having done this we
+ * can reset the card and configure it. The card does a full self test cycle
+ * in firmware so we have to wait for it to return and post us either a
+ * failure case or some addresses we use to find the board internals.
+ */
+
+static int __init mc32_probe1(struct net_device *dev, int slot)
+{
+ static unsigned version_printed;
+ int i, err;
+ u8 POS;
+ u32 base;
+ struct mc32_local *lp = netdev_priv(dev);
+ static const u16 mca_io_bases[] = {
+ 0x7280,0x7290,
+ 0x7680,0x7690,
+ 0x7A80,0x7A90,
+ 0x7E80,0x7E90
+ };
+ static const u32 mca_mem_bases[] = {
+ 0x00C0000,
+ 0x00C4000,
+ 0x00C8000,
+ 0x00CC000,
+ 0x00D0000,
+ 0x00D4000,
+ 0x00D8000,
+ 0x00DC000
+ };
+ static const char * const failures[] = {
+ "Processor instruction",
+ "Processor data bus",
+ "Processor data bus",
+ "Processor data bus",
+ "Adapter bus",
+ "ROM checksum",
+ "Base RAM",
+ "Extended RAM",
+ "82586 internal loopback",
+ "82586 initialisation failure",
+ "Adapter list configuration error"
+ };
+
+ /* Time to play MCA games */
+
+ if (mc32_debug && version_printed++ == 0)
+ pr_debug("%s", version);
+
+ pr_info("%s: %s found in slot %d: ", dev->name, cardname, slot);
+
+ POS = mca_read_stored_pos(slot, 2);
+
+ if(!(POS&1))
+ {
+ pr_cont("disabled.\n");
+ return -ENODEV;
+ }
+
+ /* Fill in the 'dev' fields. */
+ dev->base_addr = mca_io_bases[(POS>>1)&7];
+ dev->mem_start = mca_mem_bases[(POS>>4)&7];
+
+ POS = mca_read_stored_pos(slot, 4);
+ if(!(POS&1))
+ {
+ pr_cont("memory window disabled.\n");
+ return -ENODEV;
+ }
+
+ POS = mca_read_stored_pos(slot, 5);
+
+ i=(POS>>4)&3;
+ if(i==3)
+ {
+ pr_cont("invalid memory window.\n");
+ return -ENODEV;
+ }
+
+ i*=16384;
+ i+=16384;
+
+ dev->mem_end=dev->mem_start + i;
+
+ dev->irq = ((POS>>2)&3)+9;
+
+ if(!request_region(dev->base_addr, MC32_IO_EXTENT, cardname))
+ {
+ pr_cont("io 0x%3lX, which is busy.\n", dev->base_addr);
+ return -EBUSY;
+ }
+
+ pr_cont("io 0x%3lX irq %d mem 0x%lX (%dK)\n",
+ dev->base_addr, dev->irq, dev->mem_start, i/1024);
+
+
+ /* We ought to set the cache line size here.. */
+
+
+ /*
+ * Go PROM browsing
+ */
+
+ /* Retrieve and print the ethernet address. */
+ for (i = 0; i < 6; i++)
+ {
+ mca_write_pos(slot, 6, i+12);
+ mca_write_pos(slot, 7, 0);
+
+ dev->dev_addr[i] = mca_read_pos(slot,3);
+ }
+
+ pr_info("%s: Address %pM ", dev->name, dev->dev_addr);
+
+ mca_write_pos(slot, 6, 0);
+ mca_write_pos(slot, 7, 0);
+
+ POS = mca_read_stored_pos(slot, 4);
+
+ if(POS&2)
+ pr_cont(": BNC port selected.\n");
+ else
+ pr_cont(": AUI port selected.\n");
+
+ POS=inb(dev->base_addr+HOST_CTRL);
+ POS|=HOST_CTRL_ATTN|HOST_CTRL_RESET;
+ POS&=~HOST_CTRL_INTE;
+ outb(POS, dev->base_addr+HOST_CTRL);
+ /* Reset adapter */
+ udelay(100);
+ /* Reset off */
+ POS&=~(HOST_CTRL_ATTN|HOST_CTRL_RESET);
+ outb(POS, dev->base_addr+HOST_CTRL);
+
+ udelay(300);
+
+ /*
+ * Grab the IRQ
+ */
+
+ err = request_irq(dev->irq, mc32_interrupt, IRQF_SHARED, DRV_NAME, dev);
+ if (err) {
+ release_region(dev->base_addr, MC32_IO_EXTENT);
+ pr_err("%s: unable to get IRQ %d.\n", DRV_NAME, dev->irq);
+ goto err_exit_ports;
+ }
+
+ memset(lp, 0, sizeof(struct mc32_local));
+ lp->slot = slot;
+
+ i=0;
+
+ base = inb(dev->base_addr);
+
+ while(base == 0xFF)
+ {
+ i++;
+ if(i == 1000)
+ {
+ pr_err("%s: failed to boot adapter.\n", dev->name);
+ err = -ENODEV;
+ goto err_exit_irq;
+ }
+ udelay(1000);
+ if(inb(dev->base_addr+2)&(1<<5))
+ base = inb(dev->base_addr);
+ }
+
+ if(base>0)
+ {
+ if(base < 0x0C)
+ pr_err("%s: %s%s.\n", dev->name, failures[base-1],
+ base<0x0A?" test failure":"");
+ else
+ pr_err("%s: unknown failure %d.\n", dev->name, base);
+ err = -ENODEV;
+ goto err_exit_irq;
+ }
+
+ base=0;
+ for(i=0;i<4;i++)
+ {
+ int n=0;
+
+ while(!(inb(dev->base_addr+2)&(1<<5)))
+ {
+ n++;
+ udelay(50);
+ if(n>100)
+ {
+ pr_err("%s: mailbox read fail (%d).\n", dev->name, i);
+ err = -ENODEV;
+ goto err_exit_irq;
+ }
+ }
+
+ base|=(inb(dev->base_addr)<<(8*i));
+ }
+
+ lp->exec_box=isa_bus_to_virt(dev->mem_start+base);
+
+ base=lp->exec_box->data[1]<<16|lp->exec_box->data[0];
+
+ lp->base = dev->mem_start+base;
+
+ lp->rx_box=isa_bus_to_virt(lp->base + lp->exec_box->data[2]);
+ lp->tx_box=isa_bus_to_virt(lp->base + lp->exec_box->data[3]);
+
+ lp->stats = isa_bus_to_virt(lp->base + lp->exec_box->data[5]);
+
+ /*
+ * Descriptor chains (card relative)
+ */
+
+ lp->tx_chain = lp->exec_box->data[8]; /* Transmit list start offset */
+ lp->rx_chain = lp->exec_box->data[10]; /* Receive list start offset */
+ lp->tx_len = lp->exec_box->data[9]; /* Transmit list count */
+ lp->rx_len = lp->exec_box->data[11]; /* Receive list count */
+
+ sema_init(&lp->cmd_mutex, 0);
+ init_completion(&lp->execution_cmd);
+ init_completion(&lp->xceiver_cmd);
+
+ pr_info("%s: Firmware Rev %d. %d RX buffers, %d TX buffers. Base of 0x%08X.\n",
+ dev->name, lp->exec_box->data[12], lp->rx_len, lp->tx_len, lp->base);
+
+ dev->netdev_ops = &netdev_ops;
+ dev->watchdog_timeo = HZ*5; /* Board does all the work */
+ dev->ethtool_ops = &netdev_ethtool_ops;
+
+ return 0;
+
+err_exit_irq:
+ free_irq(dev->irq, dev);
+err_exit_ports:
+ release_region(dev->base_addr, MC32_IO_EXTENT);
+ return err;
+}
+
+
+/**
+ * mc32_ready_poll - wait until we can feed it a command
+ * @dev: The device to wait for
+ *
+ * Wait until the card becomes ready to accept a command via the
+ * command register. This tells us nothing about the completion
+ * status of any pending commands and takes very little time at all.
+ */
+
+static inline void mc32_ready_poll(struct net_device *dev)
+{
+ int ioaddr = dev->base_addr;
+ while(!(inb(ioaddr+HOST_STATUS)&HOST_STATUS_CRR));
+}
+
+
+/**
+ * mc32_command_nowait - send a command non blocking
+ * @dev: The 3c527 to issue the command to
+ * @cmd: The command word to write to the mailbox
+ * @data: A data block if the command expects one
+ * @len: Length of the data block
+ *
+ * Send a command from interrupt state. If there is a command
+ * currently being executed then we return an error of -1. It
+ * simply isn't viable to wait around as commands may be
+ * slow. This can theoretically be starved on SMP, but it's hard
+ * to see a realistic situation. We do not wait for the command
+ * to complete --- we rely on the interrupt handler to tidy up
+ * after us.
+ */
+
+static int mc32_command_nowait(struct net_device *dev, u16 cmd, void *data, int len)
+{
+ struct mc32_local *lp = netdev_priv(dev);
+ int ioaddr = dev->base_addr;
+ int ret = -1;
+
+ if (down_trylock(&lp->cmd_mutex) == 0)
+ {
+ lp->cmd_nonblocking=1;
+ lp->exec_box->mbox=0;
+ lp->exec_box->mbox=cmd;
+ memcpy((void *)lp->exec_box->data, data, len);
+ barrier(); /* the memcpy forgot the volatile so be sure */
+
+ /* Send the command */
+ mc32_ready_poll(dev);
+ outb(1<<6, ioaddr+HOST_CMD);
+
+ ret = 0;
+
+ /* Interrupt handler will signal mutex on completion */
+ }
+
+ return ret;
+}
+
+
+/**
+ * mc32_command - send a command and sleep until completion
+ * @dev: The 3c527 card to issue the command to
+ * @cmd: The command word to write to the mailbox
+ * @data: A data block if the command expects one
+ * @len: Length of the data block
+ *
+ * Sends exec commands in a user context. This permits us to wait around
+ * for the replies and also to wait for the command buffer to complete
+ * from a previous command before we execute our command. After our
+ * command completes we will attempt any pending multicast reload
+ * we blocked off by hogging the exec buffer.
+ *
+ * You feed the card a command, you wait, it interrupts you get a
+ * reply. All well and good. The complication arises because you use
+ * commands for filter list changes which come in at bh level from things
+ * like IPV6 group stuff.
+ */
+
+static int mc32_command(struct net_device *dev, u16 cmd, void *data, int len)
+{
+ struct mc32_local *lp = netdev_priv(dev);
+ int ioaddr = dev->base_addr;
+ int ret = 0;
+
+ down(&lp->cmd_mutex);
+
+ /*
+ * My Turn
+ */
+
+ lp->cmd_nonblocking=0;
+ lp->exec_box->mbox=0;
+ lp->exec_box->mbox=cmd;
+ memcpy((void *)lp->exec_box->data, data, len);
+ barrier(); /* the memcpy forgot the volatile so be sure */
+
+ mc32_ready_poll(dev);
+ outb(1<<6, ioaddr+HOST_CMD);
+
+ wait_for_completion(&lp->execution_cmd);
+
+ if(lp->exec_box->mbox&(1<<13))
+ ret = -1;
+
+ up(&lp->cmd_mutex);
+
+ /*
+ * A multicast set got blocked - try it now
+ */
+
+ if(lp->mc_reload_wait)
+ {
+ mc32_reset_multicast_list(dev);
+ }
+
+ return ret;
+}
+
+
+/**
+ * mc32_start_transceiver - tell board to restart tx/rx
+ * @dev: The 3c527 card to issue the command to
+ *
+ * This may be called from the interrupt state, where it is used
+ * to restart the rx ring if the card runs out of rx buffers.
+ *
+ * We must first check if it's ok to (re)start the transceiver. See
+ * mc32_close for details.
+ */
+
+static void mc32_start_transceiver(struct net_device *dev) {
+
+ struct mc32_local *lp = netdev_priv(dev);
+ int ioaddr = dev->base_addr;
+
+ /* Ignore RX overflow on device closure */
+ if (lp->xceiver_desired_state==HALTED)
+ return;
+
+ /* Give the card the offset to the post-EOL-bit RX descriptor */
+ mc32_ready_poll(dev);
+ lp->rx_box->mbox=0;
+ lp->rx_box->data[0]=lp->rx_ring[prev_rx(lp->rx_ring_tail)].p->next;
+ outb(HOST_CMD_START_RX, ioaddr+HOST_CMD);
+
+ mc32_ready_poll(dev);
+ lp->tx_box->mbox=0;
+ outb(HOST_CMD_RESTRT_TX, ioaddr+HOST_CMD); /* card ignores this on RX restart */
+
+ /* We are not interrupted on start completion */
+}
+
+
+/**
+ * mc32_halt_transceiver - tell board to stop tx/rx
+ * @dev: The 3c527 card to issue the command to
+ *
+ * We issue the commands to halt the card's transceiver. In fact,
+ * after some experimenting we now simply tell the card to
+ * suspend. When issuing aborts occasionally odd things happened.
+ *
+ * We then sleep until the card has notified us that both rx and
+ * tx have been suspended.
+ */
+
+static void mc32_halt_transceiver(struct net_device *dev)
+{
+ struct mc32_local *lp = netdev_priv(dev);
+ int ioaddr = dev->base_addr;
+
+ mc32_ready_poll(dev);
+ lp->rx_box->mbox=0;
+ outb(HOST_CMD_SUSPND_RX, ioaddr+HOST_CMD);
+ wait_for_completion(&lp->xceiver_cmd);
+
+ mc32_ready_poll(dev);
+ lp->tx_box->mbox=0;
+ outb(HOST_CMD_SUSPND_TX, ioaddr+HOST_CMD);
+ wait_for_completion(&lp->xceiver_cmd);
+}
+
+
+/**
+ * mc32_load_rx_ring - load the ring of receive buffers
+ * @dev: 3c527 to build the ring for
+ *
+ * This initialises the on-card and driver datastructures to
+ * the point where mc32_start_transceiver() can be called.
+ *
+ * The card sets up the receive ring for us. We are required to use the
+ * ring it provides, although the size of the ring is configurable.
+ *
+ * We allocate an sk_buff for each ring entry in turn and
+ * initialise its house-keeping info. At the same time, we read
+ * each 'next' pointer in our rx_ring array. This reduces slow
+ * shared-memory reads and makes it easy to access predecessor
+ * descriptors.
+ *
+ * We then set the end-of-list bit for the last entry so that the
+ * card will know when it has run out of buffers.
+ */
+
+static int mc32_load_rx_ring(struct net_device *dev)
+{
+ struct mc32_local *lp = netdev_priv(dev);
+ int i;
+ u16 rx_base;
+ volatile struct skb_header *p;
+
+ rx_base=lp->rx_chain;
+
+ for(i=0; i<RX_RING_LEN; i++) {
+ lp->rx_ring[i].skb=alloc_skb(1532, GFP_KERNEL);
+ if (lp->rx_ring[i].skb==NULL) {
+ for (;i>=0;i--)
+ kfree_skb(lp->rx_ring[i].skb);
+ return -ENOBUFS;
+ }
+ skb_reserve(lp->rx_ring[i].skb, 18);
+
+ p=isa_bus_to_virt(lp->base+rx_base);
+
+ p->control=0;
+ p->data=isa_virt_to_bus(lp->rx_ring[i].skb->data);
+ p->status=0;
+ p->length=1532;
+
+ lp->rx_ring[i].p=p;
+ rx_base=p->next;
+ }
+
+ lp->rx_ring[i-1].p->control |= CONTROL_EOL;
+
+ lp->rx_ring_tail=0;
+
+ return 0;
+}
+
+
+/**
+ * mc32_flush_rx_ring - free the ring of receive buffers
+ * @lp: Local data of 3c527 to flush the rx ring of
+ *
+ * Free the buffer for each ring slot. This may be called
+ * before mc32_load_rx_ring(), eg. on error in mc32_open().
+ * Requires rx skb pointers to point to a valid skb, or NULL.
+ */
+
+static void mc32_flush_rx_ring(struct net_device *dev)
+{
+ struct mc32_local *lp = netdev_priv(dev);
+ int i;
+
+ for(i=0; i < RX_RING_LEN; i++)
+ {
+ if (lp->rx_ring[i].skb) {
+ dev_kfree_skb(lp->rx_ring[i].skb);
+ lp->rx_ring[i].skb = NULL;
+ }
+ lp->rx_ring[i].p=NULL;
+ }
+}
+
+
+/**
+ * mc32_load_tx_ring - load transmit ring
+ * @dev: The 3c527 card to issue the command to
+ *
+ * This sets up the host transmit data-structures.
+ *
+ * First, we obtain from the card it's current position in the tx
+ * ring, so that we will know where to begin transmitting
+ * packets.
+ *
+ * Then, we read the 'next' pointers from the on-card tx ring into
+ * our tx_ring array to reduce slow shared-mem reads. Finally, we
+ * intitalise the tx house keeping variables.
+ *
+ */
+
+static void mc32_load_tx_ring(struct net_device *dev)
+{
+ struct mc32_local *lp = netdev_priv(dev);
+ volatile struct skb_header *p;
+ int i;
+ u16 tx_base;
+
+ tx_base=lp->tx_box->data[0];
+
+ for(i=0 ; i<TX_RING_LEN ; i++)
+ {
+ p=isa_bus_to_virt(lp->base+tx_base);
+ lp->tx_ring[i].p=p;
+ lp->tx_ring[i].skb=NULL;
+
+ tx_base=p->next;
+ }
+
+ /* -1 so that tx_ring_head cannot "lap" tx_ring_tail */
+ /* see mc32_tx_ring */
+
+ atomic_set(&lp->tx_count, TX_RING_LEN-1);
+ atomic_set(&lp->tx_ring_head, 0);
+ lp->tx_ring_tail=0;
+}
+
+
+/**
+ * mc32_flush_tx_ring - free transmit ring
+ * @lp: Local data of 3c527 to flush the tx ring of
+ *
+ * If the ring is non-empty, zip over the it, freeing any
+ * allocated skb_buffs. The tx ring house-keeping variables are
+ * then reset. Requires rx skb pointers to point to a valid skb,
+ * or NULL.
+ */
+
+static void mc32_flush_tx_ring(struct net_device *dev)
+{
+ struct mc32_local *lp = netdev_priv(dev);
+ int i;
+
+ for (i=0; i < TX_RING_LEN; i++)
+ {
+ if (lp->tx_ring[i].skb)
+ {
+ dev_kfree_skb(lp->tx_ring[i].skb);
+ lp->tx_ring[i].skb = NULL;
+ }
+ }
+
+ atomic_set(&lp->tx_count, 0);
+ atomic_set(&lp->tx_ring_head, 0);
+ lp->tx_ring_tail=0;
+}
+
+
+/**
+ * mc32_open - handle 'up' of card
+ * @dev: device to open
+ *
+ * The user is trying to bring the card into ready state. This requires
+ * a brief dialogue with the card. Firstly we enable interrupts and then
+ * 'indications'. Without these enabled the card doesn't bother telling
+ * us what it has done. This had me puzzled for a week.
+ *
+ * We configure the number of card descriptors, then load the network
+ * address and multicast filters. Turn on the workaround mode. This
+ * works around a bug in the 82586 - it asks the firmware to do
+ * so. It has a performance (latency) hit but is needed on busy
+ * [read most] lans. We load the ring with buffers then we kick it
+ * all off.
+ */
+
+static int mc32_open(struct net_device *dev)
+{
+ int ioaddr = dev->base_addr;
+ struct mc32_local *lp = netdev_priv(dev);
+ u8 one=1;
+ u8 regs;
+ u16 descnumbuffs[2] = {TX_RING_LEN, RX_RING_LEN};
+
+ /*
+ * Interrupts enabled
+ */
+
+ regs=inb(ioaddr+HOST_CTRL);
+ regs|=HOST_CTRL_INTE;
+ outb(regs, ioaddr+HOST_CTRL);
+
+ /*
+ * Allow ourselves to issue commands
+ */
+
+ up(&lp->cmd_mutex);
+
+
+ /*
+ * Send the indications on command
+ */
+
+ mc32_command(dev, 4, &one, 2);
+
+ /*
+ * Poke it to make sure it's really dead.
+ */
+
+ mc32_halt_transceiver(dev);
+ mc32_flush_tx_ring(dev);
+
+ /*
+ * Ask card to set up on-card descriptors to our spec
+ */
+
+ if(mc32_command(dev, 8, descnumbuffs, 4)) {
+ pr_info("%s: %s rejected our buffer configuration!\n",
+ dev->name, cardname);
+ mc32_close(dev);
+ return -ENOBUFS;
+ }
+
+ /* Report new configuration */
+ mc32_command(dev, 6, NULL, 0);
+
+ lp->tx_chain = lp->exec_box->data[8]; /* Transmit list start offset */
+ lp->rx_chain = lp->exec_box->data[10]; /* Receive list start offset */
+ lp->tx_len = lp->exec_box->data[9]; /* Transmit list count */
+ lp->rx_len = lp->exec_box->data[11]; /* Receive list count */
+
+ /* Set Network Address */
+ mc32_command(dev, 1, dev->dev_addr, 6);
+
+ /* Set the filters */
+ mc32_set_multicast_list(dev);
+
+ if (WORKAROUND_82586) {
+ u16 zero_word=0;
+ mc32_command(dev, 0x0D, &zero_word, 2); /* 82586 bug workaround on */
+ }
+
+ mc32_load_tx_ring(dev);
+
+ if(mc32_load_rx_ring(dev))
+ {
+ mc32_close(dev);
+ return -ENOBUFS;
+ }
+
+ lp->xceiver_desired_state = RUNNING;
+
+ /* And finally, set the ball rolling... */
+ mc32_start_transceiver(dev);
+
+ netif_start_queue(dev);
+
+ return 0;
+}
+
+
+/**
+ * mc32_timeout - handle a timeout from the network layer
+ * @dev: 3c527 that timed out
+ *
+ * Handle a timeout on transmit from the 3c527. This normally means
+ * bad things as the hardware handles cable timeouts and mess for
+ * us.
+ *
+ */
+
+static void mc32_timeout(struct net_device *dev)
+{
+ pr_warning("%s: transmit timed out?\n", dev->name);
+ /* Try to restart the adaptor. */
+ netif_wake_queue(dev);
+}
+
+
+/**
+ * mc32_send_packet - queue a frame for transmit
+ * @skb: buffer to transmit
+ * @dev: 3c527 to send it out of
+ *
+ * Transmit a buffer. This normally means throwing the buffer onto
+ * the transmit queue as the queue is quite large. If the queue is
+ * full then we set tx_busy and return. Once the interrupt handler
+ * gets messages telling it to reclaim transmit queue entries, we will
+ * clear tx_busy and the kernel will start calling this again.
+ *
+ * We do not disable interrupts or acquire any locks; this can
+ * run concurrently with mc32_tx_ring(), and the function itself
+ * is serialised at a higher layer. However, similarly for the
+ * card itself, we must ensure that we update tx_ring_head only
+ * after we've established a valid packet on the tx ring (and
+ * before we let the card "see" it, to prevent it racing with the
+ * irq handler).
+ *
+ */
+
+static netdev_tx_t mc32_send_packet(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ struct mc32_local *lp = netdev_priv(dev);
+ u32 head = atomic_read(&lp->tx_ring_head);
+
+ volatile struct skb_header *p, *np;
+
+ netif_stop_queue(dev);
+
+ if(atomic_read(&lp->tx_count)==0) {
+ return NETDEV_TX_BUSY;
+ }
+
+ if (skb_padto(skb, ETH_ZLEN)) {
+ netif_wake_queue(dev);
+ return NETDEV_TX_OK;
+ }
+
+ atomic_dec(&lp->tx_count);
+
+ /* P is the last sending/sent buffer as a pointer */
+ p=lp->tx_ring[head].p;
+
+ head = next_tx(head);
+
+ /* NP is the buffer we will be loading */
+ np=lp->tx_ring[head].p;
+
+ /* We will need this to flush the buffer out */
+ lp->tx_ring[head].skb=skb;
+
+ np->length = unlikely(skb->len < ETH_ZLEN) ? ETH_ZLEN : skb->len;
+ np->data = isa_virt_to_bus(skb->data);
+ np->status = 0;
+ np->control = CONTROL_EOP | CONTROL_EOL;
+ wmb();
+
+ /*
+ * The new frame has been setup; we can now
+ * let the interrupt handler and card "see" it
+ */
+
+ atomic_set(&lp->tx_ring_head, head);
+ p->control &= ~CONTROL_EOL;
+
+ netif_wake_queue(dev);
+ return NETDEV_TX_OK;
+}
+
+
+/**
+ * mc32_update_stats - pull off the on board statistics
+ * @dev: 3c527 to service
+ *
+ *
+ * Query and reset the on-card stats. There's the small possibility
+ * of a race here, which would result in an underestimation of
+ * actual errors. As such, we'd prefer to keep all our stats
+ * collection in software. As a rule, we do. However it can't be
+ * used for rx errors and collisions as, by default, the card discards
+ * bad rx packets.
+ *
+ * Setting the SAV BP in the rx filter command supposedly
+ * stops this behaviour. However, testing shows that it only seems to
+ * enable the collation of on-card rx statistics --- the driver
+ * never sees an RX descriptor with an error status set.
+ *
+ */
+
+static void mc32_update_stats(struct net_device *dev)
+{
+ struct mc32_local *lp = netdev_priv(dev);
+ volatile struct mc32_stats *st = lp->stats;
+
+ u32 rx_errors=0;
+
+ rx_errors+=dev->stats.rx_crc_errors +=st->rx_crc_errors;
+ st->rx_crc_errors=0;
+ rx_errors+=dev->stats.rx_fifo_errors +=st->rx_overrun_errors;
+ st->rx_overrun_errors=0;
+ rx_errors+=dev->stats.rx_frame_errors +=st->rx_alignment_errors;
+ st->rx_alignment_errors=0;
+ rx_errors+=dev->stats.rx_length_errors+=st->rx_tooshort_errors;
+ st->rx_tooshort_errors=0;
+ rx_errors+=dev->stats.rx_missed_errors+=st->rx_outofresource_errors;
+ st->rx_outofresource_errors=0;
+ dev->stats.rx_errors=rx_errors;
+
+ /* Number of packets which saw one collision */
+ dev->stats.collisions+=st->dataC[10];
+ st->dataC[10]=0;
+
+ /* Number of packets which saw 2--15 collisions */
+ dev->stats.collisions+=st->dataC[11];
+ st->dataC[11]=0;
+}
+
+
+/**
+ * mc32_rx_ring - process the receive ring
+ * @dev: 3c527 that needs its receive ring processing
+ *
+ *
+ * We have received one or more indications from the card that a
+ * receive has completed. The buffer ring thus contains dirty
+ * entries. We walk the ring by iterating over the circular rx_ring
+ * array, starting at the next dirty buffer (which happens to be the
+ * one we finished up at last time around).
+ *
+ * For each completed packet, we will either copy it and pass it up
+ * the stack or, if the packet is near MTU sized, we allocate
+ * another buffer and flip the old one up the stack.
+ *
+ * We must succeed in keeping a buffer on the ring. If necessary we
+ * will toss a received packet rather than lose a ring entry. Once
+ * the first uncompleted descriptor is found, we move the
+ * End-Of-List bit to include the buffers just processed.
+ *
+ */
+
+static void mc32_rx_ring(struct net_device *dev)
+{
+ struct mc32_local *lp = netdev_priv(dev);
+ volatile struct skb_header *p;
+ u16 rx_ring_tail;
+ u16 rx_old_tail;
+ int x=0;
+
+ rx_old_tail = rx_ring_tail = lp->rx_ring_tail;
+
+ do
+ {
+ p=lp->rx_ring[rx_ring_tail].p;
+
+ if(!(p->status & (1<<7))) { /* Not COMPLETED */
+ break;
+ }
+ if(p->status & (1<<6)) /* COMPLETED_OK */
+ {
+
+ u16 length=p->length;
+ struct sk_buff *skb;
+ struct sk_buff *newskb;
+
+ /* Try to save time by avoiding a copy on big frames */
+
+ if ((length > RX_COPYBREAK) &&
+ ((newskb=dev_alloc_skb(1532)) != NULL))
+ {
+ skb=lp->rx_ring[rx_ring_tail].skb;
+ skb_put(skb, length);
+
+ skb_reserve(newskb,18);
+ lp->rx_ring[rx_ring_tail].skb=newskb;
+ p->data=isa_virt_to_bus(newskb->data);
+ }
+ else
+ {
+ skb=dev_alloc_skb(length+2);
+
+ if(skb==NULL) {
+ dev->stats.rx_dropped++;
+ goto dropped;
+ }
+
+ skb_reserve(skb,2);
+ memcpy(skb_put(skb, length),
+ lp->rx_ring[rx_ring_tail].skb->data, length);
+ }
+
+ skb->protocol=eth_type_trans(skb,dev);
+ dev->stats.rx_packets++;
+ dev->stats.rx_bytes += length;
+ netif_rx(skb);
+ }
+
+ dropped:
+ p->length = 1532;
+ p->status = 0;
+
+ rx_ring_tail=next_rx(rx_ring_tail);
+ }
+ while(x++<48);
+
+ /* If there was actually a frame to be processed, place the EOL bit */
+ /* at the descriptor prior to the one to be filled next */
+
+ if (rx_ring_tail != rx_old_tail)
+ {
+ lp->rx_ring[prev_rx(rx_ring_tail)].p->control |= CONTROL_EOL;
+ lp->rx_ring[prev_rx(rx_old_tail)].p->control &= ~CONTROL_EOL;
+
+ lp->rx_ring_tail=rx_ring_tail;
+ }
+}
+
+
+/**
+ * mc32_tx_ring - process completed transmits
+ * @dev: 3c527 that needs its transmit ring processing
+ *
+ *
+ * This operates in a similar fashion to mc32_rx_ring. We iterate
+ * over the transmit ring. For each descriptor which has been
+ * processed by the card, we free its associated buffer and note
+ * any errors. This continues until the transmit ring is emptied
+ * or we reach a descriptor that hasn't yet been processed by the
+ * card.
+ *
+ */
+
+static void mc32_tx_ring(struct net_device *dev)
+{
+ struct mc32_local *lp = netdev_priv(dev);
+ volatile struct skb_header *np;
+
+ /*
+ * We rely on head==tail to mean 'queue empty'.
+ * This is why lp->tx_count=TX_RING_LEN-1: in order to prevent
+ * tx_ring_head wrapping to tail and confusing a 'queue empty'
+ * condition with 'queue full'
+ */
+
+ while (lp->tx_ring_tail != atomic_read(&lp->tx_ring_head))
+ {
+ u16 t;
+
+ t=next_tx(lp->tx_ring_tail);
+ np=lp->tx_ring[t].p;
+
+ if(!(np->status & (1<<7)))
+ {
+ /* Not COMPLETED */
+ break;
+ }
+ dev->stats.tx_packets++;
+ if(!(np->status & (1<<6))) /* Not COMPLETED_OK */
+ {
+ dev->stats.tx_errors++;
+
+ switch(np->status&0x0F)
+ {
+ case 1:
+ dev->stats.tx_aborted_errors++;
+ break; /* Max collisions */
+ case 2:
+ dev->stats.tx_fifo_errors++;
+ break;
+ case 3:
+ dev->stats.tx_carrier_errors++;
+ break;
+ case 4:
+ dev->stats.tx_window_errors++;
+ break; /* CTS Lost */
+ case 5:
+ dev->stats.tx_aborted_errors++;
+ break; /* Transmit timeout */
+ }
+ }
+ /* Packets are sent in order - this is
+ basically a FIFO queue of buffers matching
+ the card ring */
+ dev->stats.tx_bytes+=lp->tx_ring[t].skb->len;
+ dev_kfree_skb_irq(lp->tx_ring[t].skb);
+ lp->tx_ring[t].skb=NULL;
+ atomic_inc(&lp->tx_count);
+ netif_wake_queue(dev);
+
+ lp->tx_ring_tail=t;
+ }
+
+}
+
+
+/**
+ * mc32_interrupt - handle an interrupt from a 3c527
+ * @irq: Interrupt number
+ * @dev_id: 3c527 that requires servicing
+ * @regs: Registers (unused)
+ *
+ *
+ * An interrupt is raised whenever the 3c527 writes to the command
+ * register. This register contains the message it wishes to send us
+ * packed into a single byte field. We keep reading status entries
+ * until we have processed all the control items, but simply count
+ * transmit and receive reports. When all reports are in we empty the
+ * transceiver rings as appropriate. This saves the overhead of
+ * multiple command requests.
+ *
+ * Because MCA is level-triggered, we shouldn't miss indications.
+ * Therefore, we needn't ask the card to suspend interrupts within
+ * this handler. The card receives an implicit acknowledgment of the
+ * current interrupt when we read the command register.
+ *
+ */
+
+static irqreturn_t mc32_interrupt(int irq, void *dev_id)
+{
+ struct net_device *dev = dev_id;
+ struct mc32_local *lp;
+ int ioaddr, status, boguscount = 0;
+ int rx_event = 0;
+ int tx_event = 0;
+
+ ioaddr = dev->base_addr;
+ lp = netdev_priv(dev);
+
+ /* See whats cooking */
+
+ while((inb(ioaddr+HOST_STATUS)&HOST_STATUS_CWR) && boguscount++<2000)
+ {
+ status=inb(ioaddr+HOST_CMD);
+
+ pr_debug("Status TX%d RX%d EX%d OV%d BC%d\n",
+ (status&7), (status>>3)&7, (status>>6)&1,
+ (status>>7)&1, boguscount);
+
+ switch(status&7)
+ {
+ case 0:
+ break;
+ case 6: /* TX fail */
+ case 2: /* TX ok */
+ tx_event = 1;
+ break;
+ case 3: /* Halt */
+ case 4: /* Abort */
+ complete(&lp->xceiver_cmd);
+ break;
+ default:
+ pr_notice("%s: strange tx ack %d\n", dev->name, status&7);
+ }
+ status>>=3;
+ switch(status&7)
+ {
+ case 0:
+ break;
+ case 2: /* RX */
+ rx_event=1;
+ break;
+ case 3: /* Halt */
+ case 4: /* Abort */
+ complete(&lp->xceiver_cmd);
+ break;
+ case 6:
+ /* Out of RX buffers stat */
+ /* Must restart rx */
+ dev->stats.rx_dropped++;
+ mc32_rx_ring(dev);
+ mc32_start_transceiver(dev);
+ break;
+ default:
+ pr_notice("%s: strange rx ack %d\n",
+ dev->name, status&7);
+ }
+ status>>=3;
+ if(status&1)
+ {
+ /*
+ * No thread is waiting: we need to tidy
+ * up ourself.
+ */
+
+ if (lp->cmd_nonblocking) {
+ up(&lp->cmd_mutex);
+ if (lp->mc_reload_wait)
+ mc32_reset_multicast_list(dev);
+ }
+ else complete(&lp->execution_cmd);
+ }
+ if(status&2)
+ {
+ /*
+ * We get interrupted once per
+ * counter that is about to overflow.
+ */
+
+ mc32_update_stats(dev);
+ }
+ }
+
+
+ /*
+ * Process the transmit and receive rings
+ */
+
+ if(tx_event)
+ mc32_tx_ring(dev);
+
+ if(rx_event)
+ mc32_rx_ring(dev);
+
+ return IRQ_HANDLED;
+}
+
+
+/**
+ * mc32_close - user configuring the 3c527 down
+ * @dev: 3c527 card to shut down
+ *
+ * The 3c527 is a bus mastering device. We must be careful how we
+ * shut it down. It may also be running shared interrupt so we have
+ * to be sure to silence it properly
+ *
+ * We indicate that the card is closing to the rest of the
+ * driver. Otherwise, it is possible that the card may run out
+ * of receive buffers and restart the transceiver while we're
+ * trying to close it.
+ *
+ * We abort any receive and transmits going on and then wait until
+ * any pending exec commands have completed in other code threads.
+ * In theory we can't get here while that is true, in practice I am
+ * paranoid
+ *
+ * We turn off the interrupt enable for the board to be sure it can't
+ * intefere with other devices.
+ */
+
+static int mc32_close(struct net_device *dev)
+{
+ struct mc32_local *lp = netdev_priv(dev);
+ int ioaddr = dev->base_addr;
+
+ u8 regs;
+ u16 one=1;
+
+ lp->xceiver_desired_state = HALTED;
+ netif_stop_queue(dev);
+
+ /*
+ * Send the indications on command (handy debug check)
+ */
+
+ mc32_command(dev, 4, &one, 2);
+
+ /* Shut down the transceiver */
+
+ mc32_halt_transceiver(dev);
+
+ /* Ensure we issue no more commands beyond this point */
+
+ down(&lp->cmd_mutex);
+
+ /* Ok the card is now stopping */
+
+ regs=inb(ioaddr+HOST_CTRL);
+ regs&=~HOST_CTRL_INTE;
+ outb(regs, ioaddr+HOST_CTRL);
+
+ mc32_flush_rx_ring(dev);
+ mc32_flush_tx_ring(dev);
+
+ mc32_update_stats(dev);
+
+ return 0;
+}
+
+
+/**
+ * mc32_get_stats - hand back stats to network layer
+ * @dev: The 3c527 card to handle
+ *
+ * We've collected all the stats we can in software already. Now
+ * it's time to update those kept on-card and return the lot.
+ *
+ */
+
+static struct net_device_stats *mc32_get_stats(struct net_device *dev)
+{
+ mc32_update_stats(dev);
+ return &dev->stats;
+}
+
+
+/**
+ * do_mc32_set_multicast_list - attempt to update multicasts
+ * @dev: 3c527 device to load the list on
+ * @retry: indicates this is not the first call.
+ *
+ *
+ * Actually set or clear the multicast filter for this adaptor. The
+ * locking issues are handled by this routine. We have to track
+ * state as it may take multiple calls to get the command sequence
+ * completed. We just keep trying to schedule the loads until we
+ * manage to process them all.
+ *
+ * num_addrs == -1 Promiscuous mode, receive all packets
+ *
+ * num_addrs == 0 Normal mode, clear multicast list
+ *
+ * num_addrs > 0 Multicast mode, receive normal and MC packets,
+ * and do best-effort filtering.
+ *
+ * See mc32_update_stats() regards setting the SAV BP bit.
+ *
+ */
+
+static void do_mc32_set_multicast_list(struct net_device *dev, int retry)
+{
+ struct mc32_local *lp = netdev_priv(dev);
+ u16 filt = (1<<2); /* Save Bad Packets, for stats purposes */
+
+ if ((dev->flags&IFF_PROMISC) ||
+ (dev->flags&IFF_ALLMULTI) ||
+ netdev_mc_count(dev) > 10)
+ /* Enable promiscuous mode */
+ filt |= 1;
+ else if (!netdev_mc_empty(dev))
+ {
+ unsigned char block[62];
+ unsigned char *bp;
+ struct netdev_hw_addr *ha;
+
+ if(retry==0)
+ lp->mc_list_valid = 0;
+ if(!lp->mc_list_valid)
+ {
+ block[1]=0;
+ block[0]=netdev_mc_count(dev);
+ bp=block+2;
+
+ netdev_for_each_mc_addr(ha, dev) {
+ memcpy(bp, ha->addr, 6);
+ bp+=6;
+ }
+ if(mc32_command_nowait(dev, 2, block,
+ 2+6*netdev_mc_count(dev))==-1)
+ {
+ lp->mc_reload_wait = 1;
+ return;
+ }
+ lp->mc_list_valid=1;
+ }
+ }
+
+ if(mc32_command_nowait(dev, 0, &filt, 2)==-1)
+ {
+ lp->mc_reload_wait = 1;
+ }
+ else {
+ lp->mc_reload_wait = 0;
+ }
+}
+
+
+/**
+ * mc32_set_multicast_list - queue multicast list update
+ * @dev: The 3c527 to use
+ *
+ * Commence loading the multicast list. This is called when the kernel
+ * changes the lists. It will override any pending list we are trying to
+ * load.
+ */
+
+static void mc32_set_multicast_list(struct net_device *dev)
+{
+ do_mc32_set_multicast_list(dev,0);
+}
+
+
+/**
+ * mc32_reset_multicast_list - reset multicast list
+ * @dev: The 3c527 to use
+ *
+ * Attempt the next step in loading the multicast lists. If this attempt
+ * fails to complete then it will be scheduled and this function called
+ * again later from elsewhere.
+ */
+
+static void mc32_reset_multicast_list(struct net_device *dev)
+{
+ do_mc32_set_multicast_list(dev,1);
+}
+
+static void netdev_get_drvinfo(struct net_device *dev,
+ struct ethtool_drvinfo *info)
+{
+ strcpy(info->driver, DRV_NAME);
+ strcpy(info->version, DRV_VERSION);
+ sprintf(info->bus_info, "MCA 0x%lx", dev->base_addr);
+}
+
+static u32 netdev_get_msglevel(struct net_device *dev)
+{
+ return mc32_debug;
+}
+
+static void netdev_set_msglevel(struct net_device *dev, u32 level)
+{
+ mc32_debug = level;
+}
+
+static const struct ethtool_ops netdev_ethtool_ops = {
+ .get_drvinfo = netdev_get_drvinfo,
+ .get_msglevel = netdev_get_msglevel,
+ .set_msglevel = netdev_set_msglevel,
+};
+
+#ifdef MODULE
+
+static struct net_device *this_device;
+
+/**
+ * init_module - entry point
+ *
+ * Probe and locate a 3c527 card. This really should probe and locate
+ * all the 3c527 cards in the machine not just one of them. Yes you can
+ * insmod multiple modules for now but it's a hack.
+ */
+
+int __init init_module(void)
+{
+ this_device = mc32_probe(-1);
+ if (IS_ERR(this_device))
+ return PTR_ERR(this_device);
+ return 0;
+}
+
+/**
+ * cleanup_module - free resources for an unload
+ *
+ * Unloading time. We release the MCA bus resources and the interrupt
+ * at which point everything is ready to unload. The card must be stopped
+ * at this point or we would not have been called. When we unload we
+ * leave the card stopped but not totally shut down. When the card is
+ * initialized it must be rebooted or the rings reloaded before any
+ * transmit operations are allowed to start scribbling into memory.
+ */
+
+void __exit cleanup_module(void)
+{
+ unregister_netdev(this_device);
+ cleanup_card(this_device);
+ free_netdev(this_device);
+}
+
+#endif /* MODULE */
diff --git a/drivers/net/ethernet/i825xx/3c527.h b/drivers/net/ethernet/i825xx/3c527.h
new file mode 100644
index 00000000000..d693b8d15cd
--- /dev/null
+++ b/drivers/net/ethernet/i825xx/3c527.h
@@ -0,0 +1,81 @@
+/*
+ * 3COM "EtherLink MC/32" Descriptions
+ */
+
+/*
+ * Registers
+ */
+
+#define HOST_CMD 0
+#define HOST_CMD_START_RX (1<<3)
+#define HOST_CMD_SUSPND_RX (3<<3)
+#define HOST_CMD_RESTRT_RX (5<<3)
+
+#define HOST_CMD_SUSPND_TX 3
+#define HOST_CMD_RESTRT_TX 5
+
+
+#define HOST_STATUS 2
+#define HOST_STATUS_CRR (1<<6)
+#define HOST_STATUS_CWR (1<<5)
+
+
+#define HOST_CTRL 6
+#define HOST_CTRL_ATTN (1<<7)
+#define HOST_CTRL_RESET (1<<6)
+#define HOST_CTRL_INTE (1<<2)
+
+#define HOST_RAMPAGE 8
+
+#define HALTED 0
+#define RUNNING 1
+
+struct mc32_mailbox
+{
+ u16 mbox;
+ u16 data[1];
+} __packed;
+
+struct skb_header
+{
+ u8 status;
+ u8 control;
+ u16 next; /* Do not change! */
+ u16 length;
+ u32 data;
+} __packed;
+
+struct mc32_stats
+{
+ /* RX Errors */
+ u32 rx_crc_errors;
+ u32 rx_alignment_errors;
+ u32 rx_overrun_errors;
+ u32 rx_tooshort_errors;
+ u32 rx_toolong_errors;
+ u32 rx_outofresource_errors;
+
+ u32 rx_discarded; /* via card pattern match filter */
+
+ /* TX Errors */
+ u32 tx_max_collisions;
+ u32 tx_carrier_errors;
+ u32 tx_underrun_errors;
+ u32 tx_cts_errors;
+ u32 tx_timeout_errors;
+
+ /* various cruft */
+ u32 dataA[6];
+ u16 dataB[5];
+ u32 dataC[14];
+} __packed;
+
+#define STATUS_MASK 0x0F
+#define COMPLETED (1<<7)
+#define COMPLETED_OK (1<<6)
+#define BUFFER_BUSY (1<<5)
+
+#define CONTROL_EOP (1<<7) /* End Of Packet */
+#define CONTROL_EOL (1<<6) /* End of List */
+
+#define MCA_MC32_ID 0x0041 /* Our MCA ident */
diff --git a/drivers/net/ethernet/i825xx/82596.c b/drivers/net/ethernet/i825xx/82596.c
new file mode 100644
index 00000000000..f2408a4d5d9
--- /dev/null
+++ b/drivers/net/ethernet/i825xx/82596.c
@@ -0,0 +1,1632 @@
+/* 82596.c: A generic 82596 ethernet driver for linux. */
+/*
+ Based on Apricot.c
+ Written 1994 by Mark Evans.
+ This driver is for the Apricot 82596 bus-master interface
+
+ Modularised 12/94 Mark Evans
+
+
+ Modified to support the 82596 ethernet chips on 680x0 VME boards.
+ by Richard Hirst <richard@sleepie.demon.co.uk>
+ Renamed to be 82596.c
+
+ 980825: Changed to receive directly in to sk_buffs which are
+ allocated at open() time. Eliminates copy on incoming frames
+ (small ones are still copied). Shared data now held in a
+ non-cached page, so we can run on 68060 in copyback mode.
+
+ TBD:
+ * look at deferring rx frames rather than discarding (as per tulip)
+ * handle tx ring full as per tulip
+ * performance test to tune rx_copybreak
+
+ Most of my modifications relate to the braindead big-endian
+ implementation by Intel. When the i596 is operating in
+ 'big-endian' mode, it thinks a 32 bit value of 0x12345678
+ should be stored as 0x56781234. This is a real pain, when
+ you have linked lists which are shared by the 680x0 and the
+ i596.
+
+ Driver skeleton
+ Written 1993 by Donald Becker.
+ Copyright 1993 United States Government as represented by the Director,
+ National Security Agency. This software may only be used and distributed
+ according to the terms of the GNU General Public License as modified by SRC,
+ incorporated herein by reference.
+
+ The author may be reached as becker@scyld.com, or C/O
+ Scyld Computing Corporation, 410 Severn Ave., Suite 210, Annapolis MD 21403
+
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/init.h>
+#include <linux/bitops.h>
+#include <linux/gfp.h>
+
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/pgtable.h>
+#include <asm/cacheflush.h>
+
+static char version[] __initdata =
+ "82596.c $Revision: 1.5 $\n";
+
+#define DRV_NAME "82596"
+
+/* DEBUG flags
+ */
+
+#define DEB_INIT 0x0001
+#define DEB_PROBE 0x0002
+#define DEB_SERIOUS 0x0004
+#define DEB_ERRORS 0x0008
+#define DEB_MULTI 0x0010
+#define DEB_TDR 0x0020
+#define DEB_OPEN 0x0040
+#define DEB_RESET 0x0080
+#define DEB_ADDCMD 0x0100
+#define DEB_STATUS 0x0200
+#define DEB_STARTTX 0x0400
+#define DEB_RXADDR 0x0800
+#define DEB_TXADDR 0x1000
+#define DEB_RXFRAME 0x2000
+#define DEB_INTS 0x4000
+#define DEB_STRUCT 0x8000
+#define DEB_ANY 0xffff
+
+
+#define DEB(x,y) if (i596_debug & (x)) y
+
+
+#if defined(CONFIG_MVME16x_NET) || defined(CONFIG_MVME16x_NET_MODULE)
+#define ENABLE_MVME16x_NET
+#endif
+#if defined(CONFIG_BVME6000_NET) || defined(CONFIG_BVME6000_NET_MODULE)
+#define ENABLE_BVME6000_NET
+#endif
+#if defined(CONFIG_APRICOT) || defined(CONFIG_APRICOT_MODULE)
+#define ENABLE_APRICOT
+#endif
+
+#ifdef ENABLE_MVME16x_NET
+#include <asm/mvme16xhw.h>
+#endif
+#ifdef ENABLE_BVME6000_NET
+#include <asm/bvme6000hw.h>
+#endif
+
+/*
+ * Define various macros for Channel Attention, word swapping etc., dependent
+ * on architecture. MVME and BVME are 680x0 based, otherwise it is Intel.
+ */
+
+#ifdef __mc68000__
+#define WSWAPrfd(x) ((struct i596_rfd *) (((u32)(x)<<16) | ((((u32)(x)))>>16)))
+#define WSWAPrbd(x) ((struct i596_rbd *) (((u32)(x)<<16) | ((((u32)(x)))>>16)))
+#define WSWAPiscp(x) ((struct i596_iscp *)(((u32)(x)<<16) | ((((u32)(x)))>>16)))
+#define WSWAPscb(x) ((struct i596_scb *) (((u32)(x)<<16) | ((((u32)(x)))>>16)))
+#define WSWAPcmd(x) ((struct i596_cmd *) (((u32)(x)<<16) | ((((u32)(x)))>>16)))
+#define WSWAPtbd(x) ((struct i596_tbd *) (((u32)(x)<<16) | ((((u32)(x)))>>16)))
+#define WSWAPchar(x) ((char *) (((u32)(x)<<16) | ((((u32)(x)))>>16)))
+#define ISCP_BUSY 0x00010000
+#define MACH_IS_APRICOT 0
+#else
+#define WSWAPrfd(x) ((struct i596_rfd *)((long)x))
+#define WSWAPrbd(x) ((struct i596_rbd *)((long)x))
+#define WSWAPiscp(x) ((struct i596_iscp *)((long)x))
+#define WSWAPscb(x) ((struct i596_scb *)((long)x))
+#define WSWAPcmd(x) ((struct i596_cmd *)((long)x))
+#define WSWAPtbd(x) ((struct i596_tbd *)((long)x))
+#define WSWAPchar(x) ((char *)((long)x))
+#define ISCP_BUSY 0x0001
+#define MACH_IS_APRICOT 1
+#endif
+
+/*
+ * The MPU_PORT command allows direct access to the 82596. With PORT access
+ * the following commands are available (p5-18). The 32-bit port command
+ * must be word-swapped with the most significant word written first.
+ * This only applies to VME boards.
+ */
+#define PORT_RESET 0x00 /* reset 82596 */
+#define PORT_SELFTEST 0x01 /* selftest */
+#define PORT_ALTSCP 0x02 /* alternate SCB address */
+#define PORT_ALTDUMP 0x03 /* Alternate DUMP address */
+
+static int i596_debug = (DEB_SERIOUS|DEB_PROBE);
+
+MODULE_AUTHOR("Richard Hirst");
+MODULE_DESCRIPTION("i82596 driver");
+MODULE_LICENSE("GPL");
+
+module_param(i596_debug, int, 0);
+MODULE_PARM_DESC(i596_debug, "i82596 debug mask");
+
+
+/* Copy frames shorter than rx_copybreak, otherwise pass on up in
+ * a full sized sk_buff. Value of 100 stolen from tulip.c (!alpha).
+ */
+static int rx_copybreak = 100;
+
+#define PKT_BUF_SZ 1536
+#define MAX_MC_CNT 64
+
+#define I596_TOTAL_SIZE 17
+
+#define I596_NULL ((void *)0xffffffff)
+
+#define CMD_EOL 0x8000 /* The last command of the list, stop. */
+#define CMD_SUSP 0x4000 /* Suspend after doing cmd. */
+#define CMD_INTR 0x2000 /* Interrupt after doing cmd. */
+
+#define CMD_FLEX 0x0008 /* Enable flexible memory model */
+
+enum commands {
+ CmdNOp = 0, CmdSASetup = 1, CmdConfigure = 2, CmdMulticastList = 3,
+ CmdTx = 4, CmdTDR = 5, CmdDump = 6, CmdDiagnose = 7
+};
+
+#define STAT_C 0x8000 /* Set to 0 after execution */
+#define STAT_B 0x4000 /* Command being executed */
+#define STAT_OK 0x2000 /* Command executed ok */
+#define STAT_A 0x1000 /* Command aborted */
+
+#define CUC_START 0x0100
+#define CUC_RESUME 0x0200
+#define CUC_SUSPEND 0x0300
+#define CUC_ABORT 0x0400
+#define RX_START 0x0010
+#define RX_RESUME 0x0020
+#define RX_SUSPEND 0x0030
+#define RX_ABORT 0x0040
+
+#define TX_TIMEOUT (HZ/20)
+
+
+struct i596_reg {
+ unsigned short porthi;
+ unsigned short portlo;
+ unsigned long ca;
+};
+
+#define EOF 0x8000
+#define SIZE_MASK 0x3fff
+
+struct i596_tbd {
+ unsigned short size;
+ unsigned short pad;
+ struct i596_tbd *next;
+ char *data;
+};
+
+/* The command structure has two 'next' pointers; v_next is the address of
+ * the next command as seen by the CPU, b_next is the address of the next
+ * command as seen by the 82596. The b_next pointer, as used by the 82596
+ * always references the status field of the next command, rather than the
+ * v_next field, because the 82596 is unaware of v_next. It may seem more
+ * logical to put v_next at the end of the structure, but we cannot do that
+ * because the 82596 expects other fields to be there, depending on command
+ * type.
+ */
+
+struct i596_cmd {
+ struct i596_cmd *v_next; /* Address from CPUs viewpoint */
+ unsigned short status;
+ unsigned short command;
+ struct i596_cmd *b_next; /* Address from i596 viewpoint */
+};
+
+struct tx_cmd {
+ struct i596_cmd cmd;
+ struct i596_tbd *tbd;
+ unsigned short size;
+ unsigned short pad;
+ struct sk_buff *skb; /* So we can free it after tx */
+};
+
+struct tdr_cmd {
+ struct i596_cmd cmd;
+ unsigned short status;
+ unsigned short pad;
+};
+
+struct mc_cmd {
+ struct i596_cmd cmd;
+ short mc_cnt;
+ char mc_addrs[MAX_MC_CNT*6];
+};
+
+struct sa_cmd {
+ struct i596_cmd cmd;
+ char eth_addr[8];
+};
+
+struct cf_cmd {
+ struct i596_cmd cmd;
+ char i596_config[16];
+};
+
+struct i596_rfd {
+ unsigned short stat;
+ unsigned short cmd;
+ struct i596_rfd *b_next; /* Address from i596 viewpoint */
+ struct i596_rbd *rbd;
+ unsigned short count;
+ unsigned short size;
+ struct i596_rfd *v_next; /* Address from CPUs viewpoint */
+ struct i596_rfd *v_prev;
+};
+
+struct i596_rbd {
+ unsigned short count;
+ unsigned short zero1;
+ struct i596_rbd *b_next;
+ unsigned char *b_data; /* Address from i596 viewpoint */
+ unsigned short size;
+ unsigned short zero2;
+ struct sk_buff *skb;
+ struct i596_rbd *v_next;
+ struct i596_rbd *b_addr; /* This rbd addr from i596 view */
+ unsigned char *v_data; /* Address from CPUs viewpoint */
+};
+
+#define TX_RING_SIZE 64
+#define RX_RING_SIZE 16
+
+struct i596_scb {
+ unsigned short status;
+ unsigned short command;
+ struct i596_cmd *cmd;
+ struct i596_rfd *rfd;
+ unsigned long crc_err;
+ unsigned long align_err;
+ unsigned long resource_err;
+ unsigned long over_err;
+ unsigned long rcvdt_err;
+ unsigned long short_err;
+ unsigned short t_on;
+ unsigned short t_off;
+};
+
+struct i596_iscp {
+ unsigned long stat;
+ struct i596_scb *scb;
+};
+
+struct i596_scp {
+ unsigned long sysbus;
+ unsigned long pad;
+ struct i596_iscp *iscp;
+};
+
+struct i596_private {
+ volatile struct i596_scp scp;
+ volatile struct i596_iscp iscp;
+ volatile struct i596_scb scb;
+ struct sa_cmd sa_cmd;
+ struct cf_cmd cf_cmd;
+ struct tdr_cmd tdr_cmd;
+ struct mc_cmd mc_cmd;
+ unsigned long stat;
+ int last_restart __attribute__((aligned(4)));
+ struct i596_rfd *rfd_head;
+ struct i596_rbd *rbd_head;
+ struct i596_cmd *cmd_tail;
+ struct i596_cmd *cmd_head;
+ int cmd_backlog;
+ unsigned long last_cmd;
+ struct i596_rfd rfds[RX_RING_SIZE];
+ struct i596_rbd rbds[RX_RING_SIZE];
+ struct tx_cmd tx_cmds[TX_RING_SIZE];
+ struct i596_tbd tbds[TX_RING_SIZE];
+ int next_tx_cmd;
+ spinlock_t lock;
+};
+
+static char init_setup[] =
+{
+ 0x8E, /* length, prefetch on */
+ 0xC8, /* fifo to 8, monitor off */
+#ifdef CONFIG_VME
+ 0xc0, /* don't save bad frames */
+#else
+ 0x80, /* don't save bad frames */
+#endif
+ 0x2E, /* No source address insertion, 8 byte preamble */
+ 0x00, /* priority and backoff defaults */
+ 0x60, /* interframe spacing */
+ 0x00, /* slot time LSB */
+ 0xf2, /* slot time and retries */
+ 0x00, /* promiscuous mode */
+ 0x00, /* collision detect */
+ 0x40, /* minimum frame length */
+ 0xff,
+ 0x00,
+ 0x7f /* *multi IA */ };
+
+static int i596_open(struct net_device *dev);
+static netdev_tx_t i596_start_xmit(struct sk_buff *skb, struct net_device *dev);
+static irqreturn_t i596_interrupt(int irq, void *dev_id);
+static int i596_close(struct net_device *dev);
+static void i596_add_cmd(struct net_device *dev, struct i596_cmd *cmd);
+static void i596_tx_timeout (struct net_device *dev);
+static void print_eth(unsigned char *buf, char *str);
+static void set_multicast_list(struct net_device *dev);
+
+static int rx_ring_size = RX_RING_SIZE;
+static int ticks_limit = 25;
+static int max_cmd_backlog = TX_RING_SIZE-1;
+
+
+static inline void CA(struct net_device *dev)
+{
+#ifdef ENABLE_MVME16x_NET
+ if (MACH_IS_MVME16x) {
+ ((struct i596_reg *) dev->base_addr)->ca = 1;
+ }
+#endif
+#ifdef ENABLE_BVME6000_NET
+ if (MACH_IS_BVME6000) {
+ volatile u32 i;
+
+ i = *(volatile u32 *) (dev->base_addr);
+ }
+#endif
+#ifdef ENABLE_APRICOT
+ if (MACH_IS_APRICOT) {
+ outw(0, (short) (dev->base_addr) + 4);
+ }
+#endif
+}
+
+
+static inline void MPU_PORT(struct net_device *dev, int c, volatile void *x)
+{
+#ifdef ENABLE_MVME16x_NET
+ if (MACH_IS_MVME16x) {
+ struct i596_reg *p = (struct i596_reg *) (dev->base_addr);
+ p->porthi = ((c) | (u32) (x)) & 0xffff;
+ p->portlo = ((c) | (u32) (x)) >> 16;
+ }
+#endif
+#ifdef ENABLE_BVME6000_NET
+ if (MACH_IS_BVME6000) {
+ u32 v = (u32) (c) | (u32) (x);
+ v = ((u32) (v) << 16) | ((u32) (v) >> 16);
+ *(volatile u32 *) dev->base_addr = v;
+ udelay(1);
+ *(volatile u32 *) dev->base_addr = v;
+ }
+#endif
+}
+
+
+static inline int wait_istat(struct net_device *dev, struct i596_private *lp, int delcnt, char *str)
+{
+ while (--delcnt && lp->iscp.stat)
+ udelay(10);
+ if (!delcnt) {
+ printk(KERN_ERR "%s: %s, status %4.4x, cmd %4.4x.\n",
+ dev->name, str, lp->scb.status, lp->scb.command);
+ return -1;
+ }
+ else
+ return 0;
+}
+
+
+static inline int wait_cmd(struct net_device *dev, struct i596_private *lp, int delcnt, char *str)
+{
+ while (--delcnt && lp->scb.command)
+ udelay(10);
+ if (!delcnt) {
+ printk(KERN_ERR "%s: %s, status %4.4x, cmd %4.4x.\n",
+ dev->name, str, lp->scb.status, lp->scb.command);
+ return -1;
+ }
+ else
+ return 0;
+}
+
+
+static inline int wait_cfg(struct net_device *dev, struct i596_cmd *cmd, int delcnt, char *str)
+{
+ volatile struct i596_cmd *c = cmd;
+
+ while (--delcnt && c->command)
+ udelay(10);
+ if (!delcnt) {
+ printk(KERN_ERR "%s: %s.\n", dev->name, str);
+ return -1;
+ }
+ else
+ return 0;
+}
+
+
+static void i596_display_data(struct net_device *dev)
+{
+ struct i596_private *lp = dev->ml_priv;
+ struct i596_cmd *cmd;
+ struct i596_rfd *rfd;
+ struct i596_rbd *rbd;
+
+ printk(KERN_ERR "lp and scp at %p, .sysbus = %08lx, .iscp = %p\n",
+ &lp->scp, lp->scp.sysbus, lp->scp.iscp);
+ printk(KERN_ERR "iscp at %p, iscp.stat = %08lx, .scb = %p\n",
+ &lp->iscp, lp->iscp.stat, lp->iscp.scb);
+ printk(KERN_ERR "scb at %p, scb.status = %04x, .command = %04x,"
+ " .cmd = %p, .rfd = %p\n",
+ &lp->scb, lp->scb.status, lp->scb.command,
+ lp->scb.cmd, lp->scb.rfd);
+ printk(KERN_ERR " errors: crc %lx, align %lx, resource %lx,"
+ " over %lx, rcvdt %lx, short %lx\n",
+ lp->scb.crc_err, lp->scb.align_err, lp->scb.resource_err,
+ lp->scb.over_err, lp->scb.rcvdt_err, lp->scb.short_err);
+ cmd = lp->cmd_head;
+ while (cmd != I596_NULL) {
+ printk(KERN_ERR "cmd at %p, .status = %04x, .command = %04x, .b_next = %p\n",
+ cmd, cmd->status, cmd->command, cmd->b_next);
+ cmd = cmd->v_next;
+ }
+ rfd = lp->rfd_head;
+ printk(KERN_ERR "rfd_head = %p\n", rfd);
+ do {
+ printk(KERN_ERR " %p .stat %04x, .cmd %04x, b_next %p, rbd %p,"
+ " count %04x\n",
+ rfd, rfd->stat, rfd->cmd, rfd->b_next, rfd->rbd,
+ rfd->count);
+ rfd = rfd->v_next;
+ } while (rfd != lp->rfd_head);
+ rbd = lp->rbd_head;
+ printk(KERN_ERR "rbd_head = %p\n", rbd);
+ do {
+ printk(KERN_ERR " %p .count %04x, b_next %p, b_data %p, size %04x\n",
+ rbd, rbd->count, rbd->b_next, rbd->b_data, rbd->size);
+ rbd = rbd->v_next;
+ } while (rbd != lp->rbd_head);
+}
+
+
+#if defined(ENABLE_MVME16x_NET) || defined(ENABLE_BVME6000_NET)
+static irqreturn_t i596_error(int irq, void *dev_id)
+{
+ struct net_device *dev = dev_id;
+#ifdef ENABLE_MVME16x_NET
+ if (MACH_IS_MVME16x) {
+ volatile unsigned char *pcc2 = (unsigned char *) 0xfff42000;
+
+ pcc2[0x28] = 1;
+ pcc2[0x2b] = 0x1d;
+ }
+#endif
+#ifdef ENABLE_BVME6000_NET
+ if (MACH_IS_BVME6000) {
+ volatile unsigned char *ethirq = (unsigned char *) BVME_ETHIRQ_REG;
+
+ *ethirq = 1;
+ *ethirq = 3;
+ }
+#endif
+ printk(KERN_ERR "%s: Error interrupt\n", dev->name);
+ i596_display_data(dev);
+ return IRQ_HANDLED;
+}
+#endif
+
+static inline void remove_rx_bufs(struct net_device *dev)
+{
+ struct i596_private *lp = dev->ml_priv;
+ struct i596_rbd *rbd;
+ int i;
+
+ for (i = 0, rbd = lp->rbds; i < rx_ring_size; i++, rbd++) {
+ if (rbd->skb == NULL)
+ break;
+ dev_kfree_skb(rbd->skb);
+ rbd->skb = NULL;
+ }
+}
+
+static inline int init_rx_bufs(struct net_device *dev)
+{
+ struct i596_private *lp = dev->ml_priv;
+ int i;
+ struct i596_rfd *rfd;
+ struct i596_rbd *rbd;
+
+ /* First build the Receive Buffer Descriptor List */
+
+ for (i = 0, rbd = lp->rbds; i < rx_ring_size; i++, rbd++) {
+ struct sk_buff *skb = dev_alloc_skb(PKT_BUF_SZ);
+
+ if (skb == NULL) {
+ remove_rx_bufs(dev);
+ return -ENOMEM;
+ }
+
+ skb->dev = dev;
+ rbd->v_next = rbd+1;
+ rbd->b_next = WSWAPrbd(virt_to_bus(rbd+1));
+ rbd->b_addr = WSWAPrbd(virt_to_bus(rbd));
+ rbd->skb = skb;
+ rbd->v_data = skb->data;
+ rbd->b_data = WSWAPchar(virt_to_bus(skb->data));
+ rbd->size = PKT_BUF_SZ;
+#ifdef __mc68000__
+ cache_clear(virt_to_phys(skb->data), PKT_BUF_SZ);
+#endif
+ }
+ lp->rbd_head = lp->rbds;
+ rbd = lp->rbds + rx_ring_size - 1;
+ rbd->v_next = lp->rbds;
+ rbd->b_next = WSWAPrbd(virt_to_bus(lp->rbds));
+
+ /* Now build the Receive Frame Descriptor List */
+
+ for (i = 0, rfd = lp->rfds; i < rx_ring_size; i++, rfd++) {
+ rfd->rbd = I596_NULL;
+ rfd->v_next = rfd+1;
+ rfd->v_prev = rfd-1;
+ rfd->b_next = WSWAPrfd(virt_to_bus(rfd+1));
+ rfd->cmd = CMD_FLEX;
+ }
+ lp->rfd_head = lp->rfds;
+ lp->scb.rfd = WSWAPrfd(virt_to_bus(lp->rfds));
+ rfd = lp->rfds;
+ rfd->rbd = lp->rbd_head;
+ rfd->v_prev = lp->rfds + rx_ring_size - 1;
+ rfd = lp->rfds + rx_ring_size - 1;
+ rfd->v_next = lp->rfds;
+ rfd->b_next = WSWAPrfd(virt_to_bus(lp->rfds));
+ rfd->cmd = CMD_EOL|CMD_FLEX;
+
+ return 0;
+}
+
+
+static void rebuild_rx_bufs(struct net_device *dev)
+{
+ struct i596_private *lp = dev->ml_priv;
+ int i;
+
+ /* Ensure rx frame/buffer descriptors are tidy */
+
+ for (i = 0; i < rx_ring_size; i++) {
+ lp->rfds[i].rbd = I596_NULL;
+ lp->rfds[i].cmd = CMD_FLEX;
+ }
+ lp->rfds[rx_ring_size-1].cmd = CMD_EOL|CMD_FLEX;
+ lp->rfd_head = lp->rfds;
+ lp->scb.rfd = WSWAPrfd(virt_to_bus(lp->rfds));
+ lp->rbd_head = lp->rbds;
+ lp->rfds[0].rbd = WSWAPrbd(virt_to_bus(lp->rbds));
+}
+
+
+static int init_i596_mem(struct net_device *dev)
+{
+ struct i596_private *lp = dev->ml_priv;
+#if !defined(ENABLE_MVME16x_NET) && !defined(ENABLE_BVME6000_NET) || defined(ENABLE_APRICOT)
+ short ioaddr = dev->base_addr;
+#endif
+ unsigned long flags;
+
+ MPU_PORT(dev, PORT_RESET, NULL);
+
+ udelay(100); /* Wait 100us - seems to help */
+
+#if defined(ENABLE_MVME16x_NET) || defined(ENABLE_BVME6000_NET)
+#ifdef ENABLE_MVME16x_NET
+ if (MACH_IS_MVME16x) {
+ volatile unsigned char *pcc2 = (unsigned char *) 0xfff42000;
+
+ /* Disable all ints for now */
+ pcc2[0x28] = 1;
+ pcc2[0x2a] = 0x48;
+ /* Following disables snooping. Snooping is not required
+ * as we make appropriate use of non-cached pages for
+ * shared data, and cache_push/cache_clear.
+ */
+ pcc2[0x2b] = 0x08;
+ }
+#endif
+#ifdef ENABLE_BVME6000_NET
+ if (MACH_IS_BVME6000) {
+ volatile unsigned char *ethirq = (unsigned char *) BVME_ETHIRQ_REG;
+
+ *ethirq = 1;
+ }
+#endif
+
+ /* change the scp address */
+
+ MPU_PORT(dev, PORT_ALTSCP, (void *)virt_to_bus((void *)&lp->scp));
+
+#elif defined(ENABLE_APRICOT)
+
+ {
+ u32 scp = virt_to_bus(&lp->scp);
+
+ /* change the scp address */
+ outw(0, ioaddr);
+ outw(0, ioaddr);
+ outb(4, ioaddr + 0xf);
+ outw(scp | 2, ioaddr);
+ outw(scp >> 16, ioaddr);
+ }
+#endif
+
+ lp->last_cmd = jiffies;
+
+#ifdef ENABLE_MVME16x_NET
+ if (MACH_IS_MVME16x)
+ lp->scp.sysbus = 0x00000054;
+#endif
+#ifdef ENABLE_BVME6000_NET
+ if (MACH_IS_BVME6000)
+ lp->scp.sysbus = 0x0000004c;
+#endif
+#ifdef ENABLE_APRICOT
+ if (MACH_IS_APRICOT)
+ lp->scp.sysbus = 0x00440000;
+#endif
+
+ lp->scp.iscp = WSWAPiscp(virt_to_bus((void *)&lp->iscp));
+ lp->iscp.scb = WSWAPscb(virt_to_bus((void *)&lp->scb));
+ lp->iscp.stat = ISCP_BUSY;
+ lp->cmd_backlog = 0;
+
+ lp->cmd_head = lp->scb.cmd = I596_NULL;
+
+#ifdef ENABLE_BVME6000_NET
+ if (MACH_IS_BVME6000) {
+ lp->scb.t_on = 7 * 25;
+ lp->scb.t_off = 1 * 25;
+ }
+#endif
+
+ DEB(DEB_INIT,printk(KERN_DEBUG "%s: starting i82596.\n", dev->name));
+
+#if defined(ENABLE_APRICOT)
+ (void) inb(ioaddr + 0x10);
+ outb(4, ioaddr + 0xf);
+#endif
+ CA(dev);
+
+ if (wait_istat(dev,lp,1000,"initialization timed out"))
+ goto failed;
+ DEB(DEB_INIT,printk(KERN_DEBUG "%s: i82596 initialization successful\n", dev->name));
+
+ /* Ensure rx frame/buffer descriptors are tidy */
+ rebuild_rx_bufs(dev);
+ lp->scb.command = 0;
+
+#ifdef ENABLE_MVME16x_NET
+ if (MACH_IS_MVME16x) {
+ volatile unsigned char *pcc2 = (unsigned char *) 0xfff42000;
+
+ /* Enable ints, etc. now */
+ pcc2[0x2a] = 0x55; /* Edge sensitive */
+ pcc2[0x2b] = 0x15;
+ }
+#endif
+#ifdef ENABLE_BVME6000_NET
+ if (MACH_IS_BVME6000) {
+ volatile unsigned char *ethirq = (unsigned char *) BVME_ETHIRQ_REG;
+
+ *ethirq = 3;
+ }
+#endif
+
+
+ DEB(DEB_INIT,printk(KERN_DEBUG "%s: queuing CmdConfigure\n", dev->name));
+ memcpy(lp->cf_cmd.i596_config, init_setup, 14);
+ lp->cf_cmd.cmd.command = CmdConfigure;
+ i596_add_cmd(dev, &lp->cf_cmd.cmd);
+
+ DEB(DEB_INIT,printk(KERN_DEBUG "%s: queuing CmdSASetup\n", dev->name));
+ memcpy(lp->sa_cmd.eth_addr, dev->dev_addr, 6);
+ lp->sa_cmd.cmd.command = CmdSASetup;
+ i596_add_cmd(dev, &lp->sa_cmd.cmd);
+
+ DEB(DEB_INIT,printk(KERN_DEBUG "%s: queuing CmdTDR\n", dev->name));
+ lp->tdr_cmd.cmd.command = CmdTDR;
+ i596_add_cmd(dev, &lp->tdr_cmd.cmd);
+
+ spin_lock_irqsave (&lp->lock, flags);
+
+ if (wait_cmd(dev,lp,1000,"timed out waiting to issue RX_START")) {
+ spin_unlock_irqrestore (&lp->lock, flags);
+ goto failed;
+ }
+ DEB(DEB_INIT,printk(KERN_DEBUG "%s: Issuing RX_START\n", dev->name));
+ lp->scb.command = RX_START;
+ CA(dev);
+
+ spin_unlock_irqrestore (&lp->lock, flags);
+
+ if (wait_cmd(dev,lp,1000,"RX_START not processed"))
+ goto failed;
+ DEB(DEB_INIT,printk(KERN_DEBUG "%s: Receive unit started OK\n", dev->name));
+ return 0;
+
+failed:
+ printk(KERN_CRIT "%s: Failed to initialise 82596\n", dev->name);
+ MPU_PORT(dev, PORT_RESET, NULL);
+ return -1;
+}
+
+static inline int i596_rx(struct net_device *dev)
+{
+ struct i596_private *lp = dev->ml_priv;
+ struct i596_rfd *rfd;
+ struct i596_rbd *rbd;
+ int frames = 0;
+
+ DEB(DEB_RXFRAME,printk(KERN_DEBUG "i596_rx(), rfd_head %p, rbd_head %p\n",
+ lp->rfd_head, lp->rbd_head));
+
+ rfd = lp->rfd_head; /* Ref next frame to check */
+
+ while ((rfd->stat) & STAT_C) { /* Loop while complete frames */
+ if (rfd->rbd == I596_NULL)
+ rbd = I596_NULL;
+ else if (rfd->rbd == lp->rbd_head->b_addr)
+ rbd = lp->rbd_head;
+ else {
+ printk(KERN_CRIT "%s: rbd chain broken!\n", dev->name);
+ /* XXX Now what? */
+ rbd = I596_NULL;
+ }
+ DEB(DEB_RXFRAME, printk(KERN_DEBUG " rfd %p, rfd.rbd %p, rfd.stat %04x\n",
+ rfd, rfd->rbd, rfd->stat));
+
+ if (rbd != I596_NULL && ((rfd->stat) & STAT_OK)) {
+ /* a good frame */
+ int pkt_len = rbd->count & 0x3fff;
+ struct sk_buff *skb = rbd->skb;
+ int rx_in_place = 0;
+
+ DEB(DEB_RXADDR,print_eth(rbd->v_data, "received"));
+ frames++;
+
+ /* Check if the packet is long enough to just accept
+ * without copying to a properly sized skbuff.
+ */
+
+ if (pkt_len > rx_copybreak) {
+ struct sk_buff *newskb;
+
+ /* Get fresh skbuff to replace filled one. */
+ newskb = dev_alloc_skb(PKT_BUF_SZ);
+ if (newskb == NULL) {
+ skb = NULL; /* drop pkt */
+ goto memory_squeeze;
+ }
+ /* Pass up the skb already on the Rx ring. */
+ skb_put(skb, pkt_len);
+ rx_in_place = 1;
+ rbd->skb = newskb;
+ newskb->dev = dev;
+ rbd->v_data = newskb->data;
+ rbd->b_data = WSWAPchar(virt_to_bus(newskb->data));
+#ifdef __mc68000__
+ cache_clear(virt_to_phys(newskb->data), PKT_BUF_SZ);
+#endif
+ }
+ else
+ skb = dev_alloc_skb(pkt_len + 2);
+memory_squeeze:
+ if (skb == NULL) {
+ /* XXX tulip.c can defer packets here!! */
+ printk(KERN_WARNING "%s: i596_rx Memory squeeze, dropping packet.\n", dev->name);
+ dev->stats.rx_dropped++;
+ }
+ else {
+ if (!rx_in_place) {
+ /* 16 byte align the data fields */
+ skb_reserve(skb, 2);
+ memcpy(skb_put(skb,pkt_len), rbd->v_data, pkt_len);
+ }
+ skb->protocol=eth_type_trans(skb,dev);
+ skb->len = pkt_len;
+#ifdef __mc68000__
+ cache_clear(virt_to_phys(rbd->skb->data),
+ pkt_len);
+#endif
+ netif_rx(skb);
+ dev->stats.rx_packets++;
+ dev->stats.rx_bytes+=pkt_len;
+ }
+ }
+ else {
+ DEB(DEB_ERRORS, printk(KERN_DEBUG "%s: Error, rfd.stat = 0x%04x\n",
+ dev->name, rfd->stat));
+ dev->stats.rx_errors++;
+ if ((rfd->stat) & 0x0001)
+ dev->stats.collisions++;
+ if ((rfd->stat) & 0x0080)
+ dev->stats.rx_length_errors++;
+ if ((rfd->stat) & 0x0100)
+ dev->stats.rx_over_errors++;
+ if ((rfd->stat) & 0x0200)
+ dev->stats.rx_fifo_errors++;
+ if ((rfd->stat) & 0x0400)
+ dev->stats.rx_frame_errors++;
+ if ((rfd->stat) & 0x0800)
+ dev->stats.rx_crc_errors++;
+ if ((rfd->stat) & 0x1000)
+ dev->stats.rx_length_errors++;
+ }
+
+ /* Clear the buffer descriptor count and EOF + F flags */
+
+ if (rbd != I596_NULL && (rbd->count & 0x4000)) {
+ rbd->count = 0;
+ lp->rbd_head = rbd->v_next;
+ }
+
+ /* Tidy the frame descriptor, marking it as end of list */
+
+ rfd->rbd = I596_NULL;
+ rfd->stat = 0;
+ rfd->cmd = CMD_EOL|CMD_FLEX;
+ rfd->count = 0;
+
+ /* Remove end-of-list from old end descriptor */
+
+ rfd->v_prev->cmd = CMD_FLEX;
+
+ /* Update record of next frame descriptor to process */
+
+ lp->scb.rfd = rfd->b_next;
+ lp->rfd_head = rfd->v_next;
+ rfd = lp->rfd_head;
+ }
+
+ DEB(DEB_RXFRAME,printk(KERN_DEBUG "frames %d\n", frames));
+
+ return 0;
+}
+
+
+static void i596_cleanup_cmd(struct net_device *dev, struct i596_private *lp)
+{
+ struct i596_cmd *ptr;
+
+ while (lp->cmd_head != I596_NULL) {
+ ptr = lp->cmd_head;
+ lp->cmd_head = ptr->v_next;
+ lp->cmd_backlog--;
+
+ switch ((ptr->command) & 0x7) {
+ case CmdTx:
+ {
+ struct tx_cmd *tx_cmd = (struct tx_cmd *) ptr;
+ struct sk_buff *skb = tx_cmd->skb;
+
+ dev_kfree_skb(skb);
+
+ dev->stats.tx_errors++;
+ dev->stats.tx_aborted_errors++;
+
+ ptr->v_next = ptr->b_next = I596_NULL;
+ tx_cmd->cmd.command = 0; /* Mark as free */
+ break;
+ }
+ default:
+ ptr->v_next = ptr->b_next = I596_NULL;
+ }
+ }
+
+ wait_cmd(dev,lp,100,"i596_cleanup_cmd timed out");
+ lp->scb.cmd = I596_NULL;
+}
+
+static void i596_reset(struct net_device *dev, struct i596_private *lp,
+ int ioaddr)
+{
+ unsigned long flags;
+
+ DEB(DEB_RESET,printk(KERN_DEBUG "i596_reset\n"));
+
+ spin_lock_irqsave (&lp->lock, flags);
+
+ wait_cmd(dev,lp,100,"i596_reset timed out");
+
+ netif_stop_queue(dev);
+
+ lp->scb.command = CUC_ABORT | RX_ABORT;
+ CA(dev);
+
+ /* wait for shutdown */
+ wait_cmd(dev,lp,1000,"i596_reset 2 timed out");
+ spin_unlock_irqrestore (&lp->lock, flags);
+
+ i596_cleanup_cmd(dev,lp);
+ i596_rx(dev);
+
+ netif_start_queue(dev);
+ init_i596_mem(dev);
+}
+
+static void i596_add_cmd(struct net_device *dev, struct i596_cmd *cmd)
+{
+ struct i596_private *lp = dev->ml_priv;
+ int ioaddr = dev->base_addr;
+ unsigned long flags;
+
+ DEB(DEB_ADDCMD,printk(KERN_DEBUG "i596_add_cmd\n"));
+
+ cmd->status = 0;
+ cmd->command |= (CMD_EOL | CMD_INTR);
+ cmd->v_next = cmd->b_next = I596_NULL;
+
+ spin_lock_irqsave (&lp->lock, flags);
+
+ if (lp->cmd_head != I596_NULL) {
+ lp->cmd_tail->v_next = cmd;
+ lp->cmd_tail->b_next = WSWAPcmd(virt_to_bus(&cmd->status));
+ } else {
+ lp->cmd_head = cmd;
+ wait_cmd(dev,lp,100,"i596_add_cmd timed out");
+ lp->scb.cmd = WSWAPcmd(virt_to_bus(&cmd->status));
+ lp->scb.command = CUC_START;
+ CA(dev);
+ }
+ lp->cmd_tail = cmd;
+ lp->cmd_backlog++;
+
+ spin_unlock_irqrestore (&lp->lock, flags);
+
+ if (lp->cmd_backlog > max_cmd_backlog) {
+ unsigned long tickssofar = jiffies - lp->last_cmd;
+
+ if (tickssofar < ticks_limit)
+ return;
+
+ printk(KERN_NOTICE "%s: command unit timed out, status resetting.\n", dev->name);
+
+ i596_reset(dev, lp, ioaddr);
+ }
+}
+
+static int i596_open(struct net_device *dev)
+{
+ int res = 0;
+
+ DEB(DEB_OPEN,printk(KERN_DEBUG "%s: i596_open() irq %d.\n", dev->name, dev->irq));
+
+ if (request_irq(dev->irq, i596_interrupt, 0, "i82596", dev)) {
+ printk(KERN_ERR "%s: IRQ %d not free\n", dev->name, dev->irq);
+ return -EAGAIN;
+ }
+#ifdef ENABLE_MVME16x_NET
+ if (MACH_IS_MVME16x) {
+ if (request_irq(0x56, i596_error, 0, "i82596_error", dev)) {
+ res = -EAGAIN;
+ goto err_irq_dev;
+ }
+ }
+#endif
+ res = init_rx_bufs(dev);
+ if (res)
+ goto err_irq_56;
+
+ netif_start_queue(dev);
+
+ if (init_i596_mem(dev)) {
+ res = -EAGAIN;
+ goto err_queue;
+ }
+
+ return 0;
+
+err_queue:
+ netif_stop_queue(dev);
+ remove_rx_bufs(dev);
+err_irq_56:
+#ifdef ENABLE_MVME16x_NET
+ free_irq(0x56, dev);
+err_irq_dev:
+#endif
+ free_irq(dev->irq, dev);
+
+ return res;
+}
+
+static void i596_tx_timeout (struct net_device *dev)
+{
+ struct i596_private *lp = dev->ml_priv;
+ int ioaddr = dev->base_addr;
+
+ /* Transmitter timeout, serious problems. */
+ DEB(DEB_ERRORS,printk(KERN_ERR "%s: transmit timed out, status resetting.\n",
+ dev->name));
+
+ dev->stats.tx_errors++;
+
+ /* Try to restart the adaptor */
+ if (lp->last_restart == dev->stats.tx_packets) {
+ DEB(DEB_ERRORS,printk(KERN_ERR "Resetting board.\n"));
+ /* Shutdown and restart */
+ i596_reset (dev, lp, ioaddr);
+ } else {
+ /* Issue a channel attention signal */
+ DEB(DEB_ERRORS,printk(KERN_ERR "Kicking board.\n"));
+ lp->scb.command = CUC_START | RX_START;
+ CA (dev);
+ lp->last_restart = dev->stats.tx_packets;
+ }
+
+ dev->trans_start = jiffies; /* prevent tx timeout */
+ netif_wake_queue (dev);
+}
+
+static netdev_tx_t i596_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct i596_private *lp = dev->ml_priv;
+ struct tx_cmd *tx_cmd;
+ struct i596_tbd *tbd;
+ short length = skb->len;
+
+ DEB(DEB_STARTTX,printk(KERN_DEBUG "%s: i596_start_xmit(%x,%p) called\n",
+ dev->name, skb->len, skb->data));
+
+ if (skb->len < ETH_ZLEN) {
+ if (skb_padto(skb, ETH_ZLEN))
+ return NETDEV_TX_OK;
+ length = ETH_ZLEN;
+ }
+ netif_stop_queue(dev);
+
+ tx_cmd = lp->tx_cmds + lp->next_tx_cmd;
+ tbd = lp->tbds + lp->next_tx_cmd;
+
+ if (tx_cmd->cmd.command) {
+ printk(KERN_NOTICE "%s: xmit ring full, dropping packet.\n",
+ dev->name);
+ dev->stats.tx_dropped++;
+
+ dev_kfree_skb(skb);
+ } else {
+ if (++lp->next_tx_cmd == TX_RING_SIZE)
+ lp->next_tx_cmd = 0;
+ tx_cmd->tbd = WSWAPtbd(virt_to_bus(tbd));
+ tbd->next = I596_NULL;
+
+ tx_cmd->cmd.command = CMD_FLEX | CmdTx;
+ tx_cmd->skb = skb;
+
+ tx_cmd->pad = 0;
+ tx_cmd->size = 0;
+ tbd->pad = 0;
+ tbd->size = EOF | length;
+
+ tbd->data = WSWAPchar(virt_to_bus(skb->data));
+
+#ifdef __mc68000__
+ cache_push(virt_to_phys(skb->data), length);
+#endif
+ DEB(DEB_TXADDR,print_eth(skb->data, "tx-queued"));
+ i596_add_cmd(dev, &tx_cmd->cmd);
+
+ dev->stats.tx_packets++;
+ dev->stats.tx_bytes += length;
+ }
+
+ netif_start_queue(dev);
+
+ return NETDEV_TX_OK;
+}
+
+static void print_eth(unsigned char *add, char *str)
+{
+ printk(KERN_DEBUG "i596 0x%p, %pM --> %pM %02X%02X, %s\n",
+ add, add + 6, add, add[12], add[13], str);
+}
+
+static int io = 0x300;
+static int irq = 10;
+
+static const struct net_device_ops i596_netdev_ops = {
+ .ndo_open = i596_open,
+ .ndo_stop = i596_close,
+ .ndo_start_xmit = i596_start_xmit,
+ .ndo_set_rx_mode = set_multicast_list,
+ .ndo_tx_timeout = i596_tx_timeout,
+ .ndo_change_mtu = eth_change_mtu,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_validate_addr = eth_validate_addr,
+};
+
+struct net_device * __init i82596_probe(int unit)
+{
+ struct net_device *dev;
+ int i;
+ struct i596_private *lp;
+ char eth_addr[8];
+ static int probed;
+ int err;
+
+ if (probed)
+ return ERR_PTR(-ENODEV);
+ probed++;
+
+ dev = alloc_etherdev(0);
+ if (!dev)
+ return ERR_PTR(-ENOMEM);
+
+ if (unit >= 0) {
+ sprintf(dev->name, "eth%d", unit);
+ netdev_boot_setup_check(dev);
+ } else {
+ dev->base_addr = io;
+ dev->irq = irq;
+ }
+
+#ifdef ENABLE_MVME16x_NET
+ if (MACH_IS_MVME16x) {
+ if (mvme16x_config & MVME16x_CONFIG_NO_ETHERNET) {
+ printk(KERN_NOTICE "Ethernet probe disabled - chip not present\n");
+ err = -ENODEV;
+ goto out;
+ }
+ memcpy(eth_addr, (void *) 0xfffc1f2c, 6); /* YUCK! Get addr from NOVRAM */
+ dev->base_addr = MVME_I596_BASE;
+ dev->irq = (unsigned) MVME16x_IRQ_I596;
+ goto found;
+ }
+#endif
+#ifdef ENABLE_BVME6000_NET
+ if (MACH_IS_BVME6000) {
+ volatile unsigned char *rtc = (unsigned char *) BVME_RTC_BASE;
+ unsigned char msr = rtc[3];
+ int i;
+
+ rtc[3] |= 0x80;
+ for (i = 0; i < 6; i++)
+ eth_addr[i] = rtc[i * 4 + 7]; /* Stored in RTC RAM at offset 1 */
+ rtc[3] = msr;
+ dev->base_addr = BVME_I596_BASE;
+ dev->irq = (unsigned) BVME_IRQ_I596;
+ goto found;
+ }
+#endif
+#ifdef ENABLE_APRICOT
+ {
+ int checksum = 0;
+ int ioaddr = 0x300;
+
+ /* this is easy the ethernet interface can only be at 0x300 */
+ /* first check nothing is already registered here */
+
+ if (!request_region(ioaddr, I596_TOTAL_SIZE, DRV_NAME)) {
+ printk(KERN_ERR "82596: IO address 0x%04x in use\n", ioaddr);
+ err = -EBUSY;
+ goto out;
+ }
+
+ dev->base_addr = ioaddr;
+
+ for (i = 0; i < 8; i++) {
+ eth_addr[i] = inb(ioaddr + 8 + i);
+ checksum += eth_addr[i];
+ }
+
+ /* checksum is a multiple of 0x100, got this wrong first time
+ some machines have 0x100, some 0x200. The DOS driver doesn't
+ even bother with the checksum.
+ Some other boards trip the checksum.. but then appear as
+ ether address 0. Trap these - AC */
+
+ if ((checksum % 0x100) ||
+ (memcmp(eth_addr, "\x00\x00\x49", 3) != 0)) {
+ err = -ENODEV;
+ goto out1;
+ }
+
+ dev->irq = 10;
+ goto found;
+ }
+#endif
+ err = -ENODEV;
+ goto out;
+
+found:
+ dev->mem_start = (int)__get_free_pages(GFP_ATOMIC, 0);
+ if (!dev->mem_start) {
+ err = -ENOMEM;
+ goto out1;
+ }
+
+ DEB(DEB_PROBE,printk(KERN_INFO "%s: 82596 at %#3lx,", dev->name, dev->base_addr));
+
+ for (i = 0; i < 6; i++)
+ DEB(DEB_PROBE,printk(" %2.2X", dev->dev_addr[i] = eth_addr[i]));
+
+ DEB(DEB_PROBE,printk(" IRQ %d.\n", dev->irq));
+
+ DEB(DEB_PROBE,printk(KERN_INFO "%s", version));
+
+ /* The 82596-specific entries in the device structure. */
+ dev->netdev_ops = &i596_netdev_ops;
+ dev->watchdog_timeo = TX_TIMEOUT;
+
+ dev->ml_priv = (void *)(dev->mem_start);
+
+ lp = dev->ml_priv;
+ DEB(DEB_INIT,printk(KERN_DEBUG "%s: lp at 0x%08lx (%zd bytes), "
+ "lp->scb at 0x%08lx\n",
+ dev->name, (unsigned long)lp,
+ sizeof(struct i596_private), (unsigned long)&lp->scb));
+ memset((void *) lp, 0, sizeof(struct i596_private));
+
+#ifdef __mc68000__
+ cache_push(virt_to_phys((void *)(dev->mem_start)), 4096);
+ cache_clear(virt_to_phys((void *)(dev->mem_start)), 4096);
+ kernel_set_cachemode((void *)(dev->mem_start), 4096, IOMAP_NOCACHE_SER);
+#endif
+ lp->scb.command = 0;
+ lp->scb.cmd = I596_NULL;
+ lp->scb.rfd = I596_NULL;
+ spin_lock_init(&lp->lock);
+
+ err = register_netdev(dev);
+ if (err)
+ goto out2;
+ return dev;
+out2:
+#ifdef __mc68000__
+ /* XXX This assumes default cache mode to be IOMAP_FULL_CACHING,
+ * XXX which may be invalid (CONFIG_060_WRITETHROUGH)
+ */
+ kernel_set_cachemode((void *)(dev->mem_start), 4096,
+ IOMAP_FULL_CACHING);
+#endif
+ free_page ((u32)(dev->mem_start));
+out1:
+#ifdef ENABLE_APRICOT
+ release_region(dev->base_addr, I596_TOTAL_SIZE);
+#endif
+out:
+ free_netdev(dev);
+ return ERR_PTR(err);
+}
+
+static irqreturn_t i596_interrupt(int irq, void *dev_id)
+{
+ struct net_device *dev = dev_id;
+ struct i596_private *lp;
+ short ioaddr;
+ unsigned short status, ack_cmd = 0;
+ int handled = 0;
+
+#ifdef ENABLE_BVME6000_NET
+ if (MACH_IS_BVME6000) {
+ if (*(char *) BVME_LOCAL_IRQ_STAT & BVME_ETHERR) {
+ i596_error(irq, dev_id);
+ return IRQ_HANDLED;
+ }
+ }
+#endif
+ if (dev == NULL) {
+ printk(KERN_ERR "i596_interrupt(): irq %d for unknown device.\n", irq);
+ return IRQ_NONE;
+ }
+
+ ioaddr = dev->base_addr;
+ lp = dev->ml_priv;
+
+ spin_lock (&lp->lock);
+
+ wait_cmd(dev,lp,100,"i596 interrupt, timeout");
+ status = lp->scb.status;
+
+ DEB(DEB_INTS,printk(KERN_DEBUG "%s: i596 interrupt, IRQ %d, status %4.4x.\n",
+ dev->name, irq, status));
+
+ ack_cmd = status & 0xf000;
+
+ if ((status & 0x8000) || (status & 0x2000)) {
+ struct i596_cmd *ptr;
+
+ handled = 1;
+ if ((status & 0x8000))
+ DEB(DEB_INTS,printk(KERN_DEBUG "%s: i596 interrupt completed command.\n", dev->name));
+ if ((status & 0x2000))
+ DEB(DEB_INTS,printk(KERN_DEBUG "%s: i596 interrupt command unit inactive %x.\n", dev->name, status & 0x0700));
+
+ while ((lp->cmd_head != I596_NULL) && (lp->cmd_head->status & STAT_C)) {
+ ptr = lp->cmd_head;
+
+ DEB(DEB_STATUS,printk(KERN_DEBUG "cmd_head->status = %04x, ->command = %04x\n",
+ lp->cmd_head->status, lp->cmd_head->command));
+ lp->cmd_head = ptr->v_next;
+ lp->cmd_backlog--;
+
+ switch ((ptr->command) & 0x7) {
+ case CmdTx:
+ {
+ struct tx_cmd *tx_cmd = (struct tx_cmd *) ptr;
+ struct sk_buff *skb = tx_cmd->skb;
+
+ if ((ptr->status) & STAT_OK) {
+ DEB(DEB_TXADDR,print_eth(skb->data, "tx-done"));
+ } else {
+ dev->stats.tx_errors++;
+ if ((ptr->status) & 0x0020)
+ dev->stats.collisions++;
+ if (!((ptr->status) & 0x0040))
+ dev->stats.tx_heartbeat_errors++;
+ if ((ptr->status) & 0x0400)
+ dev->stats.tx_carrier_errors++;
+ if ((ptr->status) & 0x0800)
+ dev->stats.collisions++;
+ if ((ptr->status) & 0x1000)
+ dev->stats.tx_aborted_errors++;
+ }
+
+ dev_kfree_skb_irq(skb);
+
+ tx_cmd->cmd.command = 0; /* Mark free */
+ break;
+ }
+ case CmdTDR:
+ {
+ unsigned short status = ((struct tdr_cmd *)ptr)->status;
+
+ if (status & 0x8000) {
+ DEB(DEB_TDR,printk(KERN_INFO "%s: link ok.\n", dev->name));
+ } else {
+ if (status & 0x4000)
+ printk(KERN_ERR "%s: Transceiver problem.\n", dev->name);
+ if (status & 0x2000)
+ printk(KERN_ERR "%s: Termination problem.\n", dev->name);
+ if (status & 0x1000)
+ printk(KERN_ERR "%s: Short circuit.\n", dev->name);
+
+ DEB(DEB_TDR,printk(KERN_INFO "%s: Time %d.\n", dev->name, status & 0x07ff));
+ }
+ break;
+ }
+ case CmdConfigure:
+ case CmdMulticastList:
+ /* Zap command so set_multicast_list() knows it is free */
+ ptr->command = 0;
+ break;
+ }
+ ptr->v_next = ptr->b_next = I596_NULL;
+ lp->last_cmd = jiffies;
+ }
+
+ ptr = lp->cmd_head;
+ while ((ptr != I596_NULL) && (ptr != lp->cmd_tail)) {
+ ptr->command &= 0x1fff;
+ ptr = ptr->v_next;
+ }
+
+ if ((lp->cmd_head != I596_NULL))
+ ack_cmd |= CUC_START;
+ lp->scb.cmd = WSWAPcmd(virt_to_bus(&lp->cmd_head->status));
+ }
+ if ((status & 0x1000) || (status & 0x4000)) {
+ if ((status & 0x4000))
+ DEB(DEB_INTS,printk(KERN_DEBUG "%s: i596 interrupt received a frame.\n", dev->name));
+ i596_rx(dev);
+ /* Only RX_START if stopped - RGH 07-07-96 */
+ if (status & 0x1000) {
+ if (netif_running(dev)) {
+ DEB(DEB_ERRORS,printk(KERN_ERR "%s: i596 interrupt receive unit inactive, status 0x%x\n", dev->name, status));
+ ack_cmd |= RX_START;
+ dev->stats.rx_errors++;
+ dev->stats.rx_fifo_errors++;
+ rebuild_rx_bufs(dev);
+ }
+ }
+ }
+ wait_cmd(dev,lp,100,"i596 interrupt, timeout");
+ lp->scb.command = ack_cmd;
+
+#ifdef ENABLE_MVME16x_NET
+ if (MACH_IS_MVME16x) {
+ /* Ack the interrupt */
+
+ volatile unsigned char *pcc2 = (unsigned char *) 0xfff42000;
+
+ pcc2[0x2a] |= 0x08;
+ }
+#endif
+#ifdef ENABLE_BVME6000_NET
+ if (MACH_IS_BVME6000) {
+ volatile unsigned char *ethirq = (unsigned char *) BVME_ETHIRQ_REG;
+
+ *ethirq = 1;
+ *ethirq = 3;
+ }
+#endif
+#ifdef ENABLE_APRICOT
+ (void) inb(ioaddr + 0x10);
+ outb(4, ioaddr + 0xf);
+#endif
+ CA(dev);
+
+ DEB(DEB_INTS,printk(KERN_DEBUG "%s: exiting interrupt.\n", dev->name));
+
+ spin_unlock (&lp->lock);
+ return IRQ_RETVAL(handled);
+}
+
+static int i596_close(struct net_device *dev)
+{
+ struct i596_private *lp = dev->ml_priv;
+ unsigned long flags;
+
+ netif_stop_queue(dev);
+
+ DEB(DEB_INIT,printk(KERN_DEBUG "%s: Shutting down ethercard, status was %4.4x.\n",
+ dev->name, lp->scb.status));
+
+ spin_lock_irqsave(&lp->lock, flags);
+
+ wait_cmd(dev,lp,100,"close1 timed out");
+ lp->scb.command = CUC_ABORT | RX_ABORT;
+ CA(dev);
+
+ wait_cmd(dev,lp,100,"close2 timed out");
+
+ spin_unlock_irqrestore(&lp->lock, flags);
+ DEB(DEB_STRUCT,i596_display_data(dev));
+ i596_cleanup_cmd(dev,lp);
+
+#ifdef ENABLE_MVME16x_NET
+ if (MACH_IS_MVME16x) {
+ volatile unsigned char *pcc2 = (unsigned char *) 0xfff42000;
+
+ /* Disable all ints */
+ pcc2[0x28] = 1;
+ pcc2[0x2a] = 0x40;
+ pcc2[0x2b] = 0x40; /* Set snooping bits now! */
+ }
+#endif
+#ifdef ENABLE_BVME6000_NET
+ if (MACH_IS_BVME6000) {
+ volatile unsigned char *ethirq = (unsigned char *) BVME_ETHIRQ_REG;
+
+ *ethirq = 1;
+ }
+#endif
+
+#ifdef ENABLE_MVME16x_NET
+ free_irq(0x56, dev);
+#endif
+ free_irq(dev->irq, dev);
+ remove_rx_bufs(dev);
+
+ return 0;
+}
+
+/*
+ * Set or clear the multicast filter for this adaptor.
+ */
+
+static void set_multicast_list(struct net_device *dev)
+{
+ struct i596_private *lp = dev->ml_priv;
+ int config = 0, cnt;
+
+ DEB(DEB_MULTI,printk(KERN_DEBUG "%s: set multicast list, %d entries, promisc %s, allmulti %s\n",
+ dev->name, netdev_mc_count(dev),
+ dev->flags & IFF_PROMISC ? "ON" : "OFF",
+ dev->flags & IFF_ALLMULTI ? "ON" : "OFF"));
+
+ if (wait_cfg(dev, &lp->cf_cmd.cmd, 1000, "config change request timed out"))
+ return;
+
+ if ((dev->flags & IFF_PROMISC) && !(lp->cf_cmd.i596_config[8] & 0x01)) {
+ lp->cf_cmd.i596_config[8] |= 0x01;
+ config = 1;
+ }
+ if (!(dev->flags & IFF_PROMISC) && (lp->cf_cmd.i596_config[8] & 0x01)) {
+ lp->cf_cmd.i596_config[8] &= ~0x01;
+ config = 1;
+ }
+ if ((dev->flags & IFF_ALLMULTI) && (lp->cf_cmd.i596_config[11] & 0x20)) {
+ lp->cf_cmd.i596_config[11] &= ~0x20;
+ config = 1;
+ }
+ if (!(dev->flags & IFF_ALLMULTI) && !(lp->cf_cmd.i596_config[11] & 0x20)) {
+ lp->cf_cmd.i596_config[11] |= 0x20;
+ config = 1;
+ }
+ if (config) {
+ lp->cf_cmd.cmd.command = CmdConfigure;
+ i596_add_cmd(dev, &lp->cf_cmd.cmd);
+ }
+
+ cnt = netdev_mc_count(dev);
+ if (cnt > MAX_MC_CNT)
+ {
+ cnt = MAX_MC_CNT;
+ printk(KERN_ERR "%s: Only %d multicast addresses supported",
+ dev->name, cnt);
+ }
+
+ if (!netdev_mc_empty(dev)) {
+ struct netdev_hw_addr *ha;
+ unsigned char *cp;
+ struct mc_cmd *cmd;
+
+ if (wait_cfg(dev, &lp->mc_cmd.cmd, 1000, "multicast list change request timed out"))
+ return;
+ cmd = &lp->mc_cmd;
+ cmd->cmd.command = CmdMulticastList;
+ cmd->mc_cnt = cnt * ETH_ALEN;
+ cp = cmd->mc_addrs;
+ netdev_for_each_mc_addr(ha, dev) {
+ if (!cnt--)
+ break;
+ memcpy(cp, ha->addr, ETH_ALEN);
+ if (i596_debug > 1)
+ DEB(DEB_MULTI,printk(KERN_INFO "%s: Adding address %pM\n",
+ dev->name, cp));
+ cp += ETH_ALEN;
+ }
+ i596_add_cmd(dev, &cmd->cmd);
+ }
+}
+
+#ifdef MODULE
+static struct net_device *dev_82596;
+
+#ifdef ENABLE_APRICOT
+module_param(irq, int, 0);
+MODULE_PARM_DESC(irq, "Apricot IRQ number");
+#endif
+
+static int debug = -1;
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "i82596 debug mask");
+
+int __init init_module(void)
+{
+ if (debug >= 0)
+ i596_debug = debug;
+ dev_82596 = i82596_probe(-1);
+ if (IS_ERR(dev_82596))
+ return PTR_ERR(dev_82596);
+ return 0;
+}
+
+void __exit cleanup_module(void)
+{
+ unregister_netdev(dev_82596);
+#ifdef __mc68000__
+ /* XXX This assumes default cache mode to be IOMAP_FULL_CACHING,
+ * XXX which may be invalid (CONFIG_060_WRITETHROUGH)
+ */
+
+ kernel_set_cachemode((void *)(dev_82596->mem_start), 4096,
+ IOMAP_FULL_CACHING);
+#endif
+ free_page ((u32)(dev_82596->mem_start));
+#ifdef ENABLE_APRICOT
+ /* If we don't do this, we can't re-insmod it later. */
+ release_region(dev_82596->base_addr, I596_TOTAL_SIZE);
+#endif
+ free_netdev(dev_82596);
+}
+
+#endif /* MODULE */
diff --git a/drivers/net/ethernet/i825xx/Kconfig b/drivers/net/ethernet/i825xx/Kconfig
new file mode 100644
index 00000000000..2be46986cbe
--- /dev/null
+++ b/drivers/net/ethernet/i825xx/Kconfig
@@ -0,0 +1,183 @@
+#
+# Intel 82596/82593/82596 network device configuration
+#
+
+config NET_VENDOR_I825XX
+ bool "Intel (82586/82593/82596) devices"
+ default y
+ depends on NET_VENDOR_INTEL && (ISA || ISA_DMA_API || ARM || \
+ ARCH_ACORN || MCA || MCA_LEGACY || SNI_RM || SUN3 || \
+ GSC || BVME6000 || MVME16x || EXPERIMENTAL)
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say Y
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ Note that the answer to this question does not directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about these devices. If you say Y, you will be asked for
+ your specific card in the following questions.
+
+if NET_VENDOR_I825XX
+
+config ELPLUS
+ tristate "3c505 \"EtherLink Plus\" support"
+ depends on ISA && ISA_DMA_API
+ ---help---
+ Information about this network (Ethernet) card can be found in
+ <file:Documentation/networking/3c505.txt>. If you have a card of
+ this type, say Y and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ To compile this driver as a module, choose M here. The module
+ will be called 3c505.
+
+config EL16
+ tristate "3c507 \"EtherLink 16\" support (EXPERIMENTAL)"
+ depends on ISA && EXPERIMENTAL
+ ---help---
+ If you have a network (Ethernet) card of this type, say Y and read
+ the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ To compile this driver as a module, choose M here. The module
+ will be called 3c507.
+
+config ELMC
+ tristate "3c523 \"EtherLink/MC\" support"
+ depends on MCA_LEGACY
+ ---help---
+ If you have a network (Ethernet) card of this type, say Y and read
+ the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ To compile this driver as a module, choose M here. The module
+ will be called 3c523.
+
+config ELMC_II
+ tristate "3c527 \"EtherLink/MC 32\" support (EXPERIMENTAL)"
+ depends on MCA && MCA_LEGACY
+ ---help---
+ If you have a network (Ethernet) card of this type, say Y and read
+ the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ To compile this driver as a module, choose M here. The module
+ will be called 3c527.
+
+config ARM_ETHER1
+ tristate "Acorn Ether1 support"
+ depends on ARM && ARCH_ACORN
+ ---help---
+ If you have an Acorn system with one of these (AKA25) network cards,
+ you should say Y to this option if you wish to use it with Linux.
+
+config APRICOT
+ tristate "Apricot Xen-II on board Ethernet"
+ depends on ISA
+ ---help---
+ If you have a network (Ethernet) controller of this type, say Y and
+ read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ To compile this driver as a module, choose M here. The module
+ will be called apricot.
+
+config BVME6000_NET
+ tristate "BVME6000 Ethernet support"
+ depends on BVME6000MVME16x
+ ---help---
+ This is the driver for the Ethernet interface on BVME4000 and
+ BVME6000 VME boards. Say Y here to include the driver for this chip
+ in your kernel.
+ To compile this driver as a module, choose M here.
+
+config EEXPRESS
+ tristate "EtherExpress 16 support"
+ depends on ISA
+ ---help---
+ If you have an EtherExpress16 network (Ethernet) card, say Y and
+ read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>. Note that the Intel
+ EtherExpress16 card used to be regarded as a very poor choice
+ because the driver was very unreliable. We now have a new driver
+ that should do better.
+
+ To compile this driver as a module, choose M here. The module
+ will be called eexpress.
+
+config EEXPRESS_PRO
+ tristate "EtherExpressPro support/EtherExpress 10 (i82595) support"
+ depends on ISA
+ ---help---
+ If you have a network (Ethernet) card of this type, say Y. This
+ driver supports Intel i82595{FX,TX} based boards. Note however
+ that the EtherExpress PRO/100 Ethernet card has its own separate
+ driver. Please read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ To compile this driver as a module, choose M here. The module
+ will be called eepro.
+
+config LASI_82596
+ tristate "Lasi ethernet"
+ depends on GSC
+ ---help---
+ Say Y here to support the builtin Intel 82596 ethernet controller
+ found in Hewlett-Packard PA-RISC machines with 10Mbit ethernet.
+
+config LP486E
+ tristate "LP486E on board Ethernet"
+ depends on ISA
+ ---help---
+ Say Y here to support the 82596-based on-board Ethernet controller
+ for the Panther motherboard, which is one of the two shipped in the
+ Intel Professional Workstation.
+
+config MVME16x_NET
+ tristate "MVME16x Ethernet support"
+ depends on MVME16x
+ ---help---
+ This is the driver for the Ethernet interface on the Motorola
+ MVME162, 166, 167, 172 and 177 boards. Say Y here to include the
+ driver for this chip in your kernel.
+ To compile this driver as a module, choose M here.
+
+config NI52
+ tristate "NI5210 support"
+ depends on ISA
+ ---help---
+ If you have a network (Ethernet) card of this type, say Y and read
+ the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ To compile this driver as a module, choose M here. The module
+ will be called ni52.
+
+config SNI_82596
+ tristate "SNI RM ethernet"
+ depends on SNI_RM
+ ---help---
+ Say Y here to support the on-board Intel 82596 ethernet controller
+ built into SNI RM machines.
+
+config SUN3_82586
+ bool "Sun3 on-board Intel 82586 support"
+ depends on SUN3
+ ---help---
+ This driver enables support for the on-board Intel 82586 based
+ Ethernet adapter found on Sun 3/1xx and 3/2xx motherboards. Note
+ that this driver does not support 82586-based adapters on additional
+ VME boards.
+
+config ZNET
+ tristate "Zenith Z-Note support (EXPERIMENTAL)"
+ depends on EXPERIMENTAL && ISA_DMA_API
+ ---help---
+ The Zenith Z-Note notebook computer has a built-in network
+ (Ethernet) card, and this is the Linux driver for it. Note that the
+ IBM Thinkpad 300 is compatible with the Z-Note and is also supported
+ by this driver. Read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+endif # NET_VENDOR_I825XX
diff --git a/drivers/net/ethernet/i825xx/Makefile b/drivers/net/ethernet/i825xx/Makefile
new file mode 100644
index 00000000000..f68a3694968
--- /dev/null
+++ b/drivers/net/ethernet/i825xx/Makefile
@@ -0,0 +1,20 @@
+#
+# Makefile for the Intel 82586/82593/82596 chipset device drivers.
+#
+
+obj-$(CONFIG_ARM_ETHER1) += ether1.o
+obj-$(CONFIG_EEXPRESS) += eexpress.o
+obj-$(CONFIG_EEXPRESS_PRO) += eepro.o
+obj-$(CONFIG_ELPLUS) += 3c505.o
+obj-$(CONFIG_EL16) += 3c507.o
+obj-$(CONFIG_ELMC) += 3c523.o
+obj-$(CONFIG_ELMC_II) += 3c527.o
+obj-$(CONFIG_LP486E) += lp486e.o
+obj-$(CONFIG_NI52) += ni52.o
+obj-$(CONFIG_SUN3_82586) += sun3_82586.o
+obj-$(CONFIG_ZNET) += znet.o
+obj-$(CONFIG_APRICOT) += 82596.o
+obj-$(CONFIG_LASI_82596) += lasi_82596.o
+obj-$(CONFIG_SNI_82596) += sni_82596.o
+obj-$(CONFIG_MVME16x_NET) += 82596.o
+obj-$(CONFIG_BVME6000_NET) += 82596.o
diff --git a/drivers/net/ethernet/i825xx/eepro.c b/drivers/net/ethernet/i825xx/eepro.c
new file mode 100644
index 00000000000..067c46069a1
--- /dev/null
+++ b/drivers/net/ethernet/i825xx/eepro.c
@@ -0,0 +1,1822 @@
+/* eepro.c: Intel EtherExpress Pro/10 device driver for Linux. */
+/*
+ Written 1994, 1995,1996 by Bao C. Ha.
+
+ Copyright (C) 1994, 1995,1996 by Bao C. Ha.
+
+ This software may be used and distributed
+ according to the terms of the GNU General Public License,
+ incorporated herein by reference.
+
+ The author may be reached at bao.ha@srs.gov
+ or 418 Hastings Place, Martinez, GA 30907.
+
+ Things remaining to do:
+ Better record keeping of errors.
+ Eliminate transmit interrupt to reduce overhead.
+ Implement "concurrent processing". I won't be doing it!
+
+ Bugs:
+
+ If you have a problem of not detecting the 82595 during a
+ reboot (warm reset), disable the FLASH memory should fix it.
+ This is a compatibility hardware problem.
+
+ Versions:
+ 0.13b basic ethtool support (aris, 09/13/2004)
+ 0.13a in memory shortage, drop packets also in board
+ (Michael Westermann <mw@microdata-pos.de>, 07/30/2002)
+ 0.13 irq sharing, rewrote probe function, fixed a nasty bug in
+ hardware_send_packet and a major cleanup (aris, 11/08/2001)
+ 0.12d fixing a problem with single card detected as eight eth devices
+ fixing a problem with sudden drop in card performance
+ (chris (asdn@go2.pl), 10/29/2001)
+ 0.12c fixing some problems with old cards (aris, 01/08/2001)
+ 0.12b misc fixes (aris, 06/26/2000)
+ 0.12a port of version 0.12a of 2.2.x kernels to 2.3.x
+ (aris (aris@conectiva.com.br), 05/19/2000)
+ 0.11e some tweaks about multiple cards support (PdP, jul/aug 1999)
+ 0.11d added __initdata, __init stuff; call spin_lock_init
+ in eepro_probe1. Replaced "eepro" by dev->name. Augmented
+ the code protected by spin_lock in interrupt routine
+ (PdP, 12/12/1998)
+ 0.11c minor cleanup (PdP, RMC, 09/12/1998)
+ 0.11b Pascal Dupuis (dupuis@lei.ucl.ac.be): works as a module
+ under 2.1.xx. Debug messages are flagged as KERN_DEBUG to
+ avoid console flooding. Added locking at critical parts. Now
+ the dawn thing is SMP safe.
+ 0.11a Attempt to get 2.1.xx support up (RMC)
+ 0.11 Brian Candler added support for multiple cards. Tested as
+ a module, no idea if it works when compiled into kernel.
+
+ 0.10e Rick Bressler notified me that ifconfig up;ifconfig down fails
+ because the irq is lost somewhere. Fixed that by moving
+ request_irq and free_irq to eepro_open and eepro_close respectively.
+ 0.10d Ugh! Now Wakeup works. Was seriously broken in my first attempt.
+ I'll need to find a way to specify an ioport other than
+ the default one in the PnP case. PnP definitively sucks.
+ And, yes, this is not the only reason.
+ 0.10c PnP Wakeup Test for 595FX. uncomment #define PnPWakeup;
+ to use.
+ 0.10b Should work now with (some) Pro/10+. At least for
+ me (and my two cards) it does. _No_ guarantee for
+ function with non-Pro/10+ cards! (don't have any)
+ (RMC, 9/11/96)
+
+ 0.10 Added support for the Etherexpress Pro/10+. The
+ IRQ map was changed significantly from the old
+ pro/10. The new interrupt map was provided by
+ Rainer M. Canavan (Canavan@Zeus.cs.bonn.edu).
+ (BCH, 9/3/96)
+
+ 0.09 Fixed a race condition in the transmit algorithm,
+ which causes crashes under heavy load with fast
+ pentium computers. The performance should also
+ improve a bit. The size of RX buffer, and hence
+ TX buffer, can also be changed via lilo or insmod.
+ (BCH, 7/31/96)
+
+ 0.08 Implement 32-bit I/O for the 82595TX and 82595FX
+ based lan cards. Disable full-duplex mode if TPE
+ is not used. (BCH, 4/8/96)
+
+ 0.07a Fix a stat report which counts every packet as a
+ heart-beat failure. (BCH, 6/3/95)
+
+ 0.07 Modified to support all other 82595-based lan cards.
+ The IRQ vector of the EtherExpress Pro will be set
+ according to the value saved in the EEPROM. For other
+ cards, I will do autoirq_request() to grab the next
+ available interrupt vector. (BCH, 3/17/95)
+
+ 0.06a,b Interim released. Minor changes in the comments and
+ print out format. (BCH, 3/9/95 and 3/14/95)
+
+ 0.06 First stable release that I am comfortable with. (BCH,
+ 3/2/95)
+
+ 0.05 Complete testing of multicast. (BCH, 2/23/95)
+
+ 0.04 Adding multicast support. (BCH, 2/14/95)
+
+ 0.03 First widely alpha release for public testing.
+ (BCH, 2/14/95)
+
+*/
+
+static const char version[] =
+ "eepro.c: v0.13b 09/13/2004 aris@cathedrallabs.org\n";
+
+#include <linux/module.h>
+
+/*
+ Sources:
+
+ This driver wouldn't have been written without the availability
+ of the Crynwr's Lan595 driver source code. It helps me to
+ familiarize with the 82595 chipset while waiting for the Intel
+ documentation. I also learned how to detect the 82595 using
+ the packet driver's technique.
+
+ This driver is written by cutting and pasting the skeleton.c driver
+ provided by Donald Becker. I also borrowed the EEPROM routine from
+ Donald Becker's 82586 driver.
+
+ Datasheet for the Intel 82595 (including the TX and FX version). It
+ provides just enough info that the casual reader might think that it
+ documents the i82595.
+
+ The User Manual for the 82595. It provides a lot of the missing
+ information.
+
+*/
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+#include <linux/ethtool.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#define DRV_NAME "eepro"
+#define DRV_VERSION "0.13c"
+
+#define compat_dev_kfree_skb( skb, mode ) dev_kfree_skb( (skb) )
+/* I had reports of looong delays with SLOW_DOWN defined as udelay(2) */
+#define SLOW_DOWN inb(0x80)
+/* udelay(2) */
+#define compat_init_data __initdata
+enum iftype { AUI=0, BNC=1, TPE=2 };
+
+/* First, a few definitions that the brave might change. */
+/* A zero-terminated list of I/O addresses to be probed. */
+static unsigned int eepro_portlist[] compat_init_data =
+ { 0x300, 0x210, 0x240, 0x280, 0x2C0, 0x200, 0x320, 0x340, 0x360, 0};
+/* note: 0x300 is default, the 595FX supports ALL IO Ports
+ from 0x000 to 0x3F0, some of which are reserved in PCs */
+
+/* To try the (not-really PnP Wakeup: */
+/*
+#define PnPWakeup
+*/
+
+/* use 0 for production, 1 for verification, >2 for debug */
+#ifndef NET_DEBUG
+#define NET_DEBUG 0
+#endif
+static unsigned int net_debug = NET_DEBUG;
+
+/* The number of low I/O ports used by the ethercard. */
+#define EEPRO_IO_EXTENT 16
+
+/* Different 82595 chips */
+#define LAN595 0
+#define LAN595TX 1
+#define LAN595FX 2
+#define LAN595FX_10ISA 3
+
+/* Information that need to be kept for each board. */
+struct eepro_local {
+ unsigned rx_start;
+ unsigned tx_start; /* start of the transmit chain */
+ int tx_last; /* pointer to last packet in the transmit chain */
+ unsigned tx_end; /* end of the transmit chain (plus 1) */
+ int eepro; /* 1 for the EtherExpress Pro/10,
+ 2 for the EtherExpress Pro/10+,
+ 3 for the EtherExpress 10 (blue cards),
+ 0 for other 82595-based lan cards. */
+ int version; /* a flag to indicate if this is a TX or FX
+ version of the 82595 chip. */
+ int stepping;
+
+ spinlock_t lock; /* Serializing lock */
+
+ unsigned rcv_ram; /* pre-calculated space for rx */
+ unsigned xmt_ram; /* pre-calculated space for tx */
+ unsigned char xmt_bar;
+ unsigned char xmt_lower_limit_reg;
+ unsigned char xmt_upper_limit_reg;
+ short xmt_lower_limit;
+ short xmt_upper_limit;
+ short rcv_lower_limit;
+ short rcv_upper_limit;
+ unsigned char eeprom_reg;
+ unsigned short word[8];
+};
+
+/* The station (ethernet) address prefix, used for IDing the board. */
+#define SA_ADDR0 0x00 /* Etherexpress Pro/10 */
+#define SA_ADDR1 0xaa
+#define SA_ADDR2 0x00
+
+#define GetBit(x,y) ((x & (1<<y))>>y)
+
+/* EEPROM Word 0: */
+#define ee_PnP 0 /* Plug 'n Play enable bit */
+#define ee_Word1 1 /* Word 1? */
+#define ee_BusWidth 2 /* 8/16 bit */
+#define ee_FlashAddr 3 /* Flash Address */
+#define ee_FlashMask 0x7 /* Mask */
+#define ee_AutoIO 6 /* */
+#define ee_reserved0 7 /* =0! */
+#define ee_Flash 8 /* Flash there? */
+#define ee_AutoNeg 9 /* Auto Negotiation enabled? */
+#define ee_IO0 10 /* IO Address LSB */
+#define ee_IO0Mask 0x /*...*/
+#define ee_IO1 15 /* IO MSB */
+
+/* EEPROM Word 1: */
+#define ee_IntSel 0 /* Interrupt */
+#define ee_IntMask 0x7
+#define ee_LI 3 /* Link Integrity 0= enabled */
+#define ee_PC 4 /* Polarity Correction 0= enabled */
+#define ee_TPE_AUI 5 /* PortSelection 1=TPE */
+#define ee_Jabber 6 /* Jabber prevention 0= enabled */
+#define ee_AutoPort 7 /* Auto Port Selection 1= Disabled */
+#define ee_SMOUT 8 /* SMout Pin Control 0= Input */
+#define ee_PROM 9 /* Flash EPROM / PROM 0=Flash */
+#define ee_reserved1 10 /* .. 12 =0! */
+#define ee_AltReady 13 /* Alternate Ready, 0=normal */
+#define ee_reserved2 14 /* =0! */
+#define ee_Duplex 15
+
+/* Word2,3,4: */
+#define ee_IA5 0 /*bit start for individual Addr Byte 5 */
+#define ee_IA4 8 /*bit start for individual Addr Byte 5 */
+#define ee_IA3 0 /*bit start for individual Addr Byte 5 */
+#define ee_IA2 8 /*bit start for individual Addr Byte 5 */
+#define ee_IA1 0 /*bit start for individual Addr Byte 5 */
+#define ee_IA0 8 /*bit start for individual Addr Byte 5 */
+
+/* Word 5: */
+#define ee_BNC_TPE 0 /* 0=TPE */
+#define ee_BootType 1 /* 00=None, 01=IPX, 10=ODI, 11=NDIS */
+#define ee_BootTypeMask 0x3
+#define ee_NumConn 3 /* Number of Connections 0= One or Two */
+#define ee_FlashSock 4 /* Presence of Flash Socket 0= Present */
+#define ee_PortTPE 5
+#define ee_PortBNC 6
+#define ee_PortAUI 7
+#define ee_PowerMgt 10 /* 0= disabled */
+#define ee_CP 13 /* Concurrent Processing */
+#define ee_CPMask 0x7
+
+/* Word 6: */
+#define ee_Stepping 0 /* Stepping info */
+#define ee_StepMask 0x0F
+#define ee_BoardID 4 /* Manucaturer Board ID, reserved */
+#define ee_BoardMask 0x0FFF
+
+/* Word 7: */
+#define ee_INT_TO_IRQ 0 /* int to IRQ Mapping = 0x1EB8 for Pro/10+ */
+#define ee_FX_INT2IRQ 0x1EB8 /* the _only_ mapping allowed for FX chips */
+
+/*..*/
+#define ee_SIZE 0x40 /* total EEprom Size */
+#define ee_Checksum 0xBABA /* initial and final value for adding checksum */
+
+
+/* Card identification via EEprom: */
+#define ee_addr_vendor 0x10 /* Word offset for EISA Vendor ID */
+#define ee_addr_id 0x11 /* Word offset for Card ID */
+#define ee_addr_SN 0x12 /* Serial Number */
+#define ee_addr_CRC_8 0x14 /* CRC over last thee Bytes */
+
+
+#define ee_vendor_intel0 0x25 /* Vendor ID Intel */
+#define ee_vendor_intel1 0xD4
+#define ee_id_eepro10p0 0x10 /* ID for eepro/10+ */
+#define ee_id_eepro10p1 0x31
+
+#define TX_TIMEOUT ((4*HZ)/10)
+
+/* Index to functions, as function prototypes. */
+
+static int eepro_probe1(struct net_device *dev, int autoprobe);
+static int eepro_open(struct net_device *dev);
+static netdev_tx_t eepro_send_packet(struct sk_buff *skb,
+ struct net_device *dev);
+static irqreturn_t eepro_interrupt(int irq, void *dev_id);
+static void eepro_rx(struct net_device *dev);
+static void eepro_transmit_interrupt(struct net_device *dev);
+static int eepro_close(struct net_device *dev);
+static void set_multicast_list(struct net_device *dev);
+static void eepro_tx_timeout (struct net_device *dev);
+
+static int read_eeprom(int ioaddr, int location, struct net_device *dev);
+static int hardware_send_packet(struct net_device *dev, void *buf, short length);
+static int eepro_grab_irq(struct net_device *dev);
+
+/*
+ Details of the i82595.
+
+You will need either the datasheet or the user manual to understand what
+is going on here. The 82595 is very different from the 82586, 82593.
+
+The receive algorithm in eepro_rx() is just an implementation of the
+RCV ring structure that the Intel 82595 imposes at the hardware level.
+The receive buffer is set at 24K, and the transmit buffer is 8K. I
+am assuming that the total buffer memory is 32K, which is true for the
+Intel EtherExpress Pro/10. If it is less than that on a generic card,
+the driver will be broken.
+
+The transmit algorithm in the hardware_send_packet() is similar to the
+one in the eepro_rx(). The transmit buffer is a ring linked list.
+I just queue the next available packet to the end of the list. In my
+system, the 82595 is so fast that the list seems to always contain a
+single packet. In other systems with faster computers and more congested
+network traffics, the ring linked list should improve performance by
+allowing up to 8K worth of packets to be queued.
+
+The sizes of the receive and transmit buffers can now be changed via lilo
+or insmod. Lilo uses the appended line "ether=io,irq,debug,rx-buffer,eth0"
+where rx-buffer is in KB unit. Modules uses the parameter mem which is
+also in KB unit, for example "insmod io=io-address irq=0 mem=rx-buffer."
+The receive buffer has to be more than 3K or less than 29K. Otherwise,
+it is reset to the default of 24K, and, hence, 8K for the trasnmit
+buffer (transmit-buffer = 32K - receive-buffer).
+
+*/
+#define RAM_SIZE 0x8000
+
+#define RCV_HEADER 8
+#define RCV_DEFAULT_RAM 0x6000
+
+#define XMT_HEADER 8
+#define XMT_DEFAULT_RAM (RAM_SIZE - RCV_DEFAULT_RAM)
+
+#define XMT_START_PRO RCV_DEFAULT_RAM
+#define XMT_START_10 0x0000
+#define RCV_START_PRO 0x0000
+#define RCV_START_10 XMT_DEFAULT_RAM
+
+#define RCV_DONE 0x0008
+#define RX_OK 0x2000
+#define RX_ERROR 0x0d81
+
+#define TX_DONE_BIT 0x0080
+#define TX_OK 0x2000
+#define CHAIN_BIT 0x8000
+#define XMT_STATUS 0x02
+#define XMT_CHAIN 0x04
+#define XMT_COUNT 0x06
+
+#define BANK0_SELECT 0x00
+#define BANK1_SELECT 0x40
+#define BANK2_SELECT 0x80
+
+/* Bank 0 registers */
+#define COMMAND_REG 0x00 /* Register 0 */
+#define MC_SETUP 0x03
+#define XMT_CMD 0x04
+#define DIAGNOSE_CMD 0x07
+#define RCV_ENABLE_CMD 0x08
+#define RCV_DISABLE_CMD 0x0a
+#define STOP_RCV_CMD 0x0b
+#define RESET_CMD 0x0e
+#define POWER_DOWN_CMD 0x18
+#define RESUME_XMT_CMD 0x1c
+#define SEL_RESET_CMD 0x1e
+#define STATUS_REG 0x01 /* Register 1 */
+#define RX_INT 0x02
+#define TX_INT 0x04
+#define EXEC_STATUS 0x30
+#define ID_REG 0x02 /* Register 2 */
+#define R_ROBIN_BITS 0xc0 /* round robin counter */
+#define ID_REG_MASK 0x2c
+#define ID_REG_SIG 0x24
+#define AUTO_ENABLE 0x10
+#define INT_MASK_REG 0x03 /* Register 3 */
+#define RX_STOP_MASK 0x01
+#define RX_MASK 0x02
+#define TX_MASK 0x04
+#define EXEC_MASK 0x08
+#define ALL_MASK 0x0f
+#define IO_32_BIT 0x10
+#define RCV_BAR 0x04 /* The following are word (16-bit) registers */
+#define RCV_STOP 0x06
+
+#define XMT_BAR_PRO 0x0a
+#define XMT_BAR_10 0x0b
+
+#define HOST_ADDRESS_REG 0x0c
+#define IO_PORT 0x0e
+#define IO_PORT_32_BIT 0x0c
+
+/* Bank 1 registers */
+#define REG1 0x01
+#define WORD_WIDTH 0x02
+#define INT_ENABLE 0x80
+#define INT_NO_REG 0x02
+#define RCV_LOWER_LIMIT_REG 0x08
+#define RCV_UPPER_LIMIT_REG 0x09
+
+#define XMT_LOWER_LIMIT_REG_PRO 0x0a
+#define XMT_UPPER_LIMIT_REG_PRO 0x0b
+#define XMT_LOWER_LIMIT_REG_10 0x0b
+#define XMT_UPPER_LIMIT_REG_10 0x0a
+
+/* Bank 2 registers */
+#define XMT_Chain_Int 0x20 /* Interrupt at the end of the transmit chain */
+#define XMT_Chain_ErrStop 0x40 /* Interrupt at the end of the chain even if there are errors */
+#define RCV_Discard_BadFrame 0x80 /* Throw bad frames away, and continue to receive others */
+#define REG2 0x02
+#define PRMSC_Mode 0x01
+#define Multi_IA 0x20
+#define REG3 0x03
+#define TPE_BIT 0x04
+#define BNC_BIT 0x20
+#define REG13 0x0d
+#define FDX 0x00
+#define A_N_ENABLE 0x02
+
+#define I_ADD_REG0 0x04
+#define I_ADD_REG1 0x05
+#define I_ADD_REG2 0x06
+#define I_ADD_REG3 0x07
+#define I_ADD_REG4 0x08
+#define I_ADD_REG5 0x09
+
+#define EEPROM_REG_PRO 0x0a
+#define EEPROM_REG_10 0x0b
+
+#define EESK 0x01
+#define EECS 0x02
+#define EEDI 0x04
+#define EEDO 0x08
+
+/* do a full reset */
+#define eepro_reset(ioaddr) outb(RESET_CMD, ioaddr)
+
+/* do a nice reset */
+#define eepro_sel_reset(ioaddr) { \
+ outb(SEL_RESET_CMD, ioaddr); \
+ SLOW_DOWN; \
+ SLOW_DOWN; \
+ }
+
+/* disable all interrupts */
+#define eepro_dis_int(ioaddr) outb(ALL_MASK, ioaddr + INT_MASK_REG)
+
+/* clear all interrupts */
+#define eepro_clear_int(ioaddr) outb(ALL_MASK, ioaddr + STATUS_REG)
+
+/* enable tx/rx */
+#define eepro_en_int(ioaddr) outb(ALL_MASK & ~(RX_MASK | TX_MASK), \
+ ioaddr + INT_MASK_REG)
+
+/* enable exec event interrupt */
+#define eepro_en_intexec(ioaddr) outb(ALL_MASK & ~(EXEC_MASK), ioaddr + INT_MASK_REG)
+
+/* enable rx */
+#define eepro_en_rx(ioaddr) outb(RCV_ENABLE_CMD, ioaddr)
+
+/* disable rx */
+#define eepro_dis_rx(ioaddr) outb(RCV_DISABLE_CMD, ioaddr)
+
+/* switch bank */
+#define eepro_sw2bank0(ioaddr) outb(BANK0_SELECT, ioaddr)
+#define eepro_sw2bank1(ioaddr) outb(BANK1_SELECT, ioaddr)
+#define eepro_sw2bank2(ioaddr) outb(BANK2_SELECT, ioaddr)
+
+/* enable interrupt line */
+#define eepro_en_intline(ioaddr) outb(inb(ioaddr + REG1) | INT_ENABLE,\
+ ioaddr + REG1)
+
+/* disable interrupt line */
+#define eepro_dis_intline(ioaddr) outb(inb(ioaddr + REG1) & 0x7f, \
+ ioaddr + REG1);
+
+/* set diagnose flag */
+#define eepro_diag(ioaddr) outb(DIAGNOSE_CMD, ioaddr)
+
+/* ack for rx int */
+#define eepro_ack_rx(ioaddr) outb (RX_INT, ioaddr + STATUS_REG)
+
+/* ack for tx int */
+#define eepro_ack_tx(ioaddr) outb (TX_INT, ioaddr + STATUS_REG)
+
+/* a complete sel reset */
+#define eepro_complete_selreset(ioaddr) { \
+ dev->stats.tx_errors++;\
+ eepro_sel_reset(ioaddr);\
+ lp->tx_end = \
+ lp->xmt_lower_limit;\
+ lp->tx_start = lp->tx_end;\
+ lp->tx_last = 0;\
+ dev->trans_start = jiffies;\
+ netif_wake_queue(dev);\
+ eepro_en_rx(ioaddr);\
+ }
+
+/* Check for a network adaptor of this type, and return '0' if one exists.
+ If dev->base_addr == 0, probe all likely locations.
+ If dev->base_addr == 1, always return failure.
+ If dev->base_addr == 2, allocate space for the device and return success
+ (detachable devices only).
+ */
+static int __init do_eepro_probe(struct net_device *dev)
+{
+ int i;
+ int base_addr = dev->base_addr;
+ int irq = dev->irq;
+
+#ifdef PnPWakeup
+ /* XXXX for multiple cards should this only be run once? */
+
+ /* Wakeup: */
+ #define WakeupPort 0x279
+ #define WakeupSeq {0x6A, 0xB5, 0xDA, 0xED, 0xF6, 0xFB, 0x7D, 0xBE,\
+ 0xDF, 0x6F, 0x37, 0x1B, 0x0D, 0x86, 0xC3, 0x61,\
+ 0xB0, 0x58, 0x2C, 0x16, 0x8B, 0x45, 0xA2, 0xD1,\
+ 0xE8, 0x74, 0x3A, 0x9D, 0xCE, 0xE7, 0x73, 0x43}
+
+ {
+ unsigned short int WS[32]=WakeupSeq;
+
+ if (request_region(WakeupPort, 2, "eepro wakeup")) {
+ if (net_debug>5)
+ printk(KERN_DEBUG "Waking UP\n");
+
+ outb_p(0,WakeupPort);
+ outb_p(0,WakeupPort);
+ for (i=0; i<32; i++) {
+ outb_p(WS[i],WakeupPort);
+ if (net_debug>5) printk(KERN_DEBUG ": %#x ",WS[i]);
+ }
+
+ release_region(WakeupPort, 2);
+ } else
+ printk(KERN_WARNING "PnP wakeup region busy!\n");
+ }
+#endif
+
+ if (base_addr > 0x1ff) /* Check a single specified location. */
+ return eepro_probe1(dev, 0);
+
+ else if (base_addr != 0) /* Don't probe at all. */
+ return -ENXIO;
+
+ for (i = 0; eepro_portlist[i]; i++) {
+ dev->base_addr = eepro_portlist[i];
+ dev->irq = irq;
+ if (eepro_probe1(dev, 1) == 0)
+ return 0;
+ }
+
+ return -ENODEV;
+}
+
+#ifndef MODULE
+struct net_device * __init eepro_probe(int unit)
+{
+ struct net_device *dev = alloc_etherdev(sizeof(struct eepro_local));
+ int err;
+
+ if (!dev)
+ return ERR_PTR(-ENODEV);
+
+ sprintf(dev->name, "eth%d", unit);
+ netdev_boot_setup_check(dev);
+
+ err = do_eepro_probe(dev);
+ if (err)
+ goto out;
+ return dev;
+out:
+ free_netdev(dev);
+ return ERR_PTR(err);
+}
+#endif
+
+static void __init printEEPROMInfo(struct net_device *dev)
+{
+ struct eepro_local *lp = netdev_priv(dev);
+ int ioaddr = dev->base_addr;
+ unsigned short Word;
+ int i,j;
+
+ j = ee_Checksum;
+ for (i = 0; i < 8; i++)
+ j += lp->word[i];
+ for ( ; i < ee_SIZE; i++)
+ j += read_eeprom(ioaddr, i, dev);
+
+ printk(KERN_DEBUG "Checksum: %#x\n",j&0xffff);
+
+ Word = lp->word[0];
+ printk(KERN_DEBUG "Word0:\n");
+ printk(KERN_DEBUG " Plug 'n Pray: %d\n",GetBit(Word,ee_PnP));
+ printk(KERN_DEBUG " Buswidth: %d\n",(GetBit(Word,ee_BusWidth)+1)*8 );
+ printk(KERN_DEBUG " AutoNegotiation: %d\n",GetBit(Word,ee_AutoNeg));
+ printk(KERN_DEBUG " IO Address: %#x\n", (Word>>ee_IO0)<<4);
+
+ if (net_debug>4) {
+ Word = lp->word[1];
+ printk(KERN_DEBUG "Word1:\n");
+ printk(KERN_DEBUG " INT: %d\n", Word & ee_IntMask);
+ printk(KERN_DEBUG " LI: %d\n", GetBit(Word,ee_LI));
+ printk(KERN_DEBUG " PC: %d\n", GetBit(Word,ee_PC));
+ printk(KERN_DEBUG " TPE/AUI: %d\n", GetBit(Word,ee_TPE_AUI));
+ printk(KERN_DEBUG " Jabber: %d\n", GetBit(Word,ee_Jabber));
+ printk(KERN_DEBUG " AutoPort: %d\n", !GetBit(Word,ee_AutoPort));
+ printk(KERN_DEBUG " Duplex: %d\n", GetBit(Word,ee_Duplex));
+ }
+
+ Word = lp->word[5];
+ printk(KERN_DEBUG "Word5:\n");
+ printk(KERN_DEBUG " BNC: %d\n",GetBit(Word,ee_BNC_TPE));
+ printk(KERN_DEBUG " NumConnectors: %d\n",GetBit(Word,ee_NumConn));
+ printk(KERN_DEBUG " Has ");
+ if (GetBit(Word,ee_PortTPE)) printk(KERN_DEBUG "TPE ");
+ if (GetBit(Word,ee_PortBNC)) printk(KERN_DEBUG "BNC ");
+ if (GetBit(Word,ee_PortAUI)) printk(KERN_DEBUG "AUI ");
+ printk(KERN_DEBUG "port(s)\n");
+
+ Word = lp->word[6];
+ printk(KERN_DEBUG "Word6:\n");
+ printk(KERN_DEBUG " Stepping: %d\n",Word & ee_StepMask);
+ printk(KERN_DEBUG " BoardID: %d\n",Word>>ee_BoardID);
+
+ Word = lp->word[7];
+ printk(KERN_DEBUG "Word7:\n");
+ printk(KERN_DEBUG " INT to IRQ:\n");
+
+ for (i=0, j=0; i<15; i++)
+ if (GetBit(Word,i)) printk(KERN_DEBUG " INT%d -> IRQ %d;",j++,i);
+
+ printk(KERN_DEBUG "\n");
+}
+
+/* function to recalculate the limits of buffer based on rcv_ram */
+static void eepro_recalc (struct net_device *dev)
+{
+ struct eepro_local * lp;
+
+ lp = netdev_priv(dev);
+ lp->xmt_ram = RAM_SIZE - lp->rcv_ram;
+
+ if (lp->eepro == LAN595FX_10ISA) {
+ lp->xmt_lower_limit = XMT_START_10;
+ lp->xmt_upper_limit = (lp->xmt_ram - 2);
+ lp->rcv_lower_limit = lp->xmt_ram;
+ lp->rcv_upper_limit = (RAM_SIZE - 2);
+ }
+ else {
+ lp->rcv_lower_limit = RCV_START_PRO;
+ lp->rcv_upper_limit = (lp->rcv_ram - 2);
+ lp->xmt_lower_limit = lp->rcv_ram;
+ lp->xmt_upper_limit = (RAM_SIZE - 2);
+ }
+}
+
+/* prints boot-time info */
+static void __init eepro_print_info (struct net_device *dev)
+{
+ struct eepro_local * lp = netdev_priv(dev);
+ int i;
+ const char * ifmap[] = {"AUI", "10Base2", "10BaseT"};
+
+ i = inb(dev->base_addr + ID_REG);
+ printk(KERN_DEBUG " id: %#x ",i);
+ printk(" io: %#x ", (unsigned)dev->base_addr);
+
+ switch (lp->eepro) {
+ case LAN595FX_10ISA:
+ printk("%s: Intel EtherExpress 10 ISA\n at %#x,",
+ dev->name, (unsigned)dev->base_addr);
+ break;
+ case LAN595FX:
+ printk("%s: Intel EtherExpress Pro/10+ ISA\n at %#x,",
+ dev->name, (unsigned)dev->base_addr);
+ break;
+ case LAN595TX:
+ printk("%s: Intel EtherExpress Pro/10 ISA at %#x,",
+ dev->name, (unsigned)dev->base_addr);
+ break;
+ case LAN595:
+ printk("%s: Intel 82595-based lan card at %#x,",
+ dev->name, (unsigned)dev->base_addr);
+ break;
+ }
+
+ printk(" %pM", dev->dev_addr);
+
+ if (net_debug > 3)
+ printk(KERN_DEBUG ", %dK RCV buffer",
+ (int)(lp->rcv_ram)/1024);
+
+ if (dev->irq > 2)
+ printk(", IRQ %d, %s.\n", dev->irq, ifmap[dev->if_port]);
+ else
+ printk(", %s.\n", ifmap[dev->if_port]);
+
+ if (net_debug > 3) {
+ i = lp->word[5];
+ if (i & 0x2000) /* bit 13 of EEPROM word 5 */
+ printk(KERN_DEBUG "%s: Concurrent Processing is "
+ "enabled but not used!\n", dev->name);
+ }
+
+ /* Check the station address for the manufacturer's code */
+ if (net_debug>3)
+ printEEPROMInfo(dev);
+}
+
+static const struct ethtool_ops eepro_ethtool_ops;
+
+static const struct net_device_ops eepro_netdev_ops = {
+ .ndo_open = eepro_open,
+ .ndo_stop = eepro_close,
+ .ndo_start_xmit = eepro_send_packet,
+ .ndo_set_rx_mode = set_multicast_list,
+ .ndo_tx_timeout = eepro_tx_timeout,
+ .ndo_change_mtu = eth_change_mtu,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_validate_addr = eth_validate_addr,
+};
+
+/* This is the real probe routine. Linux has a history of friendly device
+ probes on the ISA bus. A good device probe avoids doing writes, and
+ verifies that the correct device exists and functions. */
+
+static int __init eepro_probe1(struct net_device *dev, int autoprobe)
+{
+ unsigned short station_addr[3], id, counter;
+ int i;
+ struct eepro_local *lp;
+ int ioaddr = dev->base_addr;
+ int err;
+
+ /* Grab the region so we can find another board if autoIRQ fails. */
+ if (!request_region(ioaddr, EEPRO_IO_EXTENT, DRV_NAME)) {
+ if (!autoprobe)
+ printk(KERN_WARNING "EEPRO: io-port 0x%04x in use\n",
+ ioaddr);
+ return -EBUSY;
+ }
+
+ /* Now, we are going to check for the signature of the
+ ID_REG (register 2 of bank 0) */
+
+ id = inb(ioaddr + ID_REG);
+
+ if ((id & ID_REG_MASK) != ID_REG_SIG)
+ goto exit;
+
+ /* We seem to have the 82595 signature, let's
+ play with its counter (last 2 bits of
+ register 2 of bank 0) to be sure. */
+
+ counter = id & R_ROBIN_BITS;
+
+ if ((inb(ioaddr + ID_REG) & R_ROBIN_BITS) != (counter + 0x40))
+ goto exit;
+
+ lp = netdev_priv(dev);
+ memset(lp, 0, sizeof(struct eepro_local));
+ lp->xmt_bar = XMT_BAR_PRO;
+ lp->xmt_lower_limit_reg = XMT_LOWER_LIMIT_REG_PRO;
+ lp->xmt_upper_limit_reg = XMT_UPPER_LIMIT_REG_PRO;
+ lp->eeprom_reg = EEPROM_REG_PRO;
+ spin_lock_init(&lp->lock);
+
+ /* Now, get the ethernet hardware address from
+ the EEPROM */
+ station_addr[0] = read_eeprom(ioaddr, 2, dev);
+
+ /* FIXME - find another way to know that we've found
+ * an Etherexpress 10
+ */
+ if (station_addr[0] == 0x0000 || station_addr[0] == 0xffff) {
+ lp->eepro = LAN595FX_10ISA;
+ lp->eeprom_reg = EEPROM_REG_10;
+ lp->xmt_lower_limit_reg = XMT_LOWER_LIMIT_REG_10;
+ lp->xmt_upper_limit_reg = XMT_UPPER_LIMIT_REG_10;
+ lp->xmt_bar = XMT_BAR_10;
+ station_addr[0] = read_eeprom(ioaddr, 2, dev);
+ }
+
+ /* get all words at once. will be used here and for ethtool */
+ for (i = 0; i < 8; i++) {
+ lp->word[i] = read_eeprom(ioaddr, i, dev);
+ }
+ station_addr[1] = lp->word[3];
+ station_addr[2] = lp->word[4];
+
+ if (!lp->eepro) {
+ if (lp->word[7] == ee_FX_INT2IRQ)
+ lp->eepro = 2;
+ else if (station_addr[2] == SA_ADDR1)
+ lp->eepro = 1;
+ }
+
+ /* Fill in the 'dev' fields. */
+ for (i=0; i < 6; i++)
+ dev->dev_addr[i] = ((unsigned char *) station_addr)[5-i];
+
+ /* RX buffer must be more than 3K and less than 29K */
+ if (dev->mem_end < 3072 || dev->mem_end > 29696)
+ lp->rcv_ram = RCV_DEFAULT_RAM;
+
+ /* calculate {xmt,rcv}_{lower,upper}_limit */
+ eepro_recalc(dev);
+
+ if (GetBit(lp->word[5], ee_BNC_TPE))
+ dev->if_port = BNC;
+ else
+ dev->if_port = TPE;
+
+ if (dev->irq < 2 && lp->eepro != 0) {
+ /* Mask off INT number */
+ int count = lp->word[1] & 7;
+ unsigned irqMask = lp->word[7];
+
+ while (count--)
+ irqMask &= irqMask - 1;
+
+ count = ffs(irqMask);
+
+ if (count)
+ dev->irq = count - 1;
+
+ if (dev->irq < 2) {
+ printk(KERN_ERR " Duh! illegal interrupt vector stored in EEPROM.\n");
+ goto exit;
+ } else if (dev->irq == 2) {
+ dev->irq = 9;
+ }
+ }
+
+ dev->netdev_ops = &eepro_netdev_ops;
+ dev->watchdog_timeo = TX_TIMEOUT;
+ dev->ethtool_ops = &eepro_ethtool_ops;
+
+ /* print boot time info */
+ eepro_print_info(dev);
+
+ /* reset 82595 */
+ eepro_reset(ioaddr);
+
+ err = register_netdev(dev);
+ if (err)
+ goto err;
+ return 0;
+exit:
+ err = -ENODEV;
+err:
+ release_region(dev->base_addr, EEPRO_IO_EXTENT);
+ return err;
+}
+
+/* Open/initialize the board. This is called (in the current kernel)
+ sometime after booting when the 'ifconfig' program is run.
+
+ This routine should set everything up anew at each open, even
+ registers that "should" only need to be set once at boot, so that
+ there is non-reboot way to recover if something goes wrong.
+ */
+
+static const char irqrmap[] = {-1,-1,0,1,-1,2,-1,-1,-1,0,3,4,-1,-1,-1,-1};
+static const char irqrmap2[] = {-1,-1,4,0,1,2,-1,3,-1,4,5,6,7,-1,-1,-1};
+static int eepro_grab_irq(struct net_device *dev)
+{
+ static const int irqlist[] = { 3, 4, 5, 7, 9, 10, 11, 12, 0 };
+ const int *irqp = irqlist;
+ int temp_reg, ioaddr = dev->base_addr;
+
+ eepro_sw2bank1(ioaddr); /* be CAREFUL, BANK 1 now */
+
+ /* Enable the interrupt line. */
+ eepro_en_intline(ioaddr);
+
+ /* be CAREFUL, BANK 0 now */
+ eepro_sw2bank0(ioaddr);
+
+ /* clear all interrupts */
+ eepro_clear_int(ioaddr);
+
+ /* Let EXEC event to interrupt */
+ eepro_en_intexec(ioaddr);
+
+ do {
+ eepro_sw2bank1(ioaddr); /* be CAREFUL, BANK 1 now */
+
+ temp_reg = inb(ioaddr + INT_NO_REG);
+ outb((temp_reg & 0xf8) | irqrmap[*irqp], ioaddr + INT_NO_REG);
+
+ eepro_sw2bank0(ioaddr); /* Switch back to Bank 0 */
+
+ if (request_irq (*irqp, NULL, IRQF_SHARED, "bogus", dev) != EBUSY) {
+ unsigned long irq_mask;
+ /* Twinkle the interrupt, and check if it's seen */
+ irq_mask = probe_irq_on();
+
+ eepro_diag(ioaddr); /* RESET the 82595 */
+ mdelay(20);
+
+ if (*irqp == probe_irq_off(irq_mask)) /* It's a good IRQ line */
+ break;
+
+ /* clear all interrupts */
+ eepro_clear_int(ioaddr);
+ }
+ } while (*++irqp);
+
+ eepro_sw2bank1(ioaddr); /* Switch back to Bank 1 */
+
+ /* Disable the physical interrupt line. */
+ eepro_dis_intline(ioaddr);
+
+ eepro_sw2bank0(ioaddr); /* Switch back to Bank 0 */
+
+ /* Mask all the interrupts. */
+ eepro_dis_int(ioaddr);
+
+ /* clear all interrupts */
+ eepro_clear_int(ioaddr);
+
+ return dev->irq;
+}
+
+static int eepro_open(struct net_device *dev)
+{
+ unsigned short temp_reg, old8, old9;
+ int irqMask;
+ int i, ioaddr = dev->base_addr;
+ struct eepro_local *lp = netdev_priv(dev);
+
+ if (net_debug > 3)
+ printk(KERN_DEBUG "%s: entering eepro_open routine.\n", dev->name);
+
+ irqMask = lp->word[7];
+
+ if (lp->eepro == LAN595FX_10ISA) {
+ if (net_debug > 3) printk(KERN_DEBUG "p->eepro = 3;\n");
+ }
+ else if (irqMask == ee_FX_INT2IRQ) /* INT to IRQ Mask */
+ {
+ lp->eepro = 2; /* Yes, an Intel EtherExpress Pro/10+ */
+ if (net_debug > 3) printk(KERN_DEBUG "p->eepro = 2;\n");
+ }
+
+ else if ((dev->dev_addr[0] == SA_ADDR0 &&
+ dev->dev_addr[1] == SA_ADDR1 &&
+ dev->dev_addr[2] == SA_ADDR2))
+ {
+ lp->eepro = 1;
+ if (net_debug > 3) printk(KERN_DEBUG "p->eepro = 1;\n");
+ } /* Yes, an Intel EtherExpress Pro/10 */
+
+ else lp->eepro = 0; /* No, it is a generic 82585 lan card */
+
+ /* Get the interrupt vector for the 82595 */
+ if (dev->irq < 2 && eepro_grab_irq(dev) == 0) {
+ printk(KERN_ERR "%s: unable to get IRQ %d.\n", dev->name, dev->irq);
+ return -EAGAIN;
+ }
+
+ if (request_irq(dev->irq , eepro_interrupt, 0, dev->name, dev)) {
+ printk(KERN_ERR "%s: unable to get IRQ %d.\n", dev->name, dev->irq);
+ return -EAGAIN;
+ }
+
+ /* Initialize the 82595. */
+
+ eepro_sw2bank2(ioaddr); /* be CAREFUL, BANK 2 now */
+ temp_reg = inb(ioaddr + lp->eeprom_reg);
+
+ lp->stepping = temp_reg >> 5; /* Get the stepping number of the 595 */
+
+ if (net_debug > 3)
+ printk(KERN_DEBUG "The stepping of the 82595 is %d\n", lp->stepping);
+
+ if (temp_reg & 0x10) /* Check the TurnOff Enable bit */
+ outb(temp_reg & 0xef, ioaddr + lp->eeprom_reg);
+ for (i=0; i < 6; i++)
+ outb(dev->dev_addr[i] , ioaddr + I_ADD_REG0 + i);
+
+ temp_reg = inb(ioaddr + REG1); /* Setup Transmit Chaining */
+ outb(temp_reg | XMT_Chain_Int | XMT_Chain_ErrStop /* and discard bad RCV frames */
+ | RCV_Discard_BadFrame, ioaddr + REG1);
+
+ temp_reg = inb(ioaddr + REG2); /* Match broadcast */
+ outb(temp_reg | 0x14, ioaddr + REG2);
+
+ temp_reg = inb(ioaddr + REG3);
+ outb(temp_reg & 0x3f, ioaddr + REG3); /* clear test mode */
+
+ /* Set the receiving mode */
+ eepro_sw2bank1(ioaddr); /* be CAREFUL, BANK 1 now */
+
+ /* Set the interrupt vector */
+ temp_reg = inb(ioaddr + INT_NO_REG);
+ if (lp->eepro == LAN595FX || lp->eepro == LAN595FX_10ISA)
+ outb((temp_reg & 0xf8) | irqrmap2[dev->irq], ioaddr + INT_NO_REG);
+ else outb((temp_reg & 0xf8) | irqrmap[dev->irq], ioaddr + INT_NO_REG);
+
+
+ temp_reg = inb(ioaddr + INT_NO_REG);
+ if (lp->eepro == LAN595FX || lp->eepro == LAN595FX_10ISA)
+ outb((temp_reg & 0xf0) | irqrmap2[dev->irq] | 0x08,ioaddr+INT_NO_REG);
+ else outb((temp_reg & 0xf8) | irqrmap[dev->irq], ioaddr + INT_NO_REG);
+
+ if (net_debug > 3)
+ printk(KERN_DEBUG "eepro_open: content of INT Reg is %x\n", temp_reg);
+
+
+ /* Initialize the RCV and XMT upper and lower limits */
+ outb(lp->rcv_lower_limit >> 8, ioaddr + RCV_LOWER_LIMIT_REG);
+ outb(lp->rcv_upper_limit >> 8, ioaddr + RCV_UPPER_LIMIT_REG);
+ outb(lp->xmt_lower_limit >> 8, ioaddr + lp->xmt_lower_limit_reg);
+ outb(lp->xmt_upper_limit >> 8, ioaddr + lp->xmt_upper_limit_reg);
+
+ /* Enable the interrupt line. */
+ eepro_en_intline(ioaddr);
+
+ /* Switch back to Bank 0 */
+ eepro_sw2bank0(ioaddr);
+
+ /* Let RX and TX events to interrupt */
+ eepro_en_int(ioaddr);
+
+ /* clear all interrupts */
+ eepro_clear_int(ioaddr);
+
+ /* Initialize RCV */
+ outw(lp->rcv_lower_limit, ioaddr + RCV_BAR);
+ lp->rx_start = lp->rcv_lower_limit;
+ outw(lp->rcv_upper_limit | 0xfe, ioaddr + RCV_STOP);
+
+ /* Initialize XMT */
+ outw(lp->xmt_lower_limit, ioaddr + lp->xmt_bar);
+ lp->tx_start = lp->tx_end = lp->xmt_lower_limit;
+ lp->tx_last = 0;
+
+ /* Check for the i82595TX and i82595FX */
+ old8 = inb(ioaddr + 8);
+ outb(~old8, ioaddr + 8);
+
+ if ((temp_reg = inb(ioaddr + 8)) == old8) {
+ if (net_debug > 3)
+ printk(KERN_DEBUG "i82595 detected!\n");
+ lp->version = LAN595;
+ }
+ else {
+ lp->version = LAN595TX;
+ outb(old8, ioaddr + 8);
+ old9 = inb(ioaddr + 9);
+
+ if (irqMask==ee_FX_INT2IRQ) {
+ if (net_debug > 3) {
+ printk(KERN_DEBUG "IrqMask: %#x\n",irqMask);
+ printk(KERN_DEBUG "i82595FX detected!\n");
+ }
+ lp->version = LAN595FX;
+ outb(old9, ioaddr + 9);
+ if (dev->if_port != TPE) { /* Hopefully, this will fix the
+ problem of using Pentiums and
+ pro/10 w/ BNC. */
+ eepro_sw2bank2(ioaddr); /* be CAREFUL, BANK 2 now */
+ temp_reg = inb(ioaddr + REG13);
+ /* disable the full duplex mode since it is not
+ applicable with the 10Base2 cable. */
+ outb(temp_reg & ~(FDX | A_N_ENABLE), REG13);
+ eepro_sw2bank0(ioaddr); /* be CAREFUL, BANK 0 now */
+ }
+ }
+ else if (net_debug > 3) {
+ printk(KERN_DEBUG "temp_reg: %#x ~old9: %#x\n",temp_reg,((~old9)&0xff));
+ printk(KERN_DEBUG "i82595TX detected!\n");
+ }
+ }
+
+ eepro_sel_reset(ioaddr);
+
+ netif_start_queue(dev);
+
+ if (net_debug > 3)
+ printk(KERN_DEBUG "%s: exiting eepro_open routine.\n", dev->name);
+
+ /* enabling rx */
+ eepro_en_rx(ioaddr);
+
+ return 0;
+}
+
+static void eepro_tx_timeout (struct net_device *dev)
+{
+ struct eepro_local *lp = netdev_priv(dev);
+ int ioaddr = dev->base_addr;
+
+ /* if (net_debug > 1) */
+ printk (KERN_ERR "%s: transmit timed out, %s?\n", dev->name,
+ "network cable problem");
+ /* This is not a duplicate. One message for the console,
+ one for the log file */
+ printk (KERN_DEBUG "%s: transmit timed out, %s?\n", dev->name,
+ "network cable problem");
+ eepro_complete_selreset(ioaddr);
+}
+
+
+static netdev_tx_t eepro_send_packet(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ struct eepro_local *lp = netdev_priv(dev);
+ unsigned long flags;
+ int ioaddr = dev->base_addr;
+ short length = skb->len;
+
+ if (net_debug > 5)
+ printk(KERN_DEBUG "%s: entering eepro_send_packet routine.\n", dev->name);
+
+ if (length < ETH_ZLEN) {
+ if (skb_padto(skb, ETH_ZLEN))
+ return NETDEV_TX_OK;
+ length = ETH_ZLEN;
+ }
+ netif_stop_queue (dev);
+
+ eepro_dis_int(ioaddr);
+ spin_lock_irqsave(&lp->lock, flags);
+
+ {
+ unsigned char *buf = skb->data;
+
+ if (hardware_send_packet(dev, buf, length))
+ /* we won't wake queue here because we're out of space */
+ dev->stats.tx_dropped++;
+ else {
+ dev->stats.tx_bytes+=skb->len;
+ netif_wake_queue(dev);
+ }
+
+ }
+
+ dev_kfree_skb (skb);
+
+ /* You might need to clean up and record Tx statistics here. */
+ /* dev->stats.tx_aborted_errors++; */
+
+ if (net_debug > 5)
+ printk(KERN_DEBUG "%s: exiting eepro_send_packet routine.\n", dev->name);
+
+ eepro_en_int(ioaddr);
+ spin_unlock_irqrestore(&lp->lock, flags);
+
+ return NETDEV_TX_OK;
+}
+
+
+/* The typical workload of the driver:
+ Handle the network interface interrupts. */
+
+static irqreturn_t
+eepro_interrupt(int irq, void *dev_id)
+{
+ struct net_device *dev = dev_id;
+ struct eepro_local *lp;
+ int ioaddr, status, boguscount = 20;
+ int handled = 0;
+
+ lp = netdev_priv(dev);
+
+ spin_lock(&lp->lock);
+
+ if (net_debug > 5)
+ printk(KERN_DEBUG "%s: entering eepro_interrupt routine.\n", dev->name);
+
+ ioaddr = dev->base_addr;
+
+ while (((status = inb(ioaddr + STATUS_REG)) & (RX_INT|TX_INT)) && (boguscount--))
+ {
+ handled = 1;
+ if (status & RX_INT) {
+ if (net_debug > 4)
+ printk(KERN_DEBUG "%s: packet received interrupt.\n", dev->name);
+
+ eepro_dis_int(ioaddr);
+
+ /* Get the received packets */
+ eepro_ack_rx(ioaddr);
+ eepro_rx(dev);
+
+ eepro_en_int(ioaddr);
+ }
+ if (status & TX_INT) {
+ if (net_debug > 4)
+ printk(KERN_DEBUG "%s: packet transmit interrupt.\n", dev->name);
+
+
+ eepro_dis_int(ioaddr);
+
+ /* Process the status of transmitted packets */
+ eepro_ack_tx(ioaddr);
+ eepro_transmit_interrupt(dev);
+
+ eepro_en_int(ioaddr);
+ }
+ }
+
+ if (net_debug > 5)
+ printk(KERN_DEBUG "%s: exiting eepro_interrupt routine.\n", dev->name);
+
+ spin_unlock(&lp->lock);
+ return IRQ_RETVAL(handled);
+}
+
+static int eepro_close(struct net_device *dev)
+{
+ struct eepro_local *lp = netdev_priv(dev);
+ int ioaddr = dev->base_addr;
+ short temp_reg;
+
+ netif_stop_queue(dev);
+
+ eepro_sw2bank1(ioaddr); /* Switch back to Bank 1 */
+
+ /* Disable the physical interrupt line. */
+ temp_reg = inb(ioaddr + REG1);
+ outb(temp_reg & 0x7f, ioaddr + REG1);
+
+ eepro_sw2bank0(ioaddr); /* Switch back to Bank 0 */
+
+ /* Flush the Tx and disable Rx. */
+ outb(STOP_RCV_CMD, ioaddr);
+ lp->tx_start = lp->tx_end = lp->xmt_lower_limit;
+ lp->tx_last = 0;
+
+ /* Mask all the interrupts. */
+ eepro_dis_int(ioaddr);
+
+ /* clear all interrupts */
+ eepro_clear_int(ioaddr);
+
+ /* Reset the 82595 */
+ eepro_reset(ioaddr);
+
+ /* release the interrupt */
+ free_irq(dev->irq, dev);
+
+ /* Update the statistics here. What statistics? */
+
+ return 0;
+}
+
+/* Set or clear the multicast filter for this adaptor.
+ */
+static void
+set_multicast_list(struct net_device *dev)
+{
+ struct eepro_local *lp = netdev_priv(dev);
+ short ioaddr = dev->base_addr;
+ unsigned short mode;
+ struct netdev_hw_addr *ha;
+ int mc_count = netdev_mc_count(dev);
+
+ if (dev->flags&(IFF_ALLMULTI|IFF_PROMISC) || mc_count > 63)
+ {
+ eepro_sw2bank2(ioaddr); /* be CAREFUL, BANK 2 now */
+ mode = inb(ioaddr + REG2);
+ outb(mode | PRMSC_Mode, ioaddr + REG2);
+ mode = inb(ioaddr + REG3);
+ outb(mode, ioaddr + REG3); /* writing reg. 3 to complete the update */
+ eepro_sw2bank0(ioaddr); /* Return to BANK 0 now */
+ }
+
+ else if (mc_count == 0)
+ {
+ eepro_sw2bank2(ioaddr); /* be CAREFUL, BANK 2 now */
+ mode = inb(ioaddr + REG2);
+ outb(mode & 0xd6, ioaddr + REG2); /* Turn off Multi-IA and PRMSC_Mode bits */
+ mode = inb(ioaddr + REG3);
+ outb(mode, ioaddr + REG3); /* writing reg. 3 to complete the update */
+ eepro_sw2bank0(ioaddr); /* Return to BANK 0 now */
+ }
+
+ else
+ {
+ unsigned short status, *eaddrs;
+ int i, boguscount = 0;
+
+ /* Disable RX and TX interrupts. Necessary to avoid
+ corruption of the HOST_ADDRESS_REG by interrupt
+ service routines. */
+ eepro_dis_int(ioaddr);
+
+ eepro_sw2bank2(ioaddr); /* be CAREFUL, BANK 2 now */
+ mode = inb(ioaddr + REG2);
+ outb(mode | Multi_IA, ioaddr + REG2);
+ mode = inb(ioaddr + REG3);
+ outb(mode, ioaddr + REG3); /* writing reg. 3 to complete the update */
+ eepro_sw2bank0(ioaddr); /* Return to BANK 0 now */
+ outw(lp->tx_end, ioaddr + HOST_ADDRESS_REG);
+ outw(MC_SETUP, ioaddr + IO_PORT);
+ outw(0, ioaddr + IO_PORT);
+ outw(0, ioaddr + IO_PORT);
+ outw(6 * (mc_count + 1), ioaddr + IO_PORT);
+
+ netdev_for_each_mc_addr(ha, dev) {
+ eaddrs = (unsigned short *) ha->addr;
+ outw(*eaddrs++, ioaddr + IO_PORT);
+ outw(*eaddrs++, ioaddr + IO_PORT);
+ outw(*eaddrs++, ioaddr + IO_PORT);
+ }
+
+ eaddrs = (unsigned short *) dev->dev_addr;
+ outw(eaddrs[0], ioaddr + IO_PORT);
+ outw(eaddrs[1], ioaddr + IO_PORT);
+ outw(eaddrs[2], ioaddr + IO_PORT);
+ outw(lp->tx_end, ioaddr + lp->xmt_bar);
+ outb(MC_SETUP, ioaddr);
+
+ /* Update the transmit queue */
+ i = lp->tx_end + XMT_HEADER + 6 * (mc_count + 1);
+
+ if (lp->tx_start != lp->tx_end)
+ {
+ /* update the next address and the chain bit in the
+ last packet */
+ outw(lp->tx_last + XMT_CHAIN, ioaddr + HOST_ADDRESS_REG);
+ outw(i, ioaddr + IO_PORT);
+ outw(lp->tx_last + XMT_COUNT, ioaddr + HOST_ADDRESS_REG);
+ status = inw(ioaddr + IO_PORT);
+ outw(status | CHAIN_BIT, ioaddr + IO_PORT);
+ lp->tx_end = i ;
+ }
+ else {
+ lp->tx_start = lp->tx_end = i ;
+ }
+
+ /* Acknowledge that the MC setup is done */
+ do { /* We should be doing this in the eepro_interrupt()! */
+ SLOW_DOWN;
+ SLOW_DOWN;
+ if (inb(ioaddr + STATUS_REG) & 0x08)
+ {
+ i = inb(ioaddr);
+ outb(0x08, ioaddr + STATUS_REG);
+
+ if (i & 0x20) { /* command ABORTed */
+ printk(KERN_NOTICE "%s: multicast setup failed.\n",
+ dev->name);
+ break;
+ } else if ((i & 0x0f) == 0x03) { /* MC-Done */
+ printk(KERN_DEBUG "%s: set Rx mode to %d address%s.\n",
+ dev->name, mc_count,
+ mc_count > 1 ? "es":"");
+ break;
+ }
+ }
+ } while (++boguscount < 100);
+
+ /* Re-enable RX and TX interrupts */
+ eepro_en_int(ioaddr);
+ }
+ if (lp->eepro == LAN595FX_10ISA) {
+ eepro_complete_selreset(ioaddr);
+ }
+ else
+ eepro_en_rx(ioaddr);
+}
+
+/* The horrible routine to read a word from the serial EEPROM. */
+/* IMPORTANT - the 82595 will be set to Bank 0 after the eeprom is read */
+
+/* The delay between EEPROM clock transitions. */
+#define eeprom_delay() { udelay(40); }
+#define EE_READ_CMD (6 << 6)
+
+static int
+read_eeprom(int ioaddr, int location, struct net_device *dev)
+{
+ int i;
+ unsigned short retval = 0;
+ struct eepro_local *lp = netdev_priv(dev);
+ short ee_addr = ioaddr + lp->eeprom_reg;
+ int read_cmd = location | EE_READ_CMD;
+ short ctrl_val = EECS ;
+
+ /* XXXX - black magic */
+ eepro_sw2bank1(ioaddr);
+ outb(0x00, ioaddr + STATUS_REG);
+ /* XXXX - black magic */
+
+ eepro_sw2bank2(ioaddr);
+ outb(ctrl_val, ee_addr);
+
+ /* Shift the read command bits out. */
+ for (i = 8; i >= 0; i--) {
+ short outval = (read_cmd & (1 << i)) ? ctrl_val | EEDI
+ : ctrl_val;
+ outb(outval, ee_addr);
+ outb(outval | EESK, ee_addr); /* EEPROM clock tick. */
+ eeprom_delay();
+ outb(outval, ee_addr); /* Finish EEPROM a clock tick. */
+ eeprom_delay();
+ }
+ outb(ctrl_val, ee_addr);
+
+ for (i = 16; i > 0; i--) {
+ outb(ctrl_val | EESK, ee_addr); eeprom_delay();
+ retval = (retval << 1) | ((inb(ee_addr) & EEDO) ? 1 : 0);
+ outb(ctrl_val, ee_addr); eeprom_delay();
+ }
+
+ /* Terminate the EEPROM access. */
+ ctrl_val &= ~EECS;
+ outb(ctrl_val | EESK, ee_addr);
+ eeprom_delay();
+ outb(ctrl_val, ee_addr);
+ eeprom_delay();
+ eepro_sw2bank0(ioaddr);
+ return retval;
+}
+
+static int
+hardware_send_packet(struct net_device *dev, void *buf, short length)
+{
+ struct eepro_local *lp = netdev_priv(dev);
+ short ioaddr = dev->base_addr;
+ unsigned status, tx_available, last, end;
+
+ if (net_debug > 5)
+ printk(KERN_DEBUG "%s: entering hardware_send_packet routine.\n", dev->name);
+
+ /* determine how much of the transmit buffer space is available */
+ if (lp->tx_end > lp->tx_start)
+ tx_available = lp->xmt_ram - (lp->tx_end - lp->tx_start);
+ else if (lp->tx_end < lp->tx_start)
+ tx_available = lp->tx_start - lp->tx_end;
+ else tx_available = lp->xmt_ram;
+
+ if (((((length + 3) >> 1) << 1) + 2*XMT_HEADER) >= tx_available) {
+ /* No space available ??? */
+ return 1;
+ }
+
+ last = lp->tx_end;
+ end = last + (((length + 3) >> 1) << 1) + XMT_HEADER;
+
+ if (end >= lp->xmt_upper_limit + 2) { /* the transmit buffer is wrapped around */
+ if ((lp->xmt_upper_limit + 2 - last) <= XMT_HEADER) {
+ /* Arrrr!!!, must keep the xmt header together,
+ several days were lost to chase this one down. */
+ last = lp->xmt_lower_limit;
+ end = last + (((length + 3) >> 1) << 1) + XMT_HEADER;
+ }
+ else end = lp->xmt_lower_limit + (end -
+ lp->xmt_upper_limit + 2);
+ }
+
+ outw(last, ioaddr + HOST_ADDRESS_REG);
+ outw(XMT_CMD, ioaddr + IO_PORT);
+ outw(0, ioaddr + IO_PORT);
+ outw(end, ioaddr + IO_PORT);
+ outw(length, ioaddr + IO_PORT);
+
+ if (lp->version == LAN595)
+ outsw(ioaddr + IO_PORT, buf, (length + 3) >> 1);
+ else { /* LAN595TX or LAN595FX, capable of 32-bit I/O processing */
+ unsigned short temp = inb(ioaddr + INT_MASK_REG);
+ outb(temp | IO_32_BIT, ioaddr + INT_MASK_REG);
+ outsl(ioaddr + IO_PORT_32_BIT, buf, (length + 3) >> 2);
+ outb(temp & ~(IO_32_BIT), ioaddr + INT_MASK_REG);
+ }
+
+ /* A dummy read to flush the DRAM write pipeline */
+ status = inw(ioaddr + IO_PORT);
+
+ if (lp->tx_start == lp->tx_end) {
+ outw(last, ioaddr + lp->xmt_bar);
+ outb(XMT_CMD, ioaddr);
+ lp->tx_start = last; /* I don't like to change tx_start here */
+ }
+ else {
+ /* update the next address and the chain bit in the
+ last packet */
+
+ if (lp->tx_end != last) {
+ outw(lp->tx_last + XMT_CHAIN, ioaddr + HOST_ADDRESS_REG);
+ outw(last, ioaddr + IO_PORT);
+ }
+
+ outw(lp->tx_last + XMT_COUNT, ioaddr + HOST_ADDRESS_REG);
+ status = inw(ioaddr + IO_PORT);
+ outw(status | CHAIN_BIT, ioaddr + IO_PORT);
+
+ /* Continue the transmit command */
+ outb(RESUME_XMT_CMD, ioaddr);
+ }
+
+ lp->tx_last = last;
+ lp->tx_end = end;
+
+ if (net_debug > 5)
+ printk(KERN_DEBUG "%s: exiting hardware_send_packet routine.\n", dev->name);
+
+ return 0;
+}
+
+static void
+eepro_rx(struct net_device *dev)
+{
+ struct eepro_local *lp = netdev_priv(dev);
+ short ioaddr = dev->base_addr;
+ short boguscount = 20;
+ short rcv_car = lp->rx_start;
+ unsigned rcv_event, rcv_status, rcv_next_frame, rcv_size;
+
+ if (net_debug > 5)
+ printk(KERN_DEBUG "%s: entering eepro_rx routine.\n", dev->name);
+
+ /* Set the read pointer to the start of the RCV */
+ outw(rcv_car, ioaddr + HOST_ADDRESS_REG);
+
+ rcv_event = inw(ioaddr + IO_PORT);
+
+ while (rcv_event == RCV_DONE) {
+
+ rcv_status = inw(ioaddr + IO_PORT);
+ rcv_next_frame = inw(ioaddr + IO_PORT);
+ rcv_size = inw(ioaddr + IO_PORT);
+
+ if ((rcv_status & (RX_OK | RX_ERROR)) == RX_OK) {
+
+ /* Malloc up new buffer. */
+ struct sk_buff *skb;
+
+ dev->stats.rx_bytes+=rcv_size;
+ rcv_size &= 0x3fff;
+ skb = dev_alloc_skb(rcv_size+5);
+ if (skb == NULL) {
+ printk(KERN_NOTICE "%s: Memory squeeze, dropping packet.\n", dev->name);
+ dev->stats.rx_dropped++;
+ rcv_car = lp->rx_start + RCV_HEADER + rcv_size;
+ lp->rx_start = rcv_next_frame;
+ outw(rcv_next_frame, ioaddr + HOST_ADDRESS_REG);
+
+ break;
+ }
+ skb_reserve(skb,2);
+
+ if (lp->version == LAN595)
+ insw(ioaddr+IO_PORT, skb_put(skb,rcv_size), (rcv_size + 3) >> 1);
+ else { /* LAN595TX or LAN595FX, capable of 32-bit I/O processing */
+ unsigned short temp = inb(ioaddr + INT_MASK_REG);
+ outb(temp | IO_32_BIT, ioaddr + INT_MASK_REG);
+ insl(ioaddr+IO_PORT_32_BIT, skb_put(skb,rcv_size),
+ (rcv_size + 3) >> 2);
+ outb(temp & ~(IO_32_BIT), ioaddr + INT_MASK_REG);
+ }
+
+ skb->protocol = eth_type_trans(skb,dev);
+ netif_rx(skb);
+ dev->stats.rx_packets++;
+ }
+
+ else { /* Not sure will ever reach here,
+ I set the 595 to discard bad received frames */
+ dev->stats.rx_errors++;
+
+ if (rcv_status & 0x0100)
+ dev->stats.rx_over_errors++;
+
+ else if (rcv_status & 0x0400)
+ dev->stats.rx_frame_errors++;
+
+ else if (rcv_status & 0x0800)
+ dev->stats.rx_crc_errors++;
+
+ printk(KERN_DEBUG "%s: event = %#x, status = %#x, next = %#x, size = %#x\n",
+ dev->name, rcv_event, rcv_status, rcv_next_frame, rcv_size);
+ }
+
+ if (rcv_status & 0x1000)
+ dev->stats.rx_length_errors++;
+
+ rcv_car = lp->rx_start + RCV_HEADER + rcv_size;
+ lp->rx_start = rcv_next_frame;
+
+ if (--boguscount == 0)
+ break;
+
+ outw(rcv_next_frame, ioaddr + HOST_ADDRESS_REG);
+ rcv_event = inw(ioaddr + IO_PORT);
+
+ }
+ if (rcv_car == 0)
+ rcv_car = lp->rcv_upper_limit | 0xff;
+
+ outw(rcv_car - 1, ioaddr + RCV_STOP);
+
+ if (net_debug > 5)
+ printk(KERN_DEBUG "%s: exiting eepro_rx routine.\n", dev->name);
+}
+
+static void
+eepro_transmit_interrupt(struct net_device *dev)
+{
+ struct eepro_local *lp = netdev_priv(dev);
+ short ioaddr = dev->base_addr;
+ short boguscount = 25;
+ short xmt_status;
+
+ while ((lp->tx_start != lp->tx_end) && boguscount--) {
+
+ outw(lp->tx_start, ioaddr + HOST_ADDRESS_REG);
+ xmt_status = inw(ioaddr+IO_PORT);
+
+ if (!(xmt_status & TX_DONE_BIT))
+ break;
+
+ xmt_status = inw(ioaddr+IO_PORT);
+ lp->tx_start = inw(ioaddr+IO_PORT);
+
+ netif_wake_queue (dev);
+
+ if (xmt_status & TX_OK)
+ dev->stats.tx_packets++;
+ else {
+ dev->stats.tx_errors++;
+ if (xmt_status & 0x0400) {
+ dev->stats.tx_carrier_errors++;
+ printk(KERN_DEBUG "%s: carrier error\n",
+ dev->name);
+ printk(KERN_DEBUG "%s: XMT status = %#x\n",
+ dev->name, xmt_status);
+ }
+ else {
+ printk(KERN_DEBUG "%s: XMT status = %#x\n",
+ dev->name, xmt_status);
+ printk(KERN_DEBUG "%s: XMT status = %#x\n",
+ dev->name, xmt_status);
+ }
+ }
+ if (xmt_status & 0x000f) {
+ dev->stats.collisions += (xmt_status & 0x000f);
+ }
+
+ if ((xmt_status & 0x0040) == 0x0) {
+ dev->stats.tx_heartbeat_errors++;
+ }
+ }
+}
+
+static int eepro_ethtool_get_settings(struct net_device *dev,
+ struct ethtool_cmd *cmd)
+{
+ struct eepro_local *lp = netdev_priv(dev);
+
+ cmd->supported = SUPPORTED_10baseT_Half |
+ SUPPORTED_10baseT_Full |
+ SUPPORTED_Autoneg;
+ cmd->advertising = ADVERTISED_10baseT_Half |
+ ADVERTISED_10baseT_Full |
+ ADVERTISED_Autoneg;
+
+ if (GetBit(lp->word[5], ee_PortTPE)) {
+ cmd->supported |= SUPPORTED_TP;
+ cmd->advertising |= ADVERTISED_TP;
+ }
+ if (GetBit(lp->word[5], ee_PortBNC)) {
+ cmd->supported |= SUPPORTED_BNC;
+ cmd->advertising |= ADVERTISED_BNC;
+ }
+ if (GetBit(lp->word[5], ee_PortAUI)) {
+ cmd->supported |= SUPPORTED_AUI;
+ cmd->advertising |= ADVERTISED_AUI;
+ }
+
+ ethtool_cmd_speed_set(cmd, SPEED_10);
+
+ if (dev->if_port == TPE && lp->word[1] & ee_Duplex) {
+ cmd->duplex = DUPLEX_FULL;
+ }
+ else {
+ cmd->duplex = DUPLEX_HALF;
+ }
+
+ cmd->port = dev->if_port;
+ cmd->phy_address = dev->base_addr;
+ cmd->transceiver = XCVR_INTERNAL;
+
+ if (lp->word[0] & ee_AutoNeg) {
+ cmd->autoneg = 1;
+ }
+
+ return 0;
+}
+
+static void eepro_ethtool_get_drvinfo(struct net_device *dev,
+ struct ethtool_drvinfo *drvinfo)
+{
+ strcpy(drvinfo->driver, DRV_NAME);
+ strcpy(drvinfo->version, DRV_VERSION);
+ sprintf(drvinfo->bus_info, "ISA 0x%lx", dev->base_addr);
+}
+
+static const struct ethtool_ops eepro_ethtool_ops = {
+ .get_settings = eepro_ethtool_get_settings,
+ .get_drvinfo = eepro_ethtool_get_drvinfo,
+};
+
+#ifdef MODULE
+
+#define MAX_EEPRO 8
+static struct net_device *dev_eepro[MAX_EEPRO];
+
+static int io[MAX_EEPRO] = {
+ [0 ... MAX_EEPRO-1] = -1
+};
+static int irq[MAX_EEPRO];
+static int mem[MAX_EEPRO] = { /* Size of the rx buffer in KB */
+ [0 ... MAX_EEPRO-1] = RCV_DEFAULT_RAM/1024
+};
+static int autodetect;
+
+static int n_eepro;
+/* For linux 2.1.xx */
+
+MODULE_AUTHOR("Pascal Dupuis and others");
+MODULE_DESCRIPTION("Intel i82595 ISA EtherExpressPro10/10+ driver");
+MODULE_LICENSE("GPL");
+
+module_param_array(io, int, NULL, 0);
+module_param_array(irq, int, NULL, 0);
+module_param_array(mem, int, NULL, 0);
+module_param(autodetect, int, 0);
+MODULE_PARM_DESC(io, "EtherExpress Pro/10 I/O base address(es)");
+MODULE_PARM_DESC(irq, "EtherExpress Pro/10 IRQ number(s)");
+MODULE_PARM_DESC(mem, "EtherExpress Pro/10 Rx buffer size(es) in kB (3-29)");
+MODULE_PARM_DESC(autodetect, "EtherExpress Pro/10 force board(s) detection (0-1)");
+
+int __init init_module(void)
+{
+ struct net_device *dev;
+ int i;
+ if (io[0] == -1 && autodetect == 0) {
+ printk(KERN_WARNING "eepro_init_module: Probe is very dangerous in ISA boards!\n");
+ printk(KERN_WARNING "eepro_init_module: Please add \"autodetect=1\" to force probe\n");
+ return -ENODEV;
+ }
+ else if (autodetect) {
+ /* if autodetect is set then we must force detection */
+ for (i = 0; i < MAX_EEPRO; i++) {
+ io[i] = 0;
+ }
+
+ printk(KERN_INFO "eepro_init_module: Auto-detecting boards (May God protect us...)\n");
+ }
+
+ for (i = 0; i < MAX_EEPRO && io[i] != -1; i++) {
+ dev = alloc_etherdev(sizeof(struct eepro_local));
+ if (!dev)
+ break;
+
+ dev->mem_end = mem[i];
+ dev->base_addr = io[i];
+ dev->irq = irq[i];
+
+ if (do_eepro_probe(dev) == 0) {
+ dev_eepro[n_eepro++] = dev;
+ continue;
+ }
+ free_netdev(dev);
+ break;
+ }
+
+ if (n_eepro)
+ printk(KERN_INFO "%s", version);
+
+ return n_eepro ? 0 : -ENODEV;
+}
+
+void __exit
+cleanup_module(void)
+{
+ int i;
+
+ for (i=0; i<n_eepro; i++) {
+ struct net_device *dev = dev_eepro[i];
+ unregister_netdev(dev);
+ release_region(dev->base_addr, EEPRO_IO_EXTENT);
+ free_netdev(dev);
+ }
+}
+#endif /* MODULE */
diff --git a/drivers/net/ethernet/i825xx/eexpress.c b/drivers/net/ethernet/i825xx/eexpress.c
new file mode 100644
index 00000000000..3a9580f3d4d
--- /dev/null
+++ b/drivers/net/ethernet/i825xx/eexpress.c
@@ -0,0 +1,1720 @@
+/* Intel EtherExpress 16 device driver for Linux
+ *
+ * Written by John Sullivan, 1995
+ * based on original code by Donald Becker, with changes by
+ * Alan Cox and Pauline Middelink.
+ *
+ * Support for 8-bit mode by Zoltan Szilagyi <zoltans@cs.arizona.edu>
+ *
+ * Many modifications, and currently maintained, by
+ * Philip Blundell <philb@gnu.org>
+ * Added the Compaq LTE Alan Cox <alan@lxorguk.ukuu.org.uk>
+ * Added MCA support Adam Fritzler
+ *
+ * Note - this driver is experimental still - it has problems on faster
+ * machines. Someone needs to sit down and go through it line by line with
+ * a databook...
+ */
+
+/* The EtherExpress 16 is a fairly simple card, based on a shared-memory
+ * design using the i82586 Ethernet coprocessor. It bears no relationship,
+ * as far as I know, to the similarly-named "EtherExpress Pro" range.
+ *
+ * Historically, Linux support for these cards has been very bad. However,
+ * things seem to be getting better slowly.
+ */
+
+/* If your card is confused about what sort of interface it has (eg it
+ * persistently reports "10baseT" when none is fitted), running 'SOFTSET /BART'
+ * or 'SOFTSET /LISA' from DOS seems to help.
+ */
+
+/* Here's the scoop on memory mapping.
+ *
+ * There are three ways to access EtherExpress card memory: either using the
+ * shared-memory mapping, or using PIO through the dataport, or using PIO
+ * through the "shadow memory" ports.
+ *
+ * The shadow memory system works by having the card map some of its memory
+ * as follows:
+ *
+ * (the low five bits of the SMPTR are ignored)
+ *
+ * base+0x4000..400f memory at SMPTR+0..15
+ * base+0x8000..800f memory at SMPTR+16..31
+ * base+0xc000..c007 dubious stuff (memory at SMPTR+16..23 apparently)
+ * base+0xc008..c00f memory at 0x0008..0x000f
+ *
+ * This last set (the one at c008) is particularly handy because the SCB
+ * lives at 0x0008. So that set of ports gives us easy random access to data
+ * in the SCB without having to mess around setting up pointers and the like.
+ * We always use this method to access the SCB (via the scb_xx() functions).
+ *
+ * Dataport access works by aiming the appropriate (read or write) pointer
+ * at the first address you're interested in, and then reading or writing from
+ * the dataport. The pointers auto-increment after each transfer. We use
+ * this for data transfer.
+ *
+ * We don't use the shared-memory system because it allegedly doesn't work on
+ * all cards, and because it's a bit more prone to go wrong (it's one more
+ * thing to configure...).
+ */
+
+/* Known bugs:
+ *
+ * - The card seems to want to give us two interrupts every time something
+ * happens, where just one would be better.
+ */
+
+/*
+ *
+ * Note by Zoltan Szilagyi 10-12-96:
+ *
+ * I've succeeded in eliminating the "CU wedged" messages, and hence the
+ * lockups, which were only occurring with cards running in 8-bit mode ("force
+ * 8-bit operation" in Intel's SoftSet utility). This version of the driver
+ * sets the 82586 and the ASIC to 8-bit mode at startup; it also stops the
+ * CU before submitting a packet for transmission, and then restarts it as soon
+ * as the process of handing the packet is complete. This is definitely an
+ * unnecessary slowdown if the card is running in 16-bit mode; therefore one
+ * should detect 16-bit vs 8-bit mode from the EEPROM settings and act
+ * accordingly. In 8-bit mode with this bugfix I'm getting about 150 K/s for
+ * ftp's, which is significantly better than I get in DOS, so the overhead of
+ * stopping and restarting the CU with each transmit is not prohibitive in
+ * practice.
+ *
+ * Update by David Woodhouse 11/5/99:
+ *
+ * I've seen "CU wedged" messages in 16-bit mode, on the Alpha architecture.
+ * I assume that this is because 16-bit accesses are actually handled as two
+ * 8-bit accesses.
+ */
+
+#ifdef __alpha__
+#define LOCKUP16 1
+#endif
+#ifndef LOCKUP16
+#define LOCKUP16 0
+#endif
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/string.h>
+#include <linux/in.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/mca-legacy.h>
+#include <linux/spinlock.h>
+#include <linux/bitops.h>
+#include <linux/jiffies.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#ifndef NET_DEBUG
+#define NET_DEBUG 4
+#endif
+
+#include "eexpress.h"
+
+#define EEXP_IO_EXTENT 16
+
+/*
+ * Private data declarations
+ */
+
+struct net_local
+{
+ unsigned long last_tx; /* jiffies when last transmit started */
+ unsigned long init_time; /* jiffies when eexp_hw_init586 called */
+ unsigned short rx_first; /* first rx buf, same as RX_BUF_START */
+ unsigned short rx_last; /* last rx buf */
+ unsigned short rx_ptr; /* first rx buf to look at */
+ unsigned short tx_head; /* next free tx buf */
+ unsigned short tx_reap; /* first in-use tx buf */
+ unsigned short tx_tail; /* previous tx buf to tx_head */
+ unsigned short tx_link; /* last known-executing tx buf */
+ unsigned short last_tx_restart; /* set to tx_link when we
+ restart the CU */
+ unsigned char started;
+ unsigned short rx_buf_start;
+ unsigned short rx_buf_end;
+ unsigned short num_tx_bufs;
+ unsigned short num_rx_bufs;
+ unsigned char width; /* 0 for 16bit, 1 for 8bit */
+ unsigned char was_promisc;
+ unsigned char old_mc_count;
+ spinlock_t lock;
+};
+
+/* This is the code and data that is downloaded to the EtherExpress card's
+ * memory at boot time.
+ */
+
+static unsigned short start_code[] = {
+/* 0x0000 */
+ 0x0001, /* ISCP: busy - cleared after reset */
+ 0x0008,0x0000,0x0000, /* offset,address (lo,hi) of SCB */
+
+ 0x0000,0x0000, /* SCB: status, commands */
+ 0x0000,0x0000, /* links to first command block,
+ first receive descriptor */
+ 0x0000,0x0000, /* CRC error, alignment error counts */
+ 0x0000,0x0000, /* out of resources, overrun error counts */
+
+ 0x0000,0x0000, /* pad */
+ 0x0000,0x0000,
+
+/* 0x20 -- start of 82586 CU program */
+#define CONF_LINK 0x20
+ 0x0000,Cmd_Config,
+ 0x0032, /* link to next command */
+ 0x080c, /* 12 bytes follow : fifo threshold=8 */
+ 0x2e40, /* don't rx bad frames
+ * SRDY/ARDY => ext. sync. : preamble len=8
+ * take addresses from data buffers
+ * 6 bytes/address
+ */
+ 0x6000, /* default backoff method & priority
+ * interframe spacing = 0x60 */
+ 0xf200, /* slot time=0x200
+ * max collision retry = 0xf */
+#define CONF_PROMISC 0x2e
+ 0x0000, /* no HDLC : normal CRC : enable broadcast
+ * disable promiscuous/multicast modes */
+ 0x003c, /* minimum frame length = 60 octets) */
+
+ 0x0000,Cmd_SetAddr,
+ 0x003e, /* link to next command */
+#define CONF_HWADDR 0x38
+ 0x0000,0x0000,0x0000, /* hardware address placed here */
+
+ 0x0000,Cmd_MCast,
+ 0x0076, /* link to next command */
+#define CONF_NR_MULTICAST 0x44
+ 0x0000, /* number of bytes in multicast address(es) */
+#define CONF_MULTICAST 0x46
+ 0x0000, 0x0000, 0x0000, /* some addresses */
+ 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000,
+
+#define CONF_DIAG_RESULT 0x76
+ 0x0000, Cmd_Diag,
+ 0x007c, /* link to next command */
+
+ 0x0000,Cmd_TDR|Cmd_INT,
+ 0x0084,
+#define CONF_TDR_RESULT 0x82
+ 0x0000,
+
+ 0x0000,Cmd_END|Cmd_Nop, /* end of configure sequence */
+ 0x0084 /* dummy link */
+};
+
+/* maps irq number to EtherExpress magic value */
+static char irqrmap[] = { 0,0,1,2,3,4,0,0,0,1,5,6,0,0,0,0 };
+
+#ifdef CONFIG_MCA_LEGACY
+/* mapping of the first four bits of the second POS register */
+static unsigned short mca_iomap[] = {
+ 0x270, 0x260, 0x250, 0x240, 0x230, 0x220, 0x210, 0x200,
+ 0x370, 0x360, 0x350, 0x340, 0x330, 0x320, 0x310, 0x300
+};
+/* bits 5-7 of the second POS register */
+static char mca_irqmap[] = { 12, 9, 3, 4, 5, 10, 11, 15 };
+#endif
+
+/*
+ * Prototypes for Linux interface
+ */
+
+static int eexp_open(struct net_device *dev);
+static int eexp_close(struct net_device *dev);
+static void eexp_timeout(struct net_device *dev);
+static netdev_tx_t eexp_xmit(struct sk_buff *buf,
+ struct net_device *dev);
+
+static irqreturn_t eexp_irq(int irq, void *dev_addr);
+static void eexp_set_multicast(struct net_device *dev);
+
+/*
+ * Prototypes for hardware access functions
+ */
+
+static void eexp_hw_rx_pio(struct net_device *dev);
+static void eexp_hw_tx_pio(struct net_device *dev, unsigned short *buf,
+ unsigned short len);
+static int eexp_hw_probe(struct net_device *dev,unsigned short ioaddr);
+static unsigned short eexp_hw_readeeprom(unsigned short ioaddr,
+ unsigned char location);
+
+static unsigned short eexp_hw_lasttxstat(struct net_device *dev);
+static void eexp_hw_txrestart(struct net_device *dev);
+
+static void eexp_hw_txinit (struct net_device *dev);
+static void eexp_hw_rxinit (struct net_device *dev);
+
+static void eexp_hw_init586 (struct net_device *dev);
+static void eexp_setup_filter (struct net_device *dev);
+
+static char *eexp_ifmap[]={"AUI", "BNC", "RJ45"};
+enum eexp_iftype {AUI=0, BNC=1, TPE=2};
+
+#define STARTED_RU 2
+#define STARTED_CU 1
+
+/*
+ * Primitive hardware access functions.
+ */
+
+static inline unsigned short scb_status(struct net_device *dev)
+{
+ return inw(dev->base_addr + 0xc008);
+}
+
+static inline unsigned short scb_rdcmd(struct net_device *dev)
+{
+ return inw(dev->base_addr + 0xc00a);
+}
+
+static inline void scb_command(struct net_device *dev, unsigned short cmd)
+{
+ outw(cmd, dev->base_addr + 0xc00a);
+}
+
+static inline void scb_wrcbl(struct net_device *dev, unsigned short val)
+{
+ outw(val, dev->base_addr + 0xc00c);
+}
+
+static inline void scb_wrrfa(struct net_device *dev, unsigned short val)
+{
+ outw(val, dev->base_addr + 0xc00e);
+}
+
+static inline void set_loopback(struct net_device *dev)
+{
+ outb(inb(dev->base_addr + Config) | 2, dev->base_addr + Config);
+}
+
+static inline void clear_loopback(struct net_device *dev)
+{
+ outb(inb(dev->base_addr + Config) & ~2, dev->base_addr + Config);
+}
+
+static inline unsigned short int SHADOW(short int addr)
+{
+ addr &= 0x1f;
+ if (addr > 0xf) addr += 0x3ff0;
+ return addr + 0x4000;
+}
+
+/*
+ * Linux interface
+ */
+
+/*
+ * checks for presence of EtherExpress card
+ */
+
+static int __init do_express_probe(struct net_device *dev)
+{
+ unsigned short *port;
+ static unsigned short ports[] = { 0x240,0x300,0x310,0x270,0x320,0x340,0 };
+ unsigned short ioaddr = dev->base_addr;
+ int dev_irq = dev->irq;
+ int err;
+
+ dev->if_port = 0xff; /* not set */
+
+#ifdef CONFIG_MCA_LEGACY
+ if (MCA_bus) {
+ int slot = 0;
+
+ /*
+ * Only find one card at a time. Subsequent calls
+ * will find others, however, proper multicard MCA
+ * probing and setup can't be done with the
+ * old-style Space.c init routines. -- ASF
+ */
+ while (slot != MCA_NOTFOUND) {
+ int pos0, pos1;
+
+ slot = mca_find_unused_adapter(0x628B, slot);
+ if (slot == MCA_NOTFOUND)
+ break;
+
+ pos0 = mca_read_stored_pos(slot, 2);
+ pos1 = mca_read_stored_pos(slot, 3);
+ ioaddr = mca_iomap[pos1&0xf];
+
+ dev->irq = mca_irqmap[(pos1>>4)&0x7];
+
+ /*
+ * XXX: Transceiver selection is done
+ * differently on the MCA version.
+ * How to get it to select something
+ * other than external/AUI is currently
+ * unknown. This code is just for looks. -- ASF
+ */
+ if ((pos0 & 0x7) == 0x1)
+ dev->if_port = AUI;
+ else if ((pos0 & 0x7) == 0x5) {
+ if (pos1 & 0x80)
+ dev->if_port = BNC;
+ else
+ dev->if_port = TPE;
+ }
+
+ mca_set_adapter_name(slot, "Intel EtherExpress 16 MCA");
+ mca_set_adapter_procfn(slot, NULL, dev);
+ mca_mark_as_used(slot);
+
+ break;
+ }
+ }
+#endif
+ if (ioaddr&0xfe00) {
+ if (!request_region(ioaddr, EEXP_IO_EXTENT, "EtherExpress"))
+ return -EBUSY;
+ err = eexp_hw_probe(dev,ioaddr);
+ release_region(ioaddr, EEXP_IO_EXTENT);
+ return err;
+ } else if (ioaddr)
+ return -ENXIO;
+
+ for (port=&ports[0] ; *port ; port++ )
+ {
+ unsigned short sum = 0;
+ int i;
+ if (!request_region(*port, EEXP_IO_EXTENT, "EtherExpress"))
+ continue;
+ for ( i=0 ; i<4 ; i++ )
+ {
+ unsigned short t;
+ t = inb(*port + ID_PORT);
+ sum |= (t>>4) << ((t & 0x03)<<2);
+ }
+ if (sum==0xbaba && !eexp_hw_probe(dev,*port)) {
+ release_region(*port, EEXP_IO_EXTENT);
+ return 0;
+ }
+ release_region(*port, EEXP_IO_EXTENT);
+ dev->irq = dev_irq;
+ }
+ return -ENODEV;
+}
+
+#ifndef MODULE
+struct net_device * __init express_probe(int unit)
+{
+ struct net_device *dev = alloc_etherdev(sizeof(struct net_local));
+ int err;
+
+ if (!dev)
+ return ERR_PTR(-ENOMEM);
+
+ sprintf(dev->name, "eth%d", unit);
+ netdev_boot_setup_check(dev);
+
+ err = do_express_probe(dev);
+ if (!err)
+ return dev;
+ free_netdev(dev);
+ return ERR_PTR(err);
+}
+#endif
+
+/*
+ * open and initialize the adapter, ready for use
+ */
+
+static int eexp_open(struct net_device *dev)
+{
+ int ret;
+ unsigned short ioaddr = dev->base_addr;
+ struct net_local *lp = netdev_priv(dev);
+
+#if NET_DEBUG > 6
+ printk(KERN_DEBUG "%s: eexp_open()\n", dev->name);
+#endif
+
+ if (!dev->irq || !irqrmap[dev->irq])
+ return -ENXIO;
+
+ ret = request_irq(dev->irq, eexp_irq, 0, dev->name, dev);
+ if (ret)
+ return ret;
+
+ if (!request_region(ioaddr, EEXP_IO_EXTENT, "EtherExpress")) {
+ printk(KERN_WARNING "EtherExpress io port %x, is busy.\n"
+ , ioaddr);
+ goto err_out1;
+ }
+ if (!request_region(ioaddr+0x4000, EEXP_IO_EXTENT, "EtherExpress shadow")) {
+ printk(KERN_WARNING "EtherExpress io port %x, is busy.\n"
+ , ioaddr+0x4000);
+ goto err_out2;
+ }
+ if (!request_region(ioaddr+0x8000, EEXP_IO_EXTENT, "EtherExpress shadow")) {
+ printk(KERN_WARNING "EtherExpress io port %x, is busy.\n"
+ , ioaddr+0x8000);
+ goto err_out3;
+ }
+ if (!request_region(ioaddr+0xc000, EEXP_IO_EXTENT, "EtherExpress shadow")) {
+ printk(KERN_WARNING "EtherExpress io port %x, is busy.\n"
+ , ioaddr+0xc000);
+ goto err_out4;
+ }
+
+ if (lp->width) {
+ printk("%s: forcing ASIC to 8-bit mode\n", dev->name);
+ outb(inb(dev->base_addr+Config)&~4, dev->base_addr+Config);
+ }
+
+ eexp_hw_init586(dev);
+ netif_start_queue(dev);
+#if NET_DEBUG > 6
+ printk(KERN_DEBUG "%s: leaving eexp_open()\n", dev->name);
+#endif
+ return 0;
+
+ err_out4:
+ release_region(ioaddr+0x8000, EEXP_IO_EXTENT);
+ err_out3:
+ release_region(ioaddr+0x4000, EEXP_IO_EXTENT);
+ err_out2:
+ release_region(ioaddr, EEXP_IO_EXTENT);
+ err_out1:
+ free_irq(dev->irq, dev);
+ return -EBUSY;
+}
+
+/*
+ * close and disable the interface, leaving the 586 in reset.
+ */
+
+static int eexp_close(struct net_device *dev)
+{
+ unsigned short ioaddr = dev->base_addr;
+ struct net_local *lp = netdev_priv(dev);
+
+ int irq = dev->irq;
+
+ netif_stop_queue(dev);
+
+ outb(SIRQ_dis|irqrmap[irq],ioaddr+SET_IRQ);
+ lp->started = 0;
+ scb_command(dev, SCB_CUsuspend|SCB_RUsuspend);
+ outb(0,ioaddr+SIGNAL_CA);
+ free_irq(irq,dev);
+ outb(i586_RST,ioaddr+EEPROM_Ctrl);
+ release_region(ioaddr, EEXP_IO_EXTENT);
+ release_region(ioaddr+0x4000, 16);
+ release_region(ioaddr+0x8000, 16);
+ release_region(ioaddr+0xc000, 16);
+
+ return 0;
+}
+
+/*
+ * This gets called when a higher level thinks we are broken. Check that
+ * nothing has become jammed in the CU.
+ */
+
+static void unstick_cu(struct net_device *dev)
+{
+ struct net_local *lp = netdev_priv(dev);
+ unsigned short ioaddr = dev->base_addr;
+
+ if (lp->started)
+ {
+ if (time_after(jiffies, dev_trans_start(dev) + HZ/2))
+ {
+ if (lp->tx_link==lp->last_tx_restart)
+ {
+ unsigned short boguscount=200,rsst;
+ printk(KERN_WARNING "%s: Retransmit timed out, status %04x, resetting...\n",
+ dev->name, scb_status(dev));
+ eexp_hw_txinit(dev);
+ lp->last_tx_restart = 0;
+ scb_wrcbl(dev, lp->tx_link);
+ scb_command(dev, SCB_CUstart);
+ outb(0,ioaddr+SIGNAL_CA);
+ while (!SCB_complete(rsst=scb_status(dev)))
+ {
+ if (!--boguscount)
+ {
+ boguscount=200;
+ printk(KERN_WARNING "%s: Reset timed out status %04x, retrying...\n",
+ dev->name,rsst);
+ scb_wrcbl(dev, lp->tx_link);
+ scb_command(dev, SCB_CUstart);
+ outb(0,ioaddr+SIGNAL_CA);
+ }
+ }
+ netif_wake_queue(dev);
+ }
+ else
+ {
+ unsigned short status = scb_status(dev);
+ if (SCB_CUdead(status))
+ {
+ unsigned short txstatus = eexp_hw_lasttxstat(dev);
+ printk(KERN_WARNING "%s: Transmit timed out, CU not active status %04x %04x, restarting...\n",
+ dev->name, status, txstatus);
+ eexp_hw_txrestart(dev);
+ }
+ else
+ {
+ unsigned short txstatus = eexp_hw_lasttxstat(dev);
+ if (netif_queue_stopped(dev) && !txstatus)
+ {
+ printk(KERN_WARNING "%s: CU wedged, status %04x %04x, resetting...\n",
+ dev->name,status,txstatus);
+ eexp_hw_init586(dev);
+ netif_wake_queue(dev);
+ }
+ else
+ {
+ printk(KERN_WARNING "%s: transmit timed out\n", dev->name);
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ if (time_after(jiffies, lp->init_time + 10))
+ {
+ unsigned short status = scb_status(dev);
+ printk(KERN_WARNING "%s: i82586 startup timed out, status %04x, resetting...\n",
+ dev->name, status);
+ eexp_hw_init586(dev);
+ netif_wake_queue(dev);
+ }
+ }
+}
+
+static void eexp_timeout(struct net_device *dev)
+{
+ struct net_local *lp = netdev_priv(dev);
+#ifdef CONFIG_SMP
+ unsigned long flags;
+#endif
+ int status;
+
+ disable_irq(dev->irq);
+
+ /*
+ * Best would be to use synchronize_irq(); spin_lock() here
+ * lets make it work first..
+ */
+
+#ifdef CONFIG_SMP
+ spin_lock_irqsave(&lp->lock, flags);
+#endif
+
+ status = scb_status(dev);
+ unstick_cu(dev);
+ printk(KERN_INFO "%s: transmit timed out, %s?\n", dev->name,
+ (SCB_complete(status)?"lost interrupt":
+ "board on fire"));
+ dev->stats.tx_errors++;
+ lp->last_tx = jiffies;
+ if (!SCB_complete(status)) {
+ scb_command(dev, SCB_CUabort);
+ outb(0,dev->base_addr+SIGNAL_CA);
+ }
+ netif_wake_queue(dev);
+#ifdef CONFIG_SMP
+ spin_unlock_irqrestore(&lp->lock, flags);
+#endif
+}
+
+/*
+ * Called to transmit a packet, or to allow us to right ourselves
+ * if the kernel thinks we've died.
+ */
+static netdev_tx_t eexp_xmit(struct sk_buff *buf, struct net_device *dev)
+{
+ short length = buf->len;
+#ifdef CONFIG_SMP
+ struct net_local *lp = netdev_priv(dev);
+ unsigned long flags;
+#endif
+
+#if NET_DEBUG > 6
+ printk(KERN_DEBUG "%s: eexp_xmit()\n", dev->name);
+#endif
+
+ if (buf->len < ETH_ZLEN) {
+ if (skb_padto(buf, ETH_ZLEN))
+ return NETDEV_TX_OK;
+ length = ETH_ZLEN;
+ }
+
+ disable_irq(dev->irq);
+
+ /*
+ * Best would be to use synchronize_irq(); spin_lock() here
+ * lets make it work first..
+ */
+
+#ifdef CONFIG_SMP
+ spin_lock_irqsave(&lp->lock, flags);
+#endif
+
+ {
+ unsigned short *data = (unsigned short *)buf->data;
+
+ dev->stats.tx_bytes += length;
+
+ eexp_hw_tx_pio(dev,data,length);
+ }
+ dev_kfree_skb(buf);
+#ifdef CONFIG_SMP
+ spin_unlock_irqrestore(&lp->lock, flags);
+#endif
+ enable_irq(dev->irq);
+ return NETDEV_TX_OK;
+}
+
+/*
+ * Handle an EtherExpress interrupt
+ * If we've finished initializing, start the RU and CU up.
+ * If we've already started, reap tx buffers, handle any received packets,
+ * check to make sure we've not become wedged.
+ */
+
+static unsigned short eexp_start_irq(struct net_device *dev,
+ unsigned short status)
+{
+ unsigned short ack_cmd = SCB_ack(status);
+ struct net_local *lp = netdev_priv(dev);
+ unsigned short ioaddr = dev->base_addr;
+ if ((dev->flags & IFF_UP) && !(lp->started & STARTED_CU)) {
+ short diag_status, tdr_status;
+ while (SCB_CUstat(status)==2)
+ status = scb_status(dev);
+#if NET_DEBUG > 4
+ printk("%s: CU went non-active (status %04x)\n",
+ dev->name, status);
+#endif
+
+ outw(CONF_DIAG_RESULT & ~31, ioaddr + SM_PTR);
+ diag_status = inw(ioaddr + SHADOW(CONF_DIAG_RESULT));
+ if (diag_status & 1<<11) {
+ printk(KERN_WARNING "%s: 82586 failed self-test\n",
+ dev->name);
+ } else if (!(diag_status & 1<<13)) {
+ printk(KERN_WARNING "%s: 82586 self-test failed to complete\n", dev->name);
+ }
+
+ outw(CONF_TDR_RESULT & ~31, ioaddr + SM_PTR);
+ tdr_status = inw(ioaddr + SHADOW(CONF_TDR_RESULT));
+ if (tdr_status & (TDR_SHORT|TDR_OPEN)) {
+ printk(KERN_WARNING "%s: TDR reports cable %s at %d tick%s\n", dev->name, (tdr_status & TDR_SHORT)?"short":"broken", tdr_status & TDR_TIME, ((tdr_status & TDR_TIME) != 1) ? "s" : "");
+ }
+ else if (tdr_status & TDR_XCVRPROBLEM) {
+ printk(KERN_WARNING "%s: TDR reports transceiver problem\n", dev->name);
+ }
+ else if (tdr_status & TDR_LINKOK) {
+#if NET_DEBUG > 4
+ printk(KERN_DEBUG "%s: TDR reports link OK\n", dev->name);
+#endif
+ } else {
+ printk("%s: TDR is ga-ga (status %04x)\n", dev->name,
+ tdr_status);
+ }
+
+ lp->started |= STARTED_CU;
+ scb_wrcbl(dev, lp->tx_link);
+ /* if the RU isn't running, start it now */
+ if (!(lp->started & STARTED_RU)) {
+ ack_cmd |= SCB_RUstart;
+ scb_wrrfa(dev, lp->rx_buf_start);
+ lp->rx_ptr = lp->rx_buf_start;
+ lp->started |= STARTED_RU;
+ }
+ ack_cmd |= SCB_CUstart | 0x2000;
+ }
+
+ if ((dev->flags & IFF_UP) && !(lp->started & STARTED_RU) && SCB_RUstat(status)==4)
+ lp->started|=STARTED_RU;
+
+ return ack_cmd;
+}
+
+static void eexp_cmd_clear(struct net_device *dev)
+{
+ unsigned long int oldtime = jiffies;
+ while (scb_rdcmd(dev) && (time_before(jiffies, oldtime + 10)));
+ if (scb_rdcmd(dev)) {
+ printk("%s: command didn't clear\n", dev->name);
+ }
+}
+
+static irqreturn_t eexp_irq(int dummy, void *dev_info)
+{
+ struct net_device *dev = dev_info;
+ struct net_local *lp;
+ unsigned short ioaddr,status,ack_cmd;
+ unsigned short old_read_ptr, old_write_ptr;
+
+ lp = netdev_priv(dev);
+ ioaddr = dev->base_addr;
+
+ spin_lock(&lp->lock);
+
+ old_read_ptr = inw(ioaddr+READ_PTR);
+ old_write_ptr = inw(ioaddr+WRITE_PTR);
+
+ outb(SIRQ_dis|irqrmap[dev->irq], ioaddr+SET_IRQ);
+
+ status = scb_status(dev);
+
+#if NET_DEBUG > 4
+ printk(KERN_DEBUG "%s: interrupt (status %x)\n", dev->name, status);
+#endif
+
+ if (lp->started == (STARTED_CU | STARTED_RU)) {
+
+ do {
+ eexp_cmd_clear(dev);
+
+ ack_cmd = SCB_ack(status);
+ scb_command(dev, ack_cmd);
+ outb(0,ioaddr+SIGNAL_CA);
+
+ eexp_cmd_clear(dev);
+
+ if (SCB_complete(status)) {
+ if (!eexp_hw_lasttxstat(dev)) {
+ printk("%s: tx interrupt but no status\n", dev->name);
+ }
+ }
+
+ if (SCB_rxdframe(status))
+ eexp_hw_rx_pio(dev);
+
+ status = scb_status(dev);
+ } while (status & 0xc000);
+
+ if (SCB_RUdead(status))
+ {
+ printk(KERN_WARNING "%s: RU stopped: status %04x\n",
+ dev->name,status);
+#if 0
+ printk(KERN_WARNING "%s: cur_rfd=%04x, cur_rbd=%04x\n", dev->name, lp->cur_rfd, lp->cur_rbd);
+ outw(lp->cur_rfd, ioaddr+READ_PTR);
+ printk(KERN_WARNING "%s: [%04x]\n", dev->name, inw(ioaddr+DATAPORT));
+ outw(lp->cur_rfd+6, ioaddr+READ_PTR);
+ printk(KERN_WARNING "%s: rbd is %04x\n", dev->name, rbd= inw(ioaddr+DATAPORT));
+ outw(rbd, ioaddr+READ_PTR);
+ printk(KERN_WARNING "%s: [%04x %04x] ", dev->name, inw(ioaddr+DATAPORT), inw(ioaddr+DATAPORT));
+ outw(rbd+8, ioaddr+READ_PTR);
+ printk("[%04x]\n", inw(ioaddr+DATAPORT));
+#endif
+ dev->stats.rx_errors++;
+#if 1
+ eexp_hw_rxinit(dev);
+#else
+ lp->cur_rfd = lp->first_rfd;
+#endif
+ scb_wrrfa(dev, lp->rx_buf_start);
+ scb_command(dev, SCB_RUstart);
+ outb(0,ioaddr+SIGNAL_CA);
+ }
+ } else {
+ if (status & 0x8000)
+ ack_cmd = eexp_start_irq(dev, status);
+ else
+ ack_cmd = SCB_ack(status);
+ scb_command(dev, ack_cmd);
+ outb(0,ioaddr+SIGNAL_CA);
+ }
+
+ eexp_cmd_clear(dev);
+
+ outb(SIRQ_en|irqrmap[dev->irq], ioaddr+SET_IRQ);
+
+#if NET_DEBUG > 6
+ printk("%s: leaving eexp_irq()\n", dev->name);
+#endif
+ outw(old_read_ptr, ioaddr+READ_PTR);
+ outw(old_write_ptr, ioaddr+WRITE_PTR);
+
+ spin_unlock(&lp->lock);
+ return IRQ_HANDLED;
+}
+
+/*
+ * Hardware access functions
+ */
+
+/*
+ * Set the cable type to use.
+ */
+
+static void eexp_hw_set_interface(struct net_device *dev)
+{
+ unsigned char oldval = inb(dev->base_addr + 0x300e);
+ oldval &= ~0x82;
+ switch (dev->if_port) {
+ case TPE:
+ oldval |= 0x2;
+ case BNC:
+ oldval |= 0x80;
+ break;
+ }
+ outb(oldval, dev->base_addr+0x300e);
+ mdelay(20);
+}
+
+/*
+ * Check all the receive buffers, and hand any received packets
+ * to the upper levels. Basic sanity check on each frame
+ * descriptor, though we don't bother trying to fix broken ones.
+ */
+
+static void eexp_hw_rx_pio(struct net_device *dev)
+{
+ struct net_local *lp = netdev_priv(dev);
+ unsigned short rx_block = lp->rx_ptr;
+ unsigned short boguscount = lp->num_rx_bufs;
+ unsigned short ioaddr = dev->base_addr;
+ unsigned short status;
+
+#if NET_DEBUG > 6
+ printk(KERN_DEBUG "%s: eexp_hw_rx()\n", dev->name);
+#endif
+
+ do {
+ unsigned short rfd_cmd, rx_next, pbuf, pkt_len;
+
+ outw(rx_block, ioaddr + READ_PTR);
+ status = inw(ioaddr + DATAPORT);
+
+ if (FD_Done(status))
+ {
+ rfd_cmd = inw(ioaddr + DATAPORT);
+ rx_next = inw(ioaddr + DATAPORT);
+ pbuf = inw(ioaddr + DATAPORT);
+
+ outw(pbuf, ioaddr + READ_PTR);
+ pkt_len = inw(ioaddr + DATAPORT);
+
+ if (rfd_cmd!=0x0000)
+ {
+ printk(KERN_WARNING "%s: rfd_cmd not zero:0x%04x\n",
+ dev->name, rfd_cmd);
+ continue;
+ }
+ else if (pbuf!=rx_block+0x16)
+ {
+ printk(KERN_WARNING "%s: rfd and rbd out of sync 0x%04x 0x%04x\n",
+ dev->name, rx_block+0x16, pbuf);
+ continue;
+ }
+ else if ((pkt_len & 0xc000)!=0xc000)
+ {
+ printk(KERN_WARNING "%s: EOF or F not set on received buffer (%04x)\n",
+ dev->name, pkt_len & 0xc000);
+ continue;
+ }
+ else if (!FD_OK(status))
+ {
+ dev->stats.rx_errors++;
+ if (FD_CRC(status))
+ dev->stats.rx_crc_errors++;
+ if (FD_Align(status))
+ dev->stats.rx_frame_errors++;
+ if (FD_Resrc(status))
+ dev->stats.rx_fifo_errors++;
+ if (FD_DMA(status))
+ dev->stats.rx_over_errors++;
+ if (FD_Short(status))
+ dev->stats.rx_length_errors++;
+ }
+ else
+ {
+ struct sk_buff *skb;
+ pkt_len &= 0x3fff;
+ skb = dev_alloc_skb(pkt_len+16);
+ if (skb == NULL)
+ {
+ printk(KERN_WARNING "%s: Memory squeeze, dropping packet\n",dev->name);
+ dev->stats.rx_dropped++;
+ break;
+ }
+ skb_reserve(skb, 2);
+ outw(pbuf+10, ioaddr+READ_PTR);
+ insw(ioaddr+DATAPORT, skb_put(skb,pkt_len),(pkt_len+1)>>1);
+ skb->protocol = eth_type_trans(skb,dev);
+ netif_rx(skb);
+ dev->stats.rx_packets++;
+ dev->stats.rx_bytes += pkt_len;
+ }
+ outw(rx_block, ioaddr+WRITE_PTR);
+ outw(0, ioaddr+DATAPORT);
+ outw(0, ioaddr+DATAPORT);
+ rx_block = rx_next;
+ }
+ } while (FD_Done(status) && boguscount--);
+ lp->rx_ptr = rx_block;
+}
+
+/*
+ * Hand a packet to the card for transmission
+ * If we get here, we MUST have already checked
+ * to make sure there is room in the transmit
+ * buffer region.
+ */
+
+static void eexp_hw_tx_pio(struct net_device *dev, unsigned short *buf,
+ unsigned short len)
+{
+ struct net_local *lp = netdev_priv(dev);
+ unsigned short ioaddr = dev->base_addr;
+
+ if (LOCKUP16 || lp->width) {
+ /* Stop the CU so that there is no chance that it
+ jumps off to a bogus address while we are writing the
+ pointer to the next transmit packet in 8-bit mode --
+ this eliminates the "CU wedged" errors in 8-bit mode.
+ (Zoltan Szilagyi 10-12-96) */
+ scb_command(dev, SCB_CUsuspend);
+ outw(0xFFFF, ioaddr+SIGNAL_CA);
+ }
+
+ outw(lp->tx_head, ioaddr + WRITE_PTR);
+
+ outw(0x0000, ioaddr + DATAPORT);
+ outw(Cmd_INT|Cmd_Xmit, ioaddr + DATAPORT);
+ outw(lp->tx_head+0x08, ioaddr + DATAPORT);
+ outw(lp->tx_head+0x0e, ioaddr + DATAPORT);
+
+ outw(0x0000, ioaddr + DATAPORT);
+ outw(0x0000, ioaddr + DATAPORT);
+ outw(lp->tx_head+0x08, ioaddr + DATAPORT);
+
+ outw(0x8000|len, ioaddr + DATAPORT);
+ outw(-1, ioaddr + DATAPORT);
+ outw(lp->tx_head+0x16, ioaddr + DATAPORT);
+ outw(0, ioaddr + DATAPORT);
+
+ outsw(ioaddr + DATAPORT, buf, (len+1)>>1);
+
+ outw(lp->tx_tail+0xc, ioaddr + WRITE_PTR);
+ outw(lp->tx_head, ioaddr + DATAPORT);
+
+ dev->trans_start = jiffies;
+ lp->tx_tail = lp->tx_head;
+ if (lp->tx_head==TX_BUF_START+((lp->num_tx_bufs-1)*TX_BUF_SIZE))
+ lp->tx_head = TX_BUF_START;
+ else
+ lp->tx_head += TX_BUF_SIZE;
+ if (lp->tx_head != lp->tx_reap)
+ netif_wake_queue(dev);
+
+ if (LOCKUP16 || lp->width) {
+ /* Restart the CU so that the packet can actually
+ be transmitted. (Zoltan Szilagyi 10-12-96) */
+ scb_command(dev, SCB_CUresume);
+ outw(0xFFFF, ioaddr+SIGNAL_CA);
+ }
+
+ dev->stats.tx_packets++;
+ lp->last_tx = jiffies;
+}
+
+static const struct net_device_ops eexp_netdev_ops = {
+ .ndo_open = eexp_open,
+ .ndo_stop = eexp_close,
+ .ndo_start_xmit = eexp_xmit,
+ .ndo_set_rx_mode = eexp_set_multicast,
+ .ndo_tx_timeout = eexp_timeout,
+ .ndo_change_mtu = eth_change_mtu,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_validate_addr = eth_validate_addr,
+};
+
+/*
+ * Sanity check the suspected EtherExpress card
+ * Read hardware address, reset card, size memory and initialize buffer
+ * memory pointers. These are held in netdev_priv(), in case someone has more
+ * than one card in a machine.
+ */
+
+static int __init eexp_hw_probe(struct net_device *dev, unsigned short ioaddr)
+{
+ unsigned short hw_addr[3];
+ unsigned char buswidth;
+ unsigned int memory_size;
+ int i;
+ unsigned short xsum = 0;
+ struct net_local *lp = netdev_priv(dev);
+
+ printk("%s: EtherExpress 16 at %#x ",dev->name,ioaddr);
+
+ outb(ASIC_RST, ioaddr+EEPROM_Ctrl);
+ outb(0, ioaddr+EEPROM_Ctrl);
+ udelay(500);
+ outb(i586_RST, ioaddr+EEPROM_Ctrl);
+
+ hw_addr[0] = eexp_hw_readeeprom(ioaddr,2);
+ hw_addr[1] = eexp_hw_readeeprom(ioaddr,3);
+ hw_addr[2] = eexp_hw_readeeprom(ioaddr,4);
+
+ /* Standard Address or Compaq LTE Address */
+ if (!((hw_addr[2]==0x00aa && ((hw_addr[1] & 0xff00)==0x0000)) ||
+ (hw_addr[2]==0x0080 && ((hw_addr[1] & 0xff00)==0x5F00))))
+ {
+ printk(" rejected: invalid address %04x%04x%04x\n",
+ hw_addr[2],hw_addr[1],hw_addr[0]);
+ return -ENODEV;
+ }
+
+ /* Calculate the EEPROM checksum. Carry on anyway if it's bad,
+ * though.
+ */
+ for (i = 0; i < 64; i++)
+ xsum += eexp_hw_readeeprom(ioaddr, i);
+ if (xsum != 0xbaba)
+ printk(" (bad EEPROM xsum 0x%02x)", xsum);
+
+ dev->base_addr = ioaddr;
+ for ( i=0 ; i<6 ; i++ )
+ dev->dev_addr[i] = ((unsigned char *)hw_addr)[5-i];
+
+ {
+ static const char irqmap[] = { 0, 9, 3, 4, 5, 10, 11, 0 };
+ unsigned short setupval = eexp_hw_readeeprom(ioaddr,0);
+
+ /* Use the IRQ from EEPROM if none was given */
+ if (!dev->irq)
+ dev->irq = irqmap[setupval>>13];
+
+ if (dev->if_port == 0xff) {
+ dev->if_port = !(setupval & 0x1000) ? AUI :
+ eexp_hw_readeeprom(ioaddr,5) & 0x1 ? TPE : BNC;
+ }
+
+ buswidth = !((setupval & 0x400) >> 10);
+ }
+
+ memset(lp, 0, sizeof(struct net_local));
+ spin_lock_init(&lp->lock);
+
+ printk("(IRQ %d, %s connector, %d-bit bus", dev->irq,
+ eexp_ifmap[dev->if_port], buswidth?8:16);
+
+ if (!request_region(dev->base_addr + 0x300e, 1, "EtherExpress"))
+ return -EBUSY;
+
+ eexp_hw_set_interface(dev);
+
+ release_region(dev->base_addr + 0x300e, 1);
+
+ /* Find out how much RAM we have on the card */
+ outw(0, dev->base_addr + WRITE_PTR);
+ for (i = 0; i < 32768; i++)
+ outw(0, dev->base_addr + DATAPORT);
+
+ for (memory_size = 0; memory_size < 64; memory_size++)
+ {
+ outw(memory_size<<10, dev->base_addr + READ_PTR);
+ if (inw(dev->base_addr+DATAPORT))
+ break;
+ outw(memory_size<<10, dev->base_addr + WRITE_PTR);
+ outw(memory_size | 0x5000, dev->base_addr+DATAPORT);
+ outw(memory_size<<10, dev->base_addr + READ_PTR);
+ if (inw(dev->base_addr+DATAPORT) != (memory_size | 0x5000))
+ break;
+ }
+
+ /* Sort out the number of buffers. We may have 16, 32, 48 or 64k
+ * of RAM to play with.
+ */
+ lp->num_tx_bufs = 4;
+ lp->rx_buf_end = 0x3ff6;
+ switch (memory_size)
+ {
+ case 64:
+ lp->rx_buf_end += 0x4000;
+ case 48:
+ lp->num_tx_bufs += 4;
+ lp->rx_buf_end += 0x4000;
+ case 32:
+ lp->rx_buf_end += 0x4000;
+ case 16:
+ printk(", %dk RAM)\n", memory_size);
+ break;
+ default:
+ printk(") bad memory size (%dk).\n", memory_size);
+ return -ENODEV;
+ break;
+ }
+
+ lp->rx_buf_start = TX_BUF_START + (lp->num_tx_bufs*TX_BUF_SIZE);
+ lp->width = buswidth;
+
+ dev->netdev_ops = &eexp_netdev_ops;
+ dev->watchdog_timeo = 2*HZ;
+
+ return register_netdev(dev);
+}
+
+/*
+ * Read a word from the EtherExpress on-board serial EEPROM.
+ * The EEPROM contains 64 words of 16 bits.
+ */
+static unsigned short __init eexp_hw_readeeprom(unsigned short ioaddr,
+ unsigned char location)
+{
+ unsigned short cmd = 0x180|(location&0x7f);
+ unsigned short rval = 0,wval = EC_CS|i586_RST;
+ int i;
+
+ outb(EC_CS|i586_RST,ioaddr+EEPROM_Ctrl);
+ for (i=0x100 ; i ; i>>=1 )
+ {
+ if (cmd&i)
+ wval |= EC_Wr;
+ else
+ wval &= ~EC_Wr;
+
+ outb(wval,ioaddr+EEPROM_Ctrl);
+ outb(wval|EC_Clk,ioaddr+EEPROM_Ctrl);
+ eeprom_delay();
+ outb(wval,ioaddr+EEPROM_Ctrl);
+ eeprom_delay();
+ }
+ wval &= ~EC_Wr;
+ outb(wval,ioaddr+EEPROM_Ctrl);
+ for (i=0x8000 ; i ; i>>=1 )
+ {
+ outb(wval|EC_Clk,ioaddr+EEPROM_Ctrl);
+ eeprom_delay();
+ if (inb(ioaddr+EEPROM_Ctrl)&EC_Rd)
+ rval |= i;
+ outb(wval,ioaddr+EEPROM_Ctrl);
+ eeprom_delay();
+ }
+ wval &= ~EC_CS;
+ outb(wval|EC_Clk,ioaddr+EEPROM_Ctrl);
+ eeprom_delay();
+ outb(wval,ioaddr+EEPROM_Ctrl);
+ eeprom_delay();
+ return rval;
+}
+
+/*
+ * Reap tx buffers and return last transmit status.
+ * if ==0 then either:
+ * a) we're not transmitting anything, so why are we here?
+ * b) we've died.
+ * otherwise, Stat_Busy(return) means we've still got some packets
+ * to transmit, Stat_Done(return) means our buffers should be empty
+ * again
+ */
+
+static unsigned short eexp_hw_lasttxstat(struct net_device *dev)
+{
+ struct net_local *lp = netdev_priv(dev);
+ unsigned short tx_block = lp->tx_reap;
+ unsigned short status;
+
+ if (!netif_queue_stopped(dev) && lp->tx_head==lp->tx_reap)
+ return 0x0000;
+
+ do
+ {
+ outw(tx_block & ~31, dev->base_addr + SM_PTR);
+ status = inw(dev->base_addr + SHADOW(tx_block));
+ if (!Stat_Done(status))
+ {
+ lp->tx_link = tx_block;
+ return status;
+ }
+ else
+ {
+ lp->last_tx_restart = 0;
+ dev->stats.collisions += Stat_NoColl(status);
+ if (!Stat_OK(status))
+ {
+ char *whatsup = NULL;
+ dev->stats.tx_errors++;
+ if (Stat_Abort(status))
+ dev->stats.tx_aborted_errors++;
+ if (Stat_TNoCar(status)) {
+ whatsup = "aborted, no carrier";
+ dev->stats.tx_carrier_errors++;
+ }
+ if (Stat_TNoCTS(status)) {
+ whatsup = "aborted, lost CTS";
+ dev->stats.tx_carrier_errors++;
+ }
+ if (Stat_TNoDMA(status)) {
+ whatsup = "FIFO underran";
+ dev->stats.tx_fifo_errors++;
+ }
+ if (Stat_TXColl(status)) {
+ whatsup = "aborted, too many collisions";
+ dev->stats.tx_aborted_errors++;
+ }
+ if (whatsup)
+ printk(KERN_INFO "%s: transmit %s\n",
+ dev->name, whatsup);
+ }
+ else
+ dev->stats.tx_packets++;
+ }
+ if (tx_block == TX_BUF_START+((lp->num_tx_bufs-1)*TX_BUF_SIZE))
+ lp->tx_reap = tx_block = TX_BUF_START;
+ else
+ lp->tx_reap = tx_block += TX_BUF_SIZE;
+ netif_wake_queue(dev);
+ }
+ while (lp->tx_reap != lp->tx_head);
+
+ lp->tx_link = lp->tx_tail + 0x08;
+
+ return status;
+}
+
+/*
+ * This should never happen. It is called when some higher routine detects
+ * that the CU has stopped, to try to restart it from the last packet we knew
+ * we were working on, or the idle loop if we had finished for the time.
+ */
+
+static void eexp_hw_txrestart(struct net_device *dev)
+{
+ struct net_local *lp = netdev_priv(dev);
+ unsigned short ioaddr = dev->base_addr;
+
+ lp->last_tx_restart = lp->tx_link;
+ scb_wrcbl(dev, lp->tx_link);
+ scb_command(dev, SCB_CUstart);
+ outb(0,ioaddr+SIGNAL_CA);
+
+ {
+ unsigned short boguscount=50,failcount=5;
+ while (!scb_status(dev))
+ {
+ if (!--boguscount)
+ {
+ if (--failcount)
+ {
+ printk(KERN_WARNING "%s: CU start timed out, status %04x, cmd %04x\n", dev->name, scb_status(dev), scb_rdcmd(dev));
+ scb_wrcbl(dev, lp->tx_link);
+ scb_command(dev, SCB_CUstart);
+ outb(0,ioaddr+SIGNAL_CA);
+ boguscount = 100;
+ }
+ else
+ {
+ printk(KERN_WARNING "%s: Failed to restart CU, resetting board...\n",dev->name);
+ eexp_hw_init586(dev);
+ netif_wake_queue(dev);
+ return;
+ }
+ }
+ }
+ }
+}
+
+/*
+ * Writes down the list of transmit buffers into card memory. Each
+ * entry consists of an 82586 transmit command, followed by a jump
+ * pointing to itself. When we want to transmit a packet, we write
+ * the data into the appropriate transmit buffer and then modify the
+ * preceding jump to point at the new transmit command. This means that
+ * the 586 command unit is continuously active.
+ */
+
+static void eexp_hw_txinit(struct net_device *dev)
+{
+ struct net_local *lp = netdev_priv(dev);
+ unsigned short tx_block = TX_BUF_START;
+ unsigned short curtbuf;
+ unsigned short ioaddr = dev->base_addr;
+
+ for ( curtbuf=0 ; curtbuf<lp->num_tx_bufs ; curtbuf++ )
+ {
+ outw(tx_block, ioaddr + WRITE_PTR);
+
+ outw(0x0000, ioaddr + DATAPORT);
+ outw(Cmd_INT|Cmd_Xmit, ioaddr + DATAPORT);
+ outw(tx_block+0x08, ioaddr + DATAPORT);
+ outw(tx_block+0x0e, ioaddr + DATAPORT);
+
+ outw(0x0000, ioaddr + DATAPORT);
+ outw(0x0000, ioaddr + DATAPORT);
+ outw(tx_block+0x08, ioaddr + DATAPORT);
+
+ outw(0x8000, ioaddr + DATAPORT);
+ outw(-1, ioaddr + DATAPORT);
+ outw(tx_block+0x16, ioaddr + DATAPORT);
+ outw(0x0000, ioaddr + DATAPORT);
+
+ tx_block += TX_BUF_SIZE;
+ }
+ lp->tx_head = TX_BUF_START;
+ lp->tx_reap = TX_BUF_START;
+ lp->tx_tail = tx_block - TX_BUF_SIZE;
+ lp->tx_link = lp->tx_tail + 0x08;
+ lp->rx_buf_start = tx_block;
+
+}
+
+/*
+ * Write the circular list of receive buffer descriptors to card memory.
+ * The end of the list isn't marked, which means that the 82586 receive
+ * unit will loop until buffers become available (this avoids it giving us
+ * "out of resources" messages).
+ */
+
+static void eexp_hw_rxinit(struct net_device *dev)
+{
+ struct net_local *lp = netdev_priv(dev);
+ unsigned short rx_block = lp->rx_buf_start;
+ unsigned short ioaddr = dev->base_addr;
+
+ lp->num_rx_bufs = 0;
+ lp->rx_first = lp->rx_ptr = rx_block;
+ do
+ {
+ lp->num_rx_bufs++;
+
+ outw(rx_block, ioaddr + WRITE_PTR);
+
+ outw(0, ioaddr + DATAPORT); outw(0, ioaddr+DATAPORT);
+ outw(rx_block + RX_BUF_SIZE, ioaddr+DATAPORT);
+ outw(0xffff, ioaddr+DATAPORT);
+
+ outw(0x0000, ioaddr+DATAPORT);
+ outw(0xdead, ioaddr+DATAPORT);
+ outw(0xdead, ioaddr+DATAPORT);
+ outw(0xdead, ioaddr+DATAPORT);
+ outw(0xdead, ioaddr+DATAPORT);
+ outw(0xdead, ioaddr+DATAPORT);
+ outw(0xdead, ioaddr+DATAPORT);
+
+ outw(0x0000, ioaddr+DATAPORT);
+ outw(rx_block + RX_BUF_SIZE + 0x16, ioaddr+DATAPORT);
+ outw(rx_block + 0x20, ioaddr+DATAPORT);
+ outw(0, ioaddr+DATAPORT);
+ outw(RX_BUF_SIZE-0x20, ioaddr+DATAPORT);
+
+ lp->rx_last = rx_block;
+ rx_block += RX_BUF_SIZE;
+ } while (rx_block <= lp->rx_buf_end-RX_BUF_SIZE);
+
+
+ /* Make first Rx frame descriptor point to first Rx buffer
+ descriptor */
+ outw(lp->rx_first + 6, ioaddr+WRITE_PTR);
+ outw(lp->rx_first + 0x16, ioaddr+DATAPORT);
+
+ /* Close Rx frame descriptor ring */
+ outw(lp->rx_last + 4, ioaddr+WRITE_PTR);
+ outw(lp->rx_first, ioaddr+DATAPORT);
+
+ /* Close Rx buffer descriptor ring */
+ outw(lp->rx_last + 0x16 + 2, ioaddr+WRITE_PTR);
+ outw(lp->rx_first + 0x16, ioaddr+DATAPORT);
+
+}
+
+/*
+ * Un-reset the 586, and start the configuration sequence. We don't wait for
+ * this to finish, but allow the interrupt handler to start the CU and RU for
+ * us. We can't start the receive/transmission system up before we know that
+ * the hardware is configured correctly.
+ */
+
+static void eexp_hw_init586(struct net_device *dev)
+{
+ struct net_local *lp = netdev_priv(dev);
+ unsigned short ioaddr = dev->base_addr;
+ int i;
+
+#if NET_DEBUG > 6
+ printk("%s: eexp_hw_init586()\n", dev->name);
+#endif
+
+ lp->started = 0;
+
+ set_loopback(dev);
+
+ outb(SIRQ_dis|irqrmap[dev->irq],ioaddr+SET_IRQ);
+
+ /* Download the startup code */
+ outw(lp->rx_buf_end & ~31, ioaddr + SM_PTR);
+ outw(lp->width?0x0001:0x0000, ioaddr + 0x8006);
+ outw(0x0000, ioaddr + 0x8008);
+ outw(0x0000, ioaddr + 0x800a);
+ outw(0x0000, ioaddr + 0x800c);
+ outw(0x0000, ioaddr + 0x800e);
+
+ for (i = 0; i < ARRAY_SIZE(start_code) * 2; i+=32) {
+ int j;
+ outw(i, ioaddr + SM_PTR);
+ for (j = 0; j < 16 && (i+j)/2 < ARRAY_SIZE(start_code); j+=2)
+ outw(start_code[(i+j)/2],
+ ioaddr+0x4000+j);
+ for (j = 0; j < 16 && (i+j+16)/2 < ARRAY_SIZE(start_code); j+=2)
+ outw(start_code[(i+j+16)/2],
+ ioaddr+0x8000+j);
+ }
+
+ /* Do we want promiscuous mode or multicast? */
+ outw(CONF_PROMISC & ~31, ioaddr+SM_PTR);
+ i = inw(ioaddr+SHADOW(CONF_PROMISC));
+ outw((dev->flags & IFF_PROMISC)?(i|1):(i & ~1),
+ ioaddr+SHADOW(CONF_PROMISC));
+ lp->was_promisc = dev->flags & IFF_PROMISC;
+#if 0
+ eexp_setup_filter(dev);
+#endif
+
+ /* Write our hardware address */
+ outw(CONF_HWADDR & ~31, ioaddr+SM_PTR);
+ outw(((unsigned short *)dev->dev_addr)[0], ioaddr+SHADOW(CONF_HWADDR));
+ outw(((unsigned short *)dev->dev_addr)[1],
+ ioaddr+SHADOW(CONF_HWADDR+2));
+ outw(((unsigned short *)dev->dev_addr)[2],
+ ioaddr+SHADOW(CONF_HWADDR+4));
+
+ eexp_hw_txinit(dev);
+ eexp_hw_rxinit(dev);
+
+ outb(0,ioaddr+EEPROM_Ctrl);
+ mdelay(5);
+
+ scb_command(dev, 0xf000);
+ outb(0,ioaddr+SIGNAL_CA);
+
+ outw(0, ioaddr+SM_PTR);
+
+ {
+ unsigned short rboguscount=50,rfailcount=5;
+ while (inw(ioaddr+0x4000))
+ {
+ if (!--rboguscount)
+ {
+ printk(KERN_WARNING "%s: i82586 reset timed out, kicking...\n",
+ dev->name);
+ scb_command(dev, 0);
+ outb(0,ioaddr+SIGNAL_CA);
+ rboguscount = 100;
+ if (!--rfailcount)
+ {
+ printk(KERN_WARNING "%s: i82586 not responding, giving up.\n",
+ dev->name);
+ return;
+ }
+ }
+ }
+ }
+
+ scb_wrcbl(dev, CONF_LINK);
+ scb_command(dev, 0xf000|SCB_CUstart);
+ outb(0,ioaddr+SIGNAL_CA);
+
+ {
+ unsigned short iboguscount=50,ifailcount=5;
+ while (!scb_status(dev))
+ {
+ if (!--iboguscount)
+ {
+ if (--ifailcount)
+ {
+ printk(KERN_WARNING "%s: i82586 initialization timed out, status %04x, cmd %04x\n",
+ dev->name, scb_status(dev), scb_rdcmd(dev));
+ scb_wrcbl(dev, CONF_LINK);
+ scb_command(dev, 0xf000|SCB_CUstart);
+ outb(0,ioaddr+SIGNAL_CA);
+ iboguscount = 100;
+ }
+ else
+ {
+ printk(KERN_WARNING "%s: Failed to initialize i82586, giving up.\n",dev->name);
+ return;
+ }
+ }
+ }
+ }
+
+ clear_loopback(dev);
+ outb(SIRQ_en|irqrmap[dev->irq],ioaddr+SET_IRQ);
+
+ lp->init_time = jiffies;
+#if NET_DEBUG > 6
+ printk("%s: leaving eexp_hw_init586()\n", dev->name);
+#endif
+}
+
+static void eexp_setup_filter(struct net_device *dev)
+{
+ struct netdev_hw_addr *ha;
+ unsigned short ioaddr = dev->base_addr;
+ int count = netdev_mc_count(dev);
+ int i;
+ if (count > 8) {
+ printk(KERN_INFO "%s: too many multicast addresses (%d)\n",
+ dev->name, count);
+ count = 8;
+ }
+
+ outw(CONF_NR_MULTICAST & ~31, ioaddr+SM_PTR);
+ outw(6*count, ioaddr+SHADOW(CONF_NR_MULTICAST));
+ i = 0;
+ netdev_for_each_mc_addr(ha, dev) {
+ unsigned short *data = (unsigned short *) ha->addr;
+
+ if (i == count)
+ break;
+ outw((CONF_MULTICAST+(6*i)) & ~31, ioaddr+SM_PTR);
+ outw(data[0], ioaddr+SHADOW(CONF_MULTICAST+(6*i)));
+ outw((CONF_MULTICAST+(6*i)+2) & ~31, ioaddr+SM_PTR);
+ outw(data[1], ioaddr+SHADOW(CONF_MULTICAST+(6*i)+2));
+ outw((CONF_MULTICAST+(6*i)+4) & ~31, ioaddr+SM_PTR);
+ outw(data[2], ioaddr+SHADOW(CONF_MULTICAST+(6*i)+4));
+ i++;
+ }
+}
+
+/*
+ * Set or clear the multicast filter for this adaptor.
+ */
+static void
+eexp_set_multicast(struct net_device *dev)
+{
+ unsigned short ioaddr = dev->base_addr;
+ struct net_local *lp = netdev_priv(dev);
+ int kick = 0, i;
+ if ((dev->flags & IFF_PROMISC) != lp->was_promisc) {
+ outw(CONF_PROMISC & ~31, ioaddr+SM_PTR);
+ i = inw(ioaddr+SHADOW(CONF_PROMISC));
+ outw((dev->flags & IFF_PROMISC)?(i|1):(i & ~1),
+ ioaddr+SHADOW(CONF_PROMISC));
+ lp->was_promisc = dev->flags & IFF_PROMISC;
+ kick = 1;
+ }
+ if (!(dev->flags & IFF_PROMISC)) {
+ eexp_setup_filter(dev);
+ if (lp->old_mc_count != netdev_mc_count(dev)) {
+ kick = 1;
+ lp->old_mc_count = netdev_mc_count(dev);
+ }
+ }
+ if (kick) {
+ unsigned long oj;
+ scb_command(dev, SCB_CUsuspend);
+ outb(0, ioaddr+SIGNAL_CA);
+ outb(0, ioaddr+SIGNAL_CA);
+#if 0
+ printk("%s: waiting for CU to go suspended\n", dev->name);
+#endif
+ oj = jiffies;
+ while ((SCB_CUstat(scb_status(dev)) == 2) &&
+ (time_before(jiffies, oj + 2000)));
+ if (SCB_CUstat(scb_status(dev)) == 2)
+ printk("%s: warning, CU didn't stop\n", dev->name);
+ lp->started &= ~(STARTED_CU);
+ scb_wrcbl(dev, CONF_LINK);
+ scb_command(dev, SCB_CUstart);
+ outb(0, ioaddr+SIGNAL_CA);
+ }
+}
+
+
+/*
+ * MODULE stuff
+ */
+
+#ifdef MODULE
+
+#define EEXP_MAX_CARDS 4 /* max number of cards to support */
+
+static struct net_device *dev_eexp[EEXP_MAX_CARDS];
+static int irq[EEXP_MAX_CARDS];
+static int io[EEXP_MAX_CARDS];
+
+module_param_array(io, int, NULL, 0);
+module_param_array(irq, int, NULL, 0);
+MODULE_PARM_DESC(io, "EtherExpress 16 I/O base address(es)");
+MODULE_PARM_DESC(irq, "EtherExpress 16 IRQ number(s)");
+MODULE_LICENSE("GPL");
+
+
+/* Ideally the user would give us io=, irq= for every card. If any parameters
+ * are specified, we verify and then use them. If no parameters are given, we
+ * autoprobe for one card only.
+ */
+int __init init_module(void)
+{
+ struct net_device *dev;
+ int this_dev, found = 0;
+
+ for (this_dev = 0; this_dev < EEXP_MAX_CARDS; this_dev++) {
+ dev = alloc_etherdev(sizeof(struct net_local));
+ dev->irq = irq[this_dev];
+ dev->base_addr = io[this_dev];
+ if (io[this_dev] == 0) {
+ if (this_dev)
+ break;
+ printk(KERN_NOTICE "eexpress.c: Module autoprobe not recommended, give io=xx.\n");
+ }
+ if (do_express_probe(dev) == 0) {
+ dev_eexp[this_dev] = dev;
+ found++;
+ continue;
+ }
+ printk(KERN_WARNING "eexpress.c: Failed to register card at 0x%x.\n", io[this_dev]);
+ free_netdev(dev);
+ break;
+ }
+ if (found)
+ return 0;
+ return -ENXIO;
+}
+
+void __exit cleanup_module(void)
+{
+ int this_dev;
+
+ for (this_dev = 0; this_dev < EEXP_MAX_CARDS; this_dev++) {
+ struct net_device *dev = dev_eexp[this_dev];
+ if (dev) {
+ unregister_netdev(dev);
+ free_netdev(dev);
+ }
+ }
+}
+#endif
+
+/*
+ * Local Variables:
+ * c-file-style: "linux"
+ * tab-width: 8
+ * End:
+ */
diff --git a/drivers/net/ethernet/i825xx/eexpress.h b/drivers/net/ethernet/i825xx/eexpress.h
new file mode 100644
index 00000000000..dc9c6ea289e
--- /dev/null
+++ b/drivers/net/ethernet/i825xx/eexpress.h
@@ -0,0 +1,179 @@
+/*
+ * eexpress.h: Intel EtherExpress16 defines
+ */
+
+/*
+ * EtherExpress card register addresses
+ * as offsets from the base IO region (dev->base_addr)
+ */
+
+#define DATAPORT 0x0000
+#define WRITE_PTR 0x0002
+#define READ_PTR 0x0004
+#define SIGNAL_CA 0x0006
+#define SET_IRQ 0x0007
+#define SM_PTR 0x0008
+#define MEM_Dec 0x000a
+#define MEM_Ctrl 0x000b
+#define MEM_Page_Ctrl 0x000c
+#define Config 0x000d
+#define EEPROM_Ctrl 0x000e
+#define ID_PORT 0x000f
+#define MEM_ECtrl 0x000f
+
+/*
+ * card register defines
+ */
+
+/* SET_IRQ */
+#define SIRQ_en 0x08
+#define SIRQ_dis 0x00
+
+/* EEPROM_Ctrl */
+#define EC_Clk 0x01
+#define EC_CS 0x02
+#define EC_Wr 0x04
+#define EC_Rd 0x08
+#define ASIC_RST 0x40
+#define i586_RST 0x80
+
+#define eeprom_delay() { udelay(40); }
+
+/*
+ * i82586 Memory Configuration
+ */
+
+/* (System Configuration Pointer) System start up block, read after 586_RST */
+#define SCP_START 0xfff6
+
+/* Intermediate System Configuration Pointer */
+#define ISCP_START 0x0000
+
+/* System Command Block */
+#define SCB_START 0x0008
+
+/* Start of buffer region. Everything before this is used for control
+ * structures and the CU configuration program. The memory layout is
+ * determined in eexp_hw_probe(), once we know how much memory is
+ * available on the card.
+ */
+
+#define TX_BUF_START 0x0100
+
+#define TX_BUF_SIZE ((24+ETH_FRAME_LEN+31)&~0x1f)
+#define RX_BUF_SIZE ((32+ETH_FRAME_LEN+31)&~0x1f)
+
+/*
+ * SCB defines
+ */
+
+/* these functions take the SCB status word and test the relevant status bit */
+#define SCB_complete(s) (((s) & 0x8000) != 0)
+#define SCB_rxdframe(s) (((s) & 0x4000) != 0)
+#define SCB_CUdead(s) (((s) & 0x2000) != 0)
+#define SCB_RUdead(s) (((s) & 0x1000) != 0)
+#define SCB_ack(s) ((s) & 0xf000)
+
+/* Command unit status: 0=idle, 1=suspended, 2=active */
+#define SCB_CUstat(s) (((s)&0x0300)>>8)
+
+/* Receive unit status: 0=idle, 1=suspended, 2=out of resources, 4=ready */
+#define SCB_RUstat(s) (((s)&0x0070)>>4)
+
+/* SCB commands */
+#define SCB_CUnop 0x0000
+#define SCB_CUstart 0x0100
+#define SCB_CUresume 0x0200
+#define SCB_CUsuspend 0x0300
+#define SCB_CUabort 0x0400
+#define SCB_resetchip 0x0080
+
+#define SCB_RUnop 0x0000
+#define SCB_RUstart 0x0010
+#define SCB_RUresume 0x0020
+#define SCB_RUsuspend 0x0030
+#define SCB_RUabort 0x0040
+
+/*
+ * Command block defines
+ */
+
+#define Stat_Done(s) (((s) & 0x8000) != 0)
+#define Stat_Busy(s) (((s) & 0x4000) != 0)
+#define Stat_OK(s) (((s) & 0x2000) != 0)
+#define Stat_Abort(s) (((s) & 0x1000) != 0)
+#define Stat_STFail (((s) & 0x0800) != 0)
+#define Stat_TNoCar(s) (((s) & 0x0400) != 0)
+#define Stat_TNoCTS(s) (((s) & 0x0200) != 0)
+#define Stat_TNoDMA(s) (((s) & 0x0100) != 0)
+#define Stat_TDefer(s) (((s) & 0x0080) != 0)
+#define Stat_TColl(s) (((s) & 0x0040) != 0)
+#define Stat_TXColl(s) (((s) & 0x0020) != 0)
+#define Stat_NoColl(s) ((s) & 0x000f)
+
+/* Cmd_END will end AFTER the command if this is the first
+ * command block after an SCB_CUstart, but BEFORE the command
+ * for all subsequent commands. Best strategy is to place
+ * Cmd_INT on the last command in the sequence, followed by a
+ * dummy Cmd_Nop with Cmd_END after this.
+ */
+
+#define Cmd_END 0x8000
+#define Cmd_SUS 0x4000
+#define Cmd_INT 0x2000
+
+#define Cmd_Nop 0x0000
+#define Cmd_SetAddr 0x0001
+#define Cmd_Config 0x0002
+#define Cmd_MCast 0x0003
+#define Cmd_Xmit 0x0004
+#define Cmd_TDR 0x0005
+#define Cmd_Dump 0x0006
+#define Cmd_Diag 0x0007
+
+
+/*
+ * Frame Descriptor (Receive block) defines
+ */
+
+#define FD_Done(s) (((s) & 0x8000) != 0)
+#define FD_Busy(s) (((s) & 0x4000) != 0)
+#define FD_OK(s) (((s) & 0x2000) != 0)
+
+#define FD_CRC(s) (((s) & 0x0800) != 0)
+#define FD_Align(s) (((s) & 0x0400) != 0)
+#define FD_Resrc(s) (((s) & 0x0200) != 0)
+#define FD_DMA(s) (((s) & 0x0100) != 0)
+#define FD_Short(s) (((s) & 0x0080) != 0)
+#define FD_NoEOF(s) (((s) & 0x0040) != 0)
+
+struct rfd_header {
+ volatile unsigned long flags;
+ volatile unsigned short link;
+ volatile unsigned short rbd_offset;
+ volatile unsigned short dstaddr1;
+ volatile unsigned short dstaddr2;
+ volatile unsigned short dstaddr3;
+ volatile unsigned short srcaddr1;
+ volatile unsigned short srcaddr2;
+ volatile unsigned short srcaddr3;
+ volatile unsigned short length;
+
+ /* This is actually a Receive Buffer Descriptor. The way we
+ * arrange memory means that an RBD always follows the RFD that
+ * points to it, so they might as well be in the same structure.
+ */
+ volatile unsigned short actual_count;
+ volatile unsigned short next_rbd;
+ volatile unsigned short buf_addr1;
+ volatile unsigned short buf_addr2;
+ volatile unsigned short size;
+};
+
+/* Returned data from the Time Domain Reflectometer */
+
+#define TDR_LINKOK (1<<15)
+#define TDR_XCVRPROBLEM (1<<14)
+#define TDR_OPEN (1<<13)
+#define TDR_SHORT (1<<12)
+#define TDR_TIME 0x7ff
diff --git a/drivers/net/ethernet/i825xx/ether1.c b/drivers/net/ethernet/i825xx/ether1.c
new file mode 100644
index 00000000000..42e90a97c7a
--- /dev/null
+++ b/drivers/net/ethernet/i825xx/ether1.c
@@ -0,0 +1,1094 @@
+/*
+ * linux/drivers/acorn/net/ether1.c
+ *
+ * Copyright (C) 1996-2000 Russell King
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Acorn ether1 driver (82586 chip) for Acorn machines
+ *
+ * We basically keep two queues in the cards memory - one for transmit
+ * and one for receive. Each has a head and a tail. The head is where
+ * we/the chip adds packets to be transmitted/received, and the tail
+ * is where the transmitter has got to/where the receiver will stop.
+ * Both of these queues are circular, and since the chip is running
+ * all the time, we have to be careful when we modify the pointers etc
+ * so that the buffer memory contents is valid all the time.
+ *
+ * Change log:
+ * 1.00 RMK Released
+ * 1.01 RMK 19/03/1996 Transfers the last odd byte onto/off of the card now.
+ * 1.02 RMK 25/05/1997 Added code to restart RU if it goes not ready
+ * 1.03 RMK 14/09/1997 Cleaned up the handling of a reset during the TX interrupt.
+ * Should prevent lockup.
+ * 1.04 RMK 17/09/1997 Added more info when initialsation of chip goes wrong.
+ * TDR now only reports failure when chip reports non-zero
+ * TDR time-distance.
+ * 1.05 RMK 31/12/1997 Removed calls to dev_tint for 2.1
+ * 1.06 RMK 10/02/2000 Updated for 2.3.43
+ * 1.07 RMK 13/05/2000 Updated for 2.3.99-pre8
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/bitops.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/ecard.h>
+
+#define __ETHER1_C
+#include "ether1.h"
+
+static unsigned int net_debug = NET_DEBUG;
+
+#define BUFFER_SIZE 0x10000
+#define TX_AREA_START 0x00100
+#define TX_AREA_END 0x05000
+#define RX_AREA_START 0x05000
+#define RX_AREA_END 0x0fc00
+
+static int ether1_open(struct net_device *dev);
+static int ether1_sendpacket(struct sk_buff *skb, struct net_device *dev);
+static irqreturn_t ether1_interrupt(int irq, void *dev_id);
+static int ether1_close(struct net_device *dev);
+static void ether1_setmulticastlist(struct net_device *dev);
+static void ether1_timeout(struct net_device *dev);
+
+/* ------------------------------------------------------------------------- */
+
+static char version[] __devinitdata = "ether1 ethernet driver (c) 2000 Russell King v1.07\n";
+
+#define BUS_16 16
+#define BUS_8 8
+
+/* ------------------------------------------------------------------------- */
+
+#define DISABLEIRQS 1
+#define NORMALIRQS 0
+
+#define ether1_readw(dev, addr, type, offset, svflgs) ether1_inw_p (dev, addr + (int)(&((type *)0)->offset), svflgs)
+#define ether1_writew(dev, val, addr, type, offset, svflgs) ether1_outw_p (dev, val, addr + (int)(&((type *)0)->offset), svflgs)
+
+static inline unsigned short
+ether1_inw_p (struct net_device *dev, int addr, int svflgs)
+{
+ unsigned long flags;
+ unsigned short ret;
+
+ if (svflgs)
+ local_irq_save (flags);
+
+ writeb(addr >> 12, REG_PAGE);
+ ret = readw(ETHER1_RAM + ((addr & 4095) << 1));
+ if (svflgs)
+ local_irq_restore (flags);
+ return ret;
+}
+
+static inline void
+ether1_outw_p (struct net_device *dev, unsigned short val, int addr, int svflgs)
+{
+ unsigned long flags;
+
+ if (svflgs)
+ local_irq_save (flags);
+
+ writeb(addr >> 12, REG_PAGE);
+ writew(val, ETHER1_RAM + ((addr & 4095) << 1));
+ if (svflgs)
+ local_irq_restore (flags);
+}
+
+/*
+ * Some inline assembler to allow fast transfers on to/off of the card.
+ * Since this driver depends on some features presented by the ARM
+ * specific architecture, and that you can't configure this driver
+ * without specifiing ARM mode, this is not a problem.
+ *
+ * This routine is essentially an optimised memcpy from the card's
+ * onboard RAM to kernel memory.
+ */
+static void
+ether1_writebuffer (struct net_device *dev, void *data, unsigned int start, unsigned int length)
+{
+ unsigned int page, thislen, offset;
+ void __iomem *addr;
+
+ offset = start & 4095;
+ page = start >> 12;
+ addr = ETHER1_RAM + (offset << 1);
+
+ if (offset + length > 4096)
+ thislen = 4096 - offset;
+ else
+ thislen = length;
+
+ do {
+ int used;
+
+ writeb(page, REG_PAGE);
+ length -= thislen;
+
+ __asm__ __volatile__(
+ "subs %3, %3, #2\n\
+ bmi 2f\n\
+1: ldr %0, [%1], #2\n\
+ mov %0, %0, lsl #16\n\
+ orr %0, %0, %0, lsr #16\n\
+ str %0, [%2], #4\n\
+ subs %3, %3, #2\n\
+ bmi 2f\n\
+ ldr %0, [%1], #2\n\
+ mov %0, %0, lsl #16\n\
+ orr %0, %0, %0, lsr #16\n\
+ str %0, [%2], #4\n\
+ subs %3, %3, #2\n\
+ bmi 2f\n\
+ ldr %0, [%1], #2\n\
+ mov %0, %0, lsl #16\n\
+ orr %0, %0, %0, lsr #16\n\
+ str %0, [%2], #4\n\
+ subs %3, %3, #2\n\
+ bmi 2f\n\
+ ldr %0, [%1], #2\n\
+ mov %0, %0, lsl #16\n\
+ orr %0, %0, %0, lsr #16\n\
+ str %0, [%2], #4\n\
+ subs %3, %3, #2\n\
+ bpl 1b\n\
+2: adds %3, %3, #1\n\
+ ldreqb %0, [%1]\n\
+ streqb %0, [%2]"
+ : "=&r" (used), "=&r" (data)
+ : "r" (addr), "r" (thislen), "1" (data));
+
+ addr = ETHER1_RAM;
+
+ thislen = length;
+ if (thislen > 4096)
+ thislen = 4096;
+ page++;
+ } while (thislen);
+}
+
+static void
+ether1_readbuffer (struct net_device *dev, void *data, unsigned int start, unsigned int length)
+{
+ unsigned int page, thislen, offset;
+ void __iomem *addr;
+
+ offset = start & 4095;
+ page = start >> 12;
+ addr = ETHER1_RAM + (offset << 1);
+
+ if (offset + length > 4096)
+ thislen = 4096 - offset;
+ else
+ thislen = length;
+
+ do {
+ int used;
+
+ writeb(page, REG_PAGE);
+ length -= thislen;
+
+ __asm__ __volatile__(
+ "subs %3, %3, #2\n\
+ bmi 2f\n\
+1: ldr %0, [%2], #4\n\
+ strb %0, [%1], #1\n\
+ mov %0, %0, lsr #8\n\
+ strb %0, [%1], #1\n\
+ subs %3, %3, #2\n\
+ bmi 2f\n\
+ ldr %0, [%2], #4\n\
+ strb %0, [%1], #1\n\
+ mov %0, %0, lsr #8\n\
+ strb %0, [%1], #1\n\
+ subs %3, %3, #2\n\
+ bmi 2f\n\
+ ldr %0, [%2], #4\n\
+ strb %0, [%1], #1\n\
+ mov %0, %0, lsr #8\n\
+ strb %0, [%1], #1\n\
+ subs %3, %3, #2\n\
+ bmi 2f\n\
+ ldr %0, [%2], #4\n\
+ strb %0, [%1], #1\n\
+ mov %0, %0, lsr #8\n\
+ strb %0, [%1], #1\n\
+ subs %3, %3, #2\n\
+ bpl 1b\n\
+2: adds %3, %3, #1\n\
+ ldreqb %0, [%2]\n\
+ streqb %0, [%1]"
+ : "=&r" (used), "=&r" (data)
+ : "r" (addr), "r" (thislen), "1" (data));
+
+ addr = ETHER1_RAM;
+
+ thislen = length;
+ if (thislen > 4096)
+ thislen = 4096;
+ page++;
+ } while (thislen);
+}
+
+static int __devinit
+ether1_ramtest(struct net_device *dev, unsigned char byte)
+{
+ unsigned char *buffer = kmalloc (BUFFER_SIZE, GFP_KERNEL);
+ int i, ret = BUFFER_SIZE;
+ int max_errors = 15;
+ int bad = -1;
+ int bad_start = 0;
+
+ if (!buffer)
+ return 1;
+
+ memset (buffer, byte, BUFFER_SIZE);
+ ether1_writebuffer (dev, buffer, 0, BUFFER_SIZE);
+ memset (buffer, byte ^ 0xff, BUFFER_SIZE);
+ ether1_readbuffer (dev, buffer, 0, BUFFER_SIZE);
+
+ for (i = 0; i < BUFFER_SIZE; i++) {
+ if (buffer[i] != byte) {
+ if (max_errors >= 0 && bad != buffer[i]) {
+ if (bad != -1)
+ printk ("\n");
+ printk (KERN_CRIT "%s: RAM failed with (%02X instead of %02X) at 0x%04X",
+ dev->name, buffer[i], byte, i);
+ ret = -ENODEV;
+ max_errors --;
+ bad = buffer[i];
+ bad_start = i;
+ }
+ } else {
+ if (bad != -1) {
+ if (bad_start == i - 1)
+ printk ("\n");
+ else
+ printk (" - 0x%04X\n", i - 1);
+ bad = -1;
+ }
+ }
+ }
+
+ if (bad != -1)
+ printk (" - 0x%04X\n", BUFFER_SIZE);
+ kfree (buffer);
+
+ return ret;
+}
+
+static int
+ether1_reset (struct net_device *dev)
+{
+ writeb(CTRL_RST|CTRL_ACK, REG_CONTROL);
+ return BUS_16;
+}
+
+static int __devinit
+ether1_init_2(struct net_device *dev)
+{
+ int i;
+ dev->mem_start = 0;
+
+ i = ether1_ramtest (dev, 0x5a);
+
+ if (i > 0)
+ i = ether1_ramtest (dev, 0x1e);
+
+ if (i <= 0)
+ return -ENODEV;
+
+ dev->mem_end = i;
+ return 0;
+}
+
+/*
+ * These are the structures that are loaded into the ether RAM card to
+ * initialise the 82586
+ */
+
+/* at 0x0100 */
+#define NOP_ADDR (TX_AREA_START)
+#define NOP_SIZE (0x06)
+static nop_t init_nop = {
+ 0,
+ CMD_NOP,
+ NOP_ADDR
+};
+
+/* at 0x003a */
+#define TDR_ADDR (0x003a)
+#define TDR_SIZE (0x08)
+static tdr_t init_tdr = {
+ 0,
+ CMD_TDR | CMD_INTR,
+ NOP_ADDR,
+ 0
+};
+
+/* at 0x002e */
+#define MC_ADDR (0x002e)
+#define MC_SIZE (0x0c)
+static mc_t init_mc = {
+ 0,
+ CMD_SETMULTICAST,
+ TDR_ADDR,
+ 0,
+ { { 0, } }
+};
+
+/* at 0x0022 */
+#define SA_ADDR (0x0022)
+#define SA_SIZE (0x0c)
+static sa_t init_sa = {
+ 0,
+ CMD_SETADDRESS,
+ MC_ADDR,
+ { 0, }
+};
+
+/* at 0x0010 */
+#define CFG_ADDR (0x0010)
+#define CFG_SIZE (0x12)
+static cfg_t init_cfg = {
+ 0,
+ CMD_CONFIG,
+ SA_ADDR,
+ 8,
+ 8,
+ CFG8_SRDY,
+ CFG9_PREAMB8 | CFG9_ADDRLENBUF | CFG9_ADDRLEN(6),
+ 0,
+ 0x60,
+ 0,
+ CFG13_RETRY(15) | CFG13_SLOTH(2),
+ 0,
+};
+
+/* at 0x0000 */
+#define SCB_ADDR (0x0000)
+#define SCB_SIZE (0x10)
+static scb_t init_scb = {
+ 0,
+ SCB_CMDACKRNR | SCB_CMDACKCNA | SCB_CMDACKFR | SCB_CMDACKCX,
+ CFG_ADDR,
+ RX_AREA_START,
+ 0,
+ 0,
+ 0,
+ 0
+};
+
+/* at 0xffee */
+#define ISCP_ADDR (0xffee)
+#define ISCP_SIZE (0x08)
+static iscp_t init_iscp = {
+ 1,
+ SCB_ADDR,
+ 0x0000,
+ 0x0000
+};
+
+/* at 0xfff6 */
+#define SCP_ADDR (0xfff6)
+#define SCP_SIZE (0x0a)
+static scp_t init_scp = {
+ SCP_SY_16BBUS,
+ { 0, 0 },
+ ISCP_ADDR,
+ 0
+};
+
+#define RFD_SIZE (0x16)
+static rfd_t init_rfd = {
+ 0,
+ 0,
+ 0,
+ 0,
+ { 0, },
+ { 0, },
+ 0
+};
+
+#define RBD_SIZE (0x0a)
+static rbd_t init_rbd = {
+ 0,
+ 0,
+ 0,
+ 0,
+ ETH_FRAME_LEN + 8
+};
+
+#define TX_SIZE (0x08)
+#define TBD_SIZE (0x08)
+
+static int
+ether1_init_for_open (struct net_device *dev)
+{
+ int i, status, addr, next, next2;
+ int failures = 0;
+ unsigned long timeout;
+
+ writeb(CTRL_RST|CTRL_ACK, REG_CONTROL);
+
+ for (i = 0; i < 6; i++)
+ init_sa.sa_addr[i] = dev->dev_addr[i];
+
+ /* load data structures into ether1 RAM */
+ ether1_writebuffer (dev, &init_scp, SCP_ADDR, SCP_SIZE);
+ ether1_writebuffer (dev, &init_iscp, ISCP_ADDR, ISCP_SIZE);
+ ether1_writebuffer (dev, &init_scb, SCB_ADDR, SCB_SIZE);
+ ether1_writebuffer (dev, &init_cfg, CFG_ADDR, CFG_SIZE);
+ ether1_writebuffer (dev, &init_sa, SA_ADDR, SA_SIZE);
+ ether1_writebuffer (dev, &init_mc, MC_ADDR, MC_SIZE);
+ ether1_writebuffer (dev, &init_tdr, TDR_ADDR, TDR_SIZE);
+ ether1_writebuffer (dev, &init_nop, NOP_ADDR, NOP_SIZE);
+
+ if (ether1_readw(dev, CFG_ADDR, cfg_t, cfg_command, NORMALIRQS) != CMD_CONFIG) {
+ printk (KERN_ERR "%s: detected either RAM fault or compiler bug\n",
+ dev->name);
+ return 1;
+ }
+
+ /*
+ * setup circularly linked list of { rfd, rbd, buffer }, with
+ * all rfds circularly linked, rbds circularly linked.
+ * First rfd is linked to scp, first rbd is linked to first
+ * rfd. Last rbd has a suspend command.
+ */
+ addr = RX_AREA_START;
+ do {
+ next = addr + RFD_SIZE + RBD_SIZE + ETH_FRAME_LEN + 10;
+ next2 = next + RFD_SIZE + RBD_SIZE + ETH_FRAME_LEN + 10;
+
+ if (next2 >= RX_AREA_END) {
+ next = RX_AREA_START;
+ init_rfd.rfd_command = RFD_CMDEL | RFD_CMDSUSPEND;
+ priv(dev)->rx_tail = addr;
+ } else
+ init_rfd.rfd_command = 0;
+ if (addr == RX_AREA_START)
+ init_rfd.rfd_rbdoffset = addr + RFD_SIZE;
+ else
+ init_rfd.rfd_rbdoffset = 0;
+ init_rfd.rfd_link = next;
+ init_rbd.rbd_link = next + RFD_SIZE;
+ init_rbd.rbd_bufl = addr + RFD_SIZE + RBD_SIZE;
+
+ ether1_writebuffer (dev, &init_rfd, addr, RFD_SIZE);
+ ether1_writebuffer (dev, &init_rbd, addr + RFD_SIZE, RBD_SIZE);
+ addr = next;
+ } while (next2 < RX_AREA_END);
+
+ priv(dev)->tx_link = NOP_ADDR;
+ priv(dev)->tx_head = NOP_ADDR + NOP_SIZE;
+ priv(dev)->tx_tail = TDR_ADDR;
+ priv(dev)->rx_head = RX_AREA_START;
+
+ /* release reset & give 586 a prod */
+ priv(dev)->resetting = 1;
+ priv(dev)->initialising = 1;
+ writeb(CTRL_RST, REG_CONTROL);
+ writeb(0, REG_CONTROL);
+ writeb(CTRL_CA, REG_CONTROL);
+
+ /* 586 should now unset iscp.busy */
+ timeout = jiffies + HZ/2;
+ while (ether1_readw(dev, ISCP_ADDR, iscp_t, iscp_busy, DISABLEIRQS) == 1) {
+ if (time_after(jiffies, timeout)) {
+ printk (KERN_WARNING "%s: can't initialise 82586: iscp is busy\n", dev->name);
+ return 1;
+ }
+ }
+
+ /* check status of commands that we issued */
+ timeout += HZ/10;
+ while (((status = ether1_readw(dev, CFG_ADDR, cfg_t, cfg_status, DISABLEIRQS))
+ & STAT_COMPLETE) == 0) {
+ if (time_after(jiffies, timeout))
+ break;
+ }
+
+ if ((status & (STAT_COMPLETE | STAT_OK)) != (STAT_COMPLETE | STAT_OK)) {
+ printk (KERN_WARNING "%s: can't initialise 82586: config status %04X\n", dev->name, status);
+ printk (KERN_DEBUG "%s: SCB=[STS=%04X CMD=%04X CBL=%04X RFA=%04X]\n", dev->name,
+ ether1_readw(dev, SCB_ADDR, scb_t, scb_status, NORMALIRQS),
+ ether1_readw(dev, SCB_ADDR, scb_t, scb_command, NORMALIRQS),
+ ether1_readw(dev, SCB_ADDR, scb_t, scb_cbl_offset, NORMALIRQS),
+ ether1_readw(dev, SCB_ADDR, scb_t, scb_rfa_offset, NORMALIRQS));
+ failures += 1;
+ }
+
+ timeout += HZ/10;
+ while (((status = ether1_readw(dev, SA_ADDR, sa_t, sa_status, DISABLEIRQS))
+ & STAT_COMPLETE) == 0) {
+ if (time_after(jiffies, timeout))
+ break;
+ }
+
+ if ((status & (STAT_COMPLETE | STAT_OK)) != (STAT_COMPLETE | STAT_OK)) {
+ printk (KERN_WARNING "%s: can't initialise 82586: set address status %04X\n", dev->name, status);
+ printk (KERN_DEBUG "%s: SCB=[STS=%04X CMD=%04X CBL=%04X RFA=%04X]\n", dev->name,
+ ether1_readw(dev, SCB_ADDR, scb_t, scb_status, NORMALIRQS),
+ ether1_readw(dev, SCB_ADDR, scb_t, scb_command, NORMALIRQS),
+ ether1_readw(dev, SCB_ADDR, scb_t, scb_cbl_offset, NORMALIRQS),
+ ether1_readw(dev, SCB_ADDR, scb_t, scb_rfa_offset, NORMALIRQS));
+ failures += 1;
+ }
+
+ timeout += HZ/10;
+ while (((status = ether1_readw(dev, MC_ADDR, mc_t, mc_status, DISABLEIRQS))
+ & STAT_COMPLETE) == 0) {
+ if (time_after(jiffies, timeout))
+ break;
+ }
+
+ if ((status & (STAT_COMPLETE | STAT_OK)) != (STAT_COMPLETE | STAT_OK)) {
+ printk (KERN_WARNING "%s: can't initialise 82586: set multicast status %04X\n", dev->name, status);
+ printk (KERN_DEBUG "%s: SCB=[STS=%04X CMD=%04X CBL=%04X RFA=%04X]\n", dev->name,
+ ether1_readw(dev, SCB_ADDR, scb_t, scb_status, NORMALIRQS),
+ ether1_readw(dev, SCB_ADDR, scb_t, scb_command, NORMALIRQS),
+ ether1_readw(dev, SCB_ADDR, scb_t, scb_cbl_offset, NORMALIRQS),
+ ether1_readw(dev, SCB_ADDR, scb_t, scb_rfa_offset, NORMALIRQS));
+ failures += 1;
+ }
+
+ timeout += HZ;
+ while (((status = ether1_readw(dev, TDR_ADDR, tdr_t, tdr_status, DISABLEIRQS))
+ & STAT_COMPLETE) == 0) {
+ if (time_after(jiffies, timeout))
+ break;
+ }
+
+ if ((status & (STAT_COMPLETE | STAT_OK)) != (STAT_COMPLETE | STAT_OK)) {
+ printk (KERN_WARNING "%s: can't tdr (ignored)\n", dev->name);
+ printk (KERN_DEBUG "%s: SCB=[STS=%04X CMD=%04X CBL=%04X RFA=%04X]\n", dev->name,
+ ether1_readw(dev, SCB_ADDR, scb_t, scb_status, NORMALIRQS),
+ ether1_readw(dev, SCB_ADDR, scb_t, scb_command, NORMALIRQS),
+ ether1_readw(dev, SCB_ADDR, scb_t, scb_cbl_offset, NORMALIRQS),
+ ether1_readw(dev, SCB_ADDR, scb_t, scb_rfa_offset, NORMALIRQS));
+ } else {
+ status = ether1_readw(dev, TDR_ADDR, tdr_t, tdr_result, DISABLEIRQS);
+ if (status & TDR_XCVRPROB)
+ printk (KERN_WARNING "%s: i/f failed tdr: transceiver problem\n", dev->name);
+ else if ((status & (TDR_SHORT|TDR_OPEN)) && (status & TDR_TIME)) {
+#ifdef FANCY
+ printk (KERN_WARNING "%s: i/f failed tdr: cable %s %d.%d us away\n", dev->name,
+ status & TDR_SHORT ? "short" : "open", (status & TDR_TIME) / 10,
+ (status & TDR_TIME) % 10);
+#else
+ printk (KERN_WARNING "%s: i/f failed tdr: cable %s %d clks away\n", dev->name,
+ status & TDR_SHORT ? "short" : "open", (status & TDR_TIME));
+#endif
+ }
+ }
+
+ if (failures)
+ ether1_reset (dev);
+ return failures ? 1 : 0;
+}
+
+/* ------------------------------------------------------------------------- */
+
+static int
+ether1_txalloc (struct net_device *dev, int size)
+{
+ int start, tail;
+
+ size = (size + 1) & ~1;
+ tail = priv(dev)->tx_tail;
+
+ if (priv(dev)->tx_head + size > TX_AREA_END) {
+ if (tail > priv(dev)->tx_head)
+ return -1;
+ start = TX_AREA_START;
+ if (start + size > tail)
+ return -1;
+ priv(dev)->tx_head = start + size;
+ } else {
+ if (priv(dev)->tx_head < tail && (priv(dev)->tx_head + size) > tail)
+ return -1;
+ start = priv(dev)->tx_head;
+ priv(dev)->tx_head += size;
+ }
+
+ return start;
+}
+
+static int
+ether1_open (struct net_device *dev)
+{
+ if (!is_valid_ether_addr(dev->dev_addr)) {
+ printk(KERN_WARNING "%s: invalid ethernet MAC address\n",
+ dev->name);
+ return -EINVAL;
+ }
+
+ if (request_irq(dev->irq, ether1_interrupt, 0, "ether1", dev))
+ return -EAGAIN;
+
+ if (ether1_init_for_open (dev)) {
+ free_irq (dev->irq, dev);
+ return -EAGAIN;
+ }
+
+ netif_start_queue(dev);
+
+ return 0;
+}
+
+static void
+ether1_timeout(struct net_device *dev)
+{
+ printk(KERN_WARNING "%s: transmit timeout, network cable problem?\n",
+ dev->name);
+ printk(KERN_WARNING "%s: resetting device\n", dev->name);
+
+ ether1_reset (dev);
+
+ if (ether1_init_for_open (dev))
+ printk (KERN_ERR "%s: unable to restart interface\n", dev->name);
+
+ dev->stats.tx_errors++;
+ netif_wake_queue(dev);
+}
+
+static int
+ether1_sendpacket (struct sk_buff *skb, struct net_device *dev)
+{
+ int tmp, tst, nopaddr, txaddr, tbdaddr, dataddr;
+ unsigned long flags;
+ tx_t tx;
+ tbd_t tbd;
+ nop_t nop;
+
+ if (priv(dev)->restart) {
+ printk(KERN_WARNING "%s: resetting device\n", dev->name);
+
+ ether1_reset(dev);
+
+ if (ether1_init_for_open(dev))
+ printk(KERN_ERR "%s: unable to restart interface\n", dev->name);
+ else
+ priv(dev)->restart = 0;
+ }
+
+ if (skb->len < ETH_ZLEN) {
+ if (skb_padto(skb, ETH_ZLEN))
+ goto out;
+ }
+
+ /*
+ * insert packet followed by a nop
+ */
+ txaddr = ether1_txalloc (dev, TX_SIZE);
+ tbdaddr = ether1_txalloc (dev, TBD_SIZE);
+ dataddr = ether1_txalloc (dev, skb->len);
+ nopaddr = ether1_txalloc (dev, NOP_SIZE);
+
+ tx.tx_status = 0;
+ tx.tx_command = CMD_TX | CMD_INTR;
+ tx.tx_link = nopaddr;
+ tx.tx_tbdoffset = tbdaddr;
+ tbd.tbd_opts = TBD_EOL | skb->len;
+ tbd.tbd_link = I82586_NULL;
+ tbd.tbd_bufl = dataddr;
+ tbd.tbd_bufh = 0;
+ nop.nop_status = 0;
+ nop.nop_command = CMD_NOP;
+ nop.nop_link = nopaddr;
+
+ local_irq_save(flags);
+ ether1_writebuffer (dev, &tx, txaddr, TX_SIZE);
+ ether1_writebuffer (dev, &tbd, tbdaddr, TBD_SIZE);
+ ether1_writebuffer (dev, skb->data, dataddr, skb->len);
+ ether1_writebuffer (dev, &nop, nopaddr, NOP_SIZE);
+ tmp = priv(dev)->tx_link;
+ priv(dev)->tx_link = nopaddr;
+
+ /* now reset the previous nop pointer */
+ ether1_writew(dev, txaddr, tmp, nop_t, nop_link, NORMALIRQS);
+
+ local_irq_restore(flags);
+
+ /* handle transmit */
+
+ /* check to see if we have room for a full sized ether frame */
+ tmp = priv(dev)->tx_head;
+ tst = ether1_txalloc (dev, TX_SIZE + TBD_SIZE + NOP_SIZE + ETH_FRAME_LEN);
+ priv(dev)->tx_head = tmp;
+ dev_kfree_skb (skb);
+
+ if (tst == -1)
+ netif_stop_queue(dev);
+
+ out:
+ return NETDEV_TX_OK;
+}
+
+static void
+ether1_xmit_done (struct net_device *dev)
+{
+ nop_t nop;
+ int caddr, tst;
+
+ caddr = priv(dev)->tx_tail;
+
+again:
+ ether1_readbuffer (dev, &nop, caddr, NOP_SIZE);
+
+ switch (nop.nop_command & CMD_MASK) {
+ case CMD_TDR:
+ /* special case */
+ if (ether1_readw(dev, SCB_ADDR, scb_t, scb_cbl_offset, NORMALIRQS)
+ != (unsigned short)I82586_NULL) {
+ ether1_writew(dev, SCB_CMDCUCSTART | SCB_CMDRXSTART, SCB_ADDR, scb_t,
+ scb_command, NORMALIRQS);
+ writeb(CTRL_CA, REG_CONTROL);
+ }
+ priv(dev)->tx_tail = NOP_ADDR;
+ return;
+
+ case CMD_NOP:
+ if (nop.nop_link == caddr) {
+ if (priv(dev)->initialising == 0)
+ printk (KERN_WARNING "%s: strange command complete with no tx command!\n", dev->name);
+ else
+ priv(dev)->initialising = 0;
+ return;
+ }
+ if (caddr == nop.nop_link)
+ return;
+ caddr = nop.nop_link;
+ goto again;
+
+ case CMD_TX:
+ if (nop.nop_status & STAT_COMPLETE)
+ break;
+ printk (KERN_ERR "%s: strange command complete without completed command\n", dev->name);
+ priv(dev)->restart = 1;
+ return;
+
+ default:
+ printk (KERN_WARNING "%s: strange command %d complete! (offset %04X)", dev->name,
+ nop.nop_command & CMD_MASK, caddr);
+ priv(dev)->restart = 1;
+ return;
+ }
+
+ while (nop.nop_status & STAT_COMPLETE) {
+ if (nop.nop_status & STAT_OK) {
+ dev->stats.tx_packets++;
+ dev->stats.collisions += (nop.nop_status & STAT_COLLISIONS);
+ } else {
+ dev->stats.tx_errors++;
+
+ if (nop.nop_status & STAT_COLLAFTERTX)
+ dev->stats.collisions++;
+ if (nop.nop_status & STAT_NOCARRIER)
+ dev->stats.tx_carrier_errors++;
+ if (nop.nop_status & STAT_TXLOSTCTS)
+ printk (KERN_WARNING "%s: cts lost\n", dev->name);
+ if (nop.nop_status & STAT_TXSLOWDMA)
+ dev->stats.tx_fifo_errors++;
+ if (nop.nop_status & STAT_COLLEXCESSIVE)
+ dev->stats.collisions += 16;
+ }
+
+ if (nop.nop_link == caddr) {
+ printk (KERN_ERR "%s: tx buffer chaining error: tx command points to itself\n", dev->name);
+ break;
+ }
+
+ caddr = nop.nop_link;
+ ether1_readbuffer (dev, &nop, caddr, NOP_SIZE);
+ if ((nop.nop_command & CMD_MASK) != CMD_NOP) {
+ printk (KERN_ERR "%s: tx buffer chaining error: no nop after tx command\n", dev->name);
+ break;
+ }
+
+ if (caddr == nop.nop_link)
+ break;
+
+ caddr = nop.nop_link;
+ ether1_readbuffer (dev, &nop, caddr, NOP_SIZE);
+ if ((nop.nop_command & CMD_MASK) != CMD_TX) {
+ printk (KERN_ERR "%s: tx buffer chaining error: no tx command after nop\n", dev->name);
+ break;
+ }
+ }
+ priv(dev)->tx_tail = caddr;
+
+ caddr = priv(dev)->tx_head;
+ tst = ether1_txalloc (dev, TX_SIZE + TBD_SIZE + NOP_SIZE + ETH_FRAME_LEN);
+ priv(dev)->tx_head = caddr;
+ if (tst != -1)
+ netif_wake_queue(dev);
+}
+
+static void
+ether1_recv_done (struct net_device *dev)
+{
+ int status;
+ int nexttail, rbdaddr;
+ rbd_t rbd;
+
+ do {
+ status = ether1_readw(dev, priv(dev)->rx_head, rfd_t, rfd_status, NORMALIRQS);
+ if ((status & RFD_COMPLETE) == 0)
+ break;
+
+ rbdaddr = ether1_readw(dev, priv(dev)->rx_head, rfd_t, rfd_rbdoffset, NORMALIRQS);
+ ether1_readbuffer (dev, &rbd, rbdaddr, RBD_SIZE);
+
+ if ((rbd.rbd_status & (RBD_EOF | RBD_ACNTVALID)) == (RBD_EOF | RBD_ACNTVALID)) {
+ int length = rbd.rbd_status & RBD_ACNT;
+ struct sk_buff *skb;
+
+ length = (length + 1) & ~1;
+ skb = dev_alloc_skb (length + 2);
+
+ if (skb) {
+ skb_reserve (skb, 2);
+
+ ether1_readbuffer (dev, skb_put (skb, length), rbd.rbd_bufl, length);
+
+ skb->protocol = eth_type_trans (skb, dev);
+ netif_rx (skb);
+ dev->stats.rx_packets++;
+ } else
+ dev->stats.rx_dropped++;
+ } else {
+ printk(KERN_WARNING "%s: %s\n", dev->name,
+ (rbd.rbd_status & RBD_EOF) ? "oversized packet" : "acnt not valid");
+ dev->stats.rx_dropped++;
+ }
+
+ nexttail = ether1_readw(dev, priv(dev)->rx_tail, rfd_t, rfd_link, NORMALIRQS);
+ /* nexttail should be rx_head */
+ if (nexttail != priv(dev)->rx_head)
+ printk(KERN_ERR "%s: receiver buffer chaining error (%04X != %04X)\n",
+ dev->name, nexttail, priv(dev)->rx_head);
+ ether1_writew(dev, RFD_CMDEL | RFD_CMDSUSPEND, nexttail, rfd_t, rfd_command, NORMALIRQS);
+ ether1_writew(dev, 0, priv(dev)->rx_tail, rfd_t, rfd_command, NORMALIRQS);
+ ether1_writew(dev, 0, priv(dev)->rx_tail, rfd_t, rfd_status, NORMALIRQS);
+ ether1_writew(dev, 0, priv(dev)->rx_tail, rfd_t, rfd_rbdoffset, NORMALIRQS);
+
+ priv(dev)->rx_tail = nexttail;
+ priv(dev)->rx_head = ether1_readw(dev, priv(dev)->rx_head, rfd_t, rfd_link, NORMALIRQS);
+ } while (1);
+}
+
+static irqreturn_t
+ether1_interrupt (int irq, void *dev_id)
+{
+ struct net_device *dev = (struct net_device *)dev_id;
+ int status;
+
+ status = ether1_readw(dev, SCB_ADDR, scb_t, scb_status, NORMALIRQS);
+
+ if (status) {
+ ether1_writew(dev, status & (SCB_STRNR | SCB_STCNA | SCB_STFR | SCB_STCX),
+ SCB_ADDR, scb_t, scb_command, NORMALIRQS);
+ writeb(CTRL_CA | CTRL_ACK, REG_CONTROL);
+ if (status & SCB_STCX) {
+ ether1_xmit_done (dev);
+ }
+ if (status & SCB_STCNA) {
+ if (priv(dev)->resetting == 0)
+ printk (KERN_WARNING "%s: CU went not ready ???\n", dev->name);
+ else
+ priv(dev)->resetting += 1;
+ if (ether1_readw(dev, SCB_ADDR, scb_t, scb_cbl_offset, NORMALIRQS)
+ != (unsigned short)I82586_NULL) {
+ ether1_writew(dev, SCB_CMDCUCSTART, SCB_ADDR, scb_t, scb_command, NORMALIRQS);
+ writeb(CTRL_CA, REG_CONTROL);
+ }
+ if (priv(dev)->resetting == 2)
+ priv(dev)->resetting = 0;
+ }
+ if (status & SCB_STFR) {
+ ether1_recv_done (dev);
+ }
+ if (status & SCB_STRNR) {
+ if (ether1_readw(dev, SCB_ADDR, scb_t, scb_status, NORMALIRQS) & SCB_STRXSUSP) {
+ printk (KERN_WARNING "%s: RU went not ready: RU suspended\n", dev->name);
+ ether1_writew(dev, SCB_CMDRXRESUME, SCB_ADDR, scb_t, scb_command, NORMALIRQS);
+ writeb(CTRL_CA, REG_CONTROL);
+ dev->stats.rx_dropped++; /* we suspended due to lack of buffer space */
+ } else
+ printk(KERN_WARNING "%s: RU went not ready: %04X\n", dev->name,
+ ether1_readw(dev, SCB_ADDR, scb_t, scb_status, NORMALIRQS));
+ printk (KERN_WARNING "RU ptr = %04X\n", ether1_readw(dev, SCB_ADDR, scb_t, scb_rfa_offset,
+ NORMALIRQS));
+ }
+ } else
+ writeb(CTRL_ACK, REG_CONTROL);
+
+ return IRQ_HANDLED;
+}
+
+static int
+ether1_close (struct net_device *dev)
+{
+ ether1_reset (dev);
+
+ free_irq(dev->irq, dev);
+
+ return 0;
+}
+
+/*
+ * Set or clear the multicast filter for this adaptor.
+ * num_addrs == -1 Promiscuous mode, receive all packets.
+ * num_addrs == 0 Normal mode, clear multicast list.
+ * num_addrs > 0 Multicast mode, receive normal and MC packets, and do
+ * best-effort filtering.
+ */
+static void
+ether1_setmulticastlist (struct net_device *dev)
+{
+}
+
+/* ------------------------------------------------------------------------- */
+
+static void __devinit ether1_banner(void)
+{
+ static unsigned int version_printed = 0;
+
+ if (net_debug && version_printed++ == 0)
+ printk(KERN_INFO "%s", version);
+}
+
+static const struct net_device_ops ether1_netdev_ops = {
+ .ndo_open = ether1_open,
+ .ndo_stop = ether1_close,
+ .ndo_start_xmit = ether1_sendpacket,
+ .ndo_set_rx_mode = ether1_setmulticastlist,
+ .ndo_tx_timeout = ether1_timeout,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_change_mtu = eth_change_mtu,
+ .ndo_set_mac_address = eth_mac_addr,
+};
+
+static int __devinit
+ether1_probe(struct expansion_card *ec, const struct ecard_id *id)
+{
+ struct net_device *dev;
+ int i, ret = 0;
+
+ ether1_banner();
+
+ ret = ecard_request_resources(ec);
+ if (ret)
+ goto out;
+
+ dev = alloc_etherdev(sizeof(struct ether1_priv));
+ if (!dev) {
+ ret = -ENOMEM;
+ goto release;
+ }
+
+ SET_NETDEV_DEV(dev, &ec->dev);
+
+ dev->irq = ec->irq;
+ priv(dev)->base = ecardm_iomap(ec, ECARD_RES_IOCFAST, 0, 0);
+ if (!priv(dev)->base) {
+ ret = -ENOMEM;
+ goto free;
+ }
+
+ if ((priv(dev)->bus_type = ether1_reset(dev)) == 0) {
+ ret = -ENODEV;
+ goto free;
+ }
+
+ for (i = 0; i < 6; i++)
+ dev->dev_addr[i] = readb(IDPROM_ADDRESS + (i << 2));
+
+ if (ether1_init_2(dev)) {
+ ret = -ENODEV;
+ goto free;
+ }
+
+ dev->netdev_ops = &ether1_netdev_ops;
+ dev->watchdog_timeo = 5 * HZ / 100;
+
+ ret = register_netdev(dev);
+ if (ret)
+ goto free;
+
+ printk(KERN_INFO "%s: ether1 in slot %d, %pM\n",
+ dev->name, ec->slot_no, dev->dev_addr);
+
+ ecard_set_drvdata(ec, dev);
+ return 0;
+
+ free:
+ free_netdev(dev);
+ release:
+ ecard_release_resources(ec);
+ out:
+ return ret;
+}
+
+static void __devexit ether1_remove(struct expansion_card *ec)
+{
+ struct net_device *dev = ecard_get_drvdata(ec);
+
+ ecard_set_drvdata(ec, NULL);
+
+ unregister_netdev(dev);
+ free_netdev(dev);
+ ecard_release_resources(ec);
+}
+
+static const struct ecard_id ether1_ids[] = {
+ { MANU_ACORN, PROD_ACORN_ETHER1 },
+ { 0xffff, 0xffff }
+};
+
+static struct ecard_driver ether1_driver = {
+ .probe = ether1_probe,
+ .remove = __devexit_p(ether1_remove),
+ .id_table = ether1_ids,
+ .drv = {
+ .name = "ether1",
+ },
+};
+
+static int __init ether1_init(void)
+{
+ return ecard_register_driver(&ether1_driver);
+}
+
+static void __exit ether1_exit(void)
+{
+ ecard_remove_driver(&ether1_driver);
+}
+
+module_init(ether1_init);
+module_exit(ether1_exit);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/ethernet/i825xx/ether1.h b/drivers/net/ethernet/i825xx/ether1.h
new file mode 100644
index 00000000000..3a5830ab3dc
--- /dev/null
+++ b/drivers/net/ethernet/i825xx/ether1.h
@@ -0,0 +1,280 @@
+/*
+ * linux/drivers/acorn/net/ether1.h
+ *
+ * Copyright (C) 1996 Russell King
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Network driver for Acorn Ether1 cards.
+ */
+
+#ifndef _LINUX_ether1_H
+#define _LINUX_ether1_H
+
+#ifdef __ETHER1_C
+/* use 0 for production, 1 for verification, >2 for debug */
+#ifndef NET_DEBUG
+#define NET_DEBUG 0
+#endif
+
+#define priv(dev) ((struct ether1_priv *)netdev_priv(dev))
+
+/* Page register */
+#define REG_PAGE (priv(dev)->base + 0x0000)
+
+/* Control register */
+#define REG_CONTROL (priv(dev)->base + 0x0004)
+#define CTRL_RST 0x01
+#define CTRL_LOOPBACK 0x02
+#define CTRL_CA 0x04
+#define CTRL_ACK 0x08
+
+#define ETHER1_RAM (priv(dev)->base + 0x2000)
+
+/* HW address */
+#define IDPROM_ADDRESS (priv(dev)->base + 0x0024)
+
+struct ether1_priv {
+ void __iomem *base;
+ unsigned int tx_link;
+ unsigned int tx_head;
+ volatile unsigned int tx_tail;
+ volatile unsigned int rx_head;
+ volatile unsigned int rx_tail;
+ unsigned char bus_type;
+ unsigned char resetting;
+ unsigned char initialising : 1;
+ unsigned char restart : 1;
+};
+
+#define I82586_NULL (-1)
+
+typedef struct { /* tdr */
+ unsigned short tdr_status;
+ unsigned short tdr_command;
+ unsigned short tdr_link;
+ unsigned short tdr_result;
+#define TDR_TIME (0x7ff)
+#define TDR_SHORT (1 << 12)
+#define TDR_OPEN (1 << 13)
+#define TDR_XCVRPROB (1 << 14)
+#define TDR_LNKOK (1 << 15)
+} tdr_t;
+
+typedef struct { /* transmit */
+ unsigned short tx_status;
+ unsigned short tx_command;
+ unsigned short tx_link;
+ unsigned short tx_tbdoffset;
+} tx_t;
+
+typedef struct { /* tbd */
+ unsigned short tbd_opts;
+#define TBD_CNT (0x3fff)
+#define TBD_EOL (1 << 15)
+ unsigned short tbd_link;
+ unsigned short tbd_bufl;
+ unsigned short tbd_bufh;
+} tbd_t;
+
+typedef struct { /* rfd */
+ unsigned short rfd_status;
+#define RFD_NOEOF (1 << 6)
+#define RFD_FRAMESHORT (1 << 7)
+#define RFD_DMAOVRN (1 << 8)
+#define RFD_NORESOURCES (1 << 9)
+#define RFD_ALIGNERROR (1 << 10)
+#define RFD_CRCERROR (1 << 11)
+#define RFD_OK (1 << 13)
+#define RFD_FDCONSUMED (1 << 14)
+#define RFD_COMPLETE (1 << 15)
+ unsigned short rfd_command;
+#define RFD_CMDSUSPEND (1 << 14)
+#define RFD_CMDEL (1 << 15)
+ unsigned short rfd_link;
+ unsigned short rfd_rbdoffset;
+ unsigned char rfd_dest[6];
+ unsigned char rfd_src[6];
+ unsigned short rfd_len;
+} rfd_t;
+
+typedef struct { /* rbd */
+ unsigned short rbd_status;
+#define RBD_ACNT (0x3fff)
+#define RBD_ACNTVALID (1 << 14)
+#define RBD_EOF (1 << 15)
+ unsigned short rbd_link;
+ unsigned short rbd_bufl;
+ unsigned short rbd_bufh;
+ unsigned short rbd_len;
+} rbd_t;
+
+typedef struct { /* nop */
+ unsigned short nop_status;
+ unsigned short nop_command;
+ unsigned short nop_link;
+} nop_t;
+
+typedef struct { /* set multicast */
+ unsigned short mc_status;
+ unsigned short mc_command;
+ unsigned short mc_link;
+ unsigned short mc_cnt;
+ unsigned char mc_addrs[1][6];
+} mc_t;
+
+typedef struct { /* set address */
+ unsigned short sa_status;
+ unsigned short sa_command;
+ unsigned short sa_link;
+ unsigned char sa_addr[6];
+} sa_t;
+
+typedef struct { /* config command */
+ unsigned short cfg_status;
+ unsigned short cfg_command;
+ unsigned short cfg_link;
+ unsigned char cfg_bytecnt; /* size foll data: 4 - 12 */
+ unsigned char cfg_fifolim; /* FIFO threshold */
+ unsigned char cfg_byte8;
+#define CFG8_SRDY (1 << 6)
+#define CFG8_SAVEBADF (1 << 7)
+ unsigned char cfg_byte9;
+#define CFG9_ADDRLEN(x) (x)
+#define CFG9_ADDRLENBUF (1 << 3)
+#define CFG9_PREAMB2 (0 << 4)
+#define CFG9_PREAMB4 (1 << 4)
+#define CFG9_PREAMB8 (2 << 4)
+#define CFG9_PREAMB16 (3 << 4)
+#define CFG9_ILOOPBACK (1 << 6)
+#define CFG9_ELOOPBACK (1 << 7)
+ unsigned char cfg_byte10;
+#define CFG10_LINPRI(x) (x)
+#define CFG10_ACR(x) (x << 4)
+#define CFG10_BOFMET (1 << 7)
+ unsigned char cfg_ifs;
+ unsigned char cfg_slotl;
+ unsigned char cfg_byte13;
+#define CFG13_SLOTH(x) (x)
+#define CFG13_RETRY(x) (x << 4)
+ unsigned char cfg_byte14;
+#define CFG14_PROMISC (1 << 0)
+#define CFG14_DISBRD (1 << 1)
+#define CFG14_MANCH (1 << 2)
+#define CFG14_TNCRS (1 << 3)
+#define CFG14_NOCRC (1 << 4)
+#define CFG14_CRC16 (1 << 5)
+#define CFG14_BTSTF (1 << 6)
+#define CFG14_FLGPAD (1 << 7)
+ unsigned char cfg_byte15;
+#define CFG15_CSTF(x) (x)
+#define CFG15_ICSS (1 << 3)
+#define CFG15_CDTF(x) (x << 4)
+#define CFG15_ICDS (1 << 7)
+ unsigned short cfg_minfrmlen;
+} cfg_t;
+
+typedef struct { /* scb */
+ unsigned short scb_status; /* status of 82586 */
+#define SCB_STRXMASK (7 << 4) /* Receive unit status */
+#define SCB_STRXIDLE (0 << 4) /* Idle */
+#define SCB_STRXSUSP (1 << 4) /* Suspended */
+#define SCB_STRXNRES (2 << 4) /* No resources */
+#define SCB_STRXRDY (4 << 4) /* Ready */
+#define SCB_STCUMASK (7 << 8) /* Command unit status */
+#define SCB_STCUIDLE (0 << 8) /* Idle */
+#define SCB_STCUSUSP (1 << 8) /* Suspended */
+#define SCB_STCUACTV (2 << 8) /* Active */
+#define SCB_STRNR (1 << 12) /* Receive unit not ready */
+#define SCB_STCNA (1 << 13) /* Command unit not ready */
+#define SCB_STFR (1 << 14) /* Frame received */
+#define SCB_STCX (1 << 15) /* Command completed */
+ unsigned short scb_command; /* Next command */
+#define SCB_CMDRXSTART (1 << 4) /* Start (at rfa_offset) */
+#define SCB_CMDRXRESUME (2 << 4) /* Resume reception */
+#define SCB_CMDRXSUSPEND (3 << 4) /* Suspend reception */
+#define SCB_CMDRXABORT (4 << 4) /* Abort reception */
+#define SCB_CMDCUCSTART (1 << 8) /* Start (at cbl_offset) */
+#define SCB_CMDCUCRESUME (2 << 8) /* Resume execution */
+#define SCB_CMDCUCSUSPEND (3 << 8) /* Suspend execution */
+#define SCB_CMDCUCABORT (4 << 8) /* Abort execution */
+#define SCB_CMDACKRNR (1 << 12) /* Ack RU not ready */
+#define SCB_CMDACKCNA (1 << 13) /* Ack CU not ready */
+#define SCB_CMDACKFR (1 << 14) /* Ack Frame received */
+#define SCB_CMDACKCX (1 << 15) /* Ack Command complete */
+ unsigned short scb_cbl_offset; /* Offset of first command unit */
+ unsigned short scb_rfa_offset; /* Offset of first receive frame area */
+ unsigned short scb_crc_errors; /* Properly aligned frame with CRC error*/
+ unsigned short scb_aln_errors; /* Misaligned frames */
+ unsigned short scb_rsc_errors; /* Frames lost due to no space */
+ unsigned short scb_ovn_errors; /* Frames lost due to slow bus */
+} scb_t;
+
+typedef struct { /* iscp */
+ unsigned short iscp_busy; /* set by CPU before CA */
+ unsigned short iscp_offset; /* offset of SCB */
+ unsigned short iscp_basel; /* base of SCB */
+ unsigned short iscp_baseh;
+} iscp_t;
+
+ /* this address must be 0xfff6 */
+typedef struct { /* scp */
+ unsigned short scp_sysbus; /* bus size */
+#define SCP_SY_16BBUS 0x00
+#define SCP_SY_8BBUS 0x01
+ unsigned short scp_junk[2]; /* junk */
+ unsigned short scp_iscpl; /* lower 16 bits of iscp */
+ unsigned short scp_iscph; /* upper 16 bits of iscp */
+} scp_t;
+
+/* commands */
+#define CMD_NOP 0
+#define CMD_SETADDRESS 1
+#define CMD_CONFIG 2
+#define CMD_SETMULTICAST 3
+#define CMD_TX 4
+#define CMD_TDR 5
+#define CMD_DUMP 6
+#define CMD_DIAGNOSE 7
+
+#define CMD_MASK 7
+
+#define CMD_INTR (1 << 13)
+#define CMD_SUSP (1 << 14)
+#define CMD_EOL (1 << 15)
+
+#define STAT_COLLISIONS (15)
+#define STAT_COLLEXCESSIVE (1 << 5)
+#define STAT_COLLAFTERTX (1 << 6)
+#define STAT_TXDEFERRED (1 << 7)
+#define STAT_TXSLOWDMA (1 << 8)
+#define STAT_TXLOSTCTS (1 << 9)
+#define STAT_NOCARRIER (1 << 10)
+#define STAT_FAIL (1 << 11)
+#define STAT_ABORTED (1 << 12)
+#define STAT_OK (1 << 13)
+#define STAT_BUSY (1 << 14)
+#define STAT_COMPLETE (1 << 15)
+#endif
+#endif
+
+/*
+ * Ether1 card definitions:
+ *
+ * FAST accesses:
+ * +0 Page register
+ * 16 pages
+ * +4 Control
+ * '1' = reset
+ * '2' = loopback
+ * '4' = CA
+ * '8' = int ack
+ *
+ * RAM at address + 0x2000
+ * Pod. Prod id = 3
+ * Words after ID block [base + 8 words]
+ * +0 pcb issue (0x0c and 0xf3 invalid)
+ * +1 - +6 eth hw address
+ */
diff --git a/drivers/net/ethernet/i825xx/lasi_82596.c b/drivers/net/ethernet/i825xx/lasi_82596.c
new file mode 100644
index 00000000000..6eba352c52e
--- /dev/null
+++ b/drivers/net/ethernet/i825xx/lasi_82596.c
@@ -0,0 +1,238 @@
+/* lasi_82596.c -- driver for the intel 82596 ethernet controller, as
+ munged into HPPA boxen .
+
+ This driver is based upon 82596.c, original credits are below...
+ but there were too many hoops which HP wants jumped through to
+ keep this code in there in a sane manner.
+
+ 3 primary sources of the mess --
+ 1) hppa needs *lots* of cacheline flushing to keep this kind of
+ MMIO running.
+
+ 2) The 82596 needs to see all of its pointers as their physical
+ address. Thus virt_to_bus/bus_to_virt are *everywhere*.
+
+ 3) The implementation HP is using seems to be significantly pickier
+ about when and how the command and RX units are started. some
+ command ordering was changed.
+
+ Examination of the mach driver leads one to believe that there
+ might be a saner way to pull this off... anyone who feels like a
+ full rewrite can be my guest.
+
+ Split 02/13/2000 Sam Creasey (sammy@oh.verio.com)
+
+ 02/01/2000 Initial modifications for parisc by Helge Deller (deller@gmx.de)
+ 03/02/2000 changes for better/correct(?) cache-flushing (deller)
+*/
+
+/* 82596.c: A generic 82596 ethernet driver for linux. */
+/*
+ Based on Apricot.c
+ Written 1994 by Mark Evans.
+ This driver is for the Apricot 82596 bus-master interface
+
+ Modularised 12/94 Mark Evans
+
+
+ Modified to support the 82596 ethernet chips on 680x0 VME boards.
+ by Richard Hirst <richard@sleepie.demon.co.uk>
+ Renamed to be 82596.c
+
+ 980825: Changed to receive directly in to sk_buffs which are
+ allocated at open() time. Eliminates copy on incoming frames
+ (small ones are still copied). Shared data now held in a
+ non-cached page, so we can run on 68060 in copyback mode.
+
+ TBD:
+ * look at deferring rx frames rather than discarding (as per tulip)
+ * handle tx ring full as per tulip
+ * performance test to tune rx_copybreak
+
+ Most of my modifications relate to the braindead big-endian
+ implementation by Intel. When the i596 is operating in
+ 'big-endian' mode, it thinks a 32 bit value of 0x12345678
+ should be stored as 0x56781234. This is a real pain, when
+ you have linked lists which are shared by the 680x0 and the
+ i596.
+
+ Driver skeleton
+ Written 1993 by Donald Becker.
+ Copyright 1993 United States Government as represented by the Director,
+ National Security Agency. This software may only be used and distributed
+ according to the terms of the GNU General Public License as modified by SRC,
+ incorporated herein by reference.
+
+ The author may be reached as becker@scyld.com, or C/O
+ Scyld Computing Corporation, 410 Severn Ave., Suite 210, Annapolis MD 21403
+
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/bitops.h>
+#include <linux/dma-mapping.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/pdc.h>
+#include <asm/parisc-device.h>
+
+#define LASI_82596_DRIVER_VERSION "LASI 82596 driver - Revision: 1.30"
+
+#define PA_I82596_RESET 0 /* Offsets relative to LASI-LAN-Addr.*/
+#define PA_CPU_PORT_L_ACCESS 4
+#define PA_CHANNEL_ATTENTION 8
+
+#define OPT_SWAP_PORT 0x0001 /* Need to wordswp on the MPU port */
+
+#define DMA_ALLOC dma_alloc_noncoherent
+#define DMA_FREE dma_free_noncoherent
+#define DMA_WBACK(ndev, addr, len) \
+ do { dma_cache_sync((ndev)->dev.parent, (void *)addr, len, DMA_TO_DEVICE); } while (0)
+
+#define DMA_INV(ndev, addr, len) \
+ do { dma_cache_sync((ndev)->dev.parent, (void *)addr, len, DMA_FROM_DEVICE); } while (0)
+
+#define DMA_WBACK_INV(ndev, addr, len) \
+ do { dma_cache_sync((ndev)->dev.parent, (void *)addr, len, DMA_BIDIRECTIONAL); } while (0)
+
+#define SYSBUS 0x0000006c;
+
+/* big endian CPU, 82596 "big" endian mode */
+#define SWAP32(x) (((u32)(x)<<16) | ((((u32)(x)))>>16))
+#define SWAP16(x) (x)
+
+#include "lib82596.c"
+
+MODULE_AUTHOR("Richard Hirst");
+MODULE_DESCRIPTION("i82596 driver");
+MODULE_LICENSE("GPL");
+module_param(i596_debug, int, 0);
+MODULE_PARM_DESC(i596_debug, "lasi_82596 debug mask");
+
+static inline void ca(struct net_device *dev)
+{
+ gsc_writel(0, dev->base_addr + PA_CHANNEL_ATTENTION);
+}
+
+
+static void mpu_port(struct net_device *dev, int c, dma_addr_t x)
+{
+ struct i596_private *lp = netdev_priv(dev);
+
+ u32 v = (u32) (c) | (u32) (x);
+ u16 a, b;
+
+ if (lp->options & OPT_SWAP_PORT) {
+ a = v >> 16;
+ b = v & 0xffff;
+ } else {
+ a = v & 0xffff;
+ b = v >> 16;
+ }
+
+ gsc_writel(a, dev->base_addr + PA_CPU_PORT_L_ACCESS);
+ udelay(1);
+ gsc_writel(b, dev->base_addr + PA_CPU_PORT_L_ACCESS);
+}
+
+#define LAN_PROM_ADDR 0xF0810000
+
+static int __devinit
+lan_init_chip(struct parisc_device *dev)
+{
+ struct net_device *netdevice;
+ struct i596_private *lp;
+ int retval;
+ int i;
+
+ if (!dev->irq) {
+ printk(KERN_ERR "%s: IRQ not found for i82596 at 0x%lx\n",
+ __FILE__, (unsigned long)dev->hpa.start);
+ return -ENODEV;
+ }
+
+ printk(KERN_INFO "Found i82596 at 0x%lx, IRQ %d\n",
+ (unsigned long)dev->hpa.start, dev->irq);
+
+ netdevice = alloc_etherdev(sizeof(struct i596_private));
+ if (!netdevice)
+ return -ENOMEM;
+ SET_NETDEV_DEV(netdevice, &dev->dev);
+ parisc_set_drvdata (dev, netdevice);
+
+ netdevice->base_addr = dev->hpa.start;
+ netdevice->irq = dev->irq;
+
+ if (pdc_lan_station_id(netdevice->dev_addr, netdevice->base_addr)) {
+ for (i = 0; i < 6; i++) {
+ netdevice->dev_addr[i] = gsc_readb(LAN_PROM_ADDR + i);
+ }
+ printk(KERN_INFO
+ "%s: MAC of HP700 LAN read from EEPROM\n", __FILE__);
+ }
+
+ lp = netdev_priv(netdevice);
+ lp->options = dev->id.sversion == 0x72 ? OPT_SWAP_PORT : 0;
+
+ retval = i82596_probe(netdevice);
+ if (retval) {
+ free_netdev(netdevice);
+ return -ENODEV;
+ }
+ return retval;
+}
+
+static int __devexit lan_remove_chip (struct parisc_device *pdev)
+{
+ struct net_device *dev = parisc_get_drvdata(pdev);
+ struct i596_private *lp = netdev_priv(dev);
+
+ unregister_netdev (dev);
+ DMA_FREE(&pdev->dev, sizeof(struct i596_private),
+ (void *)lp->dma, lp->dma_addr);
+ free_netdev (dev);
+ return 0;
+}
+
+static struct parisc_device_id lan_tbl[] = {
+ { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0008a },
+ { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x00072 },
+ { 0, }
+};
+
+MODULE_DEVICE_TABLE(parisc, lan_tbl);
+
+static struct parisc_driver lan_driver = {
+ .name = "lasi_82596",
+ .id_table = lan_tbl,
+ .probe = lan_init_chip,
+ .remove = __devexit_p(lan_remove_chip),
+};
+
+static int __devinit lasi_82596_init(void)
+{
+ printk(KERN_INFO LASI_82596_DRIVER_VERSION "\n");
+ return register_parisc_driver(&lan_driver);
+}
+
+module_init(lasi_82596_init);
+
+static void __exit lasi_82596_exit(void)
+{
+ unregister_parisc_driver(&lan_driver);
+}
+
+module_exit(lasi_82596_exit);
diff --git a/drivers/net/ethernet/i825xx/lib82596.c b/drivers/net/ethernet/i825xx/lib82596.c
new file mode 100644
index 00000000000..3efbd8dbb63
--- /dev/null
+++ b/drivers/net/ethernet/i825xx/lib82596.c
@@ -0,0 +1,1412 @@
+/* lasi_82596.c -- driver for the intel 82596 ethernet controller, as
+ munged into HPPA boxen .
+
+ This driver is based upon 82596.c, original credits are below...
+ but there were too many hoops which HP wants jumped through to
+ keep this code in there in a sane manner.
+
+ 3 primary sources of the mess --
+ 1) hppa needs *lots* of cacheline flushing to keep this kind of
+ MMIO running.
+
+ 2) The 82596 needs to see all of its pointers as their physical
+ address. Thus virt_to_bus/bus_to_virt are *everywhere*.
+
+ 3) The implementation HP is using seems to be significantly pickier
+ about when and how the command and RX units are started. some
+ command ordering was changed.
+
+ Examination of the mach driver leads one to believe that there
+ might be a saner way to pull this off... anyone who feels like a
+ full rewrite can be my guest.
+
+ Split 02/13/2000 Sam Creasey (sammy@oh.verio.com)
+
+ 02/01/2000 Initial modifications for parisc by Helge Deller (deller@gmx.de)
+ 03/02/2000 changes for better/correct(?) cache-flushing (deller)
+*/
+
+/* 82596.c: A generic 82596 ethernet driver for linux. */
+/*
+ Based on Apricot.c
+ Written 1994 by Mark Evans.
+ This driver is for the Apricot 82596 bus-master interface
+
+ Modularised 12/94 Mark Evans
+
+
+ Modified to support the 82596 ethernet chips on 680x0 VME boards.
+ by Richard Hirst <richard@sleepie.demon.co.uk>
+ Renamed to be 82596.c
+
+ 980825: Changed to receive directly in to sk_buffs which are
+ allocated at open() time. Eliminates copy on incoming frames
+ (small ones are still copied). Shared data now held in a
+ non-cached page, so we can run on 68060 in copyback mode.
+
+ TBD:
+ * look at deferring rx frames rather than discarding (as per tulip)
+ * handle tx ring full as per tulip
+ * performance test to tune rx_copybreak
+
+ Most of my modifications relate to the braindead big-endian
+ implementation by Intel. When the i596 is operating in
+ 'big-endian' mode, it thinks a 32 bit value of 0x12345678
+ should be stored as 0x56781234. This is a real pain, when
+ you have linked lists which are shared by the 680x0 and the
+ i596.
+
+ Driver skeleton
+ Written 1993 by Donald Becker.
+ Copyright 1993 United States Government as represented by the Director,
+ National Security Agency. This software may only be used and distributed
+ according to the terms of the GNU General Public License as modified by SRC,
+ incorporated herein by reference.
+
+ The author may be reached as becker@scyld.com, or C/O
+ Scyld Computing Corporation, 410 Severn Ave., Suite 210, Annapolis MD 21403
+
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/bitops.h>
+#include <linux/dma-mapping.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/gfp.h>
+
+/* DEBUG flags
+ */
+
+#define DEB_INIT 0x0001
+#define DEB_PROBE 0x0002
+#define DEB_SERIOUS 0x0004
+#define DEB_ERRORS 0x0008
+#define DEB_MULTI 0x0010
+#define DEB_TDR 0x0020
+#define DEB_OPEN 0x0040
+#define DEB_RESET 0x0080
+#define DEB_ADDCMD 0x0100
+#define DEB_STATUS 0x0200
+#define DEB_STARTTX 0x0400
+#define DEB_RXADDR 0x0800
+#define DEB_TXADDR 0x1000
+#define DEB_RXFRAME 0x2000
+#define DEB_INTS 0x4000
+#define DEB_STRUCT 0x8000
+#define DEB_ANY 0xffff
+
+
+#define DEB(x, y) if (i596_debug & (x)) { y; }
+
+
+/*
+ * The MPU_PORT command allows direct access to the 82596. With PORT access
+ * the following commands are available (p5-18). The 32-bit port command
+ * must be word-swapped with the most significant word written first.
+ * This only applies to VME boards.
+ */
+#define PORT_RESET 0x00 /* reset 82596 */
+#define PORT_SELFTEST 0x01 /* selftest */
+#define PORT_ALTSCP 0x02 /* alternate SCB address */
+#define PORT_ALTDUMP 0x03 /* Alternate DUMP address */
+
+static int i596_debug = (DEB_SERIOUS|DEB_PROBE);
+
+/* Copy frames shorter than rx_copybreak, otherwise pass on up in
+ * a full sized sk_buff. Value of 100 stolen from tulip.c (!alpha).
+ */
+static int rx_copybreak = 100;
+
+#define PKT_BUF_SZ 1536
+#define MAX_MC_CNT 64
+
+#define ISCP_BUSY 0x0001
+
+#define I596_NULL ((u32)0xffffffff)
+
+#define CMD_EOL 0x8000 /* The last command of the list, stop. */
+#define CMD_SUSP 0x4000 /* Suspend after doing cmd. */
+#define CMD_INTR 0x2000 /* Interrupt after doing cmd. */
+
+#define CMD_FLEX 0x0008 /* Enable flexible memory model */
+
+enum commands {
+ CmdNOp = 0, CmdSASetup = 1, CmdConfigure = 2, CmdMulticastList = 3,
+ CmdTx = 4, CmdTDR = 5, CmdDump = 6, CmdDiagnose = 7
+};
+
+#define STAT_C 0x8000 /* Set to 0 after execution */
+#define STAT_B 0x4000 /* Command being executed */
+#define STAT_OK 0x2000 /* Command executed ok */
+#define STAT_A 0x1000 /* Command aborted */
+
+#define CUC_START 0x0100
+#define CUC_RESUME 0x0200
+#define CUC_SUSPEND 0x0300
+#define CUC_ABORT 0x0400
+#define RX_START 0x0010
+#define RX_RESUME 0x0020
+#define RX_SUSPEND 0x0030
+#define RX_ABORT 0x0040
+
+#define TX_TIMEOUT (HZ/20)
+
+
+struct i596_reg {
+ unsigned short porthi;
+ unsigned short portlo;
+ u32 ca;
+};
+
+#define EOF 0x8000
+#define SIZE_MASK 0x3fff
+
+struct i596_tbd {
+ unsigned short size;
+ unsigned short pad;
+ u32 next;
+ u32 data;
+ u32 cache_pad[5]; /* Total 32 bytes... */
+};
+
+/* The command structure has two 'next' pointers; v_next is the address of
+ * the next command as seen by the CPU, b_next is the address of the next
+ * command as seen by the 82596. The b_next pointer, as used by the 82596
+ * always references the status field of the next command, rather than the
+ * v_next field, because the 82596 is unaware of v_next. It may seem more
+ * logical to put v_next at the end of the structure, but we cannot do that
+ * because the 82596 expects other fields to be there, depending on command
+ * type.
+ */
+
+struct i596_cmd {
+ struct i596_cmd *v_next; /* Address from CPUs viewpoint */
+ unsigned short status;
+ unsigned short command;
+ u32 b_next; /* Address from i596 viewpoint */
+};
+
+struct tx_cmd {
+ struct i596_cmd cmd;
+ u32 tbd;
+ unsigned short size;
+ unsigned short pad;
+ struct sk_buff *skb; /* So we can free it after tx */
+ dma_addr_t dma_addr;
+#ifdef __LP64__
+ u32 cache_pad[6]; /* Total 64 bytes... */
+#else
+ u32 cache_pad[1]; /* Total 32 bytes... */
+#endif
+};
+
+struct tdr_cmd {
+ struct i596_cmd cmd;
+ unsigned short status;
+ unsigned short pad;
+};
+
+struct mc_cmd {
+ struct i596_cmd cmd;
+ short mc_cnt;
+ char mc_addrs[MAX_MC_CNT*6];
+};
+
+struct sa_cmd {
+ struct i596_cmd cmd;
+ char eth_addr[8];
+};
+
+struct cf_cmd {
+ struct i596_cmd cmd;
+ char i596_config[16];
+};
+
+struct i596_rfd {
+ unsigned short stat;
+ unsigned short cmd;
+ u32 b_next; /* Address from i596 viewpoint */
+ u32 rbd;
+ unsigned short count;
+ unsigned short size;
+ struct i596_rfd *v_next; /* Address from CPUs viewpoint */
+ struct i596_rfd *v_prev;
+#ifndef __LP64__
+ u32 cache_pad[2]; /* Total 32 bytes... */
+#endif
+};
+
+struct i596_rbd {
+ /* hardware data */
+ unsigned short count;
+ unsigned short zero1;
+ u32 b_next;
+ u32 b_data; /* Address from i596 viewpoint */
+ unsigned short size;
+ unsigned short zero2;
+ /* driver data */
+ struct sk_buff *skb;
+ struct i596_rbd *v_next;
+ u32 b_addr; /* This rbd addr from i596 view */
+ unsigned char *v_data; /* Address from CPUs viewpoint */
+ /* Total 32 bytes... */
+#ifdef __LP64__
+ u32 cache_pad[4];
+#endif
+};
+
+/* These values as chosen so struct i596_dma fits in one page... */
+
+#define TX_RING_SIZE 32
+#define RX_RING_SIZE 16
+
+struct i596_scb {
+ unsigned short status;
+ unsigned short command;
+ u32 cmd;
+ u32 rfd;
+ u32 crc_err;
+ u32 align_err;
+ u32 resource_err;
+ u32 over_err;
+ u32 rcvdt_err;
+ u32 short_err;
+ unsigned short t_on;
+ unsigned short t_off;
+};
+
+struct i596_iscp {
+ u32 stat;
+ u32 scb;
+};
+
+struct i596_scp {
+ u32 sysbus;
+ u32 pad;
+ u32 iscp;
+};
+
+struct i596_dma {
+ struct i596_scp scp __attribute__((aligned(32)));
+ volatile struct i596_iscp iscp __attribute__((aligned(32)));
+ volatile struct i596_scb scb __attribute__((aligned(32)));
+ struct sa_cmd sa_cmd __attribute__((aligned(32)));
+ struct cf_cmd cf_cmd __attribute__((aligned(32)));
+ struct tdr_cmd tdr_cmd __attribute__((aligned(32)));
+ struct mc_cmd mc_cmd __attribute__((aligned(32)));
+ struct i596_rfd rfds[RX_RING_SIZE] __attribute__((aligned(32)));
+ struct i596_rbd rbds[RX_RING_SIZE] __attribute__((aligned(32)));
+ struct tx_cmd tx_cmds[TX_RING_SIZE] __attribute__((aligned(32)));
+ struct i596_tbd tbds[TX_RING_SIZE] __attribute__((aligned(32)));
+};
+
+struct i596_private {
+ struct i596_dma *dma;
+ u32 stat;
+ int last_restart;
+ struct i596_rfd *rfd_head;
+ struct i596_rbd *rbd_head;
+ struct i596_cmd *cmd_tail;
+ struct i596_cmd *cmd_head;
+ int cmd_backlog;
+ u32 last_cmd;
+ int next_tx_cmd;
+ int options;
+ spinlock_t lock; /* serialize access to chip */
+ dma_addr_t dma_addr;
+ void __iomem *mpu_port;
+ void __iomem *ca;
+};
+
+static const char init_setup[] =
+{
+ 0x8E, /* length, prefetch on */
+ 0xC8, /* fifo to 8, monitor off */
+ 0x80, /* don't save bad frames */
+ 0x2E, /* No source address insertion, 8 byte preamble */
+ 0x00, /* priority and backoff defaults */
+ 0x60, /* interframe spacing */
+ 0x00, /* slot time LSB */
+ 0xf2, /* slot time and retries */
+ 0x00, /* promiscuous mode */
+ 0x00, /* collision detect */
+ 0x40, /* minimum frame length */
+ 0xff,
+ 0x00,
+ 0x7f /* *multi IA */ };
+
+static int i596_open(struct net_device *dev);
+static int i596_start_xmit(struct sk_buff *skb, struct net_device *dev);
+static irqreturn_t i596_interrupt(int irq, void *dev_id);
+static int i596_close(struct net_device *dev);
+static void i596_add_cmd(struct net_device *dev, struct i596_cmd *cmd);
+static void i596_tx_timeout (struct net_device *dev);
+static void print_eth(unsigned char *buf, char *str);
+static void set_multicast_list(struct net_device *dev);
+static inline void ca(struct net_device *dev);
+static void mpu_port(struct net_device *dev, int c, dma_addr_t x);
+
+static int rx_ring_size = RX_RING_SIZE;
+static int ticks_limit = 100;
+static int max_cmd_backlog = TX_RING_SIZE-1;
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void i596_poll_controller(struct net_device *dev);
+#endif
+
+
+static inline int wait_istat(struct net_device *dev, struct i596_dma *dma, int delcnt, char *str)
+{
+ DMA_INV(dev, &(dma->iscp), sizeof(struct i596_iscp));
+ while (--delcnt && dma->iscp.stat) {
+ udelay(10);
+ DMA_INV(dev, &(dma->iscp), sizeof(struct i596_iscp));
+ }
+ if (!delcnt) {
+ printk(KERN_ERR "%s: %s, iscp.stat %04x, didn't clear\n",
+ dev->name, str, SWAP16(dma->iscp.stat));
+ return -1;
+ } else
+ return 0;
+}
+
+
+static inline int wait_cmd(struct net_device *dev, struct i596_dma *dma, int delcnt, char *str)
+{
+ DMA_INV(dev, &(dma->scb), sizeof(struct i596_scb));
+ while (--delcnt && dma->scb.command) {
+ udelay(10);
+ DMA_INV(dev, &(dma->scb), sizeof(struct i596_scb));
+ }
+ if (!delcnt) {
+ printk(KERN_ERR "%s: %s, status %4.4x, cmd %4.4x.\n",
+ dev->name, str,
+ SWAP16(dma->scb.status),
+ SWAP16(dma->scb.command));
+ return -1;
+ } else
+ return 0;
+}
+
+
+static void i596_display_data(struct net_device *dev)
+{
+ struct i596_private *lp = netdev_priv(dev);
+ struct i596_dma *dma = lp->dma;
+ struct i596_cmd *cmd;
+ struct i596_rfd *rfd;
+ struct i596_rbd *rbd;
+
+ printk(KERN_DEBUG "lp and scp at %p, .sysbus = %08x, .iscp = %08x\n",
+ &dma->scp, dma->scp.sysbus, SWAP32(dma->scp.iscp));
+ printk(KERN_DEBUG "iscp at %p, iscp.stat = %08x, .scb = %08x\n",
+ &dma->iscp, SWAP32(dma->iscp.stat), SWAP32(dma->iscp.scb));
+ printk(KERN_DEBUG "scb at %p, scb.status = %04x, .command = %04x,"
+ " .cmd = %08x, .rfd = %08x\n",
+ &dma->scb, SWAP16(dma->scb.status), SWAP16(dma->scb.command),
+ SWAP16(dma->scb.cmd), SWAP32(dma->scb.rfd));
+ printk(KERN_DEBUG " errors: crc %x, align %x, resource %x,"
+ " over %x, rcvdt %x, short %x\n",
+ SWAP32(dma->scb.crc_err), SWAP32(dma->scb.align_err),
+ SWAP32(dma->scb.resource_err), SWAP32(dma->scb.over_err),
+ SWAP32(dma->scb.rcvdt_err), SWAP32(dma->scb.short_err));
+ cmd = lp->cmd_head;
+ while (cmd != NULL) {
+ printk(KERN_DEBUG
+ "cmd at %p, .status = %04x, .command = %04x,"
+ " .b_next = %08x\n",
+ cmd, SWAP16(cmd->status), SWAP16(cmd->command),
+ SWAP32(cmd->b_next));
+ cmd = cmd->v_next;
+ }
+ rfd = lp->rfd_head;
+ printk(KERN_DEBUG "rfd_head = %p\n", rfd);
+ do {
+ printk(KERN_DEBUG
+ " %p .stat %04x, .cmd %04x, b_next %08x, rbd %08x,"
+ " count %04x\n",
+ rfd, SWAP16(rfd->stat), SWAP16(rfd->cmd),
+ SWAP32(rfd->b_next), SWAP32(rfd->rbd),
+ SWAP16(rfd->count));
+ rfd = rfd->v_next;
+ } while (rfd != lp->rfd_head);
+ rbd = lp->rbd_head;
+ printk(KERN_DEBUG "rbd_head = %p\n", rbd);
+ do {
+ printk(KERN_DEBUG
+ " %p .count %04x, b_next %08x, b_data %08x,"
+ " size %04x\n",
+ rbd, SWAP16(rbd->count), SWAP32(rbd->b_next),
+ SWAP32(rbd->b_data), SWAP16(rbd->size));
+ rbd = rbd->v_next;
+ } while (rbd != lp->rbd_head);
+ DMA_INV(dev, dma, sizeof(struct i596_dma));
+}
+
+
+#define virt_to_dma(lp, v) ((lp)->dma_addr + (dma_addr_t)((unsigned long)(v)-(unsigned long)((lp)->dma)))
+
+static inline int init_rx_bufs(struct net_device *dev)
+{
+ struct i596_private *lp = netdev_priv(dev);
+ struct i596_dma *dma = lp->dma;
+ int i;
+ struct i596_rfd *rfd;
+ struct i596_rbd *rbd;
+
+ /* First build the Receive Buffer Descriptor List */
+
+ for (i = 0, rbd = dma->rbds; i < rx_ring_size; i++, rbd++) {
+ dma_addr_t dma_addr;
+ struct sk_buff *skb;
+
+ skb = netdev_alloc_skb_ip_align(dev, PKT_BUF_SZ);
+ if (skb == NULL)
+ return -1;
+ dma_addr = dma_map_single(dev->dev.parent, skb->data,
+ PKT_BUF_SZ, DMA_FROM_DEVICE);
+ rbd->v_next = rbd+1;
+ rbd->b_next = SWAP32(virt_to_dma(lp, rbd+1));
+ rbd->b_addr = SWAP32(virt_to_dma(lp, rbd));
+ rbd->skb = skb;
+ rbd->v_data = skb->data;
+ rbd->b_data = SWAP32(dma_addr);
+ rbd->size = SWAP16(PKT_BUF_SZ);
+ }
+ lp->rbd_head = dma->rbds;
+ rbd = dma->rbds + rx_ring_size - 1;
+ rbd->v_next = dma->rbds;
+ rbd->b_next = SWAP32(virt_to_dma(lp, dma->rbds));
+
+ /* Now build the Receive Frame Descriptor List */
+
+ for (i = 0, rfd = dma->rfds; i < rx_ring_size; i++, rfd++) {
+ rfd->rbd = I596_NULL;
+ rfd->v_next = rfd+1;
+ rfd->v_prev = rfd-1;
+ rfd->b_next = SWAP32(virt_to_dma(lp, rfd+1));
+ rfd->cmd = SWAP16(CMD_FLEX);
+ }
+ lp->rfd_head = dma->rfds;
+ dma->scb.rfd = SWAP32(virt_to_dma(lp, dma->rfds));
+ rfd = dma->rfds;
+ rfd->rbd = SWAP32(virt_to_dma(lp, lp->rbd_head));
+ rfd->v_prev = dma->rfds + rx_ring_size - 1;
+ rfd = dma->rfds + rx_ring_size - 1;
+ rfd->v_next = dma->rfds;
+ rfd->b_next = SWAP32(virt_to_dma(lp, dma->rfds));
+ rfd->cmd = SWAP16(CMD_EOL|CMD_FLEX);
+
+ DMA_WBACK_INV(dev, dma, sizeof(struct i596_dma));
+ return 0;
+}
+
+static inline void remove_rx_bufs(struct net_device *dev)
+{
+ struct i596_private *lp = netdev_priv(dev);
+ struct i596_rbd *rbd;
+ int i;
+
+ for (i = 0, rbd = lp->dma->rbds; i < rx_ring_size; i++, rbd++) {
+ if (rbd->skb == NULL)
+ break;
+ dma_unmap_single(dev->dev.parent,
+ (dma_addr_t)SWAP32(rbd->b_data),
+ PKT_BUF_SZ, DMA_FROM_DEVICE);
+ dev_kfree_skb(rbd->skb);
+ }
+}
+
+
+static void rebuild_rx_bufs(struct net_device *dev)
+{
+ struct i596_private *lp = netdev_priv(dev);
+ struct i596_dma *dma = lp->dma;
+ int i;
+
+ /* Ensure rx frame/buffer descriptors are tidy */
+
+ for (i = 0; i < rx_ring_size; i++) {
+ dma->rfds[i].rbd = I596_NULL;
+ dma->rfds[i].cmd = SWAP16(CMD_FLEX);
+ }
+ dma->rfds[rx_ring_size-1].cmd = SWAP16(CMD_EOL|CMD_FLEX);
+ lp->rfd_head = dma->rfds;
+ dma->scb.rfd = SWAP32(virt_to_dma(lp, dma->rfds));
+ lp->rbd_head = dma->rbds;
+ dma->rfds[0].rbd = SWAP32(virt_to_dma(lp, dma->rbds));
+
+ DMA_WBACK_INV(dev, dma, sizeof(struct i596_dma));
+}
+
+
+static int init_i596_mem(struct net_device *dev)
+{
+ struct i596_private *lp = netdev_priv(dev);
+ struct i596_dma *dma = lp->dma;
+ unsigned long flags;
+
+ mpu_port(dev, PORT_RESET, 0);
+ udelay(100); /* Wait 100us - seems to help */
+
+ /* change the scp address */
+
+ lp->last_cmd = jiffies;
+
+ dma->scp.sysbus = SYSBUS;
+ dma->scp.iscp = SWAP32(virt_to_dma(lp, &(dma->iscp)));
+ dma->iscp.scb = SWAP32(virt_to_dma(lp, &(dma->scb)));
+ dma->iscp.stat = SWAP32(ISCP_BUSY);
+ lp->cmd_backlog = 0;
+
+ lp->cmd_head = NULL;
+ dma->scb.cmd = I596_NULL;
+
+ DEB(DEB_INIT, printk(KERN_DEBUG "%s: starting i82596.\n", dev->name));
+
+ DMA_WBACK(dev, &(dma->scp), sizeof(struct i596_scp));
+ DMA_WBACK(dev, &(dma->iscp), sizeof(struct i596_iscp));
+ DMA_WBACK(dev, &(dma->scb), sizeof(struct i596_scb));
+
+ mpu_port(dev, PORT_ALTSCP, virt_to_dma(lp, &dma->scp));
+ ca(dev);
+ if (wait_istat(dev, dma, 1000, "initialization timed out"))
+ goto failed;
+ DEB(DEB_INIT, printk(KERN_DEBUG
+ "%s: i82596 initialization successful\n",
+ dev->name));
+
+ if (request_irq(dev->irq, i596_interrupt, 0, "i82596", dev)) {
+ printk(KERN_ERR "%s: IRQ %d not free\n", dev->name, dev->irq);
+ goto failed;
+ }
+
+ /* Ensure rx frame/buffer descriptors are tidy */
+ rebuild_rx_bufs(dev);
+
+ dma->scb.command = 0;
+ DMA_WBACK(dev, &(dma->scb), sizeof(struct i596_scb));
+
+ DEB(DEB_INIT, printk(KERN_DEBUG
+ "%s: queuing CmdConfigure\n", dev->name));
+ memcpy(dma->cf_cmd.i596_config, init_setup, 14);
+ dma->cf_cmd.cmd.command = SWAP16(CmdConfigure);
+ DMA_WBACK(dev, &(dma->cf_cmd), sizeof(struct cf_cmd));
+ i596_add_cmd(dev, &dma->cf_cmd.cmd);
+
+ DEB(DEB_INIT, printk(KERN_DEBUG "%s: queuing CmdSASetup\n", dev->name));
+ memcpy(dma->sa_cmd.eth_addr, dev->dev_addr, 6);
+ dma->sa_cmd.cmd.command = SWAP16(CmdSASetup);
+ DMA_WBACK(dev, &(dma->sa_cmd), sizeof(struct sa_cmd));
+ i596_add_cmd(dev, &dma->sa_cmd.cmd);
+
+ DEB(DEB_INIT, printk(KERN_DEBUG "%s: queuing CmdTDR\n", dev->name));
+ dma->tdr_cmd.cmd.command = SWAP16(CmdTDR);
+ DMA_WBACK(dev, &(dma->tdr_cmd), sizeof(struct tdr_cmd));
+ i596_add_cmd(dev, &dma->tdr_cmd.cmd);
+
+ spin_lock_irqsave (&lp->lock, flags);
+
+ if (wait_cmd(dev, dma, 1000, "timed out waiting to issue RX_START")) {
+ spin_unlock_irqrestore (&lp->lock, flags);
+ goto failed_free_irq;
+ }
+ DEB(DEB_INIT, printk(KERN_DEBUG "%s: Issuing RX_START\n", dev->name));
+ dma->scb.command = SWAP16(RX_START);
+ dma->scb.rfd = SWAP32(virt_to_dma(lp, dma->rfds));
+ DMA_WBACK(dev, &(dma->scb), sizeof(struct i596_scb));
+
+ ca(dev);
+
+ spin_unlock_irqrestore (&lp->lock, flags);
+ if (wait_cmd(dev, dma, 1000, "RX_START not processed"))
+ goto failed_free_irq;
+ DEB(DEB_INIT, printk(KERN_DEBUG
+ "%s: Receive unit started OK\n", dev->name));
+ return 0;
+
+failed_free_irq:
+ free_irq(dev->irq, dev);
+failed:
+ printk(KERN_ERR "%s: Failed to initialise 82596\n", dev->name);
+ mpu_port(dev, PORT_RESET, 0);
+ return -1;
+}
+
+
+static inline int i596_rx(struct net_device *dev)
+{
+ struct i596_private *lp = netdev_priv(dev);
+ struct i596_rfd *rfd;
+ struct i596_rbd *rbd;
+ int frames = 0;
+
+ DEB(DEB_RXFRAME, printk(KERN_DEBUG
+ "i596_rx(), rfd_head %p, rbd_head %p\n",
+ lp->rfd_head, lp->rbd_head));
+
+
+ rfd = lp->rfd_head; /* Ref next frame to check */
+
+ DMA_INV(dev, rfd, sizeof(struct i596_rfd));
+ while (rfd->stat & SWAP16(STAT_C)) { /* Loop while complete frames */
+ if (rfd->rbd == I596_NULL)
+ rbd = NULL;
+ else if (rfd->rbd == lp->rbd_head->b_addr) {
+ rbd = lp->rbd_head;
+ DMA_INV(dev, rbd, sizeof(struct i596_rbd));
+ } else {
+ printk(KERN_ERR "%s: rbd chain broken!\n", dev->name);
+ /* XXX Now what? */
+ rbd = NULL;
+ }
+ DEB(DEB_RXFRAME, printk(KERN_DEBUG
+ " rfd %p, rfd.rbd %08x, rfd.stat %04x\n",
+ rfd, rfd->rbd, rfd->stat));
+
+ if (rbd != NULL && (rfd->stat & SWAP16(STAT_OK))) {
+ /* a good frame */
+ int pkt_len = SWAP16(rbd->count) & 0x3fff;
+ struct sk_buff *skb = rbd->skb;
+ int rx_in_place = 0;
+
+ DEB(DEB_RXADDR, print_eth(rbd->v_data, "received"));
+ frames++;
+
+ /* Check if the packet is long enough to just accept
+ * without copying to a properly sized skbuff.
+ */
+
+ if (pkt_len > rx_copybreak) {
+ struct sk_buff *newskb;
+ dma_addr_t dma_addr;
+
+ dma_unmap_single(dev->dev.parent,
+ (dma_addr_t)SWAP32(rbd->b_data),
+ PKT_BUF_SZ, DMA_FROM_DEVICE);
+ /* Get fresh skbuff to replace filled one. */
+ newskb = netdev_alloc_skb_ip_align(dev,
+ PKT_BUF_SZ);
+ if (newskb == NULL) {
+ skb = NULL; /* drop pkt */
+ goto memory_squeeze;
+ }
+
+ /* Pass up the skb already on the Rx ring. */
+ skb_put(skb, pkt_len);
+ rx_in_place = 1;
+ rbd->skb = newskb;
+ dma_addr = dma_map_single(dev->dev.parent,
+ newskb->data,
+ PKT_BUF_SZ,
+ DMA_FROM_DEVICE);
+ rbd->v_data = newskb->data;
+ rbd->b_data = SWAP32(dma_addr);
+ DMA_WBACK_INV(dev, rbd, sizeof(struct i596_rbd));
+ } else
+ skb = netdev_alloc_skb_ip_align(dev, pkt_len);
+memory_squeeze:
+ if (skb == NULL) {
+ /* XXX tulip.c can defer packets here!! */
+ printk(KERN_ERR
+ "%s: i596_rx Memory squeeze, dropping packet.\n",
+ dev->name);
+ dev->stats.rx_dropped++;
+ } else {
+ if (!rx_in_place) {
+ /* 16 byte align the data fields */
+ dma_sync_single_for_cpu(dev->dev.parent,
+ (dma_addr_t)SWAP32(rbd->b_data),
+ PKT_BUF_SZ, DMA_FROM_DEVICE);
+ memcpy(skb_put(skb, pkt_len), rbd->v_data, pkt_len);
+ dma_sync_single_for_device(dev->dev.parent,
+ (dma_addr_t)SWAP32(rbd->b_data),
+ PKT_BUF_SZ, DMA_FROM_DEVICE);
+ }
+ skb->len = pkt_len;
+ skb->protocol = eth_type_trans(skb, dev);
+ netif_rx(skb);
+ dev->stats.rx_packets++;
+ dev->stats.rx_bytes += pkt_len;
+ }
+ } else {
+ DEB(DEB_ERRORS, printk(KERN_DEBUG
+ "%s: Error, rfd.stat = 0x%04x\n",
+ dev->name, rfd->stat));
+ dev->stats.rx_errors++;
+ if (rfd->stat & SWAP16(0x0100))
+ dev->stats.collisions++;
+ if (rfd->stat & SWAP16(0x8000))
+ dev->stats.rx_length_errors++;
+ if (rfd->stat & SWAP16(0x0001))
+ dev->stats.rx_over_errors++;
+ if (rfd->stat & SWAP16(0x0002))
+ dev->stats.rx_fifo_errors++;
+ if (rfd->stat & SWAP16(0x0004))
+ dev->stats.rx_frame_errors++;
+ if (rfd->stat & SWAP16(0x0008))
+ dev->stats.rx_crc_errors++;
+ if (rfd->stat & SWAP16(0x0010))
+ dev->stats.rx_length_errors++;
+ }
+
+ /* Clear the buffer descriptor count and EOF + F flags */
+
+ if (rbd != NULL && (rbd->count & SWAP16(0x4000))) {
+ rbd->count = 0;
+ lp->rbd_head = rbd->v_next;
+ DMA_WBACK_INV(dev, rbd, sizeof(struct i596_rbd));
+ }
+
+ /* Tidy the frame descriptor, marking it as end of list */
+
+ rfd->rbd = I596_NULL;
+ rfd->stat = 0;
+ rfd->cmd = SWAP16(CMD_EOL|CMD_FLEX);
+ rfd->count = 0;
+
+ /* Update record of next frame descriptor to process */
+
+ lp->dma->scb.rfd = rfd->b_next;
+ lp->rfd_head = rfd->v_next;
+ DMA_WBACK_INV(dev, rfd, sizeof(struct i596_rfd));
+
+ /* Remove end-of-list from old end descriptor */
+
+ rfd->v_prev->cmd = SWAP16(CMD_FLEX);
+ DMA_WBACK_INV(dev, rfd->v_prev, sizeof(struct i596_rfd));
+ rfd = lp->rfd_head;
+ DMA_INV(dev, rfd, sizeof(struct i596_rfd));
+ }
+
+ DEB(DEB_RXFRAME, printk(KERN_DEBUG "frames %d\n", frames));
+
+ return 0;
+}
+
+
+static inline void i596_cleanup_cmd(struct net_device *dev, struct i596_private *lp)
+{
+ struct i596_cmd *ptr;
+
+ while (lp->cmd_head != NULL) {
+ ptr = lp->cmd_head;
+ lp->cmd_head = ptr->v_next;
+ lp->cmd_backlog--;
+
+ switch (SWAP16(ptr->command) & 0x7) {
+ case CmdTx:
+ {
+ struct tx_cmd *tx_cmd = (struct tx_cmd *) ptr;
+ struct sk_buff *skb = tx_cmd->skb;
+ dma_unmap_single(dev->dev.parent,
+ tx_cmd->dma_addr,
+ skb->len, DMA_TO_DEVICE);
+
+ dev_kfree_skb(skb);
+
+ dev->stats.tx_errors++;
+ dev->stats.tx_aborted_errors++;
+
+ ptr->v_next = NULL;
+ ptr->b_next = I596_NULL;
+ tx_cmd->cmd.command = 0; /* Mark as free */
+ break;
+ }
+ default:
+ ptr->v_next = NULL;
+ ptr->b_next = I596_NULL;
+ }
+ DMA_WBACK_INV(dev, ptr, sizeof(struct i596_cmd));
+ }
+
+ wait_cmd(dev, lp->dma, 100, "i596_cleanup_cmd timed out");
+ lp->dma->scb.cmd = I596_NULL;
+ DMA_WBACK(dev, &(lp->dma->scb), sizeof(struct i596_scb));
+}
+
+
+static inline void i596_reset(struct net_device *dev, struct i596_private *lp)
+{
+ unsigned long flags;
+
+ DEB(DEB_RESET, printk(KERN_DEBUG "i596_reset\n"));
+
+ spin_lock_irqsave (&lp->lock, flags);
+
+ wait_cmd(dev, lp->dma, 100, "i596_reset timed out");
+
+ netif_stop_queue(dev);
+
+ /* FIXME: this command might cause an lpmc */
+ lp->dma->scb.command = SWAP16(CUC_ABORT | RX_ABORT);
+ DMA_WBACK(dev, &(lp->dma->scb), sizeof(struct i596_scb));
+ ca(dev);
+
+ /* wait for shutdown */
+ wait_cmd(dev, lp->dma, 1000, "i596_reset 2 timed out");
+ spin_unlock_irqrestore (&lp->lock, flags);
+
+ i596_cleanup_cmd(dev, lp);
+ i596_rx(dev);
+
+ netif_start_queue(dev);
+ init_i596_mem(dev);
+}
+
+
+static void i596_add_cmd(struct net_device *dev, struct i596_cmd *cmd)
+{
+ struct i596_private *lp = netdev_priv(dev);
+ struct i596_dma *dma = lp->dma;
+ unsigned long flags;
+
+ DEB(DEB_ADDCMD, printk(KERN_DEBUG "i596_add_cmd cmd_head %p\n",
+ lp->cmd_head));
+
+ cmd->status = 0;
+ cmd->command |= SWAP16(CMD_EOL | CMD_INTR);
+ cmd->v_next = NULL;
+ cmd->b_next = I596_NULL;
+ DMA_WBACK(dev, cmd, sizeof(struct i596_cmd));
+
+ spin_lock_irqsave (&lp->lock, flags);
+
+ if (lp->cmd_head != NULL) {
+ lp->cmd_tail->v_next = cmd;
+ lp->cmd_tail->b_next = SWAP32(virt_to_dma(lp, &cmd->status));
+ DMA_WBACK(dev, lp->cmd_tail, sizeof(struct i596_cmd));
+ } else {
+ lp->cmd_head = cmd;
+ wait_cmd(dev, dma, 100, "i596_add_cmd timed out");
+ dma->scb.cmd = SWAP32(virt_to_dma(lp, &cmd->status));
+ dma->scb.command = SWAP16(CUC_START);
+ DMA_WBACK(dev, &(dma->scb), sizeof(struct i596_scb));
+ ca(dev);
+ }
+ lp->cmd_tail = cmd;
+ lp->cmd_backlog++;
+
+ spin_unlock_irqrestore (&lp->lock, flags);
+
+ if (lp->cmd_backlog > max_cmd_backlog) {
+ unsigned long tickssofar = jiffies - lp->last_cmd;
+
+ if (tickssofar < ticks_limit)
+ return;
+
+ printk(KERN_ERR
+ "%s: command unit timed out, status resetting.\n",
+ dev->name);
+#if 1
+ i596_reset(dev, lp);
+#endif
+ }
+}
+
+static int i596_open(struct net_device *dev)
+{
+ DEB(DEB_OPEN, printk(KERN_DEBUG
+ "%s: i596_open() irq %d.\n", dev->name, dev->irq));
+
+ if (init_rx_bufs(dev)) {
+ printk(KERN_ERR "%s: Failed to init rx bufs\n", dev->name);
+ return -EAGAIN;
+ }
+ if (init_i596_mem(dev)) {
+ printk(KERN_ERR "%s: Failed to init memory\n", dev->name);
+ goto out_remove_rx_bufs;
+ }
+ netif_start_queue(dev);
+
+ return 0;
+
+out_remove_rx_bufs:
+ remove_rx_bufs(dev);
+ return -EAGAIN;
+}
+
+static void i596_tx_timeout (struct net_device *dev)
+{
+ struct i596_private *lp = netdev_priv(dev);
+
+ /* Transmitter timeout, serious problems. */
+ DEB(DEB_ERRORS, printk(KERN_DEBUG
+ "%s: transmit timed out, status resetting.\n",
+ dev->name));
+
+ dev->stats.tx_errors++;
+
+ /* Try to restart the adaptor */
+ if (lp->last_restart == dev->stats.tx_packets) {
+ DEB(DEB_ERRORS, printk(KERN_DEBUG "Resetting board.\n"));
+ /* Shutdown and restart */
+ i596_reset (dev, lp);
+ } else {
+ /* Issue a channel attention signal */
+ DEB(DEB_ERRORS, printk(KERN_DEBUG "Kicking board.\n"));
+ lp->dma->scb.command = SWAP16(CUC_START | RX_START);
+ DMA_WBACK_INV(dev, &(lp->dma->scb), sizeof(struct i596_scb));
+ ca (dev);
+ lp->last_restart = dev->stats.tx_packets;
+ }
+
+ dev->trans_start = jiffies; /* prevent tx timeout */
+ netif_wake_queue (dev);
+}
+
+
+static int i596_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct i596_private *lp = netdev_priv(dev);
+ struct tx_cmd *tx_cmd;
+ struct i596_tbd *tbd;
+ short length = skb->len;
+
+ DEB(DEB_STARTTX, printk(KERN_DEBUG
+ "%s: i596_start_xmit(%x,%p) called\n",
+ dev->name, skb->len, skb->data));
+
+ if (length < ETH_ZLEN) {
+ if (skb_padto(skb, ETH_ZLEN))
+ return NETDEV_TX_OK;
+ length = ETH_ZLEN;
+ }
+
+ netif_stop_queue(dev);
+
+ tx_cmd = lp->dma->tx_cmds + lp->next_tx_cmd;
+ tbd = lp->dma->tbds + lp->next_tx_cmd;
+
+ if (tx_cmd->cmd.command) {
+ DEB(DEB_ERRORS, printk(KERN_DEBUG
+ "%s: xmit ring full, dropping packet.\n",
+ dev->name));
+ dev->stats.tx_dropped++;
+
+ dev_kfree_skb(skb);
+ } else {
+ if (++lp->next_tx_cmd == TX_RING_SIZE)
+ lp->next_tx_cmd = 0;
+ tx_cmd->tbd = SWAP32(virt_to_dma(lp, tbd));
+ tbd->next = I596_NULL;
+
+ tx_cmd->cmd.command = SWAP16(CMD_FLEX | CmdTx);
+ tx_cmd->skb = skb;
+
+ tx_cmd->pad = 0;
+ tx_cmd->size = 0;
+ tbd->pad = 0;
+ tbd->size = SWAP16(EOF | length);
+
+ tx_cmd->dma_addr = dma_map_single(dev->dev.parent, skb->data,
+ skb->len, DMA_TO_DEVICE);
+ tbd->data = SWAP32(tx_cmd->dma_addr);
+
+ DEB(DEB_TXADDR, print_eth(skb->data, "tx-queued"));
+ DMA_WBACK_INV(dev, tx_cmd, sizeof(struct tx_cmd));
+ DMA_WBACK_INV(dev, tbd, sizeof(struct i596_tbd));
+ i596_add_cmd(dev, &tx_cmd->cmd);
+
+ dev->stats.tx_packets++;
+ dev->stats.tx_bytes += length;
+ }
+
+ netif_start_queue(dev);
+
+ return NETDEV_TX_OK;
+}
+
+static void print_eth(unsigned char *add, char *str)
+{
+ printk(KERN_DEBUG "i596 0x%p, %pM --> %pM %02X%02X, %s\n",
+ add, add + 6, add, add[12], add[13], str);
+}
+static const struct net_device_ops i596_netdev_ops = {
+ .ndo_open = i596_open,
+ .ndo_stop = i596_close,
+ .ndo_start_xmit = i596_start_xmit,
+ .ndo_set_rx_mode = set_multicast_list,
+ .ndo_tx_timeout = i596_tx_timeout,
+ .ndo_change_mtu = eth_change_mtu,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_mac_address = eth_mac_addr,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = i596_poll_controller,
+#endif
+};
+
+static int __devinit i82596_probe(struct net_device *dev)
+{
+ int i;
+ struct i596_private *lp = netdev_priv(dev);
+ struct i596_dma *dma;
+
+ /* This lot is ensure things have been cache line aligned. */
+ BUILD_BUG_ON(sizeof(struct i596_rfd) != 32);
+ BUILD_BUG_ON(sizeof(struct i596_rbd) & 31);
+ BUILD_BUG_ON(sizeof(struct tx_cmd) & 31);
+ BUILD_BUG_ON(sizeof(struct i596_tbd) != 32);
+#ifndef __LP64__
+ BUILD_BUG_ON(sizeof(struct i596_dma) > 4096);
+#endif
+
+ if (!dev->base_addr || !dev->irq)
+ return -ENODEV;
+
+ dma = (struct i596_dma *) DMA_ALLOC(dev->dev.parent,
+ sizeof(struct i596_dma), &lp->dma_addr, GFP_KERNEL);
+ if (!dma) {
+ printk(KERN_ERR "%s: Couldn't get shared memory\n", __FILE__);
+ return -ENOMEM;
+ }
+
+ dev->netdev_ops = &i596_netdev_ops;
+ dev->watchdog_timeo = TX_TIMEOUT;
+
+ memset(dma, 0, sizeof(struct i596_dma));
+ lp->dma = dma;
+
+ dma->scb.command = 0;
+ dma->scb.cmd = I596_NULL;
+ dma->scb.rfd = I596_NULL;
+ spin_lock_init(&lp->lock);
+
+ DMA_WBACK_INV(dev, dma, sizeof(struct i596_dma));
+
+ i = register_netdev(dev);
+ if (i) {
+ DMA_FREE(dev->dev.parent, sizeof(struct i596_dma),
+ (void *)dma, lp->dma_addr);
+ return i;
+ }
+
+ DEB(DEB_PROBE, printk(KERN_INFO "%s: 82596 at %#3lx, %pM IRQ %d.\n",
+ dev->name, dev->base_addr, dev->dev_addr,
+ dev->irq));
+ DEB(DEB_INIT, printk(KERN_INFO
+ "%s: dma at 0x%p (%d bytes), lp->scb at 0x%p\n",
+ dev->name, dma, (int)sizeof(struct i596_dma),
+ &dma->scb));
+
+ return 0;
+}
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void i596_poll_controller(struct net_device *dev)
+{
+ disable_irq(dev->irq);
+ i596_interrupt(dev->irq, dev);
+ enable_irq(dev->irq);
+}
+#endif
+
+static irqreturn_t i596_interrupt(int irq, void *dev_id)
+{
+ struct net_device *dev = dev_id;
+ struct i596_private *lp;
+ struct i596_dma *dma;
+ unsigned short status, ack_cmd = 0;
+
+ lp = netdev_priv(dev);
+ dma = lp->dma;
+
+ spin_lock (&lp->lock);
+
+ wait_cmd(dev, dma, 100, "i596 interrupt, timeout");
+ status = SWAP16(dma->scb.status);
+
+ DEB(DEB_INTS, printk(KERN_DEBUG
+ "%s: i596 interrupt, IRQ %d, status %4.4x.\n",
+ dev->name, dev->irq, status));
+
+ ack_cmd = status & 0xf000;
+
+ if (!ack_cmd) {
+ DEB(DEB_ERRORS, printk(KERN_DEBUG
+ "%s: interrupt with no events\n",
+ dev->name));
+ spin_unlock (&lp->lock);
+ return IRQ_NONE;
+ }
+
+ if ((status & 0x8000) || (status & 0x2000)) {
+ struct i596_cmd *ptr;
+
+ if ((status & 0x8000))
+ DEB(DEB_INTS,
+ printk(KERN_DEBUG
+ "%s: i596 interrupt completed command.\n",
+ dev->name));
+ if ((status & 0x2000))
+ DEB(DEB_INTS,
+ printk(KERN_DEBUG
+ "%s: i596 interrupt command unit inactive %x.\n",
+ dev->name, status & 0x0700));
+
+ while (lp->cmd_head != NULL) {
+ DMA_INV(dev, lp->cmd_head, sizeof(struct i596_cmd));
+ if (!(lp->cmd_head->status & SWAP16(STAT_C)))
+ break;
+
+ ptr = lp->cmd_head;
+
+ DEB(DEB_STATUS,
+ printk(KERN_DEBUG
+ "cmd_head->status = %04x, ->command = %04x\n",
+ SWAP16(lp->cmd_head->status),
+ SWAP16(lp->cmd_head->command)));
+ lp->cmd_head = ptr->v_next;
+ lp->cmd_backlog--;
+
+ switch (SWAP16(ptr->command) & 0x7) {
+ case CmdTx:
+ {
+ struct tx_cmd *tx_cmd = (struct tx_cmd *) ptr;
+ struct sk_buff *skb = tx_cmd->skb;
+
+ if (ptr->status & SWAP16(STAT_OK)) {
+ DEB(DEB_TXADDR,
+ print_eth(skb->data, "tx-done"));
+ } else {
+ dev->stats.tx_errors++;
+ if (ptr->status & SWAP16(0x0020))
+ dev->stats.collisions++;
+ if (!(ptr->status & SWAP16(0x0040)))
+ dev->stats.tx_heartbeat_errors++;
+ if (ptr->status & SWAP16(0x0400))
+ dev->stats.tx_carrier_errors++;
+ if (ptr->status & SWAP16(0x0800))
+ dev->stats.collisions++;
+ if (ptr->status & SWAP16(0x1000))
+ dev->stats.tx_aborted_errors++;
+ }
+ dma_unmap_single(dev->dev.parent,
+ tx_cmd->dma_addr,
+ skb->len, DMA_TO_DEVICE);
+ dev_kfree_skb_irq(skb);
+
+ tx_cmd->cmd.command = 0; /* Mark free */
+ break;
+ }
+ case CmdTDR:
+ {
+ unsigned short status = SWAP16(((struct tdr_cmd *)ptr)->status);
+
+ if (status & 0x8000) {
+ DEB(DEB_ANY,
+ printk(KERN_DEBUG "%s: link ok.\n",
+ dev->name));
+ } else {
+ if (status & 0x4000)
+ printk(KERN_ERR
+ "%s: Transceiver problem.\n",
+ dev->name);
+ if (status & 0x2000)
+ printk(KERN_ERR
+ "%s: Termination problem.\n",
+ dev->name);
+ if (status & 0x1000)
+ printk(KERN_ERR
+ "%s: Short circuit.\n",
+ dev->name);
+
+ DEB(DEB_TDR,
+ printk(KERN_DEBUG "%s: Time %d.\n",
+ dev->name, status & 0x07ff));
+ }
+ break;
+ }
+ case CmdConfigure:
+ /*
+ * Zap command so set_multicast_list() know
+ * it is free
+ */
+ ptr->command = 0;
+ break;
+ }
+ ptr->v_next = NULL;
+ ptr->b_next = I596_NULL;
+ DMA_WBACK(dev, ptr, sizeof(struct i596_cmd));
+ lp->last_cmd = jiffies;
+ }
+
+ /* This mess is arranging that only the last of any outstanding
+ * commands has the interrupt bit set. Should probably really
+ * only add to the cmd queue when the CU is stopped.
+ */
+ ptr = lp->cmd_head;
+ while ((ptr != NULL) && (ptr != lp->cmd_tail)) {
+ struct i596_cmd *prev = ptr;
+
+ ptr->command &= SWAP16(0x1fff);
+ ptr = ptr->v_next;
+ DMA_WBACK_INV(dev, prev, sizeof(struct i596_cmd));
+ }
+
+ if (lp->cmd_head != NULL)
+ ack_cmd |= CUC_START;
+ dma->scb.cmd = SWAP32(virt_to_dma(lp, &lp->cmd_head->status));
+ DMA_WBACK_INV(dev, &dma->scb, sizeof(struct i596_scb));
+ }
+ if ((status & 0x1000) || (status & 0x4000)) {
+ if ((status & 0x4000))
+ DEB(DEB_INTS,
+ printk(KERN_DEBUG
+ "%s: i596 interrupt received a frame.\n",
+ dev->name));
+ i596_rx(dev);
+ /* Only RX_START if stopped - RGH 07-07-96 */
+ if (status & 0x1000) {
+ if (netif_running(dev)) {
+ DEB(DEB_ERRORS,
+ printk(KERN_DEBUG
+ "%s: i596 interrupt receive unit inactive, status 0x%x\n",
+ dev->name, status));
+ ack_cmd |= RX_START;
+ dev->stats.rx_errors++;
+ dev->stats.rx_fifo_errors++;
+ rebuild_rx_bufs(dev);
+ }
+ }
+ }
+ wait_cmd(dev, dma, 100, "i596 interrupt, timeout");
+ dma->scb.command = SWAP16(ack_cmd);
+ DMA_WBACK(dev, &dma->scb, sizeof(struct i596_scb));
+
+ /* DANGER: I suspect that some kind of interrupt
+ acknowledgement aside from acking the 82596 might be needed
+ here... but it's running acceptably without */
+
+ ca(dev);
+
+ wait_cmd(dev, dma, 100, "i596 interrupt, exit timeout");
+ DEB(DEB_INTS, printk(KERN_DEBUG "%s: exiting interrupt.\n", dev->name));
+
+ spin_unlock (&lp->lock);
+ return IRQ_HANDLED;
+}
+
+static int i596_close(struct net_device *dev)
+{
+ struct i596_private *lp = netdev_priv(dev);
+ unsigned long flags;
+
+ netif_stop_queue(dev);
+
+ DEB(DEB_INIT,
+ printk(KERN_DEBUG
+ "%s: Shutting down ethercard, status was %4.4x.\n",
+ dev->name, SWAP16(lp->dma->scb.status)));
+
+ spin_lock_irqsave(&lp->lock, flags);
+
+ wait_cmd(dev, lp->dma, 100, "close1 timed out");
+ lp->dma->scb.command = SWAP16(CUC_ABORT | RX_ABORT);
+ DMA_WBACK(dev, &lp->dma->scb, sizeof(struct i596_scb));
+
+ ca(dev);
+
+ wait_cmd(dev, lp->dma, 100, "close2 timed out");
+ spin_unlock_irqrestore(&lp->lock, flags);
+ DEB(DEB_STRUCT, i596_display_data(dev));
+ i596_cleanup_cmd(dev, lp);
+
+ free_irq(dev->irq, dev);
+ remove_rx_bufs(dev);
+
+ return 0;
+}
+
+/*
+ * Set or clear the multicast filter for this adaptor.
+ */
+
+static void set_multicast_list(struct net_device *dev)
+{
+ struct i596_private *lp = netdev_priv(dev);
+ struct i596_dma *dma = lp->dma;
+ int config = 0, cnt;
+
+ DEB(DEB_MULTI,
+ printk(KERN_DEBUG
+ "%s: set multicast list, %d entries, promisc %s, allmulti %s\n",
+ dev->name, netdev_mc_count(dev),
+ dev->flags & IFF_PROMISC ? "ON" : "OFF",
+ dev->flags & IFF_ALLMULTI ? "ON" : "OFF"));
+
+ if ((dev->flags & IFF_PROMISC) &&
+ !(dma->cf_cmd.i596_config[8] & 0x01)) {
+ dma->cf_cmd.i596_config[8] |= 0x01;
+ config = 1;
+ }
+ if (!(dev->flags & IFF_PROMISC) &&
+ (dma->cf_cmd.i596_config[8] & 0x01)) {
+ dma->cf_cmd.i596_config[8] &= ~0x01;
+ config = 1;
+ }
+ if ((dev->flags & IFF_ALLMULTI) &&
+ (dma->cf_cmd.i596_config[11] & 0x20)) {
+ dma->cf_cmd.i596_config[11] &= ~0x20;
+ config = 1;
+ }
+ if (!(dev->flags & IFF_ALLMULTI) &&
+ !(dma->cf_cmd.i596_config[11] & 0x20)) {
+ dma->cf_cmd.i596_config[11] |= 0x20;
+ config = 1;
+ }
+ if (config) {
+ if (dma->cf_cmd.cmd.command)
+ printk(KERN_INFO
+ "%s: config change request already queued\n",
+ dev->name);
+ else {
+ dma->cf_cmd.cmd.command = SWAP16(CmdConfigure);
+ DMA_WBACK_INV(dev, &dma->cf_cmd, sizeof(struct cf_cmd));
+ i596_add_cmd(dev, &dma->cf_cmd.cmd);
+ }
+ }
+
+ cnt = netdev_mc_count(dev);
+ if (cnt > MAX_MC_CNT) {
+ cnt = MAX_MC_CNT;
+ printk(KERN_NOTICE "%s: Only %d multicast addresses supported",
+ dev->name, cnt);
+ }
+
+ if (!netdev_mc_empty(dev)) {
+ struct netdev_hw_addr *ha;
+ unsigned char *cp;
+ struct mc_cmd *cmd;
+
+ cmd = &dma->mc_cmd;
+ cmd->cmd.command = SWAP16(CmdMulticastList);
+ cmd->mc_cnt = SWAP16(netdev_mc_count(dev) * 6);
+ cp = cmd->mc_addrs;
+ netdev_for_each_mc_addr(ha, dev) {
+ if (!cnt--)
+ break;
+ memcpy(cp, ha->addr, 6);
+ if (i596_debug > 1)
+ DEB(DEB_MULTI,
+ printk(KERN_DEBUG
+ "%s: Adding address %pM\n",
+ dev->name, cp));
+ cp += 6;
+ }
+ DMA_WBACK_INV(dev, &dma->mc_cmd, sizeof(struct mc_cmd));
+ i596_add_cmd(dev, &cmd->cmd);
+ }
+}
diff --git a/drivers/net/ethernet/i825xx/lp486e.c b/drivers/net/ethernet/i825xx/lp486e.c
new file mode 100644
index 00000000000..414044b3cb1
--- /dev/null
+++ b/drivers/net/ethernet/i825xx/lp486e.c
@@ -0,0 +1,1339 @@
+/* Intel Professional Workstation/panther ethernet driver */
+/* lp486e.c: A panther 82596 ethernet driver for linux. */
+/*
+ History and copyrights:
+
+ Driver skeleton
+ Written 1993 by Donald Becker.
+ Copyright 1993 United States Government as represented by the Director,
+ National Security Agency. This software may only be used and
+ distributed according to the terms of the GNU General Public License
+ as modified by SRC, incorporated herein by reference.
+
+ The author may be reached as becker@scyld.com, or C/O
+ Scyld Computing Corporation
+ 410 Severn Ave., Suite 210
+ Annapolis MD 21403
+
+ Apricot
+ Written 1994 by Mark Evans.
+ This driver is for the Apricot 82596 bus-master interface
+
+ Modularised 12/94 Mark Evans
+
+ Professional Workstation
+ Derived from apricot.c by Ard van Breemen
+ <ard@murphy.nl>|<ard@cstmel.hobby.nl>|<ard@cstmel.nl.eu.org>
+
+ Credits:
+ Thanks to Murphy Software BV for letting me write this in their time.
+ Well, actually, I get paid doing this...
+ (Also: see http://www.murphy.nl for murphy, and my homepage ~ard for
+ more information on the Professional Workstation)
+
+ Present version
+ aeb@cwi.nl
+*/
+/*
+ There are currently two motherboards that I know of in the
+ professional workstation. The only one that I know is the
+ intel panther motherboard. -- ard
+*/
+/*
+The pws is equipped with an intel 82596. This is a very intelligent controller
+which runs its own micro-code. Communication with the hostprocessor is done
+through linked lists of commands and buffers in the hostprocessors memory.
+A complete description of the 82596 is available from intel. Search for
+a file called "29021806.pdf". It is a complete description of the chip itself.
+To use it for the pws some additions are needed regarding generation of
+the PORT and CA signal, and the interrupt glue needed for a pc.
+I/O map:
+PORT SIZE ACTION MEANING
+0xCB0 2 WRITE Lower 16 bits for PORT command
+0xCB2 2 WRITE Upper 16 bits for PORT command, and issue of PORT command
+0xCB4 1 WRITE Generation of CA signal
+0xCB8 1 WRITE Clear interrupt glue
+All other communication is through memory!
+*/
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/bitops.h>
+
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#define DRV_NAME "lp486e"
+
+/* debug print flags */
+#define LOG_SRCDST 0x80000000
+#define LOG_STATINT 0x40000000
+#define LOG_STARTINT 0x20000000
+
+#define i596_debug debug
+
+static int i596_debug = 0;
+
+static const char * const medianame[] = {
+ "10baseT", "AUI",
+ "10baseT-FD", "AUI-FD",
+};
+
+#define LP486E_TOTAL_SIZE 16
+
+#define I596_NULL (0xffffffff)
+
+#define CMD_EOL 0x8000 /* The last command of the list, stop. */
+#define CMD_SUSP 0x4000 /* Suspend after doing cmd. */
+#define CMD_INTR 0x2000 /* Interrupt after doing cmd. */
+
+#define CMD_FLEX 0x0008 /* Enable flexible memory model */
+
+enum commands {
+ CmdNOP = 0,
+ CmdIASetup = 1,
+ CmdConfigure = 2,
+ CmdMulticastList = 3,
+ CmdTx = 4,
+ CmdTDR = 5,
+ CmdDump = 6,
+ CmdDiagnose = 7
+};
+
+#if 0
+static const char *CUcmdnames[8] = { "NOP", "IASetup", "Configure", "MulticastList",
+ "Tx", "TDR", "Dump", "Diagnose" };
+#endif
+
+/* Status word bits */
+#define STAT_CX 0x8000 /* The CU finished executing a command
+ with the Interrupt bit set */
+#define STAT_FR 0x4000 /* The RU finished receiving a frame */
+#define STAT_CNA 0x2000 /* The CU left the active state */
+#define STAT_RNR 0x1000 /* The RU left the active state */
+#define STAT_ACK (STAT_CX | STAT_FR | STAT_CNA | STAT_RNR)
+#define STAT_CUS 0x0700 /* Status of CU: 0: idle, 1: suspended,
+ 2: active, 3-7: unused */
+#define STAT_RUS 0x00f0 /* Status of RU: 0: idle, 1: suspended,
+ 2: no resources, 4: ready,
+ 10: no resources due to no more RBDs,
+ 12: no more RBDs, other: unused */
+#define STAT_T 0x0008 /* Bus throttle timers loaded */
+#define STAT_ZERO 0x0807 /* Always zero */
+
+#if 0
+static char *CUstates[8] = {
+ "idle", "suspended", "active", 0, 0, 0, 0, 0
+};
+static char *RUstates[16] = {
+ "idle", "suspended", "no resources", 0, "ready", 0, 0, 0,
+ 0, 0, "no RBDs", 0, "out of RBDs", 0, 0, 0
+};
+
+static void
+i596_out_status(int status) {
+ int bad = 0;
+ char *s;
+
+ printk("status %4.4x:", status);
+ if (status == 0xffff)
+ printk(" strange..\n");
+ else {
+ if (status & STAT_CX)
+ printk(" CU done");
+ if (status & STAT_CNA)
+ printk(" CU stopped");
+ if (status & STAT_FR)
+ printk(" got a frame");
+ if (status & STAT_RNR)
+ printk(" RU stopped");
+ if (status & STAT_T)
+ printk(" throttled");
+ if (status & STAT_ZERO)
+ bad = 1;
+ s = CUstates[(status & STAT_CUS) >> 8];
+ if (!s)
+ bad = 1;
+ else
+ printk(" CU(%s)", s);
+ s = RUstates[(status & STAT_RUS) >> 4];
+ if (!s)
+ bad = 1;
+ else
+ printk(" RU(%s)", s);
+ if (bad)
+ printk(" bad status");
+ printk("\n");
+ }
+}
+#endif
+
+/* Command word bits */
+#define ACK_CX 0x8000
+#define ACK_FR 0x4000
+#define ACK_CNA 0x2000
+#define ACK_RNR 0x1000
+
+#define CUC_START 0x0100
+#define CUC_RESUME 0x0200
+#define CUC_SUSPEND 0x0300
+#define CUC_ABORT 0x0400
+
+#define RX_START 0x0010
+#define RX_RESUME 0x0020
+#define RX_SUSPEND 0x0030
+#define RX_ABORT 0x0040
+
+typedef u32 phys_addr;
+
+static inline phys_addr
+va_to_pa(void *x) {
+ return x ? virt_to_bus(x) : I596_NULL;
+}
+
+static inline void *
+pa_to_va(phys_addr x) {
+ return (x == I596_NULL) ? NULL : bus_to_virt(x);
+}
+
+/* status bits for cmd */
+#define CMD_STAT_C 0x8000 /* CU command complete */
+#define CMD_STAT_B 0x4000 /* CU command in progress */
+#define CMD_STAT_OK 0x2000 /* CU command completed without errors */
+#define CMD_STAT_A 0x1000 /* CU command abnormally terminated */
+
+struct i596_cmd { /* 8 bytes */
+ unsigned short status;
+ unsigned short command;
+ phys_addr pa_next; /* va_to_pa(struct i596_cmd *next) */
+};
+
+#define EOF 0x8000
+#define SIZE_MASK 0x3fff
+
+struct i596_tbd {
+ unsigned short size;
+ unsigned short pad;
+ phys_addr pa_next; /* va_to_pa(struct i596_tbd *next) */
+ phys_addr pa_data; /* va_to_pa(char *data) */
+ struct sk_buff *skb;
+};
+
+struct tx_cmd {
+ struct i596_cmd cmd;
+ phys_addr pa_tbd; /* va_to_pa(struct i596_tbd *tbd) */
+ unsigned short size;
+ unsigned short pad;
+};
+
+/* status bits for rfd */
+#define RFD_STAT_C 0x8000 /* Frame reception complete */
+#define RFD_STAT_B 0x4000 /* Frame reception in progress */
+#define RFD_STAT_OK 0x2000 /* Frame received without errors */
+#define RFD_STATUS 0x1fff
+#define RFD_LENGTH_ERR 0x1000
+#define RFD_CRC_ERR 0x0800
+#define RFD_ALIGN_ERR 0x0400
+#define RFD_NOBUFS_ERR 0x0200
+#define RFD_DMA_ERR 0x0100 /* DMA overrun failure to acquire system bus */
+#define RFD_SHORT_FRAME_ERR 0x0080
+#define RFD_NOEOP_ERR 0x0040
+#define RFD_TRUNC_ERR 0x0020
+#define RFD_MULTICAST 0x0002 /* 0: destination had our address
+ 1: destination was broadcast/multicast */
+#define RFD_COLLISION 0x0001
+
+/* receive frame descriptor */
+struct i596_rfd {
+ unsigned short stat;
+ unsigned short cmd;
+ phys_addr pa_next; /* va_to_pa(struct i596_rfd *next) */
+ phys_addr pa_rbd; /* va_to_pa(struct i596_rbd *rbd) */
+ unsigned short count;
+ unsigned short size;
+ char data[1532];
+};
+
+#define RBD_EL 0x8000
+#define RBD_P 0x4000
+#define RBD_SIZEMASK 0x3fff
+#define RBD_EOF 0x8000
+#define RBD_F 0x4000
+
+/* receive buffer descriptor */
+struct i596_rbd {
+ unsigned short size;
+ unsigned short pad;
+ phys_addr pa_next; /* va_to_pa(struct i596_tbd *next) */
+ phys_addr pa_data; /* va_to_pa(char *data) */
+ phys_addr pa_prev; /* va_to_pa(struct i596_tbd *prev) */
+
+ /* Driver private part */
+ struct sk_buff *skb;
+};
+
+#define RX_RING_SIZE 64
+#define RX_SKBSIZE (ETH_FRAME_LEN+10)
+#define RX_RBD_SIZE 32
+
+/* System Control Block - 40 bytes */
+struct i596_scb {
+ u16 status; /* 0 */
+ u16 command; /* 2 */
+ phys_addr pa_cmd; /* 4 - va_to_pa(struct i596_cmd *cmd) */
+ phys_addr pa_rfd; /* 8 - va_to_pa(struct i596_rfd *rfd) */
+ u32 crc_err; /* 12 */
+ u32 align_err; /* 16 */
+ u32 resource_err; /* 20 */
+ u32 over_err; /* 24 */
+ u32 rcvdt_err; /* 28 */
+ u32 short_err; /* 32 */
+ u16 t_on; /* 36 */
+ u16 t_off; /* 38 */
+};
+
+/* Intermediate System Configuration Pointer - 8 bytes */
+struct i596_iscp {
+ u32 busy; /* 0 */
+ phys_addr pa_scb; /* 4 - va_to_pa(struct i596_scb *scb) */
+};
+
+/* System Configuration Pointer - 12 bytes */
+struct i596_scp {
+ u32 sysbus; /* 0 */
+ u32 pad; /* 4 */
+ phys_addr pa_iscp; /* 8 - va_to_pa(struct i596_iscp *iscp) */
+};
+
+/* Selftest and dump results - needs 16-byte alignment */
+/*
+ * The size of the dump area is 304 bytes. When the dump is executed
+ * by the Port command an extra word will be appended to the dump area.
+ * The extra word is a copy of the Dump status word (containing the
+ * C, B, OK bits). [I find 0xa006, with a0 for C+OK and 6 for dump]
+ */
+struct i596_dump {
+ u16 dump[153]; /* (304 = 130h) + 2 bytes */
+};
+
+struct i596_private { /* aligned to a 16-byte boundary */
+ struct i596_scp scp; /* 0 - needs 16-byte alignment */
+ struct i596_iscp iscp; /* 12 */
+ struct i596_scb scb; /* 20 */
+ u32 dummy; /* 60 */
+ struct i596_dump dump; /* 64 - needs 16-byte alignment */
+
+ struct i596_cmd set_add;
+ char eth_addr[8]; /* directly follows set_add */
+
+ struct i596_cmd set_conf;
+ char i596_config[16]; /* directly follows set_conf */
+
+ struct i596_cmd tdr;
+ unsigned long tdr_stat; /* directly follows tdr */
+
+ int last_restart;
+ struct i596_rbd *rbd_list;
+ struct i596_rbd *rbd_tail;
+ struct i596_rfd *rx_tail;
+ struct i596_cmd *cmd_tail;
+ struct i596_cmd *cmd_head;
+ int cmd_backlog;
+ unsigned long last_cmd;
+ spinlock_t cmd_lock;
+};
+
+static char init_setup[14] = {
+ 0x8E, /* length 14 bytes, prefetch on */
+ 0xC8, /* default: fifo to 8, monitor off */
+ 0x40, /* default: don't save bad frames (apricot.c had 0x80) */
+ 0x2E, /* (default is 0x26)
+ No source address insertion, 8 byte preamble */
+ 0x00, /* default priority and backoff */
+ 0x60, /* default interframe spacing */
+ 0x00, /* default slot time LSB */
+ 0xf2, /* default slot time and nr of retries */
+ 0x00, /* default various bits
+ (0: promiscuous mode, 1: broadcast disable,
+ 2: encoding mode, 3: transmit on no CRS,
+ 4: no CRC insertion, 5: CRC type,
+ 6: bit stuffing, 7: padding) */
+ 0x00, /* default carrier sense and collision detect */
+ 0x40, /* default minimum frame length */
+ 0xff, /* (default is 0xff, and that is what apricot.c has;
+ elp486.c has 0xfb: Enable crc append in memory.) */
+ 0x00, /* default: not full duplex */
+ 0x7f /* (default is 0x3f) multi IA */
+};
+
+static int i596_open(struct net_device *dev);
+static netdev_tx_t i596_start_xmit(struct sk_buff *skb, struct net_device *dev);
+static irqreturn_t i596_interrupt(int irq, void *dev_id);
+static int i596_close(struct net_device *dev);
+static void i596_add_cmd(struct net_device *dev, struct i596_cmd *cmd);
+static void print_eth(char *);
+static void set_multicast_list(struct net_device *dev);
+static void i596_tx_timeout(struct net_device *dev);
+
+static int
+i596_timeout(struct net_device *dev, char *msg, int ct) {
+ struct i596_private *lp;
+ int boguscnt = ct;
+
+ lp = netdev_priv(dev);
+ while (lp->scb.command) {
+ if (--boguscnt == 0) {
+ printk("%s: %s timed out - stat %4.4x, cmd %4.4x\n",
+ dev->name, msg,
+ lp->scb.status, lp->scb.command);
+ return 1;
+ }
+ udelay(5);
+ barrier();
+ }
+ return 0;
+}
+
+static inline int
+init_rx_bufs(struct net_device *dev, int num) {
+ struct i596_private *lp;
+ struct i596_rfd *rfd;
+ int i;
+ // struct i596_rbd *rbd;
+
+ lp = netdev_priv(dev);
+ lp->scb.pa_rfd = I596_NULL;
+
+ for (i = 0; i < num; i++) {
+ rfd = kmalloc(sizeof(struct i596_rfd), GFP_KERNEL);
+ if (rfd == NULL)
+ break;
+
+ rfd->stat = 0;
+ rfd->pa_rbd = I596_NULL;
+ rfd->count = 0;
+ rfd->size = 1532;
+ if (i == 0) {
+ rfd->cmd = CMD_EOL;
+ lp->rx_tail = rfd;
+ } else {
+ rfd->cmd = 0;
+ }
+ rfd->pa_next = lp->scb.pa_rfd;
+ lp->scb.pa_rfd = va_to_pa(rfd);
+ lp->rx_tail->pa_next = lp->scb.pa_rfd;
+ }
+
+#if 0
+ for (i = 0; i<RX_RBD_SIZE; i++) {
+ rbd = kmalloc(sizeof(struct i596_rbd), GFP_KERNEL);
+ if (rbd) {
+ rbd->pad = 0;
+ rbd->count = 0;
+ rbd->skb = dev_alloc_skb(RX_SKBSIZE);
+ if (!rbd->skb) {
+ printk("dev_alloc_skb failed");
+ }
+ rbd->next = rfd->rbd;
+ if (i) {
+ rfd->rbd->prev = rbd;
+ rbd->size = RX_SKBSIZE;
+ } else {
+ rbd->size = (RX_SKBSIZE | RBD_EL);
+ lp->rbd_tail = rbd;
+ }
+
+ rfd->rbd = rbd;
+ } else {
+ printk("Could not kmalloc rbd\n");
+ }
+ }
+ lp->rbd_tail->next = rfd->rbd;
+#endif
+ return i;
+}
+
+static inline void
+remove_rx_bufs(struct net_device *dev) {
+ struct i596_private *lp;
+ struct i596_rfd *rfd;
+
+ lp = netdev_priv(dev);
+ lp->rx_tail->pa_next = I596_NULL;
+
+ do {
+ rfd = pa_to_va(lp->scb.pa_rfd);
+ lp->scb.pa_rfd = rfd->pa_next;
+ kfree(rfd);
+ } while (rfd != lp->rx_tail);
+
+ lp->rx_tail = NULL;
+
+#if 0
+ for (lp->rbd_list) {
+ }
+#endif
+}
+
+#define PORT_RESET 0x00 /* reset 82596 */
+#define PORT_SELFTEST 0x01 /* selftest */
+#define PORT_ALTSCP 0x02 /* alternate SCB address */
+#define PORT_DUMP 0x03 /* dump */
+
+#define IOADDR 0xcb0 /* real constant */
+#define IRQ 10 /* default IRQ - can be changed by ECU */
+
+/* The 82596 requires two 16-bit write cycles for a port command */
+static inline void
+PORT(phys_addr a, unsigned int cmd) {
+ if (a & 0xf)
+ printk("lp486e.c: PORT: address not aligned\n");
+ outw(((a & 0xffff) | cmd), IOADDR);
+ outw(((a>>16) & 0xffff), IOADDR+2);
+}
+
+static inline void
+CA(void) {
+ outb(0, IOADDR+4);
+ udelay(8);
+}
+
+static inline void
+CLEAR_INT(void) {
+ outb(0, IOADDR+8);
+}
+
+#if 0
+/* selftest or dump */
+static void
+i596_port_do(struct net_device *dev, int portcmd, char *cmdname) {
+ struct i596_private *lp = netdev_priv(dev);
+ u16 *outp;
+ int i, m;
+
+ memset((void *)&(lp->dump), 0, sizeof(struct i596_dump));
+ outp = &(lp->dump.dump[0]);
+
+ PORT(va_to_pa(outp), portcmd);
+ mdelay(30); /* random, unmotivated */
+
+ printk("lp486e i82596 %s result:\n", cmdname);
+ for (m = ARRAY_SIZE(lp->dump.dump); m && lp->dump.dump[m-1] == 0; m--)
+ ;
+ for (i = 0; i < m; i++) {
+ printk(" %04x", lp->dump.dump[i]);
+ if (i%8 == 7)
+ printk("\n");
+ }
+ printk("\n");
+}
+#endif
+
+static int
+i596_scp_setup(struct net_device *dev) {
+ struct i596_private *lp = netdev_priv(dev);
+ int boguscnt;
+
+ /* Setup SCP, ISCP, SCB */
+ /*
+ * sysbus bits:
+ * only a single byte is significant - here 0x44
+ * 0x80: big endian mode (details depend on stepping)
+ * 0x40: 1
+ * 0x20: interrupt pin is active low
+ * 0x10: lock function disabled
+ * 0x08: external triggering of bus throttle timers
+ * 0x06: 00: 82586 compat mode, 01: segmented mode, 10: linear mode
+ * 0x01: unused
+ */
+ lp->scp.sysbus = 0x00440000; /* linear mode */
+ lp->scp.pad = 0; /* must be zero */
+ lp->scp.pa_iscp = va_to_pa(&(lp->iscp));
+
+ /*
+ * The CPU sets the ISCP to 1 before it gives the first CA()
+ */
+ lp->iscp.busy = 0x0001;
+ lp->iscp.pa_scb = va_to_pa(&(lp->scb));
+
+ lp->scb.command = 0;
+ lp->scb.status = 0;
+ lp->scb.pa_cmd = I596_NULL;
+ /* lp->scb.pa_rfd has been initialised already */
+
+ lp->last_cmd = jiffies;
+ lp->cmd_backlog = 0;
+ lp->cmd_head = NULL;
+
+ /*
+ * Reset the 82596.
+ * We need to wait 10 systemclock cycles, and
+ * 5 serial clock cycles.
+ */
+ PORT(0, PORT_RESET); /* address part ignored */
+ udelay(100);
+
+ /*
+ * Before the CA signal is asserted, the default SCP address
+ * (0x00fffff4) can be changed to a 16-byte aligned value
+ */
+ PORT(va_to_pa(&lp->scp), PORT_ALTSCP); /* change the scp address */
+
+ /*
+ * The initialization procedure begins when a
+ * Channel Attention signal is asserted after a reset.
+ */
+
+ CA();
+
+ /*
+ * The ISCP busy is cleared by the 82596 after the SCB address is read.
+ */
+ boguscnt = 100;
+ while (lp->iscp.busy) {
+ if (--boguscnt == 0) {
+ /* No i82596 present? */
+ printk("%s: i82596 initialization timed out\n",
+ dev->name);
+ return 1;
+ }
+ udelay(5);
+ barrier();
+ }
+ /* I find here boguscnt==100, so no delay was required. */
+
+ return 0;
+}
+
+static int
+init_i596(struct net_device *dev) {
+ struct i596_private *lp;
+
+ if (i596_scp_setup(dev))
+ return 1;
+
+ lp = netdev_priv(dev);
+ lp->scb.command = 0;
+
+ memcpy ((void *)lp->i596_config, init_setup, 14);
+ lp->set_conf.command = CmdConfigure;
+ i596_add_cmd(dev, (void *)&lp->set_conf);
+
+ memcpy ((void *)lp->eth_addr, dev->dev_addr, 6);
+ lp->set_add.command = CmdIASetup;
+ i596_add_cmd(dev, (struct i596_cmd *)&lp->set_add);
+
+ lp->tdr.command = CmdTDR;
+ i596_add_cmd(dev, (struct i596_cmd *)&lp->tdr);
+
+ if (lp->scb.command && i596_timeout(dev, "i82596 init", 200))
+ return 1;
+
+ lp->scb.command = RX_START;
+ CA();
+
+ barrier();
+
+ if (lp->scb.command && i596_timeout(dev, "Receive Unit start", 100))
+ return 1;
+
+ return 0;
+}
+
+/* Receive a single frame */
+static inline int
+i596_rx_one(struct net_device *dev, struct i596_private *lp,
+ struct i596_rfd *rfd, int *frames) {
+
+ if (rfd->stat & RFD_STAT_OK) {
+ /* a good frame */
+ int pkt_len = (rfd->count & 0x3fff);
+ struct sk_buff *skb = dev_alloc_skb(pkt_len);
+
+ (*frames)++;
+
+ if (rfd->cmd & CMD_EOL)
+ printk("Received on EOL\n");
+
+ if (skb == NULL) {
+ printk ("%s: i596_rx Memory squeeze, "
+ "dropping packet.\n", dev->name);
+ dev->stats.rx_dropped++;
+ return 1;
+ }
+
+ memcpy(skb_put(skb,pkt_len), rfd->data, pkt_len);
+
+ skb->protocol = eth_type_trans(skb,dev);
+ netif_rx(skb);
+ dev->stats.rx_packets++;
+ } else {
+#if 0
+ printk("Frame reception error status %04x\n",
+ rfd->stat);
+#endif
+ dev->stats.rx_errors++;
+ if (rfd->stat & RFD_COLLISION)
+ dev->stats.collisions++;
+ if (rfd->stat & RFD_SHORT_FRAME_ERR)
+ dev->stats.rx_length_errors++;
+ if (rfd->stat & RFD_DMA_ERR)
+ dev->stats.rx_over_errors++;
+ if (rfd->stat & RFD_NOBUFS_ERR)
+ dev->stats.rx_fifo_errors++;
+ if (rfd->stat & RFD_ALIGN_ERR)
+ dev->stats.rx_frame_errors++;
+ if (rfd->stat & RFD_CRC_ERR)
+ dev->stats.rx_crc_errors++;
+ if (rfd->stat & RFD_LENGTH_ERR)
+ dev->stats.rx_length_errors++;
+ }
+ rfd->stat = rfd->count = 0;
+ return 0;
+}
+
+static int
+i596_rx(struct net_device *dev) {
+ struct i596_private *lp = netdev_priv(dev);
+ struct i596_rfd *rfd;
+ int frames = 0;
+
+ while (1) {
+ rfd = pa_to_va(lp->scb.pa_rfd);
+ if (!rfd) {
+ printk(KERN_ERR "i596_rx: NULL rfd?\n");
+ return 0;
+ }
+#if 1
+ if (rfd->stat && !(rfd->stat & (RFD_STAT_C | RFD_STAT_B)))
+ printk("SF:%p-%04x\n", rfd, rfd->stat);
+#endif
+ if (!(rfd->stat & RFD_STAT_C))
+ break; /* next one not ready */
+ if (i596_rx_one(dev, lp, rfd, &frames))
+ break; /* out of memory */
+ rfd->cmd = CMD_EOL;
+ lp->rx_tail->cmd = 0;
+ lp->rx_tail = rfd;
+ lp->scb.pa_rfd = rfd->pa_next;
+ barrier();
+ }
+
+ return frames;
+}
+
+static void
+i596_cleanup_cmd(struct net_device *dev) {
+ struct i596_private *lp;
+ struct i596_cmd *cmd;
+
+ lp = netdev_priv(dev);
+ while (lp->cmd_head) {
+ cmd = (struct i596_cmd *)lp->cmd_head;
+
+ lp->cmd_head = pa_to_va(lp->cmd_head->pa_next);
+ lp->cmd_backlog--;
+
+ switch ((cmd->command) & 0x7) {
+ case CmdTx: {
+ struct tx_cmd *tx_cmd = (struct tx_cmd *) cmd;
+ struct i596_tbd * tx_cmd_tbd;
+ tx_cmd_tbd = pa_to_va(tx_cmd->pa_tbd);
+
+ dev_kfree_skb_any(tx_cmd_tbd->skb);
+
+ dev->stats.tx_errors++;
+ dev->stats.tx_aborted_errors++;
+
+ cmd->pa_next = I596_NULL;
+ kfree((unsigned char *)tx_cmd);
+ netif_wake_queue(dev);
+ break;
+ }
+ case CmdMulticastList: {
+ // unsigned short count = *((unsigned short *) (ptr + 1));
+
+ cmd->pa_next = I596_NULL;
+ kfree((unsigned char *)cmd);
+ break;
+ }
+ default: {
+ cmd->pa_next = I596_NULL;
+ break;
+ }
+ }
+ barrier();
+ }
+
+ if (lp->scb.command && i596_timeout(dev, "i596_cleanup_cmd", 100))
+ ;
+
+ lp->scb.pa_cmd = va_to_pa(lp->cmd_head);
+}
+
+static void i596_reset(struct net_device *dev, struct i596_private *lp, int ioaddr) {
+
+ if (lp->scb.command && i596_timeout(dev, "i596_reset", 100))
+ ;
+
+ netif_stop_queue(dev);
+
+ lp->scb.command = CUC_ABORT | RX_ABORT;
+ CA();
+ barrier();
+
+ /* wait for shutdown */
+ if (lp->scb.command && i596_timeout(dev, "i596_reset(2)", 400))
+ ;
+
+ i596_cleanup_cmd(dev);
+ i596_rx(dev);
+
+ netif_start_queue(dev);
+ /*dev_kfree_skb(skb, FREE_WRITE);*/
+ init_i596(dev);
+}
+
+static void i596_add_cmd(struct net_device *dev, struct i596_cmd *cmd) {
+ struct i596_private *lp = netdev_priv(dev);
+ int ioaddr = dev->base_addr;
+ unsigned long flags;
+
+ cmd->status = 0;
+ cmd->command |= (CMD_EOL | CMD_INTR);
+ cmd->pa_next = I596_NULL;
+
+ spin_lock_irqsave(&lp->cmd_lock, flags);
+
+ if (lp->cmd_head) {
+ lp->cmd_tail->pa_next = va_to_pa(cmd);
+ } else {
+ lp->cmd_head = cmd;
+ if (lp->scb.command && i596_timeout(dev, "i596_add_cmd", 100))
+ ;
+ lp->scb.pa_cmd = va_to_pa(cmd);
+ lp->scb.command = CUC_START;
+ CA();
+ }
+ lp->cmd_tail = cmd;
+ lp->cmd_backlog++;
+
+ lp->cmd_head = pa_to_va(lp->scb.pa_cmd);
+ spin_unlock_irqrestore(&lp->cmd_lock, flags);
+
+ if (lp->cmd_backlog > 16) {
+ int tickssofar = jiffies - lp->last_cmd;
+ if (tickssofar < HZ/4)
+ return;
+
+ printk(KERN_WARNING "%s: command unit timed out, status resetting.\n", dev->name);
+ i596_reset(dev, lp, ioaddr);
+ }
+}
+
+static int i596_open(struct net_device *dev)
+{
+ int i;
+
+ i = request_irq(dev->irq, i596_interrupt, IRQF_SHARED, dev->name, dev);
+ if (i) {
+ printk(KERN_ERR "%s: IRQ %d not free\n", dev->name, dev->irq);
+ return i;
+ }
+
+ if ((i = init_rx_bufs(dev, RX_RING_SIZE)) < RX_RING_SIZE)
+ printk(KERN_ERR "%s: only able to allocate %d receive buffers\n", dev->name, i);
+
+ if (i < 4) {
+ free_irq(dev->irq, dev);
+ return -EAGAIN;
+ }
+ netif_start_queue(dev);
+ init_i596(dev);
+ return 0; /* Always succeed */
+}
+
+static netdev_tx_t i596_start_xmit (struct sk_buff *skb, struct net_device *dev) {
+ struct tx_cmd *tx_cmd;
+ short length;
+
+ length = skb->len;
+
+ if (length < ETH_ZLEN) {
+ if (skb_padto(skb, ETH_ZLEN))
+ return NETDEV_TX_OK;
+ length = ETH_ZLEN;
+ }
+
+ tx_cmd = kmalloc((sizeof (struct tx_cmd) + sizeof (struct i596_tbd)), GFP_ATOMIC);
+ if (tx_cmd == NULL) {
+ printk(KERN_WARNING "%s: i596_xmit Memory squeeze, dropping packet.\n", dev->name);
+ dev->stats.tx_dropped++;
+ dev_kfree_skb (skb);
+ } else {
+ struct i596_tbd *tx_cmd_tbd;
+ tx_cmd_tbd = (struct i596_tbd *) (tx_cmd + 1);
+ tx_cmd->pa_tbd = va_to_pa (tx_cmd_tbd);
+ tx_cmd_tbd->pa_next = I596_NULL;
+
+ tx_cmd->cmd.command = (CMD_FLEX | CmdTx);
+
+ tx_cmd->pad = 0;
+ tx_cmd->size = 0;
+ tx_cmd_tbd->pad = 0;
+ tx_cmd_tbd->size = (EOF | length);
+
+ tx_cmd_tbd->pa_data = va_to_pa (skb->data);
+ tx_cmd_tbd->skb = skb;
+
+ if (i596_debug & LOG_SRCDST)
+ print_eth (skb->data);
+
+ i596_add_cmd (dev, (struct i596_cmd *) tx_cmd);
+
+ dev->stats.tx_packets++;
+ }
+
+ return NETDEV_TX_OK;
+}
+
+static void
+i596_tx_timeout (struct net_device *dev) {
+ struct i596_private *lp = netdev_priv(dev);
+ int ioaddr = dev->base_addr;
+
+ /* Transmitter timeout, serious problems. */
+ printk(KERN_WARNING "%s: transmit timed out, status resetting.\n", dev->name);
+ dev->stats.tx_errors++;
+
+ /* Try to restart the adaptor */
+ if (lp->last_restart == dev->stats.tx_packets) {
+ printk ("Resetting board.\n");
+
+ /* Shutdown and restart */
+ i596_reset (dev, lp, ioaddr);
+ } else {
+ /* Issue a channel attention signal */
+ printk ("Kicking board.\n");
+ lp->scb.command = (CUC_START | RX_START);
+ CA();
+ lp->last_restart = dev->stats.tx_packets;
+ }
+ netif_wake_queue(dev);
+}
+
+static void print_eth(char *add)
+{
+ int i;
+
+ printk ("Dest ");
+ for (i = 0; i < 6; i++)
+ printk(" %2.2X", (unsigned char) add[i]);
+ printk ("\n");
+
+ printk ("Source");
+ for (i = 0; i < 6; i++)
+ printk(" %2.2X", (unsigned char) add[i+6]);
+ printk ("\n");
+
+ printk ("type %2.2X%2.2X\n",
+ (unsigned char) add[12], (unsigned char) add[13]);
+}
+
+static const struct net_device_ops i596_netdev_ops = {
+ .ndo_open = i596_open,
+ .ndo_stop = i596_close,
+ .ndo_start_xmit = i596_start_xmit,
+ .ndo_set_rx_mode = set_multicast_list,
+ .ndo_tx_timeout = i596_tx_timeout,
+ .ndo_change_mtu = eth_change_mtu,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_validate_addr = eth_validate_addr,
+};
+
+static int __init lp486e_probe(struct net_device *dev) {
+ struct i596_private *lp;
+ unsigned char eth_addr[6] = { 0, 0xaa, 0, 0, 0, 0 };
+ unsigned char *bios;
+ int i, j;
+ int ret = -ENOMEM;
+ static int probed;
+
+ if (probed)
+ return -ENODEV;
+ probed++;
+
+ if (!request_region(IOADDR, LP486E_TOTAL_SIZE, DRV_NAME)) {
+ printk(KERN_ERR "lp486e: IO address 0x%x in use\n", IOADDR);
+ return -EBUSY;
+ }
+
+ lp = netdev_priv(dev);
+ spin_lock_init(&lp->cmd_lock);
+
+ /*
+ * Do we really have this thing?
+ */
+ if (i596_scp_setup(dev)) {
+ ret = -ENODEV;
+ goto err_out_kfree;
+ }
+
+ dev->base_addr = IOADDR;
+ dev->irq = IRQ;
+
+
+ /*
+ * How do we find the ethernet address? I don't know.
+ * One possibility is to look at the EISA configuration area
+ * [0xe8000-0xe9fff]. This contains the ethernet address
+ * but not at a fixed address - things depend on setup options.
+ *
+ * If we find no address, or the wrong address, use
+ * ifconfig eth0 hw ether a1:a2:a3:a4:a5:a6
+ * with the value found in the BIOS setup.
+ */
+ bios = bus_to_virt(0xe8000);
+ for (j = 0; j < 0x2000; j++) {
+ if (bios[j] == 0 && bios[j+1] == 0xaa && bios[j+2] == 0) {
+ printk("%s: maybe address at BIOS 0x%x:",
+ dev->name, 0xe8000+j);
+ for (i = 0; i < 6; i++) {
+ eth_addr[i] = bios[i+j];
+ printk(" %2.2X", eth_addr[i]);
+ }
+ printk("\n");
+ }
+ }
+
+ printk("%s: lp486e 82596 at %#3lx, IRQ %d,",
+ dev->name, dev->base_addr, dev->irq);
+ for (i = 0; i < 6; i++)
+ printk(" %2.2X", dev->dev_addr[i] = eth_addr[i]);
+ printk("\n");
+
+ /* The LP486E-specific entries in the device structure. */
+ dev->netdev_ops = &i596_netdev_ops;
+ dev->watchdog_timeo = 5*HZ;
+
+#if 0
+ /* selftest reports 0x320925ae - don't know what that means */
+ i596_port_do(dev, PORT_SELFTEST, "selftest");
+ i596_port_do(dev, PORT_DUMP, "dump");
+#endif
+ return 0;
+
+err_out_kfree:
+ release_region(IOADDR, LP486E_TOTAL_SIZE);
+ return ret;
+}
+
+static inline void
+i596_handle_CU_completion(struct net_device *dev,
+ struct i596_private *lp,
+ unsigned short status,
+ unsigned short *ack_cmdp) {
+ struct i596_cmd *cmd;
+ int frames_out = 0;
+ int commands_done = 0;
+ int cmd_val;
+ unsigned long flags;
+
+ spin_lock_irqsave(&lp->cmd_lock, flags);
+ cmd = lp->cmd_head;
+
+ while (lp->cmd_head && (lp->cmd_head->status & CMD_STAT_C)) {
+ cmd = lp->cmd_head;
+
+ lp->cmd_head = pa_to_va(lp->cmd_head->pa_next);
+ lp->cmd_backlog--;
+
+ commands_done++;
+ cmd_val = cmd->command & 0x7;
+#if 0
+ printk("finished CU %s command (%d)\n",
+ CUcmdnames[cmd_val], cmd_val);
+#endif
+ switch (cmd_val) {
+ case CmdTx:
+ {
+ struct tx_cmd *tx_cmd;
+ struct i596_tbd *tx_cmd_tbd;
+
+ tx_cmd = (struct tx_cmd *) cmd;
+ tx_cmd_tbd = pa_to_va(tx_cmd->pa_tbd);
+
+ frames_out++;
+ if (cmd->status & CMD_STAT_OK) {
+ if (i596_debug)
+ print_eth(pa_to_va(tx_cmd_tbd->pa_data));
+ } else {
+ dev->stats.tx_errors++;
+ if (i596_debug)
+ printk("transmission failure:%04x\n",
+ cmd->status);
+ if (cmd->status & 0x0020)
+ dev->stats.collisions++;
+ if (!(cmd->status & 0x0040))
+ dev->stats.tx_heartbeat_errors++;
+ if (cmd->status & 0x0400)
+ dev->stats.tx_carrier_errors++;
+ if (cmd->status & 0x0800)
+ dev->stats.collisions++;
+ if (cmd->status & 0x1000)
+ dev->stats.tx_aborted_errors++;
+ }
+ dev_kfree_skb_irq(tx_cmd_tbd->skb);
+
+ cmd->pa_next = I596_NULL;
+ kfree((unsigned char *)tx_cmd);
+ netif_wake_queue(dev);
+ break;
+ }
+
+ case CmdMulticastList:
+ cmd->pa_next = I596_NULL;
+ kfree((unsigned char *)cmd);
+ break;
+
+ case CmdTDR:
+ {
+ unsigned long status = *((unsigned long *) (cmd + 1));
+ if (status & 0x8000) {
+ if (i596_debug)
+ printk("%s: link ok.\n", dev->name);
+ } else {
+ if (status & 0x4000)
+ printk("%s: Transceiver problem.\n",
+ dev->name);
+ if (status & 0x2000)
+ printk("%s: Termination problem.\n",
+ dev->name);
+ if (status & 0x1000)
+ printk("%s: Short circuit.\n",
+ dev->name);
+ printk("%s: Time %ld.\n",
+ dev->name, status & 0x07ff);
+ }
+ }
+ default:
+ cmd->pa_next = I596_NULL;
+ lp->last_cmd = jiffies;
+
+ }
+ barrier();
+ }
+
+ cmd = lp->cmd_head;
+ while (cmd && (cmd != lp->cmd_tail)) {
+ cmd->command &= 0x1fff;
+ cmd = pa_to_va(cmd->pa_next);
+ barrier();
+ }
+
+ if (lp->cmd_head)
+ *ack_cmdp |= CUC_START;
+ lp->scb.pa_cmd = va_to_pa(lp->cmd_head);
+ spin_unlock_irqrestore(&lp->cmd_lock, flags);
+}
+
+static irqreturn_t
+i596_interrupt(int irq, void *dev_instance)
+{
+ struct net_device *dev = dev_instance;
+ struct i596_private *lp = netdev_priv(dev);
+ unsigned short status, ack_cmd = 0;
+ int frames_in = 0;
+
+ /*
+ * The 82596 examines the command, performs the required action,
+ * and then clears the SCB command word.
+ */
+ if (lp->scb.command && i596_timeout(dev, "interrupt", 40))
+ ;
+
+ /*
+ * The status word indicates the status of the 82596.
+ * It is modified only by the 82596.
+ *
+ * [So, we must not clear it. I find often status 0xffff,
+ * which is not one of the values allowed by the docs.]
+ */
+ status = lp->scb.status;
+#if 0
+ if (i596_debug) {
+ printk("%s: i596 interrupt, ", dev->name);
+ i596_out_status(status);
+ }
+#endif
+ /* Impossible, but it happens - perhaps when we get
+ a receive interrupt but scb.pa_rfd is I596_NULL. */
+ if (status == 0xffff) {
+ printk("%s: i596_interrupt: got status 0xffff\n", dev->name);
+ goto out;
+ }
+
+ ack_cmd = (status & STAT_ACK);
+
+ if (status & (STAT_CX | STAT_CNA))
+ i596_handle_CU_completion(dev, lp, status, &ack_cmd);
+
+ if (status & (STAT_FR | STAT_RNR)) {
+ /* Restart the receive unit when it got inactive somehow */
+ if ((status & STAT_RNR) && netif_running(dev))
+ ack_cmd |= RX_START;
+
+ if (status & STAT_FR) {
+ frames_in = i596_rx(dev);
+ if (!frames_in)
+ printk("receive frame reported, but no frames\n");
+ }
+ }
+
+ /* acknowledge the interrupt */
+ /*
+ if ((lp->scb.pa_cmd != I596_NULL) && netif_running(dev))
+ ack_cmd |= CUC_START;
+ */
+
+ if (lp->scb.command && i596_timeout(dev, "i596 interrupt", 100))
+ ;
+
+ lp->scb.command = ack_cmd;
+
+ CLEAR_INT();
+ CA();
+
+ out:
+ return IRQ_HANDLED;
+}
+
+static int i596_close(struct net_device *dev) {
+ struct i596_private *lp = netdev_priv(dev);
+
+ netif_stop_queue(dev);
+
+ if (i596_debug)
+ printk("%s: Shutting down ethercard, status was %4.4x.\n",
+ dev->name, lp->scb.status);
+
+ lp->scb.command = (CUC_ABORT | RX_ABORT);
+ CA();
+
+ i596_cleanup_cmd(dev);
+
+ if (lp->scb.command && i596_timeout(dev, "i596_close", 200))
+ ;
+
+ free_irq(dev->irq, dev);
+ remove_rx_bufs(dev);
+
+ return 0;
+}
+
+/*
+* Set or clear the multicast filter for this adaptor.
+*/
+
+static void set_multicast_list(struct net_device *dev) {
+ struct i596_private *lp = netdev_priv(dev);
+ struct i596_cmd *cmd;
+
+ if (i596_debug > 1)
+ printk ("%s: set multicast list %d\n",
+ dev->name, netdev_mc_count(dev));
+
+ if (!netdev_mc_empty(dev)) {
+ struct netdev_hw_addr *ha;
+ char *cp;
+ cmd = kmalloc(sizeof(struct i596_cmd) + 2 +
+ netdev_mc_count(dev) * 6, GFP_ATOMIC);
+ if (cmd == NULL) {
+ printk (KERN_ERR "%s: set_multicast Memory squeeze.\n", dev->name);
+ return;
+ }
+ cmd->command = CmdMulticastList;
+ *((unsigned short *) (cmd + 1)) = netdev_mc_count(dev) * 6;
+ cp = ((char *)(cmd + 1))+2;
+ netdev_for_each_mc_addr(ha, dev) {
+ memcpy(cp, ha->addr, 6);
+ cp += 6;
+ }
+ if (i596_debug & LOG_SRCDST)
+ print_eth (((char *)(cmd + 1)) + 2);
+ i596_add_cmd(dev, cmd);
+ } else {
+ if (lp->set_conf.pa_next != I596_NULL) {
+ return;
+ }
+ if (netdev_mc_empty(dev) &&
+ !(dev->flags & (IFF_PROMISC | IFF_ALLMULTI))) {
+ lp->i596_config[8] &= ~0x01;
+ } else {
+ lp->i596_config[8] |= 0x01;
+ }
+
+ i596_add_cmd(dev, (struct i596_cmd *) &lp->set_conf);
+ }
+}
+
+MODULE_AUTHOR("Ard van Breemen <ard@cstmel.nl.eu.org>");
+MODULE_DESCRIPTION("Intel Panther onboard i82596 driver");
+MODULE_LICENSE("GPL");
+
+static struct net_device *dev_lp486e;
+static int full_duplex;
+static int options;
+static int io = IOADDR;
+static int irq = IRQ;
+
+module_param(debug, int, 0);
+//module_param(max_interrupt_work, int, 0);
+//module_param(reverse_probe, int, 0);
+//module_param(rx_copybreak, int, 0);
+module_param(options, int, 0);
+module_param(full_duplex, int, 0);
+
+static int __init lp486e_init_module(void) {
+ int err;
+ struct net_device *dev = alloc_etherdev(sizeof(struct i596_private));
+ if (!dev)
+ return -ENOMEM;
+
+ dev->irq = irq;
+ dev->base_addr = io;
+ err = lp486e_probe(dev);
+ if (err) {
+ free_netdev(dev);
+ return err;
+ }
+ err = register_netdev(dev);
+ if (err) {
+ release_region(dev->base_addr, LP486E_TOTAL_SIZE);
+ free_netdev(dev);
+ return err;
+ }
+ dev_lp486e = dev;
+ full_duplex = 0;
+ options = 0;
+ return 0;
+}
+
+static void __exit lp486e_cleanup_module(void) {
+ unregister_netdev(dev_lp486e);
+ release_region(dev_lp486e->base_addr, LP486E_TOTAL_SIZE);
+ free_netdev(dev_lp486e);
+}
+
+module_init(lp486e_init_module);
+module_exit(lp486e_cleanup_module);
diff --git a/drivers/net/ethernet/i825xx/ni52.c b/drivers/net/ethernet/i825xx/ni52.c
new file mode 100644
index 00000000000..c0893715ef4
--- /dev/null
+++ b/drivers/net/ethernet/i825xx/ni52.c
@@ -0,0 +1,1346 @@
+/*
+ * net-3-driver for the NI5210 card (i82586 Ethernet chip)
+ *
+ * This is an extension to the Linux operating system, and is covered by the
+ * same GNU General Public License that covers that work.
+ *
+ * Alphacode 0.82 (96/09/29) for Linux 2.0.0 (or later)
+ * Copyrights (c) 1994,1995,1996 by M.Hipp (hippm@informatik.uni-tuebingen.de)
+ * [feel free to mail ....]
+ *
+ * when using as module: (no autoprobing!)
+ * run with e.g:
+ * insmod ni52.o io=0x360 irq=9 memstart=0xd0000 memend=0xd4000
+ *
+ * CAN YOU PLEASE REPORT ME YOUR PERFORMANCE EXPERIENCES !!.
+ *
+ * If you find a bug, please report me:
+ * The kernel panic output and any kmsg from the ni52 driver
+ * the ni5210-driver-version and the linux-kernel version
+ * how many shared memory (memsize) on the netcard,
+ * bootprom: yes/no, base_addr, mem_start
+ * maybe the ni5210-card revision and the i82586 version
+ *
+ * autoprobe for: base_addr: 0x300,0x280,0x360,0x320,0x340
+ * mem_start: 0xd0000,0xd2000,0xc8000,0xca000,0xd4000,0xd6000,
+ * 0xd8000,0xcc000,0xce000,0xda000,0xdc000
+ *
+ * sources:
+ * skeleton.c from Donald Becker
+ *
+ * I have also done a look in the following sources: (mail me if you need them)
+ * crynwr-packet-driver by Russ Nelson
+ * Garret A. Wollman's (fourth) i82586-driver for BSD
+ * (before getting an i82596 (yes 596 not 586) manual, the existing drivers
+ * helped me a lot to understand this tricky chip.)
+ *
+ * Known Problems:
+ * The internal sysbus seems to be slow. So we often lose packets because of
+ * overruns while receiving from a fast remote host.
+ * This can slow down TCP connections. Maybe the newer ni5210 cards are
+ * better. My experience is, that if a machine sends with more than about
+ * 500-600K/s the fifo/sysbus overflows.
+ *
+ * IMPORTANT NOTE:
+ * On fast networks, it's a (very) good idea to have 16K shared memory. With
+ * 8K, we can store only 4 receive frames, so it can (easily) happen that a
+ * remote machine 'overruns' our system.
+ *
+ * Known i82586/card problems (I'm sure, there are many more!):
+ * Running the NOP-mode, the i82586 sometimes seems to forget to report
+ * every xmit-interrupt until we restart the CU.
+ * Another MAJOR bug is, that the RU sometimes seems to ignore the EL-Bit
+ * in the RBD-Struct which indicates an end of the RBD queue.
+ * Instead, the RU fetches another (randomly selected and
+ * usually used) RBD and begins to fill it. (Maybe, this happens only if
+ * the last buffer from the previous RFD fits exact into the queue and
+ * the next RFD can't fetch an initial RBD. Anyone knows more? )
+ *
+ * results from ftp performance tests with Linux 1.2.5
+ * send and receive about 350-400 KByte/s (peak up to 460 kbytes/s)
+ * sending in NOP-mode: peak performance up to 530K/s (but better don't
+ * run this mode)
+ */
+
+/*
+ * 29.Sept.96: virt_to_bus changes for new memory scheme
+ * 19.Feb.96: more Mcast changes, module support (MH)
+ *
+ * 18.Nov.95: Mcast changes (AC).
+ *
+ * 23.April.95: fixed(?) receiving problems by configuring a RFD more
+ * than the number of RBD's. Can maybe cause other problems.
+ * 18.April.95: Added MODULE support (MH)
+ * 17.April.95: MC related changes in init586() and set_multicast_list().
+ * removed use of 'jiffies' in init586() (MH)
+ *
+ * 19.Sep.94: Added Multicast support (not tested yet) (MH)
+ *
+ * 18.Sep.94: Workaround for 'EL-Bug'. Removed flexible RBD-handling.
+ * Now, every RFD has exact one RBD. (MH)
+ *
+ * 14.Sep.94: added promiscuous mode, a few cleanups (MH)
+ *
+ * 19.Aug.94: changed request_irq() parameter (MH)
+ *
+ * 20.July.94: removed cleanup bugs, removed a 16K-mem-probe-bug (MH)
+ *
+ * 19.July.94: lotsa cleanups .. (MH)
+ *
+ * 17.July.94: some patches ... verified to run with 1.1.29 (MH)
+ *
+ * 4.July.94: patches for Linux 1.1.24 (MH)
+ *
+ * 26.March.94: patches for Linux 1.0 and iomem-auto-probe (MH)
+ *
+ * 30.Sep.93: Added nop-chain .. driver now runs with only one Xmit-Buff,
+ * too (MH)
+ *
+ * < 30.Sep.93: first versions
+ */
+
+static int debuglevel; /* debug-printk 0: off 1: a few 2: more */
+static int automatic_resume; /* experimental .. better should be zero */
+static int rfdadd; /* rfdadd=1 may be better for 8K MEM cards */
+static int fifo = 0x8; /* don't change */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/bitops.h>
+#include <asm/io.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include "ni52.h"
+
+#define DRV_NAME "ni52"
+
+#define DEBUG /* debug on */
+#define SYSBUSVAL 1 /* 8 Bit */
+
+#define ni_attn586() { outb(0, dev->base_addr + NI52_ATTENTION); }
+#define ni_reset586() { outb(0, dev->base_addr + NI52_RESET); }
+#define ni_disint() { outb(0, dev->base_addr + NI52_INTDIS); }
+#define ni_enaint() { outb(0, dev->base_addr + NI52_INTENA); }
+
+#define make32(ptr16) ((void __iomem *)(p->memtop + (short) (ptr16)))
+#define make24(ptr32) ((char __iomem *)(ptr32)) - p->base
+#define make16(ptr32) ((unsigned short) ((char __iomem *)(ptr32)\
+ - p->memtop))
+
+/******************* how to calculate the buffers *****************************
+
+ * IMPORTANT NOTE: if you configure only one NUM_XMIT_BUFFS, the driver works
+ * --------------- in a different (more stable?) mode. Only in this mode it's
+ * possible to configure the driver with 'NO_NOPCOMMANDS'
+
+sizeof(scp)=12; sizeof(scb)=16; sizeof(iscp)=8;
+sizeof(scp)+sizeof(iscp)+sizeof(scb) = 36 = INIT
+sizeof(rfd) = 24; sizeof(rbd) = 12;
+sizeof(tbd) = 8; sizeof(transmit_cmd) = 16;
+sizeof(nop_cmd) = 8;
+
+ * if you don't know the driver, better do not change these values: */
+
+#define RECV_BUFF_SIZE 1524 /* slightly oversized */
+#define XMIT_BUFF_SIZE 1524 /* slightly oversized */
+#define NUM_XMIT_BUFFS 1 /* config for both, 8K and 16K shmem */
+#define NUM_RECV_BUFFS_8 4 /* config for 8K shared mem */
+#define NUM_RECV_BUFFS_16 9 /* config for 16K shared mem */
+#define NO_NOPCOMMANDS /* only possible with NUM_XMIT_BUFFS=1 */
+
+/**************************************************************************/
+
+
+#define NI52_TOTAL_SIZE 16
+#define NI52_ADDR0 0x02
+#define NI52_ADDR1 0x07
+#define NI52_ADDR2 0x01
+
+static int ni52_probe1(struct net_device *dev, int ioaddr);
+static irqreturn_t ni52_interrupt(int irq, void *dev_id);
+static int ni52_open(struct net_device *dev);
+static int ni52_close(struct net_device *dev);
+static netdev_tx_t ni52_send_packet(struct sk_buff *, struct net_device *);
+static struct net_device_stats *ni52_get_stats(struct net_device *dev);
+static void set_multicast_list(struct net_device *dev);
+static void ni52_timeout(struct net_device *dev);
+
+/* helper-functions */
+static int init586(struct net_device *dev);
+static int check586(struct net_device *dev, unsigned size);
+static void alloc586(struct net_device *dev);
+static void startrecv586(struct net_device *dev);
+static void __iomem *alloc_rfa(struct net_device *dev, void __iomem *ptr);
+static void ni52_rcv_int(struct net_device *dev);
+static void ni52_xmt_int(struct net_device *dev);
+static void ni52_rnr_int(struct net_device *dev);
+
+struct priv {
+ char __iomem *base;
+ char __iomem *mapped;
+ char __iomem *memtop;
+ spinlock_t spinlock;
+ int reset;
+ struct rfd_struct __iomem *rfd_last, *rfd_top, *rfd_first;
+ struct scp_struct __iomem *scp;
+ struct iscp_struct __iomem *iscp;
+ struct scb_struct __iomem *scb;
+ struct tbd_struct __iomem *xmit_buffs[NUM_XMIT_BUFFS];
+#if (NUM_XMIT_BUFFS == 1)
+ struct transmit_cmd_struct __iomem *xmit_cmds[2];
+ struct nop_cmd_struct __iomem *nop_cmds[2];
+#else
+ struct transmit_cmd_struct __iomem *xmit_cmds[NUM_XMIT_BUFFS];
+ struct nop_cmd_struct __iomem *nop_cmds[NUM_XMIT_BUFFS];
+#endif
+ int nop_point, num_recv_buffs;
+ char __iomem *xmit_cbuffs[NUM_XMIT_BUFFS];
+ int xmit_count, xmit_last;
+};
+
+/* wait for command with timeout: */
+static void wait_for_scb_cmd(struct net_device *dev)
+{
+ struct priv *p = netdev_priv(dev);
+ int i;
+ for (i = 0; i < 16384; i++) {
+ if (readb(&p->scb->cmd_cuc) == 0)
+ break;
+ udelay(4);
+ if (i == 16383) {
+ printk(KERN_ERR "%s: scb_cmd timed out: %04x,%04x .. disabling i82586!!\n",
+ dev->name, readb(&p->scb->cmd_cuc), readb(&p->scb->cus));
+ if (!p->reset) {
+ p->reset = 1;
+ ni_reset586();
+ }
+ }
+ }
+}
+
+static void wait_for_scb_cmd_ruc(struct net_device *dev)
+{
+ struct priv *p = netdev_priv(dev);
+ int i;
+ for (i = 0; i < 16384; i++) {
+ if (readb(&p->scb->cmd_ruc) == 0)
+ break;
+ udelay(4);
+ if (i == 16383) {
+ printk(KERN_ERR "%s: scb_cmd (ruc) timed out: %04x,%04x .. disabling i82586!!\n",
+ dev->name, readb(&p->scb->cmd_ruc),
+ readb(&p->scb->rus));
+ if (!p->reset) {
+ p->reset = 1;
+ ni_reset586();
+ }
+ }
+ }
+}
+
+static void wait_for_stat_compl(void __iomem *p)
+{
+ struct nop_cmd_struct __iomem *addr = p;
+ int i;
+ for (i = 0; i < 32767; i++) {
+ if (readw(&((addr)->cmd_status)) & STAT_COMPL)
+ break;
+ udelay(32);
+ }
+}
+
+/**********************************************
+ * close device
+ */
+static int ni52_close(struct net_device *dev)
+{
+ free_irq(dev->irq, dev);
+ ni_reset586(); /* the hard way to stop the receiver */
+ netif_stop_queue(dev);
+ return 0;
+}
+
+/**********************************************
+ * open device
+ */
+static int ni52_open(struct net_device *dev)
+{
+ int ret;
+
+ ni_disint();
+ alloc586(dev);
+ init586(dev);
+ startrecv586(dev);
+ ni_enaint();
+
+ ret = request_irq(dev->irq, ni52_interrupt, 0, dev->name, dev);
+ if (ret) {
+ ni_reset586();
+ return ret;
+ }
+ netif_start_queue(dev);
+ return 0; /* most done by init */
+}
+
+static int check_iscp(struct net_device *dev, void __iomem *addr)
+{
+ struct iscp_struct __iomem *iscp = addr;
+ struct priv *p = netdev_priv(dev);
+ memset_io(iscp, 0, sizeof(struct iscp_struct));
+
+ writel(make24(iscp), &p->scp->iscp);
+ writeb(1, &iscp->busy);
+
+ ni_reset586();
+ ni_attn586();
+ mdelay(32); /* wait a while... */
+ /* i82586 clears 'busy' after successful init */
+ if (readb(&iscp->busy))
+ return 0;
+ return 1;
+}
+
+/**********************************************
+ * Check to see if there's an 82586 out there.
+ */
+static int check586(struct net_device *dev, unsigned size)
+{
+ struct priv *p = netdev_priv(dev);
+ int i;
+
+ p->mapped = ioremap(dev->mem_start, size);
+ if (!p->mapped)
+ return 0;
+
+ p->base = p->mapped + size - 0x01000000;
+ p->memtop = p->mapped + size;
+ p->scp = (struct scp_struct __iomem *)(p->base + SCP_DEFAULT_ADDRESS);
+ p->scb = (struct scb_struct __iomem *) p->mapped;
+ p->iscp = (struct iscp_struct __iomem *)p->scp - 1;
+ memset_io(p->scp, 0, sizeof(struct scp_struct));
+ for (i = 0; i < sizeof(struct scp_struct); i++)
+ /* memory was writeable? */
+ if (readb((char __iomem *)p->scp + i))
+ goto Enodev;
+ writeb(SYSBUSVAL, &p->scp->sysbus); /* 1 = 8Bit-Bus, 0 = 16 Bit */
+ if (readb(&p->scp->sysbus) != SYSBUSVAL)
+ goto Enodev;
+
+ if (!check_iscp(dev, p->mapped))
+ goto Enodev;
+ if (!check_iscp(dev, p->iscp))
+ goto Enodev;
+ return 1;
+Enodev:
+ iounmap(p->mapped);
+ return 0;
+}
+
+/******************************************************************
+ * set iscp at the right place, called by ni52_probe1 and open586.
+ */
+static void alloc586(struct net_device *dev)
+{
+ struct priv *p = netdev_priv(dev);
+
+ ni_reset586();
+ mdelay(32);
+
+ memset_io(p->iscp, 0, sizeof(struct iscp_struct));
+ memset_io(p->scp , 0, sizeof(struct scp_struct));
+
+ writel(make24(p->iscp), &p->scp->iscp);
+ writeb(SYSBUSVAL, &p->scp->sysbus);
+ writew(make16(p->scb), &p->iscp->scb_offset);
+
+ writeb(1, &p->iscp->busy);
+ ni_reset586();
+ ni_attn586();
+
+ mdelay(32);
+
+ if (readb(&p->iscp->busy))
+ printk(KERN_ERR "%s: Init-Problems (alloc).\n", dev->name);
+
+ p->reset = 0;
+
+ memset_io(p->scb, 0, sizeof(struct scb_struct));
+}
+
+/* set: io,irq,memstart,memend or set it when calling insmod */
+static int irq = 9;
+static int io = 0x300;
+static long memstart; /* e.g 0xd0000 */
+static long memend; /* e.g 0xd4000 */
+
+/**********************************************
+ * probe the ni5210-card
+ */
+struct net_device * __init ni52_probe(int unit)
+{
+ struct net_device *dev = alloc_etherdev(sizeof(struct priv));
+ static const int ports[] = {0x300, 0x280, 0x360, 0x320, 0x340, 0};
+ const int *port;
+ struct priv *p;
+ int err = 0;
+
+ if (!dev)
+ return ERR_PTR(-ENOMEM);
+
+ p = netdev_priv(dev);
+
+ if (unit >= 0) {
+ sprintf(dev->name, "eth%d", unit);
+ netdev_boot_setup_check(dev);
+ io = dev->base_addr;
+ irq = dev->irq;
+ memstart = dev->mem_start;
+ memend = dev->mem_end;
+ }
+
+ if (io > 0x1ff) { /* Check a single specified location. */
+ err = ni52_probe1(dev, io);
+ } else if (io > 0) { /* Don't probe at all. */
+ err = -ENXIO;
+ } else {
+ for (port = ports; *port && ni52_probe1(dev, *port) ; port++)
+ ;
+ if (*port)
+ goto got_it;
+#ifdef FULL_IO_PROBE
+ for (io = 0x200; io < 0x400 && ni52_probe1(dev, io); io += 8)
+ ;
+ if (io < 0x400)
+ goto got_it;
+#endif
+ err = -ENODEV;
+ }
+ if (err)
+ goto out;
+got_it:
+ err = register_netdev(dev);
+ if (err)
+ goto out1;
+ return dev;
+out1:
+ iounmap(p->mapped);
+ release_region(dev->base_addr, NI52_TOTAL_SIZE);
+out:
+ free_netdev(dev);
+ return ERR_PTR(err);
+}
+
+static const struct net_device_ops ni52_netdev_ops = {
+ .ndo_open = ni52_open,
+ .ndo_stop = ni52_close,
+ .ndo_get_stats = ni52_get_stats,
+ .ndo_tx_timeout = ni52_timeout,
+ .ndo_start_xmit = ni52_send_packet,
+ .ndo_set_rx_mode = set_multicast_list,
+ .ndo_change_mtu = eth_change_mtu,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_validate_addr = eth_validate_addr,
+};
+
+static int __init ni52_probe1(struct net_device *dev, int ioaddr)
+{
+ int i, size, retval;
+ struct priv *priv = netdev_priv(dev);
+
+ dev->base_addr = ioaddr;
+ dev->irq = irq;
+ dev->mem_start = memstart;
+ dev->mem_end = memend;
+
+ spin_lock_init(&priv->spinlock);
+
+ if (!request_region(ioaddr, NI52_TOTAL_SIZE, DRV_NAME))
+ return -EBUSY;
+
+ if (!(inb(ioaddr+NI52_MAGIC1) == NI52_MAGICVAL1) ||
+ !(inb(ioaddr+NI52_MAGIC2) == NI52_MAGICVAL2)) {
+ retval = -ENODEV;
+ goto out;
+ }
+
+ for (i = 0; i < ETH_ALEN; i++)
+ dev->dev_addr[i] = inb(dev->base_addr+i);
+
+ if (dev->dev_addr[0] != NI52_ADDR0 || dev->dev_addr[1] != NI52_ADDR1 ||
+ dev->dev_addr[2] != NI52_ADDR2) {
+ retval = -ENODEV;
+ goto out;
+ }
+
+ printk(KERN_INFO "%s: NI5210 found at %#3lx, ",
+ dev->name, dev->base_addr);
+
+ /*
+ * check (or search) IO-Memory, 8K and 16K
+ */
+#ifdef MODULE
+ size = dev->mem_end - dev->mem_start;
+ if (size != 0x2000 && size != 0x4000) {
+ printk("\n");
+ printk(KERN_ERR "%s: Invalid memory size %d. Allowed is 0x2000 or 0x4000 bytes.\n", dev->name, size);
+ retval = -ENODEV;
+ goto out;
+ }
+ if (!check586(dev, size)) {
+ printk(KERN_ERR "?memcheck, Can't find memory at 0x%lx with size %d!\n", dev->mem_start, size);
+ retval = -ENODEV;
+ goto out;
+ }
+#else
+ if (dev->mem_start != 0) {
+ /* no auto-mem-probe */
+ size = 0x4000; /* check for 16K mem */
+ if (!check586(dev, size)) {
+ size = 0x2000; /* check for 8K mem */
+ if (!check586(dev, size)) {
+ printk(KERN_ERR "?memprobe, Can't find memory at 0x%lx!\n", dev->mem_start);
+ retval = -ENODEV;
+ goto out;
+ }
+ }
+ } else {
+ static const unsigned long memaddrs[] = {
+ 0xc8000, 0xca000, 0xcc000, 0xce000, 0xd0000, 0xd2000,
+ 0xd4000, 0xd6000, 0xd8000, 0xda000, 0xdc000, 0
+ };
+ for (i = 0;; i++) {
+ if (!memaddrs[i]) {
+ printk(KERN_ERR "?memprobe, Can't find io-memory!\n");
+ retval = -ENODEV;
+ goto out;
+ }
+ dev->mem_start = memaddrs[i];
+ size = 0x2000; /* check for 8K mem */
+ if (check586(dev, size))
+ /* 8K-check */
+ break;
+ size = 0x4000; /* check for 16K mem */
+ if (check586(dev, size))
+ /* 16K-check */
+ break;
+ }
+ }
+ /* set mem_end showed by 'ifconfig' */
+ dev->mem_end = dev->mem_start + size;
+#endif
+
+ alloc586(dev);
+
+ /* set number of receive-buffs according to memsize */
+ if (size == 0x2000)
+ priv->num_recv_buffs = NUM_RECV_BUFFS_8;
+ else
+ priv->num_recv_buffs = NUM_RECV_BUFFS_16;
+
+ printk(KERN_DEBUG "Memaddr: 0x%lx, Memsize: %d, ",
+ dev->mem_start, size);
+
+ if (dev->irq < 2) {
+ unsigned long irq_mask;
+
+ irq_mask = probe_irq_on();
+ ni_reset586();
+ ni_attn586();
+
+ mdelay(20);
+ dev->irq = probe_irq_off(irq_mask);
+ if (!dev->irq) {
+ printk("?autoirq, Failed to detect IRQ line!\n");
+ retval = -EAGAIN;
+ iounmap(priv->mapped);
+ goto out;
+ }
+ printk("IRQ %d (autodetected).\n", dev->irq);
+ } else {
+ if (dev->irq == 2)
+ dev->irq = 9;
+ printk("IRQ %d (assigned and not checked!).\n", dev->irq);
+ }
+
+ dev->netdev_ops = &ni52_netdev_ops;
+ dev->watchdog_timeo = HZ/20;
+
+ return 0;
+out:
+ release_region(ioaddr, NI52_TOTAL_SIZE);
+ return retval;
+}
+
+/**********************************************
+ * init the chip (ni52-interrupt should be disabled?!)
+ * needs a correct 'allocated' memory
+ */
+
+static int init586(struct net_device *dev)
+{
+ void __iomem *ptr;
+ int i, result = 0;
+ struct priv *p = netdev_priv(dev);
+ struct configure_cmd_struct __iomem *cfg_cmd;
+ struct iasetup_cmd_struct __iomem *ias_cmd;
+ struct tdr_cmd_struct __iomem *tdr_cmd;
+ struct mcsetup_cmd_struct __iomem *mc_cmd;
+ struct netdev_hw_addr *ha;
+ int num_addrs = netdev_mc_count(dev);
+
+ ptr = p->scb + 1;
+
+ cfg_cmd = ptr; /* configure-command */
+ writew(0, &cfg_cmd->cmd_status);
+ writew(CMD_CONFIGURE | CMD_LAST, &cfg_cmd->cmd_cmd);
+ writew(0xFFFF, &cfg_cmd->cmd_link);
+
+ /* number of cfg bytes */
+ writeb(0x0a, &cfg_cmd->byte_cnt);
+ /* fifo-limit (8=tx:32/rx:64) */
+ writeb(fifo, &cfg_cmd->fifo);
+ /* hold or discard bad recv frames (bit 7) */
+ writeb(0x40, &cfg_cmd->sav_bf);
+ /* addr_len |!src_insert |pre-len |loopback */
+ writeb(0x2e, &cfg_cmd->adr_len);
+ writeb(0x00, &cfg_cmd->priority);
+ writeb(0x60, &cfg_cmd->ifs);
+ writeb(0x00, &cfg_cmd->time_low);
+ writeb(0xf2, &cfg_cmd->time_high);
+ writeb(0x00, &cfg_cmd->promisc);
+ if (dev->flags & IFF_ALLMULTI) {
+ int len = ((char __iomem *)p->iscp - (char __iomem *)ptr - 8) / 6;
+ if (num_addrs > len) {
+ printk(KERN_ERR "%s: switching to promisc. mode\n",
+ dev->name);
+ writeb(0x01, &cfg_cmd->promisc);
+ }
+ }
+ if (dev->flags & IFF_PROMISC)
+ writeb(0x01, &cfg_cmd->promisc);
+ writeb(0x00, &cfg_cmd->carr_coll);
+ writew(make16(cfg_cmd), &p->scb->cbl_offset);
+ writeb(0, &p->scb->cmd_ruc);
+
+ writeb(CUC_START, &p->scb->cmd_cuc); /* cmd.-unit start */
+ ni_attn586();
+
+ wait_for_stat_compl(cfg_cmd);
+
+ if ((readw(&cfg_cmd->cmd_status) & (STAT_OK|STAT_COMPL)) !=
+ (STAT_COMPL|STAT_OK)) {
+ printk(KERN_ERR "%s: configure command failed: %x\n",
+ dev->name, readw(&cfg_cmd->cmd_status));
+ return 1;
+ }
+
+ /*
+ * individual address setup
+ */
+
+ ias_cmd = ptr;
+
+ writew(0, &ias_cmd->cmd_status);
+ writew(CMD_IASETUP | CMD_LAST, &ias_cmd->cmd_cmd);
+ writew(0xffff, &ias_cmd->cmd_link);
+
+ memcpy_toio(&ias_cmd->iaddr, (char *)dev->dev_addr, ETH_ALEN);
+
+ writew(make16(ias_cmd), &p->scb->cbl_offset);
+
+ writeb(CUC_START, &p->scb->cmd_cuc); /* cmd.-unit start */
+ ni_attn586();
+
+ wait_for_stat_compl(ias_cmd);
+
+ if ((readw(&ias_cmd->cmd_status) & (STAT_OK|STAT_COMPL)) !=
+ (STAT_OK|STAT_COMPL)) {
+ printk(KERN_ERR "%s (ni52): individual address setup command failed: %04x\n", dev->name, readw(&ias_cmd->cmd_status));
+ return 1;
+ }
+
+ /*
+ * TDR, wire check .. e.g. no resistor e.t.c
+ */
+
+ tdr_cmd = ptr;
+
+ writew(0, &tdr_cmd->cmd_status);
+ writew(CMD_TDR | CMD_LAST, &tdr_cmd->cmd_cmd);
+ writew(0xffff, &tdr_cmd->cmd_link);
+ writew(0, &tdr_cmd->status);
+
+ writew(make16(tdr_cmd), &p->scb->cbl_offset);
+ writeb(CUC_START, &p->scb->cmd_cuc); /* cmd.-unit start */
+ ni_attn586();
+
+ wait_for_stat_compl(tdr_cmd);
+
+ if (!(readw(&tdr_cmd->cmd_status) & STAT_COMPL))
+ printk(KERN_ERR "%s: Problems while running the TDR.\n",
+ dev->name);
+ else {
+ udelay(16);
+ result = readw(&tdr_cmd->status);
+ writeb(readb(&p->scb->cus) & STAT_MASK, &p->scb->cmd_cuc);
+ ni_attn586(); /* ack the interrupts */
+
+ if (result & TDR_LNK_OK)
+ ;
+ else if (result & TDR_XCVR_PRB)
+ printk(KERN_ERR "%s: TDR: Transceiver problem. Check the cable(s)!\n",
+ dev->name);
+ else if (result & TDR_ET_OPN)
+ printk(KERN_ERR "%s: TDR: No correct termination %d clocks away.\n",
+ dev->name, result & TDR_TIMEMASK);
+ else if (result & TDR_ET_SRT) {
+ /* time == 0 -> strange :-) */
+ if (result & TDR_TIMEMASK)
+ printk(KERN_ERR "%s: TDR: Detected a short circuit %d clocks away.\n",
+ dev->name, result & TDR_TIMEMASK);
+ } else
+ printk(KERN_ERR "%s: TDR: Unknown status %04x\n",
+ dev->name, result);
+ }
+
+ /*
+ * Multicast setup
+ */
+ if (num_addrs && !(dev->flags & IFF_PROMISC)) {
+ mc_cmd = ptr;
+ writew(0, &mc_cmd->cmd_status);
+ writew(CMD_MCSETUP | CMD_LAST, &mc_cmd->cmd_cmd);
+ writew(0xffff, &mc_cmd->cmd_link);
+ writew(num_addrs * 6, &mc_cmd->mc_cnt);
+
+ i = 0;
+ netdev_for_each_mc_addr(ha, dev)
+ memcpy_toio(mc_cmd->mc_list[i++], ha->addr, 6);
+
+ writew(make16(mc_cmd), &p->scb->cbl_offset);
+ writeb(CUC_START, &p->scb->cmd_cuc);
+ ni_attn586();
+
+ wait_for_stat_compl(mc_cmd);
+
+ if ((readw(&mc_cmd->cmd_status) & (STAT_COMPL|STAT_OK))
+ != (STAT_COMPL|STAT_OK))
+ printk(KERN_ERR "%s: Can't apply multicast-address-list.\n", dev->name);
+ }
+
+ /*
+ * alloc nop/xmit-cmds
+ */
+#if (NUM_XMIT_BUFFS == 1)
+ for (i = 0; i < 2; i++) {
+ p->nop_cmds[i] = ptr;
+ writew(CMD_NOP, &p->nop_cmds[i]->cmd_cmd);
+ writew(0, &p->nop_cmds[i]->cmd_status);
+ writew(make16(p->nop_cmds[i]), &p->nop_cmds[i]->cmd_link);
+ ptr = ptr + sizeof(struct nop_cmd_struct);
+ }
+#else
+ for (i = 0; i < NUM_XMIT_BUFFS; i++) {
+ p->nop_cmds[i] = ptr;
+ writew(CMD_NOP, &p->nop_cmds[i]->cmd_cmd);
+ writew(0, &p->nop_cmds[i]->cmd_status);
+ writew(make16(p->nop_cmds[i]), &p->nop_cmds[i]->cmd_link);
+ ptr = ptr + sizeof(struct nop_cmd_struct);
+ }
+#endif
+
+ ptr = alloc_rfa(dev, ptr); /* init receive-frame-area */
+
+ /*
+ * alloc xmit-buffs / init xmit_cmds
+ */
+ for (i = 0; i < NUM_XMIT_BUFFS; i++) {
+ /* Transmit cmd/buff 0 */
+ p->xmit_cmds[i] = ptr;
+ ptr = ptr + sizeof(struct transmit_cmd_struct);
+ p->xmit_cbuffs[i] = ptr; /* char-buffs */
+ ptr = ptr + XMIT_BUFF_SIZE;
+ p->xmit_buffs[i] = ptr; /* TBD */
+ ptr = ptr + sizeof(struct tbd_struct);
+ if ((void __iomem *)ptr > (void __iomem *)p->iscp) {
+ printk(KERN_ERR "%s: not enough shared-mem for your configuration!\n",
+ dev->name);
+ return 1;
+ }
+ memset_io(p->xmit_cmds[i], 0,
+ sizeof(struct transmit_cmd_struct));
+ memset_io(p->xmit_buffs[i], 0,
+ sizeof(struct tbd_struct));
+ writew(make16(p->nop_cmds[(i+1)%NUM_XMIT_BUFFS]),
+ &p->xmit_cmds[i]->cmd_link);
+ writew(STAT_COMPL, &p->xmit_cmds[i]->cmd_status);
+ writew(CMD_XMIT|CMD_INT, &p->xmit_cmds[i]->cmd_cmd);
+ writew(make16(p->xmit_buffs[i]), &p->xmit_cmds[i]->tbd_offset);
+ writew(0xffff, &p->xmit_buffs[i]->next);
+ writel(make24(p->xmit_cbuffs[i]), &p->xmit_buffs[i]->buffer);
+ }
+
+ p->xmit_count = 0;
+ p->xmit_last = 0;
+#ifndef NO_NOPCOMMANDS
+ p->nop_point = 0;
+#endif
+
+ /*
+ * 'start transmitter'
+ */
+#ifndef NO_NOPCOMMANDS
+ writew(make16(p->nop_cmds[0]), &p->scb->cbl_offset);
+ writeb(CUC_START, &p->scb->cmd_cuc);
+ ni_attn586();
+ wait_for_scb_cmd(dev);
+#else
+ writew(make16(p->xmit_cmds[0]), &p->xmit_cmds[0]->cmd_link);
+ writew(CMD_XMIT | CMD_SUSPEND | CMD_INT, &p->xmit_cmds[0]->cmd_cmd);
+#endif
+
+ /*
+ * ack. interrupts
+ */
+ writeb(readb(&p->scb->cus) & STAT_MASK, &p->scb->cmd_cuc);
+ ni_attn586();
+ udelay(16);
+
+ ni_enaint();
+
+ return 0;
+}
+
+/******************************************************
+ * This is a helper routine for ni52_rnr_int() and init586().
+ * It sets up the Receive Frame Area (RFA).
+ */
+
+static void __iomem *alloc_rfa(struct net_device *dev, void __iomem *ptr)
+{
+ struct rfd_struct __iomem *rfd = ptr;
+ struct rbd_struct __iomem *rbd;
+ int i;
+ struct priv *p = netdev_priv(dev);
+
+ memset_io(rfd, 0,
+ sizeof(struct rfd_struct) * (p->num_recv_buffs + rfdadd));
+ p->rfd_first = rfd;
+
+ for (i = 0; i < (p->num_recv_buffs + rfdadd); i++) {
+ writew(make16(rfd + (i+1) % (p->num_recv_buffs+rfdadd)),
+ &rfd[i].next);
+ writew(0xffff, &rfd[i].rbd_offset);
+ }
+ /* RU suspend */
+ writeb(RFD_SUSP, &rfd[p->num_recv_buffs-1+rfdadd].last);
+
+ ptr = rfd + (p->num_recv_buffs + rfdadd);
+
+ rbd = ptr;
+ ptr = rbd + p->num_recv_buffs;
+
+ /* clr descriptors */
+ memset_io(rbd, 0, sizeof(struct rbd_struct) * (p->num_recv_buffs));
+
+ for (i = 0; i < p->num_recv_buffs; i++) {
+ writew(make16(rbd + (i+1) % p->num_recv_buffs), &rbd[i].next);
+ writew(RECV_BUFF_SIZE, &rbd[i].size);
+ writel(make24(ptr), &rbd[i].buffer);
+ ptr = ptr + RECV_BUFF_SIZE;
+ }
+ p->rfd_top = p->rfd_first;
+ p->rfd_last = p->rfd_first + (p->num_recv_buffs - 1 + rfdadd);
+
+ writew(make16(p->rfd_first), &p->scb->rfa_offset);
+ writew(make16(rbd), &p->rfd_first->rbd_offset);
+
+ return ptr;
+}
+
+
+/**************************************************
+ * Interrupt Handler ...
+ */
+
+static irqreturn_t ni52_interrupt(int irq, void *dev_id)
+{
+ struct net_device *dev = dev_id;
+ unsigned int stat;
+ int cnt = 0;
+ struct priv *p;
+
+ p = netdev_priv(dev);
+
+ if (debuglevel > 1)
+ printk("I");
+
+ spin_lock(&p->spinlock);
+
+ wait_for_scb_cmd(dev); /* wait for last command */
+
+ while ((stat = readb(&p->scb->cus) & STAT_MASK)) {
+ writeb(stat, &p->scb->cmd_cuc);
+ ni_attn586();
+
+ if (stat & STAT_FR) /* received a frame */
+ ni52_rcv_int(dev);
+
+ if (stat & STAT_RNR) { /* RU went 'not ready' */
+ printk("(R)");
+ if (readb(&p->scb->rus) & RU_SUSPEND) {
+ /* special case: RU_SUSPEND */
+ wait_for_scb_cmd(dev);
+ writeb(RUC_RESUME, &p->scb->cmd_ruc);
+ ni_attn586();
+ wait_for_scb_cmd_ruc(dev);
+ } else {
+ printk(KERN_ERR "%s: Receiver-Unit went 'NOT READY': %04x/%02x.\n",
+ dev->name, stat, readb(&p->scb->rus));
+ ni52_rnr_int(dev);
+ }
+ }
+
+ /* Command with I-bit set complete */
+ if (stat & STAT_CX)
+ ni52_xmt_int(dev);
+
+#ifndef NO_NOPCOMMANDS
+ if (stat & STAT_CNA) { /* CU went 'not ready' */
+ if (netif_running(dev))
+ printk(KERN_ERR "%s: oops! CU has left active state. stat: %04x/%02x.\n",
+ dev->name, stat, readb(&p->scb->cus));
+ }
+#endif
+
+ if (debuglevel > 1)
+ printk("%d", cnt++);
+
+ /* Wait for ack. (ni52_xmt_int can be faster than ack!!) */
+ wait_for_scb_cmd(dev);
+ if (readb(&p->scb->cmd_cuc)) { /* timed out? */
+ printk(KERN_ERR "%s: Acknowledge timed out.\n",
+ dev->name);
+ ni_disint();
+ break;
+ }
+ }
+ spin_unlock(&p->spinlock);
+
+ if (debuglevel > 1)
+ printk("i");
+ return IRQ_HANDLED;
+}
+
+/*******************************************************
+ * receive-interrupt
+ */
+
+static void ni52_rcv_int(struct net_device *dev)
+{
+ int status, cnt = 0;
+ unsigned short totlen;
+ struct sk_buff *skb;
+ struct rbd_struct __iomem *rbd;
+ struct priv *p = netdev_priv(dev);
+
+ if (debuglevel > 0)
+ printk("R");
+
+ for (; (status = readb(&p->rfd_top->stat_high)) & RFD_COMPL;) {
+ rbd = make32(readw(&p->rfd_top->rbd_offset));
+ if (status & RFD_OK) { /* frame received without error? */
+ totlen = readw(&rbd->status);
+ if (totlen & RBD_LAST) {
+ /* the first and the last buffer? */
+ totlen &= RBD_MASK; /* length of this frame */
+ writew(0x00, &rbd->status);
+ skb = (struct sk_buff *)dev_alloc_skb(totlen+2);
+ if (skb != NULL) {
+ skb_reserve(skb, 2);
+ skb_put(skb, totlen);
+ memcpy_fromio(skb->data, p->base + readl(&rbd->buffer), totlen);
+ skb->protocol = eth_type_trans(skb, dev);
+ netif_rx(skb);
+ dev->stats.rx_packets++;
+ dev->stats.rx_bytes += totlen;
+ } else
+ dev->stats.rx_dropped++;
+ } else {
+ int rstat;
+ /* free all RBD's until RBD_LAST is set */
+ totlen = 0;
+ while (!((rstat = readw(&rbd->status)) & RBD_LAST)) {
+ totlen += rstat & RBD_MASK;
+ if (!rstat) {
+ printk(KERN_ERR "%s: Whoops .. no end mark in RBD list\n", dev->name);
+ break;
+ }
+ writew(0, &rbd->status);
+ rbd = make32(readw(&rbd->next));
+ }
+ totlen += rstat & RBD_MASK;
+ writew(0, &rbd->status);
+ printk(KERN_ERR "%s: received oversized frame! length: %d\n",
+ dev->name, totlen);
+ dev->stats.rx_dropped++;
+ }
+ } else {/* frame !(ok), only with 'save-bad-frames' */
+ printk(KERN_ERR "%s: oops! rfd-error-status: %04x\n",
+ dev->name, status);
+ dev->stats.rx_errors++;
+ }
+ writeb(0, &p->rfd_top->stat_high);
+ writeb(RFD_SUSP, &p->rfd_top->last); /* maybe exchange by RFD_LAST */
+ writew(0xffff, &p->rfd_top->rbd_offset);
+ writeb(0, &p->rfd_last->last); /* delete RFD_SUSP */
+ p->rfd_last = p->rfd_top;
+ p->rfd_top = make32(readw(&p->rfd_top->next)); /* step to next RFD */
+ writew(make16(p->rfd_top), &p->scb->rfa_offset);
+
+ if (debuglevel > 0)
+ printk("%d", cnt++);
+ }
+
+ if (automatic_resume) {
+ wait_for_scb_cmd(dev);
+ writeb(RUC_RESUME, &p->scb->cmd_ruc);
+ ni_attn586();
+ wait_for_scb_cmd_ruc(dev);
+ }
+
+#ifdef WAIT_4_BUSY
+ {
+ int i;
+ for (i = 0; i < 1024; i++) {
+ if (p->rfd_top->status)
+ break;
+ udelay(16);
+ if (i == 1023)
+ printk(KERN_ERR "%s: RU hasn't fetched next RFD (not busy/complete)\n", dev->name);
+ }
+ }
+#endif
+ if (debuglevel > 0)
+ printk("r");
+}
+
+/**********************************************************
+ * handle 'Receiver went not ready'.
+ */
+
+static void ni52_rnr_int(struct net_device *dev)
+{
+ struct priv *p = netdev_priv(dev);
+
+ dev->stats.rx_errors++;
+
+ wait_for_scb_cmd(dev); /* wait for the last cmd, WAIT_4_FULLSTAT?? */
+ writeb(RUC_ABORT, &p->scb->cmd_ruc); /* usually the RU is in the 'no resource'-state .. abort it now. */
+ ni_attn586();
+ wait_for_scb_cmd_ruc(dev); /* wait for accept cmd. */
+
+ alloc_rfa(dev, p->rfd_first);
+ /* maybe add a check here, before restarting the RU */
+ startrecv586(dev); /* restart RU */
+
+ printk(KERN_ERR "%s: Receive-Unit restarted. Status: %04x\n",
+ dev->name, readb(&p->scb->rus));
+
+}
+
+/**********************************************************
+ * handle xmit - interrupt
+ */
+
+static void ni52_xmt_int(struct net_device *dev)
+{
+ int status;
+ struct priv *p = netdev_priv(dev);
+
+ if (debuglevel > 0)
+ printk("X");
+
+ status = readw(&p->xmit_cmds[p->xmit_last]->cmd_status);
+ if (!(status & STAT_COMPL))
+ printk(KERN_ERR "%s: strange .. xmit-int without a 'COMPLETE'\n", dev->name);
+
+ if (status & STAT_OK) {
+ dev->stats.tx_packets++;
+ dev->stats.collisions += (status & TCMD_MAXCOLLMASK);
+ } else {
+ dev->stats.tx_errors++;
+ if (status & TCMD_LATECOLL) {
+ printk(KERN_ERR "%s: late collision detected.\n",
+ dev->name);
+ dev->stats.collisions++;
+ } else if (status & TCMD_NOCARRIER) {
+ dev->stats.tx_carrier_errors++;
+ printk(KERN_ERR "%s: no carrier detected.\n",
+ dev->name);
+ } else if (status & TCMD_LOSTCTS)
+ printk(KERN_ERR "%s: loss of CTS detected.\n",
+ dev->name);
+ else if (status & TCMD_UNDERRUN) {
+ dev->stats.tx_fifo_errors++;
+ printk(KERN_ERR "%s: DMA underrun detected.\n",
+ dev->name);
+ } else if (status & TCMD_MAXCOLL) {
+ printk(KERN_ERR "%s: Max. collisions exceeded.\n",
+ dev->name);
+ dev->stats.collisions += 16;
+ }
+ }
+#if (NUM_XMIT_BUFFS > 1)
+ if ((++p->xmit_last) == NUM_XMIT_BUFFS)
+ p->xmit_last = 0;
+#endif
+ netif_wake_queue(dev);
+}
+
+/***********************************************************
+ * (re)start the receiver
+ */
+
+static void startrecv586(struct net_device *dev)
+{
+ struct priv *p = netdev_priv(dev);
+
+ wait_for_scb_cmd(dev);
+ wait_for_scb_cmd_ruc(dev);
+ writew(make16(p->rfd_first), &p->scb->rfa_offset);
+ writeb(RUC_START, &p->scb->cmd_ruc);
+ ni_attn586(); /* start cmd. */
+ wait_for_scb_cmd_ruc(dev);
+ /* wait for accept cmd. (no timeout!!) */
+}
+
+static void ni52_timeout(struct net_device *dev)
+{
+ struct priv *p = netdev_priv(dev);
+#ifndef NO_NOPCOMMANDS
+ if (readb(&p->scb->cus) & CU_ACTIVE) { /* COMMAND-UNIT active? */
+ netif_wake_queue(dev);
+#ifdef DEBUG
+ printk(KERN_ERR "%s: strange ... timeout with CU active?!?\n",
+ dev->name);
+ printk(KERN_ERR "%s: X0: %04x N0: %04x N1: %04x %d\n",
+ dev->name, (int)p->xmit_cmds[0]->cmd_status,
+ readw(&p->nop_cmds[0]->cmd_status),
+ readw(&p->nop_cmds[1]->cmd_status),
+ p->nop_point);
+#endif
+ writeb(CUC_ABORT, &p->scb->cmd_cuc);
+ ni_attn586();
+ wait_for_scb_cmd(dev);
+ writew(make16(p->nop_cmds[p->nop_point]), &p->scb->cbl_offset);
+ writeb(CUC_START, &p->scb->cmd_cuc);
+ ni_attn586();
+ wait_for_scb_cmd(dev);
+ dev->trans_start = jiffies; /* prevent tx timeout */
+ return 0;
+ }
+#endif
+ {
+#ifdef DEBUG
+ printk(KERN_ERR "%s: xmitter timed out, try to restart! stat: %02x\n",
+ dev->name, readb(&p->scb->cus));
+ printk(KERN_ERR "%s: command-stats: %04x %04x\n",
+ dev->name,
+ readw(&p->xmit_cmds[0]->cmd_status),
+ readw(&p->xmit_cmds[1]->cmd_status));
+ printk(KERN_ERR "%s: check, whether you set the right interrupt number!\n",
+ dev->name);
+#endif
+ ni52_close(dev);
+ ni52_open(dev);
+ }
+ dev->trans_start = jiffies; /* prevent tx timeout */
+}
+
+/******************************************************
+ * send frame
+ */
+
+static netdev_tx_t ni52_send_packet(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ int len, i;
+#ifndef NO_NOPCOMMANDS
+ int next_nop;
+#endif
+ struct priv *p = netdev_priv(dev);
+
+ if (skb->len > XMIT_BUFF_SIZE) {
+ printk(KERN_ERR "%s: Sorry, max. framelength is %d bytes. The length of your frame is %d bytes.\n", dev->name, XMIT_BUFF_SIZE, skb->len);
+ return NETDEV_TX_OK;
+ }
+
+ netif_stop_queue(dev);
+
+ memcpy_toio(p->xmit_cbuffs[p->xmit_count], skb->data, skb->len);
+ len = skb->len;
+ if (len < ETH_ZLEN) {
+ len = ETH_ZLEN;
+ memset_io(p->xmit_cbuffs[p->xmit_count]+skb->len, 0,
+ len - skb->len);
+ }
+
+#if (NUM_XMIT_BUFFS == 1)
+# ifdef NO_NOPCOMMANDS
+
+#ifdef DEBUG
+ if (readb(&p->scb->cus) & CU_ACTIVE) {
+ printk(KERN_ERR "%s: Hmmm .. CU is still running and we wanna send a new packet.\n", dev->name);
+ printk(KERN_ERR "%s: stat: %04x %04x\n",
+ dev->name, readb(&p->scb->cus),
+ readw(&p->xmit_cmds[0]->cmd_status));
+ }
+#endif
+ writew(TBD_LAST | len, &p->xmit_buffs[0]->size);
+ for (i = 0; i < 16; i++) {
+ writew(0, &p->xmit_cmds[0]->cmd_status);
+ wait_for_scb_cmd(dev);
+ if ((readb(&p->scb->cus) & CU_STATUS) == CU_SUSPEND)
+ writeb(CUC_RESUME, &p->scb->cmd_cuc);
+ else {
+ writew(make16(p->xmit_cmds[0]), &p->scb->cbl_offset);
+ writeb(CUC_START, &p->scb->cmd_cuc);
+ }
+ ni_attn586();
+ if (!i)
+ dev_kfree_skb(skb);
+ wait_for_scb_cmd(dev);
+ /* test it, because CU sometimes doesn't start immediately */
+ if (readb(&p->scb->cus) & CU_ACTIVE)
+ break;
+ if (readw(&p->xmit_cmds[0]->cmd_status))
+ break;
+ if (i == 15)
+ printk(KERN_WARNING "%s: Can't start transmit-command.\n", dev->name);
+ }
+# else
+ next_nop = (p->nop_point + 1) & 0x1;
+ writew(TBD_LAST | len, &p->xmit_buffs[0]->size);
+ writew(make16(p->nop_cmds[next_nop]), &p->xmit_cmds[0]->cmd_link);
+ writew(make16(p->nop_cmds[next_nop]),
+ &p->nop_cmds[next_nop]->cmd_link);
+ writew(0, &p->xmit_cmds[0]->cmd_status);
+ writew(0, &p->nop_cmds[next_nop]->cmd_status);
+
+ writew(make16(p->xmit_cmds[0]), &p->nop_cmds[p->nop_point]->cmd_link);
+ p->nop_point = next_nop;
+ dev_kfree_skb(skb);
+# endif
+#else
+ writew(TBD_LAST | len, &p->xmit_buffs[p->xmit_count]->size);
+ next_nop = p->xmit_count + 1
+ if (next_nop == NUM_XMIT_BUFFS)
+ next_nop = 0;
+ writew(0, &p->xmit_cmds[p->xmit_count]->cmd_status);
+ /* linkpointer of xmit-command already points to next nop cmd */
+ writew(make16(p->nop_cmds[next_nop]),
+ &p->nop_cmds[next_nop]->cmd_link);
+ writew(0, &p->nop_cmds[next_nop]->cmd_status);
+ writew(make16(p->xmit_cmds[p->xmit_count]),
+ &p->nop_cmds[p->xmit_count]->cmd_link);
+ p->xmit_count = next_nop;
+ {
+ unsigned long flags;
+ spin_lock_irqsave(&p->spinlock);
+ if (p->xmit_count != p->xmit_last)
+ netif_wake_queue(dev);
+ spin_unlock_irqrestore(&p->spinlock);
+ }
+ dev_kfree_skb(skb);
+#endif
+ return NETDEV_TX_OK;
+}
+
+/*******************************************
+ * Someone wanna have the statistics
+ */
+
+static struct net_device_stats *ni52_get_stats(struct net_device *dev)
+{
+ struct priv *p = netdev_priv(dev);
+ unsigned short crc, aln, rsc, ovrn;
+
+ /* Get error-statistics from the ni82586 */
+ crc = readw(&p->scb->crc_errs);
+ writew(0, &p->scb->crc_errs);
+ aln = readw(&p->scb->aln_errs);
+ writew(0, &p->scb->aln_errs);
+ rsc = readw(&p->scb->rsc_errs);
+ writew(0, &p->scb->rsc_errs);
+ ovrn = readw(&p->scb->ovrn_errs);
+ writew(0, &p->scb->ovrn_errs);
+
+ dev->stats.rx_crc_errors += crc;
+ dev->stats.rx_fifo_errors += ovrn;
+ dev->stats.rx_frame_errors += aln;
+ dev->stats.rx_dropped += rsc;
+
+ return &dev->stats;
+}
+
+/********************************************************
+ * Set MC list ..
+ */
+
+static void set_multicast_list(struct net_device *dev)
+{
+ netif_stop_queue(dev);
+ ni_disint();
+ alloc586(dev);
+ init586(dev);
+ startrecv586(dev);
+ ni_enaint();
+ netif_wake_queue(dev);
+}
+
+#ifdef MODULE
+static struct net_device *dev_ni52;
+
+module_param(io, int, 0);
+module_param(irq, int, 0);
+module_param(memstart, long, 0);
+module_param(memend, long, 0);
+MODULE_PARM_DESC(io, "NI5210 I/O base address,required");
+MODULE_PARM_DESC(irq, "NI5210 IRQ number,required");
+MODULE_PARM_DESC(memstart, "NI5210 memory base address,required");
+MODULE_PARM_DESC(memend, "NI5210 memory end address,required");
+
+int __init init_module(void)
+{
+ if (io <= 0x0 || !memend || !memstart || irq < 2) {
+ printk(KERN_ERR "ni52: Autoprobing not allowed for modules.\n");
+ printk(KERN_ERR "ni52: Set symbols 'io' 'irq' 'memstart' and 'memend'\n");
+ return -ENODEV;
+ }
+ dev_ni52 = ni52_probe(-1);
+ if (IS_ERR(dev_ni52))
+ return PTR_ERR(dev_ni52);
+ return 0;
+}
+
+void __exit cleanup_module(void)
+{
+ struct priv *p = netdev_priv(dev_ni52);
+ unregister_netdev(dev_ni52);
+ iounmap(p->mapped);
+ release_region(dev_ni52->base_addr, NI52_TOTAL_SIZE);
+ free_netdev(dev_ni52);
+}
+#endif /* MODULE */
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/ethernet/i825xx/ni52.h b/drivers/net/ethernet/i825xx/ni52.h
new file mode 100644
index 00000000000..0a03b288332
--- /dev/null
+++ b/drivers/net/ethernet/i825xx/ni52.h
@@ -0,0 +1,310 @@
+/*
+ * Intel i82586 Ethernet definitions
+ *
+ * This is an extension to the Linux operating system, and is covered by the
+ * same GNU General Public License that covers that work.
+ *
+ * copyrights (c) 1994 by Michael Hipp (hippm@informatik.uni-tuebingen.de)
+ *
+ * I have done a look in the following sources:
+ * crynwr-packet-driver by Russ Nelson
+ * Garret A. Wollman's i82586-driver for BSD
+ */
+
+
+#define NI52_RESET 0 /* writing to this address, resets the i82586 */
+#define NI52_ATTENTION 1 /* channel attention, kick the 586 */
+#define NI52_TENA 3 /* 2-5 possibly wrong, Xmit enable */
+#define NI52_TDIS 2 /* Xmit disable */
+#define NI52_INTENA 5 /* Interrupt enable */
+#define NI52_INTDIS 4 /* Interrupt disable */
+#define NI52_MAGIC1 6 /* dunno exact function */
+#define NI52_MAGIC2 7 /* dunno exact function */
+
+#define NI52_MAGICVAL1 0x00 /* magic-values for ni5210 card */
+#define NI52_MAGICVAL2 0x55
+
+/*
+ * where to find the System Configuration Pointer (SCP)
+ */
+#define SCP_DEFAULT_ADDRESS 0xfffff4
+
+
+/*
+ * System Configuration Pointer Struct
+ */
+
+struct scp_struct
+{
+ u16 zero_dum0; /* has to be zero */
+ u8 sysbus; /* 0=16Bit,1=8Bit */
+ u8 zero_dum1; /* has to be zero for 586 */
+ u16 zero_dum2;
+ u16 zero_dum3;
+ u32 iscp; /* pointer to the iscp-block */
+};
+
+
+/*
+ * Intermediate System Configuration Pointer (ISCP)
+ */
+struct iscp_struct
+{
+ u8 busy; /* 586 clears after successful init */
+ u8 zero_dummy; /* has to be zero */
+ u16 scb_offset; /* pointeroffset to the scb_base */
+ u32 scb_base; /* base-address of all 16-bit offsets */
+};
+
+/*
+ * System Control Block (SCB)
+ */
+struct scb_struct
+{
+ u8 rus;
+ u8 cus;
+ u8 cmd_ruc; /* command word: RU part */
+ u8 cmd_cuc; /* command word: CU part & ACK */
+ u16 cbl_offset; /* pointeroffset, command block list */
+ u16 rfa_offset; /* pointeroffset, receive frame area */
+ u16 crc_errs; /* CRC-Error counter */
+ u16 aln_errs; /* alignmenterror counter */
+ u16 rsc_errs; /* Resourceerror counter */
+ u16 ovrn_errs; /* OVerrunerror counter */
+};
+
+/*
+ * possible command values for the command word
+ */
+#define RUC_MASK 0x0070 /* mask for RU commands */
+#define RUC_NOP 0x0000 /* NOP-command */
+#define RUC_START 0x0010 /* start RU */
+#define RUC_RESUME 0x0020 /* resume RU after suspend */
+#define RUC_SUSPEND 0x0030 /* suspend RU */
+#define RUC_ABORT 0x0040 /* abort receiver operation immediately */
+
+#define CUC_MASK 0x07 /* mask for CU command */
+#define CUC_NOP 0x00 /* NOP-command */
+#define CUC_START 0x01 /* start execution of 1. cmd on the CBL */
+#define CUC_RESUME 0x02 /* resume after suspend */
+#define CUC_SUSPEND 0x03 /* Suspend CU */
+#define CUC_ABORT 0x04 /* abort command operation immediately */
+
+#define ACK_MASK 0xf0 /* mask for ACK command */
+#define ACK_CX 0x80 /* acknowledges STAT_CX */
+#define ACK_FR 0x40 /* ack. STAT_FR */
+#define ACK_CNA 0x20 /* ack. STAT_CNA */
+#define ACK_RNR 0x10 /* ack. STAT_RNR */
+
+/*
+ * possible status values for the status word
+ */
+#define STAT_MASK 0xf0 /* mask for cause of interrupt */
+#define STAT_CX 0x80 /* CU finished cmd with its I bit set */
+#define STAT_FR 0x40 /* RU finished receiving a frame */
+#define STAT_CNA 0x20 /* CU left active state */
+#define STAT_RNR 0x10 /* RU left ready state */
+
+#define CU_STATUS 0x7 /* CU status, 0=idle */
+#define CU_SUSPEND 0x1 /* CU is suspended */
+#define CU_ACTIVE 0x2 /* CU is active */
+
+#define RU_STATUS 0x70 /* RU status, 0=idle */
+#define RU_SUSPEND 0x10 /* RU suspended */
+#define RU_NOSPACE 0x20 /* RU no resources */
+#define RU_READY 0x40 /* RU is ready */
+
+/*
+ * Receive Frame Descriptor (RFD)
+ */
+struct rfd_struct
+{
+ u8 stat_low; /* status word */
+ u8 stat_high; /* status word */
+ u8 rfd_sf; /* 82596 mode only */
+ u8 last; /* Bit15,Last Frame on List / Bit14,suspend */
+ u16 next; /* linkoffset to next RFD */
+ u16 rbd_offset; /* pointeroffset to RBD-buffer */
+ u8 dest[6]; /* ethernet-address, destination */
+ u8 source[6]; /* ethernet-address, source */
+ u16 length; /* 802.3 frame-length */
+ u16 zero_dummy; /* dummy */
+};
+
+#define RFD_LAST 0x80 /* last: last rfd in the list */
+#define RFD_SUSP 0x40 /* last: suspend RU after */
+#define RFD_COMPL 0x80
+#define RFD_OK 0x20
+#define RFD_BUSY 0x40
+#define RFD_ERR_LEN 0x10 /* Length error (if enabled length-checking */
+#define RFD_ERR_CRC 0x08 /* CRC error */
+#define RFD_ERR_ALGN 0x04 /* Alignment error */
+#define RFD_ERR_RNR 0x02 /* status: receiver out of resources */
+#define RFD_ERR_OVR 0x01 /* DMA Overrun! */
+
+#define RFD_ERR_FTS 0x0080 /* Frame to short */
+#define RFD_ERR_NEOP 0x0040 /* No EOP flag (for bitstuffing only) */
+#define RFD_ERR_TRUN 0x0020 /* (82596 only/SF mode) indicates truncated frame */
+#define RFD_MATCHADD 0x0002 /* status: Destinationaddress !matches IA (only 82596) */
+#define RFD_COLLDET 0x0001 /* Detected collision during reception */
+
+/*
+ * Receive Buffer Descriptor (RBD)
+ */
+struct rbd_struct
+{
+ u16 status; /* status word,number of used bytes in buff */
+ u16 next; /* pointeroffset to next RBD */
+ u32 buffer; /* receive buffer address pointer */
+ u16 size; /* size of this buffer */
+ u16 zero_dummy; /* dummy */
+};
+
+#define RBD_LAST 0x8000 /* last buffer */
+#define RBD_USED 0x4000 /* this buffer has data */
+#define RBD_MASK 0x3fff /* size-mask for length */
+
+/*
+ * Statusvalues for Commands/RFD
+ */
+#define STAT_COMPL 0x8000 /* status: frame/command is complete */
+#define STAT_BUSY 0x4000 /* status: frame/command is busy */
+#define STAT_OK 0x2000 /* status: frame/command is ok */
+
+/*
+ * Action-Commands
+ */
+#define CMD_NOP 0x0000 /* NOP */
+#define CMD_IASETUP 0x0001 /* initial address setup command */
+#define CMD_CONFIGURE 0x0002 /* configure command */
+#define CMD_MCSETUP 0x0003 /* MC setup command */
+#define CMD_XMIT 0x0004 /* transmit command */
+#define CMD_TDR 0x0005 /* time domain reflectometer (TDR) command */
+#define CMD_DUMP 0x0006 /* dump command */
+#define CMD_DIAGNOSE 0x0007 /* diagnose command */
+
+/*
+ * Action command bits
+ */
+#define CMD_LAST 0x8000 /* indicates last command in the CBL */
+#define CMD_SUSPEND 0x4000 /* suspend CU after this CB */
+#define CMD_INT 0x2000 /* generate interrupt after execution */
+
+/*
+ * NOP - command
+ */
+struct nop_cmd_struct
+{
+ u16 cmd_status; /* status of this command */
+ u16 cmd_cmd; /* the command itself (+bits) */
+ u16 cmd_link; /* offsetpointer to next command */
+};
+
+/*
+ * IA Setup command
+ */
+struct iasetup_cmd_struct
+{
+ u16 cmd_status;
+ u16 cmd_cmd;
+ u16 cmd_link;
+ u8 iaddr[6];
+};
+
+/*
+ * Configure command
+ */
+struct configure_cmd_struct
+{
+ u16 cmd_status;
+ u16 cmd_cmd;
+ u16 cmd_link;
+ u8 byte_cnt; /* size of the config-cmd */
+ u8 fifo; /* fifo/recv monitor */
+ u8 sav_bf; /* save bad frames (bit7=1)*/
+ u8 adr_len; /* adr_len(0-2),al_loc(3),pream(4-5),loopbak(6-7)*/
+ u8 priority; /* lin_prio(0-2),exp_prio(4-6),bof_metd(7) */
+ u8 ifs; /* inter frame spacing */
+ u8 time_low; /* slot time low */
+ u8 time_high; /* slot time high(0-2) and max. retries(4-7) */
+ u8 promisc; /* promisc-mode(0) , et al (1-7) */
+ u8 carr_coll; /* carrier(0-3)/collision(4-7) stuff */
+ u8 fram_len; /* minimal frame len */
+ u8 dummy; /* dummy */
+};
+
+/*
+ * Multicast Setup command
+ */
+struct mcsetup_cmd_struct
+{
+ u16 cmd_status;
+ u16 cmd_cmd;
+ u16 cmd_link;
+ u16 mc_cnt; /* number of bytes in the MC-List */
+ u8 mc_list[0][6]; /* pointer to 6 bytes entries */
+};
+
+/*
+ * DUMP command
+ */
+struct dump_cmd_struct
+{
+ u16 cmd_status;
+ u16 cmd_cmd;
+ u16 cmd_link;
+ u16 dump_offset; /* pointeroffset to DUMP space */
+};
+
+/*
+ * transmit command
+ */
+struct transmit_cmd_struct
+{
+ u16 cmd_status;
+ u16 cmd_cmd;
+ u16 cmd_link;
+ u16 tbd_offset; /* pointeroffset to TBD */
+ u8 dest[6]; /* destination address of the frame */
+ u16 length; /* user defined: 802.3 length / Ether type */
+};
+
+#define TCMD_ERRMASK 0x0fa0
+#define TCMD_MAXCOLLMASK 0x000f
+#define TCMD_MAXCOLL 0x0020
+#define TCMD_HEARTBEAT 0x0040
+#define TCMD_DEFERRED 0x0080
+#define TCMD_UNDERRUN 0x0100
+#define TCMD_LOSTCTS 0x0200
+#define TCMD_NOCARRIER 0x0400
+#define TCMD_LATECOLL 0x0800
+
+struct tdr_cmd_struct
+{
+ u16 cmd_status;
+ u16 cmd_cmd;
+ u16 cmd_link;
+ u16 status;
+};
+
+#define TDR_LNK_OK 0x8000 /* No link problem identified */
+#define TDR_XCVR_PRB 0x4000 /* indicates a transceiver problem */
+#define TDR_ET_OPN 0x2000 /* open, no correct termination */
+#define TDR_ET_SRT 0x1000 /* TDR detected a short circuit */
+#define TDR_TIMEMASK 0x07ff /* mask for the time field */
+
+/*
+ * Transmit Buffer Descriptor (TBD)
+ */
+struct tbd_struct
+{
+ u16 size; /* size + EOF-Flag(15) */
+ u16 next; /* pointeroffset to next TBD */
+ u32 buffer; /* pointer to buffer */
+};
+
+#define TBD_LAST 0x8000 /* EOF-Flag, indicates last buffer in list */
+
+
+
+
diff --git a/drivers/net/ethernet/i825xx/sni_82596.c b/drivers/net/ethernet/i825xx/sni_82596.c
new file mode 100644
index 00000000000..6b2a8881747
--- /dev/null
+++ b/drivers/net/ethernet/i825xx/sni_82596.c
@@ -0,0 +1,186 @@
+/*
+ * sni_82596.c -- driver for intel 82596 ethernet controller, as
+ * used in older SNI RM machines
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/bitops.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+
+#define SNI_82596_DRIVER_VERSION "SNI RM 82596 driver - Revision: 0.01"
+
+static const char sni_82596_string[] = "snirm_82596";
+
+#define DMA_ALLOC dma_alloc_coherent
+#define DMA_FREE dma_free_coherent
+#define DMA_WBACK(priv, addr, len) do { } while (0)
+#define DMA_INV(priv, addr, len) do { } while (0)
+#define DMA_WBACK_INV(priv, addr, len) do { } while (0)
+
+#define SYSBUS 0x00004400
+
+/* big endian CPU, 82596 little endian */
+#define SWAP32(x) cpu_to_le32((u32)(x))
+#define SWAP16(x) cpu_to_le16((u16)(x))
+
+#define OPT_MPU_16BIT 0x01
+
+#include "lib82596.c"
+
+MODULE_AUTHOR("Thomas Bogendoerfer");
+MODULE_DESCRIPTION("i82596 driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:snirm_82596");
+module_param(i596_debug, int, 0);
+MODULE_PARM_DESC(i596_debug, "82596 debug mask");
+
+static inline void ca(struct net_device *dev)
+{
+ struct i596_private *lp = netdev_priv(dev);
+
+ writel(0, lp->ca);
+}
+
+
+static void mpu_port(struct net_device *dev, int c, dma_addr_t x)
+{
+ struct i596_private *lp = netdev_priv(dev);
+
+ u32 v = (u32) (c) | (u32) (x);
+
+ if (lp->options & OPT_MPU_16BIT) {
+ writew(v & 0xffff, lp->mpu_port);
+ wmb(); /* order writes to MPU port */
+ udelay(1);
+ writew(v >> 16, lp->mpu_port);
+ } else {
+ writel(v, lp->mpu_port);
+ wmb(); /* order writes to MPU port */
+ udelay(1);
+ writel(v, lp->mpu_port);
+ }
+}
+
+
+static int __devinit sni_82596_probe(struct platform_device *dev)
+{
+ struct net_device *netdevice;
+ struct i596_private *lp;
+ struct resource *res, *ca, *idprom, *options;
+ int retval = -ENOMEM;
+ void __iomem *mpu_addr;
+ void __iomem *ca_addr;
+ u8 __iomem *eth_addr;
+
+ res = platform_get_resource(dev, IORESOURCE_MEM, 0);
+ ca = platform_get_resource(dev, IORESOURCE_MEM, 1);
+ options = platform_get_resource(dev, 0, 0);
+ idprom = platform_get_resource(dev, IORESOURCE_MEM, 2);
+ if (!res || !ca || !options || !idprom)
+ return -ENODEV;
+ mpu_addr = ioremap_nocache(res->start, 4);
+ if (!mpu_addr)
+ return -ENOMEM;
+ ca_addr = ioremap_nocache(ca->start, 4);
+ if (!ca_addr)
+ goto probe_failed_free_mpu;
+
+ printk(KERN_INFO "Found i82596 at 0x%x\n", res->start);
+
+ netdevice = alloc_etherdev(sizeof(struct i596_private));
+ if (!netdevice)
+ goto probe_failed_free_ca;
+
+ SET_NETDEV_DEV(netdevice, &dev->dev);
+ platform_set_drvdata (dev, netdevice);
+
+ netdevice->base_addr = res->start;
+ netdevice->irq = platform_get_irq(dev, 0);
+
+ eth_addr = ioremap_nocache(idprom->start, 0x10);
+ if (!eth_addr)
+ goto probe_failed;
+
+ /* someone seems to like messed up stuff */
+ netdevice->dev_addr[0] = readb(eth_addr + 0x0b);
+ netdevice->dev_addr[1] = readb(eth_addr + 0x0a);
+ netdevice->dev_addr[2] = readb(eth_addr + 0x09);
+ netdevice->dev_addr[3] = readb(eth_addr + 0x08);
+ netdevice->dev_addr[4] = readb(eth_addr + 0x07);
+ netdevice->dev_addr[5] = readb(eth_addr + 0x06);
+ iounmap(eth_addr);
+
+ if (!netdevice->irq) {
+ printk(KERN_ERR "%s: IRQ not found for i82596 at 0x%lx\n",
+ __FILE__, netdevice->base_addr);
+ goto probe_failed;
+ }
+
+ lp = netdev_priv(netdevice);
+ lp->options = options->flags & IORESOURCE_BITS;
+ lp->ca = ca_addr;
+ lp->mpu_port = mpu_addr;
+
+ retval = i82596_probe(netdevice);
+ if (retval == 0)
+ return 0;
+
+probe_failed:
+ free_netdev(netdevice);
+probe_failed_free_ca:
+ iounmap(ca_addr);
+probe_failed_free_mpu:
+ iounmap(mpu_addr);
+ return retval;
+}
+
+static int __devexit sni_82596_driver_remove(struct platform_device *pdev)
+{
+ struct net_device *dev = platform_get_drvdata(pdev);
+ struct i596_private *lp = netdev_priv(dev);
+
+ unregister_netdev(dev);
+ DMA_FREE(dev->dev.parent, sizeof(struct i596_private),
+ lp->dma, lp->dma_addr);
+ iounmap(lp->ca);
+ iounmap(lp->mpu_port);
+ free_netdev (dev);
+ return 0;
+}
+
+static struct platform_driver sni_82596_driver = {
+ .probe = sni_82596_probe,
+ .remove = __devexit_p(sni_82596_driver_remove),
+ .driver = {
+ .name = sni_82596_string,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __devinit sni_82596_init(void)
+{
+ printk(KERN_INFO SNI_82596_DRIVER_VERSION "\n");
+ return platform_driver_register(&sni_82596_driver);
+}
+
+
+static void __exit sni_82596_exit(void)
+{
+ platform_driver_unregister(&sni_82596_driver);
+}
+
+module_init(sni_82596_init);
+module_exit(sni_82596_exit);
diff --git a/drivers/net/ethernet/i825xx/sun3_82586.c b/drivers/net/ethernet/i825xx/sun3_82586.c
new file mode 100644
index 00000000000..6ef5e11d1c8
--- /dev/null
+++ b/drivers/net/ethernet/i825xx/sun3_82586.c
@@ -0,0 +1,1213 @@
+/*
+ * Sun3 i82586 Ethernet driver
+ *
+ * Cloned from ni52.c for the Sun3 by Sam Creasey (sammy@sammy.net)
+ *
+ * Original copyright follows:
+ * --------------------------
+ *
+ * net-3-driver for the NI5210 card (i82586 Ethernet chip)
+ *
+ * This is an extension to the Linux operating system, and is covered by the
+ * same Gnu Public License that covers that work.
+ *
+ * Alphacode 0.82 (96/09/29) for Linux 2.0.0 (or later)
+ * Copyrights (c) 1994,1995,1996 by M.Hipp (hippm@informatik.uni-tuebingen.de)
+ * --------------------------
+ *
+ * Consult ni52.c for further notes from the original driver.
+ *
+ * This incarnation currently supports the OBIO version of the i82586 chip
+ * used in certain sun3 models. It should be fairly doable to expand this
+ * to support VME if I should every acquire such a board.
+ *
+ */
+
+static int debuglevel = 0; /* debug-printk 0: off 1: a few 2: more */
+static int automatic_resume = 0; /* experimental .. better should be zero */
+static int rfdadd = 0; /* rfdadd=1 may be better for 8K MEM cards */
+static int fifo=0x8; /* don't change */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/bitops.h>
+#include <asm/io.h>
+#include <asm/idprom.h>
+#include <asm/machines.h>
+#include <asm/sun3mmu.h>
+#include <asm/dvma.h>
+#include <asm/byteorder.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include "sun3_82586.h"
+
+#define DRV_NAME "sun3_82586"
+
+#define DEBUG /* debug on */
+#define SYSBUSVAL 0 /* 16 Bit */
+#define SUN3_82586_TOTAL_SIZE PAGE_SIZE
+
+#define sun3_attn586() {*(volatile unsigned char *)(dev->base_addr) |= IEOB_ATTEN; *(volatile unsigned char *)(dev->base_addr) &= ~IEOB_ATTEN;}
+#define sun3_reset586() {*(volatile unsigned char *)(dev->base_addr) = 0; udelay(100); *(volatile unsigned char *)(dev->base_addr) = IEOB_NORSET;}
+#define sun3_disint() {*(volatile unsigned char *)(dev->base_addr) &= ~IEOB_IENAB;}
+#define sun3_enaint() {*(volatile unsigned char *)(dev->base_addr) |= IEOB_IENAB;}
+#define sun3_active() {*(volatile unsigned char *)(dev->base_addr) |= (IEOB_IENAB|IEOB_ONAIR|IEOB_NORSET);}
+
+#define make32(ptr16) (p->memtop + (swab16((unsigned short) (ptr16))) )
+#define make24(ptr32) (char *)swab32(( ((unsigned long) (ptr32)) - p->base))
+#define make16(ptr32) (swab16((unsigned short) ((unsigned long)(ptr32) - (unsigned long) p->memtop )))
+
+/******************* how to calculate the buffers *****************************
+
+ * IMPORTANT NOTE: if you configure only one NUM_XMIT_BUFFS, the driver works
+ * --------------- in a different (more stable?) mode. Only in this mode it's
+ * possible to configure the driver with 'NO_NOPCOMMANDS'
+
+sizeof(scp)=12; sizeof(scb)=16; sizeof(iscp)=8;
+sizeof(scp)+sizeof(iscp)+sizeof(scb) = 36 = INIT
+sizeof(rfd) = 24; sizeof(rbd) = 12;
+sizeof(tbd) = 8; sizeof(transmit_cmd) = 16;
+sizeof(nop_cmd) = 8;
+
+ * if you don't know the driver, better do not change these values: */
+
+#define RECV_BUFF_SIZE 1536 /* slightly oversized */
+#define XMIT_BUFF_SIZE 1536 /* slightly oversized */
+#define NUM_XMIT_BUFFS 1 /* config for 32K shmem */
+#define NUM_RECV_BUFFS_8 4 /* config for 32K shared mem */
+#define NUM_RECV_BUFFS_16 9 /* config for 32K shared mem */
+#define NUM_RECV_BUFFS_32 16 /* config for 32K shared mem */
+#define NO_NOPCOMMANDS /* only possible with NUM_XMIT_BUFFS=1 */
+
+/**************************************************************************/
+
+/* different DELAYs */
+#define DELAY(x) mdelay(32 * x);
+#define DELAY_16(); { udelay(16); }
+#define DELAY_18(); { udelay(4); }
+
+/* wait for command with timeout: */
+#define WAIT_4_SCB_CMD() \
+{ int i; \
+ for(i=0;i<16384;i++) { \
+ if(!p->scb->cmd_cuc) break; \
+ DELAY_18(); \
+ if(i == 16383) { \
+ printk("%s: scb_cmd timed out: %04x,%04x .. disabling i82586!!\n",dev->name,p->scb->cmd_cuc,p->scb->cus); \
+ if(!p->reseted) { p->reseted = 1; sun3_reset586(); } } } }
+
+#define WAIT_4_SCB_CMD_RUC() { int i; \
+ for(i=0;i<16384;i++) { \
+ if(!p->scb->cmd_ruc) break; \
+ DELAY_18(); \
+ if(i == 16383) { \
+ printk("%s: scb_cmd (ruc) timed out: %04x,%04x .. disabling i82586!!\n",dev->name,p->scb->cmd_ruc,p->scb->rus); \
+ if(!p->reseted) { p->reseted = 1; sun3_reset586(); } } } }
+
+#define WAIT_4_STAT_COMPL(addr) { int i; \
+ for(i=0;i<32767;i++) { \
+ if(swab16((addr)->cmd_status) & STAT_COMPL) break; \
+ DELAY_16(); DELAY_16(); } }
+
+static int sun3_82586_probe1(struct net_device *dev,int ioaddr);
+static irqreturn_t sun3_82586_interrupt(int irq,void *dev_id);
+static int sun3_82586_open(struct net_device *dev);
+static int sun3_82586_close(struct net_device *dev);
+static int sun3_82586_send_packet(struct sk_buff *,struct net_device *);
+static struct net_device_stats *sun3_82586_get_stats(struct net_device *dev);
+static void set_multicast_list(struct net_device *dev);
+static void sun3_82586_timeout(struct net_device *dev);
+#if 0
+static void sun3_82586_dump(struct net_device *,void *);
+#endif
+
+/* helper-functions */
+static int init586(struct net_device *dev);
+static int check586(struct net_device *dev,char *where,unsigned size);
+static void alloc586(struct net_device *dev);
+static void startrecv586(struct net_device *dev);
+static void *alloc_rfa(struct net_device *dev,void *ptr);
+static void sun3_82586_rcv_int(struct net_device *dev);
+static void sun3_82586_xmt_int(struct net_device *dev);
+static void sun3_82586_rnr_int(struct net_device *dev);
+
+struct priv
+{
+ unsigned long base;
+ char *memtop;
+ long int lock;
+ int reseted;
+ volatile struct rfd_struct *rfd_last,*rfd_top,*rfd_first;
+ volatile struct scp_struct *scp; /* volatile is important */
+ volatile struct iscp_struct *iscp; /* volatile is important */
+ volatile struct scb_struct *scb; /* volatile is important */
+ volatile struct tbd_struct *xmit_buffs[NUM_XMIT_BUFFS];
+ volatile struct transmit_cmd_struct *xmit_cmds[NUM_XMIT_BUFFS];
+#if (NUM_XMIT_BUFFS == 1)
+ volatile struct nop_cmd_struct *nop_cmds[2];
+#else
+ volatile struct nop_cmd_struct *nop_cmds[NUM_XMIT_BUFFS];
+#endif
+ volatile int nop_point,num_recv_buffs;
+ volatile char *xmit_cbuffs[NUM_XMIT_BUFFS];
+ volatile int xmit_count,xmit_last;
+};
+
+/**********************************************
+ * close device
+ */
+static int sun3_82586_close(struct net_device *dev)
+{
+ free_irq(dev->irq, dev);
+
+ sun3_reset586(); /* the hard way to stop the receiver */
+
+ netif_stop_queue(dev);
+
+ return 0;
+}
+
+/**********************************************
+ * open device
+ */
+static int sun3_82586_open(struct net_device *dev)
+{
+ int ret;
+
+ sun3_disint();
+ alloc586(dev);
+ init586(dev);
+ startrecv586(dev);
+ sun3_enaint();
+
+ ret = request_irq(dev->irq, sun3_82586_interrupt,0,dev->name,dev);
+ if (ret)
+ {
+ sun3_reset586();
+ return ret;
+ }
+
+ netif_start_queue(dev);
+
+ return 0; /* most done by init */
+}
+
+/**********************************************
+ * Check to see if there's an 82586 out there.
+ */
+static int check586(struct net_device *dev,char *where,unsigned size)
+{
+ struct priv pb;
+ struct priv *p = &pb;
+ char *iscp_addr;
+ int i;
+
+ p->base = (unsigned long) dvma_btov(0);
+ p->memtop = (char *)dvma_btov((unsigned long)where);
+ p->scp = (struct scp_struct *)(p->base + SCP_DEFAULT_ADDRESS);
+ memset((char *)p->scp,0, sizeof(struct scp_struct));
+ for(i=0;i<sizeof(struct scp_struct);i++) /* memory was writeable? */
+ if(((char *)p->scp)[i])
+ return 0;
+ p->scp->sysbus = SYSBUSVAL; /* 1 = 8Bit-Bus, 0 = 16 Bit */
+ if(p->scp->sysbus != SYSBUSVAL)
+ return 0;
+
+ iscp_addr = (char *)dvma_btov((unsigned long)where);
+
+ p->iscp = (struct iscp_struct *) iscp_addr;
+ memset((char *)p->iscp,0, sizeof(struct iscp_struct));
+
+ p->scp->iscp = make24(p->iscp);
+ p->iscp->busy = 1;
+
+ sun3_reset586();
+ sun3_attn586();
+ DELAY(1); /* wait a while... */
+
+ if(p->iscp->busy) /* i82586 clears 'busy' after successful init */
+ return 0;
+
+ return 1;
+}
+
+/******************************************************************
+ * set iscp at the right place, called by sun3_82586_probe1 and open586.
+ */
+static void alloc586(struct net_device *dev)
+{
+ struct priv *p = netdev_priv(dev);
+
+ sun3_reset586();
+ DELAY(1);
+
+ p->scp = (struct scp_struct *) (p->base + SCP_DEFAULT_ADDRESS);
+ p->iscp = (struct iscp_struct *) dvma_btov(dev->mem_start);
+ p->scb = (struct scb_struct *) ((char *)p->iscp + sizeof(struct iscp_struct));
+
+ memset((char *) p->iscp,0,sizeof(struct iscp_struct));
+ memset((char *) p->scp ,0,sizeof(struct scp_struct));
+
+ p->scp->iscp = make24(p->iscp);
+ p->scp->sysbus = SYSBUSVAL;
+ p->iscp->scb_offset = make16(p->scb);
+ p->iscp->scb_base = make24(dvma_btov(dev->mem_start));
+
+ p->iscp->busy = 1;
+ sun3_reset586();
+ sun3_attn586();
+
+ DELAY(1);
+
+ if(p->iscp->busy)
+ printk("%s: Init-Problems (alloc).\n",dev->name);
+
+ p->reseted = 0;
+
+ memset((char *)p->scb,0,sizeof(struct scb_struct));
+}
+
+struct net_device * __init sun3_82586_probe(int unit)
+{
+ struct net_device *dev;
+ unsigned long ioaddr;
+ static int found = 0;
+ int err = -ENOMEM;
+
+ /* check that this machine has an onboard 82586 */
+ switch(idprom->id_machtype) {
+ case SM_SUN3|SM_3_160:
+ case SM_SUN3|SM_3_260:
+ /* these machines have 82586 */
+ break;
+
+ default:
+ return ERR_PTR(-ENODEV);
+ }
+
+ if (found)
+ return ERR_PTR(-ENODEV);
+
+ ioaddr = (unsigned long)ioremap(IE_OBIO, SUN3_82586_TOTAL_SIZE);
+ if (!ioaddr)
+ return ERR_PTR(-ENOMEM);
+ found = 1;
+
+ dev = alloc_etherdev(sizeof(struct priv));
+ if (!dev)
+ goto out;
+ if (unit >= 0) {
+ sprintf(dev->name, "eth%d", unit);
+ netdev_boot_setup_check(dev);
+ }
+
+ dev->irq = IE_IRQ;
+ dev->base_addr = ioaddr;
+ err = sun3_82586_probe1(dev, ioaddr);
+ if (err)
+ goto out1;
+ err = register_netdev(dev);
+ if (err)
+ goto out2;
+ return dev;
+
+out2:
+ release_region(ioaddr, SUN3_82586_TOTAL_SIZE);
+out1:
+ free_netdev(dev);
+out:
+ iounmap((void __iomem *)ioaddr);
+ return ERR_PTR(err);
+}
+
+static const struct net_device_ops sun3_82586_netdev_ops = {
+ .ndo_open = sun3_82586_open,
+ .ndo_stop = sun3_82586_close,
+ .ndo_start_xmit = sun3_82586_send_packet,
+ .ndo_set_rx_mode = set_multicast_list,
+ .ndo_tx_timeout = sun3_82586_timeout,
+ .ndo_get_stats = sun3_82586_get_stats,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_change_mtu = eth_change_mtu,
+};
+
+static int __init sun3_82586_probe1(struct net_device *dev,int ioaddr)
+{
+ int i, size, retval;
+
+ if (!request_region(ioaddr, SUN3_82586_TOTAL_SIZE, DRV_NAME))
+ return -EBUSY;
+
+ /* copy in the ethernet address from the prom */
+ for(i = 0; i < 6 ; i++)
+ dev->dev_addr[i] = idprom->id_ethaddr[i];
+
+ printk("%s: SUN3 Intel 82586 found at %lx, ",dev->name,dev->base_addr);
+
+ /*
+ * check (or search) IO-Memory, 32K
+ */
+ size = 0x8000;
+
+ dev->mem_start = (unsigned long)dvma_malloc_align(0x8000, 0x1000);
+ dev->mem_end = dev->mem_start + size;
+
+ if(size != 0x2000 && size != 0x4000 && size != 0x8000) {
+ printk("\n%s: Illegal memory size %d. Allowed is 0x2000 or 0x4000 or 0x8000 bytes.\n",dev->name,size);
+ retval = -ENODEV;
+ goto out;
+ }
+ if(!check586(dev,(char *) dev->mem_start,size)) {
+ printk("?memcheck, Can't find memory at 0x%lx with size %d!\n",dev->mem_start,size);
+ retval = -ENODEV;
+ goto out;
+ }
+
+ ((struct priv *)netdev_priv(dev))->memtop =
+ (char *)dvma_btov(dev->mem_start);
+ ((struct priv *)netdev_priv(dev))->base = (unsigned long) dvma_btov(0);
+ alloc586(dev);
+
+ /* set number of receive-buffs according to memsize */
+ if(size == 0x2000)
+ ((struct priv *)netdev_priv(dev))->num_recv_buffs =
+ NUM_RECV_BUFFS_8;
+ else if(size == 0x4000)
+ ((struct priv *)netdev_priv(dev))->num_recv_buffs =
+ NUM_RECV_BUFFS_16;
+ else
+ ((struct priv *)netdev_priv(dev))->num_recv_buffs =
+ NUM_RECV_BUFFS_32;
+
+ printk("Memaddr: 0x%lx, Memsize: %d, IRQ %d\n",dev->mem_start,size, dev->irq);
+
+ dev->netdev_ops = &sun3_82586_netdev_ops;
+ dev->watchdog_timeo = HZ/20;
+
+ dev->if_port = 0;
+ return 0;
+out:
+ release_region(ioaddr, SUN3_82586_TOTAL_SIZE);
+ return retval;
+}
+
+
+static int init586(struct net_device *dev)
+{
+ void *ptr;
+ int i,result=0;
+ struct priv *p = netdev_priv(dev);
+ volatile struct configure_cmd_struct *cfg_cmd;
+ volatile struct iasetup_cmd_struct *ias_cmd;
+ volatile struct tdr_cmd_struct *tdr_cmd;
+ volatile struct mcsetup_cmd_struct *mc_cmd;
+ struct netdev_hw_addr *ha;
+ int num_addrs=netdev_mc_count(dev);
+
+ ptr = (void *) ((char *)p->scb + sizeof(struct scb_struct));
+
+ cfg_cmd = (struct configure_cmd_struct *)ptr; /* configure-command */
+ cfg_cmd->cmd_status = 0;
+ cfg_cmd->cmd_cmd = swab16(CMD_CONFIGURE | CMD_LAST);
+ cfg_cmd->cmd_link = 0xffff;
+
+ cfg_cmd->byte_cnt = 0x0a; /* number of cfg bytes */
+ cfg_cmd->fifo = fifo; /* fifo-limit (8=tx:32/rx:64) */
+ cfg_cmd->sav_bf = 0x40; /* hold or discard bad recv frames (bit 7) */
+ cfg_cmd->adr_len = 0x2e; /* addr_len |!src_insert |pre-len |loopback */
+ cfg_cmd->priority = 0x00;
+ cfg_cmd->ifs = 0x60;
+ cfg_cmd->time_low = 0x00;
+ cfg_cmd->time_high = 0xf2;
+ cfg_cmd->promisc = 0;
+ if(dev->flags & IFF_ALLMULTI) {
+ int len = ((char *) p->iscp - (char *) ptr - 8) / 6;
+ if(num_addrs > len) {
+ printk("%s: switching to promisc. mode\n",dev->name);
+ cfg_cmd->promisc = 1;
+ }
+ }
+ if(dev->flags&IFF_PROMISC)
+ cfg_cmd->promisc = 1;
+ cfg_cmd->carr_coll = 0x00;
+
+ p->scb->cbl_offset = make16(cfg_cmd);
+ p->scb->cmd_ruc = 0;
+
+ p->scb->cmd_cuc = CUC_START; /* cmd.-unit start */
+ sun3_attn586();
+
+ WAIT_4_STAT_COMPL(cfg_cmd);
+
+ if((swab16(cfg_cmd->cmd_status) & (STAT_OK|STAT_COMPL)) != (STAT_COMPL|STAT_OK))
+ {
+ printk("%s: configure command failed: %x\n",dev->name,swab16(cfg_cmd->cmd_status));
+ return 1;
+ }
+
+ /*
+ * individual address setup
+ */
+
+ ias_cmd = (struct iasetup_cmd_struct *)ptr;
+
+ ias_cmd->cmd_status = 0;
+ ias_cmd->cmd_cmd = swab16(CMD_IASETUP | CMD_LAST);
+ ias_cmd->cmd_link = 0xffff;
+
+ memcpy((char *)&ias_cmd->iaddr,(char *) dev->dev_addr,ETH_ALEN);
+
+ p->scb->cbl_offset = make16(ias_cmd);
+
+ p->scb->cmd_cuc = CUC_START; /* cmd.-unit start */
+ sun3_attn586();
+
+ WAIT_4_STAT_COMPL(ias_cmd);
+
+ if((swab16(ias_cmd->cmd_status) & (STAT_OK|STAT_COMPL)) != (STAT_OK|STAT_COMPL)) {
+ printk("%s (82586): individual address setup command failed: %04x\n",dev->name,swab16(ias_cmd->cmd_status));
+ return 1;
+ }
+
+ /*
+ * TDR, wire check .. e.g. no resistor e.t.c
+ */
+
+ tdr_cmd = (struct tdr_cmd_struct *)ptr;
+
+ tdr_cmd->cmd_status = 0;
+ tdr_cmd->cmd_cmd = swab16(CMD_TDR | CMD_LAST);
+ tdr_cmd->cmd_link = 0xffff;
+ tdr_cmd->status = 0;
+
+ p->scb->cbl_offset = make16(tdr_cmd);
+ p->scb->cmd_cuc = CUC_START; /* cmd.-unit start */
+ sun3_attn586();
+
+ WAIT_4_STAT_COMPL(tdr_cmd);
+
+ if(!(swab16(tdr_cmd->cmd_status) & STAT_COMPL))
+ {
+ printk("%s: Problems while running the TDR.\n",dev->name);
+ }
+ else
+ {
+ DELAY_16(); /* wait for result */
+ result = swab16(tdr_cmd->status);
+
+ p->scb->cmd_cuc = p->scb->cus & STAT_MASK;
+ sun3_attn586(); /* ack the interrupts */
+
+ if(result & TDR_LNK_OK)
+ ;
+ else if(result & TDR_XCVR_PRB)
+ printk("%s: TDR: Transceiver problem. Check the cable(s)!\n",dev->name);
+ else if(result & TDR_ET_OPN)
+ printk("%s: TDR: No correct termination %d clocks away.\n",dev->name,result & TDR_TIMEMASK);
+ else if(result & TDR_ET_SRT)
+ {
+ if (result & TDR_TIMEMASK) /* time == 0 -> strange :-) */
+ printk("%s: TDR: Detected a short circuit %d clocks away.\n",dev->name,result & TDR_TIMEMASK);
+ }
+ else
+ printk("%s: TDR: Unknown status %04x\n",dev->name,result);
+ }
+
+ /*
+ * Multicast setup
+ */
+ if(num_addrs && !(dev->flags & IFF_PROMISC) )
+ {
+ mc_cmd = (struct mcsetup_cmd_struct *) ptr;
+ mc_cmd->cmd_status = 0;
+ mc_cmd->cmd_cmd = swab16(CMD_MCSETUP | CMD_LAST);
+ mc_cmd->cmd_link = 0xffff;
+ mc_cmd->mc_cnt = swab16(num_addrs * 6);
+
+ i = 0;
+ netdev_for_each_mc_addr(ha, dev)
+ memcpy((char *) mc_cmd->mc_list[i++],
+ ha->addr, ETH_ALEN);
+
+ p->scb->cbl_offset = make16(mc_cmd);
+ p->scb->cmd_cuc = CUC_START;
+ sun3_attn586();
+
+ WAIT_4_STAT_COMPL(mc_cmd);
+
+ if( (swab16(mc_cmd->cmd_status) & (STAT_COMPL|STAT_OK)) != (STAT_COMPL|STAT_OK) )
+ printk("%s: Can't apply multicast-address-list.\n",dev->name);
+ }
+
+ /*
+ * alloc nop/xmit-cmds
+ */
+#if (NUM_XMIT_BUFFS == 1)
+ for(i=0;i<2;i++)
+ {
+ p->nop_cmds[i] = (struct nop_cmd_struct *)ptr;
+ p->nop_cmds[i]->cmd_cmd = swab16(CMD_NOP);
+ p->nop_cmds[i]->cmd_status = 0;
+ p->nop_cmds[i]->cmd_link = make16((p->nop_cmds[i]));
+ ptr = (char *) ptr + sizeof(struct nop_cmd_struct);
+ }
+#else
+ for(i=0;i<NUM_XMIT_BUFFS;i++)
+ {
+ p->nop_cmds[i] = (struct nop_cmd_struct *)ptr;
+ p->nop_cmds[i]->cmd_cmd = swab16(CMD_NOP);
+ p->nop_cmds[i]->cmd_status = 0;
+ p->nop_cmds[i]->cmd_link = make16((p->nop_cmds[i]));
+ ptr = (char *) ptr + sizeof(struct nop_cmd_struct);
+ }
+#endif
+
+ ptr = alloc_rfa(dev,(void *)ptr); /* init receive-frame-area */
+
+ /*
+ * alloc xmit-buffs / init xmit_cmds
+ */
+ for(i=0;i<NUM_XMIT_BUFFS;i++)
+ {
+ p->xmit_cmds[i] = (struct transmit_cmd_struct *)ptr; /*transmit cmd/buff 0*/
+ ptr = (char *) ptr + sizeof(struct transmit_cmd_struct);
+ p->xmit_cbuffs[i] = (char *)ptr; /* char-buffs */
+ ptr = (char *) ptr + XMIT_BUFF_SIZE;
+ p->xmit_buffs[i] = (struct tbd_struct *)ptr; /* TBD */
+ ptr = (char *) ptr + sizeof(struct tbd_struct);
+ if((void *)ptr > (void *)dev->mem_end)
+ {
+ printk("%s: not enough shared-mem for your configuration!\n",dev->name);
+ return 1;
+ }
+ memset((char *)(p->xmit_cmds[i]) ,0, sizeof(struct transmit_cmd_struct));
+ memset((char *)(p->xmit_buffs[i]),0, sizeof(struct tbd_struct));
+ p->xmit_cmds[i]->cmd_link = make16(p->nop_cmds[(i+1)%NUM_XMIT_BUFFS]);
+ p->xmit_cmds[i]->cmd_status = swab16(STAT_COMPL);
+ p->xmit_cmds[i]->cmd_cmd = swab16(CMD_XMIT | CMD_INT);
+ p->xmit_cmds[i]->tbd_offset = make16((p->xmit_buffs[i]));
+ p->xmit_buffs[i]->next = 0xffff;
+ p->xmit_buffs[i]->buffer = make24((p->xmit_cbuffs[i]));
+ }
+
+ p->xmit_count = 0;
+ p->xmit_last = 0;
+#ifndef NO_NOPCOMMANDS
+ p->nop_point = 0;
+#endif
+
+ /*
+ * 'start transmitter'
+ */
+#ifndef NO_NOPCOMMANDS
+ p->scb->cbl_offset = make16(p->nop_cmds[0]);
+ p->scb->cmd_cuc = CUC_START;
+ sun3_attn586();
+ WAIT_4_SCB_CMD();
+#else
+ p->xmit_cmds[0]->cmd_link = make16(p->xmit_cmds[0]);
+ p->xmit_cmds[0]->cmd_cmd = swab16(CMD_XMIT | CMD_SUSPEND | CMD_INT);
+#endif
+
+ /*
+ * ack. interrupts
+ */
+ p->scb->cmd_cuc = p->scb->cus & STAT_MASK;
+ sun3_attn586();
+ DELAY_16();
+
+ sun3_enaint();
+ sun3_active();
+
+ return 0;
+}
+
+/******************************************************
+ * This is a helper routine for sun3_82586_rnr_int() and init586().
+ * It sets up the Receive Frame Area (RFA).
+ */
+
+static void *alloc_rfa(struct net_device *dev,void *ptr)
+{
+ volatile struct rfd_struct *rfd = (struct rfd_struct *)ptr;
+ volatile struct rbd_struct *rbd;
+ int i;
+ struct priv *p = netdev_priv(dev);
+
+ memset((char *) rfd,0,sizeof(struct rfd_struct)*(p->num_recv_buffs+rfdadd));
+ p->rfd_first = rfd;
+
+ for(i = 0; i < (p->num_recv_buffs+rfdadd); i++) {
+ rfd[i].next = make16(rfd + (i+1) % (p->num_recv_buffs+rfdadd) );
+ rfd[i].rbd_offset = 0xffff;
+ }
+ rfd[p->num_recv_buffs-1+rfdadd].last = RFD_SUSP; /* RU suspend */
+
+ ptr = (void *) (rfd + (p->num_recv_buffs + rfdadd) );
+
+ rbd = (struct rbd_struct *) ptr;
+ ptr = (void *) (rbd + p->num_recv_buffs);
+
+ /* clr descriptors */
+ memset((char *) rbd,0,sizeof(struct rbd_struct)*(p->num_recv_buffs));
+
+ for(i=0;i<p->num_recv_buffs;i++)
+ {
+ rbd[i].next = make16((rbd + (i+1) % p->num_recv_buffs));
+ rbd[i].size = swab16(RECV_BUFF_SIZE);
+ rbd[i].buffer = make24(ptr);
+ ptr = (char *) ptr + RECV_BUFF_SIZE;
+ }
+
+ p->rfd_top = p->rfd_first;
+ p->rfd_last = p->rfd_first + (p->num_recv_buffs - 1 + rfdadd);
+
+ p->scb->rfa_offset = make16(p->rfd_first);
+ p->rfd_first->rbd_offset = make16(rbd);
+
+ return ptr;
+}
+
+
+/**************************************************
+ * Interrupt Handler ...
+ */
+
+static irqreturn_t sun3_82586_interrupt(int irq,void *dev_id)
+{
+ struct net_device *dev = dev_id;
+ unsigned short stat;
+ int cnt=0;
+ struct priv *p;
+
+ if (!dev) {
+ printk ("sun3_82586-interrupt: irq %d for unknown device.\n",irq);
+ return IRQ_NONE;
+ }
+ p = netdev_priv(dev);
+
+ if(debuglevel > 1)
+ printk("I");
+
+ WAIT_4_SCB_CMD(); /* wait for last command */
+
+ while((stat=p->scb->cus & STAT_MASK))
+ {
+ p->scb->cmd_cuc = stat;
+ sun3_attn586();
+
+ if(stat & STAT_FR) /* received a frame */
+ sun3_82586_rcv_int(dev);
+
+ if(stat & STAT_RNR) /* RU went 'not ready' */
+ {
+ printk("(R)");
+ if(p->scb->rus & RU_SUSPEND) /* special case: RU_SUSPEND */
+ {
+ WAIT_4_SCB_CMD();
+ p->scb->cmd_ruc = RUC_RESUME;
+ sun3_attn586();
+ WAIT_4_SCB_CMD_RUC();
+ }
+ else
+ {
+ printk("%s: Receiver-Unit went 'NOT READY': %04x/%02x.\n",dev->name,(int) stat,(int) p->scb->rus);
+ sun3_82586_rnr_int(dev);
+ }
+ }
+
+ if(stat & STAT_CX) /* command with I-bit set complete */
+ sun3_82586_xmt_int(dev);
+
+#ifndef NO_NOPCOMMANDS
+ if(stat & STAT_CNA) /* CU went 'not ready' */
+ {
+ if(netif_running(dev))
+ printk("%s: oops! CU has left active state. stat: %04x/%02x.\n",dev->name,(int) stat,(int) p->scb->cus);
+ }
+#endif
+
+ if(debuglevel > 1)
+ printk("%d",cnt++);
+
+ WAIT_4_SCB_CMD(); /* wait for ack. (sun3_82586_xmt_int can be faster than ack!!) */
+ if(p->scb->cmd_cuc) /* timed out? */
+ {
+ printk("%s: Acknowledge timed out.\n",dev->name);
+ sun3_disint();
+ break;
+ }
+ }
+
+ if(debuglevel > 1)
+ printk("i");
+ return IRQ_HANDLED;
+}
+
+/*******************************************************
+ * receive-interrupt
+ */
+
+static void sun3_82586_rcv_int(struct net_device *dev)
+{
+ int status,cnt=0;
+ unsigned short totlen;
+ struct sk_buff *skb;
+ struct rbd_struct *rbd;
+ struct priv *p = netdev_priv(dev);
+
+ if(debuglevel > 0)
+ printk("R");
+
+ for(;(status = p->rfd_top->stat_high) & RFD_COMPL;)
+ {
+ rbd = (struct rbd_struct *) make32(p->rfd_top->rbd_offset);
+
+ if(status & RFD_OK) /* frame received without error? */
+ {
+ if( (totlen = swab16(rbd->status)) & RBD_LAST) /* the first and the last buffer? */
+ {
+ totlen &= RBD_MASK; /* length of this frame */
+ rbd->status = 0;
+ skb = (struct sk_buff *) dev_alloc_skb(totlen+2);
+ if(skb != NULL)
+ {
+ skb_reserve(skb,2);
+ skb_put(skb,totlen);
+ skb_copy_to_linear_data(skb,(char *) p->base+swab32((unsigned long) rbd->buffer),totlen);
+ skb->protocol=eth_type_trans(skb,dev);
+ netif_rx(skb);
+ dev->stats.rx_packets++;
+ }
+ else
+ dev->stats.rx_dropped++;
+ }
+ else
+ {
+ int rstat;
+ /* free all RBD's until RBD_LAST is set */
+ totlen = 0;
+ while(!((rstat=swab16(rbd->status)) & RBD_LAST))
+ {
+ totlen += rstat & RBD_MASK;
+ if(!rstat)
+ {
+ printk("%s: Whoops .. no end mark in RBD list\n",dev->name);
+ break;
+ }
+ rbd->status = 0;
+ rbd = (struct rbd_struct *) make32(rbd->next);
+ }
+ totlen += rstat & RBD_MASK;
+ rbd->status = 0;
+ printk("%s: received oversized frame! length: %d\n",dev->name,totlen);
+ dev->stats.rx_dropped++;
+ }
+ }
+ else /* frame !(ok), only with 'save-bad-frames' */
+ {
+ printk("%s: oops! rfd-error-status: %04x\n",dev->name,status);
+ dev->stats.rx_errors++;
+ }
+ p->rfd_top->stat_high = 0;
+ p->rfd_top->last = RFD_SUSP; /* maybe exchange by RFD_LAST */
+ p->rfd_top->rbd_offset = 0xffff;
+ p->rfd_last->last = 0; /* delete RFD_SUSP */
+ p->rfd_last = p->rfd_top;
+ p->rfd_top = (struct rfd_struct *) make32(p->rfd_top->next); /* step to next RFD */
+ p->scb->rfa_offset = make16(p->rfd_top);
+
+ if(debuglevel > 0)
+ printk("%d",cnt++);
+ }
+
+ if(automatic_resume)
+ {
+ WAIT_4_SCB_CMD();
+ p->scb->cmd_ruc = RUC_RESUME;
+ sun3_attn586();
+ WAIT_4_SCB_CMD_RUC();
+ }
+
+#ifdef WAIT_4_BUSY
+ {
+ int i;
+ for(i=0;i<1024;i++)
+ {
+ if(p->rfd_top->status)
+ break;
+ DELAY_16();
+ if(i == 1023)
+ printk("%s: RU hasn't fetched next RFD (not busy/complete)\n",dev->name);
+ }
+ }
+#endif
+
+#if 0
+ if(!at_least_one)
+ {
+ int i;
+ volatile struct rfd_struct *rfds=p->rfd_top;
+ volatile struct rbd_struct *rbds;
+ printk("%s: received a FC intr. without having a frame: %04x %d\n",dev->name,status,old_at_least);
+ for(i=0;i< (p->num_recv_buffs+4);i++)
+ {
+ rbds = (struct rbd_struct *) make32(rfds->rbd_offset);
+ printk("%04x:%04x ",rfds->status,rbds->status);
+ rfds = (struct rfd_struct *) make32(rfds->next);
+ }
+ printk("\nerrs: %04x %04x stat: %04x\n",(int)p->scb->rsc_errs,(int)p->scb->ovrn_errs,(int)p->scb->status);
+ printk("\nerrs: %04x %04x rus: %02x, cus: %02x\n",(int)p->scb->rsc_errs,(int)p->scb->ovrn_errs,(int)p->scb->rus,(int)p->scb->cus);
+ }
+ old_at_least = at_least_one;
+#endif
+
+ if(debuglevel > 0)
+ printk("r");
+}
+
+/**********************************************************
+ * handle 'Receiver went not ready'.
+ */
+
+static void sun3_82586_rnr_int(struct net_device *dev)
+{
+ struct priv *p = netdev_priv(dev);
+
+ dev->stats.rx_errors++;
+
+ WAIT_4_SCB_CMD(); /* wait for the last cmd, WAIT_4_FULLSTAT?? */
+ p->scb->cmd_ruc = RUC_ABORT; /* usually the RU is in the 'no resource'-state .. abort it now. */
+ sun3_attn586();
+ WAIT_4_SCB_CMD_RUC(); /* wait for accept cmd. */
+
+ alloc_rfa(dev,(char *)p->rfd_first);
+/* maybe add a check here, before restarting the RU */
+ startrecv586(dev); /* restart RU */
+
+ printk("%s: Receive-Unit restarted. Status: %04x\n",dev->name,p->scb->rus);
+
+}
+
+/**********************************************************
+ * handle xmit - interrupt
+ */
+
+static void sun3_82586_xmt_int(struct net_device *dev)
+{
+ int status;
+ struct priv *p = netdev_priv(dev);
+
+ if(debuglevel > 0)
+ printk("X");
+
+ status = swab16(p->xmit_cmds[p->xmit_last]->cmd_status);
+ if(!(status & STAT_COMPL))
+ printk("%s: strange .. xmit-int without a 'COMPLETE'\n",dev->name);
+
+ if(status & STAT_OK)
+ {
+ dev->stats.tx_packets++;
+ dev->stats.collisions += (status & TCMD_MAXCOLLMASK);
+ }
+ else
+ {
+ dev->stats.tx_errors++;
+ if(status & TCMD_LATECOLL) {
+ printk("%s: late collision detected.\n",dev->name);
+ dev->stats.collisions++;
+ }
+ else if(status & TCMD_NOCARRIER) {
+ dev->stats.tx_carrier_errors++;
+ printk("%s: no carrier detected.\n",dev->name);
+ }
+ else if(status & TCMD_LOSTCTS)
+ printk("%s: loss of CTS detected.\n",dev->name);
+ else if(status & TCMD_UNDERRUN) {
+ dev->stats.tx_fifo_errors++;
+ printk("%s: DMA underrun detected.\n",dev->name);
+ }
+ else if(status & TCMD_MAXCOLL) {
+ printk("%s: Max. collisions exceeded.\n",dev->name);
+ dev->stats.collisions += 16;
+ }
+ }
+
+#if (NUM_XMIT_BUFFS > 1)
+ if( (++p->xmit_last) == NUM_XMIT_BUFFS)
+ p->xmit_last = 0;
+#endif
+ netif_wake_queue(dev);
+}
+
+/***********************************************************
+ * (re)start the receiver
+ */
+
+static void startrecv586(struct net_device *dev)
+{
+ struct priv *p = netdev_priv(dev);
+
+ WAIT_4_SCB_CMD();
+ WAIT_4_SCB_CMD_RUC();
+ p->scb->rfa_offset = make16(p->rfd_first);
+ p->scb->cmd_ruc = RUC_START;
+ sun3_attn586(); /* start cmd. */
+ WAIT_4_SCB_CMD_RUC(); /* wait for accept cmd. (no timeout!!) */
+}
+
+static void sun3_82586_timeout(struct net_device *dev)
+{
+ struct priv *p = netdev_priv(dev);
+#ifndef NO_NOPCOMMANDS
+ if(p->scb->cus & CU_ACTIVE) /* COMMAND-UNIT active? */
+ {
+ netif_wake_queue(dev);
+#ifdef DEBUG
+ printk("%s: strange ... timeout with CU active?!?\n",dev->name);
+ printk("%s: X0: %04x N0: %04x N1: %04x %d\n",dev->name,(int)swab16(p->xmit_cmds[0]->cmd_status),(int)swab16(p->nop_cmds[0]->cmd_status),(int)swab16(p->nop_cmds[1]->cmd_status),(int)p->nop_point);
+#endif
+ p->scb->cmd_cuc = CUC_ABORT;
+ sun3_attn586();
+ WAIT_4_SCB_CMD();
+ p->scb->cbl_offset = make16(p->nop_cmds[p->nop_point]);
+ p->scb->cmd_cuc = CUC_START;
+ sun3_attn586();
+ WAIT_4_SCB_CMD();
+ dev->trans_start = jiffies; /* prevent tx timeout */
+ return 0;
+ }
+#endif
+ {
+#ifdef DEBUG
+ printk("%s: xmitter timed out, try to restart! stat: %02x\n",dev->name,p->scb->cus);
+ printk("%s: command-stats: %04x %04x\n",dev->name,swab16(p->xmit_cmds[0]->cmd_status),swab16(p->xmit_cmds[1]->cmd_status));
+ printk("%s: check, whether you set the right interrupt number!\n",dev->name);
+#endif
+ sun3_82586_close(dev);
+ sun3_82586_open(dev);
+ }
+ dev->trans_start = jiffies; /* prevent tx timeout */
+}
+
+/******************************************************
+ * send frame
+ */
+
+static int sun3_82586_send_packet(struct sk_buff *skb, struct net_device *dev)
+{
+ int len,i;
+#ifndef NO_NOPCOMMANDS
+ int next_nop;
+#endif
+ struct priv *p = netdev_priv(dev);
+
+ if(skb->len > XMIT_BUFF_SIZE)
+ {
+ printk("%s: Sorry, max. framelength is %d bytes. The length of your frame is %d bytes.\n",dev->name,XMIT_BUFF_SIZE,skb->len);
+ return NETDEV_TX_OK;
+ }
+
+ netif_stop_queue(dev);
+
+#if(NUM_XMIT_BUFFS > 1)
+ if(test_and_set_bit(0,(void *) &p->lock)) {
+ printk("%s: Queue was locked\n",dev->name);
+ return NETDEV_TX_BUSY;
+ }
+ else
+#endif
+ {
+ len = skb->len;
+ if (len < ETH_ZLEN) {
+ memset((void *)p->xmit_cbuffs[p->xmit_count], 0,
+ ETH_ZLEN);
+ len = ETH_ZLEN;
+ }
+ skb_copy_from_linear_data(skb, (void *)p->xmit_cbuffs[p->xmit_count], skb->len);
+
+#if (NUM_XMIT_BUFFS == 1)
+# ifdef NO_NOPCOMMANDS
+
+#ifdef DEBUG
+ if(p->scb->cus & CU_ACTIVE)
+ {
+ printk("%s: Hmmm .. CU is still running and we wanna send a new packet.\n",dev->name);
+ printk("%s: stat: %04x %04x\n",dev->name,p->scb->cus,swab16(p->xmit_cmds[0]->cmd_status));
+ }
+#endif
+
+ p->xmit_buffs[0]->size = swab16(TBD_LAST | len);
+ for(i=0;i<16;i++)
+ {
+ p->xmit_cmds[0]->cmd_status = 0;
+ WAIT_4_SCB_CMD();
+ if( (p->scb->cus & CU_STATUS) == CU_SUSPEND)
+ p->scb->cmd_cuc = CUC_RESUME;
+ else
+ {
+ p->scb->cbl_offset = make16(p->xmit_cmds[0]);
+ p->scb->cmd_cuc = CUC_START;
+ }
+
+ sun3_attn586();
+ if(!i)
+ dev_kfree_skb(skb);
+ WAIT_4_SCB_CMD();
+ if( (p->scb->cus & CU_ACTIVE)) /* test it, because CU sometimes doesn't start immediately */
+ break;
+ if(p->xmit_cmds[0]->cmd_status)
+ break;
+ if(i==15)
+ printk("%s: Can't start transmit-command.\n",dev->name);
+ }
+# else
+ next_nop = (p->nop_point + 1) & 0x1;
+ p->xmit_buffs[0]->size = swab16(TBD_LAST | len);
+
+ p->xmit_cmds[0]->cmd_link = p->nop_cmds[next_nop]->cmd_link
+ = make16((p->nop_cmds[next_nop]));
+ p->xmit_cmds[0]->cmd_status = p->nop_cmds[next_nop]->cmd_status = 0;
+
+ p->nop_cmds[p->nop_point]->cmd_link = make16((p->xmit_cmds[0]));
+ p->nop_point = next_nop;
+ dev_kfree_skb(skb);
+# endif
+#else
+ p->xmit_buffs[p->xmit_count]->size = swab16(TBD_LAST | len);
+ if( (next_nop = p->xmit_count + 1) == NUM_XMIT_BUFFS )
+ next_nop = 0;
+
+ p->xmit_cmds[p->xmit_count]->cmd_status = 0;
+ /* linkpointer of xmit-command already points to next nop cmd */
+ p->nop_cmds[next_nop]->cmd_link = make16((p->nop_cmds[next_nop]));
+ p->nop_cmds[next_nop]->cmd_status = 0;
+
+ p->nop_cmds[p->xmit_count]->cmd_link = make16((p->xmit_cmds[p->xmit_count]));
+ p->xmit_count = next_nop;
+
+ {
+ unsigned long flags;
+ local_irq_save(flags);
+ if(p->xmit_count != p->xmit_last)
+ netif_wake_queue(dev);
+ p->lock = 0;
+ local_irq_restore(flags);
+ }
+ dev_kfree_skb(skb);
+#endif
+ }
+ return NETDEV_TX_OK;
+}
+
+/*******************************************
+ * Someone wanna have the statistics
+ */
+
+static struct net_device_stats *sun3_82586_get_stats(struct net_device *dev)
+{
+ struct priv *p = netdev_priv(dev);
+ unsigned short crc,aln,rsc,ovrn;
+
+ crc = swab16(p->scb->crc_errs); /* get error-statistic from the ni82586 */
+ p->scb->crc_errs = 0;
+ aln = swab16(p->scb->aln_errs);
+ p->scb->aln_errs = 0;
+ rsc = swab16(p->scb->rsc_errs);
+ p->scb->rsc_errs = 0;
+ ovrn = swab16(p->scb->ovrn_errs);
+ p->scb->ovrn_errs = 0;
+
+ dev->stats.rx_crc_errors += crc;
+ dev->stats.rx_fifo_errors += ovrn;
+ dev->stats.rx_frame_errors += aln;
+ dev->stats.rx_dropped += rsc;
+
+ return &dev->stats;
+}
+
+/********************************************************
+ * Set MC list ..
+ */
+
+static void set_multicast_list(struct net_device *dev)
+{
+ netif_stop_queue(dev);
+ sun3_disint();
+ alloc586(dev);
+ init586(dev);
+ startrecv586(dev);
+ sun3_enaint();
+ netif_wake_queue(dev);
+}
+
+#ifdef MODULE
+#error This code is not currently supported as a module
+static struct net_device *dev_sun3_82586;
+
+int init_module(void)
+{
+ dev_sun3_82586 = sun3_82586_probe(-1);
+ if (IS_ERR(dev_sun3_82586))
+ return PTR_ERR(dev_sun3_82586);
+ return 0;
+}
+
+void cleanup_module(void)
+{
+ unsigned long ioaddr = dev_sun3_82586->base_addr;
+ unregister_netdev(dev_sun3_82586);
+ release_region(ioaddr, SUN3_82586_TOTAL_SIZE);
+ iounmap((void *)ioaddr);
+ free_netdev(dev_sun3_82586);
+}
+#endif /* MODULE */
+
+#if 0
+/*
+ * DUMP .. we expect a not running CMD unit and enough space
+ */
+void sun3_82586_dump(struct net_device *dev,void *ptr)
+{
+ struct priv *p = netdev_priv(dev);
+ struct dump_cmd_struct *dump_cmd = (struct dump_cmd_struct *) ptr;
+ int i;
+
+ p->scb->cmd_cuc = CUC_ABORT;
+ sun3_attn586();
+ WAIT_4_SCB_CMD();
+ WAIT_4_SCB_CMD_RUC();
+
+ dump_cmd->cmd_status = 0;
+ dump_cmd->cmd_cmd = CMD_DUMP | CMD_LAST;
+ dump_cmd->dump_offset = make16((dump_cmd + 1));
+ dump_cmd->cmd_link = 0xffff;
+
+ p->scb->cbl_offset = make16(dump_cmd);
+ p->scb->cmd_cuc = CUC_START;
+ sun3_attn586();
+ WAIT_4_STAT_COMPL(dump_cmd);
+
+ if( (dump_cmd->cmd_status & (STAT_COMPL|STAT_OK)) != (STAT_COMPL|STAT_OK) )
+ printk("%s: Can't get dump information.\n",dev->name);
+
+ for(i=0;i<170;i++) {
+ printk("%02x ",(int) ((unsigned char *) (dump_cmd + 1))[i]);
+ if(i % 24 == 23)
+ printk("\n");
+ }
+ printk("\n");
+}
+#endif
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/ethernet/i825xx/sun3_82586.h b/drivers/net/ethernet/i825xx/sun3_82586.h
new file mode 100644
index 00000000000..93346f00486
--- /dev/null
+++ b/drivers/net/ethernet/i825xx/sun3_82586.h
@@ -0,0 +1,318 @@
+/*
+ * Intel i82586 Ethernet definitions
+ *
+ * This is an extension to the Linux operating system, and is covered by the
+ * same Gnu Public License that covers that work.
+ *
+ * copyrights (c) 1994 by Michael Hipp (hippm@informatik.uni-tuebingen.de)
+ *
+ * I have done a look in the following sources:
+ * crynwr-packet-driver by Russ Nelson
+ * Garret A. Wollman's i82586-driver for BSD
+ */
+
+/*
+ * Cloned from ni52.h, copyright as above.
+ *
+ * Modified for Sun3 OBIO i82586 by Sam Creasey (sammy@sammy.net)
+ */
+
+
+/* defines for the obio chip (not vme) */
+#define IEOB_NORSET 0x80 /* don't reset the board */
+#define IEOB_ONAIR 0x40 /* put us on the air */
+#define IEOB_ATTEN 0x20 /* attention! */
+#define IEOB_IENAB 0x10 /* interrupt enable */
+#define IEOB_XXXXX 0x08 /* free bit */
+#define IEOB_XCVRL2 0x04 /* level 2 transceiver? */
+#define IEOB_BUSERR 0x02 /* bus error */
+#define IEOB_INT 0x01 /* interrupt */
+
+/* where the obio one lives */
+#define IE_OBIO 0xc0000
+#define IE_IRQ 3
+
+/*
+ * where to find the System Configuration Pointer (SCP)
+ */
+#define SCP_DEFAULT_ADDRESS 0xfffff4
+
+
+/*
+ * System Configuration Pointer Struct
+ */
+
+struct scp_struct
+{
+ unsigned short zero_dum0; /* has to be zero */
+ unsigned char sysbus; /* 0=16Bit,1=8Bit */
+ unsigned char zero_dum1; /* has to be zero for 586 */
+ unsigned short zero_dum2;
+ unsigned short zero_dum3;
+ char *iscp; /* pointer to the iscp-block */
+};
+
+
+/*
+ * Intermediate System Configuration Pointer (ISCP)
+ */
+struct iscp_struct
+{
+ unsigned char busy; /* 586 clears after successful init */
+ unsigned char zero_dummy; /* has to be zero */
+ unsigned short scb_offset; /* pointeroffset to the scb_base */
+ char *scb_base; /* base-address of all 16-bit offsets */
+};
+
+/*
+ * System Control Block (SCB)
+ */
+struct scb_struct
+{
+ unsigned char rus;
+ unsigned char cus;
+ unsigned char cmd_ruc; /* command word: RU part */
+ unsigned char cmd_cuc; /* command word: CU part & ACK */
+ unsigned short cbl_offset; /* pointeroffset, command block list */
+ unsigned short rfa_offset; /* pointeroffset, receive frame area */
+ unsigned short crc_errs; /* CRC-Error counter */
+ unsigned short aln_errs; /* allignmenterror counter */
+ unsigned short rsc_errs; /* Resourceerror counter */
+ unsigned short ovrn_errs; /* OVerrunerror counter */
+};
+
+/*
+ * possible command values for the command word
+ */
+#define RUC_MASK 0x0070 /* mask for RU commands */
+#define RUC_NOP 0x0000 /* NOP-command */
+#define RUC_START 0x0010 /* start RU */
+#define RUC_RESUME 0x0020 /* resume RU after suspend */
+#define RUC_SUSPEND 0x0030 /* suspend RU */
+#define RUC_ABORT 0x0040 /* abort receiver operation immediately */
+
+#define CUC_MASK 0x07 /* mask for CU command */
+#define CUC_NOP 0x00 /* NOP-command */
+#define CUC_START 0x01 /* start execution of 1. cmd on the CBL */
+#define CUC_RESUME 0x02 /* resume after suspend */
+#define CUC_SUSPEND 0x03 /* Suspend CU */
+#define CUC_ABORT 0x04 /* abort command operation immediately */
+
+#define ACK_MASK 0xf0 /* mask for ACK command */
+#define ACK_CX 0x80 /* acknowledges STAT_CX */
+#define ACK_FR 0x40 /* ack. STAT_FR */
+#define ACK_CNA 0x20 /* ack. STAT_CNA */
+#define ACK_RNR 0x10 /* ack. STAT_RNR */
+
+/*
+ * possible status values for the status word
+ */
+#define STAT_MASK 0xf0 /* mask for cause of interrupt */
+#define STAT_CX 0x80 /* CU finished cmd with its I bit set */
+#define STAT_FR 0x40 /* RU finished receiving a frame */
+#define STAT_CNA 0x20 /* CU left active state */
+#define STAT_RNR 0x10 /* RU left ready state */
+
+#define CU_STATUS 0x7 /* CU status, 0=idle */
+#define CU_SUSPEND 0x1 /* CU is suspended */
+#define CU_ACTIVE 0x2 /* CU is active */
+
+#define RU_STATUS 0x70 /* RU status, 0=idle */
+#define RU_SUSPEND 0x10 /* RU suspended */
+#define RU_NOSPACE 0x20 /* RU no resources */
+#define RU_READY 0x40 /* RU is ready */
+
+/*
+ * Receive Frame Descriptor (RFD)
+ */
+struct rfd_struct
+{
+ unsigned char stat_low; /* status word */
+ unsigned char stat_high; /* status word */
+ unsigned char rfd_sf; /* 82596 mode only */
+ unsigned char last; /* Bit15,Last Frame on List / Bit14,suspend */
+ unsigned short next; /* linkoffset to next RFD */
+ unsigned short rbd_offset; /* pointeroffset to RBD-buffer */
+ unsigned char dest[6]; /* ethernet-address, destination */
+ unsigned char source[6]; /* ethernet-address, source */
+ unsigned short length; /* 802.3 frame-length */
+ unsigned short zero_dummy; /* dummy */
+};
+
+#define RFD_LAST 0x80 /* last: last rfd in the list */
+#define RFD_SUSP 0x40 /* last: suspend RU after */
+#define RFD_COMPL 0x80
+#define RFD_OK 0x20
+#define RFD_BUSY 0x40
+#define RFD_ERR_LEN 0x10 /* Length error (if enabled length-checking */
+#define RFD_ERR_CRC 0x08 /* CRC error */
+#define RFD_ERR_ALGN 0x04 /* Alignment error */
+#define RFD_ERR_RNR 0x02 /* status: receiver out of resources */
+#define RFD_ERR_OVR 0x01 /* DMA Overrun! */
+
+#define RFD_ERR_FTS 0x0080 /* Frame to short */
+#define RFD_ERR_NEOP 0x0040 /* No EOP flag (for bitstuffing only) */
+#define RFD_ERR_TRUN 0x0020 /* (82596 only/SF mode) indicates truncated frame */
+#define RFD_MATCHADD 0x0002 /* status: Destinationaddress !matches IA (only 82596) */
+#define RFD_COLLDET 0x0001 /* Detected collision during reception */
+
+/*
+ * Receive Buffer Descriptor (RBD)
+ */
+struct rbd_struct
+{
+ unsigned short status; /* status word,number of used bytes in buff */
+ unsigned short next; /* pointeroffset to next RBD */
+ char *buffer; /* receive buffer address pointer */
+ unsigned short size; /* size of this buffer */
+ unsigned short zero_dummy; /* dummy */
+};
+
+#define RBD_LAST 0x8000 /* last buffer */
+#define RBD_USED 0x4000 /* this buffer has data */
+#define RBD_MASK 0x3fff /* size-mask for length */
+
+/*
+ * Statusvalues for Commands/RFD
+ */
+#define STAT_COMPL 0x8000 /* status: frame/command is complete */
+#define STAT_BUSY 0x4000 /* status: frame/command is busy */
+#define STAT_OK 0x2000 /* status: frame/command is ok */
+
+/*
+ * Action-Commands
+ */
+#define CMD_NOP 0x0000 /* NOP */
+#define CMD_IASETUP 0x0001 /* initial address setup command */
+#define CMD_CONFIGURE 0x0002 /* configure command */
+#define CMD_MCSETUP 0x0003 /* MC setup command */
+#define CMD_XMIT 0x0004 /* transmit command */
+#define CMD_TDR 0x0005 /* time domain reflectometer (TDR) command */
+#define CMD_DUMP 0x0006 /* dump command */
+#define CMD_DIAGNOSE 0x0007 /* diagnose command */
+
+/*
+ * Action command bits
+ */
+#define CMD_LAST 0x8000 /* indicates last command in the CBL */
+#define CMD_SUSPEND 0x4000 /* suspend CU after this CB */
+#define CMD_INT 0x2000 /* generate interrupt after execution */
+
+/*
+ * NOP - command
+ */
+struct nop_cmd_struct
+{
+ unsigned short cmd_status; /* status of this command */
+ unsigned short cmd_cmd; /* the command itself (+bits) */
+ unsigned short cmd_link; /* offsetpointer to next command */
+};
+
+/*
+ * IA Setup command
+ */
+struct iasetup_cmd_struct
+{
+ unsigned short cmd_status;
+ unsigned short cmd_cmd;
+ unsigned short cmd_link;
+ unsigned char iaddr[6];
+};
+
+/*
+ * Configure command
+ */
+struct configure_cmd_struct
+{
+ unsigned short cmd_status;
+ unsigned short cmd_cmd;
+ unsigned short cmd_link;
+ unsigned char byte_cnt; /* size of the config-cmd */
+ unsigned char fifo; /* fifo/recv monitor */
+ unsigned char sav_bf; /* save bad frames (bit7=1)*/
+ unsigned char adr_len; /* adr_len(0-2),al_loc(3),pream(4-5),loopbak(6-7)*/
+ unsigned char priority; /* lin_prio(0-2),exp_prio(4-6),bof_metd(7) */
+ unsigned char ifs; /* inter frame spacing */
+ unsigned char time_low; /* slot time low */
+ unsigned char time_high; /* slot time high(0-2) and max. retries(4-7) */
+ unsigned char promisc; /* promisc-mode(0) , et al (1-7) */
+ unsigned char carr_coll; /* carrier(0-3)/collision(4-7) stuff */
+ unsigned char fram_len; /* minimal frame len */
+ unsigned char dummy; /* dummy */
+};
+
+/*
+ * Multicast Setup command
+ */
+struct mcsetup_cmd_struct
+{
+ unsigned short cmd_status;
+ unsigned short cmd_cmd;
+ unsigned short cmd_link;
+ unsigned short mc_cnt; /* number of bytes in the MC-List */
+ unsigned char mc_list[0][6]; /* pointer to 6 bytes entries */
+};
+
+/*
+ * DUMP command
+ */
+struct dump_cmd_struct
+{
+ unsigned short cmd_status;
+ unsigned short cmd_cmd;
+ unsigned short cmd_link;
+ unsigned short dump_offset; /* pointeroffset to DUMP space */
+};
+
+/*
+ * transmit command
+ */
+struct transmit_cmd_struct
+{
+ unsigned short cmd_status;
+ unsigned short cmd_cmd;
+ unsigned short cmd_link;
+ unsigned short tbd_offset; /* pointeroffset to TBD */
+ unsigned char dest[6]; /* destination address of the frame */
+ unsigned short length; /* user defined: 802.3 length / Ether type */
+};
+
+#define TCMD_ERRMASK 0x0fa0
+#define TCMD_MAXCOLLMASK 0x000f
+#define TCMD_MAXCOLL 0x0020
+#define TCMD_HEARTBEAT 0x0040
+#define TCMD_DEFERRED 0x0080
+#define TCMD_UNDERRUN 0x0100
+#define TCMD_LOSTCTS 0x0200
+#define TCMD_NOCARRIER 0x0400
+#define TCMD_LATECOLL 0x0800
+
+struct tdr_cmd_struct
+{
+ unsigned short cmd_status;
+ unsigned short cmd_cmd;
+ unsigned short cmd_link;
+ unsigned short status;
+};
+
+#define TDR_LNK_OK 0x8000 /* No link problem identified */
+#define TDR_XCVR_PRB 0x4000 /* indicates a transceiver problem */
+#define TDR_ET_OPN 0x2000 /* open, no correct termination */
+#define TDR_ET_SRT 0x1000 /* TDR detected a short circuit */
+#define TDR_TIMEMASK 0x07ff /* mask for the time field */
+
+/*
+ * Transmit Buffer Descriptor (TBD)
+ */
+struct tbd_struct
+{
+ unsigned short size; /* size + EOF-Flag(15) */
+ unsigned short next; /* pointeroffset to next TBD */
+ char *buffer; /* pointer to buffer */
+};
+
+#define TBD_LAST 0x8000 /* EOF-Flag, indicates last buffer in list */
+
+
+
+
diff --git a/drivers/net/ethernet/i825xx/znet.c b/drivers/net/ethernet/i825xx/znet.c
new file mode 100644
index 00000000000..962b4c421f3
--- /dev/null
+++ b/drivers/net/ethernet/i825xx/znet.c
@@ -0,0 +1,924 @@
+/* znet.c: An Zenith Z-Note ethernet driver for linux. */
+
+/*
+ Written by Donald Becker.
+
+ The author may be reached as becker@scyld.com.
+ This driver is based on the Linux skeleton driver. The copyright of the
+ skeleton driver is held by the United States Government, as represented
+ by DIRNSA, and it is released under the GPL.
+
+ Thanks to Mike Hollick for alpha testing and suggestions.
+
+ References:
+ The Crynwr packet driver.
+
+ "82593 CSMA/CD Core LAN Controller" Intel datasheet, 1992
+ Intel Microcommunications Databook, Vol. 1, 1990.
+ As usual with Intel, the documentation is incomplete and inaccurate.
+ I had to read the Crynwr packet driver to figure out how to actually
+ use the i82593, and guess at what register bits matched the loosely
+ related i82586.
+
+ Theory of Operation
+
+ The i82593 used in the Zenith Z-Note series operates using two(!) slave
+ DMA channels, one interrupt, and one 8-bit I/O port.
+
+ While there several ways to configure '593 DMA system, I chose the one
+ that seemed commensurate with the highest system performance in the face
+ of moderate interrupt latency: Both DMA channels are configured as
+ recirculating ring buffers, with one channel (#0) dedicated to Rx and
+ the other channel (#1) to Tx and configuration. (Note that this is
+ different than the Crynwr driver, where the Tx DMA channel is initialized
+ before each operation. That approach simplifies operation and Tx error
+ recovery, but requires additional I/O in normal operation and precludes
+ transmit buffer chaining.)
+
+ Both rings are set to 8192 bytes using {TX,RX}_RING_SIZE. This provides
+ a reasonable ring size for Rx, while simplifying DMA buffer allocation --
+ DMA buffers must not cross a 128K boundary. (In truth the size selection
+ was influenced by my lack of '593 documentation. I thus was constrained
+ to use the Crynwr '593 initialization table, which sets the Rx ring size
+ to 8K.)
+
+ Despite my usual low opinion about Intel-designed parts, I must admit
+ that the bulk data handling of the i82593 is a good design for
+ an integrated system, like a laptop, where using two slave DMA channels
+ doesn't pose a problem. I still take issue with using only a single I/O
+ port. In the same controlled environment there are essentially no
+ limitations on I/O space, and using multiple locations would eliminate
+ the need for multiple operations when looking at status registers,
+ setting the Rx ring boundary, or switching to promiscuous mode.
+
+ I also question Zenith's selection of the '593: one of the advertised
+ advantages of earlier Intel parts was that if you figured out the magic
+ initialization incantation you could use the same part on many different
+ network types. Zenith's use of the "FriendlyNet" (sic) connector rather
+ than an on-board transceiver leads me to believe that they were planning
+ to take advantage of this. But, uhmmm, the '593 omits all but ethernet
+ functionality from the serial subsystem.
+ */
+
+/* 10/2002
+
+ o Resurected for Linux 2.5+ by Marc Zyngier <maz@wild-wind.fr.eu.org> :
+
+ - Removed strange DMA snooping in znet_sent_packet, which lead to
+ TX buffer corruption on my laptop.
+ - Use init_etherdev stuff.
+ - Use kmalloc-ed DMA buffers.
+ - Use as few global variables as possible.
+ - Use proper resources management.
+ - Use wireless/i82593.h as much as possible (structure, constants)
+ - Compiles as module or build-in.
+ - Now survives unplugging/replugging cable.
+
+ Some code was taken from wavelan_cs.
+
+ Tested on a vintage Zenith Z-Note 433Lnp+. Probably broken on
+ anything else. Testers (and detailed bug reports) are welcome :-).
+
+ o TODO :
+
+ - Properly handle multicast
+ - Understand why some traffic patterns add a 1s latency...
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/if_arp.h>
+#include <linux/bitops.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#include <linux/i82593.h>
+
+static char version[] __initdata = "znet.c:v1.02 9/23/94 becker@scyld.com\n";
+
+#ifndef ZNET_DEBUG
+#define ZNET_DEBUG 1
+#endif
+static unsigned int znet_debug = ZNET_DEBUG;
+module_param (znet_debug, int, 0);
+MODULE_PARM_DESC (znet_debug, "ZNet debug level");
+MODULE_LICENSE("GPL");
+
+/* The DMA modes we need aren't in <dma.h>. */
+#define DMA_RX_MODE 0x14 /* Auto init, I/O to mem, ++, demand. */
+#define DMA_TX_MODE 0x18 /* Auto init, Mem to I/O, ++, demand. */
+#define dma_page_eq(ptr1, ptr2) ((long)(ptr1)>>17 == (long)(ptr2)>>17)
+#define RX_BUF_SIZE 8192
+#define TX_BUF_SIZE 8192
+#define DMA_BUF_SIZE (RX_BUF_SIZE + 16) /* 8k + 16 bytes for trailers */
+
+#define TX_TIMEOUT (HZ/10)
+
+struct znet_private {
+ int rx_dma, tx_dma;
+ spinlock_t lock;
+ short sia_base, sia_size, io_size;
+ struct i82593_conf_block i593_init;
+ /* The starting, current, and end pointers for the packet buffers. */
+ ushort *rx_start, *rx_cur, *rx_end;
+ ushort *tx_start, *tx_cur, *tx_end;
+ ushort tx_buf_len; /* Tx buffer length, in words. */
+};
+
+/* Only one can be built-in;-> */
+static struct net_device *znet_dev;
+
+struct netidblk {
+ char magic[8]; /* The magic number (string) "NETIDBLK" */
+ unsigned char netid[8]; /* The physical station address */
+ char nettype, globalopt;
+ char vendor[8]; /* The machine vendor and product name. */
+ char product[8];
+ char irq1, irq2; /* Interrupts, only one is currently used. */
+ char dma1, dma2;
+ short dma_mem_misc[8]; /* DMA buffer locations (unused in Linux). */
+ short iobase1, iosize1;
+ short iobase2, iosize2; /* Second iobase unused. */
+ char driver_options; /* Misc. bits */
+ char pad;
+};
+
+static int znet_open(struct net_device *dev);
+static netdev_tx_t znet_send_packet(struct sk_buff *skb,
+ struct net_device *dev);
+static irqreturn_t znet_interrupt(int irq, void *dev_id);
+static void znet_rx(struct net_device *dev);
+static int znet_close(struct net_device *dev);
+static void hardware_init(struct net_device *dev);
+static void update_stop_hit(short ioaddr, unsigned short rx_stop_offset);
+static void znet_tx_timeout (struct net_device *dev);
+
+/* Request needed resources */
+static int znet_request_resources (struct net_device *dev)
+{
+ struct znet_private *znet = netdev_priv(dev);
+
+ if (request_irq (dev->irq, znet_interrupt, 0, "ZNet", dev))
+ goto failed;
+ if (request_dma (znet->rx_dma, "ZNet rx"))
+ goto free_irq;
+ if (request_dma (znet->tx_dma, "ZNet tx"))
+ goto free_rx_dma;
+ if (!request_region (znet->sia_base, znet->sia_size, "ZNet SIA"))
+ goto free_tx_dma;
+ if (!request_region (dev->base_addr, znet->io_size, "ZNet I/O"))
+ goto free_sia;
+
+ return 0; /* Happy ! */
+
+ free_sia:
+ release_region (znet->sia_base, znet->sia_size);
+ free_tx_dma:
+ free_dma (znet->tx_dma);
+ free_rx_dma:
+ free_dma (znet->rx_dma);
+ free_irq:
+ free_irq (dev->irq, dev);
+ failed:
+ return -1;
+}
+
+static void znet_release_resources (struct net_device *dev)
+{
+ struct znet_private *znet = netdev_priv(dev);
+
+ release_region (znet->sia_base, znet->sia_size);
+ release_region (dev->base_addr, znet->io_size);
+ free_dma (znet->tx_dma);
+ free_dma (znet->rx_dma);
+ free_irq (dev->irq, dev);
+}
+
+/* Keep the magical SIA stuff in a single function... */
+static void znet_transceiver_power (struct net_device *dev, int on)
+{
+ struct znet_private *znet = netdev_priv(dev);
+ unsigned char v;
+
+ /* Turn on/off the 82501 SIA, using zenith-specific magic. */
+ /* Select LAN control register */
+ outb(0x10, znet->sia_base);
+
+ if (on)
+ v = inb(znet->sia_base + 1) | 0x84;
+ else
+ v = inb(znet->sia_base + 1) & ~0x84;
+
+ outb(v, znet->sia_base+1); /* Turn on/off LAN power (bit 2). */
+}
+
+/* Init the i82593, with current promisc/mcast configuration.
+ Also used from hardware_init. */
+static void znet_set_multicast_list (struct net_device *dev)
+{
+ struct znet_private *znet = netdev_priv(dev);
+ short ioaddr = dev->base_addr;
+ struct i82593_conf_block *cfblk = &znet->i593_init;
+
+ memset(cfblk, 0x00, sizeof(struct i82593_conf_block));
+
+ /* The configuration block. What an undocumented nightmare.
+ The first set of values are those suggested (without explanation)
+ for ethernet in the Intel 82586 databook. The rest appear to be
+ completely undocumented, except for cryptic notes in the Crynwr
+ packet driver. This driver uses the Crynwr values verbatim. */
+
+ /* maz : Rewritten to take advantage of the wanvelan includes.
+ At least we have names, not just blind values */
+
+ /* Byte 0 */
+ cfblk->fifo_limit = 10; /* = 16 B rx and 80 B tx fifo thresholds */
+ cfblk->forgnesi = 0; /* 0=82C501, 1=AMD7992B compatibility */
+ cfblk->fifo_32 = 1;
+ cfblk->d6mod = 0; /* Run in i82593 advanced mode */
+ cfblk->throttle_enb = 1;
+
+ /* Byte 1 */
+ cfblk->throttle = 8; /* Continuous w/interrupts, 128-clock DMA. */
+ cfblk->cntrxint = 0; /* enable continuous mode receive interrupts */
+ cfblk->contin = 1; /* enable continuous mode */
+
+ /* Byte 2 */
+ cfblk->addr_len = ETH_ALEN;
+ cfblk->acloc = 1; /* Disable source addr insertion by i82593 */
+ cfblk->preamb_len = 2; /* 8 bytes preamble */
+ cfblk->loopback = 0; /* Loopback off */
+
+ /* Byte 3 */
+ cfblk->lin_prio = 0; /* Default priorities & backoff methods. */
+ cfblk->tbofstop = 0;
+ cfblk->exp_prio = 0;
+ cfblk->bof_met = 0;
+
+ /* Byte 4 */
+ cfblk->ifrm_spc = 6; /* 96 bit times interframe spacing */
+
+ /* Byte 5 */
+ cfblk->slottim_low = 0; /* 512 bit times slot time (low) */
+
+ /* Byte 6 */
+ cfblk->slottim_hi = 2; /* 512 bit times slot time (high) */
+ cfblk->max_retr = 15; /* 15 collisions retries */
+
+ /* Byte 7 */
+ cfblk->prmisc = ((dev->flags & IFF_PROMISC) ? 1 : 0); /* Promiscuous mode */
+ cfblk->bc_dis = 0; /* Enable broadcast reception */
+ cfblk->crs_1 = 0; /* Don't transmit without carrier sense */
+ cfblk->nocrc_ins = 0; /* i82593 generates CRC */
+ cfblk->crc_1632 = 0; /* 32-bit Autodin-II CRC */
+ cfblk->crs_cdt = 0; /* CD not to be interpreted as CS */
+
+ /* Byte 8 */
+ cfblk->cs_filter = 0; /* CS is recognized immediately */
+ cfblk->crs_src = 0; /* External carrier sense */
+ cfblk->cd_filter = 0; /* CD is recognized immediately */
+
+ /* Byte 9 */
+ cfblk->min_fr_len = ETH_ZLEN >> 2; /* Minimum frame length */
+
+ /* Byte A */
+ cfblk->lng_typ = 1; /* Type/length checks OFF */
+ cfblk->lng_fld = 1; /* Disable 802.3 length field check */
+ cfblk->rxcrc_xf = 1; /* Don't transfer CRC to memory */
+ cfblk->artx = 1; /* Disable automatic retransmission */
+ cfblk->sarec = 1; /* Disable source addr trig of CD */
+ cfblk->tx_jabber = 0; /* Disable jabber jam sequence */
+ cfblk->hash_1 = 1; /* Use bits 0-5 in mc address hash */
+ cfblk->lbpkpol = 0; /* Loopback pin active high */
+
+ /* Byte B */
+ cfblk->fdx = 0; /* Disable full duplex operation */
+
+ /* Byte C */
+ cfblk->dummy_6 = 0x3f; /* all ones, Default multicast addresses & backoff. */
+ cfblk->mult_ia = 0; /* No multiple individual addresses */
+ cfblk->dis_bof = 0; /* Disable the backoff algorithm ?! */
+
+ /* Byte D */
+ cfblk->dummy_1 = 1; /* set to 1 */
+ cfblk->tx_ifs_retrig = 3; /* Hmm... Disabled */
+ cfblk->mc_all = (!netdev_mc_empty(dev) ||
+ (dev->flags & IFF_ALLMULTI)); /* multicast all mode */
+ cfblk->rcv_mon = 0; /* Monitor mode disabled */
+ cfblk->frag_acpt = 0; /* Do not accept fragments */
+ cfblk->tstrttrs = 0; /* No start transmission threshold */
+
+ /* Byte E */
+ cfblk->fretx = 1; /* FIFO automatic retransmission */
+ cfblk->runt_eop = 0; /* drop "runt" packets */
+ cfblk->hw_sw_pin = 0; /* ?? */
+ cfblk->big_endn = 0; /* Big Endian ? no... */
+ cfblk->syncrqs = 1; /* Synchronous DRQ deassertion... */
+ cfblk->sttlen = 1; /* 6 byte status registers */
+ cfblk->rx_eop = 0; /* Signal EOP on packet reception */
+ cfblk->tx_eop = 0; /* Signal EOP on packet transmission */
+
+ /* Byte F */
+ cfblk->rbuf_size = RX_BUF_SIZE >> 12; /* Set receive buffer size */
+ cfblk->rcvstop = 1; /* Enable Receive Stop Register */
+
+ if (znet_debug > 2) {
+ int i;
+ unsigned char *c;
+
+ for (i = 0, c = (char *) cfblk; i < sizeof (*cfblk); i++)
+ printk ("%02X ", c[i]);
+ printk ("\n");
+ }
+
+ *znet->tx_cur++ = sizeof(struct i82593_conf_block);
+ memcpy(znet->tx_cur, cfblk, sizeof(struct i82593_conf_block));
+ znet->tx_cur += sizeof(struct i82593_conf_block)/2;
+ outb(OP0_CONFIGURE | CR0_CHNL, ioaddr);
+
+ /* XXX FIXME maz : Add multicast addresses here, so having a
+ * multicast address configured isn't equal to IFF_ALLMULTI */
+}
+
+static const struct net_device_ops znet_netdev_ops = {
+ .ndo_open = znet_open,
+ .ndo_stop = znet_close,
+ .ndo_start_xmit = znet_send_packet,
+ .ndo_set_rx_mode = znet_set_multicast_list,
+ .ndo_tx_timeout = znet_tx_timeout,
+ .ndo_change_mtu = eth_change_mtu,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_validate_addr = eth_validate_addr,
+};
+
+/* The Z-Note probe is pretty easy. The NETIDBLK exists in the safe-to-probe
+ BIOS area. We just scan for the signature, and pull the vital parameters
+ out of the structure. */
+
+static int __init znet_probe (void)
+{
+ int i;
+ struct netidblk *netinfo;
+ struct znet_private *znet;
+ struct net_device *dev;
+ char *p;
+ int err = -ENOMEM;
+
+ /* This code scans the region 0xf0000 to 0xfffff for a "NETIDBLK". */
+ for(p = (char *)phys_to_virt(0xf0000); p < (char *)phys_to_virt(0x100000); p++)
+ if (*p == 'N' && strncmp(p, "NETIDBLK", 8) == 0)
+ break;
+
+ if (p >= (char *)phys_to_virt(0x100000)) {
+ if (znet_debug > 1)
+ printk(KERN_INFO "No Z-Note ethernet adaptor found.\n");
+ return -ENODEV;
+ }
+
+ dev = alloc_etherdev(sizeof(struct znet_private));
+ if (!dev)
+ return -ENOMEM;
+
+ znet = netdev_priv(dev);
+
+ netinfo = (struct netidblk *)p;
+ dev->base_addr = netinfo->iobase1;
+ dev->irq = netinfo->irq1;
+
+ /* The station address is in the "netidblk" at 0x0f0000. */
+ for (i = 0; i < 6; i++)
+ dev->dev_addr[i] = netinfo->netid[i];
+
+ printk(KERN_INFO "%s: ZNET at %#3lx, %pM"
+ ", using IRQ %d DMA %d and %d.\n",
+ dev->name, dev->base_addr, dev->dev_addr,
+ dev->irq, netinfo->dma1, netinfo->dma2);
+
+ if (znet_debug > 1) {
+ printk(KERN_INFO "%s: vendor '%16.16s' IRQ1 %d IRQ2 %d DMA1 %d DMA2 %d.\n",
+ dev->name, netinfo->vendor,
+ netinfo->irq1, netinfo->irq2,
+ netinfo->dma1, netinfo->dma2);
+ printk(KERN_INFO "%s: iobase1 %#x size %d iobase2 %#x size %d net type %2.2x.\n",
+ dev->name, netinfo->iobase1, netinfo->iosize1,
+ netinfo->iobase2, netinfo->iosize2, netinfo->nettype);
+ }
+
+ if (znet_debug > 0)
+ printk(KERN_INFO "%s", version);
+
+ znet->rx_dma = netinfo->dma1;
+ znet->tx_dma = netinfo->dma2;
+ spin_lock_init(&znet->lock);
+ znet->sia_base = 0xe6; /* Magic address for the 82501 SIA */
+ znet->sia_size = 2;
+ /* maz: Despite the '593 being advertised above as using a
+ * single 8bits I/O port, this driver does many 16bits
+ * access. So set io_size accordingly */
+ znet->io_size = 2;
+
+ if (!(znet->rx_start = kmalloc (DMA_BUF_SIZE, GFP_KERNEL | GFP_DMA)))
+ goto free_dev;
+ if (!(znet->tx_start = kmalloc (DMA_BUF_SIZE, GFP_KERNEL | GFP_DMA)))
+ goto free_rx;
+
+ if (!dma_page_eq (znet->rx_start, znet->rx_start + (RX_BUF_SIZE/2-1)) ||
+ !dma_page_eq (znet->tx_start, znet->tx_start + (TX_BUF_SIZE/2-1))) {
+ printk (KERN_WARNING "tx/rx crossing DMA frontiers, giving up\n");
+ goto free_tx;
+ }
+
+ znet->rx_end = znet->rx_start + RX_BUF_SIZE/2;
+ znet->tx_buf_len = TX_BUF_SIZE/2;
+ znet->tx_end = znet->tx_start + znet->tx_buf_len;
+
+ /* The ZNET-specific entries in the device structure. */
+ dev->netdev_ops = &znet_netdev_ops;
+ dev->watchdog_timeo = TX_TIMEOUT;
+ err = register_netdev(dev);
+ if (err)
+ goto free_tx;
+ znet_dev = dev;
+ return 0;
+
+ free_tx:
+ kfree(znet->tx_start);
+ free_rx:
+ kfree(znet->rx_start);
+ free_dev:
+ free_netdev(dev);
+ return err;
+}
+
+
+static int znet_open(struct net_device *dev)
+{
+ int ioaddr = dev->base_addr;
+
+ if (znet_debug > 2)
+ printk(KERN_DEBUG "%s: znet_open() called.\n", dev->name);
+
+ /* These should never fail. You can't add devices to a sealed box! */
+ if (znet_request_resources (dev)) {
+ printk(KERN_WARNING "%s: Not opened -- resource busy?!?\n", dev->name);
+ return -EBUSY;
+ }
+
+ znet_transceiver_power (dev, 1);
+
+ /* According to the Crynwr driver we should wait 50 msec. for the
+ LAN clock to stabilize. My experiments indicates that the '593 can
+ be initialized immediately. The delay is probably needed for the
+ DC-to-DC converter to come up to full voltage, and for the oscillator
+ to be spot-on at 20Mhz before transmitting.
+ Until this proves to be a problem we rely on the higher layers for the
+ delay and save allocating a timer entry. */
+
+ /* maz : Well, I'm getting every time the following message
+ * without the delay on a 486@33. This machine is much too
+ * fast... :-) So maybe the Crynwr driver wasn't wrong after
+ * all, even if the message is completly harmless on my
+ * setup. */
+ mdelay (50);
+
+ /* This follows the packet driver's lead, and checks for success. */
+ if (inb(ioaddr) != 0x10 && inb(ioaddr) != 0x00)
+ printk(KERN_WARNING "%s: Problem turning on the transceiver power.\n",
+ dev->name);
+
+ hardware_init(dev);
+ netif_start_queue (dev);
+
+ return 0;
+}
+
+
+static void znet_tx_timeout (struct net_device *dev)
+{
+ int ioaddr = dev->base_addr;
+ ushort event, tx_status, rx_offset, state;
+
+ outb (CR0_STATUS_0, ioaddr);
+ event = inb (ioaddr);
+ outb (CR0_STATUS_1, ioaddr);
+ tx_status = inw (ioaddr);
+ outb (CR0_STATUS_2, ioaddr);
+ rx_offset = inw (ioaddr);
+ outb (CR0_STATUS_3, ioaddr);
+ state = inb (ioaddr);
+ printk (KERN_WARNING "%s: transmit timed out, status %02x %04x %04x %02x,"
+ " resetting.\n", dev->name, event, tx_status, rx_offset, state);
+ if (tx_status == TX_LOST_CRS)
+ printk (KERN_WARNING "%s: Tx carrier error, check transceiver cable.\n",
+ dev->name);
+ outb (OP0_RESET, ioaddr);
+ hardware_init (dev);
+ netif_wake_queue (dev);
+}
+
+static netdev_tx_t znet_send_packet(struct sk_buff *skb, struct net_device *dev)
+{
+ int ioaddr = dev->base_addr;
+ struct znet_private *znet = netdev_priv(dev);
+ unsigned long flags;
+ short length = skb->len;
+
+ if (znet_debug > 4)
+ printk(KERN_DEBUG "%s: ZNet_send_packet.\n", dev->name);
+
+ if (length < ETH_ZLEN) {
+ if (skb_padto(skb, ETH_ZLEN))
+ return NETDEV_TX_OK;
+ length = ETH_ZLEN;
+ }
+
+ netif_stop_queue (dev);
+
+ /* Check that the part hasn't reset itself, probably from suspend. */
+ outb(CR0_STATUS_0, ioaddr);
+ if (inw(ioaddr) == 0x0010 &&
+ inw(ioaddr) == 0x0000 &&
+ inw(ioaddr) == 0x0010) {
+ if (znet_debug > 1)
+ printk (KERN_WARNING "%s : waking up\n", dev->name);
+ hardware_init(dev);
+ znet_transceiver_power (dev, 1);
+ }
+
+ if (1) {
+ unsigned char *buf = (void *)skb->data;
+ ushort *tx_link = znet->tx_cur - 1;
+ ushort rnd_len = (length + 1)>>1;
+
+ dev->stats.tx_bytes+=length;
+
+ if (znet->tx_cur >= znet->tx_end)
+ znet->tx_cur = znet->tx_start;
+ *znet->tx_cur++ = length;
+ if (znet->tx_cur + rnd_len + 1 > znet->tx_end) {
+ int semi_cnt = (znet->tx_end - znet->tx_cur)<<1; /* Cvrt to byte cnt. */
+ memcpy(znet->tx_cur, buf, semi_cnt);
+ rnd_len -= semi_cnt>>1;
+ memcpy(znet->tx_start, buf + semi_cnt, length - semi_cnt);
+ znet->tx_cur = znet->tx_start + rnd_len;
+ } else {
+ memcpy(znet->tx_cur, buf, skb->len);
+ znet->tx_cur += rnd_len;
+ }
+ *znet->tx_cur++ = 0;
+
+ spin_lock_irqsave(&znet->lock, flags);
+ {
+ *tx_link = OP0_TRANSMIT | CR0_CHNL;
+ /* Is this always safe to do? */
+ outb(OP0_TRANSMIT | CR0_CHNL, ioaddr);
+ }
+ spin_unlock_irqrestore (&znet->lock, flags);
+
+ netif_start_queue (dev);
+
+ if (znet_debug > 4)
+ printk(KERN_DEBUG "%s: Transmitter queued, length %d.\n", dev->name, length);
+ }
+ dev_kfree_skb(skb);
+ return NETDEV_TX_OK;
+}
+
+/* The ZNET interrupt handler. */
+static irqreturn_t znet_interrupt(int irq, void *dev_id)
+{
+ struct net_device *dev = dev_id;
+ struct znet_private *znet = netdev_priv(dev);
+ int ioaddr;
+ int boguscnt = 20;
+ int handled = 0;
+
+ spin_lock (&znet->lock);
+
+ ioaddr = dev->base_addr;
+
+ outb(CR0_STATUS_0, ioaddr);
+ do {
+ ushort status = inb(ioaddr);
+ if (znet_debug > 5) {
+ ushort result, rx_ptr, running;
+ outb(CR0_STATUS_1, ioaddr);
+ result = inw(ioaddr);
+ outb(CR0_STATUS_2, ioaddr);
+ rx_ptr = inw(ioaddr);
+ outb(CR0_STATUS_3, ioaddr);
+ running = inb(ioaddr);
+ printk(KERN_DEBUG "%s: interrupt, status %02x, %04x %04x %02x serial %d.\n",
+ dev->name, status, result, rx_ptr, running, boguscnt);
+ }
+ if ((status & SR0_INTERRUPT) == 0)
+ break;
+
+ handled = 1;
+
+ if ((status & SR0_EVENT_MASK) == SR0_TRANSMIT_DONE ||
+ (status & SR0_EVENT_MASK) == SR0_RETRANSMIT_DONE ||
+ (status & SR0_EVENT_MASK) == SR0_TRANSMIT_NO_CRC_DONE) {
+ int tx_status;
+ outb(CR0_STATUS_1, ioaddr);
+ tx_status = inw(ioaddr);
+ /* It's undocumented, but tx_status seems to match the i82586. */
+ if (tx_status & TX_OK) {
+ dev->stats.tx_packets++;
+ dev->stats.collisions += tx_status & TX_NCOL_MASK;
+ } else {
+ if (tx_status & (TX_LOST_CTS | TX_LOST_CRS))
+ dev->stats.tx_carrier_errors++;
+ if (tx_status & TX_UND_RUN)
+ dev->stats.tx_fifo_errors++;
+ if (!(tx_status & TX_HRT_BEAT))
+ dev->stats.tx_heartbeat_errors++;
+ if (tx_status & TX_MAX_COL)
+ dev->stats.tx_aborted_errors++;
+ /* ...and the catch-all. */
+ if ((tx_status | (TX_LOST_CRS | TX_LOST_CTS | TX_UND_RUN | TX_HRT_BEAT | TX_MAX_COL)) != (TX_LOST_CRS | TX_LOST_CTS | TX_UND_RUN | TX_HRT_BEAT | TX_MAX_COL))
+ dev->stats.tx_errors++;
+
+ /* Transceiver may be stuck if cable
+ * was removed while emitting a
+ * packet. Flip it off, then on to
+ * reset it. This is very empirical,
+ * but it seems to work. */
+
+ znet_transceiver_power (dev, 0);
+ znet_transceiver_power (dev, 1);
+ }
+ netif_wake_queue (dev);
+ }
+
+ if ((status & SR0_RECEPTION) ||
+ (status & SR0_EVENT_MASK) == SR0_STOP_REG_HIT) {
+ znet_rx(dev);
+ }
+ /* Clear the interrupts we've handled. */
+ outb(CR0_INT_ACK, ioaddr);
+ } while (boguscnt--);
+
+ spin_unlock (&znet->lock);
+
+ return IRQ_RETVAL(handled);
+}
+
+static void znet_rx(struct net_device *dev)
+{
+ struct znet_private *znet = netdev_priv(dev);
+ int ioaddr = dev->base_addr;
+ int boguscount = 1;
+ short next_frame_end_offset = 0; /* Offset of next frame start. */
+ short *cur_frame_end;
+ short cur_frame_end_offset;
+
+ outb(CR0_STATUS_2, ioaddr);
+ cur_frame_end_offset = inw(ioaddr);
+
+ if (cur_frame_end_offset == znet->rx_cur - znet->rx_start) {
+ printk(KERN_WARNING "%s: Interrupted, but nothing to receive, offset %03x.\n",
+ dev->name, cur_frame_end_offset);
+ return;
+ }
+
+ /* Use same method as the Crynwr driver: construct a forward list in
+ the same area of the backwards links we now have. This allows us to
+ pass packets to the upper layers in the order they were received --
+ important for fast-path sequential operations. */
+ while (znet->rx_start + cur_frame_end_offset != znet->rx_cur &&
+ ++boguscount < 5) {
+ unsigned short hi_cnt, lo_cnt, hi_status, lo_status;
+ int count, status;
+
+ if (cur_frame_end_offset < 4) {
+ /* Oh no, we have a special case: the frame trailer wraps around
+ the end of the ring buffer. We've saved space at the end of
+ the ring buffer for just this problem. */
+ memcpy(znet->rx_end, znet->rx_start, 8);
+ cur_frame_end_offset += (RX_BUF_SIZE/2);
+ }
+ cur_frame_end = znet->rx_start + cur_frame_end_offset - 4;
+
+ lo_status = *cur_frame_end++;
+ hi_status = *cur_frame_end++;
+ status = ((hi_status & 0xff) << 8) + (lo_status & 0xff);
+ lo_cnt = *cur_frame_end++;
+ hi_cnt = *cur_frame_end++;
+ count = ((hi_cnt & 0xff) << 8) + (lo_cnt & 0xff);
+
+ if (znet_debug > 5)
+ printk(KERN_DEBUG "Constructing trailer at location %03x, %04x %04x %04x %04x"
+ " count %#x status %04x.\n",
+ cur_frame_end_offset<<1, lo_status, hi_status, lo_cnt, hi_cnt,
+ count, status);
+ cur_frame_end[-4] = status;
+ cur_frame_end[-3] = next_frame_end_offset;
+ cur_frame_end[-2] = count;
+ next_frame_end_offset = cur_frame_end_offset;
+ cur_frame_end_offset -= ((count + 1)>>1) + 3;
+ if (cur_frame_end_offset < 0)
+ cur_frame_end_offset += RX_BUF_SIZE/2;
+ }
+
+ /* Now step forward through the list. */
+ do {
+ ushort *this_rfp_ptr = znet->rx_start + next_frame_end_offset;
+ int status = this_rfp_ptr[-4];
+ int pkt_len = this_rfp_ptr[-2];
+
+ if (znet_debug > 5)
+ printk(KERN_DEBUG "Looking at trailer ending at %04x status %04x length %03x"
+ " next %04x.\n", next_frame_end_offset<<1, status, pkt_len,
+ this_rfp_ptr[-3]<<1);
+ /* Once again we must assume that the i82586 docs apply. */
+ if ( ! (status & RX_RCV_OK)) { /* There was an error. */
+ dev->stats.rx_errors++;
+ if (status & RX_CRC_ERR) dev->stats.rx_crc_errors++;
+ if (status & RX_ALG_ERR) dev->stats.rx_frame_errors++;
+#if 0
+ if (status & 0x0200) dev->stats.rx_over_errors++; /* Wrong. */
+ if (status & 0x0100) dev->stats.rx_fifo_errors++;
+#else
+ /* maz : Wild guess... */
+ if (status & RX_OVRRUN) dev->stats.rx_over_errors++;
+#endif
+ if (status & RX_SRT_FRM) dev->stats.rx_length_errors++;
+ } else if (pkt_len > 1536) {
+ dev->stats.rx_length_errors++;
+ } else {
+ /* Malloc up new buffer. */
+ struct sk_buff *skb;
+
+ skb = dev_alloc_skb(pkt_len);
+ if (skb == NULL) {
+ if (znet_debug)
+ printk(KERN_WARNING "%s: Memory squeeze, dropping packet.\n", dev->name);
+ dev->stats.rx_dropped++;
+ break;
+ }
+
+ if (&znet->rx_cur[(pkt_len+1)>>1] > znet->rx_end) {
+ int semi_cnt = (znet->rx_end - znet->rx_cur)<<1;
+ memcpy(skb_put(skb,semi_cnt), znet->rx_cur, semi_cnt);
+ memcpy(skb_put(skb,pkt_len-semi_cnt), znet->rx_start,
+ pkt_len - semi_cnt);
+ } else {
+ memcpy(skb_put(skb,pkt_len), znet->rx_cur, pkt_len);
+ if (znet_debug > 6) {
+ unsigned int *packet = (unsigned int *) skb->data;
+ printk(KERN_DEBUG "Packet data is %08x %08x %08x %08x.\n", packet[0],
+ packet[1], packet[2], packet[3]);
+ }
+ }
+ skb->protocol=eth_type_trans(skb,dev);
+ netif_rx(skb);
+ dev->stats.rx_packets++;
+ dev->stats.rx_bytes += pkt_len;
+ }
+ znet->rx_cur = this_rfp_ptr;
+ if (znet->rx_cur >= znet->rx_end)
+ znet->rx_cur -= RX_BUF_SIZE/2;
+ update_stop_hit(ioaddr, (znet->rx_cur - znet->rx_start)<<1);
+ next_frame_end_offset = this_rfp_ptr[-3];
+ if (next_frame_end_offset == 0) /* Read all the frames? */
+ break; /* Done for now */
+ this_rfp_ptr = znet->rx_start + next_frame_end_offset;
+ } while (--boguscount);
+
+ /* If any worth-while packets have been received, dev_rint()
+ has done a mark_bh(INET_BH) for us and will work on them
+ when we get to the bottom-half routine. */
+}
+
+/* The inverse routine to znet_open(). */
+static int znet_close(struct net_device *dev)
+{
+ int ioaddr = dev->base_addr;
+
+ netif_stop_queue (dev);
+
+ outb(OP0_RESET, ioaddr); /* CMD0_RESET */
+
+ if (znet_debug > 1)
+ printk(KERN_DEBUG "%s: Shutting down ethercard.\n", dev->name);
+ /* Turn off transceiver power. */
+ znet_transceiver_power (dev, 0);
+
+ znet_release_resources (dev);
+
+ return 0;
+}
+
+static void show_dma(struct net_device *dev)
+{
+ short ioaddr = dev->base_addr;
+ unsigned char stat = inb (ioaddr);
+ struct znet_private *znet = netdev_priv(dev);
+ unsigned long flags;
+ short dma_port = ((znet->tx_dma&3)<<2) + IO_DMA2_BASE;
+ unsigned addr = inb(dma_port);
+ short residue;
+
+ addr |= inb(dma_port) << 8;
+ residue = get_dma_residue(znet->tx_dma);
+
+ if (znet_debug > 1) {
+ flags=claim_dma_lock();
+ printk(KERN_DEBUG "Stat:%02x Addr: %04x cnt:%3x\n",
+ stat, addr<<1, residue);
+ release_dma_lock(flags);
+ }
+}
+
+/* Initialize the hardware. We have to do this when the board is open()ed
+ or when we come out of suspend mode. */
+static void hardware_init(struct net_device *dev)
+{
+ unsigned long flags;
+ short ioaddr = dev->base_addr;
+ struct znet_private *znet = netdev_priv(dev);
+
+ znet->rx_cur = znet->rx_start;
+ znet->tx_cur = znet->tx_start;
+
+ /* Reset the chip, and start it up. */
+ outb(OP0_RESET, ioaddr);
+
+ flags=claim_dma_lock();
+ disable_dma(znet->rx_dma); /* reset by an interrupting task. */
+ clear_dma_ff(znet->rx_dma);
+ set_dma_mode(znet->rx_dma, DMA_RX_MODE);
+ set_dma_addr(znet->rx_dma, (unsigned int) znet->rx_start);
+ set_dma_count(znet->rx_dma, RX_BUF_SIZE);
+ enable_dma(znet->rx_dma);
+ /* Now set up the Tx channel. */
+ disable_dma(znet->tx_dma);
+ clear_dma_ff(znet->tx_dma);
+ set_dma_mode(znet->tx_dma, DMA_TX_MODE);
+ set_dma_addr(znet->tx_dma, (unsigned int) znet->tx_start);
+ set_dma_count(znet->tx_dma, znet->tx_buf_len<<1);
+ enable_dma(znet->tx_dma);
+ release_dma_lock(flags);
+
+ if (znet_debug > 1)
+ printk(KERN_DEBUG "%s: Initializing the i82593, rx buf %p tx buf %p\n",
+ dev->name, znet->rx_start,znet->tx_start);
+ /* Do an empty configure command, just like the Crynwr driver. This
+ resets to chip to its default values. */
+ *znet->tx_cur++ = 0;
+ *znet->tx_cur++ = 0;
+ show_dma(dev);
+ outb(OP0_CONFIGURE | CR0_CHNL, ioaddr);
+
+ znet_set_multicast_list (dev);
+
+ *znet->tx_cur++ = 6;
+ memcpy(znet->tx_cur, dev->dev_addr, 6);
+ znet->tx_cur += 3;
+ show_dma(dev);
+ outb(OP0_IA_SETUP | CR0_CHNL, ioaddr);
+ show_dma(dev);
+
+ update_stop_hit(ioaddr, 8192);
+ if (znet_debug > 1) printk(KERN_DEBUG "enabling Rx.\n");
+ outb(OP0_RCV_ENABLE, ioaddr);
+ netif_start_queue (dev);
+}
+
+static void update_stop_hit(short ioaddr, unsigned short rx_stop_offset)
+{
+ outb(OP0_SWIT_TO_PORT_1 | CR0_CHNL, ioaddr);
+ if (znet_debug > 5)
+ printk(KERN_DEBUG "Updating stop hit with value %02x.\n",
+ (rx_stop_offset >> 6) | CR1_STOP_REG_UPDATE);
+ outb((rx_stop_offset >> 6) | CR1_STOP_REG_UPDATE, ioaddr);
+ outb(OP1_SWIT_TO_PORT_0, ioaddr);
+}
+
+static __exit void znet_cleanup (void)
+{
+ if (znet_dev) {
+ struct znet_private *znet = netdev_priv(znet_dev);
+
+ unregister_netdev (znet_dev);
+ kfree (znet->rx_start);
+ kfree (znet->tx_start);
+ free_netdev (znet_dev);
+ }
+}
+
+module_init (znet_probe);
+module_exit (znet_cleanup);