1 /* 2 * drivers/net/phy/realtek.c 3 * 4 * Driver for Realtek PHYs 5 * 6 * Author: Johnson Leung <r58129@freescale.com> 7 * 8 * Copyright (c) 2004 Freescale Semiconductor, Inc. 9 * 10 * This program is free software; you can redistribute it and/or modify it 11 * under the terms of the GNU General Public License as published by the 12 * Free Software Foundation; either version 2 of the License, or (at your 13 * option) any later version. 14 * 15 */ 16 #include <linux/phy.h> 17 #include <linux/module.h> 18 19 #define RTL821x_PHYSR 0x11 20 #define RTL821x_PHYSR_DUPLEX 0x2000 21 #define RTL821x_PHYSR_SPEED 0xc000 22 #define RTL821x_INER 0x12 23 #define RTL821x_INER_INIT 0x6400 24 #define RTL821x_INSR 0x13 25 #define RTL821x_PAGE_SELECT 0x1f 26 #define RTL8211E_INER_LINK_STATUS 0x400 27 28 #define RTL8211F_INER_LINK_STATUS 0x0010 29 #define RTL8211F_INSR 0x1d 30 #define RTL8211F_TX_DELAY 0x100 31 32 #define RTL8201F_ISR 0x1e 33 #define RTL8201F_IER 0x13 34 35 MODULE_DESCRIPTION("Realtek PHY driver"); 36 MODULE_AUTHOR("Johnson Leung"); 37 MODULE_LICENSE("GPL"); 38 39 static int rtl8201_ack_interrupt(struct phy_device *phydev) 40 { 41 int err; 42 43 err = phy_read(phydev, RTL8201F_ISR); 44 45 return (err < 0) ? err : 0; 46 } 47 48 static int rtl821x_ack_interrupt(struct phy_device *phydev) 49 { 50 int err; 51 52 err = phy_read(phydev, RTL821x_INSR); 53 54 return (err < 0) ? err : 0; 55 } 56 57 static int rtl8211f_ack_interrupt(struct phy_device *phydev) 58 { 59 int err; 60 61 phy_write(phydev, RTL821x_PAGE_SELECT, 0xa43); 62 err = phy_read(phydev, RTL8211F_INSR); 63 /* restore to default page 0 */ 64 phy_write(phydev, RTL821x_PAGE_SELECT, 0x0); 65 66 return (err < 0) ? err : 0; 67 } 68 69 static int rtl8201_config_intr(struct phy_device *phydev) 70 { 71 int err; 72 73 /* switch to page 7 */ 74 phy_write(phydev, RTL821x_PAGE_SELECT, 0x7); 75 76 if (phydev->interrupts == PHY_INTERRUPT_ENABLED) 77 err = phy_write(phydev, RTL8201F_IER, 78 BIT(13) | BIT(12) | BIT(11)); 79 else 80 err = phy_write(phydev, RTL8201F_IER, 0); 81 82 /* restore to default page 0 */ 83 phy_write(phydev, RTL821x_PAGE_SELECT, 0x0); 84 85 return err; 86 } 87 88 static int rtl8211b_config_intr(struct phy_device *phydev) 89 { 90 int err; 91 92 if (phydev->interrupts == PHY_INTERRUPT_ENABLED) 93 err = phy_write(phydev, RTL821x_INER, 94 RTL821x_INER_INIT); 95 else 96 err = phy_write(phydev, RTL821x_INER, 0); 97 98 return err; 99 } 100 101 static int rtl8211e_config_intr(struct phy_device *phydev) 102 { 103 int err; 104 105 if (phydev->interrupts == PHY_INTERRUPT_ENABLED) 106 err = phy_write(phydev, RTL821x_INER, 107 RTL8211E_INER_LINK_STATUS); 108 else 109 err = phy_write(phydev, RTL821x_INER, 0); 110 111 return err; 112 } 113 114 static int rtl8211f_config_intr(struct phy_device *phydev) 115 { 116 int err; 117 118 if (phydev->interrupts == PHY_INTERRUPT_ENABLED) 119 err = phy_write(phydev, RTL821x_INER, 120 RTL8211F_INER_LINK_STATUS); 121 else 122 err = phy_write(phydev, RTL821x_INER, 0); 123 124 return err; 125 } 126 127 static int rtl8211f_config_init(struct phy_device *phydev) 128 { 129 int ret; 130 u16 reg; 131 132 ret = genphy_config_init(phydev); 133 if (ret < 0) 134 return ret; 135 136 phy_write(phydev, RTL821x_PAGE_SELECT, 0xd08); 137 reg = phy_read(phydev, 0x11); 138 139 /* enable TX-delay for rgmii-id and rgmii-txid, otherwise disable it */ 140 if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID || 141 phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) 142 reg |= RTL8211F_TX_DELAY; 143 else 144 reg &= ~RTL8211F_TX_DELAY; 145 146 phy_write(phydev, 0x11, reg); 147 /* restore to default page 0 */ 148 phy_write(phydev, RTL821x_PAGE_SELECT, 0x0); 149 150 return 0; 151 } 152 153 static struct phy_driver realtek_drvs[] = { 154 { 155 .phy_id = 0x00008201, 156 .name = "RTL8201CP Ethernet", 157 .phy_id_mask = 0x0000ffff, 158 .features = PHY_BASIC_FEATURES, 159 .flags = PHY_HAS_INTERRUPT, 160 .config_aneg = &genphy_config_aneg, 161 .read_status = &genphy_read_status, 162 }, { 163 .phy_id = 0x001cc816, 164 .name = "RTL8201F 10/100Mbps Ethernet", 165 .phy_id_mask = 0x001fffff, 166 .features = PHY_BASIC_FEATURES, 167 .flags = PHY_HAS_INTERRUPT, 168 .config_aneg = &genphy_config_aneg, 169 .read_status = &genphy_read_status, 170 .ack_interrupt = &rtl8201_ack_interrupt, 171 .config_intr = &rtl8201_config_intr, 172 .suspend = genphy_suspend, 173 .resume = genphy_resume, 174 }, { 175 .phy_id = 0x001cc912, 176 .name = "RTL8211B Gigabit Ethernet", 177 .phy_id_mask = 0x001fffff, 178 .features = PHY_GBIT_FEATURES, 179 .flags = PHY_HAS_INTERRUPT, 180 .config_aneg = &genphy_config_aneg, 181 .read_status = &genphy_read_status, 182 .ack_interrupt = &rtl821x_ack_interrupt, 183 .config_intr = &rtl8211b_config_intr, 184 }, { 185 .phy_id = 0x001cc914, 186 .name = "RTL8211DN Gigabit Ethernet", 187 .phy_id_mask = 0x001fffff, 188 .features = PHY_GBIT_FEATURES, 189 .flags = PHY_HAS_INTERRUPT, 190 .config_aneg = genphy_config_aneg, 191 .read_status = genphy_read_status, 192 .ack_interrupt = rtl821x_ack_interrupt, 193 .config_intr = rtl8211e_config_intr, 194 .suspend = genphy_suspend, 195 .resume = genphy_resume, 196 }, { 197 .phy_id = 0x001cc915, 198 .name = "RTL8211E Gigabit Ethernet", 199 .phy_id_mask = 0x001fffff, 200 .features = PHY_GBIT_FEATURES, 201 .flags = PHY_HAS_INTERRUPT, 202 .config_aneg = &genphy_config_aneg, 203 .read_status = &genphy_read_status, 204 .ack_interrupt = &rtl821x_ack_interrupt, 205 .config_intr = &rtl8211e_config_intr, 206 .suspend = genphy_suspend, 207 .resume = genphy_resume, 208 }, { 209 .phy_id = 0x001cc916, 210 .name = "RTL8211F Gigabit Ethernet", 211 .phy_id_mask = 0x001fffff, 212 .features = PHY_GBIT_FEATURES, 213 .flags = PHY_HAS_INTERRUPT, 214 .config_aneg = &genphy_config_aneg, 215 .config_init = &rtl8211f_config_init, 216 .read_status = &genphy_read_status, 217 .ack_interrupt = &rtl8211f_ack_interrupt, 218 .config_intr = &rtl8211f_config_intr, 219 .suspend = genphy_suspend, 220 .resume = genphy_resume, 221 }, 222 }; 223 224 module_phy_driver(realtek_drvs); 225 226 static struct mdio_device_id __maybe_unused realtek_tbl[] = { 227 { 0x001cc816, 0x001fffff }, 228 { 0x001cc912, 0x001fffff }, 229 { 0x001cc914, 0x001fffff }, 230 { 0x001cc915, 0x001fffff }, 231 { 0x001cc916, 0x001fffff }, 232 { } 233 }; 234 235 MODULE_DEVICE_TABLE(mdio, realtek_tbl); 236