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);
--- /dev/null
+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)
+ {
--- /dev/null
+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);
+
--- /dev/null
+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");
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 {
}
--- 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.
*/
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);
--- 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
/**
* 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
}
/**
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);
--- 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;
/* 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;
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";
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;
&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
/* 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 */