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 <jelonek.jonas@gmail.com>
This commit is contained in:
Jonas Jelonek 2026-07-27 20:29:37 +00:00
parent 7ed1c1d472
commit 4e7a9a7969
No known key found for this signature in database

View file

@ -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)