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;
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);
/* Stop tx from updating HW */
netif_tx_disable(dev);
- bcmasp_netif_deinit(dev);
+ bcmasp_netif_deinit(dev, true);
bcmasp_reclaim_free_buffers(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
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;
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;
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);