]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
net: bcmasp: Keep phy link during WoL sleep cycle
authorJustin Chen <justin.chen@broadcom.com>
Wed, 6 May 2026 21:31:14 +0000 (14:31 -0700)
committerJakub Kicinski <kuba@kernel.org>
Fri, 8 May 2026 23:38:56 +0000 (16:38 -0700)
We currently more or less restart all the HW on resume. Since we also
stop the PHY, it takes a while for the PHY link to be re-negotiated on
resume. Instead of doing a full restart, we keep the HW state and the
PHY link, that way we can resume network traffic with a much smaller
delay.

Signed-off-by: Justin Chen <justin.chen@broadcom.com>
Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
Link: https://patch.msgid.link/20260506213114.2002886-3-justin.chen@broadcom.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/net/ethernet/broadcom/asp2/bcmasp_intf.c

index e2b51ec903af492c624fb5d6c285353e82883447..ed0977832ce4f522ff4b328f5372b91a7afee281 100644 (file)
@@ -923,7 +923,7 @@ static void bcmasp_phy_hw_unprepare(struct bcmasp_intf *intf)
                bcmasp_rgmii_mode_en_set(intf, false);
 }
 
-static void bcmasp_netif_deinit(struct net_device *dev)
+static void bcmasp_netif_deinit(struct net_device *dev, bool stop_phy)
 {
        struct bcmasp_intf *intf = netdev_priv(dev);
        u32 reg, timeout = 1000;
@@ -946,7 +946,8 @@ static void bcmasp_netif_deinit(struct net_device *dev)
 
        umac_enable_set(intf, UMC_CMD_TX_EN, 0);
 
-       phy_stop(dev->phydev);
+       if (stop_phy)
+               phy_stop(dev->phydev);
 
        umac_enable_set(intf, UMC_CMD_RX_EN, 0);
 
@@ -974,7 +975,7 @@ static int bcmasp_stop(struct net_device *dev)
        /* Stop tx from updating HW */
        netif_tx_disable(dev);
 
-       bcmasp_netif_deinit(dev);
+       bcmasp_netif_deinit(dev, true);
 
        bcmasp_reclaim_free_buffers(intf);
 
@@ -1385,15 +1386,26 @@ int bcmasp_interface_suspend(struct bcmasp_intf *intf)
 {
        struct device *kdev = &intf->parent->pdev->dev;
        struct net_device *dev = intf->ndev;
+       bool wake;
 
        if (!netif_running(dev))
                return 0;
 
        netif_device_detach(dev);
 
-       bcmasp_netif_deinit(dev);
+       wake = device_may_wakeup(kdev) && intf->wolopts;
 
-       if (!intf->wolopts) {
+       bcmasp_netif_deinit(dev, !wake);
+
+       if (wake) {
+               /* Disable phy status updates while suspending */
+               mutex_lock(&dev->phydev->lock);
+               dev->phydev->state = PHY_READY;
+               mutex_unlock(&dev->phydev->lock);
+               cancel_delayed_work_sync(&dev->phydev->state_queue);
+
+               bcmasp_suspend_to_wol(intf);
+       } else {
                bcmasp_phy_hw_unprepare(intf);
 
                /* If Wake-on-LAN is disabled, we can safely
@@ -1402,9 +1414,6 @@ int bcmasp_interface_suspend(struct bcmasp_intf *intf)
                bcmasp_core_clock_set_intf(intf, false);
        }
 
-       if (device_may_wakeup(kdev) && intf->wolopts)
-               bcmasp_suspend_to_wol(intf);
-
        clk_disable_unprepare(intf->parent->clk);
 
        return 0;
@@ -1428,8 +1437,11 @@ static void bcmasp_resume_from_wol(struct bcmasp_intf *intf)
 
 int bcmasp_interface_resume(struct bcmasp_intf *intf)
 {
+       struct device *kdev = &intf->parent->pdev->dev;
        struct net_device *dev = intf->ndev;
+       bool wake;
        int ret;
+       u32 reg;
 
        if (!netif_running(dev))
                return 0;
@@ -1438,17 +1450,39 @@ int bcmasp_interface_resume(struct bcmasp_intf *intf)
        if (ret)
                return ret;
 
-       bcmasp_core_clock_set_intf(intf, true);
+       wake = device_may_wakeup(kdev) && intf->wolopts;
 
-       bcmasp_resume_from_wol(intf);
-
-       bcmasp_phy_hw_prepare(intf);
+       bcmasp_core_clock_set_intf(intf, true);
 
-       umac_reset_and_init(intf, dev->dev_addr);
+       /* The interface might be HW reset in some suspend modes, so we may
+        * need to restore the UNIMAC/PHY if that is the case.
+        */
+       reg = umac_rl(intf, UMC_CMD);
+       if (wake && (reg & UMC_CMD_RX_EN)) {
+               umac_enable_set(intf, UMC_CMD_TX_EN, 1);
+               bcmasp_resume_from_wol(intf);
+       } else {
+               bcmasp_phy_hw_prepare(intf);
+               umac_reset_and_init(intf, dev->dev_addr);
+       }
 
        bcmasp_netif_init(dev);
 
-       phy_start(dev->phydev);
+       if (wake) {
+               /* If HW was reset, reprogram the unimac/PHY before resuming
+                * link status tracking to avoid racing the state machine.
+                */
+               if (!(reg & UMC_CMD_RX_EN))
+                       bcmasp_adj_link(dev);
+
+               /* Resume link status tracking */
+               mutex_lock(&dev->phydev->lock);
+               dev->phydev->state = dev->phydev->link ? PHY_RUNNING : PHY_NOLINK;
+               mutex_unlock(&dev->phydev->lock);
+               phy_trigger_machine(dev->phydev);
+       } else {
+               phy_start(dev->phydev);
+       }
 
        netif_device_attach(dev);