From eb236c2994b031f805be691fa9ea39cba4690166 Mon Sep 17 00:00:00 2001 From: Doug Berger Date: Thu, 30 Apr 2020 16:26:51 -0700 Subject: 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: Doug Berger Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/genet/bcmgenet.c | 72 ++++++++++++++++++---- drivers/net/ethernet/broadcom/genet/bcmgenet.h | 2 + drivers/net/ethernet/broadcom/genet/bcmgenet_wol.c | 6 ++ 3 files changed, 67 insertions(+), 13 deletions(-) diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c index ad614d7201bd..ff31da0ed846 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c @@ -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 }, diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.h b/drivers/net/ethernet/broadcom/genet/bcmgenet.h index 031d91f45067..a12cb59298f4 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.h +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.h @@ -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) diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet_wol.c b/drivers/net/ethernet/broadcom/genet/bcmgenet_wol.c index 4b9d65f392c2..4ea6a26b04f7 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet_wol.c +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet_wol.c @@ -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; } -- cgit