Commit dbcd85b0 authored by Maxime Chevallier's avatar Maxime Chevallier Committed by Paolo Abeni
Browse files

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: default avatarMaxime Chevallier <maxime.chevallier@bootlin.com>
Link: https://patch.msgid.link/20250307173611.129125-7-maxime.chevallier@bootlin.com


Signed-off-by: default avatarPaolo Abeni <pabeni@redhat.com>
parent 87b22ce3
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -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
Original line number Diff line number Diff line
@@ -469,15 +469,14 @@ 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 −0
Original line number Diff line number Diff line
@@ -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