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 phy_write(phydev, RTL821x_PAGE_SELECT, 0xa42); 119 if (phydev->interrupts == PHY_INTERRUPT_ENABLED) 120 err = phy_write(phydev, RTL821x_INER, 121 RTL8211F_INER_LINK_STATUS); 122 else 123 err = phy_write(phydev, RTL821x_INER, 0); 124 phy_write(phydev, RTL821x_PAGE_SELECT, 0); 125 126 return err; 127 } 128 129 static int rtl8211f_config_init(struct phy_device *phydev) 130 { 131 int ret; 132 u16 reg; 133 134 ret = genphy_config_init(phydev); 135 if (ret < 0) 136 return ret; 137 138 phy_write(phydev, RTL821x_PAGE_SELECT, 0xd08); 139 reg = phy_read(phydev, 0x11); 140 141 /* enable TX-delay for rgmii-id and rgmii-txid, otherwise disable it */ 142 if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID || 143 phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) 144 reg |= RTL8211F_TX_DELAY; 145 else 146 reg &= ~RTL8211F_TX_DELAY; 147 148 phy_write(phydev, 0x11, reg); 149 /* restore to default page 0 */ 150 phy_write(phydev, RTL821x_PAGE_SELECT, 0x0); 151 152 return 0; 153 } 154 155 static struct phy_driver realtek_drvs[] = { 156 { 157 .phy_id = 0x00008201, 158 .name = "RTL8201CP Ethernet", 159 .phy_id_mask = 0x0000ffff, 160 .features = PHY_BASIC_FEATURES, 161 .flags = PHY_HAS_INTERRUPT, 162 .config_aneg = &genphy_config_aneg, 163 .read_status = &genphy_read_status, 164 }, { 165 .phy_id = 0x001cc816, 166 .name = "RTL8201F 10/100Mbps Ethernet", 167 .phy_id_mask = 0x001fffff, 168 .features = PHY_BASIC_FEATURES, 169 .flags = PHY_HAS_INTERRUPT, 170 .config_aneg = &genphy_config_aneg, 171 .read_status = &genphy_read_status, 172 .ack_interrupt = &rtl8201_ack_interrupt, 173 .config_intr = &rtl8201_config_intr, 174 .suspend = genphy_suspend, 175 .resume = genphy_resume, 176 }, { 177 .phy_id = 0x001cc912, 178 .name = "RTL8211B Gigabit Ethernet", 179 .phy_id_mask = 0x001fffff, 180 .features = PHY_GBIT_FEATURES, 181 .flags = PHY_HAS_INTERRUPT, 182 .config_aneg = &genphy_config_aneg, 183 .read_status = &genphy_read_status, 184 .ack_interrupt = &rtl821x_ack_interrupt, 185 .config_intr = &rtl8211b_config_intr, 186 }, { 187 .phy_id = 0x001cc914, 188 .name = "RTL8211DN Gigabit Ethernet", 189 .phy_id_mask = 0x001fffff, 190 .features = PHY_GBIT_FEATURES, 191 .flags = PHY_HAS_INTERRUPT, 192 .config_aneg = genphy_config_aneg, 193 .read_status = genphy_read_status, 194 .ack_interrupt = rtl821x_ack_interrupt, 195 .config_intr = rtl8211e_config_intr, 196 .suspend = genphy_suspend, 197 .resume = genphy_resume, 198 }, { 199 .phy_id = 0x001cc915, 200 .name = "RTL8211E Gigabit Ethernet", 201 .phy_id_mask = 0x001fffff, 202 .features = PHY_GBIT_FEATURES, 203 .flags = PHY_HAS_INTERRUPT, 204 .config_aneg = &genphy_config_aneg, 205 .read_status = &genphy_read_status, 206 .ack_interrupt = &rtl821x_ack_interrupt, 207 .config_intr = &rtl8211e_config_intr, 208 .suspend = genphy_suspend, 209 .resume = genphy_resume, 210 }, { 211 .phy_id = 0x001cc916, 212 .name = "RTL8211F Gigabit Ethernet", 213 .phy_id_mask = 0x001fffff, 214 .features = PHY_GBIT_FEATURES, 215 .flags = PHY_HAS_INTERRUPT, 216 .config_aneg = &genphy_config_aneg, 217 .config_init = &rtl8211f_config_init, 218 .read_status = &genphy_read_status, 219 .ack_interrupt = &rtl8211f_ack_interrupt, 220 .config_intr = &rtl8211f_config_intr, 221 .suspend = genphy_suspend, 222 .resume = genphy_resume, 223 }, 224 }; 225 226 module_phy_driver(realtek_drvs); 227 228 static struct mdio_device_id __maybe_unused realtek_tbl[] = { 229 { 0x001cc816, 0x001fffff }, 230 { 0x001cc912, 0x001fffff }, 231 { 0x001cc914, 0x001fffff }, 232 { 0x001cc915, 0x001fffff }, 233 { 0x001cc916, 0x001fffff }, 234 { } 235 }; 236 237 MODULE_DEVICE_TABLE(mdio, realtek_tbl); 238