Commit d1bf7338 authored by David S. Miller's avatar David S. Miller

Merge branch 'stmmac-wol-fix'

Joakim Zhang says:

====================
net: stmmac: fix WoL issue

This patch set fixes stmmac not working after system resume back with WoL
active. Thanks a lot for Russell King keeps looking into this issue.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 0341d5e3 90702dcd
...@@ -7123,8 +7123,6 @@ int stmmac_suspend(struct device *dev) ...@@ -7123,8 +7123,6 @@ int stmmac_suspend(struct device *dev)
if (!ndev || !netif_running(ndev)) if (!ndev || !netif_running(ndev))
return 0; return 0;
phylink_mac_change(priv->phylink, false);
mutex_lock(&priv->lock); mutex_lock(&priv->lock);
netif_device_detach(ndev); netif_device_detach(ndev);
...@@ -7150,14 +7148,6 @@ int stmmac_suspend(struct device *dev) ...@@ -7150,14 +7148,6 @@ int stmmac_suspend(struct device *dev)
stmmac_pmt(priv, priv->hw, priv->wolopts); stmmac_pmt(priv, priv->hw, priv->wolopts);
priv->irq_wake = 1; priv->irq_wake = 1;
} else { } else {
mutex_unlock(&priv->lock);
rtnl_lock();
if (device_may_wakeup(priv->device))
phylink_speed_down(priv->phylink, false);
phylink_stop(priv->phylink);
rtnl_unlock();
mutex_lock(&priv->lock);
stmmac_mac_set(priv, priv->ioaddr, false); stmmac_mac_set(priv, priv->ioaddr, false);
pinctrl_pm_select_sleep_state(priv->device); pinctrl_pm_select_sleep_state(priv->device);
/* Disable clock in case of PWM is off */ /* Disable clock in case of PWM is off */
...@@ -7171,6 +7161,16 @@ int stmmac_suspend(struct device *dev) ...@@ -7171,6 +7161,16 @@ int stmmac_suspend(struct device *dev)
mutex_unlock(&priv->lock); mutex_unlock(&priv->lock);
rtnl_lock();
if (device_may_wakeup(priv->device) && priv->plat->pmt) {
phylink_suspend(priv->phylink, true);
} else {
if (device_may_wakeup(priv->device))
phylink_speed_down(priv->phylink, false);
phylink_suspend(priv->phylink, false);
}
rtnl_unlock();
if (priv->dma_cap.fpesel) { if (priv->dma_cap.fpesel) {
/* Disable FPE */ /* Disable FPE */
stmmac_fpe_configure(priv, priv->ioaddr, stmmac_fpe_configure(priv, priv->ioaddr,
...@@ -7261,13 +7261,15 @@ int stmmac_resume(struct device *dev) ...@@ -7261,13 +7261,15 @@ int stmmac_resume(struct device *dev)
return ret; return ret;
} }
if (!device_may_wakeup(priv->device) || !priv->plat->pmt) { rtnl_lock();
rtnl_lock(); if (device_may_wakeup(priv->device) && priv->plat->pmt) {
phylink_start(priv->phylink); phylink_resume(priv->phylink);
/* We may have called phylink_speed_down before */ } else {
phylink_speed_up(priv->phylink); phylink_resume(priv->phylink);
rtnl_unlock(); if (device_may_wakeup(priv->device))
phylink_speed_up(priv->phylink);
} }
rtnl_unlock();
rtnl_lock(); rtnl_lock();
mutex_lock(&priv->lock); mutex_lock(&priv->lock);
...@@ -7288,8 +7290,6 @@ int stmmac_resume(struct device *dev) ...@@ -7288,8 +7290,6 @@ int stmmac_resume(struct device *dev)
mutex_unlock(&priv->lock); mutex_unlock(&priv->lock);
rtnl_unlock(); rtnl_unlock();
phylink_mac_change(priv->phylink, true);
netif_device_attach(ndev); netif_device_attach(ndev);
return 0; return 0;
......
...@@ -33,6 +33,7 @@ ...@@ -33,6 +33,7 @@
enum { enum {
PHYLINK_DISABLE_STOPPED, PHYLINK_DISABLE_STOPPED,
PHYLINK_DISABLE_LINK, PHYLINK_DISABLE_LINK,
PHYLINK_DISABLE_MAC_WOL,
}; };
/** /**
...@@ -1282,6 +1283,9 @@ EXPORT_SYMBOL_GPL(phylink_start); ...@@ -1282,6 +1283,9 @@ EXPORT_SYMBOL_GPL(phylink_start);
* network device driver's &struct net_device_ops ndo_stop() method. The * network device driver's &struct net_device_ops ndo_stop() method. The
* network device's carrier state should not be changed prior to calling this * network device's carrier state should not be changed prior to calling this
* function. * function.
*
* This will synchronously bring down the link if the link is not already
* down (in other words, it will trigger a mac_link_down() method call.)
*/ */
void phylink_stop(struct phylink *pl) void phylink_stop(struct phylink *pl)
{ {
...@@ -1301,6 +1305,84 @@ void phylink_stop(struct phylink *pl) ...@@ -1301,6 +1305,84 @@ void phylink_stop(struct phylink *pl)
} }
EXPORT_SYMBOL_GPL(phylink_stop); EXPORT_SYMBOL_GPL(phylink_stop);
/**
* phylink_suspend() - handle a network device suspend event
* @pl: a pointer to a &struct phylink returned from phylink_create()
* @mac_wol: true if the MAC needs to receive packets for Wake-on-Lan
*
* Handle a network device suspend event. There are several cases:
* - If Wake-on-Lan is not active, we can bring down the link between
* the MAC and PHY by calling phylink_stop().
* - If Wake-on-Lan is active, and being handled only by the PHY, we
* can also bring down the link between the MAC and PHY.
* - If Wake-on-Lan is active, but being handled by the MAC, the MAC
* still needs to receive packets, so we can not bring the link down.
*/
void phylink_suspend(struct phylink *pl, bool mac_wol)
{
ASSERT_RTNL();
if (mac_wol && (!pl->netdev || pl->netdev->wol_enabled)) {
/* Wake-on-Lan enabled, MAC handling */
mutex_lock(&pl->state_mutex);
/* Stop the resolver bringing the link up */
__set_bit(PHYLINK_DISABLE_MAC_WOL, &pl->phylink_disable_state);
/* Disable the carrier, to prevent transmit timeouts,
* but one would hope all packets have been sent. This
* also means phylink_resolve() will do nothing.
*/
netif_carrier_off(pl->netdev);
/* We do not call mac_link_down() here as we want the
* link to remain up to receive the WoL packets.
*/
mutex_unlock(&pl->state_mutex);
} else {
phylink_stop(pl);
}
}
EXPORT_SYMBOL_GPL(phylink_suspend);
/**
* phylink_resume() - handle a network device resume event
* @pl: a pointer to a &struct phylink returned from phylink_create()
*
* Undo the effects of phylink_suspend(), returning the link to an
* operational state.
*/
void phylink_resume(struct phylink *pl)
{
ASSERT_RTNL();
if (test_bit(PHYLINK_DISABLE_MAC_WOL, &pl->phylink_disable_state)) {
/* Wake-on-Lan enabled, MAC handling */
/* Call mac_link_down() so we keep the overall state balanced.
* Do this under the state_mutex lock for consistency. This
* will cause a "Link Down" message to be printed during
* resume, which is harmless - the true link state will be
* printed when we run a resolve.
*/
mutex_lock(&pl->state_mutex);
phylink_link_down(pl);
mutex_unlock(&pl->state_mutex);
/* Re-apply the link parameters so that all the settings get
* restored to the MAC.
*/
phylink_mac_initial_config(pl, true);
/* Re-enable and re-resolve the link parameters */
clear_bit(PHYLINK_DISABLE_MAC_WOL, &pl->phylink_disable_state);
phylink_run_resolve(pl);
} else {
phylink_start(pl);
}
}
EXPORT_SYMBOL_GPL(phylink_resume);
/** /**
* phylink_ethtool_get_wol() - get the wake on lan parameters for the PHY * phylink_ethtool_get_wol() - get the wake on lan parameters for the PHY
* @pl: a pointer to a &struct phylink returned from phylink_create() * @pl: a pointer to a &struct phylink returned from phylink_create()
......
...@@ -451,6 +451,9 @@ void phylink_mac_change(struct phylink *, bool up); ...@@ -451,6 +451,9 @@ void phylink_mac_change(struct phylink *, bool up);
void phylink_start(struct phylink *); void phylink_start(struct phylink *);
void phylink_stop(struct phylink *); void phylink_stop(struct phylink *);
void phylink_suspend(struct phylink *pl, bool mac_wol);
void phylink_resume(struct phylink *pl);
void phylink_ethtool_get_wol(struct phylink *, struct ethtool_wolinfo *); void phylink_ethtool_get_wol(struct phylink *, struct ethtool_wolinfo *);
int phylink_ethtool_set_wol(struct phylink *, struct ethtool_wolinfo *); int phylink_ethtool_set_wol(struct phylink *, struct ethtool_wolinfo *);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment