blob: dd45e11b3ad9852ef2cf9afada767dce6ab7a9bd [file] [log] [blame]
Tom Rini83d290c2018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Andy Fleming9082eea2011-04-07 21:56:05 -05002/*
3 * RealTek PHY drivers
4 *
Codrin Ciubotariu3cee1382015-02-13 14:47:58 +02005 * Copyright 2010-2011, 2015 Freescale Semiconductor, Inc.
Andy Fleming9082eea2011-04-07 21:56:05 -05006 * author Andy Fleming
Karsten Merker563d8d92016-03-21 20:29:07 +01007 * Copyright 2016 Karsten Merker <merker@debian.org>
Andy Fleming9082eea2011-04-07 21:56:05 -05008 */
Andy Fleming9082eea2011-04-07 21:56:05 -05009#include <common.h>
oliver@schinagl.nl020f6762016-11-08 17:38:57 +010010#include <linux/bitops.h>
Andy Fleming9082eea2011-04-07 21:56:05 -050011#include <phy.h>
12
oliver@schinagl.nlcebf3f52016-11-08 17:38:59 +010013#define PHY_RTL8211x_FORCE_MASTER BIT(1)
kevans@FreeBSD.org66526e72018-02-14 17:02:15 -060014#define PHY_RTL8211E_PINE64_GIGABIT_FIX BIT(2)
oliver@schinagl.nlcebf3f52016-11-08 17:38:59 +010015
Andy Fleming9082eea2011-04-07 21:56:05 -050016#define PHY_AUTONEGOTIATE_TIMEOUT 5000
17
Michael Haas525d1872016-03-25 18:22:50 +010018/* RTL8211x 1000BASE-T Control Register */
oliver@schinagl.nl020f6762016-11-08 17:38:57 +010019#define MIIM_RTL8211x_CTRL1000T_MSCE BIT(12);
oliver@schinagl.nlcbe40e12016-11-08 17:38:58 +010020#define MIIM_RTL8211x_CTRL1000T_MASTER BIT(11);
Michael Haas525d1872016-03-25 18:22:50 +010021
Bhupesh Sharmac624d162013-07-18 13:58:20 +053022/* RTL8211x PHY Status Register */
23#define MIIM_RTL8211x_PHY_STATUS 0x11
24#define MIIM_RTL8211x_PHYSTAT_SPEED 0xc000
25#define MIIM_RTL8211x_PHYSTAT_GBIT 0x8000
26#define MIIM_RTL8211x_PHYSTAT_100 0x4000
27#define MIIM_RTL8211x_PHYSTAT_DUPLEX 0x2000
28#define MIIM_RTL8211x_PHYSTAT_SPDDONE 0x0800
29#define MIIM_RTL8211x_PHYSTAT_LINK 0x0400
Andy Fleming9082eea2011-04-07 21:56:05 -050030
Codrin Ciubotariu3cee1382015-02-13 14:47:58 +020031/* RTL8211x PHY Interrupt Enable Register */
32#define MIIM_RTL8211x_PHY_INER 0x12
33#define MIIM_RTL8211x_PHY_INTR_ENA 0x9f01
34#define MIIM_RTL8211x_PHY_INTR_DIS 0x0000
35
36/* RTL8211x PHY Interrupt Status Register */
37#define MIIM_RTL8211x_PHY_INSR 0x13
Andy Fleming9082eea2011-04-07 21:56:05 -050038
Shengzhou Liu3d6af742015-03-12 18:54:59 +080039/* RTL8211F PHY Status Register */
40#define MIIM_RTL8211F_PHY_STATUS 0x1a
41#define MIIM_RTL8211F_AUTONEG_ENABLE 0x1000
42#define MIIM_RTL8211F_PHYSTAT_SPEED 0x0030
43#define MIIM_RTL8211F_PHYSTAT_GBIT 0x0020
44#define MIIM_RTL8211F_PHYSTAT_100 0x0010
45#define MIIM_RTL8211F_PHYSTAT_DUPLEX 0x0008
46#define MIIM_RTL8211F_PHYSTAT_SPDDONE 0x0800
47#define MIIM_RTL8211F_PHYSTAT_LINK 0x0004
48
kevans@FreeBSD.org66526e72018-02-14 17:02:15 -060049#define MIIM_RTL8211E_CONFREG 0x1c
50#define MIIM_RTL8211E_CONFREG_TXD 0x0002
51#define MIIM_RTL8211E_CONFREG_RXD 0x0004
52#define MIIM_RTL8211E_CONFREG_MAGIC 0xb400 /* Undocumented */
53
54#define MIIM_RTL8211E_EXT_PAGE_SELECT 0x1e
55
Shengzhou Liu3d6af742015-03-12 18:54:59 +080056#define MIIM_RTL8211F_PAGE_SELECT 0x1f
Shengzhou Liu793ea942015-04-24 16:57:17 +080057#define MIIM_RTL8211F_TX_DELAY 0x100
Shengzhou Liu90712742015-05-21 18:07:35 +080058#define MIIM_RTL8211F_LCR 0x10
Shengzhou Liu3d6af742015-03-12 18:54:59 +080059
Carlo Caionee57c9fd2019-01-16 11:34:50 +000060static int rtl8211f_phy_extread(struct phy_device *phydev, int addr,
61 int devaddr, int regnum)
62{
63 int oldpage = phy_read(phydev, MDIO_DEVAD_NONE,
64 MIIM_RTL8211F_PAGE_SELECT);
65 int val;
66
67 phy_write(phydev, MDIO_DEVAD_NONE, MIIM_RTL8211F_PAGE_SELECT, devaddr);
68 val = phy_read(phydev, MDIO_DEVAD_NONE, regnum);
69 phy_write(phydev, MDIO_DEVAD_NONE, MIIM_RTL8211F_PAGE_SELECT, oldpage);
70
71 return val;
72}
73
74static int rtl8211f_phy_extwrite(struct phy_device *phydev, int addr,
75 int devaddr, int regnum, u16 val)
76{
77 int oldpage = phy_read(phydev, MDIO_DEVAD_NONE,
78 MIIM_RTL8211F_PAGE_SELECT);
79
80 phy_write(phydev, MDIO_DEVAD_NONE, MIIM_RTL8211F_PAGE_SELECT, devaddr);
81 phy_write(phydev, MDIO_DEVAD_NONE, regnum, val);
82 phy_write(phydev, MDIO_DEVAD_NONE, MIIM_RTL8211F_PAGE_SELECT, oldpage);
83
84 return 0;
85}
86
oliver@schinagl.nlcebf3f52016-11-08 17:38:59 +010087static int rtl8211b_probe(struct phy_device *phydev)
88{
89#ifdef CONFIG_RTL8211X_PHY_FORCE_MASTER
90 phydev->flags |= PHY_RTL8211x_FORCE_MASTER;
91#endif
92
93 return 0;
94}
95
kevans@FreeBSD.org66526e72018-02-14 17:02:15 -060096static int rtl8211e_probe(struct phy_device *phydev)
97{
98#ifdef CONFIG_RTL8211E_PINE64_GIGABIT_FIX
99 phydev->flags |= PHY_RTL8211E_PINE64_GIGABIT_FIX;
100#endif
101
102 return 0;
103}
104
Bhupesh Sharmac624d162013-07-18 13:58:20 +0530105/* RealTek RTL8211x */
106static int rtl8211x_config(struct phy_device *phydev)
Andy Fleming9082eea2011-04-07 21:56:05 -0500107{
108 phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, BMCR_RESET);
109
Codrin Ciubotariu3cee1382015-02-13 14:47:58 +0200110 /* mask interrupt at init; if the interrupt is
111 * needed indeed, it should be explicitly enabled
112 */
113 phy_write(phydev, MDIO_DEVAD_NONE, MIIM_RTL8211x_PHY_INER,
114 MIIM_RTL8211x_PHY_INTR_DIS);
oliver@schinagl.nlcebf3f52016-11-08 17:38:59 +0100115
116 if (phydev->flags & PHY_RTL8211x_FORCE_MASTER) {
117 unsigned int reg;
118
119 reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_CTRL1000);
120 /* force manual master/slave configuration */
121 reg |= MIIM_RTL8211x_CTRL1000T_MSCE;
122 /* force master mode */
123 reg |= MIIM_RTL8211x_CTRL1000T_MASTER;
124 phy_write(phydev, MDIO_DEVAD_NONE, MII_CTRL1000, reg);
125 }
kevans@FreeBSD.org66526e72018-02-14 17:02:15 -0600126 if (phydev->flags & PHY_RTL8211E_PINE64_GIGABIT_FIX) {
127 unsigned int reg;
128
129 phy_write(phydev, MDIO_DEVAD_NONE, MIIM_RTL8211F_PAGE_SELECT,
130 7);
131 phy_write(phydev, MDIO_DEVAD_NONE,
132 MIIM_RTL8211E_EXT_PAGE_SELECT, 0xa4);
133 reg = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_RTL8211E_CONFREG);
134 /* Ensure both internal delays are turned off */
135 reg &= ~(MIIM_RTL8211E_CONFREG_TXD | MIIM_RTL8211E_CONFREG_RXD);
136 /* Flip the magic undocumented bits */
137 reg |= MIIM_RTL8211E_CONFREG_MAGIC;
138 phy_write(phydev, MDIO_DEVAD_NONE, MIIM_RTL8211E_CONFREG, reg);
139 phy_write(phydev, MDIO_DEVAD_NONE, MIIM_RTL8211F_PAGE_SELECT,
140 0);
141 }
Codrin Ciubotariu3cee1382015-02-13 14:47:58 +0200142 /* read interrupt status just to clear it */
143 phy_read(phydev, MDIO_DEVAD_NONE, MIIM_RTL8211x_PHY_INER);
144
Andy Fleming9082eea2011-04-07 21:56:05 -0500145 genphy_config_aneg(phydev);
146
147 return 0;
148}
149
Shengzhou Liu793ea942015-04-24 16:57:17 +0800150static int rtl8211f_config(struct phy_device *phydev)
151{
152 u16 reg;
153
154 phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, BMCR_RESET);
155
Madalin Bucur05b29aa2017-08-18 11:35:24 +0300156 phy_write(phydev, MDIO_DEVAD_NONE,
157 MIIM_RTL8211F_PAGE_SELECT, 0xd08);
158 reg = phy_read(phydev, MDIO_DEVAD_NONE, 0x11);
159
160 /* enable TX-delay for rgmii-id and rgmii-txid, otherwise disable it */
161 if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
162 phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)
Shengzhou Liu793ea942015-04-24 16:57:17 +0800163 reg |= MIIM_RTL8211F_TX_DELAY;
Madalin Bucur05b29aa2017-08-18 11:35:24 +0300164 else
165 reg &= ~MIIM_RTL8211F_TX_DELAY;
166
167 phy_write(phydev, MDIO_DEVAD_NONE, 0x11, reg);
168 /* restore to default page 0 */
169 phy_write(phydev, MDIO_DEVAD_NONE,
170 MIIM_RTL8211F_PAGE_SELECT, 0x0);
Shengzhou Liu793ea942015-04-24 16:57:17 +0800171
Shengzhou Liu90712742015-05-21 18:07:35 +0800172 /* Set green LED for Link, yellow LED for Active */
173 phy_write(phydev, MDIO_DEVAD_NONE,
174 MIIM_RTL8211F_PAGE_SELECT, 0xd04);
175 phy_write(phydev, MDIO_DEVAD_NONE, 0x10, 0x617f);
176 phy_write(phydev, MDIO_DEVAD_NONE,
177 MIIM_RTL8211F_PAGE_SELECT, 0x0);
178
Shengzhou Liu793ea942015-04-24 16:57:17 +0800179 genphy_config_aneg(phydev);
180
181 return 0;
182}
183
Bhupesh Sharmac624d162013-07-18 13:58:20 +0530184static int rtl8211x_parse_status(struct phy_device *phydev)
Andy Fleming9082eea2011-04-07 21:56:05 -0500185{
186 unsigned int speed;
187 unsigned int mii_reg;
188
Bhupesh Sharmac624d162013-07-18 13:58:20 +0530189 mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_RTL8211x_PHY_STATUS);
Andy Fleming9082eea2011-04-07 21:56:05 -0500190
Bhupesh Sharmac624d162013-07-18 13:58:20 +0530191 if (!(mii_reg & MIIM_RTL8211x_PHYSTAT_SPDDONE)) {
Andy Fleming9082eea2011-04-07 21:56:05 -0500192 int i = 0;
193
194 /* in case of timeout ->link is cleared */
195 phydev->link = 1;
196 puts("Waiting for PHY realtime link");
Bhupesh Sharmac624d162013-07-18 13:58:20 +0530197 while (!(mii_reg & MIIM_RTL8211x_PHYSTAT_SPDDONE)) {
Andy Fleming9082eea2011-04-07 21:56:05 -0500198 /* Timeout reached ? */
199 if (i > PHY_AUTONEGOTIATE_TIMEOUT) {
200 puts(" TIMEOUT !\n");
201 phydev->link = 0;
202 break;
203 }
204
205 if ((i++ % 1000) == 0)
206 putc('.');
207 udelay(1000); /* 1 ms */
208 mii_reg = phy_read(phydev, MDIO_DEVAD_NONE,
Bhupesh Sharmac624d162013-07-18 13:58:20 +0530209 MIIM_RTL8211x_PHY_STATUS);
Andy Fleming9082eea2011-04-07 21:56:05 -0500210 }
211 puts(" done\n");
212 udelay(500000); /* another 500 ms (results in faster booting) */
213 } else {
Bhupesh Sharmac624d162013-07-18 13:58:20 +0530214 if (mii_reg & MIIM_RTL8211x_PHYSTAT_LINK)
Andy Fleming9082eea2011-04-07 21:56:05 -0500215 phydev->link = 1;
216 else
217 phydev->link = 0;
218 }
219
Bhupesh Sharmac624d162013-07-18 13:58:20 +0530220 if (mii_reg & MIIM_RTL8211x_PHYSTAT_DUPLEX)
Andy Fleming9082eea2011-04-07 21:56:05 -0500221 phydev->duplex = DUPLEX_FULL;
222 else
223 phydev->duplex = DUPLEX_HALF;
224
Bhupesh Sharmac624d162013-07-18 13:58:20 +0530225 speed = (mii_reg & MIIM_RTL8211x_PHYSTAT_SPEED);
Andy Fleming9082eea2011-04-07 21:56:05 -0500226
227 switch (speed) {
Bhupesh Sharmac624d162013-07-18 13:58:20 +0530228 case MIIM_RTL8211x_PHYSTAT_GBIT:
Andy Fleming9082eea2011-04-07 21:56:05 -0500229 phydev->speed = SPEED_1000;
230 break;
Bhupesh Sharmac624d162013-07-18 13:58:20 +0530231 case MIIM_RTL8211x_PHYSTAT_100:
Andy Fleming9082eea2011-04-07 21:56:05 -0500232 phydev->speed = SPEED_100;
233 break;
234 default:
235 phydev->speed = SPEED_10;
236 }
237
238 return 0;
239}
240
Shengzhou Liu3d6af742015-03-12 18:54:59 +0800241static int rtl8211f_parse_status(struct phy_device *phydev)
242{
243 unsigned int speed;
244 unsigned int mii_reg;
245 int i = 0;
246
247 phy_write(phydev, MDIO_DEVAD_NONE, MIIM_RTL8211F_PAGE_SELECT, 0xa43);
248 mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_RTL8211F_PHY_STATUS);
249
250 phydev->link = 1;
251 while (!(mii_reg & MIIM_RTL8211F_PHYSTAT_LINK)) {
252 if (i > PHY_AUTONEGOTIATE_TIMEOUT) {
253 puts(" TIMEOUT !\n");
254 phydev->link = 0;
255 break;
256 }
257
258 if ((i++ % 1000) == 0)
259 putc('.');
260 udelay(1000);
261 mii_reg = phy_read(phydev, MDIO_DEVAD_NONE,
262 MIIM_RTL8211F_PHY_STATUS);
263 }
264
265 if (mii_reg & MIIM_RTL8211F_PHYSTAT_DUPLEX)
266 phydev->duplex = DUPLEX_FULL;
267 else
268 phydev->duplex = DUPLEX_HALF;
269
270 speed = (mii_reg & MIIM_RTL8211F_PHYSTAT_SPEED);
271
272 switch (speed) {
273 case MIIM_RTL8211F_PHYSTAT_GBIT:
274 phydev->speed = SPEED_1000;
275 break;
276 case MIIM_RTL8211F_PHYSTAT_100:
277 phydev->speed = SPEED_100;
278 break;
279 default:
280 phydev->speed = SPEED_10;
281 }
282
Shengzhou Liu3d6af742015-03-12 18:54:59 +0800283 return 0;
284}
285
Bhupesh Sharmac624d162013-07-18 13:58:20 +0530286static int rtl8211x_startup(struct phy_device *phydev)
Andy Fleming9082eea2011-04-07 21:56:05 -0500287{
Michal Simekb733c272016-05-18 12:46:12 +0200288 int ret;
Andy Fleming9082eea2011-04-07 21:56:05 -0500289
Michal Simekb733c272016-05-18 12:46:12 +0200290 /* Read the Status (2x to make sure link is right) */
291 ret = genphy_update_link(phydev);
292 if (ret)
293 return ret;
294
295 return rtl8211x_parse_status(phydev);
Andy Fleming9082eea2011-04-07 21:56:05 -0500296}
297
Michal Simek6a10bc52016-02-13 10:31:32 +0100298static int rtl8211e_startup(struct phy_device *phydev)
299{
Michal Simekb733c272016-05-18 12:46:12 +0200300 int ret;
Michal Simek6a10bc52016-02-13 10:31:32 +0100301
Michal Simekb733c272016-05-18 12:46:12 +0200302 ret = genphy_update_link(phydev);
303 if (ret)
304 return ret;
305
306 return genphy_parse_link(phydev);
Michal Simek6a10bc52016-02-13 10:31:32 +0100307}
308
Shengzhou Liu3d6af742015-03-12 18:54:59 +0800309static int rtl8211f_startup(struct phy_device *phydev)
310{
Michal Simekb733c272016-05-18 12:46:12 +0200311 int ret;
Shengzhou Liu3d6af742015-03-12 18:54:59 +0800312
Michal Simekb733c272016-05-18 12:46:12 +0200313 /* Read the Status (2x to make sure link is right) */
314 ret = genphy_update_link(phydev);
315 if (ret)
316 return ret;
317 /* Read the Status (2x to make sure link is right) */
318
319 return rtl8211f_parse_status(phydev);
Shengzhou Liu3d6af742015-03-12 18:54:59 +0800320}
321
Bhupesh Sharmac624d162013-07-18 13:58:20 +0530322/* Support for RTL8211B PHY */
Andy Fleming9082eea2011-04-07 21:56:05 -0500323static struct phy_driver RTL8211B_driver = {
324 .name = "RealTek RTL8211B",
Karsten Merker563d8d92016-03-21 20:29:07 +0100325 .uid = 0x1cc912,
Bhupesh Sharma42205042013-09-01 04:40:52 +0530326 .mask = 0xffffff,
Andy Fleming9082eea2011-04-07 21:56:05 -0500327 .features = PHY_GBIT_FEATURES,
oliver@schinagl.nlcebf3f52016-11-08 17:38:59 +0100328 .probe = &rtl8211b_probe,
Bhupesh Sharmac624d162013-07-18 13:58:20 +0530329 .config = &rtl8211x_config,
330 .startup = &rtl8211x_startup,
331 .shutdown = &genphy_shutdown,
332};
333
334/* Support for RTL8211E-VB-CG, RTL8211E-VL-CG and RTL8211EG-VB-CG PHYs */
335static struct phy_driver RTL8211E_driver = {
336 .name = "RealTek RTL8211E",
337 .uid = 0x1cc915,
Bhupesh Sharma42205042013-09-01 04:40:52 +0530338 .mask = 0xffffff,
Bhupesh Sharmac624d162013-07-18 13:58:20 +0530339 .features = PHY_GBIT_FEATURES,
kevans@FreeBSD.org66526e72018-02-14 17:02:15 -0600340 .probe = &rtl8211e_probe,
Bhupesh Sharmac624d162013-07-18 13:58:20 +0530341 .config = &rtl8211x_config,
Michal Simek6a10bc52016-02-13 10:31:32 +0100342 .startup = &rtl8211e_startup,
Bhupesh Sharmac624d162013-07-18 13:58:20 +0530343 .shutdown = &genphy_shutdown,
344};
345
346/* Support for RTL8211DN PHY */
347static struct phy_driver RTL8211DN_driver = {
348 .name = "RealTek RTL8211DN",
349 .uid = 0x1cc914,
Bhupesh Sharma42205042013-09-01 04:40:52 +0530350 .mask = 0xffffff,
Bhupesh Sharmac624d162013-07-18 13:58:20 +0530351 .features = PHY_GBIT_FEATURES,
352 .config = &rtl8211x_config,
353 .startup = &rtl8211x_startup,
Andy Fleming9082eea2011-04-07 21:56:05 -0500354 .shutdown = &genphy_shutdown,
355};
356
Shengzhou Liu3d6af742015-03-12 18:54:59 +0800357/* Support for RTL8211F PHY */
358static struct phy_driver RTL8211F_driver = {
359 .name = "RealTek RTL8211F",
360 .uid = 0x1cc916,
361 .mask = 0xffffff,
362 .features = PHY_GBIT_FEATURES,
Shengzhou Liu793ea942015-04-24 16:57:17 +0800363 .config = &rtl8211f_config,
Shengzhou Liu3d6af742015-03-12 18:54:59 +0800364 .startup = &rtl8211f_startup,
365 .shutdown = &genphy_shutdown,
Carlo Caionee57c9fd2019-01-16 11:34:50 +0000366 .readext = &rtl8211f_phy_extread,
367 .writeext = &rtl8211f_phy_extwrite,
Shengzhou Liu3d6af742015-03-12 18:54:59 +0800368};
369
Andy Fleming9082eea2011-04-07 21:56:05 -0500370int phy_realtek_init(void)
371{
372 phy_register(&RTL8211B_driver);
Bhupesh Sharmac624d162013-07-18 13:58:20 +0530373 phy_register(&RTL8211E_driver);
Shengzhou Liu3d6af742015-03-12 18:54:59 +0800374 phy_register(&RTL8211F_driver);
Bhupesh Sharmac624d162013-07-18 13:58:20 +0530375 phy_register(&RTL8211DN_driver);
Andy Fleming9082eea2011-04-07 21:56:05 -0500376
377 return 0;
378}