162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * QLogic qlcnic NIC Driver 462306a36Sopenharmony_ci * Copyright (c) 2009-2013 QLogic Corporation 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/types.h> 862306a36Sopenharmony_ci#include <linux/delay.h> 962306a36Sopenharmony_ci#include <linux/pci.h> 1062306a36Sopenharmony_ci#include <linux/io.h> 1162306a36Sopenharmony_ci#include <linux/netdevice.h> 1262306a36Sopenharmony_ci#include <linux/ethtool.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include "qlcnic.h" 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_cistruct qlcnic_stats { 1762306a36Sopenharmony_ci char stat_string[ETH_GSTRING_LEN]; 1862306a36Sopenharmony_ci int sizeof_stat; 1962306a36Sopenharmony_ci int stat_offset; 2062306a36Sopenharmony_ci}; 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#define QLC_SIZEOF(m) sizeof_field(struct qlcnic_adapter, m) 2362306a36Sopenharmony_ci#define QLC_OFF(m) offsetof(struct qlcnic_adapter, m) 2462306a36Sopenharmony_cistatic const u32 qlcnic_fw_dump_level[] = { 2562306a36Sopenharmony_ci 0x3, 0x7, 0xf, 0x1f, 0x3f, 0x7f, 0xff 2662306a36Sopenharmony_ci}; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic const struct qlcnic_stats qlcnic_gstrings_stats[] = { 2962306a36Sopenharmony_ci {"xmit_on", QLC_SIZEOF(stats.xmit_on), QLC_OFF(stats.xmit_on)}, 3062306a36Sopenharmony_ci {"xmit_off", QLC_SIZEOF(stats.xmit_off), QLC_OFF(stats.xmit_off)}, 3162306a36Sopenharmony_ci {"xmit_called", QLC_SIZEOF(stats.xmitcalled), 3262306a36Sopenharmony_ci QLC_OFF(stats.xmitcalled)}, 3362306a36Sopenharmony_ci {"xmit_finished", QLC_SIZEOF(stats.xmitfinished), 3462306a36Sopenharmony_ci QLC_OFF(stats.xmitfinished)}, 3562306a36Sopenharmony_ci {"tx dma map error", QLC_SIZEOF(stats.tx_dma_map_error), 3662306a36Sopenharmony_ci QLC_OFF(stats.tx_dma_map_error)}, 3762306a36Sopenharmony_ci {"tx_bytes", QLC_SIZEOF(stats.txbytes), QLC_OFF(stats.txbytes)}, 3862306a36Sopenharmony_ci {"tx_dropped", QLC_SIZEOF(stats.txdropped), QLC_OFF(stats.txdropped)}, 3962306a36Sopenharmony_ci {"rx dma map error", QLC_SIZEOF(stats.rx_dma_map_error), 4062306a36Sopenharmony_ci QLC_OFF(stats.rx_dma_map_error)}, 4162306a36Sopenharmony_ci {"rx_pkts", QLC_SIZEOF(stats.rx_pkts), QLC_OFF(stats.rx_pkts)}, 4262306a36Sopenharmony_ci {"rx_bytes", QLC_SIZEOF(stats.rxbytes), QLC_OFF(stats.rxbytes)}, 4362306a36Sopenharmony_ci {"rx_dropped", QLC_SIZEOF(stats.rxdropped), QLC_OFF(stats.rxdropped)}, 4462306a36Sopenharmony_ci {"null rxbuf", QLC_SIZEOF(stats.null_rxbuf), QLC_OFF(stats.null_rxbuf)}, 4562306a36Sopenharmony_ci {"csummed", QLC_SIZEOF(stats.csummed), QLC_OFF(stats.csummed)}, 4662306a36Sopenharmony_ci {"lro_pkts", QLC_SIZEOF(stats.lro_pkts), QLC_OFF(stats.lro_pkts)}, 4762306a36Sopenharmony_ci {"lrobytes", QLC_SIZEOF(stats.lrobytes), QLC_OFF(stats.lrobytes)}, 4862306a36Sopenharmony_ci {"lso_frames", QLC_SIZEOF(stats.lso_frames), QLC_OFF(stats.lso_frames)}, 4962306a36Sopenharmony_ci {"encap_lso_frames", QLC_SIZEOF(stats.encap_lso_frames), 5062306a36Sopenharmony_ci QLC_OFF(stats.encap_lso_frames)}, 5162306a36Sopenharmony_ci {"encap_tx_csummed", QLC_SIZEOF(stats.encap_tx_csummed), 5262306a36Sopenharmony_ci QLC_OFF(stats.encap_tx_csummed)}, 5362306a36Sopenharmony_ci {"encap_rx_csummed", QLC_SIZEOF(stats.encap_rx_csummed), 5462306a36Sopenharmony_ci QLC_OFF(stats.encap_rx_csummed)}, 5562306a36Sopenharmony_ci {"skb_alloc_failure", QLC_SIZEOF(stats.skb_alloc_failure), 5662306a36Sopenharmony_ci QLC_OFF(stats.skb_alloc_failure)}, 5762306a36Sopenharmony_ci {"mac_filter_limit_overrun", QLC_SIZEOF(stats.mac_filter_limit_overrun), 5862306a36Sopenharmony_ci QLC_OFF(stats.mac_filter_limit_overrun)}, 5962306a36Sopenharmony_ci {"spurious intr", QLC_SIZEOF(stats.spurious_intr), 6062306a36Sopenharmony_ci QLC_OFF(stats.spurious_intr)}, 6162306a36Sopenharmony_ci {"mbx spurious intr", QLC_SIZEOF(stats.mbx_spurious_intr), 6262306a36Sopenharmony_ci QLC_OFF(stats.mbx_spurious_intr)}, 6362306a36Sopenharmony_ci}; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_cistatic const char qlcnic_device_gstrings_stats[][ETH_GSTRING_LEN] = { 6662306a36Sopenharmony_ci "tx unicast frames", 6762306a36Sopenharmony_ci "tx multicast frames", 6862306a36Sopenharmony_ci "tx broadcast frames", 6962306a36Sopenharmony_ci "tx dropped frames", 7062306a36Sopenharmony_ci "tx errors", 7162306a36Sopenharmony_ci "tx local frames", 7262306a36Sopenharmony_ci "tx numbytes", 7362306a36Sopenharmony_ci "rx unicast frames", 7462306a36Sopenharmony_ci "rx multicast frames", 7562306a36Sopenharmony_ci "rx broadcast frames", 7662306a36Sopenharmony_ci "rx dropped frames", 7762306a36Sopenharmony_ci "rx errors", 7862306a36Sopenharmony_ci "rx local frames", 7962306a36Sopenharmony_ci "rx numbytes", 8062306a36Sopenharmony_ci}; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistatic const char qlcnic_83xx_tx_stats_strings[][ETH_GSTRING_LEN] = { 8362306a36Sopenharmony_ci "ctx_tx_bytes", 8462306a36Sopenharmony_ci "ctx_tx_pkts", 8562306a36Sopenharmony_ci "ctx_tx_errors", 8662306a36Sopenharmony_ci "ctx_tx_dropped_pkts", 8762306a36Sopenharmony_ci "ctx_tx_num_buffers", 8862306a36Sopenharmony_ci}; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_cistatic const char qlcnic_83xx_mac_stats_strings[][ETH_GSTRING_LEN] = { 9162306a36Sopenharmony_ci "mac_tx_frames", 9262306a36Sopenharmony_ci "mac_tx_bytes", 9362306a36Sopenharmony_ci "mac_tx_mcast_pkts", 9462306a36Sopenharmony_ci "mac_tx_bcast_pkts", 9562306a36Sopenharmony_ci "mac_tx_pause_cnt", 9662306a36Sopenharmony_ci "mac_tx_ctrl_pkt", 9762306a36Sopenharmony_ci "mac_tx_lt_64b_pkts", 9862306a36Sopenharmony_ci "mac_tx_lt_127b_pkts", 9962306a36Sopenharmony_ci "mac_tx_lt_255b_pkts", 10062306a36Sopenharmony_ci "mac_tx_lt_511b_pkts", 10162306a36Sopenharmony_ci "mac_tx_lt_1023b_pkts", 10262306a36Sopenharmony_ci "mac_tx_lt_1518b_pkts", 10362306a36Sopenharmony_ci "mac_tx_gt_1518b_pkts", 10462306a36Sopenharmony_ci "mac_rx_frames", 10562306a36Sopenharmony_ci "mac_rx_bytes", 10662306a36Sopenharmony_ci "mac_rx_mcast_pkts", 10762306a36Sopenharmony_ci "mac_rx_bcast_pkts", 10862306a36Sopenharmony_ci "mac_rx_pause_cnt", 10962306a36Sopenharmony_ci "mac_rx_ctrl_pkt", 11062306a36Sopenharmony_ci "mac_rx_lt_64b_pkts", 11162306a36Sopenharmony_ci "mac_rx_lt_127b_pkts", 11262306a36Sopenharmony_ci "mac_rx_lt_255b_pkts", 11362306a36Sopenharmony_ci "mac_rx_lt_511b_pkts", 11462306a36Sopenharmony_ci "mac_rx_lt_1023b_pkts", 11562306a36Sopenharmony_ci "mac_rx_lt_1518b_pkts", 11662306a36Sopenharmony_ci "mac_rx_gt_1518b_pkts", 11762306a36Sopenharmony_ci "mac_rx_length_error", 11862306a36Sopenharmony_ci "mac_rx_length_small", 11962306a36Sopenharmony_ci "mac_rx_length_large", 12062306a36Sopenharmony_ci "mac_rx_jabber", 12162306a36Sopenharmony_ci "mac_rx_dropped", 12262306a36Sopenharmony_ci "mac_crc_error", 12362306a36Sopenharmony_ci "mac_align_error", 12462306a36Sopenharmony_ci "eswitch_frames", 12562306a36Sopenharmony_ci "eswitch_bytes", 12662306a36Sopenharmony_ci "eswitch_multicast_frames", 12762306a36Sopenharmony_ci "eswitch_broadcast_frames", 12862306a36Sopenharmony_ci "eswitch_unicast_frames", 12962306a36Sopenharmony_ci "eswitch_error_free_frames", 13062306a36Sopenharmony_ci "eswitch_error_free_bytes", 13162306a36Sopenharmony_ci}; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci#define QLCNIC_STATS_LEN ARRAY_SIZE(qlcnic_gstrings_stats) 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_cistatic const char qlcnic_tx_queue_stats_strings[][ETH_GSTRING_LEN] = { 13662306a36Sopenharmony_ci "xmit_on", 13762306a36Sopenharmony_ci "xmit_off", 13862306a36Sopenharmony_ci "xmit_called", 13962306a36Sopenharmony_ci "xmit_finished", 14062306a36Sopenharmony_ci "tx_bytes", 14162306a36Sopenharmony_ci}; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci#define QLCNIC_TX_STATS_LEN ARRAY_SIZE(qlcnic_tx_queue_stats_strings) 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_cistatic const char qlcnic_83xx_rx_stats_strings[][ETH_GSTRING_LEN] = { 14662306a36Sopenharmony_ci "ctx_rx_bytes", 14762306a36Sopenharmony_ci "ctx_rx_pkts", 14862306a36Sopenharmony_ci "ctx_lro_pkt_cnt", 14962306a36Sopenharmony_ci "ctx_ip_csum_error", 15062306a36Sopenharmony_ci "ctx_rx_pkts_wo_ctx", 15162306a36Sopenharmony_ci "ctx_rx_pkts_drop_wo_sds_on_card", 15262306a36Sopenharmony_ci "ctx_rx_pkts_drop_wo_sds_on_host", 15362306a36Sopenharmony_ci "ctx_rx_osized_pkts", 15462306a36Sopenharmony_ci "ctx_rx_pkts_dropped_wo_rds", 15562306a36Sopenharmony_ci "ctx_rx_unexpected_mcast_pkts", 15662306a36Sopenharmony_ci "ctx_invalid_mac_address", 15762306a36Sopenharmony_ci "ctx_rx_rds_ring_prim_attempted", 15862306a36Sopenharmony_ci "ctx_rx_rds_ring_prim_success", 15962306a36Sopenharmony_ci "ctx_num_lro_flows_added", 16062306a36Sopenharmony_ci "ctx_num_lro_flows_removed", 16162306a36Sopenharmony_ci "ctx_num_lro_flows_active", 16262306a36Sopenharmony_ci "ctx_pkts_dropped_unknown", 16362306a36Sopenharmony_ci}; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_cistatic const char qlcnic_gstrings_test[][ETH_GSTRING_LEN] = { 16662306a36Sopenharmony_ci "Register_Test_on_offline", 16762306a36Sopenharmony_ci "Link_Test_on_offline", 16862306a36Sopenharmony_ci "Interrupt_Test_offline", 16962306a36Sopenharmony_ci "Internal_Loopback_offline", 17062306a36Sopenharmony_ci "External_Loopback_offline", 17162306a36Sopenharmony_ci "EEPROM_Test_offline" 17262306a36Sopenharmony_ci}; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci#define QLCNIC_TEST_LEN ARRAY_SIZE(qlcnic_gstrings_test) 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_cistatic inline int qlcnic_82xx_statistics(struct qlcnic_adapter *adapter) 17762306a36Sopenharmony_ci{ 17862306a36Sopenharmony_ci return ARRAY_SIZE(qlcnic_gstrings_stats) + 17962306a36Sopenharmony_ci ARRAY_SIZE(qlcnic_83xx_mac_stats_strings) + 18062306a36Sopenharmony_ci QLCNIC_TX_STATS_LEN * adapter->drv_tx_rings; 18162306a36Sopenharmony_ci} 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_cistatic inline int qlcnic_83xx_statistics(struct qlcnic_adapter *adapter) 18462306a36Sopenharmony_ci{ 18562306a36Sopenharmony_ci return ARRAY_SIZE(qlcnic_gstrings_stats) + 18662306a36Sopenharmony_ci ARRAY_SIZE(qlcnic_83xx_tx_stats_strings) + 18762306a36Sopenharmony_ci ARRAY_SIZE(qlcnic_83xx_mac_stats_strings) + 18862306a36Sopenharmony_ci ARRAY_SIZE(qlcnic_83xx_rx_stats_strings) + 18962306a36Sopenharmony_ci QLCNIC_TX_STATS_LEN * adapter->drv_tx_rings; 19062306a36Sopenharmony_ci} 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_cistatic int qlcnic_dev_statistics_len(struct qlcnic_adapter *adapter) 19362306a36Sopenharmony_ci{ 19462306a36Sopenharmony_ci int len = -1; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci if (qlcnic_82xx_check(adapter)) { 19762306a36Sopenharmony_ci len = qlcnic_82xx_statistics(adapter); 19862306a36Sopenharmony_ci if (adapter->flags & QLCNIC_ESWITCH_ENABLED) 19962306a36Sopenharmony_ci len += ARRAY_SIZE(qlcnic_device_gstrings_stats); 20062306a36Sopenharmony_ci } else if (qlcnic_83xx_check(adapter)) { 20162306a36Sopenharmony_ci len = qlcnic_83xx_statistics(adapter); 20262306a36Sopenharmony_ci } 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci return len; 20562306a36Sopenharmony_ci} 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci#define QLCNIC_TX_INTR_NOT_CONFIGURED 0X78563412 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci#define QLCNIC_MAX_EEPROM_LEN 1024 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_cistatic const u32 diag_registers[] = { 21262306a36Sopenharmony_ci QLCNIC_CMDPEG_STATE, 21362306a36Sopenharmony_ci QLCNIC_RCVPEG_STATE, 21462306a36Sopenharmony_ci QLCNIC_FW_CAPABILITIES, 21562306a36Sopenharmony_ci QLCNIC_CRB_DRV_ACTIVE, 21662306a36Sopenharmony_ci QLCNIC_CRB_DEV_STATE, 21762306a36Sopenharmony_ci QLCNIC_CRB_DRV_STATE, 21862306a36Sopenharmony_ci QLCNIC_CRB_DRV_SCRATCH, 21962306a36Sopenharmony_ci QLCNIC_CRB_DEV_PARTITION_INFO, 22062306a36Sopenharmony_ci QLCNIC_CRB_DRV_IDC_VER, 22162306a36Sopenharmony_ci QLCNIC_PEG_ALIVE_COUNTER, 22262306a36Sopenharmony_ci QLCNIC_PEG_HALT_STATUS1, 22362306a36Sopenharmony_ci QLCNIC_PEG_HALT_STATUS2, 22462306a36Sopenharmony_ci -1 22562306a36Sopenharmony_ci}; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_cistatic const u32 ext_diag_registers[] = { 22962306a36Sopenharmony_ci CRB_XG_STATE_P3P, 23062306a36Sopenharmony_ci ISR_INT_STATE_REG, 23162306a36Sopenharmony_ci QLCNIC_CRB_PEG_NET_0+0x3c, 23262306a36Sopenharmony_ci QLCNIC_CRB_PEG_NET_1+0x3c, 23362306a36Sopenharmony_ci QLCNIC_CRB_PEG_NET_2+0x3c, 23462306a36Sopenharmony_ci QLCNIC_CRB_PEG_NET_4+0x3c, 23562306a36Sopenharmony_ci -1 23662306a36Sopenharmony_ci}; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci#define QLCNIC_MGMT_API_VERSION 3 23962306a36Sopenharmony_ci#define QLCNIC_ETHTOOL_REGS_VER 4 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_cistatic inline int qlcnic_get_ring_regs_len(struct qlcnic_adapter *adapter) 24262306a36Sopenharmony_ci{ 24362306a36Sopenharmony_ci int ring_regs_cnt = (adapter->drv_tx_rings * 5) + 24462306a36Sopenharmony_ci (adapter->max_rds_rings * 2) + 24562306a36Sopenharmony_ci (adapter->drv_sds_rings * 3) + 5; 24662306a36Sopenharmony_ci return ring_regs_cnt * sizeof(u32); 24762306a36Sopenharmony_ci} 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_cistatic int qlcnic_get_regs_len(struct net_device *dev) 25062306a36Sopenharmony_ci{ 25162306a36Sopenharmony_ci struct qlcnic_adapter *adapter = netdev_priv(dev); 25262306a36Sopenharmony_ci u32 len; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci if (qlcnic_83xx_check(adapter)) 25562306a36Sopenharmony_ci len = qlcnic_83xx_get_regs_len(adapter); 25662306a36Sopenharmony_ci else 25762306a36Sopenharmony_ci len = sizeof(ext_diag_registers) + sizeof(diag_registers); 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci len += ((QLCNIC_DEV_INFO_SIZE + 2) * sizeof(u32)); 26062306a36Sopenharmony_ci len += qlcnic_get_ring_regs_len(adapter); 26162306a36Sopenharmony_ci return len; 26262306a36Sopenharmony_ci} 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_cistatic int qlcnic_get_eeprom_len(struct net_device *dev) 26562306a36Sopenharmony_ci{ 26662306a36Sopenharmony_ci return QLCNIC_FLASH_TOTAL_SIZE; 26762306a36Sopenharmony_ci} 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_cistatic void 27062306a36Sopenharmony_ciqlcnic_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *drvinfo) 27162306a36Sopenharmony_ci{ 27262306a36Sopenharmony_ci struct qlcnic_adapter *adapter = netdev_priv(dev); 27362306a36Sopenharmony_ci u32 fw_major, fw_minor, fw_build; 27462306a36Sopenharmony_ci fw_major = QLC_SHARED_REG_RD32(adapter, QLCNIC_FW_VERSION_MAJOR); 27562306a36Sopenharmony_ci fw_minor = QLC_SHARED_REG_RD32(adapter, QLCNIC_FW_VERSION_MINOR); 27662306a36Sopenharmony_ci fw_build = QLC_SHARED_REG_RD32(adapter, QLCNIC_FW_VERSION_SUB); 27762306a36Sopenharmony_ci snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version), 27862306a36Sopenharmony_ci "%d.%d.%d", fw_major, fw_minor, fw_build); 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci strscpy(drvinfo->bus_info, pci_name(adapter->pdev), 28162306a36Sopenharmony_ci sizeof(drvinfo->bus_info)); 28262306a36Sopenharmony_ci strscpy(drvinfo->driver, qlcnic_driver_name, sizeof(drvinfo->driver)); 28362306a36Sopenharmony_ci strscpy(drvinfo->version, QLCNIC_LINUX_VERSIONID, 28462306a36Sopenharmony_ci sizeof(drvinfo->version)); 28562306a36Sopenharmony_ci} 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_cistatic int qlcnic_82xx_get_link_ksettings(struct qlcnic_adapter *adapter, 28862306a36Sopenharmony_ci struct ethtool_link_ksettings *ecmd) 28962306a36Sopenharmony_ci{ 29062306a36Sopenharmony_ci struct qlcnic_hardware_context *ahw = adapter->ahw; 29162306a36Sopenharmony_ci u32 speed, reg; 29262306a36Sopenharmony_ci int check_sfp_module = 0, err = 0; 29362306a36Sopenharmony_ci u16 pcifn = ahw->pci_func; 29462306a36Sopenharmony_ci u32 supported, advertising; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci /* read which mode */ 29762306a36Sopenharmony_ci if (adapter->ahw->port_type == QLCNIC_GBE) { 29862306a36Sopenharmony_ci supported = (SUPPORTED_10baseT_Half | 29962306a36Sopenharmony_ci SUPPORTED_10baseT_Full | 30062306a36Sopenharmony_ci SUPPORTED_100baseT_Half | 30162306a36Sopenharmony_ci SUPPORTED_100baseT_Full | 30262306a36Sopenharmony_ci SUPPORTED_1000baseT_Half | 30362306a36Sopenharmony_ci SUPPORTED_1000baseT_Full); 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci advertising = (ADVERTISED_100baseT_Half | 30662306a36Sopenharmony_ci ADVERTISED_100baseT_Full | 30762306a36Sopenharmony_ci ADVERTISED_1000baseT_Half | 30862306a36Sopenharmony_ci ADVERTISED_1000baseT_Full); 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci ecmd->base.speed = adapter->ahw->link_speed; 31162306a36Sopenharmony_ci ecmd->base.duplex = adapter->ahw->link_duplex; 31262306a36Sopenharmony_ci ecmd->base.autoneg = adapter->ahw->link_autoneg; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci } else if (adapter->ahw->port_type == QLCNIC_XGBE) { 31562306a36Sopenharmony_ci u32 val = 0; 31662306a36Sopenharmony_ci val = QLCRD32(adapter, QLCNIC_PORT_MODE_ADDR, &err); 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci if (val == QLCNIC_PORT_MODE_802_3_AP) { 31962306a36Sopenharmony_ci supported = SUPPORTED_1000baseT_Full; 32062306a36Sopenharmony_ci advertising = ADVERTISED_1000baseT_Full; 32162306a36Sopenharmony_ci } else { 32262306a36Sopenharmony_ci supported = SUPPORTED_10000baseT_Full; 32362306a36Sopenharmony_ci advertising = ADVERTISED_10000baseT_Full; 32462306a36Sopenharmony_ci } 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci if (netif_running(adapter->netdev) && ahw->has_link_events) { 32762306a36Sopenharmony_ci if (ahw->linkup) { 32862306a36Sopenharmony_ci reg = QLCRD32(adapter, 32962306a36Sopenharmony_ci P3P_LINK_SPEED_REG(pcifn), &err); 33062306a36Sopenharmony_ci speed = P3P_LINK_SPEED_VAL(pcifn, reg); 33162306a36Sopenharmony_ci ahw->link_speed = speed * P3P_LINK_SPEED_MHZ; 33262306a36Sopenharmony_ci } 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci ecmd->base.speed = ahw->link_speed; 33562306a36Sopenharmony_ci ecmd->base.autoneg = ahw->link_autoneg; 33662306a36Sopenharmony_ci ecmd->base.duplex = ahw->link_duplex; 33762306a36Sopenharmony_ci goto skip; 33862306a36Sopenharmony_ci } 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci ecmd->base.speed = SPEED_UNKNOWN; 34162306a36Sopenharmony_ci ecmd->base.duplex = DUPLEX_UNKNOWN; 34262306a36Sopenharmony_ci ecmd->base.autoneg = AUTONEG_DISABLE; 34362306a36Sopenharmony_ci } else 34462306a36Sopenharmony_ci return -EIO; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ciskip: 34762306a36Sopenharmony_ci ecmd->base.phy_address = adapter->ahw->physical_port; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci switch (adapter->ahw->board_type) { 35062306a36Sopenharmony_ci case QLCNIC_BRDTYPE_P3P_REF_QG: 35162306a36Sopenharmony_ci case QLCNIC_BRDTYPE_P3P_4_GB: 35262306a36Sopenharmony_ci case QLCNIC_BRDTYPE_P3P_4_GB_MM: 35362306a36Sopenharmony_ci supported |= SUPPORTED_Autoneg; 35462306a36Sopenharmony_ci advertising |= ADVERTISED_Autoneg; 35562306a36Sopenharmony_ci fallthrough; 35662306a36Sopenharmony_ci case QLCNIC_BRDTYPE_P3P_10G_CX4: 35762306a36Sopenharmony_ci case QLCNIC_BRDTYPE_P3P_10G_CX4_LP: 35862306a36Sopenharmony_ci case QLCNIC_BRDTYPE_P3P_10000_BASE_T: 35962306a36Sopenharmony_ci supported |= SUPPORTED_TP; 36062306a36Sopenharmony_ci advertising |= ADVERTISED_TP; 36162306a36Sopenharmony_ci ecmd->base.port = PORT_TP; 36262306a36Sopenharmony_ci ecmd->base.autoneg = adapter->ahw->link_autoneg; 36362306a36Sopenharmony_ci break; 36462306a36Sopenharmony_ci case QLCNIC_BRDTYPE_P3P_IMEZ: 36562306a36Sopenharmony_ci case QLCNIC_BRDTYPE_P3P_XG_LOM: 36662306a36Sopenharmony_ci case QLCNIC_BRDTYPE_P3P_HMEZ: 36762306a36Sopenharmony_ci supported |= SUPPORTED_MII; 36862306a36Sopenharmony_ci advertising |= ADVERTISED_MII; 36962306a36Sopenharmony_ci ecmd->base.port = PORT_MII; 37062306a36Sopenharmony_ci ecmd->base.autoneg = AUTONEG_DISABLE; 37162306a36Sopenharmony_ci break; 37262306a36Sopenharmony_ci case QLCNIC_BRDTYPE_P3P_10G_SFP_PLUS: 37362306a36Sopenharmony_ci case QLCNIC_BRDTYPE_P3P_10G_SFP_CT: 37462306a36Sopenharmony_ci case QLCNIC_BRDTYPE_P3P_10G_SFP_QT: 37562306a36Sopenharmony_ci advertising |= ADVERTISED_TP; 37662306a36Sopenharmony_ci supported |= SUPPORTED_TP; 37762306a36Sopenharmony_ci check_sfp_module = netif_running(adapter->netdev) && 37862306a36Sopenharmony_ci ahw->has_link_events; 37962306a36Sopenharmony_ci fallthrough; 38062306a36Sopenharmony_ci case QLCNIC_BRDTYPE_P3P_10G_XFP: 38162306a36Sopenharmony_ci supported |= SUPPORTED_FIBRE; 38262306a36Sopenharmony_ci advertising |= ADVERTISED_FIBRE; 38362306a36Sopenharmony_ci ecmd->base.port = PORT_FIBRE; 38462306a36Sopenharmony_ci ecmd->base.autoneg = AUTONEG_DISABLE; 38562306a36Sopenharmony_ci break; 38662306a36Sopenharmony_ci case QLCNIC_BRDTYPE_P3P_10G_TP: 38762306a36Sopenharmony_ci if (adapter->ahw->port_type == QLCNIC_XGBE) { 38862306a36Sopenharmony_ci ecmd->base.autoneg = AUTONEG_DISABLE; 38962306a36Sopenharmony_ci supported |= (SUPPORTED_FIBRE | SUPPORTED_TP); 39062306a36Sopenharmony_ci advertising |= 39162306a36Sopenharmony_ci (ADVERTISED_FIBRE | ADVERTISED_TP); 39262306a36Sopenharmony_ci ecmd->base.port = PORT_FIBRE; 39362306a36Sopenharmony_ci check_sfp_module = netif_running(adapter->netdev) && 39462306a36Sopenharmony_ci ahw->has_link_events; 39562306a36Sopenharmony_ci } else { 39662306a36Sopenharmony_ci ecmd->base.autoneg = AUTONEG_ENABLE; 39762306a36Sopenharmony_ci supported |= (SUPPORTED_TP | SUPPORTED_Autoneg); 39862306a36Sopenharmony_ci advertising |= 39962306a36Sopenharmony_ci (ADVERTISED_TP | ADVERTISED_Autoneg); 40062306a36Sopenharmony_ci ecmd->base.port = PORT_TP; 40162306a36Sopenharmony_ci } 40262306a36Sopenharmony_ci break; 40362306a36Sopenharmony_ci default: 40462306a36Sopenharmony_ci dev_err(&adapter->pdev->dev, "Unsupported board model %d\n", 40562306a36Sopenharmony_ci adapter->ahw->board_type); 40662306a36Sopenharmony_ci return -EIO; 40762306a36Sopenharmony_ci } 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci if (check_sfp_module) { 41062306a36Sopenharmony_ci switch (adapter->ahw->module_type) { 41162306a36Sopenharmony_ci case LINKEVENT_MODULE_OPTICAL_UNKNOWN: 41262306a36Sopenharmony_ci case LINKEVENT_MODULE_OPTICAL_SRLR: 41362306a36Sopenharmony_ci case LINKEVENT_MODULE_OPTICAL_LRM: 41462306a36Sopenharmony_ci case LINKEVENT_MODULE_OPTICAL_SFP_1G: 41562306a36Sopenharmony_ci ecmd->base.port = PORT_FIBRE; 41662306a36Sopenharmony_ci break; 41762306a36Sopenharmony_ci case LINKEVENT_MODULE_TWINAX_UNSUPPORTED_CABLE: 41862306a36Sopenharmony_ci case LINKEVENT_MODULE_TWINAX_UNSUPPORTED_CABLELEN: 41962306a36Sopenharmony_ci case LINKEVENT_MODULE_TWINAX: 42062306a36Sopenharmony_ci ecmd->base.port = PORT_TP; 42162306a36Sopenharmony_ci break; 42262306a36Sopenharmony_ci default: 42362306a36Sopenharmony_ci ecmd->base.port = PORT_OTHER; 42462306a36Sopenharmony_ci } 42562306a36Sopenharmony_ci } 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci ethtool_convert_legacy_u32_to_link_mode(ecmd->link_modes.supported, 42862306a36Sopenharmony_ci supported); 42962306a36Sopenharmony_ci ethtool_convert_legacy_u32_to_link_mode(ecmd->link_modes.advertising, 43062306a36Sopenharmony_ci advertising); 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci return 0; 43362306a36Sopenharmony_ci} 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_cistatic int qlcnic_get_link_ksettings(struct net_device *dev, 43662306a36Sopenharmony_ci struct ethtool_link_ksettings *ecmd) 43762306a36Sopenharmony_ci{ 43862306a36Sopenharmony_ci struct qlcnic_adapter *adapter = netdev_priv(dev); 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci if (qlcnic_82xx_check(adapter)) 44162306a36Sopenharmony_ci return qlcnic_82xx_get_link_ksettings(adapter, ecmd); 44262306a36Sopenharmony_ci else if (qlcnic_83xx_check(adapter)) 44362306a36Sopenharmony_ci return qlcnic_83xx_get_link_ksettings(adapter, ecmd); 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci return -EIO; 44662306a36Sopenharmony_ci} 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_cistatic int qlcnic_set_port_config(struct qlcnic_adapter *adapter, 45062306a36Sopenharmony_ci const struct ethtool_link_ksettings *ecmd) 45162306a36Sopenharmony_ci{ 45262306a36Sopenharmony_ci u32 ret = 0, config = 0; 45362306a36Sopenharmony_ci /* read which mode */ 45462306a36Sopenharmony_ci if (ecmd->base.duplex) 45562306a36Sopenharmony_ci config |= 0x1; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci if (ecmd->base.autoneg) 45862306a36Sopenharmony_ci config |= 0x2; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci switch (ecmd->base.speed) { 46162306a36Sopenharmony_ci case SPEED_10: 46262306a36Sopenharmony_ci config |= (0 << 8); 46362306a36Sopenharmony_ci break; 46462306a36Sopenharmony_ci case SPEED_100: 46562306a36Sopenharmony_ci config |= (1 << 8); 46662306a36Sopenharmony_ci break; 46762306a36Sopenharmony_ci case SPEED_1000: 46862306a36Sopenharmony_ci config |= (10 << 8); 46962306a36Sopenharmony_ci break; 47062306a36Sopenharmony_ci default: 47162306a36Sopenharmony_ci return -EIO; 47262306a36Sopenharmony_ci } 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci ret = qlcnic_fw_cmd_set_port(adapter, config); 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci if (ret == QLCNIC_RCODE_NOT_SUPPORTED) 47762306a36Sopenharmony_ci return -EOPNOTSUPP; 47862306a36Sopenharmony_ci else if (ret) 47962306a36Sopenharmony_ci return -EIO; 48062306a36Sopenharmony_ci return ret; 48162306a36Sopenharmony_ci} 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_cistatic int qlcnic_set_link_ksettings(struct net_device *dev, 48462306a36Sopenharmony_ci const struct ethtool_link_ksettings *ecmd) 48562306a36Sopenharmony_ci{ 48662306a36Sopenharmony_ci u32 ret = 0; 48762306a36Sopenharmony_ci struct qlcnic_adapter *adapter = netdev_priv(dev); 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci if (qlcnic_83xx_check(adapter)) 49062306a36Sopenharmony_ci qlcnic_83xx_get_port_type(adapter); 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci if (adapter->ahw->port_type != QLCNIC_GBE) 49362306a36Sopenharmony_ci return -EOPNOTSUPP; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci if (qlcnic_83xx_check(adapter)) 49662306a36Sopenharmony_ci ret = qlcnic_83xx_set_link_ksettings(adapter, ecmd); 49762306a36Sopenharmony_ci else 49862306a36Sopenharmony_ci ret = qlcnic_set_port_config(adapter, ecmd); 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci if (!ret) 50162306a36Sopenharmony_ci return ret; 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci adapter->ahw->link_speed = ecmd->base.speed; 50462306a36Sopenharmony_ci adapter->ahw->link_duplex = ecmd->base.duplex; 50562306a36Sopenharmony_ci adapter->ahw->link_autoneg = ecmd->base.autoneg; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci if (!netif_running(dev)) 50862306a36Sopenharmony_ci return 0; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci dev->netdev_ops->ndo_stop(dev); 51162306a36Sopenharmony_ci return dev->netdev_ops->ndo_open(dev); 51262306a36Sopenharmony_ci} 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_cistatic int qlcnic_82xx_get_registers(struct qlcnic_adapter *adapter, 51562306a36Sopenharmony_ci u32 *regs_buff) 51662306a36Sopenharmony_ci{ 51762306a36Sopenharmony_ci int i, j = 0, err = 0; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci for (i = QLCNIC_DEV_INFO_SIZE + 1; diag_registers[j] != -1; j++, i++) 52062306a36Sopenharmony_ci regs_buff[i] = QLC_SHARED_REG_RD32(adapter, diag_registers[j]); 52162306a36Sopenharmony_ci j = 0; 52262306a36Sopenharmony_ci while (ext_diag_registers[j] != -1) 52362306a36Sopenharmony_ci regs_buff[i++] = QLCRD32(adapter, ext_diag_registers[j++], 52462306a36Sopenharmony_ci &err); 52562306a36Sopenharmony_ci return i; 52662306a36Sopenharmony_ci} 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_cistatic void 52962306a36Sopenharmony_ciqlcnic_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *p) 53062306a36Sopenharmony_ci{ 53162306a36Sopenharmony_ci struct qlcnic_adapter *adapter = netdev_priv(dev); 53262306a36Sopenharmony_ci struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx; 53362306a36Sopenharmony_ci struct qlcnic_host_sds_ring *sds_ring; 53462306a36Sopenharmony_ci struct qlcnic_host_rds_ring *rds_rings; 53562306a36Sopenharmony_ci struct qlcnic_host_tx_ring *tx_ring; 53662306a36Sopenharmony_ci u32 *regs_buff = p; 53762306a36Sopenharmony_ci int ring, i = 0; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci memset(p, 0, qlcnic_get_regs_len(dev)); 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci regs->version = (QLCNIC_ETHTOOL_REGS_VER << 24) | 54262306a36Sopenharmony_ci (adapter->ahw->revision_id << 16) | (adapter->pdev)->device; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci regs_buff[0] = (0xcafe0000 | (QLCNIC_DEV_INFO_SIZE & 0xffff)); 54562306a36Sopenharmony_ci regs_buff[1] = QLCNIC_MGMT_API_VERSION; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci if (adapter->ahw->capabilities & QLC_83XX_ESWITCH_CAPABILITY) 54862306a36Sopenharmony_ci regs_buff[2] = adapter->ahw->max_vnic_func; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci if (qlcnic_82xx_check(adapter)) 55162306a36Sopenharmony_ci i = qlcnic_82xx_get_registers(adapter, regs_buff); 55262306a36Sopenharmony_ci else 55362306a36Sopenharmony_ci i = qlcnic_83xx_get_registers(adapter, regs_buff); 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci if (!test_bit(__QLCNIC_DEV_UP, &adapter->state)) 55662306a36Sopenharmony_ci return; 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci /* Marker btw regs and TX ring count */ 55962306a36Sopenharmony_ci regs_buff[i++] = 0xFFEFCDAB; 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci regs_buff[i++] = adapter->drv_tx_rings; /* No. of TX ring */ 56262306a36Sopenharmony_ci for (ring = 0; ring < adapter->drv_tx_rings; ring++) { 56362306a36Sopenharmony_ci tx_ring = &adapter->tx_ring[ring]; 56462306a36Sopenharmony_ci regs_buff[i++] = le32_to_cpu(*(tx_ring->hw_consumer)); 56562306a36Sopenharmony_ci regs_buff[i++] = tx_ring->sw_consumer; 56662306a36Sopenharmony_ci regs_buff[i++] = readl(tx_ring->crb_cmd_producer); 56762306a36Sopenharmony_ci regs_buff[i++] = tx_ring->producer; 56862306a36Sopenharmony_ci if (tx_ring->crb_intr_mask) 56962306a36Sopenharmony_ci regs_buff[i++] = readl(tx_ring->crb_intr_mask); 57062306a36Sopenharmony_ci else 57162306a36Sopenharmony_ci regs_buff[i++] = QLCNIC_TX_INTR_NOT_CONFIGURED; 57262306a36Sopenharmony_ci } 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci regs_buff[i++] = adapter->max_rds_rings; /* No. of RX ring */ 57562306a36Sopenharmony_ci for (ring = 0; ring < adapter->max_rds_rings; ring++) { 57662306a36Sopenharmony_ci rds_rings = &recv_ctx->rds_rings[ring]; 57762306a36Sopenharmony_ci regs_buff[i++] = readl(rds_rings->crb_rcv_producer); 57862306a36Sopenharmony_ci regs_buff[i++] = rds_rings->producer; 57962306a36Sopenharmony_ci } 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci regs_buff[i++] = adapter->drv_sds_rings; /* No. of SDS ring */ 58262306a36Sopenharmony_ci for (ring = 0; ring < adapter->drv_sds_rings; ring++) { 58362306a36Sopenharmony_ci sds_ring = &(recv_ctx->sds_rings[ring]); 58462306a36Sopenharmony_ci regs_buff[i++] = readl(sds_ring->crb_sts_consumer); 58562306a36Sopenharmony_ci regs_buff[i++] = sds_ring->consumer; 58662306a36Sopenharmony_ci regs_buff[i++] = readl(sds_ring->crb_intr_mask); 58762306a36Sopenharmony_ci } 58862306a36Sopenharmony_ci} 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_cistatic u32 qlcnic_test_link(struct net_device *dev) 59162306a36Sopenharmony_ci{ 59262306a36Sopenharmony_ci struct qlcnic_adapter *adapter = netdev_priv(dev); 59362306a36Sopenharmony_ci int err = 0; 59462306a36Sopenharmony_ci u32 val; 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci if (qlcnic_83xx_check(adapter)) { 59762306a36Sopenharmony_ci val = qlcnic_83xx_test_link(adapter); 59862306a36Sopenharmony_ci return (val & 1) ? 0 : 1; 59962306a36Sopenharmony_ci } 60062306a36Sopenharmony_ci val = QLCRD32(adapter, CRB_XG_STATE_P3P, &err); 60162306a36Sopenharmony_ci if (err == -EIO) 60262306a36Sopenharmony_ci return err; 60362306a36Sopenharmony_ci val = XG_LINK_STATE_P3P(adapter->ahw->pci_func, val); 60462306a36Sopenharmony_ci return (val == XG_LINK_UP_P3P) ? 0 : 1; 60562306a36Sopenharmony_ci} 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_cistatic int 60862306a36Sopenharmony_ciqlcnic_get_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom, 60962306a36Sopenharmony_ci u8 *bytes) 61062306a36Sopenharmony_ci{ 61162306a36Sopenharmony_ci struct qlcnic_adapter *adapter = netdev_priv(dev); 61262306a36Sopenharmony_ci int offset; 61362306a36Sopenharmony_ci int ret = -1; 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci if (qlcnic_83xx_check(adapter)) 61662306a36Sopenharmony_ci return 0; 61762306a36Sopenharmony_ci if (eeprom->len == 0) 61862306a36Sopenharmony_ci return -EINVAL; 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci eeprom->magic = (adapter->pdev)->vendor | 62162306a36Sopenharmony_ci ((adapter->pdev)->device << 16); 62262306a36Sopenharmony_ci offset = eeprom->offset; 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci if (qlcnic_82xx_check(adapter)) 62562306a36Sopenharmony_ci ret = qlcnic_rom_fast_read_words(adapter, offset, bytes, 62662306a36Sopenharmony_ci eeprom->len); 62762306a36Sopenharmony_ci if (ret < 0) 62862306a36Sopenharmony_ci return ret; 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci return 0; 63162306a36Sopenharmony_ci} 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_cistatic void 63462306a36Sopenharmony_ciqlcnic_get_ringparam(struct net_device *dev, 63562306a36Sopenharmony_ci struct ethtool_ringparam *ring, 63662306a36Sopenharmony_ci struct kernel_ethtool_ringparam *kernel_ring, 63762306a36Sopenharmony_ci struct netlink_ext_ack *extack) 63862306a36Sopenharmony_ci{ 63962306a36Sopenharmony_ci struct qlcnic_adapter *adapter = netdev_priv(dev); 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci ring->rx_pending = adapter->num_rxd; 64262306a36Sopenharmony_ci ring->rx_jumbo_pending = adapter->num_jumbo_rxd; 64362306a36Sopenharmony_ci ring->tx_pending = adapter->num_txd; 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci ring->rx_max_pending = adapter->max_rxd; 64662306a36Sopenharmony_ci ring->rx_jumbo_max_pending = adapter->max_jumbo_rxd; 64762306a36Sopenharmony_ci ring->tx_max_pending = MAX_CMD_DESCRIPTORS; 64862306a36Sopenharmony_ci} 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_cistatic u32 65162306a36Sopenharmony_ciqlcnic_validate_ringparam(u32 val, u32 min, u32 max, char *r_name) 65262306a36Sopenharmony_ci{ 65362306a36Sopenharmony_ci u32 num_desc; 65462306a36Sopenharmony_ci num_desc = max(val, min); 65562306a36Sopenharmony_ci num_desc = min(num_desc, max); 65662306a36Sopenharmony_ci num_desc = roundup_pow_of_two(num_desc); 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci if (val != num_desc) { 65962306a36Sopenharmony_ci printk(KERN_INFO "%s: setting %s ring size %d instead of %d\n", 66062306a36Sopenharmony_ci qlcnic_driver_name, r_name, num_desc, val); 66162306a36Sopenharmony_ci } 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci return num_desc; 66462306a36Sopenharmony_ci} 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_cistatic int 66762306a36Sopenharmony_ciqlcnic_set_ringparam(struct net_device *dev, 66862306a36Sopenharmony_ci struct ethtool_ringparam *ring, 66962306a36Sopenharmony_ci struct kernel_ethtool_ringparam *kernel_ring, 67062306a36Sopenharmony_ci struct netlink_ext_ack *extack) 67162306a36Sopenharmony_ci{ 67262306a36Sopenharmony_ci struct qlcnic_adapter *adapter = netdev_priv(dev); 67362306a36Sopenharmony_ci u16 num_rxd, num_jumbo_rxd, num_txd; 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci if (ring->rx_mini_pending) 67662306a36Sopenharmony_ci return -EOPNOTSUPP; 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci num_rxd = qlcnic_validate_ringparam(ring->rx_pending, 67962306a36Sopenharmony_ci MIN_RCV_DESCRIPTORS, adapter->max_rxd, "rx"); 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci num_jumbo_rxd = qlcnic_validate_ringparam(ring->rx_jumbo_pending, 68262306a36Sopenharmony_ci MIN_JUMBO_DESCRIPTORS, adapter->max_jumbo_rxd, 68362306a36Sopenharmony_ci "rx jumbo"); 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci num_txd = qlcnic_validate_ringparam(ring->tx_pending, 68662306a36Sopenharmony_ci MIN_CMD_DESCRIPTORS, MAX_CMD_DESCRIPTORS, "tx"); 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci if (num_rxd == adapter->num_rxd && num_txd == adapter->num_txd && 68962306a36Sopenharmony_ci num_jumbo_rxd == adapter->num_jumbo_rxd) 69062306a36Sopenharmony_ci return 0; 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci adapter->num_rxd = num_rxd; 69362306a36Sopenharmony_ci adapter->num_jumbo_rxd = num_jumbo_rxd; 69462306a36Sopenharmony_ci adapter->num_txd = num_txd; 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci return qlcnic_reset_context(adapter); 69762306a36Sopenharmony_ci} 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_cistatic int qlcnic_validate_ring_count(struct qlcnic_adapter *adapter, 70062306a36Sopenharmony_ci u8 rx_ring, u8 tx_ring) 70162306a36Sopenharmony_ci{ 70262306a36Sopenharmony_ci if (rx_ring == 0 || tx_ring == 0) 70362306a36Sopenharmony_ci return -EINVAL; 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci if (rx_ring != 0) { 70662306a36Sopenharmony_ci if (rx_ring > adapter->max_sds_rings) { 70762306a36Sopenharmony_ci netdev_err(adapter->netdev, 70862306a36Sopenharmony_ci "Invalid ring count, SDS ring count %d should not be greater than max %d driver sds rings.\n", 70962306a36Sopenharmony_ci rx_ring, adapter->max_sds_rings); 71062306a36Sopenharmony_ci return -EINVAL; 71162306a36Sopenharmony_ci } 71262306a36Sopenharmony_ci } 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci if (tx_ring != 0) { 71562306a36Sopenharmony_ci if (tx_ring > adapter->max_tx_rings) { 71662306a36Sopenharmony_ci netdev_err(adapter->netdev, 71762306a36Sopenharmony_ci "Invalid ring count, Tx ring count %d should not be greater than max %d driver Tx rings.\n", 71862306a36Sopenharmony_ci tx_ring, adapter->max_tx_rings); 71962306a36Sopenharmony_ci return -EINVAL; 72062306a36Sopenharmony_ci } 72162306a36Sopenharmony_ci } 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci return 0; 72462306a36Sopenharmony_ci} 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_cistatic void qlcnic_get_channels(struct net_device *dev, 72762306a36Sopenharmony_ci struct ethtool_channels *channel) 72862306a36Sopenharmony_ci{ 72962306a36Sopenharmony_ci struct qlcnic_adapter *adapter = netdev_priv(dev); 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci channel->max_rx = adapter->max_sds_rings; 73262306a36Sopenharmony_ci channel->max_tx = adapter->max_tx_rings; 73362306a36Sopenharmony_ci channel->rx_count = adapter->drv_sds_rings; 73462306a36Sopenharmony_ci channel->tx_count = adapter->drv_tx_rings; 73562306a36Sopenharmony_ci} 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_cistatic int qlcnic_set_channels(struct net_device *dev, 73862306a36Sopenharmony_ci struct ethtool_channels *channel) 73962306a36Sopenharmony_ci{ 74062306a36Sopenharmony_ci struct qlcnic_adapter *adapter = netdev_priv(dev); 74162306a36Sopenharmony_ci int err; 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci if (!(adapter->flags & QLCNIC_MSIX_ENABLED)) { 74462306a36Sopenharmony_ci netdev_err(dev, "No RSS/TSS support in non MSI-X mode\n"); 74562306a36Sopenharmony_ci return -EINVAL; 74662306a36Sopenharmony_ci } 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci if (channel->other_count || channel->combined_count) 74962306a36Sopenharmony_ci return -EINVAL; 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci err = qlcnic_validate_ring_count(adapter, channel->rx_count, 75262306a36Sopenharmony_ci channel->tx_count); 75362306a36Sopenharmony_ci if (err) 75462306a36Sopenharmony_ci return err; 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci if (adapter->drv_sds_rings != channel->rx_count) { 75762306a36Sopenharmony_ci err = qlcnic_validate_rings(adapter, channel->rx_count, 75862306a36Sopenharmony_ci QLCNIC_RX_QUEUE); 75962306a36Sopenharmony_ci if (err) { 76062306a36Sopenharmony_ci netdev_err(dev, "Unable to configure %u SDS rings\n", 76162306a36Sopenharmony_ci channel->rx_count); 76262306a36Sopenharmony_ci return err; 76362306a36Sopenharmony_ci } 76462306a36Sopenharmony_ci adapter->drv_rss_rings = channel->rx_count; 76562306a36Sopenharmony_ci } 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci if (adapter->drv_tx_rings != channel->tx_count) { 76862306a36Sopenharmony_ci err = qlcnic_validate_rings(adapter, channel->tx_count, 76962306a36Sopenharmony_ci QLCNIC_TX_QUEUE); 77062306a36Sopenharmony_ci if (err) { 77162306a36Sopenharmony_ci netdev_err(dev, "Unable to configure %u Tx rings\n", 77262306a36Sopenharmony_ci channel->tx_count); 77362306a36Sopenharmony_ci return err; 77462306a36Sopenharmony_ci } 77562306a36Sopenharmony_ci adapter->drv_tss_rings = channel->tx_count; 77662306a36Sopenharmony_ci } 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci adapter->flags |= QLCNIC_TSS_RSS; 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci err = qlcnic_setup_rings(adapter); 78162306a36Sopenharmony_ci netdev_info(dev, "Allocated %d SDS rings and %d Tx rings\n", 78262306a36Sopenharmony_ci adapter->drv_sds_rings, adapter->drv_tx_rings); 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci return err; 78562306a36Sopenharmony_ci} 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_cistatic void 78862306a36Sopenharmony_ciqlcnic_get_pauseparam(struct net_device *netdev, 78962306a36Sopenharmony_ci struct ethtool_pauseparam *pause) 79062306a36Sopenharmony_ci{ 79162306a36Sopenharmony_ci struct qlcnic_adapter *adapter = netdev_priv(netdev); 79262306a36Sopenharmony_ci int port = adapter->ahw->physical_port; 79362306a36Sopenharmony_ci int err = 0; 79462306a36Sopenharmony_ci __u32 val; 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci if (qlcnic_83xx_check(adapter)) { 79762306a36Sopenharmony_ci qlcnic_83xx_get_pauseparam(adapter, pause); 79862306a36Sopenharmony_ci return; 79962306a36Sopenharmony_ci } 80062306a36Sopenharmony_ci if (adapter->ahw->port_type == QLCNIC_GBE) { 80162306a36Sopenharmony_ci if ((port < 0) || (port > QLCNIC_NIU_MAX_GBE_PORTS)) 80262306a36Sopenharmony_ci return; 80362306a36Sopenharmony_ci /* get flow control settings */ 80462306a36Sopenharmony_ci val = QLCRD32(adapter, QLCNIC_NIU_GB_MAC_CONFIG_0(port), &err); 80562306a36Sopenharmony_ci if (err == -EIO) 80662306a36Sopenharmony_ci return; 80762306a36Sopenharmony_ci pause->rx_pause = qlcnic_gb_get_rx_flowctl(val); 80862306a36Sopenharmony_ci val = QLCRD32(adapter, QLCNIC_NIU_GB_PAUSE_CTL, &err); 80962306a36Sopenharmony_ci if (err == -EIO) 81062306a36Sopenharmony_ci return; 81162306a36Sopenharmony_ci switch (port) { 81262306a36Sopenharmony_ci case 0: 81362306a36Sopenharmony_ci pause->tx_pause = !(qlcnic_gb_get_gb0_mask(val)); 81462306a36Sopenharmony_ci break; 81562306a36Sopenharmony_ci case 1: 81662306a36Sopenharmony_ci pause->tx_pause = !(qlcnic_gb_get_gb1_mask(val)); 81762306a36Sopenharmony_ci break; 81862306a36Sopenharmony_ci case 2: 81962306a36Sopenharmony_ci pause->tx_pause = !(qlcnic_gb_get_gb2_mask(val)); 82062306a36Sopenharmony_ci break; 82162306a36Sopenharmony_ci case 3: 82262306a36Sopenharmony_ci default: 82362306a36Sopenharmony_ci pause->tx_pause = !(qlcnic_gb_get_gb3_mask(val)); 82462306a36Sopenharmony_ci break; 82562306a36Sopenharmony_ci } 82662306a36Sopenharmony_ci } else if (adapter->ahw->port_type == QLCNIC_XGBE) { 82762306a36Sopenharmony_ci if ((port < 0) || (port > QLCNIC_NIU_MAX_XG_PORTS)) 82862306a36Sopenharmony_ci return; 82962306a36Sopenharmony_ci pause->rx_pause = 1; 83062306a36Sopenharmony_ci val = QLCRD32(adapter, QLCNIC_NIU_XG_PAUSE_CTL, &err); 83162306a36Sopenharmony_ci if (err == -EIO) 83262306a36Sopenharmony_ci return; 83362306a36Sopenharmony_ci if (port == 0) 83462306a36Sopenharmony_ci pause->tx_pause = !(qlcnic_xg_get_xg0_mask(val)); 83562306a36Sopenharmony_ci else 83662306a36Sopenharmony_ci pause->tx_pause = !(qlcnic_xg_get_xg1_mask(val)); 83762306a36Sopenharmony_ci } else { 83862306a36Sopenharmony_ci dev_err(&netdev->dev, "Unknown board type: %x\n", 83962306a36Sopenharmony_ci adapter->ahw->port_type); 84062306a36Sopenharmony_ci } 84162306a36Sopenharmony_ci} 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_cistatic int 84462306a36Sopenharmony_ciqlcnic_set_pauseparam(struct net_device *netdev, 84562306a36Sopenharmony_ci struct ethtool_pauseparam *pause) 84662306a36Sopenharmony_ci{ 84762306a36Sopenharmony_ci struct qlcnic_adapter *adapter = netdev_priv(netdev); 84862306a36Sopenharmony_ci int port = adapter->ahw->physical_port; 84962306a36Sopenharmony_ci int err = 0; 85062306a36Sopenharmony_ci __u32 val; 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci if (qlcnic_83xx_check(adapter)) 85362306a36Sopenharmony_ci return qlcnic_83xx_set_pauseparam(adapter, pause); 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci /* read mode */ 85662306a36Sopenharmony_ci if (adapter->ahw->port_type == QLCNIC_GBE) { 85762306a36Sopenharmony_ci if ((port < 0) || (port > QLCNIC_NIU_MAX_GBE_PORTS)) 85862306a36Sopenharmony_ci return -EIO; 85962306a36Sopenharmony_ci /* set flow control */ 86062306a36Sopenharmony_ci val = QLCRD32(adapter, QLCNIC_NIU_GB_MAC_CONFIG_0(port), &err); 86162306a36Sopenharmony_ci if (err == -EIO) 86262306a36Sopenharmony_ci return err; 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci if (pause->rx_pause) 86562306a36Sopenharmony_ci qlcnic_gb_rx_flowctl(val); 86662306a36Sopenharmony_ci else 86762306a36Sopenharmony_ci qlcnic_gb_unset_rx_flowctl(val); 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci QLCWR32(adapter, QLCNIC_NIU_GB_MAC_CONFIG_0(port), 87062306a36Sopenharmony_ci val); 87162306a36Sopenharmony_ci QLCWR32(adapter, QLCNIC_NIU_GB_MAC_CONFIG_0(port), val); 87262306a36Sopenharmony_ci /* set autoneg */ 87362306a36Sopenharmony_ci val = QLCRD32(adapter, QLCNIC_NIU_GB_PAUSE_CTL, &err); 87462306a36Sopenharmony_ci if (err == -EIO) 87562306a36Sopenharmony_ci return err; 87662306a36Sopenharmony_ci switch (port) { 87762306a36Sopenharmony_ci case 0: 87862306a36Sopenharmony_ci if (pause->tx_pause) 87962306a36Sopenharmony_ci qlcnic_gb_unset_gb0_mask(val); 88062306a36Sopenharmony_ci else 88162306a36Sopenharmony_ci qlcnic_gb_set_gb0_mask(val); 88262306a36Sopenharmony_ci break; 88362306a36Sopenharmony_ci case 1: 88462306a36Sopenharmony_ci if (pause->tx_pause) 88562306a36Sopenharmony_ci qlcnic_gb_unset_gb1_mask(val); 88662306a36Sopenharmony_ci else 88762306a36Sopenharmony_ci qlcnic_gb_set_gb1_mask(val); 88862306a36Sopenharmony_ci break; 88962306a36Sopenharmony_ci case 2: 89062306a36Sopenharmony_ci if (pause->tx_pause) 89162306a36Sopenharmony_ci qlcnic_gb_unset_gb2_mask(val); 89262306a36Sopenharmony_ci else 89362306a36Sopenharmony_ci qlcnic_gb_set_gb2_mask(val); 89462306a36Sopenharmony_ci break; 89562306a36Sopenharmony_ci case 3: 89662306a36Sopenharmony_ci default: 89762306a36Sopenharmony_ci if (pause->tx_pause) 89862306a36Sopenharmony_ci qlcnic_gb_unset_gb3_mask(val); 89962306a36Sopenharmony_ci else 90062306a36Sopenharmony_ci qlcnic_gb_set_gb3_mask(val); 90162306a36Sopenharmony_ci break; 90262306a36Sopenharmony_ci } 90362306a36Sopenharmony_ci QLCWR32(adapter, QLCNIC_NIU_GB_PAUSE_CTL, val); 90462306a36Sopenharmony_ci } else if (adapter->ahw->port_type == QLCNIC_XGBE) { 90562306a36Sopenharmony_ci if (!pause->rx_pause || pause->autoneg) 90662306a36Sopenharmony_ci return -EOPNOTSUPP; 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci if ((port < 0) || (port > QLCNIC_NIU_MAX_XG_PORTS)) 90962306a36Sopenharmony_ci return -EIO; 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_ci val = QLCRD32(adapter, QLCNIC_NIU_XG_PAUSE_CTL, &err); 91262306a36Sopenharmony_ci if (err == -EIO) 91362306a36Sopenharmony_ci return err; 91462306a36Sopenharmony_ci if (port == 0) { 91562306a36Sopenharmony_ci if (pause->tx_pause) 91662306a36Sopenharmony_ci qlcnic_xg_unset_xg0_mask(val); 91762306a36Sopenharmony_ci else 91862306a36Sopenharmony_ci qlcnic_xg_set_xg0_mask(val); 91962306a36Sopenharmony_ci } else { 92062306a36Sopenharmony_ci if (pause->tx_pause) 92162306a36Sopenharmony_ci qlcnic_xg_unset_xg1_mask(val); 92262306a36Sopenharmony_ci else 92362306a36Sopenharmony_ci qlcnic_xg_set_xg1_mask(val); 92462306a36Sopenharmony_ci } 92562306a36Sopenharmony_ci QLCWR32(adapter, QLCNIC_NIU_XG_PAUSE_CTL, val); 92662306a36Sopenharmony_ci } else { 92762306a36Sopenharmony_ci dev_err(&netdev->dev, "Unknown board type: %x\n", 92862306a36Sopenharmony_ci adapter->ahw->port_type); 92962306a36Sopenharmony_ci } 93062306a36Sopenharmony_ci return 0; 93162306a36Sopenharmony_ci} 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_cistatic int qlcnic_reg_test(struct net_device *dev) 93462306a36Sopenharmony_ci{ 93562306a36Sopenharmony_ci struct qlcnic_adapter *adapter = netdev_priv(dev); 93662306a36Sopenharmony_ci u32 data_read; 93762306a36Sopenharmony_ci int err = 0; 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_ci if (qlcnic_83xx_check(adapter)) 94062306a36Sopenharmony_ci return qlcnic_83xx_reg_test(adapter); 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_ci data_read = QLCRD32(adapter, QLCNIC_PCIX_PH_REG(0), &err); 94362306a36Sopenharmony_ci if (err == -EIO) 94462306a36Sopenharmony_ci return err; 94562306a36Sopenharmony_ci if ((data_read & 0xffff) != adapter->pdev->vendor) 94662306a36Sopenharmony_ci return 1; 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci return 0; 94962306a36Sopenharmony_ci} 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_cistatic int qlcnic_eeprom_test(struct net_device *dev) 95262306a36Sopenharmony_ci{ 95362306a36Sopenharmony_ci struct qlcnic_adapter *adapter = netdev_priv(dev); 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci if (qlcnic_82xx_check(adapter)) 95662306a36Sopenharmony_ci return 0; 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci return qlcnic_83xx_flash_test(adapter); 95962306a36Sopenharmony_ci} 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_cistatic int qlcnic_get_sset_count(struct net_device *dev, int sset) 96262306a36Sopenharmony_ci{ 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci struct qlcnic_adapter *adapter = netdev_priv(dev); 96562306a36Sopenharmony_ci switch (sset) { 96662306a36Sopenharmony_ci case ETH_SS_TEST: 96762306a36Sopenharmony_ci return QLCNIC_TEST_LEN; 96862306a36Sopenharmony_ci case ETH_SS_STATS: 96962306a36Sopenharmony_ci return qlcnic_dev_statistics_len(adapter); 97062306a36Sopenharmony_ci default: 97162306a36Sopenharmony_ci return -EOPNOTSUPP; 97262306a36Sopenharmony_ci } 97362306a36Sopenharmony_ci} 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_cistatic int qlcnic_irq_test(struct net_device *netdev) 97662306a36Sopenharmony_ci{ 97762306a36Sopenharmony_ci struct qlcnic_adapter *adapter = netdev_priv(netdev); 97862306a36Sopenharmony_ci struct qlcnic_hardware_context *ahw = adapter->ahw; 97962306a36Sopenharmony_ci struct qlcnic_cmd_args cmd; 98062306a36Sopenharmony_ci int ret, drv_sds_rings = adapter->drv_sds_rings; 98162306a36Sopenharmony_ci int drv_tx_rings = adapter->drv_tx_rings; 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_ci if (qlcnic_83xx_check(adapter)) 98462306a36Sopenharmony_ci return qlcnic_83xx_interrupt_test(netdev); 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci if (test_and_set_bit(__QLCNIC_RESETTING, &adapter->state)) 98762306a36Sopenharmony_ci return -EIO; 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_ci ret = qlcnic_diag_alloc_res(netdev, QLCNIC_INTERRUPT_TEST); 99062306a36Sopenharmony_ci if (ret) 99162306a36Sopenharmony_ci goto clear_diag_irq; 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_ci ahw->diag_cnt = 0; 99462306a36Sopenharmony_ci ret = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_INTRPT_TEST); 99562306a36Sopenharmony_ci if (ret) 99662306a36Sopenharmony_ci goto free_diag_res; 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci cmd.req.arg[1] = ahw->pci_func; 99962306a36Sopenharmony_ci ret = qlcnic_issue_cmd(adapter, &cmd); 100062306a36Sopenharmony_ci if (ret) 100162306a36Sopenharmony_ci goto done; 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci usleep_range(1000, 12000); 100462306a36Sopenharmony_ci ret = !ahw->diag_cnt; 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_cidone: 100762306a36Sopenharmony_ci qlcnic_free_mbx_args(&cmd); 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_cifree_diag_res: 101062306a36Sopenharmony_ci qlcnic_diag_free_res(netdev, drv_sds_rings); 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_ciclear_diag_irq: 101362306a36Sopenharmony_ci adapter->drv_sds_rings = drv_sds_rings; 101462306a36Sopenharmony_ci adapter->drv_tx_rings = drv_tx_rings; 101562306a36Sopenharmony_ci clear_bit(__QLCNIC_RESETTING, &adapter->state); 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_ci return ret; 101862306a36Sopenharmony_ci} 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_ci#define QLCNIC_ILB_PKT_SIZE 64 102162306a36Sopenharmony_ci#define QLCNIC_NUM_ILB_PKT 16 102262306a36Sopenharmony_ci#define QLCNIC_ILB_MAX_RCV_LOOP 10 102362306a36Sopenharmony_ci#define QLCNIC_LB_PKT_POLL_DELAY_MSEC 1 102462306a36Sopenharmony_ci#define QLCNIC_LB_PKT_POLL_COUNT 20 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_cistatic void qlcnic_create_loopback_buff(unsigned char *data, u8 mac[]) 102762306a36Sopenharmony_ci{ 102862306a36Sopenharmony_ci static const unsigned char random_data[] = {0xa8, 0x06, 0x45, 0x00}; 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci memset(data, 0x4e, QLCNIC_ILB_PKT_SIZE); 103162306a36Sopenharmony_ci 103262306a36Sopenharmony_ci memcpy(data, mac, ETH_ALEN); 103362306a36Sopenharmony_ci memcpy(data + ETH_ALEN, mac, ETH_ALEN); 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_ci memcpy(data + 2 * ETH_ALEN, random_data, sizeof(random_data)); 103662306a36Sopenharmony_ci} 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ciint qlcnic_check_loopback_buff(unsigned char *data, u8 mac[]) 103962306a36Sopenharmony_ci{ 104062306a36Sopenharmony_ci unsigned char buff[QLCNIC_ILB_PKT_SIZE]; 104162306a36Sopenharmony_ci qlcnic_create_loopback_buff(buff, mac); 104262306a36Sopenharmony_ci return memcmp(data, buff, QLCNIC_ILB_PKT_SIZE); 104362306a36Sopenharmony_ci} 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_ciint qlcnic_do_lb_test(struct qlcnic_adapter *adapter, u8 mode) 104662306a36Sopenharmony_ci{ 104762306a36Sopenharmony_ci struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx; 104862306a36Sopenharmony_ci struct qlcnic_host_sds_ring *sds_ring = &recv_ctx->sds_rings[0]; 104962306a36Sopenharmony_ci struct sk_buff *skb; 105062306a36Sopenharmony_ci int i, loop, cnt = 0; 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ci for (i = 0; i < QLCNIC_NUM_ILB_PKT; i++) { 105362306a36Sopenharmony_ci skb = netdev_alloc_skb(adapter->netdev, QLCNIC_ILB_PKT_SIZE); 105462306a36Sopenharmony_ci if (!skb) 105562306a36Sopenharmony_ci goto error; 105662306a36Sopenharmony_ci qlcnic_create_loopback_buff(skb->data, adapter->mac_addr); 105762306a36Sopenharmony_ci skb_put(skb, QLCNIC_ILB_PKT_SIZE); 105862306a36Sopenharmony_ci adapter->ahw->diag_cnt = 0; 105962306a36Sopenharmony_ci qlcnic_xmit_frame(skb, adapter->netdev); 106062306a36Sopenharmony_ci loop = 0; 106162306a36Sopenharmony_ci 106262306a36Sopenharmony_ci do { 106362306a36Sopenharmony_ci msleep(QLCNIC_LB_PKT_POLL_DELAY_MSEC); 106462306a36Sopenharmony_ci qlcnic_process_rcv_ring_diag(sds_ring); 106562306a36Sopenharmony_ci if (loop++ > QLCNIC_LB_PKT_POLL_COUNT) 106662306a36Sopenharmony_ci break; 106762306a36Sopenharmony_ci } while (!adapter->ahw->diag_cnt); 106862306a36Sopenharmony_ci 106962306a36Sopenharmony_ci dev_kfree_skb_any(skb); 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci if (!adapter->ahw->diag_cnt) 107262306a36Sopenharmony_ci dev_warn(&adapter->pdev->dev, 107362306a36Sopenharmony_ci "LB Test: packet #%d was not received\n", 107462306a36Sopenharmony_ci i + 1); 107562306a36Sopenharmony_ci else 107662306a36Sopenharmony_ci cnt++; 107762306a36Sopenharmony_ci } 107862306a36Sopenharmony_ci if (cnt != i) { 107962306a36Sopenharmony_cierror: 108062306a36Sopenharmony_ci dev_err(&adapter->pdev->dev, 108162306a36Sopenharmony_ci "LB Test: failed, TX[%d], RX[%d]\n", i, cnt); 108262306a36Sopenharmony_ci if (mode != QLCNIC_ILB_MODE) 108362306a36Sopenharmony_ci dev_warn(&adapter->pdev->dev, 108462306a36Sopenharmony_ci "WARNING: Please check loopback cable\n"); 108562306a36Sopenharmony_ci return -1; 108662306a36Sopenharmony_ci } 108762306a36Sopenharmony_ci return 0; 108862306a36Sopenharmony_ci} 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_cistatic int qlcnic_loopback_test(struct net_device *netdev, u8 mode) 109162306a36Sopenharmony_ci{ 109262306a36Sopenharmony_ci struct qlcnic_adapter *adapter = netdev_priv(netdev); 109362306a36Sopenharmony_ci int drv_tx_rings = adapter->drv_tx_rings; 109462306a36Sopenharmony_ci int drv_sds_rings = adapter->drv_sds_rings; 109562306a36Sopenharmony_ci struct qlcnic_host_sds_ring *sds_ring; 109662306a36Sopenharmony_ci struct qlcnic_hardware_context *ahw = adapter->ahw; 109762306a36Sopenharmony_ci int loop = 0; 109862306a36Sopenharmony_ci int ret; 109962306a36Sopenharmony_ci 110062306a36Sopenharmony_ci if (qlcnic_83xx_check(adapter)) 110162306a36Sopenharmony_ci return qlcnic_83xx_loopback_test(netdev, mode); 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_ci if (!(ahw->capabilities & QLCNIC_FW_CAPABILITY_MULTI_LOOPBACK)) { 110462306a36Sopenharmony_ci dev_info(&adapter->pdev->dev, 110562306a36Sopenharmony_ci "Firmware do not support loopback test\n"); 110662306a36Sopenharmony_ci return -EOPNOTSUPP; 110762306a36Sopenharmony_ci } 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_ci dev_warn(&adapter->pdev->dev, "%s loopback test in progress\n", 111062306a36Sopenharmony_ci mode == QLCNIC_ILB_MODE ? "internal" : "external"); 111162306a36Sopenharmony_ci if (ahw->op_mode == QLCNIC_NON_PRIV_FUNC) { 111262306a36Sopenharmony_ci dev_warn(&adapter->pdev->dev, 111362306a36Sopenharmony_ci "Loopback test not supported in nonprivileged mode\n"); 111462306a36Sopenharmony_ci return 0; 111562306a36Sopenharmony_ci } 111662306a36Sopenharmony_ci 111762306a36Sopenharmony_ci if (test_and_set_bit(__QLCNIC_RESETTING, &adapter->state)) 111862306a36Sopenharmony_ci return -EBUSY; 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_ci ret = qlcnic_diag_alloc_res(netdev, QLCNIC_LOOPBACK_TEST); 112162306a36Sopenharmony_ci if (ret) 112262306a36Sopenharmony_ci goto clear_it; 112362306a36Sopenharmony_ci 112462306a36Sopenharmony_ci sds_ring = &adapter->recv_ctx->sds_rings[0]; 112562306a36Sopenharmony_ci ret = qlcnic_set_lb_mode(adapter, mode); 112662306a36Sopenharmony_ci if (ret) 112762306a36Sopenharmony_ci goto free_res; 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_ci ahw->diag_cnt = 0; 113062306a36Sopenharmony_ci do { 113162306a36Sopenharmony_ci msleep(500); 113262306a36Sopenharmony_ci qlcnic_process_rcv_ring_diag(sds_ring); 113362306a36Sopenharmony_ci if (loop++ > QLCNIC_ILB_MAX_RCV_LOOP) { 113462306a36Sopenharmony_ci netdev_info(netdev, 113562306a36Sopenharmony_ci "Firmware didn't sent link up event to loopback request\n"); 113662306a36Sopenharmony_ci ret = -ETIMEDOUT; 113762306a36Sopenharmony_ci goto free_res; 113862306a36Sopenharmony_ci } else if (adapter->ahw->diag_cnt) { 113962306a36Sopenharmony_ci ret = adapter->ahw->diag_cnt; 114062306a36Sopenharmony_ci goto free_res; 114162306a36Sopenharmony_ci } 114262306a36Sopenharmony_ci } while (!QLCNIC_IS_LB_CONFIGURED(ahw->loopback_state)); 114362306a36Sopenharmony_ci 114462306a36Sopenharmony_ci ret = qlcnic_do_lb_test(adapter, mode); 114562306a36Sopenharmony_ci 114662306a36Sopenharmony_ci qlcnic_clear_lb_mode(adapter, mode); 114762306a36Sopenharmony_ci 114862306a36Sopenharmony_ci free_res: 114962306a36Sopenharmony_ci qlcnic_diag_free_res(netdev, drv_sds_rings); 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_ci clear_it: 115262306a36Sopenharmony_ci adapter->drv_sds_rings = drv_sds_rings; 115362306a36Sopenharmony_ci adapter->drv_tx_rings = drv_tx_rings; 115462306a36Sopenharmony_ci clear_bit(__QLCNIC_RESETTING, &adapter->state); 115562306a36Sopenharmony_ci return ret; 115662306a36Sopenharmony_ci} 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_cistatic void 115962306a36Sopenharmony_ciqlcnic_diag_test(struct net_device *dev, struct ethtool_test *eth_test, 116062306a36Sopenharmony_ci u64 *data) 116162306a36Sopenharmony_ci{ 116262306a36Sopenharmony_ci memset(data, 0, sizeof(u64) * QLCNIC_TEST_LEN); 116362306a36Sopenharmony_ci 116462306a36Sopenharmony_ci data[0] = qlcnic_reg_test(dev); 116562306a36Sopenharmony_ci if (data[0]) 116662306a36Sopenharmony_ci eth_test->flags |= ETH_TEST_FL_FAILED; 116762306a36Sopenharmony_ci 116862306a36Sopenharmony_ci data[1] = (u64) qlcnic_test_link(dev); 116962306a36Sopenharmony_ci if (data[1]) 117062306a36Sopenharmony_ci eth_test->flags |= ETH_TEST_FL_FAILED; 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ci if (eth_test->flags & ETH_TEST_FL_OFFLINE) { 117362306a36Sopenharmony_ci data[2] = qlcnic_irq_test(dev); 117462306a36Sopenharmony_ci if (data[2]) 117562306a36Sopenharmony_ci eth_test->flags |= ETH_TEST_FL_FAILED; 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_ci data[3] = qlcnic_loopback_test(dev, QLCNIC_ILB_MODE); 117862306a36Sopenharmony_ci if (data[3]) 117962306a36Sopenharmony_ci eth_test->flags |= ETH_TEST_FL_FAILED; 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci if (eth_test->flags & ETH_TEST_FL_EXTERNAL_LB) { 118262306a36Sopenharmony_ci data[4] = qlcnic_loopback_test(dev, QLCNIC_ELB_MODE); 118362306a36Sopenharmony_ci if (data[4]) 118462306a36Sopenharmony_ci eth_test->flags |= ETH_TEST_FL_FAILED; 118562306a36Sopenharmony_ci eth_test->flags |= ETH_TEST_FL_EXTERNAL_LB_DONE; 118662306a36Sopenharmony_ci } 118762306a36Sopenharmony_ci 118862306a36Sopenharmony_ci data[5] = qlcnic_eeprom_test(dev); 118962306a36Sopenharmony_ci if (data[5]) 119062306a36Sopenharmony_ci eth_test->flags |= ETH_TEST_FL_FAILED; 119162306a36Sopenharmony_ci } 119262306a36Sopenharmony_ci} 119362306a36Sopenharmony_ci 119462306a36Sopenharmony_cistatic void 119562306a36Sopenharmony_ciqlcnic_get_strings(struct net_device *dev, u32 stringset, u8 *data) 119662306a36Sopenharmony_ci{ 119762306a36Sopenharmony_ci struct qlcnic_adapter *adapter = netdev_priv(dev); 119862306a36Sopenharmony_ci int index, i, num_stats; 119962306a36Sopenharmony_ci 120062306a36Sopenharmony_ci switch (stringset) { 120162306a36Sopenharmony_ci case ETH_SS_TEST: 120262306a36Sopenharmony_ci memcpy(data, *qlcnic_gstrings_test, 120362306a36Sopenharmony_ci QLCNIC_TEST_LEN * ETH_GSTRING_LEN); 120462306a36Sopenharmony_ci break; 120562306a36Sopenharmony_ci case ETH_SS_STATS: 120662306a36Sopenharmony_ci num_stats = ARRAY_SIZE(qlcnic_tx_queue_stats_strings); 120762306a36Sopenharmony_ci for (i = 0; i < adapter->drv_tx_rings; i++) { 120862306a36Sopenharmony_ci for (index = 0; index < num_stats; index++) { 120962306a36Sopenharmony_ci sprintf(data, "tx_queue_%d %s", i, 121062306a36Sopenharmony_ci qlcnic_tx_queue_stats_strings[index]); 121162306a36Sopenharmony_ci data += ETH_GSTRING_LEN; 121262306a36Sopenharmony_ci } 121362306a36Sopenharmony_ci } 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_ci for (index = 0; index < QLCNIC_STATS_LEN; index++) { 121662306a36Sopenharmony_ci memcpy(data + index * ETH_GSTRING_LEN, 121762306a36Sopenharmony_ci qlcnic_gstrings_stats[index].stat_string, 121862306a36Sopenharmony_ci ETH_GSTRING_LEN); 121962306a36Sopenharmony_ci } 122062306a36Sopenharmony_ci 122162306a36Sopenharmony_ci if (qlcnic_83xx_check(adapter)) { 122262306a36Sopenharmony_ci num_stats = ARRAY_SIZE(qlcnic_83xx_tx_stats_strings); 122362306a36Sopenharmony_ci for (i = 0; i < num_stats; i++, index++) 122462306a36Sopenharmony_ci memcpy(data + index * ETH_GSTRING_LEN, 122562306a36Sopenharmony_ci qlcnic_83xx_tx_stats_strings[i], 122662306a36Sopenharmony_ci ETH_GSTRING_LEN); 122762306a36Sopenharmony_ci num_stats = ARRAY_SIZE(qlcnic_83xx_mac_stats_strings); 122862306a36Sopenharmony_ci for (i = 0; i < num_stats; i++, index++) 122962306a36Sopenharmony_ci memcpy(data + index * ETH_GSTRING_LEN, 123062306a36Sopenharmony_ci qlcnic_83xx_mac_stats_strings[i], 123162306a36Sopenharmony_ci ETH_GSTRING_LEN); 123262306a36Sopenharmony_ci num_stats = ARRAY_SIZE(qlcnic_83xx_rx_stats_strings); 123362306a36Sopenharmony_ci for (i = 0; i < num_stats; i++, index++) 123462306a36Sopenharmony_ci memcpy(data + index * ETH_GSTRING_LEN, 123562306a36Sopenharmony_ci qlcnic_83xx_rx_stats_strings[i], 123662306a36Sopenharmony_ci ETH_GSTRING_LEN); 123762306a36Sopenharmony_ci return; 123862306a36Sopenharmony_ci } else { 123962306a36Sopenharmony_ci num_stats = ARRAY_SIZE(qlcnic_83xx_mac_stats_strings); 124062306a36Sopenharmony_ci for (i = 0; i < num_stats; i++, index++) 124162306a36Sopenharmony_ci memcpy(data + index * ETH_GSTRING_LEN, 124262306a36Sopenharmony_ci qlcnic_83xx_mac_stats_strings[i], 124362306a36Sopenharmony_ci ETH_GSTRING_LEN); 124462306a36Sopenharmony_ci } 124562306a36Sopenharmony_ci if (!(adapter->flags & QLCNIC_ESWITCH_ENABLED)) 124662306a36Sopenharmony_ci return; 124762306a36Sopenharmony_ci num_stats = ARRAY_SIZE(qlcnic_device_gstrings_stats); 124862306a36Sopenharmony_ci for (i = 0; i < num_stats; index++, i++) { 124962306a36Sopenharmony_ci memcpy(data + index * ETH_GSTRING_LEN, 125062306a36Sopenharmony_ci qlcnic_device_gstrings_stats[i], 125162306a36Sopenharmony_ci ETH_GSTRING_LEN); 125262306a36Sopenharmony_ci } 125362306a36Sopenharmony_ci } 125462306a36Sopenharmony_ci} 125562306a36Sopenharmony_ci 125662306a36Sopenharmony_cistatic u64 *qlcnic_fill_stats(u64 *data, void *stats, int type) 125762306a36Sopenharmony_ci{ 125862306a36Sopenharmony_ci if (type == QLCNIC_MAC_STATS) { 125962306a36Sopenharmony_ci struct qlcnic_mac_statistics *mac_stats = 126062306a36Sopenharmony_ci (struct qlcnic_mac_statistics *)stats; 126162306a36Sopenharmony_ci *data++ = QLCNIC_FILL_STATS(mac_stats->mac_tx_frames); 126262306a36Sopenharmony_ci *data++ = QLCNIC_FILL_STATS(mac_stats->mac_tx_bytes); 126362306a36Sopenharmony_ci *data++ = QLCNIC_FILL_STATS(mac_stats->mac_tx_mcast_pkts); 126462306a36Sopenharmony_ci *data++ = QLCNIC_FILL_STATS(mac_stats->mac_tx_bcast_pkts); 126562306a36Sopenharmony_ci *data++ = QLCNIC_FILL_STATS(mac_stats->mac_tx_pause_cnt); 126662306a36Sopenharmony_ci *data++ = QLCNIC_FILL_STATS(mac_stats->mac_tx_ctrl_pkt); 126762306a36Sopenharmony_ci *data++ = QLCNIC_FILL_STATS(mac_stats->mac_tx_lt_64b_pkts); 126862306a36Sopenharmony_ci *data++ = QLCNIC_FILL_STATS(mac_stats->mac_tx_lt_127b_pkts); 126962306a36Sopenharmony_ci *data++ = QLCNIC_FILL_STATS(mac_stats->mac_tx_lt_255b_pkts); 127062306a36Sopenharmony_ci *data++ = QLCNIC_FILL_STATS(mac_stats->mac_tx_lt_511b_pkts); 127162306a36Sopenharmony_ci *data++ = QLCNIC_FILL_STATS(mac_stats->mac_tx_lt_1023b_pkts); 127262306a36Sopenharmony_ci *data++ = QLCNIC_FILL_STATS(mac_stats->mac_tx_lt_1518b_pkts); 127362306a36Sopenharmony_ci *data++ = QLCNIC_FILL_STATS(mac_stats->mac_tx_gt_1518b_pkts); 127462306a36Sopenharmony_ci *data++ = QLCNIC_FILL_STATS(mac_stats->mac_rx_frames); 127562306a36Sopenharmony_ci *data++ = QLCNIC_FILL_STATS(mac_stats->mac_rx_bytes); 127662306a36Sopenharmony_ci *data++ = QLCNIC_FILL_STATS(mac_stats->mac_rx_mcast_pkts); 127762306a36Sopenharmony_ci *data++ = QLCNIC_FILL_STATS(mac_stats->mac_rx_bcast_pkts); 127862306a36Sopenharmony_ci *data++ = QLCNIC_FILL_STATS(mac_stats->mac_rx_pause_cnt); 127962306a36Sopenharmony_ci *data++ = QLCNIC_FILL_STATS(mac_stats->mac_rx_ctrl_pkt); 128062306a36Sopenharmony_ci *data++ = QLCNIC_FILL_STATS(mac_stats->mac_rx_lt_64b_pkts); 128162306a36Sopenharmony_ci *data++ = QLCNIC_FILL_STATS(mac_stats->mac_rx_lt_127b_pkts); 128262306a36Sopenharmony_ci *data++ = QLCNIC_FILL_STATS(mac_stats->mac_rx_lt_255b_pkts); 128362306a36Sopenharmony_ci *data++ = QLCNIC_FILL_STATS(mac_stats->mac_rx_lt_511b_pkts); 128462306a36Sopenharmony_ci *data++ = QLCNIC_FILL_STATS(mac_stats->mac_rx_lt_1023b_pkts); 128562306a36Sopenharmony_ci *data++ = QLCNIC_FILL_STATS(mac_stats->mac_rx_lt_1518b_pkts); 128662306a36Sopenharmony_ci *data++ = QLCNIC_FILL_STATS(mac_stats->mac_rx_gt_1518b_pkts); 128762306a36Sopenharmony_ci *data++ = QLCNIC_FILL_STATS(mac_stats->mac_rx_length_error); 128862306a36Sopenharmony_ci *data++ = QLCNIC_FILL_STATS(mac_stats->mac_rx_length_small); 128962306a36Sopenharmony_ci *data++ = QLCNIC_FILL_STATS(mac_stats->mac_rx_length_large); 129062306a36Sopenharmony_ci *data++ = QLCNIC_FILL_STATS(mac_stats->mac_rx_jabber); 129162306a36Sopenharmony_ci *data++ = QLCNIC_FILL_STATS(mac_stats->mac_rx_dropped); 129262306a36Sopenharmony_ci *data++ = QLCNIC_FILL_STATS(mac_stats->mac_rx_crc_error); 129362306a36Sopenharmony_ci *data++ = QLCNIC_FILL_STATS(mac_stats->mac_align_error); 129462306a36Sopenharmony_ci } else if (type == QLCNIC_ESW_STATS) { 129562306a36Sopenharmony_ci struct __qlcnic_esw_statistics *esw_stats = 129662306a36Sopenharmony_ci (struct __qlcnic_esw_statistics *)stats; 129762306a36Sopenharmony_ci *data++ = QLCNIC_FILL_STATS(esw_stats->unicast_frames); 129862306a36Sopenharmony_ci *data++ = QLCNIC_FILL_STATS(esw_stats->multicast_frames); 129962306a36Sopenharmony_ci *data++ = QLCNIC_FILL_STATS(esw_stats->broadcast_frames); 130062306a36Sopenharmony_ci *data++ = QLCNIC_FILL_STATS(esw_stats->dropped_frames); 130162306a36Sopenharmony_ci *data++ = QLCNIC_FILL_STATS(esw_stats->errors); 130262306a36Sopenharmony_ci *data++ = QLCNIC_FILL_STATS(esw_stats->local_frames); 130362306a36Sopenharmony_ci *data++ = QLCNIC_FILL_STATS(esw_stats->numbytes); 130462306a36Sopenharmony_ci } 130562306a36Sopenharmony_ci return data; 130662306a36Sopenharmony_ci} 130762306a36Sopenharmony_ci 130862306a36Sopenharmony_civoid qlcnic_update_stats(struct qlcnic_adapter *adapter) 130962306a36Sopenharmony_ci{ 131062306a36Sopenharmony_ci struct qlcnic_tx_queue_stats tx_stats; 131162306a36Sopenharmony_ci struct qlcnic_host_tx_ring *tx_ring; 131262306a36Sopenharmony_ci int ring; 131362306a36Sopenharmony_ci 131462306a36Sopenharmony_ci memset(&tx_stats, 0, sizeof(tx_stats)); 131562306a36Sopenharmony_ci for (ring = 0; ring < adapter->drv_tx_rings; ring++) { 131662306a36Sopenharmony_ci tx_ring = &adapter->tx_ring[ring]; 131762306a36Sopenharmony_ci tx_stats.xmit_on += tx_ring->tx_stats.xmit_on; 131862306a36Sopenharmony_ci tx_stats.xmit_off += tx_ring->tx_stats.xmit_off; 131962306a36Sopenharmony_ci tx_stats.xmit_called += tx_ring->tx_stats.xmit_called; 132062306a36Sopenharmony_ci tx_stats.xmit_finished += tx_ring->tx_stats.xmit_finished; 132162306a36Sopenharmony_ci tx_stats.tx_bytes += tx_ring->tx_stats.tx_bytes; 132262306a36Sopenharmony_ci } 132362306a36Sopenharmony_ci 132462306a36Sopenharmony_ci adapter->stats.xmit_on = tx_stats.xmit_on; 132562306a36Sopenharmony_ci adapter->stats.xmit_off = tx_stats.xmit_off; 132662306a36Sopenharmony_ci adapter->stats.xmitcalled = tx_stats.xmit_called; 132762306a36Sopenharmony_ci adapter->stats.xmitfinished = tx_stats.xmit_finished; 132862306a36Sopenharmony_ci adapter->stats.txbytes = tx_stats.tx_bytes; 132962306a36Sopenharmony_ci} 133062306a36Sopenharmony_ci 133162306a36Sopenharmony_cistatic u64 *qlcnic_fill_tx_queue_stats(u64 *data, void *stats) 133262306a36Sopenharmony_ci{ 133362306a36Sopenharmony_ci struct qlcnic_host_tx_ring *tx_ring; 133462306a36Sopenharmony_ci 133562306a36Sopenharmony_ci tx_ring = (struct qlcnic_host_tx_ring *)stats; 133662306a36Sopenharmony_ci 133762306a36Sopenharmony_ci *data++ = QLCNIC_FILL_STATS(tx_ring->tx_stats.xmit_on); 133862306a36Sopenharmony_ci *data++ = QLCNIC_FILL_STATS(tx_ring->tx_stats.xmit_off); 133962306a36Sopenharmony_ci *data++ = QLCNIC_FILL_STATS(tx_ring->tx_stats.xmit_called); 134062306a36Sopenharmony_ci *data++ = QLCNIC_FILL_STATS(tx_ring->tx_stats.xmit_finished); 134162306a36Sopenharmony_ci *data++ = QLCNIC_FILL_STATS(tx_ring->tx_stats.tx_bytes); 134262306a36Sopenharmony_ci 134362306a36Sopenharmony_ci return data; 134462306a36Sopenharmony_ci} 134562306a36Sopenharmony_ci 134662306a36Sopenharmony_cistatic void qlcnic_get_ethtool_stats(struct net_device *dev, 134762306a36Sopenharmony_ci struct ethtool_stats *stats, u64 *data) 134862306a36Sopenharmony_ci{ 134962306a36Sopenharmony_ci struct qlcnic_adapter *adapter = netdev_priv(dev); 135062306a36Sopenharmony_ci struct qlcnic_host_tx_ring *tx_ring; 135162306a36Sopenharmony_ci struct qlcnic_esw_statistics port_stats; 135262306a36Sopenharmony_ci struct qlcnic_mac_statistics mac_stats; 135362306a36Sopenharmony_ci int index, ret, length, size, ring; 135462306a36Sopenharmony_ci char *p; 135562306a36Sopenharmony_ci 135662306a36Sopenharmony_ci memset(data, 0, stats->n_stats * sizeof(u64)); 135762306a36Sopenharmony_ci 135862306a36Sopenharmony_ci for (ring = 0; ring < adapter->drv_tx_rings; ring++) { 135962306a36Sopenharmony_ci if (adapter->is_up == QLCNIC_ADAPTER_UP_MAGIC) { 136062306a36Sopenharmony_ci tx_ring = &adapter->tx_ring[ring]; 136162306a36Sopenharmony_ci data = qlcnic_fill_tx_queue_stats(data, tx_ring); 136262306a36Sopenharmony_ci qlcnic_update_stats(adapter); 136362306a36Sopenharmony_ci } else { 136462306a36Sopenharmony_ci data += QLCNIC_TX_STATS_LEN; 136562306a36Sopenharmony_ci } 136662306a36Sopenharmony_ci } 136762306a36Sopenharmony_ci 136862306a36Sopenharmony_ci length = QLCNIC_STATS_LEN; 136962306a36Sopenharmony_ci for (index = 0; index < length; index++) { 137062306a36Sopenharmony_ci p = (char *)adapter + qlcnic_gstrings_stats[index].stat_offset; 137162306a36Sopenharmony_ci size = qlcnic_gstrings_stats[index].sizeof_stat; 137262306a36Sopenharmony_ci *data++ = (size == sizeof(u64)) ? (*(u64 *)p) : ((*(u32 *)p)); 137362306a36Sopenharmony_ci } 137462306a36Sopenharmony_ci 137562306a36Sopenharmony_ci if (qlcnic_83xx_check(adapter)) { 137662306a36Sopenharmony_ci if (adapter->ahw->linkup) 137762306a36Sopenharmony_ci qlcnic_83xx_get_stats(adapter, data); 137862306a36Sopenharmony_ci return; 137962306a36Sopenharmony_ci } else { 138062306a36Sopenharmony_ci /* Retrieve MAC statistics from firmware */ 138162306a36Sopenharmony_ci memset(&mac_stats, 0, sizeof(struct qlcnic_mac_statistics)); 138262306a36Sopenharmony_ci qlcnic_get_mac_stats(adapter, &mac_stats); 138362306a36Sopenharmony_ci data = qlcnic_fill_stats(data, &mac_stats, QLCNIC_MAC_STATS); 138462306a36Sopenharmony_ci } 138562306a36Sopenharmony_ci 138662306a36Sopenharmony_ci if (!(adapter->flags & QLCNIC_ESWITCH_ENABLED)) 138762306a36Sopenharmony_ci return; 138862306a36Sopenharmony_ci 138962306a36Sopenharmony_ci memset(&port_stats, 0, sizeof(struct qlcnic_esw_statistics)); 139062306a36Sopenharmony_ci ret = qlcnic_get_port_stats(adapter, adapter->ahw->pci_func, 139162306a36Sopenharmony_ci QLCNIC_QUERY_RX_COUNTER, &port_stats.rx); 139262306a36Sopenharmony_ci if (ret) 139362306a36Sopenharmony_ci return; 139462306a36Sopenharmony_ci 139562306a36Sopenharmony_ci data = qlcnic_fill_stats(data, &port_stats.rx, QLCNIC_ESW_STATS); 139662306a36Sopenharmony_ci ret = qlcnic_get_port_stats(adapter, adapter->ahw->pci_func, 139762306a36Sopenharmony_ci QLCNIC_QUERY_TX_COUNTER, &port_stats.tx); 139862306a36Sopenharmony_ci if (ret) 139962306a36Sopenharmony_ci return; 140062306a36Sopenharmony_ci 140162306a36Sopenharmony_ci qlcnic_fill_stats(data, &port_stats.tx, QLCNIC_ESW_STATS); 140262306a36Sopenharmony_ci} 140362306a36Sopenharmony_ci 140462306a36Sopenharmony_cistatic int qlcnic_set_led(struct net_device *dev, 140562306a36Sopenharmony_ci enum ethtool_phys_id_state state) 140662306a36Sopenharmony_ci{ 140762306a36Sopenharmony_ci struct qlcnic_adapter *adapter = netdev_priv(dev); 140862306a36Sopenharmony_ci int drv_sds_rings = adapter->drv_sds_rings; 140962306a36Sopenharmony_ci int err = -EIO, active = 1; 141062306a36Sopenharmony_ci 141162306a36Sopenharmony_ci if (qlcnic_83xx_check(adapter)) 141262306a36Sopenharmony_ci return qlcnic_83xx_set_led(dev, state); 141362306a36Sopenharmony_ci 141462306a36Sopenharmony_ci if (adapter->ahw->op_mode == QLCNIC_NON_PRIV_FUNC) { 141562306a36Sopenharmony_ci netdev_warn(dev, "LED test not supported for non " 141662306a36Sopenharmony_ci "privilege function\n"); 141762306a36Sopenharmony_ci return -EOPNOTSUPP; 141862306a36Sopenharmony_ci } 141962306a36Sopenharmony_ci 142062306a36Sopenharmony_ci switch (state) { 142162306a36Sopenharmony_ci case ETHTOOL_ID_ACTIVE: 142262306a36Sopenharmony_ci if (test_and_set_bit(__QLCNIC_LED_ENABLE, &adapter->state)) 142362306a36Sopenharmony_ci return -EBUSY; 142462306a36Sopenharmony_ci 142562306a36Sopenharmony_ci if (test_bit(__QLCNIC_RESETTING, &adapter->state)) 142662306a36Sopenharmony_ci break; 142762306a36Sopenharmony_ci 142862306a36Sopenharmony_ci if (!test_bit(__QLCNIC_DEV_UP, &adapter->state)) { 142962306a36Sopenharmony_ci if (qlcnic_diag_alloc_res(dev, QLCNIC_LED_TEST)) 143062306a36Sopenharmony_ci break; 143162306a36Sopenharmony_ci set_bit(__QLCNIC_DIAG_RES_ALLOC, &adapter->state); 143262306a36Sopenharmony_ci } 143362306a36Sopenharmony_ci 143462306a36Sopenharmony_ci if (adapter->nic_ops->config_led(adapter, 1, 0xf) == 0) { 143562306a36Sopenharmony_ci err = 0; 143662306a36Sopenharmony_ci break; 143762306a36Sopenharmony_ci } 143862306a36Sopenharmony_ci 143962306a36Sopenharmony_ci dev_err(&adapter->pdev->dev, 144062306a36Sopenharmony_ci "Failed to set LED blink state.\n"); 144162306a36Sopenharmony_ci break; 144262306a36Sopenharmony_ci 144362306a36Sopenharmony_ci case ETHTOOL_ID_INACTIVE: 144462306a36Sopenharmony_ci active = 0; 144562306a36Sopenharmony_ci 144662306a36Sopenharmony_ci if (test_bit(__QLCNIC_RESETTING, &adapter->state)) 144762306a36Sopenharmony_ci break; 144862306a36Sopenharmony_ci 144962306a36Sopenharmony_ci if (!test_bit(__QLCNIC_DEV_UP, &adapter->state)) { 145062306a36Sopenharmony_ci if (qlcnic_diag_alloc_res(dev, QLCNIC_LED_TEST)) 145162306a36Sopenharmony_ci break; 145262306a36Sopenharmony_ci set_bit(__QLCNIC_DIAG_RES_ALLOC, &adapter->state); 145362306a36Sopenharmony_ci } 145462306a36Sopenharmony_ci 145562306a36Sopenharmony_ci if (adapter->nic_ops->config_led(adapter, 0, 0xf)) 145662306a36Sopenharmony_ci dev_err(&adapter->pdev->dev, 145762306a36Sopenharmony_ci "Failed to reset LED blink state.\n"); 145862306a36Sopenharmony_ci 145962306a36Sopenharmony_ci break; 146062306a36Sopenharmony_ci 146162306a36Sopenharmony_ci default: 146262306a36Sopenharmony_ci return -EINVAL; 146362306a36Sopenharmony_ci } 146462306a36Sopenharmony_ci 146562306a36Sopenharmony_ci if (test_and_clear_bit(__QLCNIC_DIAG_RES_ALLOC, &adapter->state)) 146662306a36Sopenharmony_ci qlcnic_diag_free_res(dev, drv_sds_rings); 146762306a36Sopenharmony_ci 146862306a36Sopenharmony_ci if (!active || err) 146962306a36Sopenharmony_ci clear_bit(__QLCNIC_LED_ENABLE, &adapter->state); 147062306a36Sopenharmony_ci 147162306a36Sopenharmony_ci return err; 147262306a36Sopenharmony_ci} 147362306a36Sopenharmony_ci 147462306a36Sopenharmony_cistatic void 147562306a36Sopenharmony_ciqlcnic_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) 147662306a36Sopenharmony_ci{ 147762306a36Sopenharmony_ci struct qlcnic_adapter *adapter = netdev_priv(dev); 147862306a36Sopenharmony_ci u32 wol_cfg; 147962306a36Sopenharmony_ci int err = 0; 148062306a36Sopenharmony_ci 148162306a36Sopenharmony_ci if (qlcnic_83xx_check(adapter)) 148262306a36Sopenharmony_ci return; 148362306a36Sopenharmony_ci wol->supported = 0; 148462306a36Sopenharmony_ci wol->wolopts = 0; 148562306a36Sopenharmony_ci 148662306a36Sopenharmony_ci wol_cfg = QLCRD32(adapter, QLCNIC_WOL_CONFIG_NV, &err); 148762306a36Sopenharmony_ci if (err == -EIO) 148862306a36Sopenharmony_ci return; 148962306a36Sopenharmony_ci if (wol_cfg & (1UL << adapter->portnum)) 149062306a36Sopenharmony_ci wol->supported |= WAKE_MAGIC; 149162306a36Sopenharmony_ci 149262306a36Sopenharmony_ci wol_cfg = QLCRD32(adapter, QLCNIC_WOL_CONFIG, &err); 149362306a36Sopenharmony_ci if (wol_cfg & (1UL << adapter->portnum)) 149462306a36Sopenharmony_ci wol->wolopts |= WAKE_MAGIC; 149562306a36Sopenharmony_ci} 149662306a36Sopenharmony_ci 149762306a36Sopenharmony_cistatic int 149862306a36Sopenharmony_ciqlcnic_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) 149962306a36Sopenharmony_ci{ 150062306a36Sopenharmony_ci struct qlcnic_adapter *adapter = netdev_priv(dev); 150162306a36Sopenharmony_ci u32 wol_cfg; 150262306a36Sopenharmony_ci int err = 0; 150362306a36Sopenharmony_ci 150462306a36Sopenharmony_ci if (qlcnic_83xx_check(adapter)) 150562306a36Sopenharmony_ci return -EOPNOTSUPP; 150662306a36Sopenharmony_ci if (wol->wolopts & ~WAKE_MAGIC) 150762306a36Sopenharmony_ci return -EINVAL; 150862306a36Sopenharmony_ci 150962306a36Sopenharmony_ci wol_cfg = QLCRD32(adapter, QLCNIC_WOL_CONFIG_NV, &err); 151062306a36Sopenharmony_ci if (err == -EIO) 151162306a36Sopenharmony_ci return err; 151262306a36Sopenharmony_ci if (!(wol_cfg & (1 << adapter->portnum))) 151362306a36Sopenharmony_ci return -EOPNOTSUPP; 151462306a36Sopenharmony_ci 151562306a36Sopenharmony_ci wol_cfg = QLCRD32(adapter, QLCNIC_WOL_CONFIG, &err); 151662306a36Sopenharmony_ci if (err == -EIO) 151762306a36Sopenharmony_ci return err; 151862306a36Sopenharmony_ci if (wol->wolopts & WAKE_MAGIC) 151962306a36Sopenharmony_ci wol_cfg |= 1UL << adapter->portnum; 152062306a36Sopenharmony_ci else 152162306a36Sopenharmony_ci wol_cfg &= ~(1UL << adapter->portnum); 152262306a36Sopenharmony_ci 152362306a36Sopenharmony_ci QLCWR32(adapter, QLCNIC_WOL_CONFIG, wol_cfg); 152462306a36Sopenharmony_ci 152562306a36Sopenharmony_ci return 0; 152662306a36Sopenharmony_ci} 152762306a36Sopenharmony_ci 152862306a36Sopenharmony_ci/* 152962306a36Sopenharmony_ci * Set the coalescing parameters. Currently only normal is supported. 153062306a36Sopenharmony_ci * If rx_coalesce_usecs == 0 or rx_max_coalesced_frames == 0 then set the 153162306a36Sopenharmony_ci * firmware coalescing to default. 153262306a36Sopenharmony_ci */ 153362306a36Sopenharmony_cistatic int qlcnic_set_intr_coalesce(struct net_device *netdev, 153462306a36Sopenharmony_ci struct ethtool_coalesce *ethcoal, 153562306a36Sopenharmony_ci struct kernel_ethtool_coalesce *kernel_coal, 153662306a36Sopenharmony_ci struct netlink_ext_ack *extack) 153762306a36Sopenharmony_ci{ 153862306a36Sopenharmony_ci struct qlcnic_adapter *adapter = netdev_priv(netdev); 153962306a36Sopenharmony_ci int err; 154062306a36Sopenharmony_ci 154162306a36Sopenharmony_ci if (!test_bit(__QLCNIC_DEV_UP, &adapter->state)) 154262306a36Sopenharmony_ci return -EINVAL; 154362306a36Sopenharmony_ci 154462306a36Sopenharmony_ci /* 154562306a36Sopenharmony_ci * Return Error if unsupported values or 154662306a36Sopenharmony_ci * unsupported parameters are set. 154762306a36Sopenharmony_ci */ 154862306a36Sopenharmony_ci if (ethcoal->rx_coalesce_usecs > 0xffff || 154962306a36Sopenharmony_ci ethcoal->rx_max_coalesced_frames > 0xffff || 155062306a36Sopenharmony_ci ethcoal->tx_coalesce_usecs > 0xffff || 155162306a36Sopenharmony_ci ethcoal->tx_max_coalesced_frames > 0xffff) 155262306a36Sopenharmony_ci return -EINVAL; 155362306a36Sopenharmony_ci 155462306a36Sopenharmony_ci err = qlcnic_config_intr_coalesce(adapter, ethcoal); 155562306a36Sopenharmony_ci 155662306a36Sopenharmony_ci return err; 155762306a36Sopenharmony_ci} 155862306a36Sopenharmony_ci 155962306a36Sopenharmony_cistatic int qlcnic_get_intr_coalesce(struct net_device *netdev, 156062306a36Sopenharmony_ci struct ethtool_coalesce *ethcoal, 156162306a36Sopenharmony_ci struct kernel_ethtool_coalesce *kernel_coal, 156262306a36Sopenharmony_ci struct netlink_ext_ack *extack) 156362306a36Sopenharmony_ci{ 156462306a36Sopenharmony_ci struct qlcnic_adapter *adapter = netdev_priv(netdev); 156562306a36Sopenharmony_ci 156662306a36Sopenharmony_ci if (adapter->is_up != QLCNIC_ADAPTER_UP_MAGIC) 156762306a36Sopenharmony_ci return -EINVAL; 156862306a36Sopenharmony_ci 156962306a36Sopenharmony_ci ethcoal->rx_coalesce_usecs = adapter->ahw->coal.rx_time_us; 157062306a36Sopenharmony_ci ethcoal->rx_max_coalesced_frames = adapter->ahw->coal.rx_packets; 157162306a36Sopenharmony_ci ethcoal->tx_coalesce_usecs = adapter->ahw->coal.tx_time_us; 157262306a36Sopenharmony_ci ethcoal->tx_max_coalesced_frames = adapter->ahw->coal.tx_packets; 157362306a36Sopenharmony_ci 157462306a36Sopenharmony_ci return 0; 157562306a36Sopenharmony_ci} 157662306a36Sopenharmony_ci 157762306a36Sopenharmony_cistatic u32 qlcnic_get_msglevel(struct net_device *netdev) 157862306a36Sopenharmony_ci{ 157962306a36Sopenharmony_ci struct qlcnic_adapter *adapter = netdev_priv(netdev); 158062306a36Sopenharmony_ci 158162306a36Sopenharmony_ci return adapter->ahw->msg_enable; 158262306a36Sopenharmony_ci} 158362306a36Sopenharmony_ci 158462306a36Sopenharmony_cistatic void qlcnic_set_msglevel(struct net_device *netdev, u32 msglvl) 158562306a36Sopenharmony_ci{ 158662306a36Sopenharmony_ci struct qlcnic_adapter *adapter = netdev_priv(netdev); 158762306a36Sopenharmony_ci 158862306a36Sopenharmony_ci adapter->ahw->msg_enable = msglvl; 158962306a36Sopenharmony_ci} 159062306a36Sopenharmony_ci 159162306a36Sopenharmony_ciint qlcnic_enable_fw_dump_state(struct qlcnic_adapter *adapter) 159262306a36Sopenharmony_ci{ 159362306a36Sopenharmony_ci struct qlcnic_fw_dump *fw_dump = &adapter->ahw->fw_dump; 159462306a36Sopenharmony_ci u32 val; 159562306a36Sopenharmony_ci 159662306a36Sopenharmony_ci if (qlcnic_84xx_check(adapter)) { 159762306a36Sopenharmony_ci if (qlcnic_83xx_lock_driver(adapter)) 159862306a36Sopenharmony_ci return -EBUSY; 159962306a36Sopenharmony_ci 160062306a36Sopenharmony_ci val = QLCRDX(adapter->ahw, QLC_83XX_IDC_CTRL); 160162306a36Sopenharmony_ci val &= ~QLC_83XX_IDC_DISABLE_FW_DUMP; 160262306a36Sopenharmony_ci QLCWRX(adapter->ahw, QLC_83XX_IDC_CTRL, val); 160362306a36Sopenharmony_ci 160462306a36Sopenharmony_ci qlcnic_83xx_unlock_driver(adapter); 160562306a36Sopenharmony_ci } else { 160662306a36Sopenharmony_ci fw_dump->enable = true; 160762306a36Sopenharmony_ci } 160862306a36Sopenharmony_ci 160962306a36Sopenharmony_ci dev_info(&adapter->pdev->dev, "FW dump enabled\n"); 161062306a36Sopenharmony_ci 161162306a36Sopenharmony_ci return 0; 161262306a36Sopenharmony_ci} 161362306a36Sopenharmony_ci 161462306a36Sopenharmony_cistatic int qlcnic_disable_fw_dump_state(struct qlcnic_adapter *adapter) 161562306a36Sopenharmony_ci{ 161662306a36Sopenharmony_ci struct qlcnic_fw_dump *fw_dump = &adapter->ahw->fw_dump; 161762306a36Sopenharmony_ci u32 val; 161862306a36Sopenharmony_ci 161962306a36Sopenharmony_ci if (qlcnic_84xx_check(adapter)) { 162062306a36Sopenharmony_ci if (qlcnic_83xx_lock_driver(adapter)) 162162306a36Sopenharmony_ci return -EBUSY; 162262306a36Sopenharmony_ci 162362306a36Sopenharmony_ci val = QLCRDX(adapter->ahw, QLC_83XX_IDC_CTRL); 162462306a36Sopenharmony_ci val |= QLC_83XX_IDC_DISABLE_FW_DUMP; 162562306a36Sopenharmony_ci QLCWRX(adapter->ahw, QLC_83XX_IDC_CTRL, val); 162662306a36Sopenharmony_ci 162762306a36Sopenharmony_ci qlcnic_83xx_unlock_driver(adapter); 162862306a36Sopenharmony_ci } else { 162962306a36Sopenharmony_ci fw_dump->enable = false; 163062306a36Sopenharmony_ci } 163162306a36Sopenharmony_ci 163262306a36Sopenharmony_ci dev_info(&adapter->pdev->dev, "FW dump disabled\n"); 163362306a36Sopenharmony_ci 163462306a36Sopenharmony_ci return 0; 163562306a36Sopenharmony_ci} 163662306a36Sopenharmony_ci 163762306a36Sopenharmony_cibool qlcnic_check_fw_dump_state(struct qlcnic_adapter *adapter) 163862306a36Sopenharmony_ci{ 163962306a36Sopenharmony_ci struct qlcnic_fw_dump *fw_dump = &adapter->ahw->fw_dump; 164062306a36Sopenharmony_ci bool state; 164162306a36Sopenharmony_ci u32 val; 164262306a36Sopenharmony_ci 164362306a36Sopenharmony_ci if (qlcnic_84xx_check(adapter)) { 164462306a36Sopenharmony_ci val = QLCRDX(adapter->ahw, QLC_83XX_IDC_CTRL); 164562306a36Sopenharmony_ci state = (val & QLC_83XX_IDC_DISABLE_FW_DUMP) ? false : true; 164662306a36Sopenharmony_ci } else { 164762306a36Sopenharmony_ci state = fw_dump->enable; 164862306a36Sopenharmony_ci } 164962306a36Sopenharmony_ci 165062306a36Sopenharmony_ci return state; 165162306a36Sopenharmony_ci} 165262306a36Sopenharmony_ci 165362306a36Sopenharmony_cistatic int 165462306a36Sopenharmony_ciqlcnic_get_dump_flag(struct net_device *netdev, struct ethtool_dump *dump) 165562306a36Sopenharmony_ci{ 165662306a36Sopenharmony_ci struct qlcnic_adapter *adapter = netdev_priv(netdev); 165762306a36Sopenharmony_ci struct qlcnic_fw_dump *fw_dump = &adapter->ahw->fw_dump; 165862306a36Sopenharmony_ci 165962306a36Sopenharmony_ci if (!fw_dump->tmpl_hdr) { 166062306a36Sopenharmony_ci netdev_err(adapter->netdev, "FW Dump not supported\n"); 166162306a36Sopenharmony_ci return -ENOTSUPP; 166262306a36Sopenharmony_ci } 166362306a36Sopenharmony_ci 166462306a36Sopenharmony_ci if (fw_dump->clr) 166562306a36Sopenharmony_ci dump->len = fw_dump->tmpl_hdr_size + fw_dump->size; 166662306a36Sopenharmony_ci else 166762306a36Sopenharmony_ci dump->len = 0; 166862306a36Sopenharmony_ci 166962306a36Sopenharmony_ci if (!qlcnic_check_fw_dump_state(adapter)) 167062306a36Sopenharmony_ci dump->flag = ETH_FW_DUMP_DISABLE; 167162306a36Sopenharmony_ci else 167262306a36Sopenharmony_ci dump->flag = fw_dump->cap_mask; 167362306a36Sopenharmony_ci 167462306a36Sopenharmony_ci dump->version = adapter->fw_version; 167562306a36Sopenharmony_ci return 0; 167662306a36Sopenharmony_ci} 167762306a36Sopenharmony_ci 167862306a36Sopenharmony_cistatic int 167962306a36Sopenharmony_ciqlcnic_get_dump_data(struct net_device *netdev, struct ethtool_dump *dump, 168062306a36Sopenharmony_ci void *buffer) 168162306a36Sopenharmony_ci{ 168262306a36Sopenharmony_ci int i, copy_sz; 168362306a36Sopenharmony_ci u32 *hdr_ptr; 168462306a36Sopenharmony_ci __le32 *data; 168562306a36Sopenharmony_ci struct qlcnic_adapter *adapter = netdev_priv(netdev); 168662306a36Sopenharmony_ci struct qlcnic_fw_dump *fw_dump = &adapter->ahw->fw_dump; 168762306a36Sopenharmony_ci 168862306a36Sopenharmony_ci if (!fw_dump->tmpl_hdr) { 168962306a36Sopenharmony_ci netdev_err(netdev, "FW Dump not supported\n"); 169062306a36Sopenharmony_ci return -ENOTSUPP; 169162306a36Sopenharmony_ci } 169262306a36Sopenharmony_ci 169362306a36Sopenharmony_ci if (!fw_dump->clr) { 169462306a36Sopenharmony_ci netdev_info(netdev, "Dump not available\n"); 169562306a36Sopenharmony_ci return -EINVAL; 169662306a36Sopenharmony_ci } 169762306a36Sopenharmony_ci 169862306a36Sopenharmony_ci /* Copy template header first */ 169962306a36Sopenharmony_ci copy_sz = fw_dump->tmpl_hdr_size; 170062306a36Sopenharmony_ci hdr_ptr = (u32 *)fw_dump->tmpl_hdr; 170162306a36Sopenharmony_ci data = buffer; 170262306a36Sopenharmony_ci for (i = 0; i < copy_sz/sizeof(u32); i++) 170362306a36Sopenharmony_ci *data++ = cpu_to_le32(*hdr_ptr++); 170462306a36Sopenharmony_ci 170562306a36Sopenharmony_ci /* Copy captured dump data */ 170662306a36Sopenharmony_ci memcpy(buffer + copy_sz, fw_dump->data, fw_dump->size); 170762306a36Sopenharmony_ci dump->len = copy_sz + fw_dump->size; 170862306a36Sopenharmony_ci dump->flag = fw_dump->cap_mask; 170962306a36Sopenharmony_ci 171062306a36Sopenharmony_ci /* Free dump area once data has been captured */ 171162306a36Sopenharmony_ci vfree(fw_dump->data); 171262306a36Sopenharmony_ci fw_dump->data = NULL; 171362306a36Sopenharmony_ci fw_dump->clr = 0; 171462306a36Sopenharmony_ci netdev_info(netdev, "extracted the FW dump Successfully\n"); 171562306a36Sopenharmony_ci return 0; 171662306a36Sopenharmony_ci} 171762306a36Sopenharmony_ci 171862306a36Sopenharmony_cistatic int qlcnic_set_dump_mask(struct qlcnic_adapter *adapter, u32 mask) 171962306a36Sopenharmony_ci{ 172062306a36Sopenharmony_ci struct qlcnic_fw_dump *fw_dump = &adapter->ahw->fw_dump; 172162306a36Sopenharmony_ci struct net_device *netdev = adapter->netdev; 172262306a36Sopenharmony_ci 172362306a36Sopenharmony_ci if (!qlcnic_check_fw_dump_state(adapter)) { 172462306a36Sopenharmony_ci netdev_info(netdev, 172562306a36Sopenharmony_ci "Can not change driver mask to 0x%x. FW dump not enabled\n", 172662306a36Sopenharmony_ci mask); 172762306a36Sopenharmony_ci return -EOPNOTSUPP; 172862306a36Sopenharmony_ci } 172962306a36Sopenharmony_ci 173062306a36Sopenharmony_ci fw_dump->cap_mask = mask; 173162306a36Sopenharmony_ci 173262306a36Sopenharmony_ci /* Store new capture mask in template header as well*/ 173362306a36Sopenharmony_ci qlcnic_store_cap_mask(adapter, fw_dump->tmpl_hdr, mask); 173462306a36Sopenharmony_ci 173562306a36Sopenharmony_ci netdev_info(netdev, "Driver mask changed to: 0x%x\n", mask); 173662306a36Sopenharmony_ci return 0; 173762306a36Sopenharmony_ci} 173862306a36Sopenharmony_ci 173962306a36Sopenharmony_cistatic int 174062306a36Sopenharmony_ciqlcnic_set_dump(struct net_device *netdev, struct ethtool_dump *val) 174162306a36Sopenharmony_ci{ 174262306a36Sopenharmony_ci struct qlcnic_adapter *adapter = netdev_priv(netdev); 174362306a36Sopenharmony_ci struct qlcnic_fw_dump *fw_dump = &adapter->ahw->fw_dump; 174462306a36Sopenharmony_ci bool valid_mask = false; 174562306a36Sopenharmony_ci int i, ret = 0; 174662306a36Sopenharmony_ci 174762306a36Sopenharmony_ci switch (val->flag) { 174862306a36Sopenharmony_ci case QLCNIC_FORCE_FW_DUMP_KEY: 174962306a36Sopenharmony_ci if (!fw_dump->tmpl_hdr) { 175062306a36Sopenharmony_ci netdev_err(netdev, "FW dump not supported\n"); 175162306a36Sopenharmony_ci ret = -EOPNOTSUPP; 175262306a36Sopenharmony_ci break; 175362306a36Sopenharmony_ci } 175462306a36Sopenharmony_ci 175562306a36Sopenharmony_ci if (!qlcnic_check_fw_dump_state(adapter)) { 175662306a36Sopenharmony_ci netdev_info(netdev, "FW dump not enabled\n"); 175762306a36Sopenharmony_ci ret = -EOPNOTSUPP; 175862306a36Sopenharmony_ci break; 175962306a36Sopenharmony_ci } 176062306a36Sopenharmony_ci 176162306a36Sopenharmony_ci if (fw_dump->clr) { 176262306a36Sopenharmony_ci netdev_info(netdev, 176362306a36Sopenharmony_ci "Previous dump not cleared, not forcing dump\n"); 176462306a36Sopenharmony_ci break; 176562306a36Sopenharmony_ci } 176662306a36Sopenharmony_ci 176762306a36Sopenharmony_ci netdev_info(netdev, "Forcing a FW dump\n"); 176862306a36Sopenharmony_ci qlcnic_dev_request_reset(adapter, val->flag); 176962306a36Sopenharmony_ci break; 177062306a36Sopenharmony_ci case QLCNIC_DISABLE_FW_DUMP: 177162306a36Sopenharmony_ci if (!fw_dump->tmpl_hdr) { 177262306a36Sopenharmony_ci netdev_err(netdev, "FW dump not supported\n"); 177362306a36Sopenharmony_ci ret = -EOPNOTSUPP; 177462306a36Sopenharmony_ci break; 177562306a36Sopenharmony_ci } 177662306a36Sopenharmony_ci 177762306a36Sopenharmony_ci ret = qlcnic_disable_fw_dump_state(adapter); 177862306a36Sopenharmony_ci break; 177962306a36Sopenharmony_ci 178062306a36Sopenharmony_ci case QLCNIC_ENABLE_FW_DUMP: 178162306a36Sopenharmony_ci if (!fw_dump->tmpl_hdr) { 178262306a36Sopenharmony_ci netdev_err(netdev, "FW dump not supported\n"); 178362306a36Sopenharmony_ci ret = -EOPNOTSUPP; 178462306a36Sopenharmony_ci break; 178562306a36Sopenharmony_ci } 178662306a36Sopenharmony_ci 178762306a36Sopenharmony_ci ret = qlcnic_enable_fw_dump_state(adapter); 178862306a36Sopenharmony_ci break; 178962306a36Sopenharmony_ci 179062306a36Sopenharmony_ci case QLCNIC_FORCE_FW_RESET: 179162306a36Sopenharmony_ci netdev_info(netdev, "Forcing a FW reset\n"); 179262306a36Sopenharmony_ci qlcnic_dev_request_reset(adapter, val->flag); 179362306a36Sopenharmony_ci adapter->flags &= ~QLCNIC_FW_RESET_OWNER; 179462306a36Sopenharmony_ci break; 179562306a36Sopenharmony_ci 179662306a36Sopenharmony_ci case QLCNIC_SET_QUIESCENT: 179762306a36Sopenharmony_ci case QLCNIC_RESET_QUIESCENT: 179862306a36Sopenharmony_ci if (test_bit(__QLCNIC_MAINTENANCE_MODE, &adapter->state)) 179962306a36Sopenharmony_ci netdev_info(netdev, "Device is in non-operational state\n"); 180062306a36Sopenharmony_ci break; 180162306a36Sopenharmony_ci 180262306a36Sopenharmony_ci default: 180362306a36Sopenharmony_ci if (!fw_dump->tmpl_hdr) { 180462306a36Sopenharmony_ci netdev_err(netdev, "FW dump not supported\n"); 180562306a36Sopenharmony_ci ret = -EOPNOTSUPP; 180662306a36Sopenharmony_ci break; 180762306a36Sopenharmony_ci } 180862306a36Sopenharmony_ci 180962306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(qlcnic_fw_dump_level); i++) { 181062306a36Sopenharmony_ci if (val->flag == qlcnic_fw_dump_level[i]) { 181162306a36Sopenharmony_ci valid_mask = true; 181262306a36Sopenharmony_ci break; 181362306a36Sopenharmony_ci } 181462306a36Sopenharmony_ci } 181562306a36Sopenharmony_ci 181662306a36Sopenharmony_ci if (valid_mask) { 181762306a36Sopenharmony_ci ret = qlcnic_set_dump_mask(adapter, val->flag); 181862306a36Sopenharmony_ci } else { 181962306a36Sopenharmony_ci netdev_info(netdev, "Invalid dump level: 0x%x\n", 182062306a36Sopenharmony_ci val->flag); 182162306a36Sopenharmony_ci ret = -EINVAL; 182262306a36Sopenharmony_ci } 182362306a36Sopenharmony_ci } 182462306a36Sopenharmony_ci return ret; 182562306a36Sopenharmony_ci} 182662306a36Sopenharmony_ci 182762306a36Sopenharmony_ciconst struct ethtool_ops qlcnic_ethtool_ops = { 182862306a36Sopenharmony_ci .supported_coalesce_params = ETHTOOL_COALESCE_USECS | 182962306a36Sopenharmony_ci ETHTOOL_COALESCE_MAX_FRAMES, 183062306a36Sopenharmony_ci .get_drvinfo = qlcnic_get_drvinfo, 183162306a36Sopenharmony_ci .get_regs_len = qlcnic_get_regs_len, 183262306a36Sopenharmony_ci .get_regs = qlcnic_get_regs, 183362306a36Sopenharmony_ci .get_link = ethtool_op_get_link, 183462306a36Sopenharmony_ci .get_eeprom_len = qlcnic_get_eeprom_len, 183562306a36Sopenharmony_ci .get_eeprom = qlcnic_get_eeprom, 183662306a36Sopenharmony_ci .get_ringparam = qlcnic_get_ringparam, 183762306a36Sopenharmony_ci .set_ringparam = qlcnic_set_ringparam, 183862306a36Sopenharmony_ci .get_channels = qlcnic_get_channels, 183962306a36Sopenharmony_ci .set_channels = qlcnic_set_channels, 184062306a36Sopenharmony_ci .get_pauseparam = qlcnic_get_pauseparam, 184162306a36Sopenharmony_ci .set_pauseparam = qlcnic_set_pauseparam, 184262306a36Sopenharmony_ci .get_wol = qlcnic_get_wol, 184362306a36Sopenharmony_ci .set_wol = qlcnic_set_wol, 184462306a36Sopenharmony_ci .self_test = qlcnic_diag_test, 184562306a36Sopenharmony_ci .get_strings = qlcnic_get_strings, 184662306a36Sopenharmony_ci .get_ethtool_stats = qlcnic_get_ethtool_stats, 184762306a36Sopenharmony_ci .get_sset_count = qlcnic_get_sset_count, 184862306a36Sopenharmony_ci .get_coalesce = qlcnic_get_intr_coalesce, 184962306a36Sopenharmony_ci .set_coalesce = qlcnic_set_intr_coalesce, 185062306a36Sopenharmony_ci .set_phys_id = qlcnic_set_led, 185162306a36Sopenharmony_ci .set_msglevel = qlcnic_set_msglevel, 185262306a36Sopenharmony_ci .get_msglevel = qlcnic_get_msglevel, 185362306a36Sopenharmony_ci .get_dump_flag = qlcnic_get_dump_flag, 185462306a36Sopenharmony_ci .get_dump_data = qlcnic_get_dump_data, 185562306a36Sopenharmony_ci .set_dump = qlcnic_set_dump, 185662306a36Sopenharmony_ci .get_link_ksettings = qlcnic_get_link_ksettings, 185762306a36Sopenharmony_ci .set_link_ksettings = qlcnic_set_link_ksettings, 185862306a36Sopenharmony_ci}; 185962306a36Sopenharmony_ci 186062306a36Sopenharmony_ciconst struct ethtool_ops qlcnic_sriov_vf_ethtool_ops = { 186162306a36Sopenharmony_ci .supported_coalesce_params = ETHTOOL_COALESCE_USECS | 186262306a36Sopenharmony_ci ETHTOOL_COALESCE_MAX_FRAMES, 186362306a36Sopenharmony_ci .get_drvinfo = qlcnic_get_drvinfo, 186462306a36Sopenharmony_ci .get_regs_len = qlcnic_get_regs_len, 186562306a36Sopenharmony_ci .get_regs = qlcnic_get_regs, 186662306a36Sopenharmony_ci .get_link = ethtool_op_get_link, 186762306a36Sopenharmony_ci .get_eeprom_len = qlcnic_get_eeprom_len, 186862306a36Sopenharmony_ci .get_eeprom = qlcnic_get_eeprom, 186962306a36Sopenharmony_ci .get_ringparam = qlcnic_get_ringparam, 187062306a36Sopenharmony_ci .set_ringparam = qlcnic_set_ringparam, 187162306a36Sopenharmony_ci .get_channels = qlcnic_get_channels, 187262306a36Sopenharmony_ci .get_pauseparam = qlcnic_get_pauseparam, 187362306a36Sopenharmony_ci .get_wol = qlcnic_get_wol, 187462306a36Sopenharmony_ci .get_strings = qlcnic_get_strings, 187562306a36Sopenharmony_ci .get_ethtool_stats = qlcnic_get_ethtool_stats, 187662306a36Sopenharmony_ci .get_sset_count = qlcnic_get_sset_count, 187762306a36Sopenharmony_ci .get_coalesce = qlcnic_get_intr_coalesce, 187862306a36Sopenharmony_ci .set_coalesce = qlcnic_set_intr_coalesce, 187962306a36Sopenharmony_ci .set_msglevel = qlcnic_set_msglevel, 188062306a36Sopenharmony_ci .get_msglevel = qlcnic_get_msglevel, 188162306a36Sopenharmony_ci .get_link_ksettings = qlcnic_get_link_ksettings, 188262306a36Sopenharmony_ci}; 188362306a36Sopenharmony_ci 188462306a36Sopenharmony_ciconst struct ethtool_ops qlcnic_ethtool_failed_ops = { 188562306a36Sopenharmony_ci .get_drvinfo = qlcnic_get_drvinfo, 188662306a36Sopenharmony_ci .set_msglevel = qlcnic_set_msglevel, 188762306a36Sopenharmony_ci .get_msglevel = qlcnic_get_msglevel, 188862306a36Sopenharmony_ci .set_dump = qlcnic_set_dump, 188962306a36Sopenharmony_ci .get_link_ksettings = qlcnic_get_link_ksettings, 189062306a36Sopenharmony_ci}; 1891