Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v3.14-rc2 270 lines 6.4 kB view raw
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_DEBUG_ADDR 0x1D 31#define AT803X_DEBUG_DATA 0x1E 32#define AT803X_DEBUG_SYSTEM_MODE_CTRL 0x05 33#define AT803X_DEBUG_RGMII_TX_CLK_DLY BIT(8) 34 35MODULE_DESCRIPTION("Atheros 803x PHY driver"); 36MODULE_AUTHOR("Matus Ujhelyi"); 37MODULE_LICENSE("GPL"); 38 39static int at803x_set_wol(struct phy_device *phydev, 40 struct ethtool_wolinfo *wol) 41{ 42 struct net_device *ndev = phydev->attached_dev; 43 const u8 *mac; 44 int ret; 45 u32 value; 46 unsigned int i, offsets[] = { 47 AT803X_LOC_MAC_ADDR_32_47_OFFSET, 48 AT803X_LOC_MAC_ADDR_16_31_OFFSET, 49 AT803X_LOC_MAC_ADDR_0_15_OFFSET, 50 }; 51 52 if (!ndev) 53 return -ENODEV; 54 55 if (wol->wolopts & WAKE_MAGIC) { 56 mac = (const u8 *) ndev->dev_addr; 57 58 if (!is_valid_ether_addr(mac)) 59 return -EFAULT; 60 61 for (i = 0; i < 3; i++) { 62 phy_write(phydev, AT803X_MMD_ACCESS_CONTROL, 63 AT803X_DEVICE_ADDR); 64 phy_write(phydev, AT803X_MMD_ACCESS_CONTROL_DATA, 65 offsets[i]); 66 phy_write(phydev, AT803X_MMD_ACCESS_CONTROL, 67 AT803X_FUNC_DATA); 68 phy_write(phydev, AT803X_MMD_ACCESS_CONTROL_DATA, 69 mac[(i * 2) + 1] | (mac[(i * 2)] << 8)); 70 } 71 72 value = phy_read(phydev, AT803X_INTR_ENABLE); 73 value |= AT803X_WOL_ENABLE; 74 ret = phy_write(phydev, AT803X_INTR_ENABLE, value); 75 if (ret) 76 return ret; 77 value = phy_read(phydev, AT803X_INTR_STATUS); 78 } else { 79 value = phy_read(phydev, AT803X_INTR_ENABLE); 80 value &= (~AT803X_WOL_ENABLE); 81 ret = phy_write(phydev, AT803X_INTR_ENABLE, value); 82 if (ret) 83 return ret; 84 value = phy_read(phydev, AT803X_INTR_STATUS); 85 } 86 87 return ret; 88} 89 90static void at803x_get_wol(struct phy_device *phydev, 91 struct ethtool_wolinfo *wol) 92{ 93 u32 value; 94 95 wol->supported = WAKE_MAGIC; 96 wol->wolopts = 0; 97 98 value = phy_read(phydev, AT803X_INTR_ENABLE); 99 if (value & AT803X_WOL_ENABLE) 100 wol->wolopts |= WAKE_MAGIC; 101} 102 103static int at803x_suspend(struct phy_device *phydev) 104{ 105 int value; 106 int wol_enabled; 107 108 mutex_lock(&phydev->lock); 109 110 value = phy_read(phydev, AT803X_INTR_ENABLE); 111 wol_enabled = value & AT803X_WOL_ENABLE; 112 113 value = phy_read(phydev, MII_BMCR); 114 115 if (wol_enabled) 116 value |= BMCR_ISOLATE; 117 else 118 value |= BMCR_PDOWN; 119 120 phy_write(phydev, MII_BMCR, value); 121 122 mutex_unlock(&phydev->lock); 123 124 return 0; 125} 126 127static int at803x_resume(struct phy_device *phydev) 128{ 129 int value; 130 131 mutex_lock(&phydev->lock); 132 133 value = phy_read(phydev, MII_BMCR); 134 value &= ~(BMCR_PDOWN | BMCR_ISOLATE); 135 phy_write(phydev, MII_BMCR, value); 136 137 mutex_unlock(&phydev->lock); 138 139 return 0; 140} 141 142static int at803x_config_init(struct phy_device *phydev) 143{ 144 int val; 145 int ret; 146 u32 features; 147 148 features = SUPPORTED_TP | SUPPORTED_MII | SUPPORTED_AUI | 149 SUPPORTED_FIBRE | SUPPORTED_BNC; 150 151 val = phy_read(phydev, MII_BMSR); 152 if (val < 0) 153 return val; 154 155 if (val & BMSR_ANEGCAPABLE) 156 features |= SUPPORTED_Autoneg; 157 if (val & BMSR_100FULL) 158 features |= SUPPORTED_100baseT_Full; 159 if (val & BMSR_100HALF) 160 features |= SUPPORTED_100baseT_Half; 161 if (val & BMSR_10FULL) 162 features |= SUPPORTED_10baseT_Full; 163 if (val & BMSR_10HALF) 164 features |= SUPPORTED_10baseT_Half; 165 166 if (val & BMSR_ESTATEN) { 167 val = phy_read(phydev, MII_ESTATUS); 168 if (val < 0) 169 return val; 170 171 if (val & ESTATUS_1000_TFULL) 172 features |= SUPPORTED_1000baseT_Full; 173 if (val & ESTATUS_1000_THALF) 174 features |= SUPPORTED_1000baseT_Half; 175 } 176 177 phydev->supported = features; 178 phydev->advertising = features; 179 180 if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) { 181 ret = phy_write(phydev, AT803X_DEBUG_ADDR, 182 AT803X_DEBUG_SYSTEM_MODE_CTRL); 183 if (ret) 184 return ret; 185 ret = phy_write(phydev, AT803X_DEBUG_DATA, 186 AT803X_DEBUG_RGMII_TX_CLK_DLY); 187 if (ret) 188 return ret; 189 } 190 191 return 0; 192} 193 194static struct phy_driver at803x_driver[] = { 195{ 196 /* ATHEROS 8035 */ 197 .phy_id = 0x004dd072, 198 .name = "Atheros 8035 ethernet", 199 .phy_id_mask = 0xffffffef, 200 .config_init = at803x_config_init, 201 .set_wol = at803x_set_wol, 202 .get_wol = at803x_get_wol, 203 .suspend = at803x_suspend, 204 .resume = at803x_resume, 205 .features = PHY_GBIT_FEATURES, 206 .flags = PHY_HAS_INTERRUPT, 207 .config_aneg = genphy_config_aneg, 208 .read_status = genphy_read_status, 209 .driver = { 210 .owner = THIS_MODULE, 211 }, 212}, { 213 /* ATHEROS 8030 */ 214 .phy_id = 0x004dd076, 215 .name = "Atheros 8030 ethernet", 216 .phy_id_mask = 0xffffffef, 217 .config_init = at803x_config_init, 218 .set_wol = at803x_set_wol, 219 .get_wol = at803x_get_wol, 220 .suspend = at803x_suspend, 221 .resume = at803x_resume, 222 .features = PHY_GBIT_FEATURES, 223 .flags = PHY_HAS_INTERRUPT, 224 .config_aneg = genphy_config_aneg, 225 .read_status = genphy_read_status, 226 .driver = { 227 .owner = THIS_MODULE, 228 }, 229}, { 230 /* ATHEROS 8031 */ 231 .phy_id = 0x004dd074, 232 .name = "Atheros 8031 ethernet", 233 .phy_id_mask = 0xffffffef, 234 .config_init = at803x_config_init, 235 .set_wol = at803x_set_wol, 236 .get_wol = at803x_get_wol, 237 .suspend = at803x_suspend, 238 .resume = at803x_resume, 239 .features = PHY_GBIT_FEATURES, 240 .flags = PHY_HAS_INTERRUPT, 241 .config_aneg = genphy_config_aneg, 242 .read_status = genphy_read_status, 243 .driver = { 244 .owner = THIS_MODULE, 245 }, 246} }; 247 248static int __init atheros_init(void) 249{ 250 return phy_drivers_register(at803x_driver, 251 ARRAY_SIZE(at803x_driver)); 252} 253 254static void __exit atheros_exit(void) 255{ 256 return phy_drivers_unregister(at803x_driver, 257 ARRAY_SIZE(at803x_driver)); 258} 259 260module_init(atheros_init); 261module_exit(atheros_exit); 262 263static struct mdio_device_id __maybe_unused atheros_tbl[] = { 264 { 0x004dd076, 0xffffffef }, 265 { 0x004dd074, 0xffffffef }, 266 { 0x004dd072, 0xffffffef }, 267 { } 268}; 269 270MODULE_DEVICE_TABLE(mdio, atheros_tbl);