summaryrefslogtreecommitdiffstats
path: root/drivers/net/pcs/pcs-xpcs-wx.c
blob: 19c75886f070eaf2b856b8953b93911e0948e2ba (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
// 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) ||
	    xpcs->interface == PHY_INTERFACE_MODE_SGMII)
		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);
}