[PATCH net-next v2 07/13] net: phy: phy_caps: Allow looking-up link caps based on speed and duplex
From: Maxime Chevallier <maxime.chevallier@bootlin.com>
Date: 2025-02-26 10:09:43
Also in:
linux-arm-kernel, lkml
Subsystem:
ethernet phy library, networking drivers, sff/sfp/sfp+ module support, the rest · Maintainers:
Andrew Lunn, Heiner Kallweit, "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Russell King, Linus Torvalds
As the link_caps array is efficient for <speed,duplex> lookups, implement a function for speed/duplex lookups that matches a given mask. This replicates to some extent the phy_lookup_settings() behaviour, matching full link_capabilities instead of a single linkmode. phy.c's phy_santize_settings() and phylink's phylink_ethtool_ksettings_set() performs such lookup using the phy_settings table, but are only interested in the actual speed/duplex that were matched, rathet than the individual linkmode. Similar to phy_lookup_settings(), the newly introduced phy_caps_lookup() will run through the link_caps[] array by descending speed/duplex order. If the link_capabilities for a given <speed/duplex> tuple intersects the passed linkmodes, we consider that a match. Similar to phy_lookup_settings(), we also allow passing an 'exact' boolean, allowing non-exact match. Here, we MUST always match the linkmodes mask, but we allow matching on lower speed settings. Signed-off-by: Maxime Chevallier <maxime.chevallier@bootlin.com> --- V1 -> V2: - No changes drivers/net/phy/phy-caps.h | 4 ++++ drivers/net/phy/phy.c | 32 ++++++------------------- drivers/net/phy/phy_caps.c | 49 ++++++++++++++++++++++++++++++++++++++ drivers/net/phy/phylink.c | 17 ++++++------- 4 files changed, 69 insertions(+), 33 deletions(-)
diff --git a/drivers/net/phy/phy-caps.h b/drivers/net/phy/phy-caps.h
index 7b95a9cb5ebb..dd3ea9f77f1c 100644
--- a/drivers/net/phy/phy-caps.h
+++ b/drivers/net/phy/phy-caps.h@@ -52,4 +52,8 @@ phy_caps_lookup_by_linkmode(const unsigned long *linkmodes); const struct link_capabilities * phy_caps_lookup_by_linkmode_rev(const unsigned long *linkmodes, bool fdx_only); +const struct link_capabilities * +phy_caps_lookup(int speed, unsigned int duplex, const unsigned long *supported, + bool exact); + #endif /* __PHY_CAPS_H */
diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
index 8df37d221fba..562acde89224 100644
--- a/drivers/net/phy/phy.c
+++ b/drivers/net/phy/phy.c@@ -213,25 +213,6 @@ int phy_aneg_done(struct phy_device *phydev) } EXPORT_SYMBOL(phy_aneg_done); -/** - * phy_find_valid - find a PHY setting that matches the requested parameters - * @speed: desired speed - * @duplex: desired duplex - * @supported: mask of supported link modes - * - * Locate a supported phy setting that is, in priority order: - * - an exact match for the specified speed and duplex mode - * - a match for the specified speed, or slower speed - * - the slowest supported speed - * Returns the matched phy_setting entry, or %NULL if no supported phy - * settings were found. - */ -static const struct phy_setting * -phy_find_valid(int speed, int duplex, unsigned long *supported) -{ - return phy_lookup_setting(speed, duplex, supported, false); -} - /** * phy_supported_speeds - return all speeds currently supported by a phy device * @phy: The phy device to return supported speeds of.
@@ -274,13 +255,14 @@ EXPORT_SYMBOL(phy_check_valid); */ static void phy_sanitize_settings(struct phy_device *phydev) { - const struct phy_setting *setting; + const struct link_capabilities *c; + + c = phy_caps_lookup(phydev->speed, phydev->duplex, phydev->supported, + false); - setting = phy_find_valid(phydev->speed, phydev->duplex, - phydev->supported); - if (setting) { - phydev->speed = setting->speed; - phydev->duplex = setting->duplex; + if (c) { + phydev->speed = c->speed; + phydev->duplex = c->duplex; } else { /* We failed to find anything (no supported speeds?) */ phydev->speed = SPEED_UNKNOWN;
diff --git a/drivers/net/phy/phy_caps.c b/drivers/net/phy/phy_caps.c
index 8cf7ff3f1368..77bf85043e82 100644
--- a/drivers/net/phy/phy_caps.c
+++ b/drivers/net/phy/phy_caps.c@@ -149,6 +149,55 @@ phy_caps_lookup_by_linkmode_rev(const unsigned long *linkmodes, bool fdx_only) return NULL; } +/** + * phy_caps_lookup() - Lookup capabilities by speed/duplex that matches a mask + * @speed: Speed to match + * @duplex: Duplex to match + * @supported: Mask of linkmodes to match + * @exact: Perform an exact match or not. + * + * Lookup a link_capabilities entry that intersect the supported linkmodes mask, + * and that matches the passed speed and duplex. + * + * When @exact is set, an exact match is performed on speed and duplex, meaning + * that if the linkmodes for the given speed and duplex intersect the supported + * mask, this capability is returned, otherwise we don't have a match and return + * NULL. + * + * When @exact is not set, we return either an exact match, or matching capabilities + * at lower speed, or the lowest matching speed, or NULL. + * + * Returns: a matched link_capabilities according to the above process, NULL + * otherwise. + */ +const struct link_capabilities * +phy_caps_lookup(int speed, unsigned int duplex, const unsigned long *supported, + bool exact) +{ + const struct link_capabilities *c, *last = NULL; + int capa; + + for (capa = __LINK_CAPA_LAST; capa >= 0; capa--) { + c = &link_caps[capa]; + if (linkmode_intersects(c->linkmodes, supported)) { + last = c; + /* exact match on speed and duplex*/ + if (c->speed == speed && c->duplex == duplex) { + return c; + } else if (!exact) { + if (c->speed <= speed) + return c; + } + } + } + + if (!exact) + return last; + + return NULL; +} +EXPORT_SYMBOL_GPL(phy_caps_lookup); + /** * phy_caps_linkmode_max_speed() - Clamp a linkmodes set to a max speed * @max_speed: Speed limit for the linkmode set
diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c
index a3b186ab3854..6c67d5c9b787 100644
--- a/drivers/net/phy/phylink.c
+++ b/drivers/net/phy/phylink.c@@ -20,6 +20,7 @@ #include <linux/timer.h> #include <linux/workqueue.h> +#include "phy-caps.h" #include "sfp.h" #include "swphy.h"
@@ -2852,8 +2853,8 @@ int phylink_ethtool_ksettings_set(struct phylink *pl, const struct ethtool_link_ksettings *kset) { __ETHTOOL_DECLARE_LINK_MODE_MASK(support); + const struct link_capabilities *c; struct phylink_link_state config; - const struct phy_setting *s; ASSERT_RTNL();
@@ -2896,23 +2897,23 @@ int phylink_ethtool_ksettings_set(struct phylink *pl, /* Autonegotiation disabled, select a suitable speed and * duplex. */ - s = phy_lookup_setting(kset->base.speed, kset->base.duplex, - pl->supported, false); - if (!s) + c = phy_caps_lookup(kset->base.speed, kset->base.duplex, + pl->supported, false); + if (!c) return -EINVAL; /* If we have a fixed link, refuse to change link parameters. * If the link parameters match, accept them but do nothing. */ if (pl->req_link_an_mode == MLO_AN_FIXED) { - if (s->speed != pl->link_config.speed || - s->duplex != pl->link_config.duplex) + if (c->speed != pl->link_config.speed || + c->duplex != pl->link_config.duplex) return -EINVAL; return 0; } - config.speed = s->speed; - config.duplex = s->duplex; + config.speed = c->speed; + config.duplex = c->duplex; break; case AUTONEG_ENABLE:
--
2.48.1