Просмотр исходного кода

realtek: switch to use generic MDIO accessor functions

Instead of directly calling SoC-specific functions in order to access
(paged) MII registers or MMD registers, create infrastructure to allow
using the generic phy_*, phy_*_paged and phy_*_mmd functions.

Signed-off-by: Daniel Golle <[email protected]>
Daniel Golle 4 лет назад
Родитель
Сommit
b53202a8c3

+ 4 - 1
target/linux/realtek/files-5.10/drivers/net/dsa/rtl83xx/common.c

@@ -292,11 +292,14 @@ 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;
+	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) {

+ 122 - 29
target/linux/realtek/files-5.10/drivers/net/ethernet/rtl838x_eth.c

@@ -1623,7 +1623,7 @@ static int rtl838x_set_link_ksettings(struct net_device *ndev,
 	return phylink_ethtool_ksettings_set(priv->phylink, cmd);
 }
 
-static int rtl838x_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
+static int rtl838x_mdio_read_paged(struct mii_bus *bus, int mii_id, u16 page, int regnum)
 {
 	u32 val;
 	int err;
@@ -1631,13 +1631,29 @@ static int rtl838x_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
 
 	if (mii_id >= 24 && mii_id <= 27 && priv->id == 0x8380)
 		return rtl838x_read_sds_phy(mii_id, regnum);
-	err = rtl838x_read_phy(mii_id, 0, regnum, &val);
+
+	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;
 }
 
-static int rtl839x_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
+static int rtl838x_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
+{
+	return rtl838x_mdio_read_paged(bus, mii_id, 0, regnum);
+}
+
+static int rtl839x_mdio_read_paged(struct mii_bus *bus, int mii_id, u16 page, int regnum)
 {
 	u32 val;
 	int err;
@@ -1646,22 +1662,37 @@ static int rtl839x_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
 	if (mii_id >= 48 && mii_id <= 49 && priv->id == 0x8393)
 		return rtl839x_read_sds_phy(mii_id, regnum);
 
-	err = rtl839x_read_phy(mii_id, 0, regnum, &val);
-	if (err)
+	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);
+	}
+		if (err)
 		return err;
 	return val;
 }
 
-static int rtl930x_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
+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], 0, regnum);
+		return rtl930x_read_sds_phy(priv->sds_id[mii_id], page, regnum);
 
-	if (regnum & MII_ADDR_C45) {
+	if (regnum & (MII_ADDR_C45 | MII_ADDR_C22_MMD)) {
 		err = rtl930x_read_mmd_phy(mii_id,
 					   mdiobus_c45_devad(regnum),
 					   regnum, &val);
@@ -1669,7 +1700,7 @@ static int rtl930x_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
 			 mdiobus_c45_devad(regnum), mdiobus_c45_regad(regnum),
 			 val, err);
 	} else {
-		err = rtl930x_read_phy(mii_id, 0, regnum, &val);
+		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)
@@ -1677,16 +1708,20 @@ static int rtl930x_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
 	return val;
 }
 
+static int rtl930x_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
+{
+	return rtl930x_mdio_read_paged(bus, mii_id, 0, regnum);
+}
 
-static int rtl931x_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
+static int rtl931x_mdio_read_paged(struct mii_bus *bus, int mii_id, u16 page, int regnum)
 {
 	u32 val;
 	int err, v;
 	struct rtl838x_eth_priv *priv = bus->priv;
 
 	pr_debug("%s: In here, port %d\n", __func__, mii_id);
-	if (priv->sds_id[mii_id] >= 0 && mii_id >= 52) {
-		v = rtl931x_read_sds_phy(priv->sds_id[mii_id], 0, regnum);
+	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 {
@@ -1694,7 +1729,7 @@ static int rtl931x_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
 			val = v;
 		}
 	} else {
-		if (regnum & MII_ADDR_C45) {
+		if (regnum & (MII_ADDR_C45 | MII_ADDR_C22_MMD)) {
 			err = rtl931x_read_mmd_phy(mii_id,
 						   mdiobus_c45_devad(regnum),
 						   regnum, &val);
@@ -1702,7 +1737,7 @@ static int rtl931x_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
 				 mdiobus_c45_devad(regnum), mdiobus_c45_regad(regnum),
 				 val, err);
 		} else {
-			err = rtl931x_read_phy(mii_id, 0, regnum, &val);
+			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);
 		}
 	}
@@ -1712,8 +1747,13 @@ static int rtl931x_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
 	return val;
 }
 
-static int rtl838x_mdio_write(struct mii_bus *bus, int mii_id,
-			      int regnum, u16 value)
+static int rtl931x_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
+{
+	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;
@@ -1725,13 +1765,29 @@ static int rtl838x_mdio_write(struct mii_bus *bus, int mii_id,
 		sw_w32(value, RTL838X_SDS4_FIB_REG0 + offset + (regnum << 2));
 		return 0;
 	}
-	err = rtl838x_write_phy(mii_id, 0, regnum, value);
+
+	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;
+	}
+	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;
 }
 
-static int rtl839x_mdio_write(struct mii_bus *bus, int mii_id,
+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;
@@ -1739,39 +1795,61 @@ static int rtl839x_mdio_write(struct mii_bus *bus, int mii_id,
 	if (mii_id >= 48 && mii_id <= 49 && priv->id == 0x8393)
 		return rtl839x_write_sds_phy(mii_id, regnum, value);
 
-	err = rtl839x_write_phy(mii_id, 0, 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);
+
+		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;
 }
 
-static int rtl930x_mdio_write(struct mii_bus *bus, int mii_id,
+static int rtl839x_mdio_write(struct mii_bus *bus, int mii_id,
 			      int regnum, u16 value)
+{
+	return rtl839x_mdio_write_paged(bus, mii_id, 0, regnum, value);
+}
+
+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->sds_id[mii_id] >= 0)
-		return rtl930x_write_sds_phy(priv->sds_id[mii_id], 0, regnum, value);
+	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)
+	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, 0, 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 rtl931x_mdio_write(struct mii_bus *bus, int mii_id,
+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->sds_id[mii_id] >= 0 && mii_id >= 52)
-		return rtl931x_write_sds_phy(priv->sds_id[mii_id], 0, regnum, value);
+	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) {
+	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,
@@ -1781,11 +1859,17 @@ static int rtl931x_mdio_write(struct mii_bus *bus, int mii_id,
 		return err;
 	}
 
-	err = rtl931x_write_phy(mii_id, 0, regnum, value);
+	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);
 	return err;
 }
 
+static int rtl931x_mdio_write(struct mii_bus *bus, int mii_id,
+			      int regnum, u16 value)
+{
+	return rtl931x_mdio_write_paged(bus, mii_id, 0, regnum, value);
+}
+
 static int rtl838x_mdio_reset(struct mii_bus *bus)
 {
 	pr_debug("%s called\n", __func__);
@@ -2047,30 +2131,39 @@ static int rtl838x_mdio_init(struct rtl838x_eth_priv *priv)
 	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;
 		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;
 		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->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->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;
 

+ 4 - 0
target/linux/realtek/files-5.10/drivers/net/ethernet/rtl838x_eth.h

@@ -442,8 +442,12 @@ struct rtl838x_eth_reg {
 
 int rtl838x_write_phy(u32 port, u32 page, u32 reg, u32 val);
 int rtl838x_read_phy(u32 port, u32 page, u32 reg, u32 *val);
+int rtl838x_write_mmd_phy(u32 port, u32 addr, u32 reg, u32 val);
+int rtl838x_read_mmd_phy(u32 port, u32 addr, u32 reg, u32 *val);
 int rtl839x_write_phy(u32 port, u32 page, u32 reg, u32 val);
 int rtl839x_read_phy(u32 port, u32 page, u32 reg, u32 *val);
+int rtl839x_read_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 *val);
+int rtl839x_write_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 val);
 int rtl930x_write_phy(u32 port, u32 page, u32 reg, u32 val);
 int rtl930x_read_phy(u32 port, u32 page, u32 reg, u32 *val);
 int rtl931x_write_phy(u32 port, u32 page, u32 reg, u32 val);

Разница между файлами не показана из-за своего большого размера
+ 199 - 323
target/linux/realtek/files-5.10/drivers/net/phy/rtl83xx-phy.c


+ 1 - 2
target/linux/realtek/files-5.10/drivers/net/phy/rtl83xx-phy.h

@@ -1,7 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0-only
 
-// TODO: not really used
-struct rtl838x_phy_priv {
+struct rtl83xx_shared_private {
 	char *name;
 };
 

+ 840 - 0
target/linux/realtek/patches-5.10/800-net-mdio-support-hardware-assisted-indirect-access.patch

@@ -0,0 +1,840 @@
+From 5d84f16b0036b33487b94abef15ad3c224c81ee9 Mon Sep 17 00:00:00 2001
+From: Daniel Golle <[email protected]>
+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 <[email protected]>
+---
+ 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
+@@ -734,6 +734,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
+@@ -749,7 +775,10 @@ int __mdiobus_read(struct mii_bus *bus,
+ 
+ 	WARN_ON_ONCE(!mutex_is_locked(&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);
+@@ -759,6 +788,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
+@@ -775,7 +838,10 @@ int __mdiobus_write(struct mii_bus *bus,
+ 
+ 	WARN_ON_ONCE(!mutex_is_locked(&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);
+@@ -785,6 +851,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
+@@ -817,6 +916,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
+@@ -842,6 +978,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
+@@ -864,6 +1073,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
+@@ -890,6 +1122,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
+@@ -913,6 +1172,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
+@@ -934,6 +1217,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
+@@ -481,10 +481,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;
+ }
+@@ -537,12 +543,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;
+ }
+@@ -748,6 +760,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;
+ 
+@@ -756,6 +775,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;
+ 
+@@ -857,6 +883,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);
+@@ -878,6 +916,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)
+@@ -327,11 +328,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);
+@@ -339,11 +348,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);
+@@ -367,6 +416,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
+@@ -80,6 +80,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
+ 
+ /**
+@@ -374,6 +375,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)
+ 
+@@ -1651,6 +1668,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)
+ {
+@@ -1673,6 +1750,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...                   */

Некоторые файлы не были показаны из-за большого количества измененных файлов