drivers: net: xgene: Add workaround for errata 10GE_8/ENET_11
authorIyappan Subramanian <isubramanian@apm.com>
Wed, 15 Mar 2017 20:27:20 +0000 (13:27 -0700)
committerDavid S. Miller <davem@davemloft.net>
Thu, 16 Mar 2017 04:52:52 +0000 (21:52 -0700)
This patch implements workaround for errata 10GE_8 and ENET_11:
"HW reports length error for valid 64 byte frames with len <46 bytes"
by recovering them from error.

Signed-off-by: Iyappan Subramanian <isubramanian@apm.com>
Signed-off-by: Quan Nguyen <qnguyen@apm.com>
Signed-off-by: Toan Le <toanle@apm.com>
Tested-by: Fushen Chen <fchen@apm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/apm/xgene/xgene_enet_hw.c
drivers/net/ethernet/apm/xgene/xgene_enet_hw.h
drivers/net/ethernet/apm/xgene/xgene_enet_main.c
drivers/net/ethernet/apm/xgene/xgene_enet_main.h

index c72a17e98f13f1e6ae0ae610b3e7495e106bcccb..2a835e07adfb58b2b6e4021e4b93a2555be8ff73 100644 (file)
@@ -494,7 +494,7 @@ static void xgene_gmac_set_speed(struct xgene_enet_pdata *pdata)
                break;
        }
 
-       mc2 |= FULL_DUPLEX2 | PAD_CRC;
+       mc2 |= FULL_DUPLEX2 | PAD_CRC | LENGTH_CHK;
        xgene_enet_wr_mcx_mac(pdata, MAC_CONFIG_2_ADDR, mc2);
        xgene_enet_wr_mcx_mac(pdata, INTERFACE_CONTROL_ADDR, intf_ctl);
        xgene_enet_wr_csr(pdata, RGMII_REG_0_ADDR, rgmii);
index b6cd625fad84161538014cbd9cd5a5c9377e9020..d250bfe94d24cb8f2a3a1f19487ac98a9659332e 100644 (file)
@@ -216,6 +216,7 @@ enum xgene_enet_rm {
 #define ENET_GHD_MODE                  BIT(26)
 #define FULL_DUPLEX2                   BIT(0)
 #define PAD_CRC                                BIT(2)
+#define LENGTH_CHK                     BIT(4)
 #define SCAN_AUTO_INCR                 BIT(5)
 #define TBYT_ADDR                      0x38
 #define TPKT_ADDR                      0x39
index e881365160e413ba757ae1b7fe1dccea38bf1fd7..5f37ed3506d571d6ddaad170f2147f430a88e51c 100644 (file)
@@ -658,12 +658,24 @@ static void xgene_enet_free_pagepool(struct xgene_enet_desc_ring *buf_pool,
        buf_pool->head = head;
 }
 
+/* Errata 10GE_8 and ENET_11 - allow packet with length <=64B */
+static bool xgene_enet_errata_10GE_8(struct sk_buff *skb, u32 len, u8 status)
+{
+       if (status == INGRESS_PKT_LEN && len == ETHER_MIN_PACKET) {
+               if (ntohs(eth_hdr(skb)->h_proto) < 46)
+                       return true;
+       }
+
+       return false;
+}
+
 static int xgene_enet_rx_frame(struct xgene_enet_desc_ring *rx_ring,
                               struct xgene_enet_raw_desc *raw_desc,
                               struct xgene_enet_raw_desc *exp_desc)
 {
        struct xgene_enet_desc_ring *buf_pool, *page_pool;
        u32 datalen, frag_size, skb_index;
+       struct xgene_enet_pdata *pdata;
        struct net_device *ndev;
        dma_addr_t dma_addr;
        struct sk_buff *skb;
@@ -676,6 +688,7 @@ static int xgene_enet_rx_frame(struct xgene_enet_desc_ring *rx_ring,
        bool nv;
 
        ndev = rx_ring->ndev;
+       pdata = netdev_priv(ndev);
        dev = ndev_to_dev(rx_ring->ndev);
        buf_pool = rx_ring->buf_pool;
        page_pool = rx_ring->page_pool;
@@ -686,30 +699,29 @@ static int xgene_enet_rx_frame(struct xgene_enet_desc_ring *rx_ring,
        skb = buf_pool->rx_skb[skb_index];
        buf_pool->rx_skb[skb_index] = NULL;
 
+       datalen = xgene_enet_get_data_len(le64_to_cpu(raw_desc->m1));
+       skb_put(skb, datalen);
+       prefetch(skb->data - NET_IP_ALIGN);
+       skb->protocol = eth_type_trans(skb, ndev);
+
        /* checking for error */
        status = (GET_VAL(ELERR, le64_to_cpu(raw_desc->m0)) << LERR_LEN) |
                  GET_VAL(LERR, le64_to_cpu(raw_desc->m0));
        if (unlikely(status)) {
-               dev_kfree_skb_any(skb);
-               xgene_enet_free_pagepool(page_pool, raw_desc, exp_desc);
-               xgene_enet_parse_error(rx_ring, netdev_priv(rx_ring->ndev),
-                                      status);
-               ret = -EIO;
-               goto out;
+               if (!xgene_enet_errata_10GE_8(skb, datalen, status)) {
+                       dev_kfree_skb_any(skb);
+                       xgene_enet_free_pagepool(page_pool, raw_desc, exp_desc);
+                       xgene_enet_parse_error(rx_ring, pdata, status);
+                       goto out;
+               }
        }
 
-       /* strip off CRC as HW isn't doing this */
-       datalen = xgene_enet_get_data_len(le64_to_cpu(raw_desc->m1));
-
        nv = GET_VAL(NV, le64_to_cpu(raw_desc->m0));
-       if (!nv)
+       if (!nv) {
+               /* strip off CRC as HW isn't doing this */
                datalen -= 4;
-
-       skb_put(skb, datalen);
-       prefetch(skb->data - NET_IP_ALIGN);
-
-       if (!nv)
                goto skip_jumbo;
+       }
 
        slots = page_pool->slots - 1;
        head = page_pool->head;
@@ -738,7 +750,6 @@ static int xgene_enet_rx_frame(struct xgene_enet_desc_ring *rx_ring,
 
 skip_jumbo:
        skb_checksum_none_assert(skb);
-       skb->protocol = eth_type_trans(skb, ndev);
        xgene_enet_rx_csum(skb);
 
        rx_ring->rx_packets++;
index 52571741da9f5559145e5ce172a7ee910818bbc4..0d4be2425ebc2c24b4ecfa26fc4637f76ff76f91 100644 (file)
@@ -41,6 +41,7 @@
 #include "../../../phy/mdio-xgene.h"
 
 #define XGENE_DRV_VERSION      "v1.0"
+#define ETHER_MIN_PACKET       64
 #define XGENE_ENET_STD_MTU     1536
 #define XGENE_ENET_MAX_MTU     9600
 #define SKB_BUFFER_SIZE                (XGENE_ENET_STD_MTU - NET_IP_ALIGN)