Commit fd580c98 authored by Russell King's avatar Russell King Committed by David S. Miller

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: default avatarRussell King <rmk+kernel@armlinux.org.uk>
Signed-off-by: default avatarMarek Behún <kabel@kernel.org>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 1645f44d
...@@ -676,6 +676,7 @@ static int at803x_sfp_insert(void *upstream, const struct sfp_eeprom_id *id) ...@@ -676,6 +676,7 @@ static int at803x_sfp_insert(void *upstream, const struct sfp_eeprom_id *id)
struct phy_device *phydev = upstream; struct phy_device *phydev = upstream;
__ETHTOOL_DECLARE_LINK_MODE_MASK(phy_support); __ETHTOOL_DECLARE_LINK_MODE_MASK(phy_support);
__ETHTOOL_DECLARE_LINK_MODE_MASK(sfp_support); __ETHTOOL_DECLARE_LINK_MODE_MASK(sfp_support);
DECLARE_PHY_INTERFACE_MASK(interfaces);
phy_interface_t iface; phy_interface_t iface;
linkmode_zero(phy_support); linkmode_zero(phy_support);
...@@ -686,7 +687,7 @@ static int at803x_sfp_insert(void *upstream, const struct sfp_eeprom_id *id) ...@@ -686,7 +687,7 @@ static int at803x_sfp_insert(void *upstream, const struct sfp_eeprom_id *id)
phylink_set(phy_support, Asym_Pause); phylink_set(phy_support, Asym_Pause);
linkmode_zero(sfp_support); 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. /* Some modules support 10G modes as well as others we support.
* Mask out non-supported modes so the correct interface is picked. * Mask out non-supported modes so the correct interface is picked.
*/ */
......
...@@ -478,6 +478,7 @@ static int mv2222_config_init(struct phy_device *phydev) ...@@ -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) static int mv2222_sfp_insert(void *upstream, const struct sfp_eeprom_id *id)
{ {
DECLARE_PHY_INTERFACE_MASK(interfaces);
struct phy_device *phydev = upstream; struct phy_device *phydev = upstream;
phy_interface_t sfp_interface; phy_interface_t sfp_interface;
struct mv2222_data *priv; struct mv2222_data *priv;
...@@ -489,7 +490,7 @@ static int mv2222_sfp_insert(void *upstream, const struct sfp_eeprom_id *id) ...@@ -489,7 +490,7 @@ static int mv2222_sfp_insert(void *upstream, const struct sfp_eeprom_id *id)
priv = (struct mv2222_data *)phydev->priv; priv = (struct mv2222_data *)phydev->priv;
dev = &phydev->mdio.dev; 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); phydev->port = sfp_parse_port(phydev->sfp_bus, id, sfp_supported);
sfp_interface = sfp_select_interface(phydev->sfp_bus, sfp_supported); sfp_interface = sfp_select_interface(phydev->sfp_bus, sfp_supported);
......
...@@ -2845,6 +2845,7 @@ static int marvell_probe(struct phy_device *phydev) ...@@ -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) static int m88e1510_sfp_insert(void *upstream, const struct sfp_eeprom_id *id)
{ {
DECLARE_PHY_INTERFACE_MASK(interfaces);
struct phy_device *phydev = upstream; struct phy_device *phydev = upstream;
phy_interface_t interface; phy_interface_t interface;
struct device *dev; struct device *dev;
...@@ -2856,7 +2857,7 @@ static int m88e1510_sfp_insert(void *upstream, const struct sfp_eeprom_id *id) ...@@ -2856,7 +2857,7 @@ static int m88e1510_sfp_insert(void *upstream, const struct sfp_eeprom_id *id)
dev = &phydev->mdio.dev; 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); interface = sfp_select_interface(phydev->sfp_bus, supported);
dev_info(dev, "%s SFP module inserted\n", phy_modes(interface)); dev_info(dev, "%s SFP module inserted\n", phy_modes(interface));
......
...@@ -466,9 +466,10 @@ static int mv3310_sfp_insert(void *upstream, const struct sfp_eeprom_id *id) ...@@ -466,9 +466,10 @@ static int mv3310_sfp_insert(void *upstream, const struct sfp_eeprom_id *id)
{ {
struct phy_device *phydev = upstream; struct phy_device *phydev = upstream;
__ETHTOOL_DECLARE_LINK_MODE_MASK(support) = { 0, }; __ETHTOOL_DECLARE_LINK_MODE_MASK(support) = { 0, };
DECLARE_PHY_INTERFACE_MASK(interfaces);
phy_interface_t iface; 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); iface = sfp_select_interface(phydev->sfp_bus, support);
if (iface != PHY_INTERFACE_MODE_10GBASER) { if (iface != PHY_INTERFACE_MODE_10GBASER) {
......
...@@ -77,6 +77,7 @@ struct phylink { ...@@ -77,6 +77,7 @@ struct phylink {
struct sfp_bus *sfp_bus; struct sfp_bus *sfp_bus;
bool sfp_may_have_phy; bool sfp_may_have_phy;
DECLARE_PHY_INTERFACE_MASK(sfp_interfaces);
__ETHTOOL_DECLARE_LINK_MODE_MASK(sfp_support); __ETHTOOL_DECLARE_LINK_MODE_MASK(sfp_support);
u8 sfp_port; u8 sfp_port;
}; };
...@@ -2898,7 +2899,8 @@ static int phylink_sfp_module_insert(void *upstream, ...@@ -2898,7 +2899,8 @@ static int phylink_sfp_module_insert(void *upstream,
ASSERT_RTNL(); ASSERT_RTNL();
linkmode_zero(support); 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); pl->sfp_port = sfp_parse_port(pl->sfp_bus, id, support);
/* If this module may have a PHY connecting later, defer until later */ /* If this module may have a PHY connecting later, defer until later */
......
...@@ -139,12 +139,14 @@ EXPORT_SYMBOL_GPL(sfp_may_have_phy); ...@@ -139,12 +139,14 @@ EXPORT_SYMBOL_GPL(sfp_may_have_phy);
* @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
* @id: a pointer to the module's &struct sfp_eeprom_id * @id: a pointer to the module's &struct sfp_eeprom_id
* @support: pointer to an array of unsigned long for the ethtool support mask * @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 * Parse the EEPROM identification information and derive the supported
* ethtool link modes for the module. * ethtool link modes for the module.
*/ */
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, unsigned long *interfaces)
{ {
unsigned int br_min, br_nom, br_max; unsigned int br_min, br_nom, br_max;
__ETHTOOL_DECLARE_LINK_MODE_MASK(modes) = { 0, }; __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, ...@@ -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. */ /* Set ethtool support from the compliance fields. */
if (id->base.e10g_base_sr) if (id->base.e10g_base_sr) {
phylink_set(modes, 10000baseSR_Full); 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); 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); 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); phylink_set(modes, 10000baseER_Full);
__set_bit(PHY_INTERFACE_MODE_10GBASER, interfaces);
}
if (id->base.e1000_base_sx || if (id->base.e1000_base_sx ||
id->base.e1000_base_lx || id->base.e1000_base_lx ||
id->base.e1000_base_cx) id->base.e1000_base_cx) {
phylink_set(modes, 1000baseX_Full); phylink_set(modes, 1000baseX_Full);
__set_bit(PHY_INTERFACE_MODE_1000BASEX, interfaces);
}
if (id->base.e1000_base_t) { if (id->base.e1000_base_t) {
phylink_set(modes, 1000baseT_Half); phylink_set(modes, 1000baseT_Half);
phylink_set(modes, 1000baseT_Full); phylink_set(modes, 1000baseT_Full);
__set_bit(PHY_INTERFACE_MODE_1000BASEX, interfaces);
__set_bit(PHY_INTERFACE_MODE_SGMII, interfaces);
} }
/* 1000Base-PX or 1000Base-BX10 */ /* 1000Base-PX or 1000Base-BX10 */
if ((id->base.e_base_px || id->base.e_base_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); phylink_set(modes, 1000baseX_Full);
__set_bit(PHY_INTERFACE_MODE_1000BASEX, interfaces);
}
/* 100Base-FX, 100Base-LX, 100Base-PX, 100Base-BX10 */ /* 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); 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); phylink_set(modes, 100baseFX_Full);
__set_bit(PHY_INTERFACE_MODE_100BASEX, interfaces);
}
/* For active or passive cables, select the link modes /* For active or passive cables, select the link modes
* based on the bit rates and the cable compliance bytes. * based on the bit rates and the cable compliance bytes.
*/ */
if ((id->base.sfp_ct_passive || id->base.sfp_ct_active) && br_nom) { if ((id->base.sfp_ct_passive || id->base.sfp_ct_active) && br_nom) {
/* This may look odd, but some manufacturers use 12000MBd */ /* 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); 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); 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); phylink_set(modes, 1000baseX_Full);
__set_bit(PHY_INTERFACE_MODE_1000BASEX, interfaces);
}
} }
if (id->base.sfp_ct_passive) { 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); phylink_set(modes, 10000baseCR_Full);
__set_bit(PHY_INTERFACE_MODE_10GBASER, interfaces);
}
} }
if (id->base.sfp_ct_active) { if (id->base.sfp_ct_active) {
if (id->base.active.sff8431_app_e || if (id->base.active.sff8431_app_e ||
id->base.active.sff8431_lim) { id->base.active.sff8431_lim) {
phylink_set(modes, 10000baseCR_Full); 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, ...@@ -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_SFI:
case SFF8024_ECC_10GBASE_T_SR: case SFF8024_ECC_10GBASE_T_SR:
phylink_set(modes, 10000baseT_Full); phylink_set(modes, 10000baseT_Full);
__set_bit(PHY_INTERFACE_MODE_10GBASER, interfaces);
break; break;
case SFF8024_ECC_5GBASE_T: case SFF8024_ECC_5GBASE_T:
phylink_set(modes, 5000baseT_Full); phylink_set(modes, 5000baseT_Full);
break; break;
case SFF8024_ECC_2_5GBASE_T: case SFF8024_ECC_2_5GBASE_T:
phylink_set(modes, 2500baseT_Full); phylink_set(modes, 2500baseT_Full);
__set_bit(PHY_INTERFACE_MODE_2500BASEX, interfaces);
break; break;
default: default:
dev_warn(bus->sfp_dev, dev_warn(bus->sfp_dev,
...@@ -261,10 +292,14 @@ void sfp_parse_support(struct sfp_bus *bus, const struct sfp_eeprom_id *id, ...@@ -261,10 +292,14 @@ void sfp_parse_support(struct sfp_bus *bus, const struct sfp_eeprom_id *id,
if (id->base.fc_speed_100 || if (id->base.fc_speed_100 ||
id->base.fc_speed_200 || id->base.fc_speed_200 ||
id->base.fc_speed_400) { id->base.fc_speed_400) {
if (id->base.br_nominal >= 31) if (id->base.br_nominal >= 31) {
phylink_set(modes, 2500baseX_Full); 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); phylink_set(modes, 1000baseX_Full);
__set_bit(PHY_INTERFACE_MODE_1000BASEX, interfaces);
}
} }
/* If we haven't discovered any modes that this module supports, try /* 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, ...@@ -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. * 2500BASE-X, so we allow some slack here.
*/ */
if (bitmap_empty(modes, __ETHTOOL_LINK_MODE_MASK_NBITS) && br_nom) { 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); 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); phylink_set(modes, 2500baseX_Full);
__set_bit(PHY_INTERFACE_MODE_2500BASEX, interfaces);
}
} }
if (bus->sfp_quirk && bus->sfp_quirk->modes) 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); linkmode_or(support, support, modes);
......
...@@ -331,13 +331,16 @@ static void sfp_fixup_halny_gsfp(struct sfp *sfp) ...@@ -331,13 +331,16 @@ static void sfp_fixup_halny_gsfp(struct sfp *sfp)
} }
static void sfp_quirk_2500basex(const struct sfp_eeprom_id *id, 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); 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, 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 /* Ubiquiti U-Fiber Instant module claims that support all transceiver
* types including 10G Ethernet which is not truth. So clear all claimed * types including 10G Ethernet which is not truth. So clear all claimed
......
...@@ -9,7 +9,8 @@ struct sfp; ...@@ -9,7 +9,8 @@ struct sfp;
struct sfp_quirk { struct sfp_quirk {
const char *vendor; const char *vendor;
const char *part; 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); void (*fixup)(struct sfp *sfp);
}; };
......
...@@ -535,7 +535,7 @@ int sfp_parse_port(struct sfp_bus *bus, const struct sfp_eeprom_id *id, ...@@ -535,7 +535,7 @@ int sfp_parse_port(struct sfp_bus *bus, const struct sfp_eeprom_id *id,
unsigned long *support); unsigned long *support);
bool sfp_may_have_phy(struct sfp_bus *bus, const struct sfp_eeprom_id *id); 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, 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, phy_interface_t sfp_select_interface(struct sfp_bus *bus,
unsigned long *link_modes); unsigned long *link_modes);
...@@ -568,7 +568,8 @@ static inline bool sfp_may_have_phy(struct sfp_bus *bus, ...@@ -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, 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,
unsigned long *interfaces)
{ {
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment