#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_VLAN_VLC                  GENMASK(17, 16)
 #define GMAC_VLAN_VLC_SHIFT            16
 
+/* 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)
 #define GMAC_HW_FEAT_FRPBS             GENMASK(12, 11)
 #define GMAC_HW_FEAT_FRPSEL            BIT(10)
 #define GMAC_HW_FEAT_DVLAN             BIT(5)
+#define GMAC_HW_FEAT_NRVF              GENMASK(2, 0)
 
 /* MAC HW ADDR regs */
 #define GMAC_HI_DCS                    GENMASK(18, 16)
 
        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 i, timeout = 10;
+       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);
+
+       for (i = 0; i < timeout; i++) {
+               val = readl(ioaddr + GMAC_VLAN_TAG);
+               if (!(val & GMAC_VLAN_TAG_CTRL_OB))
+                       return 0;
+               udelay(1);
+       }
+
+       netdev_err(dev, "Timeout accessing MAC_VLAN_Tag_Filter\n");
+
+       return -EBUSY;
+}
+
+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)
+{
+       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);
+               }
+       }
+}
+
 static void dwmac4_set_filter(struct mac_device_info *hw,
                              struct net_device *dev)
 {
                }
        }
 
+       /* VLAN filtering */
+       if (dev->features & NETIF_F_HW_VLAN_CTAG_FILTER)
+               value |= GMAC_PACKET_FILTER_VTFE;
+
        writel(value, ioaddr + GMAC_PACKET_FILTER);
 }
 
        .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,
 };
 
 const struct stmmac_ops dwmac410_ops = {
        .config_l4_filter = dwmac4_config_l4_filter,
        .est_configure = dwmac5_est_configure,
        .fpe_configure = dwmac5_fpe_configure,
+       .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,
 };
 
 const struct stmmac_ops dwmac510_ops = {
        .config_l4_filter = dwmac4_config_l4_filter,
        .est_configure = dwmac5_est_configure,
        .fpe_configure = dwmac5_fpe_configure,
+       .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,
 };
 
+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;
        mac->mii.reg_mask = GENMASK(20, 16);
        mac->mii.clk_csr_shift = 8;
        mac->mii.clk_csr_mask = GENMASK(11, 8);
+       mac->num_vlan = dwmac4_get_num_vlan(priv->ioaddr);
 
        return 0;
 }
 
        void (*update_vlan_hash)(struct mac_device_info *hw, u32 hash,
                                 __le16 perfect_match, bool is_double);
        void (*enable_vlan)(struct mac_device_info *hw, u32 type);
+       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 */
        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_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...) \