[openwrt/openwrt] generic: backport support for Aeonsemi AS21xxx PHY

LEDE Commits lede-commits at lists.infradead.org
Tue Sep 2 15:59:13 PDT 2025


ansuel pushed a commit to openwrt/openwrt.git, branch main:
https://git.openwrt.org/08a616b2163ed229f071569d0d009568638fb21f

commit 08a616b2163ed229f071569d0d009568638fb21f
Author: Christian Marangi <ansuelsmth at gmail.com>
AuthorDate: Sat Jul 5 13:01:21 2025 +0200

    generic: backport support for Aeonsemi AS21xxx PHY
    
    Backport support for Aeonsemi AS121xxx PHY. The PHY require dedicated
    firmware to be loaded to correctly work and support a big family of
    Aeonsemi PHY that provide from 1G to 10G speed.
    
    Automatically refresh all affected patch and file (rtl PHY).
    
    Link: https://github.com/openwrt/openwrt/pull/19816
    Signed-off-by: Christian Marangi <ansuelsmth at gmail.com>
---
 package/kernel/linux/modules/netdevices.mk         |   17 +
 ...y-pass-PHY-driver-to-.match_phy_device-OP.patch |  303 +++++
 ...net-phy-introduce-genphy_match_phy_device.patch |  109 ++
 ...phy-Add-support-for-Aeonsemi-AS21xxx-PHYs.patch | 1165 ++++++++++++++++++++
 ...y-pass-PHY-driver-to-.match_phy_device-OP.patch |  273 +++++
 ...net-phy-introduce-genphy_match_phy_device.patch |  109 ++
 ...phy-Add-support-for-Aeonsemi-AS21xxx-PHYs.patch | 1165 ++++++++++++++++++++
 ..._en8811h-Add-the-Airoha-EN8811H-PHY-drive.patch |    8 +-
 ....7-net-phy-amd-Support-the-Altima-AMI101L.patch |    2 +-
 ...y-add-support-for-PHY-LEDs-polarity-modes.patch |    6 +-
 ...support-active-high-property-for-PHY-LEDs.patch |    2 +-
 ...5-01-v6.8-net-phy-add-possible-interfaces.patch |    2 +-
 target/linux/generic/config-6.12                   |    1 +
 target/linux/generic/config-6.6                    |    1 +
 .../hack-6.12/700-swconfig_switch_drivers.patch    |    2 +-
 .../hack-6.12/735-net-phy-realtek-rtl8261n.patch   |    4 +-
 .../hack-6.6/700-swconfig_switch_drivers.patch     |    2 +-
 .../hack-6.6/735-net-phy-realtek-rtl8261n.patch    |    4 +-
 ...-add-detach-callback-to-struct-phy_driver.patch |    2 +-
 ...altek-use-genphy_soft_reset-for-2.5G-PHYs.patch |   14 +-
 ...-realtek-detect-early-version-of-RTL8221B.patch |    4 +-
 ...phy-realtek-support-interrupt-of-RTL8221B.patch |   10 +-
 ...net-phy-realtek-work-around-broken-serdes.patch |    4 +-
 ...-phy-broadcom-update-dependency-condition.patch |    2 +-
 ...-add-detach-callback-to-struct-phy_driver.patch |    2 +-
 ...altek-use-genphy_soft_reset-for-2.5G-PHYs.patch |   16 +-
 ...-realtek-detect-early-version-of-RTL8221B.patch |    4 +-
 ...phy-realtek-support-interrupt-of-RTL8221B.patch |   10 +-
 ...net-phy-realtek-work-around-broken-serdes.patch |    4 +-
 ...-phy-broadcom-update-dependency-condition.patch |    2 +-
 .../500-gsw-rtl8367s-mt7622-support.patch          |    4 +-
 .../734-net-phy-add-Airoha-EN8801SC-PHY.patch      |    6 +-
 .../737-net-dsa-add-Airoha-AN8855.patch            |    2 +-
 ...t-add-negotiation-of-in-band-capabilities.patch |    2 +-
 .../files-6.12/drivers/net/phy/rtl83xx-phy.c       |    6 +-
 ...706-include-linux-add-phy-ops-for-rtl838x.patch |    2 +-
 .../realtek/patches-6.12/720-add-rtl-phy.patch     |    4 +-
 ...-add-genphy_c45_pma_read_ext_abilities-fu.patch |    2 +-
 ...-support-for-Siflower-SF23P1211-SF23P1240.patch |    2 +-
 39 files changed, 3212 insertions(+), 67 deletions(-)

diff --git a/package/kernel/linux/modules/netdevices.mk b/package/kernel/linux/modules/netdevices.mk
index 9ae3958dfe..f27624d4cc 100644
--- a/package/kernel/linux/modules/netdevices.mk
+++ b/package/kernel/linux/modules/netdevices.mk
@@ -557,6 +557,23 @@ endef
 $(eval $(call KernelPackage,phy-vitesse))
 
 
+define KernelPackage/phy-aeonsemi-as21xxx
+  SUBMENU:=$(NETWORK_DEVICES_MENU)
+  TITLE:=Aeonsemi AS21xxx 10G Ethernet PHY
+  DEPENDS:=+aeonsemi-as21xxx-firmware +kmod-libphy
+  KCONFIG:=CONFIG_AS21XXX_PHY
+  FILES:= \
+   $(LINUX_DIR)/drivers/net/phy/as21xxx.ko
+  AUTOLOAD:=$(call AutoLoad,18,as21xxx)
+endef
+
+define KernelPackage/phy-aeonsemi-as21x1x/description
+  Kernel modules for Aeonsemi AS21x1x 10G Ethernet PHY
+endef
+
+$(eval $(call KernelPackage,phy-aeonsemi-as21xxx))
+
+
 define KernelPackage/phy-airoha-en8811h
   SUBMENU:=$(NETWORK_DEVICES_MENU)
   TITLE:=Airoha EN8811H 2.5G Ethernet PHY
diff --git a/target/linux/generic/backport-6.12/782-01-v6.16-net-phy-pass-PHY-driver-to-.match_phy_device-OP.patch b/target/linux/generic/backport-6.12/782-01-v6.16-net-phy-pass-PHY-driver-to-.match_phy_device-OP.patch
new file mode 100644
index 0000000000..cda5226904
--- /dev/null
+++ b/target/linux/generic/backport-6.12/782-01-v6.16-net-phy-pass-PHY-driver-to-.match_phy_device-OP.patch
@@ -0,0 +1,303 @@
+From 31afd6bc55cc0093c3e5b0a368319e423d4de8ea Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth at gmail.com>
+Date: Sat, 17 May 2025 22:13:45 +0200
+Subject: [PATCH] net: phy: pass PHY driver to .match_phy_device OP
+
+Pass PHY driver pointer to .match_phy_device OP in addition to phydev.
+Having access to the PHY driver struct might be useful to check the
+PHY ID of the driver is being matched for in case the PHY ID scanned in
+the phydev is not consistent.
+
+A scenario for this is a PHY that change PHY ID after a firmware is
+loaded, in such case, the PHY ID stored in PHY device struct is not
+valid anymore and PHY will manually scan the ID in the match_phy_device
+function.
+
+Having the PHY driver info is also useful for those PHY driver that
+implement multiple simple .match_phy_device OP to match specific MMD PHY
+ID. With this extra info if the parsing logic is the same, the matching
+function can be generalized by using the phy_id in the PHY driver
+instead of hardcoding.
+
+Rust wrapper callback is updated to align to the new match_phy_device
+arguments.
+
+Suggested-by: Russell King (Oracle) <rmk+kernel at armlinux.org.uk>
+Reviewed-by: Russell King (Oracle) <rmk+kernel at armlinux.org.uk>
+Signed-off-by: Christian Marangi <ansuelsmth at gmail.com>
+Reviewed-by: Benno Lossin <lossin at kernel.org> # for Rust
+Reviewed-by: FUJITA Tomonori <fujita.tomonori at gmail.com>
+Link: https://patch.msgid.link/20250517201353.5137-2-ansuelsmth@gmail.com
+Signed-off-by: Jakub Kicinski <kuba at kernel.org>
+---
+ drivers/net/phy/bcm87xx.c              |  6 ++++--
+ drivers/net/phy/icplus.c               |  6 ++++--
+ drivers/net/phy/marvell10g.c           | 12 ++++++++----
+ drivers/net/phy/micrel.c               |  6 ++++--
+ drivers/net/phy/nxp-c45-tja11xx.c      | 12 ++++++++----
+ drivers/net/phy/nxp-tja11xx.c          |  6 ++++--
+ drivers/net/phy/phy_device.c           |  2 +-
+ drivers/net/phy/realtek/realtek_main.c | 27 +++++++++++++++++---------
+ drivers/net/phy/teranetics.c           |  3 ++-
+ include/linux/phy.h                    |  3 ++-
+ rust/kernel/net/phy.rs                 |  1 +
+ 11 files changed, 56 insertions(+), 28 deletions(-)
+
+--- a/drivers/net/phy/bcm87xx.c
++++ b/drivers/net/phy/bcm87xx.c
+@@ -185,12 +185,14 @@ static irqreturn_t bcm87xx_handle_interr
+ 	return IRQ_HANDLED;
+ }
+ 
+-static int bcm8706_match_phy_device(struct phy_device *phydev)
++static int bcm8706_match_phy_device(struct phy_device *phydev,
++				    const struct phy_driver *phydrv)
+ {
+ 	return phydev->c45_ids.device_ids[4] == PHY_ID_BCM8706;
+ }
+ 
+-static int bcm8727_match_phy_device(struct phy_device *phydev)
++static int bcm8727_match_phy_device(struct phy_device *phydev,
++				    const struct phy_driver *phydrv)
+ {
+ 	return phydev->c45_ids.device_ids[4] == PHY_ID_BCM8727;
+ }
+--- a/drivers/net/phy/icplus.c
++++ b/drivers/net/phy/icplus.c
+@@ -520,12 +520,14 @@ static int ip101a_g_match_phy_device(str
+ 	return ip101a == !ret;
+ }
+ 
+-static int ip101a_match_phy_device(struct phy_device *phydev)
++static int ip101a_match_phy_device(struct phy_device *phydev,
++				   const struct phy_driver *phydrv)
+ {
+ 	return ip101a_g_match_phy_device(phydev, true);
+ }
+ 
+-static int ip101g_match_phy_device(struct phy_device *phydev)
++static int ip101g_match_phy_device(struct phy_device *phydev,
++				   const struct phy_driver *phydrv)
+ {
+ 	return ip101a_g_match_phy_device(phydev, false);
+ }
+--- a/drivers/net/phy/marvell10g.c
++++ b/drivers/net/phy/marvell10g.c
+@@ -1284,7 +1284,8 @@ static int mv3310_get_number_of_ports(st
+ 	return ret + 1;
+ }
+ 
+-static int mv3310_match_phy_device(struct phy_device *phydev)
++static int mv3310_match_phy_device(struct phy_device *phydev,
++				   const struct phy_driver *phydrv)
+ {
+ 	if ((phydev->c45_ids.device_ids[MDIO_MMD_PMAPMD] &
+ 	     MARVELL_PHY_ID_MASK) != MARVELL_PHY_ID_88X3310)
+@@ -1293,7 +1294,8 @@ static int mv3310_match_phy_device(struc
+ 	return mv3310_get_number_of_ports(phydev) == 1;
+ }
+ 
+-static int mv3340_match_phy_device(struct phy_device *phydev)
++static int mv3340_match_phy_device(struct phy_device *phydev,
++				   const struct phy_driver *phydrv)
+ {
+ 	if ((phydev->c45_ids.device_ids[MDIO_MMD_PMAPMD] &
+ 	     MARVELL_PHY_ID_MASK) != MARVELL_PHY_ID_88X3310)
+@@ -1317,12 +1319,14 @@ static int mv211x_match_phy_device(struc
+ 	return !!(val & MDIO_PCS_SPEED_5G) == has_5g;
+ }
+ 
+-static int mv2110_match_phy_device(struct phy_device *phydev)
++static int mv2110_match_phy_device(struct phy_device *phydev,
++				   const struct phy_driver *phydrv)
+ {
+ 	return mv211x_match_phy_device(phydev, true);
+ }
+ 
+-static int mv2111_match_phy_device(struct phy_device *phydev)
++static int mv2111_match_phy_device(struct phy_device *phydev,
++				   const struct phy_driver *phydrv)
+ {
+ 	return mv211x_match_phy_device(phydev, false);
+ }
+--- a/drivers/net/phy/micrel.c
++++ b/drivers/net/phy/micrel.c
+@@ -768,7 +768,8 @@ static int ksz8051_ksz8795_match_phy_dev
+ 		return !ret;
+ }
+ 
+-static int ksz8051_match_phy_device(struct phy_device *phydev)
++static int ksz8051_match_phy_device(struct phy_device *phydev,
++				    const struct phy_driver *phydrv)
+ {
+ 	return ksz8051_ksz8795_match_phy_device(phydev, true);
+ }
+@@ -888,7 +889,8 @@ static int ksz8061_config_init(struct ph
+ 	return kszphy_config_init(phydev);
+ }
+ 
+-static int ksz8795_match_phy_device(struct phy_device *phydev)
++static int ksz8795_match_phy_device(struct phy_device *phydev,
++				    const struct phy_driver *phydrv)
+ {
+ 	return ksz8051_ksz8795_match_phy_device(phydev, false);
+ }
+--- a/drivers/net/phy/nxp-c45-tja11xx.c
++++ b/drivers/net/phy/nxp-c45-tja11xx.c
+@@ -1944,13 +1944,15 @@ static int nxp_c45_macsec_ability(struct
+ 	return macsec_ability;
+ }
+ 
+-static int tja1103_match_phy_device(struct phy_device *phydev)
++static int tja1103_match_phy_device(struct phy_device *phydev,
++				    const struct phy_driver *phydrv)
+ {
+ 	return phy_id_compare(phydev->phy_id, PHY_ID_TJA_1103, PHY_ID_MASK) &&
+ 	       !nxp_c45_macsec_ability(phydev);
+ }
+ 
+-static int tja1104_match_phy_device(struct phy_device *phydev)
++static int tja1104_match_phy_device(struct phy_device *phydev,
++				    const struct phy_driver *phydrv)
+ {
+ 	return phy_id_compare(phydev->phy_id, PHY_ID_TJA_1103, PHY_ID_MASK) &&
+ 	       nxp_c45_macsec_ability(phydev);
+--- a/drivers/net/phy/nxp-tja11xx.c
++++ b/drivers/net/phy/nxp-tja11xx.c
+@@ -646,12 +646,14 @@ static int tja1102_match_phy_device(stru
+ 	return !ret;
+ }
+ 
+-static int tja1102_p0_match_phy_device(struct phy_device *phydev)
++static int tja1102_p0_match_phy_device(struct phy_device *phydev,
++				       const struct phy_driver *phydrv)
+ {
+ 	return tja1102_match_phy_device(phydev, true);
+ }
+ 
+-static int tja1102_p1_match_phy_device(struct phy_device *phydev)
++static int tja1102_p1_match_phy_device(struct phy_device *phydev,
++				       const struct phy_driver *phydrv)
+ {
+ 	return tja1102_match_phy_device(phydev, false);
+ }
+--- a/drivers/net/phy/phy_device.c
++++ b/drivers/net/phy/phy_device.c
+@@ -600,7 +600,7 @@ static int phy_bus_match(struct device *
+ 		return 0;
+ 
+ 	if (phydrv->match_phy_device)
+-		return phydrv->match_phy_device(phydev);
++		return phydrv->match_phy_device(phydev, phydrv);
+ 
+ 	if (phydev->is_c45) {
+ 		for (i = 1; i < num_ids; i++) {
+--- a/drivers/net/phy/realtek/realtek_main.c
++++ b/drivers/net/phy/realtek/realtek_main.c
+@@ -1343,13 +1343,15 @@ static bool rtlgen_supports_mmd(struct p
+ 	return val > 0;
+ }
+ 
+-static int rtlgen_match_phy_device(struct phy_device *phydev)
++static int rtlgen_match_phy_device(struct phy_device *phydev,
++				   const struct phy_driver *phydrv)
+ {
+ 	return phydev->phy_id == RTL_GENERIC_PHYID &&
+ 	       !rtlgen_supports_2_5gbps(phydev);
+ }
+ 
+-static int rtl8226_match_phy_device(struct phy_device *phydev)
++static int rtl8226_match_phy_device(struct phy_device *phydev,
++				    const struct phy_driver *phydrv)
+ {
+ 	return phydev->phy_id == RTL_GENERIC_PHYID &&
+ 	       rtlgen_supports_2_5gbps(phydev) &&
+@@ -1365,32 +1367,38 @@ static int rtlgen_is_c45_match(struct ph
+ 		return !is_c45 && (id == phydev->phy_id);
+ }
+ 
+-static int rtl8221b_match_phy_device(struct phy_device *phydev)
++static int rtl8221b_match_phy_device(struct phy_device *phydev,
++				     const struct phy_driver *phydrv)
+ {
+ 	return phydev->phy_id == RTL_8221B && rtlgen_supports_mmd(phydev);
+ }
+ 
+-static int rtl8221b_vb_cg_c22_match_phy_device(struct phy_device *phydev)
++static int rtl8221b_vb_cg_c22_match_phy_device(struct phy_device *phydev,
++					       const struct phy_driver *phydrv)
+ {
+ 	return rtlgen_is_c45_match(phydev, RTL_8221B_VB_CG, false);
+ }
+ 
+-static int rtl8221b_vb_cg_c45_match_phy_device(struct phy_device *phydev)
++static int rtl8221b_vb_cg_c45_match_phy_device(struct phy_device *phydev,
++					       const struct phy_driver *phydrv)
+ {
+ 	return rtlgen_is_c45_match(phydev, RTL_8221B_VB_CG, true);
+ }
+ 
+-static int rtl8221b_vn_cg_c22_match_phy_device(struct phy_device *phydev)
++static int rtl8221b_vn_cg_c22_match_phy_device(struct phy_device *phydev,
++					       const struct phy_driver *phydrv)
+ {
+ 	return rtlgen_is_c45_match(phydev, RTL_8221B_VN_CG, false);
+ }
+ 
+-static int rtl8221b_vn_cg_c45_match_phy_device(struct phy_device *phydev)
++static int rtl8221b_vn_cg_c45_match_phy_device(struct phy_device *phydev,
++					       const struct phy_driver *phydrv)
+ {
+ 	return rtlgen_is_c45_match(phydev, RTL_8221B_VN_CG, true);
+ }
+ 
+-static int rtl_internal_nbaset_match_phy_device(struct phy_device *phydev)
++static int rtl_internal_nbaset_match_phy_device(struct phy_device *phydev,
++						const struct phy_driver *phydrv)
+ {
+ 	if (phydev->is_c45)
+ 		return false;
+@@ -1409,7 +1417,8 @@ static int rtl_internal_nbaset_match_phy
+ 	return rtlgen_supports_2_5gbps(phydev) && !rtlgen_supports_mmd(phydev);
+ }
+ 
+-static int rtl8251b_c45_match_phy_device(struct phy_device *phydev)
++static int rtl8251b_c45_match_phy_device(struct phy_device *phydev,
++					 const struct phy_driver *phydrv)
+ {
+ 	return rtlgen_is_c45_match(phydev, RTL_8251B, true);
+ }
+--- a/drivers/net/phy/teranetics.c
++++ b/drivers/net/phy/teranetics.c
+@@ -67,7 +67,8 @@ static int teranetics_read_status(struct
+ 	return 0;
+ }
+ 
+-static int teranetics_match_phy_device(struct phy_device *phydev)
++static int teranetics_match_phy_device(struct phy_device *phydev,
++				       const struct phy_driver *phydrv)
+ {
+ 	return phydev->c45_ids.device_ids[3] == PHY_ID_TN2020;
+ }
+--- a/include/linux/phy.h
++++ b/include/linux/phy.h
+@@ -1004,7 +1004,8 @@ struct phy_driver {
+ 	 * driver for the given phydev.	 If NULL, matching is based on
+ 	 * phy_id and phy_id_mask.
+ 	 */
+-	int (*match_phy_device)(struct phy_device *phydev);
++	int (*match_phy_device)(struct phy_device *phydev,
++				const struct phy_driver *phydrv);
+ 
+ 	/**
+ 	 * @set_wol: Some devices (e.g. qnap TS-119P II) require PHY
+--- a/rust/kernel/net/phy.rs
++++ b/rust/kernel/net/phy.rs
+@@ -421,6 +421,7 @@ impl<T: Driver> Adapter<T> {
+     /// `phydev` must be passed by the corresponding callback in `phy_driver`.
+     unsafe extern "C" fn match_phy_device_callback(
+         phydev: *mut bindings::phy_device,
++        _phydrv: *const bindings::phy_driver,
+     ) -> crate::ffi::c_int {
+         // SAFETY: This callback is called only in contexts
+         // where we hold `phy_device->lock`, so the accessors on
diff --git a/target/linux/generic/backport-6.12/782-04-v6.16-net-phy-introduce-genphy_match_phy_device.patch b/target/linux/generic/backport-6.12/782-04-v6.16-net-phy-introduce-genphy_match_phy_device.patch
new file mode 100644
index 0000000000..9e28011b0c
--- /dev/null
+++ b/target/linux/generic/backport-6.12/782-04-v6.16-net-phy-introduce-genphy_match_phy_device.patch
@@ -0,0 +1,109 @@
+From d6c45707ac84c2d9f274ece1cea4dddb97996bde Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth at gmail.com>
+Date: Sat, 17 May 2025 22:13:48 +0200
+Subject: [PATCH 4/5] net: phy: introduce genphy_match_phy_device()
+
+Introduce new API, genphy_match_phy_device(), to provide a way to check
+to match a PHY driver for a PHY device based on the info stored in the
+PHY device struct.
+
+The function generalize the logic used in phy_bus_match() to check the
+PHY ID whether if C45 or C22 ID should be used for matching.
+
+This is useful for custom .match_phy_device function that wants to use
+the generic logic under some condition. (example a PHY is already setup
+and provide the correct PHY ID)
+
+Reviewed-by: Russell King (Oracle) <rmk+kernel at armlinux.org.uk>
+Signed-off-by: Christian Marangi <ansuelsmth at gmail.com>
+Link: https://patch.msgid.link/20250517201353.5137-5-ansuelsmth@gmail.com
+Signed-off-by: Jakub Kicinski <kuba at kernel.org>
+---
+ drivers/net/phy/phy_device.c | 52 +++++++++++++++++++++++++-----------
+ include/linux/phy.h          |  3 +++
+ 2 files changed, 40 insertions(+), 15 deletions(-)
+
+--- a/drivers/net/phy/phy_device.c
++++ b/drivers/net/phy/phy_device.c
+@@ -589,20 +589,26 @@ static int phy_scan_fixups(struct phy_de
+ 	return 0;
+ }
+ 
+-static int phy_bus_match(struct device *dev, const struct device_driver *drv)
++/**
++ * genphy_match_phy_device - match a PHY device with a PHY driver
++ * @phydev: target phy_device struct
++ * @phydrv: target phy_driver struct
++ *
++ * Description: Checks whether the given PHY device matches the specified
++ * PHY driver. For Clause 45 PHYs, iterates over the available device
++ * identifiers and compares them against the driver's expected PHY ID,
++ * applying the provided mask. For Clause 22 PHYs, a direct ID comparison
++ * is performed.
++ *
++ * Return: 1 if the PHY device matches the driver, 0 otherwise.
++ */
++int genphy_match_phy_device(struct phy_device *phydev,
++			    const struct phy_driver *phydrv)
+ {
+-	struct phy_device *phydev = to_phy_device(dev);
+-	const struct phy_driver *phydrv = to_phy_driver(drv);
+-	const int num_ids = ARRAY_SIZE(phydev->c45_ids.device_ids);
+-	int i;
+-
+-	if (!(phydrv->mdiodrv.flags & MDIO_DEVICE_IS_PHY))
+-		return 0;
+-
+-	if (phydrv->match_phy_device)
+-		return phydrv->match_phy_device(phydev, phydrv);
+-
+ 	if (phydev->is_c45) {
++		const int num_ids = ARRAY_SIZE(phydev->c45_ids.device_ids);
++		int i;
++
+ 		for (i = 1; i < num_ids; i++) {
+ 			if (phydev->c45_ids.device_ids[i] == 0xffffffff)
+ 				continue;
+@@ -611,11 +617,27 @@ static int phy_bus_match(struct device *
+ 					   phydrv->phy_id, phydrv->phy_id_mask))
+ 				return 1;
+ 		}
++
+ 		return 0;
+-	} else {
+-		return phy_id_compare(phydev->phy_id, phydrv->phy_id,
+-				      phydrv->phy_id_mask);
+ 	}
++
++	return phy_id_compare(phydev->phy_id, phydrv->phy_id,
++			      phydrv->phy_id_mask);
++}
++EXPORT_SYMBOL_GPL(genphy_match_phy_device);
++
++static int phy_bus_match(struct device *dev, const struct device_driver *drv)
++{
++	struct phy_device *phydev = to_phy_device(dev);
++	const struct phy_driver *phydrv = to_phy_driver(drv);
++
++	if (!(phydrv->mdiodrv.flags & MDIO_DEVICE_IS_PHY))
++		return 0;
++
++	if (phydrv->match_phy_device)
++		return phydrv->match_phy_device(phydev, phydrv);
++
++	return genphy_match_phy_device(phydev, phydrv);
+ }
+ 
+ static ssize_t
+--- a/include/linux/phy.h
++++ b/include/linux/phy.h
+@@ -1906,6 +1906,9 @@ char *phy_attached_info_irq(struct phy_d
+ 	__malloc;
+ void phy_attached_info(struct phy_device *phydev);
+ 
++int genphy_match_phy_device(struct phy_device *phydev,
++			    const struct phy_driver *phydrv);
++
+ /* Clause 22 PHY */
+ int genphy_read_abilities(struct phy_device *phydev);
+ int genphy_setup_forced(struct phy_device *phydev);
diff --git a/target/linux/generic/backport-6.12/782-05-v6.16-net-phy-Add-support-for-Aeonsemi-AS21xxx-PHYs.patch b/target/linux/generic/backport-6.12/782-05-v6.16-net-phy-Add-support-for-Aeonsemi-AS21xxx-PHYs.patch
new file mode 100644
index 0000000000..bcf9b1a6e5
--- /dev/null
+++ b/target/linux/generic/backport-6.12/782-05-v6.16-net-phy-Add-support-for-Aeonsemi-AS21xxx-PHYs.patch
@@ -0,0 +1,1165 @@
+From 830877d89edcd834e4b4d0fcc021ff619d89505e Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth at gmail.com>
+Date: Sat, 17 May 2025 22:13:49 +0200
+Subject: [PATCH 5/5] net: phy: Add support for Aeonsemi AS21xxx PHYs
+
+Add support for Aeonsemi AS21xxx 10G C45 PHYs. These PHYs integrate
+an IPC to setup some configuration and require special handling to
+sync with the parity bit. The parity bit is a way the IPC use to
+follow correct order of command sent.
+
+Supported PHYs AS21011JB1, AS21011PB1, AS21010JB1, AS21010PB1,
+AS21511JB1, AS21511PB1, AS21510JB1, AS21510PB1, AS21210JB1,
+AS21210PB1 that all register with the PHY ID 0x7500 0x7510
+before the firmware is loaded.
+
+They all support up to 5 LEDs with various HW mode supported.
+
+While implementing it was found some strange coincidence with using the
+same logic for implementing C22 in MMD regs in Broadcom PHYs.
+
+For reference here the AS21xxx PHY name logic:
+
+AS21x1xxB1
+    ^ ^^
+    | |J: Supports SyncE/PTP
+    | |P: No SyncE/PTP support
+    | 1: Supports 2nd Serdes
+    | 2: Not 2nd Serdes support
+    0: 10G, 5G, 2.5G
+    5: 5G, 2.5G
+    2: 2.5G
+
+Reviewed-by: Andrew Lunn <andrew at lunn.ch>
+Signed-off-by: Christian Marangi <ansuelsmth at gmail.com>
+Link: https://patch.msgid.link/20250517201353.5137-6-ansuelsmth@gmail.com
+Signed-off-by: Jakub Kicinski <kuba at kernel.org>
+---
+ MAINTAINERS               |    6 +
+ drivers/net/phy/Kconfig   |   12 +
+ drivers/net/phy/Makefile  |    1 +
+ drivers/net/phy/as21xxx.c | 1087 +++++++++++++++++++++++++++++++++++++
+ 4 files changed, 1106 insertions(+)
+ create mode 100644 drivers/net/phy/as21xxx.c
+
+--- a/drivers/net/phy/Kconfig
++++ b/drivers/net/phy/Kconfig
+@@ -79,6 +79,18 @@ config SFP
+ 
+ comment "MII PHY device drivers"
+ 
++config AS21XXX_PHY
++	tristate "Aeonsemi AS21xxx PHYs"
++	help
++	  Currently supports the Aeonsemi AS21xxx PHY.
++
++	  These are C45 PHYs 10G that require all a generic firmware.
++
++	  Supported PHYs AS21011JB1, AS21011PB1, AS21010JB1, AS21010PB1,
++	  AS21511JB1, AS21511PB1, AS21510JB1, AS21510PB1, AS21210JB1,
++	  AS21210PB1 that all register with the PHY ID 0x7500 0x7500
++	  before the firmware is loaded.
++
+ config AIR_EN8811H_PHY
+ 	tristate "Airoha EN8811H 2.5 Gigabit PHY"
+ 	help
+--- a/drivers/net/phy/Makefile
++++ b/drivers/net/phy/Makefile
+@@ -39,6 +39,7 @@ obj-$(CONFIG_AIR_EN8811H_PHY)   += air_e
+ obj-$(CONFIG_AMD_PHY)		+= amd.o
+ obj-$(CONFIG_AMCC_QT2025_PHY)	+= qt2025.o
+ obj-$(CONFIG_AQUANTIA_PHY)	+= aquantia/
++obj-$(CONFIG_AS21XXX_PHY)	+= as21xxx.o
+ ifdef CONFIG_AX88796B_RUST_PHY
+   obj-$(CONFIG_AX88796B_PHY)	+= ax88796b_rust.o
+ else
+--- /dev/null
++++ b/drivers/net/phy/as21xxx.c
+@@ -0,0 +1,1087 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Aeonsemi AS21XXxX PHY Driver
++ *
++ * Author: Christian Marangi <ansuelsmth at gmail.com>
++ */
++
++#include <linux/bitfield.h>
++#include <linux/firmware.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/phy.h>
++
++#define VEND1_GLB_REG_CPU_RESET_ADDR_LO_BASEADDR 0x3
++#define VEND1_GLB_REG_CPU_RESET_ADDR_HI_BASEADDR 0x4
++
++#define VEND1_GLB_REG_CPU_CTRL		0xe
++#define   VEND1_GLB_CPU_CTRL_MASK	GENMASK(4, 0)
++#define   VEND1_GLB_CPU_CTRL_LED_POLARITY_MASK GENMASK(12, 8)
++#define   VEND1_GLB_CPU_CTRL_LED_POLARITY(_n) FIELD_PREP(VEND1_GLB_CPU_CTRL_LED_POLARITY_MASK, \
++							 BIT(_n))
++
++#define VEND1_FW_START_ADDR		0x100
++
++#define VEND1_GLB_REG_MDIO_INDIRECT_ADDRCMD 0x101
++#define VEND1_GLB_REG_MDIO_INDIRECT_LOAD 0x102
++
++#define VEND1_GLB_REG_MDIO_INDIRECT_STATUS 0x103
++
++#define VEND1_PTP_CLK			0x142
++#define   VEND1_PTP_CLK_EN		BIT(6)
++
++/* 5 LED at step of 0x20
++ * FE: Fast-Ethernet (10/100)
++ * GE: Gigabit-Ethernet (1000)
++ * NG: New-Generation (2500/5000/10000)
++ */
++#define VEND1_LED_REG(_n)		(0x1800 + ((_n) * 0x10))
++#define   VEND1_LED_REG_A_EVENT		GENMASK(15, 11)
++#define VEND1_LED_CONF			0x1881
++#define   VEND1_LED_CONFG_BLINK		GENMASK(7, 0)
++
++#define VEND1_SPEED_STATUS		0x4002
++#define   VEND1_SPEED_MASK		GENMASK(7, 0)
++#define   VEND1_SPEED_10000		FIELD_PREP_CONST(VEND1_SPEED_MASK, 0x3)
++#define   VEND1_SPEED_5000		FIELD_PREP_CONST(VEND1_SPEED_MASK, 0x5)
++#define   VEND1_SPEED_2500		FIELD_PREP_CONST(VEND1_SPEED_MASK, 0x9)
++#define   VEND1_SPEED_1000		FIELD_PREP_CONST(VEND1_SPEED_MASK, 0x10)
++#define   VEND1_SPEED_100		FIELD_PREP_CONST(VEND1_SPEED_MASK, 0x20)
++#define   VEND1_SPEED_10		FIELD_PREP_CONST(VEND1_SPEED_MASK, 0x0)
++
++#define VEND1_IPC_CMD			0x5801
++#define   AEON_IPC_CMD_PARITY		BIT(15)
++#define   AEON_IPC_CMD_SIZE		GENMASK(10, 6)
++#define   AEON_IPC_CMD_OPCODE		GENMASK(5, 0)
++
++#define IPC_CMD_NOOP			0x0  /* Do nothing */
++#define IPC_CMD_INFO			0x1  /* Get Firmware Version */
++#define IPC_CMD_SYS_CPU			0x2  /* SYS_CPU */
++#define IPC_CMD_BULK_DATA		0xa  /* Pass bulk data in ipc registers. */
++#define IPC_CMD_BULK_WRITE		0xc  /* Write bulk data to memory */
++#define IPC_CMD_CFG_PARAM		0x1a /* Write config parameters to memory */
++#define IPC_CMD_NG_TESTMODE		0x1b /* Set NG test mode and tone */
++#define IPC_CMD_TEMP_MON		0x15 /* Temperature monitoring function */
++#define IPC_CMD_SET_LED			0x23 /* Set led */
++
++#define VEND1_IPC_STS			0x5802
++#define   AEON_IPC_STS_PARITY		BIT(15)
++#define   AEON_IPC_STS_SIZE		GENMASK(14, 10)
++#define   AEON_IPC_STS_OPCODE		GENMASK(9, 4)
++#define   AEON_IPC_STS_STATUS		GENMASK(3, 0)
++#define   AEON_IPC_STS_STATUS_RCVD	FIELD_PREP_CONST(AEON_IPC_STS_STATUS, 0x1)
++#define   AEON_IPC_STS_STATUS_PROCESS	FIELD_PREP_CONST(AEON_IPC_STS_STATUS, 0x2)
++#define   AEON_IPC_STS_STATUS_SUCCESS	FIELD_PREP_CONST(AEON_IPC_STS_STATUS, 0x4)
++#define   AEON_IPC_STS_STATUS_ERROR	FIELD_PREP_CONST(AEON_IPC_STS_STATUS, 0x8)
++#define   AEON_IPC_STS_STATUS_BUSY	FIELD_PREP_CONST(AEON_IPC_STS_STATUS, 0xe)
++#define   AEON_IPC_STS_STATUS_READY	FIELD_PREP_CONST(AEON_IPC_STS_STATUS, 0xf)
++
++#define VEND1_IPC_DATA0			0x5808
++#define VEND1_IPC_DATA1			0x5809
++#define VEND1_IPC_DATA2			0x580a
++#define VEND1_IPC_DATA3			0x580b
++#define VEND1_IPC_DATA4			0x580c
++#define VEND1_IPC_DATA5			0x580d
++#define VEND1_IPC_DATA6			0x580e
++#define VEND1_IPC_DATA7			0x580f
++#define VEND1_IPC_DATA(_n)		(VEND1_IPC_DATA0 + (_n))
++
++/* Sub command of CMD_INFO */
++#define IPC_INFO_VERSION		0x1
++
++/* Sub command of CMD_SYS_CPU */
++#define IPC_SYS_CPU_REBOOT		0x3
++#define IPC_SYS_CPU_IMAGE_OFST		0x4
++#define IPC_SYS_CPU_IMAGE_CHECK		0x5
++#define IPC_SYS_CPU_PHY_ENABLE		0x6
++
++/* Sub command of CMD_CFG_PARAM */
++#define IPC_CFG_PARAM_DIRECT		0x4
++
++/* CFG DIRECT sub command */
++#define IPC_CFG_PARAM_DIRECT_NG_PHYCTRL	0x1
++#define IPC_CFG_PARAM_DIRECT_CU_AN	0x2
++#define IPC_CFG_PARAM_DIRECT_SDS_PCS	0x3
++#define IPC_CFG_PARAM_DIRECT_AUTO_EEE	0x4
++#define IPC_CFG_PARAM_DIRECT_SDS_PMA	0x5
++#define IPC_CFG_PARAM_DIRECT_DPC_RA	0x6
++#define IPC_CFG_PARAM_DIRECT_DPC_PKT_CHK 0x7
++#define IPC_CFG_PARAM_DIRECT_DPC_SDS_WAIT_ETH 0x8
++#define IPC_CFG_PARAM_DIRECT_WDT	0x9
++#define IPC_CFG_PARAM_DIRECT_SDS_RESTART_AN 0x10
++#define IPC_CFG_PARAM_DIRECT_TEMP_MON	0x11
++#define IPC_CFG_PARAM_DIRECT_WOL	0x12
++
++/* Sub command of CMD_TEMP_MON */
++#define IPC_CMD_TEMP_MON_GET		0x4
++
++#define AS21XXX_MDIO_AN_C22		0xffe0
++
++#define PHY_ID_AS21XXX			0x75009410
++/* AS21xxx ID Legend
++ * AS21x1xxB1
++ *     ^ ^^
++ *     | |J: Supports SyncE/PTP
++ *     | |P: No SyncE/PTP support
++ *     | 1: Supports 2nd Serdes
++ *     | 2: Not 2nd Serdes support
++ *     0: 10G, 5G, 2.5G
++ *     5: 5G, 2.5G
++ *     2: 2.5G
++ */
++#define PHY_ID_AS21011JB1		0x75009402
++#define PHY_ID_AS21011PB1		0x75009412
++#define PHY_ID_AS21010JB1		0x75009422
++#define PHY_ID_AS21010PB1		0x75009432
++#define PHY_ID_AS21511JB1		0x75009442
++#define PHY_ID_AS21511PB1		0x75009452
++#define PHY_ID_AS21510JB1		0x75009462
++#define PHY_ID_AS21510PB1		0x75009472
++#define PHY_ID_AS21210JB1		0x75009482
++#define PHY_ID_AS21210PB1		0x75009492
++#define PHY_VENDOR_AEONSEMI		0x75009400
++
++#define AEON_MAX_LEDS			5
++#define AEON_IPC_DELAY			10000
++#define AEON_IPC_TIMEOUT		(AEON_IPC_DELAY * 100)
++#define AEON_IPC_DATA_NUM_REGISTERS	8
++#define AEON_IPC_DATA_MAX		(AEON_IPC_DATA_NUM_REGISTERS * sizeof(u16))
++
++#define AEON_BOOT_ADDR			0x1000
++#define AEON_CPU_BOOT_ADDR		0x2000
++#define AEON_CPU_CTRL_FW_LOAD		(BIT(4) | BIT(2) | BIT(1) | BIT(0))
++#define AEON_CPU_CTRL_FW_START		BIT(0)
++
++enum as21xxx_led_event {
++	VEND1_LED_REG_A_EVENT_ON_10 = 0x0,
++	VEND1_LED_REG_A_EVENT_ON_100,
++	VEND1_LED_REG_A_EVENT_ON_1000,
++	VEND1_LED_REG_A_EVENT_ON_2500,
++	VEND1_LED_REG_A_EVENT_ON_5000,
++	VEND1_LED_REG_A_EVENT_ON_10000,
++	VEND1_LED_REG_A_EVENT_ON_FE_GE,
++	VEND1_LED_REG_A_EVENT_ON_NG,
++	VEND1_LED_REG_A_EVENT_ON_FULL_DUPLEX,
++	VEND1_LED_REG_A_EVENT_ON_COLLISION,
++	VEND1_LED_REG_A_EVENT_BLINK_TX,
++	VEND1_LED_REG_A_EVENT_BLINK_RX,
++	VEND1_LED_REG_A_EVENT_BLINK_ACT,
++	VEND1_LED_REG_A_EVENT_ON_LINK,
++	VEND1_LED_REG_A_EVENT_ON_LINK_BLINK_ACT,
++	VEND1_LED_REG_A_EVENT_ON_LINK_BLINK_RX,
++	VEND1_LED_REG_A_EVENT_ON_FE_GE_BLINK_ACT,
++	VEND1_LED_REG_A_EVENT_ON_NG_BLINK_ACT,
++	VEND1_LED_REG_A_EVENT_ON_NG_BLINK_FE_GE,
++	VEND1_LED_REG_A_EVENT_ON_FD_BLINK_COLLISION,
++	VEND1_LED_REG_A_EVENT_ON,
++	VEND1_LED_REG_A_EVENT_OFF,
++};
++
++struct as21xxx_led_pattern_info {
++	unsigned int pattern;
++	u16 val;
++};
++
++struct as21xxx_priv {
++	bool parity_status;
++	/* Protect concurrent IPC access */
++	struct mutex ipc_lock;
++};
++
++static struct as21xxx_led_pattern_info as21xxx_led_supported_pattern[] = {
++	{
++		.pattern = BIT(TRIGGER_NETDEV_LINK_10),
++		.val = VEND1_LED_REG_A_EVENT_ON_10
++	},
++	{
++		.pattern = BIT(TRIGGER_NETDEV_LINK_100),
++		.val = VEND1_LED_REG_A_EVENT_ON_100
++	},
++	{
++		.pattern = BIT(TRIGGER_NETDEV_LINK_1000),
++		.val = VEND1_LED_REG_A_EVENT_ON_1000
++	},
++	{
++		.pattern = BIT(TRIGGER_NETDEV_LINK_2500),
++		.val = VEND1_LED_REG_A_EVENT_ON_2500
++	},
++	{
++		.pattern = BIT(TRIGGER_NETDEV_LINK_5000),
++		.val = VEND1_LED_REG_A_EVENT_ON_5000
++	},
++	{
++		.pattern = BIT(TRIGGER_NETDEV_LINK_10000),
++		.val = VEND1_LED_REG_A_EVENT_ON_10000
++	},
++	{
++		.pattern = BIT(TRIGGER_NETDEV_LINK),
++		.val = VEND1_LED_REG_A_EVENT_ON_LINK
++	},
++	{
++		.pattern = BIT(TRIGGER_NETDEV_LINK_10) |
++			   BIT(TRIGGER_NETDEV_LINK_100) |
++			   BIT(TRIGGER_NETDEV_LINK_1000),
++		.val = VEND1_LED_REG_A_EVENT_ON_FE_GE
++	},
++	{
++		.pattern = BIT(TRIGGER_NETDEV_LINK_2500) |
++			   BIT(TRIGGER_NETDEV_LINK_5000) |
++			   BIT(TRIGGER_NETDEV_LINK_10000),
++		.val = VEND1_LED_REG_A_EVENT_ON_NG
++	},
++	{
++		.pattern = BIT(TRIGGER_NETDEV_FULL_DUPLEX),
++		.val = VEND1_LED_REG_A_EVENT_ON_FULL_DUPLEX
++	},
++	{
++		.pattern = BIT(TRIGGER_NETDEV_TX),
++		.val = VEND1_LED_REG_A_EVENT_BLINK_TX
++	},
++	{
++		.pattern = BIT(TRIGGER_NETDEV_RX),
++		.val = VEND1_LED_REG_A_EVENT_BLINK_RX
++	},
++	{
++		.pattern = BIT(TRIGGER_NETDEV_TX) |
++			   BIT(TRIGGER_NETDEV_RX),
++		.val = VEND1_LED_REG_A_EVENT_BLINK_ACT
++	},
++	{
++		.pattern = BIT(TRIGGER_NETDEV_LINK_10) |
++			   BIT(TRIGGER_NETDEV_LINK_100) |
++			   BIT(TRIGGER_NETDEV_LINK_1000) |
++			   BIT(TRIGGER_NETDEV_LINK_2500) |
++			   BIT(TRIGGER_NETDEV_LINK_5000) |
++			   BIT(TRIGGER_NETDEV_LINK_10000),
++		.val = VEND1_LED_REG_A_EVENT_ON_LINK
++	},
++	{
++		.pattern = BIT(TRIGGER_NETDEV_LINK_10) |
++			   BIT(TRIGGER_NETDEV_LINK_100) |
++			   BIT(TRIGGER_NETDEV_LINK_1000) |
++			   BIT(TRIGGER_NETDEV_LINK_2500) |
++			   BIT(TRIGGER_NETDEV_LINK_5000) |
++			   BIT(TRIGGER_NETDEV_LINK_10000) |
++			   BIT(TRIGGER_NETDEV_TX) |
++			   BIT(TRIGGER_NETDEV_RX),
++		.val = VEND1_LED_REG_A_EVENT_ON_LINK_BLINK_ACT
++	},
++	{
++		.pattern = BIT(TRIGGER_NETDEV_LINK_10) |
++			   BIT(TRIGGER_NETDEV_LINK_100) |
++			   BIT(TRIGGER_NETDEV_LINK_1000) |
++			   BIT(TRIGGER_NETDEV_LINK_2500) |
++			   BIT(TRIGGER_NETDEV_LINK_5000) |
++			   BIT(TRIGGER_NETDEV_LINK_10000) |
++			   BIT(TRIGGER_NETDEV_RX),
++		.val = VEND1_LED_REG_A_EVENT_ON_LINK_BLINK_RX
++	},
++	{
++		.pattern = BIT(TRIGGER_NETDEV_LINK_10) |
++			   BIT(TRIGGER_NETDEV_LINK_100) |
++			   BIT(TRIGGER_NETDEV_LINK_1000) |
++			   BIT(TRIGGER_NETDEV_TX) |
++			   BIT(TRIGGER_NETDEV_RX),
++		.val = VEND1_LED_REG_A_EVENT_ON_FE_GE_BLINK_ACT
++	},
++	{
++		.pattern = BIT(TRIGGER_NETDEV_LINK_2500) |
++			   BIT(TRIGGER_NETDEV_LINK_5000) |
++			   BIT(TRIGGER_NETDEV_LINK_10000) |
++			   BIT(TRIGGER_NETDEV_TX) |
++			   BIT(TRIGGER_NETDEV_RX),
++		.val = VEND1_LED_REG_A_EVENT_ON_NG_BLINK_ACT
++	}
++};
++
++static int aeon_firmware_boot(struct phy_device *phydev, const u8 *data,
++			      size_t size)
++{
++	int i, ret;
++	u16 val;
++
++	ret = phy_modify_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLB_REG_CPU_CTRL,
++			     VEND1_GLB_CPU_CTRL_MASK, AEON_CPU_CTRL_FW_LOAD);
++	if (ret)
++		return ret;
++
++	ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_FW_START_ADDR,
++			    AEON_BOOT_ADDR);
++	if (ret)
++		return ret;
++
++	ret = phy_modify_mmd(phydev, MDIO_MMD_VEND1,
++			     VEND1_GLB_REG_MDIO_INDIRECT_ADDRCMD,
++			     0x3ffc, 0xc000);
++	if (ret)
++		return ret;
++
++	val = phy_read_mmd(phydev, MDIO_MMD_VEND1,
++			   VEND1_GLB_REG_MDIO_INDIRECT_STATUS);
++	if (val > 1) {
++		phydev_err(phydev, "wrong origin mdio_indirect_status: %x\n", val);
++		return -EINVAL;
++	}
++
++	/* Firmware is always aligned to u16 */
++	for (i = 0; i < size; i += 2) {
++		val = data[i + 1] << 8 | data[i];
++
++		ret = phy_write_mmd(phydev, MDIO_MMD_VEND1,
++				    VEND1_GLB_REG_MDIO_INDIRECT_LOAD, val);
++		if (ret)
++			return ret;
++	}
++
++	ret = phy_write_mmd(phydev, MDIO_MMD_VEND1,
++			    VEND1_GLB_REG_CPU_RESET_ADDR_LO_BASEADDR,
++			    lower_16_bits(AEON_CPU_BOOT_ADDR));
++	if (ret)
++		return ret;
++
++	ret = phy_write_mmd(phydev, MDIO_MMD_VEND1,
++			    VEND1_GLB_REG_CPU_RESET_ADDR_HI_BASEADDR,
++			    upper_16_bits(AEON_CPU_BOOT_ADDR));
++	if (ret)
++		return ret;
++
++	return phy_modify_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLB_REG_CPU_CTRL,
++			      VEND1_GLB_CPU_CTRL_MASK, AEON_CPU_CTRL_FW_START);
++}
++
++static int aeon_firmware_load(struct phy_device *phydev)
++{
++	struct device *dev = &phydev->mdio.dev;
++	const struct firmware *fw;
++	const char *fw_name;
++	int ret;
++
++	ret = of_property_read_string(dev->of_node, "firmware-name",
++				      &fw_name);
++	if (ret)
++		return ret;
++
++	ret = request_firmware(&fw, fw_name, dev);
++	if (ret) {
++		phydev_err(phydev, "failed to find FW file %s (%d)\n",
++			   fw_name, ret);
++		return ret;
++	}
++
++	ret = aeon_firmware_boot(phydev, fw->data, fw->size);
++
++	release_firmware(fw);
++
++	return ret;
++}
++
++static bool aeon_ipc_ready(u16 val, bool parity_status)
++{
++	u16 status;
++
++	if (FIELD_GET(AEON_IPC_STS_PARITY, val) != parity_status)
++		return false;
++
++	status = val & AEON_IPC_STS_STATUS;
++
++	return status != AEON_IPC_STS_STATUS_RCVD &&
++	       status != AEON_IPC_STS_STATUS_PROCESS &&
++	       status != AEON_IPC_STS_STATUS_BUSY;
++}
++
++static int aeon_ipc_wait_cmd(struct phy_device *phydev, bool parity_status)
++{
++	u16 val;
++
++	/* Exit condition logic:
++	 * - Wait for parity bit equal
++	 * - Wait for status success, error OR ready
++	 */
++	return phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1, VEND1_IPC_STS, val,
++					 aeon_ipc_ready(val, parity_status),
++					 AEON_IPC_DELAY, AEON_IPC_TIMEOUT, false);
++}
++
++static int aeon_ipc_send_cmd(struct phy_device *phydev,
++			     struct as21xxx_priv *priv,
++			     u16 cmd, u16 *ret_sts)
++{
++	bool curr_parity;
++	int ret;
++
++	/* The IPC sync by using a single parity bit.
++	 * Each CMD have alternately this bit set or clear
++	 * to understand correct flow and packet order.
++	 */
++	curr_parity = priv->parity_status;
++	if (priv->parity_status)
++		cmd |= AEON_IPC_CMD_PARITY;
++
++	/* Always update parity for next packet */
++	priv->parity_status = !priv->parity_status;
++
++	ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_IPC_CMD, cmd);
++	if (ret)
++		return ret;
++
++	/* Wait for packet to be processed */
++	usleep_range(AEON_IPC_DELAY, AEON_IPC_DELAY + 5000);
++
++	/* With no ret_sts, ignore waiting for packet completion
++	 * (ipc parity bit sync)
++	 */
++	if (!ret_sts)
++		return 0;
++
++	ret = aeon_ipc_wait_cmd(phydev, curr_parity);
++	if (ret)
++		return ret;
++
++	ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_IPC_STS);
++	if (ret < 0)
++		return ret;
++
++	*ret_sts = ret;
++	if ((*ret_sts & AEON_IPC_STS_STATUS) != AEON_IPC_STS_STATUS_SUCCESS)
++		return -EINVAL;
++
++	return 0;
++}
++
++/* If data is NULL, return 0 or negative error.
++ * If data not NULL, return number of Bytes received from IPC or
++ * a negative error.
++ */
++static int aeon_ipc_send_msg(struct phy_device *phydev,
++			     u16 opcode, u16 *data, unsigned int data_len,
++			     u16 *ret_data)
++{
++	struct as21xxx_priv *priv = phydev->priv;
++	unsigned int ret_size;
++	u16 cmd, ret_sts;
++	int ret;
++	int i;
++
++	/* IPC have a max of 8 register to transfer data,
++	 * make sure we never exceed this.
++	 */
++	if (data_len > AEON_IPC_DATA_MAX)
++		return -EINVAL;
++
++	for (i = 0; i < data_len / sizeof(u16); i++)
++		phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_IPC_DATA(i),
++			      data[i]);
++
++	cmd = FIELD_PREP(AEON_IPC_CMD_SIZE, data_len) |
++	      FIELD_PREP(AEON_IPC_CMD_OPCODE, opcode);
++
++	mutex_lock(&priv->ipc_lock);
++
++	ret = aeon_ipc_send_cmd(phydev, priv, cmd, &ret_sts);
++	if (ret) {
++		phydev_err(phydev, "failed to send ipc msg for %x: %d\n",
++			   opcode, ret);
++		goto out;
++	}
++
++	if (!data)
++		goto out;
++
++	if ((ret_sts & AEON_IPC_STS_STATUS) == AEON_IPC_STS_STATUS_ERROR) {
++		ret = -EINVAL;
++		goto out;
++	}
++
++	/* Prevent IPC from stack smashing the kernel.
++	 * We can't trust IPC to return a good value and we always
++	 * preallocate space for 16 Bytes.
++	 */
++	ret_size = FIELD_GET(AEON_IPC_STS_SIZE, ret_sts);
++	if (ret_size > AEON_IPC_DATA_MAX) {
++		ret = -EINVAL;
++		goto out;
++	}
++
++	/* Read data from IPC data register for ret_size value from IPC */
++	for (i = 0; i < DIV_ROUND_UP(ret_size, sizeof(u16)); i++) {
++		ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_IPC_DATA(i));
++		if (ret < 0)
++			goto out;
++
++		ret_data[i] = ret;
++	}
++
++	ret = ret_size;
++
++out:
++	mutex_unlock(&priv->ipc_lock);
++
++	return ret;
++}
++
++static int aeon_ipc_noop(struct phy_device *phydev,
++			 struct as21xxx_priv *priv, u16 *ret_sts)
++{
++	u16 cmd;
++
++	cmd = FIELD_PREP(AEON_IPC_CMD_SIZE, 0) |
++	      FIELD_PREP(AEON_IPC_CMD_OPCODE, IPC_CMD_NOOP);
++
++	return aeon_ipc_send_cmd(phydev, priv, cmd, ret_sts);
++}
++
++/* Logic to sync parity bit with IPC.
++ * We send 2 NOP cmd with same partity and we wait for IPC
++ * to handle the packet only for the second one. This way
++ * we make sure we are sync for every next cmd.
++ */
++static int aeon_ipc_sync_parity(struct phy_device *phydev,
++				struct as21xxx_priv *priv)
++{
++	u16 ret_sts;
++	int ret;
++
++	mutex_lock(&priv->ipc_lock);
++
++	/* Send NOP with no parity */
++	aeon_ipc_noop(phydev, priv, NULL);
++
++	/* Reset packet parity */
++	priv->parity_status = false;
++
++	/* Send second NOP with no parity */
++	ret = aeon_ipc_noop(phydev, priv, &ret_sts);
++
++	mutex_unlock(&priv->ipc_lock);
++
++	/* We expect to return -EINVAL */
++	if (ret != -EINVAL)
++		return ret;
++
++	if ((ret_sts & AEON_IPC_STS_STATUS) != AEON_IPC_STS_STATUS_READY) {
++		phydev_err(phydev, "Invalid IPC status on sync parity: %x\n",
++			   ret_sts);
++		return -EINVAL;
++	}
++
++	return 0;
++}
++
++static int aeon_ipc_get_fw_version(struct phy_device *phydev)
++{
++	u16 ret_data[AEON_IPC_DATA_NUM_REGISTERS], data[1];
++	char fw_version[AEON_IPC_DATA_MAX + 1];
++	int ret;
++
++	data[0] = IPC_INFO_VERSION;
++
++	ret = aeon_ipc_send_msg(phydev, IPC_CMD_INFO, data,
++				sizeof(data), ret_data);
++	if (ret < 0)
++		return ret;
++
++	/* Make sure FW version is NULL terminated */
++	memcpy(fw_version, ret_data, ret);
++	fw_version[ret] = '\0';
++
++	phydev_info(phydev, "Firmware Version: %s\n", fw_version);
++
++	return 0;
++}
++
++static int aeon_dpc_ra_enable(struct phy_device *phydev)
++{
++	u16 data[2];
++
++	data[0] = IPC_CFG_PARAM_DIRECT;
++	data[1] = IPC_CFG_PARAM_DIRECT_DPC_RA;
++
++	return aeon_ipc_send_msg(phydev, IPC_CMD_CFG_PARAM, data,
++				 sizeof(data), NULL);
++}
++
++static int as21xxx_probe(struct phy_device *phydev)
++{
++	struct as21xxx_priv *priv;
++	int ret;
++
++	priv = devm_kzalloc(&phydev->mdio.dev,
++			    sizeof(*priv), GFP_KERNEL);
++	if (!priv)
++		return -ENOMEM;
++	phydev->priv = priv;
++
++	ret = devm_mutex_init(&phydev->mdio.dev,
++			      &priv->ipc_lock);
++	if (ret)
++		return ret;
++
++	ret = aeon_ipc_sync_parity(phydev, priv);
++	if (ret)
++		return ret;
++
++	ret = aeon_ipc_get_fw_version(phydev);
++	if (ret)
++		return ret;
++
++	/* Enable PTP clk if not already Enabled */
++	ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, VEND1_PTP_CLK,
++			       VEND1_PTP_CLK_EN);
++	if (ret)
++		return ret;
++
++	return aeon_dpc_ra_enable(phydev);
++}
++
++static int as21xxx_read_link(struct phy_device *phydev, int *bmcr)
++{
++	int status;
++
++	/* Normal C22 BMCR report inconsistent data, use
++	 * the mapped C22 in C45 to have more consistent link info.
++	 */
++	*bmcr = phy_read_mmd(phydev, MDIO_MMD_AN,
++			     AS21XXX_MDIO_AN_C22 + MII_BMCR);
++	if (*bmcr < 0)
++		return *bmcr;
++
++	/* Autoneg is being started, therefore disregard current
++	 * link status and report link as down.
++	 */
++	if (*bmcr & BMCR_ANRESTART) {
++		phydev->link = 0;
++		return 0;
++	}
++
++	status = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_STAT1);
++	if (status < 0)
++		return status;
++
++	phydev->link = !!(status & MDIO_STAT1_LSTATUS);
++
++	return 0;
++}
++
++static int as21xxx_read_c22_lpa(struct phy_device *phydev)
++{
++	int lpagb;
++
++	/* MII_STAT1000 are only filled in the mapped C22
++	 * in C45, use that to fill lpagb values and check.
++	 */
++	lpagb = phy_read_mmd(phydev, MDIO_MMD_AN,
++			     AS21XXX_MDIO_AN_C22 + MII_STAT1000);
++	if (lpagb < 0)
++		return lpagb;
++
++	if (lpagb & LPA_1000MSFAIL) {
++		int adv = phy_read_mmd(phydev, MDIO_MMD_AN,
++				       AS21XXX_MDIO_AN_C22 + MII_CTRL1000);
++
++		if (adv < 0)
++			return adv;
++
++		if (adv & CTL1000_ENABLE_MASTER)
++			phydev_err(phydev, "Master/Slave resolution failed, maybe conflicting manual settings?\n");
++		else
++			phydev_err(phydev, "Master/Slave resolution failed\n");
++		return -ENOLINK;
++	}
++
++	mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising,
++					lpagb);
++
++	return 0;
++}
++
++static int as21xxx_read_status(struct phy_device *phydev)
++{
++	int bmcr, old_link = phydev->link;
++	int ret;
++
++	ret = as21xxx_read_link(phydev, &bmcr);
++	if (ret)
++		return ret;
++
++	/* 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;
++
++	if (phydev->autoneg == AUTONEG_ENABLE) {
++		ret = genphy_c45_read_lpa(phydev);
++		if (ret)
++			return ret;
++
++		ret = as21xxx_read_c22_lpa(phydev);
++		if (ret)
++			return ret;
++
++		phy_resolve_aneg_linkmode(phydev);
++	} else {
++		int speed;
++
++		linkmode_zero(phydev->lp_advertising);
++
++		speed = phy_read_mmd(phydev, MDIO_MMD_VEND1,
++				     VEND1_SPEED_STATUS);
++		if (speed < 0)
++			return speed;
++
++		switch (speed & VEND1_SPEED_STATUS) {
++		case VEND1_SPEED_10000:
++			phydev->speed = SPEED_10000;
++			phydev->duplex = DUPLEX_FULL;
++			break;
++		case VEND1_SPEED_5000:
++			phydev->speed = SPEED_5000;
++			phydev->duplex = DUPLEX_FULL;
++			break;
++		case VEND1_SPEED_2500:
++			phydev->speed = SPEED_2500;
++			phydev->duplex = DUPLEX_FULL;
++			break;
++		case VEND1_SPEED_1000:
++			phydev->speed = SPEED_1000;
++			if (bmcr & BMCR_FULLDPLX)
++				phydev->duplex = DUPLEX_FULL;
++			else
++				phydev->duplex = DUPLEX_HALF;
++			break;
++		case VEND1_SPEED_100:
++			phydev->speed = SPEED_100;
++			phydev->duplex = DUPLEX_FULL;
++			break;
++		case VEND1_SPEED_10:
++			phydev->speed = SPEED_10;
++			phydev->duplex = DUPLEX_FULL;
++			break;
++		default:
++			return -EINVAL;
++		}
++	}
++
++	return 0;
++}
++
++static int as21xxx_led_brightness_set(struct phy_device *phydev,
++				      u8 index, enum led_brightness value)
++{
++	u16 val = VEND1_LED_REG_A_EVENT_OFF;
++
++	if (index > AEON_MAX_LEDS)
++		return -EINVAL;
++
++	if (value)
++		val = VEND1_LED_REG_A_EVENT_ON;
++
++	return phy_modify_mmd(phydev, MDIO_MMD_VEND1,
++			      VEND1_LED_REG(index),
++			      VEND1_LED_REG_A_EVENT,
++			      FIELD_PREP(VEND1_LED_REG_A_EVENT, val));
++}
++
++static int as21xxx_led_hw_is_supported(struct phy_device *phydev, u8 index,
++				       unsigned long rules)
++{
++	int i;
++
++	if (index > AEON_MAX_LEDS)
++		return -EINVAL;
++
++	for (i = 0; i < ARRAY_SIZE(as21xxx_led_supported_pattern); i++)
++		if (rules == as21xxx_led_supported_pattern[i].pattern)
++			return 0;
++
++	return -EOPNOTSUPP;
++}
++
++static int as21xxx_led_hw_control_get(struct phy_device *phydev, u8 index,
++				      unsigned long *rules)
++{
++	int i, val;
++
++	if (index > AEON_MAX_LEDS)
++		return -EINVAL;
++
++	val = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_LED_REG(index));
++	if (val < 0)
++		return val;
++
++	val = FIELD_GET(VEND1_LED_REG_A_EVENT, val);
++	for (i = 0; i < ARRAY_SIZE(as21xxx_led_supported_pattern); i++)
++		if (val == as21xxx_led_supported_pattern[i].val) {
++			*rules = as21xxx_led_supported_pattern[i].pattern;
++			return 0;
++		}
++
++	/* Should be impossible */
++	return -EINVAL;
++}
++
++static int as21xxx_led_hw_control_set(struct phy_device *phydev, u8 index,
++				      unsigned long rules)
++{
++	u16 val = 0;
++	int i;
++
++	if (index > AEON_MAX_LEDS)
++		return -EINVAL;
++
++	for (i = 0; i < ARRAY_SIZE(as21xxx_led_supported_pattern); i++)
++		if (rules == as21xxx_led_supported_pattern[i].pattern) {
++			val = as21xxx_led_supported_pattern[i].val;
++			break;
++		}
++
++	return phy_modify_mmd(phydev, MDIO_MMD_VEND1,
++			      VEND1_LED_REG(index),
++			      VEND1_LED_REG_A_EVENT,
++			      FIELD_PREP(VEND1_LED_REG_A_EVENT, val));
++}
++
++static int as21xxx_led_polarity_set(struct phy_device *phydev, int index,
++				    unsigned long modes)
++{
++	bool led_active_low = false;
++	u16 mask, val = 0;
++	u32 mode;
++
++	if (index > AEON_MAX_LEDS)
++		return -EINVAL;
++
++	for_each_set_bit(mode, &modes, __PHY_LED_MODES_NUM) {
++		switch (mode) {
++		case PHY_LED_ACTIVE_LOW:
++			led_active_low = true;
++			break;
++		case PHY_LED_ACTIVE_HIGH: /* default mode */
++			led_active_low = false;
++			break;
++		default:
++			return -EINVAL;
++		}
++	}
++
++	mask = VEND1_GLB_CPU_CTRL_LED_POLARITY(index);
++	if (led_active_low)
++		val = VEND1_GLB_CPU_CTRL_LED_POLARITY(index);
++
++	return phy_modify_mmd(phydev, MDIO_MMD_VEND1,
++			      VEND1_GLB_REG_CPU_CTRL,
++			      mask, val);
++}
++
++static int as21xxx_match_phy_device(struct phy_device *phydev,
++				    const struct phy_driver *phydrv)
++{
++	struct as21xxx_priv *priv;
++	u16 ret_sts;
++	u32 phy_id;
++	int ret;
++
++	/* Skip PHY that are not AS21xxx or already have firmware loaded */
++	if (phydev->c45_ids.device_ids[MDIO_MMD_PCS] != PHY_ID_AS21XXX)
++		return genphy_match_phy_device(phydev, (struct phy_driver *)phydrv);
++
++	/* Read PHY ID to handle firmware just loaded */
++	ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MII_PHYSID1);
++	if (ret < 0)
++		return ret;
++	phy_id = ret << 16;
++
++	ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MII_PHYSID2);
++	if (ret < 0)
++		return ret;
++	phy_id |= ret;
++
++	/* With PHY ID not the generic AS21xxx one assume
++	 * the firmware just loaded
++	 */
++	if (phy_id != PHY_ID_AS21XXX)
++		return phy_id == phydrv->phy_id;
++
++	/* Allocate temp priv and load the firmware */
++	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
++	if (!priv)
++		return -ENOMEM;
++
++	mutex_init(&priv->ipc_lock);
++
++	ret = aeon_firmware_load(phydev);
++	if (ret)
++		goto out;
++
++	/* Sync parity... */
++	ret = aeon_ipc_sync_parity(phydev, priv);
++	if (ret)
++		goto out;
++
++	/* ...and send a third NOOP cmd to wait for firmware finish loading */
++	ret = aeon_ipc_noop(phydev, priv, &ret_sts);
++	if (ret)
++		goto out;
++
++out:
++	mutex_destroy(&priv->ipc_lock);
++	kfree(priv);
++
++	/* Return can either be 0 or a negative error code.
++	 * Returning 0 here means THIS is NOT a suitable PHY.
++	 *
++	 * For the specific case of the generic Aeonsemi PHY ID that
++	 * needs the firmware the be loaded first to have a correct PHY ID,
++	 * this is OK as a matching PHY ID will be found right after.
++	 * This relies on the driver probe order where the first PHY driver
++	 * probed is the generic one.
++	 */
++	return ret;
++}
++
++static struct phy_driver as21xxx_drivers[] = {
++	{
++		/* PHY expose in C45 as 0x7500 0x9410
++		 * before firmware is loaded.
++		 * This driver entry must be attempted first to load
++		 * the firmware and thus update the ID registers.
++		 */
++		PHY_ID_MATCH_EXACT(PHY_ID_AS21XXX),
++		.name		= "Aeonsemi AS21xxx",
++		.match_phy_device = as21xxx_match_phy_device,
++	},
++	{
++		PHY_ID_MATCH_EXACT(PHY_ID_AS21011JB1),
++		.name		= "Aeonsemi AS21011JB1",
++		.probe		= as21xxx_probe,
++		.match_phy_device = as21xxx_match_phy_device,
++		.read_status	= as21xxx_read_status,
++		.led_brightness_set = as21xxx_led_brightness_set,
++		.led_hw_is_supported = as21xxx_led_hw_is_supported,
++		.led_hw_control_set = as21xxx_led_hw_control_set,
++		.led_hw_control_get = as21xxx_led_hw_control_get,
++		.led_polarity_set = as21xxx_led_polarity_set,
++	},
++	{
++		PHY_ID_MATCH_EXACT(PHY_ID_AS21011PB1),
++		.name		= "Aeonsemi AS21011PB1",
++		.probe		= as21xxx_probe,
++		.match_phy_device = as21xxx_match_phy_device,
++		.read_status	= as21xxx_read_status,
++		.led_brightness_set = as21xxx_led_brightness_set,
++		.led_hw_is_supported = as21xxx_led_hw_is_supported,
++		.led_hw_control_set = as21xxx_led_hw_control_set,
++		.led_hw_control_get = as21xxx_led_hw_control_get,
++		.led_polarity_set = as21xxx_led_polarity_set,
++	},
++	{
++		PHY_ID_MATCH_EXACT(PHY_ID_AS21010PB1),
++		.name		= "Aeonsemi AS21010PB1",
++		.probe		= as21xxx_probe,
++		.match_phy_device = as21xxx_match_phy_device,
++		.read_status	= as21xxx_read_status,
++		.led_brightness_set = as21xxx_led_brightness_set,
++		.led_hw_is_supported = as21xxx_led_hw_is_supported,
++		.led_hw_control_set = as21xxx_led_hw_control_set,
++		.led_hw_control_get = as21xxx_led_hw_control_get,
++		.led_polarity_set = as21xxx_led_polarity_set,
++	},
++	{
++		PHY_ID_MATCH_EXACT(PHY_ID_AS21010JB1),
++		.name		= "Aeonsemi AS21010JB1",
++		.probe		= as21xxx_probe,
++		.match_phy_device = as21xxx_match_phy_device,
++		.read_status	= as21xxx_read_status,
++		.led_brightness_set = as21xxx_led_brightness_set,
++		.led_hw_is_supported = as21xxx_led_hw_is_supported,
++		.led_hw_control_set = as21xxx_led_hw_control_set,
++		.led_hw_control_get = as21xxx_led_hw_control_get,
++		.led_polarity_set = as21xxx_led_polarity_set,
++	},
++	{
++		PHY_ID_MATCH_EXACT(PHY_ID_AS21210PB1),
++		.name		= "Aeonsemi AS21210PB1",
++		.probe		= as21xxx_probe,
++		.match_phy_device = as21xxx_match_phy_device,
++		.read_status	= as21xxx_read_status,
++		.led_brightness_set = as21xxx_led_brightness_set,
++		.led_hw_is_supported = as21xxx_led_hw_is_supported,
++		.led_hw_control_set = as21xxx_led_hw_control_set,
++		.led_hw_control_get = as21xxx_led_hw_control_get,
++		.led_polarity_set = as21xxx_led_polarity_set,
++	},
++	{
++		PHY_ID_MATCH_EXACT(PHY_ID_AS21510JB1),
++		.name		= "Aeonsemi AS21510JB1",
++		.probe		= as21xxx_probe,
++		.match_phy_device = as21xxx_match_phy_device,
++		.read_status	= as21xxx_read_status,
++		.led_brightness_set = as21xxx_led_brightness_set,
++		.led_hw_is_supported = as21xxx_led_hw_is_supported,
++		.led_hw_control_set = as21xxx_led_hw_control_set,
++		.led_hw_control_get = as21xxx_led_hw_control_get,
++		.led_polarity_set = as21xxx_led_polarity_set,
++	},
++	{
++		PHY_ID_MATCH_EXACT(PHY_ID_AS21510PB1),
++		.name		= "Aeonsemi AS21510PB1",
++		.probe		= as21xxx_probe,
++		.match_phy_device = as21xxx_match_phy_device,
++		.read_status	= as21xxx_read_status,
++		.led_brightness_set = as21xxx_led_brightness_set,
++		.led_hw_is_supported = as21xxx_led_hw_is_supported,
++		.led_hw_control_set = as21xxx_led_hw_control_set,
++		.led_hw_control_get = as21xxx_led_hw_control_get,
++		.led_polarity_set = as21xxx_led_polarity_set,
++	},
++	{
++		PHY_ID_MATCH_EXACT(PHY_ID_AS21511JB1),
++		.name		= "Aeonsemi AS21511JB1",
++		.probe		= as21xxx_probe,
++		.match_phy_device = as21xxx_match_phy_device,
++		.read_status	= as21xxx_read_status,
++		.led_brightness_set = as21xxx_led_brightness_set,
++		.led_hw_is_supported = as21xxx_led_hw_is_supported,
++		.led_hw_control_set = as21xxx_led_hw_control_set,
++		.led_hw_control_get = as21xxx_led_hw_control_get,
++		.led_polarity_set = as21xxx_led_polarity_set,
++	},
++	{
++		PHY_ID_MATCH_EXACT(PHY_ID_AS21210JB1),
++		.name		= "Aeonsemi AS21210JB1",
++		.probe		= as21xxx_probe,
++		.match_phy_device = as21xxx_match_phy_device,
++		.read_status	= as21xxx_read_status,
++		.led_brightness_set = as21xxx_led_brightness_set,
++		.led_hw_is_supported = as21xxx_led_hw_is_supported,
++		.led_hw_control_set = as21xxx_led_hw_control_set,
++		.led_hw_control_get = as21xxx_led_hw_control_get,
++		.led_polarity_set = as21xxx_led_polarity_set,
++	},
++	{
++		PHY_ID_MATCH_EXACT(PHY_ID_AS21511PB1),
++		.name		= "Aeonsemi AS21511PB1",
++		.probe		= as21xxx_probe,
++		.match_phy_device = as21xxx_match_phy_device,
++		.read_status	= as21xxx_read_status,
++		.led_brightness_set = as21xxx_led_brightness_set,
++		.led_hw_is_supported = as21xxx_led_hw_is_supported,
++		.led_hw_control_set = as21xxx_led_hw_control_set,
++		.led_hw_control_get = as21xxx_led_hw_control_get,
++		.led_polarity_set = as21xxx_led_polarity_set,
++	},
++};
++module_phy_driver(as21xxx_drivers);
++
++static struct mdio_device_id __maybe_unused as21xxx_tbl[] = {
++	{ PHY_ID_MATCH_VENDOR(PHY_VENDOR_AEONSEMI) },
++	{ }
++};
++MODULE_DEVICE_TABLE(mdio, as21xxx_tbl);
++
++MODULE_DESCRIPTION("Aeonsemi AS21xxx PHY driver");
++MODULE_AUTHOR("Christian Marangi <ansuelsmth at gmail.com>");
++MODULE_LICENSE("GPL");
diff --git a/target/linux/generic/backport-6.6/782-01-v6.16-net-phy-pass-PHY-driver-to-.match_phy_device-OP.patch b/target/linux/generic/backport-6.6/782-01-v6.16-net-phy-pass-PHY-driver-to-.match_phy_device-OP.patch
new file mode 100644
index 0000000000..b8460a2b5e
--- /dev/null
+++ b/target/linux/generic/backport-6.6/782-01-v6.16-net-phy-pass-PHY-driver-to-.match_phy_device-OP.patch
@@ -0,0 +1,273 @@
+From 31afd6bc55cc0093c3e5b0a368319e423d4de8ea Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth at gmail.com>
+Date: Sat, 17 May 2025 22:13:45 +0200
+Subject: [PATCH 1/5] net: phy: pass PHY driver to .match_phy_device OP
+
+Pass PHY driver pointer to .match_phy_device OP in addition to phydev.
+Having access to the PHY driver struct might be useful to check the
+PHY ID of the driver is being matched for in case the PHY ID scanned in
+the phydev is not consistent.
+
+A scenario for this is a PHY that change PHY ID after a firmware is
+loaded, in such case, the PHY ID stored in PHY device struct is not
+valid anymore and PHY will manually scan the ID in the match_phy_device
+function.
+
+Having the PHY driver info is also useful for those PHY driver that
+implement multiple simple .match_phy_device OP to match specific MMD PHY
+ID. With this extra info if the parsing logic is the same, the matching
+function can be generalized by using the phy_id in the PHY driver
+instead of hardcoding.
+
+Rust wrapper callback is updated to align to the new match_phy_device
+arguments.
+
+Suggested-by: Russell King (Oracle) <rmk+kernel at armlinux.org.uk>
+Reviewed-by: Russell King (Oracle) <rmk+kernel at armlinux.org.uk>
+Signed-off-by: Christian Marangi <ansuelsmth at gmail.com>
+Reviewed-by: Benno Lossin <lossin at kernel.org> # for Rust
+Reviewed-by: FUJITA Tomonori <fujita.tomonori at gmail.com>
+Link: https://patch.msgid.link/20250517201353.5137-2-ansuelsmth@gmail.com
+Signed-off-by: Jakub Kicinski <kuba at kernel.org>
+---
+ drivers/net/phy/bcm87xx.c              |  6 ++++--
+ drivers/net/phy/icplus.c               |  6 ++++--
+ drivers/net/phy/marvell10g.c           | 12 ++++++++----
+ drivers/net/phy/micrel.c               |  6 ++++--
+ drivers/net/phy/nxp-c45-tja11xx.c      | 12 ++++++++----
+ drivers/net/phy/nxp-tja11xx.c          |  6 ++++--
+ drivers/net/phy/phy_device.c           |  2 +-
+ drivers/net/phy/realtek/realtek_main.c | 27 +++++++++++++++++---------
+ drivers/net/phy/teranetics.c           |  3 ++-
+ include/linux/phy.h                    |  3 ++-
+ rust/kernel/net/phy.rs                 |  1 +
+ 11 files changed, 56 insertions(+), 28 deletions(-)
+
+--- a/drivers/net/phy/bcm87xx.c
++++ b/drivers/net/phy/bcm87xx.c
+@@ -185,12 +185,14 @@ static irqreturn_t bcm87xx_handle_interr
+ 	return IRQ_HANDLED;
+ }
+ 
+-static int bcm8706_match_phy_device(struct phy_device *phydev)
++static int bcm8706_match_phy_device(struct phy_device *phydev,
++				    const struct phy_driver *phydrv)
+ {
+ 	return phydev->c45_ids.device_ids[4] == PHY_ID_BCM8706;
+ }
+ 
+-static int bcm8727_match_phy_device(struct phy_device *phydev)
++static int bcm8727_match_phy_device(struct phy_device *phydev,
++				    const struct phy_driver *phydrv)
+ {
+ 	return phydev->c45_ids.device_ids[4] == PHY_ID_BCM8727;
+ }
+--- a/drivers/net/phy/icplus.c
++++ b/drivers/net/phy/icplus.c
+@@ -520,12 +520,14 @@ static int ip101a_g_match_phy_device(str
+ 	return ip101a == !ret;
+ }
+ 
+-static int ip101a_match_phy_device(struct phy_device *phydev)
++static int ip101a_match_phy_device(struct phy_device *phydev,
++				   const struct phy_driver *phydrv)
+ {
+ 	return ip101a_g_match_phy_device(phydev, true);
+ }
+ 
+-static int ip101g_match_phy_device(struct phy_device *phydev)
++static int ip101g_match_phy_device(struct phy_device *phydev,
++				   const struct phy_driver *phydrv)
+ {
+ 	return ip101a_g_match_phy_device(phydev, false);
+ }
+--- a/drivers/net/phy/marvell10g.c
++++ b/drivers/net/phy/marvell10g.c
+@@ -1221,7 +1221,8 @@ static int mv3310_get_number_of_ports(st
+ 	return ret + 1;
+ }
+ 
+-static int mv3310_match_phy_device(struct phy_device *phydev)
++static int mv3310_match_phy_device(struct phy_device *phydev,
++				   const struct phy_driver *phydrv)
+ {
+ 	if ((phydev->c45_ids.device_ids[MDIO_MMD_PMAPMD] &
+ 	     MARVELL_PHY_ID_MASK) != MARVELL_PHY_ID_88X3310)
+@@ -1230,7 +1231,8 @@ static int mv3310_match_phy_device(struc
+ 	return mv3310_get_number_of_ports(phydev) == 1;
+ }
+ 
+-static int mv3340_match_phy_device(struct phy_device *phydev)
++static int mv3340_match_phy_device(struct phy_device *phydev,
++				   const struct phy_driver *phydrv)
+ {
+ 	if ((phydev->c45_ids.device_ids[MDIO_MMD_PMAPMD] &
+ 	     MARVELL_PHY_ID_MASK) != MARVELL_PHY_ID_88X3310)
+@@ -1254,12 +1256,14 @@ static int mv211x_match_phy_device(struc
+ 	return !!(val & MDIO_PCS_SPEED_5G) == has_5g;
+ }
+ 
+-static int mv2110_match_phy_device(struct phy_device *phydev)
++static int mv2110_match_phy_device(struct phy_device *phydev,
++				   const struct phy_driver *phydrv)
+ {
+ 	return mv211x_match_phy_device(phydev, true);
+ }
+ 
+-static int mv2111_match_phy_device(struct phy_device *phydev)
++static int mv2111_match_phy_device(struct phy_device *phydev,
++				   const struct phy_driver *phydrv)
+ {
+ 	return mv211x_match_phy_device(phydev, false);
+ }
+--- a/drivers/net/phy/micrel.c
++++ b/drivers/net/phy/micrel.c
+@@ -670,7 +670,8 @@ static int ksz8051_ksz8795_match_phy_dev
+ 		return !ret;
+ }
+ 
+-static int ksz8051_match_phy_device(struct phy_device *phydev)
++static int ksz8051_match_phy_device(struct phy_device *phydev,
++				    const struct phy_driver *phydrv)
+ {
+ 	return ksz8051_ksz8795_match_phy_device(phydev, true);
+ }
+@@ -790,7 +791,8 @@ static int ksz8061_config_init(struct ph
+ 	return kszphy_config_init(phydev);
+ }
+ 
+-static int ksz8795_match_phy_device(struct phy_device *phydev)
++static int ksz8795_match_phy_device(struct phy_device *phydev,
++				    const struct phy_driver *phydrv)
+ {
+ 	return ksz8051_ksz8795_match_phy_device(phydev, false);
+ }
+--- a/drivers/net/phy/nxp-tja11xx.c
++++ b/drivers/net/phy/nxp-tja11xx.c
+@@ -648,12 +648,14 @@ static int tja1102_match_phy_device(stru
+ 	return !ret;
+ }
+ 
+-static int tja1102_p0_match_phy_device(struct phy_device *phydev)
++static int tja1102_p0_match_phy_device(struct phy_device *phydev,
++				       const struct phy_driver *phydrv)
+ {
+ 	return tja1102_match_phy_device(phydev, true);
+ }
+ 
+-static int tja1102_p1_match_phy_device(struct phy_device *phydev)
++static int tja1102_p1_match_phy_device(struct phy_device *phydev,
++				       const struct phy_driver *phydrv)
+ {
+ 	return tja1102_match_phy_device(phydev, false);
+ }
+--- a/drivers/net/phy/phy_device.c
++++ b/drivers/net/phy/phy_device.c
+@@ -533,7 +533,7 @@ static int phy_bus_match(struct device *
+ 		return 0;
+ 
+ 	if (phydrv->match_phy_device)
+-		return phydrv->match_phy_device(phydev);
++		return phydrv->match_phy_device(phydev, phydrv);
+ 
+ 	if (phydev->is_c45) {
+ 		for (i = 1; i < num_ids; i++) {
+--- a/drivers/net/phy/realtek/realtek_main.c
++++ b/drivers/net/phy/realtek/realtek_main.c
+@@ -1315,13 +1315,15 @@ static bool rtlgen_supports_mmd(struct p
+ 	return val > 0;
+ }
+ 
+-static int rtlgen_match_phy_device(struct phy_device *phydev)
++static int rtlgen_match_phy_device(struct phy_device *phydev,
++				   const struct phy_driver *phydrv)
+ {
+ 	return phydev->phy_id == RTL_GENERIC_PHYID &&
+ 	       !rtlgen_supports_2_5gbps(phydev);
+ }
+ 
+-static int rtl8226_match_phy_device(struct phy_device *phydev)
++static int rtl8226_match_phy_device(struct phy_device *phydev,
++				    const struct phy_driver *phydrv)
+ {
+ 	return phydev->phy_id == RTL_GENERIC_PHYID &&
+ 	       rtlgen_supports_2_5gbps(phydev) &&
+@@ -1337,32 +1339,38 @@ static int rtlgen_is_c45_match(struct ph
+ 		return !is_c45 && (id == phydev->phy_id);
+ }
+ 
+-static int rtl8221b_match_phy_device(struct phy_device *phydev)
++static int rtl8221b_match_phy_device(struct phy_device *phydev,
++				     const struct phy_driver *phydrv)
+ {
+ 	return phydev->phy_id == RTL_8221B && rtlgen_supports_mmd(phydev);
+ }
+ 
+-static int rtl8221b_vb_cg_c22_match_phy_device(struct phy_device *phydev)
++static int rtl8221b_vb_cg_c22_match_phy_device(struct phy_device *phydev,
++					       const struct phy_driver *phydrv)
+ {
+ 	return rtlgen_is_c45_match(phydev, RTL_8221B_VB_CG, false);
+ }
+ 
+-static int rtl8221b_vb_cg_c45_match_phy_device(struct phy_device *phydev)
++static int rtl8221b_vb_cg_c45_match_phy_device(struct phy_device *phydev,
++					       const struct phy_driver *phydrv)
+ {
+ 	return rtlgen_is_c45_match(phydev, RTL_8221B_VB_CG, true);
+ }
+ 
+-static int rtl8221b_vn_cg_c22_match_phy_device(struct phy_device *phydev)
++static int rtl8221b_vn_cg_c22_match_phy_device(struct phy_device *phydev,
++					       const struct phy_driver *phydrv)
+ {
+ 	return rtlgen_is_c45_match(phydev, RTL_8221B_VN_CG, false);
+ }
+ 
+-static int rtl8221b_vn_cg_c45_match_phy_device(struct phy_device *phydev)
++static int rtl8221b_vn_cg_c45_match_phy_device(struct phy_device *phydev,
++					       const struct phy_driver *phydrv)
+ {
+ 	return rtlgen_is_c45_match(phydev, RTL_8221B_VN_CG, true);
+ }
+ 
+-static int rtl_internal_nbaset_match_phy_device(struct phy_device *phydev)
++static int rtl_internal_nbaset_match_phy_device(struct phy_device *phydev,
++						const struct phy_driver *phydrv)
+ {
+ 	if (phydev->is_c45)
+ 		return false;
+@@ -1381,7 +1389,8 @@ static int rtl_internal_nbaset_match_phy
+ 	return rtlgen_supports_2_5gbps(phydev) && !rtlgen_supports_mmd(phydev);
+ }
+ 
+-static int rtl8251b_c45_match_phy_device(struct phy_device *phydev)
++static int rtl8251b_c45_match_phy_device(struct phy_device *phydev,
++					 const struct phy_driver *phydrv)
+ {
+ 	return rtlgen_is_c45_match(phydev, RTL_8251B, true);
+ }
+--- a/drivers/net/phy/teranetics.c
++++ b/drivers/net/phy/teranetics.c
+@@ -67,7 +67,8 @@ static int teranetics_read_status(struct
+ 	return 0;
+ }
+ 
+-static int teranetics_match_phy_device(struct phy_device *phydev)
++static int teranetics_match_phy_device(struct phy_device *phydev,
++				       const struct phy_driver *phydrv)
+ {
+ 	return phydev->c45_ids.device_ids[3] == PHY_ID_TN2020;
+ }
+--- a/include/linux/phy.h
++++ b/include/linux/phy.h
+@@ -972,7 +972,8 @@ struct phy_driver {
+ 	 * driver for the given phydev.	 If NULL, matching is based on
+ 	 * phy_id and phy_id_mask.
+ 	 */
+-	int (*match_phy_device)(struct phy_device *phydev);
++	int (*match_phy_device)(struct phy_device *phydev,
++				const struct phy_driver *phydrv);
+ 
+ 	/**
+ 	 * @set_wol: Some devices (e.g. qnap TS-119P II) require PHY
diff --git a/target/linux/generic/backport-6.6/782-04-v6.16-net-phy-introduce-genphy_match_phy_device.patch b/target/linux/generic/backport-6.6/782-04-v6.16-net-phy-introduce-genphy_match_phy_device.patch
new file mode 100644
index 0000000000..1c0b5d836d
--- /dev/null
+++ b/target/linux/generic/backport-6.6/782-04-v6.16-net-phy-introduce-genphy_match_phy_device.patch
@@ -0,0 +1,109 @@
+From d6c45707ac84c2d9f274ece1cea4dddb97996bde Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth at gmail.com>
+Date: Sat, 17 May 2025 22:13:48 +0200
+Subject: [PATCH 4/5] net: phy: introduce genphy_match_phy_device()
+
+Introduce new API, genphy_match_phy_device(), to provide a way to check
+to match a PHY driver for a PHY device based on the info stored in the
+PHY device struct.
+
+The function generalize the logic used in phy_bus_match() to check the
+PHY ID whether if C45 or C22 ID should be used for matching.
+
+This is useful for custom .match_phy_device function that wants to use
+the generic logic under some condition. (example a PHY is already setup
+and provide the correct PHY ID)
+
+Reviewed-by: Russell King (Oracle) <rmk+kernel at armlinux.org.uk>
+Signed-off-by: Christian Marangi <ansuelsmth at gmail.com>
+Link: https://patch.msgid.link/20250517201353.5137-5-ansuelsmth@gmail.com
+Signed-off-by: Jakub Kicinski <kuba at kernel.org>
+---
+ drivers/net/phy/phy_device.c | 52 +++++++++++++++++++++++++-----------
+ include/linux/phy.h          |  3 +++
+ 2 files changed, 40 insertions(+), 15 deletions(-)
+
+--- a/drivers/net/phy/phy_device.c
++++ b/drivers/net/phy/phy_device.c
+@@ -522,20 +522,26 @@ static int phy_scan_fixups(struct phy_de
+ 	return 0;
+ }
+ 
+-static int phy_bus_match(struct device *dev, struct device_driver *drv)
++/**
++ * genphy_match_phy_device - match a PHY device with a PHY driver
++ * @phydev: target phy_device struct
++ * @phydrv: target phy_driver struct
++ *
++ * Description: Checks whether the given PHY device matches the specified
++ * PHY driver. For Clause 45 PHYs, iterates over the available device
++ * identifiers and compares them against the driver's expected PHY ID,
++ * applying the provided mask. For Clause 22 PHYs, a direct ID comparison
++ * is performed.
++ *
++ * Return: 1 if the PHY device matches the driver, 0 otherwise.
++ */
++int genphy_match_phy_device(struct phy_device *phydev,
++			    struct phy_driver *phydrv)
+ {
+-	struct phy_device *phydev = to_phy_device(dev);
+-	struct phy_driver *phydrv = to_phy_driver(drv);
+-	const int num_ids = ARRAY_SIZE(phydev->c45_ids.device_ids);
+-	int i;
+-
+-	if (!(phydrv->mdiodrv.flags & MDIO_DEVICE_IS_PHY))
+-		return 0;
+-
+-	if (phydrv->match_phy_device)
+-		return phydrv->match_phy_device(phydev, phydrv);
+-
+ 	if (phydev->is_c45) {
++		const int num_ids = ARRAY_SIZE(phydev->c45_ids.device_ids);
++		int i;
++
+ 		for (i = 1; i < num_ids; i++) {
+ 			if (phydev->c45_ids.device_ids[i] == 0xffffffff)
+ 				continue;
+@@ -544,11 +550,27 @@ static int phy_bus_match(struct device *
+ 					   phydrv->phy_id, phydrv->phy_id_mask))
+ 				return 1;
+ 		}
++
+ 		return 0;
+-	} else {
+-		return phy_id_compare(phydev->phy_id, phydrv->phy_id,
+-				      phydrv->phy_id_mask);
+ 	}
++
++	return phy_id_compare(phydev->phy_id, phydrv->phy_id,
++			      phydrv->phy_id_mask);
++}
++EXPORT_SYMBOL_GPL(genphy_match_phy_device);
++
++static int phy_bus_match(struct device *dev, struct device_driver *drv)
++{
++	struct phy_device *phydev = to_phy_device(dev);
++	struct phy_driver *phydrv = to_phy_driver(drv);
++
++	if (!(phydrv->mdiodrv.flags & MDIO_DEVICE_IS_PHY))
++		return 0;
++
++	if (phydrv->match_phy_device)
++		return phydrv->match_phy_device(phydev, phydrv);
++
++	return genphy_match_phy_device(phydev, phydrv);
+ }
+ 
+ static ssize_t
+--- a/include/linux/phy.h
++++ b/include/linux/phy.h
+@@ -1812,6 +1812,9 @@ char *phy_attached_info_irq(struct phy_d
+ 	__malloc;
+ void phy_attached_info(struct phy_device *phydev);
+ 
++int genphy_match_phy_device(struct phy_device *phydev,
++			    struct phy_driver *phydrv);
++
+ /* Clause 22 PHY */
+ int genphy_read_abilities(struct phy_device *phydev);
+ int genphy_setup_forced(struct phy_device *phydev);
diff --git a/target/linux/generic/backport-6.6/782-05-v6.16-net-phy-Add-support-for-Aeonsemi-AS21xxx-PHYs.patch b/target/linux/generic/backport-6.6/782-05-v6.16-net-phy-Add-support-for-Aeonsemi-AS21xxx-PHYs.patch
new file mode 100644
index 0000000000..cf33b7bfdf
--- /dev/null
+++ b/target/linux/generic/backport-6.6/782-05-v6.16-net-phy-Add-support-for-Aeonsemi-AS21xxx-PHYs.patch
@@ -0,0 +1,1165 @@
+From 830877d89edcd834e4b4d0fcc021ff619d89505e Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth at gmail.com>
+Date: Sat, 17 May 2025 22:13:49 +0200
+Subject: [PATCH 5/5] net: phy: Add support for Aeonsemi AS21xxx PHYs
+
+Add support for Aeonsemi AS21xxx 10G C45 PHYs. These PHYs integrate
+an IPC to setup some configuration and require special handling to
+sync with the parity bit. The parity bit is a way the IPC use to
+follow correct order of command sent.
+
+Supported PHYs AS21011JB1, AS21011PB1, AS21010JB1, AS21010PB1,
+AS21511JB1, AS21511PB1, AS21510JB1, AS21510PB1, AS21210JB1,
+AS21210PB1 that all register with the PHY ID 0x7500 0x7510
+before the firmware is loaded.
+
+They all support up to 5 LEDs with various HW mode supported.
+
+While implementing it was found some strange coincidence with using the
+same logic for implementing C22 in MMD regs in Broadcom PHYs.
+
+For reference here the AS21xxx PHY name logic:
+
+AS21x1xxB1
+    ^ ^^
+    | |J: Supports SyncE/PTP
+    | |P: No SyncE/PTP support
+    | 1: Supports 2nd Serdes
+    | 2: Not 2nd Serdes support
+    0: 10G, 5G, 2.5G
+    5: 5G, 2.5G
+    2: 2.5G
+
+Reviewed-by: Andrew Lunn <andrew at lunn.ch>
+Signed-off-by: Christian Marangi <ansuelsmth at gmail.com>
+Link: https://patch.msgid.link/20250517201353.5137-6-ansuelsmth@gmail.com
+Signed-off-by: Jakub Kicinski <kuba at kernel.org>
+---
+ MAINTAINERS               |    6 +
+ drivers/net/phy/Kconfig   |   12 +
+ drivers/net/phy/Makefile  |    1 +
+ drivers/net/phy/as21xxx.c | 1087 +++++++++++++++++++++++++++++++++++++
+ 4 files changed, 1106 insertions(+)
+ create mode 100644 drivers/net/phy/as21xxx.c
+
+--- a/drivers/net/phy/Kconfig
++++ b/drivers/net/phy/Kconfig
+@@ -68,6 +68,18 @@ config SFP
+ 
+ comment "MII PHY device drivers"
+ 
++config AS21XXX_PHY
++	tristate "Aeonsemi AS21xxx PHYs"
++	help
++	  Currently supports the Aeonsemi AS21xxx PHY.
++
++	  These are C45 PHYs 10G that require all a generic firmware.
++
++	  Supported PHYs AS21011JB1, AS21011PB1, AS21010JB1, AS21010PB1,
++	  AS21511JB1, AS21511PB1, AS21510JB1, AS21510PB1, AS21210JB1,
++	  AS21210PB1 that all register with the PHY ID 0x7500 0x7500
++	  before the firmware is loaded.
++
+ config AMD_PHY
+ 	tristate "AMD PHYs"
+ 	help
+--- a/drivers/net/phy/Makefile
++++ b/drivers/net/phy/Makefile
+@@ -36,6 +36,7 @@ obj-$(CONFIG_ADIN_PHY)		+= adin.o
+ obj-$(CONFIG_ADIN1100_PHY)	+= adin1100.o
+ obj-$(CONFIG_AMD_PHY)		+= amd.o
+ obj-$(CONFIG_AQUANTIA_PHY)	+= aquantia/
++obj-$(CONFIG_AS21XXX_PHY)	+= as21xxx.o
+ obj-$(CONFIG_AX88796B_PHY)	+= ax88796b.o
+ obj-$(CONFIG_BCM54140_PHY)	+= bcm54140.o
+ obj-$(CONFIG_BCM63XX_PHY)	+= bcm63xx.o
+--- /dev/null
++++ b/drivers/net/phy/as21xxx.c
+@@ -0,0 +1,1087 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Aeonsemi AS21XXxX PHY Driver
++ *
++ * Author: Christian Marangi <ansuelsmth at gmail.com>
++ */
++
++#include <linux/bitfield.h>
++#include <linux/firmware.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/phy.h>
++
++#define VEND1_GLB_REG_CPU_RESET_ADDR_LO_BASEADDR 0x3
++#define VEND1_GLB_REG_CPU_RESET_ADDR_HI_BASEADDR 0x4
++
++#define VEND1_GLB_REG_CPU_CTRL		0xe
++#define   VEND1_GLB_CPU_CTRL_MASK	GENMASK(4, 0)
++#define   VEND1_GLB_CPU_CTRL_LED_POLARITY_MASK GENMASK(12, 8)
++#define   VEND1_GLB_CPU_CTRL_LED_POLARITY(_n) FIELD_PREP(VEND1_GLB_CPU_CTRL_LED_POLARITY_MASK, \
++							 BIT(_n))
++
++#define VEND1_FW_START_ADDR		0x100
++
++#define VEND1_GLB_REG_MDIO_INDIRECT_ADDRCMD 0x101
++#define VEND1_GLB_REG_MDIO_INDIRECT_LOAD 0x102
++
++#define VEND1_GLB_REG_MDIO_INDIRECT_STATUS 0x103
++
++#define VEND1_PTP_CLK			0x142
++#define   VEND1_PTP_CLK_EN		BIT(6)
++
++/* 5 LED at step of 0x20
++ * FE: Fast-Ethernet (10/100)
++ * GE: Gigabit-Ethernet (1000)
++ * NG: New-Generation (2500/5000/10000)
++ */
++#define VEND1_LED_REG(_n)		(0x1800 + ((_n) * 0x10))
++#define   VEND1_LED_REG_A_EVENT		GENMASK(15, 11)
++#define VEND1_LED_CONF			0x1881
++#define   VEND1_LED_CONFG_BLINK		GENMASK(7, 0)
++
++#define VEND1_SPEED_STATUS		0x4002
++#define   VEND1_SPEED_MASK		GENMASK(7, 0)
++#define   VEND1_SPEED_10000		FIELD_PREP_CONST(VEND1_SPEED_MASK, 0x3)
++#define   VEND1_SPEED_5000		FIELD_PREP_CONST(VEND1_SPEED_MASK, 0x5)
++#define   VEND1_SPEED_2500		FIELD_PREP_CONST(VEND1_SPEED_MASK, 0x9)
++#define   VEND1_SPEED_1000		FIELD_PREP_CONST(VEND1_SPEED_MASK, 0x10)
++#define   VEND1_SPEED_100		FIELD_PREP_CONST(VEND1_SPEED_MASK, 0x20)
++#define   VEND1_SPEED_10		FIELD_PREP_CONST(VEND1_SPEED_MASK, 0x0)
++
++#define VEND1_IPC_CMD			0x5801
++#define   AEON_IPC_CMD_PARITY		BIT(15)
++#define   AEON_IPC_CMD_SIZE		GENMASK(10, 6)
++#define   AEON_IPC_CMD_OPCODE		GENMASK(5, 0)
++
++#define IPC_CMD_NOOP			0x0  /* Do nothing */
++#define IPC_CMD_INFO			0x1  /* Get Firmware Version */
++#define IPC_CMD_SYS_CPU			0x2  /* SYS_CPU */
++#define IPC_CMD_BULK_DATA		0xa  /* Pass bulk data in ipc registers. */
++#define IPC_CMD_BULK_WRITE		0xc  /* Write bulk data to memory */
++#define IPC_CMD_CFG_PARAM		0x1a /* Write config parameters to memory */
++#define IPC_CMD_NG_TESTMODE		0x1b /* Set NG test mode and tone */
++#define IPC_CMD_TEMP_MON		0x15 /* Temperature monitoring function */
++#define IPC_CMD_SET_LED			0x23 /* Set led */
++
++#define VEND1_IPC_STS			0x5802
++#define   AEON_IPC_STS_PARITY		BIT(15)
++#define   AEON_IPC_STS_SIZE		GENMASK(14, 10)
++#define   AEON_IPC_STS_OPCODE		GENMASK(9, 4)
++#define   AEON_IPC_STS_STATUS		GENMASK(3, 0)
++#define   AEON_IPC_STS_STATUS_RCVD	FIELD_PREP_CONST(AEON_IPC_STS_STATUS, 0x1)
++#define   AEON_IPC_STS_STATUS_PROCESS	FIELD_PREP_CONST(AEON_IPC_STS_STATUS, 0x2)
++#define   AEON_IPC_STS_STATUS_SUCCESS	FIELD_PREP_CONST(AEON_IPC_STS_STATUS, 0x4)
++#define   AEON_IPC_STS_STATUS_ERROR	FIELD_PREP_CONST(AEON_IPC_STS_STATUS, 0x8)
++#define   AEON_IPC_STS_STATUS_BUSY	FIELD_PREP_CONST(AEON_IPC_STS_STATUS, 0xe)
++#define   AEON_IPC_STS_STATUS_READY	FIELD_PREP_CONST(AEON_IPC_STS_STATUS, 0xf)
++
++#define VEND1_IPC_DATA0			0x5808
++#define VEND1_IPC_DATA1			0x5809
++#define VEND1_IPC_DATA2			0x580a
++#define VEND1_IPC_DATA3			0x580b
++#define VEND1_IPC_DATA4			0x580c
++#define VEND1_IPC_DATA5			0x580d
++#define VEND1_IPC_DATA6			0x580e
++#define VEND1_IPC_DATA7			0x580f
++#define VEND1_IPC_DATA(_n)		(VEND1_IPC_DATA0 + (_n))
++
++/* Sub command of CMD_INFO */
++#define IPC_INFO_VERSION		0x1
++
++/* Sub command of CMD_SYS_CPU */
++#define IPC_SYS_CPU_REBOOT		0x3
++#define IPC_SYS_CPU_IMAGE_OFST		0x4
++#define IPC_SYS_CPU_IMAGE_CHECK		0x5
++#define IPC_SYS_CPU_PHY_ENABLE		0x6
++
++/* Sub command of CMD_CFG_PARAM */
++#define IPC_CFG_PARAM_DIRECT		0x4
++
++/* CFG DIRECT sub command */
++#define IPC_CFG_PARAM_DIRECT_NG_PHYCTRL	0x1
++#define IPC_CFG_PARAM_DIRECT_CU_AN	0x2
++#define IPC_CFG_PARAM_DIRECT_SDS_PCS	0x3
++#define IPC_CFG_PARAM_DIRECT_AUTO_EEE	0x4
++#define IPC_CFG_PARAM_DIRECT_SDS_PMA	0x5
++#define IPC_CFG_PARAM_DIRECT_DPC_RA	0x6
++#define IPC_CFG_PARAM_DIRECT_DPC_PKT_CHK 0x7
++#define IPC_CFG_PARAM_DIRECT_DPC_SDS_WAIT_ETH 0x8
++#define IPC_CFG_PARAM_DIRECT_WDT	0x9
++#define IPC_CFG_PARAM_DIRECT_SDS_RESTART_AN 0x10
++#define IPC_CFG_PARAM_DIRECT_TEMP_MON	0x11
++#define IPC_CFG_PARAM_DIRECT_WOL	0x12
++
++/* Sub command of CMD_TEMP_MON */
++#define IPC_CMD_TEMP_MON_GET		0x4
++
++#define AS21XXX_MDIO_AN_C22		0xffe0
++
++#define PHY_ID_AS21XXX			0x75009410
++/* AS21xxx ID Legend
++ * AS21x1xxB1
++ *     ^ ^^
++ *     | |J: Supports SyncE/PTP
++ *     | |P: No SyncE/PTP support
++ *     | 1: Supports 2nd Serdes
++ *     | 2: Not 2nd Serdes support
++ *     0: 10G, 5G, 2.5G
++ *     5: 5G, 2.5G
++ *     2: 2.5G
++ */
++#define PHY_ID_AS21011JB1		0x75009402
++#define PHY_ID_AS21011PB1		0x75009412
++#define PHY_ID_AS21010JB1		0x75009422
++#define PHY_ID_AS21010PB1		0x75009432
++#define PHY_ID_AS21511JB1		0x75009442
++#define PHY_ID_AS21511PB1		0x75009452
++#define PHY_ID_AS21510JB1		0x75009462
++#define PHY_ID_AS21510PB1		0x75009472
++#define PHY_ID_AS21210JB1		0x75009482
++#define PHY_ID_AS21210PB1		0x75009492
++#define PHY_VENDOR_AEONSEMI		0x75009400
++
++#define AEON_MAX_LEDS			5
++#define AEON_IPC_DELAY			10000
++#define AEON_IPC_TIMEOUT		(AEON_IPC_DELAY * 100)
++#define AEON_IPC_DATA_NUM_REGISTERS	8
++#define AEON_IPC_DATA_MAX		(AEON_IPC_DATA_NUM_REGISTERS * sizeof(u16))
++
++#define AEON_BOOT_ADDR			0x1000
++#define AEON_CPU_BOOT_ADDR		0x2000
++#define AEON_CPU_CTRL_FW_LOAD		(BIT(4) | BIT(2) | BIT(1) | BIT(0))
++#define AEON_CPU_CTRL_FW_START		BIT(0)
++
++enum as21xxx_led_event {
++	VEND1_LED_REG_A_EVENT_ON_10 = 0x0,
++	VEND1_LED_REG_A_EVENT_ON_100,
++	VEND1_LED_REG_A_EVENT_ON_1000,
++	VEND1_LED_REG_A_EVENT_ON_2500,
++	VEND1_LED_REG_A_EVENT_ON_5000,
++	VEND1_LED_REG_A_EVENT_ON_10000,
++	VEND1_LED_REG_A_EVENT_ON_FE_GE,
++	VEND1_LED_REG_A_EVENT_ON_NG,
++	VEND1_LED_REG_A_EVENT_ON_FULL_DUPLEX,
++	VEND1_LED_REG_A_EVENT_ON_COLLISION,
++	VEND1_LED_REG_A_EVENT_BLINK_TX,
++	VEND1_LED_REG_A_EVENT_BLINK_RX,
++	VEND1_LED_REG_A_EVENT_BLINK_ACT,
++	VEND1_LED_REG_A_EVENT_ON_LINK,
++	VEND1_LED_REG_A_EVENT_ON_LINK_BLINK_ACT,
++	VEND1_LED_REG_A_EVENT_ON_LINK_BLINK_RX,
++	VEND1_LED_REG_A_EVENT_ON_FE_GE_BLINK_ACT,
++	VEND1_LED_REG_A_EVENT_ON_NG_BLINK_ACT,
++	VEND1_LED_REG_A_EVENT_ON_NG_BLINK_FE_GE,
++	VEND1_LED_REG_A_EVENT_ON_FD_BLINK_COLLISION,
++	VEND1_LED_REG_A_EVENT_ON,
++	VEND1_LED_REG_A_EVENT_OFF,
++};
++
++struct as21xxx_led_pattern_info {
++	unsigned int pattern;
++	u16 val;
++};
++
++struct as21xxx_priv {
++	bool parity_status;
++	/* Protect concurrent IPC access */
++	struct mutex ipc_lock;
++};
++
++static struct as21xxx_led_pattern_info as21xxx_led_supported_pattern[] = {
++	{
++		.pattern = BIT(TRIGGER_NETDEV_LINK_10),
++		.val = VEND1_LED_REG_A_EVENT_ON_10
++	},
++	{
++		.pattern = BIT(TRIGGER_NETDEV_LINK_100),
++		.val = VEND1_LED_REG_A_EVENT_ON_100
++	},
++	{
++		.pattern = BIT(TRIGGER_NETDEV_LINK_1000),
++		.val = VEND1_LED_REG_A_EVENT_ON_1000
++	},
++	{
++		.pattern = BIT(TRIGGER_NETDEV_LINK_2500),
++		.val = VEND1_LED_REG_A_EVENT_ON_2500
++	},
++	{
++		.pattern = BIT(TRIGGER_NETDEV_LINK_5000),
++		.val = VEND1_LED_REG_A_EVENT_ON_5000
++	},
++	{
++		.pattern = BIT(TRIGGER_NETDEV_LINK_10000),
++		.val = VEND1_LED_REG_A_EVENT_ON_10000
++	},
++	{
++		.pattern = BIT(TRIGGER_NETDEV_LINK),
++		.val = VEND1_LED_REG_A_EVENT_ON_LINK
++	},
++	{
++		.pattern = BIT(TRIGGER_NETDEV_LINK_10) |
++			   BIT(TRIGGER_NETDEV_LINK_100) |
++			   BIT(TRIGGER_NETDEV_LINK_1000),
++		.val = VEND1_LED_REG_A_EVENT_ON_FE_GE
++	},
++	{
++		.pattern = BIT(TRIGGER_NETDEV_LINK_2500) |
++			   BIT(TRIGGER_NETDEV_LINK_5000) |
++			   BIT(TRIGGER_NETDEV_LINK_10000),
++		.val = VEND1_LED_REG_A_EVENT_ON_NG
++	},
++	{
++		.pattern = BIT(TRIGGER_NETDEV_FULL_DUPLEX),
++		.val = VEND1_LED_REG_A_EVENT_ON_FULL_DUPLEX
++	},
++	{
++		.pattern = BIT(TRIGGER_NETDEV_TX),
++		.val = VEND1_LED_REG_A_EVENT_BLINK_TX
++	},
++	{
++		.pattern = BIT(TRIGGER_NETDEV_RX),
++		.val = VEND1_LED_REG_A_EVENT_BLINK_RX
++	},
++	{
++		.pattern = BIT(TRIGGER_NETDEV_TX) |
++			   BIT(TRIGGER_NETDEV_RX),
++		.val = VEND1_LED_REG_A_EVENT_BLINK_ACT
++	},
++	{
++		.pattern = BIT(TRIGGER_NETDEV_LINK_10) |
++			   BIT(TRIGGER_NETDEV_LINK_100) |
++			   BIT(TRIGGER_NETDEV_LINK_1000) |
++			   BIT(TRIGGER_NETDEV_LINK_2500) |
++			   BIT(TRIGGER_NETDEV_LINK_5000) |
++			   BIT(TRIGGER_NETDEV_LINK_10000),
++		.val = VEND1_LED_REG_A_EVENT_ON_LINK
++	},
++	{
++		.pattern = BIT(TRIGGER_NETDEV_LINK_10) |
++			   BIT(TRIGGER_NETDEV_LINK_100) |
++			   BIT(TRIGGER_NETDEV_LINK_1000) |
++			   BIT(TRIGGER_NETDEV_LINK_2500) |
++			   BIT(TRIGGER_NETDEV_LINK_5000) |
++			   BIT(TRIGGER_NETDEV_LINK_10000) |
++			   BIT(TRIGGER_NETDEV_TX) |
++			   BIT(TRIGGER_NETDEV_RX),
++		.val = VEND1_LED_REG_A_EVENT_ON_LINK_BLINK_ACT
++	},
++	{
++		.pattern = BIT(TRIGGER_NETDEV_LINK_10) |
++			   BIT(TRIGGER_NETDEV_LINK_100) |
++			   BIT(TRIGGER_NETDEV_LINK_1000) |
++			   BIT(TRIGGER_NETDEV_LINK_2500) |
++			   BIT(TRIGGER_NETDEV_LINK_5000) |
++			   BIT(TRIGGER_NETDEV_LINK_10000) |
++			   BIT(TRIGGER_NETDEV_RX),
++		.val = VEND1_LED_REG_A_EVENT_ON_LINK_BLINK_RX
++	},
++	{
++		.pattern = BIT(TRIGGER_NETDEV_LINK_10) |
++			   BIT(TRIGGER_NETDEV_LINK_100) |
++			   BIT(TRIGGER_NETDEV_LINK_1000) |
++			   BIT(TRIGGER_NETDEV_TX) |
++			   BIT(TRIGGER_NETDEV_RX),
++		.val = VEND1_LED_REG_A_EVENT_ON_FE_GE_BLINK_ACT
++	},
++	{
++		.pattern = BIT(TRIGGER_NETDEV_LINK_2500) |
++			   BIT(TRIGGER_NETDEV_LINK_5000) |
++			   BIT(TRIGGER_NETDEV_LINK_10000) |
++			   BIT(TRIGGER_NETDEV_TX) |
++			   BIT(TRIGGER_NETDEV_RX),
++		.val = VEND1_LED_REG_A_EVENT_ON_NG_BLINK_ACT
++	}
++};
++
++static int aeon_firmware_boot(struct phy_device *phydev, const u8 *data,
++			      size_t size)
++{
++	int i, ret;
++	u16 val;
++
++	ret = phy_modify_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLB_REG_CPU_CTRL,
++			     VEND1_GLB_CPU_CTRL_MASK, AEON_CPU_CTRL_FW_LOAD);
++	if (ret)
++		return ret;
++
++	ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_FW_START_ADDR,
++			    AEON_BOOT_ADDR);
++	if (ret)
++		return ret;
++
++	ret = phy_modify_mmd(phydev, MDIO_MMD_VEND1,
++			     VEND1_GLB_REG_MDIO_INDIRECT_ADDRCMD,
++			     0x3ffc, 0xc000);
++	if (ret)
++		return ret;
++
++	val = phy_read_mmd(phydev, MDIO_MMD_VEND1,
++			   VEND1_GLB_REG_MDIO_INDIRECT_STATUS);
++	if (val > 1) {
++		phydev_err(phydev, "wrong origin mdio_indirect_status: %x\n", val);
++		return -EINVAL;
++	}
++
++	/* Firmware is always aligned to u16 */
++	for (i = 0; i < size; i += 2) {
++		val = data[i + 1] << 8 | data[i];
++
++		ret = phy_write_mmd(phydev, MDIO_MMD_VEND1,
++				    VEND1_GLB_REG_MDIO_INDIRECT_LOAD, val);
++		if (ret)
++			return ret;
++	}
++
++	ret = phy_write_mmd(phydev, MDIO_MMD_VEND1,
++			    VEND1_GLB_REG_CPU_RESET_ADDR_LO_BASEADDR,
++			    lower_16_bits(AEON_CPU_BOOT_ADDR));
++	if (ret)
++		return ret;
++
++	ret = phy_write_mmd(phydev, MDIO_MMD_VEND1,
++			    VEND1_GLB_REG_CPU_RESET_ADDR_HI_BASEADDR,
++			    upper_16_bits(AEON_CPU_BOOT_ADDR));
++	if (ret)
++		return ret;
++
++	return phy_modify_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLB_REG_CPU_CTRL,
++			      VEND1_GLB_CPU_CTRL_MASK, AEON_CPU_CTRL_FW_START);
++}
++
++static int aeon_firmware_load(struct phy_device *phydev)
++{
++	struct device *dev = &phydev->mdio.dev;
++	const struct firmware *fw;
++	const char *fw_name;
++	int ret;
++
++	ret = of_property_read_string(dev->of_node, "firmware-name",
++				      &fw_name);
++	if (ret)
++		return ret;
++
++	ret = request_firmware(&fw, fw_name, dev);
++	if (ret) {
++		phydev_err(phydev, "failed to find FW file %s (%d)\n",
++			   fw_name, ret);
++		return ret;
++	}
++
++	ret = aeon_firmware_boot(phydev, fw->data, fw->size);
++
++	release_firmware(fw);
++
++	return ret;
++}
++
++static bool aeon_ipc_ready(u16 val, bool parity_status)
++{
++	u16 status;
++
++	if (FIELD_GET(AEON_IPC_STS_PARITY, val) != parity_status)
++		return false;
++
++	status = val & AEON_IPC_STS_STATUS;
++
++	return status != AEON_IPC_STS_STATUS_RCVD &&
++	       status != AEON_IPC_STS_STATUS_PROCESS &&
++	       status != AEON_IPC_STS_STATUS_BUSY;
++}
++
++static int aeon_ipc_wait_cmd(struct phy_device *phydev, bool parity_status)
++{
++	u16 val;
++
++	/* Exit condition logic:
++	 * - Wait for parity bit equal
++	 * - Wait for status success, error OR ready
++	 */
++	return phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1, VEND1_IPC_STS, val,
++					 aeon_ipc_ready(val, parity_status),
++					 AEON_IPC_DELAY, AEON_IPC_TIMEOUT, false);
++}
++
++static int aeon_ipc_send_cmd(struct phy_device *phydev,
++			     struct as21xxx_priv *priv,
++			     u16 cmd, u16 *ret_sts)
++{
++	bool curr_parity;
++	int ret;
++
++	/* The IPC sync by using a single parity bit.
++	 * Each CMD have alternately this bit set or clear
++	 * to understand correct flow and packet order.
++	 */
++	curr_parity = priv->parity_status;
++	if (priv->parity_status)
++		cmd |= AEON_IPC_CMD_PARITY;
++
++	/* Always update parity for next packet */
++	priv->parity_status = !priv->parity_status;
++
++	ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_IPC_CMD, cmd);
++	if (ret)
++		return ret;
++
++	/* Wait for packet to be processed */
++	usleep_range(AEON_IPC_DELAY, AEON_IPC_DELAY + 5000);
++
++	/* With no ret_sts, ignore waiting for packet completion
++	 * (ipc parity bit sync)
++	 */
++	if (!ret_sts)
++		return 0;
++
++	ret = aeon_ipc_wait_cmd(phydev, curr_parity);
++	if (ret)
++		return ret;
++
++	ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_IPC_STS);
++	if (ret < 0)
++		return ret;
++
++	*ret_sts = ret;
++	if ((*ret_sts & AEON_IPC_STS_STATUS) != AEON_IPC_STS_STATUS_SUCCESS)
++		return -EINVAL;
++
++	return 0;
++}
++
++/* If data is NULL, return 0 or negative error.
++ * If data not NULL, return number of Bytes received from IPC or
++ * a negative error.
++ */
++static int aeon_ipc_send_msg(struct phy_device *phydev,
++			     u16 opcode, u16 *data, unsigned int data_len,
++			     u16 *ret_data)
++{
++	struct as21xxx_priv *priv = phydev->priv;
++	unsigned int ret_size;
++	u16 cmd, ret_sts;
++	int ret;
++	int i;
++
++	/* IPC have a max of 8 register to transfer data,
++	 * make sure we never exceed this.
++	 */
++	if (data_len > AEON_IPC_DATA_MAX)
++		return -EINVAL;
++
++	for (i = 0; i < data_len / sizeof(u16); i++)
++		phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_IPC_DATA(i),
++			      data[i]);
++
++	cmd = FIELD_PREP(AEON_IPC_CMD_SIZE, data_len) |
++	      FIELD_PREP(AEON_IPC_CMD_OPCODE, opcode);
++
++	mutex_lock(&priv->ipc_lock);
++
++	ret = aeon_ipc_send_cmd(phydev, priv, cmd, &ret_sts);
++	if (ret) {
++		phydev_err(phydev, "failed to send ipc msg for %x: %d\n",
++			   opcode, ret);
++		goto out;
++	}
++
++	if (!data)
++		goto out;
++
++	if ((ret_sts & AEON_IPC_STS_STATUS) == AEON_IPC_STS_STATUS_ERROR) {
++		ret = -EINVAL;
++		goto out;
++	}
++
++	/* Prevent IPC from stack smashing the kernel.
++	 * We can't trust IPC to return a good value and we always
++	 * preallocate space for 16 Bytes.
++	 */
++	ret_size = FIELD_GET(AEON_IPC_STS_SIZE, ret_sts);
++	if (ret_size > AEON_IPC_DATA_MAX) {
++		ret = -EINVAL;
++		goto out;
++	}
++
++	/* Read data from IPC data register for ret_size value from IPC */
++	for (i = 0; i < DIV_ROUND_UP(ret_size, sizeof(u16)); i++) {
++		ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_IPC_DATA(i));
++		if (ret < 0)
++			goto out;
++
++		ret_data[i] = ret;
++	}
++
++	ret = ret_size;
++
++out:
++	mutex_unlock(&priv->ipc_lock);
++
++	return ret;
++}
++
++static int aeon_ipc_noop(struct phy_device *phydev,
++			 struct as21xxx_priv *priv, u16 *ret_sts)
++{
++	u16 cmd;
++
++	cmd = FIELD_PREP(AEON_IPC_CMD_SIZE, 0) |
++	      FIELD_PREP(AEON_IPC_CMD_OPCODE, IPC_CMD_NOOP);
++
++	return aeon_ipc_send_cmd(phydev, priv, cmd, ret_sts);
++}
++
++/* Logic to sync parity bit with IPC.
++ * We send 2 NOP cmd with same partity and we wait for IPC
++ * to handle the packet only for the second one. This way
++ * we make sure we are sync for every next cmd.
++ */
++static int aeon_ipc_sync_parity(struct phy_device *phydev,
++				struct as21xxx_priv *priv)
++{
++	u16 ret_sts;
++	int ret;
++
++	mutex_lock(&priv->ipc_lock);
++
++	/* Send NOP with no parity */
++	aeon_ipc_noop(phydev, priv, NULL);
++
++	/* Reset packet parity */
++	priv->parity_status = false;
++
++	/* Send second NOP with no parity */
++	ret = aeon_ipc_noop(phydev, priv, &ret_sts);
++
++	mutex_unlock(&priv->ipc_lock);
++
++	/* We expect to return -EINVAL */
++	if (ret != -EINVAL)
++		return ret;
++
++	if ((ret_sts & AEON_IPC_STS_STATUS) != AEON_IPC_STS_STATUS_READY) {
++		phydev_err(phydev, "Invalid IPC status on sync parity: %x\n",
++			   ret_sts);
++		return -EINVAL;
++	}
++
++	return 0;
++}
++
++static int aeon_ipc_get_fw_version(struct phy_device *phydev)
++{
++	u16 ret_data[AEON_IPC_DATA_NUM_REGISTERS], data[1];
++	char fw_version[AEON_IPC_DATA_MAX + 1];
++	int ret;
++
++	data[0] = IPC_INFO_VERSION;
++
++	ret = aeon_ipc_send_msg(phydev, IPC_CMD_INFO, data,
++				sizeof(data), ret_data);
++	if (ret < 0)
++		return ret;
++
++	/* Make sure FW version is NULL terminated */
++	memcpy(fw_version, ret_data, ret);
++	fw_version[ret] = '\0';
++
++	phydev_info(phydev, "Firmware Version: %s\n", fw_version);
++
++	return 0;
++}
++
++static int aeon_dpc_ra_enable(struct phy_device *phydev)
++{
++	u16 data[2];
++
++	data[0] = IPC_CFG_PARAM_DIRECT;
++	data[1] = IPC_CFG_PARAM_DIRECT_DPC_RA;
++
++	return aeon_ipc_send_msg(phydev, IPC_CMD_CFG_PARAM, data,
++				 sizeof(data), NULL);
++}
++
++static int as21xxx_probe(struct phy_device *phydev)
++{
++	struct as21xxx_priv *priv;
++	int ret;
++
++	priv = devm_kzalloc(&phydev->mdio.dev,
++			    sizeof(*priv), GFP_KERNEL);
++	if (!priv)
++		return -ENOMEM;
++	phydev->priv = priv;
++
++	ret = devm_mutex_init(&phydev->mdio.dev,
++			      &priv->ipc_lock);
++	if (ret)
++		return ret;
++
++	ret = aeon_ipc_sync_parity(phydev, priv);
++	if (ret)
++		return ret;
++
++	ret = aeon_ipc_get_fw_version(phydev);
++	if (ret)
++		return ret;
++
++	/* Enable PTP clk if not already Enabled */
++	ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, VEND1_PTP_CLK,
++			       VEND1_PTP_CLK_EN);
++	if (ret)
++		return ret;
++
++	return aeon_dpc_ra_enable(phydev);
++}
++
++static int as21xxx_read_link(struct phy_device *phydev, int *bmcr)
++{
++	int status;
++
++	/* Normal C22 BMCR report inconsistent data, use
++	 * the mapped C22 in C45 to have more consistent link info.
++	 */
++	*bmcr = phy_read_mmd(phydev, MDIO_MMD_AN,
++			     AS21XXX_MDIO_AN_C22 + MII_BMCR);
++	if (*bmcr < 0)
++		return *bmcr;
++
++	/* Autoneg is being started, therefore disregard current
++	 * link status and report link as down.
++	 */
++	if (*bmcr & BMCR_ANRESTART) {
++		phydev->link = 0;
++		return 0;
++	}
++
++	status = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_STAT1);
++	if (status < 0)
++		return status;
++
++	phydev->link = !!(status & MDIO_STAT1_LSTATUS);
++
++	return 0;
++}
++
++static int as21xxx_read_c22_lpa(struct phy_device *phydev)
++{
++	int lpagb;
++
++	/* MII_STAT1000 are only filled in the mapped C22
++	 * in C45, use that to fill lpagb values and check.
++	 */
++	lpagb = phy_read_mmd(phydev, MDIO_MMD_AN,
++			     AS21XXX_MDIO_AN_C22 + MII_STAT1000);
++	if (lpagb < 0)
++		return lpagb;
++
++	if (lpagb & LPA_1000MSFAIL) {
++		int adv = phy_read_mmd(phydev, MDIO_MMD_AN,
++				       AS21XXX_MDIO_AN_C22 + MII_CTRL1000);
++
++		if (adv < 0)
++			return adv;
++
++		if (adv & CTL1000_ENABLE_MASTER)
++			phydev_err(phydev, "Master/Slave resolution failed, maybe conflicting manual settings?\n");
++		else
++			phydev_err(phydev, "Master/Slave resolution failed\n");
++		return -ENOLINK;
++	}
++
++	mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising,
++					lpagb);
++
++	return 0;
++}
++
++static int as21xxx_read_status(struct phy_device *phydev)
++{
++	int bmcr, old_link = phydev->link;
++	int ret;
++
++	ret = as21xxx_read_link(phydev, &bmcr);
++	if (ret)
++		return ret;
++
++	/* 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;
++
++	if (phydev->autoneg == AUTONEG_ENABLE) {
++		ret = genphy_c45_read_lpa(phydev);
++		if (ret)
++			return ret;
++
++		ret = as21xxx_read_c22_lpa(phydev);
++		if (ret)
++			return ret;
++
++		phy_resolve_aneg_linkmode(phydev);
++	} else {
++		int speed;
++
++		linkmode_zero(phydev->lp_advertising);
++
++		speed = phy_read_mmd(phydev, MDIO_MMD_VEND1,
++				     VEND1_SPEED_STATUS);
++		if (speed < 0)
++			return speed;
++
++		switch (speed & VEND1_SPEED_STATUS) {
++		case VEND1_SPEED_10000:
++			phydev->speed = SPEED_10000;
++			phydev->duplex = DUPLEX_FULL;
++			break;
++		case VEND1_SPEED_5000:
++			phydev->speed = SPEED_5000;
++			phydev->duplex = DUPLEX_FULL;
++			break;
++		case VEND1_SPEED_2500:
++			phydev->speed = SPEED_2500;
++			phydev->duplex = DUPLEX_FULL;
++			break;
++		case VEND1_SPEED_1000:
++			phydev->speed = SPEED_1000;
++			if (bmcr & BMCR_FULLDPLX)
++				phydev->duplex = DUPLEX_FULL;
++			else
++				phydev->duplex = DUPLEX_HALF;
++			break;
++		case VEND1_SPEED_100:
++			phydev->speed = SPEED_100;
++			phydev->duplex = DUPLEX_FULL;
++			break;
++		case VEND1_SPEED_10:
++			phydev->speed = SPEED_10;
++			phydev->duplex = DUPLEX_FULL;
++			break;
++		default:
++			return -EINVAL;
++		}
++	}
++
++	return 0;
++}
++
++static int as21xxx_led_brightness_set(struct phy_device *phydev,
++				      u8 index, enum led_brightness value)
++{
++	u16 val = VEND1_LED_REG_A_EVENT_OFF;
++
++	if (index > AEON_MAX_LEDS)
++		return -EINVAL;
++
++	if (value)
++		val = VEND1_LED_REG_A_EVENT_ON;
++
++	return phy_modify_mmd(phydev, MDIO_MMD_VEND1,
++			      VEND1_LED_REG(index),
++			      VEND1_LED_REG_A_EVENT,
++			      FIELD_PREP(VEND1_LED_REG_A_EVENT, val));
++}
++
++static int as21xxx_led_hw_is_supported(struct phy_device *phydev, u8 index,
++				       unsigned long rules)
++{
++	int i;
++
++	if (index > AEON_MAX_LEDS)
++		return -EINVAL;
++
++	for (i = 0; i < ARRAY_SIZE(as21xxx_led_supported_pattern); i++)
++		if (rules == as21xxx_led_supported_pattern[i].pattern)
++			return 0;
++
++	return -EOPNOTSUPP;
++}
++
++static int as21xxx_led_hw_control_get(struct phy_device *phydev, u8 index,
++				      unsigned long *rules)
++{
++	int i, val;
++
++	if (index > AEON_MAX_LEDS)
++		return -EINVAL;
++
++	val = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_LED_REG(index));
++	if (val < 0)
++		return val;
++
++	val = FIELD_GET(VEND1_LED_REG_A_EVENT, val);
++	for (i = 0; i < ARRAY_SIZE(as21xxx_led_supported_pattern); i++)
++		if (val == as21xxx_led_supported_pattern[i].val) {
++			*rules = as21xxx_led_supported_pattern[i].pattern;
++			return 0;
++		}
++
++	/* Should be impossible */
++	return -EINVAL;
++}
++
++static int as21xxx_led_hw_control_set(struct phy_device *phydev, u8 index,
++				      unsigned long rules)
++{
++	u16 val = 0;
++	int i;
++
++	if (index > AEON_MAX_LEDS)
++		return -EINVAL;
++
++	for (i = 0; i < ARRAY_SIZE(as21xxx_led_supported_pattern); i++)
++		if (rules == as21xxx_led_supported_pattern[i].pattern) {
++			val = as21xxx_led_supported_pattern[i].val;
++			break;
++		}
++
++	return phy_modify_mmd(phydev, MDIO_MMD_VEND1,
++			      VEND1_LED_REG(index),
++			      VEND1_LED_REG_A_EVENT,
++			      FIELD_PREP(VEND1_LED_REG_A_EVENT, val));
++}
++
++static int as21xxx_led_polarity_set(struct phy_device *phydev, int index,
++				    unsigned long modes)
++{
++	bool led_active_low = false;
++	u16 mask, val = 0;
++	u32 mode;
++
++	if (index > AEON_MAX_LEDS)
++		return -EINVAL;
++
++	for_each_set_bit(mode, &modes, __PHY_LED_MODES_NUM) {
++		switch (mode) {
++		case PHY_LED_ACTIVE_LOW:
++			led_active_low = true;
++			break;
++		case PHY_LED_ACTIVE_HIGH: /* default mode */
++			led_active_low = false;
++			break;
++		default:
++			return -EINVAL;
++		}
++	}
++
++	mask = VEND1_GLB_CPU_CTRL_LED_POLARITY(index);
++	if (led_active_low)
++		val = VEND1_GLB_CPU_CTRL_LED_POLARITY(index);
++
++	return phy_modify_mmd(phydev, MDIO_MMD_VEND1,
++			      VEND1_GLB_REG_CPU_CTRL,
++			      mask, val);
++}
++
++static int as21xxx_match_phy_device(struct phy_device *phydev,
++				    const struct phy_driver *phydrv)
++{
++	struct as21xxx_priv *priv;
++	u16 ret_sts;
++	u32 phy_id;
++	int ret;
++
++	/* Skip PHY that are not AS21xxx or already have firmware loaded */
++	if (phydev->c45_ids.device_ids[MDIO_MMD_PCS] != PHY_ID_AS21XXX)
++		return genphy_match_phy_device(phydev, (struct phy_driver *)phydrv);
++
++	/* Read PHY ID to handle firmware just loaded */
++	ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MII_PHYSID1);
++	if (ret < 0)
++		return ret;
++	phy_id = ret << 16;
++
++	ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MII_PHYSID2);
++	if (ret < 0)
++		return ret;
++	phy_id |= ret;
++
++	/* With PHY ID not the generic AS21xxx one assume
++	 * the firmware just loaded
++	 */
++	if (phy_id != PHY_ID_AS21XXX)
++		return phy_id == phydrv->phy_id;
++
++	/* Allocate temp priv and load the firmware */
++	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
++	if (!priv)
++		return -ENOMEM;
++
++	mutex_init(&priv->ipc_lock);
++
++	ret = aeon_firmware_load(phydev);
++	if (ret)
++		goto out;
++
++	/* Sync parity... */
++	ret = aeon_ipc_sync_parity(phydev, priv);
++	if (ret)
++		goto out;
++
++	/* ...and send a third NOOP cmd to wait for firmware finish loading */
++	ret = aeon_ipc_noop(phydev, priv, &ret_sts);
++	if (ret)
++		goto out;
++
++out:
++	mutex_destroy(&priv->ipc_lock);
++	kfree(priv);
++
++	/* Return can either be 0 or a negative error code.
++	 * Returning 0 here means THIS is NOT a suitable PHY.
++	 *
++	 * For the specific case of the generic Aeonsemi PHY ID that
++	 * needs the firmware the be loaded first to have a correct PHY ID,
++	 * this is OK as a matching PHY ID will be found right after.
++	 * This relies on the driver probe order where the first PHY driver
++	 * probed is the generic one.
++	 */
++	return ret;
++}
++
++static struct phy_driver as21xxx_drivers[] = {
++	{
++		/* PHY expose in C45 as 0x7500 0x9410
++		 * before firmware is loaded.
++		 * This driver entry must be attempted first to load
++		 * the firmware and thus update the ID registers.
++		 */
++		PHY_ID_MATCH_EXACT(PHY_ID_AS21XXX),
++		.name		= "Aeonsemi AS21xxx",
++		.match_phy_device = as21xxx_match_phy_device,
++	},
++	{
++		PHY_ID_MATCH_EXACT(PHY_ID_AS21011JB1),
++		.name		= "Aeonsemi AS21011JB1",
++		.probe		= as21xxx_probe,
++		.match_phy_device = as21xxx_match_phy_device,
++		.read_status	= as21xxx_read_status,
++		.led_brightness_set = as21xxx_led_brightness_set,
++		.led_hw_is_supported = as21xxx_led_hw_is_supported,
++		.led_hw_control_set = as21xxx_led_hw_control_set,
++		.led_hw_control_get = as21xxx_led_hw_control_get,
++		.led_polarity_set = as21xxx_led_polarity_set,
++	},
++	{
++		PHY_ID_MATCH_EXACT(PHY_ID_AS21011PB1),
++		.name		= "Aeonsemi AS21011PB1",
++		.probe		= as21xxx_probe,
++		.match_phy_device = as21xxx_match_phy_device,
++		.read_status	= as21xxx_read_status,
++		.led_brightness_set = as21xxx_led_brightness_set,
++		.led_hw_is_supported = as21xxx_led_hw_is_supported,
++		.led_hw_control_set = as21xxx_led_hw_control_set,
++		.led_hw_control_get = as21xxx_led_hw_control_get,
++		.led_polarity_set = as21xxx_led_polarity_set,
++	},
++	{
++		PHY_ID_MATCH_EXACT(PHY_ID_AS21010PB1),
++		.name		= "Aeonsemi AS21010PB1",
++		.probe		= as21xxx_probe,
++		.match_phy_device = as21xxx_match_phy_device,
++		.read_status	= as21xxx_read_status,
++		.led_brightness_set = as21xxx_led_brightness_set,
++		.led_hw_is_supported = as21xxx_led_hw_is_supported,
++		.led_hw_control_set = as21xxx_led_hw_control_set,
++		.led_hw_control_get = as21xxx_led_hw_control_get,
++		.led_polarity_set = as21xxx_led_polarity_set,
++	},
++	{
++		PHY_ID_MATCH_EXACT(PHY_ID_AS21010JB1),
++		.name		= "Aeonsemi AS21010JB1",
++		.probe		= as21xxx_probe,
++		.match_phy_device = as21xxx_match_phy_device,
++		.read_status	= as21xxx_read_status,
++		.led_brightness_set = as21xxx_led_brightness_set,
++		.led_hw_is_supported = as21xxx_led_hw_is_supported,
++		.led_hw_control_set = as21xxx_led_hw_control_set,
++		.led_hw_control_get = as21xxx_led_hw_control_get,
++		.led_polarity_set = as21xxx_led_polarity_set,
++	},
++	{
++		PHY_ID_MATCH_EXACT(PHY_ID_AS21210PB1),
++		.name		= "Aeonsemi AS21210PB1",
++		.probe		= as21xxx_probe,
++		.match_phy_device = as21xxx_match_phy_device,
++		.read_status	= as21xxx_read_status,
++		.led_brightness_set = as21xxx_led_brightness_set,
++		.led_hw_is_supported = as21xxx_led_hw_is_supported,
++		.led_hw_control_set = as21xxx_led_hw_control_set,
++		.led_hw_control_get = as21xxx_led_hw_control_get,
++		.led_polarity_set = as21xxx_led_polarity_set,
++	},
++	{
++		PHY_ID_MATCH_EXACT(PHY_ID_AS21510JB1),
++		.name		= "Aeonsemi AS21510JB1",
++		.probe		= as21xxx_probe,
++		.match_phy_device = as21xxx_match_phy_device,
++		.read_status	= as21xxx_read_status,
++		.led_brightness_set = as21xxx_led_brightness_set,
++		.led_hw_is_supported = as21xxx_led_hw_is_supported,
++		.led_hw_control_set = as21xxx_led_hw_control_set,
++		.led_hw_control_get = as21xxx_led_hw_control_get,
++		.led_polarity_set = as21xxx_led_polarity_set,
++	},
++	{
++		PHY_ID_MATCH_EXACT(PHY_ID_AS21510PB1),
++		.name		= "Aeonsemi AS21510PB1",
++		.probe		= as21xxx_probe,
++		.match_phy_device = as21xxx_match_phy_device,
++		.read_status	= as21xxx_read_status,
++		.led_brightness_set = as21xxx_led_brightness_set,
++		.led_hw_is_supported = as21xxx_led_hw_is_supported,
++		.led_hw_control_set = as21xxx_led_hw_control_set,
++		.led_hw_control_get = as21xxx_led_hw_control_get,
++		.led_polarity_set = as21xxx_led_polarity_set,
++	},
++	{
++		PHY_ID_MATCH_EXACT(PHY_ID_AS21511JB1),
++		.name		= "Aeonsemi AS21511JB1",
++		.probe		= as21xxx_probe,
++		.match_phy_device = as21xxx_match_phy_device,
++		.read_status	= as21xxx_read_status,
++		.led_brightness_set = as21xxx_led_brightness_set,
++		.led_hw_is_supported = as21xxx_led_hw_is_supported,
++		.led_hw_control_set = as21xxx_led_hw_control_set,
++		.led_hw_control_get = as21xxx_led_hw_control_get,
++		.led_polarity_set = as21xxx_led_polarity_set,
++	},
++	{
++		PHY_ID_MATCH_EXACT(PHY_ID_AS21210JB1),
++		.name		= "Aeonsemi AS21210JB1",
++		.probe		= as21xxx_probe,
++		.match_phy_device = as21xxx_match_phy_device,
++		.read_status	= as21xxx_read_status,
++		.led_brightness_set = as21xxx_led_brightness_set,
++		.led_hw_is_supported = as21xxx_led_hw_is_supported,
++		.led_hw_control_set = as21xxx_led_hw_control_set,
++		.led_hw_control_get = as21xxx_led_hw_control_get,
++		.led_polarity_set = as21xxx_led_polarity_set,
++	},
++	{
++		PHY_ID_MATCH_EXACT(PHY_ID_AS21511PB1),
++		.name		= "Aeonsemi AS21511PB1",
++		.probe		= as21xxx_probe,
++		.match_phy_device = as21xxx_match_phy_device,
++		.read_status	= as21xxx_read_status,
++		.led_brightness_set = as21xxx_led_brightness_set,
++		.led_hw_is_supported = as21xxx_led_hw_is_supported,
++		.led_hw_control_set = as21xxx_led_hw_control_set,
++		.led_hw_control_get = as21xxx_led_hw_control_get,
++		.led_polarity_set = as21xxx_led_polarity_set,
++	},
++};
++module_phy_driver(as21xxx_drivers);
++
++static struct mdio_device_id __maybe_unused as21xxx_tbl[] = {
++	{ PHY_ID_MATCH_VENDOR(PHY_VENDOR_AEONSEMI) },
++	{ }
++};
++MODULE_DEVICE_TABLE(mdio, as21xxx_tbl);
++
++MODULE_DESCRIPTION("Aeonsemi AS21xxx PHY driver");
++MODULE_AUTHOR("Christian Marangi <ansuelsmth at gmail.com>");
++MODULE_LICENSE("GPL");
diff --git a/target/linux/generic/backport-6.6/798-v6.10-01-net-phy-air_en8811h-Add-the-Airoha-EN8811H-PHY-drive.patch b/target/linux/generic/backport-6.6/798-v6.10-01-net-phy-air_en8811h-Add-the-Airoha-EN8811H-PHY-drive.patch
index 5b627cf449..ecea0e987d 100644
--- a/target/linux/generic/backport-6.6/798-v6.10-01-net-phy-air_en8811h-Add-the-Airoha-EN8811H-PHY-drive.patch
+++ b/target/linux/generic/backport-6.6/798-v6.10-01-net-phy-air_en8811h-Add-the-Airoha-EN8811H-PHY-drive.patch
@@ -27,9 +27,9 @@ Signed-off-by: Jakub Kicinski <kuba at kernel.org>
 
 --- a/drivers/net/phy/Kconfig
 +++ b/drivers/net/phy/Kconfig
-@@ -68,6 +68,11 @@ config SFP
- 
- comment "MII PHY device drivers"
+@@ -80,6 +80,11 @@ config AS21XXX_PHY
+ 	  AS21210PB1 that all register with the PHY ID 0x7500 0x7500
+ 	  before the firmware is loaded.
  
 +config AIR_EN8811H_PHY
 +	tristate "Airoha EN8811H 2.5 Gigabit PHY"
@@ -48,7 +48,7 @@ Signed-off-by: Jakub Kicinski <kuba at kernel.org>
 +obj-$(CONFIG_AIR_EN8811H_PHY)   += air_en8811h.o
  obj-$(CONFIG_AMD_PHY)		+= amd.o
  obj-$(CONFIG_AQUANTIA_PHY)	+= aquantia/
- obj-$(CONFIG_AX88796B_PHY)	+= ax88796b.o
+ obj-$(CONFIG_AS21XXX_PHY)	+= as21xxx.o
 --- /dev/null
 +++ b/drivers/net/phy/air_en8811h.c
 @@ -0,0 +1,1086 @@
diff --git a/target/linux/generic/backport-6.6/832-v6.7-net-phy-amd-Support-the-Altima-AMI101L.patch b/target/linux/generic/backport-6.6/832-v6.7-net-phy-amd-Support-the-Altima-AMI101L.patch
index 07287206f6..1ed1008ee5 100644
--- a/target/linux/generic/backport-6.6/832-v6.7-net-phy-amd-Support-the-Altima-AMI101L.patch
+++ b/target/linux/generic/backport-6.6/832-v6.7-net-phy-amd-Support-the-Altima-AMI101L.patch
@@ -16,7 +16,7 @@ Signed-off-by: Linus Walleij <linus.walleij at linaro.org>
 
 --- a/drivers/net/phy/Kconfig
 +++ b/drivers/net/phy/Kconfig
-@@ -74,9 +74,9 @@ config AIR_EN8811H_PHY
+@@ -86,9 +86,9 @@ config AIR_EN8811H_PHY
  	  Currently supports the Airoha EN8811H PHY.
  
  config AMD_PHY
diff --git a/target/linux/generic/backport-6.6/835-v6.9-net-phy-add-support-for-PHY-LEDs-polarity-modes.patch b/target/linux/generic/backport-6.6/835-v6.9-net-phy-add-support-for-PHY-LEDs-polarity-modes.patch
index 54932436c7..4d8742f0e3 100644
--- a/target/linux/generic/backport-6.6/835-v6.9-net-phy-add-support-for-PHY-LEDs-polarity-modes.patch
+++ b/target/linux/generic/backport-6.6/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
-@@ -3204,6 +3204,7 @@ static int of_phy_led(struct phy_device
+@@ -3226,6 +3226,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;
-@@ -3221,6 +3222,21 @@ static int of_phy_led(struct phy_device
+@@ -3243,6 +3244,21 @@ static int of_phy_led(struct phy_device
  	if (index > U8_MAX)
  		return -EINVAL;
  
@@ -76,7 +76,7 @@ Signed-off-by: Jakub Kicinski <kuba at kernel.org>
  /**
   * struct phy_driver - Driver structure for a particular PHY type
   *
-@@ -1143,6 +1152,19 @@ struct phy_driver {
+@@ -1144,6 +1153,19 @@ struct phy_driver {
  	int (*led_hw_control_get)(struct phy_device *dev, u8 index,
  				  unsigned long *rules);
  
diff --git a/target/linux/generic/backport-6.6/841-v6.13-net-phy-support-active-high-property-for-PHY-LEDs.patch b/target/linux/generic/backport-6.6/841-v6.13-net-phy-support-active-high-property-for-PHY-LEDs.patch
index 3d8a15bd1e..1b979f8662 100644
--- a/target/linux/generic/backport-6.6/841-v6.13-net-phy-support-active-high-property-for-PHY-LEDs.patch
+++ b/target/linux/generic/backport-6.6/841-v6.13-net-phy-support-active-high-property-for-PHY-LEDs.patch
@@ -19,7 +19,7 @@ Signed-off-by: Paolo Abeni <pabeni at redhat.com>
 
 --- a/drivers/net/phy/phy_device.c
 +++ b/drivers/net/phy/phy_device.c
-@@ -3222,11 +3222,17 @@ static int of_phy_led(struct phy_device
+@@ -3244,11 +3244,17 @@ static int of_phy_led(struct phy_device
  	if (index > U8_MAX)
  		return -EINVAL;
  
diff --git a/target/linux/generic/backport-6.6/895-01-v6.8-net-phy-add-possible-interfaces.patch b/target/linux/generic/backport-6.6/895-01-v6.8-net-phy-add-possible-interfaces.patch
index b86dbea898..43e2c92ef0 100644
--- a/target/linux/generic/backport-6.6/895-01-v6.8-net-phy-add-possible-interfaces.patch
+++ b/target/linux/generic/backport-6.6/895-01-v6.8-net-phy-add-possible-interfaces.patch
@@ -30,7 +30,7 @@ Signed-off-by: Jakub Kicinski <kuba at kernel.org>
 
 --- a/drivers/net/phy/phy_device.c
 +++ b/drivers/net/phy/phy_device.c
-@@ -1247,6 +1247,8 @@ int phy_init_hw(struct phy_device *phyde
+@@ -1269,6 +1269,8 @@ int phy_init_hw(struct phy_device *phyde
  	if (ret < 0)
  		return ret;
  
diff --git a/target/linux/generic/config-6.12 b/target/linux/generic/config-6.12
index 261166c6c3..dc836ccb10 100644
--- a/target/linux/generic/config-6.12
+++ b/target/linux/generic/config-6.12
@@ -458,6 +458,7 @@ CONFIG_ARM_MODULE_PLTS=y
 # CONFIG_ARM_TIMER_SP804 is not set
 # CONFIG_ARM_UNWIND is not set
 # CONFIG_ARM_VIRT_EXT is not set
+# CONFIG_AS21XXX_PHY is not set
 # CONFIG_AS3935 is not set
 # CONFIG_AS73211 is not set
 # CONFIG_ASM9260_TIMER is not set
diff --git a/target/linux/generic/config-6.6 b/target/linux/generic/config-6.6
index a7d06efed3..be73510dd0 100644
--- a/target/linux/generic/config-6.6
+++ b/target/linux/generic/config-6.6
@@ -440,6 +440,7 @@ CONFIG_ARM_MODULE_PLTS=y
 # CONFIG_ARM_TIMER_SP804 is not set
 # CONFIG_ARM_UNWIND is not set
 # CONFIG_ARM_VIRT_EXT is not set
+# CONFIG_AS21XXX_PHY is not set
 # CONFIG_AS3935 is not set
 # CONFIG_AS73211 is not set
 # CONFIG_ASM9260_TIMER is not set
diff --git a/target/linux/generic/hack-6.12/700-swconfig_switch_drivers.patch b/target/linux/generic/hack-6.12/700-swconfig_switch_drivers.patch
index 4591a42f78..9b1f53af3e 100644
--- a/target/linux/generic/hack-6.12/700-swconfig_switch_drivers.patch
+++ b/target/linux/generic/hack-6.12/700-swconfig_switch_drivers.patch
@@ -92,7 +92,7 @@ Signed-off-by: Felix Fietkau <nbd at nbd.name>
 +
  comment "MII PHY device drivers"
  
- config AIR_EN8811H_PHY
+ config AS21XXX_PHY
 --- a/drivers/net/phy/Makefile
 +++ b/drivers/net/phy/Makefile
 @@ -27,6 +27,21 @@ libphy-$(CONFIG_OPEN_ALLIANCE_HELPERS) +
diff --git a/target/linux/generic/hack-6.12/735-net-phy-realtek-rtl8261n.patch b/target/linux/generic/hack-6.12/735-net-phy-realtek-rtl8261n.patch
index 616db25890..cfcc720662 100644
--- a/target/linux/generic/hack-6.12/735-net-phy-realtek-rtl8261n.patch
+++ b/target/linux/generic/hack-6.12/735-net-phy-realtek-rtl8261n.patch
@@ -1,6 +1,6 @@
 --- a/drivers/net/phy/Kconfig
 +++ b/drivers/net/phy/Kconfig
-@@ -419,6 +419,8 @@ config QSEMI_PHY
+@@ -431,6 +431,8 @@ config QSEMI_PHY
  
  source "drivers/net/phy/realtek/Kconfig"
  
@@ -11,7 +11,7 @@
  	help
 --- a/drivers/net/phy/Makefile
 +++ b/drivers/net/phy/Makefile
-@@ -110,6 +110,7 @@ obj-$(CONFIG_NXP_TJA11XX_PHY)	+= nxp-tja
+@@ -111,6 +111,7 @@ obj-$(CONFIG_NXP_TJA11XX_PHY)	+= nxp-tja
  obj-y				+= qcom/
  obj-$(CONFIG_QSEMI_PHY)		+= qsemi.o
  obj-$(CONFIG_REALTEK_PHY)	+= realtek/
diff --git a/target/linux/generic/hack-6.6/700-swconfig_switch_drivers.patch b/target/linux/generic/hack-6.6/700-swconfig_switch_drivers.patch
index 4428ebbb5a..59cc16cb75 100644
--- a/target/linux/generic/hack-6.6/700-swconfig_switch_drivers.patch
+++ b/target/linux/generic/hack-6.6/700-swconfig_switch_drivers.patch
@@ -92,7 +92,7 @@ Signed-off-by: Felix Fietkau <nbd at nbd.name>
 +
  comment "MII PHY device drivers"
  
- config AIR_EN8811H_PHY
+ config AS21XXX_PHY
 --- a/drivers/net/phy/Makefile
 +++ b/drivers/net/phy/Makefile
 @@ -26,6 +26,21 @@ libphy-$(CONFIG_LED_TRIGGER_PHY)	+= phy_
diff --git a/target/linux/generic/hack-6.6/735-net-phy-realtek-rtl8261n.patch b/target/linux/generic/hack-6.6/735-net-phy-realtek-rtl8261n.patch
index 946869c303..b142683a28 100644
--- a/target/linux/generic/hack-6.6/735-net-phy-realtek-rtl8261n.patch
+++ b/target/linux/generic/hack-6.6/735-net-phy-realtek-rtl8261n.patch
@@ -1,6 +1,6 @@
 --- a/drivers/net/phy/Kconfig
 +++ b/drivers/net/phy/Kconfig
-@@ -406,6 +406,8 @@ config QSEMI_PHY
+@@ -418,6 +418,8 @@ config QSEMI_PHY
  
  source "drivers/net/phy/realtek/Kconfig"
  
@@ -11,7 +11,7 @@
  	help
 --- a/drivers/net/phy/Makefile
 +++ b/drivers/net/phy/Makefile
-@@ -100,6 +100,7 @@ obj-$(CONFIG_NXP_TJA11XX_PHY)	+= nxp-tja
+@@ -101,6 +101,7 @@ obj-$(CONFIG_NXP_TJA11XX_PHY)	+= nxp-tja
  obj-y				+= qcom/
  obj-$(CONFIG_QSEMI_PHY)		+= qsemi.o
  obj-$(CONFIG_REALTEK_PHY)	+= realtek/
diff --git a/target/linux/generic/pending-6.12/703-phy-add-detach-callback-to-struct-phy_driver.patch b/target/linux/generic/pending-6.12/703-phy-add-detach-callback-to-struct-phy_driver.patch
index b8d20d8610..ed6c9070b2 100644
--- a/target/linux/generic/pending-6.12/703-phy-add-detach-callback-to-struct-phy_driver.patch
+++ b/target/linux/generic/pending-6.12/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
-@@ -2015,6 +2015,9 @@ void phy_detach(struct phy_device *phyde
+@@ -2037,6 +2037,9 @@ void phy_detach(struct phy_device *phyde
  		phydev->devlink = NULL;
  	}
  
diff --git a/target/linux/generic/pending-6.12/720-01-net-phy-realtek-use-genphy_soft_reset-for-2.5G-PHYs.patch b/target/linux/generic/pending-6.12/720-01-net-phy-realtek-use-genphy_soft_reset-for-2.5G-PHYs.patch
index 9bb5737b39..a4cb71b83b 100644
--- a/target/linux/generic/pending-6.12/720-01-net-phy-realtek-use-genphy_soft_reset-for-2.5G-PHYs.patch
+++ b/target/linux/generic/pending-6.12/720-01-net-phy-realtek-use-genphy_soft_reset-for-2.5G-PHYs.patch
@@ -15,7 +15,7 @@ Signed-off-by: Daniel Golle <daniel at makrotopia.org>
 
 --- a/drivers/net/phy/realtek/realtek_main.c
 +++ b/drivers/net/phy/realtek/realtek_main.c
-@@ -1666,6 +1666,7 @@ static struct phy_driver realtek_drvs[]
+@@ -1675,6 +1675,7 @@ static struct phy_driver realtek_drvs[]
  	}, {
  		.name		= "RTL8226 2.5Gbps PHY",
  		.match_phy_device = rtl8226_match_phy_device,
@@ -23,7 +23,7 @@ Signed-off-by: Daniel Golle <daniel at makrotopia.org>
  		.get_features	= rtl822x_get_features,
  		.config_aneg	= rtl822x_config_aneg,
  		.read_status	= rtl822x_read_status,
-@@ -1676,6 +1677,7 @@ static struct phy_driver realtek_drvs[]
+@@ -1685,6 +1686,7 @@ static struct phy_driver realtek_drvs[]
  	}, {
  		.match_phy_device = rtl8221b_match_phy_device,
  		.name		= "RTL8226B_RTL8221B 2.5Gbps PHY",
@@ -31,7 +31,7 @@ Signed-off-by: Daniel Golle <daniel at makrotopia.org>
  		.get_features	= rtl822x_get_features,
  		.config_aneg	= rtl822x_config_aneg,
  		.config_init    = rtl822xb_config_init,
-@@ -1698,6 +1700,7 @@ static struct phy_driver realtek_drvs[]
+@@ -1707,6 +1709,7 @@ static struct phy_driver realtek_drvs[]
  	}, {
  		PHY_ID_MATCH_EXACT(0x001cc848),
  		.name           = "RTL8226B-CG_RTL8221B-CG 2.5Gbps PHY",
@@ -39,7 +39,7 @@ Signed-off-by: Daniel Golle <daniel at makrotopia.org>
  		.get_features   = rtl822x_get_features,
  		.config_aneg    = rtl822x_config_aneg,
  		.config_init    = rtl822xb_config_init,
-@@ -1710,6 +1713,7 @@ static struct phy_driver realtek_drvs[]
+@@ -1719,6 +1722,7 @@ static struct phy_driver realtek_drvs[]
  	}, {
  		.match_phy_device = rtl8221b_vb_cg_c22_match_phy_device,
  		.name           = "RTL8221B-VB-CG 2.5Gbps PHY (C22)",
@@ -47,7 +47,7 @@ Signed-off-by: Daniel Golle <daniel at makrotopia.org>
  		.probe		= rtl822x_probe,
  		.get_features   = rtl822x_get_features,
  		.config_aneg    = rtl822x_config_aneg,
-@@ -1723,6 +1727,7 @@ static struct phy_driver realtek_drvs[]
+@@ -1732,6 +1736,7 @@ static struct phy_driver realtek_drvs[]
  	}, {
  		.match_phy_device = rtl8221b_vb_cg_c45_match_phy_device,
  		.name           = "RTL8221B-VB-CG 2.5Gbps PHY (C45)",
@@ -55,7 +55,7 @@ Signed-off-by: Daniel Golle <daniel at makrotopia.org>
  		.probe		= rtl822x_probe,
  		.config_init    = rtl822xb_config_init,
  		.get_rate_matching = rtl822xb_get_rate_matching,
-@@ -1734,6 +1739,7 @@ static struct phy_driver realtek_drvs[]
+@@ -1743,6 +1748,7 @@ static struct phy_driver realtek_drvs[]
  	}, {
  		.match_phy_device = rtl8221b_vn_cg_c22_match_phy_device,
  		.name           = "RTL8221B-VM-CG 2.5Gbps PHY (C22)",
@@ -63,7 +63,7 @@ Signed-off-by: Daniel Golle <daniel at makrotopia.org>
  		.probe		= rtl822x_probe,
  		.get_features   = rtl822x_get_features,
  		.config_aneg    = rtl822x_config_aneg,
-@@ -1747,6 +1753,7 @@ static struct phy_driver realtek_drvs[]
+@@ -1756,6 +1762,7 @@ static struct phy_driver realtek_drvs[]
  	}, {
  		.match_phy_device = rtl8221b_vn_cg_c45_match_phy_device,
  		.name           = "RTL8221B-VN-CG 2.5Gbps PHY (C45)",
diff --git a/target/linux/generic/pending-6.12/720-05-net-phy-realtek-detect-early-version-of-RTL8221B.patch b/target/linux/generic/pending-6.12/720-05-net-phy-realtek-detect-early-version-of-RTL8221B.patch
index 4e07882b1e..186b3ff2b9 100644
--- a/target/linux/generic/pending-6.12/720-05-net-phy-realtek-detect-early-version-of-RTL8221B.patch
+++ b/target/linux/generic/pending-6.12/720-05-net-phy-realtek-detect-early-version-of-RTL8221B.patch
@@ -14,7 +14,7 @@ Signed-off-by: Daniel Golle <daniel at makrotopia.org>
 Signed-off-by: Mieczyslaw Nalewaj <namiltd at yahoo.com>
 --- a/drivers/net/phy/realtek/realtek_main.c
 +++ b/drivers/net/phy/realtek/realtek_main.c
-@@ -1398,10 +1398,32 @@ static int rtl8226_match_phy_device(stru
+@@ -1400,10 +1400,32 @@ static int rtl8226_match_phy_device(stru
  static int rtlgen_is_c45_match(struct phy_device *phydev, unsigned int id,
  			       bool is_c45)
  {
@@ -49,4 +49,4 @@ Signed-off-by: Mieczyslaw Nalewaj <namiltd at yahoo.com>
 +	}
  }
  
- static int rtl8221b_match_phy_device(struct phy_device *phydev)
+ static int rtl8221b_match_phy_device(struct phy_device *phydev,
diff --git a/target/linux/generic/pending-6.12/720-06-net-phy-realtek-support-interrupt-of-RTL8221B.patch b/target/linux/generic/pending-6.12/720-06-net-phy-realtek-support-interrupt-of-RTL8221B.patch
index 855ea41c8c..e908af055f 100644
--- a/target/linux/generic/pending-6.12/720-06-net-phy-realtek-support-interrupt-of-RTL8221B.patch
+++ b/target/linux/generic/pending-6.12/720-06-net-phy-realtek-support-interrupt-of-RTL8221B.patch
@@ -12,7 +12,7 @@ Signed-off-by: Jianhui Zhao <zhaojh329 at gmail.com>
 
 --- a/drivers/net/phy/realtek/realtek_main.c
 +++ b/drivers/net/phy/realtek/realtek_main.c
-@@ -1610,6 +1610,51 @@ static irqreturn_t rtl9000a_handle_inter
+@@ -1619,6 +1619,51 @@ static irqreturn_t rtl9000a_handle_inter
  	return IRQ_HANDLED;
  }
  
@@ -64,7 +64,7 @@ Signed-off-by: Jianhui Zhao <zhaojh329 at gmail.com>
  static struct phy_driver realtek_drvs[] = {
  	{
  		PHY_ID_MATCH_EXACT(0x00008201),
-@@ -1774,6 +1819,8 @@ static struct phy_driver realtek_drvs[]
+@@ -1783,6 +1828,8 @@ static struct phy_driver realtek_drvs[]
  	}, {
  		.match_phy_device = rtl8221b_vb_cg_c22_match_phy_device,
  		.name           = "RTL8221B-VB-CG 2.5Gbps PHY (C22)",
@@ -73,7 +73,7 @@ Signed-off-by: Jianhui Zhao <zhaojh329 at gmail.com>
  		.soft_reset     = genphy_soft_reset,
  		.probe		= rtl822x_probe,
  		.get_features   = rtl822x_get_features,
-@@ -1788,6 +1835,8 @@ static struct phy_driver realtek_drvs[]
+@@ -1797,6 +1844,8 @@ static struct phy_driver realtek_drvs[]
  	}, {
  		.match_phy_device = rtl8221b_vb_cg_c45_match_phy_device,
  		.name           = "RTL8221B-VB-CG 2.5Gbps PHY (C45)",
@@ -82,7 +82,7 @@ Signed-off-by: Jianhui Zhao <zhaojh329 at gmail.com>
  		.soft_reset     = rtl822x_c45_soft_reset,
  		.probe		= rtl822x_probe,
  		.config_init    = rtl822xb_config_init,
-@@ -1800,6 +1849,8 @@ static struct phy_driver realtek_drvs[]
+@@ -1809,6 +1858,8 @@ static struct phy_driver realtek_drvs[]
  	}, {
  		.match_phy_device = rtl8221b_vn_cg_c22_match_phy_device,
  		.name           = "RTL8221B-VM-CG 2.5Gbps PHY (C22)",
@@ -91,7 +91,7 @@ Signed-off-by: Jianhui Zhao <zhaojh329 at gmail.com>
  		.soft_reset     = genphy_soft_reset,
  		.probe		= rtl822x_probe,
  		.get_features   = rtl822x_get_features,
-@@ -1814,6 +1865,8 @@ static struct phy_driver realtek_drvs[]
+@@ -1823,6 +1874,8 @@ static struct phy_driver realtek_drvs[]
  	}, {
  		.match_phy_device = rtl8221b_vn_cg_c45_match_phy_device,
  		.name           = "RTL8221B-VN-CG 2.5Gbps PHY (C45)",
diff --git a/target/linux/generic/pending-6.12/720-08-net-phy-realtek-work-around-broken-serdes.patch b/target/linux/generic/pending-6.12/720-08-net-phy-realtek-work-around-broken-serdes.patch
index e451d13bd8..d4920b5c97 100644
--- a/target/linux/generic/pending-6.12/720-08-net-phy-realtek-work-around-broken-serdes.patch
+++ b/target/linux/generic/pending-6.12/720-08-net-phy-realtek-work-around-broken-serdes.patch
@@ -38,7 +38,7 @@ Signed-off-by: Daniel Golle <daniel at makrotopia.org>
  static int rtl822xb_get_rate_matching(struct phy_device *phydev,
  				      phy_interface_t iface)
  {
-@@ -1842,7 +1858,7 @@ static struct phy_driver realtek_drvs[]
+@@ -1851,7 +1867,7 @@ static struct phy_driver realtek_drvs[]
  		.handle_interrupt = rtl8221b_handle_interrupt,
  		.soft_reset     = rtl822x_c45_soft_reset,
  		.probe		= rtl822x_probe,
@@ -47,7 +47,7 @@ Signed-off-by: Daniel Golle <daniel at makrotopia.org>
  		.get_rate_matching = rtl822xb_get_rate_matching,
  		.get_features   = rtl822x_c45_get_features,
  		.config_aneg    = rtl822x_c45_config_aneg,
-@@ -1872,7 +1888,7 @@ static struct phy_driver realtek_drvs[]
+@@ -1881,7 +1897,7 @@ static struct phy_driver realtek_drvs[]
  		.handle_interrupt = rtl8221b_handle_interrupt,
  		.soft_reset     = rtl822x_c45_soft_reset,
  		.probe		= rtl822x_probe,
diff --git a/target/linux/generic/pending-6.12/741-net-phy-broadcom-update-dependency-condition.patch b/target/linux/generic/pending-6.12/741-net-phy-broadcom-update-dependency-condition.patch
index f5621e34ee..7a52bd5521 100644
--- a/target/linux/generic/pending-6.12/741-net-phy-broadcom-update-dependency-condition.patch
+++ b/target/linux/generic/pending-6.12/741-net-phy-broadcom-update-dependency-condition.patch
@@ -24,7 +24,7 @@ Signed-off-by: David Bauer <mail at david-bauer.net>
 
 --- a/drivers/net/phy/Kconfig
 +++ b/drivers/net/phy/Kconfig
-@@ -139,7 +139,7 @@ config BROADCOM_PHY
+@@ -151,7 +151,7 @@ config BROADCOM_PHY
  	tristate "Broadcom 54XX PHYs"
  	select BCM_NET_PHYLIB
  	select BCM_NET_PHYPTP if NETWORK_PHY_TIMESTAMPING
diff --git a/target/linux/generic/pending-6.6/703-phy-add-detach-callback-to-struct-phy_driver.patch b/target/linux/generic/pending-6.6/703-phy-add-detach-callback-to-struct-phy_driver.patch
index 453abe65c6..efa76572f8 100644
--- a/target/linux/generic/pending-6.6/703-phy-add-detach-callback-to-struct-phy_driver.patch
+++ b/target/linux/generic/pending-6.6/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
-@@ -1912,6 +1912,9 @@ void phy_detach(struct phy_device *phyde
+@@ -1934,6 +1934,9 @@ void phy_detach(struct phy_device *phyde
  		phydev->devlink = NULL;
  	}
  
diff --git a/target/linux/generic/pending-6.6/720-01-net-phy-realtek-use-genphy_soft_reset-for-2.5G-PHYs.patch b/target/linux/generic/pending-6.6/720-01-net-phy-realtek-use-genphy_soft_reset-for-2.5G-PHYs.patch
index 1becba6da6..deb6506186 100644
--- a/target/linux/generic/pending-6.6/720-01-net-phy-realtek-use-genphy_soft_reset-for-2.5G-PHYs.patch
+++ b/target/linux/generic/pending-6.6/720-01-net-phy-realtek-use-genphy_soft_reset-for-2.5G-PHYs.patch
@@ -15,7 +15,7 @@ Signed-off-by: Daniel Golle <daniel at makrotopia.org>
 
 --- a/drivers/net/phy/realtek/realtek_main.c
 +++ b/drivers/net/phy/realtek/realtek_main.c
-@@ -1638,6 +1638,7 @@ static struct phy_driver realtek_drvs[]
+@@ -1647,6 +1647,7 @@ static struct phy_driver realtek_drvs[]
  	}, {
  		.name		= "RTL8226 2.5Gbps PHY",
  		.match_phy_device = rtl8226_match_phy_device,
@@ -23,7 +23,7 @@ Signed-off-by: Daniel Golle <daniel at makrotopia.org>
  		.get_features	= rtl822x_get_features,
  		.config_aneg	= rtl822x_config_aneg,
  		.read_status	= rtl822x_read_status,
-@@ -1648,6 +1649,7 @@ static struct phy_driver realtek_drvs[]
+@@ -1657,6 +1658,7 @@ static struct phy_driver realtek_drvs[]
  	}, {
  		.match_phy_device = rtl8221b_match_phy_device,
  		.name		= "RTL8226B_RTL8221B 2.5Gbps PHY",
@@ -31,7 +31,7 @@ Signed-off-by: Daniel Golle <daniel at makrotopia.org>
  		.get_features	= rtl822x_get_features,
  		.config_aneg	= rtl822x_config_aneg,
  		.config_init    = rtl822xb_config_init,
-@@ -1660,6 +1662,7 @@ static struct phy_driver realtek_drvs[]
+@@ -1669,6 +1671,7 @@ static struct phy_driver realtek_drvs[]
  	}, {
  		PHY_ID_MATCH_EXACT(0x001cc838),
  		.name           = "RTL8226-CG 2.5Gbps PHY",
@@ -39,7 +39,7 @@ Signed-off-by: Daniel Golle <daniel at makrotopia.org>
  		.get_features   = rtl822x_get_features,
  		.config_aneg    = rtl822x_config_aneg,
  		.read_status    = rtl822x_read_status,
-@@ -1670,6 +1673,7 @@ static struct phy_driver realtek_drvs[]
+@@ -1679,6 +1682,7 @@ static struct phy_driver realtek_drvs[]
  	}, {
  		PHY_ID_MATCH_EXACT(0x001cc848),
  		.name           = "RTL8226B-CG_RTL8221B-CG 2.5Gbps PHY",
@@ -47,7 +47,7 @@ Signed-off-by: Daniel Golle <daniel at makrotopia.org>
  		.get_features   = rtl822x_get_features,
  		.config_aneg    = rtl822x_config_aneg,
  		.config_init    = rtl822xb_config_init,
-@@ -1682,6 +1686,7 @@ static struct phy_driver realtek_drvs[]
+@@ -1691,6 +1695,7 @@ static struct phy_driver realtek_drvs[]
  	}, {
  		.match_phy_device = rtl8221b_vb_cg_c22_match_phy_device,
  		.name           = "RTL8221B-VB-CG 2.5Gbps PHY (C22)",
@@ -55,7 +55,7 @@ Signed-off-by: Daniel Golle <daniel at makrotopia.org>
  		.probe		= rtl822x_probe,
  		.get_features   = rtl822x_get_features,
  		.config_aneg    = rtl822x_config_aneg,
-@@ -1695,6 +1700,7 @@ static struct phy_driver realtek_drvs[]
+@@ -1704,6 +1709,7 @@ static struct phy_driver realtek_drvs[]
  	}, {
  		.match_phy_device = rtl8221b_vb_cg_c45_match_phy_device,
  		.name           = "RTL8221B-VB-CG 2.5Gbps PHY (C45)",
@@ -63,7 +63,7 @@ Signed-off-by: Daniel Golle <daniel at makrotopia.org>
  		.probe		= rtl822x_probe,
  		.config_init    = rtl822xb_config_init,
  		.get_rate_matching = rtl822xb_get_rate_matching,
-@@ -1706,6 +1712,7 @@ static struct phy_driver realtek_drvs[]
+@@ -1715,6 +1721,7 @@ static struct phy_driver realtek_drvs[]
  	}, {
  		.match_phy_device = rtl8221b_vn_cg_c22_match_phy_device,
  		.name           = "RTL8221B-VM-CG 2.5Gbps PHY (C22)",
@@ -71,7 +71,7 @@ Signed-off-by: Daniel Golle <daniel at makrotopia.org>
  		.probe		= rtl822x_probe,
  		.get_features   = rtl822x_get_features,
  		.config_aneg    = rtl822x_config_aneg,
-@@ -1719,6 +1726,7 @@ static struct phy_driver realtek_drvs[]
+@@ -1728,6 +1735,7 @@ static struct phy_driver realtek_drvs[]
  	}, {
  		.match_phy_device = rtl8221b_vn_cg_c45_match_phy_device,
  		.name           = "RTL8221B-VN-CG 2.5Gbps PHY (C45)",
diff --git a/target/linux/generic/pending-6.6/720-05-net-phy-realtek-detect-early-version-of-RTL8221B.patch b/target/linux/generic/pending-6.6/720-05-net-phy-realtek-detect-early-version-of-RTL8221B.patch
index 0918794d87..2886babe57 100644
--- a/target/linux/generic/pending-6.6/720-05-net-phy-realtek-detect-early-version-of-RTL8221B.patch
+++ b/target/linux/generic/pending-6.6/720-05-net-phy-realtek-detect-early-version-of-RTL8221B.patch
@@ -14,7 +14,7 @@ Signed-off-by: Daniel Golle <daniel at makrotopia.org>
 Signed-off-by: Mieczyslaw Nalewaj <namiltd at yahoo.com>
 --- a/drivers/net/phy/realtek/realtek_main.c
 +++ b/drivers/net/phy/realtek/realtek_main.c
-@@ -1368,10 +1368,32 @@ static int rtl8226_match_phy_device(stru
+@@ -1370,10 +1370,32 @@ static int rtl8226_match_phy_device(stru
  static int rtlgen_is_c45_match(struct phy_device *phydev, unsigned int id,
  			       bool is_c45)
  {
@@ -49,4 +49,4 @@ Signed-off-by: Mieczyslaw Nalewaj <namiltd at yahoo.com>
 +	}
  }
  
- static int rtl8221b_match_phy_device(struct phy_device *phydev)
+ static int rtl8221b_match_phy_device(struct phy_device *phydev,
diff --git a/target/linux/generic/pending-6.6/720-06-net-phy-realtek-support-interrupt-of-RTL8221B.patch b/target/linux/generic/pending-6.6/720-06-net-phy-realtek-support-interrupt-of-RTL8221B.patch
index 9afe8baca6..29610d2767 100644
--- a/target/linux/generic/pending-6.6/720-06-net-phy-realtek-support-interrupt-of-RTL8221B.patch
+++ b/target/linux/generic/pending-6.6/720-06-net-phy-realtek-support-interrupt-of-RTL8221B.patch
@@ -12,7 +12,7 @@ Signed-off-by: Jianhui Zhao <zhaojh329 at gmail.com>
 
 --- a/drivers/net/phy/realtek/realtek_main.c
 +++ b/drivers/net/phy/realtek/realtek_main.c
-@@ -1580,6 +1580,51 @@ static irqreturn_t rtl9000a_handle_inter
+@@ -1589,6 +1589,51 @@ static irqreturn_t rtl9000a_handle_inter
  	return IRQ_HANDLED;
  }
  
@@ -64,7 +64,7 @@ Signed-off-by: Jianhui Zhao <zhaojh329 at gmail.com>
  static struct phy_driver realtek_drvs[] = {
  	{
  		PHY_ID_MATCH_EXACT(0x00008201),
-@@ -1745,6 +1790,8 @@ static struct phy_driver realtek_drvs[]
+@@ -1754,6 +1799,8 @@ static struct phy_driver realtek_drvs[]
  	}, {
  		.match_phy_device = rtl8221b_vb_cg_c22_match_phy_device,
  		.name           = "RTL8221B-VB-CG 2.5Gbps PHY (C22)",
@@ -73,7 +73,7 @@ Signed-off-by: Jianhui Zhao <zhaojh329 at gmail.com>
  		.soft_reset     = genphy_soft_reset,
  		.probe		= rtl822x_probe,
  		.get_features   = rtl822x_get_features,
-@@ -1759,6 +1806,8 @@ static struct phy_driver realtek_drvs[]
+@@ -1768,6 +1815,8 @@ static struct phy_driver realtek_drvs[]
  	}, {
  		.match_phy_device = rtl8221b_vb_cg_c45_match_phy_device,
  		.name           = "RTL8221B-VB-CG 2.5Gbps PHY (C45)",
@@ -82,7 +82,7 @@ Signed-off-by: Jianhui Zhao <zhaojh329 at gmail.com>
  		.soft_reset     = genphy_soft_reset,
  		.probe		= rtl822x_probe,
  		.config_init    = rtl822xb_config_init,
-@@ -1771,6 +1820,8 @@ static struct phy_driver realtek_drvs[]
+@@ -1780,6 +1829,8 @@ static struct phy_driver realtek_drvs[]
  	}, {
  		.match_phy_device = rtl8221b_vn_cg_c22_match_phy_device,
  		.name           = "RTL8221B-VM-CG 2.5Gbps PHY (C22)",
@@ -91,7 +91,7 @@ Signed-off-by: Jianhui Zhao <zhaojh329 at gmail.com>
  		.soft_reset     = genphy_soft_reset,
  		.probe		= rtl822x_probe,
  		.get_features   = rtl822x_get_features,
-@@ -1785,6 +1836,8 @@ static struct phy_driver realtek_drvs[]
+@@ -1794,6 +1845,8 @@ static struct phy_driver realtek_drvs[]
  	}, {
  		.match_phy_device = rtl8221b_vn_cg_c45_match_phy_device,
  		.name           = "RTL8221B-VN-CG 2.5Gbps PHY (C45)",
diff --git a/target/linux/generic/pending-6.6/720-08-net-phy-realtek-work-around-broken-serdes.patch b/target/linux/generic/pending-6.6/720-08-net-phy-realtek-work-around-broken-serdes.patch
index 1749a74e36..1b6978547d 100644
--- a/target/linux/generic/pending-6.6/720-08-net-phy-realtek-work-around-broken-serdes.patch
+++ b/target/linux/generic/pending-6.6/720-08-net-phy-realtek-work-around-broken-serdes.patch
@@ -38,7 +38,7 @@ Signed-off-by: Daniel Golle <daniel at makrotopia.org>
  static int rtl822xb_get_rate_matching(struct phy_device *phydev,
  				      phy_interface_t iface)
  {
-@@ -1813,7 +1829,7 @@ static struct phy_driver realtek_drvs[]
+@@ -1822,7 +1838,7 @@ static struct phy_driver realtek_drvs[]
  		.handle_interrupt = rtl8221b_handle_interrupt,
  		.soft_reset     = genphy_soft_reset,
  		.probe		= rtl822x_probe,
@@ -47,7 +47,7 @@ Signed-off-by: Daniel Golle <daniel at makrotopia.org>
  		.get_rate_matching = rtl822xb_get_rate_matching,
  		.get_features   = rtl822x_c45_get_features,
  		.config_aneg    = rtl822x_c45_config_aneg,
-@@ -1843,7 +1859,7 @@ static struct phy_driver realtek_drvs[]
+@@ -1852,7 +1868,7 @@ static struct phy_driver realtek_drvs[]
  		.handle_interrupt = rtl8221b_handle_interrupt,
  		.soft_reset     = genphy_soft_reset,
  		.probe		= rtl822x_probe,
diff --git a/target/linux/generic/pending-6.6/741-net-phy-broadcom-update-dependency-condition.patch b/target/linux/generic/pending-6.6/741-net-phy-broadcom-update-dependency-condition.patch
index 80b69920e9..449b27e617 100644
--- a/target/linux/generic/pending-6.6/741-net-phy-broadcom-update-dependency-condition.patch
+++ b/target/linux/generic/pending-6.6/741-net-phy-broadcom-update-dependency-condition.patch
@@ -24,7 +24,7 @@ Signed-off-by: David Bauer <mail at david-bauer.net>
 
 --- a/drivers/net/phy/Kconfig
 +++ b/drivers/net/phy/Kconfig
-@@ -113,7 +113,7 @@ config BROADCOM_PHY
+@@ -125,7 +125,7 @@ config BROADCOM_PHY
  	tristate "Broadcom 54XX PHYs"
  	select BCM_NET_PHYLIB
  	select BCM_NET_PHYPTP if NETWORK_PHY_TIMESTAMPING
diff --git a/target/linux/mediatek/patches-6.12/500-gsw-rtl8367s-mt7622-support.patch b/target/linux/mediatek/patches-6.12/500-gsw-rtl8367s-mt7622-support.patch
index 72a1464966..8f911f6fd8 100644
--- a/target/linux/mediatek/patches-6.12/500-gsw-rtl8367s-mt7622-support.patch
+++ b/target/linux/mediatek/patches-6.12/500-gsw-rtl8367s-mt7622-support.patch
@@ -1,6 +1,6 @@
 --- a/drivers/net/phy/Kconfig
 +++ b/drivers/net/phy/Kconfig
-@@ -431,6 +431,12 @@ config ROCKCHIP_PHY
+@@ -443,6 +443,12 @@ config ROCKCHIP_PHY
  	help
  	  Currently supports the integrated Ethernet PHY.
  
@@ -15,7 +15,7 @@
  	select CRC16
 --- a/drivers/net/phy/Makefile
 +++ b/drivers/net/phy/Makefile
-@@ -113,6 +113,7 @@ obj-$(CONFIG_REALTEK_PHY)	+= realtek/
+@@ -114,6 +114,7 @@ obj-$(CONFIG_REALTEK_PHY)	+= realtek/
  obj-y				+= rtl8261n/
  obj-$(CONFIG_RENESAS_PHY)	+= uPD60620.o
  obj-$(CONFIG_ROCKCHIP_PHY)	+= rockchip.o
diff --git a/target/linux/mediatek/patches-6.12/734-net-phy-add-Airoha-EN8801SC-PHY.patch b/target/linux/mediatek/patches-6.12/734-net-phy-add-Airoha-EN8801SC-PHY.patch
index 079351b7a2..4ebaffd1dd 100644
--- a/target/linux/mediatek/patches-6.12/734-net-phy-add-Airoha-EN8801SC-PHY.patch
+++ b/target/linux/mediatek/patches-6.12/734-net-phy-add-Airoha-EN8801SC-PHY.patch
@@ -14,9 +14,9 @@ Signed-off-by: Robert Marko <robert.marko at sartura.hr>
 
 --- a/drivers/net/phy/Kconfig
 +++ b/drivers/net/phy/Kconfig
-@@ -153,6 +153,11 @@ endif # RTL8366_SMI
- 
- comment "MII PHY device drivers"
+@@ -165,6 +165,11 @@ config AS21XXX_PHY
+ 	  AS21210PB1 that all register with the PHY ID 0x7500 0x7500
+ 	  before the firmware is loaded.
  
 +config AIROHA_EN8801SC_PHY
 +	tristate "Airoha EN8801SC Gigabit PHY"
diff --git a/target/linux/mediatek/patches-6.12/737-net-dsa-add-Airoha-AN8855.patch b/target/linux/mediatek/patches-6.12/737-net-dsa-add-Airoha-AN8855.patch
index b0adf04a5b..d31215ec27 100644
--- a/target/linux/mediatek/patches-6.12/737-net-dsa-add-Airoha-AN8855.patch
+++ b/target/linux/mediatek/patches-6.12/737-net-dsa-add-Airoha-AN8855.patch
@@ -254,7 +254,7 @@ Christian Marangi (9):
  obj-$(CONFIG_MDIO_BCM_UNIMAC)		+= mdio-bcm-unimac.o
 --- a/drivers/net/phy/Kconfig
 +++ b/drivers/net/phy/Kconfig
-@@ -158,6 +158,11 @@ config AIROHA_EN8801SC_PHY
+@@ -170,6 +170,11 @@ config AIROHA_EN8801SC_PHY
  	help
  	  Currently supports the Airoha EN8801SC PHY.
  
diff --git a/target/linux/mediatek/patches-6.12/739-net-add-negotiation-of-in-band-capabilities.patch b/target/linux/mediatek/patches-6.12/739-net-add-negotiation-of-in-band-capabilities.patch
index 632aad0ed2..12638978b3 100644
--- a/target/linux/mediatek/patches-6.12/739-net-add-negotiation-of-in-band-capabilities.patch
+++ b/target/linux/mediatek/patches-6.12/739-net-add-negotiation-of-in-band-capabilities.patch
@@ -880,7 +880,7 @@ publishing the in-band capabilities from the BCM84881 PHY driver.
  	 * @get_rate_matching: Get the supported type of rate matching for a
  	 * particular phy interface. This is used by phy consumers to determine
  	 * whether to advertise lower-speed modes for that interface. It is
-@@ -1839,6 +1870,9 @@ int phy_config_aneg(struct phy_device *p
+@@ -1840,6 +1871,9 @@ int phy_config_aneg(struct phy_device *p
  int _phy_start_aneg(struct phy_device *phydev);
  int phy_start_aneg(struct phy_device *phydev);
  int phy_aneg_done(struct phy_device *phydev);
diff --git a/target/linux/realtek/files-6.12/drivers/net/phy/rtl83xx-phy.c b/target/linux/realtek/files-6.12/drivers/net/phy/rtl83xx-phy.c
index 3b14e9bcfe..7d38070091 100644
--- a/target/linux/realtek/files-6.12/drivers/net/phy/rtl83xx-phy.c
+++ b/target/linux/realtek/files-6.12/drivers/net/phy/rtl83xx-phy.c
@@ -223,12 +223,14 @@ static int rtl821x_match_phy_device(struct phy_device *phydev)
 	return PHY_IS_RTL8214FB;
 }
 
-static int rtl8218b_ext_match_phy_device(struct phy_device *phydev)
+static int rtl8218b_ext_match_phy_device(struct phy_device *phydev,
+					 const struct phy_driver *phydrv)
 {
 	return rtl821x_match_phy_device(phydev) == PHY_IS_RTL8218B_E;
 }
 
-static int rtl8214fc_match_phy_device(struct phy_device *phydev)
+static int rtl8214fc_match_phy_device(struct phy_device *phydev,
+				      const struct phy_driver *phydrv)
 {
 	return rtl821x_match_phy_device(phydev) == PHY_IS_RTL8214FC;
 }
diff --git a/target/linux/realtek/patches-6.12/706-include-linux-add-phy-ops-for-rtl838x.patch b/target/linux/realtek/patches-6.12/706-include-linux-add-phy-ops-for-rtl838x.patch
index 9b054f96a8..ca411d6954 100644
--- a/target/linux/realtek/patches-6.12/706-include-linux-add-phy-ops-for-rtl838x.patch
+++ b/target/linux/realtek/patches-6.12/706-include-linux-add-phy-ops-for-rtl838x.patch
@@ -21,7 +21,7 @@ Submitted-by: John Crispin <john at phrozen.org>
 
 --- a/include/linux/phy.h
 +++ b/include/linux/phy.h
-@@ -1226,6 +1226,8 @@ struct phy_driver {
+@@ -1227,6 +1227,8 @@ struct phy_driver {
  	 */
  	int (*led_polarity_set)(struct phy_device *dev, int index,
  				unsigned long modes);
diff --git a/target/linux/realtek/patches-6.12/720-add-rtl-phy.patch b/target/linux/realtek/patches-6.12/720-add-rtl-phy.patch
index 3be218635e..4d1efd76b7 100644
--- a/target/linux/realtek/patches-6.12/720-add-rtl-phy.patch
+++ b/target/linux/realtek/patches-6.12/720-add-rtl-phy.patch
@@ -14,7 +14,7 @@ Submitted-by: Birger Koblitz <mail at birger-koblitz.de>
 
 --- a/drivers/net/phy/Kconfig
 +++ b/drivers/net/phy/Kconfig
-@@ -422,6 +422,12 @@ source "drivers/net/phy/realtek/Kconfig"
+@@ -434,6 +434,12 @@ source "drivers/net/phy/realtek/Kconfig"
  
  source "drivers/net/phy/rtl8261n/Kconfig"
  
@@ -29,7 +29,7 @@ Submitted-by: Birger Koblitz <mail at birger-koblitz.de>
  	help
 --- a/drivers/net/phy/Makefile
 +++ b/drivers/net/phy/Makefile
-@@ -111,6 +111,7 @@ obj-y				+= qcom/
+@@ -112,6 +112,7 @@ obj-y				+= qcom/
  obj-$(CONFIG_QSEMI_PHY)		+= qsemi.o
  obj-$(CONFIG_REALTEK_PHY)	+= realtek/
  obj-y				+= rtl8261n/
diff --git a/target/linux/siflower/patches-6.6/001-net-phy-c45-add-genphy_c45_pma_read_ext_abilities-fu.patch b/target/linux/siflower/patches-6.6/001-net-phy-c45-add-genphy_c45_pma_read_ext_abilities-fu.patch
index 55532bb5e5..98a6f07de8 100644
--- a/target/linux/siflower/patches-6.6/001-net-phy-c45-add-genphy_c45_pma_read_ext_abilities-fu.patch
+++ b/target/linux/siflower/patches-6.6/001-net-phy-c45-add-genphy_c45_pma_read_ext_abilities-fu.patch
@@ -170,7 +170,7 @@ Signed-off-by: Jakub Kicinski <kuba at kernel.org>
  	/* This is optional functionality. If not supported, we may get an error
 --- a/include/linux/phy.h
 +++ b/include/linux/phy.h
-@@ -1893,6 +1893,7 @@ int genphy_c45_an_config_aneg(struct phy
+@@ -1897,6 +1897,7 @@ int genphy_c45_an_config_aneg(struct phy
  int genphy_c45_an_disable_aneg(struct phy_device *phydev);
  int genphy_c45_read_mdix(struct phy_device *phydev);
  int genphy_c45_pma_read_abilities(struct phy_device *phydev);
diff --git a/target/linux/siflower/patches-6.6/019-net-phy-add-support-for-Siflower-SF23P1211-SF23P1240.patch b/target/linux/siflower/patches-6.6/019-net-phy-add-support-for-Siflower-SF23P1211-SF23P1240.patch
index 4113d82326..745d2e8fb5 100644
--- a/target/linux/siflower/patches-6.6/019-net-phy-add-support-for-Siflower-SF23P1211-SF23P1240.patch
+++ b/target/linux/siflower/patches-6.6/019-net-phy-add-support-for-Siflower-SF23P1211-SF23P1240.patch
@@ -11,7 +11,7 @@ Signed-off-by: haoming.chen <haoming.chen at siflower.com.cn>
 
 --- a/drivers/net/phy/Kconfig
 +++ b/drivers/net/phy/Kconfig
-@@ -484,3 +484,8 @@ endif # PHYLIB
+@@ -481,3 +481,8 @@ endif # PHYLIB
  config MICREL_KS8995MA
  	tristate "Micrel KS8995MA 5-ports 10/100 managed Ethernet switch"
  	depends on SPI




More information about the lede-commits mailing list