airoha: add pending patch to fix Aeonsemi AS21xxx PHY
authorChristian Marangi <[email protected]>
Fri, 24 Oct 2025 09:51:40 +0000 (11:51 +0200)
committerChristian Marangi <[email protected]>
Fri, 24 Oct 2025 10:17:45 +0000 (12:17 +0200)
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 <[email protected]>
target/linux/airoha/patches-6.12/801-01-net-phy-add-PHY_DETACH_NO_HW_RESET-PHY-flag.patch [new file with mode: 0644]
target/linux/airoha/patches-6.12/801-02-net-phy-as21xxx-add-flag-PHY_DETACH_NO_HW_RESET.patch [new file with mode: 0644]
target/linux/airoha/patches-6.12/802-01-net-phy-as21xxx-handle-corner-case-with-link-and-aut.patch [new file with mode: 0644]
target/linux/airoha/patches-6.12/802-02-net-phy-as21xxx-fix-read_status-speed-handling.patch [new file with mode: 0644]
target/linux/airoha/patches-6.12/802-03-net-phy-as21xxx-force-C45-OPs-for-AUTONEG.patch [new file with mode: 0644]
target/linux/airoha/patches-6.12/804-net-phy-as21xxx-implement-read-workaround-for-C45-re.patch [new file with mode: 0644]

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 (file)
index 0000000..8dc48fb
--- /dev/null
@@ -0,0 +1,129 @@
+From f2c6f8711c3866caafee997cfa60af4f38879be0 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <[email protected]>
+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 <[email protected]>
+---
+ 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 (file)
index 0000000..f207831
--- /dev/null
@@ -0,0 +1,108 @@
+From 7ad1470c3d08c1abea747aa0c789e924f63fcbc4 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <[email protected]>
+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 <[email protected]>
+---
+ 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 (file)
index 0000000..6e599cd
--- /dev/null
@@ -0,0 +1,31 @@
+From 0146a02d9d182796c3d8e4a432c4d94cac042f8e Mon Sep 17 00:00:00 2001
+From: Christian Marangi <[email protected]>
+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 <[email protected]>
+---
+ 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 (file)
index 0000000..4010c40
--- /dev/null
@@ -0,0 +1,156 @@
+From d90186b1e48dd4a428abf889b1eb17d2469de08b Mon Sep 17 00:00:00 2001
+From: Christian Marangi <[email protected]>
+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 <[email protected]>
+---
+ 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 (file)
index 0000000..19118db
--- /dev/null
@@ -0,0 +1,34 @@
+From 6003da596beb6b8974e61b7ff494476a323fbef5 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <[email protected]>
+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 <[email protected]>
+---
+ 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 (file)
index 0000000..24cddf8
--- /dev/null
@@ -0,0 +1,132 @@
+From fabaa8a7183d10217e14af437fd3805bd6dd9eba Mon Sep 17 00:00:00 2001
+From: Christian Marangi <[email protected]>
+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 <[email protected]>
+---
+ 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,