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