[openwrt/openwrt] airoha: add pending patch to fix Aeonsemi AS21xxx PHY

LEDE Commits lede-commits at lists.infradead.org
Fri Oct 24 03:17:59 PDT 2025


ansuel pushed a commit to openwrt/openwrt.git, branch main:
https://git.openwrt.org/927fe598dbdf635a66a50bbb8643e282811896c2

commit 927fe598dbdf635a66a50bbb8643e282811896c2
Author: Christian Marangi <ansuelsmth at gmail.com>
AuthorDate: Fri Oct 24 11:51:40 2025 +0200

    airoha: add pending patch to fix Aeonsemi AS21xxx PHY
    
    Add pending patch to make address some workaround needed to make the
    Aeonsemi AS21xxx PHY working on the Airoha AN7581/AN7583 board.
    
    Signed-off-by: Christian Marangi <ansuelsmth at gmail.com>
---
 ...t-phy-add-PHY_DETACH_NO_HW_RESET-PHY-flag.patch | 129 +++++++++++++++++
 ...y-as21xxx-add-flag-PHY_DETACH_NO_HW_RESET.patch | 108 ++++++++++++++
 ...1xxx-handle-corner-case-with-link-and-aut.patch |  31 ++++
 ...hy-as21xxx-fix-read_status-speed-handling.patch | 156 +++++++++++++++++++++
 ...net-phy-as21xxx-force-C45-OPs-for-AUTONEG.patch |  34 +++++
 ...1xxx-implement-read-workaround-for-C45-re.patch | 132 +++++++++++++++++
 6 files changed, 590 insertions(+)

diff --git a/target/linux/airoha/patches-6.12/801-01-net-phy-add-PHY_DETACH_NO_HW_RESET-PHY-flag.patch b/target/linux/airoha/patches-6.12/801-01-net-phy-add-PHY_DETACH_NO_HW_RESET-PHY-flag.patch
new file mode 100644
index 0000000000..8dc48fb99a
--- /dev/null
+++ b/target/linux/airoha/patches-6.12/801-01-net-phy-add-PHY_DETACH_NO_HW_RESET-PHY-flag.patch
@@ -0,0 +1,129 @@
+From f2c6f8711c3866caafee997cfa60af4f38879be0 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth at gmail.com>
+Date: Wed, 25 Jun 2025 00:45:11 +0200
+Subject: [PATCH 1/2] net: phy: add PHY_DETACH_NO_HW_RESET PHY flag
+
+Some PHY require a firmware to correctly work and such firmware might
+get reset when the GPIO reset is assert.
+
+This is the case for the Aeonsemi PHY where when the PHY is torn down,
+phy_detach() is called that assert the GPIO reset pin resetting the
+firmware.
+
+To handle this introduce a flag, PHY_DETACH_NO_HW_RESET that instruct
+phy_detach() to skip asserting the GPIO reset on detaching the PHY.
+
+The PHY is still reset in all other case where it's removed or the PHY
+fails to probe.
+
+Signed-off-by: Christian Marangi <ansuelsmth at gmail.com>
+---
+ drivers/net/phy/as21xxx.c    | 10 ++++++++++
+ drivers/net/phy/phy_device.c |  3 ++-
+ include/linux/phy.h          |  1 +
+ 3 files changed, 13 insertions(+), 1 deletion(-)
+
+--- a/drivers/net/phy/as21xxx.c
++++ b/drivers/net/phy/as21xxx.c
+@@ -964,6 +964,7 @@ static struct phy_driver as21xxx_drivers
+ 		.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,
++		.flags = PHY_DETACH_NO_RESET,
+ 	},
+ 	{
+ 		PHY_ID_MATCH_EXACT(PHY_ID_AS21011PB1),
+@@ -976,6 +977,7 @@ static struct phy_driver as21xxx_drivers
+ 		.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,
++		.flags = PHY_DETACH_NO_RESET,
+ 	},
+ 	{
+ 		PHY_ID_MATCH_EXACT(PHY_ID_AS21010PB1),
+@@ -988,6 +990,7 @@ static struct phy_driver as21xxx_drivers
+ 		.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,
++		.flags = PHY_DETACH_NO_RESET,
+ 	},
+ 	{
+ 		PHY_ID_MATCH_EXACT(PHY_ID_AS21010JB1),
+@@ -1000,6 +1003,7 @@ static struct phy_driver as21xxx_drivers
+ 		.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,
++		.flags = PHY_DETACH_NO_RESET,
+ 	},
+ 	{
+ 		PHY_ID_MATCH_EXACT(PHY_ID_AS21210PB1),
+@@ -1012,6 +1016,7 @@ static struct phy_driver as21xxx_drivers
+ 		.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,
++		.flags = PHY_DETACH_NO_RESET,
+ 	},
+ 	{
+ 		PHY_ID_MATCH_EXACT(PHY_ID_AS21510JB1),
+@@ -1024,6 +1029,7 @@ static struct phy_driver as21xxx_drivers
+ 		.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,
++		.flags = PHY_DETACH_NO_RESET,
+ 	},
+ 	{
+ 		PHY_ID_MATCH_EXACT(PHY_ID_AS21510PB1),
+@@ -1036,6 +1042,7 @@ static struct phy_driver as21xxx_drivers
+ 		.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,
++		.flags = PHY_DETACH_NO_RESET,
+ 	},
+ 	{
+ 		PHY_ID_MATCH_EXACT(PHY_ID_AS21511JB1),
+@@ -1048,6 +1055,7 @@ static struct phy_driver as21xxx_drivers
+ 		.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,
++		.flags = PHY_DETACH_NO_RESET,
+ 	},
+ 	{
+ 		PHY_ID_MATCH_EXACT(PHY_ID_AS21210JB1),
+@@ -1060,6 +1068,7 @@ static struct phy_driver as21xxx_drivers
+ 		.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,
++		.flags = PHY_DETACH_NO_RESET,
+ 	},
+ 	{
+ 		PHY_ID_MATCH_EXACT(PHY_ID_AS21511PB1),
+@@ -1072,6 +1081,7 @@ static struct phy_driver as21xxx_drivers
+ 		.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,
++		.flags = PHY_DETACH_NO_RESET,
+ 	},
+ };
+ module_phy_driver(as21xxx_drivers);
+--- a/drivers/net/phy/phy_device.c
++++ b/drivers/net/phy/phy_device.c
+@@ -2074,7 +2074,8 @@ void phy_detach(struct phy_device *phyde
+ 		device_release_driver(&phydev->mdio.dev);
+ 
+ 	/* Assert the reset signal */
+-	phy_device_reset(phydev, 1);
++	if (!(phydev->drv->flags & PHY_DETACH_NO_HW_RESET))
++		phy_device_reset(phydev, 1);
+ 
+ 	/*
+ 	 * The phydev might go away on the put_device() below, so avoid
+--- a/include/linux/phy.h
++++ b/include/linux/phy.h
+@@ -90,6 +90,7 @@ extern const int phy_10gbit_features_arr
+ #define PHY_RST_AFTER_CLK_EN	0x00000002
+ #define PHY_POLL_CABLE_TEST	0x00000004
+ #define PHY_ALWAYS_CALL_SUSPEND	0x00000008
++#define PHY_DETACH_NO_HW_RESET	0x00000010
+ #define MDIO_DEVICE_IS_PHY	0x80000000
+ 
+ /**
diff --git a/target/linux/airoha/patches-6.12/801-02-net-phy-as21xxx-add-flag-PHY_DETACH_NO_HW_RESET.patch b/target/linux/airoha/patches-6.12/801-02-net-phy-as21xxx-add-flag-PHY_DETACH_NO_HW_RESET.patch
new file mode 100644
index 0000000000..f2078310d2
--- /dev/null
+++ b/target/linux/airoha/patches-6.12/801-02-net-phy-as21xxx-add-flag-PHY_DETACH_NO_HW_RESET.patch
@@ -0,0 +1,108 @@
+From 7ad1470c3d08c1abea747aa0c789e924f63fcbc4 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth at gmail.com>
+Date: Wed, 25 Jun 2025 00:51:45 +0200
+Subject: [PATCH 2/2] net: phy: as21xxx: add flag PHY_DETACH_NO_HW_RESET
+
+Add flag PHY_DETACH_NO_HW_RESET to handle firmware getting reset on
+calling phy_detach() if the GPIO reset PIN is defined in DT.
+
+This will skip the firmware from getting reset permitting the PHY to
+continue work when the PHY is torn down and gets up again.
+
+Signed-off-by: Christian Marangi <ansuelsmth at gmail.com>
+---
+ drivers/net/phy/as21xxx.c | 20 ++++++++++----------
+ 1 file changed, 10 insertions(+), 10 deletions(-)
+
+--- a/drivers/net/phy/as21xxx.c
++++ b/drivers/net/phy/as21xxx.c
+@@ -964,7 +964,7 @@ static struct phy_driver as21xxx_drivers
+ 		.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,
+-		.flags = PHY_DETACH_NO_RESET,
++		.flags = PHY_DETACH_NO_HW_RESET,
+ 	},
+ 	{
+ 		PHY_ID_MATCH_EXACT(PHY_ID_AS21011PB1),
+@@ -977,7 +977,7 @@ static struct phy_driver as21xxx_drivers
+ 		.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,
+-		.flags = PHY_DETACH_NO_RESET,
++		.flags = PHY_DETACH_NO_HW_RESET,
+ 	},
+ 	{
+ 		PHY_ID_MATCH_EXACT(PHY_ID_AS21010PB1),
+@@ -990,7 +990,7 @@ static struct phy_driver as21xxx_drivers
+ 		.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,
+-		.flags = PHY_DETACH_NO_RESET,
++		.flags = PHY_DETACH_NO_HW_RESET,
+ 	},
+ 	{
+ 		PHY_ID_MATCH_EXACT(PHY_ID_AS21010JB1),
+@@ -1003,7 +1003,7 @@ static struct phy_driver as21xxx_drivers
+ 		.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,
+-		.flags = PHY_DETACH_NO_RESET,
++		.flags = PHY_DETACH_NO_HW_RESET,
+ 	},
+ 	{
+ 		PHY_ID_MATCH_EXACT(PHY_ID_AS21210PB1),
+@@ -1016,7 +1016,7 @@ static struct phy_driver as21xxx_drivers
+ 		.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,
+-		.flags = PHY_DETACH_NO_RESET,
++		.flags = PHY_DETACH_NO_HW_RESET,
+ 	},
+ 	{
+ 		PHY_ID_MATCH_EXACT(PHY_ID_AS21510JB1),
+@@ -1029,7 +1029,7 @@ static struct phy_driver as21xxx_drivers
+ 		.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,
+-		.flags = PHY_DETACH_NO_RESET,
++		.flags = PHY_DETACH_NO_HW_RESET,
+ 	},
+ 	{
+ 		PHY_ID_MATCH_EXACT(PHY_ID_AS21510PB1),
+@@ -1042,7 +1042,7 @@ static struct phy_driver as21xxx_drivers
+ 		.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,
+-		.flags = PHY_DETACH_NO_RESET,
++		.flags = PHY_DETACH_NO_HW_RESET,
+ 	},
+ 	{
+ 		PHY_ID_MATCH_EXACT(PHY_ID_AS21511JB1),
+@@ -1055,7 +1055,7 @@ static struct phy_driver as21xxx_drivers
+ 		.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,
+-		.flags = PHY_DETACH_NO_RESET,
++		.flags = PHY_DETACH_NO_HW_RESET,
+ 	},
+ 	{
+ 		PHY_ID_MATCH_EXACT(PHY_ID_AS21210JB1),
+@@ -1068,7 +1068,7 @@ static struct phy_driver as21xxx_drivers
+ 		.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,
+-		.flags = PHY_DETACH_NO_RESET,
++		.flags = PHY_DETACH_NO_HW_RESET,
+ 	},
+ 	{
+ 		PHY_ID_MATCH_EXACT(PHY_ID_AS21511PB1),
+@@ -1081,7 +1081,7 @@ static struct phy_driver as21xxx_drivers
+ 		.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,
+-		.flags = PHY_DETACH_NO_RESET,
++		.flags = PHY_DETACH_NO_HW_RESET,
+ 	},
+ };
+ module_phy_driver(as21xxx_drivers);
diff --git a/target/linux/airoha/patches-6.12/802-01-net-phy-as21xxx-handle-corner-case-with-link-and-aut.patch b/target/linux/airoha/patches-6.12/802-01-net-phy-as21xxx-handle-corner-case-with-link-and-aut.patch
new file mode 100644
index 0000000000..6e599cdd3f
--- /dev/null
+++ b/target/linux/airoha/patches-6.12/802-01-net-phy-as21xxx-handle-corner-case-with-link-and-aut.patch
@@ -0,0 +1,31 @@
+From 0146a02d9d182796c3d8e4a432c4d94cac042f8e Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth at gmail.com>
+Date: Mon, 7 Jul 2025 18:58:25 +0200
+Subject: [PATCH 1/4] net: phy: as21xxx: handle corner case with link and
+ autoneg complete
+
+Add missing case in custom read_link, when autoneg is started, autoneg
+complete bit is reset but link is still not up.
+
+Fixes: 830877d89edc ("net: phy: Add support for Aeonsemi AS21xxx PHYs")
+Signed-off-by: Christian Marangi <ansuelsmth at gmail.com>
+---
+ drivers/net/phy/as21xxx.c | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+--- a/drivers/net/phy/as21xxx.c
++++ b/drivers/net/phy/as21xxx.c
+@@ -658,6 +658,13 @@ static int as21xxx_read_link(struct phy_
+ 		return status;
+ 
+ 	phydev->link = !!(status & MDIO_STAT1_LSTATUS);
++	phydev->autoneg_complete = !!(status & MDIO_AN_STAT1_COMPLETE);
++
++	/* Consider the case that autoneg was started and "aneg complete"
++	 * bit has been reset, but "link up" bit not yet.
++	 */
++	if (phydev->autoneg == AUTONEG_ENABLE && !phydev->autoneg_complete)
++		phydev->link = 0;
+ 
+ 	return 0;
+ }
diff --git a/target/linux/airoha/patches-6.12/802-02-net-phy-as21xxx-fix-read_status-speed-handling.patch b/target/linux/airoha/patches-6.12/802-02-net-phy-as21xxx-fix-read_status-speed-handling.patch
new file mode 100644
index 0000000000..4010c4093b
--- /dev/null
+++ b/target/linux/airoha/patches-6.12/802-02-net-phy-as21xxx-fix-read_status-speed-handling.patch
@@ -0,0 +1,156 @@
+From d90186b1e48dd4a428abf889b1eb17d2469de08b Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth at gmail.com>
+Date: Tue, 8 Jul 2025 10:50:42 +0200
+Subject: [PATCH 2/4] net: phy: as21xxx: fix read_status speed handling
+
+With further test with 2.5G NIC it was discovered that
+phy_resolve_aneg_linkmode is not enough to detect speed higher that 1G
+when autoneg is enabled.
+
+Also in the switch case there is a typo where the speed mask is AND with
+VEND1_SPEED_STATUS instead of the correct mask VEND1_SPEED_MASK.
+
+Rework the read_status code to always read the speed from the vendor
+register and parse the generic bit only for the pause frame.
+
+Fixes: 830877d89edc ("net: phy: Add support for Aeonsemi AS21xxx PHYs")
+Signed-off-by: Christian Marangi <ansuelsmth at gmail.com>
+---
+ drivers/net/phy/as21xxx.c | 96 +++++++++++++++++++++------------------
+ 1 file changed, 53 insertions(+), 43 deletions(-)
+
+--- a/drivers/net/phy/as21xxx.c
++++ b/drivers/net/phy/as21xxx.c
+@@ -671,7 +671,7 @@ static int as21xxx_read_link(struct phy_
+ 
+ static int as21xxx_read_c22_lpa(struct phy_device *phydev)
+ {
+-	int lpagb;
++	int lpagb, lpa;
+ 
+ 	/* MII_STAT1000 are only filled in the mapped C22
+ 	 * in C45, use that to fill lpagb values and check.
+@@ -698,12 +698,20 @@ static int as21xxx_read_c22_lpa(struct p
+ 	mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising,
+ 					lpagb);
+ 
++	lpa = phy_read_mmd(phydev, MDIO_MMD_AN,
++			   AS21XXX_MDIO_AN_C22 + MII_LPA);
++	if (lpa < 0)
++		return lpa;
++
++	mii_lpa_mod_linkmode_lpa_t(phydev->lp_advertising, lpa);
++
+ 	return 0;
+ }
+ 
+ static int as21xxx_read_status(struct phy_device *phydev)
+ {
+ 	int bmcr, old_link = phydev->link;
++	int speed;
+ 	int ret;
+ 
+ 	ret = as21xxx_read_link(phydev, &bmcr);
+@@ -720,58 +728,60 @@ static int as21xxx_read_status(struct ph
+ 	phydev->asym_pause = 0;
+ 
+ 	if (phydev->autoneg == AUTONEG_ENABLE) {
+-		ret = genphy_c45_read_lpa(phydev);
+-		if (ret)
+-			return ret;
++		if (!phydev->autoneg_complete) {
++			mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising,
++							0);
++			mii_lpa_mod_linkmode_lpa_t(phydev->lp_advertising, 0);
++			return 0;
++		}
+ 
+ 		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;
++	speed = phy_read_mmd(phydev, MDIO_MMD_VEND1,
++			     VEND1_SPEED_STATUS);
++	if (speed < 0)
++		return speed;
++
++	switch (speed & VEND1_SPEED_MASK) {
++	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;
+-			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;
+-		}
++		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;
+ 	}
+ 
++	if (phydev->autoneg == AUTONEG_ENABLE)
++		phy_resolve_aneg_pause(phydev);
++
+ 	return 0;
+ }
+ 
diff --git a/target/linux/airoha/patches-6.12/802-03-net-phy-as21xxx-force-C45-OPs-for-AUTONEG.patch b/target/linux/airoha/patches-6.12/802-03-net-phy-as21xxx-force-C45-OPs-for-AUTONEG.patch
new file mode 100644
index 0000000000..19118db786
--- /dev/null
+++ b/target/linux/airoha/patches-6.12/802-03-net-phy-as21xxx-force-C45-OPs-for-AUTONEG.patch
@@ -0,0 +1,34 @@
+From 6003da596beb6b8974e61b7ff494476a323fbef5 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth at gmail.com>
+Date: Tue, 8 Jul 2025 11:29:49 +0200
+Subject: [PATCH 3/4] net: phy: as21xxx: force C45 OPs for AUTONEG
+
+With further testing with 2.5G NIC, it was discovered that the PHY
+require the C45 OPs to configure and restart ANEG or speed higher than
+1G doesn't function correctly.
+
+To force C45 OPs with generic PHY function, clear the C22 bit from
+devices_in_package bitmask.
+
+Fixes: 830877d89edc ("net: phy: Add support for Aeonsemi AS21xxx PHYs")
+Signed-off-by: Christian Marangi <ansuelsmth at gmail.com>
+---
+ drivers/net/phy/as21xxx.c | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+--- a/drivers/net/phy/as21xxx.c
++++ b/drivers/net/phy/as21xxx.c
+@@ -616,6 +616,13 @@ static int as21xxx_probe(struct phy_devi
+ 	if (ret)
+ 		return ret;
+ 
++	/* Even if PHY declare support for Clause 22 register,
++	 * Clause 45 register should be used for ANEG configuration
++	 * and restart. Clear the C22 bit for devices_in_package to
++	 * force C45 generic OPs in generic PHY ANGE OPs.
++	 */
++	phydev->c45_ids.devices_in_package &= ~BIT(0);
++
+ 	ret = aeon_ipc_sync_parity(phydev, priv);
+ 	if (ret)
+ 		return ret;
diff --git a/target/linux/airoha/patches-6.12/804-net-phy-as21xxx-implement-read-workaround-for-C45-re.patch b/target/linux/airoha/patches-6.12/804-net-phy-as21xxx-implement-read-workaround-for-C45-re.patch
new file mode 100644
index 0000000000..24cddf8307
--- /dev/null
+++ b/target/linux/airoha/patches-6.12/804-net-phy-as21xxx-implement-read-workaround-for-C45-re.patch
@@ -0,0 +1,132 @@
+From fabaa8a7183d10217e14af437fd3805bd6dd9eba Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth at gmail.com>
+Date: Sat, 18 Oct 2025 04:12:41 +0200
+Subject: [PATCH] net: phy: as21xxx: implement read workaround for C45 read
+
+This PHY have lots of problems with MDIO read operation. We somehow
+workaround this with using C45 operation for pretty much everything but
+this is not enough. The reference code for this PHY makes a write to an
+unused PHY to workaround this read problem. This was also confirmed by
+Aeonsemi.
+
+Various test were made to try to workaround this ins alternative way
+than the random write.
+
+One effective solution was to limit the write only to BMSR. And also
+write to BMSR is safe since they are only read only registers.
+
+This is only done for read operation as write operation doesn't suffer
+from this problem.
+
+Worth to mention that when multiple Aeonsemi PHY are mounted, the
+workaround doesn't work if we write to another Aeonsemi PHY.
+
+Signed-off-by: Christian Marangi <ansuelsmth at gmail.com>
+---
+ drivers/net/phy/as21xxx.c | 25 +++++++++++++++++++++++++
+ 1 file changed, 25 insertions(+)
+
+--- a/drivers/net/phy/as21xxx.c
++++ b/drivers/net/phy/as21xxx.c
+@@ -966,6 +966,21 @@ out:
+ 	return ret;
+ }
+ 
++static int as21xxx_read_mmd(struct phy_device *phydev, int devad,
++			    u16 regnum)
++{
++	struct mii_bus *bus = phydev->mdio.bus;
++	int val;
++
++	val = __mdiobus_c45_read(bus, phydev->mdio.addr, devad,
++				 regnum);
++
++	/* FIXME: verify if it's actually ok to limit this to MII_BMSR */
++	__mdiobus_write(bus, 0x0, MII_BMSR, 0x1);
++
++	return val;
++}
++
+ static struct phy_driver as21xxx_drivers[] = {
+ 	{
+ 		/* PHY expose in C45 as 0x7500 0x9410
+@@ -983,6 +998,7 @@ static struct phy_driver as21xxx_drivers
+ 		.probe		= as21xxx_probe,
+ 		.match_phy_device = as21xxx_match_phy_device,
+ 		.read_status	= as21xxx_read_status,
++		.read_mmd	= as21xxx_read_mmd,
+ 		.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,
+@@ -996,6 +1012,7 @@ static struct phy_driver as21xxx_drivers
+ 		.probe		= as21xxx_probe,
+ 		.match_phy_device = as21xxx_match_phy_device,
+ 		.read_status	= as21xxx_read_status,
++		.read_mmd	= as21xxx_read_mmd,
+ 		.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,
+@@ -1009,6 +1026,7 @@ static struct phy_driver as21xxx_drivers
+ 		.probe		= as21xxx_probe,
+ 		.match_phy_device = as21xxx_match_phy_device,
+ 		.read_status	= as21xxx_read_status,
++		.read_mmd	= as21xxx_read_mmd,
+ 		.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,
+@@ -1022,6 +1040,7 @@ static struct phy_driver as21xxx_drivers
+ 		.probe		= as21xxx_probe,
+ 		.match_phy_device = as21xxx_match_phy_device,
+ 		.read_status	= as21xxx_read_status,
++		.read_mmd	= as21xxx_read_mmd,
+ 		.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,
+@@ -1035,6 +1054,7 @@ static struct phy_driver as21xxx_drivers
+ 		.probe		= as21xxx_probe,
+ 		.match_phy_device = as21xxx_match_phy_device,
+ 		.read_status	= as21xxx_read_status,
++		.read_mmd	= as21xxx_read_mmd,
+ 		.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,
+@@ -1048,6 +1068,7 @@ static struct phy_driver as21xxx_drivers
+ 		.probe		= as21xxx_probe,
+ 		.match_phy_device = as21xxx_match_phy_device,
+ 		.read_status	= as21xxx_read_status,
++		.read_mmd	= as21xxx_read_mmd,
+ 		.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,
+@@ -1061,6 +1082,7 @@ static struct phy_driver as21xxx_drivers
+ 		.probe		= as21xxx_probe,
+ 		.match_phy_device = as21xxx_match_phy_device,
+ 		.read_status	= as21xxx_read_status,
++		.read_mmd	= as21xxx_read_mmd,
+ 		.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,
+@@ -1074,6 +1096,7 @@ static struct phy_driver as21xxx_drivers
+ 		.probe		= as21xxx_probe,
+ 		.match_phy_device = as21xxx_match_phy_device,
+ 		.read_status	= as21xxx_read_status,
++		.read_mmd	= as21xxx_read_mmd,
+ 		.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,
+@@ -1087,6 +1110,7 @@ static struct phy_driver as21xxx_drivers
+ 		.probe		= as21xxx_probe,
+ 		.match_phy_device = as21xxx_match_phy_device,
+ 		.read_status	= as21xxx_read_status,
++		.read_mmd	= as21xxx_read_mmd,
+ 		.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,
+@@ -1100,6 +1124,7 @@ static struct phy_driver as21xxx_drivers
+ 		.probe		= as21xxx_probe,
+ 		.match_phy_device = as21xxx_match_phy_device,
+ 		.read_status	= as21xxx_read_status,
++		.read_mmd	= as21xxx_read_mmd,
+ 		.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,




More information about the lede-commits mailing list