wimax/i2400m: firmware_check() encodes the firmware version in i2400m->fw_version
[safe/jmp/linux-2.6] / drivers / net / forcedeth.c
index eeb55ed..e3b7305 100644 (file)
@@ -13,7 +13,7 @@
  * Copyright (C) 2004 Andrew de Quincey (wol support)
  * Copyright (C) 2004 Carl-Daniel Hailfinger (invalid MAC handling, insane
  *             IRQ rate fixes, bigendian fixes, cleanups, verification)
- * Copyright (c) 2004,2005,2006,2007,2008 NVIDIA Corporation
+ * Copyright (c) 2004,2005,2006,2007,2008,2009 NVIDIA Corporation
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -39,7 +39,7 @@
  * DEV_NEED_TIMERIRQ will not harm you on sane hardware, only generating a few
  * superfluous timer interrupts from the nic.
  */
-#define FORCEDETH_VERSION              "0.61"
+#define FORCEDETH_VERSION              "0.63"
 #define DRV_NAME                       "forcedeth"
 
 #include <linux/module.h>
 enum {
        NvRegIrqStatus = 0x000,
 #define NVREG_IRQSTAT_MIIEVENT 0x040
-#define NVREG_IRQSTAT_MASK             0x81ff
+#define NVREG_IRQSTAT_MASK             0x83ff
        NvRegIrqMask = 0x004,
 #define NVREG_IRQ_RX_ERROR             0x0001
 #define NVREG_IRQ_RX                   0x0002
@@ -113,7 +113,7 @@ enum {
 #define NVREG_IRQ_LINK                 0x0040
 #define NVREG_IRQ_RX_FORCED            0x0080
 #define NVREG_IRQ_TX_FORCED            0x0100
-#define NVREG_IRQ_RECOVER_ERROR                0x8000
+#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)
@@ -157,6 +157,9 @@ enum {
 #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
 
@@ -289,8 +292,10 @@ enum {
 #define NVREG_WAKEUPFLAGS_ACCEPT_LINKCHANGE    0x04
 #define NVREG_WAKEUPFLAGS_ENABLE       0x1111
 
-       NvRegPatternCRC = 0x204,
-       NvRegPatternMask = 0x208,
+       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)
@@ -303,6 +308,8 @@ enum {
 #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,
@@ -337,7 +344,7 @@ enum {
        NvRegMSIXIrqStatus = 0x3f0,
 
        NvRegPowerState2 = 0x600,
-#define NVREG_POWERSTATE2_POWERUP_MASK         0x0F11
+#define NVREG_POWERSTATE2_POWERUP_MASK         0x0F15
 #define NVREG_POWERSTATE2_POWERUP_REV_A3       0x0001
 #define NVREG_POWERSTATE2_PHY_RESET            0x0004
 };
@@ -582,6 +589,9 @@ union ring_type {
 #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_RESTART_TX         0x1
 #define NV_RESTART_RX         0x2
 
@@ -712,12 +722,12 @@ struct nv_skb_map {
 
 /*
  * SMP locking:
- * All hardware access under dev->priv->lock, except the performance
+ * All hardware access under netdev_priv(dev)->lock, except the performance
  * critical parts:
  * - rx is (pseudo-) lockless: it relies on the single-threading provided
  *     by the arch code for interrupts.
  * - tx setup is lockless: it relies on netif_tx_lock. Actual submission
- *     needs dev->priv->lock :-(
+ *     needs netdev_priv(dev)->lock :-(
  * - set_multicast_list: preparation lockless, relies on netif_tx_lock.
  */
 
@@ -758,6 +768,8 @@ struct fe_priv {
        u32 register_size;
        int rx_csum;
        u32 mac_in_use;
+       int mgmt_version;
+       int mgmt_sema;
 
        void __iomem *base;
 
@@ -812,13 +824,18 @@ struct fe_priv {
 
        /* power saved state */
        u32 saved_config_space[NV_PCI_REGSZ_MAX/4];
+
+       /* for different msi-x irq type */
+       char name_rx[IFNAMSIZ + 3];       /* -rx    */
+       char name_tx[IFNAMSIZ + 3];       /* -tx    */
+       char name_other[IFNAMSIZ + 6];    /* -other */
 };
 
 /*
  * Maximum number of loops until we assume that a bit in the irq mask
  * is stuck. Overridable with module param.
  */
-static int max_interrupt_work = 5;
+static int max_interrupt_work = 15;
 
 /*
  * Optimization can be either throuput mode or cpu mode
@@ -857,7 +874,7 @@ enum {
        NV_MSIX_INT_DISABLED,
        NV_MSIX_INT_ENABLED
 };
-static int msix = NV_MSIX_INT_DISABLED;
+static int msix = NV_MSIX_INT_ENABLED;
 
 /*
  * DMA 64bit
@@ -923,7 +940,7 @@ static int reg_delay(struct net_device *dev, int offset, u32 mask, u32 target,
                delaymax -= delay;
                if (delaymax < 0) {
                        if (msg)
-                               printk(msg);
+                               printk("%s", msg);
                        return 1;
                }
        } while ((readl(base + offset) & mask) != target);
@@ -1446,9 +1463,9 @@ static int phy_init(struct net_device *dev)
        /* some phys clear out pause advertisment on reset, set it back */
        mii_rw(dev, np->phyaddr, MII_ADVERTISE, reg);
 
-       /* restart auto negotiation */
+       /* restart auto negotiation, power down phy */
        mii_control = mii_rw(dev, np->phyaddr, MII_BMCR, MII_READ);
-       mii_control |= (BMCR_ANRESTART | BMCR_ANENABLE);
+       mii_control |= (BMCR_ANRESTART | BMCR_ANENABLE | BMCR_PDOWN);
        if (mii_rw(dev, np->phyaddr, MII_BMCR, mii_control)) {
                return PHY_ERROR;
        }
@@ -1760,7 +1777,7 @@ static void nv_do_rx_refill(unsigned long data)
        struct fe_priv *np = netdev_priv(dev);
 
        /* Just reschedule NAPI rx processing */
-       netif_rx_schedule(dev, &np->napi);
+       napi_schedule(&np->napi);
 }
 #else
 static void nv_do_rx_refill(unsigned long data)
@@ -2096,14 +2113,15 @@ static int nv_start_xmit(struct sk_buff *skb, struct net_device *dev)
                           ((skb_shinfo(skb)->frags[i].size & (NV_TX2_TSO_MAX_SIZE-1)) ? 1 : 0);
        }
 
+       spin_lock_irqsave(&np->lock, flags);
        empty_slots = nv_get_empty_tx_slots(np);
        if (unlikely(empty_slots <= entries)) {
-               spin_lock_irqsave(&np->lock, flags);
                netif_stop_queue(dev);
                np->tx_stop = 1;
                spin_unlock_irqrestore(&np->lock, flags);
                return NETDEV_TX_BUSY;
        }
+       spin_unlock_irqrestore(&np->lock, flags);
 
        start_tx = put_tx = np->put_tx.orig;
 
@@ -2214,14 +2232,15 @@ static int nv_start_xmit_optimized(struct sk_buff *skb, struct net_device *dev)
                           ((skb_shinfo(skb)->frags[i].size & (NV_TX2_TSO_MAX_SIZE-1)) ? 1 : 0);
        }
 
+       spin_lock_irqsave(&np->lock, flags);
        empty_slots = nv_get_empty_tx_slots(np);
        if (unlikely(empty_slots <= entries)) {
-               spin_lock_irqsave(&np->lock, flags);
                netif_stop_queue(dev);
                np->tx_stop = 1;
                spin_unlock_irqrestore(&np->lock, flags);
                return NETDEV_TX_BUSY;
        }
+       spin_unlock_irqrestore(&np->lock, flags);
 
        start_tx = put_tx = np->put_tx.ex;
        start_tx_ctx = np->put_tx_ctx;
@@ -2735,7 +2754,6 @@ static int nv_rx_process(struct net_device *dev, int limit)
 #else
                netif_rx(skb);
 #endif
-               dev->last_rx = jiffies;
                dev->stats.rx_packets++;
                dev->stats.rx_bytes += len;
 next_pkt:
@@ -2848,7 +2866,6 @@ static int nv_rx_process_optimized(struct net_device *dev, int limit)
                                }
                        }
 
-                       dev->last_rx = jiffies;
                        dev->stats.rx_packets++;
                        dev->stats.rx_bytes += len;
                } else {
@@ -3405,10 +3422,10 @@ static irqreturn_t nv_nic_irq(int foo, void *data)
 
 #ifdef CONFIG_FORCEDETH_NAPI
                if (events & NVREG_IRQ_RX_ALL) {
-                       netif_rx_schedule(dev, &np->napi);
+                       spin_lock(&np->lock);
+                       napi_schedule(&np->napi);
 
                        /* Disable furthur receive irq's */
-                       spin_lock(&np->lock);
                        np->irqmask &= ~NVREG_IRQ_RX_ALL;
 
                        if (np->msi_flags & NV_MSI_X_ENABLED)
@@ -3522,10 +3539,10 @@ static irqreturn_t nv_nic_irq_optimized(int foo, void *data)
 
 #ifdef CONFIG_FORCEDETH_NAPI
                if (events & NVREG_IRQ_RX_ALL) {
-                       netif_rx_schedule(dev, &np->napi);
+                       spin_lock(&np->lock);
+                       napi_schedule(&np->napi);
 
                        /* Disable furthur receive irq's */
-                       spin_lock(&np->lock);
                        np->irqmask &= ~NVREG_IRQ_RX_ALL;
 
                        if (np->msi_flags & NV_MSI_X_ENABLED)
@@ -3680,7 +3697,7 @@ static int nv_napi_poll(struct napi_struct *napi, int budget)
                /* re-enable receive interrupts */
                spin_lock_irqsave(&np->lock, flags);
 
-               __netif_rx_complete(dev, napi);
+               __napi_complete(napi);
 
                np->irqmask |= NVREG_IRQ_RX_ALL;
                if (np->msi_flags & NV_MSI_X_ENABLED)
@@ -3703,13 +3720,13 @@ static irqreturn_t nv_nic_irq_rx(int foo, void *data)
        u32 events;
 
        events = readl(base + NvRegMSIXIrqStatus) & NVREG_IRQ_RX_ALL;
-       writel(NVREG_IRQ_RX_ALL, base + NvRegMSIXIrqStatus);
 
        if (events) {
-               netif_rx_schedule(dev, &np->napi);
                /* disable receive interrupts on the nic */
                writel(NVREG_IRQ_RX_ALL, base + NvRegIrqMask);
                pci_push(base);
+               writel(NVREG_IRQ_RX_ALL, base + NvRegMSIXIrqStatus);
+               napi_schedule(&np->napi);
        }
        return IRQ_HANDLED;
 }
@@ -3918,21 +3935,27 @@ static int nv_request_irq(struct net_device *dev, int intr_test)
                        np->msi_flags |= NV_MSI_X_ENABLED;
                        if (optimization_mode == NV_OPTIMIZATION_MODE_THROUGHPUT && !intr_test) {
                                /* Request irq for rx handling */
-                               if (request_irq(np->msi_x_entry[NV_MSI_X_VECTOR_RX].vector, &nv_nic_irq_rx, IRQF_SHARED, dev->name, dev) != 0) {
+                               sprintf(np->name_rx, "%s-rx", dev->name);
+                               if (request_irq(np->msi_x_entry[NV_MSI_X_VECTOR_RX].vector,
+                                               &nv_nic_irq_rx, IRQF_SHARED, np->name_rx, dev) != 0) {
                                        printk(KERN_INFO "forcedeth: request_irq failed for rx %d\n", ret);
                                        pci_disable_msix(np->pci_dev);
                                        np->msi_flags &= ~NV_MSI_X_ENABLED;
                                        goto out_err;
                                }
                                /* Request irq for tx handling */
-                               if (request_irq(np->msi_x_entry[NV_MSI_X_VECTOR_TX].vector, &nv_nic_irq_tx, IRQF_SHARED, dev->name, dev) != 0) {
+                               sprintf(np->name_tx, "%s-tx", dev->name);
+                               if (request_irq(np->msi_x_entry[NV_MSI_X_VECTOR_TX].vector,
+                                               &nv_nic_irq_tx, IRQF_SHARED, np->name_tx, dev) != 0) {
                                        printk(KERN_INFO "forcedeth: request_irq failed for tx %d\n", ret);
                                        pci_disable_msix(np->pci_dev);
                                        np->msi_flags &= ~NV_MSI_X_ENABLED;
                                        goto out_free_rx;
                                }
                                /* Request irq for link and timer handling */
-                               if (request_irq(np->msi_x_entry[NV_MSI_X_VECTOR_OTHER].vector, &nv_nic_irq_other, IRQF_SHARED, dev->name, dev) != 0) {
+                               sprintf(np->name_other, "%s-other", dev->name);
+                               if (request_irq(np->msi_x_entry[NV_MSI_X_VECTOR_OTHER].vector,
+                                               &nv_nic_irq_other, IRQF_SHARED, np->name_other, dev) != 0) {
                                        printk(KERN_INFO "forcedeth: request_irq failed for link %d\n", ret);
                                        pci_disable_msix(np->pci_dev);
                                        np->msi_flags &= ~NV_MSI_X_ENABLED;
@@ -4046,19 +4069,19 @@ static void nv_do_nic_poll(unsigned long data)
                        mask |= NVREG_IRQ_OTHER;
                }
        }
-       np->nic_poll_irq = 0;
-
        /* disable_irq() contains synchronize_irq, thus no irq handler can run now */
 
        if (np->recover_error) {
                np->recover_error = 0;
-               printk(KERN_INFO "forcedeth: MAC in recoverable error state\n");
+               printk(KERN_INFO "%s: MAC in recoverable error state\n", dev->name);
                if (netif_running(dev)) {
                        netif_tx_lock_bh(dev);
                        netif_addr_lock(dev);
                        spin_lock(&np->lock);
                        /* stop engines */
                        nv_stop_rxtx(dev);
+                       if (np->driver_data & DEV_HAS_POWER_CNTRL)
+                               nv_mac_reset(dev);
                        nv_txrx_reset(dev);
                        /* drain rx queue */
                        nv_drain_rxtx(dev);
@@ -4076,6 +4099,11 @@ static void nv_do_nic_poll(unsigned long data)
                        pci_push(base);
                        writel(NVREG_TXRXCTL_KICK|np->txrxctl_bits, get_hwbase(dev) + NvRegTxRxControl);
                        pci_push(base);
+                       /* clear interrupts */
+                       if (!(np->msi_flags & NV_MSI_X_ENABLED))
+                               writel(NVREG_IRQSTAT_MASK, base + NvRegIrqStatus);
+                       else
+                               writel(NVREG_IRQSTAT_MASK, base + NvRegMSIXIrqStatus);
 
                        /* restart rx engine */
                        nv_start_rxtx(dev);
@@ -4085,11 +4113,11 @@ static void nv_do_nic_poll(unsigned long data)
                }
        }
 
-
        writel(mask, base + NvRegIrqMask);
        pci_push(base);
 
        if (!using_multi_irqs(dev)) {
+               np->nic_poll_irq = 0;
                if (nv_optimized(np))
                        nv_nic_irq_optimized(0, dev);
                else
@@ -4100,18 +4128,22 @@ static void nv_do_nic_poll(unsigned long data)
                        enable_irq_lockdep(np->pci_dev->irq);
        } else {
                if (np->nic_poll_irq & NVREG_IRQ_RX_ALL) {
+                       np->nic_poll_irq &= ~NVREG_IRQ_RX_ALL;
                        nv_nic_irq_rx(0, dev);
                        enable_irq_lockdep(np->msi_x_entry[NV_MSI_X_VECTOR_RX].vector);
                }
                if (np->nic_poll_irq & NVREG_IRQ_TX_ALL) {
+                       np->nic_poll_irq &= ~NVREG_IRQ_TX_ALL;
                        nv_nic_irq_tx(0, dev);
                        enable_irq_lockdep(np->msi_x_entry[NV_MSI_X_VECTOR_TX].vector);
                }
                if (np->nic_poll_irq & NVREG_IRQ_OTHER) {
+                       np->nic_poll_irq &= ~NVREG_IRQ_OTHER;
                        nv_nic_irq_other(0, dev);
                        enable_irq_lockdep(np->msi_x_entry[NV_MSI_X_VECTOR_OTHER].vector);
                }
        }
+
 }
 
 #ifdef CONFIG_NET_POLL_CONTROLLER
@@ -4738,7 +4770,7 @@ static int nv_set_tx_csum(struct net_device *dev, u32 data)
        struct fe_priv *np = netdev_priv(dev);
 
        if (np->driver_data & DEV_HAS_CHECKSUM)
-               return ethtool_op_set_tx_hw_csum(dev, data);
+               return ethtool_op_set_tx_csum(dev, data);
        else
                return -EOPNOTSUPP;
 }
@@ -5169,6 +5201,7 @@ static void nv_vlan_rx_register(struct net_device *dev, struct vlan_group *grp)
 /* The mgmt unit and driver use a semaphore to access the phy during init */
 static int nv_mgmt_acquire_sema(struct net_device *dev)
 {
+       struct fe_priv *np = netdev_priv(dev);
        u8 __iomem *base = get_hwbase(dev);
        int i;
        u32 tx_ctrl, mgmt_sema;
@@ -5191,8 +5224,10 @@ static int nv_mgmt_acquire_sema(struct net_device *dev)
                /* verify that semaphore was acquired */
                tx_ctrl = readl(base + 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))
+                   ((tx_ctrl & NVREG_XMITCTL_MGMT_SEMA_MASK) == NVREG_XMITCTL_MGMT_SEMA_FREE)) {
+                       np->mgmt_sema = 1;
                        return 1;
+               }
                else
                        udelay(50);
        }
@@ -5200,6 +5235,51 @@ static int nv_mgmt_acquire_sema(struct net_device *dev)
        return 0;
 }
 
+static void nv_mgmt_release_sema(struct net_device *dev)
+{
+       struct fe_priv *np = netdev_priv(dev);
+       u8 __iomem *base = get_hwbase(dev);
+       u32 tx_ctrl;
+
+       if (np->driver_data & DEV_HAS_MGMT_UNIT) {
+               if (np->mgmt_sema) {
+                       tx_ctrl = readl(base + NvRegTransmitterControl);
+                       tx_ctrl &= ~NVREG_XMITCTL_HOST_SEMA_ACQ;
+                       writel(tx_ctrl, base + NvRegTransmitterControl);
+               }
+       }
+}
+
+
+static int nv_mgmt_get_version(struct net_device *dev)
+{
+       struct fe_priv *np = netdev_priv(dev);
+       u8 __iomem *base = get_hwbase(dev);
+       u32 data_ready = readl(base + NvRegTransmitterControl);
+       u32 data_ready2 = 0;
+       unsigned long start;
+       int ready = 0;
+
+       writel(NVREG_MGMTUNITGETVERSION, base + NvRegMgmtUnitGetVersion);
+       writel(data_ready ^ NVREG_XMITCTL_DATA_START, base + NvRegTransmitterControl);
+       start = jiffies;
+       while (time_before(jiffies, start + 5*HZ)) {
+               data_ready2 = readl(base + NvRegTransmitterControl);
+               if ((data_ready & NVREG_XMITCTL_DATA_READY) != (data_ready2 & NVREG_XMITCTL_DATA_READY)) {
+                       ready = 1;
+                       break;
+               }
+               schedule_timeout_uninterruptible(1);
+       }
+
+       if (!ready || (data_ready2 & NVREG_XMITCTL_DATA_ERROR))
+               return 0;
+
+       np->mgmt_version = readl(base + NvRegMgmtUnitVersion) & NVREG_MGMTUNITVERSION;
+
+       return 1;
+}
+
 static int nv_open(struct net_device *dev)
 {
        struct fe_priv *np = netdev_priv(dev);
@@ -5210,6 +5290,10 @@ static int nv_open(struct net_device *dev)
 
        dprintk(KERN_DEBUG "nv_open: begin\n");
 
+       /* power up phy */
+       mii_rw(dev, np->phyaddr, MII_BMCR,
+              mii_rw(dev, np->phyaddr, MII_BMCR, MII_READ) & ~BMCR_PDOWN);
+
        /* erase previous misconfiguration */
        if (np->driver_data & DEV_HAS_POWER_CNTRL)
                nv_mac_reset(dev);
@@ -5403,6 +5487,10 @@ static int nv_close(struct net_device *dev)
        if (np->wolenabled) {
                writel(NVREG_PFF_ALWAYS|NVREG_PFF_MYADDR, base + NvRegPacketFilterFlags);
                nv_start_rx(dev);
+       } else {
+               /* power down phy */
+               mii_rw(dev, np->phyaddr, MII_BMCR,
+                      mii_rw(dev, np->phyaddr, MII_BMCR, MII_READ)|BMCR_PDOWN);
        }
 
        /* FIXME: power down nic */
@@ -5410,6 +5498,38 @@ static int nv_close(struct net_device *dev)
        return 0;
 }
 
+static const struct net_device_ops nv_netdev_ops = {
+       .ndo_open               = nv_open,
+       .ndo_stop               = nv_close,
+       .ndo_get_stats          = nv_get_stats,
+       .ndo_start_xmit         = nv_start_xmit,
+       .ndo_tx_timeout         = nv_tx_timeout,
+       .ndo_change_mtu         = nv_change_mtu,
+       .ndo_validate_addr      = eth_validate_addr,
+       .ndo_set_mac_address    = nv_set_mac_address,
+       .ndo_set_multicast_list = nv_set_multicast,
+       .ndo_vlan_rx_register   = nv_vlan_rx_register,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+       .ndo_poll_controller    = nv_poll_controller,
+#endif
+};
+
+static const struct net_device_ops nv_netdev_ops_optimized = {
+       .ndo_open               = nv_open,
+       .ndo_stop               = nv_close,
+       .ndo_get_stats          = nv_get_stats,
+       .ndo_start_xmit         = nv_start_xmit_optimized,
+       .ndo_tx_timeout         = nv_tx_timeout,
+       .ndo_change_mtu         = nv_change_mtu,
+       .ndo_validate_addr      = eth_validate_addr,
+       .ndo_set_mac_address    = nv_set_mac_address,
+       .ndo_set_multicast_list = nv_set_multicast,
+       .ndo_vlan_rx_register   = nv_vlan_rx_register,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+       .ndo_poll_controller    = nv_poll_controller,
+#endif
+};
+
 static int __devinit nv_probe(struct pci_dev *pci_dev, const struct pci_device_id *id)
 {
        struct net_device *dev;
@@ -5420,7 +5540,6 @@ static int __devinit nv_probe(struct pci_dev *pci_dev, const struct pci_device_i
        u32 powerstate, txreg;
        u32 phystate_orig = 0, phystate;
        int phyinitialized = 0;
-       DECLARE_MAC_BUF(mac);
        static int printed_version;
 
        if (!printed_version++)
@@ -5530,7 +5649,6 @@ static int __devinit nv_probe(struct pci_dev *pci_dev, const struct pci_device_i
        if (id->driver_data & DEV_HAS_VLAN) {
                np->vlanctl_bits = NVREG_VLANCONTROL_ENABLE;
                dev->features |= NETIF_F_HW_VLAN_RX | NETIF_F_HW_VLAN_TX;
-               dev->vlan_rx_register = nv_vlan_rx_register;
        }
 
        np->msi_flags = 0;
@@ -5580,25 +5698,15 @@ static int __devinit nv_probe(struct pci_dev *pci_dev, const struct pci_device_i
        if (!np->rx_skb || !np->tx_skb)
                goto out_freering;
 
-       dev->open = nv_open;
-       dev->stop = nv_close;
-
        if (!nv_optimized(np))
-               dev->hard_start_xmit = nv_start_xmit;
+               dev->netdev_ops = &nv_netdev_ops;
        else
-               dev->hard_start_xmit = nv_start_xmit_optimized;
-       dev->get_stats = nv_get_stats;
-       dev->change_mtu = nv_change_mtu;
-       dev->set_mac_address = nv_set_mac_address;
-       dev->set_multicast_list = nv_set_multicast;
-#ifdef CONFIG_NET_POLL_CONTROLLER
-       dev->poll_controller = nv_poll_controller;
-#endif
+               dev->netdev_ops = &nv_netdev_ops_optimized;
+
 #ifdef CONFIG_FORCEDETH_NAPI
        netif_napi_add(dev, &np->napi, nv_napi_poll, RX_WORK_PER_LOOP);
 #endif
        SET_ETHTOOL_OPS(dev, &ops);
-       dev->tx_timeout = nv_tx_timeout;
        dev->watchdog_timeo = NV_WATCHDOG_TIMEO;
 
        pci_set_drvdata(pci_dev, dev);
@@ -5653,8 +5761,8 @@ static int __devinit nv_probe(struct pci_dev *pci_dev, const struct pci_device_i
                 * to 01:23:45:67:89:ab
                 */
                dev_printk(KERN_ERR, &pci_dev->dev,
-                       "Invalid Mac address detected: %s\n",
-                       print_mac(mac, dev->dev_addr));
+                       "Invalid Mac address detected: %pM\n",
+                       dev->dev_addr);
                dev_printk(KERN_ERR, &pci_dev->dev,
                        "Please complain to your hardware vendor. Switching to a random MAC.\n");
                dev->dev_addr[0] = 0x00;
@@ -5663,8 +5771,8 @@ static int __devinit nv_probe(struct pci_dev *pci_dev, const struct pci_device_i
                get_random_bytes(&dev->dev_addr[3], 3);
        }
 
-       dprintk(KERN_DEBUG "%s: MAC Address %s\n",
-               pci_name(pci_dev), print_mac(mac, dev->dev_addr));
+       dprintk(KERN_DEBUG "%s: MAC Address %pM\n",
+               pci_name(pci_dev), dev->dev_addr);
 
        /* set mac address */
        nv_copy_mac_to_hw(dev);
@@ -5743,19 +5851,26 @@ static int __devinit nv_probe(struct pci_dev *pci_dev, const struct pci_device_i
 
        if (id->driver_data & DEV_HAS_MGMT_UNIT) {
                /* management unit running on the mac? */
-               if (readl(base + NvRegTransmitterControl) & NVREG_XMITCTL_SYNC_PHY_INIT) {
-                       np->mac_in_use = readl(base + NvRegTransmitterControl) & NVREG_XMITCTL_MGMT_ST;
-                       dprintk(KERN_INFO "%s: mgmt unit is running. mac in use %x.\n", pci_name(pci_dev), np->mac_in_use);
-                       if (nv_mgmt_acquire_sema(dev)) {
-                               /* management unit setup the phy already? */
-                               if ((readl(base + NvRegTransmitterControl) & NVREG_XMITCTL_SYNC_MASK) ==
-                                   NVREG_XMITCTL_SYNC_PHY_INIT) {
-                                       /* phy is inited by mgmt unit */
-                                       phyinitialized = 1;
-                                       dprintk(KERN_INFO "%s: Phy already initialized by mgmt unit.\n", pci_name(pci_dev));
-                               } else {
-                                       /* we need to init the phy */
-                               }
+               if ((readl(base + NvRegTransmitterControl) & NVREG_XMITCTL_MGMT_ST) &&
+                   (readl(base + NvRegTransmitterControl) & NVREG_XMITCTL_SYNC_PHY_INIT) &&
+                   nv_mgmt_acquire_sema(dev) &&
+                   nv_mgmt_get_version(dev)) {
+                       np->mac_in_use = 1;
+                       if (np->mgmt_version > 0) {
+                               np->mac_in_use = readl(base + NvRegMgmtUnitControl) & NVREG_MGMTUNITCONTROL_INUSE;
+                       }
+                       dprintk(KERN_INFO "%s: mgmt unit is running. mac in use %x.\n",
+                               pci_name(pci_dev), np->mac_in_use);
+                       /* management unit setup the phy already? */
+                       if (np->mac_in_use &&
+                           ((readl(base + NvRegTransmitterControl) & NVREG_XMITCTL_SYNC_MASK) ==
+                            NVREG_XMITCTL_SYNC_PHY_INIT)) {
+                               /* phy is inited by mgmt unit */
+                               phyinitialized = 1;
+                               dprintk(KERN_INFO "%s: Phy already initialized by mgmt unit.\n",
+                                       pci_name(pci_dev));
+                       } else {
+                               /* we need to init the phy */
                        }
                }
        }
@@ -5917,6 +6032,8 @@ static void __devexit nv_remove(struct pci_dev *pci_dev)
        /* restore any phy related changes */
        nv_restore_phy(dev);
 
+       nv_mgmt_release_sema(dev);
+
        /* free all structures */
        free_rings(dev);
        iounmap(get_hwbase(dev));
@@ -5967,6 +6084,8 @@ static int nv_resume(struct pci_dev *pdev)
        for (i = 0;i <= np->register_size/sizeof(u32); i++)
                writel(np->saved_config_space[i], base+i*sizeof(u32));
 
+       pci_write_config_dword(pdev, NV_MSI_PRIV_OFFSET, NV_MSI_PRIV_VALUE);
+
        netif_device_attach(dev);
        if (netif_running(dev)) {
                rc = nv_open(dev);
@@ -5983,9 +6102,20 @@ static void nv_shutdown(struct pci_dev *pdev)
        if (netif_running(dev))
                nv_close(dev);
 
-       nv_restore_mac_addr(pdev);
+       /*
+        * Restore the MAC so a kernel started by kexec won't get confused.
+        * If we really go for poweroff, we must not restore the MAC,
+        * otherwise the MAC for WOL will be reversed at least on some boards.
+        */
+       if (system_state != SYSTEM_POWER_OFF) {
+               nv_restore_mac_addr(pdev);
+       }
 
        pci_disable_device(pdev);
+       /*
+        * Apparently it is not possible to reinitialise from D3 hot,
+        * only put the device into D3 if we really go for poweroff.
+        */
        if (system_state == SYSTEM_POWER_OFF) {
                if (pci_enable_wake(pdev, PCI_D3cold, np->wolenabled))
                        pci_enable_wake(pdev, PCI_D3hot, np->wolenabled);
@@ -6029,11 +6159,11 @@ static struct pci_device_id pci_tbl[] = {
        },
        {       /* CK804 Ethernet Controller */
                PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_8),
-               .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_STATISTICS_V1|DEV_NEED_TX_LIMIT,
+               .driver_data = DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_STATISTICS_V1|DEV_NEED_TX_LIMIT,
        },
        {       /* CK804 Ethernet Controller */
                PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_9),
-               .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_STATISTICS_V1|DEV_NEED_TX_LIMIT,
+               .driver_data = DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_STATISTICS_V1|DEV_NEED_TX_LIMIT,
        },
        {       /* MCP04 Ethernet Controller */
                PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_10),
@@ -6053,11 +6183,11 @@ static struct pci_device_id pci_tbl[] = {
        },
        {       /* MCP55 Ethernet Controller */
                PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_14),
-               .driver_data = DEV_NEED_TIMERIRQ|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,
+               .driver_data = 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,
        },
        {       /* MCP55 Ethernet Controller */
                PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_15),
-               .driver_data = DEV_NEED_TIMERIRQ|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,
+               .driver_data = 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,
        },
        {       /* MCP61 Ethernet Controller */
                PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_16),
@@ -6141,19 +6271,19 @@ static struct pci_device_id pci_tbl[] = {
        },
        {       /* MCP79 Ethernet Controller */
                PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_36),
-               .driver_data = DEV_NEED_TIMERIRQ|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_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR|DEV_HAS_COLLISION_FIX|DEV_NEED_TX_LIMIT|DEV_HAS_GEAR_MODE,
+               .driver_data = 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_LIMIT|DEV_HAS_GEAR_MODE,
        },
        {       /* MCP79 Ethernet Controller */
                PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_37),
-               .driver_data = DEV_NEED_TIMERIRQ|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_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR|DEV_HAS_COLLISION_FIX|DEV_NEED_TX_LIMIT|DEV_HAS_GEAR_MODE,
+               .driver_data = DEV_NEED_TIMERIRQ|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_LIMIT|DEV_HAS_GEAR_MODE,
        },
        {       /* MCP79 Ethernet Controller */
                PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_38),
-               .driver_data = DEV_NEED_TIMERIRQ|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_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR|DEV_HAS_COLLISION_FIX|DEV_NEED_TX_LIMIT|DEV_HAS_GEAR_MODE,
+               .driver_data = DEV_NEED_TIMERIRQ|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_LIMIT|DEV_HAS_GEAR_MODE,
        },
        {       /* MCP79 Ethernet Controller */
                PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_39),
-               .driver_data = DEV_NEED_TIMERIRQ|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_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR|DEV_HAS_COLLISION_FIX|DEV_NEED_TX_LIMIT|DEV_HAS_GEAR_MODE,
+               .driver_data = DEV_NEED_TIMERIRQ|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_LIMIT|DEV_HAS_GEAR_MODE,
        },
        {0,},
 };