Commit eb236c29 authored by Doug Berger's avatar Doug Berger Committed by David S. Miller
Browse files

net: bcmgenet: Move wake-up event out of side band ISR



The side band interrupt service routine is not available on chips
like 7211, or rather, it does not permit the signaling of wake-up
events due to the complex interrupt hierarchy.

Move the wake-up event accounting into a .resume_noirq function,
account for possible wake-up events and clear the MPD/HFB interrupts
from there, while leaving the hardware untouched until the resume
function proceeds with doing its usual business.

Because bcmgenet_wol_power_down_cfg() now enables the MPD and HFB
interrupts, it is invoked by a .suspend_noirq function to prevent
the servicing of interrupts after the clocks have been disabled.

Signed-off-by: default avatarDoug Berger <opendmb@gmail.com>
Signed-off-by: default avatarFlorian Fainelli <f.fainelli@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent df8f348e
Loading
Loading
Loading
Loading
+59 −13
Original line number Diff line number Diff line
@@ -3270,10 +3270,7 @@ static irqreturn_t bcmgenet_isr0(int irq, void *dev_id)

static irqreturn_t bcmgenet_wol_isr(int irq, void *dev_id)
{
	struct bcmgenet_priv *priv = dev_id;

	pm_wakeup_event(&priv->pdev->dev, 0);

	/* Acknowledge the interrupt */
	return IRQ_HANDLED;
}

@@ -4174,13 +4171,12 @@ static void bcmgenet_shutdown(struct platform_device *pdev)
}

#ifdef CONFIG_PM_SLEEP
static int bcmgenet_resume(struct device *d)
static int bcmgenet_resume_noirq(struct device *d)
{
	struct net_device *dev = dev_get_drvdata(d);
	struct bcmgenet_priv *priv = netdev_priv(dev);
	unsigned long dma_ctrl;
	u32 offset, reg;
	int ret;
	u32 reg;

	if (!netif_running(dev))
		return 0;
@@ -4190,6 +4186,34 @@ static int bcmgenet_resume(struct device *d)
	if (ret)
		return ret;

	if (device_may_wakeup(d) && priv->wolopts) {
		/* Account for Wake-on-LAN events and clear those events
		 * (Some devices need more time between enabling the clocks
		 *  and the interrupt register reflecting the wake event so
		 *  read the register twice)
		 */
		reg = bcmgenet_intrl2_0_readl(priv, INTRL2_CPU_STAT);
		reg = bcmgenet_intrl2_0_readl(priv, INTRL2_CPU_STAT);
		if (reg & UMAC_IRQ_WAKE_EVENT)
			pm_wakeup_event(&priv->pdev->dev, 0);
	}

	bcmgenet_intrl2_0_writel(priv, UMAC_IRQ_WAKE_EVENT, INTRL2_CPU_CLEAR);

	return 0;
}

static int bcmgenet_resume(struct device *d)
{
	struct net_device *dev = dev_get_drvdata(d);
	struct bcmgenet_priv *priv = netdev_priv(dev);
	unsigned long dma_ctrl;
	u32 offset, reg;
	int ret;

	if (!netif_running(dev))
		return 0;

	/* From WOL-enabled suspend, switch to regular clock */
	if (device_may_wakeup(d) && priv->wolopts)
		bcmgenet_power_up(priv, GENET_POWER_WOL_MAGIC);
@@ -4262,7 +4286,6 @@ static int bcmgenet_suspend(struct device *d)
{
	struct net_device *dev = dev_get_drvdata(d);
	struct bcmgenet_priv *priv = netdev_priv(dev);
	int ret = 0;
	u32 offset;

	if (!netif_running(dev))
@@ -4282,23 +4305,46 @@ static int bcmgenet_suspend(struct device *d)
	priv->hfb_en[2] = bcmgenet_hfb_reg_readl(priv, offset + sizeof(u32));
	bcmgenet_hfb_reg_writel(priv, 0, HFB_CTRL);

	return 0;
}

static int bcmgenet_suspend_noirq(struct device *d)
{
	struct net_device *dev = dev_get_drvdata(d);
	struct bcmgenet_priv *priv = netdev_priv(dev);
	int ret = 0;

	if (!netif_running(dev))
		return 0;

	/* Prepare the device for Wake-on-LAN and switch to the slow clock */
	if (device_may_wakeup(d) && priv->wolopts)
		ret = bcmgenet_power_down(priv, GENET_POWER_WOL_MAGIC);
	else if (priv->internal_phy)
		ret = bcmgenet_power_down(priv, GENET_POWER_PASSIVE);

	/* Let the framework handle resumption and leave the clocks on */
	if (ret)
		return ret;

	/* Turn off the clocks */
	clk_disable_unprepare(priv->clk);

	if (ret)
		bcmgenet_resume(d);

	return ret;
	return 0;
}
#else
#define bcmgenet_suspend	NULL
#define bcmgenet_suspend_noirq	NULL
#define bcmgenet_resume		NULL
#define bcmgenet_resume_noirq	NULL
#endif /* CONFIG_PM_SLEEP */

static SIMPLE_DEV_PM_OPS(bcmgenet_pm_ops, bcmgenet_suspend, bcmgenet_resume);
static const struct dev_pm_ops bcmgenet_pm_ops = {
	.suspend	= bcmgenet_suspend,
	.suspend_noirq	= bcmgenet_suspend_noirq,
	.resume		= bcmgenet_resume,
	.resume_noirq	= bcmgenet_resume_noirq,
};

static const struct acpi_device_id genet_acpi_match[] = {
	{ "BCM6E4E", (kernel_ulong_t)&bcm2711_plat_data },
+2 −0
Original line number Diff line number Diff line
@@ -312,6 +312,8 @@ struct bcmgenet_mib_counters {
#define UMAC_IRQ_HFB_SM			(1 << 10)
#define UMAC_IRQ_HFB_MM			(1 << 11)
#define UMAC_IRQ_MPD_R			(1 << 12)
#define UMAC_IRQ_WAKE_EVENT		(UMAC_IRQ_HFB_SM | UMAC_IRQ_HFB_MM | \
					 UMAC_IRQ_MPD_R)
#define UMAC_IRQ_RXDMA_MBDONE		(1 << 13)
#define UMAC_IRQ_RXDMA_PDONE		(1 << 14)
#define UMAC_IRQ_RXDMA_BDONE		(1 << 15)
+6 −0
Original line number Diff line number Diff line
@@ -193,6 +193,12 @@ int bcmgenet_wol_power_down_cfg(struct bcmgenet_priv *priv,
		bcmgenet_ext_writel(priv, reg, EXT_EXT_PWR_MGMT);
	}

	reg = UMAC_IRQ_MPD_R;
	if (hfb_enable)
		reg |=  UMAC_IRQ_HFB_SM | UMAC_IRQ_HFB_MM;

	bcmgenet_intrl2_0_writel(priv, reg, INTRL2_CPU_MASK_CLEAR);

	return 0;
}