Commit f629acc6 authored by Jiawen Wu's avatar Jiawen Wu Committed by David S. Miller
Browse files

net: pcs: xpcs: support to switch mode for Wangxun NICs



According to chapter 6 of DesignWare Cores Ethernet PCS (version 3.20a)
and custom design manual, add a configuration flow for switching interface
mode.

If the interface changes, the following setting is required:
1. wait VR_XS_PCS_DIG_STS bit(4, 2) [PSEQ_STATE] = 100b (Power-Good)
2. write SR_XS_PCS_CTRL2 to select various PCS type
3. write SR_PMA_CTRL1 and/or SR_XS_PCS_CTRL1 for link speed
4. program PMA registers
5. write VR_XS_PCS_DIG_CTRL1 bit(15) [VR_RST] = 1b (Vendor-Specific
   Soft Reset)
6. wait for VR_XS_PCS_DIG_CTRL1 bit(15) [VR_RST] to get cleared

Only 10GBASE-R/SGMII/1000BASE-X modes are planned for the current Wangxun
devices. And there is a quirk for Wangxun devices to switch mode although
the interface in phylink state has not changed, since PCS will change to
default 10GBASE-R when the ethernet driver(txgbe) do LAN reset.

Signed-off-by: default avatarJiawen Wu <jiawenwu@trustnetic.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent d55595f0
Loading
Loading
Loading
Loading
+1 −0
Original line number Original line Diff line number Diff line
@@ -22925,6 +22925,7 @@ S: Maintained
W:	https://www.net-swift.com
W:	https://www.net-swift.com
F:	Documentation/networking/device_drivers/ethernet/wangxun/*
F:	Documentation/networking/device_drivers/ethernet/wangxun/*
F:	drivers/net/ethernet/wangxun/
F:	drivers/net/ethernet/wangxun/
F:	drivers/net/pcs/pcs-xpcs-wx.c
WATCHDOG DEVICE DRIVERS
WATCHDOG DEVICE DRIVERS
M:	Wim Van Sebroeck <wim@linux-watchdog.org>
M:	Wim Van Sebroeck <wim@linux-watchdog.org>
+1 −1
Original line number Original line Diff line number Diff line
# SPDX-License-Identifier: GPL-2.0
# SPDX-License-Identifier: GPL-2.0
# Makefile for Linux PCS drivers
# Makefile for Linux PCS drivers


pcs_xpcs-$(CONFIG_PCS_XPCS)	:= pcs-xpcs.o pcs-xpcs-nxp.o
pcs_xpcs-$(CONFIG_PCS_XPCS)	:= pcs-xpcs.o pcs-xpcs-nxp.o pcs-xpcs-wx.o


obj-$(CONFIG_PCS_XPCS)		+= pcs_xpcs.o
obj-$(CONFIG_PCS_XPCS)		+= pcs_xpcs.o
obj-$(CONFIG_PCS_LYNX)		+= pcs-lynx.o
obj-$(CONFIG_PCS_LYNX)		+= pcs-lynx.o
+208 −0
Original line number Original line Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2015 - 2023 Beijing WangXun Technology Co., Ltd. */

#include <linux/pcs/pcs-xpcs.h>
#include <linux/mdio.h>
#include "pcs-xpcs.h"

/* VR_XS_PMA_MMD */
#define TXGBE_PMA_MMD			0x8020
#define TXGBE_TX_GENCTL1		0x11
#define TXGBE_TX_GENCTL1_VBOOST_LVL	GENMASK(10, 8)
#define TXGBE_TX_GENCTL1_VBOOST_EN0	BIT(4)
#define TXGBE_TX_GEN_CTL2		0x12
#define TXGBE_TX_GEN_CTL2_TX0_WIDTH(v)	FIELD_PREP(GENMASK(9, 8), v)
#define TXGBE_TX_RATE_CTL		0x14
#define TXGBE_TX_RATE_CTL_TX0_RATE(v)	FIELD_PREP(GENMASK(2, 0), v)
#define TXGBE_RX_GEN_CTL2		0x32
#define TXGBE_RX_GEN_CTL2_RX0_WIDTH(v)	FIELD_PREP(GENMASK(9, 8), v)
#define TXGBE_RX_GEN_CTL3		0x33
#define TXGBE_RX_GEN_CTL3_LOS_TRSHLD0	GENMASK(2, 0)
#define TXGBE_RX_RATE_CTL		0x34
#define TXGBE_RX_RATE_CTL_RX0_RATE(v)	FIELD_PREP(GENMASK(1, 0), v)
#define TXGBE_RX_EQ_ATTN_CTL		0x37
#define TXGBE_RX_EQ_ATTN_LVL0		GENMASK(2, 0)
#define TXGBE_RX_EQ_CTL0		0x38
#define TXGBE_RX_EQ_CTL0_VGA1_GAIN(v)	FIELD_PREP(GENMASK(15, 12), v)
#define TXGBE_RX_EQ_CTL0_VGA2_GAIN(v)	FIELD_PREP(GENMASK(11, 8), v)
#define TXGBE_RX_EQ_CTL0_CTLE_POLE(v)	FIELD_PREP(GENMASK(7, 5), v)
#define TXGBE_RX_EQ_CTL0_CTLE_BOOST(v)	FIELD_PREP(GENMASK(4, 0), v)
#define TXGBE_RX_EQ_CTL4		0x3C
#define TXGBE_RX_EQ_CTL4_CONT_OFF_CAN0	BIT(4)
#define TXGBE_RX_EQ_CTL4_CONT_ADAPT0	BIT(0)
#define TXGBE_AFE_DFE_ENABLE		0x3D
#define TXGBE_DFE_EN_0			BIT(4)
#define TXGBE_AFE_EN_0			BIT(0)
#define TXGBE_DFE_TAP_CTL0		0x3E
#define TXGBE_MPLLA_CTL0		0x51
#define TXGBE_MPLLA_CTL2		0x53
#define TXGBE_MPLLA_CTL2_DIV16P5_CLK_EN	BIT(10)
#define TXGBE_MPLLA_CTL2_DIV10_CLK_EN	BIT(9)
#define TXGBE_MPLLA_CTL3		0x57
#define TXGBE_MISC_CTL0			0x70
#define TXGBE_MISC_CTL0_PLL		BIT(15)
#define TXGBE_MISC_CTL0_CR_PARA_SEL	BIT(14)
#define TXGBE_MISC_CTL0_RX_VREF(v)	FIELD_PREP(GENMASK(12, 8), v)
#define TXGBE_VCO_CAL_LD0		0x72
#define TXGBE_VCO_CAL_REF0		0x76

static int txgbe_read_pma(struct dw_xpcs *xpcs, int reg)
{
	return xpcs_read(xpcs, MDIO_MMD_PMAPMD, TXGBE_PMA_MMD + reg);
}

static int txgbe_write_pma(struct dw_xpcs *xpcs, int reg, u16 val)
{
	return xpcs_write(xpcs, MDIO_MMD_PMAPMD, TXGBE_PMA_MMD + reg, val);
}

static void txgbe_pma_config_10gbaser(struct dw_xpcs *xpcs)
{
	int val;

	txgbe_write_pma(xpcs, TXGBE_MPLLA_CTL0, 0x21);
	txgbe_write_pma(xpcs, TXGBE_MPLLA_CTL3, 0);
	val = txgbe_read_pma(xpcs, TXGBE_TX_GENCTL1);
	val = u16_replace_bits(val, 0x5, TXGBE_TX_GENCTL1_VBOOST_LVL);
	txgbe_write_pma(xpcs, TXGBE_TX_GENCTL1, val);
	txgbe_write_pma(xpcs, TXGBE_MISC_CTL0, TXGBE_MISC_CTL0_PLL |
			TXGBE_MISC_CTL0_CR_PARA_SEL | TXGBE_MISC_CTL0_RX_VREF(0xF));
	txgbe_write_pma(xpcs, TXGBE_VCO_CAL_LD0, 0x549);
	txgbe_write_pma(xpcs, TXGBE_VCO_CAL_REF0, 0x29);
	txgbe_write_pma(xpcs, TXGBE_TX_RATE_CTL, 0);
	txgbe_write_pma(xpcs, TXGBE_RX_RATE_CTL, 0);
	txgbe_write_pma(xpcs, TXGBE_TX_GEN_CTL2, TXGBE_TX_GEN_CTL2_TX0_WIDTH(3));
	txgbe_write_pma(xpcs, TXGBE_RX_GEN_CTL2, TXGBE_RX_GEN_CTL2_RX0_WIDTH(3));
	txgbe_write_pma(xpcs, TXGBE_MPLLA_CTL2, TXGBE_MPLLA_CTL2_DIV16P5_CLK_EN |
			TXGBE_MPLLA_CTL2_DIV10_CLK_EN);

	txgbe_write_pma(xpcs, TXGBE_RX_EQ_CTL0, TXGBE_RX_EQ_CTL0_CTLE_POLE(2) |
			TXGBE_RX_EQ_CTL0_CTLE_BOOST(5));
	val = txgbe_read_pma(xpcs, TXGBE_RX_EQ_ATTN_CTL);
	val &= ~TXGBE_RX_EQ_ATTN_LVL0;
	txgbe_write_pma(xpcs, TXGBE_RX_EQ_ATTN_CTL, val);
	txgbe_write_pma(xpcs, TXGBE_DFE_TAP_CTL0, 0xBE);
	val = txgbe_read_pma(xpcs, TXGBE_AFE_DFE_ENABLE);
	val &= ~(TXGBE_DFE_EN_0 | TXGBE_AFE_EN_0);
	txgbe_write_pma(xpcs, TXGBE_AFE_DFE_ENABLE, val);
	val = txgbe_read_pma(xpcs, TXGBE_RX_EQ_CTL4);
	val &= ~TXGBE_RX_EQ_CTL4_CONT_ADAPT0;
	txgbe_write_pma(xpcs, TXGBE_RX_EQ_CTL4, val);
}

static void txgbe_pma_config_1g(struct dw_xpcs *xpcs)
{
	int val;

	val = txgbe_read_pma(xpcs, TXGBE_TX_GENCTL1);
	val = u16_replace_bits(val, 0x5, TXGBE_TX_GENCTL1_VBOOST_LVL);
	val &= ~TXGBE_TX_GENCTL1_VBOOST_EN0;
	txgbe_write_pma(xpcs, TXGBE_TX_GENCTL1, val);
	txgbe_write_pma(xpcs, TXGBE_MISC_CTL0, TXGBE_MISC_CTL0_PLL |
			TXGBE_MISC_CTL0_CR_PARA_SEL | TXGBE_MISC_CTL0_RX_VREF(0xF));

	txgbe_write_pma(xpcs, TXGBE_RX_EQ_CTL0, TXGBE_RX_EQ_CTL0_VGA1_GAIN(7) |
			TXGBE_RX_EQ_CTL0_VGA2_GAIN(7) | TXGBE_RX_EQ_CTL0_CTLE_BOOST(6));
	val = txgbe_read_pma(xpcs, TXGBE_RX_EQ_ATTN_CTL);
	val &= ~TXGBE_RX_EQ_ATTN_LVL0;
	txgbe_write_pma(xpcs, TXGBE_RX_EQ_ATTN_CTL, val);
	txgbe_write_pma(xpcs, TXGBE_DFE_TAP_CTL0, 0);
	val = txgbe_read_pma(xpcs, TXGBE_RX_GEN_CTL3);
	val = u16_replace_bits(val, 0x4, TXGBE_RX_GEN_CTL3_LOS_TRSHLD0);
	txgbe_write_pma(xpcs, TXGBE_RX_EQ_ATTN_CTL, val);

	txgbe_write_pma(xpcs, TXGBE_MPLLA_CTL0, 0x20);
	txgbe_write_pma(xpcs, TXGBE_MPLLA_CTL3, 0x46);
	txgbe_write_pma(xpcs, TXGBE_VCO_CAL_LD0, 0x540);
	txgbe_write_pma(xpcs, TXGBE_VCO_CAL_REF0, 0x2A);
	txgbe_write_pma(xpcs, TXGBE_AFE_DFE_ENABLE, 0);
	txgbe_write_pma(xpcs, TXGBE_RX_EQ_CTL4, TXGBE_RX_EQ_CTL4_CONT_OFF_CAN0);
	txgbe_write_pma(xpcs, TXGBE_TX_RATE_CTL, TXGBE_TX_RATE_CTL_TX0_RATE(3));
	txgbe_write_pma(xpcs, TXGBE_RX_RATE_CTL, TXGBE_RX_RATE_CTL_RX0_RATE(3));
	txgbe_write_pma(xpcs, TXGBE_TX_GEN_CTL2, TXGBE_TX_GEN_CTL2_TX0_WIDTH(1));
	txgbe_write_pma(xpcs, TXGBE_RX_GEN_CTL2, TXGBE_RX_GEN_CTL2_RX0_WIDTH(1));
	txgbe_write_pma(xpcs, TXGBE_MPLLA_CTL2, TXGBE_MPLLA_CTL2_DIV10_CLK_EN);
}

static int txgbe_pcs_poll_power_up(struct dw_xpcs *xpcs)
{
	int val, ret;

	/* Wait xpcs power-up good */
	ret = read_poll_timeout(xpcs_read_vpcs, val,
				(val & DW_PSEQ_ST) == DW_PSEQ_ST_GOOD,
				10000, 1000000, false,
				xpcs, DW_VR_XS_PCS_DIG_STS);
	if (ret < 0)
		dev_err(&xpcs->mdiodev->dev, "xpcs power-up timeout\n");

	return ret;
}

static int txgbe_pma_init_done(struct dw_xpcs *xpcs)
{
	int val, ret;

	xpcs_write_vpcs(xpcs, DW_VR_XS_PCS_DIG_CTRL1, DW_VR_RST | DW_EN_VSMMD1);

	/* wait pma initialization done */
	ret = read_poll_timeout(xpcs_read_vpcs, val, !(val & DW_VR_RST),
				100000, 10000000, false,
				xpcs, DW_VR_XS_PCS_DIG_CTRL1);
	if (ret < 0)
		dev_err(&xpcs->mdiodev->dev, "xpcs pma initialization timeout\n");

	return ret;
}

static bool txgbe_xpcs_mode_quirk(struct dw_xpcs *xpcs)
{
	int ret;

	/* When txgbe do LAN reset, PCS will change to default 10GBASE-R mode */
	ret = xpcs_read(xpcs, MDIO_MMD_PCS, MDIO_CTRL2);
	ret &= MDIO_PCS_CTRL2_TYPE;
	if (ret == MDIO_PCS_CTRL2_10GBR &&
	    xpcs->interface != PHY_INTERFACE_MODE_10GBASER)
		return true;

	return false;
}

int txgbe_xpcs_switch_mode(struct dw_xpcs *xpcs, phy_interface_t interface)
{
	int val, ret;

	switch (interface) {
	case PHY_INTERFACE_MODE_10GBASER:
	case PHY_INTERFACE_MODE_SGMII:
	case PHY_INTERFACE_MODE_1000BASEX:
		break;
	default:
		return 0;
	}

	if (xpcs->interface == interface && !txgbe_xpcs_mode_quirk(xpcs))
		return 0;

	xpcs->interface = interface;

	ret = txgbe_pcs_poll_power_up(xpcs);
	if (ret < 0)
		return ret;

	if (interface == PHY_INTERFACE_MODE_10GBASER) {
		xpcs_write(xpcs, MDIO_MMD_PCS, MDIO_CTRL2, MDIO_PCS_CTRL2_10GBR);
		val = xpcs_read(xpcs, MDIO_MMD_PMAPMD, MDIO_CTRL1);
		val |= MDIO_CTRL1_SPEED10G;
		xpcs_write(xpcs, MDIO_MMD_PMAPMD, MDIO_CTRL1, val);
		txgbe_pma_config_10gbaser(xpcs);
	} else {
		xpcs_write(xpcs, MDIO_MMD_PCS, MDIO_CTRL2, MDIO_PCS_CTRL2_10GBX);
		xpcs_write(xpcs, MDIO_MMD_PMAPMD, MDIO_CTRL1, 0);
		xpcs_write(xpcs, MDIO_MMD_PCS, MDIO_CTRL1, 0);
		txgbe_pma_config_1g(xpcs);
	}

	return txgbe_pma_init_done(xpcs);
}
+8 −5
Original line number Original line Diff line number Diff line
@@ -228,12 +228,12 @@ static int xpcs_write_vendor(struct dw_xpcs *xpcs, int dev, int reg,
	return xpcs_write(xpcs, dev, DW_VENDOR | reg, val);
	return xpcs_write(xpcs, dev, DW_VENDOR | reg, val);
}
}


static int xpcs_read_vpcs(struct dw_xpcs *xpcs, int reg)
int xpcs_read_vpcs(struct dw_xpcs *xpcs, int reg)
{
{
	return xpcs_read_vendor(xpcs, MDIO_MMD_PCS, reg);
	return xpcs_read_vendor(xpcs, MDIO_MMD_PCS, reg);
}
}


static int xpcs_write_vpcs(struct dw_xpcs *xpcs, int reg, u16 val)
int xpcs_write_vpcs(struct dw_xpcs *xpcs, int reg, u16 val)
{
{
	return xpcs_write_vendor(xpcs, MDIO_MMD_PCS, reg, val);
	return xpcs_write_vendor(xpcs, MDIO_MMD_PCS, reg, val);
}
}
@@ -841,6 +841,12 @@ int xpcs_do_config(struct dw_xpcs *xpcs, phy_interface_t interface,
	if (!compat)
	if (!compat)
		return -ENODEV;
		return -ENODEV;


	if (xpcs->dev_flag == DW_DEV_TXGBE) {
		ret = txgbe_xpcs_switch_mode(xpcs, interface);
		if (ret)
			return ret;
	}

	switch (compat->an_mode) {
	switch (compat->an_mode) {
	case DW_10GBASER:
	case DW_10GBASER:
		break;
		break;
@@ -1313,9 +1319,6 @@ static struct dw_xpcs *xpcs_create(struct mdio_device *mdiodev,


		xpcs->pcs.ops = &xpcs_phylink_ops;
		xpcs->pcs.ops = &xpcs_phylink_ops;
		xpcs->pcs.neg_mode = true;
		xpcs->pcs.neg_mode = true;
		if (compat->an_mode == DW_10GBASER)
			return xpcs;

		xpcs->pcs.poll = true;
		xpcs->pcs.poll = true;


		if (xpcs->dev_flag != DW_DEV_TXGBE) {
		if (xpcs->dev_flag != DW_DEV_TXGBE) {
+8 −0
Original line number Original line Diff line number Diff line
@@ -15,8 +15,13 @@
/* VR_XS_PCS */
/* VR_XS_PCS */
#define DW_USXGMII_RST			BIT(10)
#define DW_USXGMII_RST			BIT(10)
#define DW_USXGMII_EN			BIT(9)
#define DW_USXGMII_EN			BIT(9)
#define DW_VR_XS_PCS_DIG_CTRL1		0x0000
#define DW_VR_RST			BIT(15)
#define DW_EN_VSMMD1			BIT(13)
#define DW_VR_XS_PCS_DIG_STS		0x0010
#define DW_VR_XS_PCS_DIG_STS		0x0010
#define DW_RXFIFO_ERR			GENMASK(6, 5)
#define DW_RXFIFO_ERR			GENMASK(6, 5)
#define DW_PSEQ_ST			GENMASK(4, 2)
#define DW_PSEQ_ST_GOOD			FIELD_PREP(GENMASK(4, 2), 0x4)


/* SR_MII */
/* SR_MII */
#define DW_USXGMII_FULL			BIT(8)
#define DW_USXGMII_FULL			BIT(8)
@@ -106,6 +111,9 @@


int xpcs_read(struct dw_xpcs *xpcs, int dev, u32 reg);
int xpcs_read(struct dw_xpcs *xpcs, int dev, u32 reg);
int xpcs_write(struct dw_xpcs *xpcs, int dev, u32 reg, u16 val);
int xpcs_write(struct dw_xpcs *xpcs, int dev, u32 reg, u16 val);
int xpcs_read_vpcs(struct dw_xpcs *xpcs, int reg);
int xpcs_write_vpcs(struct dw_xpcs *xpcs, int reg, u16 val);
int nxp_sja1105_sgmii_pma_config(struct dw_xpcs *xpcs);
int nxp_sja1105_sgmii_pma_config(struct dw_xpcs *xpcs);
int nxp_sja1110_sgmii_pma_config(struct dw_xpcs *xpcs);
int nxp_sja1110_sgmii_pma_config(struct dw_xpcs *xpcs);
int nxp_sja1110_2500basex_pma_config(struct dw_xpcs *xpcs);
int nxp_sja1110_2500basex_pma_config(struct dw_xpcs *xpcs);
int txgbe_xpcs_switch_mode(struct dw_xpcs *xpcs, phy_interface_t interface);
Loading