Skip to content

Commit

Permalink
net: stmmac: configure SerDes according to the interface mode
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Choong Yong Liang authored and Jakub Kicinski committed Mar 6, 2025
1 parent e654cfc commit a42f6b3
Show file tree
Hide file tree
Showing 3 changed files with 204 additions and 5 deletions.
1 change: 1 addition & 0 deletions drivers/net/ethernet/stmicro/stmmac/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
179 changes: 174 additions & 5 deletions drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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)
{
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -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;
Expand All @@ -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);
}

Expand Down Expand Up @@ -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);
}

Expand Down Expand Up @@ -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);
}

Expand Down
29 changes: 29 additions & 0 deletions drivers/net/ethernet/stmicro/stmmac/dwmac-intel.h
Original file line number Diff line number Diff line change
Expand Up @@ -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__ */

0 comments on commit a42f6b3

Please sign in to comment.