From 7a7ee72c4d306e7324a8ea0c1d541c4a7fea5ab1 Mon Sep 17 00:00:00 2001 From: Markus Stockhausen Date: Mon, 15 Sep 2025 12:08:53 -0400 Subject: [PATCH] realtek: mdio: add SerDes driver Until now the SerDes access is realized with some helper functions in the mdio bus. These were moved around a lot and had no real home. End that temporary solution to move them where they belong. The target design for the different Realtek drivers is as follows: - dsa driver manages switch - pcs driver manages SerDes on high level (to be developed) - mdio driver manages SerDes on low level (this commit) This driver adds the low level SerDes access via mdio. For debugging purposes the user can interact with the SerDes in different ways. First, there is a debug interface in /sys/kernel/debug/realtek_otto_serdes/serdes.X/registers. With that a dump of all registers can be shown. > cat /sys/kernel/debug/realtek_otto_serdes/serdes.4/registers Back SDS 4: 00 01 02 03 04 05 06 07 08 SDS : 0C03 0F00 7060 7106 074D 0EBF 0F0F 0359 5248 SDS_EXT : 0000 0000 85FA 8C6D 5CCC 0000 20D8 0003 79AA ... Second, one can read/write registers via the mmd functions of the mdio command line tool. Important to know: The registers are accessed on the vendor specific MDIO_MMD_VEND1 device address (=30). Additionally the SerDes page and register are concatenated into the the mmd register. Top 8 bits are SerDes page and bottom 8 bits are SerDEs register. E.g. - mmd 0x0206 : SerDes page 0x02, SerDes register 0x06 - mmd 0x041f : SerDes page 0x04, SerDes register 0x1f Read register 0x02 on page 0x03 of SerDes 0 > mdio realtek-serdes-mdio mmd 0:30 raw 0x0302 Write register 0x12 on page 0x02 of SerDes 1 > mdio realtek-serdes-mdio mmd 1:30 raw 0x0212 0x2222 For now this driver is only defined in the devicetree and activated in the kernel build. There is no current consumer but at least the debugging interface is available. Cleanup of the currently used SerDes functions will come later. Signed-off-by: Markus Stockhausen Link: https://github.com/openwrt/openwrt/pull/20062 Signed-off-by: Robert Marko --- target/linux/realtek/dts/rtl838x.dtsi | 4 + target/linux/realtek/dts/rtl839x.dtsi | 4 + target/linux/realtek/dts/rtl930x.dtsi | 4 + target/linux/realtek/dts/rtl931x.dtsi | 5 + .../net/mdio/mdio-realtek-otto-serdes.c | 532 ++++++++++++++++++ .../808-add-serdes-mdio-driver.patch | 37 ++ target/linux/realtek/rtl838x/config-6.12 | 1 + target/linux/realtek/rtl839x/config-6.12 | 1 + target/linux/realtek/rtl930x/config-6.12 | 1 + target/linux/realtek/rtl930x_nand/config-6.12 | 1 + target/linux/realtek/rtl931x/config-6.12 | 1 + target/linux/realtek/rtl931x_nand/config-6.12 | 1 + 12 files changed, 592 insertions(+) create mode 100644 target/linux/realtek/files-6.12/drivers/net/mdio/mdio-realtek-otto-serdes.c create mode 100644 target/linux/realtek/patches-6.12/808-add-serdes-mdio-driver.patch diff --git a/target/linux/realtek/dts/rtl838x.dtsi b/target/linux/realtek/dts/rtl838x.dtsi index f4b629033b2..3b17c66c0b4 100644 --- a/target/linux/realtek/dts/rtl838x.dtsi +++ b/target/linux/realtek/dts/rtl838x.dtsi @@ -226,6 +226,10 @@ pinctrl-0 = <&mdio_aux_mdx>, <&aux_mode_mdio>; }; + mdio_serdes: mdio-serdes { + compatible = "realtek,rtl8380-serdes-mdio", "realtek,otto-serdes-mdio"; + }; + soc_thermal: thermal { compatible = "realtek,rtl8380-thermal"; #thermal-sensor-cells = <0>; diff --git a/target/linux/realtek/dts/rtl839x.dtsi b/target/linux/realtek/dts/rtl839x.dtsi index 256ee0bc305..9eb4be8af80 100644 --- a/target/linux/realtek/dts/rtl839x.dtsi +++ b/target/linux/realtek/dts/rtl839x.dtsi @@ -234,6 +234,10 @@ pinctrl-0 = <&mdio_aux_mdx>; }; + mdio_serdes: mdio-serdes { + compatible = "realtek,rtl8392-serdes-mdio", "realtek,otto-serdes-mdio"; + }; + soc_thermal: thermal { compatible = "realtek,rtl8390-thermal"; #thermal-sensor-cells = <0>; diff --git a/target/linux/realtek/dts/rtl930x.dtsi b/target/linux/realtek/dts/rtl930x.dtsi index 13c6fad5a11..97bf6abe5d0 100644 --- a/target/linux/realtek/dts/rtl930x.dtsi +++ b/target/linux/realtek/dts/rtl930x.dtsi @@ -201,6 +201,10 @@ status = "disabled"; }; + mdio_serdes: mdio-serdes { + compatible = "realtek,rtl9301-serdes-mdio", "realtek,otto-serdes-mdio"; + }; + soc_thermal: thermal { compatible = "realtek,rtl9300-thermal"; #thermal-sensor-cells = <0>; diff --git a/target/linux/realtek/dts/rtl931x.dtsi b/target/linux/realtek/dts/rtl931x.dtsi index 27f12df2b80..352e9547f01 100644 --- a/target/linux/realtek/dts/rtl931x.dtsi +++ b/target/linux/realtek/dts/rtl931x.dtsi @@ -232,6 +232,11 @@ status = "disabled"; }; + + mdio_serdes: mdio-serdes { + compatible = "realtek,rtl9311-serdes-mdio", "realtek,otto-serdes-mdio"; + }; + }; pinmux@1b001358 { diff --git a/target/linux/realtek/files-6.12/drivers/net/mdio/mdio-realtek-otto-serdes.c b/target/linux/realtek/files-6.12/drivers/net/mdio/mdio-realtek-otto-serdes.c new file mode 100644 index 00000000000..6296d1b6fe2 --- /dev/null +++ b/target/linux/realtek/files-6.12/drivers/net/mdio/mdio-realtek-otto-serdes.c @@ -0,0 +1,532 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RTSDS_REG_CNT 32 +#define RTSDS_VAL_MASK GENMASK(15, 0) +#define RTSDS_SUBPAGE(page) (page % 64) + +#define RTSDS_MMD_PAGE_MASK GENMASK(15, 8) +#define RTSDS_MMD_REG_MASK GENMASK(7, 0) + +#define RTSDS_838X_SDS_CNT 6 +#define RTSDS_838X_PAGE_CNT 4 +#define RTSDS_838X_BASE 0xe780 + +#define RTSDS_839X_SDS_CNT 14 +#define RTSDS_839X_PAGE_CNT 12 +#define RTSDS_839X_BASE 0xa000 + +#define RTSDS_930X_SDS_CNT 12 +#define RTSDS_930X_PAGE_CNT 64 +#define RTSDS_930X_BASE 0x03b0 + +#define RTSDS_931X_SDS_CNT 14 +#define RTSDS_931X_PAGE_CNT 192 +#define RTSDS_931X_BASE 0x5638 + +#define RTSDS_93XX_CMD_READ 0 +#define RTSDS_93XX_CMD_WRITE BIT(1) +#define RTSDS_93XX_CMD_BUSY BIT(0) +#define RTSDS_93XX_CMD_SDS_MASK GENMASK(6, 2) +#define RTSDS_93XX_CMD_PAGE_MASK GENMASK(12, 7) +#define RTSDS_93XX_CMD_REG_MASK GENMASK(17, 13) + +struct rtsds_ctrl { + struct device *dev; + struct regmap *map; + struct mii_bus *bus; + const struct rtsds_config *cfg; +}; + +struct rtsds_config { + int sds_cnt; + int page_cnt; + int base; + int (*read)(struct rtsds_ctrl *ctrl, int sds, int page, int regnum); + int (*write)(struct rtsds_ctrl *ctrl, int sds, int page, int regnum, u16 value); + int (*backing_sds)(int sds, int page); +}; + +static bool rtsds_mmd_to_sds(struct rtsds_ctrl *ctrl, int addr, int devad, int mmd_regnum, + int *sds_page, int *sds_regnum) +{ + *sds_page = FIELD_GET(RTSDS_MMD_PAGE_MASK, mmd_regnum); + *sds_regnum = FIELD_GET(RTSDS_MMD_REG_MASK, mmd_regnum); + + return !(addr < 0 || addr >= ctrl->cfg->sds_cnt || + *sds_page < 0 || *sds_page >= ctrl->cfg->page_cnt || + *sds_regnum < 0 || *sds_regnum >= RTSDS_REG_CNT || + devad != MDIO_MMD_VEND1); +} + +static int rtsds_sds_to_mmd(int sds_page, int sds_regnum) +{ + return FIELD_PREP(RTSDS_MMD_PAGE_MASK, sds_page) | + FIELD_PREP(RTSDS_MMD_REG_MASK, sds_regnum); +} + +static int rtsds_get_backing_sds(int sds, int page) +{ + /* non RTL931x devices have 1:1 frontend/backend mapping */ + return sds; +} + +#ifdef CONFIG_DEBUG_FS + +/* + * The SerDes offer a lot of magic that sill needs to be uncovered. Getting this info manually + * with mdio command line tools can be time consuming. Provide a convenient register dump. + */ + +#define RTSDS_DBG_PAGE_NAMES 48 +#define RTSDS_DBG_ROOT_DIR "realtek_otto_serdes" + +struct rtsds_debug_info { + struct rtsds_ctrl *ctrl; + int sds; +}; + +static const char * const rtsds_page_name[RTSDS_DBG_PAGE_NAMES] = { + [0] = "SDS", [1] = "SDS_EXT", + [2] = "FIB", [3] = "FIB_EXT", + [4] = "DTE", [5] = "DTE_EXT", + [6] = "TGX", [7] = "TGX_EXT", + [8] = "ANA_RG", [9] = "ANA_RG_EXT", + [10] = "ANA_TG", [11] = "ANA_TG_EXT", + [31] = "ANA_WDIG", + [32] = "ANA_MISC", [33] = "ANA_COM", + [34] = "ANA_SP", [35] = "ANA_SP_EXT", + [36] = "ANA_1G", [37] = "ANA_1G_EXT", + [38] = "ANA_2G", [39] = "ANA_2G_EXT", + [40] = "ANA_3G", [41] = "ANA_3G_EXT", + [42] = "ANA_5G", [43] = "ANA_5G_EXT", + [44] = "ANA_6G", [45] = "ANA_6G_EXT", + [46] = "ANA_10G", [47] = "ANA_10G_EXT", +}; + +static int rtsds_dbg_registers_show(struct seq_file *seqf, void *unused) +{ + struct rtsds_debug_info *dbg_info = seqf->private; + struct rtsds_ctrl *ctrl = dbg_info->ctrl; + struct mii_bus *bus = ctrl->bus; + int sds = dbg_info->sds; + int regnum, page = 0; + int subpage; + + do { + subpage = RTSDS_SUBPAGE(page); + if (!subpage) { + seq_printf(seqf, "Back SDS %02d:", ctrl->cfg->backing_sds(sds, page)); + for (regnum = 0; regnum < RTSDS_REG_CNT; regnum++) + seq_printf(seqf, " %02X", regnum); + seq_puts(seqf, "\n"); + } + + if (subpage < RTSDS_DBG_PAGE_NAMES && rtsds_page_name[subpage]) + seq_printf(seqf, "%*s: ", -11, rtsds_page_name[subpage]); + else + seq_printf(seqf, "PAGE %02X : ", page); + + for (regnum = 0; regnum < RTSDS_REG_CNT; regnum++) + seq_printf(seqf, "%04X ", + mdiobus_c45_read(bus, sds, MDIO_MMD_VEND1, + rtsds_sds_to_mmd(page, regnum))); + seq_puts(seqf, "\n"); + } while (++page < ctrl->cfg->page_cnt); + + return 0; +} +DEFINE_SHOW_ATTRIBUTE(rtsds_dbg_registers); + +static int rtsds_debug_init(struct rtsds_ctrl *ctrl, u32 sds) +{ + struct rtsds_debug_info *dbg_info; + struct dentry *dir, *root; + char dirname[32]; + + dbg_info = kzalloc(sizeof(*dbg_info), GFP_KERNEL); + if (!dbg_info) + return -ENOMEM; + + dbg_info->ctrl = ctrl; + dbg_info->sds = sds; + + root = debugfs_lookup(RTSDS_DBG_ROOT_DIR, NULL); + if (!root) + root = debugfs_create_dir(RTSDS_DBG_ROOT_DIR, NULL); + + snprintf(dirname, sizeof(dirname), "serdes.%d", sds); + dir = debugfs_create_dir(dirname, root); + + debugfs_create_file("registers", 0600, dir, dbg_info, &rtsds_dbg_registers_fops); + + return 0; +} + +#endif /* CONFIG_DEBUG_FS */ + +/* + * The RTL838x has 6 SerDes. The 16 bit registers start at 0xbb00e780 and are mapped directly into + * 32 bit memory addresses. High 16 bits are always empty. A "lower" memory block serves pages 0/3 + * a "higher" memory block pages 1/2. + */ + +static int rtsds_838x_reg_offset(int sds, int page, int regnum) +{ + if (page == 0 || page == 3) + return (sds << 9) + (page << 7) + (regnum << 2); + + /* (page == 1 || page == 2) */ + return 0xb80 + (sds << 8) + (page << 7) + (regnum << 2); +} + +static int rtsds_838x_read(struct rtsds_ctrl *ctrl, int sds, int page, int regnum) +{ + int offset = rtsds_838x_reg_offset(sds, page, regnum); + int ret, value; + + ret = regmap_read(ctrl->map, ctrl->cfg->base + offset, &value); + + return ret ? ret : value & RTSDS_VAL_MASK; +} + +static int rtsds_838x_write(struct rtsds_ctrl *ctrl, int sds, int page, int regnum, u16 value) +{ + int offset = rtsds_838x_reg_offset(sds, page, regnum); + + return regmap_write(ctrl->map, ctrl->cfg->base + offset, value); +} + +/* + * The RTL839x has 14 SerDes starting at 0xbb00a000. 0-7, 10, 11 are 5GBit, 8, 9, 12, 13 are + * 10 GBit. Two adjacent SerDes are tightly coupled and share a 1024 bytes register area. Per 32 + * bit address two registers are stored. The first register is stored in the lower 2 bytes ("on + * the right" due to big endian) and the second register in the upper 2 bytes. The following + * register areas are known: + * + * - XSG0 (4 pages @ offset 0x000): for even SerDes + * - XSG1 (4 pages @ offset 0x100): for odd SerDes + * - TGRX (4 pages @ offset 0x200): for even 10G SerDes + * - ANA_RG (2 pages @ offset 0x300): for even 5G SerDes + * - ANA_RG (2 pages @ offset 0x380): for odd 5G SerDes + * - ANA_TG (2 pages @ offset 0x300): for even 10G SerDes + * - ANA_TG (2 pages @ offset 0x380): for odd 10G SerDes + * + * The most consistent mapping that aligns to the RTL93xx devices is: + * + * even 5G SerDes odd 5G SerDes even 10G SerDes odd 10G SerDes + * Page 0: XSG0/0 XSG1/0 XSG0/0 XSG1/0 + * Page 1: XSG0/1 XSG1/1 XSG0/1 XSG1/1 + * Page 2: XSG0/2 XSG1/2 XSG0/2 XSG1/2 + * Page 3: XSG0/3 XSG1/3 XSG0/3 XSG1/3 + * Page 4: TGRX/0 + * Page 5: TGRX/1 + * Page 6: TGRX/2 + * Page 7: TGRX/3 + * Page 8: ANA_RG ANA_RG + * Page 9: ANA_RG_EXT ANA_RG_EXT + * Page 10: ANA_TG ANA_TG + * Page 11: ANA_TG_EXT ANA_TG_EXT + */ + +static int rtsds_839x_reg_offset(int sds, int page, int regnum) +{ + int offset = ((sds & 0xfe) << 9) + ((regnum & 0xfe) << 1) + (page << 6); + int sds5g = (GENMASK(11, 10) | GENMASK(7, 0)) & BIT(sds); + + if (page < 4) + return offset + ((sds & 1) << 8); + else if ((page & 4) && (sds == 8 || sds == 12)) + return offset + 0x100; + else if (page >= 8 && page <= 9 && sds5g) + return offset + 0x100 + ((sds & 1) << 7); + else if (page >= 10 && !sds5g) + return offset + 0x80 + ((sds & 1) << 7); + + return -EINVAL; /* hole */ +} + +static int rtsds_839x_read(struct rtsds_ctrl *ctrl, int sds, int page, int regnum) +{ + int offset = rtsds_839x_reg_offset(sds, page, regnum); + int shift = regnum & 1 ? 16 : 0; + int ret, value; + + if (offset < 0) + return 0; + + ret = regmap_read(ctrl->map, ctrl->cfg->base + offset, &value); + + return ret ? ret : (value >> shift) & RTSDS_VAL_MASK; +} + +static int rtsds_839x_write(struct rtsds_ctrl *ctrl, int sds, int page, int regnum, u16 value) +{ + int offset = rtsds_839x_reg_offset(sds, page, regnum); + int write_value = value; + int neigh_value; + + if (offset < 0) + return 0; + + neigh_value = rtsds_839x_read(ctrl, sds, page, regnum ^ 1); + if (neigh_value < 0) + return neigh_value; + + if (regnum & 1) + write_value = (write_value << 16) + neigh_value; + else + write_value = (neigh_value << 16) + write_value; + + return regmap_write(ctrl->map, ctrl->cfg->base + offset, write_value); +} + +/* + * RTL93xx targets use a shared implementation. Their SerDes data is accessed through two IO + * registers which simulate commands to an internal MDIO bus. + * + * The RTL930x family has 12 SerDes of three types. + * + * - SerDes 0-1 exist on the RTL9301 and 9302B and are QSGMII capable + * - SerDes 2-9 are USXGMII capabable with either quad or single configuration + * - SerDes 10-11 are 10GBase-R capable + * + * The RTL931x family has 14 "frontend" SerDes that are cascaded. All operations (e.g. reset) work + * on this frontend view while their registers are distributed over a total of least 26 background + * SerDes with 64 pages and 32 registers. Three types of SerDes exist: + * + * - Serdes 0,1 are "simple" and work on one background serdes. + * - "Even" SerDes with numbers 2, 4, 6, 8, 10, 12 work on two background SerDes. One analog and + * one digital. + * - "Odd" SerDes with numbers 3, 5, 7, 9, 11, 13 work on a total of 3 background SerDes (one + * analog and two digital) + * + * This maps to: + * + * Frontend SerDes | 0 1 2 3 4 5 6 7 8 9 10 11 12 13 + * -----------------+------------------------------------------ + * Backend SerDes 1 | 0 1 2 3 6 7 10 11 14 15 18 19 22 23 + * Backend SerDes 2 | 0 1 2 4 6 8 10 12 14 16 18 20 22 24 + * Backend SerDes 3 | 0 1 3 5 7 9 11 13 15 17 19 21 23 25 + * + * Note: In Realtek proprietary XSGMII mode (10G pumped SGMII) the frontend SerDes works on the + * two digital SerDes while in all other modes it works on the analog and the first digital + * SerDes. Overlapping (e.g. backend SerDes 7 can be analog or digital 2) is avoided by the + * existing hardware designs. + * + * Align this for readability by simulating a total of 192 pages and mix them as follows. + * + * frontend page "even" frontend SerDes "odd" frontend SerDes + * page 0x00-0x3f (analog): page 0x00-0x3f back SDS page 0x00-0x3f back SDS + * page 0x40-0x7f (digi 1): page 0x00-0x3f back SDS page 0x00-0x3f back SDS+1 + * page 0x80-0xbf (digi 2): page 0x00-0x3f back SDS+1 page 0x00-0x3f back SDS+2 + */ + +static int rtsds_931x_get_backing_sds(int sds, int page) +{ + int map[] = { 0, 1, 2, 3, 6, 7, 10, 11, 14, 15, 18, 19, 22, 23 }; + int backsds = map[sds]; + + if (sds < 2) + return backsds; + else if (sds & 1) + backsds += (page >> 6); /* distribute "odd" to 3 background SerDes */ + else + backsds += (page >> 7); /* distribute "even" to 2 background SerDes */ + + return backsds; +} + +static int rtsds_rt93xx_io(struct rtsds_ctrl *ctrl, int sds, int page, int regnum, int cmd) +{ + int ret, op, value; + + op = FIELD_PREP(RTSDS_93XX_CMD_SDS_MASK, sds) | + FIELD_PREP(RTSDS_93XX_CMD_PAGE_MASK, page) | + FIELD_PREP(RTSDS_93XX_CMD_REG_MASK, regnum) | + RTSDS_93XX_CMD_BUSY | cmd; + + regmap_write(ctrl->map, ctrl->cfg->base, op); + ret = regmap_read_poll_timeout(ctrl->map, ctrl->cfg->base, value, + !(value & RTSDS_93XX_CMD_BUSY), 30, 1000000); + + if (ret < 0) { + dev_err(ctrl->dev, "SerDes I/O timed out\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static int rtsds_93xx_read(struct rtsds_ctrl *ctrl, int sds, int page, int regnum) +{ + int subpage = RTSDS_SUBPAGE(page); + int ret, backsds, value; + + backsds = ctrl->cfg->backing_sds(sds, page); + ret = rtsds_rt93xx_io(ctrl, backsds, subpage, regnum, RTSDS_93XX_CMD_READ); + if (ret) + return ret; + + ret = regmap_read(ctrl->map, ctrl->cfg->base + 4, &value); + + return ret ? ret : value & RTSDS_VAL_MASK; +} + +static int rtsds_93xx_write(struct rtsds_ctrl *ctrl, int sds, int page, int regnum, u16 value) +{ + int subpage = RTSDS_SUBPAGE(page); + int ret, backsds; + + backsds = ctrl->cfg->backing_sds(sds, page); + ret = regmap_write(ctrl->map, ctrl->cfg->base + 4, value); + if (ret) + return ret; + + return rtsds_rt93xx_io(ctrl, backsds, subpage, regnum, RTSDS_93XX_CMD_WRITE); +} + +static int rtsds_read(struct mii_bus *bus, int addr, int devad, int regnum) +{ + struct rtsds_ctrl *ctrl = bus->priv; + int sds_page, sds_regnum; + + if (!rtsds_mmd_to_sds(ctrl, addr, devad, regnum, &sds_page, &sds_regnum)) + return -EINVAL; + + return ctrl->cfg->read(ctrl, addr, sds_page, sds_regnum); +} + +static int rtsds_write(struct mii_bus *bus, int addr, int devad, int regnum, u16 value) +{ + struct rtsds_ctrl *ctrl = bus->priv; + int sds_page, sds_regnum; + + if (!rtsds_mmd_to_sds(ctrl, addr, devad, regnum, &sds_page, &sds_regnum)) + return -EINVAL; + + return ctrl->cfg->write(ctrl, addr, sds_page, sds_regnum, value); +} + +static int rtsds_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct device *dev = &pdev->dev; + struct rtsds_ctrl *ctrl; + struct mii_bus *bus; + int ret; + + bus = devm_mdiobus_alloc_size(&pdev->dev, sizeof(*ctrl)); + if (!bus) + return -ENOMEM; + + ctrl = bus->priv; + ctrl->map = syscon_node_to_regmap(np->parent); + if (IS_ERR(ctrl->map)) + return PTR_ERR(ctrl->map); + + ctrl->dev = dev; + ctrl->cfg = (const struct rtsds_config *)device_get_match_data(ctrl->dev); + ctrl->bus = bus; + + snprintf(bus->id, MII_BUS_ID_SIZE, "realtek-serdes-mdio") ; + bus->name = "Realtek SerDes MDIO bus"; + bus->parent = dev; + bus->read_c45 = rtsds_read; + bus->write_c45 = rtsds_write; + bus->phy_mask = ~0ULL; + + ret = devm_mdiobus_register(dev, bus); + if (ret) + return ret; + +#ifdef CONFIG_DEBUG_FS + for (int sds = 0; sds < ctrl->cfg->sds_cnt; sds++) + rtsds_debug_init(ctrl, sds); +#endif + + dev_info(dev, "Realtek SerDes mdio bus initialized. %d SerDes, %d pages, %d registers.", + ctrl->cfg->sds_cnt, ctrl->cfg->page_cnt, RTSDS_REG_CNT); + + return 0; +} + +static const struct rtsds_config rtsds_838x_cfg = { + .sds_cnt = RTSDS_838X_SDS_CNT, + .page_cnt = RTSDS_838X_PAGE_CNT, + .base = RTSDS_838X_BASE, + .read = rtsds_838x_read, + .write = rtsds_838x_write, + .backing_sds = rtsds_get_backing_sds, +}; + +static const struct rtsds_config rtsds_839x_cfg = { + .sds_cnt = RTSDS_839X_SDS_CNT, + .page_cnt = RTSDS_839X_PAGE_CNT, + .base = RTSDS_839X_BASE, + .read = rtsds_839x_read, + .write = rtsds_839x_write, + .backing_sds = rtsds_get_backing_sds, +}; + +static const struct rtsds_config rtsds_930x_cfg = { + .sds_cnt = RTSDS_930X_SDS_CNT, + .page_cnt = RTSDS_930X_PAGE_CNT, + .base = RTSDS_930X_BASE, + .read = rtsds_93xx_read, + .write = rtsds_93xx_write, + .backing_sds = rtsds_get_backing_sds, +}; + +static const struct rtsds_config rtsds_931x_cfg = { + .sds_cnt = RTSDS_931X_SDS_CNT, + .page_cnt = RTSDS_931X_PAGE_CNT, + .base = RTSDS_931X_BASE, + .read = rtsds_93xx_read, + .write = rtsds_93xx_write, + .backing_sds = rtsds_931x_get_backing_sds, +}; + +static const struct of_device_id rtsds_of_match[] = { + { + .compatible = "realtek,rtl8380-serdes-mdio", + .data = &rtsds_838x_cfg, + }, + { + .compatible = "realtek,rtl8392-serdes-mdio", + .data = &rtsds_839x_cfg, + }, + { + .compatible = "realtek,rtl9301-serdes-mdio", + .data = &rtsds_930x_cfg, + }, + { + .compatible = "realtek,rtl9311-serdes-mdio", + .data = &rtsds_931x_cfg, + }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, rtsds_mdio_of_match); + +static struct platform_driver rtsds_mdio_driver = { + .driver = { + .name = "realtek-otto-serdes-mdio", + .of_match_table = rtsds_of_match + }, + .probe = rtsds_probe, +}; +module_platform_driver(rtsds_mdio_driver); + +MODULE_AUTHOR("Markus Stockhausen "); +MODULE_DESCRIPTION("Realtek Otto SerDes MDIO bus"); +MODULE_LICENSE("GPL v2"); \ No newline at end of file diff --git a/target/linux/realtek/patches-6.12/808-add-serdes-mdio-driver.patch b/target/linux/realtek/patches-6.12/808-add-serdes-mdio-driver.patch new file mode 100644 index 00000000000..292683f8468 --- /dev/null +++ b/target/linux/realtek/patches-6.12/808-add-serdes-mdio-driver.patch @@ -0,0 +1,37 @@ +From fc3eda9aa25765b2115e7427ee4f6dbcd60f8721 Mon Sep 17 00:00:00 2001 +From: Markus Stockhausen +Date: Mon, 15 Sep 2025 17:23:31 +0200 +Subject: [PATCH] net: mdio: Add Realtek Otto SerDes controller + +SoCs in Realtek's Otto platform such as the RTL83xx and RTL93xx +have multiple SerDes to drive the PHYs. Provide a mdio interface +to access their registers. + +Signed-off-by: Markus Stockhausen +--- +--- a/drivers/net/mdio/Makefile ++++ b/drivers/net/mdio/Makefile +@@ -22,6 +22,7 @@ obj-$(CONFIG_MDIO_OCTEON) += mdio-octeo + obj-$(CONFIG_MDIO_REGMAP) += mdio-regmap.o + obj-$(CONFIG_MDIO_REALTEK_OTTO) += mdio-realtek-otto.o + obj-$(CONFIG_MDIO_REALTEK_OTTO_AUX) += mdio-realtek-otto-aux.o ++obj-$(CONFIG_MDIO_REALTEK_OTTO_SERDES) += mdio-realtek-otto-serdes.o + obj-$(CONFIG_MDIO_SMBUS) += mdio-smbus.o + obj-$(CONFIG_MDIO_SUN4I) += mdio-sun4i.o + obj-$(CONFIG_MDIO_THUNDER) += mdio-thunder.o +--- a/drivers/net/mdio/Kconfig ++++ b/drivers/net/mdio/Kconfig +@@ -224,6 +224,13 @@ config MDIO_REALTEK_OTTO_AUX + This driver supports the auxilairy MDIO bus on RTL838x SoCs. This bus + is typically used to attach RTL8231 GPIO extenders. + ++config MDIO_REALTEK_OTTO_SERDES ++ tristate "Realtek Otto MDIO SerDes interface support" ++ default MACH_REALTEK_RTL ++ depends on MACH_REALTEK_RTL || COMPILE_TEST ++ help ++ This driver provides MDIO access to the RTL83xx/RTL93xx SerDes. ++ + config MDIO_THUNDER + tristate "ThunderX SOCs MDIO buses" + depends on 64BIT diff --git a/target/linux/realtek/rtl838x/config-6.12 b/target/linux/realtek/rtl838x/config-6.12 index 287541feb26..d156ce1a175 100644 --- a/target/linux/realtek/rtl838x/config-6.12 +++ b/target/linux/realtek/rtl838x/config-6.12 @@ -136,6 +136,7 @@ CONFIG_MDIO_GPIO=y CONFIG_MDIO_I2C=y CONFIG_MDIO_REALTEK_OTTO=y CONFIG_MDIO_REALTEK_OTTO_AUX=y +CONFIG_MDIO_REALTEK_OTTO_SERDES=y CONFIG_MDIO_SMBUS=y CONFIG_MFD_CORE=y CONFIG_MFD_RTL8231=y diff --git a/target/linux/realtek/rtl839x/config-6.12 b/target/linux/realtek/rtl839x/config-6.12 index 51c0336710e..56ae0bfe77f 100644 --- a/target/linux/realtek/rtl839x/config-6.12 +++ b/target/linux/realtek/rtl839x/config-6.12 @@ -134,6 +134,7 @@ CONFIG_MDIO_DEVRES=y CONFIG_MDIO_I2C=y CONFIG_MDIO_REALTEK_OTTO=y CONFIG_MDIO_REALTEK_OTTO_AUX=y +CONFIG_MDIO_REALTEK_OTTO_SERDES=y CONFIG_MDIO_SMBUS=y CONFIG_MFD_CORE=y CONFIG_MFD_RTL8231=y diff --git a/target/linux/realtek/rtl930x/config-6.12 b/target/linux/realtek/rtl930x/config-6.12 index 82880f3a169..e160b060ac0 100644 --- a/target/linux/realtek/rtl930x/config-6.12 +++ b/target/linux/realtek/rtl930x/config-6.12 @@ -118,6 +118,7 @@ CONFIG_MDIO_DEVRES=y CONFIG_MDIO_I2C=y CONFIG_MDIO_REALTEK_OTTO=y CONFIG_MDIO_REALTEK_OTTO_AUX=y +CONFIG_MDIO_REALTEK_OTTO_SERDES=y CONFIG_MDIO_SMBUS=y CONFIG_MFD_RTL8231=y CONFIG_MFD_SYSCON=y diff --git a/target/linux/realtek/rtl930x_nand/config-6.12 b/target/linux/realtek/rtl930x_nand/config-6.12 index 5ba7b2e613a..075b06f3626 100644 --- a/target/linux/realtek/rtl930x_nand/config-6.12 +++ b/target/linux/realtek/rtl930x_nand/config-6.12 @@ -118,6 +118,7 @@ CONFIG_MDIO_DEVRES=y CONFIG_MDIO_I2C=y CONFIG_MDIO_REALTEK_OTTO=y CONFIG_MDIO_REALTEK_OTTO_AUX=y +CONFIG_MDIO_REALTEK_OTTO_SERDES=y CONFIG_MDIO_SMBUS=y CONFIG_MFD_RTL8231=y CONFIG_MFD_SYSCON=y diff --git a/target/linux/realtek/rtl931x/config-6.12 b/target/linux/realtek/rtl931x/config-6.12 index f45a17fe7e4..9c64303ec2e 100644 --- a/target/linux/realtek/rtl931x/config-6.12 +++ b/target/linux/realtek/rtl931x/config-6.12 @@ -123,6 +123,7 @@ CONFIG_MDIO_DEVRES=y CONFIG_MDIO_I2C=y CONFIG_MDIO_REALTEK_OTTO=y CONFIG_MDIO_REALTEK_OTTO_AUX=y +CONFIG_MDIO_REALTEK_OTTO_SERDES=y CONFIG_MDIO_SMBUS=y CONFIG_MFD_RTL8231=y CONFIG_MFD_SYSCON=y diff --git a/target/linux/realtek/rtl931x_nand/config-6.12 b/target/linux/realtek/rtl931x_nand/config-6.12 index 5bf65a5b8be..25e8b8ff4fc 100644 --- a/target/linux/realtek/rtl931x_nand/config-6.12 +++ b/target/linux/realtek/rtl931x_nand/config-6.12 @@ -123,6 +123,7 @@ CONFIG_MDIO_DEVRES=y CONFIG_MDIO_I2C=y CONFIG_MDIO_REALTEK_OTTO=y CONFIG_MDIO_REALTEK_OTTO_AUX=y +CONFIG_MDIO_REALTEK_OTTO_SERDES=y CONFIG_MDIO_SMBUS=y CONFIG_MFD_RTL8231=y CONFIG_MFD_SYSCON=y -- 2.47.3