xref: /OK3568_Linux_fs/kernel/drivers/net/phy/vitesse.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0+
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * Driver for Vitesse PHYs
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * Author: Kriston Carson
6*4882a593Smuzhiyun  */
7*4882a593Smuzhiyun 
8*4882a593Smuzhiyun #include <linux/kernel.h>
9*4882a593Smuzhiyun #include <linux/module.h>
10*4882a593Smuzhiyun #include <linux/mii.h>
11*4882a593Smuzhiyun #include <linux/ethtool.h>
12*4882a593Smuzhiyun #include <linux/phy.h>
13*4882a593Smuzhiyun 
14*4882a593Smuzhiyun /* Vitesse Extended Page Magic Register(s) */
15*4882a593Smuzhiyun #define MII_VSC82X4_EXT_PAGE_16E	0x10
16*4882a593Smuzhiyun #define MII_VSC82X4_EXT_PAGE_17E	0x11
17*4882a593Smuzhiyun #define MII_VSC82X4_EXT_PAGE_18E	0x12
18*4882a593Smuzhiyun 
19*4882a593Smuzhiyun /* Vitesse Extended Control Register 1 */
20*4882a593Smuzhiyun #define MII_VSC8244_EXT_CON1           0x17
21*4882a593Smuzhiyun #define MII_VSC8244_EXTCON1_INIT       0x0000
22*4882a593Smuzhiyun #define MII_VSC8244_EXTCON1_TX_SKEW_MASK	0x0c00
23*4882a593Smuzhiyun #define MII_VSC8244_EXTCON1_RX_SKEW_MASK	0x0300
24*4882a593Smuzhiyun #define MII_VSC8244_EXTCON1_TX_SKEW	0x0800
25*4882a593Smuzhiyun #define MII_VSC8244_EXTCON1_RX_SKEW	0x0200
26*4882a593Smuzhiyun 
27*4882a593Smuzhiyun /* Vitesse Interrupt Mask Register */
28*4882a593Smuzhiyun #define MII_VSC8244_IMASK		0x19
29*4882a593Smuzhiyun #define MII_VSC8244_IMASK_IEN		0x8000
30*4882a593Smuzhiyun #define MII_VSC8244_IMASK_SPEED		0x4000
31*4882a593Smuzhiyun #define MII_VSC8244_IMASK_LINK		0x2000
32*4882a593Smuzhiyun #define MII_VSC8244_IMASK_DUPLEX	0x1000
33*4882a593Smuzhiyun #define MII_VSC8244_IMASK_MASK		0xf000
34*4882a593Smuzhiyun 
35*4882a593Smuzhiyun #define MII_VSC8221_IMASK_MASK		0xa000
36*4882a593Smuzhiyun 
37*4882a593Smuzhiyun /* Vitesse Interrupt Status Register */
38*4882a593Smuzhiyun #define MII_VSC8244_ISTAT		0x1a
39*4882a593Smuzhiyun #define MII_VSC8244_ISTAT_STATUS	0x8000
40*4882a593Smuzhiyun #define MII_VSC8244_ISTAT_SPEED		0x4000
41*4882a593Smuzhiyun #define MII_VSC8244_ISTAT_LINK		0x2000
42*4882a593Smuzhiyun #define MII_VSC8244_ISTAT_DUPLEX	0x1000
43*4882a593Smuzhiyun 
44*4882a593Smuzhiyun /* Vitesse Auxiliary Control/Status Register */
45*4882a593Smuzhiyun #define MII_VSC8244_AUX_CONSTAT		0x1c
46*4882a593Smuzhiyun #define MII_VSC8244_AUXCONSTAT_INIT	0x0000
47*4882a593Smuzhiyun #define MII_VSC8244_AUXCONSTAT_DUPLEX	0x0020
48*4882a593Smuzhiyun #define MII_VSC8244_AUXCONSTAT_SPEED	0x0018
49*4882a593Smuzhiyun #define MII_VSC8244_AUXCONSTAT_GBIT	0x0010
50*4882a593Smuzhiyun #define MII_VSC8244_AUXCONSTAT_100	0x0008
51*4882a593Smuzhiyun 
52*4882a593Smuzhiyun #define MII_VSC8221_AUXCONSTAT_INIT	0x0004 /* need to set this bit? */
53*4882a593Smuzhiyun #define MII_VSC8221_AUXCONSTAT_RESERVED	0x0004
54*4882a593Smuzhiyun 
55*4882a593Smuzhiyun /* Vitesse Extended Page Access Register */
56*4882a593Smuzhiyun #define MII_VSC82X4_EXT_PAGE_ACCESS	0x1f
57*4882a593Smuzhiyun 
58*4882a593Smuzhiyun /* Vitesse VSC8601 Extended PHY Control Register 1 */
59*4882a593Smuzhiyun #define MII_VSC8601_EPHY_CTL		0x17
60*4882a593Smuzhiyun #define MII_VSC8601_EPHY_CTL_RGMII_SKEW	(1 << 8)
61*4882a593Smuzhiyun 
62*4882a593Smuzhiyun #define PHY_ID_VSC8234			0x000fc620
63*4882a593Smuzhiyun #define PHY_ID_VSC8244			0x000fc6c0
64*4882a593Smuzhiyun #define PHY_ID_VSC8572			0x000704d0
65*4882a593Smuzhiyun #define PHY_ID_VSC8601			0x00070420
66*4882a593Smuzhiyun #define PHY_ID_VSC7385			0x00070450
67*4882a593Smuzhiyun #define PHY_ID_VSC7388			0x00070480
68*4882a593Smuzhiyun #define PHY_ID_VSC7395			0x00070550
69*4882a593Smuzhiyun #define PHY_ID_VSC7398			0x00070580
70*4882a593Smuzhiyun #define PHY_ID_VSC8662			0x00070660
71*4882a593Smuzhiyun #define PHY_ID_VSC8221			0x000fc550
72*4882a593Smuzhiyun #define PHY_ID_VSC8211			0x000fc4b0
73*4882a593Smuzhiyun 
74*4882a593Smuzhiyun MODULE_DESCRIPTION("Vitesse PHY driver");
75*4882a593Smuzhiyun MODULE_AUTHOR("Kriston Carson");
76*4882a593Smuzhiyun MODULE_LICENSE("GPL");
77*4882a593Smuzhiyun 
vsc824x_add_skew(struct phy_device * phydev)78*4882a593Smuzhiyun static int vsc824x_add_skew(struct phy_device *phydev)
79*4882a593Smuzhiyun {
80*4882a593Smuzhiyun 	int err;
81*4882a593Smuzhiyun 	int extcon;
82*4882a593Smuzhiyun 
83*4882a593Smuzhiyun 	extcon = phy_read(phydev, MII_VSC8244_EXT_CON1);
84*4882a593Smuzhiyun 
85*4882a593Smuzhiyun 	if (extcon < 0)
86*4882a593Smuzhiyun 		return extcon;
87*4882a593Smuzhiyun 
88*4882a593Smuzhiyun 	extcon &= ~(MII_VSC8244_EXTCON1_TX_SKEW_MASK |
89*4882a593Smuzhiyun 			MII_VSC8244_EXTCON1_RX_SKEW_MASK);
90*4882a593Smuzhiyun 
91*4882a593Smuzhiyun 	extcon |= (MII_VSC8244_EXTCON1_TX_SKEW |
92*4882a593Smuzhiyun 			MII_VSC8244_EXTCON1_RX_SKEW);
93*4882a593Smuzhiyun 
94*4882a593Smuzhiyun 	err = phy_write(phydev, MII_VSC8244_EXT_CON1, extcon);
95*4882a593Smuzhiyun 
96*4882a593Smuzhiyun 	return err;
97*4882a593Smuzhiyun }
98*4882a593Smuzhiyun 
vsc824x_config_init(struct phy_device * phydev)99*4882a593Smuzhiyun static int vsc824x_config_init(struct phy_device *phydev)
100*4882a593Smuzhiyun {
101*4882a593Smuzhiyun 	int err;
102*4882a593Smuzhiyun 
103*4882a593Smuzhiyun 	err = phy_write(phydev, MII_VSC8244_AUX_CONSTAT,
104*4882a593Smuzhiyun 			MII_VSC8244_AUXCONSTAT_INIT);
105*4882a593Smuzhiyun 	if (err < 0)
106*4882a593Smuzhiyun 		return err;
107*4882a593Smuzhiyun 
108*4882a593Smuzhiyun 	if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID)
109*4882a593Smuzhiyun 		err = vsc824x_add_skew(phydev);
110*4882a593Smuzhiyun 
111*4882a593Smuzhiyun 	return err;
112*4882a593Smuzhiyun }
113*4882a593Smuzhiyun 
114*4882a593Smuzhiyun #define VSC73XX_EXT_PAGE_ACCESS 0x1f
115*4882a593Smuzhiyun 
vsc73xx_read_page(struct phy_device * phydev)116*4882a593Smuzhiyun static int vsc73xx_read_page(struct phy_device *phydev)
117*4882a593Smuzhiyun {
118*4882a593Smuzhiyun 	return __phy_read(phydev, VSC73XX_EXT_PAGE_ACCESS);
119*4882a593Smuzhiyun }
120*4882a593Smuzhiyun 
vsc73xx_write_page(struct phy_device * phydev,int page)121*4882a593Smuzhiyun static int vsc73xx_write_page(struct phy_device *phydev, int page)
122*4882a593Smuzhiyun {
123*4882a593Smuzhiyun 	return __phy_write(phydev, VSC73XX_EXT_PAGE_ACCESS, page);
124*4882a593Smuzhiyun }
125*4882a593Smuzhiyun 
vsc73xx_config_init(struct phy_device * phydev)126*4882a593Smuzhiyun static void vsc73xx_config_init(struct phy_device *phydev)
127*4882a593Smuzhiyun {
128*4882a593Smuzhiyun 	/* Receiver init */
129*4882a593Smuzhiyun 	phy_write(phydev, 0x1f, 0x2a30);
130*4882a593Smuzhiyun 	phy_modify(phydev, 0x0c, 0x0300, 0x0200);
131*4882a593Smuzhiyun 	phy_write(phydev, 0x1f, 0x0000);
132*4882a593Smuzhiyun 
133*4882a593Smuzhiyun 	/* Config LEDs 0x61 */
134*4882a593Smuzhiyun 	phy_modify(phydev, MII_TPISTATUS, 0xff00, 0x0061);
135*4882a593Smuzhiyun }
136*4882a593Smuzhiyun 
vsc738x_config_init(struct phy_device * phydev)137*4882a593Smuzhiyun static int vsc738x_config_init(struct phy_device *phydev)
138*4882a593Smuzhiyun {
139*4882a593Smuzhiyun 	u16 rev;
140*4882a593Smuzhiyun 	/* This magic sequence appear in the application note
141*4882a593Smuzhiyun 	 * "VSC7385/7388 PHY Configuration".
142*4882a593Smuzhiyun 	 *
143*4882a593Smuzhiyun 	 * Maybe one day we will get to know what it all means.
144*4882a593Smuzhiyun 	 */
145*4882a593Smuzhiyun 	phy_write(phydev, 0x1f, 0x2a30);
146*4882a593Smuzhiyun 	phy_modify(phydev, 0x08, 0x0200, 0x0200);
147*4882a593Smuzhiyun 	phy_write(phydev, 0x1f, 0x52b5);
148*4882a593Smuzhiyun 	phy_write(phydev, 0x10, 0xb68a);
149*4882a593Smuzhiyun 	phy_modify(phydev, 0x12, 0xff07, 0x0003);
150*4882a593Smuzhiyun 	phy_modify(phydev, 0x11, 0x00ff, 0x00a2);
151*4882a593Smuzhiyun 	phy_write(phydev, 0x10, 0x968a);
152*4882a593Smuzhiyun 	phy_write(phydev, 0x1f, 0x2a30);
153*4882a593Smuzhiyun 	phy_modify(phydev, 0x08, 0x0200, 0x0000);
154*4882a593Smuzhiyun 	phy_write(phydev, 0x1f, 0x0000);
155*4882a593Smuzhiyun 
156*4882a593Smuzhiyun 	/* Read revision */
157*4882a593Smuzhiyun 	rev = phy_read(phydev, MII_PHYSID2);
158*4882a593Smuzhiyun 	rev &= 0x0f;
159*4882a593Smuzhiyun 
160*4882a593Smuzhiyun 	/* Special quirk for revision 0 */
161*4882a593Smuzhiyun 	if (rev == 0) {
162*4882a593Smuzhiyun 		phy_write(phydev, 0x1f, 0x2a30);
163*4882a593Smuzhiyun 		phy_modify(phydev, 0x08, 0x0200, 0x0200);
164*4882a593Smuzhiyun 		phy_write(phydev, 0x1f, 0x52b5);
165*4882a593Smuzhiyun 		phy_write(phydev, 0x12, 0x0000);
166*4882a593Smuzhiyun 		phy_write(phydev, 0x11, 0x0689);
167*4882a593Smuzhiyun 		phy_write(phydev, 0x10, 0x8f92);
168*4882a593Smuzhiyun 		phy_write(phydev, 0x1f, 0x52b5);
169*4882a593Smuzhiyun 		phy_write(phydev, 0x12, 0x0000);
170*4882a593Smuzhiyun 		phy_write(phydev, 0x11, 0x0e35);
171*4882a593Smuzhiyun 		phy_write(phydev, 0x10, 0x9786);
172*4882a593Smuzhiyun 		phy_write(phydev, 0x1f, 0x2a30);
173*4882a593Smuzhiyun 		phy_modify(phydev, 0x08, 0x0200, 0x0000);
174*4882a593Smuzhiyun 		phy_write(phydev, 0x17, 0xff80);
175*4882a593Smuzhiyun 		phy_write(phydev, 0x17, 0x0000);
176*4882a593Smuzhiyun 	}
177*4882a593Smuzhiyun 
178*4882a593Smuzhiyun 	phy_write(phydev, 0x1f, 0x0000);
179*4882a593Smuzhiyun 	phy_write(phydev, 0x12, 0x0048);
180*4882a593Smuzhiyun 
181*4882a593Smuzhiyun 	if (rev == 0) {
182*4882a593Smuzhiyun 		phy_write(phydev, 0x1f, 0x2a30);
183*4882a593Smuzhiyun 		phy_write(phydev, 0x14, 0x6600);
184*4882a593Smuzhiyun 		phy_write(phydev, 0x1f, 0x0000);
185*4882a593Smuzhiyun 		phy_write(phydev, 0x18, 0xa24e);
186*4882a593Smuzhiyun 	} else {
187*4882a593Smuzhiyun 		phy_write(phydev, 0x1f, 0x2a30);
188*4882a593Smuzhiyun 		phy_modify(phydev, 0x16, 0x0fc0, 0x0240);
189*4882a593Smuzhiyun 		phy_modify(phydev, 0x14, 0x6000, 0x4000);
190*4882a593Smuzhiyun 		/* bits 14-15 in extended register 0x14 controls DACG amplitude
191*4882a593Smuzhiyun 		 * 6 = -8%, 2 is hardware default
192*4882a593Smuzhiyun 		 */
193*4882a593Smuzhiyun 		phy_write(phydev, 0x1f, 0x0001);
194*4882a593Smuzhiyun 		phy_modify(phydev, 0x14, 0xe000, 0x6000);
195*4882a593Smuzhiyun 		phy_write(phydev, 0x1f, 0x0000);
196*4882a593Smuzhiyun 	}
197*4882a593Smuzhiyun 
198*4882a593Smuzhiyun 	vsc73xx_config_init(phydev);
199*4882a593Smuzhiyun 
200*4882a593Smuzhiyun 	return 0;
201*4882a593Smuzhiyun }
202*4882a593Smuzhiyun 
vsc739x_config_init(struct phy_device * phydev)203*4882a593Smuzhiyun static int vsc739x_config_init(struct phy_device *phydev)
204*4882a593Smuzhiyun {
205*4882a593Smuzhiyun 	/* This magic sequence appears in the VSC7395 SparX-G5e application
206*4882a593Smuzhiyun 	 * note "VSC7395/VSC7398 PHY Configuration"
207*4882a593Smuzhiyun 	 *
208*4882a593Smuzhiyun 	 * Maybe one day we will get to know what it all means.
209*4882a593Smuzhiyun 	 */
210*4882a593Smuzhiyun 	phy_write(phydev, 0x1f, 0x2a30);
211*4882a593Smuzhiyun 	phy_modify(phydev, 0x08, 0x0200, 0x0200);
212*4882a593Smuzhiyun 	phy_write(phydev, 0x1f, 0x52b5);
213*4882a593Smuzhiyun 	phy_write(phydev, 0x10, 0xb68a);
214*4882a593Smuzhiyun 	phy_modify(phydev, 0x12, 0xff07, 0x0003);
215*4882a593Smuzhiyun 	phy_modify(phydev, 0x11, 0x00ff, 0x00a2);
216*4882a593Smuzhiyun 	phy_write(phydev, 0x10, 0x968a);
217*4882a593Smuzhiyun 	phy_write(phydev, 0x1f, 0x2a30);
218*4882a593Smuzhiyun 	phy_modify(phydev, 0x08, 0x0200, 0x0000);
219*4882a593Smuzhiyun 	phy_write(phydev, 0x1f, 0x0000);
220*4882a593Smuzhiyun 
221*4882a593Smuzhiyun 	phy_write(phydev, 0x1f, 0x0000);
222*4882a593Smuzhiyun 	phy_write(phydev, 0x12, 0x0048);
223*4882a593Smuzhiyun 	phy_write(phydev, 0x1f, 0x2a30);
224*4882a593Smuzhiyun 	phy_modify(phydev, 0x16, 0x0fc0, 0x0240);
225*4882a593Smuzhiyun 	phy_modify(phydev, 0x14, 0x6000, 0x4000);
226*4882a593Smuzhiyun 	phy_write(phydev, 0x1f, 0x0001);
227*4882a593Smuzhiyun 	phy_modify(phydev, 0x14, 0xe000, 0x6000);
228*4882a593Smuzhiyun 	phy_write(phydev, 0x1f, 0x0000);
229*4882a593Smuzhiyun 
230*4882a593Smuzhiyun 	vsc73xx_config_init(phydev);
231*4882a593Smuzhiyun 
232*4882a593Smuzhiyun 	return 0;
233*4882a593Smuzhiyun }
234*4882a593Smuzhiyun 
vsc73xx_config_aneg(struct phy_device * phydev)235*4882a593Smuzhiyun static int vsc73xx_config_aneg(struct phy_device *phydev)
236*4882a593Smuzhiyun {
237*4882a593Smuzhiyun 	/* The VSC73xx switches does not like to be instructed to
238*4882a593Smuzhiyun 	 * do autonegotiation in any way, it prefers that you just go
239*4882a593Smuzhiyun 	 * with the power-on/reset defaults. Writing some registers will
240*4882a593Smuzhiyun 	 * just make autonegotiation permanently fail.
241*4882a593Smuzhiyun 	 */
242*4882a593Smuzhiyun 	return 0;
243*4882a593Smuzhiyun }
244*4882a593Smuzhiyun 
245*4882a593Smuzhiyun /* This adds a skew for both TX and RX clocks, so the skew should only be
246*4882a593Smuzhiyun  * applied to "rgmii-id" interfaces. It may not work as expected
247*4882a593Smuzhiyun  * on "rgmii-txid", "rgmii-rxid" or "rgmii" interfaces. */
vsc8601_add_skew(struct phy_device * phydev)248*4882a593Smuzhiyun static int vsc8601_add_skew(struct phy_device *phydev)
249*4882a593Smuzhiyun {
250*4882a593Smuzhiyun 	int ret;
251*4882a593Smuzhiyun 
252*4882a593Smuzhiyun 	ret = phy_read(phydev, MII_VSC8601_EPHY_CTL);
253*4882a593Smuzhiyun 	if (ret < 0)
254*4882a593Smuzhiyun 		return ret;
255*4882a593Smuzhiyun 
256*4882a593Smuzhiyun 	ret |= MII_VSC8601_EPHY_CTL_RGMII_SKEW;
257*4882a593Smuzhiyun 	return phy_write(phydev, MII_VSC8601_EPHY_CTL, ret);
258*4882a593Smuzhiyun }
259*4882a593Smuzhiyun 
vsc8601_config_init(struct phy_device * phydev)260*4882a593Smuzhiyun static int vsc8601_config_init(struct phy_device *phydev)
261*4882a593Smuzhiyun {
262*4882a593Smuzhiyun 	int ret = 0;
263*4882a593Smuzhiyun 
264*4882a593Smuzhiyun 	if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID)
265*4882a593Smuzhiyun 		ret = vsc8601_add_skew(phydev);
266*4882a593Smuzhiyun 
267*4882a593Smuzhiyun 	if (ret < 0)
268*4882a593Smuzhiyun 		return ret;
269*4882a593Smuzhiyun 
270*4882a593Smuzhiyun 	return 0;
271*4882a593Smuzhiyun }
272*4882a593Smuzhiyun 
vsc824x_ack_interrupt(struct phy_device * phydev)273*4882a593Smuzhiyun static int vsc824x_ack_interrupt(struct phy_device *phydev)
274*4882a593Smuzhiyun {
275*4882a593Smuzhiyun 	int err = 0;
276*4882a593Smuzhiyun 
277*4882a593Smuzhiyun 	/* Don't bother to ACK the interrupts if interrupts
278*4882a593Smuzhiyun 	 * are disabled.  The 824x cannot clear the interrupts
279*4882a593Smuzhiyun 	 * if they are disabled.
280*4882a593Smuzhiyun 	 */
281*4882a593Smuzhiyun 	if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
282*4882a593Smuzhiyun 		err = phy_read(phydev, MII_VSC8244_ISTAT);
283*4882a593Smuzhiyun 
284*4882a593Smuzhiyun 	return (err < 0) ? err : 0;
285*4882a593Smuzhiyun }
286*4882a593Smuzhiyun 
vsc82xx_config_intr(struct phy_device * phydev)287*4882a593Smuzhiyun static int vsc82xx_config_intr(struct phy_device *phydev)
288*4882a593Smuzhiyun {
289*4882a593Smuzhiyun 	int err;
290*4882a593Smuzhiyun 
291*4882a593Smuzhiyun 	if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
292*4882a593Smuzhiyun 		err = phy_write(phydev, MII_VSC8244_IMASK,
293*4882a593Smuzhiyun 			(phydev->drv->phy_id == PHY_ID_VSC8234 ||
294*4882a593Smuzhiyun 			 phydev->drv->phy_id == PHY_ID_VSC8244 ||
295*4882a593Smuzhiyun 			 phydev->drv->phy_id == PHY_ID_VSC8572 ||
296*4882a593Smuzhiyun 			 phydev->drv->phy_id == PHY_ID_VSC8601) ?
297*4882a593Smuzhiyun 				MII_VSC8244_IMASK_MASK :
298*4882a593Smuzhiyun 				MII_VSC8221_IMASK_MASK);
299*4882a593Smuzhiyun 	else {
300*4882a593Smuzhiyun 		/* The Vitesse PHY cannot clear the interrupt
301*4882a593Smuzhiyun 		 * once it has disabled them, so we clear them first
302*4882a593Smuzhiyun 		 */
303*4882a593Smuzhiyun 		err = phy_read(phydev, MII_VSC8244_ISTAT);
304*4882a593Smuzhiyun 
305*4882a593Smuzhiyun 		if (err < 0)
306*4882a593Smuzhiyun 			return err;
307*4882a593Smuzhiyun 
308*4882a593Smuzhiyun 		err = phy_write(phydev, MII_VSC8244_IMASK, 0);
309*4882a593Smuzhiyun 	}
310*4882a593Smuzhiyun 
311*4882a593Smuzhiyun 	return err;
312*4882a593Smuzhiyun }
313*4882a593Smuzhiyun 
vsc8221_config_init(struct phy_device * phydev)314*4882a593Smuzhiyun static int vsc8221_config_init(struct phy_device *phydev)
315*4882a593Smuzhiyun {
316*4882a593Smuzhiyun 	int err;
317*4882a593Smuzhiyun 
318*4882a593Smuzhiyun 	err = phy_write(phydev, MII_VSC8244_AUX_CONSTAT,
319*4882a593Smuzhiyun 			MII_VSC8221_AUXCONSTAT_INIT);
320*4882a593Smuzhiyun 	return err;
321*4882a593Smuzhiyun 
322*4882a593Smuzhiyun 	/* Perhaps we should set EXT_CON1 based on the interface?
323*4882a593Smuzhiyun 	 * Options are 802.3Z SerDes or SGMII
324*4882a593Smuzhiyun 	 */
325*4882a593Smuzhiyun }
326*4882a593Smuzhiyun 
327*4882a593Smuzhiyun /* vsc82x4_config_autocross_enable - Enable auto MDI/MDI-X for forced links
328*4882a593Smuzhiyun  * @phydev: target phy_device struct
329*4882a593Smuzhiyun  *
330*4882a593Smuzhiyun  * Enable auto MDI/MDI-X when in 10/100 forced link speeds by writing
331*4882a593Smuzhiyun  * special values in the VSC8234/VSC8244 extended reserved registers
332*4882a593Smuzhiyun  */
vsc82x4_config_autocross_enable(struct phy_device * phydev)333*4882a593Smuzhiyun static int vsc82x4_config_autocross_enable(struct phy_device *phydev)
334*4882a593Smuzhiyun {
335*4882a593Smuzhiyun 	int ret;
336*4882a593Smuzhiyun 
337*4882a593Smuzhiyun 	if (phydev->autoneg == AUTONEG_ENABLE || phydev->speed > SPEED_100)
338*4882a593Smuzhiyun 		return 0;
339*4882a593Smuzhiyun 
340*4882a593Smuzhiyun 	/* map extended registers set 0x10 - 0x1e */
341*4882a593Smuzhiyun 	ret = phy_write(phydev, MII_VSC82X4_EXT_PAGE_ACCESS, 0x52b5);
342*4882a593Smuzhiyun 	if (ret >= 0)
343*4882a593Smuzhiyun 		ret = phy_write(phydev, MII_VSC82X4_EXT_PAGE_18E, 0x0012);
344*4882a593Smuzhiyun 	if (ret >= 0)
345*4882a593Smuzhiyun 		ret = phy_write(phydev, MII_VSC82X4_EXT_PAGE_17E, 0x2803);
346*4882a593Smuzhiyun 	if (ret >= 0)
347*4882a593Smuzhiyun 		ret = phy_write(phydev, MII_VSC82X4_EXT_PAGE_16E, 0x87fa);
348*4882a593Smuzhiyun 	/* map standard registers set 0x10 - 0x1e */
349*4882a593Smuzhiyun 	if (ret >= 0)
350*4882a593Smuzhiyun 		ret = phy_write(phydev, MII_VSC82X4_EXT_PAGE_ACCESS, 0x0000);
351*4882a593Smuzhiyun 	else
352*4882a593Smuzhiyun 		phy_write(phydev, MII_VSC82X4_EXT_PAGE_ACCESS, 0x0000);
353*4882a593Smuzhiyun 
354*4882a593Smuzhiyun 	return ret;
355*4882a593Smuzhiyun }
356*4882a593Smuzhiyun 
357*4882a593Smuzhiyun /* vsc82x4_config_aneg - restart auto-negotiation or write BMCR
358*4882a593Smuzhiyun  * @phydev: target phy_device struct
359*4882a593Smuzhiyun  *
360*4882a593Smuzhiyun  * Description: If auto-negotiation is enabled, we configure the
361*4882a593Smuzhiyun  *   advertising, and then restart auto-negotiation.  If it is not
362*4882a593Smuzhiyun  *   enabled, then we write the BMCR and also start the auto
363*4882a593Smuzhiyun  *   MDI/MDI-X feature
364*4882a593Smuzhiyun  */
vsc82x4_config_aneg(struct phy_device * phydev)365*4882a593Smuzhiyun static int vsc82x4_config_aneg(struct phy_device *phydev)
366*4882a593Smuzhiyun {
367*4882a593Smuzhiyun 	int ret;
368*4882a593Smuzhiyun 
369*4882a593Smuzhiyun 	/* Enable auto MDI/MDI-X when in 10/100 forced link speeds by
370*4882a593Smuzhiyun 	 * writing special values in the VSC8234 extended reserved registers
371*4882a593Smuzhiyun 	 */
372*4882a593Smuzhiyun 	if (phydev->autoneg != AUTONEG_ENABLE && phydev->speed <= SPEED_100) {
373*4882a593Smuzhiyun 		ret = genphy_setup_forced(phydev);
374*4882a593Smuzhiyun 
375*4882a593Smuzhiyun 		if (ret < 0) /* error */
376*4882a593Smuzhiyun 			return ret;
377*4882a593Smuzhiyun 
378*4882a593Smuzhiyun 		return vsc82x4_config_autocross_enable(phydev);
379*4882a593Smuzhiyun 	}
380*4882a593Smuzhiyun 
381*4882a593Smuzhiyun 	return genphy_config_aneg(phydev);
382*4882a593Smuzhiyun }
383*4882a593Smuzhiyun 
384*4882a593Smuzhiyun /* Vitesse 82xx */
385*4882a593Smuzhiyun static struct phy_driver vsc82xx_driver[] = {
386*4882a593Smuzhiyun {
387*4882a593Smuzhiyun 	.phy_id         = PHY_ID_VSC8234,
388*4882a593Smuzhiyun 	.name           = "Vitesse VSC8234",
389*4882a593Smuzhiyun 	.phy_id_mask    = 0x000ffff0,
390*4882a593Smuzhiyun 	/* PHY_GBIT_FEATURES */
391*4882a593Smuzhiyun 	.config_init    = &vsc824x_config_init,
392*4882a593Smuzhiyun 	.config_aneg    = &vsc82x4_config_aneg,
393*4882a593Smuzhiyun 	.ack_interrupt  = &vsc824x_ack_interrupt,
394*4882a593Smuzhiyun 	.config_intr    = &vsc82xx_config_intr,
395*4882a593Smuzhiyun }, {
396*4882a593Smuzhiyun 	.phy_id		= PHY_ID_VSC8244,
397*4882a593Smuzhiyun 	.name		= "Vitesse VSC8244",
398*4882a593Smuzhiyun 	.phy_id_mask	= 0x000fffc0,
399*4882a593Smuzhiyun 	/* PHY_GBIT_FEATURES */
400*4882a593Smuzhiyun 	.config_init	= &vsc824x_config_init,
401*4882a593Smuzhiyun 	.config_aneg	= &vsc82x4_config_aneg,
402*4882a593Smuzhiyun 	.ack_interrupt	= &vsc824x_ack_interrupt,
403*4882a593Smuzhiyun 	.config_intr	= &vsc82xx_config_intr,
404*4882a593Smuzhiyun }, {
405*4882a593Smuzhiyun 	.phy_id         = PHY_ID_VSC8572,
406*4882a593Smuzhiyun 	.name           = "Vitesse VSC8572",
407*4882a593Smuzhiyun 	.phy_id_mask    = 0x000ffff0,
408*4882a593Smuzhiyun 	/* PHY_GBIT_FEATURES */
409*4882a593Smuzhiyun 	.config_init    = &vsc824x_config_init,
410*4882a593Smuzhiyun 	.config_aneg    = &vsc82x4_config_aneg,
411*4882a593Smuzhiyun 	.ack_interrupt  = &vsc824x_ack_interrupt,
412*4882a593Smuzhiyun 	.config_intr    = &vsc82xx_config_intr,
413*4882a593Smuzhiyun }, {
414*4882a593Smuzhiyun 	.phy_id         = PHY_ID_VSC8601,
415*4882a593Smuzhiyun 	.name           = "Vitesse VSC8601",
416*4882a593Smuzhiyun 	.phy_id_mask    = 0x000ffff0,
417*4882a593Smuzhiyun 	/* PHY_GBIT_FEATURES */
418*4882a593Smuzhiyun 	.config_init    = &vsc8601_config_init,
419*4882a593Smuzhiyun 	.ack_interrupt  = &vsc824x_ack_interrupt,
420*4882a593Smuzhiyun 	.config_intr    = &vsc82xx_config_intr,
421*4882a593Smuzhiyun }, {
422*4882a593Smuzhiyun 	.phy_id         = PHY_ID_VSC7385,
423*4882a593Smuzhiyun 	.name           = "Vitesse VSC7385",
424*4882a593Smuzhiyun 	.phy_id_mask    = 0x000ffff0,
425*4882a593Smuzhiyun 	/* PHY_GBIT_FEATURES */
426*4882a593Smuzhiyun 	.config_init    = vsc738x_config_init,
427*4882a593Smuzhiyun 	.config_aneg    = vsc73xx_config_aneg,
428*4882a593Smuzhiyun 	.read_page      = vsc73xx_read_page,
429*4882a593Smuzhiyun 	.write_page     = vsc73xx_write_page,
430*4882a593Smuzhiyun }, {
431*4882a593Smuzhiyun 	.phy_id         = PHY_ID_VSC7388,
432*4882a593Smuzhiyun 	.name           = "Vitesse VSC7388",
433*4882a593Smuzhiyun 	.phy_id_mask    = 0x000ffff0,
434*4882a593Smuzhiyun 	/* PHY_GBIT_FEATURES */
435*4882a593Smuzhiyun 	.config_init    = vsc738x_config_init,
436*4882a593Smuzhiyun 	.config_aneg    = vsc73xx_config_aneg,
437*4882a593Smuzhiyun 	.read_page      = vsc73xx_read_page,
438*4882a593Smuzhiyun 	.write_page     = vsc73xx_write_page,
439*4882a593Smuzhiyun }, {
440*4882a593Smuzhiyun 	.phy_id         = PHY_ID_VSC7395,
441*4882a593Smuzhiyun 	.name           = "Vitesse VSC7395",
442*4882a593Smuzhiyun 	.phy_id_mask    = 0x000ffff0,
443*4882a593Smuzhiyun 	/* PHY_GBIT_FEATURES */
444*4882a593Smuzhiyun 	.config_init    = vsc739x_config_init,
445*4882a593Smuzhiyun 	.config_aneg    = vsc73xx_config_aneg,
446*4882a593Smuzhiyun 	.read_page      = vsc73xx_read_page,
447*4882a593Smuzhiyun 	.write_page     = vsc73xx_write_page,
448*4882a593Smuzhiyun }, {
449*4882a593Smuzhiyun 	.phy_id         = PHY_ID_VSC7398,
450*4882a593Smuzhiyun 	.name           = "Vitesse VSC7398",
451*4882a593Smuzhiyun 	.phy_id_mask    = 0x000ffff0,
452*4882a593Smuzhiyun 	/* PHY_GBIT_FEATURES */
453*4882a593Smuzhiyun 	.config_init    = vsc739x_config_init,
454*4882a593Smuzhiyun 	.config_aneg    = vsc73xx_config_aneg,
455*4882a593Smuzhiyun 	.read_page      = vsc73xx_read_page,
456*4882a593Smuzhiyun 	.write_page     = vsc73xx_write_page,
457*4882a593Smuzhiyun }, {
458*4882a593Smuzhiyun 	.phy_id         = PHY_ID_VSC8662,
459*4882a593Smuzhiyun 	.name           = "Vitesse VSC8662",
460*4882a593Smuzhiyun 	.phy_id_mask    = 0x000ffff0,
461*4882a593Smuzhiyun 	/* PHY_GBIT_FEATURES */
462*4882a593Smuzhiyun 	.config_init    = &vsc824x_config_init,
463*4882a593Smuzhiyun 	.config_aneg    = &vsc82x4_config_aneg,
464*4882a593Smuzhiyun 	.ack_interrupt  = &vsc824x_ack_interrupt,
465*4882a593Smuzhiyun 	.config_intr    = &vsc82xx_config_intr,
466*4882a593Smuzhiyun }, {
467*4882a593Smuzhiyun 	/* Vitesse 8221 */
468*4882a593Smuzhiyun 	.phy_id		= PHY_ID_VSC8221,
469*4882a593Smuzhiyun 	.phy_id_mask	= 0x000ffff0,
470*4882a593Smuzhiyun 	.name		= "Vitesse VSC8221",
471*4882a593Smuzhiyun 	/* PHY_GBIT_FEATURES */
472*4882a593Smuzhiyun 	.config_init	= &vsc8221_config_init,
473*4882a593Smuzhiyun 	.ack_interrupt	= &vsc824x_ack_interrupt,
474*4882a593Smuzhiyun 	.config_intr	= &vsc82xx_config_intr,
475*4882a593Smuzhiyun }, {
476*4882a593Smuzhiyun 	/* Vitesse 8211 */
477*4882a593Smuzhiyun 	.phy_id		= PHY_ID_VSC8211,
478*4882a593Smuzhiyun 	.phy_id_mask	= 0x000ffff0,
479*4882a593Smuzhiyun 	.name		= "Vitesse VSC8211",
480*4882a593Smuzhiyun 	/* PHY_GBIT_FEATURES */
481*4882a593Smuzhiyun 	.config_init	= &vsc8221_config_init,
482*4882a593Smuzhiyun 	.ack_interrupt	= &vsc824x_ack_interrupt,
483*4882a593Smuzhiyun 	.config_intr	= &vsc82xx_config_intr,
484*4882a593Smuzhiyun } };
485*4882a593Smuzhiyun 
486*4882a593Smuzhiyun module_phy_driver(vsc82xx_driver);
487*4882a593Smuzhiyun 
488*4882a593Smuzhiyun static struct mdio_device_id __maybe_unused vitesse_tbl[] = {
489*4882a593Smuzhiyun 	{ PHY_ID_VSC8234, 0x000ffff0 },
490*4882a593Smuzhiyun 	{ PHY_ID_VSC8244, 0x000fffc0 },
491*4882a593Smuzhiyun 	{ PHY_ID_VSC8572, 0x000ffff0 },
492*4882a593Smuzhiyun 	{ PHY_ID_VSC7385, 0x000ffff0 },
493*4882a593Smuzhiyun 	{ PHY_ID_VSC7388, 0x000ffff0 },
494*4882a593Smuzhiyun 	{ PHY_ID_VSC7395, 0x000ffff0 },
495*4882a593Smuzhiyun 	{ PHY_ID_VSC7398, 0x000ffff0 },
496*4882a593Smuzhiyun 	{ PHY_ID_VSC8662, 0x000ffff0 },
497*4882a593Smuzhiyun 	{ PHY_ID_VSC8221, 0x000ffff0 },
498*4882a593Smuzhiyun 	{ PHY_ID_VSC8211, 0x000ffff0 },
499*4882a593Smuzhiyun 	{ }
500*4882a593Smuzhiyun };
501*4882a593Smuzhiyun 
502*4882a593Smuzhiyun MODULE_DEVICE_TABLE(mdio, vitesse_tbl);
503