net: sfp: augment SFP parsing with phy_interface_t bitmap

We currently parse the SFP EEPROM to a bitmap of ethtool link modes,
and then attempt to convert the link modes to a PHY interface mode.
While this works at present, there are cases where this is sub-optimal.
For example, where a module can operate with several different PHY
interface modes.

To start addressing this, arrange for the SFP EEPROM parsing to also
provide a bitmap of the possible PHY interface modes.

Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
Signed-off-by: Marek Behún <kabel@kernel.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Russell King 2022-09-30 16:21:00 +02:00 committed by David S. Miller
parent 1645f44dd5
commit fd580c9830
9 changed files with 78 additions and 28 deletions

View File

@ -676,6 +676,7 @@ static int at803x_sfp_insert(void *upstream, const struct sfp_eeprom_id *id)
struct phy_device *phydev = upstream;
__ETHTOOL_DECLARE_LINK_MODE_MASK(phy_support);
__ETHTOOL_DECLARE_LINK_MODE_MASK(sfp_support);
DECLARE_PHY_INTERFACE_MASK(interfaces);
phy_interface_t iface;
linkmode_zero(phy_support);
@ -686,7 +687,7 @@ static int at803x_sfp_insert(void *upstream, const struct sfp_eeprom_id *id)
phylink_set(phy_support, Asym_Pause);
linkmode_zero(sfp_support);
sfp_parse_support(phydev->sfp_bus, id, sfp_support);
sfp_parse_support(phydev->sfp_bus, id, sfp_support, interfaces);
/* Some modules support 10G modes as well as others we support.
* Mask out non-supported modes so the correct interface is picked.
*/

View File

@ -478,6 +478,7 @@ static int mv2222_config_init(struct phy_device *phydev)
static int mv2222_sfp_insert(void *upstream, const struct sfp_eeprom_id *id)
{
DECLARE_PHY_INTERFACE_MASK(interfaces);
struct phy_device *phydev = upstream;
phy_interface_t sfp_interface;
struct mv2222_data *priv;
@ -489,7 +490,7 @@ static int mv2222_sfp_insert(void *upstream, const struct sfp_eeprom_id *id)
priv = (struct mv2222_data *)phydev->priv;
dev = &phydev->mdio.dev;
sfp_parse_support(phydev->sfp_bus, id, sfp_supported);
sfp_parse_support(phydev->sfp_bus, id, sfp_supported, interfaces);
phydev->port = sfp_parse_port(phydev->sfp_bus, id, sfp_supported);
sfp_interface = sfp_select_interface(phydev->sfp_bus, sfp_supported);

View File

@ -2845,6 +2845,7 @@ static int marvell_probe(struct phy_device *phydev)
static int m88e1510_sfp_insert(void *upstream, const struct sfp_eeprom_id *id)
{
DECLARE_PHY_INTERFACE_MASK(interfaces);
struct phy_device *phydev = upstream;
phy_interface_t interface;
struct device *dev;
@ -2856,7 +2857,7 @@ static int m88e1510_sfp_insert(void *upstream, const struct sfp_eeprom_id *id)
dev = &phydev->mdio.dev;
sfp_parse_support(phydev->sfp_bus, id, supported);
sfp_parse_support(phydev->sfp_bus, id, supported, interfaces);
interface = sfp_select_interface(phydev->sfp_bus, supported);
dev_info(dev, "%s SFP module inserted\n", phy_modes(interface));

View File

@ -466,9 +466,10 @@ static int mv3310_sfp_insert(void *upstream, const struct sfp_eeprom_id *id)
{
struct phy_device *phydev = upstream;
__ETHTOOL_DECLARE_LINK_MODE_MASK(support) = { 0, };
DECLARE_PHY_INTERFACE_MASK(interfaces);
phy_interface_t iface;
sfp_parse_support(phydev->sfp_bus, id, support);
sfp_parse_support(phydev->sfp_bus, id, support, interfaces);
iface = sfp_select_interface(phydev->sfp_bus, support);
if (iface != PHY_INTERFACE_MODE_10GBASER) {

View File

@ -77,6 +77,7 @@ struct phylink {
struct sfp_bus *sfp_bus;
bool sfp_may_have_phy;
DECLARE_PHY_INTERFACE_MASK(sfp_interfaces);
__ETHTOOL_DECLARE_LINK_MODE_MASK(sfp_support);
u8 sfp_port;
};
@ -2898,7 +2899,8 @@ static int phylink_sfp_module_insert(void *upstream,
ASSERT_RTNL();
linkmode_zero(support);
sfp_parse_support(pl->sfp_bus, id, support);
phy_interface_zero(pl->sfp_interfaces);
sfp_parse_support(pl->sfp_bus, id, support, pl->sfp_interfaces);
pl->sfp_port = sfp_parse_port(pl->sfp_bus, id, support);
/* If this module may have a PHY connecting later, defer until later */

View File

@ -139,12 +139,14 @@ EXPORT_SYMBOL_GPL(sfp_may_have_phy);
* @bus: a pointer to the &struct sfp_bus structure for the sfp module
* @id: a pointer to the module's &struct sfp_eeprom_id
* @support: pointer to an array of unsigned long for the ethtool support mask
* @interfaces: pointer to an array of unsigned long for phy interface modes
* mask
*
* Parse the EEPROM identification information and derive the supported
* ethtool link modes for the module.
*/
void sfp_parse_support(struct sfp_bus *bus, const struct sfp_eeprom_id *id,
unsigned long *support)
unsigned long *support, unsigned long *interfaces)
{
unsigned int br_min, br_nom, br_max;
__ETHTOOL_DECLARE_LINK_MODE_MASK(modes) = { 0, };
@ -171,54 +173,81 @@ void sfp_parse_support(struct sfp_bus *bus, const struct sfp_eeprom_id *id,
}
/* Set ethtool support from the compliance fields. */
if (id->base.e10g_base_sr)
if (id->base.e10g_base_sr) {
phylink_set(modes, 10000baseSR_Full);
if (id->base.e10g_base_lr)
__set_bit(PHY_INTERFACE_MODE_10GBASER, interfaces);
}
if (id->base.e10g_base_lr) {
phylink_set(modes, 10000baseLR_Full);
if (id->base.e10g_base_lrm)
__set_bit(PHY_INTERFACE_MODE_10GBASER, interfaces);
}
if (id->base.e10g_base_lrm) {
phylink_set(modes, 10000baseLRM_Full);
if (id->base.e10g_base_er)
__set_bit(PHY_INTERFACE_MODE_10GBASER, interfaces);
}
if (id->base.e10g_base_er) {
phylink_set(modes, 10000baseER_Full);
__set_bit(PHY_INTERFACE_MODE_10GBASER, interfaces);
}
if (id->base.e1000_base_sx ||
id->base.e1000_base_lx ||
id->base.e1000_base_cx)
id->base.e1000_base_cx) {
phylink_set(modes, 1000baseX_Full);
__set_bit(PHY_INTERFACE_MODE_1000BASEX, interfaces);
}
if (id->base.e1000_base_t) {
phylink_set(modes, 1000baseT_Half);
phylink_set(modes, 1000baseT_Full);
__set_bit(PHY_INTERFACE_MODE_1000BASEX, interfaces);
__set_bit(PHY_INTERFACE_MODE_SGMII, interfaces);
}
/* 1000Base-PX or 1000Base-BX10 */
if ((id->base.e_base_px || id->base.e_base_bx10) &&
br_min <= 1300 && br_max >= 1200)
br_min <= 1300 && br_max >= 1200) {
phylink_set(modes, 1000baseX_Full);
__set_bit(PHY_INTERFACE_MODE_1000BASEX, interfaces);
}
/* 100Base-FX, 100Base-LX, 100Base-PX, 100Base-BX10 */
if (id->base.e100_base_fx || id->base.e100_base_lx)
if (id->base.e100_base_fx || id->base.e100_base_lx) {
phylink_set(modes, 100baseFX_Full);
if ((id->base.e_base_px || id->base.e_base_bx10) && br_nom == 100)
__set_bit(PHY_INTERFACE_MODE_100BASEX, interfaces);
}
if ((id->base.e_base_px || id->base.e_base_bx10) && br_nom == 100) {
phylink_set(modes, 100baseFX_Full);
__set_bit(PHY_INTERFACE_MODE_100BASEX, interfaces);
}
/* For active or passive cables, select the link modes
* based on the bit rates and the cable compliance bytes.
*/
if ((id->base.sfp_ct_passive || id->base.sfp_ct_active) && br_nom) {
/* This may look odd, but some manufacturers use 12000MBd */
if (br_min <= 12000 && br_max >= 10300)
if (br_min <= 12000 && br_max >= 10300) {
phylink_set(modes, 10000baseCR_Full);
if (br_min <= 3200 && br_max >= 3100)
__set_bit(PHY_INTERFACE_MODE_10GBASER, interfaces);
}
if (br_min <= 3200 && br_max >= 3100) {
phylink_set(modes, 2500baseX_Full);
if (br_min <= 1300 && br_max >= 1200)
__set_bit(PHY_INTERFACE_MODE_2500BASEX, interfaces);
}
if (br_min <= 1300 && br_max >= 1200) {
phylink_set(modes, 1000baseX_Full);
__set_bit(PHY_INTERFACE_MODE_1000BASEX, interfaces);
}
}
if (id->base.sfp_ct_passive) {
if (id->base.passive.sff8431_app_e)
if (id->base.passive.sff8431_app_e) {
phylink_set(modes, 10000baseCR_Full);
__set_bit(PHY_INTERFACE_MODE_10GBASER, interfaces);
}
}
if (id->base.sfp_ct_active) {
if (id->base.active.sff8431_app_e ||
id->base.active.sff8431_lim) {
phylink_set(modes, 10000baseCR_Full);
__set_bit(PHY_INTERFACE_MODE_10GBASER, interfaces);
}
}
@ -243,12 +272,14 @@ void sfp_parse_support(struct sfp_bus *bus, const struct sfp_eeprom_id *id,
case SFF8024_ECC_10GBASE_T_SFI:
case SFF8024_ECC_10GBASE_T_SR:
phylink_set(modes, 10000baseT_Full);
__set_bit(PHY_INTERFACE_MODE_10GBASER, interfaces);
break;
case SFF8024_ECC_5GBASE_T:
phylink_set(modes, 5000baseT_Full);
break;
case SFF8024_ECC_2_5GBASE_T:
phylink_set(modes, 2500baseT_Full);
__set_bit(PHY_INTERFACE_MODE_2500BASEX, interfaces);
break;
default:
dev_warn(bus->sfp_dev,
@ -261,10 +292,14 @@ void sfp_parse_support(struct sfp_bus *bus, const struct sfp_eeprom_id *id,
if (id->base.fc_speed_100 ||
id->base.fc_speed_200 ||
id->base.fc_speed_400) {
if (id->base.br_nominal >= 31)
if (id->base.br_nominal >= 31) {
phylink_set(modes, 2500baseX_Full);
if (id->base.br_nominal >= 12)
__set_bit(PHY_INTERFACE_MODE_2500BASEX, interfaces);
}
if (id->base.br_nominal >= 12) {
phylink_set(modes, 1000baseX_Full);
__set_bit(PHY_INTERFACE_MODE_1000BASEX, interfaces);
}
}
/* If we haven't discovered any modes that this module supports, try
@ -277,14 +312,18 @@ void sfp_parse_support(struct sfp_bus *bus, const struct sfp_eeprom_id *id,
* 2500BASE-X, so we allow some slack here.
*/
if (bitmap_empty(modes, __ETHTOOL_LINK_MODE_MASK_NBITS) && br_nom) {
if (br_min <= 1300 && br_max >= 1200)
if (br_min <= 1300 && br_max >= 1200) {
phylink_set(modes, 1000baseX_Full);
if (br_min <= 3200 && br_max >= 2500)
__set_bit(PHY_INTERFACE_MODE_1000BASEX, interfaces);
}
if (br_min <= 3200 && br_max >= 2500) {
phylink_set(modes, 2500baseX_Full);
__set_bit(PHY_INTERFACE_MODE_2500BASEX, interfaces);
}
}
if (bus->sfp_quirk && bus->sfp_quirk->modes)
bus->sfp_quirk->modes(id, modes);
bus->sfp_quirk->modes(id, modes, interfaces);
linkmode_or(support, support, modes);

View File

@ -331,13 +331,16 @@ static void sfp_fixup_halny_gsfp(struct sfp *sfp)
}
static void sfp_quirk_2500basex(const struct sfp_eeprom_id *id,
unsigned long *modes)
unsigned long *modes,
unsigned long *interfaces)
{
linkmode_set_bit(ETHTOOL_LINK_MODE_2500baseX_Full_BIT, modes);
__set_bit(PHY_INTERFACE_MODE_2500BASEX, interfaces);
}
static void sfp_quirk_ubnt_uf_instant(const struct sfp_eeprom_id *id,
unsigned long *modes)
unsigned long *modes,
unsigned long *interfaces)
{
/* Ubiquiti U-Fiber Instant module claims that support all transceiver
* types including 10G Ethernet which is not truth. So clear all claimed

View File

@ -9,7 +9,8 @@ struct sfp;
struct sfp_quirk {
const char *vendor;
const char *part;
void (*modes)(const struct sfp_eeprom_id *id, unsigned long *modes);
void (*modes)(const struct sfp_eeprom_id *id, unsigned long *modes,
unsigned long *interfaces);
void (*fixup)(struct sfp *sfp);
};

View File

@ -535,7 +535,7 @@ int sfp_parse_port(struct sfp_bus *bus, const struct sfp_eeprom_id *id,
unsigned long *support);
bool sfp_may_have_phy(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, unsigned long *interfaces);
phy_interface_t sfp_select_interface(struct sfp_bus *bus,
unsigned long *link_modes);
@ -568,7 +568,8 @@ static inline bool sfp_may_have_phy(struct sfp_bus *bus,
static inline void sfp_parse_support(struct sfp_bus *bus,
const struct sfp_eeprom_id *id,
unsigned long *support)
unsigned long *support,
unsigned long *interfaces)
{
}