[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