++ Intel PRO/1000 Linux driver
++ Copyright(c) 1999 - 2009 Intel Corporation.
++
++ This program is free software; you can redistribute it and/or modify it
++ under the terms and conditions of the GNU General Public License,
++ version 2, as published by the Free Software Foundation.
++
++ This program is distributed in the hope it will be useful, but WITHOUT
++ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
++ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
++ more details.
++
++ You should have received a copy of the GNU General Public License along with
++ this program; if not, write to the Free Software Foundation, Inc.,
++ 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
++
++ The full GNU General Public License is included in this distribution in
++ the file called "COPYING".
++
++ Contact Information:
++ Linux NICS <linux.nics@intel.com>
++ e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
++ Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
++
++*******************************************************************************/
++
++/* ethtool support for e1000 */
++
++#include <linux/netdevice.h>
++#ifdef SIOCETHTOOL
++#include <linux/ethtool.h>
++#include <linux/pci.h>
++#include <linux/delay.h>
++
++#include "e1000.h"
++#ifdef NETIF_F_HW_VLAN_TX
++#include <linux/if_vlan.h>
++#endif
++#ifdef ETHTOOL_OPS_COMPAT
++#include "kcompat_ethtool.c"
++#endif
++
++struct e1000_stats {
++ char stat_string[ETH_GSTRING_LEN];
++ int sizeof_stat;
++ int stat_offset;
++};
++
++#define E1000_STAT(m) sizeof(((struct e1000_adapter *)0)->m), \
++ offsetof(struct e1000_adapter, m)
++static const struct e1000_stats e1000_gstrings_stats[] = {
++ { "rx_packets", E1000_STAT(stats.gprc) },
++ { "tx_packets", E1000_STAT(stats.gptc) },
++ { "rx_bytes", E1000_STAT(stats.gorc) },
++ { "tx_bytes", E1000_STAT(stats.gotc) },
++ { "rx_broadcast", E1000_STAT(stats.bprc) },
++ { "tx_broadcast", E1000_STAT(stats.bptc) },
++ { "rx_multicast", E1000_STAT(stats.mprc) },
++ { "tx_multicast", E1000_STAT(stats.mptc) },
++ { "rx_errors", E1000_STAT(net_stats.rx_errors) },
++ { "tx_errors", E1000_STAT(net_stats.tx_errors) },
++#ifndef CONFIG_E1000E_NAPI
++ { "rx_dropped_backlog", E1000_STAT(rx_dropped_backlog) },
++#endif
++ { "tx_dropped", E1000_STAT(net_stats.tx_dropped) },
++ { "multicast", E1000_STAT(stats.mprc) },
++ { "collisions", E1000_STAT(stats.colc) },
++ { "rx_length_errors", E1000_STAT(net_stats.rx_length_errors) },
++ { "rx_over_errors", E1000_STAT(net_stats.rx_over_errors) },
++ { "rx_crc_errors", E1000_STAT(stats.crcerrs) },
++ { "rx_frame_errors", E1000_STAT(net_stats.rx_frame_errors) },
++ { "rx_no_buffer_count", E1000_STAT(stats.rnbc) },
++ { "rx_missed_errors", E1000_STAT(stats.mpc) },
++ { "tx_aborted_errors", E1000_STAT(stats.ecol) },
++ { "tx_carrier_errors", E1000_STAT(stats.tncrs) },
++ { "tx_fifo_errors", E1000_STAT(net_stats.tx_fifo_errors) },
++ { "tx_heartbeat_errors", E1000_STAT(net_stats.tx_heartbeat_errors) },
++ { "tx_window_errors", E1000_STAT(stats.latecol) },
++ { "tx_abort_late_coll", E1000_STAT(stats.latecol) },
++ { "tx_deferred_ok", E1000_STAT(stats.dc) },
++ { "tx_single_coll_ok", E1000_STAT(stats.scc) },
++ { "tx_multi_coll_ok", E1000_STAT(stats.mcc) },
++ { "tx_timeout_count", E1000_STAT(tx_timeout_count) },
++ { "tx_restart_queue", E1000_STAT(restart_queue) },
++ { "rx_long_length_errors", E1000_STAT(stats.roc) },
++ { "rx_short_length_errors", E1000_STAT(stats.ruc) },
++ { "rx_align_errors", E1000_STAT(stats.algnerrc) },
++ { "tx_tcp_seg_good", E1000_STAT(stats.tsctc) },
++ { "tx_tcp_seg_failed", E1000_STAT(stats.tsctfc) },
++ { "rx_flow_control_xon", E1000_STAT(stats.xonrxc) },
++ { "rx_flow_control_xoff", E1000_STAT(stats.xoffrxc) },
++ { "tx_flow_control_xon", E1000_STAT(stats.xontxc) },
++ { "tx_flow_control_xoff", E1000_STAT(stats.xofftxc) },
++ { "rx_long_byte_count", E1000_STAT(stats.gorc) },
++ { "rx_csum_offload_good", E1000_STAT(hw_csum_good) },
++ { "rx_csum_offload_errors", E1000_STAT(hw_csum_err) },
++ { "rx_header_split", E1000_STAT(rx_hdr_split) },
++ { "alloc_rx_buff_failed", E1000_STAT(alloc_rx_buff_failed) },
++ { "tx_smbus", E1000_STAT(stats.mgptc) },
++ { "rx_smbus", E1000_STAT(stats.mgprc) },
++ { "dropped_smbus", E1000_STAT(stats.mgpdc) },
++ { "rx_dma_failed", E1000_STAT(rx_dma_failed) },
++ { "tx_dma_failed", E1000_STAT(tx_dma_failed) },
++};
++
++#define E1000_GLOBAL_STATS_LEN ARRAY_SIZE(e1000_gstrings_stats)
++#define E1000_STATS_LEN (E1000_GLOBAL_STATS_LEN)
++static const char e1000_gstrings_test[][ETH_GSTRING_LEN] = {
++ "Register test (offline)", "Eeprom test (offline)",
++ "Interrupt test (offline)", "Loopback test (offline)",
++ "Link test (on/offline)"
++};
++#define E1000_TEST_LEN ARRAY_SIZE(e1000_gstrings_test)
++
++static int e1000_get_settings(struct net_device *netdev,
++ struct ethtool_cmd *ecmd)
++{
++ struct e1000_adapter *adapter = netdev_priv(netdev);
++ struct e1000_hw *hw = &adapter->hw;
++ u32 status;
++
++ if (hw->phy.media_type == e1000_media_type_copper) {
++
++ ecmd->supported = (SUPPORTED_10baseT_Half |
++ SUPPORTED_10baseT_Full |
++ SUPPORTED_100baseT_Half |
++ SUPPORTED_100baseT_Full |
++ SUPPORTED_1000baseT_Full |
++ SUPPORTED_Autoneg |
++ SUPPORTED_TP);
++ if (hw->phy.type == e1000_phy_ife)
++ ecmd->supported &= ~SUPPORTED_1000baseT_Full;
++ ecmd->advertising = ADVERTISED_TP;
++
++ if (hw->mac.autoneg == 1) {
++ ecmd->advertising |= ADVERTISED_Autoneg;
++ /* the e1000 autoneg seems to match ethtool nicely */
++ ecmd->advertising |= hw->phy.autoneg_advertised;
++ }
++
++ ecmd->port = PORT_TP;
++ ecmd->phy_address = hw->phy.addr;
++ ecmd->transceiver = XCVR_INTERNAL;
++
++ } else {
++ ecmd->supported = (SUPPORTED_1000baseT_Full |
++ SUPPORTED_FIBRE |
++ SUPPORTED_Autoneg);
++
++ ecmd->advertising = (ADVERTISED_1000baseT_Full |
++ ADVERTISED_FIBRE |
++ ADVERTISED_Autoneg);
++
++ ecmd->port = PORT_FIBRE;
++ ecmd->transceiver = XCVR_EXTERNAL;
++ }
++
++ status = er32(STATUS);
++ if (status & E1000_STATUS_LU) {
++ if (status & E1000_STATUS_SPEED_1000)
++ ecmd->speed = 1000;
++ else if (status & E1000_STATUS_SPEED_100)
++ ecmd->speed = 100;
++ else
++ ecmd->speed = 10;
++
++ if (status & E1000_STATUS_FD)
++ ecmd->duplex = DUPLEX_FULL;
++ else
++ ecmd->duplex = DUPLEX_HALF;
++ } else {
++ ecmd->speed = -1;
++ ecmd->duplex = -1;
++ }
++
++ ecmd->autoneg = ((hw->phy.media_type == e1000_media_type_fiber) ||
++ hw->mac.autoneg) ? AUTONEG_ENABLE : AUTONEG_DISABLE;
++ return 0;
++}
++
++static u32 e1000_get_link(struct net_device *netdev)
++{
++ struct e1000_adapter *adapter = netdev_priv(netdev);
++
++ return e1000_has_link(adapter);
++}
++
++static int e1000_set_spd_dplx(struct e1000_adapter *adapter, u16 spddplx)
++{
++ struct e1000_mac_info *mac = &adapter->hw.mac;
++
++ mac->autoneg = 0;
++
++ /* Fiber NICs only allow 1000 gbps Full duplex */
++ if ((adapter->hw.phy.media_type == e1000_media_type_fiber) &&
++ spddplx != (SPEED_1000 + DUPLEX_FULL)) {
++ e_err("Unsupported Speed/Duplex configuration\n");
++ return -EINVAL;
++ }
++
++ switch (spddplx) {
++ case SPEED_10 + DUPLEX_HALF:
++ mac->forced_speed_duplex = ADVERTISE_10_HALF;
++ break;
++ case SPEED_10 + DUPLEX_FULL:
++ mac->forced_speed_duplex = ADVERTISE_10_FULL;
++ break;
++ case SPEED_100 + DUPLEX_HALF:
++ mac->forced_speed_duplex = ADVERTISE_100_HALF;
++ break;
++ case SPEED_100 + DUPLEX_FULL:
++ mac->forced_speed_duplex = ADVERTISE_100_FULL;
++ break;
++ case SPEED_1000 + DUPLEX_FULL:
++ mac->autoneg = 1;
++ adapter->hw.phy.autoneg_advertised = ADVERTISE_1000_FULL;
++ break;
++ case SPEED_1000 + DUPLEX_HALF: /* not supported */
++ default:
++ e_err("Unsupported Speed/Duplex configuration\n");
++ return -EINVAL;
++ }
++ return 0;
++}
++
++static int e1000_set_settings(struct net_device *netdev,
++ struct ethtool_cmd *ecmd)
++{
++ struct e1000_adapter *adapter = netdev_priv(netdev);
++ struct e1000_hw *hw = &adapter->hw;
++
++ /*
++ * When SoL/IDER sessions are active, autoneg/speed/duplex
++ * cannot be changed
++ */
++ if (e1000_check_reset_block(hw)) {
++ e_err("Cannot change link characteristics when SoL/IDER is "
++ "active.\n");
++ return -EINVAL;
++ }
++
++ while (test_and_set_bit(__E1000_RESETTING, &adapter->state))
++ msleep(1);
++
++ if (ecmd->autoneg == AUTONEG_ENABLE) {
++ hw->mac.autoneg = 1;
++ if (hw->phy.media_type == e1000_media_type_fiber)
++ hw->phy.autoneg_advertised = ADVERTISED_1000baseT_Full |
++ ADVERTISED_FIBRE |
++ ADVERTISED_Autoneg;
++ else
++ hw->phy.autoneg_advertised = ecmd->advertising |
++ ADVERTISED_TP |
++ ADVERTISED_Autoneg;
++ ecmd->advertising = hw->phy.autoneg_advertised;
++ if (adapter->fc_autoneg) {
++ if (hw->mac.type == e1000_pchlan) {
++ /* Workaround h/w hang when Tx flow control
++ * enabled */
++ hw->fc.requested_mode = e1000_fc_rx_pause;
++ } else {
++ hw->fc.requested_mode = e1000_fc_default;
++ }
++ }
++ } else {
++ if (e1000_set_spd_dplx(adapter, ecmd->speed + ecmd->duplex)) {
++ clear_bit(__E1000_RESETTING, &adapter->state);
++ return -EINVAL;
++ }
++ }
++
++ /* reset the link */
++
++ if (netif_running(adapter->netdev)) {
++ e1000e_down(adapter);
++ e1000e_up(adapter);
++ } else {
++ e1000e_reset(adapter);
++ }
++
++ clear_bit(__E1000_RESETTING, &adapter->state);
++ return 0;
++}
++
++static void e1000_get_pauseparam(struct net_device *netdev,
++ struct ethtool_pauseparam *pause)
++{
++ struct e1000_adapter *adapter = netdev_priv(netdev);
++ struct e1000_hw *hw = &adapter->hw;
++
++ pause->autoneg =
++ (adapter->fc_autoneg ? AUTONEG_ENABLE : AUTONEG_DISABLE);
++
++ if (hw->fc.current_mode == e1000_fc_rx_pause) {
++ pause->rx_pause = 1;
++ } else if (hw->fc.current_mode == e1000_fc_tx_pause) {
++ pause->tx_pause = 1;
++ } else if (hw->fc.current_mode == e1000_fc_full) {
++ pause->rx_pause = 1;
++ pause->tx_pause = 1;
++ }
++}
++
++static int e1000_set_pauseparam(struct net_device *netdev,
++ struct ethtool_pauseparam *pause)
++{
++ struct e1000_adapter *adapter = netdev_priv(netdev);
++ struct e1000_hw *hw = &adapter->hw;
++ int retval = 0;
++
++ adapter->fc_autoneg = pause->autoneg;
++
++ while (test_and_set_bit(__E1000_RESETTING, &adapter->state))
++ msleep(1);
++
++ if (adapter->fc_autoneg == AUTONEG_ENABLE) {
++ if (hw->mac.type == e1000_pchlan) {
++ /* Workaround h/w hang when Tx flow control enabled */
++ hw->fc.requested_mode = e1000_fc_rx_pause;
++ } else {
++ hw->fc.requested_mode = e1000_fc_default;
++ }
++ if (netif_running(adapter->netdev)) {
++ e1000e_down(adapter);
++ e1000e_up(adapter);
++ } else {
++ e1000e_reset(adapter);
++ }
++ } else {
++ if (pause->rx_pause && pause->tx_pause)
++ hw->fc.requested_mode = e1000_fc_full;
++ else if (pause->rx_pause && !pause->tx_pause)
++ hw->fc.requested_mode = e1000_fc_rx_pause;
++ else if (!pause->rx_pause && pause->tx_pause)
++ hw->fc.requested_mode = e1000_fc_tx_pause;
++ else if (!pause->rx_pause && !pause->tx_pause)
++ hw->fc.requested_mode = e1000_fc_none;
++
++ hw->fc.current_mode = hw->fc.requested_mode;
++
++ retval = ((hw->phy.media_type == e1000_media_type_fiber) ?
++ hw->mac.ops.setup_link(hw) : e1000e_force_mac_fc(hw));
++ }
++
++ clear_bit(__E1000_RESETTING, &adapter->state);
++ return retval;
++}
++
++static u32 e1000_get_rx_csum(struct net_device *netdev)
++{
++ struct e1000_adapter *adapter = netdev_priv(netdev);
++ return adapter->flags & FLAG_RX_CSUM_ENABLED;
++}
++
++static int e1000_set_rx_csum(struct net_device *netdev, u32 data)
++{
++ struct e1000_adapter *adapter = netdev_priv(netdev);
++
++ if (data)
++ adapter->flags |= FLAG_RX_CSUM_ENABLED;
++ else
++ adapter->flags &= ~FLAG_RX_CSUM_ENABLED;
++
++ if (netif_running(netdev))
++ e1000e_reinit_locked(adapter);
++ else
++ e1000e_reset(adapter);
++ return 0;
++}
++
++static u32 e1000_get_tx_csum(struct net_device *netdev)
++{
++ return ((netdev->features & NETIF_F_HW_CSUM) != 0);
++}
++
++static int e1000_set_tx_csum(struct net_device *netdev, u32 data)
++{
++ if (data)
++ netdev->features |= NETIF_F_HW_CSUM;
++ else
++ netdev->features &= ~NETIF_F_HW_CSUM;
++
++ return 0;
++}
++
++#ifdef NETIF_F_TSO
++static int e1000_set_tso(struct net_device *netdev, u32 data)
++{
++ struct e1000_adapter *adapter = netdev_priv(netdev);
++ int i;
++ struct net_device *v_netdev;
++
++ if (data) {
++ netdev->features |= NETIF_F_TSO;
++#ifdef NETIF_F_TSO6
++ netdev->features |= NETIF_F_TSO6;
++#endif
++ } else {
++ netdev->features &= ~NETIF_F_TSO;
++#ifdef NETIF_F_TSO6
++ netdev->features &= ~NETIF_F_TSO6;
++#endif
++#ifdef NETIF_F_HW_VLAN_TX
++ /* disable TSO on all VLANs if they're present */
++ if (!adapter->vlgrp)
++ goto tso_out;
++ for (i = 0; i < VLAN_GROUP_ARRAY_LEN; i++) {
++ v_netdev = vlan_group_get_device(adapter->vlgrp, i);
++ if (!v_netdev)
++ continue;
++
++ v_netdev->features &= ~NETIF_F_TSO;
++#ifdef NETIF_F_TSO6
++ v_netdev->features &= ~NETIF_F_TSO6;
++#endif
++ vlan_group_set_device(adapter->vlgrp, i, v_netdev);
++ }
++#endif
++ }
++
++tso_out:
++ e_info("TSO is %s\n", data ? "Enabled" : "Disabled");
++ adapter->flags |= FLAG_TSO_FORCE;
++ return 0;
++}
++#endif
++
++static u32 e1000_get_msglevel(struct net_device *netdev)
++{
++ struct e1000_adapter *adapter = netdev_priv(netdev);
++ return adapter->msg_enable;
++}
++
++static void e1000_set_msglevel(struct net_device *netdev, u32 data)
++{
++ struct e1000_adapter *adapter = netdev_priv(netdev);
++ adapter->msg_enable = data;
++}
++
++static int e1000_get_regs_len(struct net_device *netdev)
++{
++#define E1000_REGS_LEN 32 /* overestimate */
++ return E1000_REGS_LEN * sizeof(u32);
++}
++
++static void e1000_get_regs(struct net_device *netdev,
++ struct ethtool_regs *regs, void *p)
++{
++ struct e1000_adapter *adapter = netdev_priv(netdev);
++ struct e1000_hw *hw = &adapter->hw;
++ u32 *regs_buff = p;
++ u16 phy_data;
++ u8 revision_id;
++
++ memset(p, 0, E1000_REGS_LEN * sizeof(u32));
++
++ pci_read_config_byte(adapter->pdev, PCI_REVISION_ID, &revision_id);
++
++ regs->version = (1 << 24) | (revision_id << 16) | adapter->pdev->device;
++
++ regs_buff[0] = er32(CTRL);
++ regs_buff[1] = er32(STATUS);
++
++ regs_buff[2] = er32(RCTL);
++ regs_buff[3] = er32(RDLEN(0));
++ regs_buff[4] = er32(RDH(0));
++ regs_buff[5] = er32(RDT(0));
++ regs_buff[6] = er32(RDTR);
++
++ regs_buff[7] = er32(TCTL);
++ regs_buff[8] = er32(TDLEN(0));
++ regs_buff[9] = er32(TDH(0));
++ regs_buff[10] = er32(TDT(0));
++ regs_buff[11] = er32(TIDV);
++
++ regs_buff[12] = adapter->hw.phy.type; /* PHY type (IGP=1, M88=0) */
++
++ /* ethtool doesn't use anything past this point, so all this
++ * code is likely legacy junk for apps that may or may not
++ * exist */
++ if (hw->phy.type == e1000_phy_m88) {
++ e1e_rphy(hw, M88E1000_PHY_SPEC_STATUS, &phy_data);
++ regs_buff[13] = (u32)phy_data; /* cable length */
++ regs_buff[14] = 0; /* Dummy (to align w/ IGP phy reg dump) */
++ regs_buff[15] = 0; /* Dummy (to align w/ IGP phy reg dump) */
++ regs_buff[16] = 0; /* Dummy (to align w/ IGP phy reg dump) */
++ e1e_rphy(hw, M88E1000_PHY_SPEC_CTRL, &phy_data);
++ regs_buff[17] = (u32)phy_data; /* extended 10bt distance */
++ regs_buff[18] = regs_buff[13]; /* cable polarity */
++ regs_buff[19] = 0; /* Dummy (to align w/ IGP phy reg dump) */
++ regs_buff[20] = regs_buff[17]; /* polarity correction */
++ /* phy receive errors */
++ regs_buff[22] = adapter->phy_stats.receive_errors;
++ regs_buff[23] = regs_buff[13]; /* mdix mode */
++ }
++ regs_buff[21] = 0; /* was idle_errors */
++ e1e_rphy(hw, PHY_1000T_STATUS, &phy_data);
++ regs_buff[24] = (u32)phy_data; /* phy local receiver status */
++ regs_buff[25] = regs_buff[24]; /* phy remote receiver status */
++}
++
++static int e1000_get_eeprom_len(struct net_device *netdev)
++{
++ struct e1000_adapter *adapter = netdev_priv(netdev);
++ return adapter->hw.nvm.word_size * 2;
++}
++
++static int e1000_get_eeprom(struct net_device *netdev,
++ struct ethtool_eeprom *eeprom, u8 *bytes)
++{
++ struct e1000_adapter *adapter = netdev_priv(netdev);
++ struct e1000_hw *hw = &adapter->hw;
++ u16 *eeprom_buff;
++ int first_word;
++ int last_word;
++ int ret_val = 0;
++ u16 i;
++
++ if (eeprom->len == 0)
++ return -EINVAL;
++
++ eeprom->magic = adapter->pdev->vendor | (adapter->pdev->device << 16);
++
++ first_word = eeprom->offset >> 1;
++ last_word = (eeprom->offset + eeprom->len - 1) >> 1;
++
++ eeprom_buff = kmalloc(sizeof(u16) *
++ (last_word - first_word + 1), GFP_KERNEL);
++ if (!eeprom_buff)
++ return -ENOMEM;
++
++ if (hw->nvm.type == e1000_nvm_eeprom_spi) {
++ ret_val = e1000_read_nvm(hw, first_word,
++ last_word - first_word + 1,
++ eeprom_buff);
++ } else {
++ for (i = 0; i < last_word - first_word + 1; i++) {
++ ret_val = e1000_read_nvm(hw, first_word + i, 1,
++ &eeprom_buff[i]);
++ if (ret_val)
++ break;
++ }
++ }
++
++ if (ret_val) {
++ /* a read error occurred, throw away the result */
++ memset(eeprom_buff, 0xff, sizeof(eeprom_buff));
++ } else {
++ /* Device's eeprom is always little-endian, word addressable */
++ for (i = 0; i < last_word - first_word + 1; i++)
++ le16_to_cpus(&eeprom_buff[i]);
++ }
++
++ memcpy(bytes, (u8 *)eeprom_buff + (eeprom->offset & 1), eeprom->len);
++ kfree(eeprom_buff);
++
++ return ret_val;
++}
++
++static int e1000_set_eeprom(struct net_device *netdev,
++ struct ethtool_eeprom *eeprom, u8 *bytes)
++{
++ struct e1000_adapter *adapter = netdev_priv(netdev);
++ struct e1000_hw *hw = &adapter->hw;
++ u16 *eeprom_buff;
++ void *ptr;
++ int max_len;
++ int first_word;
++ int last_word;
++ int ret_val = 0;
++ u16 i;
++
++ if (eeprom->len == 0)
++ return -EOPNOTSUPP;
++
++ if (eeprom->magic != (adapter->pdev->vendor | (adapter->pdev->device << 16)))
++ return -EFAULT;
++
++ max_len = hw->nvm.word_size * 2;
++
++ first_word = eeprom->offset >> 1;
++ last_word = (eeprom->offset + eeprom->len - 1) >> 1;
++ eeprom_buff = kmalloc(max_len, GFP_KERNEL);
++ if (!eeprom_buff)
++ return -ENOMEM;
++
++ ptr = (void *)eeprom_buff;