162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/* Copyright(c) 2013 - 2019 Intel Corporation. */
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci#include <linux/ethtool.h>
562306a36Sopenharmony_ci#include <linux/vmalloc.h>
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include "fm10k.h"
862306a36Sopenharmony_ci
962306a36Sopenharmony_cistruct fm10k_stats {
1062306a36Sopenharmony_ci	/* The stat_string is expected to be a format string formatted using
1162306a36Sopenharmony_ci	 * vsnprintf by fm10k_add_stat_strings. Every member of a stats array
1262306a36Sopenharmony_ci	 * should use the same format specifiers as they will be formatted
1362306a36Sopenharmony_ci	 * using the same variadic arguments.
1462306a36Sopenharmony_ci	 */
1562306a36Sopenharmony_ci	char stat_string[ETH_GSTRING_LEN];
1662306a36Sopenharmony_ci	int sizeof_stat;
1762306a36Sopenharmony_ci	int stat_offset;
1862306a36Sopenharmony_ci};
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#define FM10K_STAT_FIELDS(_type, _name, _stat) { \
2162306a36Sopenharmony_ci	.stat_string = _name, \
2262306a36Sopenharmony_ci	.sizeof_stat = sizeof_field(_type, _stat), \
2362306a36Sopenharmony_ci	.stat_offset = offsetof(_type, _stat) \
2462306a36Sopenharmony_ci}
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci/* netdevice statistics */
2762306a36Sopenharmony_ci#define FM10K_NETDEV_STAT(_net_stat) \
2862306a36Sopenharmony_ci	FM10K_STAT_FIELDS(struct net_device_stats, __stringify(_net_stat), \
2962306a36Sopenharmony_ci			  _net_stat)
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cistatic const struct fm10k_stats fm10k_gstrings_net_stats[] = {
3262306a36Sopenharmony_ci	FM10K_NETDEV_STAT(tx_packets),
3362306a36Sopenharmony_ci	FM10K_NETDEV_STAT(tx_bytes),
3462306a36Sopenharmony_ci	FM10K_NETDEV_STAT(tx_errors),
3562306a36Sopenharmony_ci	FM10K_NETDEV_STAT(rx_packets),
3662306a36Sopenharmony_ci	FM10K_NETDEV_STAT(rx_bytes),
3762306a36Sopenharmony_ci	FM10K_NETDEV_STAT(rx_errors),
3862306a36Sopenharmony_ci	FM10K_NETDEV_STAT(rx_dropped),
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci	/* detailed Rx errors */
4162306a36Sopenharmony_ci	FM10K_NETDEV_STAT(rx_length_errors),
4262306a36Sopenharmony_ci	FM10K_NETDEV_STAT(rx_crc_errors),
4362306a36Sopenharmony_ci	FM10K_NETDEV_STAT(rx_fifo_errors),
4462306a36Sopenharmony_ci};
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci#define FM10K_NETDEV_STATS_LEN	ARRAY_SIZE(fm10k_gstrings_net_stats)
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci/* General interface statistics */
4962306a36Sopenharmony_ci#define FM10K_STAT(_name, _stat) \
5062306a36Sopenharmony_ci	FM10K_STAT_FIELDS(struct fm10k_intfc, _name, _stat)
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_cistatic const struct fm10k_stats fm10k_gstrings_global_stats[] = {
5362306a36Sopenharmony_ci	FM10K_STAT("tx_restart_queue", restart_queue),
5462306a36Sopenharmony_ci	FM10K_STAT("tx_busy", tx_busy),
5562306a36Sopenharmony_ci	FM10K_STAT("tx_csum_errors", tx_csum_errors),
5662306a36Sopenharmony_ci	FM10K_STAT("rx_alloc_failed", alloc_failed),
5762306a36Sopenharmony_ci	FM10K_STAT("rx_csum_errors", rx_csum_errors),
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	FM10K_STAT("tx_packets_nic", tx_packets_nic),
6062306a36Sopenharmony_ci	FM10K_STAT("tx_bytes_nic", tx_bytes_nic),
6162306a36Sopenharmony_ci	FM10K_STAT("rx_packets_nic", rx_packets_nic),
6262306a36Sopenharmony_ci	FM10K_STAT("rx_bytes_nic", rx_bytes_nic),
6362306a36Sopenharmony_ci	FM10K_STAT("rx_drops_nic", rx_drops_nic),
6462306a36Sopenharmony_ci	FM10K_STAT("rx_overrun_pf", rx_overrun_pf),
6562306a36Sopenharmony_ci	FM10K_STAT("rx_overrun_vf", rx_overrun_vf),
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	FM10K_STAT("swapi_status", hw.swapi.status),
6862306a36Sopenharmony_ci	FM10K_STAT("mac_rules_used", hw.swapi.mac.used),
6962306a36Sopenharmony_ci	FM10K_STAT("mac_rules_avail", hw.swapi.mac.avail),
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	FM10K_STAT("reset_while_pending", hw.mac.reset_while_pending),
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	FM10K_STAT("tx_hang_count", tx_timeout_count),
7462306a36Sopenharmony_ci};
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistatic const struct fm10k_stats fm10k_gstrings_pf_stats[] = {
7762306a36Sopenharmony_ci	FM10K_STAT("timeout", stats.timeout.count),
7862306a36Sopenharmony_ci	FM10K_STAT("ur", stats.ur.count),
7962306a36Sopenharmony_ci	FM10K_STAT("ca", stats.ca.count),
8062306a36Sopenharmony_ci	FM10K_STAT("um", stats.um.count),
8162306a36Sopenharmony_ci	FM10K_STAT("xec", stats.xec.count),
8262306a36Sopenharmony_ci	FM10K_STAT("vlan_drop", stats.vlan_drop.count),
8362306a36Sopenharmony_ci	FM10K_STAT("loopback_drop", stats.loopback_drop.count),
8462306a36Sopenharmony_ci	FM10K_STAT("nodesc_drop", stats.nodesc_drop.count),
8562306a36Sopenharmony_ci};
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci/* mailbox statistics */
8862306a36Sopenharmony_ci#define FM10K_MBX_STAT(_name, _stat) \
8962306a36Sopenharmony_ci	FM10K_STAT_FIELDS(struct fm10k_mbx_info, _name, _stat)
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_cistatic const struct fm10k_stats fm10k_gstrings_mbx_stats[] = {
9262306a36Sopenharmony_ci	FM10K_MBX_STAT("mbx_tx_busy", tx_busy),
9362306a36Sopenharmony_ci	FM10K_MBX_STAT("mbx_tx_dropped", tx_dropped),
9462306a36Sopenharmony_ci	FM10K_MBX_STAT("mbx_tx_messages", tx_messages),
9562306a36Sopenharmony_ci	FM10K_MBX_STAT("mbx_tx_dwords", tx_dwords),
9662306a36Sopenharmony_ci	FM10K_MBX_STAT("mbx_tx_mbmem_pulled", tx_mbmem_pulled),
9762306a36Sopenharmony_ci	FM10K_MBX_STAT("mbx_rx_messages", rx_messages),
9862306a36Sopenharmony_ci	FM10K_MBX_STAT("mbx_rx_dwords", rx_dwords),
9962306a36Sopenharmony_ci	FM10K_MBX_STAT("mbx_rx_parse_err", rx_parse_err),
10062306a36Sopenharmony_ci	FM10K_MBX_STAT("mbx_rx_mbmem_pushed", rx_mbmem_pushed),
10162306a36Sopenharmony_ci};
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci/* per-queue ring statistics */
10462306a36Sopenharmony_ci#define FM10K_QUEUE_STAT(_name, _stat) \
10562306a36Sopenharmony_ci	FM10K_STAT_FIELDS(struct fm10k_ring, _name, _stat)
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_cistatic const struct fm10k_stats fm10k_gstrings_queue_stats[] = {
10862306a36Sopenharmony_ci	FM10K_QUEUE_STAT("%s_queue_%u_packets", stats.packets),
10962306a36Sopenharmony_ci	FM10K_QUEUE_STAT("%s_queue_%u_bytes", stats.bytes),
11062306a36Sopenharmony_ci};
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci#define FM10K_GLOBAL_STATS_LEN ARRAY_SIZE(fm10k_gstrings_global_stats)
11362306a36Sopenharmony_ci#define FM10K_PF_STATS_LEN ARRAY_SIZE(fm10k_gstrings_pf_stats)
11462306a36Sopenharmony_ci#define FM10K_MBX_STATS_LEN ARRAY_SIZE(fm10k_gstrings_mbx_stats)
11562306a36Sopenharmony_ci#define FM10K_QUEUE_STATS_LEN ARRAY_SIZE(fm10k_gstrings_queue_stats)
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci#define FM10K_STATIC_STATS_LEN (FM10K_GLOBAL_STATS_LEN + \
11862306a36Sopenharmony_ci				FM10K_NETDEV_STATS_LEN + \
11962306a36Sopenharmony_ci				FM10K_MBX_STATS_LEN)
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_cistatic const char fm10k_gstrings_test[][ETH_GSTRING_LEN] = {
12262306a36Sopenharmony_ci	"Mailbox test (on/offline)"
12362306a36Sopenharmony_ci};
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci#define FM10K_TEST_LEN (sizeof(fm10k_gstrings_test) / ETH_GSTRING_LEN)
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_cienum fm10k_self_test_types {
12862306a36Sopenharmony_ci	FM10K_TEST_MBX,
12962306a36Sopenharmony_ci	FM10K_TEST_MAX = FM10K_TEST_LEN
13062306a36Sopenharmony_ci};
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_cienum {
13362306a36Sopenharmony_ci	FM10K_PRV_FLAG_LEN,
13462306a36Sopenharmony_ci};
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_cistatic const char fm10k_prv_flags[FM10K_PRV_FLAG_LEN][ETH_GSTRING_LEN] = {
13762306a36Sopenharmony_ci};
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_cistatic void __fm10k_add_stat_strings(u8 **p, const struct fm10k_stats stats[],
14062306a36Sopenharmony_ci				     const unsigned int size, ...)
14162306a36Sopenharmony_ci{
14262306a36Sopenharmony_ci	unsigned int i;
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	for (i = 0; i < size; i++) {
14562306a36Sopenharmony_ci		va_list args;
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci		va_start(args, size);
14862306a36Sopenharmony_ci		vsnprintf(*p, ETH_GSTRING_LEN, stats[i].stat_string, args);
14962306a36Sopenharmony_ci		*p += ETH_GSTRING_LEN;
15062306a36Sopenharmony_ci		va_end(args);
15162306a36Sopenharmony_ci	}
15262306a36Sopenharmony_ci}
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci#define fm10k_add_stat_strings(p, stats, ...) \
15562306a36Sopenharmony_ci	__fm10k_add_stat_strings(p, stats, ARRAY_SIZE(stats), ## __VA_ARGS__)
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_cistatic void fm10k_get_stat_strings(struct net_device *dev, u8 *data)
15862306a36Sopenharmony_ci{
15962306a36Sopenharmony_ci	struct fm10k_intfc *interface = netdev_priv(dev);
16062306a36Sopenharmony_ci	unsigned int i;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	fm10k_add_stat_strings(&data, fm10k_gstrings_net_stats);
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	fm10k_add_stat_strings(&data, fm10k_gstrings_global_stats);
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	fm10k_add_stat_strings(&data, fm10k_gstrings_mbx_stats);
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	if (interface->hw.mac.type != fm10k_mac_vf)
16962306a36Sopenharmony_ci		fm10k_add_stat_strings(&data, fm10k_gstrings_pf_stats);
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	for (i = 0; i < interface->hw.mac.max_queues; i++) {
17262306a36Sopenharmony_ci		fm10k_add_stat_strings(&data, fm10k_gstrings_queue_stats,
17362306a36Sopenharmony_ci				       "tx", i);
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci		fm10k_add_stat_strings(&data, fm10k_gstrings_queue_stats,
17662306a36Sopenharmony_ci				       "rx", i);
17762306a36Sopenharmony_ci	}
17862306a36Sopenharmony_ci}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_cistatic void fm10k_get_strings(struct net_device *dev,
18162306a36Sopenharmony_ci			      u32 stringset, u8 *data)
18262306a36Sopenharmony_ci{
18362306a36Sopenharmony_ci	switch (stringset) {
18462306a36Sopenharmony_ci	case ETH_SS_TEST:
18562306a36Sopenharmony_ci		memcpy(data, fm10k_gstrings_test,
18662306a36Sopenharmony_ci		       FM10K_TEST_LEN * ETH_GSTRING_LEN);
18762306a36Sopenharmony_ci		break;
18862306a36Sopenharmony_ci	case ETH_SS_STATS:
18962306a36Sopenharmony_ci		fm10k_get_stat_strings(dev, data);
19062306a36Sopenharmony_ci		break;
19162306a36Sopenharmony_ci	case ETH_SS_PRIV_FLAGS:
19262306a36Sopenharmony_ci		memcpy(data, fm10k_prv_flags,
19362306a36Sopenharmony_ci		       FM10K_PRV_FLAG_LEN * ETH_GSTRING_LEN);
19462306a36Sopenharmony_ci		break;
19562306a36Sopenharmony_ci	}
19662306a36Sopenharmony_ci}
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_cistatic int fm10k_get_sset_count(struct net_device *dev, int sset)
19962306a36Sopenharmony_ci{
20062306a36Sopenharmony_ci	struct fm10k_intfc *interface = netdev_priv(dev);
20162306a36Sopenharmony_ci	struct fm10k_hw *hw = &interface->hw;
20262306a36Sopenharmony_ci	int stats_len = FM10K_STATIC_STATS_LEN;
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	switch (sset) {
20562306a36Sopenharmony_ci	case ETH_SS_TEST:
20662306a36Sopenharmony_ci		return FM10K_TEST_LEN;
20762306a36Sopenharmony_ci	case ETH_SS_STATS:
20862306a36Sopenharmony_ci		stats_len += hw->mac.max_queues * 2 * FM10K_QUEUE_STATS_LEN;
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci		if (hw->mac.type != fm10k_mac_vf)
21162306a36Sopenharmony_ci			stats_len += FM10K_PF_STATS_LEN;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci		return stats_len;
21462306a36Sopenharmony_ci	case ETH_SS_PRIV_FLAGS:
21562306a36Sopenharmony_ci		return FM10K_PRV_FLAG_LEN;
21662306a36Sopenharmony_ci	default:
21762306a36Sopenharmony_ci		return -EOPNOTSUPP;
21862306a36Sopenharmony_ci	}
21962306a36Sopenharmony_ci}
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_cistatic void __fm10k_add_ethtool_stats(u64 **data, void *pointer,
22262306a36Sopenharmony_ci				      const struct fm10k_stats stats[],
22362306a36Sopenharmony_ci				      const unsigned int size)
22462306a36Sopenharmony_ci{
22562306a36Sopenharmony_ci	unsigned int i;
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	if (!pointer) {
22862306a36Sopenharmony_ci		/* memory is not zero allocated so we have to clear it */
22962306a36Sopenharmony_ci		for (i = 0; i < size; i++)
23062306a36Sopenharmony_ci			*((*data)++) = 0;
23162306a36Sopenharmony_ci		return;
23262306a36Sopenharmony_ci	}
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	for (i = 0; i < size; i++) {
23562306a36Sopenharmony_ci		char *p = (char *)pointer + stats[i].stat_offset;
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci		switch (stats[i].sizeof_stat) {
23862306a36Sopenharmony_ci		case sizeof(u64):
23962306a36Sopenharmony_ci			*((*data)++) = *(u64 *)p;
24062306a36Sopenharmony_ci			break;
24162306a36Sopenharmony_ci		case sizeof(u32):
24262306a36Sopenharmony_ci			*((*data)++) = *(u32 *)p;
24362306a36Sopenharmony_ci			break;
24462306a36Sopenharmony_ci		case sizeof(u16):
24562306a36Sopenharmony_ci			*((*data)++) = *(u16 *)p;
24662306a36Sopenharmony_ci			break;
24762306a36Sopenharmony_ci		case sizeof(u8):
24862306a36Sopenharmony_ci			*((*data)++) = *(u8 *)p;
24962306a36Sopenharmony_ci			break;
25062306a36Sopenharmony_ci		default:
25162306a36Sopenharmony_ci			WARN_ONCE(1, "unexpected stat size for %s",
25262306a36Sopenharmony_ci				  stats[i].stat_string);
25362306a36Sopenharmony_ci			*((*data)++) = 0;
25462306a36Sopenharmony_ci		}
25562306a36Sopenharmony_ci	}
25662306a36Sopenharmony_ci}
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci#define fm10k_add_ethtool_stats(data, pointer, stats) \
25962306a36Sopenharmony_ci	__fm10k_add_ethtool_stats(data, pointer, stats, ARRAY_SIZE(stats))
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_cistatic void fm10k_get_ethtool_stats(struct net_device *netdev,
26262306a36Sopenharmony_ci				    struct ethtool_stats __always_unused *stats,
26362306a36Sopenharmony_ci				    u64 *data)
26462306a36Sopenharmony_ci{
26562306a36Sopenharmony_ci	struct fm10k_intfc *interface = netdev_priv(netdev);
26662306a36Sopenharmony_ci	struct net_device_stats *net_stats = &netdev->stats;
26762306a36Sopenharmony_ci	int i;
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	fm10k_update_stats(interface);
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	fm10k_add_ethtool_stats(&data, net_stats, fm10k_gstrings_net_stats);
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	fm10k_add_ethtool_stats(&data, interface, fm10k_gstrings_global_stats);
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	fm10k_add_ethtool_stats(&data, &interface->hw.mbx,
27662306a36Sopenharmony_ci				fm10k_gstrings_mbx_stats);
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	if (interface->hw.mac.type != fm10k_mac_vf) {
27962306a36Sopenharmony_ci		fm10k_add_ethtool_stats(&data, interface,
28062306a36Sopenharmony_ci					fm10k_gstrings_pf_stats);
28162306a36Sopenharmony_ci	}
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	for (i = 0; i < interface->hw.mac.max_queues; i++) {
28462306a36Sopenharmony_ci		struct fm10k_ring *ring;
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci		ring = interface->tx_ring[i];
28762306a36Sopenharmony_ci		fm10k_add_ethtool_stats(&data, ring,
28862306a36Sopenharmony_ci					fm10k_gstrings_queue_stats);
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci		ring = interface->rx_ring[i];
29162306a36Sopenharmony_ci		fm10k_add_ethtool_stats(&data, ring,
29262306a36Sopenharmony_ci					fm10k_gstrings_queue_stats);
29362306a36Sopenharmony_ci	}
29462306a36Sopenharmony_ci}
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci/* If function below adds more registers this define needs to be updated */
29762306a36Sopenharmony_ci#define FM10K_REGS_LEN_Q 29
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_cistatic void fm10k_get_reg_q(struct fm10k_hw *hw, u32 *buff, int i)
30062306a36Sopenharmony_ci{
30162306a36Sopenharmony_ci	int idx = 0;
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	buff[idx++] = fm10k_read_reg(hw, FM10K_RDBAL(i));
30462306a36Sopenharmony_ci	buff[idx++] = fm10k_read_reg(hw, FM10K_RDBAH(i));
30562306a36Sopenharmony_ci	buff[idx++] = fm10k_read_reg(hw, FM10K_RDLEN(i));
30662306a36Sopenharmony_ci	buff[idx++] = fm10k_read_reg(hw, FM10K_TPH_RXCTRL(i));
30762306a36Sopenharmony_ci	buff[idx++] = fm10k_read_reg(hw, FM10K_RDH(i));
30862306a36Sopenharmony_ci	buff[idx++] = fm10k_read_reg(hw, FM10K_RDT(i));
30962306a36Sopenharmony_ci	buff[idx++] = fm10k_read_reg(hw, FM10K_RXQCTL(i));
31062306a36Sopenharmony_ci	buff[idx++] = fm10k_read_reg(hw, FM10K_RXDCTL(i));
31162306a36Sopenharmony_ci	buff[idx++] = fm10k_read_reg(hw, FM10K_RXINT(i));
31262306a36Sopenharmony_ci	buff[idx++] = fm10k_read_reg(hw, FM10K_SRRCTL(i));
31362306a36Sopenharmony_ci	buff[idx++] = fm10k_read_reg(hw, FM10K_QPRC(i));
31462306a36Sopenharmony_ci	buff[idx++] = fm10k_read_reg(hw, FM10K_QPRDC(i));
31562306a36Sopenharmony_ci	buff[idx++] = fm10k_read_reg(hw, FM10K_QBRC_L(i));
31662306a36Sopenharmony_ci	buff[idx++] = fm10k_read_reg(hw, FM10K_QBRC_H(i));
31762306a36Sopenharmony_ci	buff[idx++] = fm10k_read_reg(hw, FM10K_TDBAL(i));
31862306a36Sopenharmony_ci	buff[idx++] = fm10k_read_reg(hw, FM10K_TDBAH(i));
31962306a36Sopenharmony_ci	buff[idx++] = fm10k_read_reg(hw, FM10K_TDLEN(i));
32062306a36Sopenharmony_ci	buff[idx++] = fm10k_read_reg(hw, FM10K_TPH_TXCTRL(i));
32162306a36Sopenharmony_ci	buff[idx++] = fm10k_read_reg(hw, FM10K_TDH(i));
32262306a36Sopenharmony_ci	buff[idx++] = fm10k_read_reg(hw, FM10K_TDT(i));
32362306a36Sopenharmony_ci	buff[idx++] = fm10k_read_reg(hw, FM10K_TXDCTL(i));
32462306a36Sopenharmony_ci	buff[idx++] = fm10k_read_reg(hw, FM10K_TXQCTL(i));
32562306a36Sopenharmony_ci	buff[idx++] = fm10k_read_reg(hw, FM10K_TXINT(i));
32662306a36Sopenharmony_ci	buff[idx++] = fm10k_read_reg(hw, FM10K_QPTC(i));
32762306a36Sopenharmony_ci	buff[idx++] = fm10k_read_reg(hw, FM10K_QBTC_L(i));
32862306a36Sopenharmony_ci	buff[idx++] = fm10k_read_reg(hw, FM10K_QBTC_H(i));
32962306a36Sopenharmony_ci	buff[idx++] = fm10k_read_reg(hw, FM10K_TQDLOC(i));
33062306a36Sopenharmony_ci	buff[idx++] = fm10k_read_reg(hw, FM10K_TX_SGLORT(i));
33162306a36Sopenharmony_ci	buff[idx++] = fm10k_read_reg(hw, FM10K_PFVTCTL(i));
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	BUG_ON(idx != FM10K_REGS_LEN_Q);
33462306a36Sopenharmony_ci}
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci/* If function above adds more registers this define needs to be updated */
33762306a36Sopenharmony_ci#define FM10K_REGS_LEN_VSI 43
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_cistatic void fm10k_get_reg_vsi(struct fm10k_hw *hw, u32 *buff, int i)
34062306a36Sopenharmony_ci{
34162306a36Sopenharmony_ci	int idx = 0, j;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	buff[idx++] = fm10k_read_reg(hw, FM10K_MRQC(i));
34462306a36Sopenharmony_ci	for (j = 0; j < 10; j++)
34562306a36Sopenharmony_ci		buff[idx++] = fm10k_read_reg(hw, FM10K_RSSRK(i, j));
34662306a36Sopenharmony_ci	for (j = 0; j < 32; j++)
34762306a36Sopenharmony_ci		buff[idx++] = fm10k_read_reg(hw, FM10K_RETA(i, j));
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	BUG_ON(idx != FM10K_REGS_LEN_VSI);
35062306a36Sopenharmony_ci}
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_cistatic void fm10k_get_regs(struct net_device *netdev,
35362306a36Sopenharmony_ci			   struct ethtool_regs *regs, void *p)
35462306a36Sopenharmony_ci{
35562306a36Sopenharmony_ci	struct fm10k_intfc *interface = netdev_priv(netdev);
35662306a36Sopenharmony_ci	struct fm10k_hw *hw = &interface->hw;
35762306a36Sopenharmony_ci	u32 *buff = p;
35862306a36Sopenharmony_ci	u16 i;
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	regs->version = BIT(24) | (hw->revision_id << 16) | hw->device_id;
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	switch (hw->mac.type) {
36362306a36Sopenharmony_ci	case fm10k_mac_pf:
36462306a36Sopenharmony_ci		/* General PF Registers */
36562306a36Sopenharmony_ci		*(buff++) = fm10k_read_reg(hw, FM10K_CTRL);
36662306a36Sopenharmony_ci		*(buff++) = fm10k_read_reg(hw, FM10K_CTRL_EXT);
36762306a36Sopenharmony_ci		*(buff++) = fm10k_read_reg(hw, FM10K_GCR);
36862306a36Sopenharmony_ci		*(buff++) = fm10k_read_reg(hw, FM10K_GCR_EXT);
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci		for (i = 0; i < 8; i++) {
37162306a36Sopenharmony_ci			*(buff++) = fm10k_read_reg(hw, FM10K_DGLORTMAP(i));
37262306a36Sopenharmony_ci			*(buff++) = fm10k_read_reg(hw, FM10K_DGLORTDEC(i));
37362306a36Sopenharmony_ci		}
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci		for (i = 0; i < 65; i++) {
37662306a36Sopenharmony_ci			fm10k_get_reg_vsi(hw, buff, i);
37762306a36Sopenharmony_ci			buff += FM10K_REGS_LEN_VSI;
37862306a36Sopenharmony_ci		}
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci		*(buff++) = fm10k_read_reg(hw, FM10K_DMA_CTRL);
38162306a36Sopenharmony_ci		*(buff++) = fm10k_read_reg(hw, FM10K_DMA_CTRL2);
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci		for (i = 0; i < FM10K_MAX_QUEUES_PF; i++) {
38462306a36Sopenharmony_ci			fm10k_get_reg_q(hw, buff, i);
38562306a36Sopenharmony_ci			buff += FM10K_REGS_LEN_Q;
38662306a36Sopenharmony_ci		}
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci		*(buff++) = fm10k_read_reg(hw, FM10K_TPH_CTRL);
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci		for (i = 0; i < 8; i++)
39162306a36Sopenharmony_ci			*(buff++) = fm10k_read_reg(hw, FM10K_INT_MAP(i));
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci		/* Interrupt Throttling Registers */
39462306a36Sopenharmony_ci		for (i = 0; i < 130; i++)
39562306a36Sopenharmony_ci			*(buff++) = fm10k_read_reg(hw, FM10K_ITR(i));
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci		break;
39862306a36Sopenharmony_ci	case fm10k_mac_vf:
39962306a36Sopenharmony_ci		/* General VF registers */
40062306a36Sopenharmony_ci		*(buff++) = fm10k_read_reg(hw, FM10K_VFCTRL);
40162306a36Sopenharmony_ci		*(buff++) = fm10k_read_reg(hw, FM10K_VFINT_MAP);
40262306a36Sopenharmony_ci		*(buff++) = fm10k_read_reg(hw, FM10K_VFSYSTIME);
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci		/* Interrupt Throttling Registers */
40562306a36Sopenharmony_ci		for (i = 0; i < 8; i++)
40662306a36Sopenharmony_ci			*(buff++) = fm10k_read_reg(hw, FM10K_VFITR(i));
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci		fm10k_get_reg_vsi(hw, buff, 0);
40962306a36Sopenharmony_ci		buff += FM10K_REGS_LEN_VSI;
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci		for (i = 0; i < FM10K_MAX_QUEUES_POOL; i++) {
41262306a36Sopenharmony_ci			if (i < hw->mac.max_queues)
41362306a36Sopenharmony_ci				fm10k_get_reg_q(hw, buff, i);
41462306a36Sopenharmony_ci			else
41562306a36Sopenharmony_ci				memset(buff, 0, sizeof(u32) * FM10K_REGS_LEN_Q);
41662306a36Sopenharmony_ci			buff += FM10K_REGS_LEN_Q;
41762306a36Sopenharmony_ci		}
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci		break;
42062306a36Sopenharmony_ci	default:
42162306a36Sopenharmony_ci		return;
42262306a36Sopenharmony_ci	}
42362306a36Sopenharmony_ci}
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci/* If function above adds more registers these define need to be updated */
42662306a36Sopenharmony_ci#define FM10K_REGS_LEN_PF \
42762306a36Sopenharmony_ci(162 + (65 * FM10K_REGS_LEN_VSI) + (FM10K_MAX_QUEUES_PF * FM10K_REGS_LEN_Q))
42862306a36Sopenharmony_ci#define FM10K_REGS_LEN_VF \
42962306a36Sopenharmony_ci(11 + FM10K_REGS_LEN_VSI + (FM10K_MAX_QUEUES_POOL * FM10K_REGS_LEN_Q))
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_cistatic int fm10k_get_regs_len(struct net_device *netdev)
43262306a36Sopenharmony_ci{
43362306a36Sopenharmony_ci	struct fm10k_intfc *interface = netdev_priv(netdev);
43462306a36Sopenharmony_ci	struct fm10k_hw *hw = &interface->hw;
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	switch (hw->mac.type) {
43762306a36Sopenharmony_ci	case fm10k_mac_pf:
43862306a36Sopenharmony_ci		return FM10K_REGS_LEN_PF * sizeof(u32);
43962306a36Sopenharmony_ci	case fm10k_mac_vf:
44062306a36Sopenharmony_ci		return FM10K_REGS_LEN_VF * sizeof(u32);
44162306a36Sopenharmony_ci	default:
44262306a36Sopenharmony_ci		return 0;
44362306a36Sopenharmony_ci	}
44462306a36Sopenharmony_ci}
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_cistatic void fm10k_get_drvinfo(struct net_device *dev,
44762306a36Sopenharmony_ci			      struct ethtool_drvinfo *info)
44862306a36Sopenharmony_ci{
44962306a36Sopenharmony_ci	struct fm10k_intfc *interface = netdev_priv(dev);
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	strncpy(info->driver, fm10k_driver_name,
45262306a36Sopenharmony_ci		sizeof(info->driver) - 1);
45362306a36Sopenharmony_ci	strncpy(info->bus_info, pci_name(interface->pdev),
45462306a36Sopenharmony_ci		sizeof(info->bus_info) - 1);
45562306a36Sopenharmony_ci}
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_cistatic void fm10k_get_pauseparam(struct net_device *dev,
45862306a36Sopenharmony_ci				 struct ethtool_pauseparam *pause)
45962306a36Sopenharmony_ci{
46062306a36Sopenharmony_ci	struct fm10k_intfc *interface = netdev_priv(dev);
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	/* record fixed values for autoneg and tx pause */
46362306a36Sopenharmony_ci	pause->autoneg = 0;
46462306a36Sopenharmony_ci	pause->tx_pause = 1;
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	pause->rx_pause = interface->rx_pause ? 1 : 0;
46762306a36Sopenharmony_ci}
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_cistatic int fm10k_set_pauseparam(struct net_device *dev,
47062306a36Sopenharmony_ci				struct ethtool_pauseparam *pause)
47162306a36Sopenharmony_ci{
47262306a36Sopenharmony_ci	struct fm10k_intfc *interface = netdev_priv(dev);
47362306a36Sopenharmony_ci	struct fm10k_hw *hw = &interface->hw;
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	if (pause->autoneg || !pause->tx_pause)
47662306a36Sopenharmony_ci		return -EINVAL;
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	/* we can only support pause on the PF to avoid head-of-line blocking */
47962306a36Sopenharmony_ci	if (hw->mac.type == fm10k_mac_pf)
48062306a36Sopenharmony_ci		interface->rx_pause = pause->rx_pause ? ~0 : 0;
48162306a36Sopenharmony_ci	else if (pause->rx_pause)
48262306a36Sopenharmony_ci		return -EINVAL;
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	if (netif_running(dev))
48562306a36Sopenharmony_ci		fm10k_update_rx_drop_en(interface);
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	return 0;
48862306a36Sopenharmony_ci}
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_cistatic u32 fm10k_get_msglevel(struct net_device *netdev)
49162306a36Sopenharmony_ci{
49262306a36Sopenharmony_ci	struct fm10k_intfc *interface = netdev_priv(netdev);
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	return interface->msg_enable;
49562306a36Sopenharmony_ci}
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_cistatic void fm10k_set_msglevel(struct net_device *netdev, u32 data)
49862306a36Sopenharmony_ci{
49962306a36Sopenharmony_ci	struct fm10k_intfc *interface = netdev_priv(netdev);
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci	interface->msg_enable = data;
50262306a36Sopenharmony_ci}
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_cistatic void fm10k_get_ringparam(struct net_device *netdev,
50562306a36Sopenharmony_ci				struct ethtool_ringparam *ring,
50662306a36Sopenharmony_ci				struct kernel_ethtool_ringparam *kernel_ring,
50762306a36Sopenharmony_ci				struct netlink_ext_ack *extack)
50862306a36Sopenharmony_ci{
50962306a36Sopenharmony_ci	struct fm10k_intfc *interface = netdev_priv(netdev);
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	ring->rx_max_pending = FM10K_MAX_RXD;
51262306a36Sopenharmony_ci	ring->tx_max_pending = FM10K_MAX_TXD;
51362306a36Sopenharmony_ci	ring->rx_mini_max_pending = 0;
51462306a36Sopenharmony_ci	ring->rx_jumbo_max_pending = 0;
51562306a36Sopenharmony_ci	ring->rx_pending = interface->rx_ring_count;
51662306a36Sopenharmony_ci	ring->tx_pending = interface->tx_ring_count;
51762306a36Sopenharmony_ci	ring->rx_mini_pending = 0;
51862306a36Sopenharmony_ci	ring->rx_jumbo_pending = 0;
51962306a36Sopenharmony_ci}
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_cistatic int fm10k_set_ringparam(struct net_device *netdev,
52262306a36Sopenharmony_ci			       struct ethtool_ringparam *ring,
52362306a36Sopenharmony_ci			       struct kernel_ethtool_ringparam *kernel_ring,
52462306a36Sopenharmony_ci			       struct netlink_ext_ack *extack)
52562306a36Sopenharmony_ci{
52662306a36Sopenharmony_ci	struct fm10k_intfc *interface = netdev_priv(netdev);
52762306a36Sopenharmony_ci	struct fm10k_ring *temp_ring;
52862306a36Sopenharmony_ci	int i, err = 0;
52962306a36Sopenharmony_ci	u32 new_rx_count, new_tx_count;
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	if ((ring->rx_mini_pending) || (ring->rx_jumbo_pending))
53262306a36Sopenharmony_ci		return -EINVAL;
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	new_tx_count = clamp_t(u32, ring->tx_pending,
53562306a36Sopenharmony_ci			       FM10K_MIN_TXD, FM10K_MAX_TXD);
53662306a36Sopenharmony_ci	new_tx_count = ALIGN(new_tx_count, FM10K_REQ_TX_DESCRIPTOR_MULTIPLE);
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	new_rx_count = clamp_t(u32, ring->rx_pending,
53962306a36Sopenharmony_ci			       FM10K_MIN_RXD, FM10K_MAX_RXD);
54062306a36Sopenharmony_ci	new_rx_count = ALIGN(new_rx_count, FM10K_REQ_RX_DESCRIPTOR_MULTIPLE);
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	if ((new_tx_count == interface->tx_ring_count) &&
54362306a36Sopenharmony_ci	    (new_rx_count == interface->rx_ring_count)) {
54462306a36Sopenharmony_ci		/* nothing to do */
54562306a36Sopenharmony_ci		return 0;
54662306a36Sopenharmony_ci	}
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	while (test_and_set_bit(__FM10K_RESETTING, interface->state))
54962306a36Sopenharmony_ci		usleep_range(1000, 2000);
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci	if (!netif_running(interface->netdev)) {
55262306a36Sopenharmony_ci		for (i = 0; i < interface->num_tx_queues; i++)
55362306a36Sopenharmony_ci			interface->tx_ring[i]->count = new_tx_count;
55462306a36Sopenharmony_ci		for (i = 0; i < interface->num_rx_queues; i++)
55562306a36Sopenharmony_ci			interface->rx_ring[i]->count = new_rx_count;
55662306a36Sopenharmony_ci		interface->tx_ring_count = new_tx_count;
55762306a36Sopenharmony_ci		interface->rx_ring_count = new_rx_count;
55862306a36Sopenharmony_ci		goto clear_reset;
55962306a36Sopenharmony_ci	}
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci	/* allocate temporary buffer to store rings in */
56262306a36Sopenharmony_ci	i = max_t(int, interface->num_tx_queues, interface->num_rx_queues);
56362306a36Sopenharmony_ci	temp_ring = vmalloc(array_size(i, sizeof(struct fm10k_ring)));
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci	if (!temp_ring) {
56662306a36Sopenharmony_ci		err = -ENOMEM;
56762306a36Sopenharmony_ci		goto clear_reset;
56862306a36Sopenharmony_ci	}
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	fm10k_down(interface);
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	/* Setup new Tx resources and free the old Tx resources in that order.
57362306a36Sopenharmony_ci	 * We can then assign the new resources to the rings via a memcpy.
57462306a36Sopenharmony_ci	 * The advantage to this approach is that we are guaranteed to still
57562306a36Sopenharmony_ci	 * have resources even in the case of an allocation failure.
57662306a36Sopenharmony_ci	 */
57762306a36Sopenharmony_ci	if (new_tx_count != interface->tx_ring_count) {
57862306a36Sopenharmony_ci		for (i = 0; i < interface->num_tx_queues; i++) {
57962306a36Sopenharmony_ci			memcpy(&temp_ring[i], interface->tx_ring[i],
58062306a36Sopenharmony_ci			       sizeof(struct fm10k_ring));
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci			temp_ring[i].count = new_tx_count;
58362306a36Sopenharmony_ci			err = fm10k_setup_tx_resources(&temp_ring[i]);
58462306a36Sopenharmony_ci			if (err) {
58562306a36Sopenharmony_ci				while (i) {
58662306a36Sopenharmony_ci					i--;
58762306a36Sopenharmony_ci					fm10k_free_tx_resources(&temp_ring[i]);
58862306a36Sopenharmony_ci				}
58962306a36Sopenharmony_ci				goto err_setup;
59062306a36Sopenharmony_ci			}
59162306a36Sopenharmony_ci		}
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci		for (i = 0; i < interface->num_tx_queues; i++) {
59462306a36Sopenharmony_ci			fm10k_free_tx_resources(interface->tx_ring[i]);
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci			memcpy(interface->tx_ring[i], &temp_ring[i],
59762306a36Sopenharmony_ci			       sizeof(struct fm10k_ring));
59862306a36Sopenharmony_ci		}
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci		interface->tx_ring_count = new_tx_count;
60162306a36Sopenharmony_ci	}
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci	/* Repeat the process for the Rx rings if needed */
60462306a36Sopenharmony_ci	if (new_rx_count != interface->rx_ring_count) {
60562306a36Sopenharmony_ci		for (i = 0; i < interface->num_rx_queues; i++) {
60662306a36Sopenharmony_ci			memcpy(&temp_ring[i], interface->rx_ring[i],
60762306a36Sopenharmony_ci			       sizeof(struct fm10k_ring));
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci			temp_ring[i].count = new_rx_count;
61062306a36Sopenharmony_ci			err = fm10k_setup_rx_resources(&temp_ring[i]);
61162306a36Sopenharmony_ci			if (err) {
61262306a36Sopenharmony_ci				while (i) {
61362306a36Sopenharmony_ci					i--;
61462306a36Sopenharmony_ci					fm10k_free_rx_resources(&temp_ring[i]);
61562306a36Sopenharmony_ci				}
61662306a36Sopenharmony_ci				goto err_setup;
61762306a36Sopenharmony_ci			}
61862306a36Sopenharmony_ci		}
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci		for (i = 0; i < interface->num_rx_queues; i++) {
62162306a36Sopenharmony_ci			fm10k_free_rx_resources(interface->rx_ring[i]);
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci			memcpy(interface->rx_ring[i], &temp_ring[i],
62462306a36Sopenharmony_ci			       sizeof(struct fm10k_ring));
62562306a36Sopenharmony_ci		}
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci		interface->rx_ring_count = new_rx_count;
62862306a36Sopenharmony_ci	}
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_cierr_setup:
63162306a36Sopenharmony_ci	fm10k_up(interface);
63262306a36Sopenharmony_ci	vfree(temp_ring);
63362306a36Sopenharmony_ciclear_reset:
63462306a36Sopenharmony_ci	clear_bit(__FM10K_RESETTING, interface->state);
63562306a36Sopenharmony_ci	return err;
63662306a36Sopenharmony_ci}
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_cistatic int fm10k_get_coalesce(struct net_device *dev,
63962306a36Sopenharmony_ci			      struct ethtool_coalesce *ec,
64062306a36Sopenharmony_ci			      struct kernel_ethtool_coalesce *kernel_coal,
64162306a36Sopenharmony_ci			      struct netlink_ext_ack *extack)
64262306a36Sopenharmony_ci{
64362306a36Sopenharmony_ci	struct fm10k_intfc *interface = netdev_priv(dev);
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci	ec->use_adaptive_tx_coalesce = ITR_IS_ADAPTIVE(interface->tx_itr);
64662306a36Sopenharmony_ci	ec->tx_coalesce_usecs = interface->tx_itr & ~FM10K_ITR_ADAPTIVE;
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci	ec->use_adaptive_rx_coalesce = ITR_IS_ADAPTIVE(interface->rx_itr);
64962306a36Sopenharmony_ci	ec->rx_coalesce_usecs = interface->rx_itr & ~FM10K_ITR_ADAPTIVE;
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci	return 0;
65262306a36Sopenharmony_ci}
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_cistatic int fm10k_set_coalesce(struct net_device *dev,
65562306a36Sopenharmony_ci			      struct ethtool_coalesce *ec,
65662306a36Sopenharmony_ci			      struct kernel_ethtool_coalesce *kernel_coal,
65762306a36Sopenharmony_ci			      struct netlink_ext_ack *extack)
65862306a36Sopenharmony_ci{
65962306a36Sopenharmony_ci	struct fm10k_intfc *interface = netdev_priv(dev);
66062306a36Sopenharmony_ci	u16 tx_itr, rx_itr;
66162306a36Sopenharmony_ci	int i;
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci	/* verify limits */
66462306a36Sopenharmony_ci	if ((ec->rx_coalesce_usecs > FM10K_ITR_MAX) ||
66562306a36Sopenharmony_ci	    (ec->tx_coalesce_usecs > FM10K_ITR_MAX))
66662306a36Sopenharmony_ci		return -EINVAL;
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci	/* record settings */
66962306a36Sopenharmony_ci	tx_itr = ec->tx_coalesce_usecs;
67062306a36Sopenharmony_ci	rx_itr = ec->rx_coalesce_usecs;
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci	/* set initial values for adaptive ITR */
67362306a36Sopenharmony_ci	if (ec->use_adaptive_tx_coalesce)
67462306a36Sopenharmony_ci		tx_itr = FM10K_ITR_ADAPTIVE | FM10K_TX_ITR_DEFAULT;
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci	if (ec->use_adaptive_rx_coalesce)
67762306a36Sopenharmony_ci		rx_itr = FM10K_ITR_ADAPTIVE | FM10K_RX_ITR_DEFAULT;
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	/* update interface */
68062306a36Sopenharmony_ci	interface->tx_itr = tx_itr;
68162306a36Sopenharmony_ci	interface->rx_itr = rx_itr;
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci	/* update q_vectors */
68462306a36Sopenharmony_ci	for (i = 0; i < interface->num_q_vectors; i++) {
68562306a36Sopenharmony_ci		struct fm10k_q_vector *qv = interface->q_vector[i];
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci		qv->tx.itr = tx_itr;
68862306a36Sopenharmony_ci		qv->rx.itr = rx_itr;
68962306a36Sopenharmony_ci	}
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci	return 0;
69262306a36Sopenharmony_ci}
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_cistatic int fm10k_get_rss_hash_opts(struct fm10k_intfc *interface,
69562306a36Sopenharmony_ci				   struct ethtool_rxnfc *cmd)
69662306a36Sopenharmony_ci{
69762306a36Sopenharmony_ci	cmd->data = 0;
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci	/* Report default options for RSS on fm10k */
70062306a36Sopenharmony_ci	switch (cmd->flow_type) {
70162306a36Sopenharmony_ci	case TCP_V4_FLOW:
70262306a36Sopenharmony_ci	case TCP_V6_FLOW:
70362306a36Sopenharmony_ci		cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
70462306a36Sopenharmony_ci		fallthrough;
70562306a36Sopenharmony_ci	case UDP_V4_FLOW:
70662306a36Sopenharmony_ci		if (test_bit(FM10K_FLAG_RSS_FIELD_IPV4_UDP,
70762306a36Sopenharmony_ci			     interface->flags))
70862306a36Sopenharmony_ci			cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
70962306a36Sopenharmony_ci		fallthrough;
71062306a36Sopenharmony_ci	case SCTP_V4_FLOW:
71162306a36Sopenharmony_ci	case SCTP_V6_FLOW:
71262306a36Sopenharmony_ci	case AH_ESP_V4_FLOW:
71362306a36Sopenharmony_ci	case AH_ESP_V6_FLOW:
71462306a36Sopenharmony_ci	case AH_V4_FLOW:
71562306a36Sopenharmony_ci	case AH_V6_FLOW:
71662306a36Sopenharmony_ci	case ESP_V4_FLOW:
71762306a36Sopenharmony_ci	case ESP_V6_FLOW:
71862306a36Sopenharmony_ci	case IPV4_FLOW:
71962306a36Sopenharmony_ci	case IPV6_FLOW:
72062306a36Sopenharmony_ci		cmd->data |= RXH_IP_SRC | RXH_IP_DST;
72162306a36Sopenharmony_ci		break;
72262306a36Sopenharmony_ci	case UDP_V6_FLOW:
72362306a36Sopenharmony_ci		if (test_bit(FM10K_FLAG_RSS_FIELD_IPV6_UDP,
72462306a36Sopenharmony_ci			     interface->flags))
72562306a36Sopenharmony_ci			cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
72662306a36Sopenharmony_ci		cmd->data |= RXH_IP_SRC | RXH_IP_DST;
72762306a36Sopenharmony_ci		break;
72862306a36Sopenharmony_ci	default:
72962306a36Sopenharmony_ci		return -EINVAL;
73062306a36Sopenharmony_ci	}
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci	return 0;
73362306a36Sopenharmony_ci}
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_cistatic int fm10k_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd,
73662306a36Sopenharmony_ci			   u32 __always_unused *rule_locs)
73762306a36Sopenharmony_ci{
73862306a36Sopenharmony_ci	struct fm10k_intfc *interface = netdev_priv(dev);
73962306a36Sopenharmony_ci	int ret = -EOPNOTSUPP;
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci	switch (cmd->cmd) {
74262306a36Sopenharmony_ci	case ETHTOOL_GRXRINGS:
74362306a36Sopenharmony_ci		cmd->data = interface->num_rx_queues;
74462306a36Sopenharmony_ci		ret = 0;
74562306a36Sopenharmony_ci		break;
74662306a36Sopenharmony_ci	case ETHTOOL_GRXFH:
74762306a36Sopenharmony_ci		ret = fm10k_get_rss_hash_opts(interface, cmd);
74862306a36Sopenharmony_ci		break;
74962306a36Sopenharmony_ci	default:
75062306a36Sopenharmony_ci		break;
75162306a36Sopenharmony_ci	}
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_ci	return ret;
75462306a36Sopenharmony_ci}
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_cistatic int fm10k_set_rss_hash_opt(struct fm10k_intfc *interface,
75762306a36Sopenharmony_ci				  struct ethtool_rxnfc *nfc)
75862306a36Sopenharmony_ci{
75962306a36Sopenharmony_ci	int rss_ipv4_udp = test_bit(FM10K_FLAG_RSS_FIELD_IPV4_UDP,
76062306a36Sopenharmony_ci				    interface->flags);
76162306a36Sopenharmony_ci	int rss_ipv6_udp = test_bit(FM10K_FLAG_RSS_FIELD_IPV6_UDP,
76262306a36Sopenharmony_ci				    interface->flags);
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ci	/* RSS does not support anything other than hashing
76562306a36Sopenharmony_ci	 * to queues on src and dst IPs and ports
76662306a36Sopenharmony_ci	 */
76762306a36Sopenharmony_ci	if (nfc->data & ~(RXH_IP_SRC | RXH_IP_DST |
76862306a36Sopenharmony_ci			  RXH_L4_B_0_1 | RXH_L4_B_2_3))
76962306a36Sopenharmony_ci		return -EINVAL;
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ci	switch (nfc->flow_type) {
77262306a36Sopenharmony_ci	case TCP_V4_FLOW:
77362306a36Sopenharmony_ci	case TCP_V6_FLOW:
77462306a36Sopenharmony_ci		if (!(nfc->data & RXH_IP_SRC) ||
77562306a36Sopenharmony_ci		    !(nfc->data & RXH_IP_DST) ||
77662306a36Sopenharmony_ci		    !(nfc->data & RXH_L4_B_0_1) ||
77762306a36Sopenharmony_ci		    !(nfc->data & RXH_L4_B_2_3))
77862306a36Sopenharmony_ci			return -EINVAL;
77962306a36Sopenharmony_ci		break;
78062306a36Sopenharmony_ci	case UDP_V4_FLOW:
78162306a36Sopenharmony_ci		if (!(nfc->data & RXH_IP_SRC) ||
78262306a36Sopenharmony_ci		    !(nfc->data & RXH_IP_DST))
78362306a36Sopenharmony_ci			return -EINVAL;
78462306a36Sopenharmony_ci		switch (nfc->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) {
78562306a36Sopenharmony_ci		case 0:
78662306a36Sopenharmony_ci			clear_bit(FM10K_FLAG_RSS_FIELD_IPV4_UDP,
78762306a36Sopenharmony_ci				  interface->flags);
78862306a36Sopenharmony_ci			break;
78962306a36Sopenharmony_ci		case (RXH_L4_B_0_1 | RXH_L4_B_2_3):
79062306a36Sopenharmony_ci			set_bit(FM10K_FLAG_RSS_FIELD_IPV4_UDP,
79162306a36Sopenharmony_ci				interface->flags);
79262306a36Sopenharmony_ci			break;
79362306a36Sopenharmony_ci		default:
79462306a36Sopenharmony_ci			return -EINVAL;
79562306a36Sopenharmony_ci		}
79662306a36Sopenharmony_ci		break;
79762306a36Sopenharmony_ci	case UDP_V6_FLOW:
79862306a36Sopenharmony_ci		if (!(nfc->data & RXH_IP_SRC) ||
79962306a36Sopenharmony_ci		    !(nfc->data & RXH_IP_DST))
80062306a36Sopenharmony_ci			return -EINVAL;
80162306a36Sopenharmony_ci		switch (nfc->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) {
80262306a36Sopenharmony_ci		case 0:
80362306a36Sopenharmony_ci			clear_bit(FM10K_FLAG_RSS_FIELD_IPV6_UDP,
80462306a36Sopenharmony_ci				  interface->flags);
80562306a36Sopenharmony_ci			break;
80662306a36Sopenharmony_ci		case (RXH_L4_B_0_1 | RXH_L4_B_2_3):
80762306a36Sopenharmony_ci			set_bit(FM10K_FLAG_RSS_FIELD_IPV6_UDP,
80862306a36Sopenharmony_ci				interface->flags);
80962306a36Sopenharmony_ci			break;
81062306a36Sopenharmony_ci		default:
81162306a36Sopenharmony_ci			return -EINVAL;
81262306a36Sopenharmony_ci		}
81362306a36Sopenharmony_ci		break;
81462306a36Sopenharmony_ci	case AH_ESP_V4_FLOW:
81562306a36Sopenharmony_ci	case AH_V4_FLOW:
81662306a36Sopenharmony_ci	case ESP_V4_FLOW:
81762306a36Sopenharmony_ci	case SCTP_V4_FLOW:
81862306a36Sopenharmony_ci	case AH_ESP_V6_FLOW:
81962306a36Sopenharmony_ci	case AH_V6_FLOW:
82062306a36Sopenharmony_ci	case ESP_V6_FLOW:
82162306a36Sopenharmony_ci	case SCTP_V6_FLOW:
82262306a36Sopenharmony_ci		if (!(nfc->data & RXH_IP_SRC) ||
82362306a36Sopenharmony_ci		    !(nfc->data & RXH_IP_DST) ||
82462306a36Sopenharmony_ci		    (nfc->data & RXH_L4_B_0_1) ||
82562306a36Sopenharmony_ci		    (nfc->data & RXH_L4_B_2_3))
82662306a36Sopenharmony_ci			return -EINVAL;
82762306a36Sopenharmony_ci		break;
82862306a36Sopenharmony_ci	default:
82962306a36Sopenharmony_ci		return -EINVAL;
83062306a36Sopenharmony_ci	}
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci	/* If something changed we need to update the MRQC register. Note that
83362306a36Sopenharmony_ci	 * test_bit() is guaranteed to return strictly 0 or 1, so testing for
83462306a36Sopenharmony_ci	 * equality is safe.
83562306a36Sopenharmony_ci	 */
83662306a36Sopenharmony_ci	if ((rss_ipv4_udp != test_bit(FM10K_FLAG_RSS_FIELD_IPV4_UDP,
83762306a36Sopenharmony_ci				      interface->flags)) ||
83862306a36Sopenharmony_ci	    (rss_ipv6_udp != test_bit(FM10K_FLAG_RSS_FIELD_IPV6_UDP,
83962306a36Sopenharmony_ci				      interface->flags))) {
84062306a36Sopenharmony_ci		struct fm10k_hw *hw = &interface->hw;
84162306a36Sopenharmony_ci		bool warn = false;
84262306a36Sopenharmony_ci		u32 mrqc;
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci		/* Perform hash on these packet types */
84562306a36Sopenharmony_ci		mrqc = FM10K_MRQC_IPV4 |
84662306a36Sopenharmony_ci		       FM10K_MRQC_TCP_IPV4 |
84762306a36Sopenharmony_ci		       FM10K_MRQC_IPV6 |
84862306a36Sopenharmony_ci		       FM10K_MRQC_TCP_IPV6;
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_ci		if (test_bit(FM10K_FLAG_RSS_FIELD_IPV4_UDP,
85162306a36Sopenharmony_ci			     interface->flags)) {
85262306a36Sopenharmony_ci			mrqc |= FM10K_MRQC_UDP_IPV4;
85362306a36Sopenharmony_ci			warn = true;
85462306a36Sopenharmony_ci		}
85562306a36Sopenharmony_ci		if (test_bit(FM10K_FLAG_RSS_FIELD_IPV6_UDP,
85662306a36Sopenharmony_ci			     interface->flags)) {
85762306a36Sopenharmony_ci			mrqc |= FM10K_MRQC_UDP_IPV6;
85862306a36Sopenharmony_ci			warn = true;
85962306a36Sopenharmony_ci		}
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_ci		/* If we enable UDP RSS display a warning that this may cause
86262306a36Sopenharmony_ci		 * fragmented UDP packets to arrive out of order.
86362306a36Sopenharmony_ci		 */
86462306a36Sopenharmony_ci		if (warn)
86562306a36Sopenharmony_ci			netif_warn(interface, drv, interface->netdev,
86662306a36Sopenharmony_ci				   "enabling UDP RSS: fragmented packets may arrive out of order to the stack above\n");
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci		fm10k_write_reg(hw, FM10K_MRQC(0), mrqc);
86962306a36Sopenharmony_ci	}
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci	return 0;
87262306a36Sopenharmony_ci}
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_cistatic int fm10k_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd)
87562306a36Sopenharmony_ci{
87662306a36Sopenharmony_ci	struct fm10k_intfc *interface = netdev_priv(dev);
87762306a36Sopenharmony_ci	int ret = -EOPNOTSUPP;
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_ci	switch (cmd->cmd) {
88062306a36Sopenharmony_ci	case ETHTOOL_SRXFH:
88162306a36Sopenharmony_ci		ret = fm10k_set_rss_hash_opt(interface, cmd);
88262306a36Sopenharmony_ci		break;
88362306a36Sopenharmony_ci	default:
88462306a36Sopenharmony_ci		break;
88562306a36Sopenharmony_ci	}
88662306a36Sopenharmony_ci
88762306a36Sopenharmony_ci	return ret;
88862306a36Sopenharmony_ci}
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_cistatic int fm10k_mbx_test(struct fm10k_intfc *interface, u64 *data)
89162306a36Sopenharmony_ci{
89262306a36Sopenharmony_ci	struct fm10k_hw *hw = &interface->hw;
89362306a36Sopenharmony_ci	struct fm10k_mbx_info *mbx = &hw->mbx;
89462306a36Sopenharmony_ci	u32 attr_flag, test_msg[6];
89562306a36Sopenharmony_ci	unsigned long timeout;
89662306a36Sopenharmony_ci	int err = -EINVAL;
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci	/* For now this is a VF only feature */
89962306a36Sopenharmony_ci	if (hw->mac.type != fm10k_mac_vf)
90062306a36Sopenharmony_ci		return 0;
90162306a36Sopenharmony_ci
90262306a36Sopenharmony_ci	/* loop through both nested and unnested attribute types */
90362306a36Sopenharmony_ci	for (attr_flag = BIT(FM10K_TEST_MSG_UNSET);
90462306a36Sopenharmony_ci	     attr_flag < BIT(2 * FM10K_TEST_MSG_NESTED);
90562306a36Sopenharmony_ci	     attr_flag += attr_flag) {
90662306a36Sopenharmony_ci		/* generate message to be tested */
90762306a36Sopenharmony_ci		fm10k_tlv_msg_test_create(test_msg, attr_flag);
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_ci		fm10k_mbx_lock(interface);
91062306a36Sopenharmony_ci		mbx->test_result = FM10K_NOT_IMPLEMENTED;
91162306a36Sopenharmony_ci		err = mbx->ops.enqueue_tx(hw, mbx, test_msg);
91262306a36Sopenharmony_ci		fm10k_mbx_unlock(interface);
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_ci		/* wait up to 1 second for response */
91562306a36Sopenharmony_ci		timeout = jiffies + HZ;
91662306a36Sopenharmony_ci		do {
91762306a36Sopenharmony_ci			if (err < 0)
91862306a36Sopenharmony_ci				goto err_out;
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_ci			usleep_range(500, 1000);
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_ci			fm10k_mbx_lock(interface);
92362306a36Sopenharmony_ci			mbx->ops.process(hw, mbx);
92462306a36Sopenharmony_ci			fm10k_mbx_unlock(interface);
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ci			err = mbx->test_result;
92762306a36Sopenharmony_ci			if (!err)
92862306a36Sopenharmony_ci				break;
92962306a36Sopenharmony_ci		} while (time_is_after_jiffies(timeout));
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_ci		/* reporting errors */
93262306a36Sopenharmony_ci		if (err)
93362306a36Sopenharmony_ci			goto err_out;
93462306a36Sopenharmony_ci	}
93562306a36Sopenharmony_ci
93662306a36Sopenharmony_cierr_out:
93762306a36Sopenharmony_ci	*data = err < 0 ? (attr_flag) : (err > 0);
93862306a36Sopenharmony_ci	return err;
93962306a36Sopenharmony_ci}
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_cistatic void fm10k_self_test(struct net_device *dev,
94262306a36Sopenharmony_ci			    struct ethtool_test *eth_test, u64 *data)
94362306a36Sopenharmony_ci{
94462306a36Sopenharmony_ci	struct fm10k_intfc *interface = netdev_priv(dev);
94562306a36Sopenharmony_ci	struct fm10k_hw *hw = &interface->hw;
94662306a36Sopenharmony_ci
94762306a36Sopenharmony_ci	memset(data, 0, sizeof(*data) * FM10K_TEST_LEN);
94862306a36Sopenharmony_ci
94962306a36Sopenharmony_ci	if (FM10K_REMOVED(hw->hw_addr)) {
95062306a36Sopenharmony_ci		netif_err(interface, drv, dev,
95162306a36Sopenharmony_ci			  "Interface removed - test blocked\n");
95262306a36Sopenharmony_ci		eth_test->flags |= ETH_TEST_FL_FAILED;
95362306a36Sopenharmony_ci		return;
95462306a36Sopenharmony_ci	}
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_ci	if (fm10k_mbx_test(interface, &data[FM10K_TEST_MBX]))
95762306a36Sopenharmony_ci		eth_test->flags |= ETH_TEST_FL_FAILED;
95862306a36Sopenharmony_ci}
95962306a36Sopenharmony_ci
96062306a36Sopenharmony_cistatic u32 fm10k_get_priv_flags(struct net_device *netdev)
96162306a36Sopenharmony_ci{
96262306a36Sopenharmony_ci	return 0;
96362306a36Sopenharmony_ci}
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_cistatic int fm10k_set_priv_flags(struct net_device *netdev, u32 priv_flags)
96662306a36Sopenharmony_ci{
96762306a36Sopenharmony_ci	if (priv_flags >= BIT(FM10K_PRV_FLAG_LEN))
96862306a36Sopenharmony_ci		return -EINVAL;
96962306a36Sopenharmony_ci
97062306a36Sopenharmony_ci	return 0;
97162306a36Sopenharmony_ci}
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_cistatic u32 fm10k_get_reta_size(struct net_device __always_unused *netdev)
97462306a36Sopenharmony_ci{
97562306a36Sopenharmony_ci	return FM10K_RETA_SIZE * FM10K_RETA_ENTRIES_PER_REG;
97662306a36Sopenharmony_ci}
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_civoid fm10k_write_reta(struct fm10k_intfc *interface, const u32 *indir)
97962306a36Sopenharmony_ci{
98062306a36Sopenharmony_ci	u16 rss_i = interface->ring_feature[RING_F_RSS].indices;
98162306a36Sopenharmony_ci	struct fm10k_hw *hw = &interface->hw;
98262306a36Sopenharmony_ci	u32 table[4];
98362306a36Sopenharmony_ci	int i, j;
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_ci	/* record entries to reta table */
98662306a36Sopenharmony_ci	for (i = 0; i < FM10K_RETA_SIZE; i++) {
98762306a36Sopenharmony_ci		u32 reta, n;
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_ci		/* generate a new table if we weren't given one */
99062306a36Sopenharmony_ci		for (j = 0; j < 4; j++) {
99162306a36Sopenharmony_ci			if (indir)
99262306a36Sopenharmony_ci				n = indir[4 * i + j];
99362306a36Sopenharmony_ci			else
99462306a36Sopenharmony_ci				n = ethtool_rxfh_indir_default(4 * i + j,
99562306a36Sopenharmony_ci							       rss_i);
99662306a36Sopenharmony_ci
99762306a36Sopenharmony_ci			table[j] = n;
99862306a36Sopenharmony_ci		}
99962306a36Sopenharmony_ci
100062306a36Sopenharmony_ci		reta = table[0] |
100162306a36Sopenharmony_ci			(table[1] << 8) |
100262306a36Sopenharmony_ci			(table[2] << 16) |
100362306a36Sopenharmony_ci			(table[3] << 24);
100462306a36Sopenharmony_ci
100562306a36Sopenharmony_ci		if (interface->reta[i] == reta)
100662306a36Sopenharmony_ci			continue;
100762306a36Sopenharmony_ci
100862306a36Sopenharmony_ci		interface->reta[i] = reta;
100962306a36Sopenharmony_ci		fm10k_write_reg(hw, FM10K_RETA(0, i), reta);
101062306a36Sopenharmony_ci	}
101162306a36Sopenharmony_ci}
101262306a36Sopenharmony_ci
101362306a36Sopenharmony_cistatic int fm10k_get_reta(struct net_device *netdev, u32 *indir)
101462306a36Sopenharmony_ci{
101562306a36Sopenharmony_ci	struct fm10k_intfc *interface = netdev_priv(netdev);
101662306a36Sopenharmony_ci	int i;
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_ci	if (!indir)
101962306a36Sopenharmony_ci		return 0;
102062306a36Sopenharmony_ci
102162306a36Sopenharmony_ci	for (i = 0; i < FM10K_RETA_SIZE; i++, indir += 4) {
102262306a36Sopenharmony_ci		u32 reta = interface->reta[i];
102362306a36Sopenharmony_ci
102462306a36Sopenharmony_ci		indir[0] = (reta << 24) >> 24;
102562306a36Sopenharmony_ci		indir[1] = (reta << 16) >> 24;
102662306a36Sopenharmony_ci		indir[2] = (reta <<  8) >> 24;
102762306a36Sopenharmony_ci		indir[3] = (reta) >> 24;
102862306a36Sopenharmony_ci	}
102962306a36Sopenharmony_ci
103062306a36Sopenharmony_ci	return 0;
103162306a36Sopenharmony_ci}
103262306a36Sopenharmony_ci
103362306a36Sopenharmony_cistatic int fm10k_set_reta(struct net_device *netdev, const u32 *indir)
103462306a36Sopenharmony_ci{
103562306a36Sopenharmony_ci	struct fm10k_intfc *interface = netdev_priv(netdev);
103662306a36Sopenharmony_ci	int i;
103762306a36Sopenharmony_ci	u16 rss_i;
103862306a36Sopenharmony_ci
103962306a36Sopenharmony_ci	if (!indir)
104062306a36Sopenharmony_ci		return 0;
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_ci	/* Verify user input. */
104362306a36Sopenharmony_ci	rss_i = interface->ring_feature[RING_F_RSS].indices;
104462306a36Sopenharmony_ci	for (i = fm10k_get_reta_size(netdev); i--;) {
104562306a36Sopenharmony_ci		if (indir[i] < rss_i)
104662306a36Sopenharmony_ci			continue;
104762306a36Sopenharmony_ci		return -EINVAL;
104862306a36Sopenharmony_ci	}
104962306a36Sopenharmony_ci
105062306a36Sopenharmony_ci	fm10k_write_reta(interface, indir);
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ci	return 0;
105362306a36Sopenharmony_ci}
105462306a36Sopenharmony_ci
105562306a36Sopenharmony_cistatic u32 fm10k_get_rssrk_size(struct net_device __always_unused *netdev)
105662306a36Sopenharmony_ci{
105762306a36Sopenharmony_ci	return FM10K_RSSRK_SIZE * FM10K_RSSRK_ENTRIES_PER_REG;
105862306a36Sopenharmony_ci}
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_cistatic int fm10k_get_rssh(struct net_device *netdev, u32 *indir, u8 *key,
106162306a36Sopenharmony_ci			  u8 *hfunc)
106262306a36Sopenharmony_ci{
106362306a36Sopenharmony_ci	struct fm10k_intfc *interface = netdev_priv(netdev);
106462306a36Sopenharmony_ci	int i, err;
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_ci	if (hfunc)
106762306a36Sopenharmony_ci		*hfunc = ETH_RSS_HASH_TOP;
106862306a36Sopenharmony_ci
106962306a36Sopenharmony_ci	err = fm10k_get_reta(netdev, indir);
107062306a36Sopenharmony_ci	if (err || !key)
107162306a36Sopenharmony_ci		return err;
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_ci	for (i = 0; i < FM10K_RSSRK_SIZE; i++, key += 4)
107462306a36Sopenharmony_ci		*(__le32 *)key = cpu_to_le32(interface->rssrk[i]);
107562306a36Sopenharmony_ci
107662306a36Sopenharmony_ci	return 0;
107762306a36Sopenharmony_ci}
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_cistatic int fm10k_set_rssh(struct net_device *netdev, const u32 *indir,
108062306a36Sopenharmony_ci			  const u8 *key, const u8 hfunc)
108162306a36Sopenharmony_ci{
108262306a36Sopenharmony_ci	struct fm10k_intfc *interface = netdev_priv(netdev);
108362306a36Sopenharmony_ci	struct fm10k_hw *hw = &interface->hw;
108462306a36Sopenharmony_ci	int i, err;
108562306a36Sopenharmony_ci
108662306a36Sopenharmony_ci	/* We do not allow change in unsupported parameters */
108762306a36Sopenharmony_ci	if (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP)
108862306a36Sopenharmony_ci		return -EOPNOTSUPP;
108962306a36Sopenharmony_ci
109062306a36Sopenharmony_ci	err = fm10k_set_reta(netdev, indir);
109162306a36Sopenharmony_ci	if (err || !key)
109262306a36Sopenharmony_ci		return err;
109362306a36Sopenharmony_ci
109462306a36Sopenharmony_ci	for (i = 0; i < FM10K_RSSRK_SIZE; i++, key += 4) {
109562306a36Sopenharmony_ci		u32 rssrk = le32_to_cpu(*(__le32 *)key);
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_ci		if (interface->rssrk[i] == rssrk)
109862306a36Sopenharmony_ci			continue;
109962306a36Sopenharmony_ci
110062306a36Sopenharmony_ci		interface->rssrk[i] = rssrk;
110162306a36Sopenharmony_ci		fm10k_write_reg(hw, FM10K_RSSRK(0, i), rssrk);
110262306a36Sopenharmony_ci	}
110362306a36Sopenharmony_ci
110462306a36Sopenharmony_ci	return 0;
110562306a36Sopenharmony_ci}
110662306a36Sopenharmony_ci
110762306a36Sopenharmony_cistatic unsigned int fm10k_max_channels(struct net_device *dev)
110862306a36Sopenharmony_ci{
110962306a36Sopenharmony_ci	struct fm10k_intfc *interface = netdev_priv(dev);
111062306a36Sopenharmony_ci	unsigned int max_combined = interface->hw.mac.max_queues;
111162306a36Sopenharmony_ci	u8 tcs = netdev_get_num_tc(dev);
111262306a36Sopenharmony_ci
111362306a36Sopenharmony_ci	/* For QoS report channels per traffic class */
111462306a36Sopenharmony_ci	if (tcs > 1)
111562306a36Sopenharmony_ci		max_combined = BIT((fls(max_combined / tcs) - 1));
111662306a36Sopenharmony_ci
111762306a36Sopenharmony_ci	return max_combined;
111862306a36Sopenharmony_ci}
111962306a36Sopenharmony_ci
112062306a36Sopenharmony_cistatic void fm10k_get_channels(struct net_device *dev,
112162306a36Sopenharmony_ci			       struct ethtool_channels *ch)
112262306a36Sopenharmony_ci{
112362306a36Sopenharmony_ci	struct fm10k_intfc *interface = netdev_priv(dev);
112462306a36Sopenharmony_ci
112562306a36Sopenharmony_ci	/* report maximum channels */
112662306a36Sopenharmony_ci	ch->max_combined = fm10k_max_channels(dev);
112762306a36Sopenharmony_ci
112862306a36Sopenharmony_ci	/* report info for other vector */
112962306a36Sopenharmony_ci	ch->max_other = NON_Q_VECTORS;
113062306a36Sopenharmony_ci	ch->other_count = ch->max_other;
113162306a36Sopenharmony_ci
113262306a36Sopenharmony_ci	/* record RSS queues */
113362306a36Sopenharmony_ci	ch->combined_count = interface->ring_feature[RING_F_RSS].indices;
113462306a36Sopenharmony_ci}
113562306a36Sopenharmony_ci
113662306a36Sopenharmony_cistatic int fm10k_set_channels(struct net_device *dev,
113762306a36Sopenharmony_ci			      struct ethtool_channels *ch)
113862306a36Sopenharmony_ci{
113962306a36Sopenharmony_ci	struct fm10k_intfc *interface = netdev_priv(dev);
114062306a36Sopenharmony_ci	unsigned int count = ch->combined_count;
114162306a36Sopenharmony_ci
114262306a36Sopenharmony_ci	/* verify they are not requesting separate vectors */
114362306a36Sopenharmony_ci	if (!count || ch->rx_count || ch->tx_count)
114462306a36Sopenharmony_ci		return -EINVAL;
114562306a36Sopenharmony_ci
114662306a36Sopenharmony_ci	/* verify other_count has not changed */
114762306a36Sopenharmony_ci	if (ch->other_count != NON_Q_VECTORS)
114862306a36Sopenharmony_ci		return -EINVAL;
114962306a36Sopenharmony_ci
115062306a36Sopenharmony_ci	/* verify the number of channels does not exceed hardware limits */
115162306a36Sopenharmony_ci	if (count > fm10k_max_channels(dev))
115262306a36Sopenharmony_ci		return -EINVAL;
115362306a36Sopenharmony_ci
115462306a36Sopenharmony_ci	interface->ring_feature[RING_F_RSS].limit = count;
115562306a36Sopenharmony_ci
115662306a36Sopenharmony_ci	/* use setup TC to update any traffic class queue mapping */
115762306a36Sopenharmony_ci	return fm10k_setup_tc(dev, netdev_get_num_tc(dev));
115862306a36Sopenharmony_ci}
115962306a36Sopenharmony_ci
116062306a36Sopenharmony_cistatic const struct ethtool_ops fm10k_ethtool_ops = {
116162306a36Sopenharmony_ci	.supported_coalesce_params = ETHTOOL_COALESCE_USECS |
116262306a36Sopenharmony_ci				     ETHTOOL_COALESCE_USE_ADAPTIVE,
116362306a36Sopenharmony_ci	.get_strings		= fm10k_get_strings,
116462306a36Sopenharmony_ci	.get_sset_count		= fm10k_get_sset_count,
116562306a36Sopenharmony_ci	.get_ethtool_stats      = fm10k_get_ethtool_stats,
116662306a36Sopenharmony_ci	.get_drvinfo		= fm10k_get_drvinfo,
116762306a36Sopenharmony_ci	.get_link		= ethtool_op_get_link,
116862306a36Sopenharmony_ci	.get_pauseparam		= fm10k_get_pauseparam,
116962306a36Sopenharmony_ci	.set_pauseparam		= fm10k_set_pauseparam,
117062306a36Sopenharmony_ci	.get_msglevel		= fm10k_get_msglevel,
117162306a36Sopenharmony_ci	.set_msglevel		= fm10k_set_msglevel,
117262306a36Sopenharmony_ci	.get_ringparam		= fm10k_get_ringparam,
117362306a36Sopenharmony_ci	.set_ringparam		= fm10k_set_ringparam,
117462306a36Sopenharmony_ci	.get_coalesce		= fm10k_get_coalesce,
117562306a36Sopenharmony_ci	.set_coalesce		= fm10k_set_coalesce,
117662306a36Sopenharmony_ci	.get_rxnfc		= fm10k_get_rxnfc,
117762306a36Sopenharmony_ci	.set_rxnfc		= fm10k_set_rxnfc,
117862306a36Sopenharmony_ci	.get_regs               = fm10k_get_regs,
117962306a36Sopenharmony_ci	.get_regs_len           = fm10k_get_regs_len,
118062306a36Sopenharmony_ci	.self_test		= fm10k_self_test,
118162306a36Sopenharmony_ci	.get_priv_flags		= fm10k_get_priv_flags,
118262306a36Sopenharmony_ci	.set_priv_flags		= fm10k_set_priv_flags,
118362306a36Sopenharmony_ci	.get_rxfh_indir_size	= fm10k_get_reta_size,
118462306a36Sopenharmony_ci	.get_rxfh_key_size	= fm10k_get_rssrk_size,
118562306a36Sopenharmony_ci	.get_rxfh		= fm10k_get_rssh,
118662306a36Sopenharmony_ci	.set_rxfh		= fm10k_set_rssh,
118762306a36Sopenharmony_ci	.get_channels		= fm10k_get_channels,
118862306a36Sopenharmony_ci	.set_channels		= fm10k_set_channels,
118962306a36Sopenharmony_ci	.get_ts_info		= ethtool_op_get_ts_info,
119062306a36Sopenharmony_ci};
119162306a36Sopenharmony_ci
119262306a36Sopenharmony_civoid fm10k_set_ethtool_ops(struct net_device *dev)
119362306a36Sopenharmony_ci{
119462306a36Sopenharmony_ci	dev->ethtool_ops = &fm10k_ethtool_ops;
119562306a36Sopenharmony_ci}
1196