162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/* Copyright (c)  2018 Intel Corporation */
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci/* ethtool support for igc */
562306a36Sopenharmony_ci#include <linux/if_vlan.h>
662306a36Sopenharmony_ci#include <linux/pm_runtime.h>
762306a36Sopenharmony_ci#include <linux/mdio.h>
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include "igc.h"
1062306a36Sopenharmony_ci#include "igc_diag.h"
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci/* forward declaration */
1362306a36Sopenharmony_cistruct igc_stats {
1462306a36Sopenharmony_ci	char stat_string[ETH_GSTRING_LEN];
1562306a36Sopenharmony_ci	int sizeof_stat;
1662306a36Sopenharmony_ci	int stat_offset;
1762306a36Sopenharmony_ci};
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#define IGC_STAT(_name, _stat) { \
2062306a36Sopenharmony_ci	.stat_string = _name, \
2162306a36Sopenharmony_ci	.sizeof_stat = sizeof_field(struct igc_adapter, _stat), \
2262306a36Sopenharmony_ci	.stat_offset = offsetof(struct igc_adapter, _stat) \
2362306a36Sopenharmony_ci}
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistatic const struct igc_stats igc_gstrings_stats[] = {
2662306a36Sopenharmony_ci	IGC_STAT("rx_packets", stats.gprc),
2762306a36Sopenharmony_ci	IGC_STAT("tx_packets", stats.gptc),
2862306a36Sopenharmony_ci	IGC_STAT("rx_bytes", stats.gorc),
2962306a36Sopenharmony_ci	IGC_STAT("tx_bytes", stats.gotc),
3062306a36Sopenharmony_ci	IGC_STAT("rx_broadcast", stats.bprc),
3162306a36Sopenharmony_ci	IGC_STAT("tx_broadcast", stats.bptc),
3262306a36Sopenharmony_ci	IGC_STAT("rx_multicast", stats.mprc),
3362306a36Sopenharmony_ci	IGC_STAT("tx_multicast", stats.mptc),
3462306a36Sopenharmony_ci	IGC_STAT("multicast", stats.mprc),
3562306a36Sopenharmony_ci	IGC_STAT("collisions", stats.colc),
3662306a36Sopenharmony_ci	IGC_STAT("rx_crc_errors", stats.crcerrs),
3762306a36Sopenharmony_ci	IGC_STAT("rx_no_buffer_count", stats.rnbc),
3862306a36Sopenharmony_ci	IGC_STAT("rx_missed_errors", stats.mpc),
3962306a36Sopenharmony_ci	IGC_STAT("tx_aborted_errors", stats.ecol),
4062306a36Sopenharmony_ci	IGC_STAT("tx_carrier_errors", stats.tncrs),
4162306a36Sopenharmony_ci	IGC_STAT("tx_window_errors", stats.latecol),
4262306a36Sopenharmony_ci	IGC_STAT("tx_abort_late_coll", stats.latecol),
4362306a36Sopenharmony_ci	IGC_STAT("tx_deferred_ok", stats.dc),
4462306a36Sopenharmony_ci	IGC_STAT("tx_single_coll_ok", stats.scc),
4562306a36Sopenharmony_ci	IGC_STAT("tx_multi_coll_ok", stats.mcc),
4662306a36Sopenharmony_ci	IGC_STAT("tx_timeout_count", tx_timeout_count),
4762306a36Sopenharmony_ci	IGC_STAT("rx_long_length_errors", stats.roc),
4862306a36Sopenharmony_ci	IGC_STAT("rx_short_length_errors", stats.ruc),
4962306a36Sopenharmony_ci	IGC_STAT("rx_align_errors", stats.algnerrc),
5062306a36Sopenharmony_ci	IGC_STAT("tx_tcp_seg_good", stats.tsctc),
5162306a36Sopenharmony_ci	IGC_STAT("tx_tcp_seg_failed", stats.tsctfc),
5262306a36Sopenharmony_ci	IGC_STAT("rx_flow_control_xon", stats.xonrxc),
5362306a36Sopenharmony_ci	IGC_STAT("rx_flow_control_xoff", stats.xoffrxc),
5462306a36Sopenharmony_ci	IGC_STAT("tx_flow_control_xon", stats.xontxc),
5562306a36Sopenharmony_ci	IGC_STAT("tx_flow_control_xoff", stats.xofftxc),
5662306a36Sopenharmony_ci	IGC_STAT("rx_long_byte_count", stats.gorc),
5762306a36Sopenharmony_ci	IGC_STAT("tx_dma_out_of_sync", stats.doosync),
5862306a36Sopenharmony_ci	IGC_STAT("tx_smbus", stats.mgptc),
5962306a36Sopenharmony_ci	IGC_STAT("rx_smbus", stats.mgprc),
6062306a36Sopenharmony_ci	IGC_STAT("dropped_smbus", stats.mgpdc),
6162306a36Sopenharmony_ci	IGC_STAT("os2bmc_rx_by_bmc", stats.o2bgptc),
6262306a36Sopenharmony_ci	IGC_STAT("os2bmc_tx_by_bmc", stats.b2ospc),
6362306a36Sopenharmony_ci	IGC_STAT("os2bmc_tx_by_host", stats.o2bspc),
6462306a36Sopenharmony_ci	IGC_STAT("os2bmc_rx_by_host", stats.b2ogprc),
6562306a36Sopenharmony_ci	IGC_STAT("tx_hwtstamp_timeouts", tx_hwtstamp_timeouts),
6662306a36Sopenharmony_ci	IGC_STAT("tx_hwtstamp_skipped", tx_hwtstamp_skipped),
6762306a36Sopenharmony_ci	IGC_STAT("rx_hwtstamp_cleared", rx_hwtstamp_cleared),
6862306a36Sopenharmony_ci	IGC_STAT("tx_lpi_counter", stats.tlpic),
6962306a36Sopenharmony_ci	IGC_STAT("rx_lpi_counter", stats.rlpic),
7062306a36Sopenharmony_ci	IGC_STAT("qbv_config_change_errors", qbv_config_change_errors),
7162306a36Sopenharmony_ci};
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci#define IGC_NETDEV_STAT(_net_stat) { \
7462306a36Sopenharmony_ci	.stat_string = __stringify(_net_stat), \
7562306a36Sopenharmony_ci	.sizeof_stat = sizeof_field(struct rtnl_link_stats64, _net_stat), \
7662306a36Sopenharmony_ci	.stat_offset = offsetof(struct rtnl_link_stats64, _net_stat) \
7762306a36Sopenharmony_ci}
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_cistatic const struct igc_stats igc_gstrings_net_stats[] = {
8062306a36Sopenharmony_ci	IGC_NETDEV_STAT(rx_errors),
8162306a36Sopenharmony_ci	IGC_NETDEV_STAT(tx_errors),
8262306a36Sopenharmony_ci	IGC_NETDEV_STAT(tx_dropped),
8362306a36Sopenharmony_ci	IGC_NETDEV_STAT(rx_length_errors),
8462306a36Sopenharmony_ci	IGC_NETDEV_STAT(rx_over_errors),
8562306a36Sopenharmony_ci	IGC_NETDEV_STAT(rx_frame_errors),
8662306a36Sopenharmony_ci	IGC_NETDEV_STAT(rx_fifo_errors),
8762306a36Sopenharmony_ci	IGC_NETDEV_STAT(tx_fifo_errors),
8862306a36Sopenharmony_ci	IGC_NETDEV_STAT(tx_heartbeat_errors)
8962306a36Sopenharmony_ci};
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_cienum igc_diagnostics_results {
9262306a36Sopenharmony_ci	TEST_REG = 0,
9362306a36Sopenharmony_ci	TEST_EEP,
9462306a36Sopenharmony_ci	TEST_IRQ,
9562306a36Sopenharmony_ci	TEST_LOOP,
9662306a36Sopenharmony_ci	TEST_LINK
9762306a36Sopenharmony_ci};
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_cistatic const char igc_gstrings_test[][ETH_GSTRING_LEN] = {
10062306a36Sopenharmony_ci	[TEST_REG]  = "Register test  (offline)",
10162306a36Sopenharmony_ci	[TEST_EEP]  = "Eeprom test    (offline)",
10262306a36Sopenharmony_ci	[TEST_IRQ]  = "Interrupt test (offline)",
10362306a36Sopenharmony_ci	[TEST_LOOP] = "Loopback test  (offline)",
10462306a36Sopenharmony_ci	[TEST_LINK] = "Link test   (on/offline)"
10562306a36Sopenharmony_ci};
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci#define IGC_TEST_LEN (sizeof(igc_gstrings_test) / ETH_GSTRING_LEN)
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci#define IGC_GLOBAL_STATS_LEN	\
11062306a36Sopenharmony_ci	(sizeof(igc_gstrings_stats) / sizeof(struct igc_stats))
11162306a36Sopenharmony_ci#define IGC_NETDEV_STATS_LEN	\
11262306a36Sopenharmony_ci	(sizeof(igc_gstrings_net_stats) / sizeof(struct igc_stats))
11362306a36Sopenharmony_ci#define IGC_RX_QUEUE_STATS_LEN \
11462306a36Sopenharmony_ci	(sizeof(struct igc_rx_queue_stats) / sizeof(u64))
11562306a36Sopenharmony_ci#define IGC_TX_QUEUE_STATS_LEN 3 /* packets, bytes, restart_queue */
11662306a36Sopenharmony_ci#define IGC_QUEUE_STATS_LEN \
11762306a36Sopenharmony_ci	((((struct igc_adapter *)netdev_priv(netdev))->num_rx_queues * \
11862306a36Sopenharmony_ci	  IGC_RX_QUEUE_STATS_LEN) + \
11962306a36Sopenharmony_ci	 (((struct igc_adapter *)netdev_priv(netdev))->num_tx_queues * \
12062306a36Sopenharmony_ci	  IGC_TX_QUEUE_STATS_LEN))
12162306a36Sopenharmony_ci#define IGC_STATS_LEN \
12262306a36Sopenharmony_ci	(IGC_GLOBAL_STATS_LEN + IGC_NETDEV_STATS_LEN + IGC_QUEUE_STATS_LEN)
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_cistatic const char igc_priv_flags_strings[][ETH_GSTRING_LEN] = {
12562306a36Sopenharmony_ci#define IGC_PRIV_FLAGS_LEGACY_RX	BIT(0)
12662306a36Sopenharmony_ci	"legacy-rx",
12762306a36Sopenharmony_ci};
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci#define IGC_PRIV_FLAGS_STR_LEN ARRAY_SIZE(igc_priv_flags_strings)
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_cistatic void igc_ethtool_get_drvinfo(struct net_device *netdev,
13262306a36Sopenharmony_ci				    struct ethtool_drvinfo *drvinfo)
13362306a36Sopenharmony_ci{
13462306a36Sopenharmony_ci	struct igc_adapter *adapter = netdev_priv(netdev);
13562306a36Sopenharmony_ci	struct igc_hw *hw = &adapter->hw;
13662306a36Sopenharmony_ci	u16 nvm_version = 0;
13762306a36Sopenharmony_ci	u16 gphy_version;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	strscpy(drvinfo->driver, igc_driver_name, sizeof(drvinfo->driver));
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	/* NVM image version is reported as firmware version for i225 device */
14262306a36Sopenharmony_ci	hw->nvm.ops.read(hw, IGC_NVM_DEV_STARTER, 1, &nvm_version);
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	/* gPHY firmware version is reported as PHY FW version */
14562306a36Sopenharmony_ci	gphy_version = igc_read_phy_fw_version(hw);
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	scnprintf(adapter->fw_version,
14862306a36Sopenharmony_ci		  sizeof(adapter->fw_version),
14962306a36Sopenharmony_ci		  "%x:%x",
15062306a36Sopenharmony_ci		  nvm_version,
15162306a36Sopenharmony_ci		  gphy_version);
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	strscpy(drvinfo->fw_version, adapter->fw_version,
15462306a36Sopenharmony_ci		sizeof(drvinfo->fw_version));
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	strscpy(drvinfo->bus_info, pci_name(adapter->pdev),
15762306a36Sopenharmony_ci		sizeof(drvinfo->bus_info));
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	drvinfo->n_priv_flags = IGC_PRIV_FLAGS_STR_LEN;
16062306a36Sopenharmony_ci}
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_cistatic int igc_ethtool_get_regs_len(struct net_device *netdev)
16362306a36Sopenharmony_ci{
16462306a36Sopenharmony_ci	return IGC_REGS_LEN * sizeof(u32);
16562306a36Sopenharmony_ci}
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_cistatic void igc_ethtool_get_regs(struct net_device *netdev,
16862306a36Sopenharmony_ci				 struct ethtool_regs *regs, void *p)
16962306a36Sopenharmony_ci{
17062306a36Sopenharmony_ci	struct igc_adapter *adapter = netdev_priv(netdev);
17162306a36Sopenharmony_ci	struct igc_hw *hw = &adapter->hw;
17262306a36Sopenharmony_ci	u32 *regs_buff = p;
17362306a36Sopenharmony_ci	u8 i;
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	memset(p, 0, IGC_REGS_LEN * sizeof(u32));
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	regs->version = (2u << 24) | (hw->revision_id << 16) | hw->device_id;
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	/* General Registers */
18062306a36Sopenharmony_ci	regs_buff[0] = rd32(IGC_CTRL);
18162306a36Sopenharmony_ci	regs_buff[1] = rd32(IGC_STATUS);
18262306a36Sopenharmony_ci	regs_buff[2] = rd32(IGC_CTRL_EXT);
18362306a36Sopenharmony_ci	regs_buff[3] = rd32(IGC_MDIC);
18462306a36Sopenharmony_ci	regs_buff[4] = rd32(IGC_CONNSW);
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	/* NVM Register */
18762306a36Sopenharmony_ci	regs_buff[5] = rd32(IGC_EECD);
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	/* Interrupt */
19062306a36Sopenharmony_ci	/* Reading EICS for EICR because they read the
19162306a36Sopenharmony_ci	 * same but EICS does not clear on read
19262306a36Sopenharmony_ci	 */
19362306a36Sopenharmony_ci	regs_buff[6] = rd32(IGC_EICS);
19462306a36Sopenharmony_ci	regs_buff[7] = rd32(IGC_EICS);
19562306a36Sopenharmony_ci	regs_buff[8] = rd32(IGC_EIMS);
19662306a36Sopenharmony_ci	regs_buff[9] = rd32(IGC_EIMC);
19762306a36Sopenharmony_ci	regs_buff[10] = rd32(IGC_EIAC);
19862306a36Sopenharmony_ci	regs_buff[11] = rd32(IGC_EIAM);
19962306a36Sopenharmony_ci	/* Reading ICS for ICR because they read the
20062306a36Sopenharmony_ci	 * same but ICS does not clear on read
20162306a36Sopenharmony_ci	 */
20262306a36Sopenharmony_ci	regs_buff[12] = rd32(IGC_ICS);
20362306a36Sopenharmony_ci	regs_buff[13] = rd32(IGC_ICS);
20462306a36Sopenharmony_ci	regs_buff[14] = rd32(IGC_IMS);
20562306a36Sopenharmony_ci	regs_buff[15] = rd32(IGC_IMC);
20662306a36Sopenharmony_ci	regs_buff[16] = rd32(IGC_IAC);
20762306a36Sopenharmony_ci	regs_buff[17] = rd32(IGC_IAM);
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	/* Flow Control */
21062306a36Sopenharmony_ci	regs_buff[18] = rd32(IGC_FCAL);
21162306a36Sopenharmony_ci	regs_buff[19] = rd32(IGC_FCAH);
21262306a36Sopenharmony_ci	regs_buff[20] = rd32(IGC_FCTTV);
21362306a36Sopenharmony_ci	regs_buff[21] = rd32(IGC_FCRTL);
21462306a36Sopenharmony_ci	regs_buff[22] = rd32(IGC_FCRTH);
21562306a36Sopenharmony_ci	regs_buff[23] = rd32(IGC_FCRTV);
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	/* Receive */
21862306a36Sopenharmony_ci	regs_buff[24] = rd32(IGC_RCTL);
21962306a36Sopenharmony_ci	regs_buff[25] = rd32(IGC_RXCSUM);
22062306a36Sopenharmony_ci	regs_buff[26] = rd32(IGC_RLPML);
22162306a36Sopenharmony_ci	regs_buff[27] = rd32(IGC_RFCTL);
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	/* Transmit */
22462306a36Sopenharmony_ci	regs_buff[28] = rd32(IGC_TCTL);
22562306a36Sopenharmony_ci	regs_buff[29] = rd32(IGC_TIPG);
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	/* Wake Up */
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	/* MAC */
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	/* Statistics */
23262306a36Sopenharmony_ci	regs_buff[30] = adapter->stats.crcerrs;
23362306a36Sopenharmony_ci	regs_buff[31] = adapter->stats.algnerrc;
23462306a36Sopenharmony_ci	regs_buff[32] = adapter->stats.symerrs;
23562306a36Sopenharmony_ci	regs_buff[33] = adapter->stats.rxerrc;
23662306a36Sopenharmony_ci	regs_buff[34] = adapter->stats.mpc;
23762306a36Sopenharmony_ci	regs_buff[35] = adapter->stats.scc;
23862306a36Sopenharmony_ci	regs_buff[36] = adapter->stats.ecol;
23962306a36Sopenharmony_ci	regs_buff[37] = adapter->stats.mcc;
24062306a36Sopenharmony_ci	regs_buff[38] = adapter->stats.latecol;
24162306a36Sopenharmony_ci	regs_buff[39] = adapter->stats.colc;
24262306a36Sopenharmony_ci	regs_buff[40] = adapter->stats.dc;
24362306a36Sopenharmony_ci	regs_buff[41] = adapter->stats.tncrs;
24462306a36Sopenharmony_ci	regs_buff[42] = adapter->stats.sec;
24562306a36Sopenharmony_ci	regs_buff[43] = adapter->stats.htdpmc;
24662306a36Sopenharmony_ci	regs_buff[44] = adapter->stats.rlec;
24762306a36Sopenharmony_ci	regs_buff[45] = adapter->stats.xonrxc;
24862306a36Sopenharmony_ci	regs_buff[46] = adapter->stats.xontxc;
24962306a36Sopenharmony_ci	regs_buff[47] = adapter->stats.xoffrxc;
25062306a36Sopenharmony_ci	regs_buff[48] = adapter->stats.xofftxc;
25162306a36Sopenharmony_ci	regs_buff[49] = adapter->stats.fcruc;
25262306a36Sopenharmony_ci	regs_buff[50] = adapter->stats.prc64;
25362306a36Sopenharmony_ci	regs_buff[51] = adapter->stats.prc127;
25462306a36Sopenharmony_ci	regs_buff[52] = adapter->stats.prc255;
25562306a36Sopenharmony_ci	regs_buff[53] = adapter->stats.prc511;
25662306a36Sopenharmony_ci	regs_buff[54] = adapter->stats.prc1023;
25762306a36Sopenharmony_ci	regs_buff[55] = adapter->stats.prc1522;
25862306a36Sopenharmony_ci	regs_buff[56] = adapter->stats.gprc;
25962306a36Sopenharmony_ci	regs_buff[57] = adapter->stats.bprc;
26062306a36Sopenharmony_ci	regs_buff[58] = adapter->stats.mprc;
26162306a36Sopenharmony_ci	regs_buff[59] = adapter->stats.gptc;
26262306a36Sopenharmony_ci	regs_buff[60] = adapter->stats.gorc;
26362306a36Sopenharmony_ci	regs_buff[61] = adapter->stats.gotc;
26462306a36Sopenharmony_ci	regs_buff[62] = adapter->stats.rnbc;
26562306a36Sopenharmony_ci	regs_buff[63] = adapter->stats.ruc;
26662306a36Sopenharmony_ci	regs_buff[64] = adapter->stats.rfc;
26762306a36Sopenharmony_ci	regs_buff[65] = adapter->stats.roc;
26862306a36Sopenharmony_ci	regs_buff[66] = adapter->stats.rjc;
26962306a36Sopenharmony_ci	regs_buff[67] = adapter->stats.mgprc;
27062306a36Sopenharmony_ci	regs_buff[68] = adapter->stats.mgpdc;
27162306a36Sopenharmony_ci	regs_buff[69] = adapter->stats.mgptc;
27262306a36Sopenharmony_ci	regs_buff[70] = adapter->stats.tor;
27362306a36Sopenharmony_ci	regs_buff[71] = adapter->stats.tot;
27462306a36Sopenharmony_ci	regs_buff[72] = adapter->stats.tpr;
27562306a36Sopenharmony_ci	regs_buff[73] = adapter->stats.tpt;
27662306a36Sopenharmony_ci	regs_buff[74] = adapter->stats.ptc64;
27762306a36Sopenharmony_ci	regs_buff[75] = adapter->stats.ptc127;
27862306a36Sopenharmony_ci	regs_buff[76] = adapter->stats.ptc255;
27962306a36Sopenharmony_ci	regs_buff[77] = adapter->stats.ptc511;
28062306a36Sopenharmony_ci	regs_buff[78] = adapter->stats.ptc1023;
28162306a36Sopenharmony_ci	regs_buff[79] = adapter->stats.ptc1522;
28262306a36Sopenharmony_ci	regs_buff[80] = adapter->stats.mptc;
28362306a36Sopenharmony_ci	regs_buff[81] = adapter->stats.bptc;
28462306a36Sopenharmony_ci	regs_buff[82] = adapter->stats.tsctc;
28562306a36Sopenharmony_ci	regs_buff[83] = adapter->stats.iac;
28662306a36Sopenharmony_ci	regs_buff[84] = adapter->stats.rpthc;
28762306a36Sopenharmony_ci	regs_buff[85] = adapter->stats.hgptc;
28862306a36Sopenharmony_ci	regs_buff[86] = adapter->stats.hgorc;
28962306a36Sopenharmony_ci	regs_buff[87] = adapter->stats.hgotc;
29062306a36Sopenharmony_ci	regs_buff[88] = adapter->stats.lenerrs;
29162306a36Sopenharmony_ci	regs_buff[89] = adapter->stats.scvpc;
29262306a36Sopenharmony_ci	regs_buff[90] = adapter->stats.hrmpc;
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	for (i = 0; i < 4; i++)
29562306a36Sopenharmony_ci		regs_buff[91 + i] = rd32(IGC_SRRCTL(i));
29662306a36Sopenharmony_ci	for (i = 0; i < 4; i++)
29762306a36Sopenharmony_ci		regs_buff[95 + i] = rd32(IGC_PSRTYPE(i));
29862306a36Sopenharmony_ci	for (i = 0; i < 4; i++)
29962306a36Sopenharmony_ci		regs_buff[99 + i] = rd32(IGC_RDBAL(i));
30062306a36Sopenharmony_ci	for (i = 0; i < 4; i++)
30162306a36Sopenharmony_ci		regs_buff[103 + i] = rd32(IGC_RDBAH(i));
30262306a36Sopenharmony_ci	for (i = 0; i < 4; i++)
30362306a36Sopenharmony_ci		regs_buff[107 + i] = rd32(IGC_RDLEN(i));
30462306a36Sopenharmony_ci	for (i = 0; i < 4; i++)
30562306a36Sopenharmony_ci		regs_buff[111 + i] = rd32(IGC_RDH(i));
30662306a36Sopenharmony_ci	for (i = 0; i < 4; i++)
30762306a36Sopenharmony_ci		regs_buff[115 + i] = rd32(IGC_RDT(i));
30862306a36Sopenharmony_ci	for (i = 0; i < 4; i++)
30962306a36Sopenharmony_ci		regs_buff[119 + i] = rd32(IGC_RXDCTL(i));
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	for (i = 0; i < 10; i++)
31262306a36Sopenharmony_ci		regs_buff[123 + i] = rd32(IGC_EITR(i));
31362306a36Sopenharmony_ci	for (i = 0; i < 16; i++)
31462306a36Sopenharmony_ci		regs_buff[139 + i] = rd32(IGC_RAL(i));
31562306a36Sopenharmony_ci	for (i = 0; i < 16; i++)
31662306a36Sopenharmony_ci		regs_buff[145 + i] = rd32(IGC_RAH(i));
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	for (i = 0; i < 4; i++)
31962306a36Sopenharmony_ci		regs_buff[149 + i] = rd32(IGC_TDBAL(i));
32062306a36Sopenharmony_ci	for (i = 0; i < 4; i++)
32162306a36Sopenharmony_ci		regs_buff[152 + i] = rd32(IGC_TDBAH(i));
32262306a36Sopenharmony_ci	for (i = 0; i < 4; i++)
32362306a36Sopenharmony_ci		regs_buff[156 + i] = rd32(IGC_TDLEN(i));
32462306a36Sopenharmony_ci	for (i = 0; i < 4; i++)
32562306a36Sopenharmony_ci		regs_buff[160 + i] = rd32(IGC_TDH(i));
32662306a36Sopenharmony_ci	for (i = 0; i < 4; i++)
32762306a36Sopenharmony_ci		regs_buff[164 + i] = rd32(IGC_TDT(i));
32862306a36Sopenharmony_ci	for (i = 0; i < 4; i++)
32962306a36Sopenharmony_ci		regs_buff[168 + i] = rd32(IGC_TXDCTL(i));
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	/* XXX: Due to a bug few lines above, RAL and RAH registers are
33262306a36Sopenharmony_ci	 * overwritten. To preserve the ABI, we write these registers again in
33362306a36Sopenharmony_ci	 * regs_buff.
33462306a36Sopenharmony_ci	 */
33562306a36Sopenharmony_ci	for (i = 0; i < 16; i++)
33662306a36Sopenharmony_ci		regs_buff[172 + i] = rd32(IGC_RAL(i));
33762306a36Sopenharmony_ci	for (i = 0; i < 16; i++)
33862306a36Sopenharmony_ci		regs_buff[188 + i] = rd32(IGC_RAH(i));
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	regs_buff[204] = rd32(IGC_VLANPQF);
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	for (i = 0; i < 8; i++)
34362306a36Sopenharmony_ci		regs_buff[205 + i] = rd32(IGC_ETQF(i));
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	regs_buff[213] = adapter->stats.tlpic;
34662306a36Sopenharmony_ci	regs_buff[214] = adapter->stats.rlpic;
34762306a36Sopenharmony_ci}
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_cistatic void igc_ethtool_get_wol(struct net_device *netdev,
35062306a36Sopenharmony_ci				struct ethtool_wolinfo *wol)
35162306a36Sopenharmony_ci{
35262306a36Sopenharmony_ci	struct igc_adapter *adapter = netdev_priv(netdev);
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	wol->wolopts = 0;
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	if (!(adapter->flags & IGC_FLAG_WOL_SUPPORTED))
35762306a36Sopenharmony_ci		return;
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	wol->supported = WAKE_UCAST | WAKE_MCAST |
36062306a36Sopenharmony_ci			 WAKE_BCAST | WAKE_MAGIC |
36162306a36Sopenharmony_ci			 WAKE_PHY;
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	/* apply any specific unsupported masks here */
36462306a36Sopenharmony_ci	switch (adapter->hw.device_id) {
36562306a36Sopenharmony_ci	default:
36662306a36Sopenharmony_ci		break;
36762306a36Sopenharmony_ci	}
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	if (adapter->wol & IGC_WUFC_EX)
37062306a36Sopenharmony_ci		wol->wolopts |= WAKE_UCAST;
37162306a36Sopenharmony_ci	if (adapter->wol & IGC_WUFC_MC)
37262306a36Sopenharmony_ci		wol->wolopts |= WAKE_MCAST;
37362306a36Sopenharmony_ci	if (adapter->wol & IGC_WUFC_BC)
37462306a36Sopenharmony_ci		wol->wolopts |= WAKE_BCAST;
37562306a36Sopenharmony_ci	if (adapter->wol & IGC_WUFC_MAG)
37662306a36Sopenharmony_ci		wol->wolopts |= WAKE_MAGIC;
37762306a36Sopenharmony_ci	if (adapter->wol & IGC_WUFC_LNKC)
37862306a36Sopenharmony_ci		wol->wolopts |= WAKE_PHY;
37962306a36Sopenharmony_ci}
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_cistatic int igc_ethtool_set_wol(struct net_device *netdev,
38262306a36Sopenharmony_ci			       struct ethtool_wolinfo *wol)
38362306a36Sopenharmony_ci{
38462306a36Sopenharmony_ci	struct igc_adapter *adapter = netdev_priv(netdev);
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	if (wol->wolopts & (WAKE_ARP | WAKE_MAGICSECURE | WAKE_FILTER))
38762306a36Sopenharmony_ci		return -EOPNOTSUPP;
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	if (!(adapter->flags & IGC_FLAG_WOL_SUPPORTED))
39062306a36Sopenharmony_ci		return wol->wolopts ? -EOPNOTSUPP : 0;
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	/* these settings will always override what we currently have */
39362306a36Sopenharmony_ci	adapter->wol = 0;
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	if (wol->wolopts & WAKE_UCAST)
39662306a36Sopenharmony_ci		adapter->wol |= IGC_WUFC_EX;
39762306a36Sopenharmony_ci	if (wol->wolopts & WAKE_MCAST)
39862306a36Sopenharmony_ci		adapter->wol |= IGC_WUFC_MC;
39962306a36Sopenharmony_ci	if (wol->wolopts & WAKE_BCAST)
40062306a36Sopenharmony_ci		adapter->wol |= IGC_WUFC_BC;
40162306a36Sopenharmony_ci	if (wol->wolopts & WAKE_MAGIC)
40262306a36Sopenharmony_ci		adapter->wol |= IGC_WUFC_MAG;
40362306a36Sopenharmony_ci	if (wol->wolopts & WAKE_PHY)
40462306a36Sopenharmony_ci		adapter->wol |= IGC_WUFC_LNKC;
40562306a36Sopenharmony_ci	device_set_wakeup_enable(&adapter->pdev->dev, adapter->wol);
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	return 0;
40862306a36Sopenharmony_ci}
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_cistatic u32 igc_ethtool_get_msglevel(struct net_device *netdev)
41162306a36Sopenharmony_ci{
41262306a36Sopenharmony_ci	struct igc_adapter *adapter = netdev_priv(netdev);
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	return adapter->msg_enable;
41562306a36Sopenharmony_ci}
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_cistatic void igc_ethtool_set_msglevel(struct net_device *netdev, u32 data)
41862306a36Sopenharmony_ci{
41962306a36Sopenharmony_ci	struct igc_adapter *adapter = netdev_priv(netdev);
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	adapter->msg_enable = data;
42262306a36Sopenharmony_ci}
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_cistatic int igc_ethtool_nway_reset(struct net_device *netdev)
42562306a36Sopenharmony_ci{
42662306a36Sopenharmony_ci	struct igc_adapter *adapter = netdev_priv(netdev);
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	if (netif_running(netdev))
42962306a36Sopenharmony_ci		igc_reinit_locked(adapter);
43062306a36Sopenharmony_ci	return 0;
43162306a36Sopenharmony_ci}
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_cistatic u32 igc_ethtool_get_link(struct net_device *netdev)
43462306a36Sopenharmony_ci{
43562306a36Sopenharmony_ci	struct igc_adapter *adapter = netdev_priv(netdev);
43662306a36Sopenharmony_ci	struct igc_mac_info *mac = &adapter->hw.mac;
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	/* If the link is not reported up to netdev, interrupts are disabled,
43962306a36Sopenharmony_ci	 * and so the physical link state may have changed since we last
44062306a36Sopenharmony_ci	 * looked. Set get_link_status to make sure that the true link
44162306a36Sopenharmony_ci	 * state is interrogated, rather than pulling a cached and possibly
44262306a36Sopenharmony_ci	 * stale link state from the driver.
44362306a36Sopenharmony_ci	 */
44462306a36Sopenharmony_ci	if (!netif_carrier_ok(netdev))
44562306a36Sopenharmony_ci		mac->get_link_status = 1;
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	return igc_has_link(adapter);
44862306a36Sopenharmony_ci}
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_cistatic int igc_ethtool_get_eeprom_len(struct net_device *netdev)
45162306a36Sopenharmony_ci{
45262306a36Sopenharmony_ci	struct igc_adapter *adapter = netdev_priv(netdev);
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	return adapter->hw.nvm.word_size * 2;
45562306a36Sopenharmony_ci}
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_cistatic int igc_ethtool_get_eeprom(struct net_device *netdev,
45862306a36Sopenharmony_ci				  struct ethtool_eeprom *eeprom, u8 *bytes)
45962306a36Sopenharmony_ci{
46062306a36Sopenharmony_ci	struct igc_adapter *adapter = netdev_priv(netdev);
46162306a36Sopenharmony_ci	struct igc_hw *hw = &adapter->hw;
46262306a36Sopenharmony_ci	int first_word, last_word;
46362306a36Sopenharmony_ci	u16 *eeprom_buff;
46462306a36Sopenharmony_ci	int ret_val = 0;
46562306a36Sopenharmony_ci	u16 i;
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	if (eeprom->len == 0)
46862306a36Sopenharmony_ci		return -EINVAL;
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	eeprom->magic = hw->vendor_id | (hw->device_id << 16);
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	first_word = eeprom->offset >> 1;
47362306a36Sopenharmony_ci	last_word = (eeprom->offset + eeprom->len - 1) >> 1;
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	eeprom_buff = kmalloc_array(last_word - first_word + 1, sizeof(u16),
47662306a36Sopenharmony_ci				    GFP_KERNEL);
47762306a36Sopenharmony_ci	if (!eeprom_buff)
47862306a36Sopenharmony_ci		return -ENOMEM;
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	if (hw->nvm.type == igc_nvm_eeprom_spi) {
48162306a36Sopenharmony_ci		ret_val = hw->nvm.ops.read(hw, first_word,
48262306a36Sopenharmony_ci					   last_word - first_word + 1,
48362306a36Sopenharmony_ci					   eeprom_buff);
48462306a36Sopenharmony_ci	} else {
48562306a36Sopenharmony_ci		for (i = 0; i < last_word - first_word + 1; i++) {
48662306a36Sopenharmony_ci			ret_val = hw->nvm.ops.read(hw, first_word + i, 1,
48762306a36Sopenharmony_ci						   &eeprom_buff[i]);
48862306a36Sopenharmony_ci			if (ret_val)
48962306a36Sopenharmony_ci				break;
49062306a36Sopenharmony_ci		}
49162306a36Sopenharmony_ci	}
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	/* Device's eeprom is always little-endian, word addressable */
49462306a36Sopenharmony_ci	for (i = 0; i < last_word - first_word + 1; i++)
49562306a36Sopenharmony_ci		le16_to_cpus(&eeprom_buff[i]);
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	memcpy(bytes, (u8 *)eeprom_buff + (eeprom->offset & 1),
49862306a36Sopenharmony_ci	       eeprom->len);
49962306a36Sopenharmony_ci	kfree(eeprom_buff);
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci	return ret_val;
50262306a36Sopenharmony_ci}
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_cistatic int igc_ethtool_set_eeprom(struct net_device *netdev,
50562306a36Sopenharmony_ci				  struct ethtool_eeprom *eeprom, u8 *bytes)
50662306a36Sopenharmony_ci{
50762306a36Sopenharmony_ci	struct igc_adapter *adapter = netdev_priv(netdev);
50862306a36Sopenharmony_ci	struct igc_hw *hw = &adapter->hw;
50962306a36Sopenharmony_ci	int max_len, first_word, last_word, ret_val = 0;
51062306a36Sopenharmony_ci	u16 *eeprom_buff;
51162306a36Sopenharmony_ci	void *ptr;
51262306a36Sopenharmony_ci	u16 i;
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	if (eeprom->len == 0)
51562306a36Sopenharmony_ci		return -EOPNOTSUPP;
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	if (hw->mac.type >= igc_i225 &&
51862306a36Sopenharmony_ci	    !igc_get_flash_presence_i225(hw)) {
51962306a36Sopenharmony_ci		return -EOPNOTSUPP;
52062306a36Sopenharmony_ci	}
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	if (eeprom->magic != (hw->vendor_id | (hw->device_id << 16)))
52362306a36Sopenharmony_ci		return -EFAULT;
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	max_len = hw->nvm.word_size * 2;
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	first_word = eeprom->offset >> 1;
52862306a36Sopenharmony_ci	last_word = (eeprom->offset + eeprom->len - 1) >> 1;
52962306a36Sopenharmony_ci	eeprom_buff = kmalloc(max_len, GFP_KERNEL);
53062306a36Sopenharmony_ci	if (!eeprom_buff)
53162306a36Sopenharmony_ci		return -ENOMEM;
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci	ptr = (void *)eeprom_buff;
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	if (eeprom->offset & 1) {
53662306a36Sopenharmony_ci		/* need read/modify/write of first changed EEPROM word
53762306a36Sopenharmony_ci		 * only the second byte of the word is being modified
53862306a36Sopenharmony_ci		 */
53962306a36Sopenharmony_ci		ret_val = hw->nvm.ops.read(hw, first_word, 1,
54062306a36Sopenharmony_ci					    &eeprom_buff[0]);
54162306a36Sopenharmony_ci		ptr++;
54262306a36Sopenharmony_ci	}
54362306a36Sopenharmony_ci	if (((eeprom->offset + eeprom->len) & 1) && ret_val == 0) {
54462306a36Sopenharmony_ci		/* need read/modify/write of last changed EEPROM word
54562306a36Sopenharmony_ci		 * only the first byte of the word is being modified
54662306a36Sopenharmony_ci		 */
54762306a36Sopenharmony_ci		ret_val = hw->nvm.ops.read(hw, last_word, 1,
54862306a36Sopenharmony_ci				   &eeprom_buff[last_word - first_word]);
54962306a36Sopenharmony_ci	}
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci	/* Device's eeprom is always little-endian, word addressable */
55262306a36Sopenharmony_ci	for (i = 0; i < last_word - first_word + 1; i++)
55362306a36Sopenharmony_ci		le16_to_cpus(&eeprom_buff[i]);
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	memcpy(ptr, bytes, eeprom->len);
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	for (i = 0; i < last_word - first_word + 1; i++)
55862306a36Sopenharmony_ci		cpu_to_le16s(&eeprom_buff[i]);
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	ret_val = hw->nvm.ops.write(hw, first_word,
56162306a36Sopenharmony_ci				    last_word - first_word + 1, eeprom_buff);
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci	/* Update the checksum if nvm write succeeded */
56462306a36Sopenharmony_ci	if (ret_val == 0)
56562306a36Sopenharmony_ci		hw->nvm.ops.update(hw);
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	kfree(eeprom_buff);
56862306a36Sopenharmony_ci	return ret_val;
56962306a36Sopenharmony_ci}
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_cistatic void
57262306a36Sopenharmony_ciigc_ethtool_get_ringparam(struct net_device *netdev,
57362306a36Sopenharmony_ci			  struct ethtool_ringparam *ring,
57462306a36Sopenharmony_ci			  struct kernel_ethtool_ringparam *kernel_ering,
57562306a36Sopenharmony_ci			  struct netlink_ext_ack *extack)
57662306a36Sopenharmony_ci{
57762306a36Sopenharmony_ci	struct igc_adapter *adapter = netdev_priv(netdev);
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	ring->rx_max_pending = IGC_MAX_RXD;
58062306a36Sopenharmony_ci	ring->tx_max_pending = IGC_MAX_TXD;
58162306a36Sopenharmony_ci	ring->rx_pending = adapter->rx_ring_count;
58262306a36Sopenharmony_ci	ring->tx_pending = adapter->tx_ring_count;
58362306a36Sopenharmony_ci}
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_cistatic int
58662306a36Sopenharmony_ciigc_ethtool_set_ringparam(struct net_device *netdev,
58762306a36Sopenharmony_ci			  struct ethtool_ringparam *ring,
58862306a36Sopenharmony_ci			  struct kernel_ethtool_ringparam *kernel_ering,
58962306a36Sopenharmony_ci			  struct netlink_ext_ack *extack)
59062306a36Sopenharmony_ci{
59162306a36Sopenharmony_ci	struct igc_adapter *adapter = netdev_priv(netdev);
59262306a36Sopenharmony_ci	struct igc_ring *temp_ring;
59362306a36Sopenharmony_ci	u16 new_rx_count, new_tx_count;
59462306a36Sopenharmony_ci	int i, err = 0;
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci	if (ring->rx_mini_pending || ring->rx_jumbo_pending)
59762306a36Sopenharmony_ci		return -EINVAL;
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci	new_rx_count = min_t(u32, ring->rx_pending, IGC_MAX_RXD);
60062306a36Sopenharmony_ci	new_rx_count = max_t(u16, new_rx_count, IGC_MIN_RXD);
60162306a36Sopenharmony_ci	new_rx_count = ALIGN(new_rx_count, REQ_RX_DESCRIPTOR_MULTIPLE);
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci	new_tx_count = min_t(u32, ring->tx_pending, IGC_MAX_TXD);
60462306a36Sopenharmony_ci	new_tx_count = max_t(u16, new_tx_count, IGC_MIN_TXD);
60562306a36Sopenharmony_ci	new_tx_count = ALIGN(new_tx_count, REQ_TX_DESCRIPTOR_MULTIPLE);
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci	if (new_tx_count == adapter->tx_ring_count &&
60862306a36Sopenharmony_ci	    new_rx_count == adapter->rx_ring_count) {
60962306a36Sopenharmony_ci		/* nothing to do */
61062306a36Sopenharmony_ci		return 0;
61162306a36Sopenharmony_ci	}
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci	while (test_and_set_bit(__IGC_RESETTING, &adapter->state))
61462306a36Sopenharmony_ci		usleep_range(1000, 2000);
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	if (!netif_running(adapter->netdev)) {
61762306a36Sopenharmony_ci		for (i = 0; i < adapter->num_tx_queues; i++)
61862306a36Sopenharmony_ci			adapter->tx_ring[i]->count = new_tx_count;
61962306a36Sopenharmony_ci		for (i = 0; i < adapter->num_rx_queues; i++)
62062306a36Sopenharmony_ci			adapter->rx_ring[i]->count = new_rx_count;
62162306a36Sopenharmony_ci		adapter->tx_ring_count = new_tx_count;
62262306a36Sopenharmony_ci		adapter->rx_ring_count = new_rx_count;
62362306a36Sopenharmony_ci		goto clear_reset;
62462306a36Sopenharmony_ci	}
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci	if (adapter->num_tx_queues > adapter->num_rx_queues)
62762306a36Sopenharmony_ci		temp_ring = vmalloc(array_size(sizeof(struct igc_ring),
62862306a36Sopenharmony_ci					       adapter->num_tx_queues));
62962306a36Sopenharmony_ci	else
63062306a36Sopenharmony_ci		temp_ring = vmalloc(array_size(sizeof(struct igc_ring),
63162306a36Sopenharmony_ci					       adapter->num_rx_queues));
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci	if (!temp_ring) {
63462306a36Sopenharmony_ci		err = -ENOMEM;
63562306a36Sopenharmony_ci		goto clear_reset;
63662306a36Sopenharmony_ci	}
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci	igc_down(adapter);
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci	/* We can't just free everything and then setup again,
64162306a36Sopenharmony_ci	 * because the ISRs in MSI-X mode get passed pointers
64262306a36Sopenharmony_ci	 * to the Tx and Rx ring structs.
64362306a36Sopenharmony_ci	 */
64462306a36Sopenharmony_ci	if (new_tx_count != adapter->tx_ring_count) {
64562306a36Sopenharmony_ci		for (i = 0; i < adapter->num_tx_queues; i++) {
64662306a36Sopenharmony_ci			memcpy(&temp_ring[i], adapter->tx_ring[i],
64762306a36Sopenharmony_ci			       sizeof(struct igc_ring));
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci			temp_ring[i].count = new_tx_count;
65062306a36Sopenharmony_ci			err = igc_setup_tx_resources(&temp_ring[i]);
65162306a36Sopenharmony_ci			if (err) {
65262306a36Sopenharmony_ci				while (i) {
65362306a36Sopenharmony_ci					i--;
65462306a36Sopenharmony_ci					igc_free_tx_resources(&temp_ring[i]);
65562306a36Sopenharmony_ci				}
65662306a36Sopenharmony_ci				goto err_setup;
65762306a36Sopenharmony_ci			}
65862306a36Sopenharmony_ci		}
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci		for (i = 0; i < adapter->num_tx_queues; i++) {
66162306a36Sopenharmony_ci			igc_free_tx_resources(adapter->tx_ring[i]);
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci			memcpy(adapter->tx_ring[i], &temp_ring[i],
66462306a36Sopenharmony_ci			       sizeof(struct igc_ring));
66562306a36Sopenharmony_ci		}
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci		adapter->tx_ring_count = new_tx_count;
66862306a36Sopenharmony_ci	}
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ci	if (new_rx_count != adapter->rx_ring_count) {
67162306a36Sopenharmony_ci		for (i = 0; i < adapter->num_rx_queues; i++) {
67262306a36Sopenharmony_ci			memcpy(&temp_ring[i], adapter->rx_ring[i],
67362306a36Sopenharmony_ci			       sizeof(struct igc_ring));
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci			temp_ring[i].count = new_rx_count;
67662306a36Sopenharmony_ci			err = igc_setup_rx_resources(&temp_ring[i]);
67762306a36Sopenharmony_ci			if (err) {
67862306a36Sopenharmony_ci				while (i) {
67962306a36Sopenharmony_ci					i--;
68062306a36Sopenharmony_ci					igc_free_rx_resources(&temp_ring[i]);
68162306a36Sopenharmony_ci				}
68262306a36Sopenharmony_ci				goto err_setup;
68362306a36Sopenharmony_ci			}
68462306a36Sopenharmony_ci		}
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci		for (i = 0; i < adapter->num_rx_queues; i++) {
68762306a36Sopenharmony_ci			igc_free_rx_resources(adapter->rx_ring[i]);
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci			memcpy(adapter->rx_ring[i], &temp_ring[i],
69062306a36Sopenharmony_ci			       sizeof(struct igc_ring));
69162306a36Sopenharmony_ci		}
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci		adapter->rx_ring_count = new_rx_count;
69462306a36Sopenharmony_ci	}
69562306a36Sopenharmony_cierr_setup:
69662306a36Sopenharmony_ci	igc_up(adapter);
69762306a36Sopenharmony_ci	vfree(temp_ring);
69862306a36Sopenharmony_ciclear_reset:
69962306a36Sopenharmony_ci	clear_bit(__IGC_RESETTING, &adapter->state);
70062306a36Sopenharmony_ci	return err;
70162306a36Sopenharmony_ci}
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_cistatic void igc_ethtool_get_pauseparam(struct net_device *netdev,
70462306a36Sopenharmony_ci				       struct ethtool_pauseparam *pause)
70562306a36Sopenharmony_ci{
70662306a36Sopenharmony_ci	struct igc_adapter *adapter = netdev_priv(netdev);
70762306a36Sopenharmony_ci	struct igc_hw *hw = &adapter->hw;
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci	pause->autoneg =
71062306a36Sopenharmony_ci		(adapter->fc_autoneg ? AUTONEG_ENABLE : AUTONEG_DISABLE);
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci	if (hw->fc.current_mode == igc_fc_rx_pause) {
71362306a36Sopenharmony_ci		pause->rx_pause = 1;
71462306a36Sopenharmony_ci	} else if (hw->fc.current_mode == igc_fc_tx_pause) {
71562306a36Sopenharmony_ci		pause->tx_pause = 1;
71662306a36Sopenharmony_ci	} else if (hw->fc.current_mode == igc_fc_full) {
71762306a36Sopenharmony_ci		pause->rx_pause = 1;
71862306a36Sopenharmony_ci		pause->tx_pause = 1;
71962306a36Sopenharmony_ci	}
72062306a36Sopenharmony_ci}
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_cistatic int igc_ethtool_set_pauseparam(struct net_device *netdev,
72362306a36Sopenharmony_ci				      struct ethtool_pauseparam *pause)
72462306a36Sopenharmony_ci{
72562306a36Sopenharmony_ci	struct igc_adapter *adapter = netdev_priv(netdev);
72662306a36Sopenharmony_ci	struct igc_hw *hw = &adapter->hw;
72762306a36Sopenharmony_ci	int retval = 0;
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci	adapter->fc_autoneg = pause->autoneg;
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci	while (test_and_set_bit(__IGC_RESETTING, &adapter->state))
73262306a36Sopenharmony_ci		usleep_range(1000, 2000);
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ci	if (adapter->fc_autoneg == AUTONEG_ENABLE) {
73562306a36Sopenharmony_ci		hw->fc.requested_mode = igc_fc_default;
73662306a36Sopenharmony_ci		if (netif_running(adapter->netdev)) {
73762306a36Sopenharmony_ci			igc_down(adapter);
73862306a36Sopenharmony_ci			igc_up(adapter);
73962306a36Sopenharmony_ci		} else {
74062306a36Sopenharmony_ci			igc_reset(adapter);
74162306a36Sopenharmony_ci		}
74262306a36Sopenharmony_ci	} else {
74362306a36Sopenharmony_ci		if (pause->rx_pause && pause->tx_pause)
74462306a36Sopenharmony_ci			hw->fc.requested_mode = igc_fc_full;
74562306a36Sopenharmony_ci		else if (pause->rx_pause && !pause->tx_pause)
74662306a36Sopenharmony_ci			hw->fc.requested_mode = igc_fc_rx_pause;
74762306a36Sopenharmony_ci		else if (!pause->rx_pause && pause->tx_pause)
74862306a36Sopenharmony_ci			hw->fc.requested_mode = igc_fc_tx_pause;
74962306a36Sopenharmony_ci		else if (!pause->rx_pause && !pause->tx_pause)
75062306a36Sopenharmony_ci			hw->fc.requested_mode = igc_fc_none;
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ci		hw->fc.current_mode = hw->fc.requested_mode;
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci		retval = ((hw->phy.media_type == igc_media_type_copper) ?
75562306a36Sopenharmony_ci			  igc_force_mac_fc(hw) : igc_setup_link(hw));
75662306a36Sopenharmony_ci	}
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci	clear_bit(__IGC_RESETTING, &adapter->state);
75962306a36Sopenharmony_ci	return retval;
76062306a36Sopenharmony_ci}
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_cistatic void igc_ethtool_get_strings(struct net_device *netdev, u32 stringset,
76362306a36Sopenharmony_ci				    u8 *data)
76462306a36Sopenharmony_ci{
76562306a36Sopenharmony_ci	struct igc_adapter *adapter = netdev_priv(netdev);
76662306a36Sopenharmony_ci	u8 *p = data;
76762306a36Sopenharmony_ci	int i;
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci	switch (stringset) {
77062306a36Sopenharmony_ci	case ETH_SS_TEST:
77162306a36Sopenharmony_ci		memcpy(data, *igc_gstrings_test,
77262306a36Sopenharmony_ci		       IGC_TEST_LEN * ETH_GSTRING_LEN);
77362306a36Sopenharmony_ci		break;
77462306a36Sopenharmony_ci	case ETH_SS_STATS:
77562306a36Sopenharmony_ci		for (i = 0; i < IGC_GLOBAL_STATS_LEN; i++)
77662306a36Sopenharmony_ci			ethtool_sprintf(&p, igc_gstrings_stats[i].stat_string);
77762306a36Sopenharmony_ci		for (i = 0; i < IGC_NETDEV_STATS_LEN; i++)
77862306a36Sopenharmony_ci			ethtool_sprintf(&p,
77962306a36Sopenharmony_ci					igc_gstrings_net_stats[i].stat_string);
78062306a36Sopenharmony_ci		for (i = 0; i < adapter->num_tx_queues; i++) {
78162306a36Sopenharmony_ci			ethtool_sprintf(&p, "tx_queue_%u_packets", i);
78262306a36Sopenharmony_ci			ethtool_sprintf(&p, "tx_queue_%u_bytes", i);
78362306a36Sopenharmony_ci			ethtool_sprintf(&p, "tx_queue_%u_restart", i);
78462306a36Sopenharmony_ci		}
78562306a36Sopenharmony_ci		for (i = 0; i < adapter->num_rx_queues; i++) {
78662306a36Sopenharmony_ci			ethtool_sprintf(&p, "rx_queue_%u_packets", i);
78762306a36Sopenharmony_ci			ethtool_sprintf(&p, "rx_queue_%u_bytes", i);
78862306a36Sopenharmony_ci			ethtool_sprintf(&p, "rx_queue_%u_drops", i);
78962306a36Sopenharmony_ci			ethtool_sprintf(&p, "rx_queue_%u_csum_err", i);
79062306a36Sopenharmony_ci			ethtool_sprintf(&p, "rx_queue_%u_alloc_failed", i);
79162306a36Sopenharmony_ci		}
79262306a36Sopenharmony_ci		/* BUG_ON(p - data != IGC_STATS_LEN * ETH_GSTRING_LEN); */
79362306a36Sopenharmony_ci		break;
79462306a36Sopenharmony_ci	case ETH_SS_PRIV_FLAGS:
79562306a36Sopenharmony_ci		memcpy(data, igc_priv_flags_strings,
79662306a36Sopenharmony_ci		       IGC_PRIV_FLAGS_STR_LEN * ETH_GSTRING_LEN);
79762306a36Sopenharmony_ci		break;
79862306a36Sopenharmony_ci	}
79962306a36Sopenharmony_ci}
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_cistatic int igc_ethtool_get_sset_count(struct net_device *netdev, int sset)
80262306a36Sopenharmony_ci{
80362306a36Sopenharmony_ci	switch (sset) {
80462306a36Sopenharmony_ci	case ETH_SS_STATS:
80562306a36Sopenharmony_ci		return IGC_STATS_LEN;
80662306a36Sopenharmony_ci	case ETH_SS_TEST:
80762306a36Sopenharmony_ci		return IGC_TEST_LEN;
80862306a36Sopenharmony_ci	case ETH_SS_PRIV_FLAGS:
80962306a36Sopenharmony_ci		return IGC_PRIV_FLAGS_STR_LEN;
81062306a36Sopenharmony_ci	default:
81162306a36Sopenharmony_ci		return -ENOTSUPP;
81262306a36Sopenharmony_ci	}
81362306a36Sopenharmony_ci}
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_cistatic void igc_ethtool_get_stats(struct net_device *netdev,
81662306a36Sopenharmony_ci				  struct ethtool_stats *stats, u64 *data)
81762306a36Sopenharmony_ci{
81862306a36Sopenharmony_ci	struct igc_adapter *adapter = netdev_priv(netdev);
81962306a36Sopenharmony_ci	struct rtnl_link_stats64 *net_stats = &adapter->stats64;
82062306a36Sopenharmony_ci	unsigned int start;
82162306a36Sopenharmony_ci	struct igc_ring *ring;
82262306a36Sopenharmony_ci	int i, j;
82362306a36Sopenharmony_ci	char *p;
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci	spin_lock(&adapter->stats64_lock);
82662306a36Sopenharmony_ci	igc_update_stats(adapter);
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_ci	for (i = 0; i < IGC_GLOBAL_STATS_LEN; i++) {
82962306a36Sopenharmony_ci		p = (char *)adapter + igc_gstrings_stats[i].stat_offset;
83062306a36Sopenharmony_ci		data[i] = (igc_gstrings_stats[i].sizeof_stat ==
83162306a36Sopenharmony_ci			sizeof(u64)) ? *(u64 *)p : *(u32 *)p;
83262306a36Sopenharmony_ci	}
83362306a36Sopenharmony_ci	for (j = 0; j < IGC_NETDEV_STATS_LEN; j++, i++) {
83462306a36Sopenharmony_ci		p = (char *)net_stats + igc_gstrings_net_stats[j].stat_offset;
83562306a36Sopenharmony_ci		data[i] = (igc_gstrings_net_stats[j].sizeof_stat ==
83662306a36Sopenharmony_ci			sizeof(u64)) ? *(u64 *)p : *(u32 *)p;
83762306a36Sopenharmony_ci	}
83862306a36Sopenharmony_ci	for (j = 0; j < adapter->num_tx_queues; j++) {
83962306a36Sopenharmony_ci		u64	restart2;
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_ci		ring = adapter->tx_ring[j];
84262306a36Sopenharmony_ci		do {
84362306a36Sopenharmony_ci			start = u64_stats_fetch_begin(&ring->tx_syncp);
84462306a36Sopenharmony_ci			data[i]   = ring->tx_stats.packets;
84562306a36Sopenharmony_ci			data[i + 1] = ring->tx_stats.bytes;
84662306a36Sopenharmony_ci			data[i + 2] = ring->tx_stats.restart_queue;
84762306a36Sopenharmony_ci		} while (u64_stats_fetch_retry(&ring->tx_syncp, start));
84862306a36Sopenharmony_ci		do {
84962306a36Sopenharmony_ci			start = u64_stats_fetch_begin(&ring->tx_syncp2);
85062306a36Sopenharmony_ci			restart2  = ring->tx_stats.restart_queue2;
85162306a36Sopenharmony_ci		} while (u64_stats_fetch_retry(&ring->tx_syncp2, start));
85262306a36Sopenharmony_ci		data[i + 2] += restart2;
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci		i += IGC_TX_QUEUE_STATS_LEN;
85562306a36Sopenharmony_ci	}
85662306a36Sopenharmony_ci	for (j = 0; j < adapter->num_rx_queues; j++) {
85762306a36Sopenharmony_ci		ring = adapter->rx_ring[j];
85862306a36Sopenharmony_ci		do {
85962306a36Sopenharmony_ci			start = u64_stats_fetch_begin(&ring->rx_syncp);
86062306a36Sopenharmony_ci			data[i]   = ring->rx_stats.packets;
86162306a36Sopenharmony_ci			data[i + 1] = ring->rx_stats.bytes;
86262306a36Sopenharmony_ci			data[i + 2] = ring->rx_stats.drops;
86362306a36Sopenharmony_ci			data[i + 3] = ring->rx_stats.csum_err;
86462306a36Sopenharmony_ci			data[i + 4] = ring->rx_stats.alloc_failed;
86562306a36Sopenharmony_ci		} while (u64_stats_fetch_retry(&ring->rx_syncp, start));
86662306a36Sopenharmony_ci		i += IGC_RX_QUEUE_STATS_LEN;
86762306a36Sopenharmony_ci	}
86862306a36Sopenharmony_ci	spin_unlock(&adapter->stats64_lock);
86962306a36Sopenharmony_ci}
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_cistatic int igc_ethtool_get_previous_rx_coalesce(struct igc_adapter *adapter)
87262306a36Sopenharmony_ci{
87362306a36Sopenharmony_ci	return (adapter->rx_itr_setting <= 3) ?
87462306a36Sopenharmony_ci		adapter->rx_itr_setting : adapter->rx_itr_setting >> 2;
87562306a36Sopenharmony_ci}
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_cistatic int igc_ethtool_get_previous_tx_coalesce(struct igc_adapter *adapter)
87862306a36Sopenharmony_ci{
87962306a36Sopenharmony_ci	return (adapter->tx_itr_setting <= 3) ?
88062306a36Sopenharmony_ci		adapter->tx_itr_setting : adapter->tx_itr_setting >> 2;
88162306a36Sopenharmony_ci}
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_cistatic int igc_ethtool_get_coalesce(struct net_device *netdev,
88462306a36Sopenharmony_ci				    struct ethtool_coalesce *ec,
88562306a36Sopenharmony_ci				    struct kernel_ethtool_coalesce *kernel_coal,
88662306a36Sopenharmony_ci				    struct netlink_ext_ack *extack)
88762306a36Sopenharmony_ci{
88862306a36Sopenharmony_ci	struct igc_adapter *adapter = netdev_priv(netdev);
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_ci	ec->rx_coalesce_usecs = igc_ethtool_get_previous_rx_coalesce(adapter);
89162306a36Sopenharmony_ci	ec->tx_coalesce_usecs = igc_ethtool_get_previous_tx_coalesce(adapter);
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_ci	return 0;
89462306a36Sopenharmony_ci}
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_cistatic int igc_ethtool_set_coalesce(struct net_device *netdev,
89762306a36Sopenharmony_ci				    struct ethtool_coalesce *ec,
89862306a36Sopenharmony_ci				    struct kernel_ethtool_coalesce *kernel_coal,
89962306a36Sopenharmony_ci				    struct netlink_ext_ack *extack)
90062306a36Sopenharmony_ci{
90162306a36Sopenharmony_ci	struct igc_adapter *adapter = netdev_priv(netdev);
90262306a36Sopenharmony_ci	int i;
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci	if (ec->rx_coalesce_usecs > IGC_MAX_ITR_USECS ||
90562306a36Sopenharmony_ci	    (ec->rx_coalesce_usecs > 3 &&
90662306a36Sopenharmony_ci	     ec->rx_coalesce_usecs < IGC_MIN_ITR_USECS) ||
90762306a36Sopenharmony_ci	    ec->rx_coalesce_usecs == 2)
90862306a36Sopenharmony_ci		return -EINVAL;
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci	if (ec->tx_coalesce_usecs > IGC_MAX_ITR_USECS ||
91162306a36Sopenharmony_ci	    (ec->tx_coalesce_usecs > 3 &&
91262306a36Sopenharmony_ci	     ec->tx_coalesce_usecs < IGC_MIN_ITR_USECS) ||
91362306a36Sopenharmony_ci	    ec->tx_coalesce_usecs == 2)
91462306a36Sopenharmony_ci		return -EINVAL;
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci	if ((adapter->flags & IGC_FLAG_QUEUE_PAIRS) &&
91762306a36Sopenharmony_ci	    ec->tx_coalesce_usecs != igc_ethtool_get_previous_tx_coalesce(adapter)) {
91862306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack,
91962306a36Sopenharmony_ci				   "Queue Pair mode enabled, both Rx and Tx coalescing controlled by rx-usecs");
92062306a36Sopenharmony_ci		return -EINVAL;
92162306a36Sopenharmony_ci	}
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_ci	/* If ITR is disabled, disable DMAC */
92462306a36Sopenharmony_ci	if (ec->rx_coalesce_usecs == 0) {
92562306a36Sopenharmony_ci		if (adapter->flags & IGC_FLAG_DMAC)
92662306a36Sopenharmony_ci			adapter->flags &= ~IGC_FLAG_DMAC;
92762306a36Sopenharmony_ci	}
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_ci	/* convert to rate of irq's per second */
93062306a36Sopenharmony_ci	if (ec->rx_coalesce_usecs && ec->rx_coalesce_usecs <= 3)
93162306a36Sopenharmony_ci		adapter->rx_itr_setting = ec->rx_coalesce_usecs;
93262306a36Sopenharmony_ci	else
93362306a36Sopenharmony_ci		adapter->rx_itr_setting = ec->rx_coalesce_usecs << 2;
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_ci	/* convert to rate of irq's per second */
93662306a36Sopenharmony_ci	if (adapter->flags & IGC_FLAG_QUEUE_PAIRS)
93762306a36Sopenharmony_ci		adapter->tx_itr_setting = adapter->rx_itr_setting;
93862306a36Sopenharmony_ci	else if (ec->tx_coalesce_usecs && ec->tx_coalesce_usecs <= 3)
93962306a36Sopenharmony_ci		adapter->tx_itr_setting = ec->tx_coalesce_usecs;
94062306a36Sopenharmony_ci	else
94162306a36Sopenharmony_ci		adapter->tx_itr_setting = ec->tx_coalesce_usecs << 2;
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_ci	for (i = 0; i < adapter->num_q_vectors; i++) {
94462306a36Sopenharmony_ci		struct igc_q_vector *q_vector = adapter->q_vector[i];
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci		q_vector->tx.work_limit = adapter->tx_work_limit;
94762306a36Sopenharmony_ci		if (q_vector->rx.ring)
94862306a36Sopenharmony_ci			q_vector->itr_val = adapter->rx_itr_setting;
94962306a36Sopenharmony_ci		else
95062306a36Sopenharmony_ci			q_vector->itr_val = adapter->tx_itr_setting;
95162306a36Sopenharmony_ci		if (q_vector->itr_val && q_vector->itr_val <= 3)
95262306a36Sopenharmony_ci			q_vector->itr_val = IGC_START_ITR;
95362306a36Sopenharmony_ci		q_vector->set_itr = 1;
95462306a36Sopenharmony_ci	}
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_ci	return 0;
95762306a36Sopenharmony_ci}
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_ci#define ETHER_TYPE_FULL_MASK ((__force __be16)~0)
96062306a36Sopenharmony_ci#define VLAN_TCI_FULL_MASK ((__force __be16)~0)
96162306a36Sopenharmony_cistatic int igc_ethtool_get_nfc_rule(struct igc_adapter *adapter,
96262306a36Sopenharmony_ci				    struct ethtool_rxnfc *cmd)
96362306a36Sopenharmony_ci{
96462306a36Sopenharmony_ci	struct ethtool_rx_flow_spec *fsp = &cmd->fs;
96562306a36Sopenharmony_ci	struct igc_nfc_rule *rule = NULL;
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_ci	cmd->data = IGC_MAX_RXNFC_RULES;
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_ci	mutex_lock(&adapter->nfc_rule_lock);
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_ci	rule = igc_get_nfc_rule(adapter, fsp->location);
97262306a36Sopenharmony_ci	if (!rule)
97362306a36Sopenharmony_ci		goto out;
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_ci	fsp->flow_type = ETHER_FLOW;
97662306a36Sopenharmony_ci	fsp->ring_cookie = rule->action;
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_ci	if (rule->filter.match_flags & IGC_FILTER_FLAG_ETHER_TYPE) {
97962306a36Sopenharmony_ci		fsp->h_u.ether_spec.h_proto = htons(rule->filter.etype);
98062306a36Sopenharmony_ci		fsp->m_u.ether_spec.h_proto = ETHER_TYPE_FULL_MASK;
98162306a36Sopenharmony_ci	}
98262306a36Sopenharmony_ci
98362306a36Sopenharmony_ci	if (rule->filter.match_flags & IGC_FILTER_FLAG_VLAN_ETYPE) {
98462306a36Sopenharmony_ci		fsp->flow_type |= FLOW_EXT;
98562306a36Sopenharmony_ci		fsp->h_ext.vlan_etype = rule->filter.vlan_etype;
98662306a36Sopenharmony_ci		fsp->m_ext.vlan_etype = ETHER_TYPE_FULL_MASK;
98762306a36Sopenharmony_ci	}
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_ci	if (rule->filter.match_flags & IGC_FILTER_FLAG_VLAN_TCI) {
99062306a36Sopenharmony_ci		fsp->flow_type |= FLOW_EXT;
99162306a36Sopenharmony_ci		fsp->h_ext.vlan_tci = htons(rule->filter.vlan_tci);
99262306a36Sopenharmony_ci		fsp->m_ext.vlan_tci = htons(rule->filter.vlan_tci_mask);
99362306a36Sopenharmony_ci	}
99462306a36Sopenharmony_ci
99562306a36Sopenharmony_ci	if (rule->filter.match_flags & IGC_FILTER_FLAG_DST_MAC_ADDR) {
99662306a36Sopenharmony_ci		ether_addr_copy(fsp->h_u.ether_spec.h_dest,
99762306a36Sopenharmony_ci				rule->filter.dst_addr);
99862306a36Sopenharmony_ci		eth_broadcast_addr(fsp->m_u.ether_spec.h_dest);
99962306a36Sopenharmony_ci	}
100062306a36Sopenharmony_ci
100162306a36Sopenharmony_ci	if (rule->filter.match_flags & IGC_FILTER_FLAG_SRC_MAC_ADDR) {
100262306a36Sopenharmony_ci		ether_addr_copy(fsp->h_u.ether_spec.h_source,
100362306a36Sopenharmony_ci				rule->filter.src_addr);
100462306a36Sopenharmony_ci		eth_broadcast_addr(fsp->m_u.ether_spec.h_source);
100562306a36Sopenharmony_ci	}
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci	if (rule->filter.match_flags & IGC_FILTER_FLAG_USER_DATA) {
100862306a36Sopenharmony_ci		fsp->flow_type |= FLOW_EXT;
100962306a36Sopenharmony_ci		memcpy(fsp->h_ext.data, rule->filter.user_data, sizeof(fsp->h_ext.data));
101062306a36Sopenharmony_ci		memcpy(fsp->m_ext.data, rule->filter.user_mask, sizeof(fsp->m_ext.data));
101162306a36Sopenharmony_ci	}
101262306a36Sopenharmony_ci
101362306a36Sopenharmony_ci	mutex_unlock(&adapter->nfc_rule_lock);
101462306a36Sopenharmony_ci	return 0;
101562306a36Sopenharmony_ci
101662306a36Sopenharmony_ciout:
101762306a36Sopenharmony_ci	mutex_unlock(&adapter->nfc_rule_lock);
101862306a36Sopenharmony_ci	return -EINVAL;
101962306a36Sopenharmony_ci}
102062306a36Sopenharmony_ci
102162306a36Sopenharmony_cistatic int igc_ethtool_get_nfc_rules(struct igc_adapter *adapter,
102262306a36Sopenharmony_ci				     struct ethtool_rxnfc *cmd,
102362306a36Sopenharmony_ci				     u32 *rule_locs)
102462306a36Sopenharmony_ci{
102562306a36Sopenharmony_ci	struct igc_nfc_rule *rule;
102662306a36Sopenharmony_ci	int cnt = 0;
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci	cmd->data = IGC_MAX_RXNFC_RULES;
102962306a36Sopenharmony_ci
103062306a36Sopenharmony_ci	mutex_lock(&adapter->nfc_rule_lock);
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_ci	list_for_each_entry(rule, &adapter->nfc_rule_list, list) {
103362306a36Sopenharmony_ci		if (cnt == cmd->rule_cnt) {
103462306a36Sopenharmony_ci			mutex_unlock(&adapter->nfc_rule_lock);
103562306a36Sopenharmony_ci			return -EMSGSIZE;
103662306a36Sopenharmony_ci		}
103762306a36Sopenharmony_ci		rule_locs[cnt] = rule->location;
103862306a36Sopenharmony_ci		cnt++;
103962306a36Sopenharmony_ci	}
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_ci	mutex_unlock(&adapter->nfc_rule_lock);
104262306a36Sopenharmony_ci
104362306a36Sopenharmony_ci	cmd->rule_cnt = cnt;
104462306a36Sopenharmony_ci
104562306a36Sopenharmony_ci	return 0;
104662306a36Sopenharmony_ci}
104762306a36Sopenharmony_ci
104862306a36Sopenharmony_cistatic int igc_ethtool_get_rss_hash_opts(struct igc_adapter *adapter,
104962306a36Sopenharmony_ci					 struct ethtool_rxnfc *cmd)
105062306a36Sopenharmony_ci{
105162306a36Sopenharmony_ci	cmd->data = 0;
105262306a36Sopenharmony_ci
105362306a36Sopenharmony_ci	/* Report default options for RSS on igc */
105462306a36Sopenharmony_ci	switch (cmd->flow_type) {
105562306a36Sopenharmony_ci	case TCP_V4_FLOW:
105662306a36Sopenharmony_ci		cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
105762306a36Sopenharmony_ci		fallthrough;
105862306a36Sopenharmony_ci	case UDP_V4_FLOW:
105962306a36Sopenharmony_ci		if (adapter->flags & IGC_FLAG_RSS_FIELD_IPV4_UDP)
106062306a36Sopenharmony_ci			cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
106162306a36Sopenharmony_ci		fallthrough;
106262306a36Sopenharmony_ci	case SCTP_V4_FLOW:
106362306a36Sopenharmony_ci	case AH_ESP_V4_FLOW:
106462306a36Sopenharmony_ci	case AH_V4_FLOW:
106562306a36Sopenharmony_ci	case ESP_V4_FLOW:
106662306a36Sopenharmony_ci	case IPV4_FLOW:
106762306a36Sopenharmony_ci		cmd->data |= RXH_IP_SRC | RXH_IP_DST;
106862306a36Sopenharmony_ci		break;
106962306a36Sopenharmony_ci	case TCP_V6_FLOW:
107062306a36Sopenharmony_ci		cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
107162306a36Sopenharmony_ci		fallthrough;
107262306a36Sopenharmony_ci	case UDP_V6_FLOW:
107362306a36Sopenharmony_ci		if (adapter->flags & IGC_FLAG_RSS_FIELD_IPV6_UDP)
107462306a36Sopenharmony_ci			cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
107562306a36Sopenharmony_ci		fallthrough;
107662306a36Sopenharmony_ci	case SCTP_V6_FLOW:
107762306a36Sopenharmony_ci	case AH_ESP_V6_FLOW:
107862306a36Sopenharmony_ci	case AH_V6_FLOW:
107962306a36Sopenharmony_ci	case ESP_V6_FLOW:
108062306a36Sopenharmony_ci	case IPV6_FLOW:
108162306a36Sopenharmony_ci		cmd->data |= RXH_IP_SRC | RXH_IP_DST;
108262306a36Sopenharmony_ci		break;
108362306a36Sopenharmony_ci	default:
108462306a36Sopenharmony_ci		return -EINVAL;
108562306a36Sopenharmony_ci	}
108662306a36Sopenharmony_ci
108762306a36Sopenharmony_ci	return 0;
108862306a36Sopenharmony_ci}
108962306a36Sopenharmony_ci
109062306a36Sopenharmony_cistatic int igc_ethtool_get_rxnfc(struct net_device *dev,
109162306a36Sopenharmony_ci				 struct ethtool_rxnfc *cmd, u32 *rule_locs)
109262306a36Sopenharmony_ci{
109362306a36Sopenharmony_ci	struct igc_adapter *adapter = netdev_priv(dev);
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ci	switch (cmd->cmd) {
109662306a36Sopenharmony_ci	case ETHTOOL_GRXRINGS:
109762306a36Sopenharmony_ci		cmd->data = adapter->num_rx_queues;
109862306a36Sopenharmony_ci		return 0;
109962306a36Sopenharmony_ci	case ETHTOOL_GRXCLSRLCNT:
110062306a36Sopenharmony_ci		cmd->rule_cnt = adapter->nfc_rule_count;
110162306a36Sopenharmony_ci		return 0;
110262306a36Sopenharmony_ci	case ETHTOOL_GRXCLSRULE:
110362306a36Sopenharmony_ci		return igc_ethtool_get_nfc_rule(adapter, cmd);
110462306a36Sopenharmony_ci	case ETHTOOL_GRXCLSRLALL:
110562306a36Sopenharmony_ci		return igc_ethtool_get_nfc_rules(adapter, cmd, rule_locs);
110662306a36Sopenharmony_ci	case ETHTOOL_GRXFH:
110762306a36Sopenharmony_ci		return igc_ethtool_get_rss_hash_opts(adapter, cmd);
110862306a36Sopenharmony_ci	default:
110962306a36Sopenharmony_ci		return -EOPNOTSUPP;
111062306a36Sopenharmony_ci	}
111162306a36Sopenharmony_ci}
111262306a36Sopenharmony_ci
111362306a36Sopenharmony_ci#define UDP_RSS_FLAGS (IGC_FLAG_RSS_FIELD_IPV4_UDP | \
111462306a36Sopenharmony_ci		       IGC_FLAG_RSS_FIELD_IPV6_UDP)
111562306a36Sopenharmony_cistatic int igc_ethtool_set_rss_hash_opt(struct igc_adapter *adapter,
111662306a36Sopenharmony_ci					struct ethtool_rxnfc *nfc)
111762306a36Sopenharmony_ci{
111862306a36Sopenharmony_ci	u32 flags = adapter->flags;
111962306a36Sopenharmony_ci
112062306a36Sopenharmony_ci	/* RSS does not support anything other than hashing
112162306a36Sopenharmony_ci	 * to queues on src and dst IPs and ports
112262306a36Sopenharmony_ci	 */
112362306a36Sopenharmony_ci	if (nfc->data & ~(RXH_IP_SRC | RXH_IP_DST |
112462306a36Sopenharmony_ci			  RXH_L4_B_0_1 | RXH_L4_B_2_3))
112562306a36Sopenharmony_ci		return -EINVAL;
112662306a36Sopenharmony_ci
112762306a36Sopenharmony_ci	switch (nfc->flow_type) {
112862306a36Sopenharmony_ci	case TCP_V4_FLOW:
112962306a36Sopenharmony_ci	case TCP_V6_FLOW:
113062306a36Sopenharmony_ci		if (!(nfc->data & RXH_IP_SRC) ||
113162306a36Sopenharmony_ci		    !(nfc->data & RXH_IP_DST) ||
113262306a36Sopenharmony_ci		    !(nfc->data & RXH_L4_B_0_1) ||
113362306a36Sopenharmony_ci		    !(nfc->data & RXH_L4_B_2_3))
113462306a36Sopenharmony_ci			return -EINVAL;
113562306a36Sopenharmony_ci		break;
113662306a36Sopenharmony_ci	case UDP_V4_FLOW:
113762306a36Sopenharmony_ci		if (!(nfc->data & RXH_IP_SRC) ||
113862306a36Sopenharmony_ci		    !(nfc->data & RXH_IP_DST))
113962306a36Sopenharmony_ci			return -EINVAL;
114062306a36Sopenharmony_ci		switch (nfc->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) {
114162306a36Sopenharmony_ci		case 0:
114262306a36Sopenharmony_ci			flags &= ~IGC_FLAG_RSS_FIELD_IPV4_UDP;
114362306a36Sopenharmony_ci			break;
114462306a36Sopenharmony_ci		case (RXH_L4_B_0_1 | RXH_L4_B_2_3):
114562306a36Sopenharmony_ci			flags |= IGC_FLAG_RSS_FIELD_IPV4_UDP;
114662306a36Sopenharmony_ci			break;
114762306a36Sopenharmony_ci		default:
114862306a36Sopenharmony_ci			return -EINVAL;
114962306a36Sopenharmony_ci		}
115062306a36Sopenharmony_ci		break;
115162306a36Sopenharmony_ci	case UDP_V6_FLOW:
115262306a36Sopenharmony_ci		if (!(nfc->data & RXH_IP_SRC) ||
115362306a36Sopenharmony_ci		    !(nfc->data & RXH_IP_DST))
115462306a36Sopenharmony_ci			return -EINVAL;
115562306a36Sopenharmony_ci		switch (nfc->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) {
115662306a36Sopenharmony_ci		case 0:
115762306a36Sopenharmony_ci			flags &= ~IGC_FLAG_RSS_FIELD_IPV6_UDP;
115862306a36Sopenharmony_ci			break;
115962306a36Sopenharmony_ci		case (RXH_L4_B_0_1 | RXH_L4_B_2_3):
116062306a36Sopenharmony_ci			flags |= IGC_FLAG_RSS_FIELD_IPV6_UDP;
116162306a36Sopenharmony_ci			break;
116262306a36Sopenharmony_ci		default:
116362306a36Sopenharmony_ci			return -EINVAL;
116462306a36Sopenharmony_ci		}
116562306a36Sopenharmony_ci		break;
116662306a36Sopenharmony_ci	case AH_ESP_V4_FLOW:
116762306a36Sopenharmony_ci	case AH_V4_FLOW:
116862306a36Sopenharmony_ci	case ESP_V4_FLOW:
116962306a36Sopenharmony_ci	case SCTP_V4_FLOW:
117062306a36Sopenharmony_ci	case AH_ESP_V6_FLOW:
117162306a36Sopenharmony_ci	case AH_V6_FLOW:
117262306a36Sopenharmony_ci	case ESP_V6_FLOW:
117362306a36Sopenharmony_ci	case SCTP_V6_FLOW:
117462306a36Sopenharmony_ci		if (!(nfc->data & RXH_IP_SRC) ||
117562306a36Sopenharmony_ci		    !(nfc->data & RXH_IP_DST) ||
117662306a36Sopenharmony_ci		    (nfc->data & RXH_L4_B_0_1) ||
117762306a36Sopenharmony_ci		    (nfc->data & RXH_L4_B_2_3))
117862306a36Sopenharmony_ci			return -EINVAL;
117962306a36Sopenharmony_ci		break;
118062306a36Sopenharmony_ci	default:
118162306a36Sopenharmony_ci		return -EINVAL;
118262306a36Sopenharmony_ci	}
118362306a36Sopenharmony_ci
118462306a36Sopenharmony_ci	/* if we changed something we need to update flags */
118562306a36Sopenharmony_ci	if (flags != adapter->flags) {
118662306a36Sopenharmony_ci		struct igc_hw *hw = &adapter->hw;
118762306a36Sopenharmony_ci		u32 mrqc = rd32(IGC_MRQC);
118862306a36Sopenharmony_ci
118962306a36Sopenharmony_ci		if ((flags & UDP_RSS_FLAGS) &&
119062306a36Sopenharmony_ci		    !(adapter->flags & UDP_RSS_FLAGS))
119162306a36Sopenharmony_ci			netdev_err(adapter->netdev,
119262306a36Sopenharmony_ci				   "Enabling UDP RSS: fragmented packets may arrive out of order to the stack above\n");
119362306a36Sopenharmony_ci
119462306a36Sopenharmony_ci		adapter->flags = flags;
119562306a36Sopenharmony_ci
119662306a36Sopenharmony_ci		/* Perform hash on these packet types */
119762306a36Sopenharmony_ci		mrqc |= IGC_MRQC_RSS_FIELD_IPV4 |
119862306a36Sopenharmony_ci			IGC_MRQC_RSS_FIELD_IPV4_TCP |
119962306a36Sopenharmony_ci			IGC_MRQC_RSS_FIELD_IPV6 |
120062306a36Sopenharmony_ci			IGC_MRQC_RSS_FIELD_IPV6_TCP;
120162306a36Sopenharmony_ci
120262306a36Sopenharmony_ci		mrqc &= ~(IGC_MRQC_RSS_FIELD_IPV4_UDP |
120362306a36Sopenharmony_ci			  IGC_MRQC_RSS_FIELD_IPV6_UDP);
120462306a36Sopenharmony_ci
120562306a36Sopenharmony_ci		if (flags & IGC_FLAG_RSS_FIELD_IPV4_UDP)
120662306a36Sopenharmony_ci			mrqc |= IGC_MRQC_RSS_FIELD_IPV4_UDP;
120762306a36Sopenharmony_ci
120862306a36Sopenharmony_ci		if (flags & IGC_FLAG_RSS_FIELD_IPV6_UDP)
120962306a36Sopenharmony_ci			mrqc |= IGC_MRQC_RSS_FIELD_IPV6_UDP;
121062306a36Sopenharmony_ci
121162306a36Sopenharmony_ci		wr32(IGC_MRQC, mrqc);
121262306a36Sopenharmony_ci	}
121362306a36Sopenharmony_ci
121462306a36Sopenharmony_ci	return 0;
121562306a36Sopenharmony_ci}
121662306a36Sopenharmony_ci
121762306a36Sopenharmony_cistatic void igc_ethtool_init_nfc_rule(struct igc_nfc_rule *rule,
121862306a36Sopenharmony_ci				      const struct ethtool_rx_flow_spec *fsp)
121962306a36Sopenharmony_ci{
122062306a36Sopenharmony_ci	INIT_LIST_HEAD(&rule->list);
122162306a36Sopenharmony_ci
122262306a36Sopenharmony_ci	rule->action = fsp->ring_cookie;
122362306a36Sopenharmony_ci	rule->location = fsp->location;
122462306a36Sopenharmony_ci
122562306a36Sopenharmony_ci	if ((fsp->flow_type & FLOW_EXT) && fsp->m_ext.vlan_tci) {
122662306a36Sopenharmony_ci		rule->filter.vlan_tci = ntohs(fsp->h_ext.vlan_tci);
122762306a36Sopenharmony_ci		rule->filter.vlan_tci_mask = ntohs(fsp->m_ext.vlan_tci);
122862306a36Sopenharmony_ci		rule->filter.match_flags |= IGC_FILTER_FLAG_VLAN_TCI;
122962306a36Sopenharmony_ci	}
123062306a36Sopenharmony_ci
123162306a36Sopenharmony_ci	if (fsp->m_u.ether_spec.h_proto == ETHER_TYPE_FULL_MASK) {
123262306a36Sopenharmony_ci		rule->filter.etype = ntohs(fsp->h_u.ether_spec.h_proto);
123362306a36Sopenharmony_ci		rule->filter.match_flags = IGC_FILTER_FLAG_ETHER_TYPE;
123462306a36Sopenharmony_ci	}
123562306a36Sopenharmony_ci
123662306a36Sopenharmony_ci	/* Both source and destination address filters only support the full
123762306a36Sopenharmony_ci	 * mask.
123862306a36Sopenharmony_ci	 */
123962306a36Sopenharmony_ci	if (is_broadcast_ether_addr(fsp->m_u.ether_spec.h_source)) {
124062306a36Sopenharmony_ci		rule->filter.match_flags |= IGC_FILTER_FLAG_SRC_MAC_ADDR;
124162306a36Sopenharmony_ci		ether_addr_copy(rule->filter.src_addr,
124262306a36Sopenharmony_ci				fsp->h_u.ether_spec.h_source);
124362306a36Sopenharmony_ci	}
124462306a36Sopenharmony_ci
124562306a36Sopenharmony_ci	if (is_broadcast_ether_addr(fsp->m_u.ether_spec.h_dest)) {
124662306a36Sopenharmony_ci		rule->filter.match_flags |= IGC_FILTER_FLAG_DST_MAC_ADDR;
124762306a36Sopenharmony_ci		ether_addr_copy(rule->filter.dst_addr,
124862306a36Sopenharmony_ci				fsp->h_u.ether_spec.h_dest);
124962306a36Sopenharmony_ci	}
125062306a36Sopenharmony_ci
125162306a36Sopenharmony_ci	/* VLAN etype matching */
125262306a36Sopenharmony_ci	if ((fsp->flow_type & FLOW_EXT) && fsp->h_ext.vlan_etype) {
125362306a36Sopenharmony_ci		rule->filter.vlan_etype = fsp->h_ext.vlan_etype;
125462306a36Sopenharmony_ci		rule->filter.match_flags |= IGC_FILTER_FLAG_VLAN_ETYPE;
125562306a36Sopenharmony_ci	}
125662306a36Sopenharmony_ci
125762306a36Sopenharmony_ci	/* Check for user defined data */
125862306a36Sopenharmony_ci	if ((fsp->flow_type & FLOW_EXT) &&
125962306a36Sopenharmony_ci	    (fsp->h_ext.data[0] || fsp->h_ext.data[1])) {
126062306a36Sopenharmony_ci		rule->filter.match_flags |= IGC_FILTER_FLAG_USER_DATA;
126162306a36Sopenharmony_ci		memcpy(rule->filter.user_data, fsp->h_ext.data, sizeof(fsp->h_ext.data));
126262306a36Sopenharmony_ci		memcpy(rule->filter.user_mask, fsp->m_ext.data, sizeof(fsp->m_ext.data));
126362306a36Sopenharmony_ci	}
126462306a36Sopenharmony_ci
126562306a36Sopenharmony_ci	/* The i225/i226 has various different filters. Flex filters provide a
126662306a36Sopenharmony_ci	 * way to match up to the first 128 bytes of a packet. Use them for:
126762306a36Sopenharmony_ci	 *   a) For specific user data
126862306a36Sopenharmony_ci	 *   b) For VLAN EtherType
126962306a36Sopenharmony_ci	 *   c) For full TCI match
127062306a36Sopenharmony_ci	 *   d) Or in case multiple filter criteria are set
127162306a36Sopenharmony_ci	 *
127262306a36Sopenharmony_ci	 * Otherwise, use the simple MAC, VLAN PRIO or EtherType filters.
127362306a36Sopenharmony_ci	 */
127462306a36Sopenharmony_ci	if ((rule->filter.match_flags & IGC_FILTER_FLAG_USER_DATA) ||
127562306a36Sopenharmony_ci	    (rule->filter.match_flags & IGC_FILTER_FLAG_VLAN_ETYPE) ||
127662306a36Sopenharmony_ci	    ((rule->filter.match_flags & IGC_FILTER_FLAG_VLAN_TCI) &&
127762306a36Sopenharmony_ci	     rule->filter.vlan_tci_mask == ntohs(VLAN_TCI_FULL_MASK)) ||
127862306a36Sopenharmony_ci	    (rule->filter.match_flags & (rule->filter.match_flags - 1)))
127962306a36Sopenharmony_ci		rule->flex = true;
128062306a36Sopenharmony_ci	else
128162306a36Sopenharmony_ci		rule->flex = false;
128262306a36Sopenharmony_ci}
128362306a36Sopenharmony_ci
128462306a36Sopenharmony_ci/**
128562306a36Sopenharmony_ci * igc_ethtool_check_nfc_rule() - Check if NFC rule is valid
128662306a36Sopenharmony_ci * @adapter: Pointer to adapter
128762306a36Sopenharmony_ci * @rule: Rule under evaluation
128862306a36Sopenharmony_ci *
128962306a36Sopenharmony_ci * The driver doesn't support rules with multiple matches so if more than
129062306a36Sopenharmony_ci * one bit in filter flags is set, @rule is considered invalid.
129162306a36Sopenharmony_ci *
129262306a36Sopenharmony_ci * Also, if there is already another rule with the same filter in a different
129362306a36Sopenharmony_ci * location, @rule is considered invalid.
129462306a36Sopenharmony_ci *
129562306a36Sopenharmony_ci * Context: Expects adapter->nfc_rule_lock to be held by caller.
129662306a36Sopenharmony_ci *
129762306a36Sopenharmony_ci * Return: 0 in case of success, negative errno code otherwise.
129862306a36Sopenharmony_ci */
129962306a36Sopenharmony_cistatic int igc_ethtool_check_nfc_rule(struct igc_adapter *adapter,
130062306a36Sopenharmony_ci				      struct igc_nfc_rule *rule)
130162306a36Sopenharmony_ci{
130262306a36Sopenharmony_ci	struct net_device *dev = adapter->netdev;
130362306a36Sopenharmony_ci	u8 flags = rule->filter.match_flags;
130462306a36Sopenharmony_ci	struct igc_nfc_rule *tmp;
130562306a36Sopenharmony_ci
130662306a36Sopenharmony_ci	if (!flags) {
130762306a36Sopenharmony_ci		netdev_dbg(dev, "Rule with no match\n");
130862306a36Sopenharmony_ci		return -EINVAL;
130962306a36Sopenharmony_ci	}
131062306a36Sopenharmony_ci
131162306a36Sopenharmony_ci	list_for_each_entry(tmp, &adapter->nfc_rule_list, list) {
131262306a36Sopenharmony_ci		if (!memcmp(&rule->filter, &tmp->filter,
131362306a36Sopenharmony_ci			    sizeof(rule->filter)) &&
131462306a36Sopenharmony_ci		    tmp->location != rule->location) {
131562306a36Sopenharmony_ci			netdev_dbg(dev, "Rule already exists\n");
131662306a36Sopenharmony_ci			return -EEXIST;
131762306a36Sopenharmony_ci		}
131862306a36Sopenharmony_ci	}
131962306a36Sopenharmony_ci
132062306a36Sopenharmony_ci	return 0;
132162306a36Sopenharmony_ci}
132262306a36Sopenharmony_ci
132362306a36Sopenharmony_cistatic int igc_ethtool_add_nfc_rule(struct igc_adapter *adapter,
132462306a36Sopenharmony_ci				    struct ethtool_rxnfc *cmd)
132562306a36Sopenharmony_ci{
132662306a36Sopenharmony_ci	struct net_device *netdev = adapter->netdev;
132762306a36Sopenharmony_ci	struct ethtool_rx_flow_spec *fsp =
132862306a36Sopenharmony_ci		(struct ethtool_rx_flow_spec *)&cmd->fs;
132962306a36Sopenharmony_ci	struct igc_nfc_rule *rule, *old_rule;
133062306a36Sopenharmony_ci	int err;
133162306a36Sopenharmony_ci
133262306a36Sopenharmony_ci	if (!(netdev->hw_features & NETIF_F_NTUPLE)) {
133362306a36Sopenharmony_ci		netdev_dbg(netdev, "N-tuple filters disabled\n");
133462306a36Sopenharmony_ci		return -EOPNOTSUPP;
133562306a36Sopenharmony_ci	}
133662306a36Sopenharmony_ci
133762306a36Sopenharmony_ci	if ((fsp->flow_type & ~FLOW_EXT) != ETHER_FLOW) {
133862306a36Sopenharmony_ci		netdev_dbg(netdev, "Only ethernet flow type is supported\n");
133962306a36Sopenharmony_ci		return -EOPNOTSUPP;
134062306a36Sopenharmony_ci	}
134162306a36Sopenharmony_ci
134262306a36Sopenharmony_ci	if (fsp->ring_cookie >= adapter->num_rx_queues) {
134362306a36Sopenharmony_ci		netdev_dbg(netdev, "Invalid action\n");
134462306a36Sopenharmony_ci		return -EINVAL;
134562306a36Sopenharmony_ci	}
134662306a36Sopenharmony_ci
134762306a36Sopenharmony_ci	/* There are two ways to match the VLAN TCI:
134862306a36Sopenharmony_ci	 *  1. Match on PCP field and use vlan prio filter for it
134962306a36Sopenharmony_ci	 *  2. Match on complete TCI field and use flex filter for it
135062306a36Sopenharmony_ci	 */
135162306a36Sopenharmony_ci	if ((fsp->flow_type & FLOW_EXT) &&
135262306a36Sopenharmony_ci	    fsp->m_ext.vlan_tci &&
135362306a36Sopenharmony_ci	    fsp->m_ext.vlan_tci != htons(VLAN_PRIO_MASK) &&
135462306a36Sopenharmony_ci	    fsp->m_ext.vlan_tci != VLAN_TCI_FULL_MASK) {
135562306a36Sopenharmony_ci		netdev_dbg(netdev, "VLAN mask not supported\n");
135662306a36Sopenharmony_ci		return -EOPNOTSUPP;
135762306a36Sopenharmony_ci	}
135862306a36Sopenharmony_ci
135962306a36Sopenharmony_ci	/* VLAN EtherType can only be matched by full mask. */
136062306a36Sopenharmony_ci	if ((fsp->flow_type & FLOW_EXT) &&
136162306a36Sopenharmony_ci	    fsp->m_ext.vlan_etype &&
136262306a36Sopenharmony_ci	    fsp->m_ext.vlan_etype != ETHER_TYPE_FULL_MASK) {
136362306a36Sopenharmony_ci		netdev_dbg(netdev, "VLAN EtherType mask not supported\n");
136462306a36Sopenharmony_ci		return -EOPNOTSUPP;
136562306a36Sopenharmony_ci	}
136662306a36Sopenharmony_ci
136762306a36Sopenharmony_ci	if (fsp->location >= IGC_MAX_RXNFC_RULES) {
136862306a36Sopenharmony_ci		netdev_dbg(netdev, "Invalid location\n");
136962306a36Sopenharmony_ci		return -EINVAL;
137062306a36Sopenharmony_ci	}
137162306a36Sopenharmony_ci
137262306a36Sopenharmony_ci	rule = kzalloc(sizeof(*rule), GFP_KERNEL);
137362306a36Sopenharmony_ci	if (!rule)
137462306a36Sopenharmony_ci		return -ENOMEM;
137562306a36Sopenharmony_ci
137662306a36Sopenharmony_ci	igc_ethtool_init_nfc_rule(rule, fsp);
137762306a36Sopenharmony_ci
137862306a36Sopenharmony_ci	mutex_lock(&adapter->nfc_rule_lock);
137962306a36Sopenharmony_ci
138062306a36Sopenharmony_ci	err = igc_ethtool_check_nfc_rule(adapter, rule);
138162306a36Sopenharmony_ci	if (err)
138262306a36Sopenharmony_ci		goto err;
138362306a36Sopenharmony_ci
138462306a36Sopenharmony_ci	old_rule = igc_get_nfc_rule(adapter, fsp->location);
138562306a36Sopenharmony_ci	if (old_rule)
138662306a36Sopenharmony_ci		igc_del_nfc_rule(adapter, old_rule);
138762306a36Sopenharmony_ci
138862306a36Sopenharmony_ci	err = igc_add_nfc_rule(adapter, rule);
138962306a36Sopenharmony_ci	if (err)
139062306a36Sopenharmony_ci		goto err;
139162306a36Sopenharmony_ci
139262306a36Sopenharmony_ci	mutex_unlock(&adapter->nfc_rule_lock);
139362306a36Sopenharmony_ci	return 0;
139462306a36Sopenharmony_ci
139562306a36Sopenharmony_cierr:
139662306a36Sopenharmony_ci	mutex_unlock(&adapter->nfc_rule_lock);
139762306a36Sopenharmony_ci	kfree(rule);
139862306a36Sopenharmony_ci	return err;
139962306a36Sopenharmony_ci}
140062306a36Sopenharmony_ci
140162306a36Sopenharmony_cistatic int igc_ethtool_del_nfc_rule(struct igc_adapter *adapter,
140262306a36Sopenharmony_ci				    struct ethtool_rxnfc *cmd)
140362306a36Sopenharmony_ci{
140462306a36Sopenharmony_ci	struct ethtool_rx_flow_spec *fsp =
140562306a36Sopenharmony_ci		(struct ethtool_rx_flow_spec *)&cmd->fs;
140662306a36Sopenharmony_ci	struct igc_nfc_rule *rule;
140762306a36Sopenharmony_ci
140862306a36Sopenharmony_ci	mutex_lock(&adapter->nfc_rule_lock);
140962306a36Sopenharmony_ci
141062306a36Sopenharmony_ci	rule = igc_get_nfc_rule(adapter, fsp->location);
141162306a36Sopenharmony_ci	if (!rule) {
141262306a36Sopenharmony_ci		mutex_unlock(&adapter->nfc_rule_lock);
141362306a36Sopenharmony_ci		return -EINVAL;
141462306a36Sopenharmony_ci	}
141562306a36Sopenharmony_ci
141662306a36Sopenharmony_ci	igc_del_nfc_rule(adapter, rule);
141762306a36Sopenharmony_ci
141862306a36Sopenharmony_ci	mutex_unlock(&adapter->nfc_rule_lock);
141962306a36Sopenharmony_ci	return 0;
142062306a36Sopenharmony_ci}
142162306a36Sopenharmony_ci
142262306a36Sopenharmony_cistatic int igc_ethtool_set_rxnfc(struct net_device *dev,
142362306a36Sopenharmony_ci				 struct ethtool_rxnfc *cmd)
142462306a36Sopenharmony_ci{
142562306a36Sopenharmony_ci	struct igc_adapter *adapter = netdev_priv(dev);
142662306a36Sopenharmony_ci
142762306a36Sopenharmony_ci	switch (cmd->cmd) {
142862306a36Sopenharmony_ci	case ETHTOOL_SRXFH:
142962306a36Sopenharmony_ci		return igc_ethtool_set_rss_hash_opt(adapter, cmd);
143062306a36Sopenharmony_ci	case ETHTOOL_SRXCLSRLINS:
143162306a36Sopenharmony_ci		return igc_ethtool_add_nfc_rule(adapter, cmd);
143262306a36Sopenharmony_ci	case ETHTOOL_SRXCLSRLDEL:
143362306a36Sopenharmony_ci		return igc_ethtool_del_nfc_rule(adapter, cmd);
143462306a36Sopenharmony_ci	default:
143562306a36Sopenharmony_ci		return -EOPNOTSUPP;
143662306a36Sopenharmony_ci	}
143762306a36Sopenharmony_ci}
143862306a36Sopenharmony_ci
143962306a36Sopenharmony_civoid igc_write_rss_indir_tbl(struct igc_adapter *adapter)
144062306a36Sopenharmony_ci{
144162306a36Sopenharmony_ci	struct igc_hw *hw = &adapter->hw;
144262306a36Sopenharmony_ci	u32 reg = IGC_RETA(0);
144362306a36Sopenharmony_ci	u32 shift = 0;
144462306a36Sopenharmony_ci	int i = 0;
144562306a36Sopenharmony_ci
144662306a36Sopenharmony_ci	while (i < IGC_RETA_SIZE) {
144762306a36Sopenharmony_ci		u32 val = 0;
144862306a36Sopenharmony_ci		int j;
144962306a36Sopenharmony_ci
145062306a36Sopenharmony_ci		for (j = 3; j >= 0; j--) {
145162306a36Sopenharmony_ci			val <<= 8;
145262306a36Sopenharmony_ci			val |= adapter->rss_indir_tbl[i + j];
145362306a36Sopenharmony_ci		}
145462306a36Sopenharmony_ci
145562306a36Sopenharmony_ci		wr32(reg, val << shift);
145662306a36Sopenharmony_ci		reg += 4;
145762306a36Sopenharmony_ci		i += 4;
145862306a36Sopenharmony_ci	}
145962306a36Sopenharmony_ci}
146062306a36Sopenharmony_ci
146162306a36Sopenharmony_cistatic u32 igc_ethtool_get_rxfh_indir_size(struct net_device *netdev)
146262306a36Sopenharmony_ci{
146362306a36Sopenharmony_ci	return IGC_RETA_SIZE;
146462306a36Sopenharmony_ci}
146562306a36Sopenharmony_ci
146662306a36Sopenharmony_cistatic int igc_ethtool_get_rxfh(struct net_device *netdev, u32 *indir, u8 *key,
146762306a36Sopenharmony_ci				u8 *hfunc)
146862306a36Sopenharmony_ci{
146962306a36Sopenharmony_ci	struct igc_adapter *adapter = netdev_priv(netdev);
147062306a36Sopenharmony_ci	int i;
147162306a36Sopenharmony_ci
147262306a36Sopenharmony_ci	if (hfunc)
147362306a36Sopenharmony_ci		*hfunc = ETH_RSS_HASH_TOP;
147462306a36Sopenharmony_ci	if (!indir)
147562306a36Sopenharmony_ci		return 0;
147662306a36Sopenharmony_ci	for (i = 0; i < IGC_RETA_SIZE; i++)
147762306a36Sopenharmony_ci		indir[i] = adapter->rss_indir_tbl[i];
147862306a36Sopenharmony_ci
147962306a36Sopenharmony_ci	return 0;
148062306a36Sopenharmony_ci}
148162306a36Sopenharmony_ci
148262306a36Sopenharmony_cistatic int igc_ethtool_set_rxfh(struct net_device *netdev, const u32 *indir,
148362306a36Sopenharmony_ci				const u8 *key, const u8 hfunc)
148462306a36Sopenharmony_ci{
148562306a36Sopenharmony_ci	struct igc_adapter *adapter = netdev_priv(netdev);
148662306a36Sopenharmony_ci	u32 num_queues;
148762306a36Sopenharmony_ci	int i;
148862306a36Sopenharmony_ci
148962306a36Sopenharmony_ci	/* We do not allow change in unsupported parameters */
149062306a36Sopenharmony_ci	if (key ||
149162306a36Sopenharmony_ci	    (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP))
149262306a36Sopenharmony_ci		return -EOPNOTSUPP;
149362306a36Sopenharmony_ci	if (!indir)
149462306a36Sopenharmony_ci		return 0;
149562306a36Sopenharmony_ci
149662306a36Sopenharmony_ci	num_queues = adapter->rss_queues;
149762306a36Sopenharmony_ci
149862306a36Sopenharmony_ci	/* Verify user input. */
149962306a36Sopenharmony_ci	for (i = 0; i < IGC_RETA_SIZE; i++)
150062306a36Sopenharmony_ci		if (indir[i] >= num_queues)
150162306a36Sopenharmony_ci			return -EINVAL;
150262306a36Sopenharmony_ci
150362306a36Sopenharmony_ci	for (i = 0; i < IGC_RETA_SIZE; i++)
150462306a36Sopenharmony_ci		adapter->rss_indir_tbl[i] = indir[i];
150562306a36Sopenharmony_ci
150662306a36Sopenharmony_ci	igc_write_rss_indir_tbl(adapter);
150762306a36Sopenharmony_ci
150862306a36Sopenharmony_ci	return 0;
150962306a36Sopenharmony_ci}
151062306a36Sopenharmony_ci
151162306a36Sopenharmony_cistatic void igc_ethtool_get_channels(struct net_device *netdev,
151262306a36Sopenharmony_ci				     struct ethtool_channels *ch)
151362306a36Sopenharmony_ci{
151462306a36Sopenharmony_ci	struct igc_adapter *adapter = netdev_priv(netdev);
151562306a36Sopenharmony_ci
151662306a36Sopenharmony_ci	/* Report maximum channels */
151762306a36Sopenharmony_ci	ch->max_combined = igc_get_max_rss_queues(adapter);
151862306a36Sopenharmony_ci
151962306a36Sopenharmony_ci	/* Report info for other vector */
152062306a36Sopenharmony_ci	if (adapter->flags & IGC_FLAG_HAS_MSIX) {
152162306a36Sopenharmony_ci		ch->max_other = NON_Q_VECTORS;
152262306a36Sopenharmony_ci		ch->other_count = NON_Q_VECTORS;
152362306a36Sopenharmony_ci	}
152462306a36Sopenharmony_ci
152562306a36Sopenharmony_ci	ch->combined_count = adapter->rss_queues;
152662306a36Sopenharmony_ci}
152762306a36Sopenharmony_ci
152862306a36Sopenharmony_cistatic int igc_ethtool_set_channels(struct net_device *netdev,
152962306a36Sopenharmony_ci				    struct ethtool_channels *ch)
153062306a36Sopenharmony_ci{
153162306a36Sopenharmony_ci	struct igc_adapter *adapter = netdev_priv(netdev);
153262306a36Sopenharmony_ci	unsigned int count = ch->combined_count;
153362306a36Sopenharmony_ci	unsigned int max_combined = 0;
153462306a36Sopenharmony_ci
153562306a36Sopenharmony_ci	/* Verify they are not requesting separate vectors */
153662306a36Sopenharmony_ci	if (!count || ch->rx_count || ch->tx_count)
153762306a36Sopenharmony_ci		return -EINVAL;
153862306a36Sopenharmony_ci
153962306a36Sopenharmony_ci	/* Verify other_count is valid and has not been changed */
154062306a36Sopenharmony_ci	if (ch->other_count != NON_Q_VECTORS)
154162306a36Sopenharmony_ci		return -EINVAL;
154262306a36Sopenharmony_ci
154362306a36Sopenharmony_ci	/* Verify the number of channels doesn't exceed hw limits */
154462306a36Sopenharmony_ci	max_combined = igc_get_max_rss_queues(adapter);
154562306a36Sopenharmony_ci	if (count > max_combined)
154662306a36Sopenharmony_ci		return -EINVAL;
154762306a36Sopenharmony_ci
154862306a36Sopenharmony_ci	if (count != adapter->rss_queues) {
154962306a36Sopenharmony_ci		adapter->rss_queues = count;
155062306a36Sopenharmony_ci		igc_set_flag_queue_pairs(adapter, max_combined);
155162306a36Sopenharmony_ci
155262306a36Sopenharmony_ci		/* Hardware has to reinitialize queues and interrupts to
155362306a36Sopenharmony_ci		 * match the new configuration.
155462306a36Sopenharmony_ci		 */
155562306a36Sopenharmony_ci		return igc_reinit_queues(adapter);
155662306a36Sopenharmony_ci	}
155762306a36Sopenharmony_ci
155862306a36Sopenharmony_ci	return 0;
155962306a36Sopenharmony_ci}
156062306a36Sopenharmony_ci
156162306a36Sopenharmony_cistatic int igc_ethtool_get_ts_info(struct net_device *dev,
156262306a36Sopenharmony_ci				   struct ethtool_ts_info *info)
156362306a36Sopenharmony_ci{
156462306a36Sopenharmony_ci	struct igc_adapter *adapter = netdev_priv(dev);
156562306a36Sopenharmony_ci
156662306a36Sopenharmony_ci	if (adapter->ptp_clock)
156762306a36Sopenharmony_ci		info->phc_index = ptp_clock_index(adapter->ptp_clock);
156862306a36Sopenharmony_ci	else
156962306a36Sopenharmony_ci		info->phc_index = -1;
157062306a36Sopenharmony_ci
157162306a36Sopenharmony_ci	switch (adapter->hw.mac.type) {
157262306a36Sopenharmony_ci	case igc_i225:
157362306a36Sopenharmony_ci		info->so_timestamping =
157462306a36Sopenharmony_ci			SOF_TIMESTAMPING_TX_SOFTWARE |
157562306a36Sopenharmony_ci			SOF_TIMESTAMPING_RX_SOFTWARE |
157662306a36Sopenharmony_ci			SOF_TIMESTAMPING_SOFTWARE |
157762306a36Sopenharmony_ci			SOF_TIMESTAMPING_TX_HARDWARE |
157862306a36Sopenharmony_ci			SOF_TIMESTAMPING_RX_HARDWARE |
157962306a36Sopenharmony_ci			SOF_TIMESTAMPING_RAW_HARDWARE;
158062306a36Sopenharmony_ci
158162306a36Sopenharmony_ci		info->tx_types =
158262306a36Sopenharmony_ci			BIT(HWTSTAMP_TX_OFF) |
158362306a36Sopenharmony_ci			BIT(HWTSTAMP_TX_ON);
158462306a36Sopenharmony_ci
158562306a36Sopenharmony_ci		info->rx_filters = BIT(HWTSTAMP_FILTER_NONE);
158662306a36Sopenharmony_ci		info->rx_filters |= BIT(HWTSTAMP_FILTER_ALL);
158762306a36Sopenharmony_ci
158862306a36Sopenharmony_ci		return 0;
158962306a36Sopenharmony_ci	default:
159062306a36Sopenharmony_ci		return -EOPNOTSUPP;
159162306a36Sopenharmony_ci	}
159262306a36Sopenharmony_ci}
159362306a36Sopenharmony_ci
159462306a36Sopenharmony_cistatic u32 igc_ethtool_get_priv_flags(struct net_device *netdev)
159562306a36Sopenharmony_ci{
159662306a36Sopenharmony_ci	struct igc_adapter *adapter = netdev_priv(netdev);
159762306a36Sopenharmony_ci	u32 priv_flags = 0;
159862306a36Sopenharmony_ci
159962306a36Sopenharmony_ci	if (adapter->flags & IGC_FLAG_RX_LEGACY)
160062306a36Sopenharmony_ci		priv_flags |= IGC_PRIV_FLAGS_LEGACY_RX;
160162306a36Sopenharmony_ci
160262306a36Sopenharmony_ci	return priv_flags;
160362306a36Sopenharmony_ci}
160462306a36Sopenharmony_ci
160562306a36Sopenharmony_cistatic int igc_ethtool_set_priv_flags(struct net_device *netdev, u32 priv_flags)
160662306a36Sopenharmony_ci{
160762306a36Sopenharmony_ci	struct igc_adapter *adapter = netdev_priv(netdev);
160862306a36Sopenharmony_ci	unsigned int flags = adapter->flags;
160962306a36Sopenharmony_ci
161062306a36Sopenharmony_ci	flags &= ~IGC_FLAG_RX_LEGACY;
161162306a36Sopenharmony_ci	if (priv_flags & IGC_PRIV_FLAGS_LEGACY_RX)
161262306a36Sopenharmony_ci		flags |= IGC_FLAG_RX_LEGACY;
161362306a36Sopenharmony_ci
161462306a36Sopenharmony_ci	if (flags != adapter->flags) {
161562306a36Sopenharmony_ci		adapter->flags = flags;
161662306a36Sopenharmony_ci
161762306a36Sopenharmony_ci		/* reset interface to repopulate queues */
161862306a36Sopenharmony_ci		if (netif_running(netdev))
161962306a36Sopenharmony_ci			igc_reinit_locked(adapter);
162062306a36Sopenharmony_ci	}
162162306a36Sopenharmony_ci
162262306a36Sopenharmony_ci	return 0;
162362306a36Sopenharmony_ci}
162462306a36Sopenharmony_ci
162562306a36Sopenharmony_cistatic int igc_ethtool_get_eee(struct net_device *netdev,
162662306a36Sopenharmony_ci			       struct ethtool_eee *edata)
162762306a36Sopenharmony_ci{
162862306a36Sopenharmony_ci	struct igc_adapter *adapter = netdev_priv(netdev);
162962306a36Sopenharmony_ci	struct igc_hw *hw = &adapter->hw;
163062306a36Sopenharmony_ci	u32 eeer;
163162306a36Sopenharmony_ci
163262306a36Sopenharmony_ci	if (hw->dev_spec._base.eee_enable)
163362306a36Sopenharmony_ci		edata->advertised =
163462306a36Sopenharmony_ci			mmd_eee_adv_to_ethtool_adv_t(adapter->eee_advert);
163562306a36Sopenharmony_ci
163662306a36Sopenharmony_ci	*edata = adapter->eee;
163762306a36Sopenharmony_ci	edata->supported = SUPPORTED_Autoneg;
163862306a36Sopenharmony_ci
163962306a36Sopenharmony_ci	eeer = rd32(IGC_EEER);
164062306a36Sopenharmony_ci
164162306a36Sopenharmony_ci	/* EEE status on negotiated link */
164262306a36Sopenharmony_ci	if (eeer & IGC_EEER_EEE_NEG)
164362306a36Sopenharmony_ci		edata->eee_active = true;
164462306a36Sopenharmony_ci
164562306a36Sopenharmony_ci	if (eeer & IGC_EEER_TX_LPI_EN)
164662306a36Sopenharmony_ci		edata->tx_lpi_enabled = true;
164762306a36Sopenharmony_ci
164862306a36Sopenharmony_ci	edata->eee_enabled = hw->dev_spec._base.eee_enable;
164962306a36Sopenharmony_ci
165062306a36Sopenharmony_ci	edata->advertised = SUPPORTED_Autoneg;
165162306a36Sopenharmony_ci	edata->lp_advertised = SUPPORTED_Autoneg;
165262306a36Sopenharmony_ci
165362306a36Sopenharmony_ci	/* Report correct negotiated EEE status for devices that
165462306a36Sopenharmony_ci	 * wrongly report EEE at half-duplex
165562306a36Sopenharmony_ci	 */
165662306a36Sopenharmony_ci	if (adapter->link_duplex == HALF_DUPLEX) {
165762306a36Sopenharmony_ci		edata->eee_enabled = false;
165862306a36Sopenharmony_ci		edata->eee_active = false;
165962306a36Sopenharmony_ci		edata->tx_lpi_enabled = false;
166062306a36Sopenharmony_ci		edata->advertised &= ~edata->advertised;
166162306a36Sopenharmony_ci	}
166262306a36Sopenharmony_ci
166362306a36Sopenharmony_ci	return 0;
166462306a36Sopenharmony_ci}
166562306a36Sopenharmony_ci
166662306a36Sopenharmony_cistatic int igc_ethtool_set_eee(struct net_device *netdev,
166762306a36Sopenharmony_ci			       struct ethtool_eee *edata)
166862306a36Sopenharmony_ci{
166962306a36Sopenharmony_ci	struct igc_adapter *adapter = netdev_priv(netdev);
167062306a36Sopenharmony_ci	struct igc_hw *hw = &adapter->hw;
167162306a36Sopenharmony_ci	struct ethtool_eee eee_curr;
167262306a36Sopenharmony_ci	s32 ret_val;
167362306a36Sopenharmony_ci
167462306a36Sopenharmony_ci	memset(&eee_curr, 0, sizeof(struct ethtool_eee));
167562306a36Sopenharmony_ci
167662306a36Sopenharmony_ci	ret_val = igc_ethtool_get_eee(netdev, &eee_curr);
167762306a36Sopenharmony_ci	if (ret_val) {
167862306a36Sopenharmony_ci		netdev_err(netdev,
167962306a36Sopenharmony_ci			   "Problem setting EEE advertisement options\n");
168062306a36Sopenharmony_ci		return -EINVAL;
168162306a36Sopenharmony_ci	}
168262306a36Sopenharmony_ci
168362306a36Sopenharmony_ci	if (eee_curr.eee_enabled) {
168462306a36Sopenharmony_ci		if (eee_curr.tx_lpi_enabled != edata->tx_lpi_enabled) {
168562306a36Sopenharmony_ci			netdev_err(netdev,
168662306a36Sopenharmony_ci				   "Setting EEE tx-lpi is not supported\n");
168762306a36Sopenharmony_ci			return -EINVAL;
168862306a36Sopenharmony_ci		}
168962306a36Sopenharmony_ci
169062306a36Sopenharmony_ci		/* Tx LPI timer is not implemented currently */
169162306a36Sopenharmony_ci		if (edata->tx_lpi_timer) {
169262306a36Sopenharmony_ci			netdev_err(netdev,
169362306a36Sopenharmony_ci				   "Setting EEE Tx LPI timer is not supported\n");
169462306a36Sopenharmony_ci			return -EINVAL;
169562306a36Sopenharmony_ci		}
169662306a36Sopenharmony_ci	} else if (!edata->eee_enabled) {
169762306a36Sopenharmony_ci		netdev_err(netdev,
169862306a36Sopenharmony_ci			   "Setting EEE options are not supported with EEE disabled\n");
169962306a36Sopenharmony_ci		return -EINVAL;
170062306a36Sopenharmony_ci	}
170162306a36Sopenharmony_ci
170262306a36Sopenharmony_ci	adapter->eee_advert = ethtool_adv_to_mmd_eee_adv_t(edata->advertised);
170362306a36Sopenharmony_ci	if (hw->dev_spec._base.eee_enable != edata->eee_enabled) {
170462306a36Sopenharmony_ci		hw->dev_spec._base.eee_enable = edata->eee_enabled;
170562306a36Sopenharmony_ci		adapter->flags |= IGC_FLAG_EEE;
170662306a36Sopenharmony_ci
170762306a36Sopenharmony_ci		/* reset link */
170862306a36Sopenharmony_ci		if (netif_running(netdev))
170962306a36Sopenharmony_ci			igc_reinit_locked(adapter);
171062306a36Sopenharmony_ci		else
171162306a36Sopenharmony_ci			igc_reset(adapter);
171262306a36Sopenharmony_ci	}
171362306a36Sopenharmony_ci
171462306a36Sopenharmony_ci	return 0;
171562306a36Sopenharmony_ci}
171662306a36Sopenharmony_ci
171762306a36Sopenharmony_cistatic int igc_ethtool_begin(struct net_device *netdev)
171862306a36Sopenharmony_ci{
171962306a36Sopenharmony_ci	struct igc_adapter *adapter = netdev_priv(netdev);
172062306a36Sopenharmony_ci
172162306a36Sopenharmony_ci	pm_runtime_get_sync(&adapter->pdev->dev);
172262306a36Sopenharmony_ci	return 0;
172362306a36Sopenharmony_ci}
172462306a36Sopenharmony_ci
172562306a36Sopenharmony_cistatic void igc_ethtool_complete(struct net_device *netdev)
172662306a36Sopenharmony_ci{
172762306a36Sopenharmony_ci	struct igc_adapter *adapter = netdev_priv(netdev);
172862306a36Sopenharmony_ci
172962306a36Sopenharmony_ci	pm_runtime_put(&adapter->pdev->dev);
173062306a36Sopenharmony_ci}
173162306a36Sopenharmony_ci
173262306a36Sopenharmony_cistatic int igc_ethtool_get_link_ksettings(struct net_device *netdev,
173362306a36Sopenharmony_ci					  struct ethtool_link_ksettings *cmd)
173462306a36Sopenharmony_ci{
173562306a36Sopenharmony_ci	struct igc_adapter *adapter = netdev_priv(netdev);
173662306a36Sopenharmony_ci	struct igc_hw *hw = &adapter->hw;
173762306a36Sopenharmony_ci	u32 status;
173862306a36Sopenharmony_ci	u32 speed;
173962306a36Sopenharmony_ci
174062306a36Sopenharmony_ci	ethtool_link_ksettings_zero_link_mode(cmd, supported);
174162306a36Sopenharmony_ci	ethtool_link_ksettings_zero_link_mode(cmd, advertising);
174262306a36Sopenharmony_ci
174362306a36Sopenharmony_ci	/* supported link modes */
174462306a36Sopenharmony_ci	ethtool_link_ksettings_add_link_mode(cmd, supported, 10baseT_Half);
174562306a36Sopenharmony_ci	ethtool_link_ksettings_add_link_mode(cmd, supported, 10baseT_Full);
174662306a36Sopenharmony_ci	ethtool_link_ksettings_add_link_mode(cmd, supported, 100baseT_Half);
174762306a36Sopenharmony_ci	ethtool_link_ksettings_add_link_mode(cmd, supported, 100baseT_Full);
174862306a36Sopenharmony_ci	ethtool_link_ksettings_add_link_mode(cmd, supported, 1000baseT_Full);
174962306a36Sopenharmony_ci	ethtool_link_ksettings_add_link_mode(cmd, supported, 2500baseT_Full);
175062306a36Sopenharmony_ci
175162306a36Sopenharmony_ci	/* twisted pair */
175262306a36Sopenharmony_ci	cmd->base.port = PORT_TP;
175362306a36Sopenharmony_ci	cmd->base.phy_address = hw->phy.addr;
175462306a36Sopenharmony_ci	ethtool_link_ksettings_add_link_mode(cmd, supported, TP);
175562306a36Sopenharmony_ci	ethtool_link_ksettings_add_link_mode(cmd, advertising, TP);
175662306a36Sopenharmony_ci
175762306a36Sopenharmony_ci	/* advertising link modes */
175862306a36Sopenharmony_ci	if (hw->phy.autoneg_advertised & ADVERTISE_10_HALF)
175962306a36Sopenharmony_ci		ethtool_link_ksettings_add_link_mode(cmd, advertising, 10baseT_Half);
176062306a36Sopenharmony_ci	if (hw->phy.autoneg_advertised & ADVERTISE_10_FULL)
176162306a36Sopenharmony_ci		ethtool_link_ksettings_add_link_mode(cmd, advertising, 10baseT_Full);
176262306a36Sopenharmony_ci	if (hw->phy.autoneg_advertised & ADVERTISE_100_HALF)
176362306a36Sopenharmony_ci		ethtool_link_ksettings_add_link_mode(cmd, advertising, 100baseT_Half);
176462306a36Sopenharmony_ci	if (hw->phy.autoneg_advertised & ADVERTISE_100_FULL)
176562306a36Sopenharmony_ci		ethtool_link_ksettings_add_link_mode(cmd, advertising, 100baseT_Full);
176662306a36Sopenharmony_ci	if (hw->phy.autoneg_advertised & ADVERTISE_1000_FULL)
176762306a36Sopenharmony_ci		ethtool_link_ksettings_add_link_mode(cmd, advertising, 1000baseT_Full);
176862306a36Sopenharmony_ci	if (hw->phy.autoneg_advertised & ADVERTISE_2500_FULL)
176962306a36Sopenharmony_ci		ethtool_link_ksettings_add_link_mode(cmd, advertising, 2500baseT_Full);
177062306a36Sopenharmony_ci
177162306a36Sopenharmony_ci	/* set autoneg settings */
177262306a36Sopenharmony_ci	if (hw->mac.autoneg == 1) {
177362306a36Sopenharmony_ci		ethtool_link_ksettings_add_link_mode(cmd, supported, Autoneg);
177462306a36Sopenharmony_ci		ethtool_link_ksettings_add_link_mode(cmd, advertising,
177562306a36Sopenharmony_ci						     Autoneg);
177662306a36Sopenharmony_ci	}
177762306a36Sopenharmony_ci
177862306a36Sopenharmony_ci	/* Set pause flow control settings */
177962306a36Sopenharmony_ci	ethtool_link_ksettings_add_link_mode(cmd, supported, Pause);
178062306a36Sopenharmony_ci
178162306a36Sopenharmony_ci	switch (hw->fc.requested_mode) {
178262306a36Sopenharmony_ci	case igc_fc_full:
178362306a36Sopenharmony_ci		ethtool_link_ksettings_add_link_mode(cmd, advertising, Pause);
178462306a36Sopenharmony_ci		break;
178562306a36Sopenharmony_ci	case igc_fc_rx_pause:
178662306a36Sopenharmony_ci		ethtool_link_ksettings_add_link_mode(cmd, advertising, Pause);
178762306a36Sopenharmony_ci		ethtool_link_ksettings_add_link_mode(cmd, advertising,
178862306a36Sopenharmony_ci						     Asym_Pause);
178962306a36Sopenharmony_ci		break;
179062306a36Sopenharmony_ci	case igc_fc_tx_pause:
179162306a36Sopenharmony_ci		ethtool_link_ksettings_add_link_mode(cmd, advertising,
179262306a36Sopenharmony_ci						     Asym_Pause);
179362306a36Sopenharmony_ci		break;
179462306a36Sopenharmony_ci	default:
179562306a36Sopenharmony_ci		break;
179662306a36Sopenharmony_ci	}
179762306a36Sopenharmony_ci
179862306a36Sopenharmony_ci	status = pm_runtime_suspended(&adapter->pdev->dev) ?
179962306a36Sopenharmony_ci		 0 : rd32(IGC_STATUS);
180062306a36Sopenharmony_ci
180162306a36Sopenharmony_ci	if (status & IGC_STATUS_LU) {
180262306a36Sopenharmony_ci		if (status & IGC_STATUS_SPEED_1000) {
180362306a36Sopenharmony_ci			/* For I225, STATUS will indicate 1G speed in both
180462306a36Sopenharmony_ci			 * 1 Gbps and 2.5 Gbps link modes.
180562306a36Sopenharmony_ci			 * An additional bit is used
180662306a36Sopenharmony_ci			 * to differentiate between 1 Gbps and 2.5 Gbps.
180762306a36Sopenharmony_ci			 */
180862306a36Sopenharmony_ci			if (hw->mac.type == igc_i225 &&
180962306a36Sopenharmony_ci			    (status & IGC_STATUS_SPEED_2500)) {
181062306a36Sopenharmony_ci				speed = SPEED_2500;
181162306a36Sopenharmony_ci			} else {
181262306a36Sopenharmony_ci				speed = SPEED_1000;
181362306a36Sopenharmony_ci			}
181462306a36Sopenharmony_ci		} else if (status & IGC_STATUS_SPEED_100) {
181562306a36Sopenharmony_ci			speed = SPEED_100;
181662306a36Sopenharmony_ci		} else {
181762306a36Sopenharmony_ci			speed = SPEED_10;
181862306a36Sopenharmony_ci		}
181962306a36Sopenharmony_ci		if ((status & IGC_STATUS_FD) ||
182062306a36Sopenharmony_ci		    hw->phy.media_type != igc_media_type_copper)
182162306a36Sopenharmony_ci			cmd->base.duplex = DUPLEX_FULL;
182262306a36Sopenharmony_ci		else
182362306a36Sopenharmony_ci			cmd->base.duplex = DUPLEX_HALF;
182462306a36Sopenharmony_ci	} else {
182562306a36Sopenharmony_ci		speed = SPEED_UNKNOWN;
182662306a36Sopenharmony_ci		cmd->base.duplex = DUPLEX_UNKNOWN;
182762306a36Sopenharmony_ci	}
182862306a36Sopenharmony_ci	cmd->base.speed = speed;
182962306a36Sopenharmony_ci	if (hw->mac.autoneg)
183062306a36Sopenharmony_ci		cmd->base.autoneg = AUTONEG_ENABLE;
183162306a36Sopenharmony_ci	else
183262306a36Sopenharmony_ci		cmd->base.autoneg = AUTONEG_DISABLE;
183362306a36Sopenharmony_ci
183462306a36Sopenharmony_ci	/* MDI-X => 2; MDI =>1; Invalid =>0 */
183562306a36Sopenharmony_ci	if (hw->phy.media_type == igc_media_type_copper)
183662306a36Sopenharmony_ci		cmd->base.eth_tp_mdix = hw->phy.is_mdix ? ETH_TP_MDI_X :
183762306a36Sopenharmony_ci						      ETH_TP_MDI;
183862306a36Sopenharmony_ci	else
183962306a36Sopenharmony_ci		cmd->base.eth_tp_mdix = ETH_TP_MDI_INVALID;
184062306a36Sopenharmony_ci
184162306a36Sopenharmony_ci	if (hw->phy.mdix == AUTO_ALL_MODES)
184262306a36Sopenharmony_ci		cmd->base.eth_tp_mdix_ctrl = ETH_TP_MDI_AUTO;
184362306a36Sopenharmony_ci	else
184462306a36Sopenharmony_ci		cmd->base.eth_tp_mdix_ctrl = hw->phy.mdix;
184562306a36Sopenharmony_ci
184662306a36Sopenharmony_ci	return 0;
184762306a36Sopenharmony_ci}
184862306a36Sopenharmony_ci
184962306a36Sopenharmony_cistatic int
185062306a36Sopenharmony_ciigc_ethtool_set_link_ksettings(struct net_device *netdev,
185162306a36Sopenharmony_ci			       const struct ethtool_link_ksettings *cmd)
185262306a36Sopenharmony_ci{
185362306a36Sopenharmony_ci	struct igc_adapter *adapter = netdev_priv(netdev);
185462306a36Sopenharmony_ci	struct net_device *dev = adapter->netdev;
185562306a36Sopenharmony_ci	struct igc_hw *hw = &adapter->hw;
185662306a36Sopenharmony_ci	u16 advertised = 0;
185762306a36Sopenharmony_ci
185862306a36Sopenharmony_ci	/* When adapter in resetting mode, autoneg/speed/duplex
185962306a36Sopenharmony_ci	 * cannot be changed
186062306a36Sopenharmony_ci	 */
186162306a36Sopenharmony_ci	if (igc_check_reset_block(hw)) {
186262306a36Sopenharmony_ci		netdev_err(dev, "Cannot change link characteristics when reset is active\n");
186362306a36Sopenharmony_ci		return -EINVAL;
186462306a36Sopenharmony_ci	}
186562306a36Sopenharmony_ci
186662306a36Sopenharmony_ci	/* MDI setting is only allowed when autoneg enabled because
186762306a36Sopenharmony_ci	 * some hardware doesn't allow MDI setting when speed or
186862306a36Sopenharmony_ci	 * duplex is forced.
186962306a36Sopenharmony_ci	 */
187062306a36Sopenharmony_ci	if (cmd->base.eth_tp_mdix_ctrl) {
187162306a36Sopenharmony_ci		if (cmd->base.eth_tp_mdix_ctrl != ETH_TP_MDI_AUTO &&
187262306a36Sopenharmony_ci		    cmd->base.autoneg != AUTONEG_ENABLE) {
187362306a36Sopenharmony_ci			netdev_err(dev, "Forcing MDI/MDI-X state is not supported when link speed and/or duplex are forced\n");
187462306a36Sopenharmony_ci			return -EINVAL;
187562306a36Sopenharmony_ci		}
187662306a36Sopenharmony_ci	}
187762306a36Sopenharmony_ci
187862306a36Sopenharmony_ci	while (test_and_set_bit(__IGC_RESETTING, &adapter->state))
187962306a36Sopenharmony_ci		usleep_range(1000, 2000);
188062306a36Sopenharmony_ci
188162306a36Sopenharmony_ci	if (ethtool_link_ksettings_test_link_mode(cmd, advertising,
188262306a36Sopenharmony_ci						  2500baseT_Full))
188362306a36Sopenharmony_ci		advertised |= ADVERTISE_2500_FULL;
188462306a36Sopenharmony_ci
188562306a36Sopenharmony_ci	if (ethtool_link_ksettings_test_link_mode(cmd, advertising,
188662306a36Sopenharmony_ci						  1000baseT_Full))
188762306a36Sopenharmony_ci		advertised |= ADVERTISE_1000_FULL;
188862306a36Sopenharmony_ci
188962306a36Sopenharmony_ci	if (ethtool_link_ksettings_test_link_mode(cmd, advertising,
189062306a36Sopenharmony_ci						  100baseT_Full))
189162306a36Sopenharmony_ci		advertised |= ADVERTISE_100_FULL;
189262306a36Sopenharmony_ci
189362306a36Sopenharmony_ci	if (ethtool_link_ksettings_test_link_mode(cmd, advertising,
189462306a36Sopenharmony_ci						  100baseT_Half))
189562306a36Sopenharmony_ci		advertised |= ADVERTISE_100_HALF;
189662306a36Sopenharmony_ci
189762306a36Sopenharmony_ci	if (ethtool_link_ksettings_test_link_mode(cmd, advertising,
189862306a36Sopenharmony_ci						  10baseT_Full))
189962306a36Sopenharmony_ci		advertised |= ADVERTISE_10_FULL;
190062306a36Sopenharmony_ci
190162306a36Sopenharmony_ci	if (ethtool_link_ksettings_test_link_mode(cmd, advertising,
190262306a36Sopenharmony_ci						  10baseT_Half))
190362306a36Sopenharmony_ci		advertised |= ADVERTISE_10_HALF;
190462306a36Sopenharmony_ci
190562306a36Sopenharmony_ci	if (cmd->base.autoneg == AUTONEG_ENABLE) {
190662306a36Sopenharmony_ci		hw->mac.autoneg = 1;
190762306a36Sopenharmony_ci		hw->phy.autoneg_advertised = advertised;
190862306a36Sopenharmony_ci		if (adapter->fc_autoneg)
190962306a36Sopenharmony_ci			hw->fc.requested_mode = igc_fc_default;
191062306a36Sopenharmony_ci	} else {
191162306a36Sopenharmony_ci		netdev_info(dev, "Force mode currently not supported\n");
191262306a36Sopenharmony_ci	}
191362306a36Sopenharmony_ci
191462306a36Sopenharmony_ci	/* MDI-X => 2; MDI => 1; Auto => 3 */
191562306a36Sopenharmony_ci	if (cmd->base.eth_tp_mdix_ctrl) {
191662306a36Sopenharmony_ci		/* fix up the value for auto (3 => 0) as zero is mapped
191762306a36Sopenharmony_ci		 * internally to auto
191862306a36Sopenharmony_ci		 */
191962306a36Sopenharmony_ci		if (cmd->base.eth_tp_mdix_ctrl == ETH_TP_MDI_AUTO)
192062306a36Sopenharmony_ci			hw->phy.mdix = AUTO_ALL_MODES;
192162306a36Sopenharmony_ci		else
192262306a36Sopenharmony_ci			hw->phy.mdix = cmd->base.eth_tp_mdix_ctrl;
192362306a36Sopenharmony_ci	}
192462306a36Sopenharmony_ci
192562306a36Sopenharmony_ci	/* reset the link */
192662306a36Sopenharmony_ci	if (netif_running(adapter->netdev)) {
192762306a36Sopenharmony_ci		igc_down(adapter);
192862306a36Sopenharmony_ci		igc_up(adapter);
192962306a36Sopenharmony_ci	} else {
193062306a36Sopenharmony_ci		igc_reset(adapter);
193162306a36Sopenharmony_ci	}
193262306a36Sopenharmony_ci
193362306a36Sopenharmony_ci	clear_bit(__IGC_RESETTING, &adapter->state);
193462306a36Sopenharmony_ci
193562306a36Sopenharmony_ci	return 0;
193662306a36Sopenharmony_ci}
193762306a36Sopenharmony_ci
193862306a36Sopenharmony_cistatic void igc_ethtool_diag_test(struct net_device *netdev,
193962306a36Sopenharmony_ci				  struct ethtool_test *eth_test, u64 *data)
194062306a36Sopenharmony_ci{
194162306a36Sopenharmony_ci	struct igc_adapter *adapter = netdev_priv(netdev);
194262306a36Sopenharmony_ci	bool if_running = netif_running(netdev);
194362306a36Sopenharmony_ci
194462306a36Sopenharmony_ci	if (eth_test->flags == ETH_TEST_FL_OFFLINE) {
194562306a36Sopenharmony_ci		netdev_info(adapter->netdev, "Offline testing starting");
194662306a36Sopenharmony_ci		set_bit(__IGC_TESTING, &adapter->state);
194762306a36Sopenharmony_ci
194862306a36Sopenharmony_ci		/* Link test performed before hardware reset so autoneg doesn't
194962306a36Sopenharmony_ci		 * interfere with test result
195062306a36Sopenharmony_ci		 */
195162306a36Sopenharmony_ci		if (!igc_link_test(adapter, &data[TEST_LINK]))
195262306a36Sopenharmony_ci			eth_test->flags |= ETH_TEST_FL_FAILED;
195362306a36Sopenharmony_ci
195462306a36Sopenharmony_ci		if (if_running)
195562306a36Sopenharmony_ci			igc_close(netdev);
195662306a36Sopenharmony_ci		else
195762306a36Sopenharmony_ci			igc_reset(adapter);
195862306a36Sopenharmony_ci
195962306a36Sopenharmony_ci		netdev_info(adapter->netdev, "Register testing starting");
196062306a36Sopenharmony_ci		if (!igc_reg_test(adapter, &data[TEST_REG]))
196162306a36Sopenharmony_ci			eth_test->flags |= ETH_TEST_FL_FAILED;
196262306a36Sopenharmony_ci
196362306a36Sopenharmony_ci		igc_reset(adapter);
196462306a36Sopenharmony_ci
196562306a36Sopenharmony_ci		netdev_info(adapter->netdev, "EEPROM testing starting");
196662306a36Sopenharmony_ci		if (!igc_eeprom_test(adapter, &data[TEST_EEP]))
196762306a36Sopenharmony_ci			eth_test->flags |= ETH_TEST_FL_FAILED;
196862306a36Sopenharmony_ci
196962306a36Sopenharmony_ci		igc_reset(adapter);
197062306a36Sopenharmony_ci
197162306a36Sopenharmony_ci		/* loopback and interrupt tests
197262306a36Sopenharmony_ci		 * will be implemented in the future
197362306a36Sopenharmony_ci		 */
197462306a36Sopenharmony_ci		data[TEST_LOOP] = 0;
197562306a36Sopenharmony_ci		data[TEST_IRQ] = 0;
197662306a36Sopenharmony_ci
197762306a36Sopenharmony_ci		clear_bit(__IGC_TESTING, &adapter->state);
197862306a36Sopenharmony_ci		if (if_running)
197962306a36Sopenharmony_ci			igc_open(netdev);
198062306a36Sopenharmony_ci	} else {
198162306a36Sopenharmony_ci		netdev_info(adapter->netdev, "Online testing starting");
198262306a36Sopenharmony_ci
198362306a36Sopenharmony_ci		/* register, eeprom, intr and loopback tests not run online */
198462306a36Sopenharmony_ci		data[TEST_REG] = 0;
198562306a36Sopenharmony_ci		data[TEST_EEP] = 0;
198662306a36Sopenharmony_ci		data[TEST_IRQ] = 0;
198762306a36Sopenharmony_ci		data[TEST_LOOP] = 0;
198862306a36Sopenharmony_ci
198962306a36Sopenharmony_ci		if (!igc_link_test(adapter, &data[TEST_LINK]))
199062306a36Sopenharmony_ci			eth_test->flags |= ETH_TEST_FL_FAILED;
199162306a36Sopenharmony_ci	}
199262306a36Sopenharmony_ci
199362306a36Sopenharmony_ci	msleep_interruptible(4 * 1000);
199462306a36Sopenharmony_ci}
199562306a36Sopenharmony_ci
199662306a36Sopenharmony_cistatic const struct ethtool_ops igc_ethtool_ops = {
199762306a36Sopenharmony_ci	.supported_coalesce_params = ETHTOOL_COALESCE_USECS,
199862306a36Sopenharmony_ci	.get_drvinfo		= igc_ethtool_get_drvinfo,
199962306a36Sopenharmony_ci	.get_regs_len		= igc_ethtool_get_regs_len,
200062306a36Sopenharmony_ci	.get_regs		= igc_ethtool_get_regs,
200162306a36Sopenharmony_ci	.get_wol		= igc_ethtool_get_wol,
200262306a36Sopenharmony_ci	.set_wol		= igc_ethtool_set_wol,
200362306a36Sopenharmony_ci	.get_msglevel		= igc_ethtool_get_msglevel,
200462306a36Sopenharmony_ci	.set_msglevel		= igc_ethtool_set_msglevel,
200562306a36Sopenharmony_ci	.nway_reset		= igc_ethtool_nway_reset,
200662306a36Sopenharmony_ci	.get_link		= igc_ethtool_get_link,
200762306a36Sopenharmony_ci	.get_eeprom_len		= igc_ethtool_get_eeprom_len,
200862306a36Sopenharmony_ci	.get_eeprom		= igc_ethtool_get_eeprom,
200962306a36Sopenharmony_ci	.set_eeprom		= igc_ethtool_set_eeprom,
201062306a36Sopenharmony_ci	.get_ringparam		= igc_ethtool_get_ringparam,
201162306a36Sopenharmony_ci	.set_ringparam		= igc_ethtool_set_ringparam,
201262306a36Sopenharmony_ci	.get_pauseparam		= igc_ethtool_get_pauseparam,
201362306a36Sopenharmony_ci	.set_pauseparam		= igc_ethtool_set_pauseparam,
201462306a36Sopenharmony_ci	.get_strings		= igc_ethtool_get_strings,
201562306a36Sopenharmony_ci	.get_sset_count		= igc_ethtool_get_sset_count,
201662306a36Sopenharmony_ci	.get_ethtool_stats	= igc_ethtool_get_stats,
201762306a36Sopenharmony_ci	.get_coalesce		= igc_ethtool_get_coalesce,
201862306a36Sopenharmony_ci	.set_coalesce		= igc_ethtool_set_coalesce,
201962306a36Sopenharmony_ci	.get_rxnfc		= igc_ethtool_get_rxnfc,
202062306a36Sopenharmony_ci	.set_rxnfc		= igc_ethtool_set_rxnfc,
202162306a36Sopenharmony_ci	.get_rxfh_indir_size	= igc_ethtool_get_rxfh_indir_size,
202262306a36Sopenharmony_ci	.get_rxfh		= igc_ethtool_get_rxfh,
202362306a36Sopenharmony_ci	.set_rxfh		= igc_ethtool_set_rxfh,
202462306a36Sopenharmony_ci	.get_ts_info		= igc_ethtool_get_ts_info,
202562306a36Sopenharmony_ci	.get_channels		= igc_ethtool_get_channels,
202662306a36Sopenharmony_ci	.set_channels		= igc_ethtool_set_channels,
202762306a36Sopenharmony_ci	.get_priv_flags		= igc_ethtool_get_priv_flags,
202862306a36Sopenharmony_ci	.set_priv_flags		= igc_ethtool_set_priv_flags,
202962306a36Sopenharmony_ci	.get_eee		= igc_ethtool_get_eee,
203062306a36Sopenharmony_ci	.set_eee		= igc_ethtool_set_eee,
203162306a36Sopenharmony_ci	.begin			= igc_ethtool_begin,
203262306a36Sopenharmony_ci	.complete		= igc_ethtool_complete,
203362306a36Sopenharmony_ci	.get_link_ksettings	= igc_ethtool_get_link_ksettings,
203462306a36Sopenharmony_ci	.set_link_ksettings	= igc_ethtool_set_link_ksettings,
203562306a36Sopenharmony_ci	.self_test		= igc_ethtool_diag_test,
203662306a36Sopenharmony_ci};
203762306a36Sopenharmony_ci
203862306a36Sopenharmony_civoid igc_ethtool_set_ops(struct net_device *netdev)
203962306a36Sopenharmony_ci{
204062306a36Sopenharmony_ci	netdev->ethtool_ops = &igc_ethtool_ops;
204162306a36Sopenharmony_ci}
2042