linux/drivers/net/ethernet/atheros/atl1c/atl1c_ethtool.c
<<
>>
Prefs
   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
  29static 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
  67static 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
 117static u32 atl1c_get_msglevel(struct net_device *netdev)
 118{
 119        struct atl1c_adapter *adapter = netdev_priv(netdev);
 120        return adapter->msg_enable;
 121}
 122
 123static 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
 129static int atl1c_get_regs_len(struct net_device *netdev)
 130{
 131        return AT_REGS_LEN;
 132}
 133
 134static 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 = 1;
 145        AT_READ_REG(hw, REG_PM_CTRL,              p++);
 146        AT_READ_REG(hw, REG_MAC_HALF_DUPLX_CTRL,  p++);
 147        AT_READ_REG(hw, REG_TWSI_CTRL,            p++);
 148        AT_READ_REG(hw, REG_PCIE_DEV_MISC_CTRL,   p++);
 149        AT_READ_REG(hw, REG_MASTER_CTRL,          p++);
 150        AT_READ_REG(hw, REG_MANUAL_TIMER_INIT,    p++);
 151        AT_READ_REG(hw, REG_IRQ_MODRT_TIMER_INIT, p++);
 152        AT_READ_REG(hw, REG_GPHY_CTRL,            p++);
 153        AT_READ_REG(hw, REG_LINK_CTRL,            p++);
 154        AT_READ_REG(hw, REG_IDLE_STATUS,          p++);
 155        AT_READ_REG(hw, REG_MDIO_CTRL,            p++);
 156        AT_READ_REG(hw, REG_SERDES,               p++);
 157        AT_READ_REG(hw, REG_MAC_CTRL,             p++);
 158        AT_READ_REG(hw, REG_MAC_IPG_IFG,          p++);
 159        AT_READ_REG(hw, REG_MAC_STA_ADDR,         p++);
 160        AT_READ_REG(hw, REG_MAC_STA_ADDR+4,       p++);
 161        AT_READ_REG(hw, REG_RX_HASH_TABLE,        p++);
 162        AT_READ_REG(hw, REG_RX_HASH_TABLE+4,      p++);
 163        AT_READ_REG(hw, REG_RXQ_CTRL,             p++);
 164        AT_READ_REG(hw, REG_TXQ_CTRL,             p++);
 165        AT_READ_REG(hw, REG_MTU,                  p++);
 166        AT_READ_REG(hw, REG_WOL_CTRL,             p++);
 167
 168        atl1c_read_phy_reg(hw, MII_BMCR, &phy_data);
 169        regs_buff[AT_REGS_LEN/sizeof(u32) - 2] = (u32) phy_data;
 170        atl1c_read_phy_reg(hw, MII_BMSR, &phy_data);
 171        regs_buff[AT_REGS_LEN/sizeof(u32) - 1] = (u32) phy_data;
 172}
 173
 174static int atl1c_get_eeprom_len(struct net_device *netdev)
 175{
 176        struct atl1c_adapter *adapter = netdev_priv(netdev);
 177
 178        if (atl1c_check_eeprom_exist(&adapter->hw))
 179                return AT_EEPROM_LEN;
 180        else
 181                return 0;
 182}
 183
 184static int atl1c_get_eeprom(struct net_device *netdev,
 185                struct ethtool_eeprom *eeprom, u8 *bytes)
 186{
 187        struct atl1c_adapter *adapter = netdev_priv(netdev);
 188        struct atl1c_hw *hw = &adapter->hw;
 189        u32 *eeprom_buff;
 190        int first_dword, last_dword;
 191        int ret_val = 0;
 192        int i;
 193
 194        if (eeprom->len == 0)
 195                return -EINVAL;
 196
 197        if (!atl1c_check_eeprom_exist(hw)) /* not exist */
 198                return -EINVAL;
 199
 200        eeprom->magic = adapter->pdev->vendor |
 201                        (adapter->pdev->device << 16);
 202
 203        first_dword = eeprom->offset >> 2;
 204        last_dword = (eeprom->offset + eeprom->len - 1) >> 2;
 205
 206        eeprom_buff = kmalloc(sizeof(u32) *
 207                        (last_dword - first_dword + 1), GFP_KERNEL);
 208        if (eeprom_buff == NULL)
 209                return -ENOMEM;
 210
 211        for (i = first_dword; i < last_dword; i++) {
 212                if (!atl1c_read_eeprom(hw, i * 4, &(eeprom_buff[i-first_dword]))) {
 213                        kfree(eeprom_buff);
 214                        return -EIO;
 215                }
 216        }
 217
 218        memcpy(bytes, (u8 *)eeprom_buff + (eeprom->offset & 3),
 219                        eeprom->len);
 220        kfree(eeprom_buff);
 221
 222        return ret_val;
 223        return 0;
 224}
 225
 226static void atl1c_get_drvinfo(struct net_device *netdev,
 227                struct ethtool_drvinfo *drvinfo)
 228{
 229        struct atl1c_adapter *adapter = netdev_priv(netdev);
 230
 231        strlcpy(drvinfo->driver,  atl1c_driver_name, sizeof(drvinfo->driver));
 232        strlcpy(drvinfo->version, atl1c_driver_version,
 233                sizeof(drvinfo->version));
 234        strlcpy(drvinfo->bus_info, pci_name(adapter->pdev),
 235                sizeof(drvinfo->bus_info));
 236        drvinfo->n_stats = 0;
 237        drvinfo->testinfo_len = 0;
 238        drvinfo->regdump_len = atl1c_get_regs_len(netdev);
 239        drvinfo->eedump_len = atl1c_get_eeprom_len(netdev);
 240}
 241
 242static void atl1c_get_wol(struct net_device *netdev,
 243                          struct ethtool_wolinfo *wol)
 244{
 245        struct atl1c_adapter *adapter = netdev_priv(netdev);
 246
 247        wol->supported = WAKE_MAGIC | WAKE_PHY;
 248        wol->wolopts = 0;
 249
 250        if (adapter->wol & AT_WUFC_EX)
 251                wol->wolopts |= WAKE_UCAST;
 252        if (adapter->wol & AT_WUFC_MC)
 253                wol->wolopts |= WAKE_MCAST;
 254        if (adapter->wol & AT_WUFC_BC)
 255                wol->wolopts |= WAKE_BCAST;
 256        if (adapter->wol & AT_WUFC_MAG)
 257                wol->wolopts |= WAKE_MAGIC;
 258        if (adapter->wol & AT_WUFC_LNKC)
 259                wol->wolopts |= WAKE_PHY;
 260}
 261
 262static int atl1c_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol)
 263{
 264        struct atl1c_adapter *adapter = netdev_priv(netdev);
 265
 266        if (wol->wolopts & (WAKE_ARP | WAKE_MAGICSECURE |
 267                            WAKE_UCAST | WAKE_BCAST | WAKE_MCAST))
 268                return -EOPNOTSUPP;
 269        /* these settings will always override what we currently have */
 270        adapter->wol = 0;
 271
 272        if (wol->wolopts & WAKE_MAGIC)
 273                adapter->wol |= AT_WUFC_MAG;
 274        if (wol->wolopts & WAKE_PHY)
 275                adapter->wol |= AT_WUFC_LNKC;
 276
 277        device_set_wakeup_enable(&adapter->pdev->dev, adapter->wol);
 278
 279        return 0;
 280}
 281
 282static int atl1c_nway_reset(struct net_device *netdev)
 283{
 284        struct atl1c_adapter *adapter = netdev_priv(netdev);
 285        if (netif_running(netdev))
 286                atl1c_reinit_locked(adapter);
 287        return 0;
 288}
 289
 290static const struct ethtool_ops atl1c_ethtool_ops = {
 291        .get_settings           = atl1c_get_settings,
 292        .set_settings           = atl1c_set_settings,
 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};
 305
 306void atl1c_set_ethtool_ops(struct net_device *netdev)
 307{
 308        SET_ETHTOOL_OPS(netdev, &atl1c_ethtool_ops);
 309}
 310