Commit 0b6f65c7 authored by Joakim Zhang's avatar Joakim Zhang Committed by David S. Miller

net: fec: fix system hang during suspend/resume

1. During normal suspend (WoL not enabled) process, system has posibility
to hang. The root cause is TXF interrupt coming after clocks disabled,
system hang when accessing registers from interrupt handler. To fix this
issue, disable all interrupts when system suspend.

2. System also has posibility to hang with WoL enabled during suspend,
after entering stop mode, then magic pattern coming after clocks
disabled, system will be waked up, and interrupt handler will be called,
system hang when access registers. To fix this issue, disable wakeup
irq in .suspend(), and enable it in .resume().
Signed-off-by: default avatarJoakim Zhang <qiangqing.zhang@nxp.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 84386995
...@@ -1185,6 +1185,21 @@ static void fec_enet_stop_mode(struct fec_enet_private *fep, bool enabled) ...@@ -1185,6 +1185,21 @@ static void fec_enet_stop_mode(struct fec_enet_private *fep, bool enabled)
} }
} }
static void fec_irqs_disable(struct net_device *ndev)
{
struct fec_enet_private *fep = netdev_priv(ndev);
writel(0, fep->hwp + FEC_IMASK);
}
static void fec_irqs_disable_except_wakeup(struct net_device *ndev)
{
struct fec_enet_private *fep = netdev_priv(ndev);
writel(0, fep->hwp + FEC_IMASK);
writel(FEC_ENET_WAKEUP, fep->hwp + FEC_IMASK);
}
static void static void
fec_stop(struct net_device *ndev) fec_stop(struct net_device *ndev)
{ {
...@@ -1211,15 +1226,13 @@ fec_stop(struct net_device *ndev) ...@@ -1211,15 +1226,13 @@ fec_stop(struct net_device *ndev)
writel(1, fep->hwp + FEC_ECNTRL); writel(1, fep->hwp + FEC_ECNTRL);
udelay(10); udelay(10);
} }
writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK);
} else { } else {
writel(FEC_DEFAULT_IMASK | FEC_ENET_WAKEUP, fep->hwp + FEC_IMASK);
val = readl(fep->hwp + FEC_ECNTRL); val = readl(fep->hwp + FEC_ECNTRL);
val |= (FEC_ECR_MAGICEN | FEC_ECR_SLEEP); val |= (FEC_ECR_MAGICEN | FEC_ECR_SLEEP);
writel(val, fep->hwp + FEC_ECNTRL); writel(val, fep->hwp + FEC_ECNTRL);
fec_enet_stop_mode(fep, true);
} }
writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED); writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED);
writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK);
/* We have to keep ENET enabled to have MII interrupt stay working */ /* We have to keep ENET enabled to have MII interrupt stay working */
if (fep->quirks & FEC_QUIRK_ENET_MAC && if (fep->quirks & FEC_QUIRK_ENET_MAC &&
...@@ -2877,15 +2890,10 @@ fec_enet_set_wol(struct net_device *ndev, struct ethtool_wolinfo *wol) ...@@ -2877,15 +2890,10 @@ fec_enet_set_wol(struct net_device *ndev, struct ethtool_wolinfo *wol)
return -EINVAL; return -EINVAL;
device_set_wakeup_enable(&ndev->dev, wol->wolopts & WAKE_MAGIC); device_set_wakeup_enable(&ndev->dev, wol->wolopts & WAKE_MAGIC);
if (device_may_wakeup(&ndev->dev)) { if (device_may_wakeup(&ndev->dev))
fep->wol_flag |= FEC_WOL_FLAG_ENABLE; fep->wol_flag |= FEC_WOL_FLAG_ENABLE;
if (fep->wake_irq > 0) else
enable_irq_wake(fep->wake_irq);
} else {
fep->wol_flag &= (~FEC_WOL_FLAG_ENABLE); fep->wol_flag &= (~FEC_WOL_FLAG_ENABLE);
if (fep->wake_irq > 0)
disable_irq_wake(fep->wake_irq);
}
return 0; return 0;
} }
...@@ -4057,9 +4065,19 @@ static int __maybe_unused fec_suspend(struct device *dev) ...@@ -4057,9 +4065,19 @@ static int __maybe_unused fec_suspend(struct device *dev)
netif_device_detach(ndev); netif_device_detach(ndev);
netif_tx_unlock_bh(ndev); netif_tx_unlock_bh(ndev);
fec_stop(ndev); fec_stop(ndev);
fec_enet_clk_enable(ndev, false); if (!(fep->wol_flag & FEC_WOL_FLAG_ENABLE)) {
if (!(fep->wol_flag & FEC_WOL_FLAG_ENABLE)) fec_irqs_disable(ndev);
pinctrl_pm_select_sleep_state(&fep->pdev->dev); pinctrl_pm_select_sleep_state(&fep->pdev->dev);
} else {
fec_irqs_disable_except_wakeup(ndev);
if (fep->wake_irq > 0) {
disable_irq(fep->wake_irq);
enable_irq_wake(fep->wake_irq);
}
fec_enet_stop_mode(fep, true);
}
/* It's safe to disable clocks since interrupts are masked */
fec_enet_clk_enable(ndev, false);
} }
rtnl_unlock(); rtnl_unlock();
...@@ -4097,6 +4115,10 @@ static int __maybe_unused fec_resume(struct device *dev) ...@@ -4097,6 +4115,10 @@ static int __maybe_unused fec_resume(struct device *dev)
} }
if (fep->wol_flag & FEC_WOL_FLAG_ENABLE) { if (fep->wol_flag & FEC_WOL_FLAG_ENABLE) {
fec_enet_stop_mode(fep, false); fec_enet_stop_mode(fep, false);
if (fep->wake_irq) {
disable_irq_wake(fep->wake_irq);
enable_irq(fep->wake_irq);
}
val = readl(fep->hwp + FEC_ECNTRL); val = readl(fep->hwp + FEC_ECNTRL);
val &= ~(FEC_ECR_MAGICEN | FEC_ECR_SLEEP); val &= ~(FEC_ECR_MAGICEN | FEC_ECR_SLEEP);
......
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