diff --git a/drivers/net/ethernet/broadcom/asp2/bcmasp_intf.c b/drivers/net/ethernet/broadcom/asp2/bcmasp_intf.c index ec63f50a849e..ed0977832ce4 100644 --- a/drivers/net/ethernet/broadcom/asp2/bcmasp_intf.c +++ b/drivers/net/ethernet/broadcom/asp2/bcmasp_intf.c @@ -344,40 +344,35 @@ static netdev_tx_t bcmasp_xmit(struct sk_buff *skb, struct net_device *dev) return NETDEV_TX_OK; } -static void bcmasp_netif_start(struct net_device *dev) +static void umac_reset_and_init(struct bcmasp_intf *intf, + const unsigned char *addr) { - struct bcmasp_intf *intf = netdev_priv(dev); + struct phy_device *phydev = intf->ndev->phydev; + u32 mac0, mac1; - bcmasp_set_rx_mode(dev); - napi_enable(&intf->tx_napi); - napi_enable(&intf->rx_napi); - - bcmasp_enable_rx_irq(intf, 1); - bcmasp_enable_tx_irq(intf, 1); - bcmasp_enable_phy_irq(intf, 1); - - phy_start(dev->phydev); -} - -static void umac_reset(struct bcmasp_intf *intf) -{ umac_wl(intf, 0x0, UMC_CMD); umac_wl(intf, UMC_CMD_SW_RESET, UMC_CMD); usleep_range(10, 100); /* We hold the umac in reset and bring it out of * reset when phy link is up. */ -} -static void umac_set_hw_addr(struct bcmasp_intf *intf, - const unsigned char *addr) -{ - u32 mac0 = (addr[0] << 24) | (addr[1] << 16) | (addr[2] << 8) | - addr[3]; - u32 mac1 = (addr[4] << 8) | addr[5]; + umac_wl(intf, 0x800, UMC_FRM_LEN); + umac_wl(intf, 0xffff, UMC_PAUSE_CNTRL); + umac_wl(intf, 0x800, UMC_RX_MAX_PKT_SZ); + + mac0 = (addr[0] << 24) | (addr[1] << 16) | (addr[2] << 8) | + addr[3]; + mac1 = (addr[4] << 8) | addr[5]; umac_wl(intf, mac0, UMC_MAC0); umac_wl(intf, mac1, UMC_MAC1); + + /* Reset shadow values since we reset the umac */ + intf->old_duplex = -1; + intf->old_link = -1; + intf->old_pause = -1; + phydev->eee_cfg.tx_lpi_timer = umac_rl(intf, UMC_EEE_LPI_TIMER); } static void umac_enable_set(struct bcmasp_intf *intf, u32 mask, @@ -401,13 +396,6 @@ static void umac_enable_set(struct bcmasp_intf *intf, u32 mask, usleep_range(1000, 2000); } -static void umac_init(struct bcmasp_intf *intf) -{ - umac_wl(intf, 0x800, UMC_FRM_LEN); - umac_wl(intf, 0xffff, UMC_PAUSE_CNTRL); - umac_wl(intf, 0x800, UMC_RX_MAX_PKT_SZ); -} - static int bcmasp_tx_reclaim(struct bcmasp_intf *intf) { struct bcmasp_intf_stats64 *stats = &intf->stats64; @@ -927,7 +915,15 @@ static void bcmasp_rgmii_mode_en_set(struct bcmasp_intf *intf, bool enable) rgmii_wl(intf, reg, RGMII_OOB_CNTRL); } -static void bcmasp_netif_deinit(struct net_device *dev) +static void bcmasp_phy_hw_unprepare(struct bcmasp_intf *intf) +{ + if (intf->internal_phy) + bcmasp_ephy_enable_set(intf, false); + else + bcmasp_rgmii_mode_en_set(intf, false); +} + +static void bcmasp_netif_deinit(struct net_device *dev, bool stop_phy) { struct bcmasp_intf *intf = netdev_priv(dev); u32 reg, timeout = 1000; @@ -950,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); @@ -978,17 +975,13 @@ 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); phy_disconnect(dev->phydev); - /* Disable internal EPHY or external PHY */ - if (intf->internal_phy) - bcmasp_ephy_enable_set(intf, false); - else - bcmasp_rgmii_mode_en_set(intf, false); + bcmasp_phy_hw_unprepare(intf); /* Disable the interface clocks */ bcmasp_core_clock_set_intf(intf, false); @@ -998,10 +991,15 @@ static int bcmasp_stop(struct net_device *dev) return 0; } -static void bcmasp_configure_port(struct bcmasp_intf *intf) +static void bcmasp_phy_hw_prepare(struct bcmasp_intf *intf) { u32 reg, id_mode_dis = 0; + if (intf->internal_phy) + bcmasp_ephy_enable_set(intf, true); + else + bcmasp_rgmii_mode_en_set(intf, true); + reg = rgmii_rl(intf, RGMII_PORT_CNTRL); reg &= ~RGMII_PORT_MODE_MASK; @@ -1036,26 +1034,8 @@ static void bcmasp_configure_port(struct bcmasp_intf *intf) rgmii_wl(intf, reg, RGMII_OOB_CNTRL); } -static int bcmasp_netif_init(struct net_device *dev, bool phy_connect) +static phy_interface_t bcmasp_phy_iface_for_connect(phy_interface_t mode) { - struct bcmasp_intf *intf = netdev_priv(dev); - phy_interface_t phy_iface = intf->phy_interface; - u32 phy_flags = PHY_BRCM_AUTO_PWRDWN_ENABLE | - PHY_BRCM_DIS_TXCRXC_NOENRGY | - PHY_BRCM_IDDQ_SUSPEND; - struct phy_device *phydev = NULL; - int ret; - - /* Always enable interface clocks */ - bcmasp_core_clock_set_intf(intf, true); - - /* Enable internal PHY or external PHY before any MAC activity */ - if (intf->internal_phy) - bcmasp_ephy_enable_set(intf, true); - else - bcmasp_rgmii_mode_en_set(intf, true); - bcmasp_configure_port(intf); - /* This is an ugly quirk but we have not been correctly * interpreting the phy_interface values and we have done that * across different drivers, so at least we are consistent in @@ -1081,46 +1061,43 @@ static int bcmasp_netif_init(struct net_device *dev, bool phy_connect) * affected because they use different phy_interface_t values * or the Generic PHY driver. */ - switch (phy_iface) { + switch (mode) { case PHY_INTERFACE_MODE_RGMII: - phy_iface = PHY_INTERFACE_MODE_RGMII_ID; - break; + return PHY_INTERFACE_MODE_RGMII_ID; case PHY_INTERFACE_MODE_RGMII_TXID: - phy_iface = PHY_INTERFACE_MODE_RGMII_RXID; - break; + return PHY_INTERFACE_MODE_RGMII_RXID; default: - break; + return mode; } +} - if (phy_connect) { - phydev = of_phy_connect(dev, intf->phy_dn, - bcmasp_adj_link, phy_flags, - phy_iface); - if (!phydev) { - ret = -ENODEV; - netdev_err(dev, "could not attach to PHY\n"); - goto err_phy_disable; - } +static int bcmasp_phy_attach(struct bcmasp_intf *intf) +{ + u32 phy_flags = PHY_BRCM_AUTO_PWRDWN_ENABLE | + PHY_BRCM_DIS_TXCRXC_NOENRGY | + PHY_BRCM_IDDQ_SUSPEND; + struct phy_device *phydev; + phy_interface_t phy_iface; - if (intf->internal_phy) - dev->phydev->irq = PHY_MAC_INTERRUPT; - - /* Indicate that the MAC is responsible for PHY PM */ - phydev->mac_managed_pm = true; - - /* Set phylib's copy of the LPI timer */ - phydev->eee_cfg.tx_lpi_timer = umac_rl(intf, UMC_EEE_LPI_TIMER); + phy_iface = bcmasp_phy_iface_for_connect(intf->phy_interface); + phydev = of_phy_connect(intf->ndev, intf->phy_dn, + bcmasp_adj_link, phy_flags, + phy_iface); + if (!phydev) { + netdev_err(intf->ndev, "could not attach to PHY\n"); + return -ENODEV; } + if (intf->internal_phy) + intf->ndev->phydev->irq = PHY_MAC_INTERRUPT; - umac_reset(intf); + phydev->mac_managed_pm = true; - umac_init(intf); + return 0; +} - umac_set_hw_addr(intf, dev->dev_addr); - - intf->old_duplex = -1; - intf->old_link = -1; - intf->old_pause = -1; +static void bcmasp_netif_init(struct net_device *dev) +{ + struct bcmasp_intf *intf = netdev_priv(dev); bcmasp_init_tx(intf); netif_napi_add_tx(intf->ndev, &intf->tx_napi, bcmasp_tx_poll); @@ -1132,18 +1109,13 @@ static int bcmasp_netif_init(struct net_device *dev, bool phy_connect) intf->crc_fwd = !!(umac_rl(intf, UMC_CMD) & UMC_CMD_CRC_FWD); - bcmasp_netif_start(dev); + bcmasp_set_rx_mode(dev); + napi_enable(&intf->tx_napi); + napi_enable(&intf->rx_napi); - netif_start_queue(dev); - - return 0; - -err_phy_disable: - if (intf->internal_phy) - bcmasp_ephy_enable_set(intf, false); - else - bcmasp_rgmii_mode_en_set(intf, false); - return ret; + bcmasp_enable_rx_irq(intf, 1); + bcmasp_enable_tx_irq(intf, 1); + bcmasp_enable_phy_irq(intf, 1); } static int bcmasp_open(struct net_device *dev) @@ -1161,14 +1133,30 @@ static int bcmasp_open(struct net_device *dev) if (ret) goto err_free_mem; - ret = bcmasp_netif_init(dev, true); - if (ret) { - clk_disable_unprepare(intf->parent->clk); - goto err_free_mem; - } + bcmasp_core_clock_set_intf(intf, true); + + bcmasp_phy_hw_prepare(intf); + + ret = bcmasp_phy_attach(intf); + if (ret) + goto err_phy_attach; + + umac_reset_and_init(intf, dev->dev_addr); + + dev->phydev->eee_cfg.tx_lpi_timer = umac_rl(intf, UMC_EEE_LPI_TIMER); + + bcmasp_netif_init(dev); + + phy_start(dev->phydev); + + netif_start_queue(dev); return ret; +err_phy_attach: + bcmasp_phy_hw_unprepare(intf); + bcmasp_core_clock_set_intf(intf, false); + clk_disable_unprepare(intf->parent->clk); err_free_mem: bcmasp_reclaim_free_buffers(intf); @@ -1398,19 +1386,27 @@ 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) { - if (intf->internal_phy) - bcmasp_ephy_enable_set(intf, false); - else - bcmasp_rgmii_mode_en_set(intf, false); + 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 * disable the network interface clocks. @@ -1418,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; @@ -1444,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; @@ -1454,17 +1450,41 @@ int bcmasp_interface_resume(struct bcmasp_intf *intf) if (ret) return ret; - ret = bcmasp_netif_init(dev, false); - if (ret) - goto out; + wake = device_may_wakeup(kdev) && intf->wolopts; - bcmasp_resume_from_wol(intf); + bcmasp_core_clock_set_intf(intf, true); + + /* 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); + + 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); return 0; - -out: - clk_disable_unprepare(intf->parent->clk); - return ret; }