diff options
| author | Jan Hoffmann | 2026-02-04 15:22:06 +0000 |
|---|---|---|
| committer | Robert Marko | 2026-02-06 09:23:43 +0000 |
| commit | e3be4654a1f405985964d8b5e5add3c149f55fba (patch) | |
| tree | fb49a479069c4dd739391436a6679a93bc5e2647 | |
| parent | 6dae444ee94d05972c842e765daf01de0fe50ce5 (diff) | |
| download | openwrt-e3be4654a1f405985964d8b5e5add3c149f55fba.tar.gz | |
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@3e8.eu>
Link: https://github.com/openwrt/openwrt/pull/21869
Signed-off-by: Robert Marko <robimarko@gmail.com>
| -rw-r--r-- | target/linux/realtek/files-6.12/drivers/net/pcs/pcs-rtl-otto.c | 49 |
1 files 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); |