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

net: phylink: use phy_interface_t bitmaps for optical modules

Where a MAC provides a phy_interface_t bitmap, use these bitmaps to
select the operating interface mode for optical SFP modules, rather
than using the linkmode bitmaps.
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 fd580c98
...@@ -2803,6 +2803,70 @@ static void phylink_sfp_detach(void *upstream, struct sfp_bus *bus) ...@@ -2803,6 +2803,70 @@ static void phylink_sfp_detach(void *upstream, struct sfp_bus *bus)
pl->netdev->sfp_bus = NULL; pl->netdev->sfp_bus = NULL;
} }
static const phy_interface_t phylink_sfp_interface_preference[] = {
PHY_INTERFACE_MODE_25GBASER,
PHY_INTERFACE_MODE_USXGMII,
PHY_INTERFACE_MODE_10GBASER,
PHY_INTERFACE_MODE_5GBASER,
PHY_INTERFACE_MODE_2500BASEX,
PHY_INTERFACE_MODE_SGMII,
PHY_INTERFACE_MODE_1000BASEX,
PHY_INTERFACE_MODE_100BASEX,
};
static phy_interface_t phylink_choose_sfp_interface(struct phylink *pl,
const unsigned long *intf)
{
phy_interface_t interface;
size_t i;
interface = PHY_INTERFACE_MODE_NA;
for (i = 0; i < ARRAY_SIZE(phylink_sfp_interface_preference); i++)
if (test_bit(phylink_sfp_interface_preference[i], intf)) {
interface = phylink_sfp_interface_preference[i];
break;
}
return interface;
}
static void phylink_sfp_set_config(struct phylink *pl, u8 mode,
unsigned long *supported,
struct phylink_link_state *state)
{
bool changed = false;
phylink_dbg(pl, "requesting link mode %s/%s with support %*pb\n",
phylink_an_mode_str(mode), phy_modes(state->interface),
__ETHTOOL_LINK_MODE_MASK_NBITS, supported);
if (!linkmode_equal(pl->supported, supported)) {
linkmode_copy(pl->supported, supported);
changed = true;
}
if (!linkmode_equal(pl->link_config.advertising, state->advertising)) {
linkmode_copy(pl->link_config.advertising, state->advertising);
changed = true;
}
if (pl->cur_link_an_mode != mode ||
pl->link_config.interface != state->interface) {
pl->cur_link_an_mode = mode;
pl->link_config.interface = state->interface;
changed = true;
phylink_info(pl, "switched to %s/%s link mode\n",
phylink_an_mode_str(mode),
phy_modes(state->interface));
}
if (changed && !test_bit(PHYLINK_DISABLE_STOPPED,
&pl->phylink_disable_state))
phylink_mac_initial_config(pl, false);
}
static int phylink_sfp_config(struct phylink *pl, u8 mode, static int phylink_sfp_config(struct phylink *pl, u8 mode,
const unsigned long *supported, const unsigned long *supported,
const unsigned long *advertising) const unsigned long *advertising)
...@@ -2811,7 +2875,6 @@ static int phylink_sfp_config(struct phylink *pl, u8 mode, ...@@ -2811,7 +2875,6 @@ static int phylink_sfp_config(struct phylink *pl, u8 mode,
__ETHTOOL_DECLARE_LINK_MODE_MASK(support); __ETHTOOL_DECLARE_LINK_MODE_MASK(support);
struct phylink_link_state config; struct phylink_link_state config;
phy_interface_t iface; phy_interface_t iface;
bool changed;
int ret; int ret;
linkmode_copy(support, supported); linkmode_copy(support, supported);
...@@ -2854,61 +2917,103 @@ static int phylink_sfp_config(struct phylink *pl, u8 mode, ...@@ -2854,61 +2917,103 @@ static int phylink_sfp_config(struct phylink *pl, u8 mode,
return ret; return ret;
} }
phylink_dbg(pl, "requesting link mode %s/%s with support %*pb\n",
phylink_an_mode_str(mode), phy_modes(config.interface),
__ETHTOOL_LINK_MODE_MASK_NBITS, support);
if (phy_interface_mode_is_8023z(iface) && pl->phydev) if (phy_interface_mode_is_8023z(iface) && pl->phydev)
return -EINVAL; return -EINVAL;
changed = !linkmode_equal(pl->supported, support) || pl->link_port = pl->sfp_port;
!linkmode_equal(pl->link_config.advertising,
config.advertising); phylink_sfp_set_config(pl, mode, support, &config);
if (changed) {
linkmode_copy(pl->supported, support); return 0;
linkmode_copy(pl->link_config.advertising, config.advertising); }
static int phylink_sfp_config_optical(struct phylink *pl)
{
__ETHTOOL_DECLARE_LINK_MODE_MASK(support);
DECLARE_PHY_INTERFACE_MASK(interfaces);
struct phylink_link_state config;
phy_interface_t interface;
int ret;
phylink_dbg(pl, "optical SFP: interfaces=[mac=%*pbl, sfp=%*pbl]\n",
(int)PHY_INTERFACE_MODE_MAX,
pl->config->supported_interfaces,
(int)PHY_INTERFACE_MODE_MAX,
pl->sfp_interfaces);
/* Find the union of the supported interfaces by the PCS/MAC and
* the SFP module.
*/
phy_interface_and(interfaces, pl->config->supported_interfaces,
pl->sfp_interfaces);
if (phy_interface_empty(interfaces)) {
phylink_err(pl, "unsupported SFP module: no common interface modes\n");
return -EINVAL;
} }
if (pl->cur_link_an_mode != mode || memset(&config, 0, sizeof(config));
pl->link_config.interface != config.interface) { linkmode_copy(support, pl->sfp_support);
pl->link_config.interface = config.interface; linkmode_copy(config.advertising, pl->sfp_support);
pl->cur_link_an_mode = mode; config.speed = SPEED_UNKNOWN;
config.duplex = DUPLEX_UNKNOWN;
config.pause = MLO_PAUSE_AN;
config.an_enabled = true;
changed = true; /* For all the interfaces that are supported, reduce the sfp_support
* mask to only those link modes that can be supported.
*/
ret = phylink_validate_mask(pl, pl->sfp_support, &config, interfaces);
if (ret) {
phylink_err(pl, "unsupported SFP module: validation with support %*pb failed\n",
__ETHTOOL_LINK_MODE_MASK_NBITS, support);
return ret;
}
phylink_info(pl, "switched to %s/%s link mode\n", interface = phylink_choose_sfp_interface(pl, interfaces);
phylink_an_mode_str(mode), if (interface == PHY_INTERFACE_MODE_NA) {
phy_modes(config.interface)); phylink_err(pl, "failed to select SFP interface\n");
return -EINVAL;
}
phylink_dbg(pl, "optical SFP: chosen %s interface\n",
phy_modes(interface));
config.interface = interface;
/* Ignore errors if we're expecting a PHY to attach later */
ret = phylink_validate(pl, support, &config);
if (ret) {
phylink_err(pl, "validation with support %*pb failed: %pe\n",
__ETHTOOL_LINK_MODE_MASK_NBITS, support,
ERR_PTR(ret));
return ret;
} }
pl->link_port = pl->sfp_port; pl->link_port = pl->sfp_port;
if (changed && !test_bit(PHYLINK_DISABLE_STOPPED, phylink_sfp_set_config(pl, MLO_AN_INBAND, pl->sfp_support, &config);
&pl->phylink_disable_state))
phylink_mac_initial_config(pl, false);
return ret; return 0;
} }
static int phylink_sfp_module_insert(void *upstream, static int phylink_sfp_module_insert(void *upstream,
const struct sfp_eeprom_id *id) const struct sfp_eeprom_id *id)
{ {
struct phylink *pl = upstream; struct phylink *pl = upstream;
unsigned long *support = pl->sfp_support;
ASSERT_RTNL(); ASSERT_RTNL();
linkmode_zero(support); linkmode_zero(pl->sfp_support);
phy_interface_zero(pl->sfp_interfaces); phy_interface_zero(pl->sfp_interfaces);
sfp_parse_support(pl->sfp_bus, id, support, pl->sfp_interfaces); sfp_parse_support(pl->sfp_bus, id, pl->sfp_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, pl->sfp_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 */
pl->sfp_may_have_phy = sfp_may_have_phy(pl->sfp_bus, id); pl->sfp_may_have_phy = sfp_may_have_phy(pl->sfp_bus, id);
if (pl->sfp_may_have_phy) if (pl->sfp_may_have_phy)
return 0; return 0;
return phylink_sfp_config(pl, MLO_AN_INBAND, support, support); return phylink_sfp_config_optical(pl);
} }
static int phylink_sfp_module_start(void *upstream) static int phylink_sfp_module_start(void *upstream)
...@@ -2927,8 +3032,7 @@ static int phylink_sfp_module_start(void *upstream) ...@@ -2927,8 +3032,7 @@ static int phylink_sfp_module_start(void *upstream)
if (!pl->sfp_may_have_phy) if (!pl->sfp_may_have_phy)
return 0; return 0;
return phylink_sfp_config(pl, MLO_AN_INBAND, return phylink_sfp_config_optical(pl);
pl->sfp_support, pl->sfp_support);
} }
static void phylink_sfp_module_stop(void *upstream) static void phylink_sfp_module_stop(void *upstream)
......
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