From 927fe598dbdf635a66a50bbb8643e282811896c2 Mon Sep 17 00:00:00 2001 From: Christian Marangi Date: Fri, 24 Oct 2025 11:51:40 +0200 Subject: [PATCH] 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 --- ...-add-PHY_DETACH_NO_HW_RESET-PHY-flag.patch | 129 +++++++++++++++ ...1xxx-add-flag-PHY_DETACH_NO_HW_RESET.patch | 108 ++++++++++++ ...handle-corner-case-with-link-and-aut.patch | 31 ++++ ...21xxx-fix-read_status-speed-handling.patch | 156 ++++++++++++++++++ ...hy-as21xxx-force-C45-OPs-for-AUTONEG.patch | 34 ++++ ...implement-read-workaround-for-C45-re.patch | 132 +++++++++++++++ 6 files changed, 590 insertions(+) create mode 100644 target/linux/airoha/patches-6.12/801-01-net-phy-add-PHY_DETACH_NO_HW_RESET-PHY-flag.patch create mode 100644 target/linux/airoha/patches-6.12/801-02-net-phy-as21xxx-add-flag-PHY_DETACH_NO_HW_RESET.patch create mode 100644 target/linux/airoha/patches-6.12/802-01-net-phy-as21xxx-handle-corner-case-with-link-and-aut.patch create mode 100644 target/linux/airoha/patches-6.12/802-02-net-phy-as21xxx-fix-read_status-speed-handling.patch create mode 100644 target/linux/airoha/patches-6.12/802-03-net-phy-as21xxx-force-C45-OPs-for-AUTONEG.patch create mode 100644 target/linux/airoha/patches-6.12/804-net-phy-as21xxx-implement-read-workaround-for-C45-re.patch 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 +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 +--- + 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 +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 +--- + 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 +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 +--- + 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 +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 +--- + 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 +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 +--- + 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 +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 +--- + 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, -- 2.30.2