Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

net: phylink: add phylink managed MAC Wake-on-Lan support

Add core phylink managed Wake-on-Lan support, which is enabled when the
MAC driver fills in the new .mac_wol_set() method that this commit
creates.

When this feature is disabled, phylink acts as it has in the past,
merely passing the ethtool WoL calls to phylib whenever a PHY exists.
No other new functionality provided by this commit is enabled.

When this feature is enabled, a more inteligent approach is used.
Phylink will first pass WoL options to the PHY, read them back, and
attempt to set any options that were not set at the PHY at the MAC.

Since we have PHY drivers that report they support WoL, and accept WoL
configuration even though they aren't wired up to be capable of waking
the system, we need a way to differentiate between PHYs that think
they support WoL and those which actually do. As PHY drivers do not
make use of the driver model's wake-up infrastructure, but could, we
use this to determine whether PHY drivers can participate. This gives
a path forward where, as MAC drivers are converted to this, it
encourages PHY drivers to also be converted.

Phylink will also ignore the mac_wol argument to phylink_suspend() as
it now knows the WoL state at the MAC.

MAC drivers are expected to record/configure the Wake-on-Lan state in
their .mac_set_wol() method, and deal appropriately with it in their
suspend/resume methods. The driver model provides assistance to set the
IRQ wake support which may assist driver authors in achieving the
necessary configuration.

Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
Link: https://patch.msgid.link/E1vBrR2-0000000BLzU-1xYL@rmk-PC.armlinux.org.uk
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

Russell King (Oracle) and committed by
Jakub Kicinski
b79fbd86 b344bfac

+102 -4
+76 -4
drivers/net/phy/phylink.c
··· 93 93 u8 sfp_port; 94 94 95 95 struct eee_config eee_cfg; 96 + 97 + u32 wolopts_mac; 98 + u8 wol_sopass[SOPASS_MAX]; 96 99 }; 97 100 98 101 #define phylink_printk(level, pl, fmt, ...) \ ··· 2565 2562 } 2566 2563 EXPORT_SYMBOL_GPL(phylink_rx_clk_stop_unblock); 2567 2564 2565 + static bool phylink_mac_supports_wol(struct phylink *pl) 2566 + { 2567 + return !!pl->mac_ops->mac_wol_set; 2568 + } 2569 + 2570 + static bool phylink_phy_supports_wol(struct phylink *pl, 2571 + struct phy_device *phydev) 2572 + { 2573 + return phydev && (pl->config->wol_phy_legacy || phy_can_wakeup(phydev)); 2574 + } 2575 + 2568 2576 /** 2569 2577 * phylink_suspend() - handle a network device suspend event 2570 2578 * @pl: a pointer to a &struct phylink returned from phylink_create() ··· 2589 2575 * can also bring down the link between the MAC and PHY. 2590 2576 * - If Wake-on-Lan is active, but being handled by the MAC, the MAC 2591 2577 * still needs to receive packets, so we can not bring the link down. 2578 + * 2579 + * Note: when phylink managed Wake-on-Lan is in use, @mac_wol is ignored. 2580 + * (struct phylink_mac_ops.mac_set_wol populated.) 2592 2581 */ 2593 2582 void phylink_suspend(struct phylink *pl, bool mac_wol) 2594 2583 { 2595 2584 ASSERT_RTNL(); 2585 + 2586 + if (phylink_mac_supports_wol(pl)) 2587 + mac_wol = !!pl->wolopts_mac; 2596 2588 2597 2589 if (mac_wol && (!pl->netdev || pl->netdev->ethtool->wol_enabled)) { 2598 2590 /* Wake-on-Lan enabled, MAC handling */ ··· 2709 2689 wol->supported = 0; 2710 2690 wol->wolopts = 0; 2711 2691 2712 - if (pl->phydev) 2713 - phy_ethtool_get_wol(pl->phydev, wol); 2692 + if (phylink_mac_supports_wol(pl)) { 2693 + if (phylink_phy_supports_wol(pl, pl->phydev)) 2694 + phy_ethtool_get_wol(pl->phydev, wol); 2695 + 2696 + /* Where the MAC augments the WoL support, merge its support and 2697 + * current configuration. 2698 + */ 2699 + if (~wol->wolopts & pl->wolopts_mac & WAKE_MAGICSECURE) 2700 + memcpy(wol->sopass, pl->wol_sopass, 2701 + sizeof(wol->sopass)); 2702 + 2703 + wol->supported |= pl->config->wol_mac_support; 2704 + wol->wolopts |= pl->wolopts_mac; 2705 + } else { 2706 + /* Legacy */ 2707 + if (pl->phydev) 2708 + phy_ethtool_get_wol(pl->phydev, wol); 2709 + } 2714 2710 } 2715 2711 EXPORT_SYMBOL_GPL(phylink_ethtool_get_wol); 2716 2712 ··· 2743 2707 */ 2744 2708 int phylink_ethtool_set_wol(struct phylink *pl, struct ethtool_wolinfo *wol) 2745 2709 { 2710 + struct ethtool_wolinfo w = { .cmd = ETHTOOL_GWOL }; 2746 2711 int ret = -EOPNOTSUPP; 2712 + bool changed; 2713 + u32 wolopts; 2747 2714 2748 2715 ASSERT_RTNL(); 2749 2716 2750 - if (pl->phydev) 2751 - ret = phy_ethtool_set_wol(pl->phydev, wol); 2717 + if (phylink_mac_supports_wol(pl)) { 2718 + wolopts = wol->wolopts; 2719 + 2720 + if (phylink_phy_supports_wol(pl, pl->phydev)) { 2721 + ret = phy_ethtool_set_wol(pl->phydev, wol); 2722 + if (ret != 0 && ret != -EOPNOTSUPP) 2723 + return ret; 2724 + 2725 + phy_ethtool_get_wol(pl->phydev, &w); 2726 + 2727 + /* Any Wake-on-Lan modes which the PHY is handling 2728 + * should not be passed on to the MAC. 2729 + */ 2730 + wolopts &= ~w.wolopts; 2731 + } 2732 + 2733 + wolopts &= pl->config->wol_mac_support; 2734 + changed = pl->wolopts_mac != wolopts; 2735 + if (wolopts & WAKE_MAGICSECURE) 2736 + changed |= !!memcmp(wol->sopass, pl->wol_sopass, 2737 + sizeof(wol->sopass)); 2738 + memcpy(pl->wol_sopass, wol->sopass, sizeof(pl->wol_sopass)); 2739 + 2740 + if (changed) { 2741 + ret = pl->mac_ops->mac_wol_set(pl->config, wolopts, 2742 + wol->sopass); 2743 + if (!ret) 2744 + pl->wolopts_mac = wolopts; 2745 + } else { 2746 + ret = 0; 2747 + } 2748 + } else { 2749 + if (pl->phydev) 2750 + ret = phy_ethtool_set_wol(pl->phydev, wol); 2751 + } 2752 2752 2753 2753 return ret; 2754 2754 }
+26
include/linux/phylink.h
··· 156 156 * @lpi_capabilities: MAC speeds which can support LPI signalling 157 157 * @lpi_timer_default: Default EEE LPI timer setting. 158 158 * @eee_enabled_default: If set, EEE will be enabled by phylink at creation time 159 + * @wol_phy_legacy: Use Wake-on-Lan with PHY even if phy_can_wakeup() is false 160 + * @wol_mac_support: Bitmask of MAC supported %WAKE_* options 159 161 */ 160 162 struct phylink_config { 161 163 struct device *dev; ··· 175 173 unsigned long lpi_capabilities; 176 174 u32 lpi_timer_default; 177 175 bool eee_enabled_default; 176 + 177 + /* Wake-on-Lan support */ 178 + bool wol_phy_legacy; 179 + u32 wol_mac_support; 178 180 }; 179 181 180 182 void phylink_limit_mac_speed(struct phylink_config *config, u32 max_speed); ··· 194 188 * @mac_link_up: allow the link to come up. 195 189 * @mac_disable_tx_lpi: disable LPI. 196 190 * @mac_enable_tx_lpi: enable and configure LPI. 191 + * @mac_wol_set: configure Wake-on-Lan settings at the MAC. 197 192 * 198 193 * The individual methods are described more fully below. 199 194 */ ··· 218 211 void (*mac_disable_tx_lpi)(struct phylink_config *config); 219 212 int (*mac_enable_tx_lpi)(struct phylink_config *config, u32 timer, 220 213 bool tx_clk_stop); 214 + 215 + int (*mac_wol_set)(struct phylink_config *config, u32 wolopts, 216 + const u8 *sopass); 221 217 }; 222 218 223 219 #if 0 /* For kernel-doc purposes only. */ ··· 450 440 */ 451 441 int mac_enable_tx_lpi(struct phylink_config *config, u32 timer, 452 442 bool tx_clk_stop); 443 + 444 + /** 445 + * mac_wol_set() - configure the Wake-on-Lan parameters 446 + * @config: a pointer to a &struct phylink_config. 447 + * @wolopts: Bitmask of %WAKE_* flags for enabled Wake-On-Lan modes. 448 + * @sopass: SecureOn(tm) password; meaningful only for %WAKE_MAGICSECURE 449 + * 450 + * Enable the specified Wake-on-Lan options at the MAC. Options that the 451 + * PHY can handle will have been removed from @wolopts. 452 + * 453 + * The presence of this method enables phylink-managed WoL support. 454 + * 455 + * Returns: 0 on success. 456 + */ 457 + int (*mac_wol_set)(struct phylink_config *config, u32 wolopts, 458 + const u8 *sopass); 453 459 #endif 454 460 455 461 struct phylink_pcs_ops;