[meta-freescale] [PATCH V2][linux-fslc][4.1-1.0.x-imx] net: fec: support RRACC_SHIFT16 to align IP header

Eric Nelson eric at nelint.com
Sat Sep 24 05:18:51 PDT 2016


The i.MX6 UL and DQLS variants all support shifting the data payload
of received packets by 16-bits, which aligns the IP header on a longword
boundary, which is, if not required, at least strongly suggested by the
Linux networking layer.

Without this patch, a huge number of alignment faults will be taken
by the IP stack, as seen in /proc/cpu/alignment:

	~/$ cat /proc/cpu/alignment
	User:		0
	System:		72645 (inet_gro_receive+0x104/0x27c)
	Skipped:	0
	Half:		0
	Word:		0
	DWord:		0
	Multi:		72645
	User faults:	3 (fixup+warn)

This patch was suggested by Andrew Lunn in this message to linux-netdev:
	http://marc.info/?l=linux-arm-kernel&m=147465452108384&w=2

and adapted from a patch by Russell King from 2014:
	http://git.arm.linux.org.uk/cgit/linux-arm.git/commit/?h=fec-testing&id=70d8a8a

Signed-off-by: Eric Nelson <eric at nelint.com>
---
V2 removes code in fec_enet_copybreak() that strips the two bytes of padding
and breaks the alignment. The call to skb_pull_inline in the caller instructs
the IP stack to ignore these bytes. With V2, no alignment errors are seen.

I've only tested this patch on i.MX6UL at the moment, and encourage others using
4.1.x to try it out.

I looked at the RM for the i.MX6SX and updates will be needed for that SOC
because the bit is in a different location.

Note that there are lots of other patches in Russell's tree that deserve 
some effort in bringing up-stream and I encourage others to make use of
his work.

 drivers/net/ethernet/freescale/fec.h      |  4 ++++
 drivers/net/ethernet/freescale/fec_main.c | 24 ++++++++++++++++++------
 2 files changed, 22 insertions(+), 6 deletions(-)

diff --git a/drivers/net/ethernet/freescale/fec.h b/drivers/net/ethernet/freescale/fec.h
index 65a3cd3..5ed0a5c 100644
--- a/drivers/net/ethernet/freescale/fec.h
+++ b/drivers/net/ethernet/freescale/fec.h
@@ -436,6 +436,7 @@ struct bufdesc_ex {
 #define FEC_QUIRK_SINGLE_MDIO		(1 << 11)
 /* Controller supports RACC register */
 #define FEC_QUIRK_HAS_RACC		(1 << 12)
+
 /*
  * i.MX6Q/DL ENET cannot wake up system in wait mode because ENET tx & rx
  * interrupt signal don't connect to GPC. So use pm qos to avoid cpu enter
@@ -443,6 +444,9 @@ struct bufdesc_ex {
  */
 #define FEC_QUIRK_BUG_WAITMODE         (1 << 13)
 
+/* Controller has ability to offset rx packets */
+#define FEC_QUIRK_RX_SHIFT16            (1 << 14)
+
 struct fec_enet_stop_mode {
 	struct regmap *gpr;
 	u8 req_gpr;
diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c
index 0f9ed22..88a6e5e 100644
--- a/drivers/net/ethernet/freescale/fec_main.c
+++ b/drivers/net/ethernet/freescale/fec_main.c
@@ -103,7 +103,8 @@ static struct platform_device_id fec_devtype[] = {
 		.driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT |
 				FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM |
 				FEC_QUIRK_HAS_VLAN | FEC_QUIRK_ERR006358 |
-				FEC_QUIRK_HAS_RACC | FEC_QUIRK_BUG_WAITMODE,
+				FEC_QUIRK_HAS_RACC | FEC_QUIRK_BUG_WAITMODE |
+				FEC_QUIRK_RX_SHIFT16,
 	}, {
 		.name = "mvf600-fec",
 		.driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_RACC,
@@ -118,7 +119,8 @@ static struct platform_device_id fec_devtype[] = {
 		.name = "imx6ul-fec",
 		.driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT |
 				FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM |
-				FEC_QUIRK_HAS_VLAN,
+				FEC_QUIRK_HAS_VLAN | FEC_QUIRK_HAS_RACC |
+				FEC_QUIRK_RX_SHIFT16,
 	}, {
 		/* sentinel */
 	}
@@ -180,6 +182,7 @@ MODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address");
 /* FEC receive acceleration */
 #define FEC_RACC_IPDIS		(1 << 1)
 #define FEC_RACC_PRODIS		(1 << 2)
+#define FEC_RACC_SHIFT16	BIT(7)
 #define FEC_RACC_OPTIONS	(FEC_RACC_IPDIS | FEC_RACC_PRODIS)
 
 /*
@@ -985,9 +988,11 @@ fec_restart(struct net_device *ndev)
 
 #if !defined(CONFIG_M5272)
 	if (fep->quirks & FEC_QUIRK_HAS_RACC) {
-		/* set RX checksum */
 		val = readl(fep->hwp + FEC_RACC);
+		if (fep->quirks & FEC_QUIRK_RX_SHIFT16)
+			val |= FEC_RACC_SHIFT16;
 		if (fep->csum_flags & FLAG_RX_CSUM_ENABLED)
+			/* set RX checksum */
 			val |= FEC_RACC_OPTIONS;
 		else
 			val &= ~FEC_RACC_OPTIONS;
@@ -1387,6 +1392,7 @@ static bool fec_enet_copybreak(struct net_device *ndev, struct sk_buff **skb,
 {
 	struct  fec_enet_private *fep = netdev_priv(ndev);
 	struct sk_buff *new_skb;
+	unsigned char *data = (*skb)->data;
 
 	if (length > fep->rx_copybreak)
 		return false;
@@ -1399,9 +1405,9 @@ static bool fec_enet_copybreak(struct net_device *ndev, struct sk_buff **skb,
 				FEC_ENET_RX_FRSIZE - fep->rx_align,
 				DMA_FROM_DEVICE);
 	if (!swap)
-		memcpy(new_skb->data, (*skb)->data, length);
+		memcpy(new_skb->data, data, length);
 	else
-		swap_buffer2(new_skb->data, (*skb)->data, length);
+		swap_buffer2(new_skb->data, data, length);
 	*skb = new_skb;
 
 	return true;
@@ -1420,7 +1426,6 @@ fec_enet_rx_queue(struct net_device *ndev, int budget, u16 queue_id)
 	struct bufdesc *bdp;
 	unsigned short status;
 	struct  sk_buff *skb_new = NULL;
-	struct  sk_buff *skb;
 	ushort	pkt_len;
 	__u8 *data;
 	int	pkt_received = 0;
@@ -1443,6 +1448,7 @@ fec_enet_rx_queue(struct net_device *ndev, int budget, u16 queue_id)
 	bdp = rxq->cur_rx;
 
 	while (!((status = bdp->cbd_sc) & BD_ENET_RX_EMPTY)) {
+		struct  sk_buff *skb;
 
 		if (pkt_received >= budget)
 			break;
@@ -1504,6 +1510,12 @@ fec_enet_rx_queue(struct net_device *ndev, int budget, u16 queue_id)
 		prefetch(skb->data - NET_IP_ALIGN);
 		skb_put(skb, pkt_len - 4);
 		data = skb->data;
+
+#if !defined(CONFIG_M5272)
+		if (fep->quirks & FEC_QUIRK_RX_SHIFT16)
+			data = skb_pull_inline(skb, 2);
+#endif
+
 		if (!is_copybreak && need_swap)
 			swap_buffer(data, pkt_len);
 
-- 
2.7.4



More information about the meta-freescale mailing list