According to the XPCS datasheet, a soft reset is required to initiate Clause 37 auto-negotiation when the XPCS switches interface modes. When the interface mode is set to 2500BASE-X, Clause 37 Auto-Negotiation is turned off. Subsequently, when the interface mode switches from 2500BASE-X to SGMII, re-initiating Clause 37 auto-negotiation is required for the SGMII interface mode to function properly. Signed-off-by: Choong Yong Liang <yong.liang.choong@xxxxxxxxxxxxxxx> --- drivers/net/pcs/pcs-xpcs.c | 62 +++++++++++++++++++++++++++++++++++--- 1 file changed, 57 insertions(+), 5 deletions(-) diff --git a/drivers/net/pcs/pcs-xpcs.c b/drivers/net/pcs/pcs-xpcs.c index 52a7757ee419..cf1ed89d6418 100644 --- a/drivers/net/pcs/pcs-xpcs.c +++ b/drivers/net/pcs/pcs-xpcs.c @@ -848,6 +848,60 @@ static int xpcs_config_2500basex(struct dw_xpcs *xpcs) return xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_MMD_CTRL, ret); } +static int xpcs_switch_to_aneg_c37_sgmii(const struct xpcs_compat *compat, + struct dw_xpcs *xpcs, + unsigned int neg_mode) +{ + bool an_c37_enabled; + int ret, mdio_ctrl; + + if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) { + mdio_ctrl = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_MMD_CTRL); + if (mdio_ctrl < 0) + return mdio_ctrl; + + an_c37_enabled = mdio_ctrl & AN_CL37_EN; + if (!an_c37_enabled) { + //Perform soft reset to initiate C37 auto-negotiation + ret = xpcs_soft_reset(xpcs, compat); + if (ret) + return ret; + } + } + return 0; +} + +static int xpcs_switch_interface_mode(const struct xpcs_compat *compat, + struct dw_xpcs *xpcs, + phy_interface_t interface, + unsigned int neg_mode) +{ + int ret; + + if (xpcs->dev_flag == DW_DEV_TXGBE) { + ret = txgbe_xpcs_switch_mode(xpcs, interface); + if (ret) + return ret; + } else { + if (xpcs->interface != interface) { + xpcs->interface = interface; + + switch (compat->an_mode) { + case DW_AN_C37_SGMII: + ret = xpcs_switch_to_aneg_c37_sgmii(compat, + xpcs, + neg_mode); + if (ret) + return ret; + break; + default: + return 0; + } + } + } + return 0; +} + int xpcs_do_config(struct dw_xpcs *xpcs, phy_interface_t interface, const unsigned long *advertising, unsigned int neg_mode) { @@ -858,11 +912,9 @@ int xpcs_do_config(struct dw_xpcs *xpcs, phy_interface_t interface, if (!compat) return -ENODEV; - if (xpcs->dev_flag == DW_DEV_TXGBE) { - ret = txgbe_xpcs_switch_mode(xpcs, interface); - if (ret) - return ret; - } + ret = xpcs_switch_interface_mode(compat, xpcs, interface, neg_mode); + if (ret) + return ret; switch (compat->an_mode) { case DW_10GBASER: -- 2.34.1