Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a9c79364 authored by Russell King's avatar Russell King Committed by David S. Miller
Browse files

phylink,sfp: negotiate interface format with MAC



Negotiate the interface format with the MAC rather than requiring it to
be a fixed type specified solely by the SFP module.  This allows modules
that can work with several different interface signalling formats to
select a format compatible with the MAC - for example, a Fiber module
supporing Gigabit ethernet and faster connected to a Gigabit only MAC
needs to select the 1000BASE-X mode.

Signed-off-by: default avatarRussell King <rmk+kernel@armlinux.org.uk>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 03145864
Loading
Loading
Loading
Loading
+19 −14
Original line number Original line Diff line number Diff line
@@ -1584,25 +1584,14 @@ static int phylink_sfp_module_insert(void *upstream,
	bool changed;
	bool changed;
	u8 port;
	u8 port;


	sfp_parse_support(pl->sfp_bus, id, support);
	port = sfp_parse_port(pl->sfp_bus, id, support);
	iface = sfp_parse_interface(pl->sfp_bus, id);

	ASSERT_RTNL();
	ASSERT_RTNL();


	switch (iface) {
	sfp_parse_support(pl->sfp_bus, id, support);
	case PHY_INTERFACE_MODE_SGMII:
	port = sfp_parse_port(pl->sfp_bus, id, support);
	case PHY_INTERFACE_MODE_1000BASEX:
	case PHY_INTERFACE_MODE_2500BASEX:
	case PHY_INTERFACE_MODE_10GKR:
		break;
	default:
		return -EINVAL;
	}


	memset(&config, 0, sizeof(config));
	memset(&config, 0, sizeof(config));
	linkmode_copy(config.advertising, support);
	linkmode_copy(config.advertising, support);
	config.interface = iface;
	config.interface = PHY_INTERFACE_MODE_NA;
	config.speed = SPEED_UNKNOWN;
	config.speed = SPEED_UNKNOWN;
	config.duplex = DUPLEX_UNKNOWN;
	config.duplex = DUPLEX_UNKNOWN;
	config.pause = MLO_PAUSE_AN;
	config.pause = MLO_PAUSE_AN;
@@ -1610,6 +1599,22 @@ static int phylink_sfp_module_insert(void *upstream,


	/* Ignore errors if we're expecting a PHY to attach later */
	/* Ignore errors if we're expecting a PHY to attach later */
	ret = phylink_validate(pl, support, &config);
	ret = phylink_validate(pl, support, &config);
	if (ret) {
		netdev_err(pl->netdev, "validation with support %*pb failed: %d\n",
			   __ETHTOOL_LINK_MODE_MASK_NBITS, support, ret);
		return ret;
	}

	iface = sfp_select_interface(pl->sfp_bus, id, config.advertising);
	if (iface == PHY_INTERFACE_MODE_NA) {
		netdev_err(pl->netdev,
			   "selection of interface failed, advertisment %*pb\n",
			   __ETHTOOL_LINK_MODE_MASK_NBITS, config.advertising);
		return -EINVAL;
	}

	config.interface = iface;
	ret = phylink_validate(pl, support, &config);
	if (ret) {
	if (ret) {
		netdev_err(pl->netdev, "validation of %s/%s with support %*pb failed: %d\n",
		netdev_err(pl->netdev, "validation of %s/%s with support %*pb failed: %d\n",
			   phylink_an_mode_str(MLO_AN_INBAND),
			   phylink_an_mode_str(MLO_AN_INBAND),
+39 −62
Original line number Original line Diff line number Diff line
@@ -105,68 +105,6 @@ int sfp_parse_port(struct sfp_bus *bus, const struct sfp_eeprom_id *id,
}
}
EXPORT_SYMBOL_GPL(sfp_parse_port);
EXPORT_SYMBOL_GPL(sfp_parse_port);


/**
 * sfp_parse_interface() - Parse the phy_interface_t
 * @bus: a pointer to the &struct sfp_bus structure for the sfp module
 * @id: a pointer to the module's &struct sfp_eeprom_id
 *
 * Derive the phy_interface_t mode for the information found in the
 * module's identifying EEPROM. There is no standard or defined way
 * to derive this information, so we use some heuristics.
 *
 * If the encoding is 64b66b, then the module must be >= 10G, so
 * return %PHY_INTERFACE_MODE_10GKR.
 *
 * If it's 8b10b, then it's 1G or slower. If it's definitely a fibre
 * module, return %PHY_INTERFACE_MODE_1000BASEX mode, otherwise return
 * %PHY_INTERFACE_MODE_SGMII mode.
 *
 * If the encoding is not known, return %PHY_INTERFACE_MODE_NA.
 */
phy_interface_t sfp_parse_interface(struct sfp_bus *bus,
				    const struct sfp_eeprom_id *id)
{
	phy_interface_t iface;

	/* Setting the serdes link mode is guesswork: there's no field in
	 * the EEPROM which indicates what mode should be used.
	 *
	 * If the module wants 64b66b, then it must be >= 10G.
	 *
	 * If it's a gigabit-only fiber module, it probably does not have
	 * a PHY, so switch to 802.3z negotiation mode. Otherwise, switch
	 * to SGMII mode (which is required to support non-gigabit speeds).
	 */
	switch (id->base.encoding) {
	case SFP_ENCODING_8472_64B66B:
		iface = PHY_INTERFACE_MODE_10GKR;
		break;

	case SFP_ENCODING_8B10B:
		if (!id->base.e1000_base_t &&
		    !id->base.e100_base_lx &&
		    !id->base.e100_base_fx)
			iface = PHY_INTERFACE_MODE_1000BASEX;
		else
			iface = PHY_INTERFACE_MODE_SGMII;
		break;

	default:
		if (id->base.e1000_base_cx) {
			iface = PHY_INTERFACE_MODE_1000BASEX;
			break;
		}

		iface = PHY_INTERFACE_MODE_NA;
		dev_err(bus->sfp_dev,
			"SFP module encoding does not support 8b10b nor 64b66b\n");
		break;
	}

	return iface;
}
EXPORT_SYMBOL_GPL(sfp_parse_interface);

/**
/**
 * sfp_parse_support() - Parse the eeprom id for supported link modes
 * sfp_parse_support() - Parse the eeprom id for supported link modes
 * @bus: a pointer to the &struct sfp_bus structure for the sfp module
 * @bus: a pointer to the &struct sfp_bus structure for the sfp module
@@ -296,6 +234,45 @@ void sfp_parse_support(struct sfp_bus *bus, const struct sfp_eeprom_id *id,
}
}
EXPORT_SYMBOL_GPL(sfp_parse_support);
EXPORT_SYMBOL_GPL(sfp_parse_support);


/**
 * sfp_select_interface() - Select appropriate phy_interface_t mode
 * @bus: a pointer to the &struct sfp_bus structure for the sfp module
 * @id: a pointer to the module's &struct sfp_eeprom_id
 * @link_modes: ethtool link modes mask
 *
 * Derive the phy_interface_t mode for the information found in the
 * module's identifying EEPROM and the link modes mask. There is no
 * standard or defined way to derive this information, so we decide
 * based upon the link mode mask.
 */
phy_interface_t sfp_select_interface(struct sfp_bus *bus,
				     const struct sfp_eeprom_id *id,
				     unsigned long *link_modes)
{
	if (phylink_test(link_modes, 10000baseCR_Full) ||
	    phylink_test(link_modes, 10000baseSR_Full) ||
	    phylink_test(link_modes, 10000baseLR_Full) ||
	    phylink_test(link_modes, 10000baseLRM_Full) ||
	    phylink_test(link_modes, 10000baseER_Full))
		return PHY_INTERFACE_MODE_10GKR;

	if (phylink_test(link_modes, 2500baseX_Full))
		return PHY_INTERFACE_MODE_2500BASEX;

	if (id->base.e1000_base_t ||
	    id->base.e100_base_lx ||
	    id->base.e100_base_fx)
		return PHY_INTERFACE_MODE_SGMII;

	if (phylink_test(link_modes, 1000baseX_Full))
		return PHY_INTERFACE_MODE_1000BASEX;

	dev_warn(bus->sfp_dev, "Unable to ascertain link mode\n");

	return PHY_INTERFACE_MODE_NA;
}
EXPORT_SYMBOL_GPL(sfp_select_interface);

static LIST_HEAD(sfp_buses);
static LIST_HEAD(sfp_buses);
static DEFINE_MUTEX(sfp_mutex);
static DEFINE_MUTEX(sfp_mutex);


+10 −8
Original line number Original line Diff line number Diff line
@@ -422,10 +422,11 @@ struct sfp_upstream_ops {
#if IS_ENABLED(CONFIG_SFP)
#if IS_ENABLED(CONFIG_SFP)
int sfp_parse_port(struct sfp_bus *bus, const struct sfp_eeprom_id *id,
int sfp_parse_port(struct sfp_bus *bus, const struct sfp_eeprom_id *id,
		   unsigned long *support);
		   unsigned long *support);
phy_interface_t sfp_parse_interface(struct sfp_bus *bus,
				    const struct sfp_eeprom_id *id);
void sfp_parse_support(struct sfp_bus *bus, const struct sfp_eeprom_id *id,
void sfp_parse_support(struct sfp_bus *bus, const struct sfp_eeprom_id *id,
		       unsigned long *support);
		       unsigned long *support);
phy_interface_t sfp_select_interface(struct sfp_bus *bus,
				     const struct sfp_eeprom_id *id,
				     unsigned long *link_modes);


int sfp_get_module_info(struct sfp_bus *bus, struct ethtool_modinfo *modinfo);
int sfp_get_module_info(struct sfp_bus *bus, struct ethtool_modinfo *modinfo);
int sfp_get_module_eeprom(struct sfp_bus *bus, struct ethtool_eeprom *ee,
int sfp_get_module_eeprom(struct sfp_bus *bus, struct ethtool_eeprom *ee,
@@ -444,16 +445,17 @@ static inline int sfp_parse_port(struct sfp_bus *bus,
	return PORT_OTHER;
	return PORT_OTHER;
}
}


static inline phy_interface_t sfp_parse_interface(struct sfp_bus *bus,
static inline void sfp_parse_support(struct sfp_bus *bus,
						const struct sfp_eeprom_id *id)
				     const struct sfp_eeprom_id *id,
				     unsigned long *support)
{
{
	return PHY_INTERFACE_MODE_NA;
}
}


static inline void sfp_parse_support(struct sfp_bus *bus,
static inline phy_interface_t sfp_select_interface(struct sfp_bus *bus,
						   const struct sfp_eeprom_id *id,
						   const struct sfp_eeprom_id *id,
				     unsigned long *support)
						   unsigned long *link_modes)
{
{
	return PHY_INTERFACE_MODE_NA;
}
}


static inline int sfp_get_module_info(struct sfp_bus *bus,
static inline int sfp_get_module_info(struct sfp_bus *bus,