mirror of
				git://git.openwrt.org/openwrt/openwrt.git
				synced 2025-11-03 14:34:27 -05:00 
			
		
		
		
	Manually rebased: pending-5.15/103-kbuild-export-SUBARCH.patch All other patches automatically rebased. Build system: x86_64 Build-tested: bcm2711/RPi4B, filogic/xiaomi_redmi-router-ax6000-ubootmod Run-tested: bcm2711/RPi4B, filogic/xiaomi_redmi-router-ax6000-ubootmod Signed-off-by: John Audia <therealgraysky@proton.me>
		
			
				
	
	
		
			841 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			841 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
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
 | 
						|
@@ -482,10 +482,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;
 | 
						|
 }
 | 
						|
@@ -538,12 +544,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;
 | 
						|
 }
 | 
						|
@@ -749,6 +761,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;
 | 
						|
 
 | 
						|
@@ -757,6 +776,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;
 | 
						|
 
 | 
						|
@@ -858,6 +884,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);
 | 
						|
@@ -879,6 +917,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
 | 
						|
@@ -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
 | 
						|
 
 | 
						|
 /**
 | 
						|
@@ -420,6 +421,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)
 | 
						|
 
 | 
						|
@@ -1754,6 +1771,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)
 | 
						|
 {
 | 
						|
@@ -1776,6 +1853,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...                   */
 |