[openwrt/openwrt] realtek: pcs: rtl93xx: configure in-band AN advertisement

LEDE Commits lede-commits at lists.infradead.org
Fri Feb 6 01:23:47 PST 2026


robimarko pushed a commit to openwrt/openwrt.git, branch main:
https://git.openwrt.org/e3be4654a1f405985964d8b5e5add3c149f55fba

commit e3be4654a1f405985964d8b5e5add3c149f55fba
Author: Jan Hoffmann <jan at 3e8.eu>
AuthorDate: Wed Feb 4 16:22:06 2026 +0100

    realtek: pcs: rtl93xx: configure in-band AN advertisement
    
    Enabling and disabling in-band auto-negotation is already supported on
    RTL93xx. However, so far the advertisement is left unchanged at the
    default of 0x1a0 (full duplex + pause + asymmetric pause).
    
    Instead, set the advertisement to reflect the current configuration for
    1000Base-X and 2500Base-X. Nothing needs to be done for SGMII, as the
    advertisement register is ignored in that case.
    
    Testing shows that negotiation of flow control works for 1000Base-X and
    2500Base-X (tested with RTL930x on both ends of the link).
    
    Signed-off-by: Jan Hoffmann <jan at 3e8.eu>
    Link: https://github.com/openwrt/openwrt/pull/21869
    Signed-off-by: Robert Marko <robimarko at gmail.com>
---
 .../files-6.12/drivers/net/pcs/pcs-rtl-otto.c      | 49 ++++++++++++++++++----
 1 file changed, 42 insertions(+), 7 deletions(-)

diff --git a/target/linux/realtek/files-6.12/drivers/net/pcs/pcs-rtl-otto.c b/target/linux/realtek/files-6.12/drivers/net/pcs/pcs-rtl-otto.c
index 529c7ff4ad..a6bc37831e 100644
--- a/target/linux/realtek/files-6.12/drivers/net/pcs/pcs-rtl-otto.c
+++ b/target/linux/realtek/files-6.12/drivers/net/pcs/pcs-rtl-otto.c
@@ -164,7 +164,8 @@ struct rtpcs_serdes_ops {
 	int (*xsg_write)(struct rtpcs_serdes *sds, int page, int regnum, int bithigh, int bitlow,
 			 u16 value);
 
-	int (*set_autoneg)(struct rtpcs_serdes *sds, unsigned int neg_mode);
+	int (*set_autoneg)(struct rtpcs_serdes *sds, unsigned int neg_mode,
+			   const unsigned long *advertising);
 	void (*restart_autoneg)(struct rtpcs_serdes *sds);
 };
 
@@ -333,6 +334,15 @@ static int rtpcs_sds_modify(struct rtpcs_serdes *sds, int page, int regnum,
 				  mmd_regnum, mask, set);
 }
 
+static int rtpcs_sds_modify_changed(struct rtpcs_serdes *sds, int page, int regnum,
+				    u16 mask, u16 set)
+{
+	int mmd_regnum = rtpcs_sds_to_mmd(page, regnum);
+
+	return mdiobus_c45_modify_changed(sds->ctrl->bus, sds->id, MDIO_MMD_VEND1,
+					  mmd_regnum, mask, set);
+}
+
 static struct rtpcs_serdes *rtpcs_sds_get_even(struct rtpcs_serdes *sds)
 {
 	u32 even_sds = sds->id & ~1;
@@ -947,9 +957,12 @@ static int rtpcs_839x_setup_serdes(struct rtpcs_serdes *sds,
 
 /* RTL93XX */
 
-static int rtpcs_93xx_sds_set_autoneg(struct rtpcs_serdes *sds, unsigned int neg_mode)
+static int rtpcs_93xx_sds_set_autoneg(struct rtpcs_serdes *sds, unsigned int neg_mode,
+				      const unsigned long *advertising)
 {
-	u16 bmcr, en_val;
+	u16 bmcr, adv, en_val;
+	bool changed = 0;
+	int ret;
 
 	switch (sds->hw_mode) {
 	case RTPCS_SDS_MODE_XSGMII: /* XSG N-way state */
@@ -957,9 +970,30 @@ static int rtpcs_93xx_sds_set_autoneg(struct rtpcs_serdes *sds, unsigned int neg
 
 		return rtpcs_sds_xsg_write_bits(sds, 0x0, 0x2, 9, 8, en_val);
 	default:
+		if ((sds->hw_mode == RTPCS_SDS_MODE_1000BASEX) ||
+		    (sds->hw_mode == RTPCS_SDS_MODE_2500BASEX)) {
+			adv = ADVERTISE_1000XFULL;
+			if (linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT,
+					      advertising))
+				adv |= ADVERTISE_1000XPAUSE;
+			if (linkmode_test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT,
+					      advertising))
+				adv |= ADVERTISE_1000XPSE_ASYM;
+
+			ret = rtpcs_sds_modify_changed(sds, 0x2, MII_ADVERTISE,
+						       0xffff, adv);
+			if (ret < 0)
+				return ret;
+			changed = ret;
+		}
+
 		bmcr = neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED ? BMCR_ANENABLE : 0;
 
-		return rtpcs_sds_modify(sds, 0x2, MII_BMCR, BMCR_ANENABLE, bmcr);
+		ret = rtpcs_sds_modify(sds, 0x2, MII_BMCR, BMCR_ANENABLE, bmcr);
+		if (ret < 0)
+			return ret;
+
+		return changed;
 	}
 }
 
@@ -3697,7 +3731,8 @@ static int rtpcs_931x_setup_serdes(struct rtpcs_serdes *sds,
 
 /* Common functions */
 
-static int rtpcs_sds_set_autoneg(struct rtpcs_serdes *sds, unsigned int neg_mode)
+static int rtpcs_sds_set_autoneg(struct rtpcs_serdes *sds, unsigned int neg_mode,
+				 const unsigned long *advertising)
 {
 	if (!sds->ops->set_autoneg) {
 		dev_warn(sds->ctrl->dev, "set_autoneg not implemented for SDS %u, skipping\n",
@@ -3705,7 +3740,7 @@ static int rtpcs_sds_set_autoneg(struct rtpcs_serdes *sds, unsigned int neg_mode
 		return 0;
 	}
 
-	return sds->ops->set_autoneg(sds, neg_mode);
+	return sds->ops->set_autoneg(sds, neg_mode, advertising);
 }
 
 static void rtpcs_sds_restart_autoneg(struct rtpcs_serdes *sds)
@@ -3822,7 +3857,7 @@ static int rtpcs_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
 			 sds->id, phy_modes(interface));
 	}
 
-	ret = rtpcs_sds_set_autoneg(sds, neg_mode);
+	ret = rtpcs_sds_set_autoneg(sds, neg_mode, advertising);
 
 out:
 	mutex_unlock(&ctrl->lock);




More information about the lede-commits mailing list