i825xx: Move the Intel 82586/82593/82596 based drivers

Move the drivers that use the i82586/i82593/i82596 chipsets into
drivers/net/ethernet/i825xx/ and make the necessary Kconfig and
Makefile changes.  There were 4 3Com drivers which were initially
moved into 3com/, which now reside in i825xx since they all used
the i82586 chip.

CC: Philip Blundell <philb@gnu.org>
CC: Russell King <linux@arm.linux.org.uk>
CC: <aris@cathedrallabs.org>
CC: Donald Becker <becker@scyld.com>
CC: Chris Beauregard <cpbeaure@undergrad.math.uwaterloo.ca>
CC: Richard Procter <rnp@paradise.net.nz>
CC: Andries Brouwer <aeb@cwi.nl>
CC: "M.Hipp" <hippm@informatik.uni-tuebingen.de>
CC: Richard Hirst <richard@sleepie.demon.co.uk>
CC: Sam Creasey <sammy@oh.verio.com>
CC: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig
index ed5836c..d0a8fa8 100644
--- a/drivers/net/ethernet/Kconfig
+++ b/drivers/net/ethernet/Kconfig
@@ -17,6 +17,7 @@
 source "drivers/net/ethernet/broadcom/Kconfig"
 source "drivers/net/ethernet/chelsio/Kconfig"
 source "drivers/net/ethernet/intel/Kconfig"
+source "drivers/net/ethernet/i825xx/Kconfig"
 source "drivers/net/ethernet/qlogic/Kconfig"
 source "drivers/net/ethernet/smsc/Kconfig"
 
diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile
index 983fd27..6d3276a 100644
--- a/drivers/net/ethernet/Makefile
+++ b/drivers/net/ethernet/Makefile
@@ -8,5 +8,6 @@
 obj-$(CONFIG_NET_VENDOR_BROADCOM) += broadcom/
 obj-$(CONFIG_NET_VENDOR_CHELSIO) += chelsio/
 obj-$(CONFIG_NET_VENDOR_INTEL) += intel/
+obj-$(CONFIG_NET_VENDOR_I825XX) += i825xx/
 obj-$(CONFIG_NET_VENDOR_QLOGIC) += qlogic/
 obj-$(CONFIG_NET_VENDOR_SMSC) += smsc/
diff --git a/drivers/net/ethernet/i825xx/3c505.c b/drivers/net/ethernet/i825xx/3c505.c
new file mode 100644
index 0000000..88d766e
--- /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_multicast_list = 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 0000000..04df2a9
--- /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 0000000..1e94555
--- /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 0000000..bc0d1a1
--- /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_multicast_list = 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 0000000..6956441
--- /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 0000000..d9d056d
--- /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_multicast_list = 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 0000000..d693b8d
--- /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 0000000..be1f197
--- /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_multicast_list = 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 0000000..5c30a5b
--- /dev/null
+++ b/drivers/net/ethernet/i825xx/Kconfig
@@ -0,0 +1,182 @@
+#
+# Intel 82596/82593/82596 network device configuration
+#
+
+config NET_VENDOR_I825XX
+	bool "Intel (82586/82593/82596) devices"
+	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 0000000..f68a369
--- /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 0000000..dfeb006
--- /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_multicast_list = 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 0000000..a192285
--- /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_multicast_list = 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 0000000..dc9c6ea
--- /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 0000000..b00781c
--- /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_multicast_list	= 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 0000000..3a5830a
--- /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 0000000..6eba352
--- /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 0000000..9e04289
--- /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_multicast_list	= 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 0000000..385a953
--- /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_multicast_list = 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 0000000..d973fc6
--- /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_multicast_list = 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 0000000..0a03b28
--- /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 0000000..6b2a888
--- /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 0000000..b6ae53b
--- /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_multicast_list	= 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 0000000..93346f0
--- /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 0000000..8b88817
--- /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_multicast_list = 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);