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 35 MODULE_DESCRIPTION("Atheros 803x PHY driver"); 36 MODULE_AUTHOR("Matus Ujhelyi"); 37 MODULE_LICENSE("GPL"); 38 39 static 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 90 static 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 103 static int at803x_config_init(struct phy_device *phydev) 104 { 105 int val; 106 int ret; 107 u32 features; 108 109 features = SUPPORTED_TP | SUPPORTED_MII | SUPPORTED_AUI | 110 SUPPORTED_FIBRE | SUPPORTED_BNC; 111 112 val = phy_read(phydev, MII_BMSR); 113 if (val < 0) 114 return val; 115 116 if (val & BMSR_ANEGCAPABLE) 117 features |= SUPPORTED_Autoneg; 118 if (val & BMSR_100FULL) 119 features |= SUPPORTED_100baseT_Full; 120 if (val & BMSR_100HALF) 121 features |= SUPPORTED_100baseT_Half; 122 if (val & BMSR_10FULL) 123 features |= SUPPORTED_10baseT_Full; 124 if (val & BMSR_10HALF) 125 features |= SUPPORTED_10baseT_Half; 126 127 if (val & BMSR_ESTATEN) { 128 val = phy_read(phydev, MII_ESTATUS); 129 if (val < 0) 130 return val; 131 132 if (val & ESTATUS_1000_TFULL) 133 features |= SUPPORTED_1000baseT_Full; 134 if (val & ESTATUS_1000_THALF) 135 features |= SUPPORTED_1000baseT_Half; 136 } 137 138 phydev->supported = features; 139 phydev->advertising = features; 140 141 if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) { 142 ret = phy_write(phydev, AT803X_DEBUG_ADDR, 143 AT803X_DEBUG_SYSTEM_MODE_CTRL); 144 if (ret) 145 return ret; 146 ret = phy_write(phydev, AT803X_DEBUG_DATA, 147 AT803X_DEBUG_RGMII_TX_CLK_DLY); 148 if (ret) 149 return ret; 150 } 151 152 return 0; 153 } 154 155 static struct phy_driver at803x_driver[] = { 156 { 157 /* ATHEROS 8035 */ 158 .phy_id = 0x004dd072, 159 .name = "Atheros 8035 ethernet", 160 .phy_id_mask = 0xffffffef, 161 .config_init = at803x_config_init, 162 .set_wol = at803x_set_wol, 163 .get_wol = at803x_get_wol, 164 .features = PHY_GBIT_FEATURES, 165 .flags = PHY_HAS_INTERRUPT, 166 .config_aneg = &genphy_config_aneg, 167 .read_status = &genphy_read_status, 168 .driver = { 169 .owner = THIS_MODULE, 170 }, 171 }, { 172 /* ATHEROS 8030 */ 173 .phy_id = 0x004dd076, 174 .name = "Atheros 8030 ethernet", 175 .phy_id_mask = 0xffffffef, 176 .config_init = at803x_config_init, 177 .set_wol = at803x_set_wol, 178 .get_wol = at803x_get_wol, 179 .features = PHY_GBIT_FEATURES, 180 .flags = PHY_HAS_INTERRUPT, 181 .config_aneg = &genphy_config_aneg, 182 .read_status = &genphy_read_status, 183 .driver = { 184 .owner = THIS_MODULE, 185 }, 186 }, { 187 /* ATHEROS 8031 */ 188 .phy_id = 0x004dd074, 189 .name = "Atheros 8031 ethernet", 190 .phy_id_mask = 0xffffffef, 191 .config_init = at803x_config_init, 192 .set_wol = at803x_set_wol, 193 .get_wol = at803x_get_wol, 194 .features = PHY_GBIT_FEATURES, 195 .flags = PHY_HAS_INTERRUPT, 196 .config_aneg = &genphy_config_aneg, 197 .read_status = &genphy_read_status, 198 .driver = { 199 .owner = THIS_MODULE, 200 }, 201 } }; 202 203 static int __init atheros_init(void) 204 { 205 return phy_drivers_register(at803x_driver, 206 ARRAY_SIZE(at803x_driver)); 207 } 208 209 static void __exit atheros_exit(void) 210 { 211 return phy_drivers_unregister(at803x_driver, 212 ARRAY_SIZE(at803x_driver)); 213 } 214 215 module_init(atheros_init); 216 module_exit(atheros_exit); 217 218 static struct mdio_device_id __maybe_unused atheros_tbl[] = { 219 { 0x004dd076, 0xffffffef }, 220 { 0x004dd074, 0xffffffef }, 221 { 0x004dd072, 0xffffffef }, 222 { } 223 }; 224 225 MODULE_DEVICE_TABLE(mdio, atheros_tbl); 226