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