net: stmmac: configure SerDes according to the interface mode

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>
This commit is contained in:
Choong Yong Liang 2025-02-27 20:15:21 +08:00 committed by Jakub Kicinski
parent e654cfc718
commit a42f6b3f1c
3 changed files with 204 additions and 5 deletions

View 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.

View 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);
}

View file

@ -50,4 +50,33 @@
#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__ */