net: bcmgenet: revise suspend/resume
authorDoug Berger <opendmb@gmail.com>
Thu, 6 Mar 2025 19:26:42 +0000 (11:26 -0800)
committerJakub Kicinski <kuba@kernel.org>
Sat, 8 Mar 2025 03:33:48 +0000 (19:33 -0800)
If the network interface is configured for Wake-on-LAN we should
avoid bringing the interface down and up since it slows the time
to reestablish network traffic on resume.

Redundant calls to phy_suspend() and phy_resume() are removed
since they are already invoked from within phy_stop() and
phy_start() called from bcmgenet_netif_stop() and
bcmgenet_netif_start().

Signed-off-by: Doug Berger <opendmb@gmail.com>
Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
Link: https://patch.msgid.link/20250306192643.2383632-15-opendmb@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/net/ethernet/broadcom/genet/bcmgenet.c
drivers/net/ethernet/broadcom/genet/bcmgenet.h
drivers/net/ethernet/broadcom/genet/bcmgenet_wol.c

index 8aa575b93e5646b3d7a1ad75b76478818177c190..73d78dcb774d99242ac8440dc2a4d1d4bf409183 100644 (file)
@@ -2584,7 +2584,7 @@ static void init_umac(struct bcmgenet_priv *priv)
 
        /* Enable MDIO interrupts on GENET v3+ */
        if (bcmgenet_has_mdio_intr(priv))
-               int0_enable |= (UMAC_IRQ_MDIO_DONE | UMAC_IRQ_MDIO_ERROR);
+               int0_enable |= UMAC_IRQ_MDIO_EVENT;
 
        bcmgenet_intrl2_0_writel(priv, int0_enable, INTRL2_CPU_MASK_CLEAR);
 
@@ -3150,10 +3150,8 @@ static irqreturn_t bcmgenet_isr0(int irq, void *dev_id)
        netif_dbg(priv, intr, priv->dev,
                  "IRQ=0x%x\n", status);
 
-       if (bcmgenet_has_mdio_intr(priv) &&
-               status & (UMAC_IRQ_MDIO_DONE | UMAC_IRQ_MDIO_ERROR)) {
+       if (bcmgenet_has_mdio_intr(priv) && status & UMAC_IRQ_MDIO_EVENT)
                wake_up(&priv->wq);
-       }
 
        /* all other interested interrupts handled in bottom half */
        status &= (UMAC_IRQ_LINK_EVENT | UMAC_IRQ_PHY_DET_R);
@@ -3311,19 +3309,21 @@ static void bcmgenet_netif_stop(struct net_device *dev, bool stop_phy)
 {
        struct bcmgenet_priv *priv = netdev_priv(dev);
 
-       bcmgenet_disable_tx_napi(priv);
        netif_tx_disable(dev);
 
        /* Disable MAC receive */
+       bcmgenet_hfb_reg_writel(priv, 0, HFB_CTRL);
        umac_enable_set(priv, CMD_RX_EN, false);
 
+       if (stop_phy)
+               phy_stop(dev->phydev);
+
        bcmgenet_dma_teardown(priv);
 
        /* Disable MAC transmit. TX DMA disabled must be done before this */
        umac_enable_set(priv, CMD_TX_EN, false);
 
-       if (stop_phy)
-               phy_stop(dev->phydev);
+       bcmgenet_disable_tx_napi(priv);
        bcmgenet_disable_rx_napi(priv);
        bcmgenet_intr_disable(priv);
 
@@ -4043,7 +4043,10 @@ static int bcmgenet_resume_noirq(struct device *d)
                        pm_wakeup_event(&priv->pdev->dev, 0);
 
                /* From WOL-enabled suspend, switch to regular clock */
-               bcmgenet_power_up(priv, GENET_POWER_WOL_MAGIC);
+               if (!bcmgenet_power_up(priv, GENET_POWER_WOL_MAGIC))
+                       return 0;
+
+               /* Failed so fall through to reset MAC */
        }
 
        /* If this is an internal GPHY, power it back on now, before UniMAC is
@@ -4055,8 +4058,6 @@ static int bcmgenet_resume_noirq(struct device *d)
        /* take MAC out of reset */
        bcmgenet_umac_reset(priv);
 
-       bcmgenet_intrl2_0_writel(priv, UMAC_IRQ_WAKE_EVENT, INTRL2_CPU_CLEAR);
-
        return 0;
 }
 
@@ -4066,10 +4067,46 @@ static int bcmgenet_resume(struct device *d)
        struct bcmgenet_priv *priv = netdev_priv(dev);
        struct bcmgenet_rxnfc_rule *rule;
        int ret;
+       u32 reg;
 
        if (!netif_running(dev))
                return 0;
 
+       if (device_may_wakeup(d) && priv->wolopts) {
+               reg = bcmgenet_umac_readl(priv, UMAC_CMD);
+               if (reg & CMD_RX_EN) {
+                       /* Successfully exited WoL, just resume data flows */
+                       list_for_each_entry(rule, &priv->rxnfc_list, list)
+                               if (rule->state == BCMGENET_RXNFC_STATE_ENABLED)
+                                       bcmgenet_hfb_enable_filter(priv,
+                                                       rule->fs.location + 1);
+                       bcmgenet_hfb_enable_filter(priv, 0);
+                       bcmgenet_set_rx_mode(dev);
+                       bcmgenet_enable_rx_napi(priv);
+
+                       /* Reinitialize Tx flows */
+                       bcmgenet_tdma_disable(priv);
+                       bcmgenet_init_tx_queues(priv->dev);
+                       reg = bcmgenet_tdma_readl(priv, DMA_CTRL);
+                       reg |= DMA_EN;
+                       bcmgenet_tdma_writel(priv, reg, DMA_CTRL);
+                       bcmgenet_enable_tx_napi(priv);
+
+                       bcmgenet_link_intr_enable(priv);
+                       phy_start_machine(dev->phydev);
+
+                       netif_device_attach(dev);
+                       enable_irq(priv->irq1);
+                       return 0;
+               }
+               /* MAC was reset so complete bcmgenet_netif_stop() */
+               umac_enable_set(priv, CMD_RX_EN | CMD_TX_EN, false);
+               bcmgenet_rdma_disable(priv);
+               bcmgenet_intr_disable(priv);
+               bcmgenet_fini_dma(priv);
+               enable_irq(priv->irq1);
+       }
+
        init_umac(priv);
 
        phy_init_hw(dev->phydev);
@@ -4116,19 +4153,52 @@ static int bcmgenet_suspend(struct device *d)
 {
        struct net_device *dev = dev_get_drvdata(d);
        struct bcmgenet_priv *priv = netdev_priv(dev);
+       struct bcmgenet_rxnfc_rule *rule;
+       u32 reg, hfb_enable = 0;
 
        if (!netif_running(dev))
                return 0;
 
        netif_device_detach(dev);
 
-       bcmgenet_netif_stop(dev, true);
+       if (device_may_wakeup(d) && priv->wolopts) {
+               netif_tx_disable(dev);
+
+               /* Suspend non-wake Rx data flows */
+               if (priv->wolopts & WAKE_FILTER)
+                       list_for_each_entry(rule, &priv->rxnfc_list, list)
+                               if (rule->fs.ring_cookie == RX_CLS_FLOW_WAKE &&
+                                   rule->state == BCMGENET_RXNFC_STATE_ENABLED)
+                                       hfb_enable |= 1 << rule->fs.location;
+               reg = bcmgenet_hfb_reg_readl(priv, HFB_CTRL);
+               if (GENET_IS_V1(priv) || GENET_IS_V2(priv)) {
+                       reg &= ~RBUF_HFB_FILTER_EN_MASK;
+                       reg |= hfb_enable << (RBUF_HFB_FILTER_EN_SHIFT + 1);
+               } else {
+                       bcmgenet_hfb_reg_writel(priv, hfb_enable << 1,
+                                               HFB_FLT_ENABLE_V3PLUS + 4);
+               }
+               if (!hfb_enable)
+                       reg &= ~RBUF_HFB_EN;
+               bcmgenet_hfb_reg_writel(priv, reg, HFB_CTRL);
 
-       if (!device_may_wakeup(d))
-               phy_suspend(dev->phydev);
+               /* Clear any old filter matches so only new matches wake */
+               bcmgenet_intrl2_0_writel(priv, 0xFFFFFFFF, INTRL2_CPU_MASK_SET);
+               bcmgenet_intrl2_0_writel(priv, 0xFFFFFFFF, INTRL2_CPU_CLEAR);
 
-       /* Disable filtering */
-       bcmgenet_hfb_reg_writel(priv, 0, HFB_CTRL);
+               if (-ETIMEDOUT == bcmgenet_tdma_disable(priv))
+                       netdev_warn(priv->dev,
+                                   "Timed out while disabling TX DMA\n");
+
+               bcmgenet_disable_tx_napi(priv);
+               bcmgenet_disable_rx_napi(priv);
+               disable_irq(priv->irq1);
+               bcmgenet_tx_reclaim_all(dev);
+               bcmgenet_fini_tx_napi(priv);
+       } else {
+               /* Teardown the interface */
+               bcmgenet_netif_stop(dev, true);
+       }
 
        return 0;
 }
index c95601898bd443550f6db36f24b6db4dbab70e24..10c631bbe9649a53b75befd991a75b0e331eef41 100644 (file)
@@ -274,6 +274,8 @@ struct bcmgenet_mib_counters {
 /* Only valid for GENETv3+ */
 #define UMAC_IRQ_MDIO_DONE             (1 << 23)
 #define UMAC_IRQ_MDIO_ERROR            (1 << 24)
+#define UMAC_IRQ_MDIO_EVENT            (UMAC_IRQ_MDIO_DONE | \
+                                        UMAC_IRQ_MDIO_ERROR)
 
 /* INTRL2 instance 1 definitions */
 #define UMAC_IRQ1_TX_INTR_MASK         0xFFFF
index d0f1fa7029176b12f2468c63edda4bb721a46acf..8fb551288298064af0071e3b6b0902b7c1aa52d4 100644 (file)
@@ -145,8 +145,7 @@ int bcmgenet_wol_power_down_cfg(struct bcmgenet_priv *priv,
                                enum bcmgenet_power_mode mode)
 {
        struct net_device *dev = priv->dev;
-       struct bcmgenet_rxnfc_rule *rule;
-       u32 reg, hfb_ctrl_reg, hfb_enable = 0;
+       u32 reg, hfb_ctrl_reg;
        int retries = 0;
 
        if (mode != GENET_POWER_WOL_MAGIC) {
@@ -154,18 +153,6 @@ int bcmgenet_wol_power_down_cfg(struct bcmgenet_priv *priv,
                return -EINVAL;
        }
 
-       /* Can't suspend with WoL if MAC is still in reset */
-       spin_lock_bh(&priv->reg_lock);
-       reg = bcmgenet_umac_readl(priv, UMAC_CMD);
-       if (reg & CMD_SW_RESET)
-               reg &= ~CMD_SW_RESET;
-
-       /* disable RX */
-       reg &= ~CMD_RX_EN;
-       bcmgenet_umac_writel(priv, reg, UMAC_CMD);
-       spin_unlock_bh(&priv->reg_lock);
-       mdelay(10);
-
        if (priv->wolopts & (WAKE_MAGIC | WAKE_MAGICSECURE)) {
                reg = bcmgenet_umac_readl(priv, UMAC_MPD_CTRL);
                reg |= MPD_EN;
@@ -177,13 +164,8 @@ int bcmgenet_wol_power_down_cfg(struct bcmgenet_priv *priv,
        }
 
        hfb_ctrl_reg = bcmgenet_hfb_reg_readl(priv, HFB_CTRL);
-       if (priv->wolopts & WAKE_FILTER) {
-               list_for_each_entry(rule, &priv->rxnfc_list, list)
-                       if (rule->fs.ring_cookie == RX_CLS_FLOW_WAKE)
-                               hfb_enable |= (1 << (rule->fs.location + 1));
-               reg = (hfb_ctrl_reg & ~RBUF_HFB_EN) | RBUF_ACPI_EN;
-               bcmgenet_hfb_reg_writel(priv, reg, HFB_CTRL);
-       }
+       reg = hfb_ctrl_reg | RBUF_ACPI_EN;
+       bcmgenet_hfb_reg_writel(priv, reg, HFB_CTRL);
 
        /* Do not leave UniMAC in MPD mode only */
        retries = bcmgenet_poll_wol_status(priv);
@@ -198,14 +180,12 @@ int bcmgenet_wol_power_down_cfg(struct bcmgenet_priv *priv,
        netif_dbg(priv, wol, dev, "MPD WOL-ready status set after %d msec\n",
                  retries);
 
-       clk_prepare_enable(priv->clk_wol);
+       /* Disable phy status updates while suspending */
+       mutex_lock(&dev->phydev->lock);
+       dev->phydev->state = PHY_READY;
+       mutex_unlock(&dev->phydev->lock);
 
-       if (hfb_enable) {
-               bcmgenet_hfb_reg_writel(priv, hfb_enable,
-                                       HFB_FLT_ENABLE_V3PLUS + 4);
-               hfb_ctrl_reg = RBUF_HFB_EN | RBUF_ACPI_EN;
-               bcmgenet_hfb_reg_writel(priv, hfb_ctrl_reg, HFB_CTRL);
-       }
+       clk_prepare_enable(priv->clk_wol);
 
        /* Enable CRC forward */
        spin_lock_bh(&priv->reg_lock);
@@ -213,13 +193,17 @@ int bcmgenet_wol_power_down_cfg(struct bcmgenet_priv *priv,
        priv->crc_fwd_en = 1;
        reg |= CMD_CRC_FWD;
 
+       /* Can't suspend with WoL if MAC is still in reset */
+       if (reg & CMD_SW_RESET)
+               reg &= ~CMD_SW_RESET;
+
        /* Receiver must be enabled for WOL MP detection */
        reg |= CMD_RX_EN;
        bcmgenet_umac_writel(priv, reg, UMAC_CMD);
        spin_unlock_bh(&priv->reg_lock);
 
        reg = UMAC_IRQ_MPD_R;
-       if (hfb_enable)
+       if (hfb_ctrl_reg & RBUF_HFB_EN)
                reg |=  UMAC_IRQ_HFB_SM | UMAC_IRQ_HFB_MM;
 
        bcmgenet_intrl2_0_writel(priv, reg, INTRL2_CPU_MASK_CLEAR);
@@ -230,6 +214,7 @@ int bcmgenet_wol_power_down_cfg(struct bcmgenet_priv *priv,
 int bcmgenet_wol_power_up_cfg(struct bcmgenet_priv *priv,
                              enum bcmgenet_power_mode mode)
 {
+       struct net_device *dev = priv->dev;
        u32 reg;
 
        if (mode != GENET_POWER_WOL_MAGIC) {
@@ -242,6 +227,10 @@ int bcmgenet_wol_power_up_cfg(struct bcmgenet_priv *priv,
 
        bcmgenet_intrl2_0_writel(priv, UMAC_IRQ_WAKE_EVENT,
                                 INTRL2_CPU_MASK_SET);
+       if (bcmgenet_has_mdio_intr(priv))
+               bcmgenet_intrl2_0_writel(priv,
+                                        UMAC_IRQ_MDIO_EVENT,
+                                        INTRL2_CPU_MASK_CLEAR);
 
        /* Disable Magic Packet Detection */
        if (priv->wolopts & (WAKE_MAGIC | WAKE_MAGICSECURE)) {
@@ -252,14 +241,12 @@ int bcmgenet_wol_power_up_cfg(struct bcmgenet_priv *priv,
                bcmgenet_umac_writel(priv, reg, UMAC_MPD_CTRL);
        }
 
-       /* Disable WAKE_FILTER Detection */
-       if (priv->wolopts & WAKE_FILTER) {
-               reg = bcmgenet_hfb_reg_readl(priv, HFB_CTRL);
-               if (!(reg & RBUF_ACPI_EN))
-                       return -EPERM;  /* already reset so skip the rest */
-               reg &= ~(RBUF_HFB_EN | RBUF_ACPI_EN);
-               bcmgenet_hfb_reg_writel(priv, reg, HFB_CTRL);
-       }
+       /* Disable ACPI mode */
+       reg = bcmgenet_hfb_reg_readl(priv, HFB_CTRL);
+       if (!(reg & RBUF_ACPI_EN))
+               return -EPERM;  /* already reset so skip the rest */
+       reg &= ~RBUF_ACPI_EN;
+       bcmgenet_hfb_reg_writel(priv, reg, HFB_CTRL);
 
        /* Disable CRC Forward */
        spin_lock_bh(&priv->reg_lock);
@@ -268,5 +255,13 @@ int bcmgenet_wol_power_up_cfg(struct bcmgenet_priv *priv,
        bcmgenet_umac_writel(priv, reg, UMAC_CMD);
        spin_unlock_bh(&priv->reg_lock);
 
+       /* Resume link status tracking */
+       mutex_lock(&dev->phydev->lock);
+       if (dev->phydev->link)
+               dev->phydev->state = PHY_RUNNING;
+       else
+               dev->phydev->state = PHY_NOLINK;
+       mutex_unlock(&dev->phydev->lock);
+
        return 0;
 }