This is basically the same as Large Receive Offload (LRO)
in Linux framework.

Signed-off-by: Rafal Ozieblo <raf...@cadence.com>
---
 drivers/net/ethernet/cadence/macb.h      |  6 +++
 drivers/net/ethernet/cadence/macb_main.c | 70 +++++++++++++++++++++++++++++++-
 2 files changed, 75 insertions(+), 1 deletion(-)

diff --git a/drivers/net/ethernet/cadence/macb.h 
b/drivers/net/ethernet/cadence/macb.h
index a2cb805..9ebdde7 100644
--- a/drivers/net/ethernet/cadence/macb.h
+++ b/drivers/net/ethernet/cadence/macb.h
@@ -83,6 +83,7 @@
 #define GEM_USRIO              0x000c /* User IO */
 #define GEM_DMACFG             0x0010 /* DMA Configuration */
 #define GEM_JML                        0x0048 /* Jumbo Max Length */
+#define GEM_RSC                        0x0058 /* RSC Control */
 #define GEM_HRB                        0x0080 /* Hash Bottom */
 #define GEM_HRT                        0x0084 /* Hash Top */
 #define GEM_SA1B               0x0088 /* Specific1 Bottom */
@@ -318,6 +319,11 @@
 #define GEM_ADDR64_OFFSET      30 /* Address bus width - 64b or 32b */
 #define GEM_ADDR64_SIZE                1
 
+/* Bitfields in RSC control */
+#define GEM_RSCCTRL_OFFSET     1 /* RSC control */
+#define GEM_RSCCTRL_SIZE       15
+#define GEM_CLRMSK_OFFSET      16 /* RSC clear mask */
+#define GEM_CLRMSK_SIZE                1
 
 /* Bitfields in NSR */
 #define MACB_NSR_LINK_OFFSET   0 /* pcs_link_state */
diff --git a/drivers/net/ethernet/cadence/macb_main.c 
b/drivers/net/ethernet/cadence/macb_main.c
index 27c406c..92bdcf1 100644
--- a/drivers/net/ethernet/cadence/macb_main.c
+++ b/drivers/net/ethernet/cadence/macb_main.c
@@ -2377,6 +2377,8 @@ static int macb_open(struct net_device *dev)
 
        if (!(bp->dev->hw_features & NETIF_F_LRO))
                bufsz += NET_IP_ALIGN;
+       else
+               bufsz = 0xFF * 64; // For RSC Buffer Sizes must be set to 16K.
 
        /* RX buffers initialization */
        macb_init_rx_buffer_size(bp, bufsz);
@@ -2801,6 +2803,62 @@ static int macb_get_ts_info(struct net_device *netdev,
        return ethtool_op_get_ts_info(netdev, info);
 }
 
+static void gem_enable_hdr_data_split(struct macb *bp, bool enable)
+{
+       u32 dmacfg;
+
+       dmacfg = gem_readl(bp, DMACFG);
+       if (enable)
+               dmacfg |= GEM_BIT(HDRS);
+       else
+               dmacfg &= ~GEM_BIT(HDRS);
+       gem_writel(bp, DMACFG, dmacfg);
+}
+
+static void gem_update_rsc_state(struct macb *bp, netdev_features_t feature)
+{
+       u32 rsc_control, rsc_control_new, queue, rsc;
+       bool enable, jumbo, any_enabled = false;
+       struct ethtool_rx_fs_item *item;
+       unsigned long flags;
+       u32 ncfgr;
+
+       enable = (!!(feature & NETIF_F_NTUPLE) && !!(feature & NETIF_F_LRO));
+       rsc = gem_readl(bp, RSC);
+       rsc_control = GEM_BFEXT(RSCCTRL, rsc);
+       rsc_control_new = 0;
+       if (enable) {
+               list_for_each_entry(item, &bp->rx_fs_list.list, list) {
+                       queue = item->fs.ring_cookie;
+                       rsc_control_new |= (1 << (queue - 1));
+                       any_enabled = true;
+                       netdev_dbg(bp->dev, "RSC %sabled for queue %u\n",
+                                  enable ? "en" : "dis", queue);
+               }
+       }
+       if (rsc_control_new != rsc_control) {
+               rsc = GEM_BFINS(RSCCTRL, rsc_control_new, rsc);
+               gem_writel(bp, RSC, rsc);
+       }
+       if (bp->caps & MACB_CAPS_JUMBO) {
+               /* Don't enable jumbo mode for RSC:
+                * disable unless not RSC and large MTU
+                */
+               ncfgr = gem_readl(bp, NCFGR);
+               enable = !any_enabled;
+               jumbo = !!MACB_BFEXT(JFRAME, ncfgr);
+               /* and don't touch if already in the state we want */
+               if ((jumbo && !enable) || (!jumbo && enable)) {
+                       ncfgr = MACB_BFINS(JFRAME, enable, ncfgr);
+                       spin_lock_irqsave(&bp->lock, flags);
+                       gem_writel(bp, NCFGR, ncfgr);
+                       spin_unlock_irqrestore(&bp->lock, flags);
+               }
+       }
+       /* Need to enable header-data splitting also */
+       gem_enable_hdr_data_split(bp, any_enabled);
+}
+
 static void gem_enable_flow_filters(struct macb *bp, bool enable)
 {
        struct ethtool_rx_fs_item *item;
@@ -2969,6 +3027,8 @@ static int gem_add_flow_filter(struct net_device *netdev,
        if (netdev->features & NETIF_F_NTUPLE)
                gem_enable_flow_filters(bp, 1);
 
+       /* enable RSC if LRO & NTUPLE on */
+       gem_update_rsc_state(bp, netdev->features);
        spin_unlock_irqrestore(&bp->rx_fs_lock, flags);
        return 0;
 
@@ -3009,6 +3069,7 @@ static int gem_del_flow_filter(struct net_device *netdev,
                        return 0;
                }
        }
+       gem_update_rsc_state(bp, netdev->features);
 
        spin_unlock_irqrestore(&bp->rx_fs_lock, flags);
        return -EINVAL;
@@ -3191,7 +3252,12 @@ static int macb_set_features(struct net_device *netdev,
                bool turn_on = features & NETIF_F_NTUPLE;
 
                gem_enable_flow_filters(bp, turn_on);
+               gem_update_rsc_state(bp, features);
        }
+
+       /* LRO (Large Receive Offload) aka RSC (Receive Side Coalescing) */
+       if ((changed & NETIF_F_LRO) && macb_is_gem(bp))
+               gem_update_rsc_state(bp, features);
        return 0;
 }
 
@@ -3449,8 +3515,10 @@ static int macb_init(struct platform_device *pdev)
                dev->hw_features |= MACB_NETIF_LSO;
 
        /* Check RSC capability */
-       if (GEM_BFEXT(PBUF_RSC, gem_readl(bp, DCFG6)))
+       if (GEM_BFEXT(PBUF_RSC, gem_readl(bp, DCFG6))) {
                dev->hw_features |= NETIF_F_LRO;
+               gem_writel(bp, RSC, GEM_BIT(CLRMSK));
+       }
 
        /* Checksum offload is only available on gem with packet buffer */
        if (macb_is_gem(bp) && !(bp->caps & MACB_CAPS_FIFO_MODE))
-- 
2.4.5

Reply via email to