1 /* 2 * drivers/net/phy/at803x.c 3 * 4 * Driver for Atheros 803x PHY 5 * 6 * Author: Matus Ujhelyi <ujhelyi.m@gmail.com> 7 * 8 * This program is free software; you can redistribute it and/or modify it 9 * under the terms of the GNU General Public License as published by the 10 * Free Software Foundation; either version 2 of the License, or (at your 11 * option) any later version. 12 */ 13 14 #include <linux/phy.h> 15 #include <linux/module.h> 16 #include <linux/string.h> 17 #include <linux/netdevice.h> 18 #include <linux/etherdevice.h> 19 20 #define AT803X_INTR_ENABLE 0x12 21 #define AT803X_INTR_STATUS 0x13 22 #define AT803X_WOL_ENABLE 0x01 23 #define AT803X_DEVICE_ADDR 0x03 24 #define AT803X_LOC_MAC_ADDR_0_15_OFFSET 0x804C 25 #define AT803X_LOC_MAC_ADDR_16_31_OFFSET 0x804B 26 #define AT803X_LOC_MAC_ADDR_32_47_OFFSET 0x804A 27 #define AT803X_MMD_ACCESS_CONTROL 0x0D 28 #define AT803X_MMD_ACCESS_CONTROL_DATA 0x0E 29 #define AT803X_FUNC_DATA 0x4003 30 #define AT803X_INER 0x0012 31 #define AT803X_INER_INIT 0xec00 32 #define AT803X_INSR 0x0013 33 #define AT803X_DEBUG_ADDR 0x1D 34 #define AT803X_DEBUG_DATA 0x1E 35 #define AT803X_DEBUG_SYSTEM_MODE_CTRL 0x05 36 #define AT803X_DEBUG_RGMII_TX_CLK_DLY BIT(8) 37 38 MODULE_DESCRIPTION("Atheros 803x PHY driver"); 39 MODULE_AUTHOR("Matus Ujhelyi"); 40 MODULE_LICENSE("GPL"); 41 42 static int at803x_set_wol(struct phy_device *phydev, 43 struct ethtool_wolinfo *wol) 44 { 45 struct net_device *ndev = phydev->attached_dev; 46 const u8 *mac; 47 int ret; 48 u32 value; 49 unsigned int i, offsets[] = { 50 AT803X_LOC_MAC_ADDR_32_47_OFFSET, 51 AT803X_LOC_MAC_ADDR_16_31_OFFSET, 52 AT803X_LOC_MAC_ADDR_0_15_OFFSET, 53 }; 54 55 if (!ndev) 56 return -ENODEV; 57 58 if (wol->wolopts & WAKE_MAGIC) { 59 mac = (const u8 *) ndev->dev_addr; 60 61 if (!is_valid_ether_addr(mac)) 62 return -EFAULT; 63 64 for (i = 0; i < 3; i++) { 65 phy_write(phydev, AT803X_MMD_ACCESS_CONTROL, 66 AT803X_DEVICE_ADDR); 67 phy_write(phydev, AT803X_MMD_ACCESS_CONTROL_DATA, 68 offsets[i]); 69 phy_write(phydev, AT803X_MMD_ACCESS_CONTROL, 70 AT803X_FUNC_DATA); 71 phy_write(phydev, AT803X_MMD_ACCESS_CONTROL_DATA, 72 mac[(i * 2) + 1] | (mac[(i * 2)] << 8)); 73 } 74 75 value = phy_read(phydev, AT803X_INTR_ENABLE); 76 value |= AT803X_WOL_ENABLE; 77 ret = phy_write(phydev, AT803X_INTR_ENABLE, value); 78 if (ret) 79 return ret; 80 value = phy_read(phydev, AT803X_INTR_STATUS); 81 } else { 82 value = phy_read(phydev, AT803X_INTR_ENABLE); 83 value &= (~AT803X_WOL_ENABLE); 84 ret = phy_write(phydev, AT803X_INTR_ENABLE, value); 85 if (ret) 86 return ret; 87 value = phy_read(phydev, AT803X_INTR_STATUS); 88 } 89 90 return ret; 91 } 92 93 static void at803x_get_wol(struct phy_device *phydev, 94 struct ethtool_wolinfo *wol) 95 { 96 u32 value; 97 98 wol->supported = WAKE_MAGIC; 99 wol->wolopts = 0; 100 101 value = phy_read(phydev, AT803X_INTR_ENABLE); 102 if (value & AT803X_WOL_ENABLE) 103 wol->wolopts |= WAKE_MAGIC; 104 } 105 106 static int at803x_suspend(struct phy_device *phydev) 107 { 108 int value; 109 int wol_enabled; 110 111 mutex_lock(&phydev->lock); 112 113 value = phy_read(phydev, AT803X_INTR_ENABLE); 114 wol_enabled = value & AT803X_WOL_ENABLE; 115 116 value = phy_read(phydev, MII_BMCR); 117 118 if (wol_enabled) 119 value |= BMCR_ISOLATE; 120 else 121 value |= BMCR_PDOWN; 122 123 phy_write(phydev, MII_BMCR, value); 124 125 mutex_unlock(&phydev->lock); 126 127 return 0; 128 } 129 130 static int at803x_resume(struct phy_device *phydev) 131 { 132 int value; 133 134 mutex_lock(&phydev->lock); 135 136 value = phy_read(phydev, MII_BMCR); 137 value &= ~(BMCR_PDOWN | BMCR_ISOLATE); 138 phy_write(phydev, MII_BMCR, value); 139 140 mutex_unlock(&phydev->lock); 141 142 return 0; 143 } 144 145 static int at803x_config_init(struct phy_device *phydev) 146 { 147 int val; 148 int ret; 149 u32 features; 150 151 features = SUPPORTED_TP | SUPPORTED_MII | SUPPORTED_AUI | 152 SUPPORTED_FIBRE | SUPPORTED_BNC; 153 154 val = phy_read(phydev, MII_BMSR); 155 if (val < 0) 156 return val; 157 158 if (val & BMSR_ANEGCAPABLE) 159 features |= SUPPORTED_Autoneg; 160 if (val & BMSR_100FULL) 161 features |= SUPPORTED_100baseT_Full; 162 if (val & BMSR_100HALF) 163 features |= SUPPORTED_100baseT_Half; 164 if (val & BMSR_10FULL) 165 features |= SUPPORTED_10baseT_Full; 166 if (val & BMSR_10HALF) 167 features |= SUPPORTED_10baseT_Half; 168 169 if (val & BMSR_ESTATEN) { 170 val = phy_read(phydev, MII_ESTATUS); 171 if (val < 0) 172 return val; 173 174 if (val & ESTATUS_1000_TFULL) 175 features |= SUPPORTED_1000baseT_Full; 176 if (val & ESTATUS_1000_THALF) 177 features |= SUPPORTED_1000baseT_Half; 178 } 179 180 phydev->supported = features; 181 phydev->advertising = features; 182 183 if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) { 184 ret = phy_write(phydev, AT803X_DEBUG_ADDR, 185 AT803X_DEBUG_SYSTEM_MODE_CTRL); 186 if (ret) 187 return ret; 188 ret = phy_write(phydev, AT803X_DEBUG_DATA, 189 AT803X_DEBUG_RGMII_TX_CLK_DLY); 190 if (ret) 191 return ret; 192 } 193 194 return 0; 195 } 196 197 static int at803x_ack_interrupt(struct phy_device *phydev) 198 { 199 int err; 200 201 err = phy_read(phydev, AT803X_INSR); 202 203 return (err < 0) ? err : 0; 204 } 205 206 static int at803x_config_intr(struct phy_device *phydev) 207 { 208 int err; 209 int value; 210 211 value = phy_read(phydev, AT803X_INER); 212 213 if (phydev->interrupts == PHY_INTERRUPT_ENABLED) 214 err = phy_write(phydev, AT803X_INER, 215 value | AT803X_INER_INIT); 216 else 217 err = phy_write(phydev, AT803X_INER, 0); 218 219 return err; 220 } 221 222 static struct phy_driver at803x_driver[] = { 223 { 224 /* ATHEROS 8035 */ 225 .phy_id = 0x004dd072, 226 .name = "Atheros 8035 ethernet", 227 .phy_id_mask = 0xffffffef, 228 .config_init = at803x_config_init, 229 .set_wol = at803x_set_wol, 230 .get_wol = at803x_get_wol, 231 .suspend = at803x_suspend, 232 .resume = at803x_resume, 233 .features = PHY_GBIT_FEATURES, 234 .flags = PHY_HAS_INTERRUPT, 235 .config_aneg = genphy_config_aneg, 236 .read_status = genphy_read_status, 237 .driver = { 238 .owner = THIS_MODULE, 239 }, 240 }, { 241 /* ATHEROS 8030 */ 242 .phy_id = 0x004dd076, 243 .name = "Atheros 8030 ethernet", 244 .phy_id_mask = 0xffffffef, 245 .config_init = at803x_config_init, 246 .set_wol = at803x_set_wol, 247 .get_wol = at803x_get_wol, 248 .suspend = at803x_suspend, 249 .resume = at803x_resume, 250 .features = PHY_GBIT_FEATURES, 251 .flags = PHY_HAS_INTERRUPT, 252 .config_aneg = genphy_config_aneg, 253 .read_status = genphy_read_status, 254 .driver = { 255 .owner = THIS_MODULE, 256 }, 257 }, { 258 /* ATHEROS 8031 */ 259 .phy_id = 0x004dd074, 260 .name = "Atheros 8031 ethernet", 261 .phy_id_mask = 0xffffffef, 262 .config_init = at803x_config_init, 263 .set_wol = at803x_set_wol, 264 .get_wol = at803x_get_wol, 265 .suspend = at803x_suspend, 266 .resume = at803x_resume, 267 .features = PHY_GBIT_FEATURES, 268 .flags = PHY_HAS_INTERRUPT, 269 .config_aneg = genphy_config_aneg, 270 .read_status = genphy_read_status, 271 .ack_interrupt = &at803x_ack_interrupt, 272 .config_intr = &at803x_config_intr, 273 .driver = { 274 .owner = THIS_MODULE, 275 }, 276 } }; 277 278 static int __init atheros_init(void) 279 { 280 return phy_drivers_register(at803x_driver, 281 ARRAY_SIZE(at803x_driver)); 282 } 283 284 static void __exit atheros_exit(void) 285 { 286 return phy_drivers_unregister(at803x_driver, 287 ARRAY_SIZE(at803x_driver)); 288 } 289 290 module_init(atheros_init); 291 module_exit(atheros_exit); 292 293 static struct mdio_device_id __maybe_unused atheros_tbl[] = { 294 { 0x004dd076, 0xffffffef }, 295 { 0x004dd074, 0xffffffef }, 296 { 0x004dd072, 0xffffffef }, 297 { } 298 }; 299 300 MODULE_DEVICE_TABLE(mdio, atheros_tbl); 301