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

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
Loading
Loading
Loading
Loading
+18 −18
Original line number Diff line number Diff line
@@ -7123,8 +7123,6 @@ int stmmac_suspend(struct device *dev)
	if (!ndev || !netif_running(ndev))
		return 0;

	phylink_mac_change(priv->phylink, false);

	mutex_lock(&priv->lock);

	netif_device_detach(ndev);
@@ -7150,14 +7148,6 @@ int stmmac_suspend(struct device *dev)
		stmmac_pmt(priv, priv->hw, priv->wolopts);
		priv->irq_wake = 1;
	} 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);
		pinctrl_pm_select_sleep_state(priv->device);
		/* Disable clock in case of PWM is off */
@@ -7171,6 +7161,16 @@ int stmmac_suspend(struct device *dev)

	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) {
		/* Disable FPE */
		stmmac_fpe_configure(priv, priv->ioaddr,
@@ -7261,13 +7261,15 @@ int stmmac_resume(struct device *dev)
			return ret;
	}

	if (!device_may_wakeup(priv->device) || !priv->plat->pmt) {
	rtnl_lock();
		phylink_start(priv->phylink);
		/* We may have called phylink_speed_down before */
	if (device_may_wakeup(priv->device) && priv->plat->pmt) {
		phylink_resume(priv->phylink);
	} else {
		phylink_resume(priv->phylink);
		if (device_may_wakeup(priv->device))
			phylink_speed_up(priv->phylink);
		rtnl_unlock();
	}
	rtnl_unlock();

	rtnl_lock();
	mutex_lock(&priv->lock);
@@ -7288,8 +7290,6 @@ int stmmac_resume(struct device *dev)
	mutex_unlock(&priv->lock);
	rtnl_unlock();

	phylink_mac_change(priv->phylink, true);

	netif_device_attach(ndev);

	return 0;
+82 −0
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@
enum {
	PHYLINK_DISABLE_STOPPED,
	PHYLINK_DISABLE_LINK,
	PHYLINK_DISABLE_MAC_WOL,
};

/**
@@ -1282,6 +1283,9 @@ EXPORT_SYMBOL_GPL(phylink_start);
 * 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
 * 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)
{
@@ -1301,6 +1305,84 @@ void phylink_stop(struct phylink *pl)
}
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
 * @pl: a pointer to a &struct phylink returned from phylink_create()
+3 −0
Original line number Diff line number Diff line
@@ -451,6 +451,9 @@ void phylink_mac_change(struct phylink *, bool up);
void phylink_start(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 *);
int phylink_ethtool_set_wol(struct phylink *, struct ethtool_wolinfo *);