summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRussell King (Oracle) <rmk+kernel@armlinux.org.uk>2024-03-04 15:33:01 +0000
committerRussell King (Oracle) <rmk+kernel@armlinux.org.uk>2024-03-06 10:43:38 +0000
commit2213918077f361c58d2380525a92ead2875e99dd (patch)
tree0a0f0186edc8a6e45b53f1b6cc65ef926afe7b2c
parent83626a7d429580ca2cda05797b00aa01196994f3 (diff)
net: phylink: expand phylink_pcs_neg_mode()
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-rw-r--r--drivers/net/phy/phylink.c105
1 files 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;
}