diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-18 18:50:03 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-18 18:50:03 +0000 |
commit | 01a69402cf9d38ff180345d55c2ee51c7e89fbc7 (patch) | |
tree | b406c5242a088c4f59c6e4b719b783f43aca6ae9 /drivers/net/phy/phylink.c | |
parent | Adding upstream version 6.7.12. (diff) | |
download | linux-01a69402cf9d38ff180345d55c2ee51c7e89fbc7.tar.xz linux-01a69402cf9d38ff180345d55c2ee51c7e89fbc7.zip |
Adding upstream version 6.8.9.upstream/6.8.9
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'drivers/net/phy/phylink.c')
-rw-r--r-- | drivers/net/phy/phylink.c | 324 |
1 files changed, 199 insertions, 125 deletions
diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index 25c19496a3..ed0b4ccaa6 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -121,6 +121,19 @@ do { \ }) #endif +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 DECLARE_PHY_INTERFACE_MASK(phylink_sfp_interfaces); + /** * phylink_set_port_modes() - set the port type modes in the ethtool mask * @mask: ethtool link mode mask @@ -689,28 +702,48 @@ static int phylink_validate_mac_and_pcs(struct phylink *pl, return phylink_is_empty_linkmode(supported) ? -EINVAL : 0; } -static int phylink_validate_mask(struct phylink *pl, unsigned long *supported, +static void phylink_validate_one(struct phylink *pl, struct phy_device *phy, + const unsigned long *supported, + const struct phylink_link_state *state, + phy_interface_t interface, + unsigned long *accum_supported, + unsigned long *accum_advertising) +{ + __ETHTOOL_DECLARE_LINK_MODE_MASK(tmp_supported); + struct phylink_link_state tmp_state; + + linkmode_copy(tmp_supported, supported); + + tmp_state = *state; + tmp_state.interface = interface; + + if (phy) + tmp_state.rate_matching = phy_get_rate_matching(phy, interface); + + if (!phylink_validate_mac_and_pcs(pl, tmp_supported, &tmp_state)) { + phylink_dbg(pl, " interface %u (%s) rate match %s supports %*pbl\n", + interface, phy_modes(interface), + phy_rate_matching_to_str(tmp_state.rate_matching), + __ETHTOOL_LINK_MODE_MASK_NBITS, tmp_supported); + + linkmode_or(accum_supported, accum_supported, tmp_supported); + linkmode_or(accum_advertising, accum_advertising, + tmp_state.advertising); + } +} + +static int phylink_validate_mask(struct phylink *pl, struct phy_device *phy, + unsigned long *supported, struct phylink_link_state *state, const unsigned long *interfaces) { __ETHTOOL_DECLARE_LINK_MODE_MASK(all_adv) = { 0, }; __ETHTOOL_DECLARE_LINK_MODE_MASK(all_s) = { 0, }; - __ETHTOOL_DECLARE_LINK_MODE_MASK(s); - struct phylink_link_state t; - int intf; - - for (intf = 0; intf < PHY_INTERFACE_MODE_MAX; intf++) { - if (test_bit(intf, interfaces)) { - linkmode_copy(s, supported); - - t = *state; - t.interface = intf; - if (!phylink_validate_mac_and_pcs(pl, s, &t)) { - linkmode_or(all_s, all_s, s); - linkmode_or(all_adv, all_adv, t.advertising); - } - } - } + int interface; + + for_each_set_bit(interface, interfaces, PHY_INTERFACE_MODE_MAX) + phylink_validate_one(pl, phy, supported, state, interface, + all_s, all_adv); linkmode_copy(supported, all_s); linkmode_copy(state->advertising, all_adv); @@ -724,7 +757,8 @@ static int phylink_validate(struct phylink *pl, unsigned long *supported, const unsigned long *interfaces = pl->config->supported_interfaces; if (state->interface == PHY_INTERFACE_MODE_NA) - return phylink_validate_mask(pl, supported, state, interfaces); + return phylink_validate_mask(pl, NULL, supported, state, + interfaces); if (!test_bit(state->interface, interfaces)) return -EINVAL; @@ -805,7 +839,7 @@ static int phylink_parse_fixedlink(struct phylink *pl, phylink_warn(pl, "fixed link specifies half duplex for %dMbps link?\n", pl->link_config.speed); - bitmap_fill(pl->supported, __ETHTOOL_LINK_MODE_MASK_NBITS); + linkmode_fill(pl->supported); linkmode_copy(pl->link_config.advertising, pl->supported); phylink_validate(pl, pl->supported, &pl->link_config); @@ -849,6 +883,7 @@ static int phylink_parse_mode(struct phylink *pl, { struct fwnode_handle *dn; const char *managed; + unsigned long caps; dn = fwnode_get_named_child_node(fwnode, "fixed-link"); if (dn || fwnode_property_present(fwnode, "fixed-link")) @@ -881,80 +916,18 @@ static int phylink_parse_mode(struct phylink *pl, case PHY_INTERFACE_MODE_RGMII_RXID: case PHY_INTERFACE_MODE_RGMII_TXID: case PHY_INTERFACE_MODE_RTBI: - phylink_set(pl->supported, 10baseT_Half); - phylink_set(pl->supported, 10baseT_Full); - phylink_set(pl->supported, 100baseT_Half); - phylink_set(pl->supported, 100baseT_Full); - phylink_set(pl->supported, 1000baseT_Half); - phylink_set(pl->supported, 1000baseT_Full); - break; - case PHY_INTERFACE_MODE_1000BASEX: - phylink_set(pl->supported, 1000baseX_Full); - break; - case PHY_INTERFACE_MODE_2500BASEX: - phylink_set(pl->supported, 2500baseX_Full); - break; - case PHY_INTERFACE_MODE_5GBASER: - phylink_set(pl->supported, 5000baseT_Full); - break; - case PHY_INTERFACE_MODE_25GBASER: - phylink_set(pl->supported, 25000baseCR_Full); - phylink_set(pl->supported, 25000baseKR_Full); - phylink_set(pl->supported, 25000baseSR_Full); - fallthrough; case PHY_INTERFACE_MODE_USXGMII: case PHY_INTERFACE_MODE_10GKR: case PHY_INTERFACE_MODE_10GBASER: - phylink_set(pl->supported, 10baseT_Half); - phylink_set(pl->supported, 10baseT_Full); - phylink_set(pl->supported, 100baseT_Half); - phylink_set(pl->supported, 100baseT_Full); - phylink_set(pl->supported, 1000baseT_Half); - phylink_set(pl->supported, 1000baseT_Full); - phylink_set(pl->supported, 1000baseX_Full); - phylink_set(pl->supported, 1000baseKX_Full); - phylink_set(pl->supported, 2500baseT_Full); - phylink_set(pl->supported, 2500baseX_Full); - phylink_set(pl->supported, 5000baseT_Full); - phylink_set(pl->supported, 10000baseT_Full); - phylink_set(pl->supported, 10000baseKR_Full); - phylink_set(pl->supported, 10000baseKX4_Full); - phylink_set(pl->supported, 10000baseCR_Full); - phylink_set(pl->supported, 10000baseSR_Full); - phylink_set(pl->supported, 10000baseLR_Full); - phylink_set(pl->supported, 10000baseLRM_Full); - phylink_set(pl->supported, 10000baseER_Full); - break; - case PHY_INTERFACE_MODE_XLGMII: - phylink_set(pl->supported, 25000baseCR_Full); - phylink_set(pl->supported, 25000baseKR_Full); - phylink_set(pl->supported, 25000baseSR_Full); - phylink_set(pl->supported, 40000baseKR4_Full); - phylink_set(pl->supported, 40000baseCR4_Full); - phylink_set(pl->supported, 40000baseSR4_Full); - phylink_set(pl->supported, 40000baseLR4_Full); - phylink_set(pl->supported, 50000baseCR2_Full); - phylink_set(pl->supported, 50000baseKR2_Full); - phylink_set(pl->supported, 50000baseSR2_Full); - phylink_set(pl->supported, 50000baseKR_Full); - phylink_set(pl->supported, 50000baseSR_Full); - phylink_set(pl->supported, 50000baseCR_Full); - phylink_set(pl->supported, 50000baseLR_ER_FR_Full); - phylink_set(pl->supported, 50000baseDR_Full); - phylink_set(pl->supported, 100000baseKR4_Full); - phylink_set(pl->supported, 100000baseSR4_Full); - phylink_set(pl->supported, 100000baseCR4_Full); - phylink_set(pl->supported, 100000baseLR4_ER4_Full); - phylink_set(pl->supported, 100000baseKR2_Full); - phylink_set(pl->supported, 100000baseSR2_Full); - phylink_set(pl->supported, 100000baseCR2_Full); - phylink_set(pl->supported, 100000baseLR2_ER2_FR2_Full); - phylink_set(pl->supported, 100000baseDR2_Full); + caps = ~(MAC_SYM_PAUSE | MAC_ASYM_PAUSE); + caps = phylink_get_capabilities(pl->link_config.interface, caps, + RATE_MATCH_NONE); + phylink_caps_to_linkmodes(pl->supported, caps); break; default: @@ -1101,6 +1074,72 @@ static void phylink_pcs_an_restart(struct phylink *pl) pl->pcs->ops->pcs_an_restart(pl->pcs); } +/** + * phylink_pcs_neg_mode() - helper to determine PCS inband mode + * @mode: one of %MLO_AN_FIXED, %MLO_AN_PHY, %MLO_AN_INBAND. + * @interface: interface mode to be used + * @advertising: adertisement ethtool link mode mask + * + * Determines the negotiation mode to be used by the PCS, and returns + * one of: + * + * - %PHYLINK_PCS_NEG_NONE: interface mode does not support inband + * - %PHYLINK_PCS_NEG_OUTBAND: an out of band mode (e.g. reading the PHY) + * will be used. + * - %PHYLINK_PCS_NEG_INBAND_DISABLED: inband mode selected but autoneg + * disabled + * - %PHYLINK_PCS_NEG_INBAND_ENABLED: inband mode selected and autoneg enabled + * + * Note: this is for cases where the PCS itself is involved in negotiation + * (e.g. Clause 37, SGMII and similar) not Clause 73. + */ +static unsigned int phylink_pcs_neg_mode(unsigned int mode, + phy_interface_t interface, + const unsigned long *advertising) +{ + unsigned int neg_mode; + + switch (interface) { + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_QSGMII: + case PHY_INTERFACE_MODE_QUSGMII: + case PHY_INTERFACE_MODE_USXGMII: + /* These protocols are designed for use with a PHY which + * communicates its negotiation result back to the MAC via + * inband communication. Note: there exist PHYs that run + * with SGMII but do not send the inband data. + */ + if (!phylink_autoneg_inband(mode)) + neg_mode = PHYLINK_PCS_NEG_OUTBAND; + else + neg_mode = PHYLINK_PCS_NEG_INBAND_ENABLED; + break; + + case PHY_INTERFACE_MODE_1000BASEX: + case PHY_INTERFACE_MODE_2500BASEX: + /* 1000base-X is designed for use media-side for Fibre + * connections, and thus the Autoneg bit needs to be + * taken into account. We also do this for 2500base-X + * as well, but drivers may not support this, so may + * need to override this. + */ + if (!phylink_autoneg_inband(mode)) + neg_mode = PHYLINK_PCS_NEG_OUTBAND; + else if (linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, + advertising)) + neg_mode = PHYLINK_PCS_NEG_INBAND_ENABLED; + else + neg_mode = PHYLINK_PCS_NEG_INBAND_DISABLED; + break; + + default: + neg_mode = PHYLINK_PCS_NEG_NONE; + break; + } + + return neg_mode; +} + static void phylink_major_config(struct phylink *pl, bool restart, const struct phylink_link_state *state) { @@ -1640,7 +1679,7 @@ struct phylink *phylink_create(struct phylink_config *config, __set_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state); timer_setup(&pl->link_poll, phylink_fixed_poll, 0); - bitmap_fill(pl->supported, __ETHTOOL_LINK_MODE_MASK_NBITS); + linkmode_fill(pl->supported); linkmode_copy(pl->link_config.advertising, pl->supported); phylink_validate(pl, pl->supported, &pl->link_config); @@ -1739,31 +1778,55 @@ static void phylink_phy_change(struct phy_device *phydev, bool up) phylink_pause_to_str(pl->phy_state.pause)); } -static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy, - phy_interface_t interface) +static int phylink_validate_phy(struct phylink *pl, struct phy_device *phy, + unsigned long *supported, + struct phylink_link_state *state) { - struct phylink_link_state config; - __ETHTOOL_DECLARE_LINK_MODE_MASK(supported); - char *irq_str; - int ret; + DECLARE_PHY_INTERFACE_MASK(interfaces); - /* - * This is the new way of dealing with flow control for PHYs, - * as described by Timur Tabi in commit 529ed1275263 ("net: phy: - * phy drivers should not set SUPPORTED_[Asym_]Pause") except - * using our validate call to the MAC, we rely upon the MAC - * clearing the bits from both supported and advertising fields. + /* If the PHY provides a bitmap of the interfaces it will be using + * depending on the negotiated media speeds, use this to validate + * which ethtool link modes can be used. */ - phy_support_asym_pause(phy); + if (!phy_interface_empty(phy->possible_interfaces)) { + /* We only care about the union of the PHY's interfaces and + * those which the host supports. + */ + phy_interface_and(interfaces, phy->possible_interfaces, + pl->config->supported_interfaces); - memset(&config, 0, sizeof(config)); - linkmode_copy(supported, phy->supported); - linkmode_copy(config.advertising, phy->advertising); + if (phy_interface_empty(interfaces)) { + phylink_err(pl, "PHY has no common interfaces\n"); + return -EINVAL; + } + + if (phy_on_sfp(phy)) { + /* If the PHY is on a SFP, limit the interfaces to + * those that can be used with a SFP module. + */ + phy_interface_and(interfaces, interfaces, + phylink_sfp_interfaces); + + if (phy_interface_empty(interfaces)) { + phylink_err(pl, "SFP PHY's possible interfaces becomes empty\n"); + return -EINVAL; + } + } + + phylink_dbg(pl, "PHY %s uses interfaces %*pbl, validating %*pbl\n", + phydev_name(phy), + (int)PHY_INTERFACE_MODE_MAX, + phy->possible_interfaces, + (int)PHY_INTERFACE_MODE_MAX, interfaces); + + return phylink_validate_mask(pl, phy, supported, state, + interfaces); + } /* Check whether we would use rate matching for the proposed interface * mode. */ - config.rate_matching = phy_get_rate_matching(phy, interface); + state->rate_matching = phy_get_rate_matching(phy, state->interface); /* Clause 45 PHYs may switch their Serdes lane between, e.g. 10GBASE-R, * 5GBASE-R, 2500BASE-X and SGMII if they are not using rate matching. @@ -1776,15 +1839,38 @@ static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy, * against all interface modes, which may lead to more ethtool link * modes being advertised than are actually supported. */ - if (phy->is_c45 && config.rate_matching == RATE_MATCH_NONE && - interface != PHY_INTERFACE_MODE_RXAUI && - interface != PHY_INTERFACE_MODE_XAUI && - interface != PHY_INTERFACE_MODE_USXGMII) - config.interface = PHY_INTERFACE_MODE_NA; - else - config.interface = interface; + if (phy->is_c45 && state->rate_matching == RATE_MATCH_NONE && + state->interface != PHY_INTERFACE_MODE_RXAUI && + state->interface != PHY_INTERFACE_MODE_XAUI && + state->interface != PHY_INTERFACE_MODE_USXGMII) + state->interface = PHY_INTERFACE_MODE_NA; - ret = phylink_validate(pl, supported, &config); + return phylink_validate(pl, supported, state); +} + +static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy, + phy_interface_t interface) +{ + struct phylink_link_state config; + __ETHTOOL_DECLARE_LINK_MODE_MASK(supported); + char *irq_str; + int ret; + + /* + * This is the new way of dealing with flow control for PHYs, + * as described by Timur Tabi in commit 529ed1275263 ("net: phy: + * phy drivers should not set SUPPORTED_[Asym_]Pause") except + * using our validate call to the MAC, we rely upon the MAC + * clearing the bits from both supported and advertising fields. + */ + phy_support_asym_pause(phy); + + memset(&config, 0, sizeof(config)); + linkmode_copy(supported, phy->supported); + linkmode_copy(config.advertising, phy->advertising); + config.interface = interface; + + ret = phylink_validate_phy(pl, phy, supported, &config); if (ret) { phylink_warn(pl, "validation of %s with support %*pb and advertisement %*pb failed: %pe\n", phy_modes(config.interface), @@ -3005,19 +3091,6 @@ static void phylink_sfp_detach(void *upstream, struct sfp_bus *bus) 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 DECLARE_PHY_INTERFACE_MASK(phylink_sfp_interfaces); - static phy_interface_t phylink_choose_sfp_interface(struct phylink *pl, const unsigned long *intf) { @@ -3160,7 +3233,8 @@ static int phylink_sfp_config_optical(struct phylink *pl) /* 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); + ret = phylink_validate_mask(pl, NULL, 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); |