net: stmmac: configure SerDes according to the interface mode
authorChoong Yong Liang <yong.liang.choong@linux.intel.com>
Thu, 27 Feb 2025 12:15:21 +0000 (20:15 +0800)
committerJakub Kicinski <kuba@kernel.org>
Thu, 6 Mar 2025 03:02:47 +0000 (19:02 -0800)
Intel platform will configure the SerDes through PMC API based on the
provided interface mode.

This patch adds several new functions below:-
- intel_tsn_lane_is_available(): This new function reads FIA lane
  ownership registers and common lane registers through IPC commands
  to know which lane the mGbE port is assigned to.
- intel_mac_finish(): To configure the SerDes based on the assigned
  lane and latest interface mode, it sends IPC command to the PMC through
  PMC driver/API. The PMC acts as a proxy for R/W on behalf of the driver.
- intel_set_reg_access(): Set the register access to the available TSN
  interface.

Signed-off-by: Choong Yong Liang <yong.liang.choong@linux.intel.com>
Link: https://patch.msgid.link/20250227121522.1802832-6-yong.liang.choong@linux.intel.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/net/ethernet/stmicro/stmmac/Kconfig
drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c
drivers/net/ethernet/stmicro/stmmac/dwmac-intel.h

index 4cc85a36a1ab62c16d2b5dac57862606e12e78b4..c5f94a67b3f2fdea1a74cafc0c6d52c51a3cccb6 100644 (file)
@@ -307,6 +307,7 @@ config DWMAC_INTEL
        default X86
        depends on X86 && STMMAC_ETH && PCI
        depends on COMMON_CLK
+       depends on ACPI
        help
          This selects the Intel platform specific bus support for the
          stmmac driver. This driver is used for Intel Quark/EHL/TGL.
index 48acba5eb178e7bf2ca65c53e6c34c53dc29ae46..f73a48f985819a6972c50b406fc9cb8ca6ff4a03 100644 (file)
@@ -5,15 +5,30 @@
 #include <linux/clk-provider.h>
 #include <linux/pci.h>
 #include <linux/dmi.h>
+#include <linux/platform_data/x86/intel_pmc_ipc.h>
 #include "dwmac-intel.h"
 #include "dwmac4.h"
 #include "stmmac.h"
 #include "stmmac_ptp.h"
 
+struct pmc_serdes_regs {
+       u8 index;
+       u32 val;
+};
+
+struct pmc_serdes_reg_info {
+       const struct pmc_serdes_regs *regs;
+       u8 num_regs;
+};
+
 struct intel_priv_data {
        int mdio_adhoc_addr;    /* mdio address for serdes & etc */
        unsigned long crossts_adj;
        bool is_pse;
+       const int *tsn_lane_regs;
+       int max_tsn_lane_regs;
+       struct pmc_serdes_reg_info pid_1g;
+       struct pmc_serdes_reg_info pid_2p5g;
 };
 
 /* This struct is used to associate PCI Function of MAC controller on a board,
@@ -35,6 +50,44 @@ struct stmmac_pci_info {
        int (*setup)(struct pci_dev *pdev, struct plat_stmmacenet_data *plat);
 };
 
+static const struct pmc_serdes_regs pid_modphy3_1g_regs[] = {
+       { PID_MODPHY3_B_MODPHY_PCR_LCPLL_DWORD0,        B_MODPHY_PCR_LCPLL_DWORD0_1G },
+       { PID_MODPHY3_N_MODPHY_PCR_LCPLL_DWORD2,        N_MODPHY_PCR_LCPLL_DWORD2_1G },
+       { PID_MODPHY3_N_MODPHY_PCR_LCPLL_DWORD7,        N_MODPHY_PCR_LCPLL_DWORD7_1G },
+       { PID_MODPHY3_N_MODPHY_PCR_LPPLL_DWORD10,       N_MODPHY_PCR_LPPLL_DWORD10_1G },
+       { PID_MODPHY3_N_MODPHY_PCR_CMN_ANA_DWORD30,     N_MODPHY_PCR_CMN_ANA_DWORD30_1G },
+       {}
+};
+
+static const struct pmc_serdes_regs pid_modphy3_2p5g_regs[] = {
+       { PID_MODPHY3_B_MODPHY_PCR_LCPLL_DWORD0,        B_MODPHY_PCR_LCPLL_DWORD0_2P5G },
+       { PID_MODPHY3_N_MODPHY_PCR_LCPLL_DWORD2,        N_MODPHY_PCR_LCPLL_DWORD2_2P5G },
+       { PID_MODPHY3_N_MODPHY_PCR_LCPLL_DWORD7,        N_MODPHY_PCR_LCPLL_DWORD7_2P5G },
+       { PID_MODPHY3_N_MODPHY_PCR_LPPLL_DWORD10,       N_MODPHY_PCR_LPPLL_DWORD10_2P5G },
+       { PID_MODPHY3_N_MODPHY_PCR_CMN_ANA_DWORD30,     N_MODPHY_PCR_CMN_ANA_DWORD30_2P5G },
+       {}
+};
+
+static const struct pmc_serdes_regs pid_modphy1_1g_regs[] = {
+       { PID_MODPHY1_B_MODPHY_PCR_LCPLL_DWORD0,        B_MODPHY_PCR_LCPLL_DWORD0_1G },
+       { PID_MODPHY1_N_MODPHY_PCR_LCPLL_DWORD2,        N_MODPHY_PCR_LCPLL_DWORD2_1G },
+       { PID_MODPHY1_N_MODPHY_PCR_LCPLL_DWORD7,        N_MODPHY_PCR_LCPLL_DWORD7_1G },
+       { PID_MODPHY1_N_MODPHY_PCR_LPPLL_DWORD10,       N_MODPHY_PCR_LPPLL_DWORD10_1G },
+       { PID_MODPHY1_N_MODPHY_PCR_CMN_ANA_DWORD30,     N_MODPHY_PCR_CMN_ANA_DWORD30_1G },
+       {}
+};
+
+static const struct pmc_serdes_regs pid_modphy1_2p5g_regs[] = {
+       { PID_MODPHY1_B_MODPHY_PCR_LCPLL_DWORD0,        B_MODPHY_PCR_LCPLL_DWORD0_2P5G },
+       { PID_MODPHY1_N_MODPHY_PCR_LCPLL_DWORD2,        N_MODPHY_PCR_LCPLL_DWORD2_2P5G },
+       { PID_MODPHY1_N_MODPHY_PCR_LCPLL_DWORD7,        N_MODPHY_PCR_LCPLL_DWORD7_2P5G },
+       { PID_MODPHY1_N_MODPHY_PCR_LPPLL_DWORD10,       N_MODPHY_PCR_LPPLL_DWORD10_2P5G },
+       { PID_MODPHY1_N_MODPHY_PCR_CMN_ANA_DWORD30,     N_MODPHY_PCR_CMN_ANA_DWORD30_2P5G },
+       {}
+};
+
+static const int ehl_tsn_lane_regs[] = {7, 8, 9, 10, 11};
+
 static int stmmac_pci_find_phy_addr(struct pci_dev *pdev,
                                    const struct dmi_system_id *dmi_list)
 {
@@ -93,7 +146,7 @@ static int intel_serdes_powerup(struct net_device *ndev, void *priv_data)
        data &= ~SERDES_RATE_MASK;
        data &= ~SERDES_PCLK_MASK;
 
-       if (priv->plat->max_speed == 2500)
+       if (priv->plat->phy_interface == PHY_INTERFACE_MODE_2500BASEX)
                data |= SERDES_RATE_PCIE_GEN2 << SERDES_RATE_PCIE_SHIFT |
                        SERDES_PCLK_37p5MHZ << SERDES_PCLK_SHIFT;
        else
@@ -415,6 +468,95 @@ static void intel_mgbe_pse_crossts_adj(struct intel_priv_data *intel_priv,
        }
 }
 
+static int intel_tsn_lane_is_available(struct net_device *ndev,
+                                      struct intel_priv_data *intel_priv)
+{
+       struct stmmac_priv *priv = netdev_priv(ndev);
+       struct pmc_ipc_cmd tmp = {};
+       struct pmc_ipc_rbuf rbuf = {};
+       int ret = 0, i, j;
+       const int max_fia_regs = 5;
+
+       tmp.cmd = IPC_SOC_REGISTER_ACCESS;
+       tmp.sub_cmd = IPC_SOC_SUB_CMD_READ;
+
+       for (i = 0; i < max_fia_regs; i++) {
+               tmp.wbuf[0] = R_PCH_FIA_15_PCR_LOS1_REG_BASE + i;
+
+               ret = intel_pmc_ipc(&tmp, &rbuf);
+               if (ret < 0) {
+                       netdev_info(priv->dev, "Failed to read from PMC.\n");
+                       return ret;
+               }
+
+               for (j = 0; j <= intel_priv->max_tsn_lane_regs; j++)
+                       if ((rbuf.buf[0] >>
+                               (4 * (intel_priv->tsn_lane_regs[j] % 8)) &
+                                       B_PCH_FIA_PCR_L0O) == 0xB)
+                               return ret;
+       }
+
+       return ret;
+}
+
+static int intel_set_reg_access(const struct pmc_serdes_regs *regs, int max_regs)
+{
+       int ret = 0, i;
+
+       for (i = 0; i < max_regs; i++) {
+               struct pmc_ipc_cmd tmp = {};
+               struct pmc_ipc_rbuf rbuf = {};
+
+               tmp.cmd = IPC_SOC_REGISTER_ACCESS;
+               tmp.sub_cmd = IPC_SOC_SUB_CMD_WRITE;
+               tmp.wbuf[0] = (u32)regs[i].index;
+               tmp.wbuf[1] = regs[i].val;
+
+               ret = intel_pmc_ipc(&tmp, &rbuf);
+               if (ret < 0)
+                       return ret;
+       }
+
+       return ret;
+}
+
+static int intel_mac_finish(struct net_device *ndev,
+                           void *intel_data,
+                           unsigned int mode,
+                           phy_interface_t interface)
+{
+       struct intel_priv_data *intel_priv = intel_data;
+       struct stmmac_priv *priv = netdev_priv(ndev);
+       const struct pmc_serdes_regs *regs;
+       int max_regs = 0;
+       int ret = 0;
+
+       ret = intel_tsn_lane_is_available(ndev, intel_priv);
+       if (ret < 0) {
+               netdev_info(priv->dev, "No TSN lane available to set the registers.\n");
+               return ret;
+       }
+
+       if (interface == PHY_INTERFACE_MODE_2500BASEX) {
+               regs = intel_priv->pid_2p5g.regs;
+               max_regs = intel_priv->pid_2p5g.num_regs;
+       } else {
+               regs = intel_priv->pid_1g.regs;
+               max_regs = intel_priv->pid_1g.num_regs;
+       }
+
+       ret = intel_set_reg_access(regs, max_regs);
+       if (ret < 0)
+               return ret;
+
+       priv->plat->phy_interface = interface;
+
+       intel_serdes_powerdown(ndev, intel_priv);
+       intel_serdes_powerup(ndev, intel_priv);
+
+       return ret;
+}
+
 static void common_default_data(struct plat_stmmacenet_data *plat)
 {
        plat->clk_csr = 2;      /* clk_csr_i = 20-35MHz & MDC = clk_csr_i/16 */
@@ -624,6 +766,8 @@ static int intel_mgbe_common_data(struct pci_dev *pdev,
 static int ehl_common_data(struct pci_dev *pdev,
                           struct plat_stmmacenet_data *plat)
 {
+       struct intel_priv_data *intel_priv = plat->bsp_priv;
+
        plat->rx_queues_to_use = 8;
        plat->tx_queues_to_use = 8;
        plat->flags |= STMMAC_FLAG_USE_PHY_WOL;
@@ -639,20 +783,29 @@ static int ehl_common_data(struct pci_dev *pdev,
        plat->safety_feat_cfg->prtyen = 0;
        plat->safety_feat_cfg->tmouten = 0;
 
+       intel_priv->tsn_lane_regs = ehl_tsn_lane_regs;
+       intel_priv->max_tsn_lane_regs = ARRAY_SIZE(ehl_tsn_lane_regs);
+
        return intel_mgbe_common_data(pdev, plat);
 }
 
 static int ehl_sgmii_data(struct pci_dev *pdev,
                          struct plat_stmmacenet_data *plat)
 {
+       struct intel_priv_data *intel_priv = plat->bsp_priv;
+
        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;
-
+       plat->mac_finish = intel_mac_finish;
        plat->clk_ptp_rate = 204800000;
 
+       intel_priv->pid_1g.regs = pid_modphy3_1g_regs;
+       intel_priv->pid_1g.num_regs = ARRAY_SIZE(pid_modphy3_1g_regs);
+       intel_priv->pid_2p5g.regs = pid_modphy3_2p5g_regs;
+       intel_priv->pid_2p5g.num_regs = ARRAY_SIZE(pid_modphy3_2p5g_regs);
+
        return ehl_common_data(pdev, plat);
 }
 
@@ -705,10 +858,18 @@ static struct stmmac_pci_info ehl_pse0_rgmii1g_info = {
 static int ehl_pse0_sgmii1g_data(struct pci_dev *pdev,
                                 struct plat_stmmacenet_data *plat)
 {
+       struct intel_priv_data *intel_priv = plat->bsp_priv;
+
        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;
+       plat->mac_finish = intel_mac_finish;
+
+       intel_priv->pid_1g.regs = pid_modphy1_1g_regs;
+       intel_priv->pid_1g.num_regs = ARRAY_SIZE(pid_modphy1_1g_regs);
+       intel_priv->pid_2p5g.regs = pid_modphy1_2p5g_regs;
+       intel_priv->pid_2p5g.num_regs = ARRAY_SIZE(pid_modphy1_2p5g_regs);
+
        return ehl_pse0_common_data(pdev, plat);
 }
 
@@ -746,10 +907,18 @@ static struct stmmac_pci_info ehl_pse1_rgmii1g_info = {
 static int ehl_pse1_sgmii1g_data(struct pci_dev *pdev,
                                 struct plat_stmmacenet_data *plat)
 {
+       struct intel_priv_data *intel_priv = plat->bsp_priv;
+
        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;
+       plat->mac_finish = intel_mac_finish;
+
+       intel_priv->pid_1g.regs = pid_modphy1_1g_regs;
+       intel_priv->pid_1g.num_regs = ARRAY_SIZE(pid_modphy1_1g_regs);
+       intel_priv->pid_2p5g.regs = pid_modphy1_2p5g_regs;
+       intel_priv->pid_2p5g.num_regs = ARRAY_SIZE(pid_modphy1_2p5g_regs);
+
        return ehl_pse1_common_data(pdev, plat);
 }
 
index 0a37987478c15ac2bceabd233be5b1c8a1851b99..a12f8e65f89f1616e71aee30c9e923fd8b5d80fd 100644 (file)
 #define PCH_PTP_CLK_FREQ_19_2MHZ       (GMAC_GPO0)
 #define PCH_PTP_CLK_FREQ_200MHZ                (0)
 
+/* Modphy Register index */
+#define R_PCH_FIA_15_PCR_LOS1_REG_BASE                 8
+#define R_PCH_FIA_15_PCR_LOS2_REG_BASE                 9
+#define R_PCH_FIA_15_PCR_LOS3_REG_BASE                 10
+#define R_PCH_FIA_15_PCR_LOS4_REG_BASE                 11
+#define R_PCH_FIA_15_PCR_LOS5_REG_BASE                 12
+#define B_PCH_FIA_PCR_L0O                              GENMASK(3, 0)
+#define PID_MODPHY1_B_MODPHY_PCR_LCPLL_DWORD0          13
+#define PID_MODPHY1_N_MODPHY_PCR_LCPLL_DWORD2          14
+#define PID_MODPHY1_N_MODPHY_PCR_LCPLL_DWORD7          15
+#define PID_MODPHY1_N_MODPHY_PCR_LPPLL_DWORD10         16
+#define PID_MODPHY1_N_MODPHY_PCR_CMN_ANA_DWORD30       17
+#define PID_MODPHY3_B_MODPHY_PCR_LCPLL_DWORD0          18
+#define PID_MODPHY3_N_MODPHY_PCR_LCPLL_DWORD2          19
+#define PID_MODPHY3_N_MODPHY_PCR_LCPLL_DWORD7          20
+#define PID_MODPHY3_N_MODPHY_PCR_LPPLL_DWORD10         21
+#define PID_MODPHY3_N_MODPHY_PCR_CMN_ANA_DWORD30       22
+
+#define B_MODPHY_PCR_LCPLL_DWORD0_1G           0x46AAAA41
+#define N_MODPHY_PCR_LCPLL_DWORD2_1G           0x00000139
+#define N_MODPHY_PCR_LCPLL_DWORD7_1G           0x002A0003
+#define N_MODPHY_PCR_LPPLL_DWORD10_1G          0x00170008
+#define N_MODPHY_PCR_CMN_ANA_DWORD30_1G                0x0000D4AC
+#define B_MODPHY_PCR_LCPLL_DWORD0_2P5G         0x58555551
+#define N_MODPHY_PCR_LCPLL_DWORD2_2P5G         0x0000012D
+#define N_MODPHY_PCR_LCPLL_DWORD7_2P5G         0x001F0003
+#define N_MODPHY_PCR_LPPLL_DWORD10_2P5G                0x00170008
+#define N_MODPHY_PCR_CMN_ANA_DWORD30_2P5G      0x8200ACAC
+
 #endif /* __DWMAC_INTEL_H__ */