The quad port PHYs (AQR4*) have 4 system interfaces, and some of them, like AQR412C, can be used with a special firmware provisioning which multiplexes all ports over a single host-side SerDes lane. The protocol used over this lane is Cisco 10G-QXGMII feature, or "MUSX", as Aquantia seems to call it. One such example is the AQR412C PHY from the NXP SPF-30841 10G-QXGMII add-in card, which uses this firmware file: https://github.com/nxp-qoriq/qoriq-firmware-aquantia/blob/master/AQR-G3_v4.3.C-AQR_NXP_SPF-30841_MUSX_ID40019_VER1198.cld There seems to be no disagreement, including from Marvell FAE, that 10G-QXGMII is reported to the host over MDIO as USXGMII and indistinguishable from it. This includes the registers from the provisioning based on which the firmware configures a single system interface (lane C in the case of SPF-30841) to multiplex all ports - they are also only accessible from the firmware, or over I2C (?!). However, the Linux MAC and especially SerDes drivers may need to know if it is using 1 port per lane (USXGMII) or 4 ports per lane (10G-QXGMII). In the downstream Layerscape SDK we have previously implemented a simpler scheme where for certain PHY interface modes, we trust the device tree and never let the PHY driver overwrite phydev->interface: https://github.com/nxp-qoriq/linux/commit/862694a4961db590c4d8a5590b84791361ca773d but for upstream, a nicer detection method is implemented, where although we can not distinguish USXGMII from 10G-QXGMII per se, we create a whitelist of firmware fingerprints for which USXGMII is translated into 10G-QXGMII. At the time of writing, it is expected that this should only happen for the NXP SPF-30841 card, although extending for more is trivial - just uncomment the phydev_dbg() in aqr_build_fingerprint(). An advantage of this method is that it doesn't strictly require updates to arch/arm64/boot/dts/freescale/fsl-ls1028a-qds-13bb.dtso, since the PHY driver will transition from "usxgmii" to "10g-qxgmii". All aqr_translate_interface() callers have also previously called aqr107_probe(), so dereferencing phydev->priv is safe. Signed-off-by: Vladimir Oltean --- drivers/net/phy/aquantia/aquantia.h | 4 ++ drivers/net/phy/aquantia/aquantia_main.c | 52 ++++++++++++++++++------ 2 files changed, 44 insertions(+), 12 deletions(-) diff --git a/drivers/net/phy/aquantia/aquantia.h b/drivers/net/phy/aquantia/aquantia.h index 2911965f0868..a70c1b241827 100644 --- a/drivers/net/phy/aquantia/aquantia.h +++ b/drivers/net/phy/aquantia/aquantia.h @@ -171,6 +171,10 @@ FIELD_PREP(AQR_FW_FINGERPRINT_MISC_ID, misc_id) | \ FIELD_PREP(AQR_FW_FINGERPRINT_MISC_VER, misc_ver)) +/* 10G-QXGMII firmware for NXP SPF-30841 riser board (AQR412C) */ +#define AQR_G3_V4_3_C_AQR_NXP_SPF_30841_MUSX_ID40019_VER1198 \ + AQR_FW_FINGERPRINT(4, 3, 0xc, 1, 40019, 1198) + struct aqr107_hw_stat { const char *name; int reg; diff --git a/drivers/net/phy/aquantia/aquantia_main.c b/drivers/net/phy/aquantia/aquantia_main.c index 5fbf392a84b2..41f3676c7f1e 100644 --- a/drivers/net/phy/aquantia/aquantia_main.c +++ b/drivers/net/phy/aquantia/aquantia_main.c @@ -512,8 +512,31 @@ static int aqr_gen1_read_rate(struct phy_device *phydev) return 0; } +/* Quad port PHYs like AQR412(C) have 4 system interfaces, but they can also be + * used with a single system interface over which all 4 ports are multiplexed + * (10G-QXGMII). To the MDIO registers, this mode is indistinguishable from + * USXGMII (which implies a single 10G port). + * + * To not rely solely on the device tree, we allow the regular system interface + * detection to work as usual, but we replace USXGMII with 10G-QXGMII based on + * the specific fingerprint of firmware images that are known to be for MUSX. + */ +static phy_interface_t aqr_translate_interface(struct phy_device *phydev, + phy_interface_t interface) +{ + struct aqr107_priv *priv = phydev->priv; + + if (phy_id_compare(phydev->drv->phy_id, PHY_ID_AQR412C, phydev->drv->phy_id_mask) && + priv->fingerprint == AQR_G3_V4_3_C_AQR_NXP_SPF_30841_MUSX_ID40019_VER1198 && + interface == PHY_INTERFACE_MODE_USXGMII) + return PHY_INTERFACE_MODE_10G_QXGMII; + + return interface; +} + static int aqr_gen1_read_status(struct phy_device *phydev) { + phy_interface_t interface; int ret; int val; @@ -539,36 +562,38 @@ static int aqr_gen1_read_status(struct phy_device *phydev) 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; + interface = PHY_INTERFACE_MODE_10GKR; break; case MDIO_PHYXS_VEND_IF_STATUS_TYPE_KX: - phydev->interface = PHY_INTERFACE_MODE_1000BASEKX; + interface = PHY_INTERFACE_MODE_1000BASEKX; break; case MDIO_PHYXS_VEND_IF_STATUS_TYPE_XFI: - phydev->interface = PHY_INTERFACE_MODE_10GBASER; + interface = PHY_INTERFACE_MODE_10GBASER; break; case MDIO_PHYXS_VEND_IF_STATUS_TYPE_USXGMII: - phydev->interface = PHY_INTERFACE_MODE_USXGMII; + interface = PHY_INTERFACE_MODE_USXGMII; break; case MDIO_PHYXS_VEND_IF_STATUS_TYPE_XAUI: - phydev->interface = PHY_INTERFACE_MODE_XAUI; + interface = PHY_INTERFACE_MODE_XAUI; break; case MDIO_PHYXS_VEND_IF_STATUS_TYPE_SGMII: - phydev->interface = PHY_INTERFACE_MODE_SGMII; + interface = PHY_INTERFACE_MODE_SGMII; break; case MDIO_PHYXS_VEND_IF_STATUS_TYPE_RXAUI: - phydev->interface = PHY_INTERFACE_MODE_RXAUI; + interface = PHY_INTERFACE_MODE_RXAUI; break; case MDIO_PHYXS_VEND_IF_STATUS_TYPE_OCSGMII: - phydev->interface = PHY_INTERFACE_MODE_2500BASEX; + interface = PHY_INTERFACE_MODE_2500BASEX; break; case MDIO_PHYXS_VEND_IF_STATUS_TYPE_OFF: default: phydev->link = false; - phydev->interface = PHY_INTERFACE_MODE_NA; + interface = PHY_INTERFACE_MODE_NA; break; } + phydev->interface = aqr_translate_interface(phydev, interface); + /* Read rate from vendor register */ return aqr_gen1_read_rate(phydev); } @@ -757,6 +782,7 @@ static int aqr_gen1_config_init(struct phy_device *phydev) phydev->interface != PHY_INTERFACE_MODE_2500BASEX && phydev->interface != PHY_INTERFACE_MODE_XGMII && phydev->interface != PHY_INTERFACE_MODE_USXGMII && + phydev->interface != PHY_INTERFACE_MODE_10G_QXGMII && phydev->interface != PHY_INTERFACE_MODE_10GKR && phydev->interface != PHY_INTERFACE_MODE_10GBASER && phydev->interface != PHY_INTERFACE_MODE_XAUI && @@ -851,7 +877,7 @@ static int aqr_gen2_read_global_syscfg(struct phy_device *phydev) break; } - syscfg->interface = interface; + syscfg->interface = aqr_translate_interface(phydev, interface); switch (rate_adapt) { case VEND1_GLOBAL_CFG_RATE_ADAPT_NONE: @@ -1091,7 +1117,8 @@ 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) + interface == PHY_INTERFACE_MODE_USXGMII || + interface == PHY_INTERFACE_MODE_10G_QXGMII) return LINK_INBAND_ENABLE | LINK_INBAND_DISABLE; return 0; @@ -1101,7 +1128,8 @@ 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) { + if (phydev->interface == PHY_INTERFACE_MODE_USXGMII || + phydev->interface == PHY_INTERFACE_MODE_10G_QXGMII) { u16 set = 0; if (modes == LINK_INBAND_ENABLE) -- 2.34.1