[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