Commit df874f9e authored by Russell King (Oracle)'s avatar Russell King (Oracle) Committed by Jakub Kicinski
Browse files

net: phylink: add pcs_inband_caps() method



Add a pcs_inband_caps() method to query the PCS for its inband link
capabilities, and use this to determine whether link modes used with
optical SFPs can be supported.

When a PCS does not provide a method, we allow inband negotiation to
be either on or off, making this a no-op until the pcs_inband_caps()
method is implemented by a PCS driver.

Reviewed-by: default avatarAndrew Lunn <andrew@lunn.ch>
Signed-off-by: default avatarRussell King (Oracle) <rmk+kernel@armlinux.org.uk>
Link: https://patch.msgid.link/E1tIUs4-006IUU-7K@rmk-PC.armlinux.org.uk


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent a219912e
Loading
Loading
Loading
Loading
+60 −0
Original line number Diff line number Diff line
@@ -990,6 +990,15 @@ static void phylink_resolve_an_pause(struct phylink_link_state *state)
	}
}

static unsigned int phylink_pcs_inband_caps(struct phylink_pcs *pcs,
				    phy_interface_t interface)
{
	if (pcs && pcs->ops->pcs_inband_caps)
		return pcs->ops->pcs_inband_caps(pcs, interface);

	return 0;
}

static void phylink_pcs_pre_config(struct phylink_pcs *pcs,
				   phy_interface_t interface)
{
@@ -1043,6 +1052,24 @@ static void phylink_pcs_link_up(struct phylink_pcs *pcs, unsigned int neg_mode,
		pcs->ops->pcs_link_up(pcs, neg_mode, interface, speed, duplex);
}

/* Query inband for a specific interface mode, asking the MAC for the
 * PCS which will be used to handle the interface mode.
 */
static unsigned int phylink_inband_caps(struct phylink *pl,
					 phy_interface_t interface)
{
	struct phylink_pcs *pcs;

	if (!pl->mac_ops->mac_select_pcs)
		return 0;

	pcs = pl->mac_ops->mac_select_pcs(pl->config, interface);
	if (!pcs)
		return 0;

	return phylink_pcs_inband_caps(pcs, interface);
}

static void phylink_pcs_poll_stop(struct phylink *pl)
{
	if (pl->cfg_link_an_mode == MLO_AN_INBAND)
@@ -2532,6 +2559,26 @@ int phylink_ethtool_ksettings_get(struct phylink *pl,
}
EXPORT_SYMBOL_GPL(phylink_ethtool_ksettings_get);

static bool phylink_validate_pcs_inband_autoneg(struct phylink *pl,
					        phy_interface_t interface,
						unsigned long *adv)
{
	unsigned int inband = phylink_inband_caps(pl, interface);
	unsigned int mask;

	/* If the PCS doesn't implement inband support, be permissive. */
	if (!inband)
		return true;

	if (linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, adv))
		mask = LINK_INBAND_ENABLE;
	else
		mask = LINK_INBAND_DISABLE;

	/* Check whether the PCS implements the required mode */
	return !!(inband & mask);
}

/**
 * phylink_ethtool_ksettings_set() - set the link settings
 * @pl: a pointer to a &struct phylink returned from phylink_create()
@@ -2662,6 +2709,13 @@ int phylink_ethtool_ksettings_set(struct phylink *pl,
	    phylink_is_empty_linkmode(config.advertising))
		return -EINVAL;

	/* Validate the autonegotiation state. We don't have a PHY in this
	 * situation, so the PCS is the media-facing entity.
	 */
	if (!phylink_validate_pcs_inband_autoneg(pl, config.interface,
						 config.advertising))
		return -EINVAL;

	mutex_lock(&pl->state_mutex);
	pl->link_config.speed = config.speed;
	pl->link_config.duplex = config.duplex;
@@ -3341,6 +3395,12 @@ static int phylink_sfp_config_optical(struct phylink *pl)
	phylink_dbg(pl, "optical SFP: chosen %s interface\n",
		    phy_modes(interface));

	if (!phylink_validate_pcs_inband_autoneg(pl, interface,
						 config.advertising)) {
		phylink_err(pl, "autoneg setting not compatible with PCS");
		return -EINVAL;
	}

	config.interface = interface;

	/* Ignore errors if we're expecting a PHY to attach later */
+17 −0
Original line number Diff line number Diff line
@@ -419,6 +419,7 @@ struct phylink_pcs {
/**
 * struct phylink_pcs_ops - MAC PCS operations structure.
 * @pcs_validate: validate the link configuration.
 * @pcs_inband_caps: query inband support for interface mode.
 * @pcs_enable: enable the PCS.
 * @pcs_disable: disable the PCS.
 * @pcs_pre_config: pre-mac_config method (for errata)
@@ -434,6 +435,8 @@ struct phylink_pcs {
struct phylink_pcs_ops {
	int (*pcs_validate)(struct phylink_pcs *pcs, unsigned long *supported,
			    const struct phylink_link_state *state);
	unsigned int (*pcs_inband_caps)(struct phylink_pcs *pcs,
					phy_interface_t interface);
	int (*pcs_enable)(struct phylink_pcs *pcs);
	void (*pcs_disable)(struct phylink_pcs *pcs);
	void (*pcs_pre_config)(struct phylink_pcs *pcs,
@@ -470,6 +473,20 @@ struct phylink_pcs_ops {
int pcs_validate(struct phylink_pcs *pcs, unsigned long *supported,
		 const struct phylink_link_state *state);

/**
 * pcs_inband_caps - query PCS in-band capabilities for interface mode.
 * @pcs: a pointer to a &struct phylink_pcs.
 * @interface: interface mode to be queried
 *
 * Returns zero if it is unknown what in-band signalling is supported by the
 * PHY (e.g. because the PHY driver doesn't implement the method.) Otherwise,
 * returns a bit mask of the LINK_INBAND_* values from
 * &enum link_inband_signalling to describe which inband modes are supported
 * for this interface mode.
 */
unsigned int pcs_inband_caps(struct phylink_pcs *pcs,
			     phy_interface_t interface);

/**
 * pcs_enable() - enable the PCS.
 * @pcs: a pointer to a &struct phylink_pcs.