Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d4e0fe01 authored by Alexander Duyck's avatar Alexander Duyck Committed by David S. Miller
Browse files

igbvf: add new driver to support 82576 virtual functions



This adds an igbvf driver to handle virtual functions provided by the
igb driver when SR-IOV has been enabled.  A virtual function is a
lightweight pci-e function that supports a single queue and shares
resources with the 82576 physical function contained within the igb
driver.

To spawn virtual functions from the igb driver all that is needed is to
enable CONFIG_PCI_IOV and have an 82576 Ethernet adapter on a system that
supports SR-IOV in the BIOS.  The virtual functions will appear after the
interface is loaded.

Signed-off-by: default avatarAlexander Duyck <alexander.h.duyck@intel.com>
Signed-off-by: default avatarJeff Kirsher <jeffrey.t.kirsher@intel.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 93889d75
Loading
Loading
Loading
Loading
+21 −0
Original line number Diff line number Diff line
@@ -2058,6 +2058,27 @@ config IGB_DCA
	  driver.  DCA is a method for warming the CPU cache before data
	  is used, with the intent of lessening the impact of cache misses.

config IGBVF
       tristate "Intel(R) 82576 Virtual Function Ethernet support"
       depends on PCI
       ---help---
         This driver supports Intel(R) 82576 virtual functions.  For more
         information on how to identify your adapter, go to the Adapter &
         Driver ID Guide at:

         <http://support.intel.com/support/network/adapter/pro100/21397.htm>

         For general information and support, go to the Intel support
         website at:

         <http://support.intel.com>

         More specific information on configuring the driver is in
         <file:Documentation/networking/e1000.txt>.

         To compile this driver as a module, choose M here. The module
         will be called igbvf.

source "drivers/net/ixp2000/Kconfig"

config MYRI_SBUS
+1 −0
Original line number Diff line number Diff line
@@ -6,6 +6,7 @@ obj-$(CONFIG_E1000) += e1000/
obj-$(CONFIG_E1000E) += e1000e/
obj-$(CONFIG_IBM_NEW_EMAC) += ibm_newemac/
obj-$(CONFIG_IGB) += igb/
obj-$(CONFIG_IGBVF) += igbvf/
obj-$(CONFIG_IXGBE) += ixgbe/
obj-$(CONFIG_IXGB) += ixgb/
obj-$(CONFIG_IP1000) += ipg.o
+38 −0
Original line number Diff line number Diff line
################################################################################
#
# Intel(R) 82576 Virtual Function Linux driver
# Copyright(c) 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:
# e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
# Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
#
################################################################################

#
# Makefile for the Intel(R) 82576 VF ethernet driver
#

obj-$(CONFIG_IGBVF) += igbvf.o

igbvf-objs := vf.o \
              mbx.o \
              ethtool.o \
              netdev.o
+125 −0
Original line number Diff line number Diff line
/*******************************************************************************

  Intel(R) 82576 Virtual Function 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:
  e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497

*******************************************************************************/

#ifndef _E1000_DEFINES_H_
#define _E1000_DEFINES_H_

/* Number of Transmit and Receive Descriptors must be a multiple of 8 */
#define REQ_TX_DESCRIPTOR_MULTIPLE  8
#define REQ_RX_DESCRIPTOR_MULTIPLE  8

/* IVAR valid bit */
#define E1000_IVAR_VALID        0x80

/* Receive Descriptor bit definitions */
#define E1000_RXD_STAT_DD       0x01    /* Descriptor Done */
#define E1000_RXD_STAT_EOP      0x02    /* End of Packet */
#define E1000_RXD_STAT_IXSM     0x04    /* Ignore checksum */
#define E1000_RXD_STAT_VP       0x08    /* IEEE VLAN Packet */
#define E1000_RXD_STAT_UDPCS    0x10    /* UDP xsum calculated */
#define E1000_RXD_STAT_TCPCS    0x20    /* TCP xsum calculated */
#define E1000_RXD_STAT_IPCS     0x40    /* IP xsum calculated */
#define E1000_RXD_ERR_SE        0x02    /* Symbol Error */
#define E1000_RXD_SPC_VLAN_MASK 0x0FFF  /* VLAN ID is in lower 12 bits */

#define E1000_RXDEXT_STATERR_CE    0x01000000
#define E1000_RXDEXT_STATERR_SE    0x02000000
#define E1000_RXDEXT_STATERR_SEQ   0x04000000
#define E1000_RXDEXT_STATERR_CXE   0x10000000
#define E1000_RXDEXT_STATERR_TCPE  0x20000000
#define E1000_RXDEXT_STATERR_IPE   0x40000000
#define E1000_RXDEXT_STATERR_RXE   0x80000000


/* Same mask, but for extended and packet split descriptors */
#define E1000_RXDEXT_ERR_FRAME_ERR_MASK ( \
    E1000_RXDEXT_STATERR_CE  |            \
    E1000_RXDEXT_STATERR_SE  |            \
    E1000_RXDEXT_STATERR_SEQ |            \
    E1000_RXDEXT_STATERR_CXE |            \
    E1000_RXDEXT_STATERR_RXE)

/* Device Control */
#define E1000_CTRL_RST      0x04000000  /* Global reset */

/* Device Status */
#define E1000_STATUS_FD         0x00000001      /* Full duplex.0=half,1=full */
#define E1000_STATUS_LU         0x00000002      /* Link up.0=no,1=link */
#define E1000_STATUS_TXOFF      0x00000010      /* transmission paused */
#define E1000_STATUS_SPEED_10   0x00000000      /* Speed 10Mb/s */
#define E1000_STATUS_SPEED_100  0x00000040      /* Speed 100Mb/s */
#define E1000_STATUS_SPEED_1000 0x00000080      /* Speed 1000Mb/s */

#define SPEED_10    10
#define SPEED_100   100
#define SPEED_1000  1000
#define HALF_DUPLEX 1
#define FULL_DUPLEX 2

/* Transmit Descriptor bit definitions */
#define E1000_TXD_POPTS_IXSM 0x01       /* Insert IP checksum */
#define E1000_TXD_POPTS_TXSM 0x02       /* Insert TCP/UDP checksum */
#define E1000_TXD_CMD_DEXT   0x20000000 /* Descriptor extension (0 = legacy) */
#define E1000_TXD_STAT_DD    0x00000001 /* Descriptor Done */

#define MAX_JUMBO_FRAME_SIZE    0x3F00

/* 802.1q VLAN Packet Size */
#define VLAN_TAG_SIZE              4    /* 802.3ac tag (not DMA'd) */

/* Error Codes */
#define E1000_SUCCESS      0
#define E1000_ERR_CONFIG   3
#define E1000_ERR_MAC_INIT 5
#define E1000_ERR_MBX      15

#ifndef ETH_ADDR_LEN
#define ETH_ADDR_LEN                 6
#endif

/* SRRCTL bit definitions */
#define E1000_SRRCTL_BSIZEPKT_SHIFT                     10 /* Shift _right_ */
#define E1000_SRRCTL_BSIZEHDRSIZE_MASK                  0x00000F00
#define E1000_SRRCTL_BSIZEHDRSIZE_SHIFT                 2  /* Shift _left_ */
#define E1000_SRRCTL_DESCTYPE_ADV_ONEBUF                0x02000000
#define E1000_SRRCTL_DESCTYPE_HDR_SPLIT_ALWAYS          0x0A000000
#define E1000_SRRCTL_DESCTYPE_MASK                      0x0E000000
#define E1000_SRRCTL_DROP_EN                            0x80000000

#define E1000_SRRCTL_BSIZEPKT_MASK      0x0000007F
#define E1000_SRRCTL_BSIZEHDR_MASK      0x00003F00

/* Additional Descriptor Control definitions */
#define E1000_TXDCTL_QUEUE_ENABLE  0x02000000 /* Enable specific Tx Queue */
#define E1000_RXDCTL_QUEUE_ENABLE  0x02000000 /* Enable specific Rx Queue */

/* Direct Cache Access (DCA) definitions */
#define E1000_DCA_TXCTRL_TX_WB_RO_EN (1 << 11) /* Tx Desc writeback RO bit */

#define E1000_VF_INIT_TIMEOUT 200 /* Number of retries to clear RSTI */

#endif /* _E1000_DEFINES_H_ */
+540 −0
Original line number Diff line number Diff line
/*******************************************************************************

  Intel(R) 82576 Virtual Function Linux driver
  Copyright(c) 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:
  e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497

*******************************************************************************/

/* ethtool support for igbvf */

#include <linux/netdevice.h>
#include <linux/ethtool.h>
#include <linux/pci.h>
#include <linux/vmalloc.h>
#include <linux/delay.h>

#include "igbvf.h"
#include <linux/if_vlan.h>


struct igbvf_stats {
	char stat_string[ETH_GSTRING_LEN];
	int sizeof_stat;
	int stat_offset;
	int base_stat_offset;
};

#define IGBVF_STAT(current, base) \
		sizeof(((struct igbvf_adapter *)0)->current), \
		offsetof(struct igbvf_adapter, current), \
		offsetof(struct igbvf_adapter, base)

static const struct igbvf_stats igbvf_gstrings_stats[] = {
	{ "rx_packets", IGBVF_STAT(stats.gprc, stats.base_gprc) },
	{ "tx_packets", IGBVF_STAT(stats.gptc, stats.base_gptc) },
	{ "rx_bytes", IGBVF_STAT(stats.gorc, stats.base_gorc) },
	{ "tx_bytes", IGBVF_STAT(stats.gotc, stats.base_gotc) },
	{ "multicast", IGBVF_STAT(stats.mprc, stats.base_mprc) },
	{ "lbrx_bytes", IGBVF_STAT(stats.gorlbc, stats.base_gorlbc) },
	{ "lbrx_packets", IGBVF_STAT(stats.gprlbc, stats.base_gprlbc) },
	{ "tx_restart_queue", IGBVF_STAT(restart_queue, zero_base) },
	{ "rx_long_byte_count", IGBVF_STAT(stats.gorc, stats.base_gorc) },
	{ "rx_csum_offload_good", IGBVF_STAT(hw_csum_good, zero_base) },
	{ "rx_csum_offload_errors", IGBVF_STAT(hw_csum_err, zero_base) },
	{ "rx_header_split", IGBVF_STAT(rx_hdr_split, zero_base) },
	{ "alloc_rx_buff_failed", IGBVF_STAT(alloc_rx_buff_failed, zero_base) },
};

#define IGBVF_GLOBAL_STATS_LEN ARRAY_SIZE(igbvf_gstrings_stats)

static const char igbvf_gstrings_test[][ETH_GSTRING_LEN] = {
	"Link test   (on/offline)"
};

#define IGBVF_TEST_LEN ARRAY_SIZE(igbvf_gstrings_test)

static int igbvf_get_settings(struct net_device *netdev,
                              struct ethtool_cmd *ecmd)
{
	struct igbvf_adapter *adapter = netdev_priv(netdev);
	struct e1000_hw *hw = &adapter->hw;
	u32 status;

	ecmd->supported   = SUPPORTED_1000baseT_Full;

	ecmd->advertising = ADVERTISED_1000baseT_Full;

	ecmd->port = -1;
	ecmd->transceiver = XCVR_DUMMY1;

	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 = AUTONEG_DISABLE;

	return 0;
}

static u32 igbvf_get_link(struct net_device *netdev)
{
	return netif_carrier_ok(netdev);
}

static int igbvf_set_settings(struct net_device *netdev,
                              struct ethtool_cmd *ecmd)
{
	return -EOPNOTSUPP;
}

static void igbvf_get_pauseparam(struct net_device *netdev,
                                 struct ethtool_pauseparam *pause)
{
	return;
}

static int igbvf_set_pauseparam(struct net_device *netdev,
                                struct ethtool_pauseparam *pause)
{
	return -EOPNOTSUPP;
}

static u32 igbvf_get_tx_csum(struct net_device *netdev)
{
	return ((netdev->features & NETIF_F_IP_CSUM) != 0);
}

static int igbvf_set_tx_csum(struct net_device *netdev, u32 data)
{
	if (data)
		netdev->features |= (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM);
	else
		netdev->features &= ~(NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM);
	return 0;
}

static int igbvf_set_tso(struct net_device *netdev, u32 data)
{
	struct igbvf_adapter *adapter = netdev_priv(netdev);
	int i;
	struct net_device *v_netdev;

	if (data) {
		netdev->features |= NETIF_F_TSO;
		netdev->features |= NETIF_F_TSO6;
	} else {
		netdev->features &= ~NETIF_F_TSO;
		netdev->features &= ~NETIF_F_TSO6;
		/* 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;
			v_netdev->features &= ~NETIF_F_TSO6;
			vlan_group_set_device(adapter->vlgrp, i, v_netdev);
		}
	}

tso_out:
	dev_info(&adapter->pdev->dev, "TSO is %s\n",
	         data ? "Enabled" : "Disabled");
	adapter->flags |= FLAG_TSO_FORCE;
	return 0;
}

static u32 igbvf_get_msglevel(struct net_device *netdev)
{
	struct igbvf_adapter *adapter = netdev_priv(netdev);
	return adapter->msg_enable;
}

static void igbvf_set_msglevel(struct net_device *netdev, u32 data)
{
	struct igbvf_adapter *adapter = netdev_priv(netdev);
	adapter->msg_enable = data;
}

static int igbvf_get_regs_len(struct net_device *netdev)
{
#define IGBVF_REGS_LEN 8
	return IGBVF_REGS_LEN * sizeof(u32);
}

static void igbvf_get_regs(struct net_device *netdev,
                           struct ethtool_regs *regs, void *p)
{
	struct igbvf_adapter *adapter = netdev_priv(netdev);
	struct e1000_hw *hw = &adapter->hw;
	u32 *regs_buff = p;
	u8 revision_id;

	memset(p, 0, IGBVF_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(RDLEN(0));
	regs_buff[3] = er32(RDH(0));
	regs_buff[4] = er32(RDT(0));

	regs_buff[5] = er32(TDLEN(0));
	regs_buff[6] = er32(TDH(0));
	regs_buff[7] = er32(TDT(0));
}

static int igbvf_get_eeprom_len(struct net_device *netdev)
{
	return 0;
}

static int igbvf_get_eeprom(struct net_device *netdev,
                            struct ethtool_eeprom *eeprom, u8 *bytes)
{
	return -EOPNOTSUPP;
}

static int igbvf_set_eeprom(struct net_device *netdev,
                            struct ethtool_eeprom *eeprom, u8 *bytes)
{
	return -EOPNOTSUPP;
}

static void igbvf_get_drvinfo(struct net_device *netdev,
                              struct ethtool_drvinfo *drvinfo)
{
	struct igbvf_adapter *adapter = netdev_priv(netdev);
	char firmware_version[32] = "N/A";

	strncpy(drvinfo->driver,  igbvf_driver_name, 32);
	strncpy(drvinfo->version, igbvf_driver_version, 32);
	strncpy(drvinfo->fw_version, firmware_version, 32);
	strncpy(drvinfo->bus_info, pci_name(adapter->pdev), 32);
	drvinfo->regdump_len = igbvf_get_regs_len(netdev);
	drvinfo->eedump_len = igbvf_get_eeprom_len(netdev);
}

static void igbvf_get_ringparam(struct net_device *netdev,
                                struct ethtool_ringparam *ring)
{
	struct igbvf_adapter *adapter = netdev_priv(netdev);
	struct igbvf_ring *tx_ring = adapter->tx_ring;
	struct igbvf_ring *rx_ring = adapter->rx_ring;

	ring->rx_max_pending = IGBVF_MAX_RXD;
	ring->tx_max_pending = IGBVF_MAX_TXD;
	ring->rx_mini_max_pending = 0;
	ring->rx_jumbo_max_pending = 0;
	ring->rx_pending = rx_ring->count;
	ring->tx_pending = tx_ring->count;
	ring->rx_mini_pending = 0;
	ring->rx_jumbo_pending = 0;
}

static int igbvf_set_ringparam(struct net_device *netdev,
                               struct ethtool_ringparam *ring)
{
	struct igbvf_adapter *adapter = netdev_priv(netdev);
	struct igbvf_ring *temp_ring;
	int err;
	u32 new_rx_count, new_tx_count;

	if ((ring->rx_mini_pending) || (ring->rx_jumbo_pending))
		return -EINVAL;

	new_rx_count = max(ring->rx_pending, (u32)IGBVF_MIN_RXD);
	new_rx_count = min(new_rx_count, (u32)IGBVF_MAX_RXD);
	new_rx_count = ALIGN(new_rx_count, REQ_RX_DESCRIPTOR_MULTIPLE);

	new_tx_count = max(ring->tx_pending, (u32)IGBVF_MIN_TXD);
	new_tx_count = min(new_tx_count, (u32)IGBVF_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;
	}

	temp_ring = vmalloc(sizeof(struct igbvf_ring));
	if (!temp_ring)
		return -ENOMEM;

	while (test_and_set_bit(__IGBVF_RESETTING, &adapter->state))
		msleep(1);

	if (netif_running(adapter->netdev))
		igbvf_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.
	 */
	if (new_tx_count != adapter->tx_ring->count) {
		memcpy(temp_ring, adapter->tx_ring, sizeof(struct igbvf_ring));

		temp_ring->count = new_tx_count;
		err = igbvf_setup_tx_resources(adapter, temp_ring);
		if (err)
			goto err_setup;

		igbvf_free_tx_resources(adapter->tx_ring);

		memcpy(adapter->tx_ring, temp_ring, sizeof(struct igbvf_ring));
	}

	if (new_rx_count != adapter->rx_ring->count) {
		memcpy(temp_ring, adapter->rx_ring, sizeof(struct igbvf_ring));

		temp_ring->count = new_rx_count;
		err = igbvf_setup_rx_resources(adapter, temp_ring);
		if (err)
			goto err_setup;

		igbvf_free_rx_resources(adapter->rx_ring);

		memcpy(adapter->rx_ring, temp_ring,sizeof(struct igbvf_ring));
	}

	err = 0;
err_setup:
	if (netif_running(adapter->netdev))
		igbvf_up(adapter);

	clear_bit(__IGBVF_RESETTING, &adapter->state);
	vfree(temp_ring);
	return err;
}

static int igbvf_link_test(struct igbvf_adapter *adapter, u64 *data)
{
	struct e1000_hw *hw = &adapter->hw;
	*data = 0;

	hw->mac.ops.check_for_link(hw);

	if (!(er32(STATUS) & E1000_STATUS_LU))
		*data = 1;

	return *data;
}

static int igbvf_get_self_test_count(struct net_device *netdev)
{
	return IGBVF_TEST_LEN;
}

static int igbvf_get_stats_count(struct net_device *netdev)
{
	return IGBVF_GLOBAL_STATS_LEN;
}

static void igbvf_diag_test(struct net_device *netdev,
                            struct ethtool_test *eth_test, u64 *data)
{
	struct igbvf_adapter *adapter = netdev_priv(netdev);

	set_bit(__IGBVF_TESTING, &adapter->state);

	/*
	 * Link test performed before hardware reset so autoneg doesn't
	 * interfere with test result
	 */
	if (igbvf_link_test(adapter, &data[0]))
		eth_test->flags |= ETH_TEST_FL_FAILED;

	clear_bit(__IGBVF_TESTING, &adapter->state);
	msleep_interruptible(4 * 1000);
}

static void igbvf_get_wol(struct net_device *netdev,
                          struct ethtool_wolinfo *wol)
{
	wol->supported = 0;
	wol->wolopts = 0;

	return;
}

static int igbvf_set_wol(struct net_device *netdev,
                         struct ethtool_wolinfo *wol)
{
	return -EOPNOTSUPP;
}

static int igbvf_phys_id(struct net_device *netdev, u32 data)
{
	return 0;
}

static int igbvf_get_coalesce(struct net_device *netdev,
                              struct ethtool_coalesce *ec)
{
	struct igbvf_adapter *adapter = netdev_priv(netdev);

	if (adapter->itr_setting <= 3)
		ec->rx_coalesce_usecs = adapter->itr_setting;
	else
		ec->rx_coalesce_usecs = adapter->itr_setting >> 2;

	return 0;
}

static int igbvf_set_coalesce(struct net_device *netdev,
                              struct ethtool_coalesce *ec)
{
	struct igbvf_adapter *adapter = netdev_priv(netdev);
	struct e1000_hw *hw = &adapter->hw;

	if ((ec->rx_coalesce_usecs > IGBVF_MAX_ITR_USECS) ||
	    ((ec->rx_coalesce_usecs > 3) &&
	     (ec->rx_coalesce_usecs < IGBVF_MIN_ITR_USECS)) ||
	    (ec->rx_coalesce_usecs == 2))
		return -EINVAL;

	/* convert to rate of irq's per second */
	if (ec->rx_coalesce_usecs && ec->rx_coalesce_usecs <= 3) {
		adapter->itr = IGBVF_START_ITR;
		adapter->itr_setting = ec->rx_coalesce_usecs;
	} else {
		adapter->itr = ec->rx_coalesce_usecs << 2;
		adapter->itr_setting = adapter->itr;
	}

	writel(adapter->itr,
	       hw->hw_addr + adapter->rx_ring[0].itr_register);

	return 0;
}

static int igbvf_nway_reset(struct net_device *netdev)
{
	struct igbvf_adapter *adapter = netdev_priv(netdev);
	if (netif_running(netdev))
		igbvf_reinit_locked(adapter);
	return 0;
}


static void igbvf_get_ethtool_stats(struct net_device *netdev,
                                    struct ethtool_stats *stats,
                                    u64 *data)
{
	struct igbvf_adapter *adapter = netdev_priv(netdev);
	int i;

	igbvf_update_stats(adapter);
	for (i = 0; i < IGBVF_GLOBAL_STATS_LEN; i++) {
		char *p = (char *)adapter +
		          igbvf_gstrings_stats[i].stat_offset;
		char *b = (char *)adapter +
		          igbvf_gstrings_stats[i].base_stat_offset;
		data[i] = ((igbvf_gstrings_stats[i].sizeof_stat ==
		            sizeof(u64)) ? (*(u64 *)p - *(u64 *)b) :
		            (*(u32 *)p - *(u32 *)b));
	}

}

static void igbvf_get_strings(struct net_device *netdev, u32 stringset,
                              u8 *data)
{
	u8 *p = data;
	int i;

	switch (stringset) {
	case ETH_SS_TEST:
		memcpy(data, *igbvf_gstrings_test, sizeof(igbvf_gstrings_test));
		break;
	case ETH_SS_STATS:
		for (i = 0; i < IGBVF_GLOBAL_STATS_LEN; i++) {
			memcpy(p, igbvf_gstrings_stats[i].stat_string,
			       ETH_GSTRING_LEN);
			p += ETH_GSTRING_LEN;
		}
		break;
	}
}

static const struct ethtool_ops igbvf_ethtool_ops = {
	.get_settings		= igbvf_get_settings,
	.set_settings		= igbvf_set_settings,
	.get_drvinfo		= igbvf_get_drvinfo,
	.get_regs_len		= igbvf_get_regs_len,
	.get_regs		= igbvf_get_regs,
	.get_wol		= igbvf_get_wol,
	.set_wol		= igbvf_set_wol,
	.get_msglevel		= igbvf_get_msglevel,
	.set_msglevel		= igbvf_set_msglevel,
	.nway_reset		= igbvf_nway_reset,
	.get_link		= igbvf_get_link,
	.get_eeprom_len		= igbvf_get_eeprom_len,
	.get_eeprom		= igbvf_get_eeprom,
	.set_eeprom		= igbvf_set_eeprom,
	.get_ringparam		= igbvf_get_ringparam,
	.set_ringparam		= igbvf_set_ringparam,
	.get_pauseparam		= igbvf_get_pauseparam,
	.set_pauseparam		= igbvf_set_pauseparam,
	.get_tx_csum		= igbvf_get_tx_csum,
	.set_tx_csum		= igbvf_set_tx_csum,
	.get_sg			= ethtool_op_get_sg,
	.set_sg			= ethtool_op_set_sg,
	.get_tso		= ethtool_op_get_tso,
	.set_tso		= igbvf_set_tso,
	.self_test		= igbvf_diag_test,
	.get_strings		= igbvf_get_strings,
	.phys_id		= igbvf_phys_id,
	.get_ethtool_stats	= igbvf_get_ethtool_stats,
	.self_test_count	= igbvf_get_self_test_count,
	.get_stats_count	= igbvf_get_stats_count,
	.get_coalesce		= igbvf_get_coalesce,
	.set_coalesce		= igbvf_set_coalesce,
};

void igbvf_set_ethtool_ops(struct net_device *netdev)
{
	/* have to "undeclare" const on this struct to remove warnings */
	SET_ETHTOOL_OPS(netdev, (struct ethtool_ops *)&igbvf_ethtool_ops);
}
Loading