Linux-2.6.12-rc2

Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.

Let it rip!
diff --git a/drivers/net/arm/Kconfig b/drivers/net/arm/Kconfig
new file mode 100644
index 0000000..470364d
--- /dev/null
+++ b/drivers/net/arm/Kconfig
@@ -0,0 +1,46 @@
+#
+# Acorn Network device configuration
+#  These are for Acorn's Expansion card network interfaces
+#
+config ARM_AM79C961A
+	bool "ARM EBSA110 AM79C961A support"
+	depends on NET_ETHERNET && ARM && ARCH_EBSA110
+	select CRC32
+	help
+	  If you wish to compile a kernel for the EBSA-110, then you should
+	  always answer Y to this.
+
+config ARM_ETHER1
+	tristate "Acorn Ether1 support"
+	depends on NET_ETHERNET && ARM && ARCH_ACORN
+	help
+	  If you have an Acorn system with one of these (AKA25) network cards,
+	  you should say Y to this option if you wish to use it with Linux.
+
+config ARM_ETHER3
+	tristate "Acorn/ANT Ether3 support"
+	depends on NET_ETHERNET && ARM && ARCH_ACORN
+	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 ARM_ETHERH
+	tristate "I-cubed EtherH/ANT EtherM support"
+	depends on NET_ETHERNET && 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 ARM_ETHER00
+	tristate "Altera Ether00 support"
+	depends on NET_ETHERNET && ARM && ARCH_CAMELOT
+	help
+	  This is the driver for Altera's ether00 ethernet mac IP core. Say
+	  Y here if you want to build support for this into the kernel. It
+	  is also available as a module (say M here) that can be inserted/
+	  removed from the kernel at the same time as the PLD is configured.
+	  If this driver is running on an epxa10 development board then it
+	  will generate a suitable hw address based on the board serial
+	  number (MTD support is required for this). Otherwise you will
+	  need to set a suitable hw address using ifconfig.
diff --git a/drivers/net/arm/Makefile b/drivers/net/arm/Makefile
new file mode 100644
index 0000000..b0d7068
--- /dev/null
+++ b/drivers/net/arm/Makefile
@@ -0,0 +1,10 @@
+# File: drivers/net/arm/Makefile
+#
+# Makefile for the ARM network device drivers
+#
+
+obj-$(CONFIG_ARM_AM79C961A)	+= am79c961a.o
+obj-$(CONFIG_ARM_ETHER00)	+= ether00.o
+obj-$(CONFIG_ARM_ETHERH)	+= etherh.o
+obj-$(CONFIG_ARM_ETHER3)	+= ether3.o
+obj-$(CONFIG_ARM_ETHER1)	+= ether1.o
diff --git a/drivers/net/arm/am79c961a.c b/drivers/net/arm/am79c961a.c
new file mode 100644
index 0000000..9b659e3
--- /dev/null
+++ b/drivers/net/arm/am79c961a.c
@@ -0,0 +1,750 @@
+/*
+ *  linux/drivers/net/am79c961.c
+ *
+ *  by Russell King <rmk@arm.linux.org.uk> 1995-2001.
+ *
+ * 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.
+ *
+ * Derived from various things including skeleton.c
+ *
+ * This is a special driver for the am79c961A Lance chip used in the
+ * Intel (formally Digital Equipment Corp) EBSA110 platform.  Please
+ * note that this can not be built as a module (it doesn't make sense).
+ */
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/crc32.h>
+#include <linux/bitops.h>
+
+#include <asm/system.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#define TX_BUFFERS 15
+#define RX_BUFFERS 25
+
+#include "am79c961a.h"
+
+static irqreturn_t
+am79c961_interrupt (int irq, void *dev_id, struct pt_regs *regs);
+
+static unsigned int net_debug = NET_DEBUG;
+
+static const char version[] =
+	"am79c961 ethernet driver (C) 1995-2001 Russell King v0.04\n";
+
+/* --------------------------------------------------------------------------- */
+
+#ifdef __arm__
+static void write_rreg(u_long base, u_int reg, u_int val)
+{
+	__asm__(
+	"str%?h	%1, [%2]	@ NET_RAP\n\t"
+	"str%?h	%0, [%2, #-4]	@ NET_RDP"
+	:
+	: "r" (val), "r" (reg), "r" (ISAIO_BASE + 0x0464));
+}
+
+static inline unsigned short read_rreg(u_long base_addr, u_int reg)
+{
+	unsigned short v;
+	__asm__(
+	"str%?h	%1, [%2]	@ NET_RAP\n\t"
+	"ldr%?h	%0, [%2, #-4]	@ NET_RDP"
+	: "=r" (v)
+	: "r" (reg), "r" (ISAIO_BASE + 0x0464));
+	return v;
+}
+
+static inline void write_ireg(u_long base, u_int reg, u_int val)
+{
+	__asm__(
+	"str%?h	%1, [%2]	@ NET_RAP\n\t"
+	"str%?h	%0, [%2, #8]	@ NET_IDP"
+	:
+	: "r" (val), "r" (reg), "r" (ISAIO_BASE + 0x0464));
+}
+
+static inline unsigned short read_ireg(u_long base_addr, u_int reg)
+{
+	u_short v;
+	__asm__(
+	"str%?h	%1, [%2]	@ NAT_RAP\n\t"
+	"str%?h	%0, [%2, #8]	@ NET_IDP\n\t"
+	: "=r" (v)
+	: "r" (reg), "r" (ISAIO_BASE + 0x0464));
+	return v;
+}
+
+#define am_writeword(dev,off,val) __raw_writew(val, ISAMEM_BASE + ((off) << 1))
+#define am_readword(dev,off)      __raw_readw(ISAMEM_BASE + ((off) << 1))
+
+static inline void
+am_writebuffer(struct net_device *dev, u_int offset, unsigned char *buf, unsigned int length)
+{
+	offset = ISAMEM_BASE + (offset << 1);
+	length = (length + 1) & ~1;
+	if ((int)buf & 2) {
+		__asm__ __volatile__("str%?h	%2, [%0], #4"
+		 : "=&r" (offset) : "0" (offset), "r" (buf[0] | (buf[1] << 8)));
+		buf += 2;
+		length -= 2;
+	}
+	while (length > 8) {
+		unsigned int tmp, tmp2;
+		__asm__ __volatile__(
+			"ldm%?ia	%1!, {%2, %3}\n\t"
+			"str%?h	%2, [%0], #4\n\t"
+			"mov%?	%2, %2, lsr #16\n\t"
+			"str%?h	%2, [%0], #4\n\t"
+			"str%?h	%3, [%0], #4\n\t"
+			"mov%?	%3, %3, lsr #16\n\t"
+			"str%?h	%3, [%0], #4"
+		: "=&r" (offset), "=&r" (buf), "=r" (tmp), "=r" (tmp2)
+		: "0" (offset), "1" (buf));
+		length -= 8;
+	}
+	while (length > 0) {
+		__asm__ __volatile__("str%?h	%2, [%0], #4"
+		 : "=&r" (offset) : "0" (offset), "r" (buf[0] | (buf[1] << 8)));
+		buf += 2;
+		length -= 2;
+	}
+}
+
+static inline void
+am_readbuffer(struct net_device *dev, u_int offset, unsigned char *buf, unsigned int length)
+{
+	offset = ISAMEM_BASE + (offset << 1);
+	length = (length + 1) & ~1;
+	if ((int)buf & 2) {
+		unsigned int tmp;
+		__asm__ __volatile__(
+			"ldr%?h	%2, [%0], #4\n\t"
+			"str%?b	%2, [%1], #1\n\t"
+			"mov%?	%2, %2, lsr #8\n\t"
+			"str%?b	%2, [%1], #1"
+		: "=&r" (offset), "=&r" (buf), "=r" (tmp): "0" (offset), "1" (buf));
+		length -= 2;
+	}
+	while (length > 8) {
+		unsigned int tmp, tmp2, tmp3;
+		__asm__ __volatile__(
+			"ldr%?h	%2, [%0], #4\n\t"
+			"ldr%?h	%3, [%0], #4\n\t"
+			"orr%?	%2, %2, %3, lsl #16\n\t"
+			"ldr%?h	%3, [%0], #4\n\t"
+			"ldr%?h	%4, [%0], #4\n\t"
+			"orr%?	%3, %3, %4, lsl #16\n\t"
+			"stm%?ia	%1!, {%2, %3}"
+		: "=&r" (offset), "=&r" (buf), "=r" (tmp), "=r" (tmp2), "=r" (tmp3)
+		: "0" (offset), "1" (buf));
+		length -= 8;
+	}
+	while (length > 0) {
+		unsigned int tmp;
+		__asm__ __volatile__(
+			"ldr%?h	%2, [%0], #4\n\t"
+			"str%?b	%2, [%1], #1\n\t"
+			"mov%?	%2, %2, lsr #8\n\t"
+			"str%?b	%2, [%1], #1"
+		: "=&r" (offset), "=&r" (buf), "=r" (tmp) : "0" (offset), "1" (buf));
+		length -= 2;
+	}
+}
+#else
+#error Not compatible
+#endif
+
+static int
+am79c961_ramtest(struct net_device *dev, unsigned int val)
+{
+	unsigned char *buffer = kmalloc (65536, GFP_KERNEL);
+	int i, error = 0, errorcount = 0;
+
+	if (!buffer)
+		return 0;
+	memset (buffer, val, 65536);
+	am_writebuffer(dev, 0, buffer, 65536);
+	memset (buffer, val ^ 255, 65536);
+	am_readbuffer(dev, 0, buffer, 65536);
+	for (i = 0; i < 65536; i++) {
+		if (buffer[i] != val && !error) {
+			printk ("%s: buffer error (%02X %02X) %05X - ", dev->name, val, buffer[i], i);
+			error = 1;
+			errorcount ++;
+		} else if (error && buffer[i] == val) {
+			printk ("%05X\n", i);
+			error = 0;
+		}
+	}
+	if (error)
+		printk ("10000\n");
+	kfree (buffer);
+	return errorcount;
+}
+
+static void
+am79c961_init_for_open(struct net_device *dev)
+{
+	struct dev_priv *priv = netdev_priv(dev);
+	unsigned long flags;
+	unsigned char *p;
+	u_int hdr_addr, first_free_addr;
+	int i;
+
+	/*
+	 * Stop the chip.
+	 */
+	spin_lock_irqsave(priv->chip_lock, flags);
+	write_rreg (dev->base_addr, CSR0, CSR0_BABL|CSR0_CERR|CSR0_MISS|CSR0_MERR|CSR0_TINT|CSR0_RINT|CSR0_STOP);
+	spin_unlock_irqrestore(priv->chip_lock, flags);
+
+	write_ireg (dev->base_addr, 5, 0x00a0); /* Receive address LED */
+	write_ireg (dev->base_addr, 6, 0x0081); /* Collision LED */
+	write_ireg (dev->base_addr, 7, 0x0090); /* XMIT LED */
+	write_ireg (dev->base_addr, 2, 0x0000); /* MODE register selects media */
+
+	for (i = LADRL; i <= LADRH; i++)
+		write_rreg (dev->base_addr, i, 0);
+
+	for (i = PADRL, p = dev->dev_addr; i <= PADRH; i++, p += 2)
+		write_rreg (dev->base_addr, i, p[0] | (p[1] << 8));
+
+	i = MODE_PORT_10BT;
+	if (dev->flags & IFF_PROMISC)
+		i |= MODE_PROMISC;
+
+	write_rreg (dev->base_addr, MODE, i);
+	write_rreg (dev->base_addr, POLLINT, 0);
+	write_rreg (dev->base_addr, SIZERXR, -RX_BUFFERS);
+	write_rreg (dev->base_addr, SIZETXR, -TX_BUFFERS);
+
+	first_free_addr = RX_BUFFERS * 8 + TX_BUFFERS * 8 + 16;
+	hdr_addr = 0;
+
+	priv->rxhead = 0;
+	priv->rxtail = 0;
+	priv->rxhdr = hdr_addr;
+
+	for (i = 0; i < RX_BUFFERS; i++) {
+		priv->rxbuffer[i] = first_free_addr;
+		am_writeword (dev, hdr_addr, first_free_addr);
+		am_writeword (dev, hdr_addr + 2, RMD_OWN);
+		am_writeword (dev, hdr_addr + 4, (-1600));
+		am_writeword (dev, hdr_addr + 6, 0);
+		first_free_addr += 1600;
+		hdr_addr += 8;
+	}
+	priv->txhead = 0;
+	priv->txtail = 0;
+	priv->txhdr = hdr_addr;
+	for (i = 0; i < TX_BUFFERS; i++) {
+		priv->txbuffer[i] = first_free_addr;
+		am_writeword (dev, hdr_addr, first_free_addr);
+		am_writeword (dev, hdr_addr + 2, TMD_STP|TMD_ENP);
+		am_writeword (dev, hdr_addr + 4, 0xf000);
+		am_writeword (dev, hdr_addr + 6, 0);
+		first_free_addr += 1600;
+		hdr_addr += 8;
+	}
+
+	write_rreg (dev->base_addr, BASERXL, priv->rxhdr);
+	write_rreg (dev->base_addr, BASERXH, 0);
+	write_rreg (dev->base_addr, BASETXL, priv->txhdr);
+	write_rreg (dev->base_addr, BASERXH, 0);
+	write_rreg (dev->base_addr, CSR0, CSR0_STOP);
+	write_rreg (dev->base_addr, CSR3, CSR3_IDONM|CSR3_BABLM|CSR3_DXSUFLO);
+	write_rreg (dev->base_addr, CSR4, CSR4_APAD_XMIT|CSR4_MFCOM|CSR4_RCVCCOM|CSR4_TXSTRTM|CSR4_JABM);
+	write_rreg (dev->base_addr, CSR0, CSR0_IENA|CSR0_STRT);
+}
+
+static void am79c961_timer(unsigned long data)
+{
+	struct net_device *dev = (struct net_device *)data;
+	struct dev_priv *priv = netdev_priv(dev);
+	unsigned int lnkstat, carrier;
+
+	lnkstat = read_ireg(dev->base_addr, ISALED0) & ISALED0_LNKST;
+	carrier = netif_carrier_ok(dev);
+
+	if (lnkstat && !carrier)
+		netif_carrier_on(dev);
+	else if (!lnkstat && carrier)
+		netif_carrier_off(dev);
+
+	mod_timer(&priv->timer, jiffies + 5*HZ);
+}
+
+/*
+ * Open/initialize the board.
+ */
+static int
+am79c961_open(struct net_device *dev)
+{
+	struct dev_priv *priv = netdev_priv(dev);
+	int ret;
+
+	memset (&priv->stats, 0, sizeof (priv->stats));
+
+	ret = request_irq(dev->irq, am79c961_interrupt, 0, dev->name, dev);
+	if (ret)
+		return ret;
+
+	am79c961_init_for_open(dev);
+
+	netif_carrier_off(dev);
+
+	priv->timer.expires = jiffies;
+	add_timer(&priv->timer);
+
+	netif_start_queue(dev);
+
+	return 0;
+}
+
+/*
+ * The inverse routine to am79c961_open().
+ */
+static int
+am79c961_close(struct net_device *dev)
+{
+	struct dev_priv *priv = netdev_priv(dev);
+	unsigned long flags;
+
+	del_timer_sync(&priv->timer);
+
+	netif_stop_queue(dev);
+	netif_carrier_off(dev);
+
+	spin_lock_irqsave(priv->chip_lock, flags);
+	write_rreg (dev->base_addr, CSR0, CSR0_STOP);
+	write_rreg (dev->base_addr, CSR3, CSR3_MASKALL);
+	spin_unlock_irqrestore(priv->chip_lock, flags);
+
+	free_irq (dev->irq, dev);
+
+	return 0;
+}
+
+/*
+ * Get the current statistics.
+ */
+static struct net_device_stats *am79c961_getstats (struct net_device *dev)
+{
+	struct dev_priv *priv = netdev_priv(dev);
+	return &priv->stats;
+}
+
+static void am79c961_mc_hash(struct dev_mc_list *dmi, unsigned short *hash)
+{
+	if (dmi->dmi_addrlen == ETH_ALEN && dmi->dmi_addr[0] & 0x01) {
+		int idx, bit;
+		u32 crc;
+
+		crc = ether_crc_le(ETH_ALEN, dmi->dmi_addr);
+
+		idx = crc >> 30;
+		bit = (crc >> 26) & 15;
+
+		hash[idx] |= 1 << bit;
+	}
+}
+
+/*
+ * Set or clear promiscuous/multicast mode filter for this adapter.
+ */
+static void am79c961_setmulticastlist (struct net_device *dev)
+{
+	struct dev_priv *priv = netdev_priv(dev);
+	unsigned long flags;
+	unsigned short multi_hash[4], mode;
+	int i, stopped;
+
+	mode = MODE_PORT_10BT;
+
+	if (dev->flags & IFF_PROMISC) {
+		mode |= MODE_PROMISC;
+	} else if (dev->flags & IFF_ALLMULTI) {
+		memset(multi_hash, 0xff, sizeof(multi_hash));
+	} else {
+		struct dev_mc_list *dmi;
+
+		memset(multi_hash, 0x00, sizeof(multi_hash));
+
+		for (dmi = dev->mc_list; dmi; dmi = dmi->next)
+			am79c961_mc_hash(dmi, multi_hash);
+	}
+
+	spin_lock_irqsave(priv->chip_lock, flags);
+
+	stopped = read_rreg(dev->base_addr, CSR0) & CSR0_STOP;
+
+	if (!stopped) {
+		/*
+		 * Put the chip into suspend mode
+		 */
+		write_rreg(dev->base_addr, CTRL1, CTRL1_SPND);
+
+		/*
+		 * Spin waiting for chip to report suspend mode
+		 */
+		while ((read_rreg(dev->base_addr, CTRL1) & CTRL1_SPND) == 0) {
+			spin_unlock_irqrestore(priv->chip_lock, flags);
+			nop();
+			spin_lock_irqsave(priv->chip_lock, flags);
+		}
+	}
+
+	/*
+	 * Update the multicast hash table
+	 */
+	for (i = 0; i < sizeof(multi_hash) / sizeof(multi_hash[0]); i++)
+		write_rreg(dev->base_addr, i + LADRL, multi_hash[i]);
+
+	/*
+	 * Write the mode register
+	 */
+	write_rreg(dev->base_addr, MODE, mode);
+
+	if (!stopped) {
+		/*
+		 * Put the chip back into running mode
+		 */
+		write_rreg(dev->base_addr, CTRL1, 0);
+	}
+
+	spin_unlock_irqrestore(priv->chip_lock, flags);
+}
+
+static void am79c961_timeout(struct net_device *dev)
+{
+	printk(KERN_WARNING "%s: transmit timed out, network cable problem?\n",
+		dev->name);
+
+	/*
+	 * ought to do some setup of the tx side here
+	 */
+
+	netif_wake_queue(dev);
+}
+
+/*
+ * Transmit a packet
+ */
+static int
+am79c961_sendpacket(struct sk_buff *skb, struct net_device *dev)
+{
+	struct dev_priv *priv = netdev_priv(dev);
+	unsigned int hdraddr, bufaddr;
+	unsigned int head;
+	unsigned long flags;
+
+	head = priv->txhead;
+	hdraddr = priv->txhdr + (head << 3);
+	bufaddr = priv->txbuffer[head];
+	head += 1;
+	if (head >= TX_BUFFERS)
+		head = 0;
+
+	am_writebuffer (dev, bufaddr, skb->data, skb->len);
+	am_writeword (dev, hdraddr + 4, -skb->len);
+	am_writeword (dev, hdraddr + 2, TMD_OWN|TMD_STP|TMD_ENP);
+	priv->txhead = head;
+
+	spin_lock_irqsave(priv->chip_lock, flags);
+	write_rreg (dev->base_addr, CSR0, CSR0_TDMD|CSR0_IENA);
+	dev->trans_start = jiffies;
+	spin_unlock_irqrestore(priv->chip_lock, flags);
+
+	/*
+	 * If the next packet is owned by the ethernet device,
+	 * then the tx ring is full and we can't add another
+	 * packet.
+	 */
+	if (am_readword(dev, priv->txhdr + (priv->txhead << 3) + 2) & TMD_OWN)
+		netif_stop_queue(dev);
+
+	dev_kfree_skb(skb);
+
+	return 0;
+}
+
+/*
+ * If we have a good packet(s), get it/them out of the buffers.
+ */
+static void
+am79c961_rx(struct net_device *dev, struct dev_priv *priv)
+{
+	do {
+		struct sk_buff *skb;
+		u_int hdraddr;
+		u_int pktaddr;
+		u_int status;
+		int len;
+
+		hdraddr = priv->rxhdr + (priv->rxtail << 3);
+		pktaddr = priv->rxbuffer[priv->rxtail];
+
+		status = am_readword (dev, hdraddr + 2);
+		if (status & RMD_OWN) /* do we own it? */
+			break;
+
+		priv->rxtail ++;
+		if (priv->rxtail >= RX_BUFFERS)
+			priv->rxtail = 0;
+
+		if ((status & (RMD_ERR|RMD_STP|RMD_ENP)) != (RMD_STP|RMD_ENP)) {
+			am_writeword (dev, hdraddr + 2, RMD_OWN);
+			priv->stats.rx_errors ++;
+			if (status & RMD_ERR) {
+				if (status & RMD_FRAM)
+					priv->stats.rx_frame_errors ++;
+				if (status & RMD_CRC)
+					priv->stats.rx_crc_errors ++;
+			} else if (status & RMD_STP)
+				priv->stats.rx_length_errors ++;
+			continue;
+		}
+
+		len = am_readword(dev, hdraddr + 6);
+		skb = dev_alloc_skb(len + 2);
+
+		if (skb) {
+			skb->dev = dev;
+			skb_reserve(skb, 2);
+
+			am_readbuffer(dev, pktaddr, skb_put(skb, len), len);
+			am_writeword(dev, hdraddr + 2, RMD_OWN);
+			skb->protocol = eth_type_trans(skb, dev);
+			netif_rx(skb);
+			dev->last_rx = jiffies;
+			priv->stats.rx_bytes += len;
+			priv->stats.rx_packets ++;
+		} else {
+			am_writeword (dev, hdraddr + 2, RMD_OWN);
+			printk (KERN_WARNING "%s: memory squeeze, dropping packet.\n", dev->name);
+			priv->stats.rx_dropped ++;
+			break;
+		}
+	} while (1);
+}
+
+/*
+ * Update stats for the transmitted packet
+ */
+static void
+am79c961_tx(struct net_device *dev, struct dev_priv *priv)
+{
+	do {
+		short len;
+		u_int hdraddr;
+		u_int status;
+
+		hdraddr = priv->txhdr + (priv->txtail << 3);
+		status = am_readword (dev, hdraddr + 2);
+		if (status & TMD_OWN)
+			break;
+
+		priv->txtail ++;
+		if (priv->txtail >= TX_BUFFERS)
+			priv->txtail = 0;
+
+		if (status & TMD_ERR) {
+			u_int status2;
+
+			priv->stats.tx_errors ++;
+
+			status2 = am_readword (dev, hdraddr + 6);
+
+			/*
+			 * Clear the error byte
+			 */
+			am_writeword (dev, hdraddr + 6, 0);
+
+			if (status2 & TST_RTRY)
+				priv->stats.collisions += 16;
+			if (status2 & TST_LCOL)
+				priv->stats.tx_window_errors ++;
+			if (status2 & TST_LCAR)
+				priv->stats.tx_carrier_errors ++;
+			if (status2 & TST_UFLO)
+				priv->stats.tx_fifo_errors ++;
+			continue;
+		}
+		priv->stats.tx_packets ++;
+		len = am_readword (dev, hdraddr + 4);
+		priv->stats.tx_bytes += -len;
+	} while (priv->txtail != priv->txhead);
+
+	netif_wake_queue(dev);
+}
+
+static irqreturn_t
+am79c961_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct net_device *dev = (struct net_device *)dev_id;
+	struct dev_priv *priv = netdev_priv(dev);
+	u_int status, n = 100;
+	int handled = 0;
+
+	do {
+		status = read_rreg(dev->base_addr, CSR0);
+		write_rreg(dev->base_addr, CSR0, status &
+			   (CSR0_IENA|CSR0_TINT|CSR0_RINT|
+			    CSR0_MERR|CSR0_MISS|CSR0_CERR|CSR0_BABL));
+
+		if (status & CSR0_RINT) {
+			handled = 1;
+			am79c961_rx(dev, priv);
+		}
+		if (status & CSR0_TINT) {
+			handled = 1;
+			am79c961_tx(dev, priv);
+		}
+		if (status & CSR0_MISS) {
+			handled = 1;
+			priv->stats.rx_dropped ++;
+		}
+		if (status & CSR0_CERR) {
+			handled = 1;
+			mod_timer(&priv->timer, jiffies);
+		}
+	} while (--n && status & (CSR0_RINT | CSR0_TINT));
+
+	return IRQ_RETVAL(handled);
+}
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void am79c961_poll_controller(struct net_device *dev)
+{
+	unsigned long flags;
+	local_irq_save(flags);
+	am79c961_interrupt(dev->irq, dev, NULL);
+	local_irq_restore(flags);
+}
+#endif
+
+/*
+ * Initialise the chip.  Note that we always expect
+ * to be entered with interrupts enabled.
+ */
+static int
+am79c961_hw_init(struct net_device *dev)
+{
+	struct dev_priv *priv = netdev_priv(dev);
+
+	spin_lock_irq(&priv->chip_lock);
+	write_rreg (dev->base_addr, CSR0, CSR0_STOP);
+	write_rreg (dev->base_addr, CSR3, CSR3_MASKALL);
+	spin_unlock_irq(&priv->chip_lock);
+
+	am79c961_ramtest(dev, 0x66);
+	am79c961_ramtest(dev, 0x99);
+
+	return 0;
+}
+
+static void __init am79c961_banner(void)
+{
+	static unsigned version_printed;
+
+	if (net_debug && version_printed++ == 0)
+		printk(KERN_INFO "%s", version);
+}
+
+static int __init am79c961_init(void)
+{
+	struct net_device *dev;
+	struct dev_priv *priv;
+	int i, ret;
+
+	dev = alloc_etherdev(sizeof(struct dev_priv));
+	ret = -ENOMEM;
+	if (!dev)
+		goto out;
+
+	priv = netdev_priv(dev);
+
+	/*
+	 * Fixed address and IRQ lines here.
+	 * The PNP initialisation should have been
+	 * done by the ether bootp loader.
+	 */
+	dev->base_addr = 0x220;
+	dev->irq = IRQ_EBSA110_ETHERNET;
+
+    	ret = -ENODEV;
+	if (!request_region(dev->base_addr, 0x18, dev->name))
+		goto nodev;
+
+	/*
+	 * Reset the device.
+	 */
+	inb(dev->base_addr + NET_RESET);
+	udelay(5);
+
+	/*
+	 * Check the manufacturer part of the
+	 * ether address.
+	 */
+	if (inb(dev->base_addr) != 0x08 ||
+	    inb(dev->base_addr + 2) != 0x00 ||
+	    inb(dev->base_addr + 4) != 0x2b)
+	    	goto release;
+
+	am79c961_banner();
+	printk(KERN_INFO "%s: ether address ", dev->name);
+
+	/* Retrive and print the ethernet address. */
+	for (i = 0; i < 6; i++) {
+		dev->dev_addr[i] = inb(dev->base_addr + i * 2) & 0xff;
+		printk (i == 5 ? "%02x\n" : "%02x:", dev->dev_addr[i]);
+	}
+
+	spin_lock_init(&priv->chip_lock);
+	init_timer(&priv->timer);
+	priv->timer.data = (unsigned long)dev;
+	priv->timer.function = am79c961_timer;
+
+	if (am79c961_hw_init(dev))
+		goto release;
+
+	dev->open		= am79c961_open;
+	dev->stop		= am79c961_close;
+	dev->hard_start_xmit	= am79c961_sendpacket;
+	dev->get_stats		= am79c961_getstats;
+	dev->set_multicast_list	= am79c961_setmulticastlist;
+	dev->tx_timeout		= am79c961_timeout;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	dev->poll_controller	= am79c961_poll_controller;
+#endif
+
+	ret = register_netdev(dev);
+	if (ret == 0)
+		return 0;
+
+release:
+	release_region(dev->base_addr, 0x18);
+nodev:
+	free_netdev(dev);
+out:
+	return ret;
+}
+
+__initcall(am79c961_init);
diff --git a/drivers/net/arm/am79c961a.h b/drivers/net/arm/am79c961a.h
new file mode 100644
index 0000000..1e9b050
--- /dev/null
+++ b/drivers/net/arm/am79c961a.h
@@ -0,0 +1,148 @@
+/*
+ * linux/drivers/net/am79c961.h
+ *
+ * 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.
+ */
+
+#ifndef _LINUX_am79c961a_H
+#define _LINUX_am79c961a_H
+
+/* use 0 for production, 1 for verification, >2 for debug. debug flags: */
+#define DEBUG_TX	 2
+#define DEBUG_RX	 4
+#define DEBUG_INT	 8
+#define DEBUG_IC	16
+#ifndef NET_DEBUG
+#define NET_DEBUG 	0
+#endif
+
+#define NET_UID		0
+#define NET_RDP		0x10
+#define NET_RAP		0x12
+#define NET_RESET	0x14
+#define NET_IDP		0x16
+
+/*
+ * RAP registers
+ */
+#define CSR0		0
+#define CSR0_INIT	0x0001
+#define CSR0_STRT	0x0002
+#define CSR0_STOP	0x0004
+#define CSR0_TDMD	0x0008
+#define CSR0_TXON	0x0010
+#define CSR0_RXON	0x0020
+#define CSR0_IENA	0x0040
+#define CSR0_INTR	0x0080
+#define CSR0_IDON	0x0100
+#define CSR0_TINT	0x0200
+#define CSR0_RINT	0x0400
+#define CSR0_MERR	0x0800
+#define CSR0_MISS	0x1000
+#define CSR0_CERR	0x2000
+#define CSR0_BABL	0x4000
+#define CSR0_ERR	0x8000
+
+#define CSR3		3
+#define CSR3_EMBA	0x0008
+#define CSR3_DXMT2PD	0x0010
+#define CSR3_LAPPEN	0x0020
+#define CSR3_DXSUFLO	0x0040
+#define CSR3_IDONM	0x0100
+#define CSR3_TINTM	0x0200
+#define CSR3_RINTM	0x0400
+#define CSR3_MERRM	0x0800
+#define CSR3_MISSM	0x1000
+#define CSR3_BABLM	0x4000
+#define CSR3_MASKALL	0x5F00
+
+#define CSR4		4
+#define CSR4_JABM	0x0001
+#define CSR4_JAB	0x0002
+#define CSR4_TXSTRTM	0x0004
+#define CSR4_TXSTRT	0x0008
+#define CSR4_RCVCCOM	0x0010
+#define CSR4_RCVCCO	0x0020
+#define CSR4_MFCOM	0x0100
+#define CSR4_MFCO	0x0200
+#define CSR4_ASTRP_RCV	0x0400
+#define CSR4_APAD_XMIT	0x0800
+
+#define CTRL1		5
+#define CTRL1_SPND	0x0001
+
+#define LADRL		8
+#define LADRM1		9
+#define LADRM2		10
+#define LADRH		11
+#define PADRL		12
+#define PADRM		13
+#define PADRH		14
+
+#define MODE		15
+#define MODE_DISRX	0x0001
+#define MODE_DISTX	0x0002
+#define MODE_LOOP	0x0004
+#define MODE_DTCRC	0x0008
+#define MODE_COLL	0x0010
+#define MODE_DRETRY	0x0020
+#define MODE_INTLOOP	0x0040
+#define MODE_PORT_AUI	0x0000
+#define MODE_PORT_10BT	0x0080
+#define MODE_DRXPA	0x2000
+#define MODE_DRXBA	0x4000
+#define MODE_PROMISC	0x8000
+
+#define BASERXL		24
+#define BASERXH		25
+#define BASETXL		30
+#define BASETXH		31
+
+#define POLLINT		47
+
+#define SIZERXR		76
+#define SIZETXR		78
+
+#define CSR_MFC		112
+
+#define RMD_ENP		0x0100
+#define RMD_STP		0x0200
+#define RMD_CRC		0x0800
+#define RMD_FRAM	0x2000
+#define RMD_ERR		0x4000
+#define RMD_OWN		0x8000
+
+#define TMD_ENP		0x0100
+#define TMD_STP		0x0200
+#define TMD_MORE	0x1000
+#define TMD_ERR		0x4000
+#define TMD_OWN		0x8000
+
+#define TST_RTRY	0x0400
+#define TST_LCAR	0x0800
+#define TST_LCOL	0x1000
+#define TST_UFLO	0x4000
+#define TST_BUFF	0x8000
+
+#define ISALED0		0x0004
+#define ISALED0_LNKST	0x8000
+
+struct dev_priv {
+    struct net_device_stats stats;
+    unsigned long	rxbuffer[RX_BUFFERS];
+    unsigned long	txbuffer[TX_BUFFERS];
+    unsigned char	txhead;
+    unsigned char	txtail;
+    unsigned char	rxhead;
+    unsigned char	rxtail;
+    unsigned long	rxhdr;
+    unsigned long	txhdr;
+    spinlock_t		chip_lock;
+    struct timer_list	timer;
+};
+
+extern int	am79c961_probe (struct net_device *dev);
+
+#endif
diff --git a/drivers/net/arm/ether00.c b/drivers/net/arm/ether00.c
new file mode 100644
index 0000000..4f1f4e3
--- /dev/null
+++ b/drivers/net/arm/ether00.c
@@ -0,0 +1,1017 @@
+/*
+ *  drivers/net/ether00.c
+ *
+ *  Copyright (C) 2001 Altera Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/* includes */
+#include <linux/config.h>
+#include <linux/pci.h>
+#include <linux/sched.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <linux/etherdevice.h>
+#include <linux/module.h>
+#include <linux/tqueue.h>
+#include <linux/mtd/mtd.h>
+#include <linux/pld/pld_hotswap.h>
+#include <asm/arch/excalibur.h>
+#include <asm/arch/hardware.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+#include <asm/sizes.h>
+
+#include <asm/arch/ether00.h>
+#include <asm/arch/tdkphy.h>
+
+
+MODULE_AUTHOR("Clive Davies");
+MODULE_DESCRIPTION("Altera Ether00 IP core driver");
+MODULE_LICENSE("GPL");
+
+#define PKT_BUF_SZ 1540 /* Size of each rx buffer */
+#define ETH_NR 4 /* Number of MACs this driver supports */
+
+#define DEBUG(x)
+
+#define __dma_va(x) (unsigned int)((unsigned int)priv->dma_data+(((unsigned int)(x))&(EXC_SPSRAM_BLOCK0_SIZE-1)))
+#define __dma_pa(x) (unsigned int)(EXC_SPSRAM_BLOCK0_BASE+(((unsigned int)(x))-(unsigned int)priv->dma_data))
+
+#define ETHER00_BASE	0
+#define	ETHER00_TYPE
+#define ETHER00_NAME "ether00"
+#define MAC_REG_SIZE 0x400 /* size of MAC register area */
+
+
+
+/* typedefs */
+
+/* The definition of the driver control structure */
+
+#define RX_NUM_BUFF     10
+#define RX_NUM_FDESC    10
+#define TX_NUM_FDESC    10
+
+struct tx_fda_ent{
+	FDA_DESC  fd;
+	BUF_DESC  bd;
+	BUF_DESC  pad;
+};
+struct rx_fda_ent{
+	FDA_DESC  fd;
+	BUF_DESC  bd;
+	BUF_DESC  pad;
+};
+struct rx_blist_ent{
+	FDA_DESC  fd;
+	BUF_DESC  bd;
+	BUF_DESC  pad;
+};
+struct net_priv
+{
+	struct net_device_stats stats;
+	struct sk_buff* skb;
+	void* dma_data;
+	struct rx_blist_ent*  rx_blist_vp;
+	struct rx_fda_ent* rx_fda_ptr;
+	struct tx_fda_ent* tx_fdalist_vp;
+	struct tq_struct  tq_memupdate;
+	unsigned char   memupdate_scheduled;
+	unsigned char   rx_disabled;
+	unsigned char   queue_stopped;
+	spinlock_t rx_lock;
+};
+
+static const char vendor_id[2]={0x07,0xed};
+
+#ifdef ETHER00_DEBUG
+
+/* Dump (most) registers for debugging puposes */
+
+static void dump_regs(struct net_device *dev){
+	struct net_priv* priv=dev->priv;
+	unsigned int* i;
+
+	printk("\n RX free descriptor area:\n");
+
+	for(i=(unsigned int*)priv->rx_fda_ptr;
+	    i<((unsigned int*)(priv->rx_fda_ptr+RX_NUM_FDESC));){
+		printk("%#8x %#8x %#8x %#8x\n",*i,*(i+1),*(i+2),*(i+3));
+		i+=4;
+	}
+
+	printk("\n RX buffer list:\n");
+
+	for(i=(unsigned int*)priv->rx_blist_vp;
+	    i<((unsigned int*)(priv->rx_blist_vp+RX_NUM_BUFF));){
+		printk("%#8x %#8x %#8x %#8x\n",*i,*(i+1),*(i+2),*(i+3));
+		i+=4;
+	}
+
+	printk("\n TX frame descriptor list:\n");
+
+	for(i=(unsigned int*)priv->tx_fdalist_vp;
+	    i<((unsigned int*)(priv->tx_fdalist_vp+TX_NUM_FDESC));){
+		printk("%#8x %#8x %#8x %#8x\n",*i,*(i+1),*(i+2),*(i+3));
+		i+=4;
+	}
+
+	printk("\ndma ctl=%#x\n",readw(ETHER_DMA_CTL(dev->base_addr)));
+	printk("txfrmptr=%#x\n",readw(ETHER_TXFRMPTR(dev->base_addr)));
+	printk("txthrsh=%#x\n",readw(ETHER_TXTHRSH(dev->base_addr)));
+	printk("txpollctr=%#x\n",readw(ETHER_TXPOLLCTR(dev->base_addr)));
+	printk("blfrmptr=%#x\n",readw(ETHER_BLFRMPTR(dev->base_addr)));
+	printk("rxfragsize=%#x\n",readw(ETHER_RXFRAGSIZE(dev->base_addr)));
+	printk("tx_int_en=%#x\n",readw(ETHER_INT_EN(dev->base_addr)));
+	printk("fda_bas=%#x\n",readw(ETHER_FDA_BAS(dev->base_addr)));
+	printk("fda_lim=%#x\n",readw(ETHER_FDA_LIM(dev->base_addr)));
+	printk("int_src=%#x\n",readw(ETHER_INT_SRC(dev->base_addr)));
+	printk("pausecnt=%#x\n",readw(ETHER_PAUSECNT(dev->base_addr)));
+	printk("rempaucnt=%#x\n",readw(ETHER_REMPAUCNT(dev->base_addr)));
+	printk("txconfrmstat=%#x\n",readw(ETHER_TXCONFRMSTAT(dev->base_addr)));
+	printk("mac_ctl=%#x\n",readw(ETHER_MAC_CTL(dev->base_addr)));
+	printk("arc_ctl=%#x\n",readw(ETHER_ARC_CTL(dev->base_addr)));
+	printk("tx_ctl=%#x\n",readw(ETHER_TX_CTL(dev->base_addr)));
+}
+#endif /* ETHER00_DEBUG */
+
+
+static int ether00_write_phy(struct net_device *dev, short address, short value)
+{
+	volatile int count = 1024;
+	writew(value,ETHER_MD_DATA(dev->base_addr));
+	writew( ETHER_MD_CA_BUSY_MSK |
+		ETHER_MD_CA_WR_MSK |
+		(address & ETHER_MD_CA_ADDR_MSK),
+		ETHER_MD_CA(dev->base_addr));
+
+	/* Wait for the command to complete */
+	while((readw(ETHER_MD_CA(dev->base_addr)) & ETHER_MD_CA_BUSY_MSK)&&count){
+		count--;
+	}
+	if (!count){
+		printk("Write to phy failed, addr=%#x, data=%#x\n",address, value);
+		return -EIO;
+	}
+	return 0;
+}
+
+static int ether00_read_phy(struct net_device *dev, short address)
+{
+	volatile int count = 1024;
+	writew( ETHER_MD_CA_BUSY_MSK |
+		(address & ETHER_MD_CA_ADDR_MSK),
+		ETHER_MD_CA(dev->base_addr));
+
+	/* Wait for the command to complete */
+	while((readw(ETHER_MD_CA(dev->base_addr)) & ETHER_MD_CA_BUSY_MSK)&&count){
+		count--;
+	}
+	if (!count){
+		printk(KERN_WARNING "Read from phy timed out\n");
+		return -EIO;
+	}
+	return readw(ETHER_MD_DATA(dev->base_addr));
+}
+
+static void ether00_phy_int(int irq_num, void* dev_id, struct pt_regs* regs)
+{
+	struct net_device* dev=dev_id;
+	int irq_status;
+
+	irq_status=ether00_read_phy(dev, PHY_IRQ_CONTROL);
+
+	if(irq_status & PHY_IRQ_CONTROL_ANEG_COMP_INT_MSK){
+		/*
+		 * Autonegotiation complete on epxa10db. The mac doesn't
+		 * twig if we're in full duplex so we need to check the
+		 * phy status register and configure the mac accordingly
+		 */
+		if(ether00_read_phy(dev, PHY_STATUS)&(PHY_STATUS_10T_F_MSK|PHY_STATUS_100_X_F_MSK)){
+			int tmp;
+			tmp=readl(ETHER_MAC_CTL(dev->base_addr));
+			writel(tmp|ETHER_MAC_CTL_FULLDUP_MSK,ETHER_MAC_CTL(dev->base_addr));
+		}
+	}
+
+	if(irq_status&PHY_IRQ_CONTROL_LS_CHG_INT_MSK){
+
+		if(ether00_read_phy(dev, PHY_STATUS)& PHY_STATUS_LINK_MSK){
+			/* Link is up */
+			netif_carrier_on(dev);
+			//printk("Carrier on\n");
+		}else{
+			netif_carrier_off(dev);
+			//printk("Carrier off\n");
+
+		}
+	}
+
+}
+
+static void setup_blist_entry(struct sk_buff* skb,struct rx_blist_ent* blist_ent_ptr){
+	/* Make the buffer consistent with the cache as the mac is going to write
+	 * directly into it*/
+	blist_ent_ptr->fd.FDSystem=(unsigned int)skb;
+	blist_ent_ptr->bd.BuffData=(char*)__pa(skb->data);
+	consistent_sync(skb->data,PKT_BUF_SZ,PCI_DMA_FROMDEVICE);
+        /* align IP on 16 Byte (DMA_CTL set to skip 2 bytes) */
+	skb_reserve(skb,2);
+	blist_ent_ptr->bd.BuffLength=PKT_BUF_SZ-2;
+	blist_ent_ptr->fd.FDLength=1;
+	blist_ent_ptr->fd.FDCtl=FDCTL_COWNSFD_MSK;
+	blist_ent_ptr->bd.BDCtl=BDCTL_COWNSBD_MSK;
+}
+
+
+static int ether00_mem_init(struct net_device* dev)
+{
+	struct net_priv* priv=dev->priv;
+	struct tx_fda_ent *tx_fd_ptr,*tx_end_ptr;
+	struct rx_blist_ent* blist_ent_ptr;
+	int i;
+
+	/*
+	 * Grab a block of on chip SRAM to contain the control stuctures for
+	 * the ethernet MAC. This uncached becuase it needs to be accesses by both
+	 * bus masters (cpu + mac). However, it shouldn't matter too much in terms
+	 * of speed as its on chip memory
+	 */
+	priv->dma_data=ioremap_nocache(EXC_SPSRAM_BLOCK0_BASE,EXC_SPSRAM_BLOCK0_SIZE );
+	if (!priv->dma_data)
+		return -ENOMEM;
+
+	priv->rx_fda_ptr=(struct rx_fda_ent*)priv->dma_data;
+	/*
+	 * Now share it out amongst the Frame descriptors and the buffer list
+	 */
+	priv->rx_blist_vp=(struct rx_blist_ent*)((unsigned int)priv->dma_data+RX_NUM_FDESC*sizeof(struct rx_fda_ent));
+
+	/*
+	 *Initalise the FDA list
+	 */
+	/* set ownership to the controller */
+	memset(priv->rx_fda_ptr,0x80,RX_NUM_FDESC*sizeof(struct rx_fda_ent));
+
+	/*
+	 *Initialise the buffer list
+	 */
+	blist_ent_ptr=priv->rx_blist_vp;
+	i=0;
+	while(blist_ent_ptr<(priv->rx_blist_vp+RX_NUM_BUFF)){
+		struct sk_buff *skb;
+		blist_ent_ptr->fd.FDLength=1;
+		skb=dev_alloc_skb(PKT_BUF_SZ);
+		if(skb){
+			setup_blist_entry(skb,blist_ent_ptr);
+			blist_ent_ptr->fd.FDNext=(FDA_DESC*)__dma_pa(blist_ent_ptr+1);
+			blist_ent_ptr->bd.BDStat=i++;
+			blist_ent_ptr++;
+		}
+		else
+		{
+			printk("Failed to initalise buffer list\n");
+		}
+
+	}
+	blist_ent_ptr--;
+	blist_ent_ptr->fd.FDNext=(FDA_DESC*)__dma_pa(priv->rx_blist_vp);
+
+	priv->tx_fdalist_vp=(struct tx_fda_ent*)(priv->rx_blist_vp+RX_NUM_BUFF);
+
+	/* Initialise the buffers to be a circular list. The mac will then go poll
+	 * the list until it finds a frame ready to transmit */
+	tx_end_ptr=priv->tx_fdalist_vp+TX_NUM_FDESC;
+	for(tx_fd_ptr=priv->tx_fdalist_vp;tx_fd_ptr<tx_end_ptr;tx_fd_ptr++){
+		tx_fd_ptr->fd.FDNext=(FDA_DESC*)__dma_pa((tx_fd_ptr+1));
+		tx_fd_ptr->fd.FDCtl=1;
+		tx_fd_ptr->fd.FDStat=0;
+		tx_fd_ptr->fd.FDLength=1;
+
+	}
+	/* Change the last FDNext pointer to make a circular list */
+	tx_fd_ptr--;
+	tx_fd_ptr->fd.FDNext=(FDA_DESC*)__dma_pa(priv->tx_fdalist_vp);
+
+	/* Point the device at the chain of Rx and Tx Buffers */
+	writel((unsigned int)__dma_pa(priv->rx_fda_ptr),ETHER_FDA_BAS(dev->base_addr));
+	writel((RX_NUM_FDESC-1)*sizeof(struct rx_fda_ent),ETHER_FDA_LIM(dev->base_addr));
+	writel((unsigned int)__dma_pa(priv->rx_blist_vp),ETHER_BLFRMPTR(dev->base_addr));
+
+	writel((unsigned int)__dma_pa(priv->tx_fdalist_vp),ETHER_TXFRMPTR(dev->base_addr));
+
+	return 0;
+}
+
+
+void ether00_mem_update(void* dev_id)
+{
+	struct net_device* dev=dev_id;
+	struct net_priv* priv=dev->priv;
+	struct sk_buff* skb;
+	struct tx_fda_ent *fda_ptr=priv->tx_fdalist_vp;
+	struct rx_blist_ent* blist_ent_ptr;
+	unsigned long flags;
+
+	priv->tq_memupdate.sync=0;
+	//priv->tq_memupdate.list=
+	priv->memupdate_scheduled=0;
+
+	/* Transmit interrupt */
+	while(fda_ptr<(priv->tx_fdalist_vp+TX_NUM_FDESC)){
+		if(!(FDCTL_COWNSFD_MSK&fda_ptr->fd.FDCtl) && (ETHER_TX_STAT_COMP_MSK&fda_ptr->fd.FDStat)){
+			priv->stats.tx_packets++;
+			priv->stats.tx_bytes+=fda_ptr->bd.BuffLength;
+			skb=(struct sk_buff*)fda_ptr->fd.FDSystem;
+			//printk("%d:txcln:fda=%#x skb=%#x\n",jiffies,fda_ptr,skb);
+			dev_kfree_skb(skb);
+			fda_ptr->fd.FDSystem=0;
+			fda_ptr->fd.FDStat=0;
+			fda_ptr->fd.FDCtl=0;
+		}
+		fda_ptr++;
+	}
+	/* Fill in any missing buffers from the received queue */
+	spin_lock_irqsave(&priv->rx_lock,flags);
+	blist_ent_ptr=priv->rx_blist_vp;
+	while(blist_ent_ptr<(priv->rx_blist_vp+RX_NUM_BUFF)){
+		/* fd.FDSystem of 0 indicates we failed to allocate the buffer in the ISR */
+		if(!blist_ent_ptr->fd.FDSystem){
+			struct sk_buff *skb;
+			skb=dev_alloc_skb(PKT_BUF_SZ);
+			blist_ent_ptr->fd.FDSystem=(unsigned int)skb;
+			if(skb){
+				setup_blist_entry(skb,blist_ent_ptr);
+			}
+			else
+			{
+				break;
+			}
+		}
+		blist_ent_ptr++;
+	}
+	spin_unlock_irqrestore(&priv->rx_lock,flags);
+	if(priv->queue_stopped){
+		//printk("%d:cln:start q\n",jiffies);
+		netif_start_queue(dev);
+	}
+	if(priv->rx_disabled){
+		//printk("%d:enable_irq\n",jiffies);
+		priv->rx_disabled=0;
+		writel(ETHER_RX_CTL_RXEN_MSK,ETHER_RX_CTL(dev->base_addr));
+
+	}
+}
+
+
+static void ether00_int( int irq_num, void* dev_id, struct pt_regs* regs)
+{
+	struct net_device* dev=dev_id;
+	struct net_priv* priv=dev->priv;
+
+	unsigned int   interruptValue;
+
+	interruptValue=readl(ETHER_INT_SRC(dev->base_addr));
+
+	//printk("INT_SRC=%x\n",interruptValue);
+
+	if(!(readl(ETHER_INT_SRC(dev->base_addr)) & ETHER_INT_SRC_IRQ_MSK))
+	{
+		return;		/* Interrupt wasn't caused by us!! */
+	}
+
+	if(readl(ETHER_INT_SRC(dev->base_addr))&
+	   (ETHER_INT_SRC_INTMACRX_MSK |
+	    ETHER_INT_SRC_FDAEX_MSK |
+	    ETHER_INT_SRC_BLEX_MSK)) {
+		struct rx_blist_ent* blist_ent_ptr;
+		struct rx_fda_ent* fda_ent_ptr;
+		struct sk_buff* skb;
+
+		fda_ent_ptr=priv->rx_fda_ptr;
+		spin_lock(&priv->rx_lock);
+		while(fda_ent_ptr<(priv->rx_fda_ptr+RX_NUM_FDESC)){
+			int result;
+
+			if(!(fda_ent_ptr->fd.FDCtl&FDCTL_COWNSFD_MSK))
+			{
+				/* This frame is ready for processing */
+				/*find the corresponding buffer in the bufferlist */
+				blist_ent_ptr=priv->rx_blist_vp+fda_ent_ptr->bd.BDStat;
+				skb=(struct sk_buff*)blist_ent_ptr->fd.FDSystem;
+
+				/* Pass this skb up the stack */
+				skb->dev=dev;
+				skb_put(skb,fda_ent_ptr->fd.FDLength);
+				skb->protocol=eth_type_trans(skb,dev);
+				skb->ip_summed=CHECKSUM_UNNECESSARY;
+				result=netif_rx(skb);
+				/* Update statistics */
+				priv->stats.rx_packets++;
+				priv->stats.rx_bytes+=fda_ent_ptr->fd.FDLength;
+
+				/* Free the FDA entry */
+				fda_ent_ptr->bd.BDStat=0xff;
+				fda_ent_ptr->fd.FDCtl=FDCTL_COWNSFD_MSK;
+
+				/* Allocate a new skb and point the bd entry to it */
+				blist_ent_ptr->fd.FDSystem=0;
+				skb=dev_alloc_skb(PKT_BUF_SZ);
+				//printk("allocskb=%#x\n",skb);
+				if(skb){
+					setup_blist_entry(skb,blist_ent_ptr);
+
+				}
+				else if(!priv->memupdate_scheduled){
+					int tmp;
+					/* There are no buffers at the moment, so schedule */
+					/* the background task to sort this out */
+					schedule_task(&priv->tq_memupdate);
+					priv->memupdate_scheduled=1;
+					printk(KERN_DEBUG "%s:No buffers",dev->name);
+					/* If this interrupt was due to a lack of buffers then
+					 * we'd better stop the receiver too */
+					if(interruptValue&ETHER_INT_SRC_BLEX_MSK){
+						priv->rx_disabled=1;
+						tmp=readl(ETHER_INT_SRC(dev->base_addr));
+						writel(tmp&~ETHER_RX_CTL_RXEN_MSK,ETHER_RX_CTL(dev->base_addr));
+						printk(KERN_DEBUG "%s:Halting rx",dev->name);
+					}
+
+				}
+
+			}
+			fda_ent_ptr++;
+		}
+		spin_unlock(&priv->rx_lock);
+
+		/* Clear the  interrupts */
+		writel(ETHER_INT_SRC_INTMACRX_MSK | ETHER_INT_SRC_FDAEX_MSK
+		       | ETHER_INT_SRC_BLEX_MSK,ETHER_INT_SRC(dev->base_addr));
+
+	}
+
+	if(readl(ETHER_INT_SRC(dev->base_addr))&ETHER_INT_SRC_INTMACTX_MSK){
+
+		if(!priv->memupdate_scheduled){
+			schedule_task(&priv->tq_memupdate);
+			priv->memupdate_scheduled=1;
+		}
+		/* Clear the interrupt */
+		writel(ETHER_INT_SRC_INTMACTX_MSK,ETHER_INT_SRC(dev->base_addr));
+	}
+
+	if (readl(ETHER_INT_SRC(dev->base_addr)) & (ETHER_INT_SRC_SWINT_MSK|
+						    ETHER_INT_SRC_INTEARNOT_MSK|
+						    ETHER_INT_SRC_INTLINK_MSK|
+						    ETHER_INT_SRC_INTEXBD_MSK|
+						    ETHER_INT_SRC_INTTXCTLCMP_MSK))
+	{
+		/*
+		 *	Not using any of these so they shouldn't happen
+		 *
+		 *	In the cased of INTEXBD - if you allocate more
+		 *      than 28 decsriptors you may need to think about this
+		 */
+		printk("Not using this interrupt\n");
+	}
+
+	if (readl(ETHER_INT_SRC(dev->base_addr)) &
+	    (ETHER_INT_SRC_INTSBUS_MSK |
+	     ETHER_INT_SRC_INTNRABT_MSK
+	     |ETHER_INT_SRC_DMPARERR_MSK))
+	{
+		/*
+		 * Hardware errors, we can either ignore them and hope they go away
+		 *or reset the device, I'll try the first for now to see if they happen
+		 */
+		printk("Hardware error\n");
+	}
+}
+
+static void ether00_setup_ethernet_address(struct net_device* dev)
+{
+	int tmp;
+
+	dev->addr_len=6;
+	writew(0,ETHER_ARC_ADR(dev->base_addr));
+	writel((dev->dev_addr[0]<<24) |
+		(dev->dev_addr[1]<<16) |
+		(dev->dev_addr[2]<<8) |
+		dev->dev_addr[3],
+		ETHER_ARC_DATA(dev->base_addr));
+
+	writew(4,ETHER_ARC_ADR(dev->base_addr));
+	tmp=readl(ETHER_ARC_DATA(dev->base_addr));
+	tmp&=0xffff;
+	tmp|=(dev->dev_addr[4]<<24) | (dev->dev_addr[5]<<16);
+	writel(tmp, ETHER_ARC_DATA(dev->base_addr));
+	/* Enable this entry in the ARC */
+
+	writel(1,ETHER_ARC_ENA(dev->base_addr));
+
+	return;
+}
+
+
+static void ether00_reset(struct net_device *dev)
+{
+	/* reset the controller */
+	writew(ETHER_MAC_CTL_RESET_MSK,ETHER_MAC_CTL(dev->base_addr));
+
+	/*
+	 * Make sure we're not going to send anything
+	 */
+
+	writew(ETHER_TX_CTL_TXHALT_MSK,ETHER_TX_CTL(dev->base_addr));
+
+	/*
+	 * Make sure we're not going to receive anything
+	 */
+	writew(ETHER_RX_CTL_RXHALT_MSK,ETHER_RX_CTL(dev->base_addr));
+
+	/*
+	 * Disable Interrupts for now, and set the burst size to 8 bytes
+	 */
+
+	writel(ETHER_DMA_CTL_INTMASK_MSK |
+	       ((8 << ETHER_DMA_CTL_DMBURST_OFST) & ETHER_DMA_CTL_DMBURST_MSK)
+	       |(2<<ETHER_DMA_CTL_RXALIGN_OFST),
+	       ETHER_DMA_CTL(dev->base_addr));
+
+
+	/*
+	 * Set TxThrsh - start transmitting a packet after 1514
+	 * bytes or when a packet is complete, whichever comes first
+	 */
+	 writew(1514,ETHER_TXTHRSH(dev->base_addr));
+
+	/*
+	 * Set TxPollCtr.  Each cycle is
+	 * 61.44 microseconds with a 33 MHz bus
+	 */
+	 writew(1,ETHER_TXPOLLCTR(dev->base_addr));
+
+	/*
+	 * Set Rx_Ctl - Turn off reception and let RxData turn it
+	 * on later
+	 */
+	 writew(ETHER_RX_CTL_RXHALT_MSK,ETHER_RX_CTL(dev->base_addr));
+
+}
+
+
+static void ether00_set_multicast(struct net_device* dev)
+{
+	int count=dev->mc_count;
+
+	/* Set promiscuous mode if it's asked for. */
+
+	if (dev->flags&IFF_PROMISC){
+
+		writew( ETHER_ARC_CTL_COMPEN_MSK |
+			ETHER_ARC_CTL_BROADACC_MSK |
+			ETHER_ARC_CTL_GROUPACC_MSK |
+			ETHER_ARC_CTL_STATIONACC_MSK,
+			ETHER_ARC_CTL(dev->base_addr));
+		return;
+	}
+
+	/*
+	 * Get all multicast packets if required, or if there are too
+	 * many addresses to fit in hardware
+	 */
+	if (dev->flags & IFF_ALLMULTI){
+		writew( ETHER_ARC_CTL_COMPEN_MSK |
+			ETHER_ARC_CTL_GROUPACC_MSK |
+			ETHER_ARC_CTL_BROADACC_MSK,
+			ETHER_ARC_CTL(dev->base_addr));
+		return;
+	}
+	if (dev->mc_count > (ETHER_ARC_SIZE - 1)){
+
+		printk(KERN_WARNING "Too many multicast addresses for hardware to filter - receiving all multicast packets\n");
+		writew( ETHER_ARC_CTL_COMPEN_MSK |
+			ETHER_ARC_CTL_GROUPACC_MSK |
+			ETHER_ARC_CTL_BROADACC_MSK,
+			ETHER_ARC_CTL(dev->base_addr));
+		return;
+	}
+
+	if(dev->mc_count){
+		struct dev_mc_list *mc_list_ent=dev->mc_list;
+		unsigned int temp,i;
+		DEBUG(printk("mc_count=%d mc_list=%#x\n",dev-> mc_count, dev->mc_list));
+		DEBUG(printk("mc addr=%02#x%02x%02x%02x%02x%02x\n",
+			     mc_list_ent->dmi_addr[5],
+			     mc_list_ent->dmi_addr[4],
+			     mc_list_ent->dmi_addr[3],
+			     mc_list_ent->dmi_addr[2],
+			     mc_list_ent->dmi_addr[1],
+			     mc_list_ent->dmi_addr[0]);)
+
+		/*
+		 * The first 6 bytes are the MAC address, so
+		 * don't change them!
+		 */
+		writew(4,ETHER_ARC_ADR(dev->base_addr));
+		temp=readl(ETHER_ARC_DATA(dev->base_addr));
+		temp&=0xffff0000;
+
+		/* Disable the current multicast stuff */
+		writel(1,ETHER_ARC_ENA(dev->base_addr));
+
+		for(;;){
+			temp|=mc_list_ent->dmi_addr[1] |
+				mc_list_ent->dmi_addr[0]<<8;
+			writel(temp,ETHER_ARC_DATA(dev->base_addr));
+
+			i=readl(ETHER_ARC_ADR(dev->base_addr));
+			writew(i+4,ETHER_ARC_ADR(dev->base_addr));
+
+			temp=mc_list_ent->dmi_addr[5]|
+				mc_list_ent->dmi_addr[4]<<8 |
+				mc_list_ent->dmi_addr[3]<<16 |
+				mc_list_ent->dmi_addr[2]<<24;
+			writel(temp,ETHER_ARC_DATA(dev->base_addr));
+
+			count--;
+			if(!mc_list_ent->next || !count){
+				break;
+			}
+			DEBUG(printk("mc_list_next=%#x\n",mc_list_ent->next);)
+			mc_list_ent=mc_list_ent->next;
+
+
+			i=readl(ETHER_ARC_ADR(dev->base_addr));
+			writel(i+4,ETHER_ARC_ADR(dev->base_addr));
+
+			temp=mc_list_ent->dmi_addr[3]|
+				mc_list_ent->dmi_addr[2]<<8 |
+				mc_list_ent->dmi_addr[1]<<16 |
+				mc_list_ent->dmi_addr[0]<<24;
+			writel(temp,ETHER_ARC_DATA(dev->base_addr));
+
+			i=readl(ETHER_ARC_ADR(dev->base_addr));
+			writel(i+4,ETHER_ARC_ADR(dev->base_addr));
+
+			temp=mc_list_ent->dmi_addr[4]<<16 |
+				mc_list_ent->dmi_addr[5]<<24;
+
+			writel(temp,ETHER_ARC_DATA(dev->base_addr));
+
+			count--;
+			if(!mc_list_ent->next || !count){
+				break;
+			}
+			mc_list_ent=mc_list_ent->next;
+		}
+
+
+		if(count)
+			printk(KERN_WARNING "Multicast list size error\n");
+
+
+		writew( ETHER_ARC_CTL_BROADACC_MSK|
+			ETHER_ARC_CTL_COMPEN_MSK,
+			ETHER_ARC_CTL(dev->base_addr));
+
+	}
+
+	/* enable the active ARC enties */
+	writew((1<<(count+2))-1,ETHER_ARC_ENA(dev->base_addr));
+}
+
+
+static int ether00_open(struct net_device* dev)
+{
+	int result,tmp;
+	struct net_priv* priv;
+
+	if (!is_valid_ether_addr(dev->dev_addr))
+		return -EINVAL;
+
+	/* Install interrupt handlers */
+	result=request_irq(dev->irq,ether00_int,0,"ether00",dev);
+	if(result)
+		goto open_err1;
+
+	result=request_irq(2,ether00_phy_int,0,"ether00_phy",dev);
+	if(result)
+		goto open_err2;
+
+	ether00_reset(dev);
+	result=ether00_mem_init(dev);
+	if(result)
+		goto open_err3;
+
+
+	ether00_setup_ethernet_address(dev);
+
+	ether00_set_multicast(dev);
+
+	result=ether00_write_phy(dev,PHY_CONTROL, PHY_CONTROL_ANEGEN_MSK | PHY_CONTROL_RANEG_MSK);
+	if(result)
+		goto open_err4;
+	result=ether00_write_phy(dev,PHY_IRQ_CONTROL, PHY_IRQ_CONTROL_LS_CHG_IE_MSK |
+				 PHY_IRQ_CONTROL_ANEG_COMP_IE_MSK);
+	if(result)
+		goto open_err4;
+
+	/* Start the device enable interrupts */
+	writew(ETHER_RX_CTL_RXEN_MSK
+//	       | ETHER_RX_CTL_STRIPCRC_MSK
+	       | ETHER_RX_CTL_ENGOOD_MSK
+	       | ETHER_RX_CTL_ENRXPAR_MSK| ETHER_RX_CTL_ENLONGERR_MSK
+	       | ETHER_RX_CTL_ENOVER_MSK| ETHER_RX_CTL_ENCRCERR_MSK,
+	       ETHER_RX_CTL(dev->base_addr));
+
+	writew(ETHER_TX_CTL_TXEN_MSK|
+	       ETHER_TX_CTL_ENEXDEFER_MSK|
+	       ETHER_TX_CTL_ENLCARR_MSK|
+	       ETHER_TX_CTL_ENEXCOLL_MSK|
+	       ETHER_TX_CTL_ENLATECOLL_MSK|
+	       ETHER_TX_CTL_ENTXPAR_MSK|
+	       ETHER_TX_CTL_ENCOMP_MSK,
+	       ETHER_TX_CTL(dev->base_addr));
+
+	tmp=readl(ETHER_DMA_CTL(dev->base_addr));
+	writel(tmp&~ETHER_DMA_CTL_INTMASK_MSK,ETHER_DMA_CTL(dev->base_addr));
+
+	return 0;
+
+ open_err4:
+	ether00_reset(dev);
+ open_err3:
+	free_irq(2,dev);
+ open_err2:
+	free_irq(dev->irq,dev);
+ open_err1:
+	return result;
+
+}
+
+
+static int ether00_tx(struct sk_buff* skb, struct net_device* dev)
+{
+	struct net_priv *priv=dev->priv;
+	struct tx_fda_ent *fda_ptr;
+	int i;
+
+
+	/*
+	 *	Find an empty slot in which to stick the frame
+	 */
+	fda_ptr=(struct tx_fda_ent*)__dma_va(readl(ETHER_TXFRMPTR(dev->base_addr)));
+	i=0;
+	while(i<TX_NUM_FDESC){
+		if (fda_ptr->fd.FDStat||(fda_ptr->fd.FDCtl & FDCTL_COWNSFD_MSK)){
+			fda_ptr =(struct tx_fda_ent*) __dma_va((struct tx_fda_ent*)fda_ptr->fd.FDNext);
+		}
+		else {
+			break;
+		}
+		i++;
+	}
+
+	/* Write the skb data from the cache*/
+	consistent_sync(skb->data,skb->len,PCI_DMA_TODEVICE);
+	fda_ptr->bd.BuffData=(char*)__pa(skb->data);
+	fda_ptr->bd.BuffLength=(unsigned short)skb->len;
+	/* Save the pointer to the skb for freeing later */
+	fda_ptr->fd.FDSystem=(unsigned int)skb;
+	fda_ptr->fd.FDStat=0;
+	/* Pass ownership of the buffers to the controller */
+	fda_ptr->fd.FDCtl=1;
+	fda_ptr->fd.FDCtl|=FDCTL_COWNSFD_MSK;
+
+	/* If the next buffer in the list is full, stop the queue */
+	fda_ptr=(struct tx_fda_ent*)__dma_va(fda_ptr->fd.FDNext);
+	if ((fda_ptr->fd.FDStat)||(fda_ptr->fd.FDCtl & FDCTL_COWNSFD_MSK)){
+		netif_stop_queue(dev);
+		priv->queue_stopped=1;
+	}
+
+	return 0;
+}
+
+static struct net_device_stats *ether00_stats(struct net_device* dev)
+{
+	struct net_priv *priv=dev->priv;
+	return &priv->stats;
+}
+
+
+static int ether00_stop(struct net_device* dev)
+{
+	struct net_priv *priv=dev->priv;
+	int tmp;
+
+	/* Stop/disable the device. */
+	tmp=readw(ETHER_RX_CTL(dev->base_addr));
+	tmp&=~(ETHER_RX_CTL_RXEN_MSK | ETHER_RX_CTL_ENGOOD_MSK);
+	tmp|=ETHER_RX_CTL_RXHALT_MSK;
+	writew(tmp,ETHER_RX_CTL(dev->base_addr));
+
+	tmp=readl(ETHER_TX_CTL(dev->base_addr));
+	tmp&=~ETHER_TX_CTL_TXEN_MSK;
+	tmp|=ETHER_TX_CTL_TXHALT_MSK;
+	writel(tmp,ETHER_TX_CTL(dev->base_addr));
+
+	/* Free up system resources */
+	free_irq(dev->irq,dev);
+	free_irq(2,dev);
+	iounmap(priv->dma_data);
+
+	return 0;
+}
+
+
+static void ether00_get_ethernet_address(struct net_device* dev)
+{
+	struct mtd_info *mymtd=NULL;
+	int i;
+	size_t retlen;
+
+	/*
+	 * For the Epxa10 dev board (camelot), the ethernet MAC
+	 * address is of the form  00:aa:aa:00:xx:xx where
+	 * 00:aa:aa is the Altera vendor ID and xx:xx is the
+	 * last 2 bytes of the board serial number, as programmed
+	 * into the OTP area of the flash device on EBI1. If this
+	 * isn't an expa10 dev board, or there's no mtd support to
+	 * read the serial number from flash then we'll force the
+	 * use to set their own mac address using ifconfig.
+	 */
+
+#ifdef CONFIG_ARCH_CAMELOT
+#ifdef CONFIG_MTD
+	/* get the mtd_info structure for the first mtd device*/
+	for(i=0;i<MAX_MTD_DEVICES;i++){
+		mymtd=get_mtd_device(NULL,i);
+		if(!mymtd||!strcmp(mymtd->name,"EPXA10DB flash"))
+			break;
+	}
+
+	if(!mymtd || !mymtd->read_user_prot_reg){
+		printk(KERN_WARNING "%s: Failed to read MAC address from flash\n",dev->name);
+	}else{
+		mymtd->read_user_prot_reg(mymtd,2,1,&retlen,&dev->dev_addr[5]);
+		mymtd->read_user_prot_reg(mymtd,3,1,&retlen,&dev->dev_addr[4]);
+		dev->dev_addr[3]=0;
+		dev->dev_addr[2]=vendor_id[1];
+		dev->dev_addr[1]=vendor_id[0];
+		dev->dev_addr[0]=0;
+	}
+#else
+	printk(KERN_WARNING "%s: MTD support required to read MAC address from EPXA10 dev board\n", dev->name);
+#endif
+#endif
+
+	if (!is_valid_ether_addr(dev->dev_addr))
+		printk("%s: Invalid ethernet MAC address.  Please set using "
+			"ifconfig\n", dev->name);
+
+}
+
+/*
+ * Keep a mapping of dev_info addresses -> port lines to use when
+ * removing ports dev==NULL indicates unused entry
+ */
+
+
+static struct net_device* dev_list[ETH_NR];
+
+static int ether00_add_device(struct pldhs_dev_info* dev_info,void* dev_ps_data)
+{
+	struct net_device *dev;
+	struct net_priv *priv;
+	void *map_addr;
+	int result;
+	int i;
+
+	i=0;
+	while(dev_list[i] && i < ETH_NR)
+		i++;
+
+	if(i==ETH_NR){
+		printk(KERN_WARNING "ether00: Maximum number of ports reached\n");
+		return 0;
+	}
+
+
+	if (!request_mem_region(dev_info->base_addr, MAC_REG_SIZE, "ether00"))
+		return -EBUSY;
+
+	dev = alloc_etherdev(sizeof(struct net_priv));
+	if(!dev) {
+		result = -ENOMEM;
+		goto out_release;
+	}
+	priv = dev->priv;
+
+	priv->tq_memupdate.routine=ether00_mem_update;
+	priv->tq_memupdate.data=(void*) dev;
+
+	spin_lock_init(&priv->rx_lock);
+
+	map_addr=ioremap_nocache(dev_info->base_addr,SZ_4K);
+	if(!map_addr){
+		result = -ENOMEM;
+		out_kfree;
+	}
+
+	dev->open=ether00_open;
+	dev->stop=ether00_stop;
+	dev->set_multicast_list=ether00_set_multicast;
+	dev->hard_start_xmit=ether00_tx;
+	dev->get_stats=ether00_stats;
+
+	ether00_get_ethernet_address(dev);
+
+	SET_MODULE_OWNER(dev);
+
+	dev->base_addr=(unsigned int)map_addr;
+	dev->irq=dev_info->irq;
+	dev->features=NETIF_F_DYNALLOC | NETIF_F_HW_CSUM;
+
+	result=register_netdev(dev);
+	if(result){
+		printk("Ether00: Error %i registering driver\n",result);
+		goto out_unmap;
+	}
+	printk("registered ether00 device at %#x\n",dev_info->base_addr);
+
+	dev_list[i]=dev;
+
+	return result;
+
+ out_unmap:
+	iounmap(map_addr);
+ out_kfree:
+	free_netdev(dev);
+ out_release:
+	release_mem_region(dev_info->base_addr, MAC_REG_SIZE);
+	return result;
+}
+
+
+static int ether00_remove_devices(void)
+{
+	int i;
+
+	for(i=0;i<ETH_NR;i++){
+		if(dev_list[i]){
+			netif_device_detach(dev_list[i]);
+			unregister_netdev(dev_list[i]);
+			iounmap((void*)dev_list[i]->base_addr);
+			release_mem_region(dev_list[i]->base_addr, MAC_REG_SIZE);
+			free_netdev(dev_list[i]);
+			dev_list[i]=0;
+		}
+	}
+	return 0;
+}
+
+static struct pld_hotswap_ops ether00_pldhs_ops={
+	.name = ETHER00_NAME,
+	.add_device = ether00_add_device,
+	.remove_devices = ether00_remove_devices,
+};
+
+
+static void __exit ether00_cleanup_module(void)
+{
+	int result;
+	result=ether00_remove_devices();
+	if(result)
+		printk(KERN_WARNING "ether00: failed to remove all devices\n");
+
+	pldhs_unregister_driver(ETHER00_NAME);
+}
+module_exit(ether00_cleanup_module);
+
+
+static int __init ether00_mod_init(void)
+{
+	printk("mod init\n");
+	return pldhs_register_driver(&ether00_pldhs_ops);
+
+}
+
+module_init(ether00_mod_init);
+
diff --git a/drivers/net/arm/ether1.c b/drivers/net/arm/ether1.c
new file mode 100644
index 0000000..36475eb
--- /dev/null
+++ b/drivers/net/arm/ether1.c
@@ -0,0 +1,1110 @@
+/*
+ *  linux/drivers/acorn/net/ether1.c
+ *
+ *  Copyright (C) 1996-2000 Russell King
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ *  Acorn ether1 driver (82586 chip) for Acorn machines
+ *
+ * We basically keep two queues in the cards memory - one for transmit
+ * and one for receive.  Each has a head and a tail.  The head is where
+ * we/the chip adds packets to be transmitted/received, and the tail
+ * is where the transmitter has got to/where the receiver will stop.
+ * Both of these queues are circular, and since the chip is running
+ * all the time, we have to be careful when we modify the pointers etc
+ * so that the buffer memory contents is valid all the time.
+ *
+ * Change log:
+ * 1.00	RMK			Released
+ * 1.01	RMK	19/03/1996	Transfers the last odd byte onto/off of the card now.
+ * 1.02	RMK	25/05/1997	Added code to restart RU if it goes not ready
+ * 1.03	RMK	14/09/1997	Cleaned up the handling of a reset during the TX interrupt.
+ *				Should prevent lockup.
+ * 1.04 RMK	17/09/1997	Added more info when initialsation of chip goes wrong.
+ *				TDR now only reports failure when chip reports non-zero
+ *				TDR time-distance.
+ * 1.05	RMK	31/12/1997	Removed calls to dev_tint for 2.1
+ * 1.06	RMK	10/02/2000	Updated for 2.3.43
+ * 1.07	RMK	13/05/2000	Updated for 2.3.99-pre8
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/bitops.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/ecard.h>
+
+#define __ETHER1_C
+#include "ether1.h"
+
+static unsigned int net_debug = NET_DEBUG;
+
+#define BUFFER_SIZE	0x10000
+#define TX_AREA_START	0x00100
+#define TX_AREA_END	0x05000
+#define RX_AREA_START	0x05000
+#define RX_AREA_END	0x0fc00
+
+static int ether1_open(struct net_device *dev);
+static int ether1_sendpacket(struct sk_buff *skb, struct net_device *dev);
+static irqreturn_t ether1_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static int ether1_close(struct net_device *dev);
+static struct net_device_stats *ether1_getstats(struct net_device *dev);
+static void ether1_setmulticastlist(struct net_device *dev);
+static void ether1_timeout(struct net_device *dev);
+
+/* ------------------------------------------------------------------------- */
+
+static char version[] __initdata = "ether1 ethernet driver (c) 2000 Russell King v1.07\n";
+
+#define BUS_16 16
+#define BUS_8  8
+
+/* ------------------------------------------------------------------------- */
+
+#define DISABLEIRQS 1
+#define NORMALIRQS  0
+
+#define ether1_readw(dev, addr, type, offset, svflgs) ether1_inw_p (dev, addr + (int)(&((type *)0)->offset), svflgs)
+#define ether1_writew(dev, val, addr, type, offset, svflgs) ether1_outw_p (dev, val, addr + (int)(&((type *)0)->offset), svflgs)
+
+static inline unsigned short
+ether1_inw_p (struct net_device *dev, int addr, int svflgs)
+{
+	unsigned long flags;
+	unsigned short ret;
+
+	if (svflgs)
+		local_irq_save (flags);
+
+	writeb(addr >> 12, REG_PAGE);
+	ret = readw(ETHER1_RAM + ((addr & 4095) << 1));
+	if (svflgs)
+		local_irq_restore (flags);
+	return ret;
+}
+
+static inline void
+ether1_outw_p (struct net_device *dev, unsigned short val, int addr, int svflgs)
+{
+	unsigned long flags;
+
+	if (svflgs)
+		local_irq_save (flags);
+
+	writeb(addr >> 12, REG_PAGE);
+	writew(val, ETHER1_RAM + ((addr & 4095) << 1));
+	if (svflgs)
+		local_irq_restore (flags);
+}
+
+/*
+ * Some inline assembler to allow fast transfers on to/off of the card.
+ * Since this driver depends on some features presented by the ARM
+ * specific architecture, and that you can't configure this driver
+ * without specifiing ARM mode, this is not a problem.
+ *
+ * This routine is essentially an optimised memcpy from the card's
+ * onboard RAM to kernel memory.
+ */
+static void
+ether1_writebuffer (struct net_device *dev, void *data, unsigned int start, unsigned int length)
+{
+	unsigned int page, thislen, offset;
+	void __iomem *addr;
+
+	offset = start & 4095;
+	page = start >> 12;
+	addr = ETHER1_RAM + (offset << 1);
+
+	if (offset + length > 4096)
+		thislen = 4096 - offset;
+	else
+		thislen = length;
+
+	do {
+		int used;
+
+		writeb(page, REG_PAGE);
+		length -= thislen;
+
+		__asm__ __volatile__(
+	"subs	%3, %3, #2\n\
+	bmi	2f\n\
+1:	ldr	%0, [%1], #2\n\
+	mov	%0, %0, lsl #16\n\
+	orr	%0, %0, %0, lsr #16\n\
+	str	%0, [%2], #4\n\
+	subs	%3, %3, #2\n\
+	bmi	2f\n\
+	ldr	%0, [%1], #2\n\
+	mov	%0, %0, lsl #16\n\
+	orr	%0, %0, %0, lsr #16\n\
+	str	%0, [%2], #4\n\
+	subs	%3, %3, #2\n\
+	bmi	2f\n\
+	ldr	%0, [%1], #2\n\
+	mov	%0, %0, lsl #16\n\
+	orr	%0, %0, %0, lsr #16\n\
+	str	%0, [%2], #4\n\
+	subs	%3, %3, #2\n\
+	bmi	2f\n\
+	ldr	%0, [%1], #2\n\
+	mov	%0, %0, lsl #16\n\
+	orr	%0, %0, %0, lsr #16\n\
+	str	%0, [%2], #4\n\
+	subs	%3, %3, #2\n\
+	bpl	1b\n\
+2:	adds	%3, %3, #1\n\
+	ldreqb	%0, [%1]\n\
+	streqb	%0, [%2]"
+		: "=&r" (used), "=&r" (data)
+		: "r"  (addr), "r" (thislen), "1" (data));
+
+		addr = ETHER1_RAM;
+
+		thislen = length;
+		if (thislen > 4096)
+			thislen = 4096;
+		page++;
+	} while (thislen);
+}
+
+static void
+ether1_readbuffer (struct net_device *dev, void *data, unsigned int start, unsigned int length)
+{
+	unsigned int page, thislen, offset;
+	void __iomem *addr;
+
+	offset = start & 4095;
+	page = start >> 12;
+	addr = ETHER1_RAM + (offset << 1);
+
+	if (offset + length > 4096)
+		thislen = 4096 - offset;
+	else
+		thislen = length;
+
+	do {
+		int used;
+
+		writeb(page, REG_PAGE);
+		length -= thislen;
+
+		__asm__ __volatile__(
+	"subs	%3, %3, #2\n\
+	bmi	2f\n\
+1:	ldr	%0, [%2], #4\n\
+	strb	%0, [%1], #1\n\
+	mov	%0, %0, lsr #8\n\
+	strb	%0, [%1], #1\n\
+	subs	%3, %3, #2\n\
+	bmi	2f\n\
+	ldr	%0, [%2], #4\n\
+	strb	%0, [%1], #1\n\
+	mov	%0, %0, lsr #8\n\
+	strb	%0, [%1], #1\n\
+	subs	%3, %3, #2\n\
+	bmi	2f\n\
+	ldr	%0, [%2], #4\n\
+	strb	%0, [%1], #1\n\
+	mov	%0, %0, lsr #8\n\
+	strb	%0, [%1], #1\n\
+	subs	%3, %3, #2\n\
+	bmi	2f\n\
+	ldr	%0, [%2], #4\n\
+	strb	%0, [%1], #1\n\
+	mov	%0, %0, lsr #8\n\
+	strb	%0, [%1], #1\n\
+	subs	%3, %3, #2\n\
+	bpl	1b\n\
+2:	adds	%3, %3, #1\n\
+	ldreqb	%0, [%2]\n\
+	streqb	%0, [%1]"
+		: "=&r" (used), "=&r" (data)
+		: "r"  (addr), "r" (thislen), "1" (data));
+
+		addr = ETHER1_RAM;
+
+		thislen = length;
+		if (thislen > 4096)
+			thislen = 4096;
+		page++;
+	} while (thislen);
+}
+
+static int __init
+ether1_ramtest(struct net_device *dev, unsigned char byte)
+{
+	unsigned char *buffer = kmalloc (BUFFER_SIZE, GFP_KERNEL);
+	int i, ret = BUFFER_SIZE;
+	int max_errors = 15;
+	int bad = -1;
+	int bad_start = 0;
+
+	if (!buffer)
+		return 1;
+
+	memset (buffer, byte, BUFFER_SIZE);
+	ether1_writebuffer (dev, buffer, 0, BUFFER_SIZE);
+	memset (buffer, byte ^ 0xff, BUFFER_SIZE);
+	ether1_readbuffer (dev, buffer, 0, BUFFER_SIZE);
+
+	for (i = 0; i < BUFFER_SIZE; i++) {
+		if (buffer[i] != byte) {
+			if (max_errors >= 0 && bad != buffer[i]) {
+				if (bad != -1)
+					printk ("\n");
+				printk (KERN_CRIT "%s: RAM failed with (%02X instead of %02X) at 0x%04X",
+					dev->name, buffer[i], byte, i);
+				ret = -ENODEV;
+				max_errors --;
+				bad = buffer[i];
+				bad_start = i;
+			}
+		} else {
+			if (bad != -1) {
+			    	if (bad_start == i - 1)
+					printk ("\n");
+				else
+					printk (" - 0x%04X\n", i - 1);
+				bad = -1;
+			}
+		}
+	}
+
+	if (bad != -1)
+		printk (" - 0x%04X\n", BUFFER_SIZE);
+	kfree (buffer);
+
+	return ret;
+}
+
+static int
+ether1_reset (struct net_device *dev)
+{
+	writeb(CTRL_RST|CTRL_ACK, REG_CONTROL);
+	return BUS_16;
+}
+
+static int __init
+ether1_init_2(struct net_device *dev)
+{
+	int i;
+	dev->mem_start = 0;
+
+	i = ether1_ramtest (dev, 0x5a);
+
+	if (i > 0)
+		i = ether1_ramtest (dev, 0x1e);
+
+	if (i <= 0)
+	    	return -ENODEV;
+
+	dev->mem_end = i;
+	return 0;
+}
+
+/*
+ * These are the structures that are loaded into the ether RAM card to
+ * initialise the 82586
+ */
+
+/* at 0x0100 */
+#define NOP_ADDR	(TX_AREA_START)
+#define NOP_SIZE	(0x06)
+static nop_t  init_nop  = {
+	0,
+	CMD_NOP,
+	NOP_ADDR
+};
+
+/* at 0x003a */
+#define TDR_ADDR	(0x003a)
+#define TDR_SIZE	(0x08)
+static tdr_t  init_tdr	= {
+	0,
+	CMD_TDR | CMD_INTR,
+	NOP_ADDR,
+	0
+};
+
+/* at 0x002e */
+#define MC_ADDR		(0x002e)
+#define MC_SIZE		(0x0c)
+static mc_t   init_mc   = {
+	0,
+	CMD_SETMULTICAST,
+	TDR_ADDR,
+	0,
+	{ { 0, } }
+};
+
+/* at 0x0022 */
+#define SA_ADDR		(0x0022)
+#define SA_SIZE		(0x0c)
+static sa_t   init_sa   = {
+	0,
+	CMD_SETADDRESS,
+	MC_ADDR,
+	{ 0, }
+};
+
+/* at 0x0010 */
+#define CFG_ADDR	(0x0010)
+#define CFG_SIZE	(0x12)
+static cfg_t  init_cfg  = {
+	0,
+	CMD_CONFIG,
+	SA_ADDR,
+	8,
+	8,
+	CFG8_SRDY,
+	CFG9_PREAMB8 | CFG9_ADDRLENBUF | CFG9_ADDRLEN(6),
+	0,
+	0x60,
+	0,
+	CFG13_RETRY(15) | CFG13_SLOTH(2),
+	0,
+};
+
+/* at 0x0000 */
+#define SCB_ADDR	(0x0000)
+#define SCB_SIZE	(0x10)
+static scb_t  init_scb  = {
+	0,
+	SCB_CMDACKRNR | SCB_CMDACKCNA | SCB_CMDACKFR | SCB_CMDACKCX,
+	CFG_ADDR,
+	RX_AREA_START,
+	0,
+	0,
+	0,
+	0
+};
+
+/* at 0xffee */
+#define ISCP_ADDR	(0xffee)
+#define ISCP_SIZE	(0x08)
+static iscp_t init_iscp = {
+	1,
+	SCB_ADDR,
+	0x0000,
+	0x0000
+};
+
+/* at 0xfff6 */
+#define SCP_ADDR	(0xfff6)
+#define SCP_SIZE	(0x0a)
+static scp_t  init_scp  = {
+	SCP_SY_16BBUS,
+	{ 0, 0 },
+	ISCP_ADDR,
+	0
+};
+
+#define RFD_SIZE	(0x16)
+static rfd_t  init_rfd	= {
+	0,
+	0,
+	0,
+	0,
+	{ 0, },
+	{ 0, },
+	0
+};
+
+#define RBD_SIZE	(0x0a)
+static rbd_t  init_rbd	= {
+	0,
+	0,
+	0,
+	0,
+	ETH_FRAME_LEN + 8
+};
+
+#define TX_SIZE		(0x08)
+#define TBD_SIZE	(0x08)
+
+static int
+ether1_init_for_open (struct net_device *dev)
+{
+	int i, status, addr, next, next2;
+	int failures = 0;
+	unsigned long timeout;
+
+	writeb(CTRL_RST|CTRL_ACK, REG_CONTROL);
+
+	for (i = 0; i < 6; i++)
+		init_sa.sa_addr[i] = dev->dev_addr[i];
+
+	/* load data structures into ether1 RAM */
+	ether1_writebuffer (dev, &init_scp,  SCP_ADDR,  SCP_SIZE);
+	ether1_writebuffer (dev, &init_iscp, ISCP_ADDR, ISCP_SIZE);
+	ether1_writebuffer (dev, &init_scb,  SCB_ADDR,  SCB_SIZE);
+	ether1_writebuffer (dev, &init_cfg,  CFG_ADDR,  CFG_SIZE);
+	ether1_writebuffer (dev, &init_sa,   SA_ADDR,   SA_SIZE);
+	ether1_writebuffer (dev, &init_mc,   MC_ADDR,   MC_SIZE);
+	ether1_writebuffer (dev, &init_tdr,  TDR_ADDR,  TDR_SIZE);
+	ether1_writebuffer (dev, &init_nop,  NOP_ADDR,  NOP_SIZE);
+
+	if (ether1_readw(dev, CFG_ADDR, cfg_t, cfg_command, NORMALIRQS) != CMD_CONFIG) {
+		printk (KERN_ERR "%s: detected either RAM fault or compiler bug\n",
+			dev->name);
+		return 1;
+	}
+
+	/*
+	 * setup circularly linked list of { rfd, rbd, buffer }, with
+	 * all rfds circularly linked, rbds circularly linked.
+	 * First rfd is linked to scp, first rbd is linked to first
+	 * rfd.  Last rbd has a suspend command.
+	 */
+	addr = RX_AREA_START;
+	do {
+		next = addr + RFD_SIZE + RBD_SIZE + ETH_FRAME_LEN + 10;
+		next2 = next + RFD_SIZE + RBD_SIZE + ETH_FRAME_LEN + 10;
+
+		if (next2 >= RX_AREA_END) {
+			next = RX_AREA_START;
+			init_rfd.rfd_command = RFD_CMDEL | RFD_CMDSUSPEND;
+			priv(dev)->rx_tail = addr;
+		} else
+			init_rfd.rfd_command = 0;
+		if (addr == RX_AREA_START)
+			init_rfd.rfd_rbdoffset = addr + RFD_SIZE;
+		else
+			init_rfd.rfd_rbdoffset = 0;
+		init_rfd.rfd_link = next;
+		init_rbd.rbd_link = next + RFD_SIZE;
+		init_rbd.rbd_bufl = addr + RFD_SIZE + RBD_SIZE;
+
+		ether1_writebuffer (dev, &init_rfd, addr, RFD_SIZE);
+		ether1_writebuffer (dev, &init_rbd, addr + RFD_SIZE, RBD_SIZE);
+		addr = next;
+	} while (next2 < RX_AREA_END);
+
+	priv(dev)->tx_link = NOP_ADDR;
+	priv(dev)->tx_head = NOP_ADDR + NOP_SIZE;
+	priv(dev)->tx_tail = TDR_ADDR;
+	priv(dev)->rx_head = RX_AREA_START;
+
+	/* release reset & give 586 a prod */
+	priv(dev)->resetting = 1;
+	priv(dev)->initialising = 1;
+	writeb(CTRL_RST, REG_CONTROL);
+	writeb(0, REG_CONTROL);
+	writeb(CTRL_CA, REG_CONTROL);
+
+	/* 586 should now unset iscp.busy */
+	timeout = jiffies + HZ/2;
+	while (ether1_readw(dev, ISCP_ADDR, iscp_t, iscp_busy, DISABLEIRQS) == 1) {
+		if (time_after(jiffies, timeout)) {
+			printk (KERN_WARNING "%s: can't initialise 82586: iscp is busy\n", dev->name);
+			return 1;
+		}
+	}
+
+	/* check status of commands that we issued */
+	timeout += HZ/10;
+	while (((status = ether1_readw(dev, CFG_ADDR, cfg_t, cfg_status, DISABLEIRQS))
+			& STAT_COMPLETE) == 0) {
+		if (time_after(jiffies, timeout))
+			break;
+	}
+
+	if ((status & (STAT_COMPLETE | STAT_OK)) != (STAT_COMPLETE | STAT_OK)) {
+		printk (KERN_WARNING "%s: can't initialise 82586: config status %04X\n", dev->name, status);
+		printk (KERN_DEBUG "%s: SCB=[STS=%04X CMD=%04X CBL=%04X RFA=%04X]\n", dev->name,
+			ether1_readw(dev, SCB_ADDR, scb_t, scb_status, NORMALIRQS),
+			ether1_readw(dev, SCB_ADDR, scb_t, scb_command, NORMALIRQS),
+			ether1_readw(dev, SCB_ADDR, scb_t, scb_cbl_offset, NORMALIRQS),
+			ether1_readw(dev, SCB_ADDR, scb_t, scb_rfa_offset, NORMALIRQS));
+		failures += 1;
+	}
+
+	timeout += HZ/10;
+	while (((status = ether1_readw(dev, SA_ADDR, sa_t, sa_status, DISABLEIRQS))
+			& STAT_COMPLETE) == 0) {
+		if (time_after(jiffies, timeout))
+			break;
+	}
+
+	if ((status & (STAT_COMPLETE | STAT_OK)) != (STAT_COMPLETE | STAT_OK)) {
+		printk (KERN_WARNING "%s: can't initialise 82586: set address status %04X\n", dev->name, status);
+		printk (KERN_DEBUG "%s: SCB=[STS=%04X CMD=%04X CBL=%04X RFA=%04X]\n", dev->name,
+			ether1_readw(dev, SCB_ADDR, scb_t, scb_status, NORMALIRQS),
+			ether1_readw(dev, SCB_ADDR, scb_t, scb_command, NORMALIRQS),
+			ether1_readw(dev, SCB_ADDR, scb_t, scb_cbl_offset, NORMALIRQS),
+			ether1_readw(dev, SCB_ADDR, scb_t, scb_rfa_offset, NORMALIRQS));
+		failures += 1;
+	}
+
+	timeout += HZ/10;
+	while (((status = ether1_readw(dev, MC_ADDR, mc_t, mc_status, DISABLEIRQS))
+			& STAT_COMPLETE) == 0) {
+		if (time_after(jiffies, timeout))
+			break;
+	}
+
+	if ((status & (STAT_COMPLETE | STAT_OK)) != (STAT_COMPLETE | STAT_OK)) {
+		printk (KERN_WARNING "%s: can't initialise 82586: set multicast status %04X\n", dev->name, status);
+		printk (KERN_DEBUG "%s: SCB=[STS=%04X CMD=%04X CBL=%04X RFA=%04X]\n", dev->name,
+			ether1_readw(dev, SCB_ADDR, scb_t, scb_status, NORMALIRQS),
+			ether1_readw(dev, SCB_ADDR, scb_t, scb_command, NORMALIRQS),
+			ether1_readw(dev, SCB_ADDR, scb_t, scb_cbl_offset, NORMALIRQS),
+			ether1_readw(dev, SCB_ADDR, scb_t, scb_rfa_offset, NORMALIRQS));
+		failures += 1;
+	}
+
+	timeout += HZ;
+	while (((status = ether1_readw(dev, TDR_ADDR, tdr_t, tdr_status, DISABLEIRQS))
+			& STAT_COMPLETE) == 0) {
+		if (time_after(jiffies, timeout))
+			break;
+	}
+
+	if ((status & (STAT_COMPLETE | STAT_OK)) != (STAT_COMPLETE | STAT_OK)) {
+		printk (KERN_WARNING "%s: can't tdr (ignored)\n", dev->name);
+		printk (KERN_DEBUG "%s: SCB=[STS=%04X CMD=%04X CBL=%04X RFA=%04X]\n", dev->name,
+			ether1_readw(dev, SCB_ADDR, scb_t, scb_status, NORMALIRQS),
+			ether1_readw(dev, SCB_ADDR, scb_t, scb_command, NORMALIRQS),
+			ether1_readw(dev, SCB_ADDR, scb_t, scb_cbl_offset, NORMALIRQS),
+			ether1_readw(dev, SCB_ADDR, scb_t, scb_rfa_offset, NORMALIRQS));
+	} else {
+		status = ether1_readw(dev, TDR_ADDR, tdr_t, tdr_result, DISABLEIRQS);
+		if (status & TDR_XCVRPROB)
+			printk (KERN_WARNING "%s: i/f failed tdr: transceiver problem\n", dev->name);
+		else if ((status & (TDR_SHORT|TDR_OPEN)) && (status & TDR_TIME)) {
+#ifdef FANCY
+			printk (KERN_WARNING "%s: i/f failed tdr: cable %s %d.%d us away\n", dev->name,
+				status & TDR_SHORT ? "short" : "open", (status & TDR_TIME) / 10,
+				(status & TDR_TIME) % 10);
+#else
+			printk (KERN_WARNING "%s: i/f failed tdr: cable %s %d clks away\n", dev->name,
+				status & TDR_SHORT ? "short" : "open", (status & TDR_TIME));
+#endif
+		}
+	}
+
+	if (failures)
+		ether1_reset (dev);
+	return failures ? 1 : 0;
+}
+
+/* ------------------------------------------------------------------------- */
+
+static int
+ether1_txalloc (struct net_device *dev, int size)
+{
+	int start, tail;
+
+	size = (size + 1) & ~1;
+	tail = priv(dev)->tx_tail;
+
+	if (priv(dev)->tx_head + size > TX_AREA_END) {
+		if (tail > priv(dev)->tx_head)
+			return -1;
+		start = TX_AREA_START;
+		if (start + size > tail)
+			return -1;
+		priv(dev)->tx_head = start + size;
+	} else {
+		if (priv(dev)->tx_head < tail && (priv(dev)->tx_head + size) > tail)
+			return -1;
+		start = priv(dev)->tx_head;
+		priv(dev)->tx_head += size;
+	}
+
+	return start;
+}
+
+static int
+ether1_open (struct net_device *dev)
+{
+	if (!is_valid_ether_addr(dev->dev_addr)) {
+		printk(KERN_WARNING "%s: invalid ethernet MAC address\n",
+			dev->name);
+		return -EINVAL;
+	}
+
+	if (request_irq(dev->irq, ether1_interrupt, 0, "ether1", dev))
+		return -EAGAIN;
+
+	memset (&priv(dev)->stats, 0, sizeof (struct net_device_stats));
+
+	if (ether1_init_for_open (dev)) {
+		free_irq (dev->irq, dev);
+		return -EAGAIN;
+	}
+
+	netif_start_queue(dev);
+
+	return 0;
+}
+
+static void
+ether1_timeout(struct net_device *dev)
+{
+	printk(KERN_WARNING "%s: transmit timeout, network cable problem?\n",
+		dev->name);
+	printk(KERN_WARNING "%s: resetting device\n", dev->name);
+
+	ether1_reset (dev);
+
+	if (ether1_init_for_open (dev))
+		printk (KERN_ERR "%s: unable to restart interface\n", dev->name);
+
+	priv(dev)->stats.tx_errors++;
+	netif_wake_queue(dev);
+}
+
+static int
+ether1_sendpacket (struct sk_buff *skb, struct net_device *dev)
+{
+	int tmp, tst, nopaddr, txaddr, tbdaddr, dataddr;
+	unsigned long flags;
+	tx_t tx;
+	tbd_t tbd;
+	nop_t nop;
+
+	if (priv(dev)->restart) {
+		printk(KERN_WARNING "%s: resetting device\n", dev->name);
+
+		ether1_reset(dev);
+
+		if (ether1_init_for_open(dev))
+			printk(KERN_ERR "%s: unable to restart interface\n", dev->name);
+		else
+			priv(dev)->restart = 0;
+	}
+
+	if (skb->len < ETH_ZLEN) {
+		skb = skb_padto(skb, ETH_ZLEN);
+		if (skb == NULL)
+			goto out;
+	}
+
+	/*
+	 * insert packet followed by a nop
+	 */
+	txaddr = ether1_txalloc (dev, TX_SIZE);
+	tbdaddr = ether1_txalloc (dev, TBD_SIZE);
+	dataddr = ether1_txalloc (dev, skb->len);
+	nopaddr = ether1_txalloc (dev, NOP_SIZE);
+
+	tx.tx_status = 0;
+	tx.tx_command = CMD_TX | CMD_INTR;
+	tx.tx_link = nopaddr;
+	tx.tx_tbdoffset = tbdaddr;
+	tbd.tbd_opts = TBD_EOL | skb->len;
+	tbd.tbd_link = I82586_NULL;
+	tbd.tbd_bufl = dataddr;
+	tbd.tbd_bufh = 0;
+	nop.nop_status = 0;
+	nop.nop_command = CMD_NOP;
+	nop.nop_link = nopaddr;
+
+	local_irq_save(flags);
+	ether1_writebuffer (dev, &tx, txaddr, TX_SIZE);
+	ether1_writebuffer (dev, &tbd, tbdaddr, TBD_SIZE);
+	ether1_writebuffer (dev, skb->data, dataddr, skb->len);
+	ether1_writebuffer (dev, &nop, nopaddr, NOP_SIZE);
+	tmp = priv(dev)->tx_link;
+	priv(dev)->tx_link = nopaddr;
+
+	/* now reset the previous nop pointer */
+	ether1_writew(dev, txaddr, tmp, nop_t, nop_link, NORMALIRQS);
+
+	local_irq_restore(flags);
+
+	/* handle transmit */
+	dev->trans_start = jiffies;
+
+	/* check to see if we have room for a full sized ether frame */
+	tmp = priv(dev)->tx_head;
+	tst = ether1_txalloc (dev, TX_SIZE + TBD_SIZE + NOP_SIZE + ETH_FRAME_LEN);
+	priv(dev)->tx_head = tmp;
+	dev_kfree_skb (skb);
+
+	if (tst == -1)
+		netif_stop_queue(dev);
+
+ out:
+	return 0;
+}
+
+static void
+ether1_xmit_done (struct net_device *dev)
+{
+	nop_t nop;
+	int caddr, tst;
+
+	caddr = priv(dev)->tx_tail;
+
+again:
+	ether1_readbuffer (dev, &nop, caddr, NOP_SIZE);
+
+	switch (nop.nop_command & CMD_MASK) {
+	case CMD_TDR:
+		/* special case */
+		if (ether1_readw(dev, SCB_ADDR, scb_t, scb_cbl_offset, NORMALIRQS)
+				!= (unsigned short)I82586_NULL) {
+			ether1_writew(dev, SCB_CMDCUCSTART | SCB_CMDRXSTART, SCB_ADDR, scb_t,
+				    scb_command, NORMALIRQS);
+			writeb(CTRL_CA, REG_CONTROL);
+		}
+		priv(dev)->tx_tail = NOP_ADDR;
+		return;
+
+	case CMD_NOP:
+		if (nop.nop_link == caddr) {
+			if (priv(dev)->initialising == 0)
+				printk (KERN_WARNING "%s: strange command complete with no tx command!\n", dev->name);
+			else
+			        priv(dev)->initialising = 0;
+			return;
+		}
+		if (caddr == nop.nop_link)
+			return;
+		caddr = nop.nop_link;
+		goto again;
+
+	case CMD_TX:
+		if (nop.nop_status & STAT_COMPLETE)
+			break;
+		printk (KERN_ERR "%s: strange command complete without completed command\n", dev->name);
+		priv(dev)->restart = 1;
+		return;
+
+	default:
+		printk (KERN_WARNING "%s: strange command %d complete! (offset %04X)", dev->name,
+			nop.nop_command & CMD_MASK, caddr);
+		priv(dev)->restart = 1;
+		return;
+	}
+
+	while (nop.nop_status & STAT_COMPLETE) {
+		if (nop.nop_status & STAT_OK) {
+			priv(dev)->stats.tx_packets ++;
+			priv(dev)->stats.collisions += (nop.nop_status & STAT_COLLISIONS);
+		} else {
+			priv(dev)->stats.tx_errors ++;
+
+			if (nop.nop_status & STAT_COLLAFTERTX)
+				priv(dev)->stats.collisions ++;
+			if (nop.nop_status & STAT_NOCARRIER)
+				priv(dev)->stats.tx_carrier_errors ++;
+			if (nop.nop_status & STAT_TXLOSTCTS)
+				printk (KERN_WARNING "%s: cts lost\n", dev->name);
+			if (nop.nop_status & STAT_TXSLOWDMA)
+				priv(dev)->stats.tx_fifo_errors ++;
+			if (nop.nop_status & STAT_COLLEXCESSIVE)
+				priv(dev)->stats.collisions += 16;
+		}
+
+		if (nop.nop_link == caddr) {
+			printk (KERN_ERR "%s: tx buffer chaining error: tx command points to itself\n", dev->name);
+			break;
+		}
+
+		caddr = nop.nop_link;
+		ether1_readbuffer (dev, &nop, caddr, NOP_SIZE);
+		if ((nop.nop_command & CMD_MASK) != CMD_NOP) {
+			printk (KERN_ERR "%s: tx buffer chaining error: no nop after tx command\n", dev->name);
+			break;
+		}
+
+		if (caddr == nop.nop_link)
+			break;
+
+		caddr = nop.nop_link;
+		ether1_readbuffer (dev, &nop, caddr, NOP_SIZE);
+		if ((nop.nop_command & CMD_MASK) != CMD_TX) {
+			printk (KERN_ERR "%s: tx buffer chaining error: no tx command after nop\n", dev->name);
+			break;
+		}
+	}
+	priv(dev)->tx_tail = caddr;
+
+	caddr = priv(dev)->tx_head;
+	tst = ether1_txalloc (dev, TX_SIZE + TBD_SIZE + NOP_SIZE + ETH_FRAME_LEN);
+	priv(dev)->tx_head = caddr;
+	if (tst != -1)
+		netif_wake_queue(dev);
+}
+
+static void
+ether1_recv_done (struct net_device *dev)
+{
+	int status;
+	int nexttail, rbdaddr;
+	rbd_t rbd;
+
+	do {
+		status = ether1_readw(dev, priv(dev)->rx_head, rfd_t, rfd_status, NORMALIRQS);
+		if ((status & RFD_COMPLETE) == 0)
+			break;
+
+		rbdaddr = ether1_readw(dev, priv(dev)->rx_head, rfd_t, rfd_rbdoffset, NORMALIRQS);
+		ether1_readbuffer (dev, &rbd, rbdaddr, RBD_SIZE);
+
+		if ((rbd.rbd_status & (RBD_EOF | RBD_ACNTVALID)) == (RBD_EOF | RBD_ACNTVALID)) {
+			int length = rbd.rbd_status & RBD_ACNT;
+			struct sk_buff *skb;
+
+			length = (length + 1) & ~1;
+			skb = dev_alloc_skb (length + 2);
+
+			if (skb) {
+				skb->dev = dev;
+				skb_reserve (skb, 2);
+
+				ether1_readbuffer (dev, skb_put (skb, length), rbd.rbd_bufl, length);
+
+				skb->protocol = eth_type_trans (skb, dev);
+				netif_rx (skb);
+				priv(dev)->stats.rx_packets ++;
+			} else
+				priv(dev)->stats.rx_dropped ++;
+		} else {
+			printk(KERN_WARNING "%s: %s\n", dev->name,
+				(rbd.rbd_status & RBD_EOF) ? "oversized packet" : "acnt not valid");
+			priv(dev)->stats.rx_dropped ++;
+		}
+
+		nexttail = ether1_readw(dev, priv(dev)->rx_tail, rfd_t, rfd_link, NORMALIRQS);
+		/* nexttail should be rx_head */
+		if (nexttail != priv(dev)->rx_head)
+			printk(KERN_ERR "%s: receiver buffer chaining error (%04X != %04X)\n",
+				dev->name, nexttail, priv(dev)->rx_head);
+		ether1_writew(dev, RFD_CMDEL | RFD_CMDSUSPEND, nexttail, rfd_t, rfd_command, NORMALIRQS);
+		ether1_writew(dev, 0, priv(dev)->rx_tail, rfd_t, rfd_command, NORMALIRQS);
+		ether1_writew(dev, 0, priv(dev)->rx_tail, rfd_t, rfd_status, NORMALIRQS);
+		ether1_writew(dev, 0, priv(dev)->rx_tail, rfd_t, rfd_rbdoffset, NORMALIRQS);
+	
+		priv(dev)->rx_tail = nexttail;
+		priv(dev)->rx_head = ether1_readw(dev, priv(dev)->rx_head, rfd_t, rfd_link, NORMALIRQS);
+	} while (1);
+}
+
+static irqreturn_t
+ether1_interrupt (int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct net_device *dev = (struct net_device *)dev_id;
+	int status;
+
+	status = ether1_readw(dev, SCB_ADDR, scb_t, scb_status, NORMALIRQS);
+
+	if (status) {
+		ether1_writew(dev, status & (SCB_STRNR | SCB_STCNA | SCB_STFR | SCB_STCX),
+			    SCB_ADDR, scb_t, scb_command, NORMALIRQS);
+		writeb(CTRL_CA | CTRL_ACK, REG_CONTROL);
+		if (status & SCB_STCX) {
+			ether1_xmit_done (dev);
+		}
+		if (status & SCB_STCNA) {
+			if (priv(dev)->resetting == 0)
+				printk (KERN_WARNING "%s: CU went not ready ???\n", dev->name);
+			else
+				priv(dev)->resetting += 1;
+			if (ether1_readw(dev, SCB_ADDR, scb_t, scb_cbl_offset, NORMALIRQS)
+					!= (unsigned short)I82586_NULL) {
+				ether1_writew(dev, SCB_CMDCUCSTART, SCB_ADDR, scb_t, scb_command, NORMALIRQS);
+				writeb(CTRL_CA, REG_CONTROL);
+			}
+			if (priv(dev)->resetting == 2)
+				priv(dev)->resetting = 0;
+		}
+		if (status & SCB_STFR) {
+			ether1_recv_done (dev);
+		}
+		if (status & SCB_STRNR) {
+			if (ether1_readw(dev, SCB_ADDR, scb_t, scb_status, NORMALIRQS) & SCB_STRXSUSP) {
+				printk (KERN_WARNING "%s: RU went not ready: RU suspended\n", dev->name);
+				ether1_writew(dev, SCB_CMDRXRESUME, SCB_ADDR, scb_t, scb_command, NORMALIRQS);
+				writeb(CTRL_CA, REG_CONTROL);
+				priv(dev)->stats.rx_dropped ++;	/* we suspended due to lack of buffer space */
+			} else
+				printk(KERN_WARNING "%s: RU went not ready: %04X\n", dev->name,
+					ether1_readw(dev, SCB_ADDR, scb_t, scb_status, NORMALIRQS));
+			printk (KERN_WARNING "RU ptr = %04X\n", ether1_readw(dev, SCB_ADDR, scb_t, scb_rfa_offset,
+						NORMALIRQS));
+		}
+	} else
+		writeb(CTRL_ACK, REG_CONTROL);
+
+	return IRQ_HANDLED;
+}
+
+static int
+ether1_close (struct net_device *dev)
+{
+	ether1_reset (dev);
+
+	free_irq(dev->irq, dev);
+
+	return 0;
+}
+
+static struct net_device_stats *
+ether1_getstats (struct net_device *dev)
+{
+	return &priv(dev)->stats;
+}
+
+/*
+ * Set or clear the multicast filter for this adaptor.
+ * num_addrs == -1	Promiscuous mode, receive all packets.
+ * num_addrs == 0	Normal mode, clear multicast list.
+ * num_addrs > 0	Multicast mode, receive normal and MC packets, and do
+ *			best-effort filtering.
+ */
+static void
+ether1_setmulticastlist (struct net_device *dev)
+{
+}
+
+/* ------------------------------------------------------------------------- */
+
+static void __init ether1_banner(void)
+{
+	static unsigned int version_printed = 0;
+
+	if (net_debug && version_printed++ == 0)
+		printk(KERN_INFO "%s", version);
+}
+
+static int __devinit
+ether1_probe(struct expansion_card *ec, const struct ecard_id *id)
+{
+	struct net_device *dev;
+	int i, ret = 0;
+
+	ether1_banner();
+
+	ret = ecard_request_resources(ec);
+	if (ret)
+		goto out;
+
+	dev = alloc_etherdev(sizeof(struct ether1_priv));
+	if (!dev) {
+		ret = -ENOMEM;
+		goto release;
+	}
+
+	SET_MODULE_OWNER(dev);
+	SET_NETDEV_DEV(dev, &ec->dev);
+
+	dev->irq = ec->irq;
+	priv(dev)->base = ioremap(ecard_resource_start(ec, ECARD_RES_IOCFAST),
+				  ecard_resource_len(ec, ECARD_RES_IOCFAST));
+	if (!priv(dev)->base) {
+		ret = -ENOMEM;
+		goto free;
+	}
+
+	if ((priv(dev)->bus_type = ether1_reset(dev)) == 0) {
+		ret = -ENODEV;
+		goto free;
+	}
+
+	for (i = 0; i < 6; i++)
+		dev->dev_addr[i] = readb(IDPROM_ADDRESS + (i << 2));
+
+	if (ether1_init_2(dev)) {
+		ret = -ENODEV;
+		goto free;
+	}
+
+	dev->open		= ether1_open;
+	dev->stop		= ether1_close;
+	dev->hard_start_xmit    = ether1_sendpacket;
+	dev->get_stats		= ether1_getstats;
+	dev->set_multicast_list = ether1_setmulticastlist;
+	dev->tx_timeout		= ether1_timeout;
+	dev->watchdog_timeo	= 5 * HZ / 100;
+
+	ret = register_netdev(dev);
+	if (ret)
+		goto free;
+
+	printk(KERN_INFO "%s: ether1 in slot %d, ",
+		dev->name, ec->slot_no);
+    
+	for (i = 0; i < 6; i++)
+		printk ("%2.2x%c", dev->dev_addr[i], i == 5 ? '\n' : ':');
+
+	ecard_set_drvdata(ec, dev);
+	return 0;
+
+ free:
+	if (priv(dev)->base)
+		iounmap(priv(dev)->base);
+	free_netdev(dev);
+ release:
+	ecard_release_resources(ec);
+ out:
+	return ret;
+}
+
+static void __devexit ether1_remove(struct expansion_card *ec)
+{
+	struct net_device *dev = ecard_get_drvdata(ec);
+
+	ecard_set_drvdata(ec, NULL);	
+
+	unregister_netdev(dev);
+	iounmap(priv(dev)->base);
+	free_netdev(dev);
+	ecard_release_resources(ec);
+}
+
+static const struct ecard_id ether1_ids[] = {
+	{ MANU_ACORN, PROD_ACORN_ETHER1 },
+	{ 0xffff, 0xffff }
+};
+
+static struct ecard_driver ether1_driver = {
+	.probe		= ether1_probe,
+	.remove		= __devexit_p(ether1_remove),
+	.id_table	= ether1_ids,
+	.drv = {
+		.name	= "ether1",
+	},
+};
+
+static int __init ether1_init(void)
+{
+	return ecard_register_driver(&ether1_driver);
+}
+
+static void __exit ether1_exit(void)
+{
+	ecard_remove_driver(&ether1_driver);
+}
+
+module_init(ether1_init);
+module_exit(ether1_exit);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/arm/ether1.h b/drivers/net/arm/ether1.h
new file mode 100644
index 0000000..c8a4b23
--- /dev/null
+++ b/drivers/net/arm/ether1.h
@@ -0,0 +1,281 @@
+/*
+ *  linux/drivers/acorn/net/ether1.h
+ *
+ *  Copyright (C) 1996 Russell King
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ *  Network driver for Acorn Ether1 cards.
+ */
+
+#ifndef _LINUX_ether1_H
+#define _LINUX_ether1_H
+
+#ifdef __ETHER1_C
+/* use 0 for production, 1 for verification, >2 for debug */
+#ifndef NET_DEBUG
+#define NET_DEBUG 0
+#endif
+
+#define priv(dev)	((struct ether1_priv *)netdev_priv(dev))
+
+/* Page register */
+#define REG_PAGE	(priv(dev)->base + 0x0000)
+
+/* Control register */
+#define REG_CONTROL	(priv(dev)->base + 0x0004)
+#define CTRL_RST	0x01
+#define CTRL_LOOPBACK	0x02
+#define CTRL_CA		0x04
+#define CTRL_ACK	0x08
+
+#define ETHER1_RAM	(priv(dev)->base + 0x2000)
+
+/* HW address */
+#define IDPROM_ADDRESS	(priv(dev)->base + 0x0024)
+
+struct ether1_priv {
+	void __iomem *base;
+	struct net_device_stats stats;
+	unsigned int tx_link;
+	unsigned int tx_head;
+	volatile unsigned int tx_tail;
+	volatile unsigned int rx_head;
+	volatile unsigned int rx_tail;
+	unsigned char bus_type;
+	unsigned char resetting;
+	unsigned char initialising : 1;
+	unsigned char restart      : 1;
+};
+
+#define I82586_NULL (-1)
+
+typedef struct { /* tdr */
+	unsigned short tdr_status;
+	unsigned short tdr_command;
+	unsigned short tdr_link;
+	unsigned short tdr_result;
+#define TDR_TIME	(0x7ff)
+#define TDR_SHORT	(1 << 12)
+#define TDR_OPEN	(1 << 13)
+#define TDR_XCVRPROB	(1 << 14)
+#define TDR_LNKOK	(1 << 15)
+} tdr_t;
+
+typedef struct { /* transmit */
+	unsigned short tx_status;
+	unsigned short tx_command;
+	unsigned short tx_link;
+	unsigned short tx_tbdoffset;
+} tx_t;
+
+typedef struct { /* tbd */
+	unsigned short tbd_opts;
+#define TBD_CNT		(0x3fff)
+#define TBD_EOL		(1 << 15)
+	unsigned short tbd_link;
+	unsigned short tbd_bufl;
+	unsigned short tbd_bufh;
+} tbd_t;
+
+typedef struct { /* rfd */
+	unsigned short rfd_status;
+#define RFD_NOEOF	(1 << 6)
+#define RFD_FRAMESHORT	(1 << 7)
+#define RFD_DMAOVRN	(1 << 8)
+#define RFD_NORESOURCES	(1 << 9)
+#define RFD_ALIGNERROR	(1 << 10)
+#define RFD_CRCERROR	(1 << 11)
+#define RFD_OK		(1 << 13)
+#define RFD_FDCONSUMED	(1 << 14)
+#define RFD_COMPLETE	(1 << 15)
+	unsigned short rfd_command;
+#define RFD_CMDSUSPEND	(1 << 14)
+#define RFD_CMDEL	(1 << 15)
+	unsigned short rfd_link;
+	unsigned short rfd_rbdoffset;
+	unsigned char  rfd_dest[6];
+	unsigned char  rfd_src[6];
+	unsigned short rfd_len;
+} rfd_t;
+
+typedef struct { /* rbd */
+	unsigned short rbd_status;
+#define RBD_ACNT	(0x3fff)
+#define RBD_ACNTVALID	(1 << 14)
+#define RBD_EOF		(1 << 15)
+	unsigned short rbd_link;
+	unsigned short rbd_bufl;
+	unsigned short rbd_bufh;
+	unsigned short rbd_len;
+} rbd_t;
+
+typedef struct { /* nop */
+	unsigned short nop_status;
+	unsigned short nop_command;
+	unsigned short nop_link;
+} nop_t;
+
+typedef struct { /* set multicast */
+	unsigned short mc_status;
+	unsigned short mc_command;
+	unsigned short mc_link;
+	unsigned short mc_cnt;
+	unsigned char  mc_addrs[1][6];
+} mc_t;
+
+typedef struct { /* set address */
+	unsigned short sa_status;
+	unsigned short sa_command;
+	unsigned short sa_link;
+	unsigned char  sa_addr[6];
+} sa_t;
+
+typedef struct { /* config command */
+	unsigned short cfg_status;
+	unsigned short cfg_command;
+	unsigned short cfg_link;
+	unsigned char  cfg_bytecnt;	/* size foll data: 4 - 12		 */
+	unsigned char  cfg_fifolim;	/* FIFO threshold			 */
+	unsigned char  cfg_byte8;
+#define CFG8_SRDY	(1 << 6)
+#define CFG8_SAVEBADF	(1 << 7)
+	unsigned char  cfg_byte9;
+#define CFG9_ADDRLEN(x)	(x)
+#define CFG9_ADDRLENBUF	(1 << 3)
+#define CFG9_PREAMB2	(0 << 4)
+#define CFG9_PREAMB4	(1 << 4)
+#define CFG9_PREAMB8	(2 << 4)
+#define CFG9_PREAMB16	(3 << 4)
+#define CFG9_ILOOPBACK	(1 << 6)
+#define CFG9_ELOOPBACK	(1 << 7)
+	unsigned char  cfg_byte10;
+#define CFG10_LINPRI(x)	(x)
+#define CFG10_ACR(x)	(x << 4)
+#define CFG10_BOFMET	(1 << 7)
+	unsigned char  cfg_ifs;
+	unsigned char  cfg_slotl;
+	unsigned char  cfg_byte13;
+#define CFG13_SLOTH(x)	(x)
+#define CFG13_RETRY(x)	(x << 4)
+	unsigned char  cfg_byte14;
+#define CFG14_PROMISC	(1 << 0)
+#define CFG14_DISBRD	(1 << 1)
+#define CFG14_MANCH	(1 << 2)
+#define CFG14_TNCRS	(1 << 3)
+#define CFG14_NOCRC	(1 << 4)
+#define CFG14_CRC16	(1 << 5)
+#define CFG14_BTSTF	(1 << 6)
+#define CFG14_FLGPAD	(1 << 7)
+	unsigned char  cfg_byte15;
+#define CFG15_CSTF(x)	(x)
+#define CFG15_ICSS	(1 << 3)
+#define CFG15_CDTF(x)	(x << 4)
+#define CFG15_ICDS	(1 << 7)
+	unsigned short cfg_minfrmlen;
+} cfg_t;
+
+typedef struct { /* scb */
+	unsigned short scb_status;	/* status of 82586			*/
+#define SCB_STRXMASK		(7 << 4)	/* Receive unit status		*/
+#define SCB_STRXIDLE		(0 << 4)	/* Idle				*/
+#define SCB_STRXSUSP		(1 << 4)	/* Suspended			*/
+#define SCB_STRXNRES		(2 << 4)	/* No resources			*/
+#define SCB_STRXRDY		(4 << 4)	/* Ready			*/
+#define SCB_STCUMASK		(7 << 8)	/* Command unit status		*/
+#define SCB_STCUIDLE		(0 << 8)	/* Idle				*/
+#define SCB_STCUSUSP		(1 << 8)	/* Suspended			*/
+#define SCB_STCUACTV		(2 << 8)	/* Active			*/
+#define SCB_STRNR		(1 << 12)	/* Receive unit not ready	*/
+#define SCB_STCNA		(1 << 13)	/* Command unit not ready	*/
+#define SCB_STFR		(1 << 14)	/* Frame received		*/
+#define SCB_STCX		(1 << 15)	/* Command completed		*/
+	unsigned short scb_command;	/* Next command				*/
+#define SCB_CMDRXSTART		(1 << 4)	/* Start (at rfa_offset)	*/
+#define SCB_CMDRXRESUME		(2 << 4)	/* Resume reception		*/
+#define SCB_CMDRXSUSPEND	(3 << 4)	/* Suspend reception		*/
+#define SCB_CMDRXABORT		(4 << 4)	/* Abort reception		*/
+#define SCB_CMDCUCSTART		(1 << 8)	/* Start (at cbl_offset)	*/
+#define SCB_CMDCUCRESUME	(2 << 8)	/* Resume execution		*/
+#define SCB_CMDCUCSUSPEND	(3 << 8)	/* Suspend execution		*/
+#define SCB_CMDCUCABORT		(4 << 8)	/* Abort execution		*/
+#define SCB_CMDACKRNR		(1 << 12)	/* Ack RU not ready		*/
+#define SCB_CMDACKCNA		(1 << 13)	/* Ack CU not ready		*/
+#define SCB_CMDACKFR		(1 << 14)	/* Ack Frame received		*/
+#define SCB_CMDACKCX		(1 << 15)	/* Ack Command complete		*/
+	unsigned short scb_cbl_offset;	/* Offset of first command unit		*/
+	unsigned short scb_rfa_offset;	/* Offset of first receive frame area	*/
+	unsigned short scb_crc_errors;	/* Properly aligned frame with CRC error*/
+	unsigned short scb_aln_errors;	/* Misaligned frames			*/
+	unsigned short scb_rsc_errors;	/* Frames lost due to no space		*/
+	unsigned short scb_ovn_errors;	/* Frames lost due to slow bus		*/
+} scb_t;
+
+typedef struct { /* iscp */
+	unsigned short iscp_busy;	/* set by CPU before CA			*/
+	unsigned short iscp_offset;	/* offset of SCB			*/
+	unsigned short iscp_basel;	/* base of SCB				*/
+	unsigned short iscp_baseh;
+} iscp_t;
+
+    /* this address must be 0xfff6 */
+typedef struct { /* scp */
+	unsigned short scp_sysbus;	/* bus size */
+#define SCP_SY_16BBUS	0x00
+#define SCP_SY_8BBUS	0x01
+	unsigned short scp_junk[2];	/* junk */
+	unsigned short scp_iscpl;	/* lower 16 bits of iscp */
+	unsigned short scp_iscph;	/* upper 16 bits of iscp */
+} scp_t;
+
+/* commands */
+#define CMD_NOP			0
+#define CMD_SETADDRESS		1
+#define CMD_CONFIG		2
+#define CMD_SETMULTICAST	3
+#define CMD_TX			4
+#define CMD_TDR			5
+#define CMD_DUMP		6
+#define CMD_DIAGNOSE		7
+
+#define CMD_MASK		7
+
+#define CMD_INTR		(1 << 13)
+#define CMD_SUSP		(1 << 14)
+#define CMD_EOL			(1 << 15)
+
+#define STAT_COLLISIONS		(15)
+#define STAT_COLLEXCESSIVE	(1 << 5)
+#define STAT_COLLAFTERTX	(1 << 6)
+#define STAT_TXDEFERRED		(1 << 7)
+#define STAT_TXSLOWDMA		(1 << 8)
+#define STAT_TXLOSTCTS		(1 << 9)
+#define STAT_NOCARRIER		(1 << 10)
+#define STAT_FAIL		(1 << 11)
+#define STAT_ABORTED		(1 << 12)
+#define STAT_OK			(1 << 13)
+#define STAT_BUSY		(1 << 14)
+#define STAT_COMPLETE		(1 << 15)
+#endif
+#endif
+
+/*
+ * Ether1 card definitions:
+ *
+ * FAST accesses:
+ *	+0	Page register
+ * 			16 pages
+ *	+4	Control
+ *			'1' = reset
+ *			'2' = loopback
+ *			'4' = CA
+ *			'8' = int ack
+ *
+ * RAM at address + 0x2000
+ * Pod. Prod id = 3
+ * Words after ID block [base + 8 words]
+ *	+0 pcb issue (0x0c and 0xf3 invalid)
+ *	+1 - +6 eth hw address
+ */
diff --git a/drivers/net/arm/ether3.c b/drivers/net/arm/ether3.c
new file mode 100644
index 0000000..1cc53ab
--- /dev/null
+++ b/drivers/net/arm/ether3.c
@@ -0,0 +1,936 @@
+/*
+ *  linux/drivers/acorn/net/ether3.c
+ *
+ *  Copyright (C) 1995-2000 Russell King
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * SEEQ nq8005 ethernet driver for Acorn/ANT Ether3 card
+ *  for Acorn machines
+ *
+ * By Russell King, with some suggestions from borris@ant.co.uk
+ *
+ * Changelog:
+ * 1.04	RMK	29/02/1996	Won't pass packets that are from our ethernet
+ *				address up to the higher levels - they're
+ *				silently ignored.  I/F can now be put into
+ *				multicast mode.  Receiver routine optimised.
+ * 1.05	RMK	30/02/1996	Now claims interrupt at open when part of
+ *				the kernel rather than when a module.
+ * 1.06	RMK	02/03/1996	Various code cleanups
+ * 1.07	RMK	13/10/1996	Optimised interrupt routine and transmit
+ *				routines.
+ * 1.08	RMK	14/10/1996	Fixed problem with too many packets,
+ *				prevented the kernel message about dropped
+ *				packets appearing too many times a second.
+ *				Now does not disable all IRQs, only the IRQ
+ *				used by this card.
+ * 1.09	RMK	10/11/1996	Only enables TX irq when buffer space is low,
+ *				but we still service the TX queue if we get a
+ *				RX interrupt.
+ * 1.10	RMK	15/07/1997	Fixed autoprobing of NQ8004.
+ * 1.11	RMK	16/11/1997	Fixed autoprobing of NQ8005A.
+ * 1.12	RMK	31/12/1997	Removed reference to dev_tint for Linux 2.1.
+ *      RMK	27/06/1998	Changed asm/delay.h to linux/delay.h.
+ * 1.13	RMK	29/06/1998	Fixed problem with transmission of packets.
+ *				Chip seems to have a bug in, whereby if the
+ *				packet starts two bytes from the end of the
+ *				buffer, it corrupts the receiver chain, and
+ *				never updates the transmit status correctly.
+ * 1.14	RMK	07/01/1998	Added initial code for ETHERB addressing.
+ * 1.15	RMK	30/04/1999	More fixes to the transmit routine for buggy
+ *				hardware.
+ * 1.16	RMK	10/02/2000	Updated for 2.3.43
+ * 1.17	RMK	13/05/2000	Updated for 2.3.99-pre8
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+
+#include <asm/system.h>
+#include <asm/ecard.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+
+static char version[] __initdata = "ether3 ethernet driver (c) 1995-2000 R.M.King v1.17\n";
+
+#include "ether3.h"
+
+static unsigned int net_debug = NET_DEBUG;
+
+static void	ether3_setmulticastlist(struct net_device *dev);
+static int	ether3_rx(struct net_device *dev, unsigned int maxcnt);
+static void	ether3_tx(struct net_device *dev);
+static int	ether3_open (struct net_device *dev);
+static int	ether3_sendpacket (struct sk_buff *skb, struct net_device *dev);
+static irqreturn_t ether3_interrupt (int irq, void *dev_id, struct pt_regs *regs);
+static int	ether3_close (struct net_device *dev);
+static struct net_device_stats *ether3_getstats (struct net_device *dev);
+static void	ether3_setmulticastlist (struct net_device *dev);
+static void	ether3_timeout(struct net_device *dev);
+
+#define BUS_16		2
+#define BUS_8		1
+#define BUS_UNKNOWN	0
+
+/* --------------------------------------------------------------------------- */
+
+typedef enum {
+	buffer_write,
+	buffer_read
+} buffer_rw_t;
+
+/*
+ * ether3 read/write.  Slow things down a bit...
+ * The SEEQ8005 doesn't like us writing to its registers
+ * too quickly.
+ */
+static inline void ether3_outb(int v, const void __iomem *r)
+{
+	writeb(v, r);
+	udelay(1);
+}
+
+static inline void ether3_outw(int v, const void __iomem *r)
+{
+	writew(v, r);
+	udelay(1);
+}
+#define ether3_inb(r)		({ unsigned int __v = readb((r)); udelay(1); __v; })
+#define ether3_inw(r)		({ unsigned int __v = readw((r)); udelay(1); __v; })
+
+static int
+ether3_setbuffer(struct net_device *dev, buffer_rw_t read, int start)
+{
+	int timeout = 1000;
+
+	ether3_outw(priv(dev)->regs.config1 | CFG1_LOCBUFMEM, REG_CONFIG1);
+	ether3_outw(priv(dev)->regs.command | CMD_FIFOWRITE, REG_COMMAND);
+
+	while ((ether3_inw(REG_STATUS) & STAT_FIFOEMPTY) == 0) {
+		if (!timeout--) {
+			printk("%s: setbuffer broken\n", dev->name);
+			priv(dev)->broken = 1;
+			return 1;
+		}
+		udelay(1);
+	}
+
+	if (read == buffer_read) {
+		ether3_outw(start, REG_DMAADDR);
+		ether3_outw(priv(dev)->regs.command | CMD_FIFOREAD, REG_COMMAND);
+	} else {
+		ether3_outw(priv(dev)->regs.command | CMD_FIFOWRITE, REG_COMMAND);
+		ether3_outw(start, REG_DMAADDR);
+	}
+	return 0;
+}
+
+/*
+ * write data to the buffer memory
+ */
+#define ether3_writebuffer(dev,data,length)			\
+	writesw(REG_BUFWIN, (data), (length) >> 1)
+
+#define ether3_writeword(dev,data)				\
+	writew((data), REG_BUFWIN)
+
+#define ether3_writelong(dev,data)	{			\
+	void __iomem *reg_bufwin = REG_BUFWIN;			\
+	writew((data), reg_bufwin);				\
+	writew((data) >> 16, reg_bufwin);			\
+}
+
+/*
+ * read data from the buffer memory
+ */
+#define ether3_readbuffer(dev,data,length)			\
+	readsw(REG_BUFWIN, (data), (length) >> 1)
+
+#define ether3_readword(dev)					\
+	readw(REG_BUFWIN)
+
+#define ether3_readlong(dev)	 				\
+	readw(REG_BUFWIN) | (readw(REG_BUFWIN) << 16)
+
+/*
+ * Switch LED off...
+ */
+static void ether3_ledoff(unsigned long data)
+{
+	struct net_device *dev = (struct net_device *)data;
+	ether3_outw(priv(dev)->regs.config2 |= CFG2_CTRLO, REG_CONFIG2);
+}
+
+/*
+ * switch LED on...
+ */
+static inline void ether3_ledon(struct net_device *dev)
+{
+	del_timer(&priv(dev)->timer);
+	priv(dev)->timer.expires = jiffies + HZ / 50; /* leave on for 1/50th second */
+	priv(dev)->timer.data = (unsigned long)dev;
+	priv(dev)->timer.function = ether3_ledoff;
+	add_timer(&priv(dev)->timer);
+	if (priv(dev)->regs.config2 & CFG2_CTRLO)
+		ether3_outw(priv(dev)->regs.config2 &= ~CFG2_CTRLO, REG_CONFIG2);
+}
+
+/*
+ * Read the ethernet address string from the on board rom.
+ * This is an ascii string!!!
+ */
+static int __init
+ether3_addr(char *addr, struct expansion_card *ec)
+{
+	struct in_chunk_dir cd;
+	char *s;
+	
+	if (ecard_readchunk(&cd, ec, 0xf5, 0) && (s = strchr(cd.d.string, '('))) {
+		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;
+	}
+	/* I wonder if we should even let the user continue in this case
+	 *   - no, it would be better to disable the device
+	 */
+	printk(KERN_ERR "ether3: Couldn't read a valid MAC address from card.\n");
+	return -ENODEV;
+}
+
+/* --------------------------------------------------------------------------- */
+
+static int __init
+ether3_ramtest(struct net_device *dev, unsigned char byte)
+{
+	unsigned char *buffer = kmalloc(RX_END, GFP_KERNEL);
+	int i,ret = 0;
+	int max_errors = 4;
+	int bad = -1;
+
+	if (!buffer)
+		return 1;
+
+	memset(buffer, byte, RX_END);
+	ether3_setbuffer(dev, buffer_write, 0);
+	ether3_writebuffer(dev, buffer, TX_END);
+	ether3_setbuffer(dev, buffer_write, RX_START);
+	ether3_writebuffer(dev, buffer + RX_START, RX_LEN);
+	memset(buffer, byte ^ 0xff, RX_END);
+	ether3_setbuffer(dev, buffer_read, 0);
+	ether3_readbuffer(dev, buffer, TX_END);
+	ether3_setbuffer(dev, buffer_read, RX_START);
+	ether3_readbuffer(dev, buffer + RX_START, RX_LEN);
+
+	for (i = 0; i < RX_END; i++) {
+		if (buffer[i] != byte) {
+			if (max_errors > 0 && bad != buffer[i]) {
+				printk("%s: RAM failed with (%02X instead of %02X) at 0x%04X",
+				       dev->name, buffer[i], byte, i);
+				ret = 2;
+				max_errors--;
+				bad = i;
+			}
+		} else {
+			if (bad != -1) {
+				if (bad != i - 1)
+					printk(" - 0x%04X\n", i - 1);
+				printk("\n");
+				bad = -1;
+			}
+		}
+	}
+	if (bad != -1)
+		printk(" - 0xffff\n");
+	kfree(buffer);
+
+	return ret;
+}
+
+/* ------------------------------------------------------------------------------- */
+
+static int __init ether3_init_2(struct net_device *dev)
+{
+	int i;
+
+	priv(dev)->regs.config1 = CFG1_RECVCOMPSTAT0|CFG1_DMABURST8;
+	priv(dev)->regs.config2 = CFG2_CTRLO|CFG2_RECVCRC|CFG2_ERRENCRC;
+	priv(dev)->regs.command = 0;
+
+	/*
+	 * Set up our hardware address
+	 */
+	ether3_outw(priv(dev)->regs.config1 | CFG1_BUFSELSTAT0, REG_CONFIG1);
+	for (i = 0; i < 6; i++)
+		ether3_outb(dev->dev_addr[i], REG_BUFWIN);
+
+	if (dev->flags & IFF_PROMISC)
+		priv(dev)->regs.config1 |= CFG1_RECVPROMISC;
+	else if (dev->flags & IFF_MULTICAST)
+		priv(dev)->regs.config1 |= CFG1_RECVSPECBRMULTI;
+	else
+		priv(dev)->regs.config1 |= CFG1_RECVSPECBROAD;
+
+	/*
+	 * There is a problem with the NQ8005 in that it occasionally loses the
+	 * last two bytes.  To get round this problem, we receive the CRC as
+	 * well.  That way, if we do lose the last two, then it doesn't matter.
+	 */
+	ether3_outw(priv(dev)->regs.config1 | CFG1_TRANSEND, REG_CONFIG1);
+	ether3_outw((TX_END>>8) - 1, REG_BUFWIN);
+	ether3_outw(priv(dev)->rx_head, REG_RECVPTR);
+	ether3_outw(0, REG_TRANSMITPTR);
+	ether3_outw(priv(dev)->rx_head >> 8, REG_RECVEND);
+	ether3_outw(priv(dev)->regs.config2, REG_CONFIG2);
+	ether3_outw(priv(dev)->regs.config1 | CFG1_LOCBUFMEM, REG_CONFIG1);
+	ether3_outw(priv(dev)->regs.command, REG_COMMAND);
+
+	i = ether3_ramtest(dev, 0x5A);
+	if(i)
+		return i;
+	i = ether3_ramtest(dev, 0x1E);
+	if(i)
+		return i;
+
+	ether3_setbuffer(dev, buffer_write, 0);
+	ether3_writelong(dev, 0);
+	return 0;
+}
+
+static void
+ether3_init_for_open(struct net_device *dev)
+{
+	int i;
+
+	memset(&priv(dev)->stats, 0, sizeof(struct net_device_stats));
+
+	/* Reset the chip */
+	ether3_outw(CFG2_RESET, REG_CONFIG2);
+	udelay(4);
+
+	priv(dev)->regs.command = 0;
+	ether3_outw(CMD_RXOFF|CMD_TXOFF, REG_COMMAND);
+	while (ether3_inw(REG_STATUS) & (STAT_RXON|STAT_TXON))
+		barrier();
+
+	ether3_outw(priv(dev)->regs.config1 | CFG1_BUFSELSTAT0, REG_CONFIG1);
+	for (i = 0; i < 6; i++)
+		ether3_outb(dev->dev_addr[i], REG_BUFWIN);
+
+	priv(dev)->tx_head	= 0;
+	priv(dev)->tx_tail	= 0;
+	priv(dev)->regs.config2 |= CFG2_CTRLO;
+	priv(dev)->rx_head	= RX_START;
+
+	ether3_outw(priv(dev)->regs.config1 | CFG1_TRANSEND, REG_CONFIG1);
+	ether3_outw((TX_END>>8) - 1, REG_BUFWIN);
+	ether3_outw(priv(dev)->rx_head, REG_RECVPTR);
+	ether3_outw(priv(dev)->rx_head >> 8, REG_RECVEND);
+	ether3_outw(0, REG_TRANSMITPTR);
+	ether3_outw(priv(dev)->regs.config2, REG_CONFIG2);
+	ether3_outw(priv(dev)->regs.config1 | CFG1_LOCBUFMEM, REG_CONFIG1);
+
+	ether3_setbuffer(dev, buffer_write, 0);
+	ether3_writelong(dev, 0);
+
+	priv(dev)->regs.command = CMD_ENINTRX | CMD_ENINTTX;
+	ether3_outw(priv(dev)->regs.command | CMD_RXON, REG_COMMAND);
+}
+
+static inline int
+ether3_probe_bus_8(struct net_device *dev, int val)
+{
+	int write_low, write_high, read_low, read_high;
+
+	write_low = val & 255;
+	write_high = val >> 8;
+
+	printk(KERN_DEBUG "ether3_probe: write8 [%02X:%02X]", write_high, write_low);
+
+	ether3_outb(write_low, REG_RECVPTR);
+	ether3_outb(write_high, REG_RECVPTR + 4);
+
+	read_low = ether3_inb(REG_RECVPTR);
+	read_high = ether3_inb(REG_RECVPTR + 4);
+
+	printk(", read8 [%02X:%02X]\n", read_high, read_low);
+
+	return read_low == write_low && read_high == write_high;
+}
+
+static inline int
+ether3_probe_bus_16(struct net_device *dev, int val)
+{
+	int read_val;
+
+	ether3_outw(val, REG_RECVPTR);
+	read_val = ether3_inw(REG_RECVPTR);
+
+	printk(KERN_DEBUG "ether3_probe: write16 [%04X], read16 [%04X]\n", val, read_val);
+
+	return read_val == val;
+}
+
+/*
+ * 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
+ether3_open(struct net_device *dev)
+{
+	if (!is_valid_ether_addr(dev->dev_addr)) {
+		printk(KERN_WARNING "%s: invalid ethernet MAC address\n",
+			dev->name);
+		return -EINVAL;
+	}
+
+	if (request_irq(dev->irq, ether3_interrupt, 0, "ether3", dev))
+		return -EAGAIN;
+
+	ether3_init_for_open(dev);
+
+	netif_start_queue(dev);
+
+	return 0;
+}
+
+/*
+ * The inverse routine to ether3_open().
+ */
+static int
+ether3_close(struct net_device *dev)
+{
+	netif_stop_queue(dev);
+
+	disable_irq(dev->irq);
+
+	ether3_outw(CMD_RXOFF|CMD_TXOFF, REG_COMMAND);
+	priv(dev)->regs.command = 0;
+	while (ether3_inw(REG_STATUS) & (STAT_RXON|STAT_TXON))
+		barrier();
+	ether3_outb(0x80, REG_CONFIG2 + 4);
+	ether3_outw(0, REG_COMMAND);
+
+	free_irq(dev->irq, dev);
+
+	return 0;
+}
+
+/*
+ * Get the current statistics.	This may be called with the card open or
+ * closed.
+ */
+static struct net_device_stats *ether3_getstats(struct net_device *dev)
+{
+	return &priv(dev)->stats;
+}
+
+/*
+ * Set or clear promiscuous/multicast mode filter for this adaptor.
+ *
+ * We don't attempt any packet filtering.  The card may have a SEEQ 8004
+ * in which does not have the other ethernet address registers present...
+ */
+static void ether3_setmulticastlist(struct net_device *dev)
+{
+	priv(dev)->regs.config1 &= ~CFG1_RECVPROMISC;
+
+	if (dev->flags & IFF_PROMISC) {
+		/* promiscuous mode */
+		priv(dev)->regs.config1 |= CFG1_RECVPROMISC;
+	} else if (dev->flags & IFF_ALLMULTI) {
+		priv(dev)->regs.config1 |= CFG1_RECVSPECBRMULTI;
+	} else
+		priv(dev)->regs.config1 |= CFG1_RECVSPECBROAD;
+
+	ether3_outw(priv(dev)->regs.config1 | CFG1_LOCBUFMEM, REG_CONFIG1);
+}
+
+static void ether3_timeout(struct net_device *dev)
+{
+	unsigned long flags;
+
+	del_timer(&priv(dev)->timer);
+
+	local_irq_save(flags);
+	printk(KERN_ERR "%s: transmit timed out, network cable problem?\n", dev->name);
+	printk(KERN_ERR "%s: state: { status=%04X cfg1=%04X cfg2=%04X }\n", dev->name,
+		ether3_inw(REG_STATUS), ether3_inw(REG_CONFIG1), ether3_inw(REG_CONFIG2));
+	printk(KERN_ERR "%s: { rpr=%04X rea=%04X tpr=%04X }\n", dev->name,
+		ether3_inw(REG_RECVPTR), ether3_inw(REG_RECVEND), ether3_inw(REG_TRANSMITPTR));
+	printk(KERN_ERR "%s: tx head=%X tx tail=%X\n", dev->name,
+		priv(dev)->tx_head, priv(dev)->tx_tail);
+	ether3_setbuffer(dev, buffer_read, priv(dev)->tx_tail);
+	printk(KERN_ERR "%s: packet status = %08X\n", dev->name, ether3_readlong(dev));
+	local_irq_restore(flags);
+
+	priv(dev)->regs.config2 |= CFG2_CTRLO;
+	priv(dev)->stats.tx_errors += 1;
+	ether3_outw(priv(dev)->regs.config2, REG_CONFIG2);
+	priv(dev)->tx_head = priv(dev)->tx_tail = 0;
+
+	netif_wake_queue(dev);
+}
+
+/*
+ * Transmit a packet
+ */
+static int
+ether3_sendpacket(struct sk_buff *skb, struct net_device *dev)
+{
+	unsigned long flags;
+	unsigned int length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
+	unsigned int ptr, next_ptr;
+
+	if (priv(dev)->broken) {
+		dev_kfree_skb(skb);
+		priv(dev)->stats.tx_dropped ++;
+		netif_start_queue(dev);
+		return 0;
+	}
+
+	length = (length + 1) & ~1;
+	if (length != skb->len) {
+		skb = skb_padto(skb, length);
+		if (skb == NULL)
+			goto out;
+	}
+
+	next_ptr = (priv(dev)->tx_head + 1) & 15;
+
+	local_irq_save(flags);
+
+	if (priv(dev)->tx_tail == next_ptr) {
+		local_irq_restore(flags);
+		return 1;	/* unable to queue */
+	}
+
+	dev->trans_start = jiffies;
+	ptr		 = 0x600 * priv(dev)->tx_head;
+	priv(dev)->tx_head = next_ptr;
+	next_ptr	*= 0x600;
+
+#define TXHDR_FLAGS (TXHDR_TRANSMIT|TXHDR_CHAINCONTINUE|TXHDR_DATAFOLLOWS|TXHDR_ENSUCCESS)
+
+	ether3_setbuffer(dev, buffer_write, next_ptr);
+	ether3_writelong(dev, 0);
+	ether3_setbuffer(dev, buffer_write, ptr);
+	ether3_writelong(dev, 0);
+	ether3_writebuffer(dev, skb->data, length);
+	ether3_writeword(dev, htons(next_ptr));
+	ether3_writeword(dev, TXHDR_CHAINCONTINUE >> 16);
+	ether3_setbuffer(dev, buffer_write, ptr);
+	ether3_writeword(dev, htons((ptr + length + 4)));
+	ether3_writeword(dev, TXHDR_FLAGS >> 16);
+	ether3_ledon(dev);
+
+	if (!(ether3_inw(REG_STATUS) & STAT_TXON)) {
+		ether3_outw(ptr, REG_TRANSMITPTR);
+		ether3_outw(priv(dev)->regs.command | CMD_TXON, REG_COMMAND);
+	}
+
+	next_ptr = (priv(dev)->tx_head + 1) & 15;
+	local_irq_restore(flags);
+
+	dev_kfree_skb(skb);
+
+	if (priv(dev)->tx_tail == next_ptr)
+		netif_stop_queue(dev);
+
+ out:
+	return 0;
+}
+
+static irqreturn_t
+ether3_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct net_device *dev = (struct net_device *)dev_id;
+	unsigned int status, handled = IRQ_NONE;
+
+#if NET_DEBUG > 1
+	if(net_debug & DEBUG_INT)
+		printk("eth3irq: %d ", irq);
+#endif
+
+	status = ether3_inw(REG_STATUS);
+
+	if (status & STAT_INTRX) {
+		ether3_outw(CMD_ACKINTRX | priv(dev)->regs.command, REG_COMMAND);
+		ether3_rx(dev, 12);
+		handled = IRQ_HANDLED;
+	}
+
+	if (status & STAT_INTTX) {
+		ether3_outw(CMD_ACKINTTX | priv(dev)->regs.command, REG_COMMAND);
+		ether3_tx(dev);
+		handled = IRQ_HANDLED;
+	}
+
+#if NET_DEBUG > 1
+	if(net_debug & DEBUG_INT)
+		printk("done\n");
+#endif
+	return handled;
+}
+
+/*
+ * If we have a good packet(s), get it/them out of the buffers.
+ */
+static int ether3_rx(struct net_device *dev, unsigned int maxcnt)
+{
+	unsigned int next_ptr = priv(dev)->rx_head, received = 0;
+
+	ether3_ledon(dev);
+
+	do {
+		unsigned int this_ptr, status;
+		unsigned char addrs[16];
+
+		/*
+		 * read the first 16 bytes from the buffer.
+		 * This contains the status bytes etc and ethernet addresses,
+		 * and we also check the source ethernet address to see if
+		 * it originated from us.
+		 */
+		{
+			unsigned int temp_ptr;
+			ether3_setbuffer(dev, buffer_read, next_ptr);
+			temp_ptr = ether3_readword(dev);
+			status = ether3_readword(dev);
+			if ((status & (RXSTAT_DONE | RXHDR_CHAINCONTINUE | RXHDR_RECEIVE)) !=
+				(RXSTAT_DONE | RXHDR_CHAINCONTINUE) || !temp_ptr)
+				break;
+
+			this_ptr = next_ptr + 4;
+			next_ptr = ntohs(temp_ptr);
+		}
+		ether3_setbuffer(dev, buffer_read, this_ptr);
+		ether3_readbuffer(dev, addrs+2, 12);
+
+if (next_ptr < RX_START || next_ptr >= RX_END) {
+ int i;
+ printk("%s: bad next pointer @%04X: ", dev->name, priv(dev)->rx_head);
+ printk("%02X %02X %02X %02X ", next_ptr >> 8, next_ptr & 255, status & 255, status >> 8);
+ for (i = 2; i < 14; i++)
+   printk("%02X ", addrs[i]);
+ printk("\n");
+ next_ptr = priv(dev)->rx_head;
+ break;
+}
+		/*
+ 		 * ignore our own packets...
+	 	 */
+		if (!(*(unsigned long *)&dev->dev_addr[0] ^ *(unsigned long *)&addrs[2+6]) &&
+		    !(*(unsigned short *)&dev->dev_addr[4] ^ *(unsigned short *)&addrs[2+10])) {
+			maxcnt ++; /* compensate for loopedback packet */
+			ether3_outw(next_ptr >> 8, REG_RECVEND);
+		} else
+		if (!(status & (RXSTAT_OVERSIZE|RXSTAT_CRCERROR|RXSTAT_DRIBBLEERROR|RXSTAT_SHORTPACKET))) {
+			unsigned int length = next_ptr - this_ptr;
+			struct sk_buff *skb;
+
+			if (next_ptr <= this_ptr)
+				length += RX_END - RX_START;
+
+			skb = dev_alloc_skb(length + 2);
+			if (skb) {
+				unsigned char *buf;
+
+				skb->dev = dev;
+				skb_reserve(skb, 2);
+				buf = skb_put(skb, length);
+				ether3_readbuffer(dev, buf + 12, length - 12);
+				ether3_outw(next_ptr >> 8, REG_RECVEND);
+				*(unsigned short *)(buf + 0)	= *(unsigned short *)(addrs + 2);
+				*(unsigned long *)(buf + 2)	= *(unsigned long *)(addrs + 4);
+				*(unsigned long *)(buf + 6)	= *(unsigned long *)(addrs + 8);
+				*(unsigned short *)(buf + 10)	= *(unsigned short *)(addrs + 12);
+				skb->protocol = eth_type_trans(skb, dev);
+				netif_rx(skb);
+				received ++;
+			} else
+				goto dropping;
+		} else {
+			struct net_device_stats *stats = &priv(dev)->stats;
+			ether3_outw(next_ptr >> 8, REG_RECVEND);
+			if (status & RXSTAT_OVERSIZE)	  stats->rx_over_errors ++;
+			if (status & RXSTAT_CRCERROR)	  stats->rx_crc_errors ++;
+			if (status & RXSTAT_DRIBBLEERROR) stats->rx_fifo_errors ++;
+			if (status & RXSTAT_SHORTPACKET)  stats->rx_length_errors ++;
+			stats->rx_errors++;
+		}
+	}
+	while (-- maxcnt);
+
+done:
+	priv(dev)->stats.rx_packets += received;
+	priv(dev)->rx_head = next_ptr;
+	/*
+	 * If rx went off line, then that means that the buffer may be full.  We
+	 * have dropped at least one packet.
+	 */
+	if (!(ether3_inw(REG_STATUS) & STAT_RXON)) {
+		priv(dev)->stats.rx_dropped ++;
+    		ether3_outw(next_ptr, REG_RECVPTR);
+		ether3_outw(priv(dev)->regs.command | CMD_RXON, REG_COMMAND);
+	}
+
+	return maxcnt;
+
+dropping:{
+	static unsigned long last_warned;
+
+	ether3_outw(next_ptr >> 8, REG_RECVEND);
+	/*
+	 * Don't print this message too many times...
+	 */
+	if (time_after(jiffies, last_warned + 10 * HZ)) {
+		last_warned = jiffies;
+		printk("%s: memory squeeze, dropping packet.\n", dev->name);
+	}
+	priv(dev)->stats.rx_dropped ++;
+	goto done;
+	}
+}
+
+/*
+ * Update stats for the transmitted packet(s)
+ */
+static void ether3_tx(struct net_device *dev)
+{
+	unsigned int tx_tail = priv(dev)->tx_tail;
+	int max_work = 14;
+
+	do {
+	    	unsigned long status;
+
+    		/*
+	    	 * Read the packet header
+    		 */
+	    	ether3_setbuffer(dev, buffer_read, tx_tail * 0x600);
+    		status = ether3_readlong(dev);
+
+		/*
+		 * Check to see if this packet has been transmitted
+		 */
+		if ((status & (TXSTAT_DONE | TXHDR_TRANSMIT)) !=
+		    (TXSTAT_DONE | TXHDR_TRANSMIT))
+			break;
+
+		/*
+		 * Update errors
+		 */
+		if (!(status & (TXSTAT_BABBLED | TXSTAT_16COLLISIONS)))
+			priv(dev)->stats.tx_packets++;
+		else {
+			priv(dev)->stats.tx_errors ++;
+			if (status & TXSTAT_16COLLISIONS)
+				priv(dev)->stats.collisions += 16;
+			if (status & TXSTAT_BABBLED)
+				priv(dev)->stats.tx_fifo_errors ++;
+		}
+
+		tx_tail = (tx_tail + 1) & 15;
+	} while (--max_work);
+
+	if (priv(dev)->tx_tail != tx_tail) {
+		priv(dev)->tx_tail = tx_tail;
+		netif_wake_queue(dev);
+	}
+}
+
+static void __init ether3_banner(void)
+{
+	static unsigned version_printed = 0;
+
+	if (net_debug && version_printed++ == 0)
+		printk(KERN_INFO "%s", version);
+}
+
+static int __devinit
+ether3_probe(struct expansion_card *ec, const struct ecard_id *id)
+{
+	const struct ether3_data *data = id->data;
+	struct net_device *dev;
+	int i, bus_type, ret;
+
+	ether3_banner();
+
+	ret = ecard_request_resources(ec);
+	if (ret)
+		goto out;
+
+	dev = alloc_etherdev(sizeof(struct dev_priv));
+	if (!dev) {
+		ret = -ENOMEM;
+		goto release;
+	}
+
+	SET_MODULE_OWNER(dev);
+	SET_NETDEV_DEV(dev, &ec->dev);
+
+	priv(dev)->base = ioremap(ecard_resource_start(ec, ECARD_RES_MEMC),
+				  ecard_resource_len(ec, ECARD_RES_MEMC));
+	if (!priv(dev)->base) {
+		ret = -ENOMEM;
+		goto free;
+	}
+
+	ec->irqaddr = priv(dev)->base + data->base_offset;
+	ec->irqmask = 0xf0;
+
+	priv(dev)->seeq = priv(dev)->base + data->base_offset;
+	dev->irq = ec->irq;
+
+	ether3_addr(dev->dev_addr, ec);
+
+	init_timer(&priv(dev)->timer);
+
+	/* Reset card...
+	 */
+	ether3_outb(0x80, REG_CONFIG2 + 4);
+	bus_type = BUS_UNKNOWN;
+	udelay(4);
+
+	/* Test using Receive Pointer (16-bit register) to find out
+	 * how the ether3 is connected to the bus...
+	 */
+	if (ether3_probe_bus_8(dev, 0x100) &&
+	    ether3_probe_bus_8(dev, 0x201))
+		bus_type = BUS_8;
+
+	if (bus_type == BUS_UNKNOWN &&
+	    ether3_probe_bus_16(dev, 0x101) &&
+	    ether3_probe_bus_16(dev, 0x201))
+		bus_type = BUS_16;
+
+	switch (bus_type) {
+	case BUS_UNKNOWN:
+		printk(KERN_ERR "%s: unable to identify bus width\n", dev->name);
+		ret = -ENODEV;
+		goto free;
+
+	case BUS_8:
+		printk(KERN_ERR "%s: %s found, but is an unsupported "
+			"8-bit card\n", dev->name, data->name);
+		ret = -ENODEV;
+		goto free;
+
+	default:
+		break;
+	}
+
+	if (ether3_init_2(dev)) {
+		ret = -ENODEV;
+		goto free;
+	}
+
+	dev->open		= ether3_open;
+	dev->stop		= ether3_close;
+	dev->hard_start_xmit	= ether3_sendpacket;
+	dev->get_stats		= ether3_getstats;
+	dev->set_multicast_list	= ether3_setmulticastlist;
+	dev->tx_timeout		= ether3_timeout;
+	dev->watchdog_timeo	= 5 * HZ / 100;
+
+	ret = register_netdev(dev);
+	if (ret)
+		goto free;
+
+	printk("%s: %s in slot %d, ", dev->name, data->name, ec->slot_no);
+	for (i = 0; i < 6; i++)
+		printk("%2.2x%c", dev->dev_addr[i], i == 5 ? '\n' : ':');
+
+	ecard_set_drvdata(ec, dev);
+	return 0;
+
+ free:
+	if (priv(dev)->base)
+		iounmap(priv(dev)->base);
+	free_netdev(dev);
+ release:
+	ecard_release_resources(ec);
+ out:
+	return ret;
+}
+
+static void __devexit ether3_remove(struct expansion_card *ec)
+{
+	struct net_device *dev = ecard_get_drvdata(ec);
+
+	ecard_set_drvdata(ec, NULL);
+
+	unregister_netdev(dev);
+	iounmap(priv(dev)->base);
+	free_netdev(dev);
+	ecard_release_resources(ec);
+}
+
+static struct ether3_data ether3 = {
+	.name		= "ether3",
+	.base_offset	= 0,
+};
+
+static struct ether3_data etherb = {
+	.name		= "etherb",
+	.base_offset	= 0x800,
+};
+
+static const struct ecard_id ether3_ids[] = {
+	{ MANU_ANT2, PROD_ANT_ETHER3, &ether3 },
+	{ MANU_ANT,  PROD_ANT_ETHER3, &ether3 },
+	{ MANU_ANT,  PROD_ANT_ETHERB, &etherb },
+	{ 0xffff, 0xffff }
+};
+
+static struct ecard_driver ether3_driver = {
+	.probe		= ether3_probe,
+	.remove		= __devexit_p(ether3_remove),
+	.id_table	= ether3_ids,
+	.drv = {
+		.name	= "ether3",
+	},
+};
+
+static int __init ether3_init(void)
+{
+	return ecard_register_driver(&ether3_driver);
+}
+
+static void __exit ether3_exit(void)
+{
+	ecard_remove_driver(&ether3_driver);
+}
+
+module_init(ether3_init);
+module_exit(ether3_exit);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/arm/ether3.h b/drivers/net/arm/ether3.h
new file mode 100644
index 0000000..1921a3a
--- /dev/null
+++ b/drivers/net/arm/ether3.h
@@ -0,0 +1,177 @@
+/*
+ *  linux/drivers/acorn/net/ether3.h
+ *
+ *  Copyright (C) 1995-2000 Russell King
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ *  network driver for Acorn/ANT Ether3 cards
+ */
+
+#ifndef _LINUX_ether3_H
+#define _LINUX_ether3_H
+
+/* use 0 for production, 1 for verification, >2 for debug. debug flags: */
+#define DEBUG_TX	 2
+#define DEBUG_RX	 4
+#define DEBUG_INT	 8
+#define DEBUG_IC	16
+#ifndef NET_DEBUG
+#define NET_DEBUG 	0
+#endif
+
+#define priv(dev)	((struct dev_priv *)netdev_priv(dev))
+
+/* Command register definitions & bits */
+#define REG_COMMAND		(priv(dev)->seeq + 0x0000)
+#define CMD_ENINTDMA		0x0001
+#define CMD_ENINTRX		0x0002
+#define CMD_ENINTTX		0x0004
+#define CMD_ENINTBUFWIN		0x0008
+#define CMD_ACKINTDMA		0x0010
+#define CMD_ACKINTRX		0x0020
+#define CMD_ACKINTTX		0x0040
+#define CMD_ACKINTBUFWIN	0x0080
+#define CMD_DMAON		0x0100
+#define CMD_RXON		0x0200
+#define CMD_TXON		0x0400
+#define CMD_DMAOFF		0x0800
+#define CMD_RXOFF		0x1000
+#define CMD_TXOFF		0x2000
+#define CMD_FIFOREAD		0x4000
+#define CMD_FIFOWRITE		0x8000
+
+/* status register */
+#define REG_STATUS		(priv(dev)->seeq + 0x0000)
+#define STAT_ENINTSTAT		0x0001
+#define STAT_ENINTRX		0x0002
+#define STAT_ENINTTX		0x0004
+#define STAT_ENINTBUFWIN	0x0008
+#define STAT_INTDMA		0x0010
+#define STAT_INTRX		0x0020
+#define STAT_INTTX		0x0040
+#define STAT_INTBUFWIN		0x0080
+#define STAT_DMAON		0x0100
+#define STAT_RXON		0x0200
+#define STAT_TXON		0x0400
+#define STAT_FIFOFULL		0x2000
+#define STAT_FIFOEMPTY		0x4000
+#define STAT_FIFODIR		0x8000
+
+/* configuration register 1 */
+#define REG_CONFIG1		(priv(dev)->seeq + 0x0040)
+#define CFG1_BUFSELSTAT0	0x0000
+#define CFG1_BUFSELSTAT1	0x0001
+#define CFG1_BUFSELSTAT2	0x0002
+#define CFG1_BUFSELSTAT3	0x0003
+#define CFG1_BUFSELSTAT4	0x0004
+#define CFG1_BUFSELSTAT5	0x0005
+#define CFG1_ADDRPROM		0x0006
+#define CFG1_TRANSEND		0x0007
+#define CFG1_LOCBUFMEM		0x0008
+#define CFG1_INTVECTOR		0x0009
+#define CFG1_RECVSPECONLY	0x0000
+#define CFG1_RECVSPECBROAD	0x4000
+#define CFG1_RECVSPECBRMULTI	0x8000
+#define CFG1_RECVPROMISC	0xC000
+
+/* The following aren't in 8004 */
+#define CFG1_DMABURSTCONT	0x0000
+#define CFG1_DMABURST800NS	0x0010
+#define CFG1_DMABURST1600NS	0x0020
+#define CFG1_DMABURST3200NS	0x0030
+#define CFG1_DMABURST1		0x0000
+#define CFG1_DMABURST4		0x0040
+#define CFG1_DMABURST8		0x0080
+#define CFG1_DMABURST16		0x00C0
+#define CFG1_RECVCOMPSTAT0	0x0100
+#define CFG1_RECVCOMPSTAT1	0x0200
+#define CFG1_RECVCOMPSTAT2	0x0400
+#define CFG1_RECVCOMPSTAT3	0x0800
+#define CFG1_RECVCOMPSTAT4	0x1000
+#define CFG1_RECVCOMPSTAT5	0x2000
+
+/* configuration register 2 */
+#define REG_CONFIG2		(priv(dev)->seeq + 0x0080)
+#define CFG2_BYTESWAP		0x0001
+#define CFG2_ERRENCRC		0x0008
+#define CFG2_ERRENDRIBBLE	0x0010
+#define CFG2_ERRSHORTFRAME	0x0020
+#define CFG2_SLOTSELECT		0x0040
+#define CFG2_PREAMSELECT	0x0080
+#define CFG2_ADDRLENGTH		0x0100
+#define CFG2_RECVCRC		0x0200
+#define CFG2_XMITNOCRC		0x0400
+#define CFG2_LOOPBACK		0x0800
+#define CFG2_CTRLO		0x1000
+#define CFG2_RESET		0x8000
+
+#define REG_RECVEND		(priv(dev)->seeq + 0x00c0)
+
+#define REG_BUFWIN		(priv(dev)->seeq + 0x0100)
+
+#define REG_RECVPTR		(priv(dev)->seeq + 0x0140)
+
+#define REG_TRANSMITPTR		(priv(dev)->seeq + 0x0180)
+
+#define REG_DMAADDR		(priv(dev)->seeq + 0x01c0)
+
+/*
+ * Cards transmit/receive headers
+ */
+#define TX_NEXT			(0xffff)
+#define TXHDR_ENBABBLEINT	(1 << 16)
+#define TXHDR_ENCOLLISIONINT	(1 << 17)
+#define TXHDR_EN16COLLISION	(1 << 18)
+#define TXHDR_ENSUCCESS		(1 << 19)
+#define TXHDR_DATAFOLLOWS	(1 << 21)
+#define TXHDR_CHAINCONTINUE	(1 << 22)
+#define TXHDR_TRANSMIT		(1 << 23)
+#define TXSTAT_BABBLED		(1 << 24)
+#define TXSTAT_COLLISION	(1 << 25)
+#define TXSTAT_16COLLISIONS	(1 << 26)
+#define TXSTAT_DONE		(1 << 31)
+
+#define RX_NEXT			(0xffff)
+#define RXHDR_CHAINCONTINUE	(1 << 6)
+#define RXHDR_RECEIVE		(1 << 7)
+#define RXSTAT_OVERSIZE		(1 << 8)
+#define RXSTAT_CRCERROR		(1 << 9)
+#define RXSTAT_DRIBBLEERROR	(1 << 10)
+#define RXSTAT_SHORTPACKET	(1 << 11)
+#define RXSTAT_DONE		(1 << 15)
+
+
+#define TX_START	0x0000
+#define TX_END		0x6000
+#define RX_START	0x6000
+#define RX_LEN		0xA000
+#define RX_END		0x10000
+/* must be a power of 2 and greater than MAX_TX_BUFFERED */
+#define MAX_TXED	16
+#define MAX_TX_BUFFERED	10
+
+struct dev_priv {
+    void __iomem *base;
+    void __iomem *seeq;
+    struct {
+	unsigned int command;
+	unsigned int config1;
+	unsigned int config2;
+    } regs;
+    unsigned char tx_head;		/* buffer nr to insert next packet	 */
+    unsigned char tx_tail;		/* buffer nr of transmitting packet	 */
+    unsigned int rx_head;		/* address to fetch next packet from	 */
+    struct net_device_stats stats;
+    struct timer_list timer;
+    int broken;				/* 0 = ok, 1 = something went wrong	 */
+};
+
+struct ether3_data {
+	const char name[8];
+	unsigned long base_offset;
+};
+
+#endif
diff --git a/drivers/net/arm/etherh.c b/drivers/net/arm/etherh.c
new file mode 100644
index 0000000..942a281
--- /dev/null
+++ b/drivers/net/arm/etherh.c
@@ -0,0 +1,862 @@
+/*
+ *  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/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/slab.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 <asm/system.h>
+#include <asm/ecard.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#include "../8390.h"
+
+#define NET_DEBUG  0
+#define DEBUG_INIT 2
+
+#define DRV_NAME	"etherh"
+#define DRV_VERSION	"1.11"
+
+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");
+
+static char version[] __initdata =
+	"EtherH/EtherM Driver (c) 2002-2004 Russell King " DRV_VERSION "\n";
+
+#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 *)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 *)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 *)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 *)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 (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 *)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 *)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 __init 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",
+		       ec->dev.bus_id);
+		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",
+	       ec->dev.bus_id, 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->class_dev.dev->bus_id,
+		sizeof(info->bus_info));
+}
+
+static int etherh_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+	cmd->supported	= etherh_priv(dev)->supported;
+	cmd->speed	= 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 struct ethtool_ops etherh_ethtool_ops = {
+	.get_settings	= etherh_get_settings,
+	.set_settings	= etherh_set_settings,
+	.get_drvinfo	= etherh_get_drvinfo,
+};
+
+static u32 etherh_regoffsets[16];
+static u32 etherm_regoffsets[16];
+
+static int __init
+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 i, 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_MODULE_OWNER(dev);
+	SET_NETDEV_DEV(dev, &ec->dev);
+
+	dev->open		= etherh_open;
+	dev->stop		= etherh_close;
+	dev->set_config		= etherh_set_config;
+	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		= ioremap(ecard_resource_start(ec, ECARD_RES_MEMC), PAGE_SIZE);
+	if (!eh->memc) {
+		ret = -ENOMEM;
+		goto free;
+	}
+
+	eh->ctrl_port = eh->memc;
+	if (data->ctrl_ioc) {
+		eh->ioc_fast = ioremap(ecard_resource_start(ec, ECARD_RES_IOCFAST), 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) {
+		ec->ops		= &etherh_ops;
+		ec->irq_data	= 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, ",
+		dev->name, data->name, ec->slot_no);
+
+	for (i = 0; i < 6; i++)
+		printk("%2.2x%c", dev->dev_addr[i], i == 5 ? '\n' : ':');
+
+	ecard_set_drvdata(ec, dev);
+
+	return 0;
+
+ free:
+	if (eh->ioc_fast)
+		iounmap(eh->ioc_fast);
+	if (eh->memc)
+		iounmap(eh->memc);
+	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);
+	struct etherh_priv *eh = etherh_priv(dev);
+
+	ecard_set_drvdata(ec, NULL);
+
+	unregister_netdev(dev);
+	ec->ops = NULL;
+
+	if (eh->ioc_fast)
+		iounmap(eh->ioc_fast);
+	iounmap(eh->memc);
+
+	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);