162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * Copyright (c) 2007 Mellanox Technologies. All rights reserved.
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * This software is available to you under a choice of one of two
562306a36Sopenharmony_ci * licenses.  You may choose to be licensed under the terms of the GNU
662306a36Sopenharmony_ci * General Public License (GPL) Version 2, available from the file
762306a36Sopenharmony_ci * COPYING in the main directory of this source tree, or the
862306a36Sopenharmony_ci * OpenIB.org BSD license below:
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci *     Redistribution and use in source and binary forms, with or
1162306a36Sopenharmony_ci *     without modification, are permitted provided that the following
1262306a36Sopenharmony_ci *     conditions are met:
1362306a36Sopenharmony_ci *
1462306a36Sopenharmony_ci *      - Redistributions of source code must retain the above
1562306a36Sopenharmony_ci *        copyright notice, this list of conditions and the following
1662306a36Sopenharmony_ci *        disclaimer.
1762306a36Sopenharmony_ci *
1862306a36Sopenharmony_ci *      - Redistributions in binary form must reproduce the above
1962306a36Sopenharmony_ci *        copyright notice, this list of conditions and the following
2062306a36Sopenharmony_ci *        disclaimer in the documentation and/or other materials
2162306a36Sopenharmony_ci *        provided with the distribution.
2262306a36Sopenharmony_ci *
2362306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
2462306a36Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
2562306a36Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
2662306a36Sopenharmony_ci * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
2762306a36Sopenharmony_ci * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
2862306a36Sopenharmony_ci * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
2962306a36Sopenharmony_ci * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
3062306a36Sopenharmony_ci * SOFTWARE.
3162306a36Sopenharmony_ci *
3262306a36Sopenharmony_ci */
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci#include <linux/kernel.h>
3562306a36Sopenharmony_ci#include <linux/ethtool.h>
3662306a36Sopenharmony_ci#include <linux/netdevice.h>
3762306a36Sopenharmony_ci#include <linux/mlx4/driver.h>
3862306a36Sopenharmony_ci#include <linux/mlx4/device.h>
3962306a36Sopenharmony_ci#include <linux/in.h>
4062306a36Sopenharmony_ci#include <net/ip.h>
4162306a36Sopenharmony_ci#include <linux/bitmap.h>
4262306a36Sopenharmony_ci#include <linux/mii.h>
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci#include "mlx4_en.h"
4562306a36Sopenharmony_ci#include "en_port.h"
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci#define EN_ETHTOOL_QP_ATTACH (1ull << 63)
4862306a36Sopenharmony_ci#define EN_ETHTOOL_SHORT_MASK cpu_to_be16(0xffff)
4962306a36Sopenharmony_ci#define EN_ETHTOOL_WORD_MASK  cpu_to_be32(0xffffffff)
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ciint mlx4_en_moderation_update(struct mlx4_en_priv *priv)
5262306a36Sopenharmony_ci{
5362306a36Sopenharmony_ci	int i, t;
5462306a36Sopenharmony_ci	int err = 0;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	for (t = 0 ; t < MLX4_EN_NUM_TX_TYPES; t++) {
5762306a36Sopenharmony_ci		for (i = 0; i < priv->tx_ring_num[t]; i++) {
5862306a36Sopenharmony_ci			priv->tx_cq[t][i]->moder_cnt = priv->tx_frames;
5962306a36Sopenharmony_ci			priv->tx_cq[t][i]->moder_time = priv->tx_usecs;
6062306a36Sopenharmony_ci			if (priv->port_up) {
6162306a36Sopenharmony_ci				err = mlx4_en_set_cq_moder(priv,
6262306a36Sopenharmony_ci							   priv->tx_cq[t][i]);
6362306a36Sopenharmony_ci				if (err)
6462306a36Sopenharmony_ci					return err;
6562306a36Sopenharmony_ci			}
6662306a36Sopenharmony_ci		}
6762306a36Sopenharmony_ci	}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	if (priv->adaptive_rx_coal)
7062306a36Sopenharmony_ci		return 0;
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	for (i = 0; i < priv->rx_ring_num; i++) {
7362306a36Sopenharmony_ci		priv->rx_cq[i]->moder_cnt = priv->rx_frames;
7462306a36Sopenharmony_ci		priv->rx_cq[i]->moder_time = priv->rx_usecs;
7562306a36Sopenharmony_ci		priv->last_moder_time[i] = MLX4_EN_AUTO_CONF;
7662306a36Sopenharmony_ci		if (priv->port_up) {
7762306a36Sopenharmony_ci			err = mlx4_en_set_cq_moder(priv, priv->rx_cq[i]);
7862306a36Sopenharmony_ci			if (err)
7962306a36Sopenharmony_ci				return err;
8062306a36Sopenharmony_ci		}
8162306a36Sopenharmony_ci	}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	return err;
8462306a36Sopenharmony_ci}
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_cistatic void
8762306a36Sopenharmony_cimlx4_en_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *drvinfo)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	struct mlx4_en_priv *priv = netdev_priv(dev);
9062306a36Sopenharmony_ci	struct mlx4_en_dev *mdev = priv->mdev;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	strscpy(drvinfo->driver, DRV_NAME, sizeof(drvinfo->driver));
9362306a36Sopenharmony_ci	strscpy(drvinfo->version, DRV_VERSION,
9462306a36Sopenharmony_ci		sizeof(drvinfo->version));
9562306a36Sopenharmony_ci	snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version),
9662306a36Sopenharmony_ci		"%d.%d.%d",
9762306a36Sopenharmony_ci		(u16) (mdev->dev->caps.fw_ver >> 32),
9862306a36Sopenharmony_ci		(u16) ((mdev->dev->caps.fw_ver >> 16) & 0xffff),
9962306a36Sopenharmony_ci		(u16) (mdev->dev->caps.fw_ver & 0xffff));
10062306a36Sopenharmony_ci	strscpy(drvinfo->bus_info, pci_name(mdev->dev->persist->pdev),
10162306a36Sopenharmony_ci		sizeof(drvinfo->bus_info));
10262306a36Sopenharmony_ci}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_cistatic const char mlx4_en_priv_flags[][ETH_GSTRING_LEN] = {
10562306a36Sopenharmony_ci	"blueflame",
10662306a36Sopenharmony_ci	"phv-bit"
10762306a36Sopenharmony_ci};
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_cistatic const char main_strings[][ETH_GSTRING_LEN] = {
11062306a36Sopenharmony_ci	/* main statistics */
11162306a36Sopenharmony_ci	"rx_packets", "tx_packets", "rx_bytes", "tx_bytes", "rx_errors",
11262306a36Sopenharmony_ci	"tx_errors", "rx_dropped", "tx_dropped", "multicast", "collisions",
11362306a36Sopenharmony_ci	"rx_length_errors", "rx_over_errors", "rx_crc_errors",
11462306a36Sopenharmony_ci	"rx_frame_errors", "rx_fifo_errors", "rx_missed_errors",
11562306a36Sopenharmony_ci	"tx_aborted_errors", "tx_carrier_errors", "tx_fifo_errors",
11662306a36Sopenharmony_ci	"tx_heartbeat_errors", "tx_window_errors",
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	/* port statistics */
11962306a36Sopenharmony_ci	"tso_packets",
12062306a36Sopenharmony_ci	"xmit_more",
12162306a36Sopenharmony_ci	"queue_stopped", "wake_queue", "tx_timeout", "rx_alloc_pages",
12262306a36Sopenharmony_ci	"rx_csum_good", "rx_csum_none", "rx_csum_complete", "tx_chksum_offload",
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	/* pf statistics */
12562306a36Sopenharmony_ci	"pf_rx_packets",
12662306a36Sopenharmony_ci	"pf_rx_bytes",
12762306a36Sopenharmony_ci	"pf_tx_packets",
12862306a36Sopenharmony_ci	"pf_tx_bytes",
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	/* priority flow control statistics rx */
13162306a36Sopenharmony_ci	"rx_pause_prio_0", "rx_pause_duration_prio_0",
13262306a36Sopenharmony_ci	"rx_pause_transition_prio_0",
13362306a36Sopenharmony_ci	"rx_pause_prio_1", "rx_pause_duration_prio_1",
13462306a36Sopenharmony_ci	"rx_pause_transition_prio_1",
13562306a36Sopenharmony_ci	"rx_pause_prio_2", "rx_pause_duration_prio_2",
13662306a36Sopenharmony_ci	"rx_pause_transition_prio_2",
13762306a36Sopenharmony_ci	"rx_pause_prio_3", "rx_pause_duration_prio_3",
13862306a36Sopenharmony_ci	"rx_pause_transition_prio_3",
13962306a36Sopenharmony_ci	"rx_pause_prio_4", "rx_pause_duration_prio_4",
14062306a36Sopenharmony_ci	"rx_pause_transition_prio_4",
14162306a36Sopenharmony_ci	"rx_pause_prio_5", "rx_pause_duration_prio_5",
14262306a36Sopenharmony_ci	"rx_pause_transition_prio_5",
14362306a36Sopenharmony_ci	"rx_pause_prio_6", "rx_pause_duration_prio_6",
14462306a36Sopenharmony_ci	"rx_pause_transition_prio_6",
14562306a36Sopenharmony_ci	"rx_pause_prio_7", "rx_pause_duration_prio_7",
14662306a36Sopenharmony_ci	"rx_pause_transition_prio_7",
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	/* flow control statistics rx */
14962306a36Sopenharmony_ci	"rx_pause", "rx_pause_duration", "rx_pause_transition",
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	/* priority flow control statistics tx */
15262306a36Sopenharmony_ci	"tx_pause_prio_0", "tx_pause_duration_prio_0",
15362306a36Sopenharmony_ci	"tx_pause_transition_prio_0",
15462306a36Sopenharmony_ci	"tx_pause_prio_1", "tx_pause_duration_prio_1",
15562306a36Sopenharmony_ci	"tx_pause_transition_prio_1",
15662306a36Sopenharmony_ci	"tx_pause_prio_2", "tx_pause_duration_prio_2",
15762306a36Sopenharmony_ci	"tx_pause_transition_prio_2",
15862306a36Sopenharmony_ci	"tx_pause_prio_3", "tx_pause_duration_prio_3",
15962306a36Sopenharmony_ci	"tx_pause_transition_prio_3",
16062306a36Sopenharmony_ci	"tx_pause_prio_4", "tx_pause_duration_prio_4",
16162306a36Sopenharmony_ci	"tx_pause_transition_prio_4",
16262306a36Sopenharmony_ci	"tx_pause_prio_5", "tx_pause_duration_prio_5",
16362306a36Sopenharmony_ci	"tx_pause_transition_prio_5",
16462306a36Sopenharmony_ci	"tx_pause_prio_6", "tx_pause_duration_prio_6",
16562306a36Sopenharmony_ci	"tx_pause_transition_prio_6",
16662306a36Sopenharmony_ci	"tx_pause_prio_7", "tx_pause_duration_prio_7",
16762306a36Sopenharmony_ci	"tx_pause_transition_prio_7",
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	/* flow control statistics tx */
17062306a36Sopenharmony_ci	"tx_pause", "tx_pause_duration", "tx_pause_transition",
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	/* packet statistics */
17362306a36Sopenharmony_ci	"rx_multicast_packets",
17462306a36Sopenharmony_ci	"rx_broadcast_packets",
17562306a36Sopenharmony_ci	"rx_jabbers",
17662306a36Sopenharmony_ci	"rx_in_range_length_error",
17762306a36Sopenharmony_ci	"rx_out_range_length_error",
17862306a36Sopenharmony_ci	"tx_multicast_packets",
17962306a36Sopenharmony_ci	"tx_broadcast_packets",
18062306a36Sopenharmony_ci	"rx_prio_0_packets", "rx_prio_0_bytes",
18162306a36Sopenharmony_ci	"rx_prio_1_packets", "rx_prio_1_bytes",
18262306a36Sopenharmony_ci	"rx_prio_2_packets", "rx_prio_2_bytes",
18362306a36Sopenharmony_ci	"rx_prio_3_packets", "rx_prio_3_bytes",
18462306a36Sopenharmony_ci	"rx_prio_4_packets", "rx_prio_4_bytes",
18562306a36Sopenharmony_ci	"rx_prio_5_packets", "rx_prio_5_bytes",
18662306a36Sopenharmony_ci	"rx_prio_6_packets", "rx_prio_6_bytes",
18762306a36Sopenharmony_ci	"rx_prio_7_packets", "rx_prio_7_bytes",
18862306a36Sopenharmony_ci	"rx_novlan_packets", "rx_novlan_bytes",
18962306a36Sopenharmony_ci	"tx_prio_0_packets", "tx_prio_0_bytes",
19062306a36Sopenharmony_ci	"tx_prio_1_packets", "tx_prio_1_bytes",
19162306a36Sopenharmony_ci	"tx_prio_2_packets", "tx_prio_2_bytes",
19262306a36Sopenharmony_ci	"tx_prio_3_packets", "tx_prio_3_bytes",
19362306a36Sopenharmony_ci	"tx_prio_4_packets", "tx_prio_4_bytes",
19462306a36Sopenharmony_ci	"tx_prio_5_packets", "tx_prio_5_bytes",
19562306a36Sopenharmony_ci	"tx_prio_6_packets", "tx_prio_6_bytes",
19662306a36Sopenharmony_ci	"tx_prio_7_packets", "tx_prio_7_bytes",
19762306a36Sopenharmony_ci	"tx_novlan_packets", "tx_novlan_bytes",
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	/* xdp statistics */
20062306a36Sopenharmony_ci	"rx_xdp_drop",
20162306a36Sopenharmony_ci	"rx_xdp_redirect",
20262306a36Sopenharmony_ci	"rx_xdp_redirect_fail",
20362306a36Sopenharmony_ci	"rx_xdp_tx",
20462306a36Sopenharmony_ci	"rx_xdp_tx_full",
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	/* phy statistics */
20762306a36Sopenharmony_ci	"rx_packets_phy", "rx_bytes_phy",
20862306a36Sopenharmony_ci	"tx_packets_phy", "tx_bytes_phy",
20962306a36Sopenharmony_ci};
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_cistatic const char mlx4_en_test_names[][ETH_GSTRING_LEN]= {
21262306a36Sopenharmony_ci	"Interrupt Test",
21362306a36Sopenharmony_ci	"Link Test",
21462306a36Sopenharmony_ci	"Speed Test",
21562306a36Sopenharmony_ci	"Register Test",
21662306a36Sopenharmony_ci	"Loopback Test",
21762306a36Sopenharmony_ci};
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_cistatic u32 mlx4_en_get_msglevel(struct net_device *dev)
22062306a36Sopenharmony_ci{
22162306a36Sopenharmony_ci	return ((struct mlx4_en_priv *) netdev_priv(dev))->msg_enable;
22262306a36Sopenharmony_ci}
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_cistatic void mlx4_en_set_msglevel(struct net_device *dev, u32 val)
22562306a36Sopenharmony_ci{
22662306a36Sopenharmony_ci	((struct mlx4_en_priv *) netdev_priv(dev))->msg_enable = val;
22762306a36Sopenharmony_ci}
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_cistatic void mlx4_en_get_wol(struct net_device *netdev,
23062306a36Sopenharmony_ci			    struct ethtool_wolinfo *wol)
23162306a36Sopenharmony_ci{
23262306a36Sopenharmony_ci	struct mlx4_en_priv *priv = netdev_priv(netdev);
23362306a36Sopenharmony_ci	struct mlx4_caps *caps = &priv->mdev->dev->caps;
23462306a36Sopenharmony_ci	int err = 0;
23562306a36Sopenharmony_ci	u64 config = 0;
23662306a36Sopenharmony_ci	u64 mask;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	if ((priv->port < 1) || (priv->port > 2)) {
23962306a36Sopenharmony_ci		en_err(priv, "Failed to get WoL information\n");
24062306a36Sopenharmony_ci		return;
24162306a36Sopenharmony_ci	}
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	mask = (priv->port == 1) ? MLX4_DEV_CAP_FLAG_WOL_PORT1 :
24462306a36Sopenharmony_ci		MLX4_DEV_CAP_FLAG_WOL_PORT2;
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	if (!(caps->flags & mask)) {
24762306a36Sopenharmony_ci		wol->supported = 0;
24862306a36Sopenharmony_ci		wol->wolopts = 0;
24962306a36Sopenharmony_ci		return;
25062306a36Sopenharmony_ci	}
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	if (caps->wol_port[priv->port])
25362306a36Sopenharmony_ci		wol->supported = WAKE_MAGIC;
25462306a36Sopenharmony_ci	else
25562306a36Sopenharmony_ci		wol->supported = 0;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	err = mlx4_wol_read(priv->mdev->dev, &config, priv->port);
25862306a36Sopenharmony_ci	if (err) {
25962306a36Sopenharmony_ci		en_err(priv, "Failed to get WoL information\n");
26062306a36Sopenharmony_ci		return;
26162306a36Sopenharmony_ci	}
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	if ((config & MLX4_EN_WOL_ENABLED) && (config & MLX4_EN_WOL_MAGIC))
26462306a36Sopenharmony_ci		wol->wolopts = WAKE_MAGIC;
26562306a36Sopenharmony_ci	else
26662306a36Sopenharmony_ci		wol->wolopts = 0;
26762306a36Sopenharmony_ci}
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_cistatic int mlx4_en_set_wol(struct net_device *netdev,
27062306a36Sopenharmony_ci			    struct ethtool_wolinfo *wol)
27162306a36Sopenharmony_ci{
27262306a36Sopenharmony_ci	struct mlx4_en_priv *priv = netdev_priv(netdev);
27362306a36Sopenharmony_ci	u64 config = 0;
27462306a36Sopenharmony_ci	int err = 0;
27562306a36Sopenharmony_ci	u64 mask;
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	if ((priv->port < 1) || (priv->port > 2))
27862306a36Sopenharmony_ci		return -EOPNOTSUPP;
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	mask = (priv->port == 1) ? MLX4_DEV_CAP_FLAG_WOL_PORT1 :
28162306a36Sopenharmony_ci		MLX4_DEV_CAP_FLAG_WOL_PORT2;
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	if (!(priv->mdev->dev->caps.flags & mask))
28462306a36Sopenharmony_ci		return -EOPNOTSUPP;
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	if (wol->supported & ~WAKE_MAGIC)
28762306a36Sopenharmony_ci		return -EINVAL;
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	err = mlx4_wol_read(priv->mdev->dev, &config, priv->port);
29062306a36Sopenharmony_ci	if (err) {
29162306a36Sopenharmony_ci		en_err(priv, "Failed to get WoL info, unable to modify\n");
29262306a36Sopenharmony_ci		return err;
29362306a36Sopenharmony_ci	}
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	if (wol->wolopts & WAKE_MAGIC) {
29662306a36Sopenharmony_ci		config |= MLX4_EN_WOL_DO_MODIFY | MLX4_EN_WOL_ENABLED |
29762306a36Sopenharmony_ci				MLX4_EN_WOL_MAGIC;
29862306a36Sopenharmony_ci	} else {
29962306a36Sopenharmony_ci		config &= ~(MLX4_EN_WOL_ENABLED | MLX4_EN_WOL_MAGIC);
30062306a36Sopenharmony_ci		config |= MLX4_EN_WOL_DO_MODIFY;
30162306a36Sopenharmony_ci	}
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	err = mlx4_wol_write(priv->mdev->dev, config, priv->port);
30462306a36Sopenharmony_ci	if (err)
30562306a36Sopenharmony_ci		en_err(priv, "Failed to set WoL information\n");
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	return err;
30862306a36Sopenharmony_ci}
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_cistruct bitmap_iterator {
31162306a36Sopenharmony_ci	unsigned long *stats_bitmap;
31262306a36Sopenharmony_ci	unsigned int count;
31362306a36Sopenharmony_ci	unsigned int iterator;
31462306a36Sopenharmony_ci	bool advance_array; /* if set, force no increments */
31562306a36Sopenharmony_ci};
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_cistatic inline void bitmap_iterator_init(struct bitmap_iterator *h,
31862306a36Sopenharmony_ci					unsigned long *stats_bitmap,
31962306a36Sopenharmony_ci					int count)
32062306a36Sopenharmony_ci{
32162306a36Sopenharmony_ci	h->iterator = 0;
32262306a36Sopenharmony_ci	h->advance_array = !bitmap_empty(stats_bitmap, count);
32362306a36Sopenharmony_ci	h->count = h->advance_array ? bitmap_weight(stats_bitmap, count)
32462306a36Sopenharmony_ci		: count;
32562306a36Sopenharmony_ci	h->stats_bitmap = stats_bitmap;
32662306a36Sopenharmony_ci}
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_cistatic inline int bitmap_iterator_test(struct bitmap_iterator *h)
32962306a36Sopenharmony_ci{
33062306a36Sopenharmony_ci	return !h->advance_array ? 1 : test_bit(h->iterator, h->stats_bitmap);
33162306a36Sopenharmony_ci}
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_cistatic inline int bitmap_iterator_inc(struct bitmap_iterator *h)
33462306a36Sopenharmony_ci{
33562306a36Sopenharmony_ci	return h->iterator++;
33662306a36Sopenharmony_ci}
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_cistatic inline unsigned int
33962306a36Sopenharmony_cibitmap_iterator_count(struct bitmap_iterator *h)
34062306a36Sopenharmony_ci{
34162306a36Sopenharmony_ci	return h->count;
34262306a36Sopenharmony_ci}
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_cistatic int mlx4_en_get_sset_count(struct net_device *dev, int sset)
34562306a36Sopenharmony_ci{
34662306a36Sopenharmony_ci	struct mlx4_en_priv *priv = netdev_priv(dev);
34762306a36Sopenharmony_ci	struct bitmap_iterator it;
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	bitmap_iterator_init(&it, priv->stats_bitmap.bitmap, NUM_ALL_STATS);
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	switch (sset) {
35262306a36Sopenharmony_ci	case ETH_SS_STATS:
35362306a36Sopenharmony_ci		return bitmap_iterator_count(&it) +
35462306a36Sopenharmony_ci			(priv->tx_ring_num[TX] * 2) +
35562306a36Sopenharmony_ci			(priv->rx_ring_num * (3 + NUM_XDP_STATS));
35662306a36Sopenharmony_ci	case ETH_SS_TEST:
35762306a36Sopenharmony_ci		return MLX4_EN_NUM_SELF_TEST - !(priv->mdev->dev->caps.flags
35862306a36Sopenharmony_ci					& MLX4_DEV_CAP_FLAG_UC_LOOPBACK) * 2;
35962306a36Sopenharmony_ci	case ETH_SS_PRIV_FLAGS:
36062306a36Sopenharmony_ci		return ARRAY_SIZE(mlx4_en_priv_flags);
36162306a36Sopenharmony_ci	default:
36262306a36Sopenharmony_ci		return -EOPNOTSUPP;
36362306a36Sopenharmony_ci	}
36462306a36Sopenharmony_ci}
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_cistatic void mlx4_en_get_ethtool_stats(struct net_device *dev,
36762306a36Sopenharmony_ci		struct ethtool_stats *stats, uint64_t *data)
36862306a36Sopenharmony_ci{
36962306a36Sopenharmony_ci	struct mlx4_en_priv *priv = netdev_priv(dev);
37062306a36Sopenharmony_ci	int index = 0;
37162306a36Sopenharmony_ci	int i;
37262306a36Sopenharmony_ci	struct bitmap_iterator it;
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	bitmap_iterator_init(&it, priv->stats_bitmap.bitmap, NUM_ALL_STATS);
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	spin_lock_bh(&priv->stats_lock);
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	mlx4_en_fold_software_stats(dev);
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	for (i = 0; i < NUM_MAIN_STATS; i++, bitmap_iterator_inc(&it))
38162306a36Sopenharmony_ci		if (bitmap_iterator_test(&it))
38262306a36Sopenharmony_ci			data[index++] = ((unsigned long *)&dev->stats)[i];
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	for (i = 0; i < NUM_PORT_STATS; i++, bitmap_iterator_inc(&it))
38562306a36Sopenharmony_ci		if (bitmap_iterator_test(&it))
38662306a36Sopenharmony_ci			data[index++] = ((unsigned long *)&priv->port_stats)[i];
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	for (i = 0; i < NUM_PF_STATS; i++, bitmap_iterator_inc(&it))
38962306a36Sopenharmony_ci		if (bitmap_iterator_test(&it))
39062306a36Sopenharmony_ci			data[index++] =
39162306a36Sopenharmony_ci				((unsigned long *)&priv->pf_stats)[i];
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	for (i = 0; i < NUM_FLOW_PRIORITY_STATS_RX;
39462306a36Sopenharmony_ci	     i++, bitmap_iterator_inc(&it))
39562306a36Sopenharmony_ci		if (bitmap_iterator_test(&it))
39662306a36Sopenharmony_ci			data[index++] =
39762306a36Sopenharmony_ci				((u64 *)&priv->rx_priority_flowstats)[i];
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	for (i = 0; i < NUM_FLOW_STATS_RX; i++, bitmap_iterator_inc(&it))
40062306a36Sopenharmony_ci		if (bitmap_iterator_test(&it))
40162306a36Sopenharmony_ci			data[index++] = ((u64 *)&priv->rx_flowstats)[i];
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	for (i = 0; i < NUM_FLOW_PRIORITY_STATS_TX;
40462306a36Sopenharmony_ci	     i++, bitmap_iterator_inc(&it))
40562306a36Sopenharmony_ci		if (bitmap_iterator_test(&it))
40662306a36Sopenharmony_ci			data[index++] =
40762306a36Sopenharmony_ci				((u64 *)&priv->tx_priority_flowstats)[i];
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	for (i = 0; i < NUM_FLOW_STATS_TX; i++, bitmap_iterator_inc(&it))
41062306a36Sopenharmony_ci		if (bitmap_iterator_test(&it))
41162306a36Sopenharmony_ci			data[index++] = ((u64 *)&priv->tx_flowstats)[i];
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	for (i = 0; i < NUM_PKT_STATS; i++, bitmap_iterator_inc(&it))
41462306a36Sopenharmony_ci		if (bitmap_iterator_test(&it))
41562306a36Sopenharmony_ci			data[index++] = ((unsigned long *)&priv->pkstats)[i];
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	for (i = 0; i < NUM_XDP_STATS; i++, bitmap_iterator_inc(&it))
41862306a36Sopenharmony_ci		if (bitmap_iterator_test(&it))
41962306a36Sopenharmony_ci			data[index++] = ((unsigned long *)&priv->xdp_stats)[i];
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	for (i = 0; i < NUM_PHY_STATS; i++, bitmap_iterator_inc(&it))
42262306a36Sopenharmony_ci		if (bitmap_iterator_test(&it))
42362306a36Sopenharmony_ci			data[index++] = ((unsigned long *)&priv->phy_stats)[i];
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	for (i = 0; i < priv->tx_ring_num[TX]; i++) {
42662306a36Sopenharmony_ci		data[index++] = priv->tx_ring[TX][i]->packets;
42762306a36Sopenharmony_ci		data[index++] = priv->tx_ring[TX][i]->bytes;
42862306a36Sopenharmony_ci	}
42962306a36Sopenharmony_ci	for (i = 0; i < priv->rx_ring_num; i++) {
43062306a36Sopenharmony_ci		data[index++] = priv->rx_ring[i]->packets;
43162306a36Sopenharmony_ci		data[index++] = priv->rx_ring[i]->bytes;
43262306a36Sopenharmony_ci		data[index++] = priv->rx_ring[i]->dropped;
43362306a36Sopenharmony_ci		data[index++] = priv->rx_ring[i]->xdp_drop;
43462306a36Sopenharmony_ci		data[index++] = priv->rx_ring[i]->xdp_redirect;
43562306a36Sopenharmony_ci		data[index++] = priv->rx_ring[i]->xdp_redirect_fail;
43662306a36Sopenharmony_ci		data[index++] = priv->rx_ring[i]->xdp_tx;
43762306a36Sopenharmony_ci		data[index++] = priv->rx_ring[i]->xdp_tx_full;
43862306a36Sopenharmony_ci	}
43962306a36Sopenharmony_ci	spin_unlock_bh(&priv->stats_lock);
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci}
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_cistatic void mlx4_en_self_test(struct net_device *dev,
44462306a36Sopenharmony_ci			      struct ethtool_test *etest, u64 *buf)
44562306a36Sopenharmony_ci{
44662306a36Sopenharmony_ci	mlx4_en_ex_selftest(dev, &etest->flags, buf);
44762306a36Sopenharmony_ci}
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_cistatic void mlx4_en_get_strings(struct net_device *dev,
45062306a36Sopenharmony_ci				uint32_t stringset, uint8_t *data)
45162306a36Sopenharmony_ci{
45262306a36Sopenharmony_ci	struct mlx4_en_priv *priv = netdev_priv(dev);
45362306a36Sopenharmony_ci	int index = 0;
45462306a36Sopenharmony_ci	int i, strings = 0;
45562306a36Sopenharmony_ci	struct bitmap_iterator it;
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	bitmap_iterator_init(&it, priv->stats_bitmap.bitmap, NUM_ALL_STATS);
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	switch (stringset) {
46062306a36Sopenharmony_ci	case ETH_SS_TEST:
46162306a36Sopenharmony_ci		for (i = 0; i < MLX4_EN_NUM_SELF_TEST - 2; i++)
46262306a36Sopenharmony_ci			strcpy(data + i * ETH_GSTRING_LEN, mlx4_en_test_names[i]);
46362306a36Sopenharmony_ci		if (priv->mdev->dev->caps.flags & MLX4_DEV_CAP_FLAG_UC_LOOPBACK)
46462306a36Sopenharmony_ci			for (; i < MLX4_EN_NUM_SELF_TEST; i++)
46562306a36Sopenharmony_ci				strcpy(data + i * ETH_GSTRING_LEN, mlx4_en_test_names[i]);
46662306a36Sopenharmony_ci		break;
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci	case ETH_SS_STATS:
46962306a36Sopenharmony_ci		/* Add main counters */
47062306a36Sopenharmony_ci		for (i = 0; i < NUM_MAIN_STATS; i++, strings++,
47162306a36Sopenharmony_ci		     bitmap_iterator_inc(&it))
47262306a36Sopenharmony_ci			if (bitmap_iterator_test(&it))
47362306a36Sopenharmony_ci				strcpy(data + (index++) * ETH_GSTRING_LEN,
47462306a36Sopenharmony_ci				       main_strings[strings]);
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci		for (i = 0; i < NUM_PORT_STATS; i++, strings++,
47762306a36Sopenharmony_ci		     bitmap_iterator_inc(&it))
47862306a36Sopenharmony_ci			if (bitmap_iterator_test(&it))
47962306a36Sopenharmony_ci				strcpy(data + (index++) * ETH_GSTRING_LEN,
48062306a36Sopenharmony_ci				       main_strings[strings]);
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci		for (i = 0; i < NUM_PF_STATS; i++, strings++,
48362306a36Sopenharmony_ci		     bitmap_iterator_inc(&it))
48462306a36Sopenharmony_ci			if (bitmap_iterator_test(&it))
48562306a36Sopenharmony_ci				strcpy(data + (index++) * ETH_GSTRING_LEN,
48662306a36Sopenharmony_ci				       main_strings[strings]);
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci		for (i = 0; i < NUM_FLOW_STATS; i++, strings++,
48962306a36Sopenharmony_ci		     bitmap_iterator_inc(&it))
49062306a36Sopenharmony_ci			if (bitmap_iterator_test(&it))
49162306a36Sopenharmony_ci				strcpy(data + (index++) * ETH_GSTRING_LEN,
49262306a36Sopenharmony_ci				       main_strings[strings]);
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci		for (i = 0; i < NUM_PKT_STATS; i++, strings++,
49562306a36Sopenharmony_ci		     bitmap_iterator_inc(&it))
49662306a36Sopenharmony_ci			if (bitmap_iterator_test(&it))
49762306a36Sopenharmony_ci				strcpy(data + (index++) * ETH_GSTRING_LEN,
49862306a36Sopenharmony_ci				       main_strings[strings]);
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci		for (i = 0; i < NUM_XDP_STATS; i++, strings++,
50162306a36Sopenharmony_ci		     bitmap_iterator_inc(&it))
50262306a36Sopenharmony_ci			if (bitmap_iterator_test(&it))
50362306a36Sopenharmony_ci				strcpy(data + (index++) * ETH_GSTRING_LEN,
50462306a36Sopenharmony_ci				       main_strings[strings]);
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci		for (i = 0; i < NUM_PHY_STATS; i++, strings++,
50762306a36Sopenharmony_ci		     bitmap_iterator_inc(&it))
50862306a36Sopenharmony_ci			if (bitmap_iterator_test(&it))
50962306a36Sopenharmony_ci				strcpy(data + (index++) * ETH_GSTRING_LEN,
51062306a36Sopenharmony_ci				       main_strings[strings]);
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci		for (i = 0; i < priv->tx_ring_num[TX]; i++) {
51362306a36Sopenharmony_ci			sprintf(data + (index++) * ETH_GSTRING_LEN,
51462306a36Sopenharmony_ci				"tx%d_packets", i);
51562306a36Sopenharmony_ci			sprintf(data + (index++) * ETH_GSTRING_LEN,
51662306a36Sopenharmony_ci				"tx%d_bytes", i);
51762306a36Sopenharmony_ci		}
51862306a36Sopenharmony_ci		for (i = 0; i < priv->rx_ring_num; i++) {
51962306a36Sopenharmony_ci			sprintf(data + (index++) * ETH_GSTRING_LEN,
52062306a36Sopenharmony_ci				"rx%d_packets", i);
52162306a36Sopenharmony_ci			sprintf(data + (index++) * ETH_GSTRING_LEN,
52262306a36Sopenharmony_ci				"rx%d_bytes", i);
52362306a36Sopenharmony_ci			sprintf(data + (index++) * ETH_GSTRING_LEN,
52462306a36Sopenharmony_ci				"rx%d_dropped", i);
52562306a36Sopenharmony_ci			sprintf(data + (index++) * ETH_GSTRING_LEN,
52662306a36Sopenharmony_ci				"rx%d_xdp_drop", i);
52762306a36Sopenharmony_ci			sprintf(data + (index++) * ETH_GSTRING_LEN,
52862306a36Sopenharmony_ci				"rx%d_xdp_redirect", i);
52962306a36Sopenharmony_ci			sprintf(data + (index++) * ETH_GSTRING_LEN,
53062306a36Sopenharmony_ci				"rx%d_xdp_redirect_fail", i);
53162306a36Sopenharmony_ci			sprintf(data + (index++) * ETH_GSTRING_LEN,
53262306a36Sopenharmony_ci				"rx%d_xdp_tx", i);
53362306a36Sopenharmony_ci			sprintf(data + (index++) * ETH_GSTRING_LEN,
53462306a36Sopenharmony_ci				"rx%d_xdp_tx_full", i);
53562306a36Sopenharmony_ci		}
53662306a36Sopenharmony_ci		break;
53762306a36Sopenharmony_ci	case ETH_SS_PRIV_FLAGS:
53862306a36Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(mlx4_en_priv_flags); i++)
53962306a36Sopenharmony_ci			strcpy(data + i * ETH_GSTRING_LEN,
54062306a36Sopenharmony_ci			       mlx4_en_priv_flags[i]);
54162306a36Sopenharmony_ci		break;
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	}
54462306a36Sopenharmony_ci}
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_cistatic u32 mlx4_en_autoneg_get(struct net_device *dev)
54762306a36Sopenharmony_ci{
54862306a36Sopenharmony_ci	struct mlx4_en_priv *priv = netdev_priv(dev);
54962306a36Sopenharmony_ci	struct mlx4_en_dev *mdev = priv->mdev;
55062306a36Sopenharmony_ci	u32 autoneg = AUTONEG_DISABLE;
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	if ((mdev->dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_ETH_BACKPL_AN_REP) &&
55362306a36Sopenharmony_ci	    (priv->port_state.flags & MLX4_EN_PORT_ANE))
55462306a36Sopenharmony_ci		autoneg = AUTONEG_ENABLE;
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	return autoneg;
55762306a36Sopenharmony_ci}
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_cistatic void ptys2ethtool_update_supported_port(unsigned long *mask,
56062306a36Sopenharmony_ci					       struct mlx4_ptys_reg *ptys_reg)
56162306a36Sopenharmony_ci{
56262306a36Sopenharmony_ci	u32 eth_proto = be32_to_cpu(ptys_reg->eth_proto_cap);
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	if (eth_proto & (MLX4_PROT_MASK(MLX4_10GBASE_T)
56562306a36Sopenharmony_ci			 | MLX4_PROT_MASK(MLX4_1000BASE_T)
56662306a36Sopenharmony_ci			 | MLX4_PROT_MASK(MLX4_100BASE_TX))) {
56762306a36Sopenharmony_ci		__set_bit(ETHTOOL_LINK_MODE_TP_BIT, mask);
56862306a36Sopenharmony_ci	} else if (eth_proto & (MLX4_PROT_MASK(MLX4_10GBASE_CR)
56962306a36Sopenharmony_ci			 | MLX4_PROT_MASK(MLX4_10GBASE_SR)
57062306a36Sopenharmony_ci			 | MLX4_PROT_MASK(MLX4_56GBASE_SR4)
57162306a36Sopenharmony_ci			 | MLX4_PROT_MASK(MLX4_40GBASE_CR4)
57262306a36Sopenharmony_ci			 | MLX4_PROT_MASK(MLX4_40GBASE_SR4)
57362306a36Sopenharmony_ci			 | MLX4_PROT_MASK(MLX4_1000BASE_CX_SGMII))) {
57462306a36Sopenharmony_ci		__set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, mask);
57562306a36Sopenharmony_ci	} else if (eth_proto & (MLX4_PROT_MASK(MLX4_56GBASE_KR4)
57662306a36Sopenharmony_ci			 | MLX4_PROT_MASK(MLX4_40GBASE_KR4)
57762306a36Sopenharmony_ci			 | MLX4_PROT_MASK(MLX4_20GBASE_KR2)
57862306a36Sopenharmony_ci			 | MLX4_PROT_MASK(MLX4_10GBASE_KR)
57962306a36Sopenharmony_ci			 | MLX4_PROT_MASK(MLX4_10GBASE_KX4)
58062306a36Sopenharmony_ci			 | MLX4_PROT_MASK(MLX4_1000BASE_KX))) {
58162306a36Sopenharmony_ci		__set_bit(ETHTOOL_LINK_MODE_Backplane_BIT, mask);
58262306a36Sopenharmony_ci	}
58362306a36Sopenharmony_ci}
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_cistatic u32 ptys_get_active_port(struct mlx4_ptys_reg *ptys_reg)
58662306a36Sopenharmony_ci{
58762306a36Sopenharmony_ci	u32 eth_proto = be32_to_cpu(ptys_reg->eth_proto_oper);
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	if (!eth_proto) /* link down */
59062306a36Sopenharmony_ci		eth_proto = be32_to_cpu(ptys_reg->eth_proto_cap);
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci	if (eth_proto & (MLX4_PROT_MASK(MLX4_10GBASE_T)
59362306a36Sopenharmony_ci			 | MLX4_PROT_MASK(MLX4_1000BASE_T)
59462306a36Sopenharmony_ci			 | MLX4_PROT_MASK(MLX4_100BASE_TX))) {
59562306a36Sopenharmony_ci			return PORT_TP;
59662306a36Sopenharmony_ci	}
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci	if (eth_proto & (MLX4_PROT_MASK(MLX4_10GBASE_SR)
59962306a36Sopenharmony_ci			 | MLX4_PROT_MASK(MLX4_56GBASE_SR4)
60062306a36Sopenharmony_ci			 | MLX4_PROT_MASK(MLX4_40GBASE_SR4)
60162306a36Sopenharmony_ci			 | MLX4_PROT_MASK(MLX4_1000BASE_CX_SGMII))) {
60262306a36Sopenharmony_ci			return PORT_FIBRE;
60362306a36Sopenharmony_ci	}
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci	if (eth_proto & (MLX4_PROT_MASK(MLX4_10GBASE_CR)
60662306a36Sopenharmony_ci			 | MLX4_PROT_MASK(MLX4_56GBASE_CR4)
60762306a36Sopenharmony_ci			 | MLX4_PROT_MASK(MLX4_40GBASE_CR4))) {
60862306a36Sopenharmony_ci			return PORT_DA;
60962306a36Sopenharmony_ci	}
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci	if (eth_proto & (MLX4_PROT_MASK(MLX4_56GBASE_KR4)
61262306a36Sopenharmony_ci			 | MLX4_PROT_MASK(MLX4_40GBASE_KR4)
61362306a36Sopenharmony_ci			 | MLX4_PROT_MASK(MLX4_20GBASE_KR2)
61462306a36Sopenharmony_ci			 | MLX4_PROT_MASK(MLX4_10GBASE_KR)
61562306a36Sopenharmony_ci			 | MLX4_PROT_MASK(MLX4_10GBASE_KX4)
61662306a36Sopenharmony_ci			 | MLX4_PROT_MASK(MLX4_1000BASE_KX))) {
61762306a36Sopenharmony_ci			return PORT_NONE;
61862306a36Sopenharmony_ci	}
61962306a36Sopenharmony_ci	return PORT_OTHER;
62062306a36Sopenharmony_ci}
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci#define MLX4_LINK_MODES_SZ \
62362306a36Sopenharmony_ci	(sizeof_field(struct mlx4_ptys_reg, eth_proto_cap) * 8)
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_cienum ethtool_report {
62662306a36Sopenharmony_ci	SUPPORTED = 0,
62762306a36Sopenharmony_ci	ADVERTISED = 1,
62862306a36Sopenharmony_ci};
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_cistruct ptys2ethtool_config {
63162306a36Sopenharmony_ci	__ETHTOOL_DECLARE_LINK_MODE_MASK(supported);
63262306a36Sopenharmony_ci	__ETHTOOL_DECLARE_LINK_MODE_MASK(advertised);
63362306a36Sopenharmony_ci	u32 speed;
63462306a36Sopenharmony_ci};
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_cistatic unsigned long *ptys2ethtool_link_mode(struct ptys2ethtool_config *cfg,
63762306a36Sopenharmony_ci					     enum ethtool_report report)
63862306a36Sopenharmony_ci{
63962306a36Sopenharmony_ci	switch (report) {
64062306a36Sopenharmony_ci	case SUPPORTED:
64162306a36Sopenharmony_ci		return cfg->supported;
64262306a36Sopenharmony_ci	case ADVERTISED:
64362306a36Sopenharmony_ci		return cfg->advertised;
64462306a36Sopenharmony_ci	}
64562306a36Sopenharmony_ci	return NULL;
64662306a36Sopenharmony_ci}
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci#define MLX4_BUILD_PTYS2ETHTOOL_CONFIG(reg_, speed_, ...)		\
64962306a36Sopenharmony_ci	({								\
65062306a36Sopenharmony_ci		struct ptys2ethtool_config *cfg;			\
65162306a36Sopenharmony_ci		static const unsigned int modes[] = { __VA_ARGS__ };	\
65262306a36Sopenharmony_ci		unsigned int i;						\
65362306a36Sopenharmony_ci		cfg = &ptys2ethtool_map[reg_];				\
65462306a36Sopenharmony_ci		cfg->speed = speed_;					\
65562306a36Sopenharmony_ci		linkmode_zero(cfg->supported);				\
65662306a36Sopenharmony_ci		linkmode_zero(cfg->advertised);				\
65762306a36Sopenharmony_ci		for (i = 0 ; i < ARRAY_SIZE(modes) ; ++i) {		\
65862306a36Sopenharmony_ci			__set_bit(modes[i], cfg->supported);		\
65962306a36Sopenharmony_ci			__set_bit(modes[i], cfg->advertised);		\
66062306a36Sopenharmony_ci		}							\
66162306a36Sopenharmony_ci	})
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci/* Translates mlx4 link mode to equivalent ethtool Link modes/speed */
66462306a36Sopenharmony_cistatic struct ptys2ethtool_config ptys2ethtool_map[MLX4_LINK_MODES_SZ];
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_civoid __init mlx4_en_init_ptys2ethtool_map(void)
66762306a36Sopenharmony_ci{
66862306a36Sopenharmony_ci	MLX4_BUILD_PTYS2ETHTOOL_CONFIG(MLX4_100BASE_TX, SPEED_100,
66962306a36Sopenharmony_ci				       ETHTOOL_LINK_MODE_100baseT_Full_BIT);
67062306a36Sopenharmony_ci	MLX4_BUILD_PTYS2ETHTOOL_CONFIG(MLX4_1000BASE_T, SPEED_1000,
67162306a36Sopenharmony_ci				       ETHTOOL_LINK_MODE_1000baseT_Full_BIT);
67262306a36Sopenharmony_ci	MLX4_BUILD_PTYS2ETHTOOL_CONFIG(MLX4_1000BASE_CX_SGMII, SPEED_1000,
67362306a36Sopenharmony_ci				       ETHTOOL_LINK_MODE_1000baseX_Full_BIT);
67462306a36Sopenharmony_ci	MLX4_BUILD_PTYS2ETHTOOL_CONFIG(MLX4_1000BASE_KX, SPEED_1000,
67562306a36Sopenharmony_ci				       ETHTOOL_LINK_MODE_1000baseKX_Full_BIT);
67662306a36Sopenharmony_ci	MLX4_BUILD_PTYS2ETHTOOL_CONFIG(MLX4_10GBASE_T, SPEED_10000,
67762306a36Sopenharmony_ci				       ETHTOOL_LINK_MODE_10000baseT_Full_BIT);
67862306a36Sopenharmony_ci	MLX4_BUILD_PTYS2ETHTOOL_CONFIG(MLX4_10GBASE_CX4, SPEED_10000,
67962306a36Sopenharmony_ci				       ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT);
68062306a36Sopenharmony_ci	MLX4_BUILD_PTYS2ETHTOOL_CONFIG(MLX4_10GBASE_KX4, SPEED_10000,
68162306a36Sopenharmony_ci				       ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT);
68262306a36Sopenharmony_ci	MLX4_BUILD_PTYS2ETHTOOL_CONFIG(MLX4_10GBASE_KR, SPEED_10000,
68362306a36Sopenharmony_ci				       ETHTOOL_LINK_MODE_10000baseKR_Full_BIT);
68462306a36Sopenharmony_ci	MLX4_BUILD_PTYS2ETHTOOL_CONFIG(MLX4_10GBASE_CR, SPEED_10000,
68562306a36Sopenharmony_ci				       ETHTOOL_LINK_MODE_10000baseCR_Full_BIT);
68662306a36Sopenharmony_ci	MLX4_BUILD_PTYS2ETHTOOL_CONFIG(MLX4_10GBASE_SR, SPEED_10000,
68762306a36Sopenharmony_ci				       ETHTOOL_LINK_MODE_10000baseSR_Full_BIT);
68862306a36Sopenharmony_ci	MLX4_BUILD_PTYS2ETHTOOL_CONFIG(MLX4_20GBASE_KR2, SPEED_20000,
68962306a36Sopenharmony_ci				       ETHTOOL_LINK_MODE_20000baseMLD2_Full_BIT,
69062306a36Sopenharmony_ci				       ETHTOOL_LINK_MODE_20000baseKR2_Full_BIT);
69162306a36Sopenharmony_ci	MLX4_BUILD_PTYS2ETHTOOL_CONFIG(MLX4_40GBASE_CR4, SPEED_40000,
69262306a36Sopenharmony_ci				       ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT);
69362306a36Sopenharmony_ci	MLX4_BUILD_PTYS2ETHTOOL_CONFIG(MLX4_40GBASE_KR4, SPEED_40000,
69462306a36Sopenharmony_ci				       ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT);
69562306a36Sopenharmony_ci	MLX4_BUILD_PTYS2ETHTOOL_CONFIG(MLX4_40GBASE_SR4, SPEED_40000,
69662306a36Sopenharmony_ci				       ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT);
69762306a36Sopenharmony_ci	MLX4_BUILD_PTYS2ETHTOOL_CONFIG(MLX4_56GBASE_KR4, SPEED_56000,
69862306a36Sopenharmony_ci				       ETHTOOL_LINK_MODE_56000baseKR4_Full_BIT);
69962306a36Sopenharmony_ci	MLX4_BUILD_PTYS2ETHTOOL_CONFIG(MLX4_56GBASE_CR4, SPEED_56000,
70062306a36Sopenharmony_ci				       ETHTOOL_LINK_MODE_56000baseCR4_Full_BIT);
70162306a36Sopenharmony_ci	MLX4_BUILD_PTYS2ETHTOOL_CONFIG(MLX4_56GBASE_SR4, SPEED_56000,
70262306a36Sopenharmony_ci				       ETHTOOL_LINK_MODE_56000baseSR4_Full_BIT);
70362306a36Sopenharmony_ci};
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_cistatic void ptys2ethtool_update_link_modes(unsigned long *link_modes,
70662306a36Sopenharmony_ci					   u32 eth_proto,
70762306a36Sopenharmony_ci					   enum ethtool_report report)
70862306a36Sopenharmony_ci{
70962306a36Sopenharmony_ci	int i;
71062306a36Sopenharmony_ci	for (i = 0; i < MLX4_LINK_MODES_SZ; i++) {
71162306a36Sopenharmony_ci		if (eth_proto & MLX4_PROT_MASK(i))
71262306a36Sopenharmony_ci			linkmode_or(link_modes, link_modes,
71362306a36Sopenharmony_ci				    ptys2ethtool_link_mode(&ptys2ethtool_map[i], report));
71462306a36Sopenharmony_ci	}
71562306a36Sopenharmony_ci}
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_cistatic u32 ethtool2ptys_link_modes(const unsigned long *link_modes,
71862306a36Sopenharmony_ci				   enum ethtool_report report)
71962306a36Sopenharmony_ci{
72062306a36Sopenharmony_ci	int i;
72162306a36Sopenharmony_ci	u32 ptys_modes = 0;
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci	for (i = 0; i < MLX4_LINK_MODES_SZ; i++) {
72462306a36Sopenharmony_ci		ulong *map_mode = ptys2ethtool_link_mode(&ptys2ethtool_map[i],
72562306a36Sopenharmony_ci							 report);
72662306a36Sopenharmony_ci		if (linkmode_intersects(map_mode, link_modes))
72762306a36Sopenharmony_ci			ptys_modes |= 1 << i;
72862306a36Sopenharmony_ci	}
72962306a36Sopenharmony_ci	return ptys_modes;
73062306a36Sopenharmony_ci}
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci/* Convert actual speed (SPEED_XXX) to ptys link modes */
73362306a36Sopenharmony_cistatic u32 speed2ptys_link_modes(u32 speed)
73462306a36Sopenharmony_ci{
73562306a36Sopenharmony_ci	int i;
73662306a36Sopenharmony_ci	u32 ptys_modes = 0;
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ci	for (i = 0; i < MLX4_LINK_MODES_SZ; i++) {
73962306a36Sopenharmony_ci		if (ptys2ethtool_map[i].speed == speed)
74062306a36Sopenharmony_ci			ptys_modes |= 1 << i;
74162306a36Sopenharmony_ci	}
74262306a36Sopenharmony_ci	return ptys_modes;
74362306a36Sopenharmony_ci}
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_cistatic int
74662306a36Sopenharmony_ciethtool_get_ptys_link_ksettings(struct net_device *dev,
74762306a36Sopenharmony_ci				struct ethtool_link_ksettings *link_ksettings)
74862306a36Sopenharmony_ci{
74962306a36Sopenharmony_ci	struct mlx4_en_priv *priv = netdev_priv(dev);
75062306a36Sopenharmony_ci	struct mlx4_ptys_reg ptys_reg;
75162306a36Sopenharmony_ci	u32 eth_proto;
75262306a36Sopenharmony_ci	int ret;
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci	memset(&ptys_reg, 0, sizeof(ptys_reg));
75562306a36Sopenharmony_ci	ptys_reg.local_port = priv->port;
75662306a36Sopenharmony_ci	ptys_reg.proto_mask = MLX4_PTYS_EN;
75762306a36Sopenharmony_ci	ret = mlx4_ACCESS_PTYS_REG(priv->mdev->dev,
75862306a36Sopenharmony_ci				   MLX4_ACCESS_REG_QUERY, &ptys_reg);
75962306a36Sopenharmony_ci	if (ret) {
76062306a36Sopenharmony_ci		en_warn(priv, "Failed to run mlx4_ACCESS_PTYS_REG status(%x)",
76162306a36Sopenharmony_ci			ret);
76262306a36Sopenharmony_ci		return ret;
76362306a36Sopenharmony_ci	}
76462306a36Sopenharmony_ci	en_dbg(DRV, priv, "ptys_reg.proto_mask       %x\n",
76562306a36Sopenharmony_ci	       ptys_reg.proto_mask);
76662306a36Sopenharmony_ci	en_dbg(DRV, priv, "ptys_reg.eth_proto_cap    %x\n",
76762306a36Sopenharmony_ci	       be32_to_cpu(ptys_reg.eth_proto_cap));
76862306a36Sopenharmony_ci	en_dbg(DRV, priv, "ptys_reg.eth_proto_admin  %x\n",
76962306a36Sopenharmony_ci	       be32_to_cpu(ptys_reg.eth_proto_admin));
77062306a36Sopenharmony_ci	en_dbg(DRV, priv, "ptys_reg.eth_proto_oper   %x\n",
77162306a36Sopenharmony_ci	       be32_to_cpu(ptys_reg.eth_proto_oper));
77262306a36Sopenharmony_ci	en_dbg(DRV, priv, "ptys_reg.eth_proto_lp_adv %x\n",
77362306a36Sopenharmony_ci	       be32_to_cpu(ptys_reg.eth_proto_lp_adv));
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci	/* reset supported/advertising masks */
77662306a36Sopenharmony_ci	ethtool_link_ksettings_zero_link_mode(link_ksettings, supported);
77762306a36Sopenharmony_ci	ethtool_link_ksettings_zero_link_mode(link_ksettings, advertising);
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci	ptys2ethtool_update_supported_port(link_ksettings->link_modes.supported,
78062306a36Sopenharmony_ci					   &ptys_reg);
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci	eth_proto = be32_to_cpu(ptys_reg.eth_proto_cap);
78362306a36Sopenharmony_ci	ptys2ethtool_update_link_modes(link_ksettings->link_modes.supported,
78462306a36Sopenharmony_ci				       eth_proto, SUPPORTED);
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_ci	eth_proto = be32_to_cpu(ptys_reg.eth_proto_admin);
78762306a36Sopenharmony_ci	ptys2ethtool_update_link_modes(link_ksettings->link_modes.advertising,
78862306a36Sopenharmony_ci				       eth_proto, ADVERTISED);
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci	ethtool_link_ksettings_add_link_mode(link_ksettings, supported,
79162306a36Sopenharmony_ci					     Pause);
79262306a36Sopenharmony_ci	ethtool_link_ksettings_add_link_mode(link_ksettings, supported,
79362306a36Sopenharmony_ci					     Asym_Pause);
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_ci	if (priv->prof->tx_pause)
79662306a36Sopenharmony_ci		ethtool_link_ksettings_add_link_mode(link_ksettings,
79762306a36Sopenharmony_ci						     advertising, Pause);
79862306a36Sopenharmony_ci	if (priv->prof->tx_pause ^ priv->prof->rx_pause)
79962306a36Sopenharmony_ci		ethtool_link_ksettings_add_link_mode(link_ksettings,
80062306a36Sopenharmony_ci						     advertising, Asym_Pause);
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci	link_ksettings->base.port = ptys_get_active_port(&ptys_reg);
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci	if (mlx4_en_autoneg_get(dev)) {
80562306a36Sopenharmony_ci		ethtool_link_ksettings_add_link_mode(link_ksettings,
80662306a36Sopenharmony_ci						     supported, Autoneg);
80762306a36Sopenharmony_ci		ethtool_link_ksettings_add_link_mode(link_ksettings,
80862306a36Sopenharmony_ci						     advertising, Autoneg);
80962306a36Sopenharmony_ci	}
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci	link_ksettings->base.autoneg
81262306a36Sopenharmony_ci		= (priv->port_state.flags & MLX4_EN_PORT_ANC) ?
81362306a36Sopenharmony_ci		AUTONEG_ENABLE : AUTONEG_DISABLE;
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_ci	eth_proto = be32_to_cpu(ptys_reg.eth_proto_lp_adv);
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci	ethtool_link_ksettings_zero_link_mode(link_ksettings, lp_advertising);
81862306a36Sopenharmony_ci	ptys2ethtool_update_link_modes(
81962306a36Sopenharmony_ci		link_ksettings->link_modes.lp_advertising,
82062306a36Sopenharmony_ci		eth_proto, ADVERTISED);
82162306a36Sopenharmony_ci	if (priv->port_state.flags & MLX4_EN_PORT_ANC)
82262306a36Sopenharmony_ci		ethtool_link_ksettings_add_link_mode(link_ksettings,
82362306a36Sopenharmony_ci						     lp_advertising, Autoneg);
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci	link_ksettings->base.phy_address = 0;
82662306a36Sopenharmony_ci	link_ksettings->base.mdio_support = 0;
82762306a36Sopenharmony_ci	link_ksettings->base.eth_tp_mdix = ETH_TP_MDI_INVALID;
82862306a36Sopenharmony_ci	link_ksettings->base.eth_tp_mdix_ctrl = ETH_TP_MDI_AUTO;
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci	return ret;
83162306a36Sopenharmony_ci}
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_cistatic void
83462306a36Sopenharmony_ciethtool_get_default_link_ksettings(
83562306a36Sopenharmony_ci	struct net_device *dev, struct ethtool_link_ksettings *link_ksettings)
83662306a36Sopenharmony_ci{
83762306a36Sopenharmony_ci	struct mlx4_en_priv *priv = netdev_priv(dev);
83862306a36Sopenharmony_ci	int trans_type;
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci	link_ksettings->base.autoneg = AUTONEG_DISABLE;
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci	ethtool_link_ksettings_zero_link_mode(link_ksettings, supported);
84362306a36Sopenharmony_ci	ethtool_link_ksettings_add_link_mode(link_ksettings, supported,
84462306a36Sopenharmony_ci					     10000baseT_Full);
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci	ethtool_link_ksettings_zero_link_mode(link_ksettings, advertising);
84762306a36Sopenharmony_ci	ethtool_link_ksettings_add_link_mode(link_ksettings, advertising,
84862306a36Sopenharmony_ci					     10000baseT_Full);
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_ci	trans_type = priv->port_state.transceiver;
85162306a36Sopenharmony_ci	if (trans_type > 0 && trans_type <= 0xC) {
85262306a36Sopenharmony_ci		link_ksettings->base.port = PORT_FIBRE;
85362306a36Sopenharmony_ci		ethtool_link_ksettings_add_link_mode(link_ksettings,
85462306a36Sopenharmony_ci						     supported, FIBRE);
85562306a36Sopenharmony_ci		ethtool_link_ksettings_add_link_mode(link_ksettings,
85662306a36Sopenharmony_ci						     advertising, FIBRE);
85762306a36Sopenharmony_ci	} else if (trans_type == 0x80 || trans_type == 0) {
85862306a36Sopenharmony_ci		link_ksettings->base.port = PORT_TP;
85962306a36Sopenharmony_ci		ethtool_link_ksettings_add_link_mode(link_ksettings,
86062306a36Sopenharmony_ci						     supported, TP);
86162306a36Sopenharmony_ci		ethtool_link_ksettings_add_link_mode(link_ksettings,
86262306a36Sopenharmony_ci						     advertising, TP);
86362306a36Sopenharmony_ci	} else  {
86462306a36Sopenharmony_ci		link_ksettings->base.port = -1;
86562306a36Sopenharmony_ci	}
86662306a36Sopenharmony_ci}
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_cistatic int
86962306a36Sopenharmony_cimlx4_en_get_link_ksettings(struct net_device *dev,
87062306a36Sopenharmony_ci			   struct ethtool_link_ksettings *link_ksettings)
87162306a36Sopenharmony_ci{
87262306a36Sopenharmony_ci	struct mlx4_en_priv *priv = netdev_priv(dev);
87362306a36Sopenharmony_ci	int ret = -EINVAL;
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci	if (mlx4_en_QUERY_PORT(priv->mdev, priv->port))
87662306a36Sopenharmony_ci		return -ENOMEM;
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci	en_dbg(DRV, priv, "query port state.flags ANC(%x) ANE(%x)\n",
87962306a36Sopenharmony_ci	       priv->port_state.flags & MLX4_EN_PORT_ANC,
88062306a36Sopenharmony_ci	       priv->port_state.flags & MLX4_EN_PORT_ANE);
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_ci	if (priv->mdev->dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_ETH_PROT_CTRL)
88362306a36Sopenharmony_ci		ret = ethtool_get_ptys_link_ksettings(dev, link_ksettings);
88462306a36Sopenharmony_ci	if (ret) /* ETH PROT CRTL is not supported or PTYS CMD failed */
88562306a36Sopenharmony_ci		ethtool_get_default_link_ksettings(dev, link_ksettings);
88662306a36Sopenharmony_ci
88762306a36Sopenharmony_ci	if (netif_carrier_ok(dev)) {
88862306a36Sopenharmony_ci		link_ksettings->base.speed = priv->port_state.link_speed;
88962306a36Sopenharmony_ci		link_ksettings->base.duplex = DUPLEX_FULL;
89062306a36Sopenharmony_ci	} else {
89162306a36Sopenharmony_ci		link_ksettings->base.speed = SPEED_UNKNOWN;
89262306a36Sopenharmony_ci		link_ksettings->base.duplex = DUPLEX_UNKNOWN;
89362306a36Sopenharmony_ci	}
89462306a36Sopenharmony_ci	return 0;
89562306a36Sopenharmony_ci}
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci/* Calculate PTYS admin according ethtool speed (SPEED_XXX) */
89862306a36Sopenharmony_cistatic __be32 speed_set_ptys_admin(struct mlx4_en_priv *priv, u32 speed,
89962306a36Sopenharmony_ci				   __be32 proto_cap)
90062306a36Sopenharmony_ci{
90162306a36Sopenharmony_ci	__be32 proto_admin = 0;
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_ci	if (!speed) { /* Speed = 0 ==> Reset Link modes */
90462306a36Sopenharmony_ci		proto_admin = proto_cap;
90562306a36Sopenharmony_ci		en_info(priv, "Speed was set to 0, Reset advertised Link Modes to default (%x)\n",
90662306a36Sopenharmony_ci			be32_to_cpu(proto_cap));
90762306a36Sopenharmony_ci	} else {
90862306a36Sopenharmony_ci		u32 ptys_link_modes = speed2ptys_link_modes(speed);
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci		proto_admin = cpu_to_be32(ptys_link_modes) & proto_cap;
91162306a36Sopenharmony_ci		en_info(priv, "Setting Speed to %d\n", speed);
91262306a36Sopenharmony_ci	}
91362306a36Sopenharmony_ci	return proto_admin;
91462306a36Sopenharmony_ci}
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_cistatic int
91762306a36Sopenharmony_cimlx4_en_set_link_ksettings(struct net_device *dev,
91862306a36Sopenharmony_ci			   const struct ethtool_link_ksettings *link_ksettings)
91962306a36Sopenharmony_ci{
92062306a36Sopenharmony_ci	struct mlx4_en_priv *priv = netdev_priv(dev);
92162306a36Sopenharmony_ci	struct mlx4_ptys_reg ptys_reg;
92262306a36Sopenharmony_ci	__be32 proto_admin;
92362306a36Sopenharmony_ci	u8 cur_autoneg;
92462306a36Sopenharmony_ci	int ret;
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ci	u32 ptys_adv = ethtool2ptys_link_modes(
92762306a36Sopenharmony_ci		link_ksettings->link_modes.advertising, ADVERTISED);
92862306a36Sopenharmony_ci	const int speed = link_ksettings->base.speed;
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci	en_dbg(DRV, priv,
93162306a36Sopenharmony_ci	       "Set Speed=%d adv={%*pbl} autoneg=%d duplex=%d\n",
93262306a36Sopenharmony_ci	       speed, __ETHTOOL_LINK_MODE_MASK_NBITS,
93362306a36Sopenharmony_ci	       link_ksettings->link_modes.advertising,
93462306a36Sopenharmony_ci	       link_ksettings->base.autoneg,
93562306a36Sopenharmony_ci	       link_ksettings->base.duplex);
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_ci	if (!(priv->mdev->dev->caps.flags2 &
93862306a36Sopenharmony_ci	      MLX4_DEV_CAP_FLAG2_ETH_PROT_CTRL) ||
93962306a36Sopenharmony_ci	    (link_ksettings->base.duplex == DUPLEX_HALF))
94062306a36Sopenharmony_ci		return -EINVAL;
94162306a36Sopenharmony_ci
94262306a36Sopenharmony_ci	memset(&ptys_reg, 0, sizeof(ptys_reg));
94362306a36Sopenharmony_ci	ptys_reg.local_port = priv->port;
94462306a36Sopenharmony_ci	ptys_reg.proto_mask = MLX4_PTYS_EN;
94562306a36Sopenharmony_ci	ret = mlx4_ACCESS_PTYS_REG(priv->mdev->dev,
94662306a36Sopenharmony_ci				   MLX4_ACCESS_REG_QUERY, &ptys_reg);
94762306a36Sopenharmony_ci	if (ret) {
94862306a36Sopenharmony_ci		en_warn(priv, "Failed to QUERY mlx4_ACCESS_PTYS_REG status(%x)\n",
94962306a36Sopenharmony_ci			ret);
95062306a36Sopenharmony_ci		return 0;
95162306a36Sopenharmony_ci	}
95262306a36Sopenharmony_ci
95362306a36Sopenharmony_ci	cur_autoneg = ptys_reg.flags & MLX4_PTYS_AN_DISABLE_ADMIN ?
95462306a36Sopenharmony_ci				AUTONEG_DISABLE : AUTONEG_ENABLE;
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_ci	if (link_ksettings->base.autoneg == AUTONEG_DISABLE) {
95762306a36Sopenharmony_ci		proto_admin = speed_set_ptys_admin(priv, speed,
95862306a36Sopenharmony_ci						   ptys_reg.eth_proto_cap);
95962306a36Sopenharmony_ci		if ((be32_to_cpu(proto_admin) &
96062306a36Sopenharmony_ci		     (MLX4_PROT_MASK(MLX4_1000BASE_CX_SGMII) |
96162306a36Sopenharmony_ci		      MLX4_PROT_MASK(MLX4_1000BASE_KX))) &&
96262306a36Sopenharmony_ci		    (ptys_reg.flags & MLX4_PTYS_AN_DISABLE_CAP))
96362306a36Sopenharmony_ci			ptys_reg.flags |= MLX4_PTYS_AN_DISABLE_ADMIN;
96462306a36Sopenharmony_ci	} else {
96562306a36Sopenharmony_ci		proto_admin = cpu_to_be32(ptys_adv);
96662306a36Sopenharmony_ci		ptys_reg.flags &= ~MLX4_PTYS_AN_DISABLE_ADMIN;
96762306a36Sopenharmony_ci	}
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_ci	proto_admin &= ptys_reg.eth_proto_cap;
97062306a36Sopenharmony_ci	if (!proto_admin) {
97162306a36Sopenharmony_ci		en_warn(priv, "Not supported link mode(s) requested, check supported link modes.\n");
97262306a36Sopenharmony_ci		return -EINVAL; /* nothing to change due to bad input */
97362306a36Sopenharmony_ci	}
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_ci	if ((proto_admin == ptys_reg.eth_proto_admin) &&
97662306a36Sopenharmony_ci	    ((ptys_reg.flags & MLX4_PTYS_AN_DISABLE_CAP) &&
97762306a36Sopenharmony_ci	     (link_ksettings->base.autoneg == cur_autoneg)))
97862306a36Sopenharmony_ci		return 0; /* Nothing to change */
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ci	en_dbg(DRV, priv, "mlx4_ACCESS_PTYS_REG SET: ptys_reg.eth_proto_admin = 0x%x\n",
98162306a36Sopenharmony_ci	       be32_to_cpu(proto_admin));
98262306a36Sopenharmony_ci
98362306a36Sopenharmony_ci	ptys_reg.eth_proto_admin = proto_admin;
98462306a36Sopenharmony_ci	ret = mlx4_ACCESS_PTYS_REG(priv->mdev->dev, MLX4_ACCESS_REG_WRITE,
98562306a36Sopenharmony_ci				   &ptys_reg);
98662306a36Sopenharmony_ci	if (ret) {
98762306a36Sopenharmony_ci		en_warn(priv, "Failed to write mlx4_ACCESS_PTYS_REG eth_proto_admin(0x%x) status(0x%x)",
98862306a36Sopenharmony_ci			be32_to_cpu(ptys_reg.eth_proto_admin), ret);
98962306a36Sopenharmony_ci		return ret;
99062306a36Sopenharmony_ci	}
99162306a36Sopenharmony_ci
99262306a36Sopenharmony_ci	mutex_lock(&priv->mdev->state_lock);
99362306a36Sopenharmony_ci	if (priv->port_up) {
99462306a36Sopenharmony_ci		en_warn(priv, "Port link mode changed, restarting port...\n");
99562306a36Sopenharmony_ci		mlx4_en_stop_port(dev, 1);
99662306a36Sopenharmony_ci		if (mlx4_en_start_port(dev))
99762306a36Sopenharmony_ci			en_err(priv, "Failed restarting port %d\n", priv->port);
99862306a36Sopenharmony_ci	}
99962306a36Sopenharmony_ci	mutex_unlock(&priv->mdev->state_lock);
100062306a36Sopenharmony_ci	return 0;
100162306a36Sopenharmony_ci}
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_cistatic int mlx4_en_get_coalesce(struct net_device *dev,
100462306a36Sopenharmony_ci				struct ethtool_coalesce *coal,
100562306a36Sopenharmony_ci				struct kernel_ethtool_coalesce *kernel_coal,
100662306a36Sopenharmony_ci				struct netlink_ext_ack *extack)
100762306a36Sopenharmony_ci{
100862306a36Sopenharmony_ci	struct mlx4_en_priv *priv = netdev_priv(dev);
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_ci	coal->tx_coalesce_usecs = priv->tx_usecs;
101162306a36Sopenharmony_ci	coal->tx_max_coalesced_frames = priv->tx_frames;
101262306a36Sopenharmony_ci	coal->tx_max_coalesced_frames_irq = priv->tx_work_limit;
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_ci	coal->rx_coalesce_usecs = priv->rx_usecs;
101562306a36Sopenharmony_ci	coal->rx_max_coalesced_frames = priv->rx_frames;
101662306a36Sopenharmony_ci
101762306a36Sopenharmony_ci	coal->pkt_rate_low = priv->pkt_rate_low;
101862306a36Sopenharmony_ci	coal->rx_coalesce_usecs_low = priv->rx_usecs_low;
101962306a36Sopenharmony_ci	coal->pkt_rate_high = priv->pkt_rate_high;
102062306a36Sopenharmony_ci	coal->rx_coalesce_usecs_high = priv->rx_usecs_high;
102162306a36Sopenharmony_ci	coal->rate_sample_interval = priv->sample_interval;
102262306a36Sopenharmony_ci	coal->use_adaptive_rx_coalesce = priv->adaptive_rx_coal;
102362306a36Sopenharmony_ci
102462306a36Sopenharmony_ci	return 0;
102562306a36Sopenharmony_ci}
102662306a36Sopenharmony_ci
102762306a36Sopenharmony_cistatic int mlx4_en_set_coalesce(struct net_device *dev,
102862306a36Sopenharmony_ci				struct ethtool_coalesce *coal,
102962306a36Sopenharmony_ci				struct kernel_ethtool_coalesce *kernel_coal,
103062306a36Sopenharmony_ci				struct netlink_ext_ack *extack)
103162306a36Sopenharmony_ci{
103262306a36Sopenharmony_ci	struct mlx4_en_priv *priv = netdev_priv(dev);
103362306a36Sopenharmony_ci
103462306a36Sopenharmony_ci	if (!coal->tx_max_coalesced_frames_irq)
103562306a36Sopenharmony_ci		return -EINVAL;
103662306a36Sopenharmony_ci
103762306a36Sopenharmony_ci	if (coal->tx_coalesce_usecs > MLX4_EN_MAX_COAL_TIME ||
103862306a36Sopenharmony_ci	    coal->rx_coalesce_usecs > MLX4_EN_MAX_COAL_TIME ||
103962306a36Sopenharmony_ci	    coal->rx_coalesce_usecs_low > MLX4_EN_MAX_COAL_TIME ||
104062306a36Sopenharmony_ci	    coal->rx_coalesce_usecs_high > MLX4_EN_MAX_COAL_TIME) {
104162306a36Sopenharmony_ci		netdev_info(dev, "%s: maximum coalesce time supported is %d usecs\n",
104262306a36Sopenharmony_ci			    __func__, MLX4_EN_MAX_COAL_TIME);
104362306a36Sopenharmony_ci		return -ERANGE;
104462306a36Sopenharmony_ci	}
104562306a36Sopenharmony_ci
104662306a36Sopenharmony_ci	if (coal->tx_max_coalesced_frames > MLX4_EN_MAX_COAL_PKTS ||
104762306a36Sopenharmony_ci	    coal->rx_max_coalesced_frames > MLX4_EN_MAX_COAL_PKTS) {
104862306a36Sopenharmony_ci		netdev_info(dev, "%s: maximum coalesced frames supported is %d\n",
104962306a36Sopenharmony_ci			    __func__, MLX4_EN_MAX_COAL_PKTS);
105062306a36Sopenharmony_ci		return -ERANGE;
105162306a36Sopenharmony_ci	}
105262306a36Sopenharmony_ci
105362306a36Sopenharmony_ci	priv->rx_frames = (coal->rx_max_coalesced_frames ==
105462306a36Sopenharmony_ci			   MLX4_EN_AUTO_CONF) ?
105562306a36Sopenharmony_ci				MLX4_EN_RX_COAL_TARGET :
105662306a36Sopenharmony_ci				coal->rx_max_coalesced_frames;
105762306a36Sopenharmony_ci	priv->rx_usecs = (coal->rx_coalesce_usecs ==
105862306a36Sopenharmony_ci			  MLX4_EN_AUTO_CONF) ?
105962306a36Sopenharmony_ci				MLX4_EN_RX_COAL_TIME :
106062306a36Sopenharmony_ci				coal->rx_coalesce_usecs;
106162306a36Sopenharmony_ci
106262306a36Sopenharmony_ci	/* Setting TX coalescing parameters */
106362306a36Sopenharmony_ci	if (coal->tx_coalesce_usecs != priv->tx_usecs ||
106462306a36Sopenharmony_ci	    coal->tx_max_coalesced_frames != priv->tx_frames) {
106562306a36Sopenharmony_ci		priv->tx_usecs = coal->tx_coalesce_usecs;
106662306a36Sopenharmony_ci		priv->tx_frames = coal->tx_max_coalesced_frames;
106762306a36Sopenharmony_ci	}
106862306a36Sopenharmony_ci
106962306a36Sopenharmony_ci	/* Set adaptive coalescing params */
107062306a36Sopenharmony_ci	priv->pkt_rate_low = coal->pkt_rate_low;
107162306a36Sopenharmony_ci	priv->rx_usecs_low = coal->rx_coalesce_usecs_low;
107262306a36Sopenharmony_ci	priv->pkt_rate_high = coal->pkt_rate_high;
107362306a36Sopenharmony_ci	priv->rx_usecs_high = coal->rx_coalesce_usecs_high;
107462306a36Sopenharmony_ci	priv->sample_interval = coal->rate_sample_interval;
107562306a36Sopenharmony_ci	priv->adaptive_rx_coal = coal->use_adaptive_rx_coalesce;
107662306a36Sopenharmony_ci	priv->tx_work_limit = coal->tx_max_coalesced_frames_irq;
107762306a36Sopenharmony_ci
107862306a36Sopenharmony_ci	return mlx4_en_moderation_update(priv);
107962306a36Sopenharmony_ci}
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_cistatic int mlx4_en_set_pauseparam(struct net_device *dev,
108262306a36Sopenharmony_ci				struct ethtool_pauseparam *pause)
108362306a36Sopenharmony_ci{
108462306a36Sopenharmony_ci	struct mlx4_en_priv *priv = netdev_priv(dev);
108562306a36Sopenharmony_ci	struct mlx4_en_dev *mdev = priv->mdev;
108662306a36Sopenharmony_ci	u8 tx_pause, tx_ppp, rx_pause, rx_ppp;
108762306a36Sopenharmony_ci	int err;
108862306a36Sopenharmony_ci
108962306a36Sopenharmony_ci	if (pause->autoneg)
109062306a36Sopenharmony_ci		return -EINVAL;
109162306a36Sopenharmony_ci
109262306a36Sopenharmony_ci	tx_pause = !!(pause->tx_pause);
109362306a36Sopenharmony_ci	rx_pause = !!(pause->rx_pause);
109462306a36Sopenharmony_ci	rx_ppp = (tx_pause || rx_pause) ? 0 : priv->prof->rx_ppp;
109562306a36Sopenharmony_ci	tx_ppp = (tx_pause || rx_pause) ? 0 : priv->prof->tx_ppp;
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_ci	err = mlx4_SET_PORT_general(mdev->dev, priv->port,
109862306a36Sopenharmony_ci				    priv->rx_skb_size + ETH_FCS_LEN,
109962306a36Sopenharmony_ci				    tx_pause, tx_ppp, rx_pause, rx_ppp);
110062306a36Sopenharmony_ci	if (err) {
110162306a36Sopenharmony_ci		en_err(priv, "Failed setting pause params, err = %d\n", err);
110262306a36Sopenharmony_ci		return err;
110362306a36Sopenharmony_ci	}
110462306a36Sopenharmony_ci
110562306a36Sopenharmony_ci	mlx4_en_update_pfc_stats_bitmap(mdev->dev, &priv->stats_bitmap,
110662306a36Sopenharmony_ci					rx_ppp, rx_pause, tx_ppp, tx_pause);
110762306a36Sopenharmony_ci
110862306a36Sopenharmony_ci	priv->prof->tx_pause = tx_pause;
110962306a36Sopenharmony_ci	priv->prof->rx_pause = rx_pause;
111062306a36Sopenharmony_ci	priv->prof->tx_ppp = tx_ppp;
111162306a36Sopenharmony_ci	priv->prof->rx_ppp = rx_ppp;
111262306a36Sopenharmony_ci
111362306a36Sopenharmony_ci	return err;
111462306a36Sopenharmony_ci}
111562306a36Sopenharmony_ci
111662306a36Sopenharmony_cistatic void mlx4_en_get_pause_stats(struct net_device *dev,
111762306a36Sopenharmony_ci				    struct ethtool_pause_stats *stats)
111862306a36Sopenharmony_ci{
111962306a36Sopenharmony_ci	struct mlx4_en_priv *priv = netdev_priv(dev);
112062306a36Sopenharmony_ci	struct bitmap_iterator it;
112162306a36Sopenharmony_ci
112262306a36Sopenharmony_ci	bitmap_iterator_init(&it, priv->stats_bitmap.bitmap, NUM_ALL_STATS);
112362306a36Sopenharmony_ci
112462306a36Sopenharmony_ci	spin_lock_bh(&priv->stats_lock);
112562306a36Sopenharmony_ci	if (test_bit(FLOW_PRIORITY_STATS_IDX_TX_FRAMES,
112662306a36Sopenharmony_ci		     priv->stats_bitmap.bitmap))
112762306a36Sopenharmony_ci		stats->tx_pause_frames = priv->tx_flowstats.tx_pause;
112862306a36Sopenharmony_ci	if (test_bit(FLOW_PRIORITY_STATS_IDX_RX_FRAMES,
112962306a36Sopenharmony_ci		     priv->stats_bitmap.bitmap))
113062306a36Sopenharmony_ci		stats->rx_pause_frames = priv->rx_flowstats.rx_pause;
113162306a36Sopenharmony_ci	spin_unlock_bh(&priv->stats_lock);
113262306a36Sopenharmony_ci}
113362306a36Sopenharmony_ci
113462306a36Sopenharmony_cistatic void mlx4_en_get_pauseparam(struct net_device *dev,
113562306a36Sopenharmony_ci				 struct ethtool_pauseparam *pause)
113662306a36Sopenharmony_ci{
113762306a36Sopenharmony_ci	struct mlx4_en_priv *priv = netdev_priv(dev);
113862306a36Sopenharmony_ci
113962306a36Sopenharmony_ci	pause->tx_pause = priv->prof->tx_pause;
114062306a36Sopenharmony_ci	pause->rx_pause = priv->prof->rx_pause;
114162306a36Sopenharmony_ci}
114262306a36Sopenharmony_ci
114362306a36Sopenharmony_cistatic int mlx4_en_set_ringparam(struct net_device *dev,
114462306a36Sopenharmony_ci				 struct ethtool_ringparam *param,
114562306a36Sopenharmony_ci				 struct kernel_ethtool_ringparam *kernel_param,
114662306a36Sopenharmony_ci				 struct netlink_ext_ack *extack)
114762306a36Sopenharmony_ci{
114862306a36Sopenharmony_ci	struct mlx4_en_priv *priv = netdev_priv(dev);
114962306a36Sopenharmony_ci	struct mlx4_en_dev *mdev = priv->mdev;
115062306a36Sopenharmony_ci	struct mlx4_en_port_profile new_prof;
115162306a36Sopenharmony_ci	struct mlx4_en_priv *tmp;
115262306a36Sopenharmony_ci	u32 rx_size, tx_size;
115362306a36Sopenharmony_ci	int port_up = 0;
115462306a36Sopenharmony_ci	int err = 0;
115562306a36Sopenharmony_ci
115662306a36Sopenharmony_ci	if (param->rx_jumbo_pending || param->rx_mini_pending)
115762306a36Sopenharmony_ci		return -EINVAL;
115862306a36Sopenharmony_ci
115962306a36Sopenharmony_ci	if (param->rx_pending < MLX4_EN_MIN_RX_SIZE) {
116062306a36Sopenharmony_ci		en_warn(priv, "%s: rx_pending (%d) < min (%d)\n",
116162306a36Sopenharmony_ci			__func__, param->rx_pending,
116262306a36Sopenharmony_ci			MLX4_EN_MIN_RX_SIZE);
116362306a36Sopenharmony_ci		return -EINVAL;
116462306a36Sopenharmony_ci	}
116562306a36Sopenharmony_ci	if (param->tx_pending < MLX4_EN_MIN_TX_SIZE) {
116662306a36Sopenharmony_ci		en_warn(priv, "%s: tx_pending (%d) < min (%lu)\n",
116762306a36Sopenharmony_ci			__func__, param->tx_pending,
116862306a36Sopenharmony_ci			MLX4_EN_MIN_TX_SIZE);
116962306a36Sopenharmony_ci		return -EINVAL;
117062306a36Sopenharmony_ci	}
117162306a36Sopenharmony_ci
117262306a36Sopenharmony_ci	rx_size = roundup_pow_of_two(param->rx_pending);
117362306a36Sopenharmony_ci	tx_size = roundup_pow_of_two(param->tx_pending);
117462306a36Sopenharmony_ci
117562306a36Sopenharmony_ci	if (rx_size == (priv->port_up ? priv->rx_ring[0]->actual_size :
117662306a36Sopenharmony_ci					priv->rx_ring[0]->size) &&
117762306a36Sopenharmony_ci	    tx_size == priv->tx_ring[TX][0]->size)
117862306a36Sopenharmony_ci		return 0;
117962306a36Sopenharmony_ci
118062306a36Sopenharmony_ci	tmp = kzalloc(sizeof(*tmp), GFP_KERNEL);
118162306a36Sopenharmony_ci	if (!tmp)
118262306a36Sopenharmony_ci		return -ENOMEM;
118362306a36Sopenharmony_ci
118462306a36Sopenharmony_ci	mutex_lock(&mdev->state_lock);
118562306a36Sopenharmony_ci	memcpy(&new_prof, priv->prof, sizeof(struct mlx4_en_port_profile));
118662306a36Sopenharmony_ci	new_prof.tx_ring_size = tx_size;
118762306a36Sopenharmony_ci	new_prof.rx_ring_size = rx_size;
118862306a36Sopenharmony_ci	err = mlx4_en_try_alloc_resources(priv, tmp, &new_prof, true);
118962306a36Sopenharmony_ci	if (err)
119062306a36Sopenharmony_ci		goto out;
119162306a36Sopenharmony_ci
119262306a36Sopenharmony_ci	if (priv->port_up) {
119362306a36Sopenharmony_ci		port_up = 1;
119462306a36Sopenharmony_ci		mlx4_en_stop_port(dev, 1);
119562306a36Sopenharmony_ci	}
119662306a36Sopenharmony_ci
119762306a36Sopenharmony_ci	mlx4_en_safe_replace_resources(priv, tmp);
119862306a36Sopenharmony_ci
119962306a36Sopenharmony_ci	if (port_up) {
120062306a36Sopenharmony_ci		err = mlx4_en_start_port(dev);
120162306a36Sopenharmony_ci		if (err)
120262306a36Sopenharmony_ci			en_err(priv, "Failed starting port\n");
120362306a36Sopenharmony_ci	}
120462306a36Sopenharmony_ci
120562306a36Sopenharmony_ci	err = mlx4_en_moderation_update(priv);
120662306a36Sopenharmony_ciout:
120762306a36Sopenharmony_ci	kfree(tmp);
120862306a36Sopenharmony_ci	mutex_unlock(&mdev->state_lock);
120962306a36Sopenharmony_ci	return err;
121062306a36Sopenharmony_ci}
121162306a36Sopenharmony_ci
121262306a36Sopenharmony_cistatic void mlx4_en_get_ringparam(struct net_device *dev,
121362306a36Sopenharmony_ci				  struct ethtool_ringparam *param,
121462306a36Sopenharmony_ci				  struct kernel_ethtool_ringparam *kernel_param,
121562306a36Sopenharmony_ci				  struct netlink_ext_ack *extack)
121662306a36Sopenharmony_ci{
121762306a36Sopenharmony_ci	struct mlx4_en_priv *priv = netdev_priv(dev);
121862306a36Sopenharmony_ci
121962306a36Sopenharmony_ci	memset(param, 0, sizeof(*param));
122062306a36Sopenharmony_ci	param->rx_max_pending = MLX4_EN_MAX_RX_SIZE;
122162306a36Sopenharmony_ci	param->tx_max_pending = MLX4_EN_MAX_TX_SIZE;
122262306a36Sopenharmony_ci	param->rx_pending = priv->port_up ?
122362306a36Sopenharmony_ci		priv->rx_ring[0]->actual_size : priv->rx_ring[0]->size;
122462306a36Sopenharmony_ci	param->tx_pending = priv->tx_ring[TX][0]->size;
122562306a36Sopenharmony_ci}
122662306a36Sopenharmony_ci
122762306a36Sopenharmony_cistatic u32 mlx4_en_get_rxfh_indir_size(struct net_device *dev)
122862306a36Sopenharmony_ci{
122962306a36Sopenharmony_ci	struct mlx4_en_priv *priv = netdev_priv(dev);
123062306a36Sopenharmony_ci
123162306a36Sopenharmony_ci	return rounddown_pow_of_two(priv->rx_ring_num);
123262306a36Sopenharmony_ci}
123362306a36Sopenharmony_ci
123462306a36Sopenharmony_cistatic u32 mlx4_en_get_rxfh_key_size(struct net_device *netdev)
123562306a36Sopenharmony_ci{
123662306a36Sopenharmony_ci	return MLX4_EN_RSS_KEY_SIZE;
123762306a36Sopenharmony_ci}
123862306a36Sopenharmony_ci
123962306a36Sopenharmony_cistatic int mlx4_en_check_rxfh_func(struct net_device *dev, u8 hfunc)
124062306a36Sopenharmony_ci{
124162306a36Sopenharmony_ci	struct mlx4_en_priv *priv = netdev_priv(dev);
124262306a36Sopenharmony_ci
124362306a36Sopenharmony_ci	/* check if requested function is supported by the device */
124462306a36Sopenharmony_ci	if (hfunc == ETH_RSS_HASH_TOP) {
124562306a36Sopenharmony_ci		if (!(priv->mdev->dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_RSS_TOP))
124662306a36Sopenharmony_ci			return -EINVAL;
124762306a36Sopenharmony_ci		if (!(dev->features & NETIF_F_RXHASH))
124862306a36Sopenharmony_ci			en_warn(priv, "Toeplitz hash function should be used in conjunction with RX hashing for optimal performance\n");
124962306a36Sopenharmony_ci		return 0;
125062306a36Sopenharmony_ci	} else if (hfunc == ETH_RSS_HASH_XOR) {
125162306a36Sopenharmony_ci		if (!(priv->mdev->dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_RSS_XOR))
125262306a36Sopenharmony_ci			return -EINVAL;
125362306a36Sopenharmony_ci		if (dev->features & NETIF_F_RXHASH)
125462306a36Sopenharmony_ci			en_warn(priv, "Enabling both XOR Hash function and RX Hashing can limit RPS functionality\n");
125562306a36Sopenharmony_ci		return 0;
125662306a36Sopenharmony_ci	}
125762306a36Sopenharmony_ci
125862306a36Sopenharmony_ci	return -EINVAL;
125962306a36Sopenharmony_ci}
126062306a36Sopenharmony_ci
126162306a36Sopenharmony_cistatic int mlx4_en_get_rxfh(struct net_device *dev, u32 *ring_index, u8 *key,
126262306a36Sopenharmony_ci			    u8 *hfunc)
126362306a36Sopenharmony_ci{
126462306a36Sopenharmony_ci	struct mlx4_en_priv *priv = netdev_priv(dev);
126562306a36Sopenharmony_ci	u32 n = mlx4_en_get_rxfh_indir_size(dev);
126662306a36Sopenharmony_ci	u32 i, rss_rings;
126762306a36Sopenharmony_ci
126862306a36Sopenharmony_ci	rss_rings = priv->prof->rss_rings ?: n;
126962306a36Sopenharmony_ci	rss_rings = rounddown_pow_of_two(rss_rings);
127062306a36Sopenharmony_ci
127162306a36Sopenharmony_ci	for (i = 0; i < n; i++) {
127262306a36Sopenharmony_ci		if (!ring_index)
127362306a36Sopenharmony_ci			break;
127462306a36Sopenharmony_ci		ring_index[i] = i % rss_rings;
127562306a36Sopenharmony_ci	}
127662306a36Sopenharmony_ci	if (key)
127762306a36Sopenharmony_ci		memcpy(key, priv->rss_key, MLX4_EN_RSS_KEY_SIZE);
127862306a36Sopenharmony_ci	if (hfunc)
127962306a36Sopenharmony_ci		*hfunc = priv->rss_hash_fn;
128062306a36Sopenharmony_ci	return 0;
128162306a36Sopenharmony_ci}
128262306a36Sopenharmony_ci
128362306a36Sopenharmony_cistatic int mlx4_en_set_rxfh(struct net_device *dev, const u32 *ring_index,
128462306a36Sopenharmony_ci			    const u8 *key, const u8 hfunc)
128562306a36Sopenharmony_ci{
128662306a36Sopenharmony_ci	struct mlx4_en_priv *priv = netdev_priv(dev);
128762306a36Sopenharmony_ci	u32 n = mlx4_en_get_rxfh_indir_size(dev);
128862306a36Sopenharmony_ci	struct mlx4_en_dev *mdev = priv->mdev;
128962306a36Sopenharmony_ci	int port_up = 0;
129062306a36Sopenharmony_ci	int err = 0;
129162306a36Sopenharmony_ci	int i;
129262306a36Sopenharmony_ci	int rss_rings = 0;
129362306a36Sopenharmony_ci
129462306a36Sopenharmony_ci	/* Calculate RSS table size and make sure flows are spread evenly
129562306a36Sopenharmony_ci	 * between rings
129662306a36Sopenharmony_ci	 */
129762306a36Sopenharmony_ci	for (i = 0; i < n; i++) {
129862306a36Sopenharmony_ci		if (!ring_index)
129962306a36Sopenharmony_ci			break;
130062306a36Sopenharmony_ci		if (i > 0 && !ring_index[i] && !rss_rings)
130162306a36Sopenharmony_ci			rss_rings = i;
130262306a36Sopenharmony_ci
130362306a36Sopenharmony_ci		if (ring_index[i] != (i % (rss_rings ?: n)))
130462306a36Sopenharmony_ci			return -EINVAL;
130562306a36Sopenharmony_ci	}
130662306a36Sopenharmony_ci
130762306a36Sopenharmony_ci	if (!rss_rings)
130862306a36Sopenharmony_ci		rss_rings = n;
130962306a36Sopenharmony_ci
131062306a36Sopenharmony_ci	/* RSS table size must be an order of 2 */
131162306a36Sopenharmony_ci	if (!is_power_of_2(rss_rings))
131262306a36Sopenharmony_ci		return -EINVAL;
131362306a36Sopenharmony_ci
131462306a36Sopenharmony_ci	if (hfunc != ETH_RSS_HASH_NO_CHANGE) {
131562306a36Sopenharmony_ci		err = mlx4_en_check_rxfh_func(dev, hfunc);
131662306a36Sopenharmony_ci		if (err)
131762306a36Sopenharmony_ci			return err;
131862306a36Sopenharmony_ci	}
131962306a36Sopenharmony_ci
132062306a36Sopenharmony_ci	mutex_lock(&mdev->state_lock);
132162306a36Sopenharmony_ci	if (priv->port_up) {
132262306a36Sopenharmony_ci		port_up = 1;
132362306a36Sopenharmony_ci		mlx4_en_stop_port(dev, 1);
132462306a36Sopenharmony_ci	}
132562306a36Sopenharmony_ci
132662306a36Sopenharmony_ci	if (ring_index)
132762306a36Sopenharmony_ci		priv->prof->rss_rings = rss_rings;
132862306a36Sopenharmony_ci	if (key)
132962306a36Sopenharmony_ci		memcpy(priv->rss_key, key, MLX4_EN_RSS_KEY_SIZE);
133062306a36Sopenharmony_ci	if (hfunc !=  ETH_RSS_HASH_NO_CHANGE)
133162306a36Sopenharmony_ci		priv->rss_hash_fn = hfunc;
133262306a36Sopenharmony_ci
133362306a36Sopenharmony_ci	if (port_up) {
133462306a36Sopenharmony_ci		err = mlx4_en_start_port(dev);
133562306a36Sopenharmony_ci		if (err)
133662306a36Sopenharmony_ci			en_err(priv, "Failed starting port\n");
133762306a36Sopenharmony_ci	}
133862306a36Sopenharmony_ci
133962306a36Sopenharmony_ci	mutex_unlock(&mdev->state_lock);
134062306a36Sopenharmony_ci	return err;
134162306a36Sopenharmony_ci}
134262306a36Sopenharmony_ci
134362306a36Sopenharmony_ci#define all_zeros_or_all_ones(field)		\
134462306a36Sopenharmony_ci	((field) == 0 || (field) == (__force typeof(field))-1)
134562306a36Sopenharmony_ci
134662306a36Sopenharmony_cistatic int mlx4_en_validate_flow(struct net_device *dev,
134762306a36Sopenharmony_ci				 struct ethtool_rxnfc *cmd)
134862306a36Sopenharmony_ci{
134962306a36Sopenharmony_ci	struct ethtool_usrip4_spec *l3_mask;
135062306a36Sopenharmony_ci	struct ethtool_tcpip4_spec *l4_mask;
135162306a36Sopenharmony_ci	struct ethhdr *eth_mask;
135262306a36Sopenharmony_ci
135362306a36Sopenharmony_ci	if (cmd->fs.location >= MAX_NUM_OF_FS_RULES)
135462306a36Sopenharmony_ci		return -EINVAL;
135562306a36Sopenharmony_ci
135662306a36Sopenharmony_ci	if (cmd->fs.flow_type & FLOW_MAC_EXT) {
135762306a36Sopenharmony_ci		/* dest mac mask must be ff:ff:ff:ff:ff:ff */
135862306a36Sopenharmony_ci		if (!is_broadcast_ether_addr(cmd->fs.m_ext.h_dest))
135962306a36Sopenharmony_ci			return -EINVAL;
136062306a36Sopenharmony_ci	}
136162306a36Sopenharmony_ci
136262306a36Sopenharmony_ci	switch (cmd->fs.flow_type & ~(FLOW_EXT | FLOW_MAC_EXT)) {
136362306a36Sopenharmony_ci	case TCP_V4_FLOW:
136462306a36Sopenharmony_ci	case UDP_V4_FLOW:
136562306a36Sopenharmony_ci		if (cmd->fs.m_u.tcp_ip4_spec.tos)
136662306a36Sopenharmony_ci			return -EINVAL;
136762306a36Sopenharmony_ci		l4_mask = &cmd->fs.m_u.tcp_ip4_spec;
136862306a36Sopenharmony_ci		/* don't allow mask which isn't all 0 or 1 */
136962306a36Sopenharmony_ci		if (!all_zeros_or_all_ones(l4_mask->ip4src) ||
137062306a36Sopenharmony_ci		    !all_zeros_or_all_ones(l4_mask->ip4dst) ||
137162306a36Sopenharmony_ci		    !all_zeros_or_all_ones(l4_mask->psrc) ||
137262306a36Sopenharmony_ci		    !all_zeros_or_all_ones(l4_mask->pdst))
137362306a36Sopenharmony_ci			return -EINVAL;
137462306a36Sopenharmony_ci		break;
137562306a36Sopenharmony_ci	case IP_USER_FLOW:
137662306a36Sopenharmony_ci		l3_mask = &cmd->fs.m_u.usr_ip4_spec;
137762306a36Sopenharmony_ci		if (l3_mask->l4_4_bytes || l3_mask->tos || l3_mask->proto ||
137862306a36Sopenharmony_ci		    cmd->fs.h_u.usr_ip4_spec.ip_ver != ETH_RX_NFC_IP4 ||
137962306a36Sopenharmony_ci		    (!l3_mask->ip4src && !l3_mask->ip4dst) ||
138062306a36Sopenharmony_ci		    !all_zeros_or_all_ones(l3_mask->ip4src) ||
138162306a36Sopenharmony_ci		    !all_zeros_or_all_ones(l3_mask->ip4dst))
138262306a36Sopenharmony_ci			return -EINVAL;
138362306a36Sopenharmony_ci		break;
138462306a36Sopenharmony_ci	case ETHER_FLOW:
138562306a36Sopenharmony_ci		eth_mask = &cmd->fs.m_u.ether_spec;
138662306a36Sopenharmony_ci		/* source mac mask must not be set */
138762306a36Sopenharmony_ci		if (!is_zero_ether_addr(eth_mask->h_source))
138862306a36Sopenharmony_ci			return -EINVAL;
138962306a36Sopenharmony_ci
139062306a36Sopenharmony_ci		/* dest mac mask must be ff:ff:ff:ff:ff:ff */
139162306a36Sopenharmony_ci		if (!is_broadcast_ether_addr(eth_mask->h_dest))
139262306a36Sopenharmony_ci			return -EINVAL;
139362306a36Sopenharmony_ci
139462306a36Sopenharmony_ci		if (!all_zeros_or_all_ones(eth_mask->h_proto))
139562306a36Sopenharmony_ci			return -EINVAL;
139662306a36Sopenharmony_ci		break;
139762306a36Sopenharmony_ci	default:
139862306a36Sopenharmony_ci		return -EINVAL;
139962306a36Sopenharmony_ci	}
140062306a36Sopenharmony_ci
140162306a36Sopenharmony_ci	if ((cmd->fs.flow_type & FLOW_EXT)) {
140262306a36Sopenharmony_ci		if (cmd->fs.m_ext.vlan_etype ||
140362306a36Sopenharmony_ci		    !((cmd->fs.m_ext.vlan_tci & cpu_to_be16(VLAN_VID_MASK)) ==
140462306a36Sopenharmony_ci		      0 ||
140562306a36Sopenharmony_ci		      (cmd->fs.m_ext.vlan_tci & cpu_to_be16(VLAN_VID_MASK)) ==
140662306a36Sopenharmony_ci		      cpu_to_be16(VLAN_VID_MASK)))
140762306a36Sopenharmony_ci			return -EINVAL;
140862306a36Sopenharmony_ci
140962306a36Sopenharmony_ci		if (cmd->fs.m_ext.vlan_tci) {
141062306a36Sopenharmony_ci			if (be16_to_cpu(cmd->fs.h_ext.vlan_tci) >= VLAN_N_VID)
141162306a36Sopenharmony_ci				return -EINVAL;
141262306a36Sopenharmony_ci
141362306a36Sopenharmony_ci		}
141462306a36Sopenharmony_ci	}
141562306a36Sopenharmony_ci
141662306a36Sopenharmony_ci	return 0;
141762306a36Sopenharmony_ci}
141862306a36Sopenharmony_ci
141962306a36Sopenharmony_cistatic int mlx4_en_ethtool_add_mac_rule(struct ethtool_rxnfc *cmd,
142062306a36Sopenharmony_ci					struct list_head *rule_list_h,
142162306a36Sopenharmony_ci					struct mlx4_spec_list *spec_l2,
142262306a36Sopenharmony_ci					unsigned char *mac)
142362306a36Sopenharmony_ci{
142462306a36Sopenharmony_ci	__be64 mac_msk = cpu_to_be64(MLX4_MAC_MASK << 16);
142562306a36Sopenharmony_ci
142662306a36Sopenharmony_ci	spec_l2->id = MLX4_NET_TRANS_RULE_ID_ETH;
142762306a36Sopenharmony_ci	memcpy(spec_l2->eth.dst_mac_msk, &mac_msk, ETH_ALEN);
142862306a36Sopenharmony_ci	memcpy(spec_l2->eth.dst_mac, mac, ETH_ALEN);
142962306a36Sopenharmony_ci
143062306a36Sopenharmony_ci	if ((cmd->fs.flow_type & FLOW_EXT) &&
143162306a36Sopenharmony_ci	    (cmd->fs.m_ext.vlan_tci & cpu_to_be16(VLAN_VID_MASK))) {
143262306a36Sopenharmony_ci		spec_l2->eth.vlan_id = cmd->fs.h_ext.vlan_tci;
143362306a36Sopenharmony_ci		spec_l2->eth.vlan_id_msk = cpu_to_be16(VLAN_VID_MASK);
143462306a36Sopenharmony_ci	}
143562306a36Sopenharmony_ci
143662306a36Sopenharmony_ci	list_add_tail(&spec_l2->list, rule_list_h);
143762306a36Sopenharmony_ci
143862306a36Sopenharmony_ci	return 0;
143962306a36Sopenharmony_ci}
144062306a36Sopenharmony_ci
144162306a36Sopenharmony_cistatic int mlx4_en_ethtool_add_mac_rule_by_ipv4(struct mlx4_en_priv *priv,
144262306a36Sopenharmony_ci						struct ethtool_rxnfc *cmd,
144362306a36Sopenharmony_ci						struct list_head *rule_list_h,
144462306a36Sopenharmony_ci						struct mlx4_spec_list *spec_l2,
144562306a36Sopenharmony_ci						__be32 ipv4_dst)
144662306a36Sopenharmony_ci{
144762306a36Sopenharmony_ci#ifdef CONFIG_INET
144862306a36Sopenharmony_ci	unsigned char mac[ETH_ALEN];
144962306a36Sopenharmony_ci
145062306a36Sopenharmony_ci	if (!ipv4_is_multicast(ipv4_dst)) {
145162306a36Sopenharmony_ci		if (cmd->fs.flow_type & FLOW_MAC_EXT)
145262306a36Sopenharmony_ci			memcpy(&mac, cmd->fs.h_ext.h_dest, ETH_ALEN);
145362306a36Sopenharmony_ci		else
145462306a36Sopenharmony_ci			memcpy(&mac, priv->dev->dev_addr, ETH_ALEN);
145562306a36Sopenharmony_ci	} else {
145662306a36Sopenharmony_ci		ip_eth_mc_map(ipv4_dst, mac);
145762306a36Sopenharmony_ci	}
145862306a36Sopenharmony_ci
145962306a36Sopenharmony_ci	return mlx4_en_ethtool_add_mac_rule(cmd, rule_list_h, spec_l2, &mac[0]);
146062306a36Sopenharmony_ci#else
146162306a36Sopenharmony_ci	return -EINVAL;
146262306a36Sopenharmony_ci#endif
146362306a36Sopenharmony_ci}
146462306a36Sopenharmony_ci
146562306a36Sopenharmony_cistatic int add_ip_rule(struct mlx4_en_priv *priv,
146662306a36Sopenharmony_ci		       struct ethtool_rxnfc *cmd,
146762306a36Sopenharmony_ci		       struct list_head *list_h)
146862306a36Sopenharmony_ci{
146962306a36Sopenharmony_ci	int err;
147062306a36Sopenharmony_ci	struct mlx4_spec_list *spec_l2;
147162306a36Sopenharmony_ci	struct mlx4_spec_list *spec_l3;
147262306a36Sopenharmony_ci	struct ethtool_usrip4_spec *l3_mask = &cmd->fs.m_u.usr_ip4_spec;
147362306a36Sopenharmony_ci
147462306a36Sopenharmony_ci	spec_l3 = kzalloc(sizeof(*spec_l3), GFP_KERNEL);
147562306a36Sopenharmony_ci	spec_l2 = kzalloc(sizeof(*spec_l2), GFP_KERNEL);
147662306a36Sopenharmony_ci	if (!spec_l2 || !spec_l3) {
147762306a36Sopenharmony_ci		err = -ENOMEM;
147862306a36Sopenharmony_ci		goto free_spec;
147962306a36Sopenharmony_ci	}
148062306a36Sopenharmony_ci
148162306a36Sopenharmony_ci	err = mlx4_en_ethtool_add_mac_rule_by_ipv4(priv, cmd, list_h, spec_l2,
148262306a36Sopenharmony_ci						   cmd->fs.h_u.
148362306a36Sopenharmony_ci						   usr_ip4_spec.ip4dst);
148462306a36Sopenharmony_ci	if (err)
148562306a36Sopenharmony_ci		goto free_spec;
148662306a36Sopenharmony_ci	spec_l3->id = MLX4_NET_TRANS_RULE_ID_IPV4;
148762306a36Sopenharmony_ci	spec_l3->ipv4.src_ip = cmd->fs.h_u.usr_ip4_spec.ip4src;
148862306a36Sopenharmony_ci	if (l3_mask->ip4src)
148962306a36Sopenharmony_ci		spec_l3->ipv4.src_ip_msk = EN_ETHTOOL_WORD_MASK;
149062306a36Sopenharmony_ci	spec_l3->ipv4.dst_ip = cmd->fs.h_u.usr_ip4_spec.ip4dst;
149162306a36Sopenharmony_ci	if (l3_mask->ip4dst)
149262306a36Sopenharmony_ci		spec_l3->ipv4.dst_ip_msk = EN_ETHTOOL_WORD_MASK;
149362306a36Sopenharmony_ci	list_add_tail(&spec_l3->list, list_h);
149462306a36Sopenharmony_ci
149562306a36Sopenharmony_ci	return 0;
149662306a36Sopenharmony_ci
149762306a36Sopenharmony_cifree_spec:
149862306a36Sopenharmony_ci	kfree(spec_l2);
149962306a36Sopenharmony_ci	kfree(spec_l3);
150062306a36Sopenharmony_ci	return err;
150162306a36Sopenharmony_ci}
150262306a36Sopenharmony_ci
150362306a36Sopenharmony_cistatic int add_tcp_udp_rule(struct mlx4_en_priv *priv,
150462306a36Sopenharmony_ci			     struct ethtool_rxnfc *cmd,
150562306a36Sopenharmony_ci			     struct list_head *list_h, int proto)
150662306a36Sopenharmony_ci{
150762306a36Sopenharmony_ci	int err;
150862306a36Sopenharmony_ci	struct mlx4_spec_list *spec_l2;
150962306a36Sopenharmony_ci	struct mlx4_spec_list *spec_l3;
151062306a36Sopenharmony_ci	struct mlx4_spec_list *spec_l4;
151162306a36Sopenharmony_ci	struct ethtool_tcpip4_spec *l4_mask = &cmd->fs.m_u.tcp_ip4_spec;
151262306a36Sopenharmony_ci
151362306a36Sopenharmony_ci	spec_l2 = kzalloc(sizeof(*spec_l2), GFP_KERNEL);
151462306a36Sopenharmony_ci	spec_l3 = kzalloc(sizeof(*spec_l3), GFP_KERNEL);
151562306a36Sopenharmony_ci	spec_l4 = kzalloc(sizeof(*spec_l4), GFP_KERNEL);
151662306a36Sopenharmony_ci	if (!spec_l2 || !spec_l3 || !spec_l4) {
151762306a36Sopenharmony_ci		err = -ENOMEM;
151862306a36Sopenharmony_ci		goto free_spec;
151962306a36Sopenharmony_ci	}
152062306a36Sopenharmony_ci
152162306a36Sopenharmony_ci	spec_l3->id = MLX4_NET_TRANS_RULE_ID_IPV4;
152262306a36Sopenharmony_ci
152362306a36Sopenharmony_ci	if (proto == TCP_V4_FLOW) {
152462306a36Sopenharmony_ci		err = mlx4_en_ethtool_add_mac_rule_by_ipv4(priv, cmd, list_h,
152562306a36Sopenharmony_ci							   spec_l2,
152662306a36Sopenharmony_ci							   cmd->fs.h_u.
152762306a36Sopenharmony_ci							   tcp_ip4_spec.ip4dst);
152862306a36Sopenharmony_ci		if (err)
152962306a36Sopenharmony_ci			goto free_spec;
153062306a36Sopenharmony_ci		spec_l4->id = MLX4_NET_TRANS_RULE_ID_TCP;
153162306a36Sopenharmony_ci		spec_l3->ipv4.src_ip = cmd->fs.h_u.tcp_ip4_spec.ip4src;
153262306a36Sopenharmony_ci		spec_l3->ipv4.dst_ip = cmd->fs.h_u.tcp_ip4_spec.ip4dst;
153362306a36Sopenharmony_ci		spec_l4->tcp_udp.src_port = cmd->fs.h_u.tcp_ip4_spec.psrc;
153462306a36Sopenharmony_ci		spec_l4->tcp_udp.dst_port = cmd->fs.h_u.tcp_ip4_spec.pdst;
153562306a36Sopenharmony_ci	} else {
153662306a36Sopenharmony_ci		err = mlx4_en_ethtool_add_mac_rule_by_ipv4(priv, cmd, list_h,
153762306a36Sopenharmony_ci							   spec_l2,
153862306a36Sopenharmony_ci							   cmd->fs.h_u.
153962306a36Sopenharmony_ci							   udp_ip4_spec.ip4dst);
154062306a36Sopenharmony_ci		if (err)
154162306a36Sopenharmony_ci			goto free_spec;
154262306a36Sopenharmony_ci		spec_l4->id = MLX4_NET_TRANS_RULE_ID_UDP;
154362306a36Sopenharmony_ci		spec_l3->ipv4.src_ip = cmd->fs.h_u.udp_ip4_spec.ip4src;
154462306a36Sopenharmony_ci		spec_l3->ipv4.dst_ip = cmd->fs.h_u.udp_ip4_spec.ip4dst;
154562306a36Sopenharmony_ci		spec_l4->tcp_udp.src_port = cmd->fs.h_u.udp_ip4_spec.psrc;
154662306a36Sopenharmony_ci		spec_l4->tcp_udp.dst_port = cmd->fs.h_u.udp_ip4_spec.pdst;
154762306a36Sopenharmony_ci	}
154862306a36Sopenharmony_ci
154962306a36Sopenharmony_ci	if (l4_mask->ip4src)
155062306a36Sopenharmony_ci		spec_l3->ipv4.src_ip_msk = EN_ETHTOOL_WORD_MASK;
155162306a36Sopenharmony_ci	if (l4_mask->ip4dst)
155262306a36Sopenharmony_ci		spec_l3->ipv4.dst_ip_msk = EN_ETHTOOL_WORD_MASK;
155362306a36Sopenharmony_ci
155462306a36Sopenharmony_ci	if (l4_mask->psrc)
155562306a36Sopenharmony_ci		spec_l4->tcp_udp.src_port_msk = EN_ETHTOOL_SHORT_MASK;
155662306a36Sopenharmony_ci	if (l4_mask->pdst)
155762306a36Sopenharmony_ci		spec_l4->tcp_udp.dst_port_msk = EN_ETHTOOL_SHORT_MASK;
155862306a36Sopenharmony_ci
155962306a36Sopenharmony_ci	list_add_tail(&spec_l3->list, list_h);
156062306a36Sopenharmony_ci	list_add_tail(&spec_l4->list, list_h);
156162306a36Sopenharmony_ci
156262306a36Sopenharmony_ci	return 0;
156362306a36Sopenharmony_ci
156462306a36Sopenharmony_cifree_spec:
156562306a36Sopenharmony_ci	kfree(spec_l2);
156662306a36Sopenharmony_ci	kfree(spec_l3);
156762306a36Sopenharmony_ci	kfree(spec_l4);
156862306a36Sopenharmony_ci	return err;
156962306a36Sopenharmony_ci}
157062306a36Sopenharmony_ci
157162306a36Sopenharmony_cistatic int mlx4_en_ethtool_to_net_trans_rule(struct net_device *dev,
157262306a36Sopenharmony_ci					     struct ethtool_rxnfc *cmd,
157362306a36Sopenharmony_ci					     struct list_head *rule_list_h)
157462306a36Sopenharmony_ci{
157562306a36Sopenharmony_ci	int err;
157662306a36Sopenharmony_ci	struct ethhdr *eth_spec;
157762306a36Sopenharmony_ci	struct mlx4_spec_list *spec_l2;
157862306a36Sopenharmony_ci	struct mlx4_en_priv *priv = netdev_priv(dev);
157962306a36Sopenharmony_ci
158062306a36Sopenharmony_ci	err = mlx4_en_validate_flow(dev, cmd);
158162306a36Sopenharmony_ci	if (err)
158262306a36Sopenharmony_ci		return err;
158362306a36Sopenharmony_ci
158462306a36Sopenharmony_ci	switch (cmd->fs.flow_type & ~(FLOW_EXT | FLOW_MAC_EXT)) {
158562306a36Sopenharmony_ci	case ETHER_FLOW:
158662306a36Sopenharmony_ci		spec_l2 = kzalloc(sizeof(*spec_l2), GFP_KERNEL);
158762306a36Sopenharmony_ci		if (!spec_l2)
158862306a36Sopenharmony_ci			return -ENOMEM;
158962306a36Sopenharmony_ci
159062306a36Sopenharmony_ci		eth_spec = &cmd->fs.h_u.ether_spec;
159162306a36Sopenharmony_ci		mlx4_en_ethtool_add_mac_rule(cmd, rule_list_h, spec_l2,
159262306a36Sopenharmony_ci					     &eth_spec->h_dest[0]);
159362306a36Sopenharmony_ci		spec_l2->eth.ether_type = eth_spec->h_proto;
159462306a36Sopenharmony_ci		if (eth_spec->h_proto)
159562306a36Sopenharmony_ci			spec_l2->eth.ether_type_enable = 1;
159662306a36Sopenharmony_ci		break;
159762306a36Sopenharmony_ci	case IP_USER_FLOW:
159862306a36Sopenharmony_ci		err = add_ip_rule(priv, cmd, rule_list_h);
159962306a36Sopenharmony_ci		break;
160062306a36Sopenharmony_ci	case TCP_V4_FLOW:
160162306a36Sopenharmony_ci		err = add_tcp_udp_rule(priv, cmd, rule_list_h, TCP_V4_FLOW);
160262306a36Sopenharmony_ci		break;
160362306a36Sopenharmony_ci	case UDP_V4_FLOW:
160462306a36Sopenharmony_ci		err = add_tcp_udp_rule(priv, cmd, rule_list_h, UDP_V4_FLOW);
160562306a36Sopenharmony_ci		break;
160662306a36Sopenharmony_ci	}
160762306a36Sopenharmony_ci
160862306a36Sopenharmony_ci	return err;
160962306a36Sopenharmony_ci}
161062306a36Sopenharmony_ci
161162306a36Sopenharmony_cistatic int mlx4_en_flow_replace(struct net_device *dev,
161262306a36Sopenharmony_ci				struct ethtool_rxnfc *cmd)
161362306a36Sopenharmony_ci{
161462306a36Sopenharmony_ci	int err;
161562306a36Sopenharmony_ci	struct mlx4_en_priv *priv = netdev_priv(dev);
161662306a36Sopenharmony_ci	struct ethtool_flow_id *loc_rule;
161762306a36Sopenharmony_ci	struct mlx4_spec_list *spec, *tmp_spec;
161862306a36Sopenharmony_ci	u32 qpn;
161962306a36Sopenharmony_ci	u64 reg_id;
162062306a36Sopenharmony_ci
162162306a36Sopenharmony_ci	struct mlx4_net_trans_rule rule = {
162262306a36Sopenharmony_ci		.queue_mode = MLX4_NET_TRANS_Q_FIFO,
162362306a36Sopenharmony_ci		.exclusive = 0,
162462306a36Sopenharmony_ci		.allow_loopback = 1,
162562306a36Sopenharmony_ci		.promisc_mode = MLX4_FS_REGULAR,
162662306a36Sopenharmony_ci	};
162762306a36Sopenharmony_ci
162862306a36Sopenharmony_ci	rule.port = priv->port;
162962306a36Sopenharmony_ci	rule.priority = MLX4_DOMAIN_ETHTOOL | cmd->fs.location;
163062306a36Sopenharmony_ci	INIT_LIST_HEAD(&rule.list);
163162306a36Sopenharmony_ci
163262306a36Sopenharmony_ci	/* Allow direct QP attaches if the EN_ETHTOOL_QP_ATTACH flag is set */
163362306a36Sopenharmony_ci	if (cmd->fs.ring_cookie == RX_CLS_FLOW_DISC)
163462306a36Sopenharmony_ci		qpn = priv->drop_qp.qpn;
163562306a36Sopenharmony_ci	else if (cmd->fs.ring_cookie & EN_ETHTOOL_QP_ATTACH) {
163662306a36Sopenharmony_ci		qpn = cmd->fs.ring_cookie & (EN_ETHTOOL_QP_ATTACH - 1);
163762306a36Sopenharmony_ci	} else {
163862306a36Sopenharmony_ci		if (cmd->fs.ring_cookie >= priv->rx_ring_num) {
163962306a36Sopenharmony_ci			en_warn(priv, "rxnfc: RX ring (%llu) doesn't exist\n",
164062306a36Sopenharmony_ci				cmd->fs.ring_cookie);
164162306a36Sopenharmony_ci			return -EINVAL;
164262306a36Sopenharmony_ci		}
164362306a36Sopenharmony_ci		qpn = priv->rss_map.qps[cmd->fs.ring_cookie].qpn;
164462306a36Sopenharmony_ci		if (!qpn) {
164562306a36Sopenharmony_ci			en_warn(priv, "rxnfc: RX ring (%llu) is inactive\n",
164662306a36Sopenharmony_ci				cmd->fs.ring_cookie);
164762306a36Sopenharmony_ci			return -EINVAL;
164862306a36Sopenharmony_ci		}
164962306a36Sopenharmony_ci	}
165062306a36Sopenharmony_ci	rule.qpn = qpn;
165162306a36Sopenharmony_ci	err = mlx4_en_ethtool_to_net_trans_rule(dev, cmd, &rule.list);
165262306a36Sopenharmony_ci	if (err)
165362306a36Sopenharmony_ci		goto out_free_list;
165462306a36Sopenharmony_ci
165562306a36Sopenharmony_ci	loc_rule = &priv->ethtool_rules[cmd->fs.location];
165662306a36Sopenharmony_ci	if (loc_rule->id) {
165762306a36Sopenharmony_ci		err = mlx4_flow_detach(priv->mdev->dev, loc_rule->id);
165862306a36Sopenharmony_ci		if (err) {
165962306a36Sopenharmony_ci			en_err(priv, "Fail to detach network rule at location %d. registration id = %llx\n",
166062306a36Sopenharmony_ci			       cmd->fs.location, loc_rule->id);
166162306a36Sopenharmony_ci			goto out_free_list;
166262306a36Sopenharmony_ci		}
166362306a36Sopenharmony_ci		loc_rule->id = 0;
166462306a36Sopenharmony_ci		memset(&loc_rule->flow_spec, 0,
166562306a36Sopenharmony_ci		       sizeof(struct ethtool_rx_flow_spec));
166662306a36Sopenharmony_ci		list_del(&loc_rule->list);
166762306a36Sopenharmony_ci	}
166862306a36Sopenharmony_ci	err = mlx4_flow_attach(priv->mdev->dev, &rule, &reg_id);
166962306a36Sopenharmony_ci	if (err) {
167062306a36Sopenharmony_ci		en_err(priv, "Fail to attach network rule at location %d\n",
167162306a36Sopenharmony_ci		       cmd->fs.location);
167262306a36Sopenharmony_ci		goto out_free_list;
167362306a36Sopenharmony_ci	}
167462306a36Sopenharmony_ci	loc_rule->id = reg_id;
167562306a36Sopenharmony_ci	memcpy(&loc_rule->flow_spec, &cmd->fs,
167662306a36Sopenharmony_ci	       sizeof(struct ethtool_rx_flow_spec));
167762306a36Sopenharmony_ci	list_add_tail(&loc_rule->list, &priv->ethtool_list);
167862306a36Sopenharmony_ci
167962306a36Sopenharmony_ciout_free_list:
168062306a36Sopenharmony_ci	list_for_each_entry_safe(spec, tmp_spec, &rule.list, list) {
168162306a36Sopenharmony_ci		list_del(&spec->list);
168262306a36Sopenharmony_ci		kfree(spec);
168362306a36Sopenharmony_ci	}
168462306a36Sopenharmony_ci	return err;
168562306a36Sopenharmony_ci}
168662306a36Sopenharmony_ci
168762306a36Sopenharmony_cistatic int mlx4_en_flow_detach(struct net_device *dev,
168862306a36Sopenharmony_ci			       struct ethtool_rxnfc *cmd)
168962306a36Sopenharmony_ci{
169062306a36Sopenharmony_ci	int err = 0;
169162306a36Sopenharmony_ci	struct ethtool_flow_id *rule;
169262306a36Sopenharmony_ci	struct mlx4_en_priv *priv = netdev_priv(dev);
169362306a36Sopenharmony_ci
169462306a36Sopenharmony_ci	if (cmd->fs.location >= MAX_NUM_OF_FS_RULES)
169562306a36Sopenharmony_ci		return -EINVAL;
169662306a36Sopenharmony_ci
169762306a36Sopenharmony_ci	rule = &priv->ethtool_rules[cmd->fs.location];
169862306a36Sopenharmony_ci	if (!rule->id) {
169962306a36Sopenharmony_ci		err =  -ENOENT;
170062306a36Sopenharmony_ci		goto out;
170162306a36Sopenharmony_ci	}
170262306a36Sopenharmony_ci
170362306a36Sopenharmony_ci	err = mlx4_flow_detach(priv->mdev->dev, rule->id);
170462306a36Sopenharmony_ci	if (err) {
170562306a36Sopenharmony_ci		en_err(priv, "Fail to detach network rule at location %d. registration id = 0x%llx\n",
170662306a36Sopenharmony_ci		       cmd->fs.location, rule->id);
170762306a36Sopenharmony_ci		goto out;
170862306a36Sopenharmony_ci	}
170962306a36Sopenharmony_ci	rule->id = 0;
171062306a36Sopenharmony_ci	memset(&rule->flow_spec, 0, sizeof(struct ethtool_rx_flow_spec));
171162306a36Sopenharmony_ci	list_del(&rule->list);
171262306a36Sopenharmony_ciout:
171362306a36Sopenharmony_ci	return err;
171462306a36Sopenharmony_ci
171562306a36Sopenharmony_ci}
171662306a36Sopenharmony_ci
171762306a36Sopenharmony_cistatic int mlx4_en_get_flow(struct net_device *dev, struct ethtool_rxnfc *cmd,
171862306a36Sopenharmony_ci			    int loc)
171962306a36Sopenharmony_ci{
172062306a36Sopenharmony_ci	int err = 0;
172162306a36Sopenharmony_ci	struct ethtool_flow_id *rule;
172262306a36Sopenharmony_ci	struct mlx4_en_priv *priv = netdev_priv(dev);
172362306a36Sopenharmony_ci
172462306a36Sopenharmony_ci	if (loc < 0 || loc >= MAX_NUM_OF_FS_RULES)
172562306a36Sopenharmony_ci		return -EINVAL;
172662306a36Sopenharmony_ci
172762306a36Sopenharmony_ci	rule = &priv->ethtool_rules[loc];
172862306a36Sopenharmony_ci	if (rule->id)
172962306a36Sopenharmony_ci		memcpy(&cmd->fs, &rule->flow_spec,
173062306a36Sopenharmony_ci		       sizeof(struct ethtool_rx_flow_spec));
173162306a36Sopenharmony_ci	else
173262306a36Sopenharmony_ci		err = -ENOENT;
173362306a36Sopenharmony_ci
173462306a36Sopenharmony_ci	return err;
173562306a36Sopenharmony_ci}
173662306a36Sopenharmony_ci
173762306a36Sopenharmony_cistatic int mlx4_en_get_num_flows(struct mlx4_en_priv *priv)
173862306a36Sopenharmony_ci{
173962306a36Sopenharmony_ci
174062306a36Sopenharmony_ci	int i, res = 0;
174162306a36Sopenharmony_ci	for (i = 0; i < MAX_NUM_OF_FS_RULES; i++) {
174262306a36Sopenharmony_ci		if (priv->ethtool_rules[i].id)
174362306a36Sopenharmony_ci			res++;
174462306a36Sopenharmony_ci	}
174562306a36Sopenharmony_ci	return res;
174662306a36Sopenharmony_ci
174762306a36Sopenharmony_ci}
174862306a36Sopenharmony_ci
174962306a36Sopenharmony_cistatic int mlx4_en_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd,
175062306a36Sopenharmony_ci			     u32 *rule_locs)
175162306a36Sopenharmony_ci{
175262306a36Sopenharmony_ci	struct mlx4_en_priv *priv = netdev_priv(dev);
175362306a36Sopenharmony_ci	struct mlx4_en_dev *mdev = priv->mdev;
175462306a36Sopenharmony_ci	int err = 0;
175562306a36Sopenharmony_ci	int i = 0, priority = 0;
175662306a36Sopenharmony_ci
175762306a36Sopenharmony_ci	if ((cmd->cmd == ETHTOOL_GRXCLSRLCNT ||
175862306a36Sopenharmony_ci	     cmd->cmd == ETHTOOL_GRXCLSRULE ||
175962306a36Sopenharmony_ci	     cmd->cmd == ETHTOOL_GRXCLSRLALL) &&
176062306a36Sopenharmony_ci	    (mdev->dev->caps.steering_mode !=
176162306a36Sopenharmony_ci	     MLX4_STEERING_MODE_DEVICE_MANAGED || !priv->port_up))
176262306a36Sopenharmony_ci		return -EINVAL;
176362306a36Sopenharmony_ci
176462306a36Sopenharmony_ci	switch (cmd->cmd) {
176562306a36Sopenharmony_ci	case ETHTOOL_GRXRINGS:
176662306a36Sopenharmony_ci		cmd->data = priv->rx_ring_num;
176762306a36Sopenharmony_ci		break;
176862306a36Sopenharmony_ci	case ETHTOOL_GRXCLSRLCNT:
176962306a36Sopenharmony_ci		cmd->rule_cnt = mlx4_en_get_num_flows(priv);
177062306a36Sopenharmony_ci		break;
177162306a36Sopenharmony_ci	case ETHTOOL_GRXCLSRULE:
177262306a36Sopenharmony_ci		err = mlx4_en_get_flow(dev, cmd, cmd->fs.location);
177362306a36Sopenharmony_ci		break;
177462306a36Sopenharmony_ci	case ETHTOOL_GRXCLSRLALL:
177562306a36Sopenharmony_ci		cmd->data = MAX_NUM_OF_FS_RULES;
177662306a36Sopenharmony_ci		while ((!err || err == -ENOENT) && priority < cmd->rule_cnt) {
177762306a36Sopenharmony_ci			err = mlx4_en_get_flow(dev, cmd, i);
177862306a36Sopenharmony_ci			if (!err)
177962306a36Sopenharmony_ci				rule_locs[priority++] = i;
178062306a36Sopenharmony_ci			i++;
178162306a36Sopenharmony_ci		}
178262306a36Sopenharmony_ci		err = 0;
178362306a36Sopenharmony_ci		break;
178462306a36Sopenharmony_ci	default:
178562306a36Sopenharmony_ci		err = -EOPNOTSUPP;
178662306a36Sopenharmony_ci		break;
178762306a36Sopenharmony_ci	}
178862306a36Sopenharmony_ci
178962306a36Sopenharmony_ci	return err;
179062306a36Sopenharmony_ci}
179162306a36Sopenharmony_ci
179262306a36Sopenharmony_cistatic int mlx4_en_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd)
179362306a36Sopenharmony_ci{
179462306a36Sopenharmony_ci	int err = 0;
179562306a36Sopenharmony_ci	struct mlx4_en_priv *priv = netdev_priv(dev);
179662306a36Sopenharmony_ci	struct mlx4_en_dev *mdev = priv->mdev;
179762306a36Sopenharmony_ci
179862306a36Sopenharmony_ci	if (mdev->dev->caps.steering_mode !=
179962306a36Sopenharmony_ci	    MLX4_STEERING_MODE_DEVICE_MANAGED || !priv->port_up)
180062306a36Sopenharmony_ci		return -EINVAL;
180162306a36Sopenharmony_ci
180262306a36Sopenharmony_ci	switch (cmd->cmd) {
180362306a36Sopenharmony_ci	case ETHTOOL_SRXCLSRLINS:
180462306a36Sopenharmony_ci		err = mlx4_en_flow_replace(dev, cmd);
180562306a36Sopenharmony_ci		break;
180662306a36Sopenharmony_ci	case ETHTOOL_SRXCLSRLDEL:
180762306a36Sopenharmony_ci		err = mlx4_en_flow_detach(dev, cmd);
180862306a36Sopenharmony_ci		break;
180962306a36Sopenharmony_ci	default:
181062306a36Sopenharmony_ci		en_warn(priv, "Unsupported ethtool command. (%d)\n", cmd->cmd);
181162306a36Sopenharmony_ci		return -EINVAL;
181262306a36Sopenharmony_ci	}
181362306a36Sopenharmony_ci
181462306a36Sopenharmony_ci	return err;
181562306a36Sopenharmony_ci}
181662306a36Sopenharmony_ci
181762306a36Sopenharmony_cistatic int mlx4_en_get_max_num_rx_rings(struct net_device *dev)
181862306a36Sopenharmony_ci{
181962306a36Sopenharmony_ci	return min_t(int, num_online_cpus(), MAX_RX_RINGS);
182062306a36Sopenharmony_ci}
182162306a36Sopenharmony_ci
182262306a36Sopenharmony_cistatic void mlx4_en_get_channels(struct net_device *dev,
182362306a36Sopenharmony_ci				 struct ethtool_channels *channel)
182462306a36Sopenharmony_ci{
182562306a36Sopenharmony_ci	struct mlx4_en_priv *priv = netdev_priv(dev);
182662306a36Sopenharmony_ci
182762306a36Sopenharmony_ci	channel->max_rx = mlx4_en_get_max_num_rx_rings(dev);
182862306a36Sopenharmony_ci	channel->max_tx = priv->mdev->profile.max_num_tx_rings_p_up;
182962306a36Sopenharmony_ci
183062306a36Sopenharmony_ci	channel->rx_count = priv->rx_ring_num;
183162306a36Sopenharmony_ci	channel->tx_count = priv->tx_ring_num[TX] /
183262306a36Sopenharmony_ci			    priv->prof->num_up;
183362306a36Sopenharmony_ci}
183462306a36Sopenharmony_ci
183562306a36Sopenharmony_cistatic int mlx4_en_set_channels(struct net_device *dev,
183662306a36Sopenharmony_ci				struct ethtool_channels *channel)
183762306a36Sopenharmony_ci{
183862306a36Sopenharmony_ci	struct mlx4_en_priv *priv = netdev_priv(dev);
183962306a36Sopenharmony_ci	struct mlx4_en_dev *mdev = priv->mdev;
184062306a36Sopenharmony_ci	struct mlx4_en_port_profile new_prof;
184162306a36Sopenharmony_ci	struct mlx4_en_priv *tmp;
184262306a36Sopenharmony_ci	int total_tx_count;
184362306a36Sopenharmony_ci	int port_up = 0;
184462306a36Sopenharmony_ci	int xdp_count;
184562306a36Sopenharmony_ci	int err = 0;
184662306a36Sopenharmony_ci	u8 up;
184762306a36Sopenharmony_ci
184862306a36Sopenharmony_ci	if (!channel->tx_count || !channel->rx_count)
184962306a36Sopenharmony_ci		return -EINVAL;
185062306a36Sopenharmony_ci
185162306a36Sopenharmony_ci	tmp = kzalloc(sizeof(*tmp), GFP_KERNEL);
185262306a36Sopenharmony_ci	if (!tmp)
185362306a36Sopenharmony_ci		return -ENOMEM;
185462306a36Sopenharmony_ci
185562306a36Sopenharmony_ci	mutex_lock(&mdev->state_lock);
185662306a36Sopenharmony_ci	xdp_count = priv->tx_ring_num[TX_XDP] ? channel->rx_count : 0;
185762306a36Sopenharmony_ci	total_tx_count = channel->tx_count * priv->prof->num_up + xdp_count;
185862306a36Sopenharmony_ci	if (total_tx_count > MAX_TX_RINGS) {
185962306a36Sopenharmony_ci		err = -EINVAL;
186062306a36Sopenharmony_ci		en_err(priv,
186162306a36Sopenharmony_ci		       "Total number of TX and XDP rings (%d) exceeds the maximum supported (%d)\n",
186262306a36Sopenharmony_ci		       total_tx_count, MAX_TX_RINGS);
186362306a36Sopenharmony_ci		goto out;
186462306a36Sopenharmony_ci	}
186562306a36Sopenharmony_ci
186662306a36Sopenharmony_ci	memcpy(&new_prof, priv->prof, sizeof(struct mlx4_en_port_profile));
186762306a36Sopenharmony_ci	new_prof.num_tx_rings_p_up = channel->tx_count;
186862306a36Sopenharmony_ci	new_prof.tx_ring_num[TX] = channel->tx_count * priv->prof->num_up;
186962306a36Sopenharmony_ci	new_prof.tx_ring_num[TX_XDP] = xdp_count;
187062306a36Sopenharmony_ci	new_prof.rx_ring_num = channel->rx_count;
187162306a36Sopenharmony_ci
187262306a36Sopenharmony_ci	err = mlx4_en_try_alloc_resources(priv, tmp, &new_prof, true);
187362306a36Sopenharmony_ci	if (err)
187462306a36Sopenharmony_ci		goto out;
187562306a36Sopenharmony_ci
187662306a36Sopenharmony_ci	if (priv->port_up) {
187762306a36Sopenharmony_ci		port_up = 1;
187862306a36Sopenharmony_ci		mlx4_en_stop_port(dev, 1);
187962306a36Sopenharmony_ci	}
188062306a36Sopenharmony_ci
188162306a36Sopenharmony_ci	mlx4_en_safe_replace_resources(priv, tmp);
188262306a36Sopenharmony_ci
188362306a36Sopenharmony_ci	netif_set_real_num_rx_queues(dev, priv->rx_ring_num);
188462306a36Sopenharmony_ci
188562306a36Sopenharmony_ci	up = (priv->prof->num_up == MLX4_EN_NUM_UP_LOW) ?
188662306a36Sopenharmony_ci				    0 : priv->prof->num_up;
188762306a36Sopenharmony_ci	mlx4_en_setup_tc(dev, up);
188862306a36Sopenharmony_ci
188962306a36Sopenharmony_ci	en_warn(priv, "Using %d TX rings\n", priv->tx_ring_num[TX]);
189062306a36Sopenharmony_ci	en_warn(priv, "Using %d RX rings\n", priv->rx_ring_num);
189162306a36Sopenharmony_ci
189262306a36Sopenharmony_ci	if (port_up) {
189362306a36Sopenharmony_ci		err = mlx4_en_start_port(dev);
189462306a36Sopenharmony_ci		if (err)
189562306a36Sopenharmony_ci			en_err(priv, "Failed starting port\n");
189662306a36Sopenharmony_ci	}
189762306a36Sopenharmony_ci
189862306a36Sopenharmony_ci	err = mlx4_en_moderation_update(priv);
189962306a36Sopenharmony_ciout:
190062306a36Sopenharmony_ci	mutex_unlock(&mdev->state_lock);
190162306a36Sopenharmony_ci	kfree(tmp);
190262306a36Sopenharmony_ci	return err;
190362306a36Sopenharmony_ci}
190462306a36Sopenharmony_ci
190562306a36Sopenharmony_cistatic int mlx4_en_get_ts_info(struct net_device *dev,
190662306a36Sopenharmony_ci			       struct ethtool_ts_info *info)
190762306a36Sopenharmony_ci{
190862306a36Sopenharmony_ci	struct mlx4_en_priv *priv = netdev_priv(dev);
190962306a36Sopenharmony_ci	struct mlx4_en_dev *mdev = priv->mdev;
191062306a36Sopenharmony_ci	int ret;
191162306a36Sopenharmony_ci
191262306a36Sopenharmony_ci	ret = ethtool_op_get_ts_info(dev, info);
191362306a36Sopenharmony_ci	if (ret)
191462306a36Sopenharmony_ci		return ret;
191562306a36Sopenharmony_ci
191662306a36Sopenharmony_ci	if (mdev->dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_TS) {
191762306a36Sopenharmony_ci		info->so_timestamping |=
191862306a36Sopenharmony_ci			SOF_TIMESTAMPING_TX_HARDWARE |
191962306a36Sopenharmony_ci			SOF_TIMESTAMPING_RX_HARDWARE |
192062306a36Sopenharmony_ci			SOF_TIMESTAMPING_RAW_HARDWARE;
192162306a36Sopenharmony_ci
192262306a36Sopenharmony_ci		info->tx_types =
192362306a36Sopenharmony_ci			(1 << HWTSTAMP_TX_OFF) |
192462306a36Sopenharmony_ci			(1 << HWTSTAMP_TX_ON);
192562306a36Sopenharmony_ci
192662306a36Sopenharmony_ci		info->rx_filters =
192762306a36Sopenharmony_ci			(1 << HWTSTAMP_FILTER_NONE) |
192862306a36Sopenharmony_ci			(1 << HWTSTAMP_FILTER_ALL);
192962306a36Sopenharmony_ci
193062306a36Sopenharmony_ci		if (mdev->ptp_clock)
193162306a36Sopenharmony_ci			info->phc_index = ptp_clock_index(mdev->ptp_clock);
193262306a36Sopenharmony_ci	}
193362306a36Sopenharmony_ci
193462306a36Sopenharmony_ci	return ret;
193562306a36Sopenharmony_ci}
193662306a36Sopenharmony_ci
193762306a36Sopenharmony_cistatic int mlx4_en_set_priv_flags(struct net_device *dev, u32 flags)
193862306a36Sopenharmony_ci{
193962306a36Sopenharmony_ci	struct mlx4_en_priv *priv = netdev_priv(dev);
194062306a36Sopenharmony_ci	struct mlx4_en_dev *mdev = priv->mdev;
194162306a36Sopenharmony_ci	bool bf_enabled_new = !!(flags & MLX4_EN_PRIV_FLAGS_BLUEFLAME);
194262306a36Sopenharmony_ci	bool bf_enabled_old = !!(priv->pflags & MLX4_EN_PRIV_FLAGS_BLUEFLAME);
194362306a36Sopenharmony_ci	bool phv_enabled_new = !!(flags & MLX4_EN_PRIV_FLAGS_PHV);
194462306a36Sopenharmony_ci	bool phv_enabled_old = !!(priv->pflags & MLX4_EN_PRIV_FLAGS_PHV);
194562306a36Sopenharmony_ci	int i;
194662306a36Sopenharmony_ci	int ret = 0;
194762306a36Sopenharmony_ci
194862306a36Sopenharmony_ci	if (bf_enabled_new != bf_enabled_old) {
194962306a36Sopenharmony_ci		int t;
195062306a36Sopenharmony_ci
195162306a36Sopenharmony_ci		if (bf_enabled_new) {
195262306a36Sopenharmony_ci			bool bf_supported = true;
195362306a36Sopenharmony_ci
195462306a36Sopenharmony_ci			for (t = 0; t < MLX4_EN_NUM_TX_TYPES; t++)
195562306a36Sopenharmony_ci				for (i = 0; i < priv->tx_ring_num[t]; i++)
195662306a36Sopenharmony_ci					bf_supported &=
195762306a36Sopenharmony_ci						priv->tx_ring[t][i]->bf_alloced;
195862306a36Sopenharmony_ci
195962306a36Sopenharmony_ci			if (!bf_supported) {
196062306a36Sopenharmony_ci				en_err(priv, "BlueFlame is not supported\n");
196162306a36Sopenharmony_ci				return -EINVAL;
196262306a36Sopenharmony_ci			}
196362306a36Sopenharmony_ci
196462306a36Sopenharmony_ci			priv->pflags |= MLX4_EN_PRIV_FLAGS_BLUEFLAME;
196562306a36Sopenharmony_ci		} else {
196662306a36Sopenharmony_ci			priv->pflags &= ~MLX4_EN_PRIV_FLAGS_BLUEFLAME;
196762306a36Sopenharmony_ci		}
196862306a36Sopenharmony_ci
196962306a36Sopenharmony_ci		for (t = 0; t < MLX4_EN_NUM_TX_TYPES; t++)
197062306a36Sopenharmony_ci			for (i = 0; i < priv->tx_ring_num[t]; i++)
197162306a36Sopenharmony_ci				priv->tx_ring[t][i]->bf_enabled =
197262306a36Sopenharmony_ci					bf_enabled_new;
197362306a36Sopenharmony_ci
197462306a36Sopenharmony_ci		en_info(priv, "BlueFlame %s\n",
197562306a36Sopenharmony_ci			bf_enabled_new ?  "Enabled" : "Disabled");
197662306a36Sopenharmony_ci	}
197762306a36Sopenharmony_ci
197862306a36Sopenharmony_ci	if (phv_enabled_new != phv_enabled_old) {
197962306a36Sopenharmony_ci		ret = set_phv_bit(mdev->dev, priv->port, (int)phv_enabled_new);
198062306a36Sopenharmony_ci		if (ret)
198162306a36Sopenharmony_ci			return ret;
198262306a36Sopenharmony_ci		else if (phv_enabled_new)
198362306a36Sopenharmony_ci			priv->pflags |= MLX4_EN_PRIV_FLAGS_PHV;
198462306a36Sopenharmony_ci		else
198562306a36Sopenharmony_ci			priv->pflags &= ~MLX4_EN_PRIV_FLAGS_PHV;
198662306a36Sopenharmony_ci		en_info(priv, "PHV bit %s\n",
198762306a36Sopenharmony_ci			phv_enabled_new ?  "Enabled" : "Disabled");
198862306a36Sopenharmony_ci	}
198962306a36Sopenharmony_ci	return 0;
199062306a36Sopenharmony_ci}
199162306a36Sopenharmony_ci
199262306a36Sopenharmony_cistatic u32 mlx4_en_get_priv_flags(struct net_device *dev)
199362306a36Sopenharmony_ci{
199462306a36Sopenharmony_ci	struct mlx4_en_priv *priv = netdev_priv(dev);
199562306a36Sopenharmony_ci
199662306a36Sopenharmony_ci	return priv->pflags;
199762306a36Sopenharmony_ci}
199862306a36Sopenharmony_ci
199962306a36Sopenharmony_cistatic int mlx4_en_get_tunable(struct net_device *dev,
200062306a36Sopenharmony_ci			       const struct ethtool_tunable *tuna,
200162306a36Sopenharmony_ci			       void *data)
200262306a36Sopenharmony_ci{
200362306a36Sopenharmony_ci	const struct mlx4_en_priv *priv = netdev_priv(dev);
200462306a36Sopenharmony_ci	int ret = 0;
200562306a36Sopenharmony_ci
200662306a36Sopenharmony_ci	switch (tuna->id) {
200762306a36Sopenharmony_ci	case ETHTOOL_TX_COPYBREAK:
200862306a36Sopenharmony_ci		*(u32 *)data = priv->prof->inline_thold;
200962306a36Sopenharmony_ci		break;
201062306a36Sopenharmony_ci	default:
201162306a36Sopenharmony_ci		ret = -EINVAL;
201262306a36Sopenharmony_ci		break;
201362306a36Sopenharmony_ci	}
201462306a36Sopenharmony_ci
201562306a36Sopenharmony_ci	return ret;
201662306a36Sopenharmony_ci}
201762306a36Sopenharmony_ci
201862306a36Sopenharmony_cistatic int mlx4_en_set_tunable(struct net_device *dev,
201962306a36Sopenharmony_ci			       const struct ethtool_tunable *tuna,
202062306a36Sopenharmony_ci			       const void *data)
202162306a36Sopenharmony_ci{
202262306a36Sopenharmony_ci	struct mlx4_en_priv *priv = netdev_priv(dev);
202362306a36Sopenharmony_ci	int val, ret = 0;
202462306a36Sopenharmony_ci
202562306a36Sopenharmony_ci	switch (tuna->id) {
202662306a36Sopenharmony_ci	case ETHTOOL_TX_COPYBREAK:
202762306a36Sopenharmony_ci		val = *(u32 *)data;
202862306a36Sopenharmony_ci		if (val < MIN_PKT_LEN || val > MAX_INLINE)
202962306a36Sopenharmony_ci			ret = -EINVAL;
203062306a36Sopenharmony_ci		else
203162306a36Sopenharmony_ci			priv->prof->inline_thold = val;
203262306a36Sopenharmony_ci		break;
203362306a36Sopenharmony_ci	default:
203462306a36Sopenharmony_ci		ret = -EINVAL;
203562306a36Sopenharmony_ci		break;
203662306a36Sopenharmony_ci	}
203762306a36Sopenharmony_ci
203862306a36Sopenharmony_ci	return ret;
203962306a36Sopenharmony_ci}
204062306a36Sopenharmony_ci
204162306a36Sopenharmony_cistatic int mlx4_en_get_module_info(struct net_device *dev,
204262306a36Sopenharmony_ci				   struct ethtool_modinfo *modinfo)
204362306a36Sopenharmony_ci{
204462306a36Sopenharmony_ci	struct mlx4_en_priv *priv = netdev_priv(dev);
204562306a36Sopenharmony_ci	struct mlx4_en_dev *mdev = priv->mdev;
204662306a36Sopenharmony_ci	int ret;
204762306a36Sopenharmony_ci	u8 data[4];
204862306a36Sopenharmony_ci
204962306a36Sopenharmony_ci	/* Read first 2 bytes to get Module & REV ID */
205062306a36Sopenharmony_ci	ret = mlx4_get_module_info(mdev->dev, priv->port,
205162306a36Sopenharmony_ci				   0/*offset*/, 2/*size*/, data);
205262306a36Sopenharmony_ci	if (ret < 2)
205362306a36Sopenharmony_ci		return -EIO;
205462306a36Sopenharmony_ci
205562306a36Sopenharmony_ci	switch (data[0] /* identifier */) {
205662306a36Sopenharmony_ci	case MLX4_MODULE_ID_QSFP:
205762306a36Sopenharmony_ci		modinfo->type = ETH_MODULE_SFF_8436;
205862306a36Sopenharmony_ci		modinfo->eeprom_len = ETH_MODULE_SFF_8436_LEN;
205962306a36Sopenharmony_ci		break;
206062306a36Sopenharmony_ci	case MLX4_MODULE_ID_QSFP_PLUS:
206162306a36Sopenharmony_ci		if (data[1] >= 0x3) { /* revision id */
206262306a36Sopenharmony_ci			modinfo->type = ETH_MODULE_SFF_8636;
206362306a36Sopenharmony_ci			modinfo->eeprom_len = ETH_MODULE_SFF_8636_LEN;
206462306a36Sopenharmony_ci		} else {
206562306a36Sopenharmony_ci			modinfo->type = ETH_MODULE_SFF_8436;
206662306a36Sopenharmony_ci			modinfo->eeprom_len = ETH_MODULE_SFF_8436_LEN;
206762306a36Sopenharmony_ci		}
206862306a36Sopenharmony_ci		break;
206962306a36Sopenharmony_ci	case MLX4_MODULE_ID_QSFP28:
207062306a36Sopenharmony_ci		modinfo->type = ETH_MODULE_SFF_8636;
207162306a36Sopenharmony_ci		modinfo->eeprom_len = ETH_MODULE_SFF_8636_LEN;
207262306a36Sopenharmony_ci		break;
207362306a36Sopenharmony_ci	case MLX4_MODULE_ID_SFP:
207462306a36Sopenharmony_ci		modinfo->type = ETH_MODULE_SFF_8472;
207562306a36Sopenharmony_ci		modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN;
207662306a36Sopenharmony_ci		break;
207762306a36Sopenharmony_ci	default:
207862306a36Sopenharmony_ci		return -EINVAL;
207962306a36Sopenharmony_ci	}
208062306a36Sopenharmony_ci
208162306a36Sopenharmony_ci	return 0;
208262306a36Sopenharmony_ci}
208362306a36Sopenharmony_ci
208462306a36Sopenharmony_cistatic int mlx4_en_get_module_eeprom(struct net_device *dev,
208562306a36Sopenharmony_ci				     struct ethtool_eeprom *ee,
208662306a36Sopenharmony_ci				     u8 *data)
208762306a36Sopenharmony_ci{
208862306a36Sopenharmony_ci	struct mlx4_en_priv *priv = netdev_priv(dev);
208962306a36Sopenharmony_ci	struct mlx4_en_dev *mdev = priv->mdev;
209062306a36Sopenharmony_ci	int offset = ee->offset;
209162306a36Sopenharmony_ci	int i = 0, ret;
209262306a36Sopenharmony_ci
209362306a36Sopenharmony_ci	if (ee->len == 0)
209462306a36Sopenharmony_ci		return -EINVAL;
209562306a36Sopenharmony_ci
209662306a36Sopenharmony_ci	memset(data, 0, ee->len);
209762306a36Sopenharmony_ci
209862306a36Sopenharmony_ci	while (i < ee->len) {
209962306a36Sopenharmony_ci		en_dbg(DRV, priv,
210062306a36Sopenharmony_ci		       "mlx4_get_module_info i(%d) offset(%d) len(%d)\n",
210162306a36Sopenharmony_ci		       i, offset, ee->len - i);
210262306a36Sopenharmony_ci
210362306a36Sopenharmony_ci		ret = mlx4_get_module_info(mdev->dev, priv->port,
210462306a36Sopenharmony_ci					   offset, ee->len - i, data + i);
210562306a36Sopenharmony_ci
210662306a36Sopenharmony_ci		if (!ret) /* Done reading */
210762306a36Sopenharmony_ci			return 0;
210862306a36Sopenharmony_ci
210962306a36Sopenharmony_ci		if (ret < 0) {
211062306a36Sopenharmony_ci			en_err(priv,
211162306a36Sopenharmony_ci			       "mlx4_get_module_info i(%d) offset(%d) bytes_to_read(%d) - FAILED (0x%x)\n",
211262306a36Sopenharmony_ci			       i, offset, ee->len - i, ret);
211362306a36Sopenharmony_ci			return ret;
211462306a36Sopenharmony_ci		}
211562306a36Sopenharmony_ci
211662306a36Sopenharmony_ci		i += ret;
211762306a36Sopenharmony_ci		offset += ret;
211862306a36Sopenharmony_ci	}
211962306a36Sopenharmony_ci	return 0;
212062306a36Sopenharmony_ci}
212162306a36Sopenharmony_ci
212262306a36Sopenharmony_cistatic int mlx4_en_set_phys_id(struct net_device *dev,
212362306a36Sopenharmony_ci			       enum ethtool_phys_id_state state)
212462306a36Sopenharmony_ci{
212562306a36Sopenharmony_ci	int err;
212662306a36Sopenharmony_ci	u16 beacon_duration;
212762306a36Sopenharmony_ci	struct mlx4_en_priv *priv = netdev_priv(dev);
212862306a36Sopenharmony_ci	struct mlx4_en_dev *mdev = priv->mdev;
212962306a36Sopenharmony_ci
213062306a36Sopenharmony_ci	if (!(mdev->dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_PORT_BEACON))
213162306a36Sopenharmony_ci		return -EOPNOTSUPP;
213262306a36Sopenharmony_ci
213362306a36Sopenharmony_ci	switch (state) {
213462306a36Sopenharmony_ci	case ETHTOOL_ID_ACTIVE:
213562306a36Sopenharmony_ci		beacon_duration = PORT_BEACON_MAX_LIMIT;
213662306a36Sopenharmony_ci		break;
213762306a36Sopenharmony_ci	case ETHTOOL_ID_INACTIVE:
213862306a36Sopenharmony_ci		beacon_duration = 0;
213962306a36Sopenharmony_ci		break;
214062306a36Sopenharmony_ci	default:
214162306a36Sopenharmony_ci		return -EOPNOTSUPP;
214262306a36Sopenharmony_ci	}
214362306a36Sopenharmony_ci
214462306a36Sopenharmony_ci	err = mlx4_SET_PORT_BEACON(mdev->dev, priv->port, beacon_duration);
214562306a36Sopenharmony_ci	return err;
214662306a36Sopenharmony_ci}
214762306a36Sopenharmony_ci
214862306a36Sopenharmony_ciconst struct ethtool_ops mlx4_en_ethtool_ops = {
214962306a36Sopenharmony_ci	.supported_coalesce_params = ETHTOOL_COALESCE_USECS |
215062306a36Sopenharmony_ci				     ETHTOOL_COALESCE_MAX_FRAMES |
215162306a36Sopenharmony_ci				     ETHTOOL_COALESCE_TX_MAX_FRAMES_IRQ |
215262306a36Sopenharmony_ci				     ETHTOOL_COALESCE_PKT_RATE_RX_USECS,
215362306a36Sopenharmony_ci	.get_drvinfo = mlx4_en_get_drvinfo,
215462306a36Sopenharmony_ci	.get_link_ksettings = mlx4_en_get_link_ksettings,
215562306a36Sopenharmony_ci	.set_link_ksettings = mlx4_en_set_link_ksettings,
215662306a36Sopenharmony_ci	.get_link = ethtool_op_get_link,
215762306a36Sopenharmony_ci	.get_strings = mlx4_en_get_strings,
215862306a36Sopenharmony_ci	.get_sset_count = mlx4_en_get_sset_count,
215962306a36Sopenharmony_ci	.get_ethtool_stats = mlx4_en_get_ethtool_stats,
216062306a36Sopenharmony_ci	.self_test = mlx4_en_self_test,
216162306a36Sopenharmony_ci	.set_phys_id = mlx4_en_set_phys_id,
216262306a36Sopenharmony_ci	.get_wol = mlx4_en_get_wol,
216362306a36Sopenharmony_ci	.set_wol = mlx4_en_set_wol,
216462306a36Sopenharmony_ci	.get_msglevel = mlx4_en_get_msglevel,
216562306a36Sopenharmony_ci	.set_msglevel = mlx4_en_set_msglevel,
216662306a36Sopenharmony_ci	.get_coalesce = mlx4_en_get_coalesce,
216762306a36Sopenharmony_ci	.set_coalesce = mlx4_en_set_coalesce,
216862306a36Sopenharmony_ci	.get_pause_stats = mlx4_en_get_pause_stats,
216962306a36Sopenharmony_ci	.get_pauseparam = mlx4_en_get_pauseparam,
217062306a36Sopenharmony_ci	.set_pauseparam = mlx4_en_set_pauseparam,
217162306a36Sopenharmony_ci	.get_ringparam = mlx4_en_get_ringparam,
217262306a36Sopenharmony_ci	.set_ringparam = mlx4_en_set_ringparam,
217362306a36Sopenharmony_ci	.get_rxnfc = mlx4_en_get_rxnfc,
217462306a36Sopenharmony_ci	.set_rxnfc = mlx4_en_set_rxnfc,
217562306a36Sopenharmony_ci	.get_rxfh_indir_size = mlx4_en_get_rxfh_indir_size,
217662306a36Sopenharmony_ci	.get_rxfh_key_size = mlx4_en_get_rxfh_key_size,
217762306a36Sopenharmony_ci	.get_rxfh = mlx4_en_get_rxfh,
217862306a36Sopenharmony_ci	.set_rxfh = mlx4_en_set_rxfh,
217962306a36Sopenharmony_ci	.get_channels = mlx4_en_get_channels,
218062306a36Sopenharmony_ci	.set_channels = mlx4_en_set_channels,
218162306a36Sopenharmony_ci	.get_ts_info = mlx4_en_get_ts_info,
218262306a36Sopenharmony_ci	.set_priv_flags = mlx4_en_set_priv_flags,
218362306a36Sopenharmony_ci	.get_priv_flags = mlx4_en_get_priv_flags,
218462306a36Sopenharmony_ci	.get_tunable		= mlx4_en_get_tunable,
218562306a36Sopenharmony_ci	.set_tunable		= mlx4_en_set_tunable,
218662306a36Sopenharmony_ci	.get_module_info = mlx4_en_get_module_info,
218762306a36Sopenharmony_ci	.get_module_eeprom = mlx4_en_get_module_eeprom
218862306a36Sopenharmony_ci};
218962306a36Sopenharmony_ci
219062306a36Sopenharmony_ci
219162306a36Sopenharmony_ci
219262306a36Sopenharmony_ci
219362306a36Sopenharmony_ci
2194