From: Voon Weifeng <weifeng.v...@intel.com>

Intel mgbe supports 2.5G mode operation when PCS is in 1000BASE-X mode.
The 2.5G mode of operation is functionally same as 1000BASE-X mode,
except that the clock rate is 2.5 times the original rate. In 2.5G mode,
the link will operate as 2500Mbps/250Mbps/25Mbps. Hence, 2.5Gbps
link speed will be mutually exclusive with 1000Mbps/100Mbps/10Mbps.

Signed-off-by: Voon Weifeng <weifeng.v...@intel.com>
Signed-off-by: Michael Sit Wei Hong <michael.wei.hong....@intel.com>
---
 .../net/ethernet/stmicro/stmmac/dwmac-intel.c | 44 ++++++++++++++++++-
 .../net/ethernet/stmicro/stmmac/dwmac-intel.h | 13 ++++++
 .../net/ethernet/stmicro/stmmac/dwmac4_core.c |  1 +
 .../net/ethernet/stmicro/stmmac/stmmac_main.c | 26 ++++++++++-
 include/linux/stmmac.h                        |  2 +
 5 files changed, 83 insertions(+), 3 deletions(-)

diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c 
b/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c
index 3d9a57043af2..4f70a12b42f9 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c
@@ -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->speed_2500_en)
+               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;
@@ -220,6 +236,28 @@ static void intel_serdes_powerdown(struct net_device 
*ndev, void *intel_data)
        }
 }
 
+static bool 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");
+               return true;
+       } else {
+               return false;
+       }
+}
+
 /* Program PTP Clock Frequency for different variant of
  * Intel mGBE that has slightly different GPO mapping
  */
@@ -540,7 +578,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;
 
@@ -593,6 +631,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);
@@ -631,6 +670,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);
@@ -655,6 +695,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);
@@ -669,6 +710,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);
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.h 
b/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.h
index e723096c0b15..021a5c178d97 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.h
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.h
@@ -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 */
 
@@ -16,8 +17,20 @@
 #define SERDES_PLL_CLK         BIT(0)          /* PLL clk valid signal */
 #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__ */
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c 
b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c
index 95864f014ffa..4c3f27a9e8b2 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c
@@ -1357,6 +1357,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;
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c 
b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index d34388b1ffcc..e182f9be4247 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -882,6 +882,18 @@ static void stmmac_validate(struct phylink_config *config,
        phylink_set(mac_supported, Asym_Pause);
        phylink_set_port_modes(mac_supported);
 
+       /* 2.5G mode only support 2500baseT full duplex only */
+       if (priv->plat->has_gmac4 && priv->plat->speed_2500_en) {
+               phylink_set(mac_supported, 2500baseT_Full);
+               phylink_set(mask, 10baseT_Half);
+               phylink_set(mask, 10baseT_Full);
+               phylink_set(mask, 100baseT_Half);
+               phylink_set(mask, 100baseT_Full);
+               phylink_set(mask, 1000baseT_Half);
+               phylink_set(mask, 1000baseT_Full);
+               phylink_set(mask, 1000baseKX_Full);
+       }
+
        /* Cut down 1G if asked to */
        if ((max_speed > 0) && (max_speed < 1000)) {
                phylink_set(mask, 1000baseT_Full);
@@ -1192,8 +1204,13 @@ static int stmmac_phy_setup(struct stmmac_priv *priv)
        priv->phylink_config.dev = &priv->dev->dev;
        priv->phylink_config.type = PHYLINK_NETDEV;
        priv->phylink_config.pcs_poll = true;
-       priv->phylink_config.ovr_an_inband =
-               priv->plat->mdio_bus_data->xpcs_an_inband;
+       /* For 2.5G, we do not use SGMII in-band AN */
+       if (priv->plat->speed_2500_en) {
+               priv->phylink_config.ovr_an_inband = false;
+       } else {
+               priv->phylink_config.ovr_an_inband =
+                       priv->plat->mdio_bus_data->xpcs_an_inband;
+       }
 
        if (!fwnode)
                fwnode = dev_fwnode(priv->device);
@@ -5815,6 +5832,11 @@ int stmmac_dvr_probe(struct device *device,
                }
        }
 
+       if (priv->plat->speed_mode_2500) {
+               priv->plat->speed_2500_en = priv->plat->speed_mode_2500(ndev,
+                                                                       
priv->plat->bsp_priv);
+       }
+
        ret = stmmac_phy_setup(priv);
        if (ret) {
                netdev_err(ndev, "failed to setup phy (%d)\n", ret);
diff --git a/include/linux/stmmac.h b/include/linux/stmmac.h
index e338ef7abc00..a43ce24d2a42 100644
--- a/include/linux/stmmac.h
+++ b/include/linux/stmmac.h
@@ -209,6 +209,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);
+       bool (*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);
@@ -234,6 +235,7 @@ struct plat_stmmacenet_data {
        int has_xgmac;
        bool vlan_fail_q_en;
        u8 vlan_fail_q;
+       bool speed_2500_en;
        unsigned int eee_usecs_rate;
        struct pci_dev *pdev;
        bool has_crossts;
-- 
2.17.1

Reply via email to