enum {
        PHYLINK_DISABLE_STOPPED,
        PHYLINK_DISABLE_LINK,
+       PHYLINK_DISABLE_MAC_WOL,
 };
 
 /**
  * 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)
 {
 }
 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()