Phylink treats a MAC which implements its LPI operations but advertises no LPI capability as incapable of EEE. It disables PHY EEE during attach and rejects ethtool EEE requests. The same problem occurs when the MAC supports LPI on another interface but not on the interface currently connected to the PHY. Both cases are incorrect when the attached PHY can generate LPI autonomously without help from the MAC. Recognize the PHY autonomous-EEE capability, preserve its EEE advertisement, and allow ethtool EEE requests in that configuration. Evaluate MAC LPI support for the active interface, and restrict EEE modes to the MAC capability mask only when the MAC actually manages LPI. Signed-off-by: James Hilliard --- drivers/net/phy/phylink.c | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index 18d2ead97aa5..e64f72793aa0 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -1512,6 +1512,19 @@ static void phylink_deactivate_lpi(struct phylink *pl) } } +static bool phylink_phy_supports_autonomous_eee(struct phy_device *phy) +{ + return phy && phy->drv && phy->drv->set_tx_lpi; +} + +static bool +phylink_mac_supports_eee_interface(struct phylink *pl, + phy_interface_t interface) +{ + return pl->mac_supports_eee && + test_bit(interface, pl->config->lpi_interfaces); +} + static void phylink_activate_lpi(struct phylink *pl) { int err; @@ -2090,6 +2103,7 @@ static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy, { struct phylink_link_state config; __ETHTOOL_DECLARE_LINK_MODE_MASK(supported); + bool mac_eee; char *irq_str; int ret; @@ -2106,6 +2120,7 @@ static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy, linkmode_copy(supported, phy->supported); linkmode_copy(config.advertising, phy->advertising); config.interface = interface; + mac_eee = phylink_mac_supports_eee_interface(pl, interface); ret = phylink_validate_phy(pl, phy, supported, &config); if (ret) { @@ -2144,7 +2159,7 @@ static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy, /* If the MAC supports phylink managed EEE, restrict the EEE * advertisement according to the MAC's LPI capabilities. */ - if (pl->mac_supports_eee) { + if (mac_eee) { /* If EEE is enabled, then we need to call phy_support_eee() * to ensure that the advertising mask is appropriately set. * This also enables EEE at the PHY. @@ -2165,7 +2180,8 @@ static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy, */ linkmode_and(phy->advertising_eee, phy->advertising_eee, pl->supported_lpi); - } else if (pl->mac_supports_eee_ops) { + } else if (pl->mac_supports_eee_ops && + !phylink_phy_supports_autonomous_eee(phy)) { /* MAC supports phylink EEE, but wants EEE always disabled. */ phy_disable_eee(phy); } @@ -3300,17 +3316,20 @@ EXPORT_SYMBOL_GPL(phylink_get_eee_err); */ int phylink_ethtool_get_eee(struct phylink *pl, struct ethtool_keee *eee) { + bool mac_eee; int ret = -EOPNOTSUPP; ASSERT_RTNL(); + mac_eee = phylink_mac_supports_eee_interface(pl, pl->cur_interface); - if (pl->mac_supports_eee_ops && !pl->mac_supports_eee) + if (pl->mac_supports_eee_ops && !mac_eee && + !phylink_phy_supports_autonomous_eee(pl->phydev)) return ret; if (pl->phydev) { ret = phy_ethtool_get_eee(pl->phydev, eee); /* Restrict supported linkmode mask */ - if (ret == 0 && pl->mac_supports_eee_ops) + if (ret == 0 && mac_eee) linkmode_and(eee->supported, eee->supported, pl->supported_lpi); } @@ -3326,10 +3345,11 @@ EXPORT_SYMBOL_GPL(phylink_ethtool_get_eee); */ int phylink_ethtool_set_eee(struct phylink *pl, struct ethtool_keee *eee) { - bool mac_eee = pl->mac_supports_eee; + bool mac_eee; int ret = -EOPNOTSUPP; ASSERT_RTNL(); + mac_eee = phylink_mac_supports_eee_interface(pl, pl->cur_interface); phylink_dbg(pl, "mac %s phylink EEE%s, adv %*pbl, LPI%s timer %uus\n", mac_eee ? "supports" : "does not support", @@ -3337,12 +3357,13 @@ int phylink_ethtool_set_eee(struct phylink *pl, struct ethtool_keee *eee) __ETHTOOL_LINK_MODE_MASK_NBITS, eee->advertised, eee->tx_lpi_enabled ? " enabled" : "", eee->tx_lpi_timer); - if (pl->mac_supports_eee_ops && !mac_eee) + if (pl->mac_supports_eee_ops && !mac_eee && + !phylink_phy_supports_autonomous_eee(pl->phydev)) return ret; if (pl->phydev) { /* Restrict advertisement mask */ - if (pl->mac_supports_eee_ops) + if (mac_eee) linkmode_and(eee->advertised, eee->advertised, pl->supported_lpi); ret = phy_ethtool_set_eee(pl->phydev, eee); -- 2.53.0