Commit 89b12ca6 authored by Jakub Kicinski's avatar Jakub Kicinski
Browse files

Merge branch 'enable-sgmii-and-2500basex-interface-mode-switching-for-intel-platforms'

Choong Yong Liang says:

====================
Enable SGMII and 2500BASEX interface mode switching for Intel platforms

During the interface mode change, the 'phylink_major_config' function will
be triggered in phylink. The modification of the following functions will
support the switching between SGMII and 2500BASE-X interface modes for
the Intel platform:

- xpcs_switch_interface_mode: Re-initiates clause 37 auto-negotiation for
  the SGMII interface mode to perform auto-negotiation.
- mac_finish: Configures the SerDes according to the interface mode.

With the above changes, the code will work as follows during the interface
mode change. The PCS will reconfigure according to the pcs_neg_mode and the
selected interface mode. Then, the MAC driver will perform SerDes
configuration in 'mac_finish' based on the selected interface mode. During
the SerDes configuration, the selected interface mode will identify TSN
lane registers from FIA and then send IPC commands to the Power Management
Controller (PMC) through the PMC driver/API. The PMC will act as a proxy to
program the PLL registers.
====================

Link: https://patch.msgid.link/20250227121522.1802832-1-yong.liang.choong@linux.intel.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents c62e6f05 7598ef62
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -11862,6 +11862,7 @@ L: platform-driver-x86@vger.kernel.org
S:	Maintained
F:	Documentation/ABI/testing/sysfs-platform-intel-pmc
F:	drivers/platform/x86/intel/pmc/
F:	linux/platform_data/x86/intel_pmc_ipc.h
INTEL PMIC GPIO DRIVERS
M:	Andy Shevchenko <andy@kernel.org>
+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.
+225 −6
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,45 @@ 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 const int adln_tsn_lane_regs[] = {6};

static int stmmac_pci_find_phy_addr(struct pci_dev *pdev,
				    const struct dmi_system_id *dmi_list)
{
@@ -93,7 +147,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 +469,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 +767,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 +784,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 +859,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 +908,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);
}

@@ -835,6 +1005,55 @@ static int adls_sgmii_phy1_data(struct pci_dev *pdev,
static struct stmmac_pci_info adls_sgmii1g_phy1_info = {
	.setup = adls_sgmii_phy1_data,
};

static int adln_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 = 6;
	plat->tx_queues_to_use = 4;
	plat->clk_ptp_rate = 204800000;

	plat->safety_feat_cfg->tsoee = 1;
	plat->safety_feat_cfg->mrxpee = 0;
	plat->safety_feat_cfg->mestee = 1;
	plat->safety_feat_cfg->mrxee = 1;
	plat->safety_feat_cfg->mtxee = 1;
	plat->safety_feat_cfg->epsi = 0;
	plat->safety_feat_cfg->edpp = 0;
	plat->safety_feat_cfg->prtyen = 0;
	plat->safety_feat_cfg->tmouten = 0;

	intel_priv->tsn_lane_regs = adln_tsn_lane_regs;
	intel_priv->max_tsn_lane_regs = ARRAY_SIZE(adln_tsn_lane_regs);

	return intel_mgbe_common_data(pdev, plat);
}

static int adln_sgmii_phy0_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->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 adln_common_data(pdev, plat);
}

static struct stmmac_pci_info adln_sgmii1g_phy0_info = {
	.setup = adln_sgmii_phy0_data,
};

static const struct stmmac_pci_func_data galileo_stmmac_func_data[] = {
	{
		.func = 6,
@@ -1217,7 +1436,7 @@ static const struct pci_device_id intel_eth_pci_id_table[] = {
	{ PCI_DEVICE_DATA(INTEL, TGLH_SGMII1G_1, &tgl_sgmii1g_phy1_info) },
	{ PCI_DEVICE_DATA(INTEL, ADLS_SGMII1G_0, &adls_sgmii1g_phy0_info) },
	{ PCI_DEVICE_DATA(INTEL, ADLS_SGMII1G_1, &adls_sgmii1g_phy1_info) },
	{ PCI_DEVICE_DATA(INTEL, ADLN_SGMII1G, &tgl_sgmii1g_phy0_info) },
	{ PCI_DEVICE_DATA(INTEL, ADLN_SGMII1G, &adln_sgmii1g_phy0_info) },
	{ PCI_DEVICE_DATA(INTEL, RPLP_SGMII1G, &tgl_sgmii1g_phy0_info) },
	{}
};
+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__ */
+13 −0
Original line number Diff line number Diff line
@@ -1129,6 +1129,18 @@ static int stmmac_mac_enable_tx_lpi(struct phylink_config *config, u32 timer,
	return 0;
}

static int stmmac_mac_finish(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);

	if (priv->plat->mac_finish)
		priv->plat->mac_finish(ndev, priv->plat->bsp_priv, mode, interface);

	return 0;
}

static const struct phylink_mac_ops stmmac_phylink_mac_ops = {
	.mac_get_caps = stmmac_mac_get_caps,
	.mac_select_pcs = stmmac_mac_select_pcs,
@@ -1137,6 +1149,7 @@ static const struct phylink_mac_ops stmmac_phylink_mac_ops = {
	.mac_link_up = stmmac_mac_link_up,
	.mac_disable_tx_lpi = stmmac_mac_disable_tx_lpi,
	.mac_enable_tx_lpi = stmmac_mac_enable_tx_lpi,
	.mac_finish = stmmac_mac_finish,
};

/**
Loading