Skip to content

Commit f629acc

Browse files
Jiawen Wudavem330
authored andcommitted
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>
1 parent d55595f commit f629acc

File tree

6 files changed

+227
-6
lines changed

6 files changed

+227
-6
lines changed

MAINTAINERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22925,6 +22925,7 @@ S: Maintained
2292522925
W: https://www.net-swift.com
2292622926
F: Documentation/networking/device_drivers/ethernet/wangxun/*
2292722927
F: drivers/net/ethernet/wangxun/
22928+
F: drivers/net/pcs/pcs-xpcs-wx.c
2292822929

2292922930
WATCHDOG DEVICE DRIVERS
2293022931
M: Wim Van Sebroeck <wim@linux-watchdog.org>

drivers/net/pcs/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# SPDX-License-Identifier: GPL-2.0
22
# Makefile for Linux PCS drivers
33

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

66
obj-$(CONFIG_PCS_XPCS) += pcs_xpcs.o
77
obj-$(CONFIG_PCS_LYNX) += pcs-lynx.o

drivers/net/pcs/pcs-xpcs-wx.c

Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/* Copyright (c) 2015 - 2023 Beijing WangXun Technology Co., Ltd. */
3+
4+
#include <linux/pcs/pcs-xpcs.h>
5+
#include <linux/mdio.h>
6+
#include "pcs-xpcs.h"
7+
8+
/* VR_XS_PMA_MMD */
9+
#define TXGBE_PMA_MMD 0x8020
10+
#define TXGBE_TX_GENCTL1 0x11
11+
#define TXGBE_TX_GENCTL1_VBOOST_LVL GENMASK(10, 8)
12+
#define TXGBE_TX_GENCTL1_VBOOST_EN0 BIT(4)
13+
#define TXGBE_TX_GEN_CTL2 0x12
14+
#define TXGBE_TX_GEN_CTL2_TX0_WIDTH(v) FIELD_PREP(GENMASK(9, 8), v)
15+
#define TXGBE_TX_RATE_CTL 0x14
16+
#define TXGBE_TX_RATE_CTL_TX0_RATE(v) FIELD_PREP(GENMASK(2, 0), v)
17+
#define TXGBE_RX_GEN_CTL2 0x32
18+
#define TXGBE_RX_GEN_CTL2_RX0_WIDTH(v) FIELD_PREP(GENMASK(9, 8), v)
19+
#define TXGBE_RX_GEN_CTL3 0x33
20+
#define TXGBE_RX_GEN_CTL3_LOS_TRSHLD0 GENMASK(2, 0)
21+
#define TXGBE_RX_RATE_CTL 0x34
22+
#define TXGBE_RX_RATE_CTL_RX0_RATE(v) FIELD_PREP(GENMASK(1, 0), v)
23+
#define TXGBE_RX_EQ_ATTN_CTL 0x37
24+
#define TXGBE_RX_EQ_ATTN_LVL0 GENMASK(2, 0)
25+
#define TXGBE_RX_EQ_CTL0 0x38
26+
#define TXGBE_RX_EQ_CTL0_VGA1_GAIN(v) FIELD_PREP(GENMASK(15, 12), v)
27+
#define TXGBE_RX_EQ_CTL0_VGA2_GAIN(v) FIELD_PREP(GENMASK(11, 8), v)
28+
#define TXGBE_RX_EQ_CTL0_CTLE_POLE(v) FIELD_PREP(GENMASK(7, 5), v)
29+
#define TXGBE_RX_EQ_CTL0_CTLE_BOOST(v) FIELD_PREP(GENMASK(4, 0), v)
30+
#define TXGBE_RX_EQ_CTL4 0x3C
31+
#define TXGBE_RX_EQ_CTL4_CONT_OFF_CAN0 BIT(4)
32+
#define TXGBE_RX_EQ_CTL4_CONT_ADAPT0 BIT(0)
33+
#define TXGBE_AFE_DFE_ENABLE 0x3D
34+
#define TXGBE_DFE_EN_0 BIT(4)
35+
#define TXGBE_AFE_EN_0 BIT(0)
36+
#define TXGBE_DFE_TAP_CTL0 0x3E
37+
#define TXGBE_MPLLA_CTL0 0x51
38+
#define TXGBE_MPLLA_CTL2 0x53
39+
#define TXGBE_MPLLA_CTL2_DIV16P5_CLK_EN BIT(10)
40+
#define TXGBE_MPLLA_CTL2_DIV10_CLK_EN BIT(9)
41+
#define TXGBE_MPLLA_CTL3 0x57
42+
#define TXGBE_MISC_CTL0 0x70
43+
#define TXGBE_MISC_CTL0_PLL BIT(15)
44+
#define TXGBE_MISC_CTL0_CR_PARA_SEL BIT(14)
45+
#define TXGBE_MISC_CTL0_RX_VREF(v) FIELD_PREP(GENMASK(12, 8), v)
46+
#define TXGBE_VCO_CAL_LD0 0x72
47+
#define TXGBE_VCO_CAL_REF0 0x76
48+
49+
static int txgbe_read_pma(struct dw_xpcs *xpcs, int reg)
50+
{
51+
return xpcs_read(xpcs, MDIO_MMD_PMAPMD, TXGBE_PMA_MMD + reg);
52+
}
53+
54+
static int txgbe_write_pma(struct dw_xpcs *xpcs, int reg, u16 val)
55+
{
56+
return xpcs_write(xpcs, MDIO_MMD_PMAPMD, TXGBE_PMA_MMD + reg, val);
57+
}
58+
59+
static void txgbe_pma_config_10gbaser(struct dw_xpcs *xpcs)
60+
{
61+
int val;
62+
63+
txgbe_write_pma(xpcs, TXGBE_MPLLA_CTL0, 0x21);
64+
txgbe_write_pma(xpcs, TXGBE_MPLLA_CTL3, 0);
65+
val = txgbe_read_pma(xpcs, TXGBE_TX_GENCTL1);
66+
val = u16_replace_bits(val, 0x5, TXGBE_TX_GENCTL1_VBOOST_LVL);
67+
txgbe_write_pma(xpcs, TXGBE_TX_GENCTL1, val);
68+
txgbe_write_pma(xpcs, TXGBE_MISC_CTL0, TXGBE_MISC_CTL0_PLL |
69+
TXGBE_MISC_CTL0_CR_PARA_SEL | TXGBE_MISC_CTL0_RX_VREF(0xF));
70+
txgbe_write_pma(xpcs, TXGBE_VCO_CAL_LD0, 0x549);
71+
txgbe_write_pma(xpcs, TXGBE_VCO_CAL_REF0, 0x29);
72+
txgbe_write_pma(xpcs, TXGBE_TX_RATE_CTL, 0);
73+
txgbe_write_pma(xpcs, TXGBE_RX_RATE_CTL, 0);
74+
txgbe_write_pma(xpcs, TXGBE_TX_GEN_CTL2, TXGBE_TX_GEN_CTL2_TX0_WIDTH(3));
75+
txgbe_write_pma(xpcs, TXGBE_RX_GEN_CTL2, TXGBE_RX_GEN_CTL2_RX0_WIDTH(3));
76+
txgbe_write_pma(xpcs, TXGBE_MPLLA_CTL2, TXGBE_MPLLA_CTL2_DIV16P5_CLK_EN |
77+
TXGBE_MPLLA_CTL2_DIV10_CLK_EN);
78+
79+
txgbe_write_pma(xpcs, TXGBE_RX_EQ_CTL0, TXGBE_RX_EQ_CTL0_CTLE_POLE(2) |
80+
TXGBE_RX_EQ_CTL0_CTLE_BOOST(5));
81+
val = txgbe_read_pma(xpcs, TXGBE_RX_EQ_ATTN_CTL);
82+
val &= ~TXGBE_RX_EQ_ATTN_LVL0;
83+
txgbe_write_pma(xpcs, TXGBE_RX_EQ_ATTN_CTL, val);
84+
txgbe_write_pma(xpcs, TXGBE_DFE_TAP_CTL0, 0xBE);
85+
val = txgbe_read_pma(xpcs, TXGBE_AFE_DFE_ENABLE);
86+
val &= ~(TXGBE_DFE_EN_0 | TXGBE_AFE_EN_0);
87+
txgbe_write_pma(xpcs, TXGBE_AFE_DFE_ENABLE, val);
88+
val = txgbe_read_pma(xpcs, TXGBE_RX_EQ_CTL4);
89+
val &= ~TXGBE_RX_EQ_CTL4_CONT_ADAPT0;
90+
txgbe_write_pma(xpcs, TXGBE_RX_EQ_CTL4, val);
91+
}
92+
93+
static void txgbe_pma_config_1g(struct dw_xpcs *xpcs)
94+
{
95+
int val;
96+
97+
val = txgbe_read_pma(xpcs, TXGBE_TX_GENCTL1);
98+
val = u16_replace_bits(val, 0x5, TXGBE_TX_GENCTL1_VBOOST_LVL);
99+
val &= ~TXGBE_TX_GENCTL1_VBOOST_EN0;
100+
txgbe_write_pma(xpcs, TXGBE_TX_GENCTL1, val);
101+
txgbe_write_pma(xpcs, TXGBE_MISC_CTL0, TXGBE_MISC_CTL0_PLL |
102+
TXGBE_MISC_CTL0_CR_PARA_SEL | TXGBE_MISC_CTL0_RX_VREF(0xF));
103+
104+
txgbe_write_pma(xpcs, TXGBE_RX_EQ_CTL0, TXGBE_RX_EQ_CTL0_VGA1_GAIN(7) |
105+
TXGBE_RX_EQ_CTL0_VGA2_GAIN(7) | TXGBE_RX_EQ_CTL0_CTLE_BOOST(6));
106+
val = txgbe_read_pma(xpcs, TXGBE_RX_EQ_ATTN_CTL);
107+
val &= ~TXGBE_RX_EQ_ATTN_LVL0;
108+
txgbe_write_pma(xpcs, TXGBE_RX_EQ_ATTN_CTL, val);
109+
txgbe_write_pma(xpcs, TXGBE_DFE_TAP_CTL0, 0);
110+
val = txgbe_read_pma(xpcs, TXGBE_RX_GEN_CTL3);
111+
val = u16_replace_bits(val, 0x4, TXGBE_RX_GEN_CTL3_LOS_TRSHLD0);
112+
txgbe_write_pma(xpcs, TXGBE_RX_EQ_ATTN_CTL, val);
113+
114+
txgbe_write_pma(xpcs, TXGBE_MPLLA_CTL0, 0x20);
115+
txgbe_write_pma(xpcs, TXGBE_MPLLA_CTL3, 0x46);
116+
txgbe_write_pma(xpcs, TXGBE_VCO_CAL_LD0, 0x540);
117+
txgbe_write_pma(xpcs, TXGBE_VCO_CAL_REF0, 0x2A);
118+
txgbe_write_pma(xpcs, TXGBE_AFE_DFE_ENABLE, 0);
119+
txgbe_write_pma(xpcs, TXGBE_RX_EQ_CTL4, TXGBE_RX_EQ_CTL4_CONT_OFF_CAN0);
120+
txgbe_write_pma(xpcs, TXGBE_TX_RATE_CTL, TXGBE_TX_RATE_CTL_TX0_RATE(3));
121+
txgbe_write_pma(xpcs, TXGBE_RX_RATE_CTL, TXGBE_RX_RATE_CTL_RX0_RATE(3));
122+
txgbe_write_pma(xpcs, TXGBE_TX_GEN_CTL2, TXGBE_TX_GEN_CTL2_TX0_WIDTH(1));
123+
txgbe_write_pma(xpcs, TXGBE_RX_GEN_CTL2, TXGBE_RX_GEN_CTL2_RX0_WIDTH(1));
124+
txgbe_write_pma(xpcs, TXGBE_MPLLA_CTL2, TXGBE_MPLLA_CTL2_DIV10_CLK_EN);
125+
}
126+
127+
static int txgbe_pcs_poll_power_up(struct dw_xpcs *xpcs)
128+
{
129+
int val, ret;
130+
131+
/* Wait xpcs power-up good */
132+
ret = read_poll_timeout(xpcs_read_vpcs, val,
133+
(val & DW_PSEQ_ST) == DW_PSEQ_ST_GOOD,
134+
10000, 1000000, false,
135+
xpcs, DW_VR_XS_PCS_DIG_STS);
136+
if (ret < 0)
137+
dev_err(&xpcs->mdiodev->dev, "xpcs power-up timeout\n");
138+
139+
return ret;
140+
}
141+
142+
static int txgbe_pma_init_done(struct dw_xpcs *xpcs)
143+
{
144+
int val, ret;
145+
146+
xpcs_write_vpcs(xpcs, DW_VR_XS_PCS_DIG_CTRL1, DW_VR_RST | DW_EN_VSMMD1);
147+
148+
/* wait pma initialization done */
149+
ret = read_poll_timeout(xpcs_read_vpcs, val, !(val & DW_VR_RST),
150+
100000, 10000000, false,
151+
xpcs, DW_VR_XS_PCS_DIG_CTRL1);
152+
if (ret < 0)
153+
dev_err(&xpcs->mdiodev->dev, "xpcs pma initialization timeout\n");
154+
155+
return ret;
156+
}
157+
158+
static bool txgbe_xpcs_mode_quirk(struct dw_xpcs *xpcs)
159+
{
160+
int ret;
161+
162+
/* When txgbe do LAN reset, PCS will change to default 10GBASE-R mode */
163+
ret = xpcs_read(xpcs, MDIO_MMD_PCS, MDIO_CTRL2);
164+
ret &= MDIO_PCS_CTRL2_TYPE;
165+
if (ret == MDIO_PCS_CTRL2_10GBR &&
166+
xpcs->interface != PHY_INTERFACE_MODE_10GBASER)
167+
return true;
168+
169+
return false;
170+
}
171+
172+
int txgbe_xpcs_switch_mode(struct dw_xpcs *xpcs, phy_interface_t interface)
173+
{
174+
int val, ret;
175+
176+
switch (interface) {
177+
case PHY_INTERFACE_MODE_10GBASER:
178+
case PHY_INTERFACE_MODE_SGMII:
179+
case PHY_INTERFACE_MODE_1000BASEX:
180+
break;
181+
default:
182+
return 0;
183+
}
184+
185+
if (xpcs->interface == interface && !txgbe_xpcs_mode_quirk(xpcs))
186+
return 0;
187+
188+
xpcs->interface = interface;
189+
190+
ret = txgbe_pcs_poll_power_up(xpcs);
191+
if (ret < 0)
192+
return ret;
193+
194+
if (interface == PHY_INTERFACE_MODE_10GBASER) {
195+
xpcs_write(xpcs, MDIO_MMD_PCS, MDIO_CTRL2, MDIO_PCS_CTRL2_10GBR);
196+
val = xpcs_read(xpcs, MDIO_MMD_PMAPMD, MDIO_CTRL1);
197+
val |= MDIO_CTRL1_SPEED10G;
198+
xpcs_write(xpcs, MDIO_MMD_PMAPMD, MDIO_CTRL1, val);
199+
txgbe_pma_config_10gbaser(xpcs);
200+
} else {
201+
xpcs_write(xpcs, MDIO_MMD_PCS, MDIO_CTRL2, MDIO_PCS_CTRL2_10GBX);
202+
xpcs_write(xpcs, MDIO_MMD_PMAPMD, MDIO_CTRL1, 0);
203+
xpcs_write(xpcs, MDIO_MMD_PCS, MDIO_CTRL1, 0);
204+
txgbe_pma_config_1g(xpcs);
205+
}
206+
207+
return txgbe_pma_init_done(xpcs);
208+
}

drivers/net/pcs/pcs-xpcs.c

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -228,12 +228,12 @@ static int xpcs_write_vendor(struct dw_xpcs *xpcs, int dev, int reg,
228228
return xpcs_write(xpcs, dev, DW_VENDOR | reg, val);
229229
}
230230

231-
static int xpcs_read_vpcs(struct dw_xpcs *xpcs, int reg)
231+
int xpcs_read_vpcs(struct dw_xpcs *xpcs, int reg)
232232
{
233233
return xpcs_read_vendor(xpcs, MDIO_MMD_PCS, reg);
234234
}
235235

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

844+
if (xpcs->dev_flag == DW_DEV_TXGBE) {
845+
ret = txgbe_xpcs_switch_mode(xpcs, interface);
846+
if (ret)
847+
return ret;
848+
}
849+
844850
switch (compat->an_mode) {
845851
case DW_10GBASER:
846852
break;
@@ -1313,9 +1319,6 @@ static struct dw_xpcs *xpcs_create(struct mdio_device *mdiodev,
13131319

13141320
xpcs->pcs.ops = &xpcs_phylink_ops;
13151321
xpcs->pcs.neg_mode = true;
1316-
if (compat->an_mode == DW_10GBASER)
1317-
return xpcs;
1318-
13191322
xpcs->pcs.poll = true;
13201323

13211324
if (xpcs->dev_flag != DW_DEV_TXGBE) {

drivers/net/pcs/pcs-xpcs.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,13 @@
1515
/* VR_XS_PCS */
1616
#define DW_USXGMII_RST BIT(10)
1717
#define DW_USXGMII_EN BIT(9)
18+
#define DW_VR_XS_PCS_DIG_CTRL1 0x0000
19+
#define DW_VR_RST BIT(15)
20+
#define DW_EN_VSMMD1 BIT(13)
1821
#define DW_VR_XS_PCS_DIG_STS 0x0010
1922
#define DW_RXFIFO_ERR GENMASK(6, 5)
23+
#define DW_PSEQ_ST GENMASK(4, 2)
24+
#define DW_PSEQ_ST_GOOD FIELD_PREP(GENMASK(4, 2), 0x4)
2025

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

107112
int xpcs_read(struct dw_xpcs *xpcs, int dev, u32 reg);
108113
int xpcs_write(struct dw_xpcs *xpcs, int dev, u32 reg, u16 val);
114+
int xpcs_read_vpcs(struct dw_xpcs *xpcs, int reg);
115+
int xpcs_write_vpcs(struct dw_xpcs *xpcs, int reg, u16 val);
109116
int nxp_sja1105_sgmii_pma_config(struct dw_xpcs *xpcs);
110117
int nxp_sja1110_sgmii_pma_config(struct dw_xpcs *xpcs);
111118
int nxp_sja1110_2500basex_pma_config(struct dw_xpcs *xpcs);
119+
int txgbe_xpcs_switch_mode(struct dw_xpcs *xpcs, phy_interface_t interface);

include/linux/pcs/pcs-xpcs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ struct dw_xpcs {
3232
struct mdio_device *mdiodev;
3333
const struct xpcs_id *id;
3434
struct phylink_pcs pcs;
35+
phy_interface_t interface;
3536
int dev_flag;
3637
};
3738

0 commit comments

Comments
 (0)