diff --git a/drivers/net/ethernet/nvidia/nvethernet/ether_linux.c b/drivers/net/ethernet/nvidia/nvethernet/ether_linux.c index 02b4a938..cc7217c2 100644 --- a/drivers/net/ethernet/nvidia/nvethernet/ether_linux.c +++ b/drivers/net/ethernet/nvidia/nvethernet/ether_linux.c @@ -6831,6 +6831,107 @@ static void ether_shutdown(struct platform_device *pdev) } #ifdef CONFIG_PM +#ifndef OSI_STRIPPED_LIB +/** + * @brief Revert the WOL settings + * + * Alogorithm: Create a struct ethtool_wolinfo to disable WOL settings + * + * @param[in] dev: Platform device associated with platform driver. + * + * @retval 0 on success + * @retval "negative value" on failure. + */ +static inline int ether_revert_wol(struct device *dev) +{ + int ret = 0; + u32 wolopts = 0; + struct net_device *ndev = dev_get_drvdata(dev); + struct ether_priv_data *pdata = netdev_priv(ndev); + + swap(pdata->wol.wolopts, wolopts); + ret = ether_set_wol_impl(ndev, &pdata->wol); + pdata->wol.wolopts = wolopts; + if (ret) + dev_err(pdata->dev, "Fail to enable PHY network functionality %d\n", ret); + + return ret; +} + +/** + * @brief Ethernet platform driver prepare callback. + * + * Alogorithm: Configure the defer WOL settings if enabled by user + * + * @param[in] dev: Platform device associated with platform driver. + * + * @retval 0 on success + * @retval "negative value" on failure. + */ +static int ether_prepare(struct device *dev) +{ + int ret = 0; + struct net_device *ndev = dev_get_drvdata(dev); + struct ether_priv_data *pdata = netdev_priv(ndev); + struct phy_device *phydev = pdata->phydev; + + if (pdata->wol.wolopts) { + ret = ether_set_wol_impl(ndev, &pdata->wol); + if (ret) + goto ether_prepare_fail; + + ret = enable_irq_wake(phydev->irq); + if (ret) { + dev_err(pdata->dev, "PHY enable irq wake failed, %d\n", + ret); + goto ether_prepare_fail; + } + /* enable device wake on WoL set */ + device_init_wakeup(&ndev->dev, true); + } + +ether_prepare_fail: + if (unlikely(ret)) + ether_revert_wol(dev); + + return ret; +} + +/** + * @brief Ethernet platform driver complete callback. + * + * Alogorithm: Revert the defer WOL settings if enabled by user + * + * @param[in] dev: Platform device associated with platform driver. + */ +static void ether_complete(struct device *dev) +{ + int ret; + struct net_device *ndev = dev_get_drvdata(dev); + struct ether_priv_data *pdata = netdev_priv(ndev); + struct phy_device *phydev = pdata->phydev; + + if (pdata->wol.wolopts) { + ret = ether_revert_wol(dev); + if (ret) { + dev_err(pdata->dev, "Fail to enable PHY network functionality %d\n", ret); + return; + } + + ret = disable_irq_wake(phydev->irq); + if (ret) { + dev_info(pdata->dev, + "PHY disable irq wake failed, %d\n", + ret); + } + /* disable device wake on WoL reset */ + device_init_wakeup(&ndev->dev, false); + } + + return; +} +#endif /* !(OSI_STRIPPED_LIB) */ + /** * @brief Ethernet platform driver resume call. * @@ -7035,10 +7136,14 @@ static int ether_resume_noirq(struct device *dev) return ret; } - return 0; + return ret; } static const struct dev_pm_ops ether_pm_ops = { +#ifndef OSI_STRIPPED_LIB + .prepare = ether_prepare, + .complete = ether_complete, +#endif /* !OSI_STRIPPED_LIB */ .suspend = ether_suspend_noirq, .resume = ether_resume_noirq, }; diff --git a/drivers/net/ethernet/nvidia/nvethernet/ether_linux.h b/drivers/net/ethernet/nvidia/nvethernet/ether_linux.h index 019959b6..51aff9d6 100644 --- a/drivers/net/ethernet/nvidia/nvethernet/ether_linux.h +++ b/drivers/net/ethernet/nvidia/nvethernet/ether_linux.h @@ -668,6 +668,8 @@ struct ether_priv_data { struct tasklet_struct lane_restart_task; /** xtra sw error counters */ struct ether_xtra_stat_counters xstats; + /** wol configs */ + struct ethtool_wolinfo wol; }; /** @@ -678,6 +680,21 @@ struct ether_priv_data { * @note Network device needs to created. */ void ether_set_ethtool_ops(struct net_device *ndev); + +/** + * @brief Configure WOL settings in PHY subsystem + * + * @param[in] ndev – pointer to net device structure. + * @param[in] wol – pointer to ethtool_wolinfo structure. + * + * @note MAC and PHY need to be initialized. + * + * @retval zero on success and -ve number on failure. + */ + +int ether_set_wol_impl(struct net_device *ndev, struct ethtool_wolinfo *wol); + + /** * @brief Creates Ethernet sysfs group * diff --git a/drivers/net/ethernet/nvidia/nvethernet/ethtool.c b/drivers/net/ethernet/nvidia/nvethernet/ethtool.c index 235d5bf1..e31fdae2 100644 --- a/drivers/net/ethernet/nvidia/nvethernet/ethtool.c +++ b/drivers/net/ethernet/nvidia/nvethernet/ethtool.c @@ -1336,6 +1336,38 @@ static int ether_set_eee(struct net_device *ndev, return 0; } +/** + * @brief Configure WOL settings in PHY subsystem + * + * Algorithm: Call phy subsystem to enable or disable + * Wake On Lan settings based on wol param + * + * @param[in] ndev – pointer to net device structure. + * @param[in] wol – pointer to ethtool_wolinfo structure. + * + * @note MAC and PHY need to be initialized. + * + * @retval zero on success and -ve number on failure. + */ + +int ether_set_wol_impl(struct net_device *ndev, struct ethtool_wolinfo *wol) +{ + struct ether_priv_data *pdata = netdev_priv(ndev); + struct phy_device *phydev = pdata->phydev; + + if (!phydev) { + netdev_err(pdata->ndev, + "%s: phydev is null check iface up status\n", + __func__); + return -ENOTSUPP; + } + + if (!phy_interrupt_is_valid(phydev)) + return -ENOTSUPP; + + return phy_ethtool_set_wol(pdata->phydev, wol); +} + /** * @brief This function is invoked by kernel when user request to set * pmt parameters for remote wakeup or magic wakeup @@ -1351,46 +1383,24 @@ static int ether_set_eee(struct net_device *ndev, */ static int ether_set_wol(struct net_device *ndev, struct ethtool_wolinfo *wol) { + int ret = 0; struct ether_priv_data *pdata = netdev_priv(ndev); - int ret; - if (!wol) return -EINVAL; - if (!pdata->phydev) { - netdev_err(pdata->ndev, - "%s: phydev is null check iface up status\n", - __func__); - return -ENOTSUPP; - } - - if (!phy_interrupt_is_valid(pdata->phydev)) - return -ENOTSUPP; - - ret = phy_ethtool_set_wol(pdata->phydev, wol); - if (ret < 0) - return ret; - + /* Disable WOL on demand. + * Enabling WOL will to deferred to before system suspend. + */ if (wol->wolopts) { - ret = enable_irq_wake(pdata->phydev->irq); - if (ret) { - dev_err(pdata->dev, "PHY enable irq wake failed, %d\n", - ret); - return ret; - } - /* enable device wake on WoL set */ - device_init_wakeup(&ndev->dev, true); - } else { - ret = disable_irq_wake(pdata->phydev->irq); - if (ret) { - dev_info(pdata->dev, - "PHY disable irq wake failed, %d\n", - ret); - } - /* disable device wake on WoL reset */ - device_init_wakeup(&ndev->dev, false); - } + /* The WOL request to the PHY layer is defered, and will apply on + * it only when the system is going to suspend. */ + memcpy(&pdata->wol, wol, sizeof(struct ethtool_wolinfo)); + } else { + /* Set wolopts to 0 to implicitly say that WOL is disabled */ + pdata->wol.wolopts = 0; + ret = ether_set_wol_impl(ndev, wol); + } return ret; } @@ -1411,6 +1421,7 @@ static int ether_set_wol(struct net_device *ndev, struct ethtool_wolinfo *wol) static void ether_get_wol(struct net_device *ndev, struct ethtool_wolinfo *wol) { struct ether_priv_data *pdata = netdev_priv(ndev); + u32 wolopts; if (!wol) return; @@ -1422,13 +1433,20 @@ static void ether_get_wol(struct net_device *ndev, struct ethtool_wolinfo *wol) return; } - wol->supported = 0; - wol->wolopts = 0; - if (!phy_interrupt_is_valid(pdata->phydev)) return; - phy_ethtool_get_wol(pdata->phydev, wol); + wolopts = pdata->wol.wolopts; + + pdata->wol.supported = 0; + pdata->wol.wolopts = 0; + + phy_ethtool_get_wol(pdata->phydev, &pdata->wol); + + if (wolopts & WAKE_MAGIC) { + pdata->wol.wolopts |= WAKE_MAGIC; + } + memcpy(wol, &pdata->wol, sizeof(struct ethtool_wolinfo)); } /**