net: phy: phy_caps: Implement link_capabilities lookup by linkmode
authorMaxime Chevallier <maxime.chevallier@bootlin.com>
Fri, 7 Mar 2025 17:36:03 +0000 (18:36 +0100)
committerPaolo Abeni <pabeni@redhat.com>
Tue, 18 Mar 2025 08:03:11 +0000 (09:03 +0100)
In several occasions, phylib needs to lookup a set of matching speed and
duplex against a given linkmode set. Instead of relying on the
phy_settings array and thus iterate over the whole linkmodes list, use
the link_capabilities array to lookup these matches, as we aren't
interested in the actual link setting that matches but rather the speed
and duplex for that setting.

Signed-off-by: Maxime Chevallier <maxime.chevallier@bootlin.com>
Link: https://patch.msgid.link/20250307173611.129125-7-maxime.chevallier@bootlin.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
drivers/net/phy/phy-caps.h
drivers/net/phy/phy-core.c
drivers/net/phy/phy_caps.c

index 1217686f1f9178052a20b16f0d42c86c246f2228..8833798f141f33a12343231999d0e6edc5294ad0 100644 (file)
@@ -45,5 +45,10 @@ size_t phy_caps_speeds(unsigned int *speeds, size_t size,
 void phy_caps_linkmode_max_speed(u32 max_speed, unsigned long *linkmodes);
 bool phy_caps_valid(int speed, int duplex, const unsigned long *linkmodes);
 
+const struct link_capabilities *
+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);
 
 #endif /* __PHY_CAPS_H */
index 741350d7d29cfc748372e357c0a656816139f348..a1caa0f313ecd27c8fcf64b6e417388eed4417cb 100644 (file)
@@ -469,16 +469,15 @@ EXPORT_SYMBOL_GPL(phy_resolve_aneg_pause);
 void phy_resolve_aneg_linkmode(struct phy_device *phydev)
 {
        __ETHTOOL_DECLARE_LINK_MODE_MASK(common);
-       int i;
+       const struct link_capabilities *c;
 
        linkmode_and(common, phydev->lp_advertising, phydev->advertising);
 
-       for (i = 0; i < ARRAY_SIZE(settings); i++)
-               if (test_bit(settings[i].bit, common)) {
-                       phydev->speed = settings[i].speed;
-                       phydev->duplex = settings[i].duplex;
-                       break;
-               }
+       c = phy_caps_lookup_by_linkmode(common);
+       if (c) {
+               phydev->speed = c->speed;
+               phydev->duplex = c->duplex;
+       }
 
        phy_resolve_aneg_pause(phydev);
 }
@@ -496,7 +495,8 @@ EXPORT_SYMBOL_GPL(phy_resolve_aneg_linkmode);
 void phy_check_downshift(struct phy_device *phydev)
 {
        __ETHTOOL_DECLARE_LINK_MODE_MASK(common);
-       int i, speed = SPEED_UNKNOWN;
+       const struct link_capabilities *c;
+       int speed = SPEED_UNKNOWN;
 
        phydev->downshifted_rate = 0;
 
@@ -506,11 +506,9 @@ void phy_check_downshift(struct phy_device *phydev)
 
        linkmode_and(common, phydev->lp_advertising, phydev->advertising);
 
-       for (i = 0; i < ARRAY_SIZE(settings); i++)
-               if (test_bit(settings[i].bit, common)) {
-                       speed = settings[i].speed;
-                       break;
-               }
+       c = phy_caps_lookup_by_linkmode(common);
+       if (c)
+               speed = c->speed;
 
        if (speed == SPEED_UNKNOWN || phydev->speed >= speed)
                return;
@@ -524,17 +522,13 @@ void phy_check_downshift(struct phy_device *phydev)
 static int phy_resolve_min_speed(struct phy_device *phydev, bool fdx_only)
 {
        __ETHTOOL_DECLARE_LINK_MODE_MASK(common);
-       int i = ARRAY_SIZE(settings);
+       const struct link_capabilities *c;
 
        linkmode_and(common, phydev->lp_advertising, phydev->advertising);
 
-       while (--i >= 0) {
-               if (test_bit(settings[i].bit, common)) {
-                       if (fdx_only && settings[i].duplex != DUPLEX_FULL)
-                               continue;
-                       return settings[i].speed;
-               }
-       }
+       c = phy_caps_lookup_by_linkmode_rev(common, fdx_only);
+       if (c)
+               return c->speed;
 
        return SPEED_UNKNOWN;
 }
index 4ee8c25c8521ce2b26c64bbc836122b580e7279f..c39f38c12ef2a15e2d0e99b881def61389268c11 100644 (file)
@@ -125,6 +125,51 @@ size_t phy_caps_speeds(unsigned int *speeds, size_t size,
        return count;
 }
 
+/**
+ * phy_caps_lookup_by_linkmode() - Lookup the fastest matching link_capabilities
+ * @linkmodes: Linkmodes to match against
+ *
+ * Returns: The highest-speed link_capabilities that intersects the given
+ *         linkmodes. In case several DUPLEX_ options exist at that speed,
+ *         DUPLEX_FULL is matched first. NULL is returned if no match.
+ */
+const struct link_capabilities *
+phy_caps_lookup_by_linkmode(const unsigned long *linkmodes)
+{
+       struct link_capabilities *lcap;
+
+       for_each_link_caps_desc_speed(lcap)
+               if (linkmode_intersects(lcap->linkmodes, linkmodes))
+                       return lcap;
+
+       return NULL;
+}
+
+/**
+ * phy_caps_lookup_by_linkmode_rev() - Lookup the slowest matching link_capabilities
+ * @linkmodes: Linkmodes to match against
+ * @fdx_only: Full duplex match only when set
+ *
+ * Returns: The lowest-speed link_capabilities that intersects the given
+ *         linkmodes. When set, fdx_only will ignore half-duplex matches.
+ *         NULL is returned if no match.
+ */
+const struct link_capabilities *
+phy_caps_lookup_by_linkmode_rev(const unsigned long *linkmodes, bool fdx_only)
+{
+       struct link_capabilities *lcap;
+
+       for_each_link_caps_asc_speed(lcap) {
+               if (fdx_only && lcap->duplex != DUPLEX_FULL)
+                       continue;
+
+               if (linkmode_intersects(lcap->linkmodes, linkmodes))
+                       return lcap;
+       }
+
+       return NULL;
+}
+
 /**
  * phy_caps_linkmode_max_speed() - Clamp a linkmodes set to a max speed
  * @max_speed: Speed limit for the linkmode set