// 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 (*get_backing_sds)(struct rtsds_ctrl *ctrl, int sds, int page); 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); }; 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); } #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_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_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->get_backing_sds(ctrl, 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); } static int rtsds_83xx_get_backing_sds(struct rtsds_ctrl *ctrl, int sds, int page) { return sds; } 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; } /* * 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 */ static int rtsds_930x_get_backing_sds(struct rtsds_ctrl *ctrl, int sds, int page) { if (sds == 3 && page < 4) return 10; return sds; } /* * 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(struct rtsds_ctrl *ctrl, int sds, int page) { int map[] = { 0, 1, 2, 3, 6, 7, 10, 11, 14, 15, 18, 19, 22, 23 }; int backsds; /* First two RTL931x SerDes have 1:1 frontend/backend mapping */ if (sds < 2) return sds; backsds = map[sds]; 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_93xx_read(struct rtsds_ctrl *ctrl, int sds, int page, int regnum) { int subpage = RTSDS_SUBPAGE(page); int ret, backsds, value; backsds = ctrl->cfg->get_backing_sds(ctrl, 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->get_backing_sds(ctrl, 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_of_mdiobus_register(dev, bus, dev->of_node); 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\n", 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, .get_backing_sds = rtsds_83xx_get_backing_sds, .read = rtsds_838x_read, .write = rtsds_838x_write, }; static const struct rtsds_config rtsds_839x_cfg = { .sds_cnt = RTSDS_839X_SDS_CNT, .page_cnt = RTSDS_839X_PAGE_CNT, .base = RTSDS_839X_BASE, .get_backing_sds = rtsds_83xx_get_backing_sds, .read = rtsds_839x_read, .write = rtsds_839x_write, }; static const struct rtsds_config rtsds_930x_cfg = { .sds_cnt = RTSDS_930X_SDS_CNT, .page_cnt = RTSDS_930X_PAGE_CNT, .base = RTSDS_930X_BASE, .get_backing_sds = rtsds_930x_get_backing_sds, .read = rtsds_93xx_read, .write = rtsds_93xx_write, }; static const struct rtsds_config rtsds_931x_cfg = { .sds_cnt = RTSDS_931X_SDS_CNT, .page_cnt = RTSDS_931X_PAGE_CNT, .base = RTSDS_931X_BASE, .get_backing_sds = rtsds_931x_get_backing_sds, .read = rtsds_93xx_read, .write = rtsds_93xx_write, }; 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");