From e64c16519dd594611545bb5d3c7dc6811bcfcfef Mon Sep 17 00:00:00 2001 From: "Russell King (Oracle)" Date: Wed, 31 May 2023 10:02:35 +0100 Subject: net: phylink: add EEE management Add EEE management to phylink. Signed-off-by: Russell King (Oracle) --- drivers/net/phy/phylink.c | 144 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 139 insertions(+), 5 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index 91985694c3c0..10df48be457f 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -86,6 +86,9 @@ struct phylink { DECLARE_PHY_INTERFACE_MASK(sfp_interfaces); __ETHTOOL_DECLARE_LINK_MODE_MASK(sfp_support); u8 sfp_port; + + struct eee_config eee_cfg; + bool eee_active; }; #define phylink_printk(level, pl, fmt, ...) \ @@ -1365,6 +1368,75 @@ static const char *phylink_pause_to_str(int pause) } } +static void phylink_disable_tx_lpi(struct phylink *pl) +{ + phylink_dbg(pl, "disabling tx_lpi\n"); + + if (pl->mac_ops->mac_disable_tx_lpi) + pl->mac_ops->mac_disable_tx_lpi(pl->config); +} + +static void phylink_enable_tx_lpi(struct phylink *pl) +{ + phylink_dbg(pl, "enabling tx_lpi, timer %uus\n", + pl->eee_cfg.tx_lpi_timer); + + if (pl->mac_ops->mac_enable_tx_lpi) + pl->mac_ops->mac_enable_tx_lpi(pl->config, + pl->eee_cfg.tx_lpi_timer); +} + +static bool phylink_eee_is_active(struct phylink *pl) +{ + return phylink_init_eee(pl, pl->config->eee_clk_stop_enable) >= 0; +} + +static void phylink_deactivate_eee(struct phylink *pl) +{ + phylink_dbg(pl, "deactivating EEE, was %sactive\n", + pl->eee_active ? "" : "in"); + + if (pl->eee_active) { + pl->eee_active = false; + phylink_disable_tx_lpi(pl); + } +} + +static void phylink_activate_eee(struct phylink *pl) +{ + pl->eee_active = phylink_eee_is_active(pl); + + phylink_dbg(pl, "can LPI, EEE enabled, %sactive\n", + pl->eee_active ? "" : "in"); + + if (pl->eee_active) + phylink_enable_tx_lpi(pl); +} + +/* Determine whether the MAC has new EEE support. We detect this by checking + * for the two new methods being present, but for DSA it will populate these + * anyway, so also check that lpi_capabilities is non-zero. + */ +static bool phylink_mac_supports_eee(struct phylink *pl) +{ + return pl->mac_ops->mac_disable_tx_lpi && + pl->mac_ops->mac_enable_tx_lpi && + pl->config->lpi_capabilities; +} + +static void phylink_phy_restrict_eee(struct phylink *pl, struct phy_device *phy) +{ + __ETHTOOL_DECLARE_LINK_MODE_MASK(eee_supported); + + /* Convert the MAC's LPI capabilities to linkmodes */ + linkmode_zero(eee_supported); + phylink_caps_to_linkmodes(eee_supported, pl->config->lpi_capabilities); + + /* Mask out EEE modes that are not supported */ + linkmode_and(phy->supported_eee, phy->supported_eee, eee_supported); + linkmode_and(phy->advertising_eee, phy->advertising_eee, eee_supported); +} + static void phylink_link_up(struct phylink *pl, struct phylink_link_state link_state) { @@ -1411,6 +1483,9 @@ static void phylink_link_up(struct phylink *pl, pl->cur_interface, speed, duplex, !!(link_state.pause & MLO_PAUSE_TX), rx_pause); + if (eeecfg_mac_can_tx_lpi(&pl->eee_cfg)) + phylink_activate_eee(pl); + if (ndev) netif_carrier_on(ndev); @@ -1427,25 +1502,29 @@ static void phylink_link_down(struct phylink *pl) if (ndev) netif_carrier_off(ndev); + + phylink_deactivate_eee(pl); + pl->mac_ops->mac_link_down(pl->config, pl->cur_link_an_mode, pl->cur_interface); phylink_info(pl, "Link is Down\n"); } +static bool phylink_link_is_up(struct phylink *pl) +{ + return pl->netdev ? netif_carrier_ok(pl->netdev) : pl->old_link_state; +} + static void phylink_resolve(struct work_struct *w) { struct phylink *pl = container_of(w, struct phylink, resolve); struct phylink_link_state link_state; - struct net_device *ndev = pl->netdev; bool mac_config = false; bool retrigger = false; bool cur_link_state; mutex_lock(&pl->state_mutex); - if (pl->netdev) - cur_link_state = netif_carrier_ok(ndev); - else - cur_link_state = pl->old_link_state; + cur_link_state = phylink_link_is_up(pl); if (pl->phylink_disable_state) { pl->mac_link_dropped = false; @@ -1683,6 +1762,9 @@ struct phylink *phylink_create(struct phylink_config *config, linkmode_copy(pl->link_config.advertising, pl->supported); phylink_validate(pl, pl->supported, &pl->link_config); + /* Set the default EEE configuration */ + pl->eee_cfg = pl->config->eee; + ret = phylink_parse_mode(pl, fwnode); if (ret < 0) { kfree(pl); @@ -1905,6 +1987,13 @@ static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy, /* Restrict the phy advertisement according to the MAC support. */ linkmode_copy(phy->advertising, config.advertising); + + /* If the MAC supports phylink managed EEE, restrict the EEE + * advertisement according to the MAC's LPI capabilities. + */ + if (phylink_mac_supports_eee(pl)) + phylink_phy_restrict_eee(pl, phy); + mutex_unlock(&pl->state_mutex); mutex_unlock(&phy->lock); @@ -2778,6 +2867,12 @@ int phylink_ethtool_get_eee(struct phylink *pl, struct ethtool_eee *eee) if (pl->phydev) ret = phy_ethtool_get_eee(pl->phydev, eee); + if (!ret && phylink_mac_supports_eee(pl)) { + /* Overwrite phylib's interpretation of configuration */ + eeecfg_to_eee(&pl->eee_cfg, eee); + eee->eee_active = pl->eee_active; + } + return ret; } EXPORT_SYMBOL_GPL(phylink_ethtool_get_eee); @@ -2790,12 +2885,51 @@ EXPORT_SYMBOL_GPL(phylink_ethtool_get_eee); int phylink_ethtool_set_eee(struct phylink *pl, struct ethtool_eee *eee) { int ret = -EOPNOTSUPP; + bool mac_eee; ASSERT_RTNL(); + mac_eee = phylink_mac_supports_eee(pl); + + phylink_dbg(pl, "mac %s phylink EEE%s, adv 0x%08x, LPI%s timer %uus\n", + mac_eee ? "supports" : "does not support", + eee->eee_enabled ? ", enabled" : "", eee->advertised, + eee->tx_lpi_enabled ? " enabled" : "", eee->tx_lpi_timer); + + /* Clamp the LPI timer maximum value */ + if (mac_eee && eee->tx_lpi_timer > pl->config->lpi_timer_limit_us) { + eee->tx_lpi_timer = pl->config->lpi_timer_limit_us; + phylink_dbg(pl, "LPI timer limited to %uus\n", + eee->tx_lpi_timer); + } + if (pl->phydev) ret = phy_ethtool_set_eee(pl->phydev, eee); + if (!ret && mac_eee) { + bool can_lpi, old_can_lpi; + + mutex_lock(&pl->state_mutex); + old_can_lpi = eeecfg_mac_can_tx_lpi(&pl->eee_cfg); + eee_to_eeecfg(eee, &pl->eee_cfg); + can_lpi = eeecfg_mac_can_tx_lpi(&pl->eee_cfg); + + phylink_dbg(pl, "can_lpi %u -> %u\n", old_can_lpi, can_lpi); + + /* If the link is up, and the configuration changes the + * LPI permissive state, deal with the change at the MAC. + */ + if (phylink_link_is_up(pl) && old_can_lpi != can_lpi) { + phylink_dbg(pl, "link is up, lpi changed\n"); + if (can_lpi) + phylink_activate_eee(pl); + else + phylink_deactivate_eee(pl); + } + + mutex_unlock(&pl->state_mutex); + } + return ret; } EXPORT_SYMBOL_GPL(phylink_ethtool_set_eee); -- cgit