*/
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) {
* 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);
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);
*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);
{
int err = 0;
u32 v;
- int type = (regnum & MII_ADDR_C45)?2:1;
+ int type = 2;
u64 pm;
mutex_lock(&smi_lock);
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);
} 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;
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 (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;
+ if (WARN_ONCE(!phydev->drv->read_page,
+ "read_page callback not available, PHY driver not loaded?\n"))
+ return -EOPNOTSUPP;
- 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;
-
- if (priv->phy_is_internal[mii_id])
- return rtl839x_read_sds_phy(mii_id, regnum);
-
- 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);
+ int r, ret = 0, oldpage;
+ struct phy_package_shared *shared = phydev->shared;
+
+ if (!shared)
+ return -EIO;
+
+ /* 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;
+ if (oldpage >= 0) {
+ if (op == RTMDIO_WRITE)
+ ret = __phy_write(phydev, regnum, val);
+ else
+ ret = __phy_read(phydev, regnum);
+ }
+
+ if (oldpage >= 0) {
+ r = rtmdio_write_page(phydev, oldpage);
+ if (ret >= 0 && r < 0)
+ ret = r;
+ } else
+ ret = oldpage;
+
+ /* 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 val;
+ return ret;
}
-static int rtl839x_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 rtl839x_mdio_read_paged(bus, mii_id, 0, regnum);
+ return rtmdio_package_rw(phydev, RTMDIO_WRITE, port, page, regnum, val);
}
-static int rtl930x_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;
- 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 (err)
- return err;
-
- return val;
+ return rtmdio_package_rw(phydev, RTMDIO_WRITE, 0, page, regnum, val);
}
-static int rtl930x_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
+int phy_package_port_read_paged(struct phy_device *phydev, int port, int page, u32 regnum)
{
- return rtl930x_mdio_read_paged(bus, mii_id, 0, regnum);
+ return rtmdio_package_rw(phydev, RTMDIO_READ, port, page, regnum, 0);
}
-static int rtl931x_mdio_read_paged(struct mii_bus *bus, int mii_id, u16 page, int regnum)
+int phy_package_read_paged(struct phy_device *phydev, int page, u32 regnum)
{
- u32 val;
- int err, v;
- struct rtl838x_eth_priv *priv = bus->priv;
+ return rtmdio_package_rw(phydev, RTMDIO_READ, 0, page, regnum, 0);
+}
- 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);
- }
- }
+/* These are the core functions of our fancy Realtek SoC MDIO bus. */
- if (err)
- return err;
+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;
- return val;
+ 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 rtl931x_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
+static int rtmdio_83xx_read(struct mii_bus *bus, int addr, int regnum)
{
- return rtl931x_mdio_read_paged(bus, mii_id, 0, 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 rtl838x_mdio_write_paged(struct mii_bus *bus, int mii_id, u16 page,
- int regnum, u16 value)
+static int rtmdio_93xx_read(struct mii_bus *bus, int addr, int regnum)
{
- u32 offset = 0;
- struct rtl838x_eth_priv *priv = bus->priv;
- int err;
-
- 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));
- return 0;
- }
+ 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 & (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);
+ if (regnum == RTL821X_PAGE_SELECT && bus_priv->page[portaddr] != RTL930X_PAGE_RAW)
+ return bus_priv->page[portaddr];
- return err;
+ 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);
}
- 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;
+ 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 rtl838x_mdio_write(struct mii_bus *bus, int mii_id,
- int regnum, u16 value)
+static int rtmdio_write_c45(struct mii_bus *bus, int addr, int devnum, int regnum, u16 val)
{
- return rtl838x_mdio_write_paged(bus, mii_id, 0, regnum, value);
+ int err;
+ struct rtl838x_bus_priv *bus_priv = bus->priv;
+ int portaddr = bus_priv->portaddr ? bus_priv->portaddr : addr;
+
+ 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 rtl839x_mdio_write_paged(struct mii_bus *bus, int mii_id, u16 page,
- int regnum, u16 value)
+static int rtmdio_83xx_write(struct mii_bus *bus, int addr, int regnum, u16 val)
{
- struct rtl838x_eth_priv *priv = bus->priv;
- int err;
+ 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 (priv->phy_is_internal[mii_id])
- return rtl839x_write_sds_phy(mii_id, regnum, value);
+ 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;
+ }
- 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 (eth_priv->family_id == RTL8390_FAMILY_ID && eth_priv->phy_is_internal[portaddr])
+ return rtl839x_write_sds_phy(portaddr, regnum, val);
+ 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 (regnum == RTL821X_PORT_SELECT) {
+ bus_priv->portaddr = val;
+ return 0;
+ }
- if (priv->phy_is_internal[mii_id])
- return rtl930x_write_sds_phy(priv->sds_id[mii_id], page, regnum, value);
+ if (regnum == RTL821X_PAGE_SELECT)
+ bus_priv->page[portaddr] = val;
- if (regnum & (MII_ADDR_C45 | MII_ADDR_C22_MMD))
- return rtl930x_write_mmd_phy(mii_id, mdiobus_c45_devad(regnum),
- regnum, value);
+ 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 = rtl930x_write_phy(mii_id, page, regnum, value);
- pr_debug("PHY: %d register %x write %x, err %d\n", mii_id, regnum, value, err);
+ 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;
+ bus_priv->raw[portaddr] = false;
+ return 0;
}
-static int rtl930x_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 rtl930x_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 rtl931x_mdio_write_paged(struct mii_bus *bus, int mii_id, u16 page,
- int regnum, u16 value)
+static int rtmdio_93xx_read_legacy(struct mii_bus *bus, int addr, int regnum)
{
- 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;
- }
-
- 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 & 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);
+}
- return err;
+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 rtl931x_mdio_write(struct mii_bus *bus, int mii_id,
- int regnum, u16 value)
+static int rtmdio_93xx_write_legacy(struct mii_bus *bus, int addr, int regnum, u16 val)
{
- return rtl931x_mdio_write_paged(bus, mii_id, 0, regnum, value);
+ 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 rtl838x_mdio_reset(struct mii_bus *bus)
+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 */
return 0;
}
-static int rtl839x_mdio_reset(struct mii_bus *bus)
+static int rtmdio_839x_reset(struct mii_bus *bus)
{
return 0;
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;
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;
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");
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") {
err = -EINVAL;
goto err_free;
}
+
priv->phylink_config.dev = &dev->dev;
priv->phylink_config.type = PHYLINK_NETDEV;
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);
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
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);
}
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
+++ /dev/null
-From 5d84f16b0036b33487b94abef15ad3c224c81ee9 Mon Sep 17 00:00:00 2001
-From: Daniel Golle <daniel@makrotopia.org>
-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 <daniel@makrotopia.org>
----
- 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... */