Update support to linux 2.6.10 changes - Untested
authortimlegge <timlegge>
Sun, 15 May 2005 02:29:56 +0000 (02:29 +0000)
committertimlegge <timlegge>
Sun, 15 May 2005 02:29:56 +0000 (02:29 +0000)
src/drivers/net/forcedeth.c

index 326c4cc..efb59b2 100644 (file)
 *      (C) 2003 Manfred Spraul
 *              See Linux Driver for full information
 *      
-*      Linux Driver Version 0.22, 19 Jan 2004
+*      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
 ***************************************************************************/
 
 #include "pci.h"
 /* Include timer support functions */
 #include "timer.h"
+#include "mii.h"
 
-#define drv_version "v1.1"
-#define drv_date "02-03-2004"
+#define drv_version "v1.2"
+#define drv_date "05-14-2005"
 
 //#define TFTM_DEBUG
 #ifdef TFTM_DEBUG
 #define dprintf(x)
 #endif
 
-typedef unsigned char u8;
-typedef signed char s8;
-typedef unsigned short u16;
-typedef signed short s16;
-typedef unsigned int u32;
-typedef signed int s32;
+#define ETH_DATA_LEN   1500
 
 /* Condensed operations for readability. */
 #define virt_to_le32desc(addr)  cpu_to_le32(virt_to_bus(addr))
@@ -73,22 +71,35 @@ typedef signed int s32;
 
 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
 
 
 /*
  * Hardware access:
  */
 
-#define DEV_NEED_LASTPACKET1   0x0001
-#define DEV_IRQMASK_1          0x0002
-#define DEV_IRQMASK_2          0x0004
-#define DEV_NEED_TIMERIRQ      0x0008
+#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
@@ -98,7 +109,7 @@ enum {
 #define NVREG_IRQ_TX1                  0x0100
 #define NVREG_IRQMASK_WANTED_1         0x005f
 #define NVREG_IRQMASK_WANTED_2         0x0147
-#define NVREG_IRQ_UNKNOWN              (~(NVREG_IRQ_RX|NVREG_IRQ_RX_NOBUF|NVREG_IRQ_TX_ERR|NVREG_IRQ_TX2|NVREG_IRQ_TIMER|NVREG_IRQ_LINK|NVREG_IRQ_TX1))
+#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
@@ -125,7 +136,7 @@ enum {
 
        NvRegOffloadConfig = 0x90,
 #define NVREG_OFFLOAD_HOMEPHY  0x601
-#define NVREG_OFFLOAD_NORMAL   0x5ee
+#define NVREG_OFFLOAD_NORMAL   RX_NIC_BUFSIZE
        NvRegReceiverControl = 0x094,
 #define NVREG_RCVCTL_START     0x01
        NvRegReceiverStatus = 0x98,
@@ -134,6 +145,8 @@ enum {
        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
@@ -147,6 +160,9 @@ enum {
        NvRegMulticastMaskA = 0xB8,
        NvRegMulticastMaskB = 0xBC,
 
+       NvRegPhyInterface = 0xC0,
+#define PHY_RGMII              0x10000000
+
        NvRegTxRingPhysAddr = 0x100,
        NvRegRxRingPhysAddr = 0x104,
        NvRegRingSizes = 0x108,
@@ -155,12 +171,12 @@ enum {
        NvRegUnknownTransmitterReg = 0x10c,
        NvRegLinkSpeed = 0x110,
 #define NVREG_LINKSPEED_FORCE 0x10000
-#define NVREG_LINKSPEED_10     10
+#define NVREG_LINKSPEED_10     1000
 #define NVREG_LINKSPEED_100    100
-#define NVREG_LINKSPEED_1000   1000
+#define NVREG_LINKSPEED_1000   50
        NvRegUnknownSetupReg5 = 0x130,
 #define NVREG_UNKSETUP5_BIT31  (1<<31)
-       NvRegUnknownSetupReg3 = 0x134,
+       NvRegUnknownSetupReg3 = 0x13c,
 #define NVREG_UNKSETUP3_VAL1   0x200010
        NvRegTxRxControl = 0x144,
 #define NVREG_TXRXCTL_KICK     0x0001
@@ -168,6 +184,7 @@ enum {
 #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
@@ -179,15 +196,15 @@ enum {
        NvRegAdapterControl = 0x188,
 #define NVREG_ADAPTCTL_START   0x02
 #define NVREG_ADAPTCTL_LINKUP  0x04
-#define NVREG_ADAPTCTL_PHYVALID        0x4000
+#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     0x10000
-#define NVREG_MIICTL_WRITE     0x08000
+#define NVREG_MIICTL_INUSE     0x08000
+#define NVREG_MIICTL_WRITE     0x00400
 #define NVREG_MIICTL_ADDRSHIFT 5
        NvRegMIIData = 0x194,
        NvRegWakeUpFlags = 0x200,
@@ -201,6 +218,7 @@ enum {
 #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,
@@ -218,30 +236,61 @@ enum {
 #define NVREG_POWERSTATE_D3            0x0003
 };
 
-
-
-#define NV_TX_LASTPACKET       (1<<0)
-#define NV_TX_RETRYERROR       (1<<3)
-#define NV_TX_LASTPACKET1      (1<<8)
-#define NV_TX_DEFERRED         (1<<10)
-#define NV_TX_CARRIERLOST      (1<<11)
-#define NV_TX_LATECOLLISION    (1<<12)
-#define NV_TX_UNDERFLOW                (1<<13)
-#define NV_TX_ERROR            (1<<14)
-#define NV_TX_VALID            (1<<15)
-
-#define NV_RX_DESCRIPTORVALID  (1<<0)
-#define NV_RX_MISSEDFRAME      (1<<1)
-#define NV_RX_SUBSTRACT1       (1<<3)
-#define NV_RX_ERROR1           (1<<7)
-#define NV_RX_ERROR2           (1<<8)
-#define NV_RX_ERROR3           (1<<9)
-#define NV_RX_ERROR4           (1<<10)
-#define NV_RX_CRCERR           (1<<11)
-#define NV_RX_OVERFLOW         (1<<12)
-#define NV_RX_FRAMINGERR       (1<<13)
-#define NV_RX_ERROR            (1<<14)
-#define NV_RX_AVAIL            (1<<15)
+#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
@@ -266,27 +315,73 @@ enum {
 #define NV_WAKEUPMASKENTRIES   4
 
 /* General driver defaults */
-#define NV_WATCHDOG_TIMEO      (2*HZ)
-#define DEFAULT_MTU            1500    /* also maximum supported, at least for now */
+#define NV_WATCHDOG_TIMEO      (5*HZ)
 
 #define RX_RING                4
 #define TX_RING                2
-/* limited to 1 packet until we understand NV_TX_LASTPACKET */
-#define TX_LIMIT_STOP  10
-#define TX_LIMIT_START 5
+
+/* 
+ * 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         (DEFAULT_MTU + 64)
+#define RX_NIC_BUFSIZE         (ETH_DATA_LEN + 64)
 /* even more slack */
-#define RX_ALLOC_BUFSIZE       (DEFAULT_MTU + 128)
+#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
+
+/* FIXME: MII defines that should be added to <linux/mii.h> */
+#define MII_1000BT_CR  0x09
+#define MII_1000BT_SR  0x0a
+#define ADVERTISE_1000FULL     0x0200
+#define ADVERTISE_1000HALF     0x0100
+#define LPA_1000FULL   0x0800
+#define LPA_1000HALF   0x0400
+
+/* Big endian: should work, but is untested */
 struct ring_desc {
        u32 PacketBuffer;
-       u16 Length;
-       u16 Flags;
+       u32 FlagLen;
 };
 
 
@@ -314,11 +409,15 @@ struct forcedeth_private {
        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);
         */
@@ -335,7 +434,7 @@ struct forcedeth_private {
        unsigned int next_tx, nic_tx;
        struct sk_buff *tx_skbuff[TX_RING];
        u32 tx_dma[TX_RING];
-       u16 tx_flags;
+       u32 tx_flags;
 } npx;
 
 static struct forcedeth_private *np;
@@ -345,6 +444,13 @@ 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)
 {
@@ -448,6 +554,115 @@ static int mii_rw(struct nic *nic __unused, int addr, int miireg,
        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_1000BT_CR, 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_1000BT_CR, 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;
@@ -507,11 +722,12 @@ static void txrx_reset(struct nic *nic __unused)
        u8 *base = (u8 *) BASE;
 
        dprintf(("txrx_reset\n"));
-       writel(NVREG_TXRXCTL_BIT2 | NVREG_TXRXCTL_RESET,
+       writel(NVREG_TXRXCTL_BIT2 | NVREG_TXRXCTL_RESET | np->desc_ver,
               base + NvRegTxRxControl);
+
        pci_push(base);
        udelay(NV_TXRX_RESET_DELAY);
-       writel(NVREG_TXRXCTL_BIT2, base + NvRegTxRxControl);
+       writel(NVREG_TXRXCTL_BIT2 | np->desc_ver, base + NvRegTxRxControl);
        pci_push(base);
 }
 
@@ -529,9 +745,9 @@ static int alloc_rx(struct nic *nic __unused)
                //int nr = refill_rx % RX_RING;
                rx_ring[i].PacketBuffer =
                    virt_to_le32desc(&rxb[i * RX_NIC_BUFSIZE]);
-               rx_ring[i].Length = cpu_to_le16(RX_NIC_BUFSIZE);
                wmb();
-               rx_ring[i].Flags = cpu_to_le16(NV_RX_AVAIL);
+               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++;
@@ -544,8 +760,57 @@ static int alloc_rx(struct nic *nic __unused)
 
 static int update_linkspeed(struct nic *nic)
 {
-       int adv, lpa, newdup;
+       int adv, lpa;
        u32 newls;
+       int newdup = np->duplex;
+       int mii_status;
+       int retval = 0;
+       u32 control_1000, status_1000, phyreg;
+       u8 *base = (u8 *) BASE;
+
+       /* 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 (!(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_1000BT_CR, MII_READ);
+               status_1000 =
+                   mii_rw(nic, np->phyaddr, MII_1000BT_SR, MII_READ);
+
+               if ((control_1000 & ADVERTISE_1000FULL) &&
+                   (status_1000 & LPA_1000FULL)) {
+                       printf
+                           ("nv_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",
@@ -570,14 +835,66 @@ static int update_linkspeed(struct nic *nic)
                newls = NVREG_LINKSPEED_FORCE | NVREG_LINKSPEED_10;
                newdup = 0;
        }
-       if (np->duplex != newdup || np->linkspeed != newls) {
-               np->duplex = newdup;
-               np->linkspeed = newls;
-               return 1;
+
+      set_speed:
+       if (np->duplex == newdup && np->linkspeed == newls)
+               return retval;
+
+       printf("changing link setting from %d/%d to %d/%d.\n",
+              np->linkspeed, np->duplex, newls, newdup);
+
+       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 0;
 }
 
+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();
+               //  }
+       }
+}
 
 
 static int init_ring(struct nic *nic)
@@ -585,15 +902,13 @@ 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].Flags = 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].Flags = 0;
-       }
+       for (i = 0; i < RX_RING; i++)
+               rx_ring[i].FlagLen = 0;
        return alloc_rx(nic);
 }
 
@@ -647,7 +962,14 @@ static int forcedeth_reset(struct nic *nic)
        writel(0, base + NvRegMulticastMaskA);
        writel(0, base + NvRegMulticastMaskB);
        writel(0, base + NvRegPacketFilterFlags);
+
+       writel(0, base + NvRegTransmitterControl);
+       writel(0, base + NvRegReceiverControl);
+
        writel(0, base + NvRegAdapterControl);
+
+       oom = init_ring(nic);
+
        writel(0, base + NvRegLinkSpeed);
        writel(0, base + NvRegUnknownTransmitterReg);
        txrx_reset(nic);
@@ -655,7 +977,6 @@ static int forcedeth_reset(struct nic *nic)
 
        /* 2) initialize descriptor rings */
        np->in_shutdown = 0;
-       oom = init_ring(nic);
 
        /* 3) set mac address */
        {
@@ -671,51 +992,38 @@ static int forcedeth_reset(struct nic *nic)
                writel(mac[1], base + NvRegMacAddrB);
        }
 
-       /* 4) continue setup */
+       /* 4) start packet processing */
+       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(0, base + NvRegTxRxControl);
+       writel(np->desc_ver, base + NvRegTxRxControl);
        pci_push(base);
-       writel(NVREG_TXRXCTL_BIT1, base + NvRegTxRxControl);
-
+       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);
-
-       /* 5) Find a suitable PHY */
-       writel(NVREG_MIISPEED_BIT8 | NVREG_MIIDELAY, base + NvRegMIISpeed);
-       for (i = 1; i < 32; i++) {
-               int id1, id2;
-
-               id1 = mii_rw(nic, i, MII_PHYSID1, MII_READ);
-               if (id1 < 0)
-                       continue;
-               id2 = mii_rw(nic, i, MII_PHYSID2, MII_READ);
-               if (id2 < 0)
-                       continue;
-               dprintf(("open: Found PHY %04x:%04x at address %d.\n",
-                        id1, id2, i));
-               np->phyaddr = i;
-
-               update_linkspeed(nic);
 
-               break;
-       }
-       if (i == 32) {
-               printf("open: failing due to lack of suitable PHY.\n");
-               ret = -1;
-               goto out_drain;
-       }
+       writel(0, base + NvRegUnknownSetupReg4);
+//       writel(NVREG_IRQSTAT_MASK, base + NvRegIrqStatus);
+       writel(NVREG_MIISTAT_MASK2, base + NvRegMIIStatus);
 
        printf("%d-Mbs Link, %s-Duplex\n",
               np->linkspeed & NVREG_LINKSPEED_10 ? 10 : 100,
               np->duplex ? "Full" : "Half");
        /* 6) continue setup */
-       writel(NVREG_MISC1_FORCE | (np->duplex ? 0 : NVREG_MISC1_HD),
-              base + NvRegMisc1);
+       writel(NVREG_MISC1_FORCE | NVREG_MISC1_HD, base + NvRegMisc1);
        writel(readl(base + NvRegTransmitterStatus),
               base + NvRegTransmitterStatus);
        writel(NVREG_PFF_ALWAYS, base + NvRegPacketFilterFlags);
@@ -734,26 +1042,17 @@ static int forcedeth_reset(struct nic *nic)
        writel(NVREG_UNKSETUP6_VAL, base + NvRegUnknownSetupReg6);
        writel((np->
                phyaddr << NVREG_ADAPTCTL_PHYSHIFT) |
-              NVREG_ADAPTCTL_PHYVALID, base + NvRegAdapterControl);
+              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);
 
-       /* 7) start packet processing */
-       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);
-
        i = readl(base + NvRegPowerState);
-       if ((i & NVREG_POWERSTATE_POWEREDUP) == 0) {
+       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,
@@ -778,12 +1077,23 @@ static int forcedeth_reset(struct nic *nic)
               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);
+               printf("startup: got 0x%hX.\n", miistat);
+       }
+       ret = update_linkspeed(nic);
+
        //start_rx(nic);
        start_tx(nic);
 
-       if (!
-           (mii_rw(nic, np->phyaddr, MII_BMSR, MII_READ) &
-            BMSR_ANEGCOMPLETE)) {
+       if (ret) {
+               //Start Connection netif_carrier_on(dev);
+       } else {
                printf("no link during initialization.\n");
        }
 
@@ -803,25 +1113,34 @@ static int forcedeth_poll(struct nic *nic, int retrieve)
        /* nic->packet should contain data on return */
        /* nic->packetlen should contain length of data */
 
-       struct ring_desc *prd;
        int len;
        int i;
+       u32 Flags;
 
        i = np->cur_rx % RX_RING;
-       prd = &rx_ring[i];
+//      prd = &rx_ring[i];
+
+       Flags = le32_to_cpu(np->rx_ring[i].FlagLen);
+       len = nv_descr_getlength(&np->rx_ring[i], np->desc_ver);
 
-       if ( ! (prd->Flags & cpu_to_le16(NV_RX_DESCRIPTORVALID)) ) {
-         return 0;
+       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;
+       if (!retrieve)
+               return 1;
 
        /* got a valid packet - forward it to the network core */
-       len = cpu_to_le16(prd->Length);
        nic->packetlen = len;
        //hex_dump(rxb + (i * RX_NIC_BUFSIZE), len);
-       memcpy(nic->packet, rxb +
-              (i * RX_NIC_BUFSIZE), nic->packetlen);
+       memcpy(nic->packet, rxb + (i * RX_NIC_BUFSIZE), nic->packetlen);
 
        wmb();
        np->cur_rx++;
@@ -861,14 +1180,13 @@ static void forcedeth_transmit(struct nic *nic, const char *d,   /* Destination */
                ptxb[s++] = '\0';
 
        tx_ring[nr].PacketBuffer = (u32) virt_to_le32desc(ptxb);
-       tx_ring[nr].Length = cpu_to_le16(s - 1);
 
        wmb();
-       tx_ring[nr].Flags = np->tx_flags;
+       tx_ring[nr].FlagLen = cpu_to_le32((s - 1) | np->tx_flags);
 
-       writel(NVREG_TXRXCTL_KICK, base + NvRegTxRxControl);
+       writel(NVREG_TXRXCTL_KICK | np->desc_ver, base + NvRegTxRxControl);
        pci_push(base);
-       tx_ring[nr].Flags = np->tx_flags;
+       tx_ring[nr].FlagLen = np->tx_flags;
        np->next_tx++;
 }
 
@@ -907,16 +1225,17 @@ static void forcedeth_disable(struct dev *dev __unused)
 /**************************************************************************
 IRQ - Enable, Disable, or Force interrupts
 ***************************************************************************/
-static void forcedeth_irq(struct nic *nic __unused, irq_action_t action __unused)
+static void forcedeth_irq(struct nic *nic __unused,
+                         irq_action_t action __unused)
 {
-  switch ( action ) {
-  case DISABLE :
-    break;
-  case ENABLE :
-    break;
-  case FORCE :
-    break;
-  }
+       switch (action) {
+       case DISABLE:
+               break;
+       case ENABLE:
+               break;
+       case FORCE:
+               break;
+       }
 }
 
 /**************************************************************************
@@ -931,6 +1250,7 @@ static int forcedeth_probe(struct dev *dev, struct pci_device *pci)
        unsigned long addr;
        int sz;
        u8 *base;
+       int i;
 
        if (pci->ioaddr == 0)
                return 0;
@@ -938,7 +1258,7 @@ static int forcedeth_probe(struct dev *dev, struct pci_device *pci)
        printf("forcedeth.c: Found %s, vendor=0x%hX, device=0x%hX\n",
               pci->name, pci->vendor, pci->dev_id);
 
-       nic->irqno  = 0;
+       nic->irqno = 0;
        nic->ioaddr = pci->ioaddr & ~3;
 
        /* point to private storage */
@@ -953,6 +1273,15 @@ static int forcedeth_probe(struct dev *dev, struct pci_device *pci)
        BASE = (unsigned long) ioremap(addr, sz);
        if (!BASE)
                return 0;
+
+       /* handle different descriptor versions */
+       if (pci->dev_id == PCI_DEVICE_ID_NVIDIA_NVENET_1 ||
+           pci->dev_id == PCI_DEVICE_ID_NVIDIA_NVENET_2 ||
+           pci->dev_id == 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; 
 
@@ -988,52 +1317,137 @@ static int forcedeth_probe(struct dev *dev, struct pci_device *pci)
        }
 #endif
        printf("%s: MAC Address %!, ", pci->name, 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;
+       }
 
-       np->tx_flags =
-           cpu_to_le16(NV_TX_LASTPACKET | NV_TX_LASTPACKET1 |
-                       NV_TX_VALID);
        switch (pci->dev_id) {
        case 0x01C3:            // nforce
-               np->irqmask = NVREG_IRQMASK_WANTED_2;
-               np->irqmask |= NVREG_IRQ_TIMER;
+               // 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:            // nforce2
-               np->tx_flags |= cpu_to_le16(NV_TX_LASTPACKET1);
+       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 0x00D6:            // nforce3
-               np->tx_flags |= cpu_to_le16(NV_TX_LASTPACKET1);
+       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;
+               printf
+                   ("%s: open: Found PHY %hX:%hX at address %d.\n",
+                    pci->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->name);
+       }
 
+       if (i != 32) {
+               /* reset it */
+               phy_init(nic);
        }
        dprintf(("%s: forcedeth.c: subsystem: %hX:%hX bound to %s\n",
                 pci->name, pci->vendor, pci->dev_id, pci->name));
-
        forcedeth_reset(nic);
 //      if (board_found && valid_link)
        /* point to NIC specific routines */
        dev->disable = forcedeth_disable;
        nic->poll = forcedeth_poll;
        nic->transmit = forcedeth_transmit;
-       nic->irq    = forcedeth_irq;
+       nic->irq = forcedeth_irq;
        return 1;
 //      }
        /* else */
 }
 
 static struct pci_id forcedeth_nics[] = {
-       PCI_ROM(0x10de, 0x01C3, "nforce", "nForce Ethernet Controller"),
-       PCI_ROM(0x10de, 0x0066, "nforce2", "nForce2 Ethernet Controller"),
-       PCI_ROM(0x10de, 0x00D6, "nforce3", "nForce3 Ethernet Controller"),
+       PCI_ROM(0x10de, 0x01C3, "nforce",
+               "nForce NVENET_1 Ethernet Controller"),
+       PCI_ROM(0x10de, 0x0066, "nforce2",
+               "nForce NVENET_2 Ethernet Controller"),
+       PCI_ROM(0x10de, 0x00D6, "nforce3",
+               "nForce NVENET_3 Ethernet Controller"),
+       PCI_ROM(0x10de, 0x0086, "nforce",
+               "nForce NVENET_4 Ethernet Controller"),
+       PCI_ROM(0x10de, 0x008c, "nforce2",
+               "nForce NVENET_5 Ethernet Controller"),
+       PCI_ROM(0x10de, 0x00e6, "nforce3",
+               "nForce NVENET_6 Ethernet Controller"),
+       PCI_ROM(0x10de, 0x00df, "nforce",
+               "nForce NVENET_7 Ethernet Controller"),
+       PCI_ROM(0x10de, 0x0056, "nforce2",
+               "nForce NVENET_8 Ethernet Controller"),
+       PCI_ROM(0x10de, 0x0057, "nforce3",
+               "nForce NVENET_9 Ethernet Controller"),
+       PCI_ROM(0x10de, 0x0037, "nforce",
+               "nForce NVENET_10 Ethernet Controller"),
+       PCI_ROM(0x10de, 0x0038, "nforce2",
+               "nForce NVENET_11 Ethernet Controller"),
 };
-
 static struct pci_driver forcedeth_driver __pci_driver = {
-       .type = NIC_DRIVER,
-       .name = "forcedeth",
-       .probe = forcedeth_probe,
-       .ids = forcedeth_nics,
-       .id_count = sizeof(forcedeth_nics) / sizeof(forcedeth_nics[0]),
-       .class = 0,
+       .type = NIC_DRIVER,.name = "forcedeth",.probe =
+           forcedeth_probe,.ids =
+           forcedeth_nics,.id_count =
+           sizeof(forcedeth_nics) / sizeof(forcedeth_nics[0]),.class = 0,
 };