mirror of
https://github.com/torvalds/linux.git
synced 2024-11-10 14:11:52 +00:00
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: Jiawen Wu <jiawenwu@trustnetic.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
d55595f04d
commit
f629acc6f2
@ -22925,6 +22925,7 @@ S: Maintained
|
||||
W: https://www.net-swift.com
|
||||
F: Documentation/networking/device_drivers/ethernet/wangxun/*
|
||||
F: drivers/net/ethernet/wangxun/
|
||||
F: drivers/net/pcs/pcs-xpcs-wx.c
|
||||
|
||||
WATCHDOG DEVICE DRIVERS
|
||||
M: Wim Van Sebroeck <wim@linux-watchdog.org>
|
||||
|
@ -1,7 +1,7 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
# 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_LYNX) += pcs-lynx.o
|
||||
|
208
drivers/net/pcs/pcs-xpcs-wx.c
Normal file
208
drivers/net/pcs/pcs-xpcs-wx.c
Normal file
@ -0,0 +1,208 @@
|
||||
// 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);
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
@ -841,6 +841,12 @@ int xpcs_do_config(struct dw_xpcs *xpcs, phy_interface_t interface,
|
||||
if (!compat)
|
||||
return -ENODEV;
|
||||
|
||||
if (xpcs->dev_flag == DW_DEV_TXGBE) {
|
||||
ret = txgbe_xpcs_switch_mode(xpcs, interface);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
switch (compat->an_mode) {
|
||||
case DW_10GBASER:
|
||||
break;
|
||||
@ -1313,9 +1319,6 @@ static struct dw_xpcs *xpcs_create(struct mdio_device *mdiodev,
|
||||
|
||||
xpcs->pcs.ops = &xpcs_phylink_ops;
|
||||
xpcs->pcs.neg_mode = true;
|
||||
if (compat->an_mode == DW_10GBASER)
|
||||
return xpcs;
|
||||
|
||||
xpcs->pcs.poll = true;
|
||||
|
||||
if (xpcs->dev_flag != DW_DEV_TXGBE) {
|
||||
|
@ -15,8 +15,13 @@
|
||||
/* VR_XS_PCS */
|
||||
#define DW_USXGMII_RST BIT(10)
|
||||
#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_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 */
|
||||
#define DW_USXGMII_FULL BIT(8)
|
||||
@ -106,6 +111,9 @@
|
||||
|
||||
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_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_sja1110_sgmii_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);
|
||||
|
@ -32,6 +32,7 @@ struct dw_xpcs {
|
||||
struct mdio_device *mdiodev;
|
||||
const struct xpcs_id *id;
|
||||
struct phylink_pcs pcs;
|
||||
phy_interface_t interface;
|
||||
int dev_flag;
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user