1a2443fd1SAndrew Lunn // SPDX-License-Identifier: GPL-2.0+ 2097c2aa8SJohnson Leung /* 3097c2aa8SJohnson Leung * drivers/net/phy/realtek.c 4097c2aa8SJohnson Leung * 5097c2aa8SJohnson Leung * Driver for Realtek PHYs 6097c2aa8SJohnson Leung * 7097c2aa8SJohnson Leung * Author: Johnson Leung <r58129@freescale.com> 8097c2aa8SJohnson Leung * 9097c2aa8SJohnson Leung * Copyright (c) 2004 Freescale Semiconductor, Inc. 10097c2aa8SJohnson Leung */ 118cc5baefSMartin Blumenstingl #include <linux/bitops.h> 12097c2aa8SJohnson Leung #include <linux/phy.h> 139d9779e7SPaul Gortmaker #include <linux/module.h> 14097c2aa8SJohnson Leung 15097c2aa8SJohnson Leung #define RTL821x_PHYSR 0x11 168cc5baefSMartin Blumenstingl #define RTL821x_PHYSR_DUPLEX BIT(13) 178cc5baefSMartin Blumenstingl #define RTL821x_PHYSR_SPEED GENMASK(15, 14) 18a82f266dSMartin Blumenstingl 19097c2aa8SJohnson Leung #define RTL821x_INER 0x12 2069021e32SMartin Blumenstingl #define RTL8211B_INER_INIT 0x6400 218cc5baefSMartin Blumenstingl #define RTL8211E_INER_LINK_STATUS BIT(10) 228cc5baefSMartin Blumenstingl #define RTL8211F_INER_LINK_STATUS BIT(4) 23a82f266dSMartin Blumenstingl 24a82f266dSMartin Blumenstingl #define RTL821x_INSR 0x13 25a82f266dSMartin Blumenstingl 26a82f266dSMartin Blumenstingl #define RTL821x_PAGE_SELECT 0x1f 27a82f266dSMartin Blumenstingl 283447cf2eSShengzhou Liu #define RTL8211F_INSR 0x1d 29f609ab0eSMartin Blumenstingl 308cc5baefSMartin Blumenstingl #define RTL8211F_TX_DELAY BIT(8) 313447cf2eSShengzhou Liu 32513588ddSJassi Brar #define RTL8201F_ISR 0x1e 33513588ddSJassi Brar #define RTL8201F_IER 0x13 34513588ddSJassi Brar 35d8545825SLinus Walleij #define RTL8366RB_POWER_SAVE 0x15 36d8545825SLinus Walleij #define RTL8366RB_POWER_SAVE_ON BIT(12) 37d8545825SLinus Walleij 38097c2aa8SJohnson Leung MODULE_DESCRIPTION("Realtek PHY driver"); 39097c2aa8SJohnson Leung MODULE_AUTHOR("Johnson Leung"); 40097c2aa8SJohnson Leung MODULE_LICENSE("GPL"); 41097c2aa8SJohnson Leung 42d98c8ccdSHeiner Kallweit static int rtl821x_read_page(struct phy_device *phydev) 43136819a6SMartin Blumenstingl { 44d98c8ccdSHeiner Kallweit return __phy_read(phydev, RTL821x_PAGE_SELECT); 45136819a6SMartin Blumenstingl } 46136819a6SMartin Blumenstingl 47d98c8ccdSHeiner Kallweit static int rtl821x_write_page(struct phy_device *phydev, int page) 48136819a6SMartin Blumenstingl { 49d98c8ccdSHeiner Kallweit return __phy_write(phydev, RTL821x_PAGE_SELECT, page); 50136819a6SMartin Blumenstingl } 51136819a6SMartin Blumenstingl 52513588ddSJassi Brar static int rtl8201_ack_interrupt(struct phy_device *phydev) 53513588ddSJassi Brar { 54513588ddSJassi Brar int err; 55513588ddSJassi Brar 56513588ddSJassi Brar err = phy_read(phydev, RTL8201F_ISR); 57513588ddSJassi Brar 58513588ddSJassi Brar return (err < 0) ? err : 0; 59513588ddSJassi Brar } 60513588ddSJassi Brar 61097c2aa8SJohnson Leung static int rtl821x_ack_interrupt(struct phy_device *phydev) 62097c2aa8SJohnson Leung { 63097c2aa8SJohnson Leung int err; 64097c2aa8SJohnson Leung 65097c2aa8SJohnson Leung err = phy_read(phydev, RTL821x_INSR); 66097c2aa8SJohnson Leung 67097c2aa8SJohnson Leung return (err < 0) ? err : 0; 68097c2aa8SJohnson Leung } 69097c2aa8SJohnson Leung 703447cf2eSShengzhou Liu static int rtl8211f_ack_interrupt(struct phy_device *phydev) 713447cf2eSShengzhou Liu { 723447cf2eSShengzhou Liu int err; 733447cf2eSShengzhou Liu 74d98c8ccdSHeiner Kallweit err = phy_read_paged(phydev, 0xa43, RTL8211F_INSR); 753447cf2eSShengzhou Liu 763447cf2eSShengzhou Liu return (err < 0) ? err : 0; 773447cf2eSShengzhou Liu } 783447cf2eSShengzhou Liu 79513588ddSJassi Brar static int rtl8201_config_intr(struct phy_device *phydev) 80513588ddSJassi Brar { 81136819a6SMartin Blumenstingl u16 val; 82513588ddSJassi Brar 83513588ddSJassi Brar if (phydev->interrupts == PHY_INTERRUPT_ENABLED) 84136819a6SMartin Blumenstingl val = BIT(13) | BIT(12) | BIT(11); 85513588ddSJassi Brar else 86136819a6SMartin Blumenstingl val = 0; 87513588ddSJassi Brar 88d98c8ccdSHeiner Kallweit return phy_write_paged(phydev, 0x7, RTL8201F_IER, val); 89513588ddSJassi Brar } 90513588ddSJassi Brar 91ef3d9049SGiuseppe CAVALLARO static int rtl8211b_config_intr(struct phy_device *phydev) 92097c2aa8SJohnson Leung { 93097c2aa8SJohnson Leung int err; 94097c2aa8SJohnson Leung 95097c2aa8SJohnson Leung if (phydev->interrupts == PHY_INTERRUPT_ENABLED) 96097c2aa8SJohnson Leung err = phy_write(phydev, RTL821x_INER, 9769021e32SMartin Blumenstingl RTL8211B_INER_INIT); 98097c2aa8SJohnson Leung else 99097c2aa8SJohnson Leung err = phy_write(phydev, RTL821x_INER, 0); 100097c2aa8SJohnson Leung 101097c2aa8SJohnson Leung return err; 102097c2aa8SJohnson Leung } 103097c2aa8SJohnson Leung 104ef3d9049SGiuseppe CAVALLARO static int rtl8211e_config_intr(struct phy_device *phydev) 105ef3d9049SGiuseppe CAVALLARO { 106ef3d9049SGiuseppe CAVALLARO int err; 107ef3d9049SGiuseppe CAVALLARO 108ef3d9049SGiuseppe CAVALLARO if (phydev->interrupts == PHY_INTERRUPT_ENABLED) 109ef3d9049SGiuseppe CAVALLARO err = phy_write(phydev, RTL821x_INER, 1108b64fd61SGiuseppe CAVALLARO RTL8211E_INER_LINK_STATUS); 111ef3d9049SGiuseppe CAVALLARO else 112ef3d9049SGiuseppe CAVALLARO err = phy_write(phydev, RTL821x_INER, 0); 113ef3d9049SGiuseppe CAVALLARO 114ef3d9049SGiuseppe CAVALLARO return err; 115ef3d9049SGiuseppe CAVALLARO } 116ef3d9049SGiuseppe CAVALLARO 1173447cf2eSShengzhou Liu static int rtl8211f_config_intr(struct phy_device *phydev) 1183447cf2eSShengzhou Liu { 119136819a6SMartin Blumenstingl u16 val; 1203447cf2eSShengzhou Liu 1213447cf2eSShengzhou Liu if (phydev->interrupts == PHY_INTERRUPT_ENABLED) 122136819a6SMartin Blumenstingl val = RTL8211F_INER_LINK_STATUS; 1233447cf2eSShengzhou Liu else 124136819a6SMartin Blumenstingl val = 0; 1253447cf2eSShengzhou Liu 126d98c8ccdSHeiner Kallweit return phy_write_paged(phydev, 0xa42, RTL821x_INER, val); 1273447cf2eSShengzhou Liu } 1283447cf2eSShengzhou Liu 129d241d4aaSHeiner Kallweit static int rtl8211_config_aneg(struct phy_device *phydev) 130d241d4aaSHeiner Kallweit { 131d241d4aaSHeiner Kallweit int ret; 132d241d4aaSHeiner Kallweit 133d241d4aaSHeiner Kallweit ret = genphy_config_aneg(phydev); 134d241d4aaSHeiner Kallweit if (ret < 0) 135d241d4aaSHeiner Kallweit return ret; 136d241d4aaSHeiner Kallweit 137d241d4aaSHeiner Kallweit /* Quirk was copied from vendor driver. Unfortunately it includes no 138d241d4aaSHeiner Kallweit * description of the magic numbers. 139d241d4aaSHeiner Kallweit */ 140d241d4aaSHeiner Kallweit if (phydev->speed == SPEED_100 && phydev->autoneg == AUTONEG_DISABLE) { 141d241d4aaSHeiner Kallweit phy_write(phydev, 0x17, 0x2138); 142d241d4aaSHeiner Kallweit phy_write(phydev, 0x0e, 0x0260); 143d241d4aaSHeiner Kallweit } else { 144d241d4aaSHeiner Kallweit phy_write(phydev, 0x17, 0x2108); 145d241d4aaSHeiner Kallweit phy_write(phydev, 0x0e, 0x0000); 146d241d4aaSHeiner Kallweit } 147d241d4aaSHeiner Kallweit 148d241d4aaSHeiner Kallweit return 0; 149d241d4aaSHeiner Kallweit } 150d241d4aaSHeiner Kallweit 151cf87915cSHeiner Kallweit static int rtl8211c_config_init(struct phy_device *phydev) 152cf87915cSHeiner Kallweit { 153cf87915cSHeiner Kallweit /* RTL8211C has an issue when operating in Gigabit slave mode */ 154*48e4adf9SHeiner Kallweit return phy_set_bits(phydev, MII_CTRL1000, 155cf87915cSHeiner Kallweit CTL1000_ENABLE_MASTER | CTL1000_AS_MASTER); 156cf87915cSHeiner Kallweit } 157cf87915cSHeiner Kallweit 1583447cf2eSShengzhou Liu static int rtl8211f_config_init(struct phy_device *phydev) 1593447cf2eSShengzhou Liu { 160d98c8ccdSHeiner Kallweit u16 val = 0; 1613447cf2eSShengzhou Liu 162e3230494SMartin Blumenstingl /* enable TX-delay for rgmii-id and rgmii-txid, otherwise disable it */ 163e3230494SMartin Blumenstingl if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID || 164e3230494SMartin Blumenstingl phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) 165d98c8ccdSHeiner Kallweit val = RTL8211F_TX_DELAY; 166e3230494SMartin Blumenstingl 167d98c8ccdSHeiner Kallweit return phy_modify_paged(phydev, 0xd08, 0x11, RTL8211F_TX_DELAY, val); 1683447cf2eSShengzhou Liu } 1693447cf2eSShengzhou Liu 170049ff57aSHeiner Kallweit static int rtl8211b_suspend(struct phy_device *phydev) 171049ff57aSHeiner Kallweit { 172049ff57aSHeiner Kallweit phy_write(phydev, MII_MMD_DATA, BIT(9)); 173049ff57aSHeiner Kallweit 174049ff57aSHeiner Kallweit return genphy_suspend(phydev); 175049ff57aSHeiner Kallweit } 176049ff57aSHeiner Kallweit 177049ff57aSHeiner Kallweit static int rtl8211b_resume(struct phy_device *phydev) 178049ff57aSHeiner Kallweit { 179049ff57aSHeiner Kallweit phy_write(phydev, MII_MMD_DATA, 0); 180049ff57aSHeiner Kallweit 181049ff57aSHeiner Kallweit return genphy_resume(phydev); 182049ff57aSHeiner Kallweit } 183049ff57aSHeiner Kallweit 184d8545825SLinus Walleij static int rtl8366rb_config_init(struct phy_device *phydev) 185d8545825SLinus Walleij { 186d8545825SLinus Walleij int ret; 187d8545825SLinus Walleij 188d8545825SLinus Walleij ret = phy_set_bits(phydev, RTL8366RB_POWER_SAVE, 189d8545825SLinus Walleij RTL8366RB_POWER_SAVE_ON); 190d8545825SLinus Walleij if (ret) { 191d8545825SLinus Walleij dev_err(&phydev->mdio.dev, 192d8545825SLinus Walleij "error enabling power management\n"); 193d8545825SLinus Walleij } 194d8545825SLinus Walleij 195d8545825SLinus Walleij return ret; 196d8545825SLinus Walleij } 197d8545825SLinus Walleij 19871b9c4a8SJongsung Kim static struct phy_driver realtek_drvs[] = { 19971b9c4a8SJongsung Kim { 200ca494936SHeiner Kallweit PHY_ID_MATCH_EXACT(0x00008201), 20112959667SJonas Jensen .name = "RTL8201CP Ethernet", 202*48e4adf9SHeiner Kallweit .get_features = genphy_read_abilities, 20371b9c4a8SJongsung Kim }, { 204ca494936SHeiner Kallweit PHY_ID_MATCH_EXACT(0x001cc816), 2050432e833SHolger Hoffstätte .name = "RTL8201F Fast Ethernet", 206*48e4adf9SHeiner Kallweit .get_features = genphy_read_abilities, 207513588ddSJassi Brar .ack_interrupt = &rtl8201_ack_interrupt, 208513588ddSJassi Brar .config_intr = &rtl8201_config_intr, 209513588ddSJassi Brar .suspend = genphy_suspend, 210513588ddSJassi Brar .resume = genphy_resume, 211d98c8ccdSHeiner Kallweit .read_page = rtl821x_read_page, 212d98c8ccdSHeiner Kallweit .write_page = rtl821x_write_page, 213513588ddSJassi Brar }, { 214ca494936SHeiner Kallweit PHY_ID_MATCH_EXACT(0x001cc910), 215d241d4aaSHeiner Kallweit .name = "RTL8211 Gigabit Ethernet", 216*48e4adf9SHeiner Kallweit .get_features = genphy_read_abilities, 217d241d4aaSHeiner Kallweit .config_aneg = rtl8211_config_aneg, 218d241d4aaSHeiner Kallweit .read_mmd = &genphy_read_mmd_unsupported, 219d241d4aaSHeiner Kallweit .write_mmd = &genphy_write_mmd_unsupported, 220d241d4aaSHeiner Kallweit }, { 221ca494936SHeiner Kallweit PHY_ID_MATCH_EXACT(0x001cc912), 222ef3d9049SGiuseppe CAVALLARO .name = "RTL8211B Gigabit Ethernet", 223*48e4adf9SHeiner Kallweit .get_features = genphy_read_abilities, 224097c2aa8SJohnson Leung .ack_interrupt = &rtl821x_ack_interrupt, 225ef3d9049SGiuseppe CAVALLARO .config_intr = &rtl8211b_config_intr, 2260231b1a0SKevin Hao .read_mmd = &genphy_read_mmd_unsupported, 2270231b1a0SKevin Hao .write_mmd = &genphy_write_mmd_unsupported, 228049ff57aSHeiner Kallweit .suspend = rtl8211b_suspend, 229049ff57aSHeiner Kallweit .resume = rtl8211b_resume, 23071b9c4a8SJongsung Kim }, { 231ca494936SHeiner Kallweit PHY_ID_MATCH_EXACT(0x001cc913), 232cf87915cSHeiner Kallweit .name = "RTL8211C Gigabit Ethernet", 233*48e4adf9SHeiner Kallweit .get_features = genphy_read_abilities, 234cf87915cSHeiner Kallweit .config_init = rtl8211c_config_init, 235cf87915cSHeiner Kallweit .read_mmd = &genphy_read_mmd_unsupported, 236cf87915cSHeiner Kallweit .write_mmd = &genphy_write_mmd_unsupported, 237cf87915cSHeiner Kallweit }, { 238ca494936SHeiner Kallweit PHY_ID_MATCH_EXACT(0x001cc914), 2390024f892SShaohui Xie .name = "RTL8211DN Gigabit Ethernet", 240*48e4adf9SHeiner Kallweit .get_features = genphy_read_abilities, 2410024f892SShaohui Xie .ack_interrupt = rtl821x_ack_interrupt, 2420024f892SShaohui Xie .config_intr = rtl8211e_config_intr, 2430024f892SShaohui Xie .suspend = genphy_suspend, 2440024f892SShaohui Xie .resume = genphy_resume, 2450024f892SShaohui Xie }, { 246ca494936SHeiner Kallweit PHY_ID_MATCH_EXACT(0x001cc915), 247ef3d9049SGiuseppe CAVALLARO .name = "RTL8211E Gigabit Ethernet", 248*48e4adf9SHeiner Kallweit .get_features = genphy_read_abilities, 249ef3d9049SGiuseppe CAVALLARO .ack_interrupt = &rtl821x_ack_interrupt, 250ef3d9049SGiuseppe CAVALLARO .config_intr = &rtl8211e_config_intr, 251ef3d9049SGiuseppe CAVALLARO .suspend = genphy_suspend, 252ef3d9049SGiuseppe CAVALLARO .resume = genphy_resume, 2533447cf2eSShengzhou Liu }, { 254ca494936SHeiner Kallweit PHY_ID_MATCH_EXACT(0x001cc916), 2553447cf2eSShengzhou Liu .name = "RTL8211F Gigabit Ethernet", 256*48e4adf9SHeiner Kallweit .get_features = genphy_read_abilities, 2573447cf2eSShengzhou Liu .config_init = &rtl8211f_config_init, 2583447cf2eSShengzhou Liu .ack_interrupt = &rtl8211f_ack_interrupt, 2593447cf2eSShengzhou Liu .config_intr = &rtl8211f_config_intr, 2603447cf2eSShengzhou Liu .suspend = genphy_suspend, 2613447cf2eSShengzhou Liu .resume = genphy_resume, 262d98c8ccdSHeiner Kallweit .read_page = rtl821x_read_page, 263d98c8ccdSHeiner Kallweit .write_page = rtl821x_write_page, 264d8545825SLinus Walleij }, { 265f66ebd14SHeiner Kallweit PHY_ID_MATCH_EXACT(0x001cc800), 266f66ebd14SHeiner Kallweit .name = "Generic Realtek PHY", 267*48e4adf9SHeiner Kallweit .get_features = genphy_read_abilities, 268f66ebd14SHeiner Kallweit .suspend = genphy_suspend, 269f66ebd14SHeiner Kallweit .resume = genphy_resume, 270f66ebd14SHeiner Kallweit .read_page = rtl821x_read_page, 271f66ebd14SHeiner Kallweit .write_page = rtl821x_write_page, 272f66ebd14SHeiner Kallweit }, { 273ca494936SHeiner Kallweit PHY_ID_MATCH_EXACT(0x001cc961), 274d8545825SLinus Walleij .name = "RTL8366RB Gigabit Ethernet", 275*48e4adf9SHeiner Kallweit .get_features = genphy_read_abilities, 276d8545825SLinus Walleij .config_init = &rtl8366rb_config_init, 2774c8e0459SLinus Walleij /* These interrupts are handled by the irq controller 2784c8e0459SLinus Walleij * embedded inside the RTL8366RB, they get unmasked when the 2794c8e0459SLinus Walleij * irq is requested and ACKed by reading the status register, 2804c8e0459SLinus Walleij * which is done by the irqchip code. 2814c8e0459SLinus Walleij */ 2824c8e0459SLinus Walleij .ack_interrupt = genphy_no_ack_interrupt, 2834c8e0459SLinus Walleij .config_intr = genphy_no_config_intr, 284d8545825SLinus Walleij .suspend = genphy_suspend, 285d8545825SLinus Walleij .resume = genphy_resume, 28671b9c4a8SJongsung Kim }, 287097c2aa8SJohnson Leung }; 288097c2aa8SJohnson Leung 28950fd7150SJohan Hovold module_phy_driver(realtek_drvs); 2904e4f10f6SDavid Woodhouse 2913b73e842SHeiner Kallweit static const struct mdio_device_id __maybe_unused realtek_tbl[] = { 292ca494936SHeiner Kallweit { PHY_ID_MATCH_VENDOR(0x001cc800) }, 2934e4f10f6SDavid Woodhouse { } 2944e4f10f6SDavid Woodhouse }; 2954e4f10f6SDavid Woodhouse 2964e4f10f6SDavid Woodhouse MODULE_DEVICE_TABLE(mdio, realtek_tbl); 297