162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/**************************************************************************** 362306a36Sopenharmony_ci * Driver for Solarflare network controllers and boards 462306a36Sopenharmony_ci * Copyright 2005-2006 Fen Systems Ltd. 562306a36Sopenharmony_ci * Copyright 2006-2013 Solarflare Communications Inc. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/netdevice.h> 962306a36Sopenharmony_ci#include <linux/ethtool.h> 1062306a36Sopenharmony_ci#include <linux/rtnetlink.h> 1162306a36Sopenharmony_ci#include <linux/in.h> 1262306a36Sopenharmony_ci#include "net_driver.h" 1362306a36Sopenharmony_ci#include "workarounds.h" 1462306a36Sopenharmony_ci#include "selftest.h" 1562306a36Sopenharmony_ci#include "efx.h" 1662306a36Sopenharmony_ci#include "efx_channels.h" 1762306a36Sopenharmony_ci#include "rx_common.h" 1862306a36Sopenharmony_ci#include "tx_common.h" 1962306a36Sopenharmony_ci#include "ethtool_common.h" 2062306a36Sopenharmony_ci#include "filter.h" 2162306a36Sopenharmony_ci#include "nic.h" 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#define EFX_ETHTOOL_EEPROM_MAGIC 0xEFAB 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci/************************************************************************** 2662306a36Sopenharmony_ci * 2762306a36Sopenharmony_ci * Ethtool operations 2862306a36Sopenharmony_ci * 2962306a36Sopenharmony_ci ************************************************************************** 3062306a36Sopenharmony_ci */ 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci/* Identify device by flashing LEDs */ 3362306a36Sopenharmony_cistatic int efx_ethtool_phys_id(struct net_device *net_dev, 3462306a36Sopenharmony_ci enum ethtool_phys_id_state state) 3562306a36Sopenharmony_ci{ 3662306a36Sopenharmony_ci struct efx_nic *efx = netdev_priv(net_dev); 3762306a36Sopenharmony_ci enum efx_led_mode mode = EFX_LED_DEFAULT; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci switch (state) { 4062306a36Sopenharmony_ci case ETHTOOL_ID_ON: 4162306a36Sopenharmony_ci mode = EFX_LED_ON; 4262306a36Sopenharmony_ci break; 4362306a36Sopenharmony_ci case ETHTOOL_ID_OFF: 4462306a36Sopenharmony_ci mode = EFX_LED_OFF; 4562306a36Sopenharmony_ci break; 4662306a36Sopenharmony_ci case ETHTOOL_ID_INACTIVE: 4762306a36Sopenharmony_ci mode = EFX_LED_DEFAULT; 4862306a36Sopenharmony_ci break; 4962306a36Sopenharmony_ci case ETHTOOL_ID_ACTIVE: 5062306a36Sopenharmony_ci return 1; /* cycle on/off once per second */ 5162306a36Sopenharmony_ci } 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci return efx_siena_mcdi_set_id_led(efx, mode); 5462306a36Sopenharmony_ci} 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cistatic int efx_ethtool_get_regs_len(struct net_device *net_dev) 5762306a36Sopenharmony_ci{ 5862306a36Sopenharmony_ci return efx_siena_get_regs_len(netdev_priv(net_dev)); 5962306a36Sopenharmony_ci} 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistatic void efx_ethtool_get_regs(struct net_device *net_dev, 6262306a36Sopenharmony_ci struct ethtool_regs *regs, void *buf) 6362306a36Sopenharmony_ci{ 6462306a36Sopenharmony_ci struct efx_nic *efx = netdev_priv(net_dev); 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci regs->version = efx->type->revision; 6762306a36Sopenharmony_ci efx_siena_get_regs(efx, buf); 6862306a36Sopenharmony_ci} 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci/* 7162306a36Sopenharmony_ci * Each channel has a single IRQ and moderation timer, started by any 7262306a36Sopenharmony_ci * completion (or other event). Unless the module parameter 7362306a36Sopenharmony_ci * separate_tx_channels is set, IRQs and moderation are therefore 7462306a36Sopenharmony_ci * shared between RX and TX completions. In this case, when RX IRQ 7562306a36Sopenharmony_ci * moderation is explicitly changed then TX IRQ moderation is 7662306a36Sopenharmony_ci * automatically changed too, but otherwise we fail if the two values 7762306a36Sopenharmony_ci * are requested to be different. 7862306a36Sopenharmony_ci * 7962306a36Sopenharmony_ci * The hardware does not support a limit on the number of completions 8062306a36Sopenharmony_ci * before an IRQ, so we do not use the max_frames fields. We should 8162306a36Sopenharmony_ci * report and require that max_frames == (usecs != 0), but this would 8262306a36Sopenharmony_ci * invalidate existing user documentation. 8362306a36Sopenharmony_ci * 8462306a36Sopenharmony_ci * The hardware does not have distinct settings for interrupt 8562306a36Sopenharmony_ci * moderation while the previous IRQ is being handled, so we should 8662306a36Sopenharmony_ci * not use the 'irq' fields. However, an earlier developer 8762306a36Sopenharmony_ci * misunderstood the meaning of the 'irq' fields and the driver did 8862306a36Sopenharmony_ci * not support the standard fields. To avoid invalidating existing 8962306a36Sopenharmony_ci * user documentation, we report and accept changes through either the 9062306a36Sopenharmony_ci * standard or 'irq' fields. If both are changed at the same time, we 9162306a36Sopenharmony_ci * prefer the standard field. 9262306a36Sopenharmony_ci * 9362306a36Sopenharmony_ci * We implement adaptive IRQ moderation, but use a different algorithm 9462306a36Sopenharmony_ci * from that assumed in the definition of struct ethtool_coalesce. 9562306a36Sopenharmony_ci * Therefore we do not use any of the adaptive moderation parameters 9662306a36Sopenharmony_ci * in it. 9762306a36Sopenharmony_ci */ 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistatic int efx_ethtool_get_coalesce(struct net_device *net_dev, 10062306a36Sopenharmony_ci struct ethtool_coalesce *coalesce, 10162306a36Sopenharmony_ci struct kernel_ethtool_coalesce *kernel_coal, 10262306a36Sopenharmony_ci struct netlink_ext_ack *extack) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci struct efx_nic *efx = netdev_priv(net_dev); 10562306a36Sopenharmony_ci unsigned int tx_usecs, rx_usecs; 10662306a36Sopenharmony_ci bool rx_adaptive; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci efx_siena_get_irq_moderation(efx, &tx_usecs, &rx_usecs, &rx_adaptive); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci coalesce->tx_coalesce_usecs = tx_usecs; 11162306a36Sopenharmony_ci coalesce->tx_coalesce_usecs_irq = tx_usecs; 11262306a36Sopenharmony_ci coalesce->rx_coalesce_usecs = rx_usecs; 11362306a36Sopenharmony_ci coalesce->rx_coalesce_usecs_irq = rx_usecs; 11462306a36Sopenharmony_ci coalesce->use_adaptive_rx_coalesce = rx_adaptive; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci return 0; 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistatic int efx_ethtool_set_coalesce(struct net_device *net_dev, 12062306a36Sopenharmony_ci struct ethtool_coalesce *coalesce, 12162306a36Sopenharmony_ci struct kernel_ethtool_coalesce *kernel_coal, 12262306a36Sopenharmony_ci struct netlink_ext_ack *extack) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci struct efx_nic *efx = netdev_priv(net_dev); 12562306a36Sopenharmony_ci struct efx_channel *channel; 12662306a36Sopenharmony_ci unsigned int tx_usecs, rx_usecs; 12762306a36Sopenharmony_ci bool adaptive, rx_may_override_tx; 12862306a36Sopenharmony_ci int rc; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci efx_siena_get_irq_moderation(efx, &tx_usecs, &rx_usecs, &adaptive); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci if (coalesce->rx_coalesce_usecs != rx_usecs) 13362306a36Sopenharmony_ci rx_usecs = coalesce->rx_coalesce_usecs; 13462306a36Sopenharmony_ci else 13562306a36Sopenharmony_ci rx_usecs = coalesce->rx_coalesce_usecs_irq; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci adaptive = coalesce->use_adaptive_rx_coalesce; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci /* If channels are shared, TX IRQ moderation can be quietly 14062306a36Sopenharmony_ci * overridden unless it is changed from its old value. 14162306a36Sopenharmony_ci */ 14262306a36Sopenharmony_ci rx_may_override_tx = (coalesce->tx_coalesce_usecs == tx_usecs && 14362306a36Sopenharmony_ci coalesce->tx_coalesce_usecs_irq == tx_usecs); 14462306a36Sopenharmony_ci if (coalesce->tx_coalesce_usecs != tx_usecs) 14562306a36Sopenharmony_ci tx_usecs = coalesce->tx_coalesce_usecs; 14662306a36Sopenharmony_ci else 14762306a36Sopenharmony_ci tx_usecs = coalesce->tx_coalesce_usecs_irq; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci rc = efx_siena_init_irq_moderation(efx, tx_usecs, rx_usecs, adaptive, 15062306a36Sopenharmony_ci rx_may_override_tx); 15162306a36Sopenharmony_ci if (rc != 0) 15262306a36Sopenharmony_ci return rc; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci efx_for_each_channel(channel, efx) 15562306a36Sopenharmony_ci efx->type->push_irq_moderation(channel); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci return 0; 15862306a36Sopenharmony_ci} 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_cistatic void 16162306a36Sopenharmony_ciefx_ethtool_get_ringparam(struct net_device *net_dev, 16262306a36Sopenharmony_ci struct ethtool_ringparam *ring, 16362306a36Sopenharmony_ci struct kernel_ethtool_ringparam *kernel_ring, 16462306a36Sopenharmony_ci struct netlink_ext_ack *extack) 16562306a36Sopenharmony_ci{ 16662306a36Sopenharmony_ci struct efx_nic *efx = netdev_priv(net_dev); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci ring->rx_max_pending = EFX_MAX_DMAQ_SIZE; 16962306a36Sopenharmony_ci ring->tx_max_pending = EFX_TXQ_MAX_ENT(efx); 17062306a36Sopenharmony_ci ring->rx_pending = efx->rxq_entries; 17162306a36Sopenharmony_ci ring->tx_pending = efx->txq_entries; 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_cistatic int 17562306a36Sopenharmony_ciefx_ethtool_set_ringparam(struct net_device *net_dev, 17662306a36Sopenharmony_ci struct ethtool_ringparam *ring, 17762306a36Sopenharmony_ci struct kernel_ethtool_ringparam *kernel_ring, 17862306a36Sopenharmony_ci struct netlink_ext_ack *extack) 17962306a36Sopenharmony_ci{ 18062306a36Sopenharmony_ci struct efx_nic *efx = netdev_priv(net_dev); 18162306a36Sopenharmony_ci u32 txq_entries; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci if (ring->rx_mini_pending || ring->rx_jumbo_pending || 18462306a36Sopenharmony_ci ring->rx_pending > EFX_MAX_DMAQ_SIZE || 18562306a36Sopenharmony_ci ring->tx_pending > EFX_TXQ_MAX_ENT(efx)) 18662306a36Sopenharmony_ci return -EINVAL; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci if (ring->rx_pending < EFX_RXQ_MIN_ENT) { 18962306a36Sopenharmony_ci netif_err(efx, drv, efx->net_dev, 19062306a36Sopenharmony_ci "RX queues cannot be smaller than %u\n", 19162306a36Sopenharmony_ci EFX_RXQ_MIN_ENT); 19262306a36Sopenharmony_ci return -EINVAL; 19362306a36Sopenharmony_ci } 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci txq_entries = max(ring->tx_pending, EFX_TXQ_MIN_ENT(efx)); 19662306a36Sopenharmony_ci if (txq_entries != ring->tx_pending) 19762306a36Sopenharmony_ci netif_warn(efx, drv, efx->net_dev, 19862306a36Sopenharmony_ci "increasing TX queue size to minimum of %u\n", 19962306a36Sopenharmony_ci txq_entries); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci return efx_siena_realloc_channels(efx, ring->rx_pending, txq_entries); 20262306a36Sopenharmony_ci} 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_cistatic void efx_ethtool_get_wol(struct net_device *net_dev, 20562306a36Sopenharmony_ci struct ethtool_wolinfo *wol) 20662306a36Sopenharmony_ci{ 20762306a36Sopenharmony_ci struct efx_nic *efx = netdev_priv(net_dev); 20862306a36Sopenharmony_ci return efx->type->get_wol(efx, wol); 20962306a36Sopenharmony_ci} 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_cistatic int efx_ethtool_set_wol(struct net_device *net_dev, 21362306a36Sopenharmony_ci struct ethtool_wolinfo *wol) 21462306a36Sopenharmony_ci{ 21562306a36Sopenharmony_ci struct efx_nic *efx = netdev_priv(net_dev); 21662306a36Sopenharmony_ci return efx->type->set_wol(efx, wol->wolopts); 21762306a36Sopenharmony_ci} 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_cistatic void efx_ethtool_get_fec_stats(struct net_device *net_dev, 22062306a36Sopenharmony_ci struct ethtool_fec_stats *fec_stats) 22162306a36Sopenharmony_ci{ 22262306a36Sopenharmony_ci struct efx_nic *efx = netdev_priv(net_dev); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci if (efx->type->get_fec_stats) 22562306a36Sopenharmony_ci efx->type->get_fec_stats(efx, fec_stats); 22662306a36Sopenharmony_ci} 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_cistatic int efx_ethtool_get_ts_info(struct net_device *net_dev, 22962306a36Sopenharmony_ci struct ethtool_ts_info *ts_info) 23062306a36Sopenharmony_ci{ 23162306a36Sopenharmony_ci struct efx_nic *efx = netdev_priv(net_dev); 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci /* Software capabilities */ 23462306a36Sopenharmony_ci ts_info->so_timestamping = (SOF_TIMESTAMPING_RX_SOFTWARE | 23562306a36Sopenharmony_ci SOF_TIMESTAMPING_SOFTWARE); 23662306a36Sopenharmony_ci ts_info->phc_index = -1; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci efx_siena_ptp_get_ts_info(efx, ts_info); 23962306a36Sopenharmony_ci return 0; 24062306a36Sopenharmony_ci} 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ciconst struct ethtool_ops efx_siena_ethtool_ops = { 24362306a36Sopenharmony_ci .supported_coalesce_params = ETHTOOL_COALESCE_USECS | 24462306a36Sopenharmony_ci ETHTOOL_COALESCE_USECS_IRQ | 24562306a36Sopenharmony_ci ETHTOOL_COALESCE_USE_ADAPTIVE_RX, 24662306a36Sopenharmony_ci .get_drvinfo = efx_siena_ethtool_get_drvinfo, 24762306a36Sopenharmony_ci .get_regs_len = efx_ethtool_get_regs_len, 24862306a36Sopenharmony_ci .get_regs = efx_ethtool_get_regs, 24962306a36Sopenharmony_ci .get_msglevel = efx_siena_ethtool_get_msglevel, 25062306a36Sopenharmony_ci .set_msglevel = efx_siena_ethtool_set_msglevel, 25162306a36Sopenharmony_ci .get_link = ethtool_op_get_link, 25262306a36Sopenharmony_ci .get_coalesce = efx_ethtool_get_coalesce, 25362306a36Sopenharmony_ci .set_coalesce = efx_ethtool_set_coalesce, 25462306a36Sopenharmony_ci .get_ringparam = efx_ethtool_get_ringparam, 25562306a36Sopenharmony_ci .set_ringparam = efx_ethtool_set_ringparam, 25662306a36Sopenharmony_ci .get_pauseparam = efx_siena_ethtool_get_pauseparam, 25762306a36Sopenharmony_ci .set_pauseparam = efx_siena_ethtool_set_pauseparam, 25862306a36Sopenharmony_ci .get_sset_count = efx_siena_ethtool_get_sset_count, 25962306a36Sopenharmony_ci .self_test = efx_siena_ethtool_self_test, 26062306a36Sopenharmony_ci .get_strings = efx_siena_ethtool_get_strings, 26162306a36Sopenharmony_ci .set_phys_id = efx_ethtool_phys_id, 26262306a36Sopenharmony_ci .get_ethtool_stats = efx_siena_ethtool_get_stats, 26362306a36Sopenharmony_ci .get_wol = efx_ethtool_get_wol, 26462306a36Sopenharmony_ci .set_wol = efx_ethtool_set_wol, 26562306a36Sopenharmony_ci .reset = efx_siena_ethtool_reset, 26662306a36Sopenharmony_ci .get_rxnfc = efx_siena_ethtool_get_rxnfc, 26762306a36Sopenharmony_ci .set_rxnfc = efx_siena_ethtool_set_rxnfc, 26862306a36Sopenharmony_ci .get_rxfh_indir_size = efx_siena_ethtool_get_rxfh_indir_size, 26962306a36Sopenharmony_ci .get_rxfh_key_size = efx_siena_ethtool_get_rxfh_key_size, 27062306a36Sopenharmony_ci .get_rxfh = efx_siena_ethtool_get_rxfh, 27162306a36Sopenharmony_ci .set_rxfh = efx_siena_ethtool_set_rxfh, 27262306a36Sopenharmony_ci .get_rxfh_context = efx_siena_ethtool_get_rxfh_context, 27362306a36Sopenharmony_ci .set_rxfh_context = efx_siena_ethtool_set_rxfh_context, 27462306a36Sopenharmony_ci .get_ts_info = efx_ethtool_get_ts_info, 27562306a36Sopenharmony_ci .get_module_info = efx_siena_ethtool_get_module_info, 27662306a36Sopenharmony_ci .get_module_eeprom = efx_siena_ethtool_get_module_eeprom, 27762306a36Sopenharmony_ci .get_link_ksettings = efx_siena_ethtool_get_link_ksettings, 27862306a36Sopenharmony_ci .set_link_ksettings = efx_siena_ethtool_set_link_ksettings, 27962306a36Sopenharmony_ci .get_fec_stats = efx_ethtool_get_fec_stats, 28062306a36Sopenharmony_ci .get_fecparam = efx_siena_ethtool_get_fecparam, 28162306a36Sopenharmony_ci .set_fecparam = efx_siena_ethtool_set_fecparam, 28262306a36Sopenharmony_ci}; 283