linux/drivers/net/atl1e/atl1e_ethtool.c
<<
>>
Prefs
   1/*
   2 * Copyright(c) 2007 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 "atl1e.h"
  27
  28static int atl1e_get_settings(struct net_device *netdev,
  29                              struct ethtool_cmd *ecmd)
  30{
  31        struct atl1e_adapter *adapter = netdev_priv(netdev);
  32        struct atl1e_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->nic_type == athr_l1e)
  41                ecmd->supported |= SUPPORTED_1000baseT_Full;
  42
  43        ecmd->advertising = ADVERTISED_TP;
  44
  45        ecmd->advertising |= ADVERTISED_Autoneg;
  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                ecmd->speed = 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                ecmd->speed = -1;
  60                ecmd->duplex = -1;
  61        }
  62
  63        ecmd->autoneg = AUTONEG_ENABLE;
  64        return 0;
  65}
  66
  67static int atl1e_set_settings(struct net_device *netdev,
  68                              struct ethtool_cmd *ecmd)
  69{
  70        struct atl1e_adapter *adapter = netdev_priv(netdev);
  71        struct atl1e_hw *hw = &adapter->hw;
  72
  73        while (test_and_set_bit(__AT_RESETTING, &adapter->flags))
  74                msleep(1);
  75
  76        if (ecmd->autoneg == AUTONEG_ENABLE) {
  77                u16 adv4, adv9;
  78
  79                if ((ecmd->advertising&ADVERTISE_1000_FULL)) {
  80                        if (hw->nic_type == athr_l1e) {
  81                                hw->autoneg_advertised =
  82                                        ecmd->advertising & AT_ADV_MASK;
  83                        } else {
  84                                clear_bit(__AT_RESETTING, &adapter->flags);
  85                                return -EINVAL;
  86                        }
  87                } else if (ecmd->advertising&ADVERTISE_1000_HALF) {
  88                        clear_bit(__AT_RESETTING, &adapter->flags);
  89                        return -EINVAL;
  90                } else {
  91                        hw->autoneg_advertised =
  92                                ecmd->advertising & AT_ADV_MASK;
  93                }
  94                ecmd->advertising = hw->autoneg_advertised |
  95                                    ADVERTISED_TP | ADVERTISED_Autoneg;
  96
  97                adv4 = hw->mii_autoneg_adv_reg & ~MII_AR_SPEED_MASK;
  98                adv9 = hw->mii_1000t_ctrl_reg & ~MII_AT001_CR_1000T_SPEED_MASK;
  99                if (hw->autoneg_advertised & ADVERTISE_10_HALF)
 100                        adv4 |= MII_AR_10T_HD_CAPS;
 101                if (hw->autoneg_advertised & ADVERTISE_10_FULL)
 102                        adv4 |= MII_AR_10T_FD_CAPS;
 103                if (hw->autoneg_advertised & ADVERTISE_100_HALF)
 104                        adv4 |= MII_AR_100TX_HD_CAPS;
 105                if (hw->autoneg_advertised & ADVERTISE_100_FULL)
 106                        adv4 |= MII_AR_100TX_FD_CAPS;
 107                if (hw->autoneg_advertised & ADVERTISE_1000_FULL)
 108                        adv9 |= MII_AT001_CR_1000T_FD_CAPS;
 109
 110                if (adv4 != hw->mii_autoneg_adv_reg ||
 111                                adv9 != hw->mii_1000t_ctrl_reg) {
 112                        hw->mii_autoneg_adv_reg = adv4;
 113                        hw->mii_1000t_ctrl_reg = adv9;
 114                        hw->re_autoneg = true;
 115                }
 116
 117        } else {
 118                clear_bit(__AT_RESETTING, &adapter->flags);
 119                return -EINVAL;
 120        }
 121
 122        /* reset the link */
 123
 124        if (netif_running(adapter->netdev)) {
 125                atl1e_down(adapter);
 126                atl1e_up(adapter);
 127        } else
 128                atl1e_reset_hw(&adapter->hw);
 129
 130        clear_bit(__AT_RESETTING, &adapter->flags);
 131        return 0;
 132}
 133
 134static u32 atl1e_get_tx_csum(struct net_device *netdev)
 135{
 136        return (netdev->features & NETIF_F_HW_CSUM) != 0;
 137}
 138
 139static u32 atl1e_get_msglevel(struct net_device *netdev)
 140{
 141#ifdef DBG
 142        return 1;
 143#else
 144        return 0;
 145#endif
 146}
 147
 148static void atl1e_set_msglevel(struct net_device *netdev, u32 data)
 149{
 150}
 151
 152static int atl1e_get_regs_len(struct net_device *netdev)
 153{
 154        return AT_REGS_LEN * sizeof(u32);
 155}
 156
 157static void atl1e_get_regs(struct net_device *netdev,
 158                           struct ethtool_regs *regs, void *p)
 159{
 160        struct atl1e_adapter *adapter = netdev_priv(netdev);
 161        struct atl1e_hw *hw = &adapter->hw;
 162        u32 *regs_buff = p;
 163        u16 phy_data;
 164
 165        memset(p, 0, AT_REGS_LEN * sizeof(u32));
 166
 167        regs->version = (1 << 24) | (hw->revision_id << 16) | hw->device_id;
 168
 169        regs_buff[0]  = AT_READ_REG(hw, REG_VPD_CAP);
 170        regs_buff[1]  = AT_READ_REG(hw, REG_SPI_FLASH_CTRL);
 171        regs_buff[2]  = AT_READ_REG(hw, REG_SPI_FLASH_CONFIG);
 172        regs_buff[3]  = AT_READ_REG(hw, REG_TWSI_CTRL);
 173        regs_buff[4]  = AT_READ_REG(hw, REG_PCIE_DEV_MISC_CTRL);
 174        regs_buff[5]  = AT_READ_REG(hw, REG_MASTER_CTRL);
 175        regs_buff[6]  = AT_READ_REG(hw, REG_MANUAL_TIMER_INIT);
 176        regs_buff[7]  = AT_READ_REG(hw, REG_IRQ_MODU_TIMER_INIT);
 177        regs_buff[8]  = AT_READ_REG(hw, REG_GPHY_CTRL);
 178        regs_buff[9]  = AT_READ_REG(hw, REG_CMBDISDMA_TIMER);
 179        regs_buff[10] = AT_READ_REG(hw, REG_IDLE_STATUS);
 180        regs_buff[11] = AT_READ_REG(hw, REG_MDIO_CTRL);
 181        regs_buff[12] = AT_READ_REG(hw, REG_SERDES_LOCK);
 182        regs_buff[13] = AT_READ_REG(hw, REG_MAC_CTRL);
 183        regs_buff[14] = AT_READ_REG(hw, REG_MAC_IPG_IFG);
 184        regs_buff[15] = AT_READ_REG(hw, REG_MAC_STA_ADDR);
 185        regs_buff[16] = AT_READ_REG(hw, REG_MAC_STA_ADDR+4);
 186        regs_buff[17] = AT_READ_REG(hw, REG_RX_HASH_TABLE);
 187        regs_buff[18] = AT_READ_REG(hw, REG_RX_HASH_TABLE+4);
 188        regs_buff[19] = AT_READ_REG(hw, REG_MAC_HALF_DUPLX_CTRL);
 189        regs_buff[20] = AT_READ_REG(hw, REG_MTU);
 190        regs_buff[21] = AT_READ_REG(hw, REG_WOL_CTRL);
 191        regs_buff[22] = AT_READ_REG(hw, REG_SRAM_TRD_ADDR);
 192        regs_buff[23] = AT_READ_REG(hw, REG_SRAM_TRD_LEN);
 193        regs_buff[24] = AT_READ_REG(hw, REG_SRAM_RXF_ADDR);
 194        regs_buff[25] = AT_READ_REG(hw, REG_SRAM_RXF_LEN);
 195        regs_buff[26] = AT_READ_REG(hw, REG_SRAM_TXF_ADDR);
 196        regs_buff[27] = AT_READ_REG(hw, REG_SRAM_TXF_LEN);
 197        regs_buff[28] = AT_READ_REG(hw, REG_SRAM_TCPH_ADDR);
 198        regs_buff[29] = AT_READ_REG(hw, REG_SRAM_PKTH_ADDR);
 199
 200        atl1e_read_phy_reg(hw, MII_BMCR, &phy_data);
 201        regs_buff[73] = (u32)phy_data;
 202        atl1e_read_phy_reg(hw, MII_BMSR, &phy_data);
 203        regs_buff[74] = (u32)phy_data;
 204}
 205
 206static int atl1e_get_eeprom_len(struct net_device *netdev)
 207{
 208        struct atl1e_adapter *adapter = netdev_priv(netdev);
 209
 210        if (!atl1e_check_eeprom_exist(&adapter->hw))
 211                return AT_EEPROM_LEN;
 212        else
 213                return 0;
 214}
 215
 216static int atl1e_get_eeprom(struct net_device *netdev,
 217                struct ethtool_eeprom *eeprom, u8 *bytes)
 218{
 219        struct atl1e_adapter *adapter = netdev_priv(netdev);
 220        struct atl1e_hw *hw = &adapter->hw;
 221        u32 *eeprom_buff;
 222        int first_dword, last_dword;
 223        int ret_val = 0;
 224        int i;
 225
 226        if (eeprom->len == 0)
 227                return -EINVAL;
 228
 229        if (atl1e_check_eeprom_exist(hw)) /* not exist */
 230                return -EINVAL;
 231
 232        eeprom->magic = hw->vendor_id | (hw->device_id << 16);
 233
 234        first_dword = eeprom->offset >> 2;
 235        last_dword = (eeprom->offset + eeprom->len - 1) >> 2;
 236
 237        eeprom_buff = kmalloc(sizeof(u32) *
 238                        (last_dword - first_dword + 1), GFP_KERNEL);
 239        if (eeprom_buff == NULL)
 240                return -ENOMEM;
 241
 242        for (i = first_dword; i < last_dword; i++) {
 243                if (!atl1e_read_eeprom(hw, i * 4, &(eeprom_buff[i-first_dword]))) {
 244                        kfree(eeprom_buff);
 245                        return -EIO;
 246                }
 247        }
 248
 249        memcpy(bytes, (u8 *)eeprom_buff + (eeprom->offset & 3),
 250                        eeprom->len);
 251        kfree(eeprom_buff);
 252
 253        return ret_val;
 254}
 255
 256static int atl1e_set_eeprom(struct net_device *netdev,
 257                            struct ethtool_eeprom *eeprom, u8 *bytes)
 258{
 259        struct atl1e_adapter *adapter = netdev_priv(netdev);
 260        struct atl1e_hw *hw = &adapter->hw;
 261        u32 *eeprom_buff;
 262        u32 *ptr;
 263        int first_dword, last_dword;
 264        int ret_val = 0;
 265        int i;
 266
 267        if (eeprom->len == 0)
 268                return -EOPNOTSUPP;
 269
 270        if (eeprom->magic != (hw->vendor_id | (hw->device_id << 16)))
 271                return -EINVAL;
 272
 273        first_dword = eeprom->offset >> 2;
 274        last_dword = (eeprom->offset + eeprom->len - 1) >> 2;
 275        eeprom_buff = kmalloc(AT_EEPROM_LEN, GFP_KERNEL);
 276        if (eeprom_buff == NULL)
 277                return -ENOMEM;
 278
 279        ptr = (u32 *)eeprom_buff;
 280
 281        if (eeprom->offset & 3) {
 282                /* need read/modify/write of first changed EEPROM word */
 283                /* only the second byte of the word is being modified */
 284                if (!atl1e_read_eeprom(hw, first_dword * 4, &(eeprom_buff[0]))) {
 285                        ret_val = -EIO;
 286                        goto out;
 287                }
 288                ptr++;
 289        }
 290        if (((eeprom->offset + eeprom->len) & 3)) {
 291                /* need read/modify/write of last changed EEPROM word */
 292                /* only the first byte of the word is being modified */
 293
 294                if (!atl1e_read_eeprom(hw, last_dword * 4,
 295                                &(eeprom_buff[last_dword - first_dword]))) {
 296                        ret_val = -EIO;
 297                        goto out;
 298                }
 299        }
 300
 301        /* Device's eeprom is always little-endian, word addressable */
 302        memcpy(ptr, bytes, eeprom->len);
 303
 304        for (i = 0; i < last_dword - first_dword + 1; i++) {
 305                if (!atl1e_write_eeprom(hw, ((first_dword + i) * 4),
 306                                  eeprom_buff[i])) {
 307                        ret_val = -EIO;
 308                        goto out;
 309                }
 310        }
 311out:
 312        kfree(eeprom_buff);
 313        return ret_val;
 314}
 315
 316static void atl1e_get_drvinfo(struct net_device *netdev,
 317                struct ethtool_drvinfo *drvinfo)
 318{
 319        struct atl1e_adapter *adapter = netdev_priv(netdev);
 320
 321        strncpy(drvinfo->driver,  atl1e_driver_name, 32);
 322        strncpy(drvinfo->version, atl1e_driver_version, 32);
 323        strncpy(drvinfo->fw_version, "L1e", 32);
 324        strncpy(drvinfo->bus_info, pci_name(adapter->pdev), 32);
 325        drvinfo->n_stats = 0;
 326        drvinfo->testinfo_len = 0;
 327        drvinfo->regdump_len = atl1e_get_regs_len(netdev);
 328        drvinfo->eedump_len = atl1e_get_eeprom_len(netdev);
 329}
 330
 331static void atl1e_get_wol(struct net_device *netdev,
 332                          struct ethtool_wolinfo *wol)
 333{
 334        struct atl1e_adapter *adapter = netdev_priv(netdev);
 335
 336        wol->supported = WAKE_MAGIC | WAKE_PHY;
 337        wol->wolopts = 0;
 338
 339        if (adapter->wol & AT_WUFC_EX)
 340                wol->wolopts |= WAKE_UCAST;
 341        if (adapter->wol & AT_WUFC_MC)
 342                wol->wolopts |= WAKE_MCAST;
 343        if (adapter->wol & AT_WUFC_BC)
 344                wol->wolopts |= WAKE_BCAST;
 345        if (adapter->wol & AT_WUFC_MAG)
 346                wol->wolopts |= WAKE_MAGIC;
 347        if (adapter->wol & AT_WUFC_LNKC)
 348                wol->wolopts |= WAKE_PHY;
 349
 350        return;
 351}
 352
 353static int atl1e_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol)
 354{
 355        struct atl1e_adapter *adapter = netdev_priv(netdev);
 356
 357        if (wol->wolopts & (WAKE_ARP | WAKE_MAGICSECURE |
 358                            WAKE_UCAST | WAKE_MCAST | WAKE_BCAST))
 359                return -EOPNOTSUPP;
 360        /* these settings will always override what we currently have */
 361        adapter->wol = 0;
 362
 363        if (wol->wolopts & WAKE_MAGIC)
 364                adapter->wol |= AT_WUFC_MAG;
 365        if (wol->wolopts & WAKE_PHY)
 366                adapter->wol |= AT_WUFC_LNKC;
 367
 368        device_set_wakeup_enable(&adapter->pdev->dev, adapter->wol);
 369
 370        return 0;
 371}
 372
 373static int atl1e_nway_reset(struct net_device *netdev)
 374{
 375        struct atl1e_adapter *adapter = netdev_priv(netdev);
 376        if (netif_running(netdev))
 377                atl1e_reinit_locked(adapter);
 378        return 0;
 379}
 380
 381static const struct ethtool_ops atl1e_ethtool_ops = {
 382        .get_settings           = atl1e_get_settings,
 383        .set_settings           = atl1e_set_settings,
 384        .get_drvinfo            = atl1e_get_drvinfo,
 385        .get_regs_len           = atl1e_get_regs_len,
 386        .get_regs               = atl1e_get_regs,
 387        .get_wol                = atl1e_get_wol,
 388        .set_wol                = atl1e_set_wol,
 389        .get_msglevel           = atl1e_get_msglevel,
 390        .set_msglevel           = atl1e_set_msglevel,
 391        .nway_reset             = atl1e_nway_reset,
 392        .get_link               = ethtool_op_get_link,
 393        .get_eeprom_len         = atl1e_get_eeprom_len,
 394        .get_eeprom             = atl1e_get_eeprom,
 395        .set_eeprom             = atl1e_set_eeprom,
 396        .get_tx_csum            = atl1e_get_tx_csum,
 397        .get_sg                 = ethtool_op_get_sg,
 398        .set_sg                 = ethtool_op_set_sg,
 399#ifdef NETIF_F_TSO
 400        .get_tso                = ethtool_op_get_tso,
 401#endif
 402};
 403
 404void atl1e_set_ethtool_ops(struct net_device *netdev)
 405{
 406        SET_ETHTOOL_OPS(netdev, &atl1e_ethtool_ops);
 407}
 408