gianfar: Fix Tx csum generation errata handling
authorClaudiu Manoil <claudiu.manoil@freescale.com>
Mon, 5 Aug 2013 14:20:09 +0000 (17:20 +0300)
committerDavid S. Miller <davem@davemloft.net>
Mon, 5 Aug 2013 19:29:15 +0000 (12:29 -0700)
Both [eTSEC76] and [eTSEC12] errata relate to Tx checksum generation
(for some MPC83xx and MCP8548 older revisions). They require the same
workaround: manual checksum computation and insertion, and disabling
the H/W Tx csum acceleration feature (per frame) through Tx FCB
(Frame Control Block) csum offload settings.

The workaround for [eTSEC76] needs to be fixed because it currently
fails to disable H/W Tx csum insertion via FCB. This patch fixes it
and provides a common workaround implementation for both Tx csum errata.

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

index dbb34f7ce448fc0576adea06a9b6e8025c772c6f..352035e440d89fd1d8245fec0b3043ab69f7e163 100644 (file)
@@ -2051,6 +2051,24 @@ static inline struct txbd8 *next_txbd(struct txbd8 *bdp, struct txbd8 *base,
        return skip_txbd(bdp, 1, base, ring_size);
 }
 
+/* eTSEC12: csum generation not supported for some fcb offsets */
+static inline bool gfar_csum_errata_12(struct gfar_private *priv,
+                                      unsigned long fcb_addr)
+{
+       return (gfar_has_errata(priv, GFAR_ERRATA_12) &&
+              (fcb_addr % 0x20) > 0x18);
+}
+
+/* eTSEC76: csum generation for frames larger than 2500 may
+ * cause excess delays before start of transmission
+ */
+static inline bool gfar_csum_errata_76(struct gfar_private *priv,
+                                      unsigned int len)
+{
+       return (gfar_has_errata(priv, GFAR_ERRATA_76) &&
+              (len > 2500));
+}
+
 /* This is called by the kernel when a frame is ready for transmission.
  * It is pointed to by the dev->hard_start_xmit function pointer
  */
@@ -2068,19 +2086,6 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev)
        unsigned long flags;
        unsigned int nr_frags, nr_txbds, length, fcb_length = GMAC_FCB_LEN;
 
-       /* TOE=1 frames larger than 2500 bytes may see excess delays
-        * before start of transmission.
-        */
-       if (unlikely(gfar_has_errata(priv, GFAR_ERRATA_76) &&
-                    skb->ip_summed == CHECKSUM_PARTIAL &&
-                    skb->len > 2500)) {
-               int ret;
-
-               ret = skb_checksum_help(skb);
-               if (ret)
-                       return ret;
-       }
-
        rq = skb->queue_mapping;
        tx_queue = priv->tx_queue[rq];
        txq = netdev_get_tx_queue(dev, rq);
@@ -2187,14 +2192,15 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev)
        /* Set up checksumming */
        if (CHECKSUM_PARTIAL == skb->ip_summed) {
                fcb = gfar_add_fcb(skb);
-               /* as specified by errata */
-               if (unlikely(gfar_has_errata(priv, GFAR_ERRATA_12) &&
-                            ((unsigned long)fcb % 0x20) > 0x18)) {
+               lstatus |= BD_LFLAG(TXBD_TOE);
+               gfar_tx_checksum(skb, fcb, fcb_length);
+
+               if (unlikely(gfar_csum_errata_12(priv, (unsigned long)fcb)) ||
+                   unlikely(gfar_csum_errata_76(priv, skb->len))) {
                        __skb_pull(skb, GMAC_FCB_LEN);
                        skb_checksum_help(skb);
-               } else {
-                       lstatus |= BD_LFLAG(TXBD_TOE);
-                       gfar_tx_checksum(skb, fcb, fcb_length);
+                       lstatus &= ~(BD_LFLAG(TXBD_TOE));
+                       fcb = NULL;
                }
        }