]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
net: phy: aquantia: report and configure in-band autoneg capabilities
authorVladimir Oltean <vladimir.oltean@nxp.com>
Wed, 3 Sep 2025 13:07:28 +0000 (16:07 +0300)
committerJakub Kicinski <kuba@kernel.org>
Sat, 6 Sep 2025 02:03:40 +0000 (19:03 -0700)
The Global System Configuration registers for each media side link speed
have bit 3 which controls auto-negotiation for the system interface.
Since bits 2:0 of the same register indicate the SerDes protocol for the
same system interface, it makes sense to filter these registers for the
SerDes protocol matching phydev->interface, and to read/write the
auto-negotiation bit.

However, experimentally, USXGMII in-band auto-negotiation is unaffected
by this bit, and instead reacts to bit 3 of register 4.C441 (PHY XS
Transmit Reserved Vendor Provisioning 2).

Both the Global System Configuration as well as the aforementioned
register 4.C441 are documented as PD (Provisioning Defaults), i.e. each
PHY firmware may provision its own values.

I was initially planning to only read these values and not support
changing them (instead just the MAC PCS reconfigures itself, if it can).
But there is one problem: Linux expects that the in-band capability is
configured the same for all speeds where a given SerDes protocol is used.
I was going to add logic that detects mismatched vendor provisioning
(in-band autoneg enabled for speed X, disabled for speed Y) and warn
about it and return 0 (unknown capabilities).

Funnily enough, there is already a known instance where speed 2500 has
"autoneg 1" and the lower speeds have "autoneg 0":
https://lore.kernel.org/netdev/aJH8n0zheqB8tWzb@FUE-ALEWI-WINX/

I don't think it's worth fighting the battle with inconsistent firmware
images built by Aquantia/Marvell, and reporting that to the user, when
we have the ability to modify these fields to values that make sense to
us. We see the same situation with all the aqr*_get_features() functions
which fix up nonsensical supported link modes.

Furthermore, altering the in-band auto-negotiation setting can be
considered a minor change, compared to changing the SerDes protocol in
its entirety, for which we are still not prepared.

Testing was done on:
- AQR107 (Gen2) in USXGMII mode, as found on the NXP LX2160A-RDB.
- AQR112 (Gen3) in USXGMII mode, as found on the NXP SCH-30842 riser
  card, plugged into LS1028A-QDS.
- AQR412C (Gen3) in 10G-QXGMII mode, as found on the NXP SCH-30841 riser
  card, plugged into the LS1028A-QDS.
- AQR115 (Gen4) in SGMII mode, as found on the NXP LS1046A-RDB rev E.

Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Link: https://patch.msgid.link/20250903130730.2836022-5-vladimir.oltean@nxp.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/net/phy/aquantia/aquantia.h
drivers/net/phy/aquantia/aquantia_main.c

index 492052cf1e6e25a1024cca78323e10faf79f5038..3fc7044bcdc7f20976726c7183ffd23edece1711 100644 (file)
@@ -55,6 +55,7 @@
 #define VEND1_GLOBAL_CFG_SERDES_MODE_SGMII     3
 #define VEND1_GLOBAL_CFG_SERDES_MODE_OCSGMII   4
 #define VEND1_GLOBAL_CFG_SERDES_MODE_XFI5G     6
+#define VEND1_GLOBAL_CFG_AUTONEG_ENA           BIT(3)
 #define VEND1_GLOBAL_CFG_RATE_ADAPT            GENMASK(8, 7)
 #define VEND1_GLOBAL_CFG_RATE_ADAPT_NONE       0
 #define VEND1_GLOBAL_CFG_RATE_ADAPT_USX                1
index 309eecbf71f10c9a599630129a6ce3c1afbf2903..a9bd35b3be4b25e23faf21e0f520d29195576dd8 100644 (file)
@@ -35,6 +35,9 @@
 #define PHY_ID_AQR115C 0x31c31c33
 #define PHY_ID_AQR813  0x31c31cb2
 
+#define MDIO_PHYXS_VEND_PROV2                  0xc441
+#define MDIO_PHYXS_VEND_PROV2_USX_AN           BIT(3)
+
 #define MDIO_PHYXS_VEND_IF_STATUS              0xe812
 #define MDIO_PHYXS_VEND_IF_STATUS_TYPE_MASK    GENMASK(7, 3)
 #define MDIO_PHYXS_VEND_IF_STATUS_TYPE_KR      0
@@ -1056,6 +1059,52 @@ static int aqr_gen4_config_init(struct phy_device *phydev)
        return aqr_gen1_wait_processor_intensive_op(phydev);
 }
 
+static unsigned int aqr_gen2_inband_caps(struct phy_device *phydev,
+                                        phy_interface_t interface)
+{
+       if (interface == PHY_INTERFACE_MODE_SGMII ||
+           interface == PHY_INTERFACE_MODE_USXGMII)
+               return LINK_INBAND_ENABLE | LINK_INBAND_DISABLE;
+
+       return 0;
+}
+
+static int aqr_gen2_config_inband(struct phy_device *phydev, unsigned int modes)
+{
+       struct aqr107_priv *priv = phydev->priv;
+
+       if (phydev->interface == PHY_INTERFACE_MODE_USXGMII) {
+               u16 set = 0;
+
+               if (modes == LINK_INBAND_ENABLE)
+                       set = MDIO_PHYXS_VEND_PROV2_USX_AN;
+
+               return phy_modify_mmd(phydev, MDIO_MMD_PHYXS,
+                                     MDIO_PHYXS_VEND_PROV2,
+                                     MDIO_PHYXS_VEND_PROV2_USX_AN, set);
+       }
+
+       for (int i = 0; i < AQR_NUM_GLOBAL_CFG; i++) {
+               struct aqr_global_syscfg *syscfg = &priv->global_cfg[i];
+               u16 set = 0;
+               int err;
+
+               if (syscfg->interface != phydev->interface)
+                       continue;
+
+               if (modes == LINK_INBAND_ENABLE)
+                       set = VEND1_GLOBAL_CFG_AUTONEG_ENA;
+
+               err = phy_modify_mmd(phydev, MDIO_MMD_VEND1,
+                                    aqr_global_cfg_regs[i].reg,
+                                    VEND1_GLOBAL_CFG_AUTONEG_ENA, set);
+               if (err)
+                       return err;
+       }
+
+       return 0;
+}
+
 static int aqr107_probe(struct phy_device *phydev)
 {
        int ret;
@@ -1134,6 +1183,8 @@ static struct phy_driver aqr_driver[] = {
        .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,
+       .inband_caps    = aqr_gen2_inband_caps,
+       .config_inband  = aqr_gen2_config_inband,
 },
 {
        PHY_ID_MATCH_MODEL(PHY_ID_AQCS109),
@@ -1159,6 +1210,8 @@ static struct phy_driver aqr_driver[] = {
        .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,
+       .inband_caps    = aqr_gen2_inband_caps,
+       .config_inband  = aqr_gen2_config_inband,
 },
 {
        PHY_ID_MATCH_MODEL(PHY_ID_AQR111),
@@ -1184,6 +1237,8 @@ static struct phy_driver aqr_driver[] = {
        .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,
+       .inband_caps    = aqr_gen2_inband_caps,
+       .config_inband  = aqr_gen2_config_inband,
 },
 {
        PHY_ID_MATCH_MODEL(PHY_ID_AQR111B0),
@@ -1209,6 +1264,8 @@ static struct phy_driver aqr_driver[] = {
        .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,
+       .inband_caps    = aqr_gen2_inband_caps,
+       .config_inband  = aqr_gen2_config_inband,
 },
 {
        PHY_ID_MATCH_MODEL(PHY_ID_AQR405),
@@ -1217,6 +1274,8 @@ static struct phy_driver aqr_driver[] = {
        .config_intr    = aqr_config_intr,
        .handle_interrupt = aqr_handle_interrupt,
        .read_status    = aqr_read_status,
+       .inband_caps    = aqr_gen2_inband_caps,
+       .config_inband  = aqr_gen2_config_inband,
 },
 {
        PHY_ID_MATCH_MODEL(PHY_ID_AQR112),
@@ -1241,6 +1300,8 @@ static struct phy_driver aqr_driver[] = {
        .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,
+       .inband_caps    = aqr_gen2_inband_caps,
+       .config_inband  = aqr_gen2_config_inband,
 },
 {
        PHY_ID_MATCH_MODEL(PHY_ID_AQR412),
@@ -1260,6 +1321,8 @@ static struct phy_driver aqr_driver[] = {
        .get_strings    = aqr107_get_strings,
        .get_stats      = aqr107_get_stats,
        .link_change_notify = aqr107_link_change_notify,
+       .inband_caps    = aqr_gen2_inband_caps,
+       .config_inband  = aqr_gen2_config_inband,
 },
 {
        PHY_ID_MATCH_MODEL(PHY_ID_AQR412C),
@@ -1279,6 +1342,8 @@ static struct phy_driver aqr_driver[] = {
        .get_strings    = aqr107_get_strings,
        .get_stats      = aqr107_get_stats,
        .link_change_notify = aqr107_link_change_notify,
+       .inband_caps    = aqr_gen2_inband_caps,
+       .config_inband  = aqr_gen2_config_inband,
 },
 {
        PHY_ID_MATCH_MODEL(PHY_ID_AQR113),
@@ -1303,6 +1368,8 @@ static struct phy_driver aqr_driver[] = {
        .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,
+       .inband_caps    = aqr_gen2_inband_caps,
+       .config_inband  = aqr_gen2_config_inband,
 },
 {
        PHY_ID_MATCH_MODEL(PHY_ID_AQR113C),
@@ -1327,6 +1394,8 @@ static struct phy_driver aqr_driver[] = {
        .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,
+       .inband_caps    = aqr_gen2_inband_caps,
+       .config_inband  = aqr_gen2_config_inband,
 },
 {
        PHY_ID_MATCH_MODEL(PHY_ID_AQR114C),
@@ -1352,6 +1421,8 @@ static struct phy_driver aqr_driver[] = {
        .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,
+       .inband_caps    = aqr_gen2_inband_caps,
+       .config_inband  = aqr_gen2_config_inband,
 },
 {
        PHY_ID_MATCH_MODEL(PHY_ID_AQR115),
@@ -1377,6 +1448,8 @@ static struct phy_driver aqr_driver[] = {
        .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,
+       .inband_caps    = aqr_gen2_inband_caps,
+       .config_inband  = aqr_gen2_config_inband,
 },
 {
        PHY_ID_MATCH_MODEL(PHY_ID_AQR115C),
@@ -1402,6 +1475,8 @@ static struct phy_driver aqr_driver[] = {
        .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,
+       .inband_caps    = aqr_gen2_inband_caps,
+       .config_inband  = aqr_gen2_config_inband,
 },
 {
        PHY_ID_MATCH_MODEL(PHY_ID_AQR813),
@@ -1426,6 +1501,8 @@ static struct phy_driver aqr_driver[] = {
        .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,
+       .inband_caps    = aqr_gen2_inband_caps,
+       .config_inband  = aqr_gen2_config_inband,
 },
 };