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 15cebf3f55Soliver@schinagl.nl #define PHY_RTL8211x_FORCE_MASTER BIT(1) 16cebf3f55Soliver@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 54cebf3f55Soliver@schinagl.nl static int rtl8211b_probe(struct phy_device *phydev) 55cebf3f55Soliver@schinagl.nl { 56cebf3f55Soliver@schinagl.nl #ifdef CONFIG_RTL8211X_PHY_FORCE_MASTER 57cebf3f55Soliver@schinagl.nl phydev->flags |= PHY_RTL8211x_FORCE_MASTER; 58cebf3f55Soliver@schinagl.nl #endif 59cebf3f55Soliver@schinagl.nl 60cebf3f55Soliver@schinagl.nl return 0; 61cebf3f55Soliver@schinagl.nl } 62cebf3f55Soliver@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); 73cebf3f55Soliver@schinagl.nl 74cebf3f55Soliver@schinagl.nl if (phydev->flags & PHY_RTL8211x_FORCE_MASTER) { 75cebf3f55Soliver@schinagl.nl unsigned int reg; 76cebf3f55Soliver@schinagl.nl 77cebf3f55Soliver@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); 83cebf3f55Soliver@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 phy_write(phydev, MDIO_DEVAD_NONE, 99793ea947SShengzhou Liu MIIM_RTL8211F_PAGE_SELECT, 0xd08); 100793ea947SShengzhou Liu reg = phy_read(phydev, MDIO_DEVAD_NONE, 0x11); 101*05b29aa0SMadalin Bucur 102*05b29aa0SMadalin Bucur /* enable TX-delay for rgmii-id and rgmii-txid, otherwise disable it */ 103*05b29aa0SMadalin Bucur if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID || 104*05b29aa0SMadalin Bucur phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) 105793ea947SShengzhou Liu reg |= MIIM_RTL8211F_TX_DELAY; 106*05b29aa0SMadalin Bucur else 107*05b29aa0SMadalin Bucur reg &= ~MIIM_RTL8211F_TX_DELAY; 108*05b29aa0SMadalin Bucur 109793ea947SShengzhou Liu phy_write(phydev, MDIO_DEVAD_NONE, 0x11, reg); 110793ea947SShengzhou Liu /* restore to default page 0 */ 111793ea947SShengzhou Liu phy_write(phydev, MDIO_DEVAD_NONE, 112793ea947SShengzhou Liu MIIM_RTL8211F_PAGE_SELECT, 0x0); 113793ea947SShengzhou Liu 11490712741SShengzhou Liu /* Set green LED for Link, yellow LED for Active */ 11590712741SShengzhou Liu phy_write(phydev, MDIO_DEVAD_NONE, 11690712741SShengzhou Liu MIIM_RTL8211F_PAGE_SELECT, 0xd04); 11790712741SShengzhou Liu phy_write(phydev, MDIO_DEVAD_NONE, 0x10, 0x617f); 11890712741SShengzhou Liu phy_write(phydev, MDIO_DEVAD_NONE, 11990712741SShengzhou Liu MIIM_RTL8211F_PAGE_SELECT, 0x0); 12090712741SShengzhou Liu 121793ea947SShengzhou Liu genphy_config_aneg(phydev); 122793ea947SShengzhou Liu 123793ea947SShengzhou Liu return 0; 124793ea947SShengzhou Liu } 125793ea947SShengzhou Liu 126c624d168SBhupesh Sharma static int rtl8211x_parse_status(struct phy_device *phydev) 1279082eeacSAndy Fleming { 1289082eeacSAndy Fleming unsigned int speed; 1299082eeacSAndy Fleming unsigned int mii_reg; 1309082eeacSAndy Fleming 131c624d168SBhupesh Sharma mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_RTL8211x_PHY_STATUS); 1329082eeacSAndy Fleming 133c624d168SBhupesh Sharma if (!(mii_reg & MIIM_RTL8211x_PHYSTAT_SPDDONE)) { 1349082eeacSAndy Fleming int i = 0; 1359082eeacSAndy Fleming 1369082eeacSAndy Fleming /* in case of timeout ->link is cleared */ 1379082eeacSAndy Fleming phydev->link = 1; 1389082eeacSAndy Fleming puts("Waiting for PHY realtime link"); 139c624d168SBhupesh Sharma while (!(mii_reg & MIIM_RTL8211x_PHYSTAT_SPDDONE)) { 1409082eeacSAndy Fleming /* Timeout reached ? */ 1419082eeacSAndy Fleming if (i > PHY_AUTONEGOTIATE_TIMEOUT) { 1429082eeacSAndy Fleming puts(" TIMEOUT !\n"); 1439082eeacSAndy Fleming phydev->link = 0; 1449082eeacSAndy Fleming break; 1459082eeacSAndy Fleming } 1469082eeacSAndy Fleming 1479082eeacSAndy Fleming if ((i++ % 1000) == 0) 1489082eeacSAndy Fleming putc('.'); 1499082eeacSAndy Fleming udelay(1000); /* 1 ms */ 1509082eeacSAndy Fleming mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, 151c624d168SBhupesh Sharma MIIM_RTL8211x_PHY_STATUS); 1529082eeacSAndy Fleming } 1539082eeacSAndy Fleming puts(" done\n"); 1549082eeacSAndy Fleming udelay(500000); /* another 500 ms (results in faster booting) */ 1559082eeacSAndy Fleming } else { 156c624d168SBhupesh Sharma if (mii_reg & MIIM_RTL8211x_PHYSTAT_LINK) 1579082eeacSAndy Fleming phydev->link = 1; 1589082eeacSAndy Fleming else 1599082eeacSAndy Fleming phydev->link = 0; 1609082eeacSAndy Fleming } 1619082eeacSAndy Fleming 162c624d168SBhupesh Sharma if (mii_reg & MIIM_RTL8211x_PHYSTAT_DUPLEX) 1639082eeacSAndy Fleming phydev->duplex = DUPLEX_FULL; 1649082eeacSAndy Fleming else 1659082eeacSAndy Fleming phydev->duplex = DUPLEX_HALF; 1669082eeacSAndy Fleming 167c624d168SBhupesh Sharma speed = (mii_reg & MIIM_RTL8211x_PHYSTAT_SPEED); 1689082eeacSAndy Fleming 1699082eeacSAndy Fleming switch (speed) { 170c624d168SBhupesh Sharma case MIIM_RTL8211x_PHYSTAT_GBIT: 1719082eeacSAndy Fleming phydev->speed = SPEED_1000; 1729082eeacSAndy Fleming break; 173c624d168SBhupesh Sharma case MIIM_RTL8211x_PHYSTAT_100: 1749082eeacSAndy Fleming phydev->speed = SPEED_100; 1759082eeacSAndy Fleming break; 1769082eeacSAndy Fleming default: 1779082eeacSAndy Fleming phydev->speed = SPEED_10; 1789082eeacSAndy Fleming } 1799082eeacSAndy Fleming 1809082eeacSAndy Fleming return 0; 1819082eeacSAndy Fleming } 1829082eeacSAndy Fleming 1833d6af748SShengzhou Liu static int rtl8211f_parse_status(struct phy_device *phydev) 1843d6af748SShengzhou Liu { 1853d6af748SShengzhou Liu unsigned int speed; 1863d6af748SShengzhou Liu unsigned int mii_reg; 1873d6af748SShengzhou Liu int i = 0; 1883d6af748SShengzhou Liu 1893d6af748SShengzhou Liu phy_write(phydev, MDIO_DEVAD_NONE, MIIM_RTL8211F_PAGE_SELECT, 0xa43); 1903d6af748SShengzhou Liu mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_RTL8211F_PHY_STATUS); 1913d6af748SShengzhou Liu 1923d6af748SShengzhou Liu phydev->link = 1; 1933d6af748SShengzhou Liu while (!(mii_reg & MIIM_RTL8211F_PHYSTAT_LINK)) { 1943d6af748SShengzhou Liu if (i > PHY_AUTONEGOTIATE_TIMEOUT) { 1953d6af748SShengzhou Liu puts(" TIMEOUT !\n"); 1963d6af748SShengzhou Liu phydev->link = 0; 1973d6af748SShengzhou Liu break; 1983d6af748SShengzhou Liu } 1993d6af748SShengzhou Liu 2003d6af748SShengzhou Liu if ((i++ % 1000) == 0) 2013d6af748SShengzhou Liu putc('.'); 2023d6af748SShengzhou Liu udelay(1000); 2033d6af748SShengzhou Liu mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, 2043d6af748SShengzhou Liu MIIM_RTL8211F_PHY_STATUS); 2053d6af748SShengzhou Liu } 2063d6af748SShengzhou Liu 2073d6af748SShengzhou Liu if (mii_reg & MIIM_RTL8211F_PHYSTAT_DUPLEX) 2083d6af748SShengzhou Liu phydev->duplex = DUPLEX_FULL; 2093d6af748SShengzhou Liu else 2103d6af748SShengzhou Liu phydev->duplex = DUPLEX_HALF; 2113d6af748SShengzhou Liu 2123d6af748SShengzhou Liu speed = (mii_reg & MIIM_RTL8211F_PHYSTAT_SPEED); 2133d6af748SShengzhou Liu 2143d6af748SShengzhou Liu switch (speed) { 2153d6af748SShengzhou Liu case MIIM_RTL8211F_PHYSTAT_GBIT: 2163d6af748SShengzhou Liu phydev->speed = SPEED_1000; 2173d6af748SShengzhou Liu break; 2183d6af748SShengzhou Liu case MIIM_RTL8211F_PHYSTAT_100: 2193d6af748SShengzhou Liu phydev->speed = SPEED_100; 2203d6af748SShengzhou Liu break; 2213d6af748SShengzhou Liu default: 2223d6af748SShengzhou Liu phydev->speed = SPEED_10; 2233d6af748SShengzhou Liu } 2243d6af748SShengzhou Liu 2253d6af748SShengzhou Liu return 0; 2263d6af748SShengzhou Liu } 2273d6af748SShengzhou Liu 228c624d168SBhupesh Sharma static int rtl8211x_startup(struct phy_device *phydev) 2299082eeacSAndy Fleming { 230b733c278SMichal Simek int ret; 2319082eeacSAndy Fleming 232b733c278SMichal Simek /* Read the Status (2x to make sure link is right) */ 233b733c278SMichal Simek ret = genphy_update_link(phydev); 234b733c278SMichal Simek if (ret) 235b733c278SMichal Simek return ret; 236b733c278SMichal Simek 237b733c278SMichal Simek return rtl8211x_parse_status(phydev); 2389082eeacSAndy Fleming } 2399082eeacSAndy Fleming 2406a10bc5bSMichal Simek static int rtl8211e_startup(struct phy_device *phydev) 2416a10bc5bSMichal Simek { 242b733c278SMichal Simek int ret; 2436a10bc5bSMichal Simek 244b733c278SMichal Simek ret = genphy_update_link(phydev); 245b733c278SMichal Simek if (ret) 246b733c278SMichal Simek return ret; 247b733c278SMichal Simek 248b733c278SMichal Simek return genphy_parse_link(phydev); 2496a10bc5bSMichal Simek } 2506a10bc5bSMichal Simek 2513d6af748SShengzhou Liu static int rtl8211f_startup(struct phy_device *phydev) 2523d6af748SShengzhou Liu { 253b733c278SMichal Simek int ret; 2543d6af748SShengzhou Liu 255b733c278SMichal Simek /* Read the Status (2x to make sure link is right) */ 256b733c278SMichal Simek ret = genphy_update_link(phydev); 257b733c278SMichal Simek if (ret) 258b733c278SMichal Simek return ret; 259b733c278SMichal Simek /* Read the Status (2x to make sure link is right) */ 260b733c278SMichal Simek 261b733c278SMichal Simek return rtl8211f_parse_status(phydev); 2623d6af748SShengzhou Liu } 2633d6af748SShengzhou Liu 264c624d168SBhupesh Sharma /* Support for RTL8211B PHY */ 2659082eeacSAndy Fleming static struct phy_driver RTL8211B_driver = { 2669082eeacSAndy Fleming .name = "RealTek RTL8211B", 267563d8d93SKarsten Merker .uid = 0x1cc912, 26842205047SBhupesh Sharma .mask = 0xffffff, 2699082eeacSAndy Fleming .features = PHY_GBIT_FEATURES, 270cebf3f55Soliver@schinagl.nl .probe = &rtl8211b_probe, 271c624d168SBhupesh Sharma .config = &rtl8211x_config, 272c624d168SBhupesh Sharma .startup = &rtl8211x_startup, 273c624d168SBhupesh Sharma .shutdown = &genphy_shutdown, 274c624d168SBhupesh Sharma }; 275c624d168SBhupesh Sharma 276c624d168SBhupesh Sharma /* Support for RTL8211E-VB-CG, RTL8211E-VL-CG and RTL8211EG-VB-CG PHYs */ 277c624d168SBhupesh Sharma static struct phy_driver RTL8211E_driver = { 278c624d168SBhupesh Sharma .name = "RealTek RTL8211E", 279c624d168SBhupesh Sharma .uid = 0x1cc915, 28042205047SBhupesh Sharma .mask = 0xffffff, 281c624d168SBhupesh Sharma .features = PHY_GBIT_FEATURES, 282c624d168SBhupesh Sharma .config = &rtl8211x_config, 2836a10bc5bSMichal Simek .startup = &rtl8211e_startup, 284c624d168SBhupesh Sharma .shutdown = &genphy_shutdown, 285c624d168SBhupesh Sharma }; 286c624d168SBhupesh Sharma 287c624d168SBhupesh Sharma /* Support for RTL8211DN PHY */ 288c624d168SBhupesh Sharma static struct phy_driver RTL8211DN_driver = { 289c624d168SBhupesh Sharma .name = "RealTek RTL8211DN", 290c624d168SBhupesh Sharma .uid = 0x1cc914, 29142205047SBhupesh Sharma .mask = 0xffffff, 292c624d168SBhupesh Sharma .features = PHY_GBIT_FEATURES, 293c624d168SBhupesh Sharma .config = &rtl8211x_config, 294c624d168SBhupesh Sharma .startup = &rtl8211x_startup, 2959082eeacSAndy Fleming .shutdown = &genphy_shutdown, 2969082eeacSAndy Fleming }; 2979082eeacSAndy Fleming 2983d6af748SShengzhou Liu /* Support for RTL8211F PHY */ 2993d6af748SShengzhou Liu static struct phy_driver RTL8211F_driver = { 3003d6af748SShengzhou Liu .name = "RealTek RTL8211F", 3013d6af748SShengzhou Liu .uid = 0x1cc916, 3023d6af748SShengzhou Liu .mask = 0xffffff, 3033d6af748SShengzhou Liu .features = PHY_GBIT_FEATURES, 304793ea947SShengzhou Liu .config = &rtl8211f_config, 3053d6af748SShengzhou Liu .startup = &rtl8211f_startup, 3063d6af748SShengzhou Liu .shutdown = &genphy_shutdown, 3073d6af748SShengzhou Liu }; 3083d6af748SShengzhou Liu 3099082eeacSAndy Fleming int phy_realtek_init(void) 3109082eeacSAndy Fleming { 3119082eeacSAndy Fleming phy_register(&RTL8211B_driver); 312c624d168SBhupesh Sharma phy_register(&RTL8211E_driver); 3133d6af748SShengzhou Liu phy_register(&RTL8211F_driver); 314c624d168SBhupesh Sharma phy_register(&RTL8211DN_driver); 3159082eeacSAndy Fleming 3169082eeacSAndy Fleming return 0; 3179082eeacSAndy Fleming } 318