From: Markus Stockhausen Date: Wed, 30 Jul 2025 10:41:28 +0000 (-0400) Subject: realtek: mdio: RTL838x: create new SerDes phy register layout X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=HEAD;p=thirdparty%2Fopenwrt.git realtek: mdio: RTL838x: create new SerDes phy register layout When a SerDes directly drives a SFP module there is no clear rule of how to handle its register set that consists of two parts: - c22 phy registers 0-15 live in the fiber page (2) of the SerDes - other SerDes specific registers exist in pages before and after The mdio bus and other SerDes functions are a wild mix of directly looking into page 2 or just using self defined methods to access data. Provide a consistent phy interface that mixes the SerDes register set like classic Realtek phys do it. - Use register 31 as page select (already in the bus) - Always keep the common registers 0-15 in place and read fiber page - Map the SerDes internal registers into the upper vendor specific registers 16-23 according to the page select register (31). That gives a register mapping as follows: +-----------------------+-----------------------+---------------+-------------+ | reg 0x00-0x0f | reg 0x10-0x17 | reg 0x18-0x1e | reg 0x1f | +-----------------------+-----------------------+---------------+-------------+ | SerDes fiber page (3) | real SerDes registers | zero | SerDes page | | registers 0 - 15 | in packages of 8 | | select reg | +-----------------------+-----------------------+---------------+-------------+ Example to make it as clear as possible. SerDes registers on a RTL838x show Page / Reg | 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B ... -------------+---------------------------------------------------------------- 0 - SDS | 0C03 0F00 7060 7106 074D 0EBF 0F0F 0359 5248 0000 0F80 0000 ... 1 - SDS_EXT | 0000 0000 85FA 8C6D 5CCC 0000 20D8 0003 79AA 8C64 00C3 1482 ... 2 - FIB | 1140 6189 001C CA40 01A0 0000 0000 0004 0000 0000 0000 0000 ... 3 - FIB_EXT | 1140 6109 001C CA40 01A0 0000 0000 0004 0000 0000 0000 0000 ... This translates to this phy layout | SerDes fiber registers normal SerDes registers zero p.sel Page / Reg | 0x00 0x01 0x02 0x03 ... 0x10 0x11 0x12 0x13 ... 0x18 ... 0x1f -------------+--------------------------------------------------------------- 0 | 1140 6189 001C CA40 ... 0C03 0F00 7060 7106 ... 0000 ... 0000 1 | 1140 6189 001C CA40 ... 5248 0000 0F80 0000 ... 0000 ... 0001 ... 4 | 1140 6189 001C CA40 ... 0000 0000 85FA 8C6D ... 0000 ... 0004 For now just do it for RTL838x devices. Signed-off-by: Markus Stockhausen Link: https://github.com/openwrt/openwrt/pull/19604 Signed-off-by: Robert Marko --- diff --git a/target/linux/realtek/files-6.12/drivers/net/ethernet/rtl838x_eth.c b/target/linux/realtek/files-6.12/drivers/net/ethernet/rtl838x_eth.c index 5c6d79d19b5..c6e03ba571f 100644 --- a/target/linux/realtek/files-6.12/drivers/net/ethernet/rtl838x_eth.c +++ b/target/linux/realtek/files-6.12/drivers/net/ethernet/rtl838x_eth.c @@ -83,6 +83,8 @@ extern int rtl931x_write_sds_phy(int phy_addr, int page, int phy_reg, u16 v); #define RTMDIO_ABS BIT(2) #define RTMDIO_PKG BIT(3) +#define RTMDIO_838X_BASE (0xe780) + struct p_hdr { uint8_t *buf; uint16_t reserved; @@ -1647,7 +1649,7 @@ struct rtmdio_bus_priv { bool raw[RTMDIO_MAX_PORT]; int smi_bus[RTMDIO_MAX_PORT]; u8 smi_addr[RTMDIO_MAX_PORT]; - u32 sds_id[RTMDIO_MAX_PORT]; + int sds_id[RTMDIO_MAX_PORT]; bool smi_bus_isc45[RTMDIO_MAX_SMI_BUS]; bool phy_is_internal[RTMDIO_MAX_PORT]; phy_interface_t interfaces[RTMDIO_MAX_PORT]; @@ -1655,6 +1657,8 @@ struct rtmdio_bus_priv { int (*write_mmd_phy)(u32 port, u32 addr, u32 reg, u32 val); int (*read_phy)(u32 port, u32 page, u32 reg, u32 *val); int (*write_phy)(u32 port, u32 page, u32 reg, u32 val); + int (*read_sds_phy)(int sds, int page, int regnum); + int (*write_sds_phy)(int sds, int page, int regnum, u16 val); }; /* @@ -1750,18 +1754,43 @@ int phy_port_read_paged(struct phy_device *phydev, int port, int page, u32 regnu /* SerDes reader/writer functions for the ports without external phy. */ -static int rtmdio_838x_read_sds(int addr, int regnum) +/* + * 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 rtmdio_838x_reg_offset(int sds, int page, int regnum) { - int offset = addr == 26 ? 0x100 : 0x0; + if (sds < 0 || sds > 5) + return -EINVAL; + + if (page == 0 || page == 3) + return (sds << 9) + (page << 7) + (regnum << 2); + else if (page == 1 || page == 2) + return 0xb80 + (sds << 8) + (page << 7) + (regnum << 2); + + return -EINVAL; +} + +static int rtmdio_838x_read_sds_phy(int sds, int page, int regnum) +{ + int offset = rtmdio_838x_reg_offset(sds, page, regnum); + + if (offset < 0) + return offset; - return sw_r32(RTL838X_SDS4_FIB_REG0 + offset + (regnum << 2)) & 0xffff; + return sw_r32(RTMDIO_838X_BASE + offset) & GENMASK(15, 0); } -static int rtmdio_838x_write_sds(int addr, int regnum, u16 val) +static int rtmdio_838x_write_sds_phy(int sds, int page, int regnum, u16 val) { - int offset = addr == 26 ? 0x100 : 0x0; + int offset = rtmdio_838x_reg_offset(sds, page, regnum); - sw_w32(val, RTL838X_SDS4_FIB_REG0 + offset + (regnum << 2)); + if (offset < 0) + return offset; + + sw_w32(val, RTMDIO_838X_BASE + offset); return 0; } @@ -1950,6 +1979,58 @@ static int rtmdio_read_c45(struct mii_bus *bus, int addr, int devnum, int regnum return err ? err : val; } +static int rtmdio_map_sds_register(int page, int regnum, int *sds_page, int *sds_regnum) +{ + /* + * For the SerDes PHY simulate a register mapping like common RealTek PHYs do. Always + * keep the common registers 0x00-0x0f in place and map the SerDes registers into the + * upper vendor specific registers 0x10-0x17 according to the page select register + * (0x1f). That gives a register mapping as follows: + * + * +-----------------------+-----------------------+---------------+-----------------+ + * | reg 0x00-0x0f | reg 0x10-0x17 | reg 0x18-0x1e | reg 0x1f | + * +-----------------------+-----------------------+---------------+-----------------+ + * | SerDes fiber page (2) | real SerDes registers | zero | SerDes page | + * | registers 0x00-0x0f | in packages of 8 | | select register | + * +-----------------------+-----------------------+---------------+-----------------+ + */ + + if (regnum < 16) { + *sds_page = 2; + *sds_regnum = regnum; + } else if (regnum < 24) { + *sds_page = page / 4; + *sds_regnum = 8 * (page % 4) + (regnum - 16); + } else + return 0; + + return 1; +} + +static int rtmdio_read_sds_phy(struct rtmdio_bus_priv *priv, int sds, int page, int regnum) +{ + int ret, sds_page, sds_regnum; + + ret = rtmdio_map_sds_register(page, regnum, &sds_page, &sds_regnum); + if (ret) + ret = priv->read_sds_phy(sds, sds_page, sds_regnum); + pr_debug("rd_SDS(sds=%d, pag=%d, reg=%d) = %d\n", sds, page, regnum, ret); + + return ret; +} + +static int rtmdio_write_sds_phy(struct rtmdio_bus_priv *priv, int sds, int page, int regnum, u16 val) +{ + int ret, sds_page, sds_regnum; + + ret = rtmdio_map_sds_register(page, regnum, &sds_page, &sds_regnum); + if (ret) + ret = priv->write_sds_phy(sds, sds_page, sds_regnum, val); + pr_debug("wr_SDS(sds=%d, pag=%d, reg=%d, val=%d) err = %d\n", sds, page, regnum, val, ret); + + return ret; +} + static int rtmdio_83xx_read(struct mii_bus *bus, int addr, int regnum) { struct rtmdio_bus_priv *priv = bus->priv; @@ -1961,16 +2042,18 @@ static int rtmdio_83xx_read(struct mii_bus *bus, int addr, int regnum) if (addr >= priv->cpu_port) return -ENODEV; - if (addr >= 24 && addr <= 27 && priv->id == 0x8380) - return rtmdio_838x_read_sds(addr, regnum); - - if (priv->family_id == RTL8390_FAMILY_ID && priv->phy_is_internal[addr]) - return rtl839x_read_sds_phy(addr, regnum); - if (regnum == RTMDIO_PAGE_SELECT && priv->page[addr] != priv->rawpage) return priv->page[addr]; priv->raw[addr] = (priv->page[addr] == priv->rawpage); + if ((priv->phy_is_internal[addr]) && (priv->sds_id[addr] >=0)) { + if (priv->family_id == RTL8380_FAMILY_ID) + return rtmdio_read_sds_phy(priv, priv->sds_id[addr], + priv->page[addr], regnum); + else + return rtl839x_read_sds_phy(addr, regnum); + } + err = (*priv->read_phy)(addr, priv->page[addr], regnum, &val); pr_debug("rd_PHY(adr=%d, pag=%d, reg=%d) = %d, err = %d\n", addr, priv->page[addr], regnum, val, err); @@ -2042,17 +2125,19 @@ static int rtmdio_83xx_write(struct mii_bus *bus, int addr, int regnum, u16 val) page = priv->page[addr]; - if (addr >= 24 && addr <= 27 && priv->id == 0x8380) - return rtmdio_838x_write_sds(addr, regnum, val); - - if (priv->family_id == RTL8390_FAMILY_ID && priv->phy_is_internal[addr]) - return rtl839x_write_sds_phy(addr, regnum, val); - if (regnum == RTMDIO_PAGE_SELECT) priv->page[addr] = val; if (!priv->raw[addr] && (regnum != RTMDIO_PAGE_SELECT || page == priv->rawpage)) { priv->raw[addr] = (page == priv->rawpage); + if (priv->phy_is_internal[addr] && priv->sds_id[addr] >=0 ) { + if (priv->family_id == RTL8380_FAMILY_ID) + return rtmdio_write_sds_phy(priv, priv->sds_id[addr], + priv->page[addr], regnum, val); + else + return rtl839x_write_sds_phy(addr, regnum, val); + } + err = (*priv->write_phy)(addr, page, regnum, val); pr_debug("wr_PHY(adr=%d, pag=%d, reg=%d, val=%d) err = %d\n", addr, page, regnum, val, err); @@ -2377,6 +2462,8 @@ static int rtl838x_mdio_init(struct rtl838x_eth_priv *priv) priv->mii_bus->read = rtmdio_83xx_read; priv->mii_bus->write = rtmdio_83xx_write; priv->mii_bus->reset = rtmdio_838x_reset; + bus_priv->read_sds_phy = rtmdio_838x_read_sds_phy; + bus_priv->write_sds_phy = rtmdio_838x_write_sds_phy; bus_priv->read_mmd_phy = rtmdio_838x_read_mmd_phy; bus_priv->write_mmd_phy = rtmdio_838x_write_mmd_phy; bus_priv->read_phy = rtmdio_838x_read_phy;