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: 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:
parent
fd580c9830
commit
f81fa96d8a
@ -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,
|
|||||||
__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,
|
|||||||
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)
|
|||||||
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)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user