xref: /linux/drivers/phy/freescale/phy-fsl-lynx-10g.c (revision 62cf248de32f061d99cf7cd1675419d739031c5e)
1*6dc92ba4SVladimir Oltean // SPDX-License-Identifier: GPL-2.0+
2*6dc92ba4SVladimir Oltean /* Copyright 2021-2026 NXP */
3*6dc92ba4SVladimir Oltean 
4*6dc92ba4SVladimir Oltean #include <linux/delay.h>
5*6dc92ba4SVladimir Oltean #include <linux/module.h>
6*6dc92ba4SVladimir Oltean #include <linux/of.h>
7*6dc92ba4SVladimir Oltean #include <linux/phy.h>
8*6dc92ba4SVladimir Oltean #include <linux/phy/phy.h>
9*6dc92ba4SVladimir Oltean #include <linux/platform_device.h>
10*6dc92ba4SVladimir Oltean #include <linux/workqueue.h>
11*6dc92ba4SVladimir Oltean 
12*6dc92ba4SVladimir Oltean #include "phy-fsl-lynx-core.h"
13*6dc92ba4SVladimir Oltean 
14*6dc92ba4SVladimir Oltean /* SoC IP wrapper for protocol converters */
15*6dc92ba4SVladimir Oltean #define PCCR8				0x220
16*6dc92ba4SVladimir Oltean #define PCCR8_SGMIIa_KX			BIT(3)
17*6dc92ba4SVladimir Oltean #define PCCR8_SGMIIa_CFG		GENMASK(2, 0)
18*6dc92ba4SVladimir Oltean 
19*6dc92ba4SVladimir Oltean #define PCCR9				0x224
20*6dc92ba4SVladimir Oltean #define PCCR9_QSGMIIa_CFG		GENMASK(2, 0)
21*6dc92ba4SVladimir Oltean #define PCCR9_QXGMIIa_CFG		GENMASK(2, 0)
22*6dc92ba4SVladimir Oltean 
23*6dc92ba4SVladimir Oltean #define PCCRB				0x22c
24*6dc92ba4SVladimir Oltean #define PCCRB_XFIa_CFG			GENMASK(2, 0)
25*6dc92ba4SVladimir Oltean #define PCCRB_SXGMIIa_CFG		GENMASK(2, 0)
26*6dc92ba4SVladimir Oltean 
27*6dc92ba4SVladimir Oltean #define SGMII_CFG(id)			(28 - (id) * 4)
28*6dc92ba4SVladimir Oltean #define QSGMII_CFG(id)			(28 - (id) * 4)
29*6dc92ba4SVladimir Oltean #define SXGMII_CFG(id)			(28 - (id) * 4)
30*6dc92ba4SVladimir Oltean #define QXGMII_CFG(id)			(12 - (id) * 4)
31*6dc92ba4SVladimir Oltean #define XFI_CFG(id)			(28 - (id) * 4)
32*6dc92ba4SVladimir Oltean 
33*6dc92ba4SVladimir Oltean #define CR(x)				((x) * 4)
34*6dc92ba4SVladimir Oltean 
35*6dc92ba4SVladimir Oltean #define A				0
36*6dc92ba4SVladimir Oltean #define B				1
37*6dc92ba4SVladimir Oltean #define C				2
38*6dc92ba4SVladimir Oltean #define D				3
39*6dc92ba4SVladimir Oltean #define E				4
40*6dc92ba4SVladimir Oltean #define F				5
41*6dc92ba4SVladimir Oltean #define G				6
42*6dc92ba4SVladimir Oltean #define H				7
43*6dc92ba4SVladimir Oltean 
44*6dc92ba4SVladimir Oltean #define SGMIIaCR0(id)			(0x1800 + (id) * 0x10)
45*6dc92ba4SVladimir Oltean #define QSGMIIaCR0(id)			(0x1880 + (id) * 0x10)
46*6dc92ba4SVladimir Oltean #define XAUIaCR0(id)			(0x1900 + (id) * 0x10)
47*6dc92ba4SVladimir Oltean #define XFIaCR0(id)			(0x1980 + (id) * 0x10)
48*6dc92ba4SVladimir Oltean #define SXGMIIaCR0(id)			(0x1a80 + (id) * 0x10)
49*6dc92ba4SVladimir Oltean #define QXGMIIaCR0(id)			(0x1b00 + (id) * 0x20)
50*6dc92ba4SVladimir Oltean 
51*6dc92ba4SVladimir Oltean #define SGMIIaCR0_RST_SGM		BIT(31)
52*6dc92ba4SVladimir Oltean #define SGMIIaCR0_RST_SGM_OFF		SGMIIaCR0_RST_SGM
53*6dc92ba4SVladimir Oltean #define SGMIIaCR0_RST_SGM_ON		0
54*6dc92ba4SVladimir Oltean #define SGMIIaCR0_PD_SGM		BIT(30)
55*6dc92ba4SVladimir Oltean #define SGMIIaCR1_SGPCS_EN		BIT(11)
56*6dc92ba4SVladimir Oltean #define SGMIIaCR1_SGPCS_DIS		0x0
57*6dc92ba4SVladimir Oltean 
58*6dc92ba4SVladimir Oltean #define QSGMIIaCR0_RST_QSGM		BIT(31)
59*6dc92ba4SVladimir Oltean #define QSGMIIaCR0_RST_QSGM_OFF		QSGMIIaCR0_RST_QSGM
60*6dc92ba4SVladimir Oltean #define QSGMIIaCR0_RST_QSGM_ON		0
61*6dc92ba4SVladimir Oltean #define QSGMIIaCR0_PD_QSGM		BIT(30)
62*6dc92ba4SVladimir Oltean 
63*6dc92ba4SVladimir Oltean /* Per PLL registers */
64*6dc92ba4SVladimir Oltean #define PLLnCR0(pll)			((pll) * 0x20 + 0x4)
65*6dc92ba4SVladimir Oltean 
66*6dc92ba4SVladimir Oltean #define PLLnCR0_POFF			BIT(31)
67*6dc92ba4SVladimir Oltean 
68*6dc92ba4SVladimir Oltean #define PLLnCR0_REFCLK_SEL		GENMASK(30, 28)
69*6dc92ba4SVladimir Oltean #define PLLnCR0_REFCLK_SEL_100MHZ	0x0
70*6dc92ba4SVladimir Oltean #define PLLnCR0_REFCLK_SEL_125MHZ	0x1
71*6dc92ba4SVladimir Oltean #define PLLnCR0_REFCLK_SEL_156MHZ	0x2
72*6dc92ba4SVladimir Oltean #define PLLnCR0_REFCLK_SEL_150MHZ	0x3
73*6dc92ba4SVladimir Oltean #define PLLnCR0_REFCLK_SEL_161MHZ	0x4
74*6dc92ba4SVladimir Oltean #define PLLnCR0_PLL_LCK			BIT(23)
75*6dc92ba4SVladimir Oltean #define PLLnCR0_FRATE_SEL		GENMASK(19, 16)
76*6dc92ba4SVladimir Oltean #define PLLnCR0_FRATE_5G		0x0
77*6dc92ba4SVladimir Oltean #define PLLnCR0_FRATE_5_15625G		0x6
78*6dc92ba4SVladimir Oltean #define PLLnCR0_FRATE_4G		0x7
79*6dc92ba4SVladimir Oltean #define PLLnCR0_FRATE_3_125G		0x9
80*6dc92ba4SVladimir Oltean #define PLLnCR0_FRATE_3G		0xa
81*6dc92ba4SVladimir Oltean 
82*6dc92ba4SVladimir Oltean /* Per SerDes lane registers */
83*6dc92ba4SVladimir Oltean 
84*6dc92ba4SVladimir Oltean /* Lane a Protocol Select status register */
85*6dc92ba4SVladimir Oltean #define LNaPSSR0(lane)			(0x100 + (lane) * 0x20)
86*6dc92ba4SVladimir Oltean #define LNaPSSR0_TYPE			GENMASK(30, 26)
87*6dc92ba4SVladimir Oltean #define LNaPSSR0_IS_QUAD		GENMASK(25, 24)
88*6dc92ba4SVladimir Oltean #define LNaPSSR0_MAC			GENMASK(19, 16)
89*6dc92ba4SVladimir Oltean #define LNaPSSR0_PCS			GENMASK(10, 8)
90*6dc92ba4SVladimir Oltean #define LNaPSSR0_LANE			GENMASK(2, 0)
91*6dc92ba4SVladimir Oltean 
92*6dc92ba4SVladimir Oltean /* Lane a General Control Register */
93*6dc92ba4SVladimir Oltean #define LNaGCR0(lane)			(0x800 + (lane) * 0x40 + 0x0)
94*6dc92ba4SVladimir Oltean #define LNaGCR0_RPLL_PLLF		BIT(31)
95*6dc92ba4SVladimir Oltean #define LNaGCR0_RPLL_PLLS		0x0
96*6dc92ba4SVladimir Oltean #define LNaGCR0_RPLL_MSK		BIT(31)
97*6dc92ba4SVladimir Oltean #define LNaGCR0_RRAT_SEL		GENMASK(29, 28)
98*6dc92ba4SVladimir Oltean #define LNaGCR0_TRAT_SEL		GENMASK(25, 24)
99*6dc92ba4SVladimir Oltean #define LNaGCR0_TPLL_PLLF		BIT(27)
100*6dc92ba4SVladimir Oltean #define LNaGCR0_TPLL_PLLS		0x0
101*6dc92ba4SVladimir Oltean #define LNaGCR0_TPLL_MSK		BIT(27)
102*6dc92ba4SVladimir Oltean #define LNaGCR0_RRST_OFF		LNaGCR0_RRST
103*6dc92ba4SVladimir Oltean #define LNaGCR0_TRST_OFF		LNaGCR0_TRST
104*6dc92ba4SVladimir Oltean #define LNaGCR0_RRST_ON			0x0
105*6dc92ba4SVladimir Oltean #define LNaGCR0_TRST_ON			0x0
106*6dc92ba4SVladimir Oltean #define LNaGCR0_RRST			BIT(22)
107*6dc92ba4SVladimir Oltean #define LNaGCR0_TRST			BIT(21)
108*6dc92ba4SVladimir Oltean #define LNaGCR0_RX_PD			BIT(20)
109*6dc92ba4SVladimir Oltean #define LNaGCR0_TX_PD			BIT(19)
110*6dc92ba4SVladimir Oltean #define LNaGCR0_IF20BIT_EN		BIT(18)
111*6dc92ba4SVladimir Oltean #define LNaGCR0_PROTS			GENMASK(11, 7)
112*6dc92ba4SVladimir Oltean 
113*6dc92ba4SVladimir Oltean #define LNaGCR1(lane)			(0x800 + (lane) * 0x40 + 0x4)
114*6dc92ba4SVladimir Oltean #define LNaGCR1_RDAT_INV		BIT(31)
115*6dc92ba4SVladimir Oltean #define LNaGCR1_TDAT_INV		BIT(30)
116*6dc92ba4SVladimir Oltean #define LNaGCR1_OPAD_CTL		BIT(26)
117*6dc92ba4SVladimir Oltean #define LNaGCR1_REIDL_TH		GENMASK(22, 20)
118*6dc92ba4SVladimir Oltean #define LNaGCR1_REIDL_EX_SEL		GENMASK(19, 18)
119*6dc92ba4SVladimir Oltean #define LNaGCR1_REIDL_ET_SEL		GENMASK(17, 16)
120*6dc92ba4SVladimir Oltean #define LNaGCR1_REIDL_EX_MSB		BIT(15)
121*6dc92ba4SVladimir Oltean #define LNaGCR1_REIDL_ET_MSB		BIT(14)
122*6dc92ba4SVladimir Oltean #define LNaGCR1_REQ_CTL_SNP		BIT(13)
123*6dc92ba4SVladimir Oltean #define LNaGCR1_REQ_CDR_SNP		BIT(12)
124*6dc92ba4SVladimir Oltean #define LNaGCR1_TRSTDIR			BIT(7)
125*6dc92ba4SVladimir Oltean #define LNaGCR1_REQ_BIN_SNP		BIT(6)
126*6dc92ba4SVladimir Oltean #define LNaGCR1_ISLEW_RCTL		GENMASK(5, 4)
127*6dc92ba4SVladimir Oltean #define LNaGCR1_OSLEW_RCTL		GENMASK(1, 0)
128*6dc92ba4SVladimir Oltean 
129*6dc92ba4SVladimir Oltean #define LNaRECR0(lane)			(0x800 + (lane) * 0x40 + 0x10)
130*6dc92ba4SVladimir Oltean #define LNaRECR0_RXEQ_BST		BIT(28)
131*6dc92ba4SVladimir Oltean #define LNaRECR0_GK2OVD			GENMASK(27, 24)
132*6dc92ba4SVladimir Oltean #define LNaRECR0_GK3OVD			GENMASK(19, 16)
133*6dc92ba4SVladimir Oltean #define LNaRECR0_GK2OVD_EN		BIT(15)
134*6dc92ba4SVladimir Oltean #define LNaRECR0_GK3OVD_EN		BIT(14)
135*6dc92ba4SVladimir Oltean #define LNaRECR0_OSETOVD_EN		BIT(13)
136*6dc92ba4SVladimir Oltean #define LNaRECR0_BASE_WAND		GENMASK(11, 10)
137*6dc92ba4SVladimir Oltean #define LNaRECR0_OSETOVD		GENMASK(6, 0)
138*6dc92ba4SVladimir Oltean 
139*6dc92ba4SVladimir Oltean #define LNaTECR0(lane)			(0x800 + (lane) * 0x40 + 0x18)
140*6dc92ba4SVladimir Oltean #define LNaTECR0_TEQ_TYPE		GENMASK(29, 28)
141*6dc92ba4SVladimir Oltean #define LNaTECR0_SGN_PREQ		BIT(26)
142*6dc92ba4SVladimir Oltean #define LNaTECR0_RATIO_PREQ		GENMASK(25, 22)
143*6dc92ba4SVladimir Oltean #define LNaTECR0_SGN_POST1Q		BIT(21)
144*6dc92ba4SVladimir Oltean #define LNaTECR0_RATIO_PST1Q		GENMASK(20, 16)
145*6dc92ba4SVladimir Oltean #define LNaTECR0_ADPT_EQ		GENMASK(13, 8)
146*6dc92ba4SVladimir Oltean #define LNaTECR0_AMP_RED		GENMASK(5, 0)
147*6dc92ba4SVladimir Oltean 
148*6dc92ba4SVladimir Oltean #define LNaTTLCR0(lane)			(0x800 + (lane) * 0x40 + 0x20)
149*6dc92ba4SVladimir Oltean #define LNaTTLCR1(lane)			(0x800 + (lane) * 0x40 + 0x24)
150*6dc92ba4SVladimir Oltean #define LNaTTLCR2(lane)			(0x800 + (lane) * 0x40 + 0x28)
151*6dc92ba4SVladimir Oltean 
152*6dc92ba4SVladimir Oltean #define LNaTCSR3(lane)			(0x800 + (lane) * 0x40 + 0x3C)
153*6dc92ba4SVladimir Oltean #define LNaTCSR3_CDR_LCK		BIT(27)
154*6dc92ba4SVladimir Oltean 
155*6dc92ba4SVladimir Oltean enum lynx_10g_rat_sel {
156*6dc92ba4SVladimir Oltean 	RAT_SEL_FULL = 0x0,
157*6dc92ba4SVladimir Oltean 	RAT_SEL_HALF = 0x1,
158*6dc92ba4SVladimir Oltean 	RAT_SEL_QUARTER = 0x2,
159*6dc92ba4SVladimir Oltean 	RAT_SEL_DOUBLE = 0x3,
160*6dc92ba4SVladimir Oltean };
161*6dc92ba4SVladimir Oltean 
162*6dc92ba4SVladimir Oltean enum lynx_10g_eq_type {
163*6dc92ba4SVladimir Oltean 	EQ_TYPE_NO_EQ = 0,
164*6dc92ba4SVladimir Oltean 	EQ_TYPE_2TAP = 1,
165*6dc92ba4SVladimir Oltean 	EQ_TYPE_3TAP = 2,
166*6dc92ba4SVladimir Oltean };
167*6dc92ba4SVladimir Oltean 
168*6dc92ba4SVladimir Oltean enum lynx_10g_proto_sel {
169*6dc92ba4SVladimir Oltean 	PROTO_SEL_PCIE = 0,
170*6dc92ba4SVladimir Oltean 	PROTO_SEL_SGMII_BASEX_KX_QSGMII = 1,
171*6dc92ba4SVladimir Oltean 	PROTO_SEL_SATA = 2,
172*6dc92ba4SVladimir Oltean 	PROTO_SEL_XAUI = 4,
173*6dc92ba4SVladimir Oltean 	PROTO_SEL_XFI_10GBASER_KR_SXGMII = 0xa,
174*6dc92ba4SVladimir Oltean };
175*6dc92ba4SVladimir Oltean 
176*6dc92ba4SVladimir Oltean struct lynx_10g_proto_conf {
177*6dc92ba4SVladimir Oltean 	int proto_sel;
178*6dc92ba4SVladimir Oltean 	int if20bit_en;
179*6dc92ba4SVladimir Oltean 	int reidl_th;
180*6dc92ba4SVladimir Oltean 	int reidl_et_msb;
181*6dc92ba4SVladimir Oltean 	int reidl_et_sel;
182*6dc92ba4SVladimir Oltean 	int reidl_ex_msb;
183*6dc92ba4SVladimir Oltean 	int reidl_ex_sel;
184*6dc92ba4SVladimir Oltean 	int islew_rctl;
185*6dc92ba4SVladimir Oltean 	int oslew_rctl;
186*6dc92ba4SVladimir Oltean 	int rxeq_bst;
187*6dc92ba4SVladimir Oltean 	int gk2ovd;
188*6dc92ba4SVladimir Oltean 	int gk3ovd;
189*6dc92ba4SVladimir Oltean 	int gk2ovd_en;
190*6dc92ba4SVladimir Oltean 	int gk3ovd_en;
191*6dc92ba4SVladimir Oltean 	int base_wand;
192*6dc92ba4SVladimir Oltean 	int teq_type;
193*6dc92ba4SVladimir Oltean 	int sgn_preq;
194*6dc92ba4SVladimir Oltean 	int ratio_preq;
195*6dc92ba4SVladimir Oltean 	int sgn_post1q;
196*6dc92ba4SVladimir Oltean 	int ratio_post1q;
197*6dc92ba4SVladimir Oltean 	int adpt_eq;
198*6dc92ba4SVladimir Oltean 	int amp_red;
199*6dc92ba4SVladimir Oltean 	int ttlcr0;
200*6dc92ba4SVladimir Oltean };
201*6dc92ba4SVladimir Oltean 
202*6dc92ba4SVladimir Oltean static const struct lynx_10g_proto_conf lynx_10g_proto_conf[LANE_MODE_MAX] = {
203*6dc92ba4SVladimir Oltean 	[LANE_MODE_1000BASEX_SGMII] = {
204*6dc92ba4SVladimir Oltean 		.proto_sel = PROTO_SEL_SGMII_BASEX_KX_QSGMII,
205*6dc92ba4SVladimir Oltean 		.reidl_th = 1,
206*6dc92ba4SVladimir Oltean 		.reidl_ex_sel = 3,
207*6dc92ba4SVladimir Oltean 		.reidl_et_msb = 1,
208*6dc92ba4SVladimir Oltean 		.islew_rctl = 1,
209*6dc92ba4SVladimir Oltean 		.oslew_rctl = 1,
210*6dc92ba4SVladimir Oltean 		.gk2ovd = 15,
211*6dc92ba4SVladimir Oltean 		.gk3ovd = 15,
212*6dc92ba4SVladimir Oltean 		.gk2ovd_en = 1,
213*6dc92ba4SVladimir Oltean 		.gk3ovd_en = 1,
214*6dc92ba4SVladimir Oltean 		.teq_type = EQ_TYPE_NO_EQ,
215*6dc92ba4SVladimir Oltean 		.adpt_eq = 48,
216*6dc92ba4SVladimir Oltean 		.amp_red = 6,
217*6dc92ba4SVladimir Oltean 		.ttlcr0 = 0x39000400,
218*6dc92ba4SVladimir Oltean 	},
219*6dc92ba4SVladimir Oltean 	[LANE_MODE_2500BASEX] = {
220*6dc92ba4SVladimir Oltean 		.proto_sel = PROTO_SEL_SGMII_BASEX_KX_QSGMII,
221*6dc92ba4SVladimir Oltean 		.islew_rctl = 2,
222*6dc92ba4SVladimir Oltean 		.oslew_rctl = 2,
223*6dc92ba4SVladimir Oltean 		.teq_type = EQ_TYPE_2TAP,
224*6dc92ba4SVladimir Oltean 		.sgn_post1q = 1,
225*6dc92ba4SVladimir Oltean 		.ratio_post1q = 6,
226*6dc92ba4SVladimir Oltean 		.adpt_eq = 48,
227*6dc92ba4SVladimir Oltean 		.ttlcr0 = 0x00000400,
228*6dc92ba4SVladimir Oltean 	},
229*6dc92ba4SVladimir Oltean 	[LANE_MODE_QSGMII] = {
230*6dc92ba4SVladimir Oltean 		.proto_sel = PROTO_SEL_SGMII_BASEX_KX_QSGMII,
231*6dc92ba4SVladimir Oltean 		.islew_rctl = 1,
232*6dc92ba4SVladimir Oltean 		.oslew_rctl = 1,
233*6dc92ba4SVladimir Oltean 		.teq_type = EQ_TYPE_2TAP,
234*6dc92ba4SVladimir Oltean 		.sgn_post1q = 1,
235*6dc92ba4SVladimir Oltean 		.ratio_post1q = 6,
236*6dc92ba4SVladimir Oltean 		.adpt_eq = 48,
237*6dc92ba4SVladimir Oltean 		.amp_red = 2,
238*6dc92ba4SVladimir Oltean 		.ttlcr0 = 0x00000400,
239*6dc92ba4SVladimir Oltean 	},
240*6dc92ba4SVladimir Oltean 	[LANE_MODE_10G_QXGMII] = {
241*6dc92ba4SVladimir Oltean 		.proto_sel = PROTO_SEL_XFI_10GBASER_KR_SXGMII,
242*6dc92ba4SVladimir Oltean 		.if20bit_en = 1,
243*6dc92ba4SVladimir Oltean 		.islew_rctl = 1,
244*6dc92ba4SVladimir Oltean 		.oslew_rctl = 1,
245*6dc92ba4SVladimir Oltean 		.base_wand = 1,
246*6dc92ba4SVladimir Oltean 		.teq_type = EQ_TYPE_NO_EQ,
247*6dc92ba4SVladimir Oltean 		.adpt_eq = 48,
248*6dc92ba4SVladimir Oltean 		.ttlcr0 = 0x00000400,
249*6dc92ba4SVladimir Oltean 	},
250*6dc92ba4SVladimir Oltean 	[LANE_MODE_USXGMII] = {
251*6dc92ba4SVladimir Oltean 		.proto_sel = PROTO_SEL_XFI_10GBASER_KR_SXGMII,
252*6dc92ba4SVladimir Oltean 		.if20bit_en = 1,
253*6dc92ba4SVladimir Oltean 		.islew_rctl = 1,
254*6dc92ba4SVladimir Oltean 		.oslew_rctl = 1,
255*6dc92ba4SVladimir Oltean 		.base_wand = 1,
256*6dc92ba4SVladimir Oltean 		.teq_type = EQ_TYPE_NO_EQ,
257*6dc92ba4SVladimir Oltean 		.sgn_post1q = 1,
258*6dc92ba4SVladimir Oltean 		.adpt_eq = 48,
259*6dc92ba4SVladimir Oltean 		.ttlcr0 = 0x00000400,
260*6dc92ba4SVladimir Oltean 	},
261*6dc92ba4SVladimir Oltean 	[LANE_MODE_10GBASER] = {
262*6dc92ba4SVladimir Oltean 		.proto_sel = PROTO_SEL_XFI_10GBASER_KR_SXGMII,
263*6dc92ba4SVladimir Oltean 		.if20bit_en = 1,
264*6dc92ba4SVladimir Oltean 		.islew_rctl = 2,
265*6dc92ba4SVladimir Oltean 		.oslew_rctl = 2,
266*6dc92ba4SVladimir Oltean 		.rxeq_bst = 1,
267*6dc92ba4SVladimir Oltean 		.base_wand = 1,
268*6dc92ba4SVladimir Oltean 		.teq_type = EQ_TYPE_2TAP,
269*6dc92ba4SVladimir Oltean 		.sgn_post1q = 1,
270*6dc92ba4SVladimir Oltean 		.ratio_post1q = 3,
271*6dc92ba4SVladimir Oltean 		.adpt_eq = 48,
272*6dc92ba4SVladimir Oltean 		.amp_red = 7,
273*6dc92ba4SVladimir Oltean 		.ttlcr0 = 0x00000400,
274*6dc92ba4SVladimir Oltean 	},
275*6dc92ba4SVladimir Oltean };
276*6dc92ba4SVladimir Oltean 
277*6dc92ba4SVladimir Oltean static void lynx_10g_cdr_lock_check(struct lynx_lane *lane)
278*6dc92ba4SVladimir Oltean {
279*6dc92ba4SVladimir Oltean 	u32 tcsr3 = lynx_lane_read(lane, LNaTCSR3);
280*6dc92ba4SVladimir Oltean 
281*6dc92ba4SVladimir Oltean 	if (tcsr3 & LNaTCSR3_CDR_LCK)
282*6dc92ba4SVladimir Oltean 		return;
283*6dc92ba4SVladimir Oltean 
284*6dc92ba4SVladimir Oltean 	dev_dbg(&lane->phy->dev,
285*6dc92ba4SVladimir Oltean 		"Lane %c CDR unlocked, resetting receiver...\n",
286*6dc92ba4SVladimir Oltean 		'A' + lane->id);
287*6dc92ba4SVladimir Oltean 
288*6dc92ba4SVladimir Oltean 	lynx_lane_rmw(lane, LNaGCR0, LNaGCR0_RRST_ON, LNaGCR0_RRST);
289*6dc92ba4SVladimir Oltean 	usleep_range(1, 2);
290*6dc92ba4SVladimir Oltean 	lynx_lane_rmw(lane, LNaGCR0, LNaGCR0_RRST_OFF, LNaGCR0_RRST);
291*6dc92ba4SVladimir Oltean 
292*6dc92ba4SVladimir Oltean 	usleep_range(1, 2);
293*6dc92ba4SVladimir Oltean }
294*6dc92ba4SVladimir Oltean 
295*6dc92ba4SVladimir Oltean static void lynx_10g_pll_read_configuration(struct lynx_pll *pll)
296*6dc92ba4SVladimir Oltean {
297*6dc92ba4SVladimir Oltean 	u32 val;
298*6dc92ba4SVladimir Oltean 
299*6dc92ba4SVladimir Oltean 	val = lynx_pll_read(pll, PLLnCR0);
300*6dc92ba4SVladimir Oltean 	pll->frate_sel = FIELD_GET(PLLnCR0_FRATE_SEL, val);
301*6dc92ba4SVladimir Oltean 	pll->refclk_sel = FIELD_GET(PLLnCR0_REFCLK_SEL, val);
302*6dc92ba4SVladimir Oltean 	pll->enabled = !(val & PLLnCR0_POFF);
303*6dc92ba4SVladimir Oltean 	pll->locked = !!(val & PLLnCR0_PLL_LCK);
304*6dc92ba4SVladimir Oltean 
305*6dc92ba4SVladimir Oltean 	if (!pll->enabled)
306*6dc92ba4SVladimir Oltean 		return;
307*6dc92ba4SVladimir Oltean 
308*6dc92ba4SVladimir Oltean 	switch (pll->frate_sel) {
309*6dc92ba4SVladimir Oltean 	case PLLnCR0_FRATE_5G:
310*6dc92ba4SVladimir Oltean 		/* 5GHz clock net */
311*6dc92ba4SVladimir Oltean 		__set_bit(LANE_MODE_1000BASEX_SGMII, pll->supported);
312*6dc92ba4SVladimir Oltean 		__set_bit(LANE_MODE_QSGMII, pll->supported);
313*6dc92ba4SVladimir Oltean 		break;
314*6dc92ba4SVladimir Oltean 	case PLLnCR0_FRATE_3_125G:
315*6dc92ba4SVladimir Oltean 		__set_bit(LANE_MODE_2500BASEX, pll->supported);
316*6dc92ba4SVladimir Oltean 		break;
317*6dc92ba4SVladimir Oltean 	case PLLnCR0_FRATE_5_15625G:
318*6dc92ba4SVladimir Oltean 		/* 10.3125GHz clock net */
319*6dc92ba4SVladimir Oltean 		__set_bit(LANE_MODE_10GBASER, pll->supported);
320*6dc92ba4SVladimir Oltean 		__set_bit(LANE_MODE_USXGMII, pll->supported);
321*6dc92ba4SVladimir Oltean 		__set_bit(LANE_MODE_10G_QXGMII, pll->supported);
322*6dc92ba4SVladimir Oltean 		break;
323*6dc92ba4SVladimir Oltean 	default:
324*6dc92ba4SVladimir Oltean 		break;
325*6dc92ba4SVladimir Oltean 	}
326*6dc92ba4SVladimir Oltean }
327*6dc92ba4SVladimir Oltean 
328*6dc92ba4SVladimir Oltean /* On LS1028A, SGMIIA_CFG, SGMIIB_CFG, and SGMIIC_CFG from PCCR8 have the
329*6dc92ba4SVladimir Oltean  * ability to map either an ENETC PCS (PCCR8_SGMIIa_CFG=2) or a Felix switch
330*6dc92ba4SVladimir Oltean  * PCS (PCCR8_SGMIIa_CFG=1) to the same lane.
331*6dc92ba4SVladimir Oltean  *
332*6dc92ba4SVladimir Oltean  * On LS1088A, the same QSGMII PCS B can be connected to SerDes lane 1
333*6dc92ba4SVladimir Oltean  * (PCCR9_QSGMIIa_CFG=1) or to lane 3 (PCCR9_QSGMIIa_CFG=2).
334*6dc92ba4SVladimir Oltean  *
335*6dc92ba4SVladimir Oltean  * The PHY API lacks the capability to distinguish anything about the consumer,
336*6dc92ba4SVladimir Oltean  * so we don't support changing the initial muxing done by the RCW.
337*6dc92ba4SVladimir Oltean  *
338*6dc92ba4SVladimir Oltean  * However, after disabling a PCS through PCCR8, we need to properly restore
339*6dc92ba4SVladimir Oltean  * the original value to keep the same muxing, and for that we need to back
340*6dc92ba4SVladimir Oltean  * it up (here).
341*6dc92ba4SVladimir Oltean  */
342*6dc92ba4SVladimir Oltean static void lynx_10g_backup_pccr_val(struct lynx_lane *lane)
343*6dc92ba4SVladimir Oltean {
344*6dc92ba4SVladimir Oltean 	u32 val;
345*6dc92ba4SVladimir Oltean 	int err;
346*6dc92ba4SVladimir Oltean 
347*6dc92ba4SVladimir Oltean 	if (lane->mode == LANE_MODE_UNKNOWN)
348*6dc92ba4SVladimir Oltean 		return;
349*6dc92ba4SVladimir Oltean 
350*6dc92ba4SVladimir Oltean 	err = lynx_pccr_read(lane, lane->mode, &val);
351*6dc92ba4SVladimir Oltean 	if (err) {
352*6dc92ba4SVladimir Oltean 		dev_warn(&lane->phy->dev,
353*6dc92ba4SVladimir Oltean 			 "The driver doesn't know how to access the PCCR for lane mode %s\n",
354*6dc92ba4SVladimir Oltean 			 lynx_lane_mode_str(lane->mode));
355*6dc92ba4SVladimir Oltean 		lane->mode = LANE_MODE_UNKNOWN;
356*6dc92ba4SVladimir Oltean 		return;
357*6dc92ba4SVladimir Oltean 	}
358*6dc92ba4SVladimir Oltean 
359*6dc92ba4SVladimir Oltean 	lane->default_pccr[lane->mode] = val;
360*6dc92ba4SVladimir Oltean 
361*6dc92ba4SVladimir Oltean 	/* 1000Base-X, 1000Base-KX, 2500Base-KX and SGMII use the same PCCR8.
362*6dc92ba4SVladimir Oltean 	 * Only the KX bit differs (set for 1000Base-KX). Since we back up PCCR
363*6dc92ba4SVladimir Oltean 	 * values per lane mode, make sure to not back up the PCCR8 value with
364*6dc92ba4SVladimir Oltean 	 * the KX bit set for the non-KX modes, if the lane was in KX mode at
365*6dc92ba4SVladimir Oltean 	 * boot time. Just preserve bits 2:0, which tell whether the (and
366*6dc92ba4SVladimir Oltean 	 * which) 1G PCS was enabled.
367*6dc92ba4SVladimir Oltean 	 */
368*6dc92ba4SVladimir Oltean 	switch (lane->mode) {
369*6dc92ba4SVladimir Oltean 	case LANE_MODE_1000BASEX_SGMII:
370*6dc92ba4SVladimir Oltean 	case LANE_MODE_2500BASEX:
371*6dc92ba4SVladimir Oltean 		lane->default_pccr[LANE_MODE_1000BASEX_SGMII] = val & ~PCCR8_SGMIIa_KX;
372*6dc92ba4SVladimir Oltean 		lane->default_pccr[LANE_MODE_2500BASEX] = val & ~PCCR8_SGMIIa_KX;
373*6dc92ba4SVladimir Oltean 		break;
374*6dc92ba4SVladimir Oltean 	default:
375*6dc92ba4SVladimir Oltean 		break;
376*6dc92ba4SVladimir Oltean 	}
377*6dc92ba4SVladimir Oltean }
378*6dc92ba4SVladimir Oltean 
379*6dc92ba4SVladimir Oltean /* Is the PCS enabled, according to the value backed up from the PCCR register
380*6dc92ba4SVladimir Oltean  * for this lane mode?
381*6dc92ba4SVladimir Oltean  *
382*6dc92ba4SVladimir Oltean  * Normally we'd need to ask "what lane mode are we talking about?", but the
383*6dc92ba4SVladimir Oltean  * answer is invariably the same regardless - PCCR8_SGMIIa_CFG has the same
384*6dc92ba4SVladimir Oltean  * layout as PCCR9_QSGMIIa_CFG, PCCRB_XFIa_CFG etc etc, and the value 0
385*6dc92ba4SVladimir Oltean  * universally means "PCS disabled". So this is just a shorthand answer.
386*6dc92ba4SVladimir Oltean  */
387*6dc92ba4SVladimir Oltean static bool lynx_10g_pccr_val_enabled(u32 pccr)
388*6dc92ba4SVladimir Oltean {
389*6dc92ba4SVladimir Oltean 	return FIELD_PREP(PCCR8_SGMIIa_CFG, pccr) != 0;
390*6dc92ba4SVladimir Oltean }
391*6dc92ba4SVladimir Oltean 
392*6dc92ba4SVladimir Oltean static bool lynx_10g_lane_is_3_125g(struct lynx_lane *lane)
393*6dc92ba4SVladimir Oltean {
394*6dc92ba4SVladimir Oltean 	struct lynx_priv *priv = lane->priv;
395*6dc92ba4SVladimir Oltean 	struct lynx_pll *pll;
396*6dc92ba4SVladimir Oltean 	u32 gcr0;
397*6dc92ba4SVladimir Oltean 
398*6dc92ba4SVladimir Oltean 	gcr0 = lynx_lane_read(lane, LNaGCR0);
399*6dc92ba4SVladimir Oltean 
400*6dc92ba4SVladimir Oltean 	if (gcr0 & LNaGCR0_TPLL_PLLF)
401*6dc92ba4SVladimir Oltean 		pll = &priv->pll[0];
402*6dc92ba4SVladimir Oltean 	else
403*6dc92ba4SVladimir Oltean 		pll = &priv->pll[1];
404*6dc92ba4SVladimir Oltean 
405*6dc92ba4SVladimir Oltean 	if (pll->frate_sel != PLLnCR0_FRATE_3_125G)
406*6dc92ba4SVladimir Oltean 		return false;
407*6dc92ba4SVladimir Oltean 
408*6dc92ba4SVladimir Oltean 	if (FIELD_GET(LNaGCR0_TRAT_SEL, gcr0) != RAT_SEL_FULL ||
409*6dc92ba4SVladimir Oltean 	    FIELD_GET(LNaGCR0_RRAT_SEL, gcr0) != RAT_SEL_FULL)
410*6dc92ba4SVladimir Oltean 		return false;
411*6dc92ba4SVladimir Oltean 
412*6dc92ba4SVladimir Oltean 	return true;
413*6dc92ba4SVladimir Oltean }
414*6dc92ba4SVladimir Oltean 
415*6dc92ba4SVladimir Oltean static void lynx_10g_lane_read_configuration(struct lynx_lane *lane)
416*6dc92ba4SVladimir Oltean {
417*6dc92ba4SVladimir Oltean 	u32 pssr0 = lynx_lane_read(lane, LNaPSSR0);
418*6dc92ba4SVladimir Oltean 	struct lynx_priv *priv = lane->priv;
419*6dc92ba4SVladimir Oltean 	int proto;
420*6dc92ba4SVladimir Oltean 
421*6dc92ba4SVladimir Oltean 	proto = FIELD_GET(LNaPSSR0_TYPE, pssr0);
422*6dc92ba4SVladimir Oltean 	switch (proto) {
423*6dc92ba4SVladimir Oltean 	case PROTO_SEL_SGMII_BASEX_KX_QSGMII:
424*6dc92ba4SVladimir Oltean 		if (lynx_10g_lane_is_3_125g(lane))
425*6dc92ba4SVladimir Oltean 			lane->mode = LANE_MODE_2500BASEX;
426*6dc92ba4SVladimir Oltean 		else if (FIELD_GET(LNaPSSR0_IS_QUAD, pssr0))
427*6dc92ba4SVladimir Oltean 			lane->mode = LANE_MODE_QSGMII;
428*6dc92ba4SVladimir Oltean 		else
429*6dc92ba4SVladimir Oltean 			lane->mode = LANE_MODE_1000BASEX_SGMII;
430*6dc92ba4SVladimir Oltean 		break;
431*6dc92ba4SVladimir Oltean 	case PROTO_SEL_XFI_10GBASER_KR_SXGMII:
432*6dc92ba4SVladimir Oltean 		if (FIELD_GET(LNaPSSR0_IS_QUAD, pssr0))
433*6dc92ba4SVladimir Oltean 			lane->mode = LANE_MODE_10G_QXGMII;
434*6dc92ba4SVladimir Oltean 		else if (priv->info->quirks & LYNX_QUIRK_HAS_HARDCODED_USXGMII)
435*6dc92ba4SVladimir Oltean 			lane->mode = LANE_MODE_USXGMII;
436*6dc92ba4SVladimir Oltean 		else
437*6dc92ba4SVladimir Oltean 			lane->mode = LANE_MODE_10GBASER;
438*6dc92ba4SVladimir Oltean 		break;
439*6dc92ba4SVladimir Oltean 	case PROTO_SEL_PCIE:
440*6dc92ba4SVladimir Oltean 	case PROTO_SEL_SATA:
441*6dc92ba4SVladimir Oltean 	case PROTO_SEL_XAUI:
442*6dc92ba4SVladimir Oltean 		break;
443*6dc92ba4SVladimir Oltean 	default:
444*6dc92ba4SVladimir Oltean 		dev_warn(&lane->phy->dev, "Unknown lane protocol 0x%x\n",
445*6dc92ba4SVladimir Oltean 			 proto);
446*6dc92ba4SVladimir Oltean 	}
447*6dc92ba4SVladimir Oltean 
448*6dc92ba4SVladimir Oltean 	lynx_10g_backup_pccr_val(lane);
449*6dc92ba4SVladimir Oltean }
450*6dc92ba4SVladimir Oltean 
451*6dc92ba4SVladimir Oltean static int ls1028a_get_pccr(enum lynx_lane_mode lane_mode, int lane,
452*6dc92ba4SVladimir Oltean 			    struct lynx_pccr *pccr)
453*6dc92ba4SVladimir Oltean {
454*6dc92ba4SVladimir Oltean 	switch (lane_mode) {
455*6dc92ba4SVladimir Oltean 	case LANE_MODE_1000BASEX_SGMII:
456*6dc92ba4SVladimir Oltean 	case LANE_MODE_2500BASEX:
457*6dc92ba4SVladimir Oltean 		pccr->offset = PCCR8;
458*6dc92ba4SVladimir Oltean 		pccr->width = 4;
459*6dc92ba4SVladimir Oltean 		pccr->shift = SGMII_CFG(lane);
460*6dc92ba4SVladimir Oltean 		break;
461*6dc92ba4SVladimir Oltean 	case LANE_MODE_QSGMII:
462*6dc92ba4SVladimir Oltean 		if (lane != 1)
463*6dc92ba4SVladimir Oltean 			return -EINVAL;
464*6dc92ba4SVladimir Oltean 
465*6dc92ba4SVladimir Oltean 		pccr->offset = PCCR9;
466*6dc92ba4SVladimir Oltean 		pccr->width = 3;
467*6dc92ba4SVladimir Oltean 		pccr->shift = QSGMII_CFG(A);
468*6dc92ba4SVladimir Oltean 		break;
469*6dc92ba4SVladimir Oltean 	case LANE_MODE_10G_QXGMII:
470*6dc92ba4SVladimir Oltean 		if (lane != 1)
471*6dc92ba4SVladimir Oltean 			return -EINVAL;
472*6dc92ba4SVladimir Oltean 
473*6dc92ba4SVladimir Oltean 		pccr->offset = PCCR9;
474*6dc92ba4SVladimir Oltean 		pccr->width = 3;
475*6dc92ba4SVladimir Oltean 		pccr->shift = QXGMII_CFG(A);
476*6dc92ba4SVladimir Oltean 		break;
477*6dc92ba4SVladimir Oltean 	case LANE_MODE_USXGMII:
478*6dc92ba4SVladimir Oltean 		if (lane != 0)
479*6dc92ba4SVladimir Oltean 			return -EINVAL;
480*6dc92ba4SVladimir Oltean 
481*6dc92ba4SVladimir Oltean 		pccr->offset = PCCRB;
482*6dc92ba4SVladimir Oltean 		pccr->width = 3;
483*6dc92ba4SVladimir Oltean 		pccr->shift = SXGMII_CFG(A);
484*6dc92ba4SVladimir Oltean 		break;
485*6dc92ba4SVladimir Oltean 	default:
486*6dc92ba4SVladimir Oltean 		return -EINVAL;
487*6dc92ba4SVladimir Oltean 	}
488*6dc92ba4SVladimir Oltean 
489*6dc92ba4SVladimir Oltean 	return 0;
490*6dc92ba4SVladimir Oltean }
491*6dc92ba4SVladimir Oltean 
492*6dc92ba4SVladimir Oltean static int ls1028a_get_pcvt_offset(int lane, enum lynx_lane_mode mode)
493*6dc92ba4SVladimir Oltean {
494*6dc92ba4SVladimir Oltean 	switch (mode) {
495*6dc92ba4SVladimir Oltean 	case LANE_MODE_1000BASEX_SGMII:
496*6dc92ba4SVladimir Oltean 	case LANE_MODE_2500BASEX:
497*6dc92ba4SVladimir Oltean 		return SGMIIaCR0(lane);
498*6dc92ba4SVladimir Oltean 	case LANE_MODE_QSGMII:
499*6dc92ba4SVladimir Oltean 		return lane == 1 ? QSGMIIaCR0(A) : -EINVAL;
500*6dc92ba4SVladimir Oltean 	case LANE_MODE_USXGMII:
501*6dc92ba4SVladimir Oltean 		return lane == 0 ? SXGMIIaCR0(A) : -EINVAL;
502*6dc92ba4SVladimir Oltean 	case LANE_MODE_10G_QXGMII:
503*6dc92ba4SVladimir Oltean 		return lane == 1 ? QXGMIIaCR0(A) : -EINVAL;
504*6dc92ba4SVladimir Oltean 	default:
505*6dc92ba4SVladimir Oltean 		return -EINVAL;
506*6dc92ba4SVladimir Oltean 	}
507*6dc92ba4SVladimir Oltean }
508*6dc92ba4SVladimir Oltean 
509*6dc92ba4SVladimir Oltean static const struct lynx_info lynx_info_ls1028a = {
510*6dc92ba4SVladimir Oltean 	.get_pccr = ls1028a_get_pccr,
511*6dc92ba4SVladimir Oltean 	.get_pcvt_offset = ls1028a_get_pcvt_offset,
512*6dc92ba4SVladimir Oltean 	.pll_read_configuration = lynx_10g_pll_read_configuration,
513*6dc92ba4SVladimir Oltean 	.lane_read_configuration = lynx_10g_lane_read_configuration,
514*6dc92ba4SVladimir Oltean 	.cdr_lock_check = lynx_10g_cdr_lock_check,
515*6dc92ba4SVladimir Oltean 	.num_lanes = 4,
516*6dc92ba4SVladimir Oltean 	.index = 1,
517*6dc92ba4SVladimir Oltean 	.quirks = LYNX_QUIRK_HAS_HARDCODED_USXGMII,
518*6dc92ba4SVladimir Oltean };
519*6dc92ba4SVladimir Oltean 
520*6dc92ba4SVladimir Oltean static int ls1046a_serdes1_get_pccr(enum lynx_lane_mode lane_mode, int lane,
521*6dc92ba4SVladimir Oltean 				    struct lynx_pccr *pccr)
522*6dc92ba4SVladimir Oltean {
523*6dc92ba4SVladimir Oltean 	switch (lane_mode) {
524*6dc92ba4SVladimir Oltean 	case LANE_MODE_1000BASEX_SGMII:
525*6dc92ba4SVladimir Oltean 	case LANE_MODE_2500BASEX:
526*6dc92ba4SVladimir Oltean 		pccr->offset = PCCR8;
527*6dc92ba4SVladimir Oltean 		pccr->width = 4;
528*6dc92ba4SVladimir Oltean 		pccr->shift = SGMII_CFG(lane);
529*6dc92ba4SVladimir Oltean 		break;
530*6dc92ba4SVladimir Oltean 	case LANE_MODE_QSGMII:
531*6dc92ba4SVladimir Oltean 		if (lane != 1)
532*6dc92ba4SVladimir Oltean 			return -EINVAL;
533*6dc92ba4SVladimir Oltean 
534*6dc92ba4SVladimir Oltean 		pccr->offset = PCCR9;
535*6dc92ba4SVladimir Oltean 		pccr->width = 3;
536*6dc92ba4SVladimir Oltean 		pccr->shift = QSGMII_CFG(B);
537*6dc92ba4SVladimir Oltean 		break;
538*6dc92ba4SVladimir Oltean 	case LANE_MODE_10GBASER:
539*6dc92ba4SVladimir Oltean 		switch (lane) {
540*6dc92ba4SVladimir Oltean 		case 2:
541*6dc92ba4SVladimir Oltean 			pccr->shift = XFI_CFG(A);
542*6dc92ba4SVladimir Oltean 			break;
543*6dc92ba4SVladimir Oltean 		case 3:
544*6dc92ba4SVladimir Oltean 			pccr->shift = XFI_CFG(B);
545*6dc92ba4SVladimir Oltean 			break;
546*6dc92ba4SVladimir Oltean 		default:
547*6dc92ba4SVladimir Oltean 			return -EINVAL;
548*6dc92ba4SVladimir Oltean 		}
549*6dc92ba4SVladimir Oltean 
550*6dc92ba4SVladimir Oltean 		pccr->offset = PCCRB;
551*6dc92ba4SVladimir Oltean 		pccr->width = 3;
552*6dc92ba4SVladimir Oltean 		break;
553*6dc92ba4SVladimir Oltean 	default:
554*6dc92ba4SVladimir Oltean 		return -EINVAL;
555*6dc92ba4SVladimir Oltean 	}
556*6dc92ba4SVladimir Oltean 
557*6dc92ba4SVladimir Oltean 	return 0;
558*6dc92ba4SVladimir Oltean }
559*6dc92ba4SVladimir Oltean 
560*6dc92ba4SVladimir Oltean static int ls1046a_serdes1_get_pcvt_offset(int lane, enum lynx_lane_mode mode)
561*6dc92ba4SVladimir Oltean {
562*6dc92ba4SVladimir Oltean 	switch (mode) {
563*6dc92ba4SVladimir Oltean 	case LANE_MODE_1000BASEX_SGMII:
564*6dc92ba4SVladimir Oltean 	case LANE_MODE_2500BASEX:
565*6dc92ba4SVladimir Oltean 		return SGMIIaCR0(lane);
566*6dc92ba4SVladimir Oltean 	case LANE_MODE_QSGMII:
567*6dc92ba4SVladimir Oltean 		if (lane != 1)
568*6dc92ba4SVladimir Oltean 			return -EINVAL;
569*6dc92ba4SVladimir Oltean 
570*6dc92ba4SVladimir Oltean 		return QSGMIIaCR0(B);
571*6dc92ba4SVladimir Oltean 	case LANE_MODE_10GBASER:
572*6dc92ba4SVladimir Oltean 		switch (lane) {
573*6dc92ba4SVladimir Oltean 		case 2:
574*6dc92ba4SVladimir Oltean 			return XFIaCR0(A);
575*6dc92ba4SVladimir Oltean 		case 3:
576*6dc92ba4SVladimir Oltean 			return XFIaCR0(B);
577*6dc92ba4SVladimir Oltean 		default:
578*6dc92ba4SVladimir Oltean 			return -EINVAL;
579*6dc92ba4SVladimir Oltean 		}
580*6dc92ba4SVladimir Oltean 	default:
581*6dc92ba4SVladimir Oltean 		return -EINVAL;
582*6dc92ba4SVladimir Oltean 	}
583*6dc92ba4SVladimir Oltean }
584*6dc92ba4SVladimir Oltean 
585*6dc92ba4SVladimir Oltean static const struct lynx_info lynx_info_ls1046a_serdes1 = {
586*6dc92ba4SVladimir Oltean 	.get_pccr = ls1046a_serdes1_get_pccr,
587*6dc92ba4SVladimir Oltean 	.get_pcvt_offset = ls1046a_serdes1_get_pcvt_offset,
588*6dc92ba4SVladimir Oltean 	.pll_read_configuration = lynx_10g_pll_read_configuration,
589*6dc92ba4SVladimir Oltean 	.lane_read_configuration = lynx_10g_lane_read_configuration,
590*6dc92ba4SVladimir Oltean 	.cdr_lock_check = lynx_10g_cdr_lock_check,
591*6dc92ba4SVladimir Oltean 	.num_lanes = 4,
592*6dc92ba4SVladimir Oltean 	.index = 1,
593*6dc92ba4SVladimir Oltean };
594*6dc92ba4SVladimir Oltean 
595*6dc92ba4SVladimir Oltean static int ls1046a_serdes2_get_pccr(enum lynx_lane_mode lane_mode, int lane,
596*6dc92ba4SVladimir Oltean 				    struct lynx_pccr *pccr)
597*6dc92ba4SVladimir Oltean {
598*6dc92ba4SVladimir Oltean 	switch (lane_mode) {
599*6dc92ba4SVladimir Oltean 	case LANE_MODE_1000BASEX_SGMII:
600*6dc92ba4SVladimir Oltean 	case LANE_MODE_2500BASEX:
601*6dc92ba4SVladimir Oltean 		if (lane != 1)
602*6dc92ba4SVladimir Oltean 			return -EINVAL;
603*6dc92ba4SVladimir Oltean 
604*6dc92ba4SVladimir Oltean 		pccr->offset = PCCR8;
605*6dc92ba4SVladimir Oltean 		pccr->width = 4;
606*6dc92ba4SVladimir Oltean 		pccr->shift = SGMII_CFG(B);
607*6dc92ba4SVladimir Oltean 		break;
608*6dc92ba4SVladimir Oltean 	default:
609*6dc92ba4SVladimir Oltean 		return -EINVAL;
610*6dc92ba4SVladimir Oltean 	}
611*6dc92ba4SVladimir Oltean 
612*6dc92ba4SVladimir Oltean 	return 0;
613*6dc92ba4SVladimir Oltean }
614*6dc92ba4SVladimir Oltean 
615*6dc92ba4SVladimir Oltean static int ls1046a_serdes2_get_pcvt_offset(int lane, enum lynx_lane_mode mode)
616*6dc92ba4SVladimir Oltean {
617*6dc92ba4SVladimir Oltean 	switch (mode) {
618*6dc92ba4SVladimir Oltean 	case LANE_MODE_1000BASEX_SGMII:
619*6dc92ba4SVladimir Oltean 	case LANE_MODE_2500BASEX:
620*6dc92ba4SVladimir Oltean 		if (lane != 1)
621*6dc92ba4SVladimir Oltean 			return -EINVAL;
622*6dc92ba4SVladimir Oltean 
623*6dc92ba4SVladimir Oltean 		return SGMIIaCR0(B);
624*6dc92ba4SVladimir Oltean 	default:
625*6dc92ba4SVladimir Oltean 		return -EINVAL;
626*6dc92ba4SVladimir Oltean 	}
627*6dc92ba4SVladimir Oltean }
628*6dc92ba4SVladimir Oltean 
629*6dc92ba4SVladimir Oltean static const struct lynx_info lynx_info_ls1046a_serdes2 = {
630*6dc92ba4SVladimir Oltean 	.get_pccr = ls1046a_serdes2_get_pccr,
631*6dc92ba4SVladimir Oltean 	.get_pcvt_offset = ls1046a_serdes2_get_pcvt_offset,
632*6dc92ba4SVladimir Oltean 	.pll_read_configuration = lynx_10g_pll_read_configuration,
633*6dc92ba4SVladimir Oltean 	.lane_read_configuration = lynx_10g_lane_read_configuration,
634*6dc92ba4SVladimir Oltean 	.cdr_lock_check = lynx_10g_cdr_lock_check,
635*6dc92ba4SVladimir Oltean 	.num_lanes = 4,
636*6dc92ba4SVladimir Oltean 	.index = 2,
637*6dc92ba4SVladimir Oltean };
638*6dc92ba4SVladimir Oltean 
639*6dc92ba4SVladimir Oltean static int ls1088a_serdes1_get_pccr(enum lynx_lane_mode lane_mode, int lane,
640*6dc92ba4SVladimir Oltean 				    struct lynx_pccr *pccr)
641*6dc92ba4SVladimir Oltean {
642*6dc92ba4SVladimir Oltean 	switch (lane_mode) {
643*6dc92ba4SVladimir Oltean 	case LANE_MODE_1000BASEX_SGMII:
644*6dc92ba4SVladimir Oltean 		pccr->offset = PCCR8;
645*6dc92ba4SVladimir Oltean 		pccr->width = 4;
646*6dc92ba4SVladimir Oltean 		pccr->shift = SGMII_CFG(lane);
647*6dc92ba4SVladimir Oltean 		break;
648*6dc92ba4SVladimir Oltean 	case LANE_MODE_QSGMII:
649*6dc92ba4SVladimir Oltean 		switch (lane) {
650*6dc92ba4SVladimir Oltean 		case 0:
651*6dc92ba4SVladimir Oltean 			pccr->shift = QSGMII_CFG(A);
652*6dc92ba4SVladimir Oltean 			break;
653*6dc92ba4SVladimir Oltean 		case 1:
654*6dc92ba4SVladimir Oltean 		case 3:
655*6dc92ba4SVladimir Oltean 			pccr->shift = QSGMII_CFG(B);
656*6dc92ba4SVladimir Oltean 			break;
657*6dc92ba4SVladimir Oltean 		default:
658*6dc92ba4SVladimir Oltean 			return -EINVAL;
659*6dc92ba4SVladimir Oltean 		}
660*6dc92ba4SVladimir Oltean 
661*6dc92ba4SVladimir Oltean 		pccr->offset = PCCR9;
662*6dc92ba4SVladimir Oltean 		pccr->width = 3;
663*6dc92ba4SVladimir Oltean 		break;
664*6dc92ba4SVladimir Oltean 	case LANE_MODE_10GBASER:
665*6dc92ba4SVladimir Oltean 		switch (lane) {
666*6dc92ba4SVladimir Oltean 		case 2:
667*6dc92ba4SVladimir Oltean 			pccr->shift = XFI_CFG(A);
668*6dc92ba4SVladimir Oltean 			break;
669*6dc92ba4SVladimir Oltean 		case 3:
670*6dc92ba4SVladimir Oltean 			pccr->shift = XFI_CFG(B);
671*6dc92ba4SVladimir Oltean 			break;
672*6dc92ba4SVladimir Oltean 		default:
673*6dc92ba4SVladimir Oltean 			return -EINVAL;
674*6dc92ba4SVladimir Oltean 		}
675*6dc92ba4SVladimir Oltean 
676*6dc92ba4SVladimir Oltean 		pccr->offset = PCCRB;
677*6dc92ba4SVladimir Oltean 		pccr->width = 3;
678*6dc92ba4SVladimir Oltean 		break;
679*6dc92ba4SVladimir Oltean 	default:
680*6dc92ba4SVladimir Oltean 		return -EINVAL;
681*6dc92ba4SVladimir Oltean 	}
682*6dc92ba4SVladimir Oltean 
683*6dc92ba4SVladimir Oltean 	return 0;
684*6dc92ba4SVladimir Oltean }
685*6dc92ba4SVladimir Oltean 
686*6dc92ba4SVladimir Oltean static int ls1088a_serdes1_get_pcvt_offset(int lane, enum lynx_lane_mode mode)
687*6dc92ba4SVladimir Oltean {
688*6dc92ba4SVladimir Oltean 	switch (mode) {
689*6dc92ba4SVladimir Oltean 	case LANE_MODE_1000BASEX_SGMII:
690*6dc92ba4SVladimir Oltean 		return SGMIIaCR0(lane);
691*6dc92ba4SVladimir Oltean 	case LANE_MODE_QSGMII:
692*6dc92ba4SVladimir Oltean 		switch (lane) {
693*6dc92ba4SVladimir Oltean 		case 0:
694*6dc92ba4SVladimir Oltean 			return QSGMIIaCR0(A);
695*6dc92ba4SVladimir Oltean 		case 1:
696*6dc92ba4SVladimir Oltean 		case 3:
697*6dc92ba4SVladimir Oltean 			return QSGMIIaCR0(B);
698*6dc92ba4SVladimir Oltean 		default:
699*6dc92ba4SVladimir Oltean 			return -EINVAL;
700*6dc92ba4SVladimir Oltean 		}
701*6dc92ba4SVladimir Oltean 	case LANE_MODE_10GBASER:
702*6dc92ba4SVladimir Oltean 		switch (lane) {
703*6dc92ba4SVladimir Oltean 		case 2:
704*6dc92ba4SVladimir Oltean 			return XFIaCR0(A);
705*6dc92ba4SVladimir Oltean 		case 3:
706*6dc92ba4SVladimir Oltean 			return XFIaCR0(B);
707*6dc92ba4SVladimir Oltean 		default:
708*6dc92ba4SVladimir Oltean 			return -EINVAL;
709*6dc92ba4SVladimir Oltean 		}
710*6dc92ba4SVladimir Oltean 	default:
711*6dc92ba4SVladimir Oltean 		return -EINVAL;
712*6dc92ba4SVladimir Oltean 	}
713*6dc92ba4SVladimir Oltean }
714*6dc92ba4SVladimir Oltean 
715*6dc92ba4SVladimir Oltean static const struct lynx_info lynx_info_ls1088a_serdes1 = {
716*6dc92ba4SVladimir Oltean 	.get_pccr = ls1088a_serdes1_get_pccr,
717*6dc92ba4SVladimir Oltean 	.get_pcvt_offset = ls1088a_serdes1_get_pcvt_offset,
718*6dc92ba4SVladimir Oltean 	.pll_read_configuration = lynx_10g_pll_read_configuration,
719*6dc92ba4SVladimir Oltean 	.lane_read_configuration = lynx_10g_lane_read_configuration,
720*6dc92ba4SVladimir Oltean 	.cdr_lock_check = lynx_10g_cdr_lock_check,
721*6dc92ba4SVladimir Oltean 	.num_lanes = 4,
722*6dc92ba4SVladimir Oltean 	.index = 1,
723*6dc92ba4SVladimir Oltean };
724*6dc92ba4SVladimir Oltean 
725*6dc92ba4SVladimir Oltean static int ls2088a_serdes1_get_pccr(enum lynx_lane_mode lane_mode, int lane,
726*6dc92ba4SVladimir Oltean 				    struct lynx_pccr *pccr)
727*6dc92ba4SVladimir Oltean {
728*6dc92ba4SVladimir Oltean 	switch (lane_mode) {
729*6dc92ba4SVladimir Oltean 	case LANE_MODE_1000BASEX_SGMII:
730*6dc92ba4SVladimir Oltean 	case LANE_MODE_2500BASEX:
731*6dc92ba4SVladimir Oltean 		pccr->offset = PCCR8;
732*6dc92ba4SVladimir Oltean 		pccr->width = 4;
733*6dc92ba4SVladimir Oltean 		pccr->shift = SGMII_CFG(lane);
734*6dc92ba4SVladimir Oltean 		break;
735*6dc92ba4SVladimir Oltean 	case LANE_MODE_QSGMII:
736*6dc92ba4SVladimir Oltean 		switch (lane) {
737*6dc92ba4SVladimir Oltean 		case 2:
738*6dc92ba4SVladimir Oltean 		case 6:
739*6dc92ba4SVladimir Oltean 			pccr->shift = QSGMII_CFG(A);
740*6dc92ba4SVladimir Oltean 			break;
741*6dc92ba4SVladimir Oltean 		case 7:
742*6dc92ba4SVladimir Oltean 			pccr->shift = QSGMII_CFG(B);
743*6dc92ba4SVladimir Oltean 			break;
744*6dc92ba4SVladimir Oltean 		case 0:
745*6dc92ba4SVladimir Oltean 		case 4:
746*6dc92ba4SVladimir Oltean 			pccr->shift = QSGMII_CFG(C);
747*6dc92ba4SVladimir Oltean 			break;
748*6dc92ba4SVladimir Oltean 		case 1:
749*6dc92ba4SVladimir Oltean 		case 5:
750*6dc92ba4SVladimir Oltean 			pccr->shift = QSGMII_CFG(D);
751*6dc92ba4SVladimir Oltean 			break;
752*6dc92ba4SVladimir Oltean 		default:
753*6dc92ba4SVladimir Oltean 			return -EINVAL;
754*6dc92ba4SVladimir Oltean 		}
755*6dc92ba4SVladimir Oltean 
756*6dc92ba4SVladimir Oltean 		pccr->offset = PCCR9;
757*6dc92ba4SVladimir Oltean 		pccr->width = 3;
758*6dc92ba4SVladimir Oltean 		break;
759*6dc92ba4SVladimir Oltean 	case LANE_MODE_10GBASER:
760*6dc92ba4SVladimir Oltean 		pccr->offset = PCCRB;
761*6dc92ba4SVladimir Oltean 		pccr->width = 3;
762*6dc92ba4SVladimir Oltean 		pccr->shift = XFI_CFG(lane);
763*6dc92ba4SVladimir Oltean 		break;
764*6dc92ba4SVladimir Oltean 	default:
765*6dc92ba4SVladimir Oltean 		return -EINVAL;
766*6dc92ba4SVladimir Oltean 	}
767*6dc92ba4SVladimir Oltean 
768*6dc92ba4SVladimir Oltean 	return 0;
769*6dc92ba4SVladimir Oltean }
770*6dc92ba4SVladimir Oltean 
771*6dc92ba4SVladimir Oltean static int ls2088a_serdes1_get_pcvt_offset(int lane, enum lynx_lane_mode mode)
772*6dc92ba4SVladimir Oltean {
773*6dc92ba4SVladimir Oltean 	switch (mode) {
774*6dc92ba4SVladimir Oltean 	case LANE_MODE_1000BASEX_SGMII:
775*6dc92ba4SVladimir Oltean 	case LANE_MODE_2500BASEX:
776*6dc92ba4SVladimir Oltean 		return SGMIIaCR0(lane);
777*6dc92ba4SVladimir Oltean 	case LANE_MODE_QSGMII:
778*6dc92ba4SVladimir Oltean 		switch (lane) {
779*6dc92ba4SVladimir Oltean 		case 2:
780*6dc92ba4SVladimir Oltean 		case 6:
781*6dc92ba4SVladimir Oltean 			return QSGMIIaCR0(A);
782*6dc92ba4SVladimir Oltean 		case 7:
783*6dc92ba4SVladimir Oltean 			return QSGMIIaCR0(B);
784*6dc92ba4SVladimir Oltean 		case 0:
785*6dc92ba4SVladimir Oltean 		case 4:
786*6dc92ba4SVladimir Oltean 			return QSGMIIaCR0(C);
787*6dc92ba4SVladimir Oltean 		case 1:
788*6dc92ba4SVladimir Oltean 		case 5:
789*6dc92ba4SVladimir Oltean 			return QSGMIIaCR0(D);
790*6dc92ba4SVladimir Oltean 		default:
791*6dc92ba4SVladimir Oltean 			return -EINVAL;
792*6dc92ba4SVladimir Oltean 		}
793*6dc92ba4SVladimir Oltean 	case LANE_MODE_10GBASER:
794*6dc92ba4SVladimir Oltean 		return XFIaCR0(lane);
795*6dc92ba4SVladimir Oltean 	default:
796*6dc92ba4SVladimir Oltean 		return -EINVAL;
797*6dc92ba4SVladimir Oltean 	}
798*6dc92ba4SVladimir Oltean }
799*6dc92ba4SVladimir Oltean 
800*6dc92ba4SVladimir Oltean static const struct lynx_info lynx_info_ls2088a_serdes1 = {
801*6dc92ba4SVladimir Oltean 	.get_pccr = ls2088a_serdes1_get_pccr,
802*6dc92ba4SVladimir Oltean 	.get_pcvt_offset = ls2088a_serdes1_get_pcvt_offset,
803*6dc92ba4SVladimir Oltean 	.pll_read_configuration = lynx_10g_pll_read_configuration,
804*6dc92ba4SVladimir Oltean 	.lane_read_configuration = lynx_10g_lane_read_configuration,
805*6dc92ba4SVladimir Oltean 	.cdr_lock_check = lynx_10g_cdr_lock_check,
806*6dc92ba4SVladimir Oltean 	.num_lanes = 8,
807*6dc92ba4SVladimir Oltean 	.index = 1,
808*6dc92ba4SVladimir Oltean };
809*6dc92ba4SVladimir Oltean 
810*6dc92ba4SVladimir Oltean static int ls2088a_serdes2_get_pccr(enum lynx_lane_mode lane_mode, int lane,
811*6dc92ba4SVladimir Oltean 				    struct lynx_pccr *pccr)
812*6dc92ba4SVladimir Oltean {
813*6dc92ba4SVladimir Oltean 	switch (lane_mode) {
814*6dc92ba4SVladimir Oltean 	case LANE_MODE_1000BASEX_SGMII:
815*6dc92ba4SVladimir Oltean 	case LANE_MODE_2500BASEX:
816*6dc92ba4SVladimir Oltean 		pccr->offset = PCCR8;
817*6dc92ba4SVladimir Oltean 		pccr->width = 4;
818*6dc92ba4SVladimir Oltean 		pccr->shift = SGMII_CFG(lane);
819*6dc92ba4SVladimir Oltean 		break;
820*6dc92ba4SVladimir Oltean 	default:
821*6dc92ba4SVladimir Oltean 		return -EINVAL;
822*6dc92ba4SVladimir Oltean 	}
823*6dc92ba4SVladimir Oltean 
824*6dc92ba4SVladimir Oltean 	return 0;
825*6dc92ba4SVladimir Oltean }
826*6dc92ba4SVladimir Oltean 
827*6dc92ba4SVladimir Oltean static int ls2088a_serdes2_get_pcvt_offset(int lane, enum lynx_lane_mode mode)
828*6dc92ba4SVladimir Oltean {
829*6dc92ba4SVladimir Oltean 	switch (mode) {
830*6dc92ba4SVladimir Oltean 	case LANE_MODE_1000BASEX_SGMII:
831*6dc92ba4SVladimir Oltean 	case LANE_MODE_2500BASEX:
832*6dc92ba4SVladimir Oltean 		return SGMIIaCR0(lane);
833*6dc92ba4SVladimir Oltean 	default:
834*6dc92ba4SVladimir Oltean 		return -EINVAL;
835*6dc92ba4SVladimir Oltean 	}
836*6dc92ba4SVladimir Oltean }
837*6dc92ba4SVladimir Oltean 
838*6dc92ba4SVladimir Oltean static const struct lynx_info lynx_info_ls2088a_serdes2 = {
839*6dc92ba4SVladimir Oltean 	.get_pccr = ls2088a_serdes2_get_pccr,
840*6dc92ba4SVladimir Oltean 	.get_pcvt_offset = ls2088a_serdes2_get_pcvt_offset,
841*6dc92ba4SVladimir Oltean 	.pll_read_configuration = lynx_10g_pll_read_configuration,
842*6dc92ba4SVladimir Oltean 	.lane_read_configuration = lynx_10g_lane_read_configuration,
843*6dc92ba4SVladimir Oltean 	.cdr_lock_check = lynx_10g_cdr_lock_check,
844*6dc92ba4SVladimir Oltean 	.num_lanes = 8,
845*6dc92ba4SVladimir Oltean 	.index = 2,
846*6dc92ba4SVladimir Oltean };
847*6dc92ba4SVladimir Oltean 
848*6dc92ba4SVladimir Oltean /* Halting puts the lane in a mode in which it can be reconfigured */
849*6dc92ba4SVladimir Oltean static void lynx_10g_lane_halt(struct phy *phy)
850*6dc92ba4SVladimir Oltean {
851*6dc92ba4SVladimir Oltean 	struct lynx_lane *lane = phy_get_drvdata(phy);
852*6dc92ba4SVladimir Oltean 
853*6dc92ba4SVladimir Oltean 	/* Issue a reset request */
854*6dc92ba4SVladimir Oltean 	lynx_lane_rmw(lane, LNaGCR0,
855*6dc92ba4SVladimir Oltean 		      LNaGCR0_RRST_ON | LNaGCR0_TRST_ON,
856*6dc92ba4SVladimir Oltean 		      LNaGCR0_RRST | LNaGCR0_TRST);
857*6dc92ba4SVladimir Oltean 
858*6dc92ba4SVladimir Oltean 	/* The RM says to wait for at least 50ns */
859*6dc92ba4SVladimir Oltean 	usleep_range(1, 2);
860*6dc92ba4SVladimir Oltean }
861*6dc92ba4SVladimir Oltean 
862*6dc92ba4SVladimir Oltean static void lynx_10g_lane_reset(struct phy *phy)
863*6dc92ba4SVladimir Oltean {
864*6dc92ba4SVladimir Oltean 	struct lynx_lane *lane = phy_get_drvdata(phy);
865*6dc92ba4SVladimir Oltean 
866*6dc92ba4SVladimir Oltean 	/* Finalize the reset request */
867*6dc92ba4SVladimir Oltean 	lynx_lane_rmw(lane, LNaGCR0,
868*6dc92ba4SVladimir Oltean 		      LNaGCR0_RRST_OFF | LNaGCR0_TRST_OFF,
869*6dc92ba4SVladimir Oltean 		      LNaGCR0_RRST | LNaGCR0_TRST);
870*6dc92ba4SVladimir Oltean }
871*6dc92ba4SVladimir Oltean 
872*6dc92ba4SVladimir Oltean static int lynx_10g_power_off(struct phy *phy)
873*6dc92ba4SVladimir Oltean {
874*6dc92ba4SVladimir Oltean 	struct lynx_lane *lane = phy_get_drvdata(phy);
875*6dc92ba4SVladimir Oltean 
876*6dc92ba4SVladimir Oltean 	if (!lane->powered_up)
877*6dc92ba4SVladimir Oltean 		return 0;
878*6dc92ba4SVladimir Oltean 
879*6dc92ba4SVladimir Oltean 	/* Issue a reset request with the power down bits set */
880*6dc92ba4SVladimir Oltean 	lynx_lane_rmw(lane, LNaGCR0,
881*6dc92ba4SVladimir Oltean 		      LNaGCR0_RRST_ON | LNaGCR0_TRST_ON |
882*6dc92ba4SVladimir Oltean 		      LNaGCR0_RX_PD | LNaGCR0_TX_PD,
883*6dc92ba4SVladimir Oltean 		      LNaGCR0_RRST | LNaGCR0_TRST |
884*6dc92ba4SVladimir Oltean 		      LNaGCR0_RX_PD | LNaGCR0_TX_PD);
885*6dc92ba4SVladimir Oltean 
886*6dc92ba4SVladimir Oltean 	/* The RM says to wait for at least 50ns */
887*6dc92ba4SVladimir Oltean 	usleep_range(1, 2);
888*6dc92ba4SVladimir Oltean 
889*6dc92ba4SVladimir Oltean 	lane->powered_up = false;
890*6dc92ba4SVladimir Oltean 
891*6dc92ba4SVladimir Oltean 	return 0;
892*6dc92ba4SVladimir Oltean }
893*6dc92ba4SVladimir Oltean 
894*6dc92ba4SVladimir Oltean static int lynx_10g_power_on(struct phy *phy)
895*6dc92ba4SVladimir Oltean {
896*6dc92ba4SVladimir Oltean 	struct lynx_lane *lane = phy_get_drvdata(phy);
897*6dc92ba4SVladimir Oltean 
898*6dc92ba4SVladimir Oltean 	if (lane->powered_up)
899*6dc92ba4SVladimir Oltean 		return 0;
900*6dc92ba4SVladimir Oltean 
901*6dc92ba4SVladimir Oltean 	/* RM says that to enable a previously powered down lane, set
902*6dc92ba4SVladimir Oltean 	 * LNmGCR0[{R,T}X_PD]=0, wait 15 us, then set LNmGCR0[{R,T}RST]=1.
903*6dc92ba4SVladimir Oltean 	 */
904*6dc92ba4SVladimir Oltean 	lynx_lane_rmw(lane, LNaGCR0, 0, LNaGCR0_RX_PD | LNaGCR0_TX_PD);
905*6dc92ba4SVladimir Oltean 	usleep_range(150, 300);
906*6dc92ba4SVladimir Oltean 	lynx_10g_lane_reset(phy);
907*6dc92ba4SVladimir Oltean 
908*6dc92ba4SVladimir Oltean 	lane->powered_up = true;
909*6dc92ba4SVladimir Oltean 
910*6dc92ba4SVladimir Oltean 	return 0;
911*6dc92ba4SVladimir Oltean }
912*6dc92ba4SVladimir Oltean 
913*6dc92ba4SVladimir Oltean static void lynx_10g_lane_set_nrate(struct lynx_lane *lane,
914*6dc92ba4SVladimir Oltean 				    struct lynx_pll *pll,
915*6dc92ba4SVladimir Oltean 				    enum lynx_lane_mode mode)
916*6dc92ba4SVladimir Oltean {
917*6dc92ba4SVladimir Oltean 	enum lynx_10g_rat_sel nrate;
918*6dc92ba4SVladimir Oltean 
919*6dc92ba4SVladimir Oltean 	switch (pll->frate_sel) {
920*6dc92ba4SVladimir Oltean 	case PLLnCR0_FRATE_5G:
921*6dc92ba4SVladimir Oltean 		switch (mode) {
922*6dc92ba4SVladimir Oltean 		case LANE_MODE_1000BASEX_SGMII:
923*6dc92ba4SVladimir Oltean 			nrate = RAT_SEL_QUARTER;
924*6dc92ba4SVladimir Oltean 			break;
925*6dc92ba4SVladimir Oltean 		case LANE_MODE_QSGMII:
926*6dc92ba4SVladimir Oltean 			nrate = RAT_SEL_FULL;
927*6dc92ba4SVladimir Oltean 			break;
928*6dc92ba4SVladimir Oltean 		default:
929*6dc92ba4SVladimir Oltean 			return;
930*6dc92ba4SVladimir Oltean 		}
931*6dc92ba4SVladimir Oltean 		break;
932*6dc92ba4SVladimir Oltean 	case PLLnCR0_FRATE_3_125G:
933*6dc92ba4SVladimir Oltean 		switch (mode) {
934*6dc92ba4SVladimir Oltean 		case LANE_MODE_2500BASEX:
935*6dc92ba4SVladimir Oltean 			nrate = RAT_SEL_FULL;
936*6dc92ba4SVladimir Oltean 			break;
937*6dc92ba4SVladimir Oltean 		default:
938*6dc92ba4SVladimir Oltean 			return;
939*6dc92ba4SVladimir Oltean 		}
940*6dc92ba4SVladimir Oltean 		break;
941*6dc92ba4SVladimir Oltean 	case PLLnCR0_FRATE_5_15625G:
942*6dc92ba4SVladimir Oltean 		switch (mode) {
943*6dc92ba4SVladimir Oltean 		case LANE_MODE_10GBASER:
944*6dc92ba4SVladimir Oltean 		case LANE_MODE_USXGMII:
945*6dc92ba4SVladimir Oltean 		case LANE_MODE_10G_QXGMII:
946*6dc92ba4SVladimir Oltean 			nrate = RAT_SEL_DOUBLE;
947*6dc92ba4SVladimir Oltean 			break;
948*6dc92ba4SVladimir Oltean 		default:
949*6dc92ba4SVladimir Oltean 			return;
950*6dc92ba4SVladimir Oltean 		}
951*6dc92ba4SVladimir Oltean 		break;
952*6dc92ba4SVladimir Oltean 	default:
953*6dc92ba4SVladimir Oltean 		return;
954*6dc92ba4SVladimir Oltean 	}
955*6dc92ba4SVladimir Oltean 
956*6dc92ba4SVladimir Oltean 	lynx_lane_rmw(lane, LNaGCR0,
957*6dc92ba4SVladimir Oltean 		      FIELD_PREP(LNaGCR0_TRAT_SEL, nrate) |
958*6dc92ba4SVladimir Oltean 		      FIELD_PREP(LNaGCR0_RRAT_SEL, nrate),
959*6dc92ba4SVladimir Oltean 		      LNaGCR0_RRAT_SEL | LNaGCR0_TRAT_SEL);
960*6dc92ba4SVladimir Oltean }
961*6dc92ba4SVladimir Oltean 
962*6dc92ba4SVladimir Oltean static void lynx_10g_lane_set_pll(struct lynx_lane *lane,
963*6dc92ba4SVladimir Oltean 				  struct lynx_pll *pll)
964*6dc92ba4SVladimir Oltean {
965*6dc92ba4SVladimir Oltean 	if (pll->id == 0) {
966*6dc92ba4SVladimir Oltean 		lynx_lane_rmw(lane, LNaGCR0,
967*6dc92ba4SVladimir Oltean 			      LNaGCR0_RPLL_PLLF | LNaGCR0_TPLL_PLLF,
968*6dc92ba4SVladimir Oltean 			      LNaGCR0_RPLL_MSK | LNaGCR0_TPLL_MSK);
969*6dc92ba4SVladimir Oltean 	} else {
970*6dc92ba4SVladimir Oltean 		lynx_lane_rmw(lane, LNaGCR0,
971*6dc92ba4SVladimir Oltean 			      LNaGCR0_RPLL_PLLS | LNaGCR0_TPLL_PLLS,
972*6dc92ba4SVladimir Oltean 			      LNaGCR0_RPLL_MSK | LNaGCR0_TPLL_MSK);
973*6dc92ba4SVladimir Oltean 	}
974*6dc92ba4SVladimir Oltean }
975*6dc92ba4SVladimir Oltean 
976*6dc92ba4SVladimir Oltean static void lynx_10g_lane_remap_pll(struct lynx_lane *lane,
977*6dc92ba4SVladimir Oltean 				    enum lynx_lane_mode lane_mode)
978*6dc92ba4SVladimir Oltean {
979*6dc92ba4SVladimir Oltean 	struct lynx_priv *priv = lane->priv;
980*6dc92ba4SVladimir Oltean 	struct lynx_pll *pll;
981*6dc92ba4SVladimir Oltean 
982*6dc92ba4SVladimir Oltean 	/* Switch to the PLL that works with this interface type */
983*6dc92ba4SVladimir Oltean 	pll = lynx_pll_get(priv, lane_mode);
984*6dc92ba4SVladimir Oltean 	if (unlikely(!pll))
985*6dc92ba4SVladimir Oltean 		return;
986*6dc92ba4SVladimir Oltean 
987*6dc92ba4SVladimir Oltean 	lynx_10g_lane_set_pll(lane, pll);
988*6dc92ba4SVladimir Oltean 
989*6dc92ba4SVladimir Oltean 	/* Choose the portion of clock net to be used on this lane */
990*6dc92ba4SVladimir Oltean 	lynx_10g_lane_set_nrate(lane, pll, lane_mode);
991*6dc92ba4SVladimir Oltean }
992*6dc92ba4SVladimir Oltean 
993*6dc92ba4SVladimir Oltean static void lynx_10g_lane_change_proto_conf(struct lynx_lane *lane,
994*6dc92ba4SVladimir Oltean 					    enum lynx_lane_mode mode)
995*6dc92ba4SVladimir Oltean {
996*6dc92ba4SVladimir Oltean 	const struct lynx_10g_proto_conf *conf = &lynx_10g_proto_conf[mode];
997*6dc92ba4SVladimir Oltean 
998*6dc92ba4SVladimir Oltean 	lynx_lane_rmw(lane, LNaGCR0,
999*6dc92ba4SVladimir Oltean 		      FIELD_PREP(LNaGCR0_PROTS, conf->proto_sel) |
1000*6dc92ba4SVladimir Oltean 		      FIELD_PREP(LNaGCR0_IF20BIT_EN, conf->if20bit_en),
1001*6dc92ba4SVladimir Oltean 		      LNaGCR0_PROTS | LNaGCR0_IF20BIT_EN);
1002*6dc92ba4SVladimir Oltean 	lynx_lane_rmw(lane, LNaGCR1,
1003*6dc92ba4SVladimir Oltean 		      FIELD_PREP(LNaGCR1_REIDL_TH, conf->reidl_th) |
1004*6dc92ba4SVladimir Oltean 		      FIELD_PREP(LNaGCR1_REIDL_ET_MSB, conf->reidl_et_msb) |
1005*6dc92ba4SVladimir Oltean 		      FIELD_PREP(LNaGCR1_REIDL_ET_SEL, conf->reidl_et_sel) |
1006*6dc92ba4SVladimir Oltean 		      FIELD_PREP(LNaGCR1_REIDL_EX_MSB, conf->reidl_ex_msb) |
1007*6dc92ba4SVladimir Oltean 		      FIELD_PREP(LNaGCR1_REIDL_EX_SEL, conf->reidl_ex_sel) |
1008*6dc92ba4SVladimir Oltean 		      FIELD_PREP(LNaGCR1_ISLEW_RCTL, conf->islew_rctl) |
1009*6dc92ba4SVladimir Oltean 		      FIELD_PREP(LNaGCR1_OSLEW_RCTL, conf->oslew_rctl),
1010*6dc92ba4SVladimir Oltean 		      LNaGCR1_REIDL_TH |
1011*6dc92ba4SVladimir Oltean 		      LNaGCR1_REIDL_ET_MSB | LNaGCR1_REIDL_ET_SEL |
1012*6dc92ba4SVladimir Oltean 		      LNaGCR1_REIDL_EX_MSB | LNaGCR1_REIDL_EX_SEL |
1013*6dc92ba4SVladimir Oltean 		      LNaGCR1_ISLEW_RCTL | LNaGCR1_OSLEW_RCTL);
1014*6dc92ba4SVladimir Oltean 	lynx_lane_rmw(lane, LNaRECR0,
1015*6dc92ba4SVladimir Oltean 		      FIELD_PREP(LNaRECR0_RXEQ_BST, conf->rxeq_bst) |
1016*6dc92ba4SVladimir Oltean 		      FIELD_PREP(LNaRECR0_GK2OVD, conf->gk2ovd) |
1017*6dc92ba4SVladimir Oltean 		      FIELD_PREP(LNaRECR0_GK3OVD, conf->gk3ovd) |
1018*6dc92ba4SVladimir Oltean 		      FIELD_PREP(LNaRECR0_GK2OVD_EN, conf->gk2ovd_en) |
1019*6dc92ba4SVladimir Oltean 		      FIELD_PREP(LNaRECR0_GK3OVD_EN, conf->gk3ovd_en) |
1020*6dc92ba4SVladimir Oltean 		      FIELD_PREP(LNaRECR0_BASE_WAND, conf->base_wand),
1021*6dc92ba4SVladimir Oltean 		      LNaRECR0_RXEQ_BST | LNaRECR0_GK2OVD | LNaRECR0_GK3OVD |
1022*6dc92ba4SVladimir Oltean 		      LNaRECR0_GK2OVD_EN | LNaRECR0_GK3OVD_EN |
1023*6dc92ba4SVladimir Oltean 		      LNaRECR0_BASE_WAND);
1024*6dc92ba4SVladimir Oltean 	lynx_lane_rmw(lane, LNaTECR0,
1025*6dc92ba4SVladimir Oltean 		      FIELD_PREP(LNaTECR0_TEQ_TYPE, conf->teq_type) |
1026*6dc92ba4SVladimir Oltean 		      FIELD_PREP(LNaTECR0_SGN_PREQ, conf->sgn_preq) |
1027*6dc92ba4SVladimir Oltean 		      FIELD_PREP(LNaTECR0_RATIO_PREQ, conf->ratio_preq) |
1028*6dc92ba4SVladimir Oltean 		      FIELD_PREP(LNaTECR0_SGN_POST1Q, conf->sgn_post1q) |
1029*6dc92ba4SVladimir Oltean 		      FIELD_PREP(LNaTECR0_RATIO_PST1Q, conf->ratio_post1q) |
1030*6dc92ba4SVladimir Oltean 		      FIELD_PREP(LNaTECR0_ADPT_EQ, conf->adpt_eq) |
1031*6dc92ba4SVladimir Oltean 		      FIELD_PREP(LNaTECR0_AMP_RED, conf->amp_red),
1032*6dc92ba4SVladimir Oltean 		      LNaTECR0_TEQ_TYPE | LNaTECR0_SGN_PREQ |
1033*6dc92ba4SVladimir Oltean 		      LNaTECR0_RATIO_PREQ | LNaTECR0_SGN_POST1Q |
1034*6dc92ba4SVladimir Oltean 		      LNaTECR0_RATIO_PST1Q | LNaTECR0_ADPT_EQ |
1035*6dc92ba4SVladimir Oltean 		      LNaTECR0_AMP_RED);
1036*6dc92ba4SVladimir Oltean 	lynx_lane_write(lane, LNaTTLCR0, conf->ttlcr0);
1037*6dc92ba4SVladimir Oltean }
1038*6dc92ba4SVladimir Oltean 
1039*6dc92ba4SVladimir Oltean static int lynx_10g_lane_disable_pcvt(struct lynx_lane *lane,
1040*6dc92ba4SVladimir Oltean 				      enum lynx_lane_mode mode)
1041*6dc92ba4SVladimir Oltean {
1042*6dc92ba4SVladimir Oltean 	struct lynx_priv *priv = lane->priv;
1043*6dc92ba4SVladimir Oltean 	int err;
1044*6dc92ba4SVladimir Oltean 
1045*6dc92ba4SVladimir Oltean 	spin_lock(&priv->pcc_lock);
1046*6dc92ba4SVladimir Oltean 
1047*6dc92ba4SVladimir Oltean 	err = lynx_pccr_write(lane, mode, 0);
1048*6dc92ba4SVladimir Oltean 	if (err)
1049*6dc92ba4SVladimir Oltean 		goto out;
1050*6dc92ba4SVladimir Oltean 
1051*6dc92ba4SVladimir Oltean 	switch (mode) {
1052*6dc92ba4SVladimir Oltean 	case LANE_MODE_1000BASEX_SGMII:
1053*6dc92ba4SVladimir Oltean 	case LANE_MODE_2500BASEX:
1054*6dc92ba4SVladimir Oltean 		err = lynx_pcvt_rmw(lane, mode, CR(1), SGMIIaCR1_SGPCS_DIS,
1055*6dc92ba4SVladimir Oltean 				    SGMIIaCR1_SGPCS_EN);
1056*6dc92ba4SVladimir Oltean 		if (err)
1057*6dc92ba4SVladimir Oltean 			goto out;
1058*6dc92ba4SVladimir Oltean 
1059*6dc92ba4SVladimir Oltean 		lynx_pcvt_rmw(lane, mode, CR(0),
1060*6dc92ba4SVladimir Oltean 			      SGMIIaCR0_RST_SGM_ON | SGMIIaCR0_PD_SGM,
1061*6dc92ba4SVladimir Oltean 			      SGMIIaCR0_RST_SGM | SGMIIaCR0_PD_SGM);
1062*6dc92ba4SVladimir Oltean 		break;
1063*6dc92ba4SVladimir Oltean 	case LANE_MODE_QSGMII:
1064*6dc92ba4SVladimir Oltean 		err = lynx_pcvt_rmw(lane, mode, CR(0),
1065*6dc92ba4SVladimir Oltean 				    QSGMIIaCR0_RST_QSGM_ON | QSGMIIaCR0_PD_QSGM,
1066*6dc92ba4SVladimir Oltean 				    QSGMIIaCR0_RST_QSGM | QSGMIIaCR0_PD_QSGM);
1067*6dc92ba4SVladimir Oltean 		if (err)
1068*6dc92ba4SVladimir Oltean 			goto out;
1069*6dc92ba4SVladimir Oltean 		break;
1070*6dc92ba4SVladimir Oltean 	default:
1071*6dc92ba4SVladimir Oltean 		err = 0;
1072*6dc92ba4SVladimir Oltean 	}
1073*6dc92ba4SVladimir Oltean 
1074*6dc92ba4SVladimir Oltean out:
1075*6dc92ba4SVladimir Oltean 	spin_unlock(&priv->pcc_lock);
1076*6dc92ba4SVladimir Oltean 
1077*6dc92ba4SVladimir Oltean 	return err;
1078*6dc92ba4SVladimir Oltean }
1079*6dc92ba4SVladimir Oltean 
1080*6dc92ba4SVladimir Oltean static int lynx_10g_lane_enable_pcvt(struct lynx_lane *lane,
1081*6dc92ba4SVladimir Oltean 				     enum lynx_lane_mode mode)
1082*6dc92ba4SVladimir Oltean {
1083*6dc92ba4SVladimir Oltean 	struct lynx_priv *priv = lane->priv;
1084*6dc92ba4SVladimir Oltean 	u32 val;
1085*6dc92ba4SVladimir Oltean 	int err;
1086*6dc92ba4SVladimir Oltean 
1087*6dc92ba4SVladimir Oltean 	spin_lock(&priv->pcc_lock);
1088*6dc92ba4SVladimir Oltean 
1089*6dc92ba4SVladimir Oltean 	switch (mode) {
1090*6dc92ba4SVladimir Oltean 	case LANE_MODE_1000BASEX_SGMII:
1091*6dc92ba4SVladimir Oltean 	case LANE_MODE_2500BASEX:
1092*6dc92ba4SVladimir Oltean 		err = lynx_pcvt_rmw(lane, mode, CR(1), SGMIIaCR1_SGPCS_EN,
1093*6dc92ba4SVladimir Oltean 				    SGMIIaCR1_SGPCS_EN);
1094*6dc92ba4SVladimir Oltean 		if (err)
1095*6dc92ba4SVladimir Oltean 			goto out;
1096*6dc92ba4SVladimir Oltean 
1097*6dc92ba4SVladimir Oltean 		lynx_pcvt_rmw(lane, mode, CR(0), SGMIIaCR0_RST_SGM_OFF,
1098*6dc92ba4SVladimir Oltean 			      SGMIIaCR0_RST_SGM | SGMIIaCR0_PD_SGM);
1099*6dc92ba4SVladimir Oltean 		break;
1100*6dc92ba4SVladimir Oltean 	case LANE_MODE_QSGMII:
1101*6dc92ba4SVladimir Oltean 		err = lynx_pcvt_rmw(lane, mode, CR(0), QSGMIIaCR0_RST_QSGM_OFF,
1102*6dc92ba4SVladimir Oltean 				    QSGMIIaCR0_RST_QSGM | QSGMIIaCR0_PD_QSGM);
1103*6dc92ba4SVladimir Oltean 		if (err)
1104*6dc92ba4SVladimir Oltean 			goto out;
1105*6dc92ba4SVladimir Oltean 		break;
1106*6dc92ba4SVladimir Oltean 	default:
1107*6dc92ba4SVladimir Oltean 		err = 0;
1108*6dc92ba4SVladimir Oltean 	}
1109*6dc92ba4SVladimir Oltean 
1110*6dc92ba4SVladimir Oltean 	/* If the PCS was enabled at boot time, use the backed up PCCR value to
1111*6dc92ba4SVladimir Oltean 	 * re-enable it here, to preserve the muxing.
1112*6dc92ba4SVladimir Oltean 	 */
1113*6dc92ba4SVladimir Oltean 	if (lynx_10g_pccr_val_enabled(lane->default_pccr[mode])) {
1114*6dc92ba4SVladimir Oltean 		err = lynx_pccr_write(lane, mode, lane->default_pccr[mode]);
1115*6dc92ba4SVladimir Oltean 		goto out;
1116*6dc92ba4SVladimir Oltean 	}
1117*6dc92ba4SVladimir Oltean 
1118*6dc92ba4SVladimir Oltean 	/* If the PCS was not enabled, set the PCCR to a default value which
1119*6dc92ba4SVladimir Oltean 	 * enables it (1). The assumption is that this is the only PCS <->
1120*6dc92ba4SVladimir Oltean 	 * SerDes lane muxing value possible.
1121*6dc92ba4SVladimir Oltean 	 *
1122*6dc92ba4SVladimir Oltean 	 * This is mostly useful for SGMII <-> 10GBase-R major protocol
1123*6dc92ba4SVladimir Oltean 	 * reconfiguration, where at boot time, either the SGMII or the
1124*6dc92ba4SVladimir Oltean 	 * 10GBase-R PCS is enabled for the lane, but not both.
1125*6dc92ba4SVladimir Oltean 	 *
1126*6dc92ba4SVladimir Oltean 	 * In fact, if there are multiple lane muxing options, this function
1127*6dc92ba4SVladimir Oltean 	 * will most likely not choose the right one. For correct functionality
1128*6dc92ba4SVladimir Oltean 	 * there, we assume that the PCS we are enabling here was found enabled
1129*6dc92ba4SVladimir Oltean 	 * at boot time (reset default, or through PBL, or...), and we preserve
1130*6dc92ba4SVladimir Oltean 	 * its muxing through the default_pccr branch above.
1131*6dc92ba4SVladimir Oltean 	 */
1132*6dc92ba4SVladimir Oltean 	val = 0;
1133*6dc92ba4SVladimir Oltean 
1134*6dc92ba4SVladimir Oltean 	switch (mode) {
1135*6dc92ba4SVladimir Oltean 	case LANE_MODE_1000BASEX_SGMII:
1136*6dc92ba4SVladimir Oltean 	case LANE_MODE_2500BASEX:
1137*6dc92ba4SVladimir Oltean 		val |= FIELD_PREP(PCCR8_SGMIIa_CFG, 1);
1138*6dc92ba4SVladimir Oltean 		break;
1139*6dc92ba4SVladimir Oltean 	case LANE_MODE_QSGMII:
1140*6dc92ba4SVladimir Oltean 		val |= FIELD_PREP(PCCR9_QSGMIIa_CFG, 1);
1141*6dc92ba4SVladimir Oltean 		break;
1142*6dc92ba4SVladimir Oltean 	case LANE_MODE_10G_QXGMII:
1143*6dc92ba4SVladimir Oltean 		val |= FIELD_PREP(PCCR9_QXGMIIa_CFG, 1);
1144*6dc92ba4SVladimir Oltean 		break;
1145*6dc92ba4SVladimir Oltean 	case LANE_MODE_10GBASER:
1146*6dc92ba4SVladimir Oltean 		val |= FIELD_PREP(PCCRB_XFIa_CFG, 1);
1147*6dc92ba4SVladimir Oltean 		break;
1148*6dc92ba4SVladimir Oltean 	case LANE_MODE_USXGMII:
1149*6dc92ba4SVladimir Oltean 		val |= FIELD_PREP(PCCRB_SXGMIIa_CFG, 1);
1150*6dc92ba4SVladimir Oltean 		break;
1151*6dc92ba4SVladimir Oltean 	default:
1152*6dc92ba4SVladimir Oltean 		err = 0;
1153*6dc92ba4SVladimir Oltean 		goto out;
1154*6dc92ba4SVladimir Oltean 	}
1155*6dc92ba4SVladimir Oltean 
1156*6dc92ba4SVladimir Oltean 	err = lynx_pccr_write(lane, mode, val);
1157*6dc92ba4SVladimir Oltean out:
1158*6dc92ba4SVladimir Oltean 	spin_unlock(&priv->pcc_lock);
1159*6dc92ba4SVladimir Oltean 
1160*6dc92ba4SVladimir Oltean 	return err;
1161*6dc92ba4SVladimir Oltean }
1162*6dc92ba4SVladimir Oltean 
1163*6dc92ba4SVladimir Oltean static bool lynx_10g_lane_mode_needs_rcw_override(struct lynx_lane *lane,
1164*6dc92ba4SVladimir Oltean 						  enum lynx_lane_mode new)
1165*6dc92ba4SVladimir Oltean {
1166*6dc92ba4SVladimir Oltean 	enum lynx_lane_mode curr = lane->mode;
1167*6dc92ba4SVladimir Oltean 
1168*6dc92ba4SVladimir Oltean 	/* Major protocol changes, which involve changing the PCS connection to
1169*6dc92ba4SVladimir Oltean 	 * the GMII MAC with the one to the XGMII MAC, require an RCW override
1170*6dc92ba4SVladimir Oltean 	 * procedure to reconfigure an internal mux, as documented here:
1171*6dc92ba4SVladimir Oltean 	 * https://lore.kernel.org/linux-phy/20230810102631.bvozjer3t67r67iy@skbuf/
1172*6dc92ba4SVladimir Oltean 	 * This is SoC-specific, and not yet implemented in drivers/soc/fsl/guts.c.
1173*6dc92ba4SVladimir Oltean 	 *
1174*6dc92ba4SVladimir Oltean 	 * So the supported set of protocols depends on the initial lane mode.
1175*6dc92ba4SVladimir Oltean 	 *
1176*6dc92ba4SVladimir Oltean 	 * Minor protocol changes (SGMII <-> 1000Base-X <-> 2500Base-X or
1177*6dc92ba4SVladimir Oltean 	 * 10GBase-R <-> USXGMII) are supported.
1178*6dc92ba4SVladimir Oltean 	 */
1179*6dc92ba4SVladimir Oltean 	if ((lynx_lane_mode_uses_gmii_mac(curr) &&
1180*6dc92ba4SVladimir Oltean 	     lynx_lane_mode_uses_xgmii_mac(new)) ||
1181*6dc92ba4SVladimir Oltean 	    (lynx_lane_mode_uses_xgmii_mac(curr) &&
1182*6dc92ba4SVladimir Oltean 	     lynx_lane_mode_uses_gmii_mac(new)))
1183*6dc92ba4SVladimir Oltean 		return true;
1184*6dc92ba4SVladimir Oltean 
1185*6dc92ba4SVladimir Oltean 	return false;
1186*6dc92ba4SVladimir Oltean }
1187*6dc92ba4SVladimir Oltean 
1188*6dc92ba4SVladimir Oltean static int lynx_10g_validate(struct phy *phy, enum phy_mode mode, int submode,
1189*6dc92ba4SVladimir Oltean 			     union phy_configure_opts *opts)
1190*6dc92ba4SVladimir Oltean {
1191*6dc92ba4SVladimir Oltean 	struct lynx_lane *lane = phy_get_drvdata(phy);
1192*6dc92ba4SVladimir Oltean 	enum lynx_lane_mode lane_mode;
1193*6dc92ba4SVladimir Oltean 	int err;
1194*6dc92ba4SVladimir Oltean 
1195*6dc92ba4SVladimir Oltean 	err = lynx_phy_mode_to_lane_mode(phy, mode, submode, &lane_mode);
1196*6dc92ba4SVladimir Oltean 	if (err)
1197*6dc92ba4SVladimir Oltean 		return err;
1198*6dc92ba4SVladimir Oltean 
1199*6dc92ba4SVladimir Oltean 	if (lynx_10g_lane_mode_needs_rcw_override(lane, lane_mode))
1200*6dc92ba4SVladimir Oltean 		return -EINVAL;
1201*6dc92ba4SVladimir Oltean 
1202*6dc92ba4SVladimir Oltean 	return 0;
1203*6dc92ba4SVladimir Oltean }
1204*6dc92ba4SVladimir Oltean 
1205*6dc92ba4SVladimir Oltean static int lynx_10g_set_mode(struct phy *phy, enum phy_mode mode, int submode)
1206*6dc92ba4SVladimir Oltean {
1207*6dc92ba4SVladimir Oltean 	struct lynx_lane *lane = phy_get_drvdata(phy);
1208*6dc92ba4SVladimir Oltean 	bool powered_up = lane->powered_up;
1209*6dc92ba4SVladimir Oltean 	enum lynx_lane_mode lane_mode;
1210*6dc92ba4SVladimir Oltean 	int err;
1211*6dc92ba4SVladimir Oltean 
1212*6dc92ba4SVladimir Oltean 	err = lynx_10g_validate(phy, mode, submode, NULL);
1213*6dc92ba4SVladimir Oltean 	if (err)
1214*6dc92ba4SVladimir Oltean 		return err;
1215*6dc92ba4SVladimir Oltean 
1216*6dc92ba4SVladimir Oltean 	lane_mode = phy_interface_to_lane_mode(submode);
1217*6dc92ba4SVladimir Oltean 	/* lynx_10g_validate() already made sure the lane_mode is supported */
1218*6dc92ba4SVladimir Oltean 
1219*6dc92ba4SVladimir Oltean 	if (lane_mode == lane->mode)
1220*6dc92ba4SVladimir Oltean 		return 0;
1221*6dc92ba4SVladimir Oltean 
1222*6dc92ba4SVladimir Oltean 	/* If the lane is powered up, put the lane into the halt state while
1223*6dc92ba4SVladimir Oltean 	 * the reconfiguration is being done.
1224*6dc92ba4SVladimir Oltean 	 */
1225*6dc92ba4SVladimir Oltean 	if (powered_up)
1226*6dc92ba4SVladimir Oltean 		lynx_10g_lane_halt(phy);
1227*6dc92ba4SVladimir Oltean 
1228*6dc92ba4SVladimir Oltean 	err = lynx_10g_lane_disable_pcvt(lane, lane->mode);
1229*6dc92ba4SVladimir Oltean 	if (err)
1230*6dc92ba4SVladimir Oltean 		goto out;
1231*6dc92ba4SVladimir Oltean 
1232*6dc92ba4SVladimir Oltean 	lynx_10g_lane_change_proto_conf(lane, lane_mode);
1233*6dc92ba4SVladimir Oltean 	lynx_10g_lane_remap_pll(lane, lane_mode);
1234*6dc92ba4SVladimir Oltean 	WARN_ON(lynx_10g_lane_enable_pcvt(lane, lane_mode));
1235*6dc92ba4SVladimir Oltean 
1236*6dc92ba4SVladimir Oltean 	lane->mode = lane_mode;
1237*6dc92ba4SVladimir Oltean 
1238*6dc92ba4SVladimir Oltean out:
1239*6dc92ba4SVladimir Oltean 	if (powered_up) {
1240*6dc92ba4SVladimir Oltean 		/* The RM says to wait for at least 120 ns */
1241*6dc92ba4SVladimir Oltean 		usleep_range(1, 2);
1242*6dc92ba4SVladimir Oltean 		lynx_10g_lane_reset(phy);
1243*6dc92ba4SVladimir Oltean 	}
1244*6dc92ba4SVladimir Oltean 
1245*6dc92ba4SVladimir Oltean 	return err;
1246*6dc92ba4SVladimir Oltean }
1247*6dc92ba4SVladimir Oltean 
1248*6dc92ba4SVladimir Oltean static int lynx_10g_init(struct phy *phy)
1249*6dc92ba4SVladimir Oltean {
1250*6dc92ba4SVladimir Oltean 	struct lynx_lane *lane = phy_get_drvdata(phy);
1251*6dc92ba4SVladimir Oltean 
1252*6dc92ba4SVladimir Oltean 	/* Mark the fact that the lane was init */
1253*6dc92ba4SVladimir Oltean 	lane->init = true;
1254*6dc92ba4SVladimir Oltean 
1255*6dc92ba4SVladimir Oltean 	/* SerDes lanes are powered on at boot time. Any lane that is
1256*6dc92ba4SVladimir Oltean 	 * managed by this driver will get powered off when its consumer
1257*6dc92ba4SVladimir Oltean 	 * calls phy_init().
1258*6dc92ba4SVladimir Oltean 	 */
1259*6dc92ba4SVladimir Oltean 	lane->powered_up = true;
1260*6dc92ba4SVladimir Oltean 	lynx_10g_power_off(phy);
1261*6dc92ba4SVladimir Oltean 
1262*6dc92ba4SVladimir Oltean 	return 0;
1263*6dc92ba4SVladimir Oltean }
1264*6dc92ba4SVladimir Oltean 
1265*6dc92ba4SVladimir Oltean static int lynx_10g_exit(struct phy *phy)
1266*6dc92ba4SVladimir Oltean {
1267*6dc92ba4SVladimir Oltean 	struct lynx_lane *lane = phy_get_drvdata(phy);
1268*6dc92ba4SVladimir Oltean 
1269*6dc92ba4SVladimir Oltean 	/* The lane returns to the state where it isn't managed by the
1270*6dc92ba4SVladimir Oltean 	 * consumer, so we must treat is as if it isn't initialized, and always
1271*6dc92ba4SVladimir Oltean 	 * powered on.
1272*6dc92ba4SVladimir Oltean 	 */
1273*6dc92ba4SVladimir Oltean 	lane->init = false;
1274*6dc92ba4SVladimir Oltean 	lane->powered_up = false;
1275*6dc92ba4SVladimir Oltean 	lynx_10g_power_on(phy);
1276*6dc92ba4SVladimir Oltean 
1277*6dc92ba4SVladimir Oltean 	return 0;
1278*6dc92ba4SVladimir Oltean }
1279*6dc92ba4SVladimir Oltean 
1280*6dc92ba4SVladimir Oltean static const struct phy_ops lynx_10g_ops = {
1281*6dc92ba4SVladimir Oltean 	.init		= lynx_10g_init,
1282*6dc92ba4SVladimir Oltean 	.exit		= lynx_10g_exit,
1283*6dc92ba4SVladimir Oltean 	.power_on	= lynx_10g_power_on,
1284*6dc92ba4SVladimir Oltean 	.power_off	= lynx_10g_power_off,
1285*6dc92ba4SVladimir Oltean 	.set_mode	= lynx_10g_set_mode,
1286*6dc92ba4SVladimir Oltean 	.validate	= lynx_10g_validate,
1287*6dc92ba4SVladimir Oltean 	.owner		= THIS_MODULE,
1288*6dc92ba4SVladimir Oltean };
1289*6dc92ba4SVladimir Oltean 
1290*6dc92ba4SVladimir Oltean static int lynx_10g_probe(struct platform_device *pdev)
1291*6dc92ba4SVladimir Oltean {
1292*6dc92ba4SVladimir Oltean 	return lynx_probe(pdev, of_device_get_match_data(&pdev->dev),
1293*6dc92ba4SVladimir Oltean 			  &lynx_10g_ops);
1294*6dc92ba4SVladimir Oltean }
1295*6dc92ba4SVladimir Oltean 
1296*6dc92ba4SVladimir Oltean static const struct of_device_id lynx_10g_of_match_table[] = {
1297*6dc92ba4SVladimir Oltean 	{ .compatible = "fsl,ls1028a-serdes", .data = &lynx_info_ls1028a },
1298*6dc92ba4SVladimir Oltean 	{ .compatible = "fsl,ls1046a-serdes1", .data = &lynx_info_ls1046a_serdes1 },
1299*6dc92ba4SVladimir Oltean 	{ .compatible = "fsl,ls1046a-serdes2", .data = &lynx_info_ls1046a_serdes2 },
1300*6dc92ba4SVladimir Oltean 	{ .compatible = "fsl,ls1088a-serdes1", .data = &lynx_info_ls1088a_serdes1 },
1301*6dc92ba4SVladimir Oltean 	{ .compatible = "fsl,ls2088a-serdes1", .data = &lynx_info_ls2088a_serdes1 },
1302*6dc92ba4SVladimir Oltean 	{ .compatible = "fsl,ls2088a-serdes2", .data = &lynx_info_ls2088a_serdes2 },
1303*6dc92ba4SVladimir Oltean 	{}
1304*6dc92ba4SVladimir Oltean };
1305*6dc92ba4SVladimir Oltean MODULE_DEVICE_TABLE(of, lynx_10g_of_match_table);
1306*6dc92ba4SVladimir Oltean 
1307*6dc92ba4SVladimir Oltean static struct platform_driver lynx_10g_driver = {
1308*6dc92ba4SVladimir Oltean 	.probe	= lynx_10g_probe,
1309*6dc92ba4SVladimir Oltean 	.remove	= lynx_remove,
1310*6dc92ba4SVladimir Oltean 	.driver	= {
1311*6dc92ba4SVladimir Oltean 		.name = "lynx-10g",
1312*6dc92ba4SVladimir Oltean 		.of_match_table = lynx_10g_of_match_table,
1313*6dc92ba4SVladimir Oltean 	},
1314*6dc92ba4SVladimir Oltean };
1315*6dc92ba4SVladimir Oltean module_platform_driver(lynx_10g_driver);
1316*6dc92ba4SVladimir Oltean 
1317*6dc92ba4SVladimir Oltean MODULE_IMPORT_NS("PHY_FSL_LYNX");
1318*6dc92ba4SVladimir Oltean MODULE_AUTHOR("Ioana Ciornei <ioana.ciornei@nxp.com>");
1319*6dc92ba4SVladimir Oltean MODULE_AUTHOR("Vladimir Oltean <vladimir.oltean@nxp.com>");
1320*6dc92ba4SVladimir Oltean MODULE_DESCRIPTION("Lynx 10G SerDes PHY driver for Layerscape SoCs");
1321*6dc92ba4SVladimir Oltean MODULE_LICENSE("GPL");
1322