vserver 1.9.5.x5
[linux-2.6.git] / drivers / net / forcedeth.c
index 8d39ca7..cda48c5 100644 (file)
  *                        for registers, link status and other minor fixes.
  *     0.28: 21 Jun 2004: Big cleanup, making driver mostly endian safe
  *     0.29: 31 Aug 2004: Add backup timer for link change notification.
+ *     0.30: 25 Sep 2004: rx checksum support for nf 250 Gb. Add rx reset
+ *                        into nv_close, otherwise reenabling for wol can
+ *                        cause DMA to kfree'd memory.
+ *     0.31: 14 Nov 2004: ethtool support for getting/setting link
+ *                        capabilities.
  *
  * Known bugs:
  * We suspect that on some hardware no TX done interrupts are generated.
@@ -87,7 +92,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.29"
+#define FORCEDETH_VERSION              "0.31"
 #define DRV_NAME                       "forcedeth"
 
 #include <linux/module.h>
@@ -207,6 +212,7 @@ enum {
 #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)
        NvRegUnknownSetupReg3 = 0x13c,
@@ -217,6 +223,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
@@ -313,6 +320,10 @@ struct ring_desc {
 #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)
@@ -371,8 +382,15 @@ struct ring_desc {
 #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
+#define DESC_VER_2     (0x02100|NVREG_TXRXCTL_RXCHECK)
 
 /* PHY defines */
 #define PHY_OUI_MARVELL        0x5043
@@ -426,6 +444,8 @@ struct fe_priv {
        int in_shutdown;
        u32 linkspeed;
        int duplex;
+       int autoneg;
+       int fixed_mode;
        int phyaddr;
        int wolenabled;
        unsigned int phy_oui;
@@ -438,6 +458,8 @@ struct fe_priv {
        u32 irqmask;
        u32 desc_ver;
 
+       void __iomem *base;
+
        /* rx specific fields.
         * Locking: Within irq hander or disable_irq+spin_lock(&np->lock);
         */
@@ -472,15 +494,15 @@ static int max_interrupt_work = 5;
 
 static inline struct fe_priv *get_nvpriv(struct net_device *dev)
 {
-       return (struct fe_priv *) dev->priv;
+       return netdev_priv(dev);
 }
 
-static inline u8 *get_hwbase(struct net_device *dev)
+static inline u8 __iomem *get_hwbase(struct net_device *dev)
 {
-       return (u8 *) dev->base_addr;
+       return get_nvpriv(dev)->base;
 }
 
-static inline void pci_push(u8 base)
+static inline void pci_push(u8 __iomem *base)
 {
        /* force out pending posted writes */
        readl(base);
@@ -495,7 +517,7 @@ static inline u32 nv_descr_getlength(struct ring_desc *prd, u32 v)
 static int reg_delay(struct net_device *dev, int offset, u32 mask, u32 target,
                                int delay, int delaymax, const char *msg)
 {
-       u8 *base = get_hwbase(dev);
+       u8 __iomem *base = get_hwbase(dev);
 
        pci_push(base);
        do {
@@ -517,7 +539,7 @@ static int reg_delay(struct net_device *dev, int offset, u32 mask, u32 target,
  */
 static int mii_rw(struct net_device *dev, int addr, int miireg, int value)
 {
-       u8 *base = get_hwbase(dev);
+       u8 __iomem *base = get_hwbase(dev);
        u32 reg;
        int retval;
 
@@ -588,7 +610,7 @@ static int phy_reset(struct net_device *dev)
 static int phy_init(struct net_device *dev)
 {
        struct fe_priv *np = get_nvpriv(dev);
-       u8 *base = get_hwbase(dev);
+       u8 __iomem *base = get_hwbase(dev);
        u32 phyinterface, phy_reserved, mii_status, mii_control, mii_control_1000,reg;
 
        /* set advertise register */
@@ -665,7 +687,7 @@ static int phy_init(struct net_device *dev)
 static void nv_start_rx(struct net_device *dev)
 {
        struct fe_priv *np = get_nvpriv(dev);
-       u8 *base = get_hwbase(dev);
+       u8 __iomem *base = get_hwbase(dev);
 
        dprintk(KERN_DEBUG "%s: nv_start_rx\n", dev->name);
        /* Already running? Stop it. */
@@ -683,7 +705,7 @@ static void nv_start_rx(struct net_device *dev)
 
 static void nv_stop_rx(struct net_device *dev)
 {
-       u8 *base = get_hwbase(dev);
+       u8 __iomem *base = get_hwbase(dev);
 
        dprintk(KERN_DEBUG "%s: nv_stop_rx\n", dev->name);
        writel(0, base + NvRegReceiverControl);
@@ -697,7 +719,7 @@ static void nv_stop_rx(struct net_device *dev)
 
 static void nv_start_tx(struct net_device *dev)
 {
-       u8 *base = get_hwbase(dev);
+       u8 __iomem *base = get_hwbase(dev);
 
        dprintk(KERN_DEBUG "%s: nv_start_tx\n", dev->name);
        writel(NVREG_XMITCTL_START, base + NvRegTransmitterControl);
@@ -706,7 +728,7 @@ static void nv_start_tx(struct net_device *dev)
 
 static void nv_stop_tx(struct net_device *dev)
 {
-       u8 *base = get_hwbase(dev);
+       u8 __iomem *base = get_hwbase(dev);
 
        dprintk(KERN_DEBUG "%s: nv_stop_tx\n", dev->name);
        writel(0, base + NvRegTransmitterControl);
@@ -721,7 +743,7 @@ static void nv_stop_tx(struct net_device *dev)
 static void nv_txrx_reset(struct net_device *dev)
 {
        struct fe_priv *np = get_nvpriv(dev);
-       u8 *base = get_hwbase(dev);
+       u8 __iomem *base = get_hwbase(dev);
 
        dprintk(KERN_DEBUG "%s: nv_txrx_reset\n", dev->name);
        writel(NVREG_TXRXCTL_BIT2 | NVREG_TXRXCTL_RESET | np->desc_ver, base + NvRegTxRxControl);
@@ -748,91 +770,6 @@ static struct net_device_stats *nv_get_stats(struct net_device *dev)
        return &np->stats;
 }
 
-static int nv_ethtool_ioctl(struct net_device *dev, void __user *useraddr)
-{
-       struct fe_priv *np = get_nvpriv(dev);
-       u8 *base = get_hwbase(dev);
-       u32 ethcmd;
-
-       if (copy_from_user(&ethcmd, useraddr, sizeof (ethcmd)))
-               return -EFAULT;
-
-       switch (ethcmd) {
-       case ETHTOOL_GDRVINFO:
-       {
-               struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO };
-               strcpy(info.driver, "forcedeth");
-               strcpy(info.version, FORCEDETH_VERSION);
-               strcpy(info.bus_info, pci_name(np->pci_dev));
-               if (copy_to_user(useraddr, &info, sizeof (info)))
-                       return -EFAULT;
-               return 0;
-       }
-       case ETHTOOL_GLINK:
-       {
-               struct ethtool_value edata = { ETHTOOL_GLINK };
-
-               edata.data = !!netif_carrier_ok(dev);
-
-               if (copy_to_user(useraddr, &edata, sizeof(edata)))
-                       return -EFAULT;
-               return 0;
-       }
-       case ETHTOOL_GWOL:
-       {
-               struct ethtool_wolinfo wolinfo;
-               memset(&wolinfo, 0, sizeof(wolinfo));
-               wolinfo.supported = WAKE_MAGIC;
-
-               spin_lock_irq(&np->lock);
-               if (np->wolenabled)
-                       wolinfo.wolopts = WAKE_MAGIC;
-               spin_unlock_irq(&np->lock);
-
-               if (copy_to_user(useraddr, &wolinfo, sizeof(wolinfo)))
-                       return -EFAULT;
-               return 0;
-       }
-       case ETHTOOL_SWOL:
-       {
-               struct ethtool_wolinfo wolinfo;
-               if (copy_from_user(&wolinfo, useraddr, sizeof(wolinfo)))
-                       return -EFAULT;
-
-               spin_lock_irq(&np->lock);
-               if (wolinfo.wolopts == 0) {
-                       writel(0, base + NvRegWakeUpFlags);
-                       np->wolenabled = 0;
-               }
-               if (wolinfo.wolopts & WAKE_MAGIC) {
-                       writel(NVREG_WAKEUPFLAGS_ENABLE, base + NvRegWakeUpFlags);
-                       np->wolenabled = 1;
-               }
-               spin_unlock_irq(&np->lock);
-               return 0;
-       }
-
-       default:
-               break;
-       }
-
-       return -EOPNOTSUPP;
-}
-/*
- * nv_ioctl: dev->do_ioctl function
- * Called with rtnl_lock held.
- */
-static int nv_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
-{
-       switch(cmd) {
-       case SIOCETHTOOL:
-               return nv_ethtool_ioctl(dev, rq->ifr_data);
-
-       default:
-               return -EOPNOTSUPP;
-       }
-}
-
 /*
  * nv_alloc_rx: fill rx ring entries.
  * Return 1 if the allocations for the skbs failed and the
@@ -1049,7 +986,7 @@ static void nv_tx_done(struct net_device *dev)
 static void nv_tx_timeout(struct net_device *dev)
 {
        struct fe_priv *np = get_nvpriv(dev);
-       u8 *base = get_hwbase(dev);
+       u8 __iomem *base = get_hwbase(dev);
 
        dprintk(KERN_DEBUG "%s: Got tx_timeout. irq: %08x\n", dev->name,
                        readl(base + NvRegIrqStatus) & NVREG_IRQSTAT_MASK);
@@ -1181,6 +1118,15 @@ static void nv_rx_process(struct net_device *dev)
                                        goto next_pkt;
                                }
                        }
+                       Flags &= NV_RX2_CHECKSUMMASK;
+                       if (Flags == NV_RX2_CHECKSUMOK1 ||
+                                       Flags == NV_RX2_CHECKSUMOK2 ||
+                                       Flags == NV_RX2_CHECKSUMOK3) {
+                               dprintk(KERN_DEBUG "%s: hw checksum hit!.\n", dev->name);
+                               np->rx_skbuff[i]->ip_summed = CHECKSUM_UNNECESSARY;
+                       } else {
+                               dprintk(KERN_DEBUG "%s: hwchecksum miss!.\n", dev->name);
+                       }
                }
                /* got a valid packet - forward it to the network core */
                skb = np->rx_skbuff[i];
@@ -1218,7 +1164,7 @@ static int nv_change_mtu(struct net_device *dev, int new_mtu)
 static void nv_set_multicast(struct net_device *dev)
 {
        struct fe_priv *np = get_nvpriv(dev);
-       u8 *base = get_hwbase(dev);
+       u8 __iomem *base = get_hwbase(dev);
        u32 addr[2];
        u32 mask[2];
        u32 pff;
@@ -1278,7 +1224,7 @@ static void nv_set_multicast(struct net_device *dev)
 static int nv_update_linkspeed(struct net_device *dev)
 {
        struct fe_priv *np = get_nvpriv(dev);
-       u8 *base = get_hwbase(dev);
+       u8 __iomem *base = get_hwbase(dev);
        int adv, lpa;
        int newls = np->linkspeed;
        int newdup = np->duplex;
@@ -1301,6 +1247,25 @@ static int nv_update_linkspeed(struct net_device *dev)
                goto set_speed;
        }
 
+       if (np->autoneg == 0) {
+               dprintk(KERN_DEBUG "%s: nv_update_linkspeed: autoneg off, PHY set to 0x%04x.\n",
+                               dev->name, np->fixed_mode);
+               if (np->fixed_mode & LPA_100FULL) {
+                       newls = NVREG_LINKSPEED_FORCE|NVREG_LINKSPEED_100;
+                       newdup = 1;
+               } else if (np->fixed_mode & LPA_100HALF) {
+                       newls = NVREG_LINKSPEED_FORCE|NVREG_LINKSPEED_100;
+                       newdup = 0;
+               } else if (np->fixed_mode & LPA_10FULL) {
+                       newls = NVREG_LINKSPEED_FORCE|NVREG_LINKSPEED_10;
+                       newdup = 1;
+               } else {
+                       newls = NVREG_LINKSPEED_FORCE|NVREG_LINKSPEED_10;
+                       newdup = 0;
+               }
+               retval = 1;
+               goto set_speed;
+       }
        /* check auto negotiation is complete */
        if (!(mii_status & BMSR_ANEGCOMPLETE)) {
                /* still in autonegotiation - configure nic for 10 MBit HD and wait. */
@@ -1318,7 +1283,7 @@ static int nv_update_linkspeed(struct net_device *dev)
 
                if ((control_1000 & ADVERTISE_1000FULL) &&
                        (status_1000 & LPA_1000FULL)) {
-               dprintk(KERN_DEBUG "%s: nv_update_linkspeed: GBit ethernet detected.\n",
+                       dprintk(KERN_DEBUG "%s: nv_update_linkspeed: GBit ethernet detected.\n",
                                dev->name);
                        newls = NVREG_LINKSPEED_FORCE|NVREG_LINKSPEED_1000;
                        newdup = 1;
@@ -1377,9 +1342,9 @@ set_speed:
        phyreg &= ~(PHY_HALF|PHY_100|PHY_1000);
        if (np->duplex == 0)
                phyreg |= PHY_HALF;
-       if ((np->linkspeed & 0xFFF) == NVREG_LINKSPEED_100)
+       if ((np->linkspeed & NVREG_LINKSPEED_MASK) == NVREG_LINKSPEED_100)
                phyreg |= PHY_100;
-       else if ((np->linkspeed & 0xFFF) == NVREG_LINKSPEED_1000)
+       else if ((np->linkspeed & NVREG_LINKSPEED_MASK) == NVREG_LINKSPEED_1000)
                phyreg |= PHY_1000;
        writel(phyreg, base + NvRegPhyInterface);
 
@@ -1413,7 +1378,7 @@ static void nv_linkchange(struct net_device *dev)
 
 static void nv_link_irq(struct net_device *dev)
 {
-       u8 *base = get_hwbase(dev);
+       u8 __iomem *base = get_hwbase(dev);
        u32 miistat;
 
        miistat = readl(base + NvRegMIIStatus);
@@ -1429,7 +1394,7 @@ static irqreturn_t nv_nic_irq(int foo, void *data, struct pt_regs *regs)
 {
        struct net_device *dev = (struct net_device *) data;
        struct fe_priv *np = get_nvpriv(dev);
-       u8 *base = get_hwbase(dev);
+       u8 __iomem *base = get_hwbase(dev);
        u32 events;
        int i;
 
@@ -1501,7 +1466,7 @@ static void nv_do_nic_poll(unsigned long data)
 {
        struct net_device *dev = (struct net_device *) data;
        struct fe_priv *np = get_nvpriv(dev);
-       u8 *base = get_hwbase(dev);
+       u8 __iomem *base = get_hwbase(dev);
 
        disable_irq(dev->irq);
        /* FIXME: Do we need synchronize_irq(dev->irq) here? */
@@ -1515,10 +1480,231 @@ static void nv_do_nic_poll(unsigned long data)
        enable_irq(dev->irq);
 }
 
+static void nv_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
+{
+       struct fe_priv *np = get_nvpriv(dev);
+       strcpy(info->driver, "forcedeth");
+       strcpy(info->version, FORCEDETH_VERSION);
+       strcpy(info->bus_info, pci_name(np->pci_dev));
+}
+
+static void nv_get_wol(struct net_device *dev, struct ethtool_wolinfo *wolinfo)
+{
+       struct fe_priv *np = get_nvpriv(dev);
+       wolinfo->supported = WAKE_MAGIC;
+
+       spin_lock_irq(&np->lock);
+       if (np->wolenabled)
+               wolinfo->wolopts = WAKE_MAGIC;
+       spin_unlock_irq(&np->lock);
+}
+
+static int nv_set_wol(struct net_device *dev, struct ethtool_wolinfo *wolinfo)
+{
+       struct fe_priv *np = get_nvpriv(dev);
+       u8 __iomem *base = get_hwbase(dev);
+
+       spin_lock_irq(&np->lock);
+       if (wolinfo->wolopts == 0) {
+               writel(0, base + NvRegWakeUpFlags);
+               np->wolenabled = 0;
+       }
+       if (wolinfo->wolopts & WAKE_MAGIC) {
+               writel(NVREG_WAKEUPFLAGS_ENABLE, base + NvRegWakeUpFlags);
+               np->wolenabled = 1;
+       }
+       spin_unlock_irq(&np->lock);
+       return 0;
+}
+
+static int nv_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
+{
+       struct fe_priv *np = netdev_priv(dev);
+       int adv;
+
+       spin_lock_irq(&np->lock);
+       ecmd->port = PORT_MII;
+       if (!netif_running(dev)) {
+               /* We do not track link speed / duplex setting if the
+                * interface is disabled. Force a link check */
+               nv_update_linkspeed(dev);
+       }
+       switch(np->linkspeed & (NVREG_LINKSPEED_MASK)) {
+               case NVREG_LINKSPEED_10:
+                       ecmd->speed = SPEED_10;
+                       break;
+               case NVREG_LINKSPEED_100:
+                       ecmd->speed = SPEED_100;
+                       break;
+               case NVREG_LINKSPEED_1000:
+                       ecmd->speed = SPEED_1000;
+                       break;
+       }
+       ecmd->duplex = DUPLEX_HALF;
+       if (np->duplex)
+               ecmd->duplex = DUPLEX_FULL;
+
+       ecmd->autoneg = np->autoneg;
+
+       ecmd->advertising = ADVERTISED_MII;
+       if (np->autoneg) {
+               ecmd->advertising |= ADVERTISED_Autoneg;
+               adv = mii_rw(dev, np->phyaddr, MII_ADVERTISE, MII_READ);
+       } else {
+               adv = np->fixed_mode;
+       }
+       if (adv & ADVERTISE_10HALF)
+               ecmd->advertising |= ADVERTISED_10baseT_Half;
+       if (adv & ADVERTISE_10FULL)
+               ecmd->advertising |= ADVERTISED_10baseT_Full;
+       if (adv & ADVERTISE_100HALF)
+               ecmd->advertising |= ADVERTISED_100baseT_Half;
+       if (adv & ADVERTISE_100FULL)
+               ecmd->advertising |= ADVERTISED_100baseT_Full;
+       if (np->autoneg && np->gigabit == PHY_GIGABIT) {
+               adv = mii_rw(dev, np->phyaddr, MII_1000BT_CR, MII_READ);
+               if (adv & ADVERTISE_1000FULL)
+                       ecmd->advertising |= ADVERTISED_1000baseT_Full;
+       }
+
+       ecmd->supported = (SUPPORTED_Autoneg |
+               SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full |
+               SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full |
+               SUPPORTED_MII);
+       if (np->gigabit == PHY_GIGABIT)
+               ecmd->supported |= SUPPORTED_1000baseT_Full;
+
+       ecmd->phy_address = np->phyaddr;
+       ecmd->transceiver = XCVR_EXTERNAL;
+
+       /* ignore maxtxpkt, maxrxpkt for now */
+       spin_unlock_irq(&np->lock);
+       return 0;
+}
+
+static int nv_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
+{
+       struct fe_priv *np = netdev_priv(dev);
+
+       if (ecmd->port != PORT_MII)
+               return -EINVAL;
+       if (ecmd->transceiver != XCVR_EXTERNAL)
+               return -EINVAL;
+       if (ecmd->phy_address != np->phyaddr) {
+               /* TODO: support switching between multiple phys. Should be
+                * trivial, but not enabled due to lack of test hardware. */
+               return -EINVAL;
+       }
+       if (ecmd->autoneg == AUTONEG_ENABLE) {
+               u32 mask;
+
+               mask = ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full |
+                         ADVERTISED_100baseT_Half | ADVERTISED_100baseT_Full;
+               if (np->gigabit == PHY_GIGABIT)
+                       mask |= ADVERTISED_1000baseT_Full;
+
+               if ((ecmd->advertising & mask) == 0)
+                       return -EINVAL;
+
+       } else if (ecmd->autoneg == AUTONEG_DISABLE) {
+               /* Note: autonegotiation disable, speed 1000 intentionally
+                * forbidden - noone should need that. */
+
+               if (ecmd->speed != SPEED_10 && ecmd->speed != SPEED_100)
+                       return -EINVAL;
+               if (ecmd->duplex != DUPLEX_HALF && ecmd->duplex != DUPLEX_FULL)
+                       return -EINVAL;
+       } else {
+               return -EINVAL;
+       }
+
+       spin_lock_irq(&np->lock);
+       if (ecmd->autoneg == AUTONEG_ENABLE) {
+               int adv, bmcr;
+
+               np->autoneg = 1;
+
+               /* advertise only what has been requested */
+               adv = mii_rw(dev, np->phyaddr, MII_ADVERTISE, MII_READ);
+               adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4);
+               if (ecmd->advertising & ADVERTISED_10baseT_Half)
+                       adv |= ADVERTISE_10HALF;
+               if (ecmd->advertising & ADVERTISED_10baseT_Full)
+                       adv |= ADVERTISE_10FULL;
+               if (ecmd->advertising & ADVERTISED_100baseT_Half)
+                       adv |= ADVERTISE_100HALF;
+               if (ecmd->advertising & ADVERTISED_100baseT_Full)
+                       adv |= ADVERTISE_100FULL;
+               mii_rw(dev, np->phyaddr, MII_ADVERTISE, adv);
+
+               if (np->gigabit == PHY_GIGABIT) {
+                       adv = mii_rw(dev, np->phyaddr, MII_1000BT_CR, MII_READ);
+                       adv &= ~ADVERTISE_1000FULL;
+                       if (ecmd->advertising & ADVERTISED_1000baseT_Full)
+                               adv |= ADVERTISE_1000FULL;
+                       mii_rw(dev, np->phyaddr, MII_1000BT_CR, adv);
+               }
+
+               bmcr = mii_rw(dev, np->phyaddr, MII_BMCR, MII_READ);
+               bmcr |= (BMCR_ANENABLE | BMCR_ANRESTART);
+               mii_rw(dev, np->phyaddr, MII_BMCR, bmcr);
+
+       } else {
+               int adv, bmcr;
+
+               np->autoneg = 0;
+
+               adv = mii_rw(dev, np->phyaddr, MII_ADVERTISE, MII_READ);
+               adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4);
+               if (ecmd->speed == SPEED_10 && ecmd->duplex == DUPLEX_HALF)
+                       adv |= ADVERTISE_10HALF;
+               if (ecmd->speed == SPEED_10 && ecmd->duplex == DUPLEX_FULL)
+                       adv |= ADVERTISE_10FULL;
+               if (ecmd->speed == SPEED_100 && ecmd->duplex == DUPLEX_HALF)
+                       adv |= ADVERTISE_100HALF;
+               if (ecmd->speed == SPEED_100 && ecmd->duplex == DUPLEX_FULL)
+                       adv |= ADVERTISE_100FULL;
+               mii_rw(dev, np->phyaddr, MII_ADVERTISE, adv);
+               np->fixed_mode = adv;
+
+               if (np->gigabit == PHY_GIGABIT) {
+                       adv = mii_rw(dev, np->phyaddr, MII_1000BT_CR, MII_READ);
+                       adv &= ~ADVERTISE_1000FULL;
+                       mii_rw(dev, np->phyaddr, MII_1000BT_CR, adv);
+               }
+
+               bmcr = mii_rw(dev, np->phyaddr, MII_BMCR, MII_READ);
+               bmcr |= ~(BMCR_ANENABLE|BMCR_SPEED100|BMCR_FULLDPLX);
+               if (adv & (ADVERTISE_10FULL|ADVERTISE_100FULL))
+                       bmcr |= BMCR_FULLDPLX;
+               if (adv & (ADVERTISE_100HALF|ADVERTISE_100FULL))
+                       bmcr |= BMCR_SPEED100;
+               mii_rw(dev, np->phyaddr, MII_BMCR, bmcr);
+
+               if (netif_running(dev)) {
+                       /* Wait a bit and then reconfigure the nic. */
+                       udelay(10);
+                       nv_linkchange(dev);
+               }
+       }
+       spin_unlock_irq(&np->lock);
+
+       return 0;
+}
+
+static struct ethtool_ops ops = {
+       .get_drvinfo = nv_get_drvinfo,
+       .get_link = ethtool_op_get_link,
+       .get_wol = nv_get_wol,
+       .set_wol = nv_set_wol,
+       .get_settings = nv_get_settings,
+       .set_settings = nv_set_settings,
+};
+
 static int nv_open(struct net_device *dev)
 {
        struct fe_priv *np = get_nvpriv(dev);
-       u8 *base = get_hwbase(dev);
+       u8 __iomem *base = get_hwbase(dev);
        int ret, oom, i;
 
        dprintk(KERN_DEBUG "nv_open: begin\n");
@@ -1565,9 +1751,6 @@ static int nv_open(struct net_device *dev)
                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);
@@ -1659,7 +1842,7 @@ out_drain:
 static int nv_close(struct net_device *dev)
 {
        struct fe_priv *np = get_nvpriv(dev);
-       u8 *base;
+       u8 __iomem *base;
 
        spin_lock_irq(&np->lock);
        np->in_shutdown = 1;
@@ -1673,9 +1856,10 @@ static int nv_close(struct net_device *dev)
        spin_lock_irq(&np->lock);
        nv_stop_tx(dev);
        nv_stop_rx(dev);
-       base = get_hwbase(dev);
+       nv_txrx_reset(dev);
 
        /* disable interrupts on the nic or we will lock up */
+       base = get_hwbase(dev);
        writel(0, base + NvRegIrqMask);
        pci_push(base);
        dprintk(KERN_INFO "%s: Irqmask is zero again\n", dev->name);
@@ -1699,7 +1883,7 @@ static int __devinit nv_probe(struct pci_dev *pci_dev, const struct pci_device_i
        struct net_device *dev;
        struct fe_priv *np;
        unsigned long addr;
-       u8 *base;
+       u8 __iomem *base;
        int err, i;
 
        dev = alloc_etherdev(sizeof(struct fe_priv));
@@ -1761,9 +1945,10 @@ static int __devinit nv_probe(struct pci_dev *pci_dev, const struct pci_device_i
                np->desc_ver = DESC_VER_2;
 
        err = -ENOMEM;
-       dev->base_addr = (unsigned long) ioremap(addr, NV_PCI_REGSZ);
-       if (!dev->base_addr)
+       np->base = ioremap(addr, NV_PCI_REGSZ);
+       if (!np->base)
                goto out_relreg;
+       dev->base_addr = (unsigned long)np->base;
        dev->irq = pci_dev->irq;
        np->rx_ring = pci_alloc_consistent(pci_dev, sizeof(struct ring_desc) * (RX_RING + TX_RING),
                                                &np->ring_addr);
@@ -1777,7 +1962,7 @@ static int __devinit nv_probe(struct pci_dev *pci_dev, const struct pci_device_i
        dev->get_stats = nv_get_stats;
        dev->change_mtu = nv_change_mtu;
        dev->set_multicast_list = nv_set_multicast;
-       dev->do_ioctl = nv_ioctl;
+       SET_ETHTOOL_OPS(dev, &ops);
        dev->tx_timeout = nv_tx_timeout;
        dev->watchdog_timeo = NV_WATCHDOG_TIMEO;
 
@@ -1879,6 +2064,11 @@ static int __devinit nv_probe(struct pci_dev *pci_dev, const struct pci_device_i
                phy_init(dev);
        }
 
+       /* set default link speed settings */
+       np->linkspeed = NVREG_LINKSPEED_FORCE|NVREG_LINKSPEED_10;
+       np->duplex = 0;
+       np->autoneg = 1;
+
        err = register_netdev(dev);
        if (err) {
                printk(KERN_INFO "forcedeth: unable to register netdev: %d\n", err);
@@ -1910,7 +2100,7 @@ static void __devexit nv_remove(struct pci_dev *pci_dev)
 {
        struct net_device *dev = pci_get_drvdata(pci_dev);
        struct fe_priv *np = get_nvpriv(dev);
-       u8 *base = get_hwbase(dev);
+       u8 __iomem *base = get_hwbase(dev);
 
        unregister_netdev(dev);