blob: 513ba697435594e0749bbb3ba7817f2ccb3caa12 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/* Copyright(c) 2013 - 2018 Intel Corporation. */
/* ethtool support for i40e */
#include "i40e.h"
#include "i40e_diag.h"
#include "i40e_txrx_common.h"
/* ethtool statistics helpers */
/**
* struct i40e_stats - definition for an ethtool statistic
* @stat_string: statistic name to display in ethtool -S output
* @sizeof_stat: the sizeof() the stat, must be no greater than sizeof(u64)
* @stat_offset: offsetof() the stat from a base pointer
*
* This structure defines a statistic to be added to the ethtool stats buffer.
* It defines a statistic as offset from a common base pointer. Stats should
* be defined in constant arrays using the I40E_STAT macro, with every element
* of the array using the same _type for calculating the sizeof_stat and
* stat_offset.
*
* The @sizeof_stat is expected to be sizeof(u8), sizeof(u16), sizeof(u32) or
* sizeof(u64). Other sizes are not expected and will produce a WARN_ONCE from
* the i40e_add_ethtool_stat() helper function.
*
* The @stat_string is interpreted as a format string, allowing formatted
* values to be inserted while looping over multiple structures for a given
* statistics array. Thus, every statistic string in an array should have the
* same type and number of format specifiers, to be formatted by variadic
* arguments to the i40e_add_stat_string() helper function.
**/
struct i40e_stats {
char stat_string[ETH_GSTRING_LEN];
int sizeof_stat;
int stat_offset;
};
/* Helper macro to define an i40e_stat structure with proper size and type.
* Use this when defining constant statistics arrays. Note that @_type expects
* only a type name and is used multiple times.
*/
#define I40E_STAT(_type, _name, _stat) { \
.stat_string = _name, \
.sizeof_stat = sizeof_field(_type, _stat), \
.stat_offset = offsetof(_type, _stat) \
}
/* Helper macro for defining some statistics directly copied from the netdev
* stats structure.
*/
#define I40E_NETDEV_STAT(_net_stat) \
I40E_STAT(struct rtnl_link_stats64, #_net_stat, _net_stat)
/* Helper macro for defining some statistics related to queues */
#define I40E_QUEUE_STAT(_name, _stat) \
I40E_STAT(struct i40e_ring, _name, _stat)
/* Stats associated with a Tx or Rx ring */
static const struct i40e_stats i40e_gstrings_queue_stats[] = {
I40E_QUEUE_STAT("%s-%u.packets", stats.packets),
I40E_QUEUE_STAT("%s-%u.bytes", stats.bytes),
};
/**
* i40e_add_one_ethtool_stat - copy the stat into the supplied buffer
* @data: location to store the stat value
* @pointer: basis for where to copy from
* @stat: the stat definition
*
* Copies the stat data defined by the pointer and stat structure pair into
* the memory supplied as data. Used to implement i40e_add_ethtool_stats and
* i40e_add_queue_stats. If the pointer is null, data will be zero'd.
*/
static void
i40e_add_one_ethtool_stat(u64 *data, void *pointer,
const struct i40e_stats *stat)
{
char *p;
if (!pointer) {
/* ensure that the ethtool data buffer is zero'd for any stats
* which don't have a valid pointer.
*/
*data = 0;
return;
}
p = (char *)pointer + stat->stat_offset;
switch (stat->sizeof_stat) {
case sizeof(u64):
*data = *((u64 *)p);
break;
case sizeof(u32):
*data = *((u32 *)p);
break;
case sizeof(u16):
*data = *((u16 *)p);
break;
case sizeof(u8):
*data = *((u8 *)p);
break;
default:
WARN_ONCE(1, "unexpected stat size for %s",
stat->stat_string);
*data = 0;
}
}
/**
* __i40e_add_ethtool_stats - copy stats into the ethtool supplied buffer
* @data: ethtool stats buffer
* @pointer: location to copy stats from
* @stats: array of stats to copy
* @size: the size of the stats definition
*
* Copy the stats defined by the stats array using the pointer as a base into
* the data buffer supplied by ethtool. Updates the data pointer to point to
* the next empty location for successive calls to __i40e_add_ethtool_stats.
* If pointer is null, set the data values to zero and update the pointer to
* skip these stats.
**/
static void
__i40e_add_ethtool_stats(u64 **data, void *pointer,
const struct i40e_stats stats[],
const unsigned int size)
{
unsigned int i;
for (i = 0; i < size; i++)
i40e_add_one_ethtool_stat((*data)++, pointer, &stats[i]);
}
/**
* i40e_add_ethtool_stats - copy stats into ethtool supplied buffer
* @data: ethtool stats buffer
* @pointer: location where stats are stored
* @stats: static const array of stat definitions
*
* Macro to ease the use of __i40e_add_ethtool_stats by taking a static
* constant stats array and passing the ARRAY_SIZE(). This avoids typos by
* ensuring that we pass the size associated with the given stats array.
*
* The parameter @stats is evaluated twice, so parameters with side effects
* should be avoided.
**/
#define i40e_add_ethtool_stats(data, pointer, stats) \
__i40e_add_ethtool_stats(data, pointer, stats, ARRAY_SIZE(stats))
/**
* i40e_add_queue_stats - copy queue statistics into supplied buffer
* @data: ethtool stats buffer
* @ring: the ring to copy
*
* Queue statistics must be copied while protected by
* u64_stats_fetch_begin_irq, so we can't directly use i40e_add_ethtool_stats.
* Assumes that queue stats are defined in i40e_gstrings_queue_stats. If the
* ring pointer is null, zero out the queue stat values and update the data
* pointer. Otherwise safely copy the stats from the ring into the supplied
* buffer and update the data pointer when finished.
*
* This function expects to be called while under rcu_read_lock().
**/
static void
i40e_add_queue_stats(u64 **data, struct i40e_ring *ring)
{
const unsigned int size = ARRAY_SIZE(i40e_gstrings_queue_stats);
const struct i40e_stats *stats = i40e_gstrings_queue_stats;
unsigned int start;
unsigned int i;
/* To avoid invalid statistics values, ensure that we keep retrying
* the copy until we get a consistent value according to
* u64_stats_fetch_retry_irq. But first, make sure our ring is
* non-null before attempting to access its syncp.
*/
do {
start = !ring ? 0 : u64_stats_fetch_begin_irq(&ring->syncp);
for (i = 0; i < size; i++) {
i40e_add_one_ethtool_stat(&(*data)[i], ring,
&stats[i]);
}
} while (ring && u64_stats_fetch_retry_irq(&ring->syncp, start));
/* Once we successfully copy the stats in, update the data pointer */
*data += size;
}
/**
* __i40e_add_stat_strings - copy stat strings into ethtool buffer
* @p: ethtool supplied buffer
* @stats: stat definitions array
* @size: size of the stats array
*
* Format and copy the strings described by stats into the buffer pointed at
* by p.
**/
static void __i40e_add_stat_strings(u8 **p, const struct i40e_stats stats[],
const unsigned int size, ...)
{
unsigned int i;
for (i = 0; i < size; i++) {
va_list args;
va_start(args, size);
vsnprintf(*p, ETH_GSTRING_LEN, stats[i].stat_string, args);
*p += ETH_GSTRING_LEN;
va_end(args);
}
}
/**
* i40e_add_stat_strings - copy stat strings into ethtool buffer
* @p: ethtool supplied buffer
* @stats: stat definitions array
*
* Format and copy the strings described by the const static stats value into
* the buffer pointed at by p.
*
* The parameter @stats is evaluated twice, so parameters with side effects
* should be avoided. Additionally, stats must be an array such that
* ARRAY_SIZE can be called on it.
**/
#define i40e_add_stat_strings(p, stats, ...) \
__i40e_add_stat_strings(p, stats, ARRAY_SIZE(stats), ## __VA_ARGS__)
#define I40E_PF_STAT(_name, _stat) \
I40E_STAT(struct i40e_pf, _name, _stat)
#define I40E_VSI_STAT(_name, _stat) \
I40E_STAT(struct i40e_vsi, _name, _stat)
#define I40E_VEB_STAT(_name, _stat) \
I40E_STAT(struct i40e_veb, _name, _stat)
#define I40E_VEB_TC_STAT(_name, _stat) \
I40E_STAT(struct i40e_cp_veb_tc_stats, _name, _stat)
#define I40E_PFC_STAT(_name, _stat) \
I40E_STAT(struct i40e_pfc_stats, _name, _stat)
#define I40E_QUEUE_STAT(_name, _stat) \
I40E_STAT(struct i40e_ring, _name, _stat)
static const struct i40e_stats i40e_gstrings_net_stats[] = {
I40E_NETDEV_STAT(rx_packets),
I40E_NETDEV_STAT(tx_packets),
I40E_NETDEV_STAT(rx_bytes),
I40E_NETDEV_STAT(tx_bytes),
I40E_NETDEV_STAT(rx_errors),
I40E_NETDEV_STAT(tx_errors),
I40E_NETDEV_STAT(rx_dropped),
I40E_NETDEV_STAT(tx_dropped),
I40E_NETDEV_STAT(collisions),
I40E_NETDEV_STAT(rx_length_errors),
I40E_NETDEV_STAT(rx_crc_errors),
};
static const struct i40e_stats i40e_gstrings_veb_stats[] = {
I40E_VEB_STAT("veb.rx_bytes", stats.rx_bytes),
I40E_VEB_STAT("veb.tx_bytes", stats.tx_bytes),
I40E_VEB_STAT("veb.rx_unicast", stats.rx_unicast),
I40E_VEB_STAT("veb.tx_unicast", stats.tx_unicast),
I40E_VEB_STAT("veb.rx_multicast", stats.rx_multicast),
I40E_VEB_STAT("veb.tx_multicast", stats.tx_multicast),
I40E_VEB_STAT("veb.rx_broadcast", stats.rx_broadcast),
I40E_VEB_STAT("veb.tx_broadcast", stats.tx_broadcast),
I40E_VEB_STAT("veb.rx_discards", stats.rx_discards),
I40E_VEB_STAT("veb.tx_discards", stats.tx_discards),
I40E_VEB_STAT("veb.tx_errors", stats.tx_errors),
I40E_VEB_STAT("veb.rx_unknown_protocol", stats.rx_unknown_protocol),
};
struct i40e_cp_veb_tc_stats {
u64 tc_rx_packets;
u64 tc_rx_bytes;
u64 tc_tx_packets;
u64 tc_tx_bytes;
};
static const struct i40e_stats i40e_gstrings_veb_tc_stats[] = {
I40E_VEB_TC_STAT("veb.tc_%u_tx_packets", tc_tx_packets),
I40E_VEB_TC_STAT("veb.tc_%u_tx_bytes", tc_tx_bytes),
I40E_VEB_TC_STAT("veb.tc_%u_rx_packets", tc_rx_packets),
I40E_VEB_TC_STAT("veb.tc_%u_rx_bytes", tc_rx_bytes),
};
static const struct i40e_stats i40e_gstrings_misc_stats[] = {
I40E_VSI_STAT("rx_unicast", eth_stats.rx_unicast),
I40E_VSI_STAT("tx_unicast", eth_stats.tx_unicast),
I40E_VSI_STAT("rx_multicast", eth_stats.rx_multicast),
I40E_VSI_STAT("tx_multicast", eth_stats.tx_multicast),
I40E_VSI_STAT("rx_broadcast", eth_stats.rx_broadcast),
I40E_VSI_STAT("tx_broadcast", eth_stats.tx_broadcast),
I40E_VSI_STAT("rx_unknown_protocol", eth_stats.rx_unknown_protocol),
I40E_VSI_STAT("tx_linearize", tx_linearize),
I40E_VSI_STAT("tx_force_wb", tx_force_wb),
I40E_VSI_STAT("tx_busy", tx_busy),
I40E_VSI_STAT("rx_alloc_fail", rx_buf_failed),
I40E_VSI_STAT("rx_pg_alloc_fail", rx_page_failed),
};
/* These PF_STATs might look like duplicates of some NETDEV_STATs,
* but they are separate. This device supports Virtualization, and
* as such might have several netdevs supporting VMDq and FCoE going
* through a single port. The NETDEV_STATs are for individual netdevs
* seen at the top of the stack, and the PF_STATs are for the physical
* function at the bottom of the stack hosting those netdevs.
*
* The PF_STATs are appended to the netdev stats only when ethtool -S
* is queried on the base PF netdev, not on the VMDq or FCoE netdev.
*/
static const struct i40e_stats i40e_gstrings_stats[] = {
I40E_PF_STAT("port.rx_bytes", stats.eth.rx_bytes),
I40E_PF_STAT("port.tx_bytes", stats.eth.tx_bytes),
I40E_PF_STAT("port.rx_unicast", stats.eth.rx_unicast),
I40E_PF_STAT("port.tx_unicast", stats.eth.tx_unicast),
I40E_PF_STAT("port.rx_multicast", stats.eth.rx_multicast),
I40E_PF_STAT("port.tx_multicast", stats.eth.tx_multicast),
I40E_PF_STAT("port.rx_broadcast", stats.eth.rx_broadcast),
I40E_PF_STAT("port.tx_broadcast", stats.eth.tx_broadcast),
I40E_PF_STAT("port.tx_errors", stats.eth.tx_errors),
I40E_PF_STAT("port.rx_dropped", stats.eth.rx_discards),
I40E_PF_STAT("port.tx_dropped_link_down", stats.tx_dropped_link_down),
I40E_PF_STAT("port.rx_crc_errors", stats.crc_errors),
I40E_PF_STAT("port.illegal_bytes", stats.illegal_bytes),
I40E_PF_STAT("port.mac_local_faults", stats.mac_local_faults),
I40E_PF_STAT("port.mac_remote_faults", stats.mac_remote_faults),
I40E_PF_STAT("port.tx_timeout", tx_timeout_count),
I40E_PF_STAT("port.rx_csum_bad", hw_csum_rx_error),
I40E_PF_STAT("port.rx_length_errors", stats.rx_length_errors),
I40E_PF_STAT("port.link_xon_rx", stats.link_xon_rx),
I40E_PF_STAT("port.link_xoff_rx", stats.link_xoff_rx),
I40E_PF_STAT("port.link_xon_tx", stats.link_xon_tx),
I40E_PF_STAT("port.link_xoff_tx", stats.link_xoff_tx),
I40E_PF_STAT("port.rx_size_64", stats.rx_size_64),
I40E_PF_STAT("port.rx_size_127", stats.rx_size_127),
I40E_PF_STAT("port.rx_size_255", stats.rx_size_255),
I40E_PF_STAT("port.rx_size_511", stats.rx_size_511),
I40E_PF_STAT("port.rx_size_1023", stats.rx_size_1023),
I40E_PF_STAT("port.rx_size_1522", stats.rx_size_1522),
I40E_PF_STAT("port.rx_size_big", stats.rx_size_big),
I40E_PF_STAT("port.tx_size_64", stats.tx_size_64),
I40E_PF_STAT("port.tx_size_127", stats.tx_size_127),
I40E_PF_STAT("port.tx_size_255", stats.tx_size_255),
I40E_PF_STAT("port.tx_size_511", stats.tx_size_511),
I40E_PF_STAT("port.tx_size_1023", stats.tx_size_1023),
I40E_PF_STAT("port.tx_size_1522", stats.tx_size_1522),
I40E_PF_STAT("port.tx_size_big", stats.tx_size_big),
I40E_PF_STAT("port.rx_undersize", stats.rx_undersize),
I40E_PF_STAT("port.rx_fragments", stats.rx_fragments),
I40E_PF_STAT("port.rx_oversize", stats.rx_oversize),
I40E_PF_STAT("port.rx_jabber", stats.rx_jabber),
I40E_PF_STAT("port.VF_admin_queue_requests", vf_aq_requests),
I40E_PF_STAT("port.arq_overflows", arq_overflows),
I40E_PF_STAT("port.tx_hwtstamp_timeouts", tx_hwtstamp_timeouts),
I40E_PF_STAT("port.rx_hwtstamp_cleared", rx_hwtstamp_cleared),
I40E_PF_STAT("port.tx_hwtstamp_skipped", tx_hwtstamp_skipped),
I40E_PF_STAT("port.fdir_flush_cnt", fd_flush_cnt),
I40E_PF_STAT("port.fdir_atr_match", stats.fd_atr_match),
I40E_PF_STAT("port.fdir_atr_tunnel_match", stats.fd_atr_tunnel_match),
I40E_PF_STAT("port.fdir_atr_status", stats.fd_atr_status),
I40E_PF_STAT("port.fdir_sb_match", stats.fd_sb_match),
I40E_PF_STAT("port.fdir_sb_status", stats.fd_sb_status),
/* LPI stats */
I40E_PF_STAT("port.tx_lpi_status", stats.tx_lpi_status),
I40E_PF_STAT("port.rx_lpi_status", stats.rx_lpi_status),
I40E_PF_STAT("port.tx_lpi_count", stats.tx_lpi_count),
I40E_PF_STAT("port.rx_lpi_count", stats.rx_lpi_count),
};
struct i40e_pfc_stats {
u64 priority_xon_rx;
u64 priority_xoff_rx;
u64 priority_xon_tx;
u64 priority_xoff_tx;
u64 priority_xon_2_xoff;
};
static const struct i40e_stats i40e_gstrings_pfc_stats[] = {
I40E_PFC_STAT("port.tx_priority_%u_xon_tx", priority_xon_tx),
I40E_PFC_STAT("port.tx_priority_%u_xoff_tx", priority_xoff_tx),
I40E_PFC_STAT("port.rx_priority_%u_xon_rx", priority_xon_rx),
I40E_PFC_STAT("port.rx_priority_%u_xoff_rx", priority_xoff_rx),
I40E_PFC_STAT("port.rx_priority_%u_xon_2_xoff", priority_xon_2_xoff),
};
#define I40E_NETDEV_STATS_LEN ARRAY_SIZE(i40e_gstrings_net_stats)
#define I40E_MISC_STATS_LEN ARRAY_SIZE(i40e_gstrings_misc_stats)
#define I40E_VSI_STATS_LEN (I40E_NETDEV_STATS_LEN + I40E_MISC_STATS_LEN)
#define I40E_PFC_STATS_LEN (ARRAY_SIZE(i40e_gstrings_pfc_stats) * \
I40E_MAX_USER_PRIORITY)
#define I40E_VEB_STATS_LEN (ARRAY_SIZE(i40e_gstrings_veb_stats) + \
(ARRAY_SIZE(i40e_gstrings_veb_tc_stats) * \
I40E_MAX_TRAFFIC_CLASS))
#define I40E_GLOBAL_STATS_LEN ARRAY_SIZE(i40e_gstrings_stats)
#define I40E_PF_STATS_LEN (I40E_GLOBAL_STATS_LEN + \
I40E_PFC_STATS_LEN + \
I40E_VEB_STATS_LEN + \
I40E_VSI_STATS_LEN)
/* Length of stats for a single queue */
#define I40E_QUEUE_STATS_LEN ARRAY_SIZE(i40e_gstrings_queue_stats)
enum i40e_ethtool_test_id {
I40E_ETH_TEST_REG = 0,
I40E_ETH_TEST_EEPROM,
I40E_ETH_TEST_INTR,
I40E_ETH_TEST_LINK,
};
static const char i40e_gstrings_test[][ETH_GSTRING_LEN] = {
"Register test (offline)",
"Eeprom test (offline)",
"Interrupt test (offline)",
"Link test (on/offline)"
};
#define I40E_TEST_LEN (sizeof(i40e_gstrings_test) / ETH_GSTRING_LEN)
struct i40e_priv_flags {
char flag_string[ETH_GSTRING_LEN];
u64 flag;
bool read_only;
};
#define I40E_PRIV_FLAG(_name, _flag, _read_only) { \
.flag_string = _name, \
.flag = _flag, \
.read_only = _read_only, \
}
static const struct i40e_priv_flags i40e_gstrings_priv_flags[] = {
/* NOTE: MFP setting cannot be changed */
I40E_PRIV_FLAG("MFP", I40E_FLAG_MFP_ENABLED, 1),
I40E_PRIV_FLAG("total-port-shutdown",
I40E_FLAG_TOTAL_PORT_SHUTDOWN_ENABLED, 1),
I40E_PRIV_FLAG("LinkPolling", I40E_FLAG_LINK_POLLING_ENABLED, 0),
I40E_PRIV_FLAG("flow-director-atr", I40E_FLAG_FD_ATR_ENABLED, 0),
I40E_PRIV_FLAG("veb-stats", I40E_FLAG_VEB_STATS_ENABLED, 0),
I40E_PRIV_FLAG("hw-atr-eviction", I40E_FLAG_HW_ATR_EVICT_ENABLED, 0),
I40E_PRIV_FLAG("link-down-on-close",
I40E_FLAG_LINK_DOWN_ON_CLOSE_ENABLED, 0),
I40E_PRIV_FLAG("legacy-rx", I40E_FLAG_LEGACY_RX, 0),
I40E_PRIV_FLAG("disable-source-pruning",
I40E_FLAG_SOURCE_PRUNING_DISABLED, 0),
I40E_PRIV_FLAG("disable-fw-lldp", I40E_FLAG_DISABLE_FW_LLDP, 0),
I40E_PRIV_FLAG("rs-fec", I40E_FLAG_RS_FEC, 0),
I40E_PRIV_FLAG("base-r-fec", I40E_FLAG_BASE_R_FEC, 0),
};
#define I40E_PRIV_FLAGS_STR_LEN ARRAY_SIZE(i40e_gstrings_priv_flags)
/* Private flags with a global effect, restricted to PF 0 */
static const struct i40e_priv_flags i40e_gl_gstrings_priv_flags[] = {
I40E_PRIV_FLAG("vf-true-promisc-support",
I40E_FLAG_TRUE_PROMISC_SUPPORT, 0),
};
#define I40E_GL_PRIV_FLAGS_STR_LEN ARRAY_SIZE(i40e_gl_gstrings_priv_flags)
/**
* i40e_partition_setting_complaint - generic complaint for MFP restriction
* @pf: the PF struct
**/
static void i40e_partition_setting_complaint(struct i40e_pf *pf)
{
dev_info(&pf->pdev->dev,
"The link settings are allowed to be changed only from the first partition of a given port. Please switch to the first partition in order to change the setting.\n");
}
/**
* i40e_phy_type_to_ethtool - convert the phy_types to ethtool link modes
* @pf: PF struct with phy_types
* @ks: ethtool link ksettings struct to fill out
*
**/
static void i40e_phy_type_to_ethtool(struct i40e_pf *pf,
struct ethtool_link_ksettings *ks)
{
struct i40e_link_status *hw_link_info = &pf->hw.phy.link_info;
u64 phy_types = pf->hw.phy.phy_types;
ethtool_link_ksettings_zero_link_mode(ks, supported);
ethtool_link_ksettings_zero_link_mode(ks, advertising);
if (phy_types & I40E_CAP_PHY_TYPE_SGMII) {
ethtool_link_ksettings_add_link_mode(ks, supported,
1000baseT_Full);
if (hw_link_info->requested_speeds & I40E_LINK_SPEED_1GB)
ethtool_link_ksettings_add_link_mode(ks, advertising,
1000baseT_Full);
if (pf->hw_features & I40E_HW_100M_SGMII_CAPABLE) {
ethtool_link_ksettings_add_link_mode(ks, supported,
100baseT_Full);
ethtool_link_ksettings_add_link_mode(ks, advertising,
100baseT_Full);
}
}
if (phy_types & I40E_CAP_PHY_TYPE_XAUI ||
phy_types & I40E_CAP_PHY_TYPE_XFI ||
phy_types & I40E_CAP_PHY_TYPE_SFI ||
phy_types & I40E_CAP_PHY_TYPE_10GBASE_SFPP_CU ||
phy_types & I40E_CAP_PHY_TYPE_10GBASE_AOC) {
ethtool_link_ksettings_add_link_mode(ks, supported,
10000baseT_Full);
if (hw_link_info->requested_speeds & I40E_LINK_SPEED_10GB)
ethtool_link_ksettings_add_link_mode(ks, advertising,
10000baseT_Full);
}
if (phy_types & I40E_CAP_PHY_TYPE_10GBASE_T) {
ethtool_link_ksettings_add_link_mode(ks, supported,
10000baseT_Full);
if (hw_link_info->requested_speeds & I40E_LINK_SPEED_10GB)
ethtool_link_ksettings_add_link_mode(ks, advertising,
10000baseT_Full);
}
if (phy_types & I40E_CAP_PHY_TYPE_2_5GBASE_T) {
ethtool_link_ksettings_add_link_mode(ks, supported,
2500baseT_Full);
if (hw_link_info->requested_speeds & I40E_LINK_SPEED_2_5GB)
ethtool_link_ksettings_add_link_mode(ks, advertising,
2500baseT_Full);
}
if (phy_types & I40E_CAP_PHY_TYPE_5GBASE_T) {
ethtool_link_ksettings_add_link_mode(ks, supported,
5000baseT_Full);
if (hw_link_info->requested_speeds & I40E_LINK_SPEED_5GB)
ethtool_link_ksettings_add_link_mode(ks, advertising,
5000baseT_Full);
}
if (phy_types & I40E_CAP_PHY_TYPE_XLAUI ||
phy_types & I40E_CAP_PHY_TYPE_XLPPI ||
phy_types & I40E_CAP_PHY_TYPE_40GBASE_AOC)
ethtool_link_ksettings_add_link_mode(ks, supported,
40000baseCR4_Full);
if (phy_types & I40E_CAP_PHY_TYPE_40GBASE_CR4_CU ||
phy_types & I40E_CAP_PHY_TYPE_40GBASE_CR4) {
ethtool_link_ksettings_add_link_mode(ks, supported,
40000baseCR4_Full);
if (hw_link_info->requested_speeds & I40E_LINK_SPEED_40GB)
ethtool_link_ksettings_add_link_mode(ks, advertising,
40000baseCR4_Full);
}
if (phy_types & I40E_CAP_PHY_TYPE_100BASE_TX) {
ethtool_link_ksettings_add_link_mode(ks, supported,
100baseT_Full);
if (hw_link_info->requested_speeds & I40E_LINK_SPEED_100MB)
ethtool_link_ksettings_add_link_mode(ks, advertising,
100baseT_Full);
}
if (phy_types & I40E_CAP_PHY_TYPE_1000BASE_T) {
ethtool_link_ksettings_add_link_mode(ks, supported,
1000baseT_Full);
if (hw_link_info->requested_speeds & I40E_LINK_SPEED_1GB)
ethtool_link_ksettings_add_link_mode(ks, advertising,
1000baseT_Full);
}
if (phy_types & I40E_CAP_PHY_TYPE_40GBASE_SR4) {
ethtool_link_ksettings_add_link_mode(ks, supported,
40000baseSR4_Full);
ethtool_link_ksettings_add_link_mode(ks, advertising,
40000baseSR4_Full);
}
if (phy_types & I40E_CAP_PHY_TYPE_40GBASE_LR4) {
ethtool_link_ksettings_add_link_mode(ks, supported,
40000baseLR4_Full);
ethtool_link_ksettings_add_link_mode(ks, advertising,
40000baseLR4_Full);
}
if (phy_types & I40E_CAP_PHY_TYPE_40GBASE_KR4) {
ethtool_link_ksettings_add_link_mode(ks, supported,
40000baseKR4_Full);
ethtool_link_ksettings_add_link_mode(ks, advertising,
40000baseKR4_Full);
}
if (phy_types & I40E_CAP_PHY_TYPE_20GBASE_KR2) {
ethtool_link_ksettings_add_link_mode(ks, supported,
20000baseKR2_Full);
if (hw_link_info->requested_speeds & I40E_LINK_SPEED_20GB)
ethtool_link_ksettings_add_link_mode(ks, advertising,
20000baseKR2_Full);
}
if (phy_types & I40E_CAP_PHY_TYPE_10GBASE_KX4) {
ethtool_link_ksettings_add_link_mode(ks, supported,
10000baseKX4_Full);
if (hw_link_info->requested_speeds & I40E_LINK_SPEED_10GB)
ethtool_link_ksettings_add_link_mode(ks, advertising,
10000baseKX4_Full);
}
if (phy_types & I40E_CAP_PHY_TYPE_10GBASE_KR &&
!(pf->hw_features & I40E_HW_HAVE_CRT_RETIMER)) {
ethtool_link_ksettings_add_link_mode(ks, supported,
10000baseKR_Full);
if (hw_link_info->requested_speeds & I40E_LINK_SPEED_10GB)
ethtool_link_ksettings_add_link_mode(ks, advertising,
10000baseKR_Full);
}
if (phy_types & I40E_CAP_PHY_TYPE_1000BASE_KX &&
!(pf->hw_features & I40E_HW_HAVE_CRT_RETIMER)) {
ethtool_link_ksettings_add_link_mode(ks, supported,
1000baseKX_Full);
if (hw_link_info->requested_speeds & I40E_LINK_SPEED_1GB)
ethtool_link_ksettings_add_link_mode(ks, advertising,
1000baseKX_Full);
}
/* need to add 25G PHY types */
if (phy_types & I40E_CAP_PHY_TYPE_25GBASE_KR) {
ethtool_link_ksettings_add_link_mode(ks, supported,
25000baseKR_Full);
if (hw_link_info->requested_speeds & I40E_LINK_SPEED_25GB)
ethtool_link_ksettings_add_link_mode(ks, advertising,
25000baseKR_Full);
}
if (phy_types & I40E_CAP_PHY_TYPE_25GBASE_CR) {
ethtool_link_ksettings_add_link_mode(ks, supported,
25000baseCR_Full);
if (hw_link_info->requested_speeds & I40E_LINK_SPEED_25GB)
ethtool_link_ksettings_add_link_mode(ks, advertising,
25000baseCR_Full);
}
if (phy_types & I40E_CAP_PHY_TYPE_25GBASE_SR ||
phy_types & I40E_CAP_PHY_TYPE_25GBASE_LR) {
ethtool_link_ksettings_add_link_mode(ks, supported,
25000baseSR_Full);
if (hw_link_info->requested_speeds & I40E_LINK_SPEED_25GB)
ethtool_link_ksettings_add_link_mode(ks, advertising,
25000baseSR_Full);
}
if (phy_types & I40E_CAP_PHY_TYPE_25GBASE_AOC ||
phy_types & I40E_CAP_PHY_TYPE_25GBASE_ACC) {
ethtool_link_ksettings_add_link_mode(ks, supported,
25000baseCR_Full);
if (hw_link_info->requested_speeds & I40E_LINK_SPEED_25GB)
ethtool_link_ksettings_add_link_mode(ks, advertising,
25000baseCR_Full);
}
if (phy_types & I40E_CAP_PHY_TYPE_25GBASE_KR ||
phy_types & I40E_CAP_PHY_TYPE_25GBASE_CR ||
phy_types & I40E_CAP_PHY_TYPE_25GBASE_SR ||
phy_types & I40E_CAP_PHY_TYPE_25GBASE_LR ||
phy_types & I40E_CAP_PHY_TYPE_25GBASE_AOC ||
phy_types & I40E_CAP_PHY_TYPE_25GBASE_ACC) {
ethtool_link_ksettings_add_link_mode(ks, supported, FEC_NONE);
ethtool_link_ksettings_add_link_mode(ks, supported, FEC_RS);
ethtool_link_ksettings_add_link_mode(ks, supported, FEC_BASER);
if (hw_link_info->requested_speeds & I40E_LINK_SPEED_25GB) {
ethtool_link_ksettings_add_link_mode(ks, advertising,
FEC_NONE);
ethtool_link_ksettings_add_link_mode(ks, advertising,
FEC_RS);
ethtool_link_ksettings_add_link_mode(ks, advertising,
FEC_BASER);
}
}
/* need to add new 10G PHY types */
if (phy_types & I40E_CAP_PHY_TYPE_10GBASE_CR1 ||
phy_types & I40E_CAP_PHY_TYPE_10GBASE_CR1_CU) {
ethtool_link_ksettings_add_link_mode(ks, supported,
10000baseCR_Full);
if (hw_link_info->requested_speeds & I40E_LINK_SPEED_10GB)
ethtool_link_ksettings_add_link_mode(ks, advertising,
10000baseCR_Full);
}
if (phy_types & I40E_CAP_PHY_TYPE_10GBASE_SR) {
ethtool_link_ksettings_add_link_mode(ks, supported,
10000baseSR_Full);
if (hw_link_info->requested_speeds & I40E_LINK_SPEED_10GB)
ethtool_link_ksettings_add_link_mode(ks, advertising,
10000baseSR_Full);
}
if (phy_types & I40E_CAP_PHY_TYPE_10GBASE_LR) {
ethtool_link_ksettings_add_link_mode(ks, supported,
10000baseLR_Full);
if (hw_link_info->requested_speeds & I40E_LINK_SPEED_10GB)
ethtool_link_ksettings_add_link_mode(ks, advertising,
10000baseLR_Full);
}
if (phy_types & I40E_CAP_PHY_TYPE_1000BASE_SX ||
phy_types & I40E_CAP_PHY_TYPE_1000BASE_LX ||
phy_types & I40E_CAP_PHY_TYPE_1000BASE_T_OPTICAL) {
ethtool_link_ksettings_add_link_mode(ks, supported,
1000baseX_Full);
if (hw_link_info->requested_speeds & I40E_LINK_SPEED_1GB)
ethtool_link_ksettings_add_link_mode(ks, advertising,
1000baseX_Full);
}
/* Autoneg PHY types */
if (phy_types & I40E_CAP_PHY_TYPE_SGMII ||
phy_types & I40E_CAP_PHY_TYPE_40GBASE_KR4 ||
phy_types & I40E_CAP_PHY_TYPE_40GBASE_CR4_CU ||
phy_types & I40E_CAP_PHY_TYPE_40GBASE_CR4 ||
phy_types & I40E_CAP_PHY_TYPE_25GBASE_SR ||
phy_types & I40E_CAP_PHY_TYPE_25GBASE_LR ||
phy_types & I40E_CAP_PHY_TYPE_25GBASE_KR ||
phy_types & I40E_CAP_PHY_TYPE_25GBASE_CR ||
phy_types & I40E_CAP_PHY_TYPE_20GBASE_KR2 ||
phy_types & I40E_CAP_PHY_TYPE_10GBASE_SR ||
phy_types & I40E_CAP_PHY_TYPE_10GBASE_LR ||
phy_types & I40E_CAP_PHY_TYPE_10GBASE_KX4 ||
phy_types & I40E_CAP_PHY_TYPE_10GBASE_KR ||
phy_types & I40E_CAP_PHY_TYPE_10GBASE_CR1_CU ||
phy_types & I40E_CAP_PHY_TYPE_10GBASE_CR1 ||
phy_types & I40E_CAP_PHY_TYPE_10GBASE_T ||
phy_types & I40E_CAP_PHY_TYPE_5GBASE_T ||
phy_types & I40E_CAP_PHY_TYPE_2_5GBASE_T ||
phy_types & I40E_CAP_PHY_TYPE_1000BASE_T_OPTICAL ||
phy_types & I40E_CAP_PHY_TYPE_1000BASE_T ||
phy_types & I40E_CAP_PHY_TYPE_1000BASE_SX ||
phy_types & I40E_CAP_PHY_TYPE_1000BASE_LX ||
phy_types & I40E_CAP_PHY_TYPE_1000BASE_KX ||
phy_types & I40E_CAP_PHY_TYPE_100BASE_TX) {
ethtool_link_ksettings_add_link_mode(ks, supported,
Autoneg);
ethtool_link_ksettings_add_link_mode(ks, advertising,
Autoneg);
}
}
/**
* i40e_get_settings_link_up_fec - Get the FEC mode encoding from mask
* @req_fec_info: mask request FEC info
* @ks: ethtool ksettings to fill in
**/
static void i40e_get_settings_link_up_fec(u8 req_fec_info,
struct ethtool_link_ksettings *ks)
{
ethtool_link_ksettings_add_link_mode(ks, supported, FEC_NONE);
ethtool_link_ksettings_add_link_mode(ks, supported, FEC_RS);
ethtool_link_ksettings_add_link_mode(ks, supported, FEC_BASER);
if ((I40E_AQ_SET_FEC_REQUEST_RS & req_fec_info) &&
(I40E_AQ_SET_FEC_REQUEST_KR & req_fec_info)) {
ethtool_link_ksettings_add_link_mode(ks, advertising,
FEC_NONE);
ethtool_link_ksettings_add_link_mode(ks, advertising,
FEC_BASER);
ethtool_link_ksettings_add_link_mode(ks, advertising, FEC_RS);
} else if (I40E_AQ_SET_FEC_REQUEST_RS & req_fec_info) {
ethtool_link_ksettings_add_link_mode(ks, advertising, FEC_RS);
} else if (I40E_AQ_SET_FEC_REQUEST_KR & req_fec_info) {
ethtool_link_ksettings_add_link_mode(ks, advertising,
FEC_BASER);
} else {
ethtool_link_ksettings_add_link_mode(ks, advertising,
FEC_NONE);
}
}
/**
* i40e_get_settings_link_up - Get the Link settings for when link is up
* @hw: hw structure
* @ks: ethtool ksettings to fill in
* @netdev: network interface device structure
* @pf: pointer to physical function struct
**/
static void i40e_get_settings_link_up(struct i40e_hw *hw,
struct ethtool_link_ksettings *ks,
struct net_device *netdev,
struct i40e_pf *pf)
{
struct i40e_link_status *hw_link_info = &hw->phy.link_info;
struct ethtool_link_ksettings cap_ksettings;
u32 link_speed = hw_link_info->link_speed;
/* Initialize supported and advertised settings based on phy settings */
switch (hw_link_info->phy_type) {
case I40E_PHY_TYPE_40GBASE_CR4:
case I40E_PHY_TYPE_40GBASE_CR4_CU:
ethtool_link_ksettings_add_link_mode(ks, supported, Autoneg);
ethtool_link_ksettings_add_link_mode(ks, supported,
40000baseCR4_Full);
ethtool_link_ksettings_add_link_mode(ks, advertising, Autoneg);
ethtool_link_ksettings_add_link_mode(ks, advertising,
40000baseCR4_Full);
break;
case I40E_PHY_TYPE_XLAUI:
case I40E_PHY_TYPE_XLPPI:
case I40E_PHY_TYPE_40GBASE_AOC:
ethtool_link_ksettings_add_link_mode(ks, supported,
40000baseCR4_Full);
ethtool_link_ksettings_add_link_mode(ks, advertising,
40000baseCR4_Full);
break;
case I40E_PHY_TYPE_40GBASE_SR4:
ethtool_link_ksettings_add_link_mode(ks, supported,
40000baseSR4_Full);
ethtool_link_ksettings_add_link_mode(ks, advertising,
40000baseSR4_Full);
break;
case I40E_PHY_TYPE_40GBASE_LR4:
ethtool_link_ksettings_add_link_mode(ks, supported,
40000baseLR4_Full);
ethtool_link_ksettings_add_link_mode(ks, advertising,
40000baseLR4_Full);
break;
case I40E_PHY_TYPE_25GBASE_SR:
case I40E_PHY_TYPE_25GBASE_LR:
case I40E_PHY_TYPE_10GBASE_SR:
case I40E_PHY_TYPE_10GBASE_LR:
case I40E_PHY_TYPE_1000BASE_SX:
case I40E_PHY_TYPE_1000BASE_LX:
ethtool_link_ksettings_add_link_mode(ks, supported, Autoneg);
ethtool_link_ksettings_add_link_mode(ks, advertising, Autoneg);
ethtool_link_ksettings_add_link_mode(ks, supported,
25000baseSR_Full);
ethtool_link_ksettings_add_link_mode(ks, advertising,
25000baseSR_Full);
i40e_get_settings_link_up_fec(hw_link_info->req_fec_info, ks);
ethtool_link_ksettings_add_link_mode(ks, supported,
10000baseSR_Full);
ethtool_link_ksettings_add_link_mode(ks, advertising,
10000baseSR_Full);
ethtool_link_ksettings_add_link_mode(ks, supported,
10000baseLR_Full);
ethtool_link_ksettings_add_link_mode(ks, advertising,
10000baseLR_Full);
ethtool_link_ksettings_add_link_mode(ks, supported,
1000baseX_Full);
ethtool_link_ksettings_add_link_mode(ks, advertising,
1000baseX_Full);
ethtool_link_ksettings_add_link_mode(ks, supported,
10000baseT_Full);
if (hw_link_info->module_type[2] &
I40E_MODULE_TYPE_1000BASE_SX ||
hw_link_info->module_type[2] &
I40E_MODULE_TYPE_1000BASE_LX) {
ethtool_link_ksettings_add_link_mode(ks, supported,
1000baseT_Full);
if (hw_link_info->requested_speeds &
I40E_LINK_SPEED_1GB)
ethtool_link_ksettings_add_link_mode(
ks, advertising, 1000baseT_Full);
}
if (hw_link_info->requested_speeds & I40E_LINK_SPEED_10GB)
ethtool_link_ksettings_add_link_mode(ks, advertising,
10000baseT_Full);
break;
case I40E_PHY_TYPE_10GBASE_T:
case I40E_PHY_TYPE_5GBASE_T_LINK_STATUS:
case I40E_PHY_TYPE_2_5GBASE_T_LINK_STATUS:
case I40E_PHY_TYPE_1000BASE_T:
case I40E_PHY_TYPE_100BASE_TX:
ethtool_link_ksettings_add_link_mode(ks, supported, Autoneg);
ethtool_link_ksettings_add_link_mode(ks, supported,
10000baseT_Full);
ethtool_link_ksettings_add_link_mode(ks, supported,
5000baseT_Full);
ethtool_link_ksettings_add_link_mode(ks, supported,
2500baseT_Full);
ethtool_link_ksettings_add_link_mode(ks, supported,
1000baseT_Full);
ethtool_link_ksettings_add_link_mode(ks, supported,
100baseT_Full);
ethtool_link_ksettings_add_link_mode(ks, advertising, Autoneg);
if (hw_link_info->requested_speeds & I40E_LINK_SPEED_10GB)
ethtool_link_ksettings_add_link_mode(ks, advertising,
10000baseT_Full);
if (hw_link_info->requested_speeds & I40E_LINK_SPEED_5GB)
ethtool_link_ksettings_add_link_mode(ks, advertising,
5000baseT_Full);
if (hw_link_info->requested_speeds & I40E_LINK_SPEED_2_5GB)
ethtool_link_ksettings_add_link_mode(ks, advertising,
2500baseT_Full);
if (hw_link_info->requested_speeds & I40E_LINK_SPEED_1GB)
ethtool_link_ksettings_add_link_mode(ks, advertising,
1000baseT_Full);
if (hw_link_info->requested_speeds & I40E_LINK_SPEED_100MB)
ethtool_link_ksettings_add_link_mode(ks, advertising,
100baseT_Full);
break;
case I40E_PHY_TYPE_1000BASE_T_OPTICAL:
ethtool_link_ksettings_add_link_mode(ks, supported, Autoneg);
ethtool_link_ksettings_add_link_mode(ks, supported,
1000baseT_Full);
ethtool_link_ksettings_add_link_mode(ks, advertising, Autoneg);
ethtool_link_ksettings_add_link_mode(ks, advertising,
1000baseT_Full);
break;
case I40E_PHY_TYPE_10GBASE_CR1_CU:
case I40E_PHY_TYPE_10GBASE_CR1:
ethtool_link_ksettings_add_link_mode(ks, supported, Autoneg);
ethtool_link_ksettings_add_link_mode(ks, supported,
10000baseT_Full);
ethtool_link_ksettings_add_link_mode(ks, advertising, Autoneg);
ethtool_link_ksettings_add_link_mode(ks, advertising,
10000baseT_Full);
break;
case I40E_PHY_TYPE_XAUI:
case I40E_PHY_TYPE_XFI:
case I40E_PHY_TYPE_SFI:
case I40E_PHY_TYPE_10GBASE_SFPP_CU:
case I40E_PHY_TYPE_10GBASE_AOC:
ethtool_link_ksettings_add_link_mode(ks, supported,
10000baseT_Full);
if (hw_link_info->requested_speeds & I40E_LINK_SPEED_10GB)
ethtool_link_ksettings_add_link_mode(ks, advertising,
10000baseT_Full);
i40e_get_settings_link_up_fec(hw_link_info->req_fec_info, ks);
break;
case I40E_PHY_TYPE_SGMII:
ethtool_link_ksettings_add_link_mode(ks, supported, Autoneg);
ethtool_link_ksettings_add_link_mode(ks, supported,
1000baseT_Full);
if (hw_link_info->requested_speeds & I40E_LINK_SPEED_1GB)
ethtool_link_ksettings_add_link_mode(ks, advertising,
1000baseT_Full);
if (pf->hw_features & I40E_HW_100M_SGMII_CAPABLE) {
ethtool_link_ksettings_add_link_mode(ks, supported,
100baseT_Full);
if (hw_link_info->requested_speeds &
I40E_LINK_SPEED_100MB)
ethtool_link_ksettings_add_link_mode(
ks, advertising, 100baseT_Full);
}
break;
case I40E_PHY_TYPE_40GBASE_KR4:
case I40E_PHY_TYPE_25GBASE_KR:
case I40E_PHY_TYPE_20GBASE_KR2:
case I40E_PHY_TYPE_10GBASE_KR:
case I40E_PHY_TYPE_10GBASE_KX4:
case I40E_PHY_TYPE_1000BASE_KX:
ethtool_link_ksettings_add_link_mode(ks, supported,
40000baseKR4_Full);
ethtool_link_ksettings_add_link_mode(ks, supported,
25000baseKR_Full);
ethtool_link_ksettings_add_link_mode(ks, supported,
20000baseKR2_Full);
ethtool_link_ksettings_add_link_mode(ks, supported,
10000baseKR_Full);
ethtool_link_ksettings_add_link_mode(ks, supported,
10000baseKX4_Full);
ethtool_link_ksettings_add_link_mode(ks, supported,
1000baseKX_Full);
ethtool_link_ksettings_add_link_mode(ks, supported, Autoneg);
ethtool_link_ksettings_add_link_mode(ks, advertising,
40000baseKR4_Full);
ethtool_link_ksettings_add_link_mode(ks, advertising,
25000baseKR_Full);
i40e_get_settings_link_up_fec(hw_link_info->req_fec_info, ks);
ethtool_link_ksettings_add_link_mode(ks, advertising,
20000baseKR2_Full);
ethtool_link_ksettings_add_link_mode(ks, advertising,
10000baseKR_Full);
ethtool_link_ksettings_add_link_mode(ks, advertising,
10000baseKX4_Full);
ethtool_link_ksettings_add_link_mode(ks, advertising,
1000baseKX_Full);
ethtool_link_ksettings_add_link_mode(ks, advertising, Autoneg);
break;
case I40E_PHY_TYPE_25GBASE_CR:
ethtool_link_ksettings_add_link_mode(ks, supported, Autoneg);
ethtool_link_ksettings_add_link_mode(ks, advertising, Autoneg);
ethtool_link_ksettings_add_link_mode(ks, supported,
25000baseCR_Full);
ethtool_link_ksettings_add_link_mode(ks, advertising,
25000baseCR_Full);
i40e_get_settings_link_up_fec(hw_link_info->req_fec_info, ks);
break;
case I40E_PHY_TYPE_25GBASE_AOC:
case I40E_PHY_TYPE_25GBASE_ACC:
ethtool_link_ksettings_add_link_mode(ks, supported, Autoneg);
ethtool_link_ksettings_add_link_mode(ks, advertising, Autoneg);
ethtool_link_ksettings_add_link_mode(ks, supported,
25000baseCR_Full);
ethtool_link_ksettings_add_link_mode(ks, advertising,
25000baseCR_Full);
i40e_get_settings_link_up_fec(hw_link_info->req_fec_info, ks);
ethtool_link_ksettings_add_link_mode(ks, supported,
10000baseCR_Full);
ethtool_link_ksettings_add_link_mode(ks, advertising,
10000baseCR_Full);
break;
default:
/* if we got here and link is up something bad is afoot */
netdev_info(netdev,
"WARNING: Link is up but PHY type 0x%x is not recognized, or incorrect cable is in use\n",
hw_link_info->phy_type);
}
/* Now that we've worked out everything that could be supported by the
* current PHY type, get what is supported by the NVM and intersect
* them to get what is truly supported
*/
memset(&cap_ksettings, 0, sizeof(struct ethtool_link_ksettings));
i40e_phy_type_to_ethtool(pf, &cap_ksettings);
ethtool_intersect_link_masks(ks, &cap_ksettings);
/* Set speed and duplex */
switch (link_speed) {
case I40E_LINK_SPEED_40GB:
ks->base.speed = SPEED_40000;
break;
case I40E_LINK_SPEED_25GB:
ks->base.speed = SPEED_25000;
break;
case I40E_LINK_SPEED_20GB:
ks->base.speed = SPEED_20000;
break;
case I40E_LINK_SPEED_10GB:
ks->base.speed = SPEED_10000;
break;
case I40E_LINK_SPEED_5GB:
ks->base.speed = SPEED_5000;
break;
case I40E_LINK_SPEED_2_5GB:
ks->base.speed = SPEED_2500;
break;
case I40E_LINK_SPEED_1GB:
ks->base.speed = SPEED_1000;
break;
case I40E_LINK_SPEED_100MB:
ks->base.speed = SPEED_100;
break;
default:
ks->base.speed = SPEED_UNKNOWN;
break;
}
ks->base.duplex = DUPLEX_FULL;
}
/**
* i40e_get_settings_link_down - Get the Link settings for when link is down
* @hw: hw structure
* @ks: ethtool ksettings to fill in
* @pf: pointer to physical function struct
*
* Reports link settings that can be determined when link is down
**/
static void i40e_get_settings_link_down(struct i40e_hw *hw,
struct ethtool_link_ksettings *ks,
struct i40e_pf *pf)
{
/* link is down and the driver needs to fall back on
* supported phy types to figure out what info to display
*/
i40e_phy_type_to_ethtool(pf, ks);
/* With no link speed and duplex are unknown */
ks->base.speed = SPEED_UNKNOWN;
ks->base.duplex = DUPLEX_UNKNOWN;
}
/**
* i40e_get_link_ksettings - Get Link Speed and Duplex settings
* @netdev: network interface device structure
* @ks: ethtool ksettings
*
* Reports speed/duplex settings based on media_type
**/
static int i40e_get_link_ksettings(struct net_device *netdev,
struct ethtool_link_ksettings *ks)
{
struct i40e_netdev_priv *np = netdev_priv(netdev);
struct i40e_pf *pf = np->vsi->back;
struct i40e_hw *hw = &pf->hw;
struct i40e_link_status *hw_link_info = &hw->phy.link_info;
bool link_up = hw_link_info->link_info & I40E_AQ_LINK_UP;
ethtool_link_ksettings_zero_link_mode(ks, supported);
ethtool_link_ksettings_zero_link_mode(ks, advertising);
if (link_up)
i40e_get_settings_link_up(hw, ks, netdev, pf);
else
i40e_get_settings_link_down(hw, ks, pf);
/* Now set the settings that don't rely on link being up/down */
/* Set autoneg settings */
ks->base.autoneg = ((hw_link_info->an_info & I40E_AQ_AN_COMPLETED) ?
AUTONEG_ENABLE : AUTONEG_DISABLE);
/* Set media type settings */
switch (hw->phy.media_type) {
case I40E_MEDIA_TYPE_BACKPLANE:
ethtool_link_ksettings_add_link_mode(ks, supported, Autoneg);
ethtool_link_ksettings_add_link_mode(ks, supported, Backplane);
ethtool_link_ksettings_add_link_mode(ks, advertising, Autoneg);
ethtool_link_ksettings_add_link_mode(ks, advertising,
Backplane);
ks->base.port = PORT_NONE;
break;
case I40E_MEDIA_TYPE_BASET:
ethtool_link_ksettings_add_link_mode(ks, supported, TP);
ethtool_link_ksettings_add_link_mode(ks, advertising, TP);
ks->base.port = PORT_TP;
break;
case I40E_MEDIA_TYPE_DA:
case I40E_MEDIA_TYPE_CX4:
ethtool_link_ksettings_add_link_mode(ks, supported, FIBRE);
ethtool_link_ksettings_add_link_mode(ks, advertising, FIBRE);
ks->base.port = PORT_DA;
break;
case I40E_MEDIA_TYPE_FIBER:
ethtool_link_ksettings_add_link_mode(ks, supported, FIBRE);
ethtool_link_ksettings_add_link_mode(ks, advertising, FIBRE);
ks->base.port = PORT_FIBRE;
break;
case I40E_MEDIA_TYPE_UNKNOWN:
default:
ks->base.port = PORT_OTHER;
break;
}
/* Set flow control settings */
ethtool_link_ksettings_add_link_mode(ks, supported, Pause);
ethtool_link_ksettings_add_link_mode(ks, supported, Asym_Pause);
switch (hw->fc.requested_mode) {
case I40E_FC_FULL:
ethtool_link_ksettings_add_link_mode(ks, advertising, Pause);
break;
case I40E_FC_TX_PAUSE:
ethtool_link_ksettings_add_link_mode(ks, advertising,
Asym_Pause);
break;
case I40E_FC_RX_PAUSE:
ethtool_link_ksettings_add_link_mode(ks, advertising, Pause);
ethtool_link_ksettings_add_link_mode(ks, advertising,
Asym_Pause);
break;
default:
ethtool_link_ksettings_del_link_mode(ks, advertising, Pause);
ethtool_link_ksettings_del_link_mode(ks, advertising,
Asym_Pause);
break;
}
return 0;
}
/**
* i40e_set_link_ksettings - Set Speed and Duplex
* @netdev: network interface device structure
* @ks: ethtool ksettings
*
* Set speed/duplex per media_types advertised/forced
**/
static int i40e_set_link_ksettings(struct net_device *netdev,
const struct ethtool_link_ksettings *ks)
{
struct i40e_netdev_priv *np = netdev_priv(netdev);
struct i40e_aq_get_phy_abilities_resp abilities;
struct ethtool_link_ksettings safe_ks;
struct ethtool_link_ksettings copy_ks;
struct i40e_aq_set_phy_config config;
struct i40e_pf *pf = np->vsi->back;
struct i40e_vsi *vsi = np->vsi;
struct i40e_hw *hw = &pf->hw;
bool autoneg_changed = false;
i40e_status status = 0;
int timeout = 50;
int err = 0;
u8 autoneg;
/* Changing port settings is not supported if this isn't the
* port's controlling PF
*/
if (hw->partition_id != 1) {
i40e_partition_setting_complaint(pf);
return -EOPNOTSUPP;
}
if (vsi != pf->vsi[pf->lan_vsi])
return -EOPNOTSUPP;
if (hw->phy.media_type != I40E_MEDIA_TYPE_BASET &&
hw->phy.media_type != I40E_MEDIA_TYPE_FIBER &&
hw->phy.media_type != I40E_MEDIA_TYPE_BACKPLANE &&
hw->phy.media_type != I40E_MEDIA_TYPE_DA &&
hw->phy.link_info.link_info & I40E_AQ_LINK_UP)
return -EOPNOTSUPP;
if (hw->device_id == I40E_DEV_ID_KX_B ||
hw->device_id == I40E_DEV_ID_KX_C ||
hw->device_id == I40E_DEV_ID_20G_KR2 ||
hw->device_id == I40E_DEV_ID_20G_KR2_A ||
hw->device_id == I40E_DEV_ID_25G_B ||
hw->device_id == I40E_DEV_ID_KX_X722) {
netdev_info(netdev, "Changing settings is not supported on backplane.\n");
return -EOPNOTSUPP;
}
/* copy the ksettings to copy_ks to avoid modifying the origin */
memcpy(&copy_ks, ks, sizeof(struct ethtool_link_ksettings));
/* save autoneg out of ksettings */
autoneg = copy_ks.base.autoneg;
/* get our own copy of the bits to check against */
memset(&safe_ks, 0, sizeof(struct ethtool_link_ksettings));
safe_ks.base.cmd = copy_ks.base.cmd;
safe_ks.base.link_mode_masks_nwords =
copy_ks.base.link_mode_masks_nwords;
i40e_get_link_ksettings(netdev, &safe_ks);
/* Get link modes supported by hardware and check against modes
* requested by the user. Return an error if unsupported mode was set.
*/
if (!bitmap_subset(copy_ks.link_modes.advertising,
safe_ks.link_modes.supported,
__ETHTOOL_LINK_MODE_MASK_NBITS))
return -EINVAL;
/* set autoneg back to what it currently is */
copy_ks.base.autoneg = safe_ks.base.autoneg;
/* If copy_ks.base and safe_ks.base are not the same now, then they are
* trying to set something that we do not support.
*/
if (memcmp(&copy_ks.base, &safe_ks.base,
sizeof(struct ethtool_link_settings)))
return -EOPNOTSUPP;
while (test_and_set_bit(__I40E_CONFIG_BUSY, pf->state)) {
timeout--;
if (!timeout)
return -EBUSY;
usleep_range(1000, 2000);
}
/* Get the current phy config */
status = i40e_aq_get_phy_capabilities(hw, false, false, &abilities,
NULL);
if (status) {
err = -EAGAIN;
goto done;
}
/* Copy abilities to config in case autoneg is not
* set below
*/
memset(&config, 0, sizeof(struct i40e_aq_set_phy_config));
config.abilities = abilities.abilities;
/* Check autoneg */
if (autoneg == AUTONEG_ENABLE) {
/* If autoneg was not already enabled */
if (!(hw->phy.link_info.an_info & I40E_AQ_AN_COMPLETED)) {
/* If autoneg is not supported, return error */
if (!ethtool_link_ksettings_test_link_mode(&safe_ks,
supported,
Autoneg)) {
netdev_info(netdev, "Autoneg not supported on this phy\n");
err = -EINVAL;
goto done;
}
/* Autoneg is allowed to change */
config.abilities = abilities.abilities |
I40E_AQ_PHY_ENABLE_AN;
autoneg_changed = true;
}
} else {
/* If autoneg is currently enabled */
if (hw->phy.link_info.an_info & I40E_AQ_AN_COMPLETED) {
/* If autoneg is supported 10GBASE_T is the only PHY
* that can disable it, so otherwise return error
*/
if (ethtool_link_ksettings_test_link_mode(&safe_ks,
supported,
Autoneg) &&
hw->phy.media_type != I40E_MEDIA_TYPE_BASET) {
netdev_info(netdev, "Autoneg cannot be disabled on this phy\n");
err = -EINVAL;
goto done;
}
/* Autoneg is allowed to change */
config.abilities = abilities.abilities &
~I40E_AQ_PHY_ENABLE_AN;
autoneg_changed = true;
}
}
if (ethtool_link_ksettings_test_link_mode(ks, advertising,
100baseT_Full))
config.link_speed |= I40E_LINK_SPEED_100MB;
if (ethtool_link_ksettings_test_link_mode(ks, advertising,
1000baseT_Full) ||
ethtool_link_ksettings_test_link_mode(ks, advertising,
1000baseX_Full) ||
ethtool_link_ksettings_test_link_mode(ks, advertising,
1000baseKX_Full))
config.link_speed |= I40E_LINK_SPEED_1GB;
if (ethtool_link_ksettings_test_link_mode(ks, advertising,
10000baseT_Full) ||
ethtool_link_ksettings_test_link_mode(ks, advertising,
10000baseKX4_Full) ||
ethtool_link_ksettings_test_link_mode(ks, advertising,
10000baseKR_Full) ||
ethtool_link_ksettings_test_link_mode(ks, advertising,
10000baseCR_Full) ||
ethtool_link_ksettings_test_link_mode(ks, advertising,
10000baseSR_Full) ||
ethtool_link_ksettings_test_link_mode(ks, advertising,
10000baseLR_Full))
config.link_speed |= I40E_LINK_SPEED_10GB;
if (ethtool_link_ksettings_test_link_mode(ks, advertising,
2500baseT_Full))
config.link_speed |= I40E_LINK_SPEED_2_5GB;
if (ethtool_link_ksettings_test_link_mode(ks, advertising,
5000baseT_Full))
config.link_speed |= I40E_LINK_SPEED_5GB;
if (ethtool_link_ksettings_test_link_mode(ks, advertising,
20000baseKR2_Full))
config.link_speed |= I40E_LINK_SPEED_20GB;
if (ethtool_link_ksettings_test_link_mode(ks, advertising,
25000baseCR_Full) ||
ethtool_link_ksettings_test_link_mode(ks, advertising,
25000baseKR_Full) ||
ethtool_link_ksettings_test_link_mode(ks, advertising,
25000baseSR_Full))
config.link_speed |= I40E_LINK_SPEED_25GB;
if (ethtool_link_ksettings_test_link_mode(ks, advertising,
40000baseKR4_Full) ||
ethtool_link_ksettings_test_link_mode(ks, advertising,
40000baseCR4_Full) ||
ethtool_link_ksettings_test_link_mode(ks, advertising,
40000baseSR4_Full) ||
ethtool_link_ksettings_test_link_mode(ks, advertising,
40000baseLR4_Full))
config.link_speed |= I40E_LINK_SPEED_40GB;
/* If speed didn't get set, set it to what it currently is.
* This is needed because if advertise is 0 (as it is when autoneg
* is disabled) then speed won't get set.
*/
if (!config.link_speed)
config.link_speed = abilities.link_speed;
if (autoneg_changed || abilities.link_speed != config.link_speed) {
/* copy over the rest of the abilities */
config.phy_type = abilities.phy_type;
config.phy_type_ext = abilities.phy_type_ext;
config.eee_capability = abilities.eee_capability;
config.eeer = abilities.eeer_val;
config.low_power_ctrl = abilities.d3_lpan;
config.fec_config = abilities.fec_cfg_curr_mod_ext_info &
I40E_AQ_PHY_FEC_CONFIG_MASK;
/* save the requested speeds */
hw->phy.link_info.requested_speeds = config.link_speed;
/* set link and auto negotiation so changes take effect */
config.abilities |= I40E_AQ_PHY_ENABLE_ATOMIC_LINK;
/* If link is up put link down */
if (hw->phy.link_info.link_info & I40E_AQ_LINK_UP) {
/* Tell the OS link is going down, the link will go
* back up when fw says it is ready asynchronously
*/
i40e_print_link_message(vsi, false);
netif_carrier_off(netdev);
netif_tx_stop_all_queues(netdev);
}
/* make the aq call */
status = i40e_aq_set_phy_config(hw, &config, NULL);
if (status) {
netdev_info(netdev,
"Set phy config failed, err %s aq_err %s\n",
i40e_stat_str(hw, status),
i40e_aq_str(hw, hw->aq.asq_last_status));
err = -EAGAIN;
goto done;
}
status = i40e_update_link_info(hw);
if (status)
netdev_dbg(netdev,
"Updating link info failed with err %s aq_err %s\n",
i40e_stat_str(hw, status),
i40e_aq_str(hw, hw->aq.asq_last_status));
} else {
netdev_info(netdev, "Nothing changed, exiting without setting anything.\n");
}
done:
clear_bit(__I40E_CONFIG_BUSY, pf->state);
return err;
}
static int i40e_set_fec_cfg(struct net_device *netdev, u8 fec_cfg)
{
struct i40e_netdev_priv *np = netdev_priv(netdev);
struct i40e_aq_get_phy_abilities_resp abilities;
struct i40e_pf *pf = np->vsi->back;
struct i40e_hw *hw = &pf->hw;
i40e_status status = 0;
u32 flags = 0;
int err = 0;
flags = READ_ONCE(pf->flags);
i40e_set_fec_in_flags(fec_cfg, &flags);
/* Get the current phy config */
memset(&abilities, 0, sizeof(abilities));
status = i40e_aq_get_phy_capabilities(hw, false, false, &abilities,
NULL);
if (status) {
err = -EAGAIN;
goto done;
}
if (abilities.fec_cfg_curr_mod_ext_info != fec_cfg) {
struct i40e_aq_set_phy_config config;
memset(&config, 0, sizeof(config));
config.phy_type = abilities.phy_type;
config.abilities = abilities.abilities |
I40E_AQ_PHY_ENABLE_ATOMIC_LINK;
config.phy_type_ext = abilities.phy_type_ext;
config.link_speed = abilities.link_speed;
config.eee_capability = abilities.eee_capability;
config.eeer = abilities.eeer_val;
config.low_power_ctrl = abilities.d3_lpan;
config.fec_config = fec_cfg & I40E_AQ_PHY_FEC_CONFIG_MASK;
status = i40e_aq_set_phy_config(hw, &config, NULL);
if (status) {
netdev_info(netdev,
"Set phy config failed, err %s aq_err %s\n",
i40e_stat_str(hw, status),
i40e_aq_str(hw, hw->aq.asq_last_status));
err = -EAGAIN;
goto done;
}
pf->flags = flags;
status = i40e_update_link_info(hw);
if (status)
/* debug level message only due to relation to the link
* itself rather than to the FEC settings
* (e.g. no physical connection etc.)
*/
netdev_dbg(netdev,
"Updating link info failed with err %s aq_err %s\n",
i40e_stat_str(hw, status),
i40e_aq_str(hw, hw->aq.asq_last_status));
}
done:
return err;
}
static int i40e_get_fec_param(struct net_device *netdev,
struct ethtool_fecparam *fecparam)
{
struct i40e_netdev_priv *np = netdev_priv(netdev);
struct i40e_aq_get_phy_abilities_resp abilities;
struct i40e_pf *pf = np->vsi->back;
struct i40e_hw *hw = &pf->hw;
i40e_status status = 0;
int err = 0;
u8 fec_cfg;
/* Get the current phy config */
memset(&abilities, 0, sizeof(abilities));
status = i40e_aq_get_phy_capabilities(hw, false, false, &abilities,
NULL);
if (status) {
err = -EAGAIN;
goto done;
}
fecparam->fec = 0;
fec_cfg = abilities.fec_cfg_curr_mod_ext_info;
if (fec_cfg & I40E_AQ_SET_FEC_AUTO)
fecparam->fec |= ETHTOOL_FEC_AUTO;
else if (fec_cfg & (I40E_AQ_SET_FEC_REQUEST_RS |
I40E_AQ_SET_FEC_ABILITY_RS))
fecparam->fec |= ETHTOOL_FEC_RS;
else if (fec_cfg & (I40E_AQ_SET_FEC_REQUEST_KR |
I40E_AQ_SET_FEC_ABILITY_KR))
fecparam->fec |= ETHTOOL_FEC_BASER;
if (fec_cfg == 0)
fecparam->fec |= ETHTOOL_FEC_OFF;
if (hw->phy.link_info.fec_info & I40E_AQ_CONFIG_FEC_KR_ENA)
fecparam->active_fec = ETHTOOL_FEC_BASER;
else if (hw->phy.link_info.fec_info & I40E_AQ_CONFIG_FEC_RS_ENA)
fecparam->active_fec = ETHTOOL_FEC_RS;
else
fecparam->active_fec = ETHTOOL_FEC_OFF;
done:
return err;
}
static int i40e_set_fec_param(struct net_device *netdev,
struct ethtool_fecparam *fecparam)
{
struct i40e_netdev_priv *np = netdev_priv(netdev);
struct i40e_pf *pf = np->vsi->back;
struct i40e_hw *hw = &pf->hw;
u8 fec_cfg = 0;
if (hw->device_id != I40E_DEV_ID_25G_SFP28 &&
hw->device_id != I40E_DEV_ID_25G_B &&
hw->device_id != I40E_DEV_ID_KX_X722)
return -EPERM;
if (hw->mac.type == I40E_MAC_X722 &&
!(hw->flags & I40E_HW_FLAG_X722_FEC_REQUEST_CAPABLE)) {
netdev_err(netdev, "Setting FEC encoding not supported by firmware. Please update the NVM image.\n");
return -EOPNOTSUPP;
}
switch (fecparam->fec) {
case ETHTOOL_FEC_AUTO:
fec_cfg = I40E_AQ_SET_FEC_AUTO;
break;
case ETHTOOL_FEC_RS:
fec_cfg = (I40E_AQ_SET_FEC_REQUEST_RS |
I40E_AQ_SET_FEC_ABILITY_RS);
break;
case ETHTOOL_FEC_BASER:
fec_cfg = (I40E_AQ_SET_FEC_REQUEST_KR |
I40E_AQ_SET_FEC_ABILITY_KR);
break;
case ETHTOOL_FEC_OFF:
case ETHTOOL_FEC_NONE:
fec_cfg = 0;
break;
default:
dev_warn(&pf->pdev->dev, "Unsupported FEC mode: %d",
fecparam->fec);
return -EINVAL;
}
return i40e_set_fec_cfg(netdev, fec_cfg);
}
static int i40e_nway_reset(struct net_device *netdev)
{
/* restart autonegotiation */
struct i40e_netdev_priv *np = netdev_priv(netdev);
struct i40e_pf *pf = np->vsi->back;
struct i40e_hw *hw = &pf->hw;
bool link_up = hw->phy.link_info.link_info & I40E_AQ_LINK_UP;
i40e_status ret = 0;
ret = i40e_aq_set_link_restart_an(hw, link_up, NULL);
if (ret) {
netdev_info(netdev, "link restart failed, err %s aq_err %s\n",
i40e_stat_str(hw, ret),
i40e_aq_str(hw, hw->aq.asq_last_status));
return -EIO;
}
return 0;
}
/**
* i40e_get_pauseparam - Get Flow Control status
* @netdev: netdevice structure
* @pause: buffer to return pause parameters
*
* Return tx/rx-pause status
**/
static void i40e_get_pauseparam(struct net_device *netdev,
struct ethtool_pauseparam *pause)
{
struct i40e_netdev_priv *np = netdev_priv(netdev);
struct i40e_pf *pf = np->vsi->back;
struct i40e_hw *hw = &pf->hw;
struct i40e_link_status *hw_link_info = &hw->phy.link_info;
struct i40e_dcbx_config *dcbx_cfg = &hw->local_dcbx_config;
pause->autoneg =
((hw_link_info->an_info & I40E_AQ_AN_COMPLETED) ?
AUTONEG_ENABLE : AUTONEG_DISABLE);
/* PFC enabled so report LFC as off */
if (dcbx_cfg->pfc.pfcenable) {
pause->rx_pause = 0;
pause->tx_pause = 0;
return;
}
if (hw->fc.current_mode == I40E_FC_RX_PAUSE) {
pause->rx_pause = 1;
} else if (hw->fc.current_mode == I40E_FC_TX_PAUSE) {
pause->tx_pause = 1;
} else if (hw->fc.current_mode == I40E_FC_FULL) {
pause->rx_pause = 1;
pause->tx_pause = 1;
}
}
/**
* i40e_set_pauseparam - Set Flow Control parameter
* @netdev: network interface device structure
* @pause: return tx/rx flow control status
**/
static int i40e_set_pauseparam(struct net_device *netdev,
struct ethtool_pauseparam *pause)
{
struct i40e_netdev_priv *np = netdev_priv(netdev);
struct i40e_pf *pf = np->vsi->back;
struct i40e_vsi *vsi = np->vsi;
struct i40e_hw *hw = &pf->hw;
struct i40e_link_status *hw_link_info = &hw->phy.link_info;
struct i40e_dcbx_config *dcbx_cfg = &hw->local_dcbx_config;
bool link_up = hw_link_info->link_info & I40E_AQ_LINK_UP;
i40e_status status;
u8 aq_failures;
int err = 0;
u32 is_an;
/* Changing the port's flow control is not supported if this isn't the
* port's controlling PF
*/
if (hw->partition_id != 1) {
i40e_partition_setting_complaint(pf);
return -EOPNOTSUPP;
}
if (vsi != pf->vsi[pf->lan_vsi])
return -EOPNOTSUPP;
is_an = hw_link_info->an_info & I40E_AQ_AN_COMPLETED;
if (pause->autoneg != is_an) {
netdev_info(netdev, "To change autoneg please use: ethtool -s <dev> autoneg <on|off>\n");
return -EOPNOTSUPP;
}
/* If we have link and don't have autoneg */
if (!test_bit(__I40E_DOWN, pf->state) && !is_an) {
/* Send message that it might not necessarily work*/
netdev_info(netdev, "Autoneg did not complete so changing settings may not result in an actual change.\n");
}
if (dcbx_cfg->pfc.pfcenable) {
netdev_info(netdev,
"Priority flow control enabled. Cannot set link flow control.\n");
return -EOPNOTSUPP;
}
if (pause->rx_pause && pause->tx_pause)
hw->fc.requested_mode = I40E_FC_FULL;
else if (pause->rx_pause && !pause->tx_pause)
hw->fc.requested_mode = I40E_FC_RX_PAUSE;
else if (!pause->rx_pause && pause->tx_pause)
hw->fc.requested_mode = I40E_FC_TX_PAUSE;
else if (!pause->rx_pause && !pause->tx_pause)
hw->fc.requested_mode = I40E_FC_NONE;
else
return -EINVAL;
/* Tell the OS link is going down, the link will go back up when fw
* says it is ready asynchronously
*/
i40e_print_link_message(vsi, false);
netif_carrier_off(netdev);
netif_tx_stop_all_queues(netdev);
/* Set the fc mode and only restart an if link is up*/
status = i40e_set_fc(hw, &aq_failures, link_up);
if (aq_failures & I40E_SET_FC_AQ_FAIL_GET) {
netdev_info(netdev, "Set fc failed on the get_phy_capabilities call with err %s aq_err %s\n",
i40e_stat_str(hw, status),
i40e_aq_str(hw, hw->aq.asq_last_status));
err = -EAGAIN;
}
if (aq_failures & I40E_SET_FC_AQ_FAIL_SET) {
netdev_info(netdev, "Set fc failed on the set_phy_config call with err %s aq_err %s\n",
i40e_stat_str(hw, status),
i40e_aq_str(hw, hw->aq.asq_last_status));
err = -EAGAIN;
}
if (aq_failures & I40E_SET_FC_AQ_FAIL_UPDATE) {
netdev_info(netdev, "Set fc failed on the get_link_info call with err %s aq_err %s\n",
i40e_stat_str(hw, status),
i40e_aq_str(hw, hw->aq.asq_last_status));
err = -EAGAIN;
}
if (!test_bit(__I40E_DOWN, pf->state) && is_an) {
/* Give it a little more time to try to come back */
msleep(75);
if (!test_bit(__I40E_DOWN, pf->state))
return i40e_nway_reset(netdev);
}
return err;
}
static u32 i40e_get_msglevel(struct net_device *netdev)
{
struct i40e_netdev_priv *np = netdev_priv(netdev);
struct i40e_pf *pf = np->vsi->back;
u32 debug_mask = pf->hw.debug_mask;
if (debug_mask)
netdev_info(netdev, "i40e debug_mask: 0x%08X\n", debug_mask);
return pf->msg_enable;
}
static void i40e_set_msglevel(struct net_device *netdev, u32 data)
{
struct i40e_netdev_priv *np = netdev_priv(netdev);
struct i40e_pf *pf = np->vsi->back;
if (I40E_DEBUG_USER & data)
pf->hw.debug_mask = data;
else
pf->msg_enable = data;
}
static int i40e_get_regs_len(struct net_device *netdev)
{
int reg_count = 0;
int i;
for (i = 0; i40e_reg_list[i].offset != 0; i++)
reg_count += i40e_reg_list[i].elements;
return reg_count * sizeof(u32);
}
static void i40e_get_regs(struct net_device *netdev, struct ethtool_regs *regs,
void *p)
{
struct i40e_netdev_priv *np = netdev_priv(netdev);
struct i40e_pf *pf = np->vsi->back;
struct i40e_hw *hw = &pf->hw;
u32 *reg_buf = p;
unsigned int i, j, ri;
u32 reg;
/* Tell ethtool which driver-version-specific regs output we have.
*
* At some point, if we have ethtool doing special formatting of
* this data, it will rely on this version number to know how to
* interpret things. Hence, this needs to be updated if/when the
* diags register table is changed.
*/
regs->version = 1;
/* loop through the diags reg table for what to print */
ri = 0;
for (i = 0; i40e_reg_list[i].offset != 0; i++) {
for (j = 0; j < i40e_reg_list[i].elements; j++) {
reg = i40e_reg_list[i].offset
+ (j * i40e_reg_list[i].stride);
reg_buf[ri++] = rd32(hw, reg);
}
}
}
static int i40e_get_eeprom(struct net_device *netdev,
struct ethtool_eeprom *eeprom, u8 *bytes)
{
struct i40e_netdev_priv *np = netdev_priv(netdev);
struct i40e_hw *hw = &np->vsi->back->hw;
struct i40e_pf *pf = np->vsi->back;
int ret_val = 0, len, offset;
u8 *eeprom_buff;
u16 i, sectors;
bool last;
u32 magic;
#define I40E_NVM_SECTOR_SIZE 4096
if (eeprom->len == 0)
return -EINVAL;
/* check for NVMUpdate access method */
magic = hw->vendor_id | (hw->device_id << 16);
if (eeprom->magic && eeprom->magic != magic) {
struct i40e_nvm_access *cmd = (struct i40e_nvm_access *)eeprom;
int errno = 0;
/* make sure it is the right magic for NVMUpdate */
if ((eeprom->magic >> 16) != hw->device_id)
errno = -EINVAL;
else if (test_bit(__I40E_RESET_RECOVERY_PENDING, pf->state) ||
test_bit(__I40E_RESET_INTR_RECEIVED, pf->state))
errno = -EBUSY;
else
ret_val = i40e_nvmupd_command(hw, cmd, bytes, &errno);
if ((errno || ret_val) && (hw->debug_mask & I40E_DEBUG_NVM))
dev_info(&pf->pdev->dev,
"NVMUpdate read failed err=%d status=0x%x errno=%d module=%d offset=0x%x size=%d\n",
ret_val, hw->aq.asq_last_status, errno,
(u8)(cmd->config & I40E_NVM_MOD_PNT_MASK),
cmd->offset, cmd->data_size);
return errno;
}
/* normal ethtool get_eeprom support */
eeprom->magic = hw->vendor_id | (hw->device_id << 16);
eeprom_buff = kzalloc(eeprom->len, GFP_KERNEL);
if (!eeprom_buff)
return -ENOMEM;
ret_val = i40e_acquire_nvm(hw, I40E_RESOURCE_READ);
if (ret_val) {
dev_info(&pf->pdev->dev,
"Failed Acquiring NVM resource for read err=%d status=0x%x\n",
ret_val, hw->aq.asq_last_status);
goto free_buff;
}
sectors = eeprom->len / I40E_NVM_SECTOR_SIZE;
sectors += (eeprom->len % I40E_NVM_SECTOR_SIZE) ? 1 : 0;
len = I40E_NVM_SECTOR_SIZE;
last = false;
for (i = 0; i < sectors; i++) {
if (i == (sectors - 1)) {
len = eeprom->len - (I40E_NVM_SECTOR_SIZE * i);
last = true;
}
offset = eeprom->offset + (I40E_NVM_SECTOR_SIZE * i),
ret_val = i40e_aq_read_nvm(hw, 0x0, offset, len,
(u8 *)eeprom_buff + (I40E_NVM_SECTOR_SIZE * i),
last, NULL);
if (ret_val && hw->aq.asq_last_status == I40E_AQ_RC_EPERM) {
dev_info(&pf->pdev->dev,
"read NVM failed, invalid offset 0x%x\n",
offset);
break;
} else if (ret_val &&
hw->aq.asq_last_status == I40E_AQ_RC_EACCES) {
dev_info(&pf->pdev->dev,
"read NVM failed, access, offset 0x%x\n",
offset);
break;
} else if (ret_val) {
dev_info(&pf->pdev->dev,
"read NVM failed offset %d err=%d status=0x%x\n",
offset, ret_val, hw->aq.asq_last_status);
break;
}
}
i40e_release_nvm(hw);
memcpy(bytes, (u8 *)eeprom_buff, eeprom->len);
free_buff:
kfree(eeprom_buff);
return ret_val;
}
static int i40e_get_eeprom_len(struct net_device *netdev)
{
struct i40e_netdev_priv *np = netdev_priv(netdev);
struct i40e_hw *hw = &np->vsi->back->hw;
u32 val;
#define X722_EEPROM_SCOPE_LIMIT 0x5B9FFF
if (hw->mac.type == I40E_MAC_X722) {
val = X722_EEPROM_SCOPE_LIMIT + 1;
return val;
}
val = (rd32(hw, I40E_GLPCI_LBARCTRL)
& I40E_GLPCI_LBARCTRL_FL_SIZE_MASK)
>> I40E_GLPCI_LBARCTRL_FL_SIZE_SHIFT;
/* register returns value in power of 2, 64Kbyte chunks. */
val = (64 * 1024) * BIT(val);
return val;
}
static int i40e_set_eeprom(struct net_device *netdev,
struct ethtool_eeprom *eeprom, u8 *bytes)
{
struct i40e_netdev_priv *np = netdev_priv(netdev);
struct i40e_hw *hw = &np->vsi->back->hw;
struct i40e_pf *pf = np->vsi->back;
struct i40e_nvm_access *cmd = (struct i40e_nvm_access *)eeprom;
int ret_val = 0;
int errno = 0;
u32 magic;
/* normal ethtool set_eeprom is not supported */
magic = hw->vendor_id | (hw->device_id << 16);
if (eeprom->magic == magic)
errno = -EOPNOTSUPP;
/* check for NVMUpdate access method */
else if (!eeprom->magic || (eeprom->magic >> 16) != hw->device_id)
errno = -EINVAL;
else if (test_bit(__I40E_RESET_RECOVERY_PENDING, pf->state) ||
test_bit(__I40E_RESET_INTR_RECEIVED, pf->state))
errno = -EBUSY;
else
ret_val = i40e_nvmupd_command(hw, cmd, bytes, &errno);
if ((errno || ret_val) && (hw->debug_mask & I40E_DEBUG_NVM))
dev_info(&pf->pdev->dev,
"NVMUpdate write failed err=%d status=0x%x errno=%d module=%d offset=0x%x size=%d\n",
ret_val, hw->aq.asq_last_status, errno,
(u8)(cmd->config & I40E_NVM_MOD_PNT_MASK),
cmd->offset, cmd->data_size);
return errno;
}
static void i40e_get_drvinfo(struct net_device *netdev,
struct ethtool_drvinfo *drvinfo)
{
struct i40e_netdev_priv *np = netdev_priv(netdev);
struct i40e_vsi *vsi = np->vsi;
struct i40e_pf *pf = vsi->back;
strlcpy(drvinfo->driver, i40e_driver_name, sizeof(drvinfo->driver));
strlcpy(drvinfo->fw_version, i40e_nvm_version_str(&pf->hw),
sizeof(drvinfo->fw_version));
strlcpy(drvinfo->bus_info, pci_name(pf->pdev),
sizeof(drvinfo->bus_info));
drvinfo->n_priv_flags = I40E_PRIV_FLAGS_STR_LEN;
if (pf->hw.pf_id == 0)
drvinfo->n_priv_flags += I40E_GL_PRIV_FLAGS_STR_LEN;
}
static void i40e_get_ringparam(struct net_device *netdev,
struct ethtool_ringparam *ring)
{
struct i40e_netdev_priv *np = netdev_priv(netdev);
struct i40e_pf *pf = np->vsi->back;
struct i40e_vsi *vsi = pf->vsi[pf->lan_vsi];
ring->rx_max_pending = I40E_MAX_NUM_DESCRIPTORS;
ring->tx_max_pending = I40E_MAX_NUM_DESCRIPTORS;
ring->rx_mini_max_pending = 0;
ring->rx_jumbo_max_pending = 0;
ring->rx_pending = vsi->rx_rings[0]->count;
ring->tx_pending = vsi->tx_rings[0]->count;
ring->rx_mini_pending = 0;
ring->rx_jumbo_pending = 0;
}
static bool i40e_active_tx_ring_index(struct i40e_vsi *vsi, u16 index)
{
if (i40e_enabled_xdp_vsi(vsi)) {
return index < vsi->num_queue_pairs ||
(index >= vsi->alloc_queue_pairs &&
index < vsi->alloc_queue_pairs + vsi->num_queue_pairs);
}
return index < vsi->num_queue_pairs;
}
static int i40e_set_ringparam(struct net_device *netdev,
struct ethtool_ringparam *ring)
{
struct i40e_ring *tx_rings = NULL, *rx_rings = NULL;
struct i40e_netdev_priv *np = netdev_priv(netdev);
struct i40e_hw *hw = &np->vsi->back->hw;
struct i40e_vsi *vsi = np->vsi;
struct i40e_pf *pf = vsi->back;
u32 new_rx_count, new_tx_count;
u16 tx_alloc_queue_pairs;
int timeout = 50;
int i, err = 0;
if ((ring->rx_mini_pending) || (ring->rx_jumbo_pending))
return -EINVAL;
if (ring->tx_pending > I40E_MAX_NUM_DESCRIPTORS ||
ring->tx_pending < I40E_MIN_NUM_DESCRIPTORS ||
ring->rx_pending > I40E_MAX_NUM_DESCRIPTORS ||
ring->rx_pending < I40E_MIN_NUM_DESCRIPTORS) {
netdev_info(netdev,
"Descriptors requested (Tx: %d / Rx: %d) out of range [%d-%d]\n",
ring->tx_pending, ring->rx_pending,
I40E_MIN_NUM_DESCRIPTORS, I40E_MAX_NUM_DESCRIPTORS);
return -EINVAL;
}
new_tx_count = ALIGN(ring->tx_pending, I40E_REQ_DESCRIPTOR_MULTIPLE);
new_rx_count = ALIGN(ring->rx_pending, I40E_REQ_DESCRIPTOR_MULTIPLE);
/* if nothing to do return success */
if ((new_tx_count == vsi->tx_rings[0]->count) &&
(new_rx_count == vsi->rx_rings[0]->count))
return 0;
/* If there is a AF_XDP page pool attached to any of Rx rings,
* disallow changing the number of descriptors -- regardless
* if the netdev is running or not.
*/
if (i40e_xsk_any_rx_ring_enabled(vsi))
return -EBUSY;
while (test_and_set_bit(__I40E_CONFIG_BUSY, pf->state)) {
timeout--;
if (!timeout)
return -EBUSY;
usleep_range(1000, 2000);
}
if (!netif_running(vsi->netdev)) {
/* simple case - set for the next time the netdev is started */
for (i = 0; i < vsi->num_queue_pairs; i++) {
vsi->tx_rings[i]->count = new_tx_count;
vsi->rx_rings[i]->count = new_rx_count;
if (i40e_enabled_xdp_vsi(vsi))
vsi->xdp_rings[i]->count = new_tx_count;
}
vsi->num_tx_desc = new_tx_count;
vsi->num_rx_desc = new_rx_count;
goto done;
}
/* 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.
*/
/* alloc updated Tx and XDP Tx resources */
tx_alloc_queue_pairs = vsi->alloc_queue_pairs *
(i40e_enabled_xdp_vsi(vsi) ? 2 : 1);
if (new_tx_count != vsi->tx_rings[0]->count) {
netdev_info(netdev,
"Changing Tx descriptor count from %d to %d.\n",
vsi->tx_rings[0]->count, new_tx_count);
tx_rings = kcalloc(tx_alloc_queue_pairs,
sizeof(struct i40e_ring), GFP_KERNEL);
if (!tx_rings) {
err = -ENOMEM;
goto done;
}
for (i = 0; i < tx_alloc_queue_pairs; i++) {
if (!i40e_active_tx_ring_index(vsi, i))
continue;
tx_rings[i] = *vsi->tx_rings[i];
tx_rings[i].count = new_tx_count;
/* the desc and bi pointers will be reallocated in the
* setup call
*/
tx_rings[i].desc = NULL;
tx_rings[i].rx_bi = NULL;
err = i40e_setup_tx_descriptors(&tx_rings[i]);
if (err) {
while (i) {
i--;
if (!i40e_active_tx_ring_index(vsi, i))
continue;
i40e_free_tx_resources(&tx_rings[i]);
}
kfree(tx_rings);
tx_rings = NULL;
goto done;
}
}
}
/* alloc updated Rx resources */
if (new_rx_count != vsi->rx_rings[0]->count) {
netdev_info(netdev,
"Changing Rx descriptor count from %d to %d\n",
vsi->rx_rings[0]->count, new_rx_count);
rx_rings = kcalloc(vsi->alloc_queue_pairs,
sizeof(struct i40e_ring), GFP_KERNEL);
if (!rx_rings) {
err = -ENOMEM;
goto free_tx;
}
for (i = 0; i < vsi->num_queue_pairs; i++) {
u16 unused;
/* clone ring and setup updated count */
rx_rings[i] = *vsi->rx_rings[i];
rx_rings[i].count = new_rx_count;
/* the desc and bi pointers will be reallocated in the
* setup call
*/
rx_rings[i].desc = NULL;
rx_rings[i].rx_bi = NULL;
/* Clear cloned XDP RX-queue info before setup call */
memset(&rx_rings[i].xdp_rxq, 0, sizeof(rx_rings[i].xdp_rxq));
/* this is to allow wr32 to have something to write to
* during early allocation of Rx buffers
*/
rx_rings[i].tail = hw->hw_addr + I40E_PRTGEN_STATUS;
err = i40e_setup_rx_descriptors(&rx_rings[i]);
if (err)
goto rx_unwind;
err = i40e_alloc_rx_bi(&rx_rings[i]);
if (err)
goto rx_unwind;
/* now allocate the Rx buffers to make sure the OS
* has enough memory, any failure here means abort
*/
unused = I40E_DESC_UNUSED(&rx_rings[i]);
err = i40e_alloc_rx_buffers(&rx_rings[i], unused);
rx_unwind:
if (err) {
do {
i40e_free_rx_resources(&rx_rings[i]);
} while (i--);
kfree(rx_rings);
rx_rings = NULL;
goto free_tx;
}
}
}
/* Bring interface down, copy in the new ring info,
* then restore the interface
*/
i40e_down(vsi);
if (tx_rings) {
for (i = 0; i < tx_alloc_queue_pairs; i++) {
if (i40e_active_tx_ring_index(vsi, i)) {
i40e_free_tx_resources(vsi->tx_rings[i]);
*vsi->tx_rings[i] = tx_rings[i];
}
}
kfree(tx_rings);
tx_rings = NULL;
}
if (rx_rings) {
for (i = 0; i < vsi->num_queue_pairs; i++) {
i40e_free_rx_resources(vsi->rx_rings[i]);
/* get the real tail offset */
rx_rings[i].tail = vsi->rx_rings[i]->tail;
/* this is to fake out the allocation routine
* into thinking it has to realloc everything
* but the recycling logic will let us re-use
* the buffers allocated above
*/
rx_rings[i].next_to_use = 0;
rx_rings[i].next_to_clean = 0;
rx_rings[i].next_to_alloc = 0;
/* do a struct copy */
*vsi->rx_rings[i] = rx_rings[i];
}
kfree(rx_rings);
rx_rings = NULL;
}
vsi->num_tx_desc = new_tx_count;
vsi->num_rx_desc = new_rx_count;
i40e_up(vsi);
free_tx:
/* error cleanup if the Rx allocations failed after getting Tx */
if (tx_rings) {
for (i = 0; i < tx_alloc_queue_pairs; i++) {
if (i40e_active_tx_ring_index(vsi, i))
i40e_free_tx_resources(vsi->tx_rings[i]);
}
kfree(tx_rings);
tx_rings = NULL;
}
done:
clear_bit(__I40E_CONFIG_BUSY, pf->state);
return err;
}
/**
* i40e_get_stats_count - return the stats count for a device
* @netdev: the netdev to return the count for
*
* Returns the total number of statistics for this netdev. Note that even
* though this is a function, it is required that the count for a specific
* netdev must never change. Basing the count on static values such as the
* maximum number of queues or the device type is ok. However, the API for
* obtaining stats is *not* safe against changes based on non-static
* values such as the *current* number of queues, or runtime flags.
*
* If a statistic is not always enabled, return it as part of the count
* anyways, always return its string, and report its value as zero.
**/
static int i40e_get_stats_count(struct net_device *netdev)
{
struct i40e_netdev_priv *np = netdev_priv(netdev);
struct i40e_vsi *vsi = np->vsi;
struct i40e_pf *pf = vsi->back;
int stats_len;
if (vsi == pf->vsi[pf->lan_vsi] && pf->hw.partition_id == 1)
stats_len = I40E_PF_STATS_LEN;
else
stats_len = I40E_VSI_STATS_LEN;
/* The number of stats reported for a given net_device must remain
* constant throughout the life of that device.
*
* This is because the API for obtaining the size, strings, and stats
* is spread out over three separate ethtool ioctls. There is no safe
* way to lock the number of stats across these calls, so we must
* assume that they will never change.
*
* Due to this, we report the maximum number of queues, even if not
* every queue is currently configured. Since we always allocate
* queues in pairs, we'll just use netdev->num_tx_queues * 2. This
* works because the num_tx_queues is set at device creation and never
* changes.
*/
stats_len += I40E_QUEUE_STATS_LEN * 2 * netdev->num_tx_queues;
return stats_len;
}
static int i40e_get_sset_count(struct net_device *netdev, int sset)
{
struct i40e_netdev_priv *np = netdev_priv(netdev);
struct i40e_vsi *vsi = np->vsi;
struct i40e_pf *pf = vsi->back;
switch (sset) {
case ETH_SS_TEST:
return I40E_TEST_LEN;
case ETH_SS_STATS:
return i40e_get_stats_count(netdev);
case ETH_SS_PRIV_FLAGS:
return I40E_PRIV_FLAGS_STR_LEN +
(pf->hw.pf_id == 0 ? I40E_GL_PRIV_FLAGS_STR_LEN : 0);
default:
return -EOPNOTSUPP;
}
}
/**
* i40e_get_veb_tc_stats - copy VEB TC statistics to formatted structure
* @tc: the TC statistics in VEB structure (veb->tc_stats)
* @i: the index of traffic class in (veb->tc_stats) structure to copy
*
* Copy VEB TC statistics from structure of arrays (veb->tc_stats) to
* one dimensional structure i40e_cp_veb_tc_stats.
* Produce formatted i40e_cp_veb_tc_stats structure of the VEB TC
* statistics for the given TC.
**/
static struct i40e_cp_veb_tc_stats
i40e_get_veb_tc_stats(struct i40e_veb_tc_stats *tc, unsigned int i)
{
struct i40e_cp_veb_tc_stats veb_tc = {
.tc_rx_packets = tc->tc_rx_packets[i],
.tc_rx_bytes = tc->tc_rx_bytes[i],
.tc_tx_packets = tc->tc_tx_packets[i],
.tc_tx_bytes = tc->tc_tx_bytes[i],
};
return veb_tc;
}
/**
* i40e_get_pfc_stats - copy HW PFC statistics to formatted structure
* @pf: the PF device structure
* @i: the priority value to copy
*
* The PFC stats are found as arrays in pf->stats, which is not easy to pass
* into i40e_add_ethtool_stats. Produce a formatted i40e_pfc_stats structure
* of the PFC stats for the given priority.
**/
static inline struct i40e_pfc_stats
i40e_get_pfc_stats(struct i40e_pf *pf, unsigned int i)
{
#define I40E_GET_PFC_STAT(stat, priority) \
.stat = pf->stats.stat[priority]
struct i40e_pfc_stats pfc = {
I40E_GET_PFC_STAT(priority_xon_rx, i),
I40E_GET_PFC_STAT(priority_xoff_rx, i),
I40E_GET_PFC_STAT(priority_xon_tx, i),
I40E_GET_PFC_STAT(priority_xoff_tx, i),
I40E_GET_PFC_STAT(priority_xon_2_xoff, i),
};
return pfc;
}
/**
* i40e_get_ethtool_stats - copy stat values into supplied buffer
* @netdev: the netdev to collect stats for
* @stats: ethtool stats command structure
* @data: ethtool supplied buffer
*
* Copy the stats values for this netdev into the buffer. Expects data to be
* pre-allocated to the size returned by i40e_get_stats_count.. Note that all
* statistics must be copied in a static order, and the count must not change
* for a given netdev. See i40e_get_stats_count for more details.
*
* If a statistic is not currently valid (such as a disabled queue), this
* function reports its value as zero.
**/
static void i40e_get_ethtool_stats(struct net_device *netdev,
struct ethtool_stats *stats, u64 *data)
{
struct i40e_netdev_priv *np = netdev_priv(netdev);
struct i40e_vsi *vsi = np->vsi;
struct i40e_pf *pf = vsi->back;
struct i40e_veb *veb = NULL;
unsigned int i;
bool veb_stats;
u64 *p = data;
i40e_update_stats(vsi);
i40e_add_ethtool_stats(&data, i40e_get_vsi_stats_struct(vsi),
i40e_gstrings_net_stats);
i40e_add_ethtool_stats(&data, vsi, i40e_gstrings_misc_stats);
rcu_read_lock();
for (i = 0; i < netdev->num_tx_queues; i++) {
i40e_add_queue_stats(&data, READ_ONCE(vsi->tx_rings[i]));
i40e_add_queue_stats(&data, READ_ONCE(vsi->rx_rings[i]));
}
rcu_read_unlock();
if (vsi != pf->vsi[pf->lan_vsi] || pf->hw.partition_id != 1)
goto check_data_pointer;
veb_stats = ((pf->lan_veb != I40E_NO_VEB) &&
(pf->lan_veb < I40E_MAX_VEB) &&
(pf->flags & I40E_FLAG_VEB_STATS_ENABLED));
if (veb_stats) {
veb = pf->veb[pf->lan_veb];
i40e_update_veb_stats(veb);
}
/* If veb stats aren't enabled, pass NULL instead of the veb so that
* we initialize stats to zero and update the data pointer
* intelligently
*/
i40e_add_ethtool_stats(&data, veb_stats ? veb : NULL,
i40e_gstrings_veb_stats);
for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++)
if (veb_stats) {
struct i40e_cp_veb_tc_stats veb_tc =
i40e_get_veb_tc_stats(&veb->tc_stats, i);
i40e_add_ethtool_stats(&data, &veb_tc,
i40e_gstrings_veb_tc_stats);
} else {
i40e_add_ethtool_stats(&data, NULL,
i40e_gstrings_veb_tc_stats);
}
i40e_add_ethtool_stats(&data, pf, i40e_gstrings_stats);
for (i = 0; i < I40E_MAX_USER_PRIORITY; i++) {
struct i40e_pfc_stats pfc = i40e_get_pfc_stats(pf, i);
i40e_add_ethtool_stats(&data, &pfc, i40e_gstrings_pfc_stats);
}
check_data_pointer:
WARN_ONCE(data - p != i40e_get_stats_count(netdev),
"ethtool stats count mismatch!");
}
/**
* i40e_get_stat_strings - copy stat strings into supplied buffer
* @netdev: the netdev to collect strings for
* @data: supplied buffer to copy strings into
*
* Copy the strings related to stats for this netdev. Expects data to be
* pre-allocated with the size reported by i40e_get_stats_count. Note that the
* strings must be copied in a static order and the total count must not
* change for a given netdev. See i40e_get_stats_count for more details.
**/
static void i40e_get_stat_strings(struct net_device *netdev, u8 *data)
{
struct i40e_netdev_priv *np = netdev_priv(netdev);
struct i40e_vsi *vsi = np->vsi;
struct i40e_pf *pf = vsi->back;
unsigned int i;
u8 *p = data;
i40e_add_stat_strings(&data, i40e_gstrings_net_stats);
i40e_add_stat_strings(&data, i40e_gstrings_misc_stats);
for (i = 0; i < netdev->num_tx_queues; i++) {
i40e_add_stat_strings(&data, i40e_gstrings_queue_stats,
"tx", i);
i40e_add_stat_strings(&data, i40e_gstrings_queue_stats,
"rx", i);
}
if (vsi != pf->vsi[pf->lan_vsi] || pf->hw.partition_id != 1)
goto check_data_pointer;
i40e_add_stat_strings(&data, i40e_gstrings_veb_stats);
for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++)
i40e_add_stat_strings(&data, i40e_gstrings_veb_tc_stats, i);
i40e_add_stat_strings(&data, i40e_gstrings_stats);
for (i = 0; i < I40E_MAX_USER_PRIORITY; i++)
i40e_add_stat_strings(&data, i40e_gstrings_pfc_stats, i);
check_data_pointer:
WARN_ONCE(data - p != i40e_get_stats_count(netdev) * ETH_GSTRING_LEN,
"stat strings count mismatch!");
}
static void i40e_get_priv_flag_strings(struct net_device *netdev, u8 *data)
{
struct i40e_netdev_priv *np = netdev_priv(netdev);
struct i40e_vsi *vsi = np->vsi;
struct i40e_pf *pf = vsi->back;
unsigned int i;
u8 *p = data;
for (i = 0; i < I40E_PRIV_FLAGS_STR_LEN; i++)
ethtool_sprintf(&p, i40e_gstrings_priv_flags[i].flag_string);
if (pf->hw.pf_id != 0)
return;
for (i = 0; i < I40E_GL_PRIV_FLAGS_STR_LEN; i++)
ethtool_sprintf(&p, i40e_gl_gstrings_priv_flags[i].flag_string);
}
static void i40e_get_strings(struct net_device *netdev, u32 stringset,
u8 *data)
{
switch (stringset) {
case ETH_SS_TEST:
memcpy(data, i40e_gstrings_test,
I40E_TEST_LEN * ETH_GSTRING_LEN);
break;
case ETH_SS_STATS:
i40e_get_stat_strings(netdev, data);
break;
case ETH_SS_PRIV_FLAGS:
i40e_get_priv_flag_strings(netdev, data);
break;
default:
break;
}
}
static int i40e_get_ts_info(struct net_device *dev,
struct ethtool_ts_info *info)
{
struct i40e_pf *pf = i40e_netdev_to_pf(dev);
/* only report HW timestamping if PTP is enabled */
if (!(pf->flags & I40E_FLAG_PTP))
return ethtool_op_get_ts_info(dev, info);
info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE |
SOF_TIMESTAMPING_RX_SOFTWARE |
SOF_TIMESTAMPING_SOFTWARE |
SOF_TIMESTAMPING_TX_HARDWARE |
SOF_TIMESTAMPING_RX_HARDWARE |
SOF_TIMESTAMPING_RAW_HARDWARE;
if (pf->ptp_clock)
info->phc_index = ptp_clock_index(pf->ptp_clock);
else
info->phc_index = -1;
info->tx_types = BIT(HWTSTAMP_TX_OFF) | BIT(HWTSTAMP_TX_ON);
info->rx_filters = BIT(HWTSTAMP_FILTER_NONE) |
BIT(HWTSTAMP_FILTER_PTP_V2_L2_EVENT) |
BIT(HWTSTAMP_FILTER_PTP_V2_L2_SYNC) |
BIT(HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ);
if (pf->hw_features & I40E_HW_PTP_L4_CAPABLE)
info->rx_filters |= BIT(HWTSTAMP_FILTER_PTP_V1_L4_SYNC) |
BIT(HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ) |
BIT(HWTSTAMP_FILTER_PTP_V2_EVENT) |
BIT(HWTSTAMP_FILTER_PTP_V2_L4_EVENT) |
BIT(HWTSTAMP_FILTER_PTP_V2_SYNC) |
BIT(HWTSTAMP_FILTER_PTP_V2_L4_SYNC) |
BIT(HWTSTAMP_FILTER_PTP_V2_DELAY_REQ) |
BIT(HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ);
return 0;
}
static u64 i40e_link_test(struct net_device *netdev, u64 *data)
{
struct i40e_netdev_priv *np = netdev_priv(netdev);
struct i40e_pf *pf = np->vsi->back;
i40e_status status;
bool link_up = false;
netif_info(pf, hw, netdev, "link test\n");
status = i40e_get_link_status(&pf->hw, &link_up);
if (status) {
netif_err(pf, drv, netdev, "link query timed out, please retry test\n");
*data = 1;
return *data;
}
if (link_up)
*data = 0;
else
*data = 1;
return *data;
}
static u64 i40e_reg_test(struct net_device *netdev, u64 *data)
{
struct i40e_netdev_priv *np = netdev_priv(netdev);
struct i40e_pf *pf = np->vsi->back;
netif_info(pf, hw, netdev, "register test\n");
*data = i40e_diag_reg_test(&pf->hw);
return *data;
}
static u64 i40e_eeprom_test(struct net_device *netdev, u64 *data)
{
struct i40e_netdev_priv *np = netdev_priv(netdev);
struct i40e_pf *pf = np->vsi->back;
netif_info(pf, hw, netdev, "eeprom test\n");
*data = i40e_diag_eeprom_test(&pf->hw);
/* forcebly clear the NVM Update state machine */
pf->hw.nvmupd_state = I40E_NVMUPD_STATE_INIT;
return *data;
}
static u64 i40e_intr_test(struct net_device *netdev, u64 *data)
{
struct i40e_netdev_priv *np = netdev_priv(netdev);
struct i40e_pf *pf = np->vsi->back;
u16 swc_old = pf->sw_int_count;
netif_info(pf, hw, netdev, "interrupt test\n");
wr32(&pf->hw, I40E_PFINT_DYN_CTL0,
(I40E_PFINT_DYN_CTL0_INTENA_MASK |
I40E_PFINT_DYN_CTL0_SWINT_TRIG_MASK |
I40E_PFINT_DYN_CTL0_ITR_INDX_MASK |
I40E_PFINT_DYN_CTL0_SW_ITR_INDX_ENA_MASK |
I40E_PFINT_DYN_CTL0_SW_ITR_INDX_MASK));
usleep_range(1000, 2000);
*data = (swc_old == pf->sw_int_count);
return *data;
}
static inline bool i40e_active_vfs(struct i40e_pf *pf)
{
struct i40e_vf *vfs = pf->vf;
int i;
for (i = 0; i < pf->num_alloc_vfs; i++)
if (test_bit(I40E_VF_STATE_ACTIVE, &vfs[i].vf_states))
return true;
return false;
}
static inline bool i40e_active_vmdqs(struct i40e_pf *pf)
{
return !!i40e_find_vsi_by_type(pf, I40E_VSI_VMDQ2);
}
static void i40e_diag_test(struct net_device *netdev,
struct ethtool_test *eth_test, u64 *data)
{
struct i40e_netdev_priv *np = netdev_priv(netdev);
bool if_running = netif_running(netdev);
struct i40e_pf *pf = np->vsi->back;
if (eth_test->flags == ETH_TEST_FL_OFFLINE) {
/* Offline tests */
netif_info(pf, drv, netdev, "offline testing starting\n");
set_bit(__I40E_TESTING, pf->state);
if (i40e_active_vfs(pf) || i40e_active_vmdqs(pf)) {
dev_warn(&pf->pdev->dev,
"Please take active VFs and Netqueues offline and restart the adapter before running NIC diagnostics\n");
data[I40E_ETH_TEST_REG] = 1;
data[I40E_ETH_TEST_EEPROM] = 1;
data[I40E_ETH_TEST_INTR] = 1;
data[I40E_ETH_TEST_LINK] = 1;
eth_test->flags |= ETH_TEST_FL_FAILED;
clear_bit(__I40E_TESTING, pf->state);
goto skip_ol_tests;
}
/* If the device is online then take it offline */
if (if_running)
/* indicate we're in test mode */
i40e_close(netdev);
else
/* This reset does not affect link - if it is
* changed to a type of reset that does affect
* link then the following link test would have
* to be moved to before the reset
*/
i40e_do_reset(pf, BIT(__I40E_PF_RESET_REQUESTED), true);
if (i40e_link_test(netdev, &data[I40E_ETH_TEST_LINK]))
eth_test->flags |= ETH_TEST_FL_FAILED;
if (i40e_eeprom_test(netdev, &data[I40E_ETH_TEST_EEPROM]))
eth_test->flags |= ETH_TEST_FL_FAILED;
if (i40e_intr_test(netdev, &data[I40E_ETH_TEST_INTR]))
eth_test->flags |= ETH_TEST_FL_FAILED;
/* run reg test last, a reset is required after it */
if (i40e_reg_test(netdev, &data[I40E_ETH_TEST_REG]))
eth_test->flags |= ETH_TEST_FL_FAILED;
clear_bit(__I40E_TESTING, pf->state);
i40e_do_reset(pf, BIT(__I40E_PF_RESET_REQUESTED), true);
if (if_running)
i40e_open(netdev);
} else {
/* Online tests */
netif_info(pf, drv, netdev, "online testing starting\n");
if (i40e_link_test(netdev, &data[I40E_ETH_TEST_LINK]))
eth_test->flags |= ETH_TEST_FL_FAILED;
/* Offline only tests, not run in online; pass by default */
data[I40E_ETH_TEST_REG] = 0;
data[I40E_ETH_TEST_EEPROM] = 0;
data[I40E_ETH_TEST_INTR] = 0;
}
skip_ol_tests:
netif_info(pf, drv, netdev, "testing finished\n");
}
static void i40e_get_wol(struct net_device *netdev,
struct ethtool_wolinfo *wol)
{
struct i40e_netdev_priv *np = netdev_priv(netdev);
struct i40e_pf *pf = np->vsi->back;
struct i40e_hw *hw = &pf->hw;
u16 wol_nvm_bits;
/* NVM bit on means WoL disabled for the port */
i40e_read_nvm_word(hw, I40E_SR_NVM_WAKE_ON_LAN, &wol_nvm_bits);
if ((BIT(hw->port) & wol_nvm_bits) || (hw->partition_id != 1)) {
wol->supported = 0;
wol->wolopts = 0;
} else {
wol->supported = WAKE_MAGIC;
wol->wolopts = (pf->wol_en ? WAKE_MAGIC : 0);
}
}
/**
* i40e_set_wol - set the WakeOnLAN configuration
* @netdev: the netdev in question
* @wol: the ethtool WoL setting data
**/
static int i40e_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol)
{
struct i40e_netdev_priv *np = netdev_priv(netdev);
struct i40e_pf *pf = np->vsi->back;
struct i40e_vsi *vsi = np->vsi;
struct i40e_hw *hw = &pf->hw;
u16 wol_nvm_bits;
/* WoL not supported if this isn't the controlling PF on the port */
if (hw->partition_id != 1) {
i40e_partition_setting_complaint(pf);
return -EOPNOTSUPP;
}
if (vsi != pf->vsi[pf->lan_vsi])
return -EOPNOTSUPP;
/* NVM bit on means WoL disabled for the port */
i40e_read_nvm_word(hw, I40E_SR_NVM_WAKE_ON_LAN, &wol_nvm_bits);
if (BIT(hw->port) & wol_nvm_bits)
return -EOPNOTSUPP;
/* only magic packet is supported */
if (wol->wolopts & ~WAKE_MAGIC)
return -EOPNOTSUPP;
/* is this a new value? */
if (pf->wol_en != !!wol->wolopts) {
pf->wol_en = !!wol->wolopts;
device_set_wakeup_enable(&pf->pdev->dev, pf->wol_en);
}
return 0;
}
static int i40e_set_phys_id(struct net_device *netdev,
enum ethtool_phys_id_state state)
{
struct i40e_netdev_priv *np = netdev_priv(netdev);
i40e_status ret = 0;
struct i40e_pf *pf = np->vsi->back;
struct i40e_hw *hw = &pf->hw;
int blink_freq = 2;
u16 temp_status;
switch (state) {
case ETHTOOL_ID_ACTIVE:
if (!(pf->hw_features & I40E_HW_PHY_CONTROLS_LEDS)) {
pf->led_status = i40e_led_get(hw);
} else {
if (!(hw->flags & I40E_HW_FLAG_AQ_PHY_ACCESS_CAPABLE))
i40e_aq_set_phy_debug(hw, I40E_PHY_DEBUG_ALL,
NULL);
ret = i40e_led_get_phy(hw, &temp_status,
&pf->phy_led_val);
pf->led_status = temp_status;
}
return blink_freq;
case ETHTOOL_ID_ON:
if (!(pf->hw_features & I40E_HW_PHY_CONTROLS_LEDS))
i40e_led_set(hw, 0xf, false);
else
ret = i40e_led_set_phy(hw, true, pf->led_status, 0);
break;
case ETHTOOL_ID_OFF:
if (!(pf->hw_features & I40E_HW_PHY_CONTROLS_LEDS))
i40e_led_set(hw, 0x0, false);
else
ret = i40e_led_set_phy(hw, false, pf->led_status, 0);
break;
case ETHTOOL_ID_INACTIVE:
if (!(pf->hw_features & I40E_HW_PHY_CONTROLS_LEDS)) {
i40e_led_set(hw, pf->led_status, false);
} else {
ret = i40e_led_set_phy(hw, false, pf->led_status,
(pf->phy_led_val |
I40E_PHY_LED_MODE_ORIG));
if (!(hw->flags & I40E_HW_FLAG_AQ_PHY_ACCESS_CAPABLE))
i40e_aq_set_phy_debug(hw, 0, NULL);
}
break;
default:
break;
}
if (ret)
return -ENOENT;
else
return 0;
}
/* NOTE: i40e hardware uses a conversion factor of 2 for Interrupt
* Throttle Rate (ITR) ie. ITR(1) = 2us ITR(10) = 20 us, and also
* 125us (8000 interrupts per second) == ITR(62)
*/
/**
* __i40e_get_coalesce - get per-queue coalesce settings
* @netdev: the netdev to check
* @ec: ethtool coalesce data structure
* @queue: which queue to pick
*
* Gets the per-queue settings for coalescence. Specifically Rx and Tx usecs
* are per queue. If queue is <0 then we default to queue 0 as the
* representative value.
**/
static int __i40e_get_coalesce(struct net_device *netdev,
struct ethtool_coalesce *ec,
int queue)
{
struct i40e_netdev_priv *np = netdev_priv(netdev);
struct i40e_ring *rx_ring, *tx_ring;
struct i40e_vsi *vsi = np->vsi;
ec->tx_max_coalesced_frames_irq = vsi->work_limit;
ec->rx_max_coalesced_frames_irq = vsi->work_limit;
/* rx and tx usecs has per queue value. If user doesn't specify the
* queue, return queue 0's value to represent.
*/
if (queue < 0)
queue = 0;
else if (queue >= vsi->num_queue_pairs)
return -EINVAL;
rx_ring = vsi->rx_rings[queue];
tx_ring = vsi->tx_rings[queue];
if (ITR_IS_DYNAMIC(rx_ring->itr_setting))
ec->use_adaptive_rx_coalesce = 1;
if (ITR_IS_DYNAMIC(tx_ring->itr_setting))
ec->use_adaptive_tx_coalesce = 1;
ec->rx_coalesce_usecs = rx_ring->itr_setting & ~I40E_ITR_DYNAMIC;
ec->tx_coalesce_usecs = tx_ring->itr_setting & ~I40E_ITR_DYNAMIC;
/* we use the _usecs_high to store/set the interrupt rate limit
* that the hardware supports, that almost but not quite
* fits the original intent of the ethtool variable,
* the rx_coalesce_usecs_high limits total interrupts
* per second from both tx/rx sources.
*/
ec->rx_coalesce_usecs_high = vsi->int_rate_limit;
ec->tx_coalesce_usecs_high = vsi->int_rate_limit;
return 0;
}
/**
* i40e_get_coalesce - get a netdev's coalesce settings
* @netdev: the netdev to check
* @ec: ethtool coalesce data structure
* @kernel_coal: ethtool CQE mode setting structure
* @extack: extack for reporting error messages
*
* Gets the coalesce settings for a particular netdev. Note that if user has
* modified per-queue settings, this only guarantees to represent queue 0. See
* __i40e_get_coalesce for more details.
**/
static int i40e_get_coalesce(struct net_device *netdev,
struct ethtool_coalesce *ec,
struct kernel_ethtool_coalesce *kernel_coal,
struct netlink_ext_ack *extack)
{
return __i40e_get_coalesce(netdev, ec, -1);
}
/**
* i40e_get_per_queue_coalesce - gets coalesce settings for particular queue
* @netdev: netdev structure
* @ec: ethtool's coalesce settings
* @queue: the particular queue to read
*
* Will read a specific queue's coalesce settings
**/
static int i40e_get_per_queue_coalesce(struct net_device *netdev, u32 queue,
struct ethtool_coalesce *ec)
{
return __i40e_get_coalesce(netdev, ec, queue);
}
/**
* i40e_set_itr_per_queue - set ITR values for specific queue
* @vsi: the VSI to set values for
* @ec: coalesce settings from ethtool
* @queue: the queue to modify
*
* Change the ITR settings for a specific queue.
**/
static void i40e_set_itr_per_queue(struct i40e_vsi *vsi,
struct ethtool_coalesce *ec,
int queue)
{
struct i40e_ring *rx_ring = vsi->rx_rings[queue];
struct i40e_ring *tx_ring = vsi->tx_rings[queue];
struct i40e_pf *pf = vsi->back;
struct i40e_hw *hw = &pf->hw;
struct i40e_q_vector *q_vector;
u16 intrl;
intrl = i40e_intrl_usec_to_reg(vsi->int_rate_limit);
rx_ring->itr_setting = ITR_REG_ALIGN(ec->rx_coalesce_usecs);
tx_ring->itr_setting = ITR_REG_ALIGN(ec->tx_coalesce_usecs);
if (ec->use_adaptive_rx_coalesce)
rx_ring->itr_setting |= I40E_ITR_DYNAMIC;
else
rx_ring->itr_setting &= ~I40E_ITR_DYNAMIC;
if (ec->use_adaptive_tx_coalesce)
tx_ring->itr_setting |= I40E_ITR_DYNAMIC;
else
tx_ring->itr_setting &= ~I40E_ITR_DYNAMIC;
q_vector = rx_ring->q_vector;
q_vector->rx.target_itr = ITR_TO_REG(rx_ring->itr_setting);
q_vector = tx_ring->q_vector;
q_vector->tx.target_itr = ITR_TO_REG(tx_ring->itr_setting);
/* The interrupt handler itself will take care of programming
* the Tx and Rx ITR values based on the values we have entered
* into the q_vector, no need to write the values now.
*/
wr32(hw, I40E_PFINT_RATEN(q_vector->reg_idx), intrl);
i40e_flush(hw);
}
/**
* __i40e_set_coalesce - set coalesce settings for particular queue
* @netdev: the netdev to change
* @ec: ethtool coalesce settings
* @queue: the queue to change
*
* Sets the coalesce settings for a particular queue.
**/
static int __i40e_set_coalesce(struct net_device *netdev,
struct ethtool_coalesce *ec,
int queue)
{
struct i40e_netdev_priv *np = netdev_priv(netdev);
u16 intrl_reg, cur_rx_itr, cur_tx_itr;
struct i40e_vsi *vsi = np->vsi;
struct i40e_pf *pf = vsi->back;
int i;
if (ec->tx_max_coalesced_frames_irq || ec->rx_max_coalesced_frames_irq)
vsi->work_limit = ec->tx_max_coalesced_frames_irq;
if (queue < 0) {
cur_rx_itr = vsi->rx_rings[0]->itr_setting;
cur_tx_itr = vsi->tx_rings[0]->itr_setting;
} else if (queue < vsi->num_queue_pairs) {
cur_rx_itr = vsi->rx_rings[queue]->itr_setting;
cur_tx_itr = vsi->tx_rings[queue]->itr_setting;
} else {
netif_info(pf, drv, netdev, "Invalid queue value, queue range is 0 - %d\n",
vsi->num_queue_pairs - 1);
return -EINVAL;
}
cur_tx_itr &= ~I40E_ITR_DYNAMIC;
cur_rx_itr &= ~I40E_ITR_DYNAMIC;
/* tx_coalesce_usecs_high is ignored, use rx-usecs-high instead */
if (ec->tx_coalesce_usecs_high != vsi->int_rate_limit) {
netif_info(pf, drv, netdev, "tx-usecs-high is not used, please program rx-usecs-high\n");
return -EINVAL;
}
if (ec->rx_coalesce_usecs_high > INTRL_REG_TO_USEC(I40E_MAX_INTRL)) {
netif_info(pf, drv, netdev, "Invalid value, rx-usecs-high range is 0-%lu\n",
INTRL_REG_TO_USEC(I40E_MAX_INTRL));
return -EINVAL;
}
if (ec->rx_coalesce_usecs != cur_rx_itr &&
ec->use_adaptive_rx_coalesce) {
netif_info(pf, drv, netdev, "RX interrupt moderation cannot be changed if adaptive-rx is enabled.\n");
return -EINVAL;
}
if (ec->rx_coalesce_usecs > I40E_MAX_ITR) {
netif_info(pf, drv, netdev, "Invalid value, rx-usecs range is 0-8160\n");
return -EINVAL;
}
if (ec->tx_coalesce_usecs != cur_tx_itr &&
ec->use_adaptive_tx_coalesce) {
netif_info(pf, drv, netdev, "TX interrupt moderation cannot be changed if adaptive-tx is enabled.\n");
return -EINVAL;
}
if (ec->tx_coalesce_usecs > I40E_MAX_ITR) {
netif_info(pf, drv, netdev, "Invalid value, tx-usecs range is 0-8160\n");
return -EINVAL;
}
if (ec->use_adaptive_rx_coalesce && !cur_rx_itr)
ec->rx_coalesce_usecs = I40E_MIN_ITR;
if (ec->use_adaptive_tx_coalesce && !cur_tx_itr)
ec->tx_coalesce_usecs = I40E_MIN_ITR;
intrl_reg = i40e_intrl_usec_to_reg(ec->rx_coalesce_usecs_high);
vsi->int_rate_limit = INTRL_REG_TO_USEC(intrl_reg);
if (vsi->int_rate_limit != ec->rx_coalesce_usecs_high) {
netif_info(pf, drv, netdev, "Interrupt rate limit rounded down to %d\n",
vsi->int_rate_limit);
}
/* rx and tx usecs has per queue value. If user doesn't specify the
* queue, apply to all queues.
*/
if (queue < 0) {
for (i = 0; i < vsi->num_queue_pairs; i++)
i40e_set_itr_per_queue(vsi, ec, i);
} else {
i40e_set_itr_per_queue(vsi, ec, queue);
}
return 0;
}
/**
* i40e_set_coalesce - set coalesce settings for every queue on the netdev
* @netdev: the netdev to change