net: phy: aquantia: support phy-mode = "10g-qxgmii" on NXP SPF-30841 (AQR412C)
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:
862694a496
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 <vladimir.oltean@nxp.com>
Link: https://patch.msgid.link/20250903130730.2836022-7-vladimir.oltean@nxp.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
pull/1354/merge
parent
dda916111e
commit
a76f26f7a8
|
|
@ -171,6 +171,10 @@
|
||||||
FIELD_PREP(AQR_FW_FINGERPRINT_MISC_ID, misc_id) | \
|
FIELD_PREP(AQR_FW_FINGERPRINT_MISC_ID, misc_id) | \
|
||||||
FIELD_PREP(AQR_FW_FINGERPRINT_MISC_VER, misc_ver))
|
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 {
|
struct aqr107_hw_stat {
|
||||||
const char *name;
|
const char *name;
|
||||||
int reg;
|
int reg;
|
||||||
|
|
|
||||||
|
|
@ -512,8 +512,31 @@ static int aqr_gen1_read_rate(struct phy_device *phydev)
|
||||||
return 0;
|
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)
|
static int aqr_gen1_read_status(struct phy_device *phydev)
|
||||||
{
|
{
|
||||||
|
phy_interface_t interface;
|
||||||
int ret;
|
int ret;
|
||||||
int val;
|
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)) {
|
switch (FIELD_GET(MDIO_PHYXS_VEND_IF_STATUS_TYPE_MASK, val)) {
|
||||||
case MDIO_PHYXS_VEND_IF_STATUS_TYPE_KR:
|
case MDIO_PHYXS_VEND_IF_STATUS_TYPE_KR:
|
||||||
phydev->interface = PHY_INTERFACE_MODE_10GKR;
|
interface = PHY_INTERFACE_MODE_10GKR;
|
||||||
break;
|
break;
|
||||||
case MDIO_PHYXS_VEND_IF_STATUS_TYPE_KX:
|
case MDIO_PHYXS_VEND_IF_STATUS_TYPE_KX:
|
||||||
phydev->interface = PHY_INTERFACE_MODE_1000BASEKX;
|
interface = PHY_INTERFACE_MODE_1000BASEKX;
|
||||||
break;
|
break;
|
||||||
case MDIO_PHYXS_VEND_IF_STATUS_TYPE_XFI:
|
case MDIO_PHYXS_VEND_IF_STATUS_TYPE_XFI:
|
||||||
phydev->interface = PHY_INTERFACE_MODE_10GBASER;
|
interface = PHY_INTERFACE_MODE_10GBASER;
|
||||||
break;
|
break;
|
||||||
case MDIO_PHYXS_VEND_IF_STATUS_TYPE_USXGMII:
|
case MDIO_PHYXS_VEND_IF_STATUS_TYPE_USXGMII:
|
||||||
phydev->interface = PHY_INTERFACE_MODE_USXGMII;
|
interface = PHY_INTERFACE_MODE_USXGMII;
|
||||||
break;
|
break;
|
||||||
case MDIO_PHYXS_VEND_IF_STATUS_TYPE_XAUI:
|
case MDIO_PHYXS_VEND_IF_STATUS_TYPE_XAUI:
|
||||||
phydev->interface = PHY_INTERFACE_MODE_XAUI;
|
interface = PHY_INTERFACE_MODE_XAUI;
|
||||||
break;
|
break;
|
||||||
case MDIO_PHYXS_VEND_IF_STATUS_TYPE_SGMII:
|
case MDIO_PHYXS_VEND_IF_STATUS_TYPE_SGMII:
|
||||||
phydev->interface = PHY_INTERFACE_MODE_SGMII;
|
interface = PHY_INTERFACE_MODE_SGMII;
|
||||||
break;
|
break;
|
||||||
case MDIO_PHYXS_VEND_IF_STATUS_TYPE_RXAUI:
|
case MDIO_PHYXS_VEND_IF_STATUS_TYPE_RXAUI:
|
||||||
phydev->interface = PHY_INTERFACE_MODE_RXAUI;
|
interface = PHY_INTERFACE_MODE_RXAUI;
|
||||||
break;
|
break;
|
||||||
case MDIO_PHYXS_VEND_IF_STATUS_TYPE_OCSGMII:
|
case MDIO_PHYXS_VEND_IF_STATUS_TYPE_OCSGMII:
|
||||||
phydev->interface = PHY_INTERFACE_MODE_2500BASEX;
|
interface = PHY_INTERFACE_MODE_2500BASEX;
|
||||||
break;
|
break;
|
||||||
case MDIO_PHYXS_VEND_IF_STATUS_TYPE_OFF:
|
case MDIO_PHYXS_VEND_IF_STATUS_TYPE_OFF:
|
||||||
default:
|
default:
|
||||||
phydev->link = false;
|
phydev->link = false;
|
||||||
phydev->interface = PHY_INTERFACE_MODE_NA;
|
interface = PHY_INTERFACE_MODE_NA;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
phydev->interface = aqr_translate_interface(phydev, interface);
|
||||||
|
|
||||||
/* Read rate from vendor register */
|
/* Read rate from vendor register */
|
||||||
return aqr_gen1_read_rate(phydev);
|
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_2500BASEX &&
|
||||||
phydev->interface != PHY_INTERFACE_MODE_XGMII &&
|
phydev->interface != PHY_INTERFACE_MODE_XGMII &&
|
||||||
phydev->interface != PHY_INTERFACE_MODE_USXGMII &&
|
phydev->interface != PHY_INTERFACE_MODE_USXGMII &&
|
||||||
|
phydev->interface != PHY_INTERFACE_MODE_10G_QXGMII &&
|
||||||
phydev->interface != PHY_INTERFACE_MODE_10GKR &&
|
phydev->interface != PHY_INTERFACE_MODE_10GKR &&
|
||||||
phydev->interface != PHY_INTERFACE_MODE_10GBASER &&
|
phydev->interface != PHY_INTERFACE_MODE_10GBASER &&
|
||||||
phydev->interface != PHY_INTERFACE_MODE_XAUI &&
|
phydev->interface != PHY_INTERFACE_MODE_XAUI &&
|
||||||
|
|
@ -851,7 +877,7 @@ static int aqr_gen2_read_global_syscfg(struct phy_device *phydev)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
syscfg->interface = interface;
|
syscfg->interface = aqr_translate_interface(phydev, interface);
|
||||||
|
|
||||||
switch (rate_adapt) {
|
switch (rate_adapt) {
|
||||||
case VEND1_GLOBAL_CFG_RATE_ADAPT_NONE:
|
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)
|
phy_interface_t interface)
|
||||||
{
|
{
|
||||||
if (interface == PHY_INTERFACE_MODE_SGMII ||
|
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 LINK_INBAND_ENABLE | LINK_INBAND_DISABLE;
|
||||||
|
|
||||||
return 0;
|
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;
|
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;
|
u16 set = 0;
|
||||||
|
|
||||||
if (modes == LINK_INBAND_ENABLE)
|
if (modes == LINK_INBAND_ENABLE)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue