Commit 59b6d2e5 authored by Florian Fainelli's avatar Florian Fainelli Committed by Greg Kroah-Hartman

net: systemport: Fix wake-up interrupt race during resume

[ Upstream commit 45ec318578c0c22a11f5b9927d064418e1ab1905 ]

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: 83e82f4c ("net: systemport: add Wake-on-LAN support")
Signed-off-by: default avatarFlorian Fainelli <f.fainelli@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 2b2c9306
......@@ -850,14 +850,22 @@ static void bcm_sysport_resume_from_wol(struct bcm_sysport_priv *priv)
{
u32 reg;
/* Stop monitoring MPD interrupt */
intrl2_0_mask_set(priv, INTRL2_0_MPD);
/* Clear the MagicPacket detection logic */
reg = umac_readl(priv, UMAC_MPD_CTRL);
reg &= ~MPD_EN;
umac_writel(priv, reg, UMAC_MPD_CTRL);
reg = intrl2_0_readl(priv, INTRL2_CPU_STATUS);
if (reg & INTRL2_0_MPD)
netdev_info(priv->netdev, "Wake-on-LAN (MPD) interrupt!\n");
if (reg & INTRL2_0_BRCM_MATCH_TAG) {
reg = rxchk_readl(priv, RXCHK_BRCM_TAG_MATCH_STATUS) &
RXCHK_BRCM_TAG_MATCH_MASK;
netdev_info(priv->netdev,
"Wake-on-LAN (filters 0x%02x) interrupt!\n", reg);
}
netif_dbg(priv, wol, priv->netdev, "resumed from WOL\n");
}
......@@ -890,11 +898,6 @@ static irqreturn_t bcm_sysport_rx_isr(int irq, void *dev_id)
if (priv->irq0_stat & INTRL2_0_TX_RING_FULL)
bcm_sysport_tx_reclaim_all(priv);
if (priv->irq0_stat & INTRL2_0_MPD) {
netdev_info(priv->netdev, "Wake-on-LAN interrupt!\n");
bcm_sysport_resume_from_wol(priv);
}
return IRQ_HANDLED;
}
......@@ -1915,9 +1918,6 @@ static int bcm_sysport_suspend_to_wol(struct bcm_sysport_priv *priv)
/* UniMAC receive needs to be turned on */
umac_enable_set(priv, CMD_RX_EN, 1);
/* Enable the interrupt wake-up source */
intrl2_0_mask_clear(priv, INTRL2_0_MPD);
netif_dbg(priv, wol, ndev, "entered WOL mode\n");
return 0;
......
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