aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net
diff options
context:
space:
mode:
authorFlorian Fainelli <f.fainelli@gmail.com>2018-10-02 19:52:03 -0400
committerDavid S. Miller <davem@davemloft.net>2018-10-02 20:34:47 -0400
commit45ec318578c0c22a11f5b9927d064418e1ab1905 (patch)
tree4888562d60c9b35ea9086a22d89158f41cbfdca8 /drivers/net
parent11bde899817b575f6e9f22f35ec7e3296cd466b4 (diff)
net: systemport: Fix wake-up interrupt race during resume
The AON_PM_L2 is normally used to trigger and identify the source of a wake-up event. Since the RX_SYS clock is no longer turned off, we also have an interrupt being sent to the SYSTEMPORT INTRL_2_0 controller, and that interrupt remains active up until the magic packet detector is disabled which happens much later during the driver resumption. The race happens if we have a CPU that is entering the SYSTEMPORT INTRL2_0 handler during resume, and another CPU has managed to clear the wake-up interrupt during bcm_sysport_resume_from_wol(). In that case, we have the first CPU stuck in the interrupt handler with an interrupt cause that has been cleared under its feet, and so we keep returning IRQ_NONE and we never make any progress. This was not a problem before because we would always turn off the RX_SYS clock during WoL, so the SYSTEMPORT INTRL2_0 would also be turned off as well, thus not latching the interrupt. The fix is to make sure we do not enable either the MPD or BRCM_TAG_MATCH interrupts since those are redundant with what the AON_PM_L2 interrupt controller already processes and they would cause such a race to occur. Fixes: bb9051a2b230 ("net: systemport: Add support for WAKE_FILTER") Fixes: 83e82f4c706b ("net: systemport: add Wake-on-LAN support") Signed-off-by: Florian Fainelli <f.fainelli@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net')
-rw-r--r--drivers/net/ethernet/broadcom/bcmsysport.c28
1 files changed, 11 insertions, 17 deletions
diff --git a/drivers/net/ethernet/broadcom/bcmsysport.c b/drivers/net/ethernet/broadcom/bcmsysport.c
index 147045757b10..c57238fce863 100644
--- a/drivers/net/ethernet/broadcom/bcmsysport.c
+++ b/drivers/net/ethernet/broadcom/bcmsysport.c
@@ -1069,9 +1069,6 @@ static void bcm_sysport_resume_from_wol(struct bcm_sysport_priv *priv)
1069{ 1069{
1070 u32 reg; 1070 u32 reg;
1071 1071
1072 /* Stop monitoring MPD interrupt */
1073 intrl2_0_mask_set(priv, INTRL2_0_MPD | INTRL2_0_BRCM_MATCH_TAG);
1074
1075 /* Disable RXCHK, active filters and Broadcom tag matching */ 1072 /* Disable RXCHK, active filters and Broadcom tag matching */
1076 reg = rxchk_readl(priv, RXCHK_CONTROL); 1073 reg = rxchk_readl(priv, RXCHK_CONTROL);
1077 reg &= ~(RXCHK_BRCM_TAG_MATCH_MASK << 1074 reg &= ~(RXCHK_BRCM_TAG_MATCH_MASK <<
@@ -1081,6 +1078,17 @@ static void bcm_sysport_resume_from_wol(struct bcm_sysport_priv *priv)
1081 /* Clear the MagicPacket detection logic */ 1078 /* Clear the MagicPacket detection logic */
1082 mpd_enable_set(priv, false); 1079 mpd_enable_set(priv, false);
1083 1080
1081 reg = intrl2_0_readl(priv, INTRL2_CPU_STATUS);
1082 if (reg & INTRL2_0_MPD)
1083 netdev_info(priv->netdev, "Wake-on-LAN (MPD) interrupt!\n");
1084
1085 if (reg & INTRL2_0_BRCM_MATCH_TAG) {
1086 reg = rxchk_readl(priv, RXCHK_BRCM_TAG_MATCH_STATUS) &
1087 RXCHK_BRCM_TAG_MATCH_MASK;
1088 netdev_info(priv->netdev,
1089 "Wake-on-LAN (filters 0x%02x) interrupt!\n", reg);
1090 }
1091
1084 netif_dbg(priv, wol, priv->netdev, "resumed from WOL\n"); 1092 netif_dbg(priv, wol, priv->netdev, "resumed from WOL\n");
1085} 1093}
1086 1094
@@ -1105,7 +1113,6 @@ static irqreturn_t bcm_sysport_rx_isr(int irq, void *dev_id)
1105 struct bcm_sysport_priv *priv = netdev_priv(dev); 1113 struct bcm_sysport_priv *priv = netdev_priv(dev);
1106 struct bcm_sysport_tx_ring *txr; 1114 struct bcm_sysport_tx_ring *txr;
1107 unsigned int ring, ring_bit; 1115 unsigned int ring, ring_bit;
1108 u32 reg;
1109 1116
1110 priv->irq0_stat = intrl2_0_readl(priv, INTRL2_CPU_STATUS) & 1117 priv->irq0_stat = intrl2_0_readl(priv, INTRL2_CPU_STATUS) &
1111 ~intrl2_0_readl(priv, INTRL2_CPU_MASK_STATUS); 1118 ~intrl2_0_readl(priv, INTRL2_CPU_MASK_STATUS);
@@ -1131,16 +1138,6 @@ static irqreturn_t bcm_sysport_rx_isr(int irq, void *dev_id)
1131 if (priv->irq0_stat & INTRL2_0_TX_RING_FULL) 1138 if (priv->irq0_stat & INTRL2_0_TX_RING_FULL)
1132 bcm_sysport_tx_reclaim_all(priv); 1139 bcm_sysport_tx_reclaim_all(priv);
1133 1140
1134 if (priv->irq0_stat & INTRL2_0_MPD)
1135 netdev_info(priv->netdev, "Wake-on-LAN (MPD) interrupt!\n");
1136
1137 if (priv->irq0_stat & INTRL2_0_BRCM_MATCH_TAG) {
1138 reg = rxchk_readl(priv, RXCHK_BRCM_TAG_MATCH_STATUS) &
1139 RXCHK_BRCM_TAG_MATCH_MASK;
1140 netdev_info(priv->netdev,
1141 "Wake-on-LAN (filters 0x%02x) interrupt!\n", reg);
1142 }
1143
1144 if (!priv->is_lite) 1141 if (!priv->is_lite)
1145 goto out; 1142 goto out;
1146 1143
@@ -2641,9 +2638,6 @@ static int bcm_sysport_suspend_to_wol(struct bcm_sysport_priv *priv)
2641 /* UniMAC receive needs to be turned on */ 2638 /* UniMAC receive needs to be turned on */
2642 umac_enable_set(priv, CMD_RX_EN, 1); 2639 umac_enable_set(priv, CMD_RX_EN, 1);
2643 2640
2644 /* Enable the interrupt wake-up source */
2645 intrl2_0_mask_clear(priv, INTRL2_0_MPD | INTRL2_0_BRCM_MATCH_TAG);
2646
2647 netif_dbg(priv, wol, ndev, "entered WOL mode\n"); 2641 netif_dbg(priv, wol, ndev, "entered WOL mode\n");
2648 2642
2649 return 0; 2643 return 0;