Commit 793cc3c7 authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'phy-rate-matching'

Sean Anderson says:

====================
net: phy: Add support for rate matching

This adds support for phy rate matching: when a phy adapts between
differing phy interface and link speeds. It was originally submitted as
part of [1], which is considered "v1" of this series.

Several past discussions [2-4] around adding rate adaptation provide
some context.

Although in earlier versions of this series, userspace could disable
rate matching, now it is only possible to determine the current rate
adaptation type. Disabling or otherwise configuring rate adaptation has
been left for future work. However, because currently only
RATE_MATCH_PAUSE is implemented, it is possible to disable rate
adaptation by modifying the advertisement appropriately.

[1] https://lore.kernel.org/netdev/20220715215954.1449214-1-sean.anderson@seco.com/T/#t
[2] https://lore.kernel.org/netdev/1579701573-6609-1-git-send-email-madalin.bucur@oss.nxp.com/
[3] https://lore.kernel.org/netdev/1580137671-22081-1-git-send-email-madalin.bucur@oss.nxp.com/
[4] https://lore.kernel.org/netdev/20200116181933.32765-1-olteanv@gmail.com/



Changes in v6:
- Don't announce that we've enabled pause frames for rate adaptation
- Merry Christmas
- Rename rate adaptation to rate matching
- Reword documentation, (hopefully) taking into account feedback

Changes in v5:
- Break off patch "net: phy: Add 1000BASE-KX interface mode" for
  separate submission.
- Document phy_rate_adaptation_to_str
- Drop patch "Add some helpers for working with mac caps"; it has been
  incorperated into the autonegotiation patch.
- Move phylink_cap_from_speed_duplex to this commit
- Rebase onto net-next/master
- Remove unnecessary comma

Changes in v4:
- Export phy_rate_adaptation_to_str
- Remove phylink_interface_max_speed, which was accidentally added
- Split off the LS1046ARDB 1G fix

Changes in v3:
- Add phylink_cap_from_speed_duplex to look up the mac capability
  corresponding to the interface's speed.
- Document MAC_(A)SYM_PAUSE
- Include RATE_ADAPT_CRS; it's a few lines and it doesn't hurt.
- Modify link settings directly in phylink_link_up, instead of doing
  things more indirectly via link_*.
- Move unused defines to next commit (where they will be used)
- Remove "Support differing link/interface speed/duplex". It has been
  rendered unnecessary due to simplification of the rate adaptation
  patches. Thanks Russell!
- Rewrite cover letter to better reflect the opinions of the developers
  involved

Changes in v2:
- Add (read-only) ethtool support for rate adaptation
- Add comments clarifying the register defines
- Add locking to phy_get_rate_adaptation
- Always use the rate adaptation setting to determine the interface
  speed/duplex (instead of sometimes using the interface mode).
- Determine the interface speed and max mac speed directly instead of
  guessing based on the caps.
- Move part of commit message to cover letter, as it gives a good
  overview of the whole series, and allows this patch to focus more on
  the specifics.
- Reorder variables in aqr107_read_rate
- Use int/defines instead of enum to allow for use in ioctls/netlink
- Use the phy's rate adaptation setting to determine whether to use its
  link speed/duplex or the MAC's speed/duplex with MLO_AN_INBAND.
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 1a4019f4 3c42563b
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -426,6 +426,7 @@ Kernel response contents:
  ``ETHTOOL_A_LINKMODES_DUPLEX``              u8      duplex mode
  ``ETHTOOL_A_LINKMODES_MASTER_SLAVE_CFG``    u8      Master/slave port mode
  ``ETHTOOL_A_LINKMODES_MASTER_SLAVE_STATE``  u8      Master/slave port state
  ``ETHTOOL_A_LINKMODES_RATE_MATCHING``       u8      PHY rate matching
  ==========================================  ======  ==========================

For ``ETHTOOL_A_LINKMODES_OURS``, value represents advertised modes and mask
@@ -449,6 +450,7 @@ Request contents:
  ``ETHTOOL_A_LINKMODES_SPEED``               u32     link speed (Mb/s)
  ``ETHTOOL_A_LINKMODES_DUPLEX``              u8      duplex mode
  ``ETHTOOL_A_LINKMODES_MASTER_SLAVE_CFG``    u8      Master/slave port mode
  ``ETHTOOL_A_LINKMODES_RATE_MATCHING``       u8      PHY rate matching
  ``ETHTOOL_A_LINKMODES_LANES``               u32     lanes
  ==========================================  ======  ==========================

+63 −5
Original line number Diff line number Diff line
@@ -27,9 +27,12 @@
#define MDIO_PHYXS_VEND_IF_STATUS		0xe812
#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_MASK	GENMASK(7, 3)
#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_KR	0
#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_KX	1
#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_XFI	2
#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_USXGMII	3
#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_XAUI	4
#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_SGMII	6
#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_RXAUI	7
#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_OCSGMII	10

#define MDIO_AN_VEND_PROV			0xc400
@@ -94,6 +97,19 @@
#define VEND1_GLOBAL_GEN_STAT2			0xc831
#define VEND1_GLOBAL_GEN_STAT2_OP_IN_PROG	BIT(15)

/* The following registers all have similar layouts; first the registers... */
#define VEND1_GLOBAL_CFG_10M			0x0310
#define VEND1_GLOBAL_CFG_100M			0x031b
#define VEND1_GLOBAL_CFG_1G			0x031c
#define VEND1_GLOBAL_CFG_2_5G			0x031d
#define VEND1_GLOBAL_CFG_5G			0x031e
#define VEND1_GLOBAL_CFG_10G			0x031f
/* ...and now the fields */
#define VEND1_GLOBAL_CFG_RATE_ADAPT		GENMASK(8, 7)
#define VEND1_GLOBAL_CFG_RATE_ADAPT_NONE	0
#define VEND1_GLOBAL_CFG_RATE_ADAPT_USX		1
#define VEND1_GLOBAL_CFG_RATE_ADAPT_PAUSE	2

#define VEND1_GLOBAL_RSVD_STAT1			0xc885
#define VEND1_GLOBAL_RSVD_STAT1_FW_BUILD_ID	GENMASK(7, 4)
#define VEND1_GLOBAL_RSVD_STAT1_PROV_ID		GENMASK(3, 0)
@@ -344,40 +360,57 @@ static int aqr_read_status(struct phy_device *phydev)

static int aqr107_read_rate(struct phy_device *phydev)
{
	u32 config_reg;
	int val;

	val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_TX_VEND_STATUS1);
	if (val < 0)
		return val;

	if (val & MDIO_AN_TX_VEND_STATUS1_FULL_DUPLEX)
		phydev->duplex = DUPLEX_FULL;
	else
		phydev->duplex = DUPLEX_HALF;

	switch (FIELD_GET(MDIO_AN_TX_VEND_STATUS1_RATE_MASK, val)) {
	case MDIO_AN_TX_VEND_STATUS1_10BASET:
		phydev->speed = SPEED_10;
		config_reg = VEND1_GLOBAL_CFG_10M;
		break;
	case MDIO_AN_TX_VEND_STATUS1_100BASETX:
		phydev->speed = SPEED_100;
		config_reg = VEND1_GLOBAL_CFG_100M;
		break;
	case MDIO_AN_TX_VEND_STATUS1_1000BASET:
		phydev->speed = SPEED_1000;
		config_reg = VEND1_GLOBAL_CFG_1G;
		break;
	case MDIO_AN_TX_VEND_STATUS1_2500BASET:
		phydev->speed = SPEED_2500;
		config_reg = VEND1_GLOBAL_CFG_2_5G;
		break;
	case MDIO_AN_TX_VEND_STATUS1_5000BASET:
		phydev->speed = SPEED_5000;
		config_reg = VEND1_GLOBAL_CFG_5G;
		break;
	case MDIO_AN_TX_VEND_STATUS1_10GBASET:
		phydev->speed = SPEED_10000;
		config_reg = VEND1_GLOBAL_CFG_10G;
		break;
	default:
		phydev->speed = SPEED_UNKNOWN;
		break;
		return 0;
	}

	if (val & MDIO_AN_TX_VEND_STATUS1_FULL_DUPLEX)
		phydev->duplex = DUPLEX_FULL;
	val = phy_read_mmd(phydev, MDIO_MMD_VEND1, config_reg);
	if (val < 0)
		return val;

	if (FIELD_GET(VEND1_GLOBAL_CFG_RATE_ADAPT, val) ==
	    VEND1_GLOBAL_CFG_RATE_ADAPT_PAUSE)
		phydev->rate_matching = RATE_MATCH_PAUSE;
	else
		phydev->duplex = DUPLEX_HALF;
		phydev->rate_matching = RATE_MATCH_NONE;

	return 0;
}
@@ -401,15 +434,24 @@ static int aqr107_read_status(struct phy_device *phydev)
	case MDIO_PHYXS_VEND_IF_STATUS_TYPE_KR:
		phydev->interface = PHY_INTERFACE_MODE_10GKR;
		break;
	case MDIO_PHYXS_VEND_IF_STATUS_TYPE_KX:
		phydev->interface = PHY_INTERFACE_MODE_1000BASEKX;
		break;
	case MDIO_PHYXS_VEND_IF_STATUS_TYPE_XFI:
		phydev->interface = PHY_INTERFACE_MODE_10GBASER;
		break;
	case MDIO_PHYXS_VEND_IF_STATUS_TYPE_USXGMII:
		phydev->interface = PHY_INTERFACE_MODE_USXGMII;
		break;
	case MDIO_PHYXS_VEND_IF_STATUS_TYPE_XAUI:
		phydev->interface = PHY_INTERFACE_MODE_XAUI;
		break;
	case MDIO_PHYXS_VEND_IF_STATUS_TYPE_SGMII:
		phydev->interface = PHY_INTERFACE_MODE_SGMII;
		break;
	case MDIO_PHYXS_VEND_IF_STATUS_TYPE_RXAUI:
		phydev->interface = PHY_INTERFACE_MODE_RXAUI;
		break;
	case MDIO_PHYXS_VEND_IF_STATUS_TYPE_OCSGMII:
		phydev->interface = PHY_INTERFACE_MODE_2500BASEX;
		break;
@@ -522,11 +564,14 @@ static int aqr107_config_init(struct phy_device *phydev)

	/* Check that the PHY interface type is compatible */
	if (phydev->interface != PHY_INTERFACE_MODE_SGMII &&
	    phydev->interface != PHY_INTERFACE_MODE_1000BASEKX &&
	    phydev->interface != PHY_INTERFACE_MODE_2500BASEX &&
	    phydev->interface != PHY_INTERFACE_MODE_XGMII &&
	    phydev->interface != PHY_INTERFACE_MODE_USXGMII &&
	    phydev->interface != PHY_INTERFACE_MODE_10GKR &&
	    phydev->interface != PHY_INTERFACE_MODE_10GBASER)
	    phydev->interface != PHY_INTERFACE_MODE_10GBASER &&
	    phydev->interface != PHY_INTERFACE_MODE_XAUI &&
	    phydev->interface != PHY_INTERFACE_MODE_RXAUI)
		return -ENODEV;

	WARN(phydev->interface == PHY_INTERFACE_MODE_XGMII,
@@ -630,6 +675,16 @@ static int aqr107_wait_processor_intensive_op(struct phy_device *phydev)
	return 0;
}

static int aqr107_get_rate_matching(struct phy_device *phydev,
				    phy_interface_t iface)
{
	if (iface == PHY_INTERFACE_MODE_10GBASER ||
	    iface == PHY_INTERFACE_MODE_2500BASEX ||
	    iface == PHY_INTERFACE_MODE_NA)
		return RATE_MATCH_PAUSE;
	return RATE_MATCH_NONE;
}

static int aqr107_suspend(struct phy_device *phydev)
{
	int err;
@@ -703,6 +758,7 @@ static struct phy_driver aqr_driver[] = {
	PHY_ID_MATCH_MODEL(PHY_ID_AQR107),
	.name		= "Aquantia AQR107",
	.probe		= aqr107_probe,
	.get_rate_matching = aqr107_get_rate_matching,
	.config_init	= aqr107_config_init,
	.config_aneg    = aqr_config_aneg,
	.config_intr	= aqr_config_intr,
@@ -721,6 +777,7 @@ static struct phy_driver aqr_driver[] = {
	PHY_ID_MATCH_MODEL(PHY_ID_AQCS109),
	.name		= "Aquantia AQCS109",
	.probe		= aqr107_probe,
	.get_rate_matching = aqr107_get_rate_matching,
	.config_init	= aqcs109_config_init,
	.config_aneg    = aqr_config_aneg,
	.config_intr	= aqr_config_intr,
@@ -747,6 +804,7 @@ static struct phy_driver aqr_driver[] = {
	PHY_ID_MATCH_MODEL(PHY_ID_AQR113C),
	.name           = "Aquantia AQR113C",
	.probe          = aqr107_probe,
	.get_rate_matching = aqr107_get_rate_matching,
	.config_init    = aqr107_config_init,
	.config_aneg    = aqr_config_aneg,
	.config_intr    = aqr_config_intr,
+21 −0
Original line number Diff line number Diff line
@@ -74,6 +74,27 @@ const char *phy_duplex_to_str(unsigned int duplex)
}
EXPORT_SYMBOL_GPL(phy_duplex_to_str);

/**
 * phy_rate_matching_to_str - Return a string describing the rate matching
 *
 * @rate_matching: Type of rate matching to describe
 */
const char *phy_rate_matching_to_str(int rate_matching)
{
	switch (rate_matching) {
	case RATE_MATCH_NONE:
		return "none";
	case RATE_MATCH_PAUSE:
		return "pause";
	case RATE_MATCH_CRS:
		return "crs";
	case RATE_MATCH_OPEN_LOOP:
		return "open-loop";
	}
	return "Unsupported (update phy-core.c)";
}
EXPORT_SYMBOL_GPL(phy_rate_matching_to_str);

/**
 * phy_interface_num_ports - Return the number of links that can be carried by
 *			     a given MAC-PHY physical link. Returns 0 if this is
+28 −0
Original line number Diff line number Diff line
@@ -114,6 +114,33 @@ void phy_print_status(struct phy_device *phydev)
}
EXPORT_SYMBOL(phy_print_status);

/**
 * phy_get_rate_matching - determine if rate matching is supported
 * @phydev: The phy device to return rate matching for
 * @iface: The interface mode to use
 *
 * This determines the type of rate matching (if any) that @phy supports
 * using @iface. @iface may be %PHY_INTERFACE_MODE_NA to determine if any
 * interface supports rate matching.
 *
 * Return: The type of rate matching @phy supports for @iface, or
 *         %RATE_MATCH_NONE.
 */
int phy_get_rate_matching(struct phy_device *phydev,
			  phy_interface_t iface)
{
	int ret = RATE_MATCH_NONE;

	if (phydev->drv->get_rate_matching) {
		mutex_lock(&phydev->lock);
		ret = phydev->drv->get_rate_matching(phydev, iface);
		mutex_unlock(&phydev->lock);
	}

	return ret;
}
EXPORT_SYMBOL_GPL(phy_get_rate_matching);

/**
 * phy_config_interrupt - configure the PHY device for the requested interrupts
 * @phydev: the phy_device struct
@@ -256,6 +283,7 @@ void phy_ethtool_ksettings_get(struct phy_device *phydev,
	cmd->base.duplex = phydev->duplex;
	cmd->base.master_slave_cfg = phydev->master_slave_get;
	cmd->base.master_slave_state = phydev->master_slave_state;
	cmd->base.rate_matching = phydev->rate_matching;
	if (phydev->interface == PHY_INTERFACE_MODE_MOCA)
		cmd->base.port = PORT_BNC;
	else
+244 −24
Original line number Diff line number Diff line
@@ -155,8 +155,84 @@ static const char *phylink_an_mode_str(unsigned int mode)
	return mode < ARRAY_SIZE(modestr) ? modestr[mode] : "unknown";
}

static void phylink_caps_to_linkmodes(unsigned long *linkmodes,
				      unsigned long caps)
/**
 * phylink_interface_max_speed() - get the maximum speed of a phy interface
 * @interface: phy interface mode defined by &typedef phy_interface_t
 *
 * Determine the maximum speed of a phy interface. This is intended to help
 * determine the correct speed to pass to the MAC when the phy is performing
 * rate matching.
 *
 * Return: The maximum speed of @interface
 */
static int phylink_interface_max_speed(phy_interface_t interface)
{
	switch (interface) {
	case PHY_INTERFACE_MODE_100BASEX:
	case PHY_INTERFACE_MODE_REVRMII:
	case PHY_INTERFACE_MODE_RMII:
	case PHY_INTERFACE_MODE_SMII:
	case PHY_INTERFACE_MODE_REVMII:
	case PHY_INTERFACE_MODE_MII:
		return SPEED_100;

	case PHY_INTERFACE_MODE_TBI:
	case PHY_INTERFACE_MODE_MOCA:
	case PHY_INTERFACE_MODE_RTBI:
	case PHY_INTERFACE_MODE_1000BASEX:
	case PHY_INTERFACE_MODE_1000BASEKX:
	case PHY_INTERFACE_MODE_TRGMII:
	case PHY_INTERFACE_MODE_RGMII_TXID:
	case PHY_INTERFACE_MODE_RGMII_RXID:
	case PHY_INTERFACE_MODE_RGMII_ID:
	case PHY_INTERFACE_MODE_RGMII:
	case PHY_INTERFACE_MODE_QSGMII:
	case PHY_INTERFACE_MODE_SGMII:
	case PHY_INTERFACE_MODE_GMII:
		return SPEED_1000;

	case PHY_INTERFACE_MODE_2500BASEX:
		return SPEED_2500;

	case PHY_INTERFACE_MODE_5GBASER:
		return SPEED_5000;

	case PHY_INTERFACE_MODE_XGMII:
	case PHY_INTERFACE_MODE_RXAUI:
	case PHY_INTERFACE_MODE_XAUI:
	case PHY_INTERFACE_MODE_10GBASER:
	case PHY_INTERFACE_MODE_10GKR:
	case PHY_INTERFACE_MODE_USXGMII:
	case PHY_INTERFACE_MODE_QUSGMII:
		return SPEED_10000;

	case PHY_INTERFACE_MODE_25GBASER:
		return SPEED_25000;

	case PHY_INTERFACE_MODE_XLGMII:
		return SPEED_40000;

	case PHY_INTERFACE_MODE_INTERNAL:
	case PHY_INTERFACE_MODE_NA:
	case PHY_INTERFACE_MODE_MAX:
		/* No idea! Garbage in, unknown out */
		return SPEED_UNKNOWN;
	}

	/* If we get here, someone forgot to add an interface mode above */
	WARN_ON_ONCE(1);
	return SPEED_UNKNOWN;
}

/**
 * phylink_caps_to_linkmodes() - Convert capabilities to ethtool link modes
 * @linkmodes: ethtool linkmode mask (must be already initialised)
 * @caps: bitmask of MAC capabilities
 *
 * Set all possible pause, speed and duplex linkmodes in @linkmodes that are
 * supported by the @caps. @linkmodes must have been initialised previously.
 */
void phylink_caps_to_linkmodes(unsigned long *linkmodes, unsigned long caps)
{
	if (caps & MAC_SYM_PAUSE)
		__set_bit(ETHTOOL_LINK_MODE_Pause_BIT, linkmodes);
@@ -295,21 +371,72 @@ static void phylink_caps_to_linkmodes(unsigned long *linkmodes,
		__set_bit(ETHTOOL_LINK_MODE_400000baseCR4_Full_BIT, linkmodes);
	}
}
EXPORT_SYMBOL_GPL(phylink_caps_to_linkmodes);

static struct {
	unsigned long mask;
	int speed;
	unsigned int duplex;
} phylink_caps_params[] = {
	{ MAC_400000FD, SPEED_400000, DUPLEX_FULL },
	{ MAC_200000FD, SPEED_200000, DUPLEX_FULL },
	{ MAC_100000FD, SPEED_100000, DUPLEX_FULL },
	{ MAC_56000FD,  SPEED_56000,  DUPLEX_FULL },
	{ MAC_50000FD,  SPEED_50000,  DUPLEX_FULL },
	{ MAC_40000FD,  SPEED_40000,  DUPLEX_FULL },
	{ MAC_25000FD,  SPEED_25000,  DUPLEX_FULL },
	{ MAC_20000FD,  SPEED_20000,  DUPLEX_FULL },
	{ MAC_10000FD,  SPEED_10000,  DUPLEX_FULL },
	{ MAC_5000FD,   SPEED_5000,   DUPLEX_FULL },
	{ MAC_2500FD,   SPEED_2500,   DUPLEX_FULL },
	{ MAC_1000FD,   SPEED_1000,   DUPLEX_FULL },
	{ MAC_1000HD,   SPEED_1000,   DUPLEX_HALF },
	{ MAC_100FD,    SPEED_100,    DUPLEX_FULL },
	{ MAC_100HD,    SPEED_100,    DUPLEX_HALF },
	{ MAC_10FD,     SPEED_10,     DUPLEX_FULL },
	{ MAC_10HD,     SPEED_10,     DUPLEX_HALF },
};

/**
 * phylink_get_linkmodes() - get acceptable link modes
 * @linkmodes: ethtool linkmode mask (must be already initialised)
 * phylink_cap_from_speed_duplex - Get mac capability from speed/duplex
 * @speed: the speed to search for
 * @duplex: the duplex to search for
 *
 * Find the mac capability for a given speed and duplex.
 *
 * Return: A mask with the mac capability patching @speed and @duplex, or 0 if
 *         there were no matches.
 */
static unsigned long phylink_cap_from_speed_duplex(int speed,
						   unsigned int duplex)
{
	int i;

	for (i = 0; i < ARRAY_SIZE(phylink_caps_params); i++) {
		if (speed == phylink_caps_params[i].speed &&
		    duplex == phylink_caps_params[i].duplex)
			return phylink_caps_params[i].mask;
	}

	return 0;
}

/**
 * phylink_get_capabilities() - get capabilities for a given MAC
 * @interface: phy interface mode defined by &typedef phy_interface_t
 * @mac_capabilities: bitmask of MAC capabilities
 * @rate_matching: type of rate matching being performed
 *
 * Set all possible pause, speed and duplex linkmodes in @linkmodes that
 * are supported by the @interface mode and @mac_capabilities. @linkmodes
 * must have been initialised previously.
 * Get the MAC capabilities that are supported by the @interface mode and
 * @mac_capabilities.
 */
void phylink_get_linkmodes(unsigned long *linkmodes, phy_interface_t interface,
			   unsigned long mac_capabilities)
unsigned long phylink_get_capabilities(phy_interface_t interface,
				       unsigned long mac_capabilities,
				       int rate_matching)
{
	int max_speed = phylink_interface_max_speed(interface);
	unsigned long caps = MAC_SYM_PAUSE | MAC_ASYM_PAUSE;
	unsigned long matched_caps = 0;

	switch (interface) {
	case PHY_INTERFACE_MODE_USXGMII:
@@ -383,9 +510,55 @@ void phylink_get_linkmodes(unsigned long *linkmodes, phy_interface_t interface,
		break;
	}

	phylink_caps_to_linkmodes(linkmodes, caps & mac_capabilities);
	switch (rate_matching) {
	case RATE_MATCH_OPEN_LOOP:
		/* TODO */
		fallthrough;
	case RATE_MATCH_NONE:
		matched_caps = 0;
		break;
	case RATE_MATCH_PAUSE: {
		/* The MAC must support asymmetric pause towards the local
		 * device for this. We could allow just symmetric pause, but
		 * then we might have to renegotiate if the link partner
		 * doesn't support pause. This is because there's no way to
		 * accept pause frames without transmitting them if we only
		 * support symmetric pause.
		 */
		if (!(mac_capabilities & MAC_SYM_PAUSE) ||
		    !(mac_capabilities & MAC_ASYM_PAUSE))
			break;

		/* We can't adapt if the MAC doesn't support the interface's
		 * max speed at full duplex.
		 */
		if (mac_capabilities &
		    phylink_cap_from_speed_duplex(max_speed, DUPLEX_FULL)) {
			/* Although a duplex-matching phy might exist, we
			 * conservatively remove these modes because the MAC
			 * will not be aware of the half-duplex nature of the
			 * link.
			 */
			matched_caps = GENMASK(__fls(caps), __fls(MAC_10HD));
			matched_caps &= ~(MAC_1000HD | MAC_100HD | MAC_10HD);
		}
		break;
	}
	case RATE_MATCH_CRS:
		/* The MAC must support half duplex at the interface's max
		 * speed.
		 */
		if (mac_capabilities &
		    phylink_cap_from_speed_duplex(max_speed, DUPLEX_HALF)) {
			matched_caps = GENMASK(__fls(caps), __fls(MAC_10HD));
			matched_caps &= mac_capabilities;
		}
		break;
	}
EXPORT_SYMBOL_GPL(phylink_get_linkmodes);

	return (caps & mac_capabilities) | matched_caps;
}
EXPORT_SYMBOL_GPL(phylink_get_capabilities);

/**
 * phylink_generic_validate() - generic validate() callback implementation
@@ -402,10 +575,14 @@ void phylink_generic_validate(struct phylink_config *config,
			      struct phylink_link_state *state)
{
	__ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
	unsigned long caps;

	phylink_set_port_modes(mask);
	phylink_set(mask, Autoneg);
	phylink_get_linkmodes(mask, state->interface, config->mac_capabilities);
	caps = phylink_get_capabilities(state->interface,
					config->mac_capabilities,
					state->rate_matching);
	phylink_caps_to_linkmodes(mask, caps);

	linkmode_and(supported, supported, mask);
	linkmode_and(state->advertising, state->advertising, mask);
@@ -782,11 +959,12 @@ static void phylink_mac_config(struct phylink *pl,
			       const struct phylink_link_state *state)
{
	phylink_dbg(pl,
		    "%s: mode=%s/%s/%s/%s adv=%*pb pause=%02x link=%u an=%u\n",
		    "%s: mode=%s/%s/%s/%s/%s adv=%*pb pause=%02x link=%u an=%u\n",
		    __func__, phylink_an_mode_str(pl->cur_link_an_mode),
		    phy_modes(state->interface),
		    phy_speed_to_str(state->speed),
		    phy_duplex_to_str(state->duplex),
		    phy_rate_matching_to_str(state->rate_matching),
		    __ETHTOOL_LINK_MODE_MASK_NBITS, state->advertising,
		    state->pause, state->link, state->an_enabled);

@@ -923,6 +1101,7 @@ static void phylink_mac_pcs_get_state(struct phylink *pl,
	linkmode_zero(state->lp_advertising);
	state->interface = pl->link_config.interface;
	state->an_enabled = pl->link_config.an_enabled;
	state->rate_matching = pl->link_config.rate_matching;
	if (state->an_enabled) {
		state->speed = SPEED_UNKNOWN;
		state->duplex = DUPLEX_UNKNOWN;
@@ -1006,19 +1185,43 @@ static void phylink_link_up(struct phylink *pl,
			    struct phylink_link_state link_state)
{
	struct net_device *ndev = pl->netdev;
	int speed, duplex;
	bool rx_pause;

	speed = link_state.speed;
	duplex = link_state.duplex;
	rx_pause = !!(link_state.pause & MLO_PAUSE_RX);

	switch (link_state.rate_matching) {
	case RATE_MATCH_PAUSE:
		/* The PHY is doing rate matchion from the media rate (in
		 * the link_state) to the interface speed, and will send
		 * pause frames to the MAC to limit its transmission speed.
		 */
		speed = phylink_interface_max_speed(link_state.interface);
		duplex = DUPLEX_FULL;
		rx_pause = true;
		break;

	case RATE_MATCH_CRS:
		/* The PHY is doing rate matchion from the media rate (in
		 * the link_state) to the interface speed, and will cause
		 * collisions to the MAC to limit its transmission speed.
		 */
		speed = phylink_interface_max_speed(link_state.interface);
		duplex = DUPLEX_HALF;
		break;
	}

	pl->cur_interface = link_state.interface;

	if (pl->pcs && pl->pcs->ops->pcs_link_up)
		pl->pcs->ops->pcs_link_up(pl->pcs, pl->cur_link_an_mode,
					 pl->cur_interface,
					 link_state.speed, link_state.duplex);
					  pl->cur_interface, speed, duplex);

	pl->mac_ops->mac_link_up(pl->config, pl->phydev,
				 pl->cur_link_an_mode, pl->cur_interface,
				 link_state.speed, link_state.duplex,
				 !!(link_state.pause & MLO_PAUSE_TX),
				 !!(link_state.pause & MLO_PAUSE_RX));
	pl->mac_ops->mac_link_up(pl->config, pl->phydev, pl->cur_link_an_mode,
				 pl->cur_interface, speed, duplex,
				 !!(link_state.pause & MLO_PAUSE_TX), rx_pause);

	if (ndev)
		netif_carrier_on(ndev);
@@ -1110,6 +1313,17 @@ static void phylink_resolve(struct work_struct *w)
				}
				link_state.interface = pl->phy_state.interface;

				/* If we are doing rate matching, then the
				 * link speed/duplex comes from the PHY
				 */
				if (pl->phy_state.rate_matching) {
					link_state.rate_matching =
						pl->phy_state.rate_matching;
					link_state.speed = pl->phy_state.speed;
					link_state.duplex =
						pl->phy_state.duplex;
				}

				/* If we have a PHY, we need to update with
				 * the PHY flow control bits.
				 */
@@ -1344,6 +1558,7 @@ static void phylink_phy_change(struct phy_device *phydev, bool up)
	mutex_lock(&pl->state_mutex);
	pl->phy_state.speed = phydev->speed;
	pl->phy_state.duplex = phydev->duplex;
	pl->phy_state.rate_matching = phydev->rate_matching;
	pl->phy_state.pause = MLO_PAUSE_NONE;
	if (tx_pause)
		pl->phy_state.pause |= MLO_PAUSE_TX;
@@ -1355,10 +1570,11 @@ static void phylink_phy_change(struct phy_device *phydev, bool up)

	phylink_run_resolve(pl);

	phylink_dbg(pl, "phy link %s %s/%s/%s/%s\n", up ? "up" : "down",
	phylink_dbg(pl, "phy link %s %s/%s/%s/%s/%s\n", up ? "up" : "down",
		    phy_modes(phydev->interface),
		    phy_speed_to_str(phydev->speed),
		    phy_duplex_to_str(phydev->duplex),
		    phy_rate_matching_to_str(phydev->rate_matching),
		    phylink_pause_to_str(pl->phy_state.pause));
}

@@ -1395,6 +1611,7 @@ static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy,
		config.interface = PHY_INTERFACE_MODE_NA;
	else
		config.interface = interface;
	config.rate_matching = phy_get_rate_matching(phy, config.interface);

	ret = phylink_validate(pl, supported, &config);
	if (ret) {
@@ -1422,6 +1639,7 @@ static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy,
	pl->phy_state.pause = MLO_PAUSE_NONE;
	pl->phy_state.speed = SPEED_UNKNOWN;
	pl->phy_state.duplex = DUPLEX_UNKNOWN;
	pl->phy_state.rate_matching = RATE_MATCH_NONE;
	linkmode_copy(pl->supported, supported);
	linkmode_copy(pl->link_config.advertising, config.advertising);

@@ -1864,8 +2082,10 @@ static void phylink_get_ksettings(const struct phylink_link_state *state,
{
	phylink_merge_link_mode(kset->link_modes.advertising, state->advertising);
	linkmode_copy(kset->link_modes.lp_advertising, state->lp_advertising);
	if (kset->base.rate_matching == RATE_MATCH_NONE) {
		kset->base.speed = state->speed;
		kset->base.duplex = state->duplex;
	}
	kset->base.autoneg = state->an_enabled ? AUTONEG_ENABLE :
				AUTONEG_DISABLE;
}
Loading