]> err.no Git - linux-2.6/blobdiff - drivers/net/sky2.c
[PATCH] sky2: dual port tx completion
[linux-2.6] / drivers / net / sky2.c
index aff347f1ea4bd2a6516d996120f60596d3164be2..52c7b1886314ea7260eb99e62ce872acadc7372e 100644 (file)
@@ -723,11 +723,17 @@ static inline struct sky2_rx_le *sky2_next_rx(struct sky2_port *sky2)
        return le;
 }
 
+/* Return high part of DMA address (could be 32 or 64 bit) */
+static inline u32 high32(dma_addr_t a)
+{
+       return (a >> 16) >> 16;
+}
+
 /* Build description to hardware about buffer */
 static inline void sky2_rx_add(struct sky2_port *sky2, struct ring_info *re)
 {
        struct sky2_rx_le *le;
-       u32 hi = (re->mapaddr >> 16) >> 16;
+       u32 hi = high32(re->mapaddr);
 
        re->idx = sky2->rx_put;
        if (sky2->rx_addr64 != hi) {
@@ -735,7 +741,7 @@ static inline void sky2_rx_add(struct sky2_port *sky2, struct ring_info *re)
                le->addr = cpu_to_le32(hi);
                le->ctrl = 0;
                le->opcode = OP_ADDR64 | HW_OWNER;
-               sky2->rx_addr64 = hi;
+               sky2->rx_addr64 = high32(re->mapaddr + re->maplen);
        }
 
        le = sky2_next_rx(sky2);
@@ -1100,17 +1106,17 @@ static int sky2_xmit_frame(struct sk_buff *skb, struct net_device *dev)
 
        len = skb_headlen(skb);
        mapping = pci_map_single(hw->pdev, skb->data, len, PCI_DMA_TODEVICE);
-       addr64 = (mapping >> 16) >> 16;
+       addr64 = high32(mapping);
 
        re = sky2->tx_ring + sky2->tx_prod;
 
-       /* Send high bits if changed */
-       if (addr64 != sky2->tx_addr64) {
+       /* Send high bits if changed or crosses boundary */
+       if (addr64 != sky2->tx_addr64 || high32(mapping + len) != sky2->tx_addr64) {
                le = get_tx_le(sky2);
                le->tx.addr = cpu_to_le32(addr64);
                le->ctrl = 0;
                le->opcode = OP_ADDR64 | HW_OWNER;
-               sky2->tx_addr64 = addr64;
+               sky2->tx_addr64 = high32(mapping + len);
        }
 
        /* Check for TCP Segmentation Offload */
@@ -1236,6 +1242,9 @@ static void sky2_tx_complete(struct sky2_port *sky2, u16 done)
        struct net_device *dev = sky2->netdev;
        unsigned i;
 
+       if (done == sky2->tx_cons)
+               return;
+
        if (unlikely(netif_msg_tx_done(sky2)))
                printk(KERN_DEBUG "%s: tx done, up to %u\n",
                       dev->name, done);
@@ -1292,8 +1301,16 @@ static int sky2_down(struct net_device *dev)
        if (netif_msg_ifdown(sky2))
                printk(KERN_INFO PFX "%s: disabling interface\n", dev->name);
 
+       /* Stop more packets from being queued */
        netif_stop_queue(dev);
 
+       /* Disable port IRQ */
+       local_irq_disable();
+       hw->intr_mask &= ~((sky2->port == 0) ? Y2_IS_IRQ_PHY1 : Y2_IS_IRQ_PHY2);
+       sky2_write32(hw, B0_IMSK, hw->intr_mask);
+       local_irq_enable();
+
+
        sky2_phy_reset(hw, port);
 
        /* Stop transmitter */
@@ -1340,6 +1357,8 @@ static int sky2_down(struct net_device *dev)
        /* turn off LED's */
        sky2_write16(hw, B0_Y2LED, LED_STAT_OFF);
 
+       synchronize_irq(hw->pdev->irq);
+
        sky2_tx_clean(sky2);
        sky2_rx_clean(sky2);
 
@@ -1580,9 +1599,12 @@ static int sky2_change_mtu(struct net_device *dev, int new_mtu)
                return 0;
        }
 
-       local_irq_disable();
        sky2_write32(hw, B0_IMSK, 0);
 
+       dev->trans_start = jiffies;     /* prevent tx timeout */
+       netif_stop_queue(dev);
+       netif_poll_disable(hw->dev[0]);
+
        ctl = gma_read16(hw, sky2->port, GM_GP_CTRL);
        gma_write16(hw, sky2->port, GM_GP_CTRL, ctl & ~GM_GPCR_RX_ENA);
        sky2_rx_stop(sky2);
@@ -1602,9 +1624,10 @@ static int sky2_change_mtu(struct net_device *dev, int new_mtu)
        err = sky2_rx_start(sky2);
        gma_write16(hw, sky2->port, GM_GP_CTRL, ctl);
 
+       netif_poll_disable(hw->dev[0]);
+       netif_wake_queue(dev);
        sky2_write32(hw, B0_IMSK, hw->intr_mask);
-       sky2_read32(hw, B0_IMSK);
-       local_irq_enable();
+
        return err;
 }
 
@@ -1691,16 +1714,18 @@ error:
        goto resubmit;
 }
 
-/* Transmit ring index in reported status block is encoded as:
- *
- *   | TXS2 | TXA2 | TXS1 | TXA1
+/*
+ * Check for transmit complete
  */
-static inline u16 tx_index(u8 port, u32 status, u16 len)
+static inline void sky2_tx_check(struct sky2_hw *hw, int port)
 {
-       if (port == 0)
-               return status & 0xfff;
-       else
-               return ((status >> 24) & 0xff) | (len & 0xf) << 8;
+       struct net_device *dev = hw->dev[port];
+
+       if (dev && netif_running(dev)) {
+               sky2_tx_complete(netdev_priv(dev),
+                                sky2_read16(hw, port == 0
+                                            ? STAT_TXA1_RIDX : STAT_TXA2_RIDX));
+       }
 }
 
 /*
@@ -1783,8 +1808,7 @@ static int sky2_poll(struct net_device *dev0, int *budget)
                        break;
 
                case OP_TXINDEXLE:
-                       sky2_tx_complete(sky2,
-                                        tx_index(sky2->port, status, length));
+                       /* pick up transmit status later */
                        break;
 
                default:
@@ -1796,6 +1820,8 @@ static int sky2_poll(struct net_device *dev0, int *budget)
        }
 
 exit_loop:
+       sky2_tx_check(hw, 0);
+       sky2_tx_check(hw, 1);
 
        mmiowb();
 
@@ -1955,10 +1981,11 @@ static irqreturn_t sky2_intr(int irq, void *dev_id, struct pt_regs *regs)
        if (status & Y2_IS_STAT_BMU) {
                hw->intr_mask &= ~Y2_IS_STAT_BMU;
                sky2_write32(hw, B0_IMSK, hw->intr_mask);
-               prefetch(&hw->st_le[hw->st_idx]);
 
-               if (netif_rx_schedule_test(dev0))
+               if (likely(__netif_rx_schedule_prep(dev0))) {
+                       prefetch(&hw->st_le[hw->st_idx]);
                        __netif_rx_schedule(dev0);
+               }
        }
 
        if (status & Y2_IS_IRQ_PHY1)