]> git.hungrycats.org Git - linux/commitdiff
[PATCH] [2.6] Update forcedeth to 0.23
authorCarl-Daniel Hailfinger <c-d.hailfinger.kernel.2004@gmx.net>
Wed, 4 Feb 2004 23:48:08 +0000 (18:48 -0500)
committerJeff Garzik <jgarzik@redhat.com>
Wed, 4 Feb 2004 23:48:08 +0000 (18:48 -0500)
This is a multi-part message in MIME format.

drivers/net/forcedeth.c

index d4e3796b1935bd793c2323eefa53cffb2f3b0b5f..453f7e389e66ab970a52d05085620cb5f8d5f791 100644 (file)
  *                        addresses, really stop rx if already running
  *                        in start_rx, clean up a bit.
  *                             (C) Carl-Daniel Hailfinger
- *     0.20: 07 Dev 2003: alloc fixes
+ *     0.20: 07 Dec 2003: alloc fixes
+ *     0.21: 12 Jan 2004: additional alloc fix, nic polling fix.
+ *     0.22: 19 Jan 2004: reprogram timer to a sane rate, avoid lockup
+ *                        on close.
+ *                             (C) Carl-Daniel Hailfinger, Manfred Spraul
+ *     0.23: 26 Jan 2004: various small cleanups
  *
  * Known bugs:
- * The irq handling is wrong - no tx done interrupts are generated.
- * This means recovery from netif_stop_queue only happens in the hw timer
- * interrupt (1/2 second on nForce2, 1/100 second on nForce3), or if an
- * rx packet arrives by chance.
+ * We suspect that on some hardware no TX done interrupts are generated.
+ * This means recovery from netif_stop_queue only happens if the hw timer
+ * interrupt fires (100 times/second, configurable with NVREG_POLL_DEFAULT)
+ * and the timer is active in the IRQMask, or if a rx packet arrives by chance.
+ * If your hardware reliably generates tx done interrupts, then you can remove
+ * DEV_NEED_TIMERIRQ from the driver_data flags.
+ * DEV_NEED_TIMERIRQ will not harm you on sane hardware, only generating a few
+ * superfluous timer interrupts from the nic.
  */
-#define FORCEDETH_VERSION              "0.19"
+#define FORCEDETH_VERSION              "0.23"
 
 #include <linux/module.h>
 #include <linux/types.h>
 #define DEV_NEED_LASTPACKET1   0x0001
 #define DEV_IRQMASK_1          0x0002
 #define DEV_IRQMASK_2          0x0004
+#define DEV_NEED_TIMERIRQ      0x0008
 
 enum {
        NvRegIrqStatus = 0x000,
@@ -124,7 +134,12 @@ enum {
        NvRegUnknownSetupReg6 = 0x008,
 #define NVREG_UNKSETUP6_VAL            3
 
+/*
+ * NVREG_POLL_DEFAULT is the interval length of the timer source on the nic
+ * NVREG_POLL_DEFAULT=97 would result in an interval length of 1 ms
+ */
        NvRegPollingInterval = 0x00c,
+#define NVREG_POLL_DEFAULT     970
        NvRegMisc1 = 0x080,
 #define NVREG_MISC1_HD         0x02
 #define NVREG_MISC1_FORCE      0x3b0f3c
@@ -286,7 +301,7 @@ struct ring_desc {
 #define NV_WAKEUPMASKENTRIES   4
 
 /* General driver defaults */
-#define NV_WATCHDOG_TIMEO      (2*HZ)
+#define NV_WATCHDOG_TIMEO      (5*HZ)
 #define DEFAULT_MTU            1500    /* also maximum supported, at least for now */
 
 #define RX_RING                128
@@ -520,12 +535,12 @@ static void txrx_reset(struct net_device *dev)
 }
 
 /*
- * get_stats: dev->get_stats function
+ * nv_get_stats: dev->get_stats function
  * Get latest stats value from the nic.
  * Called with read_lock(&dev_base_lock) held for read -
  * only synchronized against unregister_netdevice.
  */
-static struct net_device_stats *get_stats(struct net_device *dev)
+static struct net_device_stats *nv_get_stats(struct net_device *dev)
 {
        struct fe_priv *np = get_nvpriv(dev);
 
@@ -536,14 +551,55 @@ static struct net_device_stats *get_stats(struct net_device *dev)
        return &np->stats;
 }
 
+static int nv_ethtool_ioctl (struct net_device *dev, void *useraddr)
+{
+       struct fe_priv *np = get_nvpriv(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;
+       }
 
+       default:
+               break;
+       }
+
+       return -EOPNOTSUPP;
+}
 /*
- * nic_ioctl: dev->do_ioctl function
+ * nv_ioctl: dev->do_ioctl function
  * Called with rtnl_lock held.
  */
-static int nic_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+static int nv_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
 {
-       return -EOPNOTSUPP;
+       switch(cmd) {
+       case SIOCETHTOOL:
+               return nv_ethtool_ioctl(dev, (void *) rq->ifr_data);
+
+       default:
+               return -EOPNOTSUPP;
+       }
 }
 
 /*
@@ -661,10 +717,10 @@ static void drain_ring(struct net_device *dev)
 }
 
 /*
- * start_xmit: dev->hard_start_xmit function
+ * nv_start_xmit: dev->hard_start_xmit function
  * Called with dev->xmit_lock held.
  */
-static int start_xmit(struct sk_buff *skb, struct net_device *dev)
+static int nv_start_xmit(struct sk_buff *skb, struct net_device *dev)
 {
        struct fe_priv *np = get_nvpriv(dev);
        int nr = np->next_tx % TX_RING;
@@ -679,7 +735,7 @@ static int start_xmit(struct sk_buff *skb, struct net_device *dev)
        spin_lock_irq(&np->lock);
        wmb();
        np->tx_ring[nr].Flags = np->tx_flags;
-       dprintk(KERN_DEBUG "%s: start_xmit: packet packet %d queued for transmission.\n",
+       dprintk(KERN_DEBUG "%s: nv_start_xmit: packet packet %d queued for transmission.\n",
                                dev->name, np->next_tx);
        {
                int j;
@@ -698,6 +754,7 @@ static int start_xmit(struct sk_buff *skb, struct net_device *dev)
                netif_stop_queue(dev);
        spin_unlock_irq(&np->lock);
        writel(NVREG_TXRXCTL_KICK, get_hwbase(dev) + NvRegTxRxControl);
+       pci_push(get_hwbase(dev));
        return 0;
 }
 
@@ -743,10 +800,10 @@ static void tx_done(struct net_device *dev)
 }
 
 /*
- * tx_timeout: dev->tx_timeout function
+ * nv_tx_timeout: dev->tx_timeout function
  * Called with dev->xmit_lock held.
  */
-static void tx_timeout(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);
@@ -797,13 +854,13 @@ static void rx_process(struct net_device *dev)
                        break;  /* still owned by hardware, */
 
                /*
-                * the packet is for us - immediately tear down the pci mapping, and
-                * prefetch the first cacheline of the packet.
+                * the packet is for us - immediately tear down the pci mapping.
+                * TODO: check if a prefetch of the first cacheline improves
+                * the performance.
                 */
                pci_unmap_single(np->pci_dev, np->rx_dma[i],
                                np->rx_skbuff[i]->len,
                                PCI_DMA_FROMDEVICE);
-               prefetch(np->rx_skbuff[i]->data);
 
                {
                        int j;
@@ -870,10 +927,10 @@ next_pkt:
 }
 
 /*
- * change_mtu: dev->change_mtu function
+ * nv_change_mtu: dev->change_mtu function
  * Called with dev_base_lock held for read.
  */
-static int change_mtu(struct net_device *dev, int new_mtu)
+static int nv_change_mtu(struct net_device *dev, int new_mtu)
 {
        if (new_mtu > DEFAULT_MTU)
                return -EINVAL;
@@ -882,10 +939,10 @@ static int change_mtu(struct net_device *dev, int new_mtu)
 }
 
 /*
- * change_mtu: dev->change_mtu function
+ * nv_set_multicast: dev->set_multicast function
  * Called with dev->xmit_lock held.
  */
-static void set_multicast(struct net_device *dev)
+static void nv_set_multicast(struct net_device *dev)
 {
        struct fe_priv *np = get_nvpriv(dev);
        u8 *base = get_hwbase(dev);
@@ -1098,13 +1155,13 @@ static void do_nic_poll(unsigned long data)
        enable_irq(dev->irq);
 }
 
-static int open(struct net_device *dev)
+static int nv_open(struct net_device *dev)
 {
        struct fe_priv *np = get_nvpriv(dev);
        u8 *base = get_hwbase(dev);
        int ret, oom, i;
 
-       dprintk(KERN_DEBUG "forcedeth: open\n");
+       dprintk(KERN_DEBUG "nv_open: begin\n");
 
        /* 1) erase previous misconfiguration */
        /* 4.1-1: stop adapter: ignored, 4.3 seems to be overkill */
@@ -1152,17 +1209,23 @@ static int open(struct net_device *dev)
        for (i = 1; i < 32; i++) {
                int id1, id2;
 
+               spin_lock_irq(&np->lock);
                id1 = mii_rw(dev, i, MII_PHYSID1, MII_READ);
-               if (id1 < 0)
+               spin_unlock_irq(&np->lock);
+               if (id1 < 0 || id1 == 0xffff)
                        continue;
+               spin_lock_irq(&np->lock);
                id2 = mii_rw(dev, i, MII_PHYSID2, MII_READ);
-               if (id2 < 0)
+               spin_unlock_irq(&np->lock);
+               if (id2 < 0 || id2 == 0xffff)
                        continue;
                dprintk(KERN_DEBUG "%s: open: Found PHY %04x:%04x at address %d.\n",
                                dev->name, id1, id2, i);
                np->phyaddr = i;
 
+               spin_lock_irq(&np->lock);
                update_linkspeed(dev);
+               spin_unlock_irq(&np->lock);
 
                break;
        }
@@ -1185,6 +1248,7 @@ static int open(struct net_device *dev)
        writel(NVREG_RNDSEED_FORCE | (i&NVREG_RNDSEED_MASK), base + NvRegRandomSeed);
        writel(NVREG_UNKSETUP1_VAL, base + NvRegUnknownSetupReg1);
        writel(NVREG_UNKSETUP2_VAL, base + NvRegUnknownSetupReg2);
+       writel(NVREG_POLL_DEFAULT, base + NvRegPollingInterval);
        writel(NVREG_UNKSETUP6_VAL, base + NvRegUnknownSetupReg6);
        writel((np->phyaddr << NVREG_ADAPTCTL_PHYSHIFT)|NVREG_ADAPTCTL_PHYVALID,
                        base + NvRegAdapterControl);
@@ -1198,9 +1262,9 @@ static int open(struct net_device *dev)
                        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, base + NvRegPowerState);
@@ -1232,7 +1296,9 @@ static int open(struct net_device *dev)
        netif_start_queue(dev);
        if (oom)
                mod_timer(&np->oom_kick, jiffies + OOM_REFILL);
-       if (!(mii_rw(dev, np->phyaddr, MII_BMSR, MII_READ) & BMSR_ANEGCOMPLETE)) {
+       if (mii_rw(dev, np->phyaddr, MII_BMSR, MII_READ) & BMSR_ANEGCOMPLETE) {
+               netif_carrier_on(dev);
+       } else {
                printk("%s: no link during initialization.\n", dev->name);
                netif_carrier_off(dev);
        }
@@ -1245,9 +1311,10 @@ out_drain:
        return ret;
 }
 
-static int close(struct net_device *dev)
+static int nv_close(struct net_device *dev)
 {
        struct fe_priv *np = get_nvpriv(dev);
+       u8 *base;
 
        spin_lock_irq(&np->lock);
        np->in_shutdown = 1;
@@ -1261,6 +1328,13 @@ static int close(struct net_device *dev)
        spin_lock_irq(&np->lock);
        stop_tx(dev);
        stop_rx(dev);
+       base = get_hwbase(dev);
+
+       /* disable interrupts on the nic or we will lock up */
+       writel(0, base + NvRegIrqMask);
+       pci_push(base);
+       dprintk(KERN_INFO "%s: Irqmask is zero again\n", dev->name);
+
        spin_unlock_irq(&np->lock);
 
        free_irq(dev->irq, dev);
@@ -1272,7 +1346,7 @@ static int close(struct net_device *dev)
        return 0;
 }
 
-static int __devinit probe_nic(struct pci_dev *pci_dev, const struct pci_device_id *id)
+static int __devinit nv_probe(struct pci_dev *pci_dev, const struct pci_device_id *id)
 {
        struct net_device *dev;
        struct fe_priv *np;
@@ -1281,11 +1355,11 @@ static int __devinit probe_nic(struct pci_dev *pci_dev, const struct pci_device_
        int err, i;
 
        dev = alloc_etherdev(sizeof(struct fe_priv));
-       np = get_nvpriv(dev);
        err = -ENOMEM;
        if (!dev)
                goto out;
 
+       np = get_nvpriv(dev);
        np->pci_dev = pci_dev;
        spin_lock_init(&np->lock);
        SET_MODULE_OWNER(dev);
@@ -1333,7 +1407,7 @@ static int __devinit probe_nic(struct pci_dev *pci_dev, const struct pci_device_
        err = -ENOMEM;
        dev->base_addr = (unsigned long) ioremap(addr, NV_PCI_REGSZ);
        if (!dev->base_addr)
-               goto out_disable;
+               goto out_relreg;
        dev->irq = pci_dev->irq;
        np->rx_ring = pci_alloc_consistent(pci_dev, sizeof(struct ring_desc) * (RX_RING + TX_RING),
                                                &np->ring_addr);
@@ -1341,19 +1415,18 @@ static int __devinit probe_nic(struct pci_dev *pci_dev, const struct pci_device_
                goto out_unmap;
        np->tx_ring = &np->rx_ring[RX_RING];
 
-       dev->open = open;
-       dev->stop = close;
-       dev->hard_start_xmit = start_xmit;
-       dev->get_stats = get_stats;
-       dev->change_mtu = change_mtu;
-       dev->set_multicast_list = set_multicast;
-       dev->do_ioctl = nic_ioctl;
-       dev->tx_timeout = tx_timeout;
+       dev->open = nv_open;
+       dev->stop = nv_close;
+       dev->hard_start_xmit = nv_start_xmit;
+       dev->get_stats = nv_get_stats;
+       dev->change_mtu = nv_change_mtu;
+       dev->set_multicast_list = nv_set_multicast;
+       dev->do_ioctl = nv_ioctl;
+       dev->tx_timeout = nv_tx_timeout;
        dev->watchdog_timeo = NV_WATCHDOG_TIMEO;
 
        pci_set_drvdata(pci_dev, dev);
 
-
        /* read the mac address */
        base = get_hwbase(dev);
        np->orig_mac[0] = readl(base + NvRegMacAddrA);
@@ -1393,6 +1466,8 @@ static int __devinit probe_nic(struct pci_dev *pci_dev, const struct pci_device_
                np->irqmask = NVREG_IRQMASK_WANTED_1;
        if (id->driver_data & DEV_IRQMASK_2)
                np->irqmask = NVREG_IRQMASK_WANTED_2;
+       if (id->driver_data & DEV_NEED_TIMERIRQ)
+               np->irqmask |= NVREG_IRQ_TIMER;
 
        err = register_netdev(dev);
        if (err) {
@@ -1408,6 +1483,7 @@ static int __devinit probe_nic(struct pci_dev *pci_dev, const struct pci_device_
 out_freering:
        pci_free_consistent(np->pci_dev, sizeof(struct ring_desc) * (RX_RING + TX_RING),
                                np->rx_ring, np->ring_addr);
+       pci_set_drvdata(pci_dev, NULL);
 out_unmap:
        iounmap(get_hwbase(dev));
 out_relreg:
@@ -1416,12 +1492,11 @@ out_disable:
        pci_disable_device(pci_dev);
 out_free:
        free_netdev(dev);
-       pci_set_drvdata(pci_dev, NULL);
 out:
        return err;
 }
 
-static void __devexit remove_nic(struct pci_dev *pci_dev)
+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);
@@ -1430,7 +1505,7 @@ static void __devexit remove_nic(struct pci_dev *pci_dev)
        unregister_netdev(dev);
 
        /* special op: write back the misordered MAC address - otherwise
-        * the next probe_nic would see a wrong address.
+        * the next nv_probe would see a wrong address.
         */
        writel(np->orig_mac[0], base + NvRegMacAddrA);
        writel(np->orig_mac[1], base + NvRegMacAddrB);
@@ -1450,21 +1525,21 @@ static struct pci_device_id pci_tbl[] = {
                .device = 0x1C3,
                .subvendor = PCI_ANY_ID,
                .subdevice = PCI_ANY_ID,
-               .driver_data = DEV_IRQMASK_1,
+               .driver_data = DEV_IRQMASK_1|DEV_NEED_TIMERIRQ,
        },
        {       /* nForce2 Ethernet Controller */
                .vendor = PCI_VENDOR_ID_NVIDIA,
                .device = 0x0066,
                .subvendor = PCI_ANY_ID,
                .subdevice = PCI_ANY_ID,
-               .driver_data = DEV_NEED_LASTPACKET1|DEV_IRQMASK_2,
+               .driver_data = DEV_NEED_LASTPACKET1|DEV_IRQMASK_2|DEV_NEED_TIMERIRQ,
        },
        {       /* nForce3 Ethernet Controller */
                .vendor = PCI_VENDOR_ID_NVIDIA,
                .device = 0x00D6,
                .subvendor = PCI_ANY_ID,
                .subdevice = PCI_ANY_ID,
-               .driver_data = DEV_NEED_LASTPACKET1|DEV_IRQMASK_2,
+               .driver_data = DEV_NEED_LASTPACKET1|DEV_IRQMASK_2|DEV_NEED_TIMERIRQ,
        },
        {0,},
 };
@@ -1472,8 +1547,8 @@ static struct pci_device_id pci_tbl[] = {
 static struct pci_driver driver = {
        .name = "forcedeth",
        .id_table = pci_tbl,
-       .probe = probe_nic,
-       .remove = __devexit_p(remove_nic),
+       .probe = nv_probe,
+       .remove = __devexit_p(nv_remove),
 };