net: stmmac: enable Intel mGbE 2.5Gbps link speed
authorVoon Weifeng <weifeng.voon@intel.com>
Tue, 8 Jun 2021 03:51:58 +0000 (11:51 +0800)
committerDavid S. Miller <davem@davemloft.net>
Tue, 8 Jun 2021 21:31:43 +0000 (14:31 -0700)
The Intel mGbE supports 2.5Gbps link speed by increasing the clock rate by
2.5 times of the original rate. In this mode, the serdes/PHY operates at a
serial baud rate of 3.125 Gbps and the PCS data path and GMII interface of
the MAC operate at 312.5 MHz instead of 125 MHz.

For Intel mGbE, the overclocking of 2.5 times clock rate to support 2.5G is
only able to be configured in the BIOS during boot time. Kernel driver has
no access to modify the clock rate for 1Gbps/2.5G mode. The way to
determined the current 1G/2.5G mode is by reading a dedicated adhoc
register through mdio bus. In short, after the system boot up, it is either
in 1G mode or 2.5G mode which not able to be changed on the fly.

Compared to 1G mode, the 2.5G mode selects the 2500BASEX as PHY interface and
disables the xpcs_an_inband. This is to cater for some PHYs that only
supports 2500BASEX PHY interface with no autonegotiation.

v2: remove MAC supported link speed masking
v3: Restructure  to introduce intel_speed_mode_2500() to read serdes registers
    for max speed supported and select the appropritate configuration.
    Use max_speed to determine the supported link speed mask.

Signed-off-by: Voon Weifeng <weifeng.voon@intel.com>
Signed-off-by: Michael Sit Wei Hong <michael.wei.hong.sit@intel.com>
Reviewed-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c
drivers/net/ethernet/stmicro/stmmac/dwmac-intel.h
drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
include/linux/stmmac.h

index 2ecf93c..6a9a19b 100644 (file)
@@ -102,6 +102,22 @@ static int intel_serdes_powerup(struct net_device *ndev, void *priv_data)
 
        serdes_phy_addr = intel_priv->mdio_adhoc_addr;
 
+       /* Set the serdes rate and the PCLK rate */
+       data = mdiobus_read(priv->mii, serdes_phy_addr,
+                           SERDES_GCR0);
+
+       data &= ~SERDES_RATE_MASK;
+       data &= ~SERDES_PCLK_MASK;
+
+       if (priv->plat->max_speed == 2500)
+               data |= SERDES_RATE_PCIE_GEN2 << SERDES_RATE_PCIE_SHIFT |
+                       SERDES_PCLK_37p5MHZ << SERDES_PCLK_SHIFT;
+       else
+               data |= SERDES_RATE_PCIE_GEN1 << SERDES_RATE_PCIE_SHIFT |
+                       SERDES_PCLK_70MHZ << SERDES_PCLK_SHIFT;
+
+       mdiobus_write(priv->mii, serdes_phy_addr, SERDES_GCR0, data);
+
        /* assert clk_req */
        data = mdiobus_read(priv->mii, serdes_phy_addr, SERDES_GCR0);
        data |= SERDES_PLL_CLK;
@@ -230,6 +246,32 @@ static void intel_serdes_powerdown(struct net_device *ndev, void *intel_data)
        }
 }
 
+static void intel_speed_mode_2500(struct net_device *ndev, void *intel_data)
+{
+       struct intel_priv_data *intel_priv = intel_data;
+       struct stmmac_priv *priv = netdev_priv(ndev);
+       int serdes_phy_addr = 0;
+       u32 data = 0;
+
+       serdes_phy_addr = intel_priv->mdio_adhoc_addr;
+
+       /* Determine the link speed mode: 2.5Gbps/1Gbps */
+       data = mdiobus_read(priv->mii, serdes_phy_addr,
+                           SERDES_GCR);
+
+       if (((data & SERDES_LINK_MODE_MASK) >> SERDES_LINK_MODE_SHIFT) ==
+           SERDES_LINK_MODE_2G5) {
+               dev_info(priv->device, "Link Speed Mode: 2.5Gbps\n");
+               priv->plat->max_speed = 2500;
+               priv->plat->phy_interface = PHY_INTERFACE_MODE_2500BASEX;
+               priv->plat->mdio_bus_data->xpcs_an_inband = false;
+       } else {
+               priv->plat->max_speed = 1000;
+               priv->plat->phy_interface = PHY_INTERFACE_MODE_SGMII;
+               priv->plat->mdio_bus_data->xpcs_an_inband = true;
+       }
+}
+
 /* Program PTP Clock Frequency for different variant of
  * Intel mGBE that has slightly different GPO mapping
  */
@@ -586,7 +628,7 @@ static int ehl_sgmii_data(struct pci_dev *pdev,
 {
        plat->bus_id = 1;
        plat->phy_interface = PHY_INTERFACE_MODE_SGMII;
-
+       plat->speed_mode_2500 = intel_speed_mode_2500;
        plat->serdes_powerup = intel_serdes_powerup;
        plat->serdes_powerdown = intel_serdes_powerdown;
 
@@ -639,6 +681,7 @@ static int ehl_pse0_sgmii1g_data(struct pci_dev *pdev,
                                 struct plat_stmmacenet_data *plat)
 {
        plat->phy_interface = PHY_INTERFACE_MODE_SGMII;
+       plat->speed_mode_2500 = intel_speed_mode_2500;
        plat->serdes_powerup = intel_serdes_powerup;
        plat->serdes_powerdown = intel_serdes_powerdown;
        return ehl_pse0_common_data(pdev, plat);
@@ -677,6 +720,7 @@ static int ehl_pse1_sgmii1g_data(struct pci_dev *pdev,
                                 struct plat_stmmacenet_data *plat)
 {
        plat->phy_interface = PHY_INTERFACE_MODE_SGMII;
+       plat->speed_mode_2500 = intel_speed_mode_2500;
        plat->serdes_powerup = intel_serdes_powerup;
        plat->serdes_powerdown = intel_serdes_powerdown;
        return ehl_pse1_common_data(pdev, plat);
@@ -711,6 +755,7 @@ static int tgl_sgmii_phy0_data(struct pci_dev *pdev,
 {
        plat->bus_id = 1;
        plat->phy_interface = PHY_INTERFACE_MODE_SGMII;
+       plat->speed_mode_2500 = intel_speed_mode_2500;
        plat->serdes_powerup = intel_serdes_powerup;
        plat->serdes_powerdown = intel_serdes_powerdown;
        return tgl_common_data(pdev, plat);
@@ -725,6 +770,7 @@ static int tgl_sgmii_phy1_data(struct pci_dev *pdev,
 {
        plat->bus_id = 2;
        plat->phy_interface = PHY_INTERFACE_MODE_SGMII;
+       plat->speed_mode_2500 = intel_speed_mode_2500;
        plat->serdes_powerup = intel_serdes_powerup;
        plat->serdes_powerdown = intel_serdes_powerdown;
        return tgl_common_data(pdev, plat);
index 542acb8..20d14e5 100644 (file)
@@ -9,6 +9,7 @@
 #define POLL_DELAY_US 8
 
 /* SERDES Register */
+#define SERDES_GCR     0x0     /* Global Conguration */
 #define SERDES_GSR0    0x5     /* Global Status Reg0 */
 #define SERDES_GCR0    0xb     /* Global Configuration Reg0 */
 
 #define SERDES_PHY_RX_CLK      BIT(1)          /* PSE SGMII PHY rx clk */
 #define SERDES_RST             BIT(2)          /* Serdes Reset */
 #define SERDES_PWR_ST_MASK     GENMASK(6, 4)   /* Serdes Power state*/
+#define SERDES_RATE_MASK       GENMASK(9, 8)
+#define SERDES_PCLK_MASK       GENMASK(14, 12) /* PCLK rate to PHY */
+#define SERDES_LINK_MODE_MASK  GENMASK(2, 1)
+#define SERDES_LINK_MODE_SHIFT 1
 #define SERDES_PWR_ST_SHIFT    4
 #define SERDES_PWR_ST_P0       0x0
 #define SERDES_PWR_ST_P3       0x3
+#define SERDES_LINK_MODE_2G5   0x3
+#define SERSED_LINK_MODE_1G    0x2
+#define SERDES_PCLK_37p5MHZ    0x0
+#define SERDES_PCLK_70MHZ      0x1
+#define SERDES_RATE_PCIE_GEN1  0x0
+#define SERDES_RATE_PCIE_GEN2  0x1
+#define SERDES_RATE_PCIE_SHIFT 8
+#define SERDES_PCLK_SHIFT      12
 
 #endif /* __DWMAC_INTEL_H__ */
index f35c03c..67ba083 100644 (file)
@@ -1358,6 +1358,7 @@ int dwmac4_setup(struct stmmac_priv *priv)
        mac->link.speed10 = GMAC_CONFIG_PS;
        mac->link.speed100 = GMAC_CONFIG_FES | GMAC_CONFIG_PS;
        mac->link.speed1000 = 0;
+       mac->link.speed2500 = GMAC_CONFIG_FES;
        mac->link.speed_mask = GMAC_CONFIG_FES | GMAC_CONFIG_PS;
        mac->mii.addr = GMAC_MDIO_ADDR;
        mac->mii.data = GMAC_MDIO_DATA;
index af406ea..1b12a2f 100644 (file)
@@ -931,6 +931,10 @@ static void stmmac_validate(struct phylink_config *config,
        if ((max_speed > 0) && (max_speed < 1000)) {
                phylink_set(mask, 1000baseT_Full);
                phylink_set(mask, 1000baseX_Full);
+       } else if (priv->plat->has_gmac4) {
+               if (!max_speed || max_speed >= 2500)
+                       phylink_set(mac_supported, 2500baseT_Full);
+                       phylink_set(mac_supported, 2500baseX_Full);
        } else if (priv->plat->has_xgmac) {
                if (!max_speed || (max_speed >= 2500)) {
                        phylink_set(mac_supported, 2500baseT_Full);
@@ -6993,6 +6997,9 @@ int stmmac_dvr_probe(struct device *device,
                }
        }
 
+       if (priv->plat->speed_mode_2500)
+               priv->plat->speed_mode_2500(ndev, priv->plat->bsp_priv);
+
        if (priv->plat->mdio_bus_data) {
                if (priv->plat->mdio_bus_data->has_xpcs) {
                        ret = stmmac_xpcs_setup(priv->mii);
index e55a480..b10be33 100644 (file)
@@ -223,6 +223,7 @@ struct plat_stmmacenet_data {
        void (*fix_mac_speed)(void *priv, unsigned int speed);
        int (*serdes_powerup)(struct net_device *ndev, void *priv);
        void (*serdes_powerdown)(struct net_device *ndev, void *priv);
+       void (*speed_mode_2500)(struct net_device *ndev, void *priv);
        void (*ptp_clk_freq_config)(void *priv);
        int (*init)(struct platform_device *pdev, void *priv);
        void (*exit)(struct platform_device *pdev, void *priv);