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 ret; 148 149 ret = genphy_config_init(phydev); 150 if (ret < 0) 151 return ret; 152 153 if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) { 154 ret = phy_write(phydev, AT803X_DEBUG_ADDR, 155 AT803X_DEBUG_SYSTEM_MODE_CTRL); 156 if (ret) 157 return ret; 158 ret = phy_write(phydev, AT803X_DEBUG_DATA, 159 AT803X_DEBUG_RGMII_TX_CLK_DLY); 160 if (ret) 161 return ret; 162 } 163 164 return 0; 165 } 166 167 static int at803x_ack_interrupt(struct phy_device *phydev) 168 { 169 int err; 170 171 err = phy_read(phydev, AT803X_INSR); 172 173 return (err < 0) ? err : 0; 174 } 175 176 static int at803x_config_intr(struct phy_device *phydev) 177 { 178 int err; 179 int value; 180 181 value = phy_read(phydev, AT803X_INER); 182 183 if (phydev->interrupts == PHY_INTERRUPT_ENABLED) 184 err = phy_write(phydev, AT803X_INER, 185 value | AT803X_INER_INIT); 186 else 187 err = phy_write(phydev, AT803X_INER, 0); 188 189 return err; 190 } 191 192 static struct phy_driver at803x_driver[] = { 193 { 194 /* ATHEROS 8035 */ 195 .phy_id = 0x004dd072, 196 .name = "Atheros 8035 ethernet", 197 .phy_id_mask = 0xffffffef, 198 .config_init = at803x_config_init, 199 .set_wol = at803x_set_wol, 200 .get_wol = at803x_get_wol, 201 .suspend = at803x_suspend, 202 .resume = at803x_resume, 203 .features = PHY_GBIT_FEATURES, 204 .flags = PHY_HAS_INTERRUPT, 205 .config_aneg = genphy_config_aneg, 206 .read_status = genphy_read_status, 207 .driver = { 208 .owner = THIS_MODULE, 209 }, 210 }, { 211 /* ATHEROS 8030 */ 212 .phy_id = 0x004dd076, 213 .name = "Atheros 8030 ethernet", 214 .phy_id_mask = 0xffffffef, 215 .config_init = at803x_config_init, 216 .set_wol = at803x_set_wol, 217 .get_wol = at803x_get_wol, 218 .suspend = at803x_suspend, 219 .resume = at803x_resume, 220 .features = PHY_GBIT_FEATURES, 221 .flags = PHY_HAS_INTERRUPT, 222 .config_aneg = genphy_config_aneg, 223 .read_status = genphy_read_status, 224 .driver = { 225 .owner = THIS_MODULE, 226 }, 227 }, { 228 /* ATHEROS 8031 */ 229 .phy_id = 0x004dd074, 230 .name = "Atheros 8031 ethernet", 231 .phy_id_mask = 0xffffffef, 232 .config_init = at803x_config_init, 233 .set_wol = at803x_set_wol, 234 .get_wol = at803x_get_wol, 235 .suspend = at803x_suspend, 236 .resume = at803x_resume, 237 .features = PHY_GBIT_FEATURES, 238 .flags = PHY_HAS_INTERRUPT, 239 .config_aneg = genphy_config_aneg, 240 .read_status = genphy_read_status, 241 .ack_interrupt = &at803x_ack_interrupt, 242 .config_intr = &at803x_config_intr, 243 .driver = { 244 .owner = THIS_MODULE, 245 }, 246 } }; 247 248 static int __init atheros_init(void) 249 { 250 return phy_drivers_register(at803x_driver, 251 ARRAY_SIZE(at803x_driver)); 252 } 253 254 static void __exit atheros_exit(void) 255 { 256 phy_drivers_unregister(at803x_driver, ARRAY_SIZE(at803x_driver)); 257 } 258 259 module_init(atheros_init); 260 module_exit(atheros_exit); 261 262 static struct mdio_device_id __maybe_unused atheros_tbl[] = { 263 { 0x004dd076, 0xffffffef }, 264 { 0x004dd074, 0xffffffef }, 265 { 0x004dd072, 0xffffffef }, 266 { } 267 }; 268 269 MODULE_DEVICE_TABLE(mdio, atheros_tbl); 270