Commit 02d88fb4 authored by Claudiu Manoil's avatar Claudiu Manoil Committed by David S. Miller
Browse files

gianfar: Fix Tx csum generation errata handling



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: default avatarClaudiu Manoil <claudiu.manoil@freescale.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 7864a1ad
Loading
Loading
Loading
Loading
+25 −19
Original line number Diff line number Diff line
@@ -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)) {
			__skb_pull(skb, GMAC_FCB_LEN);
			skb_checksum_help(skb);
		} else {
		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);
			lstatus &= ~(BD_LFLAG(TXBD_TOE));
			fcb = NULL;
		}
	}