diff options
| -rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/common.h | 3 | ||||
| -rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/hwif.h | 2 | ||||
| -rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/stmmac.h | 6 | ||||
| -rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 90 | ||||
| -rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/stmmac_vlan.c | 44 | ||||
| -rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/stmmac_vlan.h | 1 |
6 files changed, 84 insertions, 62 deletions
diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h index dff7a12047a2..4a811516474b 100644 --- a/drivers/net/ethernet/stmicro/stmmac/common.h +++ b/drivers/net/ethernet/stmicro/stmmac/common.h @@ -657,7 +657,8 @@ struct mac_device_info { unsigned int xlgmac; unsigned int num_vlan; u16 vlan_hash; - bool vlan_stag; + int vlan_ctags; + int vlan_stags; u32 vlan_filter[32]; bool vlan_fail_q_en; u8 vlan_fail_q; diff --git a/drivers/net/ethernet/stmicro/stmmac/hwif.h b/drivers/net/ethernet/stmicro/stmmac/hwif.h index a46e819aec29..b8fd4b527393 100644 --- a/drivers/net/ethernet/stmicro/stmmac/hwif.h +++ b/drivers/net/ethernet/stmicro/stmmac/hwif.h @@ -629,7 +629,7 @@ struct stmmac_est_ops { struct stmmac_vlan_ops { /* VLAN */ void (*update_vlan_hash)(struct mac_device_info *hw, u32 hash, - bool is_svlan); + int add_ctags, int add_stags); 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, bool rx_strip, diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h index 7ac58e01d846..2053f62c67f2 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h @@ -375,8 +375,10 @@ struct stmmac_priv { void __iomem *mmcaddr; void __iomem *ptpaddr; void __iomem *estaddr; - unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)]; - unsigned int num_svlans; + + DECLARE_BITMAP(active_cvlans, VLAN_N_VID); + DECLARE_BITMAP(active_svlans, VLAN_N_VID); + int sfty_irq; int sfty_ce_irq; int sfty_ue_irq; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 9dad81d3ffea..d2006d90e7e9 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -7144,18 +7144,56 @@ static u32 stmmac_vid_crc32_le(__le16 vid_le) return crc; } -static int stmmac_vlan_update(struct stmmac_priv *priv, bool is_svlan) +static int stmmac_update_hw_vlan_rx_hash(struct stmmac_priv *priv, bool add, + __be16 proto, u16 vid) { + int add_ctags = 0, add_stags = 0; + unsigned long *active_vlans; u32 crc, hash = 0; - u16 vid = 0; + u16 vid_cpu; + int ret; + + if (proto == htons(ETH_P_8021Q)) { + active_vlans = priv->active_cvlans; + add_ctags = add ? 1 : -1; + } else { + active_vlans = priv->active_svlans; + add_stags = add ? 1 : -1; + } + + if (add) + ret = test_and_set_bit(vid, active_vlans); + else + ret = test_and_clear_bit(vid, active_vlans); + + if (ret == add) + return 0; - for_each_set_bit(vid, priv->active_vlans, VLAN_N_VID) { + for_each_set_bit(vid, priv->active_cvlans, VLAN_N_VID) { __le16 vid_le = cpu_to_le16(vid); crc = bitrev32(~stmmac_vid_crc32_le(vid_le)) >> 28; hash |= (1 << crc); } - return stmmac_update_vlan_hash(priv, priv->hw, hash, is_svlan); + for_each_set_bit(vid_cpu, priv->active_svlans, VLAN_N_VID) { + __le16 vid_le = cpu_to_le16(vid_cpu); + crc = bitrev32(~stmmac_vid_crc32_le(vid_le)) >> 28; + hash |= (1 << crc); + } + + ret = stmmac_update_vlan_hash(priv, priv->hw, hash, add_ctags, add_stags); + if (ret) + goto err_revert_bit; + + return 0; + +err_revert_bit: + if (add) + clear_bit(vid, active_vlans); + else + set_bit(vid, active_vlans); + + return ret; } /* FIXME: This may need RXC to be running, but it may be called with BH @@ -7164,33 +7202,19 @@ static int stmmac_vlan_update(struct stmmac_priv *priv, bool is_svlan) static int stmmac_vlan_rx_add_vid(struct net_device *ndev, __be16 proto, u16 vid) { struct stmmac_priv *priv = netdev_priv(ndev); - unsigned int num_svlans; - bool is_svlan = false; int ret; ret = pm_runtime_resume_and_get(priv->device); if (ret < 0) return ret; - if (be16_to_cpu(proto) == ETH_P_8021AD) - is_svlan = true; - - set_bit(vid, priv->active_vlans); - num_svlans = priv->num_svlans + is_svlan; - ret = stmmac_vlan_update(priv, num_svlans); - if (ret) { - clear_bit(vid, priv->active_vlans); + ret = stmmac_update_hw_vlan_rx_hash(priv, true, proto, vid); + if (ret) goto err_pm_put; - } ret = stmmac_add_hw_vlan_rx_fltr(priv, ndev, priv->hw, proto, vid); - if (ret && ret != -EINVAL) { - clear_bit(vid, priv->active_vlans); - stmmac_vlan_update(priv, priv->num_svlans); - goto err_pm_put; - } - - priv->num_svlans = num_svlans; + if (ret && ret != -EINVAL) + stmmac_update_hw_vlan_rx_hash(priv, false, proto, vid); err_pm_put: pm_runtime_put(priv->device); @@ -7204,33 +7228,19 @@ err_pm_put: static int stmmac_vlan_rx_kill_vid(struct net_device *ndev, __be16 proto, u16 vid) { struct stmmac_priv *priv = netdev_priv(ndev); - unsigned int num_svlans; - bool is_svlan = false; int ret; ret = pm_runtime_resume_and_get(priv->device); if (ret < 0) return ret; - if (be16_to_cpu(proto) == ETH_P_8021AD) - is_svlan = true; - - clear_bit(vid, priv->active_vlans); - num_svlans = priv->num_svlans - is_double; - ret = stmmac_vlan_update(priv, num_svlans); - if (ret) { - set_bit(vid, priv->active_vlans); - goto del_vlan_error; - } - ret = stmmac_del_hw_vlan_rx_fltr(priv, ndev, priv->hw, proto, vid); - if (ret && ret != -EINVAL) { - set_bit(vid, priv->active_vlans); - stmmac_vlan_update(priv, priv->num_svlans); + if (ret && ret != -EINVAL) goto del_vlan_error; - } - priv->num_svlans = num_svlans; + ret = stmmac_update_hw_vlan_rx_hash(priv, false, proto, vid); + if (ret) + stmmac_add_hw_vlan_rx_fltr(priv, ndev, priv->hw, proto, vid); del_vlan_error: pm_runtime_put(priv->device); diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_vlan.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_vlan.c index 64270920de91..b6d4b2c7a2d3 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_vlan.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_vlan.c @@ -7,17 +7,19 @@ #include "stmmac.h" #include "stmmac_vlan.h" +static void vlan_update_hash(struct mac_device_info *hw, u32 hash, + int add_ctags, int add_stags); + static void vlan_write_single(struct net_device *dev, u32 data) { void __iomem *ioaddr = (void __iomem *)dev->base_addr; - u32 val, mask; + u32 val; - mask = VLAN_TAG_ERSVLM | VLAN_TAG_VID; if (!(data & VLAN_TAG_VID)) data |= VLAN_TAG_VID; - val = readl(ioaddr + VLAN_TAG) & ~mask; - val |= VLAN_TAG_ETV | (data & mask); + val = readl(ioaddr + VLAN_TAG) & ~VLAN_TAG_VID; + val |= VLAN_TAG_ETV | (data & VLAN_TAG_VID); writel(val, ioaddr + VLAN_TAG); } @@ -96,6 +98,8 @@ static int vlan_add_hw_rx_fltr(struct net_device *dev, return -EPERM; } + vlan_update_hash(hw, hw->vlan_hash, !is_stag, is_stag); + vlan_write_single(dev, val); hw->vlan_filter[0] = val; @@ -147,6 +151,8 @@ static int vlan_del_hw_rx_fltr(struct net_device *dev, if (is_stag != !!(hw->vlan_filter[0] & VLAN_TAG_ERSVLM)) return 0; + vlan_update_hash(hw, hw->vlan_hash, -!is_stag, -is_stag); + vlan_write_single(dev, 0); hw->vlan_filter[0] = 0; @@ -176,7 +182,7 @@ static int vlan_del_hw_rx_fltr(struct net_device *dev, } static void vlan_update_hash(struct mac_device_info *hw, u32 hash, - bool is_svlan) + int add_ctags, int add_stags) { void __iomem *ioaddr = hw->pcsr; u32 value; @@ -184,21 +190,23 @@ static void vlan_update_hash(struct mac_device_info *hw, u32 hash, hw->vlan_hash = hash; writel(hash, ioaddr + VLAN_HASH_TABLE); - value = readl(ioaddr + VLAN_TAG); + hw->vlan_ctags += add_ctags; + hw->vlan_stags += add_stags; + value = readl(ioaddr + VLAN_TAG) | VLAN_ETV; - if (hash) { - value |= VLAN_VTHM | VLAN_ETV; - if (is_svlan) { - hw->vlan_stag = true; - value |= VLAN_DOVLTC; - } else { - hw->vlan_stag = false; - value &= ~VLAN_DOVLTC; - } + if (hash) + value |= VLAN_VTHM; + else + value &= ~VLAN_VTHM; + + if (hw->vlan_ctags && hw->vlan_stags) { + value |= VLAN_DOVLTC; + } else if (hw->vlan_stags) { + value &= ~VLAN_DOVLTC; + value |= VLAN_ERSVLM; } else { - hw->vlan_stag = false; - value &= ~(VLAN_VTHM | VLAN_ETV); value &= ~VLAN_DOVLTC; + value &= ~VLAN_ERSVLM; } if (!hw->vlan_filter[0]) @@ -213,7 +221,7 @@ static void vlan_restore_hw_rx_fltr(struct net_device *dev, int i; /* Hash-based Rx VLAN Filter */ - vlan_update_hash(hw, hw->vlan_hash, hw->vlan_stag); + vlan_update_hash(hw, hw->vlan_hash, 0, 0); /* Single Rx VLAN Filter */ if (hw->num_vlan == 1) { diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_vlan.h b/drivers/net/ethernet/stmicro/stmmac/stmmac_vlan.h index 3ee0022282f1..e38f570e3297 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_vlan.h +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_vlan.h @@ -19,6 +19,7 @@ #define VLAN_EDVLP BIT(26) #define VLAN_VTHM BIT(25) #define VLAN_DOVLTC BIT(20) +#define VLAN_ERSVLM BIT(19) #define VLAN_ESVL BIT(18) #define VLAN_ETV BIT(16) #define VLAN_VID GENMASK(15, 0) |
