[gPXE-devel] [PATCH][forcedeth] Replace forcedeth driver with native gPXE driver

Andrei Faur da3drus at gmail.com
Sun Jul 18 10:19:26 EDT 2010


This patch adds a native gPXE forcedeth driver and removes the legacy
Etherboot forcedeth driver. It supports 40 different chips, compared
to the original 14.

It has been tested on a NIC with an CK804 Ethernet Controller, and
the results of downloading 5 100mb images in a row have been: 13/12/12/12/12
seconds; booting DSL using pxelinux also succeeded. The driver has also
been tested by chaining undionly.kpxe and it worked. It is approx ~1s
slower on average than the legacy driver because of additional code
to check for link state and because the legacy driver did not wait for
packets to finish sending making it faster than it should have been.

Signed-off-by: Andrei Faur <da3drus at gmail.com>
---
v1 (improvements over RFC version):
 * implemented link state checking
 * solved MAC address issue by running in promiscuous mode and
 preforming manual frame filtering. This way, we can avoid writing
 the correct MAC address back to the NIC since it was only required
 for the NIC's built-in filtering.

 src/drivers/net/forcedeth.c | 3689 ++++++++++++++++++++++++++-----------------
 src/drivers/net/forcedeth.h |  602 +++++++
 2 files changed, 2847 insertions(+), 1444 deletions(-)
 rewrite src/drivers/net/forcedeth.c (93%)
 create mode 100644 src/drivers/net/forcedeth.h

diff --git a/src/drivers/net/forcedeth.c b/src/drivers/net/forcedeth.c
dissimilarity index 93%
index 3d44d86..6be44e2 100644
--- a/src/drivers/net/forcedeth.c
+++ b/src/drivers/net/forcedeth.c
@@ -1,1444 +1,2245 @@
-/**************************************************************************
-*    forcedeth.c -- Etherboot device driver for the NVIDIA nForce 
-*			media access controllers.
-*
-* Note: This driver is based on the Linux driver that was based on
-*      a cleanroom reimplementation which was based on reverse
-*      engineered documentation written by Carl-Daniel Hailfinger
-*      and Andrew de Quincey. It's neither supported nor endorsed
-*      by NVIDIA Corp. Use at your own risk.
-*
-*    Written 2004 by Timothy Legge <tlegge at rogers.com>
-*
-*    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., 675 Mass Ave, Cambridge, MA 02139, USA.
-*
-*    Portions of this code based on:
-*		forcedeth: Ethernet driver for NVIDIA nForce media access controllers:
-*
-*	(C) 2003 Manfred Spraul
-*		See Linux Driver for full information
-*	
-*	Linux Driver Version 0.30, 25 Sep 2004
-*	Linux Kernel 2.6.10
-* 
-* 
-*    REVISION HISTORY:
-*    ================
-*    v1.0	01-31-2004	timlegge	Initial port of Linux driver
-*    v1.1	02-03-2004	timlegge	Large Clean up, first release 
-*    v1.2	05-14-2005	timlegge	Add Linux 0.22 to .030 features
-*
-*    Indent Options: indent -kr -i8
-***************************************************************************/
-
-FILE_LICENCE ( GPL2_OR_LATER );
-
-/* to get some global routines like printf */
-#include "etherboot.h"
-/* to get the interface to the body of the program */
-#include "nic.h"
-/* to get the PCI support functions, if this is a PCI NIC */
-#include <gpxe/pci.h>
-/* Include timer support functions */
-#include <gpxe/ethernet.h>
-#include "mii.h"
-
-#define drv_version "v1.2"
-#define drv_date "05-14-2005"
-
-//#define TFTM_DEBUG
-#ifdef TFTM_DEBUG
-#define dprintf(x) printf x
-#else
-#define dprintf(x)
-#endif
-
-#define ETH_DATA_LEN   1500
-
-/* Condensed operations for readability. */
-#define virt_to_le32desc(addr)  cpu_to_le32(virt_to_bus(addr))
-#define le32desc_to_virt(addr)  bus_to_virt(le32_to_cpu(addr))
-
-static unsigned long BASE;
-/* NIC specific static variables go here */
-#define PCI_DEVICE_ID_NVIDIA_NVENET_1           0x01c3
-#define PCI_DEVICE_ID_NVIDIA_NVENET_2           0x0066
-#define PCI_DEVICE_ID_NVIDIA_NVENET_4           0x0086
-#define PCI_DEVICE_ID_NVIDIA_NVENET_5           0x008c
-#define PCI_DEVICE_ID_NVIDIA_NVENET_3           0x00d6
-#define PCI_DEVICE_ID_NVIDIA_NVENET_7           0x00df
-#define PCI_DEVICE_ID_NVIDIA_NVENET_6           0x00e6
-#define PCI_DEVICE_ID_NVIDIA_NVENET_8           0x0056
-#define PCI_DEVICE_ID_NVIDIA_NVENET_9           0x0057
-#define PCI_DEVICE_ID_NVIDIA_NVENET_10          0x0037
-#define PCI_DEVICE_ID_NVIDIA_NVENET_11          0x0038
-#define PCI_DEVICE_ID_NVIDIA_NVENET_15          0x0373
-
-
-/*
- * Hardware access:
- */
-
-#define DEV_NEED_LASTPACKET1	0x0001	/* set LASTPACKET1 in tx flags */
-#define DEV_IRQMASK_1		0x0002	/* use NVREG_IRQMASK_WANTED_1 for irq mask */
-#define DEV_IRQMASK_2		0x0004	/* use NVREG_IRQMASK_WANTED_2 for irq mask */
-#define DEV_NEED_TIMERIRQ	0x0008	/* set the timer irq flag in the irq mask */
-#define DEV_NEED_LINKTIMER	0x0010	/* poll link settings. Relies on the timer irq */
-
-enum {
-	NvRegIrqStatus = 0x000,
-#define NVREG_IRQSTAT_MIIEVENT	0040
-#define NVREG_IRQSTAT_MASK		0x1ff
-	NvRegIrqMask = 0x004,
-#define NVREG_IRQ_RX_ERROR		0x0001
-#define NVREG_IRQ_RX			0x0002
-#define NVREG_IRQ_RX_NOBUF		0x0004
-#define NVREG_IRQ_TX_ERR		0x0008
-#define NVREG_IRQ_TX2			0x0010
-#define NVREG_IRQ_TIMER			0x0020
-#define NVREG_IRQ_LINK			0x0040
-#define NVREG_IRQ_TX1			0x0100
-#define NVREG_IRQMASK_WANTED_1		0x005f
-#define NVREG_IRQMASK_WANTED_2		0x0147
-#define NVREG_IRQ_UNKNOWN		(~(NVREG_IRQ_RX_ERROR|NVREG_IRQ_RX|NVREG_IRQ_RX_NOBUF|NVREG_IRQ_TX_ERR|NVREG_IRQ_TX2|NVREG_IRQ_TIMER|NVREG_IRQ_LINK|NVREG_IRQ_TX1))
-
-	NvRegUnknownSetupReg6 = 0x008,
-#define NVREG_UNKSETUP6_VAL		3
-
-/*
- * NVREG_POLL_DEFAULT is the interval length of the timer source on the nic
- * NVREG_POLL_DEFAULT=97 would result in an interval length of 1 ms
- */
-	NvRegPollingInterval = 0x00c,
-#define NVREG_POLL_DEFAULT	970
-	NvRegMisc1 = 0x080,
-#define NVREG_MISC1_HD		0x02
-#define NVREG_MISC1_FORCE	0x3b0f3c
-
-	NvRegTransmitterControl = 0x084,
-#define NVREG_XMITCTL_START	0x01
-	NvRegTransmitterStatus = 0x088,
-#define NVREG_XMITSTAT_BUSY	0x01
-
-	NvRegPacketFilterFlags = 0x8c,
-#define NVREG_PFF_ALWAYS	0x7F0008
-#define NVREG_PFF_PROMISC	0x80
-#define NVREG_PFF_MYADDR	0x20
-
-	NvRegOffloadConfig = 0x90,
-#define NVREG_OFFLOAD_HOMEPHY	0x601
-#define NVREG_OFFLOAD_NORMAL	RX_NIC_BUFSIZE
-	NvRegReceiverControl = 0x094,
-#define NVREG_RCVCTL_START	0x01
-	NvRegReceiverStatus = 0x98,
-#define NVREG_RCVSTAT_BUSY	0x01
-
-	NvRegRandomSeed = 0x9c,
-#define NVREG_RNDSEED_MASK	0x00ff
-#define NVREG_RNDSEED_FORCE	0x7f00
-#define NVREG_RNDSEED_FORCE2	0x2d00
-#define NVREG_RNDSEED_FORCE3	0x7400
-
-	NvRegUnknownSetupReg1 = 0xA0,
-#define NVREG_UNKSETUP1_VAL	0x16070f
-	NvRegUnknownSetupReg2 = 0xA4,
-#define NVREG_UNKSETUP2_VAL	0x16
-	NvRegMacAddrA = 0xA8,
-	NvRegMacAddrB = 0xAC,
-	NvRegMulticastAddrA = 0xB0,
-#define NVREG_MCASTADDRA_FORCE	0x01
-	NvRegMulticastAddrB = 0xB4,
-	NvRegMulticastMaskA = 0xB8,
-	NvRegMulticastMaskB = 0xBC,
-
-	NvRegPhyInterface = 0xC0,
-#define PHY_RGMII		0x10000000
-
-	NvRegTxRingPhysAddr = 0x100,
-	NvRegRxRingPhysAddr = 0x104,
-	NvRegRingSizes = 0x108,
-#define NVREG_RINGSZ_TXSHIFT 0
-#define NVREG_RINGSZ_RXSHIFT 16
-	NvRegUnknownTransmitterReg = 0x10c,
-	NvRegLinkSpeed = 0x110,
-#define NVREG_LINKSPEED_FORCE 0x10000
-#define NVREG_LINKSPEED_10	1000
-#define NVREG_LINKSPEED_100	100
-#define NVREG_LINKSPEED_1000	50
-	NvRegUnknownSetupReg5 = 0x130,
-#define NVREG_UNKSETUP5_BIT31	(1<<31)
-	NvRegUnknownSetupReg3 = 0x13c,
-#define NVREG_UNKSETUP3_VAL1	0x200010
-	NvRegTxRxControl = 0x144,
-#define NVREG_TXRXCTL_KICK	0x0001
-#define NVREG_TXRXCTL_BIT1	0x0002
-#define NVREG_TXRXCTL_BIT2	0x0004
-#define NVREG_TXRXCTL_IDLE	0x0008
-#define NVREG_TXRXCTL_RESET	0x0010
-#define NVREG_TXRXCTL_RXCHECK	0x0400
-	NvRegMIIStatus = 0x180,
-#define NVREG_MIISTAT_ERROR		0x0001
-#define NVREG_MIISTAT_LINKCHANGE	0x0008
-#define NVREG_MIISTAT_MASK		0x000f
-#define NVREG_MIISTAT_MASK2		0x000f
-	NvRegUnknownSetupReg4 = 0x184,
-#define NVREG_UNKSETUP4_VAL	8
-
-	NvRegAdapterControl = 0x188,
-#define NVREG_ADAPTCTL_START	0x02
-#define NVREG_ADAPTCTL_LINKUP	0x04
-#define NVREG_ADAPTCTL_PHYVALID	0x40000
-#define NVREG_ADAPTCTL_RUNNING	0x100000
-#define NVREG_ADAPTCTL_PHYSHIFT	24
-	NvRegMIISpeed = 0x18c,
-#define NVREG_MIISPEED_BIT8	(1<<8)
-#define NVREG_MIIDELAY	5
-	NvRegMIIControl = 0x190,
-#define NVREG_MIICTL_INUSE	0x08000
-#define NVREG_MIICTL_WRITE	0x00400
-#define NVREG_MIICTL_ADDRSHIFT	5
-	NvRegMIIData = 0x194,
-	NvRegWakeUpFlags = 0x200,
-#define NVREG_WAKEUPFLAGS_VAL		0x7770
-#define NVREG_WAKEUPFLAGS_BUSYSHIFT	24
-#define NVREG_WAKEUPFLAGS_ENABLESHIFT	16
-#define NVREG_WAKEUPFLAGS_D3SHIFT	12
-#define NVREG_WAKEUPFLAGS_D2SHIFT	8
-#define NVREG_WAKEUPFLAGS_D1SHIFT	4
-#define NVREG_WAKEUPFLAGS_D0SHIFT	0
-#define NVREG_WAKEUPFLAGS_ACCEPT_MAGPAT		0x01
-#define NVREG_WAKEUPFLAGS_ACCEPT_WAKEUPPAT	0x02
-#define NVREG_WAKEUPFLAGS_ACCEPT_LINKCHANGE	0x04
-#define NVREG_WAKEUPFLAGS_ENABLE	0x1111
-
-	NvRegPatternCRC = 0x204,
-	NvRegPatternMask = 0x208,
-	NvRegPowerCap = 0x268,
-#define NVREG_POWERCAP_D3SUPP	(1<<30)
-#define NVREG_POWERCAP_D2SUPP	(1<<26)
-#define NVREG_POWERCAP_D1SUPP	(1<<25)
-	NvRegPowerState = 0x26c,
-#define NVREG_POWERSTATE_POWEREDUP	0x8000
-#define NVREG_POWERSTATE_VALID		0x0100
-#define NVREG_POWERSTATE_MASK		0x0003
-#define NVREG_POWERSTATE_D0		0x0000
-#define NVREG_POWERSTATE_D1		0x0001
-#define NVREG_POWERSTATE_D2		0x0002
-#define NVREG_POWERSTATE_D3		0x0003
-};
-
-#define FLAG_MASK_V1 0xffff0000
-#define FLAG_MASK_V2 0xffffc000
-#define LEN_MASK_V1 (0xffffffff ^ FLAG_MASK_V1)
-#define LEN_MASK_V2 (0xffffffff ^ FLAG_MASK_V2)
-
-#define NV_TX_LASTPACKET	(1<<16)
-#define NV_TX_RETRYERROR	(1<<19)
-#define NV_TX_LASTPACKET1	(1<<24)
-#define NV_TX_DEFERRED		(1<<26)
-#define NV_TX_CARRIERLOST	(1<<27)
-#define NV_TX_LATECOLLISION	(1<<28)
-#define NV_TX_UNDERFLOW		(1<<29)
-#define NV_TX_ERROR		(1<<30)
-#define NV_TX_VALID		(1<<31)
-
-#define NV_TX2_LASTPACKET	(1<<29)
-#define NV_TX2_RETRYERROR	(1<<18)
-#define NV_TX2_LASTPACKET1	(1<<23)
-#define NV_TX2_DEFERRED		(1<<25)
-#define NV_TX2_CARRIERLOST	(1<<26)
-#define NV_TX2_LATECOLLISION	(1<<27)
-#define NV_TX2_UNDERFLOW	(1<<28)
-/* error and valid are the same for both */
-#define NV_TX2_ERROR		(1<<30)
-#define NV_TX2_VALID		(1<<31)
-
-#define NV_RX_DESCRIPTORVALID	(1<<16)
-#define NV_RX_MISSEDFRAME	(1<<17)
-#define NV_RX_SUBSTRACT1	(1<<18)
-#define NV_RX_ERROR1		(1<<23)
-#define NV_RX_ERROR2		(1<<24)
-#define NV_RX_ERROR3		(1<<25)
-#define NV_RX_ERROR4		(1<<26)
-#define NV_RX_CRCERR		(1<<27)
-#define NV_RX_OVERFLOW		(1<<28)
-#define NV_RX_FRAMINGERR	(1<<29)
-#define NV_RX_ERROR		(1<<30)
-#define NV_RX_AVAIL		(1<<31)
-
-#define NV_RX2_CHECKSUMMASK	(0x1C000000)
-#define NV_RX2_CHECKSUMOK1	(0x10000000)
-#define NV_RX2_CHECKSUMOK2	(0x14000000)
-#define NV_RX2_CHECKSUMOK3	(0x18000000)
-#define NV_RX2_DESCRIPTORVALID	(1<<29)
-#define NV_RX2_SUBSTRACT1	(1<<25)
-#define NV_RX2_ERROR1		(1<<18)
-#define NV_RX2_ERROR2		(1<<19)
-#define NV_RX2_ERROR3		(1<<20)
-#define NV_RX2_ERROR4		(1<<21)
-#define NV_RX2_CRCERR		(1<<22)
-#define NV_RX2_OVERFLOW		(1<<23)
-#define NV_RX2_FRAMINGERR	(1<<24)
-/* error and avail are the same for both */
-#define NV_RX2_ERROR		(1<<30)
-#define NV_RX2_AVAIL		(1<<31)
-
-/* Miscelaneous hardware related defines: */
-#define NV_PCI_REGSZ		0x270
-
-/* various timeout delays: all in usec */
-#define NV_TXRX_RESET_DELAY	4
-#define NV_TXSTOP_DELAY1	10
-#define NV_TXSTOP_DELAY1MAX	500000
-#define NV_TXSTOP_DELAY2	100
-#define NV_RXSTOP_DELAY1	10
-#define NV_RXSTOP_DELAY1MAX	500000
-#define NV_RXSTOP_DELAY2	100
-#define NV_SETUP5_DELAY		5
-#define NV_SETUP5_DELAYMAX	50000
-#define NV_POWERUP_DELAY	5
-#define NV_POWERUP_DELAYMAX	5000
-#define NV_MIIBUSY_DELAY	50
-#define NV_MIIPHY_DELAY	10
-#define NV_MIIPHY_DELAYMAX	10000
-
-#define NV_WAKEUPPATTERNS	5
-#define NV_WAKEUPMASKENTRIES	4
-
-/* General driver defaults */
-#define NV_WATCHDOG_TIMEO	(5*HZ)
-
-#define RX_RING		4
-#define TX_RING		2
-
-/* 
- * If your nic mysteriously hangs then try to reduce the limits
- * to 1/0: It might be required to set NV_TX_LASTPACKET in the
- * last valid ring entry. But this would be impossible to
- * implement - probably a disassembly error.
- */
-#define TX_LIMIT_STOP	63
-#define TX_LIMIT_START	62
-
-/* rx/tx mac addr + type + vlan + align + slack*/
-#define RX_NIC_BUFSIZE		(ETH_DATA_LEN + 64)
-/* even more slack */
-#define RX_ALLOC_BUFSIZE	(ETH_DATA_LEN + 128)
-
-#define OOM_REFILL	(1+HZ/20)
-#define POLL_WAIT	(1+HZ/100)
-#define LINK_TIMEOUT	(3*HZ)
-
-/* 
- * desc_ver values:
- * This field has two purposes:
- * - Newer nics uses a different ring layout. The layout is selected by
- *   comparing np->desc_ver with DESC_VER_xy.
- * - It contains bits that are forced on when writing to NvRegTxRxControl.
- */
-#define DESC_VER_1	0x0
-#define DESC_VER_2	(0x02100|NVREG_TXRXCTL_RXCHECK)
-
-/* PHY defines */
-#define PHY_OUI_MARVELL	0x5043
-#define PHY_OUI_CICADA	0x03f1
-#define PHYID1_OUI_MASK	0x03ff
-#define PHYID1_OUI_SHFT	6
-#define PHYID2_OUI_MASK	0xfc00
-#define PHYID2_OUI_SHFT	10
-#define PHY_INIT1	0x0f000
-#define PHY_INIT2	0x0e00
-#define PHY_INIT3	0x01000
-#define PHY_INIT4	0x0200
-#define PHY_INIT5	0x0004
-#define PHY_INIT6	0x02000
-#define PHY_GIGABIT	0x0100
-
-#define PHY_TIMEOUT	0x1
-#define PHY_ERROR	0x2
-
-#define PHY_100	0x1
-#define PHY_1000	0x2
-#define PHY_HALF	0x100
-
-
-/* Bit to know if MAC addr is stored in correct order */
-#define MAC_ADDR_CORRECT	0x01
-
-/* Big endian: should work, but is untested */
-struct ring_desc {
-	u32 PacketBuffer;
-	u32 FlagLen;
-};
-
-
-/* Define the TX and RX Descriptor and Buffers */
-struct {
-	struct ring_desc tx_ring[TX_RING];
-	unsigned char txb[TX_RING * RX_NIC_BUFSIZE];
-	struct ring_desc rx_ring[RX_RING];
-	unsigned char rxb[RX_RING * RX_NIC_BUFSIZE];
-} forcedeth_bufs __shared;
-#define tx_ring forcedeth_bufs.tx_ring
-#define rx_ring forcedeth_bufs.rx_ring
-#define txb forcedeth_bufs.txb
-#define rxb forcedeth_bufs.rxb
-
-/* Private Storage for the NIC */
-static struct forcedeth_private {
-	/* General data:
-	 * Locking: spin_lock(&np->lock); */
-	int in_shutdown;
-	u32 linkspeed;
-	int duplex;
-	int phyaddr;
-	int wolenabled;
-	unsigned int phy_oui;
-	u16 gigabit;
-
-	/* General data: RO fields */
-	u8 *ring_addr;
-	u32 orig_mac[2];
-	u32 irqmask;
-	u32 desc_ver;
-	/* rx specific fields.
-	 * Locking: Within irq hander or disable_irq+spin_lock(&np->lock);
-	 */
-	unsigned int cur_rx, refill_rx;
-
-	/*
-	 * tx specific fields.
-	 */
-	unsigned int next_tx, nic_tx;
-	u32 tx_flags;
-} npx;
-
-static struct forcedeth_private *np;
-
-static inline void pci_push(u8 * base)
-{
-	/* force out pending posted writes */
-	readl(base);
-}
-
-static inline u32 nv_descr_getlength(struct ring_desc *prd, u32 v)
-{
-	return le32_to_cpu(prd->FlagLen)
-	    & ((v == DESC_VER_1) ? LEN_MASK_V1 : LEN_MASK_V2);
-}
-
-static int reg_delay(int offset, u32 mask,
-		     u32 target, int delay, int delaymax, const char *msg)
-{
-	u8 *base = (u8 *) BASE;
-
-	pci_push(base);
-	do {
-		udelay(delay);
-		delaymax -= delay;
-		if (delaymax < 0) {
-			if (msg)
-				printf("%s", msg);
-			return 1;
-		}
-	} while ((readl(base + offset) & mask) != target);
-	return 0;
-}
-
-#define MII_READ	(-1)
-
-/* mii_rw: read/write a register on the PHY.
- *
- * Caller must guarantee serialization
- */
-static int mii_rw(struct nic *nic __unused, int addr, int miireg,
-		  int value)
-{
-	u8 *base = (u8 *) BASE;
-	u32 reg;
-	int retval;
-
-	writel(NVREG_MIISTAT_MASK, base + NvRegMIIStatus);
-
-	reg = readl(base + NvRegMIIControl);
-	if (reg & NVREG_MIICTL_INUSE) {
-		writel(NVREG_MIICTL_INUSE, base + NvRegMIIControl);
-		udelay(NV_MIIBUSY_DELAY);
-	}
-
-	reg =
-	    (addr << NVREG_MIICTL_ADDRSHIFT) | miireg;
-	if (value != MII_READ) {
-		writel(value, base + NvRegMIIData);
-		reg |= NVREG_MIICTL_WRITE;
-	}
-	writel(reg, base + NvRegMIIControl);
-
-	if (reg_delay(NvRegMIIControl, NVREG_MIICTL_INUSE, 0,
-		      NV_MIIPHY_DELAY, NV_MIIPHY_DELAYMAX, NULL)) {
-		dprintf(("mii_rw of reg %d at PHY %d timed out.\n",
-			 miireg, addr));
-		retval = -1;
-	} else if (value != MII_READ) {
-		/* it was a write operation - fewer failures are detectable */
-		dprintf(("mii_rw wrote 0x%x to reg %d at PHY %d\n",
-			 value, miireg, addr));
-		retval = 0;
-	} else if (readl(base + NvRegMIIStatus) & NVREG_MIISTAT_ERROR) {
-		dprintf(("mii_rw of reg %d at PHY %d failed.\n",
-			 miireg, addr));
-		retval = -1;
-	} else {
-		retval = readl(base + NvRegMIIData);
-		dprintf(("mii_rw read from reg %d at PHY %d: 0x%x.\n",
-			 miireg, addr, retval));
-	}
-	return retval;
-}
-
-static int phy_reset(struct nic *nic)
-{
-
-	u32 miicontrol;
-	unsigned int tries = 0;
-
-	miicontrol = mii_rw(nic, np->phyaddr, MII_BMCR, MII_READ);
-	miicontrol |= BMCR_RESET;
-	if (mii_rw(nic, np->phyaddr, MII_BMCR, miicontrol)) {
-		return -1;
-	}
-
-	/* wait for 500ms */
-	mdelay(500);
-
-	/* must wait till reset is deasserted */
-	while (miicontrol & BMCR_RESET) {
-		mdelay(10);
-		miicontrol = mii_rw(nic, np->phyaddr, MII_BMCR, MII_READ);
-		/* FIXME: 100 tries seem excessive */
-		if (tries++ > 100)
-			return -1;
-	}
-	return 0;
-}
-
-static int phy_init(struct nic *nic)
-{
-	u8 *base = (u8 *) BASE;
-	u32 phyinterface, phy_reserved, mii_status, mii_control,
-	    mii_control_1000, reg;
-
-	/* set advertise register */
-	reg = mii_rw(nic, np->phyaddr, MII_ADVERTISE, MII_READ);
-	reg |=
-	    (ADVERTISE_10HALF | ADVERTISE_10FULL | ADVERTISE_100HALF |
-	     ADVERTISE_100FULL | 0x800 | 0x400);
-	if (mii_rw(nic, np->phyaddr, MII_ADVERTISE, reg)) {
-		printf("phy write to advertise failed.\n");
-		return PHY_ERROR;
-	}
-
-	/* get phy interface type */
-	phyinterface = readl(base + NvRegPhyInterface);
-
-	/* see if gigabit phy */
-	mii_status = mii_rw(nic, np->phyaddr, MII_BMSR, MII_READ);
-
-	if (mii_status & PHY_GIGABIT) {
-		np->gigabit = PHY_GIGABIT;
-		mii_control_1000 =
-		    mii_rw(nic, np->phyaddr, MII_CTRL1000, MII_READ);
-		mii_control_1000 &= ~ADVERTISE_1000HALF;
-		if (phyinterface & PHY_RGMII)
-			mii_control_1000 |= ADVERTISE_1000FULL;
-		else
-			mii_control_1000 &= ~ADVERTISE_1000FULL;
-
-		if (mii_rw
-		    (nic, np->phyaddr, MII_CTRL1000, mii_control_1000)) {
-			printf("phy init failed.\n");
-			return PHY_ERROR;
-		}
-	} else
-		np->gigabit = 0;
-
-	/* reset the phy */
-	if (phy_reset(nic)) {
-		printf("phy reset failed\n");
-		return PHY_ERROR;
-	}
-
-	/* phy vendor specific configuration */
-	if ((np->phy_oui == PHY_OUI_CICADA) && (phyinterface & PHY_RGMII)) {
-		phy_reserved =
-		    mii_rw(nic, np->phyaddr, MII_RESV1, MII_READ);
-		phy_reserved &= ~(PHY_INIT1 | PHY_INIT2);
-		phy_reserved |= (PHY_INIT3 | PHY_INIT4);
-		if (mii_rw(nic, np->phyaddr, MII_RESV1, phy_reserved)) {
-			printf("phy init failed.\n");
-			return PHY_ERROR;
-		}
-		phy_reserved =
-		    mii_rw(nic, np->phyaddr, MII_NCONFIG, MII_READ);
-		phy_reserved |= PHY_INIT5;
-		if (mii_rw(nic, np->phyaddr, MII_NCONFIG, phy_reserved)) {
-			printf("phy init failed.\n");
-			return PHY_ERROR;
-		}
-	}
-	if (np->phy_oui == PHY_OUI_CICADA) {
-		phy_reserved =
-		    mii_rw(nic, np->phyaddr, MII_SREVISION, MII_READ);
-		phy_reserved |= PHY_INIT6;
-		if (mii_rw(nic, np->phyaddr, MII_SREVISION, phy_reserved)) {
-			printf("phy init failed.\n");
-			return PHY_ERROR;
-		}
-	}
-
-	/* restart auto negotiation */
-	mii_control = mii_rw(nic, np->phyaddr, MII_BMCR, MII_READ);
-	mii_control |= (BMCR_ANRESTART | BMCR_ANENABLE);
-	if (mii_rw(nic, np->phyaddr, MII_BMCR, mii_control)) {
-		return PHY_ERROR;
-	}
-
-	return 0;
-}
-
-static void start_rx(struct nic *nic __unused)
-{
-	u8 *base = (u8 *) BASE;
-
-	dprintf(("start_rx\n"));
-	/* Already running? Stop it. */
-	if (readl(base + NvRegReceiverControl) & NVREG_RCVCTL_START) {
-		writel(0, base + NvRegReceiverControl);
-		pci_push(base);
-	}
-	writel(np->linkspeed, base + NvRegLinkSpeed);
-	pci_push(base);
-	writel(NVREG_RCVCTL_START, base + NvRegReceiverControl);
-	pci_push(base);
-}
-
-static void stop_rx(void)
-{
-	u8 *base = (u8 *) BASE;
-
-	dprintf(("stop_rx\n"));
-	writel(0, base + NvRegReceiverControl);
-	reg_delay(NvRegReceiverStatus, NVREG_RCVSTAT_BUSY, 0,
-		  NV_RXSTOP_DELAY1, NV_RXSTOP_DELAY1MAX,
-		  "stop_rx: ReceiverStatus remained busy");
-
-	udelay(NV_RXSTOP_DELAY2);
-	writel(0, base + NvRegLinkSpeed);
-}
-
-static void start_tx(struct nic *nic __unused)
-{
-	u8 *base = (u8 *) BASE;
-
-	dprintf(("start_tx\n"));
-	writel(NVREG_XMITCTL_START, base + NvRegTransmitterControl);
-	pci_push(base);
-}
-
-static void stop_tx(void)
-{
-	u8 *base = (u8 *) BASE;
-
-	dprintf(("stop_tx\n"));
-	writel(0, base + NvRegTransmitterControl);
-	reg_delay(NvRegTransmitterStatus, NVREG_XMITSTAT_BUSY, 0,
-		  NV_TXSTOP_DELAY1, NV_TXSTOP_DELAY1MAX,
-		  "stop_tx: TransmitterStatus remained busy");
-
-	udelay(NV_TXSTOP_DELAY2);
-	writel(0, base + NvRegUnknownTransmitterReg);
-}
-
-
-static void txrx_reset(struct nic *nic __unused)
-{
-	u8 *base = (u8 *) BASE;
-
-	dprintf(("txrx_reset\n"));
-	writel(NVREG_TXRXCTL_BIT2 | NVREG_TXRXCTL_RESET | np->desc_ver,
-	       base + NvRegTxRxControl);
-
-	pci_push(base);
-	udelay(NV_TXRX_RESET_DELAY);
-	writel(NVREG_TXRXCTL_BIT2 | np->desc_ver, base + NvRegTxRxControl);
-	pci_push(base);
-}
-
-/*
- * alloc_rx: fill rx ring entries.
- * Return 1 if the allocations for the skbs failed and the
- * rx engine is without Available descriptors
- */
-static int alloc_rx(struct nic *nic __unused)
-{
-	unsigned int refill_rx = np->refill_rx;
-	int i;
-	//while (np->cur_rx != refill_rx) {
-	for (i = 0; i < RX_RING; i++) {
-		//int nr = refill_rx % RX_RING;
-		rx_ring[i].PacketBuffer =
-		    virt_to_le32desc(&rxb[i * RX_NIC_BUFSIZE]);
-		wmb();
-		rx_ring[i].FlagLen =
-		    cpu_to_le32(RX_NIC_BUFSIZE | NV_RX_AVAIL);
-		/*      printf("alloc_rx: Packet  %d marked as Available\n",
-		   refill_rx); */
-		refill_rx++;
-	}
-	np->refill_rx = refill_rx;
-	if (np->cur_rx - refill_rx == RX_RING)
-		return 1;
-	return 0;
-}
-
-static int update_linkspeed(struct nic *nic)
-{
-	int adv, lpa;
-	u32 newls;
-	int newdup = np->duplex;
-	u32 mii_status;
-	int retval = 0; 
-	u32 control_1000, status_1000, phyreg;
-	u8 *base = (u8 *) BASE;
-	int i;
-
-	/* BMSR_LSTATUS is latched, read it twice:
-	 * we want the current value.
-	 */
-	mii_rw(nic, np->phyaddr, MII_BMSR, MII_READ);
-	mii_status = mii_rw(nic, np->phyaddr, MII_BMSR, MII_READ);
-
-#if 1
-	//yhlu
-	for(i=0;i<30;i++) {
-		mii_status = mii_rw(nic, np->phyaddr, MII_BMSR, MII_READ);
-		if((mii_status & BMSR_LSTATUS) && (mii_status & BMSR_ANEGCOMPLETE)) break;
-		mdelay(100);
-	}
-#endif
-
-	if (!(mii_status & BMSR_LSTATUS)) {
-		printf
-		    ("no link detected by phy - falling back to 10HD.\n");
-		newls = NVREG_LINKSPEED_FORCE | NVREG_LINKSPEED_10;
-		newdup = 0;
-		retval = 0;
-		goto set_speed;
-	}
-
-	/* check auto negotiation is complete */
-	if (!(mii_status & BMSR_ANEGCOMPLETE)) {
-		/* still in autonegotiation - configure nic for 10 MBit HD and wait. */
-		newls = NVREG_LINKSPEED_FORCE | NVREG_LINKSPEED_10;
-		newdup = 0;
-		retval = 0;
-		printf("autoneg not completed - falling back to 10HD.\n");
-		goto set_speed;
-	}
-
-	retval = 1;
-	if (np->gigabit == PHY_GIGABIT) {
-		control_1000 =
-		    mii_rw(nic, np->phyaddr, MII_CTRL1000, MII_READ);
-		status_1000 =
-		    mii_rw(nic, np->phyaddr, MII_STAT1000, MII_READ);
-
-		if ((control_1000 & ADVERTISE_1000FULL) &&
-		    (status_1000 & LPA_1000FULL)) {
-			printf
-			    ("update_linkspeed: GBit ethernet detected.\n");
-			newls =
-			    NVREG_LINKSPEED_FORCE | NVREG_LINKSPEED_1000;
-			newdup = 1;
-			goto set_speed;
-		}
-	}
-
-	adv = mii_rw(nic, np->phyaddr, MII_ADVERTISE, MII_READ);
-	lpa = mii_rw(nic, np->phyaddr, MII_LPA, MII_READ);
-	dprintf(("update_linkspeed: PHY advertises 0x%hX, lpa 0x%hX.\n",
-		 adv, lpa));
-
-	/* FIXME: handle parallel detection properly, handle gigabit ethernet */
-	lpa = lpa & adv;
-	if (lpa & LPA_100FULL) {
-		newls = NVREG_LINKSPEED_FORCE | NVREG_LINKSPEED_100;
-		newdup = 1;
-	} else if (lpa & LPA_100HALF) {
-		newls = NVREG_LINKSPEED_FORCE | NVREG_LINKSPEED_100;
-		newdup = 0;
-	} else if (lpa & LPA_10FULL) {
-		newls = NVREG_LINKSPEED_FORCE | NVREG_LINKSPEED_10;
-		newdup = 1;
-	} else if (lpa & LPA_10HALF) {
-		newls = NVREG_LINKSPEED_FORCE | NVREG_LINKSPEED_10;
-		newdup = 0;
-	} else {
-		printf("bad ability %hX - falling back to 10HD.\n", lpa);
-		newls = NVREG_LINKSPEED_FORCE | NVREG_LINKSPEED_10;
-		newdup = 0;
-	}
-
-      set_speed:
-	if (np->duplex == newdup && np->linkspeed == newls)
-		return retval;
-
-	dprintf(("changing link setting from %d/%s to %d/%s.\n",
-	       np->linkspeed, np->duplex ? "Full-Duplex": "Half-Duplex", newls, newdup ? "Full-Duplex": "Half-Duplex"));
-
-	np->duplex = newdup;
-	np->linkspeed = newls;
-
-	if (np->gigabit == PHY_GIGABIT) {
-		phyreg = readl(base + NvRegRandomSeed);
-		phyreg &= ~(0x3FF00);
-		if ((np->linkspeed & 0xFFF) == NVREG_LINKSPEED_10)
-			phyreg |= NVREG_RNDSEED_FORCE3;
-		else if ((np->linkspeed & 0xFFF) == NVREG_LINKSPEED_100)
-			phyreg |= NVREG_RNDSEED_FORCE2;
-		else if ((np->linkspeed & 0xFFF) == NVREG_LINKSPEED_1000)
-			phyreg |= NVREG_RNDSEED_FORCE;
-		writel(phyreg, base + NvRegRandomSeed);
-	}
-
-	phyreg = readl(base + NvRegPhyInterface);
-	phyreg &= ~(PHY_HALF | PHY_100 | PHY_1000);
-	if (np->duplex == 0)
-		phyreg |= PHY_HALF;
-	if ((np->linkspeed & 0xFFF) == NVREG_LINKSPEED_100)
-		phyreg |= PHY_100;
-	else if ((np->linkspeed & 0xFFF) == NVREG_LINKSPEED_1000)
-		phyreg |= PHY_1000;
-	writel(phyreg, base + NvRegPhyInterface);
-
-	writel(NVREG_MISC1_FORCE | (np->duplex ? 0 : NVREG_MISC1_HD),
-	       base + NvRegMisc1);
-	pci_push(base);
-	writel(np->linkspeed, base + NvRegLinkSpeed);
-	pci_push(base);
-
-	return retval;
-}
-
-#if 0 /* Not used */
-static void nv_linkchange(struct nic *nic)
-{
-	if (update_linkspeed(nic)) {
-//                if (netif_carrier_ok(nic)) {
-		stop_rx();
-//=                } else {
-		//                      netif_carrier_on(dev);
-		//                    printk(KERN_INFO "%s: link up.\n", dev->name);
-		//          }
-		start_rx(nic);
-	} else {
-		//        if (netif_carrier_ok(dev)) {
-		//              netif_carrier_off(dev);
-		//            printk(KERN_INFO "%s: link down.\n", dev->name);
-		stop_rx();
-		//  }
-	}
-}
-#endif
-
-static int init_ring(struct nic *nic)
-{
-	int i;
-
-	np->next_tx = np->nic_tx = 0;
-	for (i = 0; i < TX_RING; i++)
-		tx_ring[i].FlagLen = 0;
-
-	np->cur_rx = 0;
-	np->refill_rx = 0;
-	for (i = 0; i < RX_RING; i++)
-		rx_ring[i].FlagLen = 0;
-	return alloc_rx(nic);
-}
-
-static void set_multicast(struct nic *nic)
-{
-
-	u8 *base = (u8 *) BASE;
-	u32 addr[2];
-	u32 mask[2];
-	u32 pff;
-	u32 alwaysOff[2];
-	u32 alwaysOn[2];
-
-	memset(addr, 0, sizeof(addr));
-	memset(mask, 0, sizeof(mask));
-
-	pff = NVREG_PFF_MYADDR;
-
-	alwaysOn[0] = alwaysOn[1] = alwaysOff[0] = alwaysOff[1] = 0;
-
-	addr[0] = alwaysOn[0];
-	addr[1] = alwaysOn[1];
-	mask[0] = alwaysOn[0] | alwaysOff[0];
-	mask[1] = alwaysOn[1] | alwaysOff[1];
-
-	addr[0] |= NVREG_MCASTADDRA_FORCE;
-	pff |= NVREG_PFF_ALWAYS;
-	stop_rx();
-	writel(addr[0], base + NvRegMulticastAddrA);
-	writel(addr[1], base + NvRegMulticastAddrB);
-	writel(mask[0], base + NvRegMulticastMaskA);
-	writel(mask[1], base + NvRegMulticastMaskB);
-	writel(pff, base + NvRegPacketFilterFlags);
-	start_rx(nic);
-}
-
-/**************************************************************************
-RESET - Reset the NIC to prepare for use
-***************************************************************************/
-static int forcedeth_reset(struct nic *nic)
-{
-	u8 *base = (u8 *) BASE;
-	int ret, oom, i;
-	ret = 0;
-	dprintf(("forcedeth: open\n"));
-
-	/* 1) erase previous misconfiguration */
-	/* 4.1-1: stop adapter: ignored, 4.3 seems to be overkill */
-	writel(NVREG_MCASTADDRA_FORCE, base + NvRegMulticastAddrA);
-	writel(0, base + NvRegMulticastAddrB);
-	writel(0, base + NvRegMulticastMaskA);
-	writel(0, base + NvRegMulticastMaskB);
-	writel(0, base + NvRegPacketFilterFlags);
-
-	writel(0, base + NvRegTransmitterControl);
-	writel(0, base + NvRegReceiverControl);
-
-	writel(0, base + NvRegAdapterControl);
-
-	/* 2) initialize descriptor rings */
-	oom = init_ring(nic);
-
-	writel(0, base + NvRegLinkSpeed);
-	writel(0, base + NvRegUnknownTransmitterReg);
-	txrx_reset(nic);
-	writel(0, base + NvRegUnknownSetupReg6);
-
-	np->in_shutdown = 0;
-
-	/* 3) set mac address */
-	{
-		u32 mac[2];
-
-		mac[0] =
-		    (nic->node_addr[0] << 0) + (nic->node_addr[1] << 8) +
-		    (nic->node_addr[2] << 16) + (nic->node_addr[3] << 24);
-		mac[1] =
-		    (nic->node_addr[4] << 0) + (nic->node_addr[5] << 8);
-
-		writel(mac[0], base + NvRegMacAddrA);
-		writel(mac[1], base + NvRegMacAddrB);
-	}
-
-	/* 4) give hw rings */
-	writel((u32) virt_to_le32desc(&rx_ring[0]),
-	       base + NvRegRxRingPhysAddr);
-	writel((u32) virt_to_le32desc(&tx_ring[0]),
-	       base + NvRegTxRingPhysAddr);
-
-	writel(((RX_RING - 1) << NVREG_RINGSZ_RXSHIFT) +
-	       ((TX_RING - 1) << NVREG_RINGSZ_TXSHIFT),
-	       base + NvRegRingSizes);
-
-	/* 5) continue setup */
-	np->linkspeed = NVREG_LINKSPEED_FORCE | NVREG_LINKSPEED_10;
-	np->duplex = 0;
-	writel(np->linkspeed, base + NvRegLinkSpeed);
-	writel(NVREG_UNKSETUP3_VAL1, base + NvRegUnknownSetupReg3);
-	writel(np->desc_ver, base + NvRegTxRxControl);
-	pci_push(base);
-	writel(NVREG_TXRXCTL_BIT1 | np->desc_ver, base + NvRegTxRxControl);
-	reg_delay(NvRegUnknownSetupReg5, NVREG_UNKSETUP5_BIT31,
-		  NVREG_UNKSETUP5_BIT31, NV_SETUP5_DELAY,
-		  NV_SETUP5_DELAYMAX,
-		  "open: SetupReg5, Bit 31 remained off\n");
-
-	writel(0, base + NvRegUnknownSetupReg4);
-//       writel(NVREG_IRQSTAT_MASK, base + NvRegIrqStatus);
-	writel(NVREG_MIISTAT_MASK2, base + NvRegMIIStatus);
-#if 0
-	printf("%d-Mbs Link, %s-Duplex\n",
-	       np->linkspeed & NVREG_LINKSPEED_10 ? 10 : 100,
-	       np->duplex ? "Full" : "Half");
-#endif
-
-	/* 6) continue setup */
-	writel(NVREG_MISC1_FORCE | NVREG_MISC1_HD, base + NvRegMisc1);
-	writel(readl(base + NvRegTransmitterStatus),
-	       base + NvRegTransmitterStatus);
-	writel(NVREG_PFF_ALWAYS, base + NvRegPacketFilterFlags);
-	writel(NVREG_OFFLOAD_NORMAL, base + NvRegOffloadConfig);
-
-	writel(readl(base + NvRegReceiverStatus),
-	       base + NvRegReceiverStatus);
-
-	/* Get a random number */
-	i = random();
-	writel(NVREG_RNDSEED_FORCE | (i & NVREG_RNDSEED_MASK),
-	       base + NvRegRandomSeed);
-	writel(NVREG_UNKSETUP1_VAL, base + NvRegUnknownSetupReg1);
-	writel(NVREG_UNKSETUP2_VAL, base + NvRegUnknownSetupReg2);
-	writel(NVREG_POLL_DEFAULT, base + NvRegPollingInterval);
-	writel(NVREG_UNKSETUP6_VAL, base + NvRegUnknownSetupReg6);
-	writel((np->
-		phyaddr << NVREG_ADAPTCTL_PHYSHIFT) |
-	       NVREG_ADAPTCTL_PHYVALID | NVREG_ADAPTCTL_RUNNING,
-	       base + NvRegAdapterControl);
-	writel(NVREG_MIISPEED_BIT8 | NVREG_MIIDELAY, base + NvRegMIISpeed);
-	writel(NVREG_UNKSETUP4_VAL, base + NvRegUnknownSetupReg4);
-	writel(NVREG_WAKEUPFLAGS_VAL, base + NvRegWakeUpFlags);
-
-	i = readl(base + NvRegPowerState);
-	if ((i & NVREG_POWERSTATE_POWEREDUP) == 0)
-		writel(NVREG_POWERSTATE_POWEREDUP | i,
-		       base + NvRegPowerState);
-
-	pci_push(base);
-	udelay(10);
-	writel(readl(base + NvRegPowerState) | NVREG_POWERSTATE_VALID,
-	       base + NvRegPowerState);
-
-	writel(0, base + NvRegIrqMask);
-	pci_push(base);
-	writel(NVREG_MIISTAT_MASK2, base + NvRegMIIStatus);
-	writel(NVREG_IRQSTAT_MASK, base + NvRegIrqStatus);
-	pci_push(base);
-/*
-	writel(np->irqmask, base + NvRegIrqMask);
-*/
-	writel(NVREG_MCASTADDRA_FORCE, base + NvRegMulticastAddrA);
-	writel(0, base + NvRegMulticastAddrB);
-	writel(0, base + NvRegMulticastMaskA);
-	writel(0, base + NvRegMulticastMaskB);
-	writel(NVREG_PFF_ALWAYS | NVREG_PFF_MYADDR,
-	       base + NvRegPacketFilterFlags);
-
-	set_multicast(nic);
-	/* One manual link speed update: Interrupts are enabled, future link
-	 * speed changes cause interrupts and are handled by nv_link_irq().
-	 */
-	{
-		u32 miistat;
-		miistat = readl(base + NvRegMIIStatus);
-		writel(NVREG_MIISTAT_MASK, base + NvRegMIIStatus);
-		dprintf(("startup: got 0x%hX.\n", miistat));
-	}
-	ret = update_linkspeed(nic);
-
-	//start_rx(nic);
-	start_tx(nic);
-
-	if (ret) {
-		//Start Connection netif_carrier_on(dev);
-	} else {
-		printf("no link during initialization.\n");
-	}
-
-	return ret;
-}
-
-/* 
- * extern void hex_dump(const char *data, const unsigned int len);
-*/
-/**************************************************************************
-POLL - Wait for a frame
-***************************************************************************/
-static int forcedeth_poll(struct nic *nic, int retrieve)
-{
-	/* return true if there's an ethernet packet ready to read */
-	/* nic->packet should contain data on return */
-	/* nic->packetlen should contain length of data */
-
-	int len;
-	int i;
-	u32 Flags;
-
-	i = np->cur_rx % RX_RING;
-
-	Flags = le32_to_cpu(rx_ring[i].FlagLen);
-	len = nv_descr_getlength(&rx_ring[i], np->desc_ver);
-
-	if (Flags & NV_RX_AVAIL)
-		return 0;	/* still owned by hardware, */
-
-	if (np->desc_ver == DESC_VER_1) {
-		if (!(Flags & NV_RX_DESCRIPTORVALID))
-			return 0;
-	} else {
-		if (!(Flags & NV_RX2_DESCRIPTORVALID))
-			return 0;
-	}
-
-	if (!retrieve)
-		return 1;
-
-	/* got a valid packet - forward it to the network core */
-	nic->packetlen = len;
-	memcpy(nic->packet, rxb + (i * RX_NIC_BUFSIZE), nic->packetlen);
-/*
- * 	hex_dump(rxb + (i * RX_NIC_BUFSIZE), len);
-*/
-	wmb();
-	np->cur_rx++;
-	alloc_rx(nic);
-	return 1;
-}
-
-
-/**************************************************************************
-TRANSMIT - Transmit a frame
-***************************************************************************/
-static void forcedeth_transmit(struct nic *nic, const char *d,	/* Destination */
-			       unsigned int t,	/* Type */
-			       unsigned int s,	/* size */
-			       const char *p)
-{				/* Packet */
-	/* send the packet to destination */
-	u8 *ptxb;
-	u16 nstype;
-	u8 *base = (u8 *) BASE;
-	int nr = np->next_tx % TX_RING;
-
-	/* point to the current txb incase multiple tx_rings are used */
-	ptxb = txb + (nr * RX_NIC_BUFSIZE);
-	//np->tx_skbuff[nr] = ptxb;
-
-	/* copy the packet to ring buffer */
-	memcpy(ptxb, d, ETH_ALEN);	/* dst */
-	memcpy(ptxb + ETH_ALEN, nic->node_addr, ETH_ALEN);	/* src */
-	nstype = htons((u16) t);	/* type */
-	memcpy(ptxb + 2 * ETH_ALEN, (u8 *) & nstype, 2);	/* type */
-	memcpy(ptxb + ETH_HLEN, p, s);
-
-	s += ETH_HLEN;
-	while (s < ETH_ZLEN)	/* pad to min length */
-		ptxb[s++] = '\0';
-
-	tx_ring[nr].PacketBuffer = (u32) virt_to_le32desc(ptxb);
-
-	wmb();
-	tx_ring[nr].FlagLen = cpu_to_le32((s - 1) | np->tx_flags);
-
-	writel(NVREG_TXRXCTL_KICK | np->desc_ver, base + NvRegTxRxControl);
-	pci_push(base);
-	np->next_tx++;
-}
-
-/**************************************************************************
-DISABLE - Turn off ethernet interface
-***************************************************************************/
-static void forcedeth_disable ( struct nic *nic __unused ) {
-	/* put the card in its initial state */
-	/* This function serves 3 purposes.
-	 * This disables DMA and interrupts so we don't receive
-	 *  unexpected packets or interrupts from the card after
-	 *  etherboot has finished. 
-	 * This frees resources so etherboot may use
-	 *  this driver on another interface
-	 * This allows etherboot to reinitialize the interface
-	 *  if something is something goes wrong.
-	 */
-	u8 *base = (u8 *) BASE;
-	np->in_shutdown = 1;
-	stop_tx();
-	stop_rx();
-
-	/* disable interrupts on the nic or we will lock up */
-	writel(0, base + NvRegIrqMask);
-	pci_push(base);
-	dprintf(("Irqmask is zero again\n"));
-
-	/* specia op:o write back the misordered MAC address - otherwise
-	 * the next probe_nic would see a wrong address.
-	 */
-	writel(np->orig_mac[0], base + NvRegMacAddrA);
-	writel(np->orig_mac[1], base + NvRegMacAddrB);
-}
-
-/**************************************************************************
-IRQ - Enable, Disable, or Force interrupts
-***************************************************************************/
-static void forcedeth_irq(struct nic *nic __unused,
-			  irq_action_t action __unused)
-{
-	switch (action) {
-	case DISABLE:
-		break;
-	case ENABLE:
-		break;
-	case FORCE:
-		break;
-	}
-}
-
-static struct nic_operations forcedeth_operations = {
-	.connect	= dummy_connect,
-	.poll		= forcedeth_poll,
-	.transmit	= forcedeth_transmit,
-	.irq		= forcedeth_irq,
-
-};
-
-/**************************************************************************
-PROBE - Look for an adapter, this routine's visible to the outside
-***************************************************************************/
-#define IORESOURCE_MEM 0x00000200
-#define board_found 1
-#define valid_link 0
-static int forcedeth_probe ( struct nic *nic, struct pci_device *pci ) {
-
-	unsigned long addr;
-	int sz;
-	u8 *base;
-	int i;
-	struct pci_device_id *ids = pci->driver->ids;
-	int id_count = pci->driver->id_count;
-	unsigned int flags = 0;
-
-	if (pci->ioaddr == 0)
-		return 0;
-
-	printf("forcedeth.c: Found %s, vendor=0x%hX, device=0x%hX\n",
-	       pci->driver_name, pci->vendor, pci->device);
-
-        nic->ioaddr = pci->ioaddr;
-        nic->irqno = 0;
-
-	/* point to private storage */
-	np = &npx;
-
-	adjust_pci_device(pci);
-
-	addr = pci_bar_start(pci, PCI_BASE_ADDRESS_0);
-	sz = pci_bar_size(pci, PCI_BASE_ADDRESS_0);
-
-	/* BASE is used throughout to address the card */
-	BASE = (unsigned long) ioremap(addr, sz);
-	if (!BASE)
-		return 0;
-
-	/* handle different descriptor versions */
-	if (pci->device == PCI_DEVICE_ID_NVIDIA_NVENET_1 ||
-	    pci->device == PCI_DEVICE_ID_NVIDIA_NVENET_2 ||
-	    pci->device == PCI_DEVICE_ID_NVIDIA_NVENET_3)
-		np->desc_ver = DESC_VER_1;
-	else
-		np->desc_ver = DESC_VER_2;
-
-	//rx_ring[0] = rx_ring;
-	//tx_ring[0] = tx_ring; 
-
-	/* read the mac address */
-	base = (u8 *) BASE;
-	np->orig_mac[0] = readl(base + NvRegMacAddrA);
-	np->orig_mac[1] = readl(base + NvRegMacAddrB);
-
-	/* lookup the flags from pci_device_id */
-	for(i = 0; i < id_count; i++) {
-		if(pci->vendor == ids[i].vendor &&
-		   pci->device == ids[i].device) {
-			flags = ids[i].driver_data;
-			break;
-		   }
-	}
-
-	/* read MAC address */
-	if(flags & MAC_ADDR_CORRECT) {
-		nic->node_addr[0] = (np->orig_mac[0] >>  0) & 0xff;
-		nic->node_addr[1] = (np->orig_mac[0] >>  8) & 0xff;
-		nic->node_addr[2] = (np->orig_mac[0] >> 16) & 0xff;
-		nic->node_addr[3] = (np->orig_mac[0] >> 24) & 0xff;
-		nic->node_addr[4] = (np->orig_mac[1] >>  0) & 0xff;
-		nic->node_addr[5] = (np->orig_mac[1] >>  8) & 0xff;
-	} else {
-		nic->node_addr[0] = (np->orig_mac[1] >>  8) & 0xff;
-		nic->node_addr[1] = (np->orig_mac[1] >>  0) & 0xff;
-		nic->node_addr[2] = (np->orig_mac[0] >> 24) & 0xff;
-		nic->node_addr[3] = (np->orig_mac[0] >> 16) & 0xff;
-		nic->node_addr[4] = (np->orig_mac[0] >>  8) & 0xff;
-		nic->node_addr[5] = (np->orig_mac[0] >>  0) & 0xff;
-	}
-#ifdef LINUX
-	if (!is_valid_ether_addr(dev->dev_addr)) {
-		/*
-		 * Bad mac address. At least one bios sets the mac address
-		 * to 01:23:45:67:89:ab
-		 */
-		printk(KERN_ERR
-		       "%s: Invalid Mac address detected: %02x:%02x:%02x:%02x:%02x:%02x\n",
-		       pci_name(pci_dev), dev->dev_addr[0],
-		       dev->dev_addr[1], dev->dev_addr[2],
-		       dev->dev_addr[3], dev->dev_addr[4],
-		       dev->dev_addr[5]);
-		printk(KERN_ERR
-		       "Please complain to your hardware vendor. Switching to a random MAC.\n");
-		dev->dev_addr[0] = 0x00;
-		dev->dev_addr[1] = 0x00;
-		dev->dev_addr[2] = 0x6c;
-		get_random_bytes(&dev->dev_addr[3], 3);
-	}
-#endif
-
-	DBG ( "%s: MAC Address %s\n", pci->driver_name, eth_ntoa ( nic->node_addr ) );
-
- 	/* disable WOL */
-	writel(0, base + NvRegWakeUpFlags);
- 	np->wolenabled = 0;
-	
- 	if (np->desc_ver == DESC_VER_1) {
- 		np->tx_flags = NV_TX_LASTPACKET | NV_TX_VALID;
- 	} else {
- 		np->tx_flags = NV_TX2_LASTPACKET | NV_TX2_VALID;
- 	}
-
-  	switch (pci->device) {
-  	case 0x01C3:		// nforce
-	case 0x054C:
- 		// DEV_IRQMASK_1|DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER,
- 		np->irqmask = NVREG_IRQMASK_WANTED_2 | NVREG_IRQ_TIMER;
-		//              np->need_linktimer = 1;
-		//              np->link_timeout = jiffies + LINK_TIMEOUT;
-  		break;
- 	case 0x0066:
- 		/* Fall Through */
- 	case 0x00D6:
- 		// DEV_NEED_LASTPACKET1|DEV_IRQMASK_2|DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER
-  		np->irqmask = NVREG_IRQMASK_WANTED_2;
-  		np->irqmask |= NVREG_IRQ_TIMER;
-		//              np->need_linktimer = 1;
-		//              np->link_timeout = jiffies + LINK_TIMEOUT;
- 		if (np->desc_ver == DESC_VER_1)
- 			np->tx_flags |= NV_TX_LASTPACKET1;
- 		else
- 			np->tx_flags |= NV_TX2_LASTPACKET1;
-  		break;
-	case 0x0373:
-		/* Fall Through */
- 	case 0x0086:
- 		/* Fall Through */
- 	case 0x008c:
- 		/* Fall Through */
- 	case 0x00e6:
- 		/* Fall Through */
- 	case 0x00df:
-		/* Fall Through */
- 	case 0x0056:
- 		/* Fall Through */
- 	case 0x0057:
- 		/* Fall Through */
- 	case 0x0037:
- 		/* Fall Through */
- 	case 0x0038:
- 		//DEV_NEED_LASTPACKET1|DEV_IRQMASK_2|DEV_NEED_TIMERIRQ
-  		np->irqmask = NVREG_IRQMASK_WANTED_2;
-  		np->irqmask |= NVREG_IRQ_TIMER;
-		//              np->need_linktimer = 1;
-		//              np->link_timeout = jiffies + LINK_TIMEOUT;
- 		if (np->desc_ver == DESC_VER_1)
- 			np->tx_flags |= NV_TX_LASTPACKET1;
- 		else
- 			np->tx_flags |= NV_TX2_LASTPACKET1;
- 		break;
- 	default:
- 		printf
-			("Your card was undefined in this driver.  Review driver_data in Linux driver and send a patch\n");
- 	}
-	
- 	/* find a suitable phy */
- 	for (i = 1; i < 32; i++) {
- 		int id1, id2;
- 		id1 = mii_rw(nic, i, MII_PHYSID1, MII_READ);
- 		if (id1 < 0 || id1 == 0xffff)
- 			continue;
- 		id2 = mii_rw(nic, i, MII_PHYSID2, MII_READ);
- 		if (id2 < 0 || id2 == 0xffff)
- 			continue;
- 		id1 = (id1 & PHYID1_OUI_MASK) << PHYID1_OUI_SHFT;
- 		id2 = (id2 & PHYID2_OUI_MASK) >> PHYID2_OUI_SHFT;
- 		dprintf
-			(("%s: open: Found PHY %hX:%hX at address %d.\n",
-			  pci->driver_name, id1, id2, i));
- 		np->phyaddr = i;
- 		np->phy_oui = id1 | id2;
- 		break;
- 	}
- 	if (i == 32) {
- 		/* PHY in isolate mode? No phy attached and user wants to
- 		 * test loopback? Very odd, but can be correct.
- 		 */
- 		printf
-			("%s: open: Could not find a valid PHY.\n", pci->driver_name);
- 	}
-	
- 	if (i != 32) {
- 		/* reset it */
- 		phy_init(nic);
- 	}
-	
-	dprintf(("%s: forcedeth.c: subsystem: %hX:%hX bound to %s\n",
-		 pci->driver_name, pci->vendor, pci->dev_id, pci->driver_name));
-	if(!forcedeth_reset(nic)) return 0; // no valid link
-
-	/* point to NIC specific routines */
-	nic->nic_op	= &forcedeth_operations;
-	return 1;
-}
-
-static struct pci_device_id forcedeth_nics[] = {
-PCI_ROM(0x10de, 0x01C3, "nforce", "nForce NVENET_1 Ethernet Controller", 0),
-PCI_ROM(0x10de, 0x0066, "nforce2", "nForce NVENET_2 Ethernet Controller", 0),
-PCI_ROM(0x10de, 0x00D6, "nforce3", "nForce NVENET_3 Ethernet Controller", 0),
-PCI_ROM(0x10de, 0x0086, "nforce4", "nForce NVENET_4 Ethernet Controller", 0),
-PCI_ROM(0x10de, 0x008c, "nforce5", "nForce NVENET_5 Ethernet Controller", 0),
-PCI_ROM(0x10de, 0x00e6, "nforce6", "nForce NVENET_6 Ethernet Controller", 0),
-PCI_ROM(0x10de, 0x00df, "nforce7", "nForce NVENET_7 Ethernet Controller", 0),
-PCI_ROM(0x10de, 0x0056, "nforce8", "nForce NVENET_8 Ethernet Controller", 0),
-PCI_ROM(0x10de, 0x0057, "nforce9", "nForce NVENET_9 Ethernet Controller", 0),
-PCI_ROM(0x10de, 0x0037, "nforce10", "nForce NVENET_10 Ethernet Controller", 0),
-PCI_ROM(0x10de, 0x0038, "nforce11", "nForce NVENET_11 Ethernet Controller", 0),
-PCI_ROM(0x10de, 0x0373, "nforce15", "nForce NVENET_15 Ethernet Controller", 0),
-PCI_ROM(0x10de, 0x0269, "nforce16", "nForce NVENET_16 Ethernet Controller", 0),
-PCI_ROM(0x10de, 0x0760, "nforce17", "nForce NVENET_17 Ethernet Controller", MAC_ADDR_CORRECT),
-PCI_ROM(0x10de, 0x054c, "nforce67", "nForce NVENET_67 Ethernet Controller", MAC_ADDR_CORRECT),
-};
-
-PCI_DRIVER ( forcedeth_driver, forcedeth_nics, PCI_NO_CLASS );
-
-DRIVER ( "forcedeth", nic_driver, pci_driver, forcedeth_driver,
-	 forcedeth_probe, forcedeth_disable );
-
-/*
- * Local variables:
- *  c-basic-offset: 8
- *  c-indent-level: 8
- *  tab-width: 8
- * End:
- */
+/*
+ * Copyright (c) 2010 Andrei Faur <da3drus at gmail.com>
+ *
+ * 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 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <assert.h>
+#include <byteswap.h>
+#include <errno.h>
+#include <gpxe/ethernet.h>
+#include <gpxe/if_ether.h>
+#include <gpxe/io.h>
+#include <gpxe/iobuf.h>
+#include <gpxe/malloc.h>
+#include <gpxe/netdevice.h>
+#include <gpxe/crypto.h>
+#include <gpxe/pci.h>
+#include <gpxe/timer.h>
+#include <mii.h>
+#include "forcedeth.h"
+
+static inline void pci_push ( void *ioaddr )
+{
+	/* force out pending posted writes */
+	readl ( ioaddr );
+}
+
+static inline u32 dma_low ( dma_addr_t addr )
+{
+	return addr;
+}
+
+static inline u32 dma_high ( dma_addr_t addr )
+{
+	return addr >> 31 >> 1;
+}
+
+static int
+reg_delay ( struct forcedeth_private *priv, int offset, u32 mask,
+	    u32 target, int delay, int delaymax, const char *msg )
+{
+	void *ioaddr = priv->mmio_addr;
+
+	pci_push ( ioaddr );
+	do {
+		udelay ( delay );
+		delaymax -= delay;
+		if ( delaymax < 0 ) {
+			if ( msg )
+				DBG ( "%s\n", msg );
+			return 1;
+		}
+	} while ( ( readl ( ioaddr + offset ) & mask ) != target );
+
+	return 0;
+}
+
+/* read/write a register on the PHY */
+static int
+mii_rw ( struct forcedeth_private *priv, int addr, int miireg, int value )
+{
+	void *ioaddr = priv->mmio_addr;
+	u32 reg;
+	int retval;
+
+	writel ( NVREG_MIISTAT_MASK_RW, ioaddr + NvRegMIIStatus );
+
+	reg = readl ( ioaddr + NvRegMIIControl );
+	if ( reg & NVREG_MIICTL_INUSE ) {
+		writel ( NVREG_MIICTL_INUSE, ioaddr + NvRegMIIControl );
+		udelay ( NV_MIIBUSY_DELAY );
+	}
+
+	reg = ( addr << NVREG_MIICTL_ADDRSHIFT ) | miireg;
+	if ( value != MII_READ ) {
+		writel ( value, ioaddr + NvRegMIIData );
+		reg |= NVREG_MIICTL_WRITE;
+	}
+	writel ( reg, ioaddr + NvRegMIIControl );
+
+	if ( reg_delay ( priv, NvRegMIIControl, NVREG_MIICTL_INUSE, 0,
+			NV_MIIPHY_DELAY, NV_MIIPHY_DELAYMAX, NULL ) ) {
+		DBG ( "mii_rw of reg %d at PHY %d timed out.\n",
+			miireg, addr );
+		retval = -1;
+	} else if ( value != MII_READ ) {
+		/* it was a write operation - fewer failures are detectable */
+		DBG ( "mii_rw wrote 0x%x to reg %d at PHY %d\n",
+			value, miireg, addr );
+		retval = 0;
+	} else if ( readl ( ioaddr + NvRegMIIStatus ) & NVREG_MIISTAT_ERROR ) {
+		DBG ( "mii_rw of reg %d at PHY %d failed.\n",
+			miireg, addr );
+		retval = -1;
+	} else {
+		retval = readl ( ioaddr + NvRegMIIData );
+		DBG ( "mii_rw read from reg %d at PHY %d: 0x%x.\n",
+			miireg, addr, retval );
+	}
+
+	return retval;
+}
+
+static inline u32
+nv_get_desc_len ( struct ring_desc *prd, u32 v )
+{
+	return le32_to_cpu ( prd->flaglen )
+		& ( ( v == DESC_VER_1 ) ? LEN_MASK_V1 : LEN_MASK_V2 );
+}
+
+static inline u32
+nv_get_desc_len_ex ( struct ring_desc_ex *prd, u32 v __unused )
+{
+	return le32_to_cpu ( prd->flaglen ) & LEN_MASK_V2;
+}
+
+static int
+nv_optimised ( struct forcedeth_private *priv )
+{
+	if ( priv->desc_ver == DESC_VER_1 ||
+	     priv->desc_ver == DESC_VER_2 )
+		return 0;
+
+	return 1;
+}
+
+static void
+nv_txrx_gate ( struct forcedeth_private *priv, int gate )
+{
+	void *ioaddr = priv->mmio_addr;
+	u32 powerstate;
+
+	if ( ! priv->mac_in_use &&
+	     ( priv->driver_data & DEV_HAS_POWER_CNTRL ) ) {
+		powerstate = readl ( ioaddr + NvRegPowerState2 );
+		if ( gate )
+			powerstate |= NVREG_POWERSTATE2_GATE_CLOCKS;
+		else
+			powerstate &= ~NVREG_POWERSTATE2_GATE_CLOCKS;
+		writel ( powerstate, ioaddr + NvRegPowerState2 );
+	}
+}
+
+static void
+nv_mac_reset ( struct forcedeth_private * priv )
+{
+	void *ioaddr = priv->mmio_addr;
+	u32 temp1, temp2, temp3;
+
+	writel ( NVREG_TXRXCTL_BIT2 | NVREG_TXRXCTL_RESET | priv->txrxctl_bits,
+		 ioaddr + NvRegTxRxControl );
+	pci_push ( ioaddr );
+
+	/* save registers since they will be cleared on reset */
+	temp1 = readl ( ioaddr + NvRegMacAddrA );
+	temp2 = readl ( ioaddr + NvRegMacAddrB );
+	temp3 = readl ( ioaddr + NvRegTransmitPoll );
+
+	writel ( NVREG_MAC_RESET_ASSERT, ioaddr + NvRegMacReset );
+	pci_push ( ioaddr );
+	udelay ( NV_MAC_RESET_DELAY );
+	writel ( 0, ioaddr + NvRegMacReset );
+	pci_push ( ioaddr );
+	udelay ( NV_MAC_RESET_DELAY );
+
+	/* restore saved registers */
+	writel ( temp1, ioaddr + NvRegMacAddrA );
+	writel ( temp2, ioaddr + NvRegMacAddrB );
+	writel ( temp3, ioaddr + NvRegTransmitPoll );
+
+	writel ( NVREG_TXRXCTL_BIT2 | priv->txrxctl_bits,
+		 ioaddr + NvRegTxRxControl );
+	pci_push ( ioaddr );
+}
+
+static void
+nv_init_tx_ring ( struct forcedeth_private *priv )
+{
+	int i;
+
+	for ( i = 0; i < TX_RING_SIZE; i++ ) {
+		if ( ! nv_optimised ( priv ) ) {
+			priv->tx_ring.orig[i].flaglen = 0;
+			priv->tx_ring.orig[i].buf = 0;
+		} else {
+			priv->tx_ring.ex[i].flaglen = 0;
+			priv->tx_ring.ex[i].txvlan = 0;
+			priv->tx_ring.ex[i].bufhigh = 0;
+			priv->tx_ring.ex[i].buflow = 0;
+		}
+		priv->tx_iobuf[i] = NULL;
+	}
+
+	priv->tx_fill_ctr = 0;
+	priv->tx_curr = 0;
+	priv->tx_tail = 0;
+}
+
+/**
+ * nv_alloc_rx - Allocates iobufs for every Rx descriptor 
+ * that doesn't have one and isn't in use by the hardware
+ *
+ * @v priv	Driver private structure
+ */
+static void
+nv_alloc_rx ( struct forcedeth_private *priv )
+{
+	union ring_type rx_curr_desc;
+	int i;
+	u32 status;
+
+	DBGP ( "nv_alloc_rx\n" );
+
+	for ( i = 0; i < RX_RING_SIZE; i++ ) {
+		if ( ! nv_optimised ( priv ) ) {
+			rx_curr_desc.orig = priv->rx_ring.orig + i;
+			status = le32_to_cpu ( rx_curr_desc.orig->flaglen );
+		} else {
+			rx_curr_desc.ex = priv->rx_ring.ex + i;
+			status = le32_to_cpu ( rx_curr_desc.ex->flaglen );
+		}
+
+		/* Don't touch the descriptors owned by the hardware */
+		if ( status & NV_RX_AVAIL )
+			continue;
+
+		/* Descriptors with iobufs still need to be processed */
+		if ( priv->rx_iobuf[i] != NULL )
+			continue;
+
+		/* If alloc_iob fails, try again later (next poll) */
+		if ( ! ( priv->rx_iobuf[i] = alloc_iob ( RX_BUF_SZ ) ) ) {
+			DBG ( "Refill rx_ring failed, size %d\n", RX_BUF_SZ );
+			break;
+		}
+
+		if ( ! nv_optimised ( priv ) ) {
+			rx_curr_desc.orig->buf =
+				cpu_to_le32 ( virt_to_bus ( priv->rx_iobuf[i]->data ) );
+			rx_curr_desc.orig->flaglen =
+				cpu_to_le32 ( RX_BUF_SZ | NV_RX_AVAIL );
+		} else {
+			rx_curr_desc.ex->bufhigh =
+				cpu_to_le32 ( dma_high ( virt_to_bus ( priv->rx_iobuf[i]->data ) ) );
+			rx_curr_desc.ex->buflow =
+				cpu_to_le32 ( dma_low ( virt_to_bus ( priv->rx_iobuf[i]->data ) ) );
+			rx_curr_desc.ex->txvlan = 0;
+			wmb();
+			rx_curr_desc.ex->flaglen =
+				cpu_to_le32 ( RX_BUF_SZ | NV_RX_AVAIL );
+		}
+	}
+}
+
+static void
+nv_init_rx_ring ( struct forcedeth_private *priv )
+{
+	int i;
+
+	for ( i = 0; i < RX_RING_SIZE; i++ ) {
+		if ( ! nv_optimised ( priv ) ) {
+			priv->rx_ring.orig[i].flaglen = 0;
+			priv->rx_ring.orig[i].buf = 0;
+		} else {
+			priv->rx_ring.ex[i].flaglen = 0;
+			priv->rx_ring.ex[i].txvlan = 0;
+			priv->rx_ring.ex[i].bufhigh = 0;
+			priv->rx_ring.ex[i].buflow = 0;
+		}
+		priv->rx_iobuf[i] = NULL;
+	}
+
+	priv->rx_curr = 0;
+}
+
+/**
+ * nv_init_rings - Allocate and intialize descriptor rings
+ *
+ * @v priv	Driver private structure
+ *
+ * @ret rc	Return status code
+ **/
+static int
+nv_init_rings ( struct forcedeth_private *priv )
+{
+	void *ioaddr = priv->mmio_addr;
+	int rc = -ENOMEM;
+
+	/* Allocate ring for both TX and RX */
+	if ( ! nv_optimised ( priv ) ) {
+		priv->rx_ring.orig =
+			malloc_dma ( sizeof(struct ring_desc) * RXTX_RING_SIZE, 32 );
+		if ( ! priv->rx_ring.orig )
+			goto err_malloc;
+		priv->tx_ring.orig = &priv->rx_ring.orig[RX_RING_SIZE];
+	} else {
+		priv->rx_ring.ex =
+			malloc_dma ( sizeof(struct ring_desc_ex) * RXTX_RING_SIZE, 32 );
+		if ( ! priv->rx_ring.ex )
+			goto err_malloc;
+		priv->tx_ring.ex = &priv->rx_ring.ex[RX_RING_SIZE];
+	}
+
+	/* Initialize rings */
+	nv_init_tx_ring ( priv );
+	nv_init_rx_ring ( priv );
+
+	/* Allocate iobufs for RX */
+	nv_alloc_rx ( priv );
+
+	/* Give hw rings */
+	if ( ! nv_optimised ( priv ) ) {
+		writel ( cpu_to_le32 ( virt_to_bus ( priv->rx_ring.orig ) ),
+			 ioaddr + NvRegRxRingPhysAddr );
+		writel ( cpu_to_le32 ( virt_to_bus ( priv->tx_ring.orig ) ),
+			 ioaddr + NvRegTxRingPhysAddr );
+
+		DBG ( "RX ring at phys addr: %#08lx\n",
+			virt_to_bus ( priv->rx_ring.orig ) );
+		DBG ( "TX ring at phys addr: %#08lx\n",
+			virt_to_bus ( priv->tx_ring.orig ) );
+	} else {
+		writel ( cpu_to_le32 ( dma_low ( virt_to_bus ( priv->rx_ring.ex ) ) ),
+			 ioaddr + NvRegRxRingPhysAddr );
+		writel ( cpu_to_le32 ( dma_high ( virt_to_bus ( priv->rx_ring.ex ) ) ),
+			 ioaddr + NvRegRxRingPhysAddrHigh );
+		writel ( cpu_to_le32 ( dma_low ( virt_to_bus ( priv->tx_ring.ex ) ) ),
+			 ioaddr + NvRegTxRingPhysAddr );
+		writel ( cpu_to_le32 ( dma_high ( virt_to_bus ( priv->tx_ring.ex ) ) ),
+			 ioaddr + NvRegTxRingPhysAddrHigh );
+
+		DBG ( "RX ring at phys addr: %#08lx\n",
+			virt_to_bus ( priv->rx_ring.ex ) );
+		DBG ( "TX ring at phys addr: %#08lx\n",
+			virt_to_bus ( priv->tx_ring.ex ) );
+	}
+
+	writel ( ( ( RX_RING_SIZE - 1 ) << NVREG_RINGSZ_RXSHIFT ) +
+		 ( ( TX_RING_SIZE - 1 ) << NVREG_RINGSZ_TXSHIFT ),
+		 ioaddr + NvRegRingSizes );
+
+	return 0;
+
+err_malloc:
+	DBG ( "Could not allocate descriptor rings\n");
+	return rc;
+}
+
+static void
+nv_free_rxtx_resources ( struct forcedeth_private *priv )
+{
+	int i;
+
+	DBGP ( "nv_free_rxtx_resources\n" );
+
+	if ( ! nv_optimised ( priv ) ) {
+		free_dma ( priv->rx_ring.orig,
+			   sizeof(struct ring_desc) * RXTX_RING_SIZE );
+	} else {
+		free_dma ( priv->rx_ring.ex,
+			   sizeof(struct ring_desc_ex) * RXTX_RING_SIZE );
+	}
+
+	for ( i = 0; i < RX_RING_SIZE; i++ ) {
+		free_iob ( priv->rx_iobuf[i] );
+		priv->rx_iobuf[i] = NULL;
+	}
+}
+
+static void
+nv_txrx_reset ( struct forcedeth_private *priv )
+{
+	void *ioaddr = priv->mmio_addr;
+
+	writel ( NVREG_TXRXCTL_BIT2 | NVREG_TXRXCTL_RESET | priv->txrxctl_bits,
+		 ioaddr + NvRegTxRxControl );
+	pci_push ( ioaddr );
+	udelay ( NV_TXRX_RESET_DELAY );
+	writel ( NVREG_TXRXCTL_BIT2 | priv->txrxctl_bits,
+		 ioaddr + NvRegTxRxControl );
+	pci_push ( ioaddr );
+}
+
+/* Gear Backoff Seeds */
+#define BACKOFF_SEEDSET_ROWS	8
+#define BACKOFF_SEEDSET_LFSRS	15
+
+/* Known Good seed sets */
+static const u32 main_seedset[BACKOFF_SEEDSET_ROWS][BACKOFF_SEEDSET_LFSRS] = {
+    {145, 155, 165, 175, 185, 196, 235, 245, 255, 265, 275, 285, 660, 690, 874},
+    {245, 255, 265, 575, 385, 298, 335, 345, 355, 366, 375, 385, 761, 790, 974},
+    {145, 155, 165, 175, 185, 196, 235, 245, 255, 265, 275, 285, 660, 690, 874},
+    {245, 255, 265, 575, 385, 298, 335, 345, 355, 366, 375, 386, 761, 790, 974},
+    {266, 265, 276, 585, 397, 208, 345, 355, 365, 376, 385, 396, 771, 700, 984},
+    {266, 265, 276, 586, 397, 208, 346, 355, 365, 376, 285, 396, 771, 700, 984},
+    {366, 365, 376, 686, 497, 308, 447, 455, 466, 476, 485, 496, 871, 800,  84},
+    {466, 465, 476, 786, 597, 408, 547, 555, 566, 576, 585, 597, 971, 900, 184}};
+
+static const u32 gear_seedset[BACKOFF_SEEDSET_ROWS][BACKOFF_SEEDSET_LFSRS] = {
+    {251, 262, 273, 324, 319, 508, 375, 364, 341, 371, 398, 193, 375,  30, 295},
+    {351, 375, 373, 469, 551, 639, 477, 464, 441, 472, 498, 293, 476, 130, 395},
+    {351, 375, 373, 469, 551, 639, 477, 464, 441, 472, 498, 293, 476, 130, 397},
+    {251, 262, 273, 324, 319, 508, 375, 364, 341, 371, 398, 193, 375,  30, 295},
+    {251, 262, 273, 324, 319, 508, 375, 364, 341, 371, 398, 193, 375,  30, 295},
+    {351, 375, 373, 469, 551, 639, 477, 464, 441, 472, 498, 293, 476, 130, 395},
+    {351, 375, 373, 469, 551, 639, 477, 464, 441, 472, 498, 293, 476, 130, 395},
+    {351, 375, 373, 469, 551, 639, 477, 464, 441, 472, 498, 293, 476, 130, 395}};
+
+static void nv_gear_backoff_reseed ( struct forcedeth_private *priv )
+{
+	void *ioaddr = priv->mmio_addr;
+	u32 miniseed1, miniseed2, miniseed2_reversed, miniseed3, miniseed3_reversed;
+	u32 temp, seedset, combinedSeed;
+	int i;
+
+	/* Setup seed for free running LFSR */
+	/* We are going to read the time stamp counter 3 times
+	   and swizzle bits around to increase randomness */
+	get_random_bytes ( &miniseed1, sizeof(miniseed1) );
+	miniseed1 &= 0x0fff;
+	if (miniseed1 == 0)
+		miniseed1 = 0xabc;
+
+	get_random_bytes( &miniseed2, sizeof(miniseed2) );
+	miniseed2 &= 0x0fff;
+	if ( miniseed2 == 0 )
+		miniseed2 = 0xabc;
+	miniseed2_reversed =
+		( (miniseed2 & 0xF00) >> 8 ) |
+		  (miniseed2 & 0x0F0) |
+		  ((miniseed2 & 0x00F) << 8 );
+
+	get_random_bytes( &miniseed3, sizeof(miniseed3) );
+	miniseed3 &= 0x0fff;
+	if ( miniseed3 == 0 )
+		miniseed3 = 0xabc;
+	miniseed3_reversed =
+		( (miniseed3 & 0xF00) >> 8 ) |
+		  (miniseed3 & 0x0F0) |
+		  ((miniseed3 & 0x00F) << 8 );
+
+	combinedSeed = ( (miniseed1 ^ miniseed2_reversed ) << 12 ) |
+		        (miniseed2 ^ miniseed3_reversed );
+
+	/* Seeds can not be zero */
+	if ( ( combinedSeed & NVREG_BKOFFCTRL_SEED_MASK ) == 0 )
+		combinedSeed |= 0x08;
+	if ( ( combinedSeed & ( NVREG_BKOFFCTRL_SEED_MASK << NVREG_BKOFFCTRL_GEAR ) ) == 0 )
+		combinedSeed |= 0x8000;
+
+	/* No need to disable tx here */
+	temp = NVREG_BKOFFCTRL_DEFAULT | ( 0 << NVREG_BKOFFCTRL_SELECT );
+	temp |= combinedSeed & NVREG_BKOFFCTRL_SEED_MASK;
+	temp |= combinedSeed >> NVREG_BKOFFCTRL_GEAR;
+	writel ( temp, ioaddr + NvRegBackOffControl );
+
+    	/* Setup seeds for all gear LFSRs. */
+	get_random_bytes ( &seedset, sizeof(seedset) );
+	seedset = seedset % BACKOFF_SEEDSET_ROWS;
+	for ( i = 1; i <= BACKOFF_SEEDSET_LFSRS; i++ )
+	{
+		temp = NVREG_BKOFFCTRL_DEFAULT | ( i << NVREG_BKOFFCTRL_SELECT );
+		temp |= main_seedset[seedset][i-1] & 0x3ff;
+		temp |= ( ( gear_seedset[seedset][i-1] & 0x3ff ) << NVREG_BKOFFCTRL_GEAR );
+		writel ( temp, ioaddr + NvRegBackOffControl );
+	}
+}
+
+static void
+nv_disable_hw_interrupts ( struct forcedeth_private *priv )
+{
+	void *ioaddr = priv->mmio_addr;
+
+	writel ( 0, ioaddr + NvRegIrqMask );
+}
+
+static void
+nv_enable_hw_interrupts ( struct forcedeth_private *priv )
+{
+	void *ioaddr = priv->mmio_addr;
+
+	writel ( NVREG_IRQMASK_THROUGHPUT, ioaddr + NvRegIrqMask );
+}
+
+static void
+nv_start_rx ( struct forcedeth_private *priv )
+{
+	void *ioaddr = priv->mmio_addr;
+	u32 rx_ctrl = readl ( ioaddr + NvRegReceiverControl );
+
+	DBGP ( "nv_start_rx\n" );
+	/* Already running? Stop it. */
+	if ( ( readl ( ioaddr + NvRegReceiverControl ) & NVREG_RCVCTL_START ) && !priv->mac_in_use ) {
+		rx_ctrl &= ~NVREG_RCVCTL_START;
+		writel ( rx_ctrl, ioaddr + NvRegReceiverControl );
+		pci_push ( ioaddr );
+	}
+	writel ( priv->linkspeed, ioaddr + NvRegLinkSpeed );
+	pci_push ( ioaddr );
+        rx_ctrl |= NVREG_RCVCTL_START;
+        if ( priv->mac_in_use )
+		rx_ctrl &= ~NVREG_RCVCTL_RX_PATH_EN;
+	writel ( rx_ctrl, ioaddr + NvRegReceiverControl );
+	DBG ( "nv_start_rx to duplex %d, speed 0x%08x.\n",
+		priv->duplex, priv->linkspeed);
+	pci_push ( ioaddr );
+}
+
+static void
+nv_stop_rx ( struct forcedeth_private *priv )
+{
+	void *ioaddr = priv->mmio_addr;
+	u32 rx_ctrl = readl ( ioaddr + NvRegReceiverControl );
+
+	DBGP ( "nv_stop_rx\n" );
+	if ( ! priv->mac_in_use )
+		rx_ctrl &= ~NVREG_RCVCTL_START;
+	else
+		rx_ctrl |= NVREG_RCVCTL_RX_PATH_EN;
+	writel ( rx_ctrl, ioaddr + NvRegReceiverControl );
+	reg_delay ( priv, NvRegReceiverStatus, NVREG_RCVSTAT_BUSY, 0,
+			NV_RXSTOP_DELAY1, NV_RXSTOP_DELAY1MAX,
+			"nv_stop_rx: ReceiverStatus remained busy");
+
+	udelay ( NV_RXSTOP_DELAY2 );
+	if ( ! priv->mac_in_use )
+		writel ( 0, priv + NvRegLinkSpeed );
+}
+
+static void
+nv_start_tx ( struct forcedeth_private *priv )
+{
+	void *ioaddr = priv->mmio_addr;
+	u32 tx_ctrl = readl ( ioaddr + NvRegTransmitterControl );
+
+	DBGP ( "nv_start_tx\n" );
+	tx_ctrl |= NVREG_XMITCTL_START;
+	if ( priv->mac_in_use )
+		tx_ctrl &= ~NVREG_XMITCTL_TX_PATH_EN;
+	writel ( tx_ctrl, ioaddr + NvRegTransmitterControl );
+	pci_push ( ioaddr );
+}
+
+static void
+nv_stop_tx ( struct forcedeth_private *priv )
+{
+	void *ioaddr = priv->mmio_addr;
+	u32 tx_ctrl = readl ( ioaddr + NvRegTransmitterControl );
+
+	DBGP ( "nv_stop_tx");
+
+	if ( ! priv->mac_in_use )
+		tx_ctrl &= ~NVREG_XMITCTL_START;
+	else
+		tx_ctrl |= NVREG_XMITCTL_TX_PATH_EN;
+	writel ( tx_ctrl, ioaddr + NvRegTransmitterControl );
+	reg_delay ( priv, NvRegTransmitterStatus, NVREG_XMITSTAT_BUSY, 0,
+			NV_TXSTOP_DELAY1, NV_TXSTOP_DELAY1MAX,
+			"nv_stop_tx: TransmitterStatus remained busy");
+
+	udelay ( NV_TXSTOP_DELAY2 );
+	if ( ! priv->mac_in_use )
+		writel( readl ( ioaddr + NvRegTransmitPoll) &
+				NVREG_TRANSMITPOLL_MAC_ADDR_REV,
+			ioaddr + NvRegTransmitPoll);
+}
+
+
+static void
+nv_update_pause ( struct forcedeth_private *priv, u32 pause_flags )
+{
+	void *ioaddr = priv->mmio_addr;
+
+	priv->pause_flags &= ~ ( NV_PAUSEFRAME_TX_ENABLE |
+				 NV_PAUSEFRAME_RX_ENABLE );
+
+	if ( priv->pause_flags & NV_PAUSEFRAME_RX_CAPABLE ) {
+		u32 pff = readl ( ioaddr + NvRegPacketFilterFlags ) & ~NVREG_PFF_PAUSE_RX;
+		if ( pause_flags & NV_PAUSEFRAME_RX_ENABLE ) {
+			writel ( pff | NVREG_PFF_PAUSE_RX, ioaddr + NvRegPacketFilterFlags );
+			priv->pause_flags |= NV_PAUSEFRAME_RX_ENABLE;
+		} else {
+			writel ( pff, ioaddr + NvRegPacketFilterFlags );
+		}
+	}
+	if ( priv->pause_flags & NV_PAUSEFRAME_TX_CAPABLE ) {
+		u32 regmisc = readl ( ioaddr + NvRegMisc1 ) & ~NVREG_MISC1_PAUSE_TX;
+		if ( pause_flags & NV_PAUSEFRAME_TX_ENABLE ) {
+			u32 pause_enable = NVREG_TX_PAUSEFRAME_ENABLE_V1;
+			if ( priv->driver_data & DEV_HAS_PAUSEFRAME_TX_V2 )
+				pause_enable = NVREG_TX_PAUSEFRAME_ENABLE_V2;
+			if ( priv->driver_data & DEV_HAS_PAUSEFRAME_TX_V3 ) {
+				pause_enable = NVREG_TX_PAUSEFRAME_ENABLE_V3;
+				/* limit the number of tx pause frames to a default of 8 */
+				writel ( readl ( ioaddr + NvRegTxPauseFrameLimit ) |
+						NVREG_TX_PAUSEFRAMELIMIT_ENABLE,
+					 ioaddr + NvRegTxPauseFrameLimit );
+			}
+			writel ( pause_enable, ioaddr + NvRegTxPauseFrame );
+			writel ( regmisc | NVREG_MISC1_PAUSE_TX, ioaddr + NvRegMisc1 );
+			priv->pause_flags |= NV_PAUSEFRAME_TX_ENABLE;
+		} else {
+			writel ( NVREG_TX_PAUSEFRAME_DISABLE, ioaddr + NvRegTxPauseFrame );
+			writel ( regmisc, ioaddr + NvRegMisc1 );
+		}
+	}
+}
+
+static int
+nv_update_linkspeed ( struct forcedeth_private *priv )
+{
+	void *ioaddr = priv->mmio_addr;
+	int adv = 0;
+	int lpa = 0;
+	int adv_lpa, adv_pause, lpa_pause;
+	u32 newls = priv->linkspeed;
+	int newdup = priv->duplex;
+	int mii_status;
+	int retval = 0;
+	u32 control_1000, status_1000, phyreg, pause_flags, txreg;
+	u32 txrxFlags = 0;
+	u32 phy_exp;
+
+	/* BMSR_LSTATUS is latched, read it twice:
+	 * we want the current value.
+	 */
+	mii_rw ( priv, priv->phyaddr, MII_BMSR, MII_READ );
+	mii_status = mii_rw ( priv, priv->phyaddr, MII_BMSR, MII_READ );
+
+	if ( ! ( mii_status & BMSR_LSTATUS ) ) {
+		DBG ( "No link detected by phy - falling back to 10HD.\n" );
+		newls = NVREG_LINKSPEED_FORCE | NVREG_LINKSPEED_10;
+		newdup = 0;
+		retval = 0;
+		goto set_speed;
+	}
+
+	/* check auto negotiation is complete */
+	if ( ! ( mii_status & BMSR_ANEGCOMPLETE ) ) {
+		/* still in autonegotiation - configure nic for 10 MBit HD and wait. */
+		newls = NVREG_LINKSPEED_FORCE | NVREG_LINKSPEED_10;
+		newdup = 0;
+		retval = 0;
+		DBG ( "autoneg not completed - falling back to 10HD.\n" );
+		goto set_speed;
+	}
+
+	adv = mii_rw ( priv, priv->phyaddr, MII_ADVERTISE, MII_READ );
+	lpa = mii_rw ( priv, priv->phyaddr, MII_LPA, MII_READ );
+	DBG ( "nv_update_linkspeed: PHY advertises 0x%04x, lpa 0x%04x.\n", adv, lpa );
+
+	retval = 1;
+	if ( priv->gigabit == PHY_GIGABIT ) {
+		control_1000 = mii_rw ( priv, priv->phyaddr, MII_CTRL1000, MII_READ);
+		status_1000 = mii_rw ( priv, priv->phyaddr, MII_STAT1000, MII_READ);
+
+		if ( ( control_1000 & ADVERTISE_1000FULL ) &&
+			( status_1000 & LPA_1000FULL ) ) {
+			DBG ( "nv_update_linkspeed: GBit ethernet detected.\n" );
+			newls = NVREG_LINKSPEED_FORCE | NVREG_LINKSPEED_1000;
+			newdup = 1;
+			goto set_speed;
+		}
+	}
+
+	/* FIXME: handle parallel detection properly */
+	adv_lpa = lpa & adv;
+	if ( adv_lpa & LPA_100FULL ) {
+		newls = NVREG_LINKSPEED_FORCE | NVREG_LINKSPEED_100;
+		newdup = 1;
+	} else if ( adv_lpa & LPA_100HALF ) {
+		newls = NVREG_LINKSPEED_FORCE | NVREG_LINKSPEED_100;
+		newdup = 0;
+	} else if ( adv_lpa & LPA_10FULL ) {
+		newls = NVREG_LINKSPEED_FORCE | NVREG_LINKSPEED_10;
+		newdup = 1;
+	} else if ( adv_lpa & LPA_10HALF ) {
+		newls = NVREG_LINKSPEED_FORCE | NVREG_LINKSPEED_10;
+		newdup = 0;
+	} else {
+		DBG ( "bad ability %04x - falling back to 10HD.\n", adv_lpa);
+		newls = NVREG_LINKSPEED_FORCE | NVREG_LINKSPEED_10;
+		newdup = 0;
+	}
+
+set_speed:
+	if ( priv->duplex == newdup && priv->linkspeed == newls )
+		return retval;
+
+	DBG ( "changing link setting from %d/%d to %d/%d.\n",
+		priv->linkspeed, priv->duplex, newls, newdup);
+
+	priv->duplex = newdup;
+	priv->linkspeed = newls;
+
+	/* The transmitter and receiver must be restarted for safe update */
+	if ( readl ( ioaddr + NvRegTransmitterControl ) & NVREG_XMITCTL_START ) {
+		txrxFlags |= NV_RESTART_TX;
+		nv_stop_tx ( priv );
+	}
+	if ( readl ( ioaddr + NvRegReceiverControl ) & NVREG_RCVCTL_START) {
+		txrxFlags |= NV_RESTART_RX;
+		nv_stop_rx ( priv );
+	}
+
+	if ( priv->gigabit == PHY_GIGABIT ) {
+		phyreg = readl ( ioaddr + NvRegSlotTime );
+		phyreg &= ~(0x3FF00);
+		if ( ( ( priv->linkspeed & 0xFFF ) == NVREG_LINKSPEED_10 ) ||
+		     ( ( priv->linkspeed & 0xFFF ) == NVREG_LINKSPEED_100) )
+			phyreg |= NVREG_SLOTTIME_10_100_FULL;
+		else if ( ( priv->linkspeed & 0xFFF ) == NVREG_LINKSPEED_1000 )
+			phyreg |= NVREG_SLOTTIME_1000_FULL;
+		writel( phyreg, priv + NvRegSlotTime );
+	}
+
+	phyreg = readl ( ioaddr + NvRegPhyInterface );
+	phyreg &= ~( PHY_HALF | PHY_100 | PHY_1000 );
+	if ( priv->duplex == 0 )
+		phyreg |= PHY_HALF;
+	if ( ( priv->linkspeed & NVREG_LINKSPEED_MASK ) == NVREG_LINKSPEED_100 )
+		phyreg |= PHY_100;
+	else if ( ( priv->linkspeed & NVREG_LINKSPEED_MASK ) == NVREG_LINKSPEED_1000 )
+		phyreg |= PHY_1000;
+	writel ( phyreg, ioaddr + NvRegPhyInterface );
+
+	phy_exp = mii_rw ( priv, priv->phyaddr, MII_EXPANSION, MII_READ ) & EXPANSION_NWAY; /* autoneg capable */
+	if ( phyreg & PHY_RGMII ) {
+		if ( ( priv->linkspeed & NVREG_LINKSPEED_MASK ) == NVREG_LINKSPEED_1000 ) {
+			txreg = NVREG_TX_DEFERRAL_RGMII_1000;
+		} else {
+			if ( !phy_exp && !priv->duplex && ( priv->driver_data & DEV_HAS_COLLISION_FIX ) ) {
+				if ( ( priv->linkspeed & NVREG_LINKSPEED_MASK ) == NVREG_LINKSPEED_10 )
+					txreg = NVREG_TX_DEFERRAL_RGMII_STRETCH_10;
+				else
+					txreg = NVREG_TX_DEFERRAL_RGMII_STRETCH_100;
+			} else {
+				txreg = NVREG_TX_DEFERRAL_RGMII_10_100;
+			}
+		}
+	} else {
+		if ( !phy_exp && !priv->duplex && ( priv->driver_data & DEV_HAS_COLLISION_FIX ) )
+			txreg = NVREG_TX_DEFERRAL_MII_STRETCH;
+		else
+			txreg = NVREG_TX_DEFERRAL_DEFAULT;
+	}
+	writel ( txreg, ioaddr + NvRegTxDeferral );
+
+	if ( priv->desc_ver == DESC_VER_1 ) {
+		txreg = NVREG_TX_WM_DESC1_DEFAULT;
+	} else {
+		if ( ( priv->linkspeed & NVREG_LINKSPEED_MASK ) == NVREG_LINKSPEED_1000 )
+			txreg = NVREG_TX_WM_DESC2_3_1000;
+		else
+			txreg = NVREG_TX_WM_DESC2_3_DEFAULT;
+	}
+	writel ( txreg, ioaddr + NvRegTxWatermark );
+
+	writel ( NVREG_MISC1_FORCE | ( priv->duplex ? 0 : NVREG_MISC1_HD ), ioaddr + NvRegMisc1 );
+	pci_push ( ioaddr );
+	writel ( priv->linkspeed, priv + NvRegLinkSpeed);
+	pci_push ( ioaddr );
+
+	pause_flags = 0;
+	/* setup pause frame */
+	if ( priv->duplex != 0 ) {
+		if ( priv->pause_flags & NV_PAUSEFRAME_AUTONEG ) {
+			adv_pause = adv & ( ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM );
+			lpa_pause = lpa & ( LPA_PAUSE_CAP | LPA_PAUSE_ASYM );
+
+			switch ( adv_pause ) {
+			case ADVERTISE_PAUSE_CAP:
+				if ( lpa_pause & LPA_PAUSE_CAP ) {
+					pause_flags |= NV_PAUSEFRAME_RX_ENABLE;
+					if ( priv->pause_flags & NV_PAUSEFRAME_TX_REQ )
+						pause_flags |= NV_PAUSEFRAME_TX_ENABLE;
+				}
+				break;
+			case ADVERTISE_PAUSE_ASYM:
+				if ( lpa_pause == ( LPA_PAUSE_CAP | LPA_PAUSE_ASYM ) )
+				{
+					pause_flags |= NV_PAUSEFRAME_TX_ENABLE;
+				}
+				break;
+			case ADVERTISE_PAUSE_CAP| ADVERTISE_PAUSE_ASYM:
+				if ( lpa_pause & LPA_PAUSE_CAP )
+				{
+					pause_flags |=  NV_PAUSEFRAME_RX_ENABLE;
+					if ( priv->pause_flags & NV_PAUSEFRAME_TX_REQ )
+						pause_flags |= NV_PAUSEFRAME_TX_ENABLE;
+				}
+				if ( lpa_pause == LPA_PAUSE_ASYM )
+				{
+					pause_flags |= NV_PAUSEFRAME_RX_ENABLE;
+				}
+				break;
+			}
+		} else {
+			pause_flags = priv->pause_flags;
+		}
+	}
+	nv_update_pause ( priv, pause_flags );
+
+	if ( txrxFlags & NV_RESTART_TX )
+		nv_start_tx ( priv );
+	if ( txrxFlags & NV_RESTART_RX )
+		nv_start_rx ( priv );
+
+	return retval;
+}
+
+
+/**
+ * open - Called when a network interface is made active
+ *
+ * @v netdev	Network device
+ * @ret rc	Return status code, 0 on success, negative value on failure
+ **/
+static int
+forcedeth_open ( struct net_device *netdev )
+{
+	struct forcedeth_private *priv = netdev_priv ( netdev );
+	void *ioaddr = priv->mmio_addr;
+	int i, ret = 1;
+	int rc;
+	u32 low;
+
+	DBGP ( "forcedeth_open\n" );
+
+	/* Power up phy */
+	mii_rw ( priv, priv->phyaddr, MII_BMCR,
+		 mii_rw ( priv, priv->phyaddr, MII_BMCR, MII_READ ) & ~BMCR_PDOWN );
+
+	nv_txrx_gate ( priv, 0 );
+
+	/* Erase previous misconfiguration */
+	if ( priv->driver_data & DEV_HAS_POWER_CNTRL )
+		nv_mac_reset ( priv );
+
+	/* Clear multicast masks and addresses, enter promiscuous mode */
+	writel ( 0, ioaddr + NvRegMulticastAddrA );
+	writel ( 0, ioaddr + NvRegMulticastAddrB );
+	writel ( NVREG_MCASTMASKA_NONE, ioaddr + NvRegMulticastMaskA );
+	writel ( NVREG_MCASTMASKB_NONE, ioaddr + NvRegMulticastMaskB );
+	writel ( NVREG_PFF_PROMISC, ioaddr + NvRegPacketFilterFlags );
+
+	writel ( 0, ioaddr + NvRegTransmitterControl );
+	writel ( 0, ioaddr + NvRegReceiverControl );
+
+	writel ( 0, ioaddr + NvRegAdapterControl );
+
+	writel ( 0, ioaddr + NvRegLinkSpeed );
+	writel ( readl ( ioaddr + NvRegTransmitPoll ) & NVREG_TRANSMITPOLL_MAC_ADDR_REV,
+		 ioaddr + NvRegTransmitPoll );
+	nv_txrx_reset ( priv );
+	writel ( 0, ioaddr + NvRegUnknownSetupReg6 );
+
+	/* Initialize descriptor rings */
+	if ( ( rc = nv_init_rings ( priv ) ) != 0 )
+		goto err_init_rings;
+
+	writel ( priv->linkspeed, ioaddr + NvRegLinkSpeed );
+	if ( priv->desc_ver == DESC_VER_1 )
+		writel ( NVREG_TX_WM_DESC1_DEFAULT, ioaddr + NvRegTxWatermark );
+	else
+		writel ( NVREG_TX_WM_DESC2_3_DEFAULT, ioaddr + NvRegTxWatermark );
+	writel ( priv->txrxctl_bits , ioaddr + NvRegTxRxControl );
+	writel ( 0 , ioaddr + NvRegVlanControl );
+	pci_push ( ioaddr );
+	writel ( NVREG_TXRXCTL_BIT1 | priv->txrxctl_bits ,
+		 ioaddr + NvRegTxRxControl );
+	reg_delay ( priv, NvRegUnknownSetupReg5, NVREG_UNKSETUP5_BIT31,
+		    NVREG_UNKSETUP5_BIT31, NV_SETUP5_DELAY, NV_SETUP5_DELAYMAX,
+		    "open: SetupReg5, Bit 31 remained off\n" );
+
+	writel ( 0, ioaddr + NvRegMIIMask );
+	writel ( NVREG_IRQSTAT_MASK, ioaddr + NvRegIrqStatus );
+	writel ( NVREG_MIISTAT_MASK_ALL, ioaddr + NvRegMIIStatus );
+
+	writel ( NVREG_MISC1_FORCE | NVREG_MISC1_HD, ioaddr + NvRegMisc1 );
+	writel ( readl ( ioaddr + NvRegTransmitterStatus ),
+		 ioaddr + NvRegTransmitterStatus );
+	writel ( RX_BUF_SZ, ioaddr + NvRegOffloadConfig );
+
+	writel ( readl ( ioaddr + NvRegReceiverStatus),
+		 ioaddr + NvRegReceiverStatus );
+
+	/* Set up slot time */
+	get_random_bytes ( &low, sizeof(low) );
+	low &= NVREG_SLOTTIME_MASK;
+	if ( priv->desc_ver == DESC_VER_1 ) {
+		writel ( low | NVREG_SLOTTIME_DEFAULT, ioaddr + NvRegSlotTime );
+	} else {
+		if ( ! ( priv->driver_data & DEV_HAS_GEAR_MODE ) ) {
+			writel ( NVREG_SLOTTIME_LEGBF_ENABLED | NVREG_SLOTTIME_10_100_FULL | low,
+				 ioaddr + NvRegSlotTime );
+		} else {
+			writel ( NVREG_SLOTTIME_10_100_FULL, ioaddr + NvRegSlotTime );
+			nv_gear_backoff_reseed ( priv );
+		}
+	}
+
+	writel ( NVREG_TX_DEFERRAL_DEFAULT , ioaddr + NvRegTxDeferral );
+	writel ( NVREG_RX_DEFERRAL_DEFAULT , ioaddr + NvRegRxDeferral );
+
+	writel ( NVREG_POLL_DEFAULT_THROUGHPUT, ioaddr + NvRegPollingInterval );
+	
+	writel ( NVREG_UNKSETUP6_VAL, ioaddr + NvRegUnknownSetupReg6 );
+	writel ( ( priv->phyaddr << NVREG_ADAPTCTL_PHYSHIFT ) |
+		 NVREG_ADAPTCTL_PHYVALID | NVREG_ADAPTCTL_RUNNING,
+		 ioaddr + NvRegAdapterControl );
+	writel ( NVREG_MIISPEED_BIT8 | NVREG_MIIDELAY, ioaddr + NvRegMIISpeed );
+	writel ( NVREG_MII_LINKCHANGE, ioaddr + NvRegMIIMask );
+
+	i = readl ( ioaddr + NvRegPowerState );
+	if ( ( i & NVREG_POWERSTATE_POWEREDUP ) == 0 )
+		writel ( NVREG_POWERSTATE_POWEREDUP | i, ioaddr + NvRegPowerState );
+
+	pci_push ( ioaddr );
+	udelay ( 10 );
+	writel ( readl ( ioaddr + NvRegPowerState ) | NVREG_POWERSTATE_VALID,
+		 ioaddr + NvRegPowerState );
+
+	nv_disable_hw_interrupts ( priv );
+	pci_push ( ioaddr );
+	writel ( NVREG_MIISTAT_MASK_ALL, ioaddr + NvRegMIIStatus );
+	writel ( NVREG_IRQSTAT_MASK, ioaddr + NvRegIrqStatus );
+	pci_push ( ioaddr );
+
+	readl ( ioaddr + NvRegMIIStatus );
+	writel ( NVREG_MIISTAT_MASK_ALL, ioaddr + NvRegMIIStatus );
+	priv->linkspeed = 0;
+	ret = nv_update_linkspeed ( priv );
+	nv_start_rx ( priv );
+	nv_start_tx ( priv );
+
+	return 0;
+
+err_init_rings:
+	return rc;
+}
+
+/**
+ * transmit - Transmit a packet
+ *
+ * @v netdev	Network device
+ * @v iobuf	I/O buffer
+ *
+ * @ret rc	Returns 0 on success, negative on failure
+ */
+static int
+forcedeth_transmit ( struct net_device *netdev, struct io_buffer *iobuf )
+{
+	struct forcedeth_private *priv = netdev_priv ( netdev );
+	void *ioaddr = priv->mmio_addr;
+	union ring_type tx_curr_desc;
+	u32 tx_flags = priv->tx_flags;
+	u32 tx_flags_extra;
+	u32 size = iob_len ( iobuf );
+
+	DBGP ( "forcedeth_transmit\n" );
+
+	/* NOTE: Some NICs have a hw bug that causes them to malfunction
+	 * when there are more than 16 outstanding TXs. Increasing the TX
+	 * ring size might trigger this bug */
+	if ( priv->tx_fill_ctr == TX_RING_SIZE ) {
+		DBG ( "Tx overflow\n" );
+		return -ENOBUFS;
+	}
+
+	/* Pad small packets to minimum length */
+	iob_pad ( iobuf, ETH_ZLEN );
+
+	priv->tx_iobuf[priv->tx_curr] = iobuf;
+
+	/* Since we don't do fragmentation offloading, we always have
+	 * the last packet bit set */
+	tx_flags_extra = priv->desc_ver == DESC_VER_1 ?
+			 NV_TX_LASTPACKET : NV_TX2_LASTPACKET;
+
+	/* Configure current descriptor to transmit packet
+	 * ( tx_flags sets the ownership bit ) */
+	if ( ! nv_optimised ( priv ) ) {
+		tx_curr_desc.orig = priv->tx_ring.orig + priv->tx_curr;
+
+		tx_curr_desc.orig->buf =
+			cpu_to_le32 ( virt_to_bus ( iobuf->data ) );
+		wmb();
+		tx_curr_desc.orig->flaglen =
+			cpu_to_le32 ( ( size -1 ) | tx_flags | tx_flags_extra );
+	} else {
+		tx_curr_desc.ex = priv->tx_ring.ex + priv->tx_curr;
+
+		tx_curr_desc.ex->bufhigh =
+			cpu_to_le32 ( dma_high ( ( virt_to_bus ( iobuf->data ) ) ) );
+		tx_curr_desc.ex->buflow =
+			cpu_to_le32 ( dma_low ( ( virt_to_bus ( iobuf->data ) ) ) );
+		tx_curr_desc.ex->txvlan = 0;
+		wmb();
+		tx_curr_desc.ex->flaglen =
+			cpu_to_le32 ( (size - 1 ) | tx_flags | tx_flags_extra );
+	}
+
+	DBG ( "forcedeth_transmit: flaglen = %#04x\n",
+		(size -1) | tx_flags | tx_flags_extra );
+	DBG ( "forcedeth_transmit: tx_fill_ctr = %d\n",
+		priv->tx_fill_ctr );
+
+	writel ( NVREG_TXRXCTL_KICK | priv->txrxctl_bits,
+		 ioaddr + NvRegTxRxControl );
+	pci_push ( ioaddr );
+
+	/* Point to the next free descriptor */
+	priv->tx_curr = ( priv->tx_curr + 1 ) % TX_RING_SIZE;
+
+	/* Increment number of descriptors in use */
+	priv->tx_fill_ctr++;
+
+	return 0;
+}
+
+/**
+ * nv_process_tx_packets - Checks for successfully sent packets,
+ * reports them to gPXE with netdev_tx_complete()
+ *
+ * @v netdev    Network device
+ */
+static void
+nv_process_tx_packets ( struct net_device *netdev )
+{
+	struct forcedeth_private *priv = netdev_priv ( netdev );
+	union ring_type tx_curr_desc;
+	u32 flaglen;
+
+	DBGP ( "nv_process_tx_packets\n" );
+
+	while ( priv->tx_tail != priv->tx_curr ) {
+
+		if ( ! nv_optimised ( priv ) ) {
+			tx_curr_desc.orig = priv->tx_ring.orig + priv->tx_tail;
+			flaglen = le32_to_cpu ( tx_curr_desc.orig->flaglen );
+			rmb();
+		} else {
+			tx_curr_desc.ex = priv->tx_ring.ex + priv->tx_tail;
+			flaglen = le32_to_cpu ( tx_curr_desc.ex->flaglen );
+			rmb();
+		}
+
+
+		/* Skip this descriptor if hardware still owns it */
+		if ( flaglen & NV_TX_VALID )
+			break;
+
+		DBG ( "Transmitted packet.\n" );
+		DBG ( "priv->tx_fill_ctr= %d\n", priv->tx_fill_ctr );
+		DBG ( "priv->tx_tail	= %d\n", priv->tx_tail );
+		DBG ( "priv->tx_curr	= %d\n", priv->tx_curr );
+		DBG ( "flaglen		= %#04x\n", flaglen );
+
+		/* This packet is ready for completion */
+		netdev_tx_complete ( netdev, priv->tx_iobuf[priv->tx_tail] );
+
+		/* Clear the descriptor */
+		if ( ! nv_optimised ( priv ) )
+			memset ( tx_curr_desc.orig, 0, sizeof(*tx_curr_desc.orig) );
+		else
+			memset ( tx_curr_desc.ex, 0, sizeof(*tx_curr_desc.ex) );
+
+		/* Reduce the number of tx descriptors in use */
+		priv->tx_fill_ctr--;
+
+		/* Go to next available descriptor */
+		priv->tx_tail = ( priv->tx_tail + 1 ) % TX_RING_SIZE;
+	}
+}
+
+/**
+ * nv_process_rx_packets - Checks for received packets, reports them
+ * to gPXE with netdev_rx() or netdev_rx_err() if there was an error receiving
+ * the packet
+ *
+ * @v netdev    Network device
+ */
+static void
+nv_process_rx_packets ( struct net_device *netdev )
+{
+	struct forcedeth_private *priv = netdev_priv ( netdev );
+	struct io_buffer *curr_iob;
+	union ring_type rx_curr_desc;
+	u32 flags, len;
+	int i;
+
+	DBGP ( "nv_process_rx_packets\n" );
+
+	for ( i = 0; i < RX_RING_SIZE; i++ ) {
+
+		if ( ! nv_optimised ( priv ) ) {
+			rx_curr_desc.orig = priv->rx_ring.orig + priv->rx_curr;
+			flags = le32_to_cpu ( rx_curr_desc.orig->flaglen );
+			rmb();
+		} else {
+			rx_curr_desc.ex = priv->rx_ring.ex + priv->rx_curr;
+			flags = le32_to_cpu ( rx_curr_desc.ex->flaglen );
+			rmb();
+		}
+
+		/* Skip this descriptor if hardware still owns it */
+		if ( flags & NV_RX_AVAIL )
+			break;
+
+		/* We own the descriptor, but it has not been refilled yet */
+		curr_iob = priv->rx_iobuf[priv->rx_curr];
+		DBG ( "%p %p\n", curr_iob, priv->rx_iobuf[priv->rx_curr] );
+		if ( curr_iob == NULL )
+			break;
+
+		DBG ( "Received packet.\n" );
+		DBG ( "priv->rx_curr	= %d\n", priv->rx_curr );
+		DBG ( "flags		= %#04x\n", flags );
+
+		/* Check for errors */
+		if ( ( priv->desc_ver == DESC_VER_1 ) &&
+		     ( flags & NV_RX_DESCRIPTORVALID ) &&
+		     ( flags & NV_RX_ERROR ) ) {
+				netdev_rx_err ( netdev, curr_iob, -EINVAL );
+				DBG ( " Corrupted packet received!\n" );
+		} else if ( ( flags & NV_RX2_DESCRIPTORVALID ) &&
+			    ( flags & NV_RX2_ERROR ) ) {
+				netdev_rx_err ( netdev, curr_iob, -EINVAL );
+				DBG ( " Corrupted packet received!\n" );
+		} else {
+			if ( ! nv_optimised ( priv ) ) {
+				len = nv_get_desc_len ( rx_curr_desc.orig,
+							priv->desc_ver );
+			} else {
+				len = nv_get_desc_len_ex ( rx_curr_desc.ex,
+							   priv->desc_ver );
+			}
+
+			/* Filter any frames that have as destination address a
+			 * local MAC address but are not meant for this NIC */
+			if ( is_local_ether_addr ( curr_iob->data ) &&
+			     memcmp ( curr_iob->data, netdev->hw_addr, ETH_ALEN ) ) {
+				free_iob ( curr_iob );
+			} else {
+				iob_put ( curr_iob, len );
+				netdev_rx ( netdev, curr_iob );
+			}
+		}
+
+		/* Invalidate iobuf */
+		priv->rx_iobuf[priv->rx_curr] = NULL;
+
+		/* Invalidate descriptor */
+		if ( ! nv_optimised ( priv ) )
+			memset ( rx_curr_desc.orig, 0, sizeof(*rx_curr_desc.orig) );
+		else
+			memset ( rx_curr_desc.ex, 0, sizeof(*rx_curr_desc.ex) );
+
+		/* Point to the next free descriptor */
+		priv->rx_curr = ( priv->rx_curr + 1 ) % RX_RING_SIZE;
+	}
+
+	nv_alloc_rx ( priv );
+}
+
+/**
+ * poll - Poll for received packets
+ *
+ * @v netdev	Network device
+ */
+static void
+forcedeth_poll ( struct net_device *netdev )
+{
+	struct forcedeth_private *priv = netdev_priv ( netdev );
+	void *ioaddr = priv->mmio_addr;
+	u32 status, mii_status;
+
+	DBGP ( "forcedeth_poll\n" );
+
+	/* Periodically check link state */
+	if ( priv->link_poll_timer-- == 0 ) {
+		mii_rw ( priv, priv->phyaddr, MII_BMSR, MII_READ );
+		mii_status = mii_rw ( priv, priv->phyaddr, MII_BMSR, MII_READ );
+
+		if ( mii_status & BMSR_LSTATUS ) {
+			DBG ( "Link is up\n" );
+			netdev_link_up ( netdev );
+		} else {
+			DBG ( "Link is down\n" );
+			netdev_link_down ( netdev );
+		}
+
+		priv->link_poll_timer = NV_LINK_POLL_FREQUENCY;
+	}
+
+	status = readl ( ioaddr + NvRegIrqStatus ) & NVREG_IRQSTAT_MASK;
+
+	/* Clear interrupts */
+	writel ( NVREG_IRQSTAT_MASK, ioaddr + NvRegIrqStatus );
+
+	DBG ( "forcedeth_poll: status = %#04x\n", status );
+
+	/* Return when no interrupts have been triggered */
+	if ( ! status )
+		return;
+
+	/* Process transmitted packets */
+	nv_process_tx_packets ( netdev );
+
+	/* Process received packets */
+	nv_process_rx_packets ( netdev );
+}
+
+/**
+ * close - Disable network interface
+ *
+ * @v netdev	network interface device structure
+ **/
+static void
+forcedeth_close ( struct net_device *netdev )
+{
+	struct forcedeth_private *priv = netdev_priv ( netdev );
+	void *ioaddr = priv->mmio_addr;
+
+	DBGP ( "forcedeth_close\n" );
+
+	nv_stop_rx ( priv );
+	nv_stop_tx ( priv );
+	nv_txrx_reset ( priv );
+
+	/* Disable interrupts on the nic or we will lock up */
+	nv_disable_hw_interrupts ( priv );
+	pci_push ( ioaddr );
+
+	nv_free_rxtx_resources ( priv );
+
+	nv_txrx_gate ( priv, 0 );
+
+	/* FIXME: power down nic */
+}
+
+/**
+ * irq - enable or disable interrupts
+ *
+ * @v netdev    network adapter
+ * @v action    requested interrupt action
+ **/
+static void
+forcedeth_irq ( struct net_device *netdev, int action )
+{
+	struct forcedeth_private *priv = netdev_priv ( netdev );
+
+	DBGP ( "forcedeth_irq\n" );
+
+	switch ( action ) {
+	case 0:
+		nv_disable_hw_interrupts ( priv );
+		break;
+	default:
+		nv_enable_hw_interrupts ( priv );
+		break;
+	}
+}
+
+static struct net_device_operations forcedeth_operations  = {
+	.open		= forcedeth_open,
+	.transmit	= forcedeth_transmit,
+	.poll		= forcedeth_poll,
+	.close		= forcedeth_close,
+	.irq		= forcedeth_irq,
+};
+
+static int
+nv_setup_mac_addr ( struct forcedeth_private *priv )
+{
+	struct net_device *dev = priv->netdev;
+	void *ioaddr = priv->mmio_addr;
+	u32 orig_mac[2];
+	u32 txreg;
+
+	orig_mac[0] = readl ( ioaddr + NvRegMacAddrA );
+	orig_mac[1] = readl ( ioaddr + NvRegMacAddrB );
+
+	txreg = readl ( ioaddr + NvRegTransmitPoll );
+
+	if ( ( priv->driver_data & DEV_HAS_CORRECT_MACADDR ) ||
+	     ( txreg & NVREG_TRANSMITPOLL_MAC_ADDR_REV ) ) {
+		/* mac address is already in correct order */
+		dev->hw_addr[0] = ( orig_mac[0] >> 0 ) & 0xff;
+		dev->hw_addr[1] = ( orig_mac[0] >> 8 ) & 0xff;
+		dev->hw_addr[2] = ( orig_mac[0] >> 16 ) & 0xff;
+		dev->hw_addr[3] = ( orig_mac[0] >> 24 ) & 0xff;
+		dev->hw_addr[4] = ( orig_mac[1] >> 0 ) & 0xff;
+		dev->hw_addr[5] = ( orig_mac[1] >> 8 ) & 0xff;
+	} else {
+		/* need to reverse mac address to correct order */
+		dev->hw_addr[0] = ( orig_mac[1] >> 8 ) & 0xff;
+		dev->hw_addr[1] = ( orig_mac[1] >> 0 ) & 0xff;
+		dev->hw_addr[2] = ( orig_mac[0] >> 24 ) & 0xff;
+		dev->hw_addr[3] = ( orig_mac[0] >> 16 ) & 0xff;
+		dev->hw_addr[4] = ( orig_mac[0] >> 8 ) & 0xff;
+		dev->hw_addr[5] = ( orig_mac[0] >> 0 ) & 0xff;
+
+		writel ( txreg | NVREG_TRANSMITPOLL_MAC_ADDR_REV,
+			 ioaddr + NvRegTransmitPoll );
+
+		DBG ( "set workaround bit for reversed mac addr\n" );
+	}
+
+	if ( ! is_valid_ether_addr ( dev->hw_addr ) )
+		return -EADDRNOTAVAIL;
+
+	DBG ( "MAC address is: %s\n", eth_ntoa ( dev->hw_addr ) );
+
+	return 0;
+}
+
+static int
+nv_mgmt_acquire_sema ( struct forcedeth_private *priv )
+{
+	void *ioaddr = priv->mmio_addr;
+	int i;
+	u32 tx_ctrl, mgmt_sema;
+
+	for ( i = 0; i < 10; i++ ) {
+		mgmt_sema = readl ( ioaddr + NvRegTransmitterControl ) &
+			NVREG_XMITCTL_MGMT_SEMA_MASK;
+		if ( mgmt_sema == NVREG_XMITCTL_MGMT_SEMA_FREE )
+			break;
+		mdelay ( 500 );
+	}
+
+	if ( mgmt_sema != NVREG_XMITCTL_MGMT_SEMA_FREE )
+		return 0;
+
+	for ( i = 0; i < 2; i++ ) {
+		tx_ctrl = readl ( ioaddr + NvRegTransmitterControl );
+		tx_ctrl |= NVREG_XMITCTL_HOST_SEMA_ACQ;
+		writel ( tx_ctrl, ioaddr + NvRegTransmitterControl );
+
+		/* verify that the semaphore was acquired */
+		tx_ctrl = readl ( ioaddr + NvRegTransmitterControl );
+		if ( ( ( tx_ctrl & NVREG_XMITCTL_HOST_SEMA_MASK ) ==
+		       NVREG_XMITCTL_HOST_SEMA_ACQ ) &&
+		     ( ( tx_ctrl & NVREG_XMITCTL_MGMT_SEMA_MASK ) ==
+		       NVREG_XMITCTL_MGMT_SEMA_FREE ) ) {
+			priv->mgmt_sema = 1;
+			return 1;
+		} else {
+			udelay ( 50 );
+		}
+	}
+
+	return 0;
+}
+
+static void
+nv_mgmt_release_sema ( struct forcedeth_private *priv )
+{
+	void *ioaddr = priv->mmio_addr;
+	u32 tx_ctrl;
+
+	if ( priv->driver_data & DEV_HAS_MGMT_UNIT ) {
+		if ( priv->mgmt_sema ) {
+			tx_ctrl = readl (ioaddr + NvRegTransmitterControl );
+			tx_ctrl &= ~NVREG_XMITCTL_HOST_SEMA_ACQ;
+			writel ( tx_ctrl, ioaddr + NvRegTransmitterControl );
+		}
+	}
+}
+
+static int
+nv_mgmt_get_version ( struct forcedeth_private *priv )
+{
+	void *ioaddr = priv->mmio_addr;
+	u32 data_ready = readl ( ioaddr + NvRegTransmitterControl );
+	u32 data_ready2 = 0;
+	unsigned long start;
+	int ready = 0;
+
+	writel ( NVREG_MGMTUNITGETVERSION,
+		ioaddr + NvRegMgmtUnitGetVersion );
+	writel ( data_ready ^ NVREG_XMITCTL_DATA_START,
+		ioaddr + NvRegTransmitterControl );
+	start = currticks();
+
+	while ( currticks() > start + 5 * ticks_per_sec() ) {
+		data_ready2 = readl ( ioaddr + NvRegTransmitterControl );
+		if ( ( data_ready & NVREG_XMITCTL_DATA_READY ) !=
+		     ( data_ready2 & NVREG_XMITCTL_DATA_READY ) ) {
+			ready = 1;
+			break;
+		}
+		mdelay ( 1000 );
+	}
+
+	if ( ! ready || ( data_ready2 & NVREG_XMITCTL_DATA_ERROR ) )
+		return 0;
+
+	priv->mgmt_version =
+		readl ( ioaddr + NvRegMgmtUnitVersion ) & NVREG_MGMTUNITVERSION;
+
+	return 1;
+}
+
+
+
+static int
+phy_reset ( struct forcedeth_private *priv, u32 bmcr_setup )
+{
+	u32 miicontrol;
+	unsigned int tries = 0;
+
+	miicontrol = BMCR_RESET | bmcr_setup;
+	if ( mii_rw ( priv, priv->phyaddr, MII_BMCR, miicontrol ) ) {
+		return -1;
+	}
+
+	mdelay ( 500 );
+
+	/* must wait till reset is deasserted */
+	while ( miicontrol & BMCR_RESET ) {
+		mdelay ( 10 );
+		miicontrol = mii_rw ( priv, priv->phyaddr, MII_BMCR, MII_READ );
+		if ( tries++ > 100 )
+			return -1;
+	}
+	return 0;
+}
+
+static int
+phy_init ( struct forcedeth_private *priv )
+{
+	void *ioaddr = priv->mmio_addr;
+	u32 phyinterface, phy_reserved, mii_status;
+	u32 mii_control, mii_control_1000, reg;
+
+	/* phy errata for E3016 phy */
+	if ( priv->phy_model == PHY_MODEL_MARVELL_E3016 ) {
+		reg = mii_rw ( priv, priv->phyaddr, MII_NCONFIG, MII_READ );
+		reg &= ~PHY_MARVELL_E3016_INITMASK;
+		if ( mii_rw ( priv, priv->phyaddr, MII_NCONFIG, reg ) ) {
+			DBG ( "PHY write to errata reg failed.\n" );
+			return PHY_ERROR;
+		}
+	}
+
+	if ( priv->phy_oui == PHY_OUI_REALTEK ) {
+		if ( priv->phy_model == PHY_MODEL_REALTEK_8211 &&
+		     priv->phy_rev == PHY_REV_REALTEK_8211B ) {
+			if ( mii_rw ( priv, priv->phyaddr,
+				PHY_REALTEK_INIT_REG1, PHY_REALTEK_INIT1 ) ) {
+				DBG ( "PHY init failed.\n" );
+				return PHY_ERROR;
+			}
+			if ( mii_rw ( priv, priv->phyaddr,
+				PHY_REALTEK_INIT_REG2, PHY_REALTEK_INIT2 ) ) {
+				DBG ( "PHY init failed.\n" );
+				return PHY_ERROR;
+			}
+			if ( mii_rw ( priv, priv->phyaddr,
+				PHY_REALTEK_INIT_REG1, PHY_REALTEK_INIT3 ) ) {
+				DBG ( "PHY init failed.\n" );
+				return PHY_ERROR;
+			}
+			if ( mii_rw ( priv, priv->phyaddr,
+				PHY_REALTEK_INIT_REG3, PHY_REALTEK_INIT4 ) ) {
+				DBG ( "PHY init failed.\n" );
+				return PHY_ERROR;
+			}
+			if ( mii_rw ( priv, priv->phyaddr,
+				PHY_REALTEK_INIT_REG4, PHY_REALTEK_INIT5 ) ) {
+				DBG ( "PHY init failed.\n" );
+				return PHY_ERROR;
+			}
+			if ( mii_rw ( priv, priv->phyaddr,
+				PHY_REALTEK_INIT_REG5, PHY_REALTEK_INIT6 ) ) {
+				DBG ( "PHY init failed.\n" );
+				return PHY_ERROR;
+			}
+			if ( mii_rw ( priv, priv->phyaddr,
+				PHY_REALTEK_INIT_REG1, PHY_REALTEK_INIT1 ) ) {
+				DBG ( "PHY init failed.\n" );
+				return PHY_ERROR;
+			}
+		}
+
+		if ( priv->phy_model == PHY_MODEL_REALTEK_8211 &&
+		     priv->phy_rev == PHY_REV_REALTEK_8211C ) {
+			u32 powerstate = readl ( ioaddr + NvRegPowerState2 );
+
+			/* need to perform hw phy reset */
+			powerstate |= NVREG_POWERSTATE2_PHY_RESET;
+			writel ( powerstate , ioaddr + NvRegPowerState2 );
+			mdelay ( 25 );
+
+			powerstate &= ~NVREG_POWERSTATE2_PHY_RESET;
+			writel ( powerstate , ioaddr + NvRegPowerState2 );
+			mdelay ( 25 );
+
+			reg = mii_rw ( priv, priv->phyaddr,
+				PHY_REALTEK_INIT_REG6, MII_READ );
+			reg |= PHY_REALTEK_INIT9;
+			if ( mii_rw ( priv, priv->phyaddr,
+				PHY_REALTEK_INIT_REG6, reg ) ) {
+				DBG ( "PHY init failed.\n" );
+				return PHY_ERROR;
+			}
+			if ( mii_rw ( priv, priv->phyaddr,
+				PHY_REALTEK_INIT_REG1, PHY_REALTEK_INIT10 ) ) {
+				DBG ( "PHY init failed.\n" );
+				return PHY_ERROR;
+			}
+
+			reg = mii_rw ( priv, priv->phyaddr,
+				PHY_REALTEK_INIT_REG7, MII_READ );
+			if ( ! ( reg & PHY_REALTEK_INIT11 ) ) {
+				reg |= PHY_REALTEK_INIT11;
+				if ( mii_rw ( priv, priv->phyaddr,
+					PHY_REALTEK_INIT_REG7, reg ) ) {
+					DBG ( "PHY init failed.\n" );
+					return PHY_ERROR;
+				}
+			}
+			if ( mii_rw ( priv, priv->phyaddr,
+				PHY_REALTEK_INIT_REG1, PHY_REALTEK_INIT1 ) ) {
+				DBG ( "PHY init failed.\n" );
+				return PHY_ERROR;
+			}
+		}
+		if ( priv->phy_model == PHY_MODEL_REALTEK_8201 ) {
+			if ( priv->driver_data & DEV_NEED_PHY_INIT_FIX ) {
+				phy_reserved = mii_rw ( priv, priv->phyaddr,
+							PHY_REALTEK_INIT_REG6,
+							MII_READ );
+				phy_reserved |= PHY_REALTEK_INIT7;
+				if ( mii_rw ( priv, priv->phyaddr,
+					      PHY_REALTEK_INIT_REG6,
+					      phy_reserved ) ) {
+					DBG ( "PHY init failed.\n" );
+					return PHY_ERROR;
+				}
+			}
+		}
+	}
+
+	/* set advertise register */
+	reg = mii_rw ( priv, priv->phyaddr, MII_ADVERTISE, MII_READ );
+	reg |= ( ADVERTISE_10HALF | ADVERTISE_10FULL | ADVERTISE_100HALF |
+		 ADVERTISE_100FULL | ADVERTISE_PAUSE_ASYM | ADVERTISE_PAUSE_CAP );
+	if ( mii_rw ( priv, priv->phyaddr, MII_ADVERTISE, reg ) ) {
+		DBG ( "PHY init failed.\n" );
+		return PHY_ERROR;
+	}
+
+	/* get phy interface type */
+	phyinterface = readl ( ioaddr + NvRegPhyInterface );
+
+	/* see if gigabit phy */
+	mii_status = mii_rw ( priv, priv->phyaddr, MII_BMSR, MII_READ );
+	if ( mii_status & PHY_GIGABIT ) {
+		priv->gigabit = PHY_GIGABIT;
+		mii_control_1000 =
+			mii_rw ( priv, priv->phyaddr, MII_CTRL1000, MII_READ );
+		mii_control_1000 &= ~ADVERTISE_1000HALF;
+		if ( phyinterface & PHY_RGMII )
+			mii_control_1000 |= ADVERTISE_1000FULL;
+		else
+			mii_control_1000 &= ~ADVERTISE_1000FULL;
+
+		if ( mii_rw ( priv, priv->phyaddr, MII_CTRL1000, mii_control_1000)) {
+			DBG ( "PHY init failed.\n" );
+			return PHY_ERROR;
+		}
+	} else {
+		priv->gigabit = 0;
+	}
+
+	mii_control = mii_rw ( priv, priv->phyaddr, MII_BMCR, MII_READ );
+	mii_control |= BMCR_ANENABLE;
+
+	if ( priv->phy_oui == PHY_OUI_REALTEK &&
+	     priv->phy_model == PHY_MODEL_REALTEK_8211 &&
+	     priv->phy_rev == PHY_REV_REALTEK_8211C ) {
+		/* start autoneg since we already performed hw reset above */
+		mii_control |= BMCR_ANRESTART;
+		if ( mii_rw ( priv, priv->phyaddr, MII_BMCR, mii_control ) ) {
+			DBG ( "PHY init failed.\n" );
+			return PHY_ERROR;
+		}
+	} else {
+		/* reset the phy
+		 * (certain phys need bmcr to be setup with reset )
+		 */
+		if ( phy_reset ( priv, mii_control ) ) {
+			DBG ( "PHY reset failed\n" );
+			return PHY_ERROR;
+		}
+	}
+
+	/* phy vendor specific configuration */
+	if ( ( priv->phy_oui == PHY_OUI_CICADA ) && ( phyinterface & PHY_RGMII ) ) {
+		phy_reserved = mii_rw ( priv, priv->phyaddr, MII_RESV1, MII_READ );
+		phy_reserved &= ~( PHY_CICADA_INIT1 | PHY_CICADA_INIT2 );
+		phy_reserved |= ( PHY_CICADA_INIT3 | PHY_CICADA_INIT4 );
+		if ( mii_rw ( priv, priv->phyaddr, MII_RESV1, phy_reserved ) ) {
+			DBG ( "PHY init failed.\n" );
+			return PHY_ERROR;
+		}
+		phy_reserved = mii_rw ( priv, priv->phyaddr, MII_NCONFIG, MII_READ );
+		phy_reserved |= PHY_CICADA_INIT5;
+		if ( mii_rw ( priv, priv->phyaddr, MII_NCONFIG, phy_reserved ) ) {
+			DBG ( "PHY init failed.\n" );
+			return PHY_ERROR;
+		}
+	}
+	if ( priv->phy_oui == PHY_OUI_CICADA ) {
+		phy_reserved = mii_rw ( priv, priv->phyaddr, MII_SREVISION, MII_READ );
+		phy_reserved |= PHY_CICADA_INIT6;
+		if ( mii_rw ( priv, priv->phyaddr, MII_SREVISION, phy_reserved ) ) {
+			DBG ( "PHY init failed.\n" );
+			return PHY_ERROR;
+		}
+	}
+	if ( priv->phy_oui == PHY_OUI_VITESSE ) {
+		if ( mii_rw ( priv, priv->phyaddr, PHY_VITESSE_INIT_REG1,
+						   PHY_VITESSE_INIT1)) {
+			DBG ( "PHY init failed.\n" );
+			return PHY_ERROR;
+		}
+		if ( mii_rw ( priv, priv->phyaddr, PHY_VITESSE_INIT_REG2,
+						   PHY_VITESSE_INIT2)) {
+			DBG ( "PHY init failed.\n" );
+			return PHY_ERROR;
+		}
+		phy_reserved = mii_rw ( priv, priv->phyaddr,
+					PHY_VITESSE_INIT_REG4, MII_READ);
+		if ( mii_rw ( priv, priv->phyaddr, PHY_VITESSE_INIT_REG4,
+						   phy_reserved ) ) {
+			DBG ( "PHY init failed.\n" );
+			return PHY_ERROR;
+		}
+		phy_reserved = mii_rw ( priv, priv->phyaddr,
+					PHY_VITESSE_INIT_REG3, MII_READ);
+		phy_reserved &= ~PHY_VITESSE_INIT_MSK1;
+		phy_reserved |= PHY_VITESSE_INIT3;
+		if ( mii_rw ( priv, priv->phyaddr, PHY_VITESSE_INIT_REG3,
+						   phy_reserved ) ) {
+			DBG ( "PHY init failed.\n" );
+			return PHY_ERROR;
+		}
+		if ( mii_rw ( priv, priv->phyaddr, PHY_VITESSE_INIT_REG2,
+						   PHY_VITESSE_INIT4 ) ) {
+			DBG ( "PHY init failed.\n" );
+			return PHY_ERROR;
+		}
+		if ( mii_rw ( priv, priv->phyaddr, PHY_VITESSE_INIT_REG2,
+						   PHY_VITESSE_INIT5 ) ) {
+			DBG ( "PHY init failed.\n" );
+			return PHY_ERROR;
+		}
+		phy_reserved = mii_rw ( priv, priv->phyaddr,
+					PHY_VITESSE_INIT_REG4, MII_READ);
+		phy_reserved &= ~PHY_VITESSE_INIT_MSK1;
+		phy_reserved |= PHY_VITESSE_INIT3;
+		if ( mii_rw ( priv, priv->phyaddr, PHY_VITESSE_INIT_REG4,
+						   phy_reserved ) ) {
+			DBG ( "PHY init failed.\n" );
+			return PHY_ERROR;
+		}
+		phy_reserved = mii_rw ( priv, priv->phyaddr,
+					PHY_VITESSE_INIT_REG3, MII_READ);
+		if ( mii_rw ( priv, priv->phyaddr, PHY_VITESSE_INIT_REG3,
+						   phy_reserved ) ) {
+			DBG ( "PHY init failed.\n" );
+			return PHY_ERROR;
+		}
+		if ( mii_rw ( priv, priv->phyaddr, PHY_VITESSE_INIT_REG2,
+						   PHY_VITESSE_INIT6 ) ) {
+			DBG ( "PHY init failed.\n" );
+			return PHY_ERROR;
+		}
+		if ( mii_rw ( priv, priv->phyaddr, PHY_VITESSE_INIT_REG2,
+						   PHY_VITESSE_INIT7 ) ) {
+			DBG ( "PHY init failed.\n" );
+			return PHY_ERROR;
+		}
+		phy_reserved = mii_rw ( priv, priv->phyaddr,
+					PHY_VITESSE_INIT_REG4, MII_READ);
+		if ( mii_rw ( priv, priv->phyaddr, PHY_VITESSE_INIT_REG4,
+						   phy_reserved ) ) {
+			DBG ( "PHY init failed.\n" );
+			return PHY_ERROR;
+		}
+		phy_reserved = mii_rw ( priv, priv->phyaddr,
+					PHY_VITESSE_INIT_REG3, MII_READ);
+		phy_reserved &= ~PHY_VITESSE_INIT_MSK2;
+		phy_reserved |= PHY_VITESSE_INIT8;
+		if ( mii_rw ( priv, priv->phyaddr, PHY_VITESSE_INIT_REG3,
+						   phy_reserved ) ) {
+			DBG ( "PHY init failed.\n" );
+			return PHY_ERROR;
+		}
+		if ( mii_rw ( priv, priv->phyaddr, PHY_VITESSE_INIT_REG2,
+						   PHY_VITESSE_INIT9 ) ) {
+			DBG ( "PHY init failed.\n" );
+			return PHY_ERROR;
+		}
+		if ( mii_rw ( priv, priv->phyaddr, PHY_VITESSE_INIT_REG1,
+						   PHY_VITESSE_INIT10 ) ) {
+			DBG ( "PHY init failed.\n" );
+			return PHY_ERROR;
+		}
+	}
+
+	if ( priv->phy_oui == PHY_OUI_REALTEK ) {
+		if ( priv->phy_model == PHY_MODEL_REALTEK_8211 &&
+		     priv->phy_rev == PHY_REV_REALTEK_8211B ) {
+			/* reset could have cleared these out, set them back */
+			if ( mii_rw ( priv, priv->phyaddr,
+				      PHY_REALTEK_INIT_REG1, PHY_REALTEK_INIT1 ) ) {
+				DBG ( "PHY init failed.\n" );
+				return PHY_ERROR;
+			}
+			if ( mii_rw ( priv, priv->phyaddr,
+				      PHY_REALTEK_INIT_REG2, PHY_REALTEK_INIT2 ) ) {
+				DBG ( "PHY init failed.\n" );
+				return PHY_ERROR;
+			}
+			if ( mii_rw ( priv, priv->phyaddr,
+				      PHY_REALTEK_INIT_REG1, PHY_REALTEK_INIT3 ) ) {
+				DBG ( "PHY init failed.\n" );
+				return PHY_ERROR;
+			}
+			if ( mii_rw ( priv, priv->phyaddr,
+				      PHY_REALTEK_INIT_REG3, PHY_REALTEK_INIT4 ) ) {
+				DBG ( "PHY init failed.\n" );
+				return PHY_ERROR;
+			}
+			if ( mii_rw ( priv, priv->phyaddr,
+				      PHY_REALTEK_INIT_REG4, PHY_REALTEK_INIT5 ) ) {
+				DBG ( "PHY init failed.\n" );
+				return PHY_ERROR;
+			}
+			if ( mii_rw ( priv, priv->phyaddr,
+				      PHY_REALTEK_INIT_REG5, PHY_REALTEK_INIT6 ) ) {
+				DBG ( "PHY init failed.\n" );
+				return PHY_ERROR;
+			}
+			if ( mii_rw ( priv, priv->phyaddr,
+				      PHY_REALTEK_INIT_REG1, PHY_REALTEK_INIT1 ) ) {
+				DBG ( "PHY init failed.\n" );
+				return PHY_ERROR;
+			}
+		}
+		if ( priv->phy_model == PHY_MODEL_REALTEK_8201 ) {
+			if ( priv->driver_data & DEV_NEED_PHY_INIT_FIX ) {
+				phy_reserved = mii_rw ( priv, priv->phyaddr,
+							PHY_REALTEK_INIT_REG6,
+							MII_READ );
+				phy_reserved |= PHY_REALTEK_INIT7;
+				if ( mii_rw ( priv, priv->phyaddr,
+					      PHY_REALTEK_INIT_REG6,
+					      phy_reserved ) ) {
+					DBG ( "PHY init failed.\n" );
+					return PHY_ERROR;
+				}
+			}
+
+			if ( mii_rw ( priv, priv->phyaddr,
+				      PHY_REALTEK_INIT_REG1,
+				      PHY_REALTEK_INIT3 ) ) {
+				DBG ( "PHY init failed.\n" );
+				return PHY_ERROR;
+			}
+			phy_reserved = mii_rw ( priv, priv->phyaddr,
+						PHY_REALTEK_INIT_REG2,
+						MII_READ );
+			phy_reserved &= ~PHY_REALTEK_INIT_MSK1;
+			phy_reserved |= PHY_REALTEK_INIT3;
+			if ( mii_rw ( priv, priv->phyaddr,
+				      PHY_REALTEK_INIT_REG2,
+				      phy_reserved ) ) {
+				DBG ( "PHY init failed.\n" );
+				return PHY_ERROR;
+			}
+			if ( mii_rw ( priv, priv->phyaddr,
+				      PHY_REALTEK_INIT_REG1,
+				      PHY_REALTEK_INIT1 ) ) {
+				DBG ( "PHY init failed.\n" );
+				return PHY_ERROR;
+			}
+		}
+	}
+
+	/* some phys clear out pause advertisement on reset, set it back */
+	mii_rw ( priv, priv->phyaddr, MII_ADVERTISE, reg );
+
+	/* restart auto negotiation, power down phy */
+	mii_control = mii_rw ( priv, priv->phyaddr, MII_BMCR, MII_READ );
+	mii_control |= ( BMCR_ANRESTART | BMCR_ANENABLE );
+	if ( mii_rw ( priv, priv->phyaddr, MII_BMCR, mii_control ) ) {
+		return PHY_ERROR;
+	}
+
+	return 0;
+}
+
+/**
+ * forcedeth_get_desc_version - Establish descriptor version
+ * and set RX and TX flags used in the descriptors
+ *
+ * @v priv	Driver private structure
+ **/
+static void
+forcedeth_get_desc_version ( struct forcedeth_private *priv )
+{
+	/* Handle different descriptor versions */
+	if ( priv->driver_data & DEV_HAS_HIGH_DMA ) {
+		/* Packet format 3: supports 40-bit addressing */
+		priv->desc_ver = DESC_VER_3;
+		priv->txrxctl_bits = NVREG_TXRXCTL_DESC_3;
+	} else if ( priv->driver_data & DEV_HAS_LARGEDESC ) {
+		/* Packet format 2: supports jumbo frames */
+		priv->desc_ver = DESC_VER_2;
+		priv->txrxctl_bits = NVREG_TXRXCTL_DESC_2;
+	} else {
+		/* Original packet format */
+		priv->desc_ver = DESC_VER_1;
+		priv->txrxctl_bits = NVREG_TXRXCTL_DESC_1;
+	}
+
+	if ( priv->desc_ver == DESC_VER_1 ) {
+		priv->tx_flags = NV_TX_VALID;
+	} else {
+		priv->tx_flags = NV_TX2_VALID;
+	}
+}
+
+/**
+ * nv_setup_phy - Find PHY and initialize it
+ *
+ * @v priv	Driver private structure
+ *
+ * @ret rc	Return status code
+ **/
+static int
+nv_setup_phy ( struct forcedeth_private *priv )
+{
+	void *ioaddr = priv->mmio_addr;
+	u32 phystate_orig = 0, phystate;
+	int phyinitialised = 0;
+	u32 powerstate;
+	int rc = 0;
+	int i;
+
+	if ( priv->driver_data & DEV_HAS_POWER_CNTRL ) {
+		/* take phy and nic out of low power mode */
+		powerstate = readl ( ioaddr + NvRegPowerState2 );
+		powerstate &= ~NVREG_POWERSTATE2_POWERUP_MASK;
+		if ( ( priv->driver_data & DEV_NEED_LOW_POWER_FIX ) &&
+		     ( ( priv->pci_dev->class & 0xff ) >= 0xA3 ) )
+			powerstate |= NVREG_POWERSTATE2_POWERUP_REV_A3;
+		writel ( powerstate, ioaddr + NvRegPowerState2 );
+	}
+
+
+	/* clear phy state and temporarily halt phy interrupts */
+	writel ( 0, ioaddr + NvRegMIIMask );
+	phystate = readl ( ioaddr + NvRegAdapterControl );
+	if ( phystate & NVREG_ADAPTCTL_RUNNING ) {
+		phystate_orig = 1;
+		phystate &= ~NVREG_ADAPTCTL_RUNNING;
+		writel ( phystate, ioaddr + NvRegAdapterControl );
+	}
+	writel ( NVREG_MIISTAT_MASK_ALL, ioaddr + NvRegMIIStatus );
+
+	if ( priv->driver_data & DEV_HAS_MGMT_UNIT ) {
+		/* management unit running on the mac? */
+		if ( ( readl ( ioaddr + NvRegTransmitterControl ) & NVREG_XMITCTL_MGMT_ST ) &&
+		     ( readl ( ioaddr + NvRegTransmitterControl ) & NVREG_XMITCTL_SYNC_PHY_INIT ) &&
+		     nv_mgmt_acquire_sema ( priv ) &&
+		     nv_mgmt_get_version ( priv ) ) {
+			priv->mac_in_use = 1;
+			if ( priv->mgmt_version > 0 ) {
+				priv->mac_in_use = readl ( ioaddr + NvRegMgmtUnitControl ) & NVREG_MGMTUNITCONTROL_INUSE;
+			}
+
+			DBG ( "mgmt unit is running. mac in use\n" );
+
+			/* management unit setup the phy already? */
+			if ( priv->mac_in_use &&
+			   ( ( readl ( ioaddr + NvRegTransmitterControl ) & NVREG_XMITCTL_SYNC_MASK ) ==
+			     NVREG_XMITCTL_SYNC_PHY_INIT ) ) {
+				/* phy is inited by mgmt unit */
+				phyinitialised = 1;
+				DBG ( "Phy already initialized by mgmt unit" );
+			}
+		}
+	}
+
+	/* find a suitable phy */
+	for ( i = 1; i <= 32; i++ ) {
+		int id1, id2;
+		int phyaddr = i & 0x1f;
+
+		id1 = mii_rw ( priv, phyaddr, MII_PHYSID1, MII_READ );
+		if ( id1 < 0 || id1 == 0xffff )
+			continue;
+		id2 = mii_rw ( priv, phyaddr, MII_PHYSID2, MII_READ );
+		if ( id2 < 0 || id2 == 0xffff )
+			continue;
+
+		priv->phy_model = id2 & PHYID2_MODEL_MASK;
+		id1 = ( id1 & PHYID1_OUI_MASK ) << PHYID1_OUI_SHFT;
+		id2 = ( id2 & PHYID2_OUI_MASK ) >> PHYID2_OUI_SHFT;
+		DBG ( "Found PHY: %04x:%04x at address %d\n", id1, id2, phyaddr );
+		priv->phyaddr = phyaddr;
+		priv->phy_oui = id1 | id2;
+
+		/* Realtek hardcoded phy id1 to all zeros on certain phys */
+		if ( priv->phy_oui == PHY_OUI_REALTEK2 )
+			priv->phy_oui = PHY_OUI_REALTEK;
+		/* Setup phy revision for Realtek */
+		if ( priv->phy_oui == PHY_OUI_REALTEK &&
+		     priv->phy_model == PHY_MODEL_REALTEK_8211 )
+			priv->phy_rev = mii_rw ( priv, phyaddr, MII_RESV1,
+						 MII_READ ) & PHY_REV_MASK;
+		break;
+	}
+	if ( i == 33 ) {
+		DBG ( "Could not find a valid PHY.\n" );
+		rc = -ENODEV;
+		goto err_phy;
+	}
+
+	if ( ! phyinitialised ) {
+		/* reset it */
+		phy_init ( priv );
+	} else {
+		u32 mii_status = mii_rw ( priv, priv->phyaddr, MII_BMSR, MII_READ );
+		if ( mii_status & PHY_GIGABIT ) {
+			priv->gigabit = PHY_GIGABIT;
+		}
+	}
+
+	return 0;
+
+err_phy:
+	if ( phystate_orig )
+		writel ( phystate | NVREG_ADAPTCTL_RUNNING,
+			 ioaddr + NvRegAdapterControl );
+	return rc;
+}
+
+/**
+ * forcedeth_map_regs - Find a suitable BAR for the NIC and
+ * map the registers in memory
+ *
+ * @v priv	Driver private structure
+ *
+ * @ret rc	Return status code
+ **/
+static int
+forcedeth_map_regs ( struct forcedeth_private *priv )
+{
+	void *ioaddr;
+	uint32_t bar;
+	unsigned long addr;
+	u32 register_size;
+	int reg;
+	int rc;
+
+	/* Set register size based on NIC */
+	if ( priv->driver_data & ( DEV_HAS_VLAN | DEV_HAS_MSI_X |
+		DEV_HAS_POWER_CNTRL | DEV_HAS_STATISTICS_V2 |
+		DEV_HAS_STATISTICS_V3 ) ) {
+		register_size = NV_PCI_REGSZ_VER3;
+	} else if ( priv->driver_data & DEV_HAS_STATISTICS_V1 ) {
+		register_size = NV_PCI_REGSZ_VER2;
+	} else {
+		register_size = NV_PCI_REGSZ_VER1;
+	}
+
+	/* Find an appropriate region for all the registers */
+	rc = -EINVAL;
+	addr = 0;
+	for ( reg = PCI_BASE_ADDRESS_0; reg <= PCI_BASE_ADDRESS_5; reg += 4 ) {
+		pci_read_config_dword ( priv->pci_dev, reg, &bar );
+
+		if ( ( ( bar & PCI_BASE_ADDRESS_SPACE ) ==
+			 PCI_BASE_ADDRESS_SPACE_MEMORY ) &&
+		       ( pci_bar_size ( priv->pci_dev, reg ) >=
+			 register_size ) ) {
+			addr = pci_bar_start ( priv->pci_dev, reg );
+			break;
+		}
+	}
+
+	if ( reg > PCI_BASE_ADDRESS_5 ) {
+		DBG ( "Couldn't find register window\n" );
+		goto err_bar_sz;
+	}
+
+	rc = -ENOMEM;
+	ioaddr = ioremap ( addr, register_size );
+	if ( ! ioaddr ) {
+		DBG ( "Cannot remap MMIO\n" );
+		goto err_ioremap;
+	}
+
+	priv->mmio_addr = ioaddr;
+
+	return 0;
+
+err_bar_sz:
+err_ioremap:
+	return rc;
+}
+
+/**
+ * probe - Initial configuration of NIC
+ *
+ * @v pdev	PCI device
+ * @v ent	PCI IDs
+ *
+ * @ret rc	Return status code
+ **/
+static int
+forcedeth_probe ( struct pci_device *pdev, const struct pci_device_id *ent )
+{
+	struct net_device *netdev;
+	struct forcedeth_private *priv;
+	void *ioaddr;
+	int rc;
+
+	DBGP ( "forcedeth_probe\n" );
+
+	DBG ( "Found %s, vendor = %#04x, device = %#04x\n",
+		pdev->driver_name, ent->vendor, ent->device );
+
+	/* Allocate our private data */
+	netdev = alloc_etherdev ( sizeof ( *priv ) );
+	if ( ! netdev ) {
+		rc = -ENOMEM;
+		DBG ( "Failed to allocate net device\n" );
+		goto err_alloc_etherdev;
+	}
+
+	/* Link our operations to the netdev struct */
+	netdev_init ( netdev, &forcedeth_operations );
+
+	/* Link the PCI device to the netdev struct */
+	pci_set_drvdata ( pdev, netdev );
+	netdev->dev = &pdev->dev;
+
+	/* Get a reference to our private data */
+	priv = netdev_priv ( netdev );
+
+	/* We'll need these set up for the rest of the routines */
+	priv->pci_dev = pdev;
+	priv->netdev = netdev;
+	priv->driver_data = ent->driver_data;
+
+	adjust_pci_device ( pdev );
+
+	/* Establish used descriptor format */
+	forcedeth_get_desc_version ( priv );
+
+	/* Use memory mapped I/O */
+	if ( ( rc = forcedeth_map_regs ( priv ) ) != 0 )
+		goto err_map_regs;
+	ioaddr = priv->mmio_addr;
+
+	/* Verify and get MAC address */
+	if ( ( rc = nv_setup_mac_addr ( priv ) ) != 0 ) {
+		DBG ( "Invalid MAC address detected\n" );
+		goto err_mac_addr;
+	}
+
+	/* Disable WOL */
+	writel ( 0, ioaddr + NvRegWakeUpFlags );
+
+	if ( ( rc = nv_setup_phy ( priv ) ) != 0 )
+		goto err_setup_phy;
+
+	/* Set Pause Frame parameters */
+	priv->pause_flags = NV_PAUSEFRAME_RX_CAPABLE |
+			    NV_PAUSEFRAME_RX_REQ |
+			    NV_PAUSEFRAME_AUTONEG;
+	if ( ( priv->driver_data & DEV_HAS_PAUSEFRAME_TX_V1 ) ||
+	     ( priv->driver_data & DEV_HAS_PAUSEFRAME_TX_V2 ) ||
+	     ( priv->driver_data & DEV_HAS_PAUSEFRAME_TX_V3 ) ) {
+		priv->pause_flags |= NV_PAUSEFRAME_TX_CAPABLE | NV_PAUSEFRAME_TX_REQ;
+	}
+
+	if ( priv->pause_flags & NV_PAUSEFRAME_TX_CAPABLE )
+		writel ( NVREG_TX_PAUSEFRAME_DISABLE, ioaddr + NvRegTxPauseFrame );
+
+	/* Set default link speed settings */
+	priv->linkspeed = NVREG_LINKSPEED_FORCE | NVREG_LINKSPEED_10;
+	priv->duplex = 0;
+
+	if ( ( rc = register_netdev ( netdev ) ) != 0 ) {
+		DBG ( "Error registering netdev\n" );
+		goto err_register_netdev;
+	}
+
+	priv->link_poll_timer = NV_LINK_POLL_FREQUENCY;
+
+	netdev_link_down ( netdev );
+
+	return 0;
+
+err_register_netdev:
+err_setup_phy:
+err_mac_addr:
+	iounmap ( priv->mmio_addr );
+err_map_regs:
+	netdev_nullify ( netdev );
+	netdev_put ( netdev );
+err_alloc_etherdev:
+	return rc;
+}
+
+static void
+nv_restore_phy ( struct forcedeth_private *priv )
+{
+	u16 phy_reserved, mii_control;
+
+	if ( priv->phy_oui == PHY_OUI_REALTEK &&
+	     priv->phy_model == PHY_MODEL_REALTEK_8201 ) {
+		mii_rw ( priv, priv->phyaddr, PHY_REALTEK_INIT_REG1,
+					      PHY_REALTEK_INIT3 );
+		phy_reserved = mii_rw ( priv, priv->phyaddr,
+					PHY_REALTEK_INIT_REG2, MII_READ );
+		phy_reserved &= ~PHY_REALTEK_INIT_MSK1;
+		phy_reserved |= PHY_REALTEK_INIT8;
+		mii_rw ( priv, priv->phyaddr, PHY_REALTEK_INIT_REG2,
+					      phy_reserved );
+		mii_rw ( priv, priv->phyaddr, PHY_REALTEK_INIT_REG1,
+					      PHY_REALTEK_INIT1 );
+
+		/* restart auto negotiation */
+		mii_control = mii_rw ( priv, priv->phyaddr, MII_BMCR, MII_READ );
+		mii_control |= ( BMCR_ANRESTART | BMCR_ANENABLE );
+		mii_rw ( priv, priv->phyaddr, MII_BMCR, mii_control );
+	}
+}
+
+/**
+ * remove - Device Removal Routine
+ *
+ * @v pdev PCI device information struct
+ **/
+static void
+forcedeth_remove ( struct pci_device *pdev )
+{
+	struct net_device *netdev = pci_get_drvdata ( pdev );
+	struct forcedeth_private *priv = netdev->priv;
+
+	DBGP ( "forcedeth_remove\n" );
+
+	nv_restore_phy ( priv );
+
+	nv_mgmt_release_sema ( priv );
+
+	iounmap ( priv->mmio_addr );
+
+	unregister_netdev ( netdev );
+	netdev_nullify ( netdev );
+	netdev_put ( netdev );
+}
+
+static struct pci_device_id forcedeth_nics[] = {
+	PCI_ROM(0x10DE, 0x01C3, "nForce", "nForce Ethernet Controller", DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER),
+	PCI_ROM(0x10DE, 0x0066, "nForce2", "nForce2 Ethernet Controller", DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER),
+	PCI_ROM(0x10DE, 0x00D6, "nForce3", "nForce3 Ethernet Controller", DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER),
+	PCI_ROM(0x10DE, 0x0086, "nForce3", "nForce3 Ethernet Controller", DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC| DEV_HAS_CHECKSUM),
+	PCI_ROM(0x10DE, 0x008C, "nForce3", "nForce3 Ethernet Controller", DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC| DEV_HAS_CHECKSUM),
+	PCI_ROM(0x10DE, 0x00E6, "nForce3", "nForce3 Ethernet Controller", DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC| DEV_HAS_CHECKSUM),
+	PCI_ROM(0x10DE, 0x00DF, "nForce3", "nForce3 Ethernet Controller", DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC| DEV_HAS_CHECKSUM),
+	PCI_ROM(0x10DE, 0x0056, "CK804", "CK804 Ethernet Controller", DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_CHECKSUM| DEV_HAS_HIGH_DMA|DEV_HAS_STATISTICS_V1|DEV_NEED_TX_LIMIT),
+	PCI_ROM(0x10DE, 0x0057, "CK804", "CK804 Ethernet Controller", DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_CHECKSUM| DEV_HAS_HIGH_DMA|DEV_HAS_STATISTICS_V1|DEV_NEED_TX_LIMIT),
+	PCI_ROM(0x10DE, 0x0037, "MCP04", "MCP04 Ethernet Controller", DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_CHECKSUM| DEV_HAS_HIGH_DMA|DEV_HAS_STATISTICS_V1|DEV_NEED_TX_LIMIT),
+	PCI_ROM(0x10DE, 0x0038, "MCP04", "MCP04 Ethernet Controller", DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_CHECKSUM| DEV_HAS_HIGH_DMA|DEV_HAS_STATISTICS_V1|DEV_NEED_TX_LIMIT),
+	PCI_ROM(0x10DE, 0x0268, "MCP51", "MCP51 Ethernet Controller", DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL| DEV_HAS_STATISTICS_V1|DEV_NEED_LOW_POWER_FIX),
+	PCI_ROM(0x10DE, 0x0269, "MCP51", "MCP51 Ethernet Controller", DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL| DEV_HAS_STATISTICS_V1|DEV_NEED_LOW_POWER_FIX),
+	PCI_ROM(0x10DE, 0x0372, "MCP55", "MCP55 Ethernet Controller", DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_CHECKSUM| DEV_HAS_HIGH_DMA|DEV_HAS_VLAN|DEV_HAS_MSI|DEV_HAS_MSI_X| DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX_V1| DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED| DEV_HAS_MGMT_UNIT|DEV_NEED_TX_LIMIT|DEV_NEED_MSI_FIX),
+	PCI_ROM(0x10DE, 0x0373, "MCP55", "MCP55 Ethernet Controller", DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_CHECKSUM| DEV_HAS_HIGH_DMA|DEV_HAS_VLAN|DEV_HAS_MSI|DEV_HAS_MSI_X| DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX_V1| DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT| DEV_NEED_TX_LIMIT|DEV_NEED_MSI_FIX),
+	PCI_ROM(0x10DE, 0x03E5, "MCP61", "MCP61 Ethernet Controller", DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL| DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX_V1|DEV_HAS_STATISTICS_V2| DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR| DEV_NEED_MSI_FIX),
+	PCI_ROM(0x10DE, 0x03E6, "MCP61", "MCP61 Ethernet Controller", DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL| DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX_V1|DEV_HAS_STATISTICS_V2| DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR| DEV_NEED_MSI_FIX),
+	PCI_ROM(0x10DE, 0x03EE, "MCP61", "MCP61 Ethernet Controller", DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL| DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX_V1|DEV_HAS_STATISTICS_V2| DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR| DEV_NEED_MSI_FIX),
+	PCI_ROM(0x10DE, 0x03EF, "MCP61", "MCP61 Ethernet Controller", DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL| DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX_V1|DEV_HAS_STATISTICS_V2| DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR| DEV_NEED_MSI_FIX),
+	PCI_ROM(0x10DE, 0x0450, "MCP65", "MCP65 Ethernet Controller", DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_HIGH_DMA| DEV_HAS_POWER_CNTRL|DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX_V1| DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT| DEV_HAS_CORRECT_MACADDR|DEV_NEED_TX_LIMIT|DEV_HAS_GEAR_MODE| DEV_NEED_MSI_FIX),
+	PCI_ROM(0x10DE, 0x0451, "MCP65", "MCP65 Ethernet Controller", DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_HIGH_DMA| DEV_HAS_POWER_CNTRL|DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX_V1| DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT| DEV_HAS_CORRECT_MACADDR|DEV_NEED_TX_LIMIT|DEV_HAS_GEAR_MODE| DEV_NEED_MSI_FIX),
+	PCI_ROM(0x10DE, 0x0452, "MCP65", "MCP65 Ethernet Controller", DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_HIGH_DMA| DEV_HAS_POWER_CNTRL|DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX_V1| DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT| DEV_HAS_CORRECT_MACADDR|DEV_NEED_TX_LIMIT|DEV_HAS_GEAR_MODE| DEV_NEED_MSI_FIX),
+	PCI_ROM(0x10DE, 0x0453, "MCP65", "MCP65 Ethernet Controller", DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_HIGH_DMA| DEV_HAS_POWER_CNTRL|DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX_V1| DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT| DEV_HAS_CORRECT_MACADDR|DEV_NEED_TX_LIMIT|DEV_HAS_GEAR_MODE| DEV_NEED_MSI_FIX),
+	PCI_ROM(0x10DE, 0x054C, "MCP67", "MCP67 Ethernet Controller", DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL| DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX_V1|DEV_HAS_STATISTICS_V2| DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR| DEV_HAS_GEAR_MODE|DEV_NEED_MSI_FIX),
+	PCI_ROM(0x10DE, 0x054D, "MCP67", "MCP67 Ethernet Controller", DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL| DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX_V1|DEV_HAS_STATISTICS_V2| DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR| DEV_HAS_GEAR_MODE|DEV_NEED_MSI_FIX),
+	PCI_ROM(0x10DE, 0x054E, "MCP67", "MCP67 Ethernet Controller", DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL| DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX_V1|DEV_HAS_STATISTICS_V2| DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR| DEV_HAS_GEAR_MODE|DEV_NEED_MSI_FIX),
+	PCI_ROM(0x10DE, 0x054F, "MCP67", "MCP67 Ethernet Controller", DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL| DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX_V1|DEV_HAS_STATISTICS_V2| DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR| DEV_HAS_GEAR_MODE|DEV_NEED_MSI_FIX),
+	PCI_ROM(0x10DE, 0x07DC, "MCP73", "MCP73 Ethernet Controller", DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL| DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX_V1|DEV_HAS_STATISTICS_V2| DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR| DEV_HAS_COLLISION_FIX|DEV_HAS_GEAR_MODE|DEV_NEED_MSI_FIX),
+	PCI_ROM(0x10DE, 0x07DD, "MCP73", "MCP73 Ethernet Controller", DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL| DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX_V1|DEV_HAS_STATISTICS_V2| DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR| DEV_HAS_COLLISION_FIX|DEV_HAS_GEAR_MODE|DEV_NEED_MSI_FIX),
+	PCI_ROM(0x10DE, 0x07DE, "MCP73", "MCP73 Ethernet Controller", DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL| DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX_V1|DEV_HAS_STATISTICS_V2| DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR| DEV_HAS_COLLISION_FIX|DEV_HAS_GEAR_MODE|DEV_NEED_MSI_FIX),
+	PCI_ROM(0x10DE, 0x07DF, "MCP73", "MCP73 Ethernet Controller", DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL| DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX_V1|DEV_HAS_STATISTICS_V2| DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR| DEV_HAS_COLLISION_FIX|DEV_HAS_GEAR_MODE|DEV_NEED_MSI_FIX),
+	PCI_ROM(0x10DE, 0x0760, "MCP77", "MCP77 Ethernet Controller", DEV_NEED_LINKTIMER|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA| DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX_V2| DEV_HAS_STATISTICS_V3|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT| DEV_HAS_CORRECT_MACADDR|DEV_HAS_COLLISION_FIX| DEV_NEED_TX_LIMIT2|DEV_HAS_GEAR_MODE|DEV_NEED_PHY_INIT_FIX| DEV_NEED_MSI_FIX),
+	PCI_ROM(0x10DE, 0x0761, "MCP77", "MCP77 Ethernet Controller", DEV_NEED_LINKTIMER|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA| DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX_V2| DEV_HAS_STATISTICS_V3|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT| DEV_HAS_CORRECT_MACADDR|DEV_HAS_COLLISION_FIX| DEV_NEED_TX_LIMIT2|DEV_HAS_GEAR_MODE|DEV_NEED_PHY_INIT_FIX| DEV_NEED_MSI_FIX),
+	PCI_ROM(0x10DE, 0x0762, "MCP77", "MCP77 Ethernet Controller", DEV_NEED_LINKTIMER|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA| DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX_V2| DEV_HAS_STATISTICS_V3|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT| DEV_HAS_CORRECT_MACADDR|DEV_HAS_COLLISION_FIX| DEV_NEED_TX_LIMIT2|DEV_HAS_GEAR_MODE|DEV_NEED_PHY_INIT_FIX| DEV_NEED_MSI_FIX),
+	PCI_ROM(0x10DE, 0x0763, "MCP77", "MCP77 Ethernet Controller", DEV_NEED_LINKTIMER|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA| DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX_V2| DEV_HAS_STATISTICS_V3|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT| DEV_HAS_CORRECT_MACADDR|DEV_HAS_COLLISION_FIX| DEV_NEED_TX_LIMIT2|DEV_HAS_GEAR_MODE|DEV_NEED_PHY_INIT_FIX| DEV_NEED_MSI_FIX),
+	PCI_ROM(0x10DE, 0x0AB0, "MCP79", "MCP79 Ethernet Controller", DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_CHECKSUM| DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL| DEV_HAS_PAUSEFRAME_TX_V3|DEV_HAS_STATISTICS_V3| DEV_HAS_TEST_EXTENDED|DEV_HAS_CORRECT_MACADDR| DEV_HAS_COLLISION_FIX|DEV_NEED_TX_LIMIT2|DEV_HAS_GEAR_MODE| DEV_NEED_PHY_INIT_FIX|DEV_NEED_MSI_FIX),
+	PCI_ROM(0x10DE, 0x0AB1, "MCP79", "MCP79 Ethernet Controller", DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_CHECKSUM| DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL| DEV_HAS_PAUSEFRAME_TX_V3|DEV_HAS_STATISTICS_V3| DEV_HAS_TEST_EXTENDED|DEV_HAS_CORRECT_MACADDR| DEV_HAS_COLLISION_FIX|DEV_NEED_TX_LIMIT2|DEV_HAS_GEAR_MODE| DEV_NEED_PHY_INIT_FIX|DEV_NEED_MSI_FIX),
+	PCI_ROM(0x10DE, 0x0AB2, "MCP79", "MCP79 Ethernet Controller", DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_CHECKSUM| DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL| DEV_HAS_PAUSEFRAME_TX_V3|DEV_HAS_STATISTICS_V3| DEV_HAS_TEST_EXTENDED|DEV_HAS_CORRECT_MACADDR| DEV_HAS_COLLISION_FIX|DEV_NEED_TX_LIMIT2|DEV_HAS_GEAR_MODE| DEV_NEED_PHY_INIT_FIX|DEV_NEED_MSI_FIX),
+	PCI_ROM(0x10DE, 0x0AB3, "MCP79", "MCP79 Ethernet Controller", DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_CHECKSUM| DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL| DEV_HAS_PAUSEFRAME_TX_V3|DEV_HAS_STATISTICS_V3| DEV_HAS_TEST_EXTENDED|DEV_HAS_CORRECT_MACADDR| DEV_HAS_COLLISION_FIX|DEV_NEED_TX_LIMIT2|DEV_HAS_GEAR_MODE| DEV_NEED_PHY_INIT_FIX|DEV_NEED_MSI_FIX),
+	PCI_ROM(0x10DE, 0x0D7D, "MCP89", "MCP89 Ethernet Controller", DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_CHECKSUM| DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL| DEV_HAS_PAUSEFRAME_TX_V3|DEV_HAS_STATISTICS_V3| DEV_HAS_TEST_EXTENDED|DEV_HAS_CORRECT_MACADDR| DEV_HAS_COLLISION_FIX|DEV_HAS_GEAR_MODE|DEV_NEED_PHY_INIT_FIX),
+};
+
+struct pci_driver forcedeth_driver __pci_driver = {
+	.ids		= forcedeth_nics,
+	.id_count	= ARRAY_SIZE(forcedeth_nics),
+	.probe		= forcedeth_probe,
+	.remove		= forcedeth_remove,
+};
+
diff --git a/src/drivers/net/forcedeth.h b/src/drivers/net/forcedeth.h
new file mode 100644
index 0000000..005f6cb
--- /dev/null
+++ b/src/drivers/net/forcedeth.h
@@ -0,0 +1,602 @@
+/*
+ * Copyright (c) 2010 Andrei Faur <da3drus at gmail.com>
+ *
+ * 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 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#ifndef _FORCEDETH_H_
+#define _FORCEDETH_H_
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+
+#define dma_addr_t unsigned long
+
+struct ring_desc {
+	u32 buf;
+	u32 flaglen;
+};
+
+struct ring_desc_ex {
+	u32 bufhigh;
+	u32 buflow;
+	u32 txvlan;
+	u32 flaglen;
+};
+
+union ring_type {
+	struct ring_desc *orig;
+	struct ring_desc_ex *ex;
+};
+
+#define DESC_VER_1	1
+#define DESC_VER_2	2
+#define DESC_VER_3	3
+
+#define RX_RING_SIZE		16
+#define TX_RING_SIZE		16
+#define RXTX_RING_SIZE		( ( RX_RING_SIZE ) + ( TX_RING_SIZE ) )
+#define RX_RING_MIN		128
+#define TX_RING_MIN		64
+#define RING_MAX_DESC_VER_1	1024
+#define RING_MAX_DESC_VER_2_3	16384
+
+#define NV_RX_ALLOC_PAD	(64)
+
+#define NV_RX_HEADERS	(64)
+
+#define RX_BUF_SZ		( ( ETH_FRAME_LEN ) + ( NV_RX_HEADERS ) )
+
+#define NV_PKTLIMIT_1	1500
+#define NV_PKTLIMIT_2	9100
+
+#define NV_LINK_POLL_FREQUENCY	128
+
+/* PHY defines */
+#define PHY_OUI_MARVELL		0x5043
+#define PHY_OUI_CICADA		0x03f1
+#define PHY_OUI_VITESSE		0x01c1
+#define PHY_OUI_REALTEK		0x0732
+#define PHY_OUI_REALTEK2	0x0020
+#define PHYID1_OUI_MASK	0x03ff
+#define PHYID1_OUI_SHFT	6
+#define PHYID2_OUI_MASK	0xfc00
+#define PHYID2_OUI_SHFT	10
+#define PHYID2_MODEL_MASK		0x03f0
+#define PHY_MODEL_REALTEK_8211		0x0110
+#define PHY_REV_MASK			0x0001
+#define PHY_REV_REALTEK_8211B		0x0000
+#define PHY_REV_REALTEK_8211C		0x0001
+#define PHY_MODEL_REALTEK_8201		0x0200
+#define PHY_MODEL_MARVELL_E3016		0x0220
+#define PHY_MARVELL_E3016_INITMASK	0x0300
+#define PHY_CICADA_INIT1	0x0f000
+#define PHY_CICADA_INIT2	0x0e00
+#define PHY_CICADA_INIT3	0x01000
+#define PHY_CICADA_INIT4	0x0200
+#define PHY_CICADA_INIT5	0x0004
+#define PHY_CICADA_INIT6	0x02000
+#define PHY_VITESSE_INIT_REG1	0x1f
+#define PHY_VITESSE_INIT_REG2	0x10
+#define PHY_VITESSE_INIT_REG3	0x11
+#define PHY_VITESSE_INIT_REG4	0x12
+#define PHY_VITESSE_INIT_MSK1	0xc
+#define PHY_VITESSE_INIT_MSK2	0x0180
+#define PHY_VITESSE_INIT1	0x52b5
+#define PHY_VITESSE_INIT2	0xaf8a
+#define PHY_VITESSE_INIT3	0x8
+#define PHY_VITESSE_INIT4	0x8f8a
+#define PHY_VITESSE_INIT5	0xaf86
+#define PHY_VITESSE_INIT6	0x8f86
+#define PHY_VITESSE_INIT7	0xaf82
+#define PHY_VITESSE_INIT8	0x0100
+#define PHY_VITESSE_INIT9	0x8f82
+#define PHY_VITESSE_INIT10	0x0
+#define PHY_REALTEK_INIT_REG1	0x1f
+#define PHY_REALTEK_INIT_REG2	0x19
+#define PHY_REALTEK_INIT_REG3	0x13
+#define PHY_REALTEK_INIT_REG4	0x14
+#define PHY_REALTEK_INIT_REG5	0x18
+#define PHY_REALTEK_INIT_REG6	0x11
+#define PHY_REALTEK_INIT_REG7	0x01
+#define PHY_REALTEK_INIT1	0x0000
+#define PHY_REALTEK_INIT2	0x8e00
+#define PHY_REALTEK_INIT3	0x0001
+#define PHY_REALTEK_INIT4	0xad17
+#define PHY_REALTEK_INIT5	0xfb54
+#define PHY_REALTEK_INIT6	0xf5c7
+#define PHY_REALTEK_INIT7	0x1000
+#define PHY_REALTEK_INIT8	0x0003
+#define PHY_REALTEK_INIT9	0x0008
+#define PHY_REALTEK_INIT10	0x0005
+#define PHY_REALTEK_INIT11	0x0200
+#define PHY_REALTEK_INIT_MSK1	0x0003
+
+#define PHY_GIGABIT	0x0100
+
+#define PHY_TIMEOUT	0x1
+#define PHY_ERROR	0x2
+
+#define PHY_100	0x1
+#define PHY_1000	0x2
+#define PHY_HALF	0x100
+
+
+#define NV_PAUSEFRAME_RX_CAPABLE	0x0001
+#define NV_PAUSEFRAME_TX_CAPABLE	0x0002
+#define NV_PAUSEFRAME_RX_ENABLE		0x0004
+#define NV_PAUSEFRAME_TX_ENABLE		0x0008
+#define NV_PAUSEFRAME_RX_REQ		0x0010
+#define NV_PAUSEFRAME_TX_REQ		0x0020
+#define NV_PAUSEFRAME_AUTONEG		0x0040
+
+/* MSI/MSI-X defines */
+#define NV_MSI_X_MAX_VECTORS  8
+#define NV_MSI_X_VECTORS_MASK 0x000f
+#define NV_MSI_CAPABLE        0x0010
+#define NV_MSI_X_CAPABLE      0x0020
+#define NV_MSI_ENABLED        0x0040
+#define NV_MSI_X_ENABLED      0x0080
+
+#define NV_MSI_X_VECTOR_ALL   0x0
+#define NV_MSI_X_VECTOR_RX    0x0
+#define NV_MSI_X_VECTOR_TX    0x1
+#define NV_MSI_X_VECTOR_OTHER 0x2
+
+#define NV_MSI_PRIV_OFFSET 0x68
+#define NV_MSI_PRIV_VALUE  0xffffffff
+
+
+#define NV_MIIBUSY_DELAY	50
+#define NV_MIIPHY_DELAY		10
+#define NV_MIIPHY_DELAYMAX	10000
+
+/* Hardware access */
+#define DEV_NEED_TIMERIRQ          0x0000001  /* set the timer irq flag in the irq mask */
+#define DEV_NEED_LINKTIMER         0x0000002  /* poll link settings. Relies on the timer irq */
+#define DEV_HAS_LARGEDESC          0x0000004  /* device supports jumbo frames and needs packet format 2 */
+#define DEV_HAS_HIGH_DMA           0x0000008  /* device supports 64bit dma */
+#define DEV_HAS_CHECKSUM           0x0000010  /* device supports tx and rx checksum offloads */
+#define DEV_HAS_VLAN               0x0000020  /* device supports vlan tagging and striping */
+#define DEV_HAS_MSI                0x0000040  /* device supports MSI */
+#define DEV_HAS_MSI_X              0x0000080  /* device supports MSI-X */
+#define DEV_HAS_POWER_CNTRL        0x0000100  /* device supports power savings */
+#define DEV_HAS_STATISTICS_V1      0x0000200  /* device supports hw statistics version 1 */
+#define DEV_HAS_STATISTICS_V2      0x0000600  /* device supports hw statistics version 2 */
+#define DEV_HAS_STATISTICS_V3      0x0000e00  /* device supports hw statistics version 3 */
+#define DEV_HAS_TEST_EXTENDED      0x0001000  /* device supports extended diagnostic test */
+#define DEV_HAS_MGMT_UNIT          0x0002000  /* device supports management unit */
+#define DEV_HAS_CORRECT_MACADDR    0x0004000  /* device supports correct mac address order */
+#define DEV_HAS_COLLISION_FIX      0x0008000  /* device supports tx collision fix */
+#define DEV_HAS_PAUSEFRAME_TX_V1   0x0010000  /* device supports tx pause frames version 1 */
+#define DEV_HAS_PAUSEFRAME_TX_V2   0x0020000  /* device supports tx pause frames version 2 */
+#define DEV_HAS_PAUSEFRAME_TX_V3   0x0040000  /* device supports tx pause frames version 3 */
+#define DEV_NEED_TX_LIMIT          0x0080000  /* device needs to limit tx */
+#define DEV_NEED_TX_LIMIT2         0x0180000  /* device needs to limit tx, expect for some revs */
+#define DEV_HAS_GEAR_MODE          0x0200000  /* device supports gear mode */
+#define DEV_NEED_PHY_INIT_FIX      0x0400000  /* device needs specific phy workaround */
+#define DEV_NEED_LOW_POWER_FIX     0x0800000  /* device needs special power up workaround */
+#define DEV_NEED_MSI_FIX           0x1000000  /* device needs msi workaround */
+
+#define FLAG_MASK_V1 0xffff0000
+#define FLAG_MASK_V2 0xffffc000
+#define LEN_MASK_V1 (0xffffffff ^ FLAG_MASK_V1)
+#define LEN_MASK_V2 (0xffffffff ^ FLAG_MASK_V2)
+
+#define NV_TX_LASTPACKET	(1<<16)
+#define NV_TX_RETRYERROR	(1<<19)
+#define NV_TX_RETRYCOUNT_MASK	(0xF<<20)
+#define NV_TX_FORCED_INTERRUPT	(1<<24)
+#define NV_TX_DEFERRED		(1<<26)
+#define NV_TX_CARRIERLOST	(1<<27)
+#define NV_TX_LATECOLLISION	(1<<28)
+#define NV_TX_UNDERFLOW		(1<<29)
+#define NV_TX_ERROR		(1<<30)
+#define NV_TX_VALID		(1<<31)
+
+#define NV_TX2_LASTPACKET	(1<<29)
+#define NV_TX2_RETRYERROR	(1<<18)
+#define NV_TX2_RETRYCOUNT_MASK	(0xF<<19)
+#define NV_TX2_FORCED_INTERRUPT	(1<<30)
+#define NV_TX2_DEFERRED		(1<<25)
+#define NV_TX2_CARRIERLOST	(1<<26)
+#define NV_TX2_LATECOLLISION	(1<<27)
+#define NV_TX2_UNDERFLOW	(1<<28)
+/* error and valid are the same for both */
+#define NV_TX2_ERROR		(1<<30)
+#define NV_TX2_VALID		(1<<31)
+#define NV_TX2_TSO		(1<<28)
+#define NV_TX2_TSO_SHIFT	14
+#define NV_TX2_TSO_MAX_SHIFT	14
+#define NV_TX2_TSO_MAX_SIZE	(1<<NV_TX2_TSO_MAX_SHIFT)
+#define NV_TX2_CHECKSUM_L3	(1<<27)
+#define NV_TX2_CHECKSUM_L4	(1<<26)
+
+#define NV_TX3_VLAN_TAG_PRESENT (1<<18)
+
+#define NV_RX_DESCRIPTORVALID	(1<<16)
+#define NV_RX_MISSEDFRAME	(1<<17)
+#define NV_RX_SUBSTRACT1	(1<<18)
+#define NV_RX_ERROR1		(1<<23)
+#define NV_RX_ERROR2		(1<<24)
+#define NV_RX_ERROR3		(1<<25)
+#define NV_RX_ERROR4		(1<<26)
+#define NV_RX_CRCERR		(1<<27)
+#define NV_RX_OVERFLOW		(1<<28)
+#define NV_RX_FRAMINGERR	(1<<29)
+#define NV_RX_ERROR		(1<<30)
+#define NV_RX_AVAIL		(1<<31)
+#define NV_RX_ERROR_MASK	(NV_RX_ERROR1|NV_RX_ERROR2|NV_RX_ERROR3|NV_RX_ERROR4|NV_RX_CRCERR|NV_RX_OVERFLOW|NV_RX_FRAMINGERR)
+
+#define NV_RX2_CHECKSUMMASK	(0x1C000000)
+#define NV_RX2_CHECKSUM_IP	(0x10000000)
+#define NV_RX2_CHECKSUM_IP_TCP	(0x14000000)
+#define NV_RX2_CHECKSUM_IP_UDP	(0x18000000)
+#define NV_RX2_DESCRIPTORVALID	(1<<29)
+#define NV_RX2_SUBSTRACT1	(1<<25)
+#define NV_RX2_ERROR1		(1<<18)
+#define NV_RX2_ERROR2		(1<<19)
+#define NV_RX2_ERROR3		(1<<20)
+#define NV_RX2_ERROR4		(1<<21)
+#define NV_RX2_CRCERR		(1<<22)
+#define NV_RX2_OVERFLOW		(1<<23)
+#define NV_RX2_FRAMINGERR	(1<<24)
+/* error and avail are the same for both */
+#define NV_RX2_ERROR		(1<<30)
+#define NV_RX2_AVAIL		(1<<31)
+#define NV_RX2_ERROR_MASK	(NV_RX2_ERROR1|NV_RX2_ERROR2|NV_RX2_ERROR3|NV_RX2_ERROR4|NV_RX2_CRCERR|NV_RX2_OVERFLOW|NV_RX2_FRAMINGERR)
+
+#define NV_RX3_VLAN_TAG_PRESENT (1<<16)
+#define NV_RX3_VLAN_TAG_MASK	(0x0000FFFF)
+
+/* Miscellaneous hardware related defines */
+#define NV_PCI_REGSZ_VER1	0x270
+#define NV_PCI_REGSZ_VER2	0x2d4
+#define NV_PCI_REGSZ_VER3	0x604
+#define NV_PCI_REGSZ_MAX	0x604
+
+/* various timeout delays: all in usec */
+#define NV_TXRX_RESET_DELAY	4
+#define NV_TXSTOP_DELAY1	10
+#define NV_TXSTOP_DELAY1MAX	500000
+#define NV_TXSTOP_DELAY2	100
+#define NV_RXSTOP_DELAY1	10
+#define NV_RXSTOP_DELAY1MAX	500000
+#define NV_RXSTOP_DELAY2	100
+#define NV_SETUP5_DELAY		5
+#define NV_SETUP5_DELAYMAX	50000
+#define NV_POWERUP_DELAY	5
+#define NV_POWERUP_DELAYMAX	5000
+#define NV_MIIBUSY_DELAY	50
+#define NV_MIIPHY_DELAY	10
+#define NV_MIIPHY_DELAYMAX	10000
+#define NV_MAC_RESET_DELAY	64
+
+#define NV_MSI_X_CAPABLE	0x0020
+
+#define MII_READ	(-1)
+
+struct forcedeth_private {
+	struct pci_device *pci_dev;
+	struct net_device *netdev;
+
+	void *mmio_addr;
+
+	u32 linkspeed;
+	int duplex;
+
+	int phyaddr;
+	unsigned int phy_oui;
+	unsigned int phy_rev;
+	unsigned int phy_model;
+
+	u16 gigabit;
+	u32 desc_ver;
+	u32 txrxctl_bits;
+	u32 tx_flags;
+	u32 irqmask;
+	u32 mac_in_use;
+	int mgmt_version;
+	int mgmt_sema;
+
+	/* rx specific fields */
+	union ring_type rx_ring;
+	struct io_buffer *rx_iobuf[RX_RING_SIZE];
+	int rx_curr;
+
+	/* tx specific fields */
+	union ring_type tx_ring;
+	struct io_buffer *tx_iobuf[TX_RING_SIZE];
+	int tx_fill_ctr;
+	int tx_curr;
+	int tx_tail;
+
+	/* flow control */
+	u32 pause_flags;
+
+	unsigned long link_poll_timer;
+
+	unsigned long driver_data;
+};
+
+enum {
+	NvRegIrqStatus = 0x000,
+#define NVREG_IRQSTAT_MIIEVENT	0x040
+#define NVREG_IRQSTAT_MASK		0x83ff
+	NvRegIrqMask = 0x004,
+#define NVREG_IRQ_RX_ERROR		0x0001
+#define NVREG_IRQ_RX			0x0002
+#define NVREG_IRQ_RX_NOBUF		0x0004
+#define NVREG_IRQ_TX_ERR		0x0008
+#define NVREG_IRQ_TX_OK			0x0010
+#define NVREG_IRQ_TIMER			0x0020
+#define NVREG_IRQ_LINK			0x0040
+#define NVREG_IRQ_RX_FORCED		0x0080
+#define NVREG_IRQ_TX_FORCED		0x0100
+#define NVREG_IRQ_RECOVER_ERROR		0x8200
+#define NVREG_IRQMASK_THROUGHPUT	0x00df
+#define NVREG_IRQMASK_CPU		0x0060
+#define NVREG_IRQ_TX_ALL		(NVREG_IRQ_TX_ERR|NVREG_IRQ_TX_OK|NVREG_IRQ_TX_FORCED)
+#define NVREG_IRQ_RX_ALL		(NVREG_IRQ_RX_ERROR|NVREG_IRQ_RX|NVREG_IRQ_RX_NOBUF|NVREG_IRQ_RX_FORCED)
+#define NVREG_IRQ_OTHER			(NVREG_IRQ_TIMER|NVREG_IRQ_LINK|NVREG_IRQ_RECOVER_ERROR)
+
+	NvRegUnknownSetupReg6 = 0x008,
+#define NVREG_UNKSETUP6_VAL		3
+
+/*
+ * NVREG_POLL_DEFAULT is the interval length of the timer source on the nic
+ * NVREG_POLL_DEFAULT=97 would result in an interval length of 1 ms
+ */
+	NvRegPollingInterval = 0x00c,
+#define NVREG_POLL_DEFAULT_THROUGHPUT	65535 /* backup tx cleanup if loop max reached */
+#define NVREG_POLL_DEFAULT_CPU	13
+	NvRegMSIMap0 = 0x020,
+	NvRegMSIMap1 = 0x024,
+	NvRegMSIIrqMask = 0x030,
+#define NVREG_MSI_VECTOR_0_ENABLED 0x01
+	NvRegMisc1 = 0x080,
+#define NVREG_MISC1_PAUSE_TX	0x01
+#define NVREG_MISC1_HD		0x02
+#define NVREG_MISC1_FORCE	0x3b0f3c
+
+	NvRegMacReset = 0x34,
+#define NVREG_MAC_RESET_ASSERT	0x0F3
+	NvRegTransmitterControl = 0x084,
+#define NVREG_XMITCTL_START	0x01
+#define NVREG_XMITCTL_MGMT_ST	0x40000000
+#define NVREG_XMITCTL_SYNC_MASK		0x000f0000
+#define NVREG_XMITCTL_SYNC_NOT_READY	0x0
+#define NVREG_XMITCTL_SYNC_PHY_INIT	0x00040000
+#define NVREG_XMITCTL_MGMT_SEMA_MASK	0x00000f00
+#define NVREG_XMITCTL_MGMT_SEMA_FREE	0x0
+#define NVREG_XMITCTL_HOST_SEMA_MASK	0x0000f000
+#define NVREG_XMITCTL_HOST_SEMA_ACQ	0x0000f000
+#define NVREG_XMITCTL_HOST_LOADED	0x00004000
+#define NVREG_XMITCTL_TX_PATH_EN	0x01000000
+#define NVREG_XMITCTL_DATA_START	0x00100000
+#define NVREG_XMITCTL_DATA_READY	0x00010000
+#define NVREG_XMITCTL_DATA_ERROR	0x00020000
+	NvRegTransmitterStatus = 0x088,
+#define NVREG_XMITSTAT_BUSY	0x01
+
+	NvRegPacketFilterFlags = 0x8c,
+#define NVREG_PFF_PAUSE_RX	0x08
+#define NVREG_PFF_ALWAYS	0x7F0000
+#define NVREG_PFF_PROMISC	0x80
+#define NVREG_PFF_MYADDR	0x20
+#define NVREG_PFF_LOOPBACK	0x10
+
+	NvRegOffloadConfig = 0x90,
+#define NVREG_OFFLOAD_HOMEPHY	0x601
+#define NVREG_OFFLOAD_NORMAL	RX_NIC_BUFSIZE
+	NvRegReceiverControl = 0x094,
+#define NVREG_RCVCTL_START	0x01
+#define NVREG_RCVCTL_RX_PATH_EN	0x01000000
+	NvRegReceiverStatus = 0x98,
+#define NVREG_RCVSTAT_BUSY	0x01
+
+	NvRegSlotTime = 0x9c,
+#define NVREG_SLOTTIME_LEGBF_ENABLED	0x80000000
+#define NVREG_SLOTTIME_10_100_FULL	0x00007f00
+#define NVREG_SLOTTIME_1000_FULL 	0x0003ff00
+#define NVREG_SLOTTIME_HALF		0x0000ff00
+#define NVREG_SLOTTIME_DEFAULT	 	0x00007f00
+#define NVREG_SLOTTIME_MASK		0x000000ff
+
+	NvRegTxDeferral = 0xA0,
+#define NVREG_TX_DEFERRAL_DEFAULT		0x15050f
+#define NVREG_TX_DEFERRAL_RGMII_10_100		0x16070f
+#define NVREG_TX_DEFERRAL_RGMII_1000		0x14050f
+#define NVREG_TX_DEFERRAL_RGMII_STRETCH_10	0x16190f
+#define NVREG_TX_DEFERRAL_RGMII_STRETCH_100	0x16300f
+#define NVREG_TX_DEFERRAL_MII_STRETCH		0x152000
+	NvRegRxDeferral = 0xA4,
+#define NVREG_RX_DEFERRAL_DEFAULT	0x16
+	NvRegMacAddrA = 0xA8,
+	NvRegMacAddrB = 0xAC,
+	NvRegMulticastAddrA = 0xB0,
+#define NVREG_MCASTADDRA_FORCE	0x01
+	NvRegMulticastAddrB = 0xB4,
+	NvRegMulticastMaskA = 0xB8,
+#define NVREG_MCASTMASKA_NONE		0xffffffff
+	NvRegMulticastMaskB = 0xBC,
+#define NVREG_MCASTMASKB_NONE		0xffff
+
+	NvRegPhyInterface = 0xC0,
+#define PHY_RGMII		0x10000000
+	NvRegBackOffControl = 0xC4,
+#define NVREG_BKOFFCTRL_DEFAULT			0x70000000
+#define NVREG_BKOFFCTRL_SEED_MASK		0x000003ff
+#define NVREG_BKOFFCTRL_SELECT			24
+#define NVREG_BKOFFCTRL_GEAR			12
+
+	NvRegTxRingPhysAddr = 0x100,
+	NvRegRxRingPhysAddr = 0x104,
+	NvRegRingSizes = 0x108,
+#define NVREG_RINGSZ_TXSHIFT 0
+#define NVREG_RINGSZ_RXSHIFT 16
+	NvRegTransmitPoll = 0x10c,
+#define NVREG_TRANSMITPOLL_MAC_ADDR_REV	0x00008000
+	NvRegLinkSpeed = 0x110,
+#define NVREG_LINKSPEED_FORCE 0x10000
+#define NVREG_LINKSPEED_10	1000
+#define NVREG_LINKSPEED_100	100
+#define NVREG_LINKSPEED_1000	50
+#define NVREG_LINKSPEED_MASK	(0xFFF)
+	NvRegUnknownSetupReg5 = 0x130,
+#define NVREG_UNKSETUP5_BIT31	(1<<31)
+	NvRegTxWatermark = 0x13c,
+#define NVREG_TX_WM_DESC1_DEFAULT	0x0200010
+#define NVREG_TX_WM_DESC2_3_DEFAULT	0x1e08000
+#define NVREG_TX_WM_DESC2_3_1000	0xfe08000
+	NvRegTxRxControl = 0x144,
+#define NVREG_TXRXCTL_KICK	0x0001
+#define NVREG_TXRXCTL_BIT1	0x0002
+#define NVREG_TXRXCTL_BIT2	0x0004
+#define NVREG_TXRXCTL_IDLE	0x0008
+#define NVREG_TXRXCTL_RESET	0x0010
+#define NVREG_TXRXCTL_RXCHECK	0x0400
+#define NVREG_TXRXCTL_DESC_1	0
+#define NVREG_TXRXCTL_DESC_2	0x002100
+#define NVREG_TXRXCTL_DESC_3	0xc02200
+#define NVREG_TXRXCTL_VLANSTRIP 0x00040
+#define NVREG_TXRXCTL_VLANINS	0x00080
+	NvRegTxRingPhysAddrHigh = 0x148,
+	NvRegRxRingPhysAddrHigh = 0x14C,
+	NvRegTxPauseFrame = 0x170,
+#define NVREG_TX_PAUSEFRAME_DISABLE	0x0fff0080
+#define NVREG_TX_PAUSEFRAME_ENABLE_V1	0x01800010
+#define NVREG_TX_PAUSEFRAME_ENABLE_V2	0x056003f0
+#define NVREG_TX_PAUSEFRAME_ENABLE_V3	0x09f00880
+	NvRegTxPauseFrameLimit = 0x174,
+#define NVREG_TX_PAUSEFRAMELIMIT_ENABLE	0x00010000
+	NvRegMIIStatus = 0x180,
+#define NVREG_MIISTAT_ERROR		0x0001
+#define NVREG_MIISTAT_LINKCHANGE	0x0008
+#define NVREG_MIISTAT_MASK_RW		0x0007
+#define NVREG_MIISTAT_MASK_ALL		0x000f
+	NvRegMIIMask = 0x184,
+#define NVREG_MII_LINKCHANGE		0x0008
+
+	NvRegAdapterControl = 0x188,
+#define NVREG_ADAPTCTL_START	0x02
+#define NVREG_ADAPTCTL_LINKUP	0x04
+#define NVREG_ADAPTCTL_PHYVALID	0x40000
+#define NVREG_ADAPTCTL_RUNNING	0x100000
+#define NVREG_ADAPTCTL_PHYSHIFT	24
+	NvRegMIISpeed = 0x18c,
+#define NVREG_MIISPEED_BIT8	(1<<8)
+#define NVREG_MIIDELAY	5
+	NvRegMIIControl = 0x190,
+#define NVREG_MIICTL_INUSE	0x08000
+#define NVREG_MIICTL_WRITE	0x00400
+#define NVREG_MIICTL_ADDRSHIFT	5
+	NvRegMIIData = 0x194,
+	NvRegTxUnicast = 0x1a0,
+	NvRegTxMulticast = 0x1a4,
+	NvRegTxBroadcast = 0x1a8,
+	NvRegWakeUpFlags = 0x200,
+#define NVREG_WAKEUPFLAGS_VAL		0x7770
+#define NVREG_WAKEUPFLAGS_BUSYSHIFT	24
+#define NVREG_WAKEUPFLAGS_ENABLESHIFT	16
+#define NVREG_WAKEUPFLAGS_D3SHIFT	12
+#define NVREG_WAKEUPFLAGS_D2SHIFT	8
+#define NVREG_WAKEUPFLAGS_D1SHIFT	4
+#define NVREG_WAKEUPFLAGS_D0SHIFT	0
+#define NVREG_WAKEUPFLAGS_ACCEPT_MAGPAT		0x01
+#define NVREG_WAKEUPFLAGS_ACCEPT_WAKEUPPAT	0x02
+#define NVREG_WAKEUPFLAGS_ACCEPT_LINKCHANGE	0x04
+#define NVREG_WAKEUPFLAGS_ENABLE	0x1111
+
+	NvRegMgmtUnitGetVersion = 0x204,
+#define NVREG_MGMTUNITGETVERSION     	0x01
+	NvRegMgmtUnitVersion = 0x208,
+#define NVREG_MGMTUNITVERSION		0x08
+	NvRegPowerCap = 0x268,
+#define NVREG_POWERCAP_D3SUPP	(1<<30)
+#define NVREG_POWERCAP_D2SUPP	(1<<26)
+#define NVREG_POWERCAP_D1SUPP	(1<<25)
+	NvRegPowerState = 0x26c,
+#define NVREG_POWERSTATE_POWEREDUP	0x8000
+#define NVREG_POWERSTATE_VALID		0x0100
+#define NVREG_POWERSTATE_MASK		0x0003
+#define NVREG_POWERSTATE_D0		0x0000
+#define NVREG_POWERSTATE_D1		0x0001
+#define NVREG_POWERSTATE_D2		0x0002
+#define NVREG_POWERSTATE_D3		0x0003
+	NvRegMgmtUnitControl = 0x278,
+#define NVREG_MGMTUNITCONTROL_INUSE	0x20000
+	NvRegTxCnt = 0x280,
+	NvRegTxZeroReXmt = 0x284,
+	NvRegTxOneReXmt = 0x288,
+	NvRegTxManyReXmt = 0x28c,
+	NvRegTxLateCol = 0x290,
+	NvRegTxUnderflow = 0x294,
+	NvRegTxLossCarrier = 0x298,
+	NvRegTxExcessDef = 0x29c,
+	NvRegTxRetryErr = 0x2a0,
+	NvRegRxFrameErr = 0x2a4,
+	NvRegRxExtraByte = 0x2a8,
+	NvRegRxLateCol = 0x2ac,
+	NvRegRxRunt = 0x2b0,
+	NvRegRxFrameTooLong = 0x2b4,
+	NvRegRxOverflow = 0x2b8,
+	NvRegRxFCSErr = 0x2bc,
+	NvRegRxFrameAlignErr = 0x2c0,
+	NvRegRxLenErr = 0x2c4,
+	NvRegRxUnicast = 0x2c8,
+	NvRegRxMulticast = 0x2cc,
+	NvRegRxBroadcast = 0x2d0,
+	NvRegTxDef = 0x2d4,
+	NvRegTxFrame = 0x2d8,
+	NvRegRxCnt = 0x2dc,
+	NvRegTxPause = 0x2e0,
+	NvRegRxPause = 0x2e4,
+	NvRegRxDropFrame = 0x2e8,
+	NvRegVlanControl = 0x300,
+#define NVREG_VLANCONTROL_ENABLE	0x2000
+	NvRegMSIXMap0 = 0x3e0,
+	NvRegMSIXMap1 = 0x3e4,
+	NvRegMSIXIrqStatus = 0x3f0,
+
+	NvRegPowerState2 = 0x600,
+#define NVREG_POWERSTATE2_POWERUP_MASK		0x0F15
+#define NVREG_POWERSTATE2_POWERUP_REV_A3	0x0001
+#define NVREG_POWERSTATE2_PHY_RESET		0x0004
+#define NVREG_POWERSTATE2_GATE_CLOCKS		0x0F00
+};
+
+enum {
+	NV_OPTIMIZATION_MODE_THROUGHPUT,
+	NV_OPTIMIZATION_MODE_CPU,
+	NV_OPTIMIZATION_MODE_DYNAMIC
+};
+
+enum {
+	NV_CROSSOVER_DETECTION_DISABLED,
+	NV_CROSSOVER_DETECTION_ENABLED
+};
+
+
+#define NV_SETUP_RX_RING	0x01
+#define NV_SETUP_TX_RING	0x02
+
+#define NV_RESTART_TX		0x1
+#define NV_RESTART_RX		0x2
+
+#endif /* _FORCEDETH_H_ */
+
-- 
1.6.3.3



More information about the gPXE-devel mailing list