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