From 393d5b01fba4dbefcaed95c1938cc2b6c47e2dfb Mon Sep 17 00:00:00 2001 From: Jonas Jelonek Date: Sat, 27 Jun 2026 19:23:05 +0000 Subject: [PATCH] realtek: pcs: rtl930x: fix LEQ calibration to use media type Replace the mode-based approximation in the LEQ adapt+lock step with proper media-based logic using the newly stored sds->media field. Those adjustments are based on SDK code and digging through some basics of signal conditioning. PCB connections route through an external PHY (PHY-attached); that PHY handles its own equalization so the SerDes LEQ is left in free-running auto-adapt with no correction offset. DAC and fiber SFP connect directly to the SerDes without an intermediate PHY and require a calibrated lock. Replace the SDK's dacLongCableOffset/eqHoldEnable variable pair with a single direct_serdes bool and encode the full per-media LEQ correction into one switch: fiber +3 (direct SerDes, no cable), DAC short +4 (base 3 + 1), DAC long +6 (base 3 + 3). Fix the MAXHOLD_EN bit (0x17[7]) and the 0x0c[8] write to be gated on direct_serdes; both were previously conditioned on PCB which is the PHY-attached case and thus wrong. Fix the LEQ lock to apply to all direct SerDes connections (DAC + fiber), not just PCB. Also drop the spurious dead-code branch that was an artifact of the original transplanting. No behavioral change because this particular function of calibration is not used right now but will be in the future. Link: https://github.com/openwrt/openwrt/pull/23983 Signed-off-by: Jonas Jelonek --- .../files-6.18/drivers/net/pcs/pcs-rtl-otto.c | 95 +++++++++---------- 1 file changed, 43 insertions(+), 52 deletions(-) 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 49b93cdc21e..5ca0def0c30 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 @@ -2549,74 +2549,65 @@ static void rtpcs_930x_sds_rxcal_fgcal(struct rtpcs_serdes *sds) } __always_unused -static void rtpcs_930x_sds_do_rx_calibration_3(struct rtpcs_serdes *sds, - enum rtpcs_sds_mode hw_mode) +static void rtpcs_930x_sds_rxcal_leq_adapt_lock(struct rtpcs_serdes *sds) { - u32 sum10 = 0, avg10, int10; - int dac_long_cable_offset; - bool eq_hold_enabled; + /* + * SDK dacLongCableOffset / eqHoldEnable from rtl9300_rxCaliConf_serdes/phy_myParam. + * These distinguish direct SerDes connections (DAC, fiber SFP — no external PHY in + * the signal path) from PHY-attached ports (PCB traces to an external PHY). On + * PHY-attached ports the PHY handles its own equalization, so the SerDes LEQ is left + * in auto-adapt and no correction offset is needed. + */ + bool direct_serdes = sds->media == RTPCS_SDS_MEDIA_FIBER || + sds->media == RTPCS_SDS_MEDIA_DAC_SHORT || + sds->media == RTPCS_SDS_MEDIA_DAC_LONG; + u32 sum10 = 0, avg10; int i; - if (hw_mode == RTPCS_SDS_MODE_10GBASER || - hw_mode == RTPCS_SDS_MODE_1000BASEX || - hw_mode == RTPCS_SDS_MODE_SGMII) { - /* rtl9300_rxCaliConf_serdes_myParam */ - dac_long_cable_offset = 3; - eq_hold_enabled = true; - } else { - /* rtl9300_rxCaliConf_phy_myParam */ - dac_long_cable_offset = 0; - eq_hold_enabled = false; + /* 1.3.1: release LEQ auto-adapt, let it settle from zero */ + if (!direct_serdes) rtpcs_sds_write_bits(sds, PAGE_ANA_10G, 0xc, 8, 8, 0x0); - } - rtpcs_sds_write_bits(sds, PAGE_ANA_10G, 0x17, 7, 7, 0x0); rtpcs_930x_sds_rxcal_leq_manual(sds, false, 0); - if (hw_mode != RTPCS_SDS_MODE_10GBASER) - pr_warn("%s: LEQ only valid for 10GR!\n", __func__); - - /* 1.3.2 --- */ - + /* 1.3.2: sample the auto-adapted LEQ value 10 times over ~100ms */ for (i = 0; i < 10; i++) { sum10 += rtpcs_930x_sds_rxcal_leq_read(sds); mdelay(10); } + /* rounded average of where auto-adapt settled */ avg10 = (sum10 / 10) + (((sum10 % 10) >= 5) ? 1 : 0); - int10 = sum10 / 10; - - pr_info("sum10:%u, avg10:%u, int10:%u", sum10, avg10, int10); - - if (hw_mode == RTPCS_SDS_MODE_10GBASER || - hw_mode == RTPCS_SDS_MODE_1000BASEX || - hw_mode == RTPCS_SDS_MODE_SGMII) { - if (dac_long_cable_offset) { - rtpcs_930x_sds_rxcal_leq_offset_manual(sds, 1, - dac_long_cable_offset); - rtpcs_sds_write_bits(sds, PAGE_ANA_10G, 0x17, 7, 7, - eq_hold_enabled); - if (hw_mode == RTPCS_SDS_MODE_10GBASER) - rtpcs_930x_sds_rxcal_leq_manual(sds, - true, avg10); - } else { - if (sum10 >= 5) { - rtpcs_930x_sds_rxcal_leq_offset_manual(sds, 1, 3); - rtpcs_sds_write_bits(sds, PAGE_ANA_10G, 0x17, 7, 7, 0x1); - if (hw_mode == RTPCS_SDS_MODE_10GBASER) - rtpcs_930x_sds_rxcal_leq_manual(sds, true, avg10); - } else { - rtpcs_930x_sds_rxcal_leq_offset_manual(sds, 1, 0); - rtpcs_sds_write_bits(sds, PAGE_ANA_10G, 0x17, 7, 7, 0x1); - if (hw_mode == RTPCS_SDS_MODE_10GBASER) - rtpcs_930x_sds_rxcal_leq_manual(sds, true, avg10); - } - } + + /* + * Empirical correction based on media type. + * Direct SerDes connections get a base offset of +3; DAC cables add further + * correction for their attenuation. PHY-attached (PCB) needs none. + */ + switch (sds->media) { + case RTPCS_SDS_MEDIA_FIBER: + avg10 += 3; + break; + case RTPCS_SDS_MEDIA_DAC_SHORT: + avg10 += 4; /* base 3 + 1 for short DAC */ + break; + case RTPCS_SDS_MEDIA_DAC_LONG: + avg10 += 6; /* base 3 + 3 for long DAC */ + break; + default: + break; } - pr_info("SDS %u LEQ = %u", sds->id, rtpcs_930x_sds_rxcal_leq_read(sds)); + pr_info("sum10:%u, avg10:%u", sum10, avg10); - /* --- 1.3.2 */ + /* lock LEQ at corrected value for direct SerDes; PHY-attached stays in auto-adapt */ + if (direct_serdes) { + rtpcs_930x_sds_rxcal_leq_offset_manual(sds, 1, 0); + rtpcs_sds_write_bits(sds, PAGE_ANA_10G, 0x17, 7, 7, 0x1); + rtpcs_930x_sds_rxcal_leq_manual(sds, true, avg10); + } + + pr_info("SDS %u LEQ = %u", sds->id, rtpcs_930x_sds_rxcal_leq_read(sds)); } static void rtpcs_930x_sds_rxcal_vth_tap0_adapt_lock(struct rtpcs_serdes *sds) -- 2.47.3