Skip to content
Snippets Groups Projects
ethtool.c 61.3 KiB
Newer Older
  • Learn to ignore specific revisions
  • /*******************************************************************************
    
      Intel PRO/1000 Linux driver
    
      Copyright(c) 1999 - 2013 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>
    
    #include <linux/interrupt.h>
    
    #include <linux/ethtool.h>
    #include <linux/pci.h>
    
    #include <linux/vmalloc.h>
    
    #include <linux/mdio.h>
    
    enum { NETDEV_STATS, E1000_STATS };
    
    struct e1000_stats {
    	char stat_string[ETH_GSTRING_LEN];
    
    #define E1000_STAT(str, m) { \
    
    Jeff Kirsher's avatar
    Jeff Kirsher committed
    		.stat_string = str, \
    		.type = E1000_STATS, \
    		.sizeof_stat = sizeof(((struct e1000_adapter *)0)->m), \
    		.stat_offset = offsetof(struct e1000_adapter, m) }
    
    #define E1000_NETDEV_STAT(str, m) { \
    
    Jeff Kirsher's avatar
    Jeff Kirsher committed
    		.stat_string = str, \
    		.type = NETDEV_STATS, \
    		.sizeof_stat = sizeof(((struct rtnl_link_stats64 *)0)->m), \
    		.stat_offset = offsetof(struct rtnl_link_stats64, m) }
    
    static const struct e1000_stats e1000_gstrings_stats[] = {
    
    	E1000_STAT("rx_packets", stats.gprc),
    	E1000_STAT("tx_packets", stats.gptc),
    	E1000_STAT("rx_bytes", stats.gorc),
    	E1000_STAT("tx_bytes", stats.gotc),
    	E1000_STAT("rx_broadcast", stats.bprc),
    	E1000_STAT("tx_broadcast", stats.bptc),
    	E1000_STAT("rx_multicast", stats.mprc),
    	E1000_STAT("tx_multicast", stats.mptc),
    
    Jeff Kirsher's avatar
    Jeff Kirsher committed
    	E1000_NETDEV_STAT("rx_errors", rx_errors),
    	E1000_NETDEV_STAT("tx_errors", tx_errors),
    	E1000_NETDEV_STAT("tx_dropped", tx_dropped),
    
    	E1000_STAT("multicast", stats.mprc),
    	E1000_STAT("collisions", stats.colc),
    
    Jeff Kirsher's avatar
    Jeff Kirsher committed
    	E1000_NETDEV_STAT("rx_length_errors", rx_length_errors),
    	E1000_NETDEV_STAT("rx_over_errors", rx_over_errors),
    
    	E1000_STAT("rx_crc_errors", stats.crcerrs),
    
    Jeff Kirsher's avatar
    Jeff Kirsher committed
    	E1000_NETDEV_STAT("rx_frame_errors", rx_frame_errors),
    
    	E1000_STAT("rx_no_buffer_count", stats.rnbc),
    	E1000_STAT("rx_missed_errors", stats.mpc),
    	E1000_STAT("tx_aborted_errors", stats.ecol),
    	E1000_STAT("tx_carrier_errors", stats.tncrs),
    
    Jeff Kirsher's avatar
    Jeff Kirsher committed
    	E1000_NETDEV_STAT("tx_fifo_errors", tx_fifo_errors),
    	E1000_NETDEV_STAT("tx_heartbeat_errors", tx_heartbeat_errors),
    
    	E1000_STAT("tx_window_errors", stats.latecol),
    	E1000_STAT("tx_abort_late_coll", stats.latecol),
    	E1000_STAT("tx_deferred_ok", stats.dc),
    	E1000_STAT("tx_single_coll_ok", stats.scc),
    	E1000_STAT("tx_multi_coll_ok", stats.mcc),
    	E1000_STAT("tx_timeout_count", tx_timeout_count),
    	E1000_STAT("tx_restart_queue", restart_queue),
    	E1000_STAT("rx_long_length_errors", stats.roc),
    	E1000_STAT("rx_short_length_errors", stats.ruc),
    	E1000_STAT("rx_align_errors", stats.algnerrc),
    	E1000_STAT("tx_tcp_seg_good", stats.tsctc),
    	E1000_STAT("tx_tcp_seg_failed", stats.tsctfc),
    	E1000_STAT("rx_flow_control_xon", stats.xonrxc),
    	E1000_STAT("rx_flow_control_xoff", stats.xoffrxc),
    	E1000_STAT("tx_flow_control_xon", stats.xontxc),
    	E1000_STAT("tx_flow_control_xoff", stats.xofftxc),
    	E1000_STAT("rx_csum_offload_good", hw_csum_good),
    	E1000_STAT("rx_csum_offload_errors", hw_csum_err),
    	E1000_STAT("rx_header_split", rx_hdr_split),
    	E1000_STAT("alloc_rx_buff_failed", alloc_rx_buff_failed),
    	E1000_STAT("tx_smbus", stats.mgptc),
    	E1000_STAT("rx_smbus", stats.mgprc),
    	E1000_STAT("dropped_smbus", stats.mgpdc),
    	E1000_STAT("rx_dma_failed", rx_dma_failed),
    	E1000_STAT("tx_dma_failed", tx_dma_failed),
    
    	E1000_STAT("rx_hwtstamp_cleared", rx_hwtstamp_cleared),
    
    	E1000_STAT("uncorr_ecc_errors", uncorr_errors),
    	E1000_STAT("corr_ecc_errors", corr_errors),
    
    #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;
    
    	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;
    	}
    
    
    	ecmd->duplex = -1;
    
    	if (netif_running(netdev)) {
    		if (netif_carrier_ok(netdev)) {
    
    			speed = adapter->link_speed;
    
    			ecmd->duplex = adapter->link_duplex - 1;
    		}
    
    		u32 status = er32(STATUS);
    		if (status & E1000_STATUS_LU) {
    			if (status & E1000_STATUS_SPEED_1000)
    
    			else if (status & E1000_STATUS_SPEED_100)
    
    
    			if (status & E1000_STATUS_FD)
    				ecmd->duplex = DUPLEX_FULL;
    			else
    				ecmd->duplex = DUPLEX_HALF;
    		}
    
    	ethtool_cmd_speed_set(ecmd, speed);
    
    	ecmd->autoneg = ((hw->phy.media_type == e1000_media_type_fiber) ||
    
    			 hw->mac.autoneg) ? AUTONEG_ENABLE : AUTONEG_DISABLE;
    
    
    	/* MDI-X => 2; MDI =>1; Invalid =>0 */
    	if ((hw->phy.media_type == e1000_media_type_copper) &&
    
    	    netif_carrier_ok(netdev))
    
    		ecmd->eth_tp_mdix = hw->phy.is_mdix ? ETH_TP_MDI_X : ETH_TP_MDI;
    
    	else
    		ecmd->eth_tp_mdix = ETH_TP_MDI_INVALID;
    
    
    	if (hw->phy.mdix == AUTO_ALL_MODES)
    		ecmd->eth_tp_mdix_ctrl = ETH_TP_MDI_AUTO;
    	else
    		ecmd->eth_tp_mdix_ctrl = hw->phy.mdix;
    
    
    static int e1000_set_spd_dplx(struct e1000_adapter *adapter, u32 spd, u8 dplx)
    
    {
    	struct e1000_mac_info *mac = &adapter->hw.mac;
    
    	mac->autoneg = 0;
    
    
    	/* Make sure dplx is at most 1 bit and lsb of speed is not set
    
    	 * for the switch() below to work
    	 */
    
    	if ((spd & 1) || (dplx & ~1))
    		goto err_inval;
    
    
    	/* Fiber NICs only allow 1000 gbps Full duplex */
    
    	if ((adapter->hw.phy.media_type == e1000_media_type_fiber) &&
    
    	    spd != SPEED_1000 &&
    	    dplx != DUPLEX_FULL) {
    		goto err_inval;
    
    	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:
    
    
    	/* clear MDI, MDI(-X) override is only allowed when autoneg enabled */
    	adapter->hw.phy.mdix = AUTO_ALL_MODES;
    
    
    
    err_inval:
    	e_err("Unsupported Speed/Duplex configuration\n");
    	return -EINVAL;
    
    }
    
    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
    
    	if (hw->phy.ops.check_reset_block &&
    	    hw->phy.ops.check_reset_block(hw)) {
    
    		e_err("Cannot change link characteristics when SoL/IDER is active.\n");
    
    	/* MDI setting is only allowed when autoneg enabled because
    
    	 * some hardware doesn't allow MDI setting when speed or
    	 * duplex is forced.
    	 */
    	if (ecmd->eth_tp_mdix_ctrl) {
    		if (hw->phy.media_type != e1000_media_type_copper)
    			return -EOPNOTSUPP;
    
    		if ((ecmd->eth_tp_mdix_ctrl != ETH_TP_MDI_AUTO) &&
    		    (ecmd->autoneg != AUTONEG_ENABLE)) {
    			e_err("forcing MDI/MDI-X state is not supported when link speed and/or duplex are forced\n");
    			return -EINVAL;
    		}
    	}
    
    
    	while (test_and_set_bit(__E1000_RESETTING, &adapter->state))
    
    		usleep_range(1000, 2000);
    
    
    	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)
    
    			hw->fc.requested_mode = e1000_fc_default;
    
    		u32 speed = ethtool_cmd_speed(ecmd);
    
    		/* calling this overrides forced MDI setting */
    
    		if (e1000_set_spd_dplx(adapter, speed, ecmd->duplex)) {
    
    			clear_bit(__E1000_RESETTING, &adapter->state);
    			return -EINVAL;
    		}
    	}
    
    
    	/* MDI-X => 2; MDI => 1; Auto => 3 */
    	if (ecmd->eth_tp_mdix_ctrl) {
    
    		/* fix up the value for auto (3 => 0) as zero is mapped
    
    		 * internally to auto
    		 */
    		if (ecmd->eth_tp_mdix_ctrl == ETH_TP_MDI_AUTO)
    			hw->phy.mdix = AUTO_ALL_MODES;
    		else
    			hw->phy.mdix = ecmd->eth_tp_mdix_ctrl;
    	}
    
    
    	/* reset the link */
    	if (netif_running(adapter->netdev)) {
    		e1000e_down(adapter);
    		e1000e_up(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) {
    
    	} else if (hw->fc.current_mode == e1000_fc_tx_pause) {
    
    	} 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))
    
    		usleep_range(1000, 2000);
    
    
    	if (adapter->fc_autoneg == AUTONEG_ENABLE) {
    
    		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;
    
    
    		if (hw->phy.media_type == e1000_media_type_fiber) {
    			retval = hw->mac.ops.setup_link(hw);
    			/* implicit goto out */
    		} else {
    			retval = e1000e_force_mac_fc(hw);
    			if (retval)
    				goto out;
    			e1000e_set_fc_watermarks(hw);
    		}
    
    	clear_bit(__E1000_RESETTING, &adapter->state);
    	return retval;
    }
    
    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 __always_unused *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;
    
    	memset(p, 0, E1000_REGS_LEN * sizeof(u32));
    
    
    	regs->version = (1 << 24) | (adapter->pdev->revision << 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, MII_STAT1000, &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) {
    		/* a read error occurred, throw away the result */
    
    		memset(eeprom_buff, 0xff, sizeof(u16) *
    		       (last_word - first_word + 1));
    
    	} 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)))
    
    	if (adapter->flags & FLAG_READ_ONLY_NVM)
    		return -EINVAL;
    
    
    	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;
    
    	if (eeprom->offset & 1) {
    		/* need read/modify/write of first changed EEPROM word */
    		/* only the second byte of the word is being modified */
    		ret_val = e1000_read_nvm(hw, first_word, 1, &eeprom_buff[0]);
    		ptr++;
    	}
    
    	if (((eeprom->offset + eeprom->len) & 1) && (!ret_val))
    
    		/* need read/modify/write of last changed EEPROM word */
    		/* only the first byte of the word is being modified */
    		ret_val = e1000_read_nvm(hw, last_word, 1,
    				  &eeprom_buff[last_word - first_word]);
    
    
    	/* 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(ptr, bytes, eeprom->len);
    
    	for (i = 0; i < last_word - first_word + 1; i++)
    
    		cpu_to_le16s(&eeprom_buff[i]);
    
    
    	ret_val = e1000_write_nvm(hw, first_word,
    				  last_word - first_word + 1, eeprom_buff);
    
    
    	/* Update the checksum over the first part of the EEPROM if needed
    
    	 * and flush shadow RAM for applicable controllers
    
    	if ((first_word <= NVM_CHECKSUM_REG) ||
    
    	    (hw->mac.type == e1000_82583) ||
    	    (hw->mac.type == e1000_82574) ||
    	    (hw->mac.type == e1000_82573))
    
    		ret_val = e1000e_update_nvm_checksum(hw);
    
    	kfree(eeprom_buff);
    	return ret_val;
    }
    
    static void e1000_get_drvinfo(struct net_device *netdev,
    			      struct ethtool_drvinfo *drvinfo)
    {
    	struct e1000_adapter *adapter = netdev_priv(netdev);
    
    
    	strlcpy(drvinfo->driver,  e1000e_driver_name,
    		sizeof(drvinfo->driver));
    
    	strlcpy(drvinfo->version, e1000e_driver_version,
    
    	/* EEPROM image version # is reported as firmware version # for
    
    	snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version),
    		"%d.%d-%d",
    
    		(adapter->eeprom_vers & 0xF000) >> 12,
    		(adapter->eeprom_vers & 0x0FF0) >> 4,
    		(adapter->eeprom_vers & 0x000F));
    
    	strlcpy(drvinfo->bus_info, pci_name(adapter->pdev),
    		sizeof(drvinfo->bus_info));
    
    	drvinfo->regdump_len = e1000_get_regs_len(netdev);
    	drvinfo->eedump_len = e1000_get_eeprom_len(netdev);
    }
    
    static void e1000_get_ringparam(struct net_device *netdev,
    				struct ethtool_ringparam *ring)
    {
    	struct e1000_adapter *adapter = netdev_priv(netdev);
    
    	ring->rx_max_pending = E1000_MAX_RXD;
    	ring->tx_max_pending = E1000_MAX_TXD;
    
    	ring->rx_pending = adapter->rx_ring_count;
    	ring->tx_pending = adapter->tx_ring_count;
    
    }
    
    static int e1000_set_ringparam(struct net_device *netdev,
    			       struct ethtool_ringparam *ring)
    {
    	struct e1000_adapter *adapter = netdev_priv(netdev);
    
    	struct e1000_ring *temp_tx = NULL, *temp_rx = NULL;
    	int err = 0, size = sizeof(struct e1000_ring);
    	bool set_tx = false, set_rx = false;
    	u16 new_rx_count, new_tx_count;
    
    
    	if ((ring->rx_mini_pending) || (ring->rx_jumbo_pending))
    		return -EINVAL;
    
    
    	new_rx_count = clamp_t(u32, ring->rx_pending, E1000_MIN_RXD,
    			       E1000_MAX_RXD);
    	new_rx_count = ALIGN(new_rx_count, REQ_RX_DESCRIPTOR_MULTIPLE);
    
    	new_tx_count = clamp_t(u32, ring->tx_pending, E1000_MIN_TXD,
    			       E1000_MAX_TXD);
    	new_tx_count = ALIGN(new_tx_count, REQ_TX_DESCRIPTOR_MULTIPLE);
    
    	if ((new_tx_count == adapter->tx_ring_count) &&
    	    (new_rx_count == adapter->rx_ring_count))
    		/* nothing to do */
    		return 0;
    
    	while (test_and_set_bit(__E1000_RESETTING, &adapter->state))
    		usleep_range(1000, 2000);
    
    	if (!netif_running(adapter->netdev)) {
    		/* Set counts now and allocate resources during open() */
    		adapter->tx_ring->count = new_tx_count;
    		adapter->rx_ring->count = new_rx_count;
    		adapter->tx_ring_count = new_tx_count;
    		adapter->rx_ring_count = new_rx_count;
    		goto clear_reset;
    	}
    
    	set_tx = (new_tx_count != adapter->tx_ring_count);
    	set_rx = (new_rx_count != adapter->rx_ring_count);
    
    	/* Allocate temporary storage for ring updates */
    	if (set_tx) {
    		temp_tx = vmalloc(size);
    		if (!temp_tx) {
    			err = -ENOMEM;
    			goto free_temp;
    		}
    	}
    	if (set_rx) {
    		temp_rx = vmalloc(size);
    		if (!temp_rx) {
    			err = -ENOMEM;
    			goto free_temp;
    		}
    	}
    
    	e1000e_down(adapter);
    
    	/* We can't just free everything and then setup again, because the
    
    	 * ISRs in MSI-X mode get passed pointers to the Tx and Rx ring
    	 * structs.  First, attempt to allocate new resources...
    	 */
    	if (set_tx) {
    		memcpy(temp_tx, adapter->tx_ring, size);
    		temp_tx->count = new_tx_count;
    		err = e1000e_setup_tx_resources(temp_tx);
    
    			goto err_setup;
    	}
    	if (set_rx) {
    		memcpy(temp_rx, adapter->rx_ring, size);
    		temp_rx->count = new_rx_count;
    		err = e1000e_setup_rx_resources(temp_rx);
    
    			goto err_setup_rx;
    	}
    
    	/* ...then free the old resources and copy back any new ring data */
    	if (set_tx) {
    
    		e1000e_free_tx_resources(adapter->tx_ring);
    
    		memcpy(adapter->tx_ring, temp_tx, size);
    		adapter->tx_ring_count = new_tx_count;
    	}
    	if (set_rx) {
    		e1000e_free_rx_resources(adapter->rx_ring);
    		memcpy(adapter->rx_ring, temp_rx, size);
    		adapter->rx_ring_count = new_rx_count;
    
    	if (err && set_tx)
    		e1000e_free_tx_resources(temp_tx);
    
    	e1000e_up(adapter);
    free_temp:
    	vfree(temp_tx);
    	vfree(temp_rx);
    clear_reset:
    
    	clear_bit(__E1000_RESETTING, &adapter->state);
    	return err;
    }
    
    
    static bool reg_pattern_test(struct e1000_adapter *adapter, u64 *data,
    			     int reg, int offset, u32 mask, u32 write)
    
    	static const u32 test[] = {
    		0x5A5A5A5A, 0xA5A5A5A5, 0x00000000, 0xFFFFFFFF};
    
    	for (pat = 0; pat < ARRAY_SIZE(test); pat++) {
    
    		E1000_WRITE_REG_ARRAY(&adapter->hw, reg, offset,
    
    				      (test[pat] & write));
    		val = E1000_READ_REG_ARRAY(&adapter->hw, reg, offset);
    		if (val != (test[pat] & write & mask)) {
    
    			e_err("pattern test failed (reg 0x%05X): got 0x%08X expected 0x%08X\n",
    			      reg + (offset << 2), val,
    			      (test[pat] & write & mask));
    
    static bool reg_set_and_check(struct e1000_adapter *adapter, u64 *data,
    			      int reg, u32 mask, u32 write)
    {
    
    	__ew32(&adapter->hw, reg, write & mask);
    
    	val = __er32(&adapter->hw, reg);
    	if ((write & mask) != (val & mask)) {
    
    		e_err("set/check test failed (reg 0x%05X): got 0x%08X expected 0x%08X\n",
    
    		      reg, (val & mask), (write & mask));
    
    #define REG_PATTERN_TEST_ARRAY(reg, offset, mask, write)                       \
    	do {                                                                   \
    		if (reg_pattern_test(adapter, data, reg, offset, mask, write)) \
    			return 1;                                              \
    
    #define REG_PATTERN_TEST(reg, mask, write)                                     \
    	REG_PATTERN_TEST_ARRAY(reg, 0, mask, write)
    
    #define REG_SET_AND_CHECK(reg, mask, write)                                    \
    	do {                                                                   \
    		if (reg_set_and_check(adapter, data, reg, mask, write))        \
    			return 1;                                              \
    
    static int e1000_reg_test(struct e1000_adapter *adapter, u64 *data)
    {
    	struct e1000_hw *hw = &adapter->hw;
    	struct e1000_mac_info *mac = &adapter->hw.mac;
    	u32 value;
    	u32 before;
    	u32 after;
    	u32 i;
    	u32 toggle;
    
    	u32 wlock_mac = 0;
    
    	/* The status register is Read Only, so a write should fail.
    
    	 * Some bits that get toggled are ignored.
    	 */
    	switch (mac->type) {
    	/* there are several bits on newer hardware that are r/w */
    	case e1000_82571:
    	case e1000_82572:
    	case e1000_80003es2lan:
    		toggle = 0x7FFFF3FF;
    		break;
    
    		toggle = 0x7FFFF033;
    		break;
    	}
    
    	before = er32(STATUS);
    	value = (er32(STATUS) & toggle);
    	ew32(STATUS, toggle);
    	after = er32(STATUS) & toggle;
    	if (value != after) {
    
    		e_err("failed STATUS register test got: 0x%08X expected: 0x%08X\n",
    		      after, value);
    
    		*data = 1;
    		return 1;
    	}
    	/* restore previous status */
    	ew32(STATUS, before);
    
    
    	if (!(adapter->flags & FLAG_IS_ICH)) {
    
    		REG_PATTERN_TEST(E1000_FCAL, 0xFFFFFFFF, 0xFFFFFFFF);
    		REG_PATTERN_TEST(E1000_FCAH, 0x0000FFFF, 0xFFFFFFFF);
    		REG_PATTERN_TEST(E1000_FCT, 0x0000FFFF, 0xFFFFFFFF);
    		REG_PATTERN_TEST(E1000_VET, 0x0000FFFF, 0xFFFFFFFF);
    	}
    
    	REG_PATTERN_TEST(E1000_RDTR, 0x0000FFFF, 0xFFFFFFFF);
    
    	REG_PATTERN_TEST(E1000_RDBAH(0), 0xFFFFFFFF, 0xFFFFFFFF);
    	REG_PATTERN_TEST(E1000_RDLEN(0), 0x000FFF80, 0x000FFFFF);
    	REG_PATTERN_TEST(E1000_RDH(0), 0x0000FFFF, 0x0000FFFF);
    	REG_PATTERN_TEST(E1000_RDT(0), 0x0000FFFF, 0x0000FFFF);
    
    	REG_PATTERN_TEST(E1000_FCRTH, 0x0000FFF8, 0x0000FFF8);
    	REG_PATTERN_TEST(E1000_FCTTV, 0x0000FFFF, 0x0000FFFF);
    	REG_PATTERN_TEST(E1000_TIPG, 0x3FFFFFFF, 0x3FFFFFFF);
    
    	REG_PATTERN_TEST(E1000_TDBAH(0), 0xFFFFFFFF, 0xFFFFFFFF);
    	REG_PATTERN_TEST(E1000_TDLEN(0), 0x000FFF80, 0x000FFFFF);
    
    
    	REG_SET_AND_CHECK(E1000_RCTL, 0xFFFFFFFF, 0x00000000);
    
    
    	before = ((adapter->flags & FLAG_IS_ICH) ? 0x06C3B33E : 0x06DFB3FE);
    
    	REG_SET_AND_CHECK(E1000_RCTL, before, 0x003FFFFB);
    	REG_SET_AND_CHECK(E1000_TCTL, 0xFFFFFFFF, 0x00000000);
    
    
    	REG_SET_AND_CHECK(E1000_RCTL, before, 0xFFFFFFFF);
    
    	REG_PATTERN_TEST(E1000_RDBAL(0), 0xFFFFFFF0, 0xFFFFFFFF);
    
    	if (!(adapter->flags & FLAG_IS_ICH))
    
    		REG_PATTERN_TEST(E1000_TXCW, 0xC000FFFF, 0x0000FFFF);
    
    	REG_PATTERN_TEST(E1000_TDBAL(0), 0xFFFFFFF0, 0xFFFFFFFF);
    
    	REG_PATTERN_TEST(E1000_TIDV, 0x0000FFFF, 0x0000FFFF);
    
    	mask = 0x8003FFFF;
    	switch (mac->type) {
    	case e1000_ich10lan:
    	case e1000_pchlan:
    
    	case e1000_pch2lan:
    
    	case e1000_pch_lpt:
    
    		mask |= (1 << 18);
    		break;
    	default:
    		break;
    	}
    
    
    	if (mac->type == e1000_pch_lpt)
    		wlock_mac = (er32(FWSM) & E1000_FWSM_WLOCK_MAC_MASK) >>
    		    E1000_FWSM_WLOCK_MAC_SHIFT;
    
    	for (i = 0; i < mac->rar_entry_count; i++) {
    
    		if (mac->type == e1000_pch_lpt) {
    			/* Cannot test write-protected SHRAL[n] registers */
    			if ((wlock_mac == 1) || (wlock_mac && (i > wlock_mac)))
    				continue;
    
    			/* SHRAH[9] different than the others */
    			if (i == 10)
    				mask |= (1 << 30);
    			else
    				mask &= ~(1 << 30);
    		}
    
    		REG_PATTERN_TEST_ARRAY(E1000_RA, ((i << 1) + 1), mask,
    				       0xFFFFFFFF);
    
    
    	for (i = 0; i < mac->mta_reg_count; i++)
    		REG_PATTERN_TEST_ARRAY(E1000_MTA, i, 0xFFFFFFFF, 0xFFFFFFFF);
    
    	*data = 0;
    
    	return 0;
    }
    
    static int e1000_eeprom_test(struct e1000_adapter *adapter, u64 *data)
    {
    	u16 temp;
    	u16 checksum = 0;
    	u16 i;
    
    	*data = 0;
    	/* Read and add up the contents of the EEPROM */
    	for (i = 0; i < (NVM_CHECKSUM_REG + 1); i++) {
    		if ((e1000_read_nvm(&adapter->hw, i, 1, &temp)) < 0) {
    			*data = 1;
    
    		}
    		checksum += temp;
    	}
    
    	/* If Checksum is not Correct return error else test passed */
    	if ((checksum != (u16) NVM_SUM) && !(*data))
    		*data = 2;
    
    	return *data;
    }
    
    
    static irqreturn_t e1000_test_intr(int __always_unused irq, void *data)
    
    {
    	struct net_device *netdev = (struct net_device *) data;
    	struct e1000_adapter *adapter = netdev_priv(netdev);
    	struct e1000_hw *hw = &adapter->hw;
    
    	adapter->test_icr |= er32(ICR);
    
    	return IRQ_HANDLED;
    }
    
    static int e1000_intr_test(struct e1000_adapter *adapter, u64 *data)
    {
    	struct net_device *netdev = adapter->netdev;
    	struct e1000_hw *hw = &adapter->hw;
    	u32 mask;
    	u32 shared_int = 1;
    	u32 irq = adapter->pdev->irq;
    	int i;
    
    	int ret_val = 0;
    	int int_mode = E1000E_INT_MODE_LEGACY;
    
    	/* NOTE: we don't test MSI/MSI-X interrupts here, yet */
    	if (adapter->int_mode == E1000E_INT_MODE_MSIX) {
    		int_mode = adapter->int_mode;
    		e1000e_reset_interrupt_capability(adapter);
    		adapter->int_mode = E1000E_INT_MODE_LEGACY;
    		e1000e_set_interrupt_capability(adapter);
    	}
    
    	/* Hook up test interrupt handler just for this test */
    
    	if (!request_irq(irq, e1000_test_intr, IRQF_PROBE_SHARED, netdev->name,
    
    	} else if (request_irq(irq, e1000_test_intr, IRQF_SHARED,
    
    		 netdev->name, netdev)) {
    		*data = 1;
    
    		ret_val = -1;
    		goto out;
    
    	e_info("testing %s interrupt\n", (shared_int ? "shared" : "unshared"));
    
    
    	/* Disable all the interrupts */
    	ew32(IMC, 0xFFFFFFFF);
    
    	usleep_range(10000, 20000);
    
    
    	/* Test each interrupt */
    	for (i = 0; i < 10; i++) {
    		/* Interrupt to test */
    		mask = 1 << i;
    
    
    		if (adapter->flags & FLAG_IS_ICH) {
    			switch (mask) {
    			case E1000_ICR_RXSEQ:
    				continue;
    			case 0x00000100:
    				if (adapter->hw.mac.type == e1000_ich8lan ||
    				    adapter->hw.mac.type == e1000_ich9lan)
    					continue;
    				break;
    			default:
    				break;
    			}
    		}