[PATCH] phy: aquantia: Configure SERDES mode by default

Sean Anderson
2022-11-14T21:08:04Z

When autonegotiation completes, the phy interface will be set based on
the global config register for that speed. If the SERDES mode is set to
something which the MAC does not support, then the link will not come
up. The register reference says that the SERDES mode should default to
XFI, but for some phys lower speeds default to XFI/2 (5G XFI). To ensure
the link comes up correctly, configure the SERDES mode.

We use the same configuration for all interfaces. We don't advertise
any speeds faster than the interface mode, so they won't be selected.
We default to pause-based rate adaptation, but enable USXGMII rate
adaptation for USXGMII. I'm not sure if this is correct for
SGMII; it might need USXGMII adaptation instead.

This effectively disables switching interface mode depending on the
speed, in favor of using rate adaptation. If this is not desired, we
would need some kind of API to configure things.

Signed-off-by: Sean Anderson <sean.anderson@seco.com>
---

drivers/net/phy/aquantia_main.c | 65 +++++++++++++++++++++++++++++++++
1 file changed, 65 insertions(+)

diff --git a/drivers/net/phy/aquantia_main.c b/drivers/net/phy/aquantia_main.c
index 47a76df36b74..88a3defb632c 100644
--- a/drivers/net/phy/aquantia_main.c
+++ b/drivers/net/phy/aquantia_main.c
@@ -109,6 +109,10 @@
#define VEND1_GLOBAL_CFG_RATE_ADAPT_NONE 0
#define VEND1_GLOBAL_CFG_RATE_ADAPT_USX 1
#define VEND1_GLOBAL_CFG_RATE_ADAPT_PAUSE 2
+#define VEND1_GLOBAL_CFG_SERDES_MODE GENMASK(2, 0)
+#define VEND1_GLOBAL_CFG_SERDES_MODE_XFI 0
+#define VEND1_GLOBAL_CFG_SERDES_MODE_SGMII 3
+#define VEND1_GLOBAL_CFG_SERDES_MODE_OCSGMII 4

#define VEND1_GLOBAL_RSVD_STAT1 0xc885
#define VEND1_GLOBAL_RSVD_STAT1_FW_BUILD_ID GENMASK(7, 4)
@@ -558,6 +562,63 @@ static void aqr107_chip_info(struct phy_device *phydev)
fw_major, fw_minor, build_id, prov_id);
}

+static int aqr107_global_config_init(struct phy_device *phydev)
+{
+ u16 mask = VEND1_GLOBAL_CFG_RATE_ADAPT | VEND1_GLOBAL_CFG_SERDES_MODE;
+ u16 val, serdes_mode, rate_adapt = VEND1_GLOBAL_CFG_RATE_ADAPT_PAUSE;
+ u16 config_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,
+ };
+ int i, ret;
+
+ switch (phydev->interface) {
+ case PHY_INTERFACE_MODE_SGMII:
+ case PHY_INTERFACE_MODE_1000BASEKX:
+ rate_adapt = VEND1_GLOBAL_CFG_RATE_ADAPT_USX;
+ serdes_mode = VEND1_GLOBAL_CFG_SERDES_MODE_SGMII;
+ break;
+
+ case PHY_INTERFACE_MODE_2500BASEX:
+ serdes_mode = VEND1_GLOBAL_CFG_SERDES_MODE_OCSGMII;
+ break;
+
+ case PHY_INTERFACE_MODE_USXGMII:
+ rate_adapt = VEND1_GLOBAL_CFG_RATE_ADAPT_USX;
+ fallthrough;
+ case PHY_INTERFACE_MODE_XGMII:
+ case PHY_INTERFACE_MODE_10GKR:
+ case PHY_INTERFACE_MODE_10GBASER:
+ case PHY_INTERFACE_MODE_XAUI:
+ case PHY_INTERFACE_MODE_RXAUI:
+ serdes_mode = VEND1_GLOBAL_CFG_SERDES_MODE_XFI;
+ break;
+
+ default:
+ WARN_ON_ONCE(1);
+ return -EINVAL;
+ }
+
+ val = FIELD_PREP(VEND1_GLOBAL_CFG_RATE_ADAPT, rate_adapt);
+ val |= FIELD_PREP(VEND1_GLOBAL_CFG_SERDES_MODE, serdes_mode);
+
+ for (i = 0; i < ARRAY_SIZE(config_regs); i++) {
+ ret = phy_modify_mmd(phydev, MDIO_MMD_VEND1, config_regs[i],
+ mask, val);
+ if (ret) {
+ phydev_err(phydev, "could not initialize register %x\n",
+ config_regs[i]);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
static int aqr107_config_init(struct phy_device *phydev)
{
int ret;
@@ -581,6 +642,10 @@ static int aqr107_config_init(struct phy_device *phydev)
if (!ret)
aqr107_chip_info(phydev);

+ ret = aqr107_global_config_init(phydev);
+ if (ret)
+ return ret;
+
return aqr107_set_downshift(phydev, MDIO_AN_VEND_PROV_DOWNSHIFT_DFLT);
}

--
2.35.1.1320.gc452695387.dirty