]> err.no Git - linux-2.6/blobdiff - drivers/net/sky2.c
sky2: dont write status ring
[linux-2.6] / drivers / net / sky2.c
index 227df9876a2c67aa6f53a9e225586a8ac525acb4..ea23da53677b0c9740767a0324a7c8cdee859b0e 100644 (file)
@@ -79,6 +79,8 @@
 #define NAPI_WEIGHT            64
 #define PHY_RETRIES            1000
 
+#define RING_NEXT(x,s) (((x)+1) & ((s)-1))
+
 static const u32 default_msg =
     NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK
     | NETIF_MSG_TIMER | NETIF_MSG_TX_ERR | NETIF_MSG_RX_ERR
@@ -96,6 +98,10 @@ static int disable_msi = 0;
 module_param(disable_msi, int, 0);
 MODULE_PARM_DESC(disable_msi, "Disable Message Signaled Interrupt (MSI)");
 
+static int idle_timeout = 100;
+module_param(idle_timeout, int, 0);
+MODULE_PARM_DESC(idle_timeout, "Idle timeout workaround for lost interrupts (ms)");
+
 static const struct pci_device_id sky2_id_table[] = {
        { PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, 0x9000) },
        { PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, 0x9E00) },
@@ -719,7 +725,7 @@ static inline struct sky2_tx_le *get_tx_le(struct sky2_port *sky2)
 {
        struct sky2_tx_le *le = sky2->tx_le + sky2->tx_prod;
 
-       sky2->tx_prod = (sky2->tx_prod + 1) % TX_RING_SIZE;
+       sky2->tx_prod = RING_NEXT(sky2->tx_prod, TX_RING_SIZE);
        return le;
 }
 
@@ -735,7 +741,7 @@ static inline void sky2_put_idx(struct sky2_hw *hw, unsigned q, u16 idx)
 static inline struct sky2_rx_le *sky2_next_rx(struct sky2_port *sky2)
 {
        struct sky2_rx_le *le = sky2->rx_le + sky2->rx_put;
-       sky2->rx_put = (sky2->rx_put + 1) % RX_LE_SIZE;
+       sky2->rx_put = RING_NEXT(sky2->rx_put, RX_LE_SIZE);
        return le;
 }
 
@@ -1078,7 +1084,7 @@ err_out:
 /* Modular subtraction in ring */
 static inline int tx_dist(unsigned tail, unsigned head)
 {
-       return (head - tail) % TX_RING_SIZE;
+       return (head - tail) & (TX_RING_SIZE - 1);
 }
 
 /* Number of list elements available for next tx */
@@ -1255,7 +1261,7 @@ static int sky2_xmit_frame(struct sk_buff *skb, struct net_device *dev)
                le->opcode = OP_BUFFER | HW_OWNER;
 
                fre = sky2->tx_ring
-                   + ((re - sky2->tx_ring) + i + 1) % TX_RING_SIZE;
+                   + RING_NEXT((re - sky2->tx_ring) + i, TX_RING_SIZE);
                pci_unmap_addr_set(fre, mapaddr, mapping);
        }
 
@@ -1315,7 +1321,7 @@ static void sky2_tx_complete(struct sky2_port *sky2, u16 done)
 
                for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
                        struct tx_ring_info *fre;
-                       fre = sky2->tx_ring + (put + i + 1) % TX_RING_SIZE;
+                       fre = sky2->tx_ring + RING_NEXT(put + i, TX_RING_SIZE);
                        pci_unmap_page(pdev, pci_unmap_addr(fre, mapaddr),
                                       skb_shinfo(skb)->frags[i].size,
                                       PCI_DMA_TODEVICE);
@@ -1859,35 +1865,28 @@ static inline void sky2_tx_done(struct net_device *dev, u16 last)
 static int sky2_status_intr(struct sky2_hw *hw, int to_do)
 {
        int work_done = 0;
+       u16 hwidx = sky2_read16(hw, STAT_PUT_IDX);
 
        rmb();
 
-       for(;;) {
+       while (hw->st_idx != hwidx) {
                struct sky2_status_le *le  = hw->st_le + hw->st_idx;
                struct net_device *dev;
                struct sky2_port *sky2;
                struct sk_buff *skb;
                u32 status;
                u16 length;
-               u8  link, opcode;
-
-               opcode = le->opcode;
-               if (!opcode)
-                       break;
-               opcode &= ~HW_OWNER;
 
-               hw->st_idx = (hw->st_idx + 1) % STATUS_RING_SIZE;
-               le->opcode = 0;
+               hw->st_idx = RING_NEXT(hw->st_idx, STATUS_RING_SIZE);
 
-               link = le->link;
-               BUG_ON(link >= 2);
-               dev = hw->dev[link];
+               BUG_ON(le->link >= 2);
+               dev = hw->dev[le->link];
 
                sky2 = netdev_priv(dev);
                length = le->length;
                status = le->status;
 
-               switch (opcode) {
+               switch (le->opcode & ~HW_OWNER) {
                case OP_RXSTAT:
                        skb = sky2_receive(sky2, length, status);
                        if (!skb)
@@ -1927,7 +1926,8 @@ static int sky2_status_intr(struct sky2_hw *hw, int to_do)
 
                case OP_TXINDEXLE:
                        /* TX index reports status for both ports */
-                       sky2_tx_done(hw->dev[0], status & 0xffff);
+                       BUILD_BUG_ON(TX_RING_SIZE > 0x1000);
+                       sky2_tx_done(hw->dev[0], status & 0xfff);
                        if (hw->dev[1])
                                sky2_tx_done(hw->dev[1],
                                     ((status >> 24) & 0xff)
@@ -1937,8 +1937,8 @@ static int sky2_status_intr(struct sky2_hw *hw, int to_do)
                default:
                        if (net_ratelimit())
                                printk(KERN_WARNING PFX
-                                      "unknown status opcode 0x%x\n", opcode);
-                       break;
+                                      "unknown status opcode 0x%x\n", le->opcode);
+                       goto exit_loop;
                }
        }
 
@@ -2089,12 +2089,13 @@ static void sky2_descriptor_error(struct sky2_hw *hw, unsigned port,
  */
 static void sky2_idle(unsigned long arg)
 {
-       struct net_device *dev = (struct net_device *) arg;
+       struct sky2_hw *hw = (struct sky2_hw *) arg;
+       struct net_device *dev = hw->dev[0];
 
-       local_irq_disable();
        if (__netif_rx_schedule_prep(dev))
                __netif_rx_schedule(dev);
-       local_irq_enable();
+
+       mod_timer(&hw->idle_timer, jiffies + msecs_to_jiffies(idle_timeout));
 }
 
 
@@ -2105,65 +2106,46 @@ static int sky2_poll(struct net_device *dev0, int *budget)
        int work_done = 0;
        u32 status = sky2_read32(hw, B0_Y2_SP_EISR);
 
- restart_poll:
-       if (unlikely(status & ~Y2_IS_STAT_BMU)) {
-               if (status & Y2_IS_HW_ERR)
-                       sky2_hw_intr(hw);
+       if (status & Y2_IS_HW_ERR)
+               sky2_hw_intr(hw);
 
-               if (status & Y2_IS_IRQ_PHY1)
-                       sky2_phy_intr(hw, 0);
+       if (status & Y2_IS_IRQ_PHY1)
+               sky2_phy_intr(hw, 0);
 
-               if (status & Y2_IS_IRQ_PHY2)
-                       sky2_phy_intr(hw, 1);
+       if (status & Y2_IS_IRQ_PHY2)
+               sky2_phy_intr(hw, 1);
 
-               if (status & Y2_IS_IRQ_MAC1)
-                       sky2_mac_intr(hw, 0);
+       if (status & Y2_IS_IRQ_MAC1)
+               sky2_mac_intr(hw, 0);
 
-               if (status & Y2_IS_IRQ_MAC2)
-                       sky2_mac_intr(hw, 1);
+       if (status & Y2_IS_IRQ_MAC2)
+               sky2_mac_intr(hw, 1);
 
-               if (status & Y2_IS_CHK_RX1)
-                       sky2_descriptor_error(hw, 0, "receive", Y2_IS_CHK_RX1);
+       if (status & Y2_IS_CHK_RX1)
+               sky2_descriptor_error(hw, 0, "receive", Y2_IS_CHK_RX1);
 
-               if (status & Y2_IS_CHK_RX2)
-                       sky2_descriptor_error(hw, 1, "receive", Y2_IS_CHK_RX2);
+       if (status & Y2_IS_CHK_RX2)
+               sky2_descriptor_error(hw, 1, "receive", Y2_IS_CHK_RX2);
 
-               if (status & Y2_IS_CHK_TXA1)
-                       sky2_descriptor_error(hw, 0, "transmit", Y2_IS_CHK_TXA1);
-
-               if (status & Y2_IS_CHK_TXA2)
-                       sky2_descriptor_error(hw, 1, "transmit", Y2_IS_CHK_TXA2);
-       }
+       if (status & Y2_IS_CHK_TXA1)
+               sky2_descriptor_error(hw, 0, "transmit", Y2_IS_CHK_TXA1);
 
-       if (status & Y2_IS_STAT_BMU) {
-               work_done += sky2_status_intr(hw, work_limit - work_done);
-               *budget -= work_done;
-               dev0->quota -= work_done;
-
-               if (work_done >= work_limit)
-                       return 1;
+       if (status & Y2_IS_CHK_TXA2)
+               sky2_descriptor_error(hw, 1, "transmit", Y2_IS_CHK_TXA2);
 
+       if (status & Y2_IS_STAT_BMU)
                sky2_write32(hw, STAT_CTRL, SC_STAT_CLR_IRQ);
-       }
 
-       mod_timer(&hw->idle_timer, jiffies + HZ);
+       work_done = sky2_status_intr(hw, work_limit);
+       *budget -= work_done;
+       dev0->quota -= work_done;
 
-       local_irq_disable();
-       __netif_rx_complete(dev0);
+       if (work_done >= work_limit)
+               return 1;
 
-       status = sky2_read32(hw, B0_Y2_SP_LISR);
+       netif_rx_complete(dev0);
 
-       if (unlikely(status)) {
-               /* More work pending, try and keep going */
-               if (__netif_rx_schedule_prep(dev0)) {
-                       __netif_rx_reschedule(dev0, work_done);
-                       status = sky2_read32(hw, B0_Y2_SP_EISR);
-                       local_irq_enable();
-                       goto restart_poll;
-               }
-       }
-
-       local_irq_enable();
+       status = sky2_read32(hw, B0_Y2_SP_LISR);
        return 0;
 }
 
@@ -3302,7 +3284,10 @@ static int __devinit sky2_probe(struct pci_dev *pdev,
 
        sky2_write32(hw, B0_IMSK, Y2_IS_BASE);
 
-       setup_timer(&hw->idle_timer, sky2_idle, (unsigned long) dev);
+       setup_timer(&hw->idle_timer, sky2_idle, (unsigned long) hw);
+       if (idle_timeout > 0)
+               mod_timer(&hw->idle_timer,
+                         jiffies + msecs_to_jiffies(idle_timeout));
 
        pci_set_drvdata(pdev, hw);