8390: Move the 8390 related drivers

Moves the drivers for the National Semi-conductor 8390 chipset into
drivers/net/ethernet/8390/ and the necessary Kconfig and Makefile
changes.

CC: Donald Becker <becker@scyld.com>
CC: Paul Gortmaker <paul.gortmaker@windriver.com>
CC: Alain Malek <alain.malek@cryogen.com>
CC: Peter De Schrijver <p2@mind.be>
CC: "David Huggins-Daines" <dhd@debian.org>
CC: Wim Dumon <wimpie@kotnet.org>
CC: Yoshinori Sato <ysato@users.sourceforge.jp>
CC: David Hinds <dahinds@users.sourceforge.net>
CC: Russell King <linux@arm.linux.org.uk>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
diff --git a/drivers/net/ethernet/8390/3c503.c b/drivers/net/ethernet/8390/3c503.c
new file mode 100644
index 0000000..84e68f1
--- /dev/null
+++ b/drivers/net/ethernet/8390/3c503.c
@@ -0,0 +1,778 @@
+/* 3c503.c: A shared-memory NS8390 ethernet driver for linux. */
+/*
+    Written 1992-94 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
+
+
+    This driver should work with the 3c503 and 3c503/16.  It should be used
+    in shared memory mode for best performance, although it may also work
+    in programmed-I/O mode.
+
+    Sources:
+    EtherLink II Technical Reference Manual,
+    EtherLink II/16 Technical Reference Manual Supplement,
+    3Com Corporation, 5400 Bayfront Plaza, Santa Clara CA 95052-8145
+
+    The Crynwr 3c503 packet driver.
+
+    Changelog:
+
+    Paul Gortmaker	: add support for the 2nd 8kB of RAM on 16 bit cards.
+    Paul Gortmaker	: multiple card support for module users.
+    rjohnson@analogic.com : Fix up PIO interface for efficient operation.
+    Jeff Garzik		: ethtool support
+
+*/
+
+#define DRV_NAME	"3c503"
+#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";
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/ethtool.h>
+
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/byteorder.h>
+
+#include "8390.h"
+#include "3c503.h"
+#define WRD_COUNT 4
+
+static int el2_pio_probe(struct net_device *dev);
+static int el2_probe1(struct net_device *dev, int ioaddr);
+
+/* A zero-terminated list of I/O addresses to be probed in PIO mode. */
+static unsigned int netcard_portlist[] __initdata =
+	{ 0x300,0x310,0x330,0x350,0x250,0x280,0x2a0,0x2e0,0};
+
+#define EL2_IO_EXTENT	16
+
+static int el2_open(struct net_device *dev);
+static int el2_close(struct net_device *dev);
+static void el2_reset_8390(struct net_device *dev);
+static void el2_init_card(struct net_device *dev);
+static void el2_block_output(struct net_device *dev, int count,
+			     const unsigned char *buf, int start_page);
+static void el2_block_input(struct net_device *dev, int count, struct sk_buff *skb,
+			   int ring_offset);
+static void el2_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
+			 int ring_page);
+static const struct ethtool_ops netdev_ethtool_ops;
+
+
+/* This routine probes for a memory-mapped 3c503 board by looking for
+   the "location register" at the end of the jumpered boot PROM space.
+   This works even if a PROM isn't there.
+
+   If the ethercard isn't found there is an optional probe for
+   ethercard jumpered to programmed-I/O mode.
+   */
+static int __init do_el2_probe(struct net_device *dev)
+{
+    int *addr, addrs[] = { 0xddffe, 0xd9ffe, 0xcdffe, 0xc9ffe, 0};
+    int base_addr = dev->base_addr;
+    int irq = dev->irq;
+
+    if (base_addr > 0x1ff)	/* Check a single specified location. */
+	return el2_probe1(dev, base_addr);
+    else if (base_addr != 0)		/* Don't probe at all. */
+	return -ENXIO;
+
+    for (addr = addrs; *addr; addr++) {
+	void __iomem *p = ioremap(*addr, 1);
+	unsigned base_bits;
+	int i;
+
+	if (!p)
+		continue;
+	base_bits = readb(p);
+	iounmap(p);
+	i = ffs(base_bits) - 1;
+	if (i == -1 || base_bits != (1 << i))
+	    continue;
+	if (el2_probe1(dev, netcard_portlist[i]) == 0)
+	    return 0;
+	dev->irq = irq;
+    }
+#if ! defined(no_probe_nonshared_memory)
+    return el2_pio_probe(dev);
+#else
+    return -ENODEV;
+#endif
+}
+
+/*  Try all of the locations that aren't obviously empty.  This touches
+    a lot of locations, and is much riskier than the code above. */
+static int __init
+el2_pio_probe(struct net_device *dev)
+{
+    int i;
+    int base_addr = dev->base_addr;
+    int irq = dev->irq;
+
+    if (base_addr > 0x1ff)	/* Check a single specified location. */
+	return el2_probe1(dev, base_addr);
+    else if (base_addr != 0)	/* Don't probe at all. */
+	return -ENXIO;
+
+    for (i = 0; netcard_portlist[i]; i++) {
+	if (el2_probe1(dev, netcard_portlist[i]) == 0)
+	    return 0;
+	dev->irq = irq;
+    }
+
+    return -ENODEV;
+}
+
+#ifndef MODULE
+struct net_device * __init el2_probe(int unit)
+{
+	struct net_device *dev = alloc_eip_netdev();
+	int err;
+
+	if (!dev)
+		return ERR_PTR(-ENOMEM);
+
+	sprintf(dev->name, "eth%d", unit);
+	netdev_boot_setup_check(dev);
+
+	err = do_el2_probe(dev);
+	if (err)
+		goto out;
+	return dev;
+out:
+	free_netdev(dev);
+	return ERR_PTR(err);
+}
+#endif
+
+static const struct net_device_ops el2_netdev_ops = {
+	.ndo_open		= el2_open,
+	.ndo_stop		= el2_close,
+
+	.ndo_start_xmit		= eip_start_xmit,
+	.ndo_tx_timeout		= eip_tx_timeout,
+	.ndo_get_stats		= eip_get_stats,
+	.ndo_set_multicast_list = eip_set_multicast_list,
+	.ndo_validate_addr	= eth_validate_addr,
+	.ndo_set_mac_address 	= eth_mac_addr,
+	.ndo_change_mtu		= eth_change_mtu,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	.ndo_poll_controller 	= eip_poll,
+#endif
+};
+
+/* Probe for the Etherlink II card at I/O port base IOADDR,
+   returning non-zero on success.  If found, set the station
+   address and memory parameters in DEVICE. */
+static int __init
+el2_probe1(struct net_device *dev, int ioaddr)
+{
+    int i, iobase_reg, membase_reg, saved_406, wordlength, retval;
+    static unsigned version_printed;
+    unsigned long vendor_id;
+
+    if (!request_region(ioaddr, EL2_IO_EXTENT, DRV_NAME))
+	return -EBUSY;
+
+    if (!request_region(ioaddr + 0x400, 8, DRV_NAME)) {
+	retval = -EBUSY;
+	goto out;
+    }
+
+    /* Reset and/or avoid any lurking NE2000 */
+    if (inb(ioaddr + 0x408) == 0xff) {
+    	mdelay(1);
+	retval = -ENODEV;
+	goto out1;
+    }
+
+    /* We verify that it's a 3C503 board by checking the first three octets
+       of its ethernet address. */
+    iobase_reg = inb(ioaddr+0x403);
+    membase_reg = inb(ioaddr+0x404);
+    /* ASIC location registers should be 0 or have only a single bit set. */
+    if ((iobase_reg  & (iobase_reg - 1)) ||
+	(membase_reg & (membase_reg - 1))) {
+	retval = -ENODEV;
+	goto out1;
+    }
+    saved_406 = inb_p(ioaddr + 0x406);
+    outb_p(ECNTRL_RESET|ECNTRL_THIN, ioaddr + 0x406); /* Reset it... */
+    outb_p(ECNTRL_THIN, ioaddr + 0x406);
+    /* Map the station addr PROM into the lower I/O ports. We now check
+       for both the old and new 3Com prefix */
+    outb(ECNTRL_SAPROM|ECNTRL_THIN, ioaddr + 0x406);
+    vendor_id = inb(ioaddr)*0x10000 + inb(ioaddr + 1)*0x100 + inb(ioaddr + 2);
+    if ((vendor_id != OLD_3COM_ID) && (vendor_id != NEW_3COM_ID)) {
+	/* Restore the register we frobbed. */
+	outb(saved_406, ioaddr + 0x406);
+	retval = -ENODEV;
+	goto out1;
+    }
+
+    if (ei_debug  &&  version_printed++ == 0)
+	pr_debug("%s", version);
+
+    dev->base_addr = ioaddr;
+
+    pr_info("%s: 3c503 at i/o base %#3x, node ", dev->name, ioaddr);
+
+    /* Retrieve and print the ethernet address. */
+    for (i = 0; i < 6; i++)
+	dev->dev_addr[i] = inb(ioaddr + i);
+    pr_cont("%pM", dev->dev_addr);
+
+    /* Map the 8390 back into the window. */
+    outb(ECNTRL_THIN, ioaddr + 0x406);
+
+    /* Check for EL2/16 as described in tech. man. */
+    outb_p(E8390_PAGE0, ioaddr + E8390_CMD);
+    outb_p(0, ioaddr + EN0_DCFG);
+    outb_p(E8390_PAGE2, ioaddr + E8390_CMD);
+    wordlength = inb_p(ioaddr + EN0_DCFG) & ENDCFG_WTS;
+    outb_p(E8390_PAGE0, ioaddr + E8390_CMD);
+
+    /* Probe for, turn on and clear the board's shared memory. */
+    if (ei_debug > 2)
+	pr_cont(" memory jumpers %2.2x ", membase_reg);
+    outb(EGACFR_NORM, ioaddr + 0x405);	/* Enable RAM */
+
+    /* This should be probed for (or set via an ioctl()) at run-time.
+       Right now we use a sleazy hack to pass in the interface number
+       at boot-time via the low bits of the mem_end field.  That value is
+       unused, and the low bits would be discarded even if it was used. */
+#if defined(EI8390_THICK) || defined(EL2_AUI)
+    ei_status.interface_num = 1;
+#else
+    ei_status.interface_num = dev->mem_end & 0xf;
+#endif
+    pr_cont(", using %sternal xcvr.\n", ei_status.interface_num == 0 ? "in" : "ex");
+
+    if ((membase_reg & 0xf0) == 0) {
+	dev->mem_start = 0;
+	ei_status.name = "3c503-PIO";
+	ei_status.mem = NULL;
+    } else {
+	dev->mem_start = ((membase_reg & 0xc0) ? 0xD8000 : 0xC8000) +
+	    ((membase_reg & 0xA0) ? 0x4000 : 0);
+#define EL2_MEMSIZE (EL2_MB1_STOP_PG - EL2_MB1_START_PG)*256
+	ei_status.mem = ioremap(dev->mem_start, EL2_MEMSIZE);
+
+#ifdef EL2MEMTEST
+	/* This has never found an error, but someone might care.
+	   Note that it only tests the 2nd 8kB on 16kB 3c503/16
+	   cards between card addr. 0x2000 and 0x3fff. */
+	{			/* Check the card's memory. */
+	    void __iomem *mem_base = ei_status.mem;
+	    unsigned int test_val = 0xbbadf00d;
+	    writel(0xba5eba5e, mem_base);
+	    for (i = sizeof(test_val); i < EL2_MEMSIZE; i+=sizeof(test_val)) {
+		writel(test_val, mem_base + i);
+		if (readl(mem_base) != 0xba5eba5e ||
+		    readl(mem_base + i) != test_val) {
+		    pr_warning("3c503: memory failure or memory address conflict.\n");
+		    dev->mem_start = 0;
+		    ei_status.name = "3c503-PIO";
+		    iounmap(mem_base);
+		    ei_status.mem = NULL;
+		    break;
+		}
+		test_val += 0x55555555;
+		writel(0, mem_base + i);
+	    }
+	}
+#endif  /* EL2MEMTEST */
+
+	if (dev->mem_start)
+		dev->mem_end = dev->mem_start + EL2_MEMSIZE;
+
+	if (wordlength) {	/* No Tx pages to skip over to get to Rx */
+		ei_status.priv = 0;
+		ei_status.name = "3c503/16";
+	} else {
+		ei_status.priv = TX_PAGES * 256;
+		ei_status.name = "3c503";
+	}
+    }
+
+    /*
+	Divide up the memory on the card. This is the same regardless of
+	whether shared-mem or PIO is used. For 16 bit cards (16kB RAM),
+	we use the entire 8k of bank1 for an Rx ring. We only use 3k
+	of the bank0 for 2 full size Tx packet slots. For 8 bit cards,
+	(8kB RAM) we use 3kB of bank1 for two Tx slots, and the remaining
+	5kB for an Rx ring.  */
+
+    if (wordlength) {
+	ei_status.tx_start_page = EL2_MB0_START_PG;
+	ei_status.rx_start_page = EL2_MB1_START_PG;
+    } else {
+	ei_status.tx_start_page = EL2_MB1_START_PG;
+	ei_status.rx_start_page = EL2_MB1_START_PG + TX_PAGES;
+    }
+
+    /* Finish setting the board's parameters. */
+    ei_status.stop_page = EL2_MB1_STOP_PG;
+    ei_status.word16 = wordlength;
+    ei_status.reset_8390 = el2_reset_8390;
+    ei_status.get_8390_hdr = el2_get_8390_hdr;
+    ei_status.block_input = el2_block_input;
+    ei_status.block_output = el2_block_output;
+
+    if (dev->irq == 2)
+	dev->irq = 9;
+    else if (dev->irq > 5 && dev->irq != 9) {
+	pr_warning("3c503: configured interrupt %d invalid, will use autoIRQ.\n",
+	       dev->irq);
+	dev->irq = 0;
+    }
+
+    ei_status.saved_irq = dev->irq;
+
+    dev->netdev_ops = &el2_netdev_ops;
+    dev->ethtool_ops = &netdev_ethtool_ops;
+
+    retval = register_netdev(dev);
+    if (retval)
+	goto out1;
+
+    if (dev->mem_start)
+	pr_info("%s: %s - %dkB RAM, 8kB shared mem window at %#6lx-%#6lx.\n",
+		dev->name, ei_status.name, (wordlength+1)<<3,
+		dev->mem_start, dev->mem_end-1);
+
+    else
+    {
+	ei_status.tx_start_page = EL2_MB1_START_PG;
+	ei_status.rx_start_page = EL2_MB1_START_PG + TX_PAGES;
+	pr_info("%s: %s, %dkB RAM, using programmed I/O (REJUMPER for SHARED MEMORY).\n",
+	       dev->name, ei_status.name, (wordlength+1)<<3);
+    }
+    release_region(ioaddr + 0x400, 8);
+    return 0;
+out1:
+    release_region(ioaddr + 0x400, 8);
+out:
+    release_region(ioaddr, EL2_IO_EXTENT);
+    return retval;
+}
+
+static irqreturn_t el2_probe_interrupt(int irq, void *seen)
+{
+	*(bool *)seen = true;
+	return IRQ_HANDLED;
+}
+
+static int
+el2_open(struct net_device *dev)
+{
+    int retval;
+
+    if (dev->irq < 2) {
+	static const int irqlist[] = {5, 9, 3, 4, 0};
+	const int *irqp = irqlist;
+
+	outb(EGACFR_NORM, E33G_GACFR);	/* Enable RAM and interrupts. */
+	do {
+		bool seen;
+
+		retval = request_irq(*irqp, el2_probe_interrupt, 0,
+				     dev->name, &seen);
+		if (retval == -EBUSY)
+			continue;
+		if (retval < 0)
+			goto err_disable;
+
+		/* Twinkle the interrupt, and check if it's seen. */
+		seen = false;
+		smp_wmb();
+		outb_p(0x04 << ((*irqp == 9) ? 2 : *irqp), E33G_IDCFR);
+		outb_p(0x00, E33G_IDCFR);
+		msleep(1);
+		free_irq(*irqp, &seen);
+		if (!seen)
+			continue;
+
+		retval = request_irq(dev->irq = *irqp, eip_interrupt, 0,
+				     dev->name, dev);
+		if (retval == -EBUSY)
+			continue;
+		if (retval < 0)
+			goto err_disable;
+		break;
+	} while (*++irqp);
+
+	if (*irqp == 0) {
+	err_disable:
+	    outb(EGACFR_IRQOFF, E33G_GACFR);	/* disable interrupts. */
+	    return -EAGAIN;
+	}
+    } else {
+	if ((retval = request_irq(dev->irq, eip_interrupt, 0, dev->name, dev))) {
+	    return retval;
+	}
+    }
+
+    el2_init_card(dev);
+    eip_open(dev);
+    return 0;
+}
+
+static int
+el2_close(struct net_device *dev)
+{
+    free_irq(dev->irq, dev);
+    dev->irq = ei_status.saved_irq;
+    outb(EGACFR_IRQOFF, E33G_GACFR);	/* disable interrupts. */
+
+    eip_close(dev);
+    return 0;
+}
+
+/* This is called whenever we have a unrecoverable failure:
+       transmit timeout
+       Bad ring buffer packet header
+ */
+static void
+el2_reset_8390(struct net_device *dev)
+{
+    if (ei_debug > 1) {
+	pr_debug("%s: Resetting the 3c503 board...", dev->name);
+	pr_cont(" %#lx=%#02x %#lx=%#02x %#lx=%#02x...", E33G_IDCFR, inb(E33G_IDCFR),
+	       E33G_CNTRL, inb(E33G_CNTRL), E33G_GACFR, inb(E33G_GACFR));
+    }
+    outb_p(ECNTRL_RESET|ECNTRL_THIN, E33G_CNTRL);
+    ei_status.txing = 0;
+    outb_p(ei_status.interface_num==0 ? ECNTRL_THIN : ECNTRL_AUI, E33G_CNTRL);
+    el2_init_card(dev);
+    if (ei_debug > 1)
+	pr_cont("done\n");
+}
+
+/* Initialize the 3c503 GA registers after a reset. */
+static void
+el2_init_card(struct net_device *dev)
+{
+    /* Unmap the station PROM and select the DIX or BNC connector. */
+    outb_p(ei_status.interface_num==0 ? ECNTRL_THIN : ECNTRL_AUI, E33G_CNTRL);
+
+    /* Set ASIC copy of rx's first and last+1 buffer pages */
+    /* These must be the same as in the 8390. */
+    outb(ei_status.rx_start_page, E33G_STARTPG);
+    outb(ei_status.stop_page,  E33G_STOPPG);
+
+    /* Point the vector pointer registers somewhere ?harmless?. */
+    outb(0xff, E33G_VP2);	/* Point at the ROM restart location 0xffff0 */
+    outb(0xff, E33G_VP1);
+    outb(0x00, E33G_VP0);
+    /* Turn off all interrupts until we're opened. */
+    outb_p(0x00,  dev->base_addr + EN0_IMR);
+    /* Enable IRQs iff started. */
+    outb(EGACFR_NORM, E33G_GACFR);
+
+    /* Set the interrupt line. */
+    outb_p((0x04 << (dev->irq == 9 ? 2 : dev->irq)), E33G_IDCFR);
+    outb_p((WRD_COUNT << 1), E33G_DRQCNT);	/* Set burst size to 8 */
+    outb_p(0x20, E33G_DMAAH);	/* Put a valid addr in the GA DMA */
+    outb_p(0x00, E33G_DMAAL);
+    return;			/* We always succeed */
+}
+
+/*
+ * Either use the shared memory (if enabled on the board) or put the packet
+ * out through the ASIC FIFO.
+ */
+static void
+el2_block_output(struct net_device *dev, int count,
+		 const unsigned char *buf, int start_page)
+{
+    unsigned short int *wrd;
+    int boguscount;		/* timeout counter */
+    unsigned short word;	/* temporary for better machine code */
+    void __iomem *base = ei_status.mem;
+
+    if (ei_status.word16)      /* Tx packets go into bank 0 on EL2/16 card */
+	outb(EGACFR_RSEL|EGACFR_TCM, E33G_GACFR);
+    else
+	outb(EGACFR_NORM, E33G_GACFR);
+
+    if (base) {	/* Shared memory transfer */
+	memcpy_toio(base + ((start_page - ei_status.tx_start_page) << 8),
+			buf, count);
+	outb(EGACFR_NORM, E33G_GACFR);	/* Back to bank1 in case on bank0 */
+	return;
+    }
+
+/*
+ *  No shared memory, put the packet out the other way.
+ *  Set up then start the internal memory transfer to Tx Start Page
+ */
+
+    word = (unsigned short)start_page;
+    outb(word&0xFF, E33G_DMAAH);
+    outb(word>>8, E33G_DMAAL);
+
+    outb_p((ei_status.interface_num ? ECNTRL_AUI : ECNTRL_THIN ) | ECNTRL_OUTPUT
+	   | ECNTRL_START, E33G_CNTRL);
+
+/*
+ *  Here I am going to write data to the FIFO as quickly as possible.
+ *  Note that E33G_FIFOH is defined incorrectly. It is really
+ *  E33G_FIFOL, the lowest port address for both the byte and
+ *  word write. Variable 'count' is NOT checked. Caller must supply a
+ *  valid count. Note that I may write a harmless extra byte to the
+ *  8390 if the byte-count was not even.
+ */
+    wrd = (unsigned short int *) buf;
+    count  = (count + 1) >> 1;
+    for(;;)
+    {
+        boguscount = 0x1000;
+        while ((inb(E33G_STATUS) & ESTAT_DPRDY) == 0)
+        {
+            if(!boguscount--)
+            {
+                pr_notice("%s: FIFO blocked in el2_block_output.\n", dev->name);
+                el2_reset_8390(dev);
+                goto blocked;
+            }
+        }
+        if(count > WRD_COUNT)
+        {
+            outsw(E33G_FIFOH, wrd, WRD_COUNT);
+            wrd   += WRD_COUNT;
+            count -= WRD_COUNT;
+        }
+        else
+        {
+            outsw(E33G_FIFOH, wrd, count);
+            break;
+        }
+    }
+    blocked:;
+    outb_p(ei_status.interface_num==0 ? ECNTRL_THIN : ECNTRL_AUI, E33G_CNTRL);
+}
+
+/* Read the 4 byte, page aligned 8390 specific header. */
+static void
+el2_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
+{
+    int boguscount;
+    void __iomem *base = ei_status.mem;
+    unsigned short word;
+
+    if (base) {       /* Use the shared memory. */
+	void __iomem *hdr_start = base + ((ring_page - EL2_MB1_START_PG)<<8);
+	memcpy_fromio(hdr, hdr_start, sizeof(struct e8390_pkt_hdr));
+	hdr->count = le16_to_cpu(hdr->count);
+	return;
+    }
+
+/*
+ *  No shared memory, use programmed I/O.
+ */
+
+    word = (unsigned short)ring_page;
+    outb(word&0xFF, E33G_DMAAH);
+    outb(word>>8, E33G_DMAAL);
+
+    outb_p((ei_status.interface_num == 0 ? ECNTRL_THIN : ECNTRL_AUI) | ECNTRL_INPUT
+	   | ECNTRL_START, E33G_CNTRL);
+    boguscount = 0x1000;
+    while ((inb(E33G_STATUS) & ESTAT_DPRDY) == 0)
+    {
+        if(!boguscount--)
+        {
+            pr_notice("%s: FIFO blocked in el2_get_8390_hdr.\n", dev->name);
+            memset(hdr, 0x00, sizeof(struct e8390_pkt_hdr));
+            el2_reset_8390(dev);
+            goto blocked;
+        }
+    }
+    insw(E33G_FIFOH, hdr, (sizeof(struct e8390_pkt_hdr))>> 1);
+    blocked:;
+    outb_p(ei_status.interface_num == 0 ? ECNTRL_THIN : ECNTRL_AUI, E33G_CNTRL);
+}
+
+
+static void
+el2_block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset)
+{
+    int boguscount = 0;
+    void __iomem *base = ei_status.mem;
+    unsigned short int *buf;
+    unsigned short word;
+
+    /* Maybe enable shared memory just be to be safe... nahh.*/
+    if (base) {	/* Use the shared memory. */
+	ring_offset -= (EL2_MB1_START_PG<<8);
+	if (ring_offset + count > EL2_MEMSIZE) {
+	    /* We must wrap the input move. */
+	    int semi_count = EL2_MEMSIZE - ring_offset;
+	    memcpy_fromio(skb->data, base + ring_offset, semi_count);
+	    count -= semi_count;
+	    memcpy_fromio(skb->data + semi_count, base + ei_status.priv, count);
+	} else {
+		memcpy_fromio(skb->data, base + ring_offset, count);
+	}
+	return;
+    }
+
+/*
+ *  No shared memory, use programmed I/O.
+ */
+    word = (unsigned short) ring_offset;
+    outb(word>>8, E33G_DMAAH);
+    outb(word&0xFF, E33G_DMAAL);
+
+    outb_p((ei_status.interface_num == 0 ? ECNTRL_THIN : ECNTRL_AUI) | ECNTRL_INPUT
+	   | ECNTRL_START, E33G_CNTRL);
+
+/*
+ *  Here I also try to get data as fast as possible. I am betting that I
+ *  can read one extra byte without clobbering anything in the kernel because
+ *  this would only occur on an odd byte-count and allocation of skb->data
+ *  is word-aligned. Variable 'count' is NOT checked. Caller must check
+ *  for a valid count.
+ *  [This is currently quite safe.... but if one day the 3c503 explodes
+ *   you know where to come looking ;)]
+ */
+
+    buf =  (unsigned short int *) skb->data;
+    count =  (count + 1) >> 1;
+    for(;;)
+    {
+        boguscount = 0x1000;
+        while ((inb(E33G_STATUS) & ESTAT_DPRDY) == 0)
+        {
+            if(!boguscount--)
+            {
+                pr_notice("%s: FIFO blocked in el2_block_input.\n", dev->name);
+                el2_reset_8390(dev);
+                goto blocked;
+            }
+        }
+        if(count > WRD_COUNT)
+        {
+            insw(E33G_FIFOH, buf, WRD_COUNT);
+            buf   += WRD_COUNT;
+            count -= WRD_COUNT;
+        }
+        else
+        {
+            insw(E33G_FIFOH, buf, count);
+            break;
+        }
+    }
+    blocked:;
+    outb_p(ei_status.interface_num == 0 ? ECNTRL_THIN : ECNTRL_AUI, E33G_CNTRL);
+}
+
+
+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 const struct ethtool_ops netdev_ethtool_ops = {
+	.get_drvinfo		= netdev_get_drvinfo,
+};
+
+#ifdef MODULE
+#define MAX_EL2_CARDS	4	/* Max number of EL2 cards per module */
+
+static struct net_device *dev_el2[MAX_EL2_CARDS];
+static int io[MAX_EL2_CARDS];
+static int irq[MAX_EL2_CARDS];
+static int xcvr[MAX_EL2_CARDS];	/* choose int. or ext. xcvr */
+module_param_array(io, int, NULL, 0);
+module_param_array(irq, int, NULL, 0);
+module_param_array(xcvr, int, NULL, 0);
+MODULE_PARM_DESC(io, "I/O base address(es)");
+MODULE_PARM_DESC(irq, "IRQ number(s) (assigned)");
+MODULE_PARM_DESC(xcvr, "transceiver(s) (0=internal, 1=external)");
+MODULE_DESCRIPTION("3Com ISA EtherLink II, II/16 (3c503, 3c503/16) driver");
+MODULE_LICENSE("GPL");
+
+/* This is set up so that only a single autoprobe takes place per call.
+ISA device autoprobes on a running machine are not recommended. */
+int __init
+init_module(void)
+{
+	struct net_device *dev;
+	int this_dev, found = 0;
+
+	for (this_dev = 0; this_dev < MAX_EL2_CARDS; this_dev++) {
+		if (io[this_dev] == 0)  {
+			if (this_dev != 0) break; /* only autoprobe 1st one */
+			pr_notice("3c503.c: Presently autoprobing (not recommended) for a single card.\n");
+		}
+		dev = alloc_eip_netdev();
+		if (!dev)
+			break;
+		dev->irq = irq[this_dev];
+		dev->base_addr = io[this_dev];
+		dev->mem_end = xcvr[this_dev];	/* low 4bits = xcvr sel. */
+		if (do_el2_probe(dev) == 0) {
+			dev_el2[found++] = dev;
+			continue;
+		}
+		free_netdev(dev);
+		pr_warning("3c503.c: No 3c503 card found (i/o = 0x%x).\n", io[this_dev]);
+		break;
+	}
+	if (found)
+		return 0;
+	return -ENXIO;
+}
+
+static void cleanup_card(struct net_device *dev)
+{
+	/* NB: el2_close() handles free_irq */
+	release_region(dev->base_addr, EL2_IO_EXTENT);
+	if (ei_status.mem)
+		iounmap(ei_status.mem);
+}
+
+void __exit
+cleanup_module(void)
+{
+	int this_dev;
+
+	for (this_dev = 0; this_dev < MAX_EL2_CARDS; this_dev++) {
+		struct net_device *dev = dev_el2[this_dev];
+		if (dev) {
+			unregister_netdev(dev);
+			cleanup_card(dev);
+			free_netdev(dev);
+		}
+	}
+}
+#endif /* MODULE */
diff --git a/drivers/net/ethernet/8390/3c503.h b/drivers/net/ethernet/8390/3c503.h
new file mode 100644
index 0000000..e2367b8
--- /dev/null
+++ b/drivers/net/ethernet/8390/3c503.h
@@ -0,0 +1,91 @@
+/* Definitions for the 3Com 3c503 Etherlink 2. */
+/* This file is distributed under the GPL.
+   Many of these names and comments are directly from the Crynwr packet
+   drivers, which are released under the GPL. */
+
+#define EL2H (dev->base_addr + 0x400)
+#define EL2L (dev->base_addr)
+
+/* Vendor unique hardware addr. prefix. 3Com has 2 because they ran
+   out of available addresses on the first one... */
+
+#define OLD_3COM_ID	0x02608c
+#define NEW_3COM_ID	0x0020af
+
+/* Shared memory management parameters. NB: The 8 bit cards have only
+   one bank (MB1) which serves both Tx and Rx packet space. The 16bit
+   cards have 2 banks, MB0 for Tx packets, and MB1 for Rx packets.
+   You choose which bank appears in the sh. mem window with EGACFR_MBSn */
+
+#define EL2_MB0_START_PG	(0x00)	/* EL2/16 Tx packets go in bank 0 */
+#define EL2_MB1_START_PG	(0x20)	/* First page of bank 1 */
+#define EL2_MB1_STOP_PG		(0x40)	/* Last page +1 of bank 1 */
+
+/* 3Com 3c503 ASIC registers */
+#define E33G_STARTPG	(EL2H+0)	/* Start page, matching EN0_STARTPG */
+#define E33G_STOPPG	(EL2H+1)	/* Stop page, must match EN0_STOPPG */
+#define E33G_DRQCNT	(EL2H+2)	/* DMA burst count */
+#define E33G_IOBASE	(EL2H+3)	/* Read of I/O base jumpers. */
+	/* (non-useful, but it also appears at the end of EPROM space) */
+#define E33G_ROMBASE	(EL2H+4)	/* Read of memory base jumpers. */
+#define E33G_GACFR	(EL2H+5)	/* Config/setup bits for the ASIC GA */
+#define E33G_CNTRL	(EL2H+6)	/* Board's main control register */
+#define E33G_STATUS	(EL2H+7)	/* Status on completions. */
+#define E33G_IDCFR	(EL2H+8)	/* Interrupt/DMA config register */
+				/* (Which IRQ to assert, DMA chan to use) */
+#define E33G_DMAAH	(EL2H+9)	/* High byte of DMA address reg */
+#define E33G_DMAAL	(EL2H+10)	/* Low byte of DMA address reg */
+/* "Vector pointer" - if this address matches a read, the EPROM (rather than
+   shared RAM) is mapped into memory space. */
+#define E33G_VP2	(EL2H+11)
+#define E33G_VP1	(EL2H+12)
+#define E33G_VP0	(EL2H+13)
+#define E33G_FIFOH	(EL2H+14)	/* FIFO for programmed I/O moves */
+#define E33G_FIFOL	(EL2H+15)	/* ... low byte of above. */
+
+/* Bits in E33G_CNTRL register: */
+
+#define ECNTRL_RESET	(0x01)	/* Software reset of the ASIC and 8390 */
+#define ECNTRL_THIN	(0x02)	/* Onboard xcvr enable, AUI disable */
+#define ECNTRL_AUI	(0x00)	/* Onboard xcvr disable, AUI enable */
+#define ECNTRL_SAPROM	(0x04)	/* Map the station address prom */
+#define ECNTRL_DBLBFR	(0x20)	/* FIFO configuration bit */
+#define ECNTRL_OUTPUT	(0x40)	/* PC-to-3C503 direction if 1 */
+#define ECNTRL_INPUT	(0x00)	/* 3C503-to-PC direction if 0 */
+#define ECNTRL_START	(0x80)	/* Start the DMA logic */
+
+/* Bits in E33G_STATUS register: */
+
+#define ESTAT_DPRDY	(0x80)	/* Data port (of FIFO) ready */
+#define ESTAT_UFLW	(0x40)	/* Tried to read FIFO when it was empty */
+#define ESTAT_OFLW	(0x20)	/* Tried to write FIFO when it was full */
+#define ESTAT_DTC	(0x10)	/* Terminal Count from PC bus DMA logic */
+#define ESTAT_DIP	(0x08)	/* DMA In Progress */
+
+/* Bits in E33G_GACFR register: */
+
+#define EGACFR_NIM	(0x80)	/* NIC interrupt mask */
+#define EGACFR_TCM	(0x40)	/* DMA term. count interrupt mask */
+#define EGACFR_RSEL	(0x08)	/* Map a bank of card mem into system mem */
+#define EGACFR_MBS2	(0x04)	/* Memory bank select, bit 2. */
+#define EGACFR_MBS1	(0x02)	/* Memory bank select, bit 1. */
+#define EGACFR_MBS0	(0x01)	/* Memory bank select, bit 0. */
+
+#define EGACFR_NORM	(0x49)	/* TCM | RSEL | MBS0 */
+#define EGACFR_IRQOFF	(0xc9)	/* TCM | RSEL | MBS0 | NIM */
+
+/*
+	MBS2	MBS1	MBS0	Sh. mem windows card mem at:
+	----	----	----	-----------------------------
+	0	0	0	0x0000 -- bank 0
+	0	0	1	0x2000 -- bank 1 (only choice for 8bit card)
+	0	1	0	0x4000 -- bank 2, not used
+	0	1	1	0x6000 -- bank 3, not used
+
+There was going to be a 32k card that used bank 2 and 3, but it
+never got produced.
+
+*/
+
+
+/* End of 3C503 parameter definitions */
diff --git a/drivers/net/ethernet/8390/8390.c b/drivers/net/ethernet/8390/8390.c
new file mode 100644
index 0000000..7c7518b
--- /dev/null
+++ b/drivers/net/ethernet/8390/8390.c
@@ -0,0 +1,103 @@
+/* 8390 core for usual drivers */
+
+static const char version[] =
+    "8390.c:v1.10cvs 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
+
+#include "lib8390.c"
+
+int ei_open(struct net_device *dev)
+{
+	return __ei_open(dev);
+}
+EXPORT_SYMBOL(ei_open);
+
+int ei_close(struct net_device *dev)
+{
+	return __ei_close(dev);
+}
+EXPORT_SYMBOL(ei_close);
+
+netdev_tx_t ei_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	return __ei_start_xmit(skb, dev);
+}
+EXPORT_SYMBOL(ei_start_xmit);
+
+struct net_device_stats *ei_get_stats(struct net_device *dev)
+{
+	return __ei_get_stats(dev);
+}
+EXPORT_SYMBOL(ei_get_stats);
+
+void ei_set_multicast_list(struct net_device *dev)
+{
+	__ei_set_multicast_list(dev);
+}
+EXPORT_SYMBOL(ei_set_multicast_list);
+
+void ei_tx_timeout(struct net_device *dev)
+{
+	__ei_tx_timeout(dev);
+}
+EXPORT_SYMBOL(ei_tx_timeout);
+
+irqreturn_t ei_interrupt(int irq, void *dev_id)
+{
+	return __ei_interrupt(irq, dev_id);
+}
+EXPORT_SYMBOL(ei_interrupt);
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+void ei_poll(struct net_device *dev)
+{
+	__ei_poll(dev);
+}
+EXPORT_SYMBOL(ei_poll);
+#endif
+
+const struct net_device_ops ei_netdev_ops = {
+	.ndo_open		= ei_open,
+	.ndo_stop		= ei_close,
+	.ndo_start_xmit		= ei_start_xmit,
+	.ndo_tx_timeout		= ei_tx_timeout,
+	.ndo_get_stats		= ei_get_stats,
+	.ndo_set_multicast_list = ei_set_multicast_list,
+	.ndo_validate_addr	= eth_validate_addr,
+	.ndo_set_mac_address 	= eth_mac_addr,
+	.ndo_change_mtu		= eth_change_mtu,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	.ndo_poll_controller	= ei_poll,
+#endif
+};
+EXPORT_SYMBOL(ei_netdev_ops);
+
+struct net_device *__alloc_ei_netdev(int size)
+{
+	struct net_device *dev = ____alloc_ei_netdev(size);
+	if (dev)
+		dev->netdev_ops = &ei_netdev_ops;
+	return dev;
+}
+EXPORT_SYMBOL(__alloc_ei_netdev);
+
+void NS8390_init(struct net_device *dev, int startp)
+{
+	__NS8390_init(dev, startp);
+}
+EXPORT_SYMBOL(NS8390_init);
+
+#if defined(MODULE)
+
+static int __init ns8390_module_init(void)
+{
+	return 0;
+}
+
+static void __exit ns8390_module_exit(void)
+{
+}
+
+module_init(ns8390_module_init);
+module_exit(ns8390_module_exit);
+#endif /* MODULE */
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/ethernet/8390/8390.h b/drivers/net/ethernet/8390/8390.h
new file mode 100644
index 0000000..58a12e4
--- /dev/null
+++ b/drivers/net/ethernet/8390/8390.h
@@ -0,0 +1,232 @@
+/* Generic NS8390 register definitions. */
+/* This file is part of Donald Becker's 8390 drivers, and is distributed
+   under the same license. Auto-loading of 8390.o only in v2.2 - Paul G.
+   Some of these names and comments originated from the Crynwr
+   packet drivers, which are distributed under the GPL. */
+
+#ifndef _8390_h
+#define _8390_h
+
+#include <linux/if_ether.h>
+#include <linux/ioport.h>
+#include <linux/irqreturn.h>
+#include <linux/skbuff.h>
+
+#define TX_PAGES 12	/* Two Tx slots */
+
+#define ETHER_ADDR_LEN 6
+
+/* The 8390 specific per-packet-header format. */
+struct e8390_pkt_hdr {
+  unsigned char status; /* status */
+  unsigned char next;   /* pointer to next packet. */
+  unsigned short count; /* header + packet length in bytes */
+};
+
+#ifdef notdef
+extern int ei_debug;
+#else
+#define ei_debug 1
+#endif
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+extern void ei_poll(struct net_device *dev);
+extern void eip_poll(struct net_device *dev);
+#endif
+
+
+/* Without I/O delay - non ISA or later chips */
+extern void NS8390_init(struct net_device *dev, int startp);
+extern int ei_open(struct net_device *dev);
+extern int ei_close(struct net_device *dev);
+extern irqreturn_t ei_interrupt(int irq, void *dev_id);
+extern void ei_tx_timeout(struct net_device *dev);
+extern netdev_tx_t ei_start_xmit(struct sk_buff *skb, struct net_device *dev);
+extern void ei_set_multicast_list(struct net_device *dev);
+extern struct net_device_stats *ei_get_stats(struct net_device *dev);
+
+extern const struct net_device_ops ei_netdev_ops;
+
+extern struct net_device *__alloc_ei_netdev(int size);
+static inline struct net_device *alloc_ei_netdev(void)
+{
+	return __alloc_ei_netdev(0);
+}
+
+/* With I/O delay form */
+extern void NS8390p_init(struct net_device *dev, int startp);
+extern int eip_open(struct net_device *dev);
+extern int eip_close(struct net_device *dev);
+extern irqreturn_t eip_interrupt(int irq, void *dev_id);
+extern void eip_tx_timeout(struct net_device *dev);
+extern netdev_tx_t eip_start_xmit(struct sk_buff *skb, struct net_device *dev);
+extern void eip_set_multicast_list(struct net_device *dev);
+extern struct net_device_stats *eip_get_stats(struct net_device *dev);
+
+extern const struct net_device_ops eip_netdev_ops;
+
+extern struct net_device *__alloc_eip_netdev(int size);
+static inline struct net_device *alloc_eip_netdev(void)
+{
+	return __alloc_eip_netdev(0);
+}
+
+/* You have one of these per-board */
+struct ei_device {
+	const char *name;
+	void (*reset_8390)(struct net_device *);
+	void (*get_8390_hdr)(struct net_device *, struct e8390_pkt_hdr *, int);
+	void (*block_output)(struct net_device *, int, const unsigned char *, int);
+	void (*block_input)(struct net_device *, int, struct sk_buff *, int);
+	unsigned long rmem_start;
+	unsigned long rmem_end;
+	void __iomem *mem;
+	unsigned char mcfilter[8];
+	unsigned open:1;
+	unsigned word16:1;  		/* We have the 16-bit (vs 8-bit) version of the card. */
+	unsigned bigendian:1;		/* 16-bit big endian mode. Do NOT */
+					/* set this on random 8390 clones! */
+	unsigned txing:1;		/* Transmit Active */
+	unsigned irqlock:1;		/* 8390's intrs disabled when '1'. */
+	unsigned dmaing:1;		/* Remote DMA Active */
+	unsigned char tx_start_page, rx_start_page, stop_page;
+	unsigned char current_page;	/* Read pointer in buffer  */
+	unsigned char interface_num;	/* Net port (AUI, 10bT.) to use. */
+	unsigned char txqueue;		/* Tx Packet buffer queue length. */
+	short tx1, tx2;			/* Packet lengths for ping-pong tx. */
+	short lasttx;			/* Alpha version consistency check. */
+	unsigned char reg0;		/* Register '0' in a WD8013 */
+	unsigned char reg5;		/* Register '5' in a WD8013 */
+	unsigned char saved_irq;	/* Original dev->irq value. */
+	u32 *reg_offset;		/* Register mapping table */
+	spinlock_t page_lock;		/* Page register locks */
+	unsigned long priv;		/* Private field to store bus IDs etc. */
+#ifdef AX88796_PLATFORM
+	unsigned char rxcr_base;	/* default value for RXCR */
+#endif
+};
+
+/* The maximum number of 8390 interrupt service routines called per IRQ. */
+#define MAX_SERVICE 12
+
+/* The maximum time waited (in jiffies) before assuming a Tx failed. (20ms) */
+#define TX_TIMEOUT (20*HZ/100)
+
+#define ei_status (*(struct ei_device *)netdev_priv(dev))
+
+/* Some generic ethernet register configurations. */
+#define E8390_TX_IRQ_MASK	0xa	/* For register EN0_ISR */
+#define E8390_RX_IRQ_MASK	0x5
+
+#ifdef AX88796_PLATFORM
+#define E8390_RXCONFIG		(ei_status.rxcr_base | 0x04)
+#define E8390_RXOFF		(ei_status.rxcr_base | 0x20)
+#else
+#define E8390_RXCONFIG		0x4	/* EN0_RXCR: broadcasts, no multicast,errors */
+#define E8390_RXOFF		0x20	/* EN0_RXCR: Accept no packets */
+#endif
+
+#define E8390_TXCONFIG		0x00	/* EN0_TXCR: Normal transmit mode */
+#define E8390_TXOFF		0x02	/* EN0_TXCR: Transmitter off */
+
+
+/*  Register accessed at EN_CMD, the 8390 base addr.  */
+#define E8390_STOP	0x01	/* Stop and reset the chip */
+#define E8390_START	0x02	/* Start the chip, clear reset */
+#define E8390_TRANS	0x04	/* Transmit a frame */
+#define E8390_RREAD	0x08	/* Remote read */
+#define E8390_RWRITE	0x10	/* Remote write  */
+#define E8390_NODMA	0x20	/* Remote DMA */
+#define E8390_PAGE0	0x00	/* Select page chip registers */
+#define E8390_PAGE1	0x40	/* using the two high-order bits */
+#define E8390_PAGE2	0x80	/* Page 3 is invalid. */
+
+/*
+ *	Only generate indirect loads given a machine that needs them.
+ *      - removed AMIGA_PCMCIA from this list, handled as ISA io now
+ *	- the _p for generates no delay by default 8390p.c overrides this.
+ */
+
+#ifndef ei_inb
+#define ei_inb(_p)	inb(_p)
+#define ei_outb(_v,_p)	outb(_v,_p)
+#define ei_inb_p(_p)	inb(_p)
+#define ei_outb_p(_v,_p) outb(_v,_p)
+#endif
+
+#ifndef EI_SHIFT
+#define EI_SHIFT(x)	(x)
+#endif
+
+#define E8390_CMD	EI_SHIFT(0x00)  /* The command register (for all pages) */
+/* Page 0 register offsets. */
+#define EN0_CLDALO	EI_SHIFT(0x01)	/* Low byte of current local dma addr  RD */
+#define EN0_STARTPG	EI_SHIFT(0x01)	/* Starting page of ring bfr WR */
+#define EN0_CLDAHI	EI_SHIFT(0x02)	/* High byte of current local dma addr  RD */
+#define EN0_STOPPG	EI_SHIFT(0x02)	/* Ending page +1 of ring bfr WR */
+#define EN0_BOUNDARY	EI_SHIFT(0x03)	/* Boundary page of ring bfr RD WR */
+#define EN0_TSR		EI_SHIFT(0x04)	/* Transmit status reg RD */
+#define EN0_TPSR	EI_SHIFT(0x04)	/* Transmit starting page WR */
+#define EN0_NCR		EI_SHIFT(0x05)	/* Number of collision reg RD */
+#define EN0_TCNTLO	EI_SHIFT(0x05)	/* Low  byte of tx byte count WR */
+#define EN0_FIFO	EI_SHIFT(0x06)	/* FIFO RD */
+#define EN0_TCNTHI	EI_SHIFT(0x06)	/* High byte of tx byte count WR */
+#define EN0_ISR		EI_SHIFT(0x07)	/* Interrupt status reg RD WR */
+#define EN0_CRDALO	EI_SHIFT(0x08)	/* low byte of current remote dma address RD */
+#define EN0_RSARLO	EI_SHIFT(0x08)	/* Remote start address reg 0 */
+#define EN0_CRDAHI	EI_SHIFT(0x09)	/* high byte, current remote dma address RD */
+#define EN0_RSARHI	EI_SHIFT(0x09)	/* Remote start address reg 1 */
+#define EN0_RCNTLO	EI_SHIFT(0x0a)	/* Remote byte count reg WR */
+#define EN0_RCNTHI	EI_SHIFT(0x0b)	/* Remote byte count reg WR */
+#define EN0_RSR		EI_SHIFT(0x0c)	/* rx status reg RD */
+#define EN0_RXCR	EI_SHIFT(0x0c)	/* RX configuration reg WR */
+#define EN0_TXCR	EI_SHIFT(0x0d)	/* TX configuration reg WR */
+#define EN0_COUNTER0	EI_SHIFT(0x0d)	/* Rcv alignment error counter RD */
+#define EN0_DCFG	EI_SHIFT(0x0e)	/* Data configuration reg WR */
+#define EN0_COUNTER1	EI_SHIFT(0x0e)	/* Rcv CRC error counter RD */
+#define EN0_IMR		EI_SHIFT(0x0f)	/* Interrupt mask reg WR */
+#define EN0_COUNTER2	EI_SHIFT(0x0f)	/* Rcv missed frame error counter RD */
+
+/* Bits in EN0_ISR - Interrupt status register */
+#define ENISR_RX	0x01	/* Receiver, no error */
+#define ENISR_TX	0x02	/* Transmitter, no error */
+#define ENISR_RX_ERR	0x04	/* Receiver, with error */
+#define ENISR_TX_ERR	0x08	/* Transmitter, with error */
+#define ENISR_OVER	0x10	/* Receiver overwrote the ring */
+#define ENISR_COUNTERS	0x20	/* Counters need emptying */
+#define ENISR_RDC	0x40	/* remote dma complete */
+#define ENISR_RESET	0x80	/* Reset completed */
+#define ENISR_ALL	0x3f	/* Interrupts we will enable */
+
+/* Bits in EN0_DCFG - Data config register */
+#define ENDCFG_WTS	0x01	/* word transfer mode selection */
+#define ENDCFG_BOS	0x02	/* byte order selection */
+
+/* Page 1 register offsets. */
+#define EN1_PHYS   EI_SHIFT(0x01)	/* This board's physical enet addr RD WR */
+#define EN1_PHYS_SHIFT(i)  EI_SHIFT(i+1) /* Get and set mac address */
+#define EN1_CURPAG EI_SHIFT(0x07)	/* Current memory page RD WR */
+#define EN1_MULT   EI_SHIFT(0x08)	/* Multicast filter mask array (8 bytes) RD WR */
+#define EN1_MULT_SHIFT(i)  EI_SHIFT(8+i) /* Get and set multicast filter */
+
+/* Bits in received packet status byte and EN0_RSR*/
+#define ENRSR_RXOK	0x01	/* Received a good packet */
+#define ENRSR_CRC	0x02	/* CRC error */
+#define ENRSR_FAE	0x04	/* frame alignment error */
+#define ENRSR_FO	0x08	/* FIFO overrun */
+#define ENRSR_MPA	0x10	/* missed pkt */
+#define ENRSR_PHY	0x20	/* physical/multicast address */
+#define ENRSR_DIS	0x40	/* receiver disable. set in monitor mode */
+#define ENRSR_DEF	0x80	/* deferring */
+
+/* Transmitted packet status, EN0_TSR. */
+#define ENTSR_PTX 0x01	/* Packet transmitted without error */
+#define ENTSR_ND  0x02	/* The transmit wasn't deferred. */
+#define ENTSR_COL 0x04	/* The transmit collided at least once. */
+#define ENTSR_ABT 0x08  /* The transmit collided 16 times, and was deferred. */
+#define ENTSR_CRS 0x10	/* The carrier sense was lost. */
+#define ENTSR_FU  0x20  /* A "FIFO underrun" occurred during transmit. */
+#define ENTSR_CDH 0x40	/* The collision detect "heartbeat" signal was lost. */
+#define ENTSR_OWC 0x80  /* There was an out-of-window collision. */
+
+#endif /* _8390_h */
diff --git a/drivers/net/ethernet/8390/8390p.c b/drivers/net/ethernet/8390/8390p.c
new file mode 100644
index 0000000..a2a64ea
--- /dev/null
+++ b/drivers/net/ethernet/8390/8390p.c
@@ -0,0 +1,105 @@
+/* 8390 core for ISA devices needing bus delays */
+
+static const char version[] =
+    "8390p.c:v1.10cvs 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
+
+#define ei_inb(_p)	inb(_p)
+#define ei_outb(_v, _p)	outb(_v, _p)
+#define ei_inb_p(_p)	inb_p(_p)
+#define ei_outb_p(_v, _p) outb_p(_v, _p)
+
+#include "lib8390.c"
+
+int eip_open(struct net_device *dev)
+{
+	return __ei_open(dev);
+}
+EXPORT_SYMBOL(eip_open);
+
+int eip_close(struct net_device *dev)
+{
+	return __ei_close(dev);
+}
+EXPORT_SYMBOL(eip_close);
+
+netdev_tx_t eip_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	return __ei_start_xmit(skb, dev);
+}
+EXPORT_SYMBOL(eip_start_xmit);
+
+struct net_device_stats *eip_get_stats(struct net_device *dev)
+{
+	return __ei_get_stats(dev);
+}
+EXPORT_SYMBOL(eip_get_stats);
+
+void eip_set_multicast_list(struct net_device *dev)
+{
+	__ei_set_multicast_list(dev);
+}
+EXPORT_SYMBOL(eip_set_multicast_list);
+
+void eip_tx_timeout(struct net_device *dev)
+{
+	__ei_tx_timeout(dev);
+}
+EXPORT_SYMBOL(eip_tx_timeout);
+
+irqreturn_t eip_interrupt(int irq, void *dev_id)
+{
+	return __ei_interrupt(irq, dev_id);
+}
+EXPORT_SYMBOL(eip_interrupt);
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+void eip_poll(struct net_device *dev)
+{
+	__ei_poll(dev);
+}
+EXPORT_SYMBOL(eip_poll);
+#endif
+
+const struct net_device_ops eip_netdev_ops = {
+	.ndo_open		= eip_open,
+	.ndo_stop		= eip_close,
+	.ndo_start_xmit		= eip_start_xmit,
+	.ndo_tx_timeout		= eip_tx_timeout,
+	.ndo_get_stats		= eip_get_stats,
+	.ndo_set_multicast_list = eip_set_multicast_list,
+	.ndo_validate_addr	= eth_validate_addr,
+	.ndo_set_mac_address 	= eth_mac_addr,
+	.ndo_change_mtu		= eth_change_mtu,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	.ndo_poll_controller	= eip_poll,
+#endif
+};
+EXPORT_SYMBOL(eip_netdev_ops);
+
+struct net_device *__alloc_eip_netdev(int size)
+{
+	struct net_device *dev = ____alloc_ei_netdev(size);
+	if (dev)
+		dev->netdev_ops = &eip_netdev_ops;
+	return dev;
+}
+EXPORT_SYMBOL(__alloc_eip_netdev);
+
+void NS8390p_init(struct net_device *dev, int startp)
+{
+	__NS8390_init(dev, startp);
+}
+EXPORT_SYMBOL(NS8390p_init);
+
+static int __init NS8390p_init_module(void)
+{
+	return 0;
+}
+
+static void __exit NS8390p_cleanup_module(void)
+{
+}
+
+module_init(NS8390p_init_module);
+module_exit(NS8390p_cleanup_module);
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/ethernet/8390/Kconfig b/drivers/net/ethernet/8390/Kconfig
new file mode 100644
index 0000000..5cd53f1
--- /dev/null
+++ b/drivers/net/ethernet/8390/Kconfig
@@ -0,0 +1,348 @@
+#
+# 8390 device configuration
+#
+
+config NET_VENDOR_8390
+	bool "National Semi-conductor 8390 devices"
+	depends on AMIGA_PCMCIA || PCI || SUPERH || ISA || MCA || EISA ||  \
+		   MAC || M32R || MACH_TX49XX || MCA_LEGACY || H8300 ||  \
+		   ARM || MIPS || ZORRO || PCMCIA || 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 doesn't directly affect the
+	  kernel: saying N will just cause the configurator to skip all
+	  the questions about Western Digital cards. If you say Y, you will be
+	  asked for your specific card in the following questions.
+
+if NET_VENDOR_8390
+
+config EL2
+	tristate "3c503 \"EtherLink II\" support"
+	depends on ISA
+	select CRC32
+	---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 3c503.
+
+config AC3200
+	tristate "Ansel Communications EISA 3200 support (EXPERIMENTAL)"
+	depends on PCI && (ISA || EISA) && EXPERIMENTAL
+	select CRC32
+	---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 ac3200.
+
+config PCMCIA_AXNET
+	tristate "Asix AX88190 PCMCIA support"
+	depends on PCMCIA
+	---help---
+	  Say Y here if you intend to attach an Asix AX88190-based PCMCIA
+	  (PC-card) Fast Ethernet card to your computer.  These cards are
+	  nearly NE2000 compatible but need a separate driver due to a few
+	  misfeatures.
+
+	  To compile this driver as a module, choose M here: the module will be
+	  called axnet_cs.  If unsure, say N.
+
+config AX88796
+	tristate "ASIX AX88796 NE2000 clone support"
+	depends on (ARM || MIPS || SUPERH)
+	select PHYLIB
+	select MDIO_BITBANG
+	---help---
+	  AX88796 driver, using platform bus to provide
+	  chip detection and resources
+
+config AX88796_93CX6
+	bool "ASIX AX88796 external 93CX6 eeprom support"
+	depends on AX88796
+	select EEPROM_93CX6
+	---help---
+	  Select this if your platform comes with an external 93CX6 eeprom.
+
+config E2100
+	tristate "Cabletron E21xx support"
+	depends on ISA
+	select CRC32
+	---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 e2100.
+
+config ES3210
+	tristate "Racal-Interlan EISA ES3210 support (EXPERIMENTAL)"
+	depends on PCI && EISA && EXPERIMENTAL
+	select CRC32
+	---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 es3210.
+
+config HPLAN_PLUS
+	tristate "HP PCLAN+ (27247B and 27252A) support"
+	depends on ISA
+	select CRC32
+	---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 hp-plus.
+
+config HPLAN
+	tristate "HP PCLAN (27245 and other 27xxx series) support"
+	depends on ISA
+	select CRC32
+	---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 hp.
+
+config HYDRA
+	tristate "Hydra support"
+	depends on ZORRO
+	select CRC32
+	---help---
+	  If you have a Hydra Ethernet adapter, say Y. Otherwise, say N.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called hydra.
+
+config ARM_ETHERH
+	tristate "I-cubed EtherH/ANT EtherM support"
+	depends on ARM && ARCH_ACORN
+	select CRC32
+	---help---
+	  If you have an Acorn system with one of these network cards, you
+	  should say Y to this option if you wish to use it with Linux.
+
+config LNE390
+	tristate "Mylex EISA LNE390A/B support (EXPERIMENTAL)"
+	depends on PCI && EISA && EXPERIMENTAL
+	select CRC32
+	---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 lne390.
+
+config MAC8390
+	bool "Macintosh NS 8390 based ethernet cards"
+	depends on MAC
+	select CRC32
+	---help---
+	  If you want to include a driver to support Nubus or LC-PDS
+	  Ethernet cards using an NS8390 chipset or its equivalent, say Y
+	  and read the Ethernet-HOWTO, available from
+	  <http://www.tldp.org/docs.html#howto>.
+
+config NE2000
+	tristate "NE2000/NE1000 support"
+	depends on (ISA || (Q40 && m) || M32R || MACH_TX49XX)
+	select CRC32
+	---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>.  Many Ethernet cards
+	  without a specific driver are compatible with NE2000.
+
+	  If you have a PCI NE2000 card however, say N here and Y to "PCI
+	  NE2000 and clone support" under "EISA, VLB, PCI and on board
+	  controllers" below. If you have a NE2000 card and are running on
+	  an MCA system (a bus system used on some IBM PS/2 computers and
+	  laptops), say N here and Y to "NE/2 (ne2000 MCA version) support",
+	  below.
+
+	  To compile this driver as a module, choose M here. The module
+	  will be called ne.
+
+config NE2_MCA
+	tristate "NE/2 (ne2000 MCA version) support"
+	depends on MCA_LEGACY
+	select CRC32
+	---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 ne2.
+
+config NE2K_PCI
+	tristate "PCI NE2000 and clones support (see help)"
+	depends on PCI
+	select CRC32
+	---help---
+	  This driver is for NE2000 compatible PCI cards. It will not work
+	  with ISA NE2000 cards (they have their own driver, "NE2000/NE1000
+	  support" below). If you have a PCI NE2000 network (Ethernet) card,
+	  say Y and read the Ethernet-HOWTO, available from
+	  <http://www.tldp.org/docs.html#howto>.
+
+	  This driver also works for the following NE2000 clone cards:
+	  RealTek RTL-8029  Winbond 89C940  Compex RL2000  KTI ET32P2
+	  NetVin NV5000SC   Via 86C926      SureCom NE34   Winbond
+	  Holtek HT80232    Holtek HT80229
+
+	  To compile this driver as a module, choose M here. The module
+	  will be called ne2k-pci.
+
+config APNE
+	tristate "PCMCIA NE2000 support"
+	depends on AMIGA_PCMCIA
+	select CRC32
+	---help---
+	  If you have a PCMCIA NE2000 compatible adapter, say Y.  Otherwise,
+	  say N.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called apne.
+
+config NE3210
+	tristate "Novell/Eagle/Microdyne NE3210 EISA support (EXPERIMENTAL)"
+	depends on PCI && EISA && EXPERIMENTAL
+	select CRC32
+	---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>.  Note that this driver
+	  will NOT WORK for NE3200 cards as they are completely different.
+
+	  To compile this driver as a module, choose M here. The module
+	  will be called ne3210.
+
+config PCMCIA_PCNET
+	tristate "NE2000 compatible PCMCIA support"
+	depends on PCMCIA
+	select CRC32
+	---help---
+	  Say Y here if you intend to attach an NE2000 compatible PCMCIA
+	  (PC-card) Ethernet or Fast Ethernet card to your computer.
+
+	  To compile this driver as a module, choose M here: the module will be
+	  called pcnet_cs.  If unsure, say N.
+
+config NE_H8300
+	tristate "NE2000 compatible support for H8/300"
+	depends on H8300
+	---help---
+	  Say Y here if you want to use the NE2000 compatible
+	  controller on the Renesas H8/300 processor.
+
+config STNIC
+	tristate "National DP83902AV  support"
+	depends on SUPERH
+	select CRC32
+	---help---
+	  Support for cards based on the National Semiconductor DP83902AV
+	  ST-NIC Serial Network Interface Controller for Twisted Pair.  This
+	  is a 10Mbit/sec Ethernet controller.  Product overview and specs at
+	  <http://www.national.com/pf/DP/DP83902A.html>.
+
+	  If unsure, say N.
+
+config NET_VENDOR_SMC
+	bool "Western Digital/SMC cards"
+	depends on (ISA || MCA || EISA || MAC)
+	---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 doesn't directly affect the
+	  kernel: saying N will just cause the configurator to skip all
+	  the questions about Western Digital cards. If you say Y, you will be
+	  asked for your specific card in the following questions.
+
+config ULTRAMCA
+	tristate "SMC Ultra MCA support"
+	depends on NET_VENDOR_SMC && MCA
+	select CRC32
+	---help---
+	  If you have a network (Ethernet) card of this type and are running
+	  an MCA based system (PS/2), 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 smc-mca.
+
+config ULTRA
+	tristate "SMC Ultra support"
+	depends on NET_VENDOR_SMC && ISA
+	select CRC32
+	---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>.
+
+	  Important: There have been many reports that, with some motherboards
+	  mixing an SMC Ultra and an Adaptec AHA154x SCSI card (or compatible,
+	  such as some BusLogic models) causes corruption problems with many
+	  operating systems. The Linux smc-ultra driver has a work-around for
+	  this but keep it in mind if you have such a SCSI card and have
+	  problems.
+
+	  To compile this driver as a module, choose M here. The module
+	  will be called smc-ultra.
+
+config ULTRA32
+	tristate "SMC Ultra32 EISA support"
+	depends on NET_VENDOR_SMC && EISA
+	select CRC32
+	---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 smc-ultra32.
+
+config WD80x3
+	tristate "WD80*3 support"
+	depends on NET_VENDOR_SMC && ISA
+	select CRC32
+	---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 wd.
+
+config ZORRO8390
+	tristate "Zorro NS8390-based Ethernet support"
+	depends on ZORRO
+	select CRC32
+	---help---
+	  This driver is for Zorro Ethernet cards using an NS8390-compatible
+	  chipset, like the Village Tronic Ariadne II and the Individual
+	  Computers X-Surf Ethernet cards. If you have such a card, say Y.
+	  Otherwise, say N.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called zorro8390.
+
+endif # NET_VENDOR_8390
diff --git a/drivers/net/ethernet/8390/Makefile b/drivers/net/ethernet/8390/Makefile
new file mode 100644
index 0000000..3337d7f
--- /dev/null
+++ b/drivers/net/ethernet/8390/Makefile
@@ -0,0 +1,29 @@
+#
+# Makefile for the 8390 network device drivers.
+#
+
+obj-$(CONFIG_MAC8390) += mac8390.o
+obj-$(CONFIG_AC3200) += ac3200.o 8390.o
+obj-$(CONFIG_APNE) += apne.o 8390.o
+obj-$(CONFIG_ARM_ETHERH) += etherh.o
+obj-$(CONFIG_AX88796) += ax88796.o
+obj-$(CONFIG_E2100) += e2100.o 8390.o
+obj-$(CONFIG_EL2) += 3c503.o 8390p.o
+obj-$(CONFIG_ES3210) += es3210.o 8390.o
+obj-$(CONFIG_HPLAN_PLUS) += hp-plus.o 8390p.o
+obj-$(CONFIG_HPLAN) += hp.o 8390p.o
+obj-$(CONFIG_HYDRA) += hydra.o 8390.o
+obj-$(CONFIG_LNE390) += lne390.o 8390.o
+obj-$(CONFIG_NE2000) += ne.o 8390p.o
+obj-$(CONFIG_NE2_MCA) += ne2.o 8390p.o
+obj-$(CONFIG_NE2K_PCI) += ne2k-pci.o 8390.o
+obj-$(CONFIG_NE3210) += ne3210.o 8390.o
+obj-$(CONFIG_NE_H8300) += ne-h8300.o 8390.o
+obj-$(CONFIG_PCMCIA_AXNET) += axnet_cs.o 8390.o
+obj-$(CONFIG_PCMCIA_PCNET) += pcnet_cs.o 8390.o
+obj-$(CONFIG_STNIC) += stnic.o 8390.o
+obj-$(CONFIG_ULTRA) += smc-ultra.o 8390.o
+obj-$(CONFIG_ULTRA32) += smc-ultra32.o 8390.o
+obj-$(CONFIG_ULTRAMCA) += smc-mca.o 8390.o
+obj-$(CONFIG_WD80x3) += wd.o 8390.o
+obj-$(CONFIG_ZORRO8390) += zorro8390.o 8390.o
diff --git a/drivers/net/ethernet/8390/ac3200.c b/drivers/net/ethernet/8390/ac3200.c
new file mode 100644
index 0000000..f07b2e9
--- /dev/null
+++ b/drivers/net/ethernet/8390/ac3200.c
@@ -0,0 +1,432 @@
+/* ac3200.c: A driver for the Ansel Communications EISA ethernet adaptor. */
+/*
+	Written 1993, 1994 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
+
+	This is driver for the Ansel Communications Model 3200 EISA Ethernet LAN
+	Adapter.  The programming information is from the users manual, as related
+	by glee@ardnassak.math.clemson.edu.
+
+	Changelog:
+
+	Paul Gortmaker 05/98	: add support for shared mem above 1MB.
+
+  */
+
+static const char version[] =
+	"ac3200.c:v1.01 7/1/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
+
+#include <linux/module.h>
+#include <linux/eisa.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#include "8390.h"
+
+#define DRV_NAME	"ac3200"
+
+/* Offsets from the base address. */
+#define AC_NIC_BASE	0x00
+#define AC_SA_PROM	0x16			/* The station address PROM. */
+#define AC_ADDR0	0x00			/* Prefix station address values. */
+#define AC_ADDR1	0x40
+#define AC_ADDR2	0x90
+#define AC_ID_PORT	0xC80
+#define AC_EISA_ID	0x0110d305
+#define AC_RESET_PORT	0xC84
+#define AC_RESET	0x00
+#define AC_ENABLE	0x01
+#define AC_CONFIG	0xC90	/* The configuration port. */
+
+#define AC_IO_EXTENT 0x20
+                                /* Actually accessed is:
+								 * AC_NIC_BASE (0-15)
+								 * AC_SA_PROM (0-5)
+								 * AC_ID_PORT (0-3)
+								 * AC_RESET_PORT
+								 * AC_CONFIG
+								 */
+
+/* Decoding of the configuration register. */
+static unsigned char config2irqmap[8] __initdata = {15, 12, 11, 10, 9, 7, 5, 3};
+static int addrmap[8] =
+{0xFF0000, 0xFE0000, 0xFD0000, 0xFFF0000, 0xFFE0000, 0xFFC0000,  0xD0000, 0 };
+static const char *port_name[4] = { "10baseT", "invalid", "AUI", "10base2"};
+
+#define config2irq(configval)	config2irqmap[((configval) >> 3) & 7]
+#define config2mem(configval)	addrmap[(configval) & 7]
+#define config2name(configval)	port_name[((configval) >> 6) & 3]
+
+/* First and last 8390 pages. */
+#define AC_START_PG		0x00	/* First page of 8390 TX buffer */
+#define AC_STOP_PG		0x80	/* Last page +1 of the 8390 RX ring */
+
+static int ac_probe1(int ioaddr, struct net_device *dev);
+
+static int ac_open(struct net_device *dev);
+static void ac_reset_8390(struct net_device *dev);
+static void ac_block_input(struct net_device *dev, int count,
+					struct sk_buff *skb, int ring_offset);
+static void ac_block_output(struct net_device *dev, const int count,
+							const unsigned char *buf, const int start_page);
+static void ac_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
+					int ring_page);
+
+static int ac_close_card(struct net_device *dev);
+
+
+/*	Probe for the AC3200.
+
+	The AC3200 can be identified by either the EISA configuration registers,
+	or the unique value in the station address PROM.
+	*/
+
+static int __init do_ac3200_probe(struct net_device *dev)
+{
+	unsigned short ioaddr = dev->base_addr;
+	int irq = dev->irq;
+	int mem_start = dev->mem_start;
+
+	if (ioaddr > 0x1ff)		/* Check a single specified location. */
+		return ac_probe1(ioaddr, dev);
+	else if (ioaddr > 0)		/* Don't probe at all. */
+		return -ENXIO;
+
+	if ( ! EISA_bus)
+		return -ENXIO;
+
+	for (ioaddr = 0x1000; ioaddr < 0x9000; ioaddr += 0x1000) {
+		if (ac_probe1(ioaddr, dev) == 0)
+			return 0;
+		dev->irq = irq;
+		dev->mem_start = mem_start;
+	}
+
+	return -ENODEV;
+}
+
+#ifndef MODULE
+struct net_device * __init ac3200_probe(int unit)
+{
+	struct net_device *dev = alloc_ei_netdev();
+	int err;
+
+	if (!dev)
+		return ERR_PTR(-ENOMEM);
+
+	sprintf(dev->name, "eth%d", unit);
+	netdev_boot_setup_check(dev);
+
+	err = do_ac3200_probe(dev);
+	if (err)
+		goto out;
+	return dev;
+out:
+	free_netdev(dev);
+	return ERR_PTR(err);
+}
+#endif
+
+static const struct net_device_ops ac_netdev_ops = {
+	.ndo_open		= ac_open,
+	.ndo_stop 		= ac_close_card,
+
+	.ndo_start_xmit		= ei_start_xmit,
+	.ndo_tx_timeout		= ei_tx_timeout,
+	.ndo_get_stats		= ei_get_stats,
+	.ndo_set_multicast_list = ei_set_multicast_list,
+	.ndo_validate_addr	= eth_validate_addr,
+	.ndo_set_mac_address 	= eth_mac_addr,
+	.ndo_change_mtu		= eth_change_mtu,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	.ndo_poll_controller	= ei_poll,
+#endif
+};
+
+static int __init ac_probe1(int ioaddr, struct net_device *dev)
+{
+	int i, retval;
+
+	if (!request_region(ioaddr, AC_IO_EXTENT, DRV_NAME))
+		return -EBUSY;
+
+	if (inb_p(ioaddr + AC_ID_PORT) == 0xff) {
+		retval = -ENODEV;
+		goto out;
+	}
+
+	if (inl(ioaddr + AC_ID_PORT) != AC_EISA_ID) {
+		retval = -ENODEV;
+		goto out;
+	}
+
+#ifndef final_version
+	printk(KERN_DEBUG "AC3200 ethercard configuration register is %#02x,"
+		   " EISA ID %02x %02x %02x %02x.\n", inb(ioaddr + AC_CONFIG),
+		   inb(ioaddr + AC_ID_PORT + 0), inb(ioaddr + AC_ID_PORT + 1),
+		   inb(ioaddr + AC_ID_PORT + 2), inb(ioaddr + AC_ID_PORT + 3));
+#endif
+
+	for (i = 0; i < 6; i++)
+		dev->dev_addr[i] = inb(ioaddr + AC_SA_PROM + i);
+
+	printk(KERN_DEBUG "AC3200 in EISA slot %d, node %pM",
+	       ioaddr/0x1000, dev->dev_addr);
+#if 0
+	/* Check the vendor ID/prefix. Redundant after checking the EISA ID */
+	if (inb(ioaddr + AC_SA_PROM + 0) != AC_ADDR0
+		|| inb(ioaddr + AC_SA_PROM + 1) != AC_ADDR1
+		|| inb(ioaddr + AC_SA_PROM + 2) != AC_ADDR2 ) {
+		printk(", not found (invalid prefix).\n");
+		retval = -ENODEV;
+		goto out;
+	}
+#endif
+
+	/* Assign and allocate the interrupt now. */
+	if (dev->irq == 0) {
+		dev->irq = config2irq(inb(ioaddr + AC_CONFIG));
+		printk(", using");
+	} else {
+		dev->irq = irq_canonicalize(dev->irq);
+		printk(", assigning");
+	}
+
+	retval = request_irq(dev->irq, ei_interrupt, 0, DRV_NAME, dev);
+	if (retval) {
+		printk (" nothing! Unable to get IRQ %d.\n", dev->irq);
+		goto out;
+	}
+
+	printk(" IRQ %d, %s port\n", dev->irq, port_name[dev->if_port]);
+
+	dev->base_addr = ioaddr;
+
+#ifdef notyet
+	if (dev->mem_start)	{		/* Override the value from the board. */
+		for (i = 0; i < 7; i++)
+			if (addrmap[i] == dev->mem_start)
+				break;
+		if (i >= 7)
+			i = 0;
+		outb((inb(ioaddr + AC_CONFIG) & ~7) | i, ioaddr + AC_CONFIG);
+	}
+#endif
+
+	dev->if_port = inb(ioaddr + AC_CONFIG) >> 6;
+	dev->mem_start = config2mem(inb(ioaddr + AC_CONFIG));
+
+	printk("%s: AC3200 at %#3x with %dkB memory at physical address %#lx.\n",
+			dev->name, ioaddr, AC_STOP_PG/4, dev->mem_start);
+
+	/*
+	 *  BEWARE!! Some dain-bramaged EISA SCUs will allow you to put
+	 *  the card mem within the region covered by `normal' RAM  !!!
+	 *
+	 *  ioremap() will fail in that case.
+	 */
+	ei_status.mem = ioremap(dev->mem_start, AC_STOP_PG*0x100);
+	if (!ei_status.mem) {
+		printk(KERN_ERR "ac3200.c: Unable to remap card memory above 1MB !!\n");
+		printk(KERN_ERR "ac3200.c: Try using EISA SCU to set memory below 1MB.\n");
+		printk(KERN_ERR "ac3200.c: Driver NOT installed.\n");
+		retval = -EINVAL;
+		goto out1;
+	}
+	printk("ac3200.c: remapped %dkB card memory to virtual address %p\n",
+			AC_STOP_PG/4, ei_status.mem);
+
+	dev->mem_start = (unsigned long)ei_status.mem;
+	dev->mem_end = dev->mem_start + (AC_STOP_PG - AC_START_PG)*256;
+
+	ei_status.name = "AC3200";
+	ei_status.tx_start_page = AC_START_PG;
+	ei_status.rx_start_page = AC_START_PG + TX_PAGES;
+	ei_status.stop_page = AC_STOP_PG;
+	ei_status.word16 = 1;
+
+	if (ei_debug > 0)
+		printk(version);
+
+	ei_status.reset_8390 = &ac_reset_8390;
+	ei_status.block_input = &ac_block_input;
+	ei_status.block_output = &ac_block_output;
+	ei_status.get_8390_hdr = &ac_get_8390_hdr;
+
+	dev->netdev_ops = &ac_netdev_ops;
+	NS8390_init(dev, 0);
+
+	retval = register_netdev(dev);
+	if (retval)
+		goto out2;
+	return 0;
+out2:
+	if (ei_status.reg0)
+		iounmap(ei_status.mem);
+out1:
+	free_irq(dev->irq, dev);
+out:
+	release_region(ioaddr, AC_IO_EXTENT);
+	return retval;
+}
+
+static int ac_open(struct net_device *dev)
+{
+#ifdef notyet
+	/* Someday we may enable the IRQ and shared memory here. */
+	int ioaddr = dev->base_addr;
+#endif
+
+	ei_open(dev);
+	return 0;
+}
+
+static void ac_reset_8390(struct net_device *dev)
+{
+	ushort ioaddr = dev->base_addr;
+
+	outb(AC_RESET, ioaddr + AC_RESET_PORT);
+	if (ei_debug > 1) printk("resetting AC3200, t=%ld...", jiffies);
+
+	ei_status.txing = 0;
+	outb(AC_ENABLE, ioaddr + AC_RESET_PORT);
+	if (ei_debug > 1) printk("reset done\n");
+}
+
+/* Grab the 8390 specific header. Similar to the block_input routine, but
+   we don't need to be concerned with ring wrap as the header will be at
+   the start of a page, so we optimize accordingly. */
+
+static void
+ac_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
+{
+	void __iomem *hdr_start = ei_status.mem + ((ring_page - AC_START_PG)<<8);
+	memcpy_fromio(hdr, hdr_start, sizeof(struct e8390_pkt_hdr));
+}
+
+/*  Block input and output are easy on shared memory ethercards, the only
+	complication is when the ring buffer wraps. */
+
+static void ac_block_input(struct net_device *dev, int count, struct sk_buff *skb,
+						  int ring_offset)
+{
+	void __iomem *start = ei_status.mem + ring_offset - AC_START_PG*256;
+
+	if (ring_offset + count > AC_STOP_PG*256) {
+		/* We must wrap the input move. */
+		int semi_count = AC_STOP_PG*256 - ring_offset;
+		memcpy_fromio(skb->data, start, semi_count);
+		count -= semi_count;
+		memcpy_fromio(skb->data + semi_count,
+				ei_status.mem + TX_PAGES*256, count);
+	} else {
+		memcpy_fromio(skb->data, start, count);
+	}
+}
+
+static void ac_block_output(struct net_device *dev, int count,
+							const unsigned char *buf, int start_page)
+{
+	void __iomem *shmem = ei_status.mem + ((start_page - AC_START_PG)<<8);
+
+	memcpy_toio(shmem, buf, count);
+}
+
+static int ac_close_card(struct net_device *dev)
+{
+	if (ei_debug > 1)
+		printk("%s: Shutting down ethercard.\n", dev->name);
+
+#ifdef notyet
+	/* We should someday disable shared memory and interrupts. */
+	outb(0x00, ioaddr + 6);	/* Disable interrupts. */
+	free_irq(dev->irq, dev);
+#endif
+
+	ei_close(dev);
+	return 0;
+}
+
+#ifdef MODULE
+#define MAX_AC32_CARDS	4	/* Max number of AC32 cards per module */
+static struct net_device *dev_ac32[MAX_AC32_CARDS];
+static int io[MAX_AC32_CARDS];
+static int irq[MAX_AC32_CARDS];
+static int mem[MAX_AC32_CARDS];
+module_param_array(io, int, NULL, 0);
+module_param_array(irq, int, NULL, 0);
+module_param_array(mem, int, NULL, 0);
+MODULE_PARM_DESC(io, "I/O base address(es)");
+MODULE_PARM_DESC(irq, "IRQ number(s)");
+MODULE_PARM_DESC(mem, "Memory base address(es)");
+MODULE_DESCRIPTION("Ansel AC3200 EISA ethernet driver");
+MODULE_LICENSE("GPL");
+
+static int __init ac3200_module_init(void)
+{
+	struct net_device *dev;
+	int this_dev, found = 0;
+
+	for (this_dev = 0; this_dev < MAX_AC32_CARDS; this_dev++) {
+		if (io[this_dev] == 0 && this_dev != 0)
+			break;
+		dev = alloc_ei_netdev();
+		if (!dev)
+			break;
+		dev->irq = irq[this_dev];
+		dev->base_addr = io[this_dev];
+		dev->mem_start = mem[this_dev];		/* Currently ignored by driver */
+		if (do_ac3200_probe(dev) == 0) {
+			dev_ac32[found++] = dev;
+			continue;
+		}
+		free_netdev(dev);
+		printk(KERN_WARNING "ac3200.c: No ac3200 card found (i/o = 0x%x).\n", io[this_dev]);
+		break;
+	}
+	if (found)
+		return 0;
+	return -ENXIO;
+}
+
+static void cleanup_card(struct net_device *dev)
+{
+	/* Someday free_irq may be in ac_close_card() */
+	free_irq(dev->irq, dev);
+	release_region(dev->base_addr, AC_IO_EXTENT);
+	iounmap(ei_status.mem);
+}
+
+static void __exit ac3200_module_exit(void)
+{
+	int this_dev;
+
+	for (this_dev = 0; this_dev < MAX_AC32_CARDS; this_dev++) {
+		struct net_device *dev = dev_ac32[this_dev];
+		if (dev) {
+			unregister_netdev(dev);
+			cleanup_card(dev);
+			free_netdev(dev);
+		}
+	}
+}
+module_init(ac3200_module_init);
+module_exit(ac3200_module_exit);
+#endif /* MODULE */
diff --git a/drivers/net/ethernet/8390/apne.c b/drivers/net/ethernet/8390/apne.c
new file mode 100644
index 0000000..5477373
--- /dev/null
+++ b/drivers/net/ethernet/8390/apne.c
@@ -0,0 +1,620 @@
+/*
+ * Amiga Linux/68k 8390 based PCMCIA Ethernet Driver for the Amiga 1200
+ *
+ * (C) Copyright 1997 Alain Malek
+ *                    (Alain.Malek@cryogen.com)
+ *
+ * ----------------------------------------------------------------------------
+ *
+ * This program is based on
+ *
+ * ne.c:       A general non-shared-memory NS8390 ethernet driver for linux
+ *             Written 1992-94 by Donald Becker.
+ *
+ * 8390.c:     A general NS8390 ethernet driver core for linux.
+ *             Written 1992-94 by Donald Becker.
+ *
+ * cnetdevice: A Sana-II ethernet driver for AmigaOS
+ *             Written by Bruce Abbott (bhabbott@inhb.co.nz)
+ *
+ * ----------------------------------------------------------------------------
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file COPYING in the main directory of the Linux
+ * distribution for more details.
+ *
+ * ----------------------------------------------------------------------------
+ *
+ */
+
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/setup.h>
+#include <asm/amigaints.h>
+#include <asm/amigahw.h>
+#include <asm/amigayle.h>
+#include <asm/amipcmcia.h>
+
+#include "8390.h"
+
+/* ---- No user-serviceable parts below ---- */
+
+#define DRV_NAME "apne"
+
+#define NE_BASE	 (dev->base_addr)
+#define NE_CMD	 		0x00
+#define NE_DATAPORT		0x10            /* NatSemi-defined port window offset. */
+#define NE_RESET		0x1f            /* Issue a read to reset, a write to clear. */
+#define NE_IO_EXTENT	        0x20
+
+#define NE_EN0_ISR		0x07
+#define NE_EN0_DCFG		0x0e
+
+#define NE_EN0_RSARLO	        0x08
+#define NE_EN0_RSARHI	        0x09
+#define NE_EN0_RCNTLO	        0x0a
+#define NE_EN0_RXCR		0x0c
+#define NE_EN0_TXCR		0x0d
+#define NE_EN0_RCNTHI	        0x0b
+#define NE_EN0_IMR		0x0f
+
+#define NE1SM_START_PG	0x20	/* First page of TX buffer */
+#define NE1SM_STOP_PG 	0x40	/* Last page +1 of RX ring */
+#define NESM_START_PG	0x40	/* First page of TX buffer */
+#define NESM_STOP_PG	0x80	/* Last page +1 of RX ring */
+
+
+struct net_device * __init apne_probe(int unit);
+static int apne_probe1(struct net_device *dev, int ioaddr);
+
+static void apne_reset_8390(struct net_device *dev);
+static void apne_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
+			  int ring_page);
+static void apne_block_input(struct net_device *dev, int count,
+								struct sk_buff *skb, int ring_offset);
+static void apne_block_output(struct net_device *dev, const int count,
+							const unsigned char *buf, const int start_page);
+static irqreturn_t apne_interrupt(int irq, void *dev_id);
+
+static int init_pcmcia(void);
+
+/* IO base address used for nic */
+
+#define IOBASE 0x300
+
+/*
+   use MANUAL_CONFIG and MANUAL_OFFSET for enabling IO by hand
+   you can find the values to use by looking at the cnet.device
+   config file example (the default values are for the CNET40BC card)
+*/
+
+/*
+#define MANUAL_CONFIG 0x20
+#define MANUAL_OFFSET 0x3f8
+
+#define MANUAL_HWADDR0 0x00
+#define MANUAL_HWADDR1 0x12
+#define MANUAL_HWADDR2 0x34
+#define MANUAL_HWADDR3 0x56
+#define MANUAL_HWADDR4 0x78
+#define MANUAL_HWADDR5 0x9a
+*/
+
+static const char version[] =
+    "apne.c:v1.1 7/10/98 Alain Malek (Alain.Malek@cryogen.ch)\n";
+
+static int apne_owned;	/* signal if card already owned */
+
+struct net_device * __init apne_probe(int unit)
+{
+	struct net_device *dev;
+#ifndef MANUAL_CONFIG
+	char tuple[8];
+#endif
+	int err;
+
+	if (!MACH_IS_AMIGA)
+		return ERR_PTR(-ENODEV);
+
+	if (apne_owned)
+		return ERR_PTR(-ENODEV);
+
+	if ( !(AMIGAHW_PRESENT(PCMCIA)) )
+		return ERR_PTR(-ENODEV);
+
+	printk("Looking for PCMCIA ethernet card : ");
+
+	/* check if a card is inserted */
+	if (!(PCMCIA_INSERTED)) {
+		printk("NO PCMCIA card inserted\n");
+		return ERR_PTR(-ENODEV);
+	}
+
+	dev = alloc_ei_netdev();
+	if (!dev)
+		return ERR_PTR(-ENOMEM);
+	if (unit >= 0) {
+		sprintf(dev->name, "eth%d", unit);
+		netdev_boot_setup_check(dev);
+	}
+
+	/* disable pcmcia irq for readtuple */
+	pcmcia_disable_irq();
+
+#ifndef MANUAL_CONFIG
+	if ((pcmcia_copy_tuple(CISTPL_FUNCID, tuple, 8) < 3) ||
+		(tuple[2] != CISTPL_FUNCID_NETWORK)) {
+		printk("not an ethernet card\n");
+		/* XXX: shouldn't we re-enable irq here? */
+		free_netdev(dev);
+		return ERR_PTR(-ENODEV);
+	}
+#endif
+
+	printk("ethernet PCMCIA card inserted\n");
+
+	if (!init_pcmcia()) {
+		/* XXX: shouldn't we re-enable irq here? */
+		free_netdev(dev);
+		return ERR_PTR(-ENODEV);
+	}
+
+	if (!request_region(IOBASE, 0x20, DRV_NAME)) {
+		free_netdev(dev);
+		return ERR_PTR(-EBUSY);
+	}
+
+	err = apne_probe1(dev, IOBASE);
+	if (err) {
+		release_region(IOBASE, 0x20);
+		free_netdev(dev);
+		return ERR_PTR(err);
+	}
+	err = register_netdev(dev);
+	if (!err)
+		return dev;
+
+	pcmcia_disable_irq();
+	free_irq(IRQ_AMIGA_PORTS, dev);
+	pcmcia_reset();
+	release_region(IOBASE, 0x20);
+	free_netdev(dev);
+	return ERR_PTR(err);
+}
+
+static int __init apne_probe1(struct net_device *dev, int ioaddr)
+{
+    int i;
+    unsigned char SA_prom[32];
+    int wordlength = 2;
+    const char *name = NULL;
+    int start_page, stop_page;
+#ifndef MANUAL_HWADDR0
+    int neX000, ctron;
+#endif
+    static unsigned version_printed;
+
+    if (ei_debug  &&  version_printed++ == 0)
+	printk(version);
+
+    printk("PCMCIA NE*000 ethercard probe");
+
+    /* Reset card. Who knows what dain-bramaged state it was left in. */
+    {	unsigned long reset_start_time = jiffies;
+
+	outb(inb(ioaddr + NE_RESET), ioaddr + NE_RESET);
+
+	while ((inb(ioaddr + NE_EN0_ISR) & ENISR_RESET) == 0)
+		if (time_after(jiffies, reset_start_time + 2*HZ/100)) {
+			printk(" not found (no reset ack).\n");
+			return -ENODEV;
+		}
+
+	outb(0xff, ioaddr + NE_EN0_ISR);		/* Ack all intr. */
+    }
+
+#ifndef MANUAL_HWADDR0
+
+    /* Read the 16 bytes of station address PROM.
+       We must first initialize registers, similar to NS8390_init(eifdev, 0).
+       We can't reliably read the SAPROM address without this.
+       (I learned the hard way!). */
+    {
+	struct {unsigned long value, offset; } program_seq[] = {
+	    {E8390_NODMA+E8390_PAGE0+E8390_STOP, NE_CMD}, /* Select page 0*/
+	    {0x48,	NE_EN0_DCFG},	/* Set byte-wide (0x48) access. */
+	    {0x00,	NE_EN0_RCNTLO},	/* Clear the count regs. */
+	    {0x00,	NE_EN0_RCNTHI},
+	    {0x00,	NE_EN0_IMR},	/* Mask completion irq. */
+	    {0xFF,	NE_EN0_ISR},
+	    {E8390_RXOFF, NE_EN0_RXCR},	/* 0x20  Set to monitor */
+	    {E8390_TXOFF, NE_EN0_TXCR},	/* 0x02  and loopback mode. */
+	    {32,	NE_EN0_RCNTLO},
+	    {0x00,	NE_EN0_RCNTHI},
+	    {0x00,	NE_EN0_RSARLO},	/* DMA starting at 0x0000. */
+	    {0x00,	NE_EN0_RSARHI},
+	    {E8390_RREAD+E8390_START, NE_CMD},
+	};
+	for (i = 0; i < ARRAY_SIZE(program_seq); i++) {
+	    outb(program_seq[i].value, ioaddr + program_seq[i].offset);
+	}
+
+    }
+    for(i = 0; i < 32 /*sizeof(SA_prom)*/; i+=2) {
+	SA_prom[i] = inb(ioaddr + NE_DATAPORT);
+	SA_prom[i+1] = inb(ioaddr + NE_DATAPORT);
+	if (SA_prom[i] != SA_prom[i+1])
+	    wordlength = 1;
+    }
+
+    /*	At this point, wordlength *only* tells us if the SA_prom is doubled
+	up or not because some broken PCI cards don't respect the byte-wide
+	request in program_seq above, and hence don't have doubled up values.
+	These broken cards would otherwise be detected as an ne1000.  */
+
+    if (wordlength == 2)
+	for (i = 0; i < 16; i++)
+		SA_prom[i] = SA_prom[i+i];
+
+    if (wordlength == 2) {
+	/* We must set the 8390 for word mode. */
+	outb(0x49, ioaddr + NE_EN0_DCFG);
+	start_page = NESM_START_PG;
+	stop_page = NESM_STOP_PG;
+    } else {
+	start_page = NE1SM_START_PG;
+	stop_page = NE1SM_STOP_PG;
+    }
+
+    neX000 = (SA_prom[14] == 0x57  &&  SA_prom[15] == 0x57);
+    ctron =  (SA_prom[0] == 0x00 && SA_prom[1] == 0x00 && SA_prom[2] == 0x1d);
+
+    /* Set up the rest of the parameters. */
+    if (neX000) {
+	name = (wordlength == 2) ? "NE2000" : "NE1000";
+    } else if (ctron) {
+	name = (wordlength == 2) ? "Ctron-8" : "Ctron-16";
+	start_page = 0x01;
+	stop_page = (wordlength == 2) ? 0x40 : 0x20;
+    } else {
+	printk(" not found.\n");
+	return -ENXIO;
+
+    }
+
+#else
+    wordlength = 2;
+    /* We must set the 8390 for word mode. */
+    outb(0x49, ioaddr + NE_EN0_DCFG);
+    start_page = NESM_START_PG;
+    stop_page = NESM_STOP_PG;
+
+    SA_prom[0] = MANUAL_HWADDR0;
+    SA_prom[1] = MANUAL_HWADDR1;
+    SA_prom[2] = MANUAL_HWADDR2;
+    SA_prom[3] = MANUAL_HWADDR3;
+    SA_prom[4] = MANUAL_HWADDR4;
+    SA_prom[5] = MANUAL_HWADDR5;
+    name = "NE2000";
+#endif
+
+    dev->base_addr = ioaddr;
+    dev->irq = IRQ_AMIGA_PORTS;
+    dev->netdev_ops = &ei_netdev_ops;
+
+    /* Install the Interrupt handler */
+    i = request_irq(dev->irq, apne_interrupt, IRQF_SHARED, DRV_NAME, dev);
+    if (i) return i;
+
+    for(i = 0; i < ETHER_ADDR_LEN; i++)
+	dev->dev_addr[i] = SA_prom[i];
+
+    printk(" %pM\n", dev->dev_addr);
+
+    printk("%s: %s found.\n", dev->name, name);
+
+    ei_status.name = name;
+    ei_status.tx_start_page = start_page;
+    ei_status.stop_page = stop_page;
+    ei_status.word16 = (wordlength == 2);
+
+    ei_status.rx_start_page = start_page + TX_PAGES;
+
+    ei_status.reset_8390 = &apne_reset_8390;
+    ei_status.block_input = &apne_block_input;
+    ei_status.block_output = &apne_block_output;
+    ei_status.get_8390_hdr = &apne_get_8390_hdr;
+
+    NS8390_init(dev, 0);
+
+    pcmcia_ack_int(pcmcia_get_intreq());		/* ack PCMCIA int req */
+    pcmcia_enable_irq();
+
+    apne_owned = 1;
+
+    return 0;
+}
+
+/* Hard reset the card.  This used to pause for the same period that a
+   8390 reset command required, but that shouldn't be necessary. */
+static void
+apne_reset_8390(struct net_device *dev)
+{
+    unsigned long reset_start_time = jiffies;
+
+    init_pcmcia();
+
+    if (ei_debug > 1) printk("resetting the 8390 t=%ld...", jiffies);
+
+    outb(inb(NE_BASE + NE_RESET), NE_BASE + NE_RESET);
+
+    ei_status.txing = 0;
+    ei_status.dmaing = 0;
+
+    /* This check _should_not_ be necessary, omit eventually. */
+    while ((inb(NE_BASE+NE_EN0_ISR) & ENISR_RESET) == 0)
+	if (time_after(jiffies, reset_start_time + 2*HZ/100)) {
+	    printk("%s: ne_reset_8390() did not complete.\n", dev->name);
+	    break;
+	}
+    outb(ENISR_RESET, NE_BASE + NE_EN0_ISR);	/* Ack intr. */
+}
+
+/* Grab the 8390 specific header. Similar to the block_input routine, but
+   we don't need to be concerned with ring wrap as the header will be at
+   the start of a page, so we optimize accordingly. */
+
+static void
+apne_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
+{
+
+    int nic_base = dev->base_addr;
+    int cnt;
+    char *ptrc;
+    short *ptrs;
+
+    /* This *shouldn't* happen. If it does, it's the last thing you'll see */
+    if (ei_status.dmaing) {
+	printk("%s: DMAing conflict in ne_get_8390_hdr "
+	   "[DMAstat:%d][irqlock:%d][intr:%d].\n",
+	   dev->name, ei_status.dmaing, ei_status.irqlock, dev->irq);
+	return;
+    }
+
+    ei_status.dmaing |= 0x01;
+    outb(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base+ NE_CMD);
+    outb(ENISR_RDC, nic_base + NE_EN0_ISR);
+    outb(sizeof(struct e8390_pkt_hdr), nic_base + NE_EN0_RCNTLO);
+    outb(0, nic_base + NE_EN0_RCNTHI);
+    outb(0, nic_base + NE_EN0_RSARLO);		/* On page boundary */
+    outb(ring_page, nic_base + NE_EN0_RSARHI);
+    outb(E8390_RREAD+E8390_START, nic_base + NE_CMD);
+
+    if (ei_status.word16) {
+        ptrs = (short*)hdr;
+        for(cnt = 0; cnt < (sizeof(struct e8390_pkt_hdr)>>1); cnt++)
+            *ptrs++ = inw(NE_BASE + NE_DATAPORT);
+    } else {
+        ptrc = (char*)hdr;
+        for(cnt = 0; cnt < sizeof(struct e8390_pkt_hdr); cnt++)
+            *ptrc++ = inb(NE_BASE + NE_DATAPORT);
+    }
+
+    outb(ENISR_RDC, nic_base + NE_EN0_ISR);	/* Ack intr. */
+    ei_status.dmaing &= ~0x01;
+
+    le16_to_cpus(&hdr->count);
+}
+
+/* Block input and output, similar to the Crynwr packet driver.  If you
+   are porting to a new ethercard, look at the packet driver source for hints.
+   The NEx000 doesn't share the on-board packet memory -- you have to put
+   the packet out through the "remote DMA" dataport using outb. */
+
+static void
+apne_block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset)
+{
+    int nic_base = dev->base_addr;
+    char *buf = skb->data;
+    char *ptrc;
+    short *ptrs;
+    int cnt;
+
+    /* This *shouldn't* happen. If it does, it's the last thing you'll see */
+    if (ei_status.dmaing) {
+	printk("%s: DMAing conflict in ne_block_input "
+	   "[DMAstat:%d][irqlock:%d][intr:%d].\n",
+	   dev->name, ei_status.dmaing, ei_status.irqlock, dev->irq);
+	return;
+    }
+    ei_status.dmaing |= 0x01;
+    outb(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base+ NE_CMD);
+    outb(ENISR_RDC, nic_base + NE_EN0_ISR);
+    outb(count & 0xff, nic_base + NE_EN0_RCNTLO);
+    outb(count >> 8, nic_base + NE_EN0_RCNTHI);
+    outb(ring_offset & 0xff, nic_base + NE_EN0_RSARLO);
+    outb(ring_offset >> 8, nic_base + NE_EN0_RSARHI);
+    outb(E8390_RREAD+E8390_START, nic_base + NE_CMD);
+    if (ei_status.word16) {
+      ptrs = (short*)buf;
+      for (cnt = 0; cnt < (count>>1); cnt++)
+        *ptrs++ = inw(NE_BASE + NE_DATAPORT);
+      if (count & 0x01) {
+	buf[count-1] = inb(NE_BASE + NE_DATAPORT);
+      }
+    } else {
+      ptrc = (char*)buf;
+      for (cnt = 0; cnt < count; cnt++)
+        *ptrc++ = inb(NE_BASE + NE_DATAPORT);
+    }
+
+    outb(ENISR_RDC, nic_base + NE_EN0_ISR);	/* Ack intr. */
+    ei_status.dmaing &= ~0x01;
+}
+
+static void
+apne_block_output(struct net_device *dev, int count,
+		const unsigned char *buf, const int start_page)
+{
+    int nic_base = NE_BASE;
+    unsigned long dma_start;
+    char *ptrc;
+    short *ptrs;
+    int cnt;
+
+    /* Round the count up for word writes.  Do we need to do this?
+       What effect will an odd byte count have on the 8390?
+       I should check someday. */
+    if (ei_status.word16 && (count & 0x01))
+      count++;
+
+    /* This *shouldn't* happen. If it does, it's the last thing you'll see */
+    if (ei_status.dmaing) {
+	printk("%s: DMAing conflict in ne_block_output."
+	   "[DMAstat:%d][irqlock:%d][intr:%d]\n",
+	   dev->name, ei_status.dmaing, ei_status.irqlock, dev->irq);
+	return;
+    }
+    ei_status.dmaing |= 0x01;
+    /* We should already be in page 0, but to be safe... */
+    outb(E8390_PAGE0+E8390_START+E8390_NODMA, nic_base + NE_CMD);
+
+    outb(ENISR_RDC, nic_base + NE_EN0_ISR);
+
+   /* Now the normal output. */
+    outb(count & 0xff, nic_base + NE_EN0_RCNTLO);
+    outb(count >> 8,   nic_base + NE_EN0_RCNTHI);
+    outb(0x00, nic_base + NE_EN0_RSARLO);
+    outb(start_page, nic_base + NE_EN0_RSARHI);
+
+    outb(E8390_RWRITE+E8390_START, nic_base + NE_CMD);
+    if (ei_status.word16) {
+        ptrs = (short*)buf;
+        for (cnt = 0; cnt < count>>1; cnt++)
+            outw(*ptrs++, NE_BASE+NE_DATAPORT);
+    } else {
+        ptrc = (char*)buf;
+        for (cnt = 0; cnt < count; cnt++)
+	    outb(*ptrc++, NE_BASE + NE_DATAPORT);
+    }
+
+    dma_start = jiffies;
+
+    while ((inb(NE_BASE + NE_EN0_ISR) & ENISR_RDC) == 0)
+	if (time_after(jiffies, dma_start + 2*HZ/100)) {	/* 20ms */
+		printk("%s: timeout waiting for Tx RDC.\n", dev->name);
+		apne_reset_8390(dev);
+		NS8390_init(dev,1);
+		break;
+	}
+
+    outb(ENISR_RDC, nic_base + NE_EN0_ISR);	/* Ack intr. */
+    ei_status.dmaing &= ~0x01;
+}
+
+static irqreturn_t apne_interrupt(int irq, void *dev_id)
+{
+    unsigned char pcmcia_intreq;
+
+    if (!(gayle.inten & GAYLE_IRQ_IRQ))
+        return IRQ_NONE;
+
+    pcmcia_intreq = pcmcia_get_intreq();
+
+    if (!(pcmcia_intreq & GAYLE_IRQ_IRQ)) {
+        pcmcia_ack_int(pcmcia_intreq);
+        return IRQ_NONE;
+    }
+    if (ei_debug > 3)
+        printk("pcmcia intreq = %x\n", pcmcia_intreq);
+    pcmcia_disable_irq();			/* to get rid of the sti() within ei_interrupt */
+    ei_interrupt(irq, dev_id);
+    pcmcia_ack_int(pcmcia_get_intreq());
+    pcmcia_enable_irq();
+    return IRQ_HANDLED;
+}
+
+#ifdef MODULE
+static struct net_device *apne_dev;
+
+static int __init apne_module_init(void)
+{
+	apne_dev = apne_probe(-1);
+	if (IS_ERR(apne_dev))
+		return PTR_ERR(apne_dev);
+	return 0;
+}
+
+static void __exit apne_module_exit(void)
+{
+	unregister_netdev(apne_dev);
+
+	pcmcia_disable_irq();
+
+	free_irq(IRQ_AMIGA_PORTS, apne_dev);
+
+	pcmcia_reset();
+
+	release_region(IOBASE, 0x20);
+
+	free_netdev(apne_dev);
+}
+module_init(apne_module_init);
+module_exit(apne_module_exit);
+#endif
+
+static int init_pcmcia(void)
+{
+	u_char config;
+#ifndef MANUAL_CONFIG
+	u_char tuple[32];
+	int offset_len;
+#endif
+	u_long offset;
+
+	pcmcia_reset();
+	pcmcia_program_voltage(PCMCIA_0V);
+	pcmcia_access_speed(PCMCIA_SPEED_250NS);
+	pcmcia_write_enable();
+
+#ifdef MANUAL_CONFIG
+	config = MANUAL_CONFIG;
+#else
+	/* get and write config byte to enable IO port */
+
+	if (pcmcia_copy_tuple(CISTPL_CFTABLE_ENTRY, tuple, 32) < 3)
+		return 0;
+
+	config = tuple[2] & 0x3f;
+#endif
+#ifdef MANUAL_OFFSET
+	offset = MANUAL_OFFSET;
+#else
+	if (pcmcia_copy_tuple(CISTPL_CONFIG, tuple, 32) < 6)
+		return 0;
+
+	offset_len = (tuple[2] & 0x3) + 1;
+	offset = 0;
+	while(offset_len--) {
+		offset = (offset << 8) | tuple[4+offset_len];
+	}
+#endif
+
+	out_8(GAYLE_ATTRIBUTE+offset, config);
+
+	return 1;
+}
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/ethernet/8390/ax88796.c b/drivers/net/ethernet/8390/ax88796.c
new file mode 100644
index 0000000..e7cb8c8
--- /dev/null
+++ b/drivers/net/ethernet/8390/ax88796.c
@@ -0,0 +1,1010 @@
+/* drivers/net/ax88796.c
+ *
+ * Copyright 2005,2007 Simtec Electronics
+ *	Ben Dooks <ben@simtec.co.uk>
+ *
+ * Asix AX88796 10/100 Ethernet controller support
+ *	Based on ne.c, by Donald Becker, et-al.
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/isapnp.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/mdio-bitbang.h>
+#include <linux/phy.h>
+#include <linux/eeprom_93cx6.h>
+#include <linux/slab.h>
+
+#include <net/ax88796.h>
+
+#include <asm/system.h>
+
+/* Rename the lib8390.c functions to show that they are in this driver */
+#define __ei_open ax_ei_open
+#define __ei_close ax_ei_close
+#define __ei_poll ax_ei_poll
+#define __ei_start_xmit ax_ei_start_xmit
+#define __ei_tx_timeout ax_ei_tx_timeout
+#define __ei_get_stats ax_ei_get_stats
+#define __ei_set_multicast_list ax_ei_set_multicast_list
+#define __ei_interrupt ax_ei_interrupt
+#define ____alloc_ei_netdev ax__alloc_ei_netdev
+#define __NS8390_init ax_NS8390_init
+
+/* force unsigned long back to 'void __iomem *' */
+#define ax_convert_addr(_a) ((void __force __iomem *)(_a))
+
+#define ei_inb(_a) readb(ax_convert_addr(_a))
+#define ei_outb(_v, _a) writeb(_v, ax_convert_addr(_a))
+
+#define ei_inb_p(_a) ei_inb(_a)
+#define ei_outb_p(_v, _a) ei_outb(_v, _a)
+
+/* define EI_SHIFT() to take into account our register offsets */
+#define EI_SHIFT(x) (ei_local->reg_offset[(x)])
+
+/* Ensure we have our RCR base value */
+#define AX88796_PLATFORM
+
+static unsigned char version[] = "ax88796.c: Copyright 2005,2007 Simtec Electronics\n";
+
+#include "lib8390.c"
+
+#define DRV_NAME "ax88796"
+#define DRV_VERSION "1.00"
+
+/* from ne.c */
+#define NE_CMD		EI_SHIFT(0x00)
+#define NE_RESET	EI_SHIFT(0x1f)
+#define NE_DATAPORT	EI_SHIFT(0x10)
+
+#define NE1SM_START_PG	0x20	/* First page of TX buffer */
+#define NE1SM_STOP_PG	0x40	/* Last page +1 of RX ring */
+#define NESM_START_PG	0x40	/* First page of TX buffer */
+#define NESM_STOP_PG	0x80	/* Last page +1 of RX ring */
+
+#define AX_GPOC_PPDSET	BIT(6)
+
+/* device private data */
+
+struct ax_device {
+	struct mii_bus *mii_bus;
+	struct mdiobb_ctrl bb_ctrl;
+	struct phy_device *phy_dev;
+	void __iomem *addr_memr;
+	u8 reg_memr;
+	int link;
+	int speed;
+	int duplex;
+
+	void __iomem *map2;
+	const struct ax_plat_data *plat;
+
+	unsigned char running;
+	unsigned char resume_open;
+	unsigned int irqflags;
+
+	u32 reg_offsets[0x20];
+};
+
+static inline struct ax_device *to_ax_dev(struct net_device *dev)
+{
+	struct ei_device *ei_local = netdev_priv(dev);
+	return (struct ax_device *)(ei_local + 1);
+}
+
+/*
+ * ax_initial_check
+ *
+ * do an initial probe for the card to check wether it exists
+ * and is functional
+ */
+static int ax_initial_check(struct net_device *dev)
+{
+	struct ei_device *ei_local = netdev_priv(dev);
+	void __iomem *ioaddr = ei_local->mem;
+	int reg0;
+	int regd;
+
+	reg0 = ei_inb(ioaddr);
+	if (reg0 == 0xFF)
+		return -ENODEV;
+
+	ei_outb(E8390_NODMA + E8390_PAGE1 + E8390_STOP, ioaddr + E8390_CMD);
+	regd = ei_inb(ioaddr + 0x0d);
+	ei_outb(0xff, ioaddr + 0x0d);
+	ei_outb(E8390_NODMA + E8390_PAGE0, ioaddr + E8390_CMD);
+	ei_inb(ioaddr + EN0_COUNTER0); /* Clear the counter by reading. */
+	if (ei_inb(ioaddr + EN0_COUNTER0) != 0) {
+		ei_outb(reg0, ioaddr);
+		ei_outb(regd, ioaddr + 0x0d);	/* Restore the old values. */
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+/*
+ * Hard reset the card. This used to pause for the same period that a
+ * 8390 reset command required, but that shouldn't be necessary.
+ */
+static void ax_reset_8390(struct net_device *dev)
+{
+	struct ei_device *ei_local = netdev_priv(dev);
+	unsigned long reset_start_time = jiffies;
+	void __iomem *addr = (void __iomem *)dev->base_addr;
+
+	if (ei_debug > 1)
+		netdev_dbg(dev, "resetting the 8390 t=%ld\n", jiffies);
+
+	ei_outb(ei_inb(addr + NE_RESET), addr + NE_RESET);
+
+	ei_local->txing = 0;
+	ei_local->dmaing = 0;
+
+	/* This check _should_not_ be necessary, omit eventually. */
+	while ((ei_inb(addr + EN0_ISR) & ENISR_RESET) == 0) {
+		if (jiffies - reset_start_time > 2 * HZ / 100) {
+			netdev_warn(dev, "%s: did not complete.\n", __func__);
+			break;
+		}
+	}
+
+	ei_outb(ENISR_RESET, addr + EN0_ISR);	/* Ack intr. */
+}
+
+
+static void ax_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
+			    int ring_page)
+{
+	struct ei_device *ei_local = netdev_priv(dev);
+	void __iomem *nic_base = ei_local->mem;
+
+	/* This *shouldn't* happen. If it does, it's the last thing you'll see */
+	if (ei_local->dmaing) {
+		netdev_err(dev, "DMAing conflict in %s "
+			"[DMAstat:%d][irqlock:%d].\n",
+			__func__,
+			ei_local->dmaing, ei_local->irqlock);
+		return;
+	}
+
+	ei_local->dmaing |= 0x01;
+	ei_outb(E8390_NODMA + E8390_PAGE0 + E8390_START, nic_base + NE_CMD);
+	ei_outb(sizeof(struct e8390_pkt_hdr), nic_base + EN0_RCNTLO);
+	ei_outb(0, nic_base + EN0_RCNTHI);
+	ei_outb(0, nic_base + EN0_RSARLO);		/* On page boundary */
+	ei_outb(ring_page, nic_base + EN0_RSARHI);
+	ei_outb(E8390_RREAD+E8390_START, nic_base + NE_CMD);
+
+	if (ei_local->word16)
+		readsw(nic_base + NE_DATAPORT, hdr,
+		       sizeof(struct e8390_pkt_hdr) >> 1);
+	else
+		readsb(nic_base + NE_DATAPORT, hdr,
+		       sizeof(struct e8390_pkt_hdr));
+
+	ei_outb(ENISR_RDC, nic_base + EN0_ISR);	/* Ack intr. */
+	ei_local->dmaing &= ~0x01;
+
+	le16_to_cpus(&hdr->count);
+}
+
+
+/*
+ * Block input and output, similar to the Crynwr packet driver. If
+ * you are porting to a new ethercard, look at the packet driver
+ * source for hints. The NEx000 doesn't share the on-board packet
+ * memory -- you have to put the packet out through the "remote DMA"
+ * dataport using ei_outb.
+ */
+static void ax_block_input(struct net_device *dev, int count,
+			   struct sk_buff *skb, int ring_offset)
+{
+	struct ei_device *ei_local = netdev_priv(dev);
+	void __iomem *nic_base = ei_local->mem;
+	char *buf = skb->data;
+
+	if (ei_local->dmaing) {
+		netdev_err(dev,
+			"DMAing conflict in %s "
+			"[DMAstat:%d][irqlock:%d].\n",
+			__func__,
+			ei_local->dmaing, ei_local->irqlock);
+		return;
+	}
+
+	ei_local->dmaing |= 0x01;
+
+	ei_outb(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base + NE_CMD);
+	ei_outb(count & 0xff, nic_base + EN0_RCNTLO);
+	ei_outb(count >> 8, nic_base + EN0_RCNTHI);
+	ei_outb(ring_offset & 0xff, nic_base + EN0_RSARLO);
+	ei_outb(ring_offset >> 8, nic_base + EN0_RSARHI);
+	ei_outb(E8390_RREAD+E8390_START, nic_base + NE_CMD);
+
+	if (ei_local->word16) {
+		readsw(nic_base + NE_DATAPORT, buf, count >> 1);
+		if (count & 0x01)
+			buf[count-1] = ei_inb(nic_base + NE_DATAPORT);
+
+	} else {
+		readsb(nic_base + NE_DATAPORT, buf, count);
+	}
+
+	ei_local->dmaing &= ~1;
+}
+
+static void ax_block_output(struct net_device *dev, int count,
+			    const unsigned char *buf, const int start_page)
+{
+	struct ei_device *ei_local = netdev_priv(dev);
+	void __iomem *nic_base = ei_local->mem;
+	unsigned long dma_start;
+
+	/*
+	 * Round the count up for word writes. Do we need to do this?
+	 * What effect will an odd byte count have on the 8390?  I
+	 * should check someday.
+	 */
+	if (ei_local->word16 && (count & 0x01))
+		count++;
+
+	/* This *shouldn't* happen. If it does, it's the last thing you'll see */
+	if (ei_local->dmaing) {
+		netdev_err(dev, "DMAing conflict in %s."
+			"[DMAstat:%d][irqlock:%d]\n",
+			__func__,
+		       ei_local->dmaing, ei_local->irqlock);
+		return;
+	}
+
+	ei_local->dmaing |= 0x01;
+	/* We should already be in page 0, but to be safe... */
+	ei_outb(E8390_PAGE0+E8390_START+E8390_NODMA, nic_base + NE_CMD);
+
+	ei_outb(ENISR_RDC, nic_base + EN0_ISR);
+
+	/* Now the normal output. */
+	ei_outb(count & 0xff, nic_base + EN0_RCNTLO);
+	ei_outb(count >> 8, nic_base + EN0_RCNTHI);
+	ei_outb(0x00, nic_base + EN0_RSARLO);
+	ei_outb(start_page, nic_base + EN0_RSARHI);
+
+	ei_outb(E8390_RWRITE+E8390_START, nic_base + NE_CMD);
+	if (ei_local->word16)
+		writesw(nic_base + NE_DATAPORT, buf, count >> 1);
+	else
+		writesb(nic_base + NE_DATAPORT, buf, count);
+
+	dma_start = jiffies;
+
+	while ((ei_inb(nic_base + EN0_ISR) & ENISR_RDC) == 0) {
+		if (jiffies - dma_start > 2 * HZ / 100) {		/* 20ms */
+			netdev_warn(dev, "timeout waiting for Tx RDC.\n");
+			ax_reset_8390(dev);
+			ax_NS8390_init(dev, 1);
+			break;
+		}
+	}
+
+	ei_outb(ENISR_RDC, nic_base + EN0_ISR);	/* Ack intr. */
+	ei_local->dmaing &= ~0x01;
+}
+
+/* definitions for accessing MII/EEPROM interface */
+
+#define AX_MEMR			EI_SHIFT(0x14)
+#define AX_MEMR_MDC		BIT(0)
+#define AX_MEMR_MDIR		BIT(1)
+#define AX_MEMR_MDI		BIT(2)
+#define AX_MEMR_MDO		BIT(3)
+#define AX_MEMR_EECS		BIT(4)
+#define AX_MEMR_EEI		BIT(5)
+#define AX_MEMR_EEO		BIT(6)
+#define AX_MEMR_EECLK		BIT(7)
+
+static void ax_handle_link_change(struct net_device *dev)
+{
+	struct ax_device  *ax = to_ax_dev(dev);
+	struct phy_device *phy_dev = ax->phy_dev;
+	int status_change = 0;
+
+	if (phy_dev->link && ((ax->speed != phy_dev->speed) ||
+			     (ax->duplex != phy_dev->duplex))) {
+
+		ax->speed = phy_dev->speed;
+		ax->duplex = phy_dev->duplex;
+		status_change = 1;
+	}
+
+	if (phy_dev->link != ax->link) {
+		if (!phy_dev->link) {
+			ax->speed = 0;
+			ax->duplex = -1;
+		}
+		ax->link = phy_dev->link;
+
+		status_change = 1;
+	}
+
+	if (status_change)
+		phy_print_status(phy_dev);
+}
+
+static int ax_mii_probe(struct net_device *dev)
+{
+	struct ax_device  *ax = to_ax_dev(dev);
+	struct phy_device *phy_dev = NULL;
+	int ret;
+
+	/* find the first phy */
+	phy_dev = phy_find_first(ax->mii_bus);
+	if (!phy_dev) {
+		netdev_err(dev, "no PHY found\n");
+		return -ENODEV;
+	}
+
+	ret = phy_connect_direct(dev, phy_dev, ax_handle_link_change, 0,
+				 PHY_INTERFACE_MODE_MII);
+	if (ret) {
+		netdev_err(dev, "Could not attach to PHY\n");
+		return ret;
+	}
+
+	/* mask with MAC supported features */
+	phy_dev->supported &= PHY_BASIC_FEATURES;
+	phy_dev->advertising = phy_dev->supported;
+
+	ax->phy_dev = phy_dev;
+
+	netdev_info(dev, "PHY driver [%s] (mii_bus:phy_addr=%s, irq=%d)\n",
+		    phy_dev->drv->name, dev_name(&phy_dev->dev), phy_dev->irq);
+
+	return 0;
+}
+
+static void ax_phy_switch(struct net_device *dev, int on)
+{
+	struct ei_device *ei_local = netdev_priv(dev);
+	struct ax_device *ax = to_ax_dev(dev);
+
+	u8 reg_gpoc =  ax->plat->gpoc_val;
+
+	if (!!on)
+		reg_gpoc &= ~AX_GPOC_PPDSET;
+	else
+		reg_gpoc |= AX_GPOC_PPDSET;
+
+	ei_outb(reg_gpoc, ei_local->mem + EI_SHIFT(0x17));
+}
+
+static int ax_open(struct net_device *dev)
+{
+	struct ax_device *ax = to_ax_dev(dev);
+	int ret;
+
+	netdev_dbg(dev, "open\n");
+
+	ret = request_irq(dev->irq, ax_ei_interrupt, ax->irqflags,
+			  dev->name, dev);
+	if (ret)
+		goto failed_request_irq;
+
+	/* turn the phy on (if turned off) */
+	ax_phy_switch(dev, 1);
+
+	ret = ax_mii_probe(dev);
+	if (ret)
+		goto failed_mii_probe;
+	phy_start(ax->phy_dev);
+
+	ret = ax_ei_open(dev);
+	if (ret)
+		goto failed_ax_ei_open;
+
+	ax->running = 1;
+
+	return 0;
+
+ failed_ax_ei_open:
+	phy_disconnect(ax->phy_dev);
+ failed_mii_probe:
+	ax_phy_switch(dev, 0);
+	free_irq(dev->irq, dev);
+ failed_request_irq:
+	return ret;
+}
+
+static int ax_close(struct net_device *dev)
+{
+	struct ax_device *ax = to_ax_dev(dev);
+
+	netdev_dbg(dev, "close\n");
+
+	ax->running = 0;
+	wmb();
+
+	ax_ei_close(dev);
+
+	/* turn the phy off */
+	ax_phy_switch(dev, 0);
+	phy_disconnect(ax->phy_dev);
+
+	free_irq(dev->irq, dev);
+	return 0;
+}
+
+static int ax_ioctl(struct net_device *dev, struct ifreq *req, int cmd)
+{
+	struct ax_device *ax = to_ax_dev(dev);
+	struct phy_device *phy_dev = ax->phy_dev;
+
+	if (!netif_running(dev))
+		return -EINVAL;
+
+	if (!phy_dev)
+		return -ENODEV;
+
+	return phy_mii_ioctl(phy_dev, req, cmd);
+}
+
+/* ethtool ops */
+
+static void ax_get_drvinfo(struct net_device *dev,
+			   struct ethtool_drvinfo *info)
+{
+	struct platform_device *pdev = to_platform_device(dev->dev.parent);
+
+	strcpy(info->driver, DRV_NAME);
+	strcpy(info->version, DRV_VERSION);
+	strcpy(info->bus_info, pdev->name);
+}
+
+static int ax_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+	struct ax_device *ax = to_ax_dev(dev);
+	struct phy_device *phy_dev = ax->phy_dev;
+
+	if (!phy_dev)
+		return -ENODEV;
+
+	return phy_ethtool_gset(phy_dev, cmd);
+}
+
+static int ax_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+	struct ax_device *ax = to_ax_dev(dev);
+	struct phy_device *phy_dev = ax->phy_dev;
+
+	if (!phy_dev)
+		return -ENODEV;
+
+	return phy_ethtool_sset(phy_dev, cmd);
+}
+
+static const struct ethtool_ops ax_ethtool_ops = {
+	.get_drvinfo		= ax_get_drvinfo,
+	.get_settings		= ax_get_settings,
+	.set_settings		= ax_set_settings,
+	.get_link		= ethtool_op_get_link,
+};
+
+#ifdef CONFIG_AX88796_93CX6
+static void ax_eeprom_register_read(struct eeprom_93cx6 *eeprom)
+{
+	struct ei_device *ei_local = eeprom->data;
+	u8 reg = ei_inb(ei_local->mem + AX_MEMR);
+
+	eeprom->reg_data_in = reg & AX_MEMR_EEI;
+	eeprom->reg_data_out = reg & AX_MEMR_EEO; /* Input pin */
+	eeprom->reg_data_clock = reg & AX_MEMR_EECLK;
+	eeprom->reg_chip_select = reg & AX_MEMR_EECS;
+}
+
+static void ax_eeprom_register_write(struct eeprom_93cx6 *eeprom)
+{
+	struct ei_device *ei_local = eeprom->data;
+	u8 reg = ei_inb(ei_local->mem + AX_MEMR);
+
+	reg &= ~(AX_MEMR_EEI | AX_MEMR_EECLK | AX_MEMR_EECS);
+
+	if (eeprom->reg_data_in)
+		reg |= AX_MEMR_EEI;
+	if (eeprom->reg_data_clock)
+		reg |= AX_MEMR_EECLK;
+	if (eeprom->reg_chip_select)
+		reg |= AX_MEMR_EECS;
+
+	ei_outb(reg, ei_local->mem + AX_MEMR);
+	udelay(10);
+}
+#endif
+
+static const struct net_device_ops ax_netdev_ops = {
+	.ndo_open		= ax_open,
+	.ndo_stop		= ax_close,
+	.ndo_do_ioctl		= ax_ioctl,
+
+	.ndo_start_xmit		= ax_ei_start_xmit,
+	.ndo_tx_timeout		= ax_ei_tx_timeout,
+	.ndo_get_stats		= ax_ei_get_stats,
+	.ndo_set_multicast_list = ax_ei_set_multicast_list,
+	.ndo_validate_addr	= eth_validate_addr,
+	.ndo_set_mac_address	= eth_mac_addr,
+	.ndo_change_mtu		= eth_change_mtu,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	.ndo_poll_controller	= ax_ei_poll,
+#endif
+};
+
+static void ax_bb_mdc(struct mdiobb_ctrl *ctrl, int level)
+{
+	struct ax_device *ax = container_of(ctrl, struct ax_device, bb_ctrl);
+
+	if (level)
+		ax->reg_memr |= AX_MEMR_MDC;
+	else
+		ax->reg_memr &= ~AX_MEMR_MDC;
+
+	ei_outb(ax->reg_memr, ax->addr_memr);
+}
+
+static void ax_bb_dir(struct mdiobb_ctrl *ctrl, int output)
+{
+	struct ax_device *ax = container_of(ctrl, struct ax_device, bb_ctrl);
+
+	if (output)
+		ax->reg_memr &= ~AX_MEMR_MDIR;
+	else
+		ax->reg_memr |= AX_MEMR_MDIR;
+
+	ei_outb(ax->reg_memr, ax->addr_memr);
+}
+
+static void ax_bb_set_data(struct mdiobb_ctrl *ctrl, int value)
+{
+	struct ax_device *ax = container_of(ctrl, struct ax_device, bb_ctrl);
+
+	if (value)
+		ax->reg_memr |= AX_MEMR_MDO;
+	else
+		ax->reg_memr &= ~AX_MEMR_MDO;
+
+	ei_outb(ax->reg_memr, ax->addr_memr);
+}
+
+static int ax_bb_get_data(struct mdiobb_ctrl *ctrl)
+{
+	struct ax_device *ax = container_of(ctrl, struct ax_device, bb_ctrl);
+	int reg_memr = ei_inb(ax->addr_memr);
+
+	return reg_memr & AX_MEMR_MDI ? 1 : 0;
+}
+
+static struct mdiobb_ops bb_ops = {
+	.owner = THIS_MODULE,
+	.set_mdc = ax_bb_mdc,
+	.set_mdio_dir = ax_bb_dir,
+	.set_mdio_data = ax_bb_set_data,
+	.get_mdio_data = ax_bb_get_data,
+};
+
+/* setup code */
+
+static int ax_mii_init(struct net_device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev->dev.parent);
+	struct ei_device *ei_local = netdev_priv(dev);
+	struct ax_device *ax = to_ax_dev(dev);
+	int err, i;
+
+	ax->bb_ctrl.ops = &bb_ops;
+	ax->addr_memr = ei_local->mem + AX_MEMR;
+	ax->mii_bus = alloc_mdio_bitbang(&ax->bb_ctrl);
+	if (!ax->mii_bus) {
+		err = -ENOMEM;
+		goto out;
+	}
+
+	ax->mii_bus->name = "ax88796_mii_bus";
+	ax->mii_bus->parent = dev->dev.parent;
+	snprintf(ax->mii_bus->id, MII_BUS_ID_SIZE, "%x", pdev->id);
+
+	ax->mii_bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
+	if (!ax->mii_bus->irq) {
+		err = -ENOMEM;
+		goto out_free_mdio_bitbang;
+	}
+
+	for (i = 0; i < PHY_MAX_ADDR; i++)
+		ax->mii_bus->irq[i] = PHY_POLL;
+
+	err = mdiobus_register(ax->mii_bus);
+	if (err)
+		goto out_free_irq;
+
+	return 0;
+
+ out_free_irq:
+	kfree(ax->mii_bus->irq);
+ out_free_mdio_bitbang:
+	free_mdio_bitbang(ax->mii_bus);
+ out:
+	return err;
+}
+
+static void ax_initial_setup(struct net_device *dev, struct ei_device *ei_local)
+{
+	void __iomem *ioaddr = ei_local->mem;
+	struct ax_device *ax = to_ax_dev(dev);
+
+	/* Select page 0 */
+	ei_outb(E8390_NODMA + E8390_PAGE0 + E8390_STOP, ioaddr + E8390_CMD);
+
+	/* set to byte access */
+	ei_outb(ax->plat->dcr_val & ~1, ioaddr + EN0_DCFG);
+	ei_outb(ax->plat->gpoc_val, ioaddr + EI_SHIFT(0x17));
+}
+
+/*
+ * ax_init_dev
+ *
+ * initialise the specified device, taking care to note the MAC
+ * address it may already have (if configured), ensure
+ * the device is ready to be used by lib8390.c and registerd with
+ * the network layer.
+ */
+static int ax_init_dev(struct net_device *dev)
+{
+	struct ei_device *ei_local = netdev_priv(dev);
+	struct ax_device *ax = to_ax_dev(dev);
+	void __iomem *ioaddr = ei_local->mem;
+	unsigned int start_page;
+	unsigned int stop_page;
+	int ret;
+	int i;
+
+	ret = ax_initial_check(dev);
+	if (ret)
+		goto err_out;
+
+	/* setup goes here */
+
+	ax_initial_setup(dev, ei_local);
+
+	/* read the mac from the card prom if we need it */
+
+	if (ax->plat->flags & AXFLG_HAS_EEPROM) {
+		unsigned char SA_prom[32];
+
+		for (i = 0; i < sizeof(SA_prom); i += 2) {
+			SA_prom[i] = ei_inb(ioaddr + NE_DATAPORT);
+			SA_prom[i + 1] = ei_inb(ioaddr + NE_DATAPORT);
+		}
+
+		if (ax->plat->wordlength == 2)
+			for (i = 0; i < 16; i++)
+				SA_prom[i] = SA_prom[i+i];
+
+		memcpy(dev->dev_addr, SA_prom, 6);
+	}
+
+#ifdef CONFIG_AX88796_93CX6
+	if (ax->plat->flags & AXFLG_HAS_93CX6) {
+		unsigned char mac_addr[6];
+		struct eeprom_93cx6 eeprom;
+
+		eeprom.data = ei_local;
+		eeprom.register_read = ax_eeprom_register_read;
+		eeprom.register_write = ax_eeprom_register_write;
+		eeprom.width = PCI_EEPROM_WIDTH_93C56;
+
+		eeprom_93cx6_multiread(&eeprom, 0,
+				       (__le16 __force *)mac_addr,
+				       sizeof(mac_addr) >> 1);
+
+		memcpy(dev->dev_addr, mac_addr, 6);
+	}
+#endif
+	if (ax->plat->wordlength == 2) {
+		/* We must set the 8390 for word mode. */
+		ei_outb(ax->plat->dcr_val, ei_local->mem + EN0_DCFG);
+		start_page = NESM_START_PG;
+		stop_page = NESM_STOP_PG;
+	} else {
+		start_page = NE1SM_START_PG;
+		stop_page = NE1SM_STOP_PG;
+	}
+
+	/* load the mac-address from the device */
+	if (ax->plat->flags & AXFLG_MAC_FROMDEV) {
+		ei_outb(E8390_NODMA + E8390_PAGE1 + E8390_STOP,
+			ei_local->mem + E8390_CMD); /* 0x61 */
+		for (i = 0; i < ETHER_ADDR_LEN; i++)
+			dev->dev_addr[i] =
+				ei_inb(ioaddr + EN1_PHYS_SHIFT(i));
+	}
+
+	if ((ax->plat->flags & AXFLG_MAC_FROMPLATFORM) &&
+	    ax->plat->mac_addr)
+		memcpy(dev->dev_addr, ax->plat->mac_addr,
+		       ETHER_ADDR_LEN);
+
+	ax_reset_8390(dev);
+
+	ei_local->name = "AX88796";
+	ei_local->tx_start_page = start_page;
+	ei_local->stop_page = stop_page;
+	ei_local->word16 = (ax->plat->wordlength == 2);
+	ei_local->rx_start_page = start_page + TX_PAGES;
+
+#ifdef PACKETBUF_MEMSIZE
+	/* Allow the packet buffer size to be overridden by know-it-alls. */
+	ei_local->stop_page = ei_local->tx_start_page + PACKETBUF_MEMSIZE;
+#endif
+
+	ei_local->reset_8390 = &ax_reset_8390;
+	ei_local->block_input = &ax_block_input;
+	ei_local->block_output = &ax_block_output;
+	ei_local->get_8390_hdr = &ax_get_8390_hdr;
+	ei_local->priv = 0;
+
+	dev->netdev_ops = &ax_netdev_ops;
+	dev->ethtool_ops = &ax_ethtool_ops;
+
+	ret = ax_mii_init(dev);
+	if (ret)
+		goto out_irq;
+
+	ax_NS8390_init(dev, 0);
+
+	ret = register_netdev(dev);
+	if (ret)
+		goto out_irq;
+
+	netdev_info(dev, "%dbit, irq %d, %lx, MAC: %pM\n",
+		    ei_local->word16 ? 16 : 8, dev->irq, dev->base_addr,
+		    dev->dev_addr);
+
+	return 0;
+
+ out_irq:
+	/* cleanup irq */
+	free_irq(dev->irq, dev);
+ err_out:
+	return ret;
+}
+
+static int ax_remove(struct platform_device *pdev)
+{
+	struct net_device *dev = platform_get_drvdata(pdev);
+	struct ei_device *ei_local = netdev_priv(dev);
+	struct ax_device *ax = to_ax_dev(dev);
+	struct resource *mem;
+
+	unregister_netdev(dev);
+	free_irq(dev->irq, dev);
+
+	iounmap(ei_local->mem);
+	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	release_mem_region(mem->start, resource_size(mem));
+
+	if (ax->map2) {
+		iounmap(ax->map2);
+		mem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+		release_mem_region(mem->start, resource_size(mem));
+	}
+
+	free_netdev(dev);
+
+	return 0;
+}
+
+/*
+ * ax_probe
+ *
+ * This is the entry point when the platform device system uses to
+ * notify us of a new device to attach to. Allocate memory, find the
+ * resources and information passed, and map the necessary registers.
+ */
+static int ax_probe(struct platform_device *pdev)
+{
+	struct net_device *dev;
+	struct ei_device *ei_local;
+	struct ax_device *ax;
+	struct resource *irq, *mem, *mem2;
+	resource_size_t mem_size, mem2_size = 0;
+	int ret = 0;
+
+	dev = ax__alloc_ei_netdev(sizeof(struct ax_device));
+	if (dev == NULL)
+		return -ENOMEM;
+
+	/* ok, let's setup our device */
+	SET_NETDEV_DEV(dev, &pdev->dev);
+	ei_local = netdev_priv(dev);
+	ax = to_ax_dev(dev);
+
+	ax->plat = pdev->dev.platform_data;
+	platform_set_drvdata(pdev, dev);
+
+	ei_local->rxcr_base = ax->plat->rcr_val;
+
+	/* find the platform resources */
+	irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (!irq) {
+		dev_err(&pdev->dev, "no IRQ specified\n");
+		ret = -ENXIO;
+		goto exit_mem;
+	}
+
+	dev->irq = irq->start;
+	ax->irqflags = irq->flags & IRQF_TRIGGER_MASK;
+
+	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!mem) {
+		dev_err(&pdev->dev, "no MEM specified\n");
+		ret = -ENXIO;
+		goto exit_mem;
+	}
+
+	mem_size = resource_size(mem);
+
+	/*
+	 * setup the register offsets from either the platform data or
+	 * by using the size of the resource provided
+	 */
+	if (ax->plat->reg_offsets)
+		ei_local->reg_offset = ax->plat->reg_offsets;
+	else {
+		ei_local->reg_offset = ax->reg_offsets;
+		for (ret = 0; ret < 0x18; ret++)
+			ax->reg_offsets[ret] = (mem_size / 0x18) * ret;
+	}
+
+	if (!request_mem_region(mem->start, mem_size, pdev->name)) {
+		dev_err(&pdev->dev, "cannot reserve registers\n");
+		ret = -ENXIO;
+		goto exit_mem;
+	}
+
+	ei_local->mem = ioremap(mem->start, mem_size);
+	dev->base_addr = (unsigned long)ei_local->mem;
+
+	if (ei_local->mem == NULL) {
+		dev_err(&pdev->dev, "Cannot ioremap area %pR\n", mem);
+
+		ret = -ENXIO;
+		goto exit_req;
+	}
+
+	/* look for reset area */
+	mem2 = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	if (!mem2) {
+		if (!ax->plat->reg_offsets) {
+			for (ret = 0; ret < 0x20; ret++)
+				ax->reg_offsets[ret] = (mem_size / 0x20) * ret;
+		}
+	} else {
+		mem2_size = resource_size(mem2);
+
+		if (!request_mem_region(mem2->start, mem2_size, pdev->name)) {
+			dev_err(&pdev->dev, "cannot reserve registers\n");
+			ret = -ENXIO;
+			goto exit_mem1;
+		}
+
+		ax->map2 = ioremap(mem2->start, mem2_size);
+		if (!ax->map2) {
+			dev_err(&pdev->dev, "cannot map reset register\n");
+			ret = -ENXIO;
+			goto exit_mem2;
+		}
+
+		ei_local->reg_offset[0x1f] = ax->map2 - ei_local->mem;
+	}
+
+	/* got resources, now initialise and register device */
+	ret = ax_init_dev(dev);
+	if (!ret)
+		return 0;
+
+	if (!ax->map2)
+		goto exit_mem1;
+
+	iounmap(ax->map2);
+
+ exit_mem2:
+	release_mem_region(mem2->start, mem2_size);
+
+ exit_mem1:
+	iounmap(ei_local->mem);
+
+ exit_req:
+	release_mem_region(mem->start, mem_size);
+
+ exit_mem:
+	free_netdev(dev);
+
+	return ret;
+}
+
+/* suspend and resume */
+
+#ifdef CONFIG_PM
+static int ax_suspend(struct platform_device *dev, pm_message_t state)
+{
+	struct net_device *ndev = platform_get_drvdata(dev);
+	struct ax_device *ax = to_ax_dev(ndev);
+
+	ax->resume_open = ax->running;
+
+	netif_device_detach(ndev);
+	ax_close(ndev);
+
+	return 0;
+}
+
+static int ax_resume(struct platform_device *pdev)
+{
+	struct net_device *ndev = platform_get_drvdata(pdev);
+	struct ax_device *ax = to_ax_dev(ndev);
+
+	ax_initial_setup(ndev, netdev_priv(ndev));
+	ax_NS8390_init(ndev, ax->resume_open);
+	netif_device_attach(ndev);
+
+	if (ax->resume_open)
+		ax_open(ndev);
+
+	return 0;
+}
+
+#else
+#define ax_suspend NULL
+#define ax_resume NULL
+#endif
+
+static struct platform_driver axdrv = {
+	.driver	= {
+		.name		= "ax88796",
+		.owner		= THIS_MODULE,
+	},
+	.probe		= ax_probe,
+	.remove		= ax_remove,
+	.suspend	= ax_suspend,
+	.resume		= ax_resume,
+};
+
+static int __init axdrv_init(void)
+{
+	return platform_driver_register(&axdrv);
+}
+
+static void __exit axdrv_exit(void)
+{
+	platform_driver_unregister(&axdrv);
+}
+
+module_init(axdrv_init);
+module_exit(axdrv_exit);
+
+MODULE_DESCRIPTION("AX88796 10/100 Ethernet platform driver");
+MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:ax88796");
diff --git a/drivers/net/ethernet/8390/axnet_cs.c b/drivers/net/ethernet/8390/axnet_cs.c
new file mode 100644
index 0000000..3e4b926
--- /dev/null
+++ b/drivers/net/ethernet/8390/axnet_cs.c
@@ -0,0 +1,1725 @@
+/*======================================================================
+
+    A PCMCIA ethernet driver for Asix AX88190-based cards
+
+    The Asix AX88190 is a NS8390-derived chipset with a few nasty
+    idiosyncracies that make it very inconvenient to support with a
+    standard 8390 driver.  This driver is based on pcnet_cs, with the
+    tweaked 8390 code grafted on the end.  Much of what I did was to
+    clean up and update a similar driver supplied by Asix, which was
+    adapted by William Lee, william@asix.com.tw.
+
+    Copyright (C) 2001 David A. Hinds -- dahinds@users.sourceforge.net
+
+    axnet_cs.c 1.28 2002/06/29 06:27:37
+
+    The network driver code is based on Donald Becker's NE2000 code:
+
+    Written 1992,1993 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.
+    Donald Becker may be reached at becker@scyld.com
+
+======================================================================*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/ptrace.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/crc32.h>
+#include <linux/mii.h>
+#include "8390.h"
+
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ciscode.h>
+#include <pcmcia/ds.h>
+#include <pcmcia/cisreg.h>
+
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/byteorder.h>
+#include <asm/uaccess.h>
+
+#define AXNET_CMD	0x00
+#define AXNET_DATAPORT	0x10	/* NatSemi-defined port window offset. */
+#define AXNET_RESET	0x1f	/* Issue a read to reset, a write to clear. */
+#define AXNET_MII_EEP	0x14	/* Offset of MII access port */
+#define AXNET_TEST	0x15	/* Offset of TEST Register port */
+#define AXNET_GPIO	0x17	/* Offset of General Purpose Register Port */
+
+#define AXNET_START_PG	0x40	/* First page of TX buffer */
+#define AXNET_STOP_PG	0x80	/* Last page +1 of RX ring */
+
+#define AXNET_RDC_TIMEOUT 0x02	/* Max wait in jiffies for Tx RDC */
+
+#define IS_AX88190	0x0001
+#define IS_AX88790	0x0002
+
+/*====================================================================*/
+
+/* Module parameters */
+
+MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
+MODULE_DESCRIPTION("Asix AX88190 PCMCIA ethernet driver");
+MODULE_LICENSE("GPL");
+
+
+/*====================================================================*/
+
+static int axnet_config(struct pcmcia_device *link);
+static void axnet_release(struct pcmcia_device *link);
+static int axnet_open(struct net_device *dev);
+static int axnet_close(struct net_device *dev);
+static int axnet_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
+static netdev_tx_t axnet_start_xmit(struct sk_buff *skb,
+					  struct net_device *dev);
+static struct net_device_stats *get_stats(struct net_device *dev);
+static void set_multicast_list(struct net_device *dev);
+static void axnet_tx_timeout(struct net_device *dev);
+static irqreturn_t ei_irq_wrapper(int irq, void *dev_id);
+static void ei_watchdog(u_long arg);
+static void axnet_reset_8390(struct net_device *dev);
+
+static int mdio_read(unsigned int addr, int phy_id, int loc);
+static void mdio_write(unsigned int addr, int phy_id, int loc, int value);
+
+static void get_8390_hdr(struct net_device *,
+			 struct e8390_pkt_hdr *, int);
+static void block_input(struct net_device *dev, int count,
+			struct sk_buff *skb, int ring_offset);
+static void block_output(struct net_device *dev, int count,
+			 const u_char *buf, const int start_page);
+
+static void axnet_detach(struct pcmcia_device *p_dev);
+
+static void AX88190_init(struct net_device *dev, int startp);
+static int ax_open(struct net_device *dev);
+static int ax_close(struct net_device *dev);
+static irqreturn_t ax_interrupt(int irq, void *dev_id);
+
+/*====================================================================*/
+
+typedef struct axnet_dev_t {
+	struct pcmcia_device	*p_dev;
+	caddr_t	base;
+	struct timer_list	watchdog;
+	int	stale, fast_poll;
+	u_short	link_status;
+	u_char	duplex_flag;
+	int	phy_id;
+	int	flags;
+	int	active_low;
+} axnet_dev_t;
+
+static inline axnet_dev_t *PRIV(struct net_device *dev)
+{
+	void *p = (char *)netdev_priv(dev) + sizeof(struct ei_device);
+	return p;
+}
+
+static const struct net_device_ops axnet_netdev_ops = {
+	.ndo_open 		= axnet_open,
+	.ndo_stop		= axnet_close,
+	.ndo_do_ioctl		= axnet_ioctl,
+	.ndo_start_xmit		= axnet_start_xmit,
+	.ndo_tx_timeout		= axnet_tx_timeout,
+	.ndo_get_stats		= get_stats,
+	.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 axnet_probe(struct pcmcia_device *link)
+{
+    axnet_dev_t *info;
+    struct net_device *dev;
+    struct ei_device *ei_local;
+
+    dev_dbg(&link->dev, "axnet_attach()\n");
+
+    dev = alloc_etherdev(sizeof(struct ei_device) + sizeof(axnet_dev_t));
+    if (!dev)
+	return -ENOMEM;
+
+    ei_local = netdev_priv(dev);
+    spin_lock_init(&ei_local->page_lock);
+
+    info = PRIV(dev);
+    info->p_dev = link;
+    link->priv = dev;
+    link->config_flags |= CONF_ENABLE_IRQ;
+
+    dev->netdev_ops = &axnet_netdev_ops;
+
+    dev->watchdog_timeo = TX_TIMEOUT;
+
+    return axnet_config(link);
+} /* axnet_attach */
+
+static void axnet_detach(struct pcmcia_device *link)
+{
+    struct net_device *dev = link->priv;
+
+    dev_dbg(&link->dev, "axnet_detach(0x%p)\n", link);
+
+    unregister_netdev(dev);
+
+    axnet_release(link);
+
+    free_netdev(dev);
+} /* axnet_detach */
+
+/*======================================================================
+
+    This probes for a card's hardware address by reading the PROM.
+
+======================================================================*/
+
+static int get_prom(struct pcmcia_device *link)
+{
+    struct net_device *dev = link->priv;
+    unsigned int ioaddr = dev->base_addr;
+    int i, j;
+
+    /* This is based on drivers/net/ne.c */
+    struct {
+	u_char value, offset;
+    } program_seq[] = {
+	{E8390_NODMA+E8390_PAGE0+E8390_STOP, E8390_CMD}, /* Select page 0*/
+	{0x01,	EN0_DCFG},	/* Set word-wide access. */
+	{0x00,	EN0_RCNTLO},	/* Clear the count regs. */
+	{0x00,	EN0_RCNTHI},
+	{0x00,	EN0_IMR},	/* Mask completion irq. */
+	{0xFF,	EN0_ISR},
+	{E8390_RXOFF|0x40, EN0_RXCR},	/* 0x60  Set to monitor */
+	{E8390_TXOFF, EN0_TXCR},	/* 0x02  and loopback mode. */
+	{0x10,	EN0_RCNTLO},
+	{0x00,	EN0_RCNTHI},
+	{0x00,	EN0_RSARLO},	/* DMA starting at 0x0400. */
+	{0x04,	EN0_RSARHI},
+	{E8390_RREAD+E8390_START, E8390_CMD},
+    };
+
+    /* Not much of a test, but the alternatives are messy */
+    if (link->config_base != 0x03c0)
+	return 0;
+
+    axnet_reset_8390(dev);
+    mdelay(10);
+
+    for (i = 0; i < ARRAY_SIZE(program_seq); i++)
+	outb_p(program_seq[i].value, ioaddr + program_seq[i].offset);
+
+    for (i = 0; i < 6; i += 2) {
+	j = inw(ioaddr + AXNET_DATAPORT);
+	dev->dev_addr[i] = j & 0xff;
+	dev->dev_addr[i+1] = j >> 8;
+    }
+    return 1;
+} /* get_prom */
+
+static int try_io_port(struct pcmcia_device *link)
+{
+    int j, ret;
+    link->resource[0]->flags &= ~IO_DATA_PATH_WIDTH;
+    link->resource[1]->flags &= ~IO_DATA_PATH_WIDTH;
+    if (link->resource[0]->end == 32) {
+	link->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO;
+	/* for master/slave multifunction cards */
+	if (link->resource[1]->end > 0)
+	    link->resource[1]->flags |= IO_DATA_PATH_WIDTH_8;
+    } else {
+	/* This should be two 16-port windows */
+	link->resource[0]->flags |= IO_DATA_PATH_WIDTH_8;
+	link->resource[1]->flags |= IO_DATA_PATH_WIDTH_16;
+    }
+    if (link->resource[0]->start == 0) {
+	for (j = 0; j < 0x400; j += 0x20) {
+	    link->resource[0]->start = j ^ 0x300;
+	    link->resource[1]->start = (j ^ 0x300) + 0x10;
+	    link->io_lines = 16;
+	    ret = pcmcia_request_io(link);
+	    if (ret == 0)
+		    return ret;
+	}
+	return ret;
+    } else {
+	return pcmcia_request_io(link);
+    }
+}
+
+static int axnet_configcheck(struct pcmcia_device *p_dev, void *priv_data)
+{
+	if (p_dev->config_index == 0)
+		return -EINVAL;
+
+	p_dev->config_index = 0x05;
+	if (p_dev->resource[0]->end + p_dev->resource[1]->end < 32)
+		return -ENODEV;
+
+	return try_io_port(p_dev);
+}
+
+static int axnet_config(struct pcmcia_device *link)
+{
+    struct net_device *dev = link->priv;
+    axnet_dev_t *info = PRIV(dev);
+    int i, j, j2, ret;
+
+    dev_dbg(&link->dev, "axnet_config(0x%p)\n", link);
+
+    /* don't trust the CIS on this; Linksys got it wrong */
+    link->config_regs = 0x63;
+    link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO;
+    ret = pcmcia_loop_config(link, axnet_configcheck, NULL);
+    if (ret != 0)
+	goto failed;
+
+    if (!link->irq)
+	    goto failed;
+
+    if (resource_size(link->resource[1]) == 8)
+	link->config_flags |= CONF_ENABLE_SPKR;
+    
+    ret = pcmcia_enable_device(link);
+    if (ret)
+	    goto failed;
+
+    dev->irq = link->irq;
+    dev->base_addr = link->resource[0]->start;
+
+    if (!get_prom(link)) {
+	pr_notice("this is not an AX88190 card!\n");
+	pr_notice("use pcnet_cs instead.\n");
+	goto failed;
+    }
+
+    ei_status.name = "AX88190";
+    ei_status.word16 = 1;
+    ei_status.tx_start_page = AXNET_START_PG;
+    ei_status.rx_start_page = AXNET_START_PG + TX_PAGES;
+    ei_status.stop_page = AXNET_STOP_PG;
+    ei_status.reset_8390 = axnet_reset_8390;
+    ei_status.get_8390_hdr = get_8390_hdr;
+    ei_status.block_input = block_input;
+    ei_status.block_output = block_output;
+
+    if (inb(dev->base_addr + AXNET_TEST) != 0)
+	info->flags |= IS_AX88790;
+    else
+	info->flags |= IS_AX88190;
+
+    if (info->flags & IS_AX88790)
+	outb(0x10, dev->base_addr + AXNET_GPIO);  /* select Internal PHY */
+
+    info->active_low = 0;
+
+    for (i = 0; i < 32; i++) {
+	j = mdio_read(dev->base_addr + AXNET_MII_EEP, i, 1);
+	j2 = mdio_read(dev->base_addr + AXNET_MII_EEP, i, 2);
+	if (j == j2) continue;
+	if ((j != 0) && (j != 0xffff)) break;
+    }
+
+    if (i == 32) {
+	/* Maybe PHY is in power down mode. (PPD_SET = 1)
+	   Bit 2 of CCSR is active low. */
+	pcmcia_write_config_byte(link, CISREG_CCSR, 0x04);
+	for (i = 0; i < 32; i++) {
+	    j = mdio_read(dev->base_addr + AXNET_MII_EEP, i, 1);
+	    j2 = mdio_read(dev->base_addr + AXNET_MII_EEP, i, 2);
+	    if (j == j2) continue;
+	    if ((j != 0) && (j != 0xffff)) {
+		info->active_low = 1;
+		break;
+	    }
+	}
+    }
+
+    info->phy_id = (i < 32) ? i : -1;
+    SET_NETDEV_DEV(dev, &link->dev);
+
+    if (register_netdev(dev) != 0) {
+	pr_notice("register_netdev() failed\n");
+	goto failed;
+    }
+
+    netdev_info(dev, "Asix AX88%d90: io %#3lx, irq %d, hw_addr %pM\n",
+		((info->flags & IS_AX88790) ? 7 : 1),
+		dev->base_addr, dev->irq, dev->dev_addr);
+    if (info->phy_id != -1) {
+	netdev_dbg(dev, "  MII transceiver at index %d, status %x\n",
+		   info->phy_id, j);
+    } else {
+	netdev_notice(dev, "  No MII transceivers found!\n");
+    }
+    return 0;
+
+failed:
+    axnet_release(link);
+    return -ENODEV;
+} /* axnet_config */
+
+static void axnet_release(struct pcmcia_device *link)
+{
+	pcmcia_disable_device(link);
+}
+
+static int axnet_suspend(struct pcmcia_device *link)
+{
+	struct net_device *dev = link->priv;
+
+	if (link->open)
+		netif_device_detach(dev);
+
+	return 0;
+}
+
+static int axnet_resume(struct pcmcia_device *link)
+{
+	struct net_device *dev = link->priv;
+	axnet_dev_t *info = PRIV(dev);
+
+	if (link->open) {
+		if (info->active_low == 1)
+			pcmcia_write_config_byte(link, CISREG_CCSR, 0x04);
+
+		axnet_reset_8390(dev);
+		AX88190_init(dev, 1);
+		netif_device_attach(dev);
+	}
+
+	return 0;
+}
+
+
+/*======================================================================
+
+    MII interface support
+
+======================================================================*/
+
+#define MDIO_SHIFT_CLK		0x01
+#define MDIO_DATA_WRITE0	0x00
+#define MDIO_DATA_WRITE1	0x08
+#define MDIO_DATA_READ		0x04
+#define MDIO_MASK		0x0f
+#define MDIO_ENB_IN		0x02
+
+static void mdio_sync(unsigned int addr)
+{
+    int bits;
+    for (bits = 0; bits < 32; bits++) {
+	outb_p(MDIO_DATA_WRITE1, addr);
+	outb_p(MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, addr);
+    }
+}
+
+static int mdio_read(unsigned int addr, int phy_id, int loc)
+{
+    u_int cmd = (0xf6<<10)|(phy_id<<5)|loc;
+    int i, retval = 0;
+
+    mdio_sync(addr);
+    for (i = 14; i >= 0; i--) {
+	int dat = (cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0;
+	outb_p(dat, addr);
+	outb_p(dat | MDIO_SHIFT_CLK, addr);
+    }
+    for (i = 19; i > 0; i--) {
+	outb_p(MDIO_ENB_IN, addr);
+	retval = (retval << 1) | ((inb_p(addr) & MDIO_DATA_READ) != 0);
+	outb_p(MDIO_ENB_IN | MDIO_SHIFT_CLK, addr);
+    }
+    return (retval>>1) & 0xffff;
+}
+
+static void mdio_write(unsigned int addr, int phy_id, int loc, int value)
+{
+    u_int cmd = (0x05<<28)|(phy_id<<23)|(loc<<18)|(1<<17)|value;
+    int i;
+
+    mdio_sync(addr);
+    for (i = 31; i >= 0; i--) {
+	int dat = (cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0;
+	outb_p(dat, addr);
+	outb_p(dat | MDIO_SHIFT_CLK, addr);
+    }
+    for (i = 1; i >= 0; i--) {
+	outb_p(MDIO_ENB_IN, addr);
+	outb_p(MDIO_ENB_IN | MDIO_SHIFT_CLK, addr);
+    }
+}
+
+/*====================================================================*/
+
+static int axnet_open(struct net_device *dev)
+{
+    int ret;
+    axnet_dev_t *info = PRIV(dev);
+    struct pcmcia_device *link = info->p_dev;
+    unsigned int nic_base = dev->base_addr;
+    
+    dev_dbg(&link->dev, "axnet_open('%s')\n", dev->name);
+
+    if (!pcmcia_dev_present(link))
+	return -ENODEV;
+
+    outb_p(0xFF, nic_base + EN0_ISR); /* Clear bogus intr. */
+    ret = request_irq(dev->irq, ei_irq_wrapper, IRQF_SHARED, "axnet_cs", dev);
+    if (ret)
+	    return ret;
+
+    link->open++;
+
+    info->link_status = 0x00;
+    init_timer(&info->watchdog);
+    info->watchdog.function = ei_watchdog;
+    info->watchdog.data = (u_long)dev;
+    info->watchdog.expires = jiffies + HZ;
+    add_timer(&info->watchdog);
+
+    return ax_open(dev);
+} /* axnet_open */
+
+/*====================================================================*/
+
+static int axnet_close(struct net_device *dev)
+{
+    axnet_dev_t *info = PRIV(dev);
+    struct pcmcia_device *link = info->p_dev;
+
+    dev_dbg(&link->dev, "axnet_close('%s')\n", dev->name);
+
+    ax_close(dev);
+    free_irq(dev->irq, dev);
+    
+    link->open--;
+    netif_stop_queue(dev);
+    del_timer_sync(&info->watchdog);
+
+    return 0;
+} /* axnet_close */
+
+/*======================================================================
+
+    Hard reset the card.  This used to pause for the same period that
+    a 8390 reset command required, but that shouldn't be necessary.
+
+======================================================================*/
+
+static void axnet_reset_8390(struct net_device *dev)
+{
+    unsigned int nic_base = dev->base_addr;
+    int i;
+
+    ei_status.txing = ei_status.dmaing = 0;
+
+    outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, nic_base + E8390_CMD);
+
+    outb(inb(nic_base + AXNET_RESET), nic_base + AXNET_RESET);
+
+    for (i = 0; i < 100; i++) {
+	if ((inb_p(nic_base+EN0_ISR) & ENISR_RESET) != 0)
+	    break;
+	udelay(100);
+    }
+    outb_p(ENISR_RESET, nic_base + EN0_ISR); /* Ack intr. */
+    
+    if (i == 100)
+	netdev_err(dev, "axnet_reset_8390() did not complete\n");
+    
+} /* axnet_reset_8390 */
+
+/*====================================================================*/
+
+static irqreturn_t ei_irq_wrapper(int irq, void *dev_id)
+{
+    struct net_device *dev = dev_id;
+    PRIV(dev)->stale = 0;
+    return ax_interrupt(irq, dev_id);
+}
+
+static void ei_watchdog(u_long arg)
+{
+    struct net_device *dev = (struct net_device *)(arg);
+    axnet_dev_t *info = PRIV(dev);
+    unsigned int nic_base = dev->base_addr;
+    unsigned int mii_addr = nic_base + AXNET_MII_EEP;
+    u_short link;
+
+    if (!netif_device_present(dev)) goto reschedule;
+
+    /* Check for pending interrupt with expired latency timer: with
+       this, we can limp along even if the interrupt is blocked */
+    if (info->stale++ && (inb_p(nic_base + EN0_ISR) & ENISR_ALL)) {
+	if (!info->fast_poll)
+	    netdev_info(dev, "interrupt(s) dropped!\n");
+	ei_irq_wrapper(dev->irq, dev);
+	info->fast_poll = HZ;
+    }
+    if (info->fast_poll) {
+	info->fast_poll--;
+	info->watchdog.expires = jiffies + 1;
+	add_timer(&info->watchdog);
+	return;
+    }
+
+    if (info->phy_id < 0)
+	goto reschedule;
+    link = mdio_read(mii_addr, info->phy_id, 1);
+    if (!link || (link == 0xffff)) {
+	netdev_info(dev, "MII is missing!\n");
+	info->phy_id = -1;
+	goto reschedule;
+    }
+
+    link &= 0x0004;
+    if (link != info->link_status) {
+	u_short p = mdio_read(mii_addr, info->phy_id, 5);
+	netdev_info(dev, "%s link beat\n", link ? "found" : "lost");
+	if (link) {
+	    info->duplex_flag = (p & 0x0140) ? 0x80 : 0x00;
+	    if (p)
+		netdev_info(dev, "autonegotiation complete: %dbaseT-%cD selected\n",
+			    (p & 0x0180) ? 100 : 10, (p & 0x0140) ? 'F' : 'H');
+	    else
+		netdev_info(dev, "link partner did not autonegotiate\n");
+	    AX88190_init(dev, 1);
+	}
+	info->link_status = link;
+    }
+
+reschedule:
+    info->watchdog.expires = jiffies + HZ;
+    add_timer(&info->watchdog);
+}
+
+/*====================================================================*/
+
+static int axnet_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+    axnet_dev_t *info = PRIV(dev);
+    struct mii_ioctl_data *data = if_mii(rq);
+    unsigned int mii_addr = dev->base_addr + AXNET_MII_EEP;
+    switch (cmd) {
+    case SIOCGMIIPHY:
+	data->phy_id = info->phy_id;
+    case SIOCGMIIREG:		/* Read MII PHY register. */
+	data->val_out = mdio_read(mii_addr, data->phy_id, data->reg_num & 0x1f);
+	return 0;
+    case SIOCSMIIREG:		/* Write MII PHY register. */
+	mdio_write(mii_addr, data->phy_id, data->reg_num & 0x1f, data->val_in);
+	return 0;
+    }
+    return -EOPNOTSUPP;
+}
+
+/*====================================================================*/
+
+static void get_8390_hdr(struct net_device *dev,
+			 struct e8390_pkt_hdr *hdr,
+			 int ring_page)
+{
+    unsigned int nic_base = dev->base_addr;
+
+    outb_p(0, nic_base + EN0_RSARLO);		/* On page boundary */
+    outb_p(ring_page, nic_base + EN0_RSARHI);
+    outb_p(E8390_RREAD+E8390_START, nic_base + AXNET_CMD);
+
+    insw(nic_base + AXNET_DATAPORT, hdr,
+	    sizeof(struct e8390_pkt_hdr)>>1);
+    /* Fix for big endian systems */
+    hdr->count = le16_to_cpu(hdr->count);
+
+}
+
+/*====================================================================*/
+
+static void block_input(struct net_device *dev, int count,
+			struct sk_buff *skb, int ring_offset)
+{
+    unsigned int nic_base = dev->base_addr;
+    int xfer_count = count;
+    char *buf = skb->data;
+
+    if ((ei_debug > 4) && (count != 4))
+	    pr_debug("%s: [bi=%d]\n", dev->name, count+4);
+    outb_p(ring_offset & 0xff, nic_base + EN0_RSARLO);
+    outb_p(ring_offset >> 8, nic_base + EN0_RSARHI);
+    outb_p(E8390_RREAD+E8390_START, nic_base + AXNET_CMD);
+
+    insw(nic_base + AXNET_DATAPORT,buf,count>>1);
+    if (count & 0x01)
+	buf[count-1] = inb(nic_base + AXNET_DATAPORT), xfer_count++;
+
+}
+
+/*====================================================================*/
+
+static void block_output(struct net_device *dev, int count,
+			 const u_char *buf, const int start_page)
+{
+    unsigned int nic_base = dev->base_addr;
+
+    pr_debug("%s: [bo=%d]\n", dev->name, count);
+
+    /* Round the count up for word writes.  Do we need to do this?
+       What effect will an odd byte count have on the 8390?
+       I should check someday. */
+    if (count & 0x01)
+	count++;
+
+    outb_p(0x00, nic_base + EN0_RSARLO);
+    outb_p(start_page, nic_base + EN0_RSARHI);
+    outb_p(E8390_RWRITE+E8390_START, nic_base + AXNET_CMD);
+    outsw(nic_base + AXNET_DATAPORT, buf, count>>1);
+}
+
+static const struct pcmcia_device_id axnet_ids[] = {
+	PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x016c, 0x0081),
+	PCMCIA_DEVICE_MANF_CARD(0x018a, 0x0301),
+	PCMCIA_DEVICE_MANF_CARD(0x01bf, 0x2328),
+	PCMCIA_DEVICE_MANF_CARD(0x026f, 0x0301),
+	PCMCIA_DEVICE_MANF_CARD(0x026f, 0x0303),
+	PCMCIA_DEVICE_MANF_CARD(0x026f, 0x0309),
+	PCMCIA_DEVICE_MANF_CARD(0x0274, 0x1106),
+	PCMCIA_DEVICE_MANF_CARD(0x8a01, 0xc1ab),
+	PCMCIA_DEVICE_MANF_CARD(0x021b, 0x0202), 
+	PCMCIA_DEVICE_MANF_CARD(0xffff, 0x1090),
+	PCMCIA_DEVICE_PROD_ID12("AmbiCom,Inc.", "Fast Ethernet PC Card(AMB8110)", 0x49b020a7, 0x119cc9fc),
+	PCMCIA_DEVICE_PROD_ID124("Fast Ethernet", "16-bit PC Card", "AX88190", 0xb4be14e3, 0x9a12eb6a, 0xab9be5ef),
+	PCMCIA_DEVICE_PROD_ID12("ASIX", "AX88190", 0x0959823b, 0xab9be5ef),
+	PCMCIA_DEVICE_PROD_ID12("Billionton", "LNA-100B", 0x552ab682, 0xbc3b87e1),
+	PCMCIA_DEVICE_PROD_ID12("CHEETAH ETHERCARD", "EN2228", 0x00fa7bc8, 0x00e990cc),
+	PCMCIA_DEVICE_PROD_ID12("CNet", "CNF301", 0xbc477dde, 0x78c5f40b),
+	PCMCIA_DEVICE_PROD_ID12("corega K.K.", "corega FEther PCC-TXD", 0x5261440f, 0x436768c5),
+	PCMCIA_DEVICE_PROD_ID12("corega K.K.", "corega FEtherII PCC-TXD", 0x5261440f, 0x730df72e),
+	PCMCIA_DEVICE_PROD_ID12("corega K.K.", "corega FEther PCC-TXM", 0x5261440f, 0x3abbd061),
+	PCMCIA_DEVICE_PROD_ID12("Dynalink", "L100C16", 0x55632fd5, 0x66bc2a90),
+	PCMCIA_DEVICE_PROD_ID12("IO DATA", "ETXPCM", 0x547e66dc, 0x233adac2),
+	PCMCIA_DEVICE_PROD_ID12("Linksys", "EtherFast 10/100 PC Card (PCMPC100 V3)", 0x0733cc81, 0x232019a8),
+	PCMCIA_DEVICE_PROD_ID12("MELCO", "LPC3-TX", 0x481e0094, 0xf91af609),
+	PCMCIA_DEVICE_PROD_ID12("NETGEAR", "FA411", 0x9aa79dc3, 0x40fad875),
+	PCMCIA_DEVICE_PROD_ID12("PCMCIA", "100BASE", 0x281f1c5d, 0x7c2add04),
+	PCMCIA_DEVICE_PROD_ID12("PCMCIA", "FastEtherCard", 0x281f1c5d, 0x7ef26116),
+	PCMCIA_DEVICE_PROD_ID12("PCMCIA", "FEP501", 0x281f1c5d, 0x2e272058),
+	PCMCIA_DEVICE_PROD_ID14("Network Everywhere", "AX88190", 0x820a67b6,  0xab9be5ef),
+	PCMCIA_DEVICE_NULL,
+};
+MODULE_DEVICE_TABLE(pcmcia, axnet_ids);
+
+static struct pcmcia_driver axnet_cs_driver = {
+	.owner		= THIS_MODULE,
+	.name		= "axnet_cs",
+	.probe		= axnet_probe,
+	.remove		= axnet_detach,
+	.id_table       = axnet_ids,
+	.suspend	= axnet_suspend,
+	.resume		= axnet_resume,
+};
+
+static int __init init_axnet_cs(void)
+{
+	return pcmcia_register_driver(&axnet_cs_driver);
+}
+
+static void __exit exit_axnet_cs(void)
+{
+	pcmcia_unregister_driver(&axnet_cs_driver);
+}
+
+module_init(init_axnet_cs);
+module_exit(exit_axnet_cs);
+
+/*====================================================================*/
+
+/* 8390.c: A general NS8390 ethernet driver core for linux. */
+/*
+	Written 1992-94 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
+
+  This is the chip-specific code for many 8390-based ethernet adaptors.
+  This is not a complete driver, it must be combined with board-specific
+  code such as ne.c, wd.c, 3c503.c, etc.
+
+  Seeing how at least eight drivers use this code, (not counting the
+  PCMCIA ones either) it is easy to break some card by what seems like
+  a simple innocent change. Please contact me or Donald if you think
+  you have found something that needs changing. -- PG
+
+  Changelog:
+
+  Paul Gortmaker	: remove set_bit lock, other cleanups.
+  Paul Gortmaker	: add ei_get_8390_hdr() so we can pass skb's to 
+			  ei_block_input() for eth_io_copy_and_sum().
+  Paul Gortmaker	: exchange static int ei_pingpong for a #define,
+			  also add better Tx error handling.
+  Paul Gortmaker	: rewrite Rx overrun handling as per NS specs.
+  Alexey Kuznetsov	: use the 8390's six bit hash multicast filter.
+  Paul Gortmaker	: tweak ANK's above multicast changes a bit.
+  Paul Gortmaker	: update packet statistics for v2.1.x
+  Alan Cox		: support arbitrary stupid port mappings on the
+  			  68K Macintosh. Support >16bit I/O spaces
+  Paul Gortmaker	: add kmod support for auto-loading of the 8390
+			  module by all drivers that require it.
+  Alan Cox		: Spinlocking work, added 'BUG_83C690'
+  Paul Gortmaker	: Separate out Tx timeout code from Tx path.
+
+  Sources:
+  The National Semiconductor LAN Databook, and the 3Com 3c503 databook.
+
+  */
+
+#include <linux/bitops.h>
+#include <asm/irq.h>
+#include <linux/fcntl.h>
+#include <linux/in.h>
+#include <linux/interrupt.h>
+
+#define BUG_83C690
+
+/* These are the operational function interfaces to board-specific
+   routines.
+	void reset_8390(struct net_device *dev)
+		Resets the board associated with DEV, including a hardware reset of
+		the 8390.  This is only called when there is a transmit timeout, and
+		it is always followed by 8390_init().
+	void block_output(struct net_device *dev, int count, const unsigned char *buf,
+					  int start_page)
+		Write the COUNT bytes of BUF to the packet buffer at START_PAGE.  The
+		"page" value uses the 8390's 256-byte pages.
+	void get_8390_hdr(struct net_device *dev, struct e8390_hdr *hdr, int ring_page)
+		Read the 4 byte, page aligned 8390 header. *If* there is a
+		subsequent read, it will be of the rest of the packet.
+	void block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset)
+		Read COUNT bytes from the packet buffer into the skb data area. Start 
+		reading from RING_OFFSET, the address as the 8390 sees it.  This will always
+		follow the read of the 8390 header. 
+*/
+#define ei_reset_8390 (ei_local->reset_8390)
+#define ei_block_output (ei_local->block_output)
+#define ei_block_input (ei_local->block_input)
+#define ei_get_8390_hdr (ei_local->get_8390_hdr)
+
+/* use 0 for production, 1 for verification, >2 for debug */
+#ifndef ei_debug
+int ei_debug = 1;
+#endif
+
+/* Index to functions. */
+static void ei_tx_intr(struct net_device *dev);
+static void ei_tx_err(struct net_device *dev);
+static void ei_receive(struct net_device *dev);
+static void ei_rx_overrun(struct net_device *dev);
+
+/* Routines generic to NS8390-based boards. */
+static void NS8390_trigger_send(struct net_device *dev, unsigned int length,
+								int start_page);
+static void do_set_multicast_list(struct net_device *dev);
+
+/*
+ *	SMP and the 8390 setup.
+ *
+ *	The 8390 isn't exactly designed to be multithreaded on RX/TX. There is
+ *	a page register that controls bank and packet buffer access. We guard
+ *	this with ei_local->page_lock. Nobody should assume or set the page other
+ *	than zero when the lock is not held. Lock holders must restore page 0
+ *	before unlocking. Even pure readers must take the lock to protect in 
+ *	page 0.
+ *
+ *	To make life difficult the chip can also be very slow. We therefore can't
+ *	just use spinlocks. For the longer lockups we disable the irq the device
+ *	sits on and hold the lock. We must hold the lock because there is a dual
+ *	processor case other than interrupts (get stats/set multicast list in
+ *	parallel with each other and transmit).
+ *
+ *	Note: in theory we can just disable the irq on the card _but_ there is
+ *	a latency on SMP irq delivery. So we can easily go "disable irq" "sync irqs"
+ *	enter lock, take the queued irq. So we waddle instead of flying.
+ *
+ *	Finally by special arrangement for the purpose of being generally 
+ *	annoying the transmit function is called bh atomic. That places
+ *	restrictions on the user context callers as disable_irq won't save
+ *	them.
+ */
+ 
+/**
+ * ax_open - Open/initialize the board.
+ * @dev: network device to initialize
+ *
+ * This routine goes all-out, setting everything
+ * up anew at each open, even though many of these registers should only
+ * need to be set once at boot.
+ */
+static int ax_open(struct net_device *dev)
+{
+	unsigned long flags;
+	struct ei_device *ei_local = netdev_priv(dev);
+
+	/*
+	 *	Grab the page lock so we own the register set, then call
+	 *	the init function.
+	 */
+      
+      	spin_lock_irqsave(&ei_local->page_lock, flags);
+	AX88190_init(dev, 1);
+	/* Set the flag before we drop the lock, That way the IRQ arrives
+	   after its set and we get no silly warnings */
+	netif_start_queue(dev);
+      	spin_unlock_irqrestore(&ei_local->page_lock, flags);
+	ei_local->irqlock = 0;
+	return 0;
+}
+
+#define dev_lock(dev) (((struct ei_device *)netdev_priv(dev))->page_lock)
+
+/**
+ * ax_close - shut down network device
+ * @dev: network device to close
+ *
+ * Opposite of ax_open(). Only used when "ifconfig <devname> down" is done.
+ */
+static int ax_close(struct net_device *dev)
+{
+	unsigned long flags;
+
+	/*
+	 *      Hold the page lock during close
+	 */
+
+	spin_lock_irqsave(&dev_lock(dev), flags);
+	AX88190_init(dev, 0);
+	spin_unlock_irqrestore(&dev_lock(dev), flags);
+	netif_stop_queue(dev);
+	return 0;
+}
+
+/**
+ * axnet_tx_timeout - handle transmit time out condition
+ * @dev: network device which has apparently fallen asleep
+ *
+ * Called by kernel when device never acknowledges a transmit has
+ * completed (or failed) - i.e. never posted a Tx related interrupt.
+ */
+
+static void axnet_tx_timeout(struct net_device *dev)
+{
+	long e8390_base = dev->base_addr;
+	struct ei_device *ei_local = netdev_priv(dev);
+	int txsr, isr, tickssofar = jiffies - dev_trans_start(dev);
+	unsigned long flags;
+
+	dev->stats.tx_errors++;
+
+	spin_lock_irqsave(&ei_local->page_lock, flags);
+	txsr = inb(e8390_base+EN0_TSR);
+	isr = inb(e8390_base+EN0_ISR);
+	spin_unlock_irqrestore(&ei_local->page_lock, flags);
+
+	netdev_printk(KERN_DEBUG, dev,
+		      "Tx timed out, %s TSR=%#2x, ISR=%#2x, t=%d.\n",
+		      (txsr & ENTSR_ABT) ? "excess collisions." :
+		      (isr) ? "lost interrupt?" : "cable problem?",
+		      txsr, isr, tickssofar);
+
+	if (!isr && !dev->stats.tx_packets) 
+	{
+		/* The 8390 probably hasn't gotten on the cable yet. */
+		ei_local->interface_num ^= 1;   /* Try a different xcvr.  */
+	}
+
+	/* Ugly but a reset can be slow, yet must be protected */
+		
+	spin_lock_irqsave(&ei_local->page_lock, flags);
+		
+	/* Try to restart the card.  Perhaps the user has fixed something. */
+	ei_reset_8390(dev);
+	AX88190_init(dev, 1);
+		
+	spin_unlock_irqrestore(&ei_local->page_lock, flags);
+	netif_wake_queue(dev);
+}
+    
+/**
+ * axnet_start_xmit - begin packet transmission
+ * @skb: packet to be sent
+ * @dev: network device to which packet is sent
+ *
+ * Sends a packet to an 8390 network device.
+ */
+ 
+static netdev_tx_t axnet_start_xmit(struct sk_buff *skb,
+					  struct net_device *dev)
+{
+	long e8390_base = dev->base_addr;
+	struct ei_device *ei_local = netdev_priv(dev);
+	int length, send_length, output_page;
+	unsigned long flags;
+	u8 packet[ETH_ZLEN];
+	
+	netif_stop_queue(dev);
+
+	length = skb->len;
+
+	/* Mask interrupts from the ethercard. 
+	   SMP: We have to grab the lock here otherwise the IRQ handler
+	   on another CPU can flip window and race the IRQ mask set. We end
+	   up trashing the mcast filter not disabling irqs if we don't lock */
+	   
+	spin_lock_irqsave(&ei_local->page_lock, flags);
+	outb_p(0x00, e8390_base + EN0_IMR);
+	
+	/*
+	 *	Slow phase with lock held.
+	 */
+	 
+	ei_local->irqlock = 1;
+
+	send_length = max(length, ETH_ZLEN);
+
+	/*
+	 * We have two Tx slots available for use. Find the first free
+	 * slot, and then perform some sanity checks. With two Tx bufs,
+	 * you get very close to transmitting back-to-back packets. With
+	 * only one Tx buf, the transmitter sits idle while you reload the
+	 * card, leaving a substantial gap between each transmitted packet.
+	 */
+
+	if (ei_local->tx1 == 0) 
+	{
+		output_page = ei_local->tx_start_page;
+		ei_local->tx1 = send_length;
+		if (ei_debug  &&  ei_local->tx2 > 0)
+			netdev_printk(KERN_DEBUG, dev,
+				      "idle transmitter tx2=%d, lasttx=%d, txing=%d\n",
+				      ei_local->tx2, ei_local->lasttx,
+				      ei_local->txing);
+	}
+	else if (ei_local->tx2 == 0) 
+	{
+		output_page = ei_local->tx_start_page + TX_PAGES/2;
+		ei_local->tx2 = send_length;
+		if (ei_debug  &&  ei_local->tx1 > 0)
+			netdev_printk(KERN_DEBUG, dev,
+				      "idle transmitter, tx1=%d, lasttx=%d, txing=%d\n",
+				      ei_local->tx1, ei_local->lasttx,
+				      ei_local->txing);
+	}
+	else
+	{	/* We should never get here. */
+		if (ei_debug)
+			netdev_printk(KERN_DEBUG, dev,
+				      "No Tx buffers free! tx1=%d tx2=%d last=%d\n",
+				      ei_local->tx1, ei_local->tx2,
+				      ei_local->lasttx);
+		ei_local->irqlock = 0;
+		netif_stop_queue(dev);
+		outb_p(ENISR_ALL, e8390_base + EN0_IMR);
+		spin_unlock_irqrestore(&ei_local->page_lock, flags);
+		dev->stats.tx_errors++;
+		return NETDEV_TX_BUSY;
+	}
+
+	/*
+	 * Okay, now upload the packet and trigger a send if the transmitter
+	 * isn't already sending. If it is busy, the interrupt handler will
+	 * trigger the send later, upon receiving a Tx done interrupt.
+	 */
+
+	if (length == skb->len)
+		ei_block_output(dev, length, skb->data, output_page);
+	else {
+		memset(packet, 0, ETH_ZLEN);
+		skb_copy_from_linear_data(skb, packet, skb->len);
+		ei_block_output(dev, length, packet, output_page);
+	}
+	
+	if (! ei_local->txing) 
+	{
+		ei_local->txing = 1;
+		NS8390_trigger_send(dev, send_length, output_page);
+		dev->trans_start = jiffies;
+		if (output_page == ei_local->tx_start_page) 
+		{
+			ei_local->tx1 = -1;
+			ei_local->lasttx = -1;
+		}
+		else 
+		{
+			ei_local->tx2 = -1;
+			ei_local->lasttx = -2;
+		}
+	}
+	else ei_local->txqueue++;
+
+	if (ei_local->tx1  &&  ei_local->tx2)
+		netif_stop_queue(dev);
+	else
+		netif_start_queue(dev);
+
+	/* Turn 8390 interrupts back on. */
+	ei_local->irqlock = 0;
+	outb_p(ENISR_ALL, e8390_base + EN0_IMR);
+	
+	spin_unlock_irqrestore(&ei_local->page_lock, flags);
+
+	dev_kfree_skb (skb);
+	dev->stats.tx_bytes += send_length;
+    
+	return NETDEV_TX_OK;
+}
+
+/**
+ * ax_interrupt - handle the interrupts from an 8390
+ * @irq: interrupt number
+ * @dev_id: a pointer to the net_device
+ *
+ * Handle the ether interface interrupts. We pull packets from
+ * the 8390 via the card specific functions and fire them at the networking
+ * stack. We also handle transmit completions and wake the transmit path if
+ * necessary. We also update the counters and do other housekeeping as
+ * needed.
+ */
+
+static irqreturn_t ax_interrupt(int irq, void *dev_id)
+{
+	struct net_device *dev = dev_id;
+	long e8390_base;
+	int interrupts, nr_serviced = 0, i;
+	struct ei_device *ei_local;
+    	int handled = 0;
+	unsigned long flags;
+
+	e8390_base = dev->base_addr;
+	ei_local = netdev_priv(dev);
+
+	/*
+	 *	Protect the irq test too.
+	 */
+	 
+	spin_lock_irqsave(&ei_local->page_lock, flags);
+
+	if (ei_local->irqlock) {
+#if 1 /* This might just be an interrupt for a PCI device sharing this line */
+		const char *msg;
+		/* The "irqlock" check is only for testing. */
+		if (ei_local->irqlock)
+			msg = "Interrupted while interrupts are masked!";
+		else
+			msg = "Reentering the interrupt handler!";
+		netdev_info(dev, "%s, isr=%#2x imr=%#2x\n",
+			    msg,
+			    inb_p(e8390_base + EN0_ISR),
+			    inb_p(e8390_base + EN0_IMR));
+#endif
+		spin_unlock_irqrestore(&ei_local->page_lock, flags);
+		return IRQ_NONE;
+	}
+    
+	if (ei_debug > 3)
+		netdev_printk(KERN_DEBUG, dev, "interrupt(isr=%#2.2x)\n",
+			      inb_p(e8390_base + EN0_ISR));
+
+	outb_p(0x00, e8390_base + EN0_ISR);
+	ei_local->irqlock = 1;
+   
+	/* !!Assumption!! -- we stay in page 0.	 Don't break this. */
+	while ((interrupts = inb_p(e8390_base + EN0_ISR)) != 0 &&
+	       ++nr_serviced < MAX_SERVICE)
+	{
+		if (!netif_running(dev) || (interrupts == 0xff)) {
+			if (ei_debug > 1)
+				netdev_warn(dev,
+					    "interrupt from stopped card\n");
+			outb_p(interrupts, e8390_base + EN0_ISR);
+			interrupts = 0;
+			break;
+		}
+		handled = 1;
+
+		/* AX88190 bug fix. */
+		outb_p(interrupts, e8390_base + EN0_ISR);
+		for (i = 0; i < 10; i++) {
+			if (!(inb(e8390_base + EN0_ISR) & interrupts))
+				break;
+			outb_p(0, e8390_base + EN0_ISR);
+			outb_p(interrupts, e8390_base + EN0_ISR);
+		}
+		if (interrupts & ENISR_OVER) 
+			ei_rx_overrun(dev);
+		else if (interrupts & (ENISR_RX+ENISR_RX_ERR)) 
+		{
+			/* Got a good (?) packet. */
+			ei_receive(dev);
+		}
+		/* Push the next to-transmit packet through. */
+		if (interrupts & ENISR_TX)
+			ei_tx_intr(dev);
+		else if (interrupts & ENISR_TX_ERR)
+			ei_tx_err(dev);
+
+		if (interrupts & ENISR_COUNTERS) 
+		{
+			dev->stats.rx_frame_errors += inb_p(e8390_base + EN0_COUNTER0);
+			dev->stats.rx_crc_errors   += inb_p(e8390_base + EN0_COUNTER1);
+			dev->stats.rx_missed_errors+= inb_p(e8390_base + EN0_COUNTER2);
+		}
+	}
+    
+	if (interrupts && ei_debug > 3) 
+	{
+		handled = 1;
+		if (nr_serviced >= MAX_SERVICE) 
+		{
+			/* 0xFF is valid for a card removal */
+			if(interrupts!=0xFF)
+				netdev_warn(dev, "Too much work at interrupt, status %#2.2x\n",
+					    interrupts);
+			outb_p(ENISR_ALL, e8390_base + EN0_ISR); /* Ack. most intrs. */
+		} else {
+			netdev_warn(dev, "unknown interrupt %#2x\n",
+				    interrupts);
+			outb_p(0xff, e8390_base + EN0_ISR); /* Ack. all intrs. */
+		}
+	}
+
+	/* Turn 8390 interrupts back on. */
+	ei_local->irqlock = 0;
+	outb_p(ENISR_ALL, e8390_base + EN0_IMR);
+
+	spin_unlock_irqrestore(&ei_local->page_lock, flags);
+	return IRQ_RETVAL(handled);
+}
+
+/**
+ * ei_tx_err - handle transmitter error
+ * @dev: network device which threw the exception
+ *
+ * A transmitter error has happened. Most likely excess collisions (which
+ * is a fairly normal condition). If the error is one where the Tx will
+ * have been aborted, we try and send another one right away, instead of
+ * letting the failed packet sit and collect dust in the Tx buffer. This
+ * is a much better solution as it avoids kernel based Tx timeouts, and
+ * an unnecessary card reset.
+ *
+ * Called with lock held.
+ */
+
+static void ei_tx_err(struct net_device *dev)
+{
+	long e8390_base = dev->base_addr;
+	unsigned char txsr = inb_p(e8390_base+EN0_TSR);
+	unsigned char tx_was_aborted = txsr & (ENTSR_ABT+ENTSR_FU);
+
+#ifdef VERBOSE_ERROR_DUMP
+	netdev_printk(KERN_DEBUG, dev,
+		      "transmitter error (%#2x):", txsr);
+	if (txsr & ENTSR_ABT)
+		pr_cont(" excess-collisions");
+	if (txsr & ENTSR_ND)
+		pr_cont(" non-deferral");
+	if (txsr & ENTSR_CRS)
+		pr_cont(" lost-carrier");
+	if (txsr & ENTSR_FU)
+		pr_cont(" FIFO-underrun");
+	if (txsr & ENTSR_CDH)
+		pr_cont(" lost-heartbeat");
+	pr_cont("\n");
+#endif
+
+	if (tx_was_aborted)
+		ei_tx_intr(dev);
+	else 
+	{
+		dev->stats.tx_errors++;
+		if (txsr & ENTSR_CRS) dev->stats.tx_carrier_errors++;
+		if (txsr & ENTSR_CDH) dev->stats.tx_heartbeat_errors++;
+		if (txsr & ENTSR_OWC) dev->stats.tx_window_errors++;
+	}
+}
+
+/**
+ * ei_tx_intr - transmit interrupt handler
+ * @dev: network device for which tx intr is handled
+ *
+ * We have finished a transmit: check for errors and then trigger the next
+ * packet to be sent. Called with lock held.
+ */
+
+static void ei_tx_intr(struct net_device *dev)
+{
+	long e8390_base = dev->base_addr;
+	struct ei_device *ei_local = netdev_priv(dev);
+	int status = inb(e8390_base + EN0_TSR);
+    
+	/*
+	 * There are two Tx buffers, see which one finished, and trigger
+	 * the send of another one if it exists.
+	 */
+	ei_local->txqueue--;
+
+	if (ei_local->tx1 < 0) 
+	{
+		if (ei_local->lasttx != 1 && ei_local->lasttx != -1)
+			netdev_err(dev, "%s: bogus last_tx_buffer %d, tx1=%d\n",
+				   ei_local->name, ei_local->lasttx,
+				   ei_local->tx1);
+		ei_local->tx1 = 0;
+		if (ei_local->tx2 > 0) 
+		{
+			ei_local->txing = 1;
+			NS8390_trigger_send(dev, ei_local->tx2, ei_local->tx_start_page + 6);
+			dev->trans_start = jiffies;
+			ei_local->tx2 = -1,
+			ei_local->lasttx = 2;
+		}
+		else ei_local->lasttx = 20, ei_local->txing = 0;	
+	}
+	else if (ei_local->tx2 < 0) 
+	{
+		if (ei_local->lasttx != 2  &&  ei_local->lasttx != -2)
+			netdev_info(dev, "%s: bogus last_tx_buffer %d, tx2=%d\n",
+				    ei_local->name, ei_local->lasttx,
+				    ei_local->tx2);
+		ei_local->tx2 = 0;
+		if (ei_local->tx1 > 0) 
+		{
+			ei_local->txing = 1;
+			NS8390_trigger_send(dev, ei_local->tx1, ei_local->tx_start_page);
+			dev->trans_start = jiffies;
+			ei_local->tx1 = -1;
+			ei_local->lasttx = 1;
+		}
+		else
+			ei_local->lasttx = 10, ei_local->txing = 0;
+	}
+//	else
+//		netdev_warn(dev, "unexpected TX-done interrupt, lasttx=%d\n",
+//			    ei_local->lasttx);
+
+	/* Minimize Tx latency: update the statistics after we restart TXing. */
+	if (status & ENTSR_COL)
+		dev->stats.collisions++;
+	if (status & ENTSR_PTX)
+		dev->stats.tx_packets++;
+	else 
+	{
+		dev->stats.tx_errors++;
+		if (status & ENTSR_ABT) 
+		{
+			dev->stats.tx_aborted_errors++;
+			dev->stats.collisions += 16;
+		}
+		if (status & ENTSR_CRS) 
+			dev->stats.tx_carrier_errors++;
+		if (status & ENTSR_FU) 
+			dev->stats.tx_fifo_errors++;
+		if (status & ENTSR_CDH)
+			dev->stats.tx_heartbeat_errors++;
+		if (status & ENTSR_OWC)
+			dev->stats.tx_window_errors++;
+	}
+	netif_wake_queue(dev);
+}
+
+/**
+ * ei_receive - receive some packets
+ * @dev: network device with which receive will be run
+ *
+ * We have a good packet(s), get it/them out of the buffers. 
+ * Called with lock held.
+ */
+
+static void ei_receive(struct net_device *dev)
+{
+	long e8390_base = dev->base_addr;
+	struct ei_device *ei_local = netdev_priv(dev);
+	unsigned char rxing_page, this_frame, next_frame;
+	unsigned short current_offset;
+	int rx_pkt_count = 0;
+	struct e8390_pkt_hdr rx_frame;
+    
+	while (++rx_pkt_count < 10) 
+	{
+		int pkt_len, pkt_stat;
+		
+		/* Get the rx page (incoming packet pointer). */
+		rxing_page = inb_p(e8390_base + EN1_CURPAG -1);
+		
+		/* Remove one frame from the ring.  Boundary is always a page behind. */
+		this_frame = inb_p(e8390_base + EN0_BOUNDARY) + 1;
+		if (this_frame >= ei_local->stop_page)
+			this_frame = ei_local->rx_start_page;
+		
+		/* Someday we'll omit the previous, iff we never get this message.
+		   (There is at least one clone claimed to have a problem.)  
+		   
+		   Keep quiet if it looks like a card removal. One problem here
+		   is that some clones crash in roughly the same way.
+		 */
+		if (ei_debug > 0  &&  this_frame != ei_local->current_page && (this_frame!=0x0 || rxing_page!=0xFF))
+		    netdev_err(dev, "mismatched read page pointers %2x vs %2x\n",
+			       this_frame, ei_local->current_page);
+		
+		if (this_frame == rxing_page)	/* Read all the frames? */
+			break;				/* Done for now */
+		
+		current_offset = this_frame << 8;
+		ei_get_8390_hdr(dev, &rx_frame, this_frame);
+		
+		pkt_len = rx_frame.count - sizeof(struct e8390_pkt_hdr);
+		pkt_stat = rx_frame.status;
+		
+		next_frame = this_frame + 1 + ((pkt_len+4)>>8);
+		
+		if (pkt_len < 60  ||  pkt_len > 1518) 
+		{
+			if (ei_debug)
+				netdev_printk(KERN_DEBUG, dev,
+					      "bogus packet size: %d, status=%#2x nxpg=%#2x\n",
+					      rx_frame.count, rx_frame.status,
+					      rx_frame.next);
+			dev->stats.rx_errors++;
+			dev->stats.rx_length_errors++;
+		}
+		 else if ((pkt_stat & 0x0F) == ENRSR_RXOK) 
+		{
+			struct sk_buff *skb;
+			
+			skb = dev_alloc_skb(pkt_len+2);
+			if (skb == NULL) 
+			{
+				if (ei_debug > 1)
+					netdev_printk(KERN_DEBUG, dev,
+						      "Couldn't allocate a sk_buff of size %d\n",
+						      pkt_len);
+				dev->stats.rx_dropped++;
+				break;
+			}
+			else
+			{
+				skb_reserve(skb,2);	/* IP headers on 16 byte boundaries */
+				skb_put(skb, pkt_len);	/* Make room */
+				ei_block_input(dev, pkt_len, skb, current_offset + sizeof(rx_frame));
+				skb->protocol=eth_type_trans(skb,dev);
+				netif_rx(skb);
+				dev->stats.rx_packets++;
+				dev->stats.rx_bytes += pkt_len;
+				if (pkt_stat & ENRSR_PHY)
+					dev->stats.multicast++;
+			}
+		} 
+		else 
+		{
+			if (ei_debug)
+				netdev_printk(KERN_DEBUG, dev,
+					      "bogus packet: status=%#2x nxpg=%#2x size=%d\n",
+					      rx_frame.status, rx_frame.next,
+					      rx_frame.count);
+			dev->stats.rx_errors++;
+			/* NB: The NIC counts CRC, frame and missed errors. */
+			if (pkt_stat & ENRSR_FO)
+				dev->stats.rx_fifo_errors++;
+		}
+		next_frame = rx_frame.next;
+		
+		/* This _should_ never happen: it's here for avoiding bad clones. */
+		if (next_frame >= ei_local->stop_page) {
+			netdev_info(dev, "next frame inconsistency, %#2x\n",
+				    next_frame);
+			next_frame = ei_local->rx_start_page;
+		}
+		ei_local->current_page = next_frame;
+		outb_p(next_frame-1, e8390_base+EN0_BOUNDARY);
+	}
+}
+
+/**
+ * ei_rx_overrun - handle receiver overrun
+ * @dev: network device which threw exception
+ *
+ * We have a receiver overrun: we have to kick the 8390 to get it started
+ * again. Problem is that you have to kick it exactly as NS prescribes in
+ * the updated datasheets, or "the NIC may act in an unpredictable manner."
+ * This includes causing "the NIC to defer indefinitely when it is stopped
+ * on a busy network."  Ugh.
+ * Called with lock held. Don't call this with the interrupts off or your
+ * computer will hate you - it takes 10ms or so. 
+ */
+
+static void ei_rx_overrun(struct net_device *dev)
+{
+	axnet_dev_t *info = PRIV(dev);
+	long e8390_base = dev->base_addr;
+	unsigned char was_txing, must_resend = 0;
+    
+	/*
+	 * Record whether a Tx was in progress and then issue the
+	 * stop command.
+	 */
+	was_txing = inb_p(e8390_base+E8390_CMD) & E8390_TRANS;
+	outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base+E8390_CMD);
+    
+	if (ei_debug > 1)
+		netdev_printk(KERN_DEBUG, dev, "Receiver overrun\n");
+	dev->stats.rx_over_errors++;
+    
+	/* 
+	 * Wait a full Tx time (1.2ms) + some guard time, NS says 1.6ms total.
+	 * We wait at least 2ms.
+	 */
+
+	mdelay(2);
+
+	/*
+	 * Reset RBCR[01] back to zero as per magic incantation.
+	 */
+	outb_p(0x00, e8390_base+EN0_RCNTLO);
+	outb_p(0x00, e8390_base+EN0_RCNTHI);
+
+	/*
+	 * See if any Tx was interrupted or not. According to NS, this
+	 * step is vital, and skipping it will cause no end of havoc.
+	 */
+
+	if (was_txing)
+	{ 
+		unsigned char tx_completed = inb_p(e8390_base+EN0_ISR) & (ENISR_TX+ENISR_TX_ERR);
+		if (!tx_completed)
+			must_resend = 1;
+	}
+
+	/*
+	 * Have to enter loopback mode and then restart the NIC before
+	 * you are allowed to slurp packets up off the ring.
+	 */
+	outb_p(E8390_TXOFF, e8390_base + EN0_TXCR);
+	outb_p(E8390_NODMA + E8390_PAGE0 + E8390_START, e8390_base + E8390_CMD);
+
+	/*
+	 * Clear the Rx ring of all the debris, and ack the interrupt.
+	 */
+	ei_receive(dev);
+
+	/*
+	 * Leave loopback mode, and resend any packet that got stopped.
+	 */
+	outb_p(E8390_TXCONFIG | info->duplex_flag, e8390_base + EN0_TXCR); 
+	if (must_resend)
+    		outb_p(E8390_NODMA + E8390_PAGE0 + E8390_START + E8390_TRANS, e8390_base + E8390_CMD);
+}
+
+/*
+ *	Collect the stats. This is called unlocked and from several contexts.
+ */
+ 
+static struct net_device_stats *get_stats(struct net_device *dev)
+{
+	long ioaddr = dev->base_addr;
+	struct ei_device *ei_local = netdev_priv(dev);
+	unsigned long flags;
+    
+	/* If the card is stopped, just return the present stats. */
+	if (!netif_running(dev))
+		return &dev->stats;
+
+	spin_lock_irqsave(&ei_local->page_lock,flags);
+	/* Read the counter registers, assuming we are in page 0. */
+	dev->stats.rx_frame_errors += inb_p(ioaddr + EN0_COUNTER0);
+	dev->stats.rx_crc_errors   += inb_p(ioaddr + EN0_COUNTER1);
+	dev->stats.rx_missed_errors+= inb_p(ioaddr + EN0_COUNTER2);
+	spin_unlock_irqrestore(&ei_local->page_lock, flags);
+    
+	return &dev->stats;
+}
+
+/*
+ * Form the 64 bit 8390 multicast table from the linked list of addresses
+ * associated with this dev structure.
+ */
+ 
+static inline void make_mc_bits(u8 *bits, struct net_device *dev)
+{
+	struct netdev_hw_addr *ha;
+	u32 crc;
+
+	netdev_for_each_mc_addr(ha, dev) {
+		crc = ether_crc(ETH_ALEN, ha->addr);
+		/* 
+		 * The 8390 uses the 6 most significant bits of the
+		 * CRC to index the multicast table.
+		 */
+		bits[crc>>29] |= (1<<((crc>>26)&7));
+	}
+}
+
+/**
+ * do_set_multicast_list - set/clear multicast filter
+ * @dev: net device for which multicast filter is adjusted
+ *
+ *	Set or clear the multicast filter for this adaptor.
+ *	Must be called with lock held. 
+ */
+ 
+static void do_set_multicast_list(struct net_device *dev)
+{
+	long e8390_base = dev->base_addr;
+	int i;
+	struct ei_device *ei_local = netdev_priv(dev);
+
+	if (!(dev->flags&(IFF_PROMISC|IFF_ALLMULTI))) {
+		memset(ei_local->mcfilter, 0, 8);
+		if (!netdev_mc_empty(dev))
+			make_mc_bits(ei_local->mcfilter, dev);
+	} else {
+		/* set to accept-all */
+		memset(ei_local->mcfilter, 0xFF, 8);
+	}
+
+	outb_p(E8390_NODMA + E8390_PAGE1, e8390_base + E8390_CMD);
+	for(i = 0; i < 8; i++) 
+	{
+		outb_p(ei_local->mcfilter[i], e8390_base + EN1_MULT_SHIFT(i));
+	}
+	outb_p(E8390_NODMA + E8390_PAGE0, e8390_base + E8390_CMD);
+
+  	if(dev->flags&IFF_PROMISC)
+  		outb_p(E8390_RXCONFIG | 0x58, e8390_base + EN0_RXCR);
+	else if (dev->flags & IFF_ALLMULTI || !netdev_mc_empty(dev))
+  		outb_p(E8390_RXCONFIG | 0x48, e8390_base + EN0_RXCR);
+  	else
+  		outb_p(E8390_RXCONFIG | 0x40, e8390_base + EN0_RXCR);
+
+	outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, e8390_base+E8390_CMD);
+}
+
+/*
+ *	Called without lock held. This is invoked from user context and may
+ *	be parallel to just about everything else. Its also fairly quick and
+ *	not called too often. Must protect against both bh and irq users
+ */
+
+static void set_multicast_list(struct net_device *dev)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev_lock(dev), flags);
+	do_set_multicast_list(dev);
+	spin_unlock_irqrestore(&dev_lock(dev), flags);
+}	
+
+/* This page of functions should be 8390 generic */
+/* Follow National Semi's recommendations for initializing the "NIC". */
+
+/**
+ * AX88190_init - initialize 8390 hardware
+ * @dev: network device to initialize
+ * @startp: boolean.  non-zero value to initiate chip processing
+ *
+ *	Must be called with lock held.
+ */
+
+static void AX88190_init(struct net_device *dev, int startp)
+{
+	axnet_dev_t *info = PRIV(dev);
+	long e8390_base = dev->base_addr;
+	struct ei_device *ei_local = netdev_priv(dev);
+	int i;
+	int endcfg = ei_local->word16 ? (0x48 | ENDCFG_WTS) : 0x48;
+    
+	if(sizeof(struct e8390_pkt_hdr)!=4)
+    		panic("8390.c: header struct mispacked\n");    
+	/* Follow National Semi's recommendations for initing the DP83902. */
+	outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base+E8390_CMD); /* 0x21 */
+	outb_p(endcfg, e8390_base + EN0_DCFG);	/* 0x48 or 0x49 */
+	/* Clear the remote byte count registers. */
+	outb_p(0x00,  e8390_base + EN0_RCNTLO);
+	outb_p(0x00,  e8390_base + EN0_RCNTHI);
+	/* Set to monitor and loopback mode -- this is vital!. */
+	outb_p(E8390_RXOFF|0x40, e8390_base + EN0_RXCR); /* 0x60 */
+	outb_p(E8390_TXOFF, e8390_base + EN0_TXCR); /* 0x02 */
+	/* Set the transmit page and receive ring. */
+	outb_p(ei_local->tx_start_page, e8390_base + EN0_TPSR);
+	ei_local->tx1 = ei_local->tx2 = 0;
+	outb_p(ei_local->rx_start_page, e8390_base + EN0_STARTPG);
+	outb_p(ei_local->stop_page-1, e8390_base + EN0_BOUNDARY);	/* 3c503 says 0x3f,NS0x26*/
+	ei_local->current_page = ei_local->rx_start_page;		/* assert boundary+1 */
+	outb_p(ei_local->stop_page, e8390_base + EN0_STOPPG);
+	/* Clear the pending interrupts and mask. */
+	outb_p(0xFF, e8390_base + EN0_ISR);
+	outb_p(0x00,  e8390_base + EN0_IMR);
+    
+	/* Copy the station address into the DS8390 registers. */
+
+	outb_p(E8390_NODMA + E8390_PAGE1 + E8390_STOP, e8390_base+E8390_CMD); /* 0x61 */
+	for(i = 0; i < 6; i++) 
+	{
+		outb_p(dev->dev_addr[i], e8390_base + EN1_PHYS_SHIFT(i));
+		if(inb_p(e8390_base + EN1_PHYS_SHIFT(i))!=dev->dev_addr[i])
+			netdev_err(dev, "Hw. address read/write mismap %d\n", i);
+	}
+
+	outb_p(ei_local->rx_start_page, e8390_base + EN1_CURPAG);
+	outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base+E8390_CMD);
+
+	netif_start_queue(dev);
+	ei_local->tx1 = ei_local->tx2 = 0;
+	ei_local->txing = 0;
+
+	if (info->flags & IS_AX88790)	/* select Internal PHY */
+		outb(0x10, e8390_base + AXNET_GPIO);
+
+	if (startp) 
+	{
+		outb_p(0xff,  e8390_base + EN0_ISR);
+		outb_p(ENISR_ALL,  e8390_base + EN0_IMR);
+		outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, e8390_base+E8390_CMD);
+		outb_p(E8390_TXCONFIG | info->duplex_flag,
+		       e8390_base + EN0_TXCR); /* xmit on. */
+		/* 3c503 TechMan says rxconfig only after the NIC is started. */
+		outb_p(E8390_RXCONFIG | 0x40, e8390_base + EN0_RXCR); /* rx on, */
+		do_set_multicast_list(dev);	/* (re)load the mcast table */
+	}
+}
+
+/* Trigger a transmit start, assuming the length is valid. 
+   Always called with the page lock held */
+   
+static void NS8390_trigger_send(struct net_device *dev, unsigned int length,
+								int start_page)
+{
+	long e8390_base = dev->base_addr;
+ 	struct ei_device *ei_local __attribute((unused)) = netdev_priv(dev);
+    
+	if (inb_p(e8390_base) & E8390_TRANS) 
+	{
+		netdev_warn(dev, "trigger_send() called with the transmitter busy\n");
+		return;
+	}
+	outb_p(length & 0xff, e8390_base + EN0_TCNTLO);
+	outb_p(length >> 8, e8390_base + EN0_TCNTHI);
+	outb_p(start_page, e8390_base + EN0_TPSR);
+	outb_p(E8390_NODMA+E8390_TRANS+E8390_START, e8390_base+E8390_CMD);
+}
diff --git a/drivers/net/ethernet/8390/e2100.c b/drivers/net/ethernet/8390/e2100.c
new file mode 100644
index 0000000..d50a999
--- /dev/null
+++ b/drivers/net/ethernet/8390/e2100.c
@@ -0,0 +1,490 @@
+/* e2100.c: A Cabletron E2100 series ethernet driver for linux. */
+/*
+	Written 1993-1994 by Donald Becker.
+
+	Copyright 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.
+
+	This is a driver for the Cabletron E2100 series ethercards.
+
+	The Author may be reached as becker@scyld.com, or C/O
+	Scyld Computing Corporation
+	410 Severn Ave., Suite 210
+	Annapolis MD 21403
+
+	The E2100 series ethercard is a fairly generic shared memory 8390
+	implementation.  The only unusual aspect is the way the shared memory
+	registers are set: first you do an inb() in what is normally the
+	station address region, and the low three bits of next outb() *address*
+	is used	as the write value for that register.  Either someone wasn't
+	too used to dem bit en bites, or they were trying to obfuscate the
+	programming interface.
+
+	There is an additional complication when setting the window on the packet
+	buffer.  You must first do a read into the packet buffer region with the
+	low 8 address bits the address setting the page for the start of the packet
+	buffer window, and then do the above operation.  See mem_on() for details.
+
+	One bug on the chip is that even a hard reset won't disable the memory
+	window, usually resulting in a hung machine if mem_off() isn't called.
+	If this happens, you must power down the machine for about 30 seconds.
+*/
+
+static const char version[] =
+	"e2100.c:v1.01 7/21/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+
+#include <asm/io.h>
+#include <asm/system.h>
+
+#include "8390.h"
+
+#define DRV_NAME "e2100"
+
+static int e21_probe_list[] = {0x300, 0x280, 0x380, 0x220, 0};
+
+/* Offsets from the base_addr.
+   Read from the ASIC register, and the low three bits of the next outb()
+   address is used to set the corresponding register. */
+#define E21_NIC_OFFSET  0		/* Offset to the 8390 NIC. */
+#define E21_ASIC		0x10
+#define E21_MEM_ENABLE	0x10
+#define  E21_MEM_ON		0x05	/* Enable memory in 16 bit mode. */
+#define  E21_MEM_ON_8	0x07	/* Enable memory in  8 bit mode. */
+#define E21_MEM_BASE	0x11
+#define E21_IRQ_LOW		0x12	/* The low three bits of the IRQ number. */
+#define E21_IRQ_HIGH	0x14	/* The high IRQ bit and media select ...  */
+#define E21_MEDIA		0x14	/* (alias). */
+#define  E21_ALT_IFPORT 0x02	/* Set to use the other (BNC,AUI) port. */
+#define  E21_BIG_MEM	0x04	/* Use a bigger (64K) buffer (we don't) */
+#define E21_SAPROM		0x10	/* Offset to station address data. */
+#define E21_IO_EXTENT	 0x20
+
+static inline void mem_on(short port, volatile char __iomem *mem_base,
+						  unsigned char start_page )
+{
+	/* This is a little weird: set the shared memory window by doing a
+	   read.  The low address bits specify the starting page. */
+	readb(mem_base+start_page);
+	inb(port + E21_MEM_ENABLE);
+	outb(E21_MEM_ON, port + E21_MEM_ENABLE + E21_MEM_ON);
+}
+
+static inline void mem_off(short port)
+{
+	inb(port + E21_MEM_ENABLE);
+	outb(0x00, port + E21_MEM_ENABLE);
+}
+
+/* In other drivers I put the TX pages first, but the E2100 window circuitry
+   is designed to have a 4K Tx region last. The windowing circuitry wraps the
+   window at 0x2fff->0x0000 so that the packets at e.g. 0x2f00 in the RX ring
+   appear contiguously in the window. */
+#define E21_RX_START_PG		0x00	/* First page of RX buffer */
+#define E21_RX_STOP_PG		0x30	/* Last page +1 of RX ring */
+#define E21_BIG_RX_STOP_PG	0xF0	/* Last page +1 of RX ring */
+#define E21_TX_START_PG		E21_RX_STOP_PG	/* First page of TX buffer */
+
+static int e21_probe1(struct net_device *dev, int ioaddr);
+
+static int e21_open(struct net_device *dev);
+static void e21_reset_8390(struct net_device *dev);
+static void e21_block_input(struct net_device *dev, int count,
+						   struct sk_buff *skb, int ring_offset);
+static void e21_block_output(struct net_device *dev, int count,
+							 const unsigned char *buf, int start_page);
+static void e21_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
+							int ring_page);
+static int e21_open(struct net_device *dev);
+static int e21_close(struct net_device *dev);
+
+
+/*  Probe for the E2100 series ethercards.  These cards have an 8390 at the
+	base address and the station address at both offset 0x10 and 0x18.  I read
+	the station address from offset 0x18 to avoid the dataport of NE2000
+	ethercards, and look for Ctron's unique ID (first three octets of the
+	station address).
+ */
+
+static int  __init do_e2100_probe(struct net_device *dev)
+{
+	int *port;
+	int base_addr = dev->base_addr;
+	int irq = dev->irq;
+
+	if (base_addr > 0x1ff)		/* Check a single specified location. */
+		return e21_probe1(dev, base_addr);
+	else if (base_addr != 0)	/* Don't probe at all. */
+		return -ENXIO;
+
+	for (port = e21_probe_list; *port; port++) {
+		dev->irq = irq;
+		if (e21_probe1(dev, *port) == 0)
+			return 0;
+	}
+
+	return -ENODEV;
+}
+
+#ifndef MODULE
+struct net_device * __init e2100_probe(int unit)
+{
+	struct net_device *dev = alloc_ei_netdev();
+	int err;
+
+	if (!dev)
+		return ERR_PTR(-ENOMEM);
+
+	sprintf(dev->name, "eth%d", unit);
+	netdev_boot_setup_check(dev);
+
+	err = do_e2100_probe(dev);
+	if (err)
+		goto out;
+	return dev;
+out:
+	free_netdev(dev);
+	return ERR_PTR(err);
+}
+#endif
+
+static const struct net_device_ops e21_netdev_ops = {
+	.ndo_open		= e21_open,
+	.ndo_stop		= e21_close,
+
+	.ndo_start_xmit		= ei_start_xmit,
+	.ndo_tx_timeout		= ei_tx_timeout,
+	.ndo_get_stats		= ei_get_stats,
+	.ndo_set_multicast_list = ei_set_multicast_list,
+	.ndo_validate_addr	= eth_validate_addr,
+	.ndo_set_mac_address 	= eth_mac_addr,
+	.ndo_change_mtu		= eth_change_mtu,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	.ndo_poll_controller 	= ei_poll,
+#endif
+};
+
+static int __init e21_probe1(struct net_device *dev, int ioaddr)
+{
+	int i, status, retval;
+	unsigned char *station_addr = dev->dev_addr;
+	static unsigned version_printed;
+
+	if (!request_region(ioaddr, E21_IO_EXTENT, DRV_NAME))
+		return -EBUSY;
+
+	/* First check the station address for the Ctron prefix. */
+	if (inb(ioaddr + E21_SAPROM + 0) != 0x00 ||
+	    inb(ioaddr + E21_SAPROM + 1) != 0x00 ||
+	    inb(ioaddr + E21_SAPROM + 2) != 0x1d) {
+		retval = -ENODEV;
+		goto out;
+	}
+
+	/* Verify by making certain that there is a 8390 at there. */
+	outb(E8390_NODMA + E8390_STOP, ioaddr);
+	udelay(1);	/* we want to delay one I/O cycle - which is 2MHz */
+	status = inb(ioaddr);
+	if (status != 0x21 && status != 0x23) {
+		retval = -ENODEV;
+		goto out;
+	}
+
+	/* Read the station address PROM.  */
+	for (i = 0; i < 6; i++)
+		station_addr[i] = inb(ioaddr + E21_SAPROM + i);
+
+	inb(ioaddr + E21_MEDIA); 		/* Point to media selection. */
+	outb(0, ioaddr + E21_ASIC); 	/* and disable the secondary interface. */
+
+	if (ei_debug  &&  version_printed++ == 0)
+		printk(version);
+
+	for (i = 0; i < 6; i++)
+		printk(" %02X", station_addr[i]);
+
+	if (dev->irq < 2) {
+		static const int irqlist[] = {15, 11, 10, 12, 5, 9, 3, 4};
+		for (i = 0; i < ARRAY_SIZE(irqlist); i++)
+			if (request_irq (irqlist[i], NULL, 0, "bogus", NULL) != -EBUSY) {
+				dev->irq = irqlist[i];
+				break;
+			}
+		if (i >= ARRAY_SIZE(irqlist)) {
+			printk(" unable to get IRQ %d.\n", dev->irq);
+			retval = -EAGAIN;
+			goto out;
+		}
+	} else if (dev->irq == 2)	/* Fixup luser bogosity: IRQ2 is really IRQ9 */
+		dev->irq = 9;
+
+	/* The 8390 is at the base address. */
+	dev->base_addr = ioaddr;
+
+	ei_status.name = "E2100";
+	ei_status.word16 = 1;
+	ei_status.tx_start_page = E21_TX_START_PG;
+	ei_status.rx_start_page = E21_RX_START_PG;
+	ei_status.stop_page = E21_RX_STOP_PG;
+	ei_status.saved_irq = dev->irq;
+
+	/* Check the media port used.  The port can be passed in on the
+	   low mem_end bits. */
+	if (dev->mem_end & 15)
+		dev->if_port = dev->mem_end & 7;
+	else {
+		dev->if_port = 0;
+		inb(ioaddr + E21_MEDIA); 	/* Turn automatic media detection on. */
+		for(i = 0; i < 6; i++)
+			if (station_addr[i] != inb(ioaddr + E21_SAPROM + 8 + i)) {
+				dev->if_port = 1;
+				break;
+			}
+	}
+
+	/* Never map in the E21 shared memory unless you are actively using it.
+	   Also, the shared memory has effective only one setting -- spread all
+	   over the 128K region! */
+	if (dev->mem_start == 0)
+		dev->mem_start = 0xd0000;
+
+	ei_status.mem = ioremap(dev->mem_start, 2*1024);
+	if (!ei_status.mem) {
+		printk("unable to remap memory\n");
+		retval = -EAGAIN;
+		goto out;
+	}
+
+#ifdef notdef
+	/* These values are unused.  The E2100 has a 2K window into the packet
+	   buffer.  The window can be set to start on any page boundary. */
+	ei_status.rmem_start = dev->mem_start + TX_PAGES*256;
+	dev->mem_end = ei_status.rmem_end = dev->mem_start + 2*1024;
+#endif
+
+	printk(", IRQ %d, %s media, memory @ %#lx.\n", dev->irq,
+		   dev->if_port ? "secondary" : "primary", dev->mem_start);
+
+	ei_status.reset_8390 = &e21_reset_8390;
+	ei_status.block_input = &e21_block_input;
+	ei_status.block_output = &e21_block_output;
+	ei_status.get_8390_hdr = &e21_get_8390_hdr;
+
+	dev->netdev_ops = &e21_netdev_ops;
+	NS8390_init(dev, 0);
+
+	retval = register_netdev(dev);
+	if (retval)
+		goto out;
+	return 0;
+out:
+	release_region(ioaddr, E21_IO_EXTENT);
+	return retval;
+}
+
+static int
+e21_open(struct net_device *dev)
+{
+	short ioaddr = dev->base_addr;
+	int retval;
+
+	if ((retval = request_irq(dev->irq, ei_interrupt, 0, dev->name, dev)))
+		return retval;
+
+	/* Set the interrupt line and memory base on the hardware. */
+	inb(ioaddr + E21_IRQ_LOW);
+	outb(0, ioaddr + E21_ASIC + (dev->irq & 7));
+	inb(ioaddr + E21_IRQ_HIGH); 			/* High IRQ bit, and if_port. */
+	outb(0, ioaddr + E21_ASIC + (dev->irq > 7 ? 1:0)
+		   + (dev->if_port ? E21_ALT_IFPORT : 0));
+	inb(ioaddr + E21_MEM_BASE);
+	outb(0, ioaddr + E21_ASIC + ((dev->mem_start >> 17) & 7));
+
+	ei_open(dev);
+	return 0;
+}
+
+static void
+e21_reset_8390(struct net_device *dev)
+{
+	short ioaddr = dev->base_addr;
+
+	outb(0x01, ioaddr);
+	if (ei_debug > 1) printk("resetting the E2180x3 t=%ld...", jiffies);
+	ei_status.txing = 0;
+
+	/* Set up the ASIC registers, just in case something changed them. */
+
+	if (ei_debug > 1) printk("reset done\n");
+}
+
+/* Grab the 8390 specific header. We put the 2k window so the header page
+   appears at the start of the shared memory. */
+
+static void
+e21_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
+{
+
+	short ioaddr = dev->base_addr;
+	char __iomem *shared_mem = ei_status.mem;
+
+	mem_on(ioaddr, shared_mem, ring_page);
+
+#ifdef notdef
+	/* Officially this is what we are doing, but the readl() is faster */
+	memcpy_fromio(hdr, shared_mem, sizeof(struct e8390_pkt_hdr));
+#else
+	((unsigned int*)hdr)[0] = readl(shared_mem);
+#endif
+
+	/* Turn off memory access: we would need to reprogram the window anyway. */
+	mem_off(ioaddr);
+
+}
+
+/*  Block input and output are easy on shared memory ethercards.
+	The E21xx makes block_input() especially easy by wrapping the top
+	ring buffer to the bottom automatically. */
+static void
+e21_block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset)
+{
+	short ioaddr = dev->base_addr;
+	char __iomem *shared_mem = ei_status.mem;
+
+	mem_on(ioaddr, shared_mem, (ring_offset>>8));
+
+	memcpy_fromio(skb->data, ei_status.mem + (ring_offset & 0xff), count);
+
+	mem_off(ioaddr);
+}
+
+static void
+e21_block_output(struct net_device *dev, int count, const unsigned char *buf,
+				 int start_page)
+{
+	short ioaddr = dev->base_addr;
+	volatile char __iomem *shared_mem = ei_status.mem;
+
+	/* Set the shared memory window start by doing a read, with the low address
+	   bits specifying the starting page. */
+	readb(shared_mem + start_page);
+	mem_on(ioaddr, shared_mem, start_page);
+
+	memcpy_toio(shared_mem, buf, count);
+	mem_off(ioaddr);
+}
+
+static int
+e21_close(struct net_device *dev)
+{
+	short ioaddr = dev->base_addr;
+
+	if (ei_debug > 1)
+		printk("%s: Shutting down ethercard.\n", dev->name);
+
+	free_irq(dev->irq, dev);
+	dev->irq = ei_status.saved_irq;
+
+	/* Shut off the interrupt line and secondary interface. */
+	inb(ioaddr + E21_IRQ_LOW);
+	outb(0, ioaddr + E21_ASIC);
+	inb(ioaddr + E21_IRQ_HIGH); 			/* High IRQ bit, and if_port. */
+	outb(0, ioaddr + E21_ASIC);
+
+	ei_close(dev);
+
+	/* Double-check that the memory has been turned off, because really
+	   really bad things happen if it isn't. */
+	mem_off(ioaddr);
+
+	return 0;
+}
+
+
+#ifdef MODULE
+#define MAX_E21_CARDS	4	/* Max number of E21 cards per module */
+static struct net_device *dev_e21[MAX_E21_CARDS];
+static int io[MAX_E21_CARDS];
+static int irq[MAX_E21_CARDS];
+static int mem[MAX_E21_CARDS];
+static int xcvr[MAX_E21_CARDS];		/* choose int. or ext. xcvr */
+
+module_param_array(io, int, NULL, 0);
+module_param_array(irq, int, NULL, 0);
+module_param_array(mem, int, NULL, 0);
+module_param_array(xcvr, int, NULL, 0);
+MODULE_PARM_DESC(io, "I/O base address(es)");
+MODULE_PARM_DESC(irq, "IRQ number(s)");
+MODULE_PARM_DESC(mem, " memory base address(es)");
+MODULE_PARM_DESC(xcvr, "transceiver(s) (0=internal, 1=external)");
+MODULE_DESCRIPTION("Cabletron E2100 ISA ethernet driver");
+MODULE_LICENSE("GPL");
+
+/* This is set up so that only a single autoprobe takes place per call.
+ISA device autoprobes on a running machine are not recommended. */
+
+int __init init_module(void)
+{
+	struct net_device *dev;
+	int this_dev, found = 0;
+
+	for (this_dev = 0; this_dev < MAX_E21_CARDS; this_dev++) {
+		if (io[this_dev] == 0)  {
+			if (this_dev != 0) break; /* only autoprobe 1st one */
+			printk(KERN_NOTICE "e2100.c: Presently autoprobing (not recommended) for a single card.\n");
+		}
+		dev = alloc_ei_netdev();
+		if (!dev)
+			break;
+		dev->irq = irq[this_dev];
+		dev->base_addr = io[this_dev];
+		dev->mem_start = mem[this_dev];
+		dev->mem_end = xcvr[this_dev];	/* low 4bits = xcvr sel. */
+		if (do_e2100_probe(dev) == 0) {
+			dev_e21[found++] = dev;
+			continue;
+		}
+		free_netdev(dev);
+		printk(KERN_WARNING "e2100.c: No E2100 card found (i/o = 0x%x).\n", io[this_dev]);
+		break;
+	}
+	if (found)
+		return 0;
+	return -ENXIO;
+}
+
+static void cleanup_card(struct net_device *dev)
+{
+	/* NB: e21_close() handles free_irq */
+	iounmap(ei_status.mem);
+	release_region(dev->base_addr, E21_IO_EXTENT);
+}
+
+void __exit
+cleanup_module(void)
+{
+	int this_dev;
+
+	for (this_dev = 0; this_dev < MAX_E21_CARDS; this_dev++) {
+		struct net_device *dev = dev_e21[this_dev];
+		if (dev) {
+			unregister_netdev(dev);
+			cleanup_card(dev);
+			free_netdev(dev);
+		}
+	}
+}
+#endif /* MODULE */
diff --git a/drivers/net/ethernet/8390/es3210.c b/drivers/net/ethernet/8390/es3210.c
new file mode 100644
index 0000000..7a09575
--- /dev/null
+++ b/drivers/net/ethernet/8390/es3210.c
@@ -0,0 +1,446 @@
+/*
+	es3210.c
+
+	Linux driver for Racal-Interlan ES3210 EISA Network Adapter
+
+	Copyright (C) 1996, Paul Gortmaker.
+
+	This software may be used and distributed according to the terms
+	of the GNU General Public License, incorporated herein by reference.
+
+	Information and Code Sources:
+
+	1) The existing myriad of Linux 8390 drivers written by Donald Becker.
+
+	2) Once again Russ Nelson's asm packet driver provided additional info.
+
+	3) Info for getting IRQ and sh-mem gleaned from the EISA cfg files.
+	   Too bad it doesn't work -- see below.
+
+	The ES3210 is an EISA shared memory NS8390 implementation. Note
+	that all memory copies to/from the board must be 32bit transfers.
+	Which rules out using eth_io_copy_and_sum() in this driver.
+
+	Apparently there are two slightly different revisions of the
+	card, since there are two distinct EISA cfg files (!rii0101.cfg
+	and !rii0102.cfg) One has media select in the cfg file and the
+	other doesn't. Hopefully this will work with either.
+
+	That is about all I can tell you about it, having never actually
+	even seen one of these cards. :)  Try http://www.interlan.com
+	if you want more info.
+
+	Thanks go to Mark Salazar for testing v0.02 of this driver.
+
+	Bugs, to-fix, etc:
+
+	1) The EISA cfg ports that are *supposed* to have the IRQ and shared
+	   mem values just read 0xff all the time. Hrrmpf. Apparently the
+	   same happens with the packet driver as the code for reading
+	   these registers is disabled there. In the meantime, boot with:
+	   ether=<IRQ>,0,0x<shared_mem_addr>,eth0 to override the IRQ and
+	   shared memory detection. (The i/o port detection is okay.)
+
+	2) Module support currently untested. Probably works though.
+
+*/
+
+static const char version[] =
+	"es3210.c: Driver revision v0.03, 14/09/96\n";
+
+#include <linux/module.h>
+#include <linux/eisa.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+
+#include <asm/io.h>
+#include <asm/system.h>
+
+#include "8390.h"
+
+static int es_probe1(struct net_device *dev, int ioaddr);
+
+static void es_reset_8390(struct net_device *dev);
+
+static void es_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page);
+static void es_block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset);
+static void es_block_output(struct net_device *dev, int count, const unsigned char *buf, int start_page);
+
+#define ES_START_PG	0x00    /* First page of TX buffer		*/
+#define ES_STOP_PG	0x40    /* Last page +1 of RX ring		*/
+
+#define ES_IO_EXTENT	0x37	/* The cfg file says 0xc90 -> 0xcc7	*/
+#define ES_ID_PORT	0xc80	/* Same for all EISA cards 		*/
+#define ES_SA_PROM	0xc90	/* Start of e'net addr.			*/
+#define ES_RESET_PORT	0xc84	/* From the packet driver source	*/
+#define ES_NIC_OFFSET	0xca0	/* Hello, the 8390 is *here*		*/
+
+#define ES_ADDR0	0x02	/* 3 byte vendor prefix			*/
+#define ES_ADDR1	0x07
+#define ES_ADDR2	0x01
+
+/*
+ * Two card revisions. EISA ID's are always rev. minor, rev. major,, and
+ * then the three vendor letters stored in 5 bits each, with an "a" = 1.
+ * For eg: "rii" = 10010 01001 01001 = 0x4929, which is how the EISA
+ * config utility determines automagically what config file(s) to use.
+ */
+#define ES_EISA_ID1	0x01012949	/* !rii0101.cfg 		*/
+#define ES_EISA_ID2	0x02012949	/* !rii0102.cfg 		*/
+
+#define ES_CFG1		0xcc0	/* IOPORT(1) --> IOPORT(6) in cfg file	*/
+#define ES_CFG2		0xcc1
+#define ES_CFG3		0xcc2
+#define ES_CFG4		0xcc3
+#define ES_CFG5		0xcc4
+#define ES_CFG6		0xc84	/* NB: 0xc84 is also "reset" port.	*/
+
+/*
+ *	You can OR any of the following bits together and assign it
+ *	to ES_DEBUG to get verbose driver info during operation.
+ *	Some of these don't do anything yet.
+ */
+
+#define ES_D_PROBE	0x01
+#define ES_D_RX_PKT	0x02
+#define ES_D_TX_PKT	0x04
+#define ED_D_IRQ	0x08
+
+#define ES_DEBUG	0
+
+static unsigned char lo_irq_map[] __initdata = {3, 4, 5, 6, 7, 9, 10};
+static unsigned char hi_irq_map[] __initdata = {11, 12, 0, 14, 0, 0, 0, 15};
+
+/*
+ *	Probe for the card. The best way is to read the EISA ID if it
+ *	is known. Then we check the prefix of the station address
+ *	PROM for a match against the Racal-Interlan assigned value.
+ */
+
+static int __init do_es_probe(struct net_device *dev)
+{
+	unsigned short ioaddr = dev->base_addr;
+	int irq = dev->irq;
+	int mem_start = dev->mem_start;
+
+	if (ioaddr > 0x1ff)		/* Check a single specified location. */
+		return es_probe1(dev, ioaddr);
+	else if (ioaddr > 0)		/* Don't probe at all. */
+		return -ENXIO;
+
+	if (!EISA_bus) {
+#if ES_DEBUG & ES_D_PROBE
+		printk("es3210.c: Not EISA bus. Not probing high ports.\n");
+#endif
+		return -ENXIO;
+	}
+
+	/* EISA spec allows for up to 16 slots, but 8 is typical. */
+	for (ioaddr = 0x1000; ioaddr < 0x9000; ioaddr += 0x1000) {
+		if (es_probe1(dev, ioaddr) == 0)
+			return 0;
+		dev->irq = irq;
+		dev->mem_start = mem_start;
+	}
+
+	return -ENODEV;
+}
+
+#ifndef MODULE
+struct net_device * __init es_probe(int unit)
+{
+	struct net_device *dev = alloc_ei_netdev();
+	int err;
+
+	if (!dev)
+		return ERR_PTR(-ENOMEM);
+
+	sprintf(dev->name, "eth%d", unit);
+	netdev_boot_setup_check(dev);
+
+	err = do_es_probe(dev);
+	if (err)
+		goto out;
+	return dev;
+out:
+	free_netdev(dev);
+	return ERR_PTR(err);
+}
+#endif
+
+static int __init es_probe1(struct net_device *dev, int ioaddr)
+{
+	int i, retval;
+	unsigned long eisa_id;
+
+	if (!request_region(ioaddr + ES_SA_PROM, ES_IO_EXTENT, "es3210"))
+		return -ENODEV;
+
+#if ES_DEBUG & ES_D_PROBE
+	printk("es3210.c: probe at %#x, ID %#8x\n", ioaddr, inl(ioaddr + ES_ID_PORT));
+	printk("es3210.c: config regs: %#x %#x %#x %#x %#x %#x\n",
+		inb(ioaddr + ES_CFG1), inb(ioaddr + ES_CFG2), inb(ioaddr + ES_CFG3),
+		inb(ioaddr + ES_CFG4), inb(ioaddr + ES_CFG5), inb(ioaddr + ES_CFG6));
+#endif
+
+/*	Check the EISA ID of the card. */
+	eisa_id = inl(ioaddr + ES_ID_PORT);
+	if ((eisa_id != ES_EISA_ID1) && (eisa_id != ES_EISA_ID2)) {
+		retval = -ENODEV;
+		goto out;
+	}
+
+	for (i = 0; i < ETHER_ADDR_LEN ; i++)
+		dev->dev_addr[i] = inb(ioaddr + ES_SA_PROM + i);
+
+/*	Check the Racal vendor ID as well. */
+	if (dev->dev_addr[0] != ES_ADDR0 ||
+	    dev->dev_addr[1] != ES_ADDR1 ||
+	    dev->dev_addr[2] != ES_ADDR2) {
+		printk("es3210.c: card not found %pM (invalid_prefix).\n",
+		       dev->dev_addr);
+		retval = -ENODEV;
+		goto out;
+	}
+
+	printk("es3210.c: ES3210 rev. %ld at %#x, node %pM",
+	       eisa_id>>24, ioaddr, dev->dev_addr);
+
+	/* Snarf the interrupt now. */
+	if (dev->irq == 0) {
+		unsigned char hi_irq = inb(ioaddr + ES_CFG2) & 0x07;
+		unsigned char lo_irq = inb(ioaddr + ES_CFG1) & 0xfe;
+
+		if (hi_irq != 0) {
+			dev->irq = hi_irq_map[hi_irq - 1];
+		} else {
+			int i = 0;
+			while (lo_irq > (1<<i)) i++;
+			dev->irq = lo_irq_map[i];
+		}
+		printk(" using IRQ %d", dev->irq);
+#if ES_DEBUG & ES_D_PROBE
+		printk("es3210.c: hi_irq %#x, lo_irq %#x, dev->irq = %d\n",
+					hi_irq, lo_irq, dev->irq);
+#endif
+	} else {
+		if (dev->irq == 2)
+			dev->irq = 9;			/* Doh! */
+		printk(" assigning IRQ %d", dev->irq);
+	}
+
+	if (request_irq(dev->irq, ei_interrupt, 0, "es3210", dev)) {
+		printk (" unable to get IRQ %d.\n", dev->irq);
+		retval = -EAGAIN;
+		goto out;
+	}
+
+	if (dev->mem_start == 0) {
+		unsigned char mem_enabled = inb(ioaddr + ES_CFG2) & 0xc0;
+		unsigned char mem_bits = inb(ioaddr + ES_CFG3) & 0x07;
+
+		if (mem_enabled != 0x80) {
+			printk(" shared mem disabled - giving up\n");
+			retval = -ENXIO;
+			goto out1;
+		}
+		dev->mem_start = 0xC0000 + mem_bits*0x4000;
+		printk(" using ");
+	} else {
+		printk(" assigning ");
+	}
+
+	ei_status.mem = ioremap(dev->mem_start, (ES_STOP_PG - ES_START_PG)*256);
+	if (!ei_status.mem) {
+		printk("ioremap failed - giving up\n");
+		retval = -ENXIO;
+		goto out1;
+	}
+
+	dev->mem_end = dev->mem_start + (ES_STOP_PG - ES_START_PG)*256;
+
+	printk("mem %#lx-%#lx\n", dev->mem_start, dev->mem_end-1);
+
+#if ES_DEBUG & ES_D_PROBE
+	if (inb(ioaddr + ES_CFG5))
+		printk("es3210: Warning - DMA channel enabled, but not used here.\n");
+#endif
+	/* Note, point at the 8390, and not the card... */
+	dev->base_addr = ioaddr + ES_NIC_OFFSET;
+
+	ei_status.name = "ES3210";
+	ei_status.tx_start_page = ES_START_PG;
+	ei_status.rx_start_page = ES_START_PG + TX_PAGES;
+	ei_status.stop_page = ES_STOP_PG;
+	ei_status.word16 = 1;
+
+	if (ei_debug > 0)
+		printk(version);
+
+	ei_status.reset_8390 = &es_reset_8390;
+	ei_status.block_input = &es_block_input;
+	ei_status.block_output = &es_block_output;
+	ei_status.get_8390_hdr = &es_get_8390_hdr;
+
+	dev->netdev_ops = &ei_netdev_ops;
+	NS8390_init(dev, 0);
+
+	retval = register_netdev(dev);
+	if (retval)
+		goto out1;
+	return 0;
+out1:
+	free_irq(dev->irq, dev);
+out:
+	release_region(ioaddr + ES_SA_PROM, ES_IO_EXTENT);
+	return retval;
+}
+
+/*
+ *	Reset as per the packet driver method. Judging by the EISA cfg
+ *	file, this just toggles the "Board Enable" bits (bit 2 and 0).
+ */
+
+static void es_reset_8390(struct net_device *dev)
+{
+	unsigned short ioaddr = dev->base_addr;
+	unsigned long end;
+
+	outb(0x04, ioaddr + ES_RESET_PORT);
+	if (ei_debug > 1) printk("%s: resetting the ES3210...", dev->name);
+
+	end = jiffies + 2*HZ/100;
+        while ((signed)(end - jiffies) > 0) continue;
+
+	ei_status.txing = 0;
+	outb(0x01, ioaddr + ES_RESET_PORT);
+	if (ei_debug > 1) printk("reset done\n");
+}
+
+/*
+ *	Note: In the following three functions is the implicit assumption
+ *	that the associated memcpy will only use "rep; movsl" as long as
+ *	we keep the counts as some multiple of doublewords. This is a
+ *	requirement of the hardware, and also prevents us from using
+ *	eth_io_copy_and_sum() since we can't guarantee it will limit
+ *	itself to doubleword access.
+ */
+
+/*
+ *	Grab the 8390 specific header. Similar to the block_input routine, but
+ *	we don't need to be concerned with ring wrap as the header will be at
+ *	the start of a page, so we optimize accordingly. (A single doubleword.)
+ */
+
+static void
+es_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
+{
+	void __iomem *hdr_start = ei_status.mem + ((ring_page - ES_START_PG)<<8);
+	memcpy_fromio(hdr, hdr_start, sizeof(struct e8390_pkt_hdr));
+	hdr->count = (hdr->count + 3) & ~3;     /* Round up allocation. */
+}
+
+/*
+ *	Block input and output are easy on shared memory ethercards, the only
+ *	complication is when the ring buffer wraps. The count will already
+ *	be rounded up to a doubleword value via es_get_8390_hdr() above.
+ */
+
+static void es_block_input(struct net_device *dev, int count, struct sk_buff *skb,
+						  int ring_offset)
+{
+	void __iomem *xfer_start = ei_status.mem + ring_offset - ES_START_PG*256;
+
+	if (ring_offset + count > ES_STOP_PG*256) {
+		/* Packet wraps over end of ring buffer. */
+		int semi_count = ES_STOP_PG*256 - ring_offset;
+		memcpy_fromio(skb->data, xfer_start, semi_count);
+		count -= semi_count;
+		memcpy_fromio(skb->data + semi_count, ei_status.mem, count);
+	} else {
+		/* Packet is in one chunk. */
+		memcpy_fromio(skb->data, xfer_start, count);
+	}
+}
+
+static void es_block_output(struct net_device *dev, int count,
+				const unsigned char *buf, int start_page)
+{
+	void __iomem *shmem = ei_status.mem + ((start_page - ES_START_PG)<<8);
+
+	count = (count + 3) & ~3;     /* Round up to doubleword */
+	memcpy_toio(shmem, buf, count);
+}
+
+#ifdef MODULE
+#define MAX_ES_CARDS	4	/* Max number of ES3210 cards per module */
+#define NAMELEN		8	/* # of chars for storing dev->name */
+static struct net_device *dev_es3210[MAX_ES_CARDS];
+static int io[MAX_ES_CARDS];
+static int irq[MAX_ES_CARDS];
+static int mem[MAX_ES_CARDS];
+
+module_param_array(io, int, NULL, 0);
+module_param_array(irq, int, NULL, 0);
+module_param_array(mem, int, NULL, 0);
+MODULE_PARM_DESC(io, "I/O base address(es)");
+MODULE_PARM_DESC(irq, "IRQ number(s)");
+MODULE_PARM_DESC(mem, "memory base address(es)");
+MODULE_DESCRIPTION("Racal-Interlan ES3210 EISA ethernet driver");
+MODULE_LICENSE("GPL");
+
+int __init init_module(void)
+{
+	struct net_device *dev;
+	int this_dev, found = 0;
+
+	for (this_dev = 0; this_dev < MAX_ES_CARDS; this_dev++) {
+		if (io[this_dev] == 0 && this_dev != 0)
+			break;
+		dev = alloc_ei_netdev();
+		if (!dev)
+			break;
+		dev->irq = irq[this_dev];
+		dev->base_addr = io[this_dev];
+		dev->mem_start = mem[this_dev];
+		if (do_es_probe(dev) == 0) {
+			dev_es3210[found++] = dev;
+			continue;
+		}
+		free_netdev(dev);
+		printk(KERN_WARNING "es3210.c: No es3210 card found (i/o = 0x%x).\n", io[this_dev]);
+		break;
+	}
+	if (found)
+		return 0;
+	return -ENXIO;
+}
+
+static void cleanup_card(struct net_device *dev)
+{
+	free_irq(dev->irq, dev);
+	release_region(dev->base_addr, ES_IO_EXTENT);
+	iounmap(ei_status.mem);
+}
+
+void __exit
+cleanup_module(void)
+{
+	int this_dev;
+
+	for (this_dev = 0; this_dev < MAX_ES_CARDS; this_dev++) {
+		struct net_device *dev = dev_es3210[this_dev];
+		if (dev) {
+			unregister_netdev(dev);
+			cleanup_card(dev);
+			free_netdev(dev);
+		}
+	}
+}
+#endif /* MODULE */
+
diff --git a/drivers/net/ethernet/8390/etherh.c b/drivers/net/ethernet/8390/etherh.c
new file mode 100644
index 0000000..cf851fa
--- /dev/null
+++ b/drivers/net/ethernet/8390/etherh.c
@@ -0,0 +1,866 @@
+/*
+ *  linux/drivers/acorn/net/etherh.c
+ *
+ *  Copyright (C) 2000-2002 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.
+ *
+ * NS8390 I-cubed EtherH and ANT EtherM specific driver
+ * Thanks to I-Cubed for information on their cards.
+ * EtherM conversion (C) 1999 Chris Kemp and Tim Watterton
+ * EtherM integration (C) 2000 Aleph One Ltd (Tak-Shing Chan)
+ * EtherM integration re-engineered by Russell King.
+ *
+ * Changelog:
+ *  08-12-1996	RMK	1.00	Created
+ *		RMK	1.03	Added support for EtherLan500 cards
+ *  23-11-1997	RMK	1.04	Added media autodetection
+ *  16-04-1998	RMK	1.05	Improved media autodetection
+ *  10-02-2000	RMK	1.06	Updated for 2.3.43
+ *  13-05-2000	RMK	1.07	Updated for 2.3.99-pre8
+ *  12-10-1999  CK/TEW		EtherM driver first release
+ *  21-12-2000	TTC		EtherH/EtherM integration
+ *  25-12-2000	RMK	1.08	Clean integration of EtherM into this driver.
+ *  03-01-2002	RMK	1.09	Always enable IRQs if we're in the nic slot.
+ */
+
+#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/errno.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/skbuff.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/bitops.h>
+#include <linux/jiffies.h>
+
+#include <asm/system.h>
+#include <asm/ecard.h>
+#include <asm/io.h>
+
+#define EI_SHIFT(x)	(ei_local->reg_offset[x])
+
+#define ei_inb(_p)	 readb((void __iomem *)_p)
+#define ei_outb(_v,_p)	 writeb(_v,(void __iomem *)_p)
+#define ei_inb_p(_p)	 readb((void __iomem *)_p)
+#define ei_outb_p(_v,_p) writeb(_v,(void __iomem *)_p)
+
+#define NET_DEBUG  0
+#define DEBUG_INIT 2
+
+#define DRV_NAME	"etherh"
+#define DRV_VERSION	"1.11"
+
+static char version[] __initdata =
+	"EtherH/EtherM Driver (c) 2002-2004 Russell King " DRV_VERSION "\n";
+
+#include "lib8390.c"
+
+static unsigned int net_debug = NET_DEBUG;
+
+struct etherh_priv {
+	void __iomem	*ioc_fast;
+	void __iomem	*memc;
+	void __iomem	*dma_base;
+	unsigned int	id;
+	void __iomem	*ctrl_port;
+	unsigned char	ctrl;
+	u32		supported;
+};
+
+struct etherh_data {
+	unsigned long	ns8390_offset;
+	unsigned long	dataport_offset;
+	unsigned long	ctrlport_offset;
+	int		ctrl_ioc;
+	const char	name[16];
+	u32		supported;
+	unsigned char	tx_start_page;
+	unsigned char	stop_page;
+};
+
+MODULE_AUTHOR("Russell King");
+MODULE_DESCRIPTION("EtherH/EtherM driver");
+MODULE_LICENSE("GPL");
+
+#define ETHERH500_DATAPORT	0x800	/* MEMC */
+#define ETHERH500_NS8390	0x000	/* MEMC */
+#define ETHERH500_CTRLPORT	0x800	/* IOC  */
+
+#define ETHERH600_DATAPORT	0x040	/* MEMC */
+#define ETHERH600_NS8390	0x800	/* MEMC */
+#define ETHERH600_CTRLPORT	0x200	/* MEMC */
+
+#define ETHERH_CP_IE		1
+#define ETHERH_CP_IF		2
+#define ETHERH_CP_HEARTBEAT	2
+
+#define ETHERH_TX_START_PAGE	1
+#define ETHERH_STOP_PAGE	127
+
+/*
+ * These came from CK/TEW
+ */
+#define ETHERM_DATAPORT		0x200	/* MEMC */
+#define ETHERM_NS8390		0x800	/* MEMC */
+#define ETHERM_CTRLPORT		0x23c	/* MEMC */
+
+#define ETHERM_TX_START_PAGE	64
+#define ETHERM_STOP_PAGE	127
+
+/* ------------------------------------------------------------------------ */
+
+#define etherh_priv(dev) \
+ ((struct etherh_priv *)(((char *)netdev_priv(dev)) + sizeof(struct ei_device)))
+
+static inline void etherh_set_ctrl(struct etherh_priv *eh, unsigned char mask)
+{
+	unsigned char ctrl = eh->ctrl | mask;
+	eh->ctrl = ctrl;
+	writeb(ctrl, eh->ctrl_port);
+}
+
+static inline void etherh_clr_ctrl(struct etherh_priv *eh, unsigned char mask)
+{
+	unsigned char ctrl = eh->ctrl & ~mask;
+	eh->ctrl = ctrl;
+	writeb(ctrl, eh->ctrl_port);
+}
+
+static inline unsigned int etherh_get_stat(struct etherh_priv *eh)
+{
+	return readb(eh->ctrl_port);
+}
+
+
+
+
+static void etherh_irq_enable(ecard_t *ec, int irqnr)
+{
+	struct etherh_priv *eh = ec->irq_data;
+
+	etherh_set_ctrl(eh, ETHERH_CP_IE);
+}
+
+static void etherh_irq_disable(ecard_t *ec, int irqnr)
+{
+	struct etherh_priv *eh = ec->irq_data;
+
+	etherh_clr_ctrl(eh, ETHERH_CP_IE);
+}
+
+static expansioncard_ops_t etherh_ops = {
+	.irqenable	= etherh_irq_enable,
+	.irqdisable	= etherh_irq_disable,
+};
+
+
+
+
+static void
+etherh_setif(struct net_device *dev)
+{
+	struct ei_device *ei_local = netdev_priv(dev);
+	unsigned long flags;
+	void __iomem *addr;
+
+	local_irq_save(flags);
+
+	/* set the interface type */
+	switch (etherh_priv(dev)->id) {
+	case PROD_I3_ETHERLAN600:
+	case PROD_I3_ETHERLAN600A:
+		addr = (void __iomem *)dev->base_addr + EN0_RCNTHI;
+
+		switch (dev->if_port) {
+		case IF_PORT_10BASE2:
+			writeb((readb(addr) & 0xf8) | 1, addr);
+			break;
+		case IF_PORT_10BASET:
+			writeb((readb(addr) & 0xf8), addr);
+			break;
+		}
+		break;
+
+	case PROD_I3_ETHERLAN500:
+		switch (dev->if_port) {
+		case IF_PORT_10BASE2:
+			etherh_clr_ctrl(etherh_priv(dev), ETHERH_CP_IF);
+			break;
+
+		case IF_PORT_10BASET:
+			etherh_set_ctrl(etherh_priv(dev), ETHERH_CP_IF);
+			break;
+		}
+		break;
+
+	default:
+		break;
+	}
+
+	local_irq_restore(flags);
+}
+
+static int
+etherh_getifstat(struct net_device *dev)
+{
+	struct ei_device *ei_local = netdev_priv(dev);
+	void __iomem *addr;
+	int stat = 0;
+
+	switch (etherh_priv(dev)->id) {
+	case PROD_I3_ETHERLAN600:
+	case PROD_I3_ETHERLAN600A:
+		addr = (void __iomem *)dev->base_addr + EN0_RCNTHI;
+		switch (dev->if_port) {
+		case IF_PORT_10BASE2:
+			stat = 1;
+			break;
+		case IF_PORT_10BASET:
+			stat = readb(addr) & 4;
+			break;
+		}
+		break;
+
+	case PROD_I3_ETHERLAN500:
+		switch (dev->if_port) {
+		case IF_PORT_10BASE2:
+			stat = 1;
+			break;
+		case IF_PORT_10BASET:
+			stat = etherh_get_stat(etherh_priv(dev)) & ETHERH_CP_HEARTBEAT;
+			break;
+		}
+		break;
+
+	default:
+		stat = 0;
+		break;
+	}
+
+	return stat != 0;
+}
+
+/*
+ * Configure the interface.  Note that we ignore the other
+ * parts of ifmap, since its mostly meaningless for this driver.
+ */
+static int etherh_set_config(struct net_device *dev, struct ifmap *map)
+{
+	switch (map->port) {
+	case IF_PORT_10BASE2:
+	case IF_PORT_10BASET:
+		/*
+		 * If the user explicitly sets the interface
+		 * media type, turn off automedia detection.
+		 */
+		dev->flags &= ~IFF_AUTOMEDIA;
+		dev->if_port = map->port;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	etherh_setif(dev);
+
+	return 0;
+}
+
+/*
+ * Reset the 8390 (hard reset).  Note that we can't actually do this.
+ */
+static void
+etherh_reset(struct net_device *dev)
+{
+	struct ei_device *ei_local = netdev_priv(dev);
+	void __iomem *addr = (void __iomem *)dev->base_addr;
+
+	writeb(E8390_NODMA+E8390_PAGE0+E8390_STOP, addr);
+
+	/*
+	 * See if we need to change the interface type.
+	 * Note that we use 'interface_num' as a flag
+	 * to indicate that we need to change the media.
+	 */
+	if (dev->flags & IFF_AUTOMEDIA && ei_local->interface_num) {
+		ei_local->interface_num = 0;
+
+		if (dev->if_port == IF_PORT_10BASET)
+			dev->if_port = IF_PORT_10BASE2;
+		else
+			dev->if_port = IF_PORT_10BASET;
+
+		etherh_setif(dev);
+	}
+}
+
+/*
+ * Write a block of data out to the 8390
+ */
+static void
+etherh_block_output (struct net_device *dev, int count, const unsigned char *buf, int start_page)
+{
+	struct ei_device *ei_local = netdev_priv(dev);
+	unsigned long dma_start;
+	void __iomem *dma_base, *addr;
+
+	if (ei_local->dmaing) {
+		printk(KERN_ERR "%s: DMAing conflict in etherh_block_input: "
+			" DMAstat %d irqlock %d\n", dev->name,
+			ei_local->dmaing, ei_local->irqlock);
+		return;
+	}
+
+	/*
+	 * Make sure we have a round number of bytes if we're in word mode.
+	 */
+	if (count & 1 && ei_local->word16)
+		count++;
+
+	ei_local->dmaing = 1;
+
+	addr = (void __iomem *)dev->base_addr;
+	dma_base = etherh_priv(dev)->dma_base;
+
+	count = (count + 1) & ~1;
+	writeb (E8390_NODMA | E8390_PAGE0 | E8390_START, addr + E8390_CMD);
+
+	writeb (0x42, addr + EN0_RCNTLO);
+	writeb (0x00, addr + EN0_RCNTHI);
+	writeb (0x42, addr + EN0_RSARLO);
+	writeb (0x00, addr + EN0_RSARHI);
+	writeb (E8390_RREAD | E8390_START, addr + E8390_CMD);
+
+	udelay (1);
+
+	writeb (ENISR_RDC, addr + EN0_ISR);
+	writeb (count, addr + EN0_RCNTLO);
+	writeb (count >> 8, addr + EN0_RCNTHI);
+	writeb (0, addr + EN0_RSARLO);
+	writeb (start_page, addr + EN0_RSARHI);
+	writeb (E8390_RWRITE | E8390_START, addr + E8390_CMD);
+
+	if (ei_local->word16)
+		writesw (dma_base, buf, count >> 1);
+	else
+		writesb (dma_base, buf, count);
+
+	dma_start = jiffies;
+
+	while ((readb (addr + EN0_ISR) & ENISR_RDC) == 0)
+		if (time_after(jiffies, dma_start + 2*HZ/100)) { /* 20ms */
+			printk(KERN_ERR "%s: timeout waiting for TX RDC\n",
+				dev->name);
+			etherh_reset (dev);
+			__NS8390_init (dev, 1);
+			break;
+		}
+
+	writeb (ENISR_RDC, addr + EN0_ISR);
+	ei_local->dmaing = 0;
+}
+
+/*
+ * Read a block of data from the 8390
+ */
+static void
+etherh_block_input (struct net_device *dev, int count, struct sk_buff *skb, int ring_offset)
+{
+	struct ei_device *ei_local = netdev_priv(dev);
+	unsigned char *buf;
+	void __iomem *dma_base, *addr;
+
+	if (ei_local->dmaing) {
+		printk(KERN_ERR "%s: DMAing conflict in etherh_block_input: "
+			" DMAstat %d irqlock %d\n", dev->name,
+			ei_local->dmaing, ei_local->irqlock);
+		return;
+	}
+
+	ei_local->dmaing = 1;
+
+	addr = (void __iomem *)dev->base_addr;
+	dma_base = etherh_priv(dev)->dma_base;
+
+	buf = skb->data;
+	writeb (E8390_NODMA | E8390_PAGE0 | E8390_START, addr + E8390_CMD);
+	writeb (count, addr + EN0_RCNTLO);
+	writeb (count >> 8, addr + EN0_RCNTHI);
+	writeb (ring_offset, addr + EN0_RSARLO);
+	writeb (ring_offset >> 8, addr + EN0_RSARHI);
+	writeb (E8390_RREAD | E8390_START, addr + E8390_CMD);
+
+	if (ei_local->word16) {
+		readsw (dma_base, buf, count >> 1);
+		if (count & 1)
+			buf[count - 1] = readb (dma_base);
+	} else
+		readsb (dma_base, buf, count);
+
+	writeb (ENISR_RDC, addr + EN0_ISR);
+	ei_local->dmaing = 0;
+}
+
+/*
+ * Read a header from the 8390
+ */
+static void
+etherh_get_header (struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
+{
+	struct ei_device *ei_local = netdev_priv(dev);
+	void __iomem *dma_base, *addr;
+
+	if (ei_local->dmaing) {
+		printk(KERN_ERR "%s: DMAing conflict in etherh_get_header: "
+			" DMAstat %d irqlock %d\n", dev->name,
+			ei_local->dmaing, ei_local->irqlock);
+		return;
+	}
+
+	ei_local->dmaing = 1;
+
+	addr = (void __iomem *)dev->base_addr;
+	dma_base = etherh_priv(dev)->dma_base;
+
+	writeb (E8390_NODMA | E8390_PAGE0 | E8390_START, addr + E8390_CMD);
+	writeb (sizeof (*hdr), addr + EN0_RCNTLO);
+	writeb (0, addr + EN0_RCNTHI);
+	writeb (0, addr + EN0_RSARLO);
+	writeb (ring_page, addr + EN0_RSARHI);
+	writeb (E8390_RREAD | E8390_START, addr + E8390_CMD);
+
+	if (ei_local->word16)
+		readsw (dma_base, hdr, sizeof (*hdr) >> 1);
+	else
+		readsb (dma_base, hdr, sizeof (*hdr));
+
+	writeb (ENISR_RDC, addr + EN0_ISR);
+	ei_local->dmaing = 0;
+}
+
+/*
+ * 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 int
+etherh_open(struct net_device *dev)
+{
+	struct ei_device *ei_local = netdev_priv(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, __ei_interrupt, 0, dev->name, dev))
+		return -EAGAIN;
+
+	/*
+	 * Make sure that we aren't going to change the
+	 * media type on the next reset - we are about to
+	 * do automedia manually now.
+	 */
+	ei_local->interface_num = 0;
+
+	/*
+	 * If we are doing automedia detection, do it now.
+	 * This is more reliable than the 8390's detection.
+	 */
+	if (dev->flags & IFF_AUTOMEDIA) {
+		dev->if_port = IF_PORT_10BASET;
+		etherh_setif(dev);
+		mdelay(1);
+		if (!etherh_getifstat(dev)) {
+			dev->if_port = IF_PORT_10BASE2;
+			etherh_setif(dev);
+		}
+	} else
+		etherh_setif(dev);
+
+	etherh_reset(dev);
+	__ei_open(dev);
+
+	return 0;
+}
+
+/*
+ * The inverse routine to etherh_open().
+ */
+static int
+etherh_close(struct net_device *dev)
+{
+	__ei_close (dev);
+	free_irq (dev->irq, dev);
+	return 0;
+}
+
+/*
+ * Initialisation
+ */
+
+static void __init etherh_banner(void)
+{
+	static int version_printed;
+
+	if (net_debug && version_printed++ == 0)
+		printk(KERN_INFO "%s", version);
+}
+
+/*
+ * Read the ethernet address string from the on board rom.
+ * This is an ascii string...
+ */
+static int __devinit etherh_addr(char *addr, struct expansion_card *ec)
+{
+	struct in_chunk_dir cd;
+	char *s;
+	
+	if (!ecard_readchunk(&cd, ec, 0xf5, 0)) {
+		printk(KERN_ERR "%s: unable to read podule description string\n",
+		       dev_name(&ec->dev));
+		goto no_addr;
+	}
+
+	s = strchr(cd.d.string, '(');
+	if (s) {
+		int i;
+
+		for (i = 0; i < 6; i++) {
+			addr[i] = simple_strtoul(s + 1, &s, 0x10);
+			if (*s != (i == 5? ')' : ':'))
+				break;
+		}
+
+		if (i == 6)
+			return 0;
+	}
+
+	printk(KERN_ERR "%s: unable to parse MAC address: %s\n",
+	       dev_name(&ec->dev), cd.d.string);
+
+ no_addr:
+	return -ENODEV;
+}
+
+/*
+ * Create an ethernet address from the system serial number.
+ */
+static int __init etherm_addr(char *addr)
+{
+	unsigned int serial;
+
+	if (system_serial_low == 0 && system_serial_high == 0)
+		return -ENODEV;
+
+	serial = system_serial_low | system_serial_high;
+
+	addr[0] = 0;
+	addr[1] = 0;
+	addr[2] = 0xa4;
+	addr[3] = 0x10 + (serial >> 24);
+	addr[4] = serial >> 16;
+	addr[5] = serial >> 8;
+	return 0;
+}
+
+static void etherh_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
+{
+	strlcpy(info->driver, DRV_NAME, sizeof(info->driver));
+	strlcpy(info->version, DRV_VERSION, sizeof(info->version));
+	strlcpy(info->bus_info, dev_name(dev->dev.parent),
+		sizeof(info->bus_info));
+}
+
+static int etherh_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+	cmd->supported	= etherh_priv(dev)->supported;
+	ethtool_cmd_speed_set(cmd, SPEED_10);
+	cmd->duplex	= DUPLEX_HALF;
+	cmd->port	= dev->if_port == IF_PORT_10BASET ? PORT_TP : PORT_BNC;
+	cmd->autoneg	= (dev->flags & IFF_AUTOMEDIA ?
+			   AUTONEG_ENABLE : AUTONEG_DISABLE);
+	return 0;
+}
+
+static int etherh_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+	switch (cmd->autoneg) {
+	case AUTONEG_ENABLE:
+		dev->flags |= IFF_AUTOMEDIA;
+		break;
+
+	case AUTONEG_DISABLE:
+		switch (cmd->port) {
+		case PORT_TP:
+			dev->if_port = IF_PORT_10BASET;
+			break;
+
+		case PORT_BNC:
+			dev->if_port = IF_PORT_10BASE2;
+			break;
+
+		default:
+			return -EINVAL;
+		}
+		dev->flags &= ~IFF_AUTOMEDIA;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	etherh_setif(dev);
+
+	return 0;
+}
+
+static const struct ethtool_ops etherh_ethtool_ops = {
+	.get_settings	= etherh_get_settings,
+	.set_settings	= etherh_set_settings,
+	.get_drvinfo	= etherh_get_drvinfo,
+};
+
+static const struct net_device_ops etherh_netdev_ops = {
+	.ndo_open		= etherh_open,
+	.ndo_stop		= etherh_close,
+	.ndo_set_config		= etherh_set_config,
+	.ndo_start_xmit		= __ei_start_xmit,
+	.ndo_tx_timeout		= __ei_tx_timeout,
+	.ndo_get_stats		= __ei_get_stats,
+	.ndo_set_multicast_list = __ei_set_multicast_list,
+	.ndo_validate_addr	= eth_validate_addr,
+	.ndo_set_mac_address	= eth_mac_addr,
+	.ndo_change_mtu		= eth_change_mtu,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	.ndo_poll_controller	= __ei_poll,
+#endif
+};
+
+static u32 etherh_regoffsets[16];
+static u32 etherm_regoffsets[16];
+
+static int __devinit
+etherh_probe(struct expansion_card *ec, const struct ecard_id *id)
+{
+	const struct etherh_data *data = id->data;
+	struct ei_device *ei_local;
+	struct net_device *dev;
+	struct etherh_priv *eh;
+	int ret;
+
+	etherh_banner();
+
+	ret = ecard_request_resources(ec);
+	if (ret)
+		goto out;
+
+	dev = ____alloc_ei_netdev(sizeof(struct etherh_priv));
+	if (!dev) {
+		ret = -ENOMEM;
+		goto release;
+	}
+
+	SET_NETDEV_DEV(dev, &ec->dev);
+
+	dev->netdev_ops		= &etherh_netdev_ops;
+	dev->irq		= ec->irq;
+	dev->ethtool_ops	= &etherh_ethtool_ops;
+
+	if (data->supported & SUPPORTED_Autoneg)
+		dev->flags |= IFF_AUTOMEDIA;
+	if (data->supported & SUPPORTED_TP) {
+		dev->flags |= IFF_PORTSEL;
+		dev->if_port = IF_PORT_10BASET;
+	} else if (data->supported & SUPPORTED_BNC) {
+		dev->flags |= IFF_PORTSEL;
+		dev->if_port = IF_PORT_10BASE2;
+	} else
+		dev->if_port = IF_PORT_UNKNOWN;
+
+	eh = etherh_priv(dev);
+	eh->supported		= data->supported;
+	eh->ctrl		= 0;
+	eh->id			= ec->cid.product;
+	eh->memc		= ecardm_iomap(ec, ECARD_RES_MEMC, 0, PAGE_SIZE);
+	if (!eh->memc) {
+		ret = -ENOMEM;
+		goto free;
+	}
+
+	eh->ctrl_port = eh->memc;
+	if (data->ctrl_ioc) {
+		eh->ioc_fast = ecardm_iomap(ec, ECARD_RES_IOCFAST, 0, PAGE_SIZE);
+		if (!eh->ioc_fast) {
+			ret = -ENOMEM;
+			goto free;
+		}
+		eh->ctrl_port = eh->ioc_fast;
+	}
+
+	dev->base_addr = (unsigned long)eh->memc + data->ns8390_offset;
+	eh->dma_base = eh->memc + data->dataport_offset;
+	eh->ctrl_port += data->ctrlport_offset;
+
+	/*
+	 * IRQ and control port handling - only for non-NIC slot cards.
+	 */
+	if (ec->slot_no != 8) {
+		ecard_setirq(ec, &etherh_ops, eh);
+	} else {
+		/*
+		 * If we're in the NIC slot, make sure the IRQ is enabled
+		 */
+		etherh_set_ctrl(eh, ETHERH_CP_IE);
+	}
+
+	ei_local = netdev_priv(dev);
+	spin_lock_init(&ei_local->page_lock);
+
+	if (ec->cid.product == PROD_ANT_ETHERM) {
+		etherm_addr(dev->dev_addr);
+		ei_local->reg_offset = etherm_regoffsets;
+	} else {
+		etherh_addr(dev->dev_addr, ec);
+		ei_local->reg_offset = etherh_regoffsets;
+	}
+
+	ei_local->name          = dev->name;
+	ei_local->word16        = 1;
+	ei_local->tx_start_page = data->tx_start_page;
+	ei_local->rx_start_page = ei_local->tx_start_page + TX_PAGES;
+	ei_local->stop_page     = data->stop_page;
+	ei_local->reset_8390    = etherh_reset;
+	ei_local->block_input   = etherh_block_input;
+	ei_local->block_output  = etherh_block_output;
+	ei_local->get_8390_hdr  = etherh_get_header;
+	ei_local->interface_num = 0;
+
+	etherh_reset(dev);
+	__NS8390_init(dev, 0);
+
+	ret = register_netdev(dev);
+	if (ret)
+		goto free;
+
+	printk(KERN_INFO "%s: %s in slot %d, %pM\n",
+		dev->name, data->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 etherh_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 struct etherh_data etherm_data = {
+	.ns8390_offset		= ETHERM_NS8390,
+	.dataport_offset	= ETHERM_NS8390 + ETHERM_DATAPORT,
+	.ctrlport_offset	= ETHERM_NS8390 + ETHERM_CTRLPORT,
+	.name			= "ANT EtherM",
+	.supported		= SUPPORTED_10baseT_Half,
+	.tx_start_page		= ETHERM_TX_START_PAGE,
+	.stop_page		= ETHERM_STOP_PAGE,
+};
+
+static struct etherh_data etherlan500_data = {
+	.ns8390_offset		= ETHERH500_NS8390,
+	.dataport_offset	= ETHERH500_NS8390 + ETHERH500_DATAPORT,
+	.ctrlport_offset	= ETHERH500_CTRLPORT,
+	.ctrl_ioc		= 1,
+	.name			= "i3 EtherH 500",
+	.supported		= SUPPORTED_10baseT_Half,
+	.tx_start_page		= ETHERH_TX_START_PAGE,
+	.stop_page		= ETHERH_STOP_PAGE,
+};
+
+static struct etherh_data etherlan600_data = {
+	.ns8390_offset		= ETHERH600_NS8390,
+	.dataport_offset	= ETHERH600_NS8390 + ETHERH600_DATAPORT,
+	.ctrlport_offset	= ETHERH600_NS8390 + ETHERH600_CTRLPORT,
+	.name			= "i3 EtherH 600",
+	.supported		= SUPPORTED_10baseT_Half | SUPPORTED_TP | SUPPORTED_BNC | SUPPORTED_Autoneg,
+	.tx_start_page		= ETHERH_TX_START_PAGE,
+	.stop_page		= ETHERH_STOP_PAGE,
+};
+
+static struct etherh_data etherlan600a_data = {
+	.ns8390_offset		= ETHERH600_NS8390,
+	.dataport_offset	= ETHERH600_NS8390 + ETHERH600_DATAPORT,
+	.ctrlport_offset	= ETHERH600_NS8390 + ETHERH600_CTRLPORT,
+	.name			= "i3 EtherH 600A",
+	.supported		= SUPPORTED_10baseT_Half | SUPPORTED_TP | SUPPORTED_BNC | SUPPORTED_Autoneg,
+	.tx_start_page		= ETHERH_TX_START_PAGE,
+	.stop_page		= ETHERH_STOP_PAGE,
+};
+
+static const struct ecard_id etherh_ids[] = {
+	{ MANU_ANT, PROD_ANT_ETHERM,      &etherm_data       },
+	{ MANU_I3,  PROD_I3_ETHERLAN500,  &etherlan500_data  },
+	{ MANU_I3,  PROD_I3_ETHERLAN600,  &etherlan600_data  },
+	{ MANU_I3,  PROD_I3_ETHERLAN600A, &etherlan600a_data },
+	{ 0xffff,   0xffff }
+};
+
+static struct ecard_driver etherh_driver = {
+	.probe		= etherh_probe,
+	.remove		= __devexit_p(etherh_remove),
+	.id_table	= etherh_ids,
+	.drv = {
+		.name	= DRV_NAME,
+	},
+};
+
+static int __init etherh_init(void)
+{
+	int i;
+
+	for (i = 0; i < 16; i++) {
+		etherh_regoffsets[i] = i << 2;
+		etherm_regoffsets[i] = i << 5;
+	}
+
+	return ecard_register_driver(&etherh_driver);
+}
+
+static void __exit etherh_exit(void)
+{
+	ecard_remove_driver(&etherh_driver);
+}
+
+module_init(etherh_init);
+module_exit(etherh_exit);
diff --git a/drivers/net/ethernet/8390/hp-plus.c b/drivers/net/ethernet/8390/hp-plus.c
new file mode 100644
index 0000000..2991736
--- /dev/null
+++ b/drivers/net/ethernet/8390/hp-plus.c
@@ -0,0 +1,506 @@
+/* hp-plus.c: A HP PCLAN/plus ethernet driver for linux. */
+/*
+	Written 1994 by Donald Becker.
+
+	This driver is for the Hewlett Packard PC LAN (27***) plus ethercards.
+	These cards are sold under several model numbers, usually 2724*.
+
+	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
+
+	As is often the case, a great deal of credit is owed to Russ Nelson.
+	The Crynwr packet driver was my primary source of HP-specific
+	programming information.
+*/
+
+static const char version[] =
+"hp-plus.c:v1.10 9/24/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
+
+#include <linux/module.h>
+
+#include <linux/string.h>		/* Important -- this inlines word moves. */
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+
+#include "8390.h"
+
+#define DRV_NAME "hp-plus"
+
+/* A zero-terminated list of I/O addresses to be probed. */
+static unsigned int hpplus_portlist[] __initdata =
+{0x200, 0x240, 0x280, 0x2C0, 0x300, 0x320, 0x340, 0};
+
+/*
+   The HP EtherTwist chip implementation is a fairly routine DP8390
+   implementation.  It allows both shared memory and programmed-I/O buffer
+   access, using a custom interface for both.  The programmed-I/O mode is
+   entirely implemented in the HP EtherTwist chip, bypassing the problem
+   ridden built-in 8390 facilities used on NE2000 designs.  The shared
+   memory mode is likewise special, with an offset register used to make
+   packets appear at the shared memory base.  Both modes use a base and bounds
+   page register to hide the Rx ring buffer wrap -- a packet that spans the
+   end of physical buffer memory appears continuous to the driver. (c.f. the
+   3c503 and Cabletron E2100)
+
+   A special note: the internal buffer of the board is only 8 bits wide.
+   This lays several nasty traps for the unaware:
+   - the 8390 must be programmed for byte-wide operations
+   - all I/O and memory operations must work on whole words (the access
+     latches are serially preloaded and have no byte-swapping ability).
+
+   This board is laid out in I/O space much like the earlier HP boards:
+   the first 16 locations are for the board registers, and the second 16 are
+   for the 8390.  The board is easy to identify, with both a dedicated 16 bit
+   ID register and a constant 0x530* value in the upper bits of the paging
+   register.
+*/
+
+#define HP_ID			0x00	/* ID register, always 0x4850. */
+#define HP_PAGING		0x02	/* Registers visible @ 8-f, see PageName. */
+#define HPP_OPTION		0x04	/* Bitmapped options, see HP_Option.	*/
+#define HPP_OUT_ADDR	0x08	/* I/O output location in Perf_Page.	*/
+#define HPP_IN_ADDR		0x0A	/* I/O input location in Perf_Page.		*/
+#define HP_DATAPORT		0x0c	/* I/O data transfer in Perf_Page.		*/
+#define NIC_OFFSET		0x10	/* Offset to the 8390 registers.		*/
+#define HP_IO_EXTENT	32
+
+#define HP_START_PG		0x00	/* First page of TX buffer */
+#define HP_STOP_PG		0x80	/* Last page +1 of RX ring */
+
+/* The register set selected in HP_PAGING. */
+enum PageName {
+	Perf_Page = 0,				/* Normal operation. */
+	MAC_Page = 1,				/* The ethernet address (+checksum). */
+	HW_Page = 2,				/* EEPROM-loaded hardware parameters. */
+	LAN_Page = 4,				/* Transceiver selection, testing, etc. */
+	ID_Page = 6 };
+
+/* The bit definitions for the HPP_OPTION register. */
+enum HP_Option {
+	NICReset = 1, ChipReset = 2, 	/* Active low, really UNreset. */
+	EnableIRQ = 4, FakeIntr = 8, BootROMEnb = 0x10, IOEnb = 0x20,
+	MemEnable = 0x40, ZeroWait = 0x80, MemDisable = 0x1000, };
+
+static int hpp_probe1(struct net_device *dev, int ioaddr);
+
+static void hpp_reset_8390(struct net_device *dev);
+static int hpp_open(struct net_device *dev);
+static int hpp_close(struct net_device *dev);
+static void hpp_mem_block_input(struct net_device *dev, int count,
+						  struct sk_buff *skb, int ring_offset);
+static void hpp_mem_block_output(struct net_device *dev, int count,
+							const unsigned char *buf, int start_page);
+static void hpp_mem_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
+						  int ring_page);
+static void hpp_io_block_input(struct net_device *dev, int count,
+						  struct sk_buff *skb, int ring_offset);
+static void hpp_io_block_output(struct net_device *dev, int count,
+							const unsigned char *buf, int start_page);
+static void hpp_io_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
+						  int ring_page);
+
+
+/*	Probe a list of addresses for an HP LAN+ adaptor.
+	This routine is almost boilerplate. */
+
+static int __init do_hpp_probe(struct net_device *dev)
+{
+	int i;
+	int base_addr = dev->base_addr;
+	int irq = dev->irq;
+
+	if (base_addr > 0x1ff)		/* Check a single specified location. */
+		return hpp_probe1(dev, base_addr);
+	else if (base_addr != 0)	/* Don't probe at all. */
+		return -ENXIO;
+
+	for (i = 0; hpplus_portlist[i]; i++) {
+		if (hpp_probe1(dev, hpplus_portlist[i]) == 0)
+			return 0;
+		dev->irq = irq;
+	}
+
+	return -ENODEV;
+}
+
+#ifndef MODULE
+struct net_device * __init hp_plus_probe(int unit)
+{
+	struct net_device *dev = alloc_eip_netdev();
+	int err;
+
+	if (!dev)
+		return ERR_PTR(-ENOMEM);
+
+	sprintf(dev->name, "eth%d", unit);
+	netdev_boot_setup_check(dev);
+
+	err = do_hpp_probe(dev);
+	if (err)
+		goto out;
+	return dev;
+out:
+	free_netdev(dev);
+	return ERR_PTR(err);
+}
+#endif
+
+static const struct net_device_ops hpp_netdev_ops = {
+	.ndo_open		= hpp_open,
+	.ndo_stop		= hpp_close,
+	.ndo_start_xmit		= eip_start_xmit,
+	.ndo_tx_timeout		= eip_tx_timeout,
+	.ndo_get_stats		= eip_get_stats,
+	.ndo_set_multicast_list = eip_set_multicast_list,
+	.ndo_validate_addr	= eth_validate_addr,
+	.ndo_set_mac_address 	= eth_mac_addr,
+	.ndo_change_mtu		= eth_change_mtu,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	.ndo_poll_controller	= eip_poll,
+#endif
+};
+
+
+/* Do the interesting part of the probe at a single address. */
+static int __init hpp_probe1(struct net_device *dev, int ioaddr)
+{
+	int i, retval;
+	unsigned char checksum = 0;
+	const char name[] = "HP-PC-LAN+";
+	int mem_start;
+	static unsigned version_printed;
+
+	if (!request_region(ioaddr, HP_IO_EXTENT, DRV_NAME))
+		return -EBUSY;
+
+	/* Check for the HP+ signature, 50 48 0x 53. */
+	if (inw(ioaddr + HP_ID) != 0x4850 ||
+	    (inw(ioaddr + HP_PAGING) & 0xfff0) != 0x5300) {
+		retval = -ENODEV;
+		goto out;
+	}
+
+	if (ei_debug  &&  version_printed++ == 0)
+		printk(version);
+
+	printk("%s: %s at %#3x, ", dev->name, name, ioaddr);
+
+	/* Retrieve and checksum the station address. */
+	outw(MAC_Page, ioaddr + HP_PAGING);
+
+	for(i = 0; i < ETHER_ADDR_LEN; i++) {
+		unsigned char inval = inb(ioaddr + 8 + i);
+		dev->dev_addr[i] = inval;
+		checksum += inval;
+	}
+	checksum += inb(ioaddr + 14);
+
+	printk("%pM", dev->dev_addr);
+
+	if (checksum != 0xff) {
+		printk(" bad checksum %2.2x.\n", checksum);
+		retval = -ENODEV;
+		goto out;
+	} else {
+		/* Point at the Software Configuration Flags. */
+		outw(ID_Page, ioaddr + HP_PAGING);
+		printk(" ID %4.4x", inw(ioaddr + 12));
+	}
+
+	/* Read the IRQ line. */
+	outw(HW_Page, ioaddr + HP_PAGING);
+	{
+		int irq = inb(ioaddr + 13) & 0x0f;
+		int option = inw(ioaddr + HPP_OPTION);
+
+		dev->irq = irq;
+		if (option & MemEnable) {
+			mem_start = inw(ioaddr + 9) << 8;
+			printk(", IRQ %d, memory address %#x.\n", irq, mem_start);
+		} else {
+			mem_start = 0;
+			printk(", IRQ %d, programmed-I/O mode.\n", irq);
+		}
+	}
+
+	/* Set the wrap registers for string I/O reads.   */
+	outw((HP_START_PG + TX_PAGES/2) | ((HP_STOP_PG - 1) << 8), ioaddr + 14);
+
+	/* Set the base address to point to the NIC, not the "real" base! */
+	dev->base_addr = ioaddr + NIC_OFFSET;
+
+	dev->netdev_ops = &hpp_netdev_ops;
+
+	ei_status.name = name;
+	ei_status.word16 = 0;		/* Agggghhhhh! Debug time: 2 days! */
+	ei_status.tx_start_page = HP_START_PG;
+	ei_status.rx_start_page = HP_START_PG + TX_PAGES/2;
+	ei_status.stop_page = HP_STOP_PG;
+
+	ei_status.reset_8390 = &hpp_reset_8390;
+	ei_status.block_input = &hpp_io_block_input;
+	ei_status.block_output = &hpp_io_block_output;
+	ei_status.get_8390_hdr = &hpp_io_get_8390_hdr;
+
+	/* Check if the memory_enable flag is set in the option register. */
+	if (mem_start) {
+		ei_status.block_input = &hpp_mem_block_input;
+		ei_status.block_output = &hpp_mem_block_output;
+		ei_status.get_8390_hdr = &hpp_mem_get_8390_hdr;
+		dev->mem_start = mem_start;
+		ei_status.mem = ioremap(mem_start,
+					(HP_STOP_PG - HP_START_PG)*256);
+		if (!ei_status.mem) {
+			retval = -ENOMEM;
+			goto out;
+		}
+		ei_status.rmem_start = dev->mem_start + TX_PAGES/2*256;
+		dev->mem_end = ei_status.rmem_end
+			= dev->mem_start + (HP_STOP_PG - HP_START_PG)*256;
+	}
+
+	outw(Perf_Page, ioaddr + HP_PAGING);
+	NS8390p_init(dev, 0);
+	/* Leave the 8390 and HP chip reset. */
+	outw(inw(ioaddr + HPP_OPTION) & ~EnableIRQ, ioaddr + HPP_OPTION);
+
+	retval = register_netdev(dev);
+	if (retval)
+		goto out1;
+	return 0;
+out1:
+	iounmap(ei_status.mem);
+out:
+	release_region(ioaddr, HP_IO_EXTENT);
+	return retval;
+}
+
+static int
+hpp_open(struct net_device *dev)
+{
+	int ioaddr = dev->base_addr - NIC_OFFSET;
+	int option_reg;
+	int retval;
+
+	if ((retval = request_irq(dev->irq, eip_interrupt, 0, dev->name, dev))) {
+	    return retval;
+	}
+
+	/* Reset the 8390 and HP chip. */
+	option_reg = inw(ioaddr + HPP_OPTION);
+	outw(option_reg & ~(NICReset + ChipReset), ioaddr + HPP_OPTION);
+	udelay(5);
+	/* Unreset the board and enable interrupts. */
+	outw(option_reg | (EnableIRQ + NICReset + ChipReset), ioaddr + HPP_OPTION);
+
+	/* Set the wrap registers for programmed-I/O operation.   */
+	outw(HW_Page, ioaddr + HP_PAGING);
+	outw((HP_START_PG + TX_PAGES/2) | ((HP_STOP_PG - 1) << 8), ioaddr + 14);
+
+	/* Select the operational page. */
+	outw(Perf_Page, ioaddr + HP_PAGING);
+
+	return eip_open(dev);
+}
+
+static int
+hpp_close(struct net_device *dev)
+{
+	int ioaddr = dev->base_addr - NIC_OFFSET;
+	int option_reg = inw(ioaddr + HPP_OPTION);
+
+	free_irq(dev->irq, dev);
+	eip_close(dev);
+	outw((option_reg & ~EnableIRQ) | MemDisable | NICReset | ChipReset,
+		 ioaddr + HPP_OPTION);
+
+	return 0;
+}
+
+static void
+hpp_reset_8390(struct net_device *dev)
+{
+	int ioaddr = dev->base_addr - NIC_OFFSET;
+	int option_reg = inw(ioaddr + HPP_OPTION);
+
+	if (ei_debug > 1) printk("resetting the 8390 time=%ld...", jiffies);
+
+	outw(option_reg & ~(NICReset + ChipReset), ioaddr + HPP_OPTION);
+	/* Pause a few cycles for the hardware reset to take place. */
+	udelay(5);
+	ei_status.txing = 0;
+	outw(option_reg | (EnableIRQ + NICReset + ChipReset), ioaddr + HPP_OPTION);
+
+	udelay(5);
+
+
+	if ((inb_p(ioaddr+NIC_OFFSET+EN0_ISR) & ENISR_RESET) == 0)
+		printk("%s: hp_reset_8390() did not complete.\n", dev->name);
+
+	if (ei_debug > 1) printk("8390 reset done (%ld).", jiffies);
+}
+
+/* The programmed-I/O version of reading the 4 byte 8390 specific header.
+   Note that transfer with the EtherTwist+ must be on word boundaries. */
+
+static void
+hpp_io_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
+{
+	int ioaddr = dev->base_addr - NIC_OFFSET;
+
+	outw((ring_page<<8), ioaddr + HPP_IN_ADDR);
+	insw(ioaddr + HP_DATAPORT, hdr, sizeof(struct e8390_pkt_hdr)>>1);
+}
+
+/* Block input and output, similar to the Crynwr packet driver. */
+
+static void
+hpp_io_block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset)
+{
+	int ioaddr = dev->base_addr - NIC_OFFSET;
+	char *buf = skb->data;
+
+	outw(ring_offset, ioaddr + HPP_IN_ADDR);
+	insw(ioaddr + HP_DATAPORT, buf, count>>1);
+	if (count & 0x01)
+        buf[count-1] = inw(ioaddr + HP_DATAPORT);
+}
+
+/* The corresponding shared memory versions of the above 2 functions. */
+
+static void
+hpp_mem_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
+{
+	int ioaddr = dev->base_addr - NIC_OFFSET;
+	int option_reg = inw(ioaddr + HPP_OPTION);
+
+	outw((ring_page<<8), ioaddr + HPP_IN_ADDR);
+	outw(option_reg & ~(MemDisable + BootROMEnb), ioaddr + HPP_OPTION);
+	memcpy_fromio(hdr, ei_status.mem, sizeof(struct e8390_pkt_hdr));
+	outw(option_reg, ioaddr + HPP_OPTION);
+	hdr->count = (le16_to_cpu(hdr->count) + 3) & ~3;	/* Round up allocation. */
+}
+
+static void
+hpp_mem_block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset)
+{
+	int ioaddr = dev->base_addr - NIC_OFFSET;
+	int option_reg = inw(ioaddr + HPP_OPTION);
+
+	outw(ring_offset, ioaddr + HPP_IN_ADDR);
+
+	outw(option_reg & ~(MemDisable + BootROMEnb), ioaddr + HPP_OPTION);
+
+	/* Caution: this relies on get_8390_hdr() rounding up count!
+	   Also note that we *can't* use eth_io_copy_and_sum() because
+	   it will not always copy "count" bytes (e.g. padded IP).  */
+
+	memcpy_fromio(skb->data, ei_status.mem, count);
+	outw(option_reg, ioaddr + HPP_OPTION);
+}
+
+/* A special note: we *must* always transfer >=16 bit words.
+   It's always safe to round up, so we do. */
+static void
+hpp_io_block_output(struct net_device *dev, int count,
+					const unsigned char *buf, int start_page)
+{
+	int ioaddr = dev->base_addr - NIC_OFFSET;
+	outw(start_page << 8, ioaddr + HPP_OUT_ADDR);
+	outsl(ioaddr + HP_DATAPORT, buf, (count+3)>>2);
+}
+
+static void
+hpp_mem_block_output(struct net_device *dev, int count,
+				const unsigned char *buf, int start_page)
+{
+	int ioaddr = dev->base_addr - NIC_OFFSET;
+	int option_reg = inw(ioaddr + HPP_OPTION);
+
+	outw(start_page << 8, ioaddr + HPP_OUT_ADDR);
+	outw(option_reg & ~(MemDisable + BootROMEnb), ioaddr + HPP_OPTION);
+	memcpy_toio(ei_status.mem, buf, (count + 3) & ~3);
+	outw(option_reg, ioaddr + HPP_OPTION);
+}
+
+
+#ifdef MODULE
+#define MAX_HPP_CARDS	4	/* Max number of HPP cards per module */
+static struct net_device *dev_hpp[MAX_HPP_CARDS];
+static int io[MAX_HPP_CARDS];
+static int irq[MAX_HPP_CARDS];
+
+module_param_array(io, int, NULL, 0);
+module_param_array(irq, int, NULL, 0);
+MODULE_PARM_DESC(io, "I/O port address(es)");
+MODULE_PARM_DESC(irq, "IRQ number(s); ignored if properly detected");
+MODULE_DESCRIPTION("HP PC-LAN+ ISA ethernet driver");
+MODULE_LICENSE("GPL");
+
+/* This is set up so that only a single autoprobe takes place per call.
+ISA device autoprobes on a running machine are not recommended. */
+int __init
+init_module(void)
+{
+	struct net_device *dev;
+	int this_dev, found = 0;
+
+	for (this_dev = 0; this_dev < MAX_HPP_CARDS; this_dev++) {
+		if (io[this_dev] == 0)  {
+			if (this_dev != 0) break; /* only autoprobe 1st one */
+			printk(KERN_NOTICE "hp-plus.c: Presently autoprobing (not recommended) for a single card.\n");
+		}
+		dev = alloc_eip_netdev();
+		if (!dev)
+			break;
+		dev->irq = irq[this_dev];
+		dev->base_addr = io[this_dev];
+		if (do_hpp_probe(dev) == 0) {
+			dev_hpp[found++] = dev;
+			continue;
+		}
+		free_netdev(dev);
+		printk(KERN_WARNING "hp-plus.c: No HP-Plus card found (i/o = 0x%x).\n", io[this_dev]);
+		break;
+	}
+	if (found)
+		return 0;
+	return -ENXIO;
+}
+
+static void cleanup_card(struct net_device *dev)
+{
+	/* NB: hpp_close() handles free_irq */
+	iounmap(ei_status.mem);
+	release_region(dev->base_addr - NIC_OFFSET, HP_IO_EXTENT);
+}
+
+void __exit
+cleanup_module(void)
+{
+	int this_dev;
+
+	for (this_dev = 0; this_dev < MAX_HPP_CARDS; this_dev++) {
+		struct net_device *dev = dev_hpp[this_dev];
+		if (dev) {
+			unregister_netdev(dev);
+			cleanup_card(dev);
+			free_netdev(dev);
+		}
+	}
+}
+#endif /* MODULE */
diff --git a/drivers/net/ethernet/8390/hp.c b/drivers/net/ethernet/8390/hp.c
new file mode 100644
index 0000000..18564d4
--- /dev/null
+++ b/drivers/net/ethernet/8390/hp.c
@@ -0,0 +1,439 @@
+/* hp.c: A HP LAN ethernet driver for linux. */
+/*
+	Written 1993-94 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
+
+	This is a driver for the HP PC-LAN adaptors.
+
+	Sources:
+	  The Crynwr packet driver.
+*/
+
+static const char version[] =
+	"hp.c:v1.10 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
+
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+
+#include "8390.h"
+
+#define DRV_NAME "hp"
+
+/* A zero-terminated list of I/O addresses to be probed. */
+static unsigned int hppclan_portlist[] __initdata =
+{ 0x300, 0x320, 0x340, 0x280, 0x2C0, 0x200, 0x240, 0};
+
+#define HP_IO_EXTENT	32
+
+#define HP_DATAPORT		0x0c	/* "Remote DMA" data port. */
+#define HP_ID			0x07
+#define HP_CONFIGURE	0x08	/* Configuration register. */
+#define	 HP_RUN			0x01	/* 1 == Run, 0 == reset. */
+#define	 HP_IRQ			0x0E	/* Mask for software-configured IRQ line. */
+#define	 HP_DATAON		0x10	/* Turn on dataport */
+#define NIC_OFFSET		0x10	/* Offset the 8390 registers. */
+
+#define HP_START_PG		0x00	/* First page of TX buffer */
+#define HP_8BSTOP_PG	0x80	/* Last page +1 of RX ring */
+#define HP_16BSTOP_PG	0xFF	/* Same, for 16 bit cards. */
+
+static int hp_probe1(struct net_device *dev, int ioaddr);
+
+static void hp_reset_8390(struct net_device *dev);
+static void hp_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
+					int ring_page);
+static void hp_block_input(struct net_device *dev, int count,
+					struct sk_buff *skb , int ring_offset);
+static void hp_block_output(struct net_device *dev, int count,
+							const unsigned char *buf, int start_page);
+
+static void hp_init_card(struct net_device *dev);
+
+/* The map from IRQ number to HP_CONFIGURE register setting. */
+/* My default is IRQ5	             0  1  2  3  4  5  6  7  8  9 10 11 */
+static char irqmap[16] __initdata= { 0, 0, 4, 6, 8,10, 0,14, 0, 4, 2,12,0,0,0,0};
+
+
+/*	Probe for an HP LAN adaptor.
+	Also initialize the card and fill in STATION_ADDR with the station
+	address. */
+
+static int __init do_hp_probe(struct net_device *dev)
+{
+	int i;
+	int base_addr = dev->base_addr;
+	int irq = dev->irq;
+
+	if (base_addr > 0x1ff)		/* Check a single specified location. */
+		return hp_probe1(dev, base_addr);
+	else if (base_addr != 0)	/* Don't probe at all. */
+		return -ENXIO;
+
+	for (i = 0; hppclan_portlist[i]; i++) {
+		if (hp_probe1(dev, hppclan_portlist[i]) == 0)
+			return 0;
+		dev->irq = irq;
+	}
+
+	return -ENODEV;
+}
+
+#ifndef MODULE
+struct net_device * __init hp_probe(int unit)
+{
+	struct net_device *dev = alloc_eip_netdev();
+	int err;
+
+	if (!dev)
+		return ERR_PTR(-ENOMEM);
+
+	sprintf(dev->name, "eth%d", unit);
+	netdev_boot_setup_check(dev);
+
+	err = do_hp_probe(dev);
+	if (err)
+		goto out;
+	return dev;
+out:
+	free_netdev(dev);
+	return ERR_PTR(err);
+}
+#endif
+
+static int __init hp_probe1(struct net_device *dev, int ioaddr)
+{
+	int i, retval, board_id, wordmode;
+	const char *name;
+	static unsigned version_printed;
+
+	if (!request_region(ioaddr, HP_IO_EXTENT, DRV_NAME))
+		return -EBUSY;
+
+	/* Check for the HP physical address, 08 00 09 xx xx xx. */
+	/* This really isn't good enough: we may pick up HP LANCE boards
+	   also!  Avoid the lance 0x5757 signature. */
+	if (inb(ioaddr) != 0x08
+		|| inb(ioaddr+1) != 0x00
+		|| inb(ioaddr+2) != 0x09
+		|| inb(ioaddr+14) == 0x57) {
+		retval = -ENODEV;
+		goto out;
+	}
+
+	/* Set up the parameters based on the board ID.
+	   If you have additional mappings, please mail them to me -djb. */
+	if ((board_id = inb(ioaddr + HP_ID)) & 0x80) {
+		name = "HP27247";
+		wordmode = 1;
+	} else {
+		name = "HP27250";
+		wordmode = 0;
+	}
+
+	if (ei_debug  &&  version_printed++ == 0)
+		printk(version);
+
+	printk("%s: %s (ID %02x) at %#3x,", dev->name, name, board_id, ioaddr);
+
+	for(i = 0; i < ETHER_ADDR_LEN; i++)
+		dev->dev_addr[i] = inb(ioaddr + i);
+
+	printk(" %pM", dev->dev_addr);
+
+	/* Snarf the interrupt now.  Someday this could be moved to open(). */
+	if (dev->irq < 2) {
+		static const int irq_16list[] = { 11, 10, 5, 3, 4, 7, 9, 0};
+		static const int irq_8list[] = { 7, 5, 3, 4, 9, 0};
+		const int *irqp = wordmode ? irq_16list : irq_8list;
+		do {
+			int irq = *irqp;
+			if (request_irq (irq, NULL, 0, "bogus", NULL) != -EBUSY) {
+				unsigned long cookie = probe_irq_on();
+				/* Twinkle the interrupt, and check if it's seen. */
+				outb_p(irqmap[irq] | HP_RUN, ioaddr + HP_CONFIGURE);
+				outb_p( 0x00 | HP_RUN, ioaddr + HP_CONFIGURE);
+				if (irq == probe_irq_off(cookie)		 /* It's a good IRQ line! */
+					&& request_irq (irq, eip_interrupt, 0, DRV_NAME, dev) == 0) {
+					printk(" selecting IRQ %d.\n", irq);
+					dev->irq = *irqp;
+					break;
+				}
+			}
+		} while (*++irqp);
+		if (*irqp == 0) {
+			printk(" no free IRQ lines.\n");
+			retval = -EBUSY;
+			goto out;
+		}
+	} else {
+		if (dev->irq == 2)
+			dev->irq = 9;
+		if ((retval = request_irq(dev->irq, eip_interrupt, 0, DRV_NAME, dev))) {
+			printk (" unable to get IRQ %d.\n", dev->irq);
+			goto out;
+		}
+	}
+
+	/* Set the base address to point to the NIC, not the "real" base! */
+	dev->base_addr = ioaddr + NIC_OFFSET;
+	dev->netdev_ops = &eip_netdev_ops;
+
+	ei_status.name = name;
+	ei_status.word16 = wordmode;
+	ei_status.tx_start_page = HP_START_PG;
+	ei_status.rx_start_page = HP_START_PG + TX_PAGES;
+	ei_status.stop_page = wordmode ? HP_16BSTOP_PG : HP_8BSTOP_PG;
+
+	ei_status.reset_8390 = hp_reset_8390;
+	ei_status.get_8390_hdr = hp_get_8390_hdr;
+	ei_status.block_input = hp_block_input;
+	ei_status.block_output = hp_block_output;
+	hp_init_card(dev);
+
+	retval = register_netdev(dev);
+	if (retval)
+		goto out1;
+	return 0;
+out1:
+	free_irq(dev->irq, dev);
+out:
+	release_region(ioaddr, HP_IO_EXTENT);
+	return retval;
+}
+
+static void
+hp_reset_8390(struct net_device *dev)
+{
+	int hp_base = dev->base_addr - NIC_OFFSET;
+	int saved_config = inb_p(hp_base + HP_CONFIGURE);
+
+	if (ei_debug > 1) printk("resetting the 8390 time=%ld...", jiffies);
+	outb_p(0x00, hp_base + HP_CONFIGURE);
+	ei_status.txing = 0;
+	/* Pause just a few cycles for the hardware reset to take place. */
+	udelay(5);
+
+	outb_p(saved_config, hp_base + HP_CONFIGURE);
+	udelay(5);
+
+	if ((inb_p(hp_base+NIC_OFFSET+EN0_ISR) & ENISR_RESET) == 0)
+		printk("%s: hp_reset_8390() did not complete.\n", dev->name);
+
+	if (ei_debug > 1) printk("8390 reset done (%ld).", jiffies);
+}
+
+static void
+hp_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
+{
+	int nic_base = dev->base_addr;
+	int saved_config = inb_p(nic_base - NIC_OFFSET + HP_CONFIGURE);
+
+	outb_p(saved_config | HP_DATAON, nic_base - NIC_OFFSET + HP_CONFIGURE);
+	outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base);
+	outb_p(sizeof(struct e8390_pkt_hdr), nic_base + EN0_RCNTLO);
+	outb_p(0, nic_base + EN0_RCNTHI);
+	outb_p(0, nic_base + EN0_RSARLO);	/* On page boundary */
+	outb_p(ring_page, nic_base + EN0_RSARHI);
+	outb_p(E8390_RREAD+E8390_START, nic_base);
+
+	if (ei_status.word16)
+	  insw(nic_base - NIC_OFFSET + HP_DATAPORT, hdr, sizeof(struct e8390_pkt_hdr)>>1);
+	else
+	  insb(nic_base - NIC_OFFSET + HP_DATAPORT, hdr, sizeof(struct e8390_pkt_hdr));
+
+	outb_p(saved_config & (~HP_DATAON), nic_base - NIC_OFFSET + HP_CONFIGURE);
+}
+
+/* Block input and output, similar to the Crynwr packet driver. If you are
+   porting to a new ethercard look at the packet driver source for hints.
+   The HP LAN doesn't use shared memory -- we put the packet
+   out through the "remote DMA" dataport. */
+
+static void
+hp_block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset)
+{
+	int nic_base = dev->base_addr;
+	int saved_config = inb_p(nic_base - NIC_OFFSET + HP_CONFIGURE);
+	int xfer_count = count;
+	char *buf = skb->data;
+
+	outb_p(saved_config | HP_DATAON, nic_base - NIC_OFFSET + HP_CONFIGURE);
+	outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base);
+	outb_p(count & 0xff, nic_base + EN0_RCNTLO);
+	outb_p(count >> 8, nic_base + EN0_RCNTHI);
+	outb_p(ring_offset & 0xff, nic_base + EN0_RSARLO);
+	outb_p(ring_offset >> 8, nic_base + EN0_RSARHI);
+	outb_p(E8390_RREAD+E8390_START, nic_base);
+	if (ei_status.word16) {
+	  insw(nic_base - NIC_OFFSET + HP_DATAPORT,buf,count>>1);
+	  if (count & 0x01)
+		buf[count-1] = inb(nic_base - NIC_OFFSET + HP_DATAPORT), xfer_count++;
+	} else {
+		insb(nic_base - NIC_OFFSET + HP_DATAPORT, buf, count);
+	}
+	/* This is for the ALPHA version only, remove for later releases. */
+	if (ei_debug > 0) {			/* DMA termination address check... */
+	  int high = inb_p(nic_base + EN0_RSARHI);
+	  int low = inb_p(nic_base + EN0_RSARLO);
+	  int addr = (high << 8) + low;
+	  /* Check only the lower 8 bits so we can ignore ring wrap. */
+	  if (((ring_offset + xfer_count) & 0xff) != (addr & 0xff))
+		printk("%s: RX transfer address mismatch, %#4.4x vs. %#4.4x (actual).\n",
+			   dev->name, ring_offset + xfer_count, addr);
+	}
+	outb_p(saved_config & (~HP_DATAON), nic_base - NIC_OFFSET + HP_CONFIGURE);
+}
+
+static void
+hp_block_output(struct net_device *dev, int count,
+				const unsigned char *buf, int start_page)
+{
+	int nic_base = dev->base_addr;
+	int saved_config = inb_p(nic_base - NIC_OFFSET + HP_CONFIGURE);
+
+	outb_p(saved_config | HP_DATAON, nic_base - NIC_OFFSET + HP_CONFIGURE);
+	/* Round the count up for word writes.	Do we need to do this?
+	   What effect will an odd byte count have on the 8390?
+	   I should check someday. */
+	if (ei_status.word16 && (count & 0x01))
+	  count++;
+	/* We should already be in page 0, but to be safe... */
+	outb_p(E8390_PAGE0+E8390_START+E8390_NODMA, nic_base);
+
+#ifdef NE8390_RW_BUGFIX
+	/* Handle the read-before-write bug the same way as the
+	   Crynwr packet driver -- the NatSemi method doesn't work. */
+	outb_p(0x42, nic_base + EN0_RCNTLO);
+	outb_p(0,	nic_base + EN0_RCNTHI);
+	outb_p(0xff, nic_base + EN0_RSARLO);
+	outb_p(0x00, nic_base + EN0_RSARHI);
+#define NE_CMD	 	0x00
+	outb_p(E8390_RREAD+E8390_START, nic_base + NE_CMD);
+	/* Make certain that the dummy read has occurred. */
+	inb_p(0x61);
+	inb_p(0x61);
+#endif
+
+	outb_p(count & 0xff, nic_base + EN0_RCNTLO);
+	outb_p(count >> 8,	 nic_base + EN0_RCNTHI);
+	outb_p(0x00, nic_base + EN0_RSARLO);
+	outb_p(start_page, nic_base + EN0_RSARHI);
+
+	outb_p(E8390_RWRITE+E8390_START, nic_base);
+	if (ei_status.word16) {
+		/* Use the 'rep' sequence for 16 bit boards. */
+		outsw(nic_base - NIC_OFFSET + HP_DATAPORT, buf, count>>1);
+	} else {
+		outsb(nic_base - NIC_OFFSET + HP_DATAPORT, buf, count);
+	}
+
+	/* DON'T check for 'inb_p(EN0_ISR) & ENISR_RDC' here -- it's broken! */
+
+	/* This is for the ALPHA version only, remove for later releases. */
+	if (ei_debug > 0) {			/* DMA termination address check... */
+	  int high = inb_p(nic_base + EN0_RSARHI);
+	  int low  = inb_p(nic_base + EN0_RSARLO);
+	  int addr = (high << 8) + low;
+	  if ((start_page << 8) + count != addr)
+		printk("%s: TX Transfer address mismatch, %#4.4x vs. %#4.4x.\n",
+			   dev->name, (start_page << 8) + count, addr);
+	}
+	outb_p(saved_config & (~HP_DATAON), nic_base - NIC_OFFSET + HP_CONFIGURE);
+}
+
+/* This function resets the ethercard if something screws up. */
+static void __init
+hp_init_card(struct net_device *dev)
+{
+	int irq = dev->irq;
+	NS8390p_init(dev, 0);
+	outb_p(irqmap[irq&0x0f] | HP_RUN,
+		   dev->base_addr - NIC_OFFSET + HP_CONFIGURE);
+}
+
+#ifdef MODULE
+#define MAX_HP_CARDS	4	/* Max number of HP cards per module */
+static struct net_device *dev_hp[MAX_HP_CARDS];
+static int io[MAX_HP_CARDS];
+static int irq[MAX_HP_CARDS];
+
+module_param_array(io, int, NULL, 0);
+module_param_array(irq, int, NULL, 0);
+MODULE_PARM_DESC(io, "I/O base address(es)");
+MODULE_PARM_DESC(irq, "IRQ number(s) (assigned)");
+MODULE_DESCRIPTION("HP PC-LAN ISA ethernet driver");
+MODULE_LICENSE("GPL");
+
+/* This is set up so that only a single autoprobe takes place per call.
+ISA device autoprobes on a running machine are not recommended. */
+int __init
+init_module(void)
+{
+	struct net_device *dev;
+	int this_dev, found = 0;
+
+	for (this_dev = 0; this_dev < MAX_HP_CARDS; this_dev++) {
+		if (io[this_dev] == 0)  {
+			if (this_dev != 0) break; /* only autoprobe 1st one */
+			printk(KERN_NOTICE "hp.c: Presently autoprobing (not recommended) for a single card.\n");
+		}
+		dev = alloc_eip_netdev();
+		if (!dev)
+			break;
+		dev->irq = irq[this_dev];
+		dev->base_addr = io[this_dev];
+		if (do_hp_probe(dev) == 0) {
+			dev_hp[found++] = dev;
+			continue;
+		}
+		free_netdev(dev);
+		printk(KERN_WARNING "hp.c: No HP card found (i/o = 0x%x).\n", io[this_dev]);
+		break;
+	}
+	if (found)
+		return 0;
+	return -ENXIO;
+}
+
+static void cleanup_card(struct net_device *dev)
+{
+	free_irq(dev->irq, dev);
+	release_region(dev->base_addr - NIC_OFFSET, HP_IO_EXTENT);
+}
+
+void __exit
+cleanup_module(void)
+{
+	int this_dev;
+
+	for (this_dev = 0; this_dev < MAX_HP_CARDS; this_dev++) {
+		struct net_device *dev = dev_hp[this_dev];
+		if (dev) {
+			unregister_netdev(dev);
+			cleanup_card(dev);
+			free_netdev(dev);
+		}
+	}
+}
+#endif /* MODULE */
diff --git a/drivers/net/ethernet/8390/hydra.c b/drivers/net/ethernet/8390/hydra.c
new file mode 100644
index 0000000..1cd481c
--- /dev/null
+++ b/drivers/net/ethernet/8390/hydra.c
@@ -0,0 +1,273 @@
+/* New Hydra driver using generic 8390 core */
+/* Based on old hydra driver by Topi Kanerva (topi@susanna.oulu.fi) */
+
+/* This file is subject to the terms and conditions of the GNU General      */
+/* Public License.  See the file COPYING in the main directory of the       */
+/* Linux distribution for more details.                                     */
+
+/* Peter De Schrijver (p2@mind.be) */
+/* Oldenburg 2000 */
+
+/* The Amiganet is a Zorro-II board made by Hydra Systems. It contains a    */
+/* NS8390 NIC (network interface controller) clone, 16 or 64K on-board RAM  */
+/* and 10BASE-2 (thin coax) and AUI connectors.                             */
+
+#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/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/init.h>
+#include <linux/bitops.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/amigaints.h>
+#include <asm/amigahw.h>
+#include <linux/zorro.h>
+
+#define EI_SHIFT(x)	(ei_local->reg_offset[x])
+#define ei_inb(port)   in_8(port)
+#define ei_outb(val,port)  out_8(port,val)
+#define ei_inb_p(port)   in_8(port)
+#define ei_outb_p(val,port)  out_8(port,val)
+
+static const char version[] =
+    "8390.c:v1.10cvs 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
+
+#include "lib8390.c"
+
+#define NE_EN0_DCFG     (0x0e*2)
+
+#define NESM_START_PG   0x0    /* First page of TX buffer */
+#define NESM_STOP_PG    0x40    /* Last page +1 of RX ring */
+
+#define HYDRA_NIC_BASE 0xffe1
+#define HYDRA_ADDRPROM 0xffc0
+#define HYDRA_VERSION "v3.0alpha"
+
+#define WORDSWAP(a)     ((((a)>>8)&0xff) | ((a)<<8))
+
+
+static int __devinit hydra_init_one(struct zorro_dev *z,
+				    const struct zorro_device_id *ent);
+static int __devinit hydra_init(struct zorro_dev *z);
+static int hydra_open(struct net_device *dev);
+static int hydra_close(struct net_device *dev);
+static void hydra_reset_8390(struct net_device *dev);
+static void hydra_get_8390_hdr(struct net_device *dev,
+			       struct e8390_pkt_hdr *hdr, int ring_page);
+static void hydra_block_input(struct net_device *dev, int count,
+			      struct sk_buff *skb, int ring_offset);
+static void hydra_block_output(struct net_device *dev, int count,
+			       const unsigned char *buf, int start_page);
+static void __devexit hydra_remove_one(struct zorro_dev *z);
+
+static struct zorro_device_id hydra_zorro_tbl[] __devinitdata = {
+    { ZORRO_PROD_HYDRA_SYSTEMS_AMIGANET },
+    { 0 }
+};
+MODULE_DEVICE_TABLE(zorro, hydra_zorro_tbl);
+
+static struct zorro_driver hydra_driver = {
+    .name	= "hydra",
+    .id_table	= hydra_zorro_tbl,
+    .probe	= hydra_init_one,
+    .remove	= __devexit_p(hydra_remove_one),
+};
+
+static int __devinit hydra_init_one(struct zorro_dev *z,
+				    const struct zorro_device_id *ent)
+{
+    int err;
+
+    if (!request_mem_region(z->resource.start, 0x10000, "Hydra"))
+	return -EBUSY;
+    if ((err = hydra_init(z))) {
+	release_mem_region(z->resource.start, 0x10000);
+	return -EBUSY;
+    }
+    return 0;
+}
+
+static const struct net_device_ops hydra_netdev_ops = {
+	.ndo_open		= hydra_open,
+	.ndo_stop		= hydra_close,
+
+	.ndo_start_xmit		= __ei_start_xmit,
+	.ndo_tx_timeout		= __ei_tx_timeout,
+	.ndo_get_stats		= __ei_get_stats,
+	.ndo_set_multicast_list = __ei_set_multicast_list,
+	.ndo_validate_addr	= eth_validate_addr,
+	.ndo_set_mac_address	= eth_mac_addr,
+	.ndo_change_mtu		= eth_change_mtu,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	.ndo_poll_controller	= __ei_poll,
+#endif
+};
+
+static int __devinit hydra_init(struct zorro_dev *z)
+{
+    struct net_device *dev;
+    unsigned long board = ZTWO_VADDR(z->resource.start);
+    unsigned long ioaddr = board+HYDRA_NIC_BASE;
+    const char name[] = "NE2000";
+    int start_page, stop_page;
+    int j;
+    int err;
+
+    static u32 hydra_offsets[16] = {
+	0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e,
+	0x10, 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e,
+    };
+
+    dev = ____alloc_ei_netdev(0);
+    if (!dev)
+	return -ENOMEM;
+
+    for(j = 0; j < ETHER_ADDR_LEN; j++)
+	dev->dev_addr[j] = *((u8 *)(board + HYDRA_ADDRPROM + 2*j));
+
+    /* We must set the 8390 for word mode. */
+    z_writeb(0x4b, ioaddr + NE_EN0_DCFG);
+    start_page = NESM_START_PG;
+    stop_page = NESM_STOP_PG;
+
+    dev->base_addr = ioaddr;
+    dev->irq = IRQ_AMIGA_PORTS;
+
+    /* Install the Interrupt handler */
+    if (request_irq(IRQ_AMIGA_PORTS, __ei_interrupt, IRQF_SHARED, "Hydra Ethernet",
+		    dev)) {
+	free_netdev(dev);
+	return -EAGAIN;
+    }
+
+    ei_status.name = name;
+    ei_status.tx_start_page = start_page;
+    ei_status.stop_page = stop_page;
+    ei_status.word16 = 1;
+    ei_status.bigendian = 1;
+
+    ei_status.rx_start_page = start_page + TX_PAGES;
+
+    ei_status.reset_8390 = hydra_reset_8390;
+    ei_status.block_input = hydra_block_input;
+    ei_status.block_output = hydra_block_output;
+    ei_status.get_8390_hdr = hydra_get_8390_hdr;
+    ei_status.reg_offset = hydra_offsets;
+
+    dev->netdev_ops = &hydra_netdev_ops;
+    __NS8390_init(dev, 0);
+
+    err = register_netdev(dev);
+    if (err) {
+	free_irq(IRQ_AMIGA_PORTS, dev);
+	free_netdev(dev);
+	return err;
+    }
+
+    zorro_set_drvdata(z, dev);
+
+    pr_info("%s: Hydra at %pR, address %pM (hydra.c " HYDRA_VERSION ")\n",
+	    dev->name, &z->resource, dev->dev_addr);
+
+    return 0;
+}
+
+static int hydra_open(struct net_device *dev)
+{
+    __ei_open(dev);
+    return 0;
+}
+
+static int hydra_close(struct net_device *dev)
+{
+    if (ei_debug > 1)
+	printk(KERN_DEBUG "%s: Shutting down ethercard.\n", dev->name);
+    __ei_close(dev);
+    return 0;
+}
+
+static void hydra_reset_8390(struct net_device *dev)
+{
+    printk(KERN_INFO "Hydra hw reset not there\n");
+}
+
+static void hydra_get_8390_hdr(struct net_device *dev,
+			       struct e8390_pkt_hdr *hdr, int ring_page)
+{
+    int nic_base = dev->base_addr;
+    short *ptrs;
+    unsigned long hdr_start= (nic_base-HYDRA_NIC_BASE) +
+			     ((ring_page - NESM_START_PG)<<8);
+    ptrs = (short *)hdr;
+
+    *(ptrs++) = z_readw(hdr_start);
+    *((short *)hdr) = WORDSWAP(*((short *)hdr));
+    hdr_start += 2;
+    *(ptrs++) = z_readw(hdr_start);
+    *((short *)hdr+1) = WORDSWAP(*((short *)hdr+1));
+}
+
+static void hydra_block_input(struct net_device *dev, int count,
+			      struct sk_buff *skb, int ring_offset)
+{
+    unsigned long nic_base = dev->base_addr;
+    unsigned long mem_base = nic_base - HYDRA_NIC_BASE;
+    unsigned long xfer_start = mem_base + ring_offset - (NESM_START_PG<<8);
+
+    if (count&1)
+	count++;
+
+    if (xfer_start+count >  mem_base + (NESM_STOP_PG<<8)) {
+	int semi_count = (mem_base + (NESM_STOP_PG<<8)) - xfer_start;
+
+	z_memcpy_fromio(skb->data,xfer_start,semi_count);
+	count -= semi_count;
+	z_memcpy_fromio(skb->data+semi_count, mem_base, count);
+    } else
+	z_memcpy_fromio(skb->data, xfer_start,count);
+
+}
+
+static void hydra_block_output(struct net_device *dev, int count,
+			       const unsigned char *buf, int start_page)
+{
+    unsigned long nic_base = dev->base_addr;
+    unsigned long mem_base = nic_base - HYDRA_NIC_BASE;
+
+    if (count&1)
+	count++;
+
+    z_memcpy_toio(mem_base+((start_page - NESM_START_PG)<<8), buf, count);
+}
+
+static void __devexit hydra_remove_one(struct zorro_dev *z)
+{
+    struct net_device *dev = zorro_get_drvdata(z);
+
+    unregister_netdev(dev);
+    free_irq(IRQ_AMIGA_PORTS, dev);
+    release_mem_region(ZTWO_PADDR(dev->base_addr)-HYDRA_NIC_BASE, 0x10000);
+    free_netdev(dev);
+}
+
+static int __init hydra_init_module(void)
+{
+    return zorro_register_driver(&hydra_driver);
+}
+
+static void __exit hydra_cleanup_module(void)
+{
+    zorro_unregister_driver(&hydra_driver);
+}
+
+module_init(hydra_init_module);
+module_exit(hydra_cleanup_module);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/ethernet/8390/lib8390.c b/drivers/net/ethernet/8390/lib8390.c
new file mode 100644
index 0000000..05ae214
--- /dev/null
+++ b/drivers/net/ethernet/8390/lib8390.c
@@ -0,0 +1,1080 @@
+/* 8390.c: A general NS8390 ethernet driver core for linux. */
+/*
+	Written 1992-94 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
+
+
+  This is the chip-specific code for many 8390-based ethernet adaptors.
+  This is not a complete driver, it must be combined with board-specific
+  code such as ne.c, wd.c, 3c503.c, etc.
+
+  Seeing how at least eight drivers use this code, (not counting the
+  PCMCIA ones either) it is easy to break some card by what seems like
+  a simple innocent change. Please contact me or Donald if you think
+  you have found something that needs changing. -- PG
+
+
+  Changelog:
+
+  Paul Gortmaker	: remove set_bit lock, other cleanups.
+  Paul Gortmaker	: add ei_get_8390_hdr() so we can pass skb's to
+			  ei_block_input() for eth_io_copy_and_sum().
+  Paul Gortmaker	: exchange static int ei_pingpong for a #define,
+			  also add better Tx error handling.
+  Paul Gortmaker	: rewrite Rx overrun handling as per NS specs.
+  Alexey Kuznetsov	: use the 8390's six bit hash multicast filter.
+  Paul Gortmaker	: tweak ANK's above multicast changes a bit.
+  Paul Gortmaker	: update packet statistics for v2.1.x
+  Alan Cox		: support arbitrary stupid port mappings on the
+			  68K Macintosh. Support >16bit I/O spaces
+  Paul Gortmaker	: add kmod support for auto-loading of the 8390
+			  module by all drivers that require it.
+  Alan Cox		: Spinlocking work, added 'BUG_83C690'
+  Paul Gortmaker	: Separate out Tx timeout code from Tx path.
+  Paul Gortmaker	: Remove old unused single Tx buffer code.
+  Hayato Fujiwara	: Add m32r support.
+  Paul Gortmaker	: use skb_padto() instead of stack scratch area
+
+  Sources:
+  The National Semiconductor LAN Databook, and the 3Com 3c503 databook.
+
+  */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/jiffies.h>
+#include <linux/fs.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/bitops.h>
+#include <asm/system.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+#include <asm/irq.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/fcntl.h>
+#include <linux/in.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/crc32.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+
+#define NS8390_CORE
+#include "8390.h"
+
+#define BUG_83C690
+
+/* These are the operational function interfaces to board-specific
+   routines.
+	void reset_8390(struct net_device *dev)
+		Resets the board associated with DEV, including a hardware reset of
+		the 8390.  This is only called when there is a transmit timeout, and
+		it is always followed by 8390_init().
+	void block_output(struct net_device *dev, int count, const unsigned char *buf,
+					  int start_page)
+		Write the COUNT bytes of BUF to the packet buffer at START_PAGE.  The
+		"page" value uses the 8390's 256-byte pages.
+	void get_8390_hdr(struct net_device *dev, struct e8390_hdr *hdr, int ring_page)
+		Read the 4 byte, page aligned 8390 header. *If* there is a
+		subsequent read, it will be of the rest of the packet.
+	void block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset)
+		Read COUNT bytes from the packet buffer into the skb data area. Start
+		reading from RING_OFFSET, the address as the 8390 sees it.  This will always
+		follow the read of the 8390 header.
+*/
+#define ei_reset_8390 (ei_local->reset_8390)
+#define ei_block_output (ei_local->block_output)
+#define ei_block_input (ei_local->block_input)
+#define ei_get_8390_hdr (ei_local->get_8390_hdr)
+
+/* use 0 for production, 1 for verification, >2 for debug */
+#ifndef ei_debug
+int ei_debug = 1;
+#endif
+
+/* Index to functions. */
+static void ei_tx_intr(struct net_device *dev);
+static void ei_tx_err(struct net_device *dev);
+static void ei_receive(struct net_device *dev);
+static void ei_rx_overrun(struct net_device *dev);
+
+/* Routines generic to NS8390-based boards. */
+static void NS8390_trigger_send(struct net_device *dev, unsigned int length,
+								int start_page);
+static void do_set_multicast_list(struct net_device *dev);
+static void __NS8390_init(struct net_device *dev, int startp);
+
+/*
+ *	SMP and the 8390 setup.
+ *
+ *	The 8390 isn't exactly designed to be multithreaded on RX/TX. There is
+ *	a page register that controls bank and packet buffer access. We guard
+ *	this with ei_local->page_lock. Nobody should assume or set the page other
+ *	than zero when the lock is not held. Lock holders must restore page 0
+ *	before unlocking. Even pure readers must take the lock to protect in
+ *	page 0.
+ *
+ *	To make life difficult the chip can also be very slow. We therefore can't
+ *	just use spinlocks. For the longer lockups we disable the irq the device
+ *	sits on and hold the lock. We must hold the lock because there is a dual
+ *	processor case other than interrupts (get stats/set multicast list in
+ *	parallel with each other and transmit).
+ *
+ *	Note: in theory we can just disable the irq on the card _but_ there is
+ *	a latency on SMP irq delivery. So we can easily go "disable irq" "sync irqs"
+ *	enter lock, take the queued irq. So we waddle instead of flying.
+ *
+ *	Finally by special arrangement for the purpose of being generally
+ *	annoying the transmit function is called bh atomic. That places
+ *	restrictions on the user context callers as disable_irq won't save
+ *	them.
+ *
+ *	Additional explanation of problems with locking by Alan Cox:
+ *
+ *	"The author (me) didn't use spin_lock_irqsave because the slowness of the
+ *	card means that approach caused horrible problems like losing serial data
+ *	at 38400 baud on some chips. Remember many 8390 nics on PCI were ISA
+ *	chips with FPGA front ends.
+ *
+ *	Ok the logic behind the 8390 is very simple:
+ *
+ *	Things to know
+ *		- IRQ delivery is asynchronous to the PCI bus
+ *		- Blocking the local CPU IRQ via spin locks was too slow
+ *		- The chip has register windows needing locking work
+ *
+ *	So the path was once (I say once as people appear to have changed it
+ *	in the mean time and it now looks rather bogus if the changes to use
+ *	disable_irq_nosync_irqsave are disabling the local IRQ)
+ *
+ *
+ *		Take the page lock
+ *		Mask the IRQ on chip
+ *		Disable the IRQ (but not mask locally- someone seems to have
+ *			broken this with the lock validator stuff)
+ *			[This must be _nosync as the page lock may otherwise
+ *				deadlock us]
+ *		Drop the page lock and turn IRQs back on
+ *
+ *		At this point an existing IRQ may still be running but we can't
+ *		get a new one
+ *
+ *		Take the lock (so we know the IRQ has terminated) but don't mask
+ *	the IRQs on the processor
+ *		Set irqlock [for debug]
+ *
+ *		Transmit (slow as ****)
+ *
+ *		re-enable the IRQ
+ *
+ *
+ *	We have to use disable_irq because otherwise you will get delayed
+ *	interrupts on the APIC bus deadlocking the transmit path.
+ *
+ *	Quite hairy but the chip simply wasn't designed for SMP and you can't
+ *	even ACK an interrupt without risking corrupting other parallel
+ *	activities on the chip." [lkml, 25 Jul 2007]
+ */
+
+
+
+/**
+ * ei_open - Open/initialize the board.
+ * @dev: network device to initialize
+ *
+ * This routine goes all-out, setting everything
+ * up anew at each open, even though many of these registers should only
+ * need to be set once at boot.
+ */
+static int __ei_open(struct net_device *dev)
+{
+	unsigned long flags;
+	struct ei_device *ei_local = netdev_priv(dev);
+
+	if (dev->watchdog_timeo <= 0)
+		dev->watchdog_timeo = TX_TIMEOUT;
+
+	/*
+	 *	Grab the page lock so we own the register set, then call
+	 *	the init function.
+	 */
+
+	spin_lock_irqsave(&ei_local->page_lock, flags);
+	__NS8390_init(dev, 1);
+	/* Set the flag before we drop the lock, That way the IRQ arrives
+	   after its set and we get no silly warnings */
+	netif_start_queue(dev);
+	spin_unlock_irqrestore(&ei_local->page_lock, flags);
+	ei_local->irqlock = 0;
+	return 0;
+}
+
+/**
+ * ei_close - shut down network device
+ * @dev: network device to close
+ *
+ * Opposite of ei_open(). Only used when "ifconfig <devname> down" is done.
+ */
+static int __ei_close(struct net_device *dev)
+{
+	struct ei_device *ei_local = netdev_priv(dev);
+	unsigned long flags;
+
+	/*
+	 *	Hold the page lock during close
+	 */
+
+	spin_lock_irqsave(&ei_local->page_lock, flags);
+	__NS8390_init(dev, 0);
+	spin_unlock_irqrestore(&ei_local->page_lock, flags);
+	netif_stop_queue(dev);
+	return 0;
+}
+
+/**
+ * ei_tx_timeout - handle transmit time out condition
+ * @dev: network device which has apparently fallen asleep
+ *
+ * Called by kernel when device never acknowledges a transmit has
+ * completed (or failed) - i.e. never posted a Tx related interrupt.
+ */
+
+static void __ei_tx_timeout(struct net_device *dev)
+{
+	unsigned long e8390_base = dev->base_addr;
+	struct ei_device *ei_local = netdev_priv(dev);
+	int txsr, isr, tickssofar = jiffies - dev_trans_start(dev);
+	unsigned long flags;
+
+	dev->stats.tx_errors++;
+
+	spin_lock_irqsave(&ei_local->page_lock, flags);
+	txsr = ei_inb(e8390_base+EN0_TSR);
+	isr = ei_inb(e8390_base+EN0_ISR);
+	spin_unlock_irqrestore(&ei_local->page_lock, flags);
+
+	netdev_dbg(dev, "Tx timed out, %s TSR=%#2x, ISR=%#2x, t=%d\n",
+		   (txsr & ENTSR_ABT) ? "excess collisions." :
+		   (isr) ? "lost interrupt?" : "cable problem?",
+		   txsr, isr, tickssofar);
+
+	if (!isr && !dev->stats.tx_packets) {
+		/* The 8390 probably hasn't gotten on the cable yet. */
+		ei_local->interface_num ^= 1;   /* Try a different xcvr.  */
+	}
+
+	/* Ugly but a reset can be slow, yet must be protected */
+
+	disable_irq_nosync_lockdep(dev->irq);
+	spin_lock(&ei_local->page_lock);
+
+	/* Try to restart the card.  Perhaps the user has fixed something. */
+	ei_reset_8390(dev);
+	__NS8390_init(dev, 1);
+
+	spin_unlock(&ei_local->page_lock);
+	enable_irq_lockdep(dev->irq);
+	netif_wake_queue(dev);
+}
+
+/**
+ * ei_start_xmit - begin packet transmission
+ * @skb: packet to be sent
+ * @dev: network device to which packet is sent
+ *
+ * Sends a packet to an 8390 network device.
+ */
+
+static netdev_tx_t __ei_start_xmit(struct sk_buff *skb,
+				   struct net_device *dev)
+{
+	unsigned long e8390_base = dev->base_addr;
+	struct ei_device *ei_local = netdev_priv(dev);
+	int send_length = skb->len, output_page;
+	unsigned long flags;
+	char buf[ETH_ZLEN];
+	char *data = skb->data;
+
+	if (skb->len < ETH_ZLEN) {
+		memset(buf, 0, ETH_ZLEN);	/* more efficient than doing just the needed bits */
+		memcpy(buf, data, skb->len);
+		send_length = ETH_ZLEN;
+		data = buf;
+	}
+
+	/* Mask interrupts from the ethercard.
+	   SMP: We have to grab the lock here otherwise the IRQ handler
+	   on another CPU can flip window and race the IRQ mask set. We end
+	   up trashing the mcast filter not disabling irqs if we don't lock */
+
+	spin_lock_irqsave(&ei_local->page_lock, flags);
+	ei_outb_p(0x00, e8390_base + EN0_IMR);
+	spin_unlock_irqrestore(&ei_local->page_lock, flags);
+
+
+	/*
+	 *	Slow phase with lock held.
+	 */
+
+	disable_irq_nosync_lockdep_irqsave(dev->irq, &flags);
+
+	spin_lock(&ei_local->page_lock);
+
+	ei_local->irqlock = 1;
+
+	/*
+	 * We have two Tx slots available for use. Find the first free
+	 * slot, and then perform some sanity checks. With two Tx bufs,
+	 * you get very close to transmitting back-to-back packets. With
+	 * only one Tx buf, the transmitter sits idle while you reload the
+	 * card, leaving a substantial gap between each transmitted packet.
+	 */
+
+	if (ei_local->tx1 == 0) {
+		output_page = ei_local->tx_start_page;
+		ei_local->tx1 = send_length;
+		if (ei_debug  &&  ei_local->tx2 > 0)
+			netdev_dbg(dev, "idle transmitter tx2=%d, lasttx=%d, txing=%d\n",
+				   ei_local->tx2, ei_local->lasttx, ei_local->txing);
+	} else if (ei_local->tx2 == 0) {
+		output_page = ei_local->tx_start_page + TX_PAGES/2;
+		ei_local->tx2 = send_length;
+		if (ei_debug  &&  ei_local->tx1 > 0)
+			netdev_dbg(dev, "idle transmitter, tx1=%d, lasttx=%d, txing=%d\n",
+				   ei_local->tx1, ei_local->lasttx, ei_local->txing);
+	} else {			/* We should never get here. */
+		if (ei_debug)
+			netdev_dbg(dev, "No Tx buffers free! tx1=%d tx2=%d last=%d\n",
+				   ei_local->tx1, ei_local->tx2, ei_local->lasttx);
+		ei_local->irqlock = 0;
+		netif_stop_queue(dev);
+		ei_outb_p(ENISR_ALL, e8390_base + EN0_IMR);
+		spin_unlock(&ei_local->page_lock);
+		enable_irq_lockdep_irqrestore(dev->irq, &flags);
+		dev->stats.tx_errors++;
+		return NETDEV_TX_BUSY;
+	}
+
+	/*
+	 * Okay, now upload the packet and trigger a send if the transmitter
+	 * isn't already sending. If it is busy, the interrupt handler will
+	 * trigger the send later, upon receiving a Tx done interrupt.
+	 */
+
+	ei_block_output(dev, send_length, data, output_page);
+
+	if (!ei_local->txing) {
+		ei_local->txing = 1;
+		NS8390_trigger_send(dev, send_length, output_page);
+		if (output_page == ei_local->tx_start_page) {
+			ei_local->tx1 = -1;
+			ei_local->lasttx = -1;
+		} else {
+			ei_local->tx2 = -1;
+			ei_local->lasttx = -2;
+		}
+	} else
+		ei_local->txqueue++;
+
+	if (ei_local->tx1  &&  ei_local->tx2)
+		netif_stop_queue(dev);
+	else
+		netif_start_queue(dev);
+
+	/* Turn 8390 interrupts back on. */
+	ei_local->irqlock = 0;
+	ei_outb_p(ENISR_ALL, e8390_base + EN0_IMR);
+
+	spin_unlock(&ei_local->page_lock);
+	enable_irq_lockdep_irqrestore(dev->irq, &flags);
+	skb_tx_timestamp(skb);
+	dev_kfree_skb(skb);
+	dev->stats.tx_bytes += send_length;
+
+	return NETDEV_TX_OK;
+}
+
+/**
+ * ei_interrupt - handle the interrupts from an 8390
+ * @irq: interrupt number
+ * @dev_id: a pointer to the net_device
+ *
+ * Handle the ether interface interrupts. We pull packets from
+ * the 8390 via the card specific functions and fire them at the networking
+ * stack. We also handle transmit completions and wake the transmit path if
+ * necessary. We also update the counters and do other housekeeping as
+ * needed.
+ */
+
+static irqreturn_t __ei_interrupt(int irq, void *dev_id)
+{
+	struct net_device *dev = dev_id;
+	unsigned long e8390_base = dev->base_addr;
+	int interrupts, nr_serviced = 0;
+	struct ei_device *ei_local = netdev_priv(dev);
+
+	/*
+	 *	Protect the irq test too.
+	 */
+
+	spin_lock(&ei_local->page_lock);
+
+	if (ei_local->irqlock) {
+		/*
+		 * This might just be an interrupt for a PCI device sharing
+		 * this line
+		 */
+		netdev_err(dev, "Interrupted while interrupts are masked! isr=%#2x imr=%#2x\n",
+			   ei_inb_p(e8390_base + EN0_ISR),
+			   ei_inb_p(e8390_base + EN0_IMR));
+		spin_unlock(&ei_local->page_lock);
+		return IRQ_NONE;
+	}
+
+	/* Change to page 0 and read the intr status reg. */
+	ei_outb_p(E8390_NODMA+E8390_PAGE0, e8390_base + E8390_CMD);
+	if (ei_debug > 3)
+		netdev_dbg(dev, "interrupt(isr=%#2.2x)\n",
+			   ei_inb_p(e8390_base + EN0_ISR));
+
+	/* !!Assumption!! -- we stay in page 0.	 Don't break this. */
+	while ((interrupts = ei_inb_p(e8390_base + EN0_ISR)) != 0 &&
+	       ++nr_serviced < MAX_SERVICE) {
+		if (!netif_running(dev)) {
+			netdev_warn(dev, "interrupt from stopped card\n");
+			/* rmk - acknowledge the interrupts */
+			ei_outb_p(interrupts, e8390_base + EN0_ISR);
+			interrupts = 0;
+			break;
+		}
+		if (interrupts & ENISR_OVER)
+			ei_rx_overrun(dev);
+		else if (interrupts & (ENISR_RX+ENISR_RX_ERR)) {
+			/* Got a good (?) packet. */
+			ei_receive(dev);
+		}
+		/* Push the next to-transmit packet through. */
+		if (interrupts & ENISR_TX)
+			ei_tx_intr(dev);
+		else if (interrupts & ENISR_TX_ERR)
+			ei_tx_err(dev);
+
+		if (interrupts & ENISR_COUNTERS) {
+			dev->stats.rx_frame_errors += ei_inb_p(e8390_base + EN0_COUNTER0);
+			dev->stats.rx_crc_errors   += ei_inb_p(e8390_base + EN0_COUNTER1);
+			dev->stats.rx_missed_errors += ei_inb_p(e8390_base + EN0_COUNTER2);
+			ei_outb_p(ENISR_COUNTERS, e8390_base + EN0_ISR); /* Ack intr. */
+		}
+
+		/* Ignore any RDC interrupts that make it back to here. */
+		if (interrupts & ENISR_RDC)
+			ei_outb_p(ENISR_RDC, e8390_base + EN0_ISR);
+
+		ei_outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, e8390_base + E8390_CMD);
+	}
+
+	if (interrupts && ei_debug) {
+		ei_outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, e8390_base + E8390_CMD);
+		if (nr_serviced >= MAX_SERVICE) {
+			/* 0xFF is valid for a card removal */
+			if (interrupts != 0xFF)
+				netdev_warn(dev, "Too much work at interrupt, status %#2.2x\n",
+					    interrupts);
+			ei_outb_p(ENISR_ALL, e8390_base + EN0_ISR); /* Ack. most intrs. */
+		} else {
+			netdev_warn(dev, "unknown interrupt %#2x\n", interrupts);
+			ei_outb_p(0xff, e8390_base + EN0_ISR); /* Ack. all intrs. */
+		}
+	}
+	spin_unlock(&ei_local->page_lock);
+	return IRQ_RETVAL(nr_serviced > 0);
+}
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void __ei_poll(struct net_device *dev)
+{
+	disable_irq(dev->irq);
+	__ei_interrupt(dev->irq, dev);
+	enable_irq(dev->irq);
+}
+#endif
+
+/**
+ * ei_tx_err - handle transmitter error
+ * @dev: network device which threw the exception
+ *
+ * A transmitter error has happened. Most likely excess collisions (which
+ * is a fairly normal condition). If the error is one where the Tx will
+ * have been aborted, we try and send another one right away, instead of
+ * letting the failed packet sit and collect dust in the Tx buffer. This
+ * is a much better solution as it avoids kernel based Tx timeouts, and
+ * an unnecessary card reset.
+ *
+ * Called with lock held.
+ */
+
+static void ei_tx_err(struct net_device *dev)
+{
+	unsigned long e8390_base = dev->base_addr;
+	/* ei_local is used on some platforms via the EI_SHIFT macro */
+	struct ei_device *ei_local __maybe_unused = netdev_priv(dev);
+	unsigned char txsr = ei_inb_p(e8390_base+EN0_TSR);
+	unsigned char tx_was_aborted = txsr & (ENTSR_ABT+ENTSR_FU);
+
+#ifdef VERBOSE_ERROR_DUMP
+	netdev_dbg(dev, "transmitter error (%#2x):", txsr);
+	if (txsr & ENTSR_ABT)
+		pr_cont(" excess-collisions ");
+	if (txsr & ENTSR_ND)
+		pr_cont(" non-deferral ");
+	if (txsr & ENTSR_CRS)
+		pr_cont(" lost-carrier ");
+	if (txsr & ENTSR_FU)
+		pr_cont(" FIFO-underrun ");
+	if (txsr & ENTSR_CDH)
+		pr_cont(" lost-heartbeat ");
+	pr_cont("\n");
+#endif
+
+	ei_outb_p(ENISR_TX_ERR, e8390_base + EN0_ISR); /* Ack intr. */
+
+	if (tx_was_aborted)
+		ei_tx_intr(dev);
+	else {
+		dev->stats.tx_errors++;
+		if (txsr & ENTSR_CRS)
+			dev->stats.tx_carrier_errors++;
+		if (txsr & ENTSR_CDH)
+			dev->stats.tx_heartbeat_errors++;
+		if (txsr & ENTSR_OWC)
+			dev->stats.tx_window_errors++;
+	}
+}
+
+/**
+ * ei_tx_intr - transmit interrupt handler
+ * @dev: network device for which tx intr is handled
+ *
+ * We have finished a transmit: check for errors and then trigger the next
+ * packet to be sent. Called with lock held.
+ */
+
+static void ei_tx_intr(struct net_device *dev)
+{
+	unsigned long e8390_base = dev->base_addr;
+	struct ei_device *ei_local = netdev_priv(dev);
+	int status = ei_inb(e8390_base + EN0_TSR);
+
+	ei_outb_p(ENISR_TX, e8390_base + EN0_ISR); /* Ack intr. */
+
+	/*
+	 * There are two Tx buffers, see which one finished, and trigger
+	 * the send of another one if it exists.
+	 */
+	ei_local->txqueue--;
+
+	if (ei_local->tx1 < 0) {
+		if (ei_local->lasttx != 1 && ei_local->lasttx != -1)
+			pr_err("%s: bogus last_tx_buffer %d, tx1=%d\n",
+			       ei_local->name, ei_local->lasttx, ei_local->tx1);
+		ei_local->tx1 = 0;
+		if (ei_local->tx2 > 0) {
+			ei_local->txing = 1;
+			NS8390_trigger_send(dev, ei_local->tx2, ei_local->tx_start_page + 6);
+			dev->trans_start = jiffies;
+			ei_local->tx2 = -1,
+			ei_local->lasttx = 2;
+		} else
+			ei_local->lasttx = 20, ei_local->txing = 0;
+	} else if (ei_local->tx2 < 0) {
+		if (ei_local->lasttx != 2  &&  ei_local->lasttx != -2)
+			pr_err("%s: bogus last_tx_buffer %d, tx2=%d\n",
+			       ei_local->name, ei_local->lasttx, ei_local->tx2);
+		ei_local->tx2 = 0;
+		if (ei_local->tx1 > 0) {
+			ei_local->txing = 1;
+			NS8390_trigger_send(dev, ei_local->tx1, ei_local->tx_start_page);
+			dev->trans_start = jiffies;
+			ei_local->tx1 = -1;
+			ei_local->lasttx = 1;
+		} else
+			ei_local->lasttx = 10, ei_local->txing = 0;
+	} /* else
+		netdev_warn(dev, "unexpected TX-done interrupt, lasttx=%d\n",
+			    ei_local->lasttx);
+*/
+
+	/* Minimize Tx latency: update the statistics after we restart TXing. */
+	if (status & ENTSR_COL)
+		dev->stats.collisions++;
+	if (status & ENTSR_PTX)
+		dev->stats.tx_packets++;
+	else {
+		dev->stats.tx_errors++;
+		if (status & ENTSR_ABT) {
+			dev->stats.tx_aborted_errors++;
+			dev->stats.collisions += 16;
+		}
+		if (status & ENTSR_CRS)
+			dev->stats.tx_carrier_errors++;
+		if (status & ENTSR_FU)
+			dev->stats.tx_fifo_errors++;
+		if (status & ENTSR_CDH)
+			dev->stats.tx_heartbeat_errors++;
+		if (status & ENTSR_OWC)
+			dev->stats.tx_window_errors++;
+	}
+	netif_wake_queue(dev);
+}
+
+/**
+ * ei_receive - receive some packets
+ * @dev: network device with which receive will be run
+ *
+ * We have a good packet(s), get it/them out of the buffers.
+ * Called with lock held.
+ */
+
+static void ei_receive(struct net_device *dev)
+{
+	unsigned long e8390_base = dev->base_addr;
+	struct ei_device *ei_local = netdev_priv(dev);
+	unsigned char rxing_page, this_frame, next_frame;
+	unsigned short current_offset;
+	int rx_pkt_count = 0;
+	struct e8390_pkt_hdr rx_frame;
+	int num_rx_pages = ei_local->stop_page-ei_local->rx_start_page;
+
+	while (++rx_pkt_count < 10) {
+		int pkt_len, pkt_stat;
+
+		/* Get the rx page (incoming packet pointer). */
+		ei_outb_p(E8390_NODMA+E8390_PAGE1, e8390_base + E8390_CMD);
+		rxing_page = ei_inb_p(e8390_base + EN1_CURPAG);
+		ei_outb_p(E8390_NODMA+E8390_PAGE0, e8390_base + E8390_CMD);
+
+		/* Remove one frame from the ring.  Boundary is always a page behind. */
+		this_frame = ei_inb_p(e8390_base + EN0_BOUNDARY) + 1;
+		if (this_frame >= ei_local->stop_page)
+			this_frame = ei_local->rx_start_page;
+
+		/* Someday we'll omit the previous, iff we never get this message.
+		   (There is at least one clone claimed to have a problem.)
+
+		   Keep quiet if it looks like a card removal. One problem here
+		   is that some clones crash in roughly the same way.
+		 */
+		if (ei_debug > 0 &&
+		    this_frame != ei_local->current_page &&
+		    (this_frame != 0x0 || rxing_page != 0xFF))
+			netdev_err(dev, "mismatched read page pointers %2x vs %2x\n",
+				   this_frame, ei_local->current_page);
+
+		if (this_frame == rxing_page)	/* Read all the frames? */
+			break;				/* Done for now */
+
+		current_offset = this_frame << 8;
+		ei_get_8390_hdr(dev, &rx_frame, this_frame);
+
+		pkt_len = rx_frame.count - sizeof(struct e8390_pkt_hdr);
+		pkt_stat = rx_frame.status;
+
+		next_frame = this_frame + 1 + ((pkt_len+4)>>8);
+
+		/* Check for bogosity warned by 3c503 book: the status byte is never
+		   written.  This happened a lot during testing! This code should be
+		   cleaned up someday. */
+		if (rx_frame.next != next_frame &&
+		    rx_frame.next != next_frame + 1 &&
+		    rx_frame.next != next_frame - num_rx_pages &&
+		    rx_frame.next != next_frame + 1 - num_rx_pages) {
+			ei_local->current_page = rxing_page;
+			ei_outb(ei_local->current_page-1, e8390_base+EN0_BOUNDARY);
+			dev->stats.rx_errors++;
+			continue;
+		}
+
+		if (pkt_len < 60  ||  pkt_len > 1518) {
+			if (ei_debug)
+				netdev_dbg(dev, "bogus packet size: %d, status=%#2x nxpg=%#2x\n",
+					   rx_frame.count, rx_frame.status,
+					   rx_frame.next);
+			dev->stats.rx_errors++;
+			dev->stats.rx_length_errors++;
+		} else if ((pkt_stat & 0x0F) == ENRSR_RXOK) {
+			struct sk_buff *skb;
+
+			skb = dev_alloc_skb(pkt_len+2);
+			if (skb == NULL) {
+				if (ei_debug > 1)
+					netdev_dbg(dev, "Couldn't allocate a sk_buff of size %d\n",
+						   pkt_len);
+				dev->stats.rx_dropped++;
+				break;
+			} else {
+				skb_reserve(skb, 2);	/* IP headers on 16 byte boundaries */
+				skb_put(skb, pkt_len);	/* Make room */
+				ei_block_input(dev, pkt_len, skb, current_offset + sizeof(rx_frame));
+				skb->protocol = eth_type_trans(skb, dev);
+				if (!skb_defer_rx_timestamp(skb))
+					netif_rx(skb);
+				dev->stats.rx_packets++;
+				dev->stats.rx_bytes += pkt_len;
+				if (pkt_stat & ENRSR_PHY)
+					dev->stats.multicast++;
+			}
+		} else {
+			if (ei_debug)
+				netdev_dbg(dev, "bogus packet: status=%#2x nxpg=%#2x size=%d\n",
+					   rx_frame.status, rx_frame.next,
+					   rx_frame.count);
+			dev->stats.rx_errors++;
+			/* NB: The NIC counts CRC, frame and missed errors. */
+			if (pkt_stat & ENRSR_FO)
+				dev->stats.rx_fifo_errors++;
+		}
+		next_frame = rx_frame.next;
+
+		/* This _should_ never happen: it's here for avoiding bad clones. */
+		if (next_frame >= ei_local->stop_page) {
+			netdev_notice(dev, "next frame inconsistency, %#2x\n",
+				      next_frame);
+			next_frame = ei_local->rx_start_page;
+		}
+		ei_local->current_page = next_frame;
+		ei_outb_p(next_frame-1, e8390_base+EN0_BOUNDARY);
+	}
+
+	/* We used to also ack ENISR_OVER here, but that would sometimes mask
+	   a real overrun, leaving the 8390 in a stopped state with rec'vr off. */
+	ei_outb_p(ENISR_RX+ENISR_RX_ERR, e8390_base+EN0_ISR);
+}
+
+/**
+ * ei_rx_overrun - handle receiver overrun
+ * @dev: network device which threw exception
+ *
+ * We have a receiver overrun: we have to kick the 8390 to get it started
+ * again. Problem is that you have to kick it exactly as NS prescribes in
+ * the updated datasheets, or "the NIC may act in an unpredictable manner."
+ * This includes causing "the NIC to defer indefinitely when it is stopped
+ * on a busy network."  Ugh.
+ * Called with lock held. Don't call this with the interrupts off or your
+ * computer will hate you - it takes 10ms or so.
+ */
+
+static void ei_rx_overrun(struct net_device *dev)
+{
+	unsigned long e8390_base = dev->base_addr;
+	unsigned char was_txing, must_resend = 0;
+	/* ei_local is used on some platforms via the EI_SHIFT macro */
+	struct ei_device *ei_local __maybe_unused = netdev_priv(dev);
+
+	/*
+	 * Record whether a Tx was in progress and then issue the
+	 * stop command.
+	 */
+	was_txing = ei_inb_p(e8390_base+E8390_CMD) & E8390_TRANS;
+	ei_outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base+E8390_CMD);
+
+	if (ei_debug > 1)
+		netdev_dbg(dev, "Receiver overrun\n");
+	dev->stats.rx_over_errors++;
+
+	/*
+	 * Wait a full Tx time (1.2ms) + some guard time, NS says 1.6ms total.
+	 * Early datasheets said to poll the reset bit, but now they say that
+	 * it "is not a reliable indicator and subsequently should be ignored."
+	 * We wait at least 10ms.
+	 */
+
+	mdelay(10);
+
+	/*
+	 * Reset RBCR[01] back to zero as per magic incantation.
+	 */
+	ei_outb_p(0x00, e8390_base+EN0_RCNTLO);
+	ei_outb_p(0x00, e8390_base+EN0_RCNTHI);
+
+	/*
+	 * See if any Tx was interrupted or not. According to NS, this
+	 * step is vital, and skipping it will cause no end of havoc.
+	 */
+
+	if (was_txing) {
+		unsigned char tx_completed = ei_inb_p(e8390_base+EN0_ISR) & (ENISR_TX+ENISR_TX_ERR);
+		if (!tx_completed)
+			must_resend = 1;
+	}
+
+	/*
+	 * Have to enter loopback mode and then restart the NIC before
+	 * you are allowed to slurp packets up off the ring.
+	 */
+	ei_outb_p(E8390_TXOFF, e8390_base + EN0_TXCR);
+	ei_outb_p(E8390_NODMA + E8390_PAGE0 + E8390_START, e8390_base + E8390_CMD);
+
+	/*
+	 * Clear the Rx ring of all the debris, and ack the interrupt.
+	 */
+	ei_receive(dev);
+	ei_outb_p(ENISR_OVER, e8390_base+EN0_ISR);
+
+	/*
+	 * Leave loopback mode, and resend any packet that got stopped.
+	 */
+	ei_outb_p(E8390_TXCONFIG, e8390_base + EN0_TXCR);
+	if (must_resend)
+		ei_outb_p(E8390_NODMA + E8390_PAGE0 + E8390_START + E8390_TRANS, e8390_base + E8390_CMD);
+}
+
+/*
+ *	Collect the stats. This is called unlocked and from several contexts.
+ */
+
+static struct net_device_stats *__ei_get_stats(struct net_device *dev)
+{
+	unsigned long ioaddr = dev->base_addr;
+	struct ei_device *ei_local = netdev_priv(dev);
+	unsigned long flags;
+
+	/* If the card is stopped, just return the present stats. */
+	if (!netif_running(dev))
+		return &dev->stats;
+
+	spin_lock_irqsave(&ei_local->page_lock, flags);
+	/* Read the counter registers, assuming we are in page 0. */
+	dev->stats.rx_frame_errors  += ei_inb_p(ioaddr + EN0_COUNTER0);
+	dev->stats.rx_crc_errors    += ei_inb_p(ioaddr + EN0_COUNTER1);
+	dev->stats.rx_missed_errors += ei_inb_p(ioaddr + EN0_COUNTER2);
+	spin_unlock_irqrestore(&ei_local->page_lock, flags);
+
+	return &dev->stats;
+}
+
+/*
+ * Form the 64 bit 8390 multicast table from the linked list of addresses
+ * associated with this dev structure.
+ */
+
+static inline void make_mc_bits(u8 *bits, struct net_device *dev)
+{
+	struct netdev_hw_addr *ha;
+
+	netdev_for_each_mc_addr(ha, dev) {
+		u32 crc = ether_crc(ETH_ALEN, ha->addr);
+		/*
+		 * The 8390 uses the 6 most significant bits of the
+		 * CRC to index the multicast table.
+		 */
+		bits[crc>>29] |= (1<<((crc>>26)&7));
+	}
+}
+
+/**
+ * do_set_multicast_list - set/clear multicast filter
+ * @dev: net device for which multicast filter is adjusted
+ *
+ *	Set or clear the multicast filter for this adaptor. May be called
+ *	from a BH in 2.1.x. Must be called with lock held.
+ */
+
+static void do_set_multicast_list(struct net_device *dev)
+{
+	unsigned long e8390_base = dev->base_addr;
+	int i;
+	struct ei_device *ei_local = netdev_priv(dev);
+
+	if (!(dev->flags&(IFF_PROMISC|IFF_ALLMULTI))) {
+		memset(ei_local->mcfilter, 0, 8);
+		if (!netdev_mc_empty(dev))
+			make_mc_bits(ei_local->mcfilter, dev);
+	} else
+		memset(ei_local->mcfilter, 0xFF, 8);	/* mcast set to accept-all */
+
+	/*
+	 * DP8390 manuals don't specify any magic sequence for altering
+	 * the multicast regs on an already running card. To be safe, we
+	 * ensure multicast mode is off prior to loading up the new hash
+	 * table. If this proves to be not enough, we can always resort
+	 * to stopping the NIC, loading the table and then restarting.
+	 *
+	 * Bug Alert!  The MC regs on the SMC 83C690 (SMC Elite and SMC
+	 * Elite16) appear to be write-only. The NS 8390 data sheet lists
+	 * them as r/w so this is a bug.  The SMC 83C790 (SMC Ultra and
+	 * Ultra32 EISA) appears to have this bug fixed.
+	 */
+
+	if (netif_running(dev))
+		ei_outb_p(E8390_RXCONFIG, e8390_base + EN0_RXCR);
+	ei_outb_p(E8390_NODMA + E8390_PAGE1, e8390_base + E8390_CMD);
+	for (i = 0; i < 8; i++) {
+		ei_outb_p(ei_local->mcfilter[i], e8390_base + EN1_MULT_SHIFT(i));
+#ifndef BUG_83C690
+		if (ei_inb_p(e8390_base + EN1_MULT_SHIFT(i)) != ei_local->mcfilter[i])
+			netdev_err(dev, "Multicast filter read/write mismap %d\n",
+				   i);
+#endif
+	}
+	ei_outb_p(E8390_NODMA + E8390_PAGE0, e8390_base + E8390_CMD);
+
+	if (dev->flags&IFF_PROMISC)
+		ei_outb_p(E8390_RXCONFIG | 0x18, e8390_base + EN0_RXCR);
+	else if (dev->flags & IFF_ALLMULTI || !netdev_mc_empty(dev))
+		ei_outb_p(E8390_RXCONFIG | 0x08, e8390_base + EN0_RXCR);
+	else
+		ei_outb_p(E8390_RXCONFIG, e8390_base + EN0_RXCR);
+}
+
+/*
+ *	Called without lock held. This is invoked from user context and may
+ *	be parallel to just about everything else. Its also fairly quick and
+ *	not called too often. Must protect against both bh and irq users
+ */
+
+static void __ei_set_multicast_list(struct net_device *dev)
+{
+	unsigned long flags;
+	struct ei_device *ei_local = netdev_priv(dev);
+
+	spin_lock_irqsave(&ei_local->page_lock, flags);
+	do_set_multicast_list(dev);
+	spin_unlock_irqrestore(&ei_local->page_lock, flags);
+}
+
+/**
+ * ethdev_setup - init rest of 8390 device struct
+ * @dev: network device structure to init
+ *
+ * Initialize the rest of the 8390 device structure.  Do NOT __init
+ * this, as it is used by 8390 based modular drivers too.
+ */
+
+static void ethdev_setup(struct net_device *dev)
+{
+	struct ei_device *ei_local = netdev_priv(dev);
+	if (ei_debug > 1)
+		printk(version);
+
+	ether_setup(dev);
+
+	spin_lock_init(&ei_local->page_lock);
+}
+
+/**
+ * alloc_ei_netdev - alloc_etherdev counterpart for 8390
+ * @size: extra bytes to allocate
+ *
+ * Allocate 8390-specific net_device.
+ */
+static struct net_device *____alloc_ei_netdev(int size)
+{
+	return alloc_netdev(sizeof(struct ei_device) + size, "eth%d",
+				ethdev_setup);
+}
+
+
+
+
+/* This page of functions should be 8390 generic */
+/* Follow National Semi's recommendations for initializing the "NIC". */
+
+/**
+ * NS8390_init - initialize 8390 hardware
+ * @dev: network device to initialize
+ * @startp: boolean.  non-zero value to initiate chip processing
+ *
+ *	Must be called with lock held.
+ */
+
+static void __NS8390_init(struct net_device *dev, int startp)
+{
+	unsigned long e8390_base = dev->base_addr;
+	struct ei_device *ei_local = netdev_priv(dev);
+	int i;
+	int endcfg = ei_local->word16
+	    ? (0x48 | ENDCFG_WTS | (ei_local->bigendian ? ENDCFG_BOS : 0))
+	    : 0x48;
+
+	if (sizeof(struct e8390_pkt_hdr) != 4)
+		panic("8390.c: header struct mispacked\n");
+	/* Follow National Semi's recommendations for initing the DP83902. */
+	ei_outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base+E8390_CMD); /* 0x21 */
+	ei_outb_p(endcfg, e8390_base + EN0_DCFG);	/* 0x48 or 0x49 */
+	/* Clear the remote byte count registers. */
+	ei_outb_p(0x00,  e8390_base + EN0_RCNTLO);
+	ei_outb_p(0x00,  e8390_base + EN0_RCNTHI);
+	/* Set to monitor and loopback mode -- this is vital!. */
+	ei_outb_p(E8390_RXOFF, e8390_base + EN0_RXCR); /* 0x20 */
+	ei_outb_p(E8390_TXOFF, e8390_base + EN0_TXCR); /* 0x02 */
+	/* Set the transmit page and receive ring. */
+	ei_outb_p(ei_local->tx_start_page, e8390_base + EN0_TPSR);
+	ei_local->tx1 = ei_local->tx2 = 0;
+	ei_outb_p(ei_local->rx_start_page, e8390_base + EN0_STARTPG);
+	ei_outb_p(ei_local->stop_page-1, e8390_base + EN0_BOUNDARY);	/* 3c503 says 0x3f,NS0x26*/
+	ei_local->current_page = ei_local->rx_start_page;		/* assert boundary+1 */
+	ei_outb_p(ei_local->stop_page, e8390_base + EN0_STOPPG);
+	/* Clear the pending interrupts and mask. */
+	ei_outb_p(0xFF, e8390_base + EN0_ISR);
+	ei_outb_p(0x00,  e8390_base + EN0_IMR);
+
+	/* Copy the station address into the DS8390 registers. */
+
+	ei_outb_p(E8390_NODMA + E8390_PAGE1 + E8390_STOP, e8390_base+E8390_CMD); /* 0x61 */
+	for (i = 0; i < 6; i++) {
+		ei_outb_p(dev->dev_addr[i], e8390_base + EN1_PHYS_SHIFT(i));
+		if (ei_debug > 1 &&
+		    ei_inb_p(e8390_base + EN1_PHYS_SHIFT(i)) != dev->dev_addr[i])
+			netdev_err(dev, "Hw. address read/write mismap %d\n", i);
+	}
+
+	ei_outb_p(ei_local->rx_start_page, e8390_base + EN1_CURPAG);
+	ei_outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base+E8390_CMD);
+
+	ei_local->tx1 = ei_local->tx2 = 0;
+	ei_local->txing = 0;
+
+	if (startp) {
+		ei_outb_p(0xff,  e8390_base + EN0_ISR);
+		ei_outb_p(ENISR_ALL,  e8390_base + EN0_IMR);
+		ei_outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, e8390_base+E8390_CMD);
+		ei_outb_p(E8390_TXCONFIG, e8390_base + EN0_TXCR); /* xmit on. */
+		/* 3c503 TechMan says rxconfig only after the NIC is started. */
+		ei_outb_p(E8390_RXCONFIG, e8390_base + EN0_RXCR); /* rx on,  */
+		do_set_multicast_list(dev);	/* (re)load the mcast table */
+	}
+}
+
+/* Trigger a transmit start, assuming the length is valid.
+   Always called with the page lock held */
+
+static void NS8390_trigger_send(struct net_device *dev, unsigned int length,
+								int start_page)
+{
+	unsigned long e8390_base = dev->base_addr;
+	struct ei_device *ei_local __attribute((unused)) = netdev_priv(dev);
+
+	ei_outb_p(E8390_NODMA+E8390_PAGE0, e8390_base+E8390_CMD);
+
+	if (ei_inb_p(e8390_base + E8390_CMD) & E8390_TRANS) {
+		netdev_warn(dev, "trigger_send() called with the transmitter busy\n");
+		return;
+	}
+	ei_outb_p(length & 0xff, e8390_base + EN0_TCNTLO);
+	ei_outb_p(length >> 8, e8390_base + EN0_TCNTHI);
+	ei_outb_p(start_page, e8390_base + EN0_TPSR);
+	ei_outb_p(E8390_NODMA+E8390_TRANS+E8390_START, e8390_base+E8390_CMD);
+}
diff --git a/drivers/net/ethernet/8390/lne390.c b/drivers/net/ethernet/8390/lne390.c
new file mode 100644
index 0000000..f9888d2
--- /dev/null
+++ b/drivers/net/ethernet/8390/lne390.c
@@ -0,0 +1,434 @@
+/*
+	lne390.c
+
+	Linux driver for Mylex LNE390 EISA Network Adapter
+
+	Copyright (C) 1996-1998, Paul Gortmaker.
+
+	This software may be used and distributed according to the terms
+	of the GNU General Public License, incorporated herein by reference.
+
+	Information and Code Sources:
+
+	1) Based upon framework of es3210 driver.
+	2) The existing myriad of other Linux 8390 drivers by Donald Becker.
+	3) Russ Nelson's asm packet driver provided additional info.
+	4) Info for getting IRQ and sh-mem gleaned from the EISA cfg files.
+
+	The LNE390 is an EISA shared memory NS8390 implementation. Note
+	that all memory copies to/from the board must be 32bit transfers.
+	There are two versions of the card: the lne390a and the lne390b.
+	Going by the EISA cfg files, the "a" has jumpers to select between
+	BNC/AUI, but the "b" also has RJ-45 and selection is via the SCU.
+	The shared memory address selection is also slightly different.
+	Note that shared memory address > 1MB are supported with this driver.
+
+	You can try <http://www.mylex.com> if you want more info, as I've
+	never even seen one of these cards.  :)
+
+	Arnaldo Carvalho de Melo <acme@conectiva.com.br> - 2000/09/01
+	- get rid of check_region
+	- no need to check if dev == NULL in lne390_probe1
+*/
+
+static const char *version =
+	"lne390.c: Driver revision v0.99.1, 01/09/2000\n";
+
+#include <linux/module.h>
+#include <linux/eisa.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+
+#include <asm/io.h>
+#include <asm/system.h>
+
+#include "8390.h"
+
+#define DRV_NAME "lne390"
+
+static int lne390_probe1(struct net_device *dev, int ioaddr);
+
+static void lne390_reset_8390(struct net_device *dev);
+
+static void lne390_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page);
+static void lne390_block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset);
+static void lne390_block_output(struct net_device *dev, int count, const unsigned char *buf, const int start_page);
+
+#define LNE390_START_PG		0x00    /* First page of TX buffer	*/
+#define LNE390_STOP_PG		0x80    /* Last page +1 of RX ring	*/
+
+#define LNE390_ID_PORT		0xc80	/* Same for all EISA cards 	*/
+#define LNE390_IO_EXTENT	0x20
+#define LNE390_SA_PROM		0x16	/* Start of e'net addr.		*/
+#define LNE390_RESET_PORT	0xc84	/* From the pkt driver source	*/
+#define LNE390_NIC_OFFSET	0x00	/* Hello, the 8390 is *here*	*/
+
+#define LNE390_ADDR0		0x00	/* 3 byte vendor prefix		*/
+#define LNE390_ADDR1		0x80
+#define LNE390_ADDR2		0xe5
+
+#define LNE390_ID0	0x10009835	/* 0x3598 = 01101 01100 11000 = mlx */
+#define LNE390_ID1	0x11009835	/* above is the 390A, this is 390B  */
+
+#define LNE390_CFG1		0xc84	/* NB: 0xc84 is also "reset" port. */
+#define LNE390_CFG2		0xc90
+
+/*
+ *	You can OR any of the following bits together and assign it
+ *	to LNE390_DEBUG to get verbose driver info during operation.
+ *	Currently only the probe one is implemented.
+ */
+
+#define LNE390_D_PROBE	0x01
+#define LNE390_D_RX_PKT	0x02
+#define LNE390_D_TX_PKT	0x04
+#define LNE390_D_IRQ	0x08
+
+#define LNE390_DEBUG	0
+
+static unsigned char irq_map[] __initdata = {15, 12, 11, 10, 9, 7, 5, 3};
+static unsigned int shmem_mapA[] __initdata = {0xff, 0xfe, 0xfd, 0xfff, 0xffe, 0xffc, 0x0d, 0x0};
+static unsigned int shmem_mapB[] __initdata = {0xff, 0xfe, 0x0e, 0xfff, 0xffe, 0xffc, 0x0d, 0x0};
+
+/*
+ *	Probe for the card. The best way is to read the EISA ID if it
+ *	is known. Then we can check the prefix of the station address
+ *	PROM for a match against the value assigned to Mylex.
+ */
+
+static int __init do_lne390_probe(struct net_device *dev)
+{
+	unsigned short ioaddr = dev->base_addr;
+	int irq = dev->irq;
+	int mem_start = dev->mem_start;
+	int ret;
+
+	if (ioaddr > 0x1ff) {		/* Check a single specified location. */
+		if (!request_region(ioaddr, LNE390_IO_EXTENT, DRV_NAME))
+			return -EBUSY;
+		ret = lne390_probe1(dev, ioaddr);
+		if (ret)
+			release_region(ioaddr, LNE390_IO_EXTENT);
+		return ret;
+	}
+	else if (ioaddr > 0)		/* Don't probe at all. */
+		return -ENXIO;
+
+	if (!EISA_bus) {
+#if LNE390_DEBUG & LNE390_D_PROBE
+		printk("lne390-debug: Not an EISA bus. Not probing high ports.\n");
+#endif
+		return -ENXIO;
+	}
+
+	/* EISA spec allows for up to 16 slots, but 8 is typical. */
+	for (ioaddr = 0x1000; ioaddr < 0x9000; ioaddr += 0x1000) {
+		if (!request_region(ioaddr, LNE390_IO_EXTENT, DRV_NAME))
+			continue;
+		if (lne390_probe1(dev, ioaddr) == 0)
+			return 0;
+		release_region(ioaddr, LNE390_IO_EXTENT);
+		dev->irq = irq;
+		dev->mem_start = mem_start;
+	}
+
+	return -ENODEV;
+}
+
+#ifndef MODULE
+struct net_device * __init lne390_probe(int unit)
+{
+	struct net_device *dev = alloc_ei_netdev();
+	int err;
+
+	if (!dev)
+		return ERR_PTR(-ENOMEM);
+
+	sprintf(dev->name, "eth%d", unit);
+	netdev_boot_setup_check(dev);
+
+	err = do_lne390_probe(dev);
+	if (err)
+		goto out;
+	return dev;
+out:
+	free_netdev(dev);
+	return ERR_PTR(err);
+}
+#endif
+
+static int __init lne390_probe1(struct net_device *dev, int ioaddr)
+{
+	int i, revision, ret;
+	unsigned long eisa_id;
+
+	if (inb_p(ioaddr + LNE390_ID_PORT) == 0xff) return -ENODEV;
+
+#if LNE390_DEBUG & LNE390_D_PROBE
+	printk("lne390-debug: probe at %#x, ID %#8x\n", ioaddr, inl(ioaddr + LNE390_ID_PORT));
+	printk("lne390-debug: config regs: %#x %#x\n",
+		inb(ioaddr + LNE390_CFG1), inb(ioaddr + LNE390_CFG2));
+#endif
+
+
+/*	Check the EISA ID of the card. */
+	eisa_id = inl(ioaddr + LNE390_ID_PORT);
+	if ((eisa_id != LNE390_ID0) && (eisa_id != LNE390_ID1)) {
+		return -ENODEV;
+	}
+
+	revision = (eisa_id >> 24) & 0x01;	/* 0 = rev A, 1 rev B */
+
+#if 0
+/*	Check the Mylex vendor ID as well. Not really required. */
+	if (inb(ioaddr + LNE390_SA_PROM + 0) != LNE390_ADDR0
+		|| inb(ioaddr + LNE390_SA_PROM + 1) != LNE390_ADDR1
+		|| inb(ioaddr + LNE390_SA_PROM + 2) != LNE390_ADDR2 ) {
+		printk("lne390.c: card not found");
+		for(i = 0; i < ETHER_ADDR_LEN; i++)
+			printk(" %02x", inb(ioaddr + LNE390_SA_PROM + i));
+		printk(" (invalid prefix).\n");
+		return -ENODEV;
+	}
+#endif
+
+	for(i = 0; i < ETHER_ADDR_LEN; i++)
+		dev->dev_addr[i] = inb(ioaddr + LNE390_SA_PROM + i);
+	printk("lne390.c: LNE390%X in EISA slot %d, address %pM.\n",
+	       0xa+revision, ioaddr/0x1000, dev->dev_addr);
+
+	printk("lne390.c: ");
+
+	/* Snarf the interrupt now. CFG file has them all listed as `edge' with share=NO */
+	if (dev->irq == 0) {
+		unsigned char irq_reg = inb(ioaddr + LNE390_CFG2) >> 3;
+		dev->irq = irq_map[irq_reg & 0x07];
+		printk("using");
+	} else {
+		/* This is useless unless we reprogram the card here too */
+		if (dev->irq == 2) dev->irq = 9;	/* Doh! */
+		printk("assigning");
+	}
+	printk(" IRQ %d,", dev->irq);
+
+	if ((ret = request_irq(dev->irq, ei_interrupt, 0, DRV_NAME, dev))) {
+		printk (" unable to get IRQ %d.\n", dev->irq);
+		return ret;
+	}
+
+	if (dev->mem_start == 0) {
+		unsigned char mem_reg = inb(ioaddr + LNE390_CFG2) & 0x07;
+
+		if (revision)	/* LNE390B */
+			dev->mem_start = shmem_mapB[mem_reg] * 0x10000;
+		else		/* LNE390A */
+			dev->mem_start = shmem_mapA[mem_reg] * 0x10000;
+		printk(" using ");
+	} else {
+		/* Should check for value in shmem_map and reprogram the card to use it */
+		dev->mem_start &= 0xfff0000;
+		printk(" assigning ");
+	}
+
+	printk("%dkB memory at physical address %#lx\n",
+			LNE390_STOP_PG/4, dev->mem_start);
+
+	/*
+	   BEWARE!! Some dain-bramaged EISA SCUs will allow you to put
+	   the card mem within the region covered by `normal' RAM  !!!
+
+	   ioremap() will fail in that case.
+	*/
+	ei_status.mem = ioremap(dev->mem_start, LNE390_STOP_PG*0x100);
+	if (!ei_status.mem) {
+		printk(KERN_ERR "lne390.c: Unable to remap card memory above 1MB !!\n");
+		printk(KERN_ERR "lne390.c: Try using EISA SCU to set memory below 1MB.\n");
+		printk(KERN_ERR "lne390.c: Driver NOT installed.\n");
+		ret = -EAGAIN;
+		goto cleanup;
+	}
+	printk("lne390.c: remapped %dkB card memory to virtual address %p\n",
+			LNE390_STOP_PG/4, ei_status.mem);
+
+	dev->mem_start = (unsigned long)ei_status.mem;
+	dev->mem_end = dev->mem_start + (LNE390_STOP_PG - LNE390_START_PG)*256;
+
+	/* The 8390 offset is zero for the LNE390 */
+	dev->base_addr = ioaddr;
+
+	ei_status.name = "LNE390";
+	ei_status.tx_start_page = LNE390_START_PG;
+	ei_status.rx_start_page = LNE390_START_PG + TX_PAGES;
+	ei_status.stop_page = LNE390_STOP_PG;
+	ei_status.word16 = 1;
+
+	if (ei_debug > 0)
+		printk(version);
+
+	ei_status.reset_8390 = &lne390_reset_8390;
+	ei_status.block_input = &lne390_block_input;
+	ei_status.block_output = &lne390_block_output;
+	ei_status.get_8390_hdr = &lne390_get_8390_hdr;
+
+	dev->netdev_ops = &ei_netdev_ops;
+	NS8390_init(dev, 0);
+
+	ret = register_netdev(dev);
+	if (ret)
+		goto unmap;
+	return 0;
+unmap:
+	if (ei_status.reg0)
+		iounmap(ei_status.mem);
+cleanup:
+	free_irq(dev->irq, dev);
+	return ret;
+}
+
+/*
+ *	Reset as per the packet driver method. Judging by the EISA cfg
+ *	file, this just toggles the "Board Enable" bits (bit 2 and 0).
+ */
+
+static void lne390_reset_8390(struct net_device *dev)
+{
+	unsigned short ioaddr = dev->base_addr;
+
+	outb(0x04, ioaddr + LNE390_RESET_PORT);
+	if (ei_debug > 1) printk("%s: resetting the LNE390...", dev->name);
+
+	mdelay(2);
+
+	ei_status.txing = 0;
+	outb(0x01, ioaddr + LNE390_RESET_PORT);
+	if (ei_debug > 1) printk("reset done\n");
+}
+
+/*
+ *	Note: In the following three functions is the implicit assumption
+ *	that the associated memcpy will only use "rep; movsl" as long as
+ *	we keep the counts as some multiple of doublewords. This is a
+ *	requirement of the hardware, and also prevents us from using
+ *	eth_io_copy_and_sum() since we can't guarantee it will limit
+ *	itself to doubleword access.
+ */
+
+/*
+ *	Grab the 8390 specific header. Similar to the block_input routine, but
+ *	we don't need to be concerned with ring wrap as the header will be at
+ *	the start of a page, so we optimize accordingly. (A single doubleword.)
+ */
+
+static void
+lne390_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
+{
+	void __iomem *hdr_start = ei_status.mem + ((ring_page - LNE390_START_PG)<<8);
+	memcpy_fromio(hdr, hdr_start, sizeof(struct e8390_pkt_hdr));
+	hdr->count = (hdr->count + 3) & ~3;     /* Round up allocation. */
+}
+
+/*
+ *	Block input and output are easy on shared memory ethercards, the only
+ *	complication is when the ring buffer wraps. The count will already
+ *	be rounded up to a doubleword value via lne390_get_8390_hdr() above.
+ */
+
+static void lne390_block_input(struct net_device *dev, int count, struct sk_buff *skb,
+						  int ring_offset)
+{
+	void __iomem *xfer_start = ei_status.mem + ring_offset - (LNE390_START_PG<<8);
+
+	if (ring_offset + count > (LNE390_STOP_PG<<8)) {
+		/* Packet wraps over end of ring buffer. */
+		int semi_count = (LNE390_STOP_PG<<8) - ring_offset;
+		memcpy_fromio(skb->data, xfer_start, semi_count);
+		count -= semi_count;
+		memcpy_fromio(skb->data + semi_count,
+			ei_status.mem + (TX_PAGES<<8), count);
+	} else {
+		/* Packet is in one chunk. */
+		memcpy_fromio(skb->data, xfer_start, count);
+	}
+}
+
+static void lne390_block_output(struct net_device *dev, int count,
+				const unsigned char *buf, int start_page)
+{
+	void __iomem *shmem = ei_status.mem + ((start_page - LNE390_START_PG)<<8);
+
+	count = (count + 3) & ~3;     /* Round up to doubleword */
+	memcpy_toio(shmem, buf, count);
+}
+
+
+#ifdef MODULE
+#define MAX_LNE_CARDS	4	/* Max number of LNE390 cards per module */
+static struct net_device *dev_lne[MAX_LNE_CARDS];
+static int io[MAX_LNE_CARDS];
+static int irq[MAX_LNE_CARDS];
+static int mem[MAX_LNE_CARDS];
+
+module_param_array(io, int, NULL, 0);
+module_param_array(irq, int, NULL, 0);
+module_param_array(mem, int, NULL, 0);
+MODULE_PARM_DESC(io, "I/O base address(es)");
+MODULE_PARM_DESC(irq, "IRQ number(s)");
+MODULE_PARM_DESC(mem, "memory base address(es)");
+MODULE_DESCRIPTION("Mylex LNE390A/B EISA Ethernet driver");
+MODULE_LICENSE("GPL");
+
+int __init init_module(void)
+{
+	struct net_device *dev;
+	int this_dev, found = 0;
+
+	for (this_dev = 0; this_dev < MAX_LNE_CARDS; this_dev++) {
+		if (io[this_dev] == 0 && this_dev != 0)
+			break;
+		dev = alloc_ei_netdev();
+		if (!dev)
+			break;
+		dev->irq = irq[this_dev];
+		dev->base_addr = io[this_dev];
+		dev->mem_start = mem[this_dev];
+		if (do_lne390_probe(dev) == 0) {
+			dev_lne[found++] = dev;
+			continue;
+		}
+		free_netdev(dev);
+		printk(KERN_WARNING "lne390.c: No LNE390 card found (i/o = 0x%x).\n", io[this_dev]);
+		break;
+	}
+	if (found)
+		return 0;
+	return -ENXIO;
+}
+
+static void cleanup_card(struct net_device *dev)
+{
+	free_irq(dev->irq, dev);
+	release_region(dev->base_addr, LNE390_IO_EXTENT);
+	iounmap(ei_status.mem);
+}
+
+void __exit cleanup_module(void)
+{
+	int this_dev;
+
+	for (this_dev = 0; this_dev < MAX_LNE_CARDS; this_dev++) {
+		struct net_device *dev = dev_lne[this_dev];
+		if (dev) {
+			unregister_netdev(dev);
+			cleanup_card(dev);
+			free_netdev(dev);
+		}
+	}
+}
+#endif /* MODULE */
+
diff --git a/drivers/net/ethernet/8390/mac8390.c b/drivers/net/ethernet/8390/mac8390.c
new file mode 100644
index 0000000..f84f5e6
--- /dev/null
+++ b/drivers/net/ethernet/8390/mac8390.c
@@ -0,0 +1,874 @@
+/* mac8390.c: New driver for 8390-based Nubus (or Nubus-alike)
+   Ethernet cards on Linux */
+/* Based on the former daynaport.c driver, by Alan Cox.  Some code
+   taken from or inspired by skeleton.c by Donald Becker, acenic.c by
+   Jes Sorensen, and ne2k-pci.c by Donald Becker and Paul Gortmaker.
+
+   This software may be used and distributed according to the terms of
+   the GNU Public License, incorporated herein by reference.  */
+
+/* 2000-02-28: support added for Dayna and Kinetics cards by
+   A.G.deWijn@phys.uu.nl */
+/* 2000-04-04: support added for Dayna2 by bart@etpmod.phys.tue.nl */
+/* 2001-04-18: support for DaynaPort E/LC-M by rayk@knightsmanor.org */
+/* 2001-05-15: support for Cabletron ported from old daynaport driver
+ * and fixed access to Sonic Sys card which masquerades as a Farallon
+ * by rayk@knightsmanor.org */
+/* 2002-12-30: Try to support more cards, some clues from NetBSD driver */
+/* 2003-12-26: Make sure Asante cards always work. */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/nubus.h>
+#include <linux/in.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/bitops.h>
+#include <linux/io.h>
+
+#include <asm/system.h>
+#include <asm/dma.h>
+#include <asm/hwtest.h>
+#include <asm/macints.h>
+
+static char version[] =
+	"v0.4 2001-05-15 David Huggins-Daines <dhd@debian.org> and others\n";
+
+#define EI_SHIFT(x)	(ei_local->reg_offset[x])
+#define ei_inb(port)	in_8(port)
+#define ei_outb(val, port)	out_8(port, val)
+#define ei_inb_p(port)	in_8(port)
+#define ei_outb_p(val, port)	out_8(port, val)
+
+#include "lib8390.c"
+
+#define WD_START_PG			0x00	/* First page of TX buffer */
+#define CABLETRON_RX_START_PG		0x00    /* First page of RX buffer */
+#define CABLETRON_RX_STOP_PG		0x30    /* Last page +1 of RX ring */
+#define CABLETRON_TX_START_PG		CABLETRON_RX_STOP_PG
+						/* First page of TX buffer */
+
+/*
+ * Unfortunately it seems we have to hardcode these for the moment
+ * Shouldn't the card know about this?
+ * Does anyone know where to read it off the card?
+ * Do we trust the data provided by the card?
+ */
+
+#define DAYNA_8390_BASE		0x80000
+#define DAYNA_8390_MEM		0x00000
+
+#define CABLETRON_8390_BASE	0x90000
+#define CABLETRON_8390_MEM	0x00000
+
+#define INTERLAN_8390_BASE	0xE0000
+#define INTERLAN_8390_MEM	0xD0000
+
+enum mac8390_type {
+	MAC8390_NONE = -1,
+	MAC8390_APPLE,
+	MAC8390_ASANTE,
+	MAC8390_FARALLON,
+	MAC8390_CABLETRON,
+	MAC8390_DAYNA,
+	MAC8390_INTERLAN,
+	MAC8390_KINETICS,
+};
+
+static const char *cardname[] = {
+	"apple",
+	"asante",
+	"farallon",
+	"cabletron",
+	"dayna",
+	"interlan",
+	"kinetics",
+};
+
+static const int word16[] = {
+	1, /* apple */
+	1, /* asante */
+	1, /* farallon */
+	1, /* cabletron */
+	0, /* dayna */
+	1, /* interlan */
+	0, /* kinetics */
+};
+
+/* on which cards do we use NuBus resources? */
+static const int useresources[] = {
+	1, /* apple */
+	1, /* asante */
+	1, /* farallon */
+	0, /* cabletron */
+	0, /* dayna */
+	0, /* interlan */
+	0, /* kinetics */
+};
+
+enum mac8390_access {
+	ACCESS_UNKNOWN = 0,
+	ACCESS_32,
+	ACCESS_16,
+};
+
+extern int mac8390_memtest(struct net_device *dev);
+static int mac8390_initdev(struct net_device *dev, struct nubus_dev *ndev,
+			   enum mac8390_type type);
+
+static int mac8390_open(struct net_device *dev);
+static int mac8390_close(struct net_device *dev);
+static void mac8390_no_reset(struct net_device *dev);
+static void interlan_reset(struct net_device *dev);
+
+/* Sane (32-bit chunk memory read/write) - Some Farallon and Apple do this*/
+static void sane_get_8390_hdr(struct net_device *dev,
+			      struct e8390_pkt_hdr *hdr, int ring_page);
+static void sane_block_input(struct net_device *dev, int count,
+			     struct sk_buff *skb, int ring_offset);
+static void sane_block_output(struct net_device *dev, int count,
+			      const unsigned char *buf, const int start_page);
+
+/* dayna_memcpy to and from card */
+static void dayna_memcpy_fromcard(struct net_device *dev, void *to,
+				int from, int count);
+static void dayna_memcpy_tocard(struct net_device *dev, int to,
+			      const void *from, int count);
+
+/* Dayna - Dayna/Kinetics use this */
+static void dayna_get_8390_hdr(struct net_device *dev,
+			       struct e8390_pkt_hdr *hdr, int ring_page);
+static void dayna_block_input(struct net_device *dev, int count,
+			      struct sk_buff *skb, int ring_offset);
+static void dayna_block_output(struct net_device *dev, int count,
+			       const unsigned char *buf, int start_page);
+
+#define memcpy_fromio(a, b, c)	memcpy((a), (void *)(b), (c))
+#define memcpy_toio(a, b, c)	memcpy((void *)(a), (b), (c))
+
+#define memcmp_withio(a, b, c)	memcmp((a), (void *)(b), (c))
+
+/* Slow Sane (16-bit chunk memory read/write) Cabletron uses this */
+static void slow_sane_get_8390_hdr(struct net_device *dev,
+				   struct e8390_pkt_hdr *hdr, int ring_page);
+static void slow_sane_block_input(struct net_device *dev, int count,
+				  struct sk_buff *skb, int ring_offset);
+static void slow_sane_block_output(struct net_device *dev, int count,
+				   const unsigned char *buf, int start_page);
+static void word_memcpy_tocard(unsigned long tp, const void *fp, int count);
+static void word_memcpy_fromcard(void *tp, unsigned long fp, int count);
+
+static enum mac8390_type __init mac8390_ident(struct nubus_dev *dev)
+{
+	switch (dev->dr_sw) {
+	case NUBUS_DRSW_3COM:
+		switch (dev->dr_hw) {
+		case NUBUS_DRHW_APPLE_SONIC_NB:
+		case NUBUS_DRHW_APPLE_SONIC_LC:
+		case NUBUS_DRHW_SONNET:
+			return MAC8390_NONE;
+			break;
+		default:
+			return MAC8390_APPLE;
+			break;
+		}
+		break;
+
+	case NUBUS_DRSW_APPLE:
+		switch (dev->dr_hw) {
+		case NUBUS_DRHW_ASANTE_LC:
+			return MAC8390_NONE;
+			break;
+		case NUBUS_DRHW_CABLETRON:
+			return MAC8390_CABLETRON;
+			break;
+		default:
+			return MAC8390_APPLE;
+			break;
+		}
+		break;
+
+	case NUBUS_DRSW_ASANTE:
+		return MAC8390_ASANTE;
+		break;
+
+	case NUBUS_DRSW_TECHWORKS:
+	case NUBUS_DRSW_DAYNA2:
+	case NUBUS_DRSW_DAYNA_LC:
+		if (dev->dr_hw == NUBUS_DRHW_CABLETRON)
+			return MAC8390_CABLETRON;
+		else
+			return MAC8390_APPLE;
+		break;
+
+	case NUBUS_DRSW_FARALLON:
+		return MAC8390_FARALLON;
+		break;
+
+	case NUBUS_DRSW_KINETICS:
+		switch (dev->dr_hw) {
+		case NUBUS_DRHW_INTERLAN:
+			return MAC8390_INTERLAN;
+			break;
+		default:
+			return MAC8390_KINETICS;
+			break;
+		}
+		break;
+
+	case NUBUS_DRSW_DAYNA:
+		/*
+		 * These correspond to Dayna Sonic cards
+		 * which use the macsonic driver
+		 */
+		if (dev->dr_hw == NUBUS_DRHW_SMC9194 ||
+		    dev->dr_hw == NUBUS_DRHW_INTERLAN)
+			return MAC8390_NONE;
+		else
+			return MAC8390_DAYNA;
+		break;
+	}
+	return MAC8390_NONE;
+}
+
+static enum mac8390_access __init mac8390_testio(volatile unsigned long membase)
+{
+	unsigned long outdata = 0xA5A0B5B0;
+	unsigned long indata =  0x00000000;
+	/* Try writing 32 bits */
+	memcpy_toio(membase, &outdata, 4);
+	/* Now compare them */
+	if (memcmp_withio(&outdata, membase, 4) == 0)
+		return ACCESS_32;
+	/* Write 16 bit output */
+	word_memcpy_tocard(membase, &outdata, 4);
+	/* Now read it back */
+	word_memcpy_fromcard(&indata, membase, 4);
+	if (outdata == indata)
+		return ACCESS_16;
+	return ACCESS_UNKNOWN;
+}
+
+static int __init mac8390_memsize(unsigned long membase)
+{
+	unsigned long flags;
+	int i, j;
+
+	local_irq_save(flags);
+	/* Check up to 32K in 4K increments */
+	for (i = 0; i < 8; i++) {
+		volatile unsigned short *m = (unsigned short *)(membase + (i * 0x1000));
+
+		/* Unwriteable - we have a fully decoded card and the
+		   RAM end located */
+		if (hwreg_present(m) == 0)
+			break;
+
+		/* write a distinctive byte */
+		*m = 0xA5A0 | i;
+		/* check that we read back what we wrote */
+		if (*m != (0xA5A0 | i))
+			break;
+
+		/* check for partial decode and wrap */
+		for (j = 0; j < i; j++) {
+			volatile unsigned short *p = (unsigned short *)(membase + (j * 0x1000));
+			if (*p != (0xA5A0 | j))
+				break;
+		}
+	}
+	local_irq_restore(flags);
+	/*
+	 * in any case, we stopped once we tried one block too many,
+	 * or once we reached 32K
+	 */
+	return i * 0x1000;
+}
+
+static bool __init mac8390_init(struct net_device *dev, struct nubus_dev *ndev,
+				enum mac8390_type cardtype)
+{
+	struct nubus_dir dir;
+	struct nubus_dirent ent;
+	int offset;
+	volatile unsigned short *i;
+
+	printk_once(KERN_INFO pr_fmt("%s"), version);
+
+	dev->irq = SLOT2IRQ(ndev->board->slot);
+	/* This is getting to be a habit */
+	dev->base_addr = (ndev->board->slot_addr |
+			  ((ndev->board->slot & 0xf) << 20));
+
+	/*
+	 * Get some Nubus info - we will trust the card's idea
+	 * of where its memory and registers are.
+	 */
+
+	if (nubus_get_func_dir(ndev, &dir) == -1) {
+		pr_err("%s: Unable to get Nubus functional directory for slot %X!\n",
+		       dev->name, ndev->board->slot);
+		return false;
+	}
+
+	/* Get the MAC address */
+	if (nubus_find_rsrc(&dir, NUBUS_RESID_MAC_ADDRESS, &ent) == -1) {
+		pr_info("%s: Couldn't get MAC address!\n", dev->name);
+		return false;
+	}
+
+	nubus_get_rsrc_mem(dev->dev_addr, &ent, 6);
+
+	if (useresources[cardtype] == 1) {
+		nubus_rewinddir(&dir);
+		if (nubus_find_rsrc(&dir, NUBUS_RESID_MINOR_BASEOS,
+				    &ent) == -1) {
+			pr_err("%s: Memory offset resource for slot %X not found!\n",
+			       dev->name, ndev->board->slot);
+			return false;
+		}
+		nubus_get_rsrc_mem(&offset, &ent, 4);
+		dev->mem_start = dev->base_addr + offset;
+		/* yes, this is how the Apple driver does it */
+		dev->base_addr = dev->mem_start + 0x10000;
+		nubus_rewinddir(&dir);
+		if (nubus_find_rsrc(&dir, NUBUS_RESID_MINOR_LENGTH,
+				    &ent) == -1) {
+			pr_info("%s: Memory length resource for slot %X not found, probing\n",
+				dev->name, ndev->board->slot);
+			offset = mac8390_memsize(dev->mem_start);
+		} else {
+			nubus_get_rsrc_mem(&offset, &ent, 4);
+		}
+		dev->mem_end = dev->mem_start + offset;
+	} else {
+		switch (cardtype) {
+		case MAC8390_KINETICS:
+		case MAC8390_DAYNA: /* it's the same */
+			dev->base_addr = (int)(ndev->board->slot_addr +
+					       DAYNA_8390_BASE);
+			dev->mem_start = (int)(ndev->board->slot_addr +
+					       DAYNA_8390_MEM);
+			dev->mem_end = dev->mem_start +
+				       mac8390_memsize(dev->mem_start);
+			break;
+		case MAC8390_INTERLAN:
+			dev->base_addr = (int)(ndev->board->slot_addr +
+					       INTERLAN_8390_BASE);
+			dev->mem_start = (int)(ndev->board->slot_addr +
+					       INTERLAN_8390_MEM);
+			dev->mem_end = dev->mem_start +
+				       mac8390_memsize(dev->mem_start);
+			break;
+		case MAC8390_CABLETRON:
+			dev->base_addr = (int)(ndev->board->slot_addr +
+					       CABLETRON_8390_BASE);
+			dev->mem_start = (int)(ndev->board->slot_addr +
+					       CABLETRON_8390_MEM);
+			/* The base address is unreadable if 0x00
+			 * has been written to the command register
+			 * Reset the chip by writing E8390_NODMA +
+			 *   E8390_PAGE0 + E8390_STOP just to be
+			 *   sure
+			 */
+			i = (void *)dev->base_addr;
+			*i = 0x21;
+			dev->mem_end = dev->mem_start +
+				       mac8390_memsize(dev->mem_start);
+			break;
+
+		default:
+			pr_err("Card type %s is unsupported, sorry\n",
+			       ndev->board->name);
+			return false;
+		}
+	}
+
+	return true;
+}
+
+struct net_device * __init mac8390_probe(int unit)
+{
+	struct net_device *dev;
+	struct nubus_dev *ndev = NULL;
+	int err = -ENODEV;
+
+	static unsigned int slots;
+
+	enum mac8390_type cardtype;
+
+	/* probably should check for Nubus instead */
+
+	if (!MACH_IS_MAC)
+		return ERR_PTR(-ENODEV);
+
+	dev = ____alloc_ei_netdev(0);
+	if (!dev)
+		return ERR_PTR(-ENOMEM);
+
+	if (unit >= 0)
+		sprintf(dev->name, "eth%d", unit);
+
+	while ((ndev = nubus_find_type(NUBUS_CAT_NETWORK, NUBUS_TYPE_ETHERNET,
+				       ndev))) {
+		/* Have we seen it already? */
+		if (slots & (1 << ndev->board->slot))
+			continue;
+		slots |= 1 << ndev->board->slot;
+
+		cardtype = mac8390_ident(ndev);
+		if (cardtype == MAC8390_NONE)
+			continue;
+
+		if (!mac8390_init(dev, ndev, cardtype))
+			continue;
+
+		/* Do the nasty 8390 stuff */
+		if (!mac8390_initdev(dev, ndev, cardtype))
+			break;
+	}
+
+	if (!ndev)
+		goto out;
+	err = register_netdev(dev);
+	if (err)
+		goto out;
+	return dev;
+
+out:
+	free_netdev(dev);
+	return ERR_PTR(err);
+}
+
+#ifdef MODULE
+MODULE_AUTHOR("David Huggins-Daines <dhd@debian.org> and others");
+MODULE_DESCRIPTION("Macintosh NS8390-based Nubus Ethernet driver");
+MODULE_LICENSE("GPL");
+
+/* overkill, of course */
+static struct net_device *dev_mac8390[15];
+int init_module(void)
+{
+	int i;
+	for (i = 0; i < 15; i++) {
+		struct net_device *dev = mac8390_probe(-1);
+		if (IS_ERR(dev))
+			break;
+		dev_mac890[i] = dev;
+	}
+	if (!i) {
+		pr_notice("No useable cards found, driver NOT installed.\n");
+		return -ENODEV;
+	}
+	return 0;
+}
+
+void cleanup_module(void)
+{
+	int i;
+	for (i = 0; i < 15; i++) {
+		struct net_device *dev = dev_mac890[i];
+		if (dev) {
+			unregister_netdev(dev);
+			free_netdev(dev);
+		}
+	}
+}
+
+#endif /* MODULE */
+
+static const struct net_device_ops mac8390_netdev_ops = {
+	.ndo_open 		= mac8390_open,
+	.ndo_stop		= mac8390_close,
+	.ndo_start_xmit		= __ei_start_xmit,
+	.ndo_tx_timeout		= __ei_tx_timeout,
+	.ndo_get_stats		= __ei_get_stats,
+	.ndo_set_multicast_list = __ei_set_multicast_list,
+	.ndo_validate_addr	= eth_validate_addr,
+	.ndo_set_mac_address 	= eth_mac_addr,
+	.ndo_change_mtu		= eth_change_mtu,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	.ndo_poll_controller	= __ei_poll,
+#endif
+};
+
+static int __init mac8390_initdev(struct net_device *dev,
+				  struct nubus_dev *ndev,
+				  enum mac8390_type type)
+{
+	static u32 fwrd4_offsets[16] = {
+		0,      4,      8,      12,
+		16,     20,     24,     28,
+		32,     36,     40,     44,
+		48,     52,     56,     60
+	};
+	static u32 back4_offsets[16] = {
+		60,     56,     52,     48,
+		44,     40,     36,     32,
+		28,     24,     20,     16,
+		12,     8,      4,      0
+	};
+	static u32 fwrd2_offsets[16] = {
+		0,      2,      4,      6,
+		8,     10,     12,     14,
+		16,    18,     20,     22,
+		24,    26,     28,     30
+	};
+
+	int access_bitmode = 0;
+
+	/* Now fill in our stuff */
+	dev->netdev_ops = &mac8390_netdev_ops;
+
+	/* GAR, ei_status is actually a macro even though it looks global */
+	ei_status.name = cardname[type];
+	ei_status.word16 = word16[type];
+
+	/* Cabletron's TX/RX buffers are backwards */
+	if (type == MAC8390_CABLETRON) {
+		ei_status.tx_start_page = CABLETRON_TX_START_PG;
+		ei_status.rx_start_page = CABLETRON_RX_START_PG;
+		ei_status.stop_page = CABLETRON_RX_STOP_PG;
+		ei_status.rmem_start = dev->mem_start;
+		ei_status.rmem_end = dev->mem_start + CABLETRON_RX_STOP_PG*256;
+	} else {
+		ei_status.tx_start_page = WD_START_PG;
+		ei_status.rx_start_page = WD_START_PG + TX_PAGES;
+		ei_status.stop_page = (dev->mem_end - dev->mem_start)/256;
+		ei_status.rmem_start = dev->mem_start + TX_PAGES*256;
+		ei_status.rmem_end = dev->mem_end;
+	}
+
+	/* Fill in model-specific information and functions */
+	switch (type) {
+	case MAC8390_FARALLON:
+	case MAC8390_APPLE:
+		switch (mac8390_testio(dev->mem_start)) {
+		case ACCESS_UNKNOWN:
+			pr_err("Don't know how to access card memory!\n");
+			return -ENODEV;
+			break;
+
+		case ACCESS_16:
+			/* 16 bit card, register map is reversed */
+			ei_status.reset_8390 = mac8390_no_reset;
+			ei_status.block_input = slow_sane_block_input;
+			ei_status.block_output = slow_sane_block_output;
+			ei_status.get_8390_hdr = slow_sane_get_8390_hdr;
+			ei_status.reg_offset = back4_offsets;
+			break;
+
+		case ACCESS_32:
+			/* 32 bit card, register map is reversed */
+			ei_status.reset_8390 = mac8390_no_reset;
+			ei_status.block_input = sane_block_input;
+			ei_status.block_output = sane_block_output;
+			ei_status.get_8390_hdr = sane_get_8390_hdr;
+			ei_status.reg_offset = back4_offsets;
+			access_bitmode = 1;
+			break;
+		}
+		break;
+
+	case MAC8390_ASANTE:
+		/* Some Asante cards pass the 32 bit test
+		 * but overwrite system memory when run at 32 bit.
+		 * so we run them all at 16 bit.
+		 */
+		ei_status.reset_8390 = mac8390_no_reset;
+		ei_status.block_input = slow_sane_block_input;
+		ei_status.block_output = slow_sane_block_output;
+		ei_status.get_8390_hdr = slow_sane_get_8390_hdr;
+		ei_status.reg_offset = back4_offsets;
+		break;
+
+	case MAC8390_CABLETRON:
+		/* 16 bit card, register map is short forward */
+		ei_status.reset_8390 = mac8390_no_reset;
+		ei_status.block_input = slow_sane_block_input;
+		ei_status.block_output = slow_sane_block_output;
+		ei_status.get_8390_hdr = slow_sane_get_8390_hdr;
+		ei_status.reg_offset = fwrd2_offsets;
+		break;
+
+	case MAC8390_DAYNA:
+	case MAC8390_KINETICS:
+		/* 16 bit memory, register map is forward */
+		/* dayna and similar */
+		ei_status.reset_8390 = mac8390_no_reset;
+		ei_status.block_input = dayna_block_input;
+		ei_status.block_output = dayna_block_output;
+		ei_status.get_8390_hdr = dayna_get_8390_hdr;
+		ei_status.reg_offset = fwrd4_offsets;
+		break;
+
+	case MAC8390_INTERLAN:
+		/* 16 bit memory, register map is forward */
+		ei_status.reset_8390 = interlan_reset;
+		ei_status.block_input = slow_sane_block_input;
+		ei_status.block_output = slow_sane_block_output;
+		ei_status.get_8390_hdr = slow_sane_get_8390_hdr;
+		ei_status.reg_offset = fwrd4_offsets;
+		break;
+
+	default:
+		pr_err("Card type %s is unsupported, sorry\n",
+		       ndev->board->name);
+		return -ENODEV;
+	}
+
+	__NS8390_init(dev, 0);
+
+	/* Good, done, now spit out some messages */
+	pr_info("%s: %s in slot %X (type %s)\n",
+		dev->name, ndev->board->name, ndev->board->slot,
+		cardname[type]);
+	pr_info("MAC %pM IRQ %d, %d KB shared memory at %#lx, %d-bit access.\n",
+		dev->dev_addr, dev->irq,
+		(unsigned int)(dev->mem_end - dev->mem_start) >> 10,
+		dev->mem_start, access_bitmode ? 32 : 16);
+	return 0;
+}
+
+static int mac8390_open(struct net_device *dev)
+{
+	int err;
+
+	__ei_open(dev);
+	err = request_irq(dev->irq, __ei_interrupt, 0, "8390 Ethernet", dev);
+	if (err)
+		pr_err("%s: unable to get IRQ %d\n", dev->name, dev->irq);
+	return err;
+}
+
+static int mac8390_close(struct net_device *dev)
+{
+	free_irq(dev->irq, dev);
+	__ei_close(dev);
+	return 0;
+}
+
+static void mac8390_no_reset(struct net_device *dev)
+{
+	ei_status.txing = 0;
+	if (ei_debug > 1)
+		pr_info("reset not supported\n");
+}
+
+static void interlan_reset(struct net_device *dev)
+{
+	unsigned char *target = nubus_slot_addr(IRQ2SLOT(dev->irq));
+	if (ei_debug > 1)
+		pr_info("Need to reset the NS8390 t=%lu...", jiffies);
+	ei_status.txing = 0;
+	target[0xC0000] = 0;
+	if (ei_debug > 1)
+		pr_cont("reset complete\n");
+}
+
+/* dayna_memcpy_fromio/dayna_memcpy_toio */
+/* directly from daynaport.c by Alan Cox */
+static void dayna_memcpy_fromcard(struct net_device *dev, void *to, int from,
+				  int count)
+{
+	volatile unsigned char *ptr;
+	unsigned char *target = to;
+	from <<= 1;	/* word, skip overhead */
+	ptr = (unsigned char *)(dev->mem_start+from);
+	/* Leading byte? */
+	if (from & 2) {
+		*target++ = ptr[-1];
+		ptr += 2;
+		count--;
+	}
+	while (count >= 2) {
+		*(unsigned short *)target = *(unsigned short volatile *)ptr;
+		ptr += 4;			/* skip cruft */
+		target += 2;
+		count -= 2;
+	}
+	/* Trailing byte? */
+	if (count)
+		*target = *ptr;
+}
+
+static void dayna_memcpy_tocard(struct net_device *dev, int to,
+				const void *from, int count)
+{
+	volatile unsigned short *ptr;
+	const unsigned char *src = from;
+	to <<= 1;	/* word, skip overhead */
+	ptr = (unsigned short *)(dev->mem_start+to);
+	/* Leading byte? */
+	if (to & 2) {		/* avoid a byte write (stomps on other data) */
+		ptr[-1] = (ptr[-1]&0xFF00)|*src++;
+		ptr++;
+		count--;
+	}
+	while (count >= 2) {
+		*ptr++ = *(unsigned short *)src;	/* Copy and */
+		ptr++;			/* skip cruft */
+		src += 2;
+		count -= 2;
+	}
+	/* Trailing byte? */
+	if (count) {
+		/* card doesn't like byte writes */
+		*ptr = (*ptr & 0x00FF) | (*src << 8);
+	}
+}
+
+/* sane block input/output */
+static void sane_get_8390_hdr(struct net_device *dev,
+			      struct e8390_pkt_hdr *hdr, int ring_page)
+{
+	unsigned long hdr_start = (ring_page - WD_START_PG)<<8;
+	memcpy_fromio(hdr, dev->mem_start + hdr_start, 4);
+	/* Fix endianness */
+	hdr->count = swab16(hdr->count);
+}
+
+static void sane_block_input(struct net_device *dev, int count,
+			     struct sk_buff *skb, int ring_offset)
+{
+	unsigned long xfer_base = ring_offset - (WD_START_PG<<8);
+	unsigned long xfer_start = xfer_base + dev->mem_start;
+
+	if (xfer_start + count > ei_status.rmem_end) {
+		/* We must wrap the input move. */
+		int semi_count = ei_status.rmem_end - xfer_start;
+		memcpy_fromio(skb->data, dev->mem_start + xfer_base,
+			      semi_count);
+		count -= semi_count;
+		memcpy_fromio(skb->data + semi_count, ei_status.rmem_start,
+			      count);
+	} else {
+		memcpy_fromio(skb->data, dev->mem_start + xfer_base, count);
+	}
+}
+
+static void sane_block_output(struct net_device *dev, int count,
+			      const unsigned char *buf, int start_page)
+{
+	long shmem = (start_page - WD_START_PG)<<8;
+
+	memcpy_toio(dev->mem_start + shmem, buf, count);
+}
+
+/* dayna block input/output */
+static void dayna_get_8390_hdr(struct net_device *dev,
+			       struct e8390_pkt_hdr *hdr, int ring_page)
+{
+	unsigned long hdr_start = (ring_page - WD_START_PG)<<8;
+
+	dayna_memcpy_fromcard(dev, hdr, hdr_start, 4);
+	/* Fix endianness */
+	hdr->count = (hdr->count & 0xFF) << 8 | (hdr->count >> 8);
+}
+
+static void dayna_block_input(struct net_device *dev, int count,
+			      struct sk_buff *skb, int ring_offset)
+{
+	unsigned long xfer_base = ring_offset - (WD_START_PG<<8);
+	unsigned long xfer_start = xfer_base+dev->mem_start;
+
+	/* Note the offset math is done in card memory space which is word
+	   per long onto our space. */
+
+	if (xfer_start + count > ei_status.rmem_end) {
+		/* We must wrap the input move. */
+		int semi_count = ei_status.rmem_end - xfer_start;
+		dayna_memcpy_fromcard(dev, skb->data, xfer_base, semi_count);
+		count -= semi_count;
+		dayna_memcpy_fromcard(dev, skb->data + semi_count,
+				      ei_status.rmem_start - dev->mem_start,
+				      count);
+	} else {
+		dayna_memcpy_fromcard(dev, skb->data, xfer_base, count);
+	}
+}
+
+static void dayna_block_output(struct net_device *dev, int count,
+			       const unsigned char *buf,
+			       int start_page)
+{
+	long shmem = (start_page - WD_START_PG)<<8;
+
+	dayna_memcpy_tocard(dev, shmem, buf, count);
+}
+
+/* Cabletron block I/O */
+static void slow_sane_get_8390_hdr(struct net_device *dev,
+				   struct e8390_pkt_hdr *hdr,
+				   int ring_page)
+{
+	unsigned long hdr_start = (ring_page - WD_START_PG)<<8;
+	word_memcpy_fromcard(hdr, dev->mem_start + hdr_start, 4);
+	/* Register endianism - fix here rather than 8390.c */
+	hdr->count = (hdr->count&0xFF)<<8|(hdr->count>>8);
+}
+
+static void slow_sane_block_input(struct net_device *dev, int count,
+				  struct sk_buff *skb, int ring_offset)
+{
+	unsigned long xfer_base = ring_offset - (WD_START_PG<<8);
+	unsigned long xfer_start = xfer_base+dev->mem_start;
+
+	if (xfer_start + count > ei_status.rmem_end) {
+		/* We must wrap the input move. */
+		int semi_count = ei_status.rmem_end - xfer_start;
+		word_memcpy_fromcard(skb->data, dev->mem_start + xfer_base,
+				     semi_count);
+		count -= semi_count;
+		word_memcpy_fromcard(skb->data + semi_count,
+				     ei_status.rmem_start, count);
+	} else {
+		word_memcpy_fromcard(skb->data, dev->mem_start + xfer_base,
+				     count);
+	}
+}
+
+static void slow_sane_block_output(struct net_device *dev, int count,
+				   const unsigned char *buf, int start_page)
+{
+	long shmem = (start_page - WD_START_PG)<<8;
+
+	word_memcpy_tocard(dev->mem_start + shmem, buf, count);
+}
+
+static void word_memcpy_tocard(unsigned long tp, const void *fp, int count)
+{
+	volatile unsigned short *to = (void *)tp;
+	const unsigned short *from = fp;
+
+	count++;
+	count /= 2;
+
+	while (count--)
+		*to++ = *from++;
+}
+
+static void word_memcpy_fromcard(void *tp, unsigned long fp, int count)
+{
+	unsigned short *to = tp;
+	const volatile unsigned short *from = (const void *)fp;
+
+	count++;
+	count /= 2;
+
+	while (count--)
+		*to++ = *from++;
+}
+
+
diff --git a/drivers/net/ethernet/8390/ne-h8300.c b/drivers/net/ethernet/8390/ne-h8300.c
new file mode 100644
index 0000000..7298a34
--- /dev/null
+++ b/drivers/net/ethernet/8390/ne-h8300.c
@@ -0,0 +1,685 @@
+/* ne-h8300.c: A NE2000 clone on H8/300 driver for linux. */
+/*
+    original ne.c
+    Written 1992-94 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
+
+    H8/300 modified
+    Yoshinori Sato <ysato@users.sourceforge.jp>
+*/
+
+static const char version1[] =
+"ne-h8300.c:v1.00 2004/04/11 ysato\n";
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/jiffies.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#define EI_SHIFT(x)	(ei_local->reg_offset[x])
+
+#include "8390.h"
+
+#define DRV_NAME "ne-h8300"
+
+/* Some defines that people can play with if so inclined. */
+
+/* Do we perform extra sanity checks on stuff ? */
+/* #define NE_SANITY_CHECK */
+
+/* Do we implement the read before write bugfix ? */
+/* #define NE_RW_BUGFIX */
+
+/* Do we have a non std. amount of memory? (in units of 256 byte pages) */
+/* #define PACKETBUF_MEMSIZE	0x40 */
+
+/* A zero-terminated list of I/O addresses to be probed at boot. */
+
+/* ---- No user-serviceable parts below ---- */
+
+static const char version[] =
+    "8390.c:v1.10cvs 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
+
+#include "lib8390.c"
+
+#define NE_BASE	 (dev->base_addr)
+#define NE_CMD	 	0x00
+#define NE_DATAPORT	(ei_status.word16?0x20:0x10)	/* NatSemi-defined port window offset. */
+#define NE_RESET	(ei_status.word16?0x3f:0x1f)	/* Issue a read to reset, a write to clear. */
+#define NE_IO_EXTENT	(ei_status.word16?0x40:0x20)
+
+#define NESM_START_PG	0x40	/* First page of TX buffer */
+#define NESM_STOP_PG	0x80	/* Last page +1 of RX ring */
+
+static int ne_probe1(struct net_device *dev, int ioaddr);
+
+static int ne_open(struct net_device *dev);
+static int ne_close(struct net_device *dev);
+
+static void ne_reset_8390(struct net_device *dev);
+static void ne_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
+			  int ring_page);
+static void ne_block_input(struct net_device *dev, int count,
+			  struct sk_buff *skb, int ring_offset);
+static void ne_block_output(struct net_device *dev, const int count,
+		const unsigned char *buf, const int start_page);
+
+
+static u32 reg_offset[16];
+
+static int __init init_reg_offset(struct net_device *dev,unsigned long base_addr)
+{
+	struct ei_device *ei_local = netdev_priv(dev);
+	int i;
+	unsigned char bus_width;
+
+	bus_width = *(volatile unsigned char *)ABWCR;
+	bus_width &= 1 << ((base_addr >> 21) & 7);
+
+	for (i = 0; i < ARRAY_SIZE(reg_offset); i++)
+		if (bus_width == 0)
+			reg_offset[i] = i * 2 + 1;
+		else
+			reg_offset[i] = i;
+
+	ei_local->reg_offset = reg_offset;
+	return 0;
+}
+
+static int __initdata h8300_ne_count = 0;
+#ifdef CONFIG_H8300H_H8MAX
+static unsigned long __initdata h8300_ne_base[] = { 0x800600 };
+static int h8300_ne_irq[] = {EXT_IRQ4};
+#endif
+#ifdef CONFIG_H8300H_AKI3068NET
+static unsigned long __initdata h8300_ne_base[] = { 0x200000 };
+static int h8300_ne_irq[] = {EXT_IRQ5};
+#endif
+
+static inline int init_dev(struct net_device *dev)
+{
+	if (h8300_ne_count < ARRAY_SIZE(h8300_ne_base)) {
+		dev->base_addr = h8300_ne_base[h8300_ne_count];
+		dev->irq       = h8300_ne_irq[h8300_ne_count];
+		h8300_ne_count++;
+		return 0;
+	} else
+		return -ENODEV;
+}
+
+/*  Probe for various non-shared-memory ethercards.
+
+   NEx000-clone boards have a Station Address PROM (SAPROM) in the packet
+   buffer memory space.  NE2000 clones have 0x57,0x57 in bytes 0x0e,0x0f of
+   the SAPROM, while other supposed NE2000 clones must be detected by their
+   SA prefix.
+
+   Reading the SAPROM from a word-wide card with the 8390 set in byte-wide
+   mode results in doubled values, which can be detected and compensated for.
+
+   The probe is also responsible for initializing the card and filling
+   in the 'dev' and 'ei_status' structures.
+
+   We use the minimum memory size for some ethercard product lines, iff we can't
+   distinguish models.  You can increase the packet buffer size by setting
+   PACKETBUF_MEMSIZE.  Reported Cabletron packet buffer locations are:
+	E1010   starts at 0x100 and ends at 0x2000.
+	E1010-x starts at 0x100 and ends at 0x8000. ("-x" means "more memory")
+	E2010	 starts at 0x100 and ends at 0x4000.
+	E2010-x starts at 0x100 and ends at 0xffff.  */
+
+static int __init do_ne_probe(struct net_device *dev)
+{
+	unsigned int base_addr = dev->base_addr;
+
+	/* First check any supplied i/o locations. User knows best. <cough> */
+	if (base_addr > 0x1ff)	/* Check a single specified location. */
+		return ne_probe1(dev, base_addr);
+	else if (base_addr != 0)	/* Don't probe at all. */
+		return -ENXIO;
+
+	return -ENODEV;
+}
+
+static void cleanup_card(struct net_device *dev)
+{
+	free_irq(dev->irq, dev);
+	release_region(dev->base_addr, NE_IO_EXTENT);
+}
+
+#ifndef MODULE
+struct net_device * __init ne_probe(int unit)
+{
+	struct net_device *dev = ____alloc_ei_netdev(0);
+	int err;
+
+	if (!dev)
+		return ERR_PTR(-ENOMEM);
+
+	if (init_dev(dev))
+		return ERR_PTR(-ENODEV);
+
+	sprintf(dev->name, "eth%d", unit);
+	netdev_boot_setup_check(dev);
+
+	err = init_reg_offset(dev, dev->base_addr);
+	if (err)
+		goto out;
+
+	err = do_ne_probe(dev);
+	if (err)
+		goto out;
+	return dev;
+out:
+	free_netdev(dev);
+	return ERR_PTR(err);
+}
+#endif
+
+static const struct net_device_ops ne_netdev_ops = {
+	.ndo_open		= ne_open,
+	.ndo_stop		= ne_close,
+
+	.ndo_start_xmit		= __ei_start_xmit,
+	.ndo_tx_timeout		= __ei_tx_timeout,
+	.ndo_get_stats		= __ei_get_stats,
+	.ndo_set_multicast_list = __ei_set_multicast_list,
+	.ndo_validate_addr	= eth_validate_addr,
+	.ndo_set_mac_address	= eth_mac_addr,
+	.ndo_change_mtu		= eth_change_mtu,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	.ndo_poll_controller	= __ei_poll,
+#endif
+};
+
+static int __init ne_probe1(struct net_device *dev, int ioaddr)
+{
+	int i;
+	unsigned char SA_prom[16];
+	int wordlength = 2;
+	const char *name = NULL;
+	int start_page, stop_page;
+	int reg0, ret;
+	static unsigned version_printed;
+	struct ei_device *ei_local = netdev_priv(dev);
+	unsigned char bus_width;
+
+	if (!request_region(ioaddr, NE_IO_EXTENT, DRV_NAME))
+		return -EBUSY;
+
+	reg0 = inb_p(ioaddr);
+	if (reg0 == 0xFF) {
+		ret = -ENODEV;
+		goto err_out;
+	}
+
+	/* Do a preliminary verification that we have a 8390. */
+	{
+		int regd;
+		outb_p(E8390_NODMA+E8390_PAGE1+E8390_STOP, ioaddr + E8390_CMD);
+		regd = inb_p(ioaddr + EI_SHIFT(0x0d));
+		outb_p(0xff, ioaddr + EI_SHIFT(0x0d));
+		outb_p(E8390_NODMA+E8390_PAGE0, ioaddr + E8390_CMD);
+		inb_p(ioaddr + EN0_COUNTER0); /* Clear the counter by reading. */
+		if (inb_p(ioaddr + EN0_COUNTER0) != 0) {
+			outb_p(reg0, ioaddr + EI_SHIFT(0));
+			outb_p(regd, ioaddr + EI_SHIFT(0x0d));	/* Restore the old values. */
+			ret = -ENODEV;
+			goto err_out;
+		}
+	}
+
+	if (ei_debug  &&  version_printed++ == 0)
+		printk(KERN_INFO "%s", version1);
+
+	printk(KERN_INFO "NE*000 ethercard probe at %08x:", ioaddr);
+
+	/* Read the 16 bytes of station address PROM.
+	   We must first initialize registers, similar to NS8390_init(eifdev, 0).
+	   We can't reliably read the SAPROM address without this.
+	   (I learned the hard way!). */
+	{
+		struct {unsigned char value, offset; } program_seq[] =
+		{
+			{E8390_NODMA+E8390_PAGE0+E8390_STOP, E8390_CMD}, /* Select page 0*/
+			{0x48,	EN0_DCFG},	/* Set byte-wide (0x48) access. */
+			{0x00,	EN0_RCNTLO},	/* Clear the count regs. */
+			{0x00,	EN0_RCNTHI},
+			{0x00,	EN0_IMR},	/* Mask completion irq. */
+			{0xFF,	EN0_ISR},
+			{E8390_RXOFF, EN0_RXCR},	/* 0x20  Set to monitor */
+			{E8390_TXOFF, EN0_TXCR},	/* 0x02  and loopback mode. */
+			{32,	EN0_RCNTLO},
+			{0x00,	EN0_RCNTHI},
+			{0x00,	EN0_RSARLO},	/* DMA starting at 0x0000. */
+			{0x00,	EN0_RSARHI},
+			{E8390_RREAD+E8390_START, E8390_CMD},
+		};
+
+		for (i = 0; i < ARRAY_SIZE(program_seq); i++)
+			outb_p(program_seq[i].value, ioaddr + program_seq[i].offset);
+
+	}
+	bus_width = *(volatile unsigned char *)ABWCR;
+	bus_width &= 1 << ((ioaddr >> 21) & 7);
+	ei_status.word16 = (bus_width == 0); /* temporary setting */
+	for(i = 0; i < 16 /*sizeof(SA_prom)*/; i++) {
+		SA_prom[i] = inb_p(ioaddr + NE_DATAPORT);
+		inb_p(ioaddr + NE_DATAPORT); /* dummy read */
+	}
+
+	start_page = NESM_START_PG;
+	stop_page = NESM_STOP_PG;
+
+	if (bus_width)
+		wordlength = 1;
+	else
+		outb_p(0x49, ioaddr + EN0_DCFG);
+
+	/* Set up the rest of the parameters. */
+	name = (wordlength == 2) ? "NE2000" : "NE1000";
+
+	if (! dev->irq) {
+		printk(" failed to detect IRQ line.\n");
+		ret = -EAGAIN;
+		goto err_out;
+	}
+
+	/* Snarf the interrupt now.  There's no point in waiting since we cannot
+	   share and the board will usually be enabled. */
+	ret = request_irq(dev->irq, __ei_interrupt, 0, name, dev);
+	if (ret) {
+		printk (" unable to get IRQ %d (errno=%d).\n", dev->irq, ret);
+		goto err_out;
+	}
+
+	dev->base_addr = ioaddr;
+
+	for(i = 0; i < ETHER_ADDR_LEN; i++)
+		dev->dev_addr[i] = SA_prom[i];
+	printk(" %pM\n", dev->dev_addr);
+
+	printk("%s: %s found at %#x, using IRQ %d.\n",
+		dev->name, name, ioaddr, dev->irq);
+
+	ei_status.name = name;
+	ei_status.tx_start_page = start_page;
+	ei_status.stop_page = stop_page;
+	ei_status.word16 = (wordlength == 2);
+
+	ei_status.rx_start_page = start_page + TX_PAGES;
+#ifdef PACKETBUF_MEMSIZE
+	 /* Allow the packet buffer size to be overridden by know-it-alls. */
+	ei_status.stop_page = ei_status.tx_start_page + PACKETBUF_MEMSIZE;
+#endif
+
+	ei_status.reset_8390 = &ne_reset_8390;
+	ei_status.block_input = &ne_block_input;
+	ei_status.block_output = &ne_block_output;
+	ei_status.get_8390_hdr = &ne_get_8390_hdr;
+	ei_status.priv = 0;
+
+	dev->netdev_ops = &ne_netdev_ops;
+
+	__NS8390_init(dev, 0);
+
+	ret = register_netdev(dev);
+	if (ret)
+		goto out_irq;
+	return 0;
+out_irq:
+	free_irq(dev->irq, dev);
+err_out:
+	release_region(ioaddr, NE_IO_EXTENT);
+	return ret;
+}
+
+static int ne_open(struct net_device *dev)
+{
+	__ei_open(dev);
+	return 0;
+}
+
+static int ne_close(struct net_device *dev)
+{
+	if (ei_debug > 1)
+		printk(KERN_DEBUG "%s: Shutting down ethercard.\n", dev->name);
+	__ei_close(dev);
+	return 0;
+}
+
+/* Hard reset the card.  This used to pause for the same period that a
+   8390 reset command required, but that shouldn't be necessary. */
+
+static void ne_reset_8390(struct net_device *dev)
+{
+	unsigned long reset_start_time = jiffies;
+	struct ei_device *ei_local = netdev_priv(dev);
+
+	if (ei_debug > 1)
+		printk(KERN_DEBUG "resetting the 8390 t=%ld...", jiffies);
+
+	/* DON'T change these to inb_p/outb_p or reset will fail on clones. */
+	outb(inb(NE_BASE + NE_RESET), NE_BASE + NE_RESET);
+
+	ei_status.txing = 0;
+	ei_status.dmaing = 0;
+
+	/* This check _should_not_ be necessary, omit eventually. */
+	while ((inb_p(NE_BASE+EN0_ISR) & ENISR_RESET) == 0)
+		if (time_after(jiffies, reset_start_time + 2*HZ/100)) {
+			printk(KERN_WARNING "%s: ne_reset_8390() did not complete.\n", dev->name);
+			break;
+		}
+	outb_p(ENISR_RESET, NE_BASE + EN0_ISR);	/* Ack intr. */
+}
+
+/* Grab the 8390 specific header. Similar to the block_input routine, but
+   we don't need to be concerned with ring wrap as the header will be at
+   the start of a page, so we optimize accordingly. */
+
+static void ne_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
+{
+	struct ei_device *ei_local = netdev_priv(dev);
+	/* This *shouldn't* happen. If it does, it's the last thing you'll see */
+
+	if (ei_status.dmaing)
+	{
+		printk(KERN_EMERG "%s: DMAing conflict in ne_get_8390_hdr "
+			"[DMAstat:%d][irqlock:%d].\n",
+			dev->name, ei_status.dmaing, ei_status.irqlock);
+		return;
+	}
+
+	ei_status.dmaing |= 0x01;
+	outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, NE_BASE + NE_CMD);
+	outb_p(sizeof(struct e8390_pkt_hdr), NE_BASE + EN0_RCNTLO);
+	outb_p(0, NE_BASE + EN0_RCNTHI);
+	outb_p(0, NE_BASE + EN0_RSARLO);		/* On page boundary */
+	outb_p(ring_page, NE_BASE + EN0_RSARHI);
+	outb_p(E8390_RREAD+E8390_START, NE_BASE + NE_CMD);
+
+	if (ei_status.word16) {
+		int len;
+		unsigned short *p = (unsigned short *)hdr;
+		for (len = sizeof(struct e8390_pkt_hdr)>>1; len > 0; len--)
+			*p++ = inw(NE_BASE + NE_DATAPORT);
+	} else
+		insb(NE_BASE + NE_DATAPORT, hdr, sizeof(struct e8390_pkt_hdr));
+
+	outb_p(ENISR_RDC, NE_BASE + EN0_ISR);	/* Ack intr. */
+	ei_status.dmaing &= ~0x01;
+
+	le16_to_cpus(&hdr->count);
+}
+
+/* Block input and output, similar to the Crynwr packet driver.  If you
+   are porting to a new ethercard, look at the packet driver source for hints.
+   The NEx000 doesn't share the on-board packet memory -- you have to put
+   the packet out through the "remote DMA" dataport using outb. */
+
+static void ne_block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset)
+{
+	struct ei_device *ei_local = netdev_priv(dev);
+#ifdef NE_SANITY_CHECK
+	int xfer_count = count;
+#endif
+	char *buf = skb->data;
+
+	/* This *shouldn't* happen. If it does, it's the last thing you'll see */
+	if (ei_status.dmaing)
+	{
+		printk(KERN_EMERG "%s: DMAing conflict in ne_block_input "
+			"[DMAstat:%d][irqlock:%d].\n",
+			dev->name, ei_status.dmaing, ei_status.irqlock);
+		return;
+	}
+	ei_status.dmaing |= 0x01;
+	outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, NE_BASE + NE_CMD);
+	outb_p(count & 0xff, NE_BASE + EN0_RCNTLO);
+	outb_p(count >> 8, NE_BASE + EN0_RCNTHI);
+	outb_p(ring_offset & 0xff, NE_BASE + EN0_RSARLO);
+	outb_p(ring_offset >> 8, NE_BASE + EN0_RSARHI);
+	outb_p(E8390_RREAD+E8390_START, NE_BASE + NE_CMD);
+	if (ei_status.word16)
+	{
+		int len;
+		unsigned short *p = (unsigned short *)buf;
+		for (len = count>>1; len > 0; len--)
+			*p++ = inw(NE_BASE + NE_DATAPORT);
+		if (count & 0x01)
+		{
+			buf[count-1] = inb(NE_BASE + NE_DATAPORT);
+#ifdef NE_SANITY_CHECK
+			xfer_count++;
+#endif
+		}
+	} else {
+		insb(NE_BASE + NE_DATAPORT, buf, count);
+	}
+
+#ifdef NE_SANITY_CHECK
+	/* This was for the ALPHA version only, but enough people have
+	   been encountering problems so it is still here.  If you see
+	   this message you either 1) have a slightly incompatible clone
+	   or 2) have noise/speed problems with your bus. */
+
+	if (ei_debug > 1)
+	{
+		/* DMA termination address check... */
+		int addr, tries = 20;
+		do {
+			/* DON'T check for 'inb_p(EN0_ISR) & ENISR_RDC' here
+			   -- it's broken for Rx on some cards! */
+			int high = inb_p(NE_BASE + EN0_RSARHI);
+			int low = inb_p(NE_BASE + EN0_RSARLO);
+			addr = (high << 8) + low;
+			if (((ring_offset + xfer_count) & 0xff) == low)
+				break;
+		} while (--tries > 0);
+	 	if (tries <= 0)
+			printk(KERN_WARNING "%s: RX transfer address mismatch,"
+				"%#4.4x (expected) vs. %#4.4x (actual).\n",
+				dev->name, ring_offset + xfer_count, addr);
+	}
+#endif
+	outb_p(ENISR_RDC, NE_BASE + EN0_ISR);	/* Ack intr. */
+	ei_status.dmaing &= ~0x01;
+}
+
+static void ne_block_output(struct net_device *dev, int count,
+		const unsigned char *buf, const int start_page)
+{
+	struct ei_device *ei_local = netdev_priv(dev);
+	unsigned long dma_start;
+#ifdef NE_SANITY_CHECK
+	int retries = 0;
+#endif
+
+	/* Round the count up for word writes.  Do we need to do this?
+	   What effect will an odd byte count have on the 8390?
+	   I should check someday. */
+
+	if (ei_status.word16 && (count & 0x01))
+		count++;
+
+	/* This *shouldn't* happen. If it does, it's the last thing you'll see */
+	if (ei_status.dmaing)
+	{
+		printk(KERN_EMERG "%s: DMAing conflict in ne_block_output."
+			"[DMAstat:%d][irqlock:%d]\n",
+			dev->name, ei_status.dmaing, ei_status.irqlock);
+		return;
+	}
+	ei_status.dmaing |= 0x01;
+	/* We should already be in page 0, but to be safe... */
+	outb_p(E8390_PAGE0+E8390_START+E8390_NODMA, NE_BASE + NE_CMD);
+
+#ifdef NE_SANITY_CHECK
+retry:
+#endif
+
+#ifdef NE8390_RW_BUGFIX
+	/* Handle the read-before-write bug the same way as the
+	   Crynwr packet driver -- the NatSemi method doesn't work.
+	   Actually this doesn't always work either, but if you have
+	   problems with your NEx000 this is better than nothing! */
+
+	outb_p(0x42, NE_BASE + EN0_RCNTLO);
+	outb_p(0x00, NE_BASE + EN0_RCNTHI);
+	outb_p(0x42, NE_BASE + EN0_RSARLO);
+	outb_p(0x00, NE_BASE + EN0_RSARHI);
+	outb_p(E8390_RREAD+E8390_START, NE_BASE + NE_CMD);
+	/* Make certain that the dummy read has occurred. */
+	udelay(6);
+#endif
+
+	outb_p(ENISR_RDC, NE_BASE + EN0_ISR);
+
+	/* Now the normal output. */
+	outb_p(count & 0xff, NE_BASE + EN0_RCNTLO);
+	outb_p(count >> 8,   NE_BASE + EN0_RCNTHI);
+	outb_p(0x00, NE_BASE + EN0_RSARLO);
+	outb_p(start_page, NE_BASE + EN0_RSARHI);
+
+	outb_p(E8390_RWRITE+E8390_START, NE_BASE + NE_CMD);
+	if (ei_status.word16) {
+		int len;
+		unsigned short *p = (unsigned short *)buf;
+		for (len = count>>1; len > 0; len--)
+			outw(*p++, NE_BASE + NE_DATAPORT);
+	} else {
+		outsb(NE_BASE + NE_DATAPORT, buf, count);
+	}
+
+	dma_start = jiffies;
+
+#ifdef NE_SANITY_CHECK
+	/* This was for the ALPHA version only, but enough people have
+	   been encountering problems so it is still here. */
+
+	if (ei_debug > 1)
+	{
+		/* DMA termination address check... */
+		int addr, tries = 20;
+		do {
+			int high = inb_p(NE_BASE + EN0_RSARHI);
+			int low = inb_p(NE_BASE + EN0_RSARLO);
+			addr = (high << 8) + low;
+			if ((start_page << 8) + count == addr)
+				break;
+		} while (--tries > 0);
+
+		if (tries <= 0)
+		{
+			printk(KERN_WARNING "%s: Tx packet transfer address mismatch,"
+				"%#4.4x (expected) vs. %#4.4x (actual).\n",
+				dev->name, (start_page << 8) + count, addr);
+			if (retries++ == 0)
+				goto retry;
+		}
+	}
+#endif
+
+	while ((inb_p(NE_BASE + EN0_ISR) & ENISR_RDC) == 0)
+		if (time_after(jiffies, dma_start + 2*HZ/100)) {		/* 20ms */
+			printk(KERN_WARNING "%s: timeout waiting for Tx RDC.\n", dev->name);
+			ne_reset_8390(dev);
+			__NS8390_init(dev,1);
+			break;
+		}
+
+	outb_p(ENISR_RDC, NE_BASE + EN0_ISR);	/* Ack intr. */
+	ei_status.dmaing &= ~0x01;
+}
+
+
+#ifdef MODULE
+#define MAX_NE_CARDS	1	/* Max number of NE cards per module */
+static struct net_device *dev_ne[MAX_NE_CARDS];
+static int io[MAX_NE_CARDS];
+static int irq[MAX_NE_CARDS];
+static int bad[MAX_NE_CARDS];	/* 0xbad = bad sig or no reset ack */
+
+module_param_array(io, int, NULL, 0);
+module_param_array(irq, int, NULL, 0);
+module_param_array(bad, int, NULL, 0);
+MODULE_PARM_DESC(io, "I/O base address(es)");
+MODULE_PARM_DESC(irq, "IRQ number(s)");
+MODULE_DESCRIPTION("H8/300 NE2000 Ethernet driver");
+MODULE_LICENSE("GPL");
+
+/* This is set up so that no ISA autoprobe takes place. We can't guarantee
+that the ne2k probe is the last 8390 based probe to take place (as it
+is at boot) and so the probe will get confused by any other 8390 cards.
+ISA device autoprobes on a running machine are not recommended anyway. */
+
+int init_module(void)
+{
+	int this_dev, found = 0;
+	int err;
+
+	for (this_dev = 0; this_dev < MAX_NE_CARDS; this_dev++) {
+		struct net_device *dev = ____alloc_ei_netdev(0);
+		if (!dev)
+			break;
+		if (io[this_dev]) {
+			dev->irq = irq[this_dev];
+			dev->mem_end = bad[this_dev];
+			dev->base_addr = io[this_dev];
+		} else {
+			dev->base_addr = h8300_ne_base[this_dev];
+			dev->irq = h8300_ne_irq[this_dev];
+		}
+		err = init_reg_offset(dev, dev->base_addr);
+		if (!err) {
+			if (do_ne_probe(dev) == 0) {
+				dev_ne[found++] = dev;
+				continue;
+			}
+		}
+		free_netdev(dev);
+		if (found)
+			break;
+		if (io[this_dev] != 0)
+			printk(KERN_WARNING "ne.c: No NE*000 card found at i/o = %#x\n", dev->base_addr);
+		else
+			printk(KERN_NOTICE "ne.c: You must supply \"io=0xNNN\" value(s) for ISA cards.\n");
+		return -ENXIO;
+	}
+	if (found)
+		return 0;
+	return -ENODEV;
+}
+
+void cleanup_module(void)
+{
+	int this_dev;
+
+	for (this_dev = 0; this_dev < MAX_NE_CARDS; this_dev++) {
+		struct net_device *dev = dev_ne[this_dev];
+		if (dev) {
+			unregister_netdev(dev);
+			cleanup_card(dev);
+			free_netdev(dev);
+		}
+	}
+}
+#endif /* MODULE */
diff --git a/drivers/net/ethernet/8390/ne.c b/drivers/net/ethernet/8390/ne.c
new file mode 100644
index 0000000..1063093
--- /dev/null
+++ b/drivers/net/ethernet/8390/ne.c
@@ -0,0 +1,1008 @@
+/* ne.c: A general non-shared-memory NS8390 ethernet driver for linux. */
+/*
+    Written 1992-94 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
+
+    This driver should work with many programmed-I/O 8390-based ethernet
+    boards.  Currently it supports the NE1000, NE2000, many clones,
+    and some Cabletron products.
+
+    Changelog:
+
+    Paul Gortmaker	: use ENISR_RDC to monitor Tx PIO uploads, made
+			  sanity checks and bad clone support optional.
+    Paul Gortmaker	: new reset code, reset card after probe at boot.
+    Paul Gortmaker	: multiple card support for module users.
+    Paul Gortmaker	: Support for PCI ne2k clones, similar to lance.c
+    Paul Gortmaker	: Allow users with bad cards to avoid full probe.
+    Paul Gortmaker	: PCI probe changes, more PCI cards supported.
+    rjohnson@analogic.com : Changed init order so an interrupt will only
+    occur after memory is allocated for dev->priv. Deallocated memory
+    last in cleanup_modue()
+    Richard Guenther    : Added support for ISAPnP cards
+    Paul Gortmaker	: Discontinued PCI support - use ne2k-pci.c instead.
+    Hayato Fujiwara	: Add m32r support.
+
+*/
+
+/* Routines for the NatSemi-based designs (NE[12]000). */
+
+static const char version1[] =
+"ne.c:v1.10 9/23/94 Donald Becker (becker@scyld.com)\n";
+static const char version2[] =
+"Last modified Nov 1, 2000 by Paul Gortmaker\n";
+
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/isapnp.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/jiffies.h>
+#include <linux/platform_device.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+
+#include "8390.h"
+
+#define DRV_NAME "ne"
+
+/* Some defines that people can play with if so inclined. */
+
+/* Do we support clones that don't adhere to 14,15 of the SAprom ? */
+#define SUPPORT_NE_BAD_CLONES
+/* 0xbad = bad sig or no reset ack */
+#define BAD 0xbad
+
+#define MAX_NE_CARDS	4	/* Max number of NE cards per module */
+static struct platform_device *pdev_ne[MAX_NE_CARDS];
+static int io[MAX_NE_CARDS];
+static int irq[MAX_NE_CARDS];
+static int bad[MAX_NE_CARDS];
+
+#ifdef MODULE
+module_param_array(io, int, NULL, 0);
+module_param_array(irq, int, NULL, 0);
+module_param_array(bad, int, NULL, 0);
+MODULE_PARM_DESC(io, "I/O base address(es),required");
+MODULE_PARM_DESC(irq, "IRQ number(s)");
+MODULE_PARM_DESC(bad, "Accept card(s) with bad signatures");
+MODULE_DESCRIPTION("NE1000/NE2000 ISA/PnP Ethernet driver");
+MODULE_LICENSE("GPL");
+#endif /* MODULE */
+
+/* Do we perform extra sanity checks on stuff ? */
+/* #define NE_SANITY_CHECK */
+
+/* Do we implement the read before write bugfix ? */
+/* #define NE_RW_BUGFIX */
+
+/* Do we have a non std. amount of memory? (in units of 256 byte pages) */
+/* #define PACKETBUF_MEMSIZE	0x40 */
+
+/* This is set up so that no ISA autoprobe takes place. We can't guarantee
+that the ne2k probe is the last 8390 based probe to take place (as it
+is at boot) and so the probe will get confused by any other 8390 cards.
+ISA device autoprobes on a running machine are not recommended anyway. */
+#if !defined(MODULE) && (defined(CONFIG_ISA) || defined(CONFIG_M32R))
+/* Do we need a portlist for the ISA auto-probe ? */
+#define NEEDS_PORTLIST
+#endif
+
+/* A zero-terminated list of I/O addresses to be probed at boot. */
+#ifdef NEEDS_PORTLIST
+static unsigned int netcard_portlist[] __initdata = {
+	0x300, 0x280, 0x320, 0x340, 0x360, 0x380, 0
+};
+#endif
+
+static struct isapnp_device_id isapnp_clone_list[] __initdata = {
+	{	ISAPNP_CARD_ID('A','X','E',0x2011),
+		ISAPNP_VENDOR('A','X','E'), ISAPNP_FUNCTION(0x2011),
+		(long) "NetGear EA201" },
+	{	ISAPNP_ANY_ID, ISAPNP_ANY_ID,
+		ISAPNP_VENDOR('E','D','I'), ISAPNP_FUNCTION(0x0216),
+		(long) "NN NE2000" },
+	{	ISAPNP_ANY_ID, ISAPNP_ANY_ID,
+		ISAPNP_VENDOR('P','N','P'), ISAPNP_FUNCTION(0x80d6),
+		(long) "Generic PNP" },
+	{ }	/* terminate list */
+};
+
+MODULE_DEVICE_TABLE(isapnp, isapnp_clone_list);
+
+#ifdef SUPPORT_NE_BAD_CLONES
+/* A list of bad clones that we none-the-less recognize. */
+static struct { const char *name8, *name16; unsigned char SAprefix[4];}
+bad_clone_list[] __initdata = {
+    {"DE100", "DE200", {0x00, 0xDE, 0x01,}},
+    {"DE120", "DE220", {0x00, 0x80, 0xc8,}},
+    {"DFI1000", "DFI2000", {'D', 'F', 'I',}}, /* Original, eh?  */
+    {"EtherNext UTP8", "EtherNext UTP16", {0x00, 0x00, 0x79}},
+    {"NE1000","NE2000-invalid", {0x00, 0x00, 0xd8}}, /* Ancient real NE1000. */
+    {"NN1000", "NN2000",  {0x08, 0x03, 0x08}}, /* Outlaw no-name clone. */
+    {"4-DIM8","4-DIM16", {0x00,0x00,0x4d,}},  /* Outlaw 4-Dimension cards. */
+    {"Con-Intl_8", "Con-Intl_16", {0x00, 0x00, 0x24}}, /* Connect Int'nl */
+    {"ET-100","ET-200", {0x00, 0x45, 0x54}}, /* YANG and YA clone */
+    {"COMPEX","COMPEX16",{0x00,0x80,0x48}}, /* Broken ISA Compex cards */
+    {"E-LAN100", "E-LAN200", {0x00, 0x00, 0x5d}}, /* Broken ne1000 clones */
+    {"PCM-4823", "PCM-4823", {0x00, 0xc0, 0x6c}}, /* Broken Advantech MoBo */
+    {"REALTEK", "RTL8019", {0x00, 0x00, 0xe8}}, /* no-name with Realtek chip */
+#ifdef CONFIG_MACH_TX49XX
+    {"RBHMA4X00-RTL8019", "RBHMA4X00-RTL8019", {0x00, 0x60, 0x0a}},  /* Toshiba built-in */
+#endif
+    {"LCS-8834", "LCS-8836", {0x04, 0x04, 0x37}}, /* ShinyNet (SET) */
+    {NULL,}
+};
+#endif
+
+/* ---- No user-serviceable parts below ---- */
+
+#define NE_BASE	 (dev->base_addr)
+#define NE_CMD	 	0x00
+#define NE_DATAPORT	0x10	/* NatSemi-defined port window offset. */
+#define NE_RESET	0x1f	/* Issue a read to reset, a write to clear. */
+#define NE_IO_EXTENT	0x20
+
+#define NE1SM_START_PG	0x20	/* First page of TX buffer */
+#define NE1SM_STOP_PG 	0x40	/* Last page +1 of RX ring */
+#define NESM_START_PG	0x40	/* First page of TX buffer */
+#define NESM_STOP_PG	0x80	/* Last page +1 of RX ring */
+
+#if defined(CONFIG_PLAT_MAPPI)
+#  define DCR_VAL 0x4b
+#elif defined(CONFIG_PLAT_OAKS32R)  || \
+   defined(CONFIG_MACH_TX49XX)
+#  define DCR_VAL 0x48		/* 8-bit mode */
+#else
+#  define DCR_VAL 0x49
+#endif
+
+static int ne_probe1(struct net_device *dev, unsigned long ioaddr);
+static int ne_probe_isapnp(struct net_device *dev);
+
+static void ne_reset_8390(struct net_device *dev);
+static void ne_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
+			  int ring_page);
+static void ne_block_input(struct net_device *dev, int count,
+			  struct sk_buff *skb, int ring_offset);
+static void ne_block_output(struct net_device *dev, const int count,
+		const unsigned char *buf, const int start_page);
+
+
+/*  Probe for various non-shared-memory ethercards.
+
+   NEx000-clone boards have a Station Address PROM (SAPROM) in the packet
+   buffer memory space.  NE2000 clones have 0x57,0x57 in bytes 0x0e,0x0f of
+   the SAPROM, while other supposed NE2000 clones must be detected by their
+   SA prefix.
+
+   Reading the SAPROM from a word-wide card with the 8390 set in byte-wide
+   mode results in doubled values, which can be detected and compensated for.
+
+   The probe is also responsible for initializing the card and filling
+   in the 'dev' and 'ei_status' structures.
+
+   We use the minimum memory size for some ethercard product lines, iff we can't
+   distinguish models.  You can increase the packet buffer size by setting
+   PACKETBUF_MEMSIZE.  Reported Cabletron packet buffer locations are:
+	E1010   starts at 0x100 and ends at 0x2000.
+	E1010-x starts at 0x100 and ends at 0x8000. ("-x" means "more memory")
+	E2010	 starts at 0x100 and ends at 0x4000.
+	E2010-x starts at 0x100 and ends at 0xffff.  */
+
+static int __init do_ne_probe(struct net_device *dev)
+{
+	unsigned long base_addr = dev->base_addr;
+#ifdef NEEDS_PORTLIST
+	int orig_irq = dev->irq;
+#endif
+
+	/* First check any supplied i/o locations. User knows best. <cough> */
+	if (base_addr > 0x1ff) {	/* Check a single specified location. */
+		int ret = ne_probe1(dev, base_addr);
+		if (ret)
+			printk(KERN_WARNING "ne.c: No NE*000 card found at "
+				"i/o = %#lx\n", base_addr);
+		return ret;
+	}
+	else if (base_addr != 0)	/* Don't probe at all. */
+		return -ENXIO;
+
+	/* Then look for any installed ISAPnP clones */
+	if (isapnp_present() && (ne_probe_isapnp(dev) == 0))
+		return 0;
+
+#ifdef NEEDS_PORTLIST
+	/* Last resort. The semi-risky ISA auto-probe. */
+	for (base_addr = 0; netcard_portlist[base_addr] != 0; base_addr++) {
+		int ioaddr = netcard_portlist[base_addr];
+		dev->irq = orig_irq;
+		if (ne_probe1(dev, ioaddr) == 0)
+			return 0;
+	}
+#endif
+
+	return -ENODEV;
+}
+
+static int __init ne_probe_isapnp(struct net_device *dev)
+{
+	int i;
+
+	for (i = 0; isapnp_clone_list[i].vendor != 0; i++) {
+		struct pnp_dev *idev = NULL;
+
+		while ((idev = pnp_find_dev(NULL,
+					    isapnp_clone_list[i].vendor,
+					    isapnp_clone_list[i].function,
+					    idev))) {
+			/* Avoid already found cards from previous calls */
+			if (pnp_device_attach(idev) < 0)
+				continue;
+			if (pnp_activate_dev(idev) < 0) {
+			      	pnp_device_detach(idev);
+			      	continue;
+			}
+			/* if no io and irq, search for next */
+			if (!pnp_port_valid(idev, 0) || !pnp_irq_valid(idev, 0)) {
+				pnp_device_detach(idev);
+				continue;
+			}
+			/* found it */
+			dev->base_addr = pnp_port_start(idev, 0);
+			dev->irq = pnp_irq(idev, 0);
+			printk(KERN_INFO "ne.c: ISAPnP reports %s at i/o %#lx, irq %d.\n",
+				(char *) isapnp_clone_list[i].driver_data,
+				dev->base_addr, dev->irq);
+			if (ne_probe1(dev, dev->base_addr) != 0) {	/* Shouldn't happen. */
+				printk(KERN_ERR "ne.c: Probe of ISAPnP card at %#lx failed.\n", dev->base_addr);
+				pnp_device_detach(idev);
+				return -ENXIO;
+			}
+			ei_status.priv = (unsigned long)idev;
+			break;
+		}
+		if (!idev)
+			continue;
+		return 0;
+	}
+
+	return -ENODEV;
+}
+
+static int __init ne_probe1(struct net_device *dev, unsigned long ioaddr)
+{
+	int i;
+	unsigned char SA_prom[32];
+	int wordlength = 2;
+	const char *name = NULL;
+	int start_page, stop_page;
+	int neX000, ctron, copam, bad_card;
+	int reg0, ret;
+	static unsigned version_printed;
+
+	if (!request_region(ioaddr, NE_IO_EXTENT, DRV_NAME))
+		return -EBUSY;
+
+	reg0 = inb_p(ioaddr);
+	if (reg0 == 0xFF) {
+		ret = -ENODEV;
+		goto err_out;
+	}
+
+	/* Do a preliminary verification that we have a 8390. */
+	{
+		int regd;
+		outb_p(E8390_NODMA+E8390_PAGE1+E8390_STOP, ioaddr + E8390_CMD);
+		regd = inb_p(ioaddr + 0x0d);
+		outb_p(0xff, ioaddr + 0x0d);
+		outb_p(E8390_NODMA+E8390_PAGE0, ioaddr + E8390_CMD);
+		inb_p(ioaddr + EN0_COUNTER0); /* Clear the counter by reading. */
+		if (inb_p(ioaddr + EN0_COUNTER0) != 0) {
+			outb_p(reg0, ioaddr);
+			outb_p(regd, ioaddr + 0x0d);	/* Restore the old values. */
+			ret = -ENODEV;
+			goto err_out;
+		}
+	}
+
+	if (ei_debug  &&  version_printed++ == 0)
+		printk(KERN_INFO "%s%s", version1, version2);
+
+	printk(KERN_INFO "NE*000 ethercard probe at %#3lx:", ioaddr);
+
+	/* A user with a poor card that fails to ack the reset, or that
+	   does not have a valid 0x57,0x57 signature can still use this
+	   without having to recompile. Specifying an i/o address along
+	   with an otherwise unused dev->mem_end value of "0xBAD" will
+	   cause the driver to skip these parts of the probe. */
+
+	bad_card = ((dev->base_addr != 0) && (dev->mem_end == BAD));
+
+	/* Reset card. Who knows what dain-bramaged state it was left in. */
+
+	{
+		unsigned long reset_start_time = jiffies;
+
+		/* DON'T change these to inb_p/outb_p or reset will fail on clones. */
+		outb(inb(ioaddr + NE_RESET), ioaddr + NE_RESET);
+
+		while ((inb_p(ioaddr + EN0_ISR) & ENISR_RESET) == 0)
+		if (time_after(jiffies, reset_start_time + 2*HZ/100)) {
+			if (bad_card) {
+				printk(" (warning: no reset ack)");
+				break;
+			} else {
+				printk(" not found (no reset ack).\n");
+				ret = -ENODEV;
+				goto err_out;
+			}
+		}
+
+		outb_p(0xff, ioaddr + EN0_ISR);		/* Ack all intr. */
+	}
+
+	/* Read the 16 bytes of station address PROM.
+	   We must first initialize registers, similar to NS8390p_init(eifdev, 0).
+	   We can't reliably read the SAPROM address without this.
+	   (I learned the hard way!). */
+	{
+		struct {unsigned char value, offset; } program_seq[] =
+		{
+			{E8390_NODMA+E8390_PAGE0+E8390_STOP, E8390_CMD}, /* Select page 0*/
+			{0x48,	EN0_DCFG},	/* Set byte-wide (0x48) access. */
+			{0x00,	EN0_RCNTLO},	/* Clear the count regs. */
+			{0x00,	EN0_RCNTHI},
+			{0x00,	EN0_IMR},	/* Mask completion irq. */
+			{0xFF,	EN0_ISR},
+			{E8390_RXOFF, EN0_RXCR},	/* 0x20  Set to monitor */
+			{E8390_TXOFF, EN0_TXCR},	/* 0x02  and loopback mode. */
+			{32,	EN0_RCNTLO},
+			{0x00,	EN0_RCNTHI},
+			{0x00,	EN0_RSARLO},	/* DMA starting at 0x0000. */
+			{0x00,	EN0_RSARHI},
+			{E8390_RREAD+E8390_START, E8390_CMD},
+		};
+
+		for (i = 0; i < ARRAY_SIZE(program_seq); i++)
+			outb_p(program_seq[i].value, ioaddr + program_seq[i].offset);
+
+	}
+	for(i = 0; i < 32 /*sizeof(SA_prom)*/; i+=2) {
+		SA_prom[i] = inb(ioaddr + NE_DATAPORT);
+		SA_prom[i+1] = inb(ioaddr + NE_DATAPORT);
+		if (SA_prom[i] != SA_prom[i+1])
+			wordlength = 1;
+	}
+
+	if (wordlength == 2)
+	{
+		for (i = 0; i < 16; i++)
+			SA_prom[i] = SA_prom[i+i];
+		/* We must set the 8390 for word mode. */
+		outb_p(DCR_VAL, ioaddr + EN0_DCFG);
+		start_page = NESM_START_PG;
+
+		/*
+		 * Realtek RTL8019AS datasheet says that the PSTOP register
+		 * shouldn't exceed 0x60 in 8-bit mode.
+		 * This chip can be identified by reading the signature from
+		 * the  remote byte count registers (otherwise write-only)...
+		 */
+		if ((DCR_VAL & 0x01) == 0 &&		/* 8-bit mode */
+		    inb(ioaddr + EN0_RCNTLO) == 0x50 &&
+		    inb(ioaddr + EN0_RCNTHI) == 0x70)
+			stop_page = 0x60;
+		else
+			stop_page = NESM_STOP_PG;
+	} else {
+		start_page = NE1SM_START_PG;
+		stop_page  = NE1SM_STOP_PG;
+	}
+
+#if  defined(CONFIG_PLAT_MAPPI) || defined(CONFIG_PLAT_OAKS32R)
+	neX000 = ((SA_prom[14] == 0x57  &&  SA_prom[15] == 0x57)
+		|| (SA_prom[14] == 0x42 && SA_prom[15] == 0x42));
+#else
+	neX000 = (SA_prom[14] == 0x57  &&  SA_prom[15] == 0x57);
+#endif
+	ctron =  (SA_prom[0] == 0x00 && SA_prom[1] == 0x00 && SA_prom[2] == 0x1d);
+	copam =  (SA_prom[14] == 0x49 && SA_prom[15] == 0x00);
+
+	/* Set up the rest of the parameters. */
+	if (neX000 || bad_card || copam) {
+		name = (wordlength == 2) ? "NE2000" : "NE1000";
+	}
+	else if (ctron)
+	{
+		name = (wordlength == 2) ? "Ctron-8" : "Ctron-16";
+		start_page = 0x01;
+		stop_page = (wordlength == 2) ? 0x40 : 0x20;
+	}
+	else
+	{
+#ifdef SUPPORT_NE_BAD_CLONES
+		/* Ack!  Well, there might be a *bad* NE*000 clone there.
+		   Check for total bogus addresses. */
+		for (i = 0; bad_clone_list[i].name8; i++)
+		{
+			if (SA_prom[0] == bad_clone_list[i].SAprefix[0] &&
+				SA_prom[1] == bad_clone_list[i].SAprefix[1] &&
+				SA_prom[2] == bad_clone_list[i].SAprefix[2])
+			{
+				if (wordlength == 2)
+				{
+					name = bad_clone_list[i].name16;
+				} else {
+					name = bad_clone_list[i].name8;
+				}
+				break;
+			}
+		}
+		if (bad_clone_list[i].name8 == NULL)
+		{
+			printk(" not found (invalid signature %2.2x %2.2x).\n",
+				SA_prom[14], SA_prom[15]);
+			ret = -ENXIO;
+			goto err_out;
+		}
+#else
+		printk(" not found.\n");
+		ret = -ENXIO;
+		goto err_out;
+#endif
+	}
+
+	if (dev->irq < 2)
+	{
+		unsigned long cookie = probe_irq_on();
+		outb_p(0x50, ioaddr + EN0_IMR);	/* Enable one interrupt. */
+		outb_p(0x00, ioaddr + EN0_RCNTLO);
+		outb_p(0x00, ioaddr + EN0_RCNTHI);
+		outb_p(E8390_RREAD+E8390_START, ioaddr); /* Trigger it... */
+		mdelay(10);		/* wait 10ms for interrupt to propagate */
+		outb_p(0x00, ioaddr + EN0_IMR); 		/* Mask it again. */
+		dev->irq = probe_irq_off(cookie);
+		if (ei_debug > 2)
+			printk(" autoirq is %d\n", dev->irq);
+	} else if (dev->irq == 2)
+		/* Fixup for users that don't know that IRQ 2 is really IRQ 9,
+		   or don't know which one to set. */
+		dev->irq = 9;
+
+	if (! dev->irq) {
+		printk(" failed to detect IRQ line.\n");
+		ret = -EAGAIN;
+		goto err_out;
+	}
+
+	/* Snarf the interrupt now.  There's no point in waiting since we cannot
+	   share and the board will usually be enabled. */
+	ret = request_irq(dev->irq, eip_interrupt, 0, name, dev);
+	if (ret) {
+		printk (" unable to get IRQ %d (errno=%d).\n", dev->irq, ret);
+		goto err_out;
+	}
+
+	dev->base_addr = ioaddr;
+
+#ifdef CONFIG_PLAT_MAPPI
+	outb_p(E8390_NODMA + E8390_PAGE1 + E8390_STOP,
+		ioaddr + E8390_CMD); /* 0x61 */
+	for (i = 0 ; i < ETHER_ADDR_LEN ; i++) {
+		dev->dev_addr[i] = SA_prom[i]
+			= inb_p(ioaddr + EN1_PHYS_SHIFT(i));
+	}
+#else
+	for(i = 0; i < ETHER_ADDR_LEN; i++) {
+		dev->dev_addr[i] = SA_prom[i];
+	}
+#endif
+
+	printk("%pM\n", dev->dev_addr);
+
+	ei_status.name = name;
+	ei_status.tx_start_page = start_page;
+	ei_status.stop_page = stop_page;
+
+	/* Use 16-bit mode only if this wasn't overridden by DCR_VAL */
+	ei_status.word16 = (wordlength == 2 && (DCR_VAL & 0x01));
+
+	ei_status.rx_start_page = start_page + TX_PAGES;
+#ifdef PACKETBUF_MEMSIZE
+	 /* Allow the packet buffer size to be overridden by know-it-alls. */
+	ei_status.stop_page = ei_status.tx_start_page + PACKETBUF_MEMSIZE;
+#endif
+
+	ei_status.reset_8390 = &ne_reset_8390;
+	ei_status.block_input = &ne_block_input;
+	ei_status.block_output = &ne_block_output;
+	ei_status.get_8390_hdr = &ne_get_8390_hdr;
+	ei_status.priv = 0;
+
+	dev->netdev_ops = &eip_netdev_ops;
+	NS8390p_init(dev, 0);
+
+	ret = register_netdev(dev);
+	if (ret)
+		goto out_irq;
+	printk(KERN_INFO "%s: %s found at %#lx, using IRQ %d.\n",
+	       dev->name, name, ioaddr, dev->irq);
+	return 0;
+
+out_irq:
+	free_irq(dev->irq, dev);
+err_out:
+	release_region(ioaddr, NE_IO_EXTENT);
+	return ret;
+}
+
+/* Hard reset the card.  This used to pause for the same period that a
+   8390 reset command required, but that shouldn't be necessary. */
+
+static void ne_reset_8390(struct net_device *dev)
+{
+	unsigned long reset_start_time = jiffies;
+
+	if (ei_debug > 1)
+		printk(KERN_DEBUG "resetting the 8390 t=%ld...", jiffies);
+
+	/* DON'T change these to inb_p/outb_p or reset will fail on clones. */
+	outb(inb(NE_BASE + NE_RESET), NE_BASE + NE_RESET);
+
+	ei_status.txing = 0;
+	ei_status.dmaing = 0;
+
+	/* This check _should_not_ be necessary, omit eventually. */
+	while ((inb_p(NE_BASE+EN0_ISR) & ENISR_RESET) == 0)
+		if (time_after(jiffies, reset_start_time + 2*HZ/100)) {
+			printk(KERN_WARNING "%s: ne_reset_8390() did not complete.\n", dev->name);
+			break;
+		}
+	outb_p(ENISR_RESET, NE_BASE + EN0_ISR);	/* Ack intr. */
+}
+
+/* Grab the 8390 specific header. Similar to the block_input routine, but
+   we don't need to be concerned with ring wrap as the header will be at
+   the start of a page, so we optimize accordingly. */
+
+static void ne_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
+{
+	int nic_base = dev->base_addr;
+
+	/* This *shouldn't* happen. If it does, it's the last thing you'll see */
+
+	if (ei_status.dmaing)
+	{
+		printk(KERN_EMERG "%s: DMAing conflict in ne_get_8390_hdr "
+			"[DMAstat:%d][irqlock:%d].\n",
+			dev->name, ei_status.dmaing, ei_status.irqlock);
+		return;
+	}
+
+	ei_status.dmaing |= 0x01;
+	outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base+ NE_CMD);
+	outb_p(sizeof(struct e8390_pkt_hdr), nic_base + EN0_RCNTLO);
+	outb_p(0, nic_base + EN0_RCNTHI);
+	outb_p(0, nic_base + EN0_RSARLO);		/* On page boundary */
+	outb_p(ring_page, nic_base + EN0_RSARHI);
+	outb_p(E8390_RREAD+E8390_START, nic_base + NE_CMD);
+
+	if (ei_status.word16)
+		insw(NE_BASE + NE_DATAPORT, hdr, sizeof(struct e8390_pkt_hdr)>>1);
+	else
+		insb(NE_BASE + NE_DATAPORT, hdr, sizeof(struct e8390_pkt_hdr));
+
+	outb_p(ENISR_RDC, nic_base + EN0_ISR);	/* Ack intr. */
+	ei_status.dmaing &= ~0x01;
+
+	le16_to_cpus(&hdr->count);
+}
+
+/* Block input and output, similar to the Crynwr packet driver.  If you
+   are porting to a new ethercard, look at the packet driver source for hints.
+   The NEx000 doesn't share the on-board packet memory -- you have to put
+   the packet out through the "remote DMA" dataport using outb. */
+
+static void ne_block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset)
+{
+#ifdef NE_SANITY_CHECK
+	int xfer_count = count;
+#endif
+	int nic_base = dev->base_addr;
+	char *buf = skb->data;
+
+	/* This *shouldn't* happen. If it does, it's the last thing you'll see */
+	if (ei_status.dmaing)
+	{
+		printk(KERN_EMERG "%s: DMAing conflict in ne_block_input "
+			"[DMAstat:%d][irqlock:%d].\n",
+			dev->name, ei_status.dmaing, ei_status.irqlock);
+		return;
+	}
+	ei_status.dmaing |= 0x01;
+	outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base+ NE_CMD);
+	outb_p(count & 0xff, nic_base + EN0_RCNTLO);
+	outb_p(count >> 8, nic_base + EN0_RCNTHI);
+	outb_p(ring_offset & 0xff, nic_base + EN0_RSARLO);
+	outb_p(ring_offset >> 8, nic_base + EN0_RSARHI);
+	outb_p(E8390_RREAD+E8390_START, nic_base + NE_CMD);
+	if (ei_status.word16)
+	{
+		insw(NE_BASE + NE_DATAPORT,buf,count>>1);
+		if (count & 0x01)
+		{
+			buf[count-1] = inb(NE_BASE + NE_DATAPORT);
+#ifdef NE_SANITY_CHECK
+			xfer_count++;
+#endif
+		}
+	} else {
+		insb(NE_BASE + NE_DATAPORT, buf, count);
+	}
+
+#ifdef NE_SANITY_CHECK
+	/* This was for the ALPHA version only, but enough people have
+	   been encountering problems so it is still here.  If you see
+	   this message you either 1) have a slightly incompatible clone
+	   or 2) have noise/speed problems with your bus. */
+
+	if (ei_debug > 1)
+	{
+		/* DMA termination address check... */
+		int addr, tries = 20;
+		do {
+			/* DON'T check for 'inb_p(EN0_ISR) & ENISR_RDC' here
+			   -- it's broken for Rx on some cards! */
+			int high = inb_p(nic_base + EN0_RSARHI);
+			int low = inb_p(nic_base + EN0_RSARLO);
+			addr = (high << 8) + low;
+			if (((ring_offset + xfer_count) & 0xff) == low)
+				break;
+		} while (--tries > 0);
+	 	if (tries <= 0)
+			printk(KERN_WARNING "%s: RX transfer address mismatch,"
+				"%#4.4x (expected) vs. %#4.4x (actual).\n",
+				dev->name, ring_offset + xfer_count, addr);
+	}
+#endif
+	outb_p(ENISR_RDC, nic_base + EN0_ISR);	/* Ack intr. */
+	ei_status.dmaing &= ~0x01;
+}
+
+static void ne_block_output(struct net_device *dev, int count,
+		const unsigned char *buf, const int start_page)
+{
+	int nic_base = NE_BASE;
+	unsigned long dma_start;
+#ifdef NE_SANITY_CHECK
+	int retries = 0;
+#endif
+
+	/* Round the count up for word writes.  Do we need to do this?
+	   What effect will an odd byte count have on the 8390?
+	   I should check someday. */
+
+	if (ei_status.word16 && (count & 0x01))
+		count++;
+
+	/* This *shouldn't* happen. If it does, it's the last thing you'll see */
+	if (ei_status.dmaing)
+	{
+		printk(KERN_EMERG "%s: DMAing conflict in ne_block_output."
+			"[DMAstat:%d][irqlock:%d]\n",
+			dev->name, ei_status.dmaing, ei_status.irqlock);
+		return;
+	}
+	ei_status.dmaing |= 0x01;
+	/* We should already be in page 0, but to be safe... */
+	outb_p(E8390_PAGE0+E8390_START+E8390_NODMA, nic_base + NE_CMD);
+
+#ifdef NE_SANITY_CHECK
+retry:
+#endif
+
+#ifdef NE8390_RW_BUGFIX
+	/* Handle the read-before-write bug the same way as the
+	   Crynwr packet driver -- the NatSemi method doesn't work.
+	   Actually this doesn't always work either, but if you have
+	   problems with your NEx000 this is better than nothing! */
+
+	outb_p(0x42, nic_base + EN0_RCNTLO);
+	outb_p(0x00,   nic_base + EN0_RCNTHI);
+	outb_p(0x42, nic_base + EN0_RSARLO);
+	outb_p(0x00, nic_base + EN0_RSARHI);
+	outb_p(E8390_RREAD+E8390_START, nic_base + NE_CMD);
+	/* Make certain that the dummy read has occurred. */
+	udelay(6);
+#endif
+
+	outb_p(ENISR_RDC, nic_base + EN0_ISR);
+
+	/* Now the normal output. */
+	outb_p(count & 0xff, nic_base + EN0_RCNTLO);
+	outb_p(count >> 8,   nic_base + EN0_RCNTHI);
+	outb_p(0x00, nic_base + EN0_RSARLO);
+	outb_p(start_page, nic_base + EN0_RSARHI);
+
+	outb_p(E8390_RWRITE+E8390_START, nic_base + NE_CMD);
+	if (ei_status.word16) {
+		outsw(NE_BASE + NE_DATAPORT, buf, count>>1);
+	} else {
+		outsb(NE_BASE + NE_DATAPORT, buf, count);
+	}
+
+	dma_start = jiffies;
+
+#ifdef NE_SANITY_CHECK
+	/* This was for the ALPHA version only, but enough people have
+	   been encountering problems so it is still here. */
+
+	if (ei_debug > 1)
+	{
+		/* DMA termination address check... */
+		int addr, tries = 20;
+		do {
+			int high = inb_p(nic_base + EN0_RSARHI);
+			int low = inb_p(nic_base + EN0_RSARLO);
+			addr = (high << 8) + low;
+			if ((start_page << 8) + count == addr)
+				break;
+		} while (--tries > 0);
+
+		if (tries <= 0)
+		{
+			printk(KERN_WARNING "%s: Tx packet transfer address mismatch,"
+				"%#4.4x (expected) vs. %#4.4x (actual).\n",
+				dev->name, (start_page << 8) + count, addr);
+			if (retries++ == 0)
+				goto retry;
+		}
+	}
+#endif
+
+	while ((inb_p(nic_base + EN0_ISR) & ENISR_RDC) == 0)
+		if (time_after(jiffies, dma_start + 2*HZ/100)) {		/* 20ms */
+			printk(KERN_WARNING "%s: timeout waiting for Tx RDC.\n", dev->name);
+			ne_reset_8390(dev);
+			NS8390p_init(dev, 1);
+			break;
+		}
+
+	outb_p(ENISR_RDC, nic_base + EN0_ISR);	/* Ack intr. */
+	ei_status.dmaing &= ~0x01;
+}
+
+static int __init ne_drv_probe(struct platform_device *pdev)
+{
+	struct net_device *dev;
+	int err, this_dev = pdev->id;
+	struct resource *res;
+
+	dev = alloc_eip_netdev();
+	if (!dev)
+		return -ENOMEM;
+
+	/* ne.c doesn't populate resources in platform_device, but
+	 * rbtx4927_ne_init and rbtx4938_ne_init do register devices
+	 * with resources.
+	 */
+	res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+	if (res) {
+		dev->base_addr = res->start;
+		dev->irq = platform_get_irq(pdev, 0);
+	} else {
+		if (this_dev < 0 || this_dev >= MAX_NE_CARDS) {
+			free_netdev(dev);
+			return -EINVAL;
+		}
+		dev->base_addr = io[this_dev];
+		dev->irq = irq[this_dev];
+		dev->mem_end = bad[this_dev];
+	}
+	err = do_ne_probe(dev);
+	if (err) {
+		free_netdev(dev);
+		return err;
+	}
+	platform_set_drvdata(pdev, dev);
+
+	/* Update with any values found by probing, don't update if
+	 * resources were specified.
+	 */
+	if (!res) {
+		io[this_dev] = dev->base_addr;
+		irq[this_dev] = dev->irq;
+	}
+	return 0;
+}
+
+static int ne_drv_remove(struct platform_device *pdev)
+{
+	struct net_device *dev = platform_get_drvdata(pdev);
+
+	if (dev) {
+		struct pnp_dev *idev = (struct pnp_dev *)ei_status.priv;
+		netif_device_detach(dev);
+		unregister_netdev(dev);
+		if (idev)
+			pnp_device_detach(idev);
+		/* Careful ne_drv_remove can be called twice, once from
+		 * the platform_driver.remove and again when the
+		 * platform_device is being removed.
+		 */
+		ei_status.priv = 0;
+		free_irq(dev->irq, dev);
+		release_region(dev->base_addr, NE_IO_EXTENT);
+		free_netdev(dev);
+		platform_set_drvdata(pdev, NULL);
+	}
+	return 0;
+}
+
+/* Remove unused devices or all if true. */
+static void ne_loop_rm_unreg(int all)
+{
+	int this_dev;
+	struct platform_device *pdev;
+	for (this_dev = 0; this_dev < MAX_NE_CARDS; this_dev++) {
+		pdev = pdev_ne[this_dev];
+		/* No network device == unused */
+		if (pdev && (!platform_get_drvdata(pdev) || all)) {
+			ne_drv_remove(pdev);
+			platform_device_unregister(pdev);
+			pdev_ne[this_dev] = NULL;
+		}
+	}
+}
+
+#ifdef CONFIG_PM
+static int ne_drv_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	struct net_device *dev = platform_get_drvdata(pdev);
+
+	if (netif_running(dev)) {
+		struct pnp_dev *idev = (struct pnp_dev *)ei_status.priv;
+		netif_device_detach(dev);
+		if (idev)
+			pnp_stop_dev(idev);
+	}
+	return 0;
+}
+
+static int ne_drv_resume(struct platform_device *pdev)
+{
+	struct net_device *dev = platform_get_drvdata(pdev);
+
+	if (netif_running(dev)) {
+		struct pnp_dev *idev = (struct pnp_dev *)ei_status.priv;
+		if (idev)
+			pnp_start_dev(idev);
+		ne_reset_8390(dev);
+		NS8390p_init(dev, 1);
+		netif_device_attach(dev);
+	}
+	return 0;
+}
+#else
+#define ne_drv_suspend NULL
+#define ne_drv_resume NULL
+#endif
+
+static struct platform_driver ne_driver = {
+	.remove		= ne_drv_remove,
+	.suspend	= ne_drv_suspend,
+	.resume		= ne_drv_resume,
+	.driver		= {
+		.name	= DRV_NAME,
+		.owner	= THIS_MODULE,
+	},
+};
+
+static void __init ne_add_devices(void)
+{
+	int this_dev;
+	struct platform_device *pdev;
+
+	for (this_dev = 0; this_dev < MAX_NE_CARDS; this_dev++) {
+		if (pdev_ne[this_dev])
+			continue;
+		pdev = platform_device_register_simple(
+			DRV_NAME, this_dev, NULL, 0);
+		if (IS_ERR(pdev))
+			continue;
+		pdev_ne[this_dev] = pdev;
+	}
+}
+
+#ifdef MODULE
+int __init init_module(void)
+{
+	int retval;
+	ne_add_devices();
+	retval = platform_driver_probe(&ne_driver, ne_drv_probe);
+	if (retval) {
+		if (io[0] == 0)
+			printk(KERN_NOTICE "ne.c: You must supply \"io=0xNNN\""
+				" value(s) for ISA cards.\n");
+		ne_loop_rm_unreg(1);
+		return retval;
+	}
+
+	/* Unregister unused platform_devices. */
+	ne_loop_rm_unreg(0);
+	return retval;
+}
+#else /* MODULE */
+static int __init ne_init(void)
+{
+	int retval = platform_driver_probe(&ne_driver, ne_drv_probe);
+
+	/* Unregister unused platform_devices. */
+	ne_loop_rm_unreg(0);
+	return retval;
+}
+module_init(ne_init);
+
+struct net_device * __init ne_probe(int unit)
+{
+	int this_dev;
+	struct net_device *dev;
+
+	/* Find an empty slot, that is no net_device and zero io port. */
+	this_dev = 0;
+	while ((pdev_ne[this_dev] && platform_get_drvdata(pdev_ne[this_dev])) ||
+		io[this_dev]) {
+		if (++this_dev == MAX_NE_CARDS)
+			return ERR_PTR(-ENOMEM);
+	}
+
+	/* Get irq, io from kernel command line */
+	dev = alloc_eip_netdev();
+	if (!dev)
+		return ERR_PTR(-ENOMEM);
+
+	sprintf(dev->name, "eth%d", unit);
+	netdev_boot_setup_check(dev);
+
+	io[this_dev] = dev->base_addr;
+	irq[this_dev] = dev->irq;
+	bad[this_dev] = dev->mem_end;
+
+	free_netdev(dev);
+
+	ne_add_devices();
+
+	/* return the first device found */
+	for (this_dev = 0; this_dev < MAX_NE_CARDS; this_dev++) {
+		if (pdev_ne[this_dev]) {
+			dev = platform_get_drvdata(pdev_ne[this_dev]);
+			if (dev)
+				return dev;
+		}
+	}
+
+	return ERR_PTR(-ENODEV);
+}
+#endif /* MODULE */
+
+static void __exit ne_exit(void)
+{
+	platform_driver_unregister(&ne_driver);
+	ne_loop_rm_unreg(1);
+}
+module_exit(ne_exit);
diff --git a/drivers/net/ethernet/8390/ne2.c b/drivers/net/ethernet/8390/ne2.c
new file mode 100644
index 0000000..70cdc69
--- /dev/null
+++ b/drivers/net/ethernet/8390/ne2.c
@@ -0,0 +1,799 @@
+/* ne2.c: A NE/2 Ethernet Driver for Linux. */
+/*
+   Based on the NE2000 driver written by Donald Becker (1992-94).
+   modified by Wim Dumon (Apr 1996)
+
+   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 wimpie@linux.cc.kuleuven.ac.be
+
+   Currently supported: NE/2
+   This patch was never tested on other MCA-ethernet adapters, but it
+   might work. Just give it a try and let me know if you have problems.
+   Also mail me if it really works, please!
+
+   Changelog:
+   Mon Feb  3 16:26:02 MET 1997
+   - adapted the driver to work with the 2.1.25 kernel
+   - multiple ne2 support (untested)
+   - module support (untested)
+
+   Fri Aug 28 00:18:36 CET 1998 (David Weinehall)
+   - fixed a few minor typos
+   - made the MODULE_PARM conditional (it only works with the v2.1.x kernels)
+   - fixed the module support (Now it's working...)
+
+   Mon Sep  7 19:01:44 CET 1998 (David Weinehall)
+   - added support for Arco Electronics AE/2-card (experimental)
+
+   Mon Sep 14 09:53:42 CET 1998 (David Weinehall)
+   - added support for Compex ENET-16MC/P (experimental)
+
+   Tue Sep 15 16:21:12 CET 1998 (David Weinehall, Magnus Jonsson, Tomas Ogren)
+   - Miscellaneous bugfixes
+
+   Tue Sep 19 16:21:12 CET 1998 (Magnus Jonsson)
+   - Cleanup
+
+   Wed Sep 23 14:33:34 CET 1998 (David Weinehall)
+   - Restructuring and rewriting for v2.1.x compliance
+
+   Wed Oct 14 17:19:21 CET 1998 (David Weinehall)
+   - Added code that unregisters irq and proc-info
+   - Version# bump
+
+   Mon Nov 16 15:28:23 CET 1998 (Wim Dumon)
+   - pass 'dev' as last parameter of request_irq in stead of 'NULL'
+
+   Wed Feb  7 21:24:00 CET 2001 (Alfred Arnold)
+   - added support for the D-Link DE-320CT
+
+   *    WARNING
+	-------
+	This is alpha-test software.  It is not guaranteed to work. As a
+	matter of fact, I'm quite sure there are *LOTS* of bugs in here. I
+	would like to hear from you if you use this driver, even if it works.
+	If it doesn't work, be sure to send me a mail with the problems !
+*/
+
+static const char *version = "ne2.c:v0.91 Nov 16 1998 Wim Dumon <wimpie@kotnet.org>\n";
+
+#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/errno.h>
+#include <linux/init.h>
+#include <linux/mca-legacy.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/bitops.h>
+#include <linux/jiffies.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#include "8390.h"
+
+#define DRV_NAME "ne2"
+
+/* Some defines that people can play with if so inclined. */
+
+/* Do we perform extra sanity checks on stuff ? */
+/* #define NE_SANITY_CHECK */
+
+/* Do we implement the read before write bugfix ? */
+/* #define NE_RW_BUGFIX */
+
+/* Do we have a non std. amount of memory? (in units of 256 byte pages) */
+/* #define PACKETBUF_MEMSIZE	0x40 */
+
+
+/* ---- No user-serviceable parts below ---- */
+
+#define NE_BASE	 (dev->base_addr)
+#define NE_CMD	 	0x00
+#define NE_DATAPORT	0x10	/* NatSemi-defined port window offset. */
+#define NE_RESET	0x20	/* Issue a read to reset, a write to clear. */
+#define NE_IO_EXTENT	0x30
+
+#define NE1SM_START_PG	0x20	/* First page of TX buffer */
+#define NE1SM_STOP_PG 	0x40	/* Last page +1 of RX ring */
+#define NESM_START_PG	0x40	/* First page of TX buffer */
+#define NESM_STOP_PG	0x80	/* Last page +1 of RX ring */
+
+/* From the .ADF file: */
+static unsigned int addresses[7] __initdata =
+		{0x1000, 0x2020, 0x8020, 0xa0a0, 0xb0b0, 0xc0c0, 0xc3d0};
+static int irqs[4] __initdata = {3, 4, 5, 9};
+
+/* From the D-Link ADF file: */
+static unsigned int dlink_addresses[4] __initdata =
+                {0x300, 0x320, 0x340, 0x360};
+static int dlink_irqs[8] __initdata = {3, 4, 5, 9, 10, 11, 14, 15};
+
+struct ne2_adapters_t {
+	unsigned int	id;
+	char		*name;
+};
+
+static struct ne2_adapters_t ne2_adapters[] __initdata = {
+	{ 0x6354, "Arco Ethernet Adapter AE/2" },
+	{ 0x70DE, "Compex ENET-16 MC/P" },
+	{ 0x7154, "Novell Ethernet Adapter NE/2" },
+        { 0x56ea, "D-Link DE-320CT" },
+	{ 0x0000, NULL }
+};
+
+extern int netcard_probe(struct net_device *dev);
+
+static int ne2_probe1(struct net_device *dev, int slot);
+
+static void ne_reset_8390(struct net_device *dev);
+static void ne_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
+		int ring_page);
+static void ne_block_input(struct net_device *dev, int count,
+		struct sk_buff *skb, int ring_offset);
+static void ne_block_output(struct net_device *dev, const int count,
+		const unsigned char *buf, const int start_page);
+
+
+/*
+ * special code to read the DE-320's MAC address EEPROM.  In contrast to a
+ * standard NE design, this is a serial EEPROM (93C46) that has to be read
+ * bit by bit.  The EEPROM cotrol port at base + 0x1e has the following
+ * layout:
+ *
+ * Bit 0 = Data out (read from EEPROM)
+ * Bit 1 = Data in  (write to EEPROM)
+ * Bit 2 = Clock
+ * Bit 3 = Chip Select
+ * Bit 7 = ~50 kHz clock for defined delays
+ *
+ */
+
+static void __init dlink_put_eeprom(unsigned char value, unsigned int addr)
+{
+	int z;
+	unsigned char v1, v2;
+
+	/* write the value to the NIC EEPROM register */
+
+	outb(value, addr + 0x1e);
+
+	/* now wait the clock line to toggle twice.  Effectively, we are
+	   waiting (at least) for one clock cycle */
+
+	for (z = 0; z < 2; z++) {
+		do {
+			v1 = inb(addr + 0x1e);
+			v2 = inb(addr + 0x1e);
+		}
+		while (!((v1 ^ v2) & 0x80));
+	}
+}
+
+static void __init dlink_send_eeprom_bit(unsigned int bit, unsigned int addr)
+{
+	/* shift data bit into correct position */
+
+	bit = bit << 1;
+
+	/* write value, keep clock line high for two cycles */
+
+	dlink_put_eeprom(0x09 | bit, addr);
+	dlink_put_eeprom(0x0d | bit, addr);
+	dlink_put_eeprom(0x0d | bit, addr);
+	dlink_put_eeprom(0x09 | bit, addr);
+}
+
+static void __init dlink_send_eeprom_word(unsigned int value, unsigned int len, unsigned int addr)
+{
+	int z;
+
+	/* adjust bits so that they are left-aligned in a 16-bit-word */
+
+	value = value << (16 - len);
+
+	/* shift bits out to the EEPROM */
+
+	for (z = 0; z < len; z++) {
+		dlink_send_eeprom_bit((value & 0x8000) >> 15, addr);
+		value = value << 1;
+	}
+}
+
+static unsigned int __init dlink_get_eeprom(unsigned int eeaddr, unsigned int addr)
+{
+	int z;
+	unsigned int value = 0;
+
+	/* pull the CS line low for a moment.  This resets the EEPROM-
+	   internal logic, and makes it ready for a new command. */
+
+	dlink_put_eeprom(0x01, addr);
+	dlink_put_eeprom(0x09, addr);
+
+	/* send one start bit, read command (1 - 0), plus the address to
+           the EEPROM */
+
+	dlink_send_eeprom_word(0x0180 | (eeaddr & 0x3f), 9, addr);
+
+	/* get the data word.  We clock by sending 0s to the EEPROM, which
+	   get ignored during the read process */
+
+	for (z = 0; z < 16; z++) {
+		dlink_send_eeprom_bit(0, addr);
+		value = (value << 1) | (inb(addr + 0x1e) & 0x01);
+	}
+
+	return value;
+}
+
+/*
+ * Note that at boot, this probe only picks up one card at a time.
+ */
+
+static int __init do_ne2_probe(struct net_device *dev)
+{
+	static int current_mca_slot = -1;
+	int i;
+	int adapter_found = 0;
+
+	/* 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; (ne2_adapters[i].name != NULL) && !adapter_found; i++) {
+		current_mca_slot =
+			mca_find_unused_adapter(ne2_adapters[i].id, 0);
+
+		if((current_mca_slot != MCA_NOTFOUND) && !adapter_found) {
+			int res;
+			mca_set_adapter_name(current_mca_slot,
+					ne2_adapters[i].name);
+			mca_mark_as_used(current_mca_slot);
+
+			res = ne2_probe1(dev, current_mca_slot);
+			if (res)
+				mca_mark_as_unused(current_mca_slot);
+			return res;
+		}
+	}
+	return -ENODEV;
+}
+
+#ifndef MODULE
+struct net_device * __init ne2_probe(int unit)
+{
+	struct net_device *dev = alloc_eip_netdev();
+	int err;
+
+	if (!dev)
+		return ERR_PTR(-ENOMEM);
+
+	sprintf(dev->name, "eth%d", unit);
+	netdev_boot_setup_check(dev);
+
+	err = do_ne2_probe(dev);
+	if (err)
+		goto out;
+	return dev;
+out:
+	free_netdev(dev);
+	return ERR_PTR(err);
+}
+#endif
+
+static int ne2_procinfo(char *buf, int slot, struct net_device *dev)
+{
+	int len=0;
+
+	len += sprintf(buf+len, "The NE/2 Ethernet Adapter\n" );
+	len += sprintf(buf+len, "Driver written by Wim Dumon ");
+	len += sprintf(buf+len, "<wimpie@kotnet.org>\n");
+	len += sprintf(buf+len, "Modified by ");
+	len += sprintf(buf+len, "David Weinehall <tao@acc.umu.se>\n");
+	len += sprintf(buf+len, "and by Magnus Jonsson <bigfoot@acc.umu.se>\n");
+	len += sprintf(buf+len, "Based on the original NE2000 drivers\n" );
+	len += sprintf(buf+len, "Base IO: %#x\n", (unsigned int)dev->base_addr);
+	len += sprintf(buf+len, "IRQ    : %d\n", dev->irq);
+	len += sprintf(buf+len, "HW addr : %pM\n", dev->dev_addr);
+
+	return len;
+}
+
+static int __init ne2_probe1(struct net_device *dev, int slot)
+{
+	int i, base_addr, irq, retval;
+	unsigned char POS;
+	unsigned char SA_prom[32];
+	const char *name = "NE/2";
+	int start_page, stop_page;
+	static unsigned version_printed;
+
+	if (ei_debug && version_printed++ == 0)
+		printk(version);
+
+	printk("NE/2 ethercard found in slot %d:", slot);
+
+	/* Read base IO and IRQ from the POS-registers */
+	POS = mca_read_stored_pos(slot, 2);
+	if(!(POS % 2)) {
+		printk(" disabled.\n");
+		return -ENODEV;
+	}
+
+	/* handle different POS register structure for D-Link card */
+
+	if (mca_read_stored_pos(slot, 0) == 0xea) {
+		base_addr = dlink_addresses[(POS >> 5) & 0x03];
+		irq = dlink_irqs[(POS >> 2) & 0x07];
+	}
+        else {
+		i = (POS & 0xE)>>1;
+		/* printk("Halleluja sdog, als er na de pijl een 1 staat is 1 - 1 == 0"
+	   	" en zou het moeten werken -> %d\n", i);
+	   	The above line was for remote testing, thanx to sdog ... */
+		base_addr = addresses[i - 1];
+		irq = irqs[(POS & 0x60)>>5];
+	}
+
+	if (!request_region(base_addr, NE_IO_EXTENT, DRV_NAME))
+		return -EBUSY;
+
+#ifdef DEBUG
+	printk("POS info : pos 2 = %#x ; base = %#x ; irq = %ld\n", POS,
+			base_addr, irq);
+#endif
+
+#ifndef CRYNWR_WAY
+	/* Reset the card the way they do it in the Crynwr packet driver */
+	for (i=0; i<8; i++)
+		outb(0x0, base_addr + NE_RESET);
+	inb(base_addr + NE_RESET);
+	outb(0x21, base_addr + NE_CMD);
+	if (inb(base_addr + NE_CMD) != 0x21) {
+		printk("NE/2 adapter not responding\n");
+		retval = -ENODEV;
+		goto out;
+	}
+
+	/* In the crynwr sources they do a RAM-test here. I skip it. I suppose
+	   my RAM is okay.  Suppose your memory is broken.  Then this test
+	   should fail and you won't be able to use your card.  But if I do not
+	   test, you won't be able to use your card, neither.  So this test
+	   won't help you. */
+
+#else  /* _I_ never tested it this way .. Go ahead and try ...*/
+	/* Reset card. Who knows what dain-bramaged state it was left in. */
+	{
+		unsigned long reset_start_time = jiffies;
+
+		/* DON'T change these to inb_p/outb_p or reset will fail on
+		   clones.. */
+		outb(inb(base_addr + NE_RESET), base_addr + NE_RESET);
+
+		while ((inb_p(base_addr + EN0_ISR) & ENISR_RESET) == 0)
+			if (time_after(jiffies, reset_start_time + 2*HZ/100)) {
+				printk(" not found (no reset ack).\n");
+				retval = -ENODEV;
+				goto out;
+			}
+
+		outb_p(0xff, base_addr + EN0_ISR);         /* Ack all intr. */
+	}
+#endif
+
+
+	/* Read the 16 bytes of station address PROM.
+	   We must first initialize registers, similar to
+	   NS8390p_init(eifdev, 0).
+	   We can't reliably read the SAPROM address without this.
+	   (I learned the hard way!). */
+	{
+		struct {
+			unsigned char value, offset;
+		} program_seq[] = {
+						/* Select page 0 */
+			{E8390_NODMA+E8390_PAGE0+E8390_STOP, E8390_CMD},
+			{0x49,	EN0_DCFG},  /* Set WORD-wide (0x49) access. */
+			{0x00,	EN0_RCNTLO},  /* Clear the count regs. */
+			{0x00,	EN0_RCNTHI},
+			{0x00,	EN0_IMR},  /* Mask completion irq. */
+			{0xFF,	EN0_ISR},
+			{E8390_RXOFF, EN0_RXCR},  /* 0x20  Set to monitor */
+			{E8390_TXOFF, EN0_TXCR},  /* 0x02  and loopback mode. */
+			{32,	EN0_RCNTLO},
+			{0x00,	EN0_RCNTHI},
+			{0x00,	EN0_RSARLO},  /* DMA starting at 0x0000. */
+			{0x00,	EN0_RSARHI},
+			{E8390_RREAD+E8390_START, E8390_CMD},
+		};
+
+		for (i = 0; i < ARRAY_SIZE(program_seq); i++)
+			outb_p(program_seq[i].value, base_addr +
+				program_seq[i].offset);
+
+	}
+	for(i = 0; i < 6 /*sizeof(SA_prom)*/; i+=1) {
+		SA_prom[i] = inb(base_addr + NE_DATAPORT);
+	}
+
+	/* I don't know whether the previous sequence includes the general
+           board reset procedure, so better don't omit it and just overwrite
+           the garbage read from a DE-320 with correct stuff. */
+
+	if (mca_read_stored_pos(slot, 0) == 0xea) {
+		unsigned int v;
+
+		for (i = 0; i < 3; i++) {
+ 			v = dlink_get_eeprom(i, base_addr);
+			SA_prom[(i << 1)    ] = v & 0xff;
+			SA_prom[(i << 1) + 1] = (v >> 8) & 0xff;
+		}
+	}
+
+	start_page = NESM_START_PG;
+	stop_page = NESM_STOP_PG;
+
+	dev->irq=irq;
+
+	/* Snarf the interrupt now.  There's no point in waiting since we cannot
+	   share and the board will usually be enabled. */
+	retval = request_irq(dev->irq, eip_interrupt, 0, DRV_NAME, dev);
+	if (retval) {
+		printk (" unable to get IRQ %d (irqval=%d).\n",
+				dev->irq, retval);
+		goto out;
+	}
+
+	dev->base_addr = base_addr;
+
+	for(i = 0; i < ETHER_ADDR_LEN; i++)
+		dev->dev_addr[i] = SA_prom[i];
+
+	printk(" %pM\n", dev->dev_addr);
+
+	printk("%s: %s found at %#x, using IRQ %d.\n",
+			dev->name, name, base_addr, dev->irq);
+
+	mca_set_adapter_procfn(slot, (MCA_ProcFn) ne2_procinfo, dev);
+
+	ei_status.name = name;
+	ei_status.tx_start_page = start_page;
+	ei_status.stop_page = stop_page;
+	ei_status.word16 = (2 == 2);
+
+	ei_status.rx_start_page = start_page + TX_PAGES;
+#ifdef PACKETBUF_MEMSIZE
+	/* Allow the packet buffer size to be overridden by know-it-alls. */
+	ei_status.stop_page = ei_status.tx_start_page + PACKETBUF_MEMSIZE;
+#endif
+
+	ei_status.reset_8390 = &ne_reset_8390;
+	ei_status.block_input = &ne_block_input;
+	ei_status.block_output = &ne_block_output;
+	ei_status.get_8390_hdr = &ne_get_8390_hdr;
+
+	ei_status.priv = slot;
+
+	dev->netdev_ops = &eip_netdev_ops;
+	NS8390p_init(dev, 0);
+
+	retval = register_netdev(dev);
+	if (retval)
+		goto out1;
+	return 0;
+out1:
+	mca_set_adapter_procfn( ei_status.priv, NULL, NULL);
+	free_irq(dev->irq, dev);
+out:
+	release_region(base_addr, NE_IO_EXTENT);
+	return retval;
+}
+
+/* Hard reset the card.  This used to pause for the same period that a
+   8390 reset command required, but that shouldn't be necessary. */
+static void ne_reset_8390(struct net_device *dev)
+{
+	unsigned long reset_start_time = jiffies;
+
+	if (ei_debug > 1)
+		printk("resetting the 8390 t=%ld...", jiffies);
+
+	/* DON'T change these to inb_p/outb_p or reset will fail on clones. */
+	outb(inb(NE_BASE + NE_RESET), NE_BASE + NE_RESET);
+
+	ei_status.txing = 0;
+	ei_status.dmaing = 0;
+
+	/* This check _should_not_ be necessary, omit eventually. */
+	while ((inb_p(NE_BASE+EN0_ISR) & ENISR_RESET) == 0)
+		if (time_after(jiffies, reset_start_time + 2*HZ/100)) {
+			printk("%s: ne_reset_8390() did not complete.\n",
+					dev->name);
+			break;
+		}
+	outb_p(ENISR_RESET, NE_BASE + EN0_ISR);	/* Ack intr. */
+}
+
+/* Grab the 8390 specific header. Similar to the block_input routine, but
+   we don't need to be concerned with ring wrap as the header will be at
+   the start of a page, so we optimize accordingly. */
+
+static void ne_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
+		int ring_page)
+{
+
+	int nic_base = dev->base_addr;
+
+	/* This *shouldn't* happen.
+	   If it does, it's the last thing you'll see */
+	if (ei_status.dmaing) {
+		printk("%s: DMAing conflict in ne_get_8390_hdr "
+				"[DMAstat:%d][irqlock:%d].\n",
+				dev->name, ei_status.dmaing, ei_status.irqlock);
+		return;
+	}
+
+	ei_status.dmaing |= 0x01;
+	outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base+ NE_CMD);
+	outb_p(sizeof(struct e8390_pkt_hdr), nic_base + EN0_RCNTLO);
+	outb_p(0, nic_base + EN0_RCNTHI);
+	outb_p(0, nic_base + EN0_RSARLO);		/* On page boundary */
+	outb_p(ring_page, nic_base + EN0_RSARHI);
+	outb_p(E8390_RREAD+E8390_START, nic_base + NE_CMD);
+
+	if (ei_status.word16)
+		insw(NE_BASE + NE_DATAPORT, hdr,
+				sizeof(struct e8390_pkt_hdr)>>1);
+	else
+		insb(NE_BASE + NE_DATAPORT, hdr,
+				sizeof(struct e8390_pkt_hdr));
+
+	outb_p(ENISR_RDC, nic_base + EN0_ISR);	/* Ack intr. */
+	ei_status.dmaing &= ~0x01;
+}
+
+/* Block input and output, similar to the Crynwr packet driver.  If you
+   are porting to a new ethercard, look at the packet driver source for
+   hints. The NEx000 doesn't share the on-board packet memory -- you have
+   to put the packet out through the "remote DMA" dataport using outb. */
+
+static void ne_block_input(struct net_device *dev, int count, struct sk_buff *skb,
+		int ring_offset)
+{
+#ifdef NE_SANITY_CHECK
+	int xfer_count = count;
+#endif
+	int nic_base = dev->base_addr;
+	char *buf = skb->data;
+
+	/* This *shouldn't* happen.
+	   If it does, it's the last thing you'll see */
+	if (ei_status.dmaing) {
+		printk("%s: DMAing conflict in ne_block_input "
+				"[DMAstat:%d][irqlock:%d].\n",
+				dev->name, ei_status.dmaing, ei_status.irqlock);
+		return;
+	}
+	ei_status.dmaing |= 0x01;
+	outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base+ NE_CMD);
+	outb_p(count & 0xff, nic_base + EN0_RCNTLO);
+	outb_p(count >> 8, nic_base + EN0_RCNTHI);
+	outb_p(ring_offset & 0xff, nic_base + EN0_RSARLO);
+	outb_p(ring_offset >> 8, nic_base + EN0_RSARHI);
+	outb_p(E8390_RREAD+E8390_START, nic_base + NE_CMD);
+	if (ei_status.word16) {
+		insw(NE_BASE + NE_DATAPORT,buf,count>>1);
+		if (count & 0x01) {
+			buf[count-1] = inb(NE_BASE + NE_DATAPORT);
+#ifdef NE_SANITY_CHECK
+			xfer_count++;
+#endif
+		}
+	} else {
+		insb(NE_BASE + NE_DATAPORT, buf, count);
+	}
+
+#ifdef NE_SANITY_CHECK
+	/* This was for the ALPHA version only, but enough people have
+	   been encountering problems so it is still here.  If you see
+	   this message you either 1) have a slightly incompatible clone
+	   or 2) have noise/speed problems with your bus. */
+	if (ei_debug > 1) {	/* DMA termination address check... */
+		int addr, tries = 20;
+		do {
+			/* DON'T check for 'inb_p(EN0_ISR) & ENISR_RDC' here
+			   -- it's broken for Rx on some cards! */
+			int high = inb_p(nic_base + EN0_RSARHI);
+			int low = inb_p(nic_base + EN0_RSARLO);
+			addr = (high << 8) + low;
+			if (((ring_offset + xfer_count) & 0xff) == low)
+				break;
+		} while (--tries > 0);
+		if (tries <= 0)
+			printk("%s: RX transfer address mismatch,"
+				"%#4.4x (expected) vs. %#4.4x (actual).\n",
+				dev->name, ring_offset + xfer_count, addr);
+	}
+#endif
+	outb_p(ENISR_RDC, nic_base + EN0_ISR);	/* Ack intr. */
+	ei_status.dmaing &= ~0x01;
+}
+
+static void ne_block_output(struct net_device *dev, int count,
+		const unsigned char *buf, const int start_page)
+{
+	int nic_base = NE_BASE;
+	unsigned long dma_start;
+#ifdef NE_SANITY_CHECK
+	int retries = 0;
+#endif
+
+	/* Round the count up for word writes. Do we need to do this?
+	   What effect will an odd byte count have on the 8390?
+	   I should check someday. */
+	if (ei_status.word16 && (count & 0x01))
+		count++;
+
+	/* This *shouldn't* happen.
+	   If it does, it's the last thing you'll see */
+	if (ei_status.dmaing) {
+		printk("%s: DMAing conflict in ne_block_output."
+				"[DMAstat:%d][irqlock:%d]\n",
+				dev->name, ei_status.dmaing, ei_status.irqlock);
+		return;
+	}
+	ei_status.dmaing |= 0x01;
+	/* We should already be in page 0, but to be safe... */
+	outb_p(E8390_PAGE0+E8390_START+E8390_NODMA, nic_base + NE_CMD);
+
+#ifdef NE_SANITY_CHECK
+retry:
+#endif
+
+#ifdef NE8390_RW_BUGFIX
+	/* Handle the read-before-write bug the same way as the
+	   Crynwr packet driver -- the NatSemi method doesn't work.
+	   Actually this doesn't always work either, but if you have
+	   problems with your NEx000 this is better than nothing! */
+	outb_p(0x42, nic_base + EN0_RCNTLO);
+	outb_p(0x00, nic_base + EN0_RCNTHI);
+	outb_p(0x42, nic_base + EN0_RSARLO);
+	outb_p(0x00, nic_base + EN0_RSARHI);
+	outb_p(E8390_RREAD+E8390_START, nic_base + NE_CMD);
+	/* Make certain that the dummy read has occurred. */
+	SLOW_DOWN_IO;
+	SLOW_DOWN_IO;
+	SLOW_DOWN_IO;
+#endif
+
+	outb_p(ENISR_RDC, nic_base + EN0_ISR);
+
+	/* Now the normal output. */
+	outb_p(count & 0xff, nic_base + EN0_RCNTLO);
+	outb_p(count >> 8,   nic_base + EN0_RCNTHI);
+	outb_p(0x00, nic_base + EN0_RSARLO);
+	outb_p(start_page, nic_base + EN0_RSARHI);
+
+	outb_p(E8390_RWRITE+E8390_START, nic_base + NE_CMD);
+	if (ei_status.word16) {
+		outsw(NE_BASE + NE_DATAPORT, buf, count>>1);
+	} else {
+		outsb(NE_BASE + NE_DATAPORT, buf, count);
+	}
+
+	dma_start = jiffies;
+
+#ifdef NE_SANITY_CHECK
+	/* This was for the ALPHA version only, but enough people have
+	   been encountering problems so it is still here. */
+
+	if (ei_debug > 1) {		/* DMA termination address check... */
+		int addr, tries = 20;
+		do {
+			int high = inb_p(nic_base + EN0_RSARHI);
+			int low = inb_p(nic_base + EN0_RSARLO);
+			addr = (high << 8) + low;
+			if ((start_page << 8) + count == addr)
+				break;
+		} while (--tries > 0);
+		if (tries <= 0) {
+			printk("%s: Tx packet transfer address mismatch,"
+					"%#4.4x (expected) vs. %#4.4x (actual).\n",
+					dev->name, (start_page << 8) + count, addr);
+			if (retries++ == 0)
+				goto retry;
+		}
+	}
+#endif
+
+	while ((inb_p(nic_base + EN0_ISR) & ENISR_RDC) == 0)
+		if (time_after(jiffies, dma_start + 2*HZ/100)) {		/* 20ms */
+			printk("%s: timeout waiting for Tx RDC.\n", dev->name);
+			ne_reset_8390(dev);
+			NS8390p_init(dev, 1);
+			break;
+		}
+
+	outb_p(ENISR_RDC, nic_base + EN0_ISR);	/* Ack intr. */
+	ei_status.dmaing &= ~0x01;
+}
+
+
+#ifdef MODULE
+#define MAX_NE_CARDS	4	/* Max number of NE cards per module */
+static struct net_device *dev_ne[MAX_NE_CARDS];
+static int io[MAX_NE_CARDS];
+static int irq[MAX_NE_CARDS];
+static int bad[MAX_NE_CARDS];	/* 0xbad = bad sig or no reset ack */
+MODULE_LICENSE("GPL");
+
+module_param_array(io, int, NULL, 0);
+module_param_array(irq, int, NULL, 0);
+module_param_array(bad, int, NULL, 0);
+MODULE_PARM_DESC(io, "(ignored)");
+MODULE_PARM_DESC(irq, "(ignored)");
+MODULE_PARM_DESC(bad, "(ignored)");
+
+/* Module code fixed by David Weinehall */
+
+int __init init_module(void)
+{
+	struct net_device *dev;
+	int this_dev, found = 0;
+
+	for (this_dev = 0; this_dev < MAX_NE_CARDS; this_dev++) {
+		dev = alloc_eip_netdev();
+		if (!dev)
+			break;
+		dev->irq = irq[this_dev];
+		dev->mem_end = bad[this_dev];
+		dev->base_addr = io[this_dev];
+		if (do_ne2_probe(dev) == 0) {
+			dev_ne[found++] = dev;
+			continue;
+		}
+		free_netdev(dev);
+		break;
+	}
+	if (found)
+		return 0;
+	printk(KERN_WARNING "ne2.c: No NE/2 card found\n");
+	return -ENXIO;
+}
+
+static void cleanup_card(struct net_device *dev)
+{
+	mca_mark_as_unused(ei_status.priv);
+	mca_set_adapter_procfn( ei_status.priv, NULL, NULL);
+	free_irq(dev->irq, dev);
+	release_region(dev->base_addr, NE_IO_EXTENT);
+}
+
+void __exit cleanup_module(void)
+{
+	int this_dev;
+
+	for (this_dev = 0; this_dev < MAX_NE_CARDS; this_dev++) {
+		struct net_device *dev = dev_ne[this_dev];
+		if (dev) {
+			unregister_netdev(dev);
+			cleanup_card(dev);
+			free_netdev(dev);
+		}
+	}
+}
+#endif /* MODULE */
diff --git a/drivers/net/ethernet/8390/ne2k-pci.c b/drivers/net/ethernet/8390/ne2k-pci.c
new file mode 100644
index 0000000..3c333cb
--- /dev/null
+++ b/drivers/net/ethernet/8390/ne2k-pci.c
@@ -0,0 +1,726 @@
+/* ne2k-pci.c: A NE2000 clone on PCI bus driver for Linux. */
+/*
+	A Linux device driver for PCI NE2000 clones.
+
+	Authors and other copyright holders:
+	1992-2000 by Donald Becker, NE2000 core and various modifications.
+	1995-1998 by Paul Gortmaker, core modifications and PCI support.
+	Copyright 1993 assigned to the 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 (GPL), incorporated herein by reference.
+	Drivers based on or derived from this code fall under the GPL and must
+	retain the authorship, copyright and license notice.  This file is not
+	a complete program and may only be used when the entire operating
+	system is licensed under the GPL.
+
+	The author may be reached as becker@scyld.com, or C/O
+	Scyld Computing Corporation
+	410 Severn Ave., Suite 210
+	Annapolis MD 21403
+
+	Issues remaining:
+	People are making PCI ne2000 clones! Oh the horror, the horror...
+	Limited full-duplex support.
+*/
+
+#define DRV_NAME	"ne2k-pci"
+#define DRV_VERSION	"1.03"
+#define DRV_RELDATE	"9/22/2003"
+
+
+/* The user-configurable values.
+   These may be modified when a driver module is loaded.*/
+
+static int debug = 1;			/* 1 normal messages, 0 quiet .. 7 verbose. */
+
+#define MAX_UNITS 8				/* More are supported, limit only on options */
+/* Used to pass the full-duplex flag, etc. */
+static int full_duplex[MAX_UNITS];
+static int options[MAX_UNITS];
+
+/* Force a non std. amount of memory.  Units are 256 byte pages. */
+/* #define PACKETBUF_MEMSIZE	0x40 */
+
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/ethtool.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+
+#include "8390.h"
+
+/* These identify the driver base version and may not be removed. */
+static const char version[] __devinitconst =
+	KERN_INFO DRV_NAME ".c:v" DRV_VERSION " " DRV_RELDATE
+	" D. Becker/P. Gortmaker\n";
+
+#if defined(__powerpc__)
+#define inl_le(addr)  le32_to_cpu(inl(addr))
+#define inw_le(addr)  le16_to_cpu(inw(addr))
+#endif
+
+#define PFX DRV_NAME ": "
+
+MODULE_AUTHOR("Donald Becker / Paul Gortmaker");
+MODULE_DESCRIPTION("PCI NE2000 clone driver");
+MODULE_LICENSE("GPL");
+
+module_param(debug, int, 0);
+module_param_array(options, int, NULL, 0);
+module_param_array(full_duplex, int, NULL, 0);
+MODULE_PARM_DESC(debug, "debug level (1-2)");
+MODULE_PARM_DESC(options, "Bit 5: full duplex");
+MODULE_PARM_DESC(full_duplex, "full duplex setting(s) (1)");
+
+/* Some defines that people can play with if so inclined. */
+
+/* Use 32 bit data-movement operations instead of 16 bit. */
+#define USE_LONGIO
+
+/* Do we implement the read before write bugfix ? */
+/* #define NE_RW_BUGFIX */
+
+/* Flags.  We rename an existing ei_status field to store flags! */
+/* Thus only the low 8 bits are usable for non-init-time flags. */
+#define ne2k_flags reg0
+enum {
+	ONLY_16BIT_IO=8, ONLY_32BIT_IO=4,	/* Chip can do only 16/32-bit xfers. */
+	FORCE_FDX=0x20,						/* User override. */
+	REALTEK_FDX=0x40, HOLTEK_FDX=0x80,
+	STOP_PG_0x60=0x100,
+};
+
+enum ne2k_pci_chipsets {
+	CH_RealTek_RTL_8029 = 0,
+	CH_Winbond_89C940,
+	CH_Compex_RL2000,
+	CH_KTI_ET32P2,
+	CH_NetVin_NV5000SC,
+	CH_Via_86C926,
+	CH_SureCom_NE34,
+	CH_Winbond_W89C940F,
+	CH_Holtek_HT80232,
+	CH_Holtek_HT80229,
+	CH_Winbond_89C940_8c4a,
+};
+
+
+static struct {
+	char *name;
+	int flags;
+} pci_clone_list[] __devinitdata = {
+	{"RealTek RTL-8029", REALTEK_FDX},
+	{"Winbond 89C940", 0},
+	{"Compex RL2000", 0},
+	{"KTI ET32P2", 0},
+	{"NetVin NV5000SC", 0},
+	{"Via 86C926", ONLY_16BIT_IO},
+	{"SureCom NE34", 0},
+	{"Winbond W89C940F", 0},
+	{"Holtek HT80232", ONLY_16BIT_IO | HOLTEK_FDX},
+	{"Holtek HT80229", ONLY_32BIT_IO | HOLTEK_FDX | STOP_PG_0x60 },
+	{"Winbond W89C940(misprogrammed)", 0},
+	{NULL,}
+};
+
+
+static DEFINE_PCI_DEVICE_TABLE(ne2k_pci_tbl) = {
+	{ 0x10ec, 0x8029, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_RealTek_RTL_8029 },
+	{ 0x1050, 0x0940, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_Winbond_89C940 },
+	{ 0x11f6, 0x1401, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_Compex_RL2000 },
+	{ 0x8e2e, 0x3000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_KTI_ET32P2 },
+	{ 0x4a14, 0x5000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_NetVin_NV5000SC },
+	{ 0x1106, 0x0926, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_Via_86C926 },
+	{ 0x10bd, 0x0e34, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_SureCom_NE34 },
+	{ 0x1050, 0x5a5a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_Winbond_W89C940F },
+	{ 0x12c3, 0x0058, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_Holtek_HT80232 },
+	{ 0x12c3, 0x5598, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_Holtek_HT80229 },
+	{ 0x8c4a, 0x1980, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_Winbond_89C940_8c4a },
+	{ 0, }
+};
+MODULE_DEVICE_TABLE(pci, ne2k_pci_tbl);
+
+
+/* ---- No user-serviceable parts below ---- */
+
+#define NE_BASE	 (dev->base_addr)
+#define NE_CMD	 	0x00
+#define NE_DATAPORT	0x10	/* NatSemi-defined port window offset. */
+#define NE_RESET	0x1f	/* Issue a read to reset, a write to clear. */
+#define NE_IO_EXTENT	0x20
+
+#define NESM_START_PG	0x40	/* First page of TX buffer */
+#define NESM_STOP_PG	0x80	/* Last page +1 of RX ring */
+
+
+static int ne2k_pci_open(struct net_device *dev);
+static int ne2k_pci_close(struct net_device *dev);
+
+static void ne2k_pci_reset_8390(struct net_device *dev);
+static void ne2k_pci_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
+			  int ring_page);
+static void ne2k_pci_block_input(struct net_device *dev, int count,
+			  struct sk_buff *skb, int ring_offset);
+static void ne2k_pci_block_output(struct net_device *dev, const int count,
+		const unsigned char *buf, const int start_page);
+static const struct ethtool_ops ne2k_pci_ethtool_ops;
+
+
+
+/* There is no room in the standard 8390 structure for extra info we need,
+   so we build a meta/outer-wrapper structure.. */
+struct ne2k_pci_card {
+	struct net_device *dev;
+	struct pci_dev *pci_dev;
+};
+
+
+
+/*
+  NEx000-clone boards have a Station Address (SA) PROM (SAPROM) in the packet
+  buffer memory space.  By-the-spec NE2000 clones have 0x57,0x57 in bytes
+  0x0e,0x0f of the SAPROM, while other supposed NE2000 clones must be
+  detected by their SA prefix.
+
+  Reading the SAPROM from a word-wide card with the 8390 set in byte-wide
+  mode results in doubled values, which can be detected and compensated for.
+
+  The probe is also responsible for initializing the card and filling
+  in the 'dev' and 'ei_status' structures.
+*/
+
+static const struct net_device_ops ne2k_netdev_ops = {
+	.ndo_open		= ne2k_pci_open,
+	.ndo_stop		= ne2k_pci_close,
+	.ndo_start_xmit		= ei_start_xmit,
+	.ndo_tx_timeout		= ei_tx_timeout,
+	.ndo_get_stats		= ei_get_stats,
+	.ndo_set_multicast_list = ei_set_multicast_list,
+	.ndo_validate_addr	= eth_validate_addr,
+	.ndo_set_mac_address 	= eth_mac_addr,
+	.ndo_change_mtu		= eth_change_mtu,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	.ndo_poll_controller = ei_poll,
+#endif
+};
+
+static int __devinit ne2k_pci_init_one (struct pci_dev *pdev,
+				     const struct pci_device_id *ent)
+{
+	struct net_device *dev;
+	int i;
+	unsigned char SA_prom[32];
+	int start_page, stop_page;
+	int irq, reg0, chip_idx = ent->driver_data;
+	static unsigned int fnd_cnt;
+	long ioaddr;
+	int flags = pci_clone_list[chip_idx].flags;
+
+/* when built into the kernel, we only print version if device is found */
+#ifndef MODULE
+	static int printed_version;
+	if (!printed_version++)
+		printk(version);
+#endif
+
+	fnd_cnt++;
+
+	i = pci_enable_device (pdev);
+	if (i)
+		return i;
+
+	ioaddr = pci_resource_start (pdev, 0);
+	irq = pdev->irq;
+
+	if (!ioaddr || ((pci_resource_flags (pdev, 0) & IORESOURCE_IO) == 0)) {
+		dev_err(&pdev->dev, "no I/O resource at PCI BAR #0\n");
+		return -ENODEV;
+	}
+
+	if (request_region (ioaddr, NE_IO_EXTENT, DRV_NAME) == NULL) {
+		dev_err(&pdev->dev, "I/O resource 0x%x @ 0x%lx busy\n",
+			NE_IO_EXTENT, ioaddr);
+		return -EBUSY;
+	}
+
+	reg0 = inb(ioaddr);
+	if (reg0 == 0xFF)
+		goto err_out_free_res;
+
+	/* Do a preliminary verification that we have a 8390. */
+	{
+		int regd;
+		outb(E8390_NODMA+E8390_PAGE1+E8390_STOP, ioaddr + E8390_CMD);
+		regd = inb(ioaddr + 0x0d);
+		outb(0xff, ioaddr + 0x0d);
+		outb(E8390_NODMA+E8390_PAGE0, ioaddr + E8390_CMD);
+		inb(ioaddr + EN0_COUNTER0); /* Clear the counter by reading. */
+		if (inb(ioaddr + EN0_COUNTER0) != 0) {
+			outb(reg0, ioaddr);
+			outb(regd, ioaddr + 0x0d);	/* Restore the old values. */
+			goto err_out_free_res;
+		}
+	}
+
+	/* Allocate net_device, dev->priv; fill in 8390 specific dev fields. */
+	dev = alloc_ei_netdev();
+	if (!dev) {
+		dev_err(&pdev->dev, "cannot allocate ethernet device\n");
+		goto err_out_free_res;
+	}
+	dev->netdev_ops = &ne2k_netdev_ops;
+
+	SET_NETDEV_DEV(dev, &pdev->dev);
+
+	/* Reset card. Who knows what dain-bramaged state it was left in. */
+	{
+		unsigned long reset_start_time = jiffies;
+
+		outb(inb(ioaddr + NE_RESET), ioaddr + NE_RESET);
+
+		/* This looks like a horrible timing loop, but it should never take
+		   more than a few cycles.
+		*/
+		while ((inb(ioaddr + EN0_ISR) & ENISR_RESET) == 0)
+			/* Limit wait: '2' avoids jiffy roll-over. */
+			if (jiffies - reset_start_time > 2) {
+				dev_err(&pdev->dev,
+					"Card failure (no reset ack).\n");
+				goto err_out_free_netdev;
+			}
+
+		outb(0xff, ioaddr + EN0_ISR);		/* Ack all intr. */
+	}
+
+	/* Read the 16 bytes of station address PROM.
+	   We must first initialize registers, similar to NS8390_init(eifdev, 0).
+	   We can't reliably read the SAPROM address without this.
+	   (I learned the hard way!). */
+	{
+		struct {unsigned char value, offset; } program_seq[] = {
+			{E8390_NODMA+E8390_PAGE0+E8390_STOP, E8390_CMD}, /* Select page 0*/
+			{0x49,	EN0_DCFG},	/* Set word-wide access. */
+			{0x00,	EN0_RCNTLO},	/* Clear the count regs. */
+			{0x00,	EN0_RCNTHI},
+			{0x00,	EN0_IMR},	/* Mask completion irq. */
+			{0xFF,	EN0_ISR},
+			{E8390_RXOFF, EN0_RXCR},	/* 0x20  Set to monitor */
+			{E8390_TXOFF, EN0_TXCR},	/* 0x02  and loopback mode. */
+			{32,	EN0_RCNTLO},
+			{0x00,	EN0_RCNTHI},
+			{0x00,	EN0_RSARLO},	/* DMA starting at 0x0000. */
+			{0x00,	EN0_RSARHI},
+			{E8390_RREAD+E8390_START, E8390_CMD},
+		};
+		for (i = 0; i < ARRAY_SIZE(program_seq); i++)
+			outb(program_seq[i].value, ioaddr + program_seq[i].offset);
+
+	}
+
+	/* Note: all PCI cards have at least 16 bit access, so we don't have
+	   to check for 8 bit cards.  Most cards permit 32 bit access. */
+	if (flags & ONLY_32BIT_IO) {
+		for (i = 0; i < 4 ; i++)
+			((u32 *)SA_prom)[i] = le32_to_cpu(inl(ioaddr + NE_DATAPORT));
+	} else
+		for(i = 0; i < 32 /*sizeof(SA_prom)*/; i++)
+			SA_prom[i] = inb(ioaddr + NE_DATAPORT);
+
+	/* We always set the 8390 registers for word mode. */
+	outb(0x49, ioaddr + EN0_DCFG);
+	start_page = NESM_START_PG;
+
+	stop_page = flags & STOP_PG_0x60 ? 0x60 : NESM_STOP_PG;
+
+	/* Set up the rest of the parameters. */
+	dev->irq = irq;
+	dev->base_addr = ioaddr;
+	pci_set_drvdata(pdev, dev);
+
+	ei_status.name = pci_clone_list[chip_idx].name;
+	ei_status.tx_start_page = start_page;
+	ei_status.stop_page = stop_page;
+	ei_status.word16 = 1;
+	ei_status.ne2k_flags = flags;
+	if (fnd_cnt < MAX_UNITS) {
+		if (full_duplex[fnd_cnt] > 0  ||  (options[fnd_cnt] & FORCE_FDX))
+			ei_status.ne2k_flags |= FORCE_FDX;
+	}
+
+	ei_status.rx_start_page = start_page + TX_PAGES;
+#ifdef PACKETBUF_MEMSIZE
+	/* Allow the packet buffer size to be overridden by know-it-alls. */
+	ei_status.stop_page = ei_status.tx_start_page + PACKETBUF_MEMSIZE;
+#endif
+
+	ei_status.reset_8390 = &ne2k_pci_reset_8390;
+	ei_status.block_input = &ne2k_pci_block_input;
+	ei_status.block_output = &ne2k_pci_block_output;
+	ei_status.get_8390_hdr = &ne2k_pci_get_8390_hdr;
+	ei_status.priv = (unsigned long) pdev;
+
+	dev->ethtool_ops = &ne2k_pci_ethtool_ops;
+	NS8390_init(dev, 0);
+
+	memcpy(dev->dev_addr, SA_prom, dev->addr_len);
+	memcpy(dev->perm_addr, dev->dev_addr, dev->addr_len);
+
+	i = register_netdev(dev);
+	if (i)
+		goto err_out_free_netdev;
+
+	printk("%s: %s found at %#lx, IRQ %d, %pM.\n",
+	       dev->name, pci_clone_list[chip_idx].name, ioaddr, dev->irq,
+	       dev->dev_addr);
+
+	return 0;
+
+err_out_free_netdev:
+	free_netdev (dev);
+err_out_free_res:
+	release_region (ioaddr, NE_IO_EXTENT);
+	pci_set_drvdata (pdev, NULL);
+	return -ENODEV;
+
+}
+
+/*
+ * Magic incantation sequence for full duplex on the supported cards.
+ */
+static inline int set_realtek_fdx(struct net_device *dev)
+{
+	long ioaddr = dev->base_addr;
+
+	outb(0xC0 + E8390_NODMA, ioaddr + NE_CMD); /* Page 3 */
+	outb(0xC0, ioaddr + 0x01); /* Enable writes to CONFIG3 */
+	outb(0x40, ioaddr + 0x06); /* Enable full duplex */
+	outb(0x00, ioaddr + 0x01); /* Disable writes to CONFIG3 */
+	outb(E8390_PAGE0 + E8390_NODMA, ioaddr + NE_CMD); /* Page 0 */
+	return 0;
+}
+
+static inline int set_holtek_fdx(struct net_device *dev)
+{
+	long ioaddr = dev->base_addr;
+
+	outb(inb(ioaddr + 0x20) | 0x80, ioaddr + 0x20);
+	return 0;
+}
+
+static int ne2k_pci_set_fdx(struct net_device *dev)
+{
+	if (ei_status.ne2k_flags & REALTEK_FDX)
+		return set_realtek_fdx(dev);
+	else if (ei_status.ne2k_flags & HOLTEK_FDX)
+		return set_holtek_fdx(dev);
+
+	return -EOPNOTSUPP;
+}
+
+static int ne2k_pci_open(struct net_device *dev)
+{
+	int ret = request_irq(dev->irq, ei_interrupt, IRQF_SHARED, dev->name, dev);
+	if (ret)
+		return ret;
+
+	if (ei_status.ne2k_flags & FORCE_FDX)
+		ne2k_pci_set_fdx(dev);
+
+	ei_open(dev);
+	return 0;
+}
+
+static int ne2k_pci_close(struct net_device *dev)
+{
+	ei_close(dev);
+	free_irq(dev->irq, dev);
+	return 0;
+}
+
+/* Hard reset the card.  This used to pause for the same period that a
+   8390 reset command required, but that shouldn't be necessary. */
+static void ne2k_pci_reset_8390(struct net_device *dev)
+{
+	unsigned long reset_start_time = jiffies;
+
+	if (debug > 1) printk("%s: Resetting the 8390 t=%ld...",
+						  dev->name, jiffies);
+
+	outb(inb(NE_BASE + NE_RESET), NE_BASE + NE_RESET);
+
+	ei_status.txing = 0;
+	ei_status.dmaing = 0;
+
+	/* This check _should_not_ be necessary, omit eventually. */
+	while ((inb(NE_BASE+EN0_ISR) & ENISR_RESET) == 0)
+		if (jiffies - reset_start_time > 2) {
+			printk("%s: ne2k_pci_reset_8390() did not complete.\n", dev->name);
+			break;
+		}
+	outb(ENISR_RESET, NE_BASE + EN0_ISR);	/* Ack intr. */
+}
+
+/* Grab the 8390 specific header. Similar to the block_input routine, but
+   we don't need to be concerned with ring wrap as the header will be at
+   the start of a page, so we optimize accordingly. */
+
+static void ne2k_pci_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
+{
+
+	long nic_base = dev->base_addr;
+
+	/* This *shouldn't* happen. If it does, it's the last thing you'll see */
+	if (ei_status.dmaing) {
+		printk("%s: DMAing conflict in ne2k_pci_get_8390_hdr "
+			   "[DMAstat:%d][irqlock:%d].\n",
+			   dev->name, ei_status.dmaing, ei_status.irqlock);
+		return;
+	}
+
+	ei_status.dmaing |= 0x01;
+	outb(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base+ NE_CMD);
+	outb(sizeof(struct e8390_pkt_hdr), nic_base + EN0_RCNTLO);
+	outb(0, nic_base + EN0_RCNTHI);
+	outb(0, nic_base + EN0_RSARLO);		/* On page boundary */
+	outb(ring_page, nic_base + EN0_RSARHI);
+	outb(E8390_RREAD+E8390_START, nic_base + NE_CMD);
+
+	if (ei_status.ne2k_flags & ONLY_16BIT_IO) {
+		insw(NE_BASE + NE_DATAPORT, hdr, sizeof(struct e8390_pkt_hdr)>>1);
+	} else {
+		*(u32*)hdr = le32_to_cpu(inl(NE_BASE + NE_DATAPORT));
+		le16_to_cpus(&hdr->count);
+	}
+
+	outb(ENISR_RDC, nic_base + EN0_ISR);	/* Ack intr. */
+	ei_status.dmaing &= ~0x01;
+}
+
+/* Block input and output, similar to the Crynwr packet driver.  If you
+   are porting to a new ethercard, look at the packet driver source for hints.
+   The NEx000 doesn't share the on-board packet memory -- you have to put
+   the packet out through the "remote DMA" dataport using outb. */
+
+static void ne2k_pci_block_input(struct net_device *dev, int count,
+				 struct sk_buff *skb, int ring_offset)
+{
+	long nic_base = dev->base_addr;
+	char *buf = skb->data;
+
+	/* This *shouldn't* happen. If it does, it's the last thing you'll see */
+	if (ei_status.dmaing) {
+		printk("%s: DMAing conflict in ne2k_pci_block_input "
+			   "[DMAstat:%d][irqlock:%d].\n",
+			   dev->name, ei_status.dmaing, ei_status.irqlock);
+		return;
+	}
+	ei_status.dmaing |= 0x01;
+	if (ei_status.ne2k_flags & ONLY_32BIT_IO)
+		count = (count + 3) & 0xFFFC;
+	outb(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base+ NE_CMD);
+	outb(count & 0xff, nic_base + EN0_RCNTLO);
+	outb(count >> 8, nic_base + EN0_RCNTHI);
+	outb(ring_offset & 0xff, nic_base + EN0_RSARLO);
+	outb(ring_offset >> 8, nic_base + EN0_RSARHI);
+	outb(E8390_RREAD+E8390_START, nic_base + NE_CMD);
+
+	if (ei_status.ne2k_flags & ONLY_16BIT_IO) {
+		insw(NE_BASE + NE_DATAPORT,buf,count>>1);
+		if (count & 0x01) {
+			buf[count-1] = inb(NE_BASE + NE_DATAPORT);
+		}
+	} else {
+		insl(NE_BASE + NE_DATAPORT, buf, count>>2);
+		if (count & 3) {
+			buf += count & ~3;
+			if (count & 2) {
+				__le16 *b = (__le16 *)buf;
+
+				*b++ = cpu_to_le16(inw(NE_BASE + NE_DATAPORT));
+				buf = (char *)b;
+			}
+			if (count & 1)
+				*buf = inb(NE_BASE + NE_DATAPORT);
+		}
+	}
+
+	outb(ENISR_RDC, nic_base + EN0_ISR);	/* Ack intr. */
+	ei_status.dmaing &= ~0x01;
+}
+
+static void ne2k_pci_block_output(struct net_device *dev, int count,
+				  const unsigned char *buf, const int start_page)
+{
+	long nic_base = NE_BASE;
+	unsigned long dma_start;
+
+	/* On little-endian it's always safe to round the count up for
+	   word writes. */
+	if (ei_status.ne2k_flags & ONLY_32BIT_IO)
+		count = (count + 3) & 0xFFFC;
+	else
+		if (count & 0x01)
+			count++;
+
+	/* This *shouldn't* happen. If it does, it's the last thing you'll see */
+	if (ei_status.dmaing) {
+		printk("%s: DMAing conflict in ne2k_pci_block_output."
+			   "[DMAstat:%d][irqlock:%d]\n",
+			   dev->name, ei_status.dmaing, ei_status.irqlock);
+		return;
+	}
+	ei_status.dmaing |= 0x01;
+	/* We should already be in page 0, but to be safe... */
+	outb(E8390_PAGE0+E8390_START+E8390_NODMA, nic_base + NE_CMD);
+
+#ifdef NE8390_RW_BUGFIX
+	/* Handle the read-before-write bug the same way as the
+	   Crynwr packet driver -- the NatSemi method doesn't work.
+	   Actually this doesn't always work either, but if you have
+	   problems with your NEx000 this is better than nothing! */
+	outb(0x42, nic_base + EN0_RCNTLO);
+	outb(0x00, nic_base + EN0_RCNTHI);
+	outb(0x42, nic_base + EN0_RSARLO);
+	outb(0x00, nic_base + EN0_RSARHI);
+	outb(E8390_RREAD+E8390_START, nic_base + NE_CMD);
+#endif
+	outb(ENISR_RDC, nic_base + EN0_ISR);
+
+   /* Now the normal output. */
+	outb(count & 0xff, nic_base + EN0_RCNTLO);
+	outb(count >> 8,   nic_base + EN0_RCNTHI);
+	outb(0x00, nic_base + EN0_RSARLO);
+	outb(start_page, nic_base + EN0_RSARHI);
+	outb(E8390_RWRITE+E8390_START, nic_base + NE_CMD);
+	if (ei_status.ne2k_flags & ONLY_16BIT_IO) {
+		outsw(NE_BASE + NE_DATAPORT, buf, count>>1);
+	} else {
+		outsl(NE_BASE + NE_DATAPORT, buf, count>>2);
+		if (count & 3) {
+			buf += count & ~3;
+			if (count & 2) {
+				__le16 *b = (__le16 *)buf;
+
+				outw(le16_to_cpu(*b++), NE_BASE + NE_DATAPORT);
+				buf = (char *)b;
+			}
+		}
+	}
+
+	dma_start = jiffies;
+
+	while ((inb(nic_base + EN0_ISR) & ENISR_RDC) == 0)
+		if (jiffies - dma_start > 2) {			/* Avoid clock roll-over. */
+			printk(KERN_WARNING "%s: timeout waiting for Tx RDC.\n", dev->name);
+			ne2k_pci_reset_8390(dev);
+			NS8390_init(dev,1);
+			break;
+		}
+
+	outb(ENISR_RDC, nic_base + EN0_ISR);	/* Ack intr. */
+	ei_status.dmaing &= ~0x01;
+}
+
+static void ne2k_pci_get_drvinfo(struct net_device *dev,
+				 struct ethtool_drvinfo *info)
+{
+	struct ei_device *ei = netdev_priv(dev);
+	struct pci_dev *pci_dev = (struct pci_dev *) ei->priv;
+
+	strcpy(info->driver, DRV_NAME);
+	strcpy(info->version, DRV_VERSION);
+	strcpy(info->bus_info, pci_name(pci_dev));
+}
+
+static const struct ethtool_ops ne2k_pci_ethtool_ops = {
+	.get_drvinfo		= ne2k_pci_get_drvinfo,
+};
+
+static void __devexit ne2k_pci_remove_one (struct pci_dev *pdev)
+{
+	struct net_device *dev = pci_get_drvdata(pdev);
+
+	BUG_ON(!dev);
+	unregister_netdev(dev);
+	release_region(dev->base_addr, NE_IO_EXTENT);
+	free_netdev(dev);
+	pci_disable_device(pdev);
+	pci_set_drvdata(pdev, NULL);
+}
+
+#ifdef CONFIG_PM
+static int ne2k_pci_suspend (struct pci_dev *pdev, pm_message_t state)
+{
+	struct net_device *dev = pci_get_drvdata (pdev);
+
+	netif_device_detach(dev);
+	pci_save_state(pdev);
+	pci_disable_device(pdev);
+	pci_set_power_state(pdev, pci_choose_state(pdev, state));
+
+	return 0;
+}
+
+static int ne2k_pci_resume (struct pci_dev *pdev)
+{
+	struct net_device *dev = pci_get_drvdata (pdev);
+	int rc;
+
+	pci_set_power_state(pdev, 0);
+	pci_restore_state(pdev);
+
+	rc = pci_enable_device(pdev);
+	if (rc)
+		return rc;
+
+	NS8390_init(dev, 1);
+	netif_device_attach(dev);
+
+	return 0;
+}
+
+#endif /* CONFIG_PM */
+
+
+static struct pci_driver ne2k_driver = {
+	.name		= DRV_NAME,
+	.probe		= ne2k_pci_init_one,
+	.remove		= __devexit_p(ne2k_pci_remove_one),
+	.id_table	= ne2k_pci_tbl,
+#ifdef CONFIG_PM
+	.suspend	= ne2k_pci_suspend,
+	.resume		= ne2k_pci_resume,
+#endif /* CONFIG_PM */
+
+};
+
+
+static int __init ne2k_pci_init(void)
+{
+/* when a module, this is printed whether or not devices are found in probe */
+#ifdef MODULE
+	printk(version);
+#endif
+	return pci_register_driver(&ne2k_driver);
+}
+
+
+static void __exit ne2k_pci_cleanup(void)
+{
+	pci_unregister_driver (&ne2k_driver);
+}
+
+module_init(ne2k_pci_init);
+module_exit(ne2k_pci_cleanup);
diff --git a/drivers/net/ethernet/8390/ne3210.c b/drivers/net/ethernet/8390/ne3210.c
new file mode 100644
index 0000000..243ed2a
--- /dev/null
+++ b/drivers/net/ethernet/8390/ne3210.c
@@ -0,0 +1,347 @@
+/*
+	ne3210.c
+
+	Linux driver for Novell NE3210 EISA Network Adapter
+
+	Copyright (C) 1998, Paul Gortmaker.
+
+	This software may be used and distributed according to the terms
+	of the GNU General Public License, incorporated herein by reference.
+
+	Information and Code Sources:
+
+	1) Based upon my other EISA 8390 drivers (lne390, es3210, smc-ultra32)
+	2) The existing myriad of other Linux 8390 drivers by Donald Becker.
+	3) Info for getting IRQ and sh-mem gleaned from the EISA cfg file
+
+	The NE3210 is an EISA shared memory NS8390 implementation.  Shared
+	memory address > 1MB should work with this driver.
+
+	Note that the .cfg file (3/11/93, v1.0) has AUI and BNC switched
+	around (or perhaps there are some defective/backwards cards ???)
+
+	This driver WILL NOT WORK FOR THE NE3200 - it is completely different
+	and does not use an 8390 at all.
+
+	Updated to EISA probing API 5/2003 by Marc Zyngier.
+*/
+
+#include <linux/module.h>
+#include <linux/eisa.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/mm.h>
+
+#include <asm/io.h>
+#include <asm/system.h>
+
+#include "8390.h"
+
+#define DRV_NAME "ne3210"
+
+static void ne3210_reset_8390(struct net_device *dev);
+
+static void ne3210_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page);
+static void ne3210_block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset);
+static void ne3210_block_output(struct net_device *dev, int count, const unsigned char *buf, const int start_page);
+
+#define NE3210_START_PG		0x00    /* First page of TX buffer	*/
+#define NE3210_STOP_PG		0x80    /* Last page +1 of RX ring	*/
+
+#define NE3210_IO_EXTENT	0x20
+#define NE3210_SA_PROM		0x16	/* Start of e'net addr.		*/
+#define NE3210_RESET_PORT	0xc84
+#define NE3210_NIC_OFFSET	0x00	/* Hello, the 8390 is *here*	*/
+
+#define NE3210_ADDR0		0x00	/* 3 byte vendor prefix		*/
+#define NE3210_ADDR1		0x00
+#define NE3210_ADDR2		0x1b
+
+#define NE3210_CFG1		0xc84	/* NB: 0xc84 is also "reset" port. */
+#define NE3210_CFG2		0xc90
+#define NE3210_CFG_EXTENT       (NE3210_CFG2 - NE3210_CFG1 + 1)
+
+/*
+ *	You can OR any of the following bits together and assign it
+ *	to NE3210_DEBUG to get verbose driver info during operation.
+ *	Currently only the probe one is implemented.
+ */
+
+#define NE3210_D_PROBE	0x01
+#define NE3210_D_RX_PKT	0x02
+#define NE3210_D_TX_PKT	0x04
+#define NE3210_D_IRQ	0x08
+
+#define NE3210_DEBUG	0x0
+
+static unsigned char irq_map[] __initdata = {15, 12, 11, 10, 9, 7, 5, 3};
+static unsigned int shmem_map[] __initdata = {0xff0, 0xfe0, 0xfff0, 0xd8, 0xffe0, 0xffc0, 0xd0, 0x0};
+static const char *ifmap[] __initdata = {"UTP", "?", "BNC", "AUI"};
+static int ifmap_val[] __initdata = {
+		IF_PORT_10BASET,
+		IF_PORT_UNKNOWN,
+		IF_PORT_10BASE2,
+		IF_PORT_AUI,
+};
+
+static int __init ne3210_eisa_probe (struct device *device)
+{
+	unsigned long ioaddr, phys_mem;
+	int i, retval, port_index;
+	struct eisa_device *edev = to_eisa_device (device);
+	struct net_device *dev;
+
+	/* Allocate dev->priv and fill in 8390 specific dev fields. */
+	if (!(dev = alloc_ei_netdev ())) {
+		printk ("ne3210.c: unable to allocate memory for dev!\n");
+		return -ENOMEM;
+	}
+
+	SET_NETDEV_DEV(dev, device);
+	dev_set_drvdata(device, dev);
+	ioaddr = edev->base_addr;
+
+	if (!request_region(ioaddr, NE3210_IO_EXTENT, DRV_NAME)) {
+		retval = -EBUSY;
+		goto out;
+	}
+
+	if (!request_region(ioaddr + NE3210_CFG1,
+			    NE3210_CFG_EXTENT, DRV_NAME)) {
+		retval = -EBUSY;
+		goto out1;
+	}
+
+#if NE3210_DEBUG & NE3210_D_PROBE
+	printk("ne3210-debug: probe at %#x, ID %s\n", ioaddr, edev->id.sig);
+	printk("ne3210-debug: config regs: %#x %#x\n",
+		inb(ioaddr + NE3210_CFG1), inb(ioaddr + NE3210_CFG2));
+#endif
+
+	port_index = inb(ioaddr + NE3210_CFG2) >> 6;
+	for(i = 0; i < ETHER_ADDR_LEN; i++)
+		dev->dev_addr[i] = inb(ioaddr + NE3210_SA_PROM + i);
+	printk("ne3210.c: NE3210 in EISA slot %d, media: %s, addr: %pM.\n",
+		edev->slot, ifmap[port_index], dev->dev_addr);
+
+	/* Snarf the interrupt now. CFG file has them all listed as `edge' with share=NO */
+	dev->irq = irq_map[(inb(ioaddr + NE3210_CFG2) >> 3) & 0x07];
+	printk("ne3210.c: using IRQ %d, ", dev->irq);
+
+	retval = request_irq(dev->irq, ei_interrupt, 0, DRV_NAME, dev);
+	if (retval) {
+		printk (" unable to get IRQ %d.\n", dev->irq);
+		goto out2;
+	}
+
+	phys_mem = shmem_map[inb(ioaddr + NE3210_CFG2) & 0x07] * 0x1000;
+
+	/*
+	   BEWARE!! Some dain-bramaged EISA SCUs will allow you to put
+	   the card mem within the region covered by `normal' RAM  !!!
+	*/
+	if (phys_mem > 1024*1024) {	/* phys addr > 1MB */
+		if (phys_mem < virt_to_phys(high_memory)) {
+			printk(KERN_CRIT "ne3210.c: Card RAM overlaps with normal memory!!!\n");
+			printk(KERN_CRIT "ne3210.c: Use EISA SCU to set card memory below 1MB,\n");
+			printk(KERN_CRIT "ne3210.c: or to an address above 0x%llx.\n",
+				(u64)virt_to_phys(high_memory));
+			printk(KERN_CRIT "ne3210.c: Driver NOT installed.\n");
+			retval = -EINVAL;
+			goto out3;
+		}
+	}
+
+	if (!request_mem_region (phys_mem, NE3210_STOP_PG*0x100, DRV_NAME)) {
+		printk ("ne3210.c: Unable to request shared memory at physical address %#lx\n",
+			phys_mem);
+		goto out3;
+	}
+
+	printk("%dkB memory at physical address %#lx\n",
+	       NE3210_STOP_PG/4, phys_mem);
+
+	ei_status.mem = ioremap(phys_mem, NE3210_STOP_PG*0x100);
+	if (!ei_status.mem) {
+		printk(KERN_ERR "ne3210.c: Unable to remap card memory !!\n");
+		printk(KERN_ERR "ne3210.c: Driver NOT installed.\n");
+		retval = -EAGAIN;
+		goto out4;
+	}
+	printk("ne3210.c: remapped %dkB card memory to virtual address %p\n",
+	       NE3210_STOP_PG/4, ei_status.mem);
+	dev->mem_start = (unsigned long)ei_status.mem;
+	dev->mem_end = dev->mem_start + (NE3210_STOP_PG - NE3210_START_PG)*256;
+
+	/* The 8390 offset is zero for the NE3210 */
+	dev->base_addr = ioaddr;
+
+	ei_status.name = "NE3210";
+	ei_status.tx_start_page = NE3210_START_PG;
+	ei_status.rx_start_page = NE3210_START_PG + TX_PAGES;
+	ei_status.stop_page = NE3210_STOP_PG;
+	ei_status.word16 = 1;
+	ei_status.priv = phys_mem;
+
+	if (ei_debug > 0)
+		printk("ne3210 loaded.\n");
+
+	ei_status.reset_8390 = &ne3210_reset_8390;
+	ei_status.block_input = &ne3210_block_input;
+	ei_status.block_output = &ne3210_block_output;
+	ei_status.get_8390_hdr = &ne3210_get_8390_hdr;
+
+	dev->netdev_ops = &ei_netdev_ops;
+
+	dev->if_port = ifmap_val[port_index];
+
+	if ((retval = register_netdev (dev)))
+		goto out5;
+
+	NS8390_init(dev, 0);
+	return 0;
+
+ out5:
+	iounmap(ei_status.mem);
+ out4:
+	release_mem_region (phys_mem, NE3210_STOP_PG*0x100);
+ out3:
+	free_irq (dev->irq, dev);
+ out2:
+	release_region (ioaddr + NE3210_CFG1, NE3210_CFG_EXTENT);
+ out1:
+	release_region (ioaddr, NE3210_IO_EXTENT);
+ out:
+	free_netdev (dev);
+
+	return retval;
+}
+
+static int __devexit ne3210_eisa_remove (struct device *device)
+{
+	struct net_device  *dev    = dev_get_drvdata(device);
+	unsigned long       ioaddr = to_eisa_device (device)->base_addr;
+
+	unregister_netdev (dev);
+	iounmap(ei_status.mem);
+	release_mem_region (ei_status.priv, NE3210_STOP_PG*0x100);
+	free_irq (dev->irq, dev);
+	release_region (ioaddr + NE3210_CFG1, NE3210_CFG_EXTENT);
+	release_region (ioaddr, NE3210_IO_EXTENT);
+	free_netdev (dev);
+
+	return 0;
+}
+
+/*
+ *	Reset by toggling the "Board Enable" bits (bit 2 and 0).
+ */
+
+static void ne3210_reset_8390(struct net_device *dev)
+{
+	unsigned short ioaddr = dev->base_addr;
+
+	outb(0x04, ioaddr + NE3210_RESET_PORT);
+	if (ei_debug > 1) printk("%s: resetting the NE3210...", dev->name);
+
+	mdelay(2);
+
+	ei_status.txing = 0;
+	outb(0x01, ioaddr + NE3210_RESET_PORT);
+	if (ei_debug > 1) printk("reset done\n");
+}
+
+/*
+ *	Note: In the following three functions is the implicit assumption
+ *	that the associated memcpy will only use "rep; movsl" as long as
+ *	we keep the counts as some multiple of doublewords. This is a
+ *	requirement of the hardware, and also prevents us from using
+ *	eth_io_copy_and_sum() since we can't guarantee it will limit
+ *	itself to doubleword access.
+ */
+
+/*
+ *	Grab the 8390 specific header. Similar to the block_input routine, but
+ *	we don't need to be concerned with ring wrap as the header will be at
+ *	the start of a page, so we optimize accordingly. (A single doubleword.)
+ */
+
+static void
+ne3210_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
+{
+	void __iomem *hdr_start = ei_status.mem + ((ring_page - NE3210_START_PG)<<8);
+	memcpy_fromio(hdr, hdr_start, sizeof(struct e8390_pkt_hdr));
+	hdr->count = (hdr->count + 3) & ~3;     /* Round up allocation. */
+}
+
+/*
+ *	Block input and output are easy on shared memory ethercards, the only
+ *	complication is when the ring buffer wraps. The count will already
+ *	be rounded up to a doubleword value via ne3210_get_8390_hdr() above.
+ */
+
+static void ne3210_block_input(struct net_device *dev, int count, struct sk_buff *skb,
+						  int ring_offset)
+{
+	void __iomem *start = ei_status.mem + ring_offset - NE3210_START_PG*256;
+
+	if (ring_offset + count > NE3210_STOP_PG*256) {
+		/* Packet wraps over end of ring buffer. */
+		int semi_count = NE3210_STOP_PG*256 - ring_offset;
+		memcpy_fromio(skb->data, start, semi_count);
+		count -= semi_count;
+		memcpy_fromio(skb->data + semi_count,
+				ei_status.mem + TX_PAGES*256, count);
+	} else {
+		/* Packet is in one chunk. */
+		memcpy_fromio(skb->data, start, count);
+	}
+}
+
+static void ne3210_block_output(struct net_device *dev, int count,
+				const unsigned char *buf, int start_page)
+{
+	void __iomem *shmem = ei_status.mem + ((start_page - NE3210_START_PG)<<8);
+
+	count = (count + 3) & ~3;     /* Round up to doubleword */
+	memcpy_toio(shmem, buf, count);
+}
+
+static struct eisa_device_id ne3210_ids[] = {
+	{ "EGL0101" },
+	{ "NVL1801" },
+	{ "" },
+};
+MODULE_DEVICE_TABLE(eisa, ne3210_ids);
+
+static struct eisa_driver ne3210_eisa_driver = {
+	.id_table = ne3210_ids,
+	.driver   = {
+		.name   = "ne3210",
+		.probe  = ne3210_eisa_probe,
+		.remove = __devexit_p (ne3210_eisa_remove),
+	},
+};
+
+MODULE_DESCRIPTION("NE3210 EISA Ethernet driver");
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(eisa, ne3210_ids);
+
+static int ne3210_init(void)
+{
+	return eisa_driver_register (&ne3210_eisa_driver);
+}
+
+static void ne3210_cleanup(void)
+{
+	eisa_driver_unregister (&ne3210_eisa_driver);
+}
+
+module_init (ne3210_init);
+module_exit (ne3210_cleanup);
diff --git a/drivers/net/ethernet/8390/pcnet_cs.c b/drivers/net/ethernet/8390/pcnet_cs.c
new file mode 100644
index 0000000..4010761
--- /dev/null
+++ b/drivers/net/ethernet/8390/pcnet_cs.c
@@ -0,0 +1,1710 @@
+/*======================================================================
+
+    A PCMCIA ethernet driver for NS8390-based cards
+
+    This driver supports the D-Link DE-650 and Linksys EthernetCard
+    cards, the newer D-Link and Linksys combo cards, Accton EN2212
+    cards, the RPTI EP400, and the PreMax PE-200 in non-shared-memory
+    mode, and the IBM Credit Card Adapter, the NE4100, the Thomas
+    Conrad ethernet card, and the Kingston KNE-PCM/x in shared-memory
+    mode.  It will also handle the Socket EA card in either mode.
+
+    Copyright (C) 1999 David A. Hinds -- dahinds@users.sourceforge.net
+
+    pcnet_cs.c 1.153 2003/11/09 18:53:09
+
+    The network driver code is based on Donald Becker's NE2000 code:
+
+    Written 1992,1993 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.
+    Donald Becker may be reached at becker@scyld.com
+
+    Based also on Keith Moore's changes to Don Becker's code, for IBM
+    CCAE support.  Drivers merged back together, and shared-memory
+    Socket EA support added, by Ken Raeburn, September 1995.
+
+======================================================================*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/ptrace.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/log2.h>
+#include <linux/etherdevice.h>
+#include <linux/mii.h>
+#include "8390.h"
+
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ciscode.h>
+#include <pcmcia/ds.h>
+#include <pcmcia/cisreg.h>
+
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/byteorder.h>
+#include <asm/uaccess.h>
+
+#define PCNET_CMD	0x00
+#define PCNET_DATAPORT	0x10	/* NatSemi-defined port window offset. */
+#define PCNET_RESET	0x1f	/* Issue a read to reset, a write to clear. */
+#define PCNET_MISC	0x18	/* For IBM CCAE and Socket EA cards */
+
+#define PCNET_START_PG	0x40	/* First page of TX buffer */
+#define PCNET_STOP_PG	0x80	/* Last page +1 of RX ring */
+
+/* Socket EA cards have a larger packet buffer */
+#define SOCKET_START_PG	0x01
+#define SOCKET_STOP_PG	0xff
+
+#define PCNET_RDC_TIMEOUT (2*HZ/100)	/* Max wait in jiffies for Tx RDC */
+
+static const char *if_names[] = { "auto", "10baseT", "10base2"};
+
+
+/*====================================================================*/
+
+/* Module parameters */
+
+MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
+MODULE_DESCRIPTION("NE2000 compatible PCMCIA ethernet driver");
+MODULE_LICENSE("GPL");
+
+#define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0)
+
+INT_MODULE_PARM(if_port,	1);	/* Transceiver type */
+INT_MODULE_PARM(use_big_buf,	1);	/* use 64K packet buffer? */
+INT_MODULE_PARM(mem_speed,	0);	/* shared mem speed, in ns */
+INT_MODULE_PARM(delay_output,	0);	/* pause after xmit? */
+INT_MODULE_PARM(delay_time,	4);	/* in usec */
+INT_MODULE_PARM(use_shmem,	-1);	/* use shared memory? */
+INT_MODULE_PARM(full_duplex,	0);	/* full duplex? */
+
+/* Ugh!  Let the user hardwire the hardware address for queer cards */
+static int hw_addr[6] = { 0, /* ... */ };
+module_param_array(hw_addr, int, NULL, 0);
+
+/*====================================================================*/
+
+static void mii_phy_probe(struct net_device *dev);
+static int pcnet_config(struct pcmcia_device *link);
+static void pcnet_release(struct pcmcia_device *link);
+static int pcnet_open(struct net_device *dev);
+static int pcnet_close(struct net_device *dev);
+static int ei_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
+static irqreturn_t ei_irq_wrapper(int irq, void *dev_id);
+static void ei_watchdog(u_long arg);
+static void pcnet_reset_8390(struct net_device *dev);
+static int set_config(struct net_device *dev, struct ifmap *map);
+static int setup_shmem_window(struct pcmcia_device *link, int start_pg,
+			      int stop_pg, int cm_offset);
+static int setup_dma_config(struct pcmcia_device *link, int start_pg,
+			    int stop_pg);
+
+static void pcnet_detach(struct pcmcia_device *p_dev);
+
+/*====================================================================*/
+
+typedef struct hw_info_t {
+    u_int	offset;
+    u_char	a0, a1, a2;
+    u_int	flags;
+} hw_info_t;
+
+#define DELAY_OUTPUT	0x01
+#define HAS_MISC_REG	0x02
+#define USE_BIG_BUF	0x04
+#define HAS_IBM_MISC	0x08
+#define IS_DL10019	0x10
+#define IS_DL10022	0x20
+#define HAS_MII		0x40
+#define USE_SHMEM	0x80	/* autodetected */
+
+#define AM79C9XX_HOME_PHY	0x00006B90  /* HomePNA PHY */
+#define AM79C9XX_ETH_PHY	0x00006B70  /* 10baseT PHY */
+#define MII_PHYID_REV_MASK	0xfffffff0
+#define MII_PHYID_REG1		0x02
+#define MII_PHYID_REG2		0x03
+
+static hw_info_t hw_info[] = {
+    { /* Accton EN2212 */ 0x0ff0, 0x00, 0x00, 0xe8, DELAY_OUTPUT },
+    { /* Allied Telesis LA-PCM */ 0x0ff0, 0x00, 0x00, 0xf4, 0 },
+    { /* APEX MultiCard */ 0x03f4, 0x00, 0x20, 0xe5, 0 },
+    { /* ASANTE FriendlyNet */ 0x4910, 0x00, 0x00, 0x94,
+      DELAY_OUTPUT | HAS_IBM_MISC },
+    { /* Danpex EN-6200P2 */ 0x0110, 0x00, 0x40, 0xc7, 0 },
+    { /* DataTrek NetCard */ 0x0ff0, 0x00, 0x20, 0xe8, 0 },
+    { /* Dayna CommuniCard E */ 0x0110, 0x00, 0x80, 0x19, 0 },
+    { /* D-Link DE-650 */ 0x0040, 0x00, 0x80, 0xc8, 0 },
+    { /* EP-210 Ethernet */ 0x0110, 0x00, 0x40, 0x33, 0 },
+    { /* EP4000 Ethernet */ 0x01c0, 0x00, 0x00, 0xb4, 0 },
+    { /* Epson EEN10B */ 0x0ff0, 0x00, 0x00, 0x48,
+      HAS_MISC_REG | HAS_IBM_MISC },
+    { /* ELECOM Laneed LD-CDWA */ 0xb8, 0x08, 0x00, 0x42, 0 },
+    { /* Hypertec Ethernet */ 0x01c0, 0x00, 0x40, 0x4c, 0 },
+    { /* IBM CCAE */ 0x0ff0, 0x08, 0x00, 0x5a,
+      HAS_MISC_REG | HAS_IBM_MISC },
+    { /* IBM CCAE */ 0x0ff0, 0x00, 0x04, 0xac,
+      HAS_MISC_REG | HAS_IBM_MISC },
+    { /* IBM CCAE */ 0x0ff0, 0x00, 0x06, 0x29,
+      HAS_MISC_REG | HAS_IBM_MISC },
+    { /* IBM FME */ 0x0374, 0x08, 0x00, 0x5a,
+      HAS_MISC_REG | HAS_IBM_MISC },
+    { /* IBM FME */ 0x0374, 0x00, 0x04, 0xac,
+      HAS_MISC_REG | HAS_IBM_MISC },
+    { /* Kansai KLA-PCM/T */ 0x0ff0, 0x00, 0x60, 0x87,
+      HAS_MISC_REG | HAS_IBM_MISC },
+    { /* NSC DP83903 */ 0x0374, 0x08, 0x00, 0x17,
+      HAS_MISC_REG | HAS_IBM_MISC },
+    { /* NSC DP83903 */ 0x0374, 0x00, 0xc0, 0xa8,
+      HAS_MISC_REG | HAS_IBM_MISC },
+    { /* NSC DP83903 */ 0x0374, 0x00, 0xa0, 0xb0,
+      HAS_MISC_REG | HAS_IBM_MISC },
+    { /* NSC DP83903 */ 0x0198, 0x00, 0x20, 0xe0,
+      HAS_MISC_REG | HAS_IBM_MISC },
+    { /* I-O DATA PCLA/T */ 0x0ff0, 0x00, 0xa0, 0xb0, 0 },
+    { /* Katron PE-520 */ 0x0110, 0x00, 0x40, 0xf6, 0 },
+    { /* Kingston KNE-PCM/x */ 0x0ff0, 0x00, 0xc0, 0xf0,
+      HAS_MISC_REG | HAS_IBM_MISC },
+    { /* Kingston KNE-PCM/x */ 0x0ff0, 0xe2, 0x0c, 0x0f,
+      HAS_MISC_REG | HAS_IBM_MISC },
+    { /* Kingston KNE-PC2 */ 0x0180, 0x00, 0xc0, 0xf0, 0 },
+    { /* Maxtech PCN2000 */ 0x5000, 0x00, 0x00, 0xe8, 0 },
+    { /* NDC Instant-Link */ 0x003a, 0x00, 0x80, 0xc6, 0 },
+    { /* NE2000 Compatible */ 0x0ff0, 0x00, 0xa0, 0x0c, 0 },
+    { /* Network General Sniffer */ 0x0ff0, 0x00, 0x00, 0x65,
+      HAS_MISC_REG | HAS_IBM_MISC },
+    { /* Panasonic VEL211 */ 0x0ff0, 0x00, 0x80, 0x45,
+      HAS_MISC_REG | HAS_IBM_MISC },
+    { /* PreMax PE-200 */ 0x07f0, 0x00, 0x20, 0xe0, 0 },
+    { /* RPTI EP400 */ 0x0110, 0x00, 0x40, 0x95, 0 },
+    { /* SCM Ethernet */ 0x0ff0, 0x00, 0x20, 0xcb, 0 },
+    { /* Socket EA */ 0x4000, 0x00, 0xc0, 0x1b,
+      DELAY_OUTPUT | HAS_MISC_REG | USE_BIG_BUF },
+    { /* Socket LP-E CF+ */ 0x01c0, 0x00, 0xc0, 0x1b, 0 },
+    { /* SuperSocket RE450T */ 0x0110, 0x00, 0xe0, 0x98, 0 },
+    { /* Volktek NPL-402CT */ 0x0060, 0x00, 0x40, 0x05, 0 },
+    { /* NEC PC-9801N-J12 */ 0x0ff0, 0x00, 0x00, 0x4c, 0 },
+    { /* PCMCIA Technology OEM */ 0x01c8, 0x00, 0xa0, 0x0c, 0 }
+};
+
+#define NR_INFO		ARRAY_SIZE(hw_info)
+
+static hw_info_t default_info = { 0, 0, 0, 0, 0 };
+static hw_info_t dl10019_info = { 0, 0, 0, 0, IS_DL10019|HAS_MII };
+static hw_info_t dl10022_info = { 0, 0, 0, 0, IS_DL10022|HAS_MII };
+
+typedef struct pcnet_dev_t {
+	struct pcmcia_device	*p_dev;
+    u_int		flags;
+    void		__iomem *base;
+    struct timer_list	watchdog;
+    int			stale, fast_poll;
+    u_char		phy_id;
+    u_char		eth_phy, pna_phy;
+    u_short		link_status;
+    u_long		mii_reset;
+} pcnet_dev_t;
+
+static inline pcnet_dev_t *PRIV(struct net_device *dev)
+{
+	char *p = netdev_priv(dev);
+	return (pcnet_dev_t *)(p + sizeof(struct ei_device));
+}
+
+static const struct net_device_ops pcnet_netdev_ops = {
+	.ndo_open		= pcnet_open,
+	.ndo_stop		= pcnet_close,
+	.ndo_set_config		= set_config,
+	.ndo_start_xmit 	= ei_start_xmit,
+	.ndo_get_stats		= ei_get_stats,
+	.ndo_do_ioctl 		= ei_ioctl,
+	.ndo_set_multicast_list = ei_set_multicast_list,
+	.ndo_tx_timeout 	= ei_tx_timeout,
+	.ndo_change_mtu		= eth_change_mtu,
+	.ndo_set_mac_address 	= eth_mac_addr,
+	.ndo_validate_addr	= eth_validate_addr,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	.ndo_poll_controller 	= ei_poll,
+#endif
+};
+
+static int pcnet_probe(struct pcmcia_device *link)
+{
+    pcnet_dev_t *info;
+    struct net_device *dev;
+
+    dev_dbg(&link->dev, "pcnet_attach()\n");
+
+    /* Create new ethernet device */
+    dev = __alloc_ei_netdev(sizeof(pcnet_dev_t));
+    if (!dev) return -ENOMEM;
+    info = PRIV(dev);
+    info->p_dev = link;
+    link->priv = dev;
+
+    link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO;
+
+    dev->netdev_ops = &pcnet_netdev_ops;
+
+    return pcnet_config(link);
+} /* pcnet_attach */
+
+static void pcnet_detach(struct pcmcia_device *link)
+{
+	struct net_device *dev = link->priv;
+
+	dev_dbg(&link->dev, "pcnet_detach\n");
+
+	unregister_netdev(dev);
+
+	pcnet_release(link);
+
+	free_netdev(dev);
+} /* pcnet_detach */
+
+/*======================================================================
+
+    This probes for a card's hardware address, for card types that
+    encode this information in their CIS.
+
+======================================================================*/
+
+static hw_info_t *get_hwinfo(struct pcmcia_device *link)
+{
+    struct net_device *dev = link->priv;
+    u_char __iomem *base, *virt;
+    int i, j;
+
+    /* Allocate a small memory window */
+    link->resource[2]->flags |= WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_AM|WIN_ENABLE;
+    link->resource[2]->start = 0; link->resource[2]->end = 0;
+    i = pcmcia_request_window(link, link->resource[2], 0);
+    if (i != 0)
+	return NULL;
+
+    virt = ioremap(link->resource[2]->start,
+	    resource_size(link->resource[2]));
+    for (i = 0; i < NR_INFO; i++) {
+	pcmcia_map_mem_page(link, link->resource[2],
+		hw_info[i].offset & ~(resource_size(link->resource[2])-1));
+	base = &virt[hw_info[i].offset & (resource_size(link->resource[2])-1)];
+	if ((readb(base+0) == hw_info[i].a0) &&
+	    (readb(base+2) == hw_info[i].a1) &&
+	    (readb(base+4) == hw_info[i].a2)) {
+		for (j = 0; j < 6; j++)
+		    dev->dev_addr[j] = readb(base + (j<<1));
+		break;
+	}
+    }
+
+    iounmap(virt);
+    j = pcmcia_release_window(link, link->resource[2]);
+    return (i < NR_INFO) ? hw_info+i : NULL;
+} /* get_hwinfo */
+
+/*======================================================================
+
+    This probes for a card's hardware address by reading the PROM.
+    It checks the address against a list of known types, then falls
+    back to a simple NE2000 clone signature check.
+
+======================================================================*/
+
+static hw_info_t *get_prom(struct pcmcia_device *link)
+{
+    struct net_device *dev = link->priv;
+    unsigned int ioaddr = dev->base_addr;
+    u_char prom[32];
+    int i, j;
+
+    /* This is lifted straight from drivers/net/ne.c */
+    struct {
+	u_char value, offset;
+    } program_seq[] = {
+	{E8390_NODMA+E8390_PAGE0+E8390_STOP, E8390_CMD}, /* Select page 0*/
+	{0x48,	EN0_DCFG},	/* Set byte-wide (0x48) access. */
+	{0x00,	EN0_RCNTLO},	/* Clear the count regs. */
+	{0x00,	EN0_RCNTHI},
+	{0x00,	EN0_IMR},	/* Mask completion irq. */
+	{0xFF,	EN0_ISR},
+	{E8390_RXOFF, EN0_RXCR},	/* 0x20  Set to monitor */
+	{E8390_TXOFF, EN0_TXCR},	/* 0x02  and loopback mode. */
+	{32,	EN0_RCNTLO},
+	{0x00,	EN0_RCNTHI},
+	{0x00,	EN0_RSARLO},	/* DMA starting at 0x0000. */
+	{0x00,	EN0_RSARHI},
+	{E8390_RREAD+E8390_START, E8390_CMD},
+    };
+
+    pcnet_reset_8390(dev);
+    mdelay(10);
+
+    for (i = 0; i < ARRAY_SIZE(program_seq); i++)
+	outb_p(program_seq[i].value, ioaddr + program_seq[i].offset);
+
+    for (i = 0; i < 32; i++)
+	prom[i] = inb(ioaddr + PCNET_DATAPORT);
+    for (i = 0; i < NR_INFO; i++) {
+	if ((prom[0] == hw_info[i].a0) &&
+	    (prom[2] == hw_info[i].a1) &&
+	    (prom[4] == hw_info[i].a2))
+	    break;
+    }
+    if ((i < NR_INFO) || ((prom[28] == 0x57) && (prom[30] == 0x57))) {
+	for (j = 0; j < 6; j++)
+	    dev->dev_addr[j] = prom[j<<1];
+	return (i < NR_INFO) ? hw_info+i : &default_info;
+    }
+    return NULL;
+} /* get_prom */
+
+/*======================================================================
+
+    For DL10019 based cards, like the Linksys EtherFast
+
+======================================================================*/
+
+static hw_info_t *get_dl10019(struct pcmcia_device *link)
+{
+    struct net_device *dev = link->priv;
+    int i;
+    u_char sum;
+
+    for (sum = 0, i = 0x14; i < 0x1c; i++)
+	sum += inb_p(dev->base_addr + i);
+    if (sum != 0xff)
+	return NULL;
+    for (i = 0; i < 6; i++)
+	dev->dev_addr[i] = inb_p(dev->base_addr + 0x14 + i);
+    i = inb(dev->base_addr + 0x1f);
+    return ((i == 0x91)||(i == 0x99)) ? &dl10022_info : &dl10019_info;
+}
+
+/*======================================================================
+
+    For Asix AX88190 based cards
+
+======================================================================*/
+
+static hw_info_t *get_ax88190(struct pcmcia_device *link)
+{
+    struct net_device *dev = link->priv;
+    unsigned int ioaddr = dev->base_addr;
+    int i, j;
+
+    /* Not much of a test, but the alternatives are messy */
+    if (link->config_base != 0x03c0)
+	return NULL;
+
+    outb_p(0x01, ioaddr + EN0_DCFG);	/* Set word-wide access. */
+    outb_p(0x00, ioaddr + EN0_RSARLO);	/* DMA starting at 0x0400. */
+    outb_p(0x04, ioaddr + EN0_RSARHI);
+    outb_p(E8390_RREAD+E8390_START, ioaddr + E8390_CMD);
+
+    for (i = 0; i < 6; i += 2) {
+	j = inw(ioaddr + PCNET_DATAPORT);
+	dev->dev_addr[i] = j & 0xff;
+	dev->dev_addr[i+1] = j >> 8;
+    }
+    return NULL;
+}
+
+/*======================================================================
+
+    This should be totally unnecessary... but when we can't figure
+    out the hardware address any other way, we'll let the user hard
+    wire it when the module is initialized.
+
+======================================================================*/
+
+static hw_info_t *get_hwired(struct pcmcia_device *link)
+{
+    struct net_device *dev = link->priv;
+    int i;
+
+    for (i = 0; i < 6; i++)
+	if (hw_addr[i] != 0) break;
+    if (i == 6)
+	return NULL;
+
+    for (i = 0; i < 6; i++)
+	dev->dev_addr[i] = hw_addr[i];
+
+    return &default_info;
+} /* get_hwired */
+
+static int try_io_port(struct pcmcia_device *link)
+{
+    int j, ret;
+    link->resource[0]->flags &= ~IO_DATA_PATH_WIDTH;
+    link->resource[1]->flags &= ~IO_DATA_PATH_WIDTH;
+    if (link->resource[0]->end == 32) {
+	link->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO;
+	if (link->resource[1]->end > 0) {
+	    /* for master/slave multifunction cards */
+	    link->resource[1]->flags |= IO_DATA_PATH_WIDTH_8;
+	}
+    } else {
+	/* This should be two 16-port windows */
+	link->resource[0]->flags |= IO_DATA_PATH_WIDTH_8;
+	link->resource[1]->flags |= IO_DATA_PATH_WIDTH_16;
+    }
+    if (link->resource[0]->start == 0) {
+	for (j = 0; j < 0x400; j += 0x20) {
+	    link->resource[0]->start = j ^ 0x300;
+	    link->resource[1]->start = (j ^ 0x300) + 0x10;
+	    link->io_lines = 16;
+	    ret = pcmcia_request_io(link);
+	    if (ret == 0)
+		    return ret;
+	}
+	return ret;
+    } else {
+	return pcmcia_request_io(link);
+    }
+}
+
+static int pcnet_confcheck(struct pcmcia_device *p_dev, void *priv_data)
+{
+	int *priv = priv_data;
+	int try = (*priv & 0x1);
+
+	*priv &= (p_dev->resource[2]->end >= 0x4000) ? 0x10 : ~0x10;
+
+	if (p_dev->config_index == 0)
+		return -EINVAL;
+
+	if (p_dev->resource[0]->end + p_dev->resource[1]->end < 32)
+		return -EINVAL;
+
+	if (try)
+		p_dev->io_lines = 16;
+	return try_io_port(p_dev);
+}
+
+static hw_info_t *pcnet_try_config(struct pcmcia_device *link,
+				   int *has_shmem, int try)
+{
+	struct net_device *dev = link->priv;
+	hw_info_t *local_hw_info;
+	pcnet_dev_t *info = PRIV(dev);
+	int priv = try;
+	int ret;
+
+	ret = pcmcia_loop_config(link, pcnet_confcheck, &priv);
+	if (ret) {
+		dev_warn(&link->dev, "no useable port range found\n");
+		return NULL;
+	}
+	*has_shmem = (priv & 0x10);
+
+	if (!link->irq)
+		return NULL;
+
+	if (resource_size(link->resource[1]) == 8)
+		link->config_flags |= CONF_ENABLE_SPKR;
+
+	if ((link->manf_id == MANFID_IBM) &&
+	    (link->card_id == PRODID_IBM_HOME_AND_AWAY))
+		link->config_index |= 0x10;
+
+	ret = pcmcia_enable_device(link);
+	if (ret)
+		return NULL;
+
+	dev->irq = link->irq;
+	dev->base_addr = link->resource[0]->start;
+
+	if (info->flags & HAS_MISC_REG) {
+		if ((if_port == 1) || (if_port == 2))
+			dev->if_port = if_port;
+		else
+			dev_notice(&link->dev, "invalid if_port requested\n");
+	} else
+		dev->if_port = 0;
+
+	if ((link->config_base == 0x03c0) &&
+	    (link->manf_id == 0x149) && (link->card_id == 0xc1ab)) {
+		dev_info(&link->dev,
+			"this is an AX88190 card - use axnet_cs instead.\n");
+		return NULL;
+	}
+
+	local_hw_info = get_hwinfo(link);
+	if (!local_hw_info)
+		local_hw_info = get_prom(link);
+	if (!local_hw_info)
+		local_hw_info = get_dl10019(link);
+	if (!local_hw_info)
+		local_hw_info = get_ax88190(link);
+	if (!local_hw_info)
+		local_hw_info = get_hwired(link);
+
+	return local_hw_info;
+}
+
+static int pcnet_config(struct pcmcia_device *link)
+{
+    struct net_device *dev = link->priv;
+    pcnet_dev_t *info = PRIV(dev);
+    int start_pg, stop_pg, cm_offset;
+    int has_shmem = 0;
+    hw_info_t *local_hw_info;
+
+    dev_dbg(&link->dev, "pcnet_config\n");
+
+    local_hw_info = pcnet_try_config(link, &has_shmem, 0);
+    if (!local_hw_info) {
+	    /* check whether forcing io_lines to 16 helps... */
+	    pcmcia_disable_device(link);
+	    local_hw_info = pcnet_try_config(link, &has_shmem, 1);
+	    if (local_hw_info == NULL) {
+		    dev_notice(&link->dev, "unable to read hardware net"
+			    " address for io base %#3lx\n", dev->base_addr);
+		    goto failed;
+	    }
+    }
+
+    info->flags = local_hw_info->flags;
+    /* Check for user overrides */
+    info->flags |= (delay_output) ? DELAY_OUTPUT : 0;
+    if ((link->manf_id == MANFID_SOCKET) &&
+	((link->card_id == PRODID_SOCKET_LPE) ||
+	 (link->card_id == PRODID_SOCKET_LPE_CF) ||
+	 (link->card_id == PRODID_SOCKET_EIO)))
+	info->flags &= ~USE_BIG_BUF;
+    if (!use_big_buf)
+	info->flags &= ~USE_BIG_BUF;
+
+    if (info->flags & USE_BIG_BUF) {
+	start_pg = SOCKET_START_PG;
+	stop_pg = SOCKET_STOP_PG;
+	cm_offset = 0x10000;
+    } else {
+	start_pg = PCNET_START_PG;
+	stop_pg = PCNET_STOP_PG;
+	cm_offset = 0;
+    }
+
+    /* has_shmem is ignored if use_shmem != -1 */
+    if ((use_shmem == 0) || (!has_shmem && (use_shmem == -1)) ||
+	(setup_shmem_window(link, start_pg, stop_pg, cm_offset) != 0))
+	setup_dma_config(link, start_pg, stop_pg);
+
+    ei_status.name = "NE2000";
+    ei_status.word16 = 1;
+    ei_status.reset_8390 = pcnet_reset_8390;
+
+    if (info->flags & (IS_DL10019|IS_DL10022))
+	mii_phy_probe(dev);
+
+    SET_NETDEV_DEV(dev, &link->dev);
+
+    if (register_netdev(dev) != 0) {
+	pr_notice("register_netdev() failed\n");
+	goto failed;
+    }
+
+    if (info->flags & (IS_DL10019|IS_DL10022)) {
+	u_char id = inb(dev->base_addr + 0x1a);
+	netdev_info(dev, "NE2000 (DL100%d rev %02x): ",
+	       (info->flags & IS_DL10022) ? 22 : 19, id);
+	if (info->pna_phy)
+	    pr_cont("PNA, ");
+    } else {
+	netdev_info(dev, "NE2000 Compatible: ");
+    }
+    pr_cont("io %#3lx, irq %d,", dev->base_addr, dev->irq);
+    if (info->flags & USE_SHMEM)
+	pr_cont(" mem %#5lx,", dev->mem_start);
+    if (info->flags & HAS_MISC_REG)
+	pr_cont(" %s xcvr,", if_names[dev->if_port]);
+    pr_cont(" hw_addr %pM\n", dev->dev_addr);
+    return 0;
+
+failed:
+    pcnet_release(link);
+    return -ENODEV;
+} /* pcnet_config */
+
+static void pcnet_release(struct pcmcia_device *link)
+{
+	pcnet_dev_t *info = PRIV(link->priv);
+
+	dev_dbg(&link->dev, "pcnet_release\n");
+
+	if (info->flags & USE_SHMEM)
+		iounmap(info->base);
+
+	pcmcia_disable_device(link);
+}
+
+static int pcnet_suspend(struct pcmcia_device *link)
+{
+	struct net_device *dev = link->priv;
+
+	if (link->open)
+		netif_device_detach(dev);
+
+	return 0;
+}
+
+static int pcnet_resume(struct pcmcia_device *link)
+{
+	struct net_device *dev = link->priv;
+
+	if (link->open) {
+		pcnet_reset_8390(dev);
+		NS8390_init(dev, 1);
+		netif_device_attach(dev);
+	}
+
+	return 0;
+}
+
+
+/*======================================================================
+
+    MII interface support for DL10019 and DL10022 based cards
+
+    On the DL10019, the MII IO direction bit is 0x10; on the DL10022
+    it is 0x20.  Setting both bits seems to work on both card types.
+
+======================================================================*/
+
+#define DLINK_GPIO		0x1c
+#define DLINK_DIAG		0x1d
+#define DLINK_EEPROM		0x1e
+
+#define MDIO_SHIFT_CLK		0x80
+#define MDIO_DATA_OUT		0x40
+#define MDIO_DIR_WRITE		0x30
+#define MDIO_DATA_WRITE0	(MDIO_DIR_WRITE)
+#define MDIO_DATA_WRITE1	(MDIO_DIR_WRITE | MDIO_DATA_OUT)
+#define MDIO_DATA_READ		0x10
+#define MDIO_MASK		0x0f
+
+static void mdio_sync(unsigned int addr)
+{
+    int bits, mask = inb(addr) & MDIO_MASK;
+    for (bits = 0; bits < 32; bits++) {
+	outb(mask | MDIO_DATA_WRITE1, addr);
+	outb(mask | MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, addr);
+    }
+}
+
+static int mdio_read(unsigned int addr, int phy_id, int loc)
+{
+    u_int cmd = (0x06<<10)|(phy_id<<5)|loc;
+    int i, retval = 0, mask = inb(addr) & MDIO_MASK;
+
+    mdio_sync(addr);
+    for (i = 13; i >= 0; i--) {
+	int dat = (cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0;
+	outb(mask | dat, addr);
+	outb(mask | dat | MDIO_SHIFT_CLK, addr);
+    }
+    for (i = 19; i > 0; i--) {
+	outb(mask, addr);
+	retval = (retval << 1) | ((inb(addr) & MDIO_DATA_READ) != 0);
+	outb(mask | MDIO_SHIFT_CLK, addr);
+    }
+    return (retval>>1) & 0xffff;
+}
+
+static void mdio_write(unsigned int addr, int phy_id, int loc, int value)
+{
+    u_int cmd = (0x05<<28)|(phy_id<<23)|(loc<<18)|(1<<17)|value;
+    int i, mask = inb(addr) & MDIO_MASK;
+
+    mdio_sync(addr);
+    for (i = 31; i >= 0; i--) {
+	int dat = (cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0;
+	outb(mask | dat, addr);
+	outb(mask | dat | MDIO_SHIFT_CLK, addr);
+    }
+    for (i = 1; i >= 0; i--) {
+	outb(mask, addr);
+	outb(mask | MDIO_SHIFT_CLK, addr);
+    }
+}
+
+/*======================================================================
+
+    EEPROM access routines for DL10019 and DL10022 based cards
+
+======================================================================*/
+
+#define EE_EEP		0x40
+#define EE_ASIC		0x10
+#define EE_CS		0x08
+#define EE_CK		0x04
+#define EE_DO		0x02
+#define EE_DI		0x01
+#define EE_ADOT		0x01	/* DataOut for ASIC */
+#define EE_READ_CMD	0x06
+
+#define DL19FDUPLX	0x0400	/* DL10019 Full duplex mode */
+
+static int read_eeprom(unsigned int ioaddr, int location)
+{
+    int i, retval = 0;
+    unsigned int ee_addr = ioaddr + DLINK_EEPROM;
+    int read_cmd = location | (EE_READ_CMD << 8);
+
+    outb(0, ee_addr);
+    outb(EE_EEP|EE_CS, ee_addr);
+
+    /* Shift the read command bits out. */
+    for (i = 10; i >= 0; i--) {
+	short dataval = (read_cmd & (1 << i)) ? EE_DO : 0;
+	outb_p(EE_EEP|EE_CS|dataval, ee_addr);
+	outb_p(EE_EEP|EE_CS|dataval|EE_CK, ee_addr);
+    }
+    outb(EE_EEP|EE_CS, ee_addr);
+
+    for (i = 16; i > 0; i--) {
+	outb_p(EE_EEP|EE_CS | EE_CK, ee_addr);
+	retval = (retval << 1) | ((inb(ee_addr) & EE_DI) ? 1 : 0);
+	outb_p(EE_EEP|EE_CS, ee_addr);
+    }
+
+    /* Terminate the EEPROM access. */
+    outb(0, ee_addr);
+    return retval;
+}
+
+/*
+    The internal ASIC registers can be changed by EEPROM READ access
+    with EE_ASIC bit set.
+    In ASIC mode, EE_ADOT is used to output the data to the ASIC.
+*/
+
+static void write_asic(unsigned int ioaddr, int location, short asic_data)
+{
+	int i;
+	unsigned int ee_addr = ioaddr + DLINK_EEPROM;
+	short dataval;
+	int read_cmd = location | (EE_READ_CMD << 8);
+
+	asic_data |= read_eeprom(ioaddr, location);
+
+	outb(0, ee_addr);
+	outb(EE_ASIC|EE_CS|EE_DI, ee_addr);
+
+	read_cmd = read_cmd >> 1;
+
+	/* Shift the read command bits out. */
+	for (i = 9; i >= 0; i--) {
+		dataval = (read_cmd & (1 << i)) ? EE_DO : 0;
+		outb_p(EE_ASIC|EE_CS|EE_DI|dataval, ee_addr);
+		outb_p(EE_ASIC|EE_CS|EE_DI|dataval|EE_CK, ee_addr);
+		outb_p(EE_ASIC|EE_CS|EE_DI|dataval, ee_addr);
+	}
+	// sync
+	outb(EE_ASIC|EE_CS, ee_addr);
+	outb(EE_ASIC|EE_CS|EE_CK, ee_addr);
+	outb(EE_ASIC|EE_CS, ee_addr);
+
+	for (i = 15; i >= 0; i--) {
+		dataval = (asic_data & (1 << i)) ? EE_ADOT : 0;
+		outb_p(EE_ASIC|EE_CS|dataval, ee_addr);
+		outb_p(EE_ASIC|EE_CS|dataval|EE_CK, ee_addr);
+		outb_p(EE_ASIC|EE_CS|dataval, ee_addr);
+	}
+
+	/* Terminate the ASIC access. */
+	outb(EE_ASIC|EE_DI, ee_addr);
+	outb(EE_ASIC|EE_DI| EE_CK, ee_addr);
+	outb(EE_ASIC|EE_DI, ee_addr);
+
+	outb(0, ee_addr);
+}
+
+/*====================================================================*/
+
+static void set_misc_reg(struct net_device *dev)
+{
+    unsigned int nic_base = dev->base_addr;
+    pcnet_dev_t *info = PRIV(dev);
+    u_char tmp;
+
+    if (info->flags & HAS_MISC_REG) {
+	tmp = inb_p(nic_base + PCNET_MISC) & ~3;
+	if (dev->if_port == 2)
+	    tmp |= 1;
+	if (info->flags & USE_BIG_BUF)
+	    tmp |= 2;
+	if (info->flags & HAS_IBM_MISC)
+	    tmp |= 8;
+	outb_p(tmp, nic_base + PCNET_MISC);
+    }
+    if (info->flags & IS_DL10022) {
+	if (info->flags & HAS_MII) {
+	    /* Advertise 100F, 100H, 10F, 10H */
+	    mdio_write(nic_base + DLINK_GPIO, info->eth_phy, 4, 0x01e1);
+	    /* Restart MII autonegotiation */
+	    mdio_write(nic_base + DLINK_GPIO, info->eth_phy, 0, 0x0000);
+	    mdio_write(nic_base + DLINK_GPIO, info->eth_phy, 0, 0x1200);
+	    info->mii_reset = jiffies;
+	} else {
+	    outb(full_duplex ? 4 : 0, nic_base + DLINK_DIAG);
+	}
+    } else if (info->flags & IS_DL10019) {
+	/* Advertise 100F, 100H, 10F, 10H */
+	mdio_write(nic_base + DLINK_GPIO, info->eth_phy, 4, 0x01e1);
+	/* Restart MII autonegotiation */
+	mdio_write(nic_base + DLINK_GPIO, info->eth_phy, 0, 0x0000);
+	mdio_write(nic_base + DLINK_GPIO, info->eth_phy, 0, 0x1200);
+    }
+}
+
+/*====================================================================*/
+
+static void mii_phy_probe(struct net_device *dev)
+{
+    pcnet_dev_t *info = PRIV(dev);
+    unsigned int mii_addr = dev->base_addr + DLINK_GPIO;
+    int i;
+    u_int tmp, phyid;
+
+    for (i = 31; i >= 0; i--) {
+	tmp = mdio_read(mii_addr, i, 1);
+	if ((tmp == 0) || (tmp == 0xffff))
+	    continue;
+	tmp = mdio_read(mii_addr, i, MII_PHYID_REG1);
+	phyid = tmp << 16;
+	phyid |= mdio_read(mii_addr, i, MII_PHYID_REG2);
+	phyid &= MII_PHYID_REV_MASK;
+	netdev_dbg(dev, "MII at %d is 0x%08x\n", i, phyid);
+	if (phyid == AM79C9XX_HOME_PHY) {
+	    info->pna_phy = i;
+	} else if (phyid != AM79C9XX_ETH_PHY) {
+	    info->eth_phy = i;
+	}
+    }
+}
+
+static int pcnet_open(struct net_device *dev)
+{
+    int ret;
+    pcnet_dev_t *info = PRIV(dev);
+    struct pcmcia_device *link = info->p_dev;
+    unsigned int nic_base = dev->base_addr;
+
+    dev_dbg(&link->dev, "pcnet_open('%s')\n", dev->name);
+
+    if (!pcmcia_dev_present(link))
+	return -ENODEV;
+
+    set_misc_reg(dev);
+
+    outb_p(0xFF, nic_base + EN0_ISR); /* Clear bogus intr. */
+    ret = request_irq(dev->irq, ei_irq_wrapper, IRQF_SHARED, dev->name, dev);
+    if (ret)
+	    return ret;
+
+    link->open++;
+
+    info->phy_id = info->eth_phy;
+    info->link_status = 0x00;
+    init_timer(&info->watchdog);
+    info->watchdog.function = ei_watchdog;
+    info->watchdog.data = (u_long)dev;
+    info->watchdog.expires = jiffies + HZ;
+    add_timer(&info->watchdog);
+
+    return ei_open(dev);
+} /* pcnet_open */
+
+/*====================================================================*/
+
+static int pcnet_close(struct net_device *dev)
+{
+    pcnet_dev_t *info = PRIV(dev);
+    struct pcmcia_device *link = info->p_dev;
+
+    dev_dbg(&link->dev, "pcnet_close('%s')\n", dev->name);
+
+    ei_close(dev);
+    free_irq(dev->irq, dev);
+
+    link->open--;
+    netif_stop_queue(dev);
+    del_timer_sync(&info->watchdog);
+
+    return 0;
+} /* pcnet_close */
+
+/*======================================================================
+
+    Hard reset the card.  This used to pause for the same period that
+    a 8390 reset command required, but that shouldn't be necessary.
+
+======================================================================*/
+
+static void pcnet_reset_8390(struct net_device *dev)
+{
+    unsigned int nic_base = dev->base_addr;
+    int i;
+
+    ei_status.txing = ei_status.dmaing = 0;
+
+    outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, nic_base + E8390_CMD);
+
+    outb(inb(nic_base + PCNET_RESET), nic_base + PCNET_RESET);
+
+    for (i = 0; i < 100; i++) {
+	if ((inb_p(nic_base+EN0_ISR) & ENISR_RESET) != 0)
+	    break;
+	udelay(100);
+    }
+    outb_p(ENISR_RESET, nic_base + EN0_ISR); /* Ack intr. */
+
+    if (i == 100)
+	netdev_err(dev, "pcnet_reset_8390() did not complete.\n");
+
+    set_misc_reg(dev);
+
+} /* pcnet_reset_8390 */
+
+/*====================================================================*/
+
+static int set_config(struct net_device *dev, struct ifmap *map)
+{
+    pcnet_dev_t *info = PRIV(dev);
+    if ((map->port != (u_char)(-1)) && (map->port != dev->if_port)) {
+	if (!(info->flags & HAS_MISC_REG))
+	    return -EOPNOTSUPP;
+	else if ((map->port < 1) || (map->port > 2))
+	    return -EINVAL;
+	dev->if_port = map->port;
+	netdev_info(dev, "switched to %s port\n", if_names[dev->if_port]);
+	NS8390_init(dev, 1);
+    }
+    return 0;
+}
+
+/*====================================================================*/
+
+static irqreturn_t ei_irq_wrapper(int irq, void *dev_id)
+{
+    struct net_device *dev = dev_id;
+    pcnet_dev_t *info;
+    irqreturn_t ret = ei_interrupt(irq, dev_id);
+
+    if (ret == IRQ_HANDLED) {
+	    info = PRIV(dev);
+	    info->stale = 0;
+    }
+    return ret;
+}
+
+static void ei_watchdog(u_long arg)
+{
+    struct net_device *dev = (struct net_device *)arg;
+    pcnet_dev_t *info = PRIV(dev);
+    unsigned int nic_base = dev->base_addr;
+    unsigned int mii_addr = nic_base + DLINK_GPIO;
+    u_short link;
+
+    if (!netif_device_present(dev)) goto reschedule;
+
+    /* Check for pending interrupt with expired latency timer: with
+       this, we can limp along even if the interrupt is blocked */
+    if (info->stale++ && (inb_p(nic_base + EN0_ISR) & ENISR_ALL)) {
+	if (!info->fast_poll)
+	    netdev_info(dev, "interrupt(s) dropped!\n");
+	ei_irq_wrapper(dev->irq, dev);
+	info->fast_poll = HZ;
+    }
+    if (info->fast_poll) {
+	info->fast_poll--;
+	info->watchdog.expires = jiffies + 1;
+	add_timer(&info->watchdog);
+	return;
+    }
+
+    if (!(info->flags & HAS_MII))
+	goto reschedule;
+
+    mdio_read(mii_addr, info->phy_id, 1);
+    link = mdio_read(mii_addr, info->phy_id, 1);
+    if (!link || (link == 0xffff)) {
+	if (info->eth_phy) {
+	    info->phy_id = info->eth_phy = 0;
+	} else {
+	    netdev_info(dev, "MII is missing!\n");
+	    info->flags &= ~HAS_MII;
+	}
+	goto reschedule;
+    }
+
+    link &= 0x0004;
+    if (link != info->link_status) {
+	u_short p = mdio_read(mii_addr, info->phy_id, 5);
+	netdev_info(dev, "%s link beat\n", link ? "found" : "lost");
+	if (link && (info->flags & IS_DL10022)) {
+	    /* Disable collision detection on full duplex links */
+	    outb((p & 0x0140) ? 4 : 0, nic_base + DLINK_DIAG);
+	} else if (link && (info->flags & IS_DL10019)) {
+	    /* Disable collision detection on full duplex links */
+	    write_asic(dev->base_addr, 4, (p & 0x140) ? DL19FDUPLX : 0);
+	}
+	if (link) {
+	    if (info->phy_id == info->eth_phy) {
+		if (p)
+		    netdev_info(dev, "autonegotiation complete: "
+			   "%sbaseT-%cD selected\n",
+			   ((p & 0x0180) ? "100" : "10"),
+			   ((p & 0x0140) ? 'F' : 'H'));
+		else
+		    netdev_info(dev, "link partner did not autonegotiate\n");
+	    }
+	    NS8390_init(dev, 1);
+	}
+	info->link_status = link;
+    }
+    if (info->pna_phy && time_after(jiffies, info->mii_reset + 6*HZ)) {
+	link = mdio_read(mii_addr, info->eth_phy, 1) & 0x0004;
+	if (((info->phy_id == info->pna_phy) && link) ||
+	    ((info->phy_id != info->pna_phy) && !link)) {
+	    /* isolate this MII and try flipping to the other one */
+	    mdio_write(mii_addr, info->phy_id, 0, 0x0400);
+	    info->phy_id ^= info->pna_phy ^ info->eth_phy;
+	    netdev_info(dev, "switched to %s transceiver\n",
+		   (info->phy_id == info->eth_phy) ? "ethernet" : "PNA");
+	    mdio_write(mii_addr, info->phy_id, 0,
+		       (info->phy_id == info->eth_phy) ? 0x1000 : 0);
+	    info->link_status = 0;
+	    info->mii_reset = jiffies;
+	}
+    }
+
+reschedule:
+    info->watchdog.expires = jiffies + HZ;
+    add_timer(&info->watchdog);
+}
+
+/*====================================================================*/
+
+
+static int ei_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+    pcnet_dev_t *info = PRIV(dev);
+    struct mii_ioctl_data *data = if_mii(rq);
+    unsigned int mii_addr = dev->base_addr + DLINK_GPIO;
+
+    if (!(info->flags & (IS_DL10019|IS_DL10022)))
+	return -EINVAL;
+
+    switch (cmd) {
+    case SIOCGMIIPHY:
+	data->phy_id = info->phy_id;
+    case SIOCGMIIREG:		/* Read MII PHY register. */
+	data->val_out = mdio_read(mii_addr, data->phy_id, data->reg_num & 0x1f);
+	return 0;
+    case SIOCSMIIREG:		/* Write MII PHY register. */
+	mdio_write(mii_addr, data->phy_id, data->reg_num & 0x1f, data->val_in);
+	return 0;
+    }
+    return -EOPNOTSUPP;
+}
+
+/*====================================================================*/
+
+static void dma_get_8390_hdr(struct net_device *dev,
+			     struct e8390_pkt_hdr *hdr,
+			     int ring_page)
+{
+    unsigned int nic_base = dev->base_addr;
+
+    if (ei_status.dmaing) {
+	netdev_notice(dev, "DMAing conflict in dma_block_input."
+	       "[DMAstat:%1x][irqlock:%1x]\n",
+	       ei_status.dmaing, ei_status.irqlock);
+	return;
+    }
+
+    ei_status.dmaing |= 0x01;
+    outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base + PCNET_CMD);
+    outb_p(sizeof(struct e8390_pkt_hdr), nic_base + EN0_RCNTLO);
+    outb_p(0, nic_base + EN0_RCNTHI);
+    outb_p(0, nic_base + EN0_RSARLO);		/* On page boundary */
+    outb_p(ring_page, nic_base + EN0_RSARHI);
+    outb_p(E8390_RREAD+E8390_START, nic_base + PCNET_CMD);
+
+    insw(nic_base + PCNET_DATAPORT, hdr,
+	    sizeof(struct e8390_pkt_hdr)>>1);
+    /* Fix for big endian systems */
+    hdr->count = le16_to_cpu(hdr->count);
+
+    outb_p(ENISR_RDC, nic_base + EN0_ISR);	/* Ack intr. */
+    ei_status.dmaing &= ~0x01;
+}
+
+/*====================================================================*/
+
+static void dma_block_input(struct net_device *dev, int count,
+			    struct sk_buff *skb, int ring_offset)
+{
+    unsigned int nic_base = dev->base_addr;
+    int xfer_count = count;
+    char *buf = skb->data;
+
+    if ((ei_debug > 4) && (count != 4))
+	netdev_dbg(dev, "[bi=%d]\n", count+4);
+    if (ei_status.dmaing) {
+	netdev_notice(dev, "DMAing conflict in dma_block_input."
+	       "[DMAstat:%1x][irqlock:%1x]\n",
+	       ei_status.dmaing, ei_status.irqlock);
+	return;
+    }
+    ei_status.dmaing |= 0x01;
+    outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base + PCNET_CMD);
+    outb_p(count & 0xff, nic_base + EN0_RCNTLO);
+    outb_p(count >> 8, nic_base + EN0_RCNTHI);
+    outb_p(ring_offset & 0xff, nic_base + EN0_RSARLO);
+    outb_p(ring_offset >> 8, nic_base + EN0_RSARHI);
+    outb_p(E8390_RREAD+E8390_START, nic_base + PCNET_CMD);
+
+    insw(nic_base + PCNET_DATAPORT,buf,count>>1);
+    if (count & 0x01)
+	buf[count-1] = inb(nic_base + PCNET_DATAPORT), xfer_count++;
+
+    /* This was for the ALPHA version only, but enough people have been
+       encountering problems that it is still here. */
+#ifdef PCMCIA_DEBUG
+    if (ei_debug > 4) {		/* DMA termination address check... */
+	int addr, tries = 20;
+	do {
+	    /* DON'T check for 'inb_p(EN0_ISR) & ENISR_RDC' here
+	       -- it's broken for Rx on some cards! */
+	    int high = inb_p(nic_base + EN0_RSARHI);
+	    int low = inb_p(nic_base + EN0_RSARLO);
+	    addr = (high << 8) + low;
+	    if (((ring_offset + xfer_count) & 0xff) == (addr & 0xff))
+		break;
+	} while (--tries > 0);
+	if (tries <= 0)
+	    netdev_notice(dev, "RX transfer address mismatch,"
+		   "%#4.4x (expected) vs. %#4.4x (actual).\n",
+		   ring_offset + xfer_count, addr);
+    }
+#endif
+    outb_p(ENISR_RDC, nic_base + EN0_ISR);	/* Ack intr. */
+    ei_status.dmaing &= ~0x01;
+} /* dma_block_input */
+
+/*====================================================================*/
+
+static void dma_block_output(struct net_device *dev, int count,
+			     const u_char *buf, const int start_page)
+{
+    unsigned int nic_base = dev->base_addr;
+    pcnet_dev_t *info = PRIV(dev);
+#ifdef PCMCIA_DEBUG
+    int retries = 0;
+#endif
+    u_long dma_start;
+
+#ifdef PCMCIA_DEBUG
+    if (ei_debug > 4)
+	netdev_dbg(dev, "[bo=%d]\n", count);
+#endif
+
+    /* Round the count up for word writes.  Do we need to do this?
+       What effect will an odd byte count have on the 8390?
+       I should check someday. */
+    if (count & 0x01)
+	count++;
+    if (ei_status.dmaing) {
+	netdev_notice(dev, "DMAing conflict in dma_block_output."
+	       "[DMAstat:%1x][irqlock:%1x]\n",
+	       ei_status.dmaing, ei_status.irqlock);
+	return;
+    }
+    ei_status.dmaing |= 0x01;
+    /* We should already be in page 0, but to be safe... */
+    outb_p(E8390_PAGE0+E8390_START+E8390_NODMA, nic_base+PCNET_CMD);
+
+#ifdef PCMCIA_DEBUG
+  retry:
+#endif
+
+    outb_p(ENISR_RDC, nic_base + EN0_ISR);
+
+    /* Now the normal output. */
+    outb_p(count & 0xff, nic_base + EN0_RCNTLO);
+    outb_p(count >> 8,   nic_base + EN0_RCNTHI);
+    outb_p(0x00, nic_base + EN0_RSARLO);
+    outb_p(start_page, nic_base + EN0_RSARHI);
+
+    outb_p(E8390_RWRITE+E8390_START, nic_base + PCNET_CMD);
+    outsw(nic_base + PCNET_DATAPORT, buf, count>>1);
+
+    dma_start = jiffies;
+
+#ifdef PCMCIA_DEBUG
+    /* This was for the ALPHA version only, but enough people have been
+       encountering problems that it is still here. */
+    if (ei_debug > 4) {	/* DMA termination address check... */
+	int addr, tries = 20;
+	do {
+	    int high = inb_p(nic_base + EN0_RSARHI);
+	    int low = inb_p(nic_base + EN0_RSARLO);
+	    addr = (high << 8) + low;
+	    if ((start_page << 8) + count == addr)
+		break;
+	} while (--tries > 0);
+	if (tries <= 0) {
+	    netdev_notice(dev, "Tx packet transfer address mismatch,"
+		   "%#4.4x (expected) vs. %#4.4x (actual).\n",
+		   (start_page << 8) + count, addr);
+	    if (retries++ == 0)
+		goto retry;
+	}
+    }
+#endif
+
+    while ((inb_p(nic_base + EN0_ISR) & ENISR_RDC) == 0)
+	if (time_after(jiffies, dma_start + PCNET_RDC_TIMEOUT)) {
+	    netdev_notice(dev, "timeout waiting for Tx RDC.\n");
+	    pcnet_reset_8390(dev);
+	    NS8390_init(dev, 1);
+	    break;
+	}
+
+    outb_p(ENISR_RDC, nic_base + EN0_ISR);	/* Ack intr. */
+    if (info->flags & DELAY_OUTPUT)
+	udelay((long)delay_time);
+    ei_status.dmaing &= ~0x01;
+}
+
+/*====================================================================*/
+
+static int setup_dma_config(struct pcmcia_device *link, int start_pg,
+			    int stop_pg)
+{
+    struct net_device *dev = link->priv;
+
+    ei_status.tx_start_page = start_pg;
+    ei_status.rx_start_page = start_pg + TX_PAGES;
+    ei_status.stop_page = stop_pg;
+
+    /* set up block i/o functions */
+    ei_status.get_8390_hdr = dma_get_8390_hdr;
+    ei_status.block_input = dma_block_input;
+    ei_status.block_output = dma_block_output;
+
+    return 0;
+}
+
+/*====================================================================*/
+
+static void copyin(void *dest, void __iomem *src, int c)
+{
+    u_short *d = dest;
+    u_short __iomem *s = src;
+    int odd;
+
+    if (c <= 0)
+	return;
+    odd = (c & 1); c >>= 1;
+
+    if (c) {
+	do { *d++ = __raw_readw(s++); } while (--c);
+    }
+    /* get last byte by fetching a word and masking */
+    if (odd)
+	*((u_char *)d) = readw(s) & 0xff;
+}
+
+static void copyout(void __iomem *dest, const void *src, int c)
+{
+    u_short __iomem *d = dest;
+    const u_short *s = src;
+    int odd;
+
+    if (c <= 0)
+	return;
+    odd = (c & 1); c >>= 1;
+
+    if (c) {
+	do { __raw_writew(*s++, d++); } while (--c);
+    }
+    /* copy last byte doing a read-modify-write */
+    if (odd)
+	writew((readw(d) & 0xff00) | *(u_char *)s, d);
+}
+
+/*====================================================================*/
+
+static void shmem_get_8390_hdr(struct net_device *dev,
+			       struct e8390_pkt_hdr *hdr,
+			       int ring_page)
+{
+    void __iomem *xfer_start = ei_status.mem + (TX_PAGES<<8)
+				+ (ring_page << 8)
+				- (ei_status.rx_start_page << 8);
+
+    copyin(hdr, xfer_start, sizeof(struct e8390_pkt_hdr));
+    /* Fix for big endian systems */
+    hdr->count = le16_to_cpu(hdr->count);
+}
+
+/*====================================================================*/
+
+static void shmem_block_input(struct net_device *dev, int count,
+			      struct sk_buff *skb, int ring_offset)
+{
+    void __iomem *base = ei_status.mem;
+    unsigned long offset = (TX_PAGES<<8) + ring_offset
+				- (ei_status.rx_start_page << 8);
+    char *buf = skb->data;
+
+    if (offset + count > ei_status.priv) {
+	/* We must wrap the input move. */
+	int semi_count = ei_status.priv - offset;
+	copyin(buf, base + offset, semi_count);
+	buf += semi_count;
+	offset = TX_PAGES<<8;
+	count -= semi_count;
+    }
+    copyin(buf, base + offset, count);
+}
+
+/*====================================================================*/
+
+static void shmem_block_output(struct net_device *dev, int count,
+			       const u_char *buf, const int start_page)
+{
+    void __iomem *shmem = ei_status.mem + (start_page << 8);
+    shmem -= ei_status.tx_start_page << 8;
+    copyout(shmem, buf, count);
+}
+
+/*====================================================================*/
+
+static int setup_shmem_window(struct pcmcia_device *link, int start_pg,
+			      int stop_pg, int cm_offset)
+{
+    struct net_device *dev = link->priv;
+    pcnet_dev_t *info = PRIV(dev);
+    int i, window_size, offset, ret;
+
+    window_size = (stop_pg - start_pg) << 8;
+    if (window_size > 32 * 1024)
+	window_size = 32 * 1024;
+
+    /* Make sure it's a power of two.  */
+    window_size = roundup_pow_of_two(window_size);
+
+    /* Allocate a memory window */
+    link->resource[3]->flags |= WIN_DATA_WIDTH_16|WIN_MEMORY_TYPE_CM|WIN_ENABLE;
+    link->resource[3]->flags |= WIN_USE_WAIT;
+    link->resource[3]->start = 0; link->resource[3]->end = window_size;
+    ret = pcmcia_request_window(link, link->resource[3], mem_speed);
+    if (ret)
+	    goto failed;
+
+    offset = (start_pg << 8) + cm_offset;
+    offset -= offset % window_size;
+    ret = pcmcia_map_mem_page(link, link->resource[3], offset);
+    if (ret)
+	    goto failed;
+
+    /* Try scribbling on the buffer */
+    info->base = ioremap(link->resource[3]->start,
+			resource_size(link->resource[3]));
+    for (i = 0; i < (TX_PAGES<<8); i += 2)
+	__raw_writew((i>>1), info->base+offset+i);
+    udelay(100);
+    for (i = 0; i < (TX_PAGES<<8); i += 2)
+	if (__raw_readw(info->base+offset+i) != (i>>1)) break;
+    pcnet_reset_8390(dev);
+    if (i != (TX_PAGES<<8)) {
+	iounmap(info->base);
+	pcmcia_release_window(link, link->resource[3]);
+	info->base = NULL;
+	goto failed;
+    }
+
+    ei_status.mem = info->base + offset;
+    ei_status.priv = resource_size(link->resource[3]);
+    dev->mem_start = (u_long)ei_status.mem;
+    dev->mem_end = dev->mem_start + resource_size(link->resource[3]);
+
+    ei_status.tx_start_page = start_pg;
+    ei_status.rx_start_page = start_pg + TX_PAGES;
+    ei_status.stop_page = start_pg + (
+	    (resource_size(link->resource[3]) - offset) >> 8);
+
+    /* set up block i/o functions */
+    ei_status.get_8390_hdr = shmem_get_8390_hdr;
+    ei_status.block_input = shmem_block_input;
+    ei_status.block_output = shmem_block_output;
+
+    info->flags |= USE_SHMEM;
+    return 0;
+
+failed:
+    return 1;
+}
+
+/*====================================================================*/
+
+static const struct pcmcia_device_id pcnet_ids[] = {
+	PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0057, 0x0021),
+	PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0104, 0x000a),
+	PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0105, 0xea15),
+	PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0143, 0x3341),
+	PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0143, 0xc0ab),
+	PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x021b, 0x0101),
+	PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x08a1, 0xc0ab),
+	PCMCIA_PFC_DEVICE_PROD_ID12(0, "AnyCom", "Fast Ethernet + 56K COMBO", 0x578ba6e7, 0xb0ac62c4),
+	PCMCIA_PFC_DEVICE_PROD_ID12(0, "ATKK", "LM33-PCM-T", 0xba9eb7e2, 0x077c174e),
+	PCMCIA_PFC_DEVICE_PROD_ID12(0, "D-Link", "DME336T", 0x1a424a1c, 0xb23897ff),
+	PCMCIA_PFC_DEVICE_PROD_ID12(0, "Grey Cell", "GCS3000", 0x2a151fac, 0x48b932ae),
+	PCMCIA_PFC_DEVICE_PROD_ID12(0, "Linksys", "EtherFast 10&100 + 56K PC Card (PCMLM56)", 0x0733cc81, 0xb3765033),
+	PCMCIA_PFC_DEVICE_PROD_ID12(0, "LINKSYS", "PCMLM336", 0xf7cb0b07, 0x7a821b58),
+	PCMCIA_PFC_DEVICE_PROD_ID12(0, "MICRO RESEARCH", "COMBO-L/M-336", 0xb2ced065, 0x3ced0555),
+	PCMCIA_PFC_DEVICE_PROD_ID12(0, "PCMCIAs", "ComboCard", 0xdcfe12d3, 0xcd8906cc),
+	PCMCIA_PFC_DEVICE_PROD_ID12(0, "PCMCIAs", "LanModem", 0xdcfe12d3, 0xc67c648f),
+	PCMCIA_MFC_DEVICE_PROD_ID12(0, "IBM", "Home and Away 28.8 PC Card       ", 0xb569a6e5, 0x5bd4ff2c),
+	PCMCIA_MFC_DEVICE_PROD_ID12(0, "IBM", "Home and Away Credit Card Adapter", 0xb569a6e5, 0x4bdf15c3),
+	PCMCIA_MFC_DEVICE_PROD_ID12(0, "IBM", "w95 Home and Away Credit Card ", 0xb569a6e5, 0xae911c15),
+	PCMCIA_MFC_DEVICE_PROD_ID123(0, "APEX DATA", "MULTICARD", "ETHERNET-MODEM", 0x11c2da09, 0x7289dc5d, 0xaad95e1f),
+	PCMCIA_MFC_DEVICE_PROD_ID2(0, "FAX/Modem/Ethernet Combo Card ", 0x1ed59302),
+	PCMCIA_DEVICE_MANF_CARD(0x0057, 0x1004),
+	PCMCIA_DEVICE_MANF_CARD(0x0104, 0x000d),
+	PCMCIA_DEVICE_MANF_CARD(0x0104, 0x0075),
+	PCMCIA_DEVICE_MANF_CARD(0x0104, 0x0145),
+	PCMCIA_DEVICE_MANF_CARD(0x0149, 0x0230),
+	PCMCIA_DEVICE_MANF_CARD(0x0149, 0x4530),
+	PCMCIA_DEVICE_MANF_CARD(0x0149, 0xc1ab),
+	PCMCIA_DEVICE_MANF_CARD(0x0186, 0x0110),
+	PCMCIA_DEVICE_MANF_CARD(0x01bf, 0x8041),
+	PCMCIA_DEVICE_MANF_CARD(0x0213, 0x2452),
+	PCMCIA_DEVICE_MANF_CARD(0x026f, 0x0300),
+	PCMCIA_DEVICE_MANF_CARD(0x026f, 0x0307),
+	PCMCIA_DEVICE_MANF_CARD(0x026f, 0x030a),
+	PCMCIA_DEVICE_MANF_CARD(0x0274, 0x1103),
+	PCMCIA_DEVICE_MANF_CARD(0x0274, 0x1121),
+	PCMCIA_DEVICE_PROD_ID12("2408LAN", "Ethernet", 0x352fff7f, 0x00b2e941),
+	PCMCIA_DEVICE_PROD_ID1234("Socket", "CF 10/100 Ethernet Card", "Revision B", "05/11/06", 0xb38bcc2e, 0x4de88352, 0xeaca6c8d, 0x7e57c22e),
+	PCMCIA_DEVICE_PROD_ID123("Cardwell", "PCMCIA", "ETHERNET", 0x9533672e, 0x281f1c5d, 0x3ff7175b),
+	PCMCIA_DEVICE_PROD_ID123("CNet  ", "CN30BC", "ETHERNET", 0x9fe55d3d, 0x85601198, 0x3ff7175b),
+	PCMCIA_DEVICE_PROD_ID123("Digital", "Ethernet", "Adapter", 0x9999ab35, 0x00b2e941, 0x4b0d829e),
+	PCMCIA_DEVICE_PROD_ID123("Edimax Technology Inc.", "PCMCIA", "Ethernet Card", 0x738a0019, 0x281f1c5d, 0x5e9d92c0),
+	PCMCIA_DEVICE_PROD_ID123("EFA   ", "EFA207", "ETHERNET", 0x3d294be4, 0xeb9aab6c, 0x3ff7175b),
+	PCMCIA_DEVICE_PROD_ID123("I-O DATA", "PCLA", "ETHERNET", 0x1d55d7ec, 0xe4c64d34, 0x3ff7175b),
+	PCMCIA_DEVICE_PROD_ID123("IO DATA", "PCLATE", "ETHERNET", 0x547e66dc, 0x6b260753, 0x3ff7175b),
+	PCMCIA_DEVICE_PROD_ID123("KingMax Technology Inc.", "EN10-T2", "PCMCIA Ethernet Card", 0x932b7189, 0x699e4436, 0x6f6652e0),
+	PCMCIA_DEVICE_PROD_ID123("PCMCIA", "PCMCIA-ETHERNET-CARD", "UE2216", 0x281f1c5d, 0xd4cd2f20, 0xb87add82),
+	PCMCIA_DEVICE_PROD_ID123("PCMCIA", "PCMCIA-ETHERNET-CARD", "UE2620", 0x281f1c5d, 0xd4cd2f20, 0x7d3d83a8),
+	PCMCIA_DEVICE_PROD_ID1("2412LAN", 0x67f236ab),
+	PCMCIA_DEVICE_PROD_ID12("ACCTON", "EN2212", 0xdfc6b5b2, 0xcb112a11),
+	PCMCIA_DEVICE_PROD_ID12("ACCTON", "EN2216-PCMCIA-ETHERNET", 0xdfc6b5b2, 0x5542bfff),
+	PCMCIA_DEVICE_PROD_ID12("Allied Telesis, K.K.", "CentreCOM LA100-PCM-T V2 100/10M LAN PC Card", 0xbb7fbdd7, 0xcd91cc68),
+	PCMCIA_DEVICE_PROD_ID12("Allied Telesis K.K.", "LA100-PCM V2", 0x36634a66, 0xc6d05997),
+  	PCMCIA_DEVICE_PROD_ID12("Allied Telesis, K.K.", "CentreCOM LA-PCM_V2", 0xbb7fBdd7, 0x28e299f8),
+	PCMCIA_DEVICE_PROD_ID12("Allied Telesis K.K.", "LA-PCM V3", 0x36634a66, 0x62241d96),
+	PCMCIA_DEVICE_PROD_ID12("AmbiCom", "AMB8010", 0x5070a7f9, 0x82f96e96),
+	PCMCIA_DEVICE_PROD_ID12("AmbiCom", "AMB8610", 0x5070a7f9, 0x86741224),
+	PCMCIA_DEVICE_PROD_ID12("AmbiCom Inc", "AMB8002", 0x93b15570, 0x75ec3efb),
+	PCMCIA_DEVICE_PROD_ID12("AmbiCom Inc", "AMB8002T", 0x93b15570, 0x461c5247),
+	PCMCIA_DEVICE_PROD_ID12("AmbiCom Inc", "AMB8010", 0x93b15570, 0x82f96e96),
+	PCMCIA_DEVICE_PROD_ID12("AnyCom", "ECO Ethernet", 0x578ba6e7, 0x0a9888c1),
+	PCMCIA_DEVICE_PROD_ID12("AnyCom", "ECO Ethernet 10/100", 0x578ba6e7, 0x939fedbd),
+	PCMCIA_DEVICE_PROD_ID12("AROWANA", "PCMCIA Ethernet LAN Card", 0x313adbc8, 0x08d9f190),
+	PCMCIA_DEVICE_PROD_ID12("ASANTE", "FriendlyNet PC Card", 0x3a7ade0f, 0x41c64504),
+	PCMCIA_DEVICE_PROD_ID12("Billionton", "LNT-10TB", 0x552ab682, 0xeeb1ba6a),
+	PCMCIA_DEVICE_PROD_ID12("CF", "10Base-Ethernet", 0x44ebf863, 0x93ae4d79),
+	PCMCIA_DEVICE_PROD_ID12("CNet", "CN40BC Ethernet", 0xbc477dde, 0xfba775a7),
+	PCMCIA_DEVICE_PROD_ID12("COMPU-SHACK", "BASEline PCMCIA 10 MBit Ethernetadapter", 0xfa2e424d, 0xe9190d8a),
+	PCMCIA_DEVICE_PROD_ID12("COMPU-SHACK", "FASTline PCMCIA 10/100 Fast-Ethernet", 0xfa2e424d, 0x3953d9b9),
+	PCMCIA_DEVICE_PROD_ID12("CONTEC", "C-NET(PC)C-10L", 0x21cab552, 0xf6f90722),
+	PCMCIA_DEVICE_PROD_ID12("corega", "FEther PCC-TXF", 0x0a21501a, 0xa51564a2),
+	PCMCIA_DEVICE_PROD_ID12("corega", "Ether CF-TD", 0x0a21501a, 0x6589340a),
+	PCMCIA_DEVICE_PROD_ID12("corega K.K.", "corega Ether CF-TD LAN Card", 0x5261440f, 0x8797663b),
+	PCMCIA_DEVICE_PROD_ID12("corega K.K.", "corega EtherII PCC-T", 0x5261440f, 0xfa9d85bd),
+	PCMCIA_DEVICE_PROD_ID12("corega K.K.", "corega EtherII PCC-TD", 0x5261440f, 0xc49bd73d),
+	PCMCIA_DEVICE_PROD_ID12("Corega K.K.", "corega EtherII PCC-TD", 0xd4fdcbd8, 0xc49bd73d),
+	PCMCIA_DEVICE_PROD_ID12("corega K.K.", "corega Ether PCC-T", 0x5261440f, 0x6705fcaa),
+	PCMCIA_DEVICE_PROD_ID12("corega K.K.", "corega Ether PCC-TD", 0x5261440f, 0x47d5ca83),
+	PCMCIA_DEVICE_PROD_ID12("corega K.K.", "corega FastEther PCC-TX", 0x5261440f, 0x485e85d9),
+	PCMCIA_DEVICE_PROD_ID12("Corega,K.K.", "Ethernet LAN Card", 0x110d26d9, 0x9fd2f0a2),
+	PCMCIA_DEVICE_PROD_ID12("corega,K.K.", "Ethernet LAN Card", 0x9791a90e, 0x9fd2f0a2),
+	PCMCIA_DEVICE_PROD_ID12("corega K.K.", "(CG-LAPCCTXD)", 0x5261440f, 0x73ec0d88),
+	PCMCIA_DEVICE_PROD_ID12("CouplerlessPCMCIA", "100BASE", 0xee5af0ad, 0x7c2add04),
+	PCMCIA_DEVICE_PROD_ID12("CyQ've", "ELA-010", 0x77008979, 0x9d8d445d),
+	PCMCIA_DEVICE_PROD_ID12("CyQ've", "ELA-110E 10/100M LAN Card", 0x77008979, 0xfd184814),
+	PCMCIA_DEVICE_PROD_ID12("DataTrek.", "NetCard ", 0x5cd66d9d, 0x84697ce0),
+	PCMCIA_DEVICE_PROD_ID12("Dayna Communications, Inc.", "CommuniCard E", 0x0c629325, 0xb4e7dbaf),
+	PCMCIA_DEVICE_PROD_ID12("Digicom", "Palladio LAN 10/100", 0x697403d8, 0xe160b995),
+	PCMCIA_DEVICE_PROD_ID12("Digicom", "Palladio LAN 10/100 Dongless", 0x697403d8, 0xa6d3b233),
+	PCMCIA_DEVICE_PROD_ID12("DIGITAL", "DEPCM-XX", 0x69616cb3, 0xe600e76e),
+	PCMCIA_DEVICE_PROD_ID12("D-Link", "DE-650", 0x1a424a1c, 0xf28c8398),
+	PCMCIA_DEVICE_PROD_ID12("D-Link", "DE-660", 0x1a424a1c, 0xd9a1d05b),
+	PCMCIA_DEVICE_PROD_ID12("D-Link", "DE-660+", 0x1a424a1c, 0x50dcd0ec),
+	PCMCIA_DEVICE_PROD_ID12("D-Link", "DFE-650", 0x1a424a1c, 0x0f0073f9),
+	PCMCIA_DEVICE_PROD_ID12("Dual Speed", "10/100 PC Card", 0x725b842d, 0xf1efee84),
+	PCMCIA_DEVICE_PROD_ID12("Dual Speed", "10/100 Port Attached PC Card", 0x725b842d, 0x2db1f8e9),
+	PCMCIA_DEVICE_PROD_ID12("Dynalink", "L10BC", 0x55632fd5, 0xdc65f2b1),
+	PCMCIA_DEVICE_PROD_ID12("DYNALINK", "L10BC", 0x6a26d1cf, 0xdc65f2b1),
+	PCMCIA_DEVICE_PROD_ID12("DYNALINK", "L10C", 0x6a26d1cf, 0xc4f84efb),
+	PCMCIA_DEVICE_PROD_ID12("E-CARD", "E-CARD", 0x6701da11, 0x6701da11),
+	PCMCIA_DEVICE_PROD_ID12("EIGER Labs Inc.", "Ethernet 10BaseT card", 0x53c864c6, 0xedd059f6),
+	PCMCIA_DEVICE_PROD_ID12("EIGER Labs Inc.", "Ethernet Combo card", 0x53c864c6, 0x929c486c),
+	PCMCIA_DEVICE_PROD_ID12("Ethernet", "Adapter", 0x00b2e941, 0x4b0d829e),
+	PCMCIA_DEVICE_PROD_ID12("Ethernet Adapter", "E2000 PCMCIA Ethernet", 0x96767301, 0x71fbbc61),
+	PCMCIA_DEVICE_PROD_ID12("Ethernet PCMCIA adapter", "EP-210", 0x8dd86181, 0xf2b52517),
+	PCMCIA_DEVICE_PROD_ID12("Fast Ethernet", "Adapter", 0xb4be14e3, 0x4b0d829e),
+	PCMCIA_DEVICE_PROD_ID12("Grey Cell", "GCS2000", 0x2a151fac, 0xf00555cb),
+	PCMCIA_DEVICE_PROD_ID12("Grey Cell", "GCS2220", 0x2a151fac, 0xc1b7e327),
+	PCMCIA_DEVICE_PROD_ID12("GVC", "NIC-2000p", 0x76e171bd, 0x6eb1c947),
+	PCMCIA_DEVICE_PROD_ID12("IBM Corp.", "Ethernet", 0xe3736c88, 0x00b2e941),
+	PCMCIA_DEVICE_PROD_ID12("IC-CARD", "IC-CARD", 0x60cb09a6, 0x60cb09a6),
+	PCMCIA_DEVICE_PROD_ID12("IC-CARD+", "IC-CARD+", 0x93693494, 0x93693494),
+	PCMCIA_DEVICE_PROD_ID12("IO DATA", "PCETTX", 0x547e66dc, 0x6fc5459b),
+	PCMCIA_DEVICE_PROD_ID12("iPort", "10/100 Ethernet Card", 0x56c538d2, 0x11b0ffc0),
+	PCMCIA_DEVICE_PROD_ID12("KANSAI ELECTRIC CO.,LTD", "KLA-PCM/T", 0xb18dc3b4, 0xcc51a956),
+	PCMCIA_DEVICE_PROD_ID12("KENTRONICS", "KEP-230", 0xaf8144c9, 0x868f6616),
+	PCMCIA_DEVICE_PROD_ID12("KCI", "PE520 PCMCIA Ethernet Adapter", 0xa89b87d3, 0x1eb88e64),
+	PCMCIA_DEVICE_PROD_ID12("KINGMAX", "EN10T2T", 0x7bcb459a, 0xa5c81fa5),
+	PCMCIA_DEVICE_PROD_ID12("Kingston", "KNE-PC2", 0x1128e633, 0xce2a89b3),
+	PCMCIA_DEVICE_PROD_ID12("Kingston Technology Corp.", "EtheRx PC Card Ethernet Adapter", 0x313c7be3, 0x0afb54a2),
+	PCMCIA_DEVICE_PROD_ID12("Laneed", "LD-10/100CD", 0x1b7827b2, 0xcda71d1c),
+	PCMCIA_DEVICE_PROD_ID12("Laneed", "LD-CDF", 0x1b7827b2, 0xfec71e40),
+	PCMCIA_DEVICE_PROD_ID12("Laneed", "LD-CDL/T", 0x1b7827b2, 0x79fba4f7),
+	PCMCIA_DEVICE_PROD_ID12("Laneed", "LD-CDS", 0x1b7827b2, 0x931afaab),
+	PCMCIA_DEVICE_PROD_ID12("LEMEL", "LM-N89TX PRO", 0xbbefb52f, 0xd2897a97),
+	PCMCIA_DEVICE_PROD_ID12("Linksys", "Combo PCMCIA EthernetCard (EC2T)", 0x0733cc81, 0x32ee8c78),
+	PCMCIA_DEVICE_PROD_ID12("LINKSYS", "E-CARD", 0xf7cb0b07, 0x6701da11),
+	PCMCIA_DEVICE_PROD_ID12("Linksys", "EtherFast 10/100 Integrated PC Card (PCM100)", 0x0733cc81, 0x453c3f9d),
+	PCMCIA_DEVICE_PROD_ID12("Linksys", "EtherFast 10/100 PC Card (PCMPC100)", 0x0733cc81, 0x66c5a389),
+	PCMCIA_DEVICE_PROD_ID12("Linksys", "EtherFast 10/100 PC Card (PCMPC100 V2)", 0x0733cc81, 0x3a3b28e9),
+	PCMCIA_DEVICE_PROD_ID12("Linksys", "HomeLink Phoneline + 10/100 Network PC Card (PCM100H1)", 0x733cc81, 0x7a3e5c3a),
+	PCMCIA_DEVICE_PROD_ID12("Logitec", "LPM-LN100TX", 0x88fcdeda, 0x6d772737),
+	PCMCIA_DEVICE_PROD_ID12("Logitec", "LPM-LN100TE", 0x88fcdeda, 0x0e714bee),
+	PCMCIA_DEVICE_PROD_ID12("Logitec", "LPM-LN20T", 0x88fcdeda, 0x81090922),
+	PCMCIA_DEVICE_PROD_ID12("Logitec", "LPM-LN10TE", 0x88fcdeda, 0xc1e2521c),
+	PCMCIA_DEVICE_PROD_ID12("LONGSHINE", "PCMCIA Ethernet Card", 0xf866b0b0, 0x6f6652e0),
+	PCMCIA_DEVICE_PROD_ID12("MACNICA", "ME1-JEIDA", 0x20841b68, 0xaf8a3578),
+	PCMCIA_DEVICE_PROD_ID12("Macsense", "MPC-10", 0xd830297f, 0xd265c307),
+	PCMCIA_DEVICE_PROD_ID12("Matsushita Electric Industrial Co.,LTD.", "CF-VEL211", 0x44445376, 0x8ded41d4),
+	PCMCIA_DEVICE_PROD_ID12("MAXTECH", "PCN2000", 0x78d64bc0, 0xca0ca4b8),
+	PCMCIA_DEVICE_PROD_ID12("MELCO", "LPC2-T", 0x481e0094, 0xa2eb0cf3),
+	PCMCIA_DEVICE_PROD_ID12("MELCO", "LPC2-TX", 0x481e0094, 0x41a6916c),
+	PCMCIA_DEVICE_PROD_ID12("Microcom C.E.", "Travel Card LAN 10/100", 0x4b91cec7, 0xe70220d6),
+	PCMCIA_DEVICE_PROD_ID12("Microdyne", "NE4200", 0x2e6da59b, 0x0478e472),
+	PCMCIA_DEVICE_PROD_ID12("MIDORI ELEC.", "LT-PCMT", 0x648d55c1, 0xbde526c7),
+	PCMCIA_DEVICE_PROD_ID12("National Semiconductor", "InfoMover 4100", 0x36e1191f, 0x60c229b9),
+	PCMCIA_DEVICE_PROD_ID12("National Semiconductor", "InfoMover NE4100", 0x36e1191f, 0xa6617ec8),
+	PCMCIA_DEVICE_PROD_ID12("NEC", "PC-9801N-J12", 0x18df0ba0, 0xbc912d76),
+	PCMCIA_DEVICE_PROD_ID12("NETGEAR", "FA410TX", 0x9aa79dc3, 0x60e5bc0e),
+	PCMCIA_DEVICE_PROD_ID12("Network Everywhere", "Fast Ethernet 10/100 PC Card", 0x820a67b6, 0x31ed1a5f),
+	PCMCIA_DEVICE_PROD_ID12("NextCom K.K.", "Next Hawk", 0xaedaec74, 0xad050ef1),
+	PCMCIA_DEVICE_PROD_ID12("PCMCIA", "10/100Mbps Ethernet Card", 0x281f1c5d, 0x6e41773b),
+	PCMCIA_DEVICE_PROD_ID12("PCMCIA", "Ethernet", 0x281f1c5d, 0x00b2e941),
+	PCMCIA_DEVICE_PROD_ID12("PCMCIA", "ETHERNET", 0x281f1c5d, 0x3ff7175b),
+	PCMCIA_DEVICE_PROD_ID12("PCMCIA", "Ethernet 10BaseT Card", 0x281f1c5d, 0x4de2f6c8),
+	PCMCIA_DEVICE_PROD_ID12("PCMCIA", "Ethernet Card", 0x281f1c5d, 0x5e9d92c0),
+	PCMCIA_DEVICE_PROD_ID12("PCMCIA", "Ethernet Combo card", 0x281f1c5d, 0x929c486c),
+	PCMCIA_DEVICE_PROD_ID12("PCMCIA", "ETHERNET V1.0", 0x281f1c5d, 0x4d8817c8),
+	PCMCIA_DEVICE_PROD_ID12("PCMCIA", "FastEthernet", 0x281f1c5d, 0xfe871eeb),
+	PCMCIA_DEVICE_PROD_ID12("PCMCIA", "Fast-Ethernet", 0x281f1c5d, 0x45f1f3b4),
+	PCMCIA_DEVICE_PROD_ID12("PCMCIA", "FAST ETHERNET CARD", 0x281f1c5d, 0xec5dbca7),
+	PCMCIA_DEVICE_PROD_ID12("PCMCIA LAN", "Ethernet", 0x7500e246, 0x00b2e941),
+	PCMCIA_DEVICE_PROD_ID12("PCMCIA", "LNT-10TN", 0x281f1c5d, 0xe707f641),
+	PCMCIA_DEVICE_PROD_ID12("PCMCIAs", "ComboCard", 0xdcfe12d3, 0xcd8906cc),
+	PCMCIA_DEVICE_PROD_ID12("PCMCIA", "UE2212", 0x281f1c5d, 0xbf17199b),
+	PCMCIA_DEVICE_PROD_ID12("PCMCIA", "    Ethernet NE2000 Compatible", 0x281f1c5d, 0x42d5d7e1),
+	PCMCIA_DEVICE_PROD_ID12("PRETEC", "Ethernet CompactLAN 10baseT 3.3V", 0xebf91155, 0x30074c80),
+	PCMCIA_DEVICE_PROD_ID12("PRETEC", "Ethernet CompactLAN 10BaseT 3.3V", 0xebf91155, 0x7f5a4f50),
+	PCMCIA_DEVICE_PROD_ID12("Psion Dacom", "Gold Card Ethernet", 0xf5f025c2, 0x3a30e110),
+	PCMCIA_DEVICE_PROD_ID12("=RELIA==", "Ethernet", 0xcdd0644a, 0x00b2e941),
+	PCMCIA_DEVICE_PROD_ID12("RIOS Systems Co.", "PC CARD3 ETHERNET", 0x7dd33481, 0x10b41826),
+	PCMCIA_DEVICE_PROD_ID12("RP", "1625B Ethernet NE2000 Compatible", 0xe3e66e22, 0xb96150df),
+	PCMCIA_DEVICE_PROD_ID12("RPTI", "EP400 Ethernet NE2000 Compatible", 0xdc6f88fd, 0x4a7e2ae0),
+	PCMCIA_DEVICE_PROD_ID12("RPTI", "EP401 Ethernet NE2000 Compatible", 0xdc6f88fd, 0x4bcbd7fd),
+	PCMCIA_DEVICE_PROD_ID12("RPTI LTD.", "EP400", 0xc53ac515, 0x81e39388),
+	PCMCIA_DEVICE_PROD_ID12("SCM", "Ethernet Combo card", 0xbdc3b102, 0x929c486c),
+	PCMCIA_DEVICE_PROD_ID12("Seiko Epson Corp.", "Ethernet", 0x09928730, 0x00b2e941),
+	PCMCIA_DEVICE_PROD_ID12("SMC", "EZCard-10-PCMCIA", 0xc4f8b18b, 0xfb21d265),
+	PCMCIA_DEVICE_PROD_ID12("Socket Communications Inc", "Socket EA PCMCIA LAN Adapter Revision D", 0xc70a4760, 0x2ade483e),
+	PCMCIA_DEVICE_PROD_ID12("Socket Communications Inc", "Socket EA PCMCIA LAN Adapter Revision E", 0xc70a4760, 0x5dd978a8),
+	PCMCIA_DEVICE_PROD_ID12("TDK", "LAK-CD031 for PCMCIA", 0x1eae9475, 0x0ed386fa),
+	PCMCIA_DEVICE_PROD_ID12("Telecom Device K.K.", "SuperSocket RE450T", 0x466b05f0, 0x8b74bc4f),
+	PCMCIA_DEVICE_PROD_ID12("Telecom Device K.K.", "SuperSocket RE550T", 0x466b05f0, 0x33c8db2a),
+	PCMCIA_DEVICE_PROD_ID13("Hypertec",  "EP401", 0x8787bec7, 0xf6e4a31e),
+	PCMCIA_DEVICE_PROD_ID13("KingMax Technology Inc.", "Ethernet Card", 0x932b7189, 0x5e9d92c0),
+	PCMCIA_DEVICE_PROD_ID13("LONGSHINE", "EP401", 0xf866b0b0, 0xf6e4a31e),
+	PCMCIA_DEVICE_PROD_ID13("Xircom", "CFE-10", 0x2e3ee845, 0x22a49f89),
+	PCMCIA_DEVICE_PROD_ID1("CyQ've 10 Base-T LAN CARD", 0x94faf360),
+	PCMCIA_DEVICE_PROD_ID1("EP-210 PCMCIA LAN CARD.", 0x8850b4de),
+	PCMCIA_DEVICE_PROD_ID1("ETHER-C16", 0x06a8514f),
+	PCMCIA_DEVICE_PROD_ID1("NE2000 Compatible", 0x75b8ad5a),
+	PCMCIA_DEVICE_PROD_ID2("EN-6200P2", 0xa996d078),
+	/* too generic! */
+	/* PCMCIA_DEVICE_PROD_ID12("PCMCIA", "10/100 Ethernet Card", 0x281f1c5d, 0x11b0ffc0), */
+	PCMCIA_PFC_DEVICE_CIS_PROD_ID12(0, "PCMCIA", "EN2218-LAN/MODEM", 0x281f1c5d, 0x570f348e, "cis/PCMLM28.cis"),
+	PCMCIA_PFC_DEVICE_CIS_PROD_ID12(0, "PCMCIA", "UE2218-LAN/MODEM", 0x281f1c5d, 0x6fdcacee, "cis/PCMLM28.cis"),
+	PCMCIA_PFC_DEVICE_CIS_PROD_ID12(0, "Psion Dacom", "Gold Card V34 Ethernet", 0xf5f025c2, 0x338e8155, "cis/PCMLM28.cis"),
+	PCMCIA_PFC_DEVICE_CIS_PROD_ID12(0, "Psion Dacom", "Gold Card V34 Ethernet GSM", 0xf5f025c2, 0x4ae85d35, "cis/PCMLM28.cis"),
+	PCMCIA_PFC_DEVICE_CIS_PROD_ID12(0, "LINKSYS", "PCMLM28", 0xf7cb0b07, 0x66881874, "cis/PCMLM28.cis"),
+	PCMCIA_PFC_DEVICE_CIS_PROD_ID12(0, "TOSHIBA", "Modem/LAN Card", 0xb4585a1a, 0x53f922f8, "cis/PCMLM28.cis"),
+	PCMCIA_MFC_DEVICE_CIS_PROD_ID12(0, "DAYNA COMMUNICATIONS", "LAN AND MODEM MULTIFUNCTION", 0x8fdf8f89, 0xdd5ed9e8, "cis/DP83903.cis"),
+	PCMCIA_MFC_DEVICE_CIS_PROD_ID4(0, "NSC MF LAN/Modem", 0x58fc6056, "cis/DP83903.cis"),
+	PCMCIA_MFC_DEVICE_CIS_MANF_CARD(0, 0x0175, 0x0000, "cis/DP83903.cis"),
+	PCMCIA_DEVICE_CIS_PROD_ID12("Allied Telesis,K.K", "Ethernet LAN Card", 0x2ad62f3c, 0x9fd2f0a2, "cis/LA-PCM.cis"),
+	PCMCIA_DEVICE_CIS_PROD_ID12("KTI", "PE520 PLUS", 0xad180345, 0x9d58d392, "cis/PE520.cis"),
+	PCMCIA_DEVICE_CIS_PROD_ID12("NDC", "Ethernet", 0x01c43ae1, 0x00b2e941, "cis/NE2K.cis"),
+	PCMCIA_DEVICE_CIS_PROD_ID12("PMX   ", "PE-200", 0x34f3f1c8, 0x10b59f8c, "cis/PE-200.cis"),
+	PCMCIA_DEVICE_CIS_PROD_ID12("TAMARACK", "Ethernet", 0xcf434fba, 0x00b2e941, "cis/tamarack.cis"),
+	PCMCIA_DEVICE_PROD_ID12("Ethernet", "CF Size PC Card", 0x00b2e941, 0x43ac239b),
+	PCMCIA_DEVICE_PROD_ID123("Fast Ethernet", "CF Size PC Card", "1.0",
+		0xb4be14e3, 0x43ac239b, 0x0877b627),
+	PCMCIA_DEVICE_NULL
+};
+MODULE_DEVICE_TABLE(pcmcia, pcnet_ids);
+MODULE_FIRMWARE("cis/PCMLM28.cis");
+MODULE_FIRMWARE("cis/DP83903.cis");
+MODULE_FIRMWARE("cis/LA-PCM.cis");
+MODULE_FIRMWARE("cis/PE520.cis");
+MODULE_FIRMWARE("cis/NE2K.cis");
+MODULE_FIRMWARE("cis/PE-200.cis");
+MODULE_FIRMWARE("cis/tamarack.cis");
+
+static struct pcmcia_driver pcnet_driver = {
+	.name		= "pcnet_cs",
+	.probe		= pcnet_probe,
+	.remove		= pcnet_detach,
+	.owner		= THIS_MODULE,
+	.id_table	= pcnet_ids,
+	.suspend	= pcnet_suspend,
+	.resume		= pcnet_resume,
+};
+
+static int __init init_pcnet_cs(void)
+{
+    return pcmcia_register_driver(&pcnet_driver);
+}
+
+static void __exit exit_pcnet_cs(void)
+{
+    pcmcia_unregister_driver(&pcnet_driver);
+}
+
+module_init(init_pcnet_cs);
+module_exit(exit_pcnet_cs);
diff --git a/drivers/net/ethernet/8390/smc-mca.c b/drivers/net/ethernet/8390/smc-mca.c
new file mode 100644
index 0000000..34934fb
--- /dev/null
+++ b/drivers/net/ethernet/8390/smc-mca.c
@@ -0,0 +1,576 @@
+/* smc-mca.c: A SMC Ultra ethernet driver for linux. */
+/*
+    Most of this driver, except for ultramca_probe is nearly
+    verbatim from smc-ultra.c by Donald Becker. The rest is
+    written and copyright 1996 by David Weis, weisd3458@uni.edu
+
+    This is a driver for the SMC Ultra and SMC EtherEZ ethercards.
+
+    This driver uses the cards in the 8390-compatible, shared memory mode.
+    Most of the run-time complexity is handled by the generic code in
+    8390.c.
+
+    This driver enables the shared memory only when doing the actual data
+    transfers to avoid a bug in early version of the card that corrupted
+    data transferred by a AHA1542.
+
+    This driver does not support the programmed-I/O data transfer mode of
+    the EtherEZ.  That support (if available) is smc-ez.c.  Nor does it
+    use the non-8390-compatible "Altego" mode. (No support currently planned.)
+
+    Changelog:
+
+    Paul Gortmaker	 : multiple card support for module users.
+    David Weis		 : Micro Channel-ized it.
+    Tom Sightler	 : Added support for IBM PS/2 Ethernet Adapter/A
+    Christopher Turcksin : Changed MCA-probe so that multiple adapters are
+			   found correctly (Jul 16, 1997)
+    Chris Beauregard	 : Tried to merge the two changes above (Dec 15, 1997)
+    Tom Sightler	 : Fixed minor detection bug caused by above merge
+    Tom Sightler	 : Added support for three more Western Digital
+			   MCA-adapters
+    Tom Sightler	 : Added support for 2.2.x mca_find_unused_adapter
+    Hartmut Schmidt	 : - Modified parameter detection to handle each
+			     card differently depending on a switch-list
+			   - 'card_ver' removed from the adapter list
+			   - Some minor bug fixes
+*/
+
+#include <linux/mca.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+
+#include <asm/io.h>
+#include <asm/system.h>
+
+#include "8390.h"
+
+#define DRV_NAME "smc-mca"
+
+static int ultramca_open(struct net_device *dev);
+static void ultramca_reset_8390(struct net_device *dev);
+static void ultramca_get_8390_hdr(struct net_device *dev,
+                                  struct e8390_pkt_hdr *hdr,
+                                  int ring_page);
+static void ultramca_block_input(struct net_device *dev, int count,
+                                 struct sk_buff *skb,
+                                 int ring_offset);
+static void ultramca_block_output(struct net_device *dev, int count,
+                                  const unsigned char *buf,
+                                  const int start_page);
+static int ultramca_close_card(struct net_device *dev);
+
+#define START_PG        0x00    /* First page of TX buffer */
+
+#define ULTRA_CMDREG 0      /* Offset to ASIC command register. */
+#define ULTRA_RESET  0x80   /* Board reset, in ULTRA_CMDREG. */
+#define ULTRA_MEMENB 0x40   /* Enable the shared memory. */
+#define ULTRA_NIC_OFFSET 16 /* NIC register offset from the base_addr. */
+#define ULTRA_IO_EXTENT 32
+#define EN0_ERWCNT      0x08  /* Early receive warning count. */
+
+#define _61c8_SMC_Ethercard_PLUS_Elite_A_BNC_AUI_WD8013EP_A            0
+#define _61c9_SMC_Ethercard_PLUS_Elite_A_UTP_AUI_WD8013EP_A            1
+#define _6fc0_WD_Ethercard_PLUS_A_WD8003E_A_OR_WD8003ET_A              2
+#define _6fc1_WD_Starcard_PLUS_A_WD8003ST_A                            3
+#define _6fc2_WD_Ethercard_PLUS_10T_A_WD8003W_A                        4
+#define _efd4_IBM_PS2_Adapter_A_for_Ethernet_UTP_AUI_WD8013WP_A        5
+#define _efd5_IBM_PS2_Adapter_A_for_Ethernet_BNC_AUI_WD8013WP_A        6
+#define _efe5_IBM_PS2_Adapter_A_for_Ethernet                           7
+
+struct smc_mca_adapters_t {
+	unsigned int id;
+	char *name;
+};
+
+#define MAX_ULTRAMCA_CARDS 4	/* Max number of Ultra cards per module */
+
+static int ultra_io[MAX_ULTRAMCA_CARDS];
+static int ultra_irq[MAX_ULTRAMCA_CARDS];
+MODULE_LICENSE("GPL");
+
+module_param_array(ultra_io, int, NULL, 0);
+module_param_array(ultra_irq, int, NULL, 0);
+MODULE_PARM_DESC(ultra_io, "SMC Ultra/EtherEZ MCA I/O base address(es)");
+MODULE_PARM_DESC(ultra_irq, "SMC Ultra/EtherEZ MCA IRQ number(s)");
+
+static const struct {
+  unsigned int base_addr;
+} addr_table[] = {
+    { 0x0800 },
+    { 0x1800 },
+    { 0x2800 },
+    { 0x3800 },
+    { 0x4800 },
+    { 0x5800 },
+    { 0x6800 },
+    { 0x7800 },
+    { 0x8800 },
+    { 0x9800 },
+    { 0xa800 },
+    { 0xb800 },
+    { 0xc800 },
+    { 0xd800 },
+    { 0xe800 },
+    { 0xf800 }
+};
+
+#define MEM_MASK 64
+
+static const struct {
+  unsigned char mem_index;
+  unsigned long mem_start;
+  unsigned char num_pages;
+} mem_table[] = {
+    { 16, 0x0c0000, 40 },
+    { 18, 0x0c4000, 40 },
+    { 20, 0x0c8000, 40 },
+    { 22, 0x0cc000, 40 },
+    { 24, 0x0d0000, 40 },
+    { 26, 0x0d4000, 40 },
+    { 28, 0x0d8000, 40 },
+    { 30, 0x0dc000, 40 },
+    {144, 0xfc0000, 40 },
+    {148, 0xfc8000, 40 },
+    {154, 0xfd0000, 40 },
+    {156, 0xfd8000, 40 },
+    {  0, 0x0c0000, 20 },
+    {  1, 0x0c2000, 20 },
+    {  2, 0x0c4000, 20 },
+    {  3, 0x0c6000, 20 }
+};
+
+#define IRQ_MASK 243
+static const struct {
+   unsigned char new_irq;
+   unsigned char old_irq;
+} irq_table[] = {
+   {  3,  3 },
+   {  4,  4 },
+   { 10, 10 },
+   { 14, 15 }
+};
+
+static short smc_mca_adapter_ids[] __initdata = {
+	0x61c8,
+	0x61c9,
+	0x6fc0,
+	0x6fc1,
+	0x6fc2,
+	0xefd4,
+	0xefd5,
+	0xefe5,
+	0x0000
+};
+
+static char *smc_mca_adapter_names[] __initdata = {
+	"SMC Ethercard PLUS Elite/A BNC/AUI (WD8013EP/A)",
+	"SMC Ethercard PLUS Elite/A UTP/AUI (WD8013WP/A)",
+	"WD Ethercard PLUS/A (WD8003E/A or WD8003ET/A)",
+	"WD Starcard PLUS/A (WD8003ST/A)",
+	"WD Ethercard PLUS 10T/A (WD8003W/A)",
+	"IBM PS/2 Adapter/A for Ethernet UTP/AUI (WD8013WP/A)",
+	"IBM PS/2 Adapter/A for Ethernet BNC/AUI (WD8013EP/A)",
+	"IBM PS/2 Adapter/A for Ethernet",
+	NULL
+};
+
+static int ultra_found = 0;
+
+
+static const struct net_device_ops ultramca_netdev_ops = {
+	.ndo_open		= ultramca_open,
+	.ndo_stop		= ultramca_close_card,
+
+	.ndo_start_xmit		= ei_start_xmit,
+	.ndo_tx_timeout		= ei_tx_timeout,
+	.ndo_get_stats		= ei_get_stats,
+	.ndo_set_multicast_list = ei_set_multicast_list,
+	.ndo_validate_addr	= eth_validate_addr,
+	.ndo_set_mac_address 	= eth_mac_addr,
+	.ndo_change_mtu		= eth_change_mtu,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	.ndo_poll_controller 	= ei_poll,
+#endif
+};
+
+static int __init ultramca_probe(struct device *gen_dev)
+{
+	unsigned short ioaddr;
+	struct net_device *dev;
+	unsigned char reg4, num_pages;
+	struct mca_device *mca_dev = to_mca_device(gen_dev);
+	char slot = mca_dev->slot;
+	unsigned char pos2 = 0xff, pos3 = 0xff, pos4 = 0xff, pos5 = 0xff;
+	int i, rc;
+	int adapter = mca_dev->index;
+	int tbase = 0;
+	int tirq = 0;
+	int base_addr = ultra_io[ultra_found];
+	int irq = ultra_irq[ultra_found];
+
+	if (base_addr || irq) {
+		printk(KERN_INFO "Probing for SMC MCA adapter");
+		if (base_addr) {
+			printk(KERN_INFO " at I/O address 0x%04x%c",
+			       base_addr, irq ? ' ' : '\n');
+		}
+		if (irq) {
+			printk(KERN_INFO "using irq %d\n", irq);
+		}
+	}
+
+	tirq = 0;
+	tbase = 0;
+
+	/* If we're trying to match a specificied irq or io address,
+	 * we'll reject the adapter found unless it's the one we're
+	 * looking for */
+
+	pos2 = mca_device_read_stored_pos(mca_dev, 2); /* io_addr */
+	pos3 = mca_device_read_stored_pos(mca_dev, 3); /* shared mem */
+	pos4 = mca_device_read_stored_pos(mca_dev, 4); /* ROM bios addr range */
+	pos5 = mca_device_read_stored_pos(mca_dev, 5); /* irq, media and RIPL */
+
+	/* Test the following conditions:
+	 * - If an irq parameter is supplied, compare it
+	 *   with the irq of the adapter we found
+	 * - If a base_addr paramater is given, compare it
+	 *   with the base_addr of the adapter we found
+	 * - Check that the irq and the base_addr of the
+	 *   adapter we found is not already in use by
+	 *   this driver
+	 */
+
+	switch (mca_dev->index) {
+	case _61c8_SMC_Ethercard_PLUS_Elite_A_BNC_AUI_WD8013EP_A:
+	case _61c9_SMC_Ethercard_PLUS_Elite_A_UTP_AUI_WD8013EP_A:
+	case _efd4_IBM_PS2_Adapter_A_for_Ethernet_UTP_AUI_WD8013WP_A:
+	case _efd5_IBM_PS2_Adapter_A_for_Ethernet_BNC_AUI_WD8013WP_A:
+		{
+			tbase = addr_table[(pos2 & 0xf0) >> 4].base_addr;
+			tirq  = irq_table[(pos5 & 0xc) >> 2].new_irq;
+			break;
+		}
+	case _6fc0_WD_Ethercard_PLUS_A_WD8003E_A_OR_WD8003ET_A:
+	case _6fc1_WD_Starcard_PLUS_A_WD8003ST_A:
+	case _6fc2_WD_Ethercard_PLUS_10T_A_WD8003W_A:
+	case _efe5_IBM_PS2_Adapter_A_for_Ethernet:
+		{
+			tbase = ((pos2 & 0x0fe) * 0x10);
+			tirq  = irq_table[(pos5 & 3)].old_irq;
+			break;
+		}
+	}
+
+	if(!tirq || !tbase ||
+	   (irq && irq != tirq) ||
+	   (base_addr && tbase != base_addr))
+		/* FIXME: we're trying to force the ordering of the
+		 * devices here, there should be a way of getting this
+		 * to happen */
+		return -ENXIO;
+
+        /* Adapter found. */
+	dev  = alloc_ei_netdev();
+	if(!dev)
+		return -ENODEV;
+
+	SET_NETDEV_DEV(dev, gen_dev);
+	mca_device_set_name(mca_dev, smc_mca_adapter_names[adapter]);
+	mca_device_set_claim(mca_dev, 1);
+
+	printk(KERN_INFO "smc_mca: %s found in slot %d\n",
+		       smc_mca_adapter_names[adapter], slot + 1);
+
+	ultra_found++;
+
+	dev->base_addr = ioaddr = mca_device_transform_ioport(mca_dev, tbase);
+	dev->irq       = mca_device_transform_irq(mca_dev, tirq);
+	dev->mem_start = 0;
+	num_pages      = 40;
+
+	switch (adapter) {	/* card-# in const array above [hs] */
+		case _61c8_SMC_Ethercard_PLUS_Elite_A_BNC_AUI_WD8013EP_A:
+		case _61c9_SMC_Ethercard_PLUS_Elite_A_UTP_AUI_WD8013EP_A:
+		{
+			for (i = 0; i < 16; i++) { /* taking 16 counts
+						    * up to 15 [hs] */
+				if (mem_table[i].mem_index == (pos3 & ~MEM_MASK)) {
+					dev->mem_start = (unsigned long)
+					  mca_device_transform_memory(mca_dev, (void *)mem_table[i].mem_start);
+					num_pages = mem_table[i].num_pages;
+				}
+			}
+			break;
+		}
+		case _6fc0_WD_Ethercard_PLUS_A_WD8003E_A_OR_WD8003ET_A:
+		case _6fc1_WD_Starcard_PLUS_A_WD8003ST_A:
+		case _6fc2_WD_Ethercard_PLUS_10T_A_WD8003W_A:
+		case _efe5_IBM_PS2_Adapter_A_for_Ethernet:
+		{
+			dev->mem_start = (unsigned long)
+			  mca_device_transform_memory(mca_dev, (void *)((pos3 & 0xfc) * 0x1000));
+			num_pages = 0x40;
+			break;
+		}
+		case _efd4_IBM_PS2_Adapter_A_for_Ethernet_UTP_AUI_WD8013WP_A:
+		case _efd5_IBM_PS2_Adapter_A_for_Ethernet_BNC_AUI_WD8013WP_A:
+		{
+			/* courtesy of gamera@quartz.ocn.ne.jp, pos3 indicates
+			 * the index of the 0x2000 step.
+			 * beware different number of pages [hs]
+			 */
+			dev->mem_start = (unsigned long)
+			  mca_device_transform_memory(mca_dev, (void *)(0xc0000 + (0x2000 * (pos3 & 0xf))));
+			num_pages = 0x20 + (2 * (pos3 & 0x10));
+			break;
+		}
+	}
+
+	/* sanity check, shouldn't happen */
+	if (dev->mem_start == 0) {
+		rc = -ENODEV;
+		goto err_unclaim;
+	}
+
+	if (!request_region(ioaddr, ULTRA_IO_EXTENT, DRV_NAME)) {
+		rc = -ENODEV;
+		goto err_unclaim;
+	}
+
+	reg4 = inb(ioaddr + 4) & 0x7f;
+	outb(reg4, ioaddr + 4);
+
+	for (i = 0; i < 6; i++)
+		dev->dev_addr[i] = inb(ioaddr + 8 + i);
+
+	printk(KERN_INFO "smc_mca[%d]: Parameters: %#3x, %pM",
+	       slot + 1, ioaddr, dev->dev_addr);
+
+	/* Switch from the station address to the alternate register set
+	 * and read the useful registers there.
+	 */
+
+	outb(0x80 | reg4, ioaddr + 4);
+
+	/* Enable FINE16 mode to avoid BIOS ROM width mismatches @ reboot.
+	 */
+
+	outb(0x80 | inb(ioaddr + 0x0c), ioaddr + 0x0c);
+
+	/* Switch back to the station address register set so that
+	 * the MS-DOS driver can find the card after a warm boot.
+	 */
+
+	outb(reg4, ioaddr + 4);
+
+	dev_set_drvdata(gen_dev, dev);
+
+	/* The 8390 isn't at the base address, so fake the offset
+	 */
+
+	dev->base_addr = ioaddr + ULTRA_NIC_OFFSET;
+
+	ei_status.name = "SMC Ultra MCA";
+	ei_status.word16 = 1;
+	ei_status.tx_start_page = START_PG;
+	ei_status.rx_start_page = START_PG + TX_PAGES;
+	ei_status.stop_page = num_pages;
+
+	ei_status.mem = ioremap(dev->mem_start, (ei_status.stop_page - START_PG) * 256);
+	if (!ei_status.mem) {
+		rc = -ENOMEM;
+		goto err_release_region;
+	}
+
+	dev->mem_end = dev->mem_start + (ei_status.stop_page - START_PG) * 256;
+
+	printk(", IRQ %d memory %#lx-%#lx.\n",
+	dev->irq, dev->mem_start, dev->mem_end - 1);
+
+	ei_status.reset_8390 = &ultramca_reset_8390;
+	ei_status.block_input = &ultramca_block_input;
+	ei_status.block_output = &ultramca_block_output;
+	ei_status.get_8390_hdr = &ultramca_get_8390_hdr;
+
+	ei_status.priv = slot;
+
+	dev->netdev_ops = &ultramca_netdev_ops;
+
+	NS8390_init(dev, 0);
+
+	rc = register_netdev(dev);
+	if (rc)
+		goto err_unmap;
+
+	return 0;
+
+err_unmap:
+	iounmap(ei_status.mem);
+err_release_region:
+	release_region(ioaddr, ULTRA_IO_EXTENT);
+err_unclaim:
+	mca_device_set_claim(mca_dev, 0);
+	free_netdev(dev);
+	return rc;
+}
+
+static int ultramca_open(struct net_device *dev)
+{
+	int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */
+	int retval;
+
+	if ((retval = request_irq(dev->irq, ei_interrupt, 0, dev->name, dev)))
+		return retval;
+
+	outb(ULTRA_MEMENB, ioaddr); /* Enable memory */
+	outb(0x80, ioaddr + 5);     /* ??? */
+	outb(0x01, ioaddr + 6);     /* Enable interrupts and memory. */
+	outb(0x04, ioaddr + 5);     /* ??? */
+
+	/* Set the early receive warning level in window 0 high enough not
+	 * to receive ERW interrupts.
+	 */
+
+	/* outb_p(E8390_NODMA + E8390_PAGE0, dev->base_addr);
+	 * outb(0xff, dev->base_addr + EN0_ERWCNT);
+	 */
+
+	ei_open(dev);
+	return 0;
+}
+
+static void ultramca_reset_8390(struct net_device *dev)
+{
+	int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */
+
+	outb(ULTRA_RESET, ioaddr);
+	if (ei_debug > 1)
+		printk("resetting Ultra, t=%ld...", jiffies);
+	ei_status.txing = 0;
+
+	outb(0x80, ioaddr + 5);     /* ??? */
+	outb(0x01, ioaddr + 6);     /* Enable interrupts and memory. */
+
+	if (ei_debug > 1)
+		printk("reset done\n");
+}
+
+/* Grab the 8390 specific header. Similar to the block_input routine, but
+ * we don't need to be concerned with ring wrap as the header will be at
+ * the start of a page, so we optimize accordingly.
+ */
+
+static void ultramca_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
+{
+	void __iomem *hdr_start = ei_status.mem + ((ring_page - START_PG) << 8);
+
+#ifdef notdef
+	/* Officially this is what we are doing, but the readl() is faster */
+	memcpy_fromio(hdr, hdr_start, sizeof(struct e8390_pkt_hdr));
+#else
+	((unsigned int*)hdr)[0] = readl(hdr_start);
+#endif
+}
+
+/* Block input and output are easy on shared memory ethercards, the only
+ * complication is when the ring buffer wraps.
+ */
+
+static void ultramca_block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset)
+{
+	void __iomem *xfer_start = ei_status.mem + ring_offset - START_PG * 256;
+
+	if (ring_offset + count > ei_status.stop_page * 256) {
+		/* We must wrap the input move. */
+		int semi_count = ei_status.stop_page * 256 - ring_offset;
+		memcpy_fromio(skb->data, xfer_start, semi_count);
+		count -= semi_count;
+		memcpy_fromio(skb->data + semi_count, ei_status.mem + TX_PAGES * 256, count);
+	} else {
+		memcpy_fromio(skb->data, xfer_start, count);
+	}
+
+}
+
+static void ultramca_block_output(struct net_device *dev, int count, const unsigned char *buf,
+                int start_page)
+{
+	void __iomem *shmem = ei_status.mem + ((start_page - START_PG) << 8);
+
+	memcpy_toio(shmem, buf, count);
+}
+
+static int ultramca_close_card(struct net_device *dev)
+{
+	int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */
+
+	netif_stop_queue(dev);
+
+	if (ei_debug > 1)
+		printk("%s: Shutting down ethercard.\n", dev->name);
+
+	outb(0x00, ioaddr + 6);     /* Disable interrupts. */
+	free_irq(dev->irq, dev);
+
+	NS8390_init(dev, 0);
+	/* We should someday disable shared memory and change to 8-bit mode
+         * "just in case"...
+	 */
+
+	return 0;
+}
+
+static int ultramca_remove(struct device *gen_dev)
+{
+	struct mca_device *mca_dev = to_mca_device(gen_dev);
+	struct net_device *dev = dev_get_drvdata(gen_dev);
+
+	if (dev) {
+		/* NB: ultra_close_card() does free_irq */
+		int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET;
+
+		unregister_netdev(dev);
+		mca_device_set_claim(mca_dev, 0);
+		release_region(ioaddr, ULTRA_IO_EXTENT);
+		iounmap(ei_status.mem);
+		free_netdev(dev);
+	}
+	return 0;
+}
+
+
+static struct mca_driver ultra_driver = {
+	.id_table = smc_mca_adapter_ids,
+	.driver = {
+		.name = "smc-mca",
+		.bus = &mca_bus_type,
+		.probe = ultramca_probe,
+		.remove = ultramca_remove,
+	}
+};
+
+static int __init ultramca_init_module(void)
+{
+	if(!MCA_bus)
+		return -ENXIO;
+
+	mca_register_driver(&ultra_driver);
+
+	return ultra_found ? 0 : -ENXIO;
+}
+
+static void __exit ultramca_cleanup_module(void)
+{
+	mca_unregister_driver(&ultra_driver);
+}
+module_init(ultramca_init_module);
+module_exit(ultramca_cleanup_module);
+
diff --git a/drivers/net/ethernet/8390/smc-ultra.c b/drivers/net/ethernet/8390/smc-ultra.c
new file mode 100644
index 0000000..ba44ede
--- /dev/null
+++ b/drivers/net/ethernet/8390/smc-ultra.c
@@ -0,0 +1,622 @@
+/* smc-ultra.c: A SMC Ultra ethernet driver for linux. */
+/*
+	This is a driver for the SMC Ultra and SMC EtherEZ ISA ethercards.
+
+	Written 1993-1998 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
+
+	This driver uses the cards in the 8390-compatible mode.
+	Most of the run-time complexity is handled by the generic code in
+	8390.c.  The code in this file is responsible for
+
+		ultra_probe()	 	Detecting and initializing the card.
+		ultra_probe1()
+		ultra_probe_isapnp()
+
+		ultra_open()		The card-specific details of starting, stopping
+		ultra_reset_8390()	and resetting the 8390 NIC core.
+		ultra_close()
+
+		ultra_block_input()		Routines for reading and writing blocks of
+		ultra_block_output()	packet buffer memory.
+		ultra_pio_input()
+		ultra_pio_output()
+
+	This driver enables the shared memory only when doing the actual data
+	transfers to avoid a bug in early version of the card that corrupted
+	data transferred by a AHA1542.
+
+	This driver now supports the programmed-I/O (PIO) data transfer mode of
+	the EtherEZ. It does not use the non-8390-compatible "Altego" mode.
+	That support (if available) is in smc-ez.c.
+
+	Changelog:
+
+	Paul Gortmaker	: multiple card support for module users.
+	Donald Becker	: 4/17/96 PIO support, minor potential problems avoided.
+	Donald Becker	: 6/6/96 correctly set auto-wrap bit.
+	Alexander Sotirov : 1/20/01 Added support for ISAPnP cards
+
+	Note about the ISA PnP support:
+
+	This driver can not autoprobe for more than one SMC EtherEZ PnP card.
+	You have to configure the second card manually through the /proc/isapnp
+	interface and then load the module with an explicit io=0x___ option.
+*/
+
+static const char version[] =
+	"smc-ultra.c:v2.02 2/3/98 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/isapnp.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+
+#include "8390.h"
+
+#define DRV_NAME "smc-ultra"
+
+/* A zero-terminated list of I/O addresses to be probed. */
+static unsigned int ultra_portlist[] __initdata =
+{0x200, 0x220, 0x240, 0x280, 0x300, 0x340, 0x380, 0};
+
+static int ultra_probe1(struct net_device *dev, int ioaddr);
+
+#ifdef __ISAPNP__
+static int ultra_probe_isapnp(struct net_device *dev);
+#endif
+
+static int ultra_open(struct net_device *dev);
+static void ultra_reset_8390(struct net_device *dev);
+static void ultra_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
+						int ring_page);
+static void ultra_block_input(struct net_device *dev, int count,
+						  struct sk_buff *skb, int ring_offset);
+static void ultra_block_output(struct net_device *dev, int count,
+							const unsigned char *buf, const int start_page);
+static void ultra_pio_get_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
+						int ring_page);
+static void ultra_pio_input(struct net_device *dev, int count,
+						  struct sk_buff *skb, int ring_offset);
+static void ultra_pio_output(struct net_device *dev, int count,
+							 const unsigned char *buf, const int start_page);
+static int ultra_close_card(struct net_device *dev);
+
+#ifdef __ISAPNP__
+static struct isapnp_device_id ultra_device_ids[] __initdata = {
+        {       ISAPNP_VENDOR('S','M','C'), ISAPNP_FUNCTION(0x8416),
+                ISAPNP_VENDOR('S','M','C'), ISAPNP_FUNCTION(0x8416),
+                (long) "SMC EtherEZ (8416)" },
+        { }	/* terminate list */
+};
+
+MODULE_DEVICE_TABLE(isapnp, ultra_device_ids);
+#endif
+
+
+#define START_PG		0x00	/* First page of TX buffer */
+
+#define ULTRA_CMDREG	0		/* Offset to ASIC command register. */
+#define	 ULTRA_RESET	0x80	/* Board reset, in ULTRA_CMDREG. */
+#define	 ULTRA_MEMENB	0x40	/* Enable the shared memory. */
+#define IOPD	0x02			/* I/O Pipe Data (16 bits), PIO operation. */
+#define IOPA	0x07			/* I/O Pipe Address for PIO operation. */
+#define ULTRA_NIC_OFFSET  16	/* NIC register offset from the base_addr. */
+#define ULTRA_IO_EXTENT 32
+#define EN0_ERWCNT		0x08	/* Early receive warning count. */
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void ultra_poll(struct net_device *dev)
+{
+	disable_irq(dev->irq);
+	ei_interrupt(dev->irq, dev);
+	enable_irq(dev->irq);
+}
+#endif
+/*	Probe for the Ultra.  This looks like a 8013 with the station
+	address PROM at I/O ports <base>+8 to <base>+13, with a checksum
+	following.
+*/
+
+static int __init do_ultra_probe(struct net_device *dev)
+{
+	int i;
+	int base_addr = dev->base_addr;
+	int irq = dev->irq;
+
+	if (base_addr > 0x1ff)		/* Check a single specified location. */
+		return ultra_probe1(dev, base_addr);
+	else if (base_addr != 0)	/* Don't probe at all. */
+		return -ENXIO;
+
+#ifdef __ISAPNP__
+	/* Look for any installed ISAPnP cards */
+	if (isapnp_present() && (ultra_probe_isapnp(dev) == 0))
+		return 0;
+#endif
+
+	for (i = 0; ultra_portlist[i]; i++) {
+		dev->irq = irq;
+		if (ultra_probe1(dev, ultra_portlist[i]) == 0)
+			return 0;
+	}
+
+	return -ENODEV;
+}
+
+#ifndef MODULE
+struct net_device * __init ultra_probe(int unit)
+{
+	struct net_device *dev = alloc_ei_netdev();
+	int err;
+
+	if (!dev)
+		return ERR_PTR(-ENOMEM);
+
+	sprintf(dev->name, "eth%d", unit);
+	netdev_boot_setup_check(dev);
+
+	err = do_ultra_probe(dev);
+	if (err)
+		goto out;
+	return dev;
+out:
+	free_netdev(dev);
+	return ERR_PTR(err);
+}
+#endif
+
+static const struct net_device_ops ultra_netdev_ops = {
+	.ndo_open		= ultra_open,
+	.ndo_stop		= ultra_close_card,
+
+	.ndo_start_xmit		= ei_start_xmit,
+	.ndo_tx_timeout		= ei_tx_timeout,
+	.ndo_get_stats		= ei_get_stats,
+	.ndo_set_multicast_list = ei_set_multicast_list,
+	.ndo_validate_addr	= eth_validate_addr,
+	.ndo_set_mac_address 	= eth_mac_addr,
+	.ndo_change_mtu		= eth_change_mtu,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	.ndo_poll_controller 	= ultra_poll,
+#endif
+};
+
+static int __init ultra_probe1(struct net_device *dev, int ioaddr)
+{
+	int i, retval;
+	int checksum = 0;
+	const char *model_name;
+	unsigned char eeprom_irq = 0;
+	static unsigned version_printed;
+	/* Values from various config regs. */
+	unsigned char num_pages, irqreg, addr, piomode;
+	unsigned char idreg = inb(ioaddr + 7);
+	unsigned char reg4 = inb(ioaddr + 4) & 0x7f;
+
+	if (!request_region(ioaddr, ULTRA_IO_EXTENT, DRV_NAME))
+		return -EBUSY;
+
+	/* Check the ID nibble. */
+	if ((idreg & 0xF0) != 0x20 			/* SMC Ultra */
+		&& (idreg & 0xF0) != 0x40) {		/* SMC EtherEZ */
+		retval = -ENODEV;
+		goto out;
+	}
+
+	/* Select the station address register set. */
+	outb(reg4, ioaddr + 4);
+
+	for (i = 0; i < 8; i++)
+		checksum += inb(ioaddr + 8 + i);
+	if ((checksum & 0xff) != 0xFF) {
+		retval = -ENODEV;
+		goto out;
+	}
+
+	if (ei_debug  &&  version_printed++ == 0)
+		printk(version);
+
+	model_name = (idreg & 0xF0) == 0x20 ? "SMC Ultra" : "SMC EtherEZ";
+
+	for (i = 0; i < 6; i++)
+		dev->dev_addr[i] = inb(ioaddr + 8 + i);
+
+	printk("%s: %s at %#3x, %pM", dev->name, model_name,
+	       ioaddr, dev->dev_addr);
+
+	/* Switch from the station address to the alternate register set and
+	   read the useful registers there. */
+	outb(0x80 | reg4, ioaddr + 4);
+
+	/* Enabled FINE16 mode to avoid BIOS ROM width mismatches @ reboot. */
+	outb(0x80 | inb(ioaddr + 0x0c), ioaddr + 0x0c);
+	piomode = inb(ioaddr + 0x8);
+	addr = inb(ioaddr + 0xb);
+	irqreg = inb(ioaddr + 0xd);
+
+	/* Switch back to the station address register set so that the MS-DOS driver
+	   can find the card after a warm boot. */
+	outb(reg4, ioaddr + 4);
+
+	if (dev->irq < 2) {
+		unsigned char irqmap[] = {0, 9, 3, 5, 7, 10, 11, 15};
+		int irq;
+
+		/* The IRQ bits are split. */
+		irq = irqmap[((irqreg & 0x40) >> 4) + ((irqreg & 0x0c) >> 2)];
+
+		if (irq == 0) {
+			printk(", failed to detect IRQ line.\n");
+			retval =  -EAGAIN;
+			goto out;
+		}
+		dev->irq = irq;
+		eeprom_irq = 1;
+	}
+
+	/* The 8390 isn't at the base address, so fake the offset */
+	dev->base_addr = ioaddr+ULTRA_NIC_OFFSET;
+
+	{
+		static const int addr_tbl[4] = {
+			0x0C0000, 0x0E0000, 0xFC0000, 0xFE0000
+		};
+		static const short num_pages_tbl[4] = {
+			0x20, 0x40, 0x80, 0xff
+		};
+
+		dev->mem_start = ((addr & 0x0f) << 13) + addr_tbl[(addr >> 6) & 3] ;
+		num_pages = num_pages_tbl[(addr >> 4) & 3];
+	}
+
+	ei_status.name = model_name;
+	ei_status.word16 = 1;
+	ei_status.tx_start_page = START_PG;
+	ei_status.rx_start_page = START_PG + TX_PAGES;
+	ei_status.stop_page = num_pages;
+
+	ei_status.mem = ioremap(dev->mem_start, (ei_status.stop_page - START_PG)*256);
+	if (!ei_status.mem) {
+		printk(", failed to ioremap.\n");
+		retval =  -ENOMEM;
+		goto out;
+	}
+
+	dev->mem_end = dev->mem_start + (ei_status.stop_page - START_PG)*256;
+
+	if (piomode) {
+		printk(",%s IRQ %d programmed-I/O mode.\n",
+			   eeprom_irq ? "EEPROM" : "assigned ", dev->irq);
+		ei_status.block_input = &ultra_pio_input;
+		ei_status.block_output = &ultra_pio_output;
+		ei_status.get_8390_hdr = &ultra_pio_get_hdr;
+	} else {
+		printk(",%s IRQ %d memory %#lx-%#lx.\n", eeprom_irq ? "" : "assigned ",
+			   dev->irq, dev->mem_start, dev->mem_end-1);
+		ei_status.block_input = &ultra_block_input;
+		ei_status.block_output = &ultra_block_output;
+		ei_status.get_8390_hdr = &ultra_get_8390_hdr;
+	}
+	ei_status.reset_8390 = &ultra_reset_8390;
+
+	dev->netdev_ops = &ultra_netdev_ops;
+	NS8390_init(dev, 0);
+
+	retval = register_netdev(dev);
+	if (retval)
+		goto out;
+	return 0;
+out:
+	release_region(ioaddr, ULTRA_IO_EXTENT);
+	return retval;
+}
+
+#ifdef __ISAPNP__
+static int __init ultra_probe_isapnp(struct net_device *dev)
+{
+        int i;
+
+        for (i = 0; ultra_device_ids[i].vendor != 0; i++) {
+		struct pnp_dev *idev = NULL;
+
+                while ((idev = pnp_find_dev(NULL,
+                                            ultra_device_ids[i].vendor,
+                                            ultra_device_ids[i].function,
+                                            idev))) {
+                        /* Avoid already found cards from previous calls */
+                        if (pnp_device_attach(idev) < 0)
+                        	continue;
+                        if (pnp_activate_dev(idev) < 0) {
+                              __again:
+                        	pnp_device_detach(idev);
+                        	continue;
+                        }
+			/* if no io and irq, search for next */
+			if (!pnp_port_valid(idev, 0) || !pnp_irq_valid(idev, 0))
+				goto __again;
+                        /* found it */
+			dev->base_addr = pnp_port_start(idev, 0);
+			dev->irq = pnp_irq(idev, 0);
+                        printk(KERN_INFO "smc-ultra.c: ISAPnP reports %s at i/o %#lx, irq %d.\n",
+                                (char *) ultra_device_ids[i].driver_data,
+                                dev->base_addr, dev->irq);
+                        if (ultra_probe1(dev, dev->base_addr) != 0) {      /* Shouldn't happen. */
+                                printk(KERN_ERR "smc-ultra.c: Probe of ISAPnP card at %#lx failed.\n", dev->base_addr);
+                                pnp_device_detach(idev);
+				return -ENXIO;
+                        }
+                        ei_status.priv = (unsigned long)idev;
+                        break;
+                }
+                if (!idev)
+                        continue;
+                return 0;
+        }
+
+        return -ENODEV;
+}
+#endif
+
+static int
+ultra_open(struct net_device *dev)
+{
+	int retval;
+	int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */
+	unsigned char irq2reg[] = {0, 0, 0x04, 0x08, 0, 0x0C, 0, 0x40,
+				   0, 0x04, 0x44, 0x48, 0, 0, 0, 0x4C, };
+
+	retval = request_irq(dev->irq, ei_interrupt, 0, dev->name, dev);
+	if (retval)
+		return retval;
+
+	outb(0x00, ioaddr);	/* Disable shared memory for safety. */
+	outb(0x80, ioaddr + 5);
+	/* Set the IRQ line. */
+	outb(inb(ioaddr + 4) | 0x80, ioaddr + 4);
+	outb((inb(ioaddr + 13) & ~0x4C) | irq2reg[dev->irq], ioaddr + 13);
+	outb(inb(ioaddr + 4) & 0x7f, ioaddr + 4);
+
+	if (ei_status.block_input == &ultra_pio_input) {
+		outb(0x11, ioaddr + 6);		/* Enable interrupts and PIO. */
+		outb(0x01, ioaddr + 0x19);  	/* Enable ring read auto-wrap. */
+	} else
+		outb(0x01, ioaddr + 6);		/* Enable interrupts and memory. */
+	/* Set the early receive warning level in window 0 high enough not
+	   to receive ERW interrupts. */
+	outb_p(E8390_NODMA+E8390_PAGE0, dev->base_addr);
+	outb(0xff, dev->base_addr + EN0_ERWCNT);
+	ei_open(dev);
+	return 0;
+}
+
+static void
+ultra_reset_8390(struct net_device *dev)
+{
+	int cmd_port = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC base addr */
+
+	outb(ULTRA_RESET, cmd_port);
+	if (ei_debug > 1) printk("resetting Ultra, t=%ld...", jiffies);
+	ei_status.txing = 0;
+
+	outb(0x00, cmd_port);	/* Disable shared memory for safety. */
+	outb(0x80, cmd_port + 5);
+	if (ei_status.block_input == &ultra_pio_input)
+		outb(0x11, cmd_port + 6);		/* Enable interrupts and PIO. */
+	else
+		outb(0x01, cmd_port + 6);		/* Enable interrupts and memory. */
+
+	if (ei_debug > 1) printk("reset done\n");
+}
+
+/* Grab the 8390 specific header. Similar to the block_input routine, but
+   we don't need to be concerned with ring wrap as the header will be at
+   the start of a page, so we optimize accordingly. */
+
+static void
+ultra_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
+{
+	void __iomem *hdr_start = ei_status.mem + ((ring_page - START_PG)<<8);
+
+	outb(ULTRA_MEMENB, dev->base_addr - ULTRA_NIC_OFFSET);	/* shmem on */
+#ifdef __BIG_ENDIAN
+	/* Officially this is what we are doing, but the readl() is faster */
+	/* unfortunately it isn't endian aware of the struct               */
+	memcpy_fromio(hdr, hdr_start, sizeof(struct e8390_pkt_hdr));
+	hdr->count = le16_to_cpu(hdr->count);
+#else
+	((unsigned int*)hdr)[0] = readl(hdr_start);
+#endif
+	outb(0x00, dev->base_addr - ULTRA_NIC_OFFSET); /* shmem off */
+}
+
+/* Block input and output are easy on shared memory ethercards, the only
+   complication is when the ring buffer wraps. */
+
+static void
+ultra_block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset)
+{
+	void __iomem *xfer_start = ei_status.mem + ring_offset - (START_PG<<8);
+
+	/* Enable shared memory. */
+	outb(ULTRA_MEMENB, dev->base_addr - ULTRA_NIC_OFFSET);
+
+	if (ring_offset + count > ei_status.stop_page*256) {
+		/* We must wrap the input move. */
+		int semi_count = ei_status.stop_page*256 - ring_offset;
+		memcpy_fromio(skb->data, xfer_start, semi_count);
+		count -= semi_count;
+		memcpy_fromio(skb->data + semi_count, ei_status.mem + TX_PAGES * 256, count);
+	} else {
+		memcpy_fromio(skb->data, xfer_start, count);
+	}
+
+	outb(0x00, dev->base_addr - ULTRA_NIC_OFFSET);	/* Disable memory. */
+}
+
+static void
+ultra_block_output(struct net_device *dev, int count, const unsigned char *buf,
+				int start_page)
+{
+	void __iomem *shmem = ei_status.mem + ((start_page - START_PG)<<8);
+
+	/* Enable shared memory. */
+	outb(ULTRA_MEMENB, dev->base_addr - ULTRA_NIC_OFFSET);
+
+	memcpy_toio(shmem, buf, count);
+
+	outb(0x00, dev->base_addr - ULTRA_NIC_OFFSET); /* Disable memory. */
+}
+
+/* The identical operations for programmed I/O cards.
+   The PIO model is trivial to use: the 16 bit start address is written
+   byte-sequentially to IOPA, with no intervening I/O operations, and the
+   data is read or written to the IOPD data port.
+   The only potential complication is that the address register is shared
+   and must be always be rewritten between each read/write direction change.
+   This is no problem for us, as the 8390 code ensures that we are single
+   threaded. */
+static void ultra_pio_get_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
+						int ring_page)
+{
+	int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */
+	outb(0x00, ioaddr + IOPA);	/* Set the address, LSB first. */
+	outb(ring_page, ioaddr + IOPA);
+	insw(ioaddr + IOPD, hdr, sizeof(struct e8390_pkt_hdr)>>1);
+}
+
+static void ultra_pio_input(struct net_device *dev, int count,
+						  struct sk_buff *skb, int ring_offset)
+{
+	int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */
+    char *buf = skb->data;
+
+	/* For now set the address again, although it should already be correct. */
+	outb(ring_offset, ioaddr + IOPA);	/* Set the address, LSB first. */
+	outb(ring_offset >> 8, ioaddr + IOPA);
+	/* We know skbuffs are padded to at least word alignment. */
+	insw(ioaddr + IOPD, buf, (count+1)>>1);
+}
+
+static void ultra_pio_output(struct net_device *dev, int count,
+							const unsigned char *buf, const int start_page)
+{
+	int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */
+	outb(0x00, ioaddr + IOPA);	/* Set the address, LSB first. */
+	outb(start_page, ioaddr + IOPA);
+	/* An extra odd byte is OK here as well. */
+	outsw(ioaddr + IOPD, buf, (count+1)>>1);
+}
+
+static int
+ultra_close_card(struct net_device *dev)
+{
+	int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* CMDREG */
+
+	netif_stop_queue(dev);
+
+	if (ei_debug > 1)
+		printk("%s: Shutting down ethercard.\n", dev->name);
+
+	outb(0x00, ioaddr + 6);		/* Disable interrupts. */
+	free_irq(dev->irq, dev);
+
+	NS8390_init(dev, 0);
+
+	/* We should someday disable shared memory and change to 8-bit mode
+	   "just in case"... */
+
+	return 0;
+}
+
+
+#ifdef MODULE
+#define MAX_ULTRA_CARDS	4	/* Max number of Ultra cards per module */
+static struct net_device *dev_ultra[MAX_ULTRA_CARDS];
+static int io[MAX_ULTRA_CARDS];
+static int irq[MAX_ULTRA_CARDS];
+
+module_param_array(io, int, NULL, 0);
+module_param_array(irq, int, NULL, 0);
+MODULE_PARM_DESC(io, "I/O base address(es)");
+MODULE_PARM_DESC(irq, "IRQ number(s) (assigned)");
+MODULE_DESCRIPTION("SMC Ultra/EtherEZ ISA/PnP Ethernet driver");
+MODULE_LICENSE("GPL");
+
+/* This is set up so that only a single autoprobe takes place per call.
+ISA device autoprobes on a running machine are not recommended. */
+int __init
+init_module(void)
+{
+	struct net_device *dev;
+	int this_dev, found = 0;
+
+	for (this_dev = 0; this_dev < MAX_ULTRA_CARDS; this_dev++) {
+		if (io[this_dev] == 0)  {
+			if (this_dev != 0) break; /* only autoprobe 1st one */
+			printk(KERN_NOTICE "smc-ultra.c: Presently autoprobing (not recommended) for a single card.\n");
+		}
+		dev = alloc_ei_netdev();
+		if (!dev)
+			break;
+		dev->irq = irq[this_dev];
+		dev->base_addr = io[this_dev];
+		if (do_ultra_probe(dev) == 0) {
+			dev_ultra[found++] = dev;
+			continue;
+		}
+		free_netdev(dev);
+		printk(KERN_WARNING "smc-ultra.c: No SMC Ultra card found (i/o = 0x%x).\n", io[this_dev]);
+		break;
+	}
+	if (found)
+		return 0;
+	return -ENXIO;
+}
+
+static void cleanup_card(struct net_device *dev)
+{
+	/* NB: ultra_close_card() does free_irq */
+#ifdef __ISAPNP__
+	struct pnp_dev *idev = (struct pnp_dev *)ei_status.priv;
+	if (idev)
+		pnp_device_detach(idev);
+#endif
+	release_region(dev->base_addr - ULTRA_NIC_OFFSET, ULTRA_IO_EXTENT);
+	iounmap(ei_status.mem);
+}
+
+void __exit
+cleanup_module(void)
+{
+	int this_dev;
+
+	for (this_dev = 0; this_dev < MAX_ULTRA_CARDS; this_dev++) {
+		struct net_device *dev = dev_ultra[this_dev];
+		if (dev) {
+			unregister_netdev(dev);
+			cleanup_card(dev);
+			free_netdev(dev);
+		}
+	}
+}
+#endif /* MODULE */
diff --git a/drivers/net/ethernet/8390/smc-ultra32.c b/drivers/net/ethernet/8390/smc-ultra32.c
new file mode 100644
index 0000000..e459c3b
--- /dev/null
+++ b/drivers/net/ethernet/8390/smc-ultra32.c
@@ -0,0 +1,464 @@
+/* 	smc-ultra32.c: An SMC Ultra32 EISA ethernet driver for linux.
+
+Sources:
+
+	This driver is based on (cloned from) the ISA SMC Ultra driver
+	written by Donald Becker. Modifications to support the EISA
+	version of the card by Paul Gortmaker and Leonard N. Zubkoff.
+
+	This software may be used and distributed according to the terms
+	of the GNU General Public License, incorporated herein by reference.
+
+Theory of Operation:
+
+	The SMC Ultra32C card uses the SMC 83c790 chip which is also
+	found on the ISA SMC Ultra cards. It has a shared memory mode of
+	operation that makes it similar to the ISA version of the card.
+	The main difference is that the EISA card has 32KB of RAM, but
+	only an 8KB window into that memory. The EISA card also can be
+	set for a bus-mastering mode of operation via the ECU, but that
+	is not (and probably will never be) supported by this driver.
+	The ECU should be run to enable shared memory and to disable the
+	bus-mastering feature for use with linux.
+
+	By programming the 8390 to use only 8KB RAM, the modifications
+	to the ISA driver can be limited to the probe and initialization
+	code. This allows easy integration of EISA support into the ISA
+	driver. However, the driver development kit from SMC provided the
+	register information for sliding the 8KB window, and hence the 8390
+	is programmed to use the full 32KB RAM.
+
+	Unfortunately this required code changes outside the probe/init
+	routines, and thus we decided to separate the EISA driver from
+	the ISA one. In this way, ISA users don't end up with a larger
+	driver due to the EISA code, and EISA users don't end up with a
+	larger driver due to the ISA EtherEZ PIO code. The driver is
+	similar to the 3c503/16 driver, in that the window must be set
+	back to the 1st 8KB of space for access to the two 8390 Tx slots.
+
+	In testing, using only 8KB RAM (3 Tx / 5 Rx) didn't appear to
+	be a limiting factor, since the EISA bus could get packets off
+	the card fast enough, but having the use of lots of RAM as Rx
+	space is extra insurance if interrupt latencies become excessive.
+
+*/
+
+static const char *version = "smc-ultra32.c: 06/97 v1.00\n";
+
+
+#include <linux/module.h>
+#include <linux/eisa.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+
+#include <asm/io.h>
+#include <asm/system.h>
+
+#include "8390.h"
+
+#define DRV_NAME "smc-ultra32"
+
+static int ultra32_probe1(struct net_device *dev, int ioaddr);
+static int ultra32_open(struct net_device *dev);
+static void ultra32_reset_8390(struct net_device *dev);
+static void ultra32_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
+				 int ring_page);
+static void ultra32_block_input(struct net_device *dev, int count,
+				struct sk_buff *skb, int ring_offset);
+static void ultra32_block_output(struct net_device *dev, int count,
+				 const unsigned char *buf,
+				 const int start_page);
+static int ultra32_close(struct net_device *dev);
+
+#define ULTRA32_CMDREG	0	/* Offset to ASIC command register. */
+#define	 ULTRA32_RESET	0x80	/* Board reset, in ULTRA32_CMDREG. */
+#define	 ULTRA32_MEMENB	0x40	/* Enable the shared memory. */
+#define ULTRA32_NIC_OFFSET 16	/* NIC register offset from the base_addr. */
+#define ULTRA32_IO_EXTENT 32
+#define EN0_ERWCNT		0x08	/* Early receive warning count. */
+
+/*
+ * Defines that apply only to the Ultra32 EISA card. Note that
+ * "smc" = 10011 01101 00011 = 0x4da3, and hence !smc8010.cfg translates
+ * into an EISA ID of 0x1080A34D
+ */
+#define ULTRA32_BASE	0xca0
+#define ULTRA32_ID	0x1080a34d
+#define ULTRA32_IDPORT	(-0x20)	/* 0xc80 */
+/* Config regs 1->7 from the EISA !SMC8010.CFG file. */
+#define ULTRA32_CFG1	0x04	/* 0xca4 */
+#define ULTRA32_CFG2	0x05	/* 0xca5 */
+#define ULTRA32_CFG3	(-0x18)	/* 0xc88 */
+#define ULTRA32_CFG4	(-0x17)	/* 0xc89 */
+#define ULTRA32_CFG5	(-0x16)	/* 0xc8a */
+#define ULTRA32_CFG6	(-0x15)	/* 0xc8b */
+#define ULTRA32_CFG7	0x0d	/* 0xcad */
+
+static void cleanup_card(struct net_device *dev)
+{
+	int ioaddr = dev->base_addr - ULTRA32_NIC_OFFSET;
+	/* NB: ultra32_close_card() does free_irq */
+	release_region(ioaddr, ULTRA32_IO_EXTENT);
+	iounmap(ei_status.mem);
+}
+
+/*	Probe for the Ultra32.  This looks like a 8013 with the station
+	address PROM at I/O ports <base>+8 to <base>+13, with a checksum
+	following.
+*/
+
+struct net_device * __init ultra32_probe(int unit)
+{
+	struct net_device *dev;
+	int base;
+	int irq;
+	int err = -ENODEV;
+
+	if (!EISA_bus)
+		return ERR_PTR(-ENODEV);
+
+	dev = alloc_ei_netdev();
+
+	if (!dev)
+		return ERR_PTR(-ENOMEM);
+
+	if (unit >= 0) {
+		sprintf(dev->name, "eth%d", unit);
+		netdev_boot_setup_check(dev);
+	}
+
+	irq = dev->irq;
+
+	/* EISA spec allows for up to 16 slots, but 8 is typical. */
+	for (base = 0x1000 + ULTRA32_BASE; base < 0x9000; base += 0x1000) {
+		if (ultra32_probe1(dev, base) == 0)
+			break;
+		dev->irq = irq;
+	}
+	if (base >= 0x9000)
+		goto out;
+	err = register_netdev(dev);
+	if (err)
+		goto out1;
+	return dev;
+out1:
+	cleanup_card(dev);
+out:
+	free_netdev(dev);
+	return ERR_PTR(err);
+}
+
+
+static const struct net_device_ops ultra32_netdev_ops = {
+	.ndo_open 		= ultra32_open,
+	.ndo_stop 		= ultra32_close,
+	.ndo_start_xmit		= ei_start_xmit,
+	.ndo_tx_timeout		= ei_tx_timeout,
+	.ndo_get_stats		= ei_get_stats,
+	.ndo_set_multicast_list = ei_set_multicast_list,
+	.ndo_validate_addr	= eth_validate_addr,
+	.ndo_set_mac_address 	= eth_mac_addr,
+	.ndo_change_mtu		= eth_change_mtu,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	.ndo_poll_controller	= ei_poll,
+#endif
+};
+
+static int __init ultra32_probe1(struct net_device *dev, int ioaddr)
+{
+	int i, edge, media, retval;
+	int checksum = 0;
+	const char *model_name;
+	static unsigned version_printed;
+	/* Values from various config regs. */
+	unsigned char idreg;
+	unsigned char reg4;
+	const char *ifmap[] = {"UTP No Link", "", "UTP/AUI", "UTP/BNC"};
+
+	if (!request_region(ioaddr, ULTRA32_IO_EXTENT, DRV_NAME))
+		return -EBUSY;
+
+	if (inb(ioaddr + ULTRA32_IDPORT) == 0xff ||
+	    inl(ioaddr + ULTRA32_IDPORT) != ULTRA32_ID) {
+		retval = -ENODEV;
+		goto out;
+	}
+
+	media = inb(ioaddr + ULTRA32_CFG7) & 0x03;
+	edge = inb(ioaddr + ULTRA32_CFG5) & 0x08;
+	printk("SMC Ultra32 in EISA Slot %d, Media: %s, %s IRQs.\n",
+		ioaddr >> 12, ifmap[media],
+		(edge ? "Edge Triggered" : "Level Sensitive"));
+
+	idreg = inb(ioaddr + 7);
+	reg4 = inb(ioaddr + 4) & 0x7f;
+
+	/* Check the ID nibble. */
+	if ((idreg & 0xf0) != 0x20) {			/* SMC Ultra */
+		retval = -ENODEV;
+		goto out;
+	}
+
+	/* Select the station address register set. */
+	outb(reg4, ioaddr + 4);
+
+	for (i = 0; i < 8; i++)
+		checksum += inb(ioaddr + 8 + i);
+	if ((checksum & 0xff) != 0xff) {
+		retval = -ENODEV;
+		goto out;
+	}
+
+	if (ei_debug  &&  version_printed++ == 0)
+		printk(version);
+
+	model_name = "SMC Ultra32";
+
+	for (i = 0; i < 6; i++)
+		dev->dev_addr[i] = inb(ioaddr + 8 + i);
+
+	printk("%s: %s at 0x%X, %pM",
+	       dev->name, model_name, ioaddr, dev->dev_addr);
+
+	/* Switch from the station address to the alternate register set and
+	   read the useful registers there. */
+	outb(0x80 | reg4, ioaddr + 4);
+
+	/* Enable FINE16 mode to avoid BIOS ROM width mismatches @ reboot. */
+	outb(0x80 | inb(ioaddr + 0x0c), ioaddr + 0x0c);
+
+	/* Reset RAM addr. */
+	outb(0x00, ioaddr + 0x0b);
+
+	/* Switch back to the station address register set so that the
+	   MS-DOS driver can find the card after a warm boot. */
+	outb(reg4, ioaddr + 4);
+
+	if ((inb(ioaddr + ULTRA32_CFG5) & 0x40) == 0) {
+		printk("\nsmc-ultra32: Card RAM is disabled!  "
+		       "Run EISA config utility.\n");
+		retval = -ENODEV;
+		goto out;
+	}
+	if ((inb(ioaddr + ULTRA32_CFG2) & 0x04) == 0)
+		printk("\nsmc-ultra32: Ignoring Bus-Master enable bit.  "
+		       "Run EISA config utility.\n");
+
+	if (dev->irq < 2) {
+		unsigned char irqmap[] = {0, 9, 3, 5, 7, 10, 11, 15};
+		int irq = irqmap[inb(ioaddr + ULTRA32_CFG5) & 0x07];
+		if (irq == 0) {
+			printk(", failed to detect IRQ line.\n");
+			retval = -EAGAIN;
+			goto out;
+		}
+		dev->irq = irq;
+	}
+
+	/* The 8390 isn't at the base address, so fake the offset */
+	dev->base_addr = ioaddr + ULTRA32_NIC_OFFSET;
+
+	/* Save RAM address in the unused reg0 to avoid excess inb's. */
+	ei_status.reg0 = inb(ioaddr + ULTRA32_CFG3) & 0xfc;
+
+	dev->mem_start =  0xc0000 + ((ei_status.reg0 & 0x7c) << 11);
+
+	ei_status.name = model_name;
+	ei_status.word16 = 1;
+	ei_status.tx_start_page = 0;
+	ei_status.rx_start_page = TX_PAGES;
+	/* All Ultra32 cards have 32KB memory with an 8KB window. */
+	ei_status.stop_page = 128;
+
+	ei_status.mem = ioremap(dev->mem_start, 0x2000);
+	if (!ei_status.mem) {
+		printk(", failed to ioremap.\n");
+		retval = -ENOMEM;
+		goto out;
+	}
+	dev->mem_end = dev->mem_start + 0x1fff;
+
+	printk(", IRQ %d, 32KB memory, 8KB window at 0x%lx-0x%lx.\n",
+	       dev->irq, dev->mem_start, dev->mem_end);
+	ei_status.block_input = &ultra32_block_input;
+	ei_status.block_output = &ultra32_block_output;
+	ei_status.get_8390_hdr = &ultra32_get_8390_hdr;
+	ei_status.reset_8390 = &ultra32_reset_8390;
+
+	dev->netdev_ops = &ultra32_netdev_ops;
+	NS8390_init(dev, 0);
+
+	return 0;
+out:
+	release_region(ioaddr, ULTRA32_IO_EXTENT);
+	return retval;
+}
+
+static int ultra32_open(struct net_device *dev)
+{
+	int ioaddr = dev->base_addr - ULTRA32_NIC_OFFSET; /* ASIC addr */
+	int irq_flags = (inb(ioaddr + ULTRA32_CFG5) & 0x08) ? 0 : IRQF_SHARED;
+	int retval;
+
+	retval = request_irq(dev->irq, ei_interrupt, irq_flags, dev->name, dev);
+	if (retval)
+		return retval;
+
+	outb(ULTRA32_MEMENB, ioaddr); /* Enable Shared Memory. */
+	outb(0x80, ioaddr + ULTRA32_CFG6); /* Enable Interrupts. */
+	outb(0x84, ioaddr + 5);	/* Enable MEM16 & Disable Bus Master. */
+	outb(0x01, ioaddr + 6);	/* Enable Interrupts. */
+	/* Set the early receive warning level in window 0 high enough not
+	   to receive ERW interrupts. */
+	outb_p(E8390_NODMA+E8390_PAGE0, dev->base_addr);
+	outb(0xff, dev->base_addr + EN0_ERWCNT);
+	ei_open(dev);
+	return 0;
+}
+
+static int ultra32_close(struct net_device *dev)
+{
+	int ioaddr = dev->base_addr - ULTRA32_NIC_OFFSET; /* CMDREG */
+
+	netif_stop_queue(dev);
+
+	if (ei_debug > 1)
+		printk("%s: Shutting down ethercard.\n", dev->name);
+
+	outb(0x00, ioaddr + ULTRA32_CFG6); /* Disable Interrupts. */
+	outb(0x00, ioaddr + 6);		/* Disable interrupts. */
+	free_irq(dev->irq, dev);
+
+	NS8390_init(dev, 0);
+
+	return 0;
+}
+
+static void ultra32_reset_8390(struct net_device *dev)
+{
+	int ioaddr = dev->base_addr - ULTRA32_NIC_OFFSET; /* ASIC base addr */
+
+	outb(ULTRA32_RESET, ioaddr);
+	if (ei_debug > 1) printk("resetting Ultra32, t=%ld...", jiffies);
+	ei_status.txing = 0;
+
+	outb(ULTRA32_MEMENB, ioaddr); /* Enable Shared Memory. */
+	outb(0x80, ioaddr + ULTRA32_CFG6); /* Enable Interrupts. */
+	outb(0x84, ioaddr + 5);	/* Enable MEM16 & Disable Bus Master. */
+	outb(0x01, ioaddr + 6);	/* Enable Interrupts. */
+	if (ei_debug > 1) printk("reset done\n");
+}
+
+/* Grab the 8390 specific header. Similar to the block_input routine, but
+   we don't need to be concerned with ring wrap as the header will be at
+   the start of a page, so we optimize accordingly. */
+
+static void ultra32_get_8390_hdr(struct net_device *dev,
+				 struct e8390_pkt_hdr *hdr,
+				 int ring_page)
+{
+	void __iomem *hdr_start = ei_status.mem + ((ring_page & 0x1f) << 8);
+	unsigned int RamReg = dev->base_addr - ULTRA32_NIC_OFFSET + ULTRA32_CFG3;
+
+	/* Select correct 8KB Window. */
+	outb(ei_status.reg0 | ((ring_page & 0x60) >> 5), RamReg);
+
+#ifdef __BIG_ENDIAN
+	/* Officially this is what we are doing, but the readl() is faster */
+	/* unfortunately it isn't endian aware of the struct               */
+	memcpy_fromio(hdr, hdr_start, sizeof(struct e8390_pkt_hdr));
+	hdr->count = le16_to_cpu(hdr->count);
+#else
+	((unsigned int*)hdr)[0] = readl(hdr_start);
+#endif
+}
+
+/* Block input and output are easy on shared memory ethercards, the only
+   complication is when the ring buffer wraps, or in this case, when a
+   packet spans an 8KB boundary. Note that the current 8KB segment is
+   already set by the get_8390_hdr routine. */
+
+static void ultra32_block_input(struct net_device *dev,
+				int count,
+				struct sk_buff *skb,
+				int ring_offset)
+{
+	void __iomem *xfer_start = ei_status.mem + (ring_offset & 0x1fff);
+	unsigned int RamReg = dev->base_addr - ULTRA32_NIC_OFFSET + ULTRA32_CFG3;
+
+	if ((ring_offset & ~0x1fff) != ((ring_offset + count - 1) & ~0x1fff)) {
+		int semi_count = 8192 - (ring_offset & 0x1FFF);
+		memcpy_fromio(skb->data, xfer_start, semi_count);
+		count -= semi_count;
+		if (ring_offset < 96*256) {
+			/* Select next 8KB Window. */
+			ring_offset += semi_count;
+			outb(ei_status.reg0 | ((ring_offset & 0x6000) >> 13), RamReg);
+			memcpy_fromio(skb->data + semi_count, ei_status.mem, count);
+		} else {
+			/* Select first 8KB Window. */
+			outb(ei_status.reg0, RamReg);
+			memcpy_fromio(skb->data + semi_count, ei_status.mem + TX_PAGES * 256, count);
+		}
+	} else {
+		memcpy_fromio(skb->data, xfer_start, count);
+	}
+}
+
+static void ultra32_block_output(struct net_device *dev,
+				 int count,
+				 const unsigned char *buf,
+				 int start_page)
+{
+	void __iomem *xfer_start = ei_status.mem + (start_page<<8);
+	unsigned int RamReg = dev->base_addr - ULTRA32_NIC_OFFSET + ULTRA32_CFG3;
+
+	/* Select first 8KB Window. */
+	outb(ei_status.reg0, RamReg);
+
+	memcpy_toio(xfer_start, buf, count);
+}
+
+#ifdef MODULE
+#define MAX_ULTRA32_CARDS   4	/* Max number of Ultra cards per module */
+static struct net_device *dev_ultra[MAX_ULTRA32_CARDS];
+
+MODULE_DESCRIPTION("SMC Ultra32 EISA ethernet driver");
+MODULE_LICENSE("GPL");
+
+int __init init_module(void)
+{
+	int this_dev, found = 0;
+
+	for (this_dev = 0; this_dev < MAX_ULTRA32_CARDS; this_dev++) {
+		struct net_device *dev = ultra32_probe(-1);
+		if (IS_ERR(dev))
+			break;
+		dev_ultra[found++] = dev;
+	}
+	if (found)
+		return 0;
+	printk(KERN_WARNING "smc-ultra32.c: No SMC Ultra32 found.\n");
+	return -ENXIO;
+}
+
+void __exit cleanup_module(void)
+{
+	int this_dev;
+
+	for (this_dev = 0; this_dev < MAX_ULTRA32_CARDS; this_dev++) {
+		struct net_device *dev = dev_ultra[this_dev];
+		if (dev) {
+			unregister_netdev(dev);
+			cleanup_card(dev);
+			free_netdev(dev);
+		}
+	}
+}
+#endif /* MODULE */
+
diff --git a/drivers/net/ethernet/8390/stnic.c b/drivers/net/ethernet/8390/stnic.c
new file mode 100644
index 0000000..d85f0a8
--- /dev/null
+++ b/drivers/net/ethernet/8390/stnic.c
@@ -0,0 +1,294 @@
+/* stnic.c : A SH7750 specific part of driver for NS DP83902A ST-NIC.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 1999 kaz Kojima
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <mach-se/mach/se.h>
+#include <asm/machvec.h>
+#ifdef CONFIG_SH_STANDARD_BIOS
+#include <asm/sh_bios.h>
+#endif
+
+#include "8390.h"
+
+#define DRV_NAME "stnic"
+
+#define byte	unsigned char
+#define half	unsigned short
+#define word	unsigned int
+#define vbyte	volatile unsigned char
+#define vhalf	volatile unsigned short
+#define vword	volatile unsigned int
+
+#define STNIC_RUN	0x01	/* 1 == Run, 0 == reset. */
+
+#define START_PG	0	/* First page of TX buffer */
+#define STOP_PG		128	/* Last page +1 of RX ring */
+
+/* Alias */
+#define STNIC_CR	E8390_CMD
+#define PG0_RSAR0	EN0_RSARLO
+#define PG0_RSAR1	EN0_RSARHI
+#define PG0_RBCR0	EN0_RCNTLO
+#define PG0_RBCR1	EN0_RCNTHI
+
+#define CR_RRD		E8390_RREAD
+#define CR_RWR		E8390_RWRITE
+#define CR_PG0		E8390_PAGE0
+#define CR_STA		E8390_START
+#define CR_RDMA		E8390_NODMA
+
+/* FIXME! YOU MUST SET YOUR OWN ETHER ADDRESS.  */
+static byte stnic_eadr[6] =
+{0x00, 0xc0, 0x6e, 0x00, 0x00, 0x07};
+
+static struct net_device *stnic_dev;
+
+static void stnic_reset (struct net_device *dev);
+static void stnic_get_hdr (struct net_device *dev, struct e8390_pkt_hdr *hdr,
+			   int ring_page);
+static void stnic_block_input (struct net_device *dev, int count,
+			       struct sk_buff *skb , int ring_offset);
+static void stnic_block_output (struct net_device *dev, int count,
+				const unsigned char *buf, int start_page);
+
+static void stnic_init (struct net_device *dev);
+
+/* SH7750 specific read/write io. */
+static inline void
+STNIC_DELAY (void)
+{
+  vword trash;
+  trash = *(vword *) 0xa0000000;
+  trash = *(vword *) 0xa0000000;
+  trash = *(vword *) 0xa0000000;
+}
+
+static inline byte
+STNIC_READ (int reg)
+{
+  byte val;
+
+  val = (*(vhalf *) (PA_83902 + ((reg) << 1)) >> 8) & 0xff;
+  STNIC_DELAY ();
+  return val;
+}
+
+static inline void
+STNIC_WRITE (int reg, byte val)
+{
+  *(vhalf *) (PA_83902 + ((reg) << 1)) = ((half) (val) << 8);
+  STNIC_DELAY ();
+}
+
+static int __init stnic_probe(void)
+{
+  struct net_device *dev;
+  int i, err;
+
+  /* If we are not running on a SolutionEngine, give up now */
+  if (! MACH_SE)
+    return -ENODEV;
+
+  /* New style probing API */
+  dev = alloc_ei_netdev();
+  if (!dev)
+  	return -ENOMEM;
+
+#ifdef CONFIG_SH_STANDARD_BIOS
+  sh_bios_get_node_addr (stnic_eadr);
+#endif
+  for (i = 0; i < ETHER_ADDR_LEN; i++)
+    dev->dev_addr[i] = stnic_eadr[i];
+
+  /* Set the base address to point to the NIC, not the "real" base! */
+  dev->base_addr = 0x1000;
+  dev->irq = IRQ_STNIC;
+  dev->netdev_ops = &ei_netdev_ops;
+
+  /* Snarf the interrupt now.  There's no point in waiting since we cannot
+     share and the board will usually be enabled. */
+  err = request_irq (dev->irq, ei_interrupt, 0, DRV_NAME, dev);
+  if (err)  {
+      printk (KERN_EMERG " unable to get IRQ %d.\n", dev->irq);
+      free_netdev(dev);
+      return err;
+    }
+
+  ei_status.name = dev->name;
+  ei_status.word16 = 1;
+#ifdef __LITTLE_ENDIAN__
+  ei_status.bigendian = 0;
+#else
+  ei_status.bigendian = 1;
+#endif
+  ei_status.tx_start_page = START_PG;
+  ei_status.rx_start_page = START_PG + TX_PAGES;
+  ei_status.stop_page = STOP_PG;
+
+  ei_status.reset_8390 = &stnic_reset;
+  ei_status.get_8390_hdr = &stnic_get_hdr;
+  ei_status.block_input = &stnic_block_input;
+  ei_status.block_output = &stnic_block_output;
+
+  stnic_init (dev);
+
+  err = register_netdev(dev);
+  if (err) {
+    free_irq(dev->irq, dev);
+    free_netdev(dev);
+    return err;
+  }
+  stnic_dev = dev;
+
+  printk (KERN_INFO "NS ST-NIC 83902A\n");
+
+  return 0;
+}
+
+static void
+stnic_reset (struct net_device *dev)
+{
+  *(vhalf *) PA_83902_RST = 0;
+  udelay (5);
+  if (ei_debug > 1)
+    printk (KERN_WARNING "8390 reset done (%ld).\n", jiffies);
+  *(vhalf *) PA_83902_RST = ~0;
+  udelay (5);
+}
+
+static void
+stnic_get_hdr (struct net_device *dev, struct e8390_pkt_hdr *hdr,
+	       int ring_page)
+{
+  half buf[2];
+
+  STNIC_WRITE (PG0_RSAR0, 0);
+  STNIC_WRITE (PG0_RSAR1, ring_page);
+  STNIC_WRITE (PG0_RBCR0, 4);
+  STNIC_WRITE (PG0_RBCR1, 0);
+  STNIC_WRITE (STNIC_CR, CR_RRD | CR_PG0 | CR_STA);
+
+  buf[0] = *(vhalf *) PA_83902_IF;
+  STNIC_DELAY ();
+  buf[1] = *(vhalf *) PA_83902_IF;
+  STNIC_DELAY ();
+  hdr->next = buf[0] >> 8;
+  hdr->status = buf[0] & 0xff;
+#ifdef __LITTLE_ENDIAN__
+  hdr->count = buf[1];
+#else
+  hdr->count = ((buf[1] >> 8) & 0xff) | (buf[1] << 8);
+#endif
+
+  if (ei_debug > 1)
+    printk (KERN_DEBUG "ring %x status %02x next %02x count %04x.\n",
+	    ring_page, hdr->status, hdr->next, hdr->count);
+
+  STNIC_WRITE (STNIC_CR, CR_RDMA | CR_PG0 | CR_STA);
+}
+
+/* Block input and output, similar to the Crynwr packet driver. If you are
+   porting to a new ethercard look at the packet driver source for hints.
+   The HP LAN doesn't use shared memory -- we put the packet
+   out through the "remote DMA" dataport. */
+
+static void
+stnic_block_input (struct net_device *dev, int length, struct sk_buff *skb,
+		   int offset)
+{
+  char *buf = skb->data;
+  half val;
+
+  STNIC_WRITE (PG0_RSAR0, offset & 0xff);
+  STNIC_WRITE (PG0_RSAR1, offset >> 8);
+  STNIC_WRITE (PG0_RBCR0, length & 0xff);
+  STNIC_WRITE (PG0_RBCR1, length >> 8);
+  STNIC_WRITE (STNIC_CR, CR_RRD | CR_PG0 | CR_STA);
+
+  if (length & 1)
+    length++;
+
+  while (length > 0)
+    {
+      val = *(vhalf *) PA_83902_IF;
+#ifdef __LITTLE_ENDIAN__
+      *buf++ = val & 0xff;
+      *buf++ = val >> 8;
+#else
+      *buf++ = val >> 8;
+      *buf++ = val & 0xff;
+#endif
+      STNIC_DELAY ();
+      length -= sizeof (half);
+    }
+
+  STNIC_WRITE (STNIC_CR, CR_RDMA | CR_PG0 | CR_STA);
+}
+
+static void
+stnic_block_output (struct net_device *dev, int length,
+		    const unsigned char *buf, int output_page)
+{
+  STNIC_WRITE (PG0_RBCR0, 1);	/* Write non-zero value */
+  STNIC_WRITE (STNIC_CR, CR_RRD | CR_PG0 | CR_STA);
+  STNIC_DELAY ();
+
+  STNIC_WRITE (PG0_RBCR0, length & 0xff);
+  STNIC_WRITE (PG0_RBCR1, length >> 8);
+  STNIC_WRITE (PG0_RSAR0, 0);
+  STNIC_WRITE (PG0_RSAR1, output_page);
+  STNIC_WRITE (STNIC_CR, CR_RWR | CR_PG0 | CR_STA);
+
+  if (length & 1)
+    length++;
+
+  while (length > 0)
+    {
+#ifdef __LITTLE_ENDIAN__
+      *(vhalf *) PA_83902_IF = ((half) buf[1] << 8) | buf[0];
+#else
+      *(vhalf *) PA_83902_IF = ((half) buf[0] << 8) | buf[1];
+#endif
+      STNIC_DELAY ();
+      buf += sizeof (half);
+      length -= sizeof (half);
+    }
+
+  STNIC_WRITE (STNIC_CR, CR_RDMA | CR_PG0 | CR_STA);
+}
+
+/* This function resets the STNIC if something screws up.  */
+static void
+stnic_init (struct net_device *dev)
+{
+  stnic_reset (dev);
+  NS8390_init (dev, 0);
+}
+
+static void __exit stnic_cleanup(void)
+{
+	unregister_netdev(stnic_dev);
+	free_irq(stnic_dev->irq, stnic_dev);
+	free_netdev(stnic_dev);
+}
+
+module_init(stnic_probe);
+module_exit(stnic_cleanup);
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/ethernet/8390/wd.c b/drivers/net/ethernet/8390/wd.c
new file mode 100644
index 0000000..8831a33
--- /dev/null
+++ b/drivers/net/ethernet/8390/wd.c
@@ -0,0 +1,567 @@
+/* wd.c: A WD80x3 ethernet driver for linux. */
+/*
+	Written 1993-94 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
+
+	This is a driver for WD8003 and WD8013 "compatible" ethercards.
+
+	Thanks to Russ Nelson (nelson@crnwyr.com) for loaning me a WD8013.
+
+	Changelog:
+
+	Paul Gortmaker	: multiple card support for module users, support
+			  for non-standard memory sizes.
+
+
+*/
+
+static const char version[] =
+	"wd.c:v1.10 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+
+#include <asm/io.h>
+#include <asm/system.h>
+
+#include "8390.h"
+
+#define DRV_NAME "wd"
+
+/* A zero-terminated list of I/O addresses to be probed. */
+static unsigned int wd_portlist[] __initdata =
+{0x300, 0x280, 0x380, 0x240, 0};
+
+static int wd_probe1(struct net_device *dev, int ioaddr);
+
+static int wd_open(struct net_device *dev);
+static void wd_reset_8390(struct net_device *dev);
+static void wd_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
+						int ring_page);
+static void wd_block_input(struct net_device *dev, int count,
+						  struct sk_buff *skb, int ring_offset);
+static void wd_block_output(struct net_device *dev, int count,
+							const unsigned char *buf, int start_page);
+static int wd_close(struct net_device *dev);
+
+
+#define WD_START_PG		0x00	/* First page of TX buffer */
+#define WD03_STOP_PG	0x20	/* Last page +1 of RX ring */
+#define WD13_STOP_PG	0x40	/* Last page +1 of RX ring */
+
+#define WD_CMDREG		0		/* Offset to ASIC command register. */
+#define	 WD_RESET		0x80	/* Board reset, in WD_CMDREG. */
+#define	 WD_MEMENB		0x40	/* Enable the shared memory. */
+#define WD_CMDREG5		5		/* Offset to 16-bit-only ASIC register 5. */
+#define	 ISA16			0x80	/* Enable 16 bit access from the ISA bus. */
+#define	 NIC16			0x40	/* Enable 16 bit access from the 8390. */
+#define WD_NIC_OFFSET	16		/* Offset to the 8390 from the base_addr. */
+#define WD_IO_EXTENT	32
+
+
+/*	Probe for the WD8003 and WD8013.  These cards have the station
+	address PROM at I/O ports <base>+8 to <base>+13, with a checksum
+	following. A Soundblaster can have the same checksum as an WDethercard,
+	so we have an extra exclusionary check for it.
+
+	The wd_probe1() routine initializes the card and fills the
+	station address field. */
+
+static int __init do_wd_probe(struct net_device *dev)
+{
+	int i;
+	struct resource *r;
+	int base_addr = dev->base_addr;
+	int irq = dev->irq;
+	int mem_start = dev->mem_start;
+	int mem_end = dev->mem_end;
+
+	if (base_addr > 0x1ff) {	/* Check a user specified location. */
+		r = request_region(base_addr, WD_IO_EXTENT, "wd-probe");
+		if ( r == NULL)
+			return -EBUSY;
+		i = wd_probe1(dev, base_addr);
+		if (i != 0)
+			release_region(base_addr, WD_IO_EXTENT);
+		else
+			r->name = dev->name;
+		return i;
+	}
+	else if (base_addr != 0)	/* Don't probe at all. */
+		return -ENXIO;
+
+	for (i = 0; wd_portlist[i]; i++) {
+		int ioaddr = wd_portlist[i];
+		r = request_region(ioaddr, WD_IO_EXTENT, "wd-probe");
+		if (r == NULL)
+			continue;
+		if (wd_probe1(dev, ioaddr) == 0) {
+			r->name = dev->name;
+			return 0;
+		}
+		release_region(ioaddr, WD_IO_EXTENT);
+		dev->irq = irq;
+		dev->mem_start = mem_start;
+		dev->mem_end = mem_end;
+	}
+
+	return -ENODEV;
+}
+
+#ifndef MODULE
+struct net_device * __init wd_probe(int unit)
+{
+	struct net_device *dev = alloc_ei_netdev();
+	int err;
+
+	if (!dev)
+		return ERR_PTR(-ENOMEM);
+
+	sprintf(dev->name, "eth%d", unit);
+	netdev_boot_setup_check(dev);
+
+	err = do_wd_probe(dev);
+	if (err)
+		goto out;
+	return dev;
+out:
+	free_netdev(dev);
+	return ERR_PTR(err);
+}
+#endif
+
+static const struct net_device_ops wd_netdev_ops = {
+	.ndo_open		= wd_open,
+	.ndo_stop		= wd_close,
+	.ndo_start_xmit		= ei_start_xmit,
+	.ndo_tx_timeout		= ei_tx_timeout,
+	.ndo_get_stats		= ei_get_stats,
+	.ndo_set_multicast_list = ei_set_multicast_list,
+	.ndo_validate_addr	= eth_validate_addr,
+	.ndo_set_mac_address 	= eth_mac_addr,
+	.ndo_change_mtu		= eth_change_mtu,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	.ndo_poll_controller 	= ei_poll,
+#endif
+};
+
+static int __init wd_probe1(struct net_device *dev, int ioaddr)
+{
+	int i;
+	int err;
+	int checksum = 0;
+	int ancient = 0;			/* An old card without config registers. */
+	int word16 = 0;				/* 0 = 8 bit, 1 = 16 bit */
+	const char *model_name;
+	static unsigned version_printed;
+
+	for (i = 0; i < 8; i++)
+		checksum += inb(ioaddr + 8 + i);
+	if (inb(ioaddr + 8) == 0xff 	/* Extra check to avoid soundcard. */
+		|| inb(ioaddr + 9) == 0xff
+		|| (checksum & 0xff) != 0xFF)
+		return -ENODEV;
+
+	/* Check for semi-valid mem_start/end values if supplied. */
+	if ((dev->mem_start % 0x2000) || (dev->mem_end % 0x2000)) {
+		printk(KERN_WARNING "wd.c: user supplied mem_start or mem_end not on 8kB boundary - ignored.\n");
+		dev->mem_start = 0;
+		dev->mem_end = 0;
+	}
+
+	if (ei_debug  &&  version_printed++ == 0)
+		printk(version);
+
+	for (i = 0; i < 6; i++)
+		dev->dev_addr[i] = inb(ioaddr + 8 + i);
+
+	printk("%s: WD80x3 at %#3x, %pM",
+	       dev->name, ioaddr, dev->dev_addr);
+
+	/* The following PureData probe code was contributed by
+	   Mike Jagdis <jaggy@purplet.demon.co.uk>. Puredata does software
+	   configuration differently from others so we have to check for them.
+	   This detects an 8 bit, 16 bit or dumb (Toshiba, jumpered) card.
+	   */
+	if (inb(ioaddr+0) == 'P' && inb(ioaddr+1) == 'D') {
+		unsigned char reg5 = inb(ioaddr+5);
+
+		switch (inb(ioaddr+2)) {
+		case 0x03: word16 = 0; model_name = "PDI8023-8";	break;
+		case 0x05: word16 = 0; model_name = "PDUC8023";	break;
+		case 0x0a: word16 = 1; model_name = "PDI8023-16"; break;
+			/* Either 0x01 (dumb) or they've released a new version. */
+		default:	 word16 = 0; model_name = "PDI8023";	break;
+		}
+		dev->mem_start = ((reg5 & 0x1c) + 0xc0) << 12;
+		dev->irq = (reg5 & 0xe0) == 0xe0 ? 10 : (reg5 >> 5) + 1;
+	} else {								/* End of PureData probe */
+		/* This method of checking for a 16-bit board is borrowed from the
+		   we.c driver.  A simpler method is just to look in ASIC reg. 0x03.
+		   I'm comparing the two method in alpha test to make certain they
+		   return the same result. */
+		/* Check for the old 8 bit board - it has register 0/8 aliasing.
+		   Do NOT check i>=6 here -- it hangs the old 8003 boards! */
+		for (i = 0; i < 6; i++)
+			if (inb(ioaddr+i) != inb(ioaddr+8+i))
+				break;
+		if (i >= 6) {
+			ancient = 1;
+			model_name = "WD8003-old";
+			word16 = 0;
+		} else {
+			int tmp = inb(ioaddr+1); /* fiddle with 16bit bit */
+			outb( tmp ^ 0x01, ioaddr+1 ); /* attempt to clear 16bit bit */
+			if (((inb( ioaddr+1) & 0x01) == 0x01) /* A 16 bit card */
+				&& (tmp & 0x01) == 0x01	) {				/* In a 16 slot. */
+				int asic_reg5 = inb(ioaddr+WD_CMDREG5);
+				/* Magic to set ASIC to word-wide mode. */
+				outb( NIC16 | (asic_reg5&0x1f), ioaddr+WD_CMDREG5);
+				outb(tmp, ioaddr+1);
+				model_name = "WD8013";
+				word16 = 1;		/* We have a 16bit board here! */
+			} else {
+				model_name = "WD8003";
+				word16 = 0;
+			}
+			outb(tmp, ioaddr+1);			/* Restore original reg1 value. */
+		}
+#ifndef final_version
+		if ( !ancient && (inb(ioaddr+1) & 0x01) != (word16 & 0x01))
+			printk("\nWD80?3: Bus width conflict, %d (probe) != %d (reg report).",
+				   word16 ? 16 : 8, (inb(ioaddr+1) & 0x01) ? 16 : 8);
+#endif
+	}
+
+#if defined(WD_SHMEM) && WD_SHMEM > 0x80000
+	/* Allow a compile-time override.	 */
+	dev->mem_start = WD_SHMEM;
+#else
+	if (dev->mem_start == 0) {
+		/* Sanity and old 8003 check */
+		int reg0 = inb(ioaddr);
+		if (reg0 == 0xff || reg0 == 0) {
+			/* Future plan: this could check a few likely locations first. */
+			dev->mem_start = 0xd0000;
+			printk(" assigning address %#lx", dev->mem_start);
+		} else {
+			int high_addr_bits = inb(ioaddr+WD_CMDREG5) & 0x1f;
+			/* Some boards don't have the register 5 -- it returns 0xff. */
+			if (high_addr_bits == 0x1f || word16 == 0)
+				high_addr_bits = 0x01;
+			dev->mem_start = ((reg0&0x3f) << 13) + (high_addr_bits << 19);
+		}
+	}
+#endif
+
+	/* The 8390 isn't at the base address -- the ASIC regs are there! */
+	dev->base_addr = ioaddr+WD_NIC_OFFSET;
+
+	if (dev->irq < 2) {
+		static const int irqmap[] = {9, 3, 5, 7, 10, 11, 15, 4};
+		int reg1 = inb(ioaddr+1);
+		int reg4 = inb(ioaddr+4);
+		if (ancient || reg1 == 0xff) {	/* Ack!! No way to read the IRQ! */
+			short nic_addr = ioaddr+WD_NIC_OFFSET;
+			unsigned long irq_mask;
+
+			/* We have an old-style ethercard that doesn't report its IRQ
+			   line.  Do autoirq to find the IRQ line. Note that this IS NOT
+			   a reliable way to trigger an interrupt. */
+			outb_p(E8390_NODMA + E8390_STOP, nic_addr);
+			outb(0x00, nic_addr+EN0_IMR);	/* Disable all intrs. */
+
+			irq_mask = probe_irq_on();
+			outb_p(0xff, nic_addr + EN0_IMR);	/* Enable all interrupts. */
+			outb_p(0x00, nic_addr + EN0_RCNTLO);
+			outb_p(0x00, nic_addr + EN0_RCNTHI);
+			outb(E8390_RREAD+E8390_START, nic_addr); /* Trigger it... */
+			mdelay(20);
+			dev->irq = probe_irq_off(irq_mask);
+
+			outb_p(0x00, nic_addr+EN0_IMR);	/* Mask all intrs. again. */
+
+			if (ei_debug > 2)
+				printk(" autoirq is %d", dev->irq);
+			if (dev->irq < 2)
+				dev->irq = word16 ? 10 : 5;
+		} else
+			dev->irq = irqmap[((reg4 >> 5) & 0x03) + (reg1 & 0x04)];
+	} else if (dev->irq == 2)		/* Fixup bogosity: IRQ2 is really IRQ9 */
+		dev->irq = 9;
+
+	/* Snarf the interrupt now.  There's no point in waiting since we cannot
+	   share and the board will usually be enabled. */
+	i = request_irq(dev->irq, ei_interrupt, 0, DRV_NAME, dev);
+	if (i) {
+		printk (" unable to get IRQ %d.\n", dev->irq);
+		return i;
+	}
+
+	/* OK, were are certain this is going to work.  Setup the device. */
+	ei_status.name = model_name;
+	ei_status.word16 = word16;
+	ei_status.tx_start_page = WD_START_PG;
+	ei_status.rx_start_page = WD_START_PG + TX_PAGES;
+
+	/* Don't map in the shared memory until the board is actually opened. */
+
+	/* Some cards (eg WD8003EBT) can be jumpered for more (32k!) memory. */
+	if (dev->mem_end != 0) {
+		ei_status.stop_page = (dev->mem_end - dev->mem_start)/256;
+		ei_status.priv = dev->mem_end - dev->mem_start;
+	} else {
+		ei_status.stop_page = word16 ? WD13_STOP_PG : WD03_STOP_PG;
+		dev->mem_end = dev->mem_start + (ei_status.stop_page - WD_START_PG)*256;
+		ei_status.priv = (ei_status.stop_page - WD_START_PG)*256;
+	}
+
+	ei_status.mem = ioremap(dev->mem_start, ei_status.priv);
+	if (!ei_status.mem) {
+		free_irq(dev->irq, dev);
+		return -ENOMEM;
+	}
+
+	printk(" %s, IRQ %d, shared memory at %#lx-%#lx.\n",
+		   model_name, dev->irq, dev->mem_start, dev->mem_end-1);
+
+	ei_status.reset_8390 = wd_reset_8390;
+	ei_status.block_input = wd_block_input;
+	ei_status.block_output = wd_block_output;
+	ei_status.get_8390_hdr = wd_get_8390_hdr;
+
+	dev->netdev_ops = &wd_netdev_ops;
+	NS8390_init(dev, 0);
+
+#if 1
+	/* Enable interrupt generation on softconfig cards -- M.U */
+	/* .. but possibly potentially unsafe - Donald */
+	if (inb(ioaddr+14) & 0x20)
+		outb(inb(ioaddr+4)|0x80, ioaddr+4);
+#endif
+
+	err = register_netdev(dev);
+	if (err) {
+		free_irq(dev->irq, dev);
+		iounmap(ei_status.mem);
+	}
+	return err;
+}
+
+static int
+wd_open(struct net_device *dev)
+{
+  int ioaddr = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
+
+  /* Map in the shared memory. Always set register 0 last to remain
+	 compatible with very old boards. */
+  ei_status.reg0 = ((dev->mem_start>>13) & 0x3f) | WD_MEMENB;
+  ei_status.reg5 = ((dev->mem_start>>19) & 0x1f) | NIC16;
+
+  if (ei_status.word16)
+	  outb(ei_status.reg5, ioaddr+WD_CMDREG5);
+  outb(ei_status.reg0, ioaddr); /* WD_CMDREG */
+
+  return ei_open(dev);
+}
+
+static void
+wd_reset_8390(struct net_device *dev)
+{
+	int wd_cmd_port = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
+
+	outb(WD_RESET, wd_cmd_port);
+	if (ei_debug > 1) printk("resetting the WD80x3 t=%lu...", jiffies);
+	ei_status.txing = 0;
+
+	/* Set up the ASIC registers, just in case something changed them. */
+	outb((((dev->mem_start>>13) & 0x3f)|WD_MEMENB), wd_cmd_port);
+	if (ei_status.word16)
+		outb(NIC16 | ((dev->mem_start>>19) & 0x1f), wd_cmd_port+WD_CMDREG5);
+
+	if (ei_debug > 1) printk("reset done\n");
+}
+
+/* Grab the 8390 specific header. Similar to the block_input routine, but
+   we don't need to be concerned with ring wrap as the header will be at
+   the start of a page, so we optimize accordingly. */
+
+static void
+wd_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
+{
+
+	int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
+	void __iomem *hdr_start = ei_status.mem + ((ring_page - WD_START_PG)<<8);
+
+	/* We'll always get a 4 byte header read followed by a packet read, so
+	   we enable 16 bit mode before the header, and disable after the body. */
+	if (ei_status.word16)
+		outb(ISA16 | ei_status.reg5, wd_cmdreg+WD_CMDREG5);
+
+#ifdef __BIG_ENDIAN
+	/* Officially this is what we are doing, but the readl() is faster */
+	/* unfortunately it isn't endian aware of the struct               */
+	memcpy_fromio(hdr, hdr_start, sizeof(struct e8390_pkt_hdr));
+	hdr->count = le16_to_cpu(hdr->count);
+#else
+	((unsigned int*)hdr)[0] = readl(hdr_start);
+#endif
+}
+
+/* Block input and output are easy on shared memory ethercards, and trivial
+   on the Western digital card where there is no choice of how to do it.
+   The only complications are that the ring buffer wraps, and need to map
+   switch between 8- and 16-bit modes. */
+
+static void
+wd_block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset)
+{
+	int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
+	unsigned long offset = ring_offset - (WD_START_PG<<8);
+	void __iomem *xfer_start = ei_status.mem + offset;
+
+	if (offset + count > ei_status.priv) {
+		/* We must wrap the input move. */
+		int semi_count = ei_status.priv - offset;
+		memcpy_fromio(skb->data, xfer_start, semi_count);
+		count -= semi_count;
+		memcpy_fromio(skb->data + semi_count, ei_status.mem + TX_PAGES * 256, count);
+	} else {
+		/* Packet is in one chunk -- we can copy + cksum. */
+		memcpy_fromio(skb->data, xfer_start, count);
+	}
+
+	/* Turn off 16 bit access so that reboot works.	 ISA brain-damage */
+	if (ei_status.word16)
+		outb(ei_status.reg5, wd_cmdreg+WD_CMDREG5);
+}
+
+static void
+wd_block_output(struct net_device *dev, int count, const unsigned char *buf,
+				int start_page)
+{
+	int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
+	void __iomem *shmem = ei_status.mem + ((start_page - WD_START_PG)<<8);
+
+
+	if (ei_status.word16) {
+		/* Turn on and off 16 bit access so that reboot works. */
+		outb(ISA16 | ei_status.reg5, wd_cmdreg+WD_CMDREG5);
+		memcpy_toio(shmem, buf, count);
+		outb(ei_status.reg5, wd_cmdreg+WD_CMDREG5);
+	} else
+		memcpy_toio(shmem, buf, count);
+}
+
+
+static int
+wd_close(struct net_device *dev)
+{
+	int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
+
+	if (ei_debug > 1)
+		printk("%s: Shutting down ethercard.\n", dev->name);
+	ei_close(dev);
+
+	/* Change from 16-bit to 8-bit shared memory so reboot works. */
+	if (ei_status.word16)
+		outb(ei_status.reg5, wd_cmdreg + WD_CMDREG5 );
+
+	/* And disable the shared memory. */
+	outb(ei_status.reg0 & ~WD_MEMENB, wd_cmdreg);
+
+	return 0;
+}
+
+
+#ifdef MODULE
+#define MAX_WD_CARDS	4	/* Max number of wd cards per module */
+static struct net_device *dev_wd[MAX_WD_CARDS];
+static int io[MAX_WD_CARDS];
+static int irq[MAX_WD_CARDS];
+static int mem[MAX_WD_CARDS];
+static int mem_end[MAX_WD_CARDS];	/* for non std. mem size */
+
+module_param_array(io, int, NULL, 0);
+module_param_array(irq, int, NULL, 0);
+module_param_array(mem, int, NULL, 0);
+module_param_array(mem_end, int, NULL, 0);
+MODULE_PARM_DESC(io, "I/O base address(es)");
+MODULE_PARM_DESC(irq, "IRQ number(s) (ignored for PureData boards)");
+MODULE_PARM_DESC(mem, "memory base address(es)(ignored for PureData boards)");
+MODULE_PARM_DESC(mem_end, "memory end address(es)");
+MODULE_DESCRIPTION("ISA Western Digital wd8003/wd8013 ; SMC Elite, Elite16 ethernet driver");
+MODULE_LICENSE("GPL");
+
+/* This is set up so that only a single autoprobe takes place per call.
+ISA device autoprobes on a running machine are not recommended. */
+
+int __init init_module(void)
+{
+	struct net_device *dev;
+	int this_dev, found = 0;
+
+	for (this_dev = 0; this_dev < MAX_WD_CARDS; this_dev++) {
+		if (io[this_dev] == 0)  {
+			if (this_dev != 0) break; /* only autoprobe 1st one */
+			printk(KERN_NOTICE "wd.c: Presently autoprobing (not recommended) for a single card.\n");
+		}
+		dev = alloc_ei_netdev();
+		if (!dev)
+			break;
+		dev->irq = irq[this_dev];
+		dev->base_addr = io[this_dev];
+		dev->mem_start = mem[this_dev];
+		dev->mem_end = mem_end[this_dev];
+		if (do_wd_probe(dev) == 0) {
+			dev_wd[found++] = dev;
+			continue;
+		}
+		free_netdev(dev);
+		printk(KERN_WARNING "wd.c: No wd80x3 card found (i/o = 0x%x).\n", io[this_dev]);
+		break;
+	}
+	if (found)
+		return 0;
+	return -ENXIO;
+}
+
+static void cleanup_card(struct net_device *dev)
+{
+	free_irq(dev->irq, dev);
+	release_region(dev->base_addr - WD_NIC_OFFSET, WD_IO_EXTENT);
+	iounmap(ei_status.mem);
+}
+
+void __exit
+cleanup_module(void)
+{
+	int this_dev;
+
+	for (this_dev = 0; this_dev < MAX_WD_CARDS; this_dev++) {
+		struct net_device *dev = dev_wd[this_dev];
+		if (dev) {
+			unregister_netdev(dev);
+			cleanup_card(dev);
+			free_netdev(dev);
+		}
+	}
+}
+#endif /* MODULE */
diff --git a/drivers/net/ethernet/8390/zorro8390.c b/drivers/net/ethernet/8390/zorro8390.c
new file mode 100644
index 0000000..15e7751a
--- /dev/null
+++ b/drivers/net/ethernet/8390/zorro8390.c
@@ -0,0 +1,452 @@
+/*
+ *  Amiga Linux/m68k and Linux/PPC Zorro NS8390 Ethernet Driver
+ *
+ *  (C) Copyright 1998-2000 by some Elitist 680x0 Users(TM)
+ *
+ *  ---------------------------------------------------------------------------
+ *
+ *  This program is based on all the other NE2000 drivers for Linux
+ *
+ *  ---------------------------------------------------------------------------
+ *
+ *  This file is subject to the terms and conditions of the GNU General Public
+ *  License.  See the file COPYING in the main directory of the Linux
+ *  distribution for more details.
+ *
+ *  ---------------------------------------------------------------------------
+ *
+ *  The Ariadne II and X-Surf are Zorro-II boards containing Realtek RTL8019AS
+ *  Ethernet Controllers.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/zorro.h>
+#include <linux/jiffies.h>
+
+#include <asm/system.h>
+#include <asm/irq.h>
+#include <asm/amigaints.h>
+#include <asm/amigahw.h>
+
+#define EI_SHIFT(x)		(ei_local->reg_offset[x])
+#define ei_inb(port)		in_8(port)
+#define ei_outb(val, port)	out_8(port, val)
+#define ei_inb_p(port)		in_8(port)
+#define ei_outb_p(val, port)	out_8(port, val)
+
+static const char version[] =
+	"8390.c:v1.10cvs 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
+
+#include "lib8390.c"
+
+#define DRV_NAME	"zorro8390"
+
+#define NE_BASE		(dev->base_addr)
+#define NE_CMD		(0x00 * 2)
+#define NE_DATAPORT	(0x10 * 2)	/* NatSemi-defined port window offset */
+#define NE_RESET	(0x1f * 2)	/* Issue a read to reset,
+					 * a write to clear. */
+#define NE_IO_EXTENT	(0x20 * 2)
+
+#define NE_EN0_ISR	(0x07 * 2)
+#define NE_EN0_DCFG	(0x0e * 2)
+
+#define NE_EN0_RSARLO	(0x08 * 2)
+#define NE_EN0_RSARHI	(0x09 * 2)
+#define NE_EN0_RCNTLO	(0x0a * 2)
+#define NE_EN0_RXCR	(0x0c * 2)
+#define NE_EN0_TXCR	(0x0d * 2)
+#define NE_EN0_RCNTHI	(0x0b * 2)
+#define NE_EN0_IMR	(0x0f * 2)
+
+#define NESM_START_PG	0x40	/* First page of TX buffer */
+#define NESM_STOP_PG	0x80	/* Last page +1 of RX ring */
+
+#define WORDSWAP(a)	((((a) >> 8) & 0xff) | ((a) << 8))
+
+static struct card_info {
+	zorro_id id;
+	const char *name;
+	unsigned int offset;
+} cards[] __devinitdata = {
+	{ ZORRO_PROD_VILLAGE_TRONIC_ARIADNE2, "Ariadne II", 0x0600 },
+	{ ZORRO_PROD_INDIVIDUAL_COMPUTERS_X_SURF, "X-Surf", 0x8600 },
+};
+
+/* Hard reset the card.  This used to pause for the same period that a
+ * 8390 reset command required, but that shouldn't be necessary.
+ */
+static void zorro8390_reset_8390(struct net_device *dev)
+{
+	unsigned long reset_start_time = jiffies;
+
+	if (ei_debug > 1)
+		netdev_dbg(dev, "resetting - t=%ld...\n", jiffies);
+
+	z_writeb(z_readb(NE_BASE + NE_RESET), NE_BASE + NE_RESET);
+
+	ei_status.txing = 0;
+	ei_status.dmaing = 0;
+
+	/* This check _should_not_ be necessary, omit eventually. */
+	while ((z_readb(NE_BASE + NE_EN0_ISR) & ENISR_RESET) == 0)
+		if (time_after(jiffies, reset_start_time + 2 * HZ / 100)) {
+			netdev_warn(dev, "%s: did not complete\n", __func__);
+			break;
+		}
+	z_writeb(ENISR_RESET, NE_BASE + NE_EN0_ISR);	/* Ack intr */
+}
+
+/* Grab the 8390 specific header. Similar to the block_input routine, but
+ * we don't need to be concerned with ring wrap as the header will be at
+ * the start of a page, so we optimize accordingly.
+ */
+static void zorro8390_get_8390_hdr(struct net_device *dev,
+				   struct e8390_pkt_hdr *hdr, int ring_page)
+{
+	int nic_base = dev->base_addr;
+	int cnt;
+	short *ptrs;
+
+	/* This *shouldn't* happen.
+	 * If it does, it's the last thing you'll see
+	 */
+	if (ei_status.dmaing) {
+		netdev_err(dev, "%s: DMAing conflict [DMAstat:%d][irqlock:%d]\n",
+			   __func__, ei_status.dmaing, ei_status.irqlock);
+		return;
+	}
+
+	ei_status.dmaing |= 0x01;
+	z_writeb(E8390_NODMA + E8390_PAGE0 + E8390_START, nic_base + NE_CMD);
+	z_writeb(ENISR_RDC, nic_base + NE_EN0_ISR);
+	z_writeb(sizeof(struct e8390_pkt_hdr), nic_base + NE_EN0_RCNTLO);
+	z_writeb(0, nic_base + NE_EN0_RCNTHI);
+	z_writeb(0, nic_base + NE_EN0_RSARLO);		/* On page boundary */
+	z_writeb(ring_page, nic_base + NE_EN0_RSARHI);
+	z_writeb(E8390_RREAD+E8390_START, nic_base + NE_CMD);
+
+	ptrs = (short *)hdr;
+	for (cnt = 0; cnt < sizeof(struct e8390_pkt_hdr) >> 1; cnt++)
+		*ptrs++ = z_readw(NE_BASE + NE_DATAPORT);
+
+	z_writeb(ENISR_RDC, nic_base + NE_EN0_ISR);	/* Ack intr */
+
+	hdr->count = WORDSWAP(hdr->count);
+
+	ei_status.dmaing &= ~0x01;
+}
+
+/* Block input and output, similar to the Crynwr packet driver.
+ * If you are porting to a new ethercard, look at the packet driver source
+ * for hints. The NEx000 doesn't share the on-board packet memory --
+ * you have to put the packet out through the "remote DMA" dataport
+ * using z_writeb.
+ */
+static void zorro8390_block_input(struct net_device *dev, int count,
+				  struct sk_buff *skb, int ring_offset)
+{
+	int nic_base = dev->base_addr;
+	char *buf = skb->data;
+	short *ptrs;
+	int cnt;
+
+	/* This *shouldn't* happen.
+	 * If it does, it's the last thing you'll see
+	 */
+	if (ei_status.dmaing) {
+		netdev_err(dev, "%s: DMAing conflict [DMAstat:%d][irqlock:%d]\n",
+			   __func__, ei_status.dmaing, ei_status.irqlock);
+		return;
+	}
+	ei_status.dmaing |= 0x01;
+	z_writeb(E8390_NODMA + E8390_PAGE0 + E8390_START, nic_base + NE_CMD);
+	z_writeb(ENISR_RDC, nic_base + NE_EN0_ISR);
+	z_writeb(count & 0xff, nic_base + NE_EN0_RCNTLO);
+	z_writeb(count >> 8, nic_base + NE_EN0_RCNTHI);
+	z_writeb(ring_offset & 0xff, nic_base + NE_EN0_RSARLO);
+	z_writeb(ring_offset >> 8, nic_base + NE_EN0_RSARHI);
+	z_writeb(E8390_RREAD+E8390_START, nic_base + NE_CMD);
+	ptrs = (short *)buf;
+	for (cnt = 0; cnt < count >> 1; cnt++)
+		*ptrs++ = z_readw(NE_BASE + NE_DATAPORT);
+	if (count & 0x01)
+		buf[count - 1] = z_readb(NE_BASE + NE_DATAPORT);
+
+	z_writeb(ENISR_RDC, nic_base + NE_EN0_ISR);	/* Ack intr */
+	ei_status.dmaing &= ~0x01;
+}
+
+static void zorro8390_block_output(struct net_device *dev, int count,
+				   const unsigned char *buf,
+				   const int start_page)
+{
+	int nic_base = NE_BASE;
+	unsigned long dma_start;
+	short *ptrs;
+	int cnt;
+
+	/* Round the count up for word writes.  Do we need to do this?
+	 * What effect will an odd byte count have on the 8390?
+	 * I should check someday.
+	 */
+	if (count & 0x01)
+		count++;
+
+	/* This *shouldn't* happen.
+	 * If it does, it's the last thing you'll see
+	 */
+	if (ei_status.dmaing) {
+		netdev_err(dev, "%s: DMAing conflict [DMAstat:%d][irqlock:%d]\n",
+			   __func__, ei_status.dmaing, ei_status.irqlock);
+		return;
+	}
+	ei_status.dmaing |= 0x01;
+	/* We should already be in page 0, but to be safe... */
+	z_writeb(E8390_PAGE0+E8390_START+E8390_NODMA, nic_base + NE_CMD);
+
+	z_writeb(ENISR_RDC, nic_base + NE_EN0_ISR);
+
+	/* Now the normal output. */
+	z_writeb(count & 0xff, nic_base + NE_EN0_RCNTLO);
+	z_writeb(count >> 8,   nic_base + NE_EN0_RCNTHI);
+	z_writeb(0x00, nic_base + NE_EN0_RSARLO);
+	z_writeb(start_page, nic_base + NE_EN0_RSARHI);
+
+	z_writeb(E8390_RWRITE + E8390_START, nic_base + NE_CMD);
+	ptrs = (short *)buf;
+	for (cnt = 0; cnt < count >> 1; cnt++)
+		z_writew(*ptrs++, NE_BASE + NE_DATAPORT);
+
+	dma_start = jiffies;
+
+	while ((z_readb(NE_BASE + NE_EN0_ISR) & ENISR_RDC) == 0)
+		if (time_after(jiffies, dma_start + 2 * HZ / 100)) {
+					/* 20ms */
+			netdev_err(dev, "timeout waiting for Tx RDC\n");
+			zorro8390_reset_8390(dev);
+			__NS8390_init(dev, 1);
+			break;
+		}
+
+	z_writeb(ENISR_RDC, nic_base + NE_EN0_ISR);	/* Ack intr */
+	ei_status.dmaing &= ~0x01;
+}
+
+static int zorro8390_open(struct net_device *dev)
+{
+	__ei_open(dev);
+	return 0;
+}
+
+static int zorro8390_close(struct net_device *dev)
+{
+	if (ei_debug > 1)
+		netdev_dbg(dev, "Shutting down ethercard\n");
+	__ei_close(dev);
+	return 0;
+}
+
+static void __devexit zorro8390_remove_one(struct zorro_dev *z)
+{
+	struct net_device *dev = zorro_get_drvdata(z);
+
+	unregister_netdev(dev);
+	free_irq(IRQ_AMIGA_PORTS, dev);
+	release_mem_region(ZTWO_PADDR(dev->base_addr), NE_IO_EXTENT * 2);
+	free_netdev(dev);
+}
+
+static struct zorro_device_id zorro8390_zorro_tbl[] __devinitdata = {
+	{ ZORRO_PROD_VILLAGE_TRONIC_ARIADNE2, },
+	{ ZORRO_PROD_INDIVIDUAL_COMPUTERS_X_SURF, },
+	{ 0 }
+};
+MODULE_DEVICE_TABLE(zorro, zorro8390_zorro_tbl);
+
+static const struct net_device_ops zorro8390_netdev_ops = {
+	.ndo_open		= zorro8390_open,
+	.ndo_stop		= zorro8390_close,
+	.ndo_start_xmit		= __ei_start_xmit,
+	.ndo_tx_timeout		= __ei_tx_timeout,
+	.ndo_get_stats		= __ei_get_stats,
+	.ndo_set_multicast_list = __ei_set_multicast_list,
+	.ndo_validate_addr	= eth_validate_addr,
+	.ndo_set_mac_address	= eth_mac_addr,
+	.ndo_change_mtu		= eth_change_mtu,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	.ndo_poll_controller	= __ei_poll,
+#endif
+};
+
+static int __devinit zorro8390_init(struct net_device *dev,
+				    unsigned long board, const char *name,
+				    unsigned long ioaddr)
+{
+	int i;
+	int err;
+	unsigned char SA_prom[32];
+	int start_page, stop_page;
+	static u32 zorro8390_offsets[16] = {
+		0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e,
+		0x10, 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e,
+	};
+
+	/* Reset card. Who knows what dain-bramaged state it was left in. */
+	{
+		unsigned long reset_start_time = jiffies;
+
+		z_writeb(z_readb(ioaddr + NE_RESET), ioaddr + NE_RESET);
+
+		while ((z_readb(ioaddr + NE_EN0_ISR) & ENISR_RESET) == 0)
+			if (time_after(jiffies,
+				       reset_start_time + 2 * HZ / 100)) {
+				netdev_warn(dev, "not found (no reset ack)\n");
+				return -ENODEV;
+			}
+
+		z_writeb(0xff, ioaddr + NE_EN0_ISR);	/* Ack all intr. */
+	}
+
+	/* Read the 16 bytes of station address PROM.
+	 * We must first initialize registers,
+	 * similar to NS8390_init(eifdev, 0).
+	 * We can't reliably read the SAPROM address without this.
+	 * (I learned the hard way!).
+	 */
+	{
+		static const struct {
+			u32 value;
+			u32 offset;
+		} program_seq[] = {
+			{E8390_NODMA + E8390_PAGE0 + E8390_STOP, NE_CMD},
+						/* Select page 0 */
+			{0x48,	NE_EN0_DCFG},	/* 0x48: Set byte-wide access */
+			{0x00,	NE_EN0_RCNTLO},	/* Clear the count regs */
+			{0x00,	NE_EN0_RCNTHI},
+			{0x00,	NE_EN0_IMR},	/* Mask completion irq */
+			{0xFF,	NE_EN0_ISR},
+			{E8390_RXOFF, NE_EN0_RXCR}, /* 0x20 Set to monitor */
+			{E8390_TXOFF, NE_EN0_TXCR}, /* 0x02 and loopback mode */
+			{32,	NE_EN0_RCNTLO},
+			{0x00,	NE_EN0_RCNTHI},
+			{0x00,	NE_EN0_RSARLO},	/* DMA starting at 0x0000 */
+			{0x00,	NE_EN0_RSARHI},
+			{E8390_RREAD + E8390_START, NE_CMD},
+		};
+		for (i = 0; i < ARRAY_SIZE(program_seq); i++)
+			z_writeb(program_seq[i].value,
+				 ioaddr + program_seq[i].offset);
+	}
+	for (i = 0; i < 16; i++) {
+		SA_prom[i] = z_readb(ioaddr + NE_DATAPORT);
+		(void)z_readb(ioaddr + NE_DATAPORT);
+	}
+
+	/* We must set the 8390 for word mode. */
+	z_writeb(0x49, ioaddr + NE_EN0_DCFG);
+	start_page = NESM_START_PG;
+	stop_page = NESM_STOP_PG;
+
+	dev->base_addr = ioaddr;
+	dev->irq = IRQ_AMIGA_PORTS;
+
+	/* Install the Interrupt handler */
+	i = request_irq(IRQ_AMIGA_PORTS, __ei_interrupt,
+			IRQF_SHARED, DRV_NAME, dev);
+	if (i)
+		return i;
+
+	for (i = 0; i < ETHER_ADDR_LEN; i++)
+		dev->dev_addr[i] = SA_prom[i];
+
+	pr_debug("Found ethernet address: %pM\n", dev->dev_addr);
+
+	ei_status.name = name;
+	ei_status.tx_start_page = start_page;
+	ei_status.stop_page = stop_page;
+	ei_status.word16 = 1;
+
+	ei_status.rx_start_page = start_page + TX_PAGES;
+
+	ei_status.reset_8390 = zorro8390_reset_8390;
+	ei_status.block_input = zorro8390_block_input;
+	ei_status.block_output = zorro8390_block_output;
+	ei_status.get_8390_hdr = zorro8390_get_8390_hdr;
+	ei_status.reg_offset = zorro8390_offsets;
+
+	dev->netdev_ops = &zorro8390_netdev_ops;
+	__NS8390_init(dev, 0);
+	err = register_netdev(dev);
+	if (err) {
+		free_irq(IRQ_AMIGA_PORTS, dev);
+		return err;
+	}
+
+	netdev_info(dev, "%s at 0x%08lx, Ethernet Address %pM\n",
+		    name, board, dev->dev_addr);
+
+	return 0;
+}
+
+static int __devinit zorro8390_init_one(struct zorro_dev *z,
+					const struct zorro_device_id *ent)
+{
+	struct net_device *dev;
+	unsigned long board, ioaddr;
+	int err, i;
+
+	for (i = ARRAY_SIZE(cards) - 1; i >= 0; i--)
+		if (z->id == cards[i].id)
+			break;
+	if (i < 0)
+		return -ENODEV;
+
+	board = z->resource.start;
+	ioaddr = board + cards[i].offset;
+	dev = ____alloc_ei_netdev(0);
+	if (!dev)
+		return -ENOMEM;
+	if (!request_mem_region(ioaddr, NE_IO_EXTENT * 2, DRV_NAME)) {
+		free_netdev(dev);
+		return -EBUSY;
+	}
+	err = zorro8390_init(dev, board, cards[i].name, ZTWO_VADDR(ioaddr));
+	if (err) {
+		release_mem_region(ioaddr, NE_IO_EXTENT * 2);
+		free_netdev(dev);
+		return err;
+	}
+	zorro_set_drvdata(z, dev);
+	return 0;
+}
+
+static struct zorro_driver zorro8390_driver = {
+	.name		= "zorro8390",
+	.id_table	= zorro8390_zorro_tbl,
+	.probe		= zorro8390_init_one,
+	.remove		= __devexit_p(zorro8390_remove_one),
+};
+
+static int __init zorro8390_init_module(void)
+{
+	return zorro_register_driver(&zorro8390_driver);
+}
+
+static void __exit zorro8390_cleanup_module(void)
+{
+	zorro_unregister_driver(&zorro8390_driver);
+}
+
+module_init(zorro8390_init_module);
+module_exit(zorro8390_cleanup_module);
+
+MODULE_LICENSE("GPL");