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 31 MODULE_DESCRIPTION("Atheros 803x PHY driver"); 32 MODULE_AUTHOR("Matus Ujhelyi"); 33 MODULE_LICENSE("GPL"); 34 35 static void at803x_set_wol_mac_addr(struct phy_device *phydev) 36 { 37 struct net_device *ndev = phydev->attached_dev; 38 const u8 *mac; 39 unsigned int i, offsets[] = { 40 AT803X_LOC_MAC_ADDR_32_47_OFFSET, 41 AT803X_LOC_MAC_ADDR_16_31_OFFSET, 42 AT803X_LOC_MAC_ADDR_0_15_OFFSET, 43 }; 44 45 if (!ndev) 46 return; 47 48 mac = (const u8 *) ndev->dev_addr; 49 50 if (!is_valid_ether_addr(mac)) 51 return; 52 53 for (i = 0; i < 3; i++) { 54 phy_write(phydev, AT803X_MMD_ACCESS_CONTROL, 55 AT803X_DEVICE_ADDR); 56 phy_write(phydev, AT803X_MMD_ACCESS_CONTROL_DATA, 57 offsets[i]); 58 phy_write(phydev, AT803X_MMD_ACCESS_CONTROL, 59 AT803X_FUNC_DATA); 60 phy_write(phydev, AT803X_MMD_ACCESS_CONTROL_DATA, 61 mac[(i * 2) + 1] | (mac[(i * 2)] << 8)); 62 } 63 } 64 65 static int at803x_config_init(struct phy_device *phydev) 66 { 67 int val; 68 u32 features; 69 int status; 70 71 features = SUPPORTED_TP | SUPPORTED_MII | SUPPORTED_AUI | 72 SUPPORTED_FIBRE | SUPPORTED_BNC; 73 74 val = phy_read(phydev, MII_BMSR); 75 if (val < 0) 76 return val; 77 78 if (val & BMSR_ANEGCAPABLE) 79 features |= SUPPORTED_Autoneg; 80 if (val & BMSR_100FULL) 81 features |= SUPPORTED_100baseT_Full; 82 if (val & BMSR_100HALF) 83 features |= SUPPORTED_100baseT_Half; 84 if (val & BMSR_10FULL) 85 features |= SUPPORTED_10baseT_Full; 86 if (val & BMSR_10HALF) 87 features |= SUPPORTED_10baseT_Half; 88 89 if (val & BMSR_ESTATEN) { 90 val = phy_read(phydev, MII_ESTATUS); 91 if (val < 0) 92 return val; 93 94 if (val & ESTATUS_1000_TFULL) 95 features |= SUPPORTED_1000baseT_Full; 96 if (val & ESTATUS_1000_THALF) 97 features |= SUPPORTED_1000baseT_Half; 98 } 99 100 phydev->supported = features; 101 phydev->advertising = features; 102 103 /* enable WOL */ 104 at803x_set_wol_mac_addr(phydev); 105 status = phy_write(phydev, AT803X_INTR_ENABLE, AT803X_WOL_ENABLE); 106 status = phy_read(phydev, AT803X_INTR_STATUS); 107 108 return 0; 109 } 110 111 /* ATHEROS 8035 */ 112 static struct phy_driver at8035_driver = { 113 .phy_id = 0x004dd072, 114 .name = "Atheros 8035 ethernet", 115 .phy_id_mask = 0xffffffef, 116 .config_init = at803x_config_init, 117 .features = PHY_GBIT_FEATURES, 118 .flags = PHY_HAS_INTERRUPT, 119 .config_aneg = &genphy_config_aneg, 120 .read_status = &genphy_read_status, 121 .driver = { 122 .owner = THIS_MODULE, 123 }, 124 }; 125 126 /* ATHEROS 8030 */ 127 static struct phy_driver at8030_driver = { 128 .phy_id = 0x004dd076, 129 .name = "Atheros 8030 ethernet", 130 .phy_id_mask = 0xffffffef, 131 .config_init = at803x_config_init, 132 .features = PHY_GBIT_FEATURES, 133 .flags = PHY_HAS_INTERRUPT, 134 .config_aneg = &genphy_config_aneg, 135 .read_status = &genphy_read_status, 136 .driver = { 137 .owner = THIS_MODULE, 138 }, 139 }; 140 141 static int __init atheros_init(void) 142 { 143 int ret; 144 145 ret = phy_driver_register(&at8035_driver); 146 if (ret) 147 goto err1; 148 149 ret = phy_driver_register(&at8030_driver); 150 if (ret) 151 goto err2; 152 153 return 0; 154 155 err2: 156 phy_driver_unregister(&at8035_driver); 157 err1: 158 return ret; 159 } 160 161 static void __exit atheros_exit(void) 162 { 163 phy_driver_unregister(&at8035_driver); 164 phy_driver_unregister(&at8030_driver); 165 } 166 167 module_init(atheros_init); 168 module_exit(atheros_exit); 169 170 static struct mdio_device_id __maybe_unused atheros_tbl[] = { 171 { 0x004dd076, 0xffffffef }, 172 { 0x004dd072, 0xffffffef }, 173 { } 174 }; 175 176 MODULE_DEVICE_TABLE(mdio, atheros_tbl); 177