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