net: pcs: xpcs: add support for sgmii with no inband AN
authorVladimir Oltean <vladimir.oltean@nxp.com>
Fri, 11 Jun 2021 20:05:23 +0000 (23:05 +0300)
committerDavid S. Miller <davem@davemloft.net>
Fri, 11 Jun 2021 20:43:55 +0000 (13:43 -0700)
In fixed-link use cases, the XPCS can disable the clause 37 in-band
autoneg process, disable the "Automatic Speed Mode Change after CL37 AN"
setting, and force operation in a speed dictated by management.

Add support for this operating mode.

Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/pcs/pcs-xpcs.c

index 8ca7592..743b537 100644 (file)
@@ -690,7 +690,7 @@ int xpcs_config_eee(struct dw_xpcs *xpcs, int mult_fact_100ns, int enable)
 }
 EXPORT_SYMBOL_GPL(xpcs_config_eee);
 
-static int xpcs_config_aneg_c37_sgmii(struct dw_xpcs *xpcs)
+static int xpcs_config_aneg_c37_sgmii(struct dw_xpcs *xpcs, unsigned int mode)
 {
        int ret;
 
@@ -726,7 +726,10 @@ static int xpcs_config_aneg_c37_sgmii(struct dw_xpcs *xpcs)
        if (ret < 0)
                return ret;
 
-       ret |= DW_VR_MII_DIG_CTRL1_MAC_AUTO_SW;
+       if (phylink_autoneg_inband(mode))
+               ret |= DW_VR_MII_DIG_CTRL1_MAC_AUTO_SW;
+       else
+               ret &= ~DW_VR_MII_DIG_CTRL1_MAC_AUTO_SW;
 
        return xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_DIG_CTRL1, ret);
 }
@@ -772,7 +775,7 @@ static int xpcs_do_config(struct dw_xpcs *xpcs, phy_interface_t interface,
                }
                break;
        case DW_AN_C37_SGMII:
-               ret = xpcs_config_aneg_c37_sgmii(xpcs);
+               ret = xpcs_config_aneg_c37_sgmii(xpcs, mode);
                if (ret)
                        return ret;
                break;
@@ -905,6 +908,36 @@ static void xpcs_get_state(struct phylink_pcs *pcs,
        }
 }
 
+static void xpcs_link_up_sgmii(struct dw_xpcs *xpcs, unsigned int mode,
+                              int speed, int duplex)
+{
+       int val, ret;
+
+       if (phylink_autoneg_inband(mode))
+               return;
+
+       switch (speed) {
+       case SPEED_1000:
+               val = BMCR_SPEED1000;
+               break;
+       case SPEED_100:
+               val = BMCR_SPEED100;
+               break;
+       case SPEED_10:
+               val = BMCR_SPEED10;
+               break;
+       default:
+               return;
+       }
+
+       if (duplex == DUPLEX_FULL)
+               val |= BMCR_FULLDPLX;
+
+       ret = xpcs_write(xpcs, MDIO_MMD_VEND2, MDIO_CTRL1, val);
+       if (ret)
+               pr_err("%s: xpcs_write returned %pe\n", __func__, ERR_PTR(ret));
+}
+
 static void xpcs_link_up(struct phylink_pcs *pcs, unsigned int mode,
                         phy_interface_t interface, int speed, int duplex)
 {
@@ -912,6 +945,8 @@ static void xpcs_link_up(struct phylink_pcs *pcs, unsigned int mode,
 
        if (interface == PHY_INTERFACE_MODE_USXGMII)
                return xpcs_config_usxgmii(xpcs, speed);
+       if (interface == PHY_INTERFACE_MODE_SGMII)
+               return xpcs_link_up_sgmii(xpcs, mode, speed, duplex);
 }
 
 static u32 xpcs_get_id(struct dw_xpcs *xpcs)