162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * Linux driver for VMware's vmxnet3 ethernet NIC.
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright (C) 2008-2022, VMware, Inc. All Rights Reserved.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * This program is free software; you can redistribute it and/or modify it
762306a36Sopenharmony_ci * under the terms of the GNU General Public License as published by the
862306a36Sopenharmony_ci * Free Software Foundation; version 2 of the License and no later version.
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci * This program is distributed in the hope that it will be useful, but
1162306a36Sopenharmony_ci * WITHOUT ANY WARRANTY; without even the implied warranty of
1262306a36Sopenharmony_ci * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
1362306a36Sopenharmony_ci * NON INFRINGEMENT.  See the GNU General Public License for more
1462306a36Sopenharmony_ci * details.
1562306a36Sopenharmony_ci *
1662306a36Sopenharmony_ci * You should have received a copy of the GNU General Public License
1762306a36Sopenharmony_ci * along with this program; if not, write to the Free Software
1862306a36Sopenharmony_ci * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
1962306a36Sopenharmony_ci *
2062306a36Sopenharmony_ci * The full GNU General Public License is included in this distribution in
2162306a36Sopenharmony_ci * the file called "COPYING".
2262306a36Sopenharmony_ci *
2362306a36Sopenharmony_ci * Maintained by: pv-drivers@vmware.com
2462306a36Sopenharmony_ci *
2562306a36Sopenharmony_ci */
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#include "vmxnet3_int.h"
2962306a36Sopenharmony_ci#include <net/vxlan.h>
3062306a36Sopenharmony_ci#include <net/geneve.h>
3162306a36Sopenharmony_ci#include "vmxnet3_xdp.h"
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci#define VXLAN_UDP_PORT 8472
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_cistruct vmxnet3_stat_desc {
3662306a36Sopenharmony_ci	char desc[ETH_GSTRING_LEN];
3762306a36Sopenharmony_ci	int  offset;
3862306a36Sopenharmony_ci};
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci/* per tq stats maintained by the device */
4262306a36Sopenharmony_cistatic const struct vmxnet3_stat_desc
4362306a36Sopenharmony_civmxnet3_tq_dev_stats[] = {
4462306a36Sopenharmony_ci	/* description,         offset */
4562306a36Sopenharmony_ci	{ "Tx Queue#",        0 },
4662306a36Sopenharmony_ci	{ "  TSO pkts tx",	offsetof(struct UPT1_TxStats, TSOPktsTxOK) },
4762306a36Sopenharmony_ci	{ "  TSO bytes tx",	offsetof(struct UPT1_TxStats, TSOBytesTxOK) },
4862306a36Sopenharmony_ci	{ "  ucast pkts tx",	offsetof(struct UPT1_TxStats, ucastPktsTxOK) },
4962306a36Sopenharmony_ci	{ "  ucast bytes tx",	offsetof(struct UPT1_TxStats, ucastBytesTxOK) },
5062306a36Sopenharmony_ci	{ "  mcast pkts tx",	offsetof(struct UPT1_TxStats, mcastPktsTxOK) },
5162306a36Sopenharmony_ci	{ "  mcast bytes tx",	offsetof(struct UPT1_TxStats, mcastBytesTxOK) },
5262306a36Sopenharmony_ci	{ "  bcast pkts tx",	offsetof(struct UPT1_TxStats, bcastPktsTxOK) },
5362306a36Sopenharmony_ci	{ "  bcast bytes tx",	offsetof(struct UPT1_TxStats, bcastBytesTxOK) },
5462306a36Sopenharmony_ci	{ "  pkts tx err",	offsetof(struct UPT1_TxStats, pktsTxError) },
5562306a36Sopenharmony_ci	{ "  pkts tx discard",	offsetof(struct UPT1_TxStats, pktsTxDiscard) },
5662306a36Sopenharmony_ci};
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci/* per tq stats maintained by the driver */
5962306a36Sopenharmony_cistatic const struct vmxnet3_stat_desc
6062306a36Sopenharmony_civmxnet3_tq_driver_stats[] = {
6162306a36Sopenharmony_ci	/* description,         offset */
6262306a36Sopenharmony_ci	{"  drv dropped tx total",	offsetof(struct vmxnet3_tq_driver_stats,
6362306a36Sopenharmony_ci						 drop_total) },
6462306a36Sopenharmony_ci	{ "     too many frags", offsetof(struct vmxnet3_tq_driver_stats,
6562306a36Sopenharmony_ci					  drop_too_many_frags) },
6662306a36Sopenharmony_ci	{ "     giant hdr",	offsetof(struct vmxnet3_tq_driver_stats,
6762306a36Sopenharmony_ci					 drop_oversized_hdr) },
6862306a36Sopenharmony_ci	{ "     hdr err",	offsetof(struct vmxnet3_tq_driver_stats,
6962306a36Sopenharmony_ci					 drop_hdr_inspect_err) },
7062306a36Sopenharmony_ci	{ "     tso",		offsetof(struct vmxnet3_tq_driver_stats,
7162306a36Sopenharmony_ci					 drop_tso) },
7262306a36Sopenharmony_ci	{ "  ring full",	offsetof(struct vmxnet3_tq_driver_stats,
7362306a36Sopenharmony_ci					 tx_ring_full) },
7462306a36Sopenharmony_ci	{ "  pkts linearized",	offsetof(struct vmxnet3_tq_driver_stats,
7562306a36Sopenharmony_ci					 linearized) },
7662306a36Sopenharmony_ci	{ "  hdr cloned",	offsetof(struct vmxnet3_tq_driver_stats,
7762306a36Sopenharmony_ci					 copy_skb_header) },
7862306a36Sopenharmony_ci	{ "  giant hdr",	offsetof(struct vmxnet3_tq_driver_stats,
7962306a36Sopenharmony_ci					 oversized_hdr) },
8062306a36Sopenharmony_ci	{ "  xdp xmit",		offsetof(struct vmxnet3_tq_driver_stats,
8162306a36Sopenharmony_ci					 xdp_xmit) },
8262306a36Sopenharmony_ci	{ "  xdp xmit err",	offsetof(struct vmxnet3_tq_driver_stats,
8362306a36Sopenharmony_ci					 xdp_xmit_err) },
8462306a36Sopenharmony_ci};
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci/* per rq stats maintained by the device */
8762306a36Sopenharmony_cistatic const struct vmxnet3_stat_desc
8862306a36Sopenharmony_civmxnet3_rq_dev_stats[] = {
8962306a36Sopenharmony_ci	{ "Rx Queue#",        0 },
9062306a36Sopenharmony_ci	{ "  LRO pkts rx",	offsetof(struct UPT1_RxStats, LROPktsRxOK) },
9162306a36Sopenharmony_ci	{ "  LRO byte rx",	offsetof(struct UPT1_RxStats, LROBytesRxOK) },
9262306a36Sopenharmony_ci	{ "  ucast pkts rx",	offsetof(struct UPT1_RxStats, ucastPktsRxOK) },
9362306a36Sopenharmony_ci	{ "  ucast bytes rx",	offsetof(struct UPT1_RxStats, ucastBytesRxOK) },
9462306a36Sopenharmony_ci	{ "  mcast pkts rx",	offsetof(struct UPT1_RxStats, mcastPktsRxOK) },
9562306a36Sopenharmony_ci	{ "  mcast bytes rx",	offsetof(struct UPT1_RxStats, mcastBytesRxOK) },
9662306a36Sopenharmony_ci	{ "  bcast pkts rx",	offsetof(struct UPT1_RxStats, bcastPktsRxOK) },
9762306a36Sopenharmony_ci	{ "  bcast bytes rx",	offsetof(struct UPT1_RxStats, bcastBytesRxOK) },
9862306a36Sopenharmony_ci	{ "  pkts rx OOB",	offsetof(struct UPT1_RxStats, pktsRxOutOfBuf) },
9962306a36Sopenharmony_ci	{ "  pkts rx err",	offsetof(struct UPT1_RxStats, pktsRxError) },
10062306a36Sopenharmony_ci};
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci/* per rq stats maintained by the driver */
10362306a36Sopenharmony_cistatic const struct vmxnet3_stat_desc
10462306a36Sopenharmony_civmxnet3_rq_driver_stats[] = {
10562306a36Sopenharmony_ci	/* description,         offset */
10662306a36Sopenharmony_ci	{ "  drv dropped rx total", offsetof(struct vmxnet3_rq_driver_stats,
10762306a36Sopenharmony_ci					     drop_total) },
10862306a36Sopenharmony_ci	{ "     err",		offsetof(struct vmxnet3_rq_driver_stats,
10962306a36Sopenharmony_ci					 drop_err) },
11062306a36Sopenharmony_ci	{ "     fcs",		offsetof(struct vmxnet3_rq_driver_stats,
11162306a36Sopenharmony_ci					 drop_fcs) },
11262306a36Sopenharmony_ci	{ "  rx buf alloc fail", offsetof(struct vmxnet3_rq_driver_stats,
11362306a36Sopenharmony_ci					  rx_buf_alloc_failure) },
11462306a36Sopenharmony_ci	{ "     xdp packets", offsetof(struct vmxnet3_rq_driver_stats,
11562306a36Sopenharmony_ci				       xdp_packets) },
11662306a36Sopenharmony_ci	{ "     xdp tx", offsetof(struct vmxnet3_rq_driver_stats,
11762306a36Sopenharmony_ci				  xdp_tx) },
11862306a36Sopenharmony_ci	{ "     xdp redirects", offsetof(struct vmxnet3_rq_driver_stats,
11962306a36Sopenharmony_ci					 xdp_redirects) },
12062306a36Sopenharmony_ci	{ "     xdp drops", offsetof(struct vmxnet3_rq_driver_stats,
12162306a36Sopenharmony_ci				     xdp_drops) },
12262306a36Sopenharmony_ci	{ "     xdp aborted", offsetof(struct vmxnet3_rq_driver_stats,
12362306a36Sopenharmony_ci				       xdp_aborted) },
12462306a36Sopenharmony_ci};
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci/* global stats maintained by the driver */
12762306a36Sopenharmony_cistatic const struct vmxnet3_stat_desc
12862306a36Sopenharmony_civmxnet3_global_stats[] = {
12962306a36Sopenharmony_ci	/* description,         offset */
13062306a36Sopenharmony_ci	{ "tx timeout count",	offsetof(struct vmxnet3_adapter,
13162306a36Sopenharmony_ci					 tx_timeout_count) }
13262306a36Sopenharmony_ci};
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_civoid
13662306a36Sopenharmony_civmxnet3_get_stats64(struct net_device *netdev,
13762306a36Sopenharmony_ci		   struct rtnl_link_stats64 *stats)
13862306a36Sopenharmony_ci{
13962306a36Sopenharmony_ci	struct vmxnet3_adapter *adapter;
14062306a36Sopenharmony_ci	struct vmxnet3_tq_driver_stats *drvTxStats;
14162306a36Sopenharmony_ci	struct vmxnet3_rq_driver_stats *drvRxStats;
14262306a36Sopenharmony_ci	struct UPT1_TxStats *devTxStats;
14362306a36Sopenharmony_ci	struct UPT1_RxStats *devRxStats;
14462306a36Sopenharmony_ci	unsigned long flags;
14562306a36Sopenharmony_ci	int i;
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	adapter = netdev_priv(netdev);
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	/* Collect the dev stats into the shared area */
15062306a36Sopenharmony_ci	spin_lock_irqsave(&adapter->cmd_lock, flags);
15162306a36Sopenharmony_ci	VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, VMXNET3_CMD_GET_STATS);
15262306a36Sopenharmony_ci	spin_unlock_irqrestore(&adapter->cmd_lock, flags);
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	for (i = 0; i < adapter->num_tx_queues; i++) {
15562306a36Sopenharmony_ci		devTxStats = &adapter->tqd_start[i].stats;
15662306a36Sopenharmony_ci		drvTxStats = &adapter->tx_queue[i].stats;
15762306a36Sopenharmony_ci		stats->tx_packets += devTxStats->ucastPktsTxOK +
15862306a36Sopenharmony_ci				     devTxStats->mcastPktsTxOK +
15962306a36Sopenharmony_ci				     devTxStats->bcastPktsTxOK;
16062306a36Sopenharmony_ci		stats->tx_bytes += devTxStats->ucastBytesTxOK +
16162306a36Sopenharmony_ci				   devTxStats->mcastBytesTxOK +
16262306a36Sopenharmony_ci				   devTxStats->bcastBytesTxOK;
16362306a36Sopenharmony_ci		stats->tx_errors += devTxStats->pktsTxError;
16462306a36Sopenharmony_ci		stats->tx_dropped += drvTxStats->drop_total;
16562306a36Sopenharmony_ci	}
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	for (i = 0; i < adapter->num_rx_queues; i++) {
16862306a36Sopenharmony_ci		devRxStats = &adapter->rqd_start[i].stats;
16962306a36Sopenharmony_ci		drvRxStats = &adapter->rx_queue[i].stats;
17062306a36Sopenharmony_ci		stats->rx_packets += devRxStats->ucastPktsRxOK +
17162306a36Sopenharmony_ci				     devRxStats->mcastPktsRxOK +
17262306a36Sopenharmony_ci				     devRxStats->bcastPktsRxOK;
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci		stats->rx_bytes += devRxStats->ucastBytesRxOK +
17562306a36Sopenharmony_ci				   devRxStats->mcastBytesRxOK +
17662306a36Sopenharmony_ci				   devRxStats->bcastBytesRxOK;
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci		stats->rx_errors += devRxStats->pktsRxError;
17962306a36Sopenharmony_ci		stats->rx_dropped += drvRxStats->drop_total;
18062306a36Sopenharmony_ci		stats->multicast +=  devRxStats->mcastPktsRxOK;
18162306a36Sopenharmony_ci	}
18262306a36Sopenharmony_ci}
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_cistatic int
18562306a36Sopenharmony_civmxnet3_get_sset_count(struct net_device *netdev, int sset)
18662306a36Sopenharmony_ci{
18762306a36Sopenharmony_ci	struct vmxnet3_adapter *adapter = netdev_priv(netdev);
18862306a36Sopenharmony_ci	switch (sset) {
18962306a36Sopenharmony_ci	case ETH_SS_STATS:
19062306a36Sopenharmony_ci		return (ARRAY_SIZE(vmxnet3_tq_dev_stats) +
19162306a36Sopenharmony_ci			ARRAY_SIZE(vmxnet3_tq_driver_stats)) *
19262306a36Sopenharmony_ci		       adapter->num_tx_queues +
19362306a36Sopenharmony_ci		       (ARRAY_SIZE(vmxnet3_rq_dev_stats) +
19462306a36Sopenharmony_ci			ARRAY_SIZE(vmxnet3_rq_driver_stats)) *
19562306a36Sopenharmony_ci		       adapter->num_rx_queues +
19662306a36Sopenharmony_ci			ARRAY_SIZE(vmxnet3_global_stats);
19762306a36Sopenharmony_ci	default:
19862306a36Sopenharmony_ci		return -EOPNOTSUPP;
19962306a36Sopenharmony_ci	}
20062306a36Sopenharmony_ci}
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci/* This is a version 2 of the vmxnet3 ethtool_regs which goes hand in hand with
20462306a36Sopenharmony_ci * the version 2 of the vmxnet3 support for ethtool(8) --register-dump.
20562306a36Sopenharmony_ci * Therefore, if any registers are added, removed or modified, then a version
20662306a36Sopenharmony_ci * bump and a corresponding change in the vmxnet3 support for ethtool(8)
20762306a36Sopenharmony_ci * --register-dump would be required.
20862306a36Sopenharmony_ci */
20962306a36Sopenharmony_cistatic int
21062306a36Sopenharmony_civmxnet3_get_regs_len(struct net_device *netdev)
21162306a36Sopenharmony_ci{
21262306a36Sopenharmony_ci	struct vmxnet3_adapter *adapter = netdev_priv(netdev);
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	return ((9 /* BAR1 registers */ +
21562306a36Sopenharmony_ci		(1 + adapter->intr.num_intrs) +
21662306a36Sopenharmony_ci		(1 + adapter->num_tx_queues * 17 /* Tx queue registers */) +
21762306a36Sopenharmony_ci		(1 + adapter->num_rx_queues * 23 /* Rx queue registers */)) *
21862306a36Sopenharmony_ci		sizeof(u32));
21962306a36Sopenharmony_ci}
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_cistatic void
22362306a36Sopenharmony_civmxnet3_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo)
22462306a36Sopenharmony_ci{
22562306a36Sopenharmony_ci	struct vmxnet3_adapter *adapter = netdev_priv(netdev);
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	strscpy(drvinfo->driver, vmxnet3_driver_name, sizeof(drvinfo->driver));
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	strscpy(drvinfo->version, VMXNET3_DRIVER_VERSION_REPORT,
23062306a36Sopenharmony_ci		sizeof(drvinfo->version));
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	strscpy(drvinfo->bus_info, pci_name(adapter->pdev),
23362306a36Sopenharmony_ci		sizeof(drvinfo->bus_info));
23462306a36Sopenharmony_ci}
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_cistatic void
23862306a36Sopenharmony_civmxnet3_get_strings(struct net_device *netdev, u32 stringset, u8 *buf)
23962306a36Sopenharmony_ci{
24062306a36Sopenharmony_ci	struct vmxnet3_adapter *adapter = netdev_priv(netdev);
24162306a36Sopenharmony_ci	int i, j;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	if (stringset != ETH_SS_STATS)
24462306a36Sopenharmony_ci		return;
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	for (j = 0; j < adapter->num_tx_queues; j++) {
24762306a36Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(vmxnet3_tq_dev_stats); i++)
24862306a36Sopenharmony_ci			ethtool_sprintf(&buf, vmxnet3_tq_dev_stats[i].desc);
24962306a36Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(vmxnet3_tq_driver_stats); i++)
25062306a36Sopenharmony_ci			ethtool_sprintf(&buf, vmxnet3_tq_driver_stats[i].desc);
25162306a36Sopenharmony_ci	}
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	for (j = 0; j < adapter->num_rx_queues; j++) {
25462306a36Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(vmxnet3_rq_dev_stats); i++)
25562306a36Sopenharmony_ci			ethtool_sprintf(&buf, vmxnet3_rq_dev_stats[i].desc);
25662306a36Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(vmxnet3_rq_driver_stats); i++)
25762306a36Sopenharmony_ci			ethtool_sprintf(&buf, vmxnet3_rq_driver_stats[i].desc);
25862306a36Sopenharmony_ci	}
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(vmxnet3_global_stats); i++)
26162306a36Sopenharmony_ci		ethtool_sprintf(&buf, vmxnet3_global_stats[i].desc);
26262306a36Sopenharmony_ci}
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_cinetdev_features_t vmxnet3_fix_features(struct net_device *netdev,
26562306a36Sopenharmony_ci				       netdev_features_t features)
26662306a36Sopenharmony_ci{
26762306a36Sopenharmony_ci	struct vmxnet3_adapter *adapter = netdev_priv(netdev);
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	/* If Rx checksum is disabled, then LRO should also be disabled */
27062306a36Sopenharmony_ci	if (!(features & NETIF_F_RXCSUM))
27162306a36Sopenharmony_ci		features &= ~NETIF_F_LRO;
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	/* If XDP is enabled, then LRO should not be enabled */
27462306a36Sopenharmony_ci	if (vmxnet3_xdp_enabled(adapter) && (features & NETIF_F_LRO)) {
27562306a36Sopenharmony_ci		netdev_err(netdev, "LRO is not supported with XDP");
27662306a36Sopenharmony_ci		features &= ~NETIF_F_LRO;
27762306a36Sopenharmony_ci	}
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	return features;
28062306a36Sopenharmony_ci}
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_cinetdev_features_t vmxnet3_features_check(struct sk_buff *skb,
28362306a36Sopenharmony_ci					 struct net_device *netdev,
28462306a36Sopenharmony_ci					 netdev_features_t features)
28562306a36Sopenharmony_ci{
28662306a36Sopenharmony_ci	struct vmxnet3_adapter *adapter = netdev_priv(netdev);
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	/* Validate if the tunneled packet is being offloaded by the device */
28962306a36Sopenharmony_ci	if (VMXNET3_VERSION_GE_4(adapter) &&
29062306a36Sopenharmony_ci	    skb->encapsulation && skb->ip_summed == CHECKSUM_PARTIAL) {
29162306a36Sopenharmony_ci		u8 l4_proto = 0;
29262306a36Sopenharmony_ci		u16 port;
29362306a36Sopenharmony_ci		struct udphdr *udph;
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci		switch (vlan_get_protocol(skb)) {
29662306a36Sopenharmony_ci		case htons(ETH_P_IP):
29762306a36Sopenharmony_ci			l4_proto = ip_hdr(skb)->protocol;
29862306a36Sopenharmony_ci			break;
29962306a36Sopenharmony_ci		case htons(ETH_P_IPV6):
30062306a36Sopenharmony_ci			l4_proto = ipv6_hdr(skb)->nexthdr;
30162306a36Sopenharmony_ci			break;
30262306a36Sopenharmony_ci		default:
30362306a36Sopenharmony_ci			return features & ~(NETIF_F_CSUM_MASK | NETIF_F_GSO_MASK);
30462306a36Sopenharmony_ci		}
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci		switch (l4_proto) {
30762306a36Sopenharmony_ci		case IPPROTO_UDP:
30862306a36Sopenharmony_ci			udph = udp_hdr(skb);
30962306a36Sopenharmony_ci			port = be16_to_cpu(udph->dest);
31062306a36Sopenharmony_ci			/* Check if offloaded port is supported */
31162306a36Sopenharmony_ci			if (port != GENEVE_UDP_PORT &&
31262306a36Sopenharmony_ci			    port != IANA_VXLAN_UDP_PORT &&
31362306a36Sopenharmony_ci			    port != VXLAN_UDP_PORT) {
31462306a36Sopenharmony_ci				return features & ~(NETIF_F_CSUM_MASK | NETIF_F_GSO_MASK);
31562306a36Sopenharmony_ci			}
31662306a36Sopenharmony_ci			break;
31762306a36Sopenharmony_ci		default:
31862306a36Sopenharmony_ci			return features & ~(NETIF_F_CSUM_MASK | NETIF_F_GSO_MASK);
31962306a36Sopenharmony_ci		}
32062306a36Sopenharmony_ci	}
32162306a36Sopenharmony_ci	return features;
32262306a36Sopenharmony_ci}
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_cistatic void vmxnet3_enable_encap_offloads(struct net_device *netdev, netdev_features_t features)
32562306a36Sopenharmony_ci{
32662306a36Sopenharmony_ci	struct vmxnet3_adapter *adapter = netdev_priv(netdev);
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	if (VMXNET3_VERSION_GE_4(adapter)) {
32962306a36Sopenharmony_ci		netdev->hw_enc_features |= NETIF_F_SG | NETIF_F_RXCSUM |
33062306a36Sopenharmony_ci			NETIF_F_HW_CSUM | NETIF_F_HW_VLAN_CTAG_TX |
33162306a36Sopenharmony_ci			NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_TSO | NETIF_F_TSO6 |
33262306a36Sopenharmony_ci			NETIF_F_LRO;
33362306a36Sopenharmony_ci		if (features & NETIF_F_GSO_UDP_TUNNEL)
33462306a36Sopenharmony_ci			netdev->hw_enc_features |= NETIF_F_GSO_UDP_TUNNEL;
33562306a36Sopenharmony_ci		if (features & NETIF_F_GSO_UDP_TUNNEL_CSUM)
33662306a36Sopenharmony_ci			netdev->hw_enc_features |= NETIF_F_GSO_UDP_TUNNEL_CSUM;
33762306a36Sopenharmony_ci	}
33862306a36Sopenharmony_ci	if (VMXNET3_VERSION_GE_7(adapter)) {
33962306a36Sopenharmony_ci		unsigned long flags;
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci		if (vmxnet3_check_ptcapability(adapter->ptcap_supported[0],
34262306a36Sopenharmony_ci					       VMXNET3_CAP_GENEVE_CHECKSUM_OFFLOAD)) {
34362306a36Sopenharmony_ci			adapter->dev_caps[0] |= 1UL << VMXNET3_CAP_GENEVE_CHECKSUM_OFFLOAD;
34462306a36Sopenharmony_ci		}
34562306a36Sopenharmony_ci		if (vmxnet3_check_ptcapability(adapter->ptcap_supported[0],
34662306a36Sopenharmony_ci					       VMXNET3_CAP_VXLAN_CHECKSUM_OFFLOAD)) {
34762306a36Sopenharmony_ci			adapter->dev_caps[0] |= 1UL << VMXNET3_CAP_VXLAN_CHECKSUM_OFFLOAD;
34862306a36Sopenharmony_ci		}
34962306a36Sopenharmony_ci		if (vmxnet3_check_ptcapability(adapter->ptcap_supported[0],
35062306a36Sopenharmony_ci					       VMXNET3_CAP_GENEVE_TSO)) {
35162306a36Sopenharmony_ci			adapter->dev_caps[0] |= 1UL << VMXNET3_CAP_GENEVE_TSO;
35262306a36Sopenharmony_ci		}
35362306a36Sopenharmony_ci		if (vmxnet3_check_ptcapability(adapter->ptcap_supported[0],
35462306a36Sopenharmony_ci					       VMXNET3_CAP_VXLAN_TSO)) {
35562306a36Sopenharmony_ci			adapter->dev_caps[0] |= 1UL << VMXNET3_CAP_VXLAN_TSO;
35662306a36Sopenharmony_ci		}
35762306a36Sopenharmony_ci		if (vmxnet3_check_ptcapability(adapter->ptcap_supported[0],
35862306a36Sopenharmony_ci					       VMXNET3_CAP_GENEVE_OUTER_CHECKSUM_OFFLOAD)) {
35962306a36Sopenharmony_ci			adapter->dev_caps[0] |= 1UL << VMXNET3_CAP_GENEVE_OUTER_CHECKSUM_OFFLOAD;
36062306a36Sopenharmony_ci		}
36162306a36Sopenharmony_ci		if (vmxnet3_check_ptcapability(adapter->ptcap_supported[0],
36262306a36Sopenharmony_ci					       VMXNET3_CAP_VXLAN_OUTER_CHECKSUM_OFFLOAD)) {
36362306a36Sopenharmony_ci			adapter->dev_caps[0] |= 1UL << VMXNET3_CAP_VXLAN_OUTER_CHECKSUM_OFFLOAD;
36462306a36Sopenharmony_ci		}
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci		VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_DCR, adapter->dev_caps[0]);
36762306a36Sopenharmony_ci		spin_lock_irqsave(&adapter->cmd_lock, flags);
36862306a36Sopenharmony_ci		VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, VMXNET3_CMD_GET_DCR0_REG);
36962306a36Sopenharmony_ci		adapter->dev_caps[0] = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_CMD);
37062306a36Sopenharmony_ci		spin_unlock_irqrestore(&adapter->cmd_lock, flags);
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci		if (!(adapter->dev_caps[0] & (1UL << VMXNET3_CAP_GENEVE_CHECKSUM_OFFLOAD)) &&
37362306a36Sopenharmony_ci		    !(adapter->dev_caps[0] & (1UL << VMXNET3_CAP_VXLAN_CHECKSUM_OFFLOAD)) &&
37462306a36Sopenharmony_ci		    !(adapter->dev_caps[0] & (1UL << VMXNET3_CAP_GENEVE_TSO)) &&
37562306a36Sopenharmony_ci		    !(adapter->dev_caps[0] & (1UL << VMXNET3_CAP_VXLAN_TSO))) {
37662306a36Sopenharmony_ci			netdev->hw_enc_features &= ~NETIF_F_GSO_UDP_TUNNEL;
37762306a36Sopenharmony_ci		}
37862306a36Sopenharmony_ci		if (!(adapter->dev_caps[0] & (1UL << VMXNET3_CAP_GENEVE_OUTER_CHECKSUM_OFFLOAD)) &&
37962306a36Sopenharmony_ci		    !(adapter->dev_caps[0] & (1UL << VMXNET3_CAP_VXLAN_OUTER_CHECKSUM_OFFLOAD))) {
38062306a36Sopenharmony_ci			netdev->hw_enc_features &= ~NETIF_F_GSO_UDP_TUNNEL_CSUM;
38162306a36Sopenharmony_ci		}
38262306a36Sopenharmony_ci	}
38362306a36Sopenharmony_ci}
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_cistatic void vmxnet3_disable_encap_offloads(struct net_device *netdev)
38662306a36Sopenharmony_ci{
38762306a36Sopenharmony_ci	struct vmxnet3_adapter *adapter = netdev_priv(netdev);
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	if (VMXNET3_VERSION_GE_4(adapter)) {
39062306a36Sopenharmony_ci		netdev->hw_enc_features &= ~(NETIF_F_SG | NETIF_F_RXCSUM |
39162306a36Sopenharmony_ci			NETIF_F_HW_CSUM | NETIF_F_HW_VLAN_CTAG_TX |
39262306a36Sopenharmony_ci			NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_TSO | NETIF_F_TSO6 |
39362306a36Sopenharmony_ci			NETIF_F_LRO | NETIF_F_GSO_UDP_TUNNEL |
39462306a36Sopenharmony_ci			NETIF_F_GSO_UDP_TUNNEL_CSUM);
39562306a36Sopenharmony_ci	}
39662306a36Sopenharmony_ci	if (VMXNET3_VERSION_GE_7(adapter)) {
39762306a36Sopenharmony_ci		unsigned long flags;
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci		adapter->dev_caps[0] &= ~(1UL << VMXNET3_CAP_GENEVE_CHECKSUM_OFFLOAD |
40062306a36Sopenharmony_ci					  1UL << VMXNET3_CAP_VXLAN_CHECKSUM_OFFLOAD  |
40162306a36Sopenharmony_ci					  1UL << VMXNET3_CAP_GENEVE_TSO |
40262306a36Sopenharmony_ci					  1UL << VMXNET3_CAP_VXLAN_TSO  |
40362306a36Sopenharmony_ci					  1UL << VMXNET3_CAP_GENEVE_OUTER_CHECKSUM_OFFLOAD |
40462306a36Sopenharmony_ci					  1UL << VMXNET3_CAP_VXLAN_OUTER_CHECKSUM_OFFLOAD);
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci		VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_DCR, adapter->dev_caps[0]);
40762306a36Sopenharmony_ci		spin_lock_irqsave(&adapter->cmd_lock, flags);
40862306a36Sopenharmony_ci		VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, VMXNET3_CMD_GET_DCR0_REG);
40962306a36Sopenharmony_ci		adapter->dev_caps[0] = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_CMD);
41062306a36Sopenharmony_ci		spin_unlock_irqrestore(&adapter->cmd_lock, flags);
41162306a36Sopenharmony_ci	}
41262306a36Sopenharmony_ci}
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ciint vmxnet3_set_features(struct net_device *netdev, netdev_features_t features)
41562306a36Sopenharmony_ci{
41662306a36Sopenharmony_ci	struct vmxnet3_adapter *adapter = netdev_priv(netdev);
41762306a36Sopenharmony_ci	unsigned long flags;
41862306a36Sopenharmony_ci	netdev_features_t changed = features ^ netdev->features;
41962306a36Sopenharmony_ci	netdev_features_t tun_offload_mask = NETIF_F_GSO_UDP_TUNNEL |
42062306a36Sopenharmony_ci					     NETIF_F_GSO_UDP_TUNNEL_CSUM;
42162306a36Sopenharmony_ci	u8 udp_tun_enabled = (netdev->features & tun_offload_mask) != 0;
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	if (changed & (NETIF_F_RXCSUM | NETIF_F_LRO |
42462306a36Sopenharmony_ci		       NETIF_F_HW_VLAN_CTAG_RX | tun_offload_mask)) {
42562306a36Sopenharmony_ci		if (features & NETIF_F_RXCSUM)
42662306a36Sopenharmony_ci			adapter->shared->devRead.misc.uptFeatures |=
42762306a36Sopenharmony_ci			UPT1_F_RXCSUM;
42862306a36Sopenharmony_ci		else
42962306a36Sopenharmony_ci			adapter->shared->devRead.misc.uptFeatures &=
43062306a36Sopenharmony_ci			~UPT1_F_RXCSUM;
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci		/* update hardware LRO capability accordingly */
43362306a36Sopenharmony_ci		if (features & NETIF_F_LRO)
43462306a36Sopenharmony_ci			adapter->shared->devRead.misc.uptFeatures |=
43562306a36Sopenharmony_ci							UPT1_F_LRO;
43662306a36Sopenharmony_ci		else
43762306a36Sopenharmony_ci			adapter->shared->devRead.misc.uptFeatures &=
43862306a36Sopenharmony_ci							~UPT1_F_LRO;
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci		if (features & NETIF_F_HW_VLAN_CTAG_RX)
44162306a36Sopenharmony_ci			adapter->shared->devRead.misc.uptFeatures |=
44262306a36Sopenharmony_ci			UPT1_F_RXVLAN;
44362306a36Sopenharmony_ci		else
44462306a36Sopenharmony_ci			adapter->shared->devRead.misc.uptFeatures &=
44562306a36Sopenharmony_ci			~UPT1_F_RXVLAN;
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci		if ((features & tun_offload_mask) != 0) {
44862306a36Sopenharmony_ci			vmxnet3_enable_encap_offloads(netdev, features);
44962306a36Sopenharmony_ci			adapter->shared->devRead.misc.uptFeatures |=
45062306a36Sopenharmony_ci			UPT1_F_RXINNEROFLD;
45162306a36Sopenharmony_ci		} else if ((features & tun_offload_mask) == 0 &&
45262306a36Sopenharmony_ci			   udp_tun_enabled) {
45362306a36Sopenharmony_ci			vmxnet3_disable_encap_offloads(netdev);
45462306a36Sopenharmony_ci			adapter->shared->devRead.misc.uptFeatures &=
45562306a36Sopenharmony_ci			~UPT1_F_RXINNEROFLD;
45662306a36Sopenharmony_ci		}
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci		spin_lock_irqsave(&adapter->cmd_lock, flags);
45962306a36Sopenharmony_ci		VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD,
46062306a36Sopenharmony_ci				       VMXNET3_CMD_UPDATE_FEATURE);
46162306a36Sopenharmony_ci		spin_unlock_irqrestore(&adapter->cmd_lock, flags);
46262306a36Sopenharmony_ci	}
46362306a36Sopenharmony_ci	return 0;
46462306a36Sopenharmony_ci}
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_cistatic void
46762306a36Sopenharmony_civmxnet3_get_ethtool_stats(struct net_device *netdev,
46862306a36Sopenharmony_ci			  struct ethtool_stats *stats, u64  *buf)
46962306a36Sopenharmony_ci{
47062306a36Sopenharmony_ci	struct vmxnet3_adapter *adapter = netdev_priv(netdev);
47162306a36Sopenharmony_ci	unsigned long flags;
47262306a36Sopenharmony_ci	u8 *base;
47362306a36Sopenharmony_ci	int i;
47462306a36Sopenharmony_ci	int j = 0;
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	spin_lock_irqsave(&adapter->cmd_lock, flags);
47762306a36Sopenharmony_ci	VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, VMXNET3_CMD_GET_STATS);
47862306a36Sopenharmony_ci	spin_unlock_irqrestore(&adapter->cmd_lock, flags);
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	/* this does assume each counter is 64-bit wide */
48162306a36Sopenharmony_ci	for (j = 0; j < adapter->num_tx_queues; j++) {
48262306a36Sopenharmony_ci		base = (u8 *)&adapter->tqd_start[j].stats;
48362306a36Sopenharmony_ci		*buf++ = (u64)j;
48462306a36Sopenharmony_ci		for (i = 1; i < ARRAY_SIZE(vmxnet3_tq_dev_stats); i++)
48562306a36Sopenharmony_ci			*buf++ = *(u64 *)(base +
48662306a36Sopenharmony_ci					  vmxnet3_tq_dev_stats[i].offset);
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci		base = (u8 *)&adapter->tx_queue[j].stats;
48962306a36Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(vmxnet3_tq_driver_stats); i++)
49062306a36Sopenharmony_ci			*buf++ = *(u64 *)(base +
49162306a36Sopenharmony_ci					  vmxnet3_tq_driver_stats[i].offset);
49262306a36Sopenharmony_ci	}
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	for (j = 0; j < adapter->num_rx_queues; j++) {
49562306a36Sopenharmony_ci		base = (u8 *)&adapter->rqd_start[j].stats;
49662306a36Sopenharmony_ci		*buf++ = (u64) j;
49762306a36Sopenharmony_ci		for (i = 1; i < ARRAY_SIZE(vmxnet3_rq_dev_stats); i++)
49862306a36Sopenharmony_ci			*buf++ = *(u64 *)(base +
49962306a36Sopenharmony_ci					  vmxnet3_rq_dev_stats[i].offset);
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci		base = (u8 *)&adapter->rx_queue[j].stats;
50262306a36Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(vmxnet3_rq_driver_stats); i++)
50362306a36Sopenharmony_ci			*buf++ = *(u64 *)(base +
50462306a36Sopenharmony_ci					  vmxnet3_rq_driver_stats[i].offset);
50562306a36Sopenharmony_ci	}
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	base = (u8 *)adapter;
50862306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(vmxnet3_global_stats); i++)
50962306a36Sopenharmony_ci		*buf++ = *(u64 *)(base + vmxnet3_global_stats[i].offset);
51062306a36Sopenharmony_ci}
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci/* This is a version 2 of the vmxnet3 ethtool_regs which goes hand in hand with
51462306a36Sopenharmony_ci * the version 2 of the vmxnet3 support for ethtool(8) --register-dump.
51562306a36Sopenharmony_ci * Therefore, if any registers are added, removed or modified, then a version
51662306a36Sopenharmony_ci * bump and a corresponding change in the vmxnet3 support for ethtool(8)
51762306a36Sopenharmony_ci * --register-dump would be required.
51862306a36Sopenharmony_ci */
51962306a36Sopenharmony_cistatic void
52062306a36Sopenharmony_civmxnet3_get_regs(struct net_device *netdev, struct ethtool_regs *regs, void *p)
52162306a36Sopenharmony_ci{
52262306a36Sopenharmony_ci	struct vmxnet3_adapter *adapter = netdev_priv(netdev);
52362306a36Sopenharmony_ci	u32 *buf = p;
52462306a36Sopenharmony_ci	int i = 0, j = 0;
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	memset(p, 0, vmxnet3_get_regs_len(netdev));
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	regs->version = 2;
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	/* Update vmxnet3_get_regs_len if we want to dump more registers */
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	buf[j++] = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_VRRS);
53362306a36Sopenharmony_ci	buf[j++] = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_UVRS);
53462306a36Sopenharmony_ci	buf[j++] = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_DSAL);
53562306a36Sopenharmony_ci	buf[j++] = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_DSAH);
53662306a36Sopenharmony_ci	buf[j++] = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_CMD);
53762306a36Sopenharmony_ci	buf[j++] = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_MACL);
53862306a36Sopenharmony_ci	buf[j++] = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_MACH);
53962306a36Sopenharmony_ci	buf[j++] = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_ICR);
54062306a36Sopenharmony_ci	buf[j++] = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_ECR);
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	buf[j++] = adapter->intr.num_intrs;
54362306a36Sopenharmony_ci	for (i = 0; i < adapter->intr.num_intrs; i++) {
54462306a36Sopenharmony_ci		buf[j++] = VMXNET3_READ_BAR0_REG(adapter, VMXNET3_REG_IMR
54562306a36Sopenharmony_ci						 + i * VMXNET3_REG_ALIGN);
54662306a36Sopenharmony_ci	}
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	buf[j++] = adapter->num_tx_queues;
54962306a36Sopenharmony_ci	for (i = 0; i < adapter->num_tx_queues; i++) {
55062306a36Sopenharmony_ci		struct vmxnet3_tx_queue *tq = &adapter->tx_queue[i];
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci		buf[j++] = VMXNET3_READ_BAR0_REG(adapter, adapter->tx_prod_offset +
55362306a36Sopenharmony_ci						 i * VMXNET3_REG_ALIGN);
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci		buf[j++] = VMXNET3_GET_ADDR_LO(tq->tx_ring.basePA);
55662306a36Sopenharmony_ci		buf[j++] = VMXNET3_GET_ADDR_HI(tq->tx_ring.basePA);
55762306a36Sopenharmony_ci		buf[j++] = tq->tx_ring.size;
55862306a36Sopenharmony_ci		buf[j++] = tq->tx_ring.next2fill;
55962306a36Sopenharmony_ci		buf[j++] = tq->tx_ring.next2comp;
56062306a36Sopenharmony_ci		buf[j++] = tq->tx_ring.gen;
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci		buf[j++] = VMXNET3_GET_ADDR_LO(tq->data_ring.basePA);
56362306a36Sopenharmony_ci		buf[j++] = VMXNET3_GET_ADDR_HI(tq->data_ring.basePA);
56462306a36Sopenharmony_ci		buf[j++] = tq->data_ring.size;
56562306a36Sopenharmony_ci		buf[j++] = tq->txdata_desc_size;
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci		buf[j++] = VMXNET3_GET_ADDR_LO(tq->comp_ring.basePA);
56862306a36Sopenharmony_ci		buf[j++] = VMXNET3_GET_ADDR_HI(tq->comp_ring.basePA);
56962306a36Sopenharmony_ci		buf[j++] = tq->comp_ring.size;
57062306a36Sopenharmony_ci		buf[j++] = tq->comp_ring.next2proc;
57162306a36Sopenharmony_ci		buf[j++] = tq->comp_ring.gen;
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci		buf[j++] = tq->stopped;
57462306a36Sopenharmony_ci	}
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci	buf[j++] = adapter->num_rx_queues;
57762306a36Sopenharmony_ci	for (i = 0; i < adapter->num_rx_queues; i++) {
57862306a36Sopenharmony_ci		struct vmxnet3_rx_queue *rq = &adapter->rx_queue[i];
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci		buf[j++] =  VMXNET3_READ_BAR0_REG(adapter, adapter->rx_prod_offset +
58162306a36Sopenharmony_ci						  i * VMXNET3_REG_ALIGN);
58262306a36Sopenharmony_ci		buf[j++] =  VMXNET3_READ_BAR0_REG(adapter, adapter->rx_prod2_offset +
58362306a36Sopenharmony_ci						  i * VMXNET3_REG_ALIGN);
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci		buf[j++] = VMXNET3_GET_ADDR_LO(rq->rx_ring[0].basePA);
58662306a36Sopenharmony_ci		buf[j++] = VMXNET3_GET_ADDR_HI(rq->rx_ring[0].basePA);
58762306a36Sopenharmony_ci		buf[j++] = rq->rx_ring[0].size;
58862306a36Sopenharmony_ci		buf[j++] = rq->rx_ring[0].next2fill;
58962306a36Sopenharmony_ci		buf[j++] = rq->rx_ring[0].next2comp;
59062306a36Sopenharmony_ci		buf[j++] = rq->rx_ring[0].gen;
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci		buf[j++] = VMXNET3_GET_ADDR_LO(rq->rx_ring[1].basePA);
59362306a36Sopenharmony_ci		buf[j++] = VMXNET3_GET_ADDR_HI(rq->rx_ring[1].basePA);
59462306a36Sopenharmony_ci		buf[j++] = rq->rx_ring[1].size;
59562306a36Sopenharmony_ci		buf[j++] = rq->rx_ring[1].next2fill;
59662306a36Sopenharmony_ci		buf[j++] = rq->rx_ring[1].next2comp;
59762306a36Sopenharmony_ci		buf[j++] = rq->rx_ring[1].gen;
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci		buf[j++] = VMXNET3_GET_ADDR_LO(rq->data_ring.basePA);
60062306a36Sopenharmony_ci		buf[j++] = VMXNET3_GET_ADDR_HI(rq->data_ring.basePA);
60162306a36Sopenharmony_ci		buf[j++] = rq->rx_ring[0].size;
60262306a36Sopenharmony_ci		buf[j++] = rq->data_ring.desc_size;
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci		buf[j++] = VMXNET3_GET_ADDR_LO(rq->comp_ring.basePA);
60562306a36Sopenharmony_ci		buf[j++] = VMXNET3_GET_ADDR_HI(rq->comp_ring.basePA);
60662306a36Sopenharmony_ci		buf[j++] = rq->comp_ring.size;
60762306a36Sopenharmony_ci		buf[j++] = rq->comp_ring.next2proc;
60862306a36Sopenharmony_ci		buf[j++] = rq->comp_ring.gen;
60962306a36Sopenharmony_ci	}
61062306a36Sopenharmony_ci}
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_cistatic void
61462306a36Sopenharmony_civmxnet3_get_wol(struct net_device *netdev, struct ethtool_wolinfo *wol)
61562306a36Sopenharmony_ci{
61662306a36Sopenharmony_ci	struct vmxnet3_adapter *adapter = netdev_priv(netdev);
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci	wol->supported = WAKE_UCAST | WAKE_ARP | WAKE_MAGIC;
61962306a36Sopenharmony_ci	wol->wolopts = adapter->wol;
62062306a36Sopenharmony_ci}
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_cistatic int
62462306a36Sopenharmony_civmxnet3_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol)
62562306a36Sopenharmony_ci{
62662306a36Sopenharmony_ci	struct vmxnet3_adapter *adapter = netdev_priv(netdev);
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci	if (wol->wolopts & (WAKE_PHY | WAKE_MCAST | WAKE_BCAST |
62962306a36Sopenharmony_ci			    WAKE_MAGICSECURE)) {
63062306a36Sopenharmony_ci		return -EOPNOTSUPP;
63162306a36Sopenharmony_ci	}
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci	adapter->wol = wol->wolopts;
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci	device_set_wakeup_enable(&adapter->pdev->dev, adapter->wol);
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci	return 0;
63862306a36Sopenharmony_ci}
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_cistatic int
64262306a36Sopenharmony_civmxnet3_get_link_ksettings(struct net_device *netdev,
64362306a36Sopenharmony_ci			   struct ethtool_link_ksettings *ecmd)
64462306a36Sopenharmony_ci{
64562306a36Sopenharmony_ci	struct vmxnet3_adapter *adapter = netdev_priv(netdev);
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci	ethtool_link_ksettings_zero_link_mode(ecmd, supported);
64862306a36Sopenharmony_ci	ethtool_link_ksettings_add_link_mode(ecmd, supported, 10000baseT_Full);
64962306a36Sopenharmony_ci	ethtool_link_ksettings_add_link_mode(ecmd, supported, 1000baseT_Full);
65062306a36Sopenharmony_ci	ethtool_link_ksettings_add_link_mode(ecmd, supported, TP);
65162306a36Sopenharmony_ci	ethtool_link_ksettings_zero_link_mode(ecmd, advertising);
65262306a36Sopenharmony_ci	ethtool_link_ksettings_add_link_mode(ecmd, advertising, TP);
65362306a36Sopenharmony_ci	ecmd->base.port = PORT_TP;
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci	if (adapter->link_speed) {
65662306a36Sopenharmony_ci		ecmd->base.speed = adapter->link_speed;
65762306a36Sopenharmony_ci		ecmd->base.duplex = DUPLEX_FULL;
65862306a36Sopenharmony_ci	} else {
65962306a36Sopenharmony_ci		ecmd->base.speed = SPEED_UNKNOWN;
66062306a36Sopenharmony_ci		ecmd->base.duplex = DUPLEX_UNKNOWN;
66162306a36Sopenharmony_ci	}
66262306a36Sopenharmony_ci	return 0;
66362306a36Sopenharmony_ci}
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_cistatic void
66662306a36Sopenharmony_civmxnet3_get_ringparam(struct net_device *netdev,
66762306a36Sopenharmony_ci		      struct ethtool_ringparam *param,
66862306a36Sopenharmony_ci		      struct kernel_ethtool_ringparam *kernel_param,
66962306a36Sopenharmony_ci		      struct netlink_ext_ack *extack)
67062306a36Sopenharmony_ci{
67162306a36Sopenharmony_ci	struct vmxnet3_adapter *adapter = netdev_priv(netdev);
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci	param->rx_max_pending = VMXNET3_RX_RING_MAX_SIZE;
67462306a36Sopenharmony_ci	param->tx_max_pending = VMXNET3_TX_RING_MAX_SIZE;
67562306a36Sopenharmony_ci	param->rx_mini_max_pending = VMXNET3_VERSION_GE_3(adapter) ?
67662306a36Sopenharmony_ci		VMXNET3_RXDATA_DESC_MAX_SIZE : 0;
67762306a36Sopenharmony_ci	param->rx_jumbo_max_pending = VMXNET3_RX_RING2_MAX_SIZE;
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	param->rx_pending = adapter->rx_ring_size;
68062306a36Sopenharmony_ci	param->tx_pending = adapter->tx_ring_size;
68162306a36Sopenharmony_ci	param->rx_mini_pending = VMXNET3_VERSION_GE_3(adapter) ?
68262306a36Sopenharmony_ci		adapter->rxdata_desc_size : 0;
68362306a36Sopenharmony_ci	param->rx_jumbo_pending = adapter->rx_ring2_size;
68462306a36Sopenharmony_ci}
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_cistatic int
68762306a36Sopenharmony_civmxnet3_set_ringparam(struct net_device *netdev,
68862306a36Sopenharmony_ci		      struct ethtool_ringparam *param,
68962306a36Sopenharmony_ci		      struct kernel_ethtool_ringparam *kernel_param,
69062306a36Sopenharmony_ci		      struct netlink_ext_ack *extack)
69162306a36Sopenharmony_ci{
69262306a36Sopenharmony_ci	struct vmxnet3_adapter *adapter = netdev_priv(netdev);
69362306a36Sopenharmony_ci	u32 new_tx_ring_size, new_rx_ring_size, new_rx_ring2_size;
69462306a36Sopenharmony_ci	u16 new_rxdata_desc_size;
69562306a36Sopenharmony_ci	u32 sz;
69662306a36Sopenharmony_ci	int err = 0;
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci	if (param->tx_pending == 0 || param->tx_pending >
69962306a36Sopenharmony_ci						VMXNET3_TX_RING_MAX_SIZE)
70062306a36Sopenharmony_ci		return -EINVAL;
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci	if (param->rx_pending == 0 || param->rx_pending >
70362306a36Sopenharmony_ci						VMXNET3_RX_RING_MAX_SIZE)
70462306a36Sopenharmony_ci		return -EINVAL;
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci	if (param->rx_jumbo_pending == 0 ||
70762306a36Sopenharmony_ci	    param->rx_jumbo_pending > VMXNET3_RX_RING2_MAX_SIZE)
70862306a36Sopenharmony_ci		return -EINVAL;
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci	/* if adapter not yet initialized, do nothing */
71162306a36Sopenharmony_ci	if (adapter->rx_buf_per_pkt == 0) {
71262306a36Sopenharmony_ci		netdev_err(netdev, "adapter not completely initialized, "
71362306a36Sopenharmony_ci			   "ring size cannot be changed yet\n");
71462306a36Sopenharmony_ci		return -EOPNOTSUPP;
71562306a36Sopenharmony_ci	}
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci	if (VMXNET3_VERSION_GE_3(adapter)) {
71862306a36Sopenharmony_ci		if (param->rx_mini_pending > VMXNET3_RXDATA_DESC_MAX_SIZE)
71962306a36Sopenharmony_ci			return -EINVAL;
72062306a36Sopenharmony_ci	} else if (param->rx_mini_pending != 0) {
72162306a36Sopenharmony_ci		return -EINVAL;
72262306a36Sopenharmony_ci	}
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci	/* round it up to a multiple of VMXNET3_RING_SIZE_ALIGN */
72562306a36Sopenharmony_ci	new_tx_ring_size = (param->tx_pending + VMXNET3_RING_SIZE_MASK) &
72662306a36Sopenharmony_ci							~VMXNET3_RING_SIZE_MASK;
72762306a36Sopenharmony_ci	new_tx_ring_size = min_t(u32, new_tx_ring_size,
72862306a36Sopenharmony_ci				 VMXNET3_TX_RING_MAX_SIZE);
72962306a36Sopenharmony_ci	if (new_tx_ring_size > VMXNET3_TX_RING_MAX_SIZE || (new_tx_ring_size %
73062306a36Sopenharmony_ci						VMXNET3_RING_SIZE_ALIGN) != 0)
73162306a36Sopenharmony_ci		return -EINVAL;
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci	/* ring0 has to be a multiple of
73462306a36Sopenharmony_ci	 * rx_buf_per_pkt * VMXNET3_RING_SIZE_ALIGN
73562306a36Sopenharmony_ci	 */
73662306a36Sopenharmony_ci	sz = adapter->rx_buf_per_pkt * VMXNET3_RING_SIZE_ALIGN;
73762306a36Sopenharmony_ci	new_rx_ring_size = (param->rx_pending + sz - 1) / sz * sz;
73862306a36Sopenharmony_ci	new_rx_ring_size = min_t(u32, new_rx_ring_size,
73962306a36Sopenharmony_ci				 VMXNET3_RX_RING_MAX_SIZE / sz * sz);
74062306a36Sopenharmony_ci	if (new_rx_ring_size > VMXNET3_RX_RING_MAX_SIZE || (new_rx_ring_size %
74162306a36Sopenharmony_ci							   sz) != 0)
74262306a36Sopenharmony_ci		return -EINVAL;
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	/* ring2 has to be a multiple of VMXNET3_RING_SIZE_ALIGN */
74562306a36Sopenharmony_ci	new_rx_ring2_size = (param->rx_jumbo_pending + VMXNET3_RING_SIZE_MASK) &
74662306a36Sopenharmony_ci				~VMXNET3_RING_SIZE_MASK;
74762306a36Sopenharmony_ci	new_rx_ring2_size = min_t(u32, new_rx_ring2_size,
74862306a36Sopenharmony_ci				  VMXNET3_RX_RING2_MAX_SIZE);
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci	/* For v7 and later, keep ring size power of 2 for UPT */
75162306a36Sopenharmony_ci	if (VMXNET3_VERSION_GE_7(adapter)) {
75262306a36Sopenharmony_ci		new_tx_ring_size = rounddown_pow_of_two(new_tx_ring_size);
75362306a36Sopenharmony_ci		new_rx_ring_size = rounddown_pow_of_two(new_rx_ring_size);
75462306a36Sopenharmony_ci		new_rx_ring2_size = rounddown_pow_of_two(new_rx_ring2_size);
75562306a36Sopenharmony_ci	}
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci	/* rx data ring buffer size has to be a multiple of
75862306a36Sopenharmony_ci	 * VMXNET3_RXDATA_DESC_SIZE_ALIGN
75962306a36Sopenharmony_ci	 */
76062306a36Sopenharmony_ci	new_rxdata_desc_size =
76162306a36Sopenharmony_ci		(param->rx_mini_pending + VMXNET3_RXDATA_DESC_SIZE_MASK) &
76262306a36Sopenharmony_ci		~VMXNET3_RXDATA_DESC_SIZE_MASK;
76362306a36Sopenharmony_ci	new_rxdata_desc_size = min_t(u16, new_rxdata_desc_size,
76462306a36Sopenharmony_ci				     VMXNET3_RXDATA_DESC_MAX_SIZE);
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci	if (new_tx_ring_size == adapter->tx_ring_size &&
76762306a36Sopenharmony_ci	    new_rx_ring_size == adapter->rx_ring_size &&
76862306a36Sopenharmony_ci	    new_rx_ring2_size == adapter->rx_ring2_size &&
76962306a36Sopenharmony_ci	    new_rxdata_desc_size == adapter->rxdata_desc_size) {
77062306a36Sopenharmony_ci		return 0;
77162306a36Sopenharmony_ci	}
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci	/*
77462306a36Sopenharmony_ci	 * Reset_work may be in the middle of resetting the device, wait for its
77562306a36Sopenharmony_ci	 * completion.
77662306a36Sopenharmony_ci	 */
77762306a36Sopenharmony_ci	while (test_and_set_bit(VMXNET3_STATE_BIT_RESETTING, &adapter->state))
77862306a36Sopenharmony_ci		usleep_range(1000, 2000);
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_ci	if (netif_running(netdev)) {
78162306a36Sopenharmony_ci		vmxnet3_quiesce_dev(adapter);
78262306a36Sopenharmony_ci		vmxnet3_reset_dev(adapter);
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci		/* recreate the rx queue and the tx queue based on the
78562306a36Sopenharmony_ci		 * new sizes */
78662306a36Sopenharmony_ci		vmxnet3_tq_destroy_all(adapter);
78762306a36Sopenharmony_ci		vmxnet3_rq_destroy_all(adapter);
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_ci		err = vmxnet3_create_queues(adapter, new_tx_ring_size,
79062306a36Sopenharmony_ci					    new_rx_ring_size, new_rx_ring2_size,
79162306a36Sopenharmony_ci					    adapter->txdata_desc_size,
79262306a36Sopenharmony_ci					    new_rxdata_desc_size);
79362306a36Sopenharmony_ci		if (err) {
79462306a36Sopenharmony_ci			/* failed, most likely because of OOM, try default
79562306a36Sopenharmony_ci			 * size */
79662306a36Sopenharmony_ci			netdev_err(netdev, "failed to apply new sizes, "
79762306a36Sopenharmony_ci				   "try the default ones\n");
79862306a36Sopenharmony_ci			new_rx_ring_size = VMXNET3_DEF_RX_RING_SIZE;
79962306a36Sopenharmony_ci			new_rx_ring2_size = VMXNET3_DEF_RX_RING2_SIZE;
80062306a36Sopenharmony_ci			new_tx_ring_size = VMXNET3_DEF_TX_RING_SIZE;
80162306a36Sopenharmony_ci			new_rxdata_desc_size = VMXNET3_VERSION_GE_3(adapter) ?
80262306a36Sopenharmony_ci				VMXNET3_DEF_RXDATA_DESC_SIZE : 0;
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci			err = vmxnet3_create_queues(adapter,
80562306a36Sopenharmony_ci						    new_tx_ring_size,
80662306a36Sopenharmony_ci						    new_rx_ring_size,
80762306a36Sopenharmony_ci						    new_rx_ring2_size,
80862306a36Sopenharmony_ci						    adapter->txdata_desc_size,
80962306a36Sopenharmony_ci						    new_rxdata_desc_size);
81062306a36Sopenharmony_ci			if (err) {
81162306a36Sopenharmony_ci				netdev_err(netdev, "failed to create queues "
81262306a36Sopenharmony_ci					   "with default sizes. Closing it\n");
81362306a36Sopenharmony_ci				goto out;
81462306a36Sopenharmony_ci			}
81562306a36Sopenharmony_ci		}
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci		err = vmxnet3_activate_dev(adapter);
81862306a36Sopenharmony_ci		if (err)
81962306a36Sopenharmony_ci			netdev_err(netdev, "failed to re-activate, error %d."
82062306a36Sopenharmony_ci				   " Closing it\n", err);
82162306a36Sopenharmony_ci	}
82262306a36Sopenharmony_ci	adapter->tx_ring_size = new_tx_ring_size;
82362306a36Sopenharmony_ci	adapter->rx_ring_size = new_rx_ring_size;
82462306a36Sopenharmony_ci	adapter->rx_ring2_size = new_rx_ring2_size;
82562306a36Sopenharmony_ci	adapter->rxdata_desc_size = new_rxdata_desc_size;
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ciout:
82862306a36Sopenharmony_ci	clear_bit(VMXNET3_STATE_BIT_RESETTING, &adapter->state);
82962306a36Sopenharmony_ci	if (err)
83062306a36Sopenharmony_ci		vmxnet3_force_close(adapter);
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci	return err;
83362306a36Sopenharmony_ci}
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_cistatic int
83662306a36Sopenharmony_civmxnet3_get_rss_hash_opts(struct vmxnet3_adapter *adapter,
83762306a36Sopenharmony_ci			  struct ethtool_rxnfc *info)
83862306a36Sopenharmony_ci{
83962306a36Sopenharmony_ci	enum Vmxnet3_RSSField rss_fields;
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_ci	if (netif_running(adapter->netdev)) {
84262306a36Sopenharmony_ci		unsigned long flags;
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci		spin_lock_irqsave(&adapter->cmd_lock, flags);
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci		VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD,
84762306a36Sopenharmony_ci				       VMXNET3_CMD_GET_RSS_FIELDS);
84862306a36Sopenharmony_ci		rss_fields = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_CMD);
84962306a36Sopenharmony_ci		spin_unlock_irqrestore(&adapter->cmd_lock, flags);
85062306a36Sopenharmony_ci	} else {
85162306a36Sopenharmony_ci		rss_fields = adapter->rss_fields;
85262306a36Sopenharmony_ci	}
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci	info->data = 0;
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_ci	/* Report default options for RSS on vmxnet3 */
85762306a36Sopenharmony_ci	switch (info->flow_type) {
85862306a36Sopenharmony_ci	case TCP_V4_FLOW:
85962306a36Sopenharmony_ci	case TCP_V6_FLOW:
86062306a36Sopenharmony_ci		info->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3 |
86162306a36Sopenharmony_ci			      RXH_IP_SRC | RXH_IP_DST;
86262306a36Sopenharmony_ci		break;
86362306a36Sopenharmony_ci	case UDP_V4_FLOW:
86462306a36Sopenharmony_ci		if (rss_fields & VMXNET3_RSS_FIELDS_UDPIP4)
86562306a36Sopenharmony_ci			info->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
86662306a36Sopenharmony_ci		info->data |= RXH_IP_SRC | RXH_IP_DST;
86762306a36Sopenharmony_ci		break;
86862306a36Sopenharmony_ci	case AH_ESP_V4_FLOW:
86962306a36Sopenharmony_ci	case AH_V4_FLOW:
87062306a36Sopenharmony_ci	case ESP_V4_FLOW:
87162306a36Sopenharmony_ci		if (rss_fields & VMXNET3_RSS_FIELDS_ESPIP4)
87262306a36Sopenharmony_ci			info->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
87362306a36Sopenharmony_ci		fallthrough;
87462306a36Sopenharmony_ci	case SCTP_V4_FLOW:
87562306a36Sopenharmony_ci	case IPV4_FLOW:
87662306a36Sopenharmony_ci		info->data |= RXH_IP_SRC | RXH_IP_DST;
87762306a36Sopenharmony_ci		break;
87862306a36Sopenharmony_ci	case UDP_V6_FLOW:
87962306a36Sopenharmony_ci		if (rss_fields & VMXNET3_RSS_FIELDS_UDPIP6)
88062306a36Sopenharmony_ci			info->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
88162306a36Sopenharmony_ci		info->data |= RXH_IP_SRC | RXH_IP_DST;
88262306a36Sopenharmony_ci		break;
88362306a36Sopenharmony_ci	case AH_ESP_V6_FLOW:
88462306a36Sopenharmony_ci	case AH_V6_FLOW:
88562306a36Sopenharmony_ci	case ESP_V6_FLOW:
88662306a36Sopenharmony_ci		if (VMXNET3_VERSION_GE_6(adapter) &&
88762306a36Sopenharmony_ci		    (rss_fields & VMXNET3_RSS_FIELDS_ESPIP6))
88862306a36Sopenharmony_ci			info->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
88962306a36Sopenharmony_ci		fallthrough;
89062306a36Sopenharmony_ci	case SCTP_V6_FLOW:
89162306a36Sopenharmony_ci	case IPV6_FLOW:
89262306a36Sopenharmony_ci		info->data |= RXH_IP_SRC | RXH_IP_DST;
89362306a36Sopenharmony_ci		break;
89462306a36Sopenharmony_ci	default:
89562306a36Sopenharmony_ci		return -EINVAL;
89662306a36Sopenharmony_ci	}
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci	return 0;
89962306a36Sopenharmony_ci}
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_cistatic int
90262306a36Sopenharmony_civmxnet3_set_rss_hash_opt(struct net_device *netdev,
90362306a36Sopenharmony_ci			 struct vmxnet3_adapter *adapter,
90462306a36Sopenharmony_ci			 struct ethtool_rxnfc *nfc)
90562306a36Sopenharmony_ci{
90662306a36Sopenharmony_ci	enum Vmxnet3_RSSField rss_fields = adapter->rss_fields;
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_ci	/* RSS does not support anything other than hashing
90962306a36Sopenharmony_ci	 * to queues on src and dst IPs and ports
91062306a36Sopenharmony_ci	 */
91162306a36Sopenharmony_ci	if (nfc->data & ~(RXH_IP_SRC | RXH_IP_DST |
91262306a36Sopenharmony_ci			  RXH_L4_B_0_1 | RXH_L4_B_2_3))
91362306a36Sopenharmony_ci		return -EINVAL;
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_ci	switch (nfc->flow_type) {
91662306a36Sopenharmony_ci	case TCP_V4_FLOW:
91762306a36Sopenharmony_ci	case TCP_V6_FLOW:
91862306a36Sopenharmony_ci		if (!(nfc->data & RXH_IP_SRC) ||
91962306a36Sopenharmony_ci		    !(nfc->data & RXH_IP_DST) ||
92062306a36Sopenharmony_ci		    !(nfc->data & RXH_L4_B_0_1) ||
92162306a36Sopenharmony_ci		    !(nfc->data & RXH_L4_B_2_3))
92262306a36Sopenharmony_ci			return -EINVAL;
92362306a36Sopenharmony_ci		break;
92462306a36Sopenharmony_ci	case UDP_V4_FLOW:
92562306a36Sopenharmony_ci		if (!(nfc->data & RXH_IP_SRC) ||
92662306a36Sopenharmony_ci		    !(nfc->data & RXH_IP_DST))
92762306a36Sopenharmony_ci			return -EINVAL;
92862306a36Sopenharmony_ci		switch (nfc->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) {
92962306a36Sopenharmony_ci		case 0:
93062306a36Sopenharmony_ci			rss_fields &= ~VMXNET3_RSS_FIELDS_UDPIP4;
93162306a36Sopenharmony_ci			break;
93262306a36Sopenharmony_ci		case (RXH_L4_B_0_1 | RXH_L4_B_2_3):
93362306a36Sopenharmony_ci			rss_fields |= VMXNET3_RSS_FIELDS_UDPIP4;
93462306a36Sopenharmony_ci			break;
93562306a36Sopenharmony_ci		default:
93662306a36Sopenharmony_ci			return -EINVAL;
93762306a36Sopenharmony_ci		}
93862306a36Sopenharmony_ci		break;
93962306a36Sopenharmony_ci	case UDP_V6_FLOW:
94062306a36Sopenharmony_ci		if (!(nfc->data & RXH_IP_SRC) ||
94162306a36Sopenharmony_ci		    !(nfc->data & RXH_IP_DST))
94262306a36Sopenharmony_ci			return -EINVAL;
94362306a36Sopenharmony_ci		switch (nfc->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) {
94462306a36Sopenharmony_ci		case 0:
94562306a36Sopenharmony_ci			rss_fields &= ~VMXNET3_RSS_FIELDS_UDPIP6;
94662306a36Sopenharmony_ci			break;
94762306a36Sopenharmony_ci		case (RXH_L4_B_0_1 | RXH_L4_B_2_3):
94862306a36Sopenharmony_ci			rss_fields |= VMXNET3_RSS_FIELDS_UDPIP6;
94962306a36Sopenharmony_ci			break;
95062306a36Sopenharmony_ci		default:
95162306a36Sopenharmony_ci			return -EINVAL;
95262306a36Sopenharmony_ci		}
95362306a36Sopenharmony_ci		break;
95462306a36Sopenharmony_ci	case ESP_V4_FLOW:
95562306a36Sopenharmony_ci	case AH_V4_FLOW:
95662306a36Sopenharmony_ci	case AH_ESP_V4_FLOW:
95762306a36Sopenharmony_ci		if (!(nfc->data & RXH_IP_SRC) ||
95862306a36Sopenharmony_ci		    !(nfc->data & RXH_IP_DST))
95962306a36Sopenharmony_ci			return -EINVAL;
96062306a36Sopenharmony_ci		switch (nfc->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) {
96162306a36Sopenharmony_ci		case 0:
96262306a36Sopenharmony_ci			rss_fields &= ~VMXNET3_RSS_FIELDS_ESPIP4;
96362306a36Sopenharmony_ci			break;
96462306a36Sopenharmony_ci		case (RXH_L4_B_0_1 | RXH_L4_B_2_3):
96562306a36Sopenharmony_ci			rss_fields |= VMXNET3_RSS_FIELDS_ESPIP4;
96662306a36Sopenharmony_ci		break;
96762306a36Sopenharmony_ci		default:
96862306a36Sopenharmony_ci			return -EINVAL;
96962306a36Sopenharmony_ci		}
97062306a36Sopenharmony_ci		break;
97162306a36Sopenharmony_ci	case ESP_V6_FLOW:
97262306a36Sopenharmony_ci	case AH_V6_FLOW:
97362306a36Sopenharmony_ci	case AH_ESP_V6_FLOW:
97462306a36Sopenharmony_ci		if (!VMXNET3_VERSION_GE_6(adapter))
97562306a36Sopenharmony_ci			return -EOPNOTSUPP;
97662306a36Sopenharmony_ci		if (!(nfc->data & RXH_IP_SRC) ||
97762306a36Sopenharmony_ci		    !(nfc->data & RXH_IP_DST))
97862306a36Sopenharmony_ci			return -EINVAL;
97962306a36Sopenharmony_ci		switch (nfc->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) {
98062306a36Sopenharmony_ci		case 0:
98162306a36Sopenharmony_ci			rss_fields &= ~VMXNET3_RSS_FIELDS_ESPIP6;
98262306a36Sopenharmony_ci			break;
98362306a36Sopenharmony_ci		case (RXH_L4_B_0_1 | RXH_L4_B_2_3):
98462306a36Sopenharmony_ci			rss_fields |= VMXNET3_RSS_FIELDS_ESPIP6;
98562306a36Sopenharmony_ci			break;
98662306a36Sopenharmony_ci		default:
98762306a36Sopenharmony_ci			return -EINVAL;
98862306a36Sopenharmony_ci		}
98962306a36Sopenharmony_ci		break;
99062306a36Sopenharmony_ci	case SCTP_V4_FLOW:
99162306a36Sopenharmony_ci	case SCTP_V6_FLOW:
99262306a36Sopenharmony_ci		if (!(nfc->data & RXH_IP_SRC) ||
99362306a36Sopenharmony_ci		    !(nfc->data & RXH_IP_DST) ||
99462306a36Sopenharmony_ci		    (nfc->data & RXH_L4_B_0_1) ||
99562306a36Sopenharmony_ci		    (nfc->data & RXH_L4_B_2_3))
99662306a36Sopenharmony_ci			return -EINVAL;
99762306a36Sopenharmony_ci		break;
99862306a36Sopenharmony_ci	default:
99962306a36Sopenharmony_ci		return -EINVAL;
100062306a36Sopenharmony_ci	}
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_ci	/* if we changed something we need to update flags */
100362306a36Sopenharmony_ci	if (rss_fields != adapter->rss_fields) {
100462306a36Sopenharmony_ci		adapter->default_rss_fields = false;
100562306a36Sopenharmony_ci		if (netif_running(netdev)) {
100662306a36Sopenharmony_ci			struct Vmxnet3_DriverShared *shared = adapter->shared;
100762306a36Sopenharmony_ci			union Vmxnet3_CmdInfo *cmdInfo = &shared->cu.cmdInfo;
100862306a36Sopenharmony_ci			unsigned long flags;
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_ci			if (VMXNET3_VERSION_GE_7(adapter)) {
101162306a36Sopenharmony_ci				if ((rss_fields & VMXNET3_RSS_FIELDS_UDPIP4 ||
101262306a36Sopenharmony_ci				     rss_fields & VMXNET3_RSS_FIELDS_UDPIP6) &&
101362306a36Sopenharmony_ci				    vmxnet3_check_ptcapability(adapter->ptcap_supported[0],
101462306a36Sopenharmony_ci							       VMXNET3_CAP_UDP_RSS)) {
101562306a36Sopenharmony_ci					adapter->dev_caps[0] |= 1UL << VMXNET3_CAP_UDP_RSS;
101662306a36Sopenharmony_ci				} else {
101762306a36Sopenharmony_ci					adapter->dev_caps[0] &= ~(1UL << VMXNET3_CAP_UDP_RSS);
101862306a36Sopenharmony_ci				}
101962306a36Sopenharmony_ci				if ((rss_fields & VMXNET3_RSS_FIELDS_ESPIP4) &&
102062306a36Sopenharmony_ci				    vmxnet3_check_ptcapability(adapter->ptcap_supported[0],
102162306a36Sopenharmony_ci							       VMXNET3_CAP_ESP_RSS_IPV4)) {
102262306a36Sopenharmony_ci					adapter->dev_caps[0] |= 1UL << VMXNET3_CAP_ESP_RSS_IPV4;
102362306a36Sopenharmony_ci				} else {
102462306a36Sopenharmony_ci					adapter->dev_caps[0] &= ~(1UL << VMXNET3_CAP_ESP_RSS_IPV4);
102562306a36Sopenharmony_ci				}
102662306a36Sopenharmony_ci				if ((rss_fields & VMXNET3_RSS_FIELDS_ESPIP6) &&
102762306a36Sopenharmony_ci				    vmxnet3_check_ptcapability(adapter->ptcap_supported[0],
102862306a36Sopenharmony_ci							       VMXNET3_CAP_ESP_RSS_IPV6)) {
102962306a36Sopenharmony_ci					adapter->dev_caps[0] |= 1UL << VMXNET3_CAP_ESP_RSS_IPV6;
103062306a36Sopenharmony_ci				} else {
103162306a36Sopenharmony_ci					adapter->dev_caps[0] &= ~(1UL << VMXNET3_CAP_ESP_RSS_IPV6);
103262306a36Sopenharmony_ci				}
103362306a36Sopenharmony_ci
103462306a36Sopenharmony_ci				VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_DCR,
103562306a36Sopenharmony_ci						       adapter->dev_caps[0]);
103662306a36Sopenharmony_ci				spin_lock_irqsave(&adapter->cmd_lock, flags);
103762306a36Sopenharmony_ci				VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD,
103862306a36Sopenharmony_ci						       VMXNET3_CMD_GET_DCR0_REG);
103962306a36Sopenharmony_ci				adapter->dev_caps[0] = VMXNET3_READ_BAR1_REG(adapter,
104062306a36Sopenharmony_ci									     VMXNET3_REG_CMD);
104162306a36Sopenharmony_ci				spin_unlock_irqrestore(&adapter->cmd_lock, flags);
104262306a36Sopenharmony_ci			}
104362306a36Sopenharmony_ci			spin_lock_irqsave(&adapter->cmd_lock, flags);
104462306a36Sopenharmony_ci			cmdInfo->setRssFields = rss_fields;
104562306a36Sopenharmony_ci			VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD,
104662306a36Sopenharmony_ci					       VMXNET3_CMD_SET_RSS_FIELDS);
104762306a36Sopenharmony_ci
104862306a36Sopenharmony_ci			/* Not all requested RSS may get applied, so get and
104962306a36Sopenharmony_ci			 * cache what was actually applied.
105062306a36Sopenharmony_ci			 */
105162306a36Sopenharmony_ci			VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD,
105262306a36Sopenharmony_ci					       VMXNET3_CMD_GET_RSS_FIELDS);
105362306a36Sopenharmony_ci			adapter->rss_fields =
105462306a36Sopenharmony_ci				VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_CMD);
105562306a36Sopenharmony_ci			spin_unlock_irqrestore(&adapter->cmd_lock, flags);
105662306a36Sopenharmony_ci		} else {
105762306a36Sopenharmony_ci			/* When the device is activated, we will try to apply
105862306a36Sopenharmony_ci			 * these rules and cache the applied value later.
105962306a36Sopenharmony_ci			 */
106062306a36Sopenharmony_ci			adapter->rss_fields = rss_fields;
106162306a36Sopenharmony_ci		}
106262306a36Sopenharmony_ci	}
106362306a36Sopenharmony_ci	return 0;
106462306a36Sopenharmony_ci}
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_cistatic int
106762306a36Sopenharmony_civmxnet3_get_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *info,
106862306a36Sopenharmony_ci		  u32 *rules)
106962306a36Sopenharmony_ci{
107062306a36Sopenharmony_ci	struct vmxnet3_adapter *adapter = netdev_priv(netdev);
107162306a36Sopenharmony_ci	int err = 0;
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_ci	switch (info->cmd) {
107462306a36Sopenharmony_ci	case ETHTOOL_GRXRINGS:
107562306a36Sopenharmony_ci		info->data = adapter->num_rx_queues;
107662306a36Sopenharmony_ci		break;
107762306a36Sopenharmony_ci	case ETHTOOL_GRXFH:
107862306a36Sopenharmony_ci		if (!VMXNET3_VERSION_GE_4(adapter)) {
107962306a36Sopenharmony_ci			err = -EOPNOTSUPP;
108062306a36Sopenharmony_ci			break;
108162306a36Sopenharmony_ci		}
108262306a36Sopenharmony_ci#ifdef VMXNET3_RSS
108362306a36Sopenharmony_ci		if (!adapter->rss) {
108462306a36Sopenharmony_ci			err = -EOPNOTSUPP;
108562306a36Sopenharmony_ci			break;
108662306a36Sopenharmony_ci		}
108762306a36Sopenharmony_ci#endif
108862306a36Sopenharmony_ci		err = vmxnet3_get_rss_hash_opts(adapter, info);
108962306a36Sopenharmony_ci		break;
109062306a36Sopenharmony_ci	default:
109162306a36Sopenharmony_ci		err = -EOPNOTSUPP;
109262306a36Sopenharmony_ci		break;
109362306a36Sopenharmony_ci	}
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ci	return err;
109662306a36Sopenharmony_ci}
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_cistatic int
109962306a36Sopenharmony_civmxnet3_set_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *info)
110062306a36Sopenharmony_ci{
110162306a36Sopenharmony_ci	struct vmxnet3_adapter *adapter = netdev_priv(netdev);
110262306a36Sopenharmony_ci	int err = 0;
110362306a36Sopenharmony_ci
110462306a36Sopenharmony_ci	if (!VMXNET3_VERSION_GE_4(adapter)) {
110562306a36Sopenharmony_ci		err = -EOPNOTSUPP;
110662306a36Sopenharmony_ci		goto done;
110762306a36Sopenharmony_ci	}
110862306a36Sopenharmony_ci#ifdef VMXNET3_RSS
110962306a36Sopenharmony_ci	if (!adapter->rss) {
111062306a36Sopenharmony_ci		err = -EOPNOTSUPP;
111162306a36Sopenharmony_ci		goto done;
111262306a36Sopenharmony_ci	}
111362306a36Sopenharmony_ci#endif
111462306a36Sopenharmony_ci
111562306a36Sopenharmony_ci	switch (info->cmd) {
111662306a36Sopenharmony_ci	case ETHTOOL_SRXFH:
111762306a36Sopenharmony_ci		err = vmxnet3_set_rss_hash_opt(netdev, adapter, info);
111862306a36Sopenharmony_ci		break;
111962306a36Sopenharmony_ci	default:
112062306a36Sopenharmony_ci		err = -EOPNOTSUPP;
112162306a36Sopenharmony_ci		break;
112262306a36Sopenharmony_ci	}
112362306a36Sopenharmony_ci
112462306a36Sopenharmony_cidone:
112562306a36Sopenharmony_ci	return err;
112662306a36Sopenharmony_ci}
112762306a36Sopenharmony_ci
112862306a36Sopenharmony_ci#ifdef VMXNET3_RSS
112962306a36Sopenharmony_cistatic u32
113062306a36Sopenharmony_civmxnet3_get_rss_indir_size(struct net_device *netdev)
113162306a36Sopenharmony_ci{
113262306a36Sopenharmony_ci	struct vmxnet3_adapter *adapter = netdev_priv(netdev);
113362306a36Sopenharmony_ci	struct UPT1_RSSConf *rssConf = adapter->rss_conf;
113462306a36Sopenharmony_ci
113562306a36Sopenharmony_ci	return rssConf->indTableSize;
113662306a36Sopenharmony_ci}
113762306a36Sopenharmony_ci
113862306a36Sopenharmony_cistatic int
113962306a36Sopenharmony_civmxnet3_get_rss(struct net_device *netdev, u32 *p, u8 *key, u8 *hfunc)
114062306a36Sopenharmony_ci{
114162306a36Sopenharmony_ci	struct vmxnet3_adapter *adapter = netdev_priv(netdev);
114262306a36Sopenharmony_ci	struct UPT1_RSSConf *rssConf = adapter->rss_conf;
114362306a36Sopenharmony_ci	unsigned int n = rssConf->indTableSize;
114462306a36Sopenharmony_ci
114562306a36Sopenharmony_ci	if (hfunc)
114662306a36Sopenharmony_ci		*hfunc = ETH_RSS_HASH_TOP;
114762306a36Sopenharmony_ci	if (!p)
114862306a36Sopenharmony_ci		return 0;
114962306a36Sopenharmony_ci	if (n > UPT1_RSS_MAX_IND_TABLE_SIZE)
115062306a36Sopenharmony_ci		return 0;
115162306a36Sopenharmony_ci	while (n--)
115262306a36Sopenharmony_ci		p[n] = rssConf->indTable[n];
115362306a36Sopenharmony_ci	return 0;
115462306a36Sopenharmony_ci
115562306a36Sopenharmony_ci}
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_cistatic int
115862306a36Sopenharmony_civmxnet3_set_rss(struct net_device *netdev, const u32 *p, const u8 *key,
115962306a36Sopenharmony_ci		const u8 hfunc)
116062306a36Sopenharmony_ci{
116162306a36Sopenharmony_ci	unsigned int i;
116262306a36Sopenharmony_ci	unsigned long flags;
116362306a36Sopenharmony_ci	struct vmxnet3_adapter *adapter = netdev_priv(netdev);
116462306a36Sopenharmony_ci	struct UPT1_RSSConf *rssConf = adapter->rss_conf;
116562306a36Sopenharmony_ci
116662306a36Sopenharmony_ci	/* We do not allow change in unsupported parameters */
116762306a36Sopenharmony_ci	if (key ||
116862306a36Sopenharmony_ci	    (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP))
116962306a36Sopenharmony_ci		return -EOPNOTSUPP;
117062306a36Sopenharmony_ci	if (!p)
117162306a36Sopenharmony_ci		return 0;
117262306a36Sopenharmony_ci	for (i = 0; i < rssConf->indTableSize; i++)
117362306a36Sopenharmony_ci		rssConf->indTable[i] = p[i];
117462306a36Sopenharmony_ci
117562306a36Sopenharmony_ci	spin_lock_irqsave(&adapter->cmd_lock, flags);
117662306a36Sopenharmony_ci	VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD,
117762306a36Sopenharmony_ci			       VMXNET3_CMD_UPDATE_RSSIDT);
117862306a36Sopenharmony_ci	spin_unlock_irqrestore(&adapter->cmd_lock, flags);
117962306a36Sopenharmony_ci
118062306a36Sopenharmony_ci	return 0;
118162306a36Sopenharmony_ci
118262306a36Sopenharmony_ci}
118362306a36Sopenharmony_ci#endif
118462306a36Sopenharmony_ci
118562306a36Sopenharmony_cistatic int vmxnet3_get_coalesce(struct net_device *netdev,
118662306a36Sopenharmony_ci				struct ethtool_coalesce *ec,
118762306a36Sopenharmony_ci				struct kernel_ethtool_coalesce *kernel_coal,
118862306a36Sopenharmony_ci				struct netlink_ext_ack *extack)
118962306a36Sopenharmony_ci{
119062306a36Sopenharmony_ci	struct vmxnet3_adapter *adapter = netdev_priv(netdev);
119162306a36Sopenharmony_ci
119262306a36Sopenharmony_ci	if (!VMXNET3_VERSION_GE_3(adapter))
119362306a36Sopenharmony_ci		return -EOPNOTSUPP;
119462306a36Sopenharmony_ci
119562306a36Sopenharmony_ci	switch (adapter->coal_conf->coalMode) {
119662306a36Sopenharmony_ci	case VMXNET3_COALESCE_DISABLED:
119762306a36Sopenharmony_ci		/* struct ethtool_coalesce is already initialized to 0 */
119862306a36Sopenharmony_ci		break;
119962306a36Sopenharmony_ci	case VMXNET3_COALESCE_ADAPT:
120062306a36Sopenharmony_ci		ec->use_adaptive_rx_coalesce = true;
120162306a36Sopenharmony_ci		break;
120262306a36Sopenharmony_ci	case VMXNET3_COALESCE_STATIC:
120362306a36Sopenharmony_ci		ec->tx_max_coalesced_frames =
120462306a36Sopenharmony_ci			adapter->coal_conf->coalPara.coalStatic.tx_comp_depth;
120562306a36Sopenharmony_ci		ec->rx_max_coalesced_frames =
120662306a36Sopenharmony_ci			adapter->coal_conf->coalPara.coalStatic.rx_depth;
120762306a36Sopenharmony_ci		break;
120862306a36Sopenharmony_ci	case VMXNET3_COALESCE_RBC: {
120962306a36Sopenharmony_ci		u32 rbc_rate;
121062306a36Sopenharmony_ci
121162306a36Sopenharmony_ci		rbc_rate = adapter->coal_conf->coalPara.coalRbc.rbc_rate;
121262306a36Sopenharmony_ci		ec->rx_coalesce_usecs = VMXNET3_COAL_RBC_USECS(rbc_rate);
121362306a36Sopenharmony_ci	}
121462306a36Sopenharmony_ci		break;
121562306a36Sopenharmony_ci	default:
121662306a36Sopenharmony_ci		return -EOPNOTSUPP;
121762306a36Sopenharmony_ci	}
121862306a36Sopenharmony_ci
121962306a36Sopenharmony_ci	return 0;
122062306a36Sopenharmony_ci}
122162306a36Sopenharmony_ci
122262306a36Sopenharmony_cistatic int vmxnet3_set_coalesce(struct net_device *netdev,
122362306a36Sopenharmony_ci				struct ethtool_coalesce *ec,
122462306a36Sopenharmony_ci				struct kernel_ethtool_coalesce *kernel_coal,
122562306a36Sopenharmony_ci				struct netlink_ext_ack *extack)
122662306a36Sopenharmony_ci{
122762306a36Sopenharmony_ci	struct vmxnet3_adapter *adapter = netdev_priv(netdev);
122862306a36Sopenharmony_ci	struct Vmxnet3_DriverShared *shared = adapter->shared;
122962306a36Sopenharmony_ci	union Vmxnet3_CmdInfo *cmdInfo = &shared->cu.cmdInfo;
123062306a36Sopenharmony_ci	unsigned long flags;
123162306a36Sopenharmony_ci
123262306a36Sopenharmony_ci	if (!VMXNET3_VERSION_GE_3(adapter))
123362306a36Sopenharmony_ci		return -EOPNOTSUPP;
123462306a36Sopenharmony_ci
123562306a36Sopenharmony_ci	if ((ec->rx_coalesce_usecs == 0) &&
123662306a36Sopenharmony_ci	    (ec->use_adaptive_rx_coalesce == 0) &&
123762306a36Sopenharmony_ci	    (ec->tx_max_coalesced_frames == 0) &&
123862306a36Sopenharmony_ci	    (ec->rx_max_coalesced_frames == 0)) {
123962306a36Sopenharmony_ci		memset(adapter->coal_conf, 0, sizeof(*adapter->coal_conf));
124062306a36Sopenharmony_ci		adapter->coal_conf->coalMode = VMXNET3_COALESCE_DISABLED;
124162306a36Sopenharmony_ci		goto done;
124262306a36Sopenharmony_ci	}
124362306a36Sopenharmony_ci
124462306a36Sopenharmony_ci	if (ec->rx_coalesce_usecs != 0) {
124562306a36Sopenharmony_ci		u32 rbc_rate;
124662306a36Sopenharmony_ci
124762306a36Sopenharmony_ci		if ((ec->use_adaptive_rx_coalesce != 0) ||
124862306a36Sopenharmony_ci		    (ec->tx_max_coalesced_frames != 0) ||
124962306a36Sopenharmony_ci		    (ec->rx_max_coalesced_frames != 0)) {
125062306a36Sopenharmony_ci			return -EINVAL;
125162306a36Sopenharmony_ci		}
125262306a36Sopenharmony_ci
125362306a36Sopenharmony_ci		rbc_rate = VMXNET3_COAL_RBC_RATE(ec->rx_coalesce_usecs);
125462306a36Sopenharmony_ci		if (rbc_rate < VMXNET3_COAL_RBC_MIN_RATE ||
125562306a36Sopenharmony_ci		    rbc_rate > VMXNET3_COAL_RBC_MAX_RATE) {
125662306a36Sopenharmony_ci			return -EINVAL;
125762306a36Sopenharmony_ci		}
125862306a36Sopenharmony_ci
125962306a36Sopenharmony_ci		memset(adapter->coal_conf, 0, sizeof(*adapter->coal_conf));
126062306a36Sopenharmony_ci		adapter->coal_conf->coalMode = VMXNET3_COALESCE_RBC;
126162306a36Sopenharmony_ci		adapter->coal_conf->coalPara.coalRbc.rbc_rate = rbc_rate;
126262306a36Sopenharmony_ci		goto done;
126362306a36Sopenharmony_ci	}
126462306a36Sopenharmony_ci
126562306a36Sopenharmony_ci	if (ec->use_adaptive_rx_coalesce != 0) {
126662306a36Sopenharmony_ci		if (ec->tx_max_coalesced_frames != 0 ||
126762306a36Sopenharmony_ci		    ec->rx_max_coalesced_frames != 0) {
126862306a36Sopenharmony_ci			return -EINVAL;
126962306a36Sopenharmony_ci		}
127062306a36Sopenharmony_ci		memset(adapter->coal_conf, 0, sizeof(*adapter->coal_conf));
127162306a36Sopenharmony_ci		adapter->coal_conf->coalMode = VMXNET3_COALESCE_ADAPT;
127262306a36Sopenharmony_ci		goto done;
127362306a36Sopenharmony_ci	}
127462306a36Sopenharmony_ci
127562306a36Sopenharmony_ci	if ((ec->tx_max_coalesced_frames != 0) ||
127662306a36Sopenharmony_ci	    (ec->rx_max_coalesced_frames != 0)) {
127762306a36Sopenharmony_ci		if ((ec->tx_max_coalesced_frames >
127862306a36Sopenharmony_ci		    VMXNET3_COAL_STATIC_MAX_DEPTH) ||
127962306a36Sopenharmony_ci		    (ec->rx_max_coalesced_frames >
128062306a36Sopenharmony_ci		     VMXNET3_COAL_STATIC_MAX_DEPTH)) {
128162306a36Sopenharmony_ci			return -EINVAL;
128262306a36Sopenharmony_ci		}
128362306a36Sopenharmony_ci
128462306a36Sopenharmony_ci		memset(adapter->coal_conf, 0, sizeof(*adapter->coal_conf));
128562306a36Sopenharmony_ci		adapter->coal_conf->coalMode = VMXNET3_COALESCE_STATIC;
128662306a36Sopenharmony_ci
128762306a36Sopenharmony_ci		adapter->coal_conf->coalPara.coalStatic.tx_comp_depth =
128862306a36Sopenharmony_ci			(ec->tx_max_coalesced_frames ?
128962306a36Sopenharmony_ci			 ec->tx_max_coalesced_frames :
129062306a36Sopenharmony_ci			 VMXNET3_COAL_STATIC_DEFAULT_DEPTH);
129162306a36Sopenharmony_ci
129262306a36Sopenharmony_ci		adapter->coal_conf->coalPara.coalStatic.rx_depth =
129362306a36Sopenharmony_ci			(ec->rx_max_coalesced_frames ?
129462306a36Sopenharmony_ci			 ec->rx_max_coalesced_frames :
129562306a36Sopenharmony_ci			 VMXNET3_COAL_STATIC_DEFAULT_DEPTH);
129662306a36Sopenharmony_ci
129762306a36Sopenharmony_ci		adapter->coal_conf->coalPara.coalStatic.tx_depth =
129862306a36Sopenharmony_ci			 VMXNET3_COAL_STATIC_DEFAULT_DEPTH;
129962306a36Sopenharmony_ci		goto done;
130062306a36Sopenharmony_ci	}
130162306a36Sopenharmony_ci
130262306a36Sopenharmony_cidone:
130362306a36Sopenharmony_ci	adapter->default_coal_mode = false;
130462306a36Sopenharmony_ci	if (netif_running(netdev)) {
130562306a36Sopenharmony_ci		spin_lock_irqsave(&adapter->cmd_lock, flags);
130662306a36Sopenharmony_ci		cmdInfo->varConf.confVer = 1;
130762306a36Sopenharmony_ci		cmdInfo->varConf.confLen =
130862306a36Sopenharmony_ci			cpu_to_le32(sizeof(*adapter->coal_conf));
130962306a36Sopenharmony_ci		cmdInfo->varConf.confPA  = cpu_to_le64(adapter->coal_conf_pa);
131062306a36Sopenharmony_ci		VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD,
131162306a36Sopenharmony_ci				       VMXNET3_CMD_SET_COALESCE);
131262306a36Sopenharmony_ci		spin_unlock_irqrestore(&adapter->cmd_lock, flags);
131362306a36Sopenharmony_ci	}
131462306a36Sopenharmony_ci
131562306a36Sopenharmony_ci	return 0;
131662306a36Sopenharmony_ci}
131762306a36Sopenharmony_ci
131862306a36Sopenharmony_cistatic void vmxnet3_get_channels(struct net_device *netdev,
131962306a36Sopenharmony_ci				 struct ethtool_channels *ec)
132062306a36Sopenharmony_ci{
132162306a36Sopenharmony_ci	struct vmxnet3_adapter *adapter = netdev_priv(netdev);
132262306a36Sopenharmony_ci
132362306a36Sopenharmony_ci	if (IS_ENABLED(CONFIG_PCI_MSI) && adapter->intr.type == VMXNET3_IT_MSIX) {
132462306a36Sopenharmony_ci		if (adapter->share_intr == VMXNET3_INTR_BUDDYSHARE) {
132562306a36Sopenharmony_ci			ec->combined_count = adapter->num_tx_queues;
132662306a36Sopenharmony_ci		} else {
132762306a36Sopenharmony_ci			ec->rx_count = adapter->num_rx_queues;
132862306a36Sopenharmony_ci			ec->tx_count =
132962306a36Sopenharmony_ci				adapter->share_intr == VMXNET3_INTR_TXSHARE ?
133062306a36Sopenharmony_ci					       1 : adapter->num_tx_queues;
133162306a36Sopenharmony_ci		}
133262306a36Sopenharmony_ci	} else {
133362306a36Sopenharmony_ci		ec->combined_count = 1;
133462306a36Sopenharmony_ci	}
133562306a36Sopenharmony_ci
133662306a36Sopenharmony_ci	ec->other_count = 1;
133762306a36Sopenharmony_ci
133862306a36Sopenharmony_ci	/* Number of interrupts cannot be changed on the fly */
133962306a36Sopenharmony_ci	/* Just set maximums to actual values */
134062306a36Sopenharmony_ci	ec->max_rx = ec->rx_count;
134162306a36Sopenharmony_ci	ec->max_tx = ec->tx_count;
134262306a36Sopenharmony_ci	ec->max_combined = ec->combined_count;
134362306a36Sopenharmony_ci	ec->max_other = ec->other_count;
134462306a36Sopenharmony_ci}
134562306a36Sopenharmony_ci
134662306a36Sopenharmony_cistatic const struct ethtool_ops vmxnet3_ethtool_ops = {
134762306a36Sopenharmony_ci	.supported_coalesce_params = ETHTOOL_COALESCE_RX_USECS |
134862306a36Sopenharmony_ci				     ETHTOOL_COALESCE_MAX_FRAMES |
134962306a36Sopenharmony_ci				     ETHTOOL_COALESCE_USE_ADAPTIVE_RX,
135062306a36Sopenharmony_ci	.get_drvinfo       = vmxnet3_get_drvinfo,
135162306a36Sopenharmony_ci	.get_regs_len      = vmxnet3_get_regs_len,
135262306a36Sopenharmony_ci	.get_regs          = vmxnet3_get_regs,
135362306a36Sopenharmony_ci	.get_wol           = vmxnet3_get_wol,
135462306a36Sopenharmony_ci	.set_wol           = vmxnet3_set_wol,
135562306a36Sopenharmony_ci	.get_link          = ethtool_op_get_link,
135662306a36Sopenharmony_ci	.get_coalesce      = vmxnet3_get_coalesce,
135762306a36Sopenharmony_ci	.set_coalesce      = vmxnet3_set_coalesce,
135862306a36Sopenharmony_ci	.get_strings       = vmxnet3_get_strings,
135962306a36Sopenharmony_ci	.get_sset_count	   = vmxnet3_get_sset_count,
136062306a36Sopenharmony_ci	.get_ethtool_stats = vmxnet3_get_ethtool_stats,
136162306a36Sopenharmony_ci	.get_ringparam     = vmxnet3_get_ringparam,
136262306a36Sopenharmony_ci	.set_ringparam     = vmxnet3_set_ringparam,
136362306a36Sopenharmony_ci	.get_rxnfc         = vmxnet3_get_rxnfc,
136462306a36Sopenharmony_ci	.set_rxnfc         = vmxnet3_set_rxnfc,
136562306a36Sopenharmony_ci#ifdef VMXNET3_RSS
136662306a36Sopenharmony_ci	.get_rxfh_indir_size = vmxnet3_get_rss_indir_size,
136762306a36Sopenharmony_ci	.get_rxfh          = vmxnet3_get_rss,
136862306a36Sopenharmony_ci	.set_rxfh          = vmxnet3_set_rss,
136962306a36Sopenharmony_ci#endif
137062306a36Sopenharmony_ci	.get_link_ksettings = vmxnet3_get_link_ksettings,
137162306a36Sopenharmony_ci	.get_channels      = vmxnet3_get_channels,
137262306a36Sopenharmony_ci};
137362306a36Sopenharmony_ci
137462306a36Sopenharmony_civoid vmxnet3_set_ethtool_ops(struct net_device *netdev)
137562306a36Sopenharmony_ci{
137662306a36Sopenharmony_ci	netdev->ethtool_ops = &vmxnet3_ethtool_ops;
137762306a36Sopenharmony_ci}
1378