]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
net: stmmac: Fallback to VLAN Perfect filtering if HASH is not available
authorJose Abreu <joabreu@synopsys.com>
Sun, 6 Oct 2019 11:17:12 +0000 (13:17 +0200)
committerDavid S. Miller <davem@davemloft.net>
Sun, 6 Oct 2019 16:46:31 +0000 (18:46 +0200)
If VLAN Hash Filtering is not available we can fallback to perfect
filtering instead. Let's implement this in XGMAC and GMAC cores and let
the user use this filter.

VLAN VID=0 always passes filter so we check if more than 2 VLANs are
created and return proper error code if so because perfect filtering
only supports 1 VID at a time.

Signed-off-by: Jose Abreu <joabreu@synopsys.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c
drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c
drivers/net/ethernet/stmicro/stmmac/hwif.h
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c

index 2cb9c53f93b84171d5a948dcb9f6218dacb5ae16..2eadfb124fc75e4a2b4420c1cb2c7236ee764916 100644 (file)
@@ -733,7 +733,7 @@ static void dwmac4_set_mac_loopback(void __iomem *ioaddr, bool enable)
 }
 
 static void dwmac4_update_vlan_hash(struct mac_device_info *hw, u32 hash,
-                                   bool is_double)
+                                   u16 perfect_match, bool is_double)
 {
        void __iomem *ioaddr = hw->pcsr;
 
@@ -748,6 +748,16 @@ static void dwmac4_update_vlan_hash(struct mac_device_info *hw, u32 hash,
                }
 
                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 {
                u32 value = readl(ioaddr + GMAC_VLAN_TAG);
 
index 5031398e612c241b12b99fc64c1205722ea6ea42..5cda360d5d071cc1781c4cc18bda76f92062558b 100644 (file)
@@ -555,7 +555,7 @@ static int dwxgmac2_rss_configure(struct mac_device_info *hw,
 }
 
 static void dwxgmac2_update_vlan_hash(struct mac_device_info *hw, u32 hash,
-                                     bool is_double)
+                                     u16 perfect_match, bool is_double)
 {
        void __iomem *ioaddr = hw->pcsr;
 
@@ -576,6 +576,21 @@ static void dwxgmac2_update_vlan_hash(struct mac_device_info *hw, u32 hash,
                }
 
                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 = XGMAC_VLAN_ETV;
+               if (is_double) {
+                       value |= XGMAC_VLAN_EDVLP;
+                       value |= XGMAC_VLAN_ESVL;
+                       value |= XGMAC_VLAN_DOVLTC;
+               }
+
+               writel(value | perfect_match, ioaddr + XGMAC_VLAN_TAG);
        } else {
                u32 value = readl(ioaddr + XGMAC_PACKET_FILTER);
 
index ddb851d99618d64179888fe96e0a8d65dafc0fc2..1303d1e9a18f1b6a6efcf11c4f8e5989ebcf7d1f 100644 (file)
@@ -357,7 +357,7 @@ struct stmmac_ops {
                             struct stmmac_rss *cfg, u32 num_rxq);
        /* VLAN */
        void (*update_vlan_hash)(struct mac_device_info *hw, u32 hash,
-                                bool is_double);
+                                u16 perfect_match, bool is_double);
        void (*enable_vlan)(struct mac_device_info *hw, u32 type);
        /* TX Timestamp */
        int (*get_mac_tx_timestamp)(struct mac_device_info *hw, u64 *ts);
index b8ac1744950e5fb650070f66b265d491031b2d95..8b76745a7ec4c371a3ebd63c9c28a92f9cb5968e 100644 (file)
@@ -4207,15 +4207,25 @@ static u32 stmmac_vid_crc32_le(__le16 vid_le)
 static int stmmac_vlan_update(struct stmmac_priv *priv, bool is_double)
 {
        u32 crc, hash = 0;
-       u16 vid;
+       int count = 0;
+       u16 vid = 0;
 
        for_each_set_bit(vid, priv->active_vlans, VLAN_N_VID) {
                __le16 vid_le = cpu_to_le16(vid);
                crc = bitrev32(~stmmac_vid_crc32_le(vid_le)) >> 28;
                hash |= (1 << crc);
+               count++;
+       }
+
+       if (!priv->dma_cap.vlhash) {
+               if (count > 2) /* VID = 0 always passes filter */
+                       return -EOPNOTSUPP;
+
+               vid = cpu_to_le16(vid);
+               hash = 0;
        }
 
-       return stmmac_update_vlan_hash(priv, priv->hw, hash, is_double);
+       return stmmac_update_vlan_hash(priv, priv->hw, hash, vid, is_double);
 }
 
 static int stmmac_vlan_rx_add_vid(struct net_device *ndev, __be16 proto, u16 vid)
@@ -4224,8 +4234,6 @@ static int stmmac_vlan_rx_add_vid(struct net_device *ndev, __be16 proto, u16 vid
        bool is_double = false;
        int ret;
 
-       if (!priv->dma_cap.vlhash)
-               return -EOPNOTSUPP;
        if (be16_to_cpu(proto) == ETH_P_8021AD)
                is_double = true;
 
@@ -4244,8 +4252,6 @@ static int stmmac_vlan_rx_kill_vid(struct net_device *ndev, __be16 proto, u16 vi
        struct stmmac_priv *priv = netdev_priv(ndev);
        bool is_double = false;
 
-       if (!priv->dma_cap.vlhash)
-               return -EOPNOTSUPP;
        if (be16_to_cpu(proto) == ETH_P_8021AD)
                is_double = true;