xref: /rk3399_rockchip-uboot/drivers/net/phy/realtek.c (revision cebf3f558ee8b07f1a2f803fdeb9051603a308ac)
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 
15*cebf3f55Soliver@schinagl.nl #define PHY_RTL8211x_FORCE_MASTER BIT(1)
16*cebf3f55Soliver@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 
54*cebf3f55Soliver@schinagl.nl static int rtl8211b_probe(struct phy_device *phydev)
55*cebf3f55Soliver@schinagl.nl {
56*cebf3f55Soliver@schinagl.nl #ifdef CONFIG_RTL8211X_PHY_FORCE_MASTER
57*cebf3f55Soliver@schinagl.nl 	phydev->flags |= PHY_RTL8211x_FORCE_MASTER;
58*cebf3f55Soliver@schinagl.nl #endif
59*cebf3f55Soliver@schinagl.nl 
60*cebf3f55Soliver@schinagl.nl 	return 0;
61*cebf3f55Soliver@schinagl.nl }
62*cebf3f55Soliver@schinagl.nl 
63c624d168SBhupesh Sharma /* RealTek RTL8211x */
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);
73*cebf3f55Soliver@schinagl.nl 
74*cebf3f55Soliver@schinagl.nl 	if (phydev->flags & PHY_RTL8211x_FORCE_MASTER) {
75*cebf3f55Soliver@schinagl.nl 		unsigned int reg;
76*cebf3f55Soliver@schinagl.nl 
77*cebf3f55Soliver@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);
83*cebf3f55Soliver@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 
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 	if (phydev->interface == PHY_INTERFACE_MODE_RGMII) {
99793ea947SShengzhou Liu 		/* enable TXDLY */
100793ea947SShengzhou Liu 		phy_write(phydev, MDIO_DEVAD_NONE,
101793ea947SShengzhou Liu 			  MIIM_RTL8211F_PAGE_SELECT, 0xd08);
102793ea947SShengzhou Liu 		reg = phy_read(phydev, MDIO_DEVAD_NONE, 0x11);
103793ea947SShengzhou Liu 		reg |= MIIM_RTL8211F_TX_DELAY;
104793ea947SShengzhou Liu 		phy_write(phydev, MDIO_DEVAD_NONE, 0x11, reg);
105793ea947SShengzhou Liu 		/* restore to default page 0 */
106793ea947SShengzhou Liu 		phy_write(phydev, MDIO_DEVAD_NONE,
107793ea947SShengzhou Liu 			  MIIM_RTL8211F_PAGE_SELECT, 0x0);
108793ea947SShengzhou Liu 	}
109793ea947SShengzhou Liu 
11090712741SShengzhou Liu 	/* Set green LED for Link, yellow LED for Active */
11190712741SShengzhou Liu 	phy_write(phydev, MDIO_DEVAD_NONE,
11290712741SShengzhou Liu 		  MIIM_RTL8211F_PAGE_SELECT, 0xd04);
11390712741SShengzhou Liu 	phy_write(phydev, MDIO_DEVAD_NONE, 0x10, 0x617f);
11490712741SShengzhou Liu 	phy_write(phydev, MDIO_DEVAD_NONE,
11590712741SShengzhou Liu 		  MIIM_RTL8211F_PAGE_SELECT, 0x0);
11690712741SShengzhou Liu 
117793ea947SShengzhou Liu 	genphy_config_aneg(phydev);
118793ea947SShengzhou Liu 
119793ea947SShengzhou Liu 	return 0;
120793ea947SShengzhou Liu }
121793ea947SShengzhou Liu 
122c624d168SBhupesh Sharma static int rtl8211x_parse_status(struct phy_device *phydev)
1239082eeacSAndy Fleming {
1249082eeacSAndy Fleming 	unsigned int speed;
1259082eeacSAndy Fleming 	unsigned int mii_reg;
1269082eeacSAndy Fleming 
127c624d168SBhupesh Sharma 	mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_RTL8211x_PHY_STATUS);
1289082eeacSAndy Fleming 
129c624d168SBhupesh Sharma 	if (!(mii_reg & MIIM_RTL8211x_PHYSTAT_SPDDONE)) {
1309082eeacSAndy Fleming 		int i = 0;
1319082eeacSAndy Fleming 
1329082eeacSAndy Fleming 		/* in case of timeout ->link is cleared */
1339082eeacSAndy Fleming 		phydev->link = 1;
1349082eeacSAndy Fleming 		puts("Waiting for PHY realtime link");
135c624d168SBhupesh Sharma 		while (!(mii_reg & MIIM_RTL8211x_PHYSTAT_SPDDONE)) {
1369082eeacSAndy Fleming 			/* Timeout reached ? */
1379082eeacSAndy Fleming 			if (i > PHY_AUTONEGOTIATE_TIMEOUT) {
1389082eeacSAndy Fleming 				puts(" TIMEOUT !\n");
1399082eeacSAndy Fleming 				phydev->link = 0;
1409082eeacSAndy Fleming 				break;
1419082eeacSAndy Fleming 			}
1429082eeacSAndy Fleming 
1439082eeacSAndy Fleming 			if ((i++ % 1000) == 0)
1449082eeacSAndy Fleming 				putc('.');
1459082eeacSAndy Fleming 			udelay(1000);	/* 1 ms */
1469082eeacSAndy Fleming 			mii_reg = phy_read(phydev, MDIO_DEVAD_NONE,
147c624d168SBhupesh Sharma 					MIIM_RTL8211x_PHY_STATUS);
1489082eeacSAndy Fleming 		}
1499082eeacSAndy Fleming 		puts(" done\n");
1509082eeacSAndy Fleming 		udelay(500000);	/* another 500 ms (results in faster booting) */
1519082eeacSAndy Fleming 	} else {
152c624d168SBhupesh Sharma 		if (mii_reg & MIIM_RTL8211x_PHYSTAT_LINK)
1539082eeacSAndy Fleming 			phydev->link = 1;
1549082eeacSAndy Fleming 		else
1559082eeacSAndy Fleming 			phydev->link = 0;
1569082eeacSAndy Fleming 	}
1579082eeacSAndy Fleming 
158c624d168SBhupesh Sharma 	if (mii_reg & MIIM_RTL8211x_PHYSTAT_DUPLEX)
1599082eeacSAndy Fleming 		phydev->duplex = DUPLEX_FULL;
1609082eeacSAndy Fleming 	else
1619082eeacSAndy Fleming 		phydev->duplex = DUPLEX_HALF;
1629082eeacSAndy Fleming 
163c624d168SBhupesh Sharma 	speed = (mii_reg & MIIM_RTL8211x_PHYSTAT_SPEED);
1649082eeacSAndy Fleming 
1659082eeacSAndy Fleming 	switch (speed) {
166c624d168SBhupesh Sharma 	case MIIM_RTL8211x_PHYSTAT_GBIT:
1679082eeacSAndy Fleming 		phydev->speed = SPEED_1000;
1689082eeacSAndy Fleming 		break;
169c624d168SBhupesh Sharma 	case MIIM_RTL8211x_PHYSTAT_100:
1709082eeacSAndy Fleming 		phydev->speed = SPEED_100;
1719082eeacSAndy Fleming 		break;
1729082eeacSAndy Fleming 	default:
1739082eeacSAndy Fleming 		phydev->speed = SPEED_10;
1749082eeacSAndy Fleming 	}
1759082eeacSAndy Fleming 
1769082eeacSAndy Fleming 	return 0;
1779082eeacSAndy Fleming }
1789082eeacSAndy Fleming 
1793d6af748SShengzhou Liu static int rtl8211f_parse_status(struct phy_device *phydev)
1803d6af748SShengzhou Liu {
1813d6af748SShengzhou Liu 	unsigned int speed;
1823d6af748SShengzhou Liu 	unsigned int mii_reg;
1833d6af748SShengzhou Liu 	int i = 0;
1843d6af748SShengzhou Liu 
1853d6af748SShengzhou Liu 	phy_write(phydev, MDIO_DEVAD_NONE, MIIM_RTL8211F_PAGE_SELECT, 0xa43);
1863d6af748SShengzhou Liu 	mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_RTL8211F_PHY_STATUS);
1873d6af748SShengzhou Liu 
1883d6af748SShengzhou Liu 	phydev->link = 1;
1893d6af748SShengzhou Liu 	while (!(mii_reg & MIIM_RTL8211F_PHYSTAT_LINK)) {
1903d6af748SShengzhou Liu 		if (i > PHY_AUTONEGOTIATE_TIMEOUT) {
1913d6af748SShengzhou Liu 			puts(" TIMEOUT !\n");
1923d6af748SShengzhou Liu 			phydev->link = 0;
1933d6af748SShengzhou Liu 			break;
1943d6af748SShengzhou Liu 		}
1953d6af748SShengzhou Liu 
1963d6af748SShengzhou Liu 		if ((i++ % 1000) == 0)
1973d6af748SShengzhou Liu 			putc('.');
1983d6af748SShengzhou Liu 		udelay(1000);
1993d6af748SShengzhou Liu 		mii_reg = phy_read(phydev, MDIO_DEVAD_NONE,
2003d6af748SShengzhou Liu 				   MIIM_RTL8211F_PHY_STATUS);
2013d6af748SShengzhou Liu 	}
2023d6af748SShengzhou Liu 
2033d6af748SShengzhou Liu 	if (mii_reg & MIIM_RTL8211F_PHYSTAT_DUPLEX)
2043d6af748SShengzhou Liu 		phydev->duplex = DUPLEX_FULL;
2053d6af748SShengzhou Liu 	else
2063d6af748SShengzhou Liu 		phydev->duplex = DUPLEX_HALF;
2073d6af748SShengzhou Liu 
2083d6af748SShengzhou Liu 	speed = (mii_reg & MIIM_RTL8211F_PHYSTAT_SPEED);
2093d6af748SShengzhou Liu 
2103d6af748SShengzhou Liu 	switch (speed) {
2113d6af748SShengzhou Liu 	case MIIM_RTL8211F_PHYSTAT_GBIT:
2123d6af748SShengzhou Liu 		phydev->speed = SPEED_1000;
2133d6af748SShengzhou Liu 		break;
2143d6af748SShengzhou Liu 	case MIIM_RTL8211F_PHYSTAT_100:
2153d6af748SShengzhou Liu 		phydev->speed = SPEED_100;
2163d6af748SShengzhou Liu 		break;
2173d6af748SShengzhou Liu 	default:
2183d6af748SShengzhou Liu 		phydev->speed = SPEED_10;
2193d6af748SShengzhou Liu 	}
2203d6af748SShengzhou Liu 
2213d6af748SShengzhou Liu 	return 0;
2223d6af748SShengzhou Liu }
2233d6af748SShengzhou Liu 
224c624d168SBhupesh Sharma static int rtl8211x_startup(struct phy_device *phydev)
2259082eeacSAndy Fleming {
226b733c278SMichal Simek 	int ret;
2279082eeacSAndy Fleming 
228b733c278SMichal Simek 	/* Read the Status (2x to make sure link is right) */
229b733c278SMichal Simek 	ret = genphy_update_link(phydev);
230b733c278SMichal Simek 	if (ret)
231b733c278SMichal Simek 		return ret;
232b733c278SMichal Simek 
233b733c278SMichal Simek 	return rtl8211x_parse_status(phydev);
2349082eeacSAndy Fleming }
2359082eeacSAndy Fleming 
2366a10bc5bSMichal Simek static int rtl8211e_startup(struct phy_device *phydev)
2376a10bc5bSMichal Simek {
238b733c278SMichal Simek 	int ret;
2396a10bc5bSMichal Simek 
240b733c278SMichal Simek 	ret = genphy_update_link(phydev);
241b733c278SMichal Simek 	if (ret)
242b733c278SMichal Simek 		return ret;
243b733c278SMichal Simek 
244b733c278SMichal Simek 	return genphy_parse_link(phydev);
2456a10bc5bSMichal Simek }
2466a10bc5bSMichal Simek 
2473d6af748SShengzhou Liu static int rtl8211f_startup(struct phy_device *phydev)
2483d6af748SShengzhou Liu {
249b733c278SMichal Simek 	int ret;
2503d6af748SShengzhou Liu 
251b733c278SMichal Simek 	/* Read the Status (2x to make sure link is right) */
252b733c278SMichal Simek 	ret = genphy_update_link(phydev);
253b733c278SMichal Simek 	if (ret)
254b733c278SMichal Simek 		return ret;
255b733c278SMichal Simek 	/* Read the Status (2x to make sure link is right) */
256b733c278SMichal Simek 
257b733c278SMichal Simek 	return rtl8211f_parse_status(phydev);
2583d6af748SShengzhou Liu }
2593d6af748SShengzhou Liu 
260c624d168SBhupesh Sharma /* Support for RTL8211B PHY */
2619082eeacSAndy Fleming static struct phy_driver RTL8211B_driver = {
2629082eeacSAndy Fleming 	.name = "RealTek RTL8211B",
263563d8d93SKarsten Merker 	.uid = 0x1cc912,
26442205047SBhupesh Sharma 	.mask = 0xffffff,
2659082eeacSAndy Fleming 	.features = PHY_GBIT_FEATURES,
266*cebf3f55Soliver@schinagl.nl 	.probe = &rtl8211b_probe,
267c624d168SBhupesh Sharma 	.config = &rtl8211x_config,
268c624d168SBhupesh Sharma 	.startup = &rtl8211x_startup,
269c624d168SBhupesh Sharma 	.shutdown = &genphy_shutdown,
270c624d168SBhupesh Sharma };
271c624d168SBhupesh Sharma 
272c624d168SBhupesh Sharma /* Support for RTL8211E-VB-CG, RTL8211E-VL-CG and RTL8211EG-VB-CG PHYs */
273c624d168SBhupesh Sharma static struct phy_driver RTL8211E_driver = {
274c624d168SBhupesh Sharma 	.name = "RealTek RTL8211E",
275c624d168SBhupesh Sharma 	.uid = 0x1cc915,
27642205047SBhupesh Sharma 	.mask = 0xffffff,
277c624d168SBhupesh Sharma 	.features = PHY_GBIT_FEATURES,
278c624d168SBhupesh Sharma 	.config = &rtl8211x_config,
2796a10bc5bSMichal Simek 	.startup = &rtl8211e_startup,
280c624d168SBhupesh Sharma 	.shutdown = &genphy_shutdown,
281c624d168SBhupesh Sharma };
282c624d168SBhupesh Sharma 
283c624d168SBhupesh Sharma /* Support for RTL8211DN PHY */
284c624d168SBhupesh Sharma static struct phy_driver RTL8211DN_driver = {
285c624d168SBhupesh Sharma 	.name = "RealTek RTL8211DN",
286c624d168SBhupesh Sharma 	.uid = 0x1cc914,
28742205047SBhupesh Sharma 	.mask = 0xffffff,
288c624d168SBhupesh Sharma 	.features = PHY_GBIT_FEATURES,
289c624d168SBhupesh Sharma 	.config = &rtl8211x_config,
290c624d168SBhupesh Sharma 	.startup = &rtl8211x_startup,
2919082eeacSAndy Fleming 	.shutdown = &genphy_shutdown,
2929082eeacSAndy Fleming };
2939082eeacSAndy Fleming 
2943d6af748SShengzhou Liu /* Support for RTL8211F PHY */
2953d6af748SShengzhou Liu static struct phy_driver RTL8211F_driver = {
2963d6af748SShengzhou Liu 	.name = "RealTek RTL8211F",
2973d6af748SShengzhou Liu 	.uid = 0x1cc916,
2983d6af748SShengzhou Liu 	.mask = 0xffffff,
2993d6af748SShengzhou Liu 	.features = PHY_GBIT_FEATURES,
300793ea947SShengzhou Liu 	.config = &rtl8211f_config,
3013d6af748SShengzhou Liu 	.startup = &rtl8211f_startup,
3023d6af748SShengzhou Liu 	.shutdown = &genphy_shutdown,
3033d6af748SShengzhou Liu };
3043d6af748SShengzhou Liu 
3059082eeacSAndy Fleming int phy_realtek_init(void)
3069082eeacSAndy Fleming {
3079082eeacSAndy Fleming 	phy_register(&RTL8211B_driver);
308c624d168SBhupesh Sharma 	phy_register(&RTL8211E_driver);
3093d6af748SShengzhou Liu 	phy_register(&RTL8211F_driver);
310c624d168SBhupesh Sharma 	phy_register(&RTL8211DN_driver);
3119082eeacSAndy Fleming 
3129082eeacSAndy Fleming 	return 0;
3139082eeacSAndy Fleming }
314