xref: /rk3399_rockchip-uboot/drivers/net/phy/realtek.c (revision 020f67628d7f3e8f1c3ca93e8388d6b5ee128ec2)
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>
12*020f6762Soliver@schinagl.nl #include <linux/bitops.h>
139082eeacSAndy Fleming #include <phy.h>
149082eeacSAndy Fleming 
159082eeacSAndy Fleming #define PHY_AUTONEGOTIATE_TIMEOUT 5000
169082eeacSAndy Fleming 
17525d187aSMichael Haas /* RTL8211x 1000BASE-T Control Register */
18*020f6762Soliver@schinagl.nl #define MIIM_RTL8211x_CTRL1000T_MSCE BIT(12);
19*020f6762Soliver@schinagl.nl #define MIIM_RTL8211X_CTRL1000T_MASTER BIT(11);
20525d187aSMichael Haas 
21c624d168SBhupesh Sharma /* RTL8211x PHY Status Register */
22c624d168SBhupesh Sharma #define MIIM_RTL8211x_PHY_STATUS       0x11
23c624d168SBhupesh Sharma #define MIIM_RTL8211x_PHYSTAT_SPEED    0xc000
24c624d168SBhupesh Sharma #define MIIM_RTL8211x_PHYSTAT_GBIT     0x8000
25c624d168SBhupesh Sharma #define MIIM_RTL8211x_PHYSTAT_100      0x4000
26c624d168SBhupesh Sharma #define MIIM_RTL8211x_PHYSTAT_DUPLEX   0x2000
27c624d168SBhupesh Sharma #define MIIM_RTL8211x_PHYSTAT_SPDDONE  0x0800
28c624d168SBhupesh Sharma #define MIIM_RTL8211x_PHYSTAT_LINK     0x0400
299082eeacSAndy Fleming 
303cee1388SCodrin Ciubotariu /* RTL8211x PHY Interrupt Enable Register */
313cee1388SCodrin Ciubotariu #define MIIM_RTL8211x_PHY_INER         0x12
323cee1388SCodrin Ciubotariu #define MIIM_RTL8211x_PHY_INTR_ENA     0x9f01
333cee1388SCodrin Ciubotariu #define MIIM_RTL8211x_PHY_INTR_DIS     0x0000
343cee1388SCodrin Ciubotariu 
353cee1388SCodrin Ciubotariu /* RTL8211x PHY Interrupt Status Register */
363cee1388SCodrin Ciubotariu #define MIIM_RTL8211x_PHY_INSR         0x13
379082eeacSAndy Fleming 
383d6af748SShengzhou Liu /* RTL8211F PHY Status Register */
393d6af748SShengzhou Liu #define MIIM_RTL8211F_PHY_STATUS       0x1a
403d6af748SShengzhou Liu #define MIIM_RTL8211F_AUTONEG_ENABLE   0x1000
413d6af748SShengzhou Liu #define MIIM_RTL8211F_PHYSTAT_SPEED    0x0030
423d6af748SShengzhou Liu #define MIIM_RTL8211F_PHYSTAT_GBIT     0x0020
433d6af748SShengzhou Liu #define MIIM_RTL8211F_PHYSTAT_100      0x0010
443d6af748SShengzhou Liu #define MIIM_RTL8211F_PHYSTAT_DUPLEX   0x0008
453d6af748SShengzhou Liu #define MIIM_RTL8211F_PHYSTAT_SPDDONE  0x0800
463d6af748SShengzhou Liu #define MIIM_RTL8211F_PHYSTAT_LINK     0x0004
473d6af748SShengzhou Liu 
483d6af748SShengzhou Liu #define MIIM_RTL8211F_PAGE_SELECT      0x1f
49793ea947SShengzhou Liu #define MIIM_RTL8211F_TX_DELAY		0x100
5090712741SShengzhou Liu #define MIIM_RTL8211F_LCR		0x10
513d6af748SShengzhou Liu 
52c624d168SBhupesh Sharma /* RealTek RTL8211x */
53c624d168SBhupesh Sharma static int rtl8211x_config(struct phy_device *phydev)
549082eeacSAndy Fleming {
559082eeacSAndy Fleming 	phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, BMCR_RESET);
569082eeacSAndy Fleming 
573cee1388SCodrin Ciubotariu 	/* mask interrupt at init; if the interrupt is
583cee1388SCodrin Ciubotariu 	 * needed indeed, it should be explicitly enabled
593cee1388SCodrin Ciubotariu 	 */
603cee1388SCodrin Ciubotariu 	phy_write(phydev, MDIO_DEVAD_NONE, MIIM_RTL8211x_PHY_INER,
613cee1388SCodrin Ciubotariu 		  MIIM_RTL8211x_PHY_INTR_DIS);
62525d187aSMichael Haas #ifdef CONFIG_RTL8211X_PHY_FORCE_MASTER
63525d187aSMichael Haas 	unsigned int reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_CTRL1000);
64525d187aSMichael Haas 	/* force manual master/slave configuration */
65525d187aSMichael Haas 	reg |= MIIM_RTL8211x_CTRL1000T_MSCE;
66525d187aSMichael Haas 	/* force master mode */
67525d187aSMichael Haas 	reg |= MIIM_RTL8211X_CTRL1000T_MASTER;
68525d187aSMichael Haas 	phy_write(phydev, MDIO_DEVAD_NONE, MII_CTRL1000, reg);
69525d187aSMichael Haas #endif
703cee1388SCodrin Ciubotariu 	/* read interrupt status just to clear it */
713cee1388SCodrin Ciubotariu 	phy_read(phydev, MDIO_DEVAD_NONE, MIIM_RTL8211x_PHY_INER);
723cee1388SCodrin Ciubotariu 
739082eeacSAndy Fleming 	genphy_config_aneg(phydev);
749082eeacSAndy Fleming 
759082eeacSAndy Fleming 	return 0;
769082eeacSAndy Fleming }
779082eeacSAndy Fleming 
78793ea947SShengzhou Liu static int rtl8211f_config(struct phy_device *phydev)
79793ea947SShengzhou Liu {
80793ea947SShengzhou Liu 	u16 reg;
81793ea947SShengzhou Liu 
82793ea947SShengzhou Liu 	phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, BMCR_RESET);
83793ea947SShengzhou Liu 
84793ea947SShengzhou Liu 	if (phydev->interface == PHY_INTERFACE_MODE_RGMII) {
85793ea947SShengzhou Liu 		/* enable TXDLY */
86793ea947SShengzhou Liu 		phy_write(phydev, MDIO_DEVAD_NONE,
87793ea947SShengzhou Liu 			  MIIM_RTL8211F_PAGE_SELECT, 0xd08);
88793ea947SShengzhou Liu 		reg = phy_read(phydev, MDIO_DEVAD_NONE, 0x11);
89793ea947SShengzhou Liu 		reg |= MIIM_RTL8211F_TX_DELAY;
90793ea947SShengzhou Liu 		phy_write(phydev, MDIO_DEVAD_NONE, 0x11, reg);
91793ea947SShengzhou Liu 		/* restore to default page 0 */
92793ea947SShengzhou Liu 		phy_write(phydev, MDIO_DEVAD_NONE,
93793ea947SShengzhou Liu 			  MIIM_RTL8211F_PAGE_SELECT, 0x0);
94793ea947SShengzhou Liu 	}
95793ea947SShengzhou Liu 
9690712741SShengzhou Liu 	/* Set green LED for Link, yellow LED for Active */
9790712741SShengzhou Liu 	phy_write(phydev, MDIO_DEVAD_NONE,
9890712741SShengzhou Liu 		  MIIM_RTL8211F_PAGE_SELECT, 0xd04);
9990712741SShengzhou Liu 	phy_write(phydev, MDIO_DEVAD_NONE, 0x10, 0x617f);
10090712741SShengzhou Liu 	phy_write(phydev, MDIO_DEVAD_NONE,
10190712741SShengzhou Liu 		  MIIM_RTL8211F_PAGE_SELECT, 0x0);
10290712741SShengzhou Liu 
103793ea947SShengzhou Liu 	genphy_config_aneg(phydev);
104793ea947SShengzhou Liu 
105793ea947SShengzhou Liu 	return 0;
106793ea947SShengzhou Liu }
107793ea947SShengzhou Liu 
108c624d168SBhupesh Sharma static int rtl8211x_parse_status(struct phy_device *phydev)
1099082eeacSAndy Fleming {
1109082eeacSAndy Fleming 	unsigned int speed;
1119082eeacSAndy Fleming 	unsigned int mii_reg;
1129082eeacSAndy Fleming 
113c624d168SBhupesh Sharma 	mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_RTL8211x_PHY_STATUS);
1149082eeacSAndy Fleming 
115c624d168SBhupesh Sharma 	if (!(mii_reg & MIIM_RTL8211x_PHYSTAT_SPDDONE)) {
1169082eeacSAndy Fleming 		int i = 0;
1179082eeacSAndy Fleming 
1189082eeacSAndy Fleming 		/* in case of timeout ->link is cleared */
1199082eeacSAndy Fleming 		phydev->link = 1;
1209082eeacSAndy Fleming 		puts("Waiting for PHY realtime link");
121c624d168SBhupesh Sharma 		while (!(mii_reg & MIIM_RTL8211x_PHYSTAT_SPDDONE)) {
1229082eeacSAndy Fleming 			/* Timeout reached ? */
1239082eeacSAndy Fleming 			if (i > PHY_AUTONEGOTIATE_TIMEOUT) {
1249082eeacSAndy Fleming 				puts(" TIMEOUT !\n");
1259082eeacSAndy Fleming 				phydev->link = 0;
1269082eeacSAndy Fleming 				break;
1279082eeacSAndy Fleming 			}
1289082eeacSAndy Fleming 
1299082eeacSAndy Fleming 			if ((i++ % 1000) == 0)
1309082eeacSAndy Fleming 				putc('.');
1319082eeacSAndy Fleming 			udelay(1000);	/* 1 ms */
1329082eeacSAndy Fleming 			mii_reg = phy_read(phydev, MDIO_DEVAD_NONE,
133c624d168SBhupesh Sharma 					MIIM_RTL8211x_PHY_STATUS);
1349082eeacSAndy Fleming 		}
1359082eeacSAndy Fleming 		puts(" done\n");
1369082eeacSAndy Fleming 		udelay(500000);	/* another 500 ms (results in faster booting) */
1379082eeacSAndy Fleming 	} else {
138c624d168SBhupesh Sharma 		if (mii_reg & MIIM_RTL8211x_PHYSTAT_LINK)
1399082eeacSAndy Fleming 			phydev->link = 1;
1409082eeacSAndy Fleming 		else
1419082eeacSAndy Fleming 			phydev->link = 0;
1429082eeacSAndy Fleming 	}
1439082eeacSAndy Fleming 
144c624d168SBhupesh Sharma 	if (mii_reg & MIIM_RTL8211x_PHYSTAT_DUPLEX)
1459082eeacSAndy Fleming 		phydev->duplex = DUPLEX_FULL;
1469082eeacSAndy Fleming 	else
1479082eeacSAndy Fleming 		phydev->duplex = DUPLEX_HALF;
1489082eeacSAndy Fleming 
149c624d168SBhupesh Sharma 	speed = (mii_reg & MIIM_RTL8211x_PHYSTAT_SPEED);
1509082eeacSAndy Fleming 
1519082eeacSAndy Fleming 	switch (speed) {
152c624d168SBhupesh Sharma 	case MIIM_RTL8211x_PHYSTAT_GBIT:
1539082eeacSAndy Fleming 		phydev->speed = SPEED_1000;
1549082eeacSAndy Fleming 		break;
155c624d168SBhupesh Sharma 	case MIIM_RTL8211x_PHYSTAT_100:
1569082eeacSAndy Fleming 		phydev->speed = SPEED_100;
1579082eeacSAndy Fleming 		break;
1589082eeacSAndy Fleming 	default:
1599082eeacSAndy Fleming 		phydev->speed = SPEED_10;
1609082eeacSAndy Fleming 	}
1619082eeacSAndy Fleming 
1629082eeacSAndy Fleming 	return 0;
1639082eeacSAndy Fleming }
1649082eeacSAndy Fleming 
1653d6af748SShengzhou Liu static int rtl8211f_parse_status(struct phy_device *phydev)
1663d6af748SShengzhou Liu {
1673d6af748SShengzhou Liu 	unsigned int speed;
1683d6af748SShengzhou Liu 	unsigned int mii_reg;
1693d6af748SShengzhou Liu 	int i = 0;
1703d6af748SShengzhou Liu 
1713d6af748SShengzhou Liu 	phy_write(phydev, MDIO_DEVAD_NONE, MIIM_RTL8211F_PAGE_SELECT, 0xa43);
1723d6af748SShengzhou Liu 	mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_RTL8211F_PHY_STATUS);
1733d6af748SShengzhou Liu 
1743d6af748SShengzhou Liu 	phydev->link = 1;
1753d6af748SShengzhou Liu 	while (!(mii_reg & MIIM_RTL8211F_PHYSTAT_LINK)) {
1763d6af748SShengzhou Liu 		if (i > PHY_AUTONEGOTIATE_TIMEOUT) {
1773d6af748SShengzhou Liu 			puts(" TIMEOUT !\n");
1783d6af748SShengzhou Liu 			phydev->link = 0;
1793d6af748SShengzhou Liu 			break;
1803d6af748SShengzhou Liu 		}
1813d6af748SShengzhou Liu 
1823d6af748SShengzhou Liu 		if ((i++ % 1000) == 0)
1833d6af748SShengzhou Liu 			putc('.');
1843d6af748SShengzhou Liu 		udelay(1000);
1853d6af748SShengzhou Liu 		mii_reg = phy_read(phydev, MDIO_DEVAD_NONE,
1863d6af748SShengzhou Liu 				   MIIM_RTL8211F_PHY_STATUS);
1873d6af748SShengzhou Liu 	}
1883d6af748SShengzhou Liu 
1893d6af748SShengzhou Liu 	if (mii_reg & MIIM_RTL8211F_PHYSTAT_DUPLEX)
1903d6af748SShengzhou Liu 		phydev->duplex = DUPLEX_FULL;
1913d6af748SShengzhou Liu 	else
1923d6af748SShengzhou Liu 		phydev->duplex = DUPLEX_HALF;
1933d6af748SShengzhou Liu 
1943d6af748SShengzhou Liu 	speed = (mii_reg & MIIM_RTL8211F_PHYSTAT_SPEED);
1953d6af748SShengzhou Liu 
1963d6af748SShengzhou Liu 	switch (speed) {
1973d6af748SShengzhou Liu 	case MIIM_RTL8211F_PHYSTAT_GBIT:
1983d6af748SShengzhou Liu 		phydev->speed = SPEED_1000;
1993d6af748SShengzhou Liu 		break;
2003d6af748SShengzhou Liu 	case MIIM_RTL8211F_PHYSTAT_100:
2013d6af748SShengzhou Liu 		phydev->speed = SPEED_100;
2023d6af748SShengzhou Liu 		break;
2033d6af748SShengzhou Liu 	default:
2043d6af748SShengzhou Liu 		phydev->speed = SPEED_10;
2053d6af748SShengzhou Liu 	}
2063d6af748SShengzhou Liu 
2073d6af748SShengzhou Liu 	return 0;
2083d6af748SShengzhou Liu }
2093d6af748SShengzhou Liu 
210c624d168SBhupesh Sharma static int rtl8211x_startup(struct phy_device *phydev)
2119082eeacSAndy Fleming {
212b733c278SMichal Simek 	int ret;
2139082eeacSAndy Fleming 
214b733c278SMichal Simek 	/* Read the Status (2x to make sure link is right) */
215b733c278SMichal Simek 	ret = genphy_update_link(phydev);
216b733c278SMichal Simek 	if (ret)
217b733c278SMichal Simek 		return ret;
218b733c278SMichal Simek 
219b733c278SMichal Simek 	return rtl8211x_parse_status(phydev);
2209082eeacSAndy Fleming }
2219082eeacSAndy Fleming 
2226a10bc5bSMichal Simek static int rtl8211e_startup(struct phy_device *phydev)
2236a10bc5bSMichal Simek {
224b733c278SMichal Simek 	int ret;
2256a10bc5bSMichal Simek 
226b733c278SMichal Simek 	ret = genphy_update_link(phydev);
227b733c278SMichal Simek 	if (ret)
228b733c278SMichal Simek 		return ret;
229b733c278SMichal Simek 
230b733c278SMichal Simek 	return genphy_parse_link(phydev);
2316a10bc5bSMichal Simek }
2326a10bc5bSMichal Simek 
2333d6af748SShengzhou Liu static int rtl8211f_startup(struct phy_device *phydev)
2343d6af748SShengzhou Liu {
235b733c278SMichal Simek 	int ret;
2363d6af748SShengzhou Liu 
237b733c278SMichal Simek 	/* Read the Status (2x to make sure link is right) */
238b733c278SMichal Simek 	ret = genphy_update_link(phydev);
239b733c278SMichal Simek 	if (ret)
240b733c278SMichal Simek 		return ret;
241b733c278SMichal Simek 	/* Read the Status (2x to make sure link is right) */
242b733c278SMichal Simek 
243b733c278SMichal Simek 	return rtl8211f_parse_status(phydev);
2443d6af748SShengzhou Liu }
2453d6af748SShengzhou Liu 
246c624d168SBhupesh Sharma /* Support for RTL8211B PHY */
2479082eeacSAndy Fleming static struct phy_driver RTL8211B_driver = {
2489082eeacSAndy Fleming 	.name = "RealTek RTL8211B",
249563d8d93SKarsten Merker 	.uid = 0x1cc912,
25042205047SBhupesh Sharma 	.mask = 0xffffff,
2519082eeacSAndy Fleming 	.features = PHY_GBIT_FEATURES,
252c624d168SBhupesh Sharma 	.config = &rtl8211x_config,
253c624d168SBhupesh Sharma 	.startup = &rtl8211x_startup,
254c624d168SBhupesh Sharma 	.shutdown = &genphy_shutdown,
255c624d168SBhupesh Sharma };
256c624d168SBhupesh Sharma 
257c624d168SBhupesh Sharma /* Support for RTL8211E-VB-CG, RTL8211E-VL-CG and RTL8211EG-VB-CG PHYs */
258c624d168SBhupesh Sharma static struct phy_driver RTL8211E_driver = {
259c624d168SBhupesh Sharma 	.name = "RealTek RTL8211E",
260c624d168SBhupesh Sharma 	.uid = 0x1cc915,
26142205047SBhupesh Sharma 	.mask = 0xffffff,
262c624d168SBhupesh Sharma 	.features = PHY_GBIT_FEATURES,
263c624d168SBhupesh Sharma 	.config = &rtl8211x_config,
2646a10bc5bSMichal Simek 	.startup = &rtl8211e_startup,
265c624d168SBhupesh Sharma 	.shutdown = &genphy_shutdown,
266c624d168SBhupesh Sharma };
267c624d168SBhupesh Sharma 
268c624d168SBhupesh Sharma /* Support for RTL8211DN PHY */
269c624d168SBhupesh Sharma static struct phy_driver RTL8211DN_driver = {
270c624d168SBhupesh Sharma 	.name = "RealTek RTL8211DN",
271c624d168SBhupesh Sharma 	.uid = 0x1cc914,
27242205047SBhupesh Sharma 	.mask = 0xffffff,
273c624d168SBhupesh Sharma 	.features = PHY_GBIT_FEATURES,
274c624d168SBhupesh Sharma 	.config = &rtl8211x_config,
275c624d168SBhupesh Sharma 	.startup = &rtl8211x_startup,
2769082eeacSAndy Fleming 	.shutdown = &genphy_shutdown,
2779082eeacSAndy Fleming };
2789082eeacSAndy Fleming 
2793d6af748SShengzhou Liu /* Support for RTL8211F PHY */
2803d6af748SShengzhou Liu static struct phy_driver RTL8211F_driver = {
2813d6af748SShengzhou Liu 	.name = "RealTek RTL8211F",
2823d6af748SShengzhou Liu 	.uid = 0x1cc916,
2833d6af748SShengzhou Liu 	.mask = 0xffffff,
2843d6af748SShengzhou Liu 	.features = PHY_GBIT_FEATURES,
285793ea947SShengzhou Liu 	.config = &rtl8211f_config,
2863d6af748SShengzhou Liu 	.startup = &rtl8211f_startup,
2873d6af748SShengzhou Liu 	.shutdown = &genphy_shutdown,
2883d6af748SShengzhou Liu };
2893d6af748SShengzhou Liu 
2909082eeacSAndy Fleming int phy_realtek_init(void)
2919082eeacSAndy Fleming {
2929082eeacSAndy Fleming 	phy_register(&RTL8211B_driver);
293c624d168SBhupesh Sharma 	phy_register(&RTL8211E_driver);
2943d6af748SShengzhou Liu 	phy_register(&RTL8211F_driver);
295c624d168SBhupesh Sharma 	phy_register(&RTL8211DN_driver);
2969082eeacSAndy Fleming 
2979082eeacSAndy Fleming 	return 0;
2989082eeacSAndy Fleming }
299