realtek: pcs: rtl931x: wire up RX calibration for 10G fiber

Port the vendor SDK's _phy_rtl9310_fiber_adapt() as
rtpcs_931x_sds_rxcal_fiber_adapt(), dispatched from post_config for
RTPCS_SDS_ATTACH_FIBER when hw_mode is 10GBase-R (1G/100M fiber isn't
calibrated by the vendor SDK either).

Add the rxeq_tap_*/rxeq_vth_* setters (set_value, set_adapt) needed to
drive it: fiber calibration runs VTH/TAP0 auto-adapt for 200ms, then
samples the auto-adapted VTH and locks it in, forces TAP0 to a fixed
value (31, per the vendor SDK), and re-locks manual mode, before letting
DFE TAP1-4 auto-adapt freely and verifying via a symbol-error recheck
loop.

Sampling VTH needs the SerDes' debug-readback routing. Add
rtpcs_931x_sds_set_debug() and rxeq_vth_get() for it, mirroring
_phy_rtl9310_dbg_set()/_phy_rtl9310_dfe_get().

The final symbol-error recheck deliberately deviates from the vendor
SDK's shape (adapt once, recheck 3x at 150ms, require exactly 0):
symErr's field is only 8 bits wide and reads as a saturated 0xff right
after rx_reset(), which looks like "link hasn't relocked yet" rather
than a genuine error count - 10G optical relock can plausibly take
longer than 150ms. Instead, recheck more times with more patience per
check and no reset in between (so a settling link isn't interrupted),
and tolerate a small nonzero symbol-error count rather than requiring
exactly 0.

rtpcs_931x_sds_fiber_get_symerr() already existed but was unused;
wire it up alongside the existing rtpcs_931x_sds_clear_symerr().

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-28 22:19:49 +00:00
parent 58e896eb70
commit 3c63abd609
No known key found for this signature in database

View file

@ -3066,7 +3066,6 @@ static int rtpcs_931x_sds_op_xsg_write(struct rtpcs_serdes *sds, enum rtpcs_page
value);
}
__maybe_unused
static int rtpcs_931x_sds_fiber_get_symerr(struct rtpcs_serdes *sds,
enum rtpcs_sds_mode hw_mode)
{
@ -3120,6 +3119,33 @@ static void rtpcs_931x_sds_clear_symerr(struct rtpcs_serdes *sds,
}
}
/*
* rtpcs_931x_sds_set_debug() - Route a coefficient's debug readback.
*
* Vendor SDK: _phy_rtl9310_dbg_set(). Selects which lane of the even/odd
* pair feeds the shared WDIG debug readback register, then selects
* dbg_sel within that lane. Must run before reading any rxeq_*_get().
*
* Note: This needs to be locked to avoid adjacent SerDes interfering with
* those settings and produce inconsistent results. Currently, this is
* achieved by the global PCS lock.
*/
static int rtpcs_931x_sds_set_debug(struct rtpcs_serdes *sds, unsigned int dbg_sel)
{
struct rtpcs_serdes *even_sds = rtpcs_sds_get_even(sds);
int ret;
ret = rtpcs_sds_write(even_sds, PAGE_WDIG, 0x2, (sds == even_sds) ? 0x4b : 0x4c);
if (ret < 0)
return ret;
ret = rtpcs_sds_write_bits(sds, PAGE_ANA_COM, 0x0, 2, 2, 0x1);
if (ret < 0)
return ret;
return rtpcs_sds_write_bits(sds, PAGE_ANA_10G, 0x15, 11, 10, dbg_sel);
}
static int rtpcs_931x_sds_rxeq_leq_set_adapt(struct rtpcs_serdes *sds, bool enable)
{
return rtpcs_sds_write_bits(sds, PAGE_ANA_10G, 0xd, 7, 7, enable ? 0x0 : 0x1);
@ -3130,6 +3156,96 @@ static int rtpcs_931x_sds_rxeq_leq_set_coef(struct rtpcs_serdes *sds, unsigned i
return rtpcs_sds_write_bits(sds, PAGE_ANA_10G, 0xd, 6, 2, gain);
}
static int rtpcs_931x_sds_rxeq_tap_set_value(struct rtpcs_serdes *sds, unsigned int tap_id,
int tap_even, int tap_odd)
{
int ret;
switch (tap_id) {
case 0:
return rtpcs_sds_write_bits(sds, PAGE_ANA_10G, 0x1c, 5, 0,
rtpcs_sign_mag_encode(tap_even, 5));
case 1:
ret = rtpcs_sds_write_bits(sds, PAGE_ANA_10G, 0x1d, 5, 0,
rtpcs_sign_mag_encode(tap_even, 5));
if (!ret)
ret = rtpcs_sds_write_bits(sds, PAGE_ANA_10G, 0x1d, 11, 6,
rtpcs_sign_mag_encode(tap_odd, 5));
return ret;
case 2:
ret = rtpcs_sds_write_bits(sds, PAGE_ANA_10G, 0x1f, 5, 0,
rtpcs_sign_mag_encode(tap_even, 5));
if (!ret)
ret = rtpcs_sds_write_bits(sds, PAGE_ANA_10G, 0x1f, 11, 6,
rtpcs_sign_mag_encode(tap_odd, 5));
return ret;
case 3:
ret = rtpcs_sds_write_bits(sds, PAGE_ANA_10G_EXT, 0x0, 5, 0,
rtpcs_sign_mag_encode(tap_even, 5));
if (!ret)
ret = rtpcs_sds_write_bits(sds, PAGE_ANA_10G_EXT, 0x0, 11, 6,
rtpcs_sign_mag_encode(tap_odd, 5));
return ret;
case 4:
ret = rtpcs_sds_write_bits(sds, PAGE_ANA_10G_EXT, 0x1, 5, 0,
rtpcs_sign_mag_encode(tap_even, 5));
if (!ret)
ret = rtpcs_sds_write_bits(sds, PAGE_ANA_10G_EXT, 0x1, 11, 6,
rtpcs_sign_mag_encode(tap_odd, 5));
return ret;
default:
return -EINVAL;
}
}
static int rtpcs_931x_sds_rxeq_tap_set_adapt(struct rtpcs_serdes *sds, unsigned int tap_id,
bool enable)
{
if (tap_id > 4)
return -EINVAL;
/* manual-mode enable bits, [10:6] = TAP0-TAP4 */
return rtpcs_sds_write_bits(sds, PAGE_ANA_10G, 0xf, tap_id + 6, tap_id + 6,
enable ? 0x0 : 0x1);
}
static int rtpcs_931x_sds_rxeq_vth_set_value(struct rtpcs_serdes *sds, unsigned int vth_p,
unsigned int vth_n)
{
return rtpcs_sds_write_bits(sds, PAGE_ANA_10G_EXT, 0x12, 11, 4,
FIELD_PREP(GENMASK(3, 0), vth_p) |
FIELD_PREP(GENMASK(7, 4), vth_n));
}
static int rtpcs_931x_sds_rxeq_vth_set_adapt(struct rtpcs_serdes *sds, bool enable)
{
return rtpcs_sds_write_bits(sds, PAGE_ANA_10G, 0xf, 12, 12, enable ? 0x0 : 0x1);
}
static int rtpcs_931x_sds_rxeq_vth_get(struct rtpcs_serdes *sds, unsigned int *vth_p,
unsigned int *vth_n)
{
int ret, val;
ret = rtpcs_931x_sds_set_debug(sds, 0x2);
if (ret < 0)
return ret;
ret = rtpcs_sds_write_bits(sds, PAGE_ANA_10G, 0x14, 10, 5, 0x0c); /* COEF_SEL = VTH */
if (ret < 0)
return ret;
usleep_range(1000, 2000);
val = rtpcs_sds_read_bits(sds, PAGE_WDIG, 0x14, 7, 0);
if (val < 0)
return val;
*vth_p = FIELD_GET(GENMASK(3, 0), val);
*vth_n = FIELD_GET(GENMASK(7, 4), val);
return 0;
}
/**
* rtpcs_931x_sds_reset_leq_dfe() - Reset LEQ + DFE to a baseline.
*
@ -3271,6 +3387,64 @@ static void rtpcs_931x_sds_rxcal_leq_adapt(struct rtpcs_serdes *sds)
msleep(100);
}
/*
* rtpcs_931x_sds_rxcal_fiber_adapt() - RX calibration for 10G fiber.
*
* Only used for 10GBase-R fiber; calibration not needed for fiber running
* on slower speeds.
*
* Deviates from the vendor SDK's retry shape which is considerably tighter
* (3 symbol error rechecks, 150ms delay, exact-0 target). symErr's field
* (8 bits wide) has been observed reading a saturated 0xff right after
* rx_reset(), which looks like "link hasn't relocked yet" rather than a
* genuine error count. Thus, recheck more times with more patience per
* check instead (no reset in between, so a settling link isn't interrupted),
* and tolerate a small nonzero symbol-error count rather than requiring
* exactly 0.
*/
static void rtpcs_931x_sds_rxcal_fiber_adapt(struct rtpcs_serdes *sds)
{
struct device *dev = sds->ctrl->dev;
unsigned int vth_p = 0, vth_n = 0;
int i, symerr = -1;
/* per-port calibration offset in the SDK, kept 0 here */
rtpcs_sds_write_bits(sds, PAGE_ANA_10G, 0xc, 14, 10, 0x0);
rtpcs_931x_sds_reset_leq_dfe(sds);
/* let VTH + TAP0 auto-adapt run and settle before sampling/forcing values */
rtpcs_931x_sds_rxeq_tap_set_adapt(sds, 0, true);
rtpcs_931x_sds_rxeq_vth_set_adapt(sds, true);
msleep(200);
/* VTH is sampled from auto-adapt and locked in; TAP0 is always forced to 31 */
if (rtpcs_931x_sds_rxeq_vth_get(sds, &vth_p, &vth_n) < 0)
dev_warn(dev, "SerDes %u failed to read auto-adapted VTH\n", sds->id);
rtpcs_931x_sds_rxeq_tap_set_value(sds, 0, 31, 0);
rtpcs_931x_sds_rxeq_tap_set_adapt(sds, 0, false);
rtpcs_931x_sds_rxeq_vth_set_value(sds, vth_p, vth_n);
rtpcs_931x_sds_rxeq_vth_set_adapt(sds, false);
rtpcs_931x_sds_rx_reset(sds);
/* let DFE TAP1-4 auto-adapt continuously */
for (i = 1; i <= 4; i++)
rtpcs_931x_sds_rxeq_tap_set_adapt(sds, i, true);
for (i = 0; i < 8; i++) {
rtpcs_931x_sds_clear_symerr(sds, RTPCS_SDS_MODE_10GBASER);
msleep(300);
symerr = rtpcs_931x_sds_fiber_get_symerr(sds, RTPCS_SDS_MODE_10GBASER);
if (symerr >= 0 && symerr <= 5)
return;
}
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)
{
int cmu_page, pll_sel;
@ -3638,8 +3812,13 @@ static int rtpcs_931x_sds_post_config(struct rtpcs_serdes *sds, enum rtpcs_sds_m
rtpcs_931x_sds_rxcal_leq_adapt(sds);
break;
case RTPCS_SDS_ATTACH_FIBER:
if (hw_mode == RTPCS_SDS_MODE_10GBASER)
rtpcs_931x_sds_rxcal_fiber_adapt(sds);
break;
default:
/* TODO: fiber/DAC RX calibration */
/* TODO: DAC RX calibration */
break;
}