]> err.no Git - linux-2.6/commitdiff
[PATCH] sky2: changing mtu doesn't have to reset link
authorshemminger@osdl.org <shemminger@osdl.org>
Tue, 27 Sep 2005 22:02:55 +0000 (15:02 -0700)
committerJeff Garzik <jgarzik@pobox.com>
Wed, 28 Sep 2005 15:49:32 +0000 (11:49 -0400)
Changing the MTU of the network device doesn't mean the whole link
has to be brought down and back up again.  Just stopping the receive
engine is good enough.

Signed-off-by: Stephen Hemminger <shmminger @osdl.org>
Signed-off-by: Jeff Garzik <jgarzik@pobox.com>
drivers/net/sky2.c

index d2c0d04be72332cefe83abc861e9fdd3d1349b97..0cc57293faf976758782d5b42527e610772eff8b 100644 (file)
@@ -468,9 +468,9 @@ static void sky2_mac_init(struct sky2_hw *hw, unsigned port)
 
        /* serial mode register */
        reg = DATA_BLIND_VAL(DATA_BLIND_DEF) |
-           GM_SMOD_VLAN_ENA | IPG_DATA_VAL(IPG_DATA_DEF);
+               GM_SMOD_VLAN_ENA | IPG_DATA_VAL(IPG_DATA_DEF);
 
-       if (hw->dev[port]->mtu > 1500)
+       if (hw->dev[port]->mtu > ETH_DATA_LEN)
                reg |= GM_SMOD_JUMBO_ENA;
 
        gma_write16(hw, port, GM_SERIAL_MODE, reg);
@@ -515,8 +515,6 @@ static void sky2_ramset(struct sky2_hw *hw, u16 q, u32 start, size_t len)
        len /= 8;
        end = start + len - 1;
 
-       pr_debug("sky2_ramset start=%d end=%d\n", start, end);
-
        sky2_write8(hw, RB_ADDR(q, RB_CTRL), RB_RST_CLR);
        sky2_write32(hw, RB_ADDR(q, RB_START), start);
        sky2_write32(hw, RB_ADDR(q, RB_END), end);
@@ -528,7 +526,6 @@ static void sky2_ramset(struct sky2_hw *hw, u16 q, u32 start, size_t len)
 
                rxlo = len/2;
                rxup = rxlo + len/4;
-               pr_debug(" utpp=%d ltpp=%d\n", rxup, rxlo);
 
                /* Set thresholds on receive queue's */
                sky2_write32(hw, RB_ADDR(q, RB_RX_UTPP), rxup);
@@ -662,16 +659,44 @@ static void rx_set_checksum(struct sky2_port *sky2)
        le->ctrl = 0;
        le->opcode = OP_TCPSTART | HW_OWNER;
 
-       sky2_write16(sky2->hw, Y2_QADDR(rxqaddr[sky2->port],
-                                       PREF_UNIT_PUT_IDX), sky2->rx_put);
-       sky2_read16(sky2->hw, Y2_QADDR(rxqaddr[sky2->port], PREF_UNIT_PUT_IDX));
-       mdelay(1);
        sky2_write32(sky2->hw,
                     Q_ADDR(rxqaddr[sky2->port], Q_CSR),
                     sky2->rx_csum ? BMU_ENA_RX_CHKSUM : BMU_DIS_RX_CHKSUM);
 
 }
 
+/*
+ * The RX Stop command will not work for Yukon-2 if the BMU does not
+ * reach the end of packet and since we can't make sure that we have
+ * incoming data, we must reset the BMU while it is not doing a DMA
+ * transfer. Since it is possible that the RX path is still active,
+ * the RX RAM buffer will be stopped first, so any possible incoming
+ * data will not trigger a DMA. After the RAM buffer is stopped, the
+ * BMU is polled until any DMA in progress is ended and only then it
+ * will be reset.
+ */
+static void sky2_rx_stop(struct sky2_port *sky2)
+{
+       struct sky2_hw *hw = sky2->hw;
+       unsigned rxq = rxqaddr[sky2->port];
+       int i;
+
+       /* disable the RAM Buffer receive queue */
+       sky2_write8(hw, RB_ADDR(rxq, RB_CTRL), RB_DIS_OP_MD);
+
+       for (i = 0; i < 0xffff; i++)
+               if (sky2_read8(hw, RB_ADDR(rxq, Q_RSL))
+                   == sky2_read8(hw, RB_ADDR(rxq, Q_RL)))
+                       goto stopped;
+
+       printk(KERN_WARNING PFX "%s: receiver stop failed\n",
+              sky2->netdev->name);
+stopped:
+       sky2_write32(hw, Q_ADDR(rxq, Q_CSR), BMU_RST_SET | BMU_FIFO_RST);
+
+       /* reset the Rx prefetch unit */
+       sky2_write32(hw, Y2_QADDR(rxq, PREF_UNIT_CTRL), PREF_UNIT_RST_SET);
+}
 
 /* Cleanout receive buffer area, assumes receiver hardware stopped */
 static void sky2_rx_clean(struct sky2_port *sky2)
@@ -693,7 +718,7 @@ static void sky2_rx_clean(struct sky2_port *sky2)
 }
 
 #define roundup(x, y)   ((((x)+((y)-1))/(y))*(y))
-static inline unsigned sky2_rx_size(const struct sky2_port *sky2)
+static inline unsigned rx_size(const struct sky2_port *sky2)
 {
        return roundup(sky2->netdev->mtu + ETH_HLEN + 4, 8);
 }
@@ -709,12 +734,18 @@ static inline unsigned sky2_rx_size(const struct sky2_port *sky2)
  * is not aligned.  This means we can't use skb_reserve to align
  * the IP header.
  */
-static int sky2_rx_fill(struct sky2_port *sky2)
+static int sky2_rx_start(struct sky2_port *sky2)
 {
-       unsigned i;
-       unsigned size = sky2_rx_size(sky2);
+       struct sky2_hw *hw = sky2->hw;
+       unsigned size = rx_size(sky2);
+       unsigned rxq = rxqaddr[sky2->port];
+       int i;
+
+       sky2->rx_put = sky2->rx_next = 0;
+       sky2_qset(hw, rxq, is_pciex(hw) ? 0x80 : 0x600);
+       sky2_prefetch_init(hw, rxq, sky2->rx_le_map, RX_LE_SIZE - 1);
 
-       pr_debug("rx_fill size=%d\n", size);
+       rx_set_checksum(sky2);
        for (i = 0; i < sky2->rx_pending; i++) {
                struct ring_info *re = sky2->rx_ring + i;
 
@@ -722,12 +753,15 @@ static int sky2_rx_fill(struct sky2_port *sky2)
                if (!re->skb)
                        goto nomem;
 
-               re->mapaddr = pci_map_single(sky2->hw->pdev, re->skb->data,
+               re->mapaddr = pci_map_single(hw->pdev, re->skb->data,
                                             size, PCI_DMA_FROMDEVICE);
                re->maplen = size;
                sky2_rx_add(sky2, re);
        }
 
+       /* Tell chip about available buffers */
+       sky2_write16(hw, Y2_QADDR(rxq, PREF_UNIT_PUT_IDX), sky2->rx_put);
+       sky2->rx_last_put = sky2_read16(hw, Y2_QADDR(rxq, PREF_UNIT_PUT_IDX));
        return 0;
 nomem:
        sky2_rx_clean(sky2);
@@ -792,28 +826,14 @@ static int sky2_up(struct net_device *dev)
        sky2_write8(hw, RB_ADDR(port == 0 ? Q_XS1 : Q_XS2, RB_CTRL),
                    RB_RST_SET);
 
-       sky2_qset(hw, rxqaddr[port], is_pciex(hw) ? 0x80 : 0x600);
        sky2_qset(hw, txqaddr[port], 0x600);
+       sky2_prefetch_init(hw, txqaddr[port], sky2->tx_le_map,
+                          TX_RING_SIZE - 1);
 
-       sky2->rx_put = sky2->rx_next = 0;
-       sky2_prefetch_init(hw, rxqaddr[port], sky2->rx_le_map, RX_LE_SIZE - 1);
-
-       rx_set_checksum(sky2);
-
-       err = sky2_rx_fill(sky2);
+       err = sky2_rx_start(sky2);
        if (err)
                goto err_out;
 
-       /* Give buffers to receiver */
-       sky2_write16(sky2->hw, Y2_QADDR(rxqaddr[port], PREF_UNIT_PUT_IDX),
-                    sky2->rx_put);
-       sky2->rx_last_put = sky2_read16(sky2->hw,
-                                       Y2_QADDR(rxqaddr[port],
-                                                PREF_UNIT_PUT_IDX));
-
-       sky2_prefetch_init(hw, txqaddr[port], sky2->tx_le_map,
-                          TX_RING_SIZE - 1);
-
        /* Enable interrupts from phy/mac for port */
        hw->intr_mask |= (port == 0) ? Y2_IS_PORT_1 : Y2_IS_PORT_2;
        sky2_write32(hw, B0_IMSK, hw->intr_mask);
@@ -1076,7 +1096,6 @@ static int sky2_down(struct net_device *dev)
        struct sky2_hw *hw = sky2->hw;
        unsigned port = sky2->port;
        u16 ctrl;
-       int i;
 
        if (netif_msg_ifdown(sky2))
                printk(KERN_INFO PFX "%s: disabling interface\n", dev->name);
@@ -1121,30 +1140,7 @@ static int sky2_down(struct net_device *dev)
 
        sky2_write32(hw, RB_ADDR(txqaddr[port], RB_CTRL), RB_RST_SET);
 
-       /*
-        * The RX Stop command will not work for Yukon-2 if the BMU does not
-        * reach the end of packet and since we can't make sure that we have
-        * incoming data, we must reset the BMU while it is not doing a DMA
-        * transfer. Since it is possible that the RX path is still active,
-        * the RX RAM buffer will be stopped first, so any possible incoming
-        * data will not trigger a DMA. After the RAM buffer is stopped, the
-        * BMU is polled until any DMA in progress is ended and only then it
-        * will be reset.
-        */
-
-       /* disable the RAM Buffer receive queue */
-       sky2_write8(hw, RB_ADDR(rxqaddr[port], RB_CTRL), RB_DIS_OP_MD);
-
-       for (i = 0; i < 0xffff; i++)
-               if (sky2_read8(hw, RB_ADDR(rxqaddr[port], Q_RSL))
-                   == sky2_read8(hw, RB_ADDR(rxqaddr[port], Q_RL)))
-                       break;
-
-       sky2_write32(hw, Q_ADDR(rxqaddr[port], Q_CSR),
-                    BMU_RST_SET | BMU_FIFO_RST);
-       /* reset the Rx prefetch unit */
-       sky2_write32(hw, Y2_QADDR(rxqaddr[port], PREF_UNIT_CTRL),
-                    PREF_UNIT_RST_SET);
+       sky2_rx_stop(sky2);
 
        sky2_write8(hw, SK_REG(port, RX_GMF_CTRL_T), GMF_RST_SET);
        sky2_write8(hw, SK_REG(port, TX_GMF_CTRL_T), GMF_RST_SET);
@@ -1379,19 +1375,44 @@ static void sky2_tx_timeout(struct net_device *dev)
 
 static int sky2_change_mtu(struct net_device *dev, int new_mtu)
 {
-       int err = 0;
+       struct sky2_port *sky2 = netdev_priv(dev);
+       struct sky2_hw *hw = sky2->hw;
+       int err;
+       u16 ctl, mode;
 
        if (new_mtu < ETH_ZLEN || new_mtu > ETH_JUMBO_MTU)
                return -EINVAL;
 
-       if (netif_running(dev))
-               sky2_down(dev);
+       if (!netif_running(dev)) {
+               dev->mtu = new_mtu;
+               return 0;
+       }
+
+       local_irq_disable();
+       sky2_write32(hw, B0_IMSK, 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);
+       sky2_rx_clean(sky2);
 
        dev->mtu = new_mtu;
+       mode = DATA_BLIND_VAL(DATA_BLIND_DEF) |
+               GM_SMOD_VLAN_ENA | IPG_DATA_VAL(IPG_DATA_DEF);
 
-       if (netif_running(dev))
-               err = sky2_up(dev);
+       if (dev->mtu > ETH_DATA_LEN)
+               mode |= GM_SMOD_JUMBO_ENA;
+
+       gma_write16(hw, sky2->port, GM_SERIAL_MODE, mode);
+
+       sky2_write8(hw, RB_ADDR(rxqaddr[sky2->port], RB_CTRL), RB_ENA_OP_MD);
+
+       err = sky2_rx_start(sky2);
+       gma_write16(hw, sky2->port, GM_GP_CTRL, ctl);
 
+       sky2_write32(hw, B0_IMSK, hw->intr_mask);
+       sky2_read32(hw, B0_IMSK);
+       local_irq_enable();
        return err;
 }
 
@@ -1407,7 +1428,7 @@ static struct sk_buff *sky2_receive(struct sky2_hw *hw, unsigned port,
        struct sky2_port *sky2 = netdev_priv(dev);
        struct ring_info *re = sky2->rx_ring + sky2->rx_next;
        struct sk_buff *skb = NULL;
-       const unsigned int bufsize = sky2_rx_size(sky2);
+       const unsigned int bufsize = rx_size(sky2);
 
        if (unlikely(netif_msg_rx_status(sky2)))
                printk(KERN_DEBUG PFX "%s: rx slot %u status 0x%x len %d\n",