diff options
| author | Jakub Kicinski <kuba@kernel.org> | 2025-07-07 19:02:39 -0700 |
|---|---|---|
| committer | Jakub Kicinski <kuba@kernel.org> | 2025-07-07 19:02:39 -0700 |
| commit | 95f6fedd623bc4fdd93d1c7e61898cb1d46da7a9 (patch) | |
| tree | a282fc9642d0341d20f386d48cb509cf77bd32da /drivers | |
| parent | 8d5d927d963b7db8e8c7cda572541f80a69272b0 (diff) | |
| parent | 320164a6e172137bcefea669f0ff6ee3f4c8611e (diff) | |
Merge branch 'net-phylink-support-autoneg-configuration-for-sfps'
Russell King says:
====================
net: phylink: support !autoneg configuration for SFPs
This series comes from discussion during a patch series that was posted
at the beginning of April, but these patches were never posted (I was
too busy!)
We restrict ->sfp_interfaces to those that the host system supports,
and ensure that ->sfp_interfaces is cleared when a SFP is removed. We
then add phylink_sfp_select_interface_speed() which will select an
appropriate interface from ->sfp_interfaces for the speed, and use that
in our phylink_ethtool_ksettings_set() when a SFP bus is present on a
directly connected host (not with a PHY.)
====================
Link: https://patch.msgid.link/aGT_hoBELDysGbrp@shell.armlinux.org.uk
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Diffstat (limited to 'drivers')
| -rw-r--r-- | drivers/net/phy/phylink.c | 60 |
1 files changed, 54 insertions, 6 deletions
diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index 67218d278ce6..f5473510b762 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -2709,6 +2709,39 @@ static phy_interface_t phylink_sfp_select_interface(struct phylink *pl, return interface; } +static phy_interface_t phylink_sfp_select_interface_speed(struct phylink *pl, + u32 speed) +{ + phy_interface_t best_interface = PHY_INTERFACE_MODE_NA; + phy_interface_t interface; + u32 max_speed; + int i; + + for (i = 0; i < ARRAY_SIZE(phylink_sfp_interface_preference); i++) { + interface = phylink_sfp_interface_preference[i]; + if (!test_bit(interface, pl->sfp_interfaces)) + continue; + + max_speed = phylink_interface_max_speed(interface); + + /* The logic here is: if speed == max_speed, then we've found + * the best interface. Otherwise we find the interface that + * can just support the requested speed. + */ + if (max_speed >= speed) + best_interface = interface; + + if (max_speed <= speed) + break; + } + + if (best_interface == PHY_INTERFACE_MODE_NA) + phylink_err(pl, "selection of interface failed, speed %u\n", + speed); + + return best_interface; +} + static void phylink_merge_link_mode(unsigned long *dst, const unsigned long *b) { __ETHTOOL_DECLARE_LINK_MODE_MASK(mask); @@ -2911,8 +2944,14 @@ int phylink_ethtool_ksettings_set(struct phylink *pl, * link can be configured correctly. */ if (pl->sfp_bus) { - config.interface = phylink_sfp_select_interface(pl, + if (kset->base.autoneg == AUTONEG_ENABLE) + config.interface = + phylink_sfp_select_interface(pl, config.advertising); + else + config.interface = + phylink_sfp_select_interface_speed(pl, + config.speed); if (config.interface == PHY_INTERFACE_MODE_NA) return -EINVAL; @@ -3536,6 +3575,8 @@ static int phylink_sfp_config_phy(struct phylink *pl, struct phy_device *phy) struct phylink_link_state config; int ret; + /* We're not using pl->sfp_interfaces, so clear it. */ + phy_interface_zero(pl->sfp_interfaces); linkmode_copy(support, phy->supported); memset(&config, 0, sizeof(config)); @@ -3582,7 +3623,6 @@ static int phylink_sfp_config_phy(struct phylink *pl, struct phy_device *phy) 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; @@ -3596,9 +3636,9 @@ static int phylink_sfp_config_optical(struct phylink *pl) /* Find the union of the supported interfaces by the PCS/MAC and * the SFP module. */ - phy_interface_and(interfaces, pl->config->supported_interfaces, + phy_interface_and(pl->sfp_interfaces, pl->config->supported_interfaces, pl->sfp_interfaces); - if (phy_interface_empty(interfaces)) { + if (phy_interface_empty(pl->sfp_interfaces)) { phylink_err(pl, "unsupported SFP module: no common interface modes\n"); return -EINVAL; } @@ -3614,14 +3654,14 @@ static int phylink_sfp_config_optical(struct phylink *pl) * mask to only those link modes that can be supported. */ ret = phylink_validate_mask(pl, NULL, pl->sfp_support, &config, - interfaces); + pl->sfp_interfaces); if (ret) { phylink_err(pl, "unsupported SFP module: validation with support %*pb failed\n", __ETHTOOL_LINK_MODE_MASK_NBITS, support); return ret; } - interface = phylink_choose_sfp_interface(pl, interfaces); + interface = phylink_choose_sfp_interface(pl, pl->sfp_interfaces); if (interface == PHY_INTERFACE_MODE_NA) { phylink_err(pl, "failed to select SFP interface\n"); return -EINVAL; @@ -3674,6 +3714,13 @@ static int phylink_sfp_module_insert(void *upstream, return phylink_sfp_config_optical(pl); } +static void phylink_sfp_module_remove(void *upstream) +{ + struct phylink *pl = upstream; + + phy_interface_zero(pl->sfp_interfaces); +} + static int phylink_sfp_module_start(void *upstream) { struct phylink *pl = upstream; @@ -3758,6 +3805,7 @@ static const struct sfp_upstream_ops sfp_phylink_ops = { .attach = phylink_sfp_attach, .detach = phylink_sfp_detach, .module_insert = phylink_sfp_module_insert, + .module_remove = phylink_sfp_module_remove, .module_start = phylink_sfp_module_start, .module_stop = phylink_sfp_module_stop, .link_up = phylink_sfp_link_up, |
