smsc: Move the SMC (SMSC) drivers

Moves the SMC (SMSC) drivers into drivers/net/ethernet/smsc/ and the
necessary Kconfig and Makefile changes.  Also did some cleanup
of NET_VENDOR_SMC Kconfig tag for the 8390 based drivers.

CC: Nicolas Pitre <nico@fluxnic.net>
CC: Donald Becker <becker@scyld.com>
CC: Erik Stahlman <erik@vt.edu>
CC: Dustin McIntire <dustin@sensoria.com>
CC: Steve Glendinning <steve.glendinning@smsc.com>
CC: David Hinds <dahinds@users.sourceforge.net>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
diff --git a/drivers/net/ethernet/8390/Kconfig b/drivers/net/ethernet/8390/Kconfig
index 5cd53f1..f1b9bdd 100644
--- a/drivers/net/ethernet/8390/Kconfig
+++ b/drivers/net/ethernet/8390/Kconfig
@@ -264,22 +264,9 @@
 
 	  If unsure, say N.
 
-config NET_VENDOR_SMC
-	bool "Western Digital/SMC cards"
-	depends on (ISA || MCA || EISA || MAC)
-	---help---
-	  If you have a network (Ethernet) card belonging to this class, say Y
-	  and read the Ethernet-HOWTO, available from
-	  <http://www.tldp.org/docs.html#howto>.
-
-	  Note that the answer to this question doesn't directly affect the
-	  kernel: saying N will just cause the configurator to skip all
-	  the questions about Western Digital cards. If you say Y, you will be
-	  asked for your specific card in the following questions.
-
 config ULTRAMCA
 	tristate "SMC Ultra MCA support"
-	depends on NET_VENDOR_SMC && MCA
+	depends on MCA
 	select CRC32
 	---help---
 	  If you have a network (Ethernet) card of this type and are running
@@ -291,7 +278,7 @@
 
 config ULTRA
 	tristate "SMC Ultra support"
-	depends on NET_VENDOR_SMC && ISA
+	depends on ISA
 	select CRC32
 	---help---
 	  If you have a network (Ethernet) card of this type, say Y and read
@@ -310,7 +297,7 @@
 
 config ULTRA32
 	tristate "SMC Ultra32 EISA support"
-	depends on NET_VENDOR_SMC && EISA
+	depends on EISA
 	select CRC32
 	---help---
 	  If you have a network (Ethernet) card of this type, say Y and read
@@ -322,7 +309,7 @@
 
 config WD80x3
 	tristate "WD80*3 support"
-	depends on NET_VENDOR_SMC && ISA
+	depends on ISA
 	select CRC32
 	---help---
 	  If you have a network (Ethernet) card of this type, say Y and read
diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig
index ab591bb..ed5836c 100644
--- a/drivers/net/ethernet/Kconfig
+++ b/drivers/net/ethernet/Kconfig
@@ -18,5 +18,6 @@
 source "drivers/net/ethernet/chelsio/Kconfig"
 source "drivers/net/ethernet/intel/Kconfig"
 source "drivers/net/ethernet/qlogic/Kconfig"
+source "drivers/net/ethernet/smsc/Kconfig"
 
 endif # ETHERNET
diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile
index d8cf120..983fd27 100644
--- a/drivers/net/ethernet/Makefile
+++ b/drivers/net/ethernet/Makefile
@@ -9,3 +9,4 @@
 obj-$(CONFIG_NET_VENDOR_CHELSIO) += chelsio/
 obj-$(CONFIG_NET_VENDOR_INTEL) += intel/
 obj-$(CONFIG_NET_VENDOR_QLOGIC) += qlogic/
+obj-$(CONFIG_NET_VENDOR_SMSC) += smsc/
diff --git a/drivers/net/ethernet/smsc/Kconfig b/drivers/net/ethernet/smsc/Kconfig
new file mode 100644
index 0000000..702efe6
--- /dev/null
+++ b/drivers/net/ethernet/smsc/Kconfig
@@ -0,0 +1,131 @@
+#
+# Western Digital/SMC network device configuration
+#
+
+config NET_VENDOR_SMSC
+	bool "SMC (SMSC)/Western Digital devices"
+	depends on ARM || ISA || MAC || ARM || MIPS || M32R || SUPERH || \
+		BLACKFIN || MN10300 || COLDFIRE || PCI || PCMCIA
+	---help---
+	  If you have a network (Ethernet) card belonging to this class, say Y
+	  and read the Ethernet-HOWTO, available from
+	  <http://www.tldp.org/docs.html#howto>.
+
+	  Note that the answer to this question doesn't directly affect the
+	  kernel: saying N will just cause the configurator to skip all
+	  the questions about SMC/Western Digital cards. If you say Y, you will
+	  be asked for your specific card in the following questions.
+
+if NET_VENDOR_SMSC
+
+config SMC9194
+	tristate "SMC 9194 support"
+	depends on (ISA || MAC && BROKEN)
+	select CRC32
+	---help---
+	  This is support for the SMC9xxx based Ethernet cards. Choose this
+	  option if you have a DELL laptop with the docking station, or
+	  another SMC9192/9194 based chipset.  Say Y if you want it compiled
+	  into the kernel, and read the file
+	  <file:Documentation/networking/smc9.txt> and the Ethernet-HOWTO,
+	  available from <http://www.tldp.org/docs.html#howto>.
+
+	  To compile this driver as a module, choose M here. The module
+	  will be called smc9194.
+
+config SMC91X
+	tristate "SMC 91C9x/91C1xxx support"
+	select CRC32
+	select MII
+	depends on (ARM || M32R || SUPERH || MIPS || BLACKFIN || \
+		    MN10300 || COLDFIRE)
+	---help---
+	  This is a driver for SMC's 91x series of Ethernet chipsets,
+	  including the SMC91C94 and the SMC91C111. Say Y if you want it
+	  compiled into the kernel, and read the file
+	  <file:Documentation/networking/smc9.txt>  and the Ethernet-HOWTO,
+	  available from  <http://www.tldp.org/docs.html#howto>.
+
+	  This driver is also available as a module ( = code which can be
+	  inserted in and removed from the running kernel whenever you want).
+	  The module will be called smc91x.  If you want to compile it as a
+	  module, say M here and read <file:Documentation/kbuild/modules.txt>.
+
+config PCMCIA_SMC91C92
+	tristate "SMC 91Cxx PCMCIA support"
+	depends on PCMCIA
+	select CRC32
+	select MII
+	---help---
+	  Say Y here if you intend to attach an SMC 91Cxx compatible PCMCIA
+	  (PC-card) Ethernet or Fast Ethernet card to your computer.
+
+	  To compile this driver as a module, choose M here: the module will be
+	  called smc91c92_cs.  If unsure, say N.
+
+config EPIC100
+	tristate "SMC EtherPower II"
+	depends on PCI
+	select CRC32
+	select MII
+	---help---
+	  This driver is for the SMC EtherPower II 9432 PCI Ethernet NIC,
+	  which is based on the SMC83c17x (EPIC/100).
+	  More specific information and updates are available from
+	  <http://www.scyld.com/network/epic100.html>.
+
+config SMC911X
+	tristate "SMSC LAN911[5678] support"
+	select CRC32
+	select MII
+	depends on (ARM || SUPERH || MN10300)
+	---help---
+	  This is a driver for SMSC's LAN911x series of Ethernet chipsets
+	  including the new LAN9115, LAN9116, LAN9117, and LAN9118.
+	  Say Y if you want it compiled into the kernel,
+	  and read the Ethernet-HOWTO, available from
+	  <http://www.tldp.org/docs.html#howto>.
+
+	  This driver is also available as a module. The module will be
+	  called smc911x.  If you want to compile it as a module, say M
+	  here and read <file:Documentation/kbuild/modules.txt>
+
+config SMSC911X
+	tristate "SMSC LAN911x/LAN921x families embedded ethernet support"
+	depends on (ARM || SUPERH || BLACKFIN || MIPS || MN10300)
+	select CRC32
+	select MII
+	select PHYLIB
+	---help---
+	  Say Y here if you want support for SMSC LAN911x and LAN921x families
+	  of ethernet controllers.
+
+	  To compile this driver as a module, choose M here and read
+	  <file:Documentation/networking/net-modules.txt>. The module
+	  will be called smsc911x.
+
+config SMSC911X_ARCH_HOOKS
+	def_bool n
+	depends on SMSC911X
+	---help---
+	  If the arch enables this, it allows the arch to implement various
+	  hooks for more comprehensive interrupt control and also to override
+	  the source of the MAC address.
+
+config SMSC9420
+	tristate "SMSC LAN9420 PCI ethernet adapter support"
+	depends on PCI
+	select CRC32
+	select PHYLIB
+	select SMSC_PHY
+	---help---
+	  This is a driver for SMSC's LAN9420 PCI ethernet adapter.
+	  Say Y if you want it compiled into the kernel,
+	  and read the Ethernet-HOWTO, available from
+	  <http://www.tldp.org/docs.html#howto>.
+
+	  This driver is also available as a module. The module will be
+	  called smsc9420.  If you want to compile it as a module, say M
+	  here and read <file:Documentation/kbuild/modules.txt>
+
+endif # NET_VENDOR_SMSC
diff --git a/drivers/net/ethernet/smsc/Makefile b/drivers/net/ethernet/smsc/Makefile
new file mode 100644
index 0000000..f3438de
--- /dev/null
+++ b/drivers/net/ethernet/smsc/Makefile
@@ -0,0 +1,11 @@
+#
+# Makefile for the SMSC network device drivers.
+#
+
+obj-$(CONFIG_SMC9194) += smc9194.o
+obj-$(CONFIG_SMC91X) += smc91x.o
+obj-$(CONFIG_PCMCIA_SMC91C92) += smc91c92_cs.o
+obj-$(CONFIG_EPIC100) += epic100.o
+obj-$(CONFIG_SMSC9420) += smsc9420.o
+obj-$(CONFIG_SMC911X) += smc911x.o
+obj-$(CONFIG_SMSC911X) += smsc911x.o
diff --git a/drivers/net/ethernet/smsc/epic100.c b/drivers/net/ethernet/smsc/epic100.c
new file mode 100644
index 0000000..814c187
--- /dev/null
+++ b/drivers/net/ethernet/smsc/epic100.c
@@ -0,0 +1,1609 @@
+/* epic100.c: A SMC 83c170 EPIC/100 Fast Ethernet driver for Linux. */
+/*
+	Written/copyright 1997-2001 by Donald Becker.
+
+	This software may be used and distributed according to the terms of
+	the GNU General Public License (GPL), incorporated herein by reference.
+	Drivers based on or derived from this code fall under the GPL and must
+	retain the authorship, copyright and license notice.  This file is not
+	a complete program and may only be used when the entire operating
+	system is licensed under the GPL.
+
+	This driver is for the SMC83c170/175 "EPIC" series, as used on the
+	SMC EtherPower II 9432 PCI adapter, and several CardBus cards.
+
+	The author may be reached as becker@scyld.com, or C/O
+	Scyld Computing Corporation
+	410 Severn Ave., Suite 210
+	Annapolis MD 21403
+
+	Information and updates available at
+	http://www.scyld.com/network/epic100.html
+	[this link no longer provides anything useful -jgarzik]
+
+	---------------------------------------------------------------------
+
+*/
+
+#define DRV_NAME        "epic100"
+#define DRV_VERSION     "2.1"
+#define DRV_RELDATE     "Sept 11, 2006"
+
+/* The user-configurable values.
+   These may be modified when a driver module is loaded.*/
+
+static int debug = 1;			/* 1 normal messages, 0 quiet .. 7 verbose. */
+
+/* Used to pass the full-duplex flag, etc. */
+#define MAX_UNITS 8		/* More are supported, limit only on options */
+static int options[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1};
+static int full_duplex[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1};
+
+/* Set the copy breakpoint for the copy-only-tiny-frames scheme.
+   Setting to > 1518 effectively disables this feature. */
+static int rx_copybreak;
+
+/* Operational parameters that are set at compile time. */
+
+/* Keep the ring sizes a power of two for operational efficiency.
+   The compiler will convert <unsigned>'%'<2^N> into a bit mask.
+   Making the Tx ring too large decreases the effectiveness of channel
+   bonding and packet priority.
+   There are no ill effects from too-large receive rings. */
+#define TX_RING_SIZE	256
+#define TX_QUEUE_LEN	240		/* Limit ring entries actually used.  */
+#define RX_RING_SIZE	256
+#define TX_TOTAL_SIZE	TX_RING_SIZE*sizeof(struct epic_tx_desc)
+#define RX_TOTAL_SIZE	RX_RING_SIZE*sizeof(struct epic_rx_desc)
+
+/* Operational parameters that usually are not changed. */
+/* Time in jiffies before concluding the transmitter is hung. */
+#define TX_TIMEOUT  (2*HZ)
+
+#define PKT_BUF_SZ		1536			/* Size of each temporary Rx buffer.*/
+
+/* Bytes transferred to chip before transmission starts. */
+/* Initial threshold, increased on underflow, rounded down to 4 byte units. */
+#define TX_FIFO_THRESH 256
+#define RX_FIFO_THRESH 1		/* 0-3, 0==32, 64,96, or 3==128 bytes  */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/ethtool.h>
+#include <linux/mii.h>
+#include <linux/crc32.h>
+#include <linux/bitops.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/byteorder.h>
+
+/* These identify the driver base version and may not be removed. */
+static char version[] __devinitdata =
+DRV_NAME ".c:v1.11 1/7/2001 Written by Donald Becker <becker@scyld.com>\n";
+static char version2[] __devinitdata =
+"  (unofficial 2.4.x kernel port, version " DRV_VERSION ", " DRV_RELDATE ")\n";
+
+MODULE_AUTHOR("Donald Becker <becker@scyld.com>");
+MODULE_DESCRIPTION("SMC 83c170 EPIC series Ethernet driver");
+MODULE_LICENSE("GPL");
+
+module_param(debug, int, 0);
+module_param(rx_copybreak, int, 0);
+module_param_array(options, int, NULL, 0);
+module_param_array(full_duplex, int, NULL, 0);
+MODULE_PARM_DESC(debug, "EPIC/100 debug level (0-5)");
+MODULE_PARM_DESC(options, "EPIC/100: Bits 0-3: media type, bit 4: full duplex");
+MODULE_PARM_DESC(rx_copybreak, "EPIC/100 copy breakpoint for copy-only-tiny-frames");
+MODULE_PARM_DESC(full_duplex, "EPIC/100 full duplex setting(s) (1)");
+
+/*
+				Theory of Operation
+
+I. Board Compatibility
+
+This device driver is designed for the SMC "EPIC/100", the SMC
+single-chip Ethernet controllers for PCI.  This chip is used on
+the SMC EtherPower II boards.
+
+II. Board-specific settings
+
+PCI bus devices are configured by the system at boot time, so no jumpers
+need to be set on the board.  The system BIOS will assign the
+PCI INTA signal to a (preferably otherwise unused) system IRQ line.
+Note: Kernel versions earlier than 1.3.73 do not support shared PCI
+interrupt lines.
+
+III. Driver operation
+
+IIIa. Ring buffers
+
+IVb. References
+
+http://www.smsc.com/media/Downloads_Public/discontinued/83c171.pdf
+http://www.smsc.com/media/Downloads_Public/discontinued/83c175.pdf
+http://scyld.com/expert/NWay.html
+http://www.national.com/pf/DP/DP83840A.html
+
+IVc. Errata
+
+*/
+
+
+enum chip_capability_flags { MII_PWRDWN=1, TYPE2_INTR=2, NO_MII=4 };
+
+#define EPIC_TOTAL_SIZE 0x100
+#define USE_IO_OPS 1
+
+typedef enum {
+	SMSC_83C170_0,
+	SMSC_83C170,
+	SMSC_83C175,
+} chip_t;
+
+
+struct epic_chip_info {
+	const char *name;
+        int drv_flags;                          /* Driver use, intended as capability flags. */
+};
+
+
+/* indexed by chip_t */
+static const struct epic_chip_info pci_id_tbl[] = {
+	{ "SMSC EPIC/100 83c170",	TYPE2_INTR | NO_MII | MII_PWRDWN },
+	{ "SMSC EPIC/100 83c170",	TYPE2_INTR },
+	{ "SMSC EPIC/C 83c175",		TYPE2_INTR | MII_PWRDWN },
+};
+
+
+static DEFINE_PCI_DEVICE_TABLE(epic_pci_tbl) = {
+	{ 0x10B8, 0x0005, 0x1092, 0x0AB4, 0, 0, SMSC_83C170_0 },
+	{ 0x10B8, 0x0005, PCI_ANY_ID, PCI_ANY_ID, 0, 0, SMSC_83C170 },
+	{ 0x10B8, 0x0006, PCI_ANY_ID, PCI_ANY_ID,
+	  PCI_CLASS_NETWORK_ETHERNET << 8, 0xffff00, SMSC_83C175 },
+	{ 0,}
+};
+MODULE_DEVICE_TABLE (pci, epic_pci_tbl);
+
+
+#ifndef USE_IO_OPS
+#undef inb
+#undef inw
+#undef inl
+#undef outb
+#undef outw
+#undef outl
+#define inb readb
+#define inw readw
+#define inl readl
+#define outb writeb
+#define outw writew
+#define outl writel
+#endif
+
+/* Offsets to registers, using the (ugh) SMC names. */
+enum epic_registers {
+  COMMAND=0, INTSTAT=4, INTMASK=8, GENCTL=0x0C, NVCTL=0x10, EECTL=0x14,
+  PCIBurstCnt=0x18,
+  TEST1=0x1C, CRCCNT=0x20, ALICNT=0x24, MPCNT=0x28,	/* Rx error counters. */
+  MIICtrl=0x30, MIIData=0x34, MIICfg=0x38,
+  LAN0=64,						/* MAC address. */
+  MC0=80,						/* Multicast filter table. */
+  RxCtrl=96, TxCtrl=112, TxSTAT=0x74,
+  PRxCDAR=0x84, RxSTAT=0xA4, EarlyRx=0xB0, PTxCDAR=0xC4, TxThresh=0xDC,
+};
+
+/* Interrupt register bits, using my own meaningful names. */
+enum IntrStatus {
+	TxIdle=0x40000, RxIdle=0x20000, IntrSummary=0x010000,
+	PCIBusErr170=0x7000, PCIBusErr175=0x1000, PhyEvent175=0x8000,
+	RxStarted=0x0800, RxEarlyWarn=0x0400, CntFull=0x0200, TxUnderrun=0x0100,
+	TxEmpty=0x0080, TxDone=0x0020, RxError=0x0010,
+	RxOverflow=0x0008, RxFull=0x0004, RxHeader=0x0002, RxDone=0x0001,
+};
+enum CommandBits {
+	StopRx=1, StartRx=2, TxQueued=4, RxQueued=8,
+	StopTxDMA=0x20, StopRxDMA=0x40, RestartTx=0x80,
+};
+
+#define EpicRemoved	0xffffffff	/* Chip failed or removed (CardBus) */
+
+#define EpicNapiEvent	(TxEmpty | TxDone | \
+			 RxDone | RxStarted | RxEarlyWarn | RxOverflow | RxFull)
+#define EpicNormalEvent	(0x0000ffff & ~EpicNapiEvent)
+
+static const u16 media2miictl[16] = {
+	0, 0x0C00, 0x0C00, 0x2000,  0x0100, 0x2100, 0, 0,
+	0, 0, 0, 0,  0, 0, 0, 0 };
+
+/*
+ * The EPIC100 Rx and Tx buffer descriptors.  Note that these
+ * really ARE host-endian; it's not a misannotation.  We tell
+ * the card to byteswap them internally on big-endian hosts -
+ * look for #ifdef __BIG_ENDIAN in epic_open().
+ */
+
+struct epic_tx_desc {
+	u32 txstatus;
+	u32 bufaddr;
+	u32 buflength;
+	u32 next;
+};
+
+struct epic_rx_desc {
+	u32 rxstatus;
+	u32 bufaddr;
+	u32 buflength;
+	u32 next;
+};
+
+enum desc_status_bits {
+	DescOwn=0x8000,
+};
+
+#define PRIV_ALIGN	15 	/* Required alignment mask */
+struct epic_private {
+	struct epic_rx_desc *rx_ring;
+	struct epic_tx_desc *tx_ring;
+	/* The saved address of a sent-in-place packet/buffer, for skfree(). */
+	struct sk_buff* tx_skbuff[TX_RING_SIZE];
+	/* The addresses of receive-in-place skbuffs. */
+	struct sk_buff* rx_skbuff[RX_RING_SIZE];
+
+	dma_addr_t tx_ring_dma;
+	dma_addr_t rx_ring_dma;
+
+	/* Ring pointers. */
+	spinlock_t lock;				/* Group with Tx control cache line. */
+	spinlock_t napi_lock;
+	struct napi_struct napi;
+	unsigned int reschedule_in_poll;
+	unsigned int cur_tx, dirty_tx;
+
+	unsigned int cur_rx, dirty_rx;
+	u32 irq_mask;
+	unsigned int rx_buf_sz;				/* Based on MTU+slack. */
+
+	struct pci_dev *pci_dev;			/* PCI bus location. */
+	int chip_id, chip_flags;
+
+	struct timer_list timer;			/* Media selection timer. */
+	int tx_threshold;
+	unsigned char mc_filter[8];
+	signed char phys[4];				/* MII device addresses. */
+	u16 advertising;					/* NWay media advertisement */
+	int mii_phy_cnt;
+	struct mii_if_info mii;
+	unsigned int tx_full:1;				/* The Tx queue is full. */
+	unsigned int default_port:4;		/* Last dev->if_port value. */
+};
+
+static int epic_open(struct net_device *dev);
+static int read_eeprom(long ioaddr, int location);
+static int mdio_read(struct net_device *dev, int phy_id, int location);
+static void mdio_write(struct net_device *dev, int phy_id, int loc, int val);
+static void epic_restart(struct net_device *dev);
+static void epic_timer(unsigned long data);
+static void epic_tx_timeout(struct net_device *dev);
+static void epic_init_ring(struct net_device *dev);
+static netdev_tx_t epic_start_xmit(struct sk_buff *skb,
+				   struct net_device *dev);
+static int epic_rx(struct net_device *dev, int budget);
+static int epic_poll(struct napi_struct *napi, int budget);
+static irqreturn_t epic_interrupt(int irq, void *dev_instance);
+static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
+static const struct ethtool_ops netdev_ethtool_ops;
+static int epic_close(struct net_device *dev);
+static struct net_device_stats *epic_get_stats(struct net_device *dev);
+static void set_rx_mode(struct net_device *dev);
+
+static const struct net_device_ops epic_netdev_ops = {
+	.ndo_open		= epic_open,
+	.ndo_stop		= epic_close,
+	.ndo_start_xmit		= epic_start_xmit,
+	.ndo_tx_timeout 	= epic_tx_timeout,
+	.ndo_get_stats		= epic_get_stats,
+	.ndo_set_multicast_list = set_rx_mode,
+	.ndo_do_ioctl 		= netdev_ioctl,
+	.ndo_change_mtu		= eth_change_mtu,
+	.ndo_set_mac_address 	= eth_mac_addr,
+	.ndo_validate_addr	= eth_validate_addr,
+};
+
+static int __devinit epic_init_one (struct pci_dev *pdev,
+				    const struct pci_device_id *ent)
+{
+	static int card_idx = -1;
+	long ioaddr;
+	int chip_idx = (int) ent->driver_data;
+	int irq;
+	struct net_device *dev;
+	struct epic_private *ep;
+	int i, ret, option = 0, duplex = 0;
+	void *ring_space;
+	dma_addr_t ring_dma;
+
+/* when built into the kernel, we only print version if device is found */
+#ifndef MODULE
+	static int printed_version;
+	if (!printed_version++)
+		printk(KERN_INFO "%s%s", version, version2);
+#endif
+
+	card_idx++;
+
+	ret = pci_enable_device(pdev);
+	if (ret)
+		goto out;
+	irq = pdev->irq;
+
+	if (pci_resource_len(pdev, 0) < EPIC_TOTAL_SIZE) {
+		dev_err(&pdev->dev, "no PCI region space\n");
+		ret = -ENODEV;
+		goto err_out_disable;
+	}
+
+	pci_set_master(pdev);
+
+	ret = pci_request_regions(pdev, DRV_NAME);
+	if (ret < 0)
+		goto err_out_disable;
+
+	ret = -ENOMEM;
+
+	dev = alloc_etherdev(sizeof (*ep));
+	if (!dev) {
+		dev_err(&pdev->dev, "no memory for eth device\n");
+		goto err_out_free_res;
+	}
+	SET_NETDEV_DEV(dev, &pdev->dev);
+
+#ifdef USE_IO_OPS
+	ioaddr = pci_resource_start (pdev, 0);
+#else
+	ioaddr = pci_resource_start (pdev, 1);
+	ioaddr = (long) pci_ioremap_bar(pdev, 1);
+	if (!ioaddr) {
+		dev_err(&pdev->dev, "ioremap failed\n");
+		goto err_out_free_netdev;
+	}
+#endif
+
+	pci_set_drvdata(pdev, dev);
+	ep = netdev_priv(dev);
+	ep->mii.dev = dev;
+	ep->mii.mdio_read = mdio_read;
+	ep->mii.mdio_write = mdio_write;
+	ep->mii.phy_id_mask = 0x1f;
+	ep->mii.reg_num_mask = 0x1f;
+
+	ring_space = pci_alloc_consistent(pdev, TX_TOTAL_SIZE, &ring_dma);
+	if (!ring_space)
+		goto err_out_iounmap;
+	ep->tx_ring = ring_space;
+	ep->tx_ring_dma = ring_dma;
+
+	ring_space = pci_alloc_consistent(pdev, RX_TOTAL_SIZE, &ring_dma);
+	if (!ring_space)
+		goto err_out_unmap_tx;
+	ep->rx_ring = ring_space;
+	ep->rx_ring_dma = ring_dma;
+
+	if (dev->mem_start) {
+		option = dev->mem_start;
+		duplex = (dev->mem_start & 16) ? 1 : 0;
+	} else if (card_idx >= 0  &&  card_idx < MAX_UNITS) {
+		if (options[card_idx] >= 0)
+			option = options[card_idx];
+		if (full_duplex[card_idx] >= 0)
+			duplex = full_duplex[card_idx];
+	}
+
+	dev->base_addr = ioaddr;
+	dev->irq = irq;
+
+	spin_lock_init(&ep->lock);
+	spin_lock_init(&ep->napi_lock);
+	ep->reschedule_in_poll = 0;
+
+	/* Bring the chip out of low-power mode. */
+	outl(0x4200, ioaddr + GENCTL);
+	/* Magic?!  If we don't set this bit the MII interface won't work. */
+	/* This magic is documented in SMSC app note 7.15 */
+	for (i = 16; i > 0; i--)
+		outl(0x0008, ioaddr + TEST1);
+
+	/* Turn on the MII transceiver. */
+	outl(0x12, ioaddr + MIICfg);
+	if (chip_idx == 1)
+		outl((inl(ioaddr + NVCTL) & ~0x003C) | 0x4800, ioaddr + NVCTL);
+	outl(0x0200, ioaddr + GENCTL);
+
+	/* Note: the '175 does not have a serial EEPROM. */
+	for (i = 0; i < 3; i++)
+		((__le16 *)dev->dev_addr)[i] = cpu_to_le16(inw(ioaddr + LAN0 + i*4));
+
+	if (debug > 2) {
+		dev_printk(KERN_DEBUG, &pdev->dev, "EEPROM contents:\n");
+		for (i = 0; i < 64; i++)
+			printk(" %4.4x%s", read_eeprom(ioaddr, i),
+				   i % 16 == 15 ? "\n" : "");
+	}
+
+	ep->pci_dev = pdev;
+	ep->chip_id = chip_idx;
+	ep->chip_flags = pci_id_tbl[chip_idx].drv_flags;
+	ep->irq_mask =
+		(ep->chip_flags & TYPE2_INTR ?  PCIBusErr175 : PCIBusErr170)
+		 | CntFull | TxUnderrun | EpicNapiEvent;
+
+	/* Find the connected MII xcvrs.
+	   Doing this in open() would allow detecting external xcvrs later, but
+	   takes much time and no cards have external MII. */
+	{
+		int phy, phy_idx = 0;
+		for (phy = 1; phy < 32 && phy_idx < sizeof(ep->phys); phy++) {
+			int mii_status = mdio_read(dev, phy, MII_BMSR);
+			if (mii_status != 0xffff  &&  mii_status != 0x0000) {
+				ep->phys[phy_idx++] = phy;
+				dev_info(&pdev->dev,
+					"MII transceiver #%d control "
+					"%4.4x status %4.4x.\n",
+					phy, mdio_read(dev, phy, 0), mii_status);
+			}
+		}
+		ep->mii_phy_cnt = phy_idx;
+		if (phy_idx != 0) {
+			phy = ep->phys[0];
+			ep->mii.advertising = mdio_read(dev, phy, MII_ADVERTISE);
+			dev_info(&pdev->dev,
+				"Autonegotiation advertising %4.4x link "
+				   "partner %4.4x.\n",
+				   ep->mii.advertising, mdio_read(dev, phy, 5));
+		} else if ( ! (ep->chip_flags & NO_MII)) {
+			dev_warn(&pdev->dev,
+				"***WARNING***: No MII transceiver found!\n");
+			/* Use the known PHY address of the EPII. */
+			ep->phys[0] = 3;
+		}
+		ep->mii.phy_id = ep->phys[0];
+	}
+
+	/* Turn off the MII xcvr (175 only!), leave the chip in low-power mode. */
+	if (ep->chip_flags & MII_PWRDWN)
+		outl(inl(ioaddr + NVCTL) & ~0x483C, ioaddr + NVCTL);
+	outl(0x0008, ioaddr + GENCTL);
+
+	/* The lower four bits are the media type. */
+	if (duplex) {
+		ep->mii.force_media = ep->mii.full_duplex = 1;
+		dev_info(&pdev->dev, "Forced full duplex requested.\n");
+	}
+	dev->if_port = ep->default_port = option;
+
+	/* The Epic-specific entries in the device structure. */
+	dev->netdev_ops = &epic_netdev_ops;
+	dev->ethtool_ops = &netdev_ethtool_ops;
+	dev->watchdog_timeo = TX_TIMEOUT;
+	netif_napi_add(dev, &ep->napi, epic_poll, 64);
+
+	ret = register_netdev(dev);
+	if (ret < 0)
+		goto err_out_unmap_rx;
+
+	printk(KERN_INFO "%s: %s at %#lx, IRQ %d, %pM\n",
+	       dev->name, pci_id_tbl[chip_idx].name, ioaddr, dev->irq,
+	       dev->dev_addr);
+
+out:
+	return ret;
+
+err_out_unmap_rx:
+	pci_free_consistent(pdev, RX_TOTAL_SIZE, ep->rx_ring, ep->rx_ring_dma);
+err_out_unmap_tx:
+	pci_free_consistent(pdev, TX_TOTAL_SIZE, ep->tx_ring, ep->tx_ring_dma);
+err_out_iounmap:
+#ifndef USE_IO_OPS
+	iounmap(ioaddr);
+err_out_free_netdev:
+#endif
+	free_netdev(dev);
+err_out_free_res:
+	pci_release_regions(pdev);
+err_out_disable:
+	pci_disable_device(pdev);
+	goto out;
+}
+
+/* Serial EEPROM section. */
+
+/*  EEPROM_Ctrl bits. */
+#define EE_SHIFT_CLK	0x04	/* EEPROM shift clock. */
+#define EE_CS			0x02	/* EEPROM chip select. */
+#define EE_DATA_WRITE	0x08	/* EEPROM chip data in. */
+#define EE_WRITE_0		0x01
+#define EE_WRITE_1		0x09
+#define EE_DATA_READ	0x10	/* EEPROM chip data out. */
+#define EE_ENB			(0x0001 | EE_CS)
+
+/* Delay between EEPROM clock transitions.
+   This serves to flush the operation to the PCI bus.
+ */
+
+#define eeprom_delay()	inl(ee_addr)
+
+/* The EEPROM commands include the alway-set leading bit. */
+#define EE_WRITE_CMD	(5 << 6)
+#define EE_READ64_CMD	(6 << 6)
+#define EE_READ256_CMD	(6 << 8)
+#define EE_ERASE_CMD	(7 << 6)
+
+static void epic_disable_int(struct net_device *dev, struct epic_private *ep)
+{
+	long ioaddr = dev->base_addr;
+
+	outl(0x00000000, ioaddr + INTMASK);
+}
+
+static inline void __epic_pci_commit(long ioaddr)
+{
+#ifndef USE_IO_OPS
+	inl(ioaddr + INTMASK);
+#endif
+}
+
+static inline void epic_napi_irq_off(struct net_device *dev,
+				     struct epic_private *ep)
+{
+	long ioaddr = dev->base_addr;
+
+	outl(ep->irq_mask & ~EpicNapiEvent, ioaddr + INTMASK);
+	__epic_pci_commit(ioaddr);
+}
+
+static inline void epic_napi_irq_on(struct net_device *dev,
+				    struct epic_private *ep)
+{
+	long ioaddr = dev->base_addr;
+
+	/* No need to commit possible posted write */
+	outl(ep->irq_mask | EpicNapiEvent, ioaddr + INTMASK);
+}
+
+static int __devinit read_eeprom(long ioaddr, int location)
+{
+	int i;
+	int retval = 0;
+	long ee_addr = ioaddr + EECTL;
+	int read_cmd = location |
+		(inl(ee_addr) & 0x40 ? EE_READ64_CMD : EE_READ256_CMD);
+
+	outl(EE_ENB & ~EE_CS, ee_addr);
+	outl(EE_ENB, ee_addr);
+
+	/* Shift the read command bits out. */
+	for (i = 12; i >= 0; i--) {
+		short dataval = (read_cmd & (1 << i)) ? EE_WRITE_1 : EE_WRITE_0;
+		outl(EE_ENB | dataval, ee_addr);
+		eeprom_delay();
+		outl(EE_ENB | dataval | EE_SHIFT_CLK, ee_addr);
+		eeprom_delay();
+	}
+	outl(EE_ENB, ee_addr);
+
+	for (i = 16; i > 0; i--) {
+		outl(EE_ENB | EE_SHIFT_CLK, ee_addr);
+		eeprom_delay();
+		retval = (retval << 1) | ((inl(ee_addr) & EE_DATA_READ) ? 1 : 0);
+		outl(EE_ENB, ee_addr);
+		eeprom_delay();
+	}
+
+	/* Terminate the EEPROM access. */
+	outl(EE_ENB & ~EE_CS, ee_addr);
+	return retval;
+}
+
+#define MII_READOP		1
+#define MII_WRITEOP		2
+static int mdio_read(struct net_device *dev, int phy_id, int location)
+{
+	long ioaddr = dev->base_addr;
+	int read_cmd = (phy_id << 9) | (location << 4) | MII_READOP;
+	int i;
+
+	outl(read_cmd, ioaddr + MIICtrl);
+	/* Typical operation takes 25 loops. */
+	for (i = 400; i > 0; i--) {
+		barrier();
+		if ((inl(ioaddr + MIICtrl) & MII_READOP) == 0) {
+			/* Work around read failure bug. */
+			if (phy_id == 1 && location < 6 &&
+			    inw(ioaddr + MIIData) == 0xffff) {
+				outl(read_cmd, ioaddr + MIICtrl);
+				continue;
+			}
+			return inw(ioaddr + MIIData);
+		}
+	}
+	return 0xffff;
+}
+
+static void mdio_write(struct net_device *dev, int phy_id, int loc, int value)
+{
+	long ioaddr = dev->base_addr;
+	int i;
+
+	outw(value, ioaddr + MIIData);
+	outl((phy_id << 9) | (loc << 4) | MII_WRITEOP, ioaddr + MIICtrl);
+	for (i = 10000; i > 0; i--) {
+		barrier();
+		if ((inl(ioaddr + MIICtrl) & MII_WRITEOP) == 0)
+			break;
+	}
+}
+
+
+static int epic_open(struct net_device *dev)
+{
+	struct epic_private *ep = netdev_priv(dev);
+	long ioaddr = dev->base_addr;
+	int i;
+	int retval;
+
+	/* Soft reset the chip. */
+	outl(0x4001, ioaddr + GENCTL);
+
+	napi_enable(&ep->napi);
+	if ((retval = request_irq(dev->irq, epic_interrupt, IRQF_SHARED, dev->name, dev))) {
+		napi_disable(&ep->napi);
+		return retval;
+	}
+
+	epic_init_ring(dev);
+
+	outl(0x4000, ioaddr + GENCTL);
+	/* This magic is documented in SMSC app note 7.15 */
+	for (i = 16; i > 0; i--)
+		outl(0x0008, ioaddr + TEST1);
+
+	/* Pull the chip out of low-power mode, enable interrupts, and set for
+	   PCI read multiple.  The MIIcfg setting and strange write order are
+	   required by the details of which bits are reset and the transceiver
+	   wiring on the Ositech CardBus card.
+	*/
+#if 0
+	outl(dev->if_port == 1 ? 0x13 : 0x12, ioaddr + MIICfg);
+#endif
+	if (ep->chip_flags & MII_PWRDWN)
+		outl((inl(ioaddr + NVCTL) & ~0x003C) | 0x4800, ioaddr + NVCTL);
+
+	/* Tell the chip to byteswap descriptors on big-endian hosts */
+#ifdef __BIG_ENDIAN
+	outl(0x4432 | (RX_FIFO_THRESH<<8), ioaddr + GENCTL);
+	inl(ioaddr + GENCTL);
+	outl(0x0432 | (RX_FIFO_THRESH<<8), ioaddr + GENCTL);
+#else
+	outl(0x4412 | (RX_FIFO_THRESH<<8), ioaddr + GENCTL);
+	inl(ioaddr + GENCTL);
+	outl(0x0412 | (RX_FIFO_THRESH<<8), ioaddr + GENCTL);
+#endif
+
+	udelay(20); /* Looks like EPII needs that if you want reliable RX init. FIXME: pci posting bug? */
+
+	for (i = 0; i < 3; i++)
+		outl(le16_to_cpu(((__le16*)dev->dev_addr)[i]), ioaddr + LAN0 + i*4);
+
+	ep->tx_threshold = TX_FIFO_THRESH;
+	outl(ep->tx_threshold, ioaddr + TxThresh);
+
+	if (media2miictl[dev->if_port & 15]) {
+		if (ep->mii_phy_cnt)
+			mdio_write(dev, ep->phys[0], MII_BMCR, media2miictl[dev->if_port&15]);
+		if (dev->if_port == 1) {
+			if (debug > 1)
+				printk(KERN_INFO "%s: Using the 10base2 transceiver, MII "
+					   "status %4.4x.\n",
+					   dev->name, mdio_read(dev, ep->phys[0], MII_BMSR));
+		}
+	} else {
+		int mii_lpa = mdio_read(dev, ep->phys[0], MII_LPA);
+		if (mii_lpa != 0xffff) {
+			if ((mii_lpa & LPA_100FULL) || (mii_lpa & 0x01C0) == LPA_10FULL)
+				ep->mii.full_duplex = 1;
+			else if (! (mii_lpa & LPA_LPACK))
+				mdio_write(dev, ep->phys[0], MII_BMCR, BMCR_ANENABLE|BMCR_ANRESTART);
+			if (debug > 1)
+				printk(KERN_INFO "%s: Setting %s-duplex based on MII xcvr %d"
+					   " register read of %4.4x.\n", dev->name,
+					   ep->mii.full_duplex ? "full" : "half",
+					   ep->phys[0], mii_lpa);
+		}
+	}
+
+	outl(ep->mii.full_duplex ? 0x7F : 0x79, ioaddr + TxCtrl);
+	outl(ep->rx_ring_dma, ioaddr + PRxCDAR);
+	outl(ep->tx_ring_dma, ioaddr + PTxCDAR);
+
+	/* Start the chip's Rx process. */
+	set_rx_mode(dev);
+	outl(StartRx | RxQueued, ioaddr + COMMAND);
+
+	netif_start_queue(dev);
+
+	/* Enable interrupts by setting the interrupt mask. */
+	outl((ep->chip_flags & TYPE2_INTR ? PCIBusErr175 : PCIBusErr170)
+		 | CntFull | TxUnderrun
+		 | RxError | RxHeader | EpicNapiEvent, ioaddr + INTMASK);
+
+	if (debug > 1)
+		printk(KERN_DEBUG "%s: epic_open() ioaddr %lx IRQ %d status %4.4x "
+			   "%s-duplex.\n",
+			   dev->name, ioaddr, dev->irq, (int)inl(ioaddr + GENCTL),
+			   ep->mii.full_duplex ? "full" : "half");
+
+	/* Set the timer to switch to check for link beat and perhaps switch
+	   to an alternate media type. */
+	init_timer(&ep->timer);
+	ep->timer.expires = jiffies + 3*HZ;
+	ep->timer.data = (unsigned long)dev;
+	ep->timer.function = epic_timer;				/* timer handler */
+	add_timer(&ep->timer);
+
+	return 0;
+}
+
+/* Reset the chip to recover from a PCI transaction error.
+   This may occur at interrupt time. */
+static void epic_pause(struct net_device *dev)
+{
+	long ioaddr = dev->base_addr;
+
+	netif_stop_queue (dev);
+
+	/* Disable interrupts by clearing the interrupt mask. */
+	outl(0x00000000, ioaddr + INTMASK);
+	/* Stop the chip's Tx and Rx DMA processes. */
+	outw(StopRx | StopTxDMA | StopRxDMA, ioaddr + COMMAND);
+
+	/* Update the error counts. */
+	if (inw(ioaddr + COMMAND) != 0xffff) {
+		dev->stats.rx_missed_errors += inb(ioaddr + MPCNT);
+		dev->stats.rx_frame_errors += inb(ioaddr + ALICNT);
+		dev->stats.rx_crc_errors += inb(ioaddr + CRCCNT);
+	}
+
+	/* Remove the packets on the Rx queue. */
+	epic_rx(dev, RX_RING_SIZE);
+}
+
+static void epic_restart(struct net_device *dev)
+{
+	long ioaddr = dev->base_addr;
+	struct epic_private *ep = netdev_priv(dev);
+	int i;
+
+	/* Soft reset the chip. */
+	outl(0x4001, ioaddr + GENCTL);
+
+	printk(KERN_DEBUG "%s: Restarting the EPIC chip, Rx %d/%d Tx %d/%d.\n",
+		   dev->name, ep->cur_rx, ep->dirty_rx, ep->dirty_tx, ep->cur_tx);
+	udelay(1);
+
+	/* This magic is documented in SMSC app note 7.15 */
+	for (i = 16; i > 0; i--)
+		outl(0x0008, ioaddr + TEST1);
+
+#ifdef __BIG_ENDIAN
+	outl(0x0432 | (RX_FIFO_THRESH<<8), ioaddr + GENCTL);
+#else
+	outl(0x0412 | (RX_FIFO_THRESH<<8), ioaddr + GENCTL);
+#endif
+	outl(dev->if_port == 1 ? 0x13 : 0x12, ioaddr + MIICfg);
+	if (ep->chip_flags & MII_PWRDWN)
+		outl((inl(ioaddr + NVCTL) & ~0x003C) | 0x4800, ioaddr + NVCTL);
+
+	for (i = 0; i < 3; i++)
+		outl(le16_to_cpu(((__le16*)dev->dev_addr)[i]), ioaddr + LAN0 + i*4);
+
+	ep->tx_threshold = TX_FIFO_THRESH;
+	outl(ep->tx_threshold, ioaddr + TxThresh);
+	outl(ep->mii.full_duplex ? 0x7F : 0x79, ioaddr + TxCtrl);
+	outl(ep->rx_ring_dma + (ep->cur_rx%RX_RING_SIZE)*
+		sizeof(struct epic_rx_desc), ioaddr + PRxCDAR);
+	outl(ep->tx_ring_dma + (ep->dirty_tx%TX_RING_SIZE)*
+		 sizeof(struct epic_tx_desc), ioaddr + PTxCDAR);
+
+	/* Start the chip's Rx process. */
+	set_rx_mode(dev);
+	outl(StartRx | RxQueued, ioaddr + COMMAND);
+
+	/* Enable interrupts by setting the interrupt mask. */
+	outl((ep->chip_flags & TYPE2_INTR ? PCIBusErr175 : PCIBusErr170)
+		 | CntFull | TxUnderrun
+		 | RxError | RxHeader | EpicNapiEvent, ioaddr + INTMASK);
+
+	printk(KERN_DEBUG "%s: epic_restart() done, cmd status %4.4x, ctl %4.4x"
+		   " interrupt %4.4x.\n",
+		   dev->name, (int)inl(ioaddr + COMMAND), (int)inl(ioaddr + GENCTL),
+		   (int)inl(ioaddr + INTSTAT));
+}
+
+static void check_media(struct net_device *dev)
+{
+	struct epic_private *ep = netdev_priv(dev);
+	long ioaddr = dev->base_addr;
+	int mii_lpa = ep->mii_phy_cnt ? mdio_read(dev, ep->phys[0], MII_LPA) : 0;
+	int negotiated = mii_lpa & ep->mii.advertising;
+	int duplex = (negotiated & 0x0100) || (negotiated & 0x01C0) == 0x0040;
+
+	if (ep->mii.force_media)
+		return;
+	if (mii_lpa == 0xffff)		/* Bogus read */
+		return;
+	if (ep->mii.full_duplex != duplex) {
+		ep->mii.full_duplex = duplex;
+		printk(KERN_INFO "%s: Setting %s-duplex based on MII #%d link"
+			   " partner capability of %4.4x.\n", dev->name,
+			   ep->mii.full_duplex ? "full" : "half", ep->phys[0], mii_lpa);
+		outl(ep->mii.full_duplex ? 0x7F : 0x79, ioaddr + TxCtrl);
+	}
+}
+
+static void epic_timer(unsigned long data)
+{
+	struct net_device *dev = (struct net_device *)data;
+	struct epic_private *ep = netdev_priv(dev);
+	long ioaddr = dev->base_addr;
+	int next_tick = 5*HZ;
+
+	if (debug > 3) {
+		printk(KERN_DEBUG "%s: Media monitor tick, Tx status %8.8x.\n",
+			   dev->name, (int)inl(ioaddr + TxSTAT));
+		printk(KERN_DEBUG "%s: Other registers are IntMask %4.4x "
+			   "IntStatus %4.4x RxStatus %4.4x.\n",
+			   dev->name, (int)inl(ioaddr + INTMASK),
+			   (int)inl(ioaddr + INTSTAT), (int)inl(ioaddr + RxSTAT));
+	}
+
+	check_media(dev);
+
+	ep->timer.expires = jiffies + next_tick;
+	add_timer(&ep->timer);
+}
+
+static void epic_tx_timeout(struct net_device *dev)
+{
+	struct epic_private *ep = netdev_priv(dev);
+	long ioaddr = dev->base_addr;
+
+	if (debug > 0) {
+		printk(KERN_WARNING "%s: Transmit timeout using MII device, "
+			   "Tx status %4.4x.\n",
+			   dev->name, (int)inw(ioaddr + TxSTAT));
+		if (debug > 1) {
+			printk(KERN_DEBUG "%s: Tx indices: dirty_tx %d, cur_tx %d.\n",
+				   dev->name, ep->dirty_tx, ep->cur_tx);
+		}
+	}
+	if (inw(ioaddr + TxSTAT) & 0x10) {		/* Tx FIFO underflow. */
+		dev->stats.tx_fifo_errors++;
+		outl(RestartTx, ioaddr + COMMAND);
+	} else {
+		epic_restart(dev);
+		outl(TxQueued, dev->base_addr + COMMAND);
+	}
+
+	dev->trans_start = jiffies; /* prevent tx timeout */
+	dev->stats.tx_errors++;
+	if (!ep->tx_full)
+		netif_wake_queue(dev);
+}
+
+/* Initialize the Rx and Tx rings, along with various 'dev' bits. */
+static void epic_init_ring(struct net_device *dev)
+{
+	struct epic_private *ep = netdev_priv(dev);
+	int i;
+
+	ep->tx_full = 0;
+	ep->dirty_tx = ep->cur_tx = 0;
+	ep->cur_rx = ep->dirty_rx = 0;
+	ep->rx_buf_sz = (dev->mtu <= 1500 ? PKT_BUF_SZ : dev->mtu + 32);
+
+	/* Initialize all Rx descriptors. */
+	for (i = 0; i < RX_RING_SIZE; i++) {
+		ep->rx_ring[i].rxstatus = 0;
+		ep->rx_ring[i].buflength = ep->rx_buf_sz;
+		ep->rx_ring[i].next = ep->rx_ring_dma +
+				      (i+1)*sizeof(struct epic_rx_desc);
+		ep->rx_skbuff[i] = NULL;
+	}
+	/* Mark the last entry as wrapping the ring. */
+	ep->rx_ring[i-1].next = ep->rx_ring_dma;
+
+	/* Fill in the Rx buffers.  Handle allocation failure gracefully. */
+	for (i = 0; i < RX_RING_SIZE; i++) {
+		struct sk_buff *skb = dev_alloc_skb(ep->rx_buf_sz + 2);
+		ep->rx_skbuff[i] = skb;
+		if (skb == NULL)
+			break;
+		skb_reserve(skb, 2);	/* 16 byte align the IP header. */
+		ep->rx_ring[i].bufaddr = pci_map_single(ep->pci_dev,
+			skb->data, ep->rx_buf_sz, PCI_DMA_FROMDEVICE);
+		ep->rx_ring[i].rxstatus = DescOwn;
+	}
+	ep->dirty_rx = (unsigned int)(i - RX_RING_SIZE);
+
+	/* The Tx buffer descriptor is filled in as needed, but we
+	   do need to clear the ownership bit. */
+	for (i = 0; i < TX_RING_SIZE; i++) {
+		ep->tx_skbuff[i] = NULL;
+		ep->tx_ring[i].txstatus = 0x0000;
+		ep->tx_ring[i].next = ep->tx_ring_dma +
+			(i+1)*sizeof(struct epic_tx_desc);
+	}
+	ep->tx_ring[i-1].next = ep->tx_ring_dma;
+}
+
+static netdev_tx_t epic_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct epic_private *ep = netdev_priv(dev);
+	int entry, free_count;
+	u32 ctrl_word;
+	unsigned long flags;
+
+	if (skb_padto(skb, ETH_ZLEN))
+		return NETDEV_TX_OK;
+
+	/* Caution: the write order is important here, set the field with the
+	   "ownership" bit last. */
+
+	/* Calculate the next Tx descriptor entry. */
+	spin_lock_irqsave(&ep->lock, flags);
+	free_count = ep->cur_tx - ep->dirty_tx;
+	entry = ep->cur_tx % TX_RING_SIZE;
+
+	ep->tx_skbuff[entry] = skb;
+	ep->tx_ring[entry].bufaddr = pci_map_single(ep->pci_dev, skb->data,
+		 			            skb->len, PCI_DMA_TODEVICE);
+	if (free_count < TX_QUEUE_LEN/2) {/* Typical path */
+		ctrl_word = 0x100000; /* No interrupt */
+	} else if (free_count == TX_QUEUE_LEN/2) {
+		ctrl_word = 0x140000; /* Tx-done intr. */
+	} else if (free_count < TX_QUEUE_LEN - 1) {
+		ctrl_word = 0x100000; /* No Tx-done intr. */
+	} else {
+		/* Leave room for an additional entry. */
+		ctrl_word = 0x140000; /* Tx-done intr. */
+		ep->tx_full = 1;
+	}
+	ep->tx_ring[entry].buflength = ctrl_word | skb->len;
+	ep->tx_ring[entry].txstatus =
+		((skb->len >= ETH_ZLEN ? skb->len : ETH_ZLEN) << 16)
+			    | DescOwn;
+
+	ep->cur_tx++;
+	if (ep->tx_full)
+		netif_stop_queue(dev);
+
+	spin_unlock_irqrestore(&ep->lock, flags);
+	/* Trigger an immediate transmit demand. */
+	outl(TxQueued, dev->base_addr + COMMAND);
+
+	if (debug > 4)
+		printk(KERN_DEBUG "%s: Queued Tx packet size %d to slot %d, "
+			   "flag %2.2x Tx status %8.8x.\n",
+			   dev->name, (int)skb->len, entry, ctrl_word,
+			   (int)inl(dev->base_addr + TxSTAT));
+
+	return NETDEV_TX_OK;
+}
+
+static void epic_tx_error(struct net_device *dev, struct epic_private *ep,
+			  int status)
+{
+	struct net_device_stats *stats = &dev->stats;
+
+#ifndef final_version
+	/* There was an major error, log it. */
+	if (debug > 1)
+		printk(KERN_DEBUG "%s: Transmit error, Tx status %8.8x.\n",
+		       dev->name, status);
+#endif
+	stats->tx_errors++;
+	if (status & 0x1050)
+		stats->tx_aborted_errors++;
+	if (status & 0x0008)
+		stats->tx_carrier_errors++;
+	if (status & 0x0040)
+		stats->tx_window_errors++;
+	if (status & 0x0010)
+		stats->tx_fifo_errors++;
+}
+
+static void epic_tx(struct net_device *dev, struct epic_private *ep)
+{
+	unsigned int dirty_tx, cur_tx;
+
+	/*
+	 * Note: if this lock becomes a problem we can narrow the locked
+	 * region at the cost of occasionally grabbing the lock more times.
+	 */
+	cur_tx = ep->cur_tx;
+	for (dirty_tx = ep->dirty_tx; cur_tx - dirty_tx > 0; dirty_tx++) {
+		struct sk_buff *skb;
+		int entry = dirty_tx % TX_RING_SIZE;
+		int txstatus = ep->tx_ring[entry].txstatus;
+
+		if (txstatus & DescOwn)
+			break;	/* It still hasn't been Txed */
+
+		if (likely(txstatus & 0x0001)) {
+			dev->stats.collisions += (txstatus >> 8) & 15;
+			dev->stats.tx_packets++;
+			dev->stats.tx_bytes += ep->tx_skbuff[entry]->len;
+		} else
+			epic_tx_error(dev, ep, txstatus);
+
+		/* Free the original skb. */
+		skb = ep->tx_skbuff[entry];
+		pci_unmap_single(ep->pci_dev, ep->tx_ring[entry].bufaddr,
+				 skb->len, PCI_DMA_TODEVICE);
+		dev_kfree_skb_irq(skb);
+		ep->tx_skbuff[entry] = NULL;
+	}
+
+#ifndef final_version
+	if (cur_tx - dirty_tx > TX_RING_SIZE) {
+		printk(KERN_WARNING
+		       "%s: Out-of-sync dirty pointer, %d vs. %d, full=%d.\n",
+		       dev->name, dirty_tx, cur_tx, ep->tx_full);
+		dirty_tx += TX_RING_SIZE;
+	}
+#endif
+	ep->dirty_tx = dirty_tx;
+	if (ep->tx_full && cur_tx - dirty_tx < TX_QUEUE_LEN - 4) {
+		/* The ring is no longer full, allow new TX entries. */
+		ep->tx_full = 0;
+		netif_wake_queue(dev);
+	}
+}
+
+/* The interrupt handler does all of the Rx thread work and cleans up
+   after the Tx thread. */
+static irqreturn_t epic_interrupt(int irq, void *dev_instance)
+{
+	struct net_device *dev = dev_instance;
+	struct epic_private *ep = netdev_priv(dev);
+	long ioaddr = dev->base_addr;
+	unsigned int handled = 0;
+	int status;
+
+	status = inl(ioaddr + INTSTAT);
+	/* Acknowledge all of the current interrupt sources ASAP. */
+	outl(status & EpicNormalEvent, ioaddr + INTSTAT);
+
+	if (debug > 4) {
+		printk(KERN_DEBUG "%s: Interrupt, status=%#8.8x new "
+				   "intstat=%#8.8x.\n", dev->name, status,
+				   (int)inl(ioaddr + INTSTAT));
+	}
+
+	if ((status & IntrSummary) == 0)
+		goto out;
+
+	handled = 1;
+
+	if ((status & EpicNapiEvent) && !ep->reschedule_in_poll) {
+		spin_lock(&ep->napi_lock);
+		if (napi_schedule_prep(&ep->napi)) {
+			epic_napi_irq_off(dev, ep);
+			__napi_schedule(&ep->napi);
+		} else
+			ep->reschedule_in_poll++;
+		spin_unlock(&ep->napi_lock);
+	}
+	status &= ~EpicNapiEvent;
+
+	/* Check uncommon events all at once. */
+	if (status & (CntFull | TxUnderrun | PCIBusErr170 | PCIBusErr175)) {
+		if (status == EpicRemoved)
+			goto out;
+
+		/* Always update the error counts to avoid overhead later. */
+		dev->stats.rx_missed_errors += inb(ioaddr + MPCNT);
+		dev->stats.rx_frame_errors += inb(ioaddr + ALICNT);
+		dev->stats.rx_crc_errors += inb(ioaddr + CRCCNT);
+
+		if (status & TxUnderrun) { /* Tx FIFO underflow. */
+			dev->stats.tx_fifo_errors++;
+			outl(ep->tx_threshold += 128, ioaddr + TxThresh);
+			/* Restart the transmit process. */
+			outl(RestartTx, ioaddr + COMMAND);
+		}
+		if (status & PCIBusErr170) {
+			printk(KERN_ERR "%s: PCI Bus Error! status %4.4x.\n",
+					 dev->name, status);
+			epic_pause(dev);
+			epic_restart(dev);
+		}
+		/* Clear all error sources. */
+		outl(status & 0x7f18, ioaddr + INTSTAT);
+	}
+
+out:
+	if (debug > 3) {
+		printk(KERN_DEBUG "%s: exit interrupt, intr_status=%#4.4x.\n",
+				   dev->name, status);
+	}
+
+	return IRQ_RETVAL(handled);
+}
+
+static int epic_rx(struct net_device *dev, int budget)
+{
+	struct epic_private *ep = netdev_priv(dev);
+	int entry = ep->cur_rx % RX_RING_SIZE;
+	int rx_work_limit = ep->dirty_rx + RX_RING_SIZE - ep->cur_rx;
+	int work_done = 0;
+
+	if (debug > 4)
+		printk(KERN_DEBUG " In epic_rx(), entry %d %8.8x.\n", entry,
+			   ep->rx_ring[entry].rxstatus);
+
+	if (rx_work_limit > budget)
+		rx_work_limit = budget;
+
+	/* If we own the next entry, it's a new packet. Send it up. */
+	while ((ep->rx_ring[entry].rxstatus & DescOwn) == 0) {
+		int status = ep->rx_ring[entry].rxstatus;
+
+		if (debug > 4)
+			printk(KERN_DEBUG "  epic_rx() status was %8.8x.\n", status);
+		if (--rx_work_limit < 0)
+			break;
+		if (status & 0x2006) {
+			if (debug > 2)
+				printk(KERN_DEBUG "%s: epic_rx() error status was %8.8x.\n",
+					   dev->name, status);
+			if (status & 0x2000) {
+				printk(KERN_WARNING "%s: Oversized Ethernet frame spanned "
+					   "multiple buffers, status %4.4x!\n", dev->name, status);
+				dev->stats.rx_length_errors++;
+			} else if (status & 0x0006)
+				/* Rx Frame errors are counted in hardware. */
+				dev->stats.rx_errors++;
+		} else {
+			/* Malloc up new buffer, compatible with net-2e. */
+			/* Omit the four octet CRC from the length. */
+			short pkt_len = (status >> 16) - 4;
+			struct sk_buff *skb;
+
+			if (pkt_len > PKT_BUF_SZ - 4) {
+				printk(KERN_ERR "%s: Oversized Ethernet frame, status %x "
+					   "%d bytes.\n",
+					   dev->name, status, pkt_len);
+				pkt_len = 1514;
+			}
+			/* Check if the packet is long enough to accept without copying
+			   to a minimally-sized skbuff. */
+			if (pkt_len < rx_copybreak &&
+			    (skb = dev_alloc_skb(pkt_len + 2)) != NULL) {
+				skb_reserve(skb, 2);	/* 16 byte align the IP header */
+				pci_dma_sync_single_for_cpu(ep->pci_dev,
+							    ep->rx_ring[entry].bufaddr,
+							    ep->rx_buf_sz,
+							    PCI_DMA_FROMDEVICE);
+				skb_copy_to_linear_data(skb, ep->rx_skbuff[entry]->data, pkt_len);
+				skb_put(skb, pkt_len);
+				pci_dma_sync_single_for_device(ep->pci_dev,
+							       ep->rx_ring[entry].bufaddr,
+							       ep->rx_buf_sz,
+							       PCI_DMA_FROMDEVICE);
+			} else {
+				pci_unmap_single(ep->pci_dev,
+					ep->rx_ring[entry].bufaddr,
+					ep->rx_buf_sz, PCI_DMA_FROMDEVICE);
+				skb_put(skb = ep->rx_skbuff[entry], pkt_len);
+				ep->rx_skbuff[entry] = NULL;
+			}
+			skb->protocol = eth_type_trans(skb, dev);
+			netif_receive_skb(skb);
+			dev->stats.rx_packets++;
+			dev->stats.rx_bytes += pkt_len;
+		}
+		work_done++;
+		entry = (++ep->cur_rx) % RX_RING_SIZE;
+	}
+
+	/* Refill the Rx ring buffers. */
+	for (; ep->cur_rx - ep->dirty_rx > 0; ep->dirty_rx++) {
+		entry = ep->dirty_rx % RX_RING_SIZE;
+		if (ep->rx_skbuff[entry] == NULL) {
+			struct sk_buff *skb;
+			skb = ep->rx_skbuff[entry] = dev_alloc_skb(ep->rx_buf_sz + 2);
+			if (skb == NULL)
+				break;
+			skb_reserve(skb, 2);	/* Align IP on 16 byte boundaries */
+			ep->rx_ring[entry].bufaddr = pci_map_single(ep->pci_dev,
+				skb->data, ep->rx_buf_sz, PCI_DMA_FROMDEVICE);
+			work_done++;
+		}
+		/* AV: shouldn't we add a barrier here? */
+		ep->rx_ring[entry].rxstatus = DescOwn;
+	}
+	return work_done;
+}
+
+static void epic_rx_err(struct net_device *dev, struct epic_private *ep)
+{
+	long ioaddr = dev->base_addr;
+	int status;
+
+	status = inl(ioaddr + INTSTAT);
+
+	if (status == EpicRemoved)
+		return;
+	if (status & RxOverflow) 	/* Missed a Rx frame. */
+		dev->stats.rx_errors++;
+	if (status & (RxOverflow | RxFull))
+		outw(RxQueued, ioaddr + COMMAND);
+}
+
+static int epic_poll(struct napi_struct *napi, int budget)
+{
+	struct epic_private *ep = container_of(napi, struct epic_private, napi);
+	struct net_device *dev = ep->mii.dev;
+	int work_done = 0;
+	long ioaddr = dev->base_addr;
+
+rx_action:
+
+	epic_tx(dev, ep);
+
+	work_done += epic_rx(dev, budget);
+
+	epic_rx_err(dev, ep);
+
+	if (work_done < budget) {
+		unsigned long flags;
+		int more;
+
+		/* A bit baroque but it avoids a (space hungry) spin_unlock */
+
+		spin_lock_irqsave(&ep->napi_lock, flags);
+
+		more = ep->reschedule_in_poll;
+		if (!more) {
+			__napi_complete(napi);
+			outl(EpicNapiEvent, ioaddr + INTSTAT);
+			epic_napi_irq_on(dev, ep);
+		} else
+			ep->reschedule_in_poll--;
+
+		spin_unlock_irqrestore(&ep->napi_lock, flags);
+
+		if (more)
+			goto rx_action;
+	}
+
+	return work_done;
+}
+
+static int epic_close(struct net_device *dev)
+{
+	long ioaddr = dev->base_addr;
+	struct epic_private *ep = netdev_priv(dev);
+	struct sk_buff *skb;
+	int i;
+
+	netif_stop_queue(dev);
+	napi_disable(&ep->napi);
+
+	if (debug > 1)
+		printk(KERN_DEBUG "%s: Shutting down ethercard, status was %2.2x.\n",
+			   dev->name, (int)inl(ioaddr + INTSTAT));
+
+	del_timer_sync(&ep->timer);
+
+	epic_disable_int(dev, ep);
+
+	free_irq(dev->irq, dev);
+
+	epic_pause(dev);
+
+	/* Free all the skbuffs in the Rx queue. */
+	for (i = 0; i < RX_RING_SIZE; i++) {
+		skb = ep->rx_skbuff[i];
+		ep->rx_skbuff[i] = NULL;
+		ep->rx_ring[i].rxstatus = 0;		/* Not owned by Epic chip. */
+		ep->rx_ring[i].buflength = 0;
+		if (skb) {
+			pci_unmap_single(ep->pci_dev, ep->rx_ring[i].bufaddr,
+				 	 ep->rx_buf_sz, PCI_DMA_FROMDEVICE);
+			dev_kfree_skb(skb);
+		}
+		ep->rx_ring[i].bufaddr = 0xBADF00D0; /* An invalid address. */
+	}
+	for (i = 0; i < TX_RING_SIZE; i++) {
+		skb = ep->tx_skbuff[i];
+		ep->tx_skbuff[i] = NULL;
+		if (!skb)
+			continue;
+		pci_unmap_single(ep->pci_dev, ep->tx_ring[i].bufaddr,
+				 skb->len, PCI_DMA_TODEVICE);
+		dev_kfree_skb(skb);
+	}
+
+	/* Green! Leave the chip in low-power mode. */
+	outl(0x0008, ioaddr + GENCTL);
+
+	return 0;
+}
+
+static struct net_device_stats *epic_get_stats(struct net_device *dev)
+{
+	long ioaddr = dev->base_addr;
+
+	if (netif_running(dev)) {
+		/* Update the error counts. */
+		dev->stats.rx_missed_errors += inb(ioaddr + MPCNT);
+		dev->stats.rx_frame_errors += inb(ioaddr + ALICNT);
+		dev->stats.rx_crc_errors += inb(ioaddr + CRCCNT);
+	}
+
+	return &dev->stats;
+}
+
+/* Set or clear the multicast filter for this adaptor.
+   Note that we only use exclusion around actually queueing the
+   new frame, not around filling ep->setup_frame.  This is non-deterministic
+   when re-entered but still correct. */
+
+static void set_rx_mode(struct net_device *dev)
+{
+	long ioaddr = dev->base_addr;
+	struct epic_private *ep = netdev_priv(dev);
+	unsigned char mc_filter[8];		 /* Multicast hash filter */
+	int i;
+
+	if (dev->flags & IFF_PROMISC) {			/* Set promiscuous. */
+		outl(0x002C, ioaddr + RxCtrl);
+		/* Unconditionally log net taps. */
+		memset(mc_filter, 0xff, sizeof(mc_filter));
+	} else if ((!netdev_mc_empty(dev)) || (dev->flags & IFF_ALLMULTI)) {
+		/* There is apparently a chip bug, so the multicast filter
+		   is never enabled. */
+		/* Too many to filter perfectly -- accept all multicasts. */
+		memset(mc_filter, 0xff, sizeof(mc_filter));
+		outl(0x000C, ioaddr + RxCtrl);
+	} else if (netdev_mc_empty(dev)) {
+		outl(0x0004, ioaddr + RxCtrl);
+		return;
+	} else {					/* Never executed, for now. */
+		struct netdev_hw_addr *ha;
+
+		memset(mc_filter, 0, sizeof(mc_filter));
+		netdev_for_each_mc_addr(ha, dev) {
+			unsigned int bit_nr =
+				ether_crc_le(ETH_ALEN, ha->addr) & 0x3f;
+			mc_filter[bit_nr >> 3] |= (1 << bit_nr);
+		}
+	}
+	/* ToDo: perhaps we need to stop the Tx and Rx process here? */
+	if (memcmp(mc_filter, ep->mc_filter, sizeof(mc_filter))) {
+		for (i = 0; i < 4; i++)
+			outw(((u16 *)mc_filter)[i], ioaddr + MC0 + i*4);
+		memcpy(ep->mc_filter, mc_filter, sizeof(mc_filter));
+	}
+}
+
+static void netdev_get_drvinfo (struct net_device *dev, struct ethtool_drvinfo *info)
+{
+	struct epic_private *np = netdev_priv(dev);
+
+	strcpy (info->driver, DRV_NAME);
+	strcpy (info->version, DRV_VERSION);
+	strcpy (info->bus_info, pci_name(np->pci_dev));
+}
+
+static int netdev_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+	struct epic_private *np = netdev_priv(dev);
+	int rc;
+
+	spin_lock_irq(&np->lock);
+	rc = mii_ethtool_gset(&np->mii, cmd);
+	spin_unlock_irq(&np->lock);
+
+	return rc;
+}
+
+static int netdev_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+	struct epic_private *np = netdev_priv(dev);
+	int rc;
+
+	spin_lock_irq(&np->lock);
+	rc = mii_ethtool_sset(&np->mii, cmd);
+	spin_unlock_irq(&np->lock);
+
+	return rc;
+}
+
+static int netdev_nway_reset(struct net_device *dev)
+{
+	struct epic_private *np = netdev_priv(dev);
+	return mii_nway_restart(&np->mii);
+}
+
+static u32 netdev_get_link(struct net_device *dev)
+{
+	struct epic_private *np = netdev_priv(dev);
+	return mii_link_ok(&np->mii);
+}
+
+static u32 netdev_get_msglevel(struct net_device *dev)
+{
+	return debug;
+}
+
+static void netdev_set_msglevel(struct net_device *dev, u32 value)
+{
+	debug = value;
+}
+
+static int ethtool_begin(struct net_device *dev)
+{
+	unsigned long ioaddr = dev->base_addr;
+	/* power-up, if interface is down */
+	if (! netif_running(dev)) {
+		outl(0x0200, ioaddr + GENCTL);
+		outl((inl(ioaddr + NVCTL) & ~0x003C) | 0x4800, ioaddr + NVCTL);
+	}
+	return 0;
+}
+
+static void ethtool_complete(struct net_device *dev)
+{
+	unsigned long ioaddr = dev->base_addr;
+	/* power-down, if interface is down */
+	if (! netif_running(dev)) {
+		outl(0x0008, ioaddr + GENCTL);
+		outl((inl(ioaddr + NVCTL) & ~0x483C) | 0x0000, ioaddr + NVCTL);
+	}
+}
+
+static const struct ethtool_ops netdev_ethtool_ops = {
+	.get_drvinfo		= netdev_get_drvinfo,
+	.get_settings		= netdev_get_settings,
+	.set_settings		= netdev_set_settings,
+	.nway_reset		= netdev_nway_reset,
+	.get_link		= netdev_get_link,
+	.get_msglevel		= netdev_get_msglevel,
+	.set_msglevel		= netdev_set_msglevel,
+	.begin			= ethtool_begin,
+	.complete		= ethtool_complete
+};
+
+static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+	struct epic_private *np = netdev_priv(dev);
+	long ioaddr = dev->base_addr;
+	struct mii_ioctl_data *data = if_mii(rq);
+	int rc;
+
+	/* power-up, if interface is down */
+	if (! netif_running(dev)) {
+		outl(0x0200, ioaddr + GENCTL);
+		outl((inl(ioaddr + NVCTL) & ~0x003C) | 0x4800, ioaddr + NVCTL);
+	}
+
+	/* all non-ethtool ioctls (the SIOC[GS]MIIxxx ioctls) */
+	spin_lock_irq(&np->lock);
+	rc = generic_mii_ioctl(&np->mii, data, cmd, NULL);
+	spin_unlock_irq(&np->lock);
+
+	/* power-down, if interface is down */
+	if (! netif_running(dev)) {
+		outl(0x0008, ioaddr + GENCTL);
+		outl((inl(ioaddr + NVCTL) & ~0x483C) | 0x0000, ioaddr + NVCTL);
+	}
+	return rc;
+}
+
+
+static void __devexit epic_remove_one (struct pci_dev *pdev)
+{
+	struct net_device *dev = pci_get_drvdata(pdev);
+	struct epic_private *ep = netdev_priv(dev);
+
+	pci_free_consistent(pdev, TX_TOTAL_SIZE, ep->tx_ring, ep->tx_ring_dma);
+	pci_free_consistent(pdev, RX_TOTAL_SIZE, ep->rx_ring, ep->rx_ring_dma);
+	unregister_netdev(dev);
+#ifndef USE_IO_OPS
+	iounmap((void*) dev->base_addr);
+#endif
+	pci_release_regions(pdev);
+	free_netdev(dev);
+	pci_disable_device(pdev);
+	pci_set_drvdata(pdev, NULL);
+	/* pci_power_off(pdev, -1); */
+}
+
+
+#ifdef CONFIG_PM
+
+static int epic_suspend (struct pci_dev *pdev, pm_message_t state)
+{
+	struct net_device *dev = pci_get_drvdata(pdev);
+	long ioaddr = dev->base_addr;
+
+	if (!netif_running(dev))
+		return 0;
+	epic_pause(dev);
+	/* Put the chip into low-power mode. */
+	outl(0x0008, ioaddr + GENCTL);
+	/* pci_power_off(pdev, -1); */
+	return 0;
+}
+
+
+static int epic_resume (struct pci_dev *pdev)
+{
+	struct net_device *dev = pci_get_drvdata(pdev);
+
+	if (!netif_running(dev))
+		return 0;
+	epic_restart(dev);
+	/* pci_power_on(pdev); */
+	return 0;
+}
+
+#endif /* CONFIG_PM */
+
+
+static struct pci_driver epic_driver = {
+	.name		= DRV_NAME,
+	.id_table	= epic_pci_tbl,
+	.probe		= epic_init_one,
+	.remove		= __devexit_p(epic_remove_one),
+#ifdef CONFIG_PM
+	.suspend	= epic_suspend,
+	.resume		= epic_resume,
+#endif /* CONFIG_PM */
+};
+
+
+static int __init epic_init (void)
+{
+/* when a module, this is printed whether or not devices are found in probe */
+#ifdef MODULE
+	printk (KERN_INFO "%s%s",
+		version, version2);
+#endif
+
+	return pci_register_driver(&epic_driver);
+}
+
+
+static void __exit epic_cleanup (void)
+{
+	pci_unregister_driver (&epic_driver);
+}
+
+
+module_init(epic_init);
+module_exit(epic_cleanup);
diff --git a/drivers/net/ethernet/smsc/smc911x.c b/drivers/net/ethernet/smsc/smc911x.c
new file mode 100644
index 0000000..a91fe17
--- /dev/null
+++ b/drivers/net/ethernet/smsc/smc911x.c
@@ -0,0 +1,2210 @@
+/*
+ * smc911x.c
+ * This is a driver for SMSC's LAN911{5,6,7,8} single-chip Ethernet devices.
+ *
+ * Copyright (C) 2005 Sensoria Corp
+ *	   Derived from the unified SMC91x driver by Nicolas Pitre
+ *	   and the smsc911x.c reference driver by SMSC
+ *
+ * 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
+ *
+ * Arguments:
+ *	 watchdog  = TX watchdog timeout
+ *	 tx_fifo_kb = Size of TX FIFO in KB
+ *
+ * History:
+ *	  04/16/05	Dustin McIntire		 Initial version
+ */
+static const char version[] =
+	 "smc911x.c: v1.0 04-16-2005 by Dustin McIntire <dustin@sensoria.com>\n";
+
+/* Debugging options */
+#define ENABLE_SMC_DEBUG_RX		0
+#define ENABLE_SMC_DEBUG_TX		0
+#define ENABLE_SMC_DEBUG_DMA		0
+#define ENABLE_SMC_DEBUG_PKTS		0
+#define ENABLE_SMC_DEBUG_MISC		0
+#define ENABLE_SMC_DEBUG_FUNC		0
+
+#define SMC_DEBUG_RX		((ENABLE_SMC_DEBUG_RX	? 1 : 0) << 0)
+#define SMC_DEBUG_TX		((ENABLE_SMC_DEBUG_TX	? 1 : 0) << 1)
+#define SMC_DEBUG_DMA		((ENABLE_SMC_DEBUG_DMA	? 1 : 0) << 2)
+#define SMC_DEBUG_PKTS		((ENABLE_SMC_DEBUG_PKTS ? 1 : 0) << 3)
+#define SMC_DEBUG_MISC		((ENABLE_SMC_DEBUG_MISC ? 1 : 0) << 4)
+#define SMC_DEBUG_FUNC		((ENABLE_SMC_DEBUG_FUNC ? 1 : 0) << 5)
+
+#ifndef SMC_DEBUG
+#define SMC_DEBUG	 ( SMC_DEBUG_RX	  | \
+			   SMC_DEBUG_TX	  | \
+			   SMC_DEBUG_DMA  | \
+			   SMC_DEBUG_PKTS | \
+			   SMC_DEBUG_MISC | \
+			   SMC_DEBUG_FUNC   \
+			 )
+#endif
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/crc32.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/ethtool.h>
+#include <linux/mii.h>
+#include <linux/workqueue.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include <asm/io.h>
+
+#include "smc911x.h"
+
+/*
+ * Transmit timeout, default 5 seconds.
+ */
+static int watchdog = 5000;
+module_param(watchdog, int, 0400);
+MODULE_PARM_DESC(watchdog, "transmit timeout in milliseconds");
+
+static int tx_fifo_kb=8;
+module_param(tx_fifo_kb, int, 0400);
+MODULE_PARM_DESC(tx_fifo_kb,"transmit FIFO size in KB (1<x<15)(default=8)");
+
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:smc911x");
+
+/*
+ * The internal workings of the driver.  If you are changing anything
+ * here with the SMC stuff, you should have the datasheet and know
+ * what you are doing.
+ */
+#define CARDNAME "smc911x"
+
+/*
+ * Use power-down feature of the chip
+ */
+#define POWER_DOWN		 1
+
+#if SMC_DEBUG > 0
+#define DBG(n, args...)				 \
+	do {					 \
+		if (SMC_DEBUG & (n))		 \
+			printk(args);		 \
+	} while (0)
+
+#define PRINTK(args...)   printk(args)
+#else
+#define DBG(n, args...)   do { } while (0)
+#define PRINTK(args...)   printk(KERN_DEBUG args)
+#endif
+
+#if SMC_DEBUG_PKTS > 0
+static void PRINT_PKT(u_char *buf, int length)
+{
+	int i;
+	int remainder;
+	int lines;
+
+	lines = length / 16;
+	remainder = length % 16;
+
+	for (i = 0; i < lines ; i ++) {
+		int cur;
+		for (cur = 0; cur < 8; cur++) {
+			u_char a, b;
+			a = *buf++;
+			b = *buf++;
+			printk("%02x%02x ", a, b);
+		}
+		printk("\n");
+	}
+	for (i = 0; i < remainder/2 ; i++) {
+		u_char a, b;
+		a = *buf++;
+		b = *buf++;
+		printk("%02x%02x ", a, b);
+	}
+	printk("\n");
+}
+#else
+#define PRINT_PKT(x...)  do { } while (0)
+#endif
+
+
+/* this enables an interrupt in the interrupt mask register */
+#define SMC_ENABLE_INT(lp, x) do {			\
+	unsigned int  __mask;				\
+	__mask = SMC_GET_INT_EN((lp));			\
+	__mask |= (x);					\
+	SMC_SET_INT_EN((lp), __mask);			\
+} while (0)
+
+/* this disables an interrupt from the interrupt mask register */
+#define SMC_DISABLE_INT(lp, x) do {			\
+	unsigned int  __mask;				\
+	__mask = SMC_GET_INT_EN((lp));			\
+	__mask &= ~(x);					\
+	SMC_SET_INT_EN((lp), __mask);			\
+} while (0)
+
+/*
+ * this does a soft reset on the device
+ */
+static void smc911x_reset(struct net_device *dev)
+{
+	struct smc911x_local *lp = netdev_priv(dev);
+	unsigned int reg, timeout=0, resets=1, irq_cfg;
+	unsigned long flags;
+
+	DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __func__);
+
+	/*	 Take out of PM setting first */
+	if ((SMC_GET_PMT_CTRL(lp) & PMT_CTRL_READY_) == 0) {
+		/* Write to the bytetest will take out of powerdown */
+		SMC_SET_BYTE_TEST(lp, 0);
+		timeout=10;
+		do {
+			udelay(10);
+			reg = SMC_GET_PMT_CTRL(lp) & PMT_CTRL_READY_;
+		} while (--timeout && !reg);
+		if (timeout == 0) {
+			PRINTK("%s: smc911x_reset timeout waiting for PM restore\n", dev->name);
+			return;
+		}
+	}
+
+	/* Disable all interrupts */
+	spin_lock_irqsave(&lp->lock, flags);
+	SMC_SET_INT_EN(lp, 0);
+	spin_unlock_irqrestore(&lp->lock, flags);
+
+	while (resets--) {
+		SMC_SET_HW_CFG(lp, HW_CFG_SRST_);
+		timeout=10;
+		do {
+			udelay(10);
+			reg = SMC_GET_HW_CFG(lp);
+			/* If chip indicates reset timeout then try again */
+			if (reg & HW_CFG_SRST_TO_) {
+				PRINTK("%s: chip reset timeout, retrying...\n", dev->name);
+				resets++;
+				break;
+			}
+		} while (--timeout && (reg & HW_CFG_SRST_));
+	}
+	if (timeout == 0) {
+		PRINTK("%s: smc911x_reset timeout waiting for reset\n", dev->name);
+		return;
+	}
+
+	/* make sure EEPROM has finished loading before setting GPIO_CFG */
+	timeout=1000;
+	while (--timeout && (SMC_GET_E2P_CMD(lp) & E2P_CMD_EPC_BUSY_))
+		udelay(10);
+
+	if (timeout == 0){
+		PRINTK("%s: smc911x_reset timeout waiting for EEPROM busy\n", dev->name);
+		return;
+	}
+
+	/* Initialize interrupts */
+	SMC_SET_INT_EN(lp, 0);
+	SMC_ACK_INT(lp, -1);
+
+	/* Reset the FIFO level and flow control settings */
+	SMC_SET_HW_CFG(lp, (lp->tx_fifo_kb & 0xF) << 16);
+//TODO: Figure out what appropriate pause time is
+	SMC_SET_FLOW(lp, FLOW_FCPT_ | FLOW_FCEN_);
+	SMC_SET_AFC_CFG(lp, lp->afc_cfg);
+
+
+	/* Set to LED outputs */
+	SMC_SET_GPIO_CFG(lp, 0x70070000);
+
+	/*
+	 * Deassert IRQ for 1*10us for edge type interrupts
+	 * and drive IRQ pin push-pull
+	 */
+	irq_cfg = (1 << 24) | INT_CFG_IRQ_EN_ | INT_CFG_IRQ_TYPE_;
+#ifdef SMC_DYNAMIC_BUS_CONFIG
+	if (lp->cfg.irq_polarity)
+		irq_cfg |= INT_CFG_IRQ_POL_;
+#endif
+	SMC_SET_IRQ_CFG(lp, irq_cfg);
+
+	/* clear anything saved */
+	if (lp->pending_tx_skb != NULL) {
+		dev_kfree_skb (lp->pending_tx_skb);
+		lp->pending_tx_skb = NULL;
+		dev->stats.tx_errors++;
+		dev->stats.tx_aborted_errors++;
+	}
+}
+
+/*
+ * Enable Interrupts, Receive, and Transmit
+ */
+static void smc911x_enable(struct net_device *dev)
+{
+	struct smc911x_local *lp = netdev_priv(dev);
+	unsigned mask, cfg, cr;
+	unsigned long flags;
+
+	DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __func__);
+
+	spin_lock_irqsave(&lp->lock, flags);
+
+	SMC_SET_MAC_ADDR(lp, dev->dev_addr);
+
+	/* Enable TX */
+	cfg = SMC_GET_HW_CFG(lp);
+	cfg &= HW_CFG_TX_FIF_SZ_ | 0xFFF;
+	cfg |= HW_CFG_SF_;
+	SMC_SET_HW_CFG(lp, cfg);
+	SMC_SET_FIFO_TDA(lp, 0xFF);
+	/* Update TX stats on every 64 packets received or every 1 sec */
+	SMC_SET_FIFO_TSL(lp, 64);
+	SMC_SET_GPT_CFG(lp, GPT_CFG_TIMER_EN_ | 10000);
+
+	SMC_GET_MAC_CR(lp, cr);
+	cr |= MAC_CR_TXEN_ | MAC_CR_HBDIS_;
+	SMC_SET_MAC_CR(lp, cr);
+	SMC_SET_TX_CFG(lp, TX_CFG_TX_ON_);
+
+	/* Add 2 byte padding to start of packets */
+	SMC_SET_RX_CFG(lp, (2<<8) & RX_CFG_RXDOFF_);
+
+	/* Turn on receiver and enable RX */
+	if (cr & MAC_CR_RXEN_)
+		DBG(SMC_DEBUG_RX, "%s: Receiver already enabled\n", dev->name);
+
+	SMC_SET_MAC_CR(lp, cr | MAC_CR_RXEN_);
+
+	/* Interrupt on every received packet */
+	SMC_SET_FIFO_RSA(lp, 0x01);
+	SMC_SET_FIFO_RSL(lp, 0x00);
+
+	/* now, enable interrupts */
+	mask = INT_EN_TDFA_EN_ | INT_EN_TSFL_EN_ | INT_EN_RSFL_EN_ |
+		INT_EN_GPT_INT_EN_ | INT_EN_RXDFH_INT_EN_ | INT_EN_RXE_EN_ |
+		INT_EN_PHY_INT_EN_;
+	if (IS_REV_A(lp->revision))
+		mask|=INT_EN_RDFL_EN_;
+	else {
+		mask|=INT_EN_RDFO_EN_;
+	}
+	SMC_ENABLE_INT(lp, mask);
+
+	spin_unlock_irqrestore(&lp->lock, flags);
+}
+
+/*
+ * this puts the device in an inactive state
+ */
+static void smc911x_shutdown(struct net_device *dev)
+{
+	struct smc911x_local *lp = netdev_priv(dev);
+	unsigned cr;
+	unsigned long flags;
+
+	DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", CARDNAME, __func__);
+
+	/* Disable IRQ's */
+	SMC_SET_INT_EN(lp, 0);
+
+	/* Turn of Rx and TX */
+	spin_lock_irqsave(&lp->lock, flags);
+	SMC_GET_MAC_CR(lp, cr);
+	cr &= ~(MAC_CR_TXEN_ | MAC_CR_RXEN_ | MAC_CR_HBDIS_);
+	SMC_SET_MAC_CR(lp, cr);
+	SMC_SET_TX_CFG(lp, TX_CFG_STOP_TX_);
+	spin_unlock_irqrestore(&lp->lock, flags);
+}
+
+static inline void smc911x_drop_pkt(struct net_device *dev)
+{
+	struct smc911x_local *lp = netdev_priv(dev);
+	unsigned int fifo_count, timeout, reg;
+
+	DBG(SMC_DEBUG_FUNC | SMC_DEBUG_RX, "%s: --> %s\n", CARDNAME, __func__);
+	fifo_count = SMC_GET_RX_FIFO_INF(lp) & 0xFFFF;
+	if (fifo_count <= 4) {
+		/* Manually dump the packet data */
+		while (fifo_count--)
+			SMC_GET_RX_FIFO(lp);
+	} else	 {
+		/* Fast forward through the bad packet */
+		SMC_SET_RX_DP_CTRL(lp, RX_DP_CTRL_FFWD_BUSY_);
+		timeout=50;
+		do {
+			udelay(10);
+			reg = SMC_GET_RX_DP_CTRL(lp) & RX_DP_CTRL_FFWD_BUSY_;
+		} while (--timeout && reg);
+		if (timeout == 0) {
+			PRINTK("%s: timeout waiting for RX fast forward\n", dev->name);
+		}
+	}
+}
+
+/*
+ * This is the procedure to handle the receipt of a packet.
+ * It should be called after checking for packet presence in
+ * the RX status FIFO.	 It must be called with the spin lock
+ * already held.
+ */
+static inline void	 smc911x_rcv(struct net_device *dev)
+{
+	struct smc911x_local *lp = netdev_priv(dev);
+	unsigned int pkt_len, status;
+	struct sk_buff *skb;
+	unsigned char *data;
+
+	DBG(SMC_DEBUG_FUNC | SMC_DEBUG_RX, "%s: --> %s\n",
+		dev->name, __func__);
+	status = SMC_GET_RX_STS_FIFO(lp);
+	DBG(SMC_DEBUG_RX, "%s: Rx pkt len %d status 0x%08x\n",
+		dev->name, (status & 0x3fff0000) >> 16, status & 0xc000ffff);
+	pkt_len = (status & RX_STS_PKT_LEN_) >> 16;
+	if (status & RX_STS_ES_) {
+		/* Deal with a bad packet */
+		dev->stats.rx_errors++;
+		if (status & RX_STS_CRC_ERR_)
+			dev->stats.rx_crc_errors++;
+		else {
+			if (status & RX_STS_LEN_ERR_)
+				dev->stats.rx_length_errors++;
+			if (status & RX_STS_MCAST_)
+				dev->stats.multicast++;
+		}
+		/* Remove the bad packet data from the RX FIFO */
+		smc911x_drop_pkt(dev);
+	} else {
+		/* Receive a valid packet */
+		/* Alloc a buffer with extra room for DMA alignment */
+		skb=dev_alloc_skb(pkt_len+32);
+		if (unlikely(skb == NULL)) {
+			PRINTK( "%s: Low memory, rcvd packet dropped.\n",
+				dev->name);
+			dev->stats.rx_dropped++;
+			smc911x_drop_pkt(dev);
+			return;
+		}
+		/* Align IP header to 32 bits
+		 * Note that the device is configured to add a 2
+		 * byte padding to the packet start, so we really
+		 * want to write to the orignal data pointer */
+		data = skb->data;
+		skb_reserve(skb, 2);
+		skb_put(skb,pkt_len-4);
+#ifdef SMC_USE_DMA
+		{
+		unsigned int fifo;
+		/* Lower the FIFO threshold if possible */
+		fifo = SMC_GET_FIFO_INT(lp);
+		if (fifo & 0xFF) fifo--;
+		DBG(SMC_DEBUG_RX, "%s: Setting RX stat FIFO threshold to %d\n",
+			dev->name, fifo & 0xff);
+		SMC_SET_FIFO_INT(lp, fifo);
+		/* Setup RX DMA */
+		SMC_SET_RX_CFG(lp, RX_CFG_RX_END_ALGN16_ | ((2<<8) & RX_CFG_RXDOFF_));
+		lp->rxdma_active = 1;
+		lp->current_rx_skb = skb;
+		SMC_PULL_DATA(lp, data, (pkt_len+2+15) & ~15);
+		/* Packet processing deferred to DMA RX interrupt */
+		}
+#else
+		SMC_SET_RX_CFG(lp, RX_CFG_RX_END_ALGN4_ | ((2<<8) & RX_CFG_RXDOFF_));
+		SMC_PULL_DATA(lp, data, pkt_len+2+3);
+
+		DBG(SMC_DEBUG_PKTS, "%s: Received packet\n", dev->name);
+		PRINT_PKT(data, ((pkt_len - 4) <= 64) ? pkt_len - 4 : 64);
+		skb->protocol = eth_type_trans(skb, dev);
+		netif_rx(skb);
+		dev->stats.rx_packets++;
+		dev->stats.rx_bytes += pkt_len-4;
+#endif
+	}
+}
+
+/*
+ * This is called to actually send a packet to the chip.
+ */
+static void smc911x_hardware_send_pkt(struct net_device *dev)
+{
+	struct smc911x_local *lp = netdev_priv(dev);
+	struct sk_buff *skb;
+	unsigned int cmdA, cmdB, len;
+	unsigned char *buf;
+
+	DBG(SMC_DEBUG_FUNC | SMC_DEBUG_TX, "%s: --> %s\n", dev->name, __func__);
+	BUG_ON(lp->pending_tx_skb == NULL);
+
+	skb = lp->pending_tx_skb;
+	lp->pending_tx_skb = NULL;
+
+	/* cmdA {25:24] data alignment [20:16] start offset [10:0] buffer length */
+	/* cmdB {31:16] pkt tag [10:0] length */
+#ifdef SMC_USE_DMA
+	/* 16 byte buffer alignment mode */
+	buf = (char*)((u32)(skb->data) & ~0xF);
+	len = (skb->len + 0xF + ((u32)skb->data & 0xF)) & ~0xF;
+	cmdA = (1<<24) | (((u32)skb->data & 0xF)<<16) |
+			TX_CMD_A_INT_FIRST_SEG_ | TX_CMD_A_INT_LAST_SEG_ |
+			skb->len;
+#else
+	buf = (char*)((u32)skb->data & ~0x3);
+	len = (skb->len + 3 + ((u32)skb->data & 3)) & ~0x3;
+	cmdA = (((u32)skb->data & 0x3) << 16) |
+			TX_CMD_A_INT_FIRST_SEG_ | TX_CMD_A_INT_LAST_SEG_ |
+			skb->len;
+#endif
+	/* tag is packet length so we can use this in stats update later */
+	cmdB = (skb->len  << 16) | (skb->len & 0x7FF);
+
+	DBG(SMC_DEBUG_TX, "%s: TX PKT LENGTH 0x%04x (%d) BUF 0x%p CMDA 0x%08x CMDB 0x%08x\n",
+		 dev->name, len, len, buf, cmdA, cmdB);
+	SMC_SET_TX_FIFO(lp, cmdA);
+	SMC_SET_TX_FIFO(lp, cmdB);
+
+	DBG(SMC_DEBUG_PKTS, "%s: Transmitted packet\n", dev->name);
+	PRINT_PKT(buf, len <= 64 ? len : 64);
+
+	/* Send pkt via PIO or DMA */
+#ifdef SMC_USE_DMA
+	lp->current_tx_skb = skb;
+	SMC_PUSH_DATA(lp, buf, len);
+	/* DMA complete IRQ will free buffer and set jiffies */
+#else
+	SMC_PUSH_DATA(lp, buf, len);
+	dev->trans_start = jiffies;
+	dev_kfree_skb_irq(skb);
+#endif
+	if (!lp->tx_throttle) {
+		netif_wake_queue(dev);
+	}
+	SMC_ENABLE_INT(lp, INT_EN_TDFA_EN_ | INT_EN_TSFL_EN_);
+}
+
+/*
+ * Since I am not sure if I will have enough room in the chip's ram
+ * to store the packet, I call this routine which either sends it
+ * now, or set the card to generates an interrupt when ready
+ * for the packet.
+ */
+static int smc911x_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct smc911x_local *lp = netdev_priv(dev);
+	unsigned int free;
+	unsigned long flags;
+
+	DBG(SMC_DEBUG_FUNC | SMC_DEBUG_TX, "%s: --> %s\n",
+		dev->name, __func__);
+
+	spin_lock_irqsave(&lp->lock, flags);
+
+	BUG_ON(lp->pending_tx_skb != NULL);
+
+	free = SMC_GET_TX_FIFO_INF(lp) & TX_FIFO_INF_TDFREE_;
+	DBG(SMC_DEBUG_TX, "%s: TX free space %d\n", dev->name, free);
+
+	/* Turn off the flow when running out of space in FIFO */
+	if (free <= SMC911X_TX_FIFO_LOW_THRESHOLD) {
+		DBG(SMC_DEBUG_TX, "%s: Disabling data flow due to low FIFO space (%d)\n",
+			dev->name, free);
+		/* Reenable when at least 1 packet of size MTU present */
+		SMC_SET_FIFO_TDA(lp, (SMC911X_TX_FIFO_LOW_THRESHOLD)/64);
+		lp->tx_throttle = 1;
+		netif_stop_queue(dev);
+	}
+
+	/* Drop packets when we run out of space in TX FIFO
+	 * Account for overhead required for:
+	 *
+	 *	  Tx command words			 8 bytes
+	 *	  Start offset				 15 bytes
+	 *	  End padding				 15 bytes
+	 */
+	if (unlikely(free < (skb->len + 8 + 15 + 15))) {
+		printk("%s: No Tx free space %d < %d\n",
+			dev->name, free, skb->len);
+		lp->pending_tx_skb = NULL;
+		dev->stats.tx_errors++;
+		dev->stats.tx_dropped++;
+		spin_unlock_irqrestore(&lp->lock, flags);
+		dev_kfree_skb(skb);
+		return NETDEV_TX_OK;
+	}
+
+#ifdef SMC_USE_DMA
+	{
+		/* If the DMA is already running then defer this packet Tx until
+		 * the DMA IRQ starts it
+		 */
+		if (lp->txdma_active) {
+			DBG(SMC_DEBUG_TX | SMC_DEBUG_DMA, "%s: Tx DMA running, deferring packet\n", dev->name);
+			lp->pending_tx_skb = skb;
+			netif_stop_queue(dev);
+			spin_unlock_irqrestore(&lp->lock, flags);
+			return NETDEV_TX_OK;
+		} else {
+			DBG(SMC_DEBUG_TX | SMC_DEBUG_DMA, "%s: Activating Tx DMA\n", dev->name);
+			lp->txdma_active = 1;
+		}
+	}
+#endif
+	lp->pending_tx_skb = skb;
+	smc911x_hardware_send_pkt(dev);
+	spin_unlock_irqrestore(&lp->lock, flags);
+
+	return NETDEV_TX_OK;
+}
+
+/*
+ * This handles a TX status interrupt, which is only called when:
+ * - a TX error occurred, or
+ * - TX of a packet completed.
+ */
+static void smc911x_tx(struct net_device *dev)
+{
+	struct smc911x_local *lp = netdev_priv(dev);
+	unsigned int tx_status;
+
+	DBG(SMC_DEBUG_FUNC | SMC_DEBUG_TX, "%s: --> %s\n",
+		dev->name, __func__);
+
+	/* Collect the TX status */
+	while (((SMC_GET_TX_FIFO_INF(lp) & TX_FIFO_INF_TSUSED_) >> 16) != 0) {
+		DBG(SMC_DEBUG_TX, "%s: Tx stat FIFO used 0x%04x\n",
+			dev->name,
+			(SMC_GET_TX_FIFO_INF(lp) & TX_FIFO_INF_TSUSED_) >> 16);
+		tx_status = SMC_GET_TX_STS_FIFO(lp);
+		dev->stats.tx_packets++;
+		dev->stats.tx_bytes+=tx_status>>16;
+		DBG(SMC_DEBUG_TX, "%s: Tx FIFO tag 0x%04x status 0x%04x\n",
+			dev->name, (tx_status & 0xffff0000) >> 16,
+			tx_status & 0x0000ffff);
+		/* count Tx errors, but ignore lost carrier errors when in
+		 * full-duplex mode */
+		if ((tx_status & TX_STS_ES_) && !(lp->ctl_rfduplx &&
+		    !(tx_status & 0x00000306))) {
+			dev->stats.tx_errors++;
+		}
+		if (tx_status & TX_STS_MANY_COLL_) {
+			dev->stats.collisions+=16;
+			dev->stats.tx_aborted_errors++;
+		} else {
+			dev->stats.collisions+=(tx_status & TX_STS_COLL_CNT_) >> 3;
+		}
+		/* carrier error only has meaning for half-duplex communication */
+		if ((tx_status & (TX_STS_LOC_ | TX_STS_NO_CARR_)) &&
+		    !lp->ctl_rfduplx) {
+			dev->stats.tx_carrier_errors++;
+		}
+		if (tx_status & TX_STS_LATE_COLL_) {
+			dev->stats.collisions++;
+			dev->stats.tx_aborted_errors++;
+		}
+	}
+}
+
+
+/*---PHY CONTROL AND CONFIGURATION-----------------------------------------*/
+/*
+ * Reads a register from the MII Management serial interface
+ */
+
+static int smc911x_phy_read(struct net_device *dev, int phyaddr, int phyreg)
+{
+	struct smc911x_local *lp = netdev_priv(dev);
+	unsigned int phydata;
+
+	SMC_GET_MII(lp, phyreg, phyaddr, phydata);
+
+	DBG(SMC_DEBUG_MISC, "%s: phyaddr=0x%x, phyreg=0x%02x, phydata=0x%04x\n",
+		__func__, phyaddr, phyreg, phydata);
+	return phydata;
+}
+
+
+/*
+ * Writes a register to the MII Management serial interface
+ */
+static void smc911x_phy_write(struct net_device *dev, int phyaddr, int phyreg,
+			int phydata)
+{
+	struct smc911x_local *lp = netdev_priv(dev);
+
+	DBG(SMC_DEBUG_MISC, "%s: phyaddr=0x%x, phyreg=0x%x, phydata=0x%x\n",
+		__func__, phyaddr, phyreg, phydata);
+
+	SMC_SET_MII(lp, phyreg, phyaddr, phydata);
+}
+
+/*
+ * Finds and reports the PHY address (115 and 117 have external
+ * PHY interface 118 has internal only
+ */
+static void smc911x_phy_detect(struct net_device *dev)
+{
+	struct smc911x_local *lp = netdev_priv(dev);
+	int phyaddr;
+	unsigned int cfg, id1, id2;
+
+	DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __func__);
+
+	lp->phy_type = 0;
+
+	/*
+	 * Scan all 32 PHY addresses if necessary, starting at
+	 * PHY#1 to PHY#31, and then PHY#0 last.
+	 */
+	switch(lp->version) {
+		case CHIP_9115:
+		case CHIP_9117:
+		case CHIP_9215:
+		case CHIP_9217:
+			cfg = SMC_GET_HW_CFG(lp);
+			if (cfg & HW_CFG_EXT_PHY_DET_) {
+				cfg &= ~HW_CFG_PHY_CLK_SEL_;
+				cfg |= HW_CFG_PHY_CLK_SEL_CLK_DIS_;
+				SMC_SET_HW_CFG(lp, cfg);
+				udelay(10); /* Wait for clocks to stop */
+
+				cfg |= HW_CFG_EXT_PHY_EN_;
+				SMC_SET_HW_CFG(lp, cfg);
+				udelay(10); /* Wait for clocks to stop */
+
+				cfg &= ~HW_CFG_PHY_CLK_SEL_;
+				cfg |= HW_CFG_PHY_CLK_SEL_EXT_PHY_;
+				SMC_SET_HW_CFG(lp, cfg);
+				udelay(10); /* Wait for clocks to stop */
+
+				cfg |= HW_CFG_SMI_SEL_;
+				SMC_SET_HW_CFG(lp, cfg);
+
+				for (phyaddr = 1; phyaddr < 32; ++phyaddr) {
+
+					/* Read the PHY identifiers */
+					SMC_GET_PHY_ID1(lp, phyaddr & 31, id1);
+					SMC_GET_PHY_ID2(lp, phyaddr & 31, id2);
+
+					/* Make sure it is a valid identifier */
+					if (id1 != 0x0000 && id1 != 0xffff &&
+					    id1 != 0x8000 && id2 != 0x0000 &&
+					    id2 != 0xffff && id2 != 0x8000) {
+						/* Save the PHY's address */
+						lp->mii.phy_id = phyaddr & 31;
+						lp->phy_type = id1 << 16 | id2;
+						break;
+					}
+				}
+				if (phyaddr < 32)
+					/* Found an external PHY */
+					break;
+			}
+		default:
+			/* Internal media only */
+			SMC_GET_PHY_ID1(lp, 1, id1);
+			SMC_GET_PHY_ID2(lp, 1, id2);
+			/* Save the PHY's address */
+			lp->mii.phy_id = 1;
+			lp->phy_type = id1 << 16 | id2;
+	}
+
+	DBG(SMC_DEBUG_MISC, "%s: phy_id1=0x%x, phy_id2=0x%x phyaddr=0x%d\n",
+		dev->name, id1, id2, lp->mii.phy_id);
+}
+
+/*
+ * Sets the PHY to a configuration as determined by the user.
+ * Called with spin_lock held.
+ */
+static int smc911x_phy_fixed(struct net_device *dev)
+{
+	struct smc911x_local *lp = netdev_priv(dev);
+	int phyaddr = lp->mii.phy_id;
+	int bmcr;
+
+	DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __func__);
+
+	/* Enter Link Disable state */
+	SMC_GET_PHY_BMCR(lp, phyaddr, bmcr);
+	bmcr |= BMCR_PDOWN;
+	SMC_SET_PHY_BMCR(lp, phyaddr, bmcr);
+
+	/*
+	 * Set our fixed capabilities
+	 * Disable auto-negotiation
+	 */
+	bmcr &= ~BMCR_ANENABLE;
+	if (lp->ctl_rfduplx)
+		bmcr |= BMCR_FULLDPLX;
+
+	if (lp->ctl_rspeed == 100)
+		bmcr |= BMCR_SPEED100;
+
+	/* Write our capabilities to the phy control register */
+	SMC_SET_PHY_BMCR(lp, phyaddr, bmcr);
+
+	/* Re-Configure the Receive/Phy Control register */
+	bmcr &= ~BMCR_PDOWN;
+	SMC_SET_PHY_BMCR(lp, phyaddr, bmcr);
+
+	return 1;
+}
+
+/*
+ * smc911x_phy_reset - reset the phy
+ * @dev: net device
+ * @phy: phy address
+ *
+ * Issue a software reset for the specified PHY and
+ * wait up to 100ms for the reset to complete.	 We should
+ * not access the PHY for 50ms after issuing the reset.
+ *
+ * The time to wait appears to be dependent on the PHY.
+ *
+ */
+static int smc911x_phy_reset(struct net_device *dev, int phy)
+{
+	struct smc911x_local *lp = netdev_priv(dev);
+	int timeout;
+	unsigned long flags;
+	unsigned int reg;
+
+	DBG(SMC_DEBUG_FUNC, "%s: --> %s()\n", dev->name, __func__);
+
+	spin_lock_irqsave(&lp->lock, flags);
+	reg = SMC_GET_PMT_CTRL(lp);
+	reg &= ~0xfffff030;
+	reg |= PMT_CTRL_PHY_RST_;
+	SMC_SET_PMT_CTRL(lp, reg);
+	spin_unlock_irqrestore(&lp->lock, flags);
+	for (timeout = 2; timeout; timeout--) {
+		msleep(50);
+		spin_lock_irqsave(&lp->lock, flags);
+		reg = SMC_GET_PMT_CTRL(lp);
+		spin_unlock_irqrestore(&lp->lock, flags);
+		if (!(reg & PMT_CTRL_PHY_RST_)) {
+			/* extra delay required because the phy may
+			 * not be completed with its reset
+			 * when PHY_BCR_RESET_ is cleared. 256us
+			 * should suffice, but use 500us to be safe
+			 */
+			udelay(500);
+		break;
+		}
+	}
+
+	return reg & PMT_CTRL_PHY_RST_;
+}
+
+/*
+ * smc911x_phy_powerdown - powerdown phy
+ * @dev: net device
+ * @phy: phy address
+ *
+ * Power down the specified PHY
+ */
+static void smc911x_phy_powerdown(struct net_device *dev, int phy)
+{
+	struct smc911x_local *lp = netdev_priv(dev);
+	unsigned int bmcr;
+
+	/* Enter Link Disable state */
+	SMC_GET_PHY_BMCR(lp, phy, bmcr);
+	bmcr |= BMCR_PDOWN;
+	SMC_SET_PHY_BMCR(lp, phy, bmcr);
+}
+
+/*
+ * smc911x_phy_check_media - check the media status and adjust BMCR
+ * @dev: net device
+ * @init: set true for initialisation
+ *
+ * Select duplex mode depending on negotiation state.	This
+ * also updates our carrier state.
+ */
+static void smc911x_phy_check_media(struct net_device *dev, int init)
+{
+	struct smc911x_local *lp = netdev_priv(dev);
+	int phyaddr = lp->mii.phy_id;
+	unsigned int bmcr, cr;
+
+	DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __func__);
+
+	if (mii_check_media(&lp->mii, netif_msg_link(lp), init)) {
+		/* duplex state has changed */
+		SMC_GET_PHY_BMCR(lp, phyaddr, bmcr);
+		SMC_GET_MAC_CR(lp, cr);
+		if (lp->mii.full_duplex) {
+			DBG(SMC_DEBUG_MISC, "%s: Configuring for full-duplex mode\n", dev->name);
+			bmcr |= BMCR_FULLDPLX;
+			cr |= MAC_CR_RCVOWN_;
+		} else {
+			DBG(SMC_DEBUG_MISC, "%s: Configuring for half-duplex mode\n", dev->name);
+			bmcr &= ~BMCR_FULLDPLX;
+			cr &= ~MAC_CR_RCVOWN_;
+		}
+		SMC_SET_PHY_BMCR(lp, phyaddr, bmcr);
+		SMC_SET_MAC_CR(lp, cr);
+	}
+}
+
+/*
+ * Configures the specified PHY through the MII management interface
+ * using Autonegotiation.
+ * Calls smc911x_phy_fixed() if the user has requested a certain config.
+ * If RPC ANEG bit is set, the media selection is dependent purely on
+ * the selection by the MII (either in the MII BMCR reg or the result
+ * of autonegotiation.)  If the RPC ANEG bit is cleared, the selection
+ * is controlled by the RPC SPEED and RPC DPLX bits.
+ */
+static void smc911x_phy_configure(struct work_struct *work)
+{
+	struct smc911x_local *lp = container_of(work, struct smc911x_local,
+						phy_configure);
+	struct net_device *dev = lp->netdev;
+	int phyaddr = lp->mii.phy_id;
+	int my_phy_caps; /* My PHY capabilities */
+	int my_ad_caps; /* My Advertised capabilities */
+	int status;
+	unsigned long flags;
+
+	DBG(SMC_DEBUG_FUNC, "%s: --> %s()\n", dev->name, __func__);
+
+	/*
+	 * We should not be called if phy_type is zero.
+	 */
+	if (lp->phy_type == 0)
+		return;
+
+	if (smc911x_phy_reset(dev, phyaddr)) {
+		printk("%s: PHY reset timed out\n", dev->name);
+		return;
+	}
+	spin_lock_irqsave(&lp->lock, flags);
+
+	/*
+	 * Enable PHY Interrupts (for register 18)
+	 * Interrupts listed here are enabled
+	 */
+	SMC_SET_PHY_INT_MASK(lp, phyaddr, PHY_INT_MASK_ENERGY_ON_ |
+		 PHY_INT_MASK_ANEG_COMP_ | PHY_INT_MASK_REMOTE_FAULT_ |
+		 PHY_INT_MASK_LINK_DOWN_);
+
+	/* If the user requested no auto neg, then go set his request */
+	if (lp->mii.force_media) {
+		smc911x_phy_fixed(dev);
+		goto smc911x_phy_configure_exit;
+	}
+
+	/* Copy our capabilities from MII_BMSR to MII_ADVERTISE */
+	SMC_GET_PHY_BMSR(lp, phyaddr, my_phy_caps);
+	if (!(my_phy_caps & BMSR_ANEGCAPABLE)) {
+		printk(KERN_INFO "Auto negotiation NOT supported\n");
+		smc911x_phy_fixed(dev);
+		goto smc911x_phy_configure_exit;
+	}
+
+	/* CSMA capable w/ both pauses */
+	my_ad_caps = ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM;
+
+	if (my_phy_caps & BMSR_100BASE4)
+		my_ad_caps |= ADVERTISE_100BASE4;
+	if (my_phy_caps & BMSR_100FULL)
+		my_ad_caps |= ADVERTISE_100FULL;
+	if (my_phy_caps & BMSR_100HALF)
+		my_ad_caps |= ADVERTISE_100HALF;
+	if (my_phy_caps & BMSR_10FULL)
+		my_ad_caps |= ADVERTISE_10FULL;
+	if (my_phy_caps & BMSR_10HALF)
+		my_ad_caps |= ADVERTISE_10HALF;
+
+	/* Disable capabilities not selected by our user */
+	if (lp->ctl_rspeed != 100)
+		my_ad_caps &= ~(ADVERTISE_100BASE4|ADVERTISE_100FULL|ADVERTISE_100HALF);
+
+	 if (!lp->ctl_rfduplx)
+		my_ad_caps &= ~(ADVERTISE_100FULL|ADVERTISE_10FULL);
+
+	/* Update our Auto-Neg Advertisement Register */
+	SMC_SET_PHY_MII_ADV(lp, phyaddr, my_ad_caps);
+	lp->mii.advertising = my_ad_caps;
+
+	/*
+	 * Read the register back.	 Without this, it appears that when
+	 * auto-negotiation is restarted, sometimes it isn't ready and
+	 * the link does not come up.
+	 */
+	udelay(10);
+	SMC_GET_PHY_MII_ADV(lp, phyaddr, status);
+
+	DBG(SMC_DEBUG_MISC, "%s: phy caps=0x%04x\n", dev->name, my_phy_caps);
+	DBG(SMC_DEBUG_MISC, "%s: phy advertised caps=0x%04x\n", dev->name, my_ad_caps);
+
+	/* Restart auto-negotiation process in order to advertise my caps */
+	SMC_SET_PHY_BMCR(lp, phyaddr, BMCR_ANENABLE | BMCR_ANRESTART);
+
+	smc911x_phy_check_media(dev, 1);
+
+smc911x_phy_configure_exit:
+	spin_unlock_irqrestore(&lp->lock, flags);
+}
+
+/*
+ * smc911x_phy_interrupt
+ *
+ * Purpose:  Handle interrupts relating to PHY register 18. This is
+ *	 called from the "hard" interrupt handler under our private spinlock.
+ */
+static void smc911x_phy_interrupt(struct net_device *dev)
+{
+	struct smc911x_local *lp = netdev_priv(dev);
+	int phyaddr = lp->mii.phy_id;
+	int status;
+
+	DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __func__);
+
+	if (lp->phy_type == 0)
+		return;
+
+	smc911x_phy_check_media(dev, 0);
+	/* read to clear status bits */
+	SMC_GET_PHY_INT_SRC(lp, phyaddr,status);
+	DBG(SMC_DEBUG_MISC, "%s: PHY interrupt status 0x%04x\n",
+		dev->name, status & 0xffff);
+	DBG(SMC_DEBUG_MISC, "%s: AFC_CFG 0x%08x\n",
+		dev->name, SMC_GET_AFC_CFG(lp));
+}
+
+/*--- END PHY CONTROL AND CONFIGURATION-------------------------------------*/
+
+/*
+ * This is the main routine of the driver, to handle the device when
+ * it needs some attention.
+ */
+static irqreturn_t smc911x_interrupt(int irq, void *dev_id)
+{
+	struct net_device *dev = dev_id;
+	struct smc911x_local *lp = netdev_priv(dev);
+	unsigned int status, mask, timeout;
+	unsigned int rx_overrun=0, cr, pkts;
+	unsigned long flags;
+
+	DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __func__);
+
+	spin_lock_irqsave(&lp->lock, flags);
+
+	/* Spurious interrupt check */
+	if ((SMC_GET_IRQ_CFG(lp) & (INT_CFG_IRQ_INT_ | INT_CFG_IRQ_EN_)) !=
+		(INT_CFG_IRQ_INT_ | INT_CFG_IRQ_EN_)) {
+		spin_unlock_irqrestore(&lp->lock, flags);
+		return IRQ_NONE;
+	}
+
+	mask = SMC_GET_INT_EN(lp);
+	SMC_SET_INT_EN(lp, 0);
+
+	/* set a timeout value, so I don't stay here forever */
+	timeout = 8;
+
+
+	do {
+		status = SMC_GET_INT(lp);
+
+		DBG(SMC_DEBUG_MISC, "%s: INT 0x%08x MASK 0x%08x OUTSIDE MASK 0x%08x\n",
+			dev->name, status, mask, status & ~mask);
+
+		status &= mask;
+		if (!status)
+			break;
+
+		/* Handle SW interrupt condition */
+		if (status & INT_STS_SW_INT_) {
+			SMC_ACK_INT(lp, INT_STS_SW_INT_);
+			mask &= ~INT_EN_SW_INT_EN_;
+		}
+		/* Handle various error conditions */
+		if (status & INT_STS_RXE_) {
+			SMC_ACK_INT(lp, INT_STS_RXE_);
+			dev->stats.rx_errors++;
+		}
+		if (status & INT_STS_RXDFH_INT_) {
+			SMC_ACK_INT(lp, INT_STS_RXDFH_INT_);
+			dev->stats.rx_dropped+=SMC_GET_RX_DROP(lp);
+		 }
+		/* Undocumented interrupt-what is the right thing to do here? */
+		if (status & INT_STS_RXDF_INT_) {
+			SMC_ACK_INT(lp, INT_STS_RXDF_INT_);
+		}
+
+		/* Rx Data FIFO exceeds set level */
+		if (status & INT_STS_RDFL_) {
+			if (IS_REV_A(lp->revision)) {
+				rx_overrun=1;
+				SMC_GET_MAC_CR(lp, cr);
+				cr &= ~MAC_CR_RXEN_;
+				SMC_SET_MAC_CR(lp, cr);
+				DBG(SMC_DEBUG_RX, "%s: RX overrun\n", dev->name);
+				dev->stats.rx_errors++;
+				dev->stats.rx_fifo_errors++;
+			}
+			SMC_ACK_INT(lp, INT_STS_RDFL_);
+		}
+		if (status & INT_STS_RDFO_) {
+			if (!IS_REV_A(lp->revision)) {
+				SMC_GET_MAC_CR(lp, cr);
+				cr &= ~MAC_CR_RXEN_;
+				SMC_SET_MAC_CR(lp, cr);
+				rx_overrun=1;
+				DBG(SMC_DEBUG_RX, "%s: RX overrun\n", dev->name);
+				dev->stats.rx_errors++;
+				dev->stats.rx_fifo_errors++;
+			}
+			SMC_ACK_INT(lp, INT_STS_RDFO_);
+		}
+		/* Handle receive condition */
+		if ((status & INT_STS_RSFL_) || rx_overrun) {
+			unsigned int fifo;
+			DBG(SMC_DEBUG_RX, "%s: RX irq\n", dev->name);
+			fifo = SMC_GET_RX_FIFO_INF(lp);
+			pkts = (fifo & RX_FIFO_INF_RXSUSED_) >> 16;
+			DBG(SMC_DEBUG_RX, "%s: Rx FIFO pkts %d, bytes %d\n",
+				dev->name, pkts, fifo & 0xFFFF );
+			if (pkts != 0) {
+#ifdef SMC_USE_DMA
+				unsigned int fifo;
+				if (lp->rxdma_active){
+					DBG(SMC_DEBUG_RX | SMC_DEBUG_DMA,
+						"%s: RX DMA active\n", dev->name);
+					/* The DMA is already running so up the IRQ threshold */
+					fifo = SMC_GET_FIFO_INT(lp) & ~0xFF;
+					fifo |= pkts & 0xFF;
+					DBG(SMC_DEBUG_RX,
+						"%s: Setting RX stat FIFO threshold to %d\n",
+						dev->name, fifo & 0xff);
+					SMC_SET_FIFO_INT(lp, fifo);
+				} else
+#endif
+				smc911x_rcv(dev);
+			}
+			SMC_ACK_INT(lp, INT_STS_RSFL_);
+		}
+		/* Handle transmit FIFO available */
+		if (status & INT_STS_TDFA_) {
+			DBG(SMC_DEBUG_TX, "%s: TX data FIFO space available irq\n", dev->name);
+			SMC_SET_FIFO_TDA(lp, 0xFF);
+			lp->tx_throttle = 0;
+#ifdef SMC_USE_DMA
+			if (!lp->txdma_active)
+#endif
+				netif_wake_queue(dev);
+			SMC_ACK_INT(lp, INT_STS_TDFA_);
+		}
+		/* Handle transmit done condition */
+#if 1
+		if (status & (INT_STS_TSFL_ | INT_STS_GPT_INT_)) {
+			DBG(SMC_DEBUG_TX | SMC_DEBUG_MISC,
+				"%s: Tx stat FIFO limit (%d) /GPT irq\n",
+				dev->name, (SMC_GET_FIFO_INT(lp) & 0x00ff0000) >> 16);
+			smc911x_tx(dev);
+			SMC_SET_GPT_CFG(lp, GPT_CFG_TIMER_EN_ | 10000);
+			SMC_ACK_INT(lp, INT_STS_TSFL_);
+			SMC_ACK_INT(lp, INT_STS_TSFL_ | INT_STS_GPT_INT_);
+		}
+#else
+		if (status & INT_STS_TSFL_) {
+			DBG(SMC_DEBUG_TX, "%s: TX status FIFO limit (%d) irq\n", dev->name, );
+			smc911x_tx(dev);
+			SMC_ACK_INT(lp, INT_STS_TSFL_);
+		}
+
+		if (status & INT_STS_GPT_INT_) {
+			DBG(SMC_DEBUG_RX, "%s: IRQ_CFG 0x%08x FIFO_INT 0x%08x RX_CFG 0x%08x\n",
+				dev->name,
+				SMC_GET_IRQ_CFG(lp),
+				SMC_GET_FIFO_INT(lp),
+				SMC_GET_RX_CFG(lp));
+			DBG(SMC_DEBUG_RX, "%s: Rx Stat FIFO Used 0x%02x "
+				"Data FIFO Used 0x%04x Stat FIFO 0x%08x\n",
+				dev->name,
+				(SMC_GET_RX_FIFO_INF(lp) & 0x00ff0000) >> 16,
+				SMC_GET_RX_FIFO_INF(lp) & 0xffff,
+				SMC_GET_RX_STS_FIFO_PEEK(lp));
+			SMC_SET_GPT_CFG(lp, GPT_CFG_TIMER_EN_ | 10000);
+			SMC_ACK_INT(lp, INT_STS_GPT_INT_);
+		}
+#endif
+
+		/* Handle PHY interrupt condition */
+		if (status & INT_STS_PHY_INT_) {
+			DBG(SMC_DEBUG_MISC, "%s: PHY irq\n", dev->name);
+			smc911x_phy_interrupt(dev);
+			SMC_ACK_INT(lp, INT_STS_PHY_INT_);
+		}
+	} while (--timeout);
+
+	/* restore mask state */
+	SMC_SET_INT_EN(lp, mask);
+
+	DBG(SMC_DEBUG_MISC, "%s: Interrupt done (%d loops)\n",
+		dev->name, 8-timeout);
+
+	spin_unlock_irqrestore(&lp->lock, flags);
+
+	return IRQ_HANDLED;
+}
+
+#ifdef SMC_USE_DMA
+static void
+smc911x_tx_dma_irq(int dma, void *data)
+{
+	struct net_device *dev = (struct net_device *)data;
+	struct smc911x_local *lp = netdev_priv(dev);
+	struct sk_buff *skb = lp->current_tx_skb;
+	unsigned long flags;
+
+	DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __func__);
+
+	DBG(SMC_DEBUG_TX | SMC_DEBUG_DMA, "%s: TX DMA irq handler\n", dev->name);
+	/* Clear the DMA interrupt sources */
+	SMC_DMA_ACK_IRQ(dev, dma);
+	BUG_ON(skb == NULL);
+	dma_unmap_single(NULL, tx_dmabuf, tx_dmalen, DMA_TO_DEVICE);
+	dev->trans_start = jiffies;
+	dev_kfree_skb_irq(skb);
+	lp->current_tx_skb = NULL;
+	if (lp->pending_tx_skb != NULL)
+		smc911x_hardware_send_pkt(dev);
+	else {
+		DBG(SMC_DEBUG_TX | SMC_DEBUG_DMA,
+			"%s: No pending Tx packets. DMA disabled\n", dev->name);
+		spin_lock_irqsave(&lp->lock, flags);
+		lp->txdma_active = 0;
+		if (!lp->tx_throttle) {
+			netif_wake_queue(dev);
+		}
+		spin_unlock_irqrestore(&lp->lock, flags);
+	}
+
+	DBG(SMC_DEBUG_TX | SMC_DEBUG_DMA,
+		"%s: TX DMA irq completed\n", dev->name);
+}
+static void
+smc911x_rx_dma_irq(int dma, void *data)
+{
+	struct net_device *dev = (struct net_device *)data;
+	unsigned long ioaddr = dev->base_addr;
+	struct smc911x_local *lp = netdev_priv(dev);
+	struct sk_buff *skb = lp->current_rx_skb;
+	unsigned long flags;
+	unsigned int pkts;
+
+	DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __func__);
+	DBG(SMC_DEBUG_RX | SMC_DEBUG_DMA, "%s: RX DMA irq handler\n", dev->name);
+	/* Clear the DMA interrupt sources */
+	SMC_DMA_ACK_IRQ(dev, dma);
+	dma_unmap_single(NULL, rx_dmabuf, rx_dmalen, DMA_FROM_DEVICE);
+	BUG_ON(skb == NULL);
+	lp->current_rx_skb = NULL;
+	PRINT_PKT(skb->data, skb->len);
+	skb->protocol = eth_type_trans(skb, dev);
+	dev->stats.rx_packets++;
+	dev->stats.rx_bytes += skb->len;
+	netif_rx(skb);
+
+	spin_lock_irqsave(&lp->lock, flags);
+	pkts = (SMC_GET_RX_FIFO_INF(lp) & RX_FIFO_INF_RXSUSED_) >> 16;
+	if (pkts != 0) {
+		smc911x_rcv(dev);
+	}else {
+		lp->rxdma_active = 0;
+	}
+	spin_unlock_irqrestore(&lp->lock, flags);
+	DBG(SMC_DEBUG_RX | SMC_DEBUG_DMA,
+		"%s: RX DMA irq completed. DMA RX FIFO PKTS %d\n",
+		dev->name, pkts);
+}
+#endif	 /* SMC_USE_DMA */
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+/*
+ * Polling receive - used by netconsole and other diagnostic tools
+ * to allow network i/o with interrupts disabled.
+ */
+static void smc911x_poll_controller(struct net_device *dev)
+{
+	disable_irq(dev->irq);
+	smc911x_interrupt(dev->irq, dev);
+	enable_irq(dev->irq);
+}
+#endif
+
+/* Our watchdog timed out. Called by the networking layer */
+static void smc911x_timeout(struct net_device *dev)
+{
+	struct smc911x_local *lp = netdev_priv(dev);
+	int status, mask;
+	unsigned long flags;
+
+	DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __func__);
+
+	spin_lock_irqsave(&lp->lock, flags);
+	status = SMC_GET_INT(lp);
+	mask = SMC_GET_INT_EN(lp);
+	spin_unlock_irqrestore(&lp->lock, flags);
+	DBG(SMC_DEBUG_MISC, "%s: INT 0x%02x MASK 0x%02x\n",
+		dev->name, status, mask);
+
+	/* Dump the current TX FIFO contents and restart */
+	mask = SMC_GET_TX_CFG(lp);
+	SMC_SET_TX_CFG(lp, mask | TX_CFG_TXS_DUMP_ | TX_CFG_TXD_DUMP_);
+	/*
+	 * Reconfiguring the PHY doesn't seem like a bad idea here, but
+	 * smc911x_phy_configure() calls msleep() which calls schedule_timeout()
+	 * which calls schedule().	 Hence we use a work queue.
+	 */
+	if (lp->phy_type != 0)
+		schedule_work(&lp->phy_configure);
+
+	/* We can accept TX packets again */
+	dev->trans_start = jiffies; /* prevent tx timeout */
+	netif_wake_queue(dev);
+}
+
+/*
+ * This routine will, depending on the values passed to it,
+ * either make it accept multicast packets, go into
+ * promiscuous mode (for TCPDUMP and cousins) or accept
+ * a select set of multicast packets
+ */
+static void smc911x_set_multicast_list(struct net_device *dev)
+{
+	struct smc911x_local *lp = netdev_priv(dev);
+	unsigned int multicast_table[2];
+	unsigned int mcr, update_multicast = 0;
+	unsigned long flags;
+
+	DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __func__);
+
+	spin_lock_irqsave(&lp->lock, flags);
+	SMC_GET_MAC_CR(lp, mcr);
+	spin_unlock_irqrestore(&lp->lock, flags);
+
+	if (dev->flags & IFF_PROMISC) {
+
+		DBG(SMC_DEBUG_MISC, "%s: RCR_PRMS\n", dev->name);
+		mcr |= MAC_CR_PRMS_;
+	}
+	/*
+	 * Here, I am setting this to accept all multicast packets.
+	 * I don't need to zero the multicast table, because the flag is
+	 * checked before the table is
+	 */
+	else if (dev->flags & IFF_ALLMULTI || netdev_mc_count(dev) > 16) {
+		DBG(SMC_DEBUG_MISC, "%s: RCR_ALMUL\n", dev->name);
+		mcr |= MAC_CR_MCPAS_;
+	}
+
+	/*
+	 * This sets the internal hardware table to filter out unwanted
+	 * multicast packets before they take up memory.
+	 *
+	 * The SMC chip uses a hash table where the high 6 bits of the CRC of
+	 * address are the offset into the table.	If that bit is 1, then the
+	 * multicast packet is accepted.  Otherwise, it's dropped silently.
+	 *
+	 * To use the 6 bits as an offset into the table, the high 1 bit is
+	 * the number of the 32 bit register, while the low 5 bits are the bit
+	 * within that register.
+	 */
+	else if (!netdev_mc_empty(dev)) {
+		struct netdev_hw_addr *ha;
+
+		/* Set the Hash perfec mode */
+		mcr |= MAC_CR_HPFILT_;
+
+		/* start with a table of all zeros: reject all */
+		memset(multicast_table, 0, sizeof(multicast_table));
+
+		netdev_for_each_mc_addr(ha, dev) {
+			u32 position;
+
+			/* upper 6 bits are used as hash index */
+			position = ether_crc(ETH_ALEN, ha->addr)>>26;
+
+			multicast_table[position>>5] |= 1 << (position&0x1f);
+		}
+
+		/* be sure I get rid of flags I might have set */
+		mcr &= ~(MAC_CR_PRMS_ | MAC_CR_MCPAS_);
+
+		/* now, the table can be loaded into the chipset */
+		update_multicast = 1;
+	} else	 {
+		DBG(SMC_DEBUG_MISC, "%s: ~(MAC_CR_PRMS_|MAC_CR_MCPAS_)\n",
+			dev->name);
+		mcr &= ~(MAC_CR_PRMS_ | MAC_CR_MCPAS_);
+
+		/*
+		 * since I'm disabling all multicast entirely, I need to
+		 * clear the multicast list
+		 */
+		memset(multicast_table, 0, sizeof(multicast_table));
+		update_multicast = 1;
+	}
+
+	spin_lock_irqsave(&lp->lock, flags);
+	SMC_SET_MAC_CR(lp, mcr);
+	if (update_multicast) {
+		DBG(SMC_DEBUG_MISC,
+			"%s: update mcast hash table 0x%08x 0x%08x\n",
+			dev->name, multicast_table[0], multicast_table[1]);
+		SMC_SET_HASHL(lp, multicast_table[0]);
+		SMC_SET_HASHH(lp, multicast_table[1]);
+	}
+	spin_unlock_irqrestore(&lp->lock, flags);
+}
+
+
+/*
+ * Open and Initialize the board
+ *
+ * Set up everything, reset the card, etc..
+ */
+static int
+smc911x_open(struct net_device *dev)
+{
+	struct smc911x_local *lp = netdev_priv(dev);
+
+	DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __func__);
+
+	/*
+	 * Check that the address is valid.  If its not, refuse
+	 * to bring the device up.	 The user must specify an
+	 * address using ifconfig eth0 hw ether xx:xx:xx:xx:xx:xx
+	 */
+	if (!is_valid_ether_addr(dev->dev_addr)) {
+		PRINTK("%s: no valid ethernet hw addr\n", __func__);
+		return -EINVAL;
+	}
+
+	/* reset the hardware */
+	smc911x_reset(dev);
+
+	/* Configure the PHY, initialize the link state */
+	smc911x_phy_configure(&lp->phy_configure);
+
+	/* Turn on Tx + Rx */
+	smc911x_enable(dev);
+
+	netif_start_queue(dev);
+
+	return 0;
+}
+
+/*
+ * smc911x_close
+ *
+ * this makes the board clean up everything that it can
+ * and not talk to the outside world.	 Caused by
+ * an 'ifconfig ethX down'
+ */
+static int smc911x_close(struct net_device *dev)
+{
+	struct smc911x_local *lp = netdev_priv(dev);
+
+	DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __func__);
+
+	netif_stop_queue(dev);
+	netif_carrier_off(dev);
+
+	/* clear everything */
+	smc911x_shutdown(dev);
+
+	if (lp->phy_type != 0) {
+		/* We need to ensure that no calls to
+		 * smc911x_phy_configure are pending.
+		 */
+		cancel_work_sync(&lp->phy_configure);
+		smc911x_phy_powerdown(dev, lp->mii.phy_id);
+	}
+
+	if (lp->pending_tx_skb) {
+		dev_kfree_skb(lp->pending_tx_skb);
+		lp->pending_tx_skb = NULL;
+	}
+
+	return 0;
+}
+
+/*
+ * Ethtool support
+ */
+static int
+smc911x_ethtool_getsettings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+	struct smc911x_local *lp = netdev_priv(dev);
+	int ret, status;
+	unsigned long flags;
+
+	DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __func__);
+	cmd->maxtxpkt = 1;
+	cmd->maxrxpkt = 1;
+
+	if (lp->phy_type != 0) {
+		spin_lock_irqsave(&lp->lock, flags);
+		ret = mii_ethtool_gset(&lp->mii, cmd);
+		spin_unlock_irqrestore(&lp->lock, flags);
+	} else {
+		cmd->supported = SUPPORTED_10baseT_Half |
+				SUPPORTED_10baseT_Full |
+				SUPPORTED_TP | SUPPORTED_AUI;
+
+		if (lp->ctl_rspeed == 10)
+			ethtool_cmd_speed_set(cmd, SPEED_10);
+		else if (lp->ctl_rspeed == 100)
+			ethtool_cmd_speed_set(cmd, SPEED_100);
+
+		cmd->autoneg = AUTONEG_DISABLE;
+		if (lp->mii.phy_id==1)
+			cmd->transceiver = XCVR_INTERNAL;
+		else
+			cmd->transceiver = XCVR_EXTERNAL;
+		cmd->port = 0;
+		SMC_GET_PHY_SPECIAL(lp, lp->mii.phy_id, status);
+		cmd->duplex =
+			(status & (PHY_SPECIAL_SPD_10FULL_ | PHY_SPECIAL_SPD_100FULL_)) ?
+				DUPLEX_FULL : DUPLEX_HALF;
+		ret = 0;
+	}
+
+	return ret;
+}
+
+static int
+smc911x_ethtool_setsettings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+	struct smc911x_local *lp = netdev_priv(dev);
+	int ret;
+	unsigned long flags;
+
+	if (lp->phy_type != 0) {
+		spin_lock_irqsave(&lp->lock, flags);
+		ret = mii_ethtool_sset(&lp->mii, cmd);
+		spin_unlock_irqrestore(&lp->lock, flags);
+	} else {
+		if (cmd->autoneg != AUTONEG_DISABLE ||
+			cmd->speed != SPEED_10 ||
+			(cmd->duplex != DUPLEX_HALF && cmd->duplex != DUPLEX_FULL) ||
+			(cmd->port != PORT_TP && cmd->port != PORT_AUI))
+			return -EINVAL;
+
+		lp->ctl_rfduplx = cmd->duplex == DUPLEX_FULL;
+
+		ret = 0;
+	}
+
+	return ret;
+}
+
+static void
+smc911x_ethtool_getdrvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
+{
+	strncpy(info->driver, CARDNAME, sizeof(info->driver));
+	strncpy(info->version, version, sizeof(info->version));
+	strncpy(info->bus_info, dev_name(dev->dev.parent), sizeof(info->bus_info));
+}
+
+static int smc911x_ethtool_nwayreset(struct net_device *dev)
+{
+	struct smc911x_local *lp = netdev_priv(dev);
+	int ret = -EINVAL;
+	unsigned long flags;
+
+	if (lp->phy_type != 0) {
+		spin_lock_irqsave(&lp->lock, flags);
+		ret = mii_nway_restart(&lp->mii);
+		spin_unlock_irqrestore(&lp->lock, flags);
+	}
+
+	return ret;
+}
+
+static u32 smc911x_ethtool_getmsglevel(struct net_device *dev)
+{
+	struct smc911x_local *lp = netdev_priv(dev);
+	return lp->msg_enable;
+}
+
+static void smc911x_ethtool_setmsglevel(struct net_device *dev, u32 level)
+{
+	struct smc911x_local *lp = netdev_priv(dev);
+	lp->msg_enable = level;
+}
+
+static int smc911x_ethtool_getregslen(struct net_device *dev)
+{
+	/* System regs + MAC regs + PHY regs */
+	return (((E2P_CMD - ID_REV)/4 + 1) +
+			(WUCSR - MAC_CR)+1 + 32) * sizeof(u32);
+}
+
+static void smc911x_ethtool_getregs(struct net_device *dev,
+										 struct ethtool_regs* regs, void *buf)
+{
+	struct smc911x_local *lp = netdev_priv(dev);
+	unsigned long flags;
+	u32 reg,i,j=0;
+	u32 *data = (u32*)buf;
+
+	regs->version = lp->version;
+	for(i=ID_REV;i<=E2P_CMD;i+=4) {
+		data[j++] = SMC_inl(lp, i);
+	}
+	for(i=MAC_CR;i<=WUCSR;i++) {
+		spin_lock_irqsave(&lp->lock, flags);
+		SMC_GET_MAC_CSR(lp, i, reg);
+		spin_unlock_irqrestore(&lp->lock, flags);
+		data[j++] = reg;
+	}
+	for(i=0;i<=31;i++) {
+		spin_lock_irqsave(&lp->lock, flags);
+		SMC_GET_MII(lp, i, lp->mii.phy_id, reg);
+		spin_unlock_irqrestore(&lp->lock, flags);
+		data[j++] = reg & 0xFFFF;
+	}
+}
+
+static int smc911x_ethtool_wait_eeprom_ready(struct net_device *dev)
+{
+	struct smc911x_local *lp = netdev_priv(dev);
+	unsigned int timeout;
+	int e2p_cmd;
+
+	e2p_cmd = SMC_GET_E2P_CMD(lp);
+	for(timeout=10;(e2p_cmd & E2P_CMD_EPC_BUSY_) && timeout; timeout--) {
+		if (e2p_cmd & E2P_CMD_EPC_TIMEOUT_) {
+			PRINTK("%s: %s timeout waiting for EEPROM to respond\n",
+				dev->name, __func__);
+			return -EFAULT;
+		}
+		mdelay(1);
+		e2p_cmd = SMC_GET_E2P_CMD(lp);
+	}
+	if (timeout == 0) {
+		PRINTK("%s: %s timeout waiting for EEPROM CMD not busy\n",
+			dev->name, __func__);
+		return -ETIMEDOUT;
+	}
+	return 0;
+}
+
+static inline int smc911x_ethtool_write_eeprom_cmd(struct net_device *dev,
+													int cmd, int addr)
+{
+	struct smc911x_local *lp = netdev_priv(dev);
+	int ret;
+
+	if ((ret = smc911x_ethtool_wait_eeprom_ready(dev))!=0)
+		return ret;
+	SMC_SET_E2P_CMD(lp, E2P_CMD_EPC_BUSY_ |
+		((cmd) & (0x7<<28)) |
+		((addr) & 0xFF));
+	return 0;
+}
+
+static inline int smc911x_ethtool_read_eeprom_byte(struct net_device *dev,
+													u8 *data)
+{
+	struct smc911x_local *lp = netdev_priv(dev);
+	int ret;
+
+	if ((ret = smc911x_ethtool_wait_eeprom_ready(dev))!=0)
+		return ret;
+	*data = SMC_GET_E2P_DATA(lp);
+	return 0;
+}
+
+static inline int smc911x_ethtool_write_eeprom_byte(struct net_device *dev,
+													 u8 data)
+{
+	struct smc911x_local *lp = netdev_priv(dev);
+	int ret;
+
+	if ((ret = smc911x_ethtool_wait_eeprom_ready(dev))!=0)
+		return ret;
+	SMC_SET_E2P_DATA(lp, data);
+	return 0;
+}
+
+static int smc911x_ethtool_geteeprom(struct net_device *dev,
+									  struct ethtool_eeprom *eeprom, u8 *data)
+{
+	u8 eebuf[SMC911X_EEPROM_LEN];
+	int i, ret;
+
+	for(i=0;i<SMC911X_EEPROM_LEN;i++) {
+		if ((ret=smc911x_ethtool_write_eeprom_cmd(dev, E2P_CMD_EPC_CMD_READ_, i ))!=0)
+			return ret;
+		if ((ret=smc911x_ethtool_read_eeprom_byte(dev, &eebuf[i]))!=0)
+			return ret;
+		}
+	memcpy(data, eebuf+eeprom->offset, eeprom->len);
+	return 0;
+}
+
+static int smc911x_ethtool_seteeprom(struct net_device *dev,
+									   struct ethtool_eeprom *eeprom, u8 *data)
+{
+	int i, ret;
+
+	/* Enable erase */
+	if ((ret=smc911x_ethtool_write_eeprom_cmd(dev, E2P_CMD_EPC_CMD_EWEN_, 0 ))!=0)
+		return ret;
+	for(i=eeprom->offset;i<(eeprom->offset+eeprom->len);i++) {
+		/* erase byte */
+		if ((ret=smc911x_ethtool_write_eeprom_cmd(dev, E2P_CMD_EPC_CMD_ERASE_, i ))!=0)
+			return ret;
+		/* write byte */
+		if ((ret=smc911x_ethtool_write_eeprom_byte(dev, *data))!=0)
+			 return ret;
+		if ((ret=smc911x_ethtool_write_eeprom_cmd(dev, E2P_CMD_EPC_CMD_WRITE_, i ))!=0)
+			return ret;
+		}
+	 return 0;
+}
+
+static int smc911x_ethtool_geteeprom_len(struct net_device *dev)
+{
+	 return SMC911X_EEPROM_LEN;
+}
+
+static const struct ethtool_ops smc911x_ethtool_ops = {
+	.get_settings	 = smc911x_ethtool_getsettings,
+	.set_settings	 = smc911x_ethtool_setsettings,
+	.get_drvinfo	 = smc911x_ethtool_getdrvinfo,
+	.get_msglevel	 = smc911x_ethtool_getmsglevel,
+	.set_msglevel	 = smc911x_ethtool_setmsglevel,
+	.nway_reset = smc911x_ethtool_nwayreset,
+	.get_link	 = ethtool_op_get_link,
+	.get_regs_len	 = smc911x_ethtool_getregslen,
+	.get_regs	 = smc911x_ethtool_getregs,
+	.get_eeprom_len = smc911x_ethtool_geteeprom_len,
+	.get_eeprom = smc911x_ethtool_geteeprom,
+	.set_eeprom = smc911x_ethtool_seteeprom,
+};
+
+/*
+ * smc911x_findirq
+ *
+ * This routine has a simple purpose -- make the SMC chip generate an
+ * interrupt, so an auto-detect routine can detect it, and find the IRQ,
+ */
+static int __devinit smc911x_findirq(struct net_device *dev)
+{
+	struct smc911x_local *lp = netdev_priv(dev);
+	int timeout = 20;
+	unsigned long cookie;
+
+	DBG(SMC_DEBUG_FUNC, "--> %s\n", __func__);
+
+	cookie = probe_irq_on();
+
+	/*
+	 * Force a SW interrupt
+	 */
+
+	SMC_SET_INT_EN(lp, INT_EN_SW_INT_EN_);
+
+	/*
+	 * Wait until positive that the interrupt has been generated
+	 */
+	do {
+		int int_status;
+		udelay(10);
+		int_status = SMC_GET_INT_EN(lp);
+		if (int_status & INT_EN_SW_INT_EN_)
+			 break;		/* got the interrupt */
+	} while (--timeout);
+
+	/*
+	 * there is really nothing that I can do here if timeout fails,
+	 * as autoirq_report will return a 0 anyway, which is what I
+	 * want in this case.	 Plus, the clean up is needed in both
+	 * cases.
+	 */
+
+	/* and disable all interrupts again */
+	SMC_SET_INT_EN(lp, 0);
+
+	/* and return what I found */
+	return probe_irq_off(cookie);
+}
+
+static const struct net_device_ops smc911x_netdev_ops = {
+	.ndo_open		= smc911x_open,
+	.ndo_stop		= smc911x_close,
+	.ndo_start_xmit		= smc911x_hard_start_xmit,
+	.ndo_tx_timeout		= smc911x_timeout,
+	.ndo_set_multicast_list	= smc911x_set_multicast_list,
+	.ndo_change_mtu		= eth_change_mtu,
+	.ndo_validate_addr	= eth_validate_addr,
+	.ndo_set_mac_address	= eth_mac_addr,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	.ndo_poll_controller	= smc911x_poll_controller,
+#endif
+};
+
+/*
+ * Function: smc911x_probe(unsigned long ioaddr)
+ *
+ * Purpose:
+ *	 Tests to see if a given ioaddr points to an SMC911x chip.
+ *	 Returns a 0 on success
+ *
+ * Algorithm:
+ *	 (1) see if the endian word is OK
+ *	 (1) see if I recognize the chip ID in the appropriate register
+ *
+ * Here I do typical initialization tasks.
+ *
+ * o  Initialize the structure if needed
+ * o  print out my vanity message if not done so already
+ * o  print out what type of hardware is detected
+ * o  print out the ethernet address
+ * o  find the IRQ
+ * o  set up my private data
+ * o  configure the dev structure with my subroutines
+ * o  actually GRAB the irq.
+ * o  GRAB the region
+ */
+static int __devinit smc911x_probe(struct net_device *dev)
+{
+	struct smc911x_local *lp = netdev_priv(dev);
+	int i, retval;
+	unsigned int val, chip_id, revision;
+	const char *version_string;
+	unsigned long irq_flags;
+
+	DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __func__);
+
+	/* First, see if the endian word is recognized */
+	val = SMC_GET_BYTE_TEST(lp);
+	DBG(SMC_DEBUG_MISC, "%s: endian probe returned 0x%04x\n", CARDNAME, val);
+	if (val != 0x87654321) {
+		printk(KERN_ERR "Invalid chip endian 0x%08x\n",val);
+		retval = -ENODEV;
+		goto err_out;
+	}
+
+	/*
+	 * check if the revision register is something that I
+	 * recognize.	These might need to be added to later,
+	 * as future revisions could be added.
+	 */
+	chip_id = SMC_GET_PN(lp);
+	DBG(SMC_DEBUG_MISC, "%s: id probe returned 0x%04x\n", CARDNAME, chip_id);
+	for(i=0;chip_ids[i].id != 0; i++) {
+		if (chip_ids[i].id == chip_id) break;
+	}
+	if (!chip_ids[i].id) {
+		printk(KERN_ERR "Unknown chip ID %04x\n", chip_id);
+		retval = -ENODEV;
+		goto err_out;
+	}
+	version_string = chip_ids[i].name;
+
+	revision = SMC_GET_REV(lp);
+	DBG(SMC_DEBUG_MISC, "%s: revision = 0x%04x\n", CARDNAME, revision);
+
+	/* At this point I'll assume that the chip is an SMC911x. */
+	DBG(SMC_DEBUG_MISC, "%s: Found a %s\n", CARDNAME, chip_ids[i].name);
+
+	/* Validate the TX FIFO size requested */
+	if ((tx_fifo_kb < 2) || (tx_fifo_kb > 14)) {
+		printk(KERN_ERR "Invalid TX FIFO size requested %d\n", tx_fifo_kb);
+		retval = -EINVAL;
+		goto err_out;
+	}
+
+	/* fill in some of the fields */
+	lp->version = chip_ids[i].id;
+	lp->revision = revision;
+	lp->tx_fifo_kb = tx_fifo_kb;
+	/* Reverse calculate the RX FIFO size from the TX */
+	lp->tx_fifo_size=(lp->tx_fifo_kb<<10) - 512;
+	lp->rx_fifo_size= ((0x4000 - 512 - lp->tx_fifo_size) / 16) * 15;
+
+	/* Set the automatic flow control values */
+	switch(lp->tx_fifo_kb) {
+		/*
+		 *	 AFC_HI is about ((Rx Data Fifo Size)*2/3)/64
+		 *	 AFC_LO is AFC_HI/2
+		 *	 BACK_DUR is about 5uS*(AFC_LO) rounded down
+		 */
+		case 2:/* 13440 Rx Data Fifo Size */
+			lp->afc_cfg=0x008C46AF;break;
+		case 3:/* 12480 Rx Data Fifo Size */
+			lp->afc_cfg=0x0082419F;break;
+		case 4:/* 11520 Rx Data Fifo Size */
+			lp->afc_cfg=0x00783C9F;break;
+		case 5:/* 10560 Rx Data Fifo Size */
+			lp->afc_cfg=0x006E374F;break;
+		case 6:/* 9600 Rx Data Fifo Size */
+			lp->afc_cfg=0x0064328F;break;
+		case 7:/* 8640 Rx Data Fifo Size */
+			lp->afc_cfg=0x005A2D7F;break;
+		case 8:/* 7680 Rx Data Fifo Size */
+			lp->afc_cfg=0x0050287F;break;
+		case 9:/* 6720 Rx Data Fifo Size */
+			lp->afc_cfg=0x0046236F;break;
+		case 10:/* 5760 Rx Data Fifo Size */
+			lp->afc_cfg=0x003C1E6F;break;
+		case 11:/* 4800 Rx Data Fifo Size */
+			lp->afc_cfg=0x0032195F;break;
+		/*
+		 *	 AFC_HI is ~1520 bytes less than RX Data Fifo Size
+		 *	 AFC_LO is AFC_HI/2
+		 *	 BACK_DUR is about 5uS*(AFC_LO) rounded down
+		 */
+		case 12:/* 3840 Rx Data Fifo Size */
+			lp->afc_cfg=0x0024124F;break;
+		case 13:/* 2880 Rx Data Fifo Size */
+			lp->afc_cfg=0x0015073F;break;
+		case 14:/* 1920 Rx Data Fifo Size */
+			lp->afc_cfg=0x0006032F;break;
+		 default:
+			 PRINTK("%s: ERROR -- no AFC_CFG setting found",
+				dev->name);
+			 break;
+	}
+
+	DBG(SMC_DEBUG_MISC | SMC_DEBUG_TX | SMC_DEBUG_RX,
+		"%s: tx_fifo %d rx_fifo %d afc_cfg 0x%08x\n", CARDNAME,
+		lp->tx_fifo_size, lp->rx_fifo_size, lp->afc_cfg);
+
+	spin_lock_init(&lp->lock);
+
+	/* Get the MAC address */
+	SMC_GET_MAC_ADDR(lp, dev->dev_addr);
+
+	/* now, reset the chip, and put it into a known state */
+	smc911x_reset(dev);
+
+	/*
+	 * If dev->irq is 0, then the device has to be banged on to see
+	 * what the IRQ is.
+	 *
+	 * Specifying an IRQ is done with the assumption that the user knows
+	 * what (s)he is doing.  No checking is done!!!!
+	 */
+	if (dev->irq < 1) {
+		int trials;
+
+		trials = 3;
+		while (trials--) {
+			dev->irq = smc911x_findirq(dev);
+			if (dev->irq)
+				break;
+			/* kick the card and try again */
+			smc911x_reset(dev);
+		}
+	}
+	if (dev->irq == 0) {
+		printk("%s: Couldn't autodetect your IRQ. Use irq=xx.\n",
+			dev->name);
+		retval = -ENODEV;
+		goto err_out;
+	}
+	dev->irq = irq_canonicalize(dev->irq);
+
+	/* Fill in the fields of the device structure with ethernet values. */
+	ether_setup(dev);
+
+	dev->netdev_ops = &smc911x_netdev_ops;
+	dev->watchdog_timeo = msecs_to_jiffies(watchdog);
+	dev->ethtool_ops = &smc911x_ethtool_ops;
+
+	INIT_WORK(&lp->phy_configure, smc911x_phy_configure);
+	lp->mii.phy_id_mask = 0x1f;
+	lp->mii.reg_num_mask = 0x1f;
+	lp->mii.force_media = 0;
+	lp->mii.full_duplex = 0;
+	lp->mii.dev = dev;
+	lp->mii.mdio_read = smc911x_phy_read;
+	lp->mii.mdio_write = smc911x_phy_write;
+
+	/*
+	 * Locate the phy, if any.
+	 */
+	smc911x_phy_detect(dev);
+
+	/* Set default parameters */
+	lp->msg_enable = NETIF_MSG_LINK;
+	lp->ctl_rfduplx = 1;
+	lp->ctl_rspeed = 100;
+
+#ifdef SMC_DYNAMIC_BUS_CONFIG
+	irq_flags = lp->cfg.irq_flags;
+#else
+	irq_flags = IRQF_SHARED | SMC_IRQ_SENSE;
+#endif
+
+	/* Grab the IRQ */
+	retval = request_irq(dev->irq, smc911x_interrupt,
+			     irq_flags, dev->name, dev);
+	if (retval)
+		goto err_out;
+
+#ifdef SMC_USE_DMA
+	lp->rxdma = SMC_DMA_REQUEST(dev, smc911x_rx_dma_irq);
+	lp->txdma = SMC_DMA_REQUEST(dev, smc911x_tx_dma_irq);
+	lp->rxdma_active = 0;
+	lp->txdma_active = 0;
+	dev->dma = lp->rxdma;
+#endif
+
+	retval = register_netdev(dev);
+	if (retval == 0) {
+		/* now, print out the card info, in a short format.. */
+		printk("%s: %s (rev %d) at %#lx IRQ %d",
+			dev->name, version_string, lp->revision,
+			dev->base_addr, dev->irq);
+
+#ifdef SMC_USE_DMA
+		if (lp->rxdma != -1)
+			printk(" RXDMA %d ", lp->rxdma);
+
+		if (lp->txdma != -1)
+			printk("TXDMA %d", lp->txdma);
+#endif
+		printk("\n");
+		if (!is_valid_ether_addr(dev->dev_addr)) {
+			printk("%s: Invalid ethernet MAC address. Please "
+					"set using ifconfig\n", dev->name);
+		} else {
+			/* Print the Ethernet address */
+			printk("%s: Ethernet addr: %pM\n",
+				dev->name, dev->dev_addr);
+		}
+
+		if (lp->phy_type == 0) {
+			PRINTK("%s: No PHY found\n", dev->name);
+		} else if ((lp->phy_type & ~0xff) == LAN911X_INTERNAL_PHY_ID) {
+			PRINTK("%s: LAN911x Internal PHY\n", dev->name);
+		} else {
+			PRINTK("%s: External PHY 0x%08x\n", dev->name, lp->phy_type);
+		}
+	}
+
+err_out:
+#ifdef SMC_USE_DMA
+	if (retval) {
+		if (lp->rxdma != -1) {
+			SMC_DMA_FREE(dev, lp->rxdma);
+		}
+		if (lp->txdma != -1) {
+			SMC_DMA_FREE(dev, lp->txdma);
+		}
+	}
+#endif
+	return retval;
+}
+
+/*
+ * smc911x_init(void)
+ *
+ *	  Output:
+ *	 0 --> there is a device
+ *	 anything else, error
+ */
+static int __devinit smc911x_drv_probe(struct platform_device *pdev)
+{
+	struct net_device *ndev;
+	struct resource *res;
+	struct smc911x_local *lp;
+	unsigned int *addr;
+	int ret;
+
+	DBG(SMC_DEBUG_FUNC, "--> %s\n",  __func__);
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		ret = -ENODEV;
+		goto out;
+	}
+
+	/*
+	 * Request the regions.
+	 */
+	if (!request_mem_region(res->start, SMC911X_IO_EXTENT, CARDNAME)) {
+		 ret = -EBUSY;
+		 goto out;
+	}
+
+	ndev = alloc_etherdev(sizeof(struct smc911x_local));
+	if (!ndev) {
+		printk("%s: could not allocate device.\n", CARDNAME);
+		ret = -ENOMEM;
+		goto release_1;
+	}
+	SET_NETDEV_DEV(ndev, &pdev->dev);
+
+	ndev->dma = (unsigned char)-1;
+	ndev->irq = platform_get_irq(pdev, 0);
+	lp = netdev_priv(ndev);
+	lp->netdev = ndev;
+#ifdef SMC_DYNAMIC_BUS_CONFIG
+	{
+		struct smc911x_platdata *pd = pdev->dev.platform_data;
+		if (!pd) {
+			ret = -EINVAL;
+			goto release_both;
+		}
+		memcpy(&lp->cfg, pd, sizeof(lp->cfg));
+	}
+#endif
+
+	addr = ioremap(res->start, SMC911X_IO_EXTENT);
+	if (!addr) {
+		ret = -ENOMEM;
+		goto release_both;
+	}
+
+	platform_set_drvdata(pdev, ndev);
+	lp->base = addr;
+	ndev->base_addr = res->start;
+	ret = smc911x_probe(ndev);
+	if (ret != 0) {
+		platform_set_drvdata(pdev, NULL);
+		iounmap(addr);
+release_both:
+		free_netdev(ndev);
+release_1:
+		release_mem_region(res->start, SMC911X_IO_EXTENT);
+out:
+		printk("%s: not found (%d).\n", CARDNAME, ret);
+	}
+#ifdef SMC_USE_DMA
+	else {
+		lp->physaddr = res->start;
+		lp->dev = &pdev->dev;
+	}
+#endif
+
+	return ret;
+}
+
+static int __devexit smc911x_drv_remove(struct platform_device *pdev)
+{
+	struct net_device *ndev = platform_get_drvdata(pdev);
+	struct smc911x_local *lp = netdev_priv(ndev);
+	struct resource *res;
+
+	DBG(SMC_DEBUG_FUNC, "--> %s\n", __func__);
+	platform_set_drvdata(pdev, NULL);
+
+	unregister_netdev(ndev);
+
+	free_irq(ndev->irq, ndev);
+
+#ifdef SMC_USE_DMA
+	{
+		if (lp->rxdma != -1) {
+			SMC_DMA_FREE(dev, lp->rxdma);
+		}
+		if (lp->txdma != -1) {
+			SMC_DMA_FREE(dev, lp->txdma);
+		}
+	}
+#endif
+	iounmap(lp->base);
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	release_mem_region(res->start, SMC911X_IO_EXTENT);
+
+	free_netdev(ndev);
+	return 0;
+}
+
+static int smc911x_drv_suspend(struct platform_device *dev, pm_message_t state)
+{
+	struct net_device *ndev = platform_get_drvdata(dev);
+	struct smc911x_local *lp = netdev_priv(ndev);
+
+	DBG(SMC_DEBUG_FUNC, "--> %s\n", __func__);
+	if (ndev) {
+		if (netif_running(ndev)) {
+			netif_device_detach(ndev);
+			smc911x_shutdown(ndev);
+#if POWER_DOWN
+			/* Set D2 - Energy detect only setting */
+			SMC_SET_PMT_CTRL(lp, 2<<12);
+#endif
+		}
+	}
+	return 0;
+}
+
+static int smc911x_drv_resume(struct platform_device *dev)
+{
+	struct net_device *ndev = platform_get_drvdata(dev);
+
+	DBG(SMC_DEBUG_FUNC, "--> %s\n", __func__);
+	if (ndev) {
+		struct smc911x_local *lp = netdev_priv(ndev);
+
+		if (netif_running(ndev)) {
+			smc911x_reset(ndev);
+			if (lp->phy_type != 0)
+				smc911x_phy_configure(&lp->phy_configure);
+			smc911x_enable(ndev);
+			netif_device_attach(ndev);
+		}
+	}
+	return 0;
+}
+
+static struct platform_driver smc911x_driver = {
+	.probe		 = smc911x_drv_probe,
+	.remove	 = __devexit_p(smc911x_drv_remove),
+	.suspend	 = smc911x_drv_suspend,
+	.resume	 = smc911x_drv_resume,
+	.driver	 = {
+		.name	 = CARDNAME,
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init smc911x_init(void)
+{
+	return platform_driver_register(&smc911x_driver);
+}
+
+static void __exit smc911x_cleanup(void)
+{
+	platform_driver_unregister(&smc911x_driver);
+}
+
+module_init(smc911x_init);
+module_exit(smc911x_cleanup);
diff --git a/drivers/net/ethernet/smsc/smc911x.h b/drivers/net/ethernet/smsc/smc911x.h
new file mode 100644
index 0000000..3269292
--- /dev/null
+++ b/drivers/net/ethernet/smsc/smc911x.h
@@ -0,0 +1,924 @@
+/*------------------------------------------------------------------------
+ . smc911x.h - macros for SMSC's LAN911{5,6,7,8} single-chip Ethernet device.
+ .
+ . Copyright (C) 2005 Sensoria Corp.
+ . Derived from the unified SMC91x driver by Nicolas Pitre
+ .
+ . 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
+ .
+ . Information contained in this file was obtained from the LAN9118
+ . manual from SMC.  To get a copy, if you really want one, you can find
+ . information under www.smsc.com.
+ .
+ . Authors
+ .	 Dustin McIntire		 <dustin@sensoria.com>
+ .
+ ---------------------------------------------------------------------------*/
+#ifndef _SMC911X_H_
+#define _SMC911X_H_
+
+#include <linux/smc911x.h>
+/*
+ * Use the DMA feature on PXA chips
+ */
+#ifdef CONFIG_ARCH_PXA
+  #define SMC_USE_PXA_DMA	1
+  #define SMC_USE_16BIT		0
+  #define SMC_USE_32BIT		1
+  #define SMC_IRQ_SENSE		IRQF_TRIGGER_FALLING
+#elif defined(CONFIG_SH_MAGIC_PANEL_R2)
+  #define SMC_USE_16BIT		0
+  #define SMC_USE_32BIT		1
+  #define SMC_IRQ_SENSE		IRQF_TRIGGER_LOW
+#elif defined(CONFIG_ARCH_OMAP3)
+  #define SMC_USE_16BIT		0
+  #define SMC_USE_32BIT		1
+  #define SMC_IRQ_SENSE		IRQF_TRIGGER_LOW
+  #define SMC_MEM_RESERVED	1
+#elif defined(CONFIG_ARCH_OMAP2)
+  #define SMC_USE_16BIT		0
+  #define SMC_USE_32BIT		1
+  #define SMC_IRQ_SENSE		IRQF_TRIGGER_LOW
+  #define SMC_MEM_RESERVED	1
+#else
+/*
+ * Default configuration
+ */
+
+#define SMC_DYNAMIC_BUS_CONFIG
+#endif
+
+#ifdef SMC_USE_PXA_DMA
+#define SMC_USE_DMA
+#endif
+
+/* store this information for the driver.. */
+struct smc911x_local {
+	/*
+	 * If I have to wait until the DMA is finished and ready to reload a
+	 * packet, I will store the skbuff here. Then, the DMA will send it
+	 * out and free it.
+	 */
+	struct sk_buff *pending_tx_skb;
+
+	/* version/revision of the SMC911x chip */
+	u16 version;
+	u16 revision;
+
+	/* FIFO sizes */
+	int tx_fifo_kb;
+	int tx_fifo_size;
+	int rx_fifo_size;
+	int afc_cfg;
+
+	/* Contains the current active receive/phy mode */
+	int ctl_rfduplx;
+	int ctl_rspeed;
+
+	u32 msg_enable;
+	u32 phy_type;
+	struct mii_if_info mii;
+
+	/* work queue */
+	struct work_struct phy_configure;
+
+	int tx_throttle;
+	spinlock_t lock;
+
+	struct net_device *netdev;
+
+#ifdef SMC_USE_DMA
+	/* DMA needs the physical address of the chip */
+	u_long physaddr;
+	int rxdma;
+	int txdma;
+	int rxdma_active;
+	int txdma_active;
+	struct sk_buff *current_rx_skb;
+	struct sk_buff *current_tx_skb;
+	struct device *dev;
+#endif
+	void __iomem *base;
+#ifdef SMC_DYNAMIC_BUS_CONFIG
+	struct smc911x_platdata cfg;
+#endif
+};
+
+/*
+ * Define the bus width specific IO macros
+ */
+
+#ifdef SMC_DYNAMIC_BUS_CONFIG
+static inline unsigned int SMC_inl(struct smc911x_local *lp, int reg)
+{
+	void __iomem *ioaddr = lp->base + reg;
+
+	if (lp->cfg.flags & SMC911X_USE_32BIT)
+		return readl(ioaddr);
+
+	if (lp->cfg.flags & SMC911X_USE_16BIT)
+		return readw(ioaddr) | (readw(ioaddr + 2) << 16);
+
+	BUG();
+}
+
+static inline void SMC_outl(unsigned int value, struct smc911x_local *lp,
+			    int reg)
+{
+	void __iomem *ioaddr = lp->base + reg;
+
+	if (lp->cfg.flags & SMC911X_USE_32BIT) {
+		writel(value, ioaddr);
+		return;
+	}
+
+	if (lp->cfg.flags & SMC911X_USE_16BIT) {
+		writew(value & 0xffff, ioaddr);
+		writew(value >> 16, ioaddr + 2);
+		return;
+	}
+
+	BUG();
+}
+
+static inline void SMC_insl(struct smc911x_local *lp, int reg,
+			      void *addr, unsigned int count)
+{
+	void __iomem *ioaddr = lp->base + reg;
+
+	if (lp->cfg.flags & SMC911X_USE_32BIT) {
+		readsl(ioaddr, addr, count);
+		return;
+	}
+
+	if (lp->cfg.flags & SMC911X_USE_16BIT) {
+		readsw(ioaddr, addr, count * 2);
+		return;
+	}
+
+	BUG();
+}
+
+static inline void SMC_outsl(struct smc911x_local *lp, int reg,
+			     void *addr, unsigned int count)
+{
+	void __iomem *ioaddr = lp->base + reg;
+
+	if (lp->cfg.flags & SMC911X_USE_32BIT) {
+		writesl(ioaddr, addr, count);
+		return;
+	}
+
+	if (lp->cfg.flags & SMC911X_USE_16BIT) {
+		writesw(ioaddr, addr, count * 2);
+		return;
+	}
+
+	BUG();
+}
+#else
+#if	SMC_USE_16BIT
+#define SMC_inl(lp, r)		 ((readw((lp)->base + (r)) & 0xFFFF) + (readw((lp)->base + (r) + 2) << 16))
+#define SMC_outl(v, lp, r) 			 \
+	do{					 \
+		 writew(v & 0xFFFF, (lp)->base + (r));	 \
+		 writew(v >> 16, (lp)->base + (r) + 2); \
+	 } while (0)
+#define SMC_insl(lp, r, p, l)	 readsw((short*)((lp)->base + (r)), p, l*2)
+#define SMC_outsl(lp, r, p, l)	 writesw((short*)((lp)->base + (r)), p, l*2)
+
+#elif	SMC_USE_32BIT
+#define SMC_inl(lp, r)		 readl((lp)->base + (r))
+#define SMC_outl(v, lp, r)	 writel(v, (lp)->base + (r))
+#define SMC_insl(lp, r, p, l)	 readsl((int*)((lp)->base + (r)), p, l)
+#define SMC_outsl(lp, r, p, l)	 writesl((int*)((lp)->base + (r)), p, l)
+
+#endif /* SMC_USE_16BIT */
+#endif /* SMC_DYNAMIC_BUS_CONFIG */
+
+
+#ifdef SMC_USE_PXA_DMA
+
+#include <mach/dma.h>
+
+/*
+ * Define the request and free functions
+ * These are unfortunately architecture specific as no generic allocation
+ * mechanism exits
+ */
+#define SMC_DMA_REQUEST(dev, handler) \
+	 pxa_request_dma(dev->name, DMA_PRIO_LOW, handler, dev)
+
+#define SMC_DMA_FREE(dev, dma) \
+	 pxa_free_dma(dma)
+
+#define SMC_DMA_ACK_IRQ(dev, dma)					\
+{									\
+	if (DCSR(dma) & DCSR_BUSERR) {					\
+		printk("%s: DMA %d bus error!\n", dev->name, dma);	\
+	}								\
+	DCSR(dma) = DCSR_STARTINTR|DCSR_ENDINTR|DCSR_BUSERR;		\
+}
+
+/*
+ * Use a DMA for RX and TX packets.
+ */
+#include <linux/dma-mapping.h>
+
+static dma_addr_t rx_dmabuf, tx_dmabuf;
+static int rx_dmalen, tx_dmalen;
+
+#ifdef SMC_insl
+#undef SMC_insl
+#define SMC_insl(lp, r, p, l) \
+	smc_pxa_dma_insl(lp, lp->physaddr, r, lp->rxdma, p, l)
+
+static inline void
+smc_pxa_dma_insl(struct smc911x_local *lp, u_long physaddr,
+		int reg, int dma, u_char *buf, int len)
+{
+	/* 64 bit alignment is required for memory to memory DMA */
+	if ((long)buf & 4) {
+		*((u32 *)buf) = SMC_inl(lp, reg);
+		buf += 4;
+		len--;
+	}
+
+	len *= 4;
+	rx_dmabuf = dma_map_single(lp->dev, buf, len, DMA_FROM_DEVICE);
+	rx_dmalen = len;
+	DCSR(dma) = DCSR_NODESC;
+	DTADR(dma) = rx_dmabuf;
+	DSADR(dma) = physaddr + reg;
+	DCMD(dma) = (DCMD_INCTRGADDR | DCMD_BURST32 |
+		DCMD_WIDTH4 | DCMD_ENDIRQEN | (DCMD_LENGTH & rx_dmalen));
+	DCSR(dma) = DCSR_NODESC | DCSR_RUN;
+}
+#endif
+
+#ifdef SMC_outsl
+#undef SMC_outsl
+#define SMC_outsl(lp, r, p, l) \
+	 smc_pxa_dma_outsl(lp, lp->physaddr, r, lp->txdma, p, l)
+
+static inline void
+smc_pxa_dma_outsl(struct smc911x_local *lp, u_long physaddr,
+		int reg, int dma, u_char *buf, int len)
+{
+	/* 64 bit alignment is required for memory to memory DMA */
+	if ((long)buf & 4) {
+		SMC_outl(*((u32 *)buf), lp, reg);
+		buf += 4;
+		len--;
+	}
+
+	len *= 4;
+	tx_dmabuf = dma_map_single(lp->dev, buf, len, DMA_TO_DEVICE);
+	tx_dmalen = len;
+	DCSR(dma) = DCSR_NODESC;
+	DSADR(dma) = tx_dmabuf;
+	DTADR(dma) = physaddr + reg;
+	DCMD(dma) = (DCMD_INCSRCADDR | DCMD_BURST32 |
+		DCMD_WIDTH4 | DCMD_ENDIRQEN | (DCMD_LENGTH & tx_dmalen));
+	DCSR(dma) = DCSR_NODESC | DCSR_RUN;
+}
+#endif
+#endif	 /* SMC_USE_PXA_DMA */
+
+
+/* Chip Parameters and Register Definitions */
+
+#define SMC911X_TX_FIFO_LOW_THRESHOLD	(1536*2)
+
+#define SMC911X_IO_EXTENT	 0x100
+
+#define SMC911X_EEPROM_LEN	 7
+
+/* Below are the register offsets and bit definitions
+ * of the Lan911x memory space
+ */
+#define RX_DATA_FIFO		 (0x00)
+
+#define TX_DATA_FIFO		 (0x20)
+#define	TX_CMD_A_INT_ON_COMP_		(0x80000000)
+#define	TX_CMD_A_INT_BUF_END_ALGN_	(0x03000000)
+#define	TX_CMD_A_INT_4_BYTE_ALGN_	(0x00000000)
+#define	TX_CMD_A_INT_16_BYTE_ALGN_	(0x01000000)
+#define	TX_CMD_A_INT_32_BYTE_ALGN_	(0x02000000)
+#define	TX_CMD_A_INT_DATA_OFFSET_	(0x001F0000)
+#define	TX_CMD_A_INT_FIRST_SEG_		(0x00002000)
+#define	TX_CMD_A_INT_LAST_SEG_		(0x00001000)
+#define	TX_CMD_A_BUF_SIZE_		(0x000007FF)
+#define	TX_CMD_B_PKT_TAG_		(0xFFFF0000)
+#define	TX_CMD_B_ADD_CRC_DISABLE_	(0x00002000)
+#define	TX_CMD_B_DISABLE_PADDING_	(0x00001000)
+#define	TX_CMD_B_PKT_BYTE_LENGTH_	(0x000007FF)
+
+#define RX_STATUS_FIFO		(0x40)
+#define	RX_STS_PKT_LEN_			(0x3FFF0000)
+#define	RX_STS_ES_			(0x00008000)
+#define	RX_STS_BCST_			(0x00002000)
+#define	RX_STS_LEN_ERR_			(0x00001000)
+#define	RX_STS_RUNT_ERR_		(0x00000800)
+#define	RX_STS_MCAST_			(0x00000400)
+#define	RX_STS_TOO_LONG_		(0x00000080)
+#define	RX_STS_COLL_			(0x00000040)
+#define	RX_STS_ETH_TYPE_		(0x00000020)
+#define	RX_STS_WDOG_TMT_		(0x00000010)
+#define	RX_STS_MII_ERR_			(0x00000008)
+#define	RX_STS_DRIBBLING_		(0x00000004)
+#define	RX_STS_CRC_ERR_			(0x00000002)
+#define RX_STATUS_FIFO_PEEK 	(0x44)
+#define TX_STATUS_FIFO		(0x48)
+#define	TX_STS_TAG_			(0xFFFF0000)
+#define	TX_STS_ES_			(0x00008000)
+#define	TX_STS_LOC_			(0x00000800)
+#define	TX_STS_NO_CARR_			(0x00000400)
+#define	TX_STS_LATE_COLL_		(0x00000200)
+#define	TX_STS_MANY_COLL_		(0x00000100)
+#define	TX_STS_COLL_CNT_		(0x00000078)
+#define	TX_STS_MANY_DEFER_		(0x00000004)
+#define	TX_STS_UNDERRUN_		(0x00000002)
+#define	TX_STS_DEFERRED_		(0x00000001)
+#define TX_STATUS_FIFO_PEEK	(0x4C)
+#define ID_REV			(0x50)
+#define	ID_REV_CHIP_ID_			(0xFFFF0000)  /* RO */
+#define	ID_REV_REV_ID_			(0x0000FFFF)  /* RO */
+
+#define INT_CFG			(0x54)
+#define	INT_CFG_INT_DEAS_		(0xFF000000)  /* R/W */
+#define	INT_CFG_INT_DEAS_CLR_		(0x00004000)
+#define	INT_CFG_INT_DEAS_STS_		(0x00002000)
+#define	INT_CFG_IRQ_INT_		(0x00001000)  /* RO */
+#define	INT_CFG_IRQ_EN_			(0x00000100)  /* R/W */
+#define	INT_CFG_IRQ_POL_		(0x00000010)  /* R/W Not Affected by SW Reset */
+#define	INT_CFG_IRQ_TYPE_		(0x00000001)  /* R/W Not Affected by SW Reset */
+
+#define INT_STS			(0x58)
+#define	INT_STS_SW_INT_			(0x80000000)  /* R/WC */
+#define	INT_STS_TXSTOP_INT_		(0x02000000)  /* R/WC */
+#define	INT_STS_RXSTOP_INT_		(0x01000000)  /* R/WC */
+#define	INT_STS_RXDFH_INT_		(0x00800000)  /* R/WC */
+#define	INT_STS_RXDF_INT_		(0x00400000)  /* R/WC */
+#define	INT_STS_TX_IOC_			(0x00200000)  /* R/WC */
+#define	INT_STS_RXD_INT_		(0x00100000)  /* R/WC */
+#define	INT_STS_GPT_INT_		(0x00080000)  /* R/WC */
+#define	INT_STS_PHY_INT_		(0x00040000)  /* RO */
+#define	INT_STS_PME_INT_		(0x00020000)  /* R/WC */
+#define	INT_STS_TXSO_			(0x00010000)  /* R/WC */
+#define	INT_STS_RWT_			(0x00008000)  /* R/WC */
+#define	INT_STS_RXE_			(0x00004000)  /* R/WC */
+#define	INT_STS_TXE_			(0x00002000)  /* R/WC */
+//#define	INT_STS_ERX_		(0x00001000)  /* R/WC */
+#define	INT_STS_TDFU_			(0x00000800)  /* R/WC */
+#define	INT_STS_TDFO_			(0x00000400)  /* R/WC */
+#define	INT_STS_TDFA_			(0x00000200)  /* R/WC */
+#define	INT_STS_TSFF_			(0x00000100)  /* R/WC */
+#define	INT_STS_TSFL_			(0x00000080)  /* R/WC */
+//#define	INT_STS_RXDF_		(0x00000040)  /* R/WC */
+#define	INT_STS_RDFO_			(0x00000040)  /* R/WC */
+#define	INT_STS_RDFL_			(0x00000020)  /* R/WC */
+#define	INT_STS_RSFF_			(0x00000010)  /* R/WC */
+#define	INT_STS_RSFL_			(0x00000008)  /* R/WC */
+#define	INT_STS_GPIO2_INT_		(0x00000004)  /* R/WC */
+#define	INT_STS_GPIO1_INT_		(0x00000002)  /* R/WC */
+#define	INT_STS_GPIO0_INT_		(0x00000001)  /* R/WC */
+
+#define INT_EN			(0x5C)
+#define	INT_EN_SW_INT_EN_		(0x80000000)  /* R/W */
+#define	INT_EN_TXSTOP_INT_EN_		(0x02000000)  /* R/W */
+#define	INT_EN_RXSTOP_INT_EN_		(0x01000000)  /* R/W */
+#define	INT_EN_RXDFH_INT_EN_		(0x00800000)  /* R/W */
+//#define	INT_EN_RXDF_INT_EN_		(0x00400000)  /* R/W */
+#define	INT_EN_TIOC_INT_EN_		(0x00200000)  /* R/W */
+#define	INT_EN_RXD_INT_EN_		(0x00100000)  /* R/W */
+#define	INT_EN_GPT_INT_EN_		(0x00080000)  /* R/W */
+#define	INT_EN_PHY_INT_EN_		(0x00040000)  /* R/W */
+#define	INT_EN_PME_INT_EN_		(0x00020000)  /* R/W */
+#define	INT_EN_TXSO_EN_			(0x00010000)  /* R/W */
+#define	INT_EN_RWT_EN_			(0x00008000)  /* R/W */
+#define	INT_EN_RXE_EN_			(0x00004000)  /* R/W */
+#define	INT_EN_TXE_EN_			(0x00002000)  /* R/W */
+//#define	INT_EN_ERX_EN_			(0x00001000)  /* R/W */
+#define	INT_EN_TDFU_EN_			(0x00000800)  /* R/W */
+#define	INT_EN_TDFO_EN_			(0x00000400)  /* R/W */
+#define	INT_EN_TDFA_EN_			(0x00000200)  /* R/W */
+#define	INT_EN_TSFF_EN_			(0x00000100)  /* R/W */
+#define	INT_EN_TSFL_EN_			(0x00000080)  /* R/W */
+//#define	INT_EN_RXDF_EN_			(0x00000040)  /* R/W */
+#define	INT_EN_RDFO_EN_			(0x00000040)  /* R/W */
+#define	INT_EN_RDFL_EN_			(0x00000020)  /* R/W */
+#define	INT_EN_RSFF_EN_			(0x00000010)  /* R/W */
+#define	INT_EN_RSFL_EN_			(0x00000008)  /* R/W */
+#define	INT_EN_GPIO2_INT_		(0x00000004)  /* R/W */
+#define	INT_EN_GPIO1_INT_		(0x00000002)  /* R/W */
+#define	INT_EN_GPIO0_INT_		(0x00000001)  /* R/W */
+
+#define BYTE_TEST		(0x64)
+#define FIFO_INT		(0x68)
+#define	FIFO_INT_TX_AVAIL_LEVEL_	(0xFF000000)  /* R/W */
+#define	FIFO_INT_TX_STS_LEVEL_		(0x00FF0000)  /* R/W */
+#define	FIFO_INT_RX_AVAIL_LEVEL_	(0x0000FF00)  /* R/W */
+#define	FIFO_INT_RX_STS_LEVEL_		(0x000000FF)  /* R/W */
+
+#define RX_CFG			(0x6C)
+#define	RX_CFG_RX_END_ALGN_		(0xC0000000)  /* R/W */
+#define		RX_CFG_RX_END_ALGN4_		(0x00000000)  /* R/W */
+#define		RX_CFG_RX_END_ALGN16_		(0x40000000)  /* R/W */
+#define		RX_CFG_RX_END_ALGN32_		(0x80000000)  /* R/W */
+#define	RX_CFG_RX_DMA_CNT_		(0x0FFF0000)  /* R/W */
+#define	RX_CFG_RX_DUMP_			(0x00008000)  /* R/W */
+#define	RX_CFG_RXDOFF_			(0x00001F00)  /* R/W */
+//#define	RX_CFG_RXBAD_			(0x00000001)  /* R/W */
+
+#define TX_CFG			(0x70)
+//#define	TX_CFG_TX_DMA_LVL_		(0xE0000000)	 /* R/W */
+//#define	TX_CFG_TX_DMA_CNT_		(0x0FFF0000)	 /* R/W Self Clearing */
+#define	TX_CFG_TXS_DUMP_		(0x00008000)  /* Self Clearing */
+#define	TX_CFG_TXD_DUMP_		(0x00004000)  /* Self Clearing */
+#define	TX_CFG_TXSAO_			(0x00000004)  /* R/W */
+#define	TX_CFG_TX_ON_			(0x00000002)  /* R/W */
+#define	TX_CFG_STOP_TX_			(0x00000001)  /* Self Clearing */
+
+#define HW_CFG			(0x74)
+#define	HW_CFG_TTM_			(0x00200000)  /* R/W */
+#define	HW_CFG_SF_			(0x00100000)  /* R/W */
+#define	HW_CFG_TX_FIF_SZ_		(0x000F0000)  /* R/W */
+#define	HW_CFG_TR_			(0x00003000)  /* R/W */
+#define	HW_CFG_PHY_CLK_SEL_		(0x00000060)  /* R/W */
+#define		 HW_CFG_PHY_CLK_SEL_INT_PHY_ 	(0x00000000) /* R/W */
+#define		 HW_CFG_PHY_CLK_SEL_EXT_PHY_ 	(0x00000020) /* R/W */
+#define		 HW_CFG_PHY_CLK_SEL_CLK_DIS_ 	(0x00000040) /* R/W */
+#define	HW_CFG_SMI_SEL_			(0x00000010)  /* R/W */
+#define	HW_CFG_EXT_PHY_DET_		(0x00000008)  /* RO */
+#define	HW_CFG_EXT_PHY_EN_		(0x00000004)  /* R/W */
+#define	HW_CFG_32_16_BIT_MODE_		(0x00000004)  /* RO */
+#define	HW_CFG_SRST_TO_			(0x00000002)  /* RO */
+#define	HW_CFG_SRST_			(0x00000001)  /* Self Clearing */
+
+#define RX_DP_CTRL		(0x78)
+#define	RX_DP_CTRL_RX_FFWD_		(0x80000000)  /* R/W */
+#define	RX_DP_CTRL_FFWD_BUSY_		(0x80000000)  /* RO */
+
+#define RX_FIFO_INF		(0x7C)
+#define	 RX_FIFO_INF_RXSUSED_		(0x00FF0000)  /* RO */
+#define	 RX_FIFO_INF_RXDUSED_		(0x0000FFFF)  /* RO */
+
+#define TX_FIFO_INF		(0x80)
+#define	TX_FIFO_INF_TSUSED_		(0x00FF0000)  /* RO */
+#define	TX_FIFO_INF_TDFREE_		(0x0000FFFF)  /* RO */
+
+#define PMT_CTRL		(0x84)
+#define	PMT_CTRL_PM_MODE_		(0x00003000)  /* Self Clearing */
+#define	PMT_CTRL_PHY_RST_		(0x00000400)  /* Self Clearing */
+#define	PMT_CTRL_WOL_EN_		(0x00000200)  /* R/W */
+#define	PMT_CTRL_ED_EN_			(0x00000100)  /* R/W */
+#define	PMT_CTRL_PME_TYPE_		(0x00000040)  /* R/W Not Affected by SW Reset */
+#define	PMT_CTRL_WUPS_			(0x00000030)  /* R/WC */
+#define		PMT_CTRL_WUPS_NOWAKE_		(0x00000000)  /* R/WC */
+#define		PMT_CTRL_WUPS_ED_		(0x00000010)  /* R/WC */
+#define		PMT_CTRL_WUPS_WOL_		(0x00000020)  /* R/WC */
+#define		PMT_CTRL_WUPS_MULTI_		(0x00000030)  /* R/WC */
+#define	PMT_CTRL_PME_IND_		(0x00000008)  /* R/W */
+#define	PMT_CTRL_PME_POL_		(0x00000004)  /* R/W */
+#define	PMT_CTRL_PME_EN_		(0x00000002)  /* R/W Not Affected by SW Reset */
+#define	PMT_CTRL_READY_			(0x00000001)  /* RO */
+
+#define GPIO_CFG		(0x88)
+#define	GPIO_CFG_LED3_EN_		(0x40000000)  /* R/W */
+#define	GPIO_CFG_LED2_EN_		(0x20000000)  /* R/W */
+#define	GPIO_CFG_LED1_EN_		(0x10000000)  /* R/W */
+#define	GPIO_CFG_GPIO2_INT_POL_		(0x04000000)  /* R/W */
+#define	GPIO_CFG_GPIO1_INT_POL_		(0x02000000)  /* R/W */
+#define	GPIO_CFG_GPIO0_INT_POL_		(0x01000000)  /* R/W */
+#define	GPIO_CFG_EEPR_EN_		(0x00700000)  /* R/W */
+#define	GPIO_CFG_GPIOBUF2_		(0x00040000)  /* R/W */
+#define	GPIO_CFG_GPIOBUF1_		(0x00020000)  /* R/W */
+#define	GPIO_CFG_GPIOBUF0_		(0x00010000)  /* R/W */
+#define	GPIO_CFG_GPIODIR2_		(0x00000400)  /* R/W */
+#define	GPIO_CFG_GPIODIR1_		(0x00000200)  /* R/W */
+#define	GPIO_CFG_GPIODIR0_		(0x00000100)  /* R/W */
+#define	GPIO_CFG_GPIOD4_		(0x00000010)  /* R/W */
+#define	GPIO_CFG_GPIOD3_		(0x00000008)  /* R/W */
+#define	GPIO_CFG_GPIOD2_		(0x00000004)  /* R/W */
+#define	GPIO_CFG_GPIOD1_		(0x00000002)  /* R/W */
+#define	GPIO_CFG_GPIOD0_		(0x00000001)  /* R/W */
+
+#define GPT_CFG			(0x8C)
+#define	GPT_CFG_TIMER_EN_		(0x20000000)  /* R/W */
+#define	GPT_CFG_GPT_LOAD_		(0x0000FFFF)  /* R/W */
+
+#define GPT_CNT			(0x90)
+#define	GPT_CNT_GPT_CNT_		(0x0000FFFF)  /* RO */
+
+#define ENDIAN			(0x98)
+#define FREE_RUN		(0x9C)
+#define RX_DROP			(0xA0)
+#define MAC_CSR_CMD		(0xA4)
+#define	 MAC_CSR_CMD_CSR_BUSY_		(0x80000000)  /* Self Clearing */
+#define	 MAC_CSR_CMD_R_NOT_W_		(0x40000000)  /* R/W */
+#define	 MAC_CSR_CMD_CSR_ADDR_		(0x000000FF)  /* R/W */
+
+#define MAC_CSR_DATA		(0xA8)
+#define AFC_CFG			(0xAC)
+#define		AFC_CFG_AFC_HI_			(0x00FF0000)  /* R/W */
+#define		AFC_CFG_AFC_LO_			(0x0000FF00)  /* R/W */
+#define		AFC_CFG_BACK_DUR_		(0x000000F0)  /* R/W */
+#define		AFC_CFG_FCMULT_			(0x00000008)  /* R/W */
+#define		AFC_CFG_FCBRD_			(0x00000004)  /* R/W */
+#define		AFC_CFG_FCADD_			(0x00000002)  /* R/W */
+#define		AFC_CFG_FCANY_			(0x00000001)  /* R/W */
+
+#define E2P_CMD			(0xB0)
+#define	E2P_CMD_EPC_BUSY_		(0x80000000)  /* Self Clearing */
+#define	E2P_CMD_EPC_CMD_			(0x70000000)  /* R/W */
+#define		E2P_CMD_EPC_CMD_READ_		(0x00000000)  /* R/W */
+#define		E2P_CMD_EPC_CMD_EWDS_		(0x10000000)  /* R/W */
+#define		E2P_CMD_EPC_CMD_EWEN_		(0x20000000)  /* R/W */
+#define		E2P_CMD_EPC_CMD_WRITE_		(0x30000000)  /* R/W */
+#define		E2P_CMD_EPC_CMD_WRAL_		(0x40000000)  /* R/W */
+#define		E2P_CMD_EPC_CMD_ERASE_		(0x50000000)  /* R/W */
+#define		E2P_CMD_EPC_CMD_ERAL_		(0x60000000)  /* R/W */
+#define		E2P_CMD_EPC_CMD_RELOAD_		(0x70000000)  /* R/W */
+#define	E2P_CMD_EPC_TIMEOUT_		(0x00000200)  /* RO */
+#define	E2P_CMD_MAC_ADDR_LOADED_	(0x00000100)  /* RO */
+#define	E2P_CMD_EPC_ADDR_		(0x000000FF)  /* R/W */
+
+#define E2P_DATA		(0xB4)
+#define	E2P_DATA_EEPROM_DATA_		(0x000000FF)  /* R/W */
+/* end of LAN register offsets and bit definitions */
+
+/*
+ ****************************************************************************
+ ****************************************************************************
+ * MAC Control and Status Register (Indirect Address)
+ * Offset (through the MAC_CSR CMD and DATA port)
+ ****************************************************************************
+ ****************************************************************************
+ *
+ */
+#define MAC_CR			(0x01)  /* R/W */
+
+/* MAC_CR - MAC Control Register */
+#define MAC_CR_RXALL_			(0x80000000)
+// TODO: delete this bit? It is not described in the data sheet.
+#define MAC_CR_HBDIS_			(0x10000000)
+#define MAC_CR_RCVOWN_			(0x00800000)
+#define MAC_CR_LOOPBK_			(0x00200000)
+#define MAC_CR_FDPX_			(0x00100000)
+#define MAC_CR_MCPAS_			(0x00080000)
+#define MAC_CR_PRMS_			(0x00040000)
+#define MAC_CR_INVFILT_			(0x00020000)
+#define MAC_CR_PASSBAD_			(0x00010000)
+#define MAC_CR_HFILT_			(0x00008000)
+#define MAC_CR_HPFILT_			(0x00002000)
+#define MAC_CR_LCOLL_			(0x00001000)
+#define MAC_CR_BCAST_			(0x00000800)
+#define MAC_CR_DISRTY_			(0x00000400)
+#define MAC_CR_PADSTR_			(0x00000100)
+#define MAC_CR_BOLMT_MASK_		(0x000000C0)
+#define MAC_CR_DFCHK_			(0x00000020)
+#define MAC_CR_TXEN_			(0x00000008)
+#define MAC_CR_RXEN_			(0x00000004)
+
+#define ADDRH			(0x02)	  /* R/W mask 0x0000FFFFUL */
+#define ADDRL			(0x03)	  /* R/W mask 0xFFFFFFFFUL */
+#define HASHH			(0x04)	  /* R/W */
+#define HASHL			(0x05)	  /* R/W */
+
+#define MII_ACC			(0x06)	  /* R/W */
+#define MII_ACC_PHY_ADDR_		(0x0000F800)
+#define MII_ACC_MIIRINDA_		(0x000007C0)
+#define MII_ACC_MII_WRITE_		(0x00000002)
+#define MII_ACC_MII_BUSY_		(0x00000001)
+
+#define MII_DATA		(0x07)	  /* R/W mask 0x0000FFFFUL */
+
+#define FLOW			(0x08)	  /* R/W */
+#define FLOW_FCPT_			(0xFFFF0000)
+#define FLOW_FCPASS_			(0x00000004)
+#define FLOW_FCEN_			(0x00000002)
+#define FLOW_FCBSY_			(0x00000001)
+
+#define VLAN1			(0x09)	  /* R/W mask 0x0000FFFFUL */
+#define VLAN1_VTI1_			(0x0000ffff)
+
+#define VLAN2			(0x0A)	  /* R/W mask 0x0000FFFFUL */
+#define VLAN2_VTI2_			(0x0000ffff)
+
+#define WUFF			(0x0B)	  /* WO */
+
+#define WUCSR			(0x0C)	  /* R/W */
+#define WUCSR_GUE_			(0x00000200)
+#define WUCSR_WUFR_			(0x00000040)
+#define WUCSR_MPR_			(0x00000020)
+#define WUCSR_WAKE_EN_			(0x00000004)
+#define WUCSR_MPEN_			(0x00000002)
+
+/*
+ ****************************************************************************
+ * Chip Specific MII Defines
+ ****************************************************************************
+ *
+ * Phy register offsets and bit definitions
+ *
+ */
+
+#define PHY_MODE_CTRL_STS	((u32)17)	/* Mode Control/Status Register */
+//#define MODE_CTRL_STS_FASTRIP_	  ((u16)0x4000)
+#define MODE_CTRL_STS_EDPWRDOWN_	 ((u16)0x2000)
+//#define MODE_CTRL_STS_LOWSQEN_	   ((u16)0x0800)
+//#define MODE_CTRL_STS_MDPREBP_	   ((u16)0x0400)
+//#define MODE_CTRL_STS_FARLOOPBACK_  ((u16)0x0200)
+//#define MODE_CTRL_STS_FASTEST_	   ((u16)0x0100)
+//#define MODE_CTRL_STS_REFCLKEN_	   ((u16)0x0010)
+//#define MODE_CTRL_STS_PHYADBP_	   ((u16)0x0008)
+//#define MODE_CTRL_STS_FORCE_G_LINK_ ((u16)0x0004)
+#define MODE_CTRL_STS_ENERGYON_	 	((u16)0x0002)
+
+#define PHY_INT_SRC			((u32)29)
+#define PHY_INT_SRC_ENERGY_ON_			((u16)0x0080)
+#define PHY_INT_SRC_ANEG_COMP_			((u16)0x0040)
+#define PHY_INT_SRC_REMOTE_FAULT_		((u16)0x0020)
+#define PHY_INT_SRC_LINK_DOWN_			((u16)0x0010)
+#define PHY_INT_SRC_ANEG_LP_ACK_		((u16)0x0008)
+#define PHY_INT_SRC_PAR_DET_FAULT_		((u16)0x0004)
+#define PHY_INT_SRC_ANEG_PGRX_			((u16)0x0002)
+
+#define PHY_INT_MASK			((u32)30)
+#define PHY_INT_MASK_ENERGY_ON_			((u16)0x0080)
+#define PHY_INT_MASK_ANEG_COMP_			((u16)0x0040)
+#define PHY_INT_MASK_REMOTE_FAULT_		((u16)0x0020)
+#define PHY_INT_MASK_LINK_DOWN_			((u16)0x0010)
+#define PHY_INT_MASK_ANEG_LP_ACK_		((u16)0x0008)
+#define PHY_INT_MASK_PAR_DET_FAULT_		((u16)0x0004)
+#define PHY_INT_MASK_ANEG_PGRX_			((u16)0x0002)
+
+#define PHY_SPECIAL			((u32)31)
+#define PHY_SPECIAL_ANEG_DONE_			((u16)0x1000)
+#define PHY_SPECIAL_RES_			((u16)0x0040)
+#define PHY_SPECIAL_RES_MASK_			((u16)0x0FE1)
+#define PHY_SPECIAL_SPD_			((u16)0x001C)
+#define PHY_SPECIAL_SPD_10HALF_			((u16)0x0004)
+#define PHY_SPECIAL_SPD_10FULL_			((u16)0x0014)
+#define PHY_SPECIAL_SPD_100HALF_		((u16)0x0008)
+#define PHY_SPECIAL_SPD_100FULL_		((u16)0x0018)
+
+#define LAN911X_INTERNAL_PHY_ID		(0x0007C000)
+
+/* Chip ID values */
+#define CHIP_9115	0x0115
+#define CHIP_9116	0x0116
+#define CHIP_9117	0x0117
+#define CHIP_9118	0x0118
+#define CHIP_9211	0x9211
+#define CHIP_9215	0x115A
+#define CHIP_9217	0x117A
+#define CHIP_9218	0x118A
+
+struct chip_id {
+	u16 id;
+	char *name;
+};
+
+static const struct chip_id chip_ids[] =  {
+	{ CHIP_9115, "LAN9115" },
+	{ CHIP_9116, "LAN9116" },
+	{ CHIP_9117, "LAN9117" },
+	{ CHIP_9118, "LAN9118" },
+	{ CHIP_9211, "LAN9211" },
+	{ CHIP_9215, "LAN9215" },
+	{ CHIP_9217, "LAN9217" },
+	{ CHIP_9218, "LAN9218" },
+	{ 0, NULL },
+};
+
+#define IS_REV_A(x)	((x & 0xFFFF)==0)
+
+/*
+ * Macros to abstract register access according to the data bus
+ * capabilities.  Please use those and not the in/out primitives.
+ */
+/* FIFO read/write macros */
+#define SMC_PUSH_DATA(lp, p, l)	SMC_outsl( lp, TX_DATA_FIFO, p, (l) >> 2 )
+#define SMC_PULL_DATA(lp, p, l)	SMC_insl ( lp, RX_DATA_FIFO, p, (l) >> 2 )
+#define SMC_SET_TX_FIFO(lp, x) 	SMC_outl( x, lp, TX_DATA_FIFO )
+#define SMC_GET_RX_FIFO(lp)	SMC_inl( lp, RX_DATA_FIFO )
+
+
+/* I/O mapped register read/write macros */
+#define SMC_GET_TX_STS_FIFO(lp)		SMC_inl( lp, TX_STATUS_FIFO )
+#define SMC_GET_RX_STS_FIFO(lp)		SMC_inl( lp, RX_STATUS_FIFO )
+#define SMC_GET_RX_STS_FIFO_PEEK(lp)	SMC_inl( lp, RX_STATUS_FIFO_PEEK )
+#define SMC_GET_PN(lp)			(SMC_inl( lp, ID_REV ) >> 16)
+#define SMC_GET_REV(lp)			(SMC_inl( lp, ID_REV ) & 0xFFFF)
+#define SMC_GET_IRQ_CFG(lp)		SMC_inl( lp, INT_CFG )
+#define SMC_SET_IRQ_CFG(lp, x)		SMC_outl( x, lp, INT_CFG )
+#define SMC_GET_INT(lp)			SMC_inl( lp, INT_STS )
+#define SMC_ACK_INT(lp, x)			SMC_outl( x, lp, INT_STS )
+#define SMC_GET_INT_EN(lp)		SMC_inl( lp, INT_EN )
+#define SMC_SET_INT_EN(lp, x)		SMC_outl( x, lp, INT_EN )
+#define SMC_GET_BYTE_TEST(lp)		SMC_inl( lp, BYTE_TEST )
+#define SMC_SET_BYTE_TEST(lp, x)		SMC_outl( x, lp, BYTE_TEST )
+#define SMC_GET_FIFO_INT(lp)		SMC_inl( lp, FIFO_INT )
+#define SMC_SET_FIFO_INT(lp, x)		SMC_outl( x, lp, FIFO_INT )
+#define SMC_SET_FIFO_TDA(lp, x)					\
+	do {							\
+		unsigned long __flags;				\
+		int __mask;					\
+		local_irq_save(__flags);			\
+		__mask = SMC_GET_FIFO_INT((lp)) & ~(0xFF<<24);	\
+		SMC_SET_FIFO_INT( (lp), __mask | (x)<<24 );	\
+		local_irq_restore(__flags);			\
+	} while (0)
+#define SMC_SET_FIFO_TSL(lp, x)					\
+	do {							\
+		unsigned long __flags;				\
+		int __mask;					\
+		local_irq_save(__flags);			\
+		__mask = SMC_GET_FIFO_INT((lp)) & ~(0xFF<<16);	\
+		SMC_SET_FIFO_INT( (lp), __mask | (((x) & 0xFF)<<16));	\
+		local_irq_restore(__flags);			\
+	} while (0)
+#define SMC_SET_FIFO_RSA(lp, x)					\
+	do {							\
+		unsigned long __flags;				\
+		int __mask;					\
+		local_irq_save(__flags);			\
+		__mask = SMC_GET_FIFO_INT((lp)) & ~(0xFF<<8);	\
+		SMC_SET_FIFO_INT( (lp), __mask | (((x) & 0xFF)<<8));	\
+		local_irq_restore(__flags);			\
+	} while (0)
+#define SMC_SET_FIFO_RSL(lp, x)					\
+	do {							\
+		unsigned long __flags;				\
+		int __mask;					\
+		local_irq_save(__flags);			\
+		__mask = SMC_GET_FIFO_INT((lp)) & ~0xFF;	\
+		SMC_SET_FIFO_INT( (lp),__mask | ((x) & 0xFF));	\
+		local_irq_restore(__flags);			\
+	} while (0)
+#define SMC_GET_RX_CFG(lp)		SMC_inl( lp, RX_CFG )
+#define SMC_SET_RX_CFG(lp, x)		SMC_outl( x, lp, RX_CFG )
+#define SMC_GET_TX_CFG(lp)		SMC_inl( lp, TX_CFG )
+#define SMC_SET_TX_CFG(lp, x)		SMC_outl( x, lp, TX_CFG )
+#define SMC_GET_HW_CFG(lp)		SMC_inl( lp, HW_CFG )
+#define SMC_SET_HW_CFG(lp, x)		SMC_outl( x, lp, HW_CFG )
+#define SMC_GET_RX_DP_CTRL(lp)		SMC_inl( lp, RX_DP_CTRL )
+#define SMC_SET_RX_DP_CTRL(lp, x)		SMC_outl( x, lp, RX_DP_CTRL )
+#define SMC_GET_PMT_CTRL(lp)		SMC_inl( lp, PMT_CTRL )
+#define SMC_SET_PMT_CTRL(lp, x)		SMC_outl( x, lp, PMT_CTRL )
+#define SMC_GET_GPIO_CFG(lp)		SMC_inl( lp, GPIO_CFG )
+#define SMC_SET_GPIO_CFG(lp, x)		SMC_outl( x, lp, GPIO_CFG )
+#define SMC_GET_RX_FIFO_INF(lp)		SMC_inl( lp, RX_FIFO_INF )
+#define SMC_SET_RX_FIFO_INF(lp, x)		SMC_outl( x, lp, RX_FIFO_INF )
+#define SMC_GET_TX_FIFO_INF(lp)		SMC_inl( lp, TX_FIFO_INF )
+#define SMC_SET_TX_FIFO_INF(lp, x)		SMC_outl( x, lp, TX_FIFO_INF )
+#define SMC_GET_GPT_CFG(lp)		SMC_inl( lp, GPT_CFG )
+#define SMC_SET_GPT_CFG(lp, x)		SMC_outl( x, lp, GPT_CFG )
+#define SMC_GET_RX_DROP(lp)		SMC_inl( lp, RX_DROP )
+#define SMC_SET_RX_DROP(lp, x)		SMC_outl( x, lp, RX_DROP )
+#define SMC_GET_MAC_CMD(lp)		SMC_inl( lp, MAC_CSR_CMD )
+#define SMC_SET_MAC_CMD(lp, x)		SMC_outl( x, lp, MAC_CSR_CMD )
+#define SMC_GET_MAC_DATA(lp)		SMC_inl( lp, MAC_CSR_DATA )
+#define SMC_SET_MAC_DATA(lp, x)		SMC_outl( x, lp, MAC_CSR_DATA )
+#define SMC_GET_AFC_CFG(lp)		SMC_inl( lp, AFC_CFG )
+#define SMC_SET_AFC_CFG(lp, x)		SMC_outl( x, lp, AFC_CFG )
+#define SMC_GET_E2P_CMD(lp)		SMC_inl( lp, E2P_CMD )
+#define SMC_SET_E2P_CMD(lp, x)		SMC_outl( x, lp, E2P_CMD )
+#define SMC_GET_E2P_DATA(lp)		SMC_inl( lp, E2P_DATA )
+#define SMC_SET_E2P_DATA(lp, x)		SMC_outl( x, lp, E2P_DATA )
+
+/* MAC register read/write macros */
+#define SMC_GET_MAC_CSR(lp,a,v)						\
+	do {								\
+		while (SMC_GET_MAC_CMD((lp)) & MAC_CSR_CMD_CSR_BUSY_);	\
+		SMC_SET_MAC_CMD((lp),MAC_CSR_CMD_CSR_BUSY_ |		\
+			MAC_CSR_CMD_R_NOT_W_ | (a) );			\
+		while (SMC_GET_MAC_CMD((lp)) & MAC_CSR_CMD_CSR_BUSY_);	\
+		v = SMC_GET_MAC_DATA((lp));			       	\
+	} while (0)
+#define SMC_SET_MAC_CSR(lp,a,v)						\
+	do {								\
+		while (SMC_GET_MAC_CMD((lp)) & MAC_CSR_CMD_CSR_BUSY_);	\
+		SMC_SET_MAC_DATA((lp), v);				\
+		SMC_SET_MAC_CMD((lp), MAC_CSR_CMD_CSR_BUSY_ | (a) );	\
+		while (SMC_GET_MAC_CMD((lp)) & MAC_CSR_CMD_CSR_BUSY_);	\
+	} while (0)
+#define SMC_GET_MAC_CR(lp, x)	SMC_GET_MAC_CSR( (lp), MAC_CR, x )
+#define SMC_SET_MAC_CR(lp, x)	SMC_SET_MAC_CSR( (lp), MAC_CR, x )
+#define SMC_GET_ADDRH(lp, x)	SMC_GET_MAC_CSR( (lp), ADDRH, x )
+#define SMC_SET_ADDRH(lp, x)	SMC_SET_MAC_CSR( (lp), ADDRH, x )
+#define SMC_GET_ADDRL(lp, x)	SMC_GET_MAC_CSR( (lp), ADDRL, x )
+#define SMC_SET_ADDRL(lp, x)	SMC_SET_MAC_CSR( (lp), ADDRL, x )
+#define SMC_GET_HASHH(lp, x)	SMC_GET_MAC_CSR( (lp), HASHH, x )
+#define SMC_SET_HASHH(lp, x)	SMC_SET_MAC_CSR( (lp), HASHH, x )
+#define SMC_GET_HASHL(lp, x)	SMC_GET_MAC_CSR( (lp), HASHL, x )
+#define SMC_SET_HASHL(lp, x)	SMC_SET_MAC_CSR( (lp), HASHL, x )
+#define SMC_GET_MII_ACC(lp, x)	SMC_GET_MAC_CSR( (lp), MII_ACC, x )
+#define SMC_SET_MII_ACC(lp, x)	SMC_SET_MAC_CSR( (lp), MII_ACC, x )
+#define SMC_GET_MII_DATA(lp, x)	SMC_GET_MAC_CSR( (lp), MII_DATA, x )
+#define SMC_SET_MII_DATA(lp, x)	SMC_SET_MAC_CSR( (lp), MII_DATA, x )
+#define SMC_GET_FLOW(lp, x)		SMC_GET_MAC_CSR( (lp), FLOW, x )
+#define SMC_SET_FLOW(lp, x)		SMC_SET_MAC_CSR( (lp), FLOW, x )
+#define SMC_GET_VLAN1(lp, x)	SMC_GET_MAC_CSR( (lp), VLAN1, x )
+#define SMC_SET_VLAN1(lp, x)	SMC_SET_MAC_CSR( (lp), VLAN1, x )
+#define SMC_GET_VLAN2(lp, x)	SMC_GET_MAC_CSR( (lp), VLAN2, x )
+#define SMC_SET_VLAN2(lp, x)	SMC_SET_MAC_CSR( (lp), VLAN2, x )
+#define SMC_SET_WUFF(lp, x)		SMC_SET_MAC_CSR( (lp), WUFF, x )
+#define SMC_GET_WUCSR(lp, x)	SMC_GET_MAC_CSR( (lp), WUCSR, x )
+#define SMC_SET_WUCSR(lp, x)	SMC_SET_MAC_CSR( (lp), WUCSR, x )
+
+/* PHY register read/write macros */
+#define SMC_GET_MII(lp,a,phy,v)					\
+	do {							\
+		u32 __v;					\
+		do {						\
+			SMC_GET_MII_ACC((lp), __v);			\
+		} while ( __v & MII_ACC_MII_BUSY_ );		\
+		SMC_SET_MII_ACC( (lp), ((phy)<<11) | ((a)<<6) |	\
+			MII_ACC_MII_BUSY_);			\
+		do {						\
+			SMC_GET_MII_ACC( (lp), __v);			\
+		} while ( __v & MII_ACC_MII_BUSY_ );		\
+		SMC_GET_MII_DATA((lp), v);				\
+	} while (0)
+#define SMC_SET_MII(lp,a,phy,v)					\
+	do {							\
+		u32 __v;					\
+		do {						\
+			SMC_GET_MII_ACC((lp), __v);			\
+		} while ( __v & MII_ACC_MII_BUSY_ );		\
+		SMC_SET_MII_DATA((lp), v);				\
+		SMC_SET_MII_ACC( (lp), ((phy)<<11) | ((a)<<6) |	\
+			MII_ACC_MII_BUSY_	 |		\
+			MII_ACC_MII_WRITE_  );			\
+		do {						\
+			SMC_GET_MII_ACC((lp), __v);			\
+		} while ( __v & MII_ACC_MII_BUSY_ );		\
+	} while (0)
+#define SMC_GET_PHY_BMCR(lp,phy,x)		SMC_GET_MII( (lp), MII_BMCR, phy, x )
+#define SMC_SET_PHY_BMCR(lp,phy,x)		SMC_SET_MII( (lp), MII_BMCR, phy, x )
+#define SMC_GET_PHY_BMSR(lp,phy,x)		SMC_GET_MII( (lp), MII_BMSR, phy, x )
+#define SMC_GET_PHY_ID1(lp,phy,x)		SMC_GET_MII( (lp), MII_PHYSID1, phy, x )
+#define SMC_GET_PHY_ID2(lp,phy,x)		SMC_GET_MII( (lp), MII_PHYSID2, phy, x )
+#define SMC_GET_PHY_MII_ADV(lp,phy,x)	SMC_GET_MII( (lp), MII_ADVERTISE, phy, x )
+#define SMC_SET_PHY_MII_ADV(lp,phy,x)	SMC_SET_MII( (lp), MII_ADVERTISE, phy, x )
+#define SMC_GET_PHY_MII_LPA(lp,phy,x)	SMC_GET_MII( (lp), MII_LPA, phy, x )
+#define SMC_SET_PHY_MII_LPA(lp,phy,x)	SMC_SET_MII( (lp), MII_LPA, phy, x )
+#define SMC_GET_PHY_CTRL_STS(lp,phy,x)	SMC_GET_MII( (lp), PHY_MODE_CTRL_STS, phy, x )
+#define SMC_SET_PHY_CTRL_STS(lp,phy,x)	SMC_SET_MII( (lp), PHY_MODE_CTRL_STS, phy, x )
+#define SMC_GET_PHY_INT_SRC(lp,phy,x)	SMC_GET_MII( (lp), PHY_INT_SRC, phy, x )
+#define SMC_SET_PHY_INT_SRC(lp,phy,x)	SMC_SET_MII( (lp), PHY_INT_SRC, phy, x )
+#define SMC_GET_PHY_INT_MASK(lp,phy,x)	SMC_GET_MII( (lp), PHY_INT_MASK, phy, x )
+#define SMC_SET_PHY_INT_MASK(lp,phy,x)	SMC_SET_MII( (lp), PHY_INT_MASK, phy, x )
+#define SMC_GET_PHY_SPECIAL(lp,phy,x)	SMC_GET_MII( (lp), PHY_SPECIAL, phy, x )
+
+
+
+/* Misc read/write macros */
+
+#ifndef SMC_GET_MAC_ADDR
+#define SMC_GET_MAC_ADDR(lp, addr)				\
+	do {							\
+		unsigned int __v;				\
+								\
+		SMC_GET_MAC_CSR((lp), ADDRL, __v);			\
+		addr[0] = __v; addr[1] = __v >> 8;		\
+		addr[2] = __v >> 16; addr[3] = __v >> 24;	\
+		SMC_GET_MAC_CSR((lp), ADDRH, __v);			\
+		addr[4] = __v; addr[5] = __v >> 8;		\
+	} while (0)
+#endif
+
+#define SMC_SET_MAC_ADDR(lp, addr)				\
+	do {							\
+		 SMC_SET_MAC_CSR((lp), ADDRL,				\
+				 addr[0] |			\
+				(addr[1] << 8) |		\
+				(addr[2] << 16) |		\
+				(addr[3] << 24));		\
+		 SMC_SET_MAC_CSR((lp), ADDRH, addr[4]|(addr[5] << 8));\
+	} while (0)
+
+
+#define SMC_WRITE_EEPROM_CMD(lp, cmd, addr)				\
+	do {								\
+		while (SMC_GET_E2P_CMD((lp)) & MAC_CSR_CMD_CSR_BUSY_);	\
+		SMC_SET_MAC_CMD((lp), MAC_CSR_CMD_R_NOT_W_ | a );		\
+		while (SMC_GET_MAC_CMD((lp)) & MAC_CSR_CMD_CSR_BUSY_);	\
+	} while (0)
+
+#endif	 /* _SMC911X_H_ */
diff --git a/drivers/net/ethernet/smsc/smc9194.c b/drivers/net/ethernet/smsc/smc9194.c
new file mode 100644
index 0000000..5b65ac4
--- /dev/null
+++ b/drivers/net/ethernet/smsc/smc9194.c
@@ -0,0 +1,1589 @@
+/*------------------------------------------------------------------------
+ . smc9194.c
+ . This is a driver for SMC's 9000 series of Ethernet cards.
+ .
+ . Copyright (C) 1996 by Erik Stahlman
+ . This software may be used and distributed according to the terms
+ . of the GNU General Public License, incorporated herein by reference.
+ .
+ . "Features" of the SMC chip:
+ .   4608 byte packet memory. ( for the 91C92.  Others have more )
+ .   EEPROM for configuration
+ .   AUI/TP selection  ( mine has 10Base2/10BaseT select )
+ .
+ . Arguments:
+ . 	io		 = for the base address
+ .	irq	 = for the IRQ
+ .	ifport = 0 for autodetect, 1 for TP, 2 for AUI ( or 10base2 )
+ .
+ . author:
+ . 	Erik Stahlman				( erik@vt.edu )
+ . contributors:
+ .      Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+ .
+ . Hardware multicast code from Peter Cammaert ( pc@denkart.be )
+ .
+ . Sources:
+ .    o   SMC databook
+ .    o   skeleton.c by Donald Becker ( becker@scyld.com )
+ .    o   ( a LOT of advice from Becker as well )
+ .
+ . History:
+ .	12/07/95  Erik Stahlman  written, got receive/xmit handled
+ . 	01/03/96  Erik Stahlman  worked out some bugs, actually usable!!! :-)
+ .	01/06/96  Erik Stahlman	 cleaned up some, better testing, etc
+ .	01/29/96  Erik Stahlman	 fixed autoirq, added multicast
+ . 	02/01/96  Erik Stahlman	 1. disabled all interrupts in smc_reset
+ .		   		 2. got rid of post-decrementing bug -- UGH.
+ .	02/13/96  Erik Stahlman  Tried to fix autoirq failure.  Added more
+ .				 descriptive error messages.
+ .	02/15/96  Erik Stahlman  Fixed typo that caused detection failure
+ . 	02/23/96  Erik Stahlman	 Modified it to fit into kernel tree
+ .				 Added support to change hardware address
+ .				 Cleared stats on opens
+ .	02/26/96  Erik Stahlman	 Trial support for Kernel 1.2.13
+ .				 Kludge for automatic IRQ detection
+ .	03/04/96  Erik Stahlman	 Fixed kernel 1.3.70 +
+ .				 Fixed bug reported by Gardner Buchanan in
+ .				   smc_enable, with outw instead of outb
+ .	03/06/96  Erik Stahlman  Added hardware multicast from Peter Cammaert
+ .	04/14/00  Heiko Pruessing (SMA Regelsysteme)  Fixed bug in chip memory
+ .				 allocation
+ .      08/20/00  Arnaldo Melo   fix kfree(skb) in smc_hardware_send_packet
+ .      12/15/00  Christian Jullien fix "Warning: kfree_skb on hard IRQ"
+ .      11/08/01 Matt Domsch     Use common crc32 function
+ ----------------------------------------------------------------------------*/
+
+static const char version[] =
+	"smc9194.c:v0.14 12/15/00 by Erik Stahlman (erik@vt.edu)\n";
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/crc32.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/bitops.h>
+
+#include <asm/io.h>
+
+#include "smc9194.h"
+
+#define DRV_NAME "smc9194"
+
+/*------------------------------------------------------------------------
+ .
+ . Configuration options, for the experienced user to change.
+ .
+ -------------------------------------------------------------------------*/
+
+/*
+ . Do you want to use 32 bit xfers?  This should work on all chips, as
+ . the chipset is designed to accommodate them.
+*/
+#ifdef __sh__
+#undef USE_32_BIT
+#else
+#define USE_32_BIT 1
+#endif
+
+#if defined(__H8300H__) || defined(__H8300S__)
+#define NO_AUTOPROBE
+#undef insl
+#undef outsl
+#define insl(a,b,l)  io_insl_noswap(a,b,l)
+#define outsl(a,b,l) io_outsl_noswap(a,b,l)
+#endif
+
+/*
+ .the SMC9194 can be at any of the following port addresses.  To change,
+ .for a slightly different card, you can add it to the array.  Keep in
+ .mind that the array must end in zero.
+*/
+
+struct devlist {
+	unsigned int port;
+	unsigned int irq;
+};
+
+#if defined(CONFIG_H8S_EDOSK2674)
+static struct devlist smc_devlist[] __initdata = {
+	{.port = 0xf80000, .irq = 16},
+	{.port = 0,        .irq = 0 },
+};
+#else
+static struct devlist smc_devlist[] __initdata = {
+	{.port = 0x200, .irq = 0},
+	{.port = 0x220, .irq = 0},
+	{.port = 0x240, .irq = 0},
+	{.port = 0x260, .irq = 0},
+	{.port = 0x280, .irq = 0},
+	{.port = 0x2A0, .irq = 0},
+	{.port = 0x2C0, .irq = 0},
+	{.port = 0x2E0, .irq = 0},
+	{.port = 0x300, .irq = 0},
+	{.port = 0x320, .irq = 0},
+	{.port = 0x340, .irq = 0},
+	{.port = 0x360, .irq = 0},
+	{.port = 0x380, .irq = 0},
+	{.port = 0x3A0, .irq = 0},
+	{.port = 0x3C0, .irq = 0},
+	{.port = 0x3E0, .irq = 0},
+	{.port = 0,     .irq = 0},
+};
+#endif
+/*
+ . Wait time for memory to be free.  This probably shouldn't be
+ . tuned that much, as waiting for this means nothing else happens
+ . in the system
+*/
+#define MEMORY_WAIT_TIME 16
+
+/*
+ . DEBUGGING LEVELS
+ .
+ . 0 for normal operation
+ . 1 for slightly more details
+ . >2 for various levels of increasingly useless information
+ .    2 for interrupt tracking, status flags
+ .    3 for packet dumps, etc.
+*/
+#define SMC_DEBUG 0
+
+#if (SMC_DEBUG > 2 )
+#define PRINTK3(x) printk x
+#else
+#define PRINTK3(x)
+#endif
+
+#if SMC_DEBUG > 1
+#define PRINTK2(x) printk x
+#else
+#define PRINTK2(x)
+#endif
+
+#ifdef SMC_DEBUG
+#define PRINTK(x) printk x
+#else
+#define PRINTK(x)
+#endif
+
+
+/*------------------------------------------------------------------------
+ .
+ . The internal workings of the driver.  If you are changing anything
+ . here with the SMC stuff, you should have the datasheet and known
+ . what you are doing.
+ .
+ -------------------------------------------------------------------------*/
+#define CARDNAME "SMC9194"
+
+
+/* store this information for the driver.. */
+struct smc_local {
+	/*
+	   If I have to wait until memory is available to send
+	   a packet, I will store the skbuff here, until I get the
+	   desired memory.  Then, I'll send it out and free it.
+	*/
+	struct sk_buff * saved_skb;
+
+	/*
+ 	 . This keeps track of how many packets that I have
+ 	 . sent out.  When an TX_EMPTY interrupt comes, I know
+	 . that all of these have been sent.
+	*/
+	int	packets_waiting;
+};
+
+
+/*-----------------------------------------------------------------
+ .
+ .  The driver can be entered at any of the following entry points.
+ .
+ .------------------------------------------------------------------  */
+
+/*
+ . This is called by  register_netdev().  It is responsible for
+ . checking the portlist for the SMC9000 series chipset.  If it finds
+ . one, then it will initialize the device, find the hardware information,
+ . and sets up the appropriate device parameters.
+ . NOTE: Interrupts are *OFF* when this procedure is called.
+ .
+ . NB:This shouldn't be static since it is referred to externally.
+*/
+struct net_device *smc_init(int unit);
+
+/*
+ . The kernel calls this function when someone wants to use the device,
+ . typically 'ifconfig ethX up'.
+*/
+static int smc_open(struct net_device *dev);
+
+/*
+ . Our watchdog timed out. Called by the networking layer
+*/
+static void smc_timeout(struct net_device *dev);
+
+/*
+ . This is called by the kernel in response to 'ifconfig ethX down'.  It
+ . is responsible for cleaning up everything that the open routine
+ . does, and maybe putting the card into a powerdown state.
+*/
+static int smc_close(struct net_device *dev);
+
+/*
+ . Finally, a call to set promiscuous mode ( for TCPDUMP and related
+ . programs ) and multicast modes.
+*/
+static void smc_set_multicast_list(struct net_device *dev);
+
+
+/*---------------------------------------------------------------
+ .
+ . Interrupt level calls..
+ .
+ ----------------------------------------------------------------*/
+
+/*
+ . Handles the actual interrupt
+*/
+static irqreturn_t smc_interrupt(int irq, void *);
+/*
+ . This is a separate procedure to handle the receipt of a packet, to
+ . leave the interrupt code looking slightly cleaner
+*/
+static inline void smc_rcv( struct net_device *dev );
+/*
+ . This handles a TX interrupt, which is only called when an error
+ . relating to a packet is sent.
+*/
+static inline void smc_tx( struct net_device * dev );
+
+/*
+ ------------------------------------------------------------
+ .
+ . Internal routines
+ .
+ ------------------------------------------------------------
+*/
+
+/*
+ . Test if a given location contains a chip, trying to cause as
+ . little damage as possible if it's not a SMC chip.
+*/
+static int smc_probe(struct net_device *dev, int ioaddr);
+
+/*
+ . A rather simple routine to print out a packet for debugging purposes.
+*/
+#if SMC_DEBUG > 2
+static void print_packet( byte *, int );
+#endif
+
+#define tx_done(dev) 1
+
+/* this is called to actually send the packet to the chip */
+static void smc_hardware_send_packet( struct net_device * dev );
+
+/* Since I am not sure if I will have enough room in the chip's ram
+ . to store the packet, I call this routine, which either sends it
+ . now, or generates an interrupt when the card is ready for the
+ . packet */
+static netdev_tx_t  smc_wait_to_send_packet( struct sk_buff * skb,
+					     struct net_device *dev );
+
+/* this does a soft reset on the device */
+static void smc_reset( int ioaddr );
+
+/* Enable Interrupts, Receive, and Transmit */
+static void smc_enable( int ioaddr );
+
+/* this puts the device in an inactive state */
+static void smc_shutdown( int ioaddr );
+
+/* This routine will find the IRQ of the driver if one is not
+ . specified in the input to the device.  */
+static int smc_findirq( int ioaddr );
+
+/*
+ . Function: smc_reset( int ioaddr )
+ . Purpose:
+ .  	This sets the SMC91xx chip to its normal state, hopefully from whatever
+ . 	mess that any other DOS driver has put it in.
+ .
+ . Maybe I should reset more registers to defaults in here?  SOFTRESET  should
+ . do that for me.
+ .
+ . Method:
+ .	1.  send a SOFT RESET
+ .	2.  wait for it to finish
+ .	3.  enable autorelease mode
+ .	4.  reset the memory management unit
+ .	5.  clear all interrupts
+ .
+*/
+static void smc_reset( int ioaddr )
+{
+	/* This resets the registers mostly to defaults, but doesn't
+	   affect EEPROM.  That seems unnecessary */
+	SMC_SELECT_BANK( 0 );
+	outw( RCR_SOFTRESET, ioaddr + RCR );
+
+	/* this should pause enough for the chip to be happy */
+	SMC_DELAY( );
+
+	/* Set the transmit and receive configuration registers to
+	   default values */
+	outw( RCR_CLEAR, ioaddr + RCR );
+	outw( TCR_CLEAR, ioaddr + TCR );
+
+	/* set the control register to automatically
+	   release successfully transmitted packets, to make the best
+	   use out of our limited memory */
+	SMC_SELECT_BANK( 1 );
+	outw( inw( ioaddr + CONTROL ) | CTL_AUTO_RELEASE , ioaddr + CONTROL );
+
+	/* Reset the MMU */
+	SMC_SELECT_BANK( 2 );
+	outw( MC_RESET, ioaddr + MMU_CMD );
+
+	/* Note:  It doesn't seem that waiting for the MMU busy is needed here,
+	   but this is a place where future chipsets _COULD_ break.  Be wary
+ 	   of issuing another MMU command right after this */
+
+	outb( 0, ioaddr + INT_MASK );
+}
+
+/*
+ . Function: smc_enable
+ . Purpose: let the chip talk to the outside work
+ . Method:
+ .	1.  Enable the transmitter
+ .	2.  Enable the receiver
+ .	3.  Enable interrupts
+*/
+static void smc_enable( int ioaddr )
+{
+	SMC_SELECT_BANK( 0 );
+	/* see the header file for options in TCR/RCR NORMAL*/
+	outw( TCR_NORMAL, ioaddr + TCR );
+	outw( RCR_NORMAL, ioaddr + RCR );
+
+	/* now, enable interrupts */
+	SMC_SELECT_BANK( 2 );
+	outb( SMC_INTERRUPT_MASK, ioaddr + INT_MASK );
+}
+
+/*
+ . Function: smc_shutdown
+ . Purpose:  closes down the SMC91xxx chip.
+ . Method:
+ .	1. zero the interrupt mask
+ .	2. clear the enable receive flag
+ .	3. clear the enable xmit flags
+ .
+ . TODO:
+ .   (1) maybe utilize power down mode.
+ .	Why not yet?  Because while the chip will go into power down mode,
+ .	the manual says that it will wake up in response to any I/O requests
+ .	in the register space.   Empirical results do not show this working.
+*/
+static void smc_shutdown( int ioaddr )
+{
+	/* no more interrupts for me */
+	SMC_SELECT_BANK( 2 );
+	outb( 0, ioaddr + INT_MASK );
+
+	/* and tell the card to stay away from that nasty outside world */
+	SMC_SELECT_BANK( 0 );
+	outb( RCR_CLEAR, ioaddr + RCR );
+	outb( TCR_CLEAR, ioaddr + TCR );
+#if 0
+	/* finally, shut the chip down */
+	SMC_SELECT_BANK( 1 );
+	outw( inw( ioaddr + CONTROL ), CTL_POWERDOWN, ioaddr + CONTROL  );
+#endif
+}
+
+
+/*
+ . Function: smc_setmulticast( int ioaddr, struct net_device *dev )
+ . Purpose:
+ .    This sets the internal hardware table to filter out unwanted multicast
+ .    packets before they take up memory.
+ .
+ .    The SMC chip uses a hash table where the high 6 bits of the CRC of
+ .    address are the offset into the table.  If that bit is 1, then the
+ .    multicast packet is accepted.  Otherwise, it's dropped silently.
+ .
+ .    To use the 6 bits as an offset into the table, the high 3 bits are the
+ .    number of the 8 bit register, while the low 3 bits are the bit within
+ .    that register.
+ .
+ . This routine is based very heavily on the one provided by Peter Cammaert.
+*/
+
+
+static void smc_setmulticast(int ioaddr, struct net_device *dev)
+{
+	int			i;
+	unsigned char		multicast_table[ 8 ];
+	struct netdev_hw_addr *ha;
+	/* table for flipping the order of 3 bits */
+	unsigned char invert3[] = { 0, 4, 2, 6, 1, 5, 3, 7 };
+
+	/* start with a table of all zeros: reject all */
+	memset( multicast_table, 0, sizeof( multicast_table ) );
+
+	netdev_for_each_mc_addr(ha, dev) {
+		int position;
+
+		/* only use the low order bits */
+		position = ether_crc_le(6, ha->addr) & 0x3f;
+
+		/* do some messy swapping to put the bit in the right spot */
+		multicast_table[invert3[position&7]] |=
+					(1<<invert3[(position>>3)&7]);
+
+	}
+	/* now, the table can be loaded into the chipset */
+	SMC_SELECT_BANK( 3 );
+
+	for ( i = 0; i < 8 ; i++ ) {
+		outb( multicast_table[i], ioaddr + MULTICAST1 + i );
+	}
+}
+
+/*
+ . Function: smc_wait_to_send_packet( struct sk_buff * skb, struct net_device * )
+ . Purpose:
+ .    Attempt to allocate memory for a packet, if chip-memory is not
+ .    available, then tell the card to generate an interrupt when it
+ .    is available.
+ .
+ . Algorithm:
+ .
+ . o	if the saved_skb is not currently null, then drop this packet
+ .	on the floor.  This should never happen, because of TBUSY.
+ . o	if the saved_skb is null, then replace it with the current packet,
+ . o	See if I can sending it now.
+ . o 	(NO): Enable interrupts and let the interrupt handler deal with it.
+ . o	(YES):Send it now.
+*/
+static netdev_tx_t smc_wait_to_send_packet(struct sk_buff *skb,
+					   struct net_device *dev)
+{
+	struct smc_local *lp = netdev_priv(dev);
+	unsigned int ioaddr 	= dev->base_addr;
+	word 			length;
+	unsigned short 		numPages;
+	word			time_out;
+
+	netif_stop_queue(dev);
+	/* Well, I want to send the packet.. but I don't know
+	   if I can send it right now...  */
+
+	if ( lp->saved_skb) {
+		/* THIS SHOULD NEVER HAPPEN. */
+		dev->stats.tx_aborted_errors++;
+		printk(CARDNAME": Bad Craziness - sent packet while busy.\n" );
+		return NETDEV_TX_BUSY;
+	}
+	lp->saved_skb = skb;
+
+	length = skb->len;
+
+	if (length < ETH_ZLEN) {
+		if (skb_padto(skb, ETH_ZLEN)) {
+			netif_wake_queue(dev);
+			return NETDEV_TX_OK;
+		}
+		length = ETH_ZLEN;
+	}
+
+	/*
+	** The MMU wants the number of pages to be the number of 256 bytes
+	** 'pages', minus 1 ( since a packet can't ever have 0 pages :) )
+	**
+	** Pkt size for allocating is data length +6 (for additional status words,
+	** length and ctl!) If odd size last byte is included in this header.
+	*/
+	numPages =  ((length & 0xfffe) + 6) / 256;
+
+	if (numPages > 7 ) {
+		printk(CARDNAME": Far too big packet error.\n");
+		/* freeing the packet is a good thing here... but should
+		 . any packets of this size get down here?   */
+		dev_kfree_skb (skb);
+		lp->saved_skb = NULL;
+		/* this IS an error, but, i don't want the skb saved */
+		netif_wake_queue(dev);
+		return NETDEV_TX_OK;
+	}
+	/* either way, a packet is waiting now */
+	lp->packets_waiting++;
+
+	/* now, try to allocate the memory */
+	SMC_SELECT_BANK( 2 );
+	outw( MC_ALLOC | numPages, ioaddr + MMU_CMD );
+	/*
+ 	. Performance Hack
+	.
+ 	. wait a short amount of time.. if I can send a packet now, I send
+	. it now.  Otherwise, I enable an interrupt and wait for one to be
+	. available.
+	.
+	. I could have handled this a slightly different way, by checking to
+	. see if any memory was available in the FREE MEMORY register.  However,
+	. either way, I need to generate an allocation, and the allocation works
+	. no matter what, so I saw no point in checking free memory.
+	*/
+	time_out = MEMORY_WAIT_TIME;
+	do {
+		word	status;
+
+		status = inb( ioaddr + INTERRUPT );
+		if ( status & IM_ALLOC_INT ) {
+			/* acknowledge the interrupt */
+			outb( IM_ALLOC_INT, ioaddr + INTERRUPT );
+  			break;
+		}
+   	} while ( -- time_out );
+
+   	if ( !time_out ) {
+		/* oh well, wait until the chip finds memory later */
+		SMC_ENABLE_INT( IM_ALLOC_INT );
+		PRINTK2((CARDNAME": memory allocation deferred.\n"));
+		/* it's deferred, but I'll handle it later */
+		return NETDEV_TX_OK;
+   	}
+	/* or YES! I can send the packet now.. */
+	smc_hardware_send_packet(dev);
+	netif_wake_queue(dev);
+	return NETDEV_TX_OK;
+}
+
+/*
+ . Function:  smc_hardware_send_packet(struct net_device * )
+ . Purpose:
+ .	This sends the actual packet to the SMC9xxx chip.
+ .
+ . Algorithm:
+ . 	First, see if a saved_skb is available.
+ .		( this should NOT be called if there is no 'saved_skb'
+ .	Now, find the packet number that the chip allocated
+ .	Point the data pointers at it in memory
+ .	Set the length word in the chip's memory
+ .	Dump the packet to chip memory
+ .	Check if a last byte is needed ( odd length packet )
+ .		if so, set the control flag right
+ . 	Tell the card to send it
+ .	Enable the transmit interrupt, so I know if it failed
+ . 	Free the kernel data if I actually sent it.
+*/
+static void smc_hardware_send_packet( struct net_device * dev )
+{
+	struct smc_local *lp = netdev_priv(dev);
+	byte	 		packet_no;
+	struct sk_buff * 	skb = lp->saved_skb;
+	word			length;
+	unsigned int		ioaddr;
+	byte			* buf;
+
+	ioaddr = dev->base_addr;
+
+	if ( !skb ) {
+		PRINTK((CARDNAME": In XMIT with no packet to send\n"));
+		return;
+	}
+	length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
+	buf = skb->data;
+
+	/* If I get here, I _know_ there is a packet slot waiting for me */
+	packet_no = inb( ioaddr + PNR_ARR + 1 );
+	if ( packet_no & 0x80 ) {
+		/* or isn't there?  BAD CHIP! */
+		printk(KERN_DEBUG CARDNAME": Memory allocation failed.\n");
+		dev_kfree_skb_any(skb);
+		lp->saved_skb = NULL;
+		netif_wake_queue(dev);
+		return;
+	}
+
+	/* we have a packet address, so tell the card to use it */
+	outb( packet_no, ioaddr + PNR_ARR );
+
+	/* point to the beginning of the packet */
+	outw( PTR_AUTOINC , ioaddr + POINTER );
+
+   	PRINTK3((CARDNAME": Trying to xmit packet of length %x\n", length ));
+#if SMC_DEBUG > 2
+	print_packet( buf, length );
+#endif
+
+	/* send the packet length ( +6 for status, length and ctl byte )
+ 	   and the status word ( set to zeros ) */
+#ifdef USE_32_BIT
+	outl(  (length +6 ) << 16 , ioaddr + DATA_1 );
+#else
+	outw( 0, ioaddr + DATA_1 );
+	/* send the packet length ( +6 for status words, length, and ctl*/
+	outb( (length+6) & 0xFF,ioaddr + DATA_1 );
+	outb( (length+6) >> 8 , ioaddr + DATA_1 );
+#endif
+
+	/* send the actual data
+	 . I _think_ it's faster to send the longs first, and then
+	 . mop up by sending the last word.  It depends heavily
+ 	 . on alignment, at least on the 486.  Maybe it would be
+ 	 . a good idea to check which is optimal?  But that could take
+	 . almost as much time as is saved?
+	*/
+#ifdef USE_32_BIT
+	if ( length & 0x2  ) {
+		outsl(ioaddr + DATA_1, buf,  length >> 2 );
+#if !defined(__H8300H__) && !defined(__H8300S__)
+		outw( *((word *)(buf + (length & 0xFFFFFFFC))),ioaddr +DATA_1);
+#else
+		ctrl_outw( *((word *)(buf + (length & 0xFFFFFFFC))),ioaddr +DATA_1);
+#endif
+	}
+	else
+		outsl(ioaddr + DATA_1, buf,  length >> 2 );
+#else
+	outsw(ioaddr + DATA_1 , buf, (length ) >> 1);
+#endif
+	/* Send the last byte, if there is one.   */
+
+	if ( (length & 1) == 0 ) {
+		outw( 0, ioaddr + DATA_1 );
+	} else {
+		outb( buf[length -1 ], ioaddr + DATA_1 );
+		outb( 0x20, ioaddr + DATA_1);
+	}
+
+	/* enable the interrupts */
+	SMC_ENABLE_INT( (IM_TX_INT | IM_TX_EMPTY_INT) );
+
+	/* and let the chipset deal with it */
+	outw( MC_ENQUEUE , ioaddr + MMU_CMD );
+
+	PRINTK2((CARDNAME": Sent packet of length %d\n", length));
+
+	lp->saved_skb = NULL;
+	dev_kfree_skb_any (skb);
+
+	dev->trans_start = jiffies;
+
+	/* we can send another packet */
+	netif_wake_queue(dev);
+}
+
+/*-------------------------------------------------------------------------
+ |
+ | smc_init(int unit)
+ |   Input parameters:
+ |	dev->base_addr == 0, try to find all possible locations
+ |	dev->base_addr == 1, return failure code
+ |	dev->base_addr == 2, always allocate space,  and return success
+ |	dev->base_addr == <anything else>   this is the address to check
+ |
+ |   Output:
+ |	pointer to net_device or ERR_PTR(error)
+ |
+ ---------------------------------------------------------------------------
+*/
+static int io;
+static int irq;
+static int ifport;
+
+struct net_device * __init smc_init(int unit)
+{
+	struct net_device *dev = alloc_etherdev(sizeof(struct smc_local));
+	struct devlist *smcdev = smc_devlist;
+	int err = 0;
+
+	if (!dev)
+		return ERR_PTR(-ENODEV);
+
+	if (unit >= 0) {
+		sprintf(dev->name, "eth%d", unit);
+		netdev_boot_setup_check(dev);
+		io = dev->base_addr;
+		irq = dev->irq;
+	}
+
+	if (io > 0x1ff) {	/* Check a single specified location. */
+		err = smc_probe(dev, io);
+	} else if (io != 0) {	/* Don't probe at all. */
+		err = -ENXIO;
+	} else {
+		for (;smcdev->port; smcdev++) {
+			if (smc_probe(dev, smcdev->port) == 0)
+				break;
+		}
+		if (!smcdev->port)
+			err = -ENODEV;
+	}
+	if (err)
+		goto out;
+	err = register_netdev(dev);
+	if (err)
+		goto out1;
+	return dev;
+out1:
+	free_irq(dev->irq, dev);
+	release_region(dev->base_addr, SMC_IO_EXTENT);
+out:
+	free_netdev(dev);
+	return ERR_PTR(err);
+}
+
+/*----------------------------------------------------------------------
+ . smc_findirq
+ .
+ . This routine has a simple purpose -- make the SMC chip generate an
+ . interrupt, so an auto-detect routine can detect it, and find the IRQ,
+ ------------------------------------------------------------------------
+*/
+static int __init smc_findirq(int ioaddr)
+{
+#ifndef NO_AUTOPROBE
+	int	timeout = 20;
+	unsigned long cookie;
+
+
+	cookie = probe_irq_on();
+
+	/*
+	 * What I try to do here is trigger an ALLOC_INT. This is done
+	 * by allocating a small chunk of memory, which will give an interrupt
+	 * when done.
+	 */
+
+
+	SMC_SELECT_BANK(2);
+	/* enable ALLOCation interrupts ONLY */
+	outb( IM_ALLOC_INT, ioaddr + INT_MASK );
+
+	/*
+ 	 . Allocate 512 bytes of memory.  Note that the chip was just
+	 . reset so all the memory is available
+	*/
+	outw( MC_ALLOC | 1, ioaddr + MMU_CMD );
+
+	/*
+	 . Wait until positive that the interrupt has been generated
+	*/
+	while ( timeout ) {
+		byte	int_status;
+
+		int_status = inb( ioaddr + INTERRUPT );
+
+		if ( int_status & IM_ALLOC_INT )
+			break;		/* got the interrupt */
+		timeout--;
+	}
+	/* there is really nothing that I can do here if timeout fails,
+	   as probe_irq_off will return a 0 anyway, which is what I
+	   want in this case.   Plus, the clean up is needed in both
+	   cases.  */
+
+	/* DELAY HERE!
+	   On a fast machine, the status might change before the interrupt
+	   is given to the processor.  This means that the interrupt was
+	   never detected, and probe_irq_off fails to report anything.
+	   This should fix probe_irq_* problems.
+	*/
+	SMC_DELAY();
+	SMC_DELAY();
+
+	/* and disable all interrupts again */
+	outb( 0, ioaddr + INT_MASK );
+
+	/* and return what I found */
+	return probe_irq_off(cookie);
+#else /* NO_AUTOPROBE */
+	struct devlist *smcdev;
+	for (smcdev = smc_devlist; smcdev->port; smcdev++) {
+		if (smcdev->port == ioaddr)
+			return smcdev->irq;
+	}
+	return 0;
+#endif
+}
+
+static const struct net_device_ops smc_netdev_ops = {
+	.ndo_open		 = smc_open,
+	.ndo_stop		= smc_close,
+	.ndo_start_xmit    	= smc_wait_to_send_packet,
+	.ndo_tx_timeout	    	= smc_timeout,
+	.ndo_set_multicast_list	= smc_set_multicast_list,
+	.ndo_change_mtu		= eth_change_mtu,
+	.ndo_set_mac_address 	= eth_mac_addr,
+	.ndo_validate_addr	= eth_validate_addr,
+};
+
+/*----------------------------------------------------------------------
+ . Function: smc_probe( int ioaddr )
+ .
+ . Purpose:
+ .	Tests to see if a given ioaddr points to an SMC9xxx chip.
+ .	Returns a 0 on success
+ .
+ . Algorithm:
+ .	(1) see if the high byte of BANK_SELECT is 0x33
+ . 	(2) compare the ioaddr with the base register's address
+ .	(3) see if I recognize the chip ID in the appropriate register
+ .
+ .---------------------------------------------------------------------
+ */
+
+/*---------------------------------------------------------------
+ . Here I do typical initialization tasks.
+ .
+ . o  Initialize the structure if needed
+ . o  print out my vanity message if not done so already
+ . o  print out what type of hardware is detected
+ . o  print out the ethernet address
+ . o  find the IRQ
+ . o  set up my private data
+ . o  configure the dev structure with my subroutines
+ . o  actually GRAB the irq.
+ . o  GRAB the region
+ .-----------------------------------------------------------------
+*/
+static int __init smc_probe(struct net_device *dev, int ioaddr)
+{
+	int i, memory, retval;
+	static unsigned version_printed;
+	unsigned int bank;
+
+	const char *version_string;
+	const char *if_string;
+
+	/* registers */
+	word revision_register;
+	word base_address_register;
+	word configuration_register;
+	word memory_info_register;
+	word memory_cfg_register;
+
+	/* Grab the region so that no one else tries to probe our ioports. */
+	if (!request_region(ioaddr, SMC_IO_EXTENT, DRV_NAME))
+		return -EBUSY;
+
+	dev->irq = irq;
+	dev->if_port = ifport;
+
+	/* First, see if the high byte is 0x33 */
+	bank = inw( ioaddr + BANK_SELECT );
+	if ( (bank & 0xFF00) != 0x3300 ) {
+		retval = -ENODEV;
+		goto err_out;
+	}
+	/* The above MIGHT indicate a device, but I need to write to further
+ 	 	test this.  */
+	outw( 0x0, ioaddr + BANK_SELECT );
+	bank = inw( ioaddr + BANK_SELECT );
+	if ( (bank & 0xFF00 ) != 0x3300 ) {
+		retval = -ENODEV;
+		goto err_out;
+	}
+#if !defined(CONFIG_H8S_EDOSK2674)
+	/* well, we've already written once, so hopefully another time won't
+ 	   hurt.  This time, I need to switch the bank register to bank 1,
+	   so I can access the base address register */
+	SMC_SELECT_BANK(1);
+	base_address_register = inw( ioaddr + BASE );
+	if ( ioaddr != ( base_address_register >> 3 & 0x3E0 ) )  {
+		printk(CARDNAME ": IOADDR %x doesn't match configuration (%x). "
+			"Probably not a SMC chip\n",
+			ioaddr, base_address_register >> 3 & 0x3E0 );
+		/* well, the base address register didn't match.  Must not have
+		   been a SMC chip after all. */
+		retval = -ENODEV;
+		goto err_out;
+	}
+#else
+	(void)base_address_register; /* Warning suppression */
+#endif
+
+
+	/*  check if the revision register is something that I recognize.
+	    These might need to be added to later, as future revisions
+	    could be added.  */
+	SMC_SELECT_BANK(3);
+	revision_register  = inw( ioaddr + REVISION );
+	if ( !chip_ids[ ( revision_register  >> 4 ) & 0xF  ] ) {
+		/* I don't recognize this chip, so... */
+		printk(CARDNAME ": IO %x: Unrecognized revision register:"
+			" %x, Contact author.\n", ioaddr, revision_register);
+
+		retval = -ENODEV;
+		goto err_out;
+	}
+
+	/* at this point I'll assume that the chip is an SMC9xxx.
+	   It might be prudent to check a listing of MAC addresses
+	   against the hardware address, or do some other tests. */
+
+	if (version_printed++ == 0)
+		printk("%s", version);
+
+	/* fill in some of the fields */
+	dev->base_addr = ioaddr;
+
+	/*
+ 	 . Get the MAC address ( bank 1, regs 4 - 9 )
+	*/
+	SMC_SELECT_BANK( 1 );
+	for ( i = 0; i < 6; i += 2 ) {
+		word	address;
+
+		address = inw( ioaddr + ADDR0 + i  );
+		dev->dev_addr[ i + 1] = address >> 8;
+		dev->dev_addr[ i ] = address & 0xFF;
+	}
+
+	/* get the memory information */
+
+	SMC_SELECT_BANK( 0 );
+	memory_info_register = inw( ioaddr + MIR );
+	memory_cfg_register  = inw( ioaddr + MCR );
+	memory = ( memory_cfg_register >> 9 )  & 0x7;  /* multiplier */
+	memory *= 256 * ( memory_info_register & 0xFF );
+
+	/*
+	 Now, I want to find out more about the chip.  This is sort of
+ 	 redundant, but it's cleaner to have it in both, rather than having
+ 	 one VERY long probe procedure.
+	*/
+	SMC_SELECT_BANK(3);
+	revision_register  = inw( ioaddr + REVISION );
+	version_string = chip_ids[ ( revision_register  >> 4 ) & 0xF  ];
+	if ( !version_string ) {
+		/* I shouldn't get here because this call was done before.... */
+		retval = -ENODEV;
+		goto err_out;
+	}
+
+	/* is it using AUI or 10BaseT ? */
+	if ( dev->if_port == 0 ) {
+		SMC_SELECT_BANK(1);
+		configuration_register = inw( ioaddr + CONFIG );
+		if ( configuration_register & CFG_AUI_SELECT )
+			dev->if_port = 2;
+		else
+			dev->if_port = 1;
+	}
+	if_string = interfaces[ dev->if_port - 1 ];
+
+	/* now, reset the chip, and put it into a known state */
+	smc_reset( ioaddr );
+
+	/*
+	 . If dev->irq is 0, then the device has to be banged on to see
+	 . what the IRQ is.
+ 	 .
+	 . This banging doesn't always detect the IRQ, for unknown reasons.
+	 . a workaround is to reset the chip and try again.
+	 .
+	 . Interestingly, the DOS packet driver *SETS* the IRQ on the card to
+	 . be what is requested on the command line.   I don't do that, mostly
+	 . because the card that I have uses a non-standard method of accessing
+	 . the IRQs, and because this _should_ work in most configurations.
+	 .
+	 . Specifying an IRQ is done with the assumption that the user knows
+	 . what (s)he is doing.  No checking is done!!!!
+ 	 .
+	*/
+	if ( dev->irq < 2 ) {
+		int	trials;
+
+		trials = 3;
+		while ( trials-- ) {
+			dev->irq = smc_findirq( ioaddr );
+			if ( dev->irq )
+				break;
+			/* kick the card and try again */
+			smc_reset( ioaddr );
+		}
+	}
+	if (dev->irq == 0 ) {
+		printk(CARDNAME": Couldn't autodetect your IRQ. Use irq=xx.\n");
+		retval = -ENODEV;
+		goto err_out;
+	}
+
+	/* now, print out the card info, in a short format.. */
+
+	printk("%s: %s(r:%d) at %#3x IRQ:%d INTF:%s MEM:%db ", dev->name,
+		version_string, revision_register & 0xF, ioaddr, dev->irq,
+		if_string, memory );
+	/*
+	 . Print the Ethernet address
+	*/
+	printk("ADDR: %pM\n", dev->dev_addr);
+
+	/* Grab the IRQ */
+      	retval = request_irq(dev->irq, smc_interrupt, 0, DRV_NAME, dev);
+      	if (retval) {
+		printk("%s: unable to get IRQ %d (irqval=%d).\n", DRV_NAME,
+			dev->irq, retval);
+  	  	goto err_out;
+      	}
+
+	dev->netdev_ops			= &smc_netdev_ops;
+	dev->watchdog_timeo		= HZ/20;
+
+	return 0;
+
+err_out:
+	release_region(ioaddr, SMC_IO_EXTENT);
+	return retval;
+}
+
+#if SMC_DEBUG > 2
+static void print_packet( byte * buf, int length )
+{
+#if 0
+	int i;
+	int remainder;
+	int lines;
+
+	printk("Packet of length %d\n", length);
+	lines = length / 16;
+	remainder = length % 16;
+
+	for ( i = 0; i < lines ; i ++ ) {
+		int cur;
+
+		for ( cur = 0; cur < 8; cur ++ ) {
+			byte a, b;
+
+			a = *(buf ++ );
+			b = *(buf ++ );
+			printk("%02x%02x ", a, b );
+		}
+		printk("\n");
+	}
+	for ( i = 0; i < remainder/2 ; i++ ) {
+		byte a, b;
+
+		a = *(buf ++ );
+		b = *(buf ++ );
+		printk("%02x%02x ", a, b );
+	}
+	printk("\n");
+#endif
+}
+#endif
+
+
+/*
+ * Open and Initialize the board
+ *
+ * Set up everything, reset the card, etc ..
+ *
+ */
+static int smc_open(struct net_device *dev)
+{
+	int	ioaddr = dev->base_addr;
+
+	int	i;	/* used to set hw ethernet address */
+
+	/* clear out all the junk that was put here before... */
+	memset(netdev_priv(dev), 0, sizeof(struct smc_local));
+
+	/* reset the hardware */
+
+	smc_reset( ioaddr );
+	smc_enable( ioaddr );
+
+	/* Select which interface to use */
+
+	SMC_SELECT_BANK( 1 );
+	if ( dev->if_port == 1 ) {
+		outw( inw( ioaddr + CONFIG ) & ~CFG_AUI_SELECT,
+			ioaddr + CONFIG );
+	}
+	else if ( dev->if_port == 2 ) {
+		outw( inw( ioaddr + CONFIG ) | CFG_AUI_SELECT,
+			ioaddr + CONFIG );
+	}
+
+	/*
+  		According to Becker, I have to set the hardware address
+		at this point, because the (l)user can set it with an
+		ioctl.  Easily done...
+	*/
+	SMC_SELECT_BANK( 1 );
+	for ( i = 0; i < 6; i += 2 ) {
+		word	address;
+
+		address = dev->dev_addr[ i + 1 ] << 8 ;
+		address  |= dev->dev_addr[ i ];
+		outw( address, ioaddr + ADDR0 + i );
+	}
+
+	netif_start_queue(dev);
+	return 0;
+}
+
+/*--------------------------------------------------------
+ . Called by the kernel to send a packet out into the void
+ . of the net.  This routine is largely based on
+ . skeleton.c, from Becker.
+ .--------------------------------------------------------
+*/
+
+static void smc_timeout(struct net_device *dev)
+{
+	/* If we get here, some higher level has decided we are broken.
+	   There should really be a "kick me" function call instead. */
+	printk(KERN_WARNING CARDNAME": transmit timed out, %s?\n",
+		tx_done(dev) ? "IRQ conflict" :
+		"network cable problem");
+	/* "kick" the adaptor */
+	smc_reset( dev->base_addr );
+	smc_enable( dev->base_addr );
+	dev->trans_start = jiffies; /* prevent tx timeout */
+	/* clear anything saved */
+	((struct smc_local *)netdev_priv(dev))->saved_skb = NULL;
+	netif_wake_queue(dev);
+}
+
+/*-------------------------------------------------------------
+ .
+ . smc_rcv -  receive a packet from the card
+ .
+ . There is ( at least ) a packet waiting to be read from
+ . chip-memory.
+ .
+ . o Read the status
+ . o If an error, record it
+ . o otherwise, read in the packet
+ --------------------------------------------------------------
+*/
+static void smc_rcv(struct net_device *dev)
+{
+	int 	ioaddr = dev->base_addr;
+	int 	packet_number;
+	word	status;
+	word	packet_length;
+
+	/* assume bank 2 */
+
+	packet_number = inw( ioaddr + FIFO_PORTS );
+
+	if ( packet_number & FP_RXEMPTY ) {
+		/* we got called , but nothing was on the FIFO */
+		PRINTK((CARDNAME ": WARNING: smc_rcv with nothing on FIFO.\n"));
+		/* don't need to restore anything */
+		return;
+	}
+
+	/*  start reading from the start of the packet */
+	outw( PTR_READ | PTR_RCV | PTR_AUTOINC, ioaddr + POINTER );
+
+	/* First two words are status and packet_length */
+	status 		= inw( ioaddr + DATA_1 );
+	packet_length 	= inw( ioaddr + DATA_1 );
+
+	packet_length &= 0x07ff;  /* mask off top bits */
+
+	PRINTK2(("RCV: STATUS %4x LENGTH %4x\n", status, packet_length ));
+	/*
+	 . the packet length contains 3 extra words :
+	 . status, length, and an extra word with an odd byte .
+	*/
+	packet_length -= 6;
+
+	if ( !(status & RS_ERRORS ) ){
+		/* do stuff to make a new packet */
+		struct sk_buff  * skb;
+		byte		* data;
+
+		/* read one extra byte */
+		if ( status & RS_ODDFRAME )
+			packet_length++;
+
+		/* set multicast stats */
+		if ( status & RS_MULTICAST )
+			dev->stats.multicast++;
+
+		skb = dev_alloc_skb( packet_length + 5);
+
+		if ( skb == NULL ) {
+			printk(KERN_NOTICE CARDNAME ": Low memory, packet dropped.\n");
+			dev->stats.rx_dropped++;
+			goto done;
+		}
+
+		/*
+		 ! This should work without alignment, but it could be
+		 ! in the worse case
+		*/
+
+		skb_reserve( skb, 2 );   /* 16 bit alignment */
+
+		data = skb_put( skb, packet_length);
+
+#ifdef USE_32_BIT
+		/* QUESTION:  Like in the TX routine, do I want
+		   to send the DWORDs or the bytes first, or some
+		   mixture.  A mixture might improve already slow PIO
+		   performance  */
+		PRINTK3((" Reading %d dwords (and %d bytes)\n",
+			packet_length >> 2, packet_length & 3 ));
+		insl(ioaddr + DATA_1 , data, packet_length >> 2 );
+		/* read the left over bytes */
+		insb( ioaddr + DATA_1, data + (packet_length & 0xFFFFFC),
+			packet_length & 0x3  );
+#else
+		PRINTK3((" Reading %d words and %d byte(s)\n",
+			(packet_length >> 1 ), packet_length & 1 ));
+		insw(ioaddr + DATA_1 , data, packet_length >> 1);
+		if ( packet_length & 1 ) {
+			data += packet_length & ~1;
+			*(data++) = inb( ioaddr + DATA_1 );
+		}
+#endif
+#if	SMC_DEBUG > 2
+			print_packet( data, packet_length );
+#endif
+
+		skb->protocol = eth_type_trans(skb, dev );
+		netif_rx(skb);
+		dev->stats.rx_packets++;
+		dev->stats.rx_bytes += packet_length;
+	} else {
+		/* error ... */
+		dev->stats.rx_errors++;
+
+		if ( status & RS_ALGNERR )  dev->stats.rx_frame_errors++;
+		if ( status & (RS_TOOSHORT | RS_TOOLONG ) )
+			dev->stats.rx_length_errors++;
+		if ( status & RS_BADCRC)	dev->stats.rx_crc_errors++;
+	}
+
+done:
+	/*  error or good, tell the card to get rid of this packet */
+	outw( MC_RELEASE, ioaddr + MMU_CMD );
+}
+
+
+/*************************************************************************
+ . smc_tx
+ .
+ . Purpose:  Handle a transmit error message.   This will only be called
+ .   when an error, because of the AUTO_RELEASE mode.
+ .
+ . Algorithm:
+ .	Save pointer and packet no
+ .	Get the packet no from the top of the queue
+ .	check if it's valid ( if not, is this an error??? )
+ .	read the status word
+ .	record the error
+ .	( resend?  Not really, since we don't want old packets around )
+ .	Restore saved values
+ ************************************************************************/
+static void smc_tx( struct net_device * dev )
+{
+	int	ioaddr = dev->base_addr;
+	struct smc_local *lp = netdev_priv(dev);
+	byte saved_packet;
+	byte packet_no;
+	word tx_status;
+
+
+	/* assume bank 2  */
+
+	saved_packet = inb( ioaddr + PNR_ARR );
+	packet_no = inw( ioaddr + FIFO_PORTS );
+	packet_no &= 0x7F;
+
+	/* select this as the packet to read from */
+	outb( packet_no, ioaddr + PNR_ARR );
+
+	/* read the first word from this packet */
+	outw( PTR_AUTOINC | PTR_READ, ioaddr + POINTER );
+
+	tx_status = inw( ioaddr + DATA_1 );
+	PRINTK3((CARDNAME": TX DONE STATUS: %4x\n", tx_status));
+
+	dev->stats.tx_errors++;
+	if ( tx_status & TS_LOSTCAR ) dev->stats.tx_carrier_errors++;
+	if ( tx_status & TS_LATCOL  ) {
+		printk(KERN_DEBUG CARDNAME
+			": Late collision occurred on last xmit.\n");
+		dev->stats.tx_window_errors++;
+	}
+#if 0
+		if ( tx_status & TS_16COL ) { ... }
+#endif
+
+	if ( tx_status & TS_SUCCESS ) {
+		printk(CARDNAME": Successful packet caused interrupt\n");
+	}
+	/* re-enable transmit */
+	SMC_SELECT_BANK( 0 );
+	outw( inw( ioaddr + TCR ) | TCR_ENABLE, ioaddr + TCR );
+
+	/* kill the packet */
+	SMC_SELECT_BANK( 2 );
+	outw( MC_FREEPKT, ioaddr + MMU_CMD );
+
+	/* one less packet waiting for me */
+	lp->packets_waiting--;
+
+	outb( saved_packet, ioaddr + PNR_ARR );
+}
+
+/*--------------------------------------------------------------------
+ .
+ . This is the main routine of the driver, to handle the device when
+ . it needs some attention.
+ .
+ . So:
+ .   first, save state of the chipset
+ .   branch off into routines to handle each case, and acknowledge
+ .	    each to the interrupt register
+ .   and finally restore state.
+ .
+ ---------------------------------------------------------------------*/
+
+static irqreturn_t smc_interrupt(int irq, void * dev_id)
+{
+	struct net_device *dev 	= dev_id;
+	int ioaddr 		= dev->base_addr;
+	struct smc_local *lp = netdev_priv(dev);
+
+	byte	status;
+	word	card_stats;
+	byte	mask;
+	int	timeout;
+	/* state registers */
+	word	saved_bank;
+	word	saved_pointer;
+	int handled = 0;
+
+
+	PRINTK3((CARDNAME": SMC interrupt started\n"));
+
+	saved_bank = inw( ioaddr + BANK_SELECT );
+
+	SMC_SELECT_BANK(2);
+	saved_pointer = inw( ioaddr + POINTER );
+
+	mask = inb( ioaddr + INT_MASK );
+	/* clear all interrupts */
+	outb( 0, ioaddr + INT_MASK );
+
+
+	/* set a timeout value, so I don't stay here forever */
+	timeout = 4;
+
+	PRINTK2((KERN_WARNING CARDNAME ": MASK IS %x\n", mask));
+	do {
+		/* read the status flag, and mask it */
+		status = inb( ioaddr + INTERRUPT ) & mask;
+		if (!status )
+			break;
+
+		handled = 1;
+
+		PRINTK3((KERN_WARNING CARDNAME
+			": Handling interrupt status %x\n", status));
+
+		if (status & IM_RCV_INT) {
+			/* Got a packet(s). */
+			PRINTK2((KERN_WARNING CARDNAME
+				": Receive Interrupt\n"));
+			smc_rcv(dev);
+		} else if (status & IM_TX_INT ) {
+			PRINTK2((KERN_WARNING CARDNAME
+				": TX ERROR handled\n"));
+			smc_tx(dev);
+			outb(IM_TX_INT, ioaddr + INTERRUPT );
+		} else if (status & IM_TX_EMPTY_INT ) {
+			/* update stats */
+			SMC_SELECT_BANK( 0 );
+			card_stats = inw( ioaddr + COUNTER );
+			/* single collisions */
+			dev->stats.collisions += card_stats & 0xF;
+			card_stats >>= 4;
+			/* multiple collisions */
+			dev->stats.collisions += card_stats & 0xF;
+
+			/* these are for when linux supports these statistics */
+
+			SMC_SELECT_BANK( 2 );
+			PRINTK2((KERN_WARNING CARDNAME
+				": TX_BUFFER_EMPTY handled\n"));
+			outb( IM_TX_EMPTY_INT, ioaddr + INTERRUPT );
+			mask &= ~IM_TX_EMPTY_INT;
+			dev->stats.tx_packets += lp->packets_waiting;
+			lp->packets_waiting = 0;
+
+		} else if (status & IM_ALLOC_INT ) {
+			PRINTK2((KERN_DEBUG CARDNAME
+				": Allocation interrupt\n"));
+			/* clear this interrupt so it doesn't happen again */
+			mask &= ~IM_ALLOC_INT;
+
+			smc_hardware_send_packet( dev );
+
+			/* enable xmit interrupts based on this */
+			mask |= ( IM_TX_EMPTY_INT | IM_TX_INT );
+
+			/* and let the card send more packets to me */
+			netif_wake_queue(dev);
+
+			PRINTK2((CARDNAME": Handoff done successfully.\n"));
+		} else if (status & IM_RX_OVRN_INT ) {
+			dev->stats.rx_errors++;
+			dev->stats.rx_fifo_errors++;
+			outb( IM_RX_OVRN_INT, ioaddr + INTERRUPT );
+		} else if (status & IM_EPH_INT ) {
+			PRINTK((CARDNAME ": UNSUPPORTED: EPH INTERRUPT\n"));
+		} else if (status & IM_ERCV_INT ) {
+			PRINTK((CARDNAME ": UNSUPPORTED: ERCV INTERRUPT\n"));
+			outb( IM_ERCV_INT, ioaddr + INTERRUPT );
+		}
+	} while ( timeout -- );
+
+
+	/* restore state register */
+	SMC_SELECT_BANK( 2 );
+	outb( mask, ioaddr + INT_MASK );
+
+	PRINTK3((KERN_WARNING CARDNAME ": MASK is now %x\n", mask));
+	outw( saved_pointer, ioaddr + POINTER );
+
+	SMC_SELECT_BANK( saved_bank );
+
+	PRINTK3((CARDNAME ": Interrupt done\n"));
+	return IRQ_RETVAL(handled);
+}
+
+
+/*----------------------------------------------------
+ . smc_close
+ .
+ . this makes the board clean up everything that it can
+ . and not talk to the outside world.   Caused by
+ . an 'ifconfig ethX down'
+ .
+ -----------------------------------------------------*/
+static int smc_close(struct net_device *dev)
+{
+	netif_stop_queue(dev);
+	/* clear everything */
+	smc_shutdown( dev->base_addr );
+
+	/* Update the statistics here. */
+	return 0;
+}
+
+/*-----------------------------------------------------------
+ . smc_set_multicast_list
+ .
+ . This routine will, depending on the values passed to it,
+ . either make it accept multicast packets, go into
+ . promiscuous mode ( for TCPDUMP and cousins ) or accept
+ . a select set of multicast packets
+*/
+static void smc_set_multicast_list(struct net_device *dev)
+{
+	short ioaddr = dev->base_addr;
+
+	SMC_SELECT_BANK(0);
+	if ( dev->flags & IFF_PROMISC )
+		outw( inw(ioaddr + RCR ) | RCR_PROMISC, ioaddr + RCR );
+
+/* BUG?  I never disable promiscuous mode if multicasting was turned on.
+   Now, I turn off promiscuous mode, but I don't do anything to multicasting
+   when promiscuous mode is turned on.
+*/
+
+	/* Here, I am setting this to accept all multicast packets.
+	   I don't need to zero the multicast table, because the flag is
+	   checked before the table is
+	*/
+	else if (dev->flags & IFF_ALLMULTI)
+		outw( inw(ioaddr + RCR ) | RCR_ALMUL, ioaddr + RCR );
+
+	/* We just get all multicast packets even if we only want them
+	 . from one source.  This will be changed at some future
+	 . point. */
+	else if (!netdev_mc_empty(dev)) {
+		/* support hardware multicasting */
+
+		/* be sure I get rid of flags I might have set */
+		outw( inw( ioaddr + RCR ) & ~(RCR_PROMISC | RCR_ALMUL),
+			ioaddr + RCR );
+		/* NOTE: this has to set the bank, so make sure it is the
+		   last thing called.  The bank is set to zero at the top */
+		smc_setmulticast(ioaddr, dev);
+	}
+	else  {
+		outw( inw( ioaddr + RCR ) & ~(RCR_PROMISC | RCR_ALMUL),
+			ioaddr + RCR );
+
+		/*
+		  since I'm disabling all multicast entirely, I need to
+		  clear the multicast list
+		*/
+		SMC_SELECT_BANK( 3 );
+		outw( 0, ioaddr + MULTICAST1 );
+		outw( 0, ioaddr + MULTICAST2 );
+		outw( 0, ioaddr + MULTICAST3 );
+		outw( 0, ioaddr + MULTICAST4 );
+	}
+}
+
+#ifdef MODULE
+
+static struct net_device *devSMC9194;
+MODULE_LICENSE("GPL");
+
+module_param(io, int, 0);
+module_param(irq, int, 0);
+module_param(ifport, int, 0);
+MODULE_PARM_DESC(io, "SMC 99194 I/O base address");
+MODULE_PARM_DESC(irq, "SMC 99194 IRQ number");
+MODULE_PARM_DESC(ifport, "SMC 99194 interface port (0-default, 1-TP, 2-AUI)");
+
+int __init init_module(void)
+{
+	if (io == 0)
+		printk(KERN_WARNING
+		CARDNAME": You shouldn't use auto-probing with insmod!\n" );
+
+	/* copy the parameters from insmod into the device structure */
+	devSMC9194 = smc_init(-1);
+	if (IS_ERR(devSMC9194))
+		return PTR_ERR(devSMC9194);
+	return 0;
+}
+
+void __exit cleanup_module(void)
+{
+	unregister_netdev(devSMC9194);
+	free_irq(devSMC9194->irq, devSMC9194);
+	release_region(devSMC9194->base_addr, SMC_IO_EXTENT);
+	free_netdev(devSMC9194);
+}
+
+#endif /* MODULE */
diff --git a/drivers/net/ethernet/smsc/smc9194.h b/drivers/net/ethernet/smsc/smc9194.h
new file mode 100644
index 0000000..cf69d0a
--- /dev/null
+++ b/drivers/net/ethernet/smsc/smc9194.h
@@ -0,0 +1,241 @@
+/*------------------------------------------------------------------------
+ . smc9194.h
+ . Copyright (C) 1996 by Erik Stahlman
+ .
+ . This software may be used and distributed according to the terms
+ . of the GNU General Public License, incorporated herein by reference.
+ .
+ . This file contains register information and access macros for
+ . the SMC91xxx chipset.
+ .
+ . Information contained in this file was obtained from the SMC91C94
+ . manual from SMC.  To get a copy, if you really want one, you can find
+ . information under www.smc.com in the components division.
+ . ( this thanks to advice from Donald Becker ).
+ .
+ . Authors
+ . 	Erik Stahlman				( erik@vt.edu )
+ .
+ . History
+ . 01/06/96		 Erik Stahlman   moved definitions here from main .c file
+ . 01/19/96		 Erik Stahlman	  polished this up some, and added better
+ .										  error handling
+ .
+ ---------------------------------------------------------------------------*/
+#ifndef _SMC9194_H_
+#define _SMC9194_H_
+
+/* I want some simple types */
+
+typedef unsigned char			byte;
+typedef unsigned short			word;
+typedef unsigned long int 		dword;
+
+
+/* Because of bank switching, the SMC91xxx uses only 16 I/O ports */
+
+#define SMC_IO_EXTENT	16
+
+
+/*---------------------------------------------------------------
+ .
+ . A description of the SMC registers is probably in order here,
+ . although for details, the SMC datasheet is invaluable.
+ .
+ . Basically, the chip has 4 banks of registers ( 0 to 3 ), which
+ . are accessed by writing a number into the BANK_SELECT register
+ . ( I also use a SMC_SELECT_BANK macro for this ).
+ .
+ . The banks are configured so that for most purposes, bank 2 is all
+ . that is needed for simple run time tasks.
+ -----------------------------------------------------------------------*/
+
+/*
+ . Bank Select Register:
+ .
+ .		yyyy yyyy 0000 00xx
+ .		xx 		= bank number
+ .		yyyy yyyy	= 0x33, for identification purposes.
+*/
+#define	BANK_SELECT		14
+
+/* BANK 0  */
+
+#define	TCR 		0    	/* transmit control register */
+#define TCR_ENABLE	0x0001	/* if this is 1, we can transmit */
+#define TCR_FDUPLX    	0x0800  /* receive packets sent out */
+#define TCR_STP_SQET	0x1000	/* stop transmitting if Signal quality error */
+#define	TCR_MON_CNS	0x0400	/* monitors the carrier status */
+#define	TCR_PAD_ENABLE	0x0080	/* pads short packets to 64 bytes */
+
+#define	TCR_CLEAR	0	/* do NOTHING */
+/* the normal settings for the TCR register : */
+/* QUESTION: do I want to enable padding of short packets ? */
+#define	TCR_NORMAL  	TCR_ENABLE
+
+
+#define EPH_STATUS	2
+#define ES_LINK_OK	0x4000	/* is the link integrity ok ? */
+
+#define	RCR		4
+#define RCR_SOFTRESET	0x8000 	/* resets the chip */
+#define	RCR_STRIP_CRC	0x200	/* strips CRC */
+#define RCR_ENABLE	0x100	/* IFF this is set, we can receive packets */
+#define RCR_ALMUL	0x4 	/* receive all multicast packets */
+#define	RCR_PROMISC	0x2	/* enable promiscuous mode */
+
+/* the normal settings for the RCR register : */
+#define	RCR_NORMAL	(RCR_STRIP_CRC | RCR_ENABLE)
+#define RCR_CLEAR	0x0		/* set it to a base state */
+
+#define	COUNTER		6
+#define	MIR		8
+#define	MCR		10
+/* 12 is reserved */
+
+/* BANK 1 */
+#define CONFIG			0
+#define CFG_AUI_SELECT	 	0x100
+#define	BASE			2
+#define	ADDR0			4
+#define	ADDR1			6
+#define	ADDR2			8
+#define	GENERAL			10
+#define	CONTROL			12
+#define	CTL_POWERDOWN		0x2000
+#define	CTL_LE_ENABLE		0x80
+#define	CTL_CR_ENABLE		0x40
+#define	CTL_TE_ENABLE		0x0020
+#define CTL_AUTO_RELEASE	0x0800
+#define	CTL_EPROM_ACCESS	0x0003 /* high if Eprom is being read */
+
+/* BANK 2 */
+#define MMU_CMD		0
+#define MC_BUSY		1	/* only readable bit in the register */
+#define MC_NOP		0
+#define	MC_ALLOC	0x20  	/* or with number of 256 byte packets */
+#define	MC_RESET	0x40
+#define	MC_REMOVE	0x60  	/* remove the current rx packet */
+#define MC_RELEASE  	0x80  	/* remove and release the current rx packet */
+#define MC_FREEPKT  	0xA0  	/* Release packet in PNR register */
+#define MC_ENQUEUE	0xC0 	/* Enqueue the packet for transmit */
+
+#define	PNR_ARR		2
+#define FIFO_PORTS	4
+
+#define FP_RXEMPTY  0x8000
+#define FP_TXEMPTY  0x80
+
+#define	POINTER		6
+#define PTR_READ	0x2000
+#define	PTR_RCV		0x8000
+#define	PTR_AUTOINC 	0x4000
+#define PTR_AUTO_INC	0x0040
+
+#define	DATA_1		8
+#define	DATA_2		10
+#define	INTERRUPT	12
+
+#define INT_MASK	13
+#define IM_RCV_INT	0x1
+#define	IM_TX_INT	0x2
+#define	IM_TX_EMPTY_INT	0x4
+#define	IM_ALLOC_INT	0x8
+#define	IM_RX_OVRN_INT	0x10
+#define	IM_EPH_INT	0x20
+#define	IM_ERCV_INT	0x40 /* not on SMC9192 */
+
+/* BANK 3 */
+#define	MULTICAST1	0
+#define	MULTICAST2	2
+#define	MULTICAST3	4
+#define	MULTICAST4	6
+#define	MGMT		8
+#define	REVISION	10 /* ( hi: chip id   low: rev # ) */
+
+
+/* this is NOT on SMC9192 */
+#define	ERCV		12
+
+#define CHIP_9190	3
+#define CHIP_9194	4
+#define CHIP_9195	5
+#define CHIP_91100	7
+
+static const char * chip_ids[ 15 ] =  {
+	NULL, NULL, NULL,
+	/* 3 */ "SMC91C90/91C92",
+	/* 4 */ "SMC91C94",
+	/* 5 */ "SMC91C95",
+	NULL,
+	/* 7 */ "SMC91C100",
+	/* 8 */ "SMC91C100FD",
+	NULL, NULL, NULL,
+	NULL, NULL, NULL};
+
+/*
+ . Transmit status bits
+*/
+#define TS_SUCCESS 0x0001
+#define TS_LOSTCAR 0x0400
+#define TS_LATCOL  0x0200
+#define TS_16COL   0x0010
+
+/*
+ . Receive status bits
+*/
+#define RS_ALGNERR	0x8000
+#define RS_BADCRC	0x2000
+#define RS_ODDFRAME	0x1000
+#define RS_TOOLONG	0x0800
+#define RS_TOOSHORT	0x0400
+#define RS_MULTICAST	0x0001
+#define RS_ERRORS	(RS_ALGNERR | RS_BADCRC | RS_TOOLONG | RS_TOOSHORT)
+
+static const char * interfaces[ 2 ] = { "TP", "AUI" };
+
+/*-------------------------------------------------------------------------
+ .  I define some macros to make it easier to do somewhat common
+ . or slightly complicated, repeated tasks.
+ --------------------------------------------------------------------------*/
+
+/* select a register bank, 0 to 3  */
+
+#define SMC_SELECT_BANK(x)  { outw( x, ioaddr + BANK_SELECT ); }
+
+/* define a small delay for the reset */
+#define SMC_DELAY() { inw( ioaddr + RCR );\
+			inw( ioaddr + RCR );\
+			inw( ioaddr + RCR );  }
+
+/* this enables an interrupt in the interrupt mask register */
+#define SMC_ENABLE_INT(x) {\
+		unsigned char mask;\
+		SMC_SELECT_BANK(2);\
+		mask = inb( ioaddr + INT_MASK );\
+		mask |= (x);\
+		outb( mask, ioaddr + INT_MASK ); \
+}
+
+/* this disables an interrupt from the interrupt mask register */
+
+#define SMC_DISABLE_INT(x) {\
+		unsigned char mask;\
+		SMC_SELECT_BANK(2);\
+		mask = inb( ioaddr + INT_MASK );\
+		mask &= ~(x);\
+		outb( mask, ioaddr + INT_MASK ); \
+}
+
+/*----------------------------------------------------------------------
+ . Define the interrupts that I want to receive from the card
+ .
+ . I want:
+ .  IM_EPH_INT, for nasty errors
+ .  IM_RCV_INT, for happy received packets
+ .  IM_RX_OVRN_INT, because I have to kick the receiver
+ --------------------------------------------------------------------------*/
+#define SMC_INTERRUPT_MASK   (IM_EPH_INT | IM_RX_OVRN_INT | IM_RCV_INT)
+
+#endif  /* _SMC_9194_H_ */
+
diff --git a/drivers/net/ethernet/smsc/smc91c92_cs.c b/drivers/net/ethernet/smsc/smc91c92_cs.c
new file mode 100644
index 0000000..cffbc03
--- /dev/null
+++ b/drivers/net/ethernet/smsc/smc91c92_cs.c
@@ -0,0 +1,2070 @@
+/*======================================================================
+
+    A PCMCIA ethernet driver for SMC91c92-based cards.
+
+    This driver supports Megahertz PCMCIA ethernet cards; and
+    Megahertz, Motorola, Ositech, and Psion Dacom ethernet/modem
+    multifunction cards.
+
+    Copyright (C) 1999 David A. Hinds -- dahinds@users.sourceforge.net
+
+    smc91c92_cs.c 1.122 2002/10/25 06:26:39
+
+    This driver contains code written by Donald Becker
+    (becker@scyld.com), Rowan Hughes (x-csrdh@jcu.edu.au),
+    David Hinds (dahinds@users.sourceforge.net), and Erik Stahlman
+    (erik@vt.edu).  Donald wrote the SMC 91c92 code using parts of
+    Erik's SMC 91c94 driver.  Rowan wrote a similar driver, and I've
+    incorporated some parts of his driver here.  I (Dave) wrote most
+    of the PCMCIA glue code, and the Ositech support code.  Kelly
+    Stephens (kstephen@holli.com) added support for the Motorola
+    Mariner, with help from Allen Brost.
+
+    This software may be used and distributed according to the terms of
+    the GNU General Public License, incorporated herein by reference.
+
+======================================================================*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/crc32.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/if_arp.h>
+#include <linux/ioport.h>
+#include <linux/ethtool.h>
+#include <linux/mii.h>
+#include <linux/jiffies.h>
+#include <linux/firmware.h>
+
+#include <pcmcia/cistpl.h>
+#include <pcmcia/cisreg.h>
+#include <pcmcia/ciscode.h>
+#include <pcmcia/ds.h>
+#include <pcmcia/ss.h>
+
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+
+/*====================================================================*/
+
+static const char *if_names[] = { "auto", "10baseT", "10base2"};
+
+/* Firmware name */
+#define FIRMWARE_NAME		"ositech/Xilinx7OD.bin"
+
+/* Module parameters */
+
+MODULE_DESCRIPTION("SMC 91c92 series PCMCIA ethernet driver");
+MODULE_LICENSE("GPL");
+MODULE_FIRMWARE(FIRMWARE_NAME);
+
+#define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0)
+
+/*
+  Transceiver/media type.
+   0 = auto
+   1 = 10baseT (and autoselect if #define AUTOSELECT),
+   2 = AUI/10base2,
+*/
+INT_MODULE_PARM(if_port, 0);
+
+
+#define DRV_NAME	"smc91c92_cs"
+#define DRV_VERSION	"1.123"
+
+/*====================================================================*/
+
+/* Operational parameter that usually are not changed. */
+
+/* Time in jiffies before concluding Tx hung */
+#define TX_TIMEOUT		((400*HZ)/1000)
+
+/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
+#define INTR_WORK		4
+
+/* Times to check the check the chip before concluding that it doesn't
+   currently have room for another Tx packet. */
+#define MEMORY_WAIT_TIME       	8
+
+struct smc_private {
+	struct pcmcia_device	*p_dev;
+    spinlock_t			lock;
+    u_short			manfid;
+    u_short			cardid;
+
+    struct sk_buff		*saved_skb;
+    int				packets_waiting;
+    void			__iomem *base;
+    u_short			cfg;
+    struct timer_list		media;
+    int				watchdog, tx_err;
+    u_short			media_status;
+    u_short			fast_poll;
+    u_short			link_status;
+    struct mii_if_info		mii_if;
+    int				duplex;
+    int				rx_ovrn;
+};
+
+/* Special definitions for Megahertz multifunction cards */
+#define MEGAHERTZ_ISR		0x0380
+
+/* Special function registers for Motorola Mariner */
+#define MOT_LAN			0x0000
+#define MOT_UART		0x0020
+#define MOT_EEPROM		0x20
+
+#define MOT_NORMAL \
+(COR_LEVEL_REQ | COR_FUNC_ENA | COR_ADDR_DECODE | COR_IREQ_ENA)
+
+/* Special function registers for Ositech cards */
+#define OSITECH_AUI_CTL		0x0c
+#define OSITECH_PWRDOWN		0x0d
+#define OSITECH_RESET		0x0e
+#define OSITECH_ISR		0x0f
+#define OSITECH_AUI_PWR		0x0c
+#define OSITECH_RESET_ISR	0x0e
+
+#define OSI_AUI_PWR		0x40
+#define OSI_LAN_PWRDOWN		0x02
+#define OSI_MODEM_PWRDOWN	0x01
+#define OSI_LAN_RESET		0x02
+#define OSI_MODEM_RESET		0x01
+
+/* Symbolic constants for the SMC91c9* series chips, from Erik Stahlman. */
+#define	BANK_SELECT		14		/* Window select register. */
+#define SMC_SELECT_BANK(x)  { outw(x, ioaddr + BANK_SELECT); }
+
+/* Bank 0 registers. */
+#define	TCR 		0	/* transmit control register */
+#define	 TCR_CLEAR	0	/* do NOTHING */
+#define  TCR_ENABLE	0x0001	/* if this is 1, we can transmit */
+#define	 TCR_PAD_EN	0x0080	/* pads short packets to 64 bytes */
+#define  TCR_MONCSN	0x0400  /* Monitor Carrier. */
+#define  TCR_FDUPLX	0x0800  /* Full duplex mode. */
+#define	 TCR_NORMAL TCR_ENABLE | TCR_PAD_EN
+
+#define EPH		2	/* Ethernet Protocol Handler report. */
+#define  EPH_TX_SUC	0x0001
+#define  EPH_SNGLCOL	0x0002
+#define  EPH_MULCOL	0x0004
+#define  EPH_LTX_MULT	0x0008
+#define  EPH_16COL	0x0010
+#define  EPH_SQET	0x0020
+#define  EPH_LTX_BRD	0x0040
+#define  EPH_TX_DEFR	0x0080
+#define  EPH_LAT_COL	0x0200
+#define  EPH_LOST_CAR	0x0400
+#define  EPH_EXC_DEF	0x0800
+#define  EPH_CTR_ROL	0x1000
+#define  EPH_RX_OVRN	0x2000
+#define  EPH_LINK_OK	0x4000
+#define  EPH_TX_UNRN	0x8000
+#define MEMINFO		8	/* Memory Information Register */
+#define MEMCFG		10	/* Memory Configuration Register */
+
+/* Bank 1 registers. */
+#define CONFIG			0
+#define  CFG_MII_SELECT		0x8000	/* 91C100 only */
+#define  CFG_NO_WAIT		0x1000
+#define  CFG_FULL_STEP		0x0400
+#define  CFG_SET_SQLCH		0x0200
+#define  CFG_AUI_SELECT	 	0x0100
+#define  CFG_16BIT		0x0080
+#define  CFG_DIS_LINK		0x0040
+#define  CFG_STATIC		0x0030
+#define  CFG_IRQ_SEL_1		0x0004
+#define  CFG_IRQ_SEL_0		0x0002
+#define BASE_ADDR		2
+#define	ADDR0			4
+#define	GENERAL			10
+#define	CONTROL			12
+#define  CTL_STORE		0x0001
+#define  CTL_RELOAD		0x0002
+#define  CTL_EE_SELECT		0x0004
+#define  CTL_TE_ENABLE		0x0020
+#define  CTL_CR_ENABLE		0x0040
+#define  CTL_LE_ENABLE		0x0080
+#define  CTL_AUTO_RELEASE	0x0800
+#define	 CTL_POWERDOWN		0x2000
+
+/* Bank 2 registers. */
+#define MMU_CMD		0
+#define	 MC_ALLOC	0x20  	/* or with number of 256 byte packets */
+#define	 MC_RESET	0x40
+#define  MC_RELEASE  	0x80  	/* remove and release the current rx packet */
+#define  MC_FREEPKT  	0xA0  	/* Release packet in PNR register */
+#define  MC_ENQUEUE	0xC0 	/* Enqueue the packet for transmit */
+#define	PNR_ARR		2
+#define FIFO_PORTS	4
+#define  FP_RXEMPTY	0x8000
+#define	POINTER		6
+#define  PTR_AUTO_INC	0x0040
+#define  PTR_READ	0x2000
+#define	 PTR_AUTOINC 	0x4000
+#define	 PTR_RCV	0x8000
+#define	DATA_1		8
+#define	INTERRUPT	12
+#define  IM_RCV_INT		0x1
+#define	 IM_TX_INT		0x2
+#define	 IM_TX_EMPTY_INT	0x4
+#define	 IM_ALLOC_INT		0x8
+#define	 IM_RX_OVRN_INT		0x10
+#define	 IM_EPH_INT		0x20
+
+#define	RCR		4
+enum RxCfg { RxAllMulti = 0x0004, RxPromisc = 0x0002,
+	     RxEnable = 0x0100, RxStripCRC = 0x0200};
+#define  RCR_SOFTRESET	0x8000 	/* resets the chip */
+#define	 RCR_STRIP_CRC	0x200	/* strips CRC */
+#define  RCR_ENABLE	0x100	/* IFF this is set, we can receive packets */
+#define  RCR_ALMUL	0x4 	/* receive all multicast packets */
+#define	 RCR_PROMISC	0x2	/* enable promiscuous mode */
+
+/* the normal settings for the RCR register : */
+#define	 RCR_NORMAL	(RCR_STRIP_CRC | RCR_ENABLE)
+#define  RCR_CLEAR	0x0		/* set it to a base state */
+#define	COUNTER		6
+
+/* BANK 3 -- not the same values as in smc9194! */
+#define	MULTICAST0	0
+#define	MULTICAST2	2
+#define	MULTICAST4	4
+#define	MULTICAST6	6
+#define MGMT    	8
+#define REVISION	0x0a
+
+/* Transmit status bits. */
+#define TS_SUCCESS 0x0001
+#define TS_16COL   0x0010
+#define TS_LATCOL  0x0200
+#define TS_LOSTCAR 0x0400
+
+/* Receive status bits. */
+#define RS_ALGNERR	0x8000
+#define RS_BADCRC	0x2000
+#define RS_ODDFRAME	0x1000
+#define RS_TOOLONG	0x0800
+#define RS_TOOSHORT	0x0400
+#define RS_MULTICAST	0x0001
+#define RS_ERRORS	(RS_ALGNERR | RS_BADCRC | RS_TOOLONG | RS_TOOSHORT)
+
+#define set_bits(v, p) outw(inw(p)|(v), (p))
+#define mask_bits(v, p) outw(inw(p)&(v), (p))
+
+/*====================================================================*/
+
+static void smc91c92_detach(struct pcmcia_device *p_dev);
+static int smc91c92_config(struct pcmcia_device *link);
+static void smc91c92_release(struct pcmcia_device *link);
+
+static int smc_open(struct net_device *dev);
+static int smc_close(struct net_device *dev);
+static int smc_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
+static void smc_tx_timeout(struct net_device *dev);
+static netdev_tx_t smc_start_xmit(struct sk_buff *skb,
+					struct net_device *dev);
+static irqreturn_t smc_interrupt(int irq, void *dev_id);
+static void smc_rx(struct net_device *dev);
+static void set_rx_mode(struct net_device *dev);
+static int s9k_config(struct net_device *dev, struct ifmap *map);
+static void smc_set_xcvr(struct net_device *dev, int if_port);
+static void smc_reset(struct net_device *dev);
+static void media_check(u_long arg);
+static void mdio_sync(unsigned int addr);
+static int mdio_read(struct net_device *dev, int phy_id, int loc);
+static void mdio_write(struct net_device *dev, int phy_id, int loc, int value);
+static int smc_link_ok(struct net_device *dev);
+static const struct ethtool_ops ethtool_ops;
+
+static const struct net_device_ops smc_netdev_ops = {
+	.ndo_open		= smc_open,
+	.ndo_stop		= smc_close,
+	.ndo_start_xmit		= smc_start_xmit,
+	.ndo_tx_timeout 	= smc_tx_timeout,
+	.ndo_set_config 	= s9k_config,
+	.ndo_set_multicast_list = set_rx_mode,
+	.ndo_do_ioctl		= smc_ioctl,
+	.ndo_change_mtu		= eth_change_mtu,
+	.ndo_set_mac_address 	= eth_mac_addr,
+	.ndo_validate_addr	= eth_validate_addr,
+};
+
+static int smc91c92_probe(struct pcmcia_device *link)
+{
+    struct smc_private *smc;
+    struct net_device *dev;
+
+    dev_dbg(&link->dev, "smc91c92_attach()\n");
+
+    /* Create new ethernet device */
+    dev = alloc_etherdev(sizeof(struct smc_private));
+    if (!dev)
+	return -ENOMEM;
+    smc = netdev_priv(dev);
+    smc->p_dev = link;
+    link->priv = dev;
+
+    spin_lock_init(&smc->lock);
+
+    /* The SMC91c92-specific entries in the device structure. */
+    dev->netdev_ops = &smc_netdev_ops;
+    SET_ETHTOOL_OPS(dev, &ethtool_ops);
+    dev->watchdog_timeo = TX_TIMEOUT;
+
+    smc->mii_if.dev = dev;
+    smc->mii_if.mdio_read = mdio_read;
+    smc->mii_if.mdio_write = mdio_write;
+    smc->mii_if.phy_id_mask = 0x1f;
+    smc->mii_if.reg_num_mask = 0x1f;
+
+    return smc91c92_config(link);
+} /* smc91c92_attach */
+
+static void smc91c92_detach(struct pcmcia_device *link)
+{
+    struct net_device *dev = link->priv;
+
+    dev_dbg(&link->dev, "smc91c92_detach\n");
+
+    unregister_netdev(dev);
+
+    smc91c92_release(link);
+
+    free_netdev(dev);
+} /* smc91c92_detach */
+
+/*====================================================================*/
+
+static int cvt_ascii_address(struct net_device *dev, char *s)
+{
+    int i, j, da, c;
+
+    if (strlen(s) != 12)
+	return -1;
+    for (i = 0; i < 6; i++) {
+	da = 0;
+	for (j = 0; j < 2; j++) {
+	    c = *s++;
+	    da <<= 4;
+	    da += ((c >= '0') && (c <= '9')) ?
+		(c - '0') : ((c & 0x0f) + 9);
+	}
+	dev->dev_addr[i] = da;
+    }
+    return 0;
+}
+
+/*====================================================================
+
+    Configuration stuff for Megahertz cards
+
+    mhz_3288_power() is used to power up a 3288's ethernet chip.
+    mhz_mfc_config() handles socket setup for multifunction (1144
+    and 3288) cards.  mhz_setup() gets a card's hardware ethernet
+    address.
+
+======================================================================*/
+
+static int mhz_3288_power(struct pcmcia_device *link)
+{
+    struct net_device *dev = link->priv;
+    struct smc_private *smc = netdev_priv(dev);
+    u_char tmp;
+
+    /* Read the ISR twice... */
+    readb(smc->base+MEGAHERTZ_ISR);
+    udelay(5);
+    readb(smc->base+MEGAHERTZ_ISR);
+
+    /* Pause 200ms... */
+    mdelay(200);
+
+    /* Now read and write the COR... */
+    tmp = readb(smc->base + link->config_base + CISREG_COR);
+    udelay(5);
+    writeb(tmp, smc->base + link->config_base + CISREG_COR);
+
+    return 0;
+}
+
+static int mhz_mfc_config_check(struct pcmcia_device *p_dev, void *priv_data)
+{
+	int k;
+	p_dev->io_lines = 16;
+	p_dev->resource[1]->start = p_dev->resource[0]->start;
+	p_dev->resource[1]->end = 8;
+	p_dev->resource[1]->flags &= ~IO_DATA_PATH_WIDTH;
+	p_dev->resource[1]->flags |= IO_DATA_PATH_WIDTH_8;
+	p_dev->resource[0]->end = 16;
+	p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH;
+	p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO;
+	for (k = 0; k < 0x400; k += 0x10) {
+		if (k & 0x80)
+			continue;
+		p_dev->resource[0]->start = k ^ 0x300;
+		if (!pcmcia_request_io(p_dev))
+			return 0;
+	}
+	return -ENODEV;
+}
+
+static int mhz_mfc_config(struct pcmcia_device *link)
+{
+    struct net_device *dev = link->priv;
+    struct smc_private *smc = netdev_priv(dev);
+    unsigned int offset;
+    int i;
+
+    link->config_flags |= CONF_ENABLE_SPKR | CONF_ENABLE_IRQ |
+	    CONF_AUTO_SET_IO;
+
+    /* The Megahertz combo cards have modem-like CIS entries, so
+       we have to explicitly try a bunch of port combinations. */
+    if (pcmcia_loop_config(link, mhz_mfc_config_check, NULL))
+	    return -ENODEV;
+
+    dev->base_addr = link->resource[0]->start;
+
+    /* Allocate a memory window, for accessing the ISR */
+    link->resource[2]->flags = WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_AM|WIN_ENABLE;
+    link->resource[2]->start = link->resource[2]->end = 0;
+    i = pcmcia_request_window(link, link->resource[2], 0);
+    if (i != 0)
+	    return -ENODEV;
+
+    smc->base = ioremap(link->resource[2]->start,
+		    resource_size(link->resource[2]));
+    offset = (smc->manfid == MANFID_MOTOROLA) ? link->config_base : 0;
+    i = pcmcia_map_mem_page(link, link->resource[2], offset);
+    if ((i == 0) &&
+	(smc->manfid == MANFID_MEGAHERTZ) &&
+	(smc->cardid == PRODID_MEGAHERTZ_EM3288))
+	    mhz_3288_power(link);
+
+    return 0;
+}
+
+static int pcmcia_get_versmac(struct pcmcia_device *p_dev,
+			      tuple_t *tuple,
+			      void *priv)
+{
+	struct net_device *dev = priv;
+	cisparse_t parse;
+	u8 *buf;
+
+	if (pcmcia_parse_tuple(tuple, &parse))
+		return -EINVAL;
+
+	buf = parse.version_1.str + parse.version_1.ofs[3];
+
+	if ((parse.version_1.ns > 3) && (cvt_ascii_address(dev, buf) == 0))
+		return 0;
+
+	return -EINVAL;
+};
+
+static int mhz_setup(struct pcmcia_device *link)
+{
+    struct net_device *dev = link->priv;
+    size_t len;
+    u8 *buf;
+    int rc;
+
+    /* Read the station address from the CIS.  It is stored as the last
+       (fourth) string in the Version 1 Version/ID tuple. */
+    if ((link->prod_id[3]) &&
+	(cvt_ascii_address(dev, link->prod_id[3]) == 0))
+	    return 0;
+
+    /* Workarounds for broken cards start here. */
+    /* Ugh -- the EM1144 card has two VERS_1 tuples!?! */
+    if (!pcmcia_loop_tuple(link, CISTPL_VERS_1, pcmcia_get_versmac, dev))
+	    return 0;
+
+    /* Another possibility: for the EM3288, in a special tuple */
+    rc = -1;
+    len = pcmcia_get_tuple(link, 0x81, &buf);
+    if (buf && len >= 13) {
+	    buf[12] = '\0';
+	    if (cvt_ascii_address(dev, buf) == 0)
+		    rc = 0;
+    }
+    kfree(buf);
+
+    return rc;
+};
+
+/*======================================================================
+
+    Configuration stuff for the Motorola Mariner
+
+    mot_config() writes directly to the Mariner configuration
+    registers because the CIS is just bogus.
+
+======================================================================*/
+
+static void mot_config(struct pcmcia_device *link)
+{
+    struct net_device *dev = link->priv;
+    struct smc_private *smc = netdev_priv(dev);
+    unsigned int ioaddr = dev->base_addr;
+    unsigned int iouart = link->resource[1]->start;
+
+    /* Set UART base address and force map with COR bit 1 */
+    writeb(iouart & 0xff,        smc->base + MOT_UART + CISREG_IOBASE_0);
+    writeb((iouart >> 8) & 0xff, smc->base + MOT_UART + CISREG_IOBASE_1);
+    writeb(MOT_NORMAL,           smc->base + MOT_UART + CISREG_COR);
+
+    /* Set SMC base address and force map with COR bit 1 */
+    writeb(ioaddr & 0xff,        smc->base + MOT_LAN + CISREG_IOBASE_0);
+    writeb((ioaddr >> 8) & 0xff, smc->base + MOT_LAN + CISREG_IOBASE_1);
+    writeb(MOT_NORMAL,           smc->base + MOT_LAN + CISREG_COR);
+
+    /* Wait for things to settle down */
+    mdelay(100);
+}
+
+static int mot_setup(struct pcmcia_device *link)
+{
+    struct net_device *dev = link->priv;
+    unsigned int ioaddr = dev->base_addr;
+    int i, wait, loop;
+    u_int addr;
+
+    /* Read Ethernet address from Serial EEPROM */
+
+    for (i = 0; i < 3; i++) {
+	SMC_SELECT_BANK(2);
+	outw(MOT_EEPROM + i, ioaddr + POINTER);
+	SMC_SELECT_BANK(1);
+	outw((CTL_RELOAD | CTL_EE_SELECT), ioaddr + CONTROL);
+
+	for (loop = wait = 0; loop < 200; loop++) {
+	    udelay(10);
+	    wait = ((CTL_RELOAD | CTL_STORE) & inw(ioaddr + CONTROL));
+	    if (wait == 0) break;
+	}
+	
+	if (wait)
+	    return -1;
+	
+	addr = inw(ioaddr + GENERAL);
+	dev->dev_addr[2*i]   = addr & 0xff;
+	dev->dev_addr[2*i+1] = (addr >> 8) & 0xff;
+    }
+
+    return 0;
+}
+
+/*====================================================================*/
+
+static int smc_configcheck(struct pcmcia_device *p_dev, void *priv_data)
+{
+	p_dev->resource[0]->end = 16;
+	p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH;
+	p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO;
+
+	return pcmcia_request_io(p_dev);
+}
+
+static int smc_config(struct pcmcia_device *link)
+{
+    struct net_device *dev = link->priv;
+    int i;
+
+    link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO;
+
+    i = pcmcia_loop_config(link, smc_configcheck, NULL);
+    if (!i)
+	    dev->base_addr = link->resource[0]->start;
+
+    return i;
+}
+
+
+static int smc_setup(struct pcmcia_device *link)
+{
+    struct net_device *dev = link->priv;
+
+    /* Check for a LAN function extension tuple */
+    if (!pcmcia_get_mac_from_cis(link, dev))
+	    return 0;
+
+    /* Try the third string in the Version 1 Version/ID tuple. */
+    if (link->prod_id[2]) {
+	    if (cvt_ascii_address(dev, link->prod_id[2]) == 0)
+		    return 0;
+    }
+    return -1;
+}
+
+/*====================================================================*/
+
+static int osi_config(struct pcmcia_device *link)
+{
+    struct net_device *dev = link->priv;
+    static const unsigned int com[4] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 };
+    int i, j;
+
+    link->config_flags |= CONF_ENABLE_SPKR | CONF_ENABLE_IRQ;
+    link->resource[0]->end = 64;
+    link->resource[1]->flags |= IO_DATA_PATH_WIDTH_8;
+    link->resource[1]->end = 8;
+
+    /* Enable Hard Decode, LAN, Modem */
+    link->io_lines = 16;
+    link->config_index = 0x23;
+
+    for (i = j = 0; j < 4; j++) {
+	link->resource[1]->start = com[j];
+	i = pcmcia_request_io(link);
+	if (i == 0)
+		break;
+    }
+    if (i != 0) {
+	/* Fallback: turn off hard decode */
+	link->config_index = 0x03;
+	link->resource[1]->end = 0;
+	i = pcmcia_request_io(link);
+    }
+    dev->base_addr = link->resource[0]->start + 0x10;
+    return i;
+}
+
+static int osi_load_firmware(struct pcmcia_device *link)
+{
+	const struct firmware *fw;
+	int i, err;
+
+	err = request_firmware(&fw, FIRMWARE_NAME, &link->dev);
+	if (err) {
+		pr_err("Failed to load firmware \"%s\"\n", FIRMWARE_NAME);
+		return err;
+	}
+
+	/* Download the Seven of Diamonds firmware */
+	for (i = 0; i < fw->size; i++) {
+	    outb(fw->data[i], link->resource[0]->start + 2);
+	    udelay(50);
+	}
+	release_firmware(fw);
+	return err;
+}
+
+static int pcmcia_osi_mac(struct pcmcia_device *p_dev,
+			  tuple_t *tuple,
+			  void *priv)
+{
+	struct net_device *dev = priv;
+	int i;
+
+	if (tuple->TupleDataLen < 8)
+		return -EINVAL;
+	if (tuple->TupleData[0] != 0x04)
+		return -EINVAL;
+	for (i = 0; i < 6; i++)
+		dev->dev_addr[i] = tuple->TupleData[i+2];
+	return 0;
+};
+
+
+static int osi_setup(struct pcmcia_device *link, u_short manfid, u_short cardid)
+{
+    struct net_device *dev = link->priv;
+    int rc;
+
+    /* Read the station address from tuple 0x90, subtuple 0x04 */
+    if (pcmcia_loop_tuple(link, 0x90, pcmcia_osi_mac, dev))
+	    return -1;
+
+    if (((manfid == MANFID_OSITECH) &&
+	 (cardid == PRODID_OSITECH_SEVEN)) ||
+	((manfid == MANFID_PSION) &&
+	 (cardid == PRODID_PSION_NET100))) {
+	rc = osi_load_firmware(link);
+	if (rc)
+		return rc;
+    } else if (manfid == MANFID_OSITECH) {
+	/* Make sure both functions are powered up */
+	set_bits(0x300, link->resource[0]->start + OSITECH_AUI_PWR);
+	/* Now, turn on the interrupt for both card functions */
+	set_bits(0x300, link->resource[0]->start + OSITECH_RESET_ISR);
+	dev_dbg(&link->dev, "AUI/PWR: %4.4x RESET/ISR: %4.4x\n",
+	      inw(link->resource[0]->start + OSITECH_AUI_PWR),
+	      inw(link->resource[0]->start + OSITECH_RESET_ISR));
+    }
+    return 0;
+}
+
+static int smc91c92_suspend(struct pcmcia_device *link)
+{
+	struct net_device *dev = link->priv;
+
+	if (link->open)
+		netif_device_detach(dev);
+
+	return 0;
+}
+
+static int smc91c92_resume(struct pcmcia_device *link)
+{
+	struct net_device *dev = link->priv;
+	struct smc_private *smc = netdev_priv(dev);
+	int i;
+
+	if ((smc->manfid == MANFID_MEGAHERTZ) &&
+	    (smc->cardid == PRODID_MEGAHERTZ_EM3288))
+		mhz_3288_power(link);
+	if (smc->manfid == MANFID_MOTOROLA)
+		mot_config(link);
+	if ((smc->manfid == MANFID_OSITECH) &&
+	    (smc->cardid != PRODID_OSITECH_SEVEN)) {
+		/* Power up the card and enable interrupts */
+		set_bits(0x0300, dev->base_addr-0x10+OSITECH_AUI_PWR);
+		set_bits(0x0300, dev->base_addr-0x10+OSITECH_RESET_ISR);
+	}
+	if (((smc->manfid == MANFID_OSITECH) &&
+	     (smc->cardid == PRODID_OSITECH_SEVEN)) ||
+	    ((smc->manfid == MANFID_PSION) &&
+	     (smc->cardid == PRODID_PSION_NET100))) {
+		i = osi_load_firmware(link);
+		if (i) {
+			pr_err("smc91c92_cs: Failed to load firmware\n");
+			return i;
+		}
+	}
+	if (link->open) {
+		smc_reset(dev);
+		netif_device_attach(dev);
+	}
+
+	return 0;
+}
+
+
+/*======================================================================
+
+    This verifies that the chip is some SMC91cXX variant, and returns
+    the revision code if successful.  Otherwise, it returns -ENODEV.
+
+======================================================================*/
+
+static int check_sig(struct pcmcia_device *link)
+{
+    struct net_device *dev = link->priv;
+    unsigned int ioaddr = dev->base_addr;
+    int width;
+    u_short s;
+
+    SMC_SELECT_BANK(1);
+    if (inw(ioaddr + BANK_SELECT) >> 8 != 0x33) {
+	/* Try powering up the chip */
+	outw(0, ioaddr + CONTROL);
+	mdelay(55);
+    }
+
+    /* Try setting bus width */
+    width = (link->resource[0]->flags == IO_DATA_PATH_WIDTH_AUTO);
+    s = inb(ioaddr + CONFIG);
+    if (width)
+	s |= CFG_16BIT;
+    else
+	s &= ~CFG_16BIT;
+    outb(s, ioaddr + CONFIG);
+
+    /* Check Base Address Register to make sure bus width is OK */
+    s = inw(ioaddr + BASE_ADDR);
+    if ((inw(ioaddr + BANK_SELECT) >> 8 == 0x33) &&
+	((s >> 8) != (s & 0xff))) {
+	SMC_SELECT_BANK(3);
+	s = inw(ioaddr + REVISION);
+	return s & 0xff;
+    }
+
+    if (width) {
+	    pr_info("using 8-bit IO window\n");
+
+	    smc91c92_suspend(link);
+	    pcmcia_fixup_iowidth(link);
+	    smc91c92_resume(link);
+	    return check_sig(link);
+    }
+    return -ENODEV;
+}
+
+static int smc91c92_config(struct pcmcia_device *link)
+{
+    struct net_device *dev = link->priv;
+    struct smc_private *smc = netdev_priv(dev);
+    char *name;
+    int i, rev, j = 0;
+    unsigned int ioaddr;
+    u_long mir;
+
+    dev_dbg(&link->dev, "smc91c92_config\n");
+
+    smc->manfid = link->manf_id;
+    smc->cardid = link->card_id;
+
+    if ((smc->manfid == MANFID_OSITECH) &&
+	(smc->cardid != PRODID_OSITECH_SEVEN)) {
+	i = osi_config(link);
+    } else if ((smc->manfid == MANFID_MOTOROLA) ||
+	       ((smc->manfid == MANFID_MEGAHERTZ) &&
+		((smc->cardid == PRODID_MEGAHERTZ_VARIOUS) ||
+		 (smc->cardid == PRODID_MEGAHERTZ_EM3288)))) {
+	i = mhz_mfc_config(link);
+    } else {
+	i = smc_config(link);
+    }
+    if (i)
+	    goto config_failed;
+
+    i = pcmcia_request_irq(link, smc_interrupt);
+    if (i)
+	    goto config_failed;
+    i = pcmcia_enable_device(link);
+    if (i)
+	    goto config_failed;
+
+    if (smc->manfid == MANFID_MOTOROLA)
+	mot_config(link);
+
+    dev->irq = link->irq;
+
+    if ((if_port >= 0) && (if_port <= 2))
+	dev->if_port = if_port;
+    else
+	dev_notice(&link->dev, "invalid if_port requested\n");
+
+    switch (smc->manfid) {
+    case MANFID_OSITECH:
+    case MANFID_PSION:
+	i = osi_setup(link, smc->manfid, smc->cardid); break;
+    case MANFID_SMC:
+    case MANFID_NEW_MEDIA:
+	i = smc_setup(link); break;
+    case 0x128: /* For broken Megahertz cards */
+    case MANFID_MEGAHERTZ:
+	i = mhz_setup(link); break;
+    case MANFID_MOTOROLA:
+    default: /* get the hw address from EEPROM */
+	i = mot_setup(link); break;
+    }
+
+    if (i != 0) {
+	dev_notice(&link->dev, "Unable to find hardware address.\n");
+	goto config_failed;
+    }
+
+    smc->duplex = 0;
+    smc->rx_ovrn = 0;
+
+    rev = check_sig(link);
+    name = "???";
+    if (rev > 0)
+	switch (rev >> 4) {
+	case 3: name = "92"; break;
+	case 4: name = ((rev & 15) >= 6) ? "96" : "94"; break;
+	case 5: name = "95"; break;
+	case 7: name = "100"; break;
+	case 8: name = "100-FD"; break;
+	case 9: name = "110"; break;
+	}
+
+    ioaddr = dev->base_addr;
+    if (rev > 0) {
+	u_long mcr;
+	SMC_SELECT_BANK(0);
+	mir = inw(ioaddr + MEMINFO) & 0xff;
+	if (mir == 0xff) mir++;
+	/* Get scale factor for memory size */
+	mcr = ((rev >> 4) > 3) ? inw(ioaddr + MEMCFG) : 0x0200;
+	mir *= 128 * (1<<((mcr >> 9) & 7));
+	SMC_SELECT_BANK(1);
+	smc->cfg = inw(ioaddr + CONFIG) & ~CFG_AUI_SELECT;
+	smc->cfg |= CFG_NO_WAIT | CFG_16BIT | CFG_STATIC;
+	if (smc->manfid == MANFID_OSITECH)
+	    smc->cfg |= CFG_IRQ_SEL_1 | CFG_IRQ_SEL_0;
+	if ((rev >> 4) >= 7)
+	    smc->cfg |= CFG_MII_SELECT;
+    } else
+	mir = 0;
+
+    if (smc->cfg & CFG_MII_SELECT) {
+	SMC_SELECT_BANK(3);
+
+	for (i = 0; i < 32; i++) {
+	    j = mdio_read(dev, i, 1);
+	    if ((j != 0) && (j != 0xffff)) break;
+	}
+	smc->mii_if.phy_id = (i < 32) ? i : -1;
+
+	SMC_SELECT_BANK(0);
+    }
+
+    SET_NETDEV_DEV(dev, &link->dev);
+
+    if (register_netdev(dev) != 0) {
+	dev_err(&link->dev, "register_netdev() failed\n");
+	goto config_undo;
+    }
+
+    netdev_info(dev, "smc91c%s rev %d: io %#3lx, irq %d, hw_addr %pM\n",
+		name, (rev & 0x0f), dev->base_addr, dev->irq, dev->dev_addr);
+
+    if (rev > 0) {
+	if (mir & 0x3ff)
+	    netdev_info(dev, "  %lu byte", mir);
+	else
+	    netdev_info(dev, "  %lu kb", mir>>10);
+	pr_cont(" buffer, %s xcvr\n",
+		(smc->cfg & CFG_MII_SELECT) ? "MII" : if_names[dev->if_port]);
+    }
+
+    if (smc->cfg & CFG_MII_SELECT) {
+	if (smc->mii_if.phy_id != -1) {
+	    netdev_dbg(dev, "  MII transceiver at index %d, status %x\n",
+		       smc->mii_if.phy_id, j);
+	} else {
+	    netdev_notice(dev, "  No MII transceivers found!\n");
+	}
+    }
+    return 0;
+
+config_undo:
+    unregister_netdev(dev);
+config_failed:
+    smc91c92_release(link);
+    free_netdev(dev);
+    return -ENODEV;
+} /* smc91c92_config */
+
+static void smc91c92_release(struct pcmcia_device *link)
+{
+	dev_dbg(&link->dev, "smc91c92_release\n");
+	if (link->resource[2]->end) {
+		struct net_device *dev = link->priv;
+		struct smc_private *smc = netdev_priv(dev);
+		iounmap(smc->base);
+	}
+	pcmcia_disable_device(link);
+}
+
+/*======================================================================
+
+    MII interface support for SMC91cXX based cards
+======================================================================*/
+
+#define MDIO_SHIFT_CLK		0x04
+#define MDIO_DATA_OUT		0x01
+#define MDIO_DIR_WRITE		0x08
+#define MDIO_DATA_WRITE0	(MDIO_DIR_WRITE)
+#define MDIO_DATA_WRITE1	(MDIO_DIR_WRITE | MDIO_DATA_OUT)
+#define MDIO_DATA_READ		0x02
+
+static void mdio_sync(unsigned int addr)
+{
+    int bits;
+    for (bits = 0; bits < 32; bits++) {
+	outb(MDIO_DATA_WRITE1, addr);
+	outb(MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, addr);
+    }
+}
+
+static int mdio_read(struct net_device *dev, int phy_id, int loc)
+{
+    unsigned int addr = dev->base_addr + MGMT;
+    u_int cmd = (0x06<<10)|(phy_id<<5)|loc;
+    int i, retval = 0;
+
+    mdio_sync(addr);
+    for (i = 13; i >= 0; i--) {
+	int dat = (cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0;
+	outb(dat, addr);
+	outb(dat | MDIO_SHIFT_CLK, addr);
+    }
+    for (i = 19; i > 0; i--) {
+	outb(0, addr);
+	retval = (retval << 1) | ((inb(addr) & MDIO_DATA_READ) != 0);
+	outb(MDIO_SHIFT_CLK, addr);
+    }
+    return (retval>>1) & 0xffff;
+}
+
+static void mdio_write(struct net_device *dev, int phy_id, int loc, int value)
+{
+    unsigned int addr = dev->base_addr + MGMT;
+    u_int cmd = (0x05<<28)|(phy_id<<23)|(loc<<18)|(1<<17)|value;
+    int i;
+
+    mdio_sync(addr);
+    for (i = 31; i >= 0; i--) {
+	int dat = (cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0;
+	outb(dat, addr);
+	outb(dat | MDIO_SHIFT_CLK, addr);
+    }
+    for (i = 1; i >= 0; i--) {
+	outb(0, addr);
+	outb(MDIO_SHIFT_CLK, addr);
+    }
+}
+
+/*======================================================================
+
+    The driver core code, most of which should be common with a
+    non-PCMCIA implementation.
+
+======================================================================*/
+
+#ifdef PCMCIA_DEBUG
+static void smc_dump(struct net_device *dev)
+{
+    unsigned int ioaddr = dev->base_addr;
+    u_short i, w, save;
+    save = inw(ioaddr + BANK_SELECT);
+    for (w = 0; w < 4; w++) {
+	SMC_SELECT_BANK(w);
+	netdev_printk(KERN_DEBUG, dev, "bank %d: ", w);
+	for (i = 0; i < 14; i += 2)
+	    pr_cont(" %04x", inw(ioaddr + i));
+	pr_cont("\n");
+    }
+    outw(save, ioaddr + BANK_SELECT);
+}
+#endif
+
+static int smc_open(struct net_device *dev)
+{
+    struct smc_private *smc = netdev_priv(dev);
+    struct pcmcia_device *link = smc->p_dev;
+
+    dev_dbg(&link->dev, "%s: smc_open(%p), ID/Window %4.4x.\n",
+	  dev->name, dev, inw(dev->base_addr + BANK_SELECT));
+#ifdef PCMCIA_DEBUG
+    smc_dump(dev);
+#endif
+
+    /* Check that the PCMCIA card is still here. */
+    if (!pcmcia_dev_present(link))
+	return -ENODEV;
+    /* Physical device present signature. */
+    if (check_sig(link) < 0) {
+	netdev_info(dev, "Yikes!  Bad chip signature!\n");
+	return -ENODEV;
+    }
+    link->open++;
+
+    netif_start_queue(dev);
+    smc->saved_skb = NULL;
+    smc->packets_waiting = 0;
+
+    smc_reset(dev);
+    init_timer(&smc->media);
+    smc->media.function = media_check;
+    smc->media.data = (u_long) dev;
+    smc->media.expires = jiffies + HZ;
+    add_timer(&smc->media);
+
+    return 0;
+} /* smc_open */
+
+/*====================================================================*/
+
+static int smc_close(struct net_device *dev)
+{
+    struct smc_private *smc = netdev_priv(dev);
+    struct pcmcia_device *link = smc->p_dev;
+    unsigned int ioaddr = dev->base_addr;
+
+    dev_dbg(&link->dev, "%s: smc_close(), status %4.4x.\n",
+	  dev->name, inw(ioaddr + BANK_SELECT));
+
+    netif_stop_queue(dev);
+
+    /* Shut off all interrupts, and turn off the Tx and Rx sections.
+       Don't bother to check for chip present. */
+    SMC_SELECT_BANK(2);	/* Nominally paranoia, but do no assume... */
+    outw(0, ioaddr + INTERRUPT);
+    SMC_SELECT_BANK(0);
+    mask_bits(0xff00, ioaddr + RCR);
+    mask_bits(0xff00, ioaddr + TCR);
+
+    /* Put the chip into power-down mode. */
+    SMC_SELECT_BANK(1);
+    outw(CTL_POWERDOWN, ioaddr + CONTROL );
+
+    link->open--;
+    del_timer_sync(&smc->media);
+
+    return 0;
+} /* smc_close */
+
+/*======================================================================
+
+   Transfer a packet to the hardware and trigger the packet send.
+   This may be called at either from either the Tx queue code
+   or the interrupt handler.
+
+======================================================================*/
+
+static void smc_hardware_send_packet(struct net_device * dev)
+{
+    struct smc_private *smc = netdev_priv(dev);
+    struct sk_buff *skb = smc->saved_skb;
+    unsigned int ioaddr = dev->base_addr;
+    u_char packet_no;
+
+    if (!skb) {
+	netdev_err(dev, "In XMIT with no packet to send\n");
+	return;
+    }
+
+    /* There should be a packet slot waiting. */
+    packet_no = inw(ioaddr + PNR_ARR) >> 8;
+    if (packet_no & 0x80) {
+	/* If not, there is a hardware problem!  Likely an ejected card. */
+	netdev_warn(dev, "hardware Tx buffer allocation failed, status %#2.2x\n",
+		    packet_no);
+	dev_kfree_skb_irq(skb);
+	smc->saved_skb = NULL;
+	netif_start_queue(dev);
+	return;
+    }
+
+    dev->stats.tx_bytes += skb->len;
+    /* The card should use the just-allocated buffer. */
+    outw(packet_no, ioaddr + PNR_ARR);
+    /* point to the beginning of the packet */
+    outw(PTR_AUTOINC , ioaddr + POINTER);
+
+    /* Send the packet length (+6 for status, length and ctl byte)
+       and the status word (set to zeros). */
+    {
+	u_char *buf = skb->data;
+	u_int length = skb->len; /* The chip will pad to ethernet min. */
+
+	netdev_dbg(dev, "Trying to xmit packet of length %d\n", length);
+	
+	/* send the packet length: +6 for status word, length, and ctl */
+	outw(0, ioaddr + DATA_1);
+	outw(length + 6, ioaddr + DATA_1);
+	outsw(ioaddr + DATA_1, buf, length >> 1);
+	
+	/* The odd last byte, if there is one, goes in the control word. */
+	outw((length & 1) ? 0x2000 | buf[length-1] : 0, ioaddr + DATA_1);
+    }
+
+    /* Enable the Tx interrupts, both Tx (TxErr) and TxEmpty. */
+    outw(((IM_TX_INT|IM_TX_EMPTY_INT)<<8) |
+	 (inw(ioaddr + INTERRUPT) & 0xff00),
+	 ioaddr + INTERRUPT);
+
+    /* The chip does the rest of the work. */
+    outw(MC_ENQUEUE , ioaddr + MMU_CMD);
+
+    smc->saved_skb = NULL;
+    dev_kfree_skb_irq(skb);
+    dev->trans_start = jiffies;
+    netif_start_queue(dev);
+}
+
+/*====================================================================*/
+
+static void smc_tx_timeout(struct net_device *dev)
+{
+    struct smc_private *smc = netdev_priv(dev);
+    unsigned int ioaddr = dev->base_addr;
+
+    netdev_notice(dev, "transmit timed out, Tx_status %2.2x status %4.4x.\n",
+		  inw(ioaddr)&0xff, inw(ioaddr + 2));
+    dev->stats.tx_errors++;
+    smc_reset(dev);
+    dev->trans_start = jiffies; /* prevent tx timeout */
+    smc->saved_skb = NULL;
+    netif_wake_queue(dev);
+}
+
+static netdev_tx_t smc_start_xmit(struct sk_buff *skb,
+					struct net_device *dev)
+{
+    struct smc_private *smc = netdev_priv(dev);
+    unsigned int ioaddr = dev->base_addr;
+    u_short num_pages;
+    short time_out, ir;
+    unsigned long flags;
+
+    netif_stop_queue(dev);
+
+    netdev_dbg(dev, "smc_start_xmit(length = %d) called, status %04x\n",
+	       skb->len, inw(ioaddr + 2));
+
+    if (smc->saved_skb) {
+	/* THIS SHOULD NEVER HAPPEN. */
+	dev->stats.tx_aborted_errors++;
+	netdev_printk(KERN_DEBUG, dev,
+		      "Internal error -- sent packet while busy\n");
+	return NETDEV_TX_BUSY;
+    }
+    smc->saved_skb = skb;
+
+    num_pages = skb->len >> 8;
+
+    if (num_pages > 7) {
+	netdev_err(dev, "Far too big packet error: %d pages\n", num_pages);
+	dev_kfree_skb (skb);
+	smc->saved_skb = NULL;
+	dev->stats.tx_dropped++;
+	return NETDEV_TX_OK;		/* Do not re-queue this packet. */
+    }
+    /* A packet is now waiting. */
+    smc->packets_waiting++;
+
+    spin_lock_irqsave(&smc->lock, flags);
+    SMC_SELECT_BANK(2);	/* Paranoia, we should always be in window 2 */
+
+    /* need MC_RESET to keep the memory consistent. errata? */
+    if (smc->rx_ovrn) {
+	outw(MC_RESET, ioaddr + MMU_CMD);
+	smc->rx_ovrn = 0;
+    }
+
+    /* Allocate the memory; send the packet now if we win. */
+    outw(MC_ALLOC | num_pages, ioaddr + MMU_CMD);
+    for (time_out = MEMORY_WAIT_TIME; time_out >= 0; time_out--) {
+	ir = inw(ioaddr+INTERRUPT);
+	if (ir & IM_ALLOC_INT) {
+	    /* Acknowledge the interrupt, send the packet. */
+	    outw((ir&0xff00) | IM_ALLOC_INT, ioaddr + INTERRUPT);
+	    smc_hardware_send_packet(dev);	/* Send the packet now.. */
+	    spin_unlock_irqrestore(&smc->lock, flags);
+	    return NETDEV_TX_OK;
+	}
+    }
+
+    /* Otherwise defer until the Tx-space-allocated interrupt. */
+    pr_debug("%s: memory allocation deferred.\n", dev->name);
+    outw((IM_ALLOC_INT << 8) | (ir & 0xff00), ioaddr + INTERRUPT);
+    spin_unlock_irqrestore(&smc->lock, flags);
+
+    return NETDEV_TX_OK;
+}
+
+/*======================================================================
+
+    Handle a Tx anomalous event.  Entered while in Window 2.
+
+======================================================================*/
+
+static void smc_tx_err(struct net_device * dev)
+{
+    struct smc_private *smc = netdev_priv(dev);
+    unsigned int ioaddr = dev->base_addr;
+    int saved_packet = inw(ioaddr + PNR_ARR) & 0xff;
+    int packet_no = inw(ioaddr + FIFO_PORTS) & 0x7f;
+    int tx_status;
+
+    /* select this as the packet to read from */
+    outw(packet_no, ioaddr + PNR_ARR);
+
+    /* read the first word from this packet */
+    outw(PTR_AUTOINC | PTR_READ | 0, ioaddr + POINTER);
+
+    tx_status = inw(ioaddr + DATA_1);
+
+    dev->stats.tx_errors++;
+    if (tx_status & TS_LOSTCAR) dev->stats.tx_carrier_errors++;
+    if (tx_status & TS_LATCOL)  dev->stats.tx_window_errors++;
+    if (tx_status & TS_16COL) {
+	dev->stats.tx_aborted_errors++;
+	smc->tx_err++;
+    }
+
+    if (tx_status & TS_SUCCESS) {
+	netdev_notice(dev, "Successful packet caused error interrupt?\n");
+    }
+    /* re-enable transmit */
+    SMC_SELECT_BANK(0);
+    outw(inw(ioaddr + TCR) | TCR_ENABLE | smc->duplex, ioaddr + TCR);
+    SMC_SELECT_BANK(2);
+
+    outw(MC_FREEPKT, ioaddr + MMU_CMD); 	/* Free the packet memory. */
+
+    /* one less packet waiting for me */
+    smc->packets_waiting--;
+
+    outw(saved_packet, ioaddr + PNR_ARR);
+}
+
+/*====================================================================*/
+
+static void smc_eph_irq(struct net_device *dev)
+{
+    struct smc_private *smc = netdev_priv(dev);
+    unsigned int ioaddr = dev->base_addr;
+    u_short card_stats, ephs;
+
+    SMC_SELECT_BANK(0);
+    ephs = inw(ioaddr + EPH);
+    pr_debug("%s: Ethernet protocol handler interrupt, status"
+	  " %4.4x.\n", dev->name, ephs);
+    /* Could be a counter roll-over warning: update stats. */
+    card_stats = inw(ioaddr + COUNTER);
+    /* single collisions */
+    dev->stats.collisions += card_stats & 0xF;
+    card_stats >>= 4;
+    /* multiple collisions */
+    dev->stats.collisions += card_stats & 0xF;
+#if 0 		/* These are for when linux supports these statistics */
+    card_stats >>= 4;			/* deferred */
+    card_stats >>= 4;			/* excess deferred */
+#endif
+    /* If we had a transmit error we must re-enable the transmitter. */
+    outw(inw(ioaddr + TCR) | TCR_ENABLE | smc->duplex, ioaddr + TCR);
+
+    /* Clear a link error interrupt. */
+    SMC_SELECT_BANK(1);
+    outw(CTL_AUTO_RELEASE | 0x0000, ioaddr + CONTROL);
+    outw(CTL_AUTO_RELEASE | CTL_TE_ENABLE | CTL_CR_ENABLE,
+	 ioaddr + CONTROL);
+    SMC_SELECT_BANK(2);
+}
+
+/*====================================================================*/
+
+static irqreturn_t smc_interrupt(int irq, void *dev_id)
+{
+    struct net_device *dev = dev_id;
+    struct smc_private *smc = netdev_priv(dev);
+    unsigned int ioaddr;
+    u_short saved_bank, saved_pointer, mask, status;
+    unsigned int handled = 1;
+    char bogus_cnt = INTR_WORK;		/* Work we are willing to do. */
+
+    if (!netif_device_present(dev))
+	return IRQ_NONE;
+
+    ioaddr = dev->base_addr;
+
+    pr_debug("%s: SMC91c92 interrupt %d at %#x.\n", dev->name,
+	  irq, ioaddr);
+
+    spin_lock(&smc->lock);
+    smc->watchdog = 0;
+    saved_bank = inw(ioaddr + BANK_SELECT);
+    if ((saved_bank & 0xff00) != 0x3300) {
+	/* The device does not exist -- the card could be off-line, or
+	   maybe it has been ejected. */
+	pr_debug("%s: SMC91c92 interrupt %d for non-existent"
+	      "/ejected device.\n", dev->name, irq);
+	handled = 0;
+	goto irq_done;
+    }
+
+    SMC_SELECT_BANK(2);
+    saved_pointer = inw(ioaddr + POINTER);
+    mask = inw(ioaddr + INTERRUPT) >> 8;
+    /* clear all interrupts */
+    outw(0, ioaddr + INTERRUPT);
+
+    do { /* read the status flag, and mask it */
+	status = inw(ioaddr + INTERRUPT) & 0xff;
+	pr_debug("%s: Status is %#2.2x (mask %#2.2x).\n", dev->name,
+	      status, mask);
+	if ((status & mask) == 0) {
+	    if (bogus_cnt == INTR_WORK)
+		handled = 0;
+	    break;
+	}
+	if (status & IM_RCV_INT) {
+	    /* Got a packet(s). */
+	    smc_rx(dev);
+	}
+	if (status & IM_TX_INT) {
+	    smc_tx_err(dev);
+	    outw(IM_TX_INT, ioaddr + INTERRUPT);
+	}
+	status &= mask;
+	if (status & IM_TX_EMPTY_INT) {
+	    outw(IM_TX_EMPTY_INT, ioaddr + INTERRUPT);
+	    mask &= ~IM_TX_EMPTY_INT;
+	    dev->stats.tx_packets += smc->packets_waiting;
+	    smc->packets_waiting = 0;
+	}
+	if (status & IM_ALLOC_INT) {
+	    /* Clear this interrupt so it doesn't happen again */
+	    mask &= ~IM_ALLOC_INT;
+	
+	    smc_hardware_send_packet(dev);
+	
+	    /* enable xmit interrupts based on this */
+	    mask |= (IM_TX_EMPTY_INT | IM_TX_INT);
+	
+	    /* and let the card send more packets to me */
+	    netif_wake_queue(dev);
+	}
+	if (status & IM_RX_OVRN_INT) {
+	    dev->stats.rx_errors++;
+	    dev->stats.rx_fifo_errors++;
+	    if (smc->duplex)
+		smc->rx_ovrn = 1; /* need MC_RESET outside smc_interrupt */
+	    outw(IM_RX_OVRN_INT, ioaddr + INTERRUPT);
+	}
+	if (status & IM_EPH_INT)
+	    smc_eph_irq(dev);
+    } while (--bogus_cnt);
+
+    pr_debug("  Restoring saved registers mask %2.2x bank %4.4x"
+	  " pointer %4.4x.\n", mask, saved_bank, saved_pointer);
+
+    /* restore state register */
+    outw((mask<<8), ioaddr + INTERRUPT);
+    outw(saved_pointer, ioaddr + POINTER);
+    SMC_SELECT_BANK(saved_bank);
+
+    pr_debug("%s: Exiting interrupt IRQ%d.\n", dev->name, irq);
+
+irq_done:
+
+    if ((smc->manfid == MANFID_OSITECH) &&
+	(smc->cardid != PRODID_OSITECH_SEVEN)) {
+	/* Retrigger interrupt if needed */
+	mask_bits(0x00ff, ioaddr-0x10+OSITECH_RESET_ISR);
+	set_bits(0x0300, ioaddr-0x10+OSITECH_RESET_ISR);
+    }
+    if (smc->manfid == MANFID_MOTOROLA) {
+	u_char cor;
+	cor = readb(smc->base + MOT_UART + CISREG_COR);
+	writeb(cor & ~COR_IREQ_ENA, smc->base + MOT_UART + CISREG_COR);
+	writeb(cor, smc->base + MOT_UART + CISREG_COR);
+	cor = readb(smc->base + MOT_LAN + CISREG_COR);
+	writeb(cor & ~COR_IREQ_ENA, smc->base + MOT_LAN + CISREG_COR);
+	writeb(cor, smc->base + MOT_LAN + CISREG_COR);
+    }
+
+    if ((smc->base != NULL) &&  /* Megahertz MFC's */
+	(smc->manfid == MANFID_MEGAHERTZ) &&
+	(smc->cardid == PRODID_MEGAHERTZ_EM3288)) {
+
+	u_char tmp;
+	tmp = readb(smc->base+MEGAHERTZ_ISR);
+	tmp = readb(smc->base+MEGAHERTZ_ISR);
+
+	/* Retrigger interrupt if needed */
+	writeb(tmp, smc->base + MEGAHERTZ_ISR);
+	writeb(tmp, smc->base + MEGAHERTZ_ISR);
+    }
+
+    spin_unlock(&smc->lock);
+    return IRQ_RETVAL(handled);
+}
+
+/*====================================================================*/
+
+static void smc_rx(struct net_device *dev)
+{
+    unsigned int ioaddr = dev->base_addr;
+    int rx_status;
+    int packet_length;	/* Caution: not frame length, rather words
+			   to transfer from the chip. */
+
+    /* Assertion: we are in Window 2. */
+
+    if (inw(ioaddr + FIFO_PORTS) & FP_RXEMPTY) {
+	netdev_err(dev, "smc_rx() with nothing on Rx FIFO\n");
+	return;
+    }
+
+    /*  Reset the read pointer, and read the status and packet length. */
+    outw(PTR_READ | PTR_RCV | PTR_AUTOINC, ioaddr + POINTER);
+    rx_status = inw(ioaddr + DATA_1);
+    packet_length = inw(ioaddr + DATA_1) & 0x07ff;
+
+    pr_debug("%s: Receive status %4.4x length %d.\n",
+	  dev->name, rx_status, packet_length);
+
+    if (!(rx_status & RS_ERRORS)) {		
+	/* do stuff to make a new packet */
+	struct sk_buff *skb;
+	
+	/* Note: packet_length adds 5 or 6 extra bytes here! */
+	skb = dev_alloc_skb(packet_length+2);
+	
+	if (skb == NULL) {
+	    pr_debug("%s: Low memory, packet dropped.\n", dev->name);
+	    dev->stats.rx_dropped++;
+	    outw(MC_RELEASE, ioaddr + MMU_CMD);
+	    return;
+	}
+	
+	packet_length -= (rx_status & RS_ODDFRAME ? 5 : 6);
+	skb_reserve(skb, 2);
+	insw(ioaddr+DATA_1, skb_put(skb, packet_length),
+	     (packet_length+1)>>1);
+	skb->protocol = eth_type_trans(skb, dev);
+	
+	netif_rx(skb);
+	dev->last_rx = jiffies;
+	dev->stats.rx_packets++;
+	dev->stats.rx_bytes += packet_length;
+	if (rx_status & RS_MULTICAST)
+	    dev->stats.multicast++;
+    } else {
+	/* error ... */
+	dev->stats.rx_errors++;
+	
+	if (rx_status & RS_ALGNERR)  dev->stats.rx_frame_errors++;
+	if (rx_status & (RS_TOOSHORT | RS_TOOLONG))
+	    dev->stats.rx_length_errors++;
+	if (rx_status & RS_BADCRC)	dev->stats.rx_crc_errors++;
+    }
+    /* Let the MMU free the memory of this packet. */
+    outw(MC_RELEASE, ioaddr + MMU_CMD);
+}
+
+/*======================================================================
+
+    Set the receive mode.
+
+    This routine is used by both the protocol level to notify us of
+    promiscuous/multicast mode changes, and by the open/reset code to
+    initialize the Rx registers.  We always set the multicast list and
+    leave the receiver running.
+
+======================================================================*/
+
+static void set_rx_mode(struct net_device *dev)
+{
+    unsigned int ioaddr = dev->base_addr;
+    struct smc_private *smc = netdev_priv(dev);
+    unsigned char multicast_table[8];
+    unsigned long flags;
+    u_short rx_cfg_setting;
+    int i;
+
+    memset(multicast_table, 0, sizeof(multicast_table));
+
+    if (dev->flags & IFF_PROMISC) {
+	rx_cfg_setting = RxStripCRC | RxEnable | RxPromisc | RxAllMulti;
+    } else if (dev->flags & IFF_ALLMULTI)
+	rx_cfg_setting = RxStripCRC | RxEnable | RxAllMulti;
+    else {
+	if (!netdev_mc_empty(dev)) {
+	    struct netdev_hw_addr *ha;
+
+	    netdev_for_each_mc_addr(ha, dev) {
+		u_int position = ether_crc(6, ha->addr);
+		multicast_table[position >> 29] |= 1 << ((position >> 26) & 7);
+	    }
+	}
+	rx_cfg_setting = RxStripCRC | RxEnable;
+    }
+
+    /* Load MC table and Rx setting into the chip without interrupts. */
+    spin_lock_irqsave(&smc->lock, flags);
+    SMC_SELECT_BANK(3);
+    for (i = 0; i < 8; i++)
+	outb(multicast_table[i], ioaddr + MULTICAST0 + i);
+    SMC_SELECT_BANK(0);
+    outw(rx_cfg_setting, ioaddr + RCR);
+    SMC_SELECT_BANK(2);
+    spin_unlock_irqrestore(&smc->lock, flags);
+}
+
+/*======================================================================
+
+    Senses when a card's config changes. Here, it's coax or TP.
+
+======================================================================*/
+
+static int s9k_config(struct net_device *dev, struct ifmap *map)
+{
+    struct smc_private *smc = netdev_priv(dev);
+    if ((map->port != (u_char)(-1)) && (map->port != dev->if_port)) {
+	if (smc->cfg & CFG_MII_SELECT)
+	    return -EOPNOTSUPP;
+	else if (map->port > 2)
+	    return -EINVAL;
+	dev->if_port = map->port;
+	netdev_info(dev, "switched to %s port\n", if_names[dev->if_port]);
+	smc_reset(dev);
+    }
+    return 0;
+}
+
+/*======================================================================
+
+    Reset the chip, reloading every register that might be corrupted.
+
+======================================================================*/
+
+/*
+  Set transceiver type, perhaps to something other than what the user
+  specified in dev->if_port.
+*/
+static void smc_set_xcvr(struct net_device *dev, int if_port)
+{
+    struct smc_private *smc = netdev_priv(dev);
+    unsigned int ioaddr = dev->base_addr;
+    u_short saved_bank;
+
+    saved_bank = inw(ioaddr + BANK_SELECT);
+    SMC_SELECT_BANK(1);
+    if (if_port == 2) {
+	outw(smc->cfg | CFG_AUI_SELECT, ioaddr + CONFIG);
+	if ((smc->manfid == MANFID_OSITECH) &&
+	    (smc->cardid != PRODID_OSITECH_SEVEN))
+	    set_bits(OSI_AUI_PWR, ioaddr - 0x10 + OSITECH_AUI_PWR);
+	smc->media_status = ((dev->if_port == 0) ? 0x0001 : 0x0002);
+    } else {
+	outw(smc->cfg, ioaddr + CONFIG);
+	if ((smc->manfid == MANFID_OSITECH) &&
+	    (smc->cardid != PRODID_OSITECH_SEVEN))
+	    mask_bits(~OSI_AUI_PWR, ioaddr - 0x10 + OSITECH_AUI_PWR);
+	smc->media_status = ((dev->if_port == 0) ? 0x0012 : 0x4001);
+    }
+    SMC_SELECT_BANK(saved_bank);
+}
+
+static void smc_reset(struct net_device *dev)
+{
+    unsigned int ioaddr = dev->base_addr;
+    struct smc_private *smc = netdev_priv(dev);
+    int i;
+
+    pr_debug("%s: smc91c92 reset called.\n", dev->name);
+
+    /* The first interaction must be a write to bring the chip out
+       of sleep mode. */
+    SMC_SELECT_BANK(0);
+    /* Reset the chip. */
+    outw(RCR_SOFTRESET, ioaddr + RCR);
+    udelay(10);
+
+    /* Clear the transmit and receive configuration registers. */
+    outw(RCR_CLEAR, ioaddr + RCR);
+    outw(TCR_CLEAR, ioaddr + TCR);
+
+    /* Set the Window 1 control, configuration and station addr registers.
+       No point in writing the I/O base register ;-> */
+    SMC_SELECT_BANK(1);
+    /* Automatically release successfully transmitted packets,
+       Accept link errors, counter and Tx error interrupts. */
+    outw(CTL_AUTO_RELEASE | CTL_TE_ENABLE | CTL_CR_ENABLE,
+	 ioaddr + CONTROL);
+    smc_set_xcvr(dev, dev->if_port);
+    if ((smc->manfid == MANFID_OSITECH) &&
+	(smc->cardid != PRODID_OSITECH_SEVEN))
+	outw((dev->if_port == 2 ? OSI_AUI_PWR : 0) |
+	     (inw(ioaddr-0x10+OSITECH_AUI_PWR) & 0xff00),
+	     ioaddr - 0x10 + OSITECH_AUI_PWR);
+
+    /* Fill in the physical address.  The databook is wrong about the order! */
+    for (i = 0; i < 6; i += 2)
+	outw((dev->dev_addr[i+1]<<8)+dev->dev_addr[i],
+	     ioaddr + ADDR0 + i);
+
+    /* Reset the MMU */
+    SMC_SELECT_BANK(2);
+    outw(MC_RESET, ioaddr + MMU_CMD);
+    outw(0, ioaddr + INTERRUPT);
+
+    /* Re-enable the chip. */
+    SMC_SELECT_BANK(0);
+    outw(((smc->cfg & CFG_MII_SELECT) ? 0 : TCR_MONCSN) |
+	 TCR_ENABLE | TCR_PAD_EN | smc->duplex, ioaddr + TCR);
+    set_rx_mode(dev);
+
+    if (smc->cfg & CFG_MII_SELECT) {
+	SMC_SELECT_BANK(3);
+
+	/* Reset MII */
+	mdio_write(dev, smc->mii_if.phy_id, 0, 0x8000);
+
+	/* Advertise 100F, 100H, 10F, 10H */
+	mdio_write(dev, smc->mii_if.phy_id, 4, 0x01e1);
+
+	/* Restart MII autonegotiation */
+	mdio_write(dev, smc->mii_if.phy_id, 0, 0x0000);
+	mdio_write(dev, smc->mii_if.phy_id, 0, 0x1200);
+    }
+
+    /* Enable interrupts. */
+    SMC_SELECT_BANK(2);
+    outw((IM_EPH_INT | IM_RX_OVRN_INT | IM_RCV_INT) << 8,
+	 ioaddr + INTERRUPT);
+}
+
+/*======================================================================
+
+    Media selection timer routine
+
+======================================================================*/
+
+static void media_check(u_long arg)
+{
+    struct net_device *dev = (struct net_device *) arg;
+    struct smc_private *smc = netdev_priv(dev);
+    unsigned int ioaddr = dev->base_addr;
+    u_short i, media, saved_bank;
+    u_short link;
+    unsigned long flags;
+
+    spin_lock_irqsave(&smc->lock, flags);
+
+    saved_bank = inw(ioaddr + BANK_SELECT);
+
+    if (!netif_device_present(dev))
+	goto reschedule;
+
+    SMC_SELECT_BANK(2);
+
+    /* need MC_RESET to keep the memory consistent. errata? */
+    if (smc->rx_ovrn) {
+	outw(MC_RESET, ioaddr + MMU_CMD);
+	smc->rx_ovrn = 0;
+    }
+    i = inw(ioaddr + INTERRUPT);
+    SMC_SELECT_BANK(0);
+    media = inw(ioaddr + EPH) & EPH_LINK_OK;
+    SMC_SELECT_BANK(1);
+    media |= (inw(ioaddr + CONFIG) & CFG_AUI_SELECT) ? 2 : 1;
+
+    SMC_SELECT_BANK(saved_bank);
+    spin_unlock_irqrestore(&smc->lock, flags);
+
+    /* Check for pending interrupt with watchdog flag set: with
+       this, we can limp along even if the interrupt is blocked */
+    if (smc->watchdog++ && ((i>>8) & i)) {
+	if (!smc->fast_poll)
+	    netdev_info(dev, "interrupt(s) dropped!\n");
+	local_irq_save(flags);
+	smc_interrupt(dev->irq, dev);
+	local_irq_restore(flags);
+	smc->fast_poll = HZ;
+    }
+    if (smc->fast_poll) {
+	smc->fast_poll--;
+	smc->media.expires = jiffies + HZ/100;
+	add_timer(&smc->media);
+	return;
+    }
+
+    spin_lock_irqsave(&smc->lock, flags);
+
+    saved_bank = inw(ioaddr + BANK_SELECT);
+
+    if (smc->cfg & CFG_MII_SELECT) {
+	if (smc->mii_if.phy_id < 0)
+	    goto reschedule;
+
+	SMC_SELECT_BANK(3);
+	link = mdio_read(dev, smc->mii_if.phy_id, 1);
+	if (!link || (link == 0xffff)) {
+	    netdev_info(dev, "MII is missing!\n");
+	    smc->mii_if.phy_id = -1;
+	    goto reschedule;
+	}
+
+	link &= 0x0004;
+	if (link != smc->link_status) {
+	    u_short p = mdio_read(dev, smc->mii_if.phy_id, 5);
+	    netdev_info(dev, "%s link beat\n", link ? "found" : "lost");
+	    smc->duplex = (((p & 0x0100) || ((p & 0x1c0) == 0x40))
+			   ? TCR_FDUPLX : 0);
+	    if (link) {
+		netdev_info(dev, "autonegotiation complete: "
+			    "%dbaseT-%cD selected\n",
+			    (p & 0x0180) ? 100 : 10, smc->duplex ? 'F' : 'H');
+	    }
+	    SMC_SELECT_BANK(0);
+	    outw(inw(ioaddr + TCR) | smc->duplex, ioaddr + TCR);
+	    smc->link_status = link;
+	}
+	goto reschedule;
+    }
+
+    /* Ignore collisions unless we've had no rx's recently */
+    if (time_after(jiffies, dev->last_rx + HZ)) {
+	if (smc->tx_err || (smc->media_status & EPH_16COL))
+	    media |= EPH_16COL;
+    }
+    smc->tx_err = 0;
+
+    if (media != smc->media_status) {
+	if ((media & smc->media_status & 1) &&
+	    ((smc->media_status ^ media) & EPH_LINK_OK))
+	    netdev_info(dev, "%s link beat\n",
+			smc->media_status & EPH_LINK_OK ? "lost" : "found");
+	else if ((media & smc->media_status & 2) &&
+		 ((smc->media_status ^ media) & EPH_16COL))
+	    netdev_info(dev, "coax cable %s\n",
+			media & EPH_16COL ? "problem" : "ok");
+	if (dev->if_port == 0) {
+	    if (media & 1) {
+		if (media & EPH_LINK_OK)
+		    netdev_info(dev, "flipped to 10baseT\n");
+		else
+		    smc_set_xcvr(dev, 2);
+	    } else {
+		if (media & EPH_16COL)
+		    smc_set_xcvr(dev, 1);
+		else
+		    netdev_info(dev, "flipped to 10base2\n");
+	    }
+	}
+	smc->media_status = media;
+    }
+
+reschedule:
+    smc->media.expires = jiffies + HZ;
+    add_timer(&smc->media);
+    SMC_SELECT_BANK(saved_bank);
+    spin_unlock_irqrestore(&smc->lock, flags);
+}
+
+static int smc_link_ok(struct net_device *dev)
+{
+    unsigned int ioaddr = dev->base_addr;
+    struct smc_private *smc = netdev_priv(dev);
+
+    if (smc->cfg & CFG_MII_SELECT) {
+	return mii_link_ok(&smc->mii_if);
+    } else {
+        SMC_SELECT_BANK(0);
+	return inw(ioaddr + EPH) & EPH_LINK_OK;
+    }
+}
+
+static int smc_netdev_get_ecmd(struct net_device *dev, struct ethtool_cmd *ecmd)
+{
+    u16 tmp;
+    unsigned int ioaddr = dev->base_addr;
+
+    ecmd->supported = (SUPPORTED_TP | SUPPORTED_AUI |
+	SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full);
+		
+    SMC_SELECT_BANK(1);
+    tmp = inw(ioaddr + CONFIG);
+    ecmd->port = (tmp & CFG_AUI_SELECT) ? PORT_AUI : PORT_TP;
+    ecmd->transceiver = XCVR_INTERNAL;
+    ethtool_cmd_speed_set(ecmd, SPEED_10);
+    ecmd->phy_address = ioaddr + MGMT;
+
+    SMC_SELECT_BANK(0);
+    tmp = inw(ioaddr + TCR);
+    ecmd->duplex = (tmp & TCR_FDUPLX) ? DUPLEX_FULL : DUPLEX_HALF;
+
+    return 0;
+}
+
+static int smc_netdev_set_ecmd(struct net_device *dev, struct ethtool_cmd *ecmd)
+{
+    u16 tmp;
+    unsigned int ioaddr = dev->base_addr;
+
+    if (ethtool_cmd_speed(ecmd) != SPEED_10)
+	return -EINVAL;
+    if (ecmd->duplex != DUPLEX_HALF && ecmd->duplex != DUPLEX_FULL)
+    	return -EINVAL;
+    if (ecmd->port != PORT_TP && ecmd->port != PORT_AUI)
+	return -EINVAL;
+    if (ecmd->transceiver != XCVR_INTERNAL)
+    	return -EINVAL;
+
+    if (ecmd->port == PORT_AUI)
+	smc_set_xcvr(dev, 1);
+    else
+	smc_set_xcvr(dev, 0);
+
+    SMC_SELECT_BANK(0);
+    tmp = inw(ioaddr + TCR);
+    if (ecmd->duplex == DUPLEX_FULL)
+	tmp |= TCR_FDUPLX;
+    else
+	tmp &= ~TCR_FDUPLX;
+    outw(tmp, ioaddr + TCR);
+	
+    return 0;
+}
+
+static int check_if_running(struct net_device *dev)
+{
+	if (!netif_running(dev))
+		return -EINVAL;
+	return 0;
+}
+
+static void smc_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
+{
+	strcpy(info->driver, DRV_NAME);
+	strcpy(info->version, DRV_VERSION);
+}
+
+static int smc_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
+{
+	struct smc_private *smc = netdev_priv(dev);
+	unsigned int ioaddr = dev->base_addr;
+	u16 saved_bank = inw(ioaddr + BANK_SELECT);
+	int ret;
+	unsigned long flags;
+
+	spin_lock_irqsave(&smc->lock, flags);
+	SMC_SELECT_BANK(3);
+	if (smc->cfg & CFG_MII_SELECT)
+		ret = mii_ethtool_gset(&smc->mii_if, ecmd);
+	else
+		ret = smc_netdev_get_ecmd(dev, ecmd);
+	SMC_SELECT_BANK(saved_bank);
+	spin_unlock_irqrestore(&smc->lock, flags);
+	return ret;
+}
+
+static int smc_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
+{
+	struct smc_private *smc = netdev_priv(dev);
+	unsigned int ioaddr = dev->base_addr;
+	u16 saved_bank = inw(ioaddr + BANK_SELECT);
+	int ret;
+	unsigned long flags;
+
+	spin_lock_irqsave(&smc->lock, flags);
+	SMC_SELECT_BANK(3);
+	if (smc->cfg & CFG_MII_SELECT)
+		ret = mii_ethtool_sset(&smc->mii_if, ecmd);
+	else
+		ret = smc_netdev_set_ecmd(dev, ecmd);
+	SMC_SELECT_BANK(saved_bank);
+	spin_unlock_irqrestore(&smc->lock, flags);
+	return ret;
+}
+
+static u32 smc_get_link(struct net_device *dev)
+{
+	struct smc_private *smc = netdev_priv(dev);
+	unsigned int ioaddr = dev->base_addr;
+	u16 saved_bank = inw(ioaddr + BANK_SELECT);
+	u32 ret;
+	unsigned long flags;
+
+	spin_lock_irqsave(&smc->lock, flags);
+	SMC_SELECT_BANK(3);
+	ret = smc_link_ok(dev);
+	SMC_SELECT_BANK(saved_bank);
+	spin_unlock_irqrestore(&smc->lock, flags);
+	return ret;
+}
+
+static int smc_nway_reset(struct net_device *dev)
+{
+	struct smc_private *smc = netdev_priv(dev);
+	if (smc->cfg & CFG_MII_SELECT) {
+		unsigned int ioaddr = dev->base_addr;
+		u16 saved_bank = inw(ioaddr + BANK_SELECT);
+		int res;
+
+		SMC_SELECT_BANK(3);
+		res = mii_nway_restart(&smc->mii_if);
+		SMC_SELECT_BANK(saved_bank);
+
+		return res;
+	} else
+		return -EOPNOTSUPP;
+}
+
+static const struct ethtool_ops ethtool_ops = {
+	.begin = check_if_running,
+	.get_drvinfo = smc_get_drvinfo,
+	.get_settings = smc_get_settings,
+	.set_settings = smc_set_settings,
+	.get_link = smc_get_link,
+	.nway_reset = smc_nway_reset,
+};
+
+static int smc_ioctl (struct net_device *dev, struct ifreq *rq, int cmd)
+{
+	struct smc_private *smc = netdev_priv(dev);
+	struct mii_ioctl_data *mii = if_mii(rq);
+	int rc = 0;
+	u16 saved_bank;
+	unsigned int ioaddr = dev->base_addr;
+	unsigned long flags;
+
+	if (!netif_running(dev))
+		return -EINVAL;
+
+	spin_lock_irqsave(&smc->lock, flags);
+	saved_bank = inw(ioaddr + BANK_SELECT);
+	SMC_SELECT_BANK(3);
+	rc = generic_mii_ioctl(&smc->mii_if, mii, cmd, NULL);
+	SMC_SELECT_BANK(saved_bank);
+	spin_unlock_irqrestore(&smc->lock, flags);
+	return rc;
+}
+
+static const struct pcmcia_device_id smc91c92_ids[] = {
+	PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0109, 0x0501),
+	PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0140, 0x000a),
+	PCMCIA_PFC_DEVICE_PROD_ID123(0, "MEGAHERTZ", "CC/XJEM3288", "DATA/FAX/CELL ETHERNET MODEM", 0xf510db04, 0x04cd2988, 0x46a52d63),
+	PCMCIA_PFC_DEVICE_PROD_ID123(0, "MEGAHERTZ", "CC/XJEM3336", "DATA/FAX/CELL ETHERNET MODEM", 0xf510db04, 0x0143b773, 0x46a52d63),
+	PCMCIA_PFC_DEVICE_PROD_ID123(0, "MEGAHERTZ", "EM1144T", "PCMCIA MODEM", 0xf510db04, 0x856d66c8, 0xbd6c43ef),
+	PCMCIA_PFC_DEVICE_PROD_ID123(0, "MEGAHERTZ", "XJEM1144/CCEM1144", "PCMCIA MODEM", 0xf510db04, 0x52d21e1e, 0xbd6c43ef),
+	PCMCIA_PFC_DEVICE_PROD_ID12(0, "Gateway 2000", "XJEM3336", 0xdd9989be, 0x662c394c),
+	PCMCIA_PFC_DEVICE_PROD_ID12(0, "MEGAHERTZ", "XJEM1144/CCEM1144", 0xf510db04, 0x52d21e1e),
+	PCMCIA_PFC_DEVICE_PROD_ID12(0, "Ositech", "Trumpcard:Jack of Diamonds Modem+Ethernet", 0xc2f80cd, 0x656947b9),
+	PCMCIA_PFC_DEVICE_PROD_ID12(0, "Ositech", "Trumpcard:Jack of Hearts Modem+Ethernet", 0xc2f80cd, 0xdc9ba5ed),
+	PCMCIA_MFC_DEVICE_MANF_CARD(0, 0x016c, 0x0020),
+	PCMCIA_DEVICE_MANF_CARD(0x016c, 0x0023),
+	PCMCIA_DEVICE_PROD_ID123("BASICS by New Media Corporation", "Ethernet", "SMC91C94", 0x23c78a9d, 0x00b2e941, 0xcef397fb),
+	PCMCIA_DEVICE_PROD_ID12("ARGOSY", "Fast Ethernet PCCard", 0x78f308dc, 0xdcea68bc),
+	PCMCIA_DEVICE_PROD_ID12("dit Co., Ltd.", "PC Card-10/100BTX", 0xe59365c8, 0x6a2161d1),
+	PCMCIA_DEVICE_PROD_ID12("DYNALINK", "L100C", 0x6a26d1cf, 0xc16ce9c5),
+	PCMCIA_DEVICE_PROD_ID12("Farallon", "Farallon Enet", 0x58d93fc4, 0x244734e9),
+	PCMCIA_DEVICE_PROD_ID12("Megahertz", "CC10BT/2", 0x33234748, 0x3c95b953),
+	PCMCIA_DEVICE_PROD_ID12("MELCO/SMC", "LPC-TX", 0xa2cd8e6d, 0x42da662a),
+	PCMCIA_DEVICE_PROD_ID12("Ositech", "Trumpcard:Four of Diamonds Ethernet", 0xc2f80cd, 0xb3466314),
+	PCMCIA_DEVICE_PROD_ID12("Ositech", "Trumpcard:Seven of Diamonds Ethernet", 0xc2f80cd, 0x194b650a),
+	PCMCIA_DEVICE_PROD_ID12("PCMCIA", "Fast Ethernet PCCard", 0x281f1c5d, 0xdcea68bc),
+	PCMCIA_DEVICE_PROD_ID12("Psion", "10Mb Ethernet", 0x4ef00b21, 0x844be9e9),
+	PCMCIA_DEVICE_PROD_ID12("SMC", "EtherEZ Ethernet 8020", 0xc4f8b18b, 0x4a0eeb2d),
+	/* These conflict with other cards! */
+	/* PCMCIA_DEVICE_MANF_CARD(0x0186, 0x0100), */
+	/* PCMCIA_DEVICE_MANF_CARD(0x8a01, 0xc1ab), */
+	PCMCIA_DEVICE_NULL,
+};
+MODULE_DEVICE_TABLE(pcmcia, smc91c92_ids);
+
+static struct pcmcia_driver smc91c92_cs_driver = {
+	.owner		= THIS_MODULE,
+	.name		= "smc91c92_cs",
+	.probe		= smc91c92_probe,
+	.remove		= smc91c92_detach,
+	.id_table       = smc91c92_ids,
+	.suspend	= smc91c92_suspend,
+	.resume		= smc91c92_resume,
+};
+
+static int __init init_smc91c92_cs(void)
+{
+	return pcmcia_register_driver(&smc91c92_cs_driver);
+}
+
+static void __exit exit_smc91c92_cs(void)
+{
+	pcmcia_unregister_driver(&smc91c92_cs_driver);
+}
+
+module_init(init_smc91c92_cs);
+module_exit(exit_smc91c92_cs);
diff --git a/drivers/net/ethernet/smsc/smc91x.c b/drivers/net/ethernet/smsc/smc91x.c
new file mode 100644
index 0000000..2b1d254
--- /dev/null
+++ b/drivers/net/ethernet/smsc/smc91x.c
@@ -0,0 +1,2431 @@
+/*
+ * smc91x.c
+ * This is a driver for SMSC's 91C9x/91C1xx single-chip Ethernet devices.
+ *
+ * Copyright (C) 1996 by Erik Stahlman
+ * Copyright (C) 2001 Standard Microsystems Corporation
+ *	Developed by Simple Network Magic Corporation
+ * Copyright (C) 2003 Monta Vista Software, Inc.
+ *	Unified SMC91x driver by Nicolas Pitre
+ *
+ * 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
+ *
+ * Arguments:
+ * 	io	= for the base address
+ *	irq	= for the IRQ
+ *	nowait	= 0 for normal wait states, 1 eliminates additional wait states
+ *
+ * original author:
+ * 	Erik Stahlman <erik@vt.edu>
+ *
+ * hardware multicast code:
+ *    Peter Cammaert <pc@denkart.be>
+ *
+ * contributors:
+ * 	Daris A Nevil <dnevil@snmc.com>
+ *      Nicolas Pitre <nico@fluxnic.net>
+ *	Russell King <rmk@arm.linux.org.uk>
+ *
+ * History:
+ *   08/20/00  Arnaldo Melo       fix kfree(skb) in smc_hardware_send_packet
+ *   12/15/00  Christian Jullien  fix "Warning: kfree_skb on hard IRQ"
+ *   03/16/01  Daris A Nevil      modified smc9194.c for use with LAN91C111
+ *   08/22/01  Scott Anderson     merge changes from smc9194 to smc91111
+ *   08/21/01  Pramod B Bhardwaj  added support for RevB of LAN91C111
+ *   12/20/01  Jeff Sutherland    initial port to Xscale PXA with DMA support
+ *   04/07/03  Nicolas Pitre      unified SMC91x driver, killed irq races,
+ *                                more bus abstraction, big cleanup, etc.
+ *   29/09/03  Russell King       - add driver model support
+ *                                - ethtool support
+ *                                - convert to use generic MII interface
+ *                                - add link up/down notification
+ *                                - don't try to handle full negotiation in
+ *                                  smc_phy_configure
+ *                                - clean up (and fix stack overrun) in PHY
+ *                                  MII read/write functions
+ *   22/09/04  Nicolas Pitre      big update (see commit log for details)
+ */
+static const char version[] =
+	"smc91x.c: v1.1, sep 22 2004 by Nicolas Pitre <nico@fluxnic.net>\n";
+
+/* Debugging level */
+#ifndef SMC_DEBUG
+#define SMC_DEBUG		0
+#endif
+
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/crc32.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/ethtool.h>
+#include <linux/mii.h>
+#include <linux/workqueue.h>
+#include <linux/of.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include <asm/io.h>
+
+#include "smc91x.h"
+
+#ifndef SMC_NOWAIT
+# define SMC_NOWAIT		0
+#endif
+static int nowait = SMC_NOWAIT;
+module_param(nowait, int, 0400);
+MODULE_PARM_DESC(nowait, "set to 1 for no wait state");
+
+/*
+ * Transmit timeout, default 5 seconds.
+ */
+static int watchdog = 1000;
+module_param(watchdog, int, 0400);
+MODULE_PARM_DESC(watchdog, "transmit timeout in milliseconds");
+
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:smc91x");
+
+/*
+ * The internal workings of the driver.  If you are changing anything
+ * here with the SMC stuff, you should have the datasheet and know
+ * what you are doing.
+ */
+#define CARDNAME "smc91x"
+
+/*
+ * Use power-down feature of the chip
+ */
+#define POWER_DOWN		1
+
+/*
+ * Wait time for memory to be free.  This probably shouldn't be
+ * tuned that much, as waiting for this means nothing else happens
+ * in the system
+ */
+#define MEMORY_WAIT_TIME	16
+
+/*
+ * The maximum number of processing loops allowed for each call to the
+ * IRQ handler.
+ */
+#define MAX_IRQ_LOOPS		8
+
+/*
+ * This selects whether TX packets are sent one by one to the SMC91x internal
+ * memory and throttled until transmission completes.  This may prevent
+ * RX overruns a litle by keeping much of the memory free for RX packets
+ * but to the expense of reduced TX throughput and increased IRQ overhead.
+ * Note this is not a cure for a too slow data bus or too high IRQ latency.
+ */
+#define THROTTLE_TX_PKTS	0
+
+/*
+ * The MII clock high/low times.  2x this number gives the MII clock period
+ * in microseconds. (was 50, but this gives 6.4ms for each MII transaction!)
+ */
+#define MII_DELAY		1
+
+#if SMC_DEBUG > 0
+#define DBG(n, args...)					\
+	do {						\
+		if (SMC_DEBUG >= (n))			\
+			printk(args);	\
+	} while (0)
+
+#define PRINTK(args...)   printk(args)
+#else
+#define DBG(n, args...)   do { } while(0)
+#define PRINTK(args...)   printk(KERN_DEBUG args)
+#endif
+
+#if SMC_DEBUG > 3
+static void PRINT_PKT(u_char *buf, int length)
+{
+	int i;
+	int remainder;
+	int lines;
+
+	lines = length / 16;
+	remainder = length % 16;
+
+	for (i = 0; i < lines ; i ++) {
+		int cur;
+		for (cur = 0; cur < 8; cur++) {
+			u_char a, b;
+			a = *buf++;
+			b = *buf++;
+			printk("%02x%02x ", a, b);
+		}
+		printk("\n");
+	}
+	for (i = 0; i < remainder/2 ; i++) {
+		u_char a, b;
+		a = *buf++;
+		b = *buf++;
+		printk("%02x%02x ", a, b);
+	}
+	printk("\n");
+}
+#else
+#define PRINT_PKT(x...)  do { } while(0)
+#endif
+
+
+/* this enables an interrupt in the interrupt mask register */
+#define SMC_ENABLE_INT(lp, x) do {					\
+	unsigned char mask;						\
+	unsigned long smc_enable_flags;					\
+	spin_lock_irqsave(&lp->lock, smc_enable_flags);			\
+	mask = SMC_GET_INT_MASK(lp);					\
+	mask |= (x);							\
+	SMC_SET_INT_MASK(lp, mask);					\
+	spin_unlock_irqrestore(&lp->lock, smc_enable_flags);		\
+} while (0)
+
+/* this disables an interrupt from the interrupt mask register */
+#define SMC_DISABLE_INT(lp, x) do {					\
+	unsigned char mask;						\
+	unsigned long smc_disable_flags;				\
+	spin_lock_irqsave(&lp->lock, smc_disable_flags);		\
+	mask = SMC_GET_INT_MASK(lp);					\
+	mask &= ~(x);							\
+	SMC_SET_INT_MASK(lp, mask);					\
+	spin_unlock_irqrestore(&lp->lock, smc_disable_flags);		\
+} while (0)
+
+/*
+ * Wait while MMU is busy.  This is usually in the order of a few nanosecs
+ * if at all, but let's avoid deadlocking the system if the hardware
+ * decides to go south.
+ */
+#define SMC_WAIT_MMU_BUSY(lp) do {					\
+	if (unlikely(SMC_GET_MMU_CMD(lp) & MC_BUSY)) {		\
+		unsigned long timeout = jiffies + 2;			\
+		while (SMC_GET_MMU_CMD(lp) & MC_BUSY) {		\
+			if (time_after(jiffies, timeout)) {		\
+				printk("%s: timeout %s line %d\n",	\
+					dev->name, __FILE__, __LINE__);	\
+				break;					\
+			}						\
+			cpu_relax();					\
+		}							\
+	}								\
+} while (0)
+
+
+/*
+ * this does a soft reset on the device
+ */
+static void smc_reset(struct net_device *dev)
+{
+	struct smc_local *lp = netdev_priv(dev);
+	void __iomem *ioaddr = lp->base;
+	unsigned int ctl, cfg;
+	struct sk_buff *pending_skb;
+
+	DBG(2, "%s: %s\n", dev->name, __func__);
+
+	/* Disable all interrupts, block TX tasklet */
+	spin_lock_irq(&lp->lock);
+	SMC_SELECT_BANK(lp, 2);
+	SMC_SET_INT_MASK(lp, 0);
+	pending_skb = lp->pending_tx_skb;
+	lp->pending_tx_skb = NULL;
+	spin_unlock_irq(&lp->lock);
+
+	/* free any pending tx skb */
+	if (pending_skb) {
+		dev_kfree_skb(pending_skb);
+		dev->stats.tx_errors++;
+		dev->stats.tx_aborted_errors++;
+	}
+
+	/*
+	 * This resets the registers mostly to defaults, but doesn't
+	 * affect EEPROM.  That seems unnecessary
+	 */
+	SMC_SELECT_BANK(lp, 0);
+	SMC_SET_RCR(lp, RCR_SOFTRST);
+
+	/*
+	 * Setup the Configuration Register
+	 * This is necessary because the CONFIG_REG is not affected
+	 * by a soft reset
+	 */
+	SMC_SELECT_BANK(lp, 1);
+
+	cfg = CONFIG_DEFAULT;
+
+	/*
+	 * Setup for fast accesses if requested.  If the card/system
+	 * can't handle it then there will be no recovery except for
+	 * a hard reset or power cycle
+	 */
+	if (lp->cfg.flags & SMC91X_NOWAIT)
+		cfg |= CONFIG_NO_WAIT;
+
+	/*
+	 * Release from possible power-down state
+	 * Configuration register is not affected by Soft Reset
+	 */
+	cfg |= CONFIG_EPH_POWER_EN;
+
+	SMC_SET_CONFIG(lp, cfg);
+
+	/* this should pause enough for the chip to be happy */
+	/*
+	 * elaborate?  What does the chip _need_? --jgarzik
+	 *
+	 * This seems to be undocumented, but something the original
+	 * driver(s) have always done.  Suspect undocumented timing
+	 * info/determined empirically. --rmk
+	 */
+	udelay(1);
+
+	/* Disable transmit and receive functionality */
+	SMC_SELECT_BANK(lp, 0);
+	SMC_SET_RCR(lp, RCR_CLEAR);
+	SMC_SET_TCR(lp, TCR_CLEAR);
+
+	SMC_SELECT_BANK(lp, 1);
+	ctl = SMC_GET_CTL(lp) | CTL_LE_ENABLE;
+
+	/*
+	 * Set the control register to automatically release successfully
+	 * transmitted packets, to make the best use out of our limited
+	 * memory
+	 */
+	if(!THROTTLE_TX_PKTS)
+		ctl |= CTL_AUTO_RELEASE;
+	else
+		ctl &= ~CTL_AUTO_RELEASE;
+	SMC_SET_CTL(lp, ctl);
+
+	/* Reset the MMU */
+	SMC_SELECT_BANK(lp, 2);
+	SMC_SET_MMU_CMD(lp, MC_RESET);
+	SMC_WAIT_MMU_BUSY(lp);
+}
+
+/*
+ * Enable Interrupts, Receive, and Transmit
+ */
+static void smc_enable(struct net_device *dev)
+{
+	struct smc_local *lp = netdev_priv(dev);
+	void __iomem *ioaddr = lp->base;
+	int mask;
+
+	DBG(2, "%s: %s\n", dev->name, __func__);
+
+	/* see the header file for options in TCR/RCR DEFAULT */
+	SMC_SELECT_BANK(lp, 0);
+	SMC_SET_TCR(lp, lp->tcr_cur_mode);
+	SMC_SET_RCR(lp, lp->rcr_cur_mode);
+
+	SMC_SELECT_BANK(lp, 1);
+	SMC_SET_MAC_ADDR(lp, dev->dev_addr);
+
+	/* now, enable interrupts */
+	mask = IM_EPH_INT|IM_RX_OVRN_INT|IM_RCV_INT;
+	if (lp->version >= (CHIP_91100 << 4))
+		mask |= IM_MDINT;
+	SMC_SELECT_BANK(lp, 2);
+	SMC_SET_INT_MASK(lp, mask);
+
+	/*
+	 * From this point the register bank must _NOT_ be switched away
+	 * to something else than bank 2 without proper locking against
+	 * races with any tasklet or interrupt handlers until smc_shutdown()
+	 * or smc_reset() is called.
+	 */
+}
+
+/*
+ * this puts the device in an inactive state
+ */
+static void smc_shutdown(struct net_device *dev)
+{
+	struct smc_local *lp = netdev_priv(dev);
+	void __iomem *ioaddr = lp->base;
+	struct sk_buff *pending_skb;
+
+	DBG(2, "%s: %s\n", CARDNAME, __func__);
+
+	/* no more interrupts for me */
+	spin_lock_irq(&lp->lock);
+	SMC_SELECT_BANK(lp, 2);
+	SMC_SET_INT_MASK(lp, 0);
+	pending_skb = lp->pending_tx_skb;
+	lp->pending_tx_skb = NULL;
+	spin_unlock_irq(&lp->lock);
+	if (pending_skb)
+		dev_kfree_skb(pending_skb);
+
+	/* and tell the card to stay away from that nasty outside world */
+	SMC_SELECT_BANK(lp, 0);
+	SMC_SET_RCR(lp, RCR_CLEAR);
+	SMC_SET_TCR(lp, TCR_CLEAR);
+
+#ifdef POWER_DOWN
+	/* finally, shut the chip down */
+	SMC_SELECT_BANK(lp, 1);
+	SMC_SET_CONFIG(lp, SMC_GET_CONFIG(lp) & ~CONFIG_EPH_POWER_EN);
+#endif
+}
+
+/*
+ * This is the procedure to handle the receipt of a packet.
+ */
+static inline void  smc_rcv(struct net_device *dev)
+{
+	struct smc_local *lp = netdev_priv(dev);
+	void __iomem *ioaddr = lp->base;
+	unsigned int packet_number, status, packet_len;
+
+	DBG(3, "%s: %s\n", dev->name, __func__);
+
+	packet_number = SMC_GET_RXFIFO(lp);
+	if (unlikely(packet_number & RXFIFO_REMPTY)) {
+		PRINTK("%s: smc_rcv with nothing on FIFO.\n", dev->name);
+		return;
+	}
+
+	/* read from start of packet */
+	SMC_SET_PTR(lp, PTR_READ | PTR_RCV | PTR_AUTOINC);
+
+	/* First two words are status and packet length */
+	SMC_GET_PKT_HDR(lp, status, packet_len);
+	packet_len &= 0x07ff;  /* mask off top bits */
+	DBG(2, "%s: RX PNR 0x%x STATUS 0x%04x LENGTH 0x%04x (%d)\n",
+		dev->name, packet_number, status,
+		packet_len, packet_len);
+
+	back:
+	if (unlikely(packet_len < 6 || status & RS_ERRORS)) {
+		if (status & RS_TOOLONG && packet_len <= (1514 + 4 + 6)) {
+			/* accept VLAN packets */
+			status &= ~RS_TOOLONG;
+			goto back;
+		}
+		if (packet_len < 6) {
+			/* bloody hardware */
+			printk(KERN_ERR "%s: fubar (rxlen %u status %x\n",
+					dev->name, packet_len, status);
+			status |= RS_TOOSHORT;
+		}
+		SMC_WAIT_MMU_BUSY(lp);
+		SMC_SET_MMU_CMD(lp, MC_RELEASE);
+		dev->stats.rx_errors++;
+		if (status & RS_ALGNERR)
+			dev->stats.rx_frame_errors++;
+		if (status & (RS_TOOSHORT | RS_TOOLONG))
+			dev->stats.rx_length_errors++;
+		if (status & RS_BADCRC)
+			dev->stats.rx_crc_errors++;
+	} else {
+		struct sk_buff *skb;
+		unsigned char *data;
+		unsigned int data_len;
+
+		/* set multicast stats */
+		if (status & RS_MULTICAST)
+			dev->stats.multicast++;
+
+		/*
+		 * Actual payload is packet_len - 6 (or 5 if odd byte).
+		 * We want skb_reserve(2) and the final ctrl word
+		 * (2 bytes, possibly containing the payload odd byte).
+		 * Furthermore, we add 2 bytes to allow rounding up to
+		 * multiple of 4 bytes on 32 bit buses.
+		 * Hence packet_len - 6 + 2 + 2 + 2.
+		 */
+		skb = dev_alloc_skb(packet_len);
+		if (unlikely(skb == NULL)) {
+			printk(KERN_NOTICE "%s: Low memory, packet dropped.\n",
+				dev->name);
+			SMC_WAIT_MMU_BUSY(lp);
+			SMC_SET_MMU_CMD(lp, MC_RELEASE);
+			dev->stats.rx_dropped++;
+			return;
+		}
+
+		/* Align IP header to 32 bits */
+		skb_reserve(skb, 2);
+
+		/* BUG: the LAN91C111 rev A never sets this bit. Force it. */
+		if (lp->version == 0x90)
+			status |= RS_ODDFRAME;
+
+		/*
+		 * If odd length: packet_len - 5,
+		 * otherwise packet_len - 6.
+		 * With the trailing ctrl byte it's packet_len - 4.
+		 */
+		data_len = packet_len - ((status & RS_ODDFRAME) ? 5 : 6);
+		data = skb_put(skb, data_len);
+		SMC_PULL_DATA(lp, data, packet_len - 4);
+
+		SMC_WAIT_MMU_BUSY(lp);
+		SMC_SET_MMU_CMD(lp, MC_RELEASE);
+
+		PRINT_PKT(data, packet_len - 4);
+
+		skb->protocol = eth_type_trans(skb, dev);
+		netif_rx(skb);
+		dev->stats.rx_packets++;
+		dev->stats.rx_bytes += data_len;
+	}
+}
+
+#ifdef CONFIG_SMP
+/*
+ * On SMP we have the following problem:
+ *
+ * 	A = smc_hardware_send_pkt()
+ * 	B = smc_hard_start_xmit()
+ * 	C = smc_interrupt()
+ *
+ * A and B can never be executed simultaneously.  However, at least on UP,
+ * it is possible (and even desirable) for C to interrupt execution of
+ * A or B in order to have better RX reliability and avoid overruns.
+ * C, just like A and B, must have exclusive access to the chip and
+ * each of them must lock against any other concurrent access.
+ * Unfortunately this is not possible to have C suspend execution of A or
+ * B taking place on another CPU. On UP this is no an issue since A and B
+ * are run from softirq context and C from hard IRQ context, and there is
+ * no other CPU where concurrent access can happen.
+ * If ever there is a way to force at least B and C to always be executed
+ * on the same CPU then we could use read/write locks to protect against
+ * any other concurrent access and C would always interrupt B. But life
+ * isn't that easy in a SMP world...
+ */
+#define smc_special_trylock(lock, flags)				\
+({									\
+	int __ret;							\
+	local_irq_save(flags);						\
+	__ret = spin_trylock(lock);					\
+	if (!__ret)							\
+		local_irq_restore(flags);				\
+	__ret;								\
+})
+#define smc_special_lock(lock, flags)		spin_lock_irqsave(lock, flags)
+#define smc_special_unlock(lock, flags) 	spin_unlock_irqrestore(lock, flags)
+#else
+#define smc_special_trylock(lock, flags)	(flags == flags)
+#define smc_special_lock(lock, flags)   	do { flags = 0; } while (0)
+#define smc_special_unlock(lock, flags)	do { flags = 0; } while (0)
+#endif
+
+/*
+ * This is called to actually send a packet to the chip.
+ */
+static void smc_hardware_send_pkt(unsigned long data)
+{
+	struct net_device *dev = (struct net_device *)data;
+	struct smc_local *lp = netdev_priv(dev);
+	void __iomem *ioaddr = lp->base;
+	struct sk_buff *skb;
+	unsigned int packet_no, len;
+	unsigned char *buf;
+	unsigned long flags;
+
+	DBG(3, "%s: %s\n", dev->name, __func__);
+
+	if (!smc_special_trylock(&lp->lock, flags)) {
+		netif_stop_queue(dev);
+		tasklet_schedule(&lp->tx_task);
+		return;
+	}
+
+	skb = lp->pending_tx_skb;
+	if (unlikely(!skb)) {
+		smc_special_unlock(&lp->lock, flags);
+		return;
+	}
+	lp->pending_tx_skb = NULL;
+
+	packet_no = SMC_GET_AR(lp);
+	if (unlikely(packet_no & AR_FAILED)) {
+		printk("%s: Memory allocation failed.\n", dev->name);
+		dev->stats.tx_errors++;
+		dev->stats.tx_fifo_errors++;
+		smc_special_unlock(&lp->lock, flags);
+		goto done;
+	}
+
+	/* point to the beginning of the packet */
+	SMC_SET_PN(lp, packet_no);
+	SMC_SET_PTR(lp, PTR_AUTOINC);
+
+	buf = skb->data;
+	len = skb->len;
+	DBG(2, "%s: TX PNR 0x%x LENGTH 0x%04x (%d) BUF 0x%p\n",
+		dev->name, packet_no, len, len, buf);
+	PRINT_PKT(buf, len);
+
+	/*
+	 * Send the packet length (+6 for status words, length, and ctl.
+	 * The card will pad to 64 bytes with zeroes if packet is too small.
+	 */
+	SMC_PUT_PKT_HDR(lp, 0, len + 6);
+
+	/* send the actual data */
+	SMC_PUSH_DATA(lp, buf, len & ~1);
+
+	/* Send final ctl word with the last byte if there is one */
+	SMC_outw(((len & 1) ? (0x2000 | buf[len-1]) : 0), ioaddr, DATA_REG(lp));
+
+	/*
+	 * If THROTTLE_TX_PKTS is set, we stop the queue here. This will
+	 * have the effect of having at most one packet queued for TX
+	 * in the chip's memory at all time.
+	 *
+	 * If THROTTLE_TX_PKTS is not set then the queue is stopped only
+	 * when memory allocation (MC_ALLOC) does not succeed right away.
+	 */
+	if (THROTTLE_TX_PKTS)
+		netif_stop_queue(dev);
+
+	/* queue the packet for TX */
+	SMC_SET_MMU_CMD(lp, MC_ENQUEUE);
+	smc_special_unlock(&lp->lock, flags);
+
+	dev->trans_start = jiffies;
+	dev->stats.tx_packets++;
+	dev->stats.tx_bytes += len;
+
+	SMC_ENABLE_INT(lp, IM_TX_INT | IM_TX_EMPTY_INT);
+
+done:	if (!THROTTLE_TX_PKTS)
+		netif_wake_queue(dev);
+
+	dev_kfree_skb(skb);
+}
+
+/*
+ * Since I am not sure if I will have enough room in the chip's ram
+ * to store the packet, I call this routine which either sends it
+ * now, or set the card to generates an interrupt when ready
+ * for the packet.
+ */
+static int smc_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct smc_local *lp = netdev_priv(dev);
+	void __iomem *ioaddr = lp->base;
+	unsigned int numPages, poll_count, status;
+	unsigned long flags;
+
+	DBG(3, "%s: %s\n", dev->name, __func__);
+
+	BUG_ON(lp->pending_tx_skb != NULL);
+
+	/*
+	 * The MMU wants the number of pages to be the number of 256 bytes
+	 * 'pages', minus 1 (since a packet can't ever have 0 pages :))
+	 *
+	 * The 91C111 ignores the size bits, but earlier models don't.
+	 *
+	 * Pkt size for allocating is data length +6 (for additional status
+	 * words, length and ctl)
+	 *
+	 * If odd size then last byte is included in ctl word.
+	 */
+	numPages = ((skb->len & ~1) + (6 - 1)) >> 8;
+	if (unlikely(numPages > 7)) {
+		printk("%s: Far too big packet error.\n", dev->name);
+		dev->stats.tx_errors++;
+		dev->stats.tx_dropped++;
+		dev_kfree_skb(skb);
+		return NETDEV_TX_OK;
+	}
+
+	smc_special_lock(&lp->lock, flags);
+
+	/* now, try to allocate the memory */
+	SMC_SET_MMU_CMD(lp, MC_ALLOC | numPages);
+
+	/*
+	 * Poll the chip for a short amount of time in case the
+	 * allocation succeeds quickly.
+	 */
+	poll_count = MEMORY_WAIT_TIME;
+	do {
+		status = SMC_GET_INT(lp);
+		if (status & IM_ALLOC_INT) {
+			SMC_ACK_INT(lp, IM_ALLOC_INT);
+  			break;
+		}
+   	} while (--poll_count);
+
+	smc_special_unlock(&lp->lock, flags);
+
+	lp->pending_tx_skb = skb;
+   	if (!poll_count) {
+		/* oh well, wait until the chip finds memory later */
+		netif_stop_queue(dev);
+		DBG(2, "%s: TX memory allocation deferred.\n", dev->name);
+		SMC_ENABLE_INT(lp, IM_ALLOC_INT);
+   	} else {
+		/*
+		 * Allocation succeeded: push packet to the chip's own memory
+		 * immediately.
+		 */
+		smc_hardware_send_pkt((unsigned long)dev);
+	}
+
+	return NETDEV_TX_OK;
+}
+
+/*
+ * This handles a TX interrupt, which is only called when:
+ * - a TX error occurred, or
+ * - CTL_AUTO_RELEASE is not set and TX of a packet completed.
+ */
+static void smc_tx(struct net_device *dev)
+{
+	struct smc_local *lp = netdev_priv(dev);
+	void __iomem *ioaddr = lp->base;
+	unsigned int saved_packet, packet_no, tx_status, pkt_len;
+
+	DBG(3, "%s: %s\n", dev->name, __func__);
+
+	/* If the TX FIFO is empty then nothing to do */
+	packet_no = SMC_GET_TXFIFO(lp);
+	if (unlikely(packet_no & TXFIFO_TEMPTY)) {
+		PRINTK("%s: smc_tx with nothing on FIFO.\n", dev->name);
+		return;
+	}
+
+	/* select packet to read from */
+	saved_packet = SMC_GET_PN(lp);
+	SMC_SET_PN(lp, packet_no);
+
+	/* read the first word (status word) from this packet */
+	SMC_SET_PTR(lp, PTR_AUTOINC | PTR_READ);
+	SMC_GET_PKT_HDR(lp, tx_status, pkt_len);
+	DBG(2, "%s: TX STATUS 0x%04x PNR 0x%02x\n",
+		dev->name, tx_status, packet_no);
+
+	if (!(tx_status & ES_TX_SUC))
+		dev->stats.tx_errors++;
+
+	if (tx_status & ES_LOSTCARR)
+		dev->stats.tx_carrier_errors++;
+
+	if (tx_status & (ES_LATCOL | ES_16COL)) {
+		PRINTK("%s: %s occurred on last xmit\n", dev->name,
+		       (tx_status & ES_LATCOL) ?
+			"late collision" : "too many collisions");
+		dev->stats.tx_window_errors++;
+		if (!(dev->stats.tx_window_errors & 63) && net_ratelimit()) {
+			printk(KERN_INFO "%s: unexpectedly large number of "
+			       "bad collisions. Please check duplex "
+			       "setting.\n", dev->name);
+		}
+	}
+
+	/* kill the packet */
+	SMC_WAIT_MMU_BUSY(lp);
+	SMC_SET_MMU_CMD(lp, MC_FREEPKT);
+
+	/* Don't restore Packet Number Reg until busy bit is cleared */
+	SMC_WAIT_MMU_BUSY(lp);
+	SMC_SET_PN(lp, saved_packet);
+
+	/* re-enable transmit */
+	SMC_SELECT_BANK(lp, 0);
+	SMC_SET_TCR(lp, lp->tcr_cur_mode);
+	SMC_SELECT_BANK(lp, 2);
+}
+
+
+/*---PHY CONTROL AND CONFIGURATION-----------------------------------------*/
+
+static void smc_mii_out(struct net_device *dev, unsigned int val, int bits)
+{
+	struct smc_local *lp = netdev_priv(dev);
+	void __iomem *ioaddr = lp->base;
+	unsigned int mii_reg, mask;
+
+	mii_reg = SMC_GET_MII(lp) & ~(MII_MCLK | MII_MDOE | MII_MDO);
+	mii_reg |= MII_MDOE;
+
+	for (mask = 1 << (bits - 1); mask; mask >>= 1) {
+		if (val & mask)
+			mii_reg |= MII_MDO;
+		else
+			mii_reg &= ~MII_MDO;
+
+		SMC_SET_MII(lp, mii_reg);
+		udelay(MII_DELAY);
+		SMC_SET_MII(lp, mii_reg | MII_MCLK);
+		udelay(MII_DELAY);
+	}
+}
+
+static unsigned int smc_mii_in(struct net_device *dev, int bits)
+{
+	struct smc_local *lp = netdev_priv(dev);
+	void __iomem *ioaddr = lp->base;
+	unsigned int mii_reg, mask, val;
+
+	mii_reg = SMC_GET_MII(lp) & ~(MII_MCLK | MII_MDOE | MII_MDO);
+	SMC_SET_MII(lp, mii_reg);
+
+	for (mask = 1 << (bits - 1), val = 0; mask; mask >>= 1) {
+		if (SMC_GET_MII(lp) & MII_MDI)
+			val |= mask;
+
+		SMC_SET_MII(lp, mii_reg);
+		udelay(MII_DELAY);
+		SMC_SET_MII(lp, mii_reg | MII_MCLK);
+		udelay(MII_DELAY);
+	}
+
+	return val;
+}
+
+/*
+ * Reads a register from the MII Management serial interface
+ */
+static int smc_phy_read(struct net_device *dev, int phyaddr, int phyreg)
+{
+	struct smc_local *lp = netdev_priv(dev);
+	void __iomem *ioaddr = lp->base;
+	unsigned int phydata;
+
+	SMC_SELECT_BANK(lp, 3);
+
+	/* Idle - 32 ones */
+	smc_mii_out(dev, 0xffffffff, 32);
+
+	/* Start code (01) + read (10) + phyaddr + phyreg */
+	smc_mii_out(dev, 6 << 10 | phyaddr << 5 | phyreg, 14);
+
+	/* Turnaround (2bits) + phydata */
+	phydata = smc_mii_in(dev, 18);
+
+	/* Return to idle state */
+	SMC_SET_MII(lp, SMC_GET_MII(lp) & ~(MII_MCLK|MII_MDOE|MII_MDO));
+
+	DBG(3, "%s: phyaddr=0x%x, phyreg=0x%x, phydata=0x%x\n",
+		__func__, phyaddr, phyreg, phydata);
+
+	SMC_SELECT_BANK(lp, 2);
+	return phydata;
+}
+
+/*
+ * Writes a register to the MII Management serial interface
+ */
+static void smc_phy_write(struct net_device *dev, int phyaddr, int phyreg,
+			  int phydata)
+{
+	struct smc_local *lp = netdev_priv(dev);
+	void __iomem *ioaddr = lp->base;
+
+	SMC_SELECT_BANK(lp, 3);
+
+	/* Idle - 32 ones */
+	smc_mii_out(dev, 0xffffffff, 32);
+
+	/* Start code (01) + write (01) + phyaddr + phyreg + turnaround + phydata */
+	smc_mii_out(dev, 5 << 28 | phyaddr << 23 | phyreg << 18 | 2 << 16 | phydata, 32);
+
+	/* Return to idle state */
+	SMC_SET_MII(lp, SMC_GET_MII(lp) & ~(MII_MCLK|MII_MDOE|MII_MDO));
+
+	DBG(3, "%s: phyaddr=0x%x, phyreg=0x%x, phydata=0x%x\n",
+		__func__, phyaddr, phyreg, phydata);
+
+	SMC_SELECT_BANK(lp, 2);
+}
+
+/*
+ * Finds and reports the PHY address
+ */
+static void smc_phy_detect(struct net_device *dev)
+{
+	struct smc_local *lp = netdev_priv(dev);
+	int phyaddr;
+
+	DBG(2, "%s: %s\n", dev->name, __func__);
+
+	lp->phy_type = 0;
+
+	/*
+	 * Scan all 32 PHY addresses if necessary, starting at
+	 * PHY#1 to PHY#31, and then PHY#0 last.
+	 */
+	for (phyaddr = 1; phyaddr < 33; ++phyaddr) {
+		unsigned int id1, id2;
+
+		/* Read the PHY identifiers */
+		id1 = smc_phy_read(dev, phyaddr & 31, MII_PHYSID1);
+		id2 = smc_phy_read(dev, phyaddr & 31, MII_PHYSID2);
+
+		DBG(3, "%s: phy_id1=0x%x, phy_id2=0x%x\n",
+			dev->name, id1, id2);
+
+		/* Make sure it is a valid identifier */
+		if (id1 != 0x0000 && id1 != 0xffff && id1 != 0x8000 &&
+		    id2 != 0x0000 && id2 != 0xffff && id2 != 0x8000) {
+			/* Save the PHY's address */
+			lp->mii.phy_id = phyaddr & 31;
+			lp->phy_type = id1 << 16 | id2;
+			break;
+		}
+	}
+}
+
+/*
+ * Sets the PHY to a configuration as determined by the user
+ */
+static int smc_phy_fixed(struct net_device *dev)
+{
+	struct smc_local *lp = netdev_priv(dev);
+	void __iomem *ioaddr = lp->base;
+	int phyaddr = lp->mii.phy_id;
+	int bmcr, cfg1;
+
+	DBG(3, "%s: %s\n", dev->name, __func__);
+
+	/* Enter Link Disable state */
+	cfg1 = smc_phy_read(dev, phyaddr, PHY_CFG1_REG);
+	cfg1 |= PHY_CFG1_LNKDIS;
+	smc_phy_write(dev, phyaddr, PHY_CFG1_REG, cfg1);
+
+	/*
+	 * Set our fixed capabilities
+	 * Disable auto-negotiation
+	 */
+	bmcr = 0;
+
+	if (lp->ctl_rfduplx)
+		bmcr |= BMCR_FULLDPLX;
+
+	if (lp->ctl_rspeed == 100)
+		bmcr |= BMCR_SPEED100;
+
+	/* Write our capabilities to the phy control register */
+	smc_phy_write(dev, phyaddr, MII_BMCR, bmcr);
+
+	/* Re-Configure the Receive/Phy Control register */
+	SMC_SELECT_BANK(lp, 0);
+	SMC_SET_RPC(lp, lp->rpc_cur_mode);
+	SMC_SELECT_BANK(lp, 2);
+
+	return 1;
+}
+
+/*
+ * smc_phy_reset - reset the phy
+ * @dev: net device
+ * @phy: phy address
+ *
+ * Issue a software reset for the specified PHY and
+ * wait up to 100ms for the reset to complete.  We should
+ * not access the PHY for 50ms after issuing the reset.
+ *
+ * The time to wait appears to be dependent on the PHY.
+ *
+ * Must be called with lp->lock locked.
+ */
+static int smc_phy_reset(struct net_device *dev, int phy)
+{
+	struct smc_local *lp = netdev_priv(dev);
+	unsigned int bmcr;
+	int timeout;
+
+	smc_phy_write(dev, phy, MII_BMCR, BMCR_RESET);
+
+	for (timeout = 2; timeout; timeout--) {
+		spin_unlock_irq(&lp->lock);
+		msleep(50);
+		spin_lock_irq(&lp->lock);
+
+		bmcr = smc_phy_read(dev, phy, MII_BMCR);
+		if (!(bmcr & BMCR_RESET))
+			break;
+	}
+
+	return bmcr & BMCR_RESET;
+}
+
+/*
+ * smc_phy_powerdown - powerdown phy
+ * @dev: net device
+ *
+ * Power down the specified PHY
+ */
+static void smc_phy_powerdown(struct net_device *dev)
+{
+	struct smc_local *lp = netdev_priv(dev);
+	unsigned int bmcr;
+	int phy = lp->mii.phy_id;
+
+	if (lp->phy_type == 0)
+		return;
+
+	/* We need to ensure that no calls to smc_phy_configure are
+	   pending.
+	*/
+	cancel_work_sync(&lp->phy_configure);
+
+	bmcr = smc_phy_read(dev, phy, MII_BMCR);
+	smc_phy_write(dev, phy, MII_BMCR, bmcr | BMCR_PDOWN);
+}
+
+/*
+ * smc_phy_check_media - check the media status and adjust TCR
+ * @dev: net device
+ * @init: set true for initialisation
+ *
+ * Select duplex mode depending on negotiation state.  This
+ * also updates our carrier state.
+ */
+static void smc_phy_check_media(struct net_device *dev, int init)
+{
+	struct smc_local *lp = netdev_priv(dev);
+	void __iomem *ioaddr = lp->base;
+
+	if (mii_check_media(&lp->mii, netif_msg_link(lp), init)) {
+		/* duplex state has changed */
+		if (lp->mii.full_duplex) {
+			lp->tcr_cur_mode |= TCR_SWFDUP;
+		} else {
+			lp->tcr_cur_mode &= ~TCR_SWFDUP;
+		}
+
+		SMC_SELECT_BANK(lp, 0);
+		SMC_SET_TCR(lp, lp->tcr_cur_mode);
+	}
+}
+
+/*
+ * Configures the specified PHY through the MII management interface
+ * using Autonegotiation.
+ * Calls smc_phy_fixed() if the user has requested a certain config.
+ * If RPC ANEG bit is set, the media selection is dependent purely on
+ * the selection by the MII (either in the MII BMCR reg or the result
+ * of autonegotiation.)  If the RPC ANEG bit is cleared, the selection
+ * is controlled by the RPC SPEED and RPC DPLX bits.
+ */
+static void smc_phy_configure(struct work_struct *work)
+{
+	struct smc_local *lp =
+		container_of(work, struct smc_local, phy_configure);
+	struct net_device *dev = lp->dev;
+	void __iomem *ioaddr = lp->base;
+	int phyaddr = lp->mii.phy_id;
+	int my_phy_caps; /* My PHY capabilities */
+	int my_ad_caps; /* My Advertised capabilities */
+	int status;
+
+	DBG(3, "%s:smc_program_phy()\n", dev->name);
+
+	spin_lock_irq(&lp->lock);
+
+	/*
+	 * We should not be called if phy_type is zero.
+	 */
+	if (lp->phy_type == 0)
+		goto smc_phy_configure_exit;
+
+	if (smc_phy_reset(dev, phyaddr)) {
+		printk("%s: PHY reset timed out\n", dev->name);
+		goto smc_phy_configure_exit;
+	}
+
+	/*
+	 * Enable PHY Interrupts (for register 18)
+	 * Interrupts listed here are disabled
+	 */
+	smc_phy_write(dev, phyaddr, PHY_MASK_REG,
+		PHY_INT_LOSSSYNC | PHY_INT_CWRD | PHY_INT_SSD |
+		PHY_INT_ESD | PHY_INT_RPOL | PHY_INT_JAB |
+		PHY_INT_SPDDET | PHY_INT_DPLXDET);
+
+	/* Configure the Receive/Phy Control register */
+	SMC_SELECT_BANK(lp, 0);
+	SMC_SET_RPC(lp, lp->rpc_cur_mode);
+
+	/* If the user requested no auto neg, then go set his request */
+	if (lp->mii.force_media) {
+		smc_phy_fixed(dev);
+		goto smc_phy_configure_exit;
+	}
+
+	/* Copy our capabilities from MII_BMSR to MII_ADVERTISE */
+	my_phy_caps = smc_phy_read(dev, phyaddr, MII_BMSR);
+
+	if (!(my_phy_caps & BMSR_ANEGCAPABLE)) {
+		printk(KERN_INFO "Auto negotiation NOT supported\n");
+		smc_phy_fixed(dev);
+		goto smc_phy_configure_exit;
+	}
+
+	my_ad_caps = ADVERTISE_CSMA; /* I am CSMA capable */
+
+	if (my_phy_caps & BMSR_100BASE4)
+		my_ad_caps |= ADVERTISE_100BASE4;
+	if (my_phy_caps & BMSR_100FULL)
+		my_ad_caps |= ADVERTISE_100FULL;
+	if (my_phy_caps & BMSR_100HALF)
+		my_ad_caps |= ADVERTISE_100HALF;
+	if (my_phy_caps & BMSR_10FULL)
+		my_ad_caps |= ADVERTISE_10FULL;
+	if (my_phy_caps & BMSR_10HALF)
+		my_ad_caps |= ADVERTISE_10HALF;
+
+	/* Disable capabilities not selected by our user */
+	if (lp->ctl_rspeed != 100)
+		my_ad_caps &= ~(ADVERTISE_100BASE4|ADVERTISE_100FULL|ADVERTISE_100HALF);
+
+	if (!lp->ctl_rfduplx)
+		my_ad_caps &= ~(ADVERTISE_100FULL|ADVERTISE_10FULL);
+
+	/* Update our Auto-Neg Advertisement Register */
+	smc_phy_write(dev, phyaddr, MII_ADVERTISE, my_ad_caps);
+	lp->mii.advertising = my_ad_caps;
+
+	/*
+	 * Read the register back.  Without this, it appears that when
+	 * auto-negotiation is restarted, sometimes it isn't ready and
+	 * the link does not come up.
+	 */
+	status = smc_phy_read(dev, phyaddr, MII_ADVERTISE);
+
+	DBG(2, "%s: phy caps=%x\n", dev->name, my_phy_caps);
+	DBG(2, "%s: phy advertised caps=%x\n", dev->name, my_ad_caps);
+
+	/* Restart auto-negotiation process in order to advertise my caps */
+	smc_phy_write(dev, phyaddr, MII_BMCR, BMCR_ANENABLE | BMCR_ANRESTART);
+
+	smc_phy_check_media(dev, 1);
+
+smc_phy_configure_exit:
+	SMC_SELECT_BANK(lp, 2);
+	spin_unlock_irq(&lp->lock);
+}
+
+/*
+ * smc_phy_interrupt
+ *
+ * Purpose:  Handle interrupts relating to PHY register 18. This is
+ *  called from the "hard" interrupt handler under our private spinlock.
+ */
+static void smc_phy_interrupt(struct net_device *dev)
+{
+	struct smc_local *lp = netdev_priv(dev);
+	int phyaddr = lp->mii.phy_id;
+	int phy18;
+
+	DBG(2, "%s: %s\n", dev->name, __func__);
+
+	if (lp->phy_type == 0)
+		return;
+
+	for(;;) {
+		smc_phy_check_media(dev, 0);
+
+		/* Read PHY Register 18, Status Output */
+		phy18 = smc_phy_read(dev, phyaddr, PHY_INT_REG);
+		if ((phy18 & PHY_INT_INT) == 0)
+			break;
+	}
+}
+
+/*--- END PHY CONTROL AND CONFIGURATION-------------------------------------*/
+
+static void smc_10bt_check_media(struct net_device *dev, int init)
+{
+	struct smc_local *lp = netdev_priv(dev);
+	void __iomem *ioaddr = lp->base;
+	unsigned int old_carrier, new_carrier;
+
+	old_carrier = netif_carrier_ok(dev) ? 1 : 0;
+
+	SMC_SELECT_BANK(lp, 0);
+	new_carrier = (SMC_GET_EPH_STATUS(lp) & ES_LINK_OK) ? 1 : 0;
+	SMC_SELECT_BANK(lp, 2);
+
+	if (init || (old_carrier != new_carrier)) {
+		if (!new_carrier) {
+			netif_carrier_off(dev);
+		} else {
+			netif_carrier_on(dev);
+		}
+		if (netif_msg_link(lp))
+			printk(KERN_INFO "%s: link %s\n", dev->name,
+			       new_carrier ? "up" : "down");
+	}
+}
+
+static void smc_eph_interrupt(struct net_device *dev)
+{
+	struct smc_local *lp = netdev_priv(dev);
+	void __iomem *ioaddr = lp->base;
+	unsigned int ctl;
+
+	smc_10bt_check_media(dev, 0);
+
+	SMC_SELECT_BANK(lp, 1);
+	ctl = SMC_GET_CTL(lp);
+	SMC_SET_CTL(lp, ctl & ~CTL_LE_ENABLE);
+	SMC_SET_CTL(lp, ctl);
+	SMC_SELECT_BANK(lp, 2);
+}
+
+/*
+ * This is the main routine of the driver, to handle the device when
+ * it needs some attention.
+ */
+static irqreturn_t smc_interrupt(int irq, void *dev_id)
+{
+	struct net_device *dev = dev_id;
+	struct smc_local *lp = netdev_priv(dev);
+	void __iomem *ioaddr = lp->base;
+	int status, mask, timeout, card_stats;
+	int saved_pointer;
+
+	DBG(3, "%s: %s\n", dev->name, __func__);
+
+	spin_lock(&lp->lock);
+
+	/* A preamble may be used when there is a potential race
+	 * between the interruptible transmit functions and this
+	 * ISR. */
+	SMC_INTERRUPT_PREAMBLE;
+
+	saved_pointer = SMC_GET_PTR(lp);
+	mask = SMC_GET_INT_MASK(lp);
+	SMC_SET_INT_MASK(lp, 0);
+
+	/* set a timeout value, so I don't stay here forever */
+	timeout = MAX_IRQ_LOOPS;
+
+	do {
+		status = SMC_GET_INT(lp);
+
+		DBG(2, "%s: INT 0x%02x MASK 0x%02x MEM 0x%04x FIFO 0x%04x\n",
+			dev->name, status, mask,
+			({ int meminfo; SMC_SELECT_BANK(lp, 0);
+			   meminfo = SMC_GET_MIR(lp);
+			   SMC_SELECT_BANK(lp, 2); meminfo; }),
+			SMC_GET_FIFO(lp));
+
+		status &= mask;
+		if (!status)
+			break;
+
+		if (status & IM_TX_INT) {
+			/* do this before RX as it will free memory quickly */
+			DBG(3, "%s: TX int\n", dev->name);
+			smc_tx(dev);
+			SMC_ACK_INT(lp, IM_TX_INT);
+			if (THROTTLE_TX_PKTS)
+				netif_wake_queue(dev);
+		} else if (status & IM_RCV_INT) {
+			DBG(3, "%s: RX irq\n", dev->name);
+			smc_rcv(dev);
+		} else if (status & IM_ALLOC_INT) {
+			DBG(3, "%s: Allocation irq\n", dev->name);
+			tasklet_hi_schedule(&lp->tx_task);
+			mask &= ~IM_ALLOC_INT;
+		} else if (status & IM_TX_EMPTY_INT) {
+			DBG(3, "%s: TX empty\n", dev->name);
+			mask &= ~IM_TX_EMPTY_INT;
+
+			/* update stats */
+			SMC_SELECT_BANK(lp, 0);
+			card_stats = SMC_GET_COUNTER(lp);
+			SMC_SELECT_BANK(lp, 2);
+
+			/* single collisions */
+			dev->stats.collisions += card_stats & 0xF;
+			card_stats >>= 4;
+
+			/* multiple collisions */
+			dev->stats.collisions += card_stats & 0xF;
+		} else if (status & IM_RX_OVRN_INT) {
+			DBG(1, "%s: RX overrun (EPH_ST 0x%04x)\n", dev->name,
+			       ({ int eph_st; SMC_SELECT_BANK(lp, 0);
+				  eph_st = SMC_GET_EPH_STATUS(lp);
+				  SMC_SELECT_BANK(lp, 2); eph_st; }));
+			SMC_ACK_INT(lp, IM_RX_OVRN_INT);
+			dev->stats.rx_errors++;
+			dev->stats.rx_fifo_errors++;
+		} else if (status & IM_EPH_INT) {
+			smc_eph_interrupt(dev);
+		} else if (status & IM_MDINT) {
+			SMC_ACK_INT(lp, IM_MDINT);
+			smc_phy_interrupt(dev);
+		} else if (status & IM_ERCV_INT) {
+			SMC_ACK_INT(lp, IM_ERCV_INT);
+			PRINTK("%s: UNSUPPORTED: ERCV INTERRUPT\n", dev->name);
+		}
+	} while (--timeout);
+
+	/* restore register states */
+	SMC_SET_PTR(lp, saved_pointer);
+	SMC_SET_INT_MASK(lp, mask);
+	spin_unlock(&lp->lock);
+
+#ifndef CONFIG_NET_POLL_CONTROLLER
+	if (timeout == MAX_IRQ_LOOPS)
+		PRINTK("%s: spurious interrupt (mask = 0x%02x)\n",
+		       dev->name, mask);
+#endif
+	DBG(3, "%s: Interrupt done (%d loops)\n",
+	       dev->name, MAX_IRQ_LOOPS - timeout);
+
+	/*
+	 * We return IRQ_HANDLED unconditionally here even if there was
+	 * nothing to do.  There is a possibility that a packet might
+	 * get enqueued into the chip right after TX_EMPTY_INT is raised
+	 * but just before the CPU acknowledges the IRQ.
+	 * Better take an unneeded IRQ in some occasions than complexifying
+	 * the code for all cases.
+	 */
+	return IRQ_HANDLED;
+}
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+/*
+ * Polling receive - used by netconsole and other diagnostic tools
+ * to allow network i/o with interrupts disabled.
+ */
+static void smc_poll_controller(struct net_device *dev)
+{
+	disable_irq(dev->irq);
+	smc_interrupt(dev->irq, dev);
+	enable_irq(dev->irq);
+}
+#endif
+
+/* Our watchdog timed out. Called by the networking layer */
+static void smc_timeout(struct net_device *dev)
+{
+	struct smc_local *lp = netdev_priv(dev);
+	void __iomem *ioaddr = lp->base;
+	int status, mask, eph_st, meminfo, fifo;
+
+	DBG(2, "%s: %s\n", dev->name, __func__);
+
+	spin_lock_irq(&lp->lock);
+	status = SMC_GET_INT(lp);
+	mask = SMC_GET_INT_MASK(lp);
+	fifo = SMC_GET_FIFO(lp);
+	SMC_SELECT_BANK(lp, 0);
+	eph_st = SMC_GET_EPH_STATUS(lp);
+	meminfo = SMC_GET_MIR(lp);
+	SMC_SELECT_BANK(lp, 2);
+	spin_unlock_irq(&lp->lock);
+	PRINTK( "%s: TX timeout (INT 0x%02x INTMASK 0x%02x "
+		"MEM 0x%04x FIFO 0x%04x EPH_ST 0x%04x)\n",
+		dev->name, status, mask, meminfo, fifo, eph_st );
+
+	smc_reset(dev);
+	smc_enable(dev);
+
+	/*
+	 * Reconfiguring the PHY doesn't seem like a bad idea here, but
+	 * smc_phy_configure() calls msleep() which calls schedule_timeout()
+	 * which calls schedule().  Hence we use a work queue.
+	 */
+	if (lp->phy_type != 0)
+		schedule_work(&lp->phy_configure);
+
+	/* We can accept TX packets again */
+	dev->trans_start = jiffies; /* prevent tx timeout */
+	netif_wake_queue(dev);
+}
+
+/*
+ * This routine will, depending on the values passed to it,
+ * either make it accept multicast packets, go into
+ * promiscuous mode (for TCPDUMP and cousins) or accept
+ * a select set of multicast packets
+ */
+static void smc_set_multicast_list(struct net_device *dev)
+{
+	struct smc_local *lp = netdev_priv(dev);
+	void __iomem *ioaddr = lp->base;
+	unsigned char multicast_table[8];
+	int update_multicast = 0;
+
+	DBG(2, "%s: %s\n", dev->name, __func__);
+
+	if (dev->flags & IFF_PROMISC) {
+		DBG(2, "%s: RCR_PRMS\n", dev->name);
+		lp->rcr_cur_mode |= RCR_PRMS;
+	}
+
+/* BUG?  I never disable promiscuous mode if multicasting was turned on.
+   Now, I turn off promiscuous mode, but I don't do anything to multicasting
+   when promiscuous mode is turned on.
+*/
+
+	/*
+	 * Here, I am setting this to accept all multicast packets.
+	 * I don't need to zero the multicast table, because the flag is
+	 * checked before the table is
+	 */
+	else if (dev->flags & IFF_ALLMULTI || netdev_mc_count(dev) > 16) {
+		DBG(2, "%s: RCR_ALMUL\n", dev->name);
+		lp->rcr_cur_mode |= RCR_ALMUL;
+	}
+
+	/*
+	 * This sets the internal hardware table to filter out unwanted
+	 * multicast packets before they take up memory.
+	 *
+	 * The SMC chip uses a hash table where the high 6 bits of the CRC of
+	 * address are the offset into the table.  If that bit is 1, then the
+	 * multicast packet is accepted.  Otherwise, it's dropped silently.
+	 *
+	 * To use the 6 bits as an offset into the table, the high 3 bits are
+	 * the number of the 8 bit register, while the low 3 bits are the bit
+	 * within that register.
+	 */
+	else if (!netdev_mc_empty(dev)) {
+		struct netdev_hw_addr *ha;
+
+		/* table for flipping the order of 3 bits */
+		static const unsigned char invert3[] = {0, 4, 2, 6, 1, 5, 3, 7};
+
+		/* start with a table of all zeros: reject all */
+		memset(multicast_table, 0, sizeof(multicast_table));
+
+		netdev_for_each_mc_addr(ha, dev) {
+			int position;
+
+			/* only use the low order bits */
+			position = crc32_le(~0, ha->addr, 6) & 0x3f;
+
+			/* do some messy swapping to put the bit in the right spot */
+			multicast_table[invert3[position&7]] |=
+				(1<<invert3[(position>>3)&7]);
+		}
+
+		/* be sure I get rid of flags I might have set */
+		lp->rcr_cur_mode &= ~(RCR_PRMS | RCR_ALMUL);
+
+		/* now, the table can be loaded into the chipset */
+		update_multicast = 1;
+	} else  {
+		DBG(2, "%s: ~(RCR_PRMS|RCR_ALMUL)\n", dev->name);
+		lp->rcr_cur_mode &= ~(RCR_PRMS | RCR_ALMUL);
+
+		/*
+		 * since I'm disabling all multicast entirely, I need to
+		 * clear the multicast list
+		 */
+		memset(multicast_table, 0, sizeof(multicast_table));
+		update_multicast = 1;
+	}
+
+	spin_lock_irq(&lp->lock);
+	SMC_SELECT_BANK(lp, 0);
+	SMC_SET_RCR(lp, lp->rcr_cur_mode);
+	if (update_multicast) {
+		SMC_SELECT_BANK(lp, 3);
+		SMC_SET_MCAST(lp, multicast_table);
+	}
+	SMC_SELECT_BANK(lp, 2);
+	spin_unlock_irq(&lp->lock);
+}
+
+
+/*
+ * Open and Initialize the board
+ *
+ * Set up everything, reset the card, etc..
+ */
+static int
+smc_open(struct net_device *dev)
+{
+	struct smc_local *lp = netdev_priv(dev);
+
+	DBG(2, "%s: %s\n", dev->name, __func__);
+
+	/*
+	 * Check that the address is valid.  If its not, refuse
+	 * to bring the device up.  The user must specify an
+	 * address using ifconfig eth0 hw ether xx:xx:xx:xx:xx:xx
+	 */
+	if (!is_valid_ether_addr(dev->dev_addr)) {
+		PRINTK("%s: no valid ethernet hw addr\n", __func__);
+		return -EINVAL;
+	}
+
+	/* Setup the default Register Modes */
+	lp->tcr_cur_mode = TCR_DEFAULT;
+	lp->rcr_cur_mode = RCR_DEFAULT;
+	lp->rpc_cur_mode = RPC_DEFAULT |
+				lp->cfg.leda << RPC_LSXA_SHFT |
+				lp->cfg.ledb << RPC_LSXB_SHFT;
+
+	/*
+	 * If we are not using a MII interface, we need to
+	 * monitor our own carrier signal to detect faults.
+	 */
+	if (lp->phy_type == 0)
+		lp->tcr_cur_mode |= TCR_MON_CSN;
+
+	/* reset the hardware */
+	smc_reset(dev);
+	smc_enable(dev);
+
+	/* Configure the PHY, initialize the link state */
+	if (lp->phy_type != 0)
+		smc_phy_configure(&lp->phy_configure);
+	else {
+		spin_lock_irq(&lp->lock);
+		smc_10bt_check_media(dev, 1);
+		spin_unlock_irq(&lp->lock);
+	}
+
+	netif_start_queue(dev);
+	return 0;
+}
+
+/*
+ * smc_close
+ *
+ * this makes the board clean up everything that it can
+ * and not talk to the outside world.   Caused by
+ * an 'ifconfig ethX down'
+ */
+static int smc_close(struct net_device *dev)
+{
+	struct smc_local *lp = netdev_priv(dev);
+
+	DBG(2, "%s: %s\n", dev->name, __func__);
+
+	netif_stop_queue(dev);
+	netif_carrier_off(dev);
+
+	/* clear everything */
+	smc_shutdown(dev);
+	tasklet_kill(&lp->tx_task);
+	smc_phy_powerdown(dev);
+	return 0;
+}
+
+/*
+ * Ethtool support
+ */
+static int
+smc_ethtool_getsettings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+	struct smc_local *lp = netdev_priv(dev);
+	int ret;
+
+	cmd->maxtxpkt = 1;
+	cmd->maxrxpkt = 1;
+
+	if (lp->phy_type != 0) {
+		spin_lock_irq(&lp->lock);
+		ret = mii_ethtool_gset(&lp->mii, cmd);
+		spin_unlock_irq(&lp->lock);
+	} else {
+		cmd->supported = SUPPORTED_10baseT_Half |
+				 SUPPORTED_10baseT_Full |
+				 SUPPORTED_TP | SUPPORTED_AUI;
+
+		if (lp->ctl_rspeed == 10)
+			ethtool_cmd_speed_set(cmd, SPEED_10);
+		else if (lp->ctl_rspeed == 100)
+			ethtool_cmd_speed_set(cmd, SPEED_100);
+
+		cmd->autoneg = AUTONEG_DISABLE;
+		cmd->transceiver = XCVR_INTERNAL;
+		cmd->port = 0;
+		cmd->duplex = lp->tcr_cur_mode & TCR_SWFDUP ? DUPLEX_FULL : DUPLEX_HALF;
+
+		ret = 0;
+	}
+
+	return ret;
+}
+
+static int
+smc_ethtool_setsettings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+	struct smc_local *lp = netdev_priv(dev);
+	int ret;
+
+	if (lp->phy_type != 0) {
+		spin_lock_irq(&lp->lock);
+		ret = mii_ethtool_sset(&lp->mii, cmd);
+		spin_unlock_irq(&lp->lock);
+	} else {
+		if (cmd->autoneg != AUTONEG_DISABLE ||
+		    cmd->speed != SPEED_10 ||
+		    (cmd->duplex != DUPLEX_HALF && cmd->duplex != DUPLEX_FULL) ||
+		    (cmd->port != PORT_TP && cmd->port != PORT_AUI))
+			return -EINVAL;
+
+//		lp->port = cmd->port;
+		lp->ctl_rfduplx = cmd->duplex == DUPLEX_FULL;
+
+//		if (netif_running(dev))
+//			smc_set_port(dev);
+
+		ret = 0;
+	}
+
+	return ret;
+}
+
+static void
+smc_ethtool_getdrvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
+{
+	strncpy(info->driver, CARDNAME, sizeof(info->driver));
+	strncpy(info->version, version, sizeof(info->version));
+	strncpy(info->bus_info, dev_name(dev->dev.parent), sizeof(info->bus_info));
+}
+
+static int smc_ethtool_nwayreset(struct net_device *dev)
+{
+	struct smc_local *lp = netdev_priv(dev);
+	int ret = -EINVAL;
+
+	if (lp->phy_type != 0) {
+		spin_lock_irq(&lp->lock);
+		ret = mii_nway_restart(&lp->mii);
+		spin_unlock_irq(&lp->lock);
+	}
+
+	return ret;
+}
+
+static u32 smc_ethtool_getmsglevel(struct net_device *dev)
+{
+	struct smc_local *lp = netdev_priv(dev);
+	return lp->msg_enable;
+}
+
+static void smc_ethtool_setmsglevel(struct net_device *dev, u32 level)
+{
+	struct smc_local *lp = netdev_priv(dev);
+	lp->msg_enable = level;
+}
+
+static int smc_write_eeprom_word(struct net_device *dev, u16 addr, u16 word)
+{
+	u16 ctl;
+	struct smc_local *lp = netdev_priv(dev);
+	void __iomem *ioaddr = lp->base;
+
+	spin_lock_irq(&lp->lock);
+	/* load word into GP register */
+	SMC_SELECT_BANK(lp, 1);
+	SMC_SET_GP(lp, word);
+	/* set the address to put the data in EEPROM */
+	SMC_SELECT_BANK(lp, 2);
+	SMC_SET_PTR(lp, addr);
+	/* tell it to write */
+	SMC_SELECT_BANK(lp, 1);
+	ctl = SMC_GET_CTL(lp);
+	SMC_SET_CTL(lp, ctl | (CTL_EEPROM_SELECT | CTL_STORE));
+	/* wait for it to finish */
+	do {
+		udelay(1);
+	} while (SMC_GET_CTL(lp) & CTL_STORE);
+	/* clean up */
+	SMC_SET_CTL(lp, ctl);
+	SMC_SELECT_BANK(lp, 2);
+	spin_unlock_irq(&lp->lock);
+	return 0;
+}
+
+static int smc_read_eeprom_word(struct net_device *dev, u16 addr, u16 *word)
+{
+	u16 ctl;
+	struct smc_local *lp = netdev_priv(dev);
+	void __iomem *ioaddr = lp->base;
+
+	spin_lock_irq(&lp->lock);
+	/* set the EEPROM address to get the data from */
+	SMC_SELECT_BANK(lp, 2);
+	SMC_SET_PTR(lp, addr | PTR_READ);
+	/* tell it to load */
+	SMC_SELECT_BANK(lp, 1);
+	SMC_SET_GP(lp, 0xffff);	/* init to known */
+	ctl = SMC_GET_CTL(lp);
+	SMC_SET_CTL(lp, ctl | (CTL_EEPROM_SELECT | CTL_RELOAD));
+	/* wait for it to finish */
+	do {
+		udelay(1);
+	} while (SMC_GET_CTL(lp) & CTL_RELOAD);
+	/* read word from GP register */
+	*word = SMC_GET_GP(lp);
+	/* clean up */
+	SMC_SET_CTL(lp, ctl);
+	SMC_SELECT_BANK(lp, 2);
+	spin_unlock_irq(&lp->lock);
+	return 0;
+}
+
+static int smc_ethtool_geteeprom_len(struct net_device *dev)
+{
+	return 0x23 * 2;
+}
+
+static int smc_ethtool_geteeprom(struct net_device *dev,
+		struct ethtool_eeprom *eeprom, u8 *data)
+{
+	int i;
+	int imax;
+
+	DBG(1, "Reading %d bytes at %d(0x%x)\n",
+		eeprom->len, eeprom->offset, eeprom->offset);
+	imax = smc_ethtool_geteeprom_len(dev);
+	for (i = 0; i < eeprom->len; i += 2) {
+		int ret;
+		u16 wbuf;
+		int offset = i + eeprom->offset;
+		if (offset > imax)
+			break;
+		ret = smc_read_eeprom_word(dev, offset >> 1, &wbuf);
+		if (ret != 0)
+			return ret;
+		DBG(2, "Read 0x%x from 0x%x\n", wbuf, offset >> 1);
+		data[i] = (wbuf >> 8) & 0xff;
+		data[i+1] = wbuf & 0xff;
+	}
+	return 0;
+}
+
+static int smc_ethtool_seteeprom(struct net_device *dev,
+		struct ethtool_eeprom *eeprom, u8 *data)
+{
+	int i;
+	int imax;
+
+	DBG(1, "Writing %d bytes to %d(0x%x)\n",
+			eeprom->len, eeprom->offset, eeprom->offset);
+	imax = smc_ethtool_geteeprom_len(dev);
+	for (i = 0; i < eeprom->len; i += 2) {
+		int ret;
+		u16 wbuf;
+		int offset = i + eeprom->offset;
+		if (offset > imax)
+			break;
+		wbuf = (data[i] << 8) | data[i + 1];
+		DBG(2, "Writing 0x%x to 0x%x\n", wbuf, offset >> 1);
+		ret = smc_write_eeprom_word(dev, offset >> 1, wbuf);
+		if (ret != 0)
+			return ret;
+	}
+	return 0;
+}
+
+
+static const struct ethtool_ops smc_ethtool_ops = {
+	.get_settings	= smc_ethtool_getsettings,
+	.set_settings	= smc_ethtool_setsettings,
+	.get_drvinfo	= smc_ethtool_getdrvinfo,
+
+	.get_msglevel	= smc_ethtool_getmsglevel,
+	.set_msglevel	= smc_ethtool_setmsglevel,
+	.nway_reset	= smc_ethtool_nwayreset,
+	.get_link	= ethtool_op_get_link,
+	.get_eeprom_len = smc_ethtool_geteeprom_len,
+	.get_eeprom	= smc_ethtool_geteeprom,
+	.set_eeprom	= smc_ethtool_seteeprom,
+};
+
+static const struct net_device_ops smc_netdev_ops = {
+	.ndo_open		= smc_open,
+	.ndo_stop		= smc_close,
+	.ndo_start_xmit		= smc_hard_start_xmit,
+	.ndo_tx_timeout		= smc_timeout,
+	.ndo_set_multicast_list	= smc_set_multicast_list,
+	.ndo_change_mtu		= eth_change_mtu,
+	.ndo_validate_addr	= eth_validate_addr,
+	.ndo_set_mac_address 	= eth_mac_addr,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	.ndo_poll_controller	= smc_poll_controller,
+#endif
+};
+
+/*
+ * smc_findirq
+ *
+ * This routine has a simple purpose -- make the SMC chip generate an
+ * interrupt, so an auto-detect routine can detect it, and find the IRQ,
+ */
+/*
+ * does this still work?
+ *
+ * I just deleted auto_irq.c, since it was never built...
+ *   --jgarzik
+ */
+static int __devinit smc_findirq(struct smc_local *lp)
+{
+	void __iomem *ioaddr = lp->base;
+	int timeout = 20;
+	unsigned long cookie;
+
+	DBG(2, "%s: %s\n", CARDNAME, __func__);
+
+	cookie = probe_irq_on();
+
+	/*
+	 * What I try to do here is trigger an ALLOC_INT. This is done
+	 * by allocating a small chunk of memory, which will give an interrupt
+	 * when done.
+	 */
+	/* enable ALLOCation interrupts ONLY */
+	SMC_SELECT_BANK(lp, 2);
+	SMC_SET_INT_MASK(lp, IM_ALLOC_INT);
+
+	/*
+ 	 * Allocate 512 bytes of memory.  Note that the chip was just
+	 * reset so all the memory is available
+	 */
+	SMC_SET_MMU_CMD(lp, MC_ALLOC | 1);
+
+	/*
+	 * Wait until positive that the interrupt has been generated
+	 */
+	do {
+		int int_status;
+		udelay(10);
+		int_status = SMC_GET_INT(lp);
+		if (int_status & IM_ALLOC_INT)
+			break;		/* got the interrupt */
+	} while (--timeout);
+
+	/*
+	 * there is really nothing that I can do here if timeout fails,
+	 * as autoirq_report will return a 0 anyway, which is what I
+	 * want in this case.   Plus, the clean up is needed in both
+	 * cases.
+	 */
+
+	/* and disable all interrupts again */
+	SMC_SET_INT_MASK(lp, 0);
+
+	/* and return what I found */
+	return probe_irq_off(cookie);
+}
+
+/*
+ * Function: smc_probe(unsigned long ioaddr)
+ *
+ * Purpose:
+ *	Tests to see if a given ioaddr points to an SMC91x chip.
+ *	Returns a 0 on success
+ *
+ * Algorithm:
+ *	(1) see if the high byte of BANK_SELECT is 0x33
+ * 	(2) compare the ioaddr with the base register's address
+ *	(3) see if I recognize the chip ID in the appropriate register
+ *
+ * Here I do typical initialization tasks.
+ *
+ * o  Initialize the structure if needed
+ * o  print out my vanity message if not done so already
+ * o  print out what type of hardware is detected
+ * o  print out the ethernet address
+ * o  find the IRQ
+ * o  set up my private data
+ * o  configure the dev structure with my subroutines
+ * o  actually GRAB the irq.
+ * o  GRAB the region
+ */
+static int __devinit smc_probe(struct net_device *dev, void __iomem *ioaddr,
+			    unsigned long irq_flags)
+{
+	struct smc_local *lp = netdev_priv(dev);
+	static int version_printed = 0;
+	int retval;
+	unsigned int val, revision_register;
+	const char *version_string;
+
+	DBG(2, "%s: %s\n", CARDNAME, __func__);
+
+	/* First, see if the high byte is 0x33 */
+	val = SMC_CURRENT_BANK(lp);
+	DBG(2, "%s: bank signature probe returned 0x%04x\n", CARDNAME, val);
+	if ((val & 0xFF00) != 0x3300) {
+		if ((val & 0xFF) == 0x33) {
+			printk(KERN_WARNING
+				"%s: Detected possible byte-swapped interface"
+				" at IOADDR %p\n", CARDNAME, ioaddr);
+		}
+		retval = -ENODEV;
+		goto err_out;
+	}
+
+	/*
+	 * The above MIGHT indicate a device, but I need to write to
+	 * further test this.
+	 */
+	SMC_SELECT_BANK(lp, 0);
+	val = SMC_CURRENT_BANK(lp);
+	if ((val & 0xFF00) != 0x3300) {
+		retval = -ENODEV;
+		goto err_out;
+	}
+
+	/*
+	 * well, we've already written once, so hopefully another
+	 * time won't hurt.  This time, I need to switch the bank
+	 * register to bank 1, so I can access the base address
+	 * register
+	 */
+	SMC_SELECT_BANK(lp, 1);
+	val = SMC_GET_BASE(lp);
+	val = ((val & 0x1F00) >> 3) << SMC_IO_SHIFT;
+	if (((unsigned int)ioaddr & (0x3e0 << SMC_IO_SHIFT)) != val) {
+		printk("%s: IOADDR %p doesn't match configuration (%x).\n",
+			CARDNAME, ioaddr, val);
+	}
+
+	/*
+	 * check if the revision register is something that I
+	 * recognize.  These might need to be added to later,
+	 * as future revisions could be added.
+	 */
+	SMC_SELECT_BANK(lp, 3);
+	revision_register = SMC_GET_REV(lp);
+	DBG(2, "%s: revision = 0x%04x\n", CARDNAME, revision_register);
+	version_string = chip_ids[ (revision_register >> 4) & 0xF];
+	if (!version_string || (revision_register & 0xff00) != 0x3300) {
+		/* I don't recognize this chip, so... */
+		printk("%s: IO %p: Unrecognized revision register 0x%04x"
+			", Contact author.\n", CARDNAME,
+			ioaddr, revision_register);
+
+		retval = -ENODEV;
+		goto err_out;
+	}
+
+	/* At this point I'll assume that the chip is an SMC91x. */
+	if (version_printed++ == 0)
+		printk("%s", version);
+
+	/* fill in some of the fields */
+	dev->base_addr = (unsigned long)ioaddr;
+	lp->base = ioaddr;
+	lp->version = revision_register & 0xff;
+	spin_lock_init(&lp->lock);
+
+	/* Get the MAC address */
+	SMC_SELECT_BANK(lp, 1);
+	SMC_GET_MAC_ADDR(lp, dev->dev_addr);
+
+	/* now, reset the chip, and put it into a known state */
+	smc_reset(dev);
+
+	/*
+	 * If dev->irq is 0, then the device has to be banged on to see
+	 * what the IRQ is.
+ 	 *
+	 * This banging doesn't always detect the IRQ, for unknown reasons.
+	 * a workaround is to reset the chip and try again.
+	 *
+	 * Interestingly, the DOS packet driver *SETS* the IRQ on the card to
+	 * be what is requested on the command line.   I don't do that, mostly
+	 * because the card that I have uses a non-standard method of accessing
+	 * the IRQs, and because this _should_ work in most configurations.
+	 *
+	 * Specifying an IRQ is done with the assumption that the user knows
+	 * what (s)he is doing.  No checking is done!!!!
+	 */
+	if (dev->irq < 1) {
+		int trials;
+
+		trials = 3;
+		while (trials--) {
+			dev->irq = smc_findirq(lp);
+			if (dev->irq)
+				break;
+			/* kick the card and try again */
+			smc_reset(dev);
+		}
+	}
+	if (dev->irq == 0) {
+		printk("%s: Couldn't autodetect your IRQ. Use irq=xx.\n",
+			dev->name);
+		retval = -ENODEV;
+		goto err_out;
+	}
+	dev->irq = irq_canonicalize(dev->irq);
+
+	/* Fill in the fields of the device structure with ethernet values. */
+	ether_setup(dev);
+
+	dev->watchdog_timeo = msecs_to_jiffies(watchdog);
+	dev->netdev_ops = &smc_netdev_ops;
+	dev->ethtool_ops = &smc_ethtool_ops;
+
+	tasklet_init(&lp->tx_task, smc_hardware_send_pkt, (unsigned long)dev);
+	INIT_WORK(&lp->phy_configure, smc_phy_configure);
+	lp->dev = dev;
+	lp->mii.phy_id_mask = 0x1f;
+	lp->mii.reg_num_mask = 0x1f;
+	lp->mii.force_media = 0;
+	lp->mii.full_duplex = 0;
+	lp->mii.dev = dev;
+	lp->mii.mdio_read = smc_phy_read;
+	lp->mii.mdio_write = smc_phy_write;
+
+	/*
+	 * Locate the phy, if any.
+	 */
+	if (lp->version >= (CHIP_91100 << 4))
+		smc_phy_detect(dev);
+
+	/* then shut everything down to save power */
+	smc_shutdown(dev);
+	smc_phy_powerdown(dev);
+
+	/* Set default parameters */
+	lp->msg_enable = NETIF_MSG_LINK;
+	lp->ctl_rfduplx = 0;
+	lp->ctl_rspeed = 10;
+
+	if (lp->version >= (CHIP_91100 << 4)) {
+		lp->ctl_rfduplx = 1;
+		lp->ctl_rspeed = 100;
+	}
+
+	/* Grab the IRQ */
+	retval = request_irq(dev->irq, smc_interrupt, irq_flags, dev->name, dev);
+      	if (retval)
+      		goto err_out;
+
+#ifdef CONFIG_ARCH_PXA
+#  ifdef SMC_USE_PXA_DMA
+	lp->cfg.flags |= SMC91X_USE_DMA;
+#  endif
+	if (lp->cfg.flags & SMC91X_USE_DMA) {
+		int dma = pxa_request_dma(dev->name, DMA_PRIO_LOW,
+					  smc_pxa_dma_irq, NULL);
+		if (dma >= 0)
+			dev->dma = dma;
+	}
+#endif
+
+	retval = register_netdev(dev);
+	if (retval == 0) {
+		/* now, print out the card info, in a short format.. */
+		printk("%s: %s (rev %d) at %p IRQ %d",
+			dev->name, version_string, revision_register & 0x0f,
+			lp->base, dev->irq);
+
+		if (dev->dma != (unsigned char)-1)
+			printk(" DMA %d", dev->dma);
+
+		printk("%s%s\n",
+			lp->cfg.flags & SMC91X_NOWAIT ? " [nowait]" : "",
+			THROTTLE_TX_PKTS ? " [throttle_tx]" : "");
+
+		if (!is_valid_ether_addr(dev->dev_addr)) {
+			printk("%s: Invalid ethernet MAC address.  Please "
+			       "set using ifconfig\n", dev->name);
+		} else {
+			/* Print the Ethernet address */
+			printk("%s: Ethernet addr: %pM\n",
+			       dev->name, dev->dev_addr);
+		}
+
+		if (lp->phy_type == 0) {
+			PRINTK("%s: No PHY found\n", dev->name);
+		} else if ((lp->phy_type & 0xfffffff0) == 0x0016f840) {
+			PRINTK("%s: PHY LAN83C183 (LAN91C111 Internal)\n", dev->name);
+		} else if ((lp->phy_type & 0xfffffff0) == 0x02821c50) {
+			PRINTK("%s: PHY LAN83C180\n", dev->name);
+		}
+	}
+
+err_out:
+#ifdef CONFIG_ARCH_PXA
+	if (retval && dev->dma != (unsigned char)-1)
+		pxa_free_dma(dev->dma);
+#endif
+	return retval;
+}
+
+static int smc_enable_device(struct platform_device *pdev)
+{
+	struct net_device *ndev = platform_get_drvdata(pdev);
+	struct smc_local *lp = netdev_priv(ndev);
+	unsigned long flags;
+	unsigned char ecor, ecsr;
+	void __iomem *addr;
+	struct resource * res;
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "smc91x-attrib");
+	if (!res)
+		return 0;
+
+	/*
+	 * Map the attribute space.  This is overkill, but clean.
+	 */
+	addr = ioremap(res->start, ATTRIB_SIZE);
+	if (!addr)
+		return -ENOMEM;
+
+	/*
+	 * Reset the device.  We must disable IRQs around this
+	 * since a reset causes the IRQ line become active.
+	 */
+	local_irq_save(flags);
+	ecor = readb(addr + (ECOR << SMC_IO_SHIFT)) & ~ECOR_RESET;
+	writeb(ecor | ECOR_RESET, addr + (ECOR << SMC_IO_SHIFT));
+	readb(addr + (ECOR << SMC_IO_SHIFT));
+
+	/*
+	 * Wait 100us for the chip to reset.
+	 */
+	udelay(100);
+
+	/*
+	 * The device will ignore all writes to the enable bit while
+	 * reset is asserted, even if the reset bit is cleared in the
+	 * same write.  Must clear reset first, then enable the device.
+	 */
+	writeb(ecor, addr + (ECOR << SMC_IO_SHIFT));
+	writeb(ecor | ECOR_ENABLE, addr + (ECOR << SMC_IO_SHIFT));
+
+	/*
+	 * Set the appropriate byte/word mode.
+	 */
+	ecsr = readb(addr + (ECSR << SMC_IO_SHIFT)) & ~ECSR_IOIS8;
+	if (!SMC_16BIT(lp))
+		ecsr |= ECSR_IOIS8;
+	writeb(ecsr, addr + (ECSR << SMC_IO_SHIFT));
+	local_irq_restore(flags);
+
+	iounmap(addr);
+
+	/*
+	 * Wait for the chip to wake up.  We could poll the control
+	 * register in the main register space, but that isn't mapped
+	 * yet.  We know this is going to take 750us.
+	 */
+	msleep(1);
+
+	return 0;
+}
+
+static int smc_request_attrib(struct platform_device *pdev,
+			      struct net_device *ndev)
+{
+	struct resource * res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "smc91x-attrib");
+	struct smc_local *lp __maybe_unused = netdev_priv(ndev);
+
+	if (!res)
+		return 0;
+
+	if (!request_mem_region(res->start, ATTRIB_SIZE, CARDNAME))
+		return -EBUSY;
+
+	return 0;
+}
+
+static void smc_release_attrib(struct platform_device *pdev,
+			       struct net_device *ndev)
+{
+	struct resource * res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "smc91x-attrib");
+	struct smc_local *lp __maybe_unused = netdev_priv(ndev);
+
+	if (res)
+		release_mem_region(res->start, ATTRIB_SIZE);
+}
+
+static inline void smc_request_datacs(struct platform_device *pdev, struct net_device *ndev)
+{
+	if (SMC_CAN_USE_DATACS) {
+		struct resource * res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "smc91x-data32");
+		struct smc_local *lp = netdev_priv(ndev);
+
+		if (!res)
+			return;
+
+		if(!request_mem_region(res->start, SMC_DATA_EXTENT, CARDNAME)) {
+			printk(KERN_INFO "%s: failed to request datacs memory region.\n", CARDNAME);
+			return;
+		}
+
+		lp->datacs = ioremap(res->start, SMC_DATA_EXTENT);
+	}
+}
+
+static void smc_release_datacs(struct platform_device *pdev, struct net_device *ndev)
+{
+	if (SMC_CAN_USE_DATACS) {
+		struct smc_local *lp = netdev_priv(ndev);
+		struct resource * res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "smc91x-data32");
+
+		if (lp->datacs)
+			iounmap(lp->datacs);
+
+		lp->datacs = NULL;
+
+		if (res)
+			release_mem_region(res->start, SMC_DATA_EXTENT);
+	}
+}
+
+/*
+ * smc_init(void)
+ *   Input parameters:
+ *	dev->base_addr == 0, try to find all possible locations
+ *	dev->base_addr > 0x1ff, this is the address to check
+ *	dev->base_addr == <anything else>, return failure code
+ *
+ *   Output:
+ *	0 --> there is a device
+ *	anything else, error
+ */
+static int __devinit smc_drv_probe(struct platform_device *pdev)
+{
+	struct smc91x_platdata *pd = pdev->dev.platform_data;
+	struct smc_local *lp;
+	struct net_device *ndev;
+	struct resource *res, *ires;
+	unsigned int __iomem *addr;
+	unsigned long irq_flags = SMC_IRQ_FLAGS;
+	int ret;
+
+	ndev = alloc_etherdev(sizeof(struct smc_local));
+	if (!ndev) {
+		printk("%s: could not allocate device.\n", CARDNAME);
+		ret = -ENOMEM;
+		goto out;
+	}
+	SET_NETDEV_DEV(ndev, &pdev->dev);
+
+	/* get configuration from platform data, only allow use of
+	 * bus width if both SMC_CAN_USE_xxx and SMC91X_USE_xxx are set.
+	 */
+
+	lp = netdev_priv(ndev);
+
+	if (pd) {
+		memcpy(&lp->cfg, pd, sizeof(lp->cfg));
+		lp->io_shift = SMC91X_IO_SHIFT(lp->cfg.flags);
+	} else {
+		lp->cfg.flags |= (SMC_CAN_USE_8BIT)  ? SMC91X_USE_8BIT  : 0;
+		lp->cfg.flags |= (SMC_CAN_USE_16BIT) ? SMC91X_USE_16BIT : 0;
+		lp->cfg.flags |= (SMC_CAN_USE_32BIT) ? SMC91X_USE_32BIT : 0;
+		lp->cfg.flags |= (nowait) ? SMC91X_NOWAIT : 0;
+	}
+
+	if (!lp->cfg.leda && !lp->cfg.ledb) {
+		lp->cfg.leda = RPC_LSA_DEFAULT;
+		lp->cfg.ledb = RPC_LSB_DEFAULT;
+	}
+
+	ndev->dma = (unsigned char)-1;
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "smc91x-regs");
+	if (!res)
+		res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		ret = -ENODEV;
+		goto out_free_netdev;
+	}
+
+
+	if (!request_mem_region(res->start, SMC_IO_EXTENT, CARDNAME)) {
+		ret = -EBUSY;
+		goto out_free_netdev;
+	}
+
+	ires = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (!ires) {
+		ret = -ENODEV;
+		goto out_release_io;
+	}
+
+	ndev->irq = ires->start;
+
+	if (irq_flags == -1 || ires->flags & IRQF_TRIGGER_MASK)
+		irq_flags = ires->flags & IRQF_TRIGGER_MASK;
+
+	ret = smc_request_attrib(pdev, ndev);
+	if (ret)
+		goto out_release_io;
+#if defined(CONFIG_SA1100_ASSABET)
+	NCR_0 |= NCR_ENET_OSC_EN;
+#endif
+	platform_set_drvdata(pdev, ndev);
+	ret = smc_enable_device(pdev);
+	if (ret)
+		goto out_release_attrib;
+
+	addr = ioremap(res->start, SMC_IO_EXTENT);
+	if (!addr) {
+		ret = -ENOMEM;
+		goto out_release_attrib;
+	}
+
+#ifdef CONFIG_ARCH_PXA
+	{
+		struct smc_local *lp = netdev_priv(ndev);
+		lp->device = &pdev->dev;
+		lp->physaddr = res->start;
+	}
+#endif
+
+	ret = smc_probe(ndev, addr, irq_flags);
+	if (ret != 0)
+		goto out_iounmap;
+
+	smc_request_datacs(pdev, ndev);
+
+	return 0;
+
+ out_iounmap:
+	platform_set_drvdata(pdev, NULL);
+	iounmap(addr);
+ out_release_attrib:
+	smc_release_attrib(pdev, ndev);
+ out_release_io:
+	release_mem_region(res->start, SMC_IO_EXTENT);
+ out_free_netdev:
+	free_netdev(ndev);
+ out:
+	printk("%s: not found (%d).\n", CARDNAME, ret);
+
+	return ret;
+}
+
+static int __devexit smc_drv_remove(struct platform_device *pdev)
+{
+	struct net_device *ndev = platform_get_drvdata(pdev);
+	struct smc_local *lp = netdev_priv(ndev);
+	struct resource *res;
+
+	platform_set_drvdata(pdev, NULL);
+
+	unregister_netdev(ndev);
+
+	free_irq(ndev->irq, ndev);
+
+#ifdef CONFIG_ARCH_PXA
+	if (ndev->dma != (unsigned char)-1)
+		pxa_free_dma(ndev->dma);
+#endif
+	iounmap(lp->base);
+
+	smc_release_datacs(pdev,ndev);
+	smc_release_attrib(pdev,ndev);
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "smc91x-regs");
+	if (!res)
+		res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	release_mem_region(res->start, SMC_IO_EXTENT);
+
+	free_netdev(ndev);
+
+	return 0;
+}
+
+static int smc_drv_suspend(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct net_device *ndev = platform_get_drvdata(pdev);
+
+	if (ndev) {
+		if (netif_running(ndev)) {
+			netif_device_detach(ndev);
+			smc_shutdown(ndev);
+			smc_phy_powerdown(ndev);
+		}
+	}
+	return 0;
+}
+
+static int smc_drv_resume(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct net_device *ndev = platform_get_drvdata(pdev);
+
+	if (ndev) {
+		struct smc_local *lp = netdev_priv(ndev);
+		smc_enable_device(pdev);
+		if (netif_running(ndev)) {
+			smc_reset(ndev);
+			smc_enable(ndev);
+			if (lp->phy_type != 0)
+				smc_phy_configure(&lp->phy_configure);
+			netif_device_attach(ndev);
+		}
+	}
+	return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id smc91x_match[] = {
+	{ .compatible = "smsc,lan91c94", },
+	{ .compatible = "smsc,lan91c111", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, smc91x_match);
+#else
+#define smc91x_match NULL
+#endif
+
+static struct dev_pm_ops smc_drv_pm_ops = {
+	.suspend	= smc_drv_suspend,
+	.resume		= smc_drv_resume,
+};
+
+static struct platform_driver smc_driver = {
+	.probe		= smc_drv_probe,
+	.remove		= __devexit_p(smc_drv_remove),
+	.driver		= {
+		.name	= CARDNAME,
+		.owner	= THIS_MODULE,
+		.pm	= &smc_drv_pm_ops,
+		.of_match_table = smc91x_match,
+	},
+};
+
+static int __init smc_init(void)
+{
+	return platform_driver_register(&smc_driver);
+}
+
+static void __exit smc_cleanup(void)
+{
+	platform_driver_unregister(&smc_driver);
+}
+
+module_init(smc_init);
+module_exit(smc_cleanup);
diff --git a/drivers/net/ethernet/smsc/smc91x.h b/drivers/net/ethernet/smsc/smc91x.h
new file mode 100644
index 0000000..5f53fbb
--- /dev/null
+++ b/drivers/net/ethernet/smsc/smc91x.h
@@ -0,0 +1,1180 @@
+/*------------------------------------------------------------------------
+ . smc91x.h - macros for SMSC's 91C9x/91C1xx single-chip Ethernet device.
+ .
+ . Copyright (C) 1996 by Erik Stahlman
+ . Copyright (C) 2001 Standard Microsystems Corporation
+ .	Developed by Simple Network Magic Corporation
+ . Copyright (C) 2003 Monta Vista Software, Inc.
+ .	Unified SMC91x driver by Nicolas Pitre
+ .
+ . 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
+ .
+ . Information contained in this file was obtained from the LAN91C111
+ . manual from SMC.  To get a copy, if you really want one, you can find
+ . information under www.smsc.com.
+ .
+ . Authors
+ .	Erik Stahlman		<erik@vt.edu>
+ .	Daris A Nevil		<dnevil@snmc.com>
+ .	Nicolas Pitre 		<nico@fluxnic.net>
+ .
+ ---------------------------------------------------------------------------*/
+#ifndef _SMC91X_H_
+#define _SMC91X_H_
+
+#include <linux/smc91x.h>
+
+/*
+ * Define your architecture specific bus configuration parameters here.
+ */
+
+#if defined(CONFIG_ARCH_LUBBOCK) ||\
+    defined(CONFIG_MACH_MAINSTONE) ||\
+    defined(CONFIG_MACH_ZYLONITE) ||\
+    defined(CONFIG_MACH_LITTLETON) ||\
+    defined(CONFIG_MACH_ZYLONITE2) ||\
+    defined(CONFIG_ARCH_VIPER) ||\
+    defined(CONFIG_MACH_STARGATE2)
+
+#include <asm/mach-types.h>
+
+/* Now the bus width is specified in the platform data
+ * pretend here to support all I/O access types
+ */
+#define SMC_CAN_USE_8BIT	1
+#define SMC_CAN_USE_16BIT	1
+#define SMC_CAN_USE_32BIT	1
+#define SMC_NOWAIT		1
+
+#define SMC_IO_SHIFT		(lp->io_shift)
+
+#define SMC_inb(a, r)		readb((a) + (r))
+#define SMC_inw(a, r)		readw((a) + (r))
+#define SMC_inl(a, r)		readl((a) + (r))
+#define SMC_outb(v, a, r)	writeb(v, (a) + (r))
+#define SMC_outl(v, a, r)	writel(v, (a) + (r))
+#define SMC_insw(a, r, p, l)	readsw((a) + (r), p, l)
+#define SMC_outsw(a, r, p, l)	writesw((a) + (r), p, l)
+#define SMC_insl(a, r, p, l)	readsl((a) + (r), p, l)
+#define SMC_outsl(a, r, p, l)	writesl((a) + (r), p, l)
+#define SMC_IRQ_FLAGS		(-1)	/* from resource */
+
+/* We actually can't write halfwords properly if not word aligned */
+static inline void SMC_outw(u16 val, void __iomem *ioaddr, int reg)
+{
+	if ((machine_is_mainstone() || machine_is_stargate2()) && reg & 2) {
+		unsigned int v = val << 16;
+		v |= readl(ioaddr + (reg & ~2)) & 0xffff;
+		writel(v, ioaddr + (reg & ~2));
+	} else {
+		writew(val, ioaddr + reg);
+	}
+}
+
+#elif defined(CONFIG_SA1100_PLEB)
+/* We can only do 16-bit reads and writes in the static memory space. */
+#define SMC_CAN_USE_8BIT	1
+#define SMC_CAN_USE_16BIT	1
+#define SMC_CAN_USE_32BIT	0
+#define SMC_IO_SHIFT		0
+#define SMC_NOWAIT		1
+
+#define SMC_inb(a, r)		readb((a) + (r))
+#define SMC_insb(a, r, p, l)	readsb((a) + (r), p, (l))
+#define SMC_inw(a, r)		readw((a) + (r))
+#define SMC_insw(a, r, p, l)	readsw((a) + (r), p, l)
+#define SMC_outb(v, a, r)	writeb(v, (a) + (r))
+#define SMC_outsb(a, r, p, l)	writesb((a) + (r), p, (l))
+#define SMC_outw(v, a, r)	writew(v, (a) + (r))
+#define SMC_outsw(a, r, p, l)	writesw((a) + (r), p, l)
+
+#define SMC_IRQ_FLAGS		(-1)
+
+#elif defined(CONFIG_SA1100_ASSABET)
+
+#include <mach/neponset.h>
+
+/* We can only do 8-bit reads and writes in the static memory space. */
+#define SMC_CAN_USE_8BIT	1
+#define SMC_CAN_USE_16BIT	0
+#define SMC_CAN_USE_32BIT	0
+#define SMC_NOWAIT		1
+
+/* The first two address lines aren't connected... */
+#define SMC_IO_SHIFT		2
+
+#define SMC_inb(a, r)		readb((a) + (r))
+#define SMC_outb(v, a, r)	writeb(v, (a) + (r))
+#define SMC_insb(a, r, p, l)	readsb((a) + (r), p, (l))
+#define SMC_outsb(a, r, p, l)	writesb((a) + (r), p, (l))
+#define SMC_IRQ_FLAGS		(-1)	/* from resource */
+
+#elif	defined(CONFIG_MACH_LOGICPD_PXA270) ||	\
+	defined(CONFIG_MACH_NOMADIK_8815NHK)
+
+#define SMC_CAN_USE_8BIT	0
+#define SMC_CAN_USE_16BIT	1
+#define SMC_CAN_USE_32BIT	0
+#define SMC_IO_SHIFT		0
+#define SMC_NOWAIT		1
+
+#define SMC_inw(a, r)		readw((a) + (r))
+#define SMC_outw(v, a, r)	writew(v, (a) + (r))
+#define SMC_insw(a, r, p, l)	readsw((a) + (r), p, l)
+#define SMC_outsw(a, r, p, l)	writesw((a) + (r), p, l)
+
+#elif	defined(CONFIG_ARCH_INNOKOM) || \
+	defined(CONFIG_ARCH_PXA_IDP) || \
+	defined(CONFIG_ARCH_RAMSES) || \
+	defined(CONFIG_ARCH_PCM027)
+
+#define SMC_CAN_USE_8BIT	1
+#define SMC_CAN_USE_16BIT	1
+#define SMC_CAN_USE_32BIT	1
+#define SMC_IO_SHIFT		0
+#define SMC_NOWAIT		1
+#define SMC_USE_PXA_DMA		1
+
+#define SMC_inb(a, r)		readb((a) + (r))
+#define SMC_inw(a, r)		readw((a) + (r))
+#define SMC_inl(a, r)		readl((a) + (r))
+#define SMC_outb(v, a, r)	writeb(v, (a) + (r))
+#define SMC_outl(v, a, r)	writel(v, (a) + (r))
+#define SMC_insl(a, r, p, l)	readsl((a) + (r), p, l)
+#define SMC_outsl(a, r, p, l)	writesl((a) + (r), p, l)
+#define SMC_IRQ_FLAGS		(-1)	/* from resource */
+
+/* We actually can't write halfwords properly if not word aligned */
+static inline void
+SMC_outw(u16 val, void __iomem *ioaddr, int reg)
+{
+	if (reg & 2) {
+		unsigned int v = val << 16;
+		v |= readl(ioaddr + (reg & ~2)) & 0xffff;
+		writel(v, ioaddr + (reg & ~2));
+	} else {
+		writew(val, ioaddr + reg);
+	}
+}
+
+#elif	defined(CONFIG_SH_SH4202_MICRODEV)
+
+#define SMC_CAN_USE_8BIT	0
+#define SMC_CAN_USE_16BIT	1
+#define SMC_CAN_USE_32BIT	0
+
+#define SMC_inb(a, r)		inb((a) + (r) - 0xa0000000)
+#define SMC_inw(a, r)		inw((a) + (r) - 0xa0000000)
+#define SMC_inl(a, r)		inl((a) + (r) - 0xa0000000)
+#define SMC_outb(v, a, r)	outb(v, (a) + (r) - 0xa0000000)
+#define SMC_outw(v, a, r)	outw(v, (a) + (r) - 0xa0000000)
+#define SMC_outl(v, a, r)	outl(v, (a) + (r) - 0xa0000000)
+#define SMC_insl(a, r, p, l)	insl((a) + (r) - 0xa0000000, p, l)
+#define SMC_outsl(a, r, p, l)	outsl((a) + (r) - 0xa0000000, p, l)
+#define SMC_insw(a, r, p, l)	insw((a) + (r) - 0xa0000000, p, l)
+#define SMC_outsw(a, r, p, l)	outsw((a) + (r) - 0xa0000000, p, l)
+
+#define SMC_IRQ_FLAGS		(0)
+
+#elif   defined(CONFIG_M32R)
+
+#define SMC_CAN_USE_8BIT	0
+#define SMC_CAN_USE_16BIT	1
+#define SMC_CAN_USE_32BIT	0
+
+#define SMC_inb(a, r)		inb(((u32)a) + (r))
+#define SMC_inw(a, r)		inw(((u32)a) + (r))
+#define SMC_outb(v, a, r)	outb(v, ((u32)a) + (r))
+#define SMC_outw(v, a, r)	outw(v, ((u32)a) + (r))
+#define SMC_insw(a, r, p, l)	insw(((u32)a) + (r), p, l)
+#define SMC_outsw(a, r, p, l)	outsw(((u32)a) + (r), p, l)
+
+#define SMC_IRQ_FLAGS		(0)
+
+#define RPC_LSA_DEFAULT		RPC_LED_TX_RX
+#define RPC_LSB_DEFAULT		RPC_LED_100_10
+
+#elif	defined(CONFIG_ARCH_VERSATILE)
+
+#define SMC_CAN_USE_8BIT	1
+#define SMC_CAN_USE_16BIT	1
+#define SMC_CAN_USE_32BIT	1
+#define SMC_NOWAIT		1
+
+#define SMC_inb(a, r)		readb((a) + (r))
+#define SMC_inw(a, r)		readw((a) + (r))
+#define SMC_inl(a, r)		readl((a) + (r))
+#define SMC_outb(v, a, r)	writeb(v, (a) + (r))
+#define SMC_outw(v, a, r)	writew(v, (a) + (r))
+#define SMC_outl(v, a, r)	writel(v, (a) + (r))
+#define SMC_insl(a, r, p, l)	readsl((a) + (r), p, l)
+#define SMC_outsl(a, r, p, l)	writesl((a) + (r), p, l)
+#define SMC_IRQ_FLAGS		(-1)	/* from resource */
+
+#elif defined(CONFIG_MN10300)
+
+/*
+ * MN10300/AM33 configuration
+ */
+
+#include <unit/smc91111.h>
+
+#elif defined(CONFIG_ARCH_MSM)
+
+#define SMC_CAN_USE_8BIT	0
+#define SMC_CAN_USE_16BIT	1
+#define SMC_CAN_USE_32BIT	0
+#define SMC_NOWAIT		1
+
+#define SMC_inw(a, r)		readw((a) + (r))
+#define SMC_outw(v, a, r)	writew(v, (a) + (r))
+#define SMC_insw(a, r, p, l)	readsw((a) + (r), p, l)
+#define SMC_outsw(a, r, p, l)	writesw((a) + (r), p, l)
+
+#define SMC_IRQ_FLAGS		IRQF_TRIGGER_HIGH
+
+#elif defined(CONFIG_COLDFIRE)
+
+#define SMC_CAN_USE_8BIT	0
+#define SMC_CAN_USE_16BIT	1
+#define SMC_CAN_USE_32BIT	0
+#define SMC_NOWAIT		1
+
+static inline void mcf_insw(void *a, unsigned char *p, int l)
+{
+	u16 *wp = (u16 *) p;
+	while (l-- > 0)
+		*wp++ = readw(a);
+}
+
+static inline void mcf_outsw(void *a, unsigned char *p, int l)
+{
+	u16 *wp = (u16 *) p;
+	while (l-- > 0)
+		writew(*wp++, a);
+}
+
+#define SMC_inw(a, r)		_swapw(readw((a) + (r)))
+#define SMC_outw(v, a, r)	writew(_swapw(v), (a) + (r))
+#define SMC_insw(a, r, p, l)	mcf_insw(a + r, p, l)
+#define SMC_outsw(a, r, p, l)	mcf_outsw(a + r, p, l)
+
+#define SMC_IRQ_FLAGS		(IRQF_DISABLED)
+
+#else
+
+/*
+ * Default configuration
+ */
+
+#define SMC_CAN_USE_8BIT	1
+#define SMC_CAN_USE_16BIT	1
+#define SMC_CAN_USE_32BIT	1
+#define SMC_NOWAIT		1
+
+#define SMC_IO_SHIFT		(lp->io_shift)
+
+#define SMC_inb(a, r)		readb((a) + (r))
+#define SMC_inw(a, r)		readw((a) + (r))
+#define SMC_inl(a, r)		readl((a) + (r))
+#define SMC_outb(v, a, r)	writeb(v, (a) + (r))
+#define SMC_outw(v, a, r)	writew(v, (a) + (r))
+#define SMC_outl(v, a, r)	writel(v, (a) + (r))
+#define SMC_insw(a, r, p, l)	readsw((a) + (r), p, l)
+#define SMC_outsw(a, r, p, l)	writesw((a) + (r), p, l)
+#define SMC_insl(a, r, p, l)	readsl((a) + (r), p, l)
+#define SMC_outsl(a, r, p, l)	writesl((a) + (r), p, l)
+
+#define RPC_LSA_DEFAULT		RPC_LED_100_10
+#define RPC_LSB_DEFAULT		RPC_LED_TX_RX
+
+#endif
+
+
+/* store this information for the driver.. */
+struct smc_local {
+	/*
+	 * If I have to wait until memory is available to send a
+	 * packet, I will store the skbuff here, until I get the
+	 * desired memory.  Then, I'll send it out and free it.
+	 */
+	struct sk_buff *pending_tx_skb;
+	struct tasklet_struct tx_task;
+
+	/* version/revision of the SMC91x chip */
+	int	version;
+
+	/* Contains the current active transmission mode */
+	int	tcr_cur_mode;
+
+	/* Contains the current active receive mode */
+	int	rcr_cur_mode;
+
+	/* Contains the current active receive/phy mode */
+	int	rpc_cur_mode;
+	int	ctl_rfduplx;
+	int	ctl_rspeed;
+
+	u32	msg_enable;
+	u32	phy_type;
+	struct mii_if_info mii;
+
+	/* work queue */
+	struct work_struct phy_configure;
+	struct net_device *dev;
+	int	work_pending;
+
+	spinlock_t lock;
+
+#ifdef CONFIG_ARCH_PXA
+	/* DMA needs the physical address of the chip */
+	u_long physaddr;
+	struct device *device;
+#endif
+	void __iomem *base;
+	void __iomem *datacs;
+
+	/* the low address lines on some platforms aren't connected... */
+	int	io_shift;
+
+	struct smc91x_platdata cfg;
+};
+
+#define SMC_8BIT(p)	((p)->cfg.flags & SMC91X_USE_8BIT)
+#define SMC_16BIT(p)	((p)->cfg.flags & SMC91X_USE_16BIT)
+#define SMC_32BIT(p)	((p)->cfg.flags & SMC91X_USE_32BIT)
+
+#ifdef CONFIG_ARCH_PXA
+/*
+ * Let's use the DMA engine on the XScale PXA2xx for RX packets. This is
+ * always happening in irq context so no need to worry about races.  TX is
+ * different and probably not worth it for that reason, and not as critical
+ * as RX which can overrun memory and lose packets.
+ */
+#include <linux/dma-mapping.h>
+#include <mach/dma.h>
+
+#ifdef SMC_insl
+#undef SMC_insl
+#define SMC_insl(a, r, p, l) \
+	smc_pxa_dma_insl(a, lp, r, dev->dma, p, l)
+static inline void
+smc_pxa_dma_insl(void __iomem *ioaddr, struct smc_local *lp, int reg, int dma,
+		 u_char *buf, int len)
+{
+	u_long physaddr = lp->physaddr;
+	dma_addr_t dmabuf;
+
+	/* fallback if no DMA available */
+	if (dma == (unsigned char)-1) {
+		readsl(ioaddr + reg, buf, len);
+		return;
+	}
+
+	/* 64 bit alignment is required for memory to memory DMA */
+	if ((long)buf & 4) {
+		*((u32 *)buf) = SMC_inl(ioaddr, reg);
+		buf += 4;
+		len--;
+	}
+
+	len *= 4;
+	dmabuf = dma_map_single(lp->device, buf, len, DMA_FROM_DEVICE);
+	DCSR(dma) = DCSR_NODESC;
+	DTADR(dma) = dmabuf;
+	DSADR(dma) = physaddr + reg;
+	DCMD(dma) = (DCMD_INCTRGADDR | DCMD_BURST32 |
+		     DCMD_WIDTH4 | (DCMD_LENGTH & len));
+	DCSR(dma) = DCSR_NODESC | DCSR_RUN;
+	while (!(DCSR(dma) & DCSR_STOPSTATE))
+		cpu_relax();
+	DCSR(dma) = 0;
+	dma_unmap_single(lp->device, dmabuf, len, DMA_FROM_DEVICE);
+}
+#endif
+
+#ifdef SMC_insw
+#undef SMC_insw
+#define SMC_insw(a, r, p, l) \
+	smc_pxa_dma_insw(a, lp, r, dev->dma, p, l)
+static inline void
+smc_pxa_dma_insw(void __iomem *ioaddr, struct smc_local *lp, int reg, int dma,
+		 u_char *buf, int len)
+{
+	u_long physaddr = lp->physaddr;
+	dma_addr_t dmabuf;
+
+	/* fallback if no DMA available */
+	if (dma == (unsigned char)-1) {
+		readsw(ioaddr + reg, buf, len);
+		return;
+	}
+
+	/* 64 bit alignment is required for memory to memory DMA */
+	while ((long)buf & 6) {
+		*((u16 *)buf) = SMC_inw(ioaddr, reg);
+		buf += 2;
+		len--;
+	}
+
+	len *= 2;
+	dmabuf = dma_map_single(lp->device, buf, len, DMA_FROM_DEVICE);
+	DCSR(dma) = DCSR_NODESC;
+	DTADR(dma) = dmabuf;
+	DSADR(dma) = physaddr + reg;
+	DCMD(dma) = (DCMD_INCTRGADDR | DCMD_BURST32 |
+		     DCMD_WIDTH2 | (DCMD_LENGTH & len));
+	DCSR(dma) = DCSR_NODESC | DCSR_RUN;
+	while (!(DCSR(dma) & DCSR_STOPSTATE))
+		cpu_relax();
+	DCSR(dma) = 0;
+	dma_unmap_single(lp->device, dmabuf, len, DMA_FROM_DEVICE);
+}
+#endif
+
+static void
+smc_pxa_dma_irq(int dma, void *dummy)
+{
+	DCSR(dma) = 0;
+}
+#endif  /* CONFIG_ARCH_PXA */
+
+
+/*
+ * Everything a particular hardware setup needs should have been defined
+ * at this point.  Add stubs for the undefined cases, mainly to avoid
+ * compilation warnings since they'll be optimized away, or to prevent buggy
+ * use of them.
+ */
+
+#if ! SMC_CAN_USE_32BIT
+#define SMC_inl(ioaddr, reg)		({ BUG(); 0; })
+#define SMC_outl(x, ioaddr, reg)	BUG()
+#define SMC_insl(a, r, p, l)		BUG()
+#define SMC_outsl(a, r, p, l)		BUG()
+#endif
+
+#if !defined(SMC_insl) || !defined(SMC_outsl)
+#define SMC_insl(a, r, p, l)		BUG()
+#define SMC_outsl(a, r, p, l)		BUG()
+#endif
+
+#if ! SMC_CAN_USE_16BIT
+
+/*
+ * Any 16-bit access is performed with two 8-bit accesses if the hardware
+ * can't do it directly. Most registers are 16-bit so those are mandatory.
+ */
+#define SMC_outw(x, ioaddr, reg)					\
+	do {								\
+		unsigned int __val16 = (x);				\
+		SMC_outb( __val16, ioaddr, reg );			\
+		SMC_outb( __val16 >> 8, ioaddr, reg + (1 << SMC_IO_SHIFT));\
+	} while (0)
+#define SMC_inw(ioaddr, reg)						\
+	({								\
+		unsigned int __val16;					\
+		__val16 =  SMC_inb( ioaddr, reg );			\
+		__val16 |= SMC_inb( ioaddr, reg + (1 << SMC_IO_SHIFT)) << 8; \
+		__val16;						\
+	})
+
+#define SMC_insw(a, r, p, l)		BUG()
+#define SMC_outsw(a, r, p, l)		BUG()
+
+#endif
+
+#if !defined(SMC_insw) || !defined(SMC_outsw)
+#define SMC_insw(a, r, p, l)		BUG()
+#define SMC_outsw(a, r, p, l)		BUG()
+#endif
+
+#if ! SMC_CAN_USE_8BIT
+#define SMC_inb(ioaddr, reg)		({ BUG(); 0; })
+#define SMC_outb(x, ioaddr, reg)	BUG()
+#define SMC_insb(a, r, p, l)		BUG()
+#define SMC_outsb(a, r, p, l)		BUG()
+#endif
+
+#if !defined(SMC_insb) || !defined(SMC_outsb)
+#define SMC_insb(a, r, p, l)		BUG()
+#define SMC_outsb(a, r, p, l)		BUG()
+#endif
+
+#ifndef SMC_CAN_USE_DATACS
+#define SMC_CAN_USE_DATACS	0
+#endif
+
+#ifndef SMC_IO_SHIFT
+#define SMC_IO_SHIFT	0
+#endif
+
+#ifndef	SMC_IRQ_FLAGS
+#define	SMC_IRQ_FLAGS		IRQF_TRIGGER_RISING
+#endif
+
+#ifndef SMC_INTERRUPT_PREAMBLE
+#define SMC_INTERRUPT_PREAMBLE
+#endif
+
+
+/* Because of bank switching, the LAN91x uses only 16 I/O ports */
+#define SMC_IO_EXTENT	(16 << SMC_IO_SHIFT)
+#define SMC_DATA_EXTENT (4)
+
+/*
+ . Bank Select Register:
+ .
+ .		yyyy yyyy 0000 00xx
+ .		xx 		= bank number
+ .		yyyy yyyy	= 0x33, for identification purposes.
+*/
+#define BANK_SELECT		(14 << SMC_IO_SHIFT)
+
+
+// Transmit Control Register
+/* BANK 0  */
+#define TCR_REG(lp) 	SMC_REG(lp, 0x0000, 0)
+#define TCR_ENABLE	0x0001	// When 1 we can transmit
+#define TCR_LOOP	0x0002	// Controls output pin LBK
+#define TCR_FORCOL	0x0004	// When 1 will force a collision
+#define TCR_PAD_EN	0x0080	// When 1 will pad tx frames < 64 bytes w/0
+#define TCR_NOCRC	0x0100	// When 1 will not append CRC to tx frames
+#define TCR_MON_CSN	0x0400	// When 1 tx monitors carrier
+#define TCR_FDUPLX    	0x0800  // When 1 enables full duplex operation
+#define TCR_STP_SQET	0x1000	// When 1 stops tx if Signal Quality Error
+#define TCR_EPH_LOOP	0x2000	// When 1 enables EPH block loopback
+#define TCR_SWFDUP	0x8000	// When 1 enables Switched Full Duplex mode
+
+#define TCR_CLEAR	0	/* do NOTHING */
+/* the default settings for the TCR register : */
+#define TCR_DEFAULT	(TCR_ENABLE | TCR_PAD_EN)
+
+
+// EPH Status Register
+/* BANK 0  */
+#define EPH_STATUS_REG(lp)	SMC_REG(lp, 0x0002, 0)
+#define ES_TX_SUC	0x0001	// Last TX was successful
+#define ES_SNGL_COL	0x0002	// Single collision detected for last tx
+#define ES_MUL_COL	0x0004	// Multiple collisions detected for last tx
+#define ES_LTX_MULT	0x0008	// Last tx was a multicast
+#define ES_16COL	0x0010	// 16 Collisions Reached
+#define ES_SQET		0x0020	// Signal Quality Error Test
+#define ES_LTXBRD	0x0040	// Last tx was a broadcast
+#define ES_TXDEFR	0x0080	// Transmit Deferred
+#define ES_LATCOL	0x0200	// Late collision detected on last tx
+#define ES_LOSTCARR	0x0400	// Lost Carrier Sense
+#define ES_EXC_DEF	0x0800	// Excessive Deferral
+#define ES_CTR_ROL	0x1000	// Counter Roll Over indication
+#define ES_LINK_OK	0x4000	// Driven by inverted value of nLNK pin
+#define ES_TXUNRN	0x8000	// Tx Underrun
+
+
+// Receive Control Register
+/* BANK 0  */
+#define RCR_REG(lp)		SMC_REG(lp, 0x0004, 0)
+#define RCR_RX_ABORT	0x0001	// Set if a rx frame was aborted
+#define RCR_PRMS	0x0002	// Enable promiscuous mode
+#define RCR_ALMUL	0x0004	// When set accepts all multicast frames
+#define RCR_RXEN	0x0100	// IFF this is set, we can receive packets
+#define RCR_STRIP_CRC	0x0200	// When set strips CRC from rx packets
+#define RCR_ABORT_ENB	0x0200	// When set will abort rx on collision
+#define RCR_FILT_CAR	0x0400	// When set filters leading 12 bit s of carrier
+#define RCR_SOFTRST	0x8000 	// resets the chip
+
+/* the normal settings for the RCR register : */
+#define RCR_DEFAULT	(RCR_STRIP_CRC | RCR_RXEN)
+#define RCR_CLEAR	0x0	// set it to a base state
+
+
+// Counter Register
+/* BANK 0  */
+#define COUNTER_REG(lp)	SMC_REG(lp, 0x0006, 0)
+
+
+// Memory Information Register
+/* BANK 0  */
+#define MIR_REG(lp)		SMC_REG(lp, 0x0008, 0)
+
+
+// Receive/Phy Control Register
+/* BANK 0  */
+#define RPC_REG(lp)		SMC_REG(lp, 0x000A, 0)
+#define RPC_SPEED	0x2000	// When 1 PHY is in 100Mbps mode.
+#define RPC_DPLX	0x1000	// When 1 PHY is in Full-Duplex Mode
+#define RPC_ANEG	0x0800	// When 1 PHY is in Auto-Negotiate Mode
+#define RPC_LSXA_SHFT	5	// Bits to shift LS2A,LS1A,LS0A to lsb
+#define RPC_LSXB_SHFT	2	// Bits to get LS2B,LS1B,LS0B to lsb
+
+#ifndef RPC_LSA_DEFAULT
+#define RPC_LSA_DEFAULT	RPC_LED_100
+#endif
+#ifndef RPC_LSB_DEFAULT
+#define RPC_LSB_DEFAULT RPC_LED_FD
+#endif
+
+#define RPC_DEFAULT (RPC_ANEG | RPC_SPEED | RPC_DPLX)
+
+
+/* Bank 0 0x0C is reserved */
+
+// Bank Select Register
+/* All Banks */
+#define BSR_REG		0x000E
+
+
+// Configuration Reg
+/* BANK 1 */
+#define CONFIG_REG(lp)	SMC_REG(lp, 0x0000,	1)
+#define CONFIG_EXT_PHY	0x0200	// 1=external MII, 0=internal Phy
+#define CONFIG_GPCNTRL	0x0400	// Inverse value drives pin nCNTRL
+#define CONFIG_NO_WAIT	0x1000	// When 1 no extra wait states on ISA bus
+#define CONFIG_EPH_POWER_EN 0x8000 // When 0 EPH is placed into low power mode.
+
+// Default is powered-up, Internal Phy, Wait States, and pin nCNTRL=low
+#define CONFIG_DEFAULT	(CONFIG_EPH_POWER_EN)
+
+
+// Base Address Register
+/* BANK 1 */
+#define BASE_REG(lp)	SMC_REG(lp, 0x0002, 1)
+
+
+// Individual Address Registers
+/* BANK 1 */
+#define ADDR0_REG(lp)	SMC_REG(lp, 0x0004, 1)
+#define ADDR1_REG(lp)	SMC_REG(lp, 0x0006, 1)
+#define ADDR2_REG(lp)	SMC_REG(lp, 0x0008, 1)
+
+
+// General Purpose Register
+/* BANK 1 */
+#define GP_REG(lp)		SMC_REG(lp, 0x000A, 1)
+
+
+// Control Register
+/* BANK 1 */
+#define CTL_REG(lp)		SMC_REG(lp, 0x000C, 1)
+#define CTL_RCV_BAD	0x4000 // When 1 bad CRC packets are received
+#define CTL_AUTO_RELEASE 0x0800 // When 1 tx pages are released automatically
+#define CTL_LE_ENABLE	0x0080 // When 1 enables Link Error interrupt
+#define CTL_CR_ENABLE	0x0040 // When 1 enables Counter Rollover interrupt
+#define CTL_TE_ENABLE	0x0020 // When 1 enables Transmit Error interrupt
+#define CTL_EEPROM_SELECT 0x0004 // Controls EEPROM reload & store
+#define CTL_RELOAD	0x0002 // When set reads EEPROM into registers
+#define CTL_STORE	0x0001 // When set stores registers into EEPROM
+
+
+// MMU Command Register
+/* BANK 2 */
+#define MMU_CMD_REG(lp)	SMC_REG(lp, 0x0000, 2)
+#define MC_BUSY		1	// When 1 the last release has not completed
+#define MC_NOP		(0<<5)	// No Op
+#define MC_ALLOC	(1<<5) 	// OR with number of 256 byte packets
+#define MC_RESET	(2<<5)	// Reset MMU to initial state
+#define MC_REMOVE	(3<<5) 	// Remove the current rx packet
+#define MC_RELEASE  	(4<<5) 	// Remove and release the current rx packet
+#define MC_FREEPKT  	(5<<5) 	// Release packet in PNR register
+#define MC_ENQUEUE	(6<<5)	// Enqueue the packet for transmit
+#define MC_RSTTXFIFO	(7<<5)	// Reset the TX FIFOs
+
+
+// Packet Number Register
+/* BANK 2 */
+#define PN_REG(lp)		SMC_REG(lp, 0x0002, 2)
+
+
+// Allocation Result Register
+/* BANK 2 */
+#define AR_REG(lp)		SMC_REG(lp, 0x0003, 2)
+#define AR_FAILED	0x80	// Alocation Failed
+
+
+// TX FIFO Ports Register
+/* BANK 2 */
+#define TXFIFO_REG(lp)	SMC_REG(lp, 0x0004, 2)
+#define TXFIFO_TEMPTY	0x80	// TX FIFO Empty
+
+// RX FIFO Ports Register
+/* BANK 2 */
+#define RXFIFO_REG(lp)	SMC_REG(lp, 0x0005, 2)
+#define RXFIFO_REMPTY	0x80	// RX FIFO Empty
+
+#define FIFO_REG(lp)	SMC_REG(lp, 0x0004, 2)
+
+// Pointer Register
+/* BANK 2 */
+#define PTR_REG(lp)		SMC_REG(lp, 0x0006, 2)
+#define PTR_RCV		0x8000 // 1=Receive area, 0=Transmit area
+#define PTR_AUTOINC 	0x4000 // Auto increment the pointer on each access
+#define PTR_READ	0x2000 // When 1 the operation is a read
+
+
+// Data Register
+/* BANK 2 */
+#define DATA_REG(lp)	SMC_REG(lp, 0x0008, 2)
+
+
+// Interrupt Status/Acknowledge Register
+/* BANK 2 */
+#define INT_REG(lp)		SMC_REG(lp, 0x000C, 2)
+
+
+// Interrupt Mask Register
+/* BANK 2 */
+#define IM_REG(lp)		SMC_REG(lp, 0x000D, 2)
+#define IM_MDINT	0x80 // PHY MI Register 18 Interrupt
+#define IM_ERCV_INT	0x40 // Early Receive Interrupt
+#define IM_EPH_INT	0x20 // Set by Ethernet Protocol Handler section
+#define IM_RX_OVRN_INT	0x10 // Set by Receiver Overruns
+#define IM_ALLOC_INT	0x08 // Set when allocation request is completed
+#define IM_TX_EMPTY_INT	0x04 // Set if the TX FIFO goes empty
+#define IM_TX_INT	0x02 // Transmit Interrupt
+#define IM_RCV_INT	0x01 // Receive Interrupt
+
+
+// Multicast Table Registers
+/* BANK 3 */
+#define MCAST_REG1(lp)	SMC_REG(lp, 0x0000, 3)
+#define MCAST_REG2(lp)	SMC_REG(lp, 0x0002, 3)
+#define MCAST_REG3(lp)	SMC_REG(lp, 0x0004, 3)
+#define MCAST_REG4(lp)	SMC_REG(lp, 0x0006, 3)
+
+
+// Management Interface Register (MII)
+/* BANK 3 */
+#define MII_REG(lp)		SMC_REG(lp, 0x0008, 3)
+#define MII_MSK_CRS100	0x4000 // Disables CRS100 detection during tx half dup
+#define MII_MDOE	0x0008 // MII Output Enable
+#define MII_MCLK	0x0004 // MII Clock, pin MDCLK
+#define MII_MDI		0x0002 // MII Input, pin MDI
+#define MII_MDO		0x0001 // MII Output, pin MDO
+
+
+// Revision Register
+/* BANK 3 */
+/* ( hi: chip id   low: rev # ) */
+#define REV_REG(lp)		SMC_REG(lp, 0x000A, 3)
+
+
+// Early RCV Register
+/* BANK 3 */
+/* this is NOT on SMC9192 */
+#define ERCV_REG(lp)	SMC_REG(lp, 0x000C, 3)
+#define ERCV_RCV_DISCRD	0x0080 // When 1 discards a packet being received
+#define ERCV_THRESHOLD	0x001F // ERCV Threshold Mask
+
+
+// External Register
+/* BANK 7 */
+#define EXT_REG(lp)		SMC_REG(lp, 0x0000, 7)
+
+
+#define CHIP_9192	3
+#define CHIP_9194	4
+#define CHIP_9195	5
+#define CHIP_9196	6
+#define CHIP_91100	7
+#define CHIP_91100FD	8
+#define CHIP_91111FD	9
+
+static const char * chip_ids[ 16 ] =  {
+	NULL, NULL, NULL,
+	/* 3 */ "SMC91C90/91C92",
+	/* 4 */ "SMC91C94",
+	/* 5 */ "SMC91C95",
+	/* 6 */ "SMC91C96",
+	/* 7 */ "SMC91C100",
+	/* 8 */ "SMC91C100FD",
+	/* 9 */ "SMC91C11xFD",
+	NULL, NULL, NULL,
+	NULL, NULL, NULL};
+
+
+/*
+ . Receive status bits
+*/
+#define RS_ALGNERR	0x8000
+#define RS_BRODCAST	0x4000
+#define RS_BADCRC	0x2000
+#define RS_ODDFRAME	0x1000
+#define RS_TOOLONG	0x0800
+#define RS_TOOSHORT	0x0400
+#define RS_MULTICAST	0x0001
+#define RS_ERRORS	(RS_ALGNERR | RS_BADCRC | RS_TOOLONG | RS_TOOSHORT)
+
+
+/*
+ * PHY IDs
+ *  LAN83C183 == LAN91C111 Internal PHY
+ */
+#define PHY_LAN83C183	0x0016f840
+#define PHY_LAN83C180	0x02821c50
+
+/*
+ * PHY Register Addresses (LAN91C111 Internal PHY)
+ *
+ * Generic PHY registers can be found in <linux/mii.h>
+ *
+ * These phy registers are specific to our on-board phy.
+ */
+
+// PHY Configuration Register 1
+#define PHY_CFG1_REG		0x10
+#define PHY_CFG1_LNKDIS		0x8000	// 1=Rx Link Detect Function disabled
+#define PHY_CFG1_XMTDIS		0x4000	// 1=TP Transmitter Disabled
+#define PHY_CFG1_XMTPDN		0x2000	// 1=TP Transmitter Powered Down
+#define PHY_CFG1_BYPSCR		0x0400	// 1=Bypass scrambler/descrambler
+#define PHY_CFG1_UNSCDS		0x0200	// 1=Unscramble Idle Reception Disable
+#define PHY_CFG1_EQLZR		0x0100	// 1=Rx Equalizer Disabled
+#define PHY_CFG1_CABLE		0x0080	// 1=STP(150ohm), 0=UTP(100ohm)
+#define PHY_CFG1_RLVL0		0x0040	// 1=Rx Squelch level reduced by 4.5db
+#define PHY_CFG1_TLVL_SHIFT	2	// Transmit Output Level Adjust
+#define PHY_CFG1_TLVL_MASK	0x003C
+#define PHY_CFG1_TRF_MASK	0x0003	// Transmitter Rise/Fall time
+
+
+// PHY Configuration Register 2
+#define PHY_CFG2_REG		0x11
+#define PHY_CFG2_APOLDIS	0x0020	// 1=Auto Polarity Correction disabled
+#define PHY_CFG2_JABDIS		0x0010	// 1=Jabber disabled
+#define PHY_CFG2_MREG		0x0008	// 1=Multiple register access (MII mgt)
+#define PHY_CFG2_INTMDIO	0x0004	// 1=Interrupt signaled with MDIO pulseo
+
+// PHY Status Output (and Interrupt status) Register
+#define PHY_INT_REG		0x12	// Status Output (Interrupt Status)
+#define PHY_INT_INT		0x8000	// 1=bits have changed since last read
+#define PHY_INT_LNKFAIL		0x4000	// 1=Link Not detected
+#define PHY_INT_LOSSSYNC	0x2000	// 1=Descrambler has lost sync
+#define PHY_INT_CWRD		0x1000	// 1=Invalid 4B5B code detected on rx
+#define PHY_INT_SSD		0x0800	// 1=No Start Of Stream detected on rx
+#define PHY_INT_ESD		0x0400	// 1=No End Of Stream detected on rx
+#define PHY_INT_RPOL		0x0200	// 1=Reverse Polarity detected
+#define PHY_INT_JAB		0x0100	// 1=Jabber detected
+#define PHY_INT_SPDDET		0x0080	// 1=100Base-TX mode, 0=10Base-T mode
+#define PHY_INT_DPLXDET		0x0040	// 1=Device in Full Duplex
+
+// PHY Interrupt/Status Mask Register
+#define PHY_MASK_REG		0x13	// Interrupt Mask
+// Uses the same bit definitions as PHY_INT_REG
+
+
+/*
+ * SMC91C96 ethernet config and status registers.
+ * These are in the "attribute" space.
+ */
+#define ECOR			0x8000
+#define ECOR_RESET		0x80
+#define ECOR_LEVEL_IRQ		0x40
+#define ECOR_WR_ATTRIB		0x04
+#define ECOR_ENABLE		0x01
+
+#define ECSR			0x8002
+#define ECSR_IOIS8		0x20
+#define ECSR_PWRDWN		0x04
+#define ECSR_INT		0x02
+
+#define ATTRIB_SIZE		((64*1024) << SMC_IO_SHIFT)
+
+
+/*
+ * Macros to abstract register access according to the data bus
+ * capabilities.  Please use those and not the in/out primitives.
+ * Note: the following macros do *not* select the bank -- this must
+ * be done separately as needed in the main code.  The SMC_REG() macro
+ * only uses the bank argument for debugging purposes (when enabled).
+ *
+ * Note: despite inline functions being safer, everything leading to this
+ * should preferably be macros to let BUG() display the line number in
+ * the core source code since we're interested in the top call site
+ * not in any inline function location.
+ */
+
+#if SMC_DEBUG > 0
+#define SMC_REG(lp, reg, bank)					\
+	({								\
+		int __b = SMC_CURRENT_BANK(lp);			\
+		if (unlikely((__b & ~0xf0) != (0x3300 | bank))) {	\
+			printk( "%s: bank reg screwed (0x%04x)\n",	\
+				CARDNAME, __b );			\
+			BUG();						\
+		}							\
+		reg<<SMC_IO_SHIFT;					\
+	})
+#else
+#define SMC_REG(lp, reg, bank)	(reg<<SMC_IO_SHIFT)
+#endif
+
+/*
+ * Hack Alert: Some setups just can't write 8 or 16 bits reliably when not
+ * aligned to a 32 bit boundary.  I tell you that does exist!
+ * Fortunately the affected register accesses can be easily worked around
+ * since we can write zeroes to the preceding 16 bits without adverse
+ * effects and use a 32-bit access.
+ *
+ * Enforce it on any 32-bit capable setup for now.
+ */
+#define SMC_MUST_ALIGN_WRITE(lp)	SMC_32BIT(lp)
+
+#define SMC_GET_PN(lp)						\
+	(SMC_8BIT(lp)	? (SMC_inb(ioaddr, PN_REG(lp)))	\
+				: (SMC_inw(ioaddr, PN_REG(lp)) & 0xFF))
+
+#define SMC_SET_PN(lp, x)						\
+	do {								\
+		if (SMC_MUST_ALIGN_WRITE(lp))				\
+			SMC_outl((x)<<16, ioaddr, SMC_REG(lp, 0, 2));	\
+		else if (SMC_8BIT(lp))				\
+			SMC_outb(x, ioaddr, PN_REG(lp));		\
+		else							\
+			SMC_outw(x, ioaddr, PN_REG(lp));		\
+	} while (0)
+
+#define SMC_GET_AR(lp)						\
+	(SMC_8BIT(lp)	? (SMC_inb(ioaddr, AR_REG(lp)))	\
+				: (SMC_inw(ioaddr, PN_REG(lp)) >> 8))
+
+#define SMC_GET_TXFIFO(lp)						\
+	(SMC_8BIT(lp)	? (SMC_inb(ioaddr, TXFIFO_REG(lp)))	\
+				: (SMC_inw(ioaddr, TXFIFO_REG(lp)) & 0xFF))
+
+#define SMC_GET_RXFIFO(lp)						\
+	(SMC_8BIT(lp)	? (SMC_inb(ioaddr, RXFIFO_REG(lp)))	\
+				: (SMC_inw(ioaddr, TXFIFO_REG(lp)) >> 8))
+
+#define SMC_GET_INT(lp)						\
+	(SMC_8BIT(lp)	? (SMC_inb(ioaddr, INT_REG(lp)))	\
+				: (SMC_inw(ioaddr, INT_REG(lp)) & 0xFF))
+
+#define SMC_ACK_INT(lp, x)						\
+	do {								\
+		if (SMC_8BIT(lp))					\
+			SMC_outb(x, ioaddr, INT_REG(lp));		\
+		else {							\
+			unsigned long __flags;				\
+			int __mask;					\
+			local_irq_save(__flags);			\
+			__mask = SMC_inw(ioaddr, INT_REG(lp)) & ~0xff; \
+			SMC_outw(__mask | (x), ioaddr, INT_REG(lp));	\
+			local_irq_restore(__flags);			\
+		}							\
+	} while (0)
+
+#define SMC_GET_INT_MASK(lp)						\
+	(SMC_8BIT(lp)	? (SMC_inb(ioaddr, IM_REG(lp)))	\
+				: (SMC_inw(ioaddr, INT_REG(lp)) >> 8))
+
+#define SMC_SET_INT_MASK(lp, x)					\
+	do {								\
+		if (SMC_8BIT(lp))					\
+			SMC_outb(x, ioaddr, IM_REG(lp));		\
+		else							\
+			SMC_outw((x) << 8, ioaddr, INT_REG(lp));	\
+	} while (0)
+
+#define SMC_CURRENT_BANK(lp)	SMC_inw(ioaddr, BANK_SELECT)
+
+#define SMC_SELECT_BANK(lp, x)					\
+	do {								\
+		if (SMC_MUST_ALIGN_WRITE(lp))				\
+			SMC_outl((x)<<16, ioaddr, 12<<SMC_IO_SHIFT);	\
+		else							\
+			SMC_outw(x, ioaddr, BANK_SELECT);		\
+	} while (0)
+
+#define SMC_GET_BASE(lp)		SMC_inw(ioaddr, BASE_REG(lp))
+
+#define SMC_SET_BASE(lp, x)		SMC_outw(x, ioaddr, BASE_REG(lp))
+
+#define SMC_GET_CONFIG(lp)	SMC_inw(ioaddr, CONFIG_REG(lp))
+
+#define SMC_SET_CONFIG(lp, x)	SMC_outw(x, ioaddr, CONFIG_REG(lp))
+
+#define SMC_GET_COUNTER(lp)	SMC_inw(ioaddr, COUNTER_REG(lp))
+
+#define SMC_GET_CTL(lp)		SMC_inw(ioaddr, CTL_REG(lp))
+
+#define SMC_SET_CTL(lp, x)		SMC_outw(x, ioaddr, CTL_REG(lp))
+
+#define SMC_GET_MII(lp)		SMC_inw(ioaddr, MII_REG(lp))
+
+#define SMC_GET_GP(lp)		SMC_inw(ioaddr, GP_REG(lp))
+
+#define SMC_SET_GP(lp, x)						\
+	do {								\
+		if (SMC_MUST_ALIGN_WRITE(lp))				\
+			SMC_outl((x)<<16, ioaddr, SMC_REG(lp, 8, 1));	\
+		else							\
+			SMC_outw(x, ioaddr, GP_REG(lp));		\
+	} while (0)
+
+#define SMC_SET_MII(lp, x)		SMC_outw(x, ioaddr, MII_REG(lp))
+
+#define SMC_GET_MIR(lp)		SMC_inw(ioaddr, MIR_REG(lp))
+
+#define SMC_SET_MIR(lp, x)		SMC_outw(x, ioaddr, MIR_REG(lp))
+
+#define SMC_GET_MMU_CMD(lp)	SMC_inw(ioaddr, MMU_CMD_REG(lp))
+
+#define SMC_SET_MMU_CMD(lp, x)	SMC_outw(x, ioaddr, MMU_CMD_REG(lp))
+
+#define SMC_GET_FIFO(lp)		SMC_inw(ioaddr, FIFO_REG(lp))
+
+#define SMC_GET_PTR(lp)		SMC_inw(ioaddr, PTR_REG(lp))
+
+#define SMC_SET_PTR(lp, x)						\
+	do {								\
+		if (SMC_MUST_ALIGN_WRITE(lp))				\
+			SMC_outl((x)<<16, ioaddr, SMC_REG(lp, 4, 2));	\
+		else							\
+			SMC_outw(x, ioaddr, PTR_REG(lp));		\
+	} while (0)
+
+#define SMC_GET_EPH_STATUS(lp)	SMC_inw(ioaddr, EPH_STATUS_REG(lp))
+
+#define SMC_GET_RCR(lp)		SMC_inw(ioaddr, RCR_REG(lp))
+
+#define SMC_SET_RCR(lp, x)		SMC_outw(x, ioaddr, RCR_REG(lp))
+
+#define SMC_GET_REV(lp)		SMC_inw(ioaddr, REV_REG(lp))
+
+#define SMC_GET_RPC(lp)		SMC_inw(ioaddr, RPC_REG(lp))
+
+#define SMC_SET_RPC(lp, x)						\
+	do {								\
+		if (SMC_MUST_ALIGN_WRITE(lp))				\
+			SMC_outl((x)<<16, ioaddr, SMC_REG(lp, 8, 0));	\
+		else							\
+			SMC_outw(x, ioaddr, RPC_REG(lp));		\
+	} while (0)
+
+#define SMC_GET_TCR(lp)		SMC_inw(ioaddr, TCR_REG(lp))
+
+#define SMC_SET_TCR(lp, x)		SMC_outw(x, ioaddr, TCR_REG(lp))
+
+#ifndef SMC_GET_MAC_ADDR
+#define SMC_GET_MAC_ADDR(lp, addr)					\
+	do {								\
+		unsigned int __v;					\
+		__v = SMC_inw(ioaddr, ADDR0_REG(lp));			\
+		addr[0] = __v; addr[1] = __v >> 8;			\
+		__v = SMC_inw(ioaddr, ADDR1_REG(lp));			\
+		addr[2] = __v; addr[3] = __v >> 8;			\
+		__v = SMC_inw(ioaddr, ADDR2_REG(lp));			\
+		addr[4] = __v; addr[5] = __v >> 8;			\
+	} while (0)
+#endif
+
+#define SMC_SET_MAC_ADDR(lp, addr)					\
+	do {								\
+		SMC_outw(addr[0]|(addr[1] << 8), ioaddr, ADDR0_REG(lp)); \
+		SMC_outw(addr[2]|(addr[3] << 8), ioaddr, ADDR1_REG(lp)); \
+		SMC_outw(addr[4]|(addr[5] << 8), ioaddr, ADDR2_REG(lp)); \
+	} while (0)
+
+#define SMC_SET_MCAST(lp, x)						\
+	do {								\
+		const unsigned char *mt = (x);				\
+		SMC_outw(mt[0] | (mt[1] << 8), ioaddr, MCAST_REG1(lp)); \
+		SMC_outw(mt[2] | (mt[3] << 8), ioaddr, MCAST_REG2(lp)); \
+		SMC_outw(mt[4] | (mt[5] << 8), ioaddr, MCAST_REG3(lp)); \
+		SMC_outw(mt[6] | (mt[7] << 8), ioaddr, MCAST_REG4(lp)); \
+	} while (0)
+
+#define SMC_PUT_PKT_HDR(lp, status, length)				\
+	do {								\
+		if (SMC_32BIT(lp))					\
+			SMC_outl((status) | (length)<<16, ioaddr,	\
+				 DATA_REG(lp));			\
+		else {							\
+			SMC_outw(status, ioaddr, DATA_REG(lp));	\
+			SMC_outw(length, ioaddr, DATA_REG(lp));	\
+		}							\
+	} while (0)
+
+#define SMC_GET_PKT_HDR(lp, status, length)				\
+	do {								\
+		if (SMC_32BIT(lp)) {				\
+			unsigned int __val = SMC_inl(ioaddr, DATA_REG(lp)); \
+			(status) = __val & 0xffff;			\
+			(length) = __val >> 16;				\
+		} else {						\
+			(status) = SMC_inw(ioaddr, DATA_REG(lp));	\
+			(length) = SMC_inw(ioaddr, DATA_REG(lp));	\
+		}							\
+	} while (0)
+
+#define SMC_PUSH_DATA(lp, p, l)					\
+	do {								\
+		if (SMC_32BIT(lp)) {				\
+			void *__ptr = (p);				\
+			int __len = (l);				\
+			void __iomem *__ioaddr = ioaddr;		\
+			if (__len >= 2 && (unsigned long)__ptr & 2) {	\
+				__len -= 2;				\
+				SMC_outw(*(u16 *)__ptr, ioaddr,		\
+					DATA_REG(lp));		\
+				__ptr += 2;				\
+			}						\
+			if (SMC_CAN_USE_DATACS && lp->datacs)		\
+				__ioaddr = lp->datacs;			\
+			SMC_outsl(__ioaddr, DATA_REG(lp), __ptr, __len>>2); \
+			if (__len & 2) {				\
+				__ptr += (__len & ~3);			\
+				SMC_outw(*((u16 *)__ptr), ioaddr,	\
+					 DATA_REG(lp));		\
+			}						\
+		} else if (SMC_16BIT(lp))				\
+			SMC_outsw(ioaddr, DATA_REG(lp), p, (l) >> 1);	\
+		else if (SMC_8BIT(lp))				\
+			SMC_outsb(ioaddr, DATA_REG(lp), p, l);	\
+	} while (0)
+
+#define SMC_PULL_DATA(lp, p, l)					\
+	do {								\
+		if (SMC_32BIT(lp)) {				\
+			void *__ptr = (p);				\
+			int __len = (l);				\
+			void __iomem *__ioaddr = ioaddr;		\
+			if ((unsigned long)__ptr & 2) {			\
+				/*					\
+				 * We want 32bit alignment here.	\
+				 * Since some buses perform a full	\
+				 * 32bit fetch even for 16bit data	\
+				 * we can't use SMC_inw() here.		\
+				 * Back both source (on-chip) and	\
+				 * destination pointers of 2 bytes.	\
+				 * This is possible since the call to	\
+				 * SMC_GET_PKT_HDR() already advanced	\
+				 * the source pointer of 4 bytes, and	\
+				 * the skb_reserve(skb, 2) advanced	\
+				 * the destination pointer of 2 bytes.	\
+				 */					\
+				__ptr -= 2;				\
+				__len += 2;				\
+				SMC_SET_PTR(lp,			\
+					2|PTR_READ|PTR_RCV|PTR_AUTOINC); \
+			}						\
+			if (SMC_CAN_USE_DATACS && lp->datacs)		\
+				__ioaddr = lp->datacs;			\
+			__len += 2;					\
+			SMC_insl(__ioaddr, DATA_REG(lp), __ptr, __len>>2); \
+		} else if (SMC_16BIT(lp))				\
+			SMC_insw(ioaddr, DATA_REG(lp), p, (l) >> 1);	\
+		else if (SMC_8BIT(lp))				\
+			SMC_insb(ioaddr, DATA_REG(lp), p, l);		\
+	} while (0)
+
+#endif  /* _SMC91X_H_ */
diff --git a/drivers/net/ethernet/smsc/smsc911x.c b/drivers/net/ethernet/smsc/smsc911x.c
new file mode 100644
index 0000000..75c08a5
--- /dev/null
+++ b/drivers/net/ethernet/smsc/smsc911x.c
@@ -0,0 +1,2404 @@
+/***************************************************************************
+ *
+ * Copyright (C) 2004-2008 SMSC
+ * Copyright (C) 2005-2008 ARM
+ *
+ * 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.
+ *
+ ***************************************************************************
+ * Rewritten, heavily based on smsc911x simple driver by SMSC.
+ * Partly uses io macros from smc91x.c by Nicolas Pitre
+ *
+ * Supported devices:
+ *   LAN9115, LAN9116, LAN9117, LAN9118
+ *   LAN9215, LAN9216, LAN9217, LAN9218
+ *   LAN9210, LAN9211
+ *   LAN9220, LAN9221
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/crc32.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/bug.h>
+#include <linux/bitops.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+#include <linux/swab.h>
+#include <linux/phy.h>
+#include <linux/smsc911x.h>
+#include <linux/device.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/of_net.h>
+#include "smsc911x.h"
+
+#define SMSC_CHIPNAME		"smsc911x"
+#define SMSC_MDIONAME		"smsc911x-mdio"
+#define SMSC_DRV_VERSION	"2008-10-21"
+
+MODULE_LICENSE("GPL");
+MODULE_VERSION(SMSC_DRV_VERSION);
+MODULE_ALIAS("platform:smsc911x");
+
+#if USE_DEBUG > 0
+static int debug = 16;
+#else
+static int debug = 3;
+#endif
+
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)");
+
+struct smsc911x_data;
+
+struct smsc911x_ops {
+	u32 (*reg_read)(struct smsc911x_data *pdata, u32 reg);
+	void (*reg_write)(struct smsc911x_data *pdata, u32 reg, u32 val);
+	void (*rx_readfifo)(struct smsc911x_data *pdata,
+				unsigned int *buf, unsigned int wordcount);
+	void (*tx_writefifo)(struct smsc911x_data *pdata,
+				unsigned int *buf, unsigned int wordcount);
+};
+
+struct smsc911x_data {
+	void __iomem *ioaddr;
+
+	unsigned int idrev;
+
+	/* used to decide which workarounds apply */
+	unsigned int generation;
+
+	/* device configuration (copied from platform_data during probe) */
+	struct smsc911x_platform_config config;
+
+	/* This needs to be acquired before calling any of below:
+	 * smsc911x_mac_read(), smsc911x_mac_write()
+	 */
+	spinlock_t mac_lock;
+
+	/* spinlock to ensure register accesses are serialised */
+	spinlock_t dev_lock;
+
+	struct phy_device *phy_dev;
+	struct mii_bus *mii_bus;
+	int phy_irq[PHY_MAX_ADDR];
+	unsigned int using_extphy;
+	int last_duplex;
+	int last_carrier;
+
+	u32 msg_enable;
+	unsigned int gpio_setting;
+	unsigned int gpio_orig_setting;
+	struct net_device *dev;
+	struct napi_struct napi;
+
+	unsigned int software_irq_signal;
+
+#ifdef USE_PHY_WORK_AROUND
+#define MIN_PACKET_SIZE (64)
+	char loopback_tx_pkt[MIN_PACKET_SIZE];
+	char loopback_rx_pkt[MIN_PACKET_SIZE];
+	unsigned int resetcount;
+#endif
+
+	/* Members for Multicast filter workaround */
+	unsigned int multicast_update_pending;
+	unsigned int set_bits_mask;
+	unsigned int clear_bits_mask;
+	unsigned int hashhi;
+	unsigned int hashlo;
+
+	/* register access functions */
+	const struct smsc911x_ops *ops;
+};
+
+/* Easy access to information */
+#define __smsc_shift(pdata, reg) ((reg) << ((pdata)->config.shift))
+
+static inline u32 __smsc911x_reg_read(struct smsc911x_data *pdata, u32 reg)
+{
+	if (pdata->config.flags & SMSC911X_USE_32BIT)
+		return readl(pdata->ioaddr + reg);
+
+	if (pdata->config.flags & SMSC911X_USE_16BIT)
+		return ((readw(pdata->ioaddr + reg) & 0xFFFF) |
+			((readw(pdata->ioaddr + reg + 2) & 0xFFFF) << 16));
+
+	BUG();
+	return 0;
+}
+
+static inline u32
+__smsc911x_reg_read_shift(struct smsc911x_data *pdata, u32 reg)
+{
+	if (pdata->config.flags & SMSC911X_USE_32BIT)
+		return readl(pdata->ioaddr + __smsc_shift(pdata, reg));
+
+	if (pdata->config.flags & SMSC911X_USE_16BIT)
+		return (readw(pdata->ioaddr +
+				__smsc_shift(pdata, reg)) & 0xFFFF) |
+			((readw(pdata->ioaddr +
+			__smsc_shift(pdata, reg + 2)) & 0xFFFF) << 16);
+
+	BUG();
+	return 0;
+}
+
+static inline u32 smsc911x_reg_read(struct smsc911x_data *pdata, u32 reg)
+{
+	u32 data;
+	unsigned long flags;
+
+	spin_lock_irqsave(&pdata->dev_lock, flags);
+	data = pdata->ops->reg_read(pdata, reg);
+	spin_unlock_irqrestore(&pdata->dev_lock, flags);
+
+	return data;
+}
+
+static inline void __smsc911x_reg_write(struct smsc911x_data *pdata, u32 reg,
+					u32 val)
+{
+	if (pdata->config.flags & SMSC911X_USE_32BIT) {
+		writel(val, pdata->ioaddr + reg);
+		return;
+	}
+
+	if (pdata->config.flags & SMSC911X_USE_16BIT) {
+		writew(val & 0xFFFF, pdata->ioaddr + reg);
+		writew((val >> 16) & 0xFFFF, pdata->ioaddr + reg + 2);
+		return;
+	}
+
+	BUG();
+}
+
+static inline void
+__smsc911x_reg_write_shift(struct smsc911x_data *pdata, u32 reg, u32 val)
+{
+	if (pdata->config.flags & SMSC911X_USE_32BIT) {
+		writel(val, pdata->ioaddr + __smsc_shift(pdata, reg));
+		return;
+	}
+
+	if (pdata->config.flags & SMSC911X_USE_16BIT) {
+		writew(val & 0xFFFF,
+			pdata->ioaddr + __smsc_shift(pdata, reg));
+		writew((val >> 16) & 0xFFFF,
+			pdata->ioaddr + __smsc_shift(pdata, reg + 2));
+		return;
+	}
+
+	BUG();
+}
+
+static inline void smsc911x_reg_write(struct smsc911x_data *pdata, u32 reg,
+				      u32 val)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&pdata->dev_lock, flags);
+	pdata->ops->reg_write(pdata, reg, val);
+	spin_unlock_irqrestore(&pdata->dev_lock, flags);
+}
+
+/* Writes a packet to the TX_DATA_FIFO */
+static inline void
+smsc911x_tx_writefifo(struct smsc911x_data *pdata, unsigned int *buf,
+		      unsigned int wordcount)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&pdata->dev_lock, flags);
+
+	if (pdata->config.flags & SMSC911X_SWAP_FIFO) {
+		while (wordcount--)
+			__smsc911x_reg_write(pdata, TX_DATA_FIFO,
+					     swab32(*buf++));
+		goto out;
+	}
+
+	if (pdata->config.flags & SMSC911X_USE_32BIT) {
+		writesl(pdata->ioaddr + TX_DATA_FIFO, buf, wordcount);
+		goto out;
+	}
+
+	if (pdata->config.flags & SMSC911X_USE_16BIT) {
+		while (wordcount--)
+			__smsc911x_reg_write(pdata, TX_DATA_FIFO, *buf++);
+		goto out;
+	}
+
+	BUG();
+out:
+	spin_unlock_irqrestore(&pdata->dev_lock, flags);
+}
+
+/* Writes a packet to the TX_DATA_FIFO - shifted version */
+static inline void
+smsc911x_tx_writefifo_shift(struct smsc911x_data *pdata, unsigned int *buf,
+		      unsigned int wordcount)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&pdata->dev_lock, flags);
+
+	if (pdata->config.flags & SMSC911X_SWAP_FIFO) {
+		while (wordcount--)
+			__smsc911x_reg_write_shift(pdata, TX_DATA_FIFO,
+					     swab32(*buf++));
+		goto out;
+	}
+
+	if (pdata->config.flags & SMSC911X_USE_32BIT) {
+		writesl(pdata->ioaddr + __smsc_shift(pdata,
+						TX_DATA_FIFO), buf, wordcount);
+		goto out;
+	}
+
+	if (pdata->config.flags & SMSC911X_USE_16BIT) {
+		while (wordcount--)
+			__smsc911x_reg_write_shift(pdata,
+						 TX_DATA_FIFO, *buf++);
+		goto out;
+	}
+
+	BUG();
+out:
+	spin_unlock_irqrestore(&pdata->dev_lock, flags);
+}
+
+/* Reads a packet out of the RX_DATA_FIFO */
+static inline void
+smsc911x_rx_readfifo(struct smsc911x_data *pdata, unsigned int *buf,
+		     unsigned int wordcount)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&pdata->dev_lock, flags);
+
+	if (pdata->config.flags & SMSC911X_SWAP_FIFO) {
+		while (wordcount--)
+			*buf++ = swab32(__smsc911x_reg_read(pdata,
+							    RX_DATA_FIFO));
+		goto out;
+	}
+
+	if (pdata->config.flags & SMSC911X_USE_32BIT) {
+		readsl(pdata->ioaddr + RX_DATA_FIFO, buf, wordcount);
+		goto out;
+	}
+
+	if (pdata->config.flags & SMSC911X_USE_16BIT) {
+		while (wordcount--)
+			*buf++ = __smsc911x_reg_read(pdata, RX_DATA_FIFO);
+		goto out;
+	}
+
+	BUG();
+out:
+	spin_unlock_irqrestore(&pdata->dev_lock, flags);
+}
+
+/* Reads a packet out of the RX_DATA_FIFO - shifted version */
+static inline void
+smsc911x_rx_readfifo_shift(struct smsc911x_data *pdata, unsigned int *buf,
+		     unsigned int wordcount)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&pdata->dev_lock, flags);
+
+	if (pdata->config.flags & SMSC911X_SWAP_FIFO) {
+		while (wordcount--)
+			*buf++ = swab32(__smsc911x_reg_read_shift(pdata,
+							    RX_DATA_FIFO));
+		goto out;
+	}
+
+	if (pdata->config.flags & SMSC911X_USE_32BIT) {
+		readsl(pdata->ioaddr + __smsc_shift(pdata,
+						RX_DATA_FIFO), buf, wordcount);
+		goto out;
+	}
+
+	if (pdata->config.flags & SMSC911X_USE_16BIT) {
+		while (wordcount--)
+			*buf++ = __smsc911x_reg_read_shift(pdata,
+								RX_DATA_FIFO);
+		goto out;
+	}
+
+	BUG();
+out:
+	spin_unlock_irqrestore(&pdata->dev_lock, flags);
+}
+
+/* waits for MAC not busy, with timeout.  Only called by smsc911x_mac_read
+ * and smsc911x_mac_write, so assumes mac_lock is held */
+static int smsc911x_mac_complete(struct smsc911x_data *pdata)
+{
+	int i;
+	u32 val;
+
+	SMSC_ASSERT_MAC_LOCK(pdata);
+
+	for (i = 0; i < 40; i++) {
+		val = smsc911x_reg_read(pdata, MAC_CSR_CMD);
+		if (!(val & MAC_CSR_CMD_CSR_BUSY_))
+			return 0;
+	}
+	SMSC_WARN(pdata, hw, "Timed out waiting for MAC not BUSY. "
+		  "MAC_CSR_CMD: 0x%08X", val);
+	return -EIO;
+}
+
+/* Fetches a MAC register value. Assumes mac_lock is acquired */
+static u32 smsc911x_mac_read(struct smsc911x_data *pdata, unsigned int offset)
+{
+	unsigned int temp;
+
+	SMSC_ASSERT_MAC_LOCK(pdata);
+
+	temp = smsc911x_reg_read(pdata, MAC_CSR_CMD);
+	if (unlikely(temp & MAC_CSR_CMD_CSR_BUSY_)) {
+		SMSC_WARN(pdata, hw, "MAC busy at entry");
+		return 0xFFFFFFFF;
+	}
+
+	/* Send the MAC cmd */
+	smsc911x_reg_write(pdata, MAC_CSR_CMD, ((offset & 0xFF) |
+		MAC_CSR_CMD_CSR_BUSY_ | MAC_CSR_CMD_R_NOT_W_));
+
+	/* Workaround for hardware read-after-write restriction */
+	temp = smsc911x_reg_read(pdata, BYTE_TEST);
+
+	/* Wait for the read to complete */
+	if (likely(smsc911x_mac_complete(pdata) == 0))
+		return smsc911x_reg_read(pdata, MAC_CSR_DATA);
+
+	SMSC_WARN(pdata, hw, "MAC busy after read");
+	return 0xFFFFFFFF;
+}
+
+/* Set a mac register, mac_lock must be acquired before calling */
+static void smsc911x_mac_write(struct smsc911x_data *pdata,
+			       unsigned int offset, u32 val)
+{
+	unsigned int temp;
+
+	SMSC_ASSERT_MAC_LOCK(pdata);
+
+	temp = smsc911x_reg_read(pdata, MAC_CSR_CMD);
+	if (unlikely(temp & MAC_CSR_CMD_CSR_BUSY_)) {
+		SMSC_WARN(pdata, hw,
+			  "smsc911x_mac_write failed, MAC busy at entry");
+		return;
+	}
+
+	/* Send data to write */
+	smsc911x_reg_write(pdata, MAC_CSR_DATA, val);
+
+	/* Write the actual data */
+	smsc911x_reg_write(pdata, MAC_CSR_CMD, ((offset & 0xFF) |
+		MAC_CSR_CMD_CSR_BUSY_));
+
+	/* Workaround for hardware read-after-write restriction */
+	temp = smsc911x_reg_read(pdata, BYTE_TEST);
+
+	/* Wait for the write to complete */
+	if (likely(smsc911x_mac_complete(pdata) == 0))
+		return;
+
+	SMSC_WARN(pdata, hw, "smsc911x_mac_write failed, MAC busy after write");
+}
+
+/* Get a phy register */
+static int smsc911x_mii_read(struct mii_bus *bus, int phyaddr, int regidx)
+{
+	struct smsc911x_data *pdata = (struct smsc911x_data *)bus->priv;
+	unsigned long flags;
+	unsigned int addr;
+	int i, reg;
+
+	spin_lock_irqsave(&pdata->mac_lock, flags);
+
+	/* Confirm MII not busy */
+	if (unlikely(smsc911x_mac_read(pdata, MII_ACC) & MII_ACC_MII_BUSY_)) {
+		SMSC_WARN(pdata, hw, "MII is busy in smsc911x_mii_read???");
+		reg = -EIO;
+		goto out;
+	}
+
+	/* Set the address, index & direction (read from PHY) */
+	addr = ((phyaddr & 0x1F) << 11) | ((regidx & 0x1F) << 6);
+	smsc911x_mac_write(pdata, MII_ACC, addr);
+
+	/* Wait for read to complete w/ timeout */
+	for (i = 0; i < 100; i++)
+		if (!(smsc911x_mac_read(pdata, MII_ACC) & MII_ACC_MII_BUSY_)) {
+			reg = smsc911x_mac_read(pdata, MII_DATA);
+			goto out;
+		}
+
+	SMSC_WARN(pdata, hw, "Timed out waiting for MII read to finish");
+	reg = -EIO;
+
+out:
+	spin_unlock_irqrestore(&pdata->mac_lock, flags);
+	return reg;
+}
+
+/* Set a phy register */
+static int smsc911x_mii_write(struct mii_bus *bus, int phyaddr, int regidx,
+			   u16 val)
+{
+	struct smsc911x_data *pdata = (struct smsc911x_data *)bus->priv;
+	unsigned long flags;
+	unsigned int addr;
+	int i, reg;
+
+	spin_lock_irqsave(&pdata->mac_lock, flags);
+
+	/* Confirm MII not busy */
+	if (unlikely(smsc911x_mac_read(pdata, MII_ACC) & MII_ACC_MII_BUSY_)) {
+		SMSC_WARN(pdata, hw, "MII is busy in smsc911x_mii_write???");
+		reg = -EIO;
+		goto out;
+	}
+
+	/* Put the data to write in the MAC */
+	smsc911x_mac_write(pdata, MII_DATA, val);
+
+	/* Set the address, index & direction (write to PHY) */
+	addr = ((phyaddr & 0x1F) << 11) | ((regidx & 0x1F) << 6) |
+		MII_ACC_MII_WRITE_;
+	smsc911x_mac_write(pdata, MII_ACC, addr);
+
+	/* Wait for write to complete w/ timeout */
+	for (i = 0; i < 100; i++)
+		if (!(smsc911x_mac_read(pdata, MII_ACC) & MII_ACC_MII_BUSY_)) {
+			reg = 0;
+			goto out;
+		}
+
+	SMSC_WARN(pdata, hw, "Timed out waiting for MII write to finish");
+	reg = -EIO;
+
+out:
+	spin_unlock_irqrestore(&pdata->mac_lock, flags);
+	return reg;
+}
+
+/* Switch to external phy. Assumes tx and rx are stopped. */
+static void smsc911x_phy_enable_external(struct smsc911x_data *pdata)
+{
+	unsigned int hwcfg = smsc911x_reg_read(pdata, HW_CFG);
+
+	/* Disable phy clocks to the MAC */
+	hwcfg &= (~HW_CFG_PHY_CLK_SEL_);
+	hwcfg |= HW_CFG_PHY_CLK_SEL_CLK_DIS_;
+	smsc911x_reg_write(pdata, HW_CFG, hwcfg);
+	udelay(10);	/* Enough time for clocks to stop */
+
+	/* Switch to external phy */
+	hwcfg |= HW_CFG_EXT_PHY_EN_;
+	smsc911x_reg_write(pdata, HW_CFG, hwcfg);
+
+	/* Enable phy clocks to the MAC */
+	hwcfg &= (~HW_CFG_PHY_CLK_SEL_);
+	hwcfg |= HW_CFG_PHY_CLK_SEL_EXT_PHY_;
+	smsc911x_reg_write(pdata, HW_CFG, hwcfg);
+	udelay(10);	/* Enough time for clocks to restart */
+
+	hwcfg |= HW_CFG_SMI_SEL_;
+	smsc911x_reg_write(pdata, HW_CFG, hwcfg);
+}
+
+/* Autodetects and enables external phy if present on supported chips.
+ * autodetection can be overridden by specifying SMSC911X_FORCE_INTERNAL_PHY
+ * or SMSC911X_FORCE_EXTERNAL_PHY in the platform_data flags. */
+static void smsc911x_phy_initialise_external(struct smsc911x_data *pdata)
+{
+	unsigned int hwcfg = smsc911x_reg_read(pdata, HW_CFG);
+
+	if (pdata->config.flags & SMSC911X_FORCE_INTERNAL_PHY) {
+		SMSC_TRACE(pdata, hw, "Forcing internal PHY");
+		pdata->using_extphy = 0;
+	} else if (pdata->config.flags & SMSC911X_FORCE_EXTERNAL_PHY) {
+		SMSC_TRACE(pdata, hw, "Forcing external PHY");
+		smsc911x_phy_enable_external(pdata);
+		pdata->using_extphy = 1;
+	} else if (hwcfg & HW_CFG_EXT_PHY_DET_) {
+		SMSC_TRACE(pdata, hw,
+			   "HW_CFG EXT_PHY_DET set, using external PHY");
+		smsc911x_phy_enable_external(pdata);
+		pdata->using_extphy = 1;
+	} else {
+		SMSC_TRACE(pdata, hw,
+			   "HW_CFG EXT_PHY_DET clear, using internal PHY");
+		pdata->using_extphy = 0;
+	}
+}
+
+/* Fetches a tx status out of the status fifo */
+static unsigned int smsc911x_tx_get_txstatus(struct smsc911x_data *pdata)
+{
+	unsigned int result =
+	    smsc911x_reg_read(pdata, TX_FIFO_INF) & TX_FIFO_INF_TSUSED_;
+
+	if (result != 0)
+		result = smsc911x_reg_read(pdata, TX_STATUS_FIFO);
+
+	return result;
+}
+
+/* Fetches the next rx status */
+static unsigned int smsc911x_rx_get_rxstatus(struct smsc911x_data *pdata)
+{
+	unsigned int result =
+	    smsc911x_reg_read(pdata, RX_FIFO_INF) & RX_FIFO_INF_RXSUSED_;
+
+	if (result != 0)
+		result = smsc911x_reg_read(pdata, RX_STATUS_FIFO);
+
+	return result;
+}
+
+#ifdef USE_PHY_WORK_AROUND
+static int smsc911x_phy_check_loopbackpkt(struct smsc911x_data *pdata)
+{
+	unsigned int tries;
+	u32 wrsz;
+	u32 rdsz;
+	ulong bufp;
+
+	for (tries = 0; tries < 10; tries++) {
+		unsigned int txcmd_a;
+		unsigned int txcmd_b;
+		unsigned int status;
+		unsigned int pktlength;
+		unsigned int i;
+
+		/* Zero-out rx packet memory */
+		memset(pdata->loopback_rx_pkt, 0, MIN_PACKET_SIZE);
+
+		/* Write tx packet to 118 */
+		txcmd_a = (u32)((ulong)pdata->loopback_tx_pkt & 0x03) << 16;
+		txcmd_a |= TX_CMD_A_FIRST_SEG_ | TX_CMD_A_LAST_SEG_;
+		txcmd_a |= MIN_PACKET_SIZE;
+
+		txcmd_b = MIN_PACKET_SIZE << 16 | MIN_PACKET_SIZE;
+
+		smsc911x_reg_write(pdata, TX_DATA_FIFO, txcmd_a);
+		smsc911x_reg_write(pdata, TX_DATA_FIFO, txcmd_b);
+
+		bufp = (ulong)pdata->loopback_tx_pkt & (~0x3);
+		wrsz = MIN_PACKET_SIZE + 3;
+		wrsz += (u32)((ulong)pdata->loopback_tx_pkt & 0x3);
+		wrsz >>= 2;
+
+		pdata->ops->tx_writefifo(pdata, (unsigned int *)bufp, wrsz);
+
+		/* Wait till transmit is done */
+		i = 60;
+		do {
+			udelay(5);
+			status = smsc911x_tx_get_txstatus(pdata);
+		} while ((i--) && (!status));
+
+		if (!status) {
+			SMSC_WARN(pdata, hw,
+				  "Failed to transmit during loopback test");
+			continue;
+		}
+		if (status & TX_STS_ES_) {
+			SMSC_WARN(pdata, hw,
+				  "Transmit encountered errors during loopback test");
+			continue;
+		}
+
+		/* Wait till receive is done */
+		i = 60;
+		do {
+			udelay(5);
+			status = smsc911x_rx_get_rxstatus(pdata);
+		} while ((i--) && (!status));
+
+		if (!status) {
+			SMSC_WARN(pdata, hw,
+				  "Failed to receive during loopback test");
+			continue;
+		}
+		if (status & RX_STS_ES_) {
+			SMSC_WARN(pdata, hw,
+				  "Receive encountered errors during loopback test");
+			continue;
+		}
+
+		pktlength = ((status & 0x3FFF0000UL) >> 16);
+		bufp = (ulong)pdata->loopback_rx_pkt;
+		rdsz = pktlength + 3;
+		rdsz += (u32)((ulong)pdata->loopback_rx_pkt & 0x3);
+		rdsz >>= 2;
+
+		pdata->ops->rx_readfifo(pdata, (unsigned int *)bufp, rdsz);
+
+		if (pktlength != (MIN_PACKET_SIZE + 4)) {
+			SMSC_WARN(pdata, hw, "Unexpected packet size "
+				  "during loop back test, size=%d, will retry",
+				  pktlength);
+		} else {
+			unsigned int j;
+			int mismatch = 0;
+			for (j = 0; j < MIN_PACKET_SIZE; j++) {
+				if (pdata->loopback_tx_pkt[j]
+				    != pdata->loopback_rx_pkt[j]) {
+					mismatch = 1;
+					break;
+				}
+			}
+			if (!mismatch) {
+				SMSC_TRACE(pdata, hw, "Successfully verified "
+					   "loopback packet");
+				return 0;
+			} else {
+				SMSC_WARN(pdata, hw, "Data mismatch "
+					  "during loop back test, will retry");
+			}
+		}
+	}
+
+	return -EIO;
+}
+
+static int smsc911x_phy_reset(struct smsc911x_data *pdata)
+{
+	struct phy_device *phy_dev = pdata->phy_dev;
+	unsigned int temp;
+	unsigned int i = 100000;
+
+	BUG_ON(!phy_dev);
+	BUG_ON(!phy_dev->bus);
+
+	SMSC_TRACE(pdata, hw, "Performing PHY BCR Reset");
+	smsc911x_mii_write(phy_dev->bus, phy_dev->addr, MII_BMCR, BMCR_RESET);
+	do {
+		msleep(1);
+		temp = smsc911x_mii_read(phy_dev->bus, phy_dev->addr,
+			MII_BMCR);
+	} while ((i--) && (temp & BMCR_RESET));
+
+	if (temp & BMCR_RESET) {
+		SMSC_WARN(pdata, hw, "PHY reset failed to complete");
+		return -EIO;
+	}
+	/* Extra delay required because the phy may not be completed with
+	* its reset when BMCR_RESET is cleared. Specs say 256 uS is
+	* enough delay but using 1ms here to be safe */
+	msleep(1);
+
+	return 0;
+}
+
+static int smsc911x_phy_loopbacktest(struct net_device *dev)
+{
+	struct smsc911x_data *pdata = netdev_priv(dev);
+	struct phy_device *phy_dev = pdata->phy_dev;
+	int result = -EIO;
+	unsigned int i, val;
+	unsigned long flags;
+
+	/* Initialise tx packet using broadcast destination address */
+	memset(pdata->loopback_tx_pkt, 0xff, ETH_ALEN);
+
+	/* Use incrementing source address */
+	for (i = 6; i < 12; i++)
+		pdata->loopback_tx_pkt[i] = (char)i;
+
+	/* Set length type field */
+	pdata->loopback_tx_pkt[12] = 0x00;
+	pdata->loopback_tx_pkt[13] = 0x00;
+
+	for (i = 14; i < MIN_PACKET_SIZE; i++)
+		pdata->loopback_tx_pkt[i] = (char)i;
+
+	val = smsc911x_reg_read(pdata, HW_CFG);
+	val &= HW_CFG_TX_FIF_SZ_;
+	val |= HW_CFG_SF_;
+	smsc911x_reg_write(pdata, HW_CFG, val);
+
+	smsc911x_reg_write(pdata, TX_CFG, TX_CFG_TX_ON_);
+	smsc911x_reg_write(pdata, RX_CFG,
+		(u32)((ulong)pdata->loopback_rx_pkt & 0x03) << 8);
+
+	for (i = 0; i < 10; i++) {
+		/* Set PHY to 10/FD, no ANEG, and loopback mode */
+		smsc911x_mii_write(phy_dev->bus, phy_dev->addr,	MII_BMCR,
+			BMCR_LOOPBACK | BMCR_FULLDPLX);
+
+		/* Enable MAC tx/rx, FD */
+		spin_lock_irqsave(&pdata->mac_lock, flags);
+		smsc911x_mac_write(pdata, MAC_CR, MAC_CR_FDPX_
+				   | MAC_CR_TXEN_ | MAC_CR_RXEN_);
+		spin_unlock_irqrestore(&pdata->mac_lock, flags);
+
+		if (smsc911x_phy_check_loopbackpkt(pdata) == 0) {
+			result = 0;
+			break;
+		}
+		pdata->resetcount++;
+
+		/* Disable MAC rx */
+		spin_lock_irqsave(&pdata->mac_lock, flags);
+		smsc911x_mac_write(pdata, MAC_CR, 0);
+		spin_unlock_irqrestore(&pdata->mac_lock, flags);
+
+		smsc911x_phy_reset(pdata);
+	}
+
+	/* Disable MAC */
+	spin_lock_irqsave(&pdata->mac_lock, flags);
+	smsc911x_mac_write(pdata, MAC_CR, 0);
+	spin_unlock_irqrestore(&pdata->mac_lock, flags);
+
+	/* Cancel PHY loopback mode */
+	smsc911x_mii_write(phy_dev->bus, phy_dev->addr, MII_BMCR, 0);
+
+	smsc911x_reg_write(pdata, TX_CFG, 0);
+	smsc911x_reg_write(pdata, RX_CFG, 0);
+
+	return result;
+}
+#endif				/* USE_PHY_WORK_AROUND */
+
+static void smsc911x_phy_update_flowcontrol(struct smsc911x_data *pdata)
+{
+	struct phy_device *phy_dev = pdata->phy_dev;
+	u32 afc = smsc911x_reg_read(pdata, AFC_CFG);
+	u32 flow;
+	unsigned long flags;
+
+	if (phy_dev->duplex == DUPLEX_FULL) {
+		u16 lcladv = phy_read(phy_dev, MII_ADVERTISE);
+		u16 rmtadv = phy_read(phy_dev, MII_LPA);
+		u8 cap = mii_resolve_flowctrl_fdx(lcladv, rmtadv);
+
+		if (cap & FLOW_CTRL_RX)
+			flow = 0xFFFF0002;
+		else
+			flow = 0;
+
+		if (cap & FLOW_CTRL_TX)
+			afc |= 0xF;
+		else
+			afc &= ~0xF;
+
+		SMSC_TRACE(pdata, hw, "rx pause %s, tx pause %s",
+			   (cap & FLOW_CTRL_RX ? "enabled" : "disabled"),
+			   (cap & FLOW_CTRL_TX ? "enabled" : "disabled"));
+	} else {
+		SMSC_TRACE(pdata, hw, "half duplex");
+		flow = 0;
+		afc |= 0xF;
+	}
+
+	spin_lock_irqsave(&pdata->mac_lock, flags);
+	smsc911x_mac_write(pdata, FLOW, flow);
+	spin_unlock_irqrestore(&pdata->mac_lock, flags);
+
+	smsc911x_reg_write(pdata, AFC_CFG, afc);
+}
+
+/* Update link mode if anything has changed.  Called periodically when the
+ * PHY is in polling mode, even if nothing has changed. */
+static void smsc911x_phy_adjust_link(struct net_device *dev)
+{
+	struct smsc911x_data *pdata = netdev_priv(dev);
+	struct phy_device *phy_dev = pdata->phy_dev;
+	unsigned long flags;
+	int carrier;
+
+	if (phy_dev->duplex != pdata->last_duplex) {
+		unsigned int mac_cr;
+		SMSC_TRACE(pdata, hw, "duplex state has changed");
+
+		spin_lock_irqsave(&pdata->mac_lock, flags);
+		mac_cr = smsc911x_mac_read(pdata, MAC_CR);
+		if (phy_dev->duplex) {
+			SMSC_TRACE(pdata, hw,
+				   "configuring for full duplex mode");
+			mac_cr |= MAC_CR_FDPX_;
+		} else {
+			SMSC_TRACE(pdata, hw,
+				   "configuring for half duplex mode");
+			mac_cr &= ~MAC_CR_FDPX_;
+		}
+		smsc911x_mac_write(pdata, MAC_CR, mac_cr);
+		spin_unlock_irqrestore(&pdata->mac_lock, flags);
+
+		smsc911x_phy_update_flowcontrol(pdata);
+		pdata->last_duplex = phy_dev->duplex;
+	}
+
+	carrier = netif_carrier_ok(dev);
+	if (carrier != pdata->last_carrier) {
+		SMSC_TRACE(pdata, hw, "carrier state has changed");
+		if (carrier) {
+			SMSC_TRACE(pdata, hw, "configuring for carrier OK");
+			if ((pdata->gpio_orig_setting & GPIO_CFG_LED1_EN_) &&
+			    (!pdata->using_extphy)) {
+				/* Restore original GPIO configuration */
+				pdata->gpio_setting = pdata->gpio_orig_setting;
+				smsc911x_reg_write(pdata, GPIO_CFG,
+					pdata->gpio_setting);
+			}
+		} else {
+			SMSC_TRACE(pdata, hw, "configuring for no carrier");
+			/* Check global setting that LED1
+			 * usage is 10/100 indicator */
+			pdata->gpio_setting = smsc911x_reg_read(pdata,
+				GPIO_CFG);
+			if ((pdata->gpio_setting & GPIO_CFG_LED1_EN_) &&
+			    (!pdata->using_extphy)) {
+				/* Force 10/100 LED off, after saving
+				 * original GPIO configuration */
+				pdata->gpio_orig_setting = pdata->gpio_setting;
+
+				pdata->gpio_setting &= ~GPIO_CFG_LED1_EN_;
+				pdata->gpio_setting |= (GPIO_CFG_GPIOBUF0_
+							| GPIO_CFG_GPIODIR0_
+							| GPIO_CFG_GPIOD0_);
+				smsc911x_reg_write(pdata, GPIO_CFG,
+					pdata->gpio_setting);
+			}
+		}
+		pdata->last_carrier = carrier;
+	}
+}
+
+static int smsc911x_mii_probe(struct net_device *dev)
+{
+	struct smsc911x_data *pdata = netdev_priv(dev);
+	struct phy_device *phydev = NULL;
+	int ret;
+
+	/* find the first phy */
+	phydev = phy_find_first(pdata->mii_bus);
+	if (!phydev) {
+		netdev_err(dev, "no PHY found\n");
+		return -ENODEV;
+	}
+
+	SMSC_TRACE(pdata, probe, "PHY: addr %d, phy_id 0x%08X",
+		   phydev->addr, phydev->phy_id);
+
+	ret = phy_connect_direct(dev, phydev,
+			&smsc911x_phy_adjust_link, 0,
+			pdata->config.phy_interface);
+
+	if (ret) {
+		netdev_err(dev, "Could not attach to PHY\n");
+		return ret;
+	}
+
+	netdev_info(dev,
+		    "attached PHY driver [%s] (mii_bus:phy_addr=%s, irq=%d)\n",
+		    phydev->drv->name, dev_name(&phydev->dev), phydev->irq);
+
+	/* mask with MAC supported features */
+	phydev->supported &= (PHY_BASIC_FEATURES | SUPPORTED_Pause |
+			      SUPPORTED_Asym_Pause);
+	phydev->advertising = phydev->supported;
+
+	pdata->phy_dev = phydev;
+	pdata->last_duplex = -1;
+	pdata->last_carrier = -1;
+
+#ifdef USE_PHY_WORK_AROUND
+	if (smsc911x_phy_loopbacktest(dev) < 0) {
+		SMSC_WARN(pdata, hw, "Failed Loop Back Test");
+		return -ENODEV;
+	}
+	SMSC_TRACE(pdata, hw, "Passed Loop Back Test");
+#endif				/* USE_PHY_WORK_AROUND */
+
+	SMSC_TRACE(pdata, hw, "phy initialised successfully");
+	return 0;
+}
+
+static int __devinit smsc911x_mii_init(struct platform_device *pdev,
+				       struct net_device *dev)
+{
+	struct smsc911x_data *pdata = netdev_priv(dev);
+	int err = -ENXIO, i;
+
+	pdata->mii_bus = mdiobus_alloc();
+	if (!pdata->mii_bus) {
+		err = -ENOMEM;
+		goto err_out_1;
+	}
+
+	pdata->mii_bus->name = SMSC_MDIONAME;
+	snprintf(pdata->mii_bus->id, MII_BUS_ID_SIZE, "%x", pdev->id);
+	pdata->mii_bus->priv = pdata;
+	pdata->mii_bus->read = smsc911x_mii_read;
+	pdata->mii_bus->write = smsc911x_mii_write;
+	pdata->mii_bus->irq = pdata->phy_irq;
+	for (i = 0; i < PHY_MAX_ADDR; ++i)
+		pdata->mii_bus->irq[i] = PHY_POLL;
+
+	pdata->mii_bus->parent = &pdev->dev;
+
+	switch (pdata->idrev & 0xFFFF0000) {
+	case 0x01170000:
+	case 0x01150000:
+	case 0x117A0000:
+	case 0x115A0000:
+		/* External PHY supported, try to autodetect */
+		smsc911x_phy_initialise_external(pdata);
+		break;
+	default:
+		SMSC_TRACE(pdata, hw, "External PHY is not supported, "
+			   "using internal PHY");
+		pdata->using_extphy = 0;
+		break;
+	}
+
+	if (!pdata->using_extphy) {
+		/* Mask all PHYs except ID 1 (internal) */
+		pdata->mii_bus->phy_mask = ~(1 << 1);
+	}
+
+	if (mdiobus_register(pdata->mii_bus)) {
+		SMSC_WARN(pdata, probe, "Error registering mii bus");
+		goto err_out_free_bus_2;
+	}
+
+	if (smsc911x_mii_probe(dev) < 0) {
+		SMSC_WARN(pdata, probe, "Error registering mii bus");
+		goto err_out_unregister_bus_3;
+	}
+
+	return 0;
+
+err_out_unregister_bus_3:
+	mdiobus_unregister(pdata->mii_bus);
+err_out_free_bus_2:
+	mdiobus_free(pdata->mii_bus);
+err_out_1:
+	return err;
+}
+
+/* Gets the number of tx statuses in the fifo */
+static unsigned int smsc911x_tx_get_txstatcount(struct smsc911x_data *pdata)
+{
+	return (smsc911x_reg_read(pdata, TX_FIFO_INF)
+		& TX_FIFO_INF_TSUSED_) >> 16;
+}
+
+/* Reads tx statuses and increments counters where necessary */
+static void smsc911x_tx_update_txcounters(struct net_device *dev)
+{
+	struct smsc911x_data *pdata = netdev_priv(dev);
+	unsigned int tx_stat;
+
+	while ((tx_stat = smsc911x_tx_get_txstatus(pdata)) != 0) {
+		if (unlikely(tx_stat & 0x80000000)) {
+			/* In this driver the packet tag is used as the packet
+			 * length. Since a packet length can never reach the
+			 * size of 0x8000, this bit is reserved. It is worth
+			 * noting that the "reserved bit" in the warning above
+			 * does not reference a hardware defined reserved bit
+			 * but rather a driver defined one.
+			 */
+			SMSC_WARN(pdata, hw, "Packet tag reserved bit is high");
+		} else {
+			if (unlikely(tx_stat & TX_STS_ES_)) {
+				dev->stats.tx_errors++;
+			} else {
+				dev->stats.tx_packets++;
+				dev->stats.tx_bytes += (tx_stat >> 16);
+			}
+			if (unlikely(tx_stat & TX_STS_EXCESS_COL_)) {
+				dev->stats.collisions += 16;
+				dev->stats.tx_aborted_errors += 1;
+			} else {
+				dev->stats.collisions +=
+				    ((tx_stat >> 3) & 0xF);
+			}
+			if (unlikely(tx_stat & TX_STS_LOST_CARRIER_))
+				dev->stats.tx_carrier_errors += 1;
+			if (unlikely(tx_stat & TX_STS_LATE_COL_)) {
+				dev->stats.collisions++;
+				dev->stats.tx_aborted_errors++;
+			}
+		}
+	}
+}
+
+/* Increments the Rx error counters */
+static void
+smsc911x_rx_counterrors(struct net_device *dev, unsigned int rxstat)
+{
+	int crc_err = 0;
+
+	if (unlikely(rxstat & RX_STS_ES_)) {
+		dev->stats.rx_errors++;
+		if (unlikely(rxstat & RX_STS_CRC_ERR_)) {
+			dev->stats.rx_crc_errors++;
+			crc_err = 1;
+		}
+	}
+	if (likely(!crc_err)) {
+		if (unlikely((rxstat & RX_STS_FRAME_TYPE_) &&
+			     (rxstat & RX_STS_LENGTH_ERR_)))
+			dev->stats.rx_length_errors++;
+		if (rxstat & RX_STS_MCAST_)
+			dev->stats.multicast++;
+	}
+}
+
+/* Quickly dumps bad packets */
+static void
+smsc911x_rx_fastforward(struct smsc911x_data *pdata, unsigned int pktbytes)
+{
+	unsigned int pktwords = (pktbytes + NET_IP_ALIGN + 3) >> 2;
+
+	if (likely(pktwords >= 4)) {
+		unsigned int timeout = 500;
+		unsigned int val;
+		smsc911x_reg_write(pdata, RX_DP_CTRL, RX_DP_CTRL_RX_FFWD_);
+		do {
+			udelay(1);
+			val = smsc911x_reg_read(pdata, RX_DP_CTRL);
+		} while ((val & RX_DP_CTRL_RX_FFWD_) && --timeout);
+
+		if (unlikely(timeout == 0))
+			SMSC_WARN(pdata, hw, "Timed out waiting for "
+				  "RX FFWD to finish, RX_DP_CTRL: 0x%08X", val);
+	} else {
+		unsigned int temp;
+		while (pktwords--)
+			temp = smsc911x_reg_read(pdata, RX_DATA_FIFO);
+	}
+}
+
+/* NAPI poll function */
+static int smsc911x_poll(struct napi_struct *napi, int budget)
+{
+	struct smsc911x_data *pdata =
+		container_of(napi, struct smsc911x_data, napi);
+	struct net_device *dev = pdata->dev;
+	int npackets = 0;
+
+	while (npackets < budget) {
+		unsigned int pktlength;
+		unsigned int pktwords;
+		struct sk_buff *skb;
+		unsigned int rxstat = smsc911x_rx_get_rxstatus(pdata);
+
+		if (!rxstat) {
+			unsigned int temp;
+			/* We processed all packets available.  Tell NAPI it can
+			 * stop polling then re-enable rx interrupts */
+			smsc911x_reg_write(pdata, INT_STS, INT_STS_RSFL_);
+			napi_complete(napi);
+			temp = smsc911x_reg_read(pdata, INT_EN);
+			temp |= INT_EN_RSFL_EN_;
+			smsc911x_reg_write(pdata, INT_EN, temp);
+			break;
+		}
+
+		/* Count packet for NAPI scheduling, even if it has an error.
+		 * Error packets still require cycles to discard */
+		npackets++;
+
+		pktlength = ((rxstat & 0x3FFF0000) >> 16);
+		pktwords = (pktlength + NET_IP_ALIGN + 3) >> 2;
+		smsc911x_rx_counterrors(dev, rxstat);
+
+		if (unlikely(rxstat & RX_STS_ES_)) {
+			SMSC_WARN(pdata, rx_err,
+				  "Discarding packet with error bit set");
+			/* Packet has an error, discard it and continue with
+			 * the next */
+			smsc911x_rx_fastforward(pdata, pktwords);
+			dev->stats.rx_dropped++;
+			continue;
+		}
+
+		skb = netdev_alloc_skb(dev, pktlength + NET_IP_ALIGN);
+		if (unlikely(!skb)) {
+			SMSC_WARN(pdata, rx_err,
+				  "Unable to allocate skb for rx packet");
+			/* Drop the packet and stop this polling iteration */
+			smsc911x_rx_fastforward(pdata, pktwords);
+			dev->stats.rx_dropped++;
+			break;
+		}
+
+		skb->data = skb->head;
+		skb_reset_tail_pointer(skb);
+
+		/* Align IP on 16B boundary */
+		skb_reserve(skb, NET_IP_ALIGN);
+		skb_put(skb, pktlength - 4);
+		pdata->ops->rx_readfifo(pdata,
+				 (unsigned int *)skb->head, pktwords);
+		skb->protocol = eth_type_trans(skb, dev);
+		skb_checksum_none_assert(skb);
+		netif_receive_skb(skb);
+
+		/* Update counters */
+		dev->stats.rx_packets++;
+		dev->stats.rx_bytes += (pktlength - 4);
+	}
+
+	/* Return total received packets */
+	return npackets;
+}
+
+/* Returns hash bit number for given MAC address
+ * Example:
+ * 01 00 5E 00 00 01 -> returns bit number 31 */
+static unsigned int smsc911x_hash(char addr[ETH_ALEN])
+{
+	return (ether_crc(ETH_ALEN, addr) >> 26) & 0x3f;
+}
+
+static void smsc911x_rx_multicast_update(struct smsc911x_data *pdata)
+{
+	/* Performs the multicast & mac_cr update.  This is called when
+	 * safe on the current hardware, and with the mac_lock held */
+	unsigned int mac_cr;
+
+	SMSC_ASSERT_MAC_LOCK(pdata);
+
+	mac_cr = smsc911x_mac_read(pdata, MAC_CR);
+	mac_cr |= pdata->set_bits_mask;
+	mac_cr &= ~(pdata->clear_bits_mask);
+	smsc911x_mac_write(pdata, MAC_CR, mac_cr);
+	smsc911x_mac_write(pdata, HASHH, pdata->hashhi);
+	smsc911x_mac_write(pdata, HASHL, pdata->hashlo);
+	SMSC_TRACE(pdata, hw, "maccr 0x%08X, HASHH 0x%08X, HASHL 0x%08X",
+		   mac_cr, pdata->hashhi, pdata->hashlo);
+}
+
+static void smsc911x_rx_multicast_update_workaround(struct smsc911x_data *pdata)
+{
+	unsigned int mac_cr;
+
+	/* This function is only called for older LAN911x devices
+	 * (revA or revB), where MAC_CR, HASHH and HASHL should not
+	 * be modified during Rx - newer devices immediately update the
+	 * registers.
+	 *
+	 * This is called from interrupt context */
+
+	spin_lock(&pdata->mac_lock);
+
+	/* Check Rx has stopped */
+	if (smsc911x_mac_read(pdata, MAC_CR) & MAC_CR_RXEN_)
+		SMSC_WARN(pdata, drv, "Rx not stopped");
+
+	/* Perform the update - safe to do now Rx has stopped */
+	smsc911x_rx_multicast_update(pdata);
+
+	/* Re-enable Rx */
+	mac_cr = smsc911x_mac_read(pdata, MAC_CR);
+	mac_cr |= MAC_CR_RXEN_;
+	smsc911x_mac_write(pdata, MAC_CR, mac_cr);
+
+	pdata->multicast_update_pending = 0;
+
+	spin_unlock(&pdata->mac_lock);
+}
+
+static int smsc911x_soft_reset(struct smsc911x_data *pdata)
+{
+	unsigned int timeout;
+	unsigned int temp;
+
+	/* Reset the LAN911x */
+	smsc911x_reg_write(pdata, HW_CFG, HW_CFG_SRST_);
+	timeout = 10;
+	do {
+		udelay(10);
+		temp = smsc911x_reg_read(pdata, HW_CFG);
+	} while ((--timeout) && (temp & HW_CFG_SRST_));
+
+	if (unlikely(temp & HW_CFG_SRST_)) {
+		SMSC_WARN(pdata, drv, "Failed to complete reset");
+		return -EIO;
+	}
+	return 0;
+}
+
+/* Sets the device MAC address to dev_addr, called with mac_lock held */
+static void
+smsc911x_set_hw_mac_address(struct smsc911x_data *pdata, u8 dev_addr[6])
+{
+	u32 mac_high16 = (dev_addr[5] << 8) | dev_addr[4];
+	u32 mac_low32 = (dev_addr[3] << 24) | (dev_addr[2] << 16) |
+	    (dev_addr[1] << 8) | dev_addr[0];
+
+	SMSC_ASSERT_MAC_LOCK(pdata);
+
+	smsc911x_mac_write(pdata, ADDRH, mac_high16);
+	smsc911x_mac_write(pdata, ADDRL, mac_low32);
+}
+
+static int smsc911x_open(struct net_device *dev)
+{
+	struct smsc911x_data *pdata = netdev_priv(dev);
+	unsigned int timeout;
+	unsigned int temp;
+	unsigned int intcfg;
+
+	/* if the phy is not yet registered, retry later*/
+	if (!pdata->phy_dev) {
+		SMSC_WARN(pdata, hw, "phy_dev is NULL");
+		return -EAGAIN;
+	}
+
+	if (!is_valid_ether_addr(dev->dev_addr)) {
+		SMSC_WARN(pdata, hw, "dev_addr is not a valid MAC address");
+		return -EADDRNOTAVAIL;
+	}
+
+	/* Reset the LAN911x */
+	if (smsc911x_soft_reset(pdata)) {
+		SMSC_WARN(pdata, hw, "soft reset failed");
+		return -EIO;
+	}
+
+	smsc911x_reg_write(pdata, HW_CFG, 0x00050000);
+	smsc911x_reg_write(pdata, AFC_CFG, 0x006E3740);
+
+	/* Increase the legal frame size of VLAN tagged frames to 1522 bytes */
+	spin_lock_irq(&pdata->mac_lock);
+	smsc911x_mac_write(pdata, VLAN1, ETH_P_8021Q);
+	spin_unlock_irq(&pdata->mac_lock);
+
+	/* Make sure EEPROM has finished loading before setting GPIO_CFG */
+	timeout = 50;
+	while ((smsc911x_reg_read(pdata, E2P_CMD) & E2P_CMD_EPC_BUSY_) &&
+	       --timeout) {
+		udelay(10);
+	}
+
+	if (unlikely(timeout == 0))
+		SMSC_WARN(pdata, ifup,
+			  "Timed out waiting for EEPROM busy bit to clear");
+
+	smsc911x_reg_write(pdata, GPIO_CFG, 0x70070000);
+
+	/* The soft reset above cleared the device's MAC address,
+	 * restore it from local copy (set in probe) */
+	spin_lock_irq(&pdata->mac_lock);
+	smsc911x_set_hw_mac_address(pdata, dev->dev_addr);
+	spin_unlock_irq(&pdata->mac_lock);
+
+	/* Initialise irqs, but leave all sources disabled */
+	smsc911x_reg_write(pdata, INT_EN, 0);
+	smsc911x_reg_write(pdata, INT_STS, 0xFFFFFFFF);
+
+	/* Set interrupt deassertion to 100uS */
+	intcfg = ((10 << 24) | INT_CFG_IRQ_EN_);
+
+	if (pdata->config.irq_polarity) {
+		SMSC_TRACE(pdata, ifup, "irq polarity: active high");
+		intcfg |= INT_CFG_IRQ_POL_;
+	} else {
+		SMSC_TRACE(pdata, ifup, "irq polarity: active low");
+	}
+
+	if (pdata->config.irq_type) {
+		SMSC_TRACE(pdata, ifup, "irq type: push-pull");
+		intcfg |= INT_CFG_IRQ_TYPE_;
+	} else {
+		SMSC_TRACE(pdata, ifup, "irq type: open drain");
+	}
+
+	smsc911x_reg_write(pdata, INT_CFG, intcfg);
+
+	SMSC_TRACE(pdata, ifup, "Testing irq handler using IRQ %d", dev->irq);
+	pdata->software_irq_signal = 0;
+	smp_wmb();
+
+	temp = smsc911x_reg_read(pdata, INT_EN);
+	temp |= INT_EN_SW_INT_EN_;
+	smsc911x_reg_write(pdata, INT_EN, temp);
+
+	timeout = 1000;
+	while (timeout--) {
+		if (pdata->software_irq_signal)
+			break;
+		msleep(1);
+	}
+
+	if (!pdata->software_irq_signal) {
+		netdev_warn(dev, "ISR failed signaling test (IRQ %d)\n",
+			    dev->irq);
+		return -ENODEV;
+	}
+	SMSC_TRACE(pdata, ifup, "IRQ handler passed test using IRQ %d",
+		   dev->irq);
+
+	netdev_info(dev, "SMSC911x/921x identified at %#08lx, IRQ: %d\n",
+		    (unsigned long)pdata->ioaddr, dev->irq);
+
+	/* Reset the last known duplex and carrier */
+	pdata->last_duplex = -1;
+	pdata->last_carrier = -1;
+
+	/* Bring the PHY up */
+	phy_start(pdata->phy_dev);
+
+	temp = smsc911x_reg_read(pdata, HW_CFG);
+	/* Preserve TX FIFO size and external PHY configuration */
+	temp &= (HW_CFG_TX_FIF_SZ_|0x00000FFF);
+	temp |= HW_CFG_SF_;
+	smsc911x_reg_write(pdata, HW_CFG, temp);
+
+	temp = smsc911x_reg_read(pdata, FIFO_INT);
+	temp |= FIFO_INT_TX_AVAIL_LEVEL_;
+	temp &= ~(FIFO_INT_RX_STS_LEVEL_);
+	smsc911x_reg_write(pdata, FIFO_INT, temp);
+
+	/* set RX Data offset to 2 bytes for alignment */
+	smsc911x_reg_write(pdata, RX_CFG, (2 << 8));
+
+	/* enable NAPI polling before enabling RX interrupts */
+	napi_enable(&pdata->napi);
+
+	temp = smsc911x_reg_read(pdata, INT_EN);
+	temp |= (INT_EN_TDFA_EN_ | INT_EN_RSFL_EN_ | INT_EN_RXSTOP_INT_EN_);
+	smsc911x_reg_write(pdata, INT_EN, temp);
+
+	spin_lock_irq(&pdata->mac_lock);
+	temp = smsc911x_mac_read(pdata, MAC_CR);
+	temp |= (MAC_CR_TXEN_ | MAC_CR_RXEN_ | MAC_CR_HBDIS_);
+	smsc911x_mac_write(pdata, MAC_CR, temp);
+	spin_unlock_irq(&pdata->mac_lock);
+
+	smsc911x_reg_write(pdata, TX_CFG, TX_CFG_TX_ON_);
+
+	netif_start_queue(dev);
+	return 0;
+}
+
+/* Entry point for stopping the interface */
+static int smsc911x_stop(struct net_device *dev)
+{
+	struct smsc911x_data *pdata = netdev_priv(dev);
+	unsigned int temp;
+
+	/* Disable all device interrupts */
+	temp = smsc911x_reg_read(pdata, INT_CFG);
+	temp &= ~INT_CFG_IRQ_EN_;
+	smsc911x_reg_write(pdata, INT_CFG, temp);
+
+	/* Stop Tx and Rx polling */
+	netif_stop_queue(dev);
+	napi_disable(&pdata->napi);
+
+	/* At this point all Rx and Tx activity is stopped */
+	dev->stats.rx_dropped += smsc911x_reg_read(pdata, RX_DROP);
+	smsc911x_tx_update_txcounters(dev);
+
+	/* Bring the PHY down */
+	if (pdata->phy_dev)
+		phy_stop(pdata->phy_dev);
+
+	SMSC_TRACE(pdata, ifdown, "Interface stopped");
+	return 0;
+}
+
+/* Entry point for transmitting a packet */
+static int smsc911x_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct smsc911x_data *pdata = netdev_priv(dev);
+	unsigned int freespace;
+	unsigned int tx_cmd_a;
+	unsigned int tx_cmd_b;
+	unsigned int temp;
+	u32 wrsz;
+	ulong bufp;
+
+	freespace = smsc911x_reg_read(pdata, TX_FIFO_INF) & TX_FIFO_INF_TDFREE_;
+
+	if (unlikely(freespace < TX_FIFO_LOW_THRESHOLD))
+		SMSC_WARN(pdata, tx_err,
+			  "Tx data fifo low, space available: %d", freespace);
+
+	/* Word alignment adjustment */
+	tx_cmd_a = (u32)((ulong)skb->data & 0x03) << 16;
+	tx_cmd_a |= TX_CMD_A_FIRST_SEG_ | TX_CMD_A_LAST_SEG_;
+	tx_cmd_a |= (unsigned int)skb->len;
+
+	tx_cmd_b = ((unsigned int)skb->len) << 16;
+	tx_cmd_b |= (unsigned int)skb->len;
+
+	smsc911x_reg_write(pdata, TX_DATA_FIFO, tx_cmd_a);
+	smsc911x_reg_write(pdata, TX_DATA_FIFO, tx_cmd_b);
+
+	bufp = (ulong)skb->data & (~0x3);
+	wrsz = (u32)skb->len + 3;
+	wrsz += (u32)((ulong)skb->data & 0x3);
+	wrsz >>= 2;
+
+	pdata->ops->tx_writefifo(pdata, (unsigned int *)bufp, wrsz);
+	freespace -= (skb->len + 32);
+	skb_tx_timestamp(skb);
+	dev_kfree_skb(skb);
+
+	if (unlikely(smsc911x_tx_get_txstatcount(pdata) >= 30))
+		smsc911x_tx_update_txcounters(dev);
+
+	if (freespace < TX_FIFO_LOW_THRESHOLD) {
+		netif_stop_queue(dev);
+		temp = smsc911x_reg_read(pdata, FIFO_INT);
+		temp &= 0x00FFFFFF;
+		temp |= 0x32000000;
+		smsc911x_reg_write(pdata, FIFO_INT, temp);
+	}
+
+	return NETDEV_TX_OK;
+}
+
+/* Entry point for getting status counters */
+static struct net_device_stats *smsc911x_get_stats(struct net_device *dev)
+{
+	struct smsc911x_data *pdata = netdev_priv(dev);
+	smsc911x_tx_update_txcounters(dev);
+	dev->stats.rx_dropped += smsc911x_reg_read(pdata, RX_DROP);
+	return &dev->stats;
+}
+
+/* Entry point for setting addressing modes */
+static void smsc911x_set_multicast_list(struct net_device *dev)
+{
+	struct smsc911x_data *pdata = netdev_priv(dev);
+	unsigned long flags;
+
+	if (dev->flags & IFF_PROMISC) {
+		/* Enabling promiscuous mode */
+		pdata->set_bits_mask = MAC_CR_PRMS_;
+		pdata->clear_bits_mask = (MAC_CR_MCPAS_ | MAC_CR_HPFILT_);
+		pdata->hashhi = 0;
+		pdata->hashlo = 0;
+	} else if (dev->flags & IFF_ALLMULTI) {
+		/* Enabling all multicast mode */
+		pdata->set_bits_mask = MAC_CR_MCPAS_;
+		pdata->clear_bits_mask = (MAC_CR_PRMS_ | MAC_CR_HPFILT_);
+		pdata->hashhi = 0;
+		pdata->hashlo = 0;
+	} else if (!netdev_mc_empty(dev)) {
+		/* Enabling specific multicast addresses */
+		unsigned int hash_high = 0;
+		unsigned int hash_low = 0;
+		struct netdev_hw_addr *ha;
+
+		pdata->set_bits_mask = MAC_CR_HPFILT_;
+		pdata->clear_bits_mask = (MAC_CR_PRMS_ | MAC_CR_MCPAS_);
+
+		netdev_for_each_mc_addr(ha, dev) {
+			unsigned int bitnum = smsc911x_hash(ha->addr);
+			unsigned int mask = 0x01 << (bitnum & 0x1F);
+
+			if (bitnum & 0x20)
+				hash_high |= mask;
+			else
+				hash_low |= mask;
+		}
+
+		pdata->hashhi = hash_high;
+		pdata->hashlo = hash_low;
+	} else {
+		/* Enabling local MAC address only */
+		pdata->set_bits_mask = 0;
+		pdata->clear_bits_mask =
+		    (MAC_CR_PRMS_ | MAC_CR_MCPAS_ | MAC_CR_HPFILT_);
+		pdata->hashhi = 0;
+		pdata->hashlo = 0;
+	}
+
+	spin_lock_irqsave(&pdata->mac_lock, flags);
+
+	if (pdata->generation <= 1) {
+		/* Older hardware revision - cannot change these flags while
+		 * receiving data */
+		if (!pdata->multicast_update_pending) {
+			unsigned int temp;
+			SMSC_TRACE(pdata, hw, "scheduling mcast update");
+			pdata->multicast_update_pending = 1;
+
+			/* Request the hardware to stop, then perform the
+			 * update when we get an RX_STOP interrupt */
+			temp = smsc911x_mac_read(pdata, MAC_CR);
+			temp &= ~(MAC_CR_RXEN_);
+			smsc911x_mac_write(pdata, MAC_CR, temp);
+		} else {
+			/* There is another update pending, this should now
+			 * use the newer values */
+		}
+	} else {
+		/* Newer hardware revision - can write immediately */
+		smsc911x_rx_multicast_update(pdata);
+	}
+
+	spin_unlock_irqrestore(&pdata->mac_lock, flags);
+}
+
+static irqreturn_t smsc911x_irqhandler(int irq, void *dev_id)
+{
+	struct net_device *dev = dev_id;
+	struct smsc911x_data *pdata = netdev_priv(dev);
+	u32 intsts = smsc911x_reg_read(pdata, INT_STS);
+	u32 inten = smsc911x_reg_read(pdata, INT_EN);
+	int serviced = IRQ_NONE;
+	u32 temp;
+
+	if (unlikely(intsts & inten & INT_STS_SW_INT_)) {
+		temp = smsc911x_reg_read(pdata, INT_EN);
+		temp &= (~INT_EN_SW_INT_EN_);
+		smsc911x_reg_write(pdata, INT_EN, temp);
+		smsc911x_reg_write(pdata, INT_STS, INT_STS_SW_INT_);
+		pdata->software_irq_signal = 1;
+		smp_wmb();
+		serviced = IRQ_HANDLED;
+	}
+
+	if (unlikely(intsts & inten & INT_STS_RXSTOP_INT_)) {
+		/* Called when there is a multicast update scheduled and
+		 * it is now safe to complete the update */
+		SMSC_TRACE(pdata, intr, "RX Stop interrupt");
+		smsc911x_reg_write(pdata, INT_STS, INT_STS_RXSTOP_INT_);
+		if (pdata->multicast_update_pending)
+			smsc911x_rx_multicast_update_workaround(pdata);
+		serviced = IRQ_HANDLED;
+	}
+
+	if (intsts & inten & INT_STS_TDFA_) {
+		temp = smsc911x_reg_read(pdata, FIFO_INT);
+		temp |= FIFO_INT_TX_AVAIL_LEVEL_;
+		smsc911x_reg_write(pdata, FIFO_INT, temp);
+		smsc911x_reg_write(pdata, INT_STS, INT_STS_TDFA_);
+		netif_wake_queue(dev);
+		serviced = IRQ_HANDLED;
+	}
+
+	if (unlikely(intsts & inten & INT_STS_RXE_)) {
+		SMSC_TRACE(pdata, intr, "RX Error interrupt");
+		smsc911x_reg_write(pdata, INT_STS, INT_STS_RXE_);
+		serviced = IRQ_HANDLED;
+	}
+
+	if (likely(intsts & inten & INT_STS_RSFL_)) {
+		if (likely(napi_schedule_prep(&pdata->napi))) {
+			/* Disable Rx interrupts */
+			temp = smsc911x_reg_read(pdata, INT_EN);
+			temp &= (~INT_EN_RSFL_EN_);
+			smsc911x_reg_write(pdata, INT_EN, temp);
+			/* Schedule a NAPI poll */
+			__napi_schedule(&pdata->napi);
+		} else {
+			SMSC_WARN(pdata, rx_err, "napi_schedule_prep failed");
+		}
+		serviced = IRQ_HANDLED;
+	}
+
+	return serviced;
+}
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void smsc911x_poll_controller(struct net_device *dev)
+{
+	disable_irq(dev->irq);
+	smsc911x_irqhandler(0, dev);
+	enable_irq(dev->irq);
+}
+#endif				/* CONFIG_NET_POLL_CONTROLLER */
+
+static int smsc911x_set_mac_address(struct net_device *dev, void *p)
+{
+	struct smsc911x_data *pdata = netdev_priv(dev);
+	struct sockaddr *addr = p;
+
+	/* On older hardware revisions we cannot change the mac address
+	 * registers while receiving data.  Newer devices can safely change
+	 * this at any time. */
+	if (pdata->generation <= 1 && netif_running(dev))
+		return -EBUSY;
+
+	if (!is_valid_ether_addr(addr->sa_data))
+		return -EADDRNOTAVAIL;
+
+	memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN);
+
+	spin_lock_irq(&pdata->mac_lock);
+	smsc911x_set_hw_mac_address(pdata, dev->dev_addr);
+	spin_unlock_irq(&pdata->mac_lock);
+
+	netdev_info(dev, "MAC Address: %pM\n", dev->dev_addr);
+
+	return 0;
+}
+
+/* Standard ioctls for mii-tool */
+static int smsc911x_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+	struct smsc911x_data *pdata = netdev_priv(dev);
+
+	if (!netif_running(dev) || !pdata->phy_dev)
+		return -EINVAL;
+
+	return phy_mii_ioctl(pdata->phy_dev, ifr, cmd);
+}
+
+static int
+smsc911x_ethtool_getsettings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+	struct smsc911x_data *pdata = netdev_priv(dev);
+
+	cmd->maxtxpkt = 1;
+	cmd->maxrxpkt = 1;
+	return phy_ethtool_gset(pdata->phy_dev, cmd);
+}
+
+static int
+smsc911x_ethtool_setsettings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+	struct smsc911x_data *pdata = netdev_priv(dev);
+
+	return phy_ethtool_sset(pdata->phy_dev, cmd);
+}
+
+static void smsc911x_ethtool_getdrvinfo(struct net_device *dev,
+					struct ethtool_drvinfo *info)
+{
+	strlcpy(info->driver, SMSC_CHIPNAME, sizeof(info->driver));
+	strlcpy(info->version, SMSC_DRV_VERSION, sizeof(info->version));
+	strlcpy(info->bus_info, dev_name(dev->dev.parent),
+		sizeof(info->bus_info));
+}
+
+static int smsc911x_ethtool_nwayreset(struct net_device *dev)
+{
+	struct smsc911x_data *pdata = netdev_priv(dev);
+
+	return phy_start_aneg(pdata->phy_dev);
+}
+
+static u32 smsc911x_ethtool_getmsglevel(struct net_device *dev)
+{
+	struct smsc911x_data *pdata = netdev_priv(dev);
+	return pdata->msg_enable;
+}
+
+static void smsc911x_ethtool_setmsglevel(struct net_device *dev, u32 level)
+{
+	struct smsc911x_data *pdata = netdev_priv(dev);
+	pdata->msg_enable = level;
+}
+
+static int smsc911x_ethtool_getregslen(struct net_device *dev)
+{
+	return (((E2P_DATA - ID_REV) / 4 + 1) + (WUCSR - MAC_CR) + 1 + 32) *
+	    sizeof(u32);
+}
+
+static void
+smsc911x_ethtool_getregs(struct net_device *dev, struct ethtool_regs *regs,
+			 void *buf)
+{
+	struct smsc911x_data *pdata = netdev_priv(dev);
+	struct phy_device *phy_dev = pdata->phy_dev;
+	unsigned long flags;
+	unsigned int i;
+	unsigned int j = 0;
+	u32 *data = buf;
+
+	regs->version = pdata->idrev;
+	for (i = ID_REV; i <= E2P_DATA; i += (sizeof(u32)))
+		data[j++] = smsc911x_reg_read(pdata, i);
+
+	for (i = MAC_CR; i <= WUCSR; i++) {
+		spin_lock_irqsave(&pdata->mac_lock, flags);
+		data[j++] = smsc911x_mac_read(pdata, i);
+		spin_unlock_irqrestore(&pdata->mac_lock, flags);
+	}
+
+	for (i = 0; i <= 31; i++)
+		data[j++] = smsc911x_mii_read(phy_dev->bus, phy_dev->addr, i);
+}
+
+static void smsc911x_eeprom_enable_access(struct smsc911x_data *pdata)
+{
+	unsigned int temp = smsc911x_reg_read(pdata, GPIO_CFG);
+	temp &= ~GPIO_CFG_EEPR_EN_;
+	smsc911x_reg_write(pdata, GPIO_CFG, temp);
+	msleep(1);
+}
+
+static int smsc911x_eeprom_send_cmd(struct smsc911x_data *pdata, u32 op)
+{
+	int timeout = 100;
+	u32 e2cmd;
+
+	SMSC_TRACE(pdata, drv, "op 0x%08x", op);
+	if (smsc911x_reg_read(pdata, E2P_CMD) & E2P_CMD_EPC_BUSY_) {
+		SMSC_WARN(pdata, drv, "Busy at start");
+		return -EBUSY;
+	}
+
+	e2cmd = op | E2P_CMD_EPC_BUSY_;
+	smsc911x_reg_write(pdata, E2P_CMD, e2cmd);
+
+	do {
+		msleep(1);
+		e2cmd = smsc911x_reg_read(pdata, E2P_CMD);
+	} while ((e2cmd & E2P_CMD_EPC_BUSY_) && (--timeout));
+
+	if (!timeout) {
+		SMSC_TRACE(pdata, drv, "TIMED OUT");
+		return -EAGAIN;
+	}
+
+	if (e2cmd & E2P_CMD_EPC_TIMEOUT_) {
+		SMSC_TRACE(pdata, drv, "Error occurred during eeprom operation");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int smsc911x_eeprom_read_location(struct smsc911x_data *pdata,
+					 u8 address, u8 *data)
+{
+	u32 op = E2P_CMD_EPC_CMD_READ_ | address;
+	int ret;
+
+	SMSC_TRACE(pdata, drv, "address 0x%x", address);
+	ret = smsc911x_eeprom_send_cmd(pdata, op);
+
+	if (!ret)
+		data[address] = smsc911x_reg_read(pdata, E2P_DATA);
+
+	return ret;
+}
+
+static int smsc911x_eeprom_write_location(struct smsc911x_data *pdata,
+					  u8 address, u8 data)
+{
+	u32 op = E2P_CMD_EPC_CMD_ERASE_ | address;
+	u32 temp;
+	int ret;
+
+	SMSC_TRACE(pdata, drv, "address 0x%x, data 0x%x", address, data);
+	ret = smsc911x_eeprom_send_cmd(pdata, op);
+
+	if (!ret) {
+		op = E2P_CMD_EPC_CMD_WRITE_ | address;
+		smsc911x_reg_write(pdata, E2P_DATA, (u32)data);
+
+		/* Workaround for hardware read-after-write restriction */
+		temp = smsc911x_reg_read(pdata, BYTE_TEST);
+
+		ret = smsc911x_eeprom_send_cmd(pdata, op);
+	}
+
+	return ret;
+}
+
+static int smsc911x_ethtool_get_eeprom_len(struct net_device *dev)
+{
+	return SMSC911X_EEPROM_SIZE;
+}
+
+static int smsc911x_ethtool_get_eeprom(struct net_device *dev,
+				       struct ethtool_eeprom *eeprom, u8 *data)
+{
+	struct smsc911x_data *pdata = netdev_priv(dev);
+	u8 eeprom_data[SMSC911X_EEPROM_SIZE];
+	int len;
+	int i;
+
+	smsc911x_eeprom_enable_access(pdata);
+
+	len = min(eeprom->len, SMSC911X_EEPROM_SIZE);
+	for (i = 0; i < len; i++) {
+		int ret = smsc911x_eeprom_read_location(pdata, i, eeprom_data);
+		if (ret < 0) {
+			eeprom->len = 0;
+			return ret;
+		}
+	}
+
+	memcpy(data, &eeprom_data[eeprom->offset], len);
+	eeprom->len = len;
+	return 0;
+}
+
+static int smsc911x_ethtool_set_eeprom(struct net_device *dev,
+				       struct ethtool_eeprom *eeprom, u8 *data)
+{
+	int ret;
+	struct smsc911x_data *pdata = netdev_priv(dev);
+
+	smsc911x_eeprom_enable_access(pdata);
+	smsc911x_eeprom_send_cmd(pdata, E2P_CMD_EPC_CMD_EWEN_);
+	ret = smsc911x_eeprom_write_location(pdata, eeprom->offset, *data);
+	smsc911x_eeprom_send_cmd(pdata, E2P_CMD_EPC_CMD_EWDS_);
+
+	/* Single byte write, according to man page */
+	eeprom->len = 1;
+
+	return ret;
+}
+
+static const struct ethtool_ops smsc911x_ethtool_ops = {
+	.get_settings = smsc911x_ethtool_getsettings,
+	.set_settings = smsc911x_ethtool_setsettings,
+	.get_link = ethtool_op_get_link,
+	.get_drvinfo = smsc911x_ethtool_getdrvinfo,
+	.nway_reset = smsc911x_ethtool_nwayreset,
+	.get_msglevel = smsc911x_ethtool_getmsglevel,
+	.set_msglevel = smsc911x_ethtool_setmsglevel,
+	.get_regs_len = smsc911x_ethtool_getregslen,
+	.get_regs = smsc911x_ethtool_getregs,
+	.get_eeprom_len = smsc911x_ethtool_get_eeprom_len,
+	.get_eeprom = smsc911x_ethtool_get_eeprom,
+	.set_eeprom = smsc911x_ethtool_set_eeprom,
+};
+
+static const struct net_device_ops smsc911x_netdev_ops = {
+	.ndo_open		= smsc911x_open,
+	.ndo_stop		= smsc911x_stop,
+	.ndo_start_xmit		= smsc911x_hard_start_xmit,
+	.ndo_get_stats		= smsc911x_get_stats,
+	.ndo_set_multicast_list	= smsc911x_set_multicast_list,
+	.ndo_do_ioctl		= smsc911x_do_ioctl,
+	.ndo_change_mtu		= eth_change_mtu,
+	.ndo_validate_addr	= eth_validate_addr,
+	.ndo_set_mac_address 	= smsc911x_set_mac_address,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	.ndo_poll_controller	= smsc911x_poll_controller,
+#endif
+};
+
+/* copies the current mac address from hardware to dev->dev_addr */
+static void __devinit smsc911x_read_mac_address(struct net_device *dev)
+{
+	struct smsc911x_data *pdata = netdev_priv(dev);
+	u32 mac_high16 = smsc911x_mac_read(pdata, ADDRH);
+	u32 mac_low32 = smsc911x_mac_read(pdata, ADDRL);
+
+	dev->dev_addr[0] = (u8)(mac_low32);
+	dev->dev_addr[1] = (u8)(mac_low32 >> 8);
+	dev->dev_addr[2] = (u8)(mac_low32 >> 16);
+	dev->dev_addr[3] = (u8)(mac_low32 >> 24);
+	dev->dev_addr[4] = (u8)(mac_high16);
+	dev->dev_addr[5] = (u8)(mac_high16 >> 8);
+}
+
+/* Initializing private device structures, only called from probe */
+static int __devinit smsc911x_init(struct net_device *dev)
+{
+	struct smsc911x_data *pdata = netdev_priv(dev);
+	unsigned int byte_test;
+
+	SMSC_TRACE(pdata, probe, "Driver Parameters:");
+	SMSC_TRACE(pdata, probe, "LAN base: 0x%08lX",
+		   (unsigned long)pdata->ioaddr);
+	SMSC_TRACE(pdata, probe, "IRQ: %d", dev->irq);
+	SMSC_TRACE(pdata, probe, "PHY will be autodetected.");
+
+	spin_lock_init(&pdata->dev_lock);
+	spin_lock_init(&pdata->mac_lock);
+
+	if (pdata->ioaddr == 0) {
+		SMSC_WARN(pdata, probe, "pdata->ioaddr: 0x00000000");
+		return -ENODEV;
+	}
+
+	/* Check byte ordering */
+	byte_test = smsc911x_reg_read(pdata, BYTE_TEST);
+	SMSC_TRACE(pdata, probe, "BYTE_TEST: 0x%08X", byte_test);
+	if (byte_test == 0x43218765) {
+		SMSC_TRACE(pdata, probe, "BYTE_TEST looks swapped, "
+			   "applying WORD_SWAP");
+		smsc911x_reg_write(pdata, WORD_SWAP, 0xffffffff);
+
+		/* 1 dummy read of BYTE_TEST is needed after a write to
+		 * WORD_SWAP before its contents are valid */
+		byte_test = smsc911x_reg_read(pdata, BYTE_TEST);
+
+		byte_test = smsc911x_reg_read(pdata, BYTE_TEST);
+	}
+
+	if (byte_test != 0x87654321) {
+		SMSC_WARN(pdata, drv, "BYTE_TEST: 0x%08X", byte_test);
+		if (((byte_test >> 16) & 0xFFFF) == (byte_test & 0xFFFF)) {
+			SMSC_WARN(pdata, probe,
+				  "top 16 bits equal to bottom 16 bits");
+			SMSC_TRACE(pdata, probe,
+				   "This may mean the chip is set "
+				   "for 32 bit while the bus is reading 16 bit");
+		}
+		return -ENODEV;
+	}
+
+	/* Default generation to zero (all workarounds apply) */
+	pdata->generation = 0;
+
+	pdata->idrev = smsc911x_reg_read(pdata, ID_REV);
+	switch (pdata->idrev & 0xFFFF0000) {
+	case 0x01180000:
+	case 0x01170000:
+	case 0x01160000:
+	case 0x01150000:
+		/* LAN911[5678] family */
+		pdata->generation = pdata->idrev & 0x0000FFFF;
+		break;
+
+	case 0x118A0000:
+	case 0x117A0000:
+	case 0x116A0000:
+	case 0x115A0000:
+		/* LAN921[5678] family */
+		pdata->generation = 3;
+		break;
+
+	case 0x92100000:
+	case 0x92110000:
+	case 0x92200000:
+	case 0x92210000:
+		/* LAN9210/LAN9211/LAN9220/LAN9221 */
+		pdata->generation = 4;
+		break;
+
+	default:
+		SMSC_WARN(pdata, probe, "LAN911x not identified, idrev: 0x%08X",
+			  pdata->idrev);
+		return -ENODEV;
+	}
+
+	SMSC_TRACE(pdata, probe,
+		   "LAN911x identified, idrev: 0x%08X, generation: %d",
+		   pdata->idrev, pdata->generation);
+
+	if (pdata->generation == 0)
+		SMSC_WARN(pdata, probe,
+			  "This driver is not intended for this chip revision");
+
+	/* workaround for platforms without an eeprom, where the mac address
+	 * is stored elsewhere and set by the bootloader.  This saves the
+	 * mac address before resetting the device */
+	if (pdata->config.flags & SMSC911X_SAVE_MAC_ADDRESS) {
+		spin_lock_irq(&pdata->mac_lock);
+		smsc911x_read_mac_address(dev);
+		spin_unlock_irq(&pdata->mac_lock);
+	}
+
+	/* Reset the LAN911x */
+	if (smsc911x_soft_reset(pdata))
+		return -ENODEV;
+
+	/* Disable all interrupt sources until we bring the device up */
+	smsc911x_reg_write(pdata, INT_EN, 0);
+
+	ether_setup(dev);
+	dev->flags |= IFF_MULTICAST;
+	netif_napi_add(dev, &pdata->napi, smsc911x_poll, SMSC_NAPI_WEIGHT);
+	dev->netdev_ops = &smsc911x_netdev_ops;
+	dev->ethtool_ops = &smsc911x_ethtool_ops;
+
+	return 0;
+}
+
+static int __devexit smsc911x_drv_remove(struct platform_device *pdev)
+{
+	struct net_device *dev;
+	struct smsc911x_data *pdata;
+	struct resource *res;
+
+	dev = platform_get_drvdata(pdev);
+	BUG_ON(!dev);
+	pdata = netdev_priv(dev);
+	BUG_ON(!pdata);
+	BUG_ON(!pdata->ioaddr);
+	BUG_ON(!pdata->phy_dev);
+
+	SMSC_TRACE(pdata, ifdown, "Stopping driver");
+
+	phy_disconnect(pdata->phy_dev);
+	pdata->phy_dev = NULL;
+	mdiobus_unregister(pdata->mii_bus);
+	mdiobus_free(pdata->mii_bus);
+
+	platform_set_drvdata(pdev, NULL);
+	unregister_netdev(dev);
+	free_irq(dev->irq, dev);
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+					   "smsc911x-memory");
+	if (!res)
+		res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+	release_mem_region(res->start, resource_size(res));
+
+	iounmap(pdata->ioaddr);
+
+	free_netdev(dev);
+
+	return 0;
+}
+
+/* standard register acces */
+static const struct smsc911x_ops standard_smsc911x_ops = {
+	.reg_read = __smsc911x_reg_read,
+	.reg_write = __smsc911x_reg_write,
+	.rx_readfifo = smsc911x_rx_readfifo,
+	.tx_writefifo = smsc911x_tx_writefifo,
+};
+
+/* shifted register access */
+static const struct smsc911x_ops shifted_smsc911x_ops = {
+	.reg_read = __smsc911x_reg_read_shift,
+	.reg_write = __smsc911x_reg_write_shift,
+	.rx_readfifo = smsc911x_rx_readfifo_shift,
+	.tx_writefifo = smsc911x_tx_writefifo_shift,
+};
+
+#ifdef CONFIG_OF
+static int __devinit smsc911x_probe_config_dt(
+				struct smsc911x_platform_config *config,
+				struct device_node *np)
+{
+	const char *mac;
+	u32 width = 0;
+
+	if (!np)
+		return -ENODEV;
+
+	config->phy_interface = of_get_phy_mode(np);
+
+	mac = of_get_mac_address(np);
+	if (mac)
+		memcpy(config->mac, mac, ETH_ALEN);
+
+	of_property_read_u32(np, "reg-shift", &config->shift);
+
+	of_property_read_u32(np, "reg-io-width", &width);
+	if (width == 4)
+		config->flags |= SMSC911X_USE_32BIT;
+
+	if (of_get_property(np, "smsc,irq-active-high", NULL))
+		config->irq_polarity = SMSC911X_IRQ_POLARITY_ACTIVE_HIGH;
+
+	if (of_get_property(np, "smsc,irq-push-pull", NULL))
+		config->irq_type = SMSC911X_IRQ_TYPE_PUSH_PULL;
+
+	if (of_get_property(np, "smsc,force-internal-phy", NULL))
+		config->flags |= SMSC911X_FORCE_INTERNAL_PHY;
+
+	if (of_get_property(np, "smsc,force-external-phy", NULL))
+		config->flags |= SMSC911X_FORCE_EXTERNAL_PHY;
+
+	if (of_get_property(np, "smsc,save-mac-address", NULL))
+		config->flags |= SMSC911X_SAVE_MAC_ADDRESS;
+
+	return 0;
+}
+#else
+static inline int smsc911x_probe_config_dt(
+				struct smsc911x_platform_config *config,
+				struct device_node *np)
+{
+	return -ENODEV;
+}
+#endif /* CONFIG_OF */
+
+static int __devinit smsc911x_drv_probe(struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	struct net_device *dev;
+	struct smsc911x_data *pdata;
+	struct smsc911x_platform_config *config = pdev->dev.platform_data;
+	struct resource *res, *irq_res;
+	unsigned int intcfg = 0;
+	int res_size, irq_flags;
+	int retval;
+
+	pr_info("Driver version %s\n", SMSC_DRV_VERSION);
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+					   "smsc911x-memory");
+	if (!res)
+		res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		pr_warn("Could not allocate resource\n");
+		retval = -ENODEV;
+		goto out_0;
+	}
+	res_size = resource_size(res);
+
+	irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (!irq_res) {
+		pr_warn("Could not allocate irq resource\n");
+		retval = -ENODEV;
+		goto out_0;
+	}
+
+	if (!request_mem_region(res->start, res_size, SMSC_CHIPNAME)) {
+		retval = -EBUSY;
+		goto out_0;
+	}
+
+	dev = alloc_etherdev(sizeof(struct smsc911x_data));
+	if (!dev) {
+		pr_warn("Could not allocate device\n");
+		retval = -ENOMEM;
+		goto out_release_io_1;
+	}
+
+	SET_NETDEV_DEV(dev, &pdev->dev);
+
+	pdata = netdev_priv(dev);
+
+	dev->irq = irq_res->start;
+	irq_flags = irq_res->flags & IRQF_TRIGGER_MASK;
+	pdata->ioaddr = ioremap_nocache(res->start, res_size);
+
+	pdata->dev = dev;
+	pdata->msg_enable = ((1 << debug) - 1);
+
+	if (pdata->ioaddr == NULL) {
+		SMSC_WARN(pdata, probe, "Error smsc911x base address invalid");
+		retval = -ENOMEM;
+		goto out_free_netdev_2;
+	}
+
+	retval = smsc911x_probe_config_dt(&pdata->config, np);
+	if (retval && config) {
+		/* copy config parameters across to pdata */
+		memcpy(&pdata->config, config, sizeof(pdata->config));
+		retval = 0;
+	}
+
+	if (retval) {
+		SMSC_WARN(pdata, probe, "Error smsc911x config not found");
+		goto out_unmap_io_3;
+	}
+
+	/* assume standard, non-shifted, access to HW registers */
+	pdata->ops = &standard_smsc911x_ops;
+	/* apply the right access if shifting is needed */
+	if (pdata->config.shift)
+		pdata->ops = &shifted_smsc911x_ops;
+
+	retval = smsc911x_init(dev);
+	if (retval < 0)
+		goto out_unmap_io_3;
+
+	/* configure irq polarity and type before connecting isr */
+	if (pdata->config.irq_polarity == SMSC911X_IRQ_POLARITY_ACTIVE_HIGH)
+		intcfg |= INT_CFG_IRQ_POL_;
+
+	if (pdata->config.irq_type == SMSC911X_IRQ_TYPE_PUSH_PULL)
+		intcfg |= INT_CFG_IRQ_TYPE_;
+
+	smsc911x_reg_write(pdata, INT_CFG, intcfg);
+
+	/* Ensure interrupts are globally disabled before connecting ISR */
+	smsc911x_reg_write(pdata, INT_EN, 0);
+	smsc911x_reg_write(pdata, INT_STS, 0xFFFFFFFF);
+
+	retval = request_irq(dev->irq, smsc911x_irqhandler,
+			     irq_flags | IRQF_SHARED, dev->name, dev);
+	if (retval) {
+		SMSC_WARN(pdata, probe,
+			  "Unable to claim requested irq: %d", dev->irq);
+		goto out_unmap_io_3;
+	}
+
+	platform_set_drvdata(pdev, dev);
+
+	retval = register_netdev(dev);
+	if (retval) {
+		SMSC_WARN(pdata, probe, "Error %i registering device", retval);
+		goto out_unset_drvdata_4;
+	} else {
+		SMSC_TRACE(pdata, probe,
+			   "Network interface: \"%s\"", dev->name);
+	}
+
+	retval = smsc911x_mii_init(pdev, dev);
+	if (retval) {
+		SMSC_WARN(pdata, probe, "Error %i initialising mii", retval);
+		goto out_unregister_netdev_5;
+	}
+
+	spin_lock_irq(&pdata->mac_lock);
+
+	/* Check if mac address has been specified when bringing interface up */
+	if (is_valid_ether_addr(dev->dev_addr)) {
+		smsc911x_set_hw_mac_address(pdata, dev->dev_addr);
+		SMSC_TRACE(pdata, probe,
+			   "MAC Address is specified by configuration");
+	} else if (is_valid_ether_addr(pdata->config.mac)) {
+		memcpy(dev->dev_addr, pdata->config.mac, 6);
+		SMSC_TRACE(pdata, probe,
+			   "MAC Address specified by platform data");
+	} else {
+		/* Try reading mac address from device. if EEPROM is present
+		 * it will already have been set */
+		smsc_get_mac(dev);
+
+		if (is_valid_ether_addr(dev->dev_addr)) {
+			/* eeprom values are valid  so use them */
+			SMSC_TRACE(pdata, probe,
+				   "Mac Address is read from LAN911x EEPROM");
+		} else {
+			/* eeprom values are invalid, generate random MAC */
+			random_ether_addr(dev->dev_addr);
+			smsc911x_set_hw_mac_address(pdata, dev->dev_addr);
+			SMSC_TRACE(pdata, probe,
+				   "MAC Address is set to random_ether_addr");
+		}
+	}
+
+	spin_unlock_irq(&pdata->mac_lock);
+
+	netdev_info(dev, "MAC Address: %pM\n", dev->dev_addr);
+
+	return 0;
+
+out_unregister_netdev_5:
+	unregister_netdev(dev);
+out_unset_drvdata_4:
+	platform_set_drvdata(pdev, NULL);
+	free_irq(dev->irq, dev);
+out_unmap_io_3:
+	iounmap(pdata->ioaddr);
+out_free_netdev_2:
+	free_netdev(dev);
+out_release_io_1:
+	release_mem_region(res->start, resource_size(res));
+out_0:
+	return retval;
+}
+
+#ifdef CONFIG_PM
+/* This implementation assumes the devices remains powered on its VDDVARIO
+ * pins during suspend. */
+
+/* TODO: implement freeze/thaw callbacks for hibernation.*/
+
+static int smsc911x_suspend(struct device *dev)
+{
+	struct net_device *ndev = dev_get_drvdata(dev);
+	struct smsc911x_data *pdata = netdev_priv(ndev);
+
+	/* enable wake on LAN, energy detection and the external PME
+	 * signal. */
+	smsc911x_reg_write(pdata, PMT_CTRL,
+		PMT_CTRL_PM_MODE_D1_ | PMT_CTRL_WOL_EN_ |
+		PMT_CTRL_ED_EN_ | PMT_CTRL_PME_EN_);
+
+	return 0;
+}
+
+static int smsc911x_resume(struct device *dev)
+{
+	struct net_device *ndev = dev_get_drvdata(dev);
+	struct smsc911x_data *pdata = netdev_priv(ndev);
+	unsigned int to = 100;
+
+	/* Note 3.11 from the datasheet:
+	 * 	"When the LAN9220 is in a power saving state, a write of any
+	 * 	 data to the BYTE_TEST register will wake-up the device."
+	 */
+	smsc911x_reg_write(pdata, BYTE_TEST, 0);
+
+	/* poll the READY bit in PMT_CTRL. Any other access to the device is
+	 * forbidden while this bit isn't set. Try for 100ms and return -EIO
+	 * if it failed. */
+	while (!(smsc911x_reg_read(pdata, PMT_CTRL) & PMT_CTRL_READY_) && --to)
+		udelay(1000);
+
+	return (to == 0) ? -EIO : 0;
+}
+
+static const struct dev_pm_ops smsc911x_pm_ops = {
+	.suspend	= smsc911x_suspend,
+	.resume		= smsc911x_resume,
+};
+
+#define SMSC911X_PM_OPS (&smsc911x_pm_ops)
+
+#else
+#define SMSC911X_PM_OPS NULL
+#endif
+
+static const struct of_device_id smsc911x_dt_ids[] = {
+	{ .compatible = "smsc,lan9115", },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, smsc911x_dt_ids);
+
+static struct platform_driver smsc911x_driver = {
+	.probe = smsc911x_drv_probe,
+	.remove = __devexit_p(smsc911x_drv_remove),
+	.driver = {
+		.name	= SMSC_CHIPNAME,
+		.owner	= THIS_MODULE,
+		.pm	= SMSC911X_PM_OPS,
+		.of_match_table = smsc911x_dt_ids,
+	},
+};
+
+/* Entry point for loading the module */
+static int __init smsc911x_init_module(void)
+{
+	SMSC_INITIALIZE();
+	return platform_driver_register(&smsc911x_driver);
+}
+
+/* entry point for unloading the module */
+static void __exit smsc911x_cleanup_module(void)
+{
+	platform_driver_unregister(&smsc911x_driver);
+}
+
+module_init(smsc911x_init_module);
+module_exit(smsc911x_cleanup_module);
diff --git a/drivers/net/ethernet/smsc/smsc911x.h b/drivers/net/ethernet/smsc/smsc911x.h
new file mode 100644
index 0000000..8d67aac
--- /dev/null
+++ b/drivers/net/ethernet/smsc/smsc911x.h
@@ -0,0 +1,404 @@
+/***************************************************************************
+ *
+ * Copyright (C) 2004-2008 SMSC
+ * Copyright (C) 2005-2008 ARM
+ *
+ * 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.
+ *
+ ***************************************************************************/
+#ifndef __SMSC911X_H__
+#define __SMSC911X_H__
+
+#define TX_FIFO_LOW_THRESHOLD	((u32)1600)
+#define SMSC911X_EEPROM_SIZE	((u32)128)
+#define USE_DEBUG		0
+
+/* This is the maximum number of packets to be received every
+ * NAPI poll */
+#define SMSC_NAPI_WEIGHT	16
+
+/* implements a PHY loopback test at initialisation time, to ensure a packet
+ * can be successfully looped back */
+#define USE_PHY_WORK_AROUND
+
+#if USE_DEBUG >= 1
+#define SMSC_WARN(pdata, nlevel, fmt, args...)			\
+	netif_warn(pdata, nlevel, (pdata)->dev,			\
+		   "%s: " fmt "\n", __func__, ##args)
+#else
+#define SMSC_WARN(pdata, nlevel, fmt, args...)			\
+	no_printk(fmt "\n", ##args)
+#endif
+
+#if USE_DEBUG >= 2
+#define SMSC_TRACE(pdata, nlevel, fmt, args...)			\
+	netif_info(pdata, nlevel, pdata->dev, fmt "\n", ##args)
+#else
+#define SMSC_TRACE(pdata, nlevel, fmt, args...)			\
+	no_printk(fmt "\n", ##args)
+#endif
+
+#ifdef CONFIG_DEBUG_SPINLOCK
+#define SMSC_ASSERT_MAC_LOCK(pdata) \
+		WARN_ON(!spin_is_locked(&pdata->mac_lock))
+#else
+#define SMSC_ASSERT_MAC_LOCK(pdata) do {} while (0)
+#endif				/* CONFIG_DEBUG_SPINLOCK */
+
+/* SMSC911x registers and bitfields */
+#define RX_DATA_FIFO			0x00
+
+#define TX_DATA_FIFO			0x20
+#define TX_CMD_A_ON_COMP_		0x80000000
+#define TX_CMD_A_BUF_END_ALGN_		0x03000000
+#define TX_CMD_A_4_BYTE_ALGN_		0x00000000
+#define TX_CMD_A_16_BYTE_ALGN_		0x01000000
+#define TX_CMD_A_32_BYTE_ALGN_		0x02000000
+#define TX_CMD_A_DATA_OFFSET_		0x001F0000
+#define TX_CMD_A_FIRST_SEG_		0x00002000
+#define TX_CMD_A_LAST_SEG_		0x00001000
+#define TX_CMD_A_BUF_SIZE_		0x000007FF
+#define TX_CMD_B_PKT_TAG_		0xFFFF0000
+#define TX_CMD_B_ADD_CRC_DISABLE_	0x00002000
+#define TX_CMD_B_DISABLE_PADDING_	0x00001000
+#define TX_CMD_B_PKT_BYTE_LENGTH_	0x000007FF
+
+#define RX_STATUS_FIFO			0x40
+#define RX_STS_ES_			0x00008000
+#define RX_STS_LENGTH_ERR_		0x00001000
+#define RX_STS_MCAST_			0x00000400
+#define RX_STS_FRAME_TYPE_		0x00000020
+#define RX_STS_CRC_ERR_			0x00000002
+
+#define RX_STATUS_FIFO_PEEK		0x44
+
+#define TX_STATUS_FIFO			0x48
+#define TX_STS_ES_			0x00008000
+#define TX_STS_LOST_CARRIER_		0x00000800
+#define TX_STS_NO_CARRIER_		0x00000400
+#define TX_STS_LATE_COL_		0x00000200
+#define TX_STS_EXCESS_COL_		0x00000100
+
+#define TX_STATUS_FIFO_PEEK		0x4C
+
+#define ID_REV				0x50
+#define ID_REV_CHIP_ID_			0xFFFF0000
+#define ID_REV_REV_ID_			0x0000FFFF
+
+#define INT_CFG				0x54
+#define INT_CFG_INT_DEAS_		0xFF000000
+#define INT_CFG_INT_DEAS_CLR_		0x00004000
+#define INT_CFG_INT_DEAS_STS_		0x00002000
+#define INT_CFG_IRQ_INT_		0x00001000
+#define INT_CFG_IRQ_EN_			0x00000100
+#define INT_CFG_IRQ_POL_		0x00000010
+#define INT_CFG_IRQ_TYPE_		0x00000001
+
+#define INT_STS				0x58
+#define INT_STS_SW_INT_			0x80000000
+#define INT_STS_TXSTOP_INT_		0x02000000
+#define INT_STS_RXSTOP_INT_		0x01000000
+#define INT_STS_RXDFH_INT_		0x00800000
+#define INT_STS_RXDF_INT_		0x00400000
+#define INT_STS_TX_IOC_			0x00200000
+#define INT_STS_RXD_INT_		0x00100000
+#define INT_STS_GPT_INT_		0x00080000
+#define INT_STS_PHY_INT_		0x00040000
+#define INT_STS_PME_INT_		0x00020000
+#define INT_STS_TXSO_			0x00010000
+#define INT_STS_RWT_			0x00008000
+#define INT_STS_RXE_			0x00004000
+#define INT_STS_TXE_			0x00002000
+#define INT_STS_TDFU_			0x00000800
+#define INT_STS_TDFO_			0x00000400
+#define INT_STS_TDFA_			0x00000200
+#define INT_STS_TSFF_			0x00000100
+#define INT_STS_TSFL_			0x00000080
+#define INT_STS_RXDF_			0x00000040
+#define INT_STS_RDFL_			0x00000020
+#define INT_STS_RSFF_			0x00000010
+#define INT_STS_RSFL_			0x00000008
+#define INT_STS_GPIO2_INT_		0x00000004
+#define INT_STS_GPIO1_INT_		0x00000002
+#define INT_STS_GPIO0_INT_		0x00000001
+
+#define INT_EN				0x5C
+#define INT_EN_SW_INT_EN_		0x80000000
+#define INT_EN_TXSTOP_INT_EN_		0x02000000
+#define INT_EN_RXSTOP_INT_EN_		0x01000000
+#define INT_EN_RXDFH_INT_EN_		0x00800000
+#define INT_EN_TIOC_INT_EN_		0x00200000
+#define INT_EN_RXD_INT_EN_		0x00100000
+#define INT_EN_GPT_INT_EN_		0x00080000
+#define INT_EN_PHY_INT_EN_		0x00040000
+#define INT_EN_PME_INT_EN_		0x00020000
+#define INT_EN_TXSO_EN_			0x00010000
+#define INT_EN_RWT_EN_			0x00008000
+#define INT_EN_RXE_EN_			0x00004000
+#define INT_EN_TXE_EN_			0x00002000
+#define INT_EN_TDFU_EN_			0x00000800
+#define INT_EN_TDFO_EN_			0x00000400
+#define INT_EN_TDFA_EN_			0x00000200
+#define INT_EN_TSFF_EN_			0x00000100
+#define INT_EN_TSFL_EN_			0x00000080
+#define INT_EN_RXDF_EN_			0x00000040
+#define INT_EN_RDFL_EN_			0x00000020
+#define INT_EN_RSFF_EN_			0x00000010
+#define INT_EN_RSFL_EN_			0x00000008
+#define INT_EN_GPIO2_INT_		0x00000004
+#define INT_EN_GPIO1_INT_		0x00000002
+#define INT_EN_GPIO0_INT_		0x00000001
+
+#define BYTE_TEST			0x64
+
+#define FIFO_INT			0x68
+#define FIFO_INT_TX_AVAIL_LEVEL_	0xFF000000
+#define FIFO_INT_TX_STS_LEVEL_		0x00FF0000
+#define FIFO_INT_RX_AVAIL_LEVEL_	0x0000FF00
+#define FIFO_INT_RX_STS_LEVEL_		0x000000FF
+
+#define RX_CFG				0x6C
+#define RX_CFG_RX_END_ALGN_		0xC0000000
+#define RX_CFG_RX_END_ALGN4_		0x00000000
+#define RX_CFG_RX_END_ALGN16_		0x40000000
+#define RX_CFG_RX_END_ALGN32_		0x80000000
+#define RX_CFG_RX_DMA_CNT_		0x0FFF0000
+#define RX_CFG_RX_DUMP_			0x00008000
+#define RX_CFG_RXDOFF_			0x00001F00
+
+#define TX_CFG				0x70
+#define TX_CFG_TXS_DUMP_		0x00008000
+#define TX_CFG_TXD_DUMP_		0x00004000
+#define TX_CFG_TXSAO_			0x00000004
+#define TX_CFG_TX_ON_			0x00000002
+#define TX_CFG_STOP_TX_			0x00000001
+
+#define HW_CFG				0x74
+#define HW_CFG_TTM_			0x00200000
+#define HW_CFG_SF_			0x00100000
+#define HW_CFG_TX_FIF_SZ_		0x000F0000
+#define HW_CFG_TR_			0x00003000
+#define HW_CFG_SRST_			0x00000001
+
+/* only available on 115/117 */
+#define HW_CFG_PHY_CLK_SEL_		0x00000060
+#define HW_CFG_PHY_CLK_SEL_INT_PHY_	0x00000000
+#define HW_CFG_PHY_CLK_SEL_EXT_PHY_	0x00000020
+#define HW_CFG_PHY_CLK_SEL_CLK_DIS_	0x00000040
+#define HW_CFG_SMI_SEL_		 	0x00000010
+#define HW_CFG_EXT_PHY_DET_		0x00000008
+#define HW_CFG_EXT_PHY_EN_		0x00000004
+#define HW_CFG_SRST_TO_			0x00000002
+
+/* only available  on 116/118 */
+#define HW_CFG_32_16_BIT_MODE_		0x00000004
+
+#define RX_DP_CTRL			0x78
+#define RX_DP_CTRL_RX_FFWD_		0x80000000
+
+#define RX_FIFO_INF			0x7C
+#define RX_FIFO_INF_RXSUSED_		0x00FF0000
+#define RX_FIFO_INF_RXDUSED_		0x0000FFFF
+
+#define TX_FIFO_INF			0x80
+#define TX_FIFO_INF_TSUSED_		0x00FF0000
+#define TX_FIFO_INF_TDFREE_		0x0000FFFF
+
+#define PMT_CTRL			0x84
+#define PMT_CTRL_PM_MODE_		0x00003000
+#define PMT_CTRL_PM_MODE_D0_		0x00000000
+#define PMT_CTRL_PM_MODE_D1_		0x00001000
+#define PMT_CTRL_PM_MODE_D2_		0x00002000
+#define PMT_CTRL_PM_MODE_D3_		0x00003000
+#define PMT_CTRL_PHY_RST_		0x00000400
+#define PMT_CTRL_WOL_EN_		0x00000200
+#define PMT_CTRL_ED_EN_			0x00000100
+#define PMT_CTRL_PME_TYPE_		0x00000040
+#define PMT_CTRL_WUPS_			0x00000030
+#define PMT_CTRL_WUPS_NOWAKE_		0x00000000
+#define PMT_CTRL_WUPS_ED_		0x00000010
+#define PMT_CTRL_WUPS_WOL_		0x00000020
+#define PMT_CTRL_WUPS_MULTI_		0x00000030
+#define PMT_CTRL_PME_IND_		0x00000008
+#define PMT_CTRL_PME_POL_		0x00000004
+#define PMT_CTRL_PME_EN_		0x00000002
+#define PMT_CTRL_READY_			0x00000001
+
+#define GPIO_CFG			0x88
+#define GPIO_CFG_LED3_EN_		0x40000000
+#define GPIO_CFG_LED2_EN_		0x20000000
+#define GPIO_CFG_LED1_EN_		0x10000000
+#define GPIO_CFG_GPIO2_INT_POL_		0x04000000
+#define GPIO_CFG_GPIO1_INT_POL_		0x02000000
+#define GPIO_CFG_GPIO0_INT_POL_		0x01000000
+#define GPIO_CFG_EEPR_EN_		0x00700000
+#define GPIO_CFG_GPIOBUF2_		0x00040000
+#define GPIO_CFG_GPIOBUF1_		0x00020000
+#define GPIO_CFG_GPIOBUF0_		0x00010000
+#define GPIO_CFG_GPIODIR2_		0x00000400
+#define GPIO_CFG_GPIODIR1_		0x00000200
+#define GPIO_CFG_GPIODIR0_		0x00000100
+#define GPIO_CFG_GPIOD4_		0x00000020
+#define GPIO_CFG_GPIOD3_		0x00000010
+#define GPIO_CFG_GPIOD2_		0x00000004
+#define GPIO_CFG_GPIOD1_		0x00000002
+#define GPIO_CFG_GPIOD0_		0x00000001
+
+#define GPT_CFG				0x8C
+#define GPT_CFG_TIMER_EN_		0x20000000
+#define GPT_CFG_GPT_LOAD_		0x0000FFFF
+
+#define GPT_CNT				0x90
+#define GPT_CNT_GPT_CNT_		0x0000FFFF
+
+#define WORD_SWAP			0x98
+
+#define FREE_RUN			0x9C
+
+#define RX_DROP				0xA0
+
+#define MAC_CSR_CMD			0xA4
+#define MAC_CSR_CMD_CSR_BUSY_		0x80000000
+#define MAC_CSR_CMD_R_NOT_W_		0x40000000
+#define MAC_CSR_CMD_CSR_ADDR_		0x000000FF
+
+#define MAC_CSR_DATA			0xA8
+
+#define AFC_CFG				0xAC
+#define AFC_CFG_AFC_HI_			0x00FF0000
+#define AFC_CFG_AFC_LO_			0x0000FF00
+#define AFC_CFG_BACK_DUR_		0x000000F0
+#define AFC_CFG_FCMULT_			0x00000008
+#define AFC_CFG_FCBRD_			0x00000004
+#define AFC_CFG_FCADD_			0x00000002
+#define AFC_CFG_FCANY_			0x00000001
+
+#define E2P_CMD				0xB0
+#define E2P_CMD_EPC_BUSY_		0x80000000
+#define E2P_CMD_EPC_CMD_		0x70000000
+#define E2P_CMD_EPC_CMD_READ_		0x00000000
+#define E2P_CMD_EPC_CMD_EWDS_		0x10000000
+#define E2P_CMD_EPC_CMD_EWEN_		0x20000000
+#define E2P_CMD_EPC_CMD_WRITE_		0x30000000
+#define E2P_CMD_EPC_CMD_WRAL_		0x40000000
+#define E2P_CMD_EPC_CMD_ERASE_		0x50000000
+#define E2P_CMD_EPC_CMD_ERAL_		0x60000000
+#define E2P_CMD_EPC_CMD_RELOAD_		0x70000000
+#define E2P_CMD_EPC_TIMEOUT_		0x00000200
+#define E2P_CMD_MAC_ADDR_LOADED_	0x00000100
+#define E2P_CMD_EPC_ADDR_		0x000000FF
+
+#define E2P_DATA			0xB4
+#define E2P_DATA_EEPROM_DATA_		0x000000FF
+#define LAN_REGISTER_EXTENT		0x00000100
+
+/*
+ * MAC Control and Status Register (Indirect Address)
+ * Offset (through the MAC_CSR CMD and DATA port)
+ */
+#define MAC_CR				0x01
+#define MAC_CR_RXALL_			0x80000000
+#define MAC_CR_HBDIS_			0x10000000
+#define MAC_CR_RCVOWN_			0x00800000
+#define MAC_CR_LOOPBK_			0x00200000
+#define MAC_CR_FDPX_			0x00100000
+#define MAC_CR_MCPAS_			0x00080000
+#define MAC_CR_PRMS_			0x00040000
+#define MAC_CR_INVFILT_			0x00020000
+#define MAC_CR_PASSBAD_			0x00010000
+#define MAC_CR_HFILT_			0x00008000
+#define MAC_CR_HPFILT_			0x00002000
+#define MAC_CR_LCOLL_			0x00001000
+#define MAC_CR_BCAST_			0x00000800
+#define MAC_CR_DISRTY_			0x00000400
+#define MAC_CR_PADSTR_			0x00000100
+#define MAC_CR_BOLMT_MASK_		0x000000C0
+#define MAC_CR_DFCHK_			0x00000020
+#define MAC_CR_TXEN_			0x00000008
+#define MAC_CR_RXEN_			0x00000004
+
+#define ADDRH				0x02
+
+#define ADDRL				0x03
+
+#define HASHH				0x04
+
+#define HASHL				0x05
+
+#define MII_ACC				0x06
+#define MII_ACC_PHY_ADDR_		0x0000F800
+#define MII_ACC_MIIRINDA_		0x000007C0
+#define MII_ACC_MII_WRITE_		0x00000002
+#define MII_ACC_MII_BUSY_		0x00000001
+
+#define MII_DATA			0x07
+
+#define FLOW				0x08
+#define FLOW_FCPT_			0xFFFF0000
+#define FLOW_FCPASS_			0x00000004
+#define FLOW_FCEN_			0x00000002
+#define FLOW_FCBSY_			0x00000001
+
+#define VLAN1				0x09
+
+#define VLAN2				0x0A
+
+#define WUFF				0x0B
+
+#define WUCSR				0x0C
+#define WUCSR_GUE_			0x00000200
+#define WUCSR_WUFR_			0x00000040
+#define WUCSR_MPR_			0x00000020
+#define WUCSR_WAKE_EN_			0x00000004
+#define WUCSR_MPEN_			0x00000002
+
+/*
+ * Phy definitions (vendor-specific)
+ */
+#define LAN9118_PHY_ID			0x00C0001C
+
+#define MII_INTSTS			0x1D
+
+#define MII_INTMSK			0x1E
+#define PHY_INTMSK_AN_RCV_		(1 << 1)
+#define PHY_INTMSK_PDFAULT_		(1 << 2)
+#define PHY_INTMSK_AN_ACK_		(1 << 3)
+#define PHY_INTMSK_LNKDOWN_		(1 << 4)
+#define PHY_INTMSK_RFAULT_		(1 << 5)
+#define PHY_INTMSK_AN_COMP_		(1 << 6)
+#define PHY_INTMSK_ENERGYON_		(1 << 7)
+#define PHY_INTMSK_DEFAULT_		(PHY_INTMSK_ENERGYON_ | \
+					 PHY_INTMSK_AN_COMP_ | \
+					 PHY_INTMSK_RFAULT_ | \
+					 PHY_INTMSK_LNKDOWN_)
+
+#define ADVERTISE_PAUSE_ALL		(ADVERTISE_PAUSE_CAP | \
+					 ADVERTISE_PAUSE_ASYM)
+
+#define LPA_PAUSE_ALL			(LPA_PAUSE_CAP | \
+					 LPA_PAUSE_ASYM)
+
+/*
+ * Provide hooks to let the arch add to the initialisation procedure
+ * and to override the source of the MAC address.
+ */
+#define SMSC_INITIALIZE()		do {} while (0)
+#define smsc_get_mac(dev)		smsc911x_read_mac_address((dev))
+
+#ifdef CONFIG_SMSC911X_ARCH_HOOKS
+#include <asm/smsc911x.h>
+#endif
+
+#endif				/* __SMSC911X_H__ */
diff --git a/drivers/net/ethernet/smsc/smsc9420.c b/drivers/net/ethernet/smsc/smsc9420.c
new file mode 100644
index 0000000..459726f
--- /dev/null
+++ b/drivers/net/ethernet/smsc/smsc9420.c
@@ -0,0 +1,1763 @@
+ /***************************************************************************
+ *
+ * Copyright (C) 2007,2008  SMSC
+ *
+ * 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.
+ *
+ ***************************************************************************
+ */
+
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/phy.h>
+#include <linux/pci.h>
+#include <linux/if_vlan.h>
+#include <linux/dma-mapping.h>
+#include <linux/crc32.h>
+#include <linux/slab.h>
+#include <asm/unaligned.h>
+#include "smsc9420.h"
+
+#define DRV_NAME		"smsc9420"
+#define PFX			DRV_NAME ": "
+#define DRV_MDIONAME		"smsc9420-mdio"
+#define DRV_DESCRIPTION		"SMSC LAN9420 driver"
+#define DRV_VERSION		"1.01"
+
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
+
+struct smsc9420_dma_desc {
+	u32 status;
+	u32 length;
+	u32 buffer1;
+	u32 buffer2;
+};
+
+struct smsc9420_ring_info {
+	struct sk_buff *skb;
+	dma_addr_t mapping;
+};
+
+struct smsc9420_pdata {
+	void __iomem *base_addr;
+	struct pci_dev *pdev;
+	struct net_device *dev;
+
+	struct smsc9420_dma_desc *rx_ring;
+	struct smsc9420_dma_desc *tx_ring;
+	struct smsc9420_ring_info *tx_buffers;
+	struct smsc9420_ring_info *rx_buffers;
+	dma_addr_t rx_dma_addr;
+	dma_addr_t tx_dma_addr;
+	int tx_ring_head, tx_ring_tail;
+	int rx_ring_head, rx_ring_tail;
+
+	spinlock_t int_lock;
+	spinlock_t phy_lock;
+
+	struct napi_struct napi;
+
+	bool software_irq_signal;
+	bool rx_csum;
+	u32 msg_enable;
+
+	struct phy_device *phy_dev;
+	struct mii_bus *mii_bus;
+	int phy_irq[PHY_MAX_ADDR];
+	int last_duplex;
+	int last_carrier;
+};
+
+static DEFINE_PCI_DEVICE_TABLE(smsc9420_id_table) = {
+	{ PCI_VENDOR_ID_9420, PCI_DEVICE_ID_9420, PCI_ANY_ID, PCI_ANY_ID, },
+	{ 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, smsc9420_id_table);
+
+#define SMSC_MSG_DEFAULT (NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK)
+
+static uint smsc_debug;
+static uint debug = -1;
+module_param(debug, uint, 0);
+MODULE_PARM_DESC(debug, "debug level");
+
+#define smsc_dbg(TYPE, f, a...) \
+do {	if ((pd)->msg_enable & NETIF_MSG_##TYPE) \
+		printk(KERN_DEBUG PFX f "\n", ## a); \
+} while (0)
+
+#define smsc_info(TYPE, f, a...) \
+do {	if ((pd)->msg_enable & NETIF_MSG_##TYPE) \
+		printk(KERN_INFO PFX f "\n", ## a); \
+} while (0)
+
+#define smsc_warn(TYPE, f, a...) \
+do {	if ((pd)->msg_enable & NETIF_MSG_##TYPE) \
+		printk(KERN_WARNING PFX f "\n", ## a); \
+} while (0)
+
+static inline u32 smsc9420_reg_read(struct smsc9420_pdata *pd, u32 offset)
+{
+	return ioread32(pd->base_addr + offset);
+}
+
+static inline void
+smsc9420_reg_write(struct smsc9420_pdata *pd, u32 offset, u32 value)
+{
+	iowrite32(value, pd->base_addr + offset);
+}
+
+static inline void smsc9420_pci_flush_write(struct smsc9420_pdata *pd)
+{
+	/* to ensure PCI write completion, we must perform a PCI read */
+	smsc9420_reg_read(pd, ID_REV);
+}
+
+static int smsc9420_mii_read(struct mii_bus *bus, int phyaddr, int regidx)
+{
+	struct smsc9420_pdata *pd = (struct smsc9420_pdata *)bus->priv;
+	unsigned long flags;
+	u32 addr;
+	int i, reg = -EIO;
+
+	spin_lock_irqsave(&pd->phy_lock, flags);
+
+	/*  confirm MII not busy */
+	if ((smsc9420_reg_read(pd, MII_ACCESS) & MII_ACCESS_MII_BUSY_)) {
+		smsc_warn(DRV, "MII is busy???");
+		goto out;
+	}
+
+	/* set the address, index & direction (read from PHY) */
+	addr = ((phyaddr & 0x1F) << 11) | ((regidx & 0x1F) << 6) |
+		MII_ACCESS_MII_READ_;
+	smsc9420_reg_write(pd, MII_ACCESS, addr);
+
+	/* wait for read to complete with 50us timeout */
+	for (i = 0; i < 5; i++) {
+		if (!(smsc9420_reg_read(pd, MII_ACCESS) &
+			MII_ACCESS_MII_BUSY_)) {
+			reg = (u16)smsc9420_reg_read(pd, MII_DATA);
+			goto out;
+		}
+		udelay(10);
+	}
+
+	smsc_warn(DRV, "MII busy timeout!");
+
+out:
+	spin_unlock_irqrestore(&pd->phy_lock, flags);
+	return reg;
+}
+
+static int smsc9420_mii_write(struct mii_bus *bus, int phyaddr, int regidx,
+			   u16 val)
+{
+	struct smsc9420_pdata *pd = (struct smsc9420_pdata *)bus->priv;
+	unsigned long flags;
+	u32 addr;
+	int i, reg = -EIO;
+
+	spin_lock_irqsave(&pd->phy_lock, flags);
+
+	/* confirm MII not busy */
+	if ((smsc9420_reg_read(pd, MII_ACCESS) & MII_ACCESS_MII_BUSY_)) {
+		smsc_warn(DRV, "MII is busy???");
+		goto out;
+	}
+
+	/* put the data to write in the MAC */
+	smsc9420_reg_write(pd, MII_DATA, (u32)val);
+
+	/* set the address, index & direction (write to PHY) */
+	addr = ((phyaddr & 0x1F) << 11) | ((regidx & 0x1F) << 6) |
+		MII_ACCESS_MII_WRITE_;
+	smsc9420_reg_write(pd, MII_ACCESS, addr);
+
+	/* wait for write to complete with 50us timeout */
+	for (i = 0; i < 5; i++) {
+		if (!(smsc9420_reg_read(pd, MII_ACCESS) &
+			MII_ACCESS_MII_BUSY_)) {
+			reg = 0;
+			goto out;
+		}
+		udelay(10);
+	}
+
+	smsc_warn(DRV, "MII busy timeout!");
+
+out:
+	spin_unlock_irqrestore(&pd->phy_lock, flags);
+	return reg;
+}
+
+/* Returns hash bit number for given MAC address
+ * Example:
+ * 01 00 5E 00 00 01 -> returns bit number 31 */
+static u32 smsc9420_hash(u8 addr[ETH_ALEN])
+{
+	return (ether_crc(ETH_ALEN, addr) >> 26) & 0x3f;
+}
+
+static int smsc9420_eeprom_reload(struct smsc9420_pdata *pd)
+{
+	int timeout = 100000;
+
+	BUG_ON(!pd);
+
+	if (smsc9420_reg_read(pd, E2P_CMD) & E2P_CMD_EPC_BUSY_) {
+		smsc_dbg(DRV, "smsc9420_eeprom_reload: Eeprom busy");
+		return -EIO;
+	}
+
+	smsc9420_reg_write(pd, E2P_CMD,
+		(E2P_CMD_EPC_BUSY_ | E2P_CMD_EPC_CMD_RELOAD_));
+
+	do {
+		udelay(10);
+		if (!(smsc9420_reg_read(pd, E2P_CMD) & E2P_CMD_EPC_BUSY_))
+			return 0;
+	} while (timeout--);
+
+	smsc_warn(DRV, "smsc9420_eeprom_reload: Eeprom timed out");
+	return -EIO;
+}
+
+/* Standard ioctls for mii-tool */
+static int smsc9420_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+	struct smsc9420_pdata *pd = netdev_priv(dev);
+
+	if (!netif_running(dev) || !pd->phy_dev)
+		return -EINVAL;
+
+	return phy_mii_ioctl(pd->phy_dev, ifr, cmd);
+}
+
+static int smsc9420_ethtool_get_settings(struct net_device *dev,
+					 struct ethtool_cmd *cmd)
+{
+	struct smsc9420_pdata *pd = netdev_priv(dev);
+
+	if (!pd->phy_dev)
+		return -ENODEV;
+
+	cmd->maxtxpkt = 1;
+	cmd->maxrxpkt = 1;
+	return phy_ethtool_gset(pd->phy_dev, cmd);
+}
+
+static int smsc9420_ethtool_set_settings(struct net_device *dev,
+					 struct ethtool_cmd *cmd)
+{
+	struct smsc9420_pdata *pd = netdev_priv(dev);
+
+	if (!pd->phy_dev)
+		return -ENODEV;
+
+	return phy_ethtool_sset(pd->phy_dev, cmd);
+}
+
+static void smsc9420_ethtool_get_drvinfo(struct net_device *netdev,
+					 struct ethtool_drvinfo *drvinfo)
+{
+	struct smsc9420_pdata *pd = netdev_priv(netdev);
+
+	strcpy(drvinfo->driver, DRV_NAME);
+	strcpy(drvinfo->bus_info, pci_name(pd->pdev));
+	strcpy(drvinfo->version, DRV_VERSION);
+}
+
+static u32 smsc9420_ethtool_get_msglevel(struct net_device *netdev)
+{
+	struct smsc9420_pdata *pd = netdev_priv(netdev);
+	return pd->msg_enable;
+}
+
+static void smsc9420_ethtool_set_msglevel(struct net_device *netdev, u32 data)
+{
+	struct smsc9420_pdata *pd = netdev_priv(netdev);
+	pd->msg_enable = data;
+}
+
+static int smsc9420_ethtool_nway_reset(struct net_device *netdev)
+{
+	struct smsc9420_pdata *pd = netdev_priv(netdev);
+
+	if (!pd->phy_dev)
+		return -ENODEV;
+
+	return phy_start_aneg(pd->phy_dev);
+}
+
+static int smsc9420_ethtool_getregslen(struct net_device *dev)
+{
+	/* all smsc9420 registers plus all phy registers */
+	return 0x100 + (32 * sizeof(u32));
+}
+
+static void
+smsc9420_ethtool_getregs(struct net_device *dev, struct ethtool_regs *regs,
+			 void *buf)
+{
+	struct smsc9420_pdata *pd = netdev_priv(dev);
+	struct phy_device *phy_dev = pd->phy_dev;
+	unsigned int i, j = 0;
+	u32 *data = buf;
+
+	regs->version = smsc9420_reg_read(pd, ID_REV);
+	for (i = 0; i < 0x100; i += (sizeof(u32)))
+		data[j++] = smsc9420_reg_read(pd, i);
+
+	// cannot read phy registers if the net device is down
+	if (!phy_dev)
+		return;
+
+	for (i = 0; i <= 31; i++)
+		data[j++] = smsc9420_mii_read(phy_dev->bus, phy_dev->addr, i);
+}
+
+static void smsc9420_eeprom_enable_access(struct smsc9420_pdata *pd)
+{
+	unsigned int temp = smsc9420_reg_read(pd, GPIO_CFG);
+	temp &= ~GPIO_CFG_EEPR_EN_;
+	smsc9420_reg_write(pd, GPIO_CFG, temp);
+	msleep(1);
+}
+
+static int smsc9420_eeprom_send_cmd(struct smsc9420_pdata *pd, u32 op)
+{
+	int timeout = 100;
+	u32 e2cmd;
+
+	smsc_dbg(HW, "op 0x%08x", op);
+	if (smsc9420_reg_read(pd, E2P_CMD) & E2P_CMD_EPC_BUSY_) {
+		smsc_warn(HW, "Busy at start");
+		return -EBUSY;
+	}
+
+	e2cmd = op | E2P_CMD_EPC_BUSY_;
+	smsc9420_reg_write(pd, E2P_CMD, e2cmd);
+
+	do {
+		msleep(1);
+		e2cmd = smsc9420_reg_read(pd, E2P_CMD);
+	} while ((e2cmd & E2P_CMD_EPC_BUSY_) && (--timeout));
+
+	if (!timeout) {
+		smsc_info(HW, "TIMED OUT");
+		return -EAGAIN;
+	}
+
+	if (e2cmd & E2P_CMD_EPC_TIMEOUT_) {
+		smsc_info(HW, "Error occurred during eeprom operation");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int smsc9420_eeprom_read_location(struct smsc9420_pdata *pd,
+					 u8 address, u8 *data)
+{
+	u32 op = E2P_CMD_EPC_CMD_READ_ | address;
+	int ret;
+
+	smsc_dbg(HW, "address 0x%x", address);
+	ret = smsc9420_eeprom_send_cmd(pd, op);
+
+	if (!ret)
+		data[address] = smsc9420_reg_read(pd, E2P_DATA);
+
+	return ret;
+}
+
+static int smsc9420_eeprom_write_location(struct smsc9420_pdata *pd,
+					  u8 address, u8 data)
+{
+	u32 op = E2P_CMD_EPC_CMD_ERASE_ | address;
+	int ret;
+
+	smsc_dbg(HW, "address 0x%x, data 0x%x", address, data);
+	ret = smsc9420_eeprom_send_cmd(pd, op);
+
+	if (!ret) {
+		op = E2P_CMD_EPC_CMD_WRITE_ | address;
+		smsc9420_reg_write(pd, E2P_DATA, (u32)data);
+		ret = smsc9420_eeprom_send_cmd(pd, op);
+	}
+
+	return ret;
+}
+
+static int smsc9420_ethtool_get_eeprom_len(struct net_device *dev)
+{
+	return SMSC9420_EEPROM_SIZE;
+}
+
+static int smsc9420_ethtool_get_eeprom(struct net_device *dev,
+				       struct ethtool_eeprom *eeprom, u8 *data)
+{
+	struct smsc9420_pdata *pd = netdev_priv(dev);
+	u8 eeprom_data[SMSC9420_EEPROM_SIZE];
+	int len, i;
+
+	smsc9420_eeprom_enable_access(pd);
+
+	len = min(eeprom->len, SMSC9420_EEPROM_SIZE);
+	for (i = 0; i < len; i++) {
+		int ret = smsc9420_eeprom_read_location(pd, i, eeprom_data);
+		if (ret < 0) {
+			eeprom->len = 0;
+			return ret;
+		}
+	}
+
+	memcpy(data, &eeprom_data[eeprom->offset], len);
+	eeprom->magic = SMSC9420_EEPROM_MAGIC;
+	eeprom->len = len;
+	return 0;
+}
+
+static int smsc9420_ethtool_set_eeprom(struct net_device *dev,
+				       struct ethtool_eeprom *eeprom, u8 *data)
+{
+	struct smsc9420_pdata *pd = netdev_priv(dev);
+	int ret;
+
+	if (eeprom->magic != SMSC9420_EEPROM_MAGIC)
+		return -EINVAL;
+
+	smsc9420_eeprom_enable_access(pd);
+	smsc9420_eeprom_send_cmd(pd, E2P_CMD_EPC_CMD_EWEN_);
+	ret = smsc9420_eeprom_write_location(pd, eeprom->offset, *data);
+	smsc9420_eeprom_send_cmd(pd, E2P_CMD_EPC_CMD_EWDS_);
+
+	/* Single byte write, according to man page */
+	eeprom->len = 1;
+
+	return ret;
+}
+
+static const struct ethtool_ops smsc9420_ethtool_ops = {
+	.get_settings = smsc9420_ethtool_get_settings,
+	.set_settings = smsc9420_ethtool_set_settings,
+	.get_drvinfo = smsc9420_ethtool_get_drvinfo,
+	.get_msglevel = smsc9420_ethtool_get_msglevel,
+	.set_msglevel = smsc9420_ethtool_set_msglevel,
+	.nway_reset = smsc9420_ethtool_nway_reset,
+	.get_link = ethtool_op_get_link,
+	.get_eeprom_len = smsc9420_ethtool_get_eeprom_len,
+	.get_eeprom = smsc9420_ethtool_get_eeprom,
+	.set_eeprom = smsc9420_ethtool_set_eeprom,
+	.get_regs_len = smsc9420_ethtool_getregslen,
+	.get_regs = smsc9420_ethtool_getregs,
+};
+
+/* Sets the device MAC address to dev_addr */
+static void smsc9420_set_mac_address(struct net_device *dev)
+{
+	struct smsc9420_pdata *pd = netdev_priv(dev);
+	u8 *dev_addr = dev->dev_addr;
+	u32 mac_high16 = (dev_addr[5] << 8) | dev_addr[4];
+	u32 mac_low32 = (dev_addr[3] << 24) | (dev_addr[2] << 16) |
+	    (dev_addr[1] << 8) | dev_addr[0];
+
+	smsc9420_reg_write(pd, ADDRH, mac_high16);
+	smsc9420_reg_write(pd, ADDRL, mac_low32);
+}
+
+static void smsc9420_check_mac_address(struct net_device *dev)
+{
+	struct smsc9420_pdata *pd = netdev_priv(dev);
+
+	/* Check if mac address has been specified when bringing interface up */
+	if (is_valid_ether_addr(dev->dev_addr)) {
+		smsc9420_set_mac_address(dev);
+		smsc_dbg(PROBE, "MAC Address is specified by configuration");
+	} else {
+		/* Try reading mac address from device. if EEPROM is present
+		 * it will already have been set */
+		u32 mac_high16 = smsc9420_reg_read(pd, ADDRH);
+		u32 mac_low32 = smsc9420_reg_read(pd, ADDRL);
+		dev->dev_addr[0] = (u8)(mac_low32);
+		dev->dev_addr[1] = (u8)(mac_low32 >> 8);
+		dev->dev_addr[2] = (u8)(mac_low32 >> 16);
+		dev->dev_addr[3] = (u8)(mac_low32 >> 24);
+		dev->dev_addr[4] = (u8)(mac_high16);
+		dev->dev_addr[5] = (u8)(mac_high16 >> 8);
+
+		if (is_valid_ether_addr(dev->dev_addr)) {
+			/* eeprom values are valid  so use them */
+			smsc_dbg(PROBE, "Mac Address is read from EEPROM");
+		} else {
+			/* eeprom values are invalid, generate random MAC */
+			random_ether_addr(dev->dev_addr);
+			smsc9420_set_mac_address(dev);
+			smsc_dbg(PROBE,
+				"MAC Address is set to random_ether_addr");
+		}
+	}
+}
+
+static void smsc9420_stop_tx(struct smsc9420_pdata *pd)
+{
+	u32 dmac_control, mac_cr, dma_intr_ena;
+	int timeout = 1000;
+
+	/* disable TX DMAC */
+	dmac_control = smsc9420_reg_read(pd, DMAC_CONTROL);
+	dmac_control &= (~DMAC_CONTROL_ST_);
+	smsc9420_reg_write(pd, DMAC_CONTROL, dmac_control);
+
+	/* Wait max 10ms for transmit process to stop */
+	while (--timeout) {
+		if (smsc9420_reg_read(pd, DMAC_STATUS) & DMAC_STS_TS_)
+			break;
+		udelay(10);
+	}
+
+	if (!timeout)
+		smsc_warn(IFDOWN, "TX DMAC failed to stop");
+
+	/* ACK Tx DMAC stop bit */
+	smsc9420_reg_write(pd, DMAC_STATUS, DMAC_STS_TXPS_);
+
+	/* mask TX DMAC interrupts */
+	dma_intr_ena = smsc9420_reg_read(pd, DMAC_INTR_ENA);
+	dma_intr_ena &= ~(DMAC_INTR_ENA_TX_);
+	smsc9420_reg_write(pd, DMAC_INTR_ENA, dma_intr_ena);
+	smsc9420_pci_flush_write(pd);
+
+	/* stop MAC TX */
+	mac_cr = smsc9420_reg_read(pd, MAC_CR) & (~MAC_CR_TXEN_);
+	smsc9420_reg_write(pd, MAC_CR, mac_cr);
+	smsc9420_pci_flush_write(pd);
+}
+
+static void smsc9420_free_tx_ring(struct smsc9420_pdata *pd)
+{
+	int i;
+
+	BUG_ON(!pd->tx_ring);
+
+	if (!pd->tx_buffers)
+		return;
+
+	for (i = 0; i < TX_RING_SIZE; i++) {
+		struct sk_buff *skb = pd->tx_buffers[i].skb;
+
+		if (skb) {
+			BUG_ON(!pd->tx_buffers[i].mapping);
+			pci_unmap_single(pd->pdev, pd->tx_buffers[i].mapping,
+					 skb->len, PCI_DMA_TODEVICE);
+			dev_kfree_skb_any(skb);
+		}
+
+		pd->tx_ring[i].status = 0;
+		pd->tx_ring[i].length = 0;
+		pd->tx_ring[i].buffer1 = 0;
+		pd->tx_ring[i].buffer2 = 0;
+	}
+	wmb();
+
+	kfree(pd->tx_buffers);
+	pd->tx_buffers = NULL;
+
+	pd->tx_ring_head = 0;
+	pd->tx_ring_tail = 0;
+}
+
+static void smsc9420_free_rx_ring(struct smsc9420_pdata *pd)
+{
+	int i;
+
+	BUG_ON(!pd->rx_ring);
+
+	if (!pd->rx_buffers)
+		return;
+
+	for (i = 0; i < RX_RING_SIZE; i++) {
+		if (pd->rx_buffers[i].skb)
+			dev_kfree_skb_any(pd->rx_buffers[i].skb);
+
+		if (pd->rx_buffers[i].mapping)
+			pci_unmap_single(pd->pdev, pd->rx_buffers[i].mapping,
+				PKT_BUF_SZ, PCI_DMA_FROMDEVICE);
+
+		pd->rx_ring[i].status = 0;
+		pd->rx_ring[i].length = 0;
+		pd->rx_ring[i].buffer1 = 0;
+		pd->rx_ring[i].buffer2 = 0;
+	}
+	wmb();
+
+	kfree(pd->rx_buffers);
+	pd->rx_buffers = NULL;
+
+	pd->rx_ring_head = 0;
+	pd->rx_ring_tail = 0;
+}
+
+static void smsc9420_stop_rx(struct smsc9420_pdata *pd)
+{
+	int timeout = 1000;
+	u32 mac_cr, dmac_control, dma_intr_ena;
+
+	/* mask RX DMAC interrupts */
+	dma_intr_ena = smsc9420_reg_read(pd, DMAC_INTR_ENA);
+	dma_intr_ena &= (~DMAC_INTR_ENA_RX_);
+	smsc9420_reg_write(pd, DMAC_INTR_ENA, dma_intr_ena);
+	smsc9420_pci_flush_write(pd);
+
+	/* stop RX MAC prior to stoping DMA */
+	mac_cr = smsc9420_reg_read(pd, MAC_CR) & (~MAC_CR_RXEN_);
+	smsc9420_reg_write(pd, MAC_CR, mac_cr);
+	smsc9420_pci_flush_write(pd);
+
+	/* stop RX DMAC */
+	dmac_control = smsc9420_reg_read(pd, DMAC_CONTROL);
+	dmac_control &= (~DMAC_CONTROL_SR_);
+	smsc9420_reg_write(pd, DMAC_CONTROL, dmac_control);
+	smsc9420_pci_flush_write(pd);
+
+	/* wait up to 10ms for receive to stop */
+	while (--timeout) {
+		if (smsc9420_reg_read(pd, DMAC_STATUS) & DMAC_STS_RS_)
+			break;
+		udelay(10);
+	}
+
+	if (!timeout)
+		smsc_warn(IFDOWN, "RX DMAC did not stop! timeout.");
+
+	/* ACK the Rx DMAC stop bit */
+	smsc9420_reg_write(pd, DMAC_STATUS, DMAC_STS_RXPS_);
+}
+
+static irqreturn_t smsc9420_isr(int irq, void *dev_id)
+{
+	struct smsc9420_pdata *pd = dev_id;
+	u32 int_cfg, int_sts, int_ctl;
+	irqreturn_t ret = IRQ_NONE;
+	ulong flags;
+
+	BUG_ON(!pd);
+	BUG_ON(!pd->base_addr);
+
+	int_cfg = smsc9420_reg_read(pd, INT_CFG);
+
+	/* check if it's our interrupt */
+	if ((int_cfg & (INT_CFG_IRQ_EN_ | INT_CFG_IRQ_INT_)) !=
+	    (INT_CFG_IRQ_EN_ | INT_CFG_IRQ_INT_))
+		return IRQ_NONE;
+
+	int_sts = smsc9420_reg_read(pd, INT_STAT);
+
+	if (likely(INT_STAT_DMAC_INT_ & int_sts)) {
+		u32 status = smsc9420_reg_read(pd, DMAC_STATUS);
+		u32 ints_to_clear = 0;
+
+		if (status & DMAC_STS_TX_) {
+			ints_to_clear |= (DMAC_STS_TX_ | DMAC_STS_NIS_);
+			netif_wake_queue(pd->dev);
+		}
+
+		if (status & DMAC_STS_RX_) {
+			/* mask RX DMAC interrupts */
+			u32 dma_intr_ena = smsc9420_reg_read(pd, DMAC_INTR_ENA);
+			dma_intr_ena &= (~DMAC_INTR_ENA_RX_);
+			smsc9420_reg_write(pd, DMAC_INTR_ENA, dma_intr_ena);
+			smsc9420_pci_flush_write(pd);
+
+			ints_to_clear |= (DMAC_STS_RX_ | DMAC_STS_NIS_);
+			napi_schedule(&pd->napi);
+		}
+
+		if (ints_to_clear)
+			smsc9420_reg_write(pd, DMAC_STATUS, ints_to_clear);
+
+		ret = IRQ_HANDLED;
+	}
+
+	if (unlikely(INT_STAT_SW_INT_ & int_sts)) {
+		/* mask software interrupt */
+		spin_lock_irqsave(&pd->int_lock, flags);
+		int_ctl = smsc9420_reg_read(pd, INT_CTL);
+		int_ctl &= (~INT_CTL_SW_INT_EN_);
+		smsc9420_reg_write(pd, INT_CTL, int_ctl);
+		spin_unlock_irqrestore(&pd->int_lock, flags);
+
+		smsc9420_reg_write(pd, INT_STAT, INT_STAT_SW_INT_);
+		pd->software_irq_signal = true;
+		smp_wmb();
+
+		ret = IRQ_HANDLED;
+	}
+
+	/* to ensure PCI write completion, we must perform a PCI read */
+	smsc9420_pci_flush_write(pd);
+
+	return ret;
+}
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void smsc9420_poll_controller(struct net_device *dev)
+{
+	disable_irq(dev->irq);
+	smsc9420_isr(0, dev);
+	enable_irq(dev->irq);
+}
+#endif /* CONFIG_NET_POLL_CONTROLLER */
+
+static void smsc9420_dmac_soft_reset(struct smsc9420_pdata *pd)
+{
+	smsc9420_reg_write(pd, BUS_MODE, BUS_MODE_SWR_);
+	smsc9420_reg_read(pd, BUS_MODE);
+	udelay(2);
+	if (smsc9420_reg_read(pd, BUS_MODE) & BUS_MODE_SWR_)
+		smsc_warn(DRV, "Software reset not cleared");
+}
+
+static int smsc9420_stop(struct net_device *dev)
+{
+	struct smsc9420_pdata *pd = netdev_priv(dev);
+	u32 int_cfg;
+	ulong flags;
+
+	BUG_ON(!pd);
+	BUG_ON(!pd->phy_dev);
+
+	/* disable master interrupt */
+	spin_lock_irqsave(&pd->int_lock, flags);
+	int_cfg = smsc9420_reg_read(pd, INT_CFG) & (~INT_CFG_IRQ_EN_);
+	smsc9420_reg_write(pd, INT_CFG, int_cfg);
+	spin_unlock_irqrestore(&pd->int_lock, flags);
+
+	netif_tx_disable(dev);
+	napi_disable(&pd->napi);
+
+	smsc9420_stop_tx(pd);
+	smsc9420_free_tx_ring(pd);
+
+	smsc9420_stop_rx(pd);
+	smsc9420_free_rx_ring(pd);
+
+	free_irq(dev->irq, pd);
+
+	smsc9420_dmac_soft_reset(pd);
+
+	phy_stop(pd->phy_dev);
+
+	phy_disconnect(pd->phy_dev);
+	pd->phy_dev = NULL;
+	mdiobus_unregister(pd->mii_bus);
+	mdiobus_free(pd->mii_bus);
+
+	return 0;
+}
+
+static void smsc9420_rx_count_stats(struct net_device *dev, u32 desc_status)
+{
+	if (unlikely(desc_status & RDES0_ERROR_SUMMARY_)) {
+		dev->stats.rx_errors++;
+		if (desc_status & RDES0_DESCRIPTOR_ERROR_)
+			dev->stats.rx_over_errors++;
+		else if (desc_status & (RDES0_FRAME_TOO_LONG_ |
+			RDES0_RUNT_FRAME_ | RDES0_COLLISION_SEEN_))
+			dev->stats.rx_frame_errors++;
+		else if (desc_status & RDES0_CRC_ERROR_)
+			dev->stats.rx_crc_errors++;
+	}
+
+	if (unlikely(desc_status & RDES0_LENGTH_ERROR_))
+		dev->stats.rx_length_errors++;
+
+	if (unlikely(!((desc_status & RDES0_LAST_DESCRIPTOR_) &&
+		(desc_status & RDES0_FIRST_DESCRIPTOR_))))
+		dev->stats.rx_length_errors++;
+
+	if (desc_status & RDES0_MULTICAST_FRAME_)
+		dev->stats.multicast++;
+}
+
+static void smsc9420_rx_handoff(struct smsc9420_pdata *pd, const int index,
+				const u32 status)
+{
+	struct net_device *dev = pd->dev;
+	struct sk_buff *skb;
+	u16 packet_length = (status & RDES0_FRAME_LENGTH_MASK_)
+		>> RDES0_FRAME_LENGTH_SHFT_;
+
+	/* remove crc from packet lendth */
+	packet_length -= 4;
+
+	if (pd->rx_csum)
+		packet_length -= 2;
+
+	dev->stats.rx_packets++;
+	dev->stats.rx_bytes += packet_length;
+
+	pci_unmap_single(pd->pdev, pd->rx_buffers[index].mapping,
+		PKT_BUF_SZ, PCI_DMA_FROMDEVICE);
+	pd->rx_buffers[index].mapping = 0;
+
+	skb = pd->rx_buffers[index].skb;
+	pd->rx_buffers[index].skb = NULL;
+
+	if (pd->rx_csum) {
+		u16 hw_csum = get_unaligned_le16(skb_tail_pointer(skb) +
+			NET_IP_ALIGN + packet_length + 4);
+		put_unaligned_le16(hw_csum, &skb->csum);
+		skb->ip_summed = CHECKSUM_COMPLETE;
+	}
+
+	skb_reserve(skb, NET_IP_ALIGN);
+	skb_put(skb, packet_length);
+
+	skb->protocol = eth_type_trans(skb, dev);
+
+	netif_receive_skb(skb);
+}
+
+static int smsc9420_alloc_rx_buffer(struct smsc9420_pdata *pd, int index)
+{
+	struct sk_buff *skb = netdev_alloc_skb(pd->dev, PKT_BUF_SZ);
+	dma_addr_t mapping;
+
+	BUG_ON(pd->rx_buffers[index].skb);
+	BUG_ON(pd->rx_buffers[index].mapping);
+
+	if (unlikely(!skb)) {
+		smsc_warn(RX_ERR, "Failed to allocate new skb!");
+		return -ENOMEM;
+	}
+
+	skb->dev = pd->dev;
+
+	mapping = pci_map_single(pd->pdev, skb_tail_pointer(skb),
+				 PKT_BUF_SZ, PCI_DMA_FROMDEVICE);
+	if (pci_dma_mapping_error(pd->pdev, mapping)) {
+		dev_kfree_skb_any(skb);
+		smsc_warn(RX_ERR, "pci_map_single failed!");
+		return -ENOMEM;
+	}
+
+	pd->rx_buffers[index].skb = skb;
+	pd->rx_buffers[index].mapping = mapping;
+	pd->rx_ring[index].buffer1 = mapping + NET_IP_ALIGN;
+	pd->rx_ring[index].status = RDES0_OWN_;
+	wmb();
+
+	return 0;
+}
+
+static void smsc9420_alloc_new_rx_buffers(struct smsc9420_pdata *pd)
+{
+	while (pd->rx_ring_tail != pd->rx_ring_head) {
+		if (smsc9420_alloc_rx_buffer(pd, pd->rx_ring_tail))
+			break;
+
+		pd->rx_ring_tail = (pd->rx_ring_tail + 1) % RX_RING_SIZE;
+	}
+}
+
+static int smsc9420_rx_poll(struct napi_struct *napi, int budget)
+{
+	struct smsc9420_pdata *pd =
+		container_of(napi, struct smsc9420_pdata, napi);
+	struct net_device *dev = pd->dev;
+	u32 drop_frame_cnt, dma_intr_ena, status;
+	int work_done;
+
+	for (work_done = 0; work_done < budget; work_done++) {
+		rmb();
+		status = pd->rx_ring[pd->rx_ring_head].status;
+
+		/* stop if DMAC owns this dma descriptor */
+		if (status & RDES0_OWN_)
+			break;
+
+		smsc9420_rx_count_stats(dev, status);
+		smsc9420_rx_handoff(pd, pd->rx_ring_head, status);
+		pd->rx_ring_head = (pd->rx_ring_head + 1) % RX_RING_SIZE;
+		smsc9420_alloc_new_rx_buffers(pd);
+	}
+
+	drop_frame_cnt = smsc9420_reg_read(pd, MISS_FRAME_CNTR);
+	dev->stats.rx_dropped +=
+	    (drop_frame_cnt & 0xFFFF) + ((drop_frame_cnt >> 17) & 0x3FF);
+
+	/* Kick RXDMA */
+	smsc9420_reg_write(pd, RX_POLL_DEMAND, 1);
+	smsc9420_pci_flush_write(pd);
+
+	if (work_done < budget) {
+		napi_complete(&pd->napi);
+
+		/* re-enable RX DMA interrupts */
+		dma_intr_ena = smsc9420_reg_read(pd, DMAC_INTR_ENA);
+		dma_intr_ena |= (DMAC_INTR_ENA_RX_ | DMAC_INTR_ENA_NIS_);
+		smsc9420_reg_write(pd, DMAC_INTR_ENA, dma_intr_ena);
+		smsc9420_pci_flush_write(pd);
+	}
+	return work_done;
+}
+
+static void
+smsc9420_tx_update_stats(struct net_device *dev, u32 status, u32 length)
+{
+	if (unlikely(status & TDES0_ERROR_SUMMARY_)) {
+		dev->stats.tx_errors++;
+		if (status & (TDES0_EXCESSIVE_DEFERRAL_ |
+			TDES0_EXCESSIVE_COLLISIONS_))
+			dev->stats.tx_aborted_errors++;
+
+		if (status & (TDES0_LOSS_OF_CARRIER_ | TDES0_NO_CARRIER_))
+			dev->stats.tx_carrier_errors++;
+	} else {
+		dev->stats.tx_packets++;
+		dev->stats.tx_bytes += (length & 0x7FF);
+	}
+
+	if (unlikely(status & TDES0_EXCESSIVE_COLLISIONS_)) {
+		dev->stats.collisions += 16;
+	} else {
+		dev->stats.collisions +=
+			(status & TDES0_COLLISION_COUNT_MASK_) >>
+			TDES0_COLLISION_COUNT_SHFT_;
+	}
+
+	if (unlikely(status & TDES0_HEARTBEAT_FAIL_))
+		dev->stats.tx_heartbeat_errors++;
+}
+
+/* Check for completed dma transfers, update stats and free skbs */
+static void smsc9420_complete_tx(struct net_device *dev)
+{
+	struct smsc9420_pdata *pd = netdev_priv(dev);
+
+	while (pd->tx_ring_tail != pd->tx_ring_head) {
+		int index = pd->tx_ring_tail;
+		u32 status, length;
+
+		rmb();
+		status = pd->tx_ring[index].status;
+		length = pd->tx_ring[index].length;
+
+		/* Check if DMA still owns this descriptor */
+		if (unlikely(TDES0_OWN_ & status))
+			break;
+
+		smsc9420_tx_update_stats(dev, status, length);
+
+		BUG_ON(!pd->tx_buffers[index].skb);
+		BUG_ON(!pd->tx_buffers[index].mapping);
+
+		pci_unmap_single(pd->pdev, pd->tx_buffers[index].mapping,
+			pd->tx_buffers[index].skb->len, PCI_DMA_TODEVICE);
+		pd->tx_buffers[index].mapping = 0;
+
+		dev_kfree_skb_any(pd->tx_buffers[index].skb);
+		pd->tx_buffers[index].skb = NULL;
+
+		pd->tx_ring[index].buffer1 = 0;
+		wmb();
+
+		pd->tx_ring_tail = (pd->tx_ring_tail + 1) % TX_RING_SIZE;
+	}
+}
+
+static netdev_tx_t smsc9420_hard_start_xmit(struct sk_buff *skb,
+					    struct net_device *dev)
+{
+	struct smsc9420_pdata *pd = netdev_priv(dev);
+	dma_addr_t mapping;
+	int index = pd->tx_ring_head;
+	u32 tmp_desc1;
+	bool about_to_take_last_desc =
+		(((pd->tx_ring_head + 2) % TX_RING_SIZE) == pd->tx_ring_tail);
+
+	smsc9420_complete_tx(dev);
+
+	rmb();
+	BUG_ON(pd->tx_ring[index].status & TDES0_OWN_);
+	BUG_ON(pd->tx_buffers[index].skb);
+	BUG_ON(pd->tx_buffers[index].mapping);
+
+	mapping = pci_map_single(pd->pdev, skb->data,
+				 skb->len, PCI_DMA_TODEVICE);
+	if (pci_dma_mapping_error(pd->pdev, mapping)) {
+		smsc_warn(TX_ERR, "pci_map_single failed, dropping packet");
+		return NETDEV_TX_BUSY;
+	}
+
+	pd->tx_buffers[index].skb = skb;
+	pd->tx_buffers[index].mapping = mapping;
+
+	tmp_desc1 = (TDES1_LS_ | ((u32)skb->len & 0x7FF));
+	if (unlikely(about_to_take_last_desc)) {
+		tmp_desc1 |= TDES1_IC_;
+		netif_stop_queue(pd->dev);
+	}
+
+	/* check if we are at the last descriptor and need to set EOR */
+	if (unlikely(index == (TX_RING_SIZE - 1)))
+		tmp_desc1 |= TDES1_TER_;
+
+	pd->tx_ring[index].buffer1 = mapping;
+	pd->tx_ring[index].length = tmp_desc1;
+	wmb();
+
+	/* increment head */
+	pd->tx_ring_head = (pd->tx_ring_head + 1) % TX_RING_SIZE;
+
+	/* assign ownership to DMAC */
+	pd->tx_ring[index].status = TDES0_OWN_;
+	wmb();
+
+	skb_tx_timestamp(skb);
+
+	/* kick the DMA */
+	smsc9420_reg_write(pd, TX_POLL_DEMAND, 1);
+	smsc9420_pci_flush_write(pd);
+
+	return NETDEV_TX_OK;
+}
+
+static struct net_device_stats *smsc9420_get_stats(struct net_device *dev)
+{
+	struct smsc9420_pdata *pd = netdev_priv(dev);
+	u32 counter = smsc9420_reg_read(pd, MISS_FRAME_CNTR);
+	dev->stats.rx_dropped +=
+	    (counter & 0x0000FFFF) + ((counter >> 17) & 0x000003FF);
+	return &dev->stats;
+}
+
+static void smsc9420_set_multicast_list(struct net_device *dev)
+{
+	struct smsc9420_pdata *pd = netdev_priv(dev);
+	u32 mac_cr = smsc9420_reg_read(pd, MAC_CR);
+
+	if (dev->flags & IFF_PROMISC) {
+		smsc_dbg(HW, "Promiscuous Mode Enabled");
+		mac_cr |= MAC_CR_PRMS_;
+		mac_cr &= (~MAC_CR_MCPAS_);
+		mac_cr &= (~MAC_CR_HPFILT_);
+	} else if (dev->flags & IFF_ALLMULTI) {
+		smsc_dbg(HW, "Receive all Multicast Enabled");
+		mac_cr &= (~MAC_CR_PRMS_);
+		mac_cr |= MAC_CR_MCPAS_;
+		mac_cr &= (~MAC_CR_HPFILT_);
+	} else if (!netdev_mc_empty(dev)) {
+		struct netdev_hw_addr *ha;
+		u32 hash_lo = 0, hash_hi = 0;
+
+		smsc_dbg(HW, "Multicast filter enabled");
+		netdev_for_each_mc_addr(ha, dev) {
+			u32 bit_num = smsc9420_hash(ha->addr);
+			u32 mask = 1 << (bit_num & 0x1F);
+
+			if (bit_num & 0x20)
+				hash_hi |= mask;
+			else
+				hash_lo |= mask;
+
+		}
+		smsc9420_reg_write(pd, HASHH, hash_hi);
+		smsc9420_reg_write(pd, HASHL, hash_lo);
+
+		mac_cr &= (~MAC_CR_PRMS_);
+		mac_cr &= (~MAC_CR_MCPAS_);
+		mac_cr |= MAC_CR_HPFILT_;
+	} else {
+		smsc_dbg(HW, "Receive own packets only.");
+		smsc9420_reg_write(pd, HASHH, 0);
+		smsc9420_reg_write(pd, HASHL, 0);
+
+		mac_cr &= (~MAC_CR_PRMS_);
+		mac_cr &= (~MAC_CR_MCPAS_);
+		mac_cr &= (~MAC_CR_HPFILT_);
+	}
+
+	smsc9420_reg_write(pd, MAC_CR, mac_cr);
+	smsc9420_pci_flush_write(pd);
+}
+
+static void smsc9420_phy_update_flowcontrol(struct smsc9420_pdata *pd)
+{
+	struct phy_device *phy_dev = pd->phy_dev;
+	u32 flow;
+
+	if (phy_dev->duplex == DUPLEX_FULL) {
+		u16 lcladv = phy_read(phy_dev, MII_ADVERTISE);
+		u16 rmtadv = phy_read(phy_dev, MII_LPA);
+		u8 cap = mii_resolve_flowctrl_fdx(lcladv, rmtadv);
+
+		if (cap & FLOW_CTRL_RX)
+			flow = 0xFFFF0002;
+		else
+			flow = 0;
+
+		smsc_info(LINK, "rx pause %s, tx pause %s",
+			(cap & FLOW_CTRL_RX ? "enabled" : "disabled"),
+			(cap & FLOW_CTRL_TX ? "enabled" : "disabled"));
+	} else {
+		smsc_info(LINK, "half duplex");
+		flow = 0;
+	}
+
+	smsc9420_reg_write(pd, FLOW, flow);
+}
+
+/* Update link mode if anything has changed.  Called periodically when the
+ * PHY is in polling mode, even if nothing has changed. */
+static void smsc9420_phy_adjust_link(struct net_device *dev)
+{
+	struct smsc9420_pdata *pd = netdev_priv(dev);
+	struct phy_device *phy_dev = pd->phy_dev;
+	int carrier;
+
+	if (phy_dev->duplex != pd->last_duplex) {
+		u32 mac_cr = smsc9420_reg_read(pd, MAC_CR);
+		if (phy_dev->duplex) {
+			smsc_dbg(LINK, "full duplex mode");
+			mac_cr |= MAC_CR_FDPX_;
+		} else {
+			smsc_dbg(LINK, "half duplex mode");
+			mac_cr &= ~MAC_CR_FDPX_;
+		}
+		smsc9420_reg_write(pd, MAC_CR, mac_cr);
+
+		smsc9420_phy_update_flowcontrol(pd);
+		pd->last_duplex = phy_dev->duplex;
+	}
+
+	carrier = netif_carrier_ok(dev);
+	if (carrier != pd->last_carrier) {
+		if (carrier)
+			smsc_dbg(LINK, "carrier OK");
+		else
+			smsc_dbg(LINK, "no carrier");
+		pd->last_carrier = carrier;
+	}
+}
+
+static int smsc9420_mii_probe(struct net_device *dev)
+{
+	struct smsc9420_pdata *pd = netdev_priv(dev);
+	struct phy_device *phydev = NULL;
+
+	BUG_ON(pd->phy_dev);
+
+	/* Device only supports internal PHY at address 1 */
+	if (!pd->mii_bus->phy_map[1]) {
+		pr_err("%s: no PHY found at address 1\n", dev->name);
+		return -ENODEV;
+	}
+
+	phydev = pd->mii_bus->phy_map[1];
+	smsc_info(PROBE, "PHY addr %d, phy_id 0x%08X", phydev->addr,
+		phydev->phy_id);
+
+	phydev = phy_connect(dev, dev_name(&phydev->dev),
+		smsc9420_phy_adjust_link, 0, PHY_INTERFACE_MODE_MII);
+
+	if (IS_ERR(phydev)) {
+		pr_err("%s: Could not attach to PHY\n", dev->name);
+		return PTR_ERR(phydev);
+	}
+
+	pr_info("%s: attached PHY driver [%s] (mii_bus:phy_addr=%s, irq=%d)\n",
+		dev->name, phydev->drv->name, dev_name(&phydev->dev), phydev->irq);
+
+	/* mask with MAC supported features */
+	phydev->supported &= (PHY_BASIC_FEATURES | SUPPORTED_Pause |
+			      SUPPORTED_Asym_Pause);
+	phydev->advertising = phydev->supported;
+
+	pd->phy_dev = phydev;
+	pd->last_duplex = -1;
+	pd->last_carrier = -1;
+
+	return 0;
+}
+
+static int smsc9420_mii_init(struct net_device *dev)
+{
+	struct smsc9420_pdata *pd = netdev_priv(dev);
+	int err = -ENXIO, i;
+
+	pd->mii_bus = mdiobus_alloc();
+	if (!pd->mii_bus) {
+		err = -ENOMEM;
+		goto err_out_1;
+	}
+	pd->mii_bus->name = DRV_MDIONAME;
+	snprintf(pd->mii_bus->id, MII_BUS_ID_SIZE, "%x",
+		(pd->pdev->bus->number << 8) | pd->pdev->devfn);
+	pd->mii_bus->priv = pd;
+	pd->mii_bus->read = smsc9420_mii_read;
+	pd->mii_bus->write = smsc9420_mii_write;
+	pd->mii_bus->irq = pd->phy_irq;
+	for (i = 0; i < PHY_MAX_ADDR; ++i)
+		pd->mii_bus->irq[i] = PHY_POLL;
+
+	/* Mask all PHYs except ID 1 (internal) */
+	pd->mii_bus->phy_mask = ~(1 << 1);
+
+	if (mdiobus_register(pd->mii_bus)) {
+		smsc_warn(PROBE, "Error registering mii bus");
+		goto err_out_free_bus_2;
+	}
+
+	if (smsc9420_mii_probe(dev) < 0) {
+		smsc_warn(PROBE, "Error probing mii bus");
+		goto err_out_unregister_bus_3;
+	}
+
+	return 0;
+
+err_out_unregister_bus_3:
+	mdiobus_unregister(pd->mii_bus);
+err_out_free_bus_2:
+	mdiobus_free(pd->mii_bus);
+err_out_1:
+	return err;
+}
+
+static int smsc9420_alloc_tx_ring(struct smsc9420_pdata *pd)
+{
+	int i;
+
+	BUG_ON(!pd->tx_ring);
+
+	pd->tx_buffers = kmalloc((sizeof(struct smsc9420_ring_info) *
+		TX_RING_SIZE), GFP_KERNEL);
+	if (!pd->tx_buffers) {
+		smsc_warn(IFUP, "Failed to allocated tx_buffers");
+		return -ENOMEM;
+	}
+
+	/* Initialize the TX Ring */
+	for (i = 0; i < TX_RING_SIZE; i++) {
+		pd->tx_buffers[i].skb = NULL;
+		pd->tx_buffers[i].mapping = 0;
+		pd->tx_ring[i].status = 0;
+		pd->tx_ring[i].length = 0;
+		pd->tx_ring[i].buffer1 = 0;
+		pd->tx_ring[i].buffer2 = 0;
+	}
+	pd->tx_ring[TX_RING_SIZE - 1].length = TDES1_TER_;
+	wmb();
+
+	pd->tx_ring_head = 0;
+	pd->tx_ring_tail = 0;
+
+	smsc9420_reg_write(pd, TX_BASE_ADDR, pd->tx_dma_addr);
+	smsc9420_pci_flush_write(pd);
+
+	return 0;
+}
+
+static int smsc9420_alloc_rx_ring(struct smsc9420_pdata *pd)
+{
+	int i;
+
+	BUG_ON(!pd->rx_ring);
+
+	pd->rx_buffers = kmalloc((sizeof(struct smsc9420_ring_info) *
+		RX_RING_SIZE), GFP_KERNEL);
+	if (pd->rx_buffers == NULL) {
+		smsc_warn(IFUP, "Failed to allocated rx_buffers");
+		goto out;
+	}
+
+	/* initialize the rx ring */
+	for (i = 0; i < RX_RING_SIZE; i++) {
+		pd->rx_ring[i].status = 0;
+		pd->rx_ring[i].length = PKT_BUF_SZ;
+		pd->rx_ring[i].buffer2 = 0;
+		pd->rx_buffers[i].skb = NULL;
+		pd->rx_buffers[i].mapping = 0;
+	}
+	pd->rx_ring[RX_RING_SIZE - 1].length = (PKT_BUF_SZ | RDES1_RER_);
+
+	/* now allocate the entire ring of skbs */
+	for (i = 0; i < RX_RING_SIZE; i++) {
+		if (smsc9420_alloc_rx_buffer(pd, i)) {
+			smsc_warn(IFUP, "failed to allocate rx skb %d", i);
+			goto out_free_rx_skbs;
+		}
+	}
+
+	pd->rx_ring_head = 0;
+	pd->rx_ring_tail = 0;
+
+	smsc9420_reg_write(pd, VLAN1, ETH_P_8021Q);
+	smsc_dbg(IFUP, "VLAN1 = 0x%08x", smsc9420_reg_read(pd, VLAN1));
+
+	if (pd->rx_csum) {
+		/* Enable RX COE */
+		u32 coe = smsc9420_reg_read(pd, COE_CR) | RX_COE_EN;
+		smsc9420_reg_write(pd, COE_CR, coe);
+		smsc_dbg(IFUP, "COE_CR = 0x%08x", coe);
+	}
+
+	smsc9420_reg_write(pd, RX_BASE_ADDR, pd->rx_dma_addr);
+	smsc9420_pci_flush_write(pd);
+
+	return 0;
+
+out_free_rx_skbs:
+	smsc9420_free_rx_ring(pd);
+out:
+	return -ENOMEM;
+}
+
+static int smsc9420_open(struct net_device *dev)
+{
+	struct smsc9420_pdata *pd;
+	u32 bus_mode, mac_cr, dmac_control, int_cfg, dma_intr_ena, int_ctl;
+	unsigned long flags;
+	int result = 0, timeout;
+
+	BUG_ON(!dev);
+	pd = netdev_priv(dev);
+	BUG_ON(!pd);
+
+	if (!is_valid_ether_addr(dev->dev_addr)) {
+		smsc_warn(IFUP, "dev_addr is not a valid MAC address");
+		result = -EADDRNOTAVAIL;
+		goto out_0;
+	}
+
+	netif_carrier_off(dev);
+
+	/* disable, mask and acknowledge all interrupts */
+	spin_lock_irqsave(&pd->int_lock, flags);
+	int_cfg = smsc9420_reg_read(pd, INT_CFG) & (~INT_CFG_IRQ_EN_);
+	smsc9420_reg_write(pd, INT_CFG, int_cfg);
+	smsc9420_reg_write(pd, INT_CTL, 0);
+	spin_unlock_irqrestore(&pd->int_lock, flags);
+	smsc9420_reg_write(pd, DMAC_INTR_ENA, 0);
+	smsc9420_reg_write(pd, INT_STAT, 0xFFFFFFFF);
+	smsc9420_pci_flush_write(pd);
+
+	if (request_irq(dev->irq, smsc9420_isr, IRQF_SHARED | IRQF_DISABLED,
+			DRV_NAME, pd)) {
+		smsc_warn(IFUP, "Unable to use IRQ = %d", dev->irq);
+		result = -ENODEV;
+		goto out_0;
+	}
+
+	smsc9420_dmac_soft_reset(pd);
+
+	/* make sure MAC_CR is sane */
+	smsc9420_reg_write(pd, MAC_CR, 0);
+
+	smsc9420_set_mac_address(dev);
+
+	/* Configure GPIO pins to drive LEDs */
+	smsc9420_reg_write(pd, GPIO_CFG,
+		(GPIO_CFG_LED_3_ | GPIO_CFG_LED_2_ | GPIO_CFG_LED_1_));
+
+	bus_mode = BUS_MODE_DMA_BURST_LENGTH_16;
+
+#ifdef __BIG_ENDIAN
+	bus_mode |= BUS_MODE_DBO_;
+#endif
+
+	smsc9420_reg_write(pd, BUS_MODE, bus_mode);
+
+	smsc9420_pci_flush_write(pd);
+
+	/* set bus master bridge arbitration priority for Rx and TX DMA */
+	smsc9420_reg_write(pd, BUS_CFG, BUS_CFG_RXTXWEIGHT_4_1);
+
+	smsc9420_reg_write(pd, DMAC_CONTROL,
+		(DMAC_CONTROL_SF_ | DMAC_CONTROL_OSF_));
+
+	smsc9420_pci_flush_write(pd);
+
+	/* test the IRQ connection to the ISR */
+	smsc_dbg(IFUP, "Testing ISR using IRQ %d", dev->irq);
+	pd->software_irq_signal = false;
+
+	spin_lock_irqsave(&pd->int_lock, flags);
+	/* configure interrupt deassertion timer and enable interrupts */
+	int_cfg = smsc9420_reg_read(pd, INT_CFG) | INT_CFG_IRQ_EN_;
+	int_cfg &= ~(INT_CFG_INT_DEAS_MASK);
+	int_cfg |= (INT_DEAS_TIME & INT_CFG_INT_DEAS_MASK);
+	smsc9420_reg_write(pd, INT_CFG, int_cfg);
+
+	/* unmask software interrupt */
+	int_ctl = smsc9420_reg_read(pd, INT_CTL) | INT_CTL_SW_INT_EN_;
+	smsc9420_reg_write(pd, INT_CTL, int_ctl);
+	spin_unlock_irqrestore(&pd->int_lock, flags);
+	smsc9420_pci_flush_write(pd);
+
+	timeout = 1000;
+	while (timeout--) {
+		if (pd->software_irq_signal)
+			break;
+		msleep(1);
+	}
+
+	/* disable interrupts */
+	spin_lock_irqsave(&pd->int_lock, flags);
+	int_cfg = smsc9420_reg_read(pd, INT_CFG) & (~INT_CFG_IRQ_EN_);
+	smsc9420_reg_write(pd, INT_CFG, int_cfg);
+	spin_unlock_irqrestore(&pd->int_lock, flags);
+
+	if (!pd->software_irq_signal) {
+		smsc_warn(IFUP, "ISR failed signaling test");
+		result = -ENODEV;
+		goto out_free_irq_1;
+	}
+
+	smsc_dbg(IFUP, "ISR passed test using IRQ %d", dev->irq);
+
+	result = smsc9420_alloc_tx_ring(pd);
+	if (result) {
+		smsc_warn(IFUP, "Failed to Initialize tx dma ring");
+		result = -ENOMEM;
+		goto out_free_irq_1;
+	}
+
+	result = smsc9420_alloc_rx_ring(pd);
+	if (result) {
+		smsc_warn(IFUP, "Failed to Initialize rx dma ring");
+		result = -ENOMEM;
+		goto out_free_tx_ring_2;
+	}
+
+	result = smsc9420_mii_init(dev);
+	if (result) {
+		smsc_warn(IFUP, "Failed to initialize Phy");
+		result = -ENODEV;
+		goto out_free_rx_ring_3;
+	}
+
+	/* Bring the PHY up */
+	phy_start(pd->phy_dev);
+
+	napi_enable(&pd->napi);
+
+	/* start tx and rx */
+	mac_cr = smsc9420_reg_read(pd, MAC_CR) | MAC_CR_TXEN_ | MAC_CR_RXEN_;
+	smsc9420_reg_write(pd, MAC_CR, mac_cr);
+
+	dmac_control = smsc9420_reg_read(pd, DMAC_CONTROL);
+	dmac_control |= DMAC_CONTROL_ST_ | DMAC_CONTROL_SR_;
+	smsc9420_reg_write(pd, DMAC_CONTROL, dmac_control);
+	smsc9420_pci_flush_write(pd);
+
+	dma_intr_ena = smsc9420_reg_read(pd, DMAC_INTR_ENA);
+	dma_intr_ena |=
+		(DMAC_INTR_ENA_TX_ | DMAC_INTR_ENA_RX_ | DMAC_INTR_ENA_NIS_);
+	smsc9420_reg_write(pd, DMAC_INTR_ENA, dma_intr_ena);
+	smsc9420_pci_flush_write(pd);
+
+	netif_wake_queue(dev);
+
+	smsc9420_reg_write(pd, RX_POLL_DEMAND, 1);
+
+	/* enable interrupts */
+	spin_lock_irqsave(&pd->int_lock, flags);
+	int_cfg = smsc9420_reg_read(pd, INT_CFG) | INT_CFG_IRQ_EN_;
+	smsc9420_reg_write(pd, INT_CFG, int_cfg);
+	spin_unlock_irqrestore(&pd->int_lock, flags);
+
+	return 0;
+
+out_free_rx_ring_3:
+	smsc9420_free_rx_ring(pd);
+out_free_tx_ring_2:
+	smsc9420_free_tx_ring(pd);
+out_free_irq_1:
+	free_irq(dev->irq, pd);
+out_0:
+	return result;
+}
+
+#ifdef CONFIG_PM
+
+static int smsc9420_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+	struct net_device *dev = pci_get_drvdata(pdev);
+	struct smsc9420_pdata *pd = netdev_priv(dev);
+	u32 int_cfg;
+	ulong flags;
+
+	/* disable interrupts */
+	spin_lock_irqsave(&pd->int_lock, flags);
+	int_cfg = smsc9420_reg_read(pd, INT_CFG) & (~INT_CFG_IRQ_EN_);
+	smsc9420_reg_write(pd, INT_CFG, int_cfg);
+	spin_unlock_irqrestore(&pd->int_lock, flags);
+
+	if (netif_running(dev)) {
+		netif_tx_disable(dev);
+		smsc9420_stop_tx(pd);
+		smsc9420_free_tx_ring(pd);
+
+		napi_disable(&pd->napi);
+		smsc9420_stop_rx(pd);
+		smsc9420_free_rx_ring(pd);
+
+		free_irq(dev->irq, pd);
+
+		netif_device_detach(dev);
+	}
+
+	pci_save_state(pdev);
+	pci_enable_wake(pdev, pci_choose_state(pdev, state), 0);
+	pci_disable_device(pdev);
+	pci_set_power_state(pdev, pci_choose_state(pdev, state));
+
+	return 0;
+}
+
+static int smsc9420_resume(struct pci_dev *pdev)
+{
+	struct net_device *dev = pci_get_drvdata(pdev);
+	struct smsc9420_pdata *pd = netdev_priv(dev);
+	int err;
+
+	pci_set_power_state(pdev, PCI_D0);
+	pci_restore_state(pdev);
+
+	err = pci_enable_device(pdev);
+	if (err)
+		return err;
+
+	pci_set_master(pdev);
+
+	err = pci_enable_wake(pdev, 0, 0);
+	if (err)
+		smsc_warn(IFUP, "pci_enable_wake failed: %d", err);
+
+	if (netif_running(dev)) {
+		err = smsc9420_open(dev);
+		netif_device_attach(dev);
+	}
+	return err;
+}
+
+#endif /* CONFIG_PM */
+
+static const struct net_device_ops smsc9420_netdev_ops = {
+	.ndo_open		= smsc9420_open,
+	.ndo_stop		= smsc9420_stop,
+	.ndo_start_xmit		= smsc9420_hard_start_xmit,
+	.ndo_get_stats		= smsc9420_get_stats,
+	.ndo_set_multicast_list	= smsc9420_set_multicast_list,
+	.ndo_do_ioctl		= smsc9420_do_ioctl,
+	.ndo_validate_addr	= eth_validate_addr,
+	.ndo_set_mac_address 	= eth_mac_addr,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	.ndo_poll_controller	= smsc9420_poll_controller,
+#endif /* CONFIG_NET_POLL_CONTROLLER */
+};
+
+static int __devinit
+smsc9420_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+	struct net_device *dev;
+	struct smsc9420_pdata *pd;
+	void __iomem *virt_addr;
+	int result = 0;
+	u32 id_rev;
+
+	printk(KERN_INFO DRV_DESCRIPTION " version " DRV_VERSION "\n");
+
+	/* First do the PCI initialisation */
+	result = pci_enable_device(pdev);
+	if (unlikely(result)) {
+		printk(KERN_ERR "Cannot enable smsc9420\n");
+		goto out_0;
+	}
+
+	pci_set_master(pdev);
+
+	dev = alloc_etherdev(sizeof(*pd));
+	if (!dev) {
+		printk(KERN_ERR "ether device alloc failed\n");
+		goto out_disable_pci_device_1;
+	}
+
+	SET_NETDEV_DEV(dev, &pdev->dev);
+
+	if (!(pci_resource_flags(pdev, SMSC_BAR) & IORESOURCE_MEM)) {
+		printk(KERN_ERR "Cannot find PCI device base address\n");
+		goto out_free_netdev_2;
+	}
+
+	if ((pci_request_regions(pdev, DRV_NAME))) {
+		printk(KERN_ERR "Cannot obtain PCI resources, aborting.\n");
+		goto out_free_netdev_2;
+	}
+
+	if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) {
+		printk(KERN_ERR "No usable DMA configuration, aborting.\n");
+		goto out_free_regions_3;
+	}
+
+	virt_addr = ioremap(pci_resource_start(pdev, SMSC_BAR),
+		pci_resource_len(pdev, SMSC_BAR));
+	if (!virt_addr) {
+		printk(KERN_ERR "Cannot map device registers, aborting.\n");
+		goto out_free_regions_3;
+	}
+
+	/* registers are double mapped with 0 offset for LE and 0x200 for BE */
+	virt_addr += LAN9420_CPSR_ENDIAN_OFFSET;
+
+	dev->base_addr = (ulong)virt_addr;
+
+	pd = netdev_priv(dev);
+
+	/* pci descriptors are created in the PCI consistent area */
+	pd->rx_ring = pci_alloc_consistent(pdev,
+		sizeof(struct smsc9420_dma_desc) * RX_RING_SIZE +
+		sizeof(struct smsc9420_dma_desc) * TX_RING_SIZE,
+		&pd->rx_dma_addr);
+
+	if (!pd->rx_ring)
+		goto out_free_io_4;
+
+	/* descriptors are aligned due to the nature of pci_alloc_consistent */
+	pd->tx_ring = (struct smsc9420_dma_desc *)
+	    (pd->rx_ring + RX_RING_SIZE);
+	pd->tx_dma_addr = pd->rx_dma_addr +
+	    sizeof(struct smsc9420_dma_desc) * RX_RING_SIZE;
+
+	pd->pdev = pdev;
+	pd->dev = dev;
+	pd->base_addr = virt_addr;
+	pd->msg_enable = smsc_debug;
+	pd->rx_csum = true;
+
+	smsc_dbg(PROBE, "lan_base=0x%08lx", (ulong)virt_addr);
+
+	id_rev = smsc9420_reg_read(pd, ID_REV);
+	switch (id_rev & 0xFFFF0000) {
+	case 0x94200000:
+		smsc_info(PROBE, "LAN9420 identified, ID_REV=0x%08X", id_rev);
+		break;
+	default:
+		smsc_warn(PROBE, "LAN9420 NOT identified");
+		smsc_warn(PROBE, "ID_REV=0x%08X", id_rev);
+		goto out_free_dmadesc_5;
+	}
+
+	smsc9420_dmac_soft_reset(pd);
+	smsc9420_eeprom_reload(pd);
+	smsc9420_check_mac_address(dev);
+
+	dev->netdev_ops = &smsc9420_netdev_ops;
+	dev->ethtool_ops = &smsc9420_ethtool_ops;
+	dev->irq = pdev->irq;
+
+	netif_napi_add(dev, &pd->napi, smsc9420_rx_poll, NAPI_WEIGHT);
+
+	result = register_netdev(dev);
+	if (result) {
+		smsc_warn(PROBE, "error %i registering device", result);
+		goto out_free_dmadesc_5;
+	}
+
+	pci_set_drvdata(pdev, dev);
+
+	spin_lock_init(&pd->int_lock);
+	spin_lock_init(&pd->phy_lock);
+
+	dev_info(&dev->dev, "MAC Address: %pM\n", dev->dev_addr);
+
+	return 0;
+
+out_free_dmadesc_5:
+	pci_free_consistent(pdev, sizeof(struct smsc9420_dma_desc) *
+		(RX_RING_SIZE + TX_RING_SIZE), pd->rx_ring, pd->rx_dma_addr);
+out_free_io_4:
+	iounmap(virt_addr - LAN9420_CPSR_ENDIAN_OFFSET);
+out_free_regions_3:
+	pci_release_regions(pdev);
+out_free_netdev_2:
+	free_netdev(dev);
+out_disable_pci_device_1:
+	pci_disable_device(pdev);
+out_0:
+	return -ENODEV;
+}
+
+static void __devexit smsc9420_remove(struct pci_dev *pdev)
+{
+	struct net_device *dev;
+	struct smsc9420_pdata *pd;
+
+	dev = pci_get_drvdata(pdev);
+	if (!dev)
+		return;
+
+	pci_set_drvdata(pdev, NULL);
+
+	pd = netdev_priv(dev);
+	unregister_netdev(dev);
+
+	/* tx_buffers and rx_buffers are freed in stop */
+	BUG_ON(pd->tx_buffers);
+	BUG_ON(pd->rx_buffers);
+
+	BUG_ON(!pd->tx_ring);
+	BUG_ON(!pd->rx_ring);
+
+	pci_free_consistent(pdev, sizeof(struct smsc9420_dma_desc) *
+		(RX_RING_SIZE + TX_RING_SIZE), pd->rx_ring, pd->rx_dma_addr);
+
+	iounmap(pd->base_addr - LAN9420_CPSR_ENDIAN_OFFSET);
+	pci_release_regions(pdev);
+	free_netdev(dev);
+	pci_disable_device(pdev);
+}
+
+static struct pci_driver smsc9420_driver = {
+	.name = DRV_NAME,
+	.id_table = smsc9420_id_table,
+	.probe = smsc9420_probe,
+	.remove = __devexit_p(smsc9420_remove),
+#ifdef CONFIG_PM
+	.suspend = smsc9420_suspend,
+	.resume = smsc9420_resume,
+#endif /* CONFIG_PM */
+};
+
+static int __init smsc9420_init_module(void)
+{
+	smsc_debug = netif_msg_init(debug, SMSC_MSG_DEFAULT);
+
+	return pci_register_driver(&smsc9420_driver);
+}
+
+static void __exit smsc9420_exit_module(void)
+{
+	pci_unregister_driver(&smsc9420_driver);
+}
+
+module_init(smsc9420_init_module);
+module_exit(smsc9420_exit_module);
diff --git a/drivers/net/ethernet/smsc/smsc9420.h b/drivers/net/ethernet/smsc/smsc9420.h
new file mode 100644
index 0000000..e441402
--- /dev/null
+++ b/drivers/net/ethernet/smsc/smsc9420.h
@@ -0,0 +1,276 @@
+ /***************************************************************************
+ *
+ * Copyright (C) 2007,2008  SMSC
+ *
+ * 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.
+ *
+ ***************************************************************************
+ */
+
+#ifndef _SMSC9420_H
+#define _SMSC9420_H
+
+#define TX_RING_SIZE			(32)
+#define RX_RING_SIZE			(128)
+
+/* interrupt deassertion in multiples of 10us */
+#define INT_DEAS_TIME			(50)
+
+#define NAPI_WEIGHT			(64)
+#define SMSC_BAR			(3)
+
+#ifdef __BIG_ENDIAN
+/* Register set is duplicated for BE at an offset of 0x200 */
+#define LAN9420_CPSR_ENDIAN_OFFSET	(0x200)
+#else
+#define LAN9420_CPSR_ENDIAN_OFFSET	(0)
+#endif
+
+#define PCI_VENDOR_ID_9420		(0x1055)
+#define PCI_DEVICE_ID_9420		(0xE420)
+
+#define LAN_REGISTER_EXTENT		(0x400)
+
+#define SMSC9420_EEPROM_SIZE		((u32)11)
+#define SMSC9420_EEPROM_MAGIC		(0x9420)
+
+#define PKT_BUF_SZ			(VLAN_ETH_FRAME_LEN + NET_IP_ALIGN + 4)
+
+/***********************************************/
+/* DMA Controller Control and Status Registers */
+/***********************************************/
+#define BUS_MODE			(0x00)
+#define BUS_MODE_SWR_			(BIT(0))
+#define BUS_MODE_DMA_BURST_LENGTH_1	(BIT(8))
+#define BUS_MODE_DMA_BURST_LENGTH_2	(BIT(9))
+#define BUS_MODE_DMA_BURST_LENGTH_4	(BIT(10))
+#define BUS_MODE_DMA_BURST_LENGTH_8	(BIT(11))
+#define BUS_MODE_DMA_BURST_LENGTH_16	(BIT(12))
+#define BUS_MODE_DMA_BURST_LENGTH_32	(BIT(13))
+#define BUS_MODE_DBO_			(BIT(20))
+
+#define TX_POLL_DEMAND			(0x04)
+
+#define RX_POLL_DEMAND			(0x08)
+
+#define RX_BASE_ADDR			(0x0C)
+
+#define TX_BASE_ADDR			(0x10)
+
+#define DMAC_STATUS			(0x14)
+#define DMAC_STS_TS_			(7 << 20)
+#define DMAC_STS_RS_ 			(7 << 17)
+#define DMAC_STS_NIS_			(BIT(16))
+#define DMAC_STS_AIS_			(BIT(15))
+#define DMAC_STS_RWT_			(BIT(9))
+#define DMAC_STS_RXPS_			(BIT(8))
+#define DMAC_STS_RXBU_			(BIT(7))
+#define DMAC_STS_RX_			(BIT(6))
+#define DMAC_STS_TXUNF_			(BIT(5))
+#define DMAC_STS_TXBU_			(BIT(2))
+#define DMAC_STS_TXPS_			(BIT(1))
+#define DMAC_STS_TX_			(BIT(0))
+
+#define DMAC_CONTROL			(0x18)
+#define DMAC_CONTROL_TTM_		(BIT(22))
+#define DMAC_CONTROL_SF_		(BIT(21))
+#define DMAC_CONTROL_ST_		(BIT(13))
+#define DMAC_CONTROL_OSF_		(BIT(2))
+#define DMAC_CONTROL_SR_		(BIT(1))
+
+#define DMAC_INTR_ENA			(0x1C)
+#define DMAC_INTR_ENA_NIS_		(BIT(16))
+#define DMAC_INTR_ENA_AIS_		(BIT(15))
+#define DMAC_INTR_ENA_RWT_		(BIT(9))
+#define DMAC_INTR_ENA_RXPS_		(BIT(8))
+#define DMAC_INTR_ENA_RXBU_		(BIT(7))
+#define DMAC_INTR_ENA_RX_		(BIT(6))
+#define DMAC_INTR_ENA_TXBU_		(BIT(2))
+#define DMAC_INTR_ENA_TXPS_		(BIT(1))
+#define DMAC_INTR_ENA_TX_		(BIT(0))
+
+#define MISS_FRAME_CNTR			(0x20)
+
+#define TX_BUFF_ADDR			(0x50)
+
+#define RX_BUFF_ADDR			(0x54)
+
+/* Transmit Descriptor Bit Defs */
+#define TDES0_OWN_			(0x80000000)
+#define TDES0_ERROR_SUMMARY_		(0x00008000)
+#define TDES0_LOSS_OF_CARRIER_		(0x00000800)
+#define TDES0_NO_CARRIER_		(0x00000400)
+#define TDES0_LATE_COLLISION_		(0x00000200)
+#define TDES0_EXCESSIVE_COLLISIONS_	(0x00000100)
+#define TDES0_HEARTBEAT_FAIL_		(0x00000080)
+#define TDES0_COLLISION_COUNT_MASK_	(0x00000078)
+#define TDES0_COLLISION_COUNT_SHFT_	(3)
+#define TDES0_EXCESSIVE_DEFERRAL_	(0x00000004)
+#define TDES0_DEFERRED_			(0x00000001)
+
+#define TDES1_IC_			0x80000000
+#define TDES1_LS_			0x40000000
+#define TDES1_FS_			0x20000000
+#define TDES1_TXCSEN_			0x08000000
+#define TDES1_TER_			(BIT(25))
+#define TDES1_TCH_			0x01000000
+
+/* Receive Descriptor 0 Bit Defs */
+#define RDES0_OWN_			(0x80000000)
+#define RDES0_FRAME_LENGTH_MASK_	(0x07FF0000)
+#define RDES0_FRAME_LENGTH_SHFT_	(16)
+#define RDES0_ERROR_SUMMARY_		(0x00008000)
+#define RDES0_DESCRIPTOR_ERROR_		(0x00004000)
+#define RDES0_LENGTH_ERROR_		(0x00001000)
+#define RDES0_RUNT_FRAME_		(0x00000800)
+#define RDES0_MULTICAST_FRAME_		(0x00000400)
+#define RDES0_FIRST_DESCRIPTOR_		(0x00000200)
+#define RDES0_LAST_DESCRIPTOR_		(0x00000100)
+#define RDES0_FRAME_TOO_LONG_		(0x00000080)
+#define RDES0_COLLISION_SEEN_		(0x00000040)
+#define RDES0_FRAME_TYPE_		(0x00000020)
+#define RDES0_WATCHDOG_TIMEOUT_		(0x00000010)
+#define RDES0_MII_ERROR_		(0x00000008)
+#define RDES0_DRIBBLING_BIT_		(0x00000004)
+#define RDES0_CRC_ERROR_		(0x00000002)
+
+/* Receive Descriptor 1 Bit Defs */
+#define RDES1_RER_			(0x02000000)
+
+/***********************************************/
+/*       MAC Control and Status Registers      */
+/***********************************************/
+#define MAC_CR				(0x80)
+#define MAC_CR_RXALL_			(0x80000000)
+#define MAC_CR_DIS_RXOWN_		(0x00800000)
+#define MAC_CR_LOOPBK_			(0x00200000)
+#define MAC_CR_FDPX_			(0x00100000)
+#define MAC_CR_MCPAS_			(0x00080000)
+#define MAC_CR_PRMS_			(0x00040000)
+#define MAC_CR_INVFILT_			(0x00020000)
+#define MAC_CR_PASSBAD_			(0x00010000)
+#define MAC_CR_HFILT_			(0x00008000)
+#define MAC_CR_HPFILT_			(0x00002000)
+#define MAC_CR_LCOLL_			(0x00001000)
+#define MAC_CR_DIS_BCAST_		(0x00000800)
+#define MAC_CR_DIS_RTRY_		(0x00000400)
+#define MAC_CR_PADSTR_			(0x00000100)
+#define MAC_CR_BOLMT_MSK		(0x000000C0)
+#define MAC_CR_MFCHK_			(0x00000020)
+#define MAC_CR_TXEN_			(0x00000008)
+#define MAC_CR_RXEN_			(0x00000004)
+
+#define ADDRH				(0x84)
+
+#define ADDRL				(0x88)
+
+#define HASHH				(0x8C)
+
+#define HASHL				(0x90)
+
+#define MII_ACCESS			(0x94)
+#define MII_ACCESS_MII_BUSY_		(0x00000001)
+#define MII_ACCESS_MII_WRITE_		(0x00000002)
+#define MII_ACCESS_MII_READ_		(0x00000000)
+#define MII_ACCESS_INDX_MSK_		(0x000007C0)
+#define MII_ACCESS_PHYADDR_MSK_		(0x0000F8C0)
+#define MII_ACCESS_INDX_SHFT_CNT	(6)
+#define MII_ACCESS_PHYADDR_SHFT_CNT	(11)
+
+#define MII_DATA			(0x98)
+
+#define FLOW				(0x9C)
+
+#define VLAN1				(0xA0)
+
+#define VLAN2				(0xA4)
+
+#define WUFF				(0xA8)
+
+#define WUCSR				(0xAC)
+
+#define COE_CR				(0xB0)
+#define TX_COE_EN			(0x00010000)
+#define RX_COE_MODE			(0x00000002)
+#define RX_COE_EN			(0x00000001)
+
+/***********************************************/
+/*     System Control and Status Registers     */
+/***********************************************/
+#define ID_REV				(0xC0)
+
+#define INT_CTL				(0xC4)
+#define INT_CTL_SW_INT_EN_		(0x00008000)
+#define INT_CTL_SBERR_INT_EN_		(1 << 12)
+#define INT_CTL_MBERR_INT_EN_		(1 << 13)
+#define INT_CTL_GPT_INT_EN_		(0x00000008)
+#define INT_CTL_PHY_INT_EN_		(0x00000004)
+#define INT_CTL_WAKE_INT_EN_		(0x00000002)
+
+#define INT_STAT			(0xC8)
+#define INT_STAT_SW_INT_		(1 << 15)
+#define INT_STAT_MBERR_INT_		(1 << 13)
+#define INT_STAT_SBERR_INT_		(1 << 12)
+#define INT_STAT_GPT_INT_		(1 << 3)
+#define INT_STAT_PHY_INT_		(0x00000004)
+#define INT_STAT_WAKE_INT_		(0x00000002)
+#define INT_STAT_DMAC_INT_		(0x00000001)
+
+#define INT_CFG				(0xCC)
+#define INT_CFG_IRQ_INT_		(0x00080000)
+#define INT_CFG_IRQ_EN_			(0x00040000)
+#define INT_CFG_INT_DEAS_CLR_		(0x00000200)
+#define INT_CFG_INT_DEAS_MASK		(0x000000FF)
+
+#define GPIO_CFG			(0xD0)
+#define GPIO_CFG_LED_3_			(0x40000000)
+#define GPIO_CFG_LED_2_			(0x20000000)
+#define GPIO_CFG_LED_1_			(0x10000000)
+#define GPIO_CFG_EEPR_EN_		(0x00700000)
+
+#define GPT_CFG				(0xD4)
+#define GPT_CFG_TIMER_EN_		(0x20000000)
+
+#define GPT_CNT				(0xD8)
+
+#define BUS_CFG				(0xDC)
+#define BUS_CFG_RXTXWEIGHT_1_1		(0 << 25)
+#define BUS_CFG_RXTXWEIGHT_2_1		(1 << 25)
+#define BUS_CFG_RXTXWEIGHT_3_1		(2 << 25)
+#define BUS_CFG_RXTXWEIGHT_4_1		(3 << 25)
+
+#define PMT_CTRL			(0xE0)
+
+#define FREE_RUN			(0xF4)
+
+#define E2P_CMD				(0xF8)
+#define E2P_CMD_EPC_BUSY_		(0x80000000)
+#define E2P_CMD_EPC_CMD_		(0x70000000)
+#define E2P_CMD_EPC_CMD_READ_		(0x00000000)
+#define E2P_CMD_EPC_CMD_EWDS_		(0x10000000)
+#define E2P_CMD_EPC_CMD_EWEN_		(0x20000000)
+#define E2P_CMD_EPC_CMD_WRITE_		(0x30000000)
+#define E2P_CMD_EPC_CMD_WRAL_		(0x40000000)
+#define E2P_CMD_EPC_CMD_ERASE_		(0x50000000)
+#define E2P_CMD_EPC_CMD_ERAL_		(0x60000000)
+#define E2P_CMD_EPC_CMD_RELOAD_		(0x70000000)
+#define E2P_CMD_EPC_TIMEOUT_		(0x00000200)
+#define E2P_CMD_MAC_ADDR_LOADED_	(0x00000100)
+#define E2P_CMD_EPC_ADDR_		(0x000000FF)
+
+#define E2P_DATA			(0xFC)
+#define E2P_DATA_EEPROM_DATA_		(0x000000FF)
+
+#endif /* _SMSC9420_H */