net: stmmac: Add support for VLAN promiscuous mode
authorChuah, Kim Tatt <kim.tatt.chuah@intel.com>
Wed, 22 Apr 2020 03:31:06 +0000 (11:31 +0800)
committerDavid S. Miller <davem@davemloft.net>
Thu, 23 Apr 2020 02:35:48 +0000 (19:35 -0700)
For dwmac4, enable VLAN promiscuity when MAC controller is requested to
enter promiscuous mode.

Signed-off-by: Chuah, Kim Tatt <kim.tatt.chuah@intel.com>
Signed-off-by: Ong Boon Leong <boon.leong.ong@intel.com>
Signed-off-by: Tan, Tee Min <tee.min.tan@intel.com>
Signed-off-by: Wong Vee Khee <vee.khee.wong@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/stmicro/stmmac/common.h
drivers/net/ethernet/stmicro/stmmac/dwmac4.h
drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c

index 6208a68a331d39bcef49438768eca7081410c849..127f7586296292a2cc03302d3f0559247f7bc64c 100644 (file)
@@ -473,6 +473,7 @@ struct mac_device_info {
        unsigned int xlgmac;
        unsigned int num_vlan;
        u32 vlan_filter[32];
+       unsigned int promisc;
 };
 
 struct stmmac_rx_routing {
index 28cac28253b8806adfe481eddf05d6687074bca6..61f3249bd724bc83f1ca74c98be3324876d1340e 100644 (file)
@@ -90,6 +90,7 @@
 #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)
index 39692d15d80cea541c0a65388f0e01b009460a4d..ecd834e0e12160dd2203cd4b6bede934804e8056 100644 (file)
@@ -450,6 +450,12 @@ static int dwmac4_add_hw_vlan_rx_fltr(struct net_device *dev,
        if (vid > 4095)
                return -EINVAL;
 
+       if (hw->promisc) {
+               netdev_err(dev,
+                          "Adding VLAN in promisc mode not supported\n");
+               return -EPERM;
+       }
+
        /* Single Rx VLAN Filter */
        if (hw->num_vlan == 1) {
                /* For single VLAN filter, VID 0 means VLAN promiscuous */
@@ -499,6 +505,12 @@ static int dwmac4_del_hw_vlan_rx_fltr(struct net_device *dev,
 {
        int i, ret = 0;
 
+       if (hw->promisc) {
+               netdev_err(dev,
+                          "Deleting VLAN in promisc mode not supported\n");
+               return -EPERM;
+       }
+
        /* Single Rx VLAN Filter */
        if (hw->num_vlan == 1) {
                if ((hw->vlan_filter[0] & GMAC_VLAN_TAG_VID) == vid) {
@@ -523,9 +535,45 @@ static int dwmac4_del_hw_vlan_rx_fltr(struct net_device *dev,
        return ret;
 }
 
+static void dwmac4_vlan_promisc_enable(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, 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] & ~GMAC_VLAN_TAG_DATA_VEN;
+                       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);
+               if (value & GMAC_VLAN_VTHM) {
+                       value &= ~GMAC_VLAN_VTHM;
+                       writel(value, ioaddr + GMAC_VLAN_TAG);
+               }
+       }
+}
+
 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;
 
@@ -542,6 +590,13 @@ static void dwmac4_restore_hw_vlan_rx_fltr(struct net_device *dev,
                        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,
@@ -624,6 +679,18 @@ static void dwmac4_set_filter(struct mac_device_info *hw,
                value |= GMAC_PACKET_FILTER_VTFE;
 
        writel(value, ioaddr + GMAC_PACKET_FILTER);
+
+       if (dev->flags & IFF_PROMISC) {
+               if (!hw->promisc) {
+                       hw->promisc = 1;
+                       dwmac4_vlan_promisc_enable(dev, hw);
+               }
+       } else {
+               if (hw->promisc) {
+                       hw->promisc = 0;
+                       dwmac4_restore_hw_vlan_rx_fltr(dev, hw);
+               }
+       }
 }
 
 static void dwmac4_flow_ctrl(struct mac_device_info *hw, unsigned int duplex,