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_link_ksettings(struct net_device *netdev, 30 struct ethtool_link_ksettings *cmd) 31 { 32 struct atl1c_adapter *adapter = netdev_priv(netdev); 33 struct atl1c_hw *hw = &adapter->hw; 34 u32 supported, advertising; 35 36 supported = (SUPPORTED_10baseT_Half | 37 SUPPORTED_10baseT_Full | 38 SUPPORTED_100baseT_Half | 39 SUPPORTED_100baseT_Full | 40 SUPPORTED_Autoneg | 41 SUPPORTED_TP); 42 if (hw->link_cap_flags & ATL1C_LINK_CAP_1000M) 43 supported |= SUPPORTED_1000baseT_Full; 44 45 advertising = ADVERTISED_TP; 46 47 advertising |= hw->autoneg_advertised; 48 49 cmd->base.port = PORT_TP; 50 cmd->base.phy_address = 0; 51 52 if (adapter->link_speed != SPEED_0) { 53 cmd->base.speed = adapter->link_speed; 54 if (adapter->link_duplex == FULL_DUPLEX) 55 cmd->base.duplex = DUPLEX_FULL; 56 else 57 cmd->base.duplex = DUPLEX_HALF; 58 } else { 59 cmd->base.speed = SPEED_UNKNOWN; 60 cmd->base.duplex = DUPLEX_UNKNOWN; 61 } 62 63 cmd->base.autoneg = AUTONEG_ENABLE; 64 65 ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported, 66 supported); 67 ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising, 68 advertising); 69 70 return 0; 71 } 72 73 static int atl1c_set_link_ksettings(struct net_device *netdev, 74 const struct ethtool_link_ksettings *cmd) 75 { 76 struct atl1c_adapter *adapter = netdev_priv(netdev); 77 struct atl1c_hw *hw = &adapter->hw; 78 u16 autoneg_advertised; 79 80 while (test_and_set_bit(__AT_RESETTING, &adapter->flags)) 81 msleep(1); 82 83 if (cmd->base.autoneg == AUTONEG_ENABLE) { 84 autoneg_advertised = ADVERTISED_Autoneg; 85 } else { 86 u32 speed = cmd->base.speed; 87 if (speed == SPEED_1000) { 88 if (cmd->base.duplex != DUPLEX_FULL) { 89 if (netif_msg_link(adapter)) 90 dev_warn(&adapter->pdev->dev, 91 "1000M half is invalid\n"); 92 clear_bit(__AT_RESETTING, &adapter->flags); 93 return -EINVAL; 94 } 95 autoneg_advertised = ADVERTISED_1000baseT_Full; 96 } else if (speed == SPEED_100) { 97 if (cmd->base.duplex == DUPLEX_FULL) 98 autoneg_advertised = ADVERTISED_100baseT_Full; 99 else 100 autoneg_advertised = ADVERTISED_100baseT_Half; 101 } else { 102 if (cmd->base.duplex == DUPLEX_FULL) 103 autoneg_advertised = ADVERTISED_10baseT_Full; 104 else 105 autoneg_advertised = ADVERTISED_10baseT_Half; 106 } 107 } 108 109 if (hw->autoneg_advertised != autoneg_advertised) { 110 hw->autoneg_advertised = autoneg_advertised; 111 if (atl1c_restart_autoneg(hw) != 0) { 112 if (netif_msg_link(adapter)) 113 dev_warn(&adapter->pdev->dev, 114 "ethtool speed/duplex setting failed\n"); 115 clear_bit(__AT_RESETTING, &adapter->flags); 116 return -EINVAL; 117 } 118 } 119 clear_bit(__AT_RESETTING, &adapter->flags); 120 return 0; 121 } 122 123 static u32 atl1c_get_msglevel(struct net_device *netdev) 124 { 125 struct atl1c_adapter *adapter = netdev_priv(netdev); 126 return adapter->msg_enable; 127 } 128 129 static void atl1c_set_msglevel(struct net_device *netdev, u32 data) 130 { 131 struct atl1c_adapter *adapter = netdev_priv(netdev); 132 adapter->msg_enable = data; 133 } 134 135 static int atl1c_get_regs_len(struct net_device *netdev) 136 { 137 return AT_REGS_LEN; 138 } 139 140 static void atl1c_get_regs(struct net_device *netdev, 141 struct ethtool_regs *regs, void *p) 142 { 143 struct atl1c_adapter *adapter = netdev_priv(netdev); 144 struct atl1c_hw *hw = &adapter->hw; 145 u32 *regs_buff = p; 146 u16 phy_data; 147 148 memset(p, 0, AT_REGS_LEN); 149 150 regs->version = 1; 151 AT_READ_REG(hw, REG_PM_CTRL, p++); 152 AT_READ_REG(hw, REG_MAC_HALF_DUPLX_CTRL, p++); 153 AT_READ_REG(hw, REG_TWSI_CTRL, p++); 154 AT_READ_REG(hw, REG_PCIE_DEV_MISC_CTRL, p++); 155 AT_READ_REG(hw, REG_MASTER_CTRL, p++); 156 AT_READ_REG(hw, REG_MANUAL_TIMER_INIT, p++); 157 AT_READ_REG(hw, REG_IRQ_MODRT_TIMER_INIT, p++); 158 AT_READ_REG(hw, REG_GPHY_CTRL, p++); 159 AT_READ_REG(hw, REG_LINK_CTRL, p++); 160 AT_READ_REG(hw, REG_IDLE_STATUS, p++); 161 AT_READ_REG(hw, REG_MDIO_CTRL, p++); 162 AT_READ_REG(hw, REG_SERDES, p++); 163 AT_READ_REG(hw, REG_MAC_CTRL, p++); 164 AT_READ_REG(hw, REG_MAC_IPG_IFG, p++); 165 AT_READ_REG(hw, REG_MAC_STA_ADDR, p++); 166 AT_READ_REG(hw, REG_MAC_STA_ADDR+4, p++); 167 AT_READ_REG(hw, REG_RX_HASH_TABLE, p++); 168 AT_READ_REG(hw, REG_RX_HASH_TABLE+4, p++); 169 AT_READ_REG(hw, REG_RXQ_CTRL, p++); 170 AT_READ_REG(hw, REG_TXQ_CTRL, p++); 171 AT_READ_REG(hw, REG_MTU, p++); 172 AT_READ_REG(hw, REG_WOL_CTRL, p++); 173 174 atl1c_read_phy_reg(hw, MII_BMCR, &phy_data); 175 regs_buff[AT_REGS_LEN/sizeof(u32) - 2] = (u32) phy_data; 176 atl1c_read_phy_reg(hw, MII_BMSR, &phy_data); 177 regs_buff[AT_REGS_LEN/sizeof(u32) - 1] = (u32) phy_data; 178 } 179 180 static int atl1c_get_eeprom_len(struct net_device *netdev) 181 { 182 struct atl1c_adapter *adapter = netdev_priv(netdev); 183 184 if (atl1c_check_eeprom_exist(&adapter->hw)) 185 return AT_EEPROM_LEN; 186 else 187 return 0; 188 } 189 190 static int atl1c_get_eeprom(struct net_device *netdev, 191 struct ethtool_eeprom *eeprom, u8 *bytes) 192 { 193 struct atl1c_adapter *adapter = netdev_priv(netdev); 194 struct atl1c_hw *hw = &adapter->hw; 195 u32 *eeprom_buff; 196 int first_dword, last_dword; 197 int ret_val = 0; 198 int i; 199 200 if (eeprom->len == 0) 201 return -EINVAL; 202 203 if (!atl1c_check_eeprom_exist(hw)) /* not exist */ 204 return -EINVAL; 205 206 eeprom->magic = adapter->pdev->vendor | 207 (adapter->pdev->device << 16); 208 209 first_dword = eeprom->offset >> 2; 210 last_dword = (eeprom->offset + eeprom->len - 1) >> 2; 211 212 eeprom_buff = kmalloc_array(last_dword - first_dword + 1, sizeof(u32), 213 GFP_KERNEL); 214 if (eeprom_buff == NULL) 215 return -ENOMEM; 216 217 for (i = first_dword; i < last_dword; i++) { 218 if (!atl1c_read_eeprom(hw, i * 4, &(eeprom_buff[i-first_dword]))) { 219 kfree(eeprom_buff); 220 return -EIO; 221 } 222 } 223 224 memcpy(bytes, (u8 *)eeprom_buff + (eeprom->offset & 3), 225 eeprom->len); 226 kfree(eeprom_buff); 227 228 return ret_val; 229 return 0; 230 } 231 232 static void atl1c_get_drvinfo(struct net_device *netdev, 233 struct ethtool_drvinfo *drvinfo) 234 { 235 struct atl1c_adapter *adapter = netdev_priv(netdev); 236 237 strlcpy(drvinfo->driver, atl1c_driver_name, sizeof(drvinfo->driver)); 238 strlcpy(drvinfo->version, atl1c_driver_version, 239 sizeof(drvinfo->version)); 240 strlcpy(drvinfo->bus_info, pci_name(adapter->pdev), 241 sizeof(drvinfo->bus_info)); 242 } 243 244 static void atl1c_get_wol(struct net_device *netdev, 245 struct ethtool_wolinfo *wol) 246 { 247 struct atl1c_adapter *adapter = netdev_priv(netdev); 248 249 wol->supported = WAKE_MAGIC | WAKE_PHY; 250 wol->wolopts = 0; 251 252 if (adapter->wol & AT_WUFC_EX) 253 wol->wolopts |= WAKE_UCAST; 254 if (adapter->wol & AT_WUFC_MC) 255 wol->wolopts |= WAKE_MCAST; 256 if (adapter->wol & AT_WUFC_BC) 257 wol->wolopts |= WAKE_BCAST; 258 if (adapter->wol & AT_WUFC_MAG) 259 wol->wolopts |= WAKE_MAGIC; 260 if (adapter->wol & AT_WUFC_LNKC) 261 wol->wolopts |= WAKE_PHY; 262 } 263 264 static int atl1c_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol) 265 { 266 struct atl1c_adapter *adapter = netdev_priv(netdev); 267 268 if (wol->wolopts & (WAKE_ARP | WAKE_MAGICSECURE | 269 WAKE_UCAST | WAKE_BCAST | WAKE_MCAST)) 270 return -EOPNOTSUPP; 271 /* these settings will always override what we currently have */ 272 adapter->wol = 0; 273 274 if (wol->wolopts & WAKE_MAGIC) 275 adapter->wol |= AT_WUFC_MAG; 276 if (wol->wolopts & WAKE_PHY) 277 adapter->wol |= AT_WUFC_LNKC; 278 279 device_set_wakeup_enable(&adapter->pdev->dev, adapter->wol); 280 281 return 0; 282 } 283 284 static int atl1c_nway_reset(struct net_device *netdev) 285 { 286 struct atl1c_adapter *adapter = netdev_priv(netdev); 287 if (netif_running(netdev)) 288 atl1c_reinit_locked(adapter); 289 return 0; 290 } 291 292 static const struct ethtool_ops atl1c_ethtool_ops = { 293 .get_drvinfo = atl1c_get_drvinfo, 294 .get_regs_len = atl1c_get_regs_len, 295 .get_regs = atl1c_get_regs, 296 .get_wol = atl1c_get_wol, 297 .set_wol = atl1c_set_wol, 298 .get_msglevel = atl1c_get_msglevel, 299 .set_msglevel = atl1c_set_msglevel, 300 .nway_reset = atl1c_nway_reset, 301 .get_link = ethtool_op_get_link, 302 .get_eeprom_len = atl1c_get_eeprom_len, 303 .get_eeprom = atl1c_get_eeprom, 304 .get_link_ksettings = atl1c_get_link_ksettings, 305 .set_link_ksettings = atl1c_set_link_ksettings, 306 }; 307 308 void atl1c_set_ethtool_ops(struct net_device *netdev) 309 { 310 netdev->ethtool_ops = &atl1c_ethtool_ops; 311 } 312