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