]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
net: stmmac: Refactor VLAN implementation
authorBoon Khai Ng <boon.khai.ng@altera.com>
Wed, 7 May 2025 06:38:10 +0000 (14:38 +0800)
committerJakub Kicinski <kuba@kernel.org>
Sat, 10 May 2025 00:29:43 +0000 (17:29 -0700)
Refactor VLAN implementation by moving common code for DWMAC4 and
DWXGMAC IPs into a separate VLAN module. VLAN implementation for
DWMAC4 and DWXGMAC differs only for CSR base address, the descriptor
for the VLAN ID and VLAN VALID bit field.

The descriptor format for VLAN is not moved to the common code due
to hardware-specific differences between DWMAC4 and DWXGMAC.

For the DWMAC4 IP, the Receive Normal Descriptor 0 (RDES0) is
formatted as follows:
    31                                                0
      ------------------------ -----------------------
RDES0| Inner VLAN TAG [31:16] | Outer VLAN TAG [15:0] |
      ------------------------ -----------------------

For the DWXGMAC IP, the RDES0 format varies based on the
Tunneled Frame bit (TNP):

a) For Non-Tunneled Frame (TNP=0)

    31                                                0
      ------------------------ -----------------------
RDES0| Inner VLAN TAG [31:16] | Outer VLAN TAG [15:0] |
      ------------------------ -----------------------

b) For Tunneled Frame (TNP=1)

     31                   8 7                3 2      0
      --------------------- ------------------ -------
RDES0| VNID/VSID           | Reserved         | OL2L3 |
      --------------------- ------------------ ------

The logic for handling tunneled frames is not yet implemented
in the dwxgmac2_wrback_get_rx_vlan_tci() function. Therefore,
it is prudent to maintain separate functions within their
respective descriptor driver files
(dwxgmac2_descs.c and dwmac4_descs.c).

Signed-off-by: Boon Khai Ng <boon.khai.ng@altera.com>
Link: https://patch.msgid.link/20250507063812.34000-2-boon.khai.ng@altera.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/net/ethernet/stmicro/stmmac/Makefile
drivers/net/ethernet/stmicro/stmmac/common.h
drivers/net/ethernet/stmicro/stmmac/dwmac4.h
drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c
drivers/net/ethernet/stmicro/stmmac/dwxgmac2.h
drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c
drivers/net/ethernet/stmicro/stmmac/hwif.c
drivers/net/ethernet/stmicro/stmmac/hwif.h
drivers/net/ethernet/stmicro/stmmac/stmmac_vlan.c [new file with mode: 0644]
drivers/net/ethernet/stmicro/stmmac/stmmac_vlan.h [new file with mode: 0644]

index 91050215511b28aa92c8032c6a4bf99fd2f35509..b591d93f850338991fa3615f03b882f27dda406b 100644 (file)
@@ -6,7 +6,7 @@ stmmac-objs:= stmmac_main.o stmmac_ethtool.o stmmac_mdio.o ring_mode.o  \
              mmc_core.o stmmac_hwtstamp.o stmmac_ptp.o dwmac4_descs.o  \
              dwmac4_dma.o dwmac4_lib.o dwmac4_core.o dwmac5.o hwif.o \
              stmmac_tc.o dwxgmac2_core.o dwxgmac2_dma.o dwxgmac2_descs.o \
-             stmmac_xdp.o stmmac_est.o stmmac_fpe.o \
+             stmmac_xdp.o stmmac_est.o stmmac_fpe.o stmmac_vlan.o \
              $(stmmac-y)
 
 stmmac-$(CONFIG_STMMAC_SELFTESTS) += stmmac_selftests.o
index 412b07e77945625c09dbdf249c14376137969f33..ea5da5793362c00d5a73b565c49b97194a72cc7f 100644 (file)
@@ -602,6 +602,7 @@ struct mac_device_info {
        const struct stmmac_tc_ops *tc;
        const struct stmmac_mmc_ops *mmc;
        const struct stmmac_est_ops *est;
+       const struct stmmac_vlan_ops *vlan;
        struct dw_xpcs *xpcs;
        struct phylink_pcs *phylink_pcs;
        struct mii_regs mii;    /* MII register Addresses */
index 5f387ec27c8cc9f336c49a83b56bbce93fafab49..f4694fd576f5c578be9c07000c06d1003f777715 100644 (file)
 #define GMAC_EXT_CONFIG                        0x00000004
 #define GMAC_PACKET_FILTER             0x00000008
 #define GMAC_HASH_TAB(x)               (0x10 + (x) * 4)
-#define GMAC_VLAN_TAG                  0x00000050
-#define GMAC_VLAN_TAG_DATA             0x00000054
-#define GMAC_VLAN_HASH_TABLE           0x00000058
 #define GMAC_RX_FLOW_CTRL              0x00000090
-#define GMAC_VLAN_INCL                 0x00000060
 #define GMAC_QX_TX_FLOW_CTRL(x)                (0x70 + x * 4)
 #define GMAC_TXQ_PRTY_MAP0             0x98
 #define GMAC_TXQ_PRTY_MAP1             0x9C
 
 #define GMAC_MAX_PERFECT_ADDRESSES     128
 
-/* MAC VLAN */
-#define GMAC_VLAN_EDVLP                        BIT(26)
-#define GMAC_VLAN_VTHM                 BIT(25)
-#define GMAC_VLAN_DOVLTC               BIT(20)
-#define GMAC_VLAN_ESVL                 BIT(18)
-#define GMAC_VLAN_ETV                  BIT(16)
-#define GMAC_VLAN_VID                  GENMASK(15, 0)
-#define GMAC_VLAN_VLTI                 BIT(20)
-#define GMAC_VLAN_CSVL                 BIT(19)
-#define GMAC_VLAN_VLC                  GENMASK(17, 16)
-#define GMAC_VLAN_VLC_SHIFT            16
-#define GMAC_VLAN_VLHT                 GENMASK(15, 0)
-
-/* MAC VLAN Tag */
-#define GMAC_VLAN_TAG_VID              GENMASK(15, 0)
-#define GMAC_VLAN_TAG_ETV              BIT(16)
-
-/* MAC VLAN Tag Control */
-#define GMAC_VLAN_TAG_CTRL_OB          BIT(0)
-#define GMAC_VLAN_TAG_CTRL_CT          BIT(1)
-#define GMAC_VLAN_TAG_CTRL_OFS_MASK    GENMASK(6, 2)
-#define GMAC_VLAN_TAG_CTRL_OFS_SHIFT   2
-#define GMAC_VLAN_TAG_CTRL_EVLS_MASK   GENMASK(22, 21)
-#define GMAC_VLAN_TAG_CTRL_EVLS_SHIFT  21
-#define GMAC_VLAN_TAG_CTRL_EVLRXS      BIT(24)
-
-#define GMAC_VLAN_TAG_STRIP_NONE       (0x0 << GMAC_VLAN_TAG_CTRL_EVLS_SHIFT)
-#define GMAC_VLAN_TAG_STRIP_PASS       (0x1 << GMAC_VLAN_TAG_CTRL_EVLS_SHIFT)
-#define GMAC_VLAN_TAG_STRIP_FAIL       (0x2 << GMAC_VLAN_TAG_CTRL_EVLS_SHIFT)
-#define GMAC_VLAN_TAG_STRIP_ALL                (0x3 << GMAC_VLAN_TAG_CTRL_EVLS_SHIFT)
-
-/* MAC VLAN Tag Data/Filter */
-#define GMAC_VLAN_TAG_DATA_VID         GENMASK(15, 0)
-#define GMAC_VLAN_TAG_DATA_VEN         BIT(16)
-#define GMAC_VLAN_TAG_DATA_ETV         BIT(17)
-
 /* MAC RX Queue Enable */
 #define GMAC_RX_QUEUE_CLEAR(queue)     ~(GENMASK(1, 0) << ((queue) * 2))
 #define GMAC_RX_AV_QUEUE_ENABLE(queue) BIT((queue) * 2)
index cc4ddf608652cb139631a747948cdca81794aea8..55f9614cd6a4b020f8e57a32e149fe8daec297fd 100644 (file)
@@ -18,6 +18,7 @@
 #include "stmmac.h"
 #include "stmmac_fpe.h"
 #include "stmmac_pcs.h"
+#include "stmmac_vlan.h"
 #include "dwmac4.h"
 #include "dwmac5.h"
 
@@ -448,165 +449,6 @@ static void dwmac4_set_eee_timer(struct mac_device_info *hw, int ls, int tw)
        writel(value, ioaddr + GMAC4_LPI_TIMER_CTRL);
 }
 
-static void dwmac4_write_single_vlan(struct net_device *dev, u16 vid)
-{
-       void __iomem *ioaddr = (void __iomem *)dev->base_addr;
-       u32 val;
-
-       val = readl(ioaddr + GMAC_VLAN_TAG);
-       val &= ~GMAC_VLAN_TAG_VID;
-       val |= GMAC_VLAN_TAG_ETV | vid;
-
-       writel(val, ioaddr + GMAC_VLAN_TAG);
-}
-
-static int dwmac4_write_vlan_filter(struct net_device *dev,
-                                   struct mac_device_info *hw,
-                                   u8 index, u32 data)
-{
-       void __iomem *ioaddr = (void __iomem *)dev->base_addr;
-       int ret;
-       u32 val;
-
-       if (index >= hw->num_vlan)
-               return -EINVAL;
-
-       writel(data, ioaddr + GMAC_VLAN_TAG_DATA);
-
-       val = readl(ioaddr + GMAC_VLAN_TAG);
-       val &= ~(GMAC_VLAN_TAG_CTRL_OFS_MASK |
-               GMAC_VLAN_TAG_CTRL_CT |
-               GMAC_VLAN_TAG_CTRL_OB);
-       val |= (index << GMAC_VLAN_TAG_CTRL_OFS_SHIFT) | GMAC_VLAN_TAG_CTRL_OB;
-
-       writel(val, ioaddr + GMAC_VLAN_TAG);
-
-       ret = readl_poll_timeout(ioaddr + GMAC_VLAN_TAG, val,
-                                !(val & GMAC_VLAN_TAG_CTRL_OB),
-                                1000, 500000);
-       if (ret) {
-               netdev_err(dev, "Timeout accessing MAC_VLAN_Tag_Filter\n");
-               return -EBUSY;
-       }
-
-       return 0;
-}
-
-static int dwmac4_add_hw_vlan_rx_fltr(struct net_device *dev,
-                                     struct mac_device_info *hw,
-                                     __be16 proto, u16 vid)
-{
-       int index = -1;
-       u32 val = 0;
-       int i, ret;
-
-       if (vid > 4095)
-               return -EINVAL;
-
-       /* Single Rx VLAN Filter */
-       if (hw->num_vlan == 1) {
-               /* For single VLAN filter, VID 0 means VLAN promiscuous */
-               if (vid == 0) {
-                       netdev_warn(dev, "Adding VLAN ID 0 is not supported\n");
-                       return -EPERM;
-               }
-
-               if (hw->vlan_filter[0] & GMAC_VLAN_TAG_VID) {
-                       netdev_err(dev, "Only single VLAN ID supported\n");
-                       return -EPERM;
-               }
-
-               hw->vlan_filter[0] = vid;
-               dwmac4_write_single_vlan(dev, vid);
-
-               return 0;
-       }
-
-       /* Extended Rx VLAN Filter Enable */
-       val |= GMAC_VLAN_TAG_DATA_ETV | GMAC_VLAN_TAG_DATA_VEN | vid;
-
-       for (i = 0; i < hw->num_vlan; i++) {
-               if (hw->vlan_filter[i] == val)
-                       return 0;
-               else if (!(hw->vlan_filter[i] & GMAC_VLAN_TAG_DATA_VEN))
-                       index = i;
-       }
-
-       if (index == -1) {
-               netdev_err(dev, "MAC_VLAN_Tag_Filter full (size: %0u)\n",
-                          hw->num_vlan);
-               return -EPERM;
-       }
-
-       ret = dwmac4_write_vlan_filter(dev, hw, index, val);
-
-       if (!ret)
-               hw->vlan_filter[index] = val;
-
-       return ret;
-}
-
-static int dwmac4_del_hw_vlan_rx_fltr(struct net_device *dev,
-                                     struct mac_device_info *hw,
-                                     __be16 proto, u16 vid)
-{
-       int i, ret = 0;
-
-       /* Single Rx VLAN Filter */
-       if (hw->num_vlan == 1) {
-               if ((hw->vlan_filter[0] & GMAC_VLAN_TAG_VID) == vid) {
-                       hw->vlan_filter[0] = 0;
-                       dwmac4_write_single_vlan(dev, 0);
-               }
-               return 0;
-       }
-
-       /* Extended Rx VLAN Filter Enable */
-       for (i = 0; i < hw->num_vlan; i++) {
-               if ((hw->vlan_filter[i] & GMAC_VLAN_TAG_DATA_VID) == vid) {
-                       ret = dwmac4_write_vlan_filter(dev, hw, i, 0);
-
-                       if (!ret)
-                               hw->vlan_filter[i] = 0;
-                       else
-                               return ret;
-               }
-       }
-
-       return ret;
-}
-
-static void dwmac4_restore_hw_vlan_rx_fltr(struct net_device *dev,
-                                          struct mac_device_info *hw)
-{
-       void __iomem *ioaddr = hw->pcsr;
-       u32 value;
-       u32 hash;
-       u32 val;
-       int i;
-
-       /* Single Rx VLAN Filter */
-       if (hw->num_vlan == 1) {
-               dwmac4_write_single_vlan(dev, hw->vlan_filter[0]);
-               return;
-       }
-
-       /* Extended Rx VLAN Filter Enable */
-       for (i = 0; i < hw->num_vlan; i++) {
-               if (hw->vlan_filter[i] & GMAC_VLAN_TAG_DATA_VEN) {
-                       val = hw->vlan_filter[i];
-                       dwmac4_write_vlan_filter(dev, hw, i, val);
-               }
-       }
-
-       hash = readl(ioaddr + GMAC_VLAN_HASH_TABLE);
-       if (hash & GMAC_VLAN_VLHT) {
-               value = readl(ioaddr + GMAC_VLAN_TAG);
-               value |= GMAC_VLAN_VTHM;
-               writel(value, ioaddr + GMAC_VLAN_TAG);
-       }
-}
-
 static void dwmac4_set_filter(struct mac_device_info *hw,
                              struct net_device *dev)
 {
@@ -965,45 +807,6 @@ static void dwmac4_set_mac_loopback(void __iomem *ioaddr, bool enable)
        writel(value, ioaddr + GMAC_CONFIG);
 }
 
-static void dwmac4_update_vlan_hash(struct mac_device_info *hw, u32 hash,
-                                   u16 perfect_match, bool is_double)
-{
-       void __iomem *ioaddr = hw->pcsr;
-       u32 value;
-
-       writel(hash, ioaddr + GMAC_VLAN_HASH_TABLE);
-
-       value = readl(ioaddr + GMAC_VLAN_TAG);
-
-       if (hash) {
-               value |= GMAC_VLAN_VTHM | GMAC_VLAN_ETV;
-               if (is_double) {
-                       value |= GMAC_VLAN_EDVLP;
-                       value |= GMAC_VLAN_ESVL;
-                       value |= GMAC_VLAN_DOVLTC;
-               }
-
-               writel(value, ioaddr + GMAC_VLAN_TAG);
-       } else if (perfect_match) {
-               u32 value = GMAC_VLAN_ETV;
-
-               if (is_double) {
-                       value |= GMAC_VLAN_EDVLP;
-                       value |= GMAC_VLAN_ESVL;
-                       value |= GMAC_VLAN_DOVLTC;
-               }
-
-               writel(value | perfect_match, ioaddr + GMAC_VLAN_TAG);
-       } else {
-               value &= ~(GMAC_VLAN_VTHM | GMAC_VLAN_ETV);
-               value &= ~(GMAC_VLAN_EDVLP | GMAC_VLAN_ESVL);
-               value &= ~GMAC_VLAN_DOVLTC;
-               value &= ~GMAC_VLAN_VID;
-
-               writel(value, ioaddr + GMAC_VLAN_TAG);
-       }
-}
-
 static void dwmac4_sarc_configure(void __iomem *ioaddr, int val)
 {
        u32 value = readl(ioaddr + GMAC_CONFIG);
@@ -1014,19 +817,6 @@ static void dwmac4_sarc_configure(void __iomem *ioaddr, int val)
        writel(value, ioaddr + GMAC_CONFIG);
 }
 
-static void dwmac4_enable_vlan(struct mac_device_info *hw, u32 type)
-{
-       void __iomem *ioaddr = hw->pcsr;
-       u32 value;
-
-       value = readl(ioaddr + GMAC_VLAN_INCL);
-       value |= GMAC_VLAN_VLTI;
-       value |= GMAC_VLAN_CSVL; /* Only use SVLAN */
-       value &= ~GMAC_VLAN_VLC;
-       value |= (type << GMAC_VLAN_VLC_SHIFT) & GMAC_VLAN_VLC;
-       writel(value, ioaddr + GMAC_VLAN_INCL);
-}
-
 static void dwmac4_set_arp_offload(struct mac_device_info *hw, bool en,
                                   u32 addr)
 {
@@ -1143,35 +933,6 @@ static int dwmac4_config_l4_filter(struct mac_device_info *hw, u32 filter_no,
        return 0;
 }
 
-static void dwmac4_rx_hw_vlan(struct mac_device_info *hw,
-                             struct dma_desc *rx_desc, struct sk_buff *skb)
-{
-       if (hw->desc->get_rx_vlan_valid(rx_desc)) {
-               u16 vid = hw->desc->get_rx_vlan_tci(rx_desc);
-
-               __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vid);
-       }
-}
-
-static void dwmac4_set_hw_vlan_mode(struct mac_device_info *hw)
-{
-       void __iomem *ioaddr = hw->pcsr;
-       u32 value = readl(ioaddr + GMAC_VLAN_TAG);
-
-       value &= ~GMAC_VLAN_TAG_CTRL_EVLS_MASK;
-
-       if (hw->hw_vlan_en)
-               /* Always strip VLAN on Receive */
-               value |= GMAC_VLAN_TAG_STRIP_ALL;
-       else
-               /* Do not strip VLAN on Receive */
-               value |= GMAC_VLAN_TAG_STRIP_NONE;
-
-       /* Enable outer VLAN Tag in Rx DMA descriptor */
-       value |= GMAC_VLAN_TAG_CTRL_EVLRXS;
-       writel(value, ioaddr + GMAC_VLAN_TAG);
-}
-
 const struct stmmac_ops dwmac4_ops = {
        .core_init = dwmac4_core_init,
        .update_caps = dwmac4_update_caps,
@@ -1201,17 +962,10 @@ const struct stmmac_ops dwmac4_ops = {
        .debug = dwmac4_debug,
        .set_filter = dwmac4_set_filter,
        .set_mac_loopback = dwmac4_set_mac_loopback,
-       .update_vlan_hash = dwmac4_update_vlan_hash,
        .sarc_configure = dwmac4_sarc_configure,
-       .enable_vlan = dwmac4_enable_vlan,
        .set_arp_offload = dwmac4_set_arp_offload,
        .config_l3_filter = dwmac4_config_l3_filter,
        .config_l4_filter = dwmac4_config_l4_filter,
-       .add_hw_vlan_rx_fltr = dwmac4_add_hw_vlan_rx_fltr,
-       .del_hw_vlan_rx_fltr = dwmac4_del_hw_vlan_rx_fltr,
-       .restore_hw_vlan_rx_fltr = dwmac4_restore_hw_vlan_rx_fltr,
-       .rx_hw_vlan = dwmac4_rx_hw_vlan,
-       .set_hw_vlan_mode = dwmac4_set_hw_vlan_mode,
 };
 
 const struct stmmac_ops dwmac410_ops = {
@@ -1244,18 +998,11 @@ const struct stmmac_ops dwmac410_ops = {
        .set_filter = dwmac4_set_filter,
        .flex_pps_config = dwmac5_flex_pps_config,
        .set_mac_loopback = dwmac4_set_mac_loopback,
-       .update_vlan_hash = dwmac4_update_vlan_hash,
        .sarc_configure = dwmac4_sarc_configure,
-       .enable_vlan = dwmac4_enable_vlan,
        .set_arp_offload = dwmac4_set_arp_offload,
        .config_l3_filter = dwmac4_config_l3_filter,
        .config_l4_filter = dwmac4_config_l4_filter,
        .fpe_map_preemption_class = dwmac5_fpe_map_preemption_class,
-       .add_hw_vlan_rx_fltr = dwmac4_add_hw_vlan_rx_fltr,
-       .del_hw_vlan_rx_fltr = dwmac4_del_hw_vlan_rx_fltr,
-       .restore_hw_vlan_rx_fltr = dwmac4_restore_hw_vlan_rx_fltr,
-       .rx_hw_vlan = dwmac4_rx_hw_vlan,
-       .set_hw_vlan_mode = dwmac4_set_hw_vlan_mode,
 };
 
 const struct stmmac_ops dwmac510_ops = {
@@ -1292,51 +1039,13 @@ const struct stmmac_ops dwmac510_ops = {
        .rxp_config = dwmac5_rxp_config,
        .flex_pps_config = dwmac5_flex_pps_config,
        .set_mac_loopback = dwmac4_set_mac_loopback,
-       .update_vlan_hash = dwmac4_update_vlan_hash,
        .sarc_configure = dwmac4_sarc_configure,
-       .enable_vlan = dwmac4_enable_vlan,
        .set_arp_offload = dwmac4_set_arp_offload,
        .config_l3_filter = dwmac4_config_l3_filter,
        .config_l4_filter = dwmac4_config_l4_filter,
        .fpe_map_preemption_class = dwmac5_fpe_map_preemption_class,
-       .add_hw_vlan_rx_fltr = dwmac4_add_hw_vlan_rx_fltr,
-       .del_hw_vlan_rx_fltr = dwmac4_del_hw_vlan_rx_fltr,
-       .restore_hw_vlan_rx_fltr = dwmac4_restore_hw_vlan_rx_fltr,
-       .rx_hw_vlan = dwmac4_rx_hw_vlan,
-       .set_hw_vlan_mode = dwmac4_set_hw_vlan_mode,
 };
 
-static u32 dwmac4_get_num_vlan(void __iomem *ioaddr)
-{
-       u32 val, num_vlan;
-
-       val = readl(ioaddr + GMAC_HW_FEATURE3);
-       switch (val & GMAC_HW_FEAT_NRVF) {
-       case 0:
-               num_vlan = 1;
-               break;
-       case 1:
-               num_vlan = 4;
-               break;
-       case 2:
-               num_vlan = 8;
-               break;
-       case 3:
-               num_vlan = 16;
-               break;
-       case 4:
-               num_vlan = 24;
-               break;
-       case 5:
-               num_vlan = 32;
-               break;
-       default:
-               num_vlan = 1;
-       }
-
-       return num_vlan;
-}
-
 int dwmac4_setup(struct stmmac_priv *priv)
 {
        struct mac_device_info *mac = priv->hw;
index a03f5d77156635ac2b448559c7ded18944b163bb..5e369a9a25959a5c45400cc437c5db7848c1bc5b 100644 (file)
 #define XGMAC_FILTER_PR                        BIT(0)
 #define XGMAC_HASH_TABLE(x)            (0x00000010 + (x) * 4)
 #define XGMAC_MAX_HASH_TABLE           8
-#define XGMAC_VLAN_TAG                 0x00000050
-#define XGMAC_VLAN_EDVLP               BIT(26)
-#define XGMAC_VLAN_VTHM                        BIT(25)
-#define XGMAC_VLAN_DOVLTC              BIT(20)
-#define XGMAC_VLAN_ESVL                        BIT(18)
-#define XGMAC_VLAN_ETV                 BIT(16)
-#define XGMAC_VLAN_VID                 GENMASK(15, 0)
-#define XGMAC_VLAN_HASH_TABLE          0x00000058
-#define XGMAC_VLAN_INCL                        0x00000060
-#define XGMAC_VLAN_VLTI                        BIT(20)
-#define XGMAC_VLAN_CSVL                        BIT(19)
-#define XGMAC_VLAN_VLC                 GENMASK(17, 16)
-#define XGMAC_VLAN_VLC_SHIFT           16
 #define XGMAC_RXQ_CTRL0                        0x000000a0
 #define XGMAC_RXQEN(x)                 GENMASK((x) * 2 + 1, (x) * 2)
 #define XGMAC_RXQEN_SHIFT(x)           ((x) * 2)
index a6d395c6bacd14b1d9965709e865482e3e296dad..d9f41c047e5e157d20779e7024a072b9ebec2e34 100644 (file)
@@ -614,76 +614,6 @@ static int dwxgmac2_rss_configure(struct mac_device_info *hw,
        return 0;
 }
 
-static void dwxgmac2_update_vlan_hash(struct mac_device_info *hw, u32 hash,
-                                     u16 perfect_match, bool is_double)
-{
-       void __iomem *ioaddr = hw->pcsr;
-
-       writel(hash, ioaddr + XGMAC_VLAN_HASH_TABLE);
-
-       if (hash) {
-               u32 value = readl(ioaddr + XGMAC_PACKET_FILTER);
-
-               value |= XGMAC_FILTER_VTFE;
-
-               writel(value, ioaddr + XGMAC_PACKET_FILTER);
-
-               value = readl(ioaddr + XGMAC_VLAN_TAG);
-
-               value |= XGMAC_VLAN_VTHM | XGMAC_VLAN_ETV;
-               if (is_double) {
-                       value |= XGMAC_VLAN_EDVLP;
-                       value |= XGMAC_VLAN_ESVL;
-                       value |= XGMAC_VLAN_DOVLTC;
-               } else {
-                       value &= ~XGMAC_VLAN_EDVLP;
-                       value &= ~XGMAC_VLAN_ESVL;
-                       value &= ~XGMAC_VLAN_DOVLTC;
-               }
-
-               value &= ~XGMAC_VLAN_VID;
-               writel(value, ioaddr + XGMAC_VLAN_TAG);
-       } else if (perfect_match) {
-               u32 value = readl(ioaddr + XGMAC_PACKET_FILTER);
-
-               value |= XGMAC_FILTER_VTFE;
-
-               writel(value, ioaddr + XGMAC_PACKET_FILTER);
-
-               value = readl(ioaddr + XGMAC_VLAN_TAG);
-
-               value &= ~XGMAC_VLAN_VTHM;
-               value |= XGMAC_VLAN_ETV;
-               if (is_double) {
-                       value |= XGMAC_VLAN_EDVLP;
-                       value |= XGMAC_VLAN_ESVL;
-                       value |= XGMAC_VLAN_DOVLTC;
-               } else {
-                       value &= ~XGMAC_VLAN_EDVLP;
-                       value &= ~XGMAC_VLAN_ESVL;
-                       value &= ~XGMAC_VLAN_DOVLTC;
-               }
-
-               value &= ~XGMAC_VLAN_VID;
-               writel(value | perfect_match, ioaddr + XGMAC_VLAN_TAG);
-       } else {
-               u32 value = readl(ioaddr + XGMAC_PACKET_FILTER);
-
-               value &= ~XGMAC_FILTER_VTFE;
-
-               writel(value, ioaddr + XGMAC_PACKET_FILTER);
-
-               value = readl(ioaddr + XGMAC_VLAN_TAG);
-
-               value &= ~(XGMAC_VLAN_VTHM | XGMAC_VLAN_ETV);
-               value &= ~(XGMAC_VLAN_EDVLP | XGMAC_VLAN_ESVL);
-               value &= ~XGMAC_VLAN_DOVLTC;
-               value &= ~XGMAC_VLAN_VID;
-
-               writel(value, ioaddr + XGMAC_VLAN_TAG);
-       }
-}
-
 struct dwxgmac3_error_desc {
        bool valid;
        const char *desc;
@@ -1300,19 +1230,6 @@ static void dwxgmac2_sarc_configure(void __iomem *ioaddr, int val)
        writel(value, ioaddr + XGMAC_TX_CONFIG);
 }
 
-static void dwxgmac2_enable_vlan(struct mac_device_info *hw, u32 type)
-{
-       void __iomem *ioaddr = hw->pcsr;
-       u32 value;
-
-       value = readl(ioaddr + XGMAC_VLAN_INCL);
-       value |= XGMAC_VLAN_VLTI;
-       value |= XGMAC_VLAN_CSVL; /* Only use SVLAN */
-       value &= ~XGMAC_VLAN_VLC;
-       value |= (type << XGMAC_VLAN_VLC_SHIFT) & XGMAC_VLAN_VLC;
-       writel(value, ioaddr + XGMAC_VLAN_INCL);
-}
-
 static int dwxgmac2_filter_wait(struct mac_device_info *hw)
 {
        void __iomem *ioaddr = hw->pcsr;
@@ -1534,12 +1451,10 @@ const struct stmmac_ops dwxgmac210_ops = {
        .safety_feat_dump = dwxgmac3_safety_feat_dump,
        .set_mac_loopback = dwxgmac2_set_mac_loopback,
        .rss_configure = dwxgmac2_rss_configure,
-       .update_vlan_hash = dwxgmac2_update_vlan_hash,
        .rxp_config = dwxgmac3_rxp_config,
        .get_mac_tx_timestamp = dwxgmac2_get_mac_tx_timestamp,
        .flex_pps_config = dwxgmac2_flex_pps_config,
        .sarc_configure = dwxgmac2_sarc_configure,
-       .enable_vlan = dwxgmac2_enable_vlan,
        .config_l3_filter = dwxgmac2_config_l3_filter,
        .config_l4_filter = dwxgmac2_config_l4_filter,
        .set_arp_offload = dwxgmac2_set_arp_offload,
@@ -1590,12 +1505,10 @@ const struct stmmac_ops dwxlgmac2_ops = {
        .safety_feat_dump = dwxgmac3_safety_feat_dump,
        .set_mac_loopback = dwxgmac2_set_mac_loopback,
        .rss_configure = dwxgmac2_rss_configure,
-       .update_vlan_hash = dwxgmac2_update_vlan_hash,
        .rxp_config = dwxgmac3_rxp_config,
        .get_mac_tx_timestamp = dwxgmac2_get_mac_tx_timestamp,
        .flex_pps_config = dwxgmac2_flex_pps_config,
        .sarc_configure = dwxgmac2_sarc_configure,
-       .enable_vlan = dwxgmac2_enable_vlan,
        .config_l3_filter = dwxgmac2_config_l3_filter,
        .config_l4_filter = dwxgmac2_config_l4_filter,
        .set_arp_offload = dwxgmac2_set_arp_offload,
index 31bdbab9a46ca2458c1ad195de2008baffed4554..d801cd40b529f04de7b102611b93a8fa12445d7f 100644 (file)
@@ -9,6 +9,7 @@
 #include "stmmac_fpe.h"
 #include "stmmac_ptp.h"
 #include "stmmac_est.h"
+#include "stmmac_vlan.h"
 #include "dwmac4_descs.h"
 #include "dwxgmac2.h"
 
@@ -120,6 +121,7 @@ static const struct stmmac_hwif_entry {
        const void *tc;
        const void *mmc;
        const void *est;
+       const void *vlan;
        int (*setup)(struct stmmac_priv *priv);
        int (*quirks)(struct stmmac_priv *priv);
 } stmmac_hw[] = {
@@ -175,6 +177,7 @@ static const struct stmmac_hwif_entry {
                .desc = &dwmac4_desc_ops,
                .dma = &dwmac4_dma_ops,
                .mac = &dwmac4_ops,
+               .vlan = &dwmac4_vlan_ops,
                .hwtimestamp = &stmmac_ptp,
                .ptp = &stmmac_ptp_clock_ops,
                .mode = NULL,
@@ -197,6 +200,7 @@ static const struct stmmac_hwif_entry {
                .desc = &dwmac4_desc_ops,
                .dma = &dwmac4_dma_ops,
                .mac = &dwmac410_ops,
+               .vlan = &dwmac410_vlan_ops,
                .hwtimestamp = &stmmac_ptp,
                .ptp = &stmmac_ptp_clock_ops,
                .mode = &dwmac4_ring_mode_ops,
@@ -219,6 +223,7 @@ static const struct stmmac_hwif_entry {
                .desc = &dwmac4_desc_ops,
                .dma = &dwmac410_dma_ops,
                .mac = &dwmac410_ops,
+               .vlan = &dwmac410_vlan_ops,
                .hwtimestamp = &stmmac_ptp,
                .ptp = &stmmac_ptp_clock_ops,
                .mode = &dwmac4_ring_mode_ops,
@@ -241,6 +246,7 @@ static const struct stmmac_hwif_entry {
                .desc = &dwmac4_desc_ops,
                .dma = &dwmac410_dma_ops,
                .mac = &dwmac510_ops,
+               .vlan = &dwmac510_vlan_ops,
                .hwtimestamp = &stmmac_ptp,
                .ptp = &stmmac_ptp_clock_ops,
                .mode = &dwmac4_ring_mode_ops,
@@ -264,6 +270,7 @@ static const struct stmmac_hwif_entry {
                .desc = &dwxgmac210_desc_ops,
                .dma = &dwxgmac210_dma_ops,
                .mac = &dwxgmac210_ops,
+               .vlan = &dwxgmac210_vlan_ops,
                .hwtimestamp = &stmmac_ptp,
                .ptp = &stmmac_ptp_clock_ops,
                .mode = NULL,
@@ -287,6 +294,7 @@ static const struct stmmac_hwif_entry {
                .desc = &dwxgmac210_desc_ops,
                .dma = &dwxgmac210_dma_ops,
                .mac = &dwxlgmac2_ops,
+               .vlan = &dwxlgmac2_vlan_ops,
                .hwtimestamp = &stmmac_ptp,
                .ptp = &stmmac_ptp_clock_ops,
                .mode = NULL,
@@ -368,6 +376,7 @@ int stmmac_hwif_init(struct stmmac_priv *priv)
                mac->tc = mac->tc ? : entry->tc;
                mac->mmc = mac->mmc ? : entry->mmc;
                mac->est = mac->est ? : entry->est;
+               mac->vlan = mac->vlan ? : entry->vlan;
 
                priv->hw = mac;
                priv->fpe_cfg.reg = entry->regs.fpe_reg;
index 27c63a9fc163de4f3796cefee340349e3d145943..ae4efffb785fcb197be85faeea358f58d8660a92 100644 (file)
@@ -398,21 +398,6 @@ struct stmmac_ops {
        /* RSS */
        int (*rss_configure)(struct mac_device_info *hw,
                             struct stmmac_rss *cfg, u32 num_rxq);
-       /* VLAN */
-       void (*update_vlan_hash)(struct mac_device_info *hw, u32 hash,
-                                u16 perfect_match, bool is_double);
-       void (*enable_vlan)(struct mac_device_info *hw, u32 type);
-       void (*rx_hw_vlan)(struct mac_device_info *hw, struct dma_desc *rx_desc,
-                          struct sk_buff *skb);
-       void (*set_hw_vlan_mode)(struct mac_device_info *hw);
-       int (*add_hw_vlan_rx_fltr)(struct net_device *dev,
-                                  struct mac_device_info *hw,
-                                  __be16 proto, u16 vid);
-       int (*del_hw_vlan_rx_fltr)(struct net_device *dev,
-                                  struct mac_device_info *hw,
-                                  __be16 proto, u16 vid);
-       void (*restore_hw_vlan_rx_fltr)(struct net_device *dev,
-                                       struct mac_device_info *hw);
        /* TX Timestamp */
        int (*get_mac_tx_timestamp)(struct mac_device_info *hw, u64 *ts);
        /* Source Address Insertion / Replacement */
@@ -498,20 +483,6 @@ struct stmmac_ops {
        stmmac_do_void_callback(__priv, mac, set_mac_loopback, __args)
 #define stmmac_rss_configure(__priv, __args...) \
        stmmac_do_callback(__priv, mac, rss_configure, __args)
-#define stmmac_update_vlan_hash(__priv, __args...) \
-       stmmac_do_void_callback(__priv, mac, update_vlan_hash, __args)
-#define stmmac_enable_vlan(__priv, __args...) \
-       stmmac_do_void_callback(__priv, mac, enable_vlan, __args)
-#define stmmac_rx_hw_vlan(__priv, __args...) \
-       stmmac_do_void_callback(__priv, mac, rx_hw_vlan, __args)
-#define stmmac_set_hw_vlan_mode(__priv, __args...) \
-       stmmac_do_void_callback(__priv, mac, set_hw_vlan_mode, __args)
-#define stmmac_add_hw_vlan_rx_fltr(__priv, __args...) \
-       stmmac_do_callback(__priv, mac, add_hw_vlan_rx_fltr, __args)
-#define stmmac_del_hw_vlan_rx_fltr(__priv, __args...) \
-       stmmac_do_callback(__priv, mac, del_hw_vlan_rx_fltr, __args)
-#define stmmac_restore_hw_vlan_rx_fltr(__priv, __args...) \
-       stmmac_do_void_callback(__priv, mac, restore_hw_vlan_rx_fltr, __args)
 #define stmmac_get_mac_tx_timestamp(__priv, __args...) \
        stmmac_do_callback(__priv, mac, get_mac_tx_timestamp, __args)
 #define stmmac_sarc_configure(__priv, __args...) \
@@ -659,6 +630,39 @@ struct stmmac_est_ops {
 #define stmmac_est_irq_status(__priv, __args...) \
        stmmac_do_void_callback(__priv, est, irq_status, __args)
 
+struct stmmac_vlan_ops {
+       /* VLAN */
+       void (*update_vlan_hash)(struct mac_device_info *hw, u32 hash,
+                                u16 perfect_match, bool is_double);
+       void (*enable_vlan)(struct mac_device_info *hw, u32 type);
+       void (*rx_hw_vlan)(struct mac_device_info *hw, struct dma_desc *rx_desc,
+                          struct sk_buff *skb);
+       void (*set_hw_vlan_mode)(struct mac_device_info *hw);
+       int (*add_hw_vlan_rx_fltr)(struct net_device *dev,
+                                  struct mac_device_info *hw,
+                                  __be16 proto, u16 vid);
+       int (*del_hw_vlan_rx_fltr)(struct net_device *dev,
+                                  struct mac_device_info *hw,
+                                  __be16 proto, u16 vid);
+       void (*restore_hw_vlan_rx_fltr)(struct net_device *dev,
+                                       struct mac_device_info *hw);
+};
+
+#define stmmac_update_vlan_hash(__priv, __args...) \
+       stmmac_do_void_callback(__priv, vlan, update_vlan_hash, __args)
+#define stmmac_enable_vlan(__priv, __args...) \
+       stmmac_do_void_callback(__priv, vlan, enable_vlan, __args)
+#define stmmac_rx_hw_vlan(__priv, __args...) \
+       stmmac_do_void_callback(__priv, vlan, rx_hw_vlan, __args)
+#define stmmac_set_hw_vlan_mode(__priv, __args...) \
+       stmmac_do_void_callback(__priv, vlan, set_hw_vlan_mode, __args)
+#define stmmac_add_hw_vlan_rx_fltr(__priv, __args...) \
+       stmmac_do_callback(__priv, vlan, add_hw_vlan_rx_fltr, __args)
+#define stmmac_del_hw_vlan_rx_fltr(__priv, __args...) \
+       stmmac_do_callback(__priv, vlan, del_hw_vlan_rx_fltr, __args)
+#define stmmac_restore_hw_vlan_rx_fltr(__priv, __args...) \
+       stmmac_do_void_callback(__priv, vlan, restore_hw_vlan_rx_fltr, __args)
+
 struct stmmac_regs_off {
        const struct stmmac_fpe_reg *fpe_reg;
        u32 ptp_off;
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_vlan.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_vlan.c
new file mode 100644 (file)
index 0000000..c84d921
--- /dev/null
@@ -0,0 +1,402 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2025, Altera Corporation
+ * stmmac VLAN (802.1Q) handling
+ */
+
+#include "stmmac.h"
+#include "stmmac_vlan.h"
+
+static void dwmac4_write_single_vlan(struct net_device *dev, u16 vid)
+{
+       void __iomem *ioaddr = (void __iomem *)dev->base_addr;
+       u32 val;
+
+       val = readl(ioaddr + GMAC_VLAN_TAG);
+       val &= ~GMAC_VLAN_TAG_VID;
+       val |= GMAC_VLAN_TAG_ETV | vid;
+
+       writel(val, ioaddr + GMAC_VLAN_TAG);
+}
+
+static int dwmac4_write_vlan_filter(struct net_device *dev,
+                                   struct mac_device_info *hw,
+                                   u8 index, u32 data)
+{
+       void __iomem *ioaddr = (void __iomem *)dev->base_addr;
+       int ret;
+       u32 val;
+
+       if (index >= hw->num_vlan)
+               return -EINVAL;
+
+       writel(data, ioaddr + GMAC_VLAN_TAG_DATA);
+
+       val = readl(ioaddr + GMAC_VLAN_TAG);
+       val &= ~(GMAC_VLAN_TAG_CTRL_OFS_MASK |
+               GMAC_VLAN_TAG_CTRL_CT |
+               GMAC_VLAN_TAG_CTRL_OB);
+       val |= (index << GMAC_VLAN_TAG_CTRL_OFS_SHIFT) | GMAC_VLAN_TAG_CTRL_OB;
+
+       writel(val, ioaddr + GMAC_VLAN_TAG);
+
+       ret = readl_poll_timeout(ioaddr + GMAC_VLAN_TAG, val,
+                                !(val & GMAC_VLAN_TAG_CTRL_OB),
+                                1000, 500000);
+       if (ret) {
+               netdev_err(dev, "Timeout accessing MAC_VLAN_Tag_Filter\n");
+               return -EBUSY;
+       }
+
+       return 0;
+}
+
+static int dwmac4_add_hw_vlan_rx_fltr(struct net_device *dev,
+                                     struct mac_device_info *hw,
+                                     __be16 proto, u16 vid)
+{
+       int index = -1;
+       u32 val = 0;
+       int i, ret;
+
+       if (vid > 4095)
+               return -EINVAL;
+
+       /* Single Rx VLAN Filter */
+       if (hw->num_vlan == 1) {
+               /* For single VLAN filter, VID 0 means VLAN promiscuous */
+               if (vid == 0) {
+                       netdev_warn(dev, "Adding VLAN ID 0 is not supported\n");
+                       return -EPERM;
+               }
+
+               if (hw->vlan_filter[0] & GMAC_VLAN_TAG_VID) {
+                       netdev_err(dev, "Only single VLAN ID supported\n");
+                       return -EPERM;
+               }
+
+               hw->vlan_filter[0] = vid;
+               dwmac4_write_single_vlan(dev, vid);
+
+               return 0;
+       }
+
+       /* Extended Rx VLAN Filter Enable */
+       val |= GMAC_VLAN_TAG_DATA_ETV | GMAC_VLAN_TAG_DATA_VEN | vid;
+
+       for (i = 0; i < hw->num_vlan; i++) {
+               if (hw->vlan_filter[i] == val)
+                       return 0;
+               else if (!(hw->vlan_filter[i] & GMAC_VLAN_TAG_DATA_VEN))
+                       index = i;
+       }
+
+       if (index == -1) {
+               netdev_err(dev, "MAC_VLAN_Tag_Filter full (size: %0u)\n",
+                          hw->num_vlan);
+               return -EPERM;
+       }
+
+       ret = dwmac4_write_vlan_filter(dev, hw, index, val);
+
+       if (!ret)
+               hw->vlan_filter[index] = val;
+
+       return ret;
+}
+
+static int dwmac4_del_hw_vlan_rx_fltr(struct net_device *dev,
+                                     struct mac_device_info *hw,
+                                     __be16 proto, u16 vid)
+{
+       int i, ret = 0;
+
+       /* Single Rx VLAN Filter */
+       if (hw->num_vlan == 1) {
+               if ((hw->vlan_filter[0] & GMAC_VLAN_TAG_VID) == vid) {
+                       hw->vlan_filter[0] = 0;
+                       dwmac4_write_single_vlan(dev, 0);
+               }
+               return 0;
+       }
+
+       /* Extended Rx VLAN Filter Enable */
+       for (i = 0; i < hw->num_vlan; i++) {
+               if ((hw->vlan_filter[i] & GMAC_VLAN_TAG_DATA_VID) == vid) {
+                       ret = dwmac4_write_vlan_filter(dev, hw, i, 0);
+
+                       if (!ret)
+                               hw->vlan_filter[i] = 0;
+                       else
+                               return ret;
+               }
+       }
+
+       return ret;
+}
+
+static void dwmac4_restore_hw_vlan_rx_fltr(struct net_device *dev,
+                                          struct mac_device_info *hw)
+{
+       void __iomem *ioaddr = hw->pcsr;
+       u32 value;
+       u32 hash;
+       u32 val;
+       int i;
+
+       /* Single Rx VLAN Filter */
+       if (hw->num_vlan == 1) {
+               dwmac4_write_single_vlan(dev, hw->vlan_filter[0]);
+               return;
+       }
+
+       /* Extended Rx VLAN Filter Enable */
+       for (i = 0; i < hw->num_vlan; i++) {
+               if (hw->vlan_filter[i] & GMAC_VLAN_TAG_DATA_VEN) {
+                       val = hw->vlan_filter[i];
+                       dwmac4_write_vlan_filter(dev, hw, i, val);
+               }
+       }
+
+       hash = readl(ioaddr + GMAC_VLAN_HASH_TABLE);
+       if (hash & GMAC_VLAN_VLHT) {
+               value = readl(ioaddr + GMAC_VLAN_TAG);
+               value |= GMAC_VLAN_VTHM;
+               writel(value, ioaddr + GMAC_VLAN_TAG);
+       }
+}
+
+static void dwmac4_update_vlan_hash(struct mac_device_info *hw, u32 hash,
+                                   u16 perfect_match, bool is_double)
+{
+       void __iomem *ioaddr = hw->pcsr;
+       u32 value;
+
+       writel(hash, ioaddr + GMAC_VLAN_HASH_TABLE);
+
+       value = readl(ioaddr + GMAC_VLAN_TAG);
+
+       if (hash) {
+               value |= GMAC_VLAN_VTHM | GMAC_VLAN_ETV;
+               if (is_double) {
+                       value |= GMAC_VLAN_EDVLP;
+                       value |= GMAC_VLAN_ESVL;
+                       value |= GMAC_VLAN_DOVLTC;
+               }
+
+               writel(value, ioaddr + GMAC_VLAN_TAG);
+       } else if (perfect_match) {
+               u32 value = GMAC_VLAN_ETV;
+
+               if (is_double) {
+                       value |= GMAC_VLAN_EDVLP;
+                       value |= GMAC_VLAN_ESVL;
+                       value |= GMAC_VLAN_DOVLTC;
+               }
+
+               writel(value | perfect_match, ioaddr + GMAC_VLAN_TAG);
+       } else {
+               value &= ~(GMAC_VLAN_VTHM | GMAC_VLAN_ETV);
+               value &= ~(GMAC_VLAN_EDVLP | GMAC_VLAN_ESVL);
+               value &= ~GMAC_VLAN_DOVLTC;
+               value &= ~GMAC_VLAN_VID;
+
+               writel(value, ioaddr + GMAC_VLAN_TAG);
+       }
+}
+
+static void dwmac4_enable_vlan(struct mac_device_info *hw, u32 type)
+{
+       void __iomem *ioaddr = hw->pcsr;
+       u32 value;
+
+       value = readl(ioaddr + GMAC_VLAN_INCL);
+       value |= GMAC_VLAN_VLTI;
+       value |= GMAC_VLAN_CSVL; /* Only use SVLAN */
+       value &= ~GMAC_VLAN_VLC;
+       value |= (type << GMAC_VLAN_VLC_SHIFT) & GMAC_VLAN_VLC;
+       writel(value, ioaddr + GMAC_VLAN_INCL);
+}
+
+static void dwmac4_rx_hw_vlan(struct mac_device_info *hw,
+                             struct dma_desc *rx_desc, struct sk_buff *skb)
+{
+       if (hw->desc->get_rx_vlan_valid(rx_desc)) {
+               u16 vid = hw->desc->get_rx_vlan_tci(rx_desc);
+
+               __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vid);
+       }
+}
+
+static void dwmac4_set_hw_vlan_mode(struct mac_device_info *hw)
+{
+       void __iomem *ioaddr = hw->pcsr;
+       u32 value = readl(ioaddr + GMAC_VLAN_TAG);
+
+       value &= ~GMAC_VLAN_TAG_CTRL_EVLS_MASK;
+
+       if (hw->hw_vlan_en)
+               /* Always strip VLAN on Receive */
+               value |= GMAC_VLAN_TAG_STRIP_ALL;
+       else
+               /* Do not strip VLAN on Receive */
+               value |= GMAC_VLAN_TAG_STRIP_NONE;
+
+       /* Enable outer VLAN Tag in Rx DMA descriptor */
+       value |= GMAC_VLAN_TAG_CTRL_EVLRXS;
+       writel(value, ioaddr + GMAC_VLAN_TAG);
+}
+
+static void dwxgmac2_update_vlan_hash(struct mac_device_info *hw, u32 hash,
+                                     u16 perfect_match, bool is_double)
+{
+       void __iomem *ioaddr = hw->pcsr;
+
+       writel(hash, ioaddr + XGMAC_VLAN_HASH_TABLE);
+
+       if (hash) {
+               u32 value = readl(ioaddr + XGMAC_PACKET_FILTER);
+
+               value |= XGMAC_FILTER_VTFE;
+
+               writel(value, ioaddr + XGMAC_PACKET_FILTER);
+
+               value = readl(ioaddr + XGMAC_VLAN_TAG);
+
+               value |= XGMAC_VLAN_VTHM | XGMAC_VLAN_ETV;
+               if (is_double) {
+                       value |= XGMAC_VLAN_EDVLP;
+                       value |= XGMAC_VLAN_ESVL;
+                       value |= XGMAC_VLAN_DOVLTC;
+               } else {
+                       value &= ~XGMAC_VLAN_EDVLP;
+                       value &= ~XGMAC_VLAN_ESVL;
+                       value &= ~XGMAC_VLAN_DOVLTC;
+               }
+
+               value &= ~XGMAC_VLAN_VID;
+               writel(value, ioaddr + XGMAC_VLAN_TAG);
+       } else if (perfect_match) {
+               u32 value = readl(ioaddr + XGMAC_PACKET_FILTER);
+
+               value |= XGMAC_FILTER_VTFE;
+
+               writel(value, ioaddr + XGMAC_PACKET_FILTER);
+
+               value = readl(ioaddr + XGMAC_VLAN_TAG);
+
+               value &= ~XGMAC_VLAN_VTHM;
+               value |= XGMAC_VLAN_ETV;
+               if (is_double) {
+                       value |= XGMAC_VLAN_EDVLP;
+                       value |= XGMAC_VLAN_ESVL;
+                       value |= XGMAC_VLAN_DOVLTC;
+               } else {
+                       value &= ~XGMAC_VLAN_EDVLP;
+                       value &= ~XGMAC_VLAN_ESVL;
+                       value &= ~XGMAC_VLAN_DOVLTC;
+               }
+
+               value &= ~XGMAC_VLAN_VID;
+               writel(value | perfect_match, ioaddr + XGMAC_VLAN_TAG);
+       } else {
+               u32 value = readl(ioaddr + XGMAC_PACKET_FILTER);
+
+               value &= ~XGMAC_FILTER_VTFE;
+
+               writel(value, ioaddr + XGMAC_PACKET_FILTER);
+
+               value = readl(ioaddr + XGMAC_VLAN_TAG);
+
+               value &= ~(XGMAC_VLAN_VTHM | XGMAC_VLAN_ETV);
+               value &= ~(XGMAC_VLAN_EDVLP | XGMAC_VLAN_ESVL);
+               value &= ~XGMAC_VLAN_DOVLTC;
+               value &= ~XGMAC_VLAN_VID;
+
+               writel(value, ioaddr + XGMAC_VLAN_TAG);
+       }
+}
+
+static void dwxgmac2_enable_vlan(struct mac_device_info *hw, u32 type)
+{
+       void __iomem *ioaddr = hw->pcsr;
+       u32 value;
+
+       value = readl(ioaddr + XGMAC_VLAN_INCL);
+       value |= XGMAC_VLAN_VLTI;
+       value |= XGMAC_VLAN_CSVL; /* Only use SVLAN */
+       value &= ~XGMAC_VLAN_VLC;
+       value |= (type << XGMAC_VLAN_VLC_SHIFT) & XGMAC_VLAN_VLC;
+       writel(value, ioaddr + XGMAC_VLAN_INCL);
+}
+
+const struct stmmac_vlan_ops dwmac4_vlan_ops = {
+       .update_vlan_hash = dwmac4_update_vlan_hash,
+       .enable_vlan = dwmac4_enable_vlan,
+       .add_hw_vlan_rx_fltr = dwmac4_add_hw_vlan_rx_fltr,
+       .del_hw_vlan_rx_fltr = dwmac4_del_hw_vlan_rx_fltr,
+       .restore_hw_vlan_rx_fltr = dwmac4_restore_hw_vlan_rx_fltr,
+       .rx_hw_vlan = dwmac4_rx_hw_vlan,
+       .set_hw_vlan_mode = dwmac4_set_hw_vlan_mode,
+};
+
+const struct stmmac_vlan_ops dwmac410_vlan_ops = {
+       .update_vlan_hash = dwmac4_update_vlan_hash,
+       .enable_vlan = dwmac4_enable_vlan,
+       .add_hw_vlan_rx_fltr = dwmac4_add_hw_vlan_rx_fltr,
+       .del_hw_vlan_rx_fltr = dwmac4_del_hw_vlan_rx_fltr,
+       .restore_hw_vlan_rx_fltr = dwmac4_restore_hw_vlan_rx_fltr,
+       .rx_hw_vlan = dwmac4_rx_hw_vlan,
+       .set_hw_vlan_mode = dwmac4_set_hw_vlan_mode,
+};
+
+const struct stmmac_vlan_ops dwmac510_vlan_ops = {
+       .update_vlan_hash = dwmac4_update_vlan_hash,
+       .enable_vlan = dwmac4_enable_vlan,
+       .add_hw_vlan_rx_fltr = dwmac4_add_hw_vlan_rx_fltr,
+       .del_hw_vlan_rx_fltr = dwmac4_del_hw_vlan_rx_fltr,
+       .restore_hw_vlan_rx_fltr = dwmac4_restore_hw_vlan_rx_fltr,
+       .rx_hw_vlan = dwmac4_rx_hw_vlan,
+       .set_hw_vlan_mode = dwmac4_set_hw_vlan_mode,
+};
+
+const struct stmmac_vlan_ops dwxgmac210_vlan_ops = {
+       .update_vlan_hash = dwxgmac2_update_vlan_hash,
+       .enable_vlan = dwxgmac2_enable_vlan,
+};
+
+const struct stmmac_vlan_ops dwxlgmac2_vlan_ops = {
+       .update_vlan_hash = dwxgmac2_update_vlan_hash,
+       .enable_vlan = dwxgmac2_enable_vlan,
+};
+
+u32 dwmac4_get_num_vlan(void __iomem *ioaddr)
+{
+       u32 val, num_vlan;
+
+       val = readl(ioaddr + GMAC_HW_FEATURE3);
+       switch (val & GMAC_HW_FEAT_NRVF) {
+       case 0:
+               num_vlan = 1;
+               break;
+       case 1:
+               num_vlan = 4;
+               break;
+       case 2:
+               num_vlan = 8;
+               break;
+       case 3:
+               num_vlan = 16;
+               break;
+       case 4:
+               num_vlan = 24;
+               break;
+       case 5:
+               num_vlan = 32;
+               break;
+       default:
+               num_vlan = 1;
+       }
+
+       return num_vlan;
+}
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_vlan.h b/drivers/net/ethernet/stmicro/stmmac/stmmac_vlan.h
new file mode 100644 (file)
index 0000000..c318e58
--- /dev/null
@@ -0,0 +1,80 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2025, Altera Corporation
+ * stmmac VLAN(802.1Q) handling
+ */
+
+#ifndef __STMMAC_VLAN_H__
+#define __STMMAC_VLAN_H__
+
+#include <linux/bitfield.h>
+#include "dwmac4.h"
+#include "dwxgmac2.h"
+
+/* DWMAC4 Setting */
+#define GMAC_VLAN_TAG                  0x00000050
+#define GMAC_VLAN_TAG_DATA             0x00000054
+#define GMAC_VLAN_HASH_TABLE           0x00000058
+#define GMAC_VLAN_INCL                 0x00000060
+
+/* MAC VLAN */
+#define GMAC_VLAN_EDVLP                        BIT(26)
+#define GMAC_VLAN_VTHM                 BIT(25)
+#define GMAC_VLAN_DOVLTC               BIT(20)
+#define GMAC_VLAN_ESVL                 BIT(18)
+#define GMAC_VLAN_ETV                  BIT(16)
+#define GMAC_VLAN_VID                  GENMASK(15, 0)
+#define GMAC_VLAN_VLTI                 BIT(20)
+#define GMAC_VLAN_CSVL                 BIT(19)
+#define GMAC_VLAN_VLC                  GENMASK(17, 16)
+#define GMAC_VLAN_VLC_SHIFT            16
+#define GMAC_VLAN_VLHT                 GENMASK(15, 0)
+
+/* MAC VLAN Tag */
+#define GMAC_VLAN_TAG_VID              GENMASK(15, 0)
+#define GMAC_VLAN_TAG_ETV              BIT(16)
+
+/* MAC VLAN Tag Control */
+#define GMAC_VLAN_TAG_CTRL_OB          BIT(0)
+#define GMAC_VLAN_TAG_CTRL_CT          BIT(1)
+#define GMAC_VLAN_TAG_CTRL_OFS_MASK    GENMASK(6, 2)
+#define GMAC_VLAN_TAG_CTRL_OFS_SHIFT   2
+#define GMAC_VLAN_TAG_CTRL_EVLS_MASK   GENMASK(22, 21)
+#define GMAC_VLAN_TAG_CTRL_EVLS_SHIFT  21
+#define GMAC_VLAN_TAG_CTRL_EVLRXS      BIT(24)
+
+#define GMAC_VLAN_TAG_STRIP_NONE       (0x0 << GMAC_VLAN_TAG_CTRL_EVLS_SHIFT)
+#define GMAC_VLAN_TAG_STRIP_PASS       (0x1 << GMAC_VLAN_TAG_CTRL_EVLS_SHIFT)
+#define GMAC_VLAN_TAG_STRIP_FAIL       (0x2 << GMAC_VLAN_TAG_CTRL_EVLS_SHIFT)
+#define GMAC_VLAN_TAG_STRIP_ALL                (0x3 << GMAC_VLAN_TAG_CTRL_EVLS_SHIFT)
+
+/* MAC VLAN Tag Data/Filter */
+#define GMAC_VLAN_TAG_DATA_VID         GENMASK(15, 0)
+#define GMAC_VLAN_TAG_DATA_VEN         BIT(16)
+#define GMAC_VLAN_TAG_DATA_ETV         BIT(17)
+
+/* DWXGMAC Setting */
+#define XGMAC_VLAN_TAG                 0x00000050
+#define XGMAC_VLAN_EDVLP               BIT(26)
+#define XGMAC_VLAN_VTHM                        BIT(25)
+#define XGMAC_VLAN_DOVLTC              BIT(20)
+#define XGMAC_VLAN_ESVL                        BIT(18)
+#define XGMAC_VLAN_ETV                 BIT(16)
+#define XGMAC_VLAN_VID                 GENMASK(15, 0)
+#define XGMAC_VLAN_HASH_TABLE          0x00000058
+#define XGMAC_VLAN_INCL                        0x00000060
+#define XGMAC_VLAN_VLTI                        BIT(20)
+#define XGMAC_VLAN_CSVL                        BIT(19)
+#define XGMAC_VLAN_VLC                 GENMASK(17, 16)
+#define XGMAC_VLAN_VLC_SHIFT           16
+#define XGMAC_FILTER_VTFE              BIT(16)
+
+extern const struct stmmac_vlan_ops dwmac4_vlan_ops;
+extern const struct stmmac_vlan_ops dwmac410_vlan_ops;
+extern const struct stmmac_vlan_ops dwmac510_vlan_ops;
+extern const struct stmmac_vlan_ops dwxgmac210_vlan_ops;
+extern const struct stmmac_vlan_ops dwxlgmac2_vlan_ops;
+
+u32 dwmac4_get_num_vlan(void __iomem *ioaddr);
+
+#endif /* __STMMAC_VLAN_H__ */