From 2213918077f361c58d2380525a92ead2875e99dd Mon Sep 17 00:00:00 2001 From: "Russell King (Oracle)" Date: Mon, 4 Mar 2024 15:33:01 +0000 Subject: net: phylink: expand phylink_pcs_neg_mode() Signed-off-by: Russell King (Oracle) --- drivers/net/phy/phylink.c | 105 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 86 insertions(+), 19 deletions(-) diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index 3f92941e227e..33ada56918d7 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -1185,12 +1185,10 @@ static unsigned int phylink_pcs_neg_mode(struct phylink *pl, unsigned int phy_link_mode = 0; unsigned int pcs_link_mode; unsigned int neg_mode; - - if (phylink_autoneg_inband(mode)) { - pcs_link_mode = phylink_pcs_query_inband(pcs, interface); - if (pl->phydev) - phy_link_mode = phy_query_inband(pl->phydev, interface); - } + enum { + INBAND_CISCO_SGMII, + INBAND_8023Z, + } type; switch (interface) { case PHY_INTERFACE_MODE_SGMII: @@ -1202,10 +1200,7 @@ static unsigned int phylink_pcs_neg_mode(struct phylink *pl, * 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; + type = INBAND_CISCO_SGMII; break; case PHY_INTERFACE_MODE_1000BASEX: @@ -1216,20 +1211,92 @@ static unsigned int phylink_pcs_neg_mode(struct phylink *pl, * 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; + type = INBAND_8023Z; break; default: - neg_mode = PHYLINK_PCS_NEG_NONE; - break; + return PHYLINK_PCS_NEG_NONE; } + pcs_link_mode = phylink_pcs_query_inband(pcs, interface); + if (pl->phydev) + phy_link_mode = phy_query_inband(pl->phydev, interface); + + if (!phylink_autoneg_inband(mode)) { + /* If either the PCS or PHY requires inband to be enabled, + * this is an invalid configuration. Provide a diagnostic + * message for this case, but don't try to force the issue. + */ + if ((pcs_link_mode | phy_link_mode) & LINK_INBAND_REQUIRED) { + const char *s1, *s2, *s3, *empty = ""; + + s1 = s2 = s3 = empty; + if (pcs_link_mode & LINK_INBAND_REQUIRED) + s1 = "PCS"; + + if (phy_link_mode & LINK_INBAND_REQUIRED) + s3 = "PHY"; + + if (s1 != empty && s3 != empty) + s2 = " and "; + + phylink_warn(pl, + "firmware wants %s mode, but %s%s%s requires inband\n", + phylink_an_mode_str(mode), + s1, s2, s3); + } + return PHYLINK_PCS_NEG_OUTBAND; + } + + /* For SGMII modes, which are designed to be used with PHYs, we + * try to use inband mode where-ever possible. + */ + if (type == INBAND_CISCO_SGMII) { + /* If the PCS or PHY can not provide inband, then use + * out of band. + */ + if (pcs_link_mode == LINK_INBAND_VALID || + phy_link_mode == LINK_INBAND_VALID) { + /* If either require inband, then we have a conflict. */ + return PHYLINK_PCS_NEG_OUTBAND; + } + + /* Otherwise, use inband as this is what was requested. + * This covers the case where the supported inband modes + * have not been provided, or the PCS and PHY report that + * they support inband. + */ + return PHYLINK_PCS_NEG_INBAND_ENABLED; + } + + /* For 802.3z, handle the PHY present vs absent cases separately */ + if (!pl->phydev) { + /* PHY absent: always use inband but inband may be disabled */ + /* If the PCS doesn't support inband, then inband must be + * disabled. + */ + if (pcs_link_mode == LINK_INBAND_VALID) + return PHYLINK_PCS_NEG_INBAND_DISABLED; + + /* If the PCS requires inband, then inband must always be + * enabled. + */ + if (pcs_link_mode & LINK_INBAND_REQUIRED) + return PHYLINK_PCS_NEG_INBAND_ENABLED; + + /* For the possible case, fall through to the "legacy" code. */ + } else { + /* PHY present, inband mode depends on the capabilities + * of both. + */ + } + + /* Legacy, so determine inband depending on the advertising bit */ + 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; + return neg_mode; } -- cgit