diff options
| author | Florian Fainelli <f.fainelli@gmail.com> | 2018-10-02 19:52:03 -0400 |
|---|---|---|
| committer | David S. Miller <davem@davemloft.net> | 2018-10-02 20:34:47 -0400 |
| commit | 45ec318578c0c22a11f5b9927d064418e1ab1905 (patch) | |
| tree | 4888562d60c9b35ea9086a22d89158f41cbfdca8 /drivers/net | |
| parent | 11bde899817b575f6e9f22f35ec7e3296cd466b4 (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.c | 28 |
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; |
