[PATCH v1 3/8] drivers: net: stmmac: Add support for HW-accelerated VLAN Stripping

From: Boon Khai Ng
Date: Thu Mar 30 2023 - 03:03:46 EST


This is to add HW-accelerated VLAN Stripping for XGMAC

Signed-off-by: Boon Khai Ng <boon.khai.ng@xxxxxxxxx>
---
.../net/ethernet/stmicro/stmmac/dwxgmac2.h | 28 ++++++++++++++
.../ethernet/stmicro/stmmac/dwxgmac2_core.c | 38 +++++++++++++++++++
.../ethernet/stmicro/stmmac/dwxgmac2_descs.c | 15 ++++++++
3 files changed, 81 insertions(+)

diff --git a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2.h b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2.h
index 1913385df685..b7beaa197343 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2.h
+++ b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2.h
@@ -431,6 +431,7 @@
#define XGMAC_TDES2_VTIR GENMASK(15, 14)
#define XGMAC_TDES2_VTIR_SHIFT 14
#define XGMAC_TDES2_B1L GENMASK(13, 0)
+#define XGMAC_TDES2_VLAN_TAG_MASK GENMASK(15, 14)
#define XGMAC_TDES3_OWN BIT(31)
#define XGMAC_TDES3_CTXT BIT(30)
#define XGMAC_TDES3_FD BIT(29)
@@ -462,6 +463,8 @@
#define XGMAC_RDES3_RSV BIT(26)
#define XGMAC_RDES3_L34T GENMASK(23, 20)
#define XGMAC_RDES3_L34T_SHIFT 20
+#define XGMAC_RDES3_ET_LT GENMASK(19, 16)
+#define XGMAC_RDES3_ET_LT_SHIFT 16
#define XGMAC_L34T_IP4TCP 0x1
#define XGMAC_L34T_IP4UDP 0x2
#define XGMAC_L34T_IP6TCP 0x9
@@ -471,4 +474,29 @@
#define XGMAC_RDES3_TSD BIT(6)
#define XGMAC_RDES3_TSA BIT(4)

+/* RDES0 (write back format) */
+#define XGMAC_RDES0_VLAN_TAG_MASK GENMASK(15, 0)
+
+/* MAC VLAN Tag Control */
+#define XGMAC_VLAN_TAG_CTRL_OB BIT(0)
+#define XGMAC_VLAN_TAG_CTRL_CT BIT(1)
+#define XGMAC_VLAN_TAG_CTRL_OFS_MASK GENMASK(6, 2)
+#define XGMAC_VLAN_TAG_CTRL_OFS_SHIFT 2
+#define XGMAC_VLAN_TAG_CTRL_EVLS_MASK GENMASK(22, 21)
+#define XGMAC_VLAN_TAG_CTRL_EVLS_SHIFT 21
+#define XGMAC_VLAN_TAG_CTRL_EVLRXS BIT(24)
+
+#define XGMAC_VLAN_TAG_STRIP_NONE (0x0 << XGMAC_VLAN_TAG_CTRL_EVLS_SHIFT)
+#define XGMAC_VLAN_TAG_STRIP_PASS (0x1 << XGMAC_VLAN_TAG_CTRL_EVLS_SHIFT)
+#define XGMAC_VLAN_TAG_STRIP_FAIL (0x2 << XGMAC_VLAN_TAG_CTRL_EVLS_SHIFT)
+#define XGMAC_VLAN_TAG_STRIP_ALL (0x3 << XGMAC_VLAN_TAG_CTRL_EVLS_SHIFT)
+
+/* Error Type or L2 Type(ET/LT) Field Number */
+#define XGMAC_ET_LT_VLAN_STAG 8
+#define XGMAC_ET_LT_VLAN_CTAG 9
+#define XGMAC_ET_LT_DVLAN_CTAG_CTAG 10
+#define XGMAC_ET_LT_DVLAN_STAG_STAG 11
+#define XGMAC_ET_LT_DVLAN_CTAG_STAG 12
+#define XGMAC_ET_LT_DVLAN_STAG_CTAG 13
+
#endif /* __STMMAC_DWXGMAC2_H__ */
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c
index c6c4d7948fe5..8bd77e30cc24 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c
@@ -7,6 +7,7 @@
#include <linux/bitrev.h>
#include <linux/crc32.h>
#include <linux/iopoll.h>
+#include <linux/if_vlan.h>
#include "stmmac.h"
#include "stmmac_ptp.h"
#include "dwxlgmac2.h"
@@ -1174,6 +1175,39 @@ static void dwxgmac2_sarc_configure(void __iomem *ioaddr, int val)
writel(value, ioaddr + XGMAC_TX_CONFIG);
}

+static void dwxgmac2_rx_hw_vlan(struct net_device *dev,
+ struct mac_device_info *hw,
+ struct dma_desc *rx_desc, struct sk_buff *skb)
+{
+ if ((dev->features & NETIF_F_HW_VLAN_CTAG_RX) &&
+ hw->desc->get_rx_vlan_valid(rx_desc)) {
+ u16 vid = (u16)hw->desc->get_rx_vlan_tci(rx_desc);
+
+ __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vid);
+ }
+}
+
+static void dwxgmac2_set_hw_vlan_mode(void __iomem *ioaddr,
+ netdev_features_t features)
+{
+ u32 val;
+
+ val = readl(ioaddr + XGMAC_VLAN_TAG);
+ val &= ~XGMAC_VLAN_TAG_CTRL_EVLS_MASK;
+
+ if (features & NETIF_F_HW_VLAN_CTAG_RX)
+ /* Always strip VLAN on Receive */
+ val |= XGMAC_VLAN_TAG_STRIP_ALL;
+ else
+ /* Do not strip VLAN on Receive */
+ val |= XGMAC_VLAN_TAG_STRIP_NONE;
+
+ /* Enable outer VLAN Tag in Rx DMA descriptro */
+ val |= XGMAC_VLAN_TAG_CTRL_EVLRXS;
+
+ writel(val, ioaddr + XGMAC_VLAN_TAG);
+}
+
static void dwxgmac2_enable_vlan(struct mac_device_info *hw, u32 type)
{
void __iomem *ioaddr = hw->pcsr;
@@ -1498,6 +1532,8 @@ const struct stmmac_ops dwxgmac210_ops = {
.set_arp_offload = dwxgmac2_set_arp_offload,
.est_configure = dwxgmac3_est_configure,
.fpe_configure = dwxgmac3_fpe_configure,
+ .rx_hw_vlan = dwxgmac2_rx_hw_vlan,
+ .set_hw_vlan_mode = dwxgmac2_set_hw_vlan_mode,
};

static void dwxlgmac2_rx_queue_enable(struct mac_device_info *hw, u8 mode,
@@ -1559,6 +1595,8 @@ const struct stmmac_ops dwxlgmac2_ops = {
.set_arp_offload = dwxgmac2_set_arp_offload,
.est_configure = dwxgmac3_est_configure,
.fpe_configure = dwxgmac3_fpe_configure,
+ .rx_hw_vlan = dwxgmac2_rx_hw_vlan,
+ .set_hw_vlan_mode = dwxgmac2_set_hw_vlan_mode,
};

int dwxgmac2_setup(struct stmmac_priv *priv)
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_descs.c b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_descs.c
index b1f0c3984a09..2bdd5ef6d048 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_descs.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_descs.c
@@ -67,6 +67,19 @@ static int dwxgmac2_get_tx_ls(struct dma_desc *p)
return (le32_to_cpu(p->des3) & XGMAC_RDES3_LD) > 0;
}

+static inline int dwxgmac2_wrback_get_rx_vlan_tci(struct dma_desc *p)
+{
+ return (le32_to_cpu(p->des0) & XGMAC_RDES0_VLAN_TAG_MASK);
+}
+
+static inline bool dwxgmac2_wrback_get_rx_vlan_valid(struct dma_desc *p)
+{
+ return((((le32_to_cpu(p->des3) & XGMAC_RDES3_ET_LT) >>
+ XGMAC_RDES3_ET_LT_SHIFT) >= XGMAC_ET_LT_VLAN_STAG) &&
+ (((le32_to_cpu(p->des3) & XGMAC_RDES3_ET_LT) >>
+ XGMAC_RDES3_ET_LT_SHIFT) <= XGMAC_ET_LT_DVLAN_STAG_CTAG));
+}
+
static int dwxgmac2_get_rx_frame_len(struct dma_desc *p, int rx_coe)
{
return (le32_to_cpu(p->des3) & XGMAC_RDES3_PL);
@@ -349,6 +362,8 @@ const struct stmmac_desc_ops dwxgmac210_desc_ops = {
.set_tx_owner = dwxgmac2_set_tx_owner,
.set_rx_owner = dwxgmac2_set_rx_owner,
.get_tx_ls = dwxgmac2_get_tx_ls,
+ .get_rx_vlan_tci = dwxgmac2_wrback_get_rx_vlan_tci,
+ .get_rx_vlan_valid = dwxgmac2_wrback_get_rx_vlan_valid,
.get_rx_frame_len = dwxgmac2_get_rx_frame_len,
.enable_tx_timestamp = dwxgmac2_enable_tx_timestamp,
.get_tx_timestamp_status = dwxgmac2_get_tx_timestamp_status,
--
2.25.1