Skip to content

Commit

Permalink
gianfar: Fix suspend/resume for wol magic packet
Browse files Browse the repository at this point in the history
If we disable NAPI in the first place we can mask the device's
interrupts (and halt it) without fearing that imask may be
concurrently accessed from interrupt context, so there's
no need to do local_irq_save() around gfar_halt_nodisable().
lock_rx_qs()/unlock_tx_qs() are just obsoleted and potentially
buggy routines.  The txlock is currently used in the driver only
to manage TX congestion, it has nothing to do with halting the
device.  With these changes, the TX processing is stopped before
gfar_halt().

Compact gfar_halt() is used instead of gfar_halt_nodisable(),
as it disables Rx/TX DMA h/w blocks and the Rx/TX h/w queues.
gfar_start() re-enables all these blocks on resume.  Enabling
the magic-packet mode remains the same, note that the RX block
is re-enabled just before entering sleep mode.

Add IRQF_NO_SUSPEND flag for the error interrupt line, to signal
that the interrupt line must remain active during sleep in order
to wake the system by magic packet (MAG) reception interrupt.
(On some systems the MAG interrupt did trigger w/o this flag
as well, but on others it didn't.)

Without these fixes, when suspended during fair Tx traffic the
interface occasionally failed to be woken up by magic packet.

Signed-off-by: Claudiu Manoil <claudiu.manoil@freescale.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Claudiu Manoil authored and David S. Miller committed Jul 31, 2015
1 parent 8486830 commit 614b424
Showing 1 changed file with 30 additions and 68 deletions.
98 changes: 30 additions & 68 deletions drivers/net/ethernet/freescale/gianfar.c
Original file line number Diff line number Diff line change
Expand Up @@ -565,24 +565,6 @@ static void gfar_ints_enable(struct gfar_private *priv)
}
}

#ifdef CONFIG_PM
static void lock_tx_qs(struct gfar_private *priv)
{
int i;

for (i = 0; i < priv->num_tx_queues; i++)
spin_lock(&priv->tx_queue[i]->txlock);
}

static void unlock_tx_qs(struct gfar_private *priv)
{
int i;

for (i = 0; i < priv->num_tx_queues; i++)
spin_unlock(&priv->tx_queue[i]->txlock);
}
#endif

static int gfar_alloc_tx_queues(struct gfar_private *priv)
{
int i;
Expand Down Expand Up @@ -1542,48 +1524,37 @@ static int gfar_suspend(struct device *dev)
struct gfar_private *priv = dev_get_drvdata(dev);
struct net_device *ndev = priv->ndev;
struct gfar __iomem *regs = priv->gfargrp[0].regs;
unsigned long flags;
u32 tempval;

int magic_packet = priv->wol_en &&
(priv->device_flags &
FSL_GIANFAR_DEV_HAS_MAGIC_PACKET);

if (!netif_running(ndev))
return 0;

disable_napi(priv);
netif_tx_lock(ndev);
netif_device_detach(ndev);
netif_tx_unlock(ndev);

if (netif_running(ndev)) {
gfar_halt(priv);

local_irq_save(flags);
lock_tx_qs(priv);
if (magic_packet) {
/* Enable interrupt on Magic Packet */
gfar_write(&regs->imask, IMASK_MAG);

gfar_halt_nodisable(priv);
/* Enable Magic Packet mode */
tempval = gfar_read(&regs->maccfg2);
tempval |= MACCFG2_MPEN;
gfar_write(&regs->maccfg2, tempval);

/* Disable Tx, and Rx if wake-on-LAN is disabled. */
/* re-enable the Rx block */
tempval = gfar_read(&regs->maccfg1);

tempval &= ~MACCFG1_TX_EN;

if (!magic_packet)
tempval &= ~MACCFG1_RX_EN;

tempval |= MACCFG1_RX_EN;
gfar_write(&regs->maccfg1, tempval);

unlock_tx_qs(priv);
local_irq_restore(flags);

disable_napi(priv);

if (magic_packet) {
/* Enable interrupt on Magic Packet */
gfar_write(&regs->imask, IMASK_MAG);

/* Enable Magic Packet mode */
tempval = gfar_read(&regs->maccfg2);
tempval |= MACCFG2_MPEN;
gfar_write(&regs->maccfg2, tempval);
} else {
phy_stop(priv->phydev);
}
} else {
phy_stop(priv->phydev);
}

return 0;
Expand All @@ -1594,37 +1565,26 @@ static int gfar_resume(struct device *dev)
struct gfar_private *priv = dev_get_drvdata(dev);
struct net_device *ndev = priv->ndev;
struct gfar __iomem *regs = priv->gfargrp[0].regs;
unsigned long flags;
u32 tempval;
int magic_packet = priv->wol_en &&
(priv->device_flags &
FSL_GIANFAR_DEV_HAS_MAGIC_PACKET);

if (!netif_running(ndev)) {
netif_device_attach(ndev);
if (!netif_running(ndev))
return 0;
}

if (!magic_packet && priv->phydev)
if (magic_packet) {
/* Disable Magic Packet mode */
tempval = gfar_read(&regs->maccfg2);
tempval &= ~MACCFG2_MPEN;
gfar_write(&regs->maccfg2, tempval);
} else {
phy_start(priv->phydev);

/* Disable Magic Packet mode, in case something
* else woke us up.
*/
local_irq_save(flags);
lock_tx_qs(priv);

tempval = gfar_read(&regs->maccfg2);
tempval &= ~MACCFG2_MPEN;
gfar_write(&regs->maccfg2, tempval);
}

gfar_start(priv);

unlock_tx_qs(priv);
local_irq_restore(flags);

netif_device_attach(ndev);

enable_napi(priv);

return 0;
Expand Down Expand Up @@ -2047,7 +2007,8 @@ static int register_grp_irqs(struct gfar_priv_grp *grp)
/* Install our interrupt handlers for Error,
* Transmit, and Receive
*/
err = request_irq(gfar_irq(grp, ER)->irq, gfar_error, 0,
err = request_irq(gfar_irq(grp, ER)->irq, gfar_error,
IRQF_NO_SUSPEND,
gfar_irq(grp, ER)->name, grp);
if (err < 0) {
netif_err(priv, intr, dev, "Can't get IRQ %d\n",
Expand All @@ -2070,7 +2031,8 @@ static int register_grp_irqs(struct gfar_priv_grp *grp)
goto rx_irq_fail;
}
} else {
err = request_irq(gfar_irq(grp, TX)->irq, gfar_interrupt, 0,
err = request_irq(gfar_irq(grp, TX)->irq, gfar_interrupt,
IRQF_NO_SUSPEND,
gfar_irq(grp, TX)->name, grp);
if (err < 0) {
netif_err(priv, intr, dev, "Can't get IRQ %d\n",
Expand Down

0 comments on commit 614b424

Please sign in to comment.