162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/* Copyright(c) 1999 - 2006 Intel Corporation. */
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci/* ethtool support for e1000 */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include "e1000.h"
762306a36Sopenharmony_ci#include <linux/jiffies.h>
862306a36Sopenharmony_ci#include <linux/uaccess.h>
962306a36Sopenharmony_ci
1062306a36Sopenharmony_cienum {NETDEV_STATS, E1000_STATS};
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_cistruct e1000_stats {
1362306a36Sopenharmony_ci	char stat_string[ETH_GSTRING_LEN];
1462306a36Sopenharmony_ci	int type;
1562306a36Sopenharmony_ci	int sizeof_stat;
1662306a36Sopenharmony_ci	int stat_offset;
1762306a36Sopenharmony_ci};
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#define E1000_STAT(m)		E1000_STATS, \
2062306a36Sopenharmony_ci				sizeof(((struct e1000_adapter *)0)->m), \
2162306a36Sopenharmony_ci				offsetof(struct e1000_adapter, m)
2262306a36Sopenharmony_ci#define E1000_NETDEV_STAT(m)	NETDEV_STATS, \
2362306a36Sopenharmony_ci				sizeof(((struct net_device *)0)->m), \
2462306a36Sopenharmony_ci				offsetof(struct net_device, m)
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_cistatic const struct e1000_stats e1000_gstrings_stats[] = {
2762306a36Sopenharmony_ci	{ "rx_packets", E1000_STAT(stats.gprc) },
2862306a36Sopenharmony_ci	{ "tx_packets", E1000_STAT(stats.gptc) },
2962306a36Sopenharmony_ci	{ "rx_bytes", E1000_STAT(stats.gorcl) },
3062306a36Sopenharmony_ci	{ "tx_bytes", E1000_STAT(stats.gotcl) },
3162306a36Sopenharmony_ci	{ "rx_broadcast", E1000_STAT(stats.bprc) },
3262306a36Sopenharmony_ci	{ "tx_broadcast", E1000_STAT(stats.bptc) },
3362306a36Sopenharmony_ci	{ "rx_multicast", E1000_STAT(stats.mprc) },
3462306a36Sopenharmony_ci	{ "tx_multicast", E1000_STAT(stats.mptc) },
3562306a36Sopenharmony_ci	{ "rx_errors", E1000_STAT(stats.rxerrc) },
3662306a36Sopenharmony_ci	{ "tx_errors", E1000_STAT(stats.txerrc) },
3762306a36Sopenharmony_ci	{ "tx_dropped", E1000_NETDEV_STAT(stats.tx_dropped) },
3862306a36Sopenharmony_ci	{ "multicast", E1000_STAT(stats.mprc) },
3962306a36Sopenharmony_ci	{ "collisions", E1000_STAT(stats.colc) },
4062306a36Sopenharmony_ci	{ "rx_length_errors", E1000_STAT(stats.rlerrc) },
4162306a36Sopenharmony_ci	{ "rx_over_errors", E1000_NETDEV_STAT(stats.rx_over_errors) },
4262306a36Sopenharmony_ci	{ "rx_crc_errors", E1000_STAT(stats.crcerrs) },
4362306a36Sopenharmony_ci	{ "rx_frame_errors", E1000_NETDEV_STAT(stats.rx_frame_errors) },
4462306a36Sopenharmony_ci	{ "rx_no_buffer_count", E1000_STAT(stats.rnbc) },
4562306a36Sopenharmony_ci	{ "rx_missed_errors", E1000_STAT(stats.mpc) },
4662306a36Sopenharmony_ci	{ "tx_aborted_errors", E1000_STAT(stats.ecol) },
4762306a36Sopenharmony_ci	{ "tx_carrier_errors", E1000_STAT(stats.tncrs) },
4862306a36Sopenharmony_ci	{ "tx_fifo_errors", E1000_NETDEV_STAT(stats.tx_fifo_errors) },
4962306a36Sopenharmony_ci	{ "tx_heartbeat_errors", E1000_NETDEV_STAT(stats.tx_heartbeat_errors) },
5062306a36Sopenharmony_ci	{ "tx_window_errors", E1000_STAT(stats.latecol) },
5162306a36Sopenharmony_ci	{ "tx_abort_late_coll", E1000_STAT(stats.latecol) },
5262306a36Sopenharmony_ci	{ "tx_deferred_ok", E1000_STAT(stats.dc) },
5362306a36Sopenharmony_ci	{ "tx_single_coll_ok", E1000_STAT(stats.scc) },
5462306a36Sopenharmony_ci	{ "tx_multi_coll_ok", E1000_STAT(stats.mcc) },
5562306a36Sopenharmony_ci	{ "tx_timeout_count", E1000_STAT(tx_timeout_count) },
5662306a36Sopenharmony_ci	{ "tx_restart_queue", E1000_STAT(restart_queue) },
5762306a36Sopenharmony_ci	{ "rx_long_length_errors", E1000_STAT(stats.roc) },
5862306a36Sopenharmony_ci	{ "rx_short_length_errors", E1000_STAT(stats.ruc) },
5962306a36Sopenharmony_ci	{ "rx_align_errors", E1000_STAT(stats.algnerrc) },
6062306a36Sopenharmony_ci	{ "tx_tcp_seg_good", E1000_STAT(stats.tsctc) },
6162306a36Sopenharmony_ci	{ "tx_tcp_seg_failed", E1000_STAT(stats.tsctfc) },
6262306a36Sopenharmony_ci	{ "rx_flow_control_xon", E1000_STAT(stats.xonrxc) },
6362306a36Sopenharmony_ci	{ "rx_flow_control_xoff", E1000_STAT(stats.xoffrxc) },
6462306a36Sopenharmony_ci	{ "tx_flow_control_xon", E1000_STAT(stats.xontxc) },
6562306a36Sopenharmony_ci	{ "tx_flow_control_xoff", E1000_STAT(stats.xofftxc) },
6662306a36Sopenharmony_ci	{ "rx_long_byte_count", E1000_STAT(stats.gorcl) },
6762306a36Sopenharmony_ci	{ "rx_csum_offload_good", E1000_STAT(hw_csum_good) },
6862306a36Sopenharmony_ci	{ "rx_csum_offload_errors", E1000_STAT(hw_csum_err) },
6962306a36Sopenharmony_ci	{ "alloc_rx_buff_failed", E1000_STAT(alloc_rx_buff_failed) },
7062306a36Sopenharmony_ci	{ "tx_smbus", E1000_STAT(stats.mgptc) },
7162306a36Sopenharmony_ci	{ "rx_smbus", E1000_STAT(stats.mgprc) },
7262306a36Sopenharmony_ci	{ "dropped_smbus", E1000_STAT(stats.mgpdc) },
7362306a36Sopenharmony_ci};
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci#define E1000_QUEUE_STATS_LEN 0
7662306a36Sopenharmony_ci#define E1000_GLOBAL_STATS_LEN ARRAY_SIZE(e1000_gstrings_stats)
7762306a36Sopenharmony_ci#define E1000_STATS_LEN (E1000_GLOBAL_STATS_LEN + E1000_QUEUE_STATS_LEN)
7862306a36Sopenharmony_cistatic const char e1000_gstrings_test[][ETH_GSTRING_LEN] = {
7962306a36Sopenharmony_ci	"Register test  (offline)", "Eeprom test    (offline)",
8062306a36Sopenharmony_ci	"Interrupt test (offline)", "Loopback test  (offline)",
8162306a36Sopenharmony_ci	"Link test   (on/offline)"
8262306a36Sopenharmony_ci};
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci#define E1000_TEST_LEN	ARRAY_SIZE(e1000_gstrings_test)
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_cistatic int e1000_get_link_ksettings(struct net_device *netdev,
8762306a36Sopenharmony_ci				    struct ethtool_link_ksettings *cmd)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	struct e1000_adapter *adapter = netdev_priv(netdev);
9062306a36Sopenharmony_ci	struct e1000_hw *hw = &adapter->hw;
9162306a36Sopenharmony_ci	u32 supported, advertising;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	if (hw->media_type == e1000_media_type_copper) {
9462306a36Sopenharmony_ci		supported = (SUPPORTED_10baseT_Half |
9562306a36Sopenharmony_ci			     SUPPORTED_10baseT_Full |
9662306a36Sopenharmony_ci			     SUPPORTED_100baseT_Half |
9762306a36Sopenharmony_ci			     SUPPORTED_100baseT_Full |
9862306a36Sopenharmony_ci			     SUPPORTED_1000baseT_Full|
9962306a36Sopenharmony_ci			     SUPPORTED_Autoneg |
10062306a36Sopenharmony_ci			     SUPPORTED_TP);
10162306a36Sopenharmony_ci		advertising = ADVERTISED_TP;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci		if (hw->autoneg == 1) {
10462306a36Sopenharmony_ci			advertising |= ADVERTISED_Autoneg;
10562306a36Sopenharmony_ci			/* the e1000 autoneg seems to match ethtool nicely */
10662306a36Sopenharmony_ci			advertising |= hw->autoneg_advertised;
10762306a36Sopenharmony_ci		}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci		cmd->base.port = PORT_TP;
11062306a36Sopenharmony_ci		cmd->base.phy_address = hw->phy_addr;
11162306a36Sopenharmony_ci	} else {
11262306a36Sopenharmony_ci		supported   = (SUPPORTED_1000baseT_Full |
11362306a36Sopenharmony_ci			       SUPPORTED_FIBRE |
11462306a36Sopenharmony_ci			       SUPPORTED_Autoneg);
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci		advertising = (ADVERTISED_1000baseT_Full |
11762306a36Sopenharmony_ci			       ADVERTISED_FIBRE |
11862306a36Sopenharmony_ci			       ADVERTISED_Autoneg);
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci		cmd->base.port = PORT_FIBRE;
12162306a36Sopenharmony_ci	}
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	if (er32(STATUS) & E1000_STATUS_LU) {
12462306a36Sopenharmony_ci		e1000_get_speed_and_duplex(hw, &adapter->link_speed,
12562306a36Sopenharmony_ci					   &adapter->link_duplex);
12662306a36Sopenharmony_ci		cmd->base.speed = adapter->link_speed;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci		/* unfortunately FULL_DUPLEX != DUPLEX_FULL
12962306a36Sopenharmony_ci		 * and HALF_DUPLEX != DUPLEX_HALF
13062306a36Sopenharmony_ci		 */
13162306a36Sopenharmony_ci		if (adapter->link_duplex == FULL_DUPLEX)
13262306a36Sopenharmony_ci			cmd->base.duplex = DUPLEX_FULL;
13362306a36Sopenharmony_ci		else
13462306a36Sopenharmony_ci			cmd->base.duplex = DUPLEX_HALF;
13562306a36Sopenharmony_ci	} else {
13662306a36Sopenharmony_ci		cmd->base.speed = SPEED_UNKNOWN;
13762306a36Sopenharmony_ci		cmd->base.duplex = DUPLEX_UNKNOWN;
13862306a36Sopenharmony_ci	}
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	cmd->base.autoneg = ((hw->media_type == e1000_media_type_fiber) ||
14162306a36Sopenharmony_ci			 hw->autoneg) ? AUTONEG_ENABLE : AUTONEG_DISABLE;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	/* MDI-X => 1; MDI => 0 */
14462306a36Sopenharmony_ci	if ((hw->media_type == e1000_media_type_copper) &&
14562306a36Sopenharmony_ci	    netif_carrier_ok(netdev))
14662306a36Sopenharmony_ci		cmd->base.eth_tp_mdix = (!!adapter->phy_info.mdix_mode ?
14762306a36Sopenharmony_ci				     ETH_TP_MDI_X : ETH_TP_MDI);
14862306a36Sopenharmony_ci	else
14962306a36Sopenharmony_ci		cmd->base.eth_tp_mdix = ETH_TP_MDI_INVALID;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	if (hw->mdix == AUTO_ALL_MODES)
15262306a36Sopenharmony_ci		cmd->base.eth_tp_mdix_ctrl = ETH_TP_MDI_AUTO;
15362306a36Sopenharmony_ci	else
15462306a36Sopenharmony_ci		cmd->base.eth_tp_mdix_ctrl = hw->mdix;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported,
15762306a36Sopenharmony_ci						supported);
15862306a36Sopenharmony_ci	ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising,
15962306a36Sopenharmony_ci						advertising);
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	return 0;
16262306a36Sopenharmony_ci}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_cistatic int e1000_set_link_ksettings(struct net_device *netdev,
16562306a36Sopenharmony_ci				    const struct ethtool_link_ksettings *cmd)
16662306a36Sopenharmony_ci{
16762306a36Sopenharmony_ci	struct e1000_adapter *adapter = netdev_priv(netdev);
16862306a36Sopenharmony_ci	struct e1000_hw *hw = &adapter->hw;
16962306a36Sopenharmony_ci	u32 advertising;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	ethtool_convert_link_mode_to_legacy_u32(&advertising,
17262306a36Sopenharmony_ci						cmd->link_modes.advertising);
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	/* MDI setting is only allowed when autoneg enabled because
17562306a36Sopenharmony_ci	 * some hardware doesn't allow MDI setting when speed or
17662306a36Sopenharmony_ci	 * duplex is forced.
17762306a36Sopenharmony_ci	 */
17862306a36Sopenharmony_ci	if (cmd->base.eth_tp_mdix_ctrl) {
17962306a36Sopenharmony_ci		if (hw->media_type != e1000_media_type_copper)
18062306a36Sopenharmony_ci			return -EOPNOTSUPP;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci		if ((cmd->base.eth_tp_mdix_ctrl != ETH_TP_MDI_AUTO) &&
18362306a36Sopenharmony_ci		    (cmd->base.autoneg != AUTONEG_ENABLE)) {
18462306a36Sopenharmony_ci			e_err(drv, "forcing MDI/MDI-X state is not supported when link speed and/or duplex are forced\n");
18562306a36Sopenharmony_ci			return -EINVAL;
18662306a36Sopenharmony_ci		}
18762306a36Sopenharmony_ci	}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	while (test_and_set_bit(__E1000_RESETTING, &adapter->flags))
19062306a36Sopenharmony_ci		msleep(1);
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	if (cmd->base.autoneg == AUTONEG_ENABLE) {
19362306a36Sopenharmony_ci		hw->autoneg = 1;
19462306a36Sopenharmony_ci		if (hw->media_type == e1000_media_type_fiber)
19562306a36Sopenharmony_ci			hw->autoneg_advertised = ADVERTISED_1000baseT_Full |
19662306a36Sopenharmony_ci						 ADVERTISED_FIBRE |
19762306a36Sopenharmony_ci						 ADVERTISED_Autoneg;
19862306a36Sopenharmony_ci		else
19962306a36Sopenharmony_ci			hw->autoneg_advertised = advertising |
20062306a36Sopenharmony_ci						 ADVERTISED_TP |
20162306a36Sopenharmony_ci						 ADVERTISED_Autoneg;
20262306a36Sopenharmony_ci	} else {
20362306a36Sopenharmony_ci		u32 speed = cmd->base.speed;
20462306a36Sopenharmony_ci		/* calling this overrides forced MDI setting */
20562306a36Sopenharmony_ci		if (e1000_set_spd_dplx(adapter, speed, cmd->base.duplex)) {
20662306a36Sopenharmony_ci			clear_bit(__E1000_RESETTING, &adapter->flags);
20762306a36Sopenharmony_ci			return -EINVAL;
20862306a36Sopenharmony_ci		}
20962306a36Sopenharmony_ci	}
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	/* MDI-X => 2; MDI => 1; Auto => 3 */
21262306a36Sopenharmony_ci	if (cmd->base.eth_tp_mdix_ctrl) {
21362306a36Sopenharmony_ci		if (cmd->base.eth_tp_mdix_ctrl == ETH_TP_MDI_AUTO)
21462306a36Sopenharmony_ci			hw->mdix = AUTO_ALL_MODES;
21562306a36Sopenharmony_ci		else
21662306a36Sopenharmony_ci			hw->mdix = cmd->base.eth_tp_mdix_ctrl;
21762306a36Sopenharmony_ci	}
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	/* reset the link */
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	if (netif_running(adapter->netdev)) {
22262306a36Sopenharmony_ci		e1000_down(adapter);
22362306a36Sopenharmony_ci		e1000_up(adapter);
22462306a36Sopenharmony_ci	} else {
22562306a36Sopenharmony_ci		e1000_reset(adapter);
22662306a36Sopenharmony_ci	}
22762306a36Sopenharmony_ci	clear_bit(__E1000_RESETTING, &adapter->flags);
22862306a36Sopenharmony_ci	return 0;
22962306a36Sopenharmony_ci}
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_cistatic u32 e1000_get_link(struct net_device *netdev)
23262306a36Sopenharmony_ci{
23362306a36Sopenharmony_ci	struct e1000_adapter *adapter = netdev_priv(netdev);
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	/* If the link is not reported up to netdev, interrupts are disabled,
23662306a36Sopenharmony_ci	 * and so the physical link state may have changed since we last
23762306a36Sopenharmony_ci	 * looked. Set get_link_status to make sure that the true link
23862306a36Sopenharmony_ci	 * state is interrogated, rather than pulling a cached and possibly
23962306a36Sopenharmony_ci	 * stale link state from the driver.
24062306a36Sopenharmony_ci	 */
24162306a36Sopenharmony_ci	if (!netif_carrier_ok(netdev))
24262306a36Sopenharmony_ci		adapter->hw.get_link_status = 1;
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	return e1000_has_link(adapter);
24562306a36Sopenharmony_ci}
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_cistatic void e1000_get_pauseparam(struct net_device *netdev,
24862306a36Sopenharmony_ci				 struct ethtool_pauseparam *pause)
24962306a36Sopenharmony_ci{
25062306a36Sopenharmony_ci	struct e1000_adapter *adapter = netdev_priv(netdev);
25162306a36Sopenharmony_ci	struct e1000_hw *hw = &adapter->hw;
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	pause->autoneg =
25462306a36Sopenharmony_ci		(adapter->fc_autoneg ? AUTONEG_ENABLE : AUTONEG_DISABLE);
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	if (hw->fc == E1000_FC_RX_PAUSE) {
25762306a36Sopenharmony_ci		pause->rx_pause = 1;
25862306a36Sopenharmony_ci	} else if (hw->fc == E1000_FC_TX_PAUSE) {
25962306a36Sopenharmony_ci		pause->tx_pause = 1;
26062306a36Sopenharmony_ci	} else if (hw->fc == E1000_FC_FULL) {
26162306a36Sopenharmony_ci		pause->rx_pause = 1;
26262306a36Sopenharmony_ci		pause->tx_pause = 1;
26362306a36Sopenharmony_ci	}
26462306a36Sopenharmony_ci}
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_cistatic int e1000_set_pauseparam(struct net_device *netdev,
26762306a36Sopenharmony_ci				struct ethtool_pauseparam *pause)
26862306a36Sopenharmony_ci{
26962306a36Sopenharmony_ci	struct e1000_adapter *adapter = netdev_priv(netdev);
27062306a36Sopenharmony_ci	struct e1000_hw *hw = &adapter->hw;
27162306a36Sopenharmony_ci	int retval = 0;
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	adapter->fc_autoneg = pause->autoneg;
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	while (test_and_set_bit(__E1000_RESETTING, &adapter->flags))
27662306a36Sopenharmony_ci		msleep(1);
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	if (pause->rx_pause && pause->tx_pause)
27962306a36Sopenharmony_ci		hw->fc = E1000_FC_FULL;
28062306a36Sopenharmony_ci	else if (pause->rx_pause && !pause->tx_pause)
28162306a36Sopenharmony_ci		hw->fc = E1000_FC_RX_PAUSE;
28262306a36Sopenharmony_ci	else if (!pause->rx_pause && pause->tx_pause)
28362306a36Sopenharmony_ci		hw->fc = E1000_FC_TX_PAUSE;
28462306a36Sopenharmony_ci	else if (!pause->rx_pause && !pause->tx_pause)
28562306a36Sopenharmony_ci		hw->fc = E1000_FC_NONE;
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	hw->original_fc = hw->fc;
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	if (adapter->fc_autoneg == AUTONEG_ENABLE) {
29062306a36Sopenharmony_ci		if (netif_running(adapter->netdev)) {
29162306a36Sopenharmony_ci			e1000_down(adapter);
29262306a36Sopenharmony_ci			e1000_up(adapter);
29362306a36Sopenharmony_ci		} else {
29462306a36Sopenharmony_ci			e1000_reset(adapter);
29562306a36Sopenharmony_ci		}
29662306a36Sopenharmony_ci	} else
29762306a36Sopenharmony_ci		retval = ((hw->media_type == e1000_media_type_fiber) ?
29862306a36Sopenharmony_ci			  e1000_setup_link(hw) : e1000_force_mac_fc(hw));
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	clear_bit(__E1000_RESETTING, &adapter->flags);
30162306a36Sopenharmony_ci	return retval;
30262306a36Sopenharmony_ci}
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_cistatic u32 e1000_get_msglevel(struct net_device *netdev)
30562306a36Sopenharmony_ci{
30662306a36Sopenharmony_ci	struct e1000_adapter *adapter = netdev_priv(netdev);
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	return adapter->msg_enable;
30962306a36Sopenharmony_ci}
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_cistatic void e1000_set_msglevel(struct net_device *netdev, u32 data)
31262306a36Sopenharmony_ci{
31362306a36Sopenharmony_ci	struct e1000_adapter *adapter = netdev_priv(netdev);
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	adapter->msg_enable = data;
31662306a36Sopenharmony_ci}
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_cistatic int e1000_get_regs_len(struct net_device *netdev)
31962306a36Sopenharmony_ci{
32062306a36Sopenharmony_ci#define E1000_REGS_LEN 32
32162306a36Sopenharmony_ci	return E1000_REGS_LEN * sizeof(u32);
32262306a36Sopenharmony_ci}
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_cistatic void e1000_get_regs(struct net_device *netdev, struct ethtool_regs *regs,
32562306a36Sopenharmony_ci			   void *p)
32662306a36Sopenharmony_ci{
32762306a36Sopenharmony_ci	struct e1000_adapter *adapter = netdev_priv(netdev);
32862306a36Sopenharmony_ci	struct e1000_hw *hw = &adapter->hw;
32962306a36Sopenharmony_ci	u32 *regs_buff = p;
33062306a36Sopenharmony_ci	u16 phy_data;
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	memset(p, 0, E1000_REGS_LEN * sizeof(u32));
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	regs->version = (1 << 24) | (hw->revision_id << 16) | hw->device_id;
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	regs_buff[0]  = er32(CTRL);
33762306a36Sopenharmony_ci	regs_buff[1]  = er32(STATUS);
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	regs_buff[2]  = er32(RCTL);
34062306a36Sopenharmony_ci	regs_buff[3]  = er32(RDLEN);
34162306a36Sopenharmony_ci	regs_buff[4]  = er32(RDH);
34262306a36Sopenharmony_ci	regs_buff[5]  = er32(RDT);
34362306a36Sopenharmony_ci	regs_buff[6]  = er32(RDTR);
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	regs_buff[7]  = er32(TCTL);
34662306a36Sopenharmony_ci	regs_buff[8]  = er32(TDLEN);
34762306a36Sopenharmony_ci	regs_buff[9]  = er32(TDH);
34862306a36Sopenharmony_ci	regs_buff[10] = er32(TDT);
34962306a36Sopenharmony_ci	regs_buff[11] = er32(TIDV);
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	regs_buff[12] = hw->phy_type;  /* PHY type (IGP=1, M88=0) */
35262306a36Sopenharmony_ci	if (hw->phy_type == e1000_phy_igp) {
35362306a36Sopenharmony_ci		e1000_write_phy_reg(hw, IGP01E1000_PHY_PAGE_SELECT,
35462306a36Sopenharmony_ci				    IGP01E1000_PHY_AGC_A);
35562306a36Sopenharmony_ci		e1000_read_phy_reg(hw, IGP01E1000_PHY_AGC_A &
35662306a36Sopenharmony_ci				   IGP01E1000_PHY_PAGE_SELECT, &phy_data);
35762306a36Sopenharmony_ci		regs_buff[13] = (u32)phy_data; /* cable length */
35862306a36Sopenharmony_ci		e1000_write_phy_reg(hw, IGP01E1000_PHY_PAGE_SELECT,
35962306a36Sopenharmony_ci				    IGP01E1000_PHY_AGC_B);
36062306a36Sopenharmony_ci		e1000_read_phy_reg(hw, IGP01E1000_PHY_AGC_B &
36162306a36Sopenharmony_ci				   IGP01E1000_PHY_PAGE_SELECT, &phy_data);
36262306a36Sopenharmony_ci		regs_buff[14] = (u32)phy_data; /* cable length */
36362306a36Sopenharmony_ci		e1000_write_phy_reg(hw, IGP01E1000_PHY_PAGE_SELECT,
36462306a36Sopenharmony_ci				    IGP01E1000_PHY_AGC_C);
36562306a36Sopenharmony_ci		e1000_read_phy_reg(hw, IGP01E1000_PHY_AGC_C &
36662306a36Sopenharmony_ci				   IGP01E1000_PHY_PAGE_SELECT, &phy_data);
36762306a36Sopenharmony_ci		regs_buff[15] = (u32)phy_data; /* cable length */
36862306a36Sopenharmony_ci		e1000_write_phy_reg(hw, IGP01E1000_PHY_PAGE_SELECT,
36962306a36Sopenharmony_ci				    IGP01E1000_PHY_AGC_D);
37062306a36Sopenharmony_ci		e1000_read_phy_reg(hw, IGP01E1000_PHY_AGC_D &
37162306a36Sopenharmony_ci				   IGP01E1000_PHY_PAGE_SELECT, &phy_data);
37262306a36Sopenharmony_ci		regs_buff[16] = (u32)phy_data; /* cable length */
37362306a36Sopenharmony_ci		regs_buff[17] = 0; /* extended 10bt distance (not needed) */
37462306a36Sopenharmony_ci		e1000_write_phy_reg(hw, IGP01E1000_PHY_PAGE_SELECT, 0x0);
37562306a36Sopenharmony_ci		e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_STATUS &
37662306a36Sopenharmony_ci				   IGP01E1000_PHY_PAGE_SELECT, &phy_data);
37762306a36Sopenharmony_ci		regs_buff[18] = (u32)phy_data; /* cable polarity */
37862306a36Sopenharmony_ci		e1000_write_phy_reg(hw, IGP01E1000_PHY_PAGE_SELECT,
37962306a36Sopenharmony_ci				    IGP01E1000_PHY_PCS_INIT_REG);
38062306a36Sopenharmony_ci		e1000_read_phy_reg(hw, IGP01E1000_PHY_PCS_INIT_REG &
38162306a36Sopenharmony_ci				   IGP01E1000_PHY_PAGE_SELECT, &phy_data);
38262306a36Sopenharmony_ci		regs_buff[19] = (u32)phy_data; /* cable polarity */
38362306a36Sopenharmony_ci		regs_buff[20] = 0; /* polarity correction enabled (always) */
38462306a36Sopenharmony_ci		regs_buff[22] = 0; /* phy receive errors (unavailable) */
38562306a36Sopenharmony_ci		regs_buff[23] = regs_buff[18]; /* mdix mode */
38662306a36Sopenharmony_ci		e1000_write_phy_reg(hw, IGP01E1000_PHY_PAGE_SELECT, 0x0);
38762306a36Sopenharmony_ci	} else {
38862306a36Sopenharmony_ci		e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_STATUS, &phy_data);
38962306a36Sopenharmony_ci		regs_buff[13] = (u32)phy_data; /* cable length */
39062306a36Sopenharmony_ci		regs_buff[14] = 0;  /* Dummy (to align w/ IGP phy reg dump) */
39162306a36Sopenharmony_ci		regs_buff[15] = 0;  /* Dummy (to align w/ IGP phy reg dump) */
39262306a36Sopenharmony_ci		regs_buff[16] = 0;  /* Dummy (to align w/ IGP phy reg dump) */
39362306a36Sopenharmony_ci		e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, &phy_data);
39462306a36Sopenharmony_ci		regs_buff[17] = (u32)phy_data; /* extended 10bt distance */
39562306a36Sopenharmony_ci		regs_buff[18] = regs_buff[13]; /* cable polarity */
39662306a36Sopenharmony_ci		regs_buff[19] = 0;  /* Dummy (to align w/ IGP phy reg dump) */
39762306a36Sopenharmony_ci		regs_buff[20] = regs_buff[17]; /* polarity correction */
39862306a36Sopenharmony_ci		/* phy receive errors */
39962306a36Sopenharmony_ci		regs_buff[22] = adapter->phy_stats.receive_errors;
40062306a36Sopenharmony_ci		regs_buff[23] = regs_buff[13]; /* mdix mode */
40162306a36Sopenharmony_ci	}
40262306a36Sopenharmony_ci	regs_buff[21] = adapter->phy_stats.idle_errors;  /* phy idle errors */
40362306a36Sopenharmony_ci	e1000_read_phy_reg(hw, PHY_1000T_STATUS, &phy_data);
40462306a36Sopenharmony_ci	regs_buff[24] = (u32)phy_data;  /* phy local receiver status */
40562306a36Sopenharmony_ci	regs_buff[25] = regs_buff[24];  /* phy remote receiver status */
40662306a36Sopenharmony_ci	if (hw->mac_type >= e1000_82540 &&
40762306a36Sopenharmony_ci	    hw->media_type == e1000_media_type_copper) {
40862306a36Sopenharmony_ci		regs_buff[26] = er32(MANC);
40962306a36Sopenharmony_ci	}
41062306a36Sopenharmony_ci}
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_cistatic int e1000_get_eeprom_len(struct net_device *netdev)
41362306a36Sopenharmony_ci{
41462306a36Sopenharmony_ci	struct e1000_adapter *adapter = netdev_priv(netdev);
41562306a36Sopenharmony_ci	struct e1000_hw *hw = &adapter->hw;
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	return hw->eeprom.word_size * 2;
41862306a36Sopenharmony_ci}
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_cistatic int e1000_get_eeprom(struct net_device *netdev,
42162306a36Sopenharmony_ci			    struct ethtool_eeprom *eeprom, u8 *bytes)
42262306a36Sopenharmony_ci{
42362306a36Sopenharmony_ci	struct e1000_adapter *adapter = netdev_priv(netdev);
42462306a36Sopenharmony_ci	struct e1000_hw *hw = &adapter->hw;
42562306a36Sopenharmony_ci	u16 *eeprom_buff;
42662306a36Sopenharmony_ci	int first_word, last_word;
42762306a36Sopenharmony_ci	int ret_val = 0;
42862306a36Sopenharmony_ci	u16 i;
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	if (eeprom->len == 0)
43162306a36Sopenharmony_ci		return -EINVAL;
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	eeprom->magic = hw->vendor_id | (hw->device_id << 16);
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	first_word = eeprom->offset >> 1;
43662306a36Sopenharmony_ci	last_word = (eeprom->offset + eeprom->len - 1) >> 1;
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	eeprom_buff = kmalloc_array(last_word - first_word + 1, sizeof(u16),
43962306a36Sopenharmony_ci				    GFP_KERNEL);
44062306a36Sopenharmony_ci	if (!eeprom_buff)
44162306a36Sopenharmony_ci		return -ENOMEM;
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	if (hw->eeprom.type == e1000_eeprom_spi)
44462306a36Sopenharmony_ci		ret_val = e1000_read_eeprom(hw, first_word,
44562306a36Sopenharmony_ci					    last_word - first_word + 1,
44662306a36Sopenharmony_ci					    eeprom_buff);
44762306a36Sopenharmony_ci	else {
44862306a36Sopenharmony_ci		for (i = 0; i < last_word - first_word + 1; i++) {
44962306a36Sopenharmony_ci			ret_val = e1000_read_eeprom(hw, first_word + i, 1,
45062306a36Sopenharmony_ci						    &eeprom_buff[i]);
45162306a36Sopenharmony_ci			if (ret_val)
45262306a36Sopenharmony_ci				break;
45362306a36Sopenharmony_ci		}
45462306a36Sopenharmony_ci	}
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	/* Device's eeprom is always little-endian, word addressable */
45762306a36Sopenharmony_ci	for (i = 0; i < last_word - first_word + 1; i++)
45862306a36Sopenharmony_ci		le16_to_cpus(&eeprom_buff[i]);
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	memcpy(bytes, (u8 *)eeprom_buff + (eeprom->offset & 1),
46162306a36Sopenharmony_ci	       eeprom->len);
46262306a36Sopenharmony_ci	kfree(eeprom_buff);
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	return ret_val;
46562306a36Sopenharmony_ci}
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_cistatic int e1000_set_eeprom(struct net_device *netdev,
46862306a36Sopenharmony_ci			    struct ethtool_eeprom *eeprom, u8 *bytes)
46962306a36Sopenharmony_ci{
47062306a36Sopenharmony_ci	struct e1000_adapter *adapter = netdev_priv(netdev);
47162306a36Sopenharmony_ci	struct e1000_hw *hw = &adapter->hw;
47262306a36Sopenharmony_ci	u16 *eeprom_buff;
47362306a36Sopenharmony_ci	void *ptr;
47462306a36Sopenharmony_ci	int max_len, first_word, last_word, ret_val = 0;
47562306a36Sopenharmony_ci	u16 i;
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	if (eeprom->len == 0)
47862306a36Sopenharmony_ci		return -EOPNOTSUPP;
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	if (eeprom->magic != (hw->vendor_id | (hw->device_id << 16)))
48162306a36Sopenharmony_ci		return -EFAULT;
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	max_len = hw->eeprom.word_size * 2;
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	first_word = eeprom->offset >> 1;
48662306a36Sopenharmony_ci	last_word = (eeprom->offset + eeprom->len - 1) >> 1;
48762306a36Sopenharmony_ci	eeprom_buff = kmalloc(max_len, GFP_KERNEL);
48862306a36Sopenharmony_ci	if (!eeprom_buff)
48962306a36Sopenharmony_ci		return -ENOMEM;
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci	ptr = (void *)eeprom_buff;
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	if (eeprom->offset & 1) {
49462306a36Sopenharmony_ci		/* need read/modify/write of first changed EEPROM word
49562306a36Sopenharmony_ci		 * only the second byte of the word is being modified
49662306a36Sopenharmony_ci		 */
49762306a36Sopenharmony_ci		ret_val = e1000_read_eeprom(hw, first_word, 1,
49862306a36Sopenharmony_ci					    &eeprom_buff[0]);
49962306a36Sopenharmony_ci		ptr++;
50062306a36Sopenharmony_ci	}
50162306a36Sopenharmony_ci	if (((eeprom->offset + eeprom->len) & 1) && (ret_val == 0)) {
50262306a36Sopenharmony_ci		/* need read/modify/write of last changed EEPROM word
50362306a36Sopenharmony_ci		 * only the first byte of the word is being modified
50462306a36Sopenharmony_ci		 */
50562306a36Sopenharmony_ci		ret_val = e1000_read_eeprom(hw, last_word, 1,
50662306a36Sopenharmony_ci					    &eeprom_buff[last_word - first_word]);
50762306a36Sopenharmony_ci	}
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	/* Device's eeprom is always little-endian, word addressable */
51062306a36Sopenharmony_ci	for (i = 0; i < last_word - first_word + 1; i++)
51162306a36Sopenharmony_ci		le16_to_cpus(&eeprom_buff[i]);
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	memcpy(ptr, bytes, eeprom->len);
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	for (i = 0; i < last_word - first_word + 1; i++)
51662306a36Sopenharmony_ci		cpu_to_le16s(&eeprom_buff[i]);
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	ret_val = e1000_write_eeprom(hw, first_word,
51962306a36Sopenharmony_ci				     last_word - first_word + 1, eeprom_buff);
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	/* Update the checksum over the first part of the EEPROM if needed */
52262306a36Sopenharmony_ci	if ((ret_val == 0) && (first_word <= EEPROM_CHECKSUM_REG))
52362306a36Sopenharmony_ci		e1000_update_eeprom_checksum(hw);
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	kfree(eeprom_buff);
52662306a36Sopenharmony_ci	return ret_val;
52762306a36Sopenharmony_ci}
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_cistatic void e1000_get_drvinfo(struct net_device *netdev,
53062306a36Sopenharmony_ci			      struct ethtool_drvinfo *drvinfo)
53162306a36Sopenharmony_ci{
53262306a36Sopenharmony_ci	struct e1000_adapter *adapter = netdev_priv(netdev);
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	strscpy(drvinfo->driver,  e1000_driver_name,
53562306a36Sopenharmony_ci		sizeof(drvinfo->driver));
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	strscpy(drvinfo->bus_info, pci_name(adapter->pdev),
53862306a36Sopenharmony_ci		sizeof(drvinfo->bus_info));
53962306a36Sopenharmony_ci}
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_cistatic void e1000_get_ringparam(struct net_device *netdev,
54262306a36Sopenharmony_ci				struct ethtool_ringparam *ring,
54362306a36Sopenharmony_ci				struct kernel_ethtool_ringparam *kernel_ring,
54462306a36Sopenharmony_ci				struct netlink_ext_ack *extack)
54562306a36Sopenharmony_ci{
54662306a36Sopenharmony_ci	struct e1000_adapter *adapter = netdev_priv(netdev);
54762306a36Sopenharmony_ci	struct e1000_hw *hw = &adapter->hw;
54862306a36Sopenharmony_ci	e1000_mac_type mac_type = hw->mac_type;
54962306a36Sopenharmony_ci	struct e1000_tx_ring *txdr = adapter->tx_ring;
55062306a36Sopenharmony_ci	struct e1000_rx_ring *rxdr = adapter->rx_ring;
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	ring->rx_max_pending = (mac_type < e1000_82544) ? E1000_MAX_RXD :
55362306a36Sopenharmony_ci		E1000_MAX_82544_RXD;
55462306a36Sopenharmony_ci	ring->tx_max_pending = (mac_type < e1000_82544) ? E1000_MAX_TXD :
55562306a36Sopenharmony_ci		E1000_MAX_82544_TXD;
55662306a36Sopenharmony_ci	ring->rx_pending = rxdr->count;
55762306a36Sopenharmony_ci	ring->tx_pending = txdr->count;
55862306a36Sopenharmony_ci}
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_cistatic int e1000_set_ringparam(struct net_device *netdev,
56162306a36Sopenharmony_ci			       struct ethtool_ringparam *ring,
56262306a36Sopenharmony_ci			       struct kernel_ethtool_ringparam *kernel_ring,
56362306a36Sopenharmony_ci			       struct netlink_ext_ack *extack)
56462306a36Sopenharmony_ci{
56562306a36Sopenharmony_ci	struct e1000_adapter *adapter = netdev_priv(netdev);
56662306a36Sopenharmony_ci	struct e1000_hw *hw = &adapter->hw;
56762306a36Sopenharmony_ci	e1000_mac_type mac_type = hw->mac_type;
56862306a36Sopenharmony_ci	struct e1000_tx_ring *txdr, *tx_old;
56962306a36Sopenharmony_ci	struct e1000_rx_ring *rxdr, *rx_old;
57062306a36Sopenharmony_ci	int i, err;
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	if ((ring->rx_mini_pending) || (ring->rx_jumbo_pending))
57362306a36Sopenharmony_ci		return -EINVAL;
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci	while (test_and_set_bit(__E1000_RESETTING, &adapter->flags))
57662306a36Sopenharmony_ci		msleep(1);
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci	if (netif_running(adapter->netdev))
57962306a36Sopenharmony_ci		e1000_down(adapter);
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci	tx_old = adapter->tx_ring;
58262306a36Sopenharmony_ci	rx_old = adapter->rx_ring;
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci	err = -ENOMEM;
58562306a36Sopenharmony_ci	txdr = kcalloc(adapter->num_tx_queues, sizeof(struct e1000_tx_ring),
58662306a36Sopenharmony_ci		       GFP_KERNEL);
58762306a36Sopenharmony_ci	if (!txdr)
58862306a36Sopenharmony_ci		goto err_alloc_tx;
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci	rxdr = kcalloc(adapter->num_rx_queues, sizeof(struct e1000_rx_ring),
59162306a36Sopenharmony_ci		       GFP_KERNEL);
59262306a36Sopenharmony_ci	if (!rxdr)
59362306a36Sopenharmony_ci		goto err_alloc_rx;
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci	adapter->tx_ring = txdr;
59662306a36Sopenharmony_ci	adapter->rx_ring = rxdr;
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci	rxdr->count = max(ring->rx_pending, (u32)E1000_MIN_RXD);
59962306a36Sopenharmony_ci	rxdr->count = min(rxdr->count, (u32)(mac_type < e1000_82544 ?
60062306a36Sopenharmony_ci			  E1000_MAX_RXD : E1000_MAX_82544_RXD));
60162306a36Sopenharmony_ci	rxdr->count = ALIGN(rxdr->count, REQ_RX_DESCRIPTOR_MULTIPLE);
60262306a36Sopenharmony_ci	txdr->count = max(ring->tx_pending, (u32)E1000_MIN_TXD);
60362306a36Sopenharmony_ci	txdr->count = min(txdr->count, (u32)(mac_type < e1000_82544 ?
60462306a36Sopenharmony_ci			  E1000_MAX_TXD : E1000_MAX_82544_TXD));
60562306a36Sopenharmony_ci	txdr->count = ALIGN(txdr->count, REQ_TX_DESCRIPTOR_MULTIPLE);
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci	for (i = 0; i < adapter->num_tx_queues; i++)
60862306a36Sopenharmony_ci		txdr[i].count = txdr->count;
60962306a36Sopenharmony_ci	for (i = 0; i < adapter->num_rx_queues; i++)
61062306a36Sopenharmony_ci		rxdr[i].count = rxdr->count;
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci	err = 0;
61362306a36Sopenharmony_ci	if (netif_running(adapter->netdev)) {
61462306a36Sopenharmony_ci		/* Try to get new resources before deleting old */
61562306a36Sopenharmony_ci		err = e1000_setup_all_rx_resources(adapter);
61662306a36Sopenharmony_ci		if (err)
61762306a36Sopenharmony_ci			goto err_setup_rx;
61862306a36Sopenharmony_ci		err = e1000_setup_all_tx_resources(adapter);
61962306a36Sopenharmony_ci		if (err)
62062306a36Sopenharmony_ci			goto err_setup_tx;
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci		/* save the new, restore the old in order to free it,
62362306a36Sopenharmony_ci		 * then restore the new back again
62462306a36Sopenharmony_ci		 */
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci		adapter->rx_ring = rx_old;
62762306a36Sopenharmony_ci		adapter->tx_ring = tx_old;
62862306a36Sopenharmony_ci		e1000_free_all_rx_resources(adapter);
62962306a36Sopenharmony_ci		e1000_free_all_tx_resources(adapter);
63062306a36Sopenharmony_ci		adapter->rx_ring = rxdr;
63162306a36Sopenharmony_ci		adapter->tx_ring = txdr;
63262306a36Sopenharmony_ci		err = e1000_up(adapter);
63362306a36Sopenharmony_ci	}
63462306a36Sopenharmony_ci	kfree(tx_old);
63562306a36Sopenharmony_ci	kfree(rx_old);
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci	clear_bit(__E1000_RESETTING, &adapter->flags);
63862306a36Sopenharmony_ci	return err;
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_cierr_setup_tx:
64162306a36Sopenharmony_ci	e1000_free_all_rx_resources(adapter);
64262306a36Sopenharmony_cierr_setup_rx:
64362306a36Sopenharmony_ci	adapter->rx_ring = rx_old;
64462306a36Sopenharmony_ci	adapter->tx_ring = tx_old;
64562306a36Sopenharmony_ci	kfree(rxdr);
64662306a36Sopenharmony_cierr_alloc_rx:
64762306a36Sopenharmony_ci	kfree(txdr);
64862306a36Sopenharmony_cierr_alloc_tx:
64962306a36Sopenharmony_ci	if (netif_running(adapter->netdev))
65062306a36Sopenharmony_ci		e1000_up(adapter);
65162306a36Sopenharmony_ci	clear_bit(__E1000_RESETTING, &adapter->flags);
65262306a36Sopenharmony_ci	return err;
65362306a36Sopenharmony_ci}
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_cistatic bool reg_pattern_test(struct e1000_adapter *adapter, u64 *data, int reg,
65662306a36Sopenharmony_ci			     u32 mask, u32 write)
65762306a36Sopenharmony_ci{
65862306a36Sopenharmony_ci	struct e1000_hw *hw = &adapter->hw;
65962306a36Sopenharmony_ci	static const u32 test[] = {
66062306a36Sopenharmony_ci		0x5A5A5A5A, 0xA5A5A5A5, 0x00000000, 0xFFFFFFFF
66162306a36Sopenharmony_ci	};
66262306a36Sopenharmony_ci	u8 __iomem *address = hw->hw_addr + reg;
66362306a36Sopenharmony_ci	u32 read;
66462306a36Sopenharmony_ci	int i;
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(test); i++) {
66762306a36Sopenharmony_ci		writel(write & test[i], address);
66862306a36Sopenharmony_ci		read = readl(address);
66962306a36Sopenharmony_ci		if (read != (write & test[i] & mask)) {
67062306a36Sopenharmony_ci			e_err(drv, "pattern test reg %04X failed: "
67162306a36Sopenharmony_ci			      "got 0x%08X expected 0x%08X\n",
67262306a36Sopenharmony_ci			      reg, read, (write & test[i] & mask));
67362306a36Sopenharmony_ci			*data = reg;
67462306a36Sopenharmony_ci			return true;
67562306a36Sopenharmony_ci		}
67662306a36Sopenharmony_ci	}
67762306a36Sopenharmony_ci	return false;
67862306a36Sopenharmony_ci}
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_cistatic bool reg_set_and_check(struct e1000_adapter *adapter, u64 *data, int reg,
68162306a36Sopenharmony_ci			      u32 mask, u32 write)
68262306a36Sopenharmony_ci{
68362306a36Sopenharmony_ci	struct e1000_hw *hw = &adapter->hw;
68462306a36Sopenharmony_ci	u8 __iomem *address = hw->hw_addr + reg;
68562306a36Sopenharmony_ci	u32 read;
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci	writel(write & mask, address);
68862306a36Sopenharmony_ci	read = readl(address);
68962306a36Sopenharmony_ci	if ((read & mask) != (write & mask)) {
69062306a36Sopenharmony_ci		e_err(drv, "set/check reg %04X test failed: "
69162306a36Sopenharmony_ci		      "got 0x%08X expected 0x%08X\n",
69262306a36Sopenharmony_ci		      reg, (read & mask), (write & mask));
69362306a36Sopenharmony_ci		*data = reg;
69462306a36Sopenharmony_ci		return true;
69562306a36Sopenharmony_ci	}
69662306a36Sopenharmony_ci	return false;
69762306a36Sopenharmony_ci}
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci#define REG_PATTERN_TEST(reg, mask, write)			     \
70062306a36Sopenharmony_ci	do {							     \
70162306a36Sopenharmony_ci		if (reg_pattern_test(adapter, data,		     \
70262306a36Sopenharmony_ci			     (hw->mac_type >= e1000_82543)   \
70362306a36Sopenharmony_ci			     ? E1000_##reg : E1000_82542_##reg,	     \
70462306a36Sopenharmony_ci			     mask, write))			     \
70562306a36Sopenharmony_ci			return 1;				     \
70662306a36Sopenharmony_ci	} while (0)
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci#define REG_SET_AND_CHECK(reg, mask, write)			     \
70962306a36Sopenharmony_ci	do {							     \
71062306a36Sopenharmony_ci		if (reg_set_and_check(adapter, data,		     \
71162306a36Sopenharmony_ci			      (hw->mac_type >= e1000_82543)  \
71262306a36Sopenharmony_ci			      ? E1000_##reg : E1000_82542_##reg,     \
71362306a36Sopenharmony_ci			      mask, write))			     \
71462306a36Sopenharmony_ci			return 1;				     \
71562306a36Sopenharmony_ci	} while (0)
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_cistatic int e1000_reg_test(struct e1000_adapter *adapter, u64 *data)
71862306a36Sopenharmony_ci{
71962306a36Sopenharmony_ci	u32 value, before, after;
72062306a36Sopenharmony_ci	u32 i, toggle;
72162306a36Sopenharmony_ci	struct e1000_hw *hw = &adapter->hw;
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci	/* The status register is Read Only, so a write should fail.
72462306a36Sopenharmony_ci	 * Some bits that get toggled are ignored.
72562306a36Sopenharmony_ci	 */
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci	/* there are several bits on newer hardware that are r/w */
72862306a36Sopenharmony_ci	toggle = 0xFFFFF833;
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci	before = er32(STATUS);
73162306a36Sopenharmony_ci	value = (er32(STATUS) & toggle);
73262306a36Sopenharmony_ci	ew32(STATUS, toggle);
73362306a36Sopenharmony_ci	after = er32(STATUS) & toggle;
73462306a36Sopenharmony_ci	if (value != after) {
73562306a36Sopenharmony_ci		e_err(drv, "failed STATUS register test got: "
73662306a36Sopenharmony_ci		      "0x%08X expected: 0x%08X\n", after, value);
73762306a36Sopenharmony_ci		*data = 1;
73862306a36Sopenharmony_ci		return 1;
73962306a36Sopenharmony_ci	}
74062306a36Sopenharmony_ci	/* restore previous status */
74162306a36Sopenharmony_ci	ew32(STATUS, before);
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci	REG_PATTERN_TEST(FCAL, 0xFFFFFFFF, 0xFFFFFFFF);
74462306a36Sopenharmony_ci	REG_PATTERN_TEST(FCAH, 0x0000FFFF, 0xFFFFFFFF);
74562306a36Sopenharmony_ci	REG_PATTERN_TEST(FCT, 0x0000FFFF, 0xFFFFFFFF);
74662306a36Sopenharmony_ci	REG_PATTERN_TEST(VET, 0x0000FFFF, 0xFFFFFFFF);
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci	REG_PATTERN_TEST(RDTR, 0x0000FFFF, 0xFFFFFFFF);
74962306a36Sopenharmony_ci	REG_PATTERN_TEST(RDBAH, 0xFFFFFFFF, 0xFFFFFFFF);
75062306a36Sopenharmony_ci	REG_PATTERN_TEST(RDLEN, 0x000FFF80, 0x000FFFFF);
75162306a36Sopenharmony_ci	REG_PATTERN_TEST(RDH, 0x0000FFFF, 0x0000FFFF);
75262306a36Sopenharmony_ci	REG_PATTERN_TEST(RDT, 0x0000FFFF, 0x0000FFFF);
75362306a36Sopenharmony_ci	REG_PATTERN_TEST(FCRTH, 0x0000FFF8, 0x0000FFF8);
75462306a36Sopenharmony_ci	REG_PATTERN_TEST(FCTTV, 0x0000FFFF, 0x0000FFFF);
75562306a36Sopenharmony_ci	REG_PATTERN_TEST(TIPG, 0x3FFFFFFF, 0x3FFFFFFF);
75662306a36Sopenharmony_ci	REG_PATTERN_TEST(TDBAH, 0xFFFFFFFF, 0xFFFFFFFF);
75762306a36Sopenharmony_ci	REG_PATTERN_TEST(TDLEN, 0x000FFF80, 0x000FFFFF);
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_ci	REG_SET_AND_CHECK(RCTL, 0xFFFFFFFF, 0x00000000);
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci	before = 0x06DFB3FE;
76262306a36Sopenharmony_ci	REG_SET_AND_CHECK(RCTL, before, 0x003FFFFB);
76362306a36Sopenharmony_ci	REG_SET_AND_CHECK(TCTL, 0xFFFFFFFF, 0x00000000);
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci	if (hw->mac_type >= e1000_82543) {
76662306a36Sopenharmony_ci		REG_SET_AND_CHECK(RCTL, before, 0xFFFFFFFF);
76762306a36Sopenharmony_ci		REG_PATTERN_TEST(RDBAL, 0xFFFFFFF0, 0xFFFFFFFF);
76862306a36Sopenharmony_ci		REG_PATTERN_TEST(TXCW, 0xC000FFFF, 0x0000FFFF);
76962306a36Sopenharmony_ci		REG_PATTERN_TEST(TDBAL, 0xFFFFFFF0, 0xFFFFFFFF);
77062306a36Sopenharmony_ci		REG_PATTERN_TEST(TIDV, 0x0000FFFF, 0x0000FFFF);
77162306a36Sopenharmony_ci		value = E1000_RAR_ENTRIES;
77262306a36Sopenharmony_ci		for (i = 0; i < value; i++) {
77362306a36Sopenharmony_ci			REG_PATTERN_TEST(RA + (((i << 1) + 1) << 2),
77462306a36Sopenharmony_ci					 0x8003FFFF, 0xFFFFFFFF);
77562306a36Sopenharmony_ci		}
77662306a36Sopenharmony_ci	} else {
77762306a36Sopenharmony_ci		REG_SET_AND_CHECK(RCTL, 0xFFFFFFFF, 0x01FFFFFF);
77862306a36Sopenharmony_ci		REG_PATTERN_TEST(RDBAL, 0xFFFFF000, 0xFFFFFFFF);
77962306a36Sopenharmony_ci		REG_PATTERN_TEST(TXCW, 0x0000FFFF, 0x0000FFFF);
78062306a36Sopenharmony_ci		REG_PATTERN_TEST(TDBAL, 0xFFFFF000, 0xFFFFFFFF);
78162306a36Sopenharmony_ci	}
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ci	value = E1000_MC_TBL_SIZE;
78462306a36Sopenharmony_ci	for (i = 0; i < value; i++)
78562306a36Sopenharmony_ci		REG_PATTERN_TEST(MTA + (i << 2), 0xFFFFFFFF, 0xFFFFFFFF);
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci	*data = 0;
78862306a36Sopenharmony_ci	return 0;
78962306a36Sopenharmony_ci}
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_cistatic int e1000_eeprom_test(struct e1000_adapter *adapter, u64 *data)
79262306a36Sopenharmony_ci{
79362306a36Sopenharmony_ci	struct e1000_hw *hw = &adapter->hw;
79462306a36Sopenharmony_ci	u16 temp;
79562306a36Sopenharmony_ci	u16 checksum = 0;
79662306a36Sopenharmony_ci	u16 i;
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ci	*data = 0;
79962306a36Sopenharmony_ci	/* Read and add up the contents of the EEPROM */
80062306a36Sopenharmony_ci	for (i = 0; i < (EEPROM_CHECKSUM_REG + 1); i++) {
80162306a36Sopenharmony_ci		if ((e1000_read_eeprom(hw, i, 1, &temp)) < 0) {
80262306a36Sopenharmony_ci			*data = 1;
80362306a36Sopenharmony_ci			break;
80462306a36Sopenharmony_ci		}
80562306a36Sopenharmony_ci		checksum += temp;
80662306a36Sopenharmony_ci	}
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ci	/* If Checksum is not Correct return error else test passed */
80962306a36Sopenharmony_ci	if ((checksum != (u16)EEPROM_SUM) && !(*data))
81062306a36Sopenharmony_ci		*data = 2;
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ci	return *data;
81362306a36Sopenharmony_ci}
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_cistatic irqreturn_t e1000_test_intr(int irq, void *data)
81662306a36Sopenharmony_ci{
81762306a36Sopenharmony_ci	struct net_device *netdev = (struct net_device *)data;
81862306a36Sopenharmony_ci	struct e1000_adapter *adapter = netdev_priv(netdev);
81962306a36Sopenharmony_ci	struct e1000_hw *hw = &adapter->hw;
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_ci	adapter->test_icr |= er32(ICR);
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci	return IRQ_HANDLED;
82462306a36Sopenharmony_ci}
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_cistatic int e1000_intr_test(struct e1000_adapter *adapter, u64 *data)
82762306a36Sopenharmony_ci{
82862306a36Sopenharmony_ci	struct net_device *netdev = adapter->netdev;
82962306a36Sopenharmony_ci	u32 mask, i = 0;
83062306a36Sopenharmony_ci	bool shared_int = true;
83162306a36Sopenharmony_ci	u32 irq = adapter->pdev->irq;
83262306a36Sopenharmony_ci	struct e1000_hw *hw = &adapter->hw;
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_ci	*data = 0;
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci	/* NOTE: we don't test MSI interrupts here, yet
83762306a36Sopenharmony_ci	 * Hook up test interrupt handler just for this test
83862306a36Sopenharmony_ci	 */
83962306a36Sopenharmony_ci	if (!request_irq(irq, e1000_test_intr, IRQF_PROBE_SHARED, netdev->name,
84062306a36Sopenharmony_ci			 netdev))
84162306a36Sopenharmony_ci		shared_int = false;
84262306a36Sopenharmony_ci	else if (request_irq(irq, e1000_test_intr, IRQF_SHARED,
84362306a36Sopenharmony_ci			     netdev->name, netdev)) {
84462306a36Sopenharmony_ci		*data = 1;
84562306a36Sopenharmony_ci		return -1;
84662306a36Sopenharmony_ci	}
84762306a36Sopenharmony_ci	e_info(hw, "testing %s interrupt\n", (shared_int ?
84862306a36Sopenharmony_ci	       "shared" : "unshared"));
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_ci	/* Disable all the interrupts */
85162306a36Sopenharmony_ci	ew32(IMC, 0xFFFFFFFF);
85262306a36Sopenharmony_ci	E1000_WRITE_FLUSH();
85362306a36Sopenharmony_ci	msleep(10);
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_ci	/* Test each interrupt */
85662306a36Sopenharmony_ci	for (; i < 10; i++) {
85762306a36Sopenharmony_ci		/* Interrupt to test */
85862306a36Sopenharmony_ci		mask = 1 << i;
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ci		if (!shared_int) {
86162306a36Sopenharmony_ci			/* Disable the interrupt to be reported in
86262306a36Sopenharmony_ci			 * the cause register and then force the same
86362306a36Sopenharmony_ci			 * interrupt and see if one gets posted.  If
86462306a36Sopenharmony_ci			 * an interrupt was posted to the bus, the
86562306a36Sopenharmony_ci			 * test failed.
86662306a36Sopenharmony_ci			 */
86762306a36Sopenharmony_ci			adapter->test_icr = 0;
86862306a36Sopenharmony_ci			ew32(IMC, mask);
86962306a36Sopenharmony_ci			ew32(ICS, mask);
87062306a36Sopenharmony_ci			E1000_WRITE_FLUSH();
87162306a36Sopenharmony_ci			msleep(10);
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_ci			if (adapter->test_icr & mask) {
87462306a36Sopenharmony_ci				*data = 3;
87562306a36Sopenharmony_ci				break;
87662306a36Sopenharmony_ci			}
87762306a36Sopenharmony_ci		}
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_ci		/* Enable the interrupt to be reported in
88062306a36Sopenharmony_ci		 * the cause register and then force the same
88162306a36Sopenharmony_ci		 * interrupt and see if one gets posted.  If
88262306a36Sopenharmony_ci		 * an interrupt was not posted to the bus, the
88362306a36Sopenharmony_ci		 * test failed.
88462306a36Sopenharmony_ci		 */
88562306a36Sopenharmony_ci		adapter->test_icr = 0;
88662306a36Sopenharmony_ci		ew32(IMS, mask);
88762306a36Sopenharmony_ci		ew32(ICS, mask);
88862306a36Sopenharmony_ci		E1000_WRITE_FLUSH();
88962306a36Sopenharmony_ci		msleep(10);
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ci		if (!(adapter->test_icr & mask)) {
89262306a36Sopenharmony_ci			*data = 4;
89362306a36Sopenharmony_ci			break;
89462306a36Sopenharmony_ci		}
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_ci		if (!shared_int) {
89762306a36Sopenharmony_ci			/* Disable the other interrupts to be reported in
89862306a36Sopenharmony_ci			 * the cause register and then force the other
89962306a36Sopenharmony_ci			 * interrupts and see if any get posted.  If
90062306a36Sopenharmony_ci			 * an interrupt was posted to the bus, the
90162306a36Sopenharmony_ci			 * test failed.
90262306a36Sopenharmony_ci			 */
90362306a36Sopenharmony_ci			adapter->test_icr = 0;
90462306a36Sopenharmony_ci			ew32(IMC, ~mask & 0x00007FFF);
90562306a36Sopenharmony_ci			ew32(ICS, ~mask & 0x00007FFF);
90662306a36Sopenharmony_ci			E1000_WRITE_FLUSH();
90762306a36Sopenharmony_ci			msleep(10);
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_ci			if (adapter->test_icr) {
91062306a36Sopenharmony_ci				*data = 5;
91162306a36Sopenharmony_ci				break;
91262306a36Sopenharmony_ci			}
91362306a36Sopenharmony_ci		}
91462306a36Sopenharmony_ci	}
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci	/* Disable all the interrupts */
91762306a36Sopenharmony_ci	ew32(IMC, 0xFFFFFFFF);
91862306a36Sopenharmony_ci	E1000_WRITE_FLUSH();
91962306a36Sopenharmony_ci	msleep(10);
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ci	/* Unhook test interrupt handler */
92262306a36Sopenharmony_ci	free_irq(irq, netdev);
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_ci	return *data;
92562306a36Sopenharmony_ci}
92662306a36Sopenharmony_ci
92762306a36Sopenharmony_cistatic void e1000_free_desc_rings(struct e1000_adapter *adapter)
92862306a36Sopenharmony_ci{
92962306a36Sopenharmony_ci	struct e1000_tx_ring *txdr = &adapter->test_tx_ring;
93062306a36Sopenharmony_ci	struct e1000_rx_ring *rxdr = &adapter->test_rx_ring;
93162306a36Sopenharmony_ci	struct pci_dev *pdev = adapter->pdev;
93262306a36Sopenharmony_ci	int i;
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_ci	if (txdr->desc && txdr->buffer_info) {
93562306a36Sopenharmony_ci		for (i = 0; i < txdr->count; i++) {
93662306a36Sopenharmony_ci			if (txdr->buffer_info[i].dma)
93762306a36Sopenharmony_ci				dma_unmap_single(&pdev->dev,
93862306a36Sopenharmony_ci						 txdr->buffer_info[i].dma,
93962306a36Sopenharmony_ci						 txdr->buffer_info[i].length,
94062306a36Sopenharmony_ci						 DMA_TO_DEVICE);
94162306a36Sopenharmony_ci			dev_kfree_skb(txdr->buffer_info[i].skb);
94262306a36Sopenharmony_ci		}
94362306a36Sopenharmony_ci	}
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_ci	if (rxdr->desc && rxdr->buffer_info) {
94662306a36Sopenharmony_ci		for (i = 0; i < rxdr->count; i++) {
94762306a36Sopenharmony_ci			if (rxdr->buffer_info[i].dma)
94862306a36Sopenharmony_ci				dma_unmap_single(&pdev->dev,
94962306a36Sopenharmony_ci						 rxdr->buffer_info[i].dma,
95062306a36Sopenharmony_ci						 E1000_RXBUFFER_2048,
95162306a36Sopenharmony_ci						 DMA_FROM_DEVICE);
95262306a36Sopenharmony_ci			kfree(rxdr->buffer_info[i].rxbuf.data);
95362306a36Sopenharmony_ci		}
95462306a36Sopenharmony_ci	}
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_ci	if (txdr->desc) {
95762306a36Sopenharmony_ci		dma_free_coherent(&pdev->dev, txdr->size, txdr->desc,
95862306a36Sopenharmony_ci				  txdr->dma);
95962306a36Sopenharmony_ci		txdr->desc = NULL;
96062306a36Sopenharmony_ci	}
96162306a36Sopenharmony_ci	if (rxdr->desc) {
96262306a36Sopenharmony_ci		dma_free_coherent(&pdev->dev, rxdr->size, rxdr->desc,
96362306a36Sopenharmony_ci				  rxdr->dma);
96462306a36Sopenharmony_ci		rxdr->desc = NULL;
96562306a36Sopenharmony_ci	}
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_ci	kfree(txdr->buffer_info);
96862306a36Sopenharmony_ci	txdr->buffer_info = NULL;
96962306a36Sopenharmony_ci	kfree(rxdr->buffer_info);
97062306a36Sopenharmony_ci	rxdr->buffer_info = NULL;
97162306a36Sopenharmony_ci}
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_cistatic int e1000_setup_desc_rings(struct e1000_adapter *adapter)
97462306a36Sopenharmony_ci{
97562306a36Sopenharmony_ci	struct e1000_hw *hw = &adapter->hw;
97662306a36Sopenharmony_ci	struct e1000_tx_ring *txdr = &adapter->test_tx_ring;
97762306a36Sopenharmony_ci	struct e1000_rx_ring *rxdr = &adapter->test_rx_ring;
97862306a36Sopenharmony_ci	struct pci_dev *pdev = adapter->pdev;
97962306a36Sopenharmony_ci	u32 rctl;
98062306a36Sopenharmony_ci	int i, ret_val;
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_ci	/* Setup Tx descriptor ring and Tx buffers */
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ci	if (!txdr->count)
98562306a36Sopenharmony_ci		txdr->count = E1000_DEFAULT_TXD;
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_ci	txdr->buffer_info = kcalloc(txdr->count, sizeof(struct e1000_tx_buffer),
98862306a36Sopenharmony_ci				    GFP_KERNEL);
98962306a36Sopenharmony_ci	if (!txdr->buffer_info) {
99062306a36Sopenharmony_ci		ret_val = 1;
99162306a36Sopenharmony_ci		goto err_nomem;
99262306a36Sopenharmony_ci	}
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_ci	txdr->size = txdr->count * sizeof(struct e1000_tx_desc);
99562306a36Sopenharmony_ci	txdr->size = ALIGN(txdr->size, 4096);
99662306a36Sopenharmony_ci	txdr->desc = dma_alloc_coherent(&pdev->dev, txdr->size, &txdr->dma,
99762306a36Sopenharmony_ci					GFP_KERNEL);
99862306a36Sopenharmony_ci	if (!txdr->desc) {
99962306a36Sopenharmony_ci		ret_val = 2;
100062306a36Sopenharmony_ci		goto err_nomem;
100162306a36Sopenharmony_ci	}
100262306a36Sopenharmony_ci	txdr->next_to_use = txdr->next_to_clean = 0;
100362306a36Sopenharmony_ci
100462306a36Sopenharmony_ci	ew32(TDBAL, ((u64)txdr->dma & 0x00000000FFFFFFFF));
100562306a36Sopenharmony_ci	ew32(TDBAH, ((u64)txdr->dma >> 32));
100662306a36Sopenharmony_ci	ew32(TDLEN, txdr->count * sizeof(struct e1000_tx_desc));
100762306a36Sopenharmony_ci	ew32(TDH, 0);
100862306a36Sopenharmony_ci	ew32(TDT, 0);
100962306a36Sopenharmony_ci	ew32(TCTL, E1000_TCTL_PSP | E1000_TCTL_EN |
101062306a36Sopenharmony_ci	     E1000_COLLISION_THRESHOLD << E1000_CT_SHIFT |
101162306a36Sopenharmony_ci	     E1000_FDX_COLLISION_DISTANCE << E1000_COLD_SHIFT);
101262306a36Sopenharmony_ci
101362306a36Sopenharmony_ci	for (i = 0; i < txdr->count; i++) {
101462306a36Sopenharmony_ci		struct e1000_tx_desc *tx_desc = E1000_TX_DESC(*txdr, i);
101562306a36Sopenharmony_ci		struct sk_buff *skb;
101662306a36Sopenharmony_ci		unsigned int size = 1024;
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_ci		skb = alloc_skb(size, GFP_KERNEL);
101962306a36Sopenharmony_ci		if (!skb) {
102062306a36Sopenharmony_ci			ret_val = 3;
102162306a36Sopenharmony_ci			goto err_nomem;
102262306a36Sopenharmony_ci		}
102362306a36Sopenharmony_ci		skb_put(skb, size);
102462306a36Sopenharmony_ci		txdr->buffer_info[i].skb = skb;
102562306a36Sopenharmony_ci		txdr->buffer_info[i].length = skb->len;
102662306a36Sopenharmony_ci		txdr->buffer_info[i].dma =
102762306a36Sopenharmony_ci			dma_map_single(&pdev->dev, skb->data, skb->len,
102862306a36Sopenharmony_ci				       DMA_TO_DEVICE);
102962306a36Sopenharmony_ci		if (dma_mapping_error(&pdev->dev, txdr->buffer_info[i].dma)) {
103062306a36Sopenharmony_ci			ret_val = 4;
103162306a36Sopenharmony_ci			goto err_nomem;
103262306a36Sopenharmony_ci		}
103362306a36Sopenharmony_ci		tx_desc->buffer_addr = cpu_to_le64(txdr->buffer_info[i].dma);
103462306a36Sopenharmony_ci		tx_desc->lower.data = cpu_to_le32(skb->len);
103562306a36Sopenharmony_ci		tx_desc->lower.data |= cpu_to_le32(E1000_TXD_CMD_EOP |
103662306a36Sopenharmony_ci						   E1000_TXD_CMD_IFCS |
103762306a36Sopenharmony_ci						   E1000_TXD_CMD_RPS);
103862306a36Sopenharmony_ci		tx_desc->upper.data = 0;
103962306a36Sopenharmony_ci	}
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_ci	/* Setup Rx descriptor ring and Rx buffers */
104262306a36Sopenharmony_ci
104362306a36Sopenharmony_ci	if (!rxdr->count)
104462306a36Sopenharmony_ci		rxdr->count = E1000_DEFAULT_RXD;
104562306a36Sopenharmony_ci
104662306a36Sopenharmony_ci	rxdr->buffer_info = kcalloc(rxdr->count, sizeof(struct e1000_rx_buffer),
104762306a36Sopenharmony_ci				    GFP_KERNEL);
104862306a36Sopenharmony_ci	if (!rxdr->buffer_info) {
104962306a36Sopenharmony_ci		ret_val = 5;
105062306a36Sopenharmony_ci		goto err_nomem;
105162306a36Sopenharmony_ci	}
105262306a36Sopenharmony_ci
105362306a36Sopenharmony_ci	rxdr->size = rxdr->count * sizeof(struct e1000_rx_desc);
105462306a36Sopenharmony_ci	rxdr->desc = dma_alloc_coherent(&pdev->dev, rxdr->size, &rxdr->dma,
105562306a36Sopenharmony_ci					GFP_KERNEL);
105662306a36Sopenharmony_ci	if (!rxdr->desc) {
105762306a36Sopenharmony_ci		ret_val = 6;
105862306a36Sopenharmony_ci		goto err_nomem;
105962306a36Sopenharmony_ci	}
106062306a36Sopenharmony_ci	rxdr->next_to_use = rxdr->next_to_clean = 0;
106162306a36Sopenharmony_ci
106262306a36Sopenharmony_ci	rctl = er32(RCTL);
106362306a36Sopenharmony_ci	ew32(RCTL, rctl & ~E1000_RCTL_EN);
106462306a36Sopenharmony_ci	ew32(RDBAL, ((u64)rxdr->dma & 0xFFFFFFFF));
106562306a36Sopenharmony_ci	ew32(RDBAH, ((u64)rxdr->dma >> 32));
106662306a36Sopenharmony_ci	ew32(RDLEN, rxdr->size);
106762306a36Sopenharmony_ci	ew32(RDH, 0);
106862306a36Sopenharmony_ci	ew32(RDT, 0);
106962306a36Sopenharmony_ci	rctl = E1000_RCTL_EN | E1000_RCTL_BAM | E1000_RCTL_SZ_2048 |
107062306a36Sopenharmony_ci		E1000_RCTL_LBM_NO | E1000_RCTL_RDMTS_HALF |
107162306a36Sopenharmony_ci		(hw->mc_filter_type << E1000_RCTL_MO_SHIFT);
107262306a36Sopenharmony_ci	ew32(RCTL, rctl);
107362306a36Sopenharmony_ci
107462306a36Sopenharmony_ci	for (i = 0; i < rxdr->count; i++) {
107562306a36Sopenharmony_ci		struct e1000_rx_desc *rx_desc = E1000_RX_DESC(*rxdr, i);
107662306a36Sopenharmony_ci		u8 *buf;
107762306a36Sopenharmony_ci
107862306a36Sopenharmony_ci		buf = kzalloc(E1000_RXBUFFER_2048 + NET_SKB_PAD + NET_IP_ALIGN,
107962306a36Sopenharmony_ci			      GFP_KERNEL);
108062306a36Sopenharmony_ci		if (!buf) {
108162306a36Sopenharmony_ci			ret_val = 7;
108262306a36Sopenharmony_ci			goto err_nomem;
108362306a36Sopenharmony_ci		}
108462306a36Sopenharmony_ci		rxdr->buffer_info[i].rxbuf.data = buf;
108562306a36Sopenharmony_ci
108662306a36Sopenharmony_ci		rxdr->buffer_info[i].dma =
108762306a36Sopenharmony_ci			dma_map_single(&pdev->dev,
108862306a36Sopenharmony_ci				       buf + NET_SKB_PAD + NET_IP_ALIGN,
108962306a36Sopenharmony_ci				       E1000_RXBUFFER_2048, DMA_FROM_DEVICE);
109062306a36Sopenharmony_ci		if (dma_mapping_error(&pdev->dev, rxdr->buffer_info[i].dma)) {
109162306a36Sopenharmony_ci			ret_val = 8;
109262306a36Sopenharmony_ci			goto err_nomem;
109362306a36Sopenharmony_ci		}
109462306a36Sopenharmony_ci		rx_desc->buffer_addr = cpu_to_le64(rxdr->buffer_info[i].dma);
109562306a36Sopenharmony_ci	}
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_ci	return 0;
109862306a36Sopenharmony_ci
109962306a36Sopenharmony_cierr_nomem:
110062306a36Sopenharmony_ci	e1000_free_desc_rings(adapter);
110162306a36Sopenharmony_ci	return ret_val;
110262306a36Sopenharmony_ci}
110362306a36Sopenharmony_ci
110462306a36Sopenharmony_cistatic void e1000_phy_disable_receiver(struct e1000_adapter *adapter)
110562306a36Sopenharmony_ci{
110662306a36Sopenharmony_ci	struct e1000_hw *hw = &adapter->hw;
110762306a36Sopenharmony_ci
110862306a36Sopenharmony_ci	/* Write out to PHY registers 29 and 30 to disable the Receiver. */
110962306a36Sopenharmony_ci	e1000_write_phy_reg(hw, 29, 0x001F);
111062306a36Sopenharmony_ci	e1000_write_phy_reg(hw, 30, 0x8FFC);
111162306a36Sopenharmony_ci	e1000_write_phy_reg(hw, 29, 0x001A);
111262306a36Sopenharmony_ci	e1000_write_phy_reg(hw, 30, 0x8FF0);
111362306a36Sopenharmony_ci}
111462306a36Sopenharmony_ci
111562306a36Sopenharmony_cistatic void e1000_phy_reset_clk_and_crs(struct e1000_adapter *adapter)
111662306a36Sopenharmony_ci{
111762306a36Sopenharmony_ci	struct e1000_hw *hw = &adapter->hw;
111862306a36Sopenharmony_ci	u16 phy_reg;
111962306a36Sopenharmony_ci
112062306a36Sopenharmony_ci	/* Because we reset the PHY above, we need to re-force TX_CLK in the
112162306a36Sopenharmony_ci	 * Extended PHY Specific Control Register to 25MHz clock.  This
112262306a36Sopenharmony_ci	 * value defaults back to a 2.5MHz clock when the PHY is reset.
112362306a36Sopenharmony_ci	 */
112462306a36Sopenharmony_ci	e1000_read_phy_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL, &phy_reg);
112562306a36Sopenharmony_ci	phy_reg |= M88E1000_EPSCR_TX_CLK_25;
112662306a36Sopenharmony_ci	e1000_write_phy_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL, phy_reg);
112762306a36Sopenharmony_ci
112862306a36Sopenharmony_ci	/* In addition, because of the s/w reset above, we need to enable
112962306a36Sopenharmony_ci	 * CRS on TX.  This must be set for both full and half duplex
113062306a36Sopenharmony_ci	 * operation.
113162306a36Sopenharmony_ci	 */
113262306a36Sopenharmony_ci	e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, &phy_reg);
113362306a36Sopenharmony_ci	phy_reg |= M88E1000_PSCR_ASSERT_CRS_ON_TX;
113462306a36Sopenharmony_ci	e1000_write_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, phy_reg);
113562306a36Sopenharmony_ci}
113662306a36Sopenharmony_ci
113762306a36Sopenharmony_cistatic int e1000_nonintegrated_phy_loopback(struct e1000_adapter *adapter)
113862306a36Sopenharmony_ci{
113962306a36Sopenharmony_ci	struct e1000_hw *hw = &adapter->hw;
114062306a36Sopenharmony_ci	u32 ctrl_reg;
114162306a36Sopenharmony_ci	u16 phy_reg;
114262306a36Sopenharmony_ci
114362306a36Sopenharmony_ci	/* Setup the Device Control Register for PHY loopback test. */
114462306a36Sopenharmony_ci
114562306a36Sopenharmony_ci	ctrl_reg = er32(CTRL);
114662306a36Sopenharmony_ci	ctrl_reg |= (E1000_CTRL_ILOS |		/* Invert Loss-Of-Signal */
114762306a36Sopenharmony_ci		     E1000_CTRL_FRCSPD |	/* Set the Force Speed Bit */
114862306a36Sopenharmony_ci		     E1000_CTRL_FRCDPX |	/* Set the Force Duplex Bit */
114962306a36Sopenharmony_ci		     E1000_CTRL_SPD_1000 |	/* Force Speed to 1000 */
115062306a36Sopenharmony_ci		     E1000_CTRL_FD);		/* Force Duplex to FULL */
115162306a36Sopenharmony_ci
115262306a36Sopenharmony_ci	ew32(CTRL, ctrl_reg);
115362306a36Sopenharmony_ci
115462306a36Sopenharmony_ci	/* Read the PHY Specific Control Register (0x10) */
115562306a36Sopenharmony_ci	e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, &phy_reg);
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_ci	/* Clear Auto-Crossover bits in PHY Specific Control Register
115862306a36Sopenharmony_ci	 * (bits 6:5).
115962306a36Sopenharmony_ci	 */
116062306a36Sopenharmony_ci	phy_reg &= ~M88E1000_PSCR_AUTO_X_MODE;
116162306a36Sopenharmony_ci	e1000_write_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, phy_reg);
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_ci	/* Perform software reset on the PHY */
116462306a36Sopenharmony_ci	e1000_phy_reset(hw);
116562306a36Sopenharmony_ci
116662306a36Sopenharmony_ci	/* Have to setup TX_CLK and TX_CRS after software reset */
116762306a36Sopenharmony_ci	e1000_phy_reset_clk_and_crs(adapter);
116862306a36Sopenharmony_ci
116962306a36Sopenharmony_ci	e1000_write_phy_reg(hw, PHY_CTRL, 0x8100);
117062306a36Sopenharmony_ci
117162306a36Sopenharmony_ci	/* Wait for reset to complete. */
117262306a36Sopenharmony_ci	udelay(500);
117362306a36Sopenharmony_ci
117462306a36Sopenharmony_ci	/* Have to setup TX_CLK and TX_CRS after software reset */
117562306a36Sopenharmony_ci	e1000_phy_reset_clk_and_crs(adapter);
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_ci	/* Write out to PHY registers 29 and 30 to disable the Receiver. */
117862306a36Sopenharmony_ci	e1000_phy_disable_receiver(adapter);
117962306a36Sopenharmony_ci
118062306a36Sopenharmony_ci	/* Set the loopback bit in the PHY control register. */
118162306a36Sopenharmony_ci	e1000_read_phy_reg(hw, PHY_CTRL, &phy_reg);
118262306a36Sopenharmony_ci	phy_reg |= MII_CR_LOOPBACK;
118362306a36Sopenharmony_ci	e1000_write_phy_reg(hw, PHY_CTRL, phy_reg);
118462306a36Sopenharmony_ci
118562306a36Sopenharmony_ci	/* Setup TX_CLK and TX_CRS one more time. */
118662306a36Sopenharmony_ci	e1000_phy_reset_clk_and_crs(adapter);
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_ci	/* Check Phy Configuration */
118962306a36Sopenharmony_ci	e1000_read_phy_reg(hw, PHY_CTRL, &phy_reg);
119062306a36Sopenharmony_ci	if (phy_reg != 0x4100)
119162306a36Sopenharmony_ci		return 9;
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_ci	e1000_read_phy_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL, &phy_reg);
119462306a36Sopenharmony_ci	if (phy_reg != 0x0070)
119562306a36Sopenharmony_ci		return 10;
119662306a36Sopenharmony_ci
119762306a36Sopenharmony_ci	e1000_read_phy_reg(hw, 29, &phy_reg);
119862306a36Sopenharmony_ci	if (phy_reg != 0x001A)
119962306a36Sopenharmony_ci		return 11;
120062306a36Sopenharmony_ci
120162306a36Sopenharmony_ci	return 0;
120262306a36Sopenharmony_ci}
120362306a36Sopenharmony_ci
120462306a36Sopenharmony_cistatic int e1000_integrated_phy_loopback(struct e1000_adapter *adapter)
120562306a36Sopenharmony_ci{
120662306a36Sopenharmony_ci	struct e1000_hw *hw = &adapter->hw;
120762306a36Sopenharmony_ci	u32 ctrl_reg = 0;
120862306a36Sopenharmony_ci	u32 stat_reg = 0;
120962306a36Sopenharmony_ci
121062306a36Sopenharmony_ci	hw->autoneg = false;
121162306a36Sopenharmony_ci
121262306a36Sopenharmony_ci	if (hw->phy_type == e1000_phy_m88) {
121362306a36Sopenharmony_ci		/* Auto-MDI/MDIX Off */
121462306a36Sopenharmony_ci		e1000_write_phy_reg(hw,
121562306a36Sopenharmony_ci				    M88E1000_PHY_SPEC_CTRL, 0x0808);
121662306a36Sopenharmony_ci		/* reset to update Auto-MDI/MDIX */
121762306a36Sopenharmony_ci		e1000_write_phy_reg(hw, PHY_CTRL, 0x9140);
121862306a36Sopenharmony_ci		/* autoneg off */
121962306a36Sopenharmony_ci		e1000_write_phy_reg(hw, PHY_CTRL, 0x8140);
122062306a36Sopenharmony_ci	}
122162306a36Sopenharmony_ci
122262306a36Sopenharmony_ci	ctrl_reg = er32(CTRL);
122362306a36Sopenharmony_ci
122462306a36Sopenharmony_ci	/* force 1000, set loopback */
122562306a36Sopenharmony_ci	e1000_write_phy_reg(hw, PHY_CTRL, 0x4140);
122662306a36Sopenharmony_ci
122762306a36Sopenharmony_ci	/* Now set up the MAC to the same speed/duplex as the PHY. */
122862306a36Sopenharmony_ci	ctrl_reg = er32(CTRL);
122962306a36Sopenharmony_ci	ctrl_reg &= ~E1000_CTRL_SPD_SEL; /* Clear the speed sel bits */
123062306a36Sopenharmony_ci	ctrl_reg |= (E1000_CTRL_FRCSPD | /* Set the Force Speed Bit */
123162306a36Sopenharmony_ci			E1000_CTRL_FRCDPX | /* Set the Force Duplex Bit */
123262306a36Sopenharmony_ci			E1000_CTRL_SPD_1000 |/* Force Speed to 1000 */
123362306a36Sopenharmony_ci			E1000_CTRL_FD); /* Force Duplex to FULL */
123462306a36Sopenharmony_ci
123562306a36Sopenharmony_ci	if (hw->media_type == e1000_media_type_copper &&
123662306a36Sopenharmony_ci	    hw->phy_type == e1000_phy_m88)
123762306a36Sopenharmony_ci		ctrl_reg |= E1000_CTRL_ILOS; /* Invert Loss of Signal */
123862306a36Sopenharmony_ci	else {
123962306a36Sopenharmony_ci		/* Set the ILOS bit on the fiber Nic is half
124062306a36Sopenharmony_ci		 * duplex link is detected.
124162306a36Sopenharmony_ci		 */
124262306a36Sopenharmony_ci		stat_reg = er32(STATUS);
124362306a36Sopenharmony_ci		if ((stat_reg & E1000_STATUS_FD) == 0)
124462306a36Sopenharmony_ci			ctrl_reg |= (E1000_CTRL_ILOS | E1000_CTRL_SLU);
124562306a36Sopenharmony_ci	}
124662306a36Sopenharmony_ci
124762306a36Sopenharmony_ci	ew32(CTRL, ctrl_reg);
124862306a36Sopenharmony_ci
124962306a36Sopenharmony_ci	/* Disable the receiver on the PHY so when a cable is plugged in, the
125062306a36Sopenharmony_ci	 * PHY does not begin to autoneg when a cable is reconnected to the NIC.
125162306a36Sopenharmony_ci	 */
125262306a36Sopenharmony_ci	if (hw->phy_type == e1000_phy_m88)
125362306a36Sopenharmony_ci		e1000_phy_disable_receiver(adapter);
125462306a36Sopenharmony_ci
125562306a36Sopenharmony_ci	udelay(500);
125662306a36Sopenharmony_ci
125762306a36Sopenharmony_ci	return 0;
125862306a36Sopenharmony_ci}
125962306a36Sopenharmony_ci
126062306a36Sopenharmony_cistatic int e1000_set_phy_loopback(struct e1000_adapter *adapter)
126162306a36Sopenharmony_ci{
126262306a36Sopenharmony_ci	struct e1000_hw *hw = &adapter->hw;
126362306a36Sopenharmony_ci	u16 phy_reg = 0;
126462306a36Sopenharmony_ci	u16 count = 0;
126562306a36Sopenharmony_ci
126662306a36Sopenharmony_ci	switch (hw->mac_type) {
126762306a36Sopenharmony_ci	case e1000_82543:
126862306a36Sopenharmony_ci		if (hw->media_type == e1000_media_type_copper) {
126962306a36Sopenharmony_ci			/* Attempt to setup Loopback mode on Non-integrated PHY.
127062306a36Sopenharmony_ci			 * Some PHY registers get corrupted at random, so
127162306a36Sopenharmony_ci			 * attempt this 10 times.
127262306a36Sopenharmony_ci			 */
127362306a36Sopenharmony_ci			while (e1000_nonintegrated_phy_loopback(adapter) &&
127462306a36Sopenharmony_ci			       count++ < 10);
127562306a36Sopenharmony_ci			if (count < 11)
127662306a36Sopenharmony_ci				return 0;
127762306a36Sopenharmony_ci		}
127862306a36Sopenharmony_ci		break;
127962306a36Sopenharmony_ci
128062306a36Sopenharmony_ci	case e1000_82544:
128162306a36Sopenharmony_ci	case e1000_82540:
128262306a36Sopenharmony_ci	case e1000_82545:
128362306a36Sopenharmony_ci	case e1000_82545_rev_3:
128462306a36Sopenharmony_ci	case e1000_82546:
128562306a36Sopenharmony_ci	case e1000_82546_rev_3:
128662306a36Sopenharmony_ci	case e1000_82541:
128762306a36Sopenharmony_ci	case e1000_82541_rev_2:
128862306a36Sopenharmony_ci	case e1000_82547:
128962306a36Sopenharmony_ci	case e1000_82547_rev_2:
129062306a36Sopenharmony_ci		return e1000_integrated_phy_loopback(adapter);
129162306a36Sopenharmony_ci	default:
129262306a36Sopenharmony_ci		/* Default PHY loopback work is to read the MII
129362306a36Sopenharmony_ci		 * control register and assert bit 14 (loopback mode).
129462306a36Sopenharmony_ci		 */
129562306a36Sopenharmony_ci		e1000_read_phy_reg(hw, PHY_CTRL, &phy_reg);
129662306a36Sopenharmony_ci		phy_reg |= MII_CR_LOOPBACK;
129762306a36Sopenharmony_ci		e1000_write_phy_reg(hw, PHY_CTRL, phy_reg);
129862306a36Sopenharmony_ci		return 0;
129962306a36Sopenharmony_ci	}
130062306a36Sopenharmony_ci
130162306a36Sopenharmony_ci	return 8;
130262306a36Sopenharmony_ci}
130362306a36Sopenharmony_ci
130462306a36Sopenharmony_cistatic int e1000_setup_loopback_test(struct e1000_adapter *adapter)
130562306a36Sopenharmony_ci{
130662306a36Sopenharmony_ci	struct e1000_hw *hw = &adapter->hw;
130762306a36Sopenharmony_ci	u32 rctl;
130862306a36Sopenharmony_ci
130962306a36Sopenharmony_ci	if (hw->media_type == e1000_media_type_fiber ||
131062306a36Sopenharmony_ci	    hw->media_type == e1000_media_type_internal_serdes) {
131162306a36Sopenharmony_ci		switch (hw->mac_type) {
131262306a36Sopenharmony_ci		case e1000_82545:
131362306a36Sopenharmony_ci		case e1000_82546:
131462306a36Sopenharmony_ci		case e1000_82545_rev_3:
131562306a36Sopenharmony_ci		case e1000_82546_rev_3:
131662306a36Sopenharmony_ci			return e1000_set_phy_loopback(adapter);
131762306a36Sopenharmony_ci		default:
131862306a36Sopenharmony_ci			rctl = er32(RCTL);
131962306a36Sopenharmony_ci			rctl |= E1000_RCTL_LBM_TCVR;
132062306a36Sopenharmony_ci			ew32(RCTL, rctl);
132162306a36Sopenharmony_ci			return 0;
132262306a36Sopenharmony_ci		}
132362306a36Sopenharmony_ci	} else if (hw->media_type == e1000_media_type_copper) {
132462306a36Sopenharmony_ci		return e1000_set_phy_loopback(adapter);
132562306a36Sopenharmony_ci	}
132662306a36Sopenharmony_ci
132762306a36Sopenharmony_ci	return 7;
132862306a36Sopenharmony_ci}
132962306a36Sopenharmony_ci
133062306a36Sopenharmony_cistatic void e1000_loopback_cleanup(struct e1000_adapter *adapter)
133162306a36Sopenharmony_ci{
133262306a36Sopenharmony_ci	struct e1000_hw *hw = &adapter->hw;
133362306a36Sopenharmony_ci	u32 rctl;
133462306a36Sopenharmony_ci	u16 phy_reg;
133562306a36Sopenharmony_ci
133662306a36Sopenharmony_ci	rctl = er32(RCTL);
133762306a36Sopenharmony_ci	rctl &= ~(E1000_RCTL_LBM_TCVR | E1000_RCTL_LBM_MAC);
133862306a36Sopenharmony_ci	ew32(RCTL, rctl);
133962306a36Sopenharmony_ci
134062306a36Sopenharmony_ci	switch (hw->mac_type) {
134162306a36Sopenharmony_ci	case e1000_82545:
134262306a36Sopenharmony_ci	case e1000_82546:
134362306a36Sopenharmony_ci	case e1000_82545_rev_3:
134462306a36Sopenharmony_ci	case e1000_82546_rev_3:
134562306a36Sopenharmony_ci	default:
134662306a36Sopenharmony_ci		hw->autoneg = true;
134762306a36Sopenharmony_ci		e1000_read_phy_reg(hw, PHY_CTRL, &phy_reg);
134862306a36Sopenharmony_ci		if (phy_reg & MII_CR_LOOPBACK) {
134962306a36Sopenharmony_ci			phy_reg &= ~MII_CR_LOOPBACK;
135062306a36Sopenharmony_ci			e1000_write_phy_reg(hw, PHY_CTRL, phy_reg);
135162306a36Sopenharmony_ci			e1000_phy_reset(hw);
135262306a36Sopenharmony_ci		}
135362306a36Sopenharmony_ci		break;
135462306a36Sopenharmony_ci	}
135562306a36Sopenharmony_ci}
135662306a36Sopenharmony_ci
135762306a36Sopenharmony_cistatic void e1000_create_lbtest_frame(struct sk_buff *skb,
135862306a36Sopenharmony_ci				      unsigned int frame_size)
135962306a36Sopenharmony_ci{
136062306a36Sopenharmony_ci	memset(skb->data, 0xFF, frame_size);
136162306a36Sopenharmony_ci	frame_size &= ~1;
136262306a36Sopenharmony_ci	memset(&skb->data[frame_size / 2], 0xAA, frame_size / 2 - 1);
136362306a36Sopenharmony_ci	skb->data[frame_size / 2 + 10] = 0xBE;
136462306a36Sopenharmony_ci	skb->data[frame_size / 2 + 12] = 0xAF;
136562306a36Sopenharmony_ci}
136662306a36Sopenharmony_ci
136762306a36Sopenharmony_cistatic int e1000_check_lbtest_frame(const unsigned char *data,
136862306a36Sopenharmony_ci				    unsigned int frame_size)
136962306a36Sopenharmony_ci{
137062306a36Sopenharmony_ci	frame_size &= ~1;
137162306a36Sopenharmony_ci	if (*(data + 3) == 0xFF) {
137262306a36Sopenharmony_ci		if ((*(data + frame_size / 2 + 10) == 0xBE) &&
137362306a36Sopenharmony_ci		    (*(data + frame_size / 2 + 12) == 0xAF)) {
137462306a36Sopenharmony_ci			return 0;
137562306a36Sopenharmony_ci		}
137662306a36Sopenharmony_ci	}
137762306a36Sopenharmony_ci	return 13;
137862306a36Sopenharmony_ci}
137962306a36Sopenharmony_ci
138062306a36Sopenharmony_cistatic int e1000_run_loopback_test(struct e1000_adapter *adapter)
138162306a36Sopenharmony_ci{
138262306a36Sopenharmony_ci	struct e1000_hw *hw = &adapter->hw;
138362306a36Sopenharmony_ci	struct e1000_tx_ring *txdr = &adapter->test_tx_ring;
138462306a36Sopenharmony_ci	struct e1000_rx_ring *rxdr = &adapter->test_rx_ring;
138562306a36Sopenharmony_ci	struct pci_dev *pdev = adapter->pdev;
138662306a36Sopenharmony_ci	int i, j, k, l, lc, good_cnt, ret_val = 0;
138762306a36Sopenharmony_ci	unsigned long time;
138862306a36Sopenharmony_ci
138962306a36Sopenharmony_ci	ew32(RDT, rxdr->count - 1);
139062306a36Sopenharmony_ci
139162306a36Sopenharmony_ci	/* Calculate the loop count based on the largest descriptor ring
139262306a36Sopenharmony_ci	 * The idea is to wrap the largest ring a number of times using 64
139362306a36Sopenharmony_ci	 * send/receive pairs during each loop
139462306a36Sopenharmony_ci	 */
139562306a36Sopenharmony_ci
139662306a36Sopenharmony_ci	if (rxdr->count <= txdr->count)
139762306a36Sopenharmony_ci		lc = ((txdr->count / 64) * 2) + 1;
139862306a36Sopenharmony_ci	else
139962306a36Sopenharmony_ci		lc = ((rxdr->count / 64) * 2) + 1;
140062306a36Sopenharmony_ci
140162306a36Sopenharmony_ci	k = l = 0;
140262306a36Sopenharmony_ci	for (j = 0; j <= lc; j++) { /* loop count loop */
140362306a36Sopenharmony_ci		for (i = 0; i < 64; i++) { /* send the packets */
140462306a36Sopenharmony_ci			e1000_create_lbtest_frame(txdr->buffer_info[i].skb,
140562306a36Sopenharmony_ci						  1024);
140662306a36Sopenharmony_ci			dma_sync_single_for_device(&pdev->dev,
140762306a36Sopenharmony_ci						   txdr->buffer_info[k].dma,
140862306a36Sopenharmony_ci						   txdr->buffer_info[k].length,
140962306a36Sopenharmony_ci						   DMA_TO_DEVICE);
141062306a36Sopenharmony_ci			if (unlikely(++k == txdr->count))
141162306a36Sopenharmony_ci				k = 0;
141262306a36Sopenharmony_ci		}
141362306a36Sopenharmony_ci		ew32(TDT, k);
141462306a36Sopenharmony_ci		E1000_WRITE_FLUSH();
141562306a36Sopenharmony_ci		msleep(200);
141662306a36Sopenharmony_ci		time = jiffies; /* set the start time for the receive */
141762306a36Sopenharmony_ci		good_cnt = 0;
141862306a36Sopenharmony_ci		do { /* receive the sent packets */
141962306a36Sopenharmony_ci			dma_sync_single_for_cpu(&pdev->dev,
142062306a36Sopenharmony_ci						rxdr->buffer_info[l].dma,
142162306a36Sopenharmony_ci						E1000_RXBUFFER_2048,
142262306a36Sopenharmony_ci						DMA_FROM_DEVICE);
142362306a36Sopenharmony_ci
142462306a36Sopenharmony_ci			ret_val = e1000_check_lbtest_frame(
142562306a36Sopenharmony_ci					rxdr->buffer_info[l].rxbuf.data +
142662306a36Sopenharmony_ci					NET_SKB_PAD + NET_IP_ALIGN,
142762306a36Sopenharmony_ci					1024);
142862306a36Sopenharmony_ci			if (!ret_val)
142962306a36Sopenharmony_ci				good_cnt++;
143062306a36Sopenharmony_ci			if (unlikely(++l == rxdr->count))
143162306a36Sopenharmony_ci				l = 0;
143262306a36Sopenharmony_ci			/* time + 20 msecs (200 msecs on 2.4) is more than
143362306a36Sopenharmony_ci			 * enough time to complete the receives, if it's
143462306a36Sopenharmony_ci			 * exceeded, break and error off
143562306a36Sopenharmony_ci			 */
143662306a36Sopenharmony_ci		} while (good_cnt < 64 && time_after(time + 20, jiffies));
143762306a36Sopenharmony_ci
143862306a36Sopenharmony_ci		if (good_cnt != 64) {
143962306a36Sopenharmony_ci			ret_val = 13; /* ret_val is the same as mis-compare */
144062306a36Sopenharmony_ci			break;
144162306a36Sopenharmony_ci		}
144262306a36Sopenharmony_ci		if (time_after_eq(jiffies, time + 2)) {
144362306a36Sopenharmony_ci			ret_val = 14; /* error code for time out error */
144462306a36Sopenharmony_ci			break;
144562306a36Sopenharmony_ci		}
144662306a36Sopenharmony_ci	} /* end loop count loop */
144762306a36Sopenharmony_ci	return ret_val;
144862306a36Sopenharmony_ci}
144962306a36Sopenharmony_ci
145062306a36Sopenharmony_cistatic int e1000_loopback_test(struct e1000_adapter *adapter, u64 *data)
145162306a36Sopenharmony_ci{
145262306a36Sopenharmony_ci	*data = e1000_setup_desc_rings(adapter);
145362306a36Sopenharmony_ci	if (*data)
145462306a36Sopenharmony_ci		goto out;
145562306a36Sopenharmony_ci	*data = e1000_setup_loopback_test(adapter);
145662306a36Sopenharmony_ci	if (*data)
145762306a36Sopenharmony_ci		goto err_loopback;
145862306a36Sopenharmony_ci	*data = e1000_run_loopback_test(adapter);
145962306a36Sopenharmony_ci	e1000_loopback_cleanup(adapter);
146062306a36Sopenharmony_ci
146162306a36Sopenharmony_cierr_loopback:
146262306a36Sopenharmony_ci	e1000_free_desc_rings(adapter);
146362306a36Sopenharmony_ciout:
146462306a36Sopenharmony_ci	return *data;
146562306a36Sopenharmony_ci}
146662306a36Sopenharmony_ci
146762306a36Sopenharmony_cistatic int e1000_link_test(struct e1000_adapter *adapter, u64 *data)
146862306a36Sopenharmony_ci{
146962306a36Sopenharmony_ci	struct e1000_hw *hw = &adapter->hw;
147062306a36Sopenharmony_ci	*data = 0;
147162306a36Sopenharmony_ci	if (hw->media_type == e1000_media_type_internal_serdes) {
147262306a36Sopenharmony_ci		int i = 0;
147362306a36Sopenharmony_ci
147462306a36Sopenharmony_ci		hw->serdes_has_link = false;
147562306a36Sopenharmony_ci
147662306a36Sopenharmony_ci		/* On some blade server designs, link establishment
147762306a36Sopenharmony_ci		 * could take as long as 2-3 minutes
147862306a36Sopenharmony_ci		 */
147962306a36Sopenharmony_ci		do {
148062306a36Sopenharmony_ci			e1000_check_for_link(hw);
148162306a36Sopenharmony_ci			if (hw->serdes_has_link)
148262306a36Sopenharmony_ci				return *data;
148362306a36Sopenharmony_ci			msleep(20);
148462306a36Sopenharmony_ci		} while (i++ < 3750);
148562306a36Sopenharmony_ci
148662306a36Sopenharmony_ci		*data = 1;
148762306a36Sopenharmony_ci	} else {
148862306a36Sopenharmony_ci		e1000_check_for_link(hw);
148962306a36Sopenharmony_ci		if (hw->autoneg)  /* if auto_neg is set wait for it */
149062306a36Sopenharmony_ci			msleep(4000);
149162306a36Sopenharmony_ci
149262306a36Sopenharmony_ci		if (!(er32(STATUS) & E1000_STATUS_LU))
149362306a36Sopenharmony_ci			*data = 1;
149462306a36Sopenharmony_ci	}
149562306a36Sopenharmony_ci	return *data;
149662306a36Sopenharmony_ci}
149762306a36Sopenharmony_ci
149862306a36Sopenharmony_cistatic int e1000_get_sset_count(struct net_device *netdev, int sset)
149962306a36Sopenharmony_ci{
150062306a36Sopenharmony_ci	switch (sset) {
150162306a36Sopenharmony_ci	case ETH_SS_TEST:
150262306a36Sopenharmony_ci		return E1000_TEST_LEN;
150362306a36Sopenharmony_ci	case ETH_SS_STATS:
150462306a36Sopenharmony_ci		return E1000_STATS_LEN;
150562306a36Sopenharmony_ci	default:
150662306a36Sopenharmony_ci		return -EOPNOTSUPP;
150762306a36Sopenharmony_ci	}
150862306a36Sopenharmony_ci}
150962306a36Sopenharmony_ci
151062306a36Sopenharmony_cistatic void e1000_diag_test(struct net_device *netdev,
151162306a36Sopenharmony_ci			    struct ethtool_test *eth_test, u64 *data)
151262306a36Sopenharmony_ci{
151362306a36Sopenharmony_ci	struct e1000_adapter *adapter = netdev_priv(netdev);
151462306a36Sopenharmony_ci	struct e1000_hw *hw = &adapter->hw;
151562306a36Sopenharmony_ci	bool if_running = netif_running(netdev);
151662306a36Sopenharmony_ci
151762306a36Sopenharmony_ci	set_bit(__E1000_TESTING, &adapter->flags);
151862306a36Sopenharmony_ci	if (eth_test->flags == ETH_TEST_FL_OFFLINE) {
151962306a36Sopenharmony_ci		/* Offline tests */
152062306a36Sopenharmony_ci
152162306a36Sopenharmony_ci		/* save speed, duplex, autoneg settings */
152262306a36Sopenharmony_ci		u16 autoneg_advertised = hw->autoneg_advertised;
152362306a36Sopenharmony_ci		u8 forced_speed_duplex = hw->forced_speed_duplex;
152462306a36Sopenharmony_ci		u8 autoneg = hw->autoneg;
152562306a36Sopenharmony_ci
152662306a36Sopenharmony_ci		e_info(hw, "offline testing starting\n");
152762306a36Sopenharmony_ci
152862306a36Sopenharmony_ci		/* Link test performed before hardware reset so autoneg doesn't
152962306a36Sopenharmony_ci		 * interfere with test result
153062306a36Sopenharmony_ci		 */
153162306a36Sopenharmony_ci		if (e1000_link_test(adapter, &data[4]))
153262306a36Sopenharmony_ci			eth_test->flags |= ETH_TEST_FL_FAILED;
153362306a36Sopenharmony_ci
153462306a36Sopenharmony_ci		if (if_running)
153562306a36Sopenharmony_ci			/* indicate we're in test mode */
153662306a36Sopenharmony_ci			e1000_close(netdev);
153762306a36Sopenharmony_ci		else
153862306a36Sopenharmony_ci			e1000_reset(adapter);
153962306a36Sopenharmony_ci
154062306a36Sopenharmony_ci		if (e1000_reg_test(adapter, &data[0]))
154162306a36Sopenharmony_ci			eth_test->flags |= ETH_TEST_FL_FAILED;
154262306a36Sopenharmony_ci
154362306a36Sopenharmony_ci		e1000_reset(adapter);
154462306a36Sopenharmony_ci		if (e1000_eeprom_test(adapter, &data[1]))
154562306a36Sopenharmony_ci			eth_test->flags |= ETH_TEST_FL_FAILED;
154662306a36Sopenharmony_ci
154762306a36Sopenharmony_ci		e1000_reset(adapter);
154862306a36Sopenharmony_ci		if (e1000_intr_test(adapter, &data[2]))
154962306a36Sopenharmony_ci			eth_test->flags |= ETH_TEST_FL_FAILED;
155062306a36Sopenharmony_ci
155162306a36Sopenharmony_ci		e1000_reset(adapter);
155262306a36Sopenharmony_ci		/* make sure the phy is powered up */
155362306a36Sopenharmony_ci		e1000_power_up_phy(adapter);
155462306a36Sopenharmony_ci		if (e1000_loopback_test(adapter, &data[3]))
155562306a36Sopenharmony_ci			eth_test->flags |= ETH_TEST_FL_FAILED;
155662306a36Sopenharmony_ci
155762306a36Sopenharmony_ci		/* restore speed, duplex, autoneg settings */
155862306a36Sopenharmony_ci		hw->autoneg_advertised = autoneg_advertised;
155962306a36Sopenharmony_ci		hw->forced_speed_duplex = forced_speed_duplex;
156062306a36Sopenharmony_ci		hw->autoneg = autoneg;
156162306a36Sopenharmony_ci
156262306a36Sopenharmony_ci		e1000_reset(adapter);
156362306a36Sopenharmony_ci		clear_bit(__E1000_TESTING, &adapter->flags);
156462306a36Sopenharmony_ci		if (if_running)
156562306a36Sopenharmony_ci			e1000_open(netdev);
156662306a36Sopenharmony_ci	} else {
156762306a36Sopenharmony_ci		e_info(hw, "online testing starting\n");
156862306a36Sopenharmony_ci		/* Online tests */
156962306a36Sopenharmony_ci		if (e1000_link_test(adapter, &data[4]))
157062306a36Sopenharmony_ci			eth_test->flags |= ETH_TEST_FL_FAILED;
157162306a36Sopenharmony_ci
157262306a36Sopenharmony_ci		/* Online tests aren't run; pass by default */
157362306a36Sopenharmony_ci		data[0] = 0;
157462306a36Sopenharmony_ci		data[1] = 0;
157562306a36Sopenharmony_ci		data[2] = 0;
157662306a36Sopenharmony_ci		data[3] = 0;
157762306a36Sopenharmony_ci
157862306a36Sopenharmony_ci		clear_bit(__E1000_TESTING, &adapter->flags);
157962306a36Sopenharmony_ci	}
158062306a36Sopenharmony_ci	msleep_interruptible(4 * 1000);
158162306a36Sopenharmony_ci}
158262306a36Sopenharmony_ci
158362306a36Sopenharmony_cistatic int e1000_wol_exclusion(struct e1000_adapter *adapter,
158462306a36Sopenharmony_ci			       struct ethtool_wolinfo *wol)
158562306a36Sopenharmony_ci{
158662306a36Sopenharmony_ci	struct e1000_hw *hw = &adapter->hw;
158762306a36Sopenharmony_ci	int retval = 1; /* fail by default */
158862306a36Sopenharmony_ci
158962306a36Sopenharmony_ci	switch (hw->device_id) {
159062306a36Sopenharmony_ci	case E1000_DEV_ID_82542:
159162306a36Sopenharmony_ci	case E1000_DEV_ID_82543GC_FIBER:
159262306a36Sopenharmony_ci	case E1000_DEV_ID_82543GC_COPPER:
159362306a36Sopenharmony_ci	case E1000_DEV_ID_82544EI_FIBER:
159462306a36Sopenharmony_ci	case E1000_DEV_ID_82546EB_QUAD_COPPER:
159562306a36Sopenharmony_ci	case E1000_DEV_ID_82545EM_FIBER:
159662306a36Sopenharmony_ci	case E1000_DEV_ID_82545EM_COPPER:
159762306a36Sopenharmony_ci	case E1000_DEV_ID_82546GB_QUAD_COPPER:
159862306a36Sopenharmony_ci	case E1000_DEV_ID_82546GB_PCIE:
159962306a36Sopenharmony_ci		/* these don't support WoL at all */
160062306a36Sopenharmony_ci		wol->supported = 0;
160162306a36Sopenharmony_ci		break;
160262306a36Sopenharmony_ci	case E1000_DEV_ID_82546EB_FIBER:
160362306a36Sopenharmony_ci	case E1000_DEV_ID_82546GB_FIBER:
160462306a36Sopenharmony_ci		/* Wake events not supported on port B */
160562306a36Sopenharmony_ci		if (er32(STATUS) & E1000_STATUS_FUNC_1) {
160662306a36Sopenharmony_ci			wol->supported = 0;
160762306a36Sopenharmony_ci			break;
160862306a36Sopenharmony_ci		}
160962306a36Sopenharmony_ci		/* return success for non excluded adapter ports */
161062306a36Sopenharmony_ci		retval = 0;
161162306a36Sopenharmony_ci		break;
161262306a36Sopenharmony_ci	case E1000_DEV_ID_82546GB_QUAD_COPPER_KSP3:
161362306a36Sopenharmony_ci		/* quad port adapters only support WoL on port A */
161462306a36Sopenharmony_ci		if (!adapter->quad_port_a) {
161562306a36Sopenharmony_ci			wol->supported = 0;
161662306a36Sopenharmony_ci			break;
161762306a36Sopenharmony_ci		}
161862306a36Sopenharmony_ci		/* return success for non excluded adapter ports */
161962306a36Sopenharmony_ci		retval = 0;
162062306a36Sopenharmony_ci		break;
162162306a36Sopenharmony_ci	default:
162262306a36Sopenharmony_ci		/* dual port cards only support WoL on port A from now on
162362306a36Sopenharmony_ci		 * unless it was enabled in the eeprom for port B
162462306a36Sopenharmony_ci		 * so exclude FUNC_1 ports from having WoL enabled
162562306a36Sopenharmony_ci		 */
162662306a36Sopenharmony_ci		if (er32(STATUS) & E1000_STATUS_FUNC_1 &&
162762306a36Sopenharmony_ci		    !adapter->eeprom_wol) {
162862306a36Sopenharmony_ci			wol->supported = 0;
162962306a36Sopenharmony_ci			break;
163062306a36Sopenharmony_ci		}
163162306a36Sopenharmony_ci
163262306a36Sopenharmony_ci		retval = 0;
163362306a36Sopenharmony_ci	}
163462306a36Sopenharmony_ci
163562306a36Sopenharmony_ci	return retval;
163662306a36Sopenharmony_ci}
163762306a36Sopenharmony_ci
163862306a36Sopenharmony_cistatic void e1000_get_wol(struct net_device *netdev,
163962306a36Sopenharmony_ci			  struct ethtool_wolinfo *wol)
164062306a36Sopenharmony_ci{
164162306a36Sopenharmony_ci	struct e1000_adapter *adapter = netdev_priv(netdev);
164262306a36Sopenharmony_ci	struct e1000_hw *hw = &adapter->hw;
164362306a36Sopenharmony_ci
164462306a36Sopenharmony_ci	wol->supported = WAKE_UCAST | WAKE_MCAST | WAKE_BCAST | WAKE_MAGIC;
164562306a36Sopenharmony_ci	wol->wolopts = 0;
164662306a36Sopenharmony_ci
164762306a36Sopenharmony_ci	/* this function will set ->supported = 0 and return 1 if wol is not
164862306a36Sopenharmony_ci	 * supported by this hardware
164962306a36Sopenharmony_ci	 */
165062306a36Sopenharmony_ci	if (e1000_wol_exclusion(adapter, wol) ||
165162306a36Sopenharmony_ci	    !device_can_wakeup(&adapter->pdev->dev))
165262306a36Sopenharmony_ci		return;
165362306a36Sopenharmony_ci
165462306a36Sopenharmony_ci	/* apply any specific unsupported masks here */
165562306a36Sopenharmony_ci	switch (hw->device_id) {
165662306a36Sopenharmony_ci	case E1000_DEV_ID_82546GB_QUAD_COPPER_KSP3:
165762306a36Sopenharmony_ci		/* KSP3 does not support UCAST wake-ups */
165862306a36Sopenharmony_ci		wol->supported &= ~WAKE_UCAST;
165962306a36Sopenharmony_ci
166062306a36Sopenharmony_ci		if (adapter->wol & E1000_WUFC_EX)
166162306a36Sopenharmony_ci			e_err(drv, "Interface does not support directed "
166262306a36Sopenharmony_ci			      "(unicast) frame wake-up packets\n");
166362306a36Sopenharmony_ci		break;
166462306a36Sopenharmony_ci	default:
166562306a36Sopenharmony_ci		break;
166662306a36Sopenharmony_ci	}
166762306a36Sopenharmony_ci
166862306a36Sopenharmony_ci	if (adapter->wol & E1000_WUFC_EX)
166962306a36Sopenharmony_ci		wol->wolopts |= WAKE_UCAST;
167062306a36Sopenharmony_ci	if (adapter->wol & E1000_WUFC_MC)
167162306a36Sopenharmony_ci		wol->wolopts |= WAKE_MCAST;
167262306a36Sopenharmony_ci	if (adapter->wol & E1000_WUFC_BC)
167362306a36Sopenharmony_ci		wol->wolopts |= WAKE_BCAST;
167462306a36Sopenharmony_ci	if (adapter->wol & E1000_WUFC_MAG)
167562306a36Sopenharmony_ci		wol->wolopts |= WAKE_MAGIC;
167662306a36Sopenharmony_ci}
167762306a36Sopenharmony_ci
167862306a36Sopenharmony_cistatic int e1000_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol)
167962306a36Sopenharmony_ci{
168062306a36Sopenharmony_ci	struct e1000_adapter *adapter = netdev_priv(netdev);
168162306a36Sopenharmony_ci	struct e1000_hw *hw = &adapter->hw;
168262306a36Sopenharmony_ci
168362306a36Sopenharmony_ci	if (wol->wolopts & (WAKE_PHY | WAKE_ARP | WAKE_MAGICSECURE))
168462306a36Sopenharmony_ci		return -EOPNOTSUPP;
168562306a36Sopenharmony_ci
168662306a36Sopenharmony_ci	if (e1000_wol_exclusion(adapter, wol) ||
168762306a36Sopenharmony_ci	    !device_can_wakeup(&adapter->pdev->dev))
168862306a36Sopenharmony_ci		return wol->wolopts ? -EOPNOTSUPP : 0;
168962306a36Sopenharmony_ci
169062306a36Sopenharmony_ci	switch (hw->device_id) {
169162306a36Sopenharmony_ci	case E1000_DEV_ID_82546GB_QUAD_COPPER_KSP3:
169262306a36Sopenharmony_ci		if (wol->wolopts & WAKE_UCAST) {
169362306a36Sopenharmony_ci			e_err(drv, "Interface does not support directed "
169462306a36Sopenharmony_ci			      "(unicast) frame wake-up packets\n");
169562306a36Sopenharmony_ci			return -EOPNOTSUPP;
169662306a36Sopenharmony_ci		}
169762306a36Sopenharmony_ci		break;
169862306a36Sopenharmony_ci	default:
169962306a36Sopenharmony_ci		break;
170062306a36Sopenharmony_ci	}
170162306a36Sopenharmony_ci
170262306a36Sopenharmony_ci	/* these settings will always override what we currently have */
170362306a36Sopenharmony_ci	adapter->wol = 0;
170462306a36Sopenharmony_ci
170562306a36Sopenharmony_ci	if (wol->wolopts & WAKE_UCAST)
170662306a36Sopenharmony_ci		adapter->wol |= E1000_WUFC_EX;
170762306a36Sopenharmony_ci	if (wol->wolopts & WAKE_MCAST)
170862306a36Sopenharmony_ci		adapter->wol |= E1000_WUFC_MC;
170962306a36Sopenharmony_ci	if (wol->wolopts & WAKE_BCAST)
171062306a36Sopenharmony_ci		adapter->wol |= E1000_WUFC_BC;
171162306a36Sopenharmony_ci	if (wol->wolopts & WAKE_MAGIC)
171262306a36Sopenharmony_ci		adapter->wol |= E1000_WUFC_MAG;
171362306a36Sopenharmony_ci
171462306a36Sopenharmony_ci	device_set_wakeup_enable(&adapter->pdev->dev, adapter->wol);
171562306a36Sopenharmony_ci
171662306a36Sopenharmony_ci	return 0;
171762306a36Sopenharmony_ci}
171862306a36Sopenharmony_ci
171962306a36Sopenharmony_cistatic int e1000_set_phys_id(struct net_device *netdev,
172062306a36Sopenharmony_ci			     enum ethtool_phys_id_state state)
172162306a36Sopenharmony_ci{
172262306a36Sopenharmony_ci	struct e1000_adapter *adapter = netdev_priv(netdev);
172362306a36Sopenharmony_ci	struct e1000_hw *hw = &adapter->hw;
172462306a36Sopenharmony_ci
172562306a36Sopenharmony_ci	switch (state) {
172662306a36Sopenharmony_ci	case ETHTOOL_ID_ACTIVE:
172762306a36Sopenharmony_ci		e1000_setup_led(hw);
172862306a36Sopenharmony_ci		return 2;
172962306a36Sopenharmony_ci
173062306a36Sopenharmony_ci	case ETHTOOL_ID_ON:
173162306a36Sopenharmony_ci		e1000_led_on(hw);
173262306a36Sopenharmony_ci		break;
173362306a36Sopenharmony_ci
173462306a36Sopenharmony_ci	case ETHTOOL_ID_OFF:
173562306a36Sopenharmony_ci		e1000_led_off(hw);
173662306a36Sopenharmony_ci		break;
173762306a36Sopenharmony_ci
173862306a36Sopenharmony_ci	case ETHTOOL_ID_INACTIVE:
173962306a36Sopenharmony_ci		e1000_cleanup_led(hw);
174062306a36Sopenharmony_ci	}
174162306a36Sopenharmony_ci
174262306a36Sopenharmony_ci	return 0;
174362306a36Sopenharmony_ci}
174462306a36Sopenharmony_ci
174562306a36Sopenharmony_cistatic int e1000_get_coalesce(struct net_device *netdev,
174662306a36Sopenharmony_ci			      struct ethtool_coalesce *ec,
174762306a36Sopenharmony_ci			      struct kernel_ethtool_coalesce *kernel_coal,
174862306a36Sopenharmony_ci			      struct netlink_ext_ack *extack)
174962306a36Sopenharmony_ci{
175062306a36Sopenharmony_ci	struct e1000_adapter *adapter = netdev_priv(netdev);
175162306a36Sopenharmony_ci
175262306a36Sopenharmony_ci	if (adapter->hw.mac_type < e1000_82545)
175362306a36Sopenharmony_ci		return -EOPNOTSUPP;
175462306a36Sopenharmony_ci
175562306a36Sopenharmony_ci	if (adapter->itr_setting <= 4)
175662306a36Sopenharmony_ci		ec->rx_coalesce_usecs = adapter->itr_setting;
175762306a36Sopenharmony_ci	else
175862306a36Sopenharmony_ci		ec->rx_coalesce_usecs = 1000000 / adapter->itr_setting;
175962306a36Sopenharmony_ci
176062306a36Sopenharmony_ci	return 0;
176162306a36Sopenharmony_ci}
176262306a36Sopenharmony_ci
176362306a36Sopenharmony_cistatic int e1000_set_coalesce(struct net_device *netdev,
176462306a36Sopenharmony_ci			      struct ethtool_coalesce *ec,
176562306a36Sopenharmony_ci			      struct kernel_ethtool_coalesce *kernel_coal,
176662306a36Sopenharmony_ci			      struct netlink_ext_ack *extack)
176762306a36Sopenharmony_ci{
176862306a36Sopenharmony_ci	struct e1000_adapter *adapter = netdev_priv(netdev);
176962306a36Sopenharmony_ci	struct e1000_hw *hw = &adapter->hw;
177062306a36Sopenharmony_ci
177162306a36Sopenharmony_ci	if (hw->mac_type < e1000_82545)
177262306a36Sopenharmony_ci		return -EOPNOTSUPP;
177362306a36Sopenharmony_ci
177462306a36Sopenharmony_ci	if ((ec->rx_coalesce_usecs > E1000_MAX_ITR_USECS) ||
177562306a36Sopenharmony_ci	    ((ec->rx_coalesce_usecs > 4) &&
177662306a36Sopenharmony_ci	     (ec->rx_coalesce_usecs < E1000_MIN_ITR_USECS)) ||
177762306a36Sopenharmony_ci	    (ec->rx_coalesce_usecs == 2))
177862306a36Sopenharmony_ci		return -EINVAL;
177962306a36Sopenharmony_ci
178062306a36Sopenharmony_ci	if (ec->rx_coalesce_usecs == 4) {
178162306a36Sopenharmony_ci		adapter->itr = adapter->itr_setting = 4;
178262306a36Sopenharmony_ci	} else if (ec->rx_coalesce_usecs <= 3) {
178362306a36Sopenharmony_ci		adapter->itr = 20000;
178462306a36Sopenharmony_ci		adapter->itr_setting = ec->rx_coalesce_usecs;
178562306a36Sopenharmony_ci	} else {
178662306a36Sopenharmony_ci		adapter->itr = (1000000 / ec->rx_coalesce_usecs);
178762306a36Sopenharmony_ci		adapter->itr_setting = adapter->itr & ~3;
178862306a36Sopenharmony_ci	}
178962306a36Sopenharmony_ci
179062306a36Sopenharmony_ci	if (adapter->itr_setting != 0)
179162306a36Sopenharmony_ci		ew32(ITR, 1000000000 / (adapter->itr * 256));
179262306a36Sopenharmony_ci	else
179362306a36Sopenharmony_ci		ew32(ITR, 0);
179462306a36Sopenharmony_ci
179562306a36Sopenharmony_ci	return 0;
179662306a36Sopenharmony_ci}
179762306a36Sopenharmony_ci
179862306a36Sopenharmony_cistatic int e1000_nway_reset(struct net_device *netdev)
179962306a36Sopenharmony_ci{
180062306a36Sopenharmony_ci	struct e1000_adapter *adapter = netdev_priv(netdev);
180162306a36Sopenharmony_ci
180262306a36Sopenharmony_ci	if (netif_running(netdev))
180362306a36Sopenharmony_ci		e1000_reinit_locked(adapter);
180462306a36Sopenharmony_ci	return 0;
180562306a36Sopenharmony_ci}
180662306a36Sopenharmony_ci
180762306a36Sopenharmony_cistatic void e1000_get_ethtool_stats(struct net_device *netdev,
180862306a36Sopenharmony_ci				    struct ethtool_stats *stats, u64 *data)
180962306a36Sopenharmony_ci{
181062306a36Sopenharmony_ci	struct e1000_adapter *adapter = netdev_priv(netdev);
181162306a36Sopenharmony_ci	int i;
181262306a36Sopenharmony_ci	const struct e1000_stats *stat = e1000_gstrings_stats;
181362306a36Sopenharmony_ci
181462306a36Sopenharmony_ci	e1000_update_stats(adapter);
181562306a36Sopenharmony_ci	for (i = 0; i < E1000_GLOBAL_STATS_LEN; i++, stat++) {
181662306a36Sopenharmony_ci		char *p;
181762306a36Sopenharmony_ci
181862306a36Sopenharmony_ci		switch (stat->type) {
181962306a36Sopenharmony_ci		case NETDEV_STATS:
182062306a36Sopenharmony_ci			p = (char *)netdev + stat->stat_offset;
182162306a36Sopenharmony_ci			break;
182262306a36Sopenharmony_ci		case E1000_STATS:
182362306a36Sopenharmony_ci			p = (char *)adapter + stat->stat_offset;
182462306a36Sopenharmony_ci			break;
182562306a36Sopenharmony_ci		default:
182662306a36Sopenharmony_ci			netdev_WARN_ONCE(netdev, "Invalid E1000 stat type: %u index %d\n",
182762306a36Sopenharmony_ci					 stat->type, i);
182862306a36Sopenharmony_ci			continue;
182962306a36Sopenharmony_ci		}
183062306a36Sopenharmony_ci
183162306a36Sopenharmony_ci		if (stat->sizeof_stat == sizeof(u64))
183262306a36Sopenharmony_ci			data[i] = *(u64 *)p;
183362306a36Sopenharmony_ci		else
183462306a36Sopenharmony_ci			data[i] = *(u32 *)p;
183562306a36Sopenharmony_ci	}
183662306a36Sopenharmony_ci/* BUG_ON(i != E1000_STATS_LEN); */
183762306a36Sopenharmony_ci}
183862306a36Sopenharmony_ci
183962306a36Sopenharmony_cistatic void e1000_get_strings(struct net_device *netdev, u32 stringset,
184062306a36Sopenharmony_ci			      u8 *data)
184162306a36Sopenharmony_ci{
184262306a36Sopenharmony_ci	u8 *p = data;
184362306a36Sopenharmony_ci	int i;
184462306a36Sopenharmony_ci
184562306a36Sopenharmony_ci	switch (stringset) {
184662306a36Sopenharmony_ci	case ETH_SS_TEST:
184762306a36Sopenharmony_ci		memcpy(data, e1000_gstrings_test, sizeof(e1000_gstrings_test));
184862306a36Sopenharmony_ci		break;
184962306a36Sopenharmony_ci	case ETH_SS_STATS:
185062306a36Sopenharmony_ci		for (i = 0; i < E1000_GLOBAL_STATS_LEN; i++) {
185162306a36Sopenharmony_ci			memcpy(p, e1000_gstrings_stats[i].stat_string,
185262306a36Sopenharmony_ci			       ETH_GSTRING_LEN);
185362306a36Sopenharmony_ci			p += ETH_GSTRING_LEN;
185462306a36Sopenharmony_ci		}
185562306a36Sopenharmony_ci		/* BUG_ON(p - data != E1000_STATS_LEN * ETH_GSTRING_LEN); */
185662306a36Sopenharmony_ci		break;
185762306a36Sopenharmony_ci	}
185862306a36Sopenharmony_ci}
185962306a36Sopenharmony_ci
186062306a36Sopenharmony_cistatic const struct ethtool_ops e1000_ethtool_ops = {
186162306a36Sopenharmony_ci	.supported_coalesce_params = ETHTOOL_COALESCE_RX_USECS,
186262306a36Sopenharmony_ci	.get_drvinfo		= e1000_get_drvinfo,
186362306a36Sopenharmony_ci	.get_regs_len		= e1000_get_regs_len,
186462306a36Sopenharmony_ci	.get_regs		= e1000_get_regs,
186562306a36Sopenharmony_ci	.get_wol		= e1000_get_wol,
186662306a36Sopenharmony_ci	.set_wol		= e1000_set_wol,
186762306a36Sopenharmony_ci	.get_msglevel		= e1000_get_msglevel,
186862306a36Sopenharmony_ci	.set_msglevel		= e1000_set_msglevel,
186962306a36Sopenharmony_ci	.nway_reset		= e1000_nway_reset,
187062306a36Sopenharmony_ci	.get_link		= e1000_get_link,
187162306a36Sopenharmony_ci	.get_eeprom_len		= e1000_get_eeprom_len,
187262306a36Sopenharmony_ci	.get_eeprom		= e1000_get_eeprom,
187362306a36Sopenharmony_ci	.set_eeprom		= e1000_set_eeprom,
187462306a36Sopenharmony_ci	.get_ringparam		= e1000_get_ringparam,
187562306a36Sopenharmony_ci	.set_ringparam		= e1000_set_ringparam,
187662306a36Sopenharmony_ci	.get_pauseparam		= e1000_get_pauseparam,
187762306a36Sopenharmony_ci	.set_pauseparam		= e1000_set_pauseparam,
187862306a36Sopenharmony_ci	.self_test		= e1000_diag_test,
187962306a36Sopenharmony_ci	.get_strings		= e1000_get_strings,
188062306a36Sopenharmony_ci	.set_phys_id		= e1000_set_phys_id,
188162306a36Sopenharmony_ci	.get_ethtool_stats	= e1000_get_ethtool_stats,
188262306a36Sopenharmony_ci	.get_sset_count		= e1000_get_sset_count,
188362306a36Sopenharmony_ci	.get_coalesce		= e1000_get_coalesce,
188462306a36Sopenharmony_ci	.set_coalesce		= e1000_set_coalesce,
188562306a36Sopenharmony_ci	.get_ts_info		= ethtool_op_get_ts_info,
188662306a36Sopenharmony_ci	.get_link_ksettings	= e1000_get_link_ksettings,
188762306a36Sopenharmony_ci	.set_link_ksettings	= e1000_set_link_ksettings,
188862306a36Sopenharmony_ci};
188962306a36Sopenharmony_ci
189062306a36Sopenharmony_civoid e1000_set_ethtool_ops(struct net_device *netdev)
189162306a36Sopenharmony_ci{
189262306a36Sopenharmony_ci	netdev->ethtool_ops = &e1000_ethtool_ops;
189362306a36Sopenharmony_ci}
1894