gianfar: Fix on-the-fly vlan and mtu updates
authorClaudiu Manoil <claudiu.manoil@freescale.com>
Mon, 24 Feb 2014 10:13:43 +0000 (12:13 +0200)
committerDavid S. Miller <davem@davemloft.net>
Tue, 25 Feb 2014 00:38:20 +0000 (19:38 -0500)
The RCTRL and TCTRL registers should not be changed
on-the-fly, while the controller is running, otherwise
unexpected behaviour occurs.  But that's exactly what
gfar_vlan_mode() does, updating the VLAN acceleration
bits inside RCTRL/TCTRL.  The attempt to lock these
operations doesn't help, but only adds to the confusion.
There's also a dependency for Rx FCB insertion (activating
/de-activating the TOE offload block on Rx) which might
change the required rx buffer size.  This makes matters
worse as gfar_vlan_mode() ends up calling gfar_change_mtu(),
though the MTU size remains the same.  Note that there are
other situations that may affect the required rx buffer size,
like changing RXCSUM or rx hw timestamping, but errorneously
the rx buffer size is not recomputed/ updated in the process.

To fix this, do the vlan updates properly inside the MAC
reset and reconfiguration procedure, which takes care of
the rx buffer size dependecy and the rx TOE block (PRSDEP)
activation/deactivation as well (in the correct order).
As a consequence, MTU/ rx buff size updates are done now
by the same MAC reset and reconfig procedure, so that out
of context updates to MAXFRM, MRBLR, and MACCFG inside
change_mtu() are no longer needed.  The rx buffer size
dependecy to Rx FCB is now handled for the other cases too
(RXCSUM and rx hw timestamping).

Signed-off-by: Claudiu Manoil <claudiu.manoil@freescale.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/freescale/gianfar.c
drivers/net/ethernet/freescale/gianfar.h
drivers/net/ethernet/freescale/gianfar_ethtool.c

index 446e9c99379dd31273dc339e2106e10429c6b331..728078fe400ea3042abaff6489769d1b8323dceb 100644 (file)
@@ -329,14 +329,35 @@ static void gfar_init_tx_rx_base(struct gfar_private *priv)
        }
 }
 
-static void gfar_mac_rx_config(struct gfar_private *priv)
+static void gfar_rx_buff_size_config(struct gfar_private *priv)
 {
-       struct gfar __iomem *regs = priv->gfargrp[0].regs;
-       u32 rctrl = 0;
+       int frame_size = priv->ndev->mtu + ETH_HLEN;
 
        /* set this when rx hw offload (TOE) functions are being used */
        priv->uses_rxfcb = 0;
 
+       if (priv->ndev->features & (NETIF_F_RXCSUM | NETIF_F_HW_VLAN_CTAG_RX))
+               priv->uses_rxfcb = 1;
+
+       if (priv->hwts_rx_en)
+               priv->uses_rxfcb = 1;
+
+       if (priv->uses_rxfcb)
+               frame_size += GMAC_FCB_LEN;
+
+       frame_size += priv->padding;
+
+       frame_size = (frame_size & ~(INCREMENTAL_BUFFER_SIZE - 1)) +
+                    INCREMENTAL_BUFFER_SIZE;
+
+       priv->rx_buffer_size = frame_size;
+}
+
+static void gfar_mac_rx_config(struct gfar_private *priv)
+{
+       struct gfar __iomem *regs = priv->gfargrp[0].regs;
+       u32 rctrl = 0;
+
        if (priv->rx_filer_enable) {
                rctrl |= RCTRL_FILREN;
                /* Program the RIR0 reg with the required distribution */
@@ -347,15 +368,11 @@ static void gfar_mac_rx_config(struct gfar_private *priv)
        if (priv->ndev->flags & IFF_PROMISC)
                rctrl |= RCTRL_PROM;
 
-       if (priv->ndev->features & NETIF_F_RXCSUM) {
+       if (priv->ndev->features & NETIF_F_RXCSUM)
                rctrl |= RCTRL_CHECKSUMMING;
-               priv->uses_rxfcb = 1;
-       }
 
-       if (priv->extended_hash) {
-               rctrl |= RCTRL_EXTHASH;
-               rctrl |= RCTRL_EMEN;
-       }
+       if (priv->extended_hash)
+               rctrl |= RCTRL_EXTHASH | RCTRL_EMEN;
 
        if (priv->padding) {
                rctrl &= ~RCTRL_PAL_MASK;
@@ -363,15 +380,11 @@ static void gfar_mac_rx_config(struct gfar_private *priv)
        }
 
        /* Enable HW time stamping if requested from user space */
-       if (priv->hwts_rx_en) {
+       if (priv->hwts_rx_en)
                rctrl |= RCTRL_PRSDEP_INIT | RCTRL_TS_ENABLE;
-               priv->uses_rxfcb = 1;
-       }
 
-       if (priv->ndev->features & NETIF_F_HW_VLAN_CTAG_RX) {
+       if (priv->ndev->features & NETIF_F_HW_VLAN_CTAG_RX)
                rctrl |= RCTRL_VLEX | RCTRL_PRSDEP_INIT;
-               priv->uses_rxfcb = 1;
-       }
 
        /* Init rctrl based on our settings */
        gfar_write(&regs->rctrl, rctrl);
@@ -393,6 +406,9 @@ static void gfar_mac_tx_config(struct gfar_private *priv)
                gfar_write(&regs->tr47wt, DEFAULT_WRRS_WEIGHT);
        }
 
+       if (priv->ndev->features & NETIF_F_HW_VLAN_CTAG_TX)
+               tctrl |= TCTRL_VLINS;
+
        gfar_write(&regs->tctrl, tctrl);
 }
 
@@ -1029,7 +1045,11 @@ static void gfar_mac_reset(struct gfar_private *priv)
 
        udelay(3);
 
-       /* Initialize the max receive buffer length */
+       /* Compute rx_buff_size based on config flags */
+       gfar_rx_buff_size_config(priv);
+
+       /* Initialize the max receive frame/buffer lengths */
+       gfar_write(&regs->maxfrm, priv->rx_buffer_size);
        gfar_write(&regs->mrblr, priv->rx_buffer_size);
 
        /* Initialize the Minimum Frame Length Register */
@@ -1037,8 +1057,15 @@ static void gfar_mac_reset(struct gfar_private *priv)
 
        /* Initialize MACCFG2. */
        tempval = MACCFG2_INIT_SETTINGS;
-       if (gfar_has_errata(priv, GFAR_ERRATA_74))
+
+       /* If the mtu is larger than the max size for standard
+        * ethernet frames (ie, a jumbo frame), then set maccfg2
+        * to allow huge frames, and to check the length
+        */
+       if (priv->rx_buffer_size > DEFAULT_RX_BUFFER_SIZE ||
+           gfar_has_errata(priv, GFAR_ERRATA_74))
                tempval |= MACCFG2_HUGEFRAME | MACCFG2_LENGTHCHECK;
+
        gfar_write(&regs->maccfg2, tempval);
 
        /* Clear mac addr hash registers */
@@ -2353,77 +2380,9 @@ static int gfar_set_mac_address(struct net_device *dev)
        return 0;
 }
 
-/* Check if rx parser should be activated */
-void gfar_check_rx_parser_mode(struct gfar_private *priv)
-{
-       struct gfar __iomem *regs;
-       u32 tempval;
-
-       regs = priv->gfargrp[0].regs;
-
-       tempval = gfar_read(&regs->rctrl);
-       /* If parse is no longer required, then disable parser */
-       if (tempval & RCTRL_REQ_PARSER) {
-               tempval |= RCTRL_PRSDEP_INIT;
-               priv->uses_rxfcb = 1;
-       } else {
-               tempval &= ~RCTRL_PRSDEP_INIT;
-               priv->uses_rxfcb = 0;
-       }
-       gfar_write(&regs->rctrl, tempval);
-}
-
-/* Enables and disables VLAN insertion/extraction */
-void gfar_vlan_mode(struct net_device *dev, netdev_features_t features)
-{
-       struct gfar_private *priv = netdev_priv(dev);
-       struct gfar __iomem *regs = NULL;
-       unsigned long flags;
-       u32 tempval;
-
-       regs = priv->gfargrp[0].regs;
-       local_irq_save(flags);
-       lock_rx_qs(priv);
-
-       if (features & NETIF_F_HW_VLAN_CTAG_TX) {
-               /* Enable VLAN tag insertion */
-               tempval = gfar_read(&regs->tctrl);
-               tempval |= TCTRL_VLINS;
-               gfar_write(&regs->tctrl, tempval);
-       } else {
-               /* Disable VLAN tag insertion */
-               tempval = gfar_read(&regs->tctrl);
-               tempval &= ~TCTRL_VLINS;
-               gfar_write(&regs->tctrl, tempval);
-       }
-
-       if (features & NETIF_F_HW_VLAN_CTAG_RX) {
-               /* Enable VLAN tag extraction */
-               tempval = gfar_read(&regs->rctrl);
-               tempval |= (RCTRL_VLEX | RCTRL_PRSDEP_INIT);
-               gfar_write(&regs->rctrl, tempval);
-               priv->uses_rxfcb = 1;
-       } else {
-               /* Disable VLAN tag extraction */
-               tempval = gfar_read(&regs->rctrl);
-               tempval &= ~RCTRL_VLEX;
-               gfar_write(&regs->rctrl, tempval);
-
-               gfar_check_rx_parser_mode(priv);
-       }
-
-       gfar_change_mtu(dev, dev->mtu);
-
-       unlock_rx_qs(priv);
-       local_irq_restore(flags);
-}
-
 static int gfar_change_mtu(struct net_device *dev, int new_mtu)
 {
-       int tempsize, tempval;
        struct gfar_private *priv = netdev_priv(dev);
-       struct gfar __iomem *regs = priv->gfargrp[0].regs;
-       int oldsize = priv->rx_buffer_size;
        int frame_size = new_mtu + ETH_HLEN;
 
        if ((frame_size < 64) || (frame_size > JUMBO_FRAME_SIZE)) {
@@ -2431,42 +2390,12 @@ static int gfar_change_mtu(struct net_device *dev, int new_mtu)
                return -EINVAL;
        }
 
-       if (priv->uses_rxfcb)
-               frame_size += GMAC_FCB_LEN;
-
-       frame_size += priv->padding;
-
-       tempsize = (frame_size & ~(INCREMENTAL_BUFFER_SIZE - 1)) +
-                  INCREMENTAL_BUFFER_SIZE;
-
-       /* Only stop and start the controller if it isn't already
-        * stopped, and we changed something
-        */
-       if ((oldsize != tempsize) && (dev->flags & IFF_UP))
+       if (dev->flags & IFF_UP)
                stop_gfar(dev);
 
-       priv->rx_buffer_size = tempsize;
-
        dev->mtu = new_mtu;
 
-       gfar_write(&regs->mrblr, priv->rx_buffer_size);
-       gfar_write(&regs->maxfrm, priv->rx_buffer_size);
-
-       /* If the mtu is larger than the max size for standard
-        * ethernet frames (ie, a jumbo frame), then set maccfg2
-        * to allow huge frames, and to check the length
-        */
-       tempval = gfar_read(&regs->maccfg2);
-
-       if (priv->rx_buffer_size > DEFAULT_RX_BUFFER_SIZE ||
-           gfar_has_errata(priv, GFAR_ERRATA_74))
-               tempval |= (MACCFG2_HUGEFRAME | MACCFG2_LENGTHCHECK);
-       else
-               tempval &= ~(MACCFG2_HUGEFRAME | MACCFG2_LENGTHCHECK);
-
-       gfar_write(&regs->maccfg2, tempval);
-
-       if ((oldsize != tempsize) && (dev->flags & IFF_UP))
+       if (dev->flags & IFF_UP)
                startup_gfar(dev);
 
        return 0;
index 2a59398f8cf0fc4562cb998ad460b4fec0502f29..9db95563f8aa3b587a3ed74772d555f9a01ed094 100644 (file)
@@ -1211,8 +1211,6 @@ void gfar_phy_test(struct mii_bus *bus, struct phy_device *phydev, int enable,
                   u32 regnum, u32 read);
 void gfar_configure_coalescing_all(struct gfar_private *priv);
 int gfar_set_features(struct net_device *dev, netdev_features_t features);
-void gfar_check_rx_parser_mode(struct gfar_private *priv);
-void gfar_vlan_mode(struct net_device *dev, netdev_features_t features);
 
 extern const struct ethtool_ops gfar_ethtool_ops;
 
index 19557ec31f3385ee990f3c3171cc7bf7cf05ee27..dd7ccec506f1a1803c537a0a2b627b8bc1828511 100644 (file)
@@ -582,18 +582,15 @@ int gfar_set_features(struct net_device *dev, netdev_features_t features)
        netdev_features_t changed = dev->features ^ features;
        int err = 0;
 
-       if (changed & (NETIF_F_HW_VLAN_CTAG_TX|NETIF_F_HW_VLAN_CTAG_RX))
-               gfar_vlan_mode(dev, features);
-
-       if (!(changed & NETIF_F_RXCSUM))
+       if (!(changed & (NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX |
+                        NETIF_F_RXCSUM)))
                return 0;
 
+       dev->features = features;
+
        if (dev->flags & IFF_UP) {
                /* Now we take down the rings to rebuild them */
                stop_gfar(dev);
-
-               dev->features = features;
-
                err = startup_gfar(dev);
                netif_tx_wake_all_queues(dev);
        }