net: phy: phy_caps: Implement link_capabilities lookup by linkmode

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>
This commit is contained in:
Maxime Chevallier
2025-03-07 18:36:03 +01:00
committed by Paolo Abeni
parent 87b22ce312
commit dbcd85b05c
3 changed files with 65 additions and 21 deletions
+5
View 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 */
+15 -21
View 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;
}
+45
View 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