Merge master.kernel.org:/pub/scm/linux/kernel/git/davem/net-2.6
[powerpc.git] / drivers / net / natsemi.c
index adf29dd..a8d7ff2 100644 (file)
@@ -260,7 +260,7 @@ static const struct {
 
 static const struct pci_device_id natsemi_pci_tbl[] __devinitdata = {
        { PCI_VENDOR_ID_NS, 0x0020, 0x12d9,     0x000c,     0, 0, 0 },
-       { PCI_VENDOR_ID_NS, 0x0020, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+       { PCI_VENDOR_ID_NS, 0x0020, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1 },
        { }     /* terminate list */
 };
 MODULE_DEVICE_TABLE(pci, natsemi_pci_tbl);
@@ -1712,7 +1712,7 @@ static void init_registers(struct net_device *dev)
 
        /* Enable interrupts by setting the interrupt mask. */
        writel(DEFAULT_INTR, ioaddr + IntrMask);
-       writel(1, ioaddr + IntrEnable);
+       natsemi_irq_enable(dev);
 
        writel(RxOn | TxOn, ioaddr + ChipCmd);
        writel(StatsClear, ioaddr + StatsCtrl); /* Clear Stats */
@@ -2024,6 +2024,7 @@ static int start_tx(struct sk_buff *skb, struct net_device *dev)
        struct netdev_private *np = netdev_priv(dev);
        void __iomem * ioaddr = ns_ioaddr(dev);
        unsigned entry;
+       unsigned long flags;
 
        /* Note: Ordering is important here, set the field with the
           "ownership" bit last, and only then increment cur_tx. */
@@ -2037,7 +2038,7 @@ static int start_tx(struct sk_buff *skb, struct net_device *dev)
 
        np->tx_ring[entry].addr = cpu_to_le32(np->tx_dma[entry]);
 
-       spin_lock_irq(&np->lock);
+       spin_lock_irqsave(&np->lock, flags);
 
        if (!np->hands_off) {
                np->tx_ring[entry].cmd_status = cpu_to_le32(DescOwn | skb->len);
@@ -2056,7 +2057,7 @@ static int start_tx(struct sk_buff *skb, struct net_device *dev)
                dev_kfree_skb_irq(skb);
                np->stats.tx_dropped++;
        }
-       spin_unlock_irq(&np->lock);
+       spin_unlock_irqrestore(&np->lock, flags);
 
        dev->trans_start = jiffies;
 
@@ -2118,28 +2119,35 @@ static irqreturn_t intr_handler(int irq, void *dev_instance)
        struct netdev_private *np = netdev_priv(dev);
        void __iomem * ioaddr = ns_ioaddr(dev);
 
-       if (np->hands_off)
+       /* Reading IntrStatus automatically acknowledges so don't do
+        * that while interrupts are disabled, (for example, while a
+        * poll is scheduled).  */
+       if (np->hands_off || !readl(ioaddr + IntrEnable))
                return IRQ_NONE;
 
-       /* Reading automatically acknowledges. */
        np->intr_status = readl(ioaddr + IntrStatus);
 
+       if (!np->intr_status)
+               return IRQ_NONE;
+
        if (netif_msg_intr(np))
                printk(KERN_DEBUG
                       "%s: Interrupt, status %#08x, mask %#08x.\n",
                       dev->name, np->intr_status,
                       readl(ioaddr + IntrMask));
 
-       if (!np->intr_status)
-               return IRQ_NONE;
-
        prefetch(&np->rx_skbuff[np->cur_rx % RX_RING_SIZE]);
 
        if (netif_rx_schedule_prep(dev)) {
                /* Disable interrupts and register for poll */
                natsemi_irq_disable(dev);
                __netif_rx_schedule(dev);
-       }
+       } else
+               printk(KERN_WARNING
+                      "%s: Ignoring interrupt, status %#08x, mask %#08x.\n",
+                      dev->name, np->intr_status,
+                      readl(ioaddr + IntrMask));
+
        return IRQ_HANDLED;
 }
 
@@ -2155,6 +2163,20 @@ static int natsemi_poll(struct net_device *dev, int *budget)
        int work_done = 0;
 
        do {
+               if (netif_msg_intr(np))
+                       printk(KERN_DEBUG
+                              "%s: Poll, status %#08x, mask %#08x.\n",
+                              dev->name, np->intr_status,
+                              readl(ioaddr + IntrMask));
+
+               /* netdev_rx() may read IntrStatus again if the RX state
+                * machine falls over so do it first. */
+               if (np->intr_status &
+                   (IntrRxDone | IntrRxIntr | RxStatusFIFOOver |
+                    IntrRxErr | IntrRxOverrun)) {
+                       netdev_rx(dev, &work_done, work_to_do);
+               }
+
                if (np->intr_status &
                    (IntrTxDone | IntrTxIntr | IntrTxIdle | IntrTxErr)) {
                        spin_lock(&np->lock);
@@ -2166,12 +2188,6 @@ static int natsemi_poll(struct net_device *dev, int *budget)
                if (np->intr_status & IntrAbnormalSummary)
                        netdev_error(dev, np->intr_status);
 
-               if (np->intr_status &
-                   (IntrRxDone | IntrRxIntr | RxStatusFIFOOver |
-                    IntrRxErr | IntrRxOverrun)) {
-                       netdev_rx(dev, &work_done, work_to_do);
-               }
-
                *budget -= work_done;
                dev->quota -= work_done;
 
@@ -2222,6 +2238,8 @@ static void netdev_rx(struct net_device *dev, int *work_done, int work_to_do)
                pkt_len = (desc_status & DescSizeMask) - 4;
                if ((desc_status&(DescMore|DescPktOK|DescRxLong)) != DescPktOK){
                        if (desc_status & DescMore) {
+                               unsigned long flags;
+
                                if (netif_msg_rx_err(np))
                                        printk(KERN_WARNING
                                                "%s: Oversized(?) Ethernet "
@@ -2236,12 +2254,12 @@ static void netdev_rx(struct net_device *dev, int *work_done, int work_to_do)
                                 * reset procedure documented in
                                 * AN-1287. */
 
-                               spin_lock_irq(&np->lock);
+                               spin_lock_irqsave(&np->lock, flags);
                                reset_rx(dev);
                                reinit_rx(dev);
                                writel(np->ring_dma, ioaddr + RxRingPtr);
                                check_link(dev);
-                               spin_unlock_irq(&np->lock);
+                               spin_unlock_irqrestore(&np->lock, flags);
 
                                /* We'll enable RX on exit from this
                                 * function. */
@@ -2271,7 +2289,6 @@ static void netdev_rx(struct net_device *dev, int *work_done, int work_to_do)
                         * without copying to a minimally-sized skbuff. */
                        if (pkt_len < rx_copybreak
                            && (skb = dev_alloc_skb(pkt_len + RX_OFFSET)) != NULL) {
-                               skb->dev = dev;
                                /* 16 byte align the IP header */
                                skb_reserve(skb, RX_OFFSET);
                                pci_dma_sync_single_for_cpu(np->pci_dev,
@@ -3057,7 +3074,7 @@ static void enable_wol_mode(struct net_device *dev, int enable_intr)
                 * Could be used to send a netlink message.
                 */
                writel(WOLPkt | LinkChange, ioaddr + IntrMask);
-               writel(1, ioaddr + IntrEnable);
+               natsemi_irq_enable(dev);
        }
 }
 
@@ -3188,7 +3205,7 @@ static int natsemi_suspend (struct pci_dev *pdev, pm_message_t state)
                disable_irq(dev->irq);
                spin_lock_irq(&np->lock);
 
-               writel(0, ioaddr + IntrEnable);
+               natsemi_irq_disable(dev);
                np->hands_off = 1;
                natsemi_stop_rxtx(dev);
                netif_stop_queue(dev);