nvethernet: defer WOL enablement.

This change defers WOL enablement to the moment
before suspending so the network retains connected
after enabling WOL.

ethtool -s eth0 wol g

Bug 4450559

Change-Id: Ib5e59052fb72a9c4f88667ab6182633af545b99d
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/3271298
Tested-by: Revanth Kumar Uppala <ruppala@nvidia.com>
Reviewed-by: Revanth Kumar Uppala <ruppala@nvidia.com>
GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
Reviewed-by: Bhadram Varka <vbhadram@nvidia.com>
Reviewed-by: Srinivas Ramachandran <srinivasra@nvidia.com>
This commit is contained in:
Robert Lin
2024-01-11 06:36:03 +00:00
committed by mobile promotions
parent e2b9c86688
commit 0652332c19
3 changed files with 179 additions and 39 deletions

View File

@@ -6831,6 +6831,107 @@ static void ether_shutdown(struct platform_device *pdev)
} }
#ifdef CONFIG_PM #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. * @brief Ethernet platform driver resume call.
* *
@@ -7035,10 +7136,14 @@ static int ether_resume_noirq(struct device *dev)
return ret; return ret;
} }
return 0; return ret;
} }
static const struct dev_pm_ops ether_pm_ops = { 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, .suspend = ether_suspend_noirq,
.resume = ether_resume_noirq, .resume = ether_resume_noirq,
}; };

View File

@@ -668,6 +668,8 @@ struct ether_priv_data {
struct tasklet_struct lane_restart_task; struct tasklet_struct lane_restart_task;
/** xtra sw error counters */ /** xtra sw error counters */
struct ether_xtra_stat_counters xstats; 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. * @note Network device needs to created.
*/ */
void ether_set_ethtool_ops(struct net_device *ndev); 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 * @brief Creates Ethernet sysfs group
* *

View File

@@ -1336,6 +1336,38 @@ static int ether_set_eee(struct net_device *ndev,
return 0; 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 * @brief This function is invoked by kernel when user request to set
* pmt parameters for remote wakeup or magic wakeup * 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) static int ether_set_wol(struct net_device *ndev, struct ethtool_wolinfo *wol)
{ {
int ret = 0;
struct ether_priv_data *pdata = netdev_priv(ndev); struct ether_priv_data *pdata = netdev_priv(ndev);
int ret;
if (!wol) if (!wol)
return -EINVAL; return -EINVAL;
if (!pdata->phydev) { /* Disable WOL on demand.
netdev_err(pdata->ndev, * Enabling WOL will to deferred to before system suspend.
"%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;
if (wol->wolopts) { if (wol->wolopts) {
ret = enable_irq_wake(pdata->phydev->irq); /* The WOL request to the PHY layer is defered, and will apply on
if (ret) { * it only when the system is going to suspend. */
dev_err(pdata->dev, "PHY enable irq wake failed, %d\n", memcpy(&pdata->wol, wol, sizeof(struct ethtool_wolinfo));
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);
}
} 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; 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) static void ether_get_wol(struct net_device *ndev, struct ethtool_wolinfo *wol)
{ {
struct ether_priv_data *pdata = netdev_priv(ndev); struct ether_priv_data *pdata = netdev_priv(ndev);
u32 wolopts;
if (!wol) if (!wol)
return; return;
@@ -1422,13 +1433,20 @@ static void ether_get_wol(struct net_device *ndev, struct ethtool_wolinfo *wol)
return; return;
} }
wol->supported = 0;
wol->wolopts = 0;
if (!phy_interrupt_is_valid(pdata->phydev)) if (!phy_interrupt_is_valid(pdata->phydev))
return; 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));
} }
/** /**