mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-10-01 06:33:07 +00:00
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: Russell King <rmk+kernel@armlinux.org.uk> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
03145864bd
commit
a9c79364df
3 changed files with 68 additions and 84 deletions
|
@ -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),
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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,18 +445,19 @@ 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,
|
|
||||||
const struct sfp_eeprom_id *id)
|
|
||||||
{
|
|
||||||
return PHY_INTERFACE_MODE_NA;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void sfp_parse_support(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)
|
unsigned long *support)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline phy_interface_t sfp_select_interface(struct sfp_bus *bus,
|
||||||
|
const struct sfp_eeprom_id *id,
|
||||||
|
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,
|
||||||
struct ethtool_modinfo *modinfo)
|
struct ethtool_modinfo *modinfo)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue