mirror of
git://nv-tegra.nvidia.com/linux-nv-oot.git
synced 2025-12-22 09:11:26 +03:00
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:
committed by
mobile promotions
parent
e2b9c86688
commit
0652332c19
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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
|
||||
*
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user