[openwrt/openwrt] generic: 6.1: backport QCA807x PHY patches

LEDE Commits lede-commits at lists.infradead.org
Sun Feb 11 12:08:36 PST 2024


ansuel pushed a commit to openwrt/openwrt.git, branch main:
https://git.openwrt.org/16364e410039a3806301338e3649d557a23deabd

commit 16364e410039a3806301338e3649d557a23deabd
Author: Christian Marangi <ansuelsmth at gmail.com>
AuthorDate: Sat Feb 10 19:27:33 2024 +0100

    generic: 6.1: backport QCA807x PHY patches
    
    Backport QCA807x PHY patches merged upstream that introduce the new
    concept of PHY package.
    
    Also add in generic config the new Kconfig CONFIG_QCA807X_PHY.
    
    All affected patch automatically refreshed with make
    target/linux/refresh.
    
    Signed-off-by: Christian Marangi <ansuelsmth at gmail.com>
---
 ...m-split-out-the-BCM54213PE-from-the-BCM54.patch |   6 +-
 ...-Populate-phy-driver-block-for-BCM54213PE.patch |   2 +-
 ...-support-for-scanning-PHY-in-PHY-packages.patch | 211 +++++++
 ...t-phy-add-devm-of_phy_package_join-helper.patch | 185 ++++++
 ...qcom-move-more-function-to-shared-library.patch | 583 ++++++++++++++++++
 ...vide-whether-link-has-changed-in-c37_read.patch | 100 +++
 ...y-qcom-add-support-for-QCA807x-PHY-Family.patch | 668 +++++++++++++++++++++
 ...m-move-common-qca808x-LED-define-to-share.patch | 179 ++++++
 ...com-generalize-some-qca808x-LED-functions.patch | 172 ++++++
 ...-qca807x-add-support-for-configurable-LED.patch | 326 ++++++++++
 ...6.4-05-net-phy-Add-a-binding-for-PHY-LEDs.patch |  10 +-
 ..._device-Call-into-the-PHY-driver-to-set-L.patch |   8 +-
 ..._device-Call-into-the-PHY-driver-to-set-L.patch |   6 +-
 ...-phy-fix-circular-LEDS_CLASS-dependencies.patch |   2 +-
 ...v6.4-net-phy-Fix-reading-LED-reg-property.patch |   4 +-
 ...ual-remove-LEDs-to-ensure-correct-orderin.patch |   8 +-
 ..._device-Call-into-the-PHY-driver-to-set-L.patch |   6 +-
 ...y-add-support-for-PHY-LEDs-polarity-modes.patch |   8 +-
 target/linux/generic/config-6.1                    |   1 +
 ...-add-detach-callback-to-struct-phy_driver.patch |   4 +-
 ...et-phy-simplify-phy_link_change-arguments.patch |   2 +-
 .../721-NET-no-auto-carrier-off-support.patch      |   2 +-
 22 files changed, 2459 insertions(+), 34 deletions(-)

diff --git a/target/linux/bcm27xx/patches-6.1/950-0286-phy-broadcom-split-out-the-BCM54213PE-from-the-BCM54.patch b/target/linux/bcm27xx/patches-6.1/950-0286-phy-broadcom-split-out-the-BCM54213PE-from-the-BCM54.patch
index ec4cd0993e..55c881286e 100644
--- a/target/linux/bcm27xx/patches-6.1/950-0286-phy-broadcom-split-out-the-BCM54213PE-from-the-BCM54.patch
+++ b/target/linux/bcm27xx/patches-6.1/950-0286-phy-broadcom-split-out-the-BCM54213PE-from-the-BCM54.patch
@@ -26,7 +26,7 @@ Signed-off-by: Jonathan Bell <jonathan at raspberrypi.org>
  		return;
  
  	val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_SCR3);
-@@ -905,7 +906,7 @@ static struct phy_driver broadcom_driver
+@@ -906,7 +907,7 @@ static struct phy_driver broadcom_driver
  	.link_change_notify	= bcm54xx_link_change_notify,
  }, {
  	.phy_id		= PHY_ID_BCM54210E,
@@ -35,7 +35,7 @@ Signed-off-by: Jonathan Bell <jonathan at raspberrypi.org>
  	.name		= "Broadcom BCM54210E",
  	/* PHY_GBIT_FEATURES */
  	.get_sset_count	= bcm_phy_get_sset_count,
-@@ -919,6 +920,13 @@ static struct phy_driver broadcom_driver
+@@ -920,6 +921,13 @@ static struct phy_driver broadcom_driver
  	.suspend	= bcm54xx_suspend,
  	.resume		= bcm54xx_resume,
  }, {
@@ -49,7 +49,7 @@ Signed-off-by: Jonathan Bell <jonathan at raspberrypi.org>
  	.phy_id		= PHY_ID_BCM5461,
  	.phy_id_mask	= 0xfffffff0,
  	.name		= "Broadcom BCM5461",
-@@ -1155,7 +1163,8 @@ module_phy_driver(broadcom_drivers);
+@@ -1156,7 +1164,8 @@ module_phy_driver(broadcom_drivers);
  static struct mdio_device_id __maybe_unused broadcom_tbl[] = {
  	{ PHY_ID_BCM5411, 0xfffffff0 },
  	{ PHY_ID_BCM5421, 0xfffffff0 },
diff --git a/target/linux/bcm27xx/patches-6.1/950-0427-Populate-phy-driver-block-for-BCM54213PE.patch b/target/linux/bcm27xx/patches-6.1/950-0427-Populate-phy-driver-block-for-BCM54213PE.patch
index 18e654071c..cdfc996abf 100644
--- a/target/linux/bcm27xx/patches-6.1/950-0427-Populate-phy-driver-block-for-BCM54213PE.patch
+++ b/target/linux/bcm27xx/patches-6.1/950-0427-Populate-phy-driver-block-for-BCM54213PE.patch
@@ -16,7 +16,7 @@ Signed-off-by: Jonathan Lemon <jonathan.lemon at gmail.com>
 
 --- a/drivers/net/phy/broadcom.c
 +++ b/drivers/net/phy/broadcom.c
-@@ -932,8 +932,14 @@ static struct phy_driver broadcom_driver
+@@ -933,8 +933,14 @@ static struct phy_driver broadcom_driver
  	.phy_id_mask	= 0xffffffff,
  	.name		= "Broadcom BCM54213PE",
  	/* PHY_GBIT_FEATURES */
diff --git a/target/linux/generic/backport-6.1/716-v6.9-02-net-phy-add-support-for-scanning-PHY-in-PHY-packages.patch b/target/linux/generic/backport-6.1/716-v6.9-02-net-phy-add-support-for-scanning-PHY-in-PHY-packages.patch
new file mode 100644
index 0000000000..b355031aa5
--- /dev/null
+++ b/target/linux/generic/backport-6.1/716-v6.9-02-net-phy-add-support-for-scanning-PHY-in-PHY-packages.patch
@@ -0,0 +1,211 @@
+From 385ef48f468696d6d172eb367656a3466fa0408d Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth at gmail.com>
+Date: Tue, 6 Feb 2024 18:31:05 +0100
+Subject: [PATCH 02/10] net: phy: add support for scanning PHY in PHY packages
+ nodes
+
+Add support for scanning PHY in PHY package nodes. PHY packages nodes
+are just container for actual PHY on the MDIO bus.
+
+Their PHY address defined in the PHY package node are absolute and
+reflect the address on the MDIO bus.
+
+mdio_bus.c and of_mdio.c is updated to now support and parse also
+PHY package subnode by checking if the node name match
+"ethernet-phy-package".
+
+As PHY package reg is mandatory and each PHY in the PHY package must
+have a reg, every invalid PHY Package node is ignored and will be
+skipped by the autoscan fallback.
+
+Signed-off-by: Christian Marangi <ansuelsmth at gmail.com>
+Reviewed-by: Andrew Lunn <andrew at lunn.ch>
+Signed-off-by: David S. Miller <davem at davemloft.net>
+---
+ drivers/net/mdio/of_mdio.c | 79 +++++++++++++++++++++++++++-----------
+ drivers/net/phy/mdio_bus.c | 44 +++++++++++++++++----
+ 2 files changed, 92 insertions(+), 31 deletions(-)
+
+--- a/drivers/net/mdio/of_mdio.c
++++ b/drivers/net/mdio/of_mdio.c
+@@ -138,6 +138,53 @@ bool of_mdiobus_child_is_phy(struct devi
+ }
+ EXPORT_SYMBOL(of_mdiobus_child_is_phy);
+ 
++static int __of_mdiobus_parse_phys(struct mii_bus *mdio, struct device_node *np,
++				   bool *scanphys)
++{
++	struct device_node *child;
++	int addr, rc = 0;
++
++	/* Loop over the child nodes and register a phy_device for each phy */
++	for_each_available_child_of_node(np, child) {
++		if (of_node_name_eq(child, "ethernet-phy-package")) {
++			/* Ignore invalid ethernet-phy-package node */
++			if (!of_find_property(child, "reg", NULL))
++				continue;
++
++			rc = __of_mdiobus_parse_phys(mdio, child, NULL);
++			if (rc && rc != -ENODEV)
++				goto exit;
++
++			continue;
++		}
++
++		addr = of_mdio_parse_addr(&mdio->dev, child);
++		if (addr < 0) {
++			/* Skip scanning for invalid ethernet-phy-package node */
++			if (scanphys)
++				*scanphys = true;
++			continue;
++		}
++
++		if (of_mdiobus_child_is_phy(child))
++			rc = of_mdiobus_register_phy(mdio, child, addr);
++		else
++			rc = of_mdiobus_register_device(mdio, child, addr);
++
++		if (rc == -ENODEV)
++			dev_err(&mdio->dev,
++				"MDIO device at address %d is missing.\n",
++				addr);
++		else if (rc)
++			goto exit;
++	}
++
++	return 0;
++exit:
++	of_node_put(child);
++	return rc;
++}
++
+ /**
+  * __of_mdiobus_register - Register mii_bus and create PHYs from the device tree
+  * @mdio: pointer to mii_bus structure
+@@ -179,33 +226,18 @@ int __of_mdiobus_register(struct mii_bus
+ 		return rc;
+ 
+ 	/* Loop over the child nodes and register a phy_device for each phy */
+-	for_each_available_child_of_node(np, child) {
+-		addr = of_mdio_parse_addr(&mdio->dev, child);
+-		if (addr < 0) {
+-			scanphys = true;
+-			continue;
+-		}
+-
+-		if (of_mdiobus_child_is_phy(child))
+-			rc = of_mdiobus_register_phy(mdio, child, addr);
+-		else
+-			rc = of_mdiobus_register_device(mdio, child, addr);
+-
+-		if (rc == -ENODEV)
+-			dev_err(&mdio->dev,
+-				"MDIO device at address %d is missing.\n",
+-				addr);
+-		else if (rc)
+-			goto unregister;
+-	}
++	rc = __of_mdiobus_parse_phys(mdio, np, &scanphys);
++	if (rc)
++		goto unregister;
+ 
+ 	if (!scanphys)
+ 		return 0;
+ 
+ 	/* auto scan for PHYs with empty reg property */
+ 	for_each_available_child_of_node(np, child) {
+-		/* Skip PHYs with reg property set */
+-		if (of_find_property(child, "reg", NULL))
++		/* Skip PHYs with reg property set or ethernet-phy-package node */
++		if (of_find_property(child, "reg", NULL) ||
++		    of_node_name_eq(child, "ethernet-phy-package"))
+ 			continue;
+ 
+ 		for (addr = 0; addr < PHY_MAX_ADDR; addr++) {
+@@ -226,15 +258,16 @@ int __of_mdiobus_register(struct mii_bus
+ 				if (!rc)
+ 					break;
+ 				if (rc != -ENODEV)
+-					goto unregister;
++					goto put_unregister;
+ 			}
+ 		}
+ 	}
+ 
+ 	return 0;
+ 
+-unregister:
++put_unregister:
+ 	of_node_put(child);
++unregister:
+ 	mdiobus_unregister(mdio);
+ 	return rc;
+ }
+--- a/drivers/net/phy/mdio_bus.c
++++ b/drivers/net/phy/mdio_bus.c
+@@ -448,19 +448,34 @@ EXPORT_SYMBOL(of_mdio_find_bus);
+  * found, set the of_node pointer for the mdio device. This allows
+  * auto-probed phy devices to be supplied with information passed in
+  * via DT.
++ * If a PHY package is found, PHY is searched also there.
+  */
+-static void of_mdiobus_link_mdiodev(struct mii_bus *bus,
+-				    struct mdio_device *mdiodev)
++static int of_mdiobus_find_phy(struct device *dev, struct mdio_device *mdiodev,
++			       struct device_node *np)
+ {
+-	struct device *dev = &mdiodev->dev;
+ 	struct device_node *child;
+ 
+-	if (dev->of_node || !bus->dev.of_node)
+-		return;
+-
+-	for_each_available_child_of_node(bus->dev.of_node, child) {
++	for_each_available_child_of_node(np, child) {
+ 		int addr;
+ 
++		if (of_node_name_eq(child, "ethernet-phy-package")) {
++			/* Validate PHY package reg presence */
++			if (!of_find_property(child, "reg", NULL)) {
++				of_node_put(child);
++				return -EINVAL;
++			}
++
++			if (!of_mdiobus_find_phy(dev, mdiodev, child)) {
++				/* The refcount for the PHY package will be
++				 * incremented later when PHY join the Package.
++				 */
++				of_node_put(child);
++				return 0;
++			}
++
++			continue;
++		}
++
+ 		addr = of_mdio_parse_addr(dev, child);
+ 		if (addr < 0)
+ 			continue;
+@@ -470,9 +485,22 @@ static void of_mdiobus_link_mdiodev(stru
+ 			/* The refcount on "child" is passed to the mdio
+ 			 * device. Do _not_ use of_node_put(child) here.
+ 			 */
+-			return;
++			return 0;
+ 		}
+ 	}
++
++	return -ENODEV;
++}
++
++static void of_mdiobus_link_mdiodev(struct mii_bus *bus,
++				    struct mdio_device *mdiodev)
++{
++	struct device *dev = &mdiodev->dev;
++
++	if (dev->of_node || !bus->dev.of_node)
++		return;
++
++	of_mdiobus_find_phy(dev, mdiodev, bus->dev.of_node);
+ }
+ #else /* !IS_ENABLED(CONFIG_OF_MDIO) */
+ static inline void of_mdiobus_link_mdiodev(struct mii_bus *mdio,
diff --git a/target/linux/generic/backport-6.1/716-v6.9-03-net-phy-add-devm-of_phy_package_join-helper.patch b/target/linux/generic/backport-6.1/716-v6.9-03-net-phy-add-devm-of_phy_package_join-helper.patch
new file mode 100644
index 0000000000..2e38269060
--- /dev/null
+++ b/target/linux/generic/backport-6.1/716-v6.9-03-net-phy-add-devm-of_phy_package_join-helper.patch
@@ -0,0 +1,185 @@
+From 471e8fd3afcef5a9f9089f0bd21965ad9ba35c91 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth at gmail.com>
+Date: Tue, 6 Feb 2024 18:31:06 +0100
+Subject: [PATCH 03/10] net: phy: add devm/of_phy_package_join helper
+
+Add devm/of_phy_package_join helper to join PHYs in a PHY package. These
+are variant of the manual phy_package_join with the difference that
+these will use DT nodes to derive the base_addr instead of manually
+passing an hardcoded value.
+
+An additional value is added in phy_package_shared, "np" to reference
+the PHY package node pointer in specific PHY driver probe_once and
+config_init_once functions to make use of additional specific properties
+defined in the PHY package node in DT.
+
+The np value is filled only with of_phy_package_join if a valid PHY
+package node is found. A valid PHY package node must have the node name
+set to "ethernet-phy-package".
+
+Signed-off-by: Christian Marangi <ansuelsmth at gmail.com>
+Reviewed-by: Andrew Lunn <andrew at lunn.ch>
+Signed-off-by: David S. Miller <davem at davemloft.net>
+---
+ drivers/net/phy/phy_device.c | 96 ++++++++++++++++++++++++++++++++++++
+ include/linux/phy.h          |  6 +++
+ 2 files changed, 102 insertions(+)
+
+--- a/drivers/net/phy/phy_device.c
++++ b/drivers/net/phy/phy_device.c
+@@ -1650,6 +1650,7 @@ int phy_package_join(struct phy_device *
+ 			shared->priv_size = priv_size;
+ 		}
+ 		shared->base_addr = base_addr;
++		shared->np = NULL;
+ 		refcount_set(&shared->refcnt, 1);
+ 		bus->shared[base_addr] = shared;
+ 	} else {
+@@ -1673,6 +1674,63 @@ err_unlock:
+ EXPORT_SYMBOL_GPL(phy_package_join);
+ 
+ /**
++ * of_phy_package_join - join a common PHY group in PHY package
++ * @phydev: target phy_device struct
++ * @priv_size: if non-zero allocate this amount of bytes for private data
++ *
++ * This is a variant of phy_package_join for PHY package defined in DT.
++ *
++ * The parent node of the @phydev is checked as a valid PHY package node
++ * structure (by matching the node name "ethernet-phy-package") and the
++ * base_addr for the PHY package is passed to phy_package_join.
++ *
++ * With this configuration the shared struct will also have the np value
++ * filled to use additional DT defined properties in PHY specific
++ * probe_once and config_init_once PHY package OPs.
++ *
++ * Returns < 0 on error, 0 on success. Esp. calling phy_package_join()
++ * with the same cookie but a different priv_size is an error. Or a parent
++ * node is not detected or is not valid or doesn't match the expected node
++ * name for PHY package.
++ */
++int of_phy_package_join(struct phy_device *phydev, size_t priv_size)
++{
++	struct device_node *node = phydev->mdio.dev.of_node;
++	struct device_node *package_node;
++	u32 base_addr;
++	int ret;
++
++	if (!node)
++		return -EINVAL;
++
++	package_node = of_get_parent(node);
++	if (!package_node)
++		return -EINVAL;
++
++	if (!of_node_name_eq(package_node, "ethernet-phy-package")) {
++		ret = -EINVAL;
++		goto exit;
++	}
++
++	if (of_property_read_u32(package_node, "reg", &base_addr)) {
++		ret = -EINVAL;
++		goto exit;
++	}
++
++	ret = phy_package_join(phydev, base_addr, priv_size);
++	if (ret)
++		goto exit;
++
++	phydev->shared->np = package_node;
++
++	return 0;
++exit:
++	of_node_put(package_node);
++	return ret;
++}
++EXPORT_SYMBOL_GPL(of_phy_package_join);
++
++/**
+  * phy_package_leave - leave a common PHY group
+  * @phydev: target phy_device struct
+  *
+@@ -1688,6 +1746,10 @@ void phy_package_leave(struct phy_device
+ 	if (!shared)
+ 		return;
+ 
++	/* Decrease the node refcount on leave if present */
++	if (shared->np)
++		of_node_put(shared->np);
++
+ 	if (refcount_dec_and_mutex_lock(&shared->refcnt, &bus->shared_lock)) {
+ 		bus->shared[shared->base_addr] = NULL;
+ 		mutex_unlock(&bus->shared_lock);
+@@ -1741,6 +1803,40 @@ int devm_phy_package_join(struct device
+ EXPORT_SYMBOL_GPL(devm_phy_package_join);
+ 
+ /**
++ * devm_of_phy_package_join - resource managed of_phy_package_join()
++ * @dev: device that is registering this PHY package
++ * @phydev: target phy_device struct
++ * @priv_size: if non-zero allocate this amount of bytes for private data
++ *
++ * Managed of_phy_package_join(). Shared storage fetched by this function,
++ * phy_package_leave() is automatically called on driver detach. See
++ * of_phy_package_join() for more information.
++ */
++int devm_of_phy_package_join(struct device *dev, struct phy_device *phydev,
++			     size_t priv_size)
++{
++	struct phy_device **ptr;
++	int ret;
++
++	ptr = devres_alloc(devm_phy_package_leave, sizeof(*ptr),
++			   GFP_KERNEL);
++	if (!ptr)
++		return -ENOMEM;
++
++	ret = of_phy_package_join(phydev, priv_size);
++
++	if (!ret) {
++		*ptr = phydev;
++		devres_add(dev, ptr);
++	} else {
++		devres_free(ptr);
++	}
++
++	return ret;
++}
++EXPORT_SYMBOL_GPL(devm_of_phy_package_join);
++
++/**
+  * phy_detach - detach a PHY device from its network device
+  * @phydev: target phy_device struct
+  *
+--- a/include/linux/phy.h
++++ b/include/linux/phy.h
+@@ -321,6 +321,7 @@ struct mdio_bus_stats {
+  * struct phy_package_shared - Shared information in PHY packages
+  * @base_addr: Base PHY address of PHY package used to combine PHYs
+  *   in one package and for offset calculation of phy_package_read/write
++ * @np: Pointer to the Device Node if PHY package defined in DT
+  * @refcnt: Number of PHYs connected to this shared data
+  * @flags: Initialization of PHY package
+  * @priv_size: Size of the shared private data @priv
+@@ -332,6 +333,8 @@ struct mdio_bus_stats {
+  */
+ struct phy_package_shared {
+ 	u8 base_addr;
++	/* With PHY package defined in DT this points to the PHY package node */
++	struct device_node *np;
+ 	refcount_t refcnt;
+ 	unsigned long flags;
+ 	size_t priv_size;
+@@ -1765,9 +1768,12 @@ int phy_ethtool_set_link_ksettings(struc
+ 				   const struct ethtool_link_ksettings *cmd);
+ int phy_ethtool_nway_reset(struct net_device *ndev);
+ int phy_package_join(struct phy_device *phydev, int base_addr, size_t priv_size);
++int of_phy_package_join(struct phy_device *phydev, size_t priv_size);
+ void phy_package_leave(struct phy_device *phydev);
+ int devm_phy_package_join(struct device *dev, struct phy_device *phydev,
+ 			  int base_addr, size_t priv_size);
++int devm_of_phy_package_join(struct device *dev, struct phy_device *phydev,
++			     size_t priv_size);
+ 
+ #if IS_ENABLED(CONFIG_PHYLIB)
+ int __init mdio_bus_init(void);
diff --git a/target/linux/generic/backport-6.1/716-v6.9-04-net-phy-qcom-move-more-function-to-shared-library.patch b/target/linux/generic/backport-6.1/716-v6.9-04-net-phy-qcom-move-more-function-to-shared-library.patch
new file mode 100644
index 0000000000..e935725630
--- /dev/null
+++ b/target/linux/generic/backport-6.1/716-v6.9-04-net-phy-qcom-move-more-function-to-shared-library.patch
@@ -0,0 +1,583 @@
+From 737eb75a815f9c08dcbb6631db57f4f4b0540a5b Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth at gmail.com>
+Date: Tue, 6 Feb 2024 18:31:07 +0100
+Subject: [PATCH 04/10] net: phy: qcom: move more function to shared library
+
+Move more function to shared library in preparation for introduction of
+new PHY Family qca807x that will make use of both functions from at803x
+and qca808x as it's a transition PHY with some implementation of at803x
+and some from the new qca808x.
+
+Signed-off-by: Christian Marangi <ansuelsmth at gmail.com>
+Reviewed-by: Andrew Lunn <andrew at lunn.ch>
+Signed-off-by: David S. Miller <davem at davemloft.net>
+---
+ drivers/net/phy/qcom/at803x.c       |  35 -----
+ drivers/net/phy/qcom/qca808x.c      | 205 ----------------------------
+ drivers/net/phy/qcom/qcom-phy-lib.c | 193 ++++++++++++++++++++++++++
+ drivers/net/phy/qcom/qcom.h         |  51 +++++++
+ 4 files changed, 244 insertions(+), 240 deletions(-)
+
+--- a/drivers/net/phy/qcom/at803x.c
++++ b/drivers/net/phy/qcom/at803x.c
+@@ -504,41 +504,6 @@ static void at803x_link_change_notify(st
+ 	}
+ }
+ 
+-static int at803x_read_status(struct phy_device *phydev)
+-{
+-	struct at803x_ss_mask ss_mask = { 0 };
+-	int err, old_link = phydev->link;
+-
+-	/* Update the link, but return if there was an error */
+-	err = genphy_update_link(phydev);
+-	if (err)
+-		return err;
+-
+-	/* why bother the PHY if nothing can have changed */
+-	if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link)
+-		return 0;
+-
+-	phydev->speed = SPEED_UNKNOWN;
+-	phydev->duplex = DUPLEX_UNKNOWN;
+-	phydev->pause = 0;
+-	phydev->asym_pause = 0;
+-
+-	err = genphy_read_lpa(phydev);
+-	if (err < 0)
+-		return err;
+-
+-	ss_mask.speed_mask = AT803X_SS_SPEED_MASK;
+-	ss_mask.speed_shift = __bf_shf(AT803X_SS_SPEED_MASK);
+-	err = at803x_read_specific_status(phydev, ss_mask);
+-	if (err < 0)
+-		return err;
+-
+-	if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete)
+-		phy_resolve_aneg_pause(phydev);
+-
+-	return 0;
+-}
+-
+ static int at803x_config_aneg(struct phy_device *phydev)
+ {
+ 	struct at803x_priv *priv = phydev->priv;
+--- a/drivers/net/phy/qcom/qca808x.c
++++ b/drivers/net/phy/qcom/qca808x.c
+@@ -2,7 +2,6 @@
+ 
+ #include <linux/phy.h>
+ #include <linux/module.h>
+-#include <linux/ethtool_netlink.h>
+ 
+ #include "qcom.h"
+ 
+@@ -63,55 +62,6 @@
+ #define QCA808X_DBG_AN_TEST			0xb
+ #define QCA808X_HIBERNATION_EN			BIT(15)
+ 
+-#define QCA808X_CDT_ENABLE_TEST			BIT(15)
+-#define QCA808X_CDT_INTER_CHECK_DIS		BIT(13)
+-#define QCA808X_CDT_STATUS			BIT(11)
+-#define QCA808X_CDT_LENGTH_UNIT			BIT(10)
+-
+-#define QCA808X_MMD3_CDT_STATUS			0x8064
+-#define QCA808X_MMD3_CDT_DIAG_PAIR_A		0x8065
+-#define QCA808X_MMD3_CDT_DIAG_PAIR_B		0x8066
+-#define QCA808X_MMD3_CDT_DIAG_PAIR_C		0x8067
+-#define QCA808X_MMD3_CDT_DIAG_PAIR_D		0x8068
+-#define QCA808X_CDT_DIAG_LENGTH_SAME_SHORT	GENMASK(15, 8)
+-#define QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT	GENMASK(7, 0)
+-
+-#define QCA808X_CDT_CODE_PAIR_A			GENMASK(15, 12)
+-#define QCA808X_CDT_CODE_PAIR_B			GENMASK(11, 8)
+-#define QCA808X_CDT_CODE_PAIR_C			GENMASK(7, 4)
+-#define QCA808X_CDT_CODE_PAIR_D			GENMASK(3, 0)
+-
+-#define QCA808X_CDT_STATUS_STAT_TYPE		GENMASK(1, 0)
+-#define QCA808X_CDT_STATUS_STAT_FAIL		FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 0)
+-#define QCA808X_CDT_STATUS_STAT_NORMAL		FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 1)
+-#define QCA808X_CDT_STATUS_STAT_SAME_OPEN	FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 2)
+-#define QCA808X_CDT_STATUS_STAT_SAME_SHORT	FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 3)
+-
+-#define QCA808X_CDT_STATUS_STAT_MDI		GENMASK(3, 2)
+-#define QCA808X_CDT_STATUS_STAT_MDI1		FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 1)
+-#define QCA808X_CDT_STATUS_STAT_MDI2		FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 2)
+-#define QCA808X_CDT_STATUS_STAT_MDI3		FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 3)
+-
+-/* NORMAL are MDI with type set to 0 */
+-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL	QCA808X_CDT_STATUS_STAT_MDI1
+-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN		(QCA808X_CDT_STATUS_STAT_SAME_OPEN |\
+-									 QCA808X_CDT_STATUS_STAT_MDI1)
+-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT	(QCA808X_CDT_STATUS_STAT_SAME_SHORT |\
+-									 QCA808X_CDT_STATUS_STAT_MDI1)
+-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL	QCA808X_CDT_STATUS_STAT_MDI2
+-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN		(QCA808X_CDT_STATUS_STAT_SAME_OPEN |\
+-									 QCA808X_CDT_STATUS_STAT_MDI2)
+-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT	(QCA808X_CDT_STATUS_STAT_SAME_SHORT |\
+-									 QCA808X_CDT_STATUS_STAT_MDI2)
+-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL	QCA808X_CDT_STATUS_STAT_MDI3
+-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN		(QCA808X_CDT_STATUS_STAT_SAME_OPEN |\
+-									 QCA808X_CDT_STATUS_STAT_MDI3)
+-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT	(QCA808X_CDT_STATUS_STAT_SAME_SHORT |\
+-									 QCA808X_CDT_STATUS_STAT_MDI3)
+-
+-/* Added for reference of existence but should be handled by wait_for_completion already */
+-#define QCA808X_CDT_STATUS_STAT_BUSY		(BIT(1) | BIT(3))
+-
+ #define QCA808X_MMD7_LED_GLOBAL			0x8073
+ #define QCA808X_LED_BLINK_1			GENMASK(11, 6)
+ #define QCA808X_LED_BLINK_2			GENMASK(5, 0)
+@@ -406,86 +356,6 @@ static int qca808x_soft_reset(struct phy
+ 	return ret;
+ }
+ 
+-static bool qca808x_cdt_fault_length_valid(int cdt_code)
+-{
+-	switch (cdt_code) {
+-	case QCA808X_CDT_STATUS_STAT_SAME_SHORT:
+-	case QCA808X_CDT_STATUS_STAT_SAME_OPEN:
+-	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL:
+-	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN:
+-	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT:
+-	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL:
+-	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN:
+-	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT:
+-	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL:
+-	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN:
+-	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT:
+-		return true;
+-	default:
+-		return false;
+-	}
+-}
+-
+-static int qca808x_cable_test_result_trans(int cdt_code)
+-{
+-	switch (cdt_code) {
+-	case QCA808X_CDT_STATUS_STAT_NORMAL:
+-		return ETHTOOL_A_CABLE_RESULT_CODE_OK;
+-	case QCA808X_CDT_STATUS_STAT_SAME_SHORT:
+-		return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT;
+-	case QCA808X_CDT_STATUS_STAT_SAME_OPEN:
+-		return ETHTOOL_A_CABLE_RESULT_CODE_OPEN;
+-	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL:
+-	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN:
+-	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT:
+-	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL:
+-	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN:
+-	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT:
+-	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL:
+-	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN:
+-	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT:
+-		return ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT;
+-	case QCA808X_CDT_STATUS_STAT_FAIL:
+-	default:
+-		return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC;
+-	}
+-}
+-
+-static int qca808x_cdt_fault_length(struct phy_device *phydev, int pair,
+-				    int result)
+-{
+-	int val;
+-	u32 cdt_length_reg = 0;
+-
+-	switch (pair) {
+-	case ETHTOOL_A_CABLE_PAIR_A:
+-		cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_A;
+-		break;
+-	case ETHTOOL_A_CABLE_PAIR_B:
+-		cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_B;
+-		break;
+-	case ETHTOOL_A_CABLE_PAIR_C:
+-		cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_C;
+-		break;
+-	case ETHTOOL_A_CABLE_PAIR_D:
+-		cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_D;
+-		break;
+-	default:
+-		return -EINVAL;
+-	}
+-
+-	val = phy_read_mmd(phydev, MDIO_MMD_PCS, cdt_length_reg);
+-	if (val < 0)
+-		return val;
+-
+-	if (result == ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT)
+-		val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_SAME_SHORT, val);
+-	else
+-		val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT, val);
+-
+-	return at803x_cdt_fault_length(val);
+-}
+-
+ static int qca808x_cable_test_start(struct phy_device *phydev)
+ {
+ 	int ret;
+@@ -526,81 +396,6 @@ static int qca808x_cable_test_start(stru
+ 
+ 	return 0;
+ }
+-
+-static int qca808x_cable_test_get_pair_status(struct phy_device *phydev, u8 pair,
+-					      u16 status)
+-{
+-	int length, result;
+-	u16 pair_code;
+-
+-	switch (pair) {
+-	case ETHTOOL_A_CABLE_PAIR_A:
+-		pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_A, status);
+-		break;
+-	case ETHTOOL_A_CABLE_PAIR_B:
+-		pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_B, status);
+-		break;
+-	case ETHTOOL_A_CABLE_PAIR_C:
+-		pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_C, status);
+-		break;
+-	case ETHTOOL_A_CABLE_PAIR_D:
+-		pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_D, status);
+-		break;
+-	default:
+-		return -EINVAL;
+-	}
+-
+-	result = qca808x_cable_test_result_trans(pair_code);
+-	ethnl_cable_test_result(phydev, pair, result);
+-
+-	if (qca808x_cdt_fault_length_valid(pair_code)) {
+-		length = qca808x_cdt_fault_length(phydev, pair, result);
+-		ethnl_cable_test_fault_length(phydev, pair, length);
+-	}
+-
+-	return 0;
+-}
+-
+-static int qca808x_cable_test_get_status(struct phy_device *phydev, bool *finished)
+-{
+-	int ret, val;
+-
+-	*finished = false;
+-
+-	val = QCA808X_CDT_ENABLE_TEST |
+-	      QCA808X_CDT_LENGTH_UNIT;
+-	ret = at803x_cdt_start(phydev, val);
+-	if (ret)
+-		return ret;
+-
+-	ret = at803x_cdt_wait_for_completion(phydev, QCA808X_CDT_ENABLE_TEST);
+-	if (ret)
+-		return ret;
+-
+-	val = phy_read_mmd(phydev, MDIO_MMD_PCS, QCA808X_MMD3_CDT_STATUS);
+-	if (val < 0)
+-		return val;
+-
+-	ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_A, val);
+-	if (ret)
+-		return ret;
+-
+-	ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_B, val);
+-	if (ret)
+-		return ret;
+-
+-	ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_C, val);
+-	if (ret)
+-		return ret;
+-
+-	ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_D, val);
+-	if (ret)
+-		return ret;
+-
+-	*finished = true;
+-
+-	return 0;
+-}
+ 
+ static int qca808x_get_features(struct phy_device *phydev)
+ {
+--- a/drivers/net/phy/qcom/qcom-phy-lib.c
++++ b/drivers/net/phy/qcom/qcom-phy-lib.c
+@@ -5,6 +5,7 @@
+ 
+ #include <linux/netdevice.h>
+ #include <linux/etherdevice.h>
++#include <linux/ethtool_netlink.h>
+ 
+ #include "qcom.h"
+ 
+@@ -311,6 +312,42 @@ int at803x_prepare_config_aneg(struct ph
+ }
+ EXPORT_SYMBOL_GPL(at803x_prepare_config_aneg);
+ 
++int at803x_read_status(struct phy_device *phydev)
++{
++	struct at803x_ss_mask ss_mask = { 0 };
++	int err, old_link = phydev->link;
++
++	/* Update the link, but return if there was an error */
++	err = genphy_update_link(phydev);
++	if (err)
++		return err;
++
++	/* why bother the PHY if nothing can have changed */
++	if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link)
++		return 0;
++
++	phydev->speed = SPEED_UNKNOWN;
++	phydev->duplex = DUPLEX_UNKNOWN;
++	phydev->pause = 0;
++	phydev->asym_pause = 0;
++
++	err = genphy_read_lpa(phydev);
++	if (err < 0)
++		return err;
++
++	ss_mask.speed_mask = AT803X_SS_SPEED_MASK;
++	ss_mask.speed_shift = __bf_shf(AT803X_SS_SPEED_MASK);
++	err = at803x_read_specific_status(phydev, ss_mask);
++	if (err < 0)
++		return err;
++
++	if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete)
++		phy_resolve_aneg_pause(phydev);
++
++	return 0;
++}
++EXPORT_SYMBOL_GPL(at803x_read_status);
++
+ static int at803x_get_downshift(struct phy_device *phydev, u8 *d)
+ {
+ 	int val;
+@@ -427,3 +464,159 @@ int at803x_cdt_wait_for_completion(struc
+ 	return ret < 0 ? ret : 0;
+ }
+ EXPORT_SYMBOL_GPL(at803x_cdt_wait_for_completion);
++
++static bool qca808x_cdt_fault_length_valid(int cdt_code)
++{
++	switch (cdt_code) {
++	case QCA808X_CDT_STATUS_STAT_SAME_SHORT:
++	case QCA808X_CDT_STATUS_STAT_SAME_OPEN:
++	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL:
++	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN:
++	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT:
++	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL:
++	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN:
++	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT:
++	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL:
++	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN:
++	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT:
++		return true;
++	default:
++		return false;
++	}
++}
++
++static int qca808x_cable_test_result_trans(int cdt_code)
++{
++	switch (cdt_code) {
++	case QCA808X_CDT_STATUS_STAT_NORMAL:
++		return ETHTOOL_A_CABLE_RESULT_CODE_OK;
++	case QCA808X_CDT_STATUS_STAT_SAME_SHORT:
++		return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT;
++	case QCA808X_CDT_STATUS_STAT_SAME_OPEN:
++		return ETHTOOL_A_CABLE_RESULT_CODE_OPEN;
++	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL:
++	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN:
++	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT:
++	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL:
++	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN:
++	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT:
++	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL:
++	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN:
++	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT:
++		return ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT;
++	case QCA808X_CDT_STATUS_STAT_FAIL:
++	default:
++		return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC;
++	}
++}
++
++static int qca808x_cdt_fault_length(struct phy_device *phydev, int pair,
++				    int result)
++{
++	int val;
++	u32 cdt_length_reg = 0;
++
++	switch (pair) {
++	case ETHTOOL_A_CABLE_PAIR_A:
++		cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_A;
++		break;
++	case ETHTOOL_A_CABLE_PAIR_B:
++		cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_B;
++		break;
++	case ETHTOOL_A_CABLE_PAIR_C:
++		cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_C;
++		break;
++	case ETHTOOL_A_CABLE_PAIR_D:
++		cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_D;
++		break;
++	default:
++		return -EINVAL;
++	}
++
++	val = phy_read_mmd(phydev, MDIO_MMD_PCS, cdt_length_reg);
++	if (val < 0)
++		return val;
++
++	if (result == ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT)
++		val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_SAME_SHORT, val);
++	else
++		val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT, val);
++
++	return at803x_cdt_fault_length(val);
++}
++
++static int qca808x_cable_test_get_pair_status(struct phy_device *phydev, u8 pair,
++					      u16 status)
++{
++	int length, result;
++	u16 pair_code;
++
++	switch (pair) {
++	case ETHTOOL_A_CABLE_PAIR_A:
++		pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_A, status);
++		break;
++	case ETHTOOL_A_CABLE_PAIR_B:
++		pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_B, status);
++		break;
++	case ETHTOOL_A_CABLE_PAIR_C:
++		pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_C, status);
++		break;
++	case ETHTOOL_A_CABLE_PAIR_D:
++		pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_D, status);
++		break;
++	default:
++		return -EINVAL;
++	}
++
++	result = qca808x_cable_test_result_trans(pair_code);
++	ethnl_cable_test_result(phydev, pair, result);
++
++	if (qca808x_cdt_fault_length_valid(pair_code)) {
++		length = qca808x_cdt_fault_length(phydev, pair, result);
++		ethnl_cable_test_fault_length(phydev, pair, length);
++	}
++
++	return 0;
++}
++
++int qca808x_cable_test_get_status(struct phy_device *phydev, bool *finished)
++{
++	int ret, val;
++
++	*finished = false;
++
++	val = QCA808X_CDT_ENABLE_TEST |
++	      QCA808X_CDT_LENGTH_UNIT;
++	ret = at803x_cdt_start(phydev, val);
++	if (ret)
++		return ret;
++
++	ret = at803x_cdt_wait_for_completion(phydev, QCA808X_CDT_ENABLE_TEST);
++	if (ret)
++		return ret;
++
++	val = phy_read_mmd(phydev, MDIO_MMD_PCS, QCA808X_MMD3_CDT_STATUS);
++	if (val < 0)
++		return val;
++
++	ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_A, val);
++	if (ret)
++		return ret;
++
++	ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_B, val);
++	if (ret)
++		return ret;
++
++	ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_C, val);
++	if (ret)
++		return ret;
++
++	ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_D, val);
++	if (ret)
++		return ret;
++
++	*finished = true;
++
++	return 0;
++}
++EXPORT_SYMBOL_GPL(qca808x_cable_test_get_status);
+--- a/drivers/net/phy/qcom/qcom.h
++++ b/drivers/net/phy/qcom/qcom.h
+@@ -54,6 +54,55 @@
+ #define AT803X_CDT_STATUS_STAT_MASK		GENMASK(9, 8)
+ #define AT803X_CDT_STATUS_DELTA_TIME_MASK	GENMASK(7, 0)
+ 
++#define QCA808X_CDT_ENABLE_TEST			BIT(15)
++#define QCA808X_CDT_INTER_CHECK_DIS		BIT(13)
++#define QCA808X_CDT_STATUS			BIT(11)
++#define QCA808X_CDT_LENGTH_UNIT			BIT(10)
++
++#define QCA808X_MMD3_CDT_STATUS			0x8064
++#define QCA808X_MMD3_CDT_DIAG_PAIR_A		0x8065
++#define QCA808X_MMD3_CDT_DIAG_PAIR_B		0x8066
++#define QCA808X_MMD3_CDT_DIAG_PAIR_C		0x8067
++#define QCA808X_MMD3_CDT_DIAG_PAIR_D		0x8068
++#define QCA808X_CDT_DIAG_LENGTH_SAME_SHORT	GENMASK(15, 8)
++#define QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT	GENMASK(7, 0)
++
++#define QCA808X_CDT_CODE_PAIR_A			GENMASK(15, 12)
++#define QCA808X_CDT_CODE_PAIR_B			GENMASK(11, 8)
++#define QCA808X_CDT_CODE_PAIR_C			GENMASK(7, 4)
++#define QCA808X_CDT_CODE_PAIR_D			GENMASK(3, 0)
++
++#define QCA808X_CDT_STATUS_STAT_TYPE		GENMASK(1, 0)
++#define QCA808X_CDT_STATUS_STAT_FAIL		FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 0)
++#define QCA808X_CDT_STATUS_STAT_NORMAL		FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 1)
++#define QCA808X_CDT_STATUS_STAT_SAME_OPEN	FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 2)
++#define QCA808X_CDT_STATUS_STAT_SAME_SHORT	FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 3)
++
++#define QCA808X_CDT_STATUS_STAT_MDI		GENMASK(3, 2)
++#define QCA808X_CDT_STATUS_STAT_MDI1		FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 1)
++#define QCA808X_CDT_STATUS_STAT_MDI2		FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 2)
++#define QCA808X_CDT_STATUS_STAT_MDI3		FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 3)
++
++/* NORMAL are MDI with type set to 0 */
++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL	QCA808X_CDT_STATUS_STAT_MDI1
++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN		(QCA808X_CDT_STATUS_STAT_SAME_OPEN |\
++									 QCA808X_CDT_STATUS_STAT_MDI1)
++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT	(QCA808X_CDT_STATUS_STAT_SAME_SHORT |\
++									 QCA808X_CDT_STATUS_STAT_MDI1)
++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL	QCA808X_CDT_STATUS_STAT_MDI2
++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN		(QCA808X_CDT_STATUS_STAT_SAME_OPEN |\
++									 QCA808X_CDT_STATUS_STAT_MDI2)
++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT	(QCA808X_CDT_STATUS_STAT_SAME_SHORT |\
++									 QCA808X_CDT_STATUS_STAT_MDI2)
++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL	QCA808X_CDT_STATUS_STAT_MDI3
++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN		(QCA808X_CDT_STATUS_STAT_SAME_OPEN |\
++									 QCA808X_CDT_STATUS_STAT_MDI3)
++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT	(QCA808X_CDT_STATUS_STAT_SAME_SHORT |\
++									 QCA808X_CDT_STATUS_STAT_MDI3)
++
++/* Added for reference of existence but should be handled by wait_for_completion already */
++#define QCA808X_CDT_STATUS_STAT_BUSY		(BIT(1) | BIT(3))
++
+ #define AT803X_LOC_MAC_ADDR_0_15_OFFSET		0x804C
+ #define AT803X_LOC_MAC_ADDR_16_31_OFFSET	0x804B
+ #define AT803X_LOC_MAC_ADDR_32_47_OFFSET	0x804A
+@@ -110,6 +159,7 @@ int at803x_read_specific_status(struct p
+ 				struct at803x_ss_mask ss_mask);
+ int at803x_config_mdix(struct phy_device *phydev, u8 ctrl);
+ int at803x_prepare_config_aneg(struct phy_device *phydev);
++int at803x_read_status(struct phy_device *phydev);
+ int at803x_get_tunable(struct phy_device *phydev,
+ 		       struct ethtool_tunable *tuna, void *data);
+ int at803x_set_tunable(struct phy_device *phydev,
+@@ -118,3 +168,4 @@ int at803x_cdt_fault_length(int dt);
+ int at803x_cdt_start(struct phy_device *phydev, u32 cdt_start);
+ int at803x_cdt_wait_for_completion(struct phy_device *phydev,
+ 				   u32 cdt_en);
++int qca808x_cable_test_get_status(struct phy_device *phydev, bool *finished);
diff --git a/target/linux/generic/backport-6.1/716-v6.9-06-net-phy-provide-whether-link-has-changed-in-c37_read.patch b/target/linux/generic/backport-6.1/716-v6.9-06-net-phy-provide-whether-link-has-changed-in-c37_read.patch
new file mode 100644
index 0000000000..4371599f4a
--- /dev/null
+++ b/target/linux/generic/backport-6.1/716-v6.9-06-net-phy-provide-whether-link-has-changed-in-c37_read.patch
@@ -0,0 +1,100 @@
+From 9b1d5e055508393561e26bd1720f4c2639b03b1a Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth at gmail.com>
+Date: Tue, 6 Feb 2024 18:31:09 +0100
+Subject: [PATCH 06/10] net: phy: provide whether link has changed in
+ c37_read_status
+
+Some PHY driver might require additional regs call after
+genphy_c37_read_status() is called.
+
+Expand genphy_c37_read_status to provide a bool wheather the link has
+changed or not to permit PHY driver to skip additional regs call if
+nothing has changed.
+
+Every user of genphy_c37_read_status() is updated with the new
+additional bool.
+
+Signed-off-by: Christian Marangi <ansuelsmth at gmail.com>
+Reviewed-by: Andrew Lunn <andrew at lunn.ch>
+Signed-off-by: David S. Miller <davem at davemloft.net>
+---
+ drivers/net/phy/broadcom.c    |  3 ++-
+ drivers/net/phy/phy_device.c  | 11 +++++++++--
+ drivers/net/phy/qcom/at803x.c |  3 ++-
+ include/linux/phy.h           |  2 +-
+ 4 files changed, 14 insertions(+), 5 deletions(-)
+
+--- a/drivers/net/phy/broadcom.c
++++ b/drivers/net/phy/broadcom.c
+@@ -609,10 +609,11 @@ static int bcm54616s_config_aneg(struct
+ static int bcm54616s_read_status(struct phy_device *phydev)
+ {
+ 	struct bcm54616s_phy_priv *priv = phydev->priv;
++	bool changed;
+ 	int err;
+ 
+ 	if (priv->mode_1000bx_en)
+-		err = genphy_c37_read_status(phydev);
++		err = genphy_c37_read_status(phydev, &changed);
+ 	else
+ 		err = genphy_read_status(phydev);
+ 
+--- a/drivers/net/phy/phy_device.c
++++ b/drivers/net/phy/phy_device.c
+@@ -2551,12 +2551,15 @@ EXPORT_SYMBOL(genphy_read_status);
+ /**
+  * genphy_c37_read_status - check the link status and update current link state
+  * @phydev: target phy_device struct
++ * @changed: pointer where to store if link changed
+  *
+  * Description: Check the link, then figure out the current state
+  *   by comparing what we advertise with what the link partner
+  *   advertises. This function is for Clause 37 1000Base-X mode.
++ *
++ *   If link has changed, @changed is set to true, false otherwise.
+  */
+-int genphy_c37_read_status(struct phy_device *phydev)
++int genphy_c37_read_status(struct phy_device *phydev, bool *changed)
+ {
+ 	int lpa, err, old_link = phydev->link;
+ 
+@@ -2566,9 +2569,13 @@ int genphy_c37_read_status(struct phy_de
+ 		return err;
+ 
+ 	/* why bother the PHY if nothing can have changed */
+-	if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link)
++	if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link) {
++		*changed = false;
+ 		return 0;
++	}
+ 
++	/* Signal link has changed */
++	*changed = true;
+ 	phydev->duplex = DUPLEX_UNKNOWN;
+ 	phydev->pause = 0;
+ 	phydev->asym_pause = 0;
+--- a/drivers/net/phy/qcom/at803x.c
++++ b/drivers/net/phy/qcom/at803x.c
+@@ -912,9 +912,10 @@ static int at8031_config_intr(struct phy
+ static int at8031_read_status(struct phy_device *phydev)
+ {
+ 	struct at803x_priv *priv = phydev->priv;
++	bool changed;
+ 
+ 	if (priv->is_1000basex)
+-		return genphy_c37_read_status(phydev);
++		return genphy_c37_read_status(phydev, &changed);
+ 
+ 	return at803x_read_status(phydev);
+ }
+--- a/include/linux/phy.h
++++ b/include/linux/phy.h
+@@ -1660,7 +1660,7 @@ int genphy_write_mmd_unsupported(struct
+ 
+ /* Clause 37 */
+ int genphy_c37_config_aneg(struct phy_device *phydev);
+-int genphy_c37_read_status(struct phy_device *phydev);
++int genphy_c37_read_status(struct phy_device *phydev, bool *changed);
+ 
+ /* Clause 45 PHY */
+ int genphy_c45_restart_aneg(struct phy_device *phydev);
diff --git a/target/linux/generic/backport-6.1/716-v6.9-07-net-phy-qcom-add-support-for-QCA807x-PHY-Family.patch b/target/linux/generic/backport-6.1/716-v6.9-07-net-phy-qcom-add-support-for-QCA807x-PHY-Family.patch
new file mode 100644
index 0000000000..bbf0f76d68
--- /dev/null
+++ b/target/linux/generic/backport-6.1/716-v6.9-07-net-phy-qcom-add-support-for-QCA807x-PHY-Family.patch
@@ -0,0 +1,668 @@
+From d1cb613efbd3cd7d0c000167816beb3f248f5eb8 Mon Sep 17 00:00:00 2001
+From: Robert Marko <robert.marko at sartura.hr>
+Date: Tue, 6 Feb 2024 18:31:10 +0100
+Subject: [PATCH 07/10] net: phy: qcom: add support for QCA807x PHY Family
+
+This adds driver for the Qualcomm QCA8072 and QCA8075 PHY-s.
+
+They are 2 or 5 port IEEE 802.3 clause 22 compliant 10BASE-Te,
+100BASE-TX and 1000BASE-T PHY-s.
+
+They feature 2 SerDes, one for PSGMII or QSGMII connection with
+MAC, while second one is SGMII for connection to MAC or fiber.
+
+Both models have a combo port that supports 1000BASE-X and
+100BASE-FX fiber.
+
+PHY package can be configured in 3 mode following this table:
+
+              First Serdes mode       Second Serdes mode
+Option 1      PSGMII for copper       Disabled
+              ports 0-4
+Option 2      PSGMII for copper       1000BASE-X / 100BASE-FX
+              ports 0-4
+Option 3      QSGMII for copper       SGMII for
+              ports 0-3               copper port 4
+
+Each PHY inside of QCA807x series has 4 digitally controlled
+output only pins that natively drive LED-s.
+But some vendors used these to driver generic LED-s controlled
+by userspace, so lets enable registering each PHY as GPIO
+controller and add driver for it.
+
+These are commonly used in Qualcomm IPQ40xx, IPQ60xx and IPQ807x
+boards.
+
+Co-developed-by: Christian Marangi <ansuelsmth at gmail.com>
+Signed-off-by: Robert Marko <robert.marko at sartura.hr>
+Signed-off-by: Christian Marangi <ansuelsmth at gmail.com>
+Reviewed-by: Andrew Lunn <andrew at lunn.ch>
+Signed-off-by: David S. Miller <davem at davemloft.net>
+---
+ drivers/net/phy/qcom/Kconfig   |   8 +
+ drivers/net/phy/qcom/Makefile  |   1 +
+ drivers/net/phy/qcom/qca807x.c | 597 +++++++++++++++++++++++++++++++++
+ 3 files changed, 606 insertions(+)
+ create mode 100644 drivers/net/phy/qcom/qca807x.c
+
+--- a/drivers/net/phy/qcom/Kconfig
++++ b/drivers/net/phy/qcom/Kconfig
+@@ -20,3 +20,11 @@ config QCA808X_PHY
+ 	select QCOM_NET_PHYLIB
+ 	help
+ 	  Currently supports the QCA8081 model
++
++config QCA807X_PHY
++	tristate "Qualcomm QCA807x PHYs"
++	select QCOM_NET_PHYLIB
++	depends on OF_MDIO
++	help
++	  Currently supports the Qualcomm QCA8072, QCA8075 and the PSGMII
++	  control PHY.
+--- a/drivers/net/phy/qcom/Makefile
++++ b/drivers/net/phy/qcom/Makefile
+@@ -3,3 +3,4 @@ obj-$(CONFIG_QCOM_NET_PHYLIB)	+= qcom-ph
+ obj-$(CONFIG_AT803X_PHY)	+= at803x.o
+ obj-$(CONFIG_QCA83XX_PHY)	+= qca83xx.o
+ obj-$(CONFIG_QCA808X_PHY)	+= qca808x.o
++obj-$(CONFIG_QCA807X_PHY)	+= qca807x.o
+--- /dev/null
++++ b/drivers/net/phy/qcom/qca807x.c
+@@ -0,0 +1,597 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * Copyright (c) 2023 Sartura Ltd.
++ *
++ * Author: Robert Marko <robert.marko at sartura.hr>
++ *         Christian Marangi <ansuelsmth at gmail.com>
++ *
++ * Qualcomm QCA8072 and QCA8075 PHY driver
++ */
++
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/phy.h>
++#include <linux/bitfield.h>
++#include <linux/gpio/driver.h>
++#include <linux/sfp.h>
++
++#include "qcom.h"
++
++#define QCA807X_CHIP_CONFIGURATION				0x1f
++#define QCA807X_BT_BX_REG_SEL					BIT(15)
++#define QCA807X_BT_BX_REG_SEL_FIBER				0
++#define QCA807X_BT_BX_REG_SEL_COPPER				1
++#define QCA807X_CHIP_CONFIGURATION_MODE_CFG_MASK		GENMASK(3, 0)
++#define QCA807X_CHIP_CONFIGURATION_MODE_QSGMII_SGMII		4
++#define QCA807X_CHIP_CONFIGURATION_MODE_PSGMII_FIBER		3
++#define QCA807X_CHIP_CONFIGURATION_MODE_PSGMII_ALL_COPPER	0
++
++#define QCA807X_MEDIA_SELECT_STATUS				0x1a
++#define QCA807X_MEDIA_DETECTED_COPPER				BIT(5)
++#define QCA807X_MEDIA_DETECTED_1000_BASE_X			BIT(4)
++#define QCA807X_MEDIA_DETECTED_100_BASE_FX			BIT(3)
++
++#define QCA807X_MMD7_FIBER_MODE_AUTO_DETECTION			0x807e
++#define QCA807X_MMD7_FIBER_MODE_AUTO_DETECTION_EN		BIT(0)
++
++#define QCA807X_MMD7_1000BASE_T_POWER_SAVE_PER_CABLE_LENGTH	0x801a
++#define QCA807X_CONTROL_DAC_MASK				GENMASK(2, 0)
++/* List of tweaks enabled by this bit:
++ * - With both FULL amplitude and FULL bias current: bias current
++ *   is set to half.
++ * - With only DSP amplitude: bias current is set to half and
++ *   is set to 1/4 with cable < 10m.
++ * - With DSP bias current (included both DSP amplitude and
++ *   DSP bias current): bias current is half the detected current
++ *   with cable < 10m.
++ */
++#define QCA807X_CONTROL_DAC_BIAS_CURRENT_TWEAK			BIT(2)
++#define QCA807X_CONTROL_DAC_DSP_BIAS_CURRENT			BIT(1)
++#define QCA807X_CONTROL_DAC_DSP_AMPLITUDE			BIT(0)
++
++#define QCA807X_MMD7_LED_100N_1				0x8074
++#define QCA807X_MMD7_LED_100N_2				0x8075
++#define QCA807X_MMD7_LED_1000N_1			0x8076
++#define QCA807X_MMD7_LED_1000N_2			0x8077
++
++#define QCA807X_MMD7_LED_CTRL(x)			(0x8074 + ((x) * 2))
++#define QCA807X_MMD7_LED_FORCE_CTRL(x)			(0x8075 + ((x) * 2))
++
++#define QCA807X_GPIO_FORCE_EN				BIT(15)
++#define QCA807X_GPIO_FORCE_MODE_MASK			GENMASK(14, 13)
++
++#define QCA807X_FUNCTION_CONTROL			0x10
++#define QCA807X_FC_MDI_CROSSOVER_MODE_MASK		GENMASK(6, 5)
++#define QCA807X_FC_MDI_CROSSOVER_AUTO			3
++#define QCA807X_FC_MDI_CROSSOVER_MANUAL_MDIX		1
++#define QCA807X_FC_MDI_CROSSOVER_MANUAL_MDI		0
++
++/* PQSGMII Analog PHY specific */
++#define PQSGMII_CTRL_REG				0x0
++#define PQSGMII_ANALOG_SW_RESET				BIT(6)
++#define PQSGMII_DRIVE_CONTROL_1				0xb
++#define PQSGMII_TX_DRIVER_MASK				GENMASK(7, 4)
++#define PQSGMII_TX_DRIVER_140MV				0x0
++#define PQSGMII_TX_DRIVER_160MV				0x1
++#define PQSGMII_TX_DRIVER_180MV				0x2
++#define PQSGMII_TX_DRIVER_200MV				0x3
++#define PQSGMII_TX_DRIVER_220MV				0x4
++#define PQSGMII_TX_DRIVER_240MV				0x5
++#define PQSGMII_TX_DRIVER_260MV				0x6
++#define PQSGMII_TX_DRIVER_280MV				0x7
++#define PQSGMII_TX_DRIVER_300MV				0x8
++#define PQSGMII_TX_DRIVER_320MV				0x9
++#define PQSGMII_TX_DRIVER_400MV				0xa
++#define PQSGMII_TX_DRIVER_500MV				0xb
++#define PQSGMII_TX_DRIVER_600MV				0xc
++#define PQSGMII_MODE_CTRL				0x6d
++#define PQSGMII_MODE_CTRL_AZ_WORKAROUND_MASK		BIT(0)
++#define PQSGMII_MMD3_SERDES_CONTROL			0x805a
++
++#define PHY_ID_QCA8072		0x004dd0b2
++#define PHY_ID_QCA8075		0x004dd0b1
++
++#define QCA807X_COMBO_ADDR_OFFSET			4
++#define QCA807X_PQSGMII_ADDR_OFFSET			5
++#define SERDES_RESET_SLEEP				100
++
++enum qca807x_global_phy {
++	QCA807X_COMBO_ADDR = 4,
++	QCA807X_PQSGMII_ADDR = 5,
++};
++
++struct qca807x_shared_priv {
++	unsigned int package_mode;
++	u32 tx_drive_strength;
++};
++
++struct qca807x_gpio_priv {
++	struct phy_device *phy;
++};
++
++struct qca807x_priv {
++	bool dac_full_amplitude;
++	bool dac_full_bias_current;
++	bool dac_disable_bias_current_tweak;
++};
++
++static int qca807x_cable_test_start(struct phy_device *phydev)
++{
++	/* we do all the (time consuming) work later */
++	return 0;
++}
++
++#ifdef CONFIG_GPIOLIB
++static int qca807x_gpio_get_direction(struct gpio_chip *gc, unsigned int offset)
++{
++	return GPIO_LINE_DIRECTION_OUT;
++}
++
++static int qca807x_gpio_get(struct gpio_chip *gc, unsigned int offset)
++{
++	struct qca807x_gpio_priv *priv = gpiochip_get_data(gc);
++	u16 reg;
++	int val;
++
++	reg = QCA807X_MMD7_LED_FORCE_CTRL(offset);
++	val = phy_read_mmd(priv->phy, MDIO_MMD_AN, reg);
++
++	return FIELD_GET(QCA807X_GPIO_FORCE_MODE_MASK, val);
++}
++
++static void qca807x_gpio_set(struct gpio_chip *gc, unsigned int offset, int value)
++{
++	struct qca807x_gpio_priv *priv = gpiochip_get_data(gc);
++	u16 reg;
++	int val;
++
++	reg = QCA807X_MMD7_LED_FORCE_CTRL(offset);
++
++	val = phy_read_mmd(priv->phy, MDIO_MMD_AN, reg);
++	val &= ~QCA807X_GPIO_FORCE_MODE_MASK;
++	val |= QCA807X_GPIO_FORCE_EN;
++	val |= FIELD_PREP(QCA807X_GPIO_FORCE_MODE_MASK, value);
++
++	phy_write_mmd(priv->phy, MDIO_MMD_AN, reg, val);
++}
++
++static int qca807x_gpio_dir_out(struct gpio_chip *gc, unsigned int offset, int value)
++{
++	qca807x_gpio_set(gc, offset, value);
++
++	return 0;
++}
++
++static int qca807x_gpio(struct phy_device *phydev)
++{
++	struct device *dev = &phydev->mdio.dev;
++	struct qca807x_gpio_priv *priv;
++	struct gpio_chip *gc;
++
++	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
++	if (!priv)
++		return -ENOMEM;
++
++	priv->phy = phydev;
++
++	gc = devm_kzalloc(dev, sizeof(*gc), GFP_KERNEL);
++	if (!gc)
++		return -ENOMEM;
++
++	gc->label = dev_name(dev);
++	gc->base = -1;
++	gc->ngpio = 2;
++	gc->parent = dev;
++	gc->owner = THIS_MODULE;
++	gc->can_sleep = true;
++	gc->get_direction = qca807x_gpio_get_direction;
++	gc->direction_output = qca807x_gpio_dir_out;
++	gc->get = qca807x_gpio_get;
++	gc->set = qca807x_gpio_set;
++
++	return devm_gpiochip_add_data(dev, gc, priv);
++}
++#endif
++
++static int qca807x_read_fiber_status(struct phy_device *phydev)
++{
++	bool changed;
++	int ss, err;
++
++	err = genphy_c37_read_status(phydev, &changed);
++	if (err || !changed)
++		return err;
++
++	/* Read the QCA807x PHY-Specific Status register fiber page,
++	 * which indicates the speed and duplex that the PHY is actually
++	 * using, irrespective of whether we are in autoneg mode or not.
++	 */
++	ss = phy_read(phydev, AT803X_SPECIFIC_STATUS);
++	if (ss < 0)
++		return ss;
++
++	phydev->speed = SPEED_UNKNOWN;
++	phydev->duplex = DUPLEX_UNKNOWN;
++	if (ss & AT803X_SS_SPEED_DUPLEX_RESOLVED) {
++		switch (FIELD_GET(AT803X_SS_SPEED_MASK, ss)) {
++		case AT803X_SS_SPEED_100:
++			phydev->speed = SPEED_100;
++			break;
++		case AT803X_SS_SPEED_1000:
++			phydev->speed = SPEED_1000;
++			break;
++		}
++
++		if (ss & AT803X_SS_DUPLEX)
++			phydev->duplex = DUPLEX_FULL;
++		else
++			phydev->duplex = DUPLEX_HALF;
++	}
++
++	return 0;
++}
++
++static int qca807x_read_status(struct phy_device *phydev)
++{
++	if (linkmode_test_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, phydev->supported)) {
++		switch (phydev->port) {
++		case PORT_FIBRE:
++			return qca807x_read_fiber_status(phydev);
++		case PORT_TP:
++			return at803x_read_status(phydev);
++		default:
++			return -EINVAL;
++		}
++	}
++
++	return at803x_read_status(phydev);
++}
++
++static int qca807x_phy_package_probe_once(struct phy_device *phydev)
++{
++	struct phy_package_shared *shared = phydev->shared;
++	struct qca807x_shared_priv *priv = shared->priv;
++	unsigned int tx_drive_strength;
++	const char *package_mode_name;
++
++	/* Default to 600mw if not defined */
++	if (of_property_read_u32(shared->np, "qcom,tx-drive-strength-milliwatt",
++				 &tx_drive_strength))
++		tx_drive_strength = 600;
++
++	switch (tx_drive_strength) {
++	case 140:
++		priv->tx_drive_strength = PQSGMII_TX_DRIVER_140MV;
++		break;
++	case 160:
++		priv->tx_drive_strength = PQSGMII_TX_DRIVER_160MV;
++		break;
++	case 180:
++		priv->tx_drive_strength = PQSGMII_TX_DRIVER_180MV;
++		break;
++	case 200:
++		priv->tx_drive_strength = PQSGMII_TX_DRIVER_200MV;
++		break;
++	case 220:
++		priv->tx_drive_strength = PQSGMII_TX_DRIVER_220MV;
++		break;
++	case 240:
++		priv->tx_drive_strength = PQSGMII_TX_DRIVER_240MV;
++		break;
++	case 260:
++		priv->tx_drive_strength = PQSGMII_TX_DRIVER_260MV;
++		break;
++	case 280:
++		priv->tx_drive_strength = PQSGMII_TX_DRIVER_280MV;
++		break;
++	case 300:
++		priv->tx_drive_strength = PQSGMII_TX_DRIVER_300MV;
++		break;
++	case 320:
++		priv->tx_drive_strength = PQSGMII_TX_DRIVER_320MV;
++		break;
++	case 400:
++		priv->tx_drive_strength = PQSGMII_TX_DRIVER_400MV;
++		break;
++	case 500:
++		priv->tx_drive_strength = PQSGMII_TX_DRIVER_500MV;
++		break;
++	case 600:
++		priv->tx_drive_strength = PQSGMII_TX_DRIVER_600MV;
++		break;
++	default:
++		return -EINVAL;
++	}
++
++	priv->package_mode = PHY_INTERFACE_MODE_NA;
++	if (!of_property_read_string(shared->np, "qcom,package-mode",
++				     &package_mode_name)) {
++		if (!strcasecmp(package_mode_name,
++				phy_modes(PHY_INTERFACE_MODE_PSGMII)))
++			priv->package_mode = PHY_INTERFACE_MODE_PSGMII;
++		else if (!strcasecmp(package_mode_name,
++				     phy_modes(PHY_INTERFACE_MODE_QSGMII)))
++			priv->package_mode = PHY_INTERFACE_MODE_QSGMII;
++		else
++			return -EINVAL;
++	}
++
++	return 0;
++}
++
++static int qca807x_phy_package_config_init_once(struct phy_device *phydev)
++{
++	struct phy_package_shared *shared = phydev->shared;
++	struct qca807x_shared_priv *priv = shared->priv;
++	int val, ret;
++
++	phy_lock_mdio_bus(phydev);
++
++	/* Set correct PHY package mode */
++	val = __phy_package_read(phydev, QCA807X_COMBO_ADDR,
++				 QCA807X_CHIP_CONFIGURATION);
++	val &= ~QCA807X_CHIP_CONFIGURATION_MODE_CFG_MASK;
++	/* package_mode can be QSGMII or PSGMII and we validate
++	 * this in probe_once.
++	 * With package_mode to NA, we default to PSGMII.
++	 */
++	switch (priv->package_mode) {
++	case PHY_INTERFACE_MODE_QSGMII:
++		val |= QCA807X_CHIP_CONFIGURATION_MODE_QSGMII_SGMII;
++		break;
++	case PHY_INTERFACE_MODE_PSGMII:
++	default:
++		val |= QCA807X_CHIP_CONFIGURATION_MODE_PSGMII_ALL_COPPER;
++	}
++	ret = __phy_package_write(phydev, QCA807X_COMBO_ADDR,
++				  QCA807X_CHIP_CONFIGURATION, val);
++	if (ret)
++		goto exit;
++
++	/* After mode change Serdes reset is required */
++	val = __phy_package_read(phydev, QCA807X_PQSGMII_ADDR,
++				 PQSGMII_CTRL_REG);
++	val &= ~PQSGMII_ANALOG_SW_RESET;
++	ret = __phy_package_write(phydev, QCA807X_PQSGMII_ADDR,
++				  PQSGMII_CTRL_REG, val);
++	if (ret)
++		goto exit;
++
++	msleep(SERDES_RESET_SLEEP);
++
++	val = __phy_package_read(phydev, QCA807X_PQSGMII_ADDR,
++				 PQSGMII_CTRL_REG);
++	val |= PQSGMII_ANALOG_SW_RESET;
++	ret = __phy_package_write(phydev, QCA807X_PQSGMII_ADDR,
++				  PQSGMII_CTRL_REG, val);
++	if (ret)
++		goto exit;
++
++	/* Workaround to enable AZ transmitting ability */
++	val = __phy_package_read_mmd(phydev, QCA807X_PQSGMII_ADDR,
++				     MDIO_MMD_PMAPMD, PQSGMII_MODE_CTRL);
++	val &= ~PQSGMII_MODE_CTRL_AZ_WORKAROUND_MASK;
++	ret = __phy_package_write_mmd(phydev, QCA807X_PQSGMII_ADDR,
++				      MDIO_MMD_PMAPMD, PQSGMII_MODE_CTRL, val);
++	if (ret)
++		goto exit;
++
++	/* Set PQSGMII TX AMP strength */
++	val = __phy_package_read(phydev, QCA807X_PQSGMII_ADDR,
++				 PQSGMII_DRIVE_CONTROL_1);
++	val &= ~PQSGMII_TX_DRIVER_MASK;
++	val |= FIELD_PREP(PQSGMII_TX_DRIVER_MASK, priv->tx_drive_strength);
++	ret = __phy_package_write(phydev, QCA807X_PQSGMII_ADDR,
++				  PQSGMII_DRIVE_CONTROL_1, val);
++	if (ret)
++		goto exit;
++
++	/* Prevent PSGMII going into hibernation via PSGMII self test */
++	val = __phy_package_read_mmd(phydev, QCA807X_COMBO_ADDR,
++				     MDIO_MMD_PCS, PQSGMII_MMD3_SERDES_CONTROL);
++	val &= ~BIT(1);
++	ret = __phy_package_write_mmd(phydev, QCA807X_COMBO_ADDR,
++				      MDIO_MMD_PCS, PQSGMII_MMD3_SERDES_CONTROL, val);
++
++exit:
++	phy_unlock_mdio_bus(phydev);
++
++	return ret;
++}
++
++static int qca807x_sfp_insert(void *upstream, const struct sfp_eeprom_id *id)
++{
++	struct phy_device *phydev = upstream;
++	__ETHTOOL_DECLARE_LINK_MODE_MASK(support) = { 0, };
++	phy_interface_t iface;
++	int ret;
++	DECLARE_PHY_INTERFACE_MASK(interfaces);
++
++	sfp_parse_support(phydev->sfp_bus, id, support, interfaces);
++	iface = sfp_select_interface(phydev->sfp_bus, support);
++
++	dev_info(&phydev->mdio.dev, "%s SFP module inserted\n", phy_modes(iface));
++
++	switch (iface) {
++	case PHY_INTERFACE_MODE_1000BASEX:
++	case PHY_INTERFACE_MODE_100BASEX:
++		/* Set PHY mode to PSGMII combo (1/4 copper + combo ports) mode */
++		ret = phy_modify(phydev,
++				 QCA807X_CHIP_CONFIGURATION,
++				 QCA807X_CHIP_CONFIGURATION_MODE_CFG_MASK,
++				 QCA807X_CHIP_CONFIGURATION_MODE_PSGMII_FIBER);
++		/* Enable fiber mode autodection (1000Base-X or 100Base-FX) */
++		ret = phy_set_bits_mmd(phydev,
++				       MDIO_MMD_AN,
++				       QCA807X_MMD7_FIBER_MODE_AUTO_DETECTION,
++				       QCA807X_MMD7_FIBER_MODE_AUTO_DETECTION_EN);
++		/* Select fiber page */
++		ret = phy_clear_bits(phydev,
++				     QCA807X_CHIP_CONFIGURATION,
++				     QCA807X_BT_BX_REG_SEL);
++
++		phydev->port = PORT_FIBRE;
++		break;
++	default:
++		dev_err(&phydev->mdio.dev, "Incompatible SFP module inserted\n");
++		return -EINVAL;
++	}
++
++	return ret;
++}
++
++static void qca807x_sfp_remove(void *upstream)
++{
++	struct phy_device *phydev = upstream;
++
++	/* Select copper page */
++	phy_set_bits(phydev,
++		     QCA807X_CHIP_CONFIGURATION,
++		     QCA807X_BT_BX_REG_SEL);
++
++	phydev->port = PORT_TP;
++}
++
++static const struct sfp_upstream_ops qca807x_sfp_ops = {
++	.attach = phy_sfp_attach,
++	.detach = phy_sfp_detach,
++	.module_insert = qca807x_sfp_insert,
++	.module_remove = qca807x_sfp_remove,
++};
++
++static int qca807x_probe(struct phy_device *phydev)
++{
++	struct device_node *node = phydev->mdio.dev.of_node;
++	struct qca807x_shared_priv *shared_priv;
++	struct device *dev = &phydev->mdio.dev;
++	struct phy_package_shared *shared;
++	struct qca807x_priv *priv;
++	int ret;
++
++	ret = devm_of_phy_package_join(dev, phydev, sizeof(*shared_priv));
++	if (ret)
++		return ret;
++
++	if (phy_package_probe_once(phydev)) {
++		ret = qca807x_phy_package_probe_once(phydev);
++		if (ret)
++			return ret;
++	}
++
++	shared = phydev->shared;
++	shared_priv = shared->priv;
++
++	/* Make sure PHY follow PHY package mode if enforced */
++	if (shared_priv->package_mode != PHY_INTERFACE_MODE_NA &&
++	    phydev->interface != shared_priv->package_mode)
++		return -EINVAL;
++
++	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
++	if (!priv)
++		return -ENOMEM;
++
++	priv->dac_full_amplitude = of_property_read_bool(node, "qcom,dac-full-amplitude");
++	priv->dac_full_bias_current = of_property_read_bool(node, "qcom,dac-full-bias-current");
++	priv->dac_disable_bias_current_tweak = of_property_read_bool(node,
++								     "qcom,dac-disable-bias-current-tweak");
++
++	if (IS_ENABLED(CONFIG_GPIOLIB)) {
++		/* Do not register a GPIO controller unless flagged for it */
++		if (of_property_read_bool(node, "gpio-controller")) {
++			ret = qca807x_gpio(phydev);
++			if (ret)
++				return ret;
++		}
++	}
++
++	/* Attach SFP bus on combo port*/
++	if (phy_read(phydev, QCA807X_CHIP_CONFIGURATION)) {
++		ret = phy_sfp_probe(phydev, &qca807x_sfp_ops);
++		if (ret)
++			return ret;
++		linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, phydev->supported);
++		linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, phydev->advertising);
++	}
++
++	phydev->priv = priv;
++
++	return 0;
++}
++
++static int qca807x_config_init(struct phy_device *phydev)
++{
++	struct qca807x_priv *priv = phydev->priv;
++	u16 control_dac;
++	int ret;
++
++	if (phy_package_init_once(phydev)) {
++		ret = qca807x_phy_package_config_init_once(phydev);
++		if (ret)
++			return ret;
++	}
++
++	control_dac = phy_read_mmd(phydev, MDIO_MMD_AN,
++				   QCA807X_MMD7_1000BASE_T_POWER_SAVE_PER_CABLE_LENGTH);
++	control_dac &= ~QCA807X_CONTROL_DAC_MASK;
++	if (!priv->dac_full_amplitude)
++		control_dac |= QCA807X_CONTROL_DAC_DSP_AMPLITUDE;
++	if (!priv->dac_full_amplitude)
++		control_dac |= QCA807X_CONTROL_DAC_DSP_BIAS_CURRENT;
++	if (!priv->dac_disable_bias_current_tweak)
++		control_dac |= QCA807X_CONTROL_DAC_BIAS_CURRENT_TWEAK;
++	return phy_write_mmd(phydev, MDIO_MMD_AN,
++			     QCA807X_MMD7_1000BASE_T_POWER_SAVE_PER_CABLE_LENGTH,
++			     control_dac);
++}
++
++static struct phy_driver qca807x_drivers[] = {
++	{
++		PHY_ID_MATCH_EXACT(PHY_ID_QCA8072),
++		.name           = "Qualcomm QCA8072",
++		.flags		= PHY_POLL_CABLE_TEST,
++		/* PHY_GBIT_FEATURES */
++		.probe		= qca807x_probe,
++		.config_init	= qca807x_config_init,
++		.read_status	= qca807x_read_status,
++		.config_intr	= at803x_config_intr,
++		.handle_interrupt = at803x_handle_interrupt,
++		.soft_reset	= genphy_soft_reset,
++		.get_tunable	= at803x_get_tunable,
++		.set_tunable	= at803x_set_tunable,
++		.resume		= genphy_resume,
++		.suspend	= genphy_suspend,
++		.cable_test_start	= qca807x_cable_test_start,
++		.cable_test_get_status	= qca808x_cable_test_get_status,
++	},
++	{
++		PHY_ID_MATCH_EXACT(PHY_ID_QCA8075),
++		.name           = "Qualcomm QCA8075",
++		.flags		= PHY_POLL_CABLE_TEST,
++		/* PHY_GBIT_FEATURES */
++		.probe		= qca807x_probe,
++		.config_init	= qca807x_config_init,
++		.read_status	= qca807x_read_status,
++		.config_intr	= at803x_config_intr,
++		.handle_interrupt = at803x_handle_interrupt,
++		.soft_reset	= genphy_soft_reset,
++		.get_tunable	= at803x_get_tunable,
++		.set_tunable	= at803x_set_tunable,
++		.resume		= genphy_resume,
++		.suspend	= genphy_suspend,
++		.cable_test_start	= qca807x_cable_test_start,
++		.cable_test_get_status	= qca808x_cable_test_get_status,
++	},
++};
++module_phy_driver(qca807x_drivers);
++
++static struct mdio_device_id __maybe_unused qca807x_tbl[] = {
++	{ PHY_ID_MATCH_EXACT(PHY_ID_QCA8072) },
++	{ PHY_ID_MATCH_EXACT(PHY_ID_QCA8075) },
++	{ }
++};
++
++MODULE_AUTHOR("Robert Marko <robert.marko at sartura.hr>");
++MODULE_AUTHOR("Christian Marangi <ansuelsmth at gmail.com>");
++MODULE_DESCRIPTION("Qualcomm QCA807x PHY driver");
++MODULE_DEVICE_TABLE(mdio, qca807x_tbl);
++MODULE_LICENSE("GPL");
diff --git a/target/linux/generic/backport-6.1/716-v6.9-08-net-phy-qcom-move-common-qca808x-LED-define-to-share.patch b/target/linux/generic/backport-6.1/716-v6.9-08-net-phy-qcom-move-common-qca808x-LED-define-to-share.patch
new file mode 100644
index 0000000000..cf4d74e8c5
--- /dev/null
+++ b/target/linux/generic/backport-6.1/716-v6.9-08-net-phy-qcom-move-common-qca808x-LED-define-to-share.patch
@@ -0,0 +1,179 @@
+From ee9d9807bee0e6af8ca2a4db6f0d1dc0e5b41f44 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth at gmail.com>
+Date: Tue, 6 Feb 2024 18:31:11 +0100
+Subject: [PATCH 08/10] net: phy: qcom: move common qca808x LED define to
+ shared header
+
+The LED implementation of qca808x and qca807x is the same but qca807x
+supports also Fiber port and have different hw control bits for Fiber
+port.
+
+In preparation for qca807x introduction, move all the common define to
+shared header.
+
+Signed-off-by: Christian Marangi <ansuelsmth at gmail.com>
+Reviewed-by: Andrew Lunn <andrew at lunn.ch>
+Signed-off-by: David S. Miller <davem at davemloft.net>
+---
+ drivers/net/phy/qcom/qca808x.c | 65 ----------------------------------
+ drivers/net/phy/qcom/qcom.h    | 65 ++++++++++++++++++++++++++++++++++
+ 2 files changed, 65 insertions(+), 65 deletions(-)
+
+--- a/drivers/net/phy/qcom/qca808x.c
++++ b/drivers/net/phy/qcom/qca808x.c
+@@ -62,29 +62,6 @@
+ #define QCA808X_DBG_AN_TEST			0xb
+ #define QCA808X_HIBERNATION_EN			BIT(15)
+ 
+-#define QCA808X_MMD7_LED_GLOBAL			0x8073
+-#define QCA808X_LED_BLINK_1			GENMASK(11, 6)
+-#define QCA808X_LED_BLINK_2			GENMASK(5, 0)
+-/* Values are the same for both BLINK_1 and BLINK_2 */
+-#define QCA808X_LED_BLINK_FREQ_MASK		GENMASK(5, 3)
+-#define QCA808X_LED_BLINK_FREQ_2HZ		FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x0)
+-#define QCA808X_LED_BLINK_FREQ_4HZ		FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x1)
+-#define QCA808X_LED_BLINK_FREQ_8HZ		FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x2)
+-#define QCA808X_LED_BLINK_FREQ_16HZ		FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x3)
+-#define QCA808X_LED_BLINK_FREQ_32HZ		FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x4)
+-#define QCA808X_LED_BLINK_FREQ_64HZ		FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x5)
+-#define QCA808X_LED_BLINK_FREQ_128HZ		FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x6)
+-#define QCA808X_LED_BLINK_FREQ_256HZ		FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x7)
+-#define QCA808X_LED_BLINK_DUTY_MASK		GENMASK(2, 0)
+-#define QCA808X_LED_BLINK_DUTY_50_50		FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x0)
+-#define QCA808X_LED_BLINK_DUTY_75_25		FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x1)
+-#define QCA808X_LED_BLINK_DUTY_25_75		FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x2)
+-#define QCA808X_LED_BLINK_DUTY_33_67		FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x3)
+-#define QCA808X_LED_BLINK_DUTY_67_33		FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x4)
+-#define QCA808X_LED_BLINK_DUTY_17_83		FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x5)
+-#define QCA808X_LED_BLINK_DUTY_83_17		FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x6)
+-#define QCA808X_LED_BLINK_DUTY_8_92		FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x7)
+-
+ #define QCA808X_MMD7_LED2_CTRL			0x8074
+ #define QCA808X_MMD7_LED2_FORCE_CTRL		0x8075
+ #define QCA808X_MMD7_LED1_CTRL			0x8076
+@@ -92,51 +69,9 @@
+ #define QCA808X_MMD7_LED0_CTRL			0x8078
+ #define QCA808X_MMD7_LED_CTRL(x)		(0x8078 - ((x) * 2))
+ 
+-/* LED hw control pattern is the same for every LED */
+-#define QCA808X_LED_PATTERN_MASK		GENMASK(15, 0)
+-#define QCA808X_LED_SPEED2500_ON		BIT(15)
+-#define QCA808X_LED_SPEED2500_BLINK		BIT(14)
+-/* Follow blink trigger even if duplex or speed condition doesn't match */
+-#define QCA808X_LED_BLINK_CHECK_BYPASS		BIT(13)
+-#define QCA808X_LED_FULL_DUPLEX_ON		BIT(12)
+-#define QCA808X_LED_HALF_DUPLEX_ON		BIT(11)
+-#define QCA808X_LED_TX_BLINK			BIT(10)
+-#define QCA808X_LED_RX_BLINK			BIT(9)
+-#define QCA808X_LED_TX_ON_10MS			BIT(8)
+-#define QCA808X_LED_RX_ON_10MS			BIT(7)
+-#define QCA808X_LED_SPEED1000_ON		BIT(6)
+-#define QCA808X_LED_SPEED100_ON			BIT(5)
+-#define QCA808X_LED_SPEED10_ON			BIT(4)
+-#define QCA808X_LED_COLLISION_BLINK		BIT(3)
+-#define QCA808X_LED_SPEED1000_BLINK		BIT(2)
+-#define QCA808X_LED_SPEED100_BLINK		BIT(1)
+-#define QCA808X_LED_SPEED10_BLINK		BIT(0)
+-
+ #define QCA808X_MMD7_LED0_FORCE_CTRL		0x8079
+ #define QCA808X_MMD7_LED_FORCE_CTRL(x)		(0x8079 - ((x) * 2))
+ 
+-/* LED force ctrl is the same for every LED
+- * No documentation exist for this, not even internal one
+- * with NDA as QCOM gives only info about configuring
+- * hw control pattern rules and doesn't indicate any way
+- * to force the LED to specific mode.
+- * These define comes from reverse and testing and maybe
+- * lack of some info or some info are not entirely correct.
+- * For the basic LED control and hw control these finding
+- * are enough to support LED control in all the required APIs.
+- *
+- * On doing some comparison with implementation with qca807x,
+- * it was found that it's 1:1 equal to it and confirms all the
+- * reverse done. It was also found further specification with the
+- * force mode and the blink modes.
+- */
+-#define QCA808X_LED_FORCE_EN			BIT(15)
+-#define QCA808X_LED_FORCE_MODE_MASK		GENMASK(14, 13)
+-#define QCA808X_LED_FORCE_BLINK_1		FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x3)
+-#define QCA808X_LED_FORCE_BLINK_2		FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x2)
+-#define QCA808X_LED_FORCE_ON			FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x1)
+-#define QCA808X_LED_FORCE_OFF			FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x0)
+-
+ #define QCA808X_MMD7_LED_POLARITY_CTRL		0x901a
+ /* QSDK sets by default 0x46 to this reg that sets BIT 6 for
+  * LED to active high. It's not clear what BIT 3 and BIT 4 does.
+--- a/drivers/net/phy/qcom/qcom.h
++++ b/drivers/net/phy/qcom/qcom.h
+@@ -103,6 +103,71 @@
+ /* Added for reference of existence but should be handled by wait_for_completion already */
+ #define QCA808X_CDT_STATUS_STAT_BUSY		(BIT(1) | BIT(3))
+ 
++#define QCA808X_MMD7_LED_GLOBAL			0x8073
++#define QCA808X_LED_BLINK_1			GENMASK(11, 6)
++#define QCA808X_LED_BLINK_2			GENMASK(5, 0)
++/* Values are the same for both BLINK_1 and BLINK_2 */
++#define QCA808X_LED_BLINK_FREQ_MASK		GENMASK(5, 3)
++#define QCA808X_LED_BLINK_FREQ_2HZ		FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x0)
++#define QCA808X_LED_BLINK_FREQ_4HZ		FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x1)
++#define QCA808X_LED_BLINK_FREQ_8HZ		FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x2)
++#define QCA808X_LED_BLINK_FREQ_16HZ		FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x3)
++#define QCA808X_LED_BLINK_FREQ_32HZ		FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x4)
++#define QCA808X_LED_BLINK_FREQ_64HZ		FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x5)
++#define QCA808X_LED_BLINK_FREQ_128HZ		FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x6)
++#define QCA808X_LED_BLINK_FREQ_256HZ		FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x7)
++#define QCA808X_LED_BLINK_DUTY_MASK		GENMASK(2, 0)
++#define QCA808X_LED_BLINK_DUTY_50_50		FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x0)
++#define QCA808X_LED_BLINK_DUTY_75_25		FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x1)
++#define QCA808X_LED_BLINK_DUTY_25_75		FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x2)
++#define QCA808X_LED_BLINK_DUTY_33_67		FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x3)
++#define QCA808X_LED_BLINK_DUTY_67_33		FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x4)
++#define QCA808X_LED_BLINK_DUTY_17_83		FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x5)
++#define QCA808X_LED_BLINK_DUTY_83_17		FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x6)
++#define QCA808X_LED_BLINK_DUTY_8_92		FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x7)
++
++/* LED hw control pattern is the same for every LED */
++#define QCA808X_LED_PATTERN_MASK		GENMASK(15, 0)
++#define QCA808X_LED_SPEED2500_ON		BIT(15)
++#define QCA808X_LED_SPEED2500_BLINK		BIT(14)
++/* Follow blink trigger even if duplex or speed condition doesn't match */
++#define QCA808X_LED_BLINK_CHECK_BYPASS		BIT(13)
++#define QCA808X_LED_FULL_DUPLEX_ON		BIT(12)
++#define QCA808X_LED_HALF_DUPLEX_ON		BIT(11)
++#define QCA808X_LED_TX_BLINK			BIT(10)
++#define QCA808X_LED_RX_BLINK			BIT(9)
++#define QCA808X_LED_TX_ON_10MS			BIT(8)
++#define QCA808X_LED_RX_ON_10MS			BIT(7)
++#define QCA808X_LED_SPEED1000_ON		BIT(6)
++#define QCA808X_LED_SPEED100_ON			BIT(5)
++#define QCA808X_LED_SPEED10_ON			BIT(4)
++#define QCA808X_LED_COLLISION_BLINK		BIT(3)
++#define QCA808X_LED_SPEED1000_BLINK		BIT(2)
++#define QCA808X_LED_SPEED100_BLINK		BIT(1)
++#define QCA808X_LED_SPEED10_BLINK		BIT(0)
++
++/* LED force ctrl is the same for every LED
++ * No documentation exist for this, not even internal one
++ * with NDA as QCOM gives only info about configuring
++ * hw control pattern rules and doesn't indicate any way
++ * to force the LED to specific mode.
++ * These define comes from reverse and testing and maybe
++ * lack of some info or some info are not entirely correct.
++ * For the basic LED control and hw control these finding
++ * are enough to support LED control in all the required APIs.
++ *
++ * On doing some comparison with implementation with qca807x,
++ * it was found that it's 1:1 equal to it and confirms all the
++ * reverse done. It was also found further specification with the
++ * force mode and the blink modes.
++ */
++#define QCA808X_LED_FORCE_EN			BIT(15)
++#define QCA808X_LED_FORCE_MODE_MASK		GENMASK(14, 13)
++#define QCA808X_LED_FORCE_BLINK_1		FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x3)
++#define QCA808X_LED_FORCE_BLINK_2		FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x2)
++#define QCA808X_LED_FORCE_ON			FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x1)
++#define QCA808X_LED_FORCE_OFF			FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x0)
++
+ #define AT803X_LOC_MAC_ADDR_0_15_OFFSET		0x804C
+ #define AT803X_LOC_MAC_ADDR_16_31_OFFSET	0x804B
+ #define AT803X_LOC_MAC_ADDR_32_47_OFFSET	0x804A
diff --git a/target/linux/generic/backport-6.1/716-v6.9-09-net-phy-qcom-generalize-some-qca808x-LED-functions.patch b/target/linux/generic/backport-6.1/716-v6.9-09-net-phy-qcom-generalize-some-qca808x-LED-functions.patch
new file mode 100644
index 0000000000..da73c1d3b8
--- /dev/null
+++ b/target/linux/generic/backport-6.1/716-v6.9-09-net-phy-qcom-generalize-some-qca808x-LED-functions.patch
@@ -0,0 +1,172 @@
+From 47b930d0dd437af927145dba50a2e2ea1ba97c67 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth at gmail.com>
+Date: Tue, 6 Feb 2024 18:31:12 +0100
+Subject: [PATCH 09/10] net: phy: qcom: generalize some qca808x LED functions
+
+Generalize some qca808x LED functions in preparation for qca807x LED
+support.
+
+The LED implementation of qca808x and qca807x is the same but qca807x
+supports also Fiber port and have different hw control bits for Fiber
+port. To limit code duplication introduce micro functions that takes reg
+instead of LED index to tweak all the supported LED modes.
+
+Signed-off-by: Christian Marangi <ansuelsmth at gmail.com>
+Signed-off-by: David S. Miller <davem at davemloft.net>
+---
+ drivers/net/phy/qcom/qca808x.c      | 38 +++-----------------
+ drivers/net/phy/qcom/qcom-phy-lib.c | 54 +++++++++++++++++++++++++++++
+ drivers/net/phy/qcom/qcom.h         |  7 ++++
+ 3 files changed, 65 insertions(+), 34 deletions(-)
+
+--- a/drivers/net/phy/qcom/qca808x.c
++++ b/drivers/net/phy/qcom/qca808x.c
+@@ -437,9 +437,7 @@ static int qca808x_led_hw_control_enable
+ 		return -EINVAL;
+ 
+ 	reg = QCA808X_MMD7_LED_FORCE_CTRL(index);
+-
+-	return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, reg,
+-				  QCA808X_LED_FORCE_EN);
++	return qca808x_led_reg_hw_control_enable(phydev, reg);
+ }
+ 
+ static int qca808x_led_hw_is_supported(struct phy_device *phydev, u8 index,
+@@ -480,16 +478,12 @@ static int qca808x_led_hw_control_set(st
+ static bool qca808x_led_hw_control_status(struct phy_device *phydev, u8 index)
+ {
+ 	u16 reg;
+-	int val;
+ 
+ 	if (index > 2)
+ 		return false;
+ 
+ 	reg = QCA808X_MMD7_LED_FORCE_CTRL(index);
+-
+-	val = phy_read_mmd(phydev, MDIO_MMD_AN, reg);
+-
+-	return !(val & QCA808X_LED_FORCE_EN);
++	return qca808x_led_reg_hw_control_status(phydev, reg);
+ }
+ 
+ static int qca808x_led_hw_control_get(struct phy_device *phydev, u8 index,
+@@ -557,44 +551,20 @@ static int qca808x_led_brightness_set(st
+ 	}
+ 
+ 	reg = QCA808X_MMD7_LED_FORCE_CTRL(index);
+-
+-	return phy_modify_mmd(phydev, MDIO_MMD_AN, reg,
+-			      QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK,
+-			      QCA808X_LED_FORCE_EN | (value ? QCA808X_LED_FORCE_ON :
+-							     QCA808X_LED_FORCE_OFF));
++	return qca808x_led_reg_brightness_set(phydev, reg, value);
+ }
+ 
+ static int qca808x_led_blink_set(struct phy_device *phydev, u8 index,
+ 				 unsigned long *delay_on,
+ 				 unsigned long *delay_off)
+ {
+-	int ret;
+ 	u16 reg;
+ 
+ 	if (index > 2)
+ 		return -EINVAL;
+ 
+ 	reg = QCA808X_MMD7_LED_FORCE_CTRL(index);
+-
+-	/* Set blink to 50% off, 50% on at 4Hz by default */
+-	ret = phy_modify_mmd(phydev, MDIO_MMD_AN, QCA808X_MMD7_LED_GLOBAL,
+-			     QCA808X_LED_BLINK_FREQ_MASK | QCA808X_LED_BLINK_DUTY_MASK,
+-			     QCA808X_LED_BLINK_FREQ_4HZ | QCA808X_LED_BLINK_DUTY_50_50);
+-	if (ret)
+-		return ret;
+-
+-	/* We use BLINK_1 for normal blinking */
+-	ret = phy_modify_mmd(phydev, MDIO_MMD_AN, reg,
+-			     QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK,
+-			     QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_BLINK_1);
+-	if (ret)
+-		return ret;
+-
+-	/* We set blink to 4Hz, aka 250ms */
+-	*delay_on = 250 / 2;
+-	*delay_off = 250 / 2;
+-
+-	return 0;
++	return qca808x_led_reg_blink_set(phydev, reg, delay_on, delay_off);
+ }
+ 
+ static int qca808x_led_polarity_set(struct phy_device *phydev, int index,
+--- a/drivers/net/phy/qcom/qcom-phy-lib.c
++++ b/drivers/net/phy/qcom/qcom-phy-lib.c
+@@ -620,3 +620,57 @@ int qca808x_cable_test_get_status(struct
+ 	return 0;
+ }
+ EXPORT_SYMBOL_GPL(qca808x_cable_test_get_status);
++
++int qca808x_led_reg_hw_control_enable(struct phy_device *phydev, u16 reg)
++{
++	return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, reg,
++				  QCA808X_LED_FORCE_EN);
++}
++EXPORT_SYMBOL_GPL(qca808x_led_reg_hw_control_enable);
++
++bool qca808x_led_reg_hw_control_status(struct phy_device *phydev, u16 reg)
++{
++	int val;
++
++	val = phy_read_mmd(phydev, MDIO_MMD_AN, reg);
++	return !(val & QCA808X_LED_FORCE_EN);
++}
++EXPORT_SYMBOL_GPL(qca808x_led_reg_hw_control_status);
++
++int qca808x_led_reg_brightness_set(struct phy_device *phydev,
++				   u16 reg, enum led_brightness value)
++{
++	return phy_modify_mmd(phydev, MDIO_MMD_AN, reg,
++			      QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK,
++			      QCA808X_LED_FORCE_EN | (value ? QCA808X_LED_FORCE_ON :
++							      QCA808X_LED_FORCE_OFF));
++}
++EXPORT_SYMBOL_GPL(qca808x_led_reg_brightness_set);
++
++int qca808x_led_reg_blink_set(struct phy_device *phydev, u16 reg,
++			      unsigned long *delay_on,
++			      unsigned long *delay_off)
++{
++	int ret;
++
++	/* Set blink to 50% off, 50% on at 4Hz by default */
++	ret = phy_modify_mmd(phydev, MDIO_MMD_AN, QCA808X_MMD7_LED_GLOBAL,
++			     QCA808X_LED_BLINK_FREQ_MASK | QCA808X_LED_BLINK_DUTY_MASK,
++			     QCA808X_LED_BLINK_FREQ_4HZ | QCA808X_LED_BLINK_DUTY_50_50);
++	if (ret)
++		return ret;
++
++	/* We use BLINK_1 for normal blinking */
++	ret = phy_modify_mmd(phydev, MDIO_MMD_AN, reg,
++			     QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK,
++			     QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_BLINK_1);
++	if (ret)
++		return ret;
++
++	/* We set blink to 4Hz, aka 250ms */
++	*delay_on = 250 / 2;
++	*delay_off = 250 / 2;
++
++	return 0;
++}
++EXPORT_SYMBOL_GPL(qca808x_led_reg_blink_set);
+--- a/drivers/net/phy/qcom/qcom.h
++++ b/drivers/net/phy/qcom/qcom.h
+@@ -234,3 +234,10 @@ int at803x_cdt_start(struct phy_device *
+ int at803x_cdt_wait_for_completion(struct phy_device *phydev,
+ 				   u32 cdt_en);
+ int qca808x_cable_test_get_status(struct phy_device *phydev, bool *finished);
++int qca808x_led_reg_hw_control_enable(struct phy_device *phydev, u16 reg);
++bool qca808x_led_reg_hw_control_status(struct phy_device *phydev, u16 reg);
++int qca808x_led_reg_brightness_set(struct phy_device *phydev,
++				   u16 reg, enum led_brightness value);
++int qca808x_led_reg_blink_set(struct phy_device *phydev, u16 reg,
++			      unsigned long *delay_on,
++			      unsigned long *delay_off);
diff --git a/target/linux/generic/backport-6.1/716-v6.9-10-net-phy-qca807x-add-support-for-configurable-LED.patch b/target/linux/generic/backport-6.1/716-v6.9-10-net-phy-qca807x-add-support-for-configurable-LED.patch
new file mode 100644
index 0000000000..3bd36f6ffe
--- /dev/null
+++ b/target/linux/generic/backport-6.1/716-v6.9-10-net-phy-qca807x-add-support-for-configurable-LED.patch
@@ -0,0 +1,326 @@
+From f508a226b517a6a8afd78a317de46bc83e3e3d51 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth at gmail.com>
+Date: Tue, 6 Feb 2024 18:31:13 +0100
+Subject: [PATCH 10/10] net: phy: qca807x: add support for configurable LED
+
+QCA8072/5 have up to 2 LEDs attached for PHY.
+
+LEDs can be configured to be ON/hw blink or be set to HW control.
+
+Hw blink mode is set to blink at 4Hz or 250ms.
+
+PHY can support both copper (TP) or fiber (FIBRE) kind and supports
+different HW control modes based on the port type.
+
+HW control modes supported for netdev trigger for copper ports are:
+- LINK_10
+- LINK_100
+- LINK_1000
+- TX
+- RX
+- FULL_DUPLEX
+- HALF_DUPLEX
+
+HW control modes supported for netdev trigger for fiber ports are:
+- LINK_100
+- LINK_1000
+- TX
+- RX
+- FULL_DUPLEX
+- HALF_DUPLEX
+
+LED support conflicts with GPIO controller feature and must be disabled
+if gpio-controller is used for the PHY.
+
+Signed-off-by: Christian Marangi <ansuelsmth at gmail.com>
+Signed-off-by: David S. Miller <davem at davemloft.net>
+---
+ drivers/net/phy/qcom/qca807x.c | 256 ++++++++++++++++++++++++++++++++-
+ 1 file changed, 254 insertions(+), 2 deletions(-)
+
+--- a/drivers/net/phy/qcom/qca807x.c
++++ b/drivers/net/phy/qcom/qca807x.c
+@@ -57,8 +57,18 @@
+ #define QCA807X_MMD7_LED_CTRL(x)			(0x8074 + ((x) * 2))
+ #define QCA807X_MMD7_LED_FORCE_CTRL(x)			(0x8075 + ((x) * 2))
+ 
+-#define QCA807X_GPIO_FORCE_EN				BIT(15)
+-#define QCA807X_GPIO_FORCE_MODE_MASK			GENMASK(14, 13)
++/* LED hw control pattern for fiber port */
++#define QCA807X_LED_FIBER_PATTERN_MASK			GENMASK(11, 1)
++#define QCA807X_LED_FIBER_TXACT_BLK_EN			BIT(10)
++#define QCA807X_LED_FIBER_RXACT_BLK_EN			BIT(9)
++#define QCA807X_LED_FIBER_FDX_ON_EN			BIT(6)
++#define QCA807X_LED_FIBER_HDX_ON_EN			BIT(5)
++#define QCA807X_LED_FIBER_1000BX_ON_EN			BIT(2)
++#define QCA807X_LED_FIBER_100FX_ON_EN			BIT(1)
++
++/* Some device repurpose the LED as GPIO out */
++#define QCA807X_GPIO_FORCE_EN				QCA808X_LED_FORCE_EN
++#define QCA807X_GPIO_FORCE_MODE_MASK			QCA808X_LED_FORCE_MODE_MASK
+ 
+ #define QCA807X_FUNCTION_CONTROL			0x10
+ #define QCA807X_FC_MDI_CROSSOVER_MODE_MASK		GENMASK(6, 5)
+@@ -121,6 +131,233 @@ static int qca807x_cable_test_start(stru
+ 	return 0;
+ }
+ 
++static int qca807x_led_parse_netdev(struct phy_device *phydev, unsigned long rules,
++				    u16 *offload_trigger)
++{
++	/* Parsing specific to netdev trigger */
++	switch (phydev->port) {
++	case PORT_TP:
++		if (test_bit(TRIGGER_NETDEV_TX, &rules))
++			*offload_trigger |= QCA808X_LED_TX_BLINK;
++		if (test_bit(TRIGGER_NETDEV_RX, &rules))
++			*offload_trigger |= QCA808X_LED_RX_BLINK;
++		if (test_bit(TRIGGER_NETDEV_LINK_10, &rules))
++			*offload_trigger |= QCA808X_LED_SPEED10_ON;
++		if (test_bit(TRIGGER_NETDEV_LINK_100, &rules))
++			*offload_trigger |= QCA808X_LED_SPEED100_ON;
++		if (test_bit(TRIGGER_NETDEV_LINK_1000, &rules))
++			*offload_trigger |= QCA808X_LED_SPEED1000_ON;
++		if (test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &rules))
++			*offload_trigger |= QCA808X_LED_HALF_DUPLEX_ON;
++		if (test_bit(TRIGGER_NETDEV_FULL_DUPLEX, &rules))
++			*offload_trigger |= QCA808X_LED_FULL_DUPLEX_ON;
++		break;
++	case PORT_FIBRE:
++		if (test_bit(TRIGGER_NETDEV_TX, &rules))
++			*offload_trigger |= QCA807X_LED_FIBER_TXACT_BLK_EN;
++		if (test_bit(TRIGGER_NETDEV_RX, &rules))
++			*offload_trigger |= QCA807X_LED_FIBER_RXACT_BLK_EN;
++		if (test_bit(TRIGGER_NETDEV_LINK_100, &rules))
++			*offload_trigger |= QCA807X_LED_FIBER_100FX_ON_EN;
++		if (test_bit(TRIGGER_NETDEV_LINK_1000, &rules))
++			*offload_trigger |= QCA807X_LED_FIBER_1000BX_ON_EN;
++		if (test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &rules))
++			*offload_trigger |= QCA807X_LED_FIBER_HDX_ON_EN;
++		if (test_bit(TRIGGER_NETDEV_FULL_DUPLEX, &rules))
++			*offload_trigger |= QCA807X_LED_FIBER_FDX_ON_EN;
++		break;
++	default:
++		return -EOPNOTSUPP;
++	}
++
++	if (rules && !*offload_trigger)
++		return -EOPNOTSUPP;
++
++	return 0;
++}
++
++static int qca807x_led_hw_control_enable(struct phy_device *phydev, u8 index)
++{
++	u16 reg;
++
++	if (index > 1)
++		return -EINVAL;
++
++	reg = QCA807X_MMD7_LED_FORCE_CTRL(index);
++	return qca808x_led_reg_hw_control_enable(phydev, reg);
++}
++
++static int qca807x_led_hw_is_supported(struct phy_device *phydev, u8 index,
++				       unsigned long rules)
++{
++	u16 offload_trigger = 0;
++
++	if (index > 1)
++		return -EINVAL;
++
++	return qca807x_led_parse_netdev(phydev, rules, &offload_trigger);
++}
++
++static int qca807x_led_hw_control_set(struct phy_device *phydev, u8 index,
++				      unsigned long rules)
++{
++	u16 reg, mask, offload_trigger = 0;
++	int ret;
++
++	if (index > 1)
++		return -EINVAL;
++
++	ret = qca807x_led_parse_netdev(phydev, rules, &offload_trigger);
++	if (ret)
++		return ret;
++
++	ret = qca807x_led_hw_control_enable(phydev, index);
++	if (ret)
++		return ret;
++
++	switch (phydev->port) {
++	case PORT_TP:
++		reg = QCA807X_MMD7_LED_CTRL(index);
++		mask = QCA808X_LED_PATTERN_MASK;
++		break;
++	case PORT_FIBRE:
++		/* HW control pattern bits are in LED FORCE reg */
++		reg = QCA807X_MMD7_LED_FORCE_CTRL(index);
++		mask = QCA807X_LED_FIBER_PATTERN_MASK;
++		break;
++	default:
++		return -EINVAL;
++	}
++
++	return phy_modify_mmd(phydev, MDIO_MMD_AN, reg, mask,
++			      offload_trigger);
++}
++
++static bool qca807x_led_hw_control_status(struct phy_device *phydev, u8 index)
++{
++	u16 reg;
++
++	if (index > 1)
++		return false;
++
++	reg = QCA807X_MMD7_LED_FORCE_CTRL(index);
++	return qca808x_led_reg_hw_control_status(phydev, reg);
++}
++
++static int qca807x_led_hw_control_get(struct phy_device *phydev, u8 index,
++				      unsigned long *rules)
++{
++	u16 reg;
++	int val;
++
++	if (index > 1)
++		return -EINVAL;
++
++	/* Check if we have hw control enabled */
++	if (qca807x_led_hw_control_status(phydev, index))
++		return -EINVAL;
++
++	/* Parsing specific to netdev trigger */
++	switch (phydev->port) {
++	case PORT_TP:
++		reg = QCA807X_MMD7_LED_CTRL(index);
++		val = phy_read_mmd(phydev, MDIO_MMD_AN, reg);
++		if (val & QCA808X_LED_TX_BLINK)
++			set_bit(TRIGGER_NETDEV_TX, rules);
++		if (val & QCA808X_LED_RX_BLINK)
++			set_bit(TRIGGER_NETDEV_RX, rules);
++		if (val & QCA808X_LED_SPEED10_ON)
++			set_bit(TRIGGER_NETDEV_LINK_10, rules);
++		if (val & QCA808X_LED_SPEED100_ON)
++			set_bit(TRIGGER_NETDEV_LINK_100, rules);
++		if (val & QCA808X_LED_SPEED1000_ON)
++			set_bit(TRIGGER_NETDEV_LINK_1000, rules);
++		if (val & QCA808X_LED_HALF_DUPLEX_ON)
++			set_bit(TRIGGER_NETDEV_HALF_DUPLEX, rules);
++		if (val & QCA808X_LED_FULL_DUPLEX_ON)
++			set_bit(TRIGGER_NETDEV_FULL_DUPLEX, rules);
++		break;
++	case PORT_FIBRE:
++		/* HW control pattern bits are in LED FORCE reg */
++		reg = QCA807X_MMD7_LED_FORCE_CTRL(index);
++		val = phy_read_mmd(phydev, MDIO_MMD_AN, reg);
++		if (val & QCA807X_LED_FIBER_TXACT_BLK_EN)
++			set_bit(TRIGGER_NETDEV_TX, rules);
++		if (val & QCA807X_LED_FIBER_RXACT_BLK_EN)
++			set_bit(TRIGGER_NETDEV_RX, rules);
++		if (val & QCA807X_LED_FIBER_100FX_ON_EN)
++			set_bit(TRIGGER_NETDEV_LINK_100, rules);
++		if (val & QCA807X_LED_FIBER_1000BX_ON_EN)
++			set_bit(TRIGGER_NETDEV_LINK_1000, rules);
++		if (val & QCA807X_LED_FIBER_HDX_ON_EN)
++			set_bit(TRIGGER_NETDEV_HALF_DUPLEX, rules);
++		if (val & QCA807X_LED_FIBER_FDX_ON_EN)
++			set_bit(TRIGGER_NETDEV_FULL_DUPLEX, rules);
++		break;
++	default:
++		return -EINVAL;
++	}
++
++	return 0;
++}
++
++static int qca807x_led_hw_control_reset(struct phy_device *phydev, u8 index)
++{
++	u16 reg, mask;
++
++	if (index > 1)
++		return -EINVAL;
++
++	switch (phydev->port) {
++	case PORT_TP:
++		reg = QCA807X_MMD7_LED_CTRL(index);
++		mask = QCA808X_LED_PATTERN_MASK;
++		break;
++	case PORT_FIBRE:
++		/* HW control pattern bits are in LED FORCE reg */
++		reg = QCA807X_MMD7_LED_FORCE_CTRL(index);
++		mask = QCA807X_LED_FIBER_PATTERN_MASK;
++		break;
++	default:
++		return -EINVAL;
++	}
++
++	return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, reg, mask);
++}
++
++static int qca807x_led_brightness_set(struct phy_device *phydev,
++				      u8 index, enum led_brightness value)
++{
++	u16 reg;
++	int ret;
++
++	if (index > 1)
++		return -EINVAL;
++
++	/* If we are setting off the LED reset any hw control rule */
++	if (!value) {
++		ret = qca807x_led_hw_control_reset(phydev, index);
++		if (ret)
++			return ret;
++	}
++
++	reg = QCA807X_MMD7_LED_FORCE_CTRL(index);
++	return qca808x_led_reg_brightness_set(phydev, reg, value);
++}
++
++static int qca807x_led_blink_set(struct phy_device *phydev, u8 index,
++				 unsigned long *delay_on,
++				 unsigned long *delay_off)
++{
++	u16 reg;
++
++	if (index > 1)
++		return -EINVAL;
++
++	reg = QCA807X_MMD7_LED_FORCE_CTRL(index);
++	return qca808x_led_reg_blink_set(phydev, reg, delay_on, delay_off);
++}
++
+ #ifdef CONFIG_GPIOLIB
+ static int qca807x_gpio_get_direction(struct gpio_chip *gc, unsigned int offset)
+ {
+@@ -496,6 +733,16 @@ static int qca807x_probe(struct phy_devi
+ 								     "qcom,dac-disable-bias-current-tweak");
+ 
+ 	if (IS_ENABLED(CONFIG_GPIOLIB)) {
++		/* Make sure we don't have mixed leds node and gpio-controller
++		 * to prevent registering leds and having gpio-controller usage
++		 * conflicting with them.
++		 */
++		if (of_find_property(node, "leds", NULL) &&
++		    of_find_property(node, "gpio-controller", NULL)) {
++			phydev_err(phydev, "Invalid property detected. LEDs and gpio-controller are mutually exclusive.");
++			return -EINVAL;
++		}
++
+ 		/* Do not register a GPIO controller unless flagged for it */
+ 		if (of_property_read_bool(node, "gpio-controller")) {
+ 			ret = qca807x_gpio(phydev);
+@@ -580,6 +827,11 @@ static struct phy_driver qca807x_drivers
+ 		.suspend	= genphy_suspend,
+ 		.cable_test_start	= qca807x_cable_test_start,
+ 		.cable_test_get_status	= qca808x_cable_test_get_status,
++		.led_brightness_set = qca807x_led_brightness_set,
++		.led_blink_set = qca807x_led_blink_set,
++		.led_hw_is_supported = qca807x_led_hw_is_supported,
++		.led_hw_control_set = qca807x_led_hw_control_set,
++		.led_hw_control_get = qca807x_led_hw_control_get,
+ 	},
+ };
+ module_phy_driver(qca807x_drivers);
diff --git a/target/linux/generic/backport-6.1/801-v6.4-05-net-phy-Add-a-binding-for-PHY-LEDs.patch b/target/linux/generic/backport-6.1/801-v6.4-05-net-phy-Add-a-binding-for-PHY-LEDs.patch
index c958362e96..70a3a4ad5c 100644
--- a/target/linux/generic/backport-6.1/801-v6.4-05-net-phy-Add-a-binding-for-PHY-LEDs.patch
+++ b/target/linux/generic/backport-6.1/801-v6.4-05-net-phy-Add-a-binding-for-PHY-LEDs.patch
@@ -56,7 +56,7 @@ Signed-off-by: David S. Miller <davem at davemloft.net>
  
  	mutex_init(&dev->lock);
  	INIT_DELAYED_WORK(&dev->state_queue, phy_state_machine);
-@@ -2934,6 +2937,74 @@ static bool phy_drv_supports_irq(struct
+@@ -3037,6 +3040,74 @@ static bool phy_drv_supports_irq(struct
  	return phydrv->config_intr && phydrv->handle_interrupt;
  }
  
@@ -131,7 +131,7 @@ Signed-off-by: David S. Miller <davem at davemloft.net>
  /**
   * fwnode_mdio_find_device - Given a fwnode, find the mdio_device
   * @fwnode: pointer to the mdio_device's fwnode
-@@ -3112,6 +3183,11 @@ static int phy_probe(struct device *dev)
+@@ -3215,6 +3286,11 @@ static int phy_probe(struct device *dev)
  	/* Set the state to READY by default */
  	phydev->state = PHY_READY;
  
@@ -153,7 +153,7 @@ Signed-off-by: David S. Miller <davem at davemloft.net>
  #include <linux/linkmode.h>
  #include <linux/netlink.h>
  #include <linux/mdio.h>
-@@ -603,6 +604,7 @@ struct macsec_ops;
+@@ -606,6 +607,7 @@ struct macsec_ops;
   * @phy_num_led_triggers: Number of triggers in @phy_led_triggers
   * @led_link_trigger: LED trigger for link up/down
   * @last_triggered: last LED trigger for link speed
@@ -161,7 +161,7 @@ Signed-off-by: David S. Miller <davem at davemloft.net>
   * @master_slave_set: User requested master/slave configuration
   * @master_slave_get: Current master/slave advertisement
   * @master_slave_state: Current master/slave configuration
-@@ -695,6 +697,7 @@ struct phy_device {
+@@ -698,6 +700,7 @@ struct phy_device {
  
  	struct phy_led_trigger *led_link_trigger;
  #endif
@@ -169,7 +169,7 @@ Signed-off-by: David S. Miller <davem at davemloft.net>
  
  	/*
  	 * Interrupt number for this PHY
-@@ -769,6 +772,19 @@ struct phy_tdr_config {
+@@ -772,6 +775,19 @@ struct phy_tdr_config {
  #define PHY_PAIR_ALL -1
  
  /**
diff --git a/target/linux/generic/backport-6.1/801-v6.4-06-net-phy-phy_device-Call-into-the-PHY-driver-to-set-L.patch b/target/linux/generic/backport-6.1/801-v6.4-06-net-phy-phy_device-Call-into-the-PHY-driver-to-set-L.patch
index 3c2477a63a..e2e7bd65b1 100644
--- a/target/linux/generic/backport-6.1/801-v6.4-06-net-phy-phy_device-Call-into-the-PHY-driver-to-set-L.patch
+++ b/target/linux/generic/backport-6.1/801-v6.4-06-net-phy-phy_device-Call-into-the-PHY-driver-to-set-L.patch
@@ -20,7 +20,7 @@ Signed-off-by: David S. Miller <davem at davemloft.net>
 
 --- a/drivers/net/phy/phy_device.c
 +++ b/drivers/net/phy/phy_device.c
-@@ -2937,11 +2937,18 @@ static bool phy_drv_supports_irq(struct
+@@ -3040,11 +3040,18 @@ static bool phy_drv_supports_irq(struct
  	return phydrv->config_intr && phydrv->handle_interrupt;
  }
  
@@ -41,7 +41,7 @@ Signed-off-by: David S. Miller <davem at davemloft.net>
  }
  
  static int of_phy_led(struct phy_device *phydev,
-@@ -2958,12 +2965,14 @@ static int of_phy_led(struct phy_device
+@@ -3061,12 +3068,14 @@ static int of_phy_led(struct phy_device
  		return -ENOMEM;
  
  	cdev = &phyled->led_cdev;
@@ -59,7 +59,7 @@ Signed-off-by: David S. Miller <davem at davemloft.net>
  	init_data.fwnode = of_fwnode_handle(led);
 --- a/include/linux/phy.h
 +++ b/include/linux/phy.h
-@@ -775,15 +775,19 @@ struct phy_tdr_config {
+@@ -778,15 +778,19 @@ struct phy_tdr_config {
   * struct phy_led: An LED driven by the PHY
   *
   * @list: List of LEDs
@@ -79,7 +79,7 @@ Signed-off-by: David S. Miller <davem at davemloft.net>
  /**
   * struct phy_driver - Driver structure for a particular PHY type
   *
-@@ -998,6 +1002,15 @@ struct phy_driver {
+@@ -1001,6 +1005,15 @@ struct phy_driver {
  	int (*get_sqi)(struct phy_device *dev);
  	/** @get_sqi_max: Get the maximum signal quality indication */
  	int (*get_sqi_max)(struct phy_device *dev);
diff --git a/target/linux/generic/backport-6.1/801-v6.4-08-net-phy-phy_device-Call-into-the-PHY-driver-to-set-L.patch b/target/linux/generic/backport-6.1/801-v6.4-08-net-phy-phy_device-Call-into-the-PHY-driver-to-set-L.patch
index 35d83d8a6a..804caf5d35 100644
--- a/target/linux/generic/backport-6.1/801-v6.4-08-net-phy-phy_device-Call-into-the-PHY-driver-to-set-L.patch
+++ b/target/linux/generic/backport-6.1/801-v6.4-08-net-phy-phy_device-Call-into-the-PHY-driver-to-set-L.patch
@@ -18,7 +18,7 @@ Signed-off-by: David S. Miller <davem at davemloft.net>
 
 --- a/drivers/net/phy/phy_device.c
 +++ b/drivers/net/phy/phy_device.c
-@@ -2951,6 +2951,22 @@ static int phy_led_set_brightness(struct
+@@ -3054,6 +3054,22 @@ static int phy_led_set_brightness(struct
  	return err;
  }
  
@@ -41,7 +41,7 @@ Signed-off-by: David S. Miller <davem at davemloft.net>
  static int of_phy_led(struct phy_device *phydev,
  		      struct device_node *led)
  {
-@@ -2973,6 +2989,8 @@ static int of_phy_led(struct phy_device
+@@ -3076,6 +3092,8 @@ static int of_phy_led(struct phy_device
  
  	if (phydev->drv->led_brightness_set)
  		cdev->brightness_set_blocking = phy_led_set_brightness;
@@ -52,7 +52,7 @@ Signed-off-by: David S. Miller <davem at davemloft.net>
  	init_data.fwnode = of_fwnode_handle(led);
 --- a/include/linux/phy.h
 +++ b/include/linux/phy.h
-@@ -1011,6 +1011,18 @@ struct phy_driver {
+@@ -1014,6 +1014,18 @@ struct phy_driver {
  	 */
  	int (*led_brightness_set)(struct phy_device *dev,
  				  u8 index, enum led_brightness value);
diff --git a/target/linux/generic/backport-6.1/820-v6.4-net-phy-fix-circular-LEDS_CLASS-dependencies.patch b/target/linux/generic/backport-6.1/820-v6.4-net-phy-fix-circular-LEDS_CLASS-dependencies.patch
index 66dbcb8a90..226c135f3c 100644
--- a/target/linux/generic/backport-6.1/820-v6.4-net-phy-fix-circular-LEDS_CLASS-dependencies.patch
+++ b/target/linux/generic/backport-6.1/820-v6.4-net-phy-fix-circular-LEDS_CLASS-dependencies.patch
@@ -53,7 +53,7 @@ Signed-off-by: Jakub Kicinski <kuba at kernel.org>
  	tristate "MDIO Bus/PHY emulation with fixed speed/link PHYs"
 --- a/drivers/net/phy/phy_device.c
 +++ b/drivers/net/phy/phy_device.c
-@@ -3213,7 +3213,8 @@ static int phy_probe(struct device *dev)
+@@ -3316,7 +3316,8 @@ static int phy_probe(struct device *dev)
  	/* Get the LEDs from the device tree, and instantiate standard
  	 * LEDs for them.
  	 */
diff --git a/target/linux/generic/backport-6.1/821-v6.4-net-phy-Fix-reading-LED-reg-property.patch b/target/linux/generic/backport-6.1/821-v6.4-net-phy-Fix-reading-LED-reg-property.patch
index 3710cfade2..5bb92bc946 100644
--- a/target/linux/generic/backport-6.1/821-v6.4-net-phy-Fix-reading-LED-reg-property.patch
+++ b/target/linux/generic/backport-6.1/821-v6.4-net-phy-Fix-reading-LED-reg-property.patch
@@ -18,7 +18,7 @@ Signed-off-by: Jakub Kicinski <kuba at kernel.org>
 
 --- a/drivers/net/phy/phy_device.c
 +++ b/drivers/net/phy/phy_device.c
-@@ -2974,6 +2974,7 @@ static int of_phy_led(struct phy_device
+@@ -3077,6 +3077,7 @@ static int of_phy_led(struct phy_device
  	struct led_init_data init_data = {};
  	struct led_classdev *cdev;
  	struct phy_led *phyled;
@@ -26,7 +26,7 @@ Signed-off-by: Jakub Kicinski <kuba at kernel.org>
  	int err;
  
  	phyled = devm_kzalloc(dev, sizeof(*phyled), GFP_KERNEL);
-@@ -2983,10 +2984,13 @@ static int of_phy_led(struct phy_device
+@@ -3086,10 +3087,13 @@ static int of_phy_led(struct phy_device
  	cdev = &phyled->led_cdev;
  	phyled->phydev = phydev;
  
diff --git a/target/linux/generic/backport-6.1/822-v6.4-net-phy-Manual-remove-LEDs-to-ensure-correct-orderin.patch b/target/linux/generic/backport-6.1/822-v6.4-net-phy-Manual-remove-LEDs-to-ensure-correct-orderin.patch
index 80ac785cdb..f7952d9f0c 100644
--- a/target/linux/generic/backport-6.1/822-v6.4-net-phy-Manual-remove-LEDs-to-ensure-correct-orderin.patch
+++ b/target/linux/generic/backport-6.1/822-v6.4-net-phy-Manual-remove-LEDs-to-ensure-correct-orderin.patch
@@ -22,7 +22,7 @@ Signed-off-by: David S. Miller <davem at davemloft.net>
 
 --- a/drivers/net/phy/phy_device.c
 +++ b/drivers/net/phy/phy_device.c
-@@ -2967,6 +2967,15 @@ static int phy_led_blink_set(struct led_
+@@ -3070,6 +3070,15 @@ static int phy_led_blink_set(struct led_
  	return err;
  }
  
@@ -38,7 +38,7 @@ Signed-off-by: David S. Miller <davem at davemloft.net>
  static int of_phy_led(struct phy_device *phydev,
  		      struct device_node *led)
  {
-@@ -3000,7 +3009,7 @@ static int of_phy_led(struct phy_device
+@@ -3103,7 +3112,7 @@ static int of_phy_led(struct phy_device
  	init_data.fwnode = of_fwnode_handle(led);
  	init_data.devname_mandatory = true;
  
@@ -47,7 +47,7 @@ Signed-off-by: David S. Miller <davem at davemloft.net>
  	if (err)
  		return err;
  
-@@ -3029,6 +3038,7 @@ static int of_phy_leds(struct phy_device
+@@ -3132,6 +3141,7 @@ static int of_phy_leds(struct phy_device
  		err = of_phy_led(phydev, led);
  		if (err) {
  			of_node_put(led);
@@ -55,7 +55,7 @@ Signed-off-by: David S. Miller <davem at davemloft.net>
  			return err;
  		}
  	}
-@@ -3234,6 +3244,9 @@ static int phy_remove(struct device *dev
+@@ -3337,6 +3347,9 @@ static int phy_remove(struct device *dev
  
  	cancel_delayed_work_sync(&phydev->state_queue);
  
diff --git a/target/linux/generic/backport-6.1/826-v6.6-02-net-phy-phy_device-Call-into-the-PHY-driver-to-set-L.patch b/target/linux/generic/backport-6.1/826-v6.6-02-net-phy-phy_device-Call-into-the-PHY-driver-to-set-L.patch
index 0bf1e03c49..947db79f06 100644
--- a/target/linux/generic/backport-6.1/826-v6.6-02-net-phy-phy_device-Call-into-the-PHY-driver-to-set-L.patch
+++ b/target/linux/generic/backport-6.1/826-v6.6-02-net-phy-phy_device-Call-into-the-PHY-driver-to-set-L.patch
@@ -23,7 +23,7 @@ Signed-off-by: Jakub Kicinski <kuba at kernel.org>
 
 --- a/drivers/net/phy/phy_device.c
 +++ b/drivers/net/phy/phy_device.c
-@@ -2967,6 +2967,61 @@ static int phy_led_blink_set(struct led_
+@@ -3070,6 +3070,61 @@ static int phy_led_blink_set(struct led_
  	return err;
  }
  
@@ -85,7 +85,7 @@ Signed-off-by: Jakub Kicinski <kuba at kernel.org>
  static void phy_leds_unregister(struct phy_device *phydev)
  {
  	struct phy_led *phyled;
-@@ -3004,6 +3059,19 @@ static int of_phy_led(struct phy_device
+@@ -3107,6 +3162,19 @@ static int of_phy_led(struct phy_device
  		cdev->brightness_set_blocking = phy_led_set_brightness;
  	if (phydev->drv->led_blink_set)
  		cdev->blink_set = phy_led_blink_set;
@@ -107,7 +107,7 @@ Signed-off-by: Jakub Kicinski <kuba at kernel.org>
  	init_data.fwnode = of_fwnode_handle(led);
 --- a/include/linux/phy.h
 +++ b/include/linux/phy.h
-@@ -1023,6 +1023,39 @@ struct phy_driver {
+@@ -1026,6 +1026,39 @@ struct phy_driver {
  	int (*led_blink_set)(struct phy_device *dev, u8 index,
  			     unsigned long *delay_on,
  			     unsigned long *delay_off);
diff --git a/target/linux/generic/backport-6.1/835-v6.9-net-phy-add-support-for-PHY-LEDs-polarity-modes.patch b/target/linux/generic/backport-6.1/835-v6.9-net-phy-add-support-for-PHY-LEDs-polarity-modes.patch
index 97fbd2bfc5..1f49b3af0c 100644
--- a/target/linux/generic/backport-6.1/835-v6.9-net-phy-add-support-for-PHY-LEDs-polarity-modes.patch
+++ b/target/linux/generic/backport-6.1/835-v6.9-net-phy-add-support-for-PHY-LEDs-polarity-modes.patch
@@ -28,7 +28,7 @@ Signed-off-by: Jakub Kicinski <kuba at kernel.org>
 
 --- a/drivers/net/phy/phy_device.c
 +++ b/drivers/net/phy/phy_device.c
-@@ -3037,6 +3037,7 @@ static int of_phy_led(struct phy_device
+@@ -3140,6 +3140,7 @@ static int of_phy_led(struct phy_device
  	struct device *dev = &phydev->mdio.dev;
  	struct led_init_data init_data = {};
  	struct led_classdev *cdev;
@@ -36,7 +36,7 @@ Signed-off-by: Jakub Kicinski <kuba at kernel.org>
  	struct phy_led *phyled;
  	u32 index;
  	int err;
-@@ -3054,6 +3055,21 @@ static int of_phy_led(struct phy_device
+@@ -3157,6 +3158,21 @@ static int of_phy_led(struct phy_device
  	if (index > U8_MAX)
  		return -EINVAL;
  
@@ -60,7 +60,7 @@ Signed-off-by: Jakub Kicinski <kuba at kernel.org>
  		cdev->brightness_set_blocking = phy_led_set_brightness;
 --- a/include/linux/phy.h
 +++ b/include/linux/phy.h
-@@ -788,6 +788,15 @@ struct phy_led {
+@@ -791,6 +791,15 @@ struct phy_led {
  
  #define to_phy_led(d) container_of(d, struct phy_led, led_cdev)
  
@@ -76,7 +76,7 @@ Signed-off-by: Jakub Kicinski <kuba at kernel.org>
  /**
   * struct phy_driver - Driver structure for a particular PHY type
   *
-@@ -1056,6 +1065,19 @@ struct phy_driver {
+@@ -1059,6 +1068,19 @@ struct phy_driver {
  	int (*led_hw_control_get)(struct phy_device *dev, u8 index,
  				  unsigned long *rules);
  
diff --git a/target/linux/generic/config-6.1 b/target/linux/generic/config-6.1
index 3730e584ac..7da5f21532 100644
--- a/target/linux/generic/config-6.1
+++ b/target/linux/generic/config-6.1
@@ -5078,6 +5078,7 @@ CONFIG_PWRSEQ_SIMPLE=y
 # CONFIG_QCA7000_SPI is not set
 # CONFIG_QCA7000_UART is not set
 # CONFIG_QCA83XX_PHY is not set
+# CONFIG_QCA807X_PHY is not set
 # CONFIG_QCA808X_PHY is not set
 # CONFIG_QCOM_A7PLL is not set
 # CONFIG_QCOM_BAM_DMUX is not set
diff --git a/target/linux/generic/pending-6.1/703-phy-add-detach-callback-to-struct-phy_driver.patch b/target/linux/generic/pending-6.1/703-phy-add-detach-callback-to-struct-phy_driver.patch
index b658d5205e..0c88109192 100644
--- a/target/linux/generic/pending-6.1/703-phy-add-detach-callback-to-struct-phy_driver.patch
+++ b/target/linux/generic/pending-6.1/703-phy-add-detach-callback-to-struct-phy_driver.patch
@@ -11,7 +11,7 @@ Signed-off-by: Gabor Juhos <juhosg at openwrt.org>
 
 --- a/drivers/net/phy/phy_device.c
 +++ b/drivers/net/phy/phy_device.c
-@@ -1756,6 +1756,9 @@ void phy_detach(struct phy_device *phyde
+@@ -1852,6 +1852,9 @@ void phy_detach(struct phy_device *phyde
  	struct module *ndev_owner = NULL;
  	struct mii_bus *bus;
  
@@ -23,7 +23,7 @@ Signed-off-by: Gabor Juhos <juhosg at openwrt.org>
  			sysfs_remove_link(&dev->dev.kobj, "phydev");
 --- a/include/linux/phy.h
 +++ b/include/linux/phy.h
-@@ -897,6 +897,12 @@ struct phy_driver {
+@@ -900,6 +900,12 @@ struct phy_driver {
  	/** @handle_interrupt: Override default interrupt handling */
  	irqreturn_t (*handle_interrupt)(struct phy_device *phydev);
  
diff --git a/target/linux/ramips/patches-6.1/720-Revert-net-phy-simplify-phy_link_change-arguments.patch b/target/linux/ramips/patches-6.1/720-Revert-net-phy-simplify-phy_link_change-arguments.patch
index 26d66d6d4a..73ed55a085 100644
--- a/target/linux/ramips/patches-6.1/720-Revert-net-phy-simplify-phy_link_change-arguments.patch
+++ b/target/linux/ramips/patches-6.1/720-Revert-net-phy-simplify-phy_link_change-arguments.patch
@@ -107,7 +107,7 @@ still required by target/linux/ramips/files/drivers/net/ethernet/ralink/mdio.c
  	bool tx_pause, rx_pause;
 --- a/include/linux/phy.h
 +++ b/include/linux/phy.h
-@@ -736,7 +736,7 @@ struct phy_device {
+@@ -739,7 +739,7 @@ struct phy_device {
  
  	int pma_extable;
  
diff --git a/target/linux/ramips/patches-6.1/721-NET-no-auto-carrier-off-support.patch b/target/linux/ramips/patches-6.1/721-NET-no-auto-carrier-off-support.patch
index 9f1c3e05f1..2594c66048 100644
--- a/target/linux/ramips/patches-6.1/721-NET-no-auto-carrier-off-support.patch
+++ b/target/linux/ramips/patches-6.1/721-NET-no-auto-carrier-off-support.patch
@@ -37,7 +37,7 @@ Signed-off-by: John Crispin <blogic at openwrt.org>
  		break;
 --- a/include/linux/phy.h
 +++ b/include/linux/phy.h
-@@ -644,6 +644,7 @@ struct phy_device {
+@@ -647,6 +647,7 @@ struct phy_device {
  	unsigned downshifted_rate:1;
  	unsigned is_on_sfp_module:1;
  	unsigned mac_managed_pm:1;




More information about the lede-commits mailing list