net: bcmgenet: Move wake-up event out of side band ISR
authorDoug Berger <opendmb@gmail.com>
Thu, 30 Apr 2020 23:26:51 +0000 (16:26 -0700)
committerDavid S. Miller <davem@davemloft.net>
Fri, 1 May 2020 22:54:33 +0000 (15:54 -0700)
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 <opendmb@gmail.com>
Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/broadcom/genet/bcmgenet.c
drivers/net/ethernet/broadcom/genet/bcmgenet.h
drivers/net/ethernet/broadcom/genet/bcmgenet_wol.c

index ad614d7..ff31da0 100644 (file)
@@ -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 },
index 031d91f..a12cb59 100644 (file)
@@ -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)
index 4b9d65f..4ea6a26 100644 (file)
@@ -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;
 }