Commit c7ab0b80 authored by Jose Abreu's avatar Jose Abreu Committed by David S. Miller

net: stmmac: Fallback to VLAN Perfect filtering if HASH is not available

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: default avatarJose Abreu <joabreu@synopsys.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 04c1b4c7
...@@ -733,7 +733,7 @@ static void dwmac4_set_mac_loopback(void __iomem *ioaddr, bool enable) ...@@ -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, 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; void __iomem *ioaddr = hw->pcsr;
...@@ -748,6 +748,16 @@ static void dwmac4_update_vlan_hash(struct mac_device_info *hw, u32 hash, ...@@ -748,6 +748,16 @@ static void dwmac4_update_vlan_hash(struct mac_device_info *hw, u32 hash,
} }
writel(value, ioaddr + GMAC_VLAN_TAG); 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 { } else {
u32 value = readl(ioaddr + GMAC_VLAN_TAG); u32 value = readl(ioaddr + GMAC_VLAN_TAG);
......
...@@ -555,7 +555,7 @@ static int dwxgmac2_rss_configure(struct mac_device_info *hw, ...@@ -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, 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; void __iomem *ioaddr = hw->pcsr;
...@@ -576,6 +576,21 @@ static void dwxgmac2_update_vlan_hash(struct mac_device_info *hw, u32 hash, ...@@ -576,6 +576,21 @@ static void dwxgmac2_update_vlan_hash(struct mac_device_info *hw, u32 hash,
} }
writel(value, ioaddr + XGMAC_VLAN_TAG); 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 { } else {
u32 value = readl(ioaddr + XGMAC_PACKET_FILTER); u32 value = readl(ioaddr + XGMAC_PACKET_FILTER);
......
...@@ -357,7 +357,7 @@ struct stmmac_ops { ...@@ -357,7 +357,7 @@ struct stmmac_ops {
struct stmmac_rss *cfg, u32 num_rxq); struct stmmac_rss *cfg, u32 num_rxq);
/* VLAN */ /* VLAN */
void (*update_vlan_hash)(struct mac_device_info *hw, u32 hash, 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); void (*enable_vlan)(struct mac_device_info *hw, u32 type);
/* TX Timestamp */ /* TX Timestamp */
int (*get_mac_tx_timestamp)(struct mac_device_info *hw, u64 *ts); int (*get_mac_tx_timestamp)(struct mac_device_info *hw, u64 *ts);
......
...@@ -4207,15 +4207,25 @@ static u32 stmmac_vid_crc32_le(__le16 vid_le) ...@@ -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) static int stmmac_vlan_update(struct stmmac_priv *priv, bool is_double)
{ {
u32 crc, hash = 0; u32 crc, hash = 0;
u16 vid; int count = 0;
u16 vid = 0;
for_each_set_bit(vid, priv->active_vlans, VLAN_N_VID) { for_each_set_bit(vid, priv->active_vlans, VLAN_N_VID) {
__le16 vid_le = cpu_to_le16(vid); __le16 vid_le = cpu_to_le16(vid);
crc = bitrev32(~stmmac_vid_crc32_le(vid_le)) >> 28; crc = bitrev32(~stmmac_vid_crc32_le(vid_le)) >> 28;
hash |= (1 << crc); 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) 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 ...@@ -4224,8 +4234,6 @@ static int stmmac_vlan_rx_add_vid(struct net_device *ndev, __be16 proto, u16 vid
bool is_double = false; bool is_double = false;
int ret; int ret;
if (!priv->dma_cap.vlhash)
return -EOPNOTSUPP;
if (be16_to_cpu(proto) == ETH_P_8021AD) if (be16_to_cpu(proto) == ETH_P_8021AD)
is_double = true; is_double = true;
...@@ -4244,8 +4252,6 @@ static int stmmac_vlan_rx_kill_vid(struct net_device *ndev, __be16 proto, u16 vi ...@@ -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); struct stmmac_priv *priv = netdev_priv(ndev);
bool is_double = false; bool is_double = false;
if (!priv->dma_cap.vlhash)
return -EOPNOTSUPP;
if (be16_to_cpu(proto) == ETH_P_8021AD) if (be16_to_cpu(proto) == ETH_P_8021AD)
is_double = true; is_double = true;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment