kernel: phylink: disable autoneg for interfaces that have no inband

This patch fixes a bug in a patch we backported.

This patch was cherry picked from upstream Linux because it references a
patch we backported in the fixes tag.

The first two patches are providing function needed by the last patch.

Fixes: 813ecda1f3 ("generic: backport phylink patches for PCS/PHY caps OPs")
Link: https://github.com/openwrt/openwrt/pull/21366
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
This commit is contained in:
Hauke Mehrtens 2026-01-01 19:31:48 +01:00
parent 2c7241b209
commit 69d76e3a14
13 changed files with 225 additions and 13 deletions

View file

@ -35,7 +35,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
static void phylink_pcs_poll_stop(struct phylink *pl)
{
if (pl->cfg_link_an_mode == MLO_AN_INBAND)
@@ -1642,6 +1648,8 @@ static void phylink_link_down(struct phy
@@ -1651,6 +1657,8 @@ static void phylink_link_down(struct phy
if (ndev)
netif_carrier_off(ndev);

View file

@ -0,0 +1,27 @@
From 4beb44a2d62dddfe450f310aa1a950901731cb3a Mon Sep 17 00:00:00 2001
From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
Date: Sun, 31 Aug 2025 18:34:33 +0100
Subject: net: phy: add phy_interface_weight()
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Link: https://patch.msgid.link/E1uslwn-00000001SOx-0a7H@rmk-PC.armlinux.org.uk
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
include/linux/phy.h | 5 +++++
1 file changed, 5 insertions(+)
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -187,6 +187,11 @@ static inline bool phy_interface_empty(c
return bitmap_empty(intf, PHY_INTERFACE_MODE_MAX);
}
+static inline unsigned int phy_interface_weight(const unsigned long *intf)
+{
+ return bitmap_weight(intf, PHY_INTERFACE_MODE_MAX);
+}
+
static inline void phy_interface_and(unsigned long *dst, const unsigned long *a,
const unsigned long *b)
{

View file

@ -0,0 +1,117 @@
From 1bd905dfea9897eafef532000702e63a66849f54 Mon Sep 17 00:00:00 2001
From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
Date: Sun, 31 Aug 2025 18:34:38 +0100
Subject: net: phylink: provide phylink_get_inband_type()
Provide a function to get the type of the inband signalling used for
a PHY interface type. This will be used in the subsequent patch to
address problems with 10G optical modules.
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Link: https://patch.msgid.link/E1uslws-00000001SP5-1R2R@rmk-PC.armlinux.org.uk
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
drivers/net/phy/phylink.c | 79 ++++++++++++++++++++++-----------------
1 file changed, 44 insertions(+), 35 deletions(-)
--- a/drivers/net/phy/phylink.c
+++ b/drivers/net/phy/phylink.c
@@ -1147,6 +1147,42 @@ static void phylink_pcs_an_restart(struc
pl->pcs->ops->pcs_an_restart(pl->pcs);
}
+enum inband_type {
+ INBAND_NONE,
+ INBAND_CISCO_SGMII,
+ INBAND_BASEX,
+};
+
+static enum inband_type phylink_get_inband_type(phy_interface_t interface)
+{
+ switch (interface) {
+ case PHY_INTERFACE_MODE_SGMII:
+ case PHY_INTERFACE_MODE_QSGMII:
+ case PHY_INTERFACE_MODE_QUSGMII:
+ case PHY_INTERFACE_MODE_USXGMII:
+ case PHY_INTERFACE_MODE_10G_QXGMII:
+ /* 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.
+ */
+ return INBAND_CISCO_SGMII;
+
+ 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.
+ */
+ return INBAND_BASEX;
+
+ default:
+ return INBAND_NONE;
+ }
+}
+
/**
* phylink_pcs_neg_mode() - helper to determine PCS inband mode
* @pl: a pointer to a &struct phylink returned from phylink_create()
@@ -1174,46 +1210,19 @@ static void phylink_pcs_neg_mode(struct
unsigned int pcs_ib_caps = 0;
unsigned int phy_ib_caps = 0;
unsigned int neg_mode, mode;
- enum {
- INBAND_CISCO_SGMII,
- INBAND_BASEX,
- } type;
-
- mode = pl->req_link_an_mode;
+ enum inband_type type;
- pl->phy_ib_mode = 0;
-
- switch (interface) {
- case PHY_INTERFACE_MODE_SGMII:
- case PHY_INTERFACE_MODE_QSGMII:
- case PHY_INTERFACE_MODE_QUSGMII:
- case PHY_INTERFACE_MODE_USXGMII:
- case PHY_INTERFACE_MODE_10G_QXGMII:
- /* 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.
- */
- type = INBAND_CISCO_SGMII;
- 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.
- */
- type = INBAND_BASEX;
- break;
-
- default:
+ type = phylink_get_inband_type(interface);
+ if (type == INBAND_NONE) {
pl->pcs_neg_mode = PHYLINK_PCS_NEG_NONE;
- pl->act_link_an_mode = mode;
+ pl->act_link_an_mode = pl->req_link_an_mode;
return;
}
+ mode = pl->req_link_an_mode;
+
+ pl->phy_ib_mode = 0;
+
if (pcs)
pcs_ib_caps = phylink_pcs_inband_caps(pcs, interface);

View file

@ -0,0 +1,68 @@
From a21202743f9ce4063e86b99cccaef48ef9813379 Mon Sep 17 00:00:00 2001
From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
Date: Sun, 31 Aug 2025 18:34:43 +0100
Subject: net: phylink: disable autoneg for interfaces that have no inband
Mathew reports that as a result of commit 6561f0e547be ("net: pcs:
pcs-lynx: implement pcs_inband_caps() method"), 10G SFP modules no
longer work with the Lynx PCS.
This problem is not specific to the Lynx PCS, but is caused by commit
df874f9e52c3 ("net: phylink: add pcs_inband_caps() method") which added
validation of the autoneg state to the optical SFP configuration path.
Fix this by handling interface modes that fundamentally have no
inband negotiation more correctly - if we only have a single interface
mode, clear the Autoneg support bit and the advertising mask. If the
module can operate with several different interface modes, autoneg may
be supported for other modes, so leave the support mask alone and just
clear the Autoneg bit in the advertising mask.
This restores 10G optical module functionality with PCS that supply
their inband support, and makes ethtool output look sane.
Reported-by: Mathew McBride <matt@traverse.com.au>
Closes: https://lore.kernel.org/r/025c0ebe-5537-4fa3-b05a-8b835e5ad317@app.fastmail.com
Fixes: df874f9e52c3 ("net: phylink: add pcs_inband_caps() method")
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
Tested-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Link: https://patch.msgid.link/E1uslwx-00000001SPB-2kiM@rmk-PC.armlinux.org.uk
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
drivers/net/phy/phylink.c | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
--- a/drivers/net/phy/phylink.c
+++ b/drivers/net/phy/phylink.c
@@ -3500,6 +3500,7 @@ static int phylink_sfp_config_optical(st
__ETHTOOL_DECLARE_LINK_MODE_MASK(support);
DECLARE_PHY_INTERFACE_MASK(interfaces);
struct phylink_link_state config;
+ enum inband_type inband_type;
phy_interface_t interface;
int ret;
@@ -3546,6 +3547,23 @@ static int phylink_sfp_config_optical(st
phylink_dbg(pl, "optical SFP: chosen %s interface\n",
phy_modes(interface));
+ inband_type = phylink_get_inband_type(interface);
+ if (inband_type == INBAND_NONE) {
+ /* If this is the sole interface, and there is no inband
+ * support, clear the advertising mask and Autoneg bit in
+ * the support mask. Otherwise, just clear the Autoneg bit
+ * in the advertising mask.
+ */
+ if (phy_interface_weight(pl->sfp_interfaces) == 1) {
+ linkmode_clear_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
+ pl->sfp_support);
+ linkmode_zero(config.advertising);
+ } else {
+ linkmode_clear_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
+ config.advertising);
+ }
+ }
+
if (!phylink_validate_pcs_inband_autoneg(pl, interface,
config.advertising)) {
phylink_err(pl, "autoneg setting not compatible with PCS");

View file

@ -39,7 +39,7 @@ Signed-off-by: Paolo Abeni <pabeni@redhat.com>
if (!phydev->drv->led_polarity_set)
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -892,8 +892,9 @@ struct phy_plca_status {
@@ -897,8 +897,9 @@ struct phy_plca_status {
/* Modes for PHY LED configuration */
enum phy_led_modes {

View file

@ -281,7 +281,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
}
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -1035,7 +1035,8 @@ struct phy_driver {
@@ -1040,7 +1040,8 @@ struct phy_driver {
* driver for the given phydev. If NULL, matching is based on
* phy_id and phy_id_mask.
*/

View file

@ -97,7 +97,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
static ssize_t
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -1940,6 +1940,9 @@ char *phy_attached_info_irq(struct phy_d
@@ -1945,6 +1945,9 @@ char *phy_attached_info_irq(struct phy_d
__malloc;
void phy_attached_info(struct phy_device *phydev);

View file

@ -19,7 +19,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -1256,9 +1256,13 @@ struct phy_driver {
@@ -1261,9 +1261,13 @@ struct phy_driver {
#define PHY_ANY_ID "MATCH ANY PHY"
#define PHY_ANY_UID 0xffffffff
@ -36,7 +36,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
/**
* phy_id_compare - compare @id1 with @id2 taking account of @mask
@@ -1275,6 +1279,19 @@ static inline bool phy_id_compare(u32 id
@@ -1280,6 +1284,19 @@ static inline bool phy_id_compare(u32 id
}
/**

View file

@ -23,7 +23,7 @@ Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
sysfs_remove_link(&dev->dev.kobj, "phydev");
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -1027,6 +1027,12 @@ struct phy_driver {
@@ -1032,6 +1032,12 @@ struct phy_driver {
/** @handle_interrupt: Override default interrupt handling */
irqreturn_t (*handle_interrupt)(struct phy_device *phydev);

View file

@ -20,7 +20,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
--- a/drivers/net/phy/phylink.c
+++ b/drivers/net/phy/phylink.c
@@ -2264,7 +2264,7 @@ int phylink_fwnode_phy_connect(struct ph
@@ -2273,7 +2273,7 @@ int phylink_fwnode_phy_connect(struct ph
{
struct fwnode_handle *phy_fwnode;
struct phy_device *phy_dev;
@ -29,7 +29,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
/* Fixed links and 802.3z are handled without needing a PHY */
if (pl->cfg_link_an_mode == MLO_AN_FIXED ||
@@ -2294,6 +2294,25 @@ int phylink_fwnode_phy_connect(struct ph
@@ -2303,6 +2303,25 @@ int phylink_fwnode_phy_connect(struct ph
if (pl->config->mac_requires_rxc)
flags |= PHY_F_RXC_ALWAYS_ON;

View file

@ -62,7 +62,7 @@ Signed-off-by: Bhaskar Upadhaya <Bhaskar.Upadhaya@nxp.com>
PHY_INTERFACE_MODE_MAX,
} phy_interface_t;
@@ -235,6 +236,8 @@ static inline const char *phy_modes(phy_
@@ -240,6 +241,8 @@ static inline const char *phy_modes(phy_
return "gmii";
case PHY_INTERFACE_MODE_SGMII:
return "sgmii";

View file

@ -21,7 +21,7 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
if (phydev->mii_ts && phydev->mii_ts->link_state)
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -675,6 +675,7 @@ struct phy_device {
@@ -680,6 +680,7 @@ struct phy_device {
unsigned downshifted_rate:1;
unsigned is_on_sfp_module:1;
unsigned mac_managed_pm:1;

View file

@ -35,7 +35,7 @@ Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
&phy->mdio.reset_assert_delay);
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -301,7 +301,7 @@ static inline const char *phy_modes(phy_
@@ -306,7 +306,7 @@ static inline const char *phy_modes(phy_
#define PHY_INIT_TIMEOUT 100000
#define PHY_FORCE_TIMEOUT 10
@ -44,7 +44,7 @@ Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
/* Used when trying to connect to a specific phy (mii bus id:phy device id) */
#define PHY_ID_FMT "%s:%02x"
@@ -421,10 +421,10 @@ struct mii_bus {
@@ -426,10 +426,10 @@ struct mii_bus {
struct mdio_device *mdio_map[PHY_MAX_ADDR];
/** @phy_mask: PHY addresses to be ignored when probing */