summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac.h1
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_main.c53
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_vlan.c60
3 files changed, 79 insertions, 35 deletions
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h
index 51c96a738151..33667a26708c 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h
@@ -323,6 +323,7 @@ struct stmmac_priv {
void __iomem *ptpaddr;
void __iomem *estaddr;
unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)];
+ unsigned int num_double_vlans;
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 e00aa42a1961..6827c99bde8c 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -156,6 +156,7 @@ static void stmmac_tx_timer_arm(struct stmmac_priv *priv, u32 queue);
static void stmmac_flush_tx_descriptors(struct stmmac_priv *priv, int queue);
static void stmmac_set_dma_operation_mode(struct stmmac_priv *priv, u32 txmode,
u32 rxmode, u32 chan);
+static int stmmac_vlan_restore(struct stmmac_priv *priv);
#ifdef CONFIG_DEBUG_FS
static const struct net_device_ops stmmac_netdev_ops;
@@ -4107,6 +4108,8 @@ static int __stmmac_open(struct net_device *dev,
phylink_start(priv->phylink);
+ stmmac_vlan_restore(priv);
+
ret = stmmac_request_irq(dev);
if (ret)
goto irq_error;
@@ -6766,6 +6769,9 @@ static int stmmac_vlan_update(struct stmmac_priv *priv, bool is_double)
hash = 0;
}
+ if (!netif_running(priv->dev))
+ return 0;
+
return stmmac_update_vlan_hash(priv, priv->hw, hash, pmatch, is_double);
}
@@ -6775,6 +6781,7 @@ static int stmmac_vlan_update(struct stmmac_priv *priv, bool is_double)
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_double_vlans;
bool is_double = false;
int ret;
@@ -6786,7 +6793,8 @@ static int stmmac_vlan_rx_add_vid(struct net_device *ndev, __be16 proto, u16 vid
is_double = true;
set_bit(vid, priv->active_vlans);
- ret = stmmac_vlan_update(priv, is_double);
+ num_double_vlans = priv->num_double_vlans + is_double;
+ ret = stmmac_vlan_update(priv, num_double_vlans);
if (ret) {
clear_bit(vid, priv->active_vlans);
goto err_pm_put;
@@ -6794,9 +6802,15 @@ static int stmmac_vlan_rx_add_vid(struct net_device *ndev, __be16 proto, u16 vid
if (priv->hw->num_vlan) {
ret = stmmac_add_hw_vlan_rx_fltr(priv, ndev, priv->hw, proto, vid);
- if (ret)
+ if (ret) {
+ clear_bit(vid, priv->active_vlans);
+ stmmac_vlan_update(priv, priv->num_double_vlans);
goto err_pm_put;
+ }
}
+
+ priv->num_double_vlans = num_double_vlans;
+
err_pm_put:
pm_runtime_put(priv->device);
@@ -6809,6 +6823,7 @@ 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_double_vlans;
bool is_double = false;
int ret;
@@ -6820,14 +6835,23 @@ static int stmmac_vlan_rx_kill_vid(struct net_device *ndev, __be16 proto, u16 vi
is_double = true;
clear_bit(vid, priv->active_vlans);
+ num_double_vlans = priv->num_double_vlans - is_double;
+ ret = stmmac_vlan_update(priv, num_double_vlans);
+ if (ret) {
+ set_bit(vid, priv->active_vlans);
+ goto del_vlan_error;
+ }
if (priv->hw->num_vlan) {
ret = stmmac_del_hw_vlan_rx_fltr(priv, ndev, priv->hw, proto, vid);
- if (ret)
+ if (ret) {
+ set_bit(vid, priv->active_vlans);
+ stmmac_vlan_update(priv, priv->num_double_vlans);
goto del_vlan_error;
+ }
}
- ret = stmmac_vlan_update(priv, is_double);
+ priv->num_double_vlans = num_double_vlans;
del_vlan_error:
pm_runtime_put(priv->device);
@@ -6835,6 +6859,23 @@ del_vlan_error:
return ret;
}
+static int stmmac_vlan_restore(struct stmmac_priv *priv)
+{
+ int ret;
+
+ if (!(priv->dev->features & NETIF_F_VLAN_FEATURES))
+ return 0;
+
+ if (priv->hw->num_vlan)
+ stmmac_restore_hw_vlan_rx_fltr(priv, priv->dev, priv->hw);
+
+ ret = stmmac_vlan_update(priv, priv->num_double_vlans);
+ if (ret)
+ netdev_err(priv->dev, "Failed to restore VLANs\n");
+
+ return ret;
+}
+
static int stmmac_bpf(struct net_device *dev, struct netdev_bpf *bpf)
{
struct stmmac_priv *priv = netdev_priv(dev);
@@ -8259,10 +8300,10 @@ int stmmac_resume(struct device *dev)
stmmac_init_coalesce(priv);
phylink_rx_clk_stop_block(priv->phylink);
stmmac_set_rx_mode(ndev);
-
- stmmac_restore_hw_vlan_rx_fltr(priv, ndev, priv->hw);
phylink_rx_clk_stop_unblock(priv->phylink);
+ stmmac_vlan_restore(priv);
+
stmmac_enable_all_queues(priv);
stmmac_enable_all_dma_irq(priv);
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_vlan.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_vlan.c
index b18404dd5a8b..e24efe3bfedb 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_vlan.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_vlan.c
@@ -76,7 +76,9 @@ static int vlan_add_hw_rx_fltr(struct net_device *dev,
}
hw->vlan_filter[0] = vid;
- vlan_write_single(dev, vid);
+
+ if (netif_running(dev))
+ vlan_write_single(dev, vid);
return 0;
}
@@ -97,12 +99,15 @@ static int vlan_add_hw_rx_fltr(struct net_device *dev,
return -EPERM;
}
- ret = vlan_write_filter(dev, hw, index, val);
+ if (netif_running(dev)) {
+ ret = vlan_write_filter(dev, hw, index, val);
+ if (ret)
+ return ret;
+ }
- if (!ret)
- hw->vlan_filter[index] = val;
+ hw->vlan_filter[index] = val;
- return ret;
+ return 0;
}
static int vlan_del_hw_rx_fltr(struct net_device *dev,
@@ -115,7 +120,9 @@ static int vlan_del_hw_rx_fltr(struct net_device *dev,
if (hw->num_vlan == 1) {
if ((hw->vlan_filter[0] & VLAN_TAG_VID) == vid) {
hw->vlan_filter[0] = 0;
- vlan_write_single(dev, 0);
+
+ if (netif_running(dev))
+ vlan_write_single(dev, 0);
}
return 0;
}
@@ -124,25 +131,23 @@ static int vlan_del_hw_rx_fltr(struct net_device *dev,
for (i = 0; i < hw->num_vlan; i++) {
if ((hw->vlan_filter[i] & VLAN_TAG_DATA_VEN) &&
((hw->vlan_filter[i] & VLAN_TAG_DATA_VID) == vid)) {
- ret = vlan_write_filter(dev, hw, i, 0);
- if (!ret)
- hw->vlan_filter[i] = 0;
- else
- return ret;
+ if (netif_running(dev)) {
+ ret = vlan_write_filter(dev, hw, i, 0);
+ if (ret)
+ return ret;
+ }
+
+ hw->vlan_filter[i] = 0;
}
}
- return ret;
+ return 0;
}
static void vlan_restore_hw_rx_fltr(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 */
@@ -152,19 +157,8 @@ static void vlan_restore_hw_rx_fltr(struct net_device *dev,
}
/* Extended Rx VLAN Filter Enable */
- for (i = 0; i < hw->num_vlan; i++) {
- if (hw->vlan_filter[i] & VLAN_TAG_DATA_VEN) {
- val = hw->vlan_filter[i];
- vlan_write_filter(dev, hw, i, val);
- }
- }
-
- hash = readl(ioaddr + VLAN_HASH_TABLE);
- if (hash & VLAN_VLHT) {
- value = readl(ioaddr + VLAN_TAG);
- value |= VLAN_VTHM;
- writel(value, ioaddr + VLAN_TAG);
- }
+ for (i = 0; i < hw->num_vlan; i++)
+ vlan_write_filter(dev, hw, i, hw->vlan_filter[i]);
}
static void vlan_update_hash(struct mac_device_info *hw, u32 hash,
@@ -183,6 +177,10 @@ static void vlan_update_hash(struct mac_device_info *hw, u32 hash,
value |= VLAN_EDVLP;
value |= VLAN_ESVL;
value |= VLAN_DOVLTC;
+ } else {
+ value &= ~VLAN_EDVLP;
+ value &= ~VLAN_ESVL;
+ value &= ~VLAN_DOVLTC;
}
writel(value, ioaddr + VLAN_TAG);
@@ -193,6 +191,10 @@ static void vlan_update_hash(struct mac_device_info *hw, u32 hash,
value |= VLAN_EDVLP;
value |= VLAN_ESVL;
value |= VLAN_DOVLTC;
+ } else {
+ value &= ~VLAN_EDVLP;
+ value &= ~VLAN_ESVL;
+ value &= ~VLAN_DOVLTC;
}
writel(value | perfect_match, ioaddr + VLAN_TAG);