From: Jonas Jelonek Date: Mon, 27 Jul 2026 20:29:37 +0000 (+0000) Subject: realtek: pcs: rtl931x: gate symErr success on actual link status X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4e7a9a7969a329106e6bcb0eb0c0bec66dcd1c84;p=thirdparty%2Fopenwrt.git realtek: pcs: rtl931x: gate symErr success on actual link status symErr reads 0 both when the link is clean and when there's no signal at all to decode errors from - the counter only increments when the PCS is actively decoding something and finds a mismatch, so a dead link and a healthy one are indistinguishable from symErr alone. Confirmed on hardware: symErr read 0x0 while the port had no link. Add rtpcs_931x_sds_10gr_link_up(), reading the same status bit as the vendor SDK's _phy_rtl9310_linkSts_get() default case, and require it alongside a low symErr count before declaring a calibration check successful. A link that isn't actually up yet now keeps the retry loop going instead of being misread as a clean, working link. Also observed on hardware: the retry budget sometimes runs out while symErr is still nonzero, but the link comes up and works fine anyway - the count just hasn't fully settled within the budget. Since that's not an actual problem, keep the final message at dev_dbg when link_up is true; only warn when the link genuinely never came up. Link: https://github.com/openwrt/openwrt/pull/24542 Signed-off-by: Jonas Jelonek --- diff --git a/target/linux/realtek/files-6.18/drivers/net/pcs/pcs-rtl-otto.c b/target/linux/realtek/files-6.18/drivers/net/pcs/pcs-rtl-otto.c index 767d5ad5a16..58c86cb9bb5 100644 --- a/target/linux/realtek/files-6.18/drivers/net/pcs/pcs-rtl-otto.c +++ b/target/linux/realtek/files-6.18/drivers/net/pcs/pcs-rtl-otto.c @@ -3089,6 +3089,11 @@ static int rtpcs_931x_sds_fiber_get_symerr(struct rtpcs_serdes *sds, return symerr; } +static bool rtpcs_931x_sds_10gr_link_up(struct rtpcs_serdes *sds) +{ + return rtpcs_sds_read_bits(sds, PAGE_TGR_STD_1, 0x0, 12, 12) == 1; +} + static void rtpcs_931x_sds_clear_symerr(struct rtpcs_serdes *sds, enum rtpcs_sds_mode hw_mode) { @@ -3438,6 +3443,7 @@ static void rtpcs_931x_sds_rxcal_fiber_adapt(struct rtpcs_serdes *sds) unsigned int vth_p = 0, vth_n = 0, sum_p = 0, sum_n = 0; struct device *dev = sds->ctrl->dev; int i, samples = 0, symerr = -1; + bool link_up = false; dev_dbg(dev, "SerDes %u fiber RX calibration...\n", sds->id); /* per-port calibration offset in the SDK, kept 0 here */ @@ -3488,18 +3494,28 @@ static void rtpcs_931x_sds_rxcal_fiber_adapt(struct rtpcs_serdes *sds) rtpcs_931x_sds_clear_symerr(sds, RTPCS_SDS_MODE_10GBASER); msleep(300); symerr = rtpcs_931x_sds_fiber_get_symerr(sds, RTPCS_SDS_MODE_10GBASER); + link_up = rtpcs_931x_sds_10gr_link_up(sds); + dev_dbg(dev, "SerDes %u symErr check %d: linkUp=%d symErr=0x%x\n", sds->id, + i + 1, link_up, symerr); - dev_dbg(dev, "SerDes %u symErr check %d: 0x%x\n", sds->id, i + 1, symerr); - - if (symerr >= 0 && symerr <= 5) { + /* + * symErr also reads 0 with no signal at all, not just a clean + * link - don't trust it without link_up confirming there's + * actually something being decoded. + */ + if (link_up && symerr >= 0 && symerr <= 5) { dev_dbg(dev, "SerDes %u fiber RX calibration OK (check %d)\n", sds->id, i + 1); return; } } - dev_warn(dev, "SerDes %u fiber RX calibration failed after %d symErr checks\n", - sds->id, i); + if (link_up) + dev_dbg(dev, "SerDes %u fiber RX calibration: symErr still 0x%x after %d checks, link up anyway\n", + sds->id, symerr, i); + else + dev_warn(dev, "SerDes %u fiber RX calibration failed after %d symErr checks\n", + sds->id, i); } static int rtpcs_931x_sds_get_pll_select(struct rtpcs_serdes *sds, enum rtpcs_sds_pll_type *pll)