From 7cd3597b8f6fcad8c62d04a20f9da46d3f37b36e Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Thu, 21 Aug 2025 18:20:08 +0300 Subject: [PATCH 01/15] net: phy: aquantia: rename AQR412 to AQR412C and add real AQR412 I have noticed from schematics and firmware images that the PHY for which I've previously added support in commit 973fbe68df39 ("net: phy: aquantia: add AQR112 and AQR412 PHY IDs") is actually an AQR412C, not AQR412. These are actually PHYs from the same generation, and Marvell documents them as differing only in the size of the FCCSP package: 19x19 mm for the AQR412, vs 14x12mm for the Compact AQR412C. I don't think there is any point in backporting this to stable kernels, since the PHYs are identical in capabilities, and no functional difference is expected regardless of how the PHY is identified. Signed-off-by: Vladimir Oltean Reviewed-by: Andrew Lunn Link: https://patch.msgid.link/20250821152022.1065237-2-vladimir.oltean@nxp.com Signed-off-by: Jakub Kicinski --- drivers/net/phy/aquantia/aquantia_main.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/drivers/net/phy/aquantia/aquantia_main.c b/drivers/net/phy/aquantia/aquantia_main.c index 77a48635d7bf..52facd318c83 100644 --- a/drivers/net/phy/aquantia/aquantia_main.c +++ b/drivers/net/phy/aquantia/aquantia_main.c @@ -26,7 +26,8 @@ #define PHY_ID_AQR111 0x03a1b610 #define PHY_ID_AQR111B0 0x03a1b612 #define PHY_ID_AQR112 0x03a1b662 -#define PHY_ID_AQR412 0x03a1b712 +#define PHY_ID_AQR412 0x03a1b6f2 +#define PHY_ID_AQR412C 0x03a1b712 #define PHY_ID_AQR113 0x31c31c40 #define PHY_ID_AQR113C 0x31c31c12 #define PHY_ID_AQR114C 0x31c31c22 @@ -1308,6 +1309,24 @@ static struct phy_driver aqr_driver[] = { .get_stats = aqr107_get_stats, .link_change_notify = aqr107_link_change_notify, }, +{ + PHY_ID_MATCH_MODEL(PHY_ID_AQR412C), + .name = "Aquantia AQR412C", + .probe = aqr107_probe, + .config_aneg = aqr_config_aneg, + .config_intr = aqr_config_intr, + .handle_interrupt = aqr_handle_interrupt, + .get_tunable = aqr107_get_tunable, + .set_tunable = aqr107_set_tunable, + .suspend = aqr107_suspend, + .resume = aqr107_resume, + .read_status = aqr107_read_status, + .get_rate_matching = aqr107_get_rate_matching, + .get_sset_count = aqr107_get_sset_count, + .get_strings = aqr107_get_strings, + .get_stats = aqr107_get_stats, + .link_change_notify = aqr107_link_change_notify, +}, { PHY_ID_MATCH_MODEL(PHY_ID_AQR113), .name = "Aquantia AQR113", @@ -1446,6 +1465,7 @@ static const struct mdio_device_id __maybe_unused aqr_tbl[] = { { PHY_ID_MATCH_MODEL(PHY_ID_AQR111B0) }, { PHY_ID_MATCH_MODEL(PHY_ID_AQR112) }, { PHY_ID_MATCH_MODEL(PHY_ID_AQR412) }, + { PHY_ID_MATCH_MODEL(PHY_ID_AQR412C) }, { PHY_ID_MATCH_MODEL(PHY_ID_AQR113) }, { PHY_ID_MATCH_MODEL(PHY_ID_AQR113C) }, { PHY_ID_MATCH_MODEL(PHY_ID_AQR114C) }, From a31b1c1591e8296060a0a2ad69b1f936f953cd96 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Thu, 21 Aug 2025 18:20:09 +0300 Subject: [PATCH 02/15] net: phy: aquantia: merge aqr113c_fill_interface_modes() into aqr107_fill_interface_modes() I'm unsure whether intentionate or not, but I think the (partially observed) naming convention in this driver is that function prefixes denote the earliest generation when a feature is available. In case of aqr107_fill_interface_modes(), that means that the GLOBAL_CFG registers are a Gen2 feature. Supporting evidence: the AQR105, a Gen1 PHY, does not have these registers, thus the function is not named aqr105_*. Based on this inferred naming scheme, I am proposing a refinement of commit a7f3abcf6357 ("net: phy: aquantia: only poll GLOBAL_CFG regs on aqr113, aqr113c and aqr115c") which introduced aqr113c_fill_interface_modes(), suggesting this may be a Gen4 PHY feature. The long-term goal is for aqr107_config_init() to tail-call aqr107_fill_interface_modes(), such that the latter function is also called by AQR107 itself, and many other PHY drivers. Currently it can't, because aqr113c_config_init() calls aqr107_config_init() and then aqr113c_fill_interface_modes(). So this would lead to a duplicate call to aqr107_fill_interface_modes() for AQR113C. Centralize the reading of GLOBAL_CFG registers in the AQR107 method, and create a boolean, set to true by AQR113C, which tests whether waiting for a non-zero value in the GLOBAL_CFG_100M register is necessary. Signed-off-by: Vladimir Oltean Reviewed-by: Andrew Lunn Link: https://patch.msgid.link/20250821152022.1065237-3-vladimir.oltean@nxp.com Signed-off-by: Jakub Kicinski --- drivers/net/phy/aquantia/aquantia.h | 1 + drivers/net/phy/aquantia/aquantia_main.c | 41 ++++++++++++------------ 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/drivers/net/phy/aquantia/aquantia.h b/drivers/net/phy/aquantia/aquantia.h index 0c78bfabace5..67ec6f7484af 100644 --- a/drivers/net/phy/aquantia/aquantia.h +++ b/drivers/net/phy/aquantia/aquantia.h @@ -178,6 +178,7 @@ struct aqr107_priv { u64 sgmii_stats[AQR107_SGMII_STAT_SZ]; unsigned long leds_active_low; unsigned long leds_active_high; + bool wait_on_global_cfg; }; #if IS_REACHABLE(CONFIG_HWMON) diff --git a/drivers/net/phy/aquantia/aquantia_main.c b/drivers/net/phy/aquantia/aquantia_main.c index 52facd318c83..b9b58c6ce686 100644 --- a/drivers/net/phy/aquantia/aquantia_main.c +++ b/drivers/net/phy/aquantia/aquantia_main.c @@ -991,9 +991,24 @@ static const u16 aqr_global_cfg_regs[] = { static int aqr107_fill_interface_modes(struct phy_device *phydev) { unsigned long *possible = phydev->possible_interfaces; + struct aqr107_priv *priv = phydev->priv; unsigned int serdes_mode, rate_adapt; phy_interface_t interface; - int i, val; + int i, val, ret; + + /* It's been observed on some models that - when coming out of suspend + * - the FW signals that the PHY is ready but the GLOBAL_CFG registers + * continue on returning zeroes for some time. Let's poll the 100M + * register until it returns a real value as both 113c and 115c support + * this mode. + */ + if (priv->wait_on_global_cfg) { + ret = phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1, + VEND1_GLOBAL_CFG_100M, val, + val != 0, 1000, 100000, false); + if (ret) + return ret; + } /* Walk the media-speed configuration registers to determine which * host-side serdes modes may be used by the PHY depending on the @@ -1042,25 +1057,6 @@ static int aqr107_fill_interface_modes(struct phy_device *phydev) return 0; } -static int aqr113c_fill_interface_modes(struct phy_device *phydev) -{ - int val, ret; - - /* It's been observed on some models that - when coming out of suspend - * - the FW signals that the PHY is ready but the GLOBAL_CFG registers - * continue on returning zeroes for some time. Let's poll the 100M - * register until it returns a real value as both 113c and 115c support - * this mode. - */ - ret = phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1, - VEND1_GLOBAL_CFG_100M, val, val != 0, - 1000, 100000, false); - if (ret) - return ret; - - return aqr107_fill_interface_modes(phydev); -} - static int aqr115c_get_features(struct phy_device *phydev) { unsigned long *supported = phydev->supported; @@ -1088,8 +1084,11 @@ static int aqr111_get_features(struct phy_device *phydev) static int aqr113c_config_init(struct phy_device *phydev) { + struct aqr107_priv *priv = phydev->priv; int ret; + priv->wait_on_global_cfg = true; + ret = aqr107_config_init(phydev); if (ret < 0) return ret; @@ -1103,7 +1102,7 @@ static int aqr113c_config_init(struct phy_device *phydev) if (ret) return ret; - return aqr113c_fill_interface_modes(phydev); + return aqr107_fill_interface_modes(phydev); } static int aqr107_probe(struct phy_device *phydev) From 5433fbc3adcd2b27aadbf76dd35520ff22cdc356 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Thu, 21 Aug 2025 18:20:10 +0300 Subject: [PATCH 03/15] net: phy: aquantia: reorder AQR113C PMD Global Transmit Disable bit clearing with supported_interfaces Introduced in commit bed90b06b681 ("net: phy: aquantia: clear PMD Global Transmit Disable bit during init"), the clearing of MDIO_PMA_TXDIS plus the call to aqr107_wait_processor_intensive_op() are only by chance placed between aqr107_config_init() and aqr107_fill_interface_modes(). In other words, aqr107_fill_interface_modes() does not depend in any way on these 2 operations. I am only 90% sure of that, and I intend to move aqr107_fill_interface_modes() to be a part of aqr107_config_init() in the future. So to isolate the issue for blame attribution purposes, make these 2 functions adjacent to each other again. Signed-off-by: Vladimir Oltean Reviewed-by: Andrew Lunn Link: https://patch.msgid.link/20250821152022.1065237-4-vladimir.oltean@nxp.com Signed-off-by: Jakub Kicinski --- drivers/net/phy/aquantia/aquantia_main.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/phy/aquantia/aquantia_main.c b/drivers/net/phy/aquantia/aquantia_main.c index b9b58c6ce686..7ac0b685a317 100644 --- a/drivers/net/phy/aquantia/aquantia_main.c +++ b/drivers/net/phy/aquantia/aquantia_main.c @@ -1093,16 +1093,16 @@ static int aqr113c_config_init(struct phy_device *phydev) if (ret < 0) return ret; + ret = aqr107_fill_interface_modes(phydev); + if (ret) + return ret; + ret = phy_clear_bits_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_TXDIS, MDIO_PMD_TXDIS_GLOBAL); if (ret) return ret; - ret = aqr107_wait_processor_intensive_op(phydev); - if (ret) - return ret; - - return aqr107_fill_interface_modes(phydev); + return aqr107_wait_processor_intensive_op(phydev); } static int aqr107_probe(struct phy_device *phydev) From 9731bcf202e653e63a4bcae2a6e82d3c3528e438 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Thu, 21 Aug 2025 18:20:11 +0300 Subject: [PATCH 04/15] net: phy: aquantia: rename some aqr107 functions according to generation Establish a more intuitive function naming convention in this driver. A GenX PHY must only call aqr_genY_ functions, where Y <= X. Loosely speaking, aqr107_ is representative of Gen2 and above, except for: - aqr107_config_init() - aqr107_suspend() - aqr107_resume() - aqr107_wait_processor_intensive_op() which are also called by AQR105, so these are renamed to Gen1. Actually aqr107_config_init() is renamed to aqr_gen1_config_init() when called by AQR105, and aqr_gen2_config_init() when called by all other PHYs. The Gen2 function calls the Gen1 function, so there is no functional change. This prefaces further Gen2-specific initialization steps which must be omitted for AQR105. These will be added to aqr_gen2_config_init(). In fact, many PHY drivers call an aqr*_config_init() beneath their generation's feature set: AQR114C is a Gen4 PHY which calls aqr_gen2_config_init(), even though AQR113C, also a Gen4 PHY which differs only in maximum link speed, calls the richer aqr113c_config_init() which also sets phydev->possible_interfaces. Many of the more subtle inconsistencies of this kind will be fixed up in later changes. Signed-off-by: Vladimir Oltean Link: https://patch.msgid.link/20250821152022.1065237-5-vladimir.oltean@nxp.com Signed-off-by: Jakub Kicinski --- drivers/net/phy/aquantia/aquantia_main.c | 117 ++++++++++++----------- 1 file changed, 61 insertions(+), 56 deletions(-) diff --git a/drivers/net/phy/aquantia/aquantia_main.c b/drivers/net/phy/aquantia/aquantia_main.c index 7ac0b685a317..8136f7843a37 100644 --- a/drivers/net/phy/aquantia/aquantia_main.c +++ b/drivers/net/phy/aquantia/aquantia_main.c @@ -811,7 +811,7 @@ static int aqr107_config_mdi(struct phy_device *phydev) mdi_conf | PMAPMD_RSVD_VEND_PROV_MDI_FORCE); } -static int aqr107_config_init(struct phy_device *phydev) +static int aqr_gen1_config_init(struct phy_device *phydev) { struct aqr107_priv *priv = phydev->priv; u32 led_idx; @@ -860,6 +860,11 @@ static int aqr107_config_init(struct phy_device *phydev) return 0; } +static int aqr_gen2_config_init(struct phy_device *phydev) +{ + return aqr_gen1_config_init(phydev); +} + static int aqcs109_config_init(struct phy_device *phydev) { int ret; @@ -921,7 +926,7 @@ static void aqr107_link_change_notify(struct phy_device *phydev) phydev_info(phydev, "Aquantia 1000Base-T2 mode active\n"); } -static int aqr107_wait_processor_intensive_op(struct phy_device *phydev) +static int aqr_gen1_wait_processor_intensive_op(struct phy_device *phydev) { int val, err; @@ -945,8 +950,8 @@ static int aqr107_wait_processor_intensive_op(struct phy_device *phydev) return 0; } -static int aqr107_get_rate_matching(struct phy_device *phydev, - phy_interface_t iface) +static int aqr_gen2_get_rate_matching(struct phy_device *phydev, + phy_interface_t iface) { if (iface == PHY_INTERFACE_MODE_10GBASER || iface == PHY_INTERFACE_MODE_2500BASEX || @@ -955,7 +960,7 @@ static int aqr107_get_rate_matching(struct phy_device *phydev, return RATE_MATCH_NONE; } -static int aqr107_suspend(struct phy_device *phydev) +static int aqr_gen1_suspend(struct phy_device *phydev) { int err; @@ -964,10 +969,10 @@ static int aqr107_suspend(struct phy_device *phydev) if (err) return err; - return aqr107_wait_processor_intensive_op(phydev); + return aqr_gen1_wait_processor_intensive_op(phydev); } -static int aqr107_resume(struct phy_device *phydev) +static int aqr_gen1_resume(struct phy_device *phydev) { int err; @@ -976,7 +981,7 @@ static int aqr107_resume(struct phy_device *phydev) if (err) return err; - return aqr107_wait_processor_intensive_op(phydev); + return aqr_gen1_wait_processor_intensive_op(phydev); } static const u16 aqr_global_cfg_regs[] = { @@ -988,7 +993,7 @@ static const u16 aqr_global_cfg_regs[] = { VEND1_GLOBAL_CFG_10G }; -static int aqr107_fill_interface_modes(struct phy_device *phydev) +static int aqr_gen2_fill_interface_modes(struct phy_device *phydev) { unsigned long *possible = phydev->possible_interfaces; struct aqr107_priv *priv = phydev->priv; @@ -1089,11 +1094,11 @@ static int aqr113c_config_init(struct phy_device *phydev) priv->wait_on_global_cfg = true; - ret = aqr107_config_init(phydev); + ret = aqr_gen2_config_init(phydev); if (ret < 0) return ret; - ret = aqr107_fill_interface_modes(phydev); + ret = aqr_gen2_fill_interface_modes(phydev); if (ret) return ret; @@ -1102,7 +1107,7 @@ static int aqr113c_config_init(struct phy_device *phydev) if (ret) return ret; - return aqr107_wait_processor_intensive_op(phydev); + return aqr_gen1_wait_processor_intensive_op(phydev); } static int aqr107_probe(struct phy_device *phydev) @@ -1144,13 +1149,13 @@ static struct phy_driver aqr_driver[] = { .name = "Aquantia AQR105", .get_features = aqr105_get_features, .probe = aqr107_probe, - .config_init = aqr107_config_init, + .config_init = aqr_gen1_config_init, .config_aneg = aqr105_config_aneg, .config_intr = aqr_config_intr, .handle_interrupt = aqr_handle_interrupt, .read_status = aqr105_read_status, - .suspend = aqr107_suspend, - .resume = aqr107_resume, + .suspend = aqr_gen1_suspend, + .resume = aqr_gen1_resume, }, { PHY_ID_MATCH_MODEL(PHY_ID_AQR106), @@ -1164,16 +1169,16 @@ static struct phy_driver aqr_driver[] = { PHY_ID_MATCH_MODEL(PHY_ID_AQR107), .name = "Aquantia AQR107", .probe = aqr107_probe, - .get_rate_matching = aqr107_get_rate_matching, - .config_init = aqr107_config_init, + .get_rate_matching = aqr_gen2_get_rate_matching, + .config_init = aqr_gen2_config_init, .config_aneg = aqr_config_aneg, .config_intr = aqr_config_intr, .handle_interrupt = aqr_handle_interrupt, .read_status = aqr107_read_status, .get_tunable = aqr107_get_tunable, .set_tunable = aqr107_set_tunable, - .suspend = aqr107_suspend, - .resume = aqr107_resume, + .suspend = aqr_gen1_suspend, + .resume = aqr_gen1_resume, .get_sset_count = aqr107_get_sset_count, .get_strings = aqr107_get_strings, .get_stats = aqr107_get_stats, @@ -1188,7 +1193,7 @@ static struct phy_driver aqr_driver[] = { PHY_ID_MATCH_MODEL(PHY_ID_AQCS109), .name = "Aquantia AQCS109", .probe = aqr107_probe, - .get_rate_matching = aqr107_get_rate_matching, + .get_rate_matching = aqr_gen2_get_rate_matching, .config_init = aqcs109_config_init, .config_aneg = aqr_config_aneg, .config_intr = aqr_config_intr, @@ -1196,8 +1201,8 @@ static struct phy_driver aqr_driver[] = { .read_status = aqr107_read_status, .get_tunable = aqr107_get_tunable, .set_tunable = aqr107_set_tunable, - .suspend = aqr107_suspend, - .resume = aqr107_resume, + .suspend = aqr_gen1_suspend, + .resume = aqr_gen1_resume, .get_sset_count = aqr107_get_sset_count, .get_strings = aqr107_get_strings, .get_stats = aqr107_get_stats, @@ -1213,16 +1218,16 @@ static struct phy_driver aqr_driver[] = { PHY_ID_MATCH_MODEL(PHY_ID_AQR111), .name = "Aquantia AQR111", .probe = aqr107_probe, - .get_rate_matching = aqr107_get_rate_matching, - .config_init = aqr107_config_init, + .get_rate_matching = aqr_gen2_get_rate_matching, + .config_init = aqr_gen2_config_init, .config_aneg = aqr_config_aneg, .config_intr = aqr_config_intr, .handle_interrupt = aqr_handle_interrupt, .read_status = aqr107_read_status, .get_tunable = aqr107_get_tunable, .set_tunable = aqr107_set_tunable, - .suspend = aqr107_suspend, - .resume = aqr107_resume, + .suspend = aqr_gen1_suspend, + .resume = aqr_gen1_resume, .get_sset_count = aqr107_get_sset_count, .get_strings = aqr107_get_strings, .get_stats = aqr107_get_stats, @@ -1238,16 +1243,16 @@ static struct phy_driver aqr_driver[] = { PHY_ID_MATCH_MODEL(PHY_ID_AQR111B0), .name = "Aquantia AQR111B0", .probe = aqr107_probe, - .get_rate_matching = aqr107_get_rate_matching, - .config_init = aqr107_config_init, + .get_rate_matching = aqr_gen2_get_rate_matching, + .config_init = aqr_gen2_config_init, .config_aneg = aqr_config_aneg, .config_intr = aqr_config_intr, .handle_interrupt = aqr_handle_interrupt, .read_status = aqr107_read_status, .get_tunable = aqr107_get_tunable, .set_tunable = aqr107_set_tunable, - .suspend = aqr107_suspend, - .resume = aqr107_resume, + .suspend = aqr_gen1_suspend, + .resume = aqr_gen1_resume, .get_sset_count = aqr107_get_sset_count, .get_strings = aqr107_get_strings, .get_stats = aqr107_get_stats, @@ -1276,10 +1281,10 @@ static struct phy_driver aqr_driver[] = { .handle_interrupt = aqr_handle_interrupt, .get_tunable = aqr107_get_tunable, .set_tunable = aqr107_set_tunable, - .suspend = aqr107_suspend, - .resume = aqr107_resume, + .suspend = aqr_gen1_suspend, + .resume = aqr_gen1_resume, .read_status = aqr107_read_status, - .get_rate_matching = aqr107_get_rate_matching, + .get_rate_matching = aqr_gen2_get_rate_matching, .get_sset_count = aqr107_get_sset_count, .get_strings = aqr107_get_strings, .get_stats = aqr107_get_stats, @@ -1299,10 +1304,10 @@ static struct phy_driver aqr_driver[] = { .handle_interrupt = aqr_handle_interrupt, .get_tunable = aqr107_get_tunable, .set_tunable = aqr107_set_tunable, - .suspend = aqr107_suspend, - .resume = aqr107_resume, + .suspend = aqr_gen1_suspend, + .resume = aqr_gen1_resume, .read_status = aqr107_read_status, - .get_rate_matching = aqr107_get_rate_matching, + .get_rate_matching = aqr_gen2_get_rate_matching, .get_sset_count = aqr107_get_sset_count, .get_strings = aqr107_get_strings, .get_stats = aqr107_get_stats, @@ -1317,10 +1322,10 @@ static struct phy_driver aqr_driver[] = { .handle_interrupt = aqr_handle_interrupt, .get_tunable = aqr107_get_tunable, .set_tunable = aqr107_set_tunable, - .suspend = aqr107_suspend, - .resume = aqr107_resume, + .suspend = aqr_gen1_suspend, + .resume = aqr_gen1_resume, .read_status = aqr107_read_status, - .get_rate_matching = aqr107_get_rate_matching, + .get_rate_matching = aqr_gen2_get_rate_matching, .get_sset_count = aqr107_get_sset_count, .get_strings = aqr107_get_strings, .get_stats = aqr107_get_stats, @@ -1330,7 +1335,7 @@ static struct phy_driver aqr_driver[] = { PHY_ID_MATCH_MODEL(PHY_ID_AQR113), .name = "Aquantia AQR113", .probe = aqr107_probe, - .get_rate_matching = aqr107_get_rate_matching, + .get_rate_matching = aqr_gen2_get_rate_matching, .config_init = aqr113c_config_init, .config_aneg = aqr_config_aneg, .config_intr = aqr_config_intr, @@ -1338,8 +1343,8 @@ static struct phy_driver aqr_driver[] = { .read_status = aqr107_read_status, .get_tunable = aqr107_get_tunable, .set_tunable = aqr107_set_tunable, - .suspend = aqr107_suspend, - .resume = aqr107_resume, + .suspend = aqr_gen1_suspend, + .resume = aqr_gen1_resume, .get_sset_count = aqr107_get_sset_count, .get_strings = aqr107_get_strings, .get_stats = aqr107_get_stats, @@ -1354,7 +1359,7 @@ static struct phy_driver aqr_driver[] = { PHY_ID_MATCH_MODEL(PHY_ID_AQR113C), .name = "Aquantia AQR113C", .probe = aqr107_probe, - .get_rate_matching = aqr107_get_rate_matching, + .get_rate_matching = aqr_gen2_get_rate_matching, .config_init = aqr113c_config_init, .config_aneg = aqr_config_aneg, .config_intr = aqr_config_intr, @@ -1362,8 +1367,8 @@ static struct phy_driver aqr_driver[] = { .read_status = aqr107_read_status, .get_tunable = aqr107_get_tunable, .set_tunable = aqr107_set_tunable, - .suspend = aqr107_suspend, - .resume = aqr107_resume, + .suspend = aqr_gen1_suspend, + .resume = aqr_gen1_resume, .get_sset_count = aqr107_get_sset_count, .get_strings = aqr107_get_strings, .get_stats = aqr107_get_stats, @@ -1378,16 +1383,16 @@ static struct phy_driver aqr_driver[] = { PHY_ID_MATCH_MODEL(PHY_ID_AQR114C), .name = "Aquantia AQR114C", .probe = aqr107_probe, - .get_rate_matching = aqr107_get_rate_matching, - .config_init = aqr107_config_init, + .get_rate_matching = aqr_gen2_get_rate_matching, + .config_init = aqr_gen2_config_init, .config_aneg = aqr_config_aneg, .config_intr = aqr_config_intr, .handle_interrupt = aqr_handle_interrupt, .read_status = aqr107_read_status, .get_tunable = aqr107_get_tunable, .set_tunable = aqr107_set_tunable, - .suspend = aqr107_suspend, - .resume = aqr107_resume, + .suspend = aqr_gen1_suspend, + .resume = aqr_gen1_resume, .get_sset_count = aqr107_get_sset_count, .get_strings = aqr107_get_strings, .get_stats = aqr107_get_stats, @@ -1403,7 +1408,7 @@ static struct phy_driver aqr_driver[] = { PHY_ID_MATCH_MODEL(PHY_ID_AQR115C), .name = "Aquantia AQR115C", .probe = aqr107_probe, - .get_rate_matching = aqr107_get_rate_matching, + .get_rate_matching = aqr_gen2_get_rate_matching, .config_init = aqr113c_config_init, .config_aneg = aqr_config_aneg, .config_intr = aqr_config_intr, @@ -1411,8 +1416,8 @@ static struct phy_driver aqr_driver[] = { .read_status = aqr107_read_status, .get_tunable = aqr107_get_tunable, .set_tunable = aqr107_set_tunable, - .suspend = aqr107_suspend, - .resume = aqr107_resume, + .suspend = aqr_gen1_suspend, + .resume = aqr_gen1_resume, .get_sset_count = aqr107_get_sset_count, .get_strings = aqr107_get_strings, .get_stats = aqr107_get_stats, @@ -1428,16 +1433,16 @@ static struct phy_driver aqr_driver[] = { PHY_ID_MATCH_MODEL(PHY_ID_AQR813), .name = "Aquantia AQR813", .probe = aqr107_probe, - .get_rate_matching = aqr107_get_rate_matching, - .config_init = aqr107_config_init, + .get_rate_matching = aqr_gen2_get_rate_matching, + .config_init = aqr_gen2_config_init, .config_aneg = aqr_config_aneg, .config_intr = aqr_config_intr, .handle_interrupt = aqr_handle_interrupt, .read_status = aqr107_read_status, .get_tunable = aqr107_get_tunable, .set_tunable = aqr107_set_tunable, - .suspend = aqr107_suspend, - .resume = aqr107_resume, + .suspend = aqr_gen1_suspend, + .resume = aqr_gen1_resume, .get_sset_count = aqr107_get_sset_count, .get_strings = aqr107_get_strings, .get_stats = aqr107_get_stats, From ab1dfcb5bce1f32807eb7110ca7e98c2afb72041 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Thu, 21 Aug 2025 18:20:12 +0300 Subject: [PATCH 05/15] net: phy: aquantia: fill supported_interfaces for all aqr_gen2_config_init() callers Since aqr_gen2_config_init() and aqr_gen2_fill_interface_modes() refer to the feature set common to the same generation, it means all callers of aqr_gen2_config_init() also support the Global System Configuration registers at addresses 1E.31B -> 1E.31F, and these should be read by the driver to figure out the list of supported interfaces for phylink. This affects the following PHYs supported by this driver: - Gen2: AQR107 - Gen3: AQR111, AQR111B0 - Gen4: AQR114C, AQR813. AQR113C, a Gen4 PHY, has unmodified logic after this change, because currently, the aqr_gen2_fill_interface_modes() call is chained after aqr_gen2_config_init(), and after this patch, it is tail-called from the latter function, leading to the same code flow. At the same time, move aqr_gen2_fill_interface_modes() upwards of its new caller, aqr_gen2_config_init(), to avoid a forward declaration. Signed-off-by: Vladimir Oltean Link: https://patch.msgid.link/20250821152022.1065237-6-vladimir.oltean@nxp.com Signed-off-by: Jakub Kicinski --- drivers/net/phy/aquantia/aquantia_main.c | 168 ++++++++++++----------- 1 file changed, 85 insertions(+), 83 deletions(-) diff --git a/drivers/net/phy/aquantia/aquantia_main.c b/drivers/net/phy/aquantia/aquantia_main.c index 8136f7843a37..21fdbda2a0e0 100644 --- a/drivers/net/phy/aquantia/aquantia_main.c +++ b/drivers/net/phy/aquantia/aquantia_main.c @@ -860,9 +860,93 @@ static int aqr_gen1_config_init(struct phy_device *phydev) return 0; } +static const u16 aqr_global_cfg_regs[] = { + VEND1_GLOBAL_CFG_10M, + VEND1_GLOBAL_CFG_100M, + VEND1_GLOBAL_CFG_1G, + VEND1_GLOBAL_CFG_2_5G, + VEND1_GLOBAL_CFG_5G, + VEND1_GLOBAL_CFG_10G, +}; + +static int aqr_gen2_fill_interface_modes(struct phy_device *phydev) +{ + unsigned long *possible = phydev->possible_interfaces; + struct aqr107_priv *priv = phydev->priv; + unsigned int serdes_mode, rate_adapt; + phy_interface_t interface; + int i, val, ret; + + /* It's been observed on some models that - when coming out of suspend + * - the FW signals that the PHY is ready but the GLOBAL_CFG registers + * continue on returning zeroes for some time. Let's poll the 100M + * register until it returns a real value as both 113c and 115c support + * this mode. + */ + if (priv->wait_on_global_cfg) { + ret = phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1, + VEND1_GLOBAL_CFG_100M, val, + val != 0, 1000, 100000, false); + if (ret) + return ret; + } + + /* Walk the media-speed configuration registers to determine which + * host-side serdes modes may be used by the PHY depending on the + * negotiated media speed. + */ + for (i = 0; i < ARRAY_SIZE(aqr_global_cfg_regs); i++) { + val = phy_read_mmd(phydev, MDIO_MMD_VEND1, + aqr_global_cfg_regs[i]); + if (val < 0) + return val; + + serdes_mode = FIELD_GET(VEND1_GLOBAL_CFG_SERDES_MODE, val); + rate_adapt = FIELD_GET(VEND1_GLOBAL_CFG_RATE_ADAPT, val); + + switch (serdes_mode) { + case VEND1_GLOBAL_CFG_SERDES_MODE_XFI: + if (rate_adapt == VEND1_GLOBAL_CFG_RATE_ADAPT_USX) + interface = PHY_INTERFACE_MODE_USXGMII; + else + interface = PHY_INTERFACE_MODE_10GBASER; + break; + + case VEND1_GLOBAL_CFG_SERDES_MODE_XFI5G: + interface = PHY_INTERFACE_MODE_5GBASER; + break; + + case VEND1_GLOBAL_CFG_SERDES_MODE_OCSGMII: + interface = PHY_INTERFACE_MODE_2500BASEX; + break; + + case VEND1_GLOBAL_CFG_SERDES_MODE_SGMII: + interface = PHY_INTERFACE_MODE_SGMII; + break; + + default: + phydev_warn(phydev, "unrecognised serdes mode %u\n", + serdes_mode); + interface = PHY_INTERFACE_MODE_NA; + break; + } + + if (interface != PHY_INTERFACE_MODE_NA) + __set_bit(interface, possible); + } + + return 0; +} + static int aqr_gen2_config_init(struct phy_device *phydev) { - return aqr_gen1_config_init(phydev); + int ret; + + ret = aqr_gen1_config_init(phydev); + if (ret) + return ret; + + return aqr_gen2_fill_interface_modes(phydev); } static int aqcs109_config_init(struct phy_device *phydev) @@ -984,84 +1068,6 @@ static int aqr_gen1_resume(struct phy_device *phydev) return aqr_gen1_wait_processor_intensive_op(phydev); } -static const u16 aqr_global_cfg_regs[] = { - VEND1_GLOBAL_CFG_10M, - VEND1_GLOBAL_CFG_100M, - VEND1_GLOBAL_CFG_1G, - VEND1_GLOBAL_CFG_2_5G, - VEND1_GLOBAL_CFG_5G, - VEND1_GLOBAL_CFG_10G -}; - -static int aqr_gen2_fill_interface_modes(struct phy_device *phydev) -{ - unsigned long *possible = phydev->possible_interfaces; - struct aqr107_priv *priv = phydev->priv; - unsigned int serdes_mode, rate_adapt; - phy_interface_t interface; - int i, val, ret; - - /* It's been observed on some models that - when coming out of suspend - * - the FW signals that the PHY is ready but the GLOBAL_CFG registers - * continue on returning zeroes for some time. Let's poll the 100M - * register until it returns a real value as both 113c and 115c support - * this mode. - */ - if (priv->wait_on_global_cfg) { - ret = phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1, - VEND1_GLOBAL_CFG_100M, val, - val != 0, 1000, 100000, false); - if (ret) - return ret; - } - - /* Walk the media-speed configuration registers to determine which - * host-side serdes modes may be used by the PHY depending on the - * negotiated media speed. - */ - for (i = 0; i < ARRAY_SIZE(aqr_global_cfg_regs); i++) { - val = phy_read_mmd(phydev, MDIO_MMD_VEND1, - aqr_global_cfg_regs[i]); - if (val < 0) - return val; - - serdes_mode = FIELD_GET(VEND1_GLOBAL_CFG_SERDES_MODE, val); - rate_adapt = FIELD_GET(VEND1_GLOBAL_CFG_RATE_ADAPT, val); - - switch (serdes_mode) { - case VEND1_GLOBAL_CFG_SERDES_MODE_XFI: - if (rate_adapt == VEND1_GLOBAL_CFG_RATE_ADAPT_USX) - interface = PHY_INTERFACE_MODE_USXGMII; - else - interface = PHY_INTERFACE_MODE_10GBASER; - break; - - case VEND1_GLOBAL_CFG_SERDES_MODE_XFI5G: - interface = PHY_INTERFACE_MODE_5GBASER; - break; - - case VEND1_GLOBAL_CFG_SERDES_MODE_OCSGMII: - interface = PHY_INTERFACE_MODE_2500BASEX; - break; - - case VEND1_GLOBAL_CFG_SERDES_MODE_SGMII: - interface = PHY_INTERFACE_MODE_SGMII; - break; - - default: - phydev_warn(phydev, "unrecognised serdes mode %u\n", - serdes_mode); - interface = PHY_INTERFACE_MODE_NA; - break; - } - - if (interface != PHY_INTERFACE_MODE_NA) - __set_bit(interface, possible); - } - - return 0; -} - static int aqr115c_get_features(struct phy_device *phydev) { unsigned long *supported = phydev->supported; @@ -1098,10 +1104,6 @@ static int aqr113c_config_init(struct phy_device *phydev) if (ret < 0) return ret; - ret = aqr_gen2_fill_interface_modes(phydev); - if (ret) - return ret; - ret = phy_clear_bits_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_TXDIS, MDIO_PMD_TXDIS_GLOBAL); if (ret) From 08048ba4285eef925cf0dc9dbcef150d31b32ce6 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Thu, 21 Aug 2025 18:20:13 +0300 Subject: [PATCH 06/15] net: phy: aquantia: save a local shadow of GLOBAL_CFG register values Currently, aqr_gen2_fill_interface_modes() reads VEND1_GLOBAL_CFG_* registers to populate phydev->supported_interfaces. But this is not the only place which needs to read these registers. There is also aqr107_read_rate(). Based on the premise that these values are statically set by firmware and the driver only needs to read them, the proposal is to read them only once, at config_init() time, and use the cached values also in aqr107_read_rate(). This patch only refactors the aqr_gen2_fill_interface_modes() code to save the registers to driver memory, and to populate supported_interfaces based on that. Signed-off-by: Vladimir Oltean Link: https://patch.msgid.link/20250821152022.1065237-7-vladimir.oltean@nxp.com Signed-off-by: Jakub Kicinski --- drivers/net/phy/aquantia/aquantia.h | 27 +++++++ drivers/net/phy/aquantia/aquantia_main.c | 91 ++++++++++++++++-------- 2 files changed, 87 insertions(+), 31 deletions(-) diff --git a/drivers/net/phy/aquantia/aquantia.h b/drivers/net/phy/aquantia/aquantia.h index 67ec6f7484af..492052cf1e6e 100644 --- a/drivers/net/phy/aquantia/aquantia.h +++ b/drivers/net/phy/aquantia/aquantia.h @@ -174,11 +174,38 @@ static const struct aqr107_hw_stat aqr107_hw_stats[] = { #define AQR107_SGMII_STAT_SZ ARRAY_SIZE(aqr107_hw_stats) +static const struct { + int speed; + u16 reg; +} aqr_global_cfg_regs[] = { + { SPEED_10, VEND1_GLOBAL_CFG_10M, }, + { SPEED_100, VEND1_GLOBAL_CFG_100M, }, + { SPEED_1000, VEND1_GLOBAL_CFG_1G, }, + { SPEED_2500, VEND1_GLOBAL_CFG_2_5G, }, + { SPEED_5000, VEND1_GLOBAL_CFG_5G, }, + { SPEED_10000, VEND1_GLOBAL_CFG_10G, }, +}; + +#define AQR_NUM_GLOBAL_CFG ARRAY_SIZE(aqr_global_cfg_regs) + +enum aqr_rate_adaptation { + AQR_RATE_ADAPT_NONE, + AQR_RATE_ADAPT_USX, + AQR_RATE_ADAPT_PAUSE, +}; + +struct aqr_global_syscfg { + int speed; + phy_interface_t interface; + enum aqr_rate_adaptation rate_adapt; +}; + struct aqr107_priv { u64 sgmii_stats[AQR107_SGMII_STAT_SZ]; unsigned long leds_active_low; unsigned long leds_active_high; bool wait_on_global_cfg; + struct aqr_global_syscfg global_cfg[AQR_NUM_GLOBAL_CFG]; }; #if IS_REACHABLE(CONFIG_HWMON) diff --git a/drivers/net/phy/aquantia/aquantia_main.c b/drivers/net/phy/aquantia/aquantia_main.c index 21fdbda2a0e0..9d704b7e3dc8 100644 --- a/drivers/net/phy/aquantia/aquantia_main.c +++ b/drivers/net/phy/aquantia/aquantia_main.c @@ -860,44 +860,24 @@ static int aqr_gen1_config_init(struct phy_device *phydev) return 0; } -static const u16 aqr_global_cfg_regs[] = { - VEND1_GLOBAL_CFG_10M, - VEND1_GLOBAL_CFG_100M, - VEND1_GLOBAL_CFG_1G, - VEND1_GLOBAL_CFG_2_5G, - VEND1_GLOBAL_CFG_5G, - VEND1_GLOBAL_CFG_10G, -}; - -static int aqr_gen2_fill_interface_modes(struct phy_device *phydev) +/* Walk the media-speed configuration registers to determine which + * host-side serdes modes may be used by the PHY depending on the + * negotiated media speed. + */ +static int aqr_gen2_read_global_syscfg(struct phy_device *phydev) { - unsigned long *possible = phydev->possible_interfaces; struct aqr107_priv *priv = phydev->priv; unsigned int serdes_mode, rate_adapt; phy_interface_t interface; - int i, val, ret; + int i, val; - /* It's been observed on some models that - when coming out of suspend - * - the FW signals that the PHY is ready but the GLOBAL_CFG registers - * continue on returning zeroes for some time. Let's poll the 100M - * register until it returns a real value as both 113c and 115c support - * this mode. - */ - if (priv->wait_on_global_cfg) { - ret = phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1, - VEND1_GLOBAL_CFG_100M, val, - val != 0, 1000, 100000, false); - if (ret) - return ret; - } + for (i = 0; i < AQR_NUM_GLOBAL_CFG; i++) { + struct aqr_global_syscfg *syscfg = &priv->global_cfg[i]; + + syscfg->speed = aqr_global_cfg_regs[i].speed; - /* Walk the media-speed configuration registers to determine which - * host-side serdes modes may be used by the PHY depending on the - * negotiated media speed. - */ - for (i = 0; i < ARRAY_SIZE(aqr_global_cfg_regs); i++) { val = phy_read_mmd(phydev, MDIO_MMD_VEND1, - aqr_global_cfg_regs[i]); + aqr_global_cfg_regs[i].reg); if (val < 0) return val; @@ -931,6 +911,55 @@ static int aqr_gen2_fill_interface_modes(struct phy_device *phydev) break; } + syscfg->interface = interface; + + switch (rate_adapt) { + case VEND1_GLOBAL_CFG_RATE_ADAPT_NONE: + syscfg->rate_adapt = AQR_RATE_ADAPT_NONE; + break; + case VEND1_GLOBAL_CFG_RATE_ADAPT_USX: + syscfg->rate_adapt = AQR_RATE_ADAPT_USX; + break; + case VEND1_GLOBAL_CFG_RATE_ADAPT_PAUSE: + syscfg->rate_adapt = AQR_RATE_ADAPT_PAUSE; + break; + default: + phydev_warn(phydev, "unrecognized rate adapt mode %u\n", + rate_adapt); + break; + } + } + + return 0; +} + +static int aqr_gen2_fill_interface_modes(struct phy_device *phydev) +{ + unsigned long *possible = phydev->possible_interfaces; + struct aqr107_priv *priv = phydev->priv; + phy_interface_t interface; + int i, val, ret; + + /* It's been observed on some models that - when coming out of suspend + * - the FW signals that the PHY is ready but the GLOBAL_CFG registers + * continue on returning zeroes for some time. Let's poll the 100M + * register until it returns a real value as both 113c and 115c support + * this mode. + */ + if (priv->wait_on_global_cfg) { + ret = phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1, + VEND1_GLOBAL_CFG_100M, val, + val != 0, 1000, 100000, false); + if (ret) + return ret; + } + + ret = aqr_gen2_read_global_syscfg(phydev); + if (ret) + return ret; + + for (i = 0; i < AQR_NUM_GLOBAL_CFG; i++) { + interface = priv->global_cfg[i].interface; if (interface != PHY_INTERFACE_MODE_NA) __set_bit(interface, possible); } From 6fa022088b60338cf995c9238fe16bcea5c27484 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Thu, 21 Aug 2025 18:20:14 +0300 Subject: [PATCH 07/15] net: phy: aquantia: remove handling for get_rate_matching(PHY_INTERFACE_MODE_NA) After commit 7642cc28fd37 ("net: phylink: fix PHY validation with rate adaption"), the API contract changed and PHY drivers are no longer required to respond to the .get_rate_matching() method for PHY_INTERFACE_MODE_NA. This was later followed up by documentation commit 6d4cfcf97986 ("net: phy: Update documentation for get_rate_matching"). As such, handling PHY_INTERFACE_MODE_NA in the Aquantia PHY driver implementation of this method is unnecessary and confusing. Remove it. Cc: Sean Anderson Signed-off-by: Vladimir Oltean Reviewed-by: Andrew Lunn Link: https://patch.msgid.link/20250821152022.1065237-8-vladimir.oltean@nxp.com Signed-off-by: Jakub Kicinski --- drivers/net/phy/aquantia/aquantia_main.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/phy/aquantia/aquantia_main.c b/drivers/net/phy/aquantia/aquantia_main.c index 9d704b7e3dc8..0f20ed6f96d8 100644 --- a/drivers/net/phy/aquantia/aquantia_main.c +++ b/drivers/net/phy/aquantia/aquantia_main.c @@ -1067,8 +1067,7 @@ static int aqr_gen2_get_rate_matching(struct phy_device *phydev, phy_interface_t iface) { if (iface == PHY_INTERFACE_MODE_10GBASER || - iface == PHY_INTERFACE_MODE_2500BASEX || - iface == PHY_INTERFACE_MODE_NA) + iface == PHY_INTERFACE_MODE_2500BASEX) return RATE_MATCH_PAUSE; return RATE_MATCH_NONE; } From 832b63c70ef0fa40747627c78c5b849dbe6e6615 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Thu, 21 Aug 2025 18:20:15 +0300 Subject: [PATCH 08/15] net: phy: aquantia: use cached GLOBAL_CFG registers in aqr107_read_rate() aqr107_read_rate() - called from aqr107_read_status() even periodically if there is no PHY IRQ - currently reads GLOBAL_CFG registers to determine what kind of rate adaptation is in use for the current phydev->speed. However, GLOBAL_CFG registers are runtime invariants, so accessing the slow MDIO bus is unnecessary. Reimplement aqr107_read_rate() by reading from the priv->global_cfg[i].rade_adapt variables (where i is the entry corresponding to the current phydev->speed). Making this change also helps disentangle the code delta between aqr105_read_rate() and aqr107_read_rate(). They are now identical up to the code snippet which iterates over priv->global_cfg[]. This will help eliminate the duplicate code in the upcoming patch. Signed-off-by: Vladimir Oltean Link: https://patch.msgid.link/20250821152022.1065237-9-vladimir.oltean@nxp.com Signed-off-by: Jakub Kicinski --- drivers/net/phy/aquantia/aquantia_main.c | 29 +++++++++++------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/drivers/net/phy/aquantia/aquantia_main.c b/drivers/net/phy/aquantia/aquantia_main.c index 0f20ed6f96d8..4795987ef61b 100644 --- a/drivers/net/phy/aquantia/aquantia_main.c +++ b/drivers/net/phy/aquantia/aquantia_main.c @@ -568,8 +568,8 @@ static int aqr105_read_status(struct phy_device *phydev) static int aqr107_read_rate(struct phy_device *phydev) { - u32 config_reg; - int val; + struct aqr107_priv *priv = phydev->priv; + int i, val; val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_TX_VEND_STATUS1); if (val < 0) @@ -583,42 +583,39 @@ static int aqr107_read_rate(struct phy_device *phydev) switch (FIELD_GET(MDIO_AN_TX_VEND_STATUS1_RATE_MASK, val)) { case MDIO_AN_TX_VEND_STATUS1_10BASET: phydev->speed = SPEED_10; - config_reg = VEND1_GLOBAL_CFG_10M; break; case MDIO_AN_TX_VEND_STATUS1_100BASETX: phydev->speed = SPEED_100; - config_reg = VEND1_GLOBAL_CFG_100M; break; case MDIO_AN_TX_VEND_STATUS1_1000BASET: phydev->speed = SPEED_1000; - config_reg = VEND1_GLOBAL_CFG_1G; break; case MDIO_AN_TX_VEND_STATUS1_2500BASET: phydev->speed = SPEED_2500; - config_reg = VEND1_GLOBAL_CFG_2_5G; break; case MDIO_AN_TX_VEND_STATUS1_5000BASET: phydev->speed = SPEED_5000; - config_reg = VEND1_GLOBAL_CFG_5G; break; case MDIO_AN_TX_VEND_STATUS1_10GBASET: phydev->speed = SPEED_10000; - config_reg = VEND1_GLOBAL_CFG_10G; break; default: phydev->speed = SPEED_UNKNOWN; return 0; } - val = phy_read_mmd(phydev, MDIO_MMD_VEND1, config_reg); - if (val < 0) - return val; + for (i = 0; i < AQR_NUM_GLOBAL_CFG; i++) { + struct aqr_global_syscfg *syscfg = &priv->global_cfg[i]; - if (FIELD_GET(VEND1_GLOBAL_CFG_RATE_ADAPT, val) == - VEND1_GLOBAL_CFG_RATE_ADAPT_PAUSE) - phydev->rate_matching = RATE_MATCH_PAUSE; - else - phydev->rate_matching = RATE_MATCH_NONE; + if (syscfg->speed != phydev->speed) + continue; + + if (syscfg->rate_adapt == AQR_RATE_ADAPT_PAUSE) + phydev->rate_matching = RATE_MATCH_PAUSE; + else + phydev->rate_matching = RATE_MATCH_NONE; + break; + } return 0; } From c03c97e55f6291b05a9b8cf7e5eafe2432182606 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Thu, 21 Aug 2025 18:20:16 +0300 Subject: [PATCH 09/15] net: phy: aquantia: merge and rename aqr105_read_status() and aqr107_read_status() aqr105_read_status() and aqr107_read_status() are very similar. In fact, they are identical, save from a code snippet accessing a Gen2 feature (rate adaptation), placed at the end of aqr107_read_rate(), and absent from aqr105_read_rate(). The code structure is: aqr105_read_status() aqr107_read_status() -> aqr105_read_rate() -> aqr107_read_rate() After the recent change "net: phy: aquantia: use cached GLOBAL_CFG registers in aqr107_read_rate()", it is absolutely trivial to restructure the code as follows: aqr_gen2_read_status() -> aqr_gen1_read_status() -> Gen2-specific stuff (read GLOBAL_CFG registers to set rate_matching) Doing so reduces code duplication. Signed-off-by: Vladimir Oltean Link: https://patch.msgid.link/20250821152022.1065237-10-vladimir.oltean@nxp.com Signed-off-by: Jakub Kicinski --- drivers/net/phy/aquantia/aquantia_main.c | 131 ++++------------------- 1 file changed, 21 insertions(+), 110 deletions(-) diff --git a/drivers/net/phy/aquantia/aquantia_main.c b/drivers/net/phy/aquantia/aquantia_main.c index 4795987ef61b..e3a18fc1b52a 100644 --- a/drivers/net/phy/aquantia/aquantia_main.c +++ b/drivers/net/phy/aquantia/aquantia_main.c @@ -466,7 +466,7 @@ static int aqr105_config_aneg(struct phy_device *phydev) return genphy_c45_check_and_restart_aneg(phydev, changed); } -static int aqr105_read_rate(struct phy_device *phydev) +static int aqr_gen1_read_rate(struct phy_device *phydev) { int val; @@ -505,7 +505,7 @@ static int aqr105_read_rate(struct phy_device *phydev) return 0; } -static int aqr105_read_status(struct phy_device *phydev) +static int aqr_gen1_read_status(struct phy_device *phydev) { int ret; int val; @@ -563,46 +563,17 @@ static int aqr105_read_status(struct phy_device *phydev) } /* Read rate from vendor register */ - return aqr105_read_rate(phydev); + return aqr_gen1_read_rate(phydev); } -static int aqr107_read_rate(struct phy_device *phydev) +static int aqr_gen2_read_status(struct phy_device *phydev) { struct aqr107_priv *priv = phydev->priv; - int i, val; + int i, ret; - val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_TX_VEND_STATUS1); - if (val < 0) - return val; - - if (val & MDIO_AN_TX_VEND_STATUS1_FULL_DUPLEX) - phydev->duplex = DUPLEX_FULL; - else - phydev->duplex = DUPLEX_HALF; - - switch (FIELD_GET(MDIO_AN_TX_VEND_STATUS1_RATE_MASK, val)) { - case MDIO_AN_TX_VEND_STATUS1_10BASET: - phydev->speed = SPEED_10; - break; - case MDIO_AN_TX_VEND_STATUS1_100BASETX: - phydev->speed = SPEED_100; - break; - case MDIO_AN_TX_VEND_STATUS1_1000BASET: - phydev->speed = SPEED_1000; - break; - case MDIO_AN_TX_VEND_STATUS1_2500BASET: - phydev->speed = SPEED_2500; - break; - case MDIO_AN_TX_VEND_STATUS1_5000BASET: - phydev->speed = SPEED_5000; - break; - case MDIO_AN_TX_VEND_STATUS1_10GBASET: - phydev->speed = SPEED_10000; - break; - default: - phydev->speed = SPEED_UNKNOWN; - return 0; - } + ret = aqr_gen1_read_status(phydev); + if (ret) + return ret; for (i = 0; i < AQR_NUM_GLOBAL_CFG; i++) { struct aqr_global_syscfg *syscfg = &priv->global_cfg[i]; @@ -620,66 +591,6 @@ static int aqr107_read_rate(struct phy_device *phydev) return 0; } -static int aqr107_read_status(struct phy_device *phydev) -{ - int val, ret; - - ret = aqr_read_status(phydev); - if (ret) - return ret; - - if (!phydev->link || phydev->autoneg == AUTONEG_DISABLE) - return 0; - - /* The status register is not immediately correct on line side link up. - * Poll periodically until it reflects the correct ON state. - * Only return fail for read error, timeout defaults to OFF state. - */ - ret = phy_read_mmd_poll_timeout(phydev, MDIO_MMD_PHYXS, - MDIO_PHYXS_VEND_IF_STATUS, val, - (FIELD_GET(MDIO_PHYXS_VEND_IF_STATUS_TYPE_MASK, val) != - MDIO_PHYXS_VEND_IF_STATUS_TYPE_OFF), - AQR107_OP_IN_PROG_SLEEP, - AQR107_OP_IN_PROG_TIMEOUT, false); - if (ret && ret != -ETIMEDOUT) - return ret; - - switch (FIELD_GET(MDIO_PHYXS_VEND_IF_STATUS_TYPE_MASK, val)) { - case MDIO_PHYXS_VEND_IF_STATUS_TYPE_KR: - phydev->interface = PHY_INTERFACE_MODE_10GKR; - break; - case MDIO_PHYXS_VEND_IF_STATUS_TYPE_KX: - phydev->interface = PHY_INTERFACE_MODE_1000BASEKX; - break; - case MDIO_PHYXS_VEND_IF_STATUS_TYPE_XFI: - phydev->interface = PHY_INTERFACE_MODE_10GBASER; - break; - case MDIO_PHYXS_VEND_IF_STATUS_TYPE_USXGMII: - phydev->interface = PHY_INTERFACE_MODE_USXGMII; - break; - case MDIO_PHYXS_VEND_IF_STATUS_TYPE_XAUI: - phydev->interface = PHY_INTERFACE_MODE_XAUI; - break; - case MDIO_PHYXS_VEND_IF_STATUS_TYPE_SGMII: - phydev->interface = PHY_INTERFACE_MODE_SGMII; - break; - case MDIO_PHYXS_VEND_IF_STATUS_TYPE_RXAUI: - phydev->interface = PHY_INTERFACE_MODE_RXAUI; - break; - case MDIO_PHYXS_VEND_IF_STATUS_TYPE_OCSGMII: - phydev->interface = PHY_INTERFACE_MODE_2500BASEX; - break; - case MDIO_PHYXS_VEND_IF_STATUS_TYPE_OFF: - default: - phydev->link = false; - phydev->interface = PHY_INTERFACE_MODE_NA; - break; - } - - /* Read possibly downshifted rate from vendor register */ - return aqr107_read_rate(phydev); -} - static int aqr107_get_downshift(struct phy_device *phydev, u8 *data) { int val, cnt, enable; @@ -1180,7 +1091,7 @@ static struct phy_driver aqr_driver[] = { .config_aneg = aqr105_config_aneg, .config_intr = aqr_config_intr, .handle_interrupt = aqr_handle_interrupt, - .read_status = aqr105_read_status, + .read_status = aqr_gen1_read_status, .suspend = aqr_gen1_suspend, .resume = aqr_gen1_resume, }, @@ -1201,7 +1112,7 @@ static struct phy_driver aqr_driver[] = { .config_aneg = aqr_config_aneg, .config_intr = aqr_config_intr, .handle_interrupt = aqr_handle_interrupt, - .read_status = aqr107_read_status, + .read_status = aqr_gen2_read_status, .get_tunable = aqr107_get_tunable, .set_tunable = aqr107_set_tunable, .suspend = aqr_gen1_suspend, @@ -1225,7 +1136,7 @@ static struct phy_driver aqr_driver[] = { .config_aneg = aqr_config_aneg, .config_intr = aqr_config_intr, .handle_interrupt = aqr_handle_interrupt, - .read_status = aqr107_read_status, + .read_status = aqr_gen2_read_status, .get_tunable = aqr107_get_tunable, .set_tunable = aqr107_set_tunable, .suspend = aqr_gen1_suspend, @@ -1250,7 +1161,7 @@ static struct phy_driver aqr_driver[] = { .config_aneg = aqr_config_aneg, .config_intr = aqr_config_intr, .handle_interrupt = aqr_handle_interrupt, - .read_status = aqr107_read_status, + .read_status = aqr_gen2_read_status, .get_tunable = aqr107_get_tunable, .set_tunable = aqr107_set_tunable, .suspend = aqr_gen1_suspend, @@ -1275,7 +1186,7 @@ static struct phy_driver aqr_driver[] = { .config_aneg = aqr_config_aneg, .config_intr = aqr_config_intr, .handle_interrupt = aqr_handle_interrupt, - .read_status = aqr107_read_status, + .read_status = aqr_gen2_read_status, .get_tunable = aqr107_get_tunable, .set_tunable = aqr107_set_tunable, .suspend = aqr_gen1_suspend, @@ -1310,7 +1221,7 @@ static struct phy_driver aqr_driver[] = { .set_tunable = aqr107_set_tunable, .suspend = aqr_gen1_suspend, .resume = aqr_gen1_resume, - .read_status = aqr107_read_status, + .read_status = aqr_gen2_read_status, .get_rate_matching = aqr_gen2_get_rate_matching, .get_sset_count = aqr107_get_sset_count, .get_strings = aqr107_get_strings, @@ -1333,7 +1244,7 @@ static struct phy_driver aqr_driver[] = { .set_tunable = aqr107_set_tunable, .suspend = aqr_gen1_suspend, .resume = aqr_gen1_resume, - .read_status = aqr107_read_status, + .read_status = aqr_gen2_read_status, .get_rate_matching = aqr_gen2_get_rate_matching, .get_sset_count = aqr107_get_sset_count, .get_strings = aqr107_get_strings, @@ -1351,7 +1262,7 @@ static struct phy_driver aqr_driver[] = { .set_tunable = aqr107_set_tunable, .suspend = aqr_gen1_suspend, .resume = aqr_gen1_resume, - .read_status = aqr107_read_status, + .read_status = aqr_gen2_read_status, .get_rate_matching = aqr_gen2_get_rate_matching, .get_sset_count = aqr107_get_sset_count, .get_strings = aqr107_get_strings, @@ -1367,7 +1278,7 @@ static struct phy_driver aqr_driver[] = { .config_aneg = aqr_config_aneg, .config_intr = aqr_config_intr, .handle_interrupt = aqr_handle_interrupt, - .read_status = aqr107_read_status, + .read_status = aqr_gen2_read_status, .get_tunable = aqr107_get_tunable, .set_tunable = aqr107_set_tunable, .suspend = aqr_gen1_suspend, @@ -1391,7 +1302,7 @@ static struct phy_driver aqr_driver[] = { .config_aneg = aqr_config_aneg, .config_intr = aqr_config_intr, .handle_interrupt = aqr_handle_interrupt, - .read_status = aqr107_read_status, + .read_status = aqr_gen2_read_status, .get_tunable = aqr107_get_tunable, .set_tunable = aqr107_set_tunable, .suspend = aqr_gen1_suspend, @@ -1415,7 +1326,7 @@ static struct phy_driver aqr_driver[] = { .config_aneg = aqr_config_aneg, .config_intr = aqr_config_intr, .handle_interrupt = aqr_handle_interrupt, - .read_status = aqr107_read_status, + .read_status = aqr_gen2_read_status, .get_tunable = aqr107_get_tunable, .set_tunable = aqr107_set_tunable, .suspend = aqr_gen1_suspend, @@ -1440,7 +1351,7 @@ static struct phy_driver aqr_driver[] = { .config_aneg = aqr_config_aneg, .config_intr = aqr_config_intr, .handle_interrupt = aqr_handle_interrupt, - .read_status = aqr107_read_status, + .read_status = aqr_gen2_read_status, .get_tunable = aqr107_get_tunable, .set_tunable = aqr107_set_tunable, .suspend = aqr_gen1_suspend, @@ -1465,7 +1376,7 @@ static struct phy_driver aqr_driver[] = { .config_aneg = aqr_config_aneg, .config_intr = aqr_config_intr, .handle_interrupt = aqr_handle_interrupt, - .read_status = aqr107_read_status, + .read_status = aqr_gen2_read_status, .get_tunable = aqr107_get_tunable, .set_tunable = aqr107_set_tunable, .suspend = aqr_gen1_suspend, From 02a7f5a92545ef99fc503fd8a86a686d29ce0974 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Thu, 21 Aug 2025 18:20:17 +0300 Subject: [PATCH 10/15] net: phy: aquantia: call aqr_gen2_fill_interface_modes() for AQCS109 I don't have documentation or hardware to test, but according to commit 99c864667c9f ("net: phy: aquantia: add support for AQCS109"), "From software point of view, it should be almost equivalent to AQR107." I am relatively confident that the GLOBAL_CFG registers read by aqr_gen2_fill_interface_modes() are supported, because aqr_gen2_read_status(), currently used by AQCS109, also reads them, and I'm unaware of any reported problem. The change is necessary because a future patch will introduce a requirement for all aqr_gen2_read_status() callers to have previously called aqr_gen2_read_global_syscfg(). This is done through aqr_gen2_fill_interface_modes(). Cc: Nikita Yushchenko Signed-off-by: Vladimir Oltean Link: https://patch.msgid.link/20250821152022.1065237-11-vladimir.oltean@nxp.com Signed-off-by: Jakub Kicinski --- drivers/net/phy/aquantia/aquantia_main.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/net/phy/aquantia/aquantia_main.c b/drivers/net/phy/aquantia/aquantia_main.c index e3a18fc1b52a..a7b1862e8a26 100644 --- a/drivers/net/phy/aquantia/aquantia_main.c +++ b/drivers/net/phy/aquantia/aquantia_main.c @@ -899,7 +899,11 @@ static int aqcs109_config_init(struct phy_device *phydev) if (!ret) aqr107_chip_info(phydev); - return aqr107_set_downshift(phydev, MDIO_AN_VEND_PROV_DOWNSHIFT_DFLT); + ret = aqr107_set_downshift(phydev, MDIO_AN_VEND_PROV_DOWNSHIFT_DFLT); + if (ret) + return ret; + + return aqr_gen2_fill_interface_modes(phydev); } static void aqr107_link_change_notify(struct phy_device *phydev) From 2d9503217520880c80c58ee380d2a8bf7311dd49 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Thu, 21 Aug 2025 18:20:18 +0300 Subject: [PATCH 11/15] net: phy: aquantia: call aqr_gen3_config_init() for AQR112 and AQR412(C) The AQrate Gen3 PHYs are AQR111(C), AQR112(C), and their multi-port variants, like AQR411(C), AQR412(C). Currently, AQR112, AQR412 and AQR412C are Gen3 PHYs supported by the driver which have no config_init() implementation. I have hardware and documentation that confirms they are compatible with the operations done in aqr_gen2_config_init(), a Gen2-level function. This is needed as a preparation for reading cached registers in aqr_gen2_read_status(), which is a function that these PHYs already call. The initial reading is done from: aqr_gen2_config_init() -> aqr_gen2_fill_interface_modes() -> aqr_gen2_read_global_syscfg() thus the need for them to also call aqr_gen2_config_init(), in order for the cached register values to be available. In expectation of Gen3-specific features, introduce aqr_gen3_config_init() which calls aqr_gen2_config_init(). Also modify the AQR111 silicon variants to call their generation-appropriate init function. No functional change for these, hence the minor mention. Signed-off-by: Vladimir Oltean Link: https://patch.msgid.link/20250821152022.1065237-12-vladimir.oltean@nxp.com Signed-off-by: Jakub Kicinski --- drivers/net/phy/aquantia/aquantia_main.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/net/phy/aquantia/aquantia_main.c b/drivers/net/phy/aquantia/aquantia_main.c index a7b1862e8a26..00bfbea81b8b 100644 --- a/drivers/net/phy/aquantia/aquantia_main.c +++ b/drivers/net/phy/aquantia/aquantia_main.c @@ -886,6 +886,11 @@ static int aqr_gen2_config_init(struct phy_device *phydev) return aqr_gen2_fill_interface_modes(phydev); } +static int aqr_gen3_config_init(struct phy_device *phydev) +{ + return aqr_gen2_config_init(phydev); +} + static int aqcs109_config_init(struct phy_device *phydev) { int ret; @@ -1161,7 +1166,7 @@ static struct phy_driver aqr_driver[] = { .name = "Aquantia AQR111", .probe = aqr107_probe, .get_rate_matching = aqr_gen2_get_rate_matching, - .config_init = aqr_gen2_config_init, + .config_init = aqr_gen3_config_init, .config_aneg = aqr_config_aneg, .config_intr = aqr_config_intr, .handle_interrupt = aqr_handle_interrupt, @@ -1186,7 +1191,7 @@ static struct phy_driver aqr_driver[] = { .name = "Aquantia AQR111B0", .probe = aqr107_probe, .get_rate_matching = aqr_gen2_get_rate_matching, - .config_init = aqr_gen2_config_init, + .config_init = aqr_gen3_config_init, .config_aneg = aqr_config_aneg, .config_intr = aqr_config_intr, .handle_interrupt = aqr_handle_interrupt, @@ -1218,6 +1223,7 @@ static struct phy_driver aqr_driver[] = { PHY_ID_MATCH_MODEL(PHY_ID_AQR112), .name = "Aquantia AQR112", .probe = aqr107_probe, + .config_init = aqr_gen3_config_init, .config_aneg = aqr_config_aneg, .config_intr = aqr_config_intr, .handle_interrupt = aqr_handle_interrupt, @@ -1241,6 +1247,7 @@ static struct phy_driver aqr_driver[] = { PHY_ID_MATCH_MODEL(PHY_ID_AQR412), .name = "Aquantia AQR412", .probe = aqr107_probe, + .config_init = aqr_gen3_config_init, .config_aneg = aqr_config_aneg, .config_intr = aqr_config_intr, .handle_interrupt = aqr_handle_interrupt, @@ -1259,6 +1266,7 @@ static struct phy_driver aqr_driver[] = { PHY_ID_MATCH_MODEL(PHY_ID_AQR412C), .name = "Aquantia AQR412C", .probe = aqr107_probe, + .config_init = aqr_gen3_config_init, .config_aneg = aqr_config_aneg, .config_intr = aqr_config_intr, .handle_interrupt = aqr_handle_interrupt, From ed1106f7f9269f583137cce5917621b2781e5c95 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Thu, 21 Aug 2025 18:20:19 +0300 Subject: [PATCH 12/15] net: phy: aquantia: reimplement aqcs109_config_init() as aqr_gen2_config_init() I lack documentation for AQCS109, but from commit 99c864667c9f ("net: phy: aquantia: add support for AQCS109"), it is known that "From software point of view, it should be almost equivalent to AQR107." Based on further conjecture of the device numbering scheme, I am treating it as similar to AQR109 (a Gen2 PHY capable of to 2.5G). Its current instructions are also present in other init sequences as below: - aqr_wait_reset_complete() ... aqr107_chip_info() as well as aqr107_set_downshift() are in aqr_gen1_config_init() - aqr_gen2_fill_interface_modes() is in aqr_gen2_config_init() So it would be good to centralize this implementation by just calling aqr_gen2_config_init(). In practice this completes support for the following features, which are present on AQR109 already: - Potentially reverse MDI lane order via "marvell,mdi-cfg-order" - Restore polarity of active-high and active-low LEDs after reset. Cc: Nikita Yushchenko Signed-off-by: Vladimir Oltean Link: https://patch.msgid.link/20250821152022.1065237-13-vladimir.oltean@nxp.com Signed-off-by: Jakub Kicinski --- drivers/net/phy/aquantia/aquantia_main.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/drivers/net/phy/aquantia/aquantia_main.c b/drivers/net/phy/aquantia/aquantia_main.c index 00bfbea81b8b..52d434283a32 100644 --- a/drivers/net/phy/aquantia/aquantia_main.c +++ b/drivers/net/phy/aquantia/aquantia_main.c @@ -893,22 +893,12 @@ static int aqr_gen3_config_init(struct phy_device *phydev) static int aqcs109_config_init(struct phy_device *phydev) { - int ret; - /* Check that the PHY interface type is compatible */ if (phydev->interface != PHY_INTERFACE_MODE_SGMII && phydev->interface != PHY_INTERFACE_MODE_2500BASEX) return -ENODEV; - ret = aqr_wait_reset_complete(phydev); - if (!ret) - aqr107_chip_info(phydev); - - ret = aqr107_set_downshift(phydev, MDIO_AN_VEND_PROV_DOWNSHIFT_DFLT); - if (ret) - return ret; - - return aqr_gen2_fill_interface_modes(phydev); + return aqr_gen2_config_init(phydev); } static void aqr107_link_change_notify(struct phy_device *phydev) From 3c904dd67f50c5aed78d38dc72d8631ff3976217 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Thu, 21 Aug 2025 18:20:20 +0300 Subject: [PATCH 13/15] net: phy: aquantia: rename aqr113c_config_init() to aqr_gen4_config_init() aqr113c_config_init() is called by AQR113, AQR113C, AQR115C, all Gen4 PHYs. Thus, rename this to aqr_gen4_config_init(). Currently, aqr113c_config_init() calls aqr_gen2_config_init(). Since we've established that these are Gen4 PHYs, it makes sense to inherit the Gen3 feature set as well. Currently, aqr_gen3_config_init() just calls aqr_gen2_config_init(), so we can safely make this extra modification and expect no functional change. Signed-off-by: Vladimir Oltean Link: https://patch.msgid.link/20250821152022.1065237-14-vladimir.oltean@nxp.com Signed-off-by: Jakub Kicinski --- drivers/net/phy/aquantia/aquantia_main.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/phy/aquantia/aquantia_main.c b/drivers/net/phy/aquantia/aquantia_main.c index 52d434283a32..eb4409fdad34 100644 --- a/drivers/net/phy/aquantia/aquantia_main.c +++ b/drivers/net/phy/aquantia/aquantia_main.c @@ -1028,14 +1028,14 @@ static int aqr111_get_features(struct phy_device *phydev) return 0; } -static int aqr113c_config_init(struct phy_device *phydev) +static int aqr_gen4_config_init(struct phy_device *phydev) { struct aqr107_priv *priv = phydev->priv; int ret; priv->wait_on_global_cfg = true; - ret = aqr_gen2_config_init(phydev); + ret = aqr_gen3_config_init(phydev); if (ret < 0) return ret; @@ -1276,7 +1276,7 @@ static struct phy_driver aqr_driver[] = { .name = "Aquantia AQR113", .probe = aqr107_probe, .get_rate_matching = aqr_gen2_get_rate_matching, - .config_init = aqr113c_config_init, + .config_init = aqr_gen4_config_init, .config_aneg = aqr_config_aneg, .config_intr = aqr_config_intr, .handle_interrupt = aqr_handle_interrupt, @@ -1300,7 +1300,7 @@ static struct phy_driver aqr_driver[] = { .name = "Aquantia AQR113C", .probe = aqr107_probe, .get_rate_matching = aqr_gen2_get_rate_matching, - .config_init = aqr113c_config_init, + .config_init = aqr_gen4_config_init, .config_aneg = aqr_config_aneg, .config_intr = aqr_config_intr, .handle_interrupt = aqr_handle_interrupt, @@ -1349,7 +1349,7 @@ static struct phy_driver aqr_driver[] = { .name = "Aquantia AQR115C", .probe = aqr107_probe, .get_rate_matching = aqr_gen2_get_rate_matching, - .config_init = aqr113c_config_init, + .config_init = aqr_gen4_config_init, .config_aneg = aqr_config_aneg, .config_intr = aqr_config_intr, .handle_interrupt = aqr_handle_interrupt, From 9dfe80a8157ba5bb403b46f8cdfed2affe716c6b Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Thu, 21 Aug 2025 18:20:21 +0300 Subject: [PATCH 14/15] net: phy: aquantia: promote AQR813 and AQR114C to aqr_gen4_config_init() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I'm not sure whether there is any similar real-life problem on AQR813 and AQR114C as were seen on the PHYs that these commit were written for: - a7f3abcf6357 ("net: phy: aquantia: only poll GLOBAL_CFG regs on aqr113, aqr113c and aqr115c") - bed90b06b681 ("net: phy: aquantia: clear PMD Global Transmit Disable bit during init") but the inconsistency in handling between PHYs of the same generation is striking. Apart from different firmware builds with different provisioning, the only difference between these PHYs should be the max link speed and/or the number of ports. Let's try and see if there's any problem if all PHYs from the same generation use the same config_init() method. Cc: Bartosz Golaszewski Cc: Robert Marko Cc: Paweł Owoc Cc: Christian Marangi Signed-off-by: Vladimir Oltean Link: https://patch.msgid.link/20250821152022.1065237-15-vladimir.oltean@nxp.com Signed-off-by: Jakub Kicinski --- drivers/net/phy/aquantia/aquantia_main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/phy/aquantia/aquantia_main.c b/drivers/net/phy/aquantia/aquantia_main.c index eb4409fdad34..dd83205a8869 100644 --- a/drivers/net/phy/aquantia/aquantia_main.c +++ b/drivers/net/phy/aquantia/aquantia_main.c @@ -1324,7 +1324,7 @@ static struct phy_driver aqr_driver[] = { .name = "Aquantia AQR114C", .probe = aqr107_probe, .get_rate_matching = aqr_gen2_get_rate_matching, - .config_init = aqr_gen2_config_init, + .config_init = aqr_gen4_config_init, .config_aneg = aqr_config_aneg, .config_intr = aqr_config_intr, .handle_interrupt = aqr_handle_interrupt, @@ -1374,7 +1374,7 @@ static struct phy_driver aqr_driver[] = { .name = "Aquantia AQR813", .probe = aqr107_probe, .get_rate_matching = aqr_gen2_get_rate_matching, - .config_init = aqr_gen2_config_init, + .config_init = aqr_gen4_config_init, .config_aneg = aqr_config_aneg, .config_intr = aqr_config_intr, .handle_interrupt = aqr_handle_interrupt, From fb4b9f13718c60aa76c702cc25bfbe24b4b1d0b8 Mon Sep 17 00:00:00 2001 From: Camelia Groza Date: Thu, 21 Aug 2025 18:20:22 +0300 Subject: [PATCH 15/15] net: phy: aquantia: add support for AQR115 AQR115 is similar to the already supported AQR115C, having speeds up to 2.5Gbps. In fact, the two differ only in the FCBGA package size (7x11mm vs 7x7mm for the Compact variant). So it makes sense that the feature set is identical for the 2 drivers. This PHY is present on the newest PCB revision E (v4.0) of the NXP LS1046A-RDB, having replaced the RTL8211FS SGMII PHY going to fm1-mac5. Signed-off-by: Camelia Groza Signed-off-by: Vladimir Oltean Link: https://patch.msgid.link/20250821152022.1065237-16-vladimir.oltean@nxp.com Signed-off-by: Jakub Kicinski --- drivers/net/phy/aquantia/aquantia_main.c | 27 ++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/drivers/net/phy/aquantia/aquantia_main.c b/drivers/net/phy/aquantia/aquantia_main.c index dd83205a8869..8516690e34db 100644 --- a/drivers/net/phy/aquantia/aquantia_main.c +++ b/drivers/net/phy/aquantia/aquantia_main.c @@ -31,6 +31,7 @@ #define PHY_ID_AQR113 0x31c31c40 #define PHY_ID_AQR113C 0x31c31c12 #define PHY_ID_AQR114C 0x31c31c22 +#define PHY_ID_AQR115 0x31c31c63 #define PHY_ID_AQR115C 0x31c31c33 #define PHY_ID_AQR813 0x31c31cb2 @@ -1344,6 +1345,31 @@ static struct phy_driver aqr_driver[] = { .led_hw_control_get = aqr_phy_led_hw_control_get, .led_polarity_set = aqr_phy_led_polarity_set, }, +{ + PHY_ID_MATCH_MODEL(PHY_ID_AQR115), + .name = "Aquantia AQR115", + .probe = aqr107_probe, + .get_rate_matching = aqr_gen2_get_rate_matching, + .config_init = aqr_gen4_config_init, + .config_aneg = aqr_config_aneg, + .config_intr = aqr_config_intr, + .handle_interrupt = aqr_handle_interrupt, + .read_status = aqr_gen2_read_status, + .get_tunable = aqr107_get_tunable, + .set_tunable = aqr107_set_tunable, + .suspend = aqr_gen1_suspend, + .resume = aqr_gen1_resume, + .get_sset_count = aqr107_get_sset_count, + .get_strings = aqr107_get_strings, + .get_stats = aqr107_get_stats, + .get_features = aqr115c_get_features, + .link_change_notify = aqr107_link_change_notify, + .led_brightness_set = aqr_phy_led_brightness_set, + .led_hw_is_supported = aqr_phy_led_hw_is_supported, + .led_hw_control_set = aqr_phy_led_hw_control_set, + .led_hw_control_get = aqr_phy_led_hw_control_get, + .led_polarity_set = aqr_phy_led_polarity_set, +}, { PHY_ID_MATCH_MODEL(PHY_ID_AQR115C), .name = "Aquantia AQR115C", @@ -1413,6 +1439,7 @@ static const struct mdio_device_id __maybe_unused aqr_tbl[] = { { PHY_ID_MATCH_MODEL(PHY_ID_AQR113) }, { PHY_ID_MATCH_MODEL(PHY_ID_AQR113C) }, { PHY_ID_MATCH_MODEL(PHY_ID_AQR114C) }, + { PHY_ID_MATCH_MODEL(PHY_ID_AQR115) }, { PHY_ID_MATCH_MODEL(PHY_ID_AQR115C) }, { PHY_ID_MATCH_MODEL(PHY_ID_AQR813) }, { }