From 6879c8ea535a00aa6ad8530878094bf58bc8f65b Mon Sep 17 00:00:00 2001 From: Jonas Jelonek Date: Fri, 16 Jan 2026 09:32:52 +0000 Subject: [PATCH] realtek: pcs: add XSG write operations There is some special logic used for certain writes to digital pages for RTL93xx SerDes, especially when configuring the XSGMII mode. For RTL930x this applies to SerDes 2 and 3, for RTL93xx to more. In this case, a dual-read/write to SDS and SDS + 1 is done. While the corresponding mapping from front to back SDS for RTL931x is currently covered in the SerDes MDIO driver, it isn't for RTL930x. To cover these special cases and provide a clear interface on that, introduce an XSG write SerDes operation. All these dual-read/write cases can be expressed with such an XSG operation whose internal semantics are defined for each switchcore family. This could be done just with plain dual read/write calls however this isn't a clean approach and may be confusing while comparing our functionality with the SDK, especially for RTL930x. In practice, if this isn't handled correctly, only half of the ports of an XSGMII-connected RTL8218D do work because some required values aren't applied for the background SerDes 3. Signed-off-by: Jonas Jelonek Link: https://github.com/openwrt/openwrt/pull/21592 Signed-off-by: Robert Marko --- .../files-6.12/drivers/net/pcs/pcs-rtl-otto.c | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/target/linux/realtek/files-6.12/drivers/net/pcs/pcs-rtl-otto.c b/target/linux/realtek/files-6.12/drivers/net/pcs/pcs-rtl-otto.c index 31b40038f4f..24b94a4952a 100644 --- a/target/linux/realtek/files-6.12/drivers/net/pcs/pcs-rtl-otto.c +++ b/target/linux/realtek/files-6.12/drivers/net/pcs/pcs-rtl-otto.c @@ -159,6 +159,10 @@ struct rtpcs_serdes_ops { int (*read)(struct rtpcs_serdes *sds, int page, int regnum, int bithigh, int bitlow); int (*write)(struct rtpcs_serdes *sds, int page, int regnum, int bithigh, int bitlow, u16 value); + + /* optional */ + int (*xsg_write)(struct rtpcs_serdes *sds, int page, int regnum, int bithigh, int bitlow, + u16 value); }; struct rtpcs_serdes { @@ -297,6 +301,25 @@ static int rtpcs_sds_write(struct rtpcs_serdes *sds, int page, int regnum, u16 v return sds->ops->write(sds, page, regnum, 15, 0, value); } +__maybe_unused +static int rtpcs_sds_xsg_write_bits(struct rtpcs_serdes *sds, int page, int regnum, int bithigh, + int bitlow, u16 value) +{ + if (!sds->ops->xsg_write) + return -ENOTSUPP; + + return sds->ops->xsg_write(sds, page, regnum, bithigh, bitlow, value); +} + +__maybe_unused +static int rtpcs_sds_xsg_write(struct rtpcs_serdes *sds, int page, int regnum, u16 value) +{ + if (!sds->ops->xsg_write) + return -ENOTSUPP; + + return sds->ops->xsg_write(sds, page, regnum, 15, 0, value); +} + /* Other helpers */ static int rtpcs_sds_modify(struct rtpcs_serdes *sds, int page, int regnum, @@ -960,6 +983,39 @@ static int rtpcs_930x_sds_op_write(struct rtpcs_serdes *sds, int page, int regnu return __rtpcs_sds_write_raw(sds->ctrl, sds_id, page, regnum, bithigh, bitlow, value); } +/* + * Realtek uses some nasty logic for digital parts of SerDes 2 and 3. + * + * This implements 'dal_longan_sds_xsg_field_write' and a combination of + * '_rtl9300_serdes_index_to_physical' and '_rtl9300_serdes_reg_write' from the SDK. + */ +static int rtpcs_930x_sds_op_xsg_write(struct rtpcs_serdes *sds, int page, int regnum, + int bithigh, int bitlow, u16 value) +{ + int phys_sds_id, ret; + + switch (sds->id) { + case 2: + phys_sds_id = 2; + break; + case 3: + phys_sds_id = 10; + break; + default: + return -ENOTSUPP; + } + + if (page >= 4) + return sds->ops->write(sds, page, regnum, bithigh, bitlow, value); + + ret = __rtpcs_sds_write_raw(sds->ctrl, phys_sds_id, page, regnum, bithigh, bitlow, value); + if (ret) + return ret; + + return __rtpcs_sds_write_raw(sds->ctrl, phys_sds_id + 1, page, regnum, bithigh, bitlow, + value); +} + static const u16 rtpcs_930x_sds_regs[] = { 0x0194, 0x0194, 0x0194, 0x0194, /* SDS_MODE_SEL_0 */ 0x02a0, 0x02a0, 0x02a0, 0x02a0, /* SDS_MODE_SEL_1 */ @@ -2742,6 +2798,29 @@ static int rtpcs_930x_setup_serdes(struct rtpcs_serdes *sds, /* RTL931X */ +/* + * The SerDes MDIO driver maps page regions to different background SerDes. + * 0x00 - 0x3f analog SDS + * 0x40 - 0x7f digital SDS 1 + * 0x80 - 0xbf digital SDS 2 + * + * An XSG write operates on digital SDS 1 and digital SDS 2. Map that to the + * page ranges accordingly. + */ +static int rtpcs_931x_sds_op_xsg_write(struct rtpcs_serdes *sds, int page, int regnum, + int bithigh, int bitlow, u16 value) +{ + int ret; + + ret = __rtpcs_sds_write_raw(sds->ctrl, sds->id, page + 0x40, regnum, bithigh, bitlow, + value); + if (ret) + return ret; + + return __rtpcs_sds_write_raw(sds->ctrl, sds->id, page + 0x80, regnum, bithigh, bitlow, + value); +} + __maybe_unused static int rtpcs_931x_sds_fiber_get_symerr(struct rtpcs_serdes *sds, enum rtpcs_sds_mode hw_mode) @@ -3887,6 +3966,7 @@ static const struct phylink_pcs_ops rtpcs_930x_pcs_ops = { static const struct rtpcs_serdes_ops rtpcs_930x_sds_ops = { .read = rtpcs_930x_sds_op_read, .write = rtpcs_930x_sds_op_write, + .xsg_write = rtpcs_930x_sds_op_xsg_write, }; static const struct rtpcs_config rtpcs_930x_cfg = { @@ -3913,6 +3993,7 @@ static const struct phylink_pcs_ops rtpcs_931x_pcs_ops = { static const struct rtpcs_serdes_ops rtpcs_931x_sds_ops = { .read = rtpcs_generic_sds_op_read, .write = rtpcs_generic_sds_op_write, + .xsg_write = rtpcs_931x_sds_op_xsg_write, }; static const struct rtpcs_config rtpcs_931x_cfg = { -- 2.47.3