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