summaryrefslogtreecommitdiff
diff options
authorPhilip Cox <philip.cox@canonical.com>2022-09-29 15:16:31 -0400
committerPhilip Cox <philip.cox@canonical.com>2022-11-14 14:16:06 -0500
commitd083fab802b582a2b4543ae6c59d016b53aab2c0 (patch)
treeae12407fad569fcfaa6dd47f07e7c56ecfafd60a
parentd0bb42ad6b726056781f9c8a83f003e0e478017a (diff)
net: stmmac: enable Intel mGbE 1G/2.5G auto-negotiation support
BugLink: https://bugs.launchpad.net/bugs/1982282 Initially, Intel mGbE was only able to configure the overclocking of 2.5 times clock rate to enable 2.5Gbps in the BIOS during boot time. Kernel driver had no access to modify the clock rate for 1G/2.5G mode at runtime. Now, this patch enables the runtime 1G/2.5G auto-negotiation support to gets rid of the dependency on BIOS to change the 1G/2.5G clock rate. This patch adds several new functions below:- - intel_check_TSN_interface(): This new function reads FIA lane ownership registers and common lane registers through IPC1 commands to know which lane the mGbE port is assigned to. - stmmac_mac_prepare(): To obtain the latest PHY interface from phylink during initialization and call intel_config_serdes() to proceed with SERDES configuration. - intel_config_serdes(): To configure the SERDES based on the assigned lane and latest PHY interface, it sends IPC1 command to the PMC through PMC driver/API. The PMC acts as a proxy for R/W on behalf of the driver. Signed-off-by: Tan, Tee Min <tee.min.tan@intel.com> (picked from https://github.com/intel/linux-intel-quilt/tree/lts-v5.15.36-linux-220520T033542Z-1/patches/0039-net-stmmac-enable-Intel-mGbE-1G-2.5G-auto-negotiatio.patch) Signed-off-by: Philip Cox <philip.cox@canonical.com>
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/Kconfig2
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c131
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-intel.h72
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_main.c20
-rw-r--r--include/linux/stmmac.h1
5 files changed, 222 insertions, 4 deletions
diff --git a/drivers/net/ethernet/stmicro/stmmac/Kconfig b/drivers/net/ethernet/stmicro/stmmac/Kconfig
index 6bf92356dacd..053ede019818 100644
--- a/drivers/net/ethernet/stmicro/stmmac/Kconfig
+++ b/drivers/net/ethernet/stmicro/stmmac/Kconfig
@@ -225,6 +225,7 @@ config DWMAC_INTEL_PLAT
tristate "Intel dwmac support"
depends on OF && COMMON_CLK
depends on STMMAC_ETH
+ select INTEL_PMC_CORE
help
Support for ethernet controllers on Intel SoCs
@@ -246,6 +247,7 @@ config DWMAC_INTEL
default X86
depends on X86 && STMMAC_ETH && PCI
depends on COMMON_CLK
+ select INTEL_PMC_CORE
help
This selects the Intel platform specific bus support for the
stmmac driver. This driver is used for Intel Quark/EHL/TGL.
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c
index 6c4bf980678f..1d3940e7bb49 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c
@@ -6,6 +6,7 @@
#include <linux/pci.h>
#include <linux/dmi.h>
#include <linux/pm_runtime.h>
+#include <linux/intel_pmc_core.h>
#include "dwmac-intel.h"
#include "dwmac4.h"
#include "stmmac.h"
@@ -94,7 +95,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
@@ -412,6 +413,125 @@ static void intel_mgbe_pse_crossts_adj(struct intel_priv_data *intel_priv,
}
}
+#if IS_ENABLED(CONFIG_INTEL_PMC_CORE)
+static bool intel_tsn_interface_is_available(struct net_device *ndev)
+{
+ struct stmmac_priv *priv = netdev_priv(ndev);
+ struct pmc_ipc_cmd tmp = {0};
+ bool has_tsn_interface = false;
+ u32 rbuf[4] = {0};
+ int ret, i, lane;
+
+ if (priv->plat->serdes_powerup) {
+ tmp.cmd = IPC_SOC_REGISTER_ACCESS;
+ tmp.sub_cmd = IPC_SOC_SUB_CMD_READ;
+
+ for (i = 0; i < 5; i++) {
+ tmp.wbuf[0] = R_PCH_FIA_15_PCR_LOS1_REG_BASE + i;
+
+ ret = intel_pmc_core_ipc(&tmp, rbuf);
+ if (ret < 0) {
+ netdev_info(priv->dev,
+ "Failed to read from PMC.\n");
+ return false;
+ }
+
+ /* Possible lanes for TSN are from 7 to 11 */
+ for (lane = 7; lane <= 11; lane++)
+ if ((rbuf[0] >> (4 * (lane % 8)) &
+ B_PCH_FIA_PCR_L0O) == 0xB)
+ has_tsn_interface = true;
+ }
+ }
+ return has_tsn_interface;
+}
+
+static int intel_config_serdes(struct net_device *ndev, void *intel_data)
+{
+ struct intel_priv_data *intel_priv = intel_data;
+ struct stmmac_priv *priv = netdev_priv(ndev);
+ int ret = 0, i;
+
+ if (!intel_tsn_interface_is_available(ndev)) {
+ netdev_info(priv->dev, "TSN interface not found.\n");
+ goto pmc_read_error;
+ }
+
+ if (intel_priv->is_pse) {
+ if (priv->plat->phy_interface == PHY_INTERFACE_MODE_2500BASEX) {
+ for (i = 0; i < ARRAY_SIZE(pse_2p5g_regs); i++) {
+ struct pmc_ipc_cmd tmp = {0};
+ u32 buf[4] = {0};
+
+ tmp.cmd = IPC_SOC_REGISTER_ACCESS;
+ tmp.sub_cmd = IPC_SOC_SUB_CMD_WRITE;
+ tmp.wbuf[0] = (u32)pse_2p5g_regs[i].index;
+ tmp.wbuf[1] = pse_2p5g_regs[i].val;
+
+ ret = intel_pmc_core_ipc(&tmp, buf);
+ if (ret < 0)
+ goto pmc_read_error;
+ }
+ } else {
+ for (i = 0; i < ARRAY_SIZE(pse_1g_regs); i++) {
+ struct pmc_ipc_cmd tmp = {0};
+ u32 buf[4] = {0};
+
+ tmp.cmd = IPC_SOC_REGISTER_ACCESS;
+ tmp.sub_cmd = IPC_SOC_SUB_CMD_WRITE;
+ tmp.wbuf[0] = (u32)pse_1g_regs[i].index;
+ tmp.wbuf[1] = pse_1g_regs[i].val;
+
+ ret = intel_pmc_core_ipc(&tmp, buf);
+ if (ret < 0)
+ goto pmc_read_error;
+ }
+ }
+ } else {
+ if (priv->plat->phy_interface == PHY_INTERFACE_MODE_2500BASEX) {
+ for (i = 0; i < ARRAY_SIZE(pch_2p5g_regs); i++) {
+ struct pmc_ipc_cmd tmp = {0};
+ u32 buf[4] = {0};
+
+ tmp.cmd = IPC_SOC_REGISTER_ACCESS;
+ tmp.sub_cmd = IPC_SOC_SUB_CMD_WRITE;
+ tmp.wbuf[0] = (u32)pch_2p5g_regs[i].index;
+ tmp.wbuf[1] = pch_2p5g_regs[i].val;
+
+ ret = intel_pmc_core_ipc(&tmp, buf);
+ if (ret < 0)
+ goto pmc_read_error;
+ }
+ } else {
+ for (i = 0; i < ARRAY_SIZE(pch_1g_regs); i++) {
+ struct pmc_ipc_cmd tmp = {0};
+ u32 buf[4] = {0};
+
+ tmp.cmd = IPC_SOC_REGISTER_ACCESS;
+ tmp.sub_cmd = IPC_SOC_SUB_CMD_WRITE;
+ tmp.wbuf[0] = (u32)pch_1g_regs[i].index;
+ tmp.wbuf[1] = pch_1g_regs[i].val;
+
+ ret = intel_pmc_core_ipc(&tmp, buf);
+ if (ret < 0)
+ goto pmc_read_error;
+ }
+ }
+ }
+
+pmc_read_error:
+ intel_serdes_powerdown(ndev, intel_priv);
+ intel_serdes_powerup(ndev, intel_priv);
+
+ return ret;
+}
+#else
+static int intel_config_serdes(struct net_device *ndev, void *intel_data)
+{
+ return -EOPNOTSUPP;
+}
+#endif
+
static void common_default_data(struct plat_stmmacenet_data *plat)
{
plat->clk_csr = 2; /* clk_csr_i = 20-35MHz & MDC = clk_csr_i/16 */
@@ -616,9 +736,10 @@ 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->max_speed = SPEED_2500;
plat->serdes_powerup = intel_serdes_powerup;
plat->serdes_powerdown = intel_serdes_powerdown;
+ plat->config_serdes = intel_config_serdes;
plat->clk_ptp_rate = 204860000;
@@ -693,9 +814,10 @@ 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->max_speed = SPEED_2500;
plat->serdes_powerup = intel_serdes_powerup;
plat->serdes_powerdown = intel_serdes_powerdown;
+ plat->config_serdes = intel_config_serdes;
return ehl_pse0_common_data(pdev, plat);
}
@@ -742,9 +864,10 @@ 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->max_speed = SPEED_2500;
plat->serdes_powerup = intel_serdes_powerup;
plat->serdes_powerdown = intel_serdes_powerdown;
+ plat->config_serdes = intel_config_serdes;
return ehl_pse1_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 43a72dfe616d..0475ced8b89e 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.h
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.h
@@ -56,4 +56,76 @@
#define PCI_PCH_A1 0x01
#define PCI_PCH_B0 0x10
+#if IS_ENABLED(CONFIG_INTEL_PMC_CORE)
+struct pmc_serdes_regs {
+ u8 index;
+ u32 val;
+};
+
+/* Modphy Register */
+#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 PSE_B_MODPHY_PCR_LCPLL_DWORD0 13
+#define PSE_N_MODPHY_PCR_LCPLL_DWORD2 14
+#define PSE_N_MODPHY_PCR_LCPLL_DWORD7 15
+#define PSE_N_MODPHY_PCR_LPPLL_DWORD10 16
+#define PSE_N_MODPHY_PCR_CMN_ANA_DWORD30 17
+#define PCH_B_MODPHY_PCR_LCPLL_DWORD0 18
+#define PCH_N_MODPHY_PCR_LCPLL_DWORD2 19
+#define PCH_N_MODPHY_PCR_LCPLL_DWORD7 20
+#define PCH_N_MODPHY_PCR_LPPLL_DWORD10 21
+#define PCH_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
+
+static const struct pmc_serdes_regs pch_1g_regs[] = {
+ { PCH_B_MODPHY_PCR_LCPLL_DWORD0, B_MODPHY_PCR_LCPLL_DWORD0_1G },
+ { PCH_N_MODPHY_PCR_LCPLL_DWORD2, N_MODPHY_PCR_LCPLL_DWORD2_1G },
+ { PCH_N_MODPHY_PCR_LCPLL_DWORD7, N_MODPHY_PCR_LCPLL_DWORD7_1G },
+ { PCH_N_MODPHY_PCR_LPPLL_DWORD10, N_MODPHY_PCR_LPPLL_DWORD10_1G },
+ { PCH_N_MODPHY_PCR_CMN_ANA_DWORD30, N_MODPHY_PCR_CMN_ANA_DWORD30_1G },
+ {}
+};
+
+static const struct pmc_serdes_regs pch_2p5g_regs[] = {
+ { PCH_B_MODPHY_PCR_LCPLL_DWORD0, B_MODPHY_PCR_LCPLL_DWORD0_2P5G },
+ { PCH_N_MODPHY_PCR_LCPLL_DWORD2, N_MODPHY_PCR_LCPLL_DWORD2_2P5G },
+ { PCH_N_MODPHY_PCR_LCPLL_DWORD7, N_MODPHY_PCR_LCPLL_DWORD7_2P5G },
+ { PCH_N_MODPHY_PCR_LPPLL_DWORD10, N_MODPHY_PCR_LPPLL_DWORD10_2P5G },
+ { PCH_N_MODPHY_PCR_CMN_ANA_DWORD30, N_MODPHY_PCR_CMN_ANA_DWORD30_2P5G },
+ {}
+};
+
+static const struct pmc_serdes_regs pse_1g_regs[] = {
+ { PSE_B_MODPHY_PCR_LCPLL_DWORD0, B_MODPHY_PCR_LCPLL_DWORD0_1G },
+ { PSE_N_MODPHY_PCR_LCPLL_DWORD2, N_MODPHY_PCR_LCPLL_DWORD2_1G },
+ { PSE_N_MODPHY_PCR_LCPLL_DWORD7, N_MODPHY_PCR_LCPLL_DWORD7_1G },
+ { PSE_N_MODPHY_PCR_LPPLL_DWORD10, N_MODPHY_PCR_LPPLL_DWORD10_1G },
+ { PSE_N_MODPHY_PCR_CMN_ANA_DWORD30, N_MODPHY_PCR_CMN_ANA_DWORD30_1G },
+ {}
+};
+
+static const struct pmc_serdes_regs pse_2p5g_regs[] = {
+ { PSE_B_MODPHY_PCR_LCPLL_DWORD0, B_MODPHY_PCR_LCPLL_DWORD0_2P5G },
+ { PSE_N_MODPHY_PCR_LCPLL_DWORD2, N_MODPHY_PCR_LCPLL_DWORD2_2P5G },
+ { PSE_N_MODPHY_PCR_LCPLL_DWORD7, N_MODPHY_PCR_LCPLL_DWORD7_2P5G },
+ { PSE_N_MODPHY_PCR_LPPLL_DWORD10, N_MODPHY_PCR_LPPLL_DWORD10_2P5G },
+ { PSE_N_MODPHY_PCR_CMN_ANA_DWORD30, N_MODPHY_PCR_CMN_ANA_DWORD30_2P5G },
+ {}
+};
+#endif /* CONFIG_INTEL_PMC_CORE */
+
#endif /* __DWMAC_INTEL_H__ */
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index 6a8b6e229331..910831c91825 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -1177,11 +1177,31 @@ static void stmmac_mac_link_up(struct phylink_config *config,
stmmac_fpe_link_state_handle(priv, true);
}
+#if IS_ENABLED(CONFIG_INTEL_PMC_CORE)
+static int stmmac_mac_prepare(struct phylink_config *config, unsigned int mode,
+ phy_interface_t interface)
+{
+ struct net_device *ndev = to_net_dev(config->dev);
+ struct stmmac_priv *priv = netdev_priv(ndev);
+ int ret = 0;
+
+ priv->plat->phy_interface = interface;
+
+ if (priv->plat->config_serdes)
+ ret = priv->plat->config_serdes(ndev, priv->plat->bsp_priv);
+
+ return ret;
+}
+#endif
+
static const struct phylink_mac_ops stmmac_phylink_mac_ops = {
.validate = stmmac_validate,
.mac_config = stmmac_mac_config,
.mac_link_down = stmmac_mac_link_down,
.mac_link_up = stmmac_mac_link_up,
+#if IS_ENABLED(CONFIG_INTEL_PMC_CORE)
+ .mac_prepare = stmmac_mac_prepare,
+#endif
};
/**
diff --git a/include/linux/stmmac.h b/include/linux/stmmac.h
index bf385968a7a1..402570b97332 100644
--- a/include/linux/stmmac.h
+++ b/include/linux/stmmac.h
@@ -230,6 +230,7 @@ struct plat_stmmacenet_data {
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);
+ int (*config_serdes)(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);