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:
parent
58e896eb70
commit
3c63abd609
1 changed files with 181 additions and 2 deletions
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue