Commit a42f6b3f authored by Choong Yong Liang's avatar Choong Yong Liang Committed by Jakub Kicinski
Browse files

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: default avatarChoong 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: default avatarJakub Kicinski <kuba@kernel.org>
parent e654cfc7
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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.
+174 −5
Original line number Diff line number Diff line
@@ -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);
}

+29 −0
Original line number Diff line number Diff line
@@ -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__ */