19082eeacSAndy Fleming /*
29082eeacSAndy Fleming * RealTek PHY drivers
39082eeacSAndy Fleming *
41a459660SWolfgang Denk * SPDX-License-Identifier: GPL-2.0+
59082eeacSAndy Fleming *
63cee1388SCodrin Ciubotariu * Copyright 2010-2011, 2015 Freescale Semiconductor, Inc.
79082eeacSAndy Fleming * author Andy Fleming
8563d8d93SKarsten Merker * Copyright 2016 Karsten Merker <merker@debian.org>
99082eeacSAndy Fleming */
109082eeacSAndy Fleming #include <config.h>
119082eeacSAndy Fleming #include <common.h>
12020f6762Soliver@schinagl.nl #include <linux/bitops.h>
139082eeacSAndy Fleming #include <phy.h>
149082eeacSAndy Fleming
15cebf3f55Soliver@schinagl.nl #define PHY_RTL8211x_FORCE_MASTER BIT(1)
16cebf3f55Soliver@schinagl.nl
179082eeacSAndy Fleming #define PHY_AUTONEGOTIATE_TIMEOUT 5000
189082eeacSAndy Fleming
19525d187aSMichael Haas /* RTL8211x 1000BASE-T Control Register */
20020f6762Soliver@schinagl.nl #define MIIM_RTL8211x_CTRL1000T_MSCE BIT(12);
21cbe40e11Soliver@schinagl.nl #define MIIM_RTL8211x_CTRL1000T_MASTER BIT(11);
22525d187aSMichael Haas
23c624d168SBhupesh Sharma /* RTL8211x PHY Status Register */
24c624d168SBhupesh Sharma #define MIIM_RTL8211x_PHY_STATUS 0x11
25c624d168SBhupesh Sharma #define MIIM_RTL8211x_PHYSTAT_SPEED 0xc000
26c624d168SBhupesh Sharma #define MIIM_RTL8211x_PHYSTAT_GBIT 0x8000
27c624d168SBhupesh Sharma #define MIIM_RTL8211x_PHYSTAT_100 0x4000
28c624d168SBhupesh Sharma #define MIIM_RTL8211x_PHYSTAT_DUPLEX 0x2000
29c624d168SBhupesh Sharma #define MIIM_RTL8211x_PHYSTAT_SPDDONE 0x0800
30c624d168SBhupesh Sharma #define MIIM_RTL8211x_PHYSTAT_LINK 0x0400
319082eeacSAndy Fleming
323cee1388SCodrin Ciubotariu /* RTL8211x PHY Interrupt Enable Register */
333cee1388SCodrin Ciubotariu #define MIIM_RTL8211x_PHY_INER 0x12
343cee1388SCodrin Ciubotariu #define MIIM_RTL8211x_PHY_INTR_ENA 0x9f01
353cee1388SCodrin Ciubotariu #define MIIM_RTL8211x_PHY_INTR_DIS 0x0000
363cee1388SCodrin Ciubotariu
373cee1388SCodrin Ciubotariu /* RTL8211x PHY Interrupt Status Register */
383cee1388SCodrin Ciubotariu #define MIIM_RTL8211x_PHY_INSR 0x13
399082eeacSAndy Fleming
403d6af748SShengzhou Liu /* RTL8211F PHY Status Register */
413d6af748SShengzhou Liu #define MIIM_RTL8211F_PHY_STATUS 0x1a
423d6af748SShengzhou Liu #define MIIM_RTL8211F_AUTONEG_ENABLE 0x1000
433d6af748SShengzhou Liu #define MIIM_RTL8211F_PHYSTAT_SPEED 0x0030
443d6af748SShengzhou Liu #define MIIM_RTL8211F_PHYSTAT_GBIT 0x0020
453d6af748SShengzhou Liu #define MIIM_RTL8211F_PHYSTAT_100 0x0010
463d6af748SShengzhou Liu #define MIIM_RTL8211F_PHYSTAT_DUPLEX 0x0008
473d6af748SShengzhou Liu #define MIIM_RTL8211F_PHYSTAT_SPDDONE 0x0800
483d6af748SShengzhou Liu #define MIIM_RTL8211F_PHYSTAT_LINK 0x0004
493d6af748SShengzhou Liu
503d6af748SShengzhou Liu #define MIIM_RTL8211F_PAGE_SELECT 0x1f
51793ea947SShengzhou Liu #define MIIM_RTL8211F_TX_DELAY 0x100
5290712741SShengzhou Liu #define MIIM_RTL8211F_LCR 0x10
533d6af748SShengzhou Liu
rtl8211b_probe(struct phy_device * phydev)54cebf3f55Soliver@schinagl.nl static int rtl8211b_probe(struct phy_device *phydev)
55cebf3f55Soliver@schinagl.nl {
56cebf3f55Soliver@schinagl.nl #ifdef CONFIG_RTL8211X_PHY_FORCE_MASTER
57cebf3f55Soliver@schinagl.nl phydev->flags |= PHY_RTL8211x_FORCE_MASTER;
58cebf3f55Soliver@schinagl.nl #endif
59cebf3f55Soliver@schinagl.nl
60cebf3f55Soliver@schinagl.nl return 0;
61cebf3f55Soliver@schinagl.nl }
62cebf3f55Soliver@schinagl.nl
63c624d168SBhupesh Sharma /* RealTek RTL8211x */
rtl8211x_config(struct phy_device * phydev)64c624d168SBhupesh Sharma static int rtl8211x_config(struct phy_device *phydev)
659082eeacSAndy Fleming {
669082eeacSAndy Fleming phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, BMCR_RESET);
679082eeacSAndy Fleming
683cee1388SCodrin Ciubotariu /* mask interrupt at init; if the interrupt is
693cee1388SCodrin Ciubotariu * needed indeed, it should be explicitly enabled
703cee1388SCodrin Ciubotariu */
713cee1388SCodrin Ciubotariu phy_write(phydev, MDIO_DEVAD_NONE, MIIM_RTL8211x_PHY_INER,
723cee1388SCodrin Ciubotariu MIIM_RTL8211x_PHY_INTR_DIS);
73cebf3f55Soliver@schinagl.nl
74cebf3f55Soliver@schinagl.nl if (phydev->flags & PHY_RTL8211x_FORCE_MASTER) {
75cebf3f55Soliver@schinagl.nl unsigned int reg;
76cebf3f55Soliver@schinagl.nl
77cebf3f55Soliver@schinagl.nl reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_CTRL1000);
78525d187aSMichael Haas /* force manual master/slave configuration */
79525d187aSMichael Haas reg |= MIIM_RTL8211x_CTRL1000T_MSCE;
80525d187aSMichael Haas /* force master mode */
81cbe40e11Soliver@schinagl.nl reg |= MIIM_RTL8211x_CTRL1000T_MASTER;
82525d187aSMichael Haas phy_write(phydev, MDIO_DEVAD_NONE, MII_CTRL1000, reg);
83cebf3f55Soliver@schinagl.nl }
843cee1388SCodrin Ciubotariu /* read interrupt status just to clear it */
853cee1388SCodrin Ciubotariu phy_read(phydev, MDIO_DEVAD_NONE, MIIM_RTL8211x_PHY_INER);
863cee1388SCodrin Ciubotariu
879082eeacSAndy Fleming genphy_config_aneg(phydev);
889082eeacSAndy Fleming
899082eeacSAndy Fleming return 0;
909082eeacSAndy Fleming }
919082eeacSAndy Fleming
rtl8211f_config(struct phy_device * phydev)92793ea947SShengzhou Liu static int rtl8211f_config(struct phy_device *phydev)
93793ea947SShengzhou Liu {
94793ea947SShengzhou Liu u16 reg;
95793ea947SShengzhou Liu
96793ea947SShengzhou Liu phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, BMCR_RESET);
97793ea947SShengzhou Liu
98793ea947SShengzhou Liu phy_write(phydev, MDIO_DEVAD_NONE,
99793ea947SShengzhou Liu MIIM_RTL8211F_PAGE_SELECT, 0xd08);
100793ea947SShengzhou Liu reg = phy_read(phydev, MDIO_DEVAD_NONE, 0x11);
101*05b29aa0SMadalin Bucur
102*05b29aa0SMadalin Bucur /* enable TX-delay for rgmii-id and rgmii-txid, otherwise disable it */
103*05b29aa0SMadalin Bucur if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
104*05b29aa0SMadalin Bucur phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)
105793ea947SShengzhou Liu reg |= MIIM_RTL8211F_TX_DELAY;
106*05b29aa0SMadalin Bucur else
107*05b29aa0SMadalin Bucur reg &= ~MIIM_RTL8211F_TX_DELAY;
108*05b29aa0SMadalin Bucur
109793ea947SShengzhou Liu phy_write(phydev, MDIO_DEVAD_NONE, 0x11, reg);
110793ea947SShengzhou Liu /* restore to default page 0 */
111793ea947SShengzhou Liu phy_write(phydev, MDIO_DEVAD_NONE,
112793ea947SShengzhou Liu MIIM_RTL8211F_PAGE_SELECT, 0x0);
113793ea947SShengzhou Liu
11490712741SShengzhou Liu /* Set green LED for Link, yellow LED for Active */
11590712741SShengzhou Liu phy_write(phydev, MDIO_DEVAD_NONE,
11690712741SShengzhou Liu MIIM_RTL8211F_PAGE_SELECT, 0xd04);
11790712741SShengzhou Liu phy_write(phydev, MDIO_DEVAD_NONE, 0x10, 0x617f);
11890712741SShengzhou Liu phy_write(phydev, MDIO_DEVAD_NONE,
11990712741SShengzhou Liu MIIM_RTL8211F_PAGE_SELECT, 0x0);
12090712741SShengzhou Liu
121793ea947SShengzhou Liu genphy_config_aneg(phydev);
122793ea947SShengzhou Liu
123793ea947SShengzhou Liu return 0;
124793ea947SShengzhou Liu }
125793ea947SShengzhou Liu
rtl8211x_parse_status(struct phy_device * phydev)126c624d168SBhupesh Sharma static int rtl8211x_parse_status(struct phy_device *phydev)
1279082eeacSAndy Fleming {
1289082eeacSAndy Fleming unsigned int speed;
1299082eeacSAndy Fleming unsigned int mii_reg;
1309082eeacSAndy Fleming
131c624d168SBhupesh Sharma mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_RTL8211x_PHY_STATUS);
1329082eeacSAndy Fleming
133c624d168SBhupesh Sharma if (!(mii_reg & MIIM_RTL8211x_PHYSTAT_SPDDONE)) {
1349082eeacSAndy Fleming int i = 0;
1359082eeacSAndy Fleming
1369082eeacSAndy Fleming /* in case of timeout ->link is cleared */
1379082eeacSAndy Fleming phydev->link = 1;
1389082eeacSAndy Fleming puts("Waiting for PHY realtime link");
139c624d168SBhupesh Sharma while (!(mii_reg & MIIM_RTL8211x_PHYSTAT_SPDDONE)) {
1409082eeacSAndy Fleming /* Timeout reached ? */
1419082eeacSAndy Fleming if (i > PHY_AUTONEGOTIATE_TIMEOUT) {
1429082eeacSAndy Fleming puts(" TIMEOUT !\n");
1439082eeacSAndy Fleming phydev->link = 0;
1449082eeacSAndy Fleming break;
1459082eeacSAndy Fleming }
1469082eeacSAndy Fleming
1479082eeacSAndy Fleming if ((i++ % 1000) == 0)
1489082eeacSAndy Fleming putc('.');
1499082eeacSAndy Fleming udelay(1000); /* 1 ms */
1509082eeacSAndy Fleming mii_reg = phy_read(phydev, MDIO_DEVAD_NONE,
151c624d168SBhupesh Sharma MIIM_RTL8211x_PHY_STATUS);
1529082eeacSAndy Fleming }
1539082eeacSAndy Fleming puts(" done\n");
1549082eeacSAndy Fleming udelay(500000); /* another 500 ms (results in faster booting) */
1559082eeacSAndy Fleming } else {
156c624d168SBhupesh Sharma if (mii_reg & MIIM_RTL8211x_PHYSTAT_LINK)
1579082eeacSAndy Fleming phydev->link = 1;
1589082eeacSAndy Fleming else
1599082eeacSAndy Fleming phydev->link = 0;
1609082eeacSAndy Fleming }
1619082eeacSAndy Fleming
162c624d168SBhupesh Sharma if (mii_reg & MIIM_RTL8211x_PHYSTAT_DUPLEX)
1639082eeacSAndy Fleming phydev->duplex = DUPLEX_FULL;
1649082eeacSAndy Fleming else
1659082eeacSAndy Fleming phydev->duplex = DUPLEX_HALF;
1669082eeacSAndy Fleming
167c624d168SBhupesh Sharma speed = (mii_reg & MIIM_RTL8211x_PHYSTAT_SPEED);
1689082eeacSAndy Fleming
1699082eeacSAndy Fleming switch (speed) {
170c624d168SBhupesh Sharma case MIIM_RTL8211x_PHYSTAT_GBIT:
1719082eeacSAndy Fleming phydev->speed = SPEED_1000;
1729082eeacSAndy Fleming break;
173c624d168SBhupesh Sharma case MIIM_RTL8211x_PHYSTAT_100:
1749082eeacSAndy Fleming phydev->speed = SPEED_100;
1759082eeacSAndy Fleming break;
1769082eeacSAndy Fleming default:
1779082eeacSAndy Fleming phydev->speed = SPEED_10;
1789082eeacSAndy Fleming }
1799082eeacSAndy Fleming
1809082eeacSAndy Fleming return 0;
1819082eeacSAndy Fleming }
1829082eeacSAndy Fleming
rtl8211f_parse_status(struct phy_device * phydev)1833d6af748SShengzhou Liu static int rtl8211f_parse_status(struct phy_device *phydev)
1843d6af748SShengzhou Liu {
1853d6af748SShengzhou Liu unsigned int speed;
1863d6af748SShengzhou Liu unsigned int mii_reg;
1873d6af748SShengzhou Liu int i = 0;
1883d6af748SShengzhou Liu
1893d6af748SShengzhou Liu phy_write(phydev, MDIO_DEVAD_NONE, MIIM_RTL8211F_PAGE_SELECT, 0xa43);
1903d6af748SShengzhou Liu mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_RTL8211F_PHY_STATUS);
1913d6af748SShengzhou Liu
1923d6af748SShengzhou Liu phydev->link = 1;
1933d6af748SShengzhou Liu while (!(mii_reg & MIIM_RTL8211F_PHYSTAT_LINK)) {
1943d6af748SShengzhou Liu if (i > PHY_AUTONEGOTIATE_TIMEOUT) {
1953d6af748SShengzhou Liu puts(" TIMEOUT !\n");
1963d6af748SShengzhou Liu phydev->link = 0;
1973d6af748SShengzhou Liu break;
1983d6af748SShengzhou Liu }
1993d6af748SShengzhou Liu
2003d6af748SShengzhou Liu if ((i++ % 1000) == 0)
2013d6af748SShengzhou Liu putc('.');
2023d6af748SShengzhou Liu udelay(1000);
2033d6af748SShengzhou Liu mii_reg = phy_read(phydev, MDIO_DEVAD_NONE,
2043d6af748SShengzhou Liu MIIM_RTL8211F_PHY_STATUS);
2053d6af748SShengzhou Liu }
2063d6af748SShengzhou Liu
2073d6af748SShengzhou Liu if (mii_reg & MIIM_RTL8211F_PHYSTAT_DUPLEX)
2083d6af748SShengzhou Liu phydev->duplex = DUPLEX_FULL;
2093d6af748SShengzhou Liu else
2103d6af748SShengzhou Liu phydev->duplex = DUPLEX_HALF;
2113d6af748SShengzhou Liu
2123d6af748SShengzhou Liu speed = (mii_reg & MIIM_RTL8211F_PHYSTAT_SPEED);
2133d6af748SShengzhou Liu
2143d6af748SShengzhou Liu switch (speed) {
2153d6af748SShengzhou Liu case MIIM_RTL8211F_PHYSTAT_GBIT:
2163d6af748SShengzhou Liu phydev->speed = SPEED_1000;
2173d6af748SShengzhou Liu break;
2183d6af748SShengzhou Liu case MIIM_RTL8211F_PHYSTAT_100:
2193d6af748SShengzhou Liu phydev->speed = SPEED_100;
2203d6af748SShengzhou Liu break;
2213d6af748SShengzhou Liu default:
2223d6af748SShengzhou Liu phydev->speed = SPEED_10;
2233d6af748SShengzhou Liu }
2243d6af748SShengzhou Liu
2253d6af748SShengzhou Liu return 0;
2263d6af748SShengzhou Liu }
2273d6af748SShengzhou Liu
rtl8211x_startup(struct phy_device * phydev)228c624d168SBhupesh Sharma static int rtl8211x_startup(struct phy_device *phydev)
2299082eeacSAndy Fleming {
230b733c278SMichal Simek int ret;
2319082eeacSAndy Fleming
232b733c278SMichal Simek /* Read the Status (2x to make sure link is right) */
233b733c278SMichal Simek ret = genphy_update_link(phydev);
234b733c278SMichal Simek if (ret)
235b733c278SMichal Simek return ret;
236b733c278SMichal Simek
237b733c278SMichal Simek return rtl8211x_parse_status(phydev);
2389082eeacSAndy Fleming }
2399082eeacSAndy Fleming
rtl8211e_startup(struct phy_device * phydev)2406a10bc5bSMichal Simek static int rtl8211e_startup(struct phy_device *phydev)
2416a10bc5bSMichal Simek {
242b733c278SMichal Simek int ret;
2436a10bc5bSMichal Simek
244b733c278SMichal Simek ret = genphy_update_link(phydev);
245b733c278SMichal Simek if (ret)
246b733c278SMichal Simek return ret;
247b733c278SMichal Simek
248b733c278SMichal Simek return genphy_parse_link(phydev);
2496a10bc5bSMichal Simek }
2506a10bc5bSMichal Simek
rtl8211f_startup(struct phy_device * phydev)2513d6af748SShengzhou Liu static int rtl8211f_startup(struct phy_device *phydev)
2523d6af748SShengzhou Liu {
253b733c278SMichal Simek int ret;
2543d6af748SShengzhou Liu
255b733c278SMichal Simek /* Read the Status (2x to make sure link is right) */
256b733c278SMichal Simek ret = genphy_update_link(phydev);
257b733c278SMichal Simek if (ret)
258b733c278SMichal Simek return ret;
259b733c278SMichal Simek /* Read the Status (2x to make sure link is right) */
260b733c278SMichal Simek
261b733c278SMichal Simek return rtl8211f_parse_status(phydev);
2623d6af748SShengzhou Liu }
2633d6af748SShengzhou Liu
264c624d168SBhupesh Sharma /* Support for RTL8211B PHY */
2659082eeacSAndy Fleming static struct phy_driver RTL8211B_driver = {
2669082eeacSAndy Fleming .name = "RealTek RTL8211B",
267563d8d93SKarsten Merker .uid = 0x1cc912,
26842205047SBhupesh Sharma .mask = 0xffffff,
2699082eeacSAndy Fleming .features = PHY_GBIT_FEATURES,
270cebf3f55Soliver@schinagl.nl .probe = &rtl8211b_probe,
271c624d168SBhupesh Sharma .config = &rtl8211x_config,
272c624d168SBhupesh Sharma .startup = &rtl8211x_startup,
273c624d168SBhupesh Sharma .shutdown = &genphy_shutdown,
274c624d168SBhupesh Sharma };
275c624d168SBhupesh Sharma
276c624d168SBhupesh Sharma /* Support for RTL8211E-VB-CG, RTL8211E-VL-CG and RTL8211EG-VB-CG PHYs */
277c624d168SBhupesh Sharma static struct phy_driver RTL8211E_driver = {
278c624d168SBhupesh Sharma .name = "RealTek RTL8211E",
279c624d168SBhupesh Sharma .uid = 0x1cc915,
28042205047SBhupesh Sharma .mask = 0xffffff,
281c624d168SBhupesh Sharma .features = PHY_GBIT_FEATURES,
282c624d168SBhupesh Sharma .config = &rtl8211x_config,
2836a10bc5bSMichal Simek .startup = &rtl8211e_startup,
284c624d168SBhupesh Sharma .shutdown = &genphy_shutdown,
285c624d168SBhupesh Sharma };
286c624d168SBhupesh Sharma
287c624d168SBhupesh Sharma /* Support for RTL8211DN PHY */
288c624d168SBhupesh Sharma static struct phy_driver RTL8211DN_driver = {
289c624d168SBhupesh Sharma .name = "RealTek RTL8211DN",
290c624d168SBhupesh Sharma .uid = 0x1cc914,
29142205047SBhupesh Sharma .mask = 0xffffff,
292c624d168SBhupesh Sharma .features = PHY_GBIT_FEATURES,
293c624d168SBhupesh Sharma .config = &rtl8211x_config,
294c624d168SBhupesh Sharma .startup = &rtl8211x_startup,
2959082eeacSAndy Fleming .shutdown = &genphy_shutdown,
2969082eeacSAndy Fleming };
2979082eeacSAndy Fleming
2983d6af748SShengzhou Liu /* Support for RTL8211F PHY */
2993d6af748SShengzhou Liu static struct phy_driver RTL8211F_driver = {
3003d6af748SShengzhou Liu .name = "RealTek RTL8211F",
3013d6af748SShengzhou Liu .uid = 0x1cc916,
3023d6af748SShengzhou Liu .mask = 0xffffff,
3033d6af748SShengzhou Liu .features = PHY_GBIT_FEATURES,
304793ea947SShengzhou Liu .config = &rtl8211f_config,
3053d6af748SShengzhou Liu .startup = &rtl8211f_startup,
3063d6af748SShengzhou Liu .shutdown = &genphy_shutdown,
3073d6af748SShengzhou Liu };
3083d6af748SShengzhou Liu
phy_realtek_init(void)3099082eeacSAndy Fleming int phy_realtek_init(void)
3109082eeacSAndy Fleming {
3119082eeacSAndy Fleming phy_register(&RTL8211B_driver);
312c624d168SBhupesh Sharma phy_register(&RTL8211E_driver);
3133d6af748SShengzhou Liu phy_register(&RTL8211F_driver);
314c624d168SBhupesh Sharma phy_register(&RTL8211DN_driver);
3159082eeacSAndy Fleming
3169082eeacSAndy Fleming return 0;
3179082eeacSAndy Fleming }
318