xref: /OK3568_Linux_fs/u-boot/drivers/net/phy/realtek.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun  * RealTek PHY drivers
3*4882a593Smuzhiyun  *
4*4882a593Smuzhiyun  * SPDX-License-Identifier:	GPL-2.0+
5*4882a593Smuzhiyun  *
6*4882a593Smuzhiyun  * Copyright 2010-2011, 2015 Freescale Semiconductor, Inc.
7*4882a593Smuzhiyun  * author Andy Fleming
8*4882a593Smuzhiyun  * Copyright 2016 Karsten Merker <merker@debian.org>
9*4882a593Smuzhiyun  */
10*4882a593Smuzhiyun #include <config.h>
11*4882a593Smuzhiyun #include <common.h>
12*4882a593Smuzhiyun #include <linux/bitops.h>
13*4882a593Smuzhiyun #include <phy.h>
14*4882a593Smuzhiyun 
15*4882a593Smuzhiyun #define PHY_RTL8211x_FORCE_MASTER BIT(1)
16*4882a593Smuzhiyun 
17*4882a593Smuzhiyun #define PHY_AUTONEGOTIATE_TIMEOUT 5000
18*4882a593Smuzhiyun 
19*4882a593Smuzhiyun /* RTL8211x 1000BASE-T Control Register */
20*4882a593Smuzhiyun #define MIIM_RTL8211x_CTRL1000T_MSCE BIT(12);
21*4882a593Smuzhiyun #define MIIM_RTL8211x_CTRL1000T_MASTER BIT(11);
22*4882a593Smuzhiyun 
23*4882a593Smuzhiyun /* RTL8211x PHY Status Register */
24*4882a593Smuzhiyun #define MIIM_RTL8211x_PHY_STATUS       0x11
25*4882a593Smuzhiyun #define MIIM_RTL8211x_PHYSTAT_SPEED    0xc000
26*4882a593Smuzhiyun #define MIIM_RTL8211x_PHYSTAT_GBIT     0x8000
27*4882a593Smuzhiyun #define MIIM_RTL8211x_PHYSTAT_100      0x4000
28*4882a593Smuzhiyun #define MIIM_RTL8211x_PHYSTAT_DUPLEX   0x2000
29*4882a593Smuzhiyun #define MIIM_RTL8211x_PHYSTAT_SPDDONE  0x0800
30*4882a593Smuzhiyun #define MIIM_RTL8211x_PHYSTAT_LINK     0x0400
31*4882a593Smuzhiyun 
32*4882a593Smuzhiyun /* RTL8211x PHY Interrupt Enable Register */
33*4882a593Smuzhiyun #define MIIM_RTL8211x_PHY_INER         0x12
34*4882a593Smuzhiyun #define MIIM_RTL8211x_PHY_INTR_ENA     0x9f01
35*4882a593Smuzhiyun #define MIIM_RTL8211x_PHY_INTR_DIS     0x0000
36*4882a593Smuzhiyun 
37*4882a593Smuzhiyun /* RTL8211x PHY Interrupt Status Register */
38*4882a593Smuzhiyun #define MIIM_RTL8211x_PHY_INSR         0x13
39*4882a593Smuzhiyun 
40*4882a593Smuzhiyun /* RTL8211F PHY Status Register */
41*4882a593Smuzhiyun #define MIIM_RTL8211F_PHY_STATUS       0x1a
42*4882a593Smuzhiyun #define MIIM_RTL8211F_AUTONEG_ENABLE   0x1000
43*4882a593Smuzhiyun #define MIIM_RTL8211F_PHYSTAT_SPEED    0x0030
44*4882a593Smuzhiyun #define MIIM_RTL8211F_PHYSTAT_GBIT     0x0020
45*4882a593Smuzhiyun #define MIIM_RTL8211F_PHYSTAT_100      0x0010
46*4882a593Smuzhiyun #define MIIM_RTL8211F_PHYSTAT_DUPLEX   0x0008
47*4882a593Smuzhiyun #define MIIM_RTL8211F_PHYSTAT_SPDDONE  0x0800
48*4882a593Smuzhiyun #define MIIM_RTL8211F_PHYSTAT_LINK     0x0004
49*4882a593Smuzhiyun 
50*4882a593Smuzhiyun #define MIIM_RTL8211F_PAGE_SELECT      0x1f
51*4882a593Smuzhiyun #define MIIM_RTL8211F_TX_DELAY		0x100
52*4882a593Smuzhiyun #define MIIM_RTL8211F_LCR		0x10
53*4882a593Smuzhiyun 
rtl8211b_probe(struct phy_device * phydev)54*4882a593Smuzhiyun static int rtl8211b_probe(struct phy_device *phydev)
55*4882a593Smuzhiyun {
56*4882a593Smuzhiyun #ifdef CONFIG_RTL8211X_PHY_FORCE_MASTER
57*4882a593Smuzhiyun 	phydev->flags |= PHY_RTL8211x_FORCE_MASTER;
58*4882a593Smuzhiyun #endif
59*4882a593Smuzhiyun 
60*4882a593Smuzhiyun 	return 0;
61*4882a593Smuzhiyun }
62*4882a593Smuzhiyun 
63*4882a593Smuzhiyun /* RealTek RTL8211x */
rtl8211x_config(struct phy_device * phydev)64*4882a593Smuzhiyun static int rtl8211x_config(struct phy_device *phydev)
65*4882a593Smuzhiyun {
66*4882a593Smuzhiyun 	phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, BMCR_RESET);
67*4882a593Smuzhiyun 
68*4882a593Smuzhiyun 	/* mask interrupt at init; if the interrupt is
69*4882a593Smuzhiyun 	 * needed indeed, it should be explicitly enabled
70*4882a593Smuzhiyun 	 */
71*4882a593Smuzhiyun 	phy_write(phydev, MDIO_DEVAD_NONE, MIIM_RTL8211x_PHY_INER,
72*4882a593Smuzhiyun 		  MIIM_RTL8211x_PHY_INTR_DIS);
73*4882a593Smuzhiyun 
74*4882a593Smuzhiyun 	if (phydev->flags & PHY_RTL8211x_FORCE_MASTER) {
75*4882a593Smuzhiyun 		unsigned int reg;
76*4882a593Smuzhiyun 
77*4882a593Smuzhiyun 		reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_CTRL1000);
78*4882a593Smuzhiyun 		/* force manual master/slave configuration */
79*4882a593Smuzhiyun 		reg |= MIIM_RTL8211x_CTRL1000T_MSCE;
80*4882a593Smuzhiyun 		/* force master mode */
81*4882a593Smuzhiyun 		reg |= MIIM_RTL8211x_CTRL1000T_MASTER;
82*4882a593Smuzhiyun 		phy_write(phydev, MDIO_DEVAD_NONE, MII_CTRL1000, reg);
83*4882a593Smuzhiyun 	}
84*4882a593Smuzhiyun 	/* read interrupt status just to clear it */
85*4882a593Smuzhiyun 	phy_read(phydev, MDIO_DEVAD_NONE, MIIM_RTL8211x_PHY_INER);
86*4882a593Smuzhiyun 
87*4882a593Smuzhiyun 	genphy_config_aneg(phydev);
88*4882a593Smuzhiyun 
89*4882a593Smuzhiyun 	return 0;
90*4882a593Smuzhiyun }
91*4882a593Smuzhiyun 
rtl8211f_config(struct phy_device * phydev)92*4882a593Smuzhiyun static int rtl8211f_config(struct phy_device *phydev)
93*4882a593Smuzhiyun {
94*4882a593Smuzhiyun 	u16 reg;
95*4882a593Smuzhiyun 
96*4882a593Smuzhiyun 	phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, BMCR_RESET);
97*4882a593Smuzhiyun 
98*4882a593Smuzhiyun 	phy_write(phydev, MDIO_DEVAD_NONE,
99*4882a593Smuzhiyun 		  MIIM_RTL8211F_PAGE_SELECT, 0xd08);
100*4882a593Smuzhiyun 	reg = phy_read(phydev, MDIO_DEVAD_NONE, 0x11);
101*4882a593Smuzhiyun 
102*4882a593Smuzhiyun 	/* enable TX-delay for rgmii-id and rgmii-txid, otherwise disable it */
103*4882a593Smuzhiyun 	if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
104*4882a593Smuzhiyun 	    phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)
105*4882a593Smuzhiyun 		reg |= MIIM_RTL8211F_TX_DELAY;
106*4882a593Smuzhiyun 	else
107*4882a593Smuzhiyun 		reg &= ~MIIM_RTL8211F_TX_DELAY;
108*4882a593Smuzhiyun 
109*4882a593Smuzhiyun 	phy_write(phydev, MDIO_DEVAD_NONE, 0x11, reg);
110*4882a593Smuzhiyun 	/* restore to default page 0 */
111*4882a593Smuzhiyun 	phy_write(phydev, MDIO_DEVAD_NONE,
112*4882a593Smuzhiyun 		  MIIM_RTL8211F_PAGE_SELECT, 0x0);
113*4882a593Smuzhiyun 
114*4882a593Smuzhiyun 	/* Set green LED for Link, yellow LED for Active */
115*4882a593Smuzhiyun 	phy_write(phydev, MDIO_DEVAD_NONE,
116*4882a593Smuzhiyun 		  MIIM_RTL8211F_PAGE_SELECT, 0xd04);
117*4882a593Smuzhiyun 	phy_write(phydev, MDIO_DEVAD_NONE, 0x10, 0x617f);
118*4882a593Smuzhiyun 	phy_write(phydev, MDIO_DEVAD_NONE,
119*4882a593Smuzhiyun 		  MIIM_RTL8211F_PAGE_SELECT, 0x0);
120*4882a593Smuzhiyun 
121*4882a593Smuzhiyun 	genphy_config_aneg(phydev);
122*4882a593Smuzhiyun 
123*4882a593Smuzhiyun 	return 0;
124*4882a593Smuzhiyun }
125*4882a593Smuzhiyun 
rtl8211x_parse_status(struct phy_device * phydev)126*4882a593Smuzhiyun static int rtl8211x_parse_status(struct phy_device *phydev)
127*4882a593Smuzhiyun {
128*4882a593Smuzhiyun 	unsigned int speed;
129*4882a593Smuzhiyun 	unsigned int mii_reg;
130*4882a593Smuzhiyun 
131*4882a593Smuzhiyun 	mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_RTL8211x_PHY_STATUS);
132*4882a593Smuzhiyun 
133*4882a593Smuzhiyun 	if (!(mii_reg & MIIM_RTL8211x_PHYSTAT_SPDDONE)) {
134*4882a593Smuzhiyun 		int i = 0;
135*4882a593Smuzhiyun 
136*4882a593Smuzhiyun 		/* in case of timeout ->link is cleared */
137*4882a593Smuzhiyun 		phydev->link = 1;
138*4882a593Smuzhiyun 		puts("Waiting for PHY realtime link");
139*4882a593Smuzhiyun 		while (!(mii_reg & MIIM_RTL8211x_PHYSTAT_SPDDONE)) {
140*4882a593Smuzhiyun 			/* Timeout reached ? */
141*4882a593Smuzhiyun 			if (i > PHY_AUTONEGOTIATE_TIMEOUT) {
142*4882a593Smuzhiyun 				puts(" TIMEOUT !\n");
143*4882a593Smuzhiyun 				phydev->link = 0;
144*4882a593Smuzhiyun 				break;
145*4882a593Smuzhiyun 			}
146*4882a593Smuzhiyun 
147*4882a593Smuzhiyun 			if ((i++ % 1000) == 0)
148*4882a593Smuzhiyun 				putc('.');
149*4882a593Smuzhiyun 			udelay(1000);	/* 1 ms */
150*4882a593Smuzhiyun 			mii_reg = phy_read(phydev, MDIO_DEVAD_NONE,
151*4882a593Smuzhiyun 					MIIM_RTL8211x_PHY_STATUS);
152*4882a593Smuzhiyun 		}
153*4882a593Smuzhiyun 		puts(" done\n");
154*4882a593Smuzhiyun 		udelay(500000);	/* another 500 ms (results in faster booting) */
155*4882a593Smuzhiyun 	} else {
156*4882a593Smuzhiyun 		if (mii_reg & MIIM_RTL8211x_PHYSTAT_LINK)
157*4882a593Smuzhiyun 			phydev->link = 1;
158*4882a593Smuzhiyun 		else
159*4882a593Smuzhiyun 			phydev->link = 0;
160*4882a593Smuzhiyun 	}
161*4882a593Smuzhiyun 
162*4882a593Smuzhiyun 	if (mii_reg & MIIM_RTL8211x_PHYSTAT_DUPLEX)
163*4882a593Smuzhiyun 		phydev->duplex = DUPLEX_FULL;
164*4882a593Smuzhiyun 	else
165*4882a593Smuzhiyun 		phydev->duplex = DUPLEX_HALF;
166*4882a593Smuzhiyun 
167*4882a593Smuzhiyun 	speed = (mii_reg & MIIM_RTL8211x_PHYSTAT_SPEED);
168*4882a593Smuzhiyun 
169*4882a593Smuzhiyun 	switch (speed) {
170*4882a593Smuzhiyun 	case MIIM_RTL8211x_PHYSTAT_GBIT:
171*4882a593Smuzhiyun 		phydev->speed = SPEED_1000;
172*4882a593Smuzhiyun 		break;
173*4882a593Smuzhiyun 	case MIIM_RTL8211x_PHYSTAT_100:
174*4882a593Smuzhiyun 		phydev->speed = SPEED_100;
175*4882a593Smuzhiyun 		break;
176*4882a593Smuzhiyun 	default:
177*4882a593Smuzhiyun 		phydev->speed = SPEED_10;
178*4882a593Smuzhiyun 	}
179*4882a593Smuzhiyun 
180*4882a593Smuzhiyun 	return 0;
181*4882a593Smuzhiyun }
182*4882a593Smuzhiyun 
rtl8211f_parse_status(struct phy_device * phydev)183*4882a593Smuzhiyun static int rtl8211f_parse_status(struct phy_device *phydev)
184*4882a593Smuzhiyun {
185*4882a593Smuzhiyun 	unsigned int speed;
186*4882a593Smuzhiyun 	unsigned int mii_reg;
187*4882a593Smuzhiyun 	int i = 0;
188*4882a593Smuzhiyun 
189*4882a593Smuzhiyun 	phy_write(phydev, MDIO_DEVAD_NONE, MIIM_RTL8211F_PAGE_SELECT, 0xa43);
190*4882a593Smuzhiyun 	mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_RTL8211F_PHY_STATUS);
191*4882a593Smuzhiyun 
192*4882a593Smuzhiyun 	phydev->link = 1;
193*4882a593Smuzhiyun 	while (!(mii_reg & MIIM_RTL8211F_PHYSTAT_LINK)) {
194*4882a593Smuzhiyun 		if (i > PHY_AUTONEGOTIATE_TIMEOUT) {
195*4882a593Smuzhiyun 			puts(" TIMEOUT !\n");
196*4882a593Smuzhiyun 			phydev->link = 0;
197*4882a593Smuzhiyun 			break;
198*4882a593Smuzhiyun 		}
199*4882a593Smuzhiyun 
200*4882a593Smuzhiyun 		if ((i++ % 1000) == 0)
201*4882a593Smuzhiyun 			putc('.');
202*4882a593Smuzhiyun 		udelay(1000);
203*4882a593Smuzhiyun 		mii_reg = phy_read(phydev, MDIO_DEVAD_NONE,
204*4882a593Smuzhiyun 				   MIIM_RTL8211F_PHY_STATUS);
205*4882a593Smuzhiyun 	}
206*4882a593Smuzhiyun 
207*4882a593Smuzhiyun 	if (mii_reg & MIIM_RTL8211F_PHYSTAT_DUPLEX)
208*4882a593Smuzhiyun 		phydev->duplex = DUPLEX_FULL;
209*4882a593Smuzhiyun 	else
210*4882a593Smuzhiyun 		phydev->duplex = DUPLEX_HALF;
211*4882a593Smuzhiyun 
212*4882a593Smuzhiyun 	speed = (mii_reg & MIIM_RTL8211F_PHYSTAT_SPEED);
213*4882a593Smuzhiyun 
214*4882a593Smuzhiyun 	switch (speed) {
215*4882a593Smuzhiyun 	case MIIM_RTL8211F_PHYSTAT_GBIT:
216*4882a593Smuzhiyun 		phydev->speed = SPEED_1000;
217*4882a593Smuzhiyun 		break;
218*4882a593Smuzhiyun 	case MIIM_RTL8211F_PHYSTAT_100:
219*4882a593Smuzhiyun 		phydev->speed = SPEED_100;
220*4882a593Smuzhiyun 		break;
221*4882a593Smuzhiyun 	default:
222*4882a593Smuzhiyun 		phydev->speed = SPEED_10;
223*4882a593Smuzhiyun 	}
224*4882a593Smuzhiyun 
225*4882a593Smuzhiyun 	return 0;
226*4882a593Smuzhiyun }
227*4882a593Smuzhiyun 
rtl8211x_startup(struct phy_device * phydev)228*4882a593Smuzhiyun static int rtl8211x_startup(struct phy_device *phydev)
229*4882a593Smuzhiyun {
230*4882a593Smuzhiyun 	int ret;
231*4882a593Smuzhiyun 
232*4882a593Smuzhiyun 	/* Read the Status (2x to make sure link is right) */
233*4882a593Smuzhiyun 	ret = genphy_update_link(phydev);
234*4882a593Smuzhiyun 	if (ret)
235*4882a593Smuzhiyun 		return ret;
236*4882a593Smuzhiyun 
237*4882a593Smuzhiyun 	return rtl8211x_parse_status(phydev);
238*4882a593Smuzhiyun }
239*4882a593Smuzhiyun 
rtl8211e_startup(struct phy_device * phydev)240*4882a593Smuzhiyun static int rtl8211e_startup(struct phy_device *phydev)
241*4882a593Smuzhiyun {
242*4882a593Smuzhiyun 	int ret;
243*4882a593Smuzhiyun 
244*4882a593Smuzhiyun 	ret = genphy_update_link(phydev);
245*4882a593Smuzhiyun 	if (ret)
246*4882a593Smuzhiyun 		return ret;
247*4882a593Smuzhiyun 
248*4882a593Smuzhiyun 	return genphy_parse_link(phydev);
249*4882a593Smuzhiyun }
250*4882a593Smuzhiyun 
rtl8211f_startup(struct phy_device * phydev)251*4882a593Smuzhiyun static int rtl8211f_startup(struct phy_device *phydev)
252*4882a593Smuzhiyun {
253*4882a593Smuzhiyun 	int ret;
254*4882a593Smuzhiyun 
255*4882a593Smuzhiyun 	/* Read the Status (2x to make sure link is right) */
256*4882a593Smuzhiyun 	ret = genphy_update_link(phydev);
257*4882a593Smuzhiyun 	if (ret)
258*4882a593Smuzhiyun 		return ret;
259*4882a593Smuzhiyun 	/* Read the Status (2x to make sure link is right) */
260*4882a593Smuzhiyun 
261*4882a593Smuzhiyun 	return rtl8211f_parse_status(phydev);
262*4882a593Smuzhiyun }
263*4882a593Smuzhiyun 
264*4882a593Smuzhiyun /* Support for RTL8211B PHY */
265*4882a593Smuzhiyun static struct phy_driver RTL8211B_driver = {
266*4882a593Smuzhiyun 	.name = "RealTek RTL8211B",
267*4882a593Smuzhiyun 	.uid = 0x1cc912,
268*4882a593Smuzhiyun 	.mask = 0xffffff,
269*4882a593Smuzhiyun 	.features = PHY_GBIT_FEATURES,
270*4882a593Smuzhiyun 	.probe = &rtl8211b_probe,
271*4882a593Smuzhiyun 	.config = &rtl8211x_config,
272*4882a593Smuzhiyun 	.startup = &rtl8211x_startup,
273*4882a593Smuzhiyun 	.shutdown = &genphy_shutdown,
274*4882a593Smuzhiyun };
275*4882a593Smuzhiyun 
276*4882a593Smuzhiyun /* Support for RTL8211E-VB-CG, RTL8211E-VL-CG and RTL8211EG-VB-CG PHYs */
277*4882a593Smuzhiyun static struct phy_driver RTL8211E_driver = {
278*4882a593Smuzhiyun 	.name = "RealTek RTL8211E",
279*4882a593Smuzhiyun 	.uid = 0x1cc915,
280*4882a593Smuzhiyun 	.mask = 0xffffff,
281*4882a593Smuzhiyun 	.features = PHY_GBIT_FEATURES,
282*4882a593Smuzhiyun 	.config = &rtl8211x_config,
283*4882a593Smuzhiyun 	.startup = &rtl8211e_startup,
284*4882a593Smuzhiyun 	.shutdown = &genphy_shutdown,
285*4882a593Smuzhiyun };
286*4882a593Smuzhiyun 
287*4882a593Smuzhiyun /* Support for RTL8211DN PHY */
288*4882a593Smuzhiyun static struct phy_driver RTL8211DN_driver = {
289*4882a593Smuzhiyun 	.name = "RealTek RTL8211DN",
290*4882a593Smuzhiyun 	.uid = 0x1cc914,
291*4882a593Smuzhiyun 	.mask = 0xffffff,
292*4882a593Smuzhiyun 	.features = PHY_GBIT_FEATURES,
293*4882a593Smuzhiyun 	.config = &rtl8211x_config,
294*4882a593Smuzhiyun 	.startup = &rtl8211x_startup,
295*4882a593Smuzhiyun 	.shutdown = &genphy_shutdown,
296*4882a593Smuzhiyun };
297*4882a593Smuzhiyun 
298*4882a593Smuzhiyun /* Support for RTL8211F PHY */
299*4882a593Smuzhiyun static struct phy_driver RTL8211F_driver = {
300*4882a593Smuzhiyun 	.name = "RealTek RTL8211F",
301*4882a593Smuzhiyun 	.uid = 0x1cc916,
302*4882a593Smuzhiyun 	.mask = 0xffffff,
303*4882a593Smuzhiyun 	.features = PHY_GBIT_FEATURES,
304*4882a593Smuzhiyun 	.config = &rtl8211f_config,
305*4882a593Smuzhiyun 	.startup = &rtl8211f_startup,
306*4882a593Smuzhiyun 	.shutdown = &genphy_shutdown,
307*4882a593Smuzhiyun };
308*4882a593Smuzhiyun 
phy_realtek_init(void)309*4882a593Smuzhiyun int phy_realtek_init(void)
310*4882a593Smuzhiyun {
311*4882a593Smuzhiyun 	phy_register(&RTL8211B_driver);
312*4882a593Smuzhiyun 	phy_register(&RTL8211E_driver);
313*4882a593Smuzhiyun 	phy_register(&RTL8211F_driver);
314*4882a593Smuzhiyun 	phy_register(&RTL8211DN_driver);
315*4882a593Smuzhiyun 
316*4882a593Smuzhiyun 	return 0;
317*4882a593Smuzhiyun }
318