diff --git a/target/linux/realtek/files-5.15/drivers/net/dsa/rtl83xx/common.c b/target/linux/realtek/files-5.15/drivers/net/dsa/rtl83xx/common.c index 221428cc77..7ce5f0146e 100644 --- a/target/linux/realtek/files-5.15/drivers/net/dsa/rtl83xx/common.c +++ b/target/linux/realtek/files-5.15/drivers/net/dsa/rtl83xx/common.c @@ -304,14 +304,11 @@ static int __init rtl83xx_mdio_probe(struct rtl838x_switch_priv *priv) */ bus->read = priv->mii_bus->read; bus->write = priv->mii_bus->write; - bus->read_paged = priv->mii_bus->read_paged; - bus->write_paged = priv->mii_bus->write_paged; snprintf(bus->id, MII_BUS_ID_SIZE, "%s-%d", bus->name, dev->id); bus->parent = dev; priv->ds->slave_mii_bus = bus; priv->ds->slave_mii_bus->priv = priv->mii_bus->priv; - priv->ds->slave_mii_bus->access_capabilities = priv->mii_bus->access_capabilities; ret = mdiobus_register(priv->ds->slave_mii_bus); if (ret && mii_np) { diff --git a/target/linux/realtek/files-5.15/drivers/net/dsa/rtl83xx/rtl931x.c b/target/linux/realtek/files-5.15/drivers/net/dsa/rtl83xx/rtl931x.c index 25ad4eaa11..2ba3a7819d 100644 --- a/target/linux/realtek/files-5.15/drivers/net/dsa/rtl83xx/rtl931x.c +++ b/target/linux/realtek/files-5.15/drivers/net/dsa/rtl83xx/rtl931x.c @@ -396,7 +396,7 @@ int rtl931x_read_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 *val) * 0x1: 1G MMD register (MMD via Clause 22 registers 13 and 14) * 0x2: 10G MMD register (MMD via Clause 45) */ - int type = (regnum & MII_ADDR_C45)?2:1; + int type = 2; mutex_lock(&smi_lock); @@ -404,7 +404,7 @@ int rtl931x_read_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 *val) sw_w32(port << 5, RTL931X_SMI_INDRT_ACCESS_BC_PHYID_CTRL); /* Set MMD device number and register to write to */ - sw_w32(devnum << 16 | mdiobus_c45_regad(regnum), RTL931X_SMI_INDRT_ACCESS_MMD_CTRL); + sw_w32(devnum << 16 | regnum, RTL931X_SMI_INDRT_ACCESS_MMD_CTRL); v = type << 2 | BIT(0); /* MMD-access-type | EXEC */ sw_w32(v, RTL931X_SMI_INDRT_ACCESS_CTRL_0); @@ -420,7 +420,7 @@ int rtl931x_read_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 *val) *val = sw_r32(RTL931X_SMI_INDRT_ACCESS_CTRL_3) >> 16; pr_debug("%s: port %d, dev: %x, regnum: %x, val: %x (err %d)\n", __func__, - port, devnum, mdiobus_c45_regad(regnum), *val, err); + port, devnum, regnum, *val, err); mutex_unlock(&smi_lock); @@ -432,7 +432,7 @@ int rtl931x_write_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 val) { int err = 0; u32 v; - int type = (regnum & MII_ADDR_C45)?2:1; + int type = 2; u64 pm; mutex_lock(&smi_lock); @@ -446,7 +446,7 @@ int rtl931x_write_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 val) sw_w32_mask(0xffff, val, RTL931X_SMI_INDRT_ACCESS_CTRL_3); /* Set MMD device number and register to write to */ - sw_w32(devnum << 16 | mdiobus_c45_regad(regnum), RTL931X_SMI_INDRT_ACCESS_MMD_CTRL); + sw_w32(devnum << 16 | regnum, RTL931X_SMI_INDRT_ACCESS_MMD_CTRL); v = BIT(4) | type << 2 | BIT(0); /* WRITE | MMD-access-type | EXEC */ sw_w32(v, RTL931X_SMI_INDRT_ACCESS_CTRL_0); @@ -456,7 +456,7 @@ int rtl931x_write_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 val) } while (v & BIT(0)); pr_debug("%s: port %d, dev: %x, regnum: %x, val: %x (err %d)\n", __func__, - port, devnum, mdiobus_c45_regad(regnum), val, err); + port, devnum, regnum, val, err); mutex_unlock(&smi_lock); return err; diff --git a/target/linux/realtek/files-5.15/drivers/net/ethernet/rtl838x_eth.c b/target/linux/realtek/files-5.15/drivers/net/ethernet/rtl838x_eth.c index 71e7937336..e6c06c4452 100644 --- a/target/linux/realtek/files-5.15/drivers/net/ethernet/rtl838x_eth.c +++ b/target/linux/realtek/files-5.15/drivers/net/ethernet/rtl838x_eth.c @@ -1621,264 +1621,369 @@ static int rtl838x_set_link_ksettings(struct net_device *ndev, return phylink_ethtool_ksettings_set(priv->phylink, cmd); } -static int rtl838x_mdio_read_paged(struct mii_bus *bus, int mii_id, u16 page, int regnum) +/* + * On all RealTek switch platforms the hardware periodically reads the link status of all + * PHYs. This is to some degree programmable, so that one can tell the hardware to read + * specific C22 registers from specific pages, or C45 registers, to determine the current + * link speed, duplex, flow-control, ... + * + * This happens without any need for the driver to do anything at runtime, completely + * invisible and in a parallel hardware thread, independent of the CPU running Linux. + * All one needs to do is to set it up once. Having the MAC link settings automatically + * follow the PHY link status also happens to be the only way to control MAC port status + * in a meaningful way, or at least it's the only way we fully understand, as this is + * what every vendor firmware is doing. + * + * The hardware PHY polling unit doesn't care about bus locking, it just assumes that all + * paged PHY operations are also done via the same hardware unit offering this PHY access + * abstractions. + * + * Additionally at least the RTL838x and RTL839x devices are known to have a so called + * raw mode. Using the special MAX_PAGE-1 with the MDIO controller found in RealTek + * SoCs allows to access the PHY in raw mode, ie. bypassing the cache and paging engine + * of the MDIO controller. E.g. for RTL838x this is 0xfff. + * + * On the other hand Realtek PHYs usually make use of select register 0x1f to switch + * pages. There is no problem to issue separate page and access bus calls to the PHYs + * when they are not attached to an Realtek SoC. The paradigm should be to keep the PHY + * implementation bus independent. + * + * As if this is not enough the PHYs consist of 8 ports that all can be programmed + * individually. Writing to port 0 can configure the whole why while other operations + * need to be replicated per port. + * + * To bring all this together we need a tricky bus design that intercepts select page + * calls but lets raw page accesses through. And especially knows how to handle raw + * accesses to the select register. Additionally we need the possibility to write to + * all 8 ports of the PHY individually. + * + * While the C45 clause stuff is pretty standard the legacy functions basically track + * the accesses and the state of the bus with the attributes page[], raw[] and portaddr + * of the bus_priv structure. The page selection works as follows: + * + * phy_write(phydev, RTL821X_PAGE_SELECT, 12) : store internal page 12 in driver + * phy_write(phydev, 7, 33) : write page=12, reg=7, val=33 + * + * or simply + * + * phy_write_paged(phydev, 12, 7, 33) : write page=12, reg=7, val=33 + * + * The port selection works as follows and must be called under a held mdio bus lock + * + * __mdiobus_write(bus, RTL821X_PORT_SELECT, 4) : switch to port paddr + * __phy_write(phydev, RTL821X_PAGE_SELECT, 11) : store internal page 11 in driver + * __phy_write(phydev, 8, 19) : write page=11, reg=8, val=19, port=4 + * + * Any Realtek PHY that will be connected to this bus must simply provide the standard + * page functions: + * + * define RTL821X_PAGE_SELECT 0x1f + * + * static int rtl821x_read_page(struct phy_device *phydev) + * { + * return __phy_read(phydev, RTL821X_PAGE_SELECT); + * } + * + * static int rtl821x_write_page(struct phy_device *phydev, int page) + * { + * return __phy_write(phydev, RTL821X_PAGE_SELECT, page); + * } + * + * In case there are non Realtek PHYs attached to the logic might need to be + * reimplemented. For now it should be sufficient. + */ + +#define RTL821X_PAGE_SELECT 0x1f +#define RTL821X_PORT_SELECT 0x2000 +#define RTL838X_PAGE_RAW 0xfff +#define RTL839X_PAGE_RAW 0x1fff +#define RTL930X_PAGE_RAW 0xfff +#define RTL931X_PAGE_RAW 0x1fff +#define RTMDIO_READ 0x0 +#define RTMDIO_WRITE 0x1 + +/* + * Provide a generic read/write function so we can access multiple ports on a shared PHY + * package of the bus with separate addresses individually. This basically resembles the + * phy_read_paged() and phy_write_paged() functions. To inform the bus that we are + * workin on a not default port (8, 16, 24, ...) we send a RTL821X_PORT_SELECT command + * at the beginning and the end to switch the port handling logic. + */ + +static int rtmdio_read_page(struct phy_device *phydev) { - u32 val; - int err; - struct rtl838x_eth_priv *priv = bus->priv; + if (WARN_ONCE(!phydev->drv->read_page, + "read_page callback not available, PHY driver not loaded?\n")) + return -EOPNOTSUPP; - if (mii_id >= 24 && mii_id <= 27 && priv->id == 0x8380) - return rtl838x_read_sds_phy(mii_id, regnum); - - if (regnum & (MII_ADDR_C45 | MII_ADDR_C22_MMD)) { - err = rtl838x_read_mmd_phy(mii_id, - mdiobus_c45_devad(regnum), - regnum, &val); - pr_debug("MMD: %d dev %x register %x read %x, err %d\n", mii_id, - mdiobus_c45_devad(regnum), mdiobus_c45_regad(regnum), - val, err); - } else { - pr_debug("PHY: %d register %x read %x, err %d\n", mii_id, regnum, val, err); - err = rtl838x_read_phy(mii_id, page, regnum, &val); - } - if (err) - return err; - - return val; + return phydev->drv->read_page(phydev); } -static int rtl838x_mdio_read(struct mii_bus *bus, int mii_id, int regnum) +static int rtmdio_write_page(struct phy_device *phydev, int page) { - return rtl838x_mdio_read_paged(bus, mii_id, 0, regnum); + if (WARN_ONCE(!phydev->drv->write_page, + "write_page callback not available, PHY driver not loaded?\n")) + return -EOPNOTSUPP; + + return phydev->drv->write_page(phydev, page); } -static int rtl839x_mdio_read_paged(struct mii_bus *bus, int mii_id, u16 page, int regnum) +static int rtmdio_package_rw(struct phy_device *phydev, int op, int port, + int page, u32 regnum, u16 val) { - u32 val; - int err; - struct rtl838x_eth_priv *priv = bus->priv; + int r, ret = 0, oldpage; + struct phy_package_shared *shared = phydev->shared; - if (priv->phy_is_internal[mii_id]) - return rtl839x_read_sds_phy(mii_id, regnum); + if (!shared) + return -EIO; - if (regnum & (MII_ADDR_C45 | MII_ADDR_C22_MMD)) { - err = rtl839x_read_mmd_phy(mii_id, - mdiobus_c45_devad(regnum), - regnum, &val); - pr_debug("MMD: %d dev %x register %x read %x, err %d\n", mii_id, - mdiobus_c45_devad(regnum), mdiobus_c45_regad(regnum), - val, err); - } else { - err = rtl839x_read_phy(mii_id, page, regnum, &val); - pr_debug("PHY: %d register %x read %x, err %d\n", mii_id, regnum, val, err); + /* lock and inform bus about non default addressing */ + phy_lock_mdio_bus(phydev); + __mdiobus_write(phydev->mdio.bus, phydev->mdio.addr, + RTL821X_PORT_SELECT, shared->addr + port); + + oldpage = ret = rtmdio_read_page(phydev); + if (oldpage >= 0 && oldpage != page) { + ret = rtmdio_write_page(phydev, page); + if (ret < 0) + oldpage = ret; } - if (err) - return err; - - return val; -} - -static int rtl839x_mdio_read(struct mii_bus *bus, int mii_id, int regnum) -{ - return rtl839x_mdio_read_paged(bus, mii_id, 0, regnum); -} - -static int rtl930x_mdio_read_paged(struct mii_bus *bus, int mii_id, u16 page, int regnum) -{ - u32 val; - int err; - struct rtl838x_eth_priv *priv = bus->priv; - - if (priv->phy_is_internal[mii_id]) - return rtl930x_read_sds_phy(priv->sds_id[mii_id], page, regnum); - - if (regnum & (MII_ADDR_C45 | MII_ADDR_C22_MMD)) { - err = rtl930x_read_mmd_phy(mii_id, - mdiobus_c45_devad(regnum), - regnum, &val); - pr_debug("MMD: %d dev %x register %x read %x, err %d\n", mii_id, - mdiobus_c45_devad(regnum), mdiobus_c45_regad(regnum), - val, err); - } else { - err = rtl930x_read_phy(mii_id, page, regnum, &val); - pr_debug("PHY: %d register %x read %x, err %d\n", mii_id, regnum, val, err); + if (oldpage >= 0) { + if (op == RTMDIO_WRITE) + ret = __phy_write(phydev, regnum, val); + else + ret = __phy_read(phydev, regnum); } - if (err) - return err; + if (oldpage >= 0) { + r = rtmdio_write_page(phydev, oldpage); + if (ret >= 0 && r < 0) + ret = r; + } else + ret = oldpage; - return val; + /* reset bus to default adressing and unlock it */ + __mdiobus_write(phydev->mdio.bus, phydev->mdio.addr, + RTL821X_PORT_SELECT, 0); + phy_unlock_mdio_bus(phydev); + + return ret; } -static int rtl930x_mdio_read(struct mii_bus *bus, int mii_id, int regnum) +/* + * To make use of the shared package functions provide wrappers that align with kernel + * naming conventions. The package() functions are useful to change settings on the + * package as a whole. The package_port() functions will allow to target the PHYs + * individually. + */ + +int phy_package_port_write_paged(struct phy_device *phydev, int port, int page, u32 regnum, u16 val) { - return rtl930x_mdio_read_paged(bus, mii_id, 0, regnum); + return rtmdio_package_rw(phydev, RTMDIO_WRITE, port, page, regnum, val); } -static int rtl931x_mdio_read_paged(struct mii_bus *bus, int mii_id, u16 page, int regnum) +int phy_package_write_paged(struct phy_device *phydev, int page, u32 regnum, u16 val) { - u32 val; - int err, v; - struct rtl838x_eth_priv *priv = bus->priv; + return rtmdio_package_rw(phydev, RTMDIO_WRITE, 0, page, regnum, val); +} - pr_debug("%s: In here, port %d\n", __func__, mii_id); - if (priv->phy_is_internal[mii_id]) { - v = rtl931x_read_sds_phy(priv->sds_id[mii_id], page, regnum); - if (v < 0) { - err = v; - } else { - err = 0; - val = v; - } - } else { - if (regnum & (MII_ADDR_C45 | MII_ADDR_C22_MMD)) { - err = rtl931x_read_mmd_phy(mii_id, - mdiobus_c45_devad(regnum), - regnum, &val); - pr_debug("MMD: %d dev %x register %x read %x, err %d\n", mii_id, - mdiobus_c45_devad(regnum), mdiobus_c45_regad(regnum), - val, err); - } else { - err = rtl931x_read_phy(mii_id, page, regnum, &val); - pr_debug("PHY: %d register %x read %x, err %d\n", mii_id, regnum, val, err); - } +int phy_package_port_read_paged(struct phy_device *phydev, int port, int page, u32 regnum) +{ + return rtmdio_package_rw(phydev, RTMDIO_READ, port, page, regnum, 0); +} + +int phy_package_read_paged(struct phy_device *phydev, int page, u32 regnum) +{ + return rtmdio_package_rw(phydev, RTMDIO_READ, 0, page, regnum, 0); +} + +/* These are the core functions of our fancy Realtek SoC MDIO bus. */ + +static int rtmdio_read_c45(struct mii_bus *bus, int addr, int devnum, int regnum) +{ + int err, val; + struct rtl838x_bus_priv *bus_priv = bus->priv; + int portaddr = bus_priv->portaddr ? bus_priv->portaddr : addr; + + err = (*bus_priv->read_mmd_phy)(portaddr, devnum, regnum, &val); + pr_debug("rd_MMD(adr=%d, dev=%d, reg=%d) = %d, err = %d\n", + portaddr, devnum, regnum, val, err); + return err ? err : val; +} + +static int rtmdio_83xx_read(struct mii_bus *bus, int addr, int regnum) +{ + int err, val; + struct rtl838x_bus_priv *bus_priv = bus->priv; + struct rtl838x_eth_priv *eth_priv = bus_priv->eth_priv; + int portaddr = bus_priv->portaddr ? bus_priv->portaddr : addr; + + if (portaddr >= 24 && portaddr <= 27 && eth_priv->id == 0x8380) + return rtl838x_read_sds_phy(portaddr, regnum); + + if (eth_priv->family_id == RTL8390_FAMILY_ID && eth_priv->phy_is_internal[portaddr]) + return rtl839x_read_sds_phy(portaddr, regnum); + + if (regnum == RTL821X_PAGE_SELECT && bus_priv->page[portaddr] != RTL838X_PAGE_RAW) + return bus_priv->page[portaddr]; + + bus_priv->raw[portaddr] = (bus_priv->page[portaddr] == RTL838X_PAGE_RAW); + err = (*bus_priv->read_phy)(portaddr, bus_priv->page[portaddr], regnum, &val); + pr_debug("rd_PHY(adr=%d, pag=%d, reg=%d) = %d, err = %d\n", + portaddr, bus_priv->page[portaddr], regnum, val, err); + return err ? err : val; +} + +static int rtmdio_93xx_read(struct mii_bus *bus, int addr, int regnum) +{ + int err, val; + struct rtl838x_bus_priv *bus_priv = bus->priv; + struct rtl838x_eth_priv *eth_priv = bus_priv->eth_priv; + int portaddr = bus_priv->portaddr ? bus_priv->portaddr : addr; + + if (regnum == RTL821X_PAGE_SELECT && bus_priv->page[portaddr] != RTL930X_PAGE_RAW) + return bus_priv->page[portaddr]; + + bus_priv->raw[portaddr] = (bus_priv->page[portaddr] == RTL930X_PAGE_RAW); + if (eth_priv->phy_is_internal[portaddr]) { + if (eth_priv->family_id == RTL9300_FAMILY_ID) + return rtl930x_read_sds_phy(eth_priv->sds_id[portaddr], + bus_priv->page[portaddr], regnum); + else + return rtl931x_read_sds_phy(eth_priv->sds_id[portaddr], + bus_priv->page[portaddr], regnum); } - if (err) - return err; - - return val; + err = (*bus_priv->read_phy)(portaddr, bus_priv->page[portaddr], regnum, &val); + pr_debug("rd_PHY(adr=%d, pag=%d, reg=%d) = %d, err = %d\n", + portaddr, bus_priv->page[portaddr], regnum, val, err); + return err ? err : val; } -static int rtl931x_mdio_read(struct mii_bus *bus, int mii_id, int regnum) +static int rtmdio_write_c45(struct mii_bus *bus, int addr, int devnum, int regnum, u16 val) { - return rtl931x_mdio_read_paged(bus, mii_id, 0, regnum); -} - -static int rtl838x_mdio_write_paged(struct mii_bus *bus, int mii_id, u16 page, - int regnum, u16 value) -{ - u32 offset = 0; - struct rtl838x_eth_priv *priv = bus->priv; int err; + struct rtl838x_bus_priv *bus_priv = bus->priv; + int portaddr = bus_priv->portaddr ? bus_priv->portaddr : addr; - if (mii_id >= 24 && mii_id <= 27 && priv->id == 0x8380) { - if (mii_id == 26) - offset = 0x100; - sw_w32(value, RTL838X_SDS4_FIB_REG0 + offset + (regnum << 2)); + err = (*bus_priv->write_mmd_phy)(portaddr, devnum, regnum, val); + pr_debug("wr_MMD(adr=%d, dev=%d, reg=%d, val=%d) err = %d\n", + portaddr, devnum, regnum, val, err); + return err; +} + +static int rtmdio_83xx_write(struct mii_bus *bus, int addr, int regnum, u16 val) +{ + struct rtl838x_bus_priv *bus_priv = bus->priv; + struct rtl838x_eth_priv *eth_priv = bus_priv->eth_priv; + int portaddr = bus_priv->portaddr ? bus_priv->portaddr : addr; + int err, page = bus_priv->page[portaddr], offset = 0; + + if (regnum == RTL821X_PORT_SELECT) { + bus_priv->portaddr = val; return 0; } - if (regnum & (MII_ADDR_C45 | MII_ADDR_C22_MMD)) { - err = rtl838x_write_mmd_phy(mii_id, mdiobus_c45_devad(regnum), - regnum, value); - pr_debug("MMD: %d dev %x register %x write %x, err %d\n", mii_id, - mdiobus_c45_devad(regnum), mdiobus_c45_regad(regnum), - value, err); - - return err; + if (portaddr >= 24 && portaddr <= 27 && eth_priv->id == 0x8380) { + if (portaddr == 26) + offset = 0x100; + sw_w32(val, RTL838X_SDS4_FIB_REG0 + offset + (regnum << 2)); + return 0; } - err = rtl838x_write_phy(mii_id, page, regnum, value); - pr_debug("PHY: %d register %x write %x, err %d\n", mii_id, regnum, value, err); - return err; -} + if (eth_priv->family_id == RTL8390_FAMILY_ID && eth_priv->phy_is_internal[portaddr]) + return rtl839x_write_sds_phy(portaddr, regnum, val); -static int rtl838x_mdio_write(struct mii_bus *bus, int mii_id, - int regnum, u16 value) -{ - return rtl838x_mdio_write_paged(bus, mii_id, 0, regnum, value); -} - -static int rtl839x_mdio_write_paged(struct mii_bus *bus, int mii_id, u16 page, - int regnum, u16 value) -{ - struct rtl838x_eth_priv *priv = bus->priv; - int err; - - if (priv->phy_is_internal[mii_id]) - return rtl839x_write_sds_phy(mii_id, regnum, value); - - if (regnum & (MII_ADDR_C45 | MII_ADDR_C22_MMD)) { - err = rtl839x_write_mmd_phy(mii_id, mdiobus_c45_devad(regnum), - regnum, value); - pr_debug("MMD: %d dev %x register %x write %x, err %d\n", mii_id, - mdiobus_c45_devad(regnum), mdiobus_c45_regad(regnum), - value, err); + if (regnum == RTL821X_PAGE_SELECT) + bus_priv->page[portaddr] = val; + if (!bus_priv->raw[portaddr] && (regnum != RTL821X_PAGE_SELECT || page == RTL838X_PAGE_RAW)) { + bus_priv->raw[portaddr] = (page == RTL838X_PAGE_RAW); + err = (*bus_priv->write_phy)(portaddr, page, regnum, val); + pr_debug("wr_PHY(adr=%d, pag=%d, reg=%d, val=%d) err = %d\n", + portaddr, page, regnum, val, err); return err; } - err = rtl839x_write_phy(mii_id, page, regnum, value); - pr_debug("PHY: %d register %x write %x, err %d\n", mii_id, regnum, value, err); - - return err; + bus_priv->raw[portaddr] = false; + return 0; } -static int rtl839x_mdio_write(struct mii_bus *bus, int mii_id, - int regnum, u16 value) +static int rtmdio_93xx_write(struct mii_bus *bus, int addr, int regnum, u16 val) { - return rtl839x_mdio_write_paged(bus, mii_id, 0, regnum, value); -} + struct rtl838x_bus_priv *bus_priv = bus->priv; + struct rtl838x_eth_priv *eth_priv = bus_priv->eth_priv; + int portaddr = bus_priv->portaddr ? bus_priv->portaddr : addr; + int err, page = bus_priv->page[portaddr]; -static int rtl930x_mdio_write_paged(struct mii_bus *bus, int mii_id, u16 page, - int regnum, u16 value) -{ - struct rtl838x_eth_priv *priv = bus->priv; - int err; - - if (priv->phy_is_internal[mii_id]) - return rtl930x_write_sds_phy(priv->sds_id[mii_id], page, regnum, value); - - if (regnum & (MII_ADDR_C45 | MII_ADDR_C22_MMD)) - return rtl930x_write_mmd_phy(mii_id, mdiobus_c45_devad(regnum), - regnum, value); - - err = rtl930x_write_phy(mii_id, page, regnum, value); - pr_debug("PHY: %d register %x write %x, err %d\n", mii_id, regnum, value, err); - - return err; -} - -static int rtl930x_mdio_write(struct mii_bus *bus, int mii_id, - int regnum, u16 value) -{ - return rtl930x_mdio_write_paged(bus, mii_id, 0, regnum, value); -} - -static int rtl931x_mdio_write_paged(struct mii_bus *bus, int mii_id, u16 page, - int regnum, u16 value) -{ - struct rtl838x_eth_priv *priv = bus->priv; - int err; - - if (priv->phy_is_internal[mii_id]) - return rtl931x_write_sds_phy(priv->sds_id[mii_id], page, regnum, value); - - if (regnum & (MII_ADDR_C45 | MII_ADDR_C22_MMD)) { - err = rtl931x_write_mmd_phy(mii_id, mdiobus_c45_devad(regnum), - regnum, value); - pr_debug("MMD: %d dev %x register %x write %x, err %d\n", mii_id, - mdiobus_c45_devad(regnum), mdiobus_c45_regad(regnum), - value, err); - - return err; + if (regnum == RTL821X_PORT_SELECT) { + bus_priv->portaddr = val; + return 0; } - err = rtl931x_write_phy(mii_id, page, regnum, value); - pr_debug("PHY: %d register %x write %x, err %d\n", mii_id, regnum, value, err); + if (regnum == RTL821X_PAGE_SELECT) + bus_priv->page[portaddr] = val; - return err; + if (!bus_priv->raw[portaddr] && (regnum != RTL821X_PAGE_SELECT || page == RTL930X_PAGE_RAW)) { + bus_priv->raw[portaddr] = (page == RTL930X_PAGE_RAW); + if (eth_priv->phy_is_internal[portaddr]) { + if (eth_priv->family_id == RTL9300_FAMILY_ID) + return rtl930x_write_sds_phy(eth_priv->sds_id[portaddr], + page, regnum, val); + else + return rtl931x_write_sds_phy(eth_priv->sds_id[portaddr], + page, regnum, val); + } + + err = (*bus_priv->write_phy)(portaddr, page, regnum, val); + pr_debug("wr_PHY(adr=%d, pag=%d, reg=%d, val=%d) err = %d\n", + portaddr, page, regnum, val, err); + } + + bus_priv->raw[portaddr] = false; + return 0; } -static int rtl931x_mdio_write(struct mii_bus *bus, int mii_id, - int regnum, u16 value) +/* These wrappers can be dropped after switch to kernel 6.6 */ + +static int rtmdio_83xx_read_legacy(struct mii_bus *bus, int addr, int regnum) { - return rtl931x_mdio_write_paged(bus, mii_id, 0, regnum, value); + if (regnum & MII_ADDR_C45) + return rtmdio_read_c45(bus, addr, mdiobus_c45_devad(regnum), + mdiobus_c45_regad(regnum)); + else + return rtmdio_83xx_read(bus, addr, regnum); } -static int rtl838x_mdio_reset(struct mii_bus *bus) +static int rtmdio_93xx_read_legacy(struct mii_bus *bus, int addr, int regnum) +{ + if (regnum & MII_ADDR_C45) + return rtmdio_read_c45(bus, addr, mdiobus_c45_devad(regnum), + mdiobus_c45_regad(regnum)); + else + return rtmdio_93xx_read(bus, addr, regnum); +} + +static int rtmdio_83xx_write_legacy(struct mii_bus *bus, int addr, int regnum, u16 val) +{ + if (regnum & MII_ADDR_C45) + return rtmdio_write_c45(bus, addr, mdiobus_c45_devad(regnum), + mdiobus_c45_regad(regnum), val); + else + return rtmdio_83xx_write(bus, addr, regnum, val); +} + +static int rtmdio_93xx_write_legacy(struct mii_bus *bus, int addr, int regnum, u16 val) +{ + if (regnum & MII_ADDR_C45) + return rtmdio_write_c45(bus, addr, mdiobus_c45_devad(regnum), + mdiobus_c45_regad(regnum), val); + else + return rtmdio_93xx_write(bus, addr, regnum, val); +} + +static int rtmdio_838x_reset(struct mii_bus *bus) { pr_debug("%s called\n", __func__); /* Disable MAC polling the PHY so that we can start configuration */ @@ -1891,7 +1996,7 @@ static int rtl838x_mdio_reset(struct mii_bus *bus) return 0; } -static int rtl839x_mdio_reset(struct mii_bus *bus) +static int rtmdio_839x_reset(struct mii_bus *bus) { return 0; @@ -1910,9 +2015,10 @@ static int rtl839x_mdio_reset(struct mii_bus *bus) u8 mac_type_bit[RTL930X_CPU_PORT] = {0, 0, 0, 0, 2, 2, 2, 2, 4, 4, 4, 4, 6, 6, 6, 6, 8, 8, 8, 8, 10, 10, 10, 10, 12, 15, 18, 21}; -static int rtl930x_mdio_reset(struct mii_bus *bus) +static int rtmdio_930x_reset(struct mii_bus *bus) { - struct rtl838x_eth_priv *priv = bus->priv; + struct rtl838x_bus_priv *bus_priv = bus->priv; + struct rtl838x_eth_priv *priv = bus_priv->eth_priv; u32 c45_mask = 0; u32 poll_sel[2]; u32 poll_ctrl = 0; @@ -2014,9 +2120,10 @@ static int rtl930x_mdio_reset(struct mii_bus *bus) return 0; } -static int rtl931x_mdio_reset(struct mii_bus *bus) +static int rtmdio_931x_reset(struct mii_bus *bus) { - struct rtl838x_eth_priv *priv = bus->priv; + struct rtl838x_bus_priv *bus_priv = bus->priv; + struct rtl838x_eth_priv *priv = bus_priv->eth_priv; u32 c45_mask = 0; u32 poll_sel[4]; u32 poll_ctrl = 0; @@ -2112,8 +2219,9 @@ static int rtl931x_chip_init(struct rtl838x_eth_priv *priv) static int rtl838x_mdio_init(struct rtl838x_eth_priv *priv) { struct device_node *mii_np, *dn; + struct rtl838x_bus_priv *bus_priv; u32 pn; - int ret; + int i, ret; pr_debug("%s called\n", __func__); mii_np = of_get_child_by_name(priv->pdev->dev.of_node, "mdio-bus"); @@ -2128,50 +2236,64 @@ static int rtl838x_mdio_init(struct rtl838x_eth_priv *priv) goto err_put_node; } - priv->mii_bus = devm_mdiobus_alloc(&priv->pdev->dev); + priv->mii_bus = devm_mdiobus_alloc_size(&priv->pdev->dev, sizeof(*bus_priv)); if (!priv->mii_bus) { ret = -ENOMEM; goto err_put_node; } + bus_priv = priv->mii_bus->priv; + bus_priv->eth_priv = priv; + for (i = 0; i < 64; i++) { + bus_priv->page[i] = 0; + bus_priv->raw[i] = false; + } + bus_priv->portaddr = 0; + switch(priv->family_id) { case RTL8380_FAMILY_ID: priv->mii_bus->name = "rtl838x-eth-mdio"; - priv->mii_bus->read = rtl838x_mdio_read; - priv->mii_bus->read_paged = rtl838x_mdio_read_paged; - priv->mii_bus->write = rtl838x_mdio_write; - priv->mii_bus->write_paged = rtl838x_mdio_write_paged; - priv->mii_bus->reset = rtl838x_mdio_reset; + priv->mii_bus->read = rtmdio_83xx_read_legacy; + priv->mii_bus->write = rtmdio_83xx_write_legacy; + priv->mii_bus->reset = rtmdio_838x_reset; + bus_priv->read_mmd_phy = rtl838x_read_mmd_phy; + bus_priv->write_mmd_phy = rtl838x_write_mmd_phy; + bus_priv->read_phy = rtl838x_read_phy; + bus_priv->write_phy = rtl838x_write_phy; break; case RTL8390_FAMILY_ID: priv->mii_bus->name = "rtl839x-eth-mdio"; - priv->mii_bus->read = rtl839x_mdio_read; - priv->mii_bus->read_paged = rtl839x_mdio_read_paged; - priv->mii_bus->write = rtl839x_mdio_write; - priv->mii_bus->write_paged = rtl839x_mdio_write_paged; - priv->mii_bus->reset = rtl839x_mdio_reset; + priv->mii_bus->read = rtmdio_83xx_read_legacy; + priv->mii_bus->write = rtmdio_83xx_write_legacy; + priv->mii_bus->reset = rtmdio_839x_reset; + bus_priv->read_mmd_phy = rtl839x_read_mmd_phy; + bus_priv->write_mmd_phy = rtl839x_write_mmd_phy; + bus_priv->read_phy = rtl839x_read_phy; + bus_priv->write_phy = rtl839x_write_phy; break; case RTL9300_FAMILY_ID: priv->mii_bus->name = "rtl930x-eth-mdio"; - priv->mii_bus->read = rtl930x_mdio_read; - priv->mii_bus->read_paged = rtl930x_mdio_read_paged; - priv->mii_bus->write = rtl930x_mdio_write; - priv->mii_bus->write_paged = rtl930x_mdio_write_paged; - priv->mii_bus->reset = rtl930x_mdio_reset; + priv->mii_bus->read = rtmdio_93xx_read_legacy; + priv->mii_bus->write = rtmdio_93xx_write_legacy; + priv->mii_bus->reset = rtmdio_930x_reset; + bus_priv->read_mmd_phy = rtl930x_read_mmd_phy; + bus_priv->write_mmd_phy = rtl930x_write_mmd_phy; + bus_priv->read_phy = rtl930x_read_phy; + bus_priv->write_phy = rtl930x_write_phy; priv->mii_bus->probe_capabilities = MDIOBUS_C22_C45; break; case RTL9310_FAMILY_ID: priv->mii_bus->name = "rtl931x-eth-mdio"; - priv->mii_bus->read = rtl931x_mdio_read; - priv->mii_bus->read_paged = rtl931x_mdio_read_paged; - priv->mii_bus->write = rtl931x_mdio_write; - priv->mii_bus->write_paged = rtl931x_mdio_write_paged; - priv->mii_bus->reset = rtl931x_mdio_reset; + priv->mii_bus->read = rtmdio_93xx_read_legacy; + priv->mii_bus->write = rtmdio_93xx_write_legacy; + priv->mii_bus->reset = rtmdio_931x_reset; + bus_priv->read_mmd_phy = rtl931x_read_mmd_phy; + bus_priv->write_mmd_phy = rtl931x_write_mmd_phy; + bus_priv->read_phy = rtl931x_read_phy; + bus_priv->write_phy = rtl931x_write_phy; priv->mii_bus->probe_capabilities = MDIOBUS_C22_C45; break; } - priv->mii_bus->access_capabilities = MDIOBUS_ACCESS_C22_MMD; - priv->mii_bus->priv = priv; priv->mii_bus->parent = &priv->pdev->dev; for_each_node_by_name(dn, "ethernet-phy") { @@ -2533,6 +2655,7 @@ static int __init rtl838x_eth_probe(struct platform_device *pdev) err = -EINVAL; goto err_free; } + priv->phylink_config.dev = &dev->dev; priv->phylink_config.type = PHYLINK_NETDEV; diff --git a/target/linux/realtek/files-5.15/drivers/net/ethernet/rtl838x_eth.h b/target/linux/realtek/files-5.15/drivers/net/ethernet/rtl838x_eth.h index 47ed286aa4..0f49eae23d 100644 --- a/target/linux/realtek/files-5.15/drivers/net/ethernet/rtl838x_eth.h +++ b/target/linux/realtek/files-5.15/drivers/net/ethernet/rtl838x_eth.h @@ -408,6 +408,17 @@ inline u32 rtl931x_get_mac_tx_pause_sts(int p) struct p_hdr; struct dsa_tag; +struct rtl838x_bus_priv { + struct rtl838x_eth_priv *eth_priv; + int portaddr; + int page[64]; + bool raw[64]; + int (*read_mmd_phy)(u32 port, u32 addr, u32 reg, u32 *val); + 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); +}; + struct rtl838x_eth_reg { irqreturn_t (*net_irq)(int irq, void *dev_id); int (*mac_port_ctrl)(int port); diff --git a/target/linux/realtek/files-5.15/drivers/net/phy/rtl83xx-phy.c b/target/linux/realtek/files-5.15/drivers/net/phy/rtl83xx-phy.c index 490020989f..0c8b1dfd4d 100644 --- a/target/linux/realtek/files-5.15/drivers/net/phy/rtl83xx-phy.c +++ b/target/linux/realtek/files-5.15/drivers/net/phy/rtl83xx-phy.c @@ -20,6 +20,10 @@ extern struct rtl83xx_soc_info soc_info; extern struct mutex smi_lock; +extern int phy_package_port_write_paged(struct phy_device *phydev, int port, int page, u32 regnum, u16 val); +extern int phy_package_write_paged(struct phy_device *phydev, int page, u32 regnum, u16 val); +extern int phy_package_port_read_paged(struct phy_device *phydev, int port, int page, u32 regnum); +extern int phy_package_read_paged(struct phy_device *phydev, int page, u32 regnum); #define PHY_PAGE_2 2 #define PHY_PAGE_4 4 @@ -437,12 +441,12 @@ static int rtl8393_read_status(struct phy_device *phydev) return err; } -static int rtl8226_read_page(struct phy_device *phydev) +static int rtl821x_read_page(struct phy_device *phydev) { return __phy_read(phydev, RTL8XXX_PAGE_SELECT); } -static int rtl8226_write_page(struct phy_device *phydev, int page) +static int rtl821x_write_page(struct phy_device *phydev, int page) { return __phy_write(phydev, RTL8XXX_PAGE_SELECT, page); } @@ -3840,9 +3844,10 @@ static struct phy_driver rtl83xx_phy_driver[] = { PHY_ID_MATCH_MODEL(PHY_ID_RTL8214C), .name = "Realtek RTL8214C", .features = PHY_GBIT_FEATURES, - .flags = PHY_HAS_REALTEK_PAGES, .match_phy_device = rtl8214c_match_phy_device, .probe = rtl8214c_phy_probe, + .read_page = rtl821x_read_page, + .write_page = rtl821x_write_page, .suspend = genphy_suspend, .resume = genphy_resume, .set_loopback = genphy_loopback, @@ -3851,9 +3856,10 @@ static struct phy_driver rtl83xx_phy_driver[] = { PHY_ID_MATCH_MODEL(PHY_ID_RTL8214FC), .name = "Realtek RTL8214FC", .features = PHY_GBIT_FIBRE_FEATURES, - .flags = PHY_HAS_REALTEK_PAGES, .match_phy_device = rtl8214fc_match_phy_device, .probe = rtl8214fc_phy_probe, + .read_page = rtl821x_read_page, + .write_page = rtl821x_write_page, .suspend = rtl8214fc_suspend, .resume = rtl8214fc_resume, .set_loopback = genphy_loopback, @@ -3866,9 +3872,10 @@ static struct phy_driver rtl83xx_phy_driver[] = { PHY_ID_MATCH_MODEL(PHY_ID_RTL8218B_E), .name = "Realtek RTL8218B (external)", .features = PHY_GBIT_FEATURES, - .flags = PHY_HAS_REALTEK_PAGES, .match_phy_device = rtl8218b_ext_match_phy_device, .probe = rtl8218b_ext_phy_probe, + .read_page = rtl821x_read_page, + .write_page = rtl821x_write_page, .suspend = genphy_suspend, .resume = genphy_resume, .set_loopback = genphy_loopback, @@ -3879,8 +3886,9 @@ static struct phy_driver rtl83xx_phy_driver[] = { PHY_ID_MATCH_MODEL(PHY_ID_RTL8218D), .name = "REALTEK RTL8218D", .features = PHY_GBIT_FEATURES, - .flags = PHY_HAS_REALTEK_PAGES, .probe = rtl8218d_phy_probe, + .read_page = rtl821x_read_page, + .write_page = rtl821x_write_page, .suspend = genphy_suspend, .resume = genphy_resume, .set_loopback = genphy_loopback, @@ -3891,12 +3899,11 @@ static struct phy_driver rtl83xx_phy_driver[] = { PHY_ID_MATCH_MODEL(PHY_ID_RTL8221B), .name = "REALTEK RTL8221B", .features = PHY_GBIT_FEATURES, - .flags = PHY_HAS_REALTEK_PAGES, .suspend = genphy_suspend, .resume = genphy_resume, .set_loopback = genphy_loopback, - .read_page = rtl8226_read_page, - .write_page = rtl8226_write_page, + .read_page = rtl821x_read_page, + .write_page = rtl821x_write_page, .read_status = rtl8226_read_status, .config_aneg = rtl8226_config_aneg, .set_eee = rtl8226_set_eee, @@ -3906,12 +3913,11 @@ static struct phy_driver rtl83xx_phy_driver[] = { PHY_ID_MATCH_MODEL(PHY_ID_RTL8226), .name = "REALTEK RTL8226", .features = PHY_GBIT_FEATURES, - .flags = PHY_HAS_REALTEK_PAGES, .suspend = genphy_suspend, .resume = genphy_resume, .set_loopback = genphy_loopback, - .read_page = rtl8226_read_page, - .write_page = rtl8226_write_page, + .read_page = rtl821x_read_page, + .write_page = rtl821x_write_page, .read_status = rtl8226_read_status, .config_aneg = rtl8226_config_aneg, .set_eee = rtl8226_set_eee, @@ -3921,8 +3927,9 @@ static struct phy_driver rtl83xx_phy_driver[] = { PHY_ID_MATCH_MODEL(PHY_ID_RTL8218B_I), .name = "Realtek RTL8218B (internal)", .features = PHY_GBIT_FEATURES, - .flags = PHY_HAS_REALTEK_PAGES, .probe = rtl8218b_int_phy_probe, + .read_page = rtl821x_read_page, + .write_page = rtl821x_write_page, .suspend = genphy_suspend, .resume = genphy_resume, .set_loopback = genphy_loopback, @@ -3933,8 +3940,9 @@ static struct phy_driver rtl83xx_phy_driver[] = { PHY_ID_MATCH_MODEL(PHY_ID_RTL8218B_I), .name = "Realtek RTL8380 SERDES", .features = PHY_GBIT_FIBRE_FEATURES, - .flags = PHY_HAS_REALTEK_PAGES, .probe = rtl838x_serdes_probe, + .read_page = rtl821x_read_page, + .write_page = rtl821x_write_page, .suspend = genphy_suspend, .resume = genphy_resume, .set_loopback = genphy_loopback, @@ -3944,8 +3952,9 @@ static struct phy_driver rtl83xx_phy_driver[] = { PHY_ID_MATCH_MODEL(PHY_ID_RTL8393_I), .name = "Realtek RTL8393 SERDES", .features = PHY_GBIT_FIBRE_FEATURES, - .flags = PHY_HAS_REALTEK_PAGES, .probe = rtl8393_serdes_probe, + .read_page = rtl821x_read_page, + .write_page = rtl821x_write_page, .suspend = genphy_suspend, .resume = genphy_resume, .set_loopback = genphy_loopback, @@ -3955,7 +3964,8 @@ static struct phy_driver rtl83xx_phy_driver[] = { PHY_ID_MATCH_MODEL(PHY_ID_RTL8390_GENERIC), .name = "Realtek RTL8390 Generic", .features = PHY_GBIT_FIBRE_FEATURES, - .flags = PHY_HAS_REALTEK_PAGES, + .read_page = rtl821x_read_page, + .write_page = rtl821x_write_page, .probe = rtl8390_serdes_probe, .suspend = genphy_suspend, .resume = genphy_resume, @@ -3965,7 +3975,8 @@ static struct phy_driver rtl83xx_phy_driver[] = { PHY_ID_MATCH_MODEL(PHY_ID_RTL9300_I), .name = "REALTEK RTL9300 SERDES", .features = PHY_GBIT_FIBRE_FEATURES, - .flags = PHY_HAS_REALTEK_PAGES, + .read_page = rtl821x_read_page, + .write_page = rtl821x_write_page, .probe = rtl9300_serdes_probe, .suspend = genphy_suspend, .resume = genphy_resume, diff --git a/target/linux/realtek/patches-5.15/800-net-mdio-support-hardware-assisted-indirect-access.patch b/target/linux/realtek/patches-5.15/800-net-mdio-support-hardware-assisted-indirect-access.patch deleted file mode 100644 index 188d68b121..0000000000 --- a/target/linux/realtek/patches-5.15/800-net-mdio-support-hardware-assisted-indirect-access.patch +++ /dev/null @@ -1,840 +0,0 @@ -From 5d84f16b0036b33487b94abef15ad3c224c81ee9 Mon Sep 17 00:00:00 2001 -From: Daniel Golle -Date: Thu, 3 Feb 2022 16:38:50 +0000 -Subject: [PATCH] net: mdio: support hardware-assisted indirect access - -MDIO controllers found in Switch-SoCs can offload some MDIO operations -to the hardware: - * MMD register access via Clause-22 - Instead of using multiple operations to access MMD registers via - MII register MII_MMD_CTRL and MII_MMD_DATA some controllers - allow transparent access to MMD PHY registers. - - * paged MII register access - Some PHYs (namely RealTek and Vitesse) use vendor-defined MII - register 0x1f for paged access. Some MDIO host controllers support - transparent paged access when used with such PHYs. - - * add convenience accessors to fully support paged access also on - multi-PHY packages (like the embedded PHYs in RTL83xx): - phy_package_read_paged and phy_package_write_paged - phy_package_port_read and phy_package_port_write - phy_package_port_read_paged and phy_package_port_write_paged - -Signed-off-by: Daniel Golle ---- - drivers/net/phy/mdio_bus.c | 335 ++++++++++++++++++++++++++++++++++++- - drivers/net/phy/phy-core.c | 66 +++++++- - include/linux/mdio.h | 59 +++++++ - include/linux/phy.h | 129 ++++++++++++++ - include/uapi/linux/mii.h | 1 + - 5 files changed, 580 insertions(+), 10 deletions(-) - ---- a/drivers/net/phy/mdio_bus.c -+++ b/drivers/net/phy/mdio_bus.c -@@ -742,6 +742,32 @@ out: - } - - /** -+ * __mdiobus_select_page - Unlocked version of the mdiobus_select_page function -+ * @bus: the mii_bus struct -+ * @addr: the phy address -+ * @page: register page to select -+ * -+ * Selects a MDIO bus register page. Caller must hold the mdio bus lock. -+ * -+ * NOTE: MUST NOT be called from interrupt context. -+ */ -+int __mdiobus_select_page(struct mii_bus *bus, int addr, u16 page) -+{ -+ lockdep_assert_held_once(&bus->mdio_lock); -+ -+ if (bus->selected_page[addr] == page) -+ return 0; -+ -+ bus->selected_page[addr] = page; -+ if (bus->read_paged) -+ return 0; -+ -+ return bus->write(bus, addr, MII_MAINPAGE, page); -+ -+} -+EXPORT_SYMBOL(__mdiobus_select_page); -+ -+/** - * __mdiobus_read - Unlocked version of the mdiobus_read function - * @bus: the mii_bus struct - * @addr: the phy address -@@ -757,7 +783,10 @@ int __mdiobus_read(struct mii_bus *bus, - - lockdep_assert_held_once(&bus->mdio_lock); - -- retval = bus->read(bus, addr, regnum); -+ if (bus->read_paged) -+ retval = bus->read_paged(bus, addr, bus->selected_page[addr], regnum); -+ else -+ retval = bus->read(bus, addr, regnum); - - trace_mdio_access(bus, 1, addr, regnum, retval, retval); - mdiobus_stats_acct(&bus->stats[addr], true, retval); -@@ -767,6 +796,40 @@ int __mdiobus_read(struct mii_bus *bus, - EXPORT_SYMBOL(__mdiobus_read); - - /** -+ * __mdiobus_read_paged - Unlocked version of the mdiobus_read_paged function -+ * @bus: the mii_bus struct -+ * @addr: the phy address -+ * @page: the register page to access -+ * @regnum: register number to read -+ * -+ * Read a MDIO bus register. Caller must hold the mdio bus lock. -+ * -+ * NOTE: MUST NOT be called from interrupt context. -+ */ -+int __mdiobus_read_paged(struct mii_bus *bus, int addr, u16 page, u32 regnum) -+{ -+ int retval; -+ int oldpage; -+ -+ lockdep_assert_held_once(&bus->mdio_lock); -+ -+ if (bus->read_paged) { -+ retval = bus->read_paged(bus, addr, page, regnum); -+ } else { -+ oldpage = bus->selected_page[addr]; -+ __mdiobus_select_page(bus, addr, page); -+ retval = bus->read(bus, addr, regnum); -+ __mdiobus_select_page(bus, addr, oldpage); -+ } -+ -+ trace_mdio_access(bus, 1, addr, regnum, retval, retval); -+ mdiobus_stats_acct(&bus->stats[addr], true, retval); -+ -+ return retval; -+} -+EXPORT_SYMBOL(__mdiobus_read_paged); -+ -+/** - * __mdiobus_write - Unlocked version of the mdiobus_write function - * @bus: the mii_bus struct - * @addr: the phy address -@@ -783,7 +846,10 @@ int __mdiobus_write(struct mii_bus *bus, - - lockdep_assert_held_once(&bus->mdio_lock); - -- err = bus->write(bus, addr, regnum, val); -+ if (bus->write_paged) -+ err = bus->write_paged(bus, addr, bus->selected_page[addr], regnum, val); -+ else -+ err = bus->write(bus, addr, regnum, val); - - trace_mdio_access(bus, 0, addr, regnum, val, err); - mdiobus_stats_acct(&bus->stats[addr], false, err); -@@ -793,6 +859,39 @@ int __mdiobus_write(struct mii_bus *bus, - EXPORT_SYMBOL(__mdiobus_write); - - /** -+ * __mdiobus_write_paged - Unlocked version of the mdiobus_write_paged function -+ * @bus: the mii_bus struct -+ * @addr: the phy address -+ * @page: the register page to access -+ * @regnum: register number to write -+ * @val: value to write to @regnum -+ * -+ * Write a MDIO bus register. Caller must hold the mdio bus lock. -+ * -+ * NOTE: MUST NOT be called from interrupt context. -+ */ -+int __mdiobus_write_paged(struct mii_bus *bus, int addr, u16 page, u32 regnum, u16 val) -+{ -+ int err, oldpage; -+ -+ lockdep_assert_held_once(&bus->mdio_lock); -+ -+ if (bus->write_paged) { -+ err = bus->write_paged(bus, addr, page, regnum, val); -+ } else { -+ oldpage = bus->selected_page[addr]; -+ __mdiobus_select_page(bus, addr, page); -+ err = bus->write(bus, addr, regnum, val); -+ __mdiobus_select_page(bus, addr, oldpage); -+ } -+ trace_mdio_access(bus, 0, addr, regnum, val, err); -+ mdiobus_stats_acct(&bus->stats[addr], false, err); -+ return err; -+} -+EXPORT_SYMBOL(__mdiobus_write_paged); -+ -+ -+/** - * __mdiobus_modify_changed - Unlocked version of the mdiobus_modify function - * @bus: the mii_bus struct - * @addr: the phy address -@@ -825,6 +924,43 @@ int __mdiobus_modify_changed(struct mii_ - EXPORT_SYMBOL_GPL(__mdiobus_modify_changed); - - /** -+ * __mdiobus_modify_changed_paged - Unlocked version of the mdiobus_modify_paged function -+ * @bus: the mii_bus struct -+ * @addr: the phy address -+ * @regnum: register number to modify -+ * @mask: bit mask of bits to clear -+ * @set: bit mask of bits to set -+ * -+ * Read, modify, and if any change, write the register value back to the -+ * device. Any error returns a negative number. -+ * -+ * NOTE: MUST NOT be called from interrupt context. -+ */ -+int __mdiobus_modify_changed_paged(struct mii_bus *bus, int addr, u32 regnum, u16 page, -+ u16 mask, u16 set) -+{ -+ int new, ret, oldpage; -+ -+ oldpage = bus->selected_page[addr]; -+ __mdiobus_select_page(bus, addr, page); -+ -+ ret = __mdiobus_read_paged(bus, addr, page, regnum); -+ if (ret < 0) -+ return ret; -+ -+ new = (ret & ~mask) | set; -+ if (new == ret) -+ return 0; -+ -+ ret = __mdiobus_write_paged(bus, addr, page, regnum, new); -+ -+ __mdiobus_select_page(bus, addr, oldpage); -+ -+ return ret < 0 ? ret : 1; -+} -+EXPORT_SYMBOL_GPL(__mdiobus_modify_changed_paged); -+ -+/** - * mdiobus_read_nested - Nested version of the mdiobus_read function - * @bus: the mii_bus struct - * @addr: the phy address -@@ -850,6 +986,79 @@ int mdiobus_read_nested(struct mii_bus * - EXPORT_SYMBOL(mdiobus_read_nested); - - /** -+ * mdiobus_select_page_nested - Nested version of the mdiobus_select_page function -+ * @bus: the mii_bus struct -+ * @addr: the phy address -+ * @page: register page to access -+ * -+ * In case of nested MDIO bus access avoid lockdep false positives by -+ * using mutex_lock_nested(). -+ * -+ * NOTE: MUST NOT be called from interrupt context, -+ * because the bus read/write functions may wait for an interrupt -+ * to conclude the operation. -+ */ -+int mdiobus_select_page_nested(struct mii_bus *bus, int addr, u16 page) -+{ -+ int retval; -+ -+ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); -+ retval = __mdiobus_select_page(bus, addr, page); -+ mutex_unlock(&bus->mdio_lock); -+ -+ return retval; -+} -+EXPORT_SYMBOL(mdiobus_select_page_nested); -+ -+/** -+ * mdiobus_read_paged_nested - Nested version of the mdiobus_read_paged function -+ * @bus: the mii_bus struct -+ * @addr: the phy address -+ * @page: register page to access -+ * @regnum: register number to read -+ * -+ * In case of nested MDIO bus access avoid lockdep false positives by -+ * using mutex_lock_nested(). -+ * -+ * NOTE: MUST NOT be called from interrupt context, -+ * because the bus read/write functions may wait for an interrupt -+ * to conclude the operation. -+ */ -+int mdiobus_read_paged_nested(struct mii_bus *bus, int addr, u16 page, u32 regnum) -+{ -+ int retval; -+ -+ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); -+ retval = __mdiobus_read_paged(bus, addr, page, regnum); -+ mutex_unlock(&bus->mdio_lock); -+ -+ return retval; -+} -+EXPORT_SYMBOL(mdiobus_read_paged_nested); -+ -+/** -+ * mdiobus_select_page - Convenience function for setting the MII register page -+ * @bus: the mii_bus struct -+ * @addr: the phy address -+ * @page: the register page to set -+ * -+ * NOTE: MUST NOT be called from interrupt context, -+ * because the bus read/write functions may wait for an interrupt -+ * to conclude the operation. -+ */ -+int mdiobus_select_page(struct mii_bus *bus, int addr, u16 page) -+{ -+ int retval; -+ -+ mutex_lock(&bus->mdio_lock); -+ retval = __mdiobus_select_page(bus, addr, page); -+ mutex_unlock(&bus->mdio_lock); -+ -+ return retval; -+} -+EXPORT_SYMBOL(mdiobus_select_page); -+ -+/** - * mdiobus_read - Convenience function for reading a given MII mgmt register - * @bus: the mii_bus struct - * @addr: the phy address -@@ -872,6 +1081,29 @@ int mdiobus_read(struct mii_bus *bus, in - EXPORT_SYMBOL(mdiobus_read); - - /** -+ * mdiobus_read_paged - Convenience function for reading a given paged MII mgmt register -+ * @bus: the mii_bus struct -+ * @addr: the phy address -+ * @page: register page to access -+ * @regnum: register number to read -+ * -+ * NOTE: MUST NOT be called from interrupt context, -+ * because the bus read/write functions may wait for an interrupt -+ * to conclude the operation. -+ */ -+int mdiobus_read_paged(struct mii_bus *bus, int addr, u16 page, u32 regnum) -+{ -+ int retval; -+ -+ mutex_lock(&bus->mdio_lock); -+ retval = __mdiobus_read_paged(bus, addr, page, regnum); -+ mutex_unlock(&bus->mdio_lock); -+ -+ return retval; -+} -+EXPORT_SYMBOL(mdiobus_read_paged); -+ -+/** - * mdiobus_write_nested - Nested version of the mdiobus_write function - * @bus: the mii_bus struct - * @addr: the phy address -@@ -898,6 +1130,33 @@ int mdiobus_write_nested(struct mii_bus - EXPORT_SYMBOL(mdiobus_write_nested); - - /** -+ * mdiobus_write_paged_nested - Nested version of the mdiobus_write_aged function -+ * @bus: the mii_bus struct -+ * @addr: the phy address -+ * @page: the register page to access -+ * @regnum: register number to write -+ * @val: value to write to @regnum -+ * -+ * In case of nested MDIO bus access avoid lockdep false positives by -+ * using mutex_lock_nested(). -+ * -+ * NOTE: MUST NOT be called from interrupt context, -+ * because the bus read/write functions may wait for an interrupt -+ * to conclude the operation. -+ */ -+int mdiobus_write_paged_nested(struct mii_bus *bus, int addr, u16 page, u32 regnum, u16 val) -+{ -+ int err; -+ -+ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); -+ err = __mdiobus_write_paged(bus, addr, page, regnum, val); -+ mutex_unlock(&bus->mdio_lock); -+ -+ return err; -+} -+EXPORT_SYMBOL(mdiobus_write_paged_nested); -+ -+/** - * mdiobus_write - Convenience function for writing a given MII mgmt register - * @bus: the mii_bus struct - * @addr: the phy address -@@ -921,6 +1180,30 @@ int mdiobus_write(struct mii_bus *bus, i - EXPORT_SYMBOL(mdiobus_write); - - /** -+ * mdiobus_write_paged - Convenience function for writing a given paged MII mgmt register -+ * @bus: the mii_bus struct -+ * @addr: the phy address -+ * @page: the register page to access -+ * @regnum: register number to write -+ * @val: value to write to @regnum -+ * -+ * NOTE: MUST NOT be called from interrupt context, -+ * because the bus read/write functions may wait for an interrupt -+ * to conclude the operation. -+ */ -+int mdiobus_write_paged(struct mii_bus *bus, int addr, u16 page, u32 regnum, u16 val) -+{ -+ int err; -+ -+ mutex_lock(&bus->mdio_lock); -+ err = __mdiobus_write_paged(bus, addr, page, regnum, val); -+ mutex_unlock(&bus->mdio_lock); -+ -+ return err; -+} -+EXPORT_SYMBOL(mdiobus_write_paged); -+ -+/** - * mdiobus_modify - Convenience function for modifying a given mdio device - * register - * @bus: the mii_bus struct -@@ -942,6 +1225,51 @@ int mdiobus_modify(struct mii_bus *bus, - EXPORT_SYMBOL_GPL(mdiobus_modify); - - /** -+ * mdiobus_modify_paged - Convenience function for modifying a given mdio device -+ * register -+ * @bus: the mii_bus struct -+ * @addr: the phy address -+ * @page: the register page to access -+ * @regnum: register number to write -+ * @mask: bit mask of bits to clear -+ * @set: bit mask of bits to set -+ */ -+int mdiobus_modify_paged(struct mii_bus *bus, int addr, u16 page, u32 regnum, u16 mask, u16 set) -+{ -+ int err; -+ -+ mutex_lock(&bus->mdio_lock); -+ err = __mdiobus_modify_changed_paged(bus, addr, page, regnum, mask, set); -+ mutex_unlock(&bus->mdio_lock); -+ -+ return err < 0 ? err : 0; -+} -+EXPORT_SYMBOL_GPL(mdiobus_modify_paged); -+ -+/** -+ * mdiobus_modify_changed_paged - Convenience function for modifying a given paged -+ * mdio device register and returning if it changed -+ * @bus: the mii_bus struct -+ * @addr: the phy address -+ * @page: the register page to access -+ * @regnum: register number to write -+ * @mask: bit mask of bits to clear -+ * @set: bit mask of bits to set -+ */ -+int mdiobus_modify_changed_paged(struct mii_bus *bus, int addr, u16 page, u32 regnum, -+ u16 mask, u16 set) -+{ -+ int err; -+ -+ mutex_lock(&bus->mdio_lock); -+ err = __mdiobus_modify_changed_paged(bus, addr, page, regnum, mask, set); -+ mutex_unlock(&bus->mdio_lock); -+ -+ return err; -+} -+EXPORT_SYMBOL_GPL(mdiobus_modify_changed_paged); -+ -+/** - * mdio_bus_match - determine if given MDIO driver supports the given - * MDIO device - * @dev: target MDIO device ---- a/drivers/net/phy/phy-core.c -+++ b/drivers/net/phy/phy-core.c -@@ -557,10 +557,16 @@ int __phy_read_mmd(struct phy_device *ph - struct mii_bus *bus = phydev->mdio.bus; - int phy_addr = phydev->mdio.addr; - -- mmd_phy_indirect(bus, phy_addr, devad, regnum); -- -- /* Read the content of the MMD's selected register */ -- val = __mdiobus_read(bus, phy_addr, MII_MMD_DATA); -+ if (bus->access_capabilities & MDIOBUS_ACCESS_C22_MMD) { -+ val = __mdiobus_c22_mmd_read(phydev->mdio.bus, -+ phydev->mdio.addr, -+ devad, regnum); -+ } else { -+ mmd_phy_indirect(bus, phy_addr, devad, regnum); -+ -+ /* Read the content of the MMD's selected register */ -+ val = __mdiobus_read(bus, phy_addr, MII_MMD_DATA); -+ } - } - return val; - } -@@ -613,12 +619,18 @@ int __phy_write_mmd(struct phy_device *p - struct mii_bus *bus = phydev->mdio.bus; - int phy_addr = phydev->mdio.addr; - -- mmd_phy_indirect(bus, phy_addr, devad, regnum); -+ if (bus->access_capabilities & MDIOBUS_ACCESS_C22_MMD) { -+ ret = __mdiobus_c22_mmd_write(phydev->mdio.bus, -+ phydev->mdio.addr, -+ devad, regnum, val); -+ } else { -+ mmd_phy_indirect(bus, phy_addr, devad, regnum); - -- /* Write the data into MMD's selected register */ -- __mdiobus_write(bus, phy_addr, MII_MMD_DATA, val); -+ /* Write the data into MMD's selected register */ -+ __mdiobus_write(bus, phy_addr, MII_MMD_DATA, val); - -- ret = 0; -+ ret = 0; -+ } - } - return ret; - } -@@ -824,6 +836,13 @@ EXPORT_SYMBOL_GPL(phy_modify_mmd); - - static int __phy_read_page(struct phy_device *phydev) - { -+ if (phydev->drv && phydev->drv->flags & PHY_HAS_REALTEK_PAGES) { -+ struct mii_bus *bus = phydev->mdio.bus; -+ int phy_addr = phydev->mdio.addr; -+ -+ return bus->selected_page[phy_addr]; -+ } -+ - if (WARN_ONCE(!phydev->drv->read_page, "read_page callback not available, PHY driver not loaded?\n")) - return -EOPNOTSUPP; - -@@ -832,6 +851,13 @@ static int __phy_read_page(struct phy_de - - static int __phy_write_page(struct phy_device *phydev, int page) - { -+ if (phydev->drv && phydev->drv->flags & PHY_HAS_REALTEK_PAGES) { -+ struct mii_bus *bus = phydev->mdio.bus; -+ int phy_addr = phydev->mdio.addr; -+ -+ return __mdiobus_select_page(bus, phy_addr, page); -+ } -+ - if (WARN_ONCE(!phydev->drv->write_page, "write_page callback not available, PHY driver not loaded?\n")) - return -EOPNOTSUPP; - -@@ -933,6 +959,18 @@ int phy_read_paged(struct phy_device *ph - { - int ret = 0, oldpage; - -+ if (phydev->drv && phydev->drv->flags & PHY_HAS_REALTEK_PAGES) { -+ struct mii_bus *bus = phydev->mdio.bus; -+ int phy_addr = phydev->mdio.addr; -+ -+ if (bus->read_paged) { -+ phy_lock_mdio_bus(phydev); -+ ret = bus->read_paged(bus, phy_addr, page, regnum); -+ phy_unlock_mdio_bus(phydev); -+ return ret; -+ } -+ } -+ - oldpage = phy_select_page(phydev, page); - if (oldpage >= 0) - ret = __phy_read(phydev, regnum); -@@ -954,6 +992,18 @@ int phy_write_paged(struct phy_device *p - { - int ret = 0, oldpage; - -+ if (phydev->drv && phydev->drv->flags & PHY_HAS_REALTEK_PAGES) { -+ struct mii_bus *bus = phydev->mdio.bus; -+ int phy_addr = phydev->mdio.addr; -+ -+ if (bus->write_paged) { -+ phy_lock_mdio_bus(phydev); -+ ret = bus->write_paged(bus, phy_addr, page, regnum, val); -+ phy_unlock_mdio_bus(phydev); -+ return ret; -+ } -+ } -+ - oldpage = phy_select_page(phydev, page); - if (oldpage >= 0) - ret = __phy_write(phydev, regnum, val); ---- a/include/linux/mdio.h -+++ b/include/linux/mdio.h -@@ -14,6 +14,7 @@ - * IEEE 802.3ae clause 45 addressing mode used by 10GIGE phy chips. - */ - #define MII_ADDR_C45 (1<<30) -+#define MII_ADDR_C22_MMD (1<<29) - #define MII_DEVADDR_C45_SHIFT 16 - #define MII_DEVADDR_C45_MASK GENMASK(20, 16) - #define MII_REGADDR_C45_MASK GENMASK(15, 0) -@@ -340,11 +341,19 @@ static inline void mii_10gbt_stat_mod_li - advertising, lpa & MDIO_AN_10GBT_STAT_LP10G); - } - -+int __mdiobus_select_page(struct mii_bus *bus, int addr, u16 page); - int __mdiobus_read(struct mii_bus *bus, int addr, u32 regnum); - int __mdiobus_write(struct mii_bus *bus, int addr, u32 regnum, u16 val); - int __mdiobus_modify_changed(struct mii_bus *bus, int addr, u32 regnum, - u16 mask, u16 set); - -+int __mdiobus_read_paged(struct mii_bus *bus, int addr, u16 page, u32 regnum); -+int __mdiobus_write_paged(struct mii_bus *bus, int addr, u16 page, u32 regnum, u16 val); -+int __mdiobus_modify_changed_paged(struct mii_bus *bus, int addr, u32 regnum, u16 page, -+ u16 mask, u16 set); -+ -+int mdiobus_select_page(struct mii_bus *bus, int addr, u16 page); -+int mdiobus_select_page_nested(struct mii_bus *bus, int addr, u16 page); - int mdiobus_read(struct mii_bus *bus, int addr, u32 regnum); - int mdiobus_read_nested(struct mii_bus *bus, int addr, u32 regnum); - int mdiobus_write(struct mii_bus *bus, int addr, u32 regnum, u16 val); -@@ -352,11 +361,51 @@ int mdiobus_write_nested(struct mii_bus - int mdiobus_modify(struct mii_bus *bus, int addr, u32 regnum, u16 mask, - u16 set); - -+int mdiobus_read_paged(struct mii_bus *bus, int addr, u16 page, u32 regnum); -+int mdiobus_read_nested_paged(struct mii_bus *bus, int addr, u16 page, u32 regnum); -+int mdiobus_write_paged(struct mii_bus *bus, int addr, u16 page, u32 regnum, u16 val); -+int mdiobus_write_nested_paged(struct mii_bus *bus, int addr, u16 page, u32 regnum, u16 val); -+int mdiobus_modify_paged(struct mii_bus *bus, int addr, u16 page, u32 regnum, u16 mask, -+ u16 set); -+int mdiobus_modify_changed_paged(struct mii_bus *bus, int addr, u16 page, u32 regnum, -+ u16 mask, u16 set); -+ -+static inline int mdiodev_read_paged(struct mdio_device *mdiodev, u16 page, -+ u32 regnum) -+{ -+ return mdiobus_read_paged(mdiodev->bus, mdiodev->addr, page, regnum); -+} -+ -+static inline int mdiodev_write_paged(struct mdio_device *mdiodev, u16 page, -+ u32 regnum, u16 val) -+{ -+ return mdiobus_write_paged(mdiodev->bus, mdiodev->addr, page, regnum, val); -+} -+ -+static inline int mdiodev_modify_paged(struct mdio_device *mdiodev, u16 page, -+ u32 regnum, u16 mask, u16 set) -+{ -+ return mdiobus_modify_paged(mdiodev->bus, mdiodev->addr, page, regnum, -+ mask, set); -+} -+ -+static inline int mdiodev_modify_changed_paged(struct mdio_device *mdiodev, u16 page, -+ u32 regnum, u16 mask, u16 set) -+{ -+ return mdiobus_modify_changed_paged(mdiodev->bus, mdiodev->addr, page, regnum, -+ mask, set); -+} -+ - static inline u32 mdiobus_c45_addr(int devad, u16 regnum) - { - return MII_ADDR_C45 | devad << MII_DEVADDR_C45_SHIFT | regnum; - } - -+static inline u32 mdiobus_c22_mmd_addr(int devad, u16 regnum) -+{ -+ return MII_ADDR_C22_MMD | devad << MII_DEVADDR_C45_SHIFT | regnum; -+} -+ - static inline u16 mdiobus_c45_regad(u32 regnum) - { - return FIELD_GET(MII_REGADDR_C45_MASK, regnum); -@@ -380,6 +429,19 @@ static inline int __mdiobus_c45_write(st - val); - } - -+static inline int __mdiobus_c22_mmd_read(struct mii_bus *bus, int prtad, -+ int devad, u16 regnum) -+{ -+ return __mdiobus_read(bus, prtad, mdiobus_c22_mmd_addr(devad, regnum)); -+} -+ -+static inline int __mdiobus_c22_mmd_write(struct mii_bus *bus, int prtad, -+ int devad, u16 regnum, u16 val) -+{ -+ return __mdiobus_write(bus, prtad, mdiobus_c22_mmd_addr(devad, regnum), -+ val); -+} -+ - static inline int mdiobus_c45_read(struct mii_bus *bus, int prtad, int devad, - u16 regnum) - { ---- a/include/linux/phy.h -+++ b/include/linux/phy.h -@@ -81,6 +81,7 @@ extern const int phy_10gbit_features_arr - #define PHY_IS_INTERNAL 0x00000001 - #define PHY_RST_AFTER_CLK_EN 0x00000002 - #define PHY_POLL_CABLE_TEST 0x00000004 -+#define PHY_HAS_REALTEK_PAGES 0x00000010 - #define MDIO_DEVICE_IS_PHY 0x80000000 - - /** -@@ -428,6 +429,22 @@ struct mii_bus { - - /** @shared: shared state across different PHYs */ - struct phy_package_shared *shared[PHY_MAX_ADDR]; -+ -+ /** @access_capabilities: hardware-assisted access capabilties */ -+ enum { -+ MDIOBUS_ACCESS_SOFTWARE_ONLY = 0, -+ MDIOBUS_ACCESS_C22_MMD = 0x1, -+ } access_capabilities; -+ -+ /** @read: Perform a read transfer on the bus, offloading page access */ -+ int (*read_paged)(struct mii_bus *bus, int addr, u16 page, int regnum); -+ /** @write: Perform a write transfer on the bus, offloading page access */ -+ int (*write_paged)(struct mii_bus *bus, int addr, u16 page, int regnum, u16 val); -+ /** currently selected page when page access is offloaded -+ * array should be PHY_MAX_ADDR+1size, but current design of the MDIO driver -+ * uses port addresses as phy addresses and they are up to 6 bit. -+ */ -+ u16 selected_page[64]; - }; - #define to_mii_bus(d) container_of(d, struct mii_bus, dev) - -@@ -1825,6 +1842,66 @@ static inline int __phy_package_read(str - return __mdiobus_read(phydev->mdio.bus, shared->addr, regnum); - } - -+static inline int phy_package_read_port(struct phy_device *phydev, u16 port, u32 regnum) -+{ -+ struct phy_package_shared *shared = phydev->shared; -+ -+ if (!shared) -+ return -EIO; -+ -+ return mdiobus_read(phydev->mdio.bus, shared->addr + port, regnum); -+} -+ -+static inline int __phy_package_read_port(struct phy_device *phydev, u16 port, u32 regnum) -+{ -+ struct phy_package_shared *shared = phydev->shared; -+ -+ if (!shared) -+ return -EIO; -+ -+ return __mdiobus_read(phydev->mdio.bus, shared->addr + port, regnum); -+} -+ -+static inline int phy_package_read_paged(struct phy_device *phydev, u16 page, u32 regnum) -+{ -+ struct phy_package_shared *shared = phydev->shared; -+ -+ if (!shared) -+ return -EIO; -+ -+ return mdiobus_read_paged(phydev->mdio.bus, shared->addr, page, regnum); -+} -+ -+static inline int __phy_package_read_paged(struct phy_device *phydev, u16 page, u32 regnum) -+{ -+ struct phy_package_shared *shared = phydev->shared; -+ -+ if (!shared) -+ return -EIO; -+ -+ return __mdiobus_read_paged(phydev->mdio.bus, shared->addr, page, regnum); -+} -+ -+static inline int phy_package_port_read_paged(struct phy_device *phydev, u16 port, u16 page, u32 regnum) -+{ -+ struct phy_package_shared *shared = phydev->shared; -+ -+ if (!shared) -+ return -EIO; -+ -+ return mdiobus_read_paged(phydev->mdio.bus, shared->addr + port, page, regnum); -+} -+ -+static inline int __phy_package_port_read_paged(struct phy_device *phydev, u16 port, u16 page, u32 regnum) -+{ -+ struct phy_package_shared *shared = phydev->shared; -+ -+ if (!shared) -+ return -EIO; -+ -+ return __mdiobus_read_paged(phydev->mdio.bus, shared->addr + port, page, regnum); -+} -+ - static inline int phy_package_write(struct phy_device *phydev, - u32 regnum, u16 val) - { -@@ -1847,6 +1924,72 @@ static inline int __phy_package_write(st - return __mdiobus_write(phydev->mdio.bus, shared->addr, regnum, val); - } - -+static inline int phy_package_port_write(struct phy_device *phydev, -+ u16 port, u32 regnum, u16 val) -+{ -+ struct phy_package_shared *shared = phydev->shared; -+ -+ if (!shared) -+ return -EIO; -+ -+ return mdiobus_write(phydev->mdio.bus, shared->addr + port, regnum, val); -+} -+ -+static inline int __phy_package_port_write(struct phy_device *phydev, -+ u16 port, u32 regnum, u16 val) -+{ -+ struct phy_package_shared *shared = phydev->shared; -+ -+ if (!shared) -+ return -EIO; -+ -+ return __mdiobus_write(phydev->mdio.bus, shared->addr + port, regnum, val); -+} -+ -+static inline int phy_package_port_write_paged(struct phy_device *phydev, -+ u16 port, u16 page, u32 regnum, u16 val) -+{ -+ struct phy_package_shared *shared = phydev->shared; -+ -+ if (!shared) -+ return -EIO; -+ -+ return mdiobus_write_paged(phydev->mdio.bus, shared->addr + port, page, regnum, val); -+} -+ -+static inline int __phy_package_port_write_paged(struct phy_device *phydev, -+ u16 port, u16 page, u32 regnum, u16 val) -+{ -+ struct phy_package_shared *shared = phydev->shared; -+ -+ if (!shared) -+ return -EIO; -+ -+ return __mdiobus_write_paged(phydev->mdio.bus, shared->addr + port, page, regnum, val); -+} -+ -+static inline int phy_package_write_paged(struct phy_device *phydev, -+ u16 page, u32 regnum, u16 val) -+{ -+ struct phy_package_shared *shared = phydev->shared; -+ -+ if (!shared) -+ return -EIO; -+ -+ return mdiobus_write_paged(phydev->mdio.bus, shared->addr, page, regnum, val); -+} -+ -+static inline int __phy_package_write_paged(struct phy_device *phydev, -+ u16 page, u32 regnum, u16 val) -+{ -+ struct phy_package_shared *shared = phydev->shared; -+ -+ if (!shared) -+ return -EIO; -+ -+ return __mdiobus_write_paged(phydev->mdio.bus, shared->addr, page, regnum, val); -+} -+ - static inline bool __phy_package_set_once(struct phy_device *phydev, - unsigned int b) - { ---- a/include/uapi/linux/mii.h -+++ b/include/uapi/linux/mii.h -@@ -36,6 +36,7 @@ - #define MII_RESV2 0x1a /* Reserved... */ - #define MII_TPISTATUS 0x1b /* TPI status for 10mbps */ - #define MII_NCONFIG 0x1c /* Network interface config */ -+#define MII_MAINPAGE 0x1f /* Page register */ - - /* Basic mode control register. */ - #define BMCR_RESV 0x003f /* Unused... */