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_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
  73static 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
 123static u32 atl1c_get_msglevel(struct net_device *netdev)
 124{
 125        struct atl1c_adapter *adapter = netdev_priv(netdev);
 126        return adapter->msg_enable;
 127}
 128
 129static 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
 135static int atl1c_get_regs_len(struct net_device *netdev)
 136{
 137        return AT_REGS_LEN;
 138}
 139
 140static 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
 180static 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
 190static 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(sizeof(u32) *
 213                        (last_dword - first_dword + 1), 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
 232static 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
 244static 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
 264static 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
 284static 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
 292static 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
 308void atl1c_set_ethtool_ops(struct net_device *netdev)
 309{
 310        netdev->ethtool_ops = &atl1c_ethtool_ops;
 311}
 312