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 89082eeacSAndy Fleming */ 99082eeacSAndy Fleming #include <config.h> 109082eeacSAndy Fleming #include <common.h> 119082eeacSAndy Fleming #include <phy.h> 129082eeacSAndy Fleming 139082eeacSAndy Fleming #define PHY_AUTONEGOTIATE_TIMEOUT 5000 149082eeacSAndy Fleming 15c624d168SBhupesh Sharma /* RTL8211x PHY Status Register */ 16c624d168SBhupesh Sharma #define MIIM_RTL8211x_PHY_STATUS 0x11 17c624d168SBhupesh Sharma #define MIIM_RTL8211x_PHYSTAT_SPEED 0xc000 18c624d168SBhupesh Sharma #define MIIM_RTL8211x_PHYSTAT_GBIT 0x8000 19c624d168SBhupesh Sharma #define MIIM_RTL8211x_PHYSTAT_100 0x4000 20c624d168SBhupesh Sharma #define MIIM_RTL8211x_PHYSTAT_DUPLEX 0x2000 21c624d168SBhupesh Sharma #define MIIM_RTL8211x_PHYSTAT_SPDDONE 0x0800 22c624d168SBhupesh Sharma #define MIIM_RTL8211x_PHYSTAT_LINK 0x0400 239082eeacSAndy Fleming 243cee1388SCodrin Ciubotariu /* RTL8211x PHY Interrupt Enable Register */ 253cee1388SCodrin Ciubotariu #define MIIM_RTL8211x_PHY_INER 0x12 263cee1388SCodrin Ciubotariu #define MIIM_RTL8211x_PHY_INTR_ENA 0x9f01 273cee1388SCodrin Ciubotariu #define MIIM_RTL8211x_PHY_INTR_DIS 0x0000 283cee1388SCodrin Ciubotariu 293cee1388SCodrin Ciubotariu /* RTL8211x PHY Interrupt Status Register */ 303cee1388SCodrin Ciubotariu #define MIIM_RTL8211x_PHY_INSR 0x13 319082eeacSAndy Fleming 323d6af748SShengzhou Liu /* RTL8211F PHY Status Register */ 333d6af748SShengzhou Liu #define MIIM_RTL8211F_PHY_STATUS 0x1a 343d6af748SShengzhou Liu #define MIIM_RTL8211F_AUTONEG_ENABLE 0x1000 353d6af748SShengzhou Liu #define MIIM_RTL8211F_PHYSTAT_SPEED 0x0030 363d6af748SShengzhou Liu #define MIIM_RTL8211F_PHYSTAT_GBIT 0x0020 373d6af748SShengzhou Liu #define MIIM_RTL8211F_PHYSTAT_100 0x0010 383d6af748SShengzhou Liu #define MIIM_RTL8211F_PHYSTAT_DUPLEX 0x0008 393d6af748SShengzhou Liu #define MIIM_RTL8211F_PHYSTAT_SPDDONE 0x0800 403d6af748SShengzhou Liu #define MIIM_RTL8211F_PHYSTAT_LINK 0x0004 413d6af748SShengzhou Liu 423d6af748SShengzhou Liu #define MIIM_RTL8211F_PAGE_SELECT 0x1f 43793ea947SShengzhou Liu #define MIIM_RTL8211F_TX_DELAY 0x100 4490712741SShengzhou Liu #define MIIM_RTL8211F_LCR 0x10 453d6af748SShengzhou Liu 46c624d168SBhupesh Sharma /* RealTek RTL8211x */ 47c624d168SBhupesh Sharma static int rtl8211x_config(struct phy_device *phydev) 489082eeacSAndy Fleming { 499082eeacSAndy Fleming phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, BMCR_RESET); 509082eeacSAndy Fleming 513cee1388SCodrin Ciubotariu /* mask interrupt at init; if the interrupt is 523cee1388SCodrin Ciubotariu * needed indeed, it should be explicitly enabled 533cee1388SCodrin Ciubotariu */ 543cee1388SCodrin Ciubotariu phy_write(phydev, MDIO_DEVAD_NONE, MIIM_RTL8211x_PHY_INER, 553cee1388SCodrin Ciubotariu MIIM_RTL8211x_PHY_INTR_DIS); 563cee1388SCodrin Ciubotariu 573cee1388SCodrin Ciubotariu /* read interrupt status just to clear it */ 583cee1388SCodrin Ciubotariu phy_read(phydev, MDIO_DEVAD_NONE, MIIM_RTL8211x_PHY_INER); 593cee1388SCodrin Ciubotariu 609082eeacSAndy Fleming genphy_config_aneg(phydev); 619082eeacSAndy Fleming 629082eeacSAndy Fleming return 0; 639082eeacSAndy Fleming } 649082eeacSAndy Fleming 65793ea947SShengzhou Liu static int rtl8211f_config(struct phy_device *phydev) 66793ea947SShengzhou Liu { 67793ea947SShengzhou Liu u16 reg; 68793ea947SShengzhou Liu 69793ea947SShengzhou Liu phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, BMCR_RESET); 70793ea947SShengzhou Liu 71793ea947SShengzhou Liu if (phydev->interface == PHY_INTERFACE_MODE_RGMII) { 72793ea947SShengzhou Liu /* enable TXDLY */ 73793ea947SShengzhou Liu phy_write(phydev, MDIO_DEVAD_NONE, 74793ea947SShengzhou Liu MIIM_RTL8211F_PAGE_SELECT, 0xd08); 75793ea947SShengzhou Liu reg = phy_read(phydev, MDIO_DEVAD_NONE, 0x11); 76793ea947SShengzhou Liu reg |= MIIM_RTL8211F_TX_DELAY; 77793ea947SShengzhou Liu phy_write(phydev, MDIO_DEVAD_NONE, 0x11, reg); 78793ea947SShengzhou Liu /* restore to default page 0 */ 79793ea947SShengzhou Liu phy_write(phydev, MDIO_DEVAD_NONE, 80793ea947SShengzhou Liu MIIM_RTL8211F_PAGE_SELECT, 0x0); 81793ea947SShengzhou Liu } 82793ea947SShengzhou Liu 8390712741SShengzhou Liu /* Set green LED for Link, yellow LED for Active */ 8490712741SShengzhou Liu phy_write(phydev, MDIO_DEVAD_NONE, 8590712741SShengzhou Liu MIIM_RTL8211F_PAGE_SELECT, 0xd04); 8690712741SShengzhou Liu phy_write(phydev, MDIO_DEVAD_NONE, 0x10, 0x617f); 8790712741SShengzhou Liu phy_write(phydev, MDIO_DEVAD_NONE, 8890712741SShengzhou Liu MIIM_RTL8211F_PAGE_SELECT, 0x0); 8990712741SShengzhou Liu 90793ea947SShengzhou Liu genphy_config_aneg(phydev); 91793ea947SShengzhou Liu 92793ea947SShengzhou Liu return 0; 93793ea947SShengzhou Liu } 94793ea947SShengzhou Liu 95c624d168SBhupesh Sharma static int rtl8211x_parse_status(struct phy_device *phydev) 969082eeacSAndy Fleming { 979082eeacSAndy Fleming unsigned int speed; 989082eeacSAndy Fleming unsigned int mii_reg; 999082eeacSAndy Fleming 100c624d168SBhupesh Sharma mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_RTL8211x_PHY_STATUS); 1019082eeacSAndy Fleming 102c624d168SBhupesh Sharma if (!(mii_reg & MIIM_RTL8211x_PHYSTAT_SPDDONE)) { 1039082eeacSAndy Fleming int i = 0; 1049082eeacSAndy Fleming 1059082eeacSAndy Fleming /* in case of timeout ->link is cleared */ 1069082eeacSAndy Fleming phydev->link = 1; 1079082eeacSAndy Fleming puts("Waiting for PHY realtime link"); 108c624d168SBhupesh Sharma while (!(mii_reg & MIIM_RTL8211x_PHYSTAT_SPDDONE)) { 1099082eeacSAndy Fleming /* Timeout reached ? */ 1109082eeacSAndy Fleming if (i > PHY_AUTONEGOTIATE_TIMEOUT) { 1119082eeacSAndy Fleming puts(" TIMEOUT !\n"); 1129082eeacSAndy Fleming phydev->link = 0; 1139082eeacSAndy Fleming break; 1149082eeacSAndy Fleming } 1159082eeacSAndy Fleming 1169082eeacSAndy Fleming if ((i++ % 1000) == 0) 1179082eeacSAndy Fleming putc('.'); 1189082eeacSAndy Fleming udelay(1000); /* 1 ms */ 1199082eeacSAndy Fleming mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, 120c624d168SBhupesh Sharma MIIM_RTL8211x_PHY_STATUS); 1219082eeacSAndy Fleming } 1229082eeacSAndy Fleming puts(" done\n"); 1239082eeacSAndy Fleming udelay(500000); /* another 500 ms (results in faster booting) */ 1249082eeacSAndy Fleming } else { 125c624d168SBhupesh Sharma if (mii_reg & MIIM_RTL8211x_PHYSTAT_LINK) 1269082eeacSAndy Fleming phydev->link = 1; 1279082eeacSAndy Fleming else 1289082eeacSAndy Fleming phydev->link = 0; 1299082eeacSAndy Fleming } 1309082eeacSAndy Fleming 131c624d168SBhupesh Sharma if (mii_reg & MIIM_RTL8211x_PHYSTAT_DUPLEX) 1329082eeacSAndy Fleming phydev->duplex = DUPLEX_FULL; 1339082eeacSAndy Fleming else 1349082eeacSAndy Fleming phydev->duplex = DUPLEX_HALF; 1359082eeacSAndy Fleming 136c624d168SBhupesh Sharma speed = (mii_reg & MIIM_RTL8211x_PHYSTAT_SPEED); 1379082eeacSAndy Fleming 1389082eeacSAndy Fleming switch (speed) { 139c624d168SBhupesh Sharma case MIIM_RTL8211x_PHYSTAT_GBIT: 1409082eeacSAndy Fleming phydev->speed = SPEED_1000; 1419082eeacSAndy Fleming break; 142c624d168SBhupesh Sharma case MIIM_RTL8211x_PHYSTAT_100: 1439082eeacSAndy Fleming phydev->speed = SPEED_100; 1449082eeacSAndy Fleming break; 1459082eeacSAndy Fleming default: 1469082eeacSAndy Fleming phydev->speed = SPEED_10; 1479082eeacSAndy Fleming } 1489082eeacSAndy Fleming 1499082eeacSAndy Fleming return 0; 1509082eeacSAndy Fleming } 1519082eeacSAndy Fleming 1523d6af748SShengzhou Liu static int rtl8211f_parse_status(struct phy_device *phydev) 1533d6af748SShengzhou Liu { 1543d6af748SShengzhou Liu unsigned int speed; 1553d6af748SShengzhou Liu unsigned int mii_reg; 1563d6af748SShengzhou Liu int i = 0; 1573d6af748SShengzhou Liu 1583d6af748SShengzhou Liu phy_write(phydev, MDIO_DEVAD_NONE, MIIM_RTL8211F_PAGE_SELECT, 0xa43); 1593d6af748SShengzhou Liu mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_RTL8211F_PHY_STATUS); 1603d6af748SShengzhou Liu 1613d6af748SShengzhou Liu phydev->link = 1; 1623d6af748SShengzhou Liu while (!(mii_reg & MIIM_RTL8211F_PHYSTAT_LINK)) { 1633d6af748SShengzhou Liu if (i > PHY_AUTONEGOTIATE_TIMEOUT) { 1643d6af748SShengzhou Liu puts(" TIMEOUT !\n"); 1653d6af748SShengzhou Liu phydev->link = 0; 1663d6af748SShengzhou Liu break; 1673d6af748SShengzhou Liu } 1683d6af748SShengzhou Liu 1693d6af748SShengzhou Liu if ((i++ % 1000) == 0) 1703d6af748SShengzhou Liu putc('.'); 1713d6af748SShengzhou Liu udelay(1000); 1723d6af748SShengzhou Liu mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, 1733d6af748SShengzhou Liu MIIM_RTL8211F_PHY_STATUS); 1743d6af748SShengzhou Liu } 1753d6af748SShengzhou Liu 1763d6af748SShengzhou Liu if (mii_reg & MIIM_RTL8211F_PHYSTAT_DUPLEX) 1773d6af748SShengzhou Liu phydev->duplex = DUPLEX_FULL; 1783d6af748SShengzhou Liu else 1793d6af748SShengzhou Liu phydev->duplex = DUPLEX_HALF; 1803d6af748SShengzhou Liu 1813d6af748SShengzhou Liu speed = (mii_reg & MIIM_RTL8211F_PHYSTAT_SPEED); 1823d6af748SShengzhou Liu 1833d6af748SShengzhou Liu switch (speed) { 1843d6af748SShengzhou Liu case MIIM_RTL8211F_PHYSTAT_GBIT: 1853d6af748SShengzhou Liu phydev->speed = SPEED_1000; 1863d6af748SShengzhou Liu break; 1873d6af748SShengzhou Liu case MIIM_RTL8211F_PHYSTAT_100: 1883d6af748SShengzhou Liu phydev->speed = SPEED_100; 1893d6af748SShengzhou Liu break; 1903d6af748SShengzhou Liu default: 1913d6af748SShengzhou Liu phydev->speed = SPEED_10; 1923d6af748SShengzhou Liu } 1933d6af748SShengzhou Liu 1943d6af748SShengzhou Liu return 0; 1953d6af748SShengzhou Liu } 1963d6af748SShengzhou Liu 197c624d168SBhupesh Sharma static int rtl8211x_startup(struct phy_device *phydev) 1989082eeacSAndy Fleming { 1999082eeacSAndy Fleming /* Read the Status (2x to make sure link is right) */ 2009082eeacSAndy Fleming genphy_update_link(phydev); 201c624d168SBhupesh Sharma rtl8211x_parse_status(phydev); 2029082eeacSAndy Fleming 2039082eeacSAndy Fleming return 0; 2049082eeacSAndy Fleming } 2059082eeacSAndy Fleming 206*6a10bc5bSMichal Simek static int rtl8211e_startup(struct phy_device *phydev) 207*6a10bc5bSMichal Simek { 208*6a10bc5bSMichal Simek genphy_update_link(phydev); 209*6a10bc5bSMichal Simek genphy_parse_link(phydev); 210*6a10bc5bSMichal Simek 211*6a10bc5bSMichal Simek return 0; 212*6a10bc5bSMichal Simek } 213*6a10bc5bSMichal Simek 2143d6af748SShengzhou Liu static int rtl8211f_startup(struct phy_device *phydev) 2153d6af748SShengzhou Liu { 2163d6af748SShengzhou Liu /* Read the Status (2x to make sure link is right) */ 2173d6af748SShengzhou Liu genphy_update_link(phydev); 2183d6af748SShengzhou Liu rtl8211f_parse_status(phydev); 2193d6af748SShengzhou Liu 2203d6af748SShengzhou Liu return 0; 2213d6af748SShengzhou Liu } 2223d6af748SShengzhou Liu 223c624d168SBhupesh Sharma /* Support for RTL8211B PHY */ 2249082eeacSAndy Fleming static struct phy_driver RTL8211B_driver = { 2259082eeacSAndy Fleming .name = "RealTek RTL8211B", 2269082eeacSAndy Fleming .uid = 0x1cc910, 22742205047SBhupesh Sharma .mask = 0xffffff, 2289082eeacSAndy Fleming .features = PHY_GBIT_FEATURES, 229c624d168SBhupesh Sharma .config = &rtl8211x_config, 230c624d168SBhupesh Sharma .startup = &rtl8211x_startup, 231c624d168SBhupesh Sharma .shutdown = &genphy_shutdown, 232c624d168SBhupesh Sharma }; 233c624d168SBhupesh Sharma 234c624d168SBhupesh Sharma /* Support for RTL8211E-VB-CG, RTL8211E-VL-CG and RTL8211EG-VB-CG PHYs */ 235c624d168SBhupesh Sharma static struct phy_driver RTL8211E_driver = { 236c624d168SBhupesh Sharma .name = "RealTek RTL8211E", 237c624d168SBhupesh Sharma .uid = 0x1cc915, 23842205047SBhupesh Sharma .mask = 0xffffff, 239c624d168SBhupesh Sharma .features = PHY_GBIT_FEATURES, 240c624d168SBhupesh Sharma .config = &rtl8211x_config, 241*6a10bc5bSMichal Simek .startup = &rtl8211e_startup, 242c624d168SBhupesh Sharma .shutdown = &genphy_shutdown, 243c624d168SBhupesh Sharma }; 244c624d168SBhupesh Sharma 245c624d168SBhupesh Sharma /* Support for RTL8211DN PHY */ 246c624d168SBhupesh Sharma static struct phy_driver RTL8211DN_driver = { 247c624d168SBhupesh Sharma .name = "RealTek RTL8211DN", 248c624d168SBhupesh Sharma .uid = 0x1cc914, 24942205047SBhupesh Sharma .mask = 0xffffff, 250c624d168SBhupesh Sharma .features = PHY_GBIT_FEATURES, 251c624d168SBhupesh Sharma .config = &rtl8211x_config, 252c624d168SBhupesh Sharma .startup = &rtl8211x_startup, 2539082eeacSAndy Fleming .shutdown = &genphy_shutdown, 2549082eeacSAndy Fleming }; 2559082eeacSAndy Fleming 2563d6af748SShengzhou Liu /* Support for RTL8211F PHY */ 2573d6af748SShengzhou Liu static struct phy_driver RTL8211F_driver = { 2583d6af748SShengzhou Liu .name = "RealTek RTL8211F", 2593d6af748SShengzhou Liu .uid = 0x1cc916, 2603d6af748SShengzhou Liu .mask = 0xffffff, 2613d6af748SShengzhou Liu .features = PHY_GBIT_FEATURES, 262793ea947SShengzhou Liu .config = &rtl8211f_config, 2633d6af748SShengzhou Liu .startup = &rtl8211f_startup, 2643d6af748SShengzhou Liu .shutdown = &genphy_shutdown, 2653d6af748SShengzhou Liu }; 2663d6af748SShengzhou Liu 2679082eeacSAndy Fleming int phy_realtek_init(void) 2689082eeacSAndy Fleming { 2699082eeacSAndy Fleming phy_register(&RTL8211B_driver); 270c624d168SBhupesh Sharma phy_register(&RTL8211E_driver); 2713d6af748SShengzhou Liu phy_register(&RTL8211F_driver); 272c624d168SBhupesh Sharma phy_register(&RTL8211DN_driver); 2739082eeacSAndy Fleming 2749082eeacSAndy Fleming return 0; 2759082eeacSAndy Fleming } 276