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

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'net-add-phylink-managed-wol-and-convert-stmmac'

Russell King says:

====================
net: add phylink managed WoL and convert stmmac

This series is implementing the thoughts of Andrew, Florian and myself
to improve the quality of Wake-on-Lan (WoL) implementations.

This changes nothing for MAC drivers that do not wish to participate in
this, but if they do, then they gain the benefit of phylink configuring
WoL at the point closest to the media as possible.

We first need to solve the problem that the multitude of PHY drivers
report their device supports WoL, but are not capable of waking the
system. Correcting this is fundamental to choosing where WoL should be
enabled - a mis-reported WoL support can render WoL completely
ineffective.

The only PHY drivers which uses the driver model's wakeup support is
drivers/net/phy/broadcom.c, and until recently, realtek. This means
we have the opportunity for PHY drivers to be _correctly_ converted
to use this method of signalling wake-up capability only when they can
actually wake the system, and thus providing a way for phylink to
know whether to use PHY-based WoL at all.

However, a PHY driver not implementing that logic doesn't become a
blocker to MACs wanting to convert. In full, the logic is:

- phylink supports a flag, wol_phy_legacy, which forces phylink to use
the PHY-based WoL even if the MDIO device is not marked as wake-up
capable.

- when wol_phy_legacy is not set, we check whether the PHY MDIO device
is wake-up capable. If it is, we offer the WoL request to the PHY.

- if neither wol_phy_legacy is set, or the PHY is not wake-up capable,
we do not offer the WoL request to the PHY.

In both cases, after setting any PHY based WoL, we remove the options
that the PHY now reports are enabled from the options mask, and offer
these (if any) to the MAC. The mac will get a "mac_set_wol()" method
call when any settings change.

Phylink mainatains the WoL state for the MAC, so there's no need for
a "mac_get_wol()" method. There may be the need to set the initial
state but this is not supported at present.

I've also added support for doing the PHY speed-up/speed-down at
suspend/resume time depending on the WoL state, which takes another
issue from the MAC authors.

Lastly, with phylink now having the full picture for WoL, the
"mac_wol" argument for phylink_suspend() becomes redundant, and for
MAC drivers that implement mac_set_wol(), the value passed becomes
irrelevant.
====================

Link: https://patch.msgid.link/aPnyW54J80h9DmhB@shell.armlinux.org.uk
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

+182 -64
+1 -10
drivers/net/ethernet/stmicro/stmmac/stmmac.h
··· 291 291 int hw_cap_support; 292 292 int synopsys_id; 293 293 u32 msg_enable; 294 + /* Our MAC Wake-on-Lan options */ 294 295 int wolopts; 295 296 int wol_irq; 296 297 u32 gmii_address_bus_config; ··· 379 378 }; 380 379 381 380 extern const struct dev_pm_ops stmmac_simple_pm_ops; 382 - 383 - static inline bool stmmac_wol_enabled_mac(struct stmmac_priv *priv) 384 - { 385 - return priv->plat->pmt && device_may_wakeup(priv->device); 386 - } 387 - 388 - static inline bool stmmac_wol_enabled_phy(struct stmmac_priv *priv) 389 - { 390 - return !priv->plat->pmt && device_may_wakeup(priv->device); 391 - } 392 381 393 382 int stmmac_mdio_unregister(struct net_device *ndev); 394 383 int stmmac_mdio_register(struct net_device *ndev);
+2 -29
drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c
··· 724 724 { 725 725 struct stmmac_priv *priv = netdev_priv(dev); 726 726 727 - if (!priv->plat->pmt) 728 - return phylink_ethtool_get_wol(priv->phylink, wol); 729 - 730 - mutex_lock(&priv->lock); 731 - if (device_can_wakeup(priv->device)) { 732 - wol->supported = WAKE_MAGIC | WAKE_UCAST; 733 - if (priv->hw_cap_support && !priv->dma_cap.pmt_magic_frame) 734 - wol->supported &= ~WAKE_MAGIC; 735 - wol->wolopts = priv->wolopts; 736 - } 737 - mutex_unlock(&priv->lock); 727 + return phylink_ethtool_get_wol(priv->phylink, wol); 738 728 } 739 729 740 730 static int stmmac_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) 741 731 { 742 732 struct stmmac_priv *priv = netdev_priv(dev); 743 733 744 - if (!device_can_wakeup(priv->device)) 745 - return -EOPNOTSUPP; 746 - 747 - if (!priv->plat->pmt) { 748 - int ret = phylink_ethtool_set_wol(priv->phylink, wol); 749 - 750 - if (!ret) 751 - device_set_wakeup_enable(priv->device, !!wol->wolopts); 752 - return ret; 753 - } 754 - 755 - device_set_wakeup_enable(priv->device, !!wol->wolopts); 756 - 757 - mutex_lock(&priv->lock); 758 - priv->wolopts = wol->wolopts; 759 - mutex_unlock(&priv->lock); 760 - 761 - return 0; 734 + return phylink_ethtool_set_wol(priv->phylink, wol); 762 735 } 763 736 764 737 static int stmmac_ethtool_op_get_eee(struct net_device *dev,
+28 -17
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
··· 1073 1073 return 0; 1074 1074 } 1075 1075 1076 + static int stmmac_mac_wol_set(struct phylink_config *config, u32 wolopts, 1077 + const u8 *sopass) 1078 + { 1079 + struct stmmac_priv *priv = netdev_priv(to_net_dev(config->dev)); 1080 + 1081 + device_set_wakeup_enable(priv->device, !!wolopts); 1082 + 1083 + mutex_lock(&priv->lock); 1084 + priv->wolopts = wolopts; 1085 + mutex_unlock(&priv->lock); 1086 + 1087 + return 0; 1088 + } 1089 + 1076 1090 static const struct phylink_mac_ops stmmac_phylink_mac_ops = { 1077 1091 .mac_get_caps = stmmac_mac_get_caps, 1078 1092 .mac_select_pcs = stmmac_mac_select_pcs, ··· 1096 1082 .mac_link_up = stmmac_mac_link_up, 1097 1083 .mac_disable_tx_lpi = stmmac_mac_disable_tx_lpi, 1098 1084 .mac_enable_tx_lpi = stmmac_mac_enable_tx_lpi, 1085 + .mac_wol_set = stmmac_mac_wol_set, 1099 1086 }; 1100 1087 1101 1088 /** ··· 1205 1190 phylink_ethtool_set_eee(priv->phylink, &eee); 1206 1191 } 1207 1192 1208 - if (!priv->plat->pmt) { 1209 - struct ethtool_wolinfo wol = { .cmd = ETHTOOL_GWOL }; 1210 - 1211 - phylink_ethtool_get_wol(priv->phylink, &wol); 1212 - device_set_wakeup_capable(priv->device, !!wol.supported); 1213 - device_set_wakeup_enable(priv->device, !!wol.wolopts); 1214 - } 1215 - 1216 1193 return 0; 1217 1194 } 1218 1195 ··· 1271 1264 config->lpi_capabilities = ~(MAC_1000FD - 1) | MAC_100FD; 1272 1265 config->lpi_timer_default = eee_timer * 1000; 1273 1266 config->eee_enabled_default = true; 1267 + } 1268 + 1269 + config->wol_phy_speed_ctrl = true; 1270 + if (priv->plat->flags & STMMAC_FLAG_USE_PHY_WOL) { 1271 + config->wol_phy_legacy = true; 1272 + } else { 1273 + if (priv->dma_cap.pmt_remote_wake_up) 1274 + config->wol_mac_support |= WAKE_UCAST; 1275 + if (priv->dma_cap.pmt_magic_frame) 1276 + config->wol_mac_support |= WAKE_MAGIC; 1274 1277 } 1275 1278 1276 1279 fwnode = priv->plat->port_node; ··· 7777 7760 priv->plat->serdes_powerdown(ndev, priv->plat->bsp_priv); 7778 7761 7779 7762 /* Enable Power down mode by programming the PMT regs */ 7780 - if (stmmac_wol_enabled_mac(priv)) { 7763 + if (priv->wolopts) { 7781 7764 stmmac_pmt(priv, priv->hw, priv->wolopts); 7782 7765 priv->irq_wake = 1; 7783 7766 } else { ··· 7788 7771 mutex_unlock(&priv->lock); 7789 7772 7790 7773 rtnl_lock(); 7791 - if (stmmac_wol_enabled_phy(priv)) 7792 - phylink_speed_down(priv->phylink, false); 7793 - 7794 - phylink_suspend(priv->phylink, stmmac_wol_enabled_mac(priv)); 7774 + phylink_suspend(priv->phylink, !!priv->wolopts); 7795 7775 rtnl_unlock(); 7796 7776 7797 7777 if (stmmac_fpe_supported(priv)) ··· 7864 7850 * this bit because it can generate problems while resuming 7865 7851 * from another devices (e.g. serial console). 7866 7852 */ 7867 - if (stmmac_wol_enabled_mac(priv)) { 7853 + if (priv->wolopts) { 7868 7854 mutex_lock(&priv->lock); 7869 7855 stmmac_pmt(priv, priv->hw, 0); 7870 7856 mutex_unlock(&priv->lock); ··· 7926 7912 * workqueue thread, which will race with initialisation. 7927 7913 */ 7928 7914 phylink_resume(priv->phylink); 7929 - if (stmmac_wol_enabled_phy(priv)) 7930 - phylink_speed_up(priv->phylink); 7931 - 7932 7915 rtnl_unlock(); 7933 7916 7934 7917 netif_device_attach(ndev);
+2 -2
drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
··· 969 969 if (!netif_running(ndev)) 970 970 return 0; 971 971 972 - if (!stmmac_wol_enabled_mac(priv)) { 972 + if (!priv->wolopts) { 973 973 /* Disable clock in case of PWM is off */ 974 974 clk_disable_unprepare(priv->plat->clk_ptp_ref); 975 975 ··· 990 990 if (!netif_running(ndev)) 991 991 return 0; 992 992 993 - if (!stmmac_wol_enabled_mac(priv)) { 993 + if (!priv->wolopts) { 994 994 /* enable the clk previously disabled */ 995 995 ret = pm_runtime_force_resume(dev); 996 996 if (ret)
+12 -2
drivers/net/phy/phy_device.c
··· 251 251 return wol.wolopts != 0; 252 252 } 253 253 254 + bool phy_may_wakeup(struct phy_device *phydev) 255 + { 256 + /* If the PHY is using driver-model based wakeup, use that state. */ 257 + if (phy_can_wakeup(phydev)) 258 + return device_may_wakeup(&phydev->mdio.dev); 259 + 260 + return phy_drv_wol_enabled(phydev); 261 + } 262 + EXPORT_SYMBOL_GPL(phy_may_wakeup); 263 + 254 264 static void phy_link_change(struct phy_device *phydev, bool up) 255 265 { 256 266 struct net_device *netdev = phydev->attached_dev; ··· 312 302 /* If the PHY on the mido bus is not attached but has WOL enabled 313 303 * we cannot suspend the PHY. 314 304 */ 315 - if (!netdev && phy_drv_wol_enabled(phydev)) 305 + if (!netdev && phy_may_wakeup(phydev)) 316 306 return false; 317 307 318 308 /* PHY not attached? May suspend if the PHY has not already been ··· 1919 1909 if (phydev->suspended || !phydrv) 1920 1910 return 0; 1921 1911 1922 - phydev->wol_enabled = phy_drv_wol_enabled(phydev) || 1912 + phydev->wol_enabled = phy_may_wakeup(phydev) || 1923 1913 (netdev && netdev->ethtool->wol_enabled); 1924 1914 /* If the device has WOL enabled, we cannot suspend the PHY */ 1925 1915 if (phydev->wol_enabled && !(phydrv->flags & PHY_ALWAYS_CALL_SUSPEND))
+88 -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 + 2576 + static bool phylink_phy_pm_speed_ctrl(struct phylink *pl) 2577 + { 2578 + return pl->config->wol_phy_speed_ctrl && !pl->wolopts_mac && 2579 + pl->phydev && phy_may_wakeup(pl->phydev); 2580 + } 2581 + 2568 2582 /** 2569 2583 * phylink_suspend() - handle a network device suspend event 2570 2584 * @pl: a pointer to a &struct phylink returned from phylink_create() ··· 2595 2575 * can also bring down the link between the MAC and PHY. 2596 2576 * - If Wake-on-Lan is active, but being handled by the MAC, the MAC 2597 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.) 2598 2581 */ 2599 2582 void phylink_suspend(struct phylink *pl, bool mac_wol) 2600 2583 { 2601 2584 ASSERT_RTNL(); 2585 + 2586 + if (phylink_mac_supports_wol(pl)) 2587 + mac_wol = !!pl->wolopts_mac; 2602 2588 2603 2589 if (mac_wol && (!pl->netdev || pl->netdev->ethtool->wol_enabled)) { 2604 2590 /* Wake-on-Lan enabled, MAC handling */ ··· 2631 2605 } else { 2632 2606 phylink_stop(pl); 2633 2607 } 2608 + 2609 + if (phylink_phy_pm_speed_ctrl(pl)) 2610 + phylink_speed_down(pl, false); 2634 2611 } 2635 2612 EXPORT_SYMBOL_GPL(phylink_suspend); 2636 2613 ··· 2672 2643 void phylink_resume(struct phylink *pl) 2673 2644 { 2674 2645 ASSERT_RTNL(); 2646 + 2647 + if (phylink_phy_pm_speed_ctrl(pl)) 2648 + phylink_speed_up(pl); 2675 2649 2676 2650 if (test_bit(PHYLINK_DISABLE_MAC_WOL, &pl->phylink_disable_state)) { 2677 2651 /* Wake-on-Lan enabled, MAC handling */ ··· 2721 2689 wol->supported = 0; 2722 2690 wol->wolopts = 0; 2723 2691 2724 - if (pl->phydev) 2725 - 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 + } 2726 2710 } 2727 2711 EXPORT_SYMBOL_GPL(phylink_ethtool_get_wol); 2728 2712 ··· 2755 2707 */ 2756 2708 int phylink_ethtool_set_wol(struct phylink *pl, struct ethtool_wolinfo *wol) 2757 2709 { 2710 + struct ethtool_wolinfo w = { .cmd = ETHTOOL_GWOL }; 2758 2711 int ret = -EOPNOTSUPP; 2712 + bool changed; 2713 + u32 wolopts; 2759 2714 2760 2715 ASSERT_RTNL(); 2761 2716 2762 - if (pl->phydev) 2763 - 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 + } 2764 2752 2765 2753 return ret; 2766 2754 }
+21
include/linux/phy.h
··· 1379 1379 linkmode_clear_bit(link_mode, phydev->advertising_eee); 1380 1380 } 1381 1381 1382 + /** 1383 + * phy_can_wakeup() - indicate whether PHY has driver model wakeup capabilities 1384 + * @phydev: The phy_device struct 1385 + * 1386 + * Returns: true/false depending on the PHY driver's device_set_wakeup_capable() 1387 + * setting. 1388 + */ 1389 + static inline bool phy_can_wakeup(struct phy_device *phydev) 1390 + { 1391 + return device_can_wakeup(&phydev->mdio.dev); 1392 + } 1393 + 1394 + /** 1395 + * phy_may_wakeup() - indicate whether PHY has wakeup enabled 1396 + * @phydev: The phy_device struct 1397 + * 1398 + * Returns: true/false depending on the PHY driver's device_set_wakeup_enabled() 1399 + * setting if using the driver model, otherwise the legacy determination. 1400 + */ 1401 + bool phy_may_wakeup(struct phy_device *phydev); 1402 + 1382 1403 void phy_resolve_aneg_pause(struct phy_device *phydev); 1383 1404 void phy_resolve_aneg_linkmode(struct phy_device *phydev); 1384 1405
+28
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_phy_speed_ctrl: Use phy speed control on suspend/resume 161 + * @wol_mac_support: Bitmask of MAC supported %WAKE_* options 159 162 */ 160 163 struct phylink_config { 161 164 struct device *dev; ··· 176 173 unsigned long lpi_capabilities; 177 174 u32 lpi_timer_default; 178 175 bool eee_enabled_default; 176 + 177 + /* Wake-on-Lan support */ 178 + bool wol_phy_legacy; 179 + bool wol_phy_speed_ctrl; 180 + u32 wol_mac_support; 179 181 }; 180 182 181 183 void phylink_limit_mac_speed(struct phylink_config *config, u32 max_speed); ··· 196 188 * @mac_link_up: allow the link to come up. 197 189 * @mac_disable_tx_lpi: disable LPI. 198 190 * @mac_enable_tx_lpi: enable and configure LPI. 191 + * @mac_wol_set: configure Wake-on-Lan settings at the MAC. 199 192 * 200 193 * The individual methods are described more fully below. 201 194 */ ··· 220 211 void (*mac_disable_tx_lpi)(struct phylink_config *config); 221 212 int (*mac_enable_tx_lpi)(struct phylink_config *config, u32 timer, 222 213 bool tx_clk_stop); 214 + 215 + int (*mac_wol_set)(struct phylink_config *config, u32 wolopts, 216 + const u8 *sopass); 223 217 }; 224 218 225 219 #if 0 /* For kernel-doc purposes only. */ ··· 452 440 */ 453 441 int mac_enable_tx_lpi(struct phylink_config *config, u32 timer, 454 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); 455 459 #endif 456 460 457 461 struct phylink_pcs_ops;