1 /* 2 * Copyright(c) 2009 - 2009 Atheros Corporation. All rights reserved. 3 * 4 * Derived from Intel e1000 driver 5 * Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved. 6 * 7 * This program is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU General Public License as published by the Free 9 * Software Foundation; either version 2 of the License, or (at your option) 10 * any later version. 11 * 12 * This program is distributed in the hope that it will be useful, but WITHOUT 13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 15 * more details. 16 * 17 * You should have received a copy of the GNU General Public License along with 18 * this program; if not, write to the Free Software Foundation, Inc., 59 19 * Temple Place - Suite 330, Boston, MA 02111-1307, USA. 20 * 21 */ 22 23 #include <linux/netdevice.h> 24 #include <linux/ethtool.h> 25 #include <linux/slab.h> 26 27 #include "atl1c.h" 28 29 static int atl1c_get_settings(struct net_device *netdev, 30 struct ethtool_cmd *ecmd) 31 { 32 struct atl1c_adapter *adapter = netdev_priv(netdev); 33 struct atl1c_hw *hw = &adapter->hw; 34 35 ecmd->supported = (SUPPORTED_10baseT_Half | 36 SUPPORTED_10baseT_Full | 37 SUPPORTED_100baseT_Half | 38 SUPPORTED_100baseT_Full | 39 SUPPORTED_Autoneg | 40 SUPPORTED_TP); 41 if (hw->link_cap_flags & ATL1C_LINK_CAP_1000M) 42 ecmd->supported |= SUPPORTED_1000baseT_Full; 43 44 ecmd->advertising = ADVERTISED_TP; 45 46 ecmd->advertising |= hw->autoneg_advertised; 47 48 ecmd->port = PORT_TP; 49 ecmd->phy_address = 0; 50 ecmd->transceiver = XCVR_INTERNAL; 51 52 if (adapter->link_speed != SPEED_0) { 53 ethtool_cmd_speed_set(ecmd, adapter->link_speed); 54 if (adapter->link_duplex == FULL_DUPLEX) 55 ecmd->duplex = DUPLEX_FULL; 56 else 57 ecmd->duplex = DUPLEX_HALF; 58 } else { 59 ethtool_cmd_speed_set(ecmd, -1); 60 ecmd->duplex = -1; 61 } 62 63 ecmd->autoneg = AUTONEG_ENABLE; 64 return 0; 65 } 66 67 static int atl1c_set_settings(struct net_device *netdev, 68 struct ethtool_cmd *ecmd) 69 { 70 struct atl1c_adapter *adapter = netdev_priv(netdev); 71 struct atl1c_hw *hw = &adapter->hw; 72 u16 autoneg_advertised; 73 74 while (test_and_set_bit(__AT_RESETTING, &adapter->flags)) 75 msleep(1); 76 77 if (ecmd->autoneg == AUTONEG_ENABLE) { 78 autoneg_advertised = ADVERTISED_Autoneg; 79 } else { 80 u32 speed = ethtool_cmd_speed(ecmd); 81 if (speed == SPEED_1000) { 82 if (ecmd->duplex != DUPLEX_FULL) { 83 if (netif_msg_link(adapter)) 84 dev_warn(&adapter->pdev->dev, 85 "1000M half is invalid\n"); 86 clear_bit(__AT_RESETTING, &adapter->flags); 87 return -EINVAL; 88 } 89 autoneg_advertised = ADVERTISED_1000baseT_Full; 90 } else if (speed == SPEED_100) { 91 if (ecmd->duplex == DUPLEX_FULL) 92 autoneg_advertised = ADVERTISED_100baseT_Full; 93 else 94 autoneg_advertised = ADVERTISED_100baseT_Half; 95 } else { 96 if (ecmd->duplex == DUPLEX_FULL) 97 autoneg_advertised = ADVERTISED_10baseT_Full; 98 else 99 autoneg_advertised = ADVERTISED_10baseT_Half; 100 } 101 } 102 103 if (hw->autoneg_advertised != autoneg_advertised) { 104 hw->autoneg_advertised = autoneg_advertised; 105 if (atl1c_restart_autoneg(hw) != 0) { 106 if (netif_msg_link(adapter)) 107 dev_warn(&adapter->pdev->dev, 108 "ethtool speed/duplex setting failed\n"); 109 clear_bit(__AT_RESETTING, &adapter->flags); 110 return -EINVAL; 111 } 112 } 113 clear_bit(__AT_RESETTING, &adapter->flags); 114 return 0; 115 } 116 117 static u32 atl1c_get_msglevel(struct net_device *netdev) 118 { 119 struct atl1c_adapter *adapter = netdev_priv(netdev); 120 return adapter->msg_enable; 121 } 122 123 static void atl1c_set_msglevel(struct net_device *netdev, u32 data) 124 { 125 struct atl1c_adapter *adapter = netdev_priv(netdev); 126 adapter->msg_enable = data; 127 } 128 129 static int atl1c_get_regs_len(struct net_device *netdev) 130 { 131 return AT_REGS_LEN; 132 } 133 134 static void atl1c_get_regs(struct net_device *netdev, 135 struct ethtool_regs *regs, void *p) 136 { 137 struct atl1c_adapter *adapter = netdev_priv(netdev); 138 struct atl1c_hw *hw = &adapter->hw; 139 u32 *regs_buff = p; 140 u16 phy_data; 141 142 memset(p, 0, AT_REGS_LEN); 143 144 regs->version = 0; 145 AT_READ_REG(hw, REG_VPD_CAP, p++); 146 AT_READ_REG(hw, REG_PM_CTRL, p++); 147 AT_READ_REG(hw, REG_MAC_HALF_DUPLX_CTRL, p++); 148 AT_READ_REG(hw, REG_TWSI_CTRL, p++); 149 AT_READ_REG(hw, REG_PCIE_DEV_MISC_CTRL, p++); 150 AT_READ_REG(hw, REG_MASTER_CTRL, p++); 151 AT_READ_REG(hw, REG_MANUAL_TIMER_INIT, p++); 152 AT_READ_REG(hw, REG_IRQ_MODRT_TIMER_INIT, p++); 153 AT_READ_REG(hw, REG_GPHY_CTRL, p++); 154 AT_READ_REG(hw, REG_LINK_CTRL, p++); 155 AT_READ_REG(hw, REG_IDLE_STATUS, p++); 156 AT_READ_REG(hw, REG_MDIO_CTRL, p++); 157 AT_READ_REG(hw, REG_SERDES_LOCK, p++); 158 AT_READ_REG(hw, REG_MAC_CTRL, p++); 159 AT_READ_REG(hw, REG_MAC_IPG_IFG, p++); 160 AT_READ_REG(hw, REG_MAC_STA_ADDR, p++); 161 AT_READ_REG(hw, REG_MAC_STA_ADDR+4, p++); 162 AT_READ_REG(hw, REG_RX_HASH_TABLE, p++); 163 AT_READ_REG(hw, REG_RX_HASH_TABLE+4, p++); 164 AT_READ_REG(hw, REG_RXQ_CTRL, p++); 165 AT_READ_REG(hw, REG_TXQ_CTRL, p++); 166 AT_READ_REG(hw, REG_MTU, p++); 167 AT_READ_REG(hw, REG_WOL_CTRL, p++); 168 169 atl1c_read_phy_reg(hw, MII_BMCR, &phy_data); 170 regs_buff[73] = (u32) phy_data; 171 atl1c_read_phy_reg(hw, MII_BMSR, &phy_data); 172 regs_buff[74] = (u32) phy_data; 173 } 174 175 static int atl1c_get_eeprom_len(struct net_device *netdev) 176 { 177 struct atl1c_adapter *adapter = netdev_priv(netdev); 178 179 if (atl1c_check_eeprom_exist(&adapter->hw)) 180 return AT_EEPROM_LEN; 181 else 182 return 0; 183 } 184 185 static int atl1c_get_eeprom(struct net_device *netdev, 186 struct ethtool_eeprom *eeprom, u8 *bytes) 187 { 188 struct atl1c_adapter *adapter = netdev_priv(netdev); 189 struct atl1c_hw *hw = &adapter->hw; 190 u32 *eeprom_buff; 191 int first_dword, last_dword; 192 int ret_val = 0; 193 int i; 194 195 if (eeprom->len == 0) 196 return -EINVAL; 197 198 if (!atl1c_check_eeprom_exist(hw)) /* not exist */ 199 return -EINVAL; 200 201 eeprom->magic = adapter->pdev->vendor | 202 (adapter->pdev->device << 16); 203 204 first_dword = eeprom->offset >> 2; 205 last_dword = (eeprom->offset + eeprom->len - 1) >> 2; 206 207 eeprom_buff = kmalloc(sizeof(u32) * 208 (last_dword - first_dword + 1), GFP_KERNEL); 209 if (eeprom_buff == NULL) 210 return -ENOMEM; 211 212 for (i = first_dword; i < last_dword; i++) { 213 if (!atl1c_read_eeprom(hw, i * 4, &(eeprom_buff[i-first_dword]))) { 214 kfree(eeprom_buff); 215 return -EIO; 216 } 217 } 218 219 memcpy(bytes, (u8 *)eeprom_buff + (eeprom->offset & 3), 220 eeprom->len); 221 kfree(eeprom_buff); 222 223 return ret_val; 224 return 0; 225 } 226 227 static void atl1c_get_drvinfo(struct net_device *netdev, 228 struct ethtool_drvinfo *drvinfo) 229 { 230 struct atl1c_adapter *adapter = netdev_priv(netdev); 231 232 strlcpy(drvinfo->driver, atl1c_driver_name, sizeof(drvinfo->driver)); 233 strlcpy(drvinfo->version, atl1c_driver_version, 234 sizeof(drvinfo->version)); 235 strlcpy(drvinfo->bus_info, pci_name(adapter->pdev), 236 sizeof(drvinfo->bus_info)); 237 drvinfo->n_stats = 0; 238 drvinfo->testinfo_len = 0; 239 drvinfo->regdump_len = atl1c_get_regs_len(netdev); 240 drvinfo->eedump_len = atl1c_get_eeprom_len(netdev); 241 } 242 243 static void atl1c_get_wol(struct net_device *netdev, 244 struct ethtool_wolinfo *wol) 245 { 246 struct atl1c_adapter *adapter = netdev_priv(netdev); 247 248 wol->supported = WAKE_MAGIC | WAKE_PHY; 249 wol->wolopts = 0; 250 251 if (adapter->wol & AT_WUFC_EX) 252 wol->wolopts |= WAKE_UCAST; 253 if (adapter->wol & AT_WUFC_MC) 254 wol->wolopts |= WAKE_MCAST; 255 if (adapter->wol & AT_WUFC_BC) 256 wol->wolopts |= WAKE_BCAST; 257 if (adapter->wol & AT_WUFC_MAG) 258 wol->wolopts |= WAKE_MAGIC; 259 if (adapter->wol & AT_WUFC_LNKC) 260 wol->wolopts |= WAKE_PHY; 261 } 262 263 static int atl1c_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol) 264 { 265 struct atl1c_adapter *adapter = netdev_priv(netdev); 266 267 if (wol->wolopts & (WAKE_ARP | WAKE_MAGICSECURE | 268 WAKE_UCAST | WAKE_BCAST | WAKE_MCAST)) 269 return -EOPNOTSUPP; 270 /* these settings will always override what we currently have */ 271 adapter->wol = 0; 272 273 if (wol->wolopts & WAKE_MAGIC) 274 adapter->wol |= AT_WUFC_MAG; 275 if (wol->wolopts & WAKE_PHY) 276 adapter->wol |= AT_WUFC_LNKC; 277 278 device_set_wakeup_enable(&adapter->pdev->dev, adapter->wol); 279 280 return 0; 281 } 282 283 static int atl1c_nway_reset(struct net_device *netdev) 284 { 285 struct atl1c_adapter *adapter = netdev_priv(netdev); 286 if (netif_running(netdev)) 287 atl1c_reinit_locked(adapter); 288 return 0; 289 } 290 291 static const struct ethtool_ops atl1c_ethtool_ops = { 292 .get_settings = atl1c_get_settings, 293 .set_settings = atl1c_set_settings, 294 .get_drvinfo = atl1c_get_drvinfo, 295 .get_regs_len = atl1c_get_regs_len, 296 .get_regs = atl1c_get_regs, 297 .get_wol = atl1c_get_wol, 298 .set_wol = atl1c_set_wol, 299 .get_msglevel = atl1c_get_msglevel, 300 .set_msglevel = atl1c_set_msglevel, 301 .nway_reset = atl1c_nway_reset, 302 .get_link = ethtool_op_get_link, 303 .get_eeprom_len = atl1c_get_eeprom_len, 304 .get_eeprom = atl1c_get_eeprom, 305 }; 306 307 void atl1c_set_ethtool_ops(struct net_device *netdev) 308 { 309 SET_ETHTOOL_OPS(netdev, &atl1c_ethtool_ops); 310 } 311