162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2013-2015 Chelsio Communications. All rights reserved. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/firmware.h> 762306a36Sopenharmony_ci#include <linux/mdio.h> 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include "cxgb4.h" 1062306a36Sopenharmony_ci#include "t4_regs.h" 1162306a36Sopenharmony_ci#include "t4fw_api.h" 1262306a36Sopenharmony_ci#include "cxgb4_cudbg.h" 1362306a36Sopenharmony_ci#include "cxgb4_filter.h" 1462306a36Sopenharmony_ci#include "cxgb4_tc_flower.h" 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#define EEPROM_MAGIC 0x38E2F10C 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_cistatic u32 get_msglevel(struct net_device *dev) 1962306a36Sopenharmony_ci{ 2062306a36Sopenharmony_ci return netdev2adap(dev)->msg_enable; 2162306a36Sopenharmony_ci} 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistatic void set_msglevel(struct net_device *dev, u32 val) 2462306a36Sopenharmony_ci{ 2562306a36Sopenharmony_ci netdev2adap(dev)->msg_enable = val; 2662306a36Sopenharmony_ci} 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cienum cxgb4_ethtool_tests { 2962306a36Sopenharmony_ci CXGB4_ETHTOOL_LB_TEST, 3062306a36Sopenharmony_ci CXGB4_ETHTOOL_MAX_TEST, 3162306a36Sopenharmony_ci}; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic const char cxgb4_selftest_strings[CXGB4_ETHTOOL_MAX_TEST][ETH_GSTRING_LEN] = { 3462306a36Sopenharmony_ci "Loop back test (offline)", 3562306a36Sopenharmony_ci}; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic const char * const flash_region_strings[] = { 3862306a36Sopenharmony_ci "All", 3962306a36Sopenharmony_ci "Firmware", 4062306a36Sopenharmony_ci "PHY Firmware", 4162306a36Sopenharmony_ci "Boot", 4262306a36Sopenharmony_ci "Boot CFG", 4362306a36Sopenharmony_ci}; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistatic const char stats_strings[][ETH_GSTRING_LEN] = { 4662306a36Sopenharmony_ci "tx_octets_ok ", 4762306a36Sopenharmony_ci "tx_frames_ok ", 4862306a36Sopenharmony_ci "tx_broadcast_frames ", 4962306a36Sopenharmony_ci "tx_multicast_frames ", 5062306a36Sopenharmony_ci "tx_unicast_frames ", 5162306a36Sopenharmony_ci "tx_error_frames ", 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci "tx_frames_64 ", 5462306a36Sopenharmony_ci "tx_frames_65_to_127 ", 5562306a36Sopenharmony_ci "tx_frames_128_to_255 ", 5662306a36Sopenharmony_ci "tx_frames_256_to_511 ", 5762306a36Sopenharmony_ci "tx_frames_512_to_1023 ", 5862306a36Sopenharmony_ci "tx_frames_1024_to_1518 ", 5962306a36Sopenharmony_ci "tx_frames_1519_to_max ", 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci "tx_frames_dropped ", 6262306a36Sopenharmony_ci "tx_pause_frames ", 6362306a36Sopenharmony_ci "tx_ppp0_frames ", 6462306a36Sopenharmony_ci "tx_ppp1_frames ", 6562306a36Sopenharmony_ci "tx_ppp2_frames ", 6662306a36Sopenharmony_ci "tx_ppp3_frames ", 6762306a36Sopenharmony_ci "tx_ppp4_frames ", 6862306a36Sopenharmony_ci "tx_ppp5_frames ", 6962306a36Sopenharmony_ci "tx_ppp6_frames ", 7062306a36Sopenharmony_ci "tx_ppp7_frames ", 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci "rx_octets_ok ", 7362306a36Sopenharmony_ci "rx_frames_ok ", 7462306a36Sopenharmony_ci "rx_broadcast_frames ", 7562306a36Sopenharmony_ci "rx_multicast_frames ", 7662306a36Sopenharmony_ci "rx_unicast_frames ", 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci "rx_frames_too_long ", 7962306a36Sopenharmony_ci "rx_jabber_errors ", 8062306a36Sopenharmony_ci "rx_fcs_errors ", 8162306a36Sopenharmony_ci "rx_length_errors ", 8262306a36Sopenharmony_ci "rx_symbol_errors ", 8362306a36Sopenharmony_ci "rx_runt_frames ", 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci "rx_frames_64 ", 8662306a36Sopenharmony_ci "rx_frames_65_to_127 ", 8762306a36Sopenharmony_ci "rx_frames_128_to_255 ", 8862306a36Sopenharmony_ci "rx_frames_256_to_511 ", 8962306a36Sopenharmony_ci "rx_frames_512_to_1023 ", 9062306a36Sopenharmony_ci "rx_frames_1024_to_1518 ", 9162306a36Sopenharmony_ci "rx_frames_1519_to_max ", 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci "rx_pause_frames ", 9462306a36Sopenharmony_ci "rx_ppp0_frames ", 9562306a36Sopenharmony_ci "rx_ppp1_frames ", 9662306a36Sopenharmony_ci "rx_ppp2_frames ", 9762306a36Sopenharmony_ci "rx_ppp3_frames ", 9862306a36Sopenharmony_ci "rx_ppp4_frames ", 9962306a36Sopenharmony_ci "rx_ppp5_frames ", 10062306a36Sopenharmony_ci "rx_ppp6_frames ", 10162306a36Sopenharmony_ci "rx_ppp7_frames ", 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci "rx_bg0_frames_dropped ", 10462306a36Sopenharmony_ci "rx_bg1_frames_dropped ", 10562306a36Sopenharmony_ci "rx_bg2_frames_dropped ", 10662306a36Sopenharmony_ci "rx_bg3_frames_dropped ", 10762306a36Sopenharmony_ci "rx_bg0_frames_trunc ", 10862306a36Sopenharmony_ci "rx_bg1_frames_trunc ", 10962306a36Sopenharmony_ci "rx_bg2_frames_trunc ", 11062306a36Sopenharmony_ci "rx_bg3_frames_trunc ", 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci "tso ", 11362306a36Sopenharmony_ci "uso ", 11462306a36Sopenharmony_ci "tx_csum_offload ", 11562306a36Sopenharmony_ci "rx_csum_good ", 11662306a36Sopenharmony_ci "vlan_extractions ", 11762306a36Sopenharmony_ci "vlan_insertions ", 11862306a36Sopenharmony_ci "gro_packets ", 11962306a36Sopenharmony_ci "gro_merged ", 12062306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_CHELSIO_TLS_DEVICE) 12162306a36Sopenharmony_ci "tx_tls_encrypted_packets", 12262306a36Sopenharmony_ci "tx_tls_encrypted_bytes ", 12362306a36Sopenharmony_ci "tx_tls_ctx ", 12462306a36Sopenharmony_ci "tx_tls_ooo ", 12562306a36Sopenharmony_ci "tx_tls_skip_no_sync_data", 12662306a36Sopenharmony_ci "tx_tls_drop_no_sync_data", 12762306a36Sopenharmony_ci "tx_tls_drop_bypass_req ", 12862306a36Sopenharmony_ci#endif 12962306a36Sopenharmony_ci}; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cistatic char adapter_stats_strings[][ETH_GSTRING_LEN] = { 13262306a36Sopenharmony_ci "db_drop ", 13362306a36Sopenharmony_ci "db_full ", 13462306a36Sopenharmony_ci "db_empty ", 13562306a36Sopenharmony_ci "write_coal_success ", 13662306a36Sopenharmony_ci "write_coal_fail ", 13762306a36Sopenharmony_ci}; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_cistatic char loopback_stats_strings[][ETH_GSTRING_LEN] = { 14062306a36Sopenharmony_ci "-------Loopback----------- ", 14162306a36Sopenharmony_ci "octets_ok ", 14262306a36Sopenharmony_ci "frames_ok ", 14362306a36Sopenharmony_ci "bcast_frames ", 14462306a36Sopenharmony_ci "mcast_frames ", 14562306a36Sopenharmony_ci "ucast_frames ", 14662306a36Sopenharmony_ci "error_frames ", 14762306a36Sopenharmony_ci "frames_64 ", 14862306a36Sopenharmony_ci "frames_65_to_127 ", 14962306a36Sopenharmony_ci "frames_128_to_255 ", 15062306a36Sopenharmony_ci "frames_256_to_511 ", 15162306a36Sopenharmony_ci "frames_512_to_1023 ", 15262306a36Sopenharmony_ci "frames_1024_to_1518 ", 15362306a36Sopenharmony_ci "frames_1519_to_max ", 15462306a36Sopenharmony_ci "frames_dropped ", 15562306a36Sopenharmony_ci "bg0_frames_dropped ", 15662306a36Sopenharmony_ci "bg1_frames_dropped ", 15762306a36Sopenharmony_ci "bg2_frames_dropped ", 15862306a36Sopenharmony_ci "bg3_frames_dropped ", 15962306a36Sopenharmony_ci "bg0_frames_trunc ", 16062306a36Sopenharmony_ci "bg1_frames_trunc ", 16162306a36Sopenharmony_ci "bg2_frames_trunc ", 16262306a36Sopenharmony_ci "bg3_frames_trunc ", 16362306a36Sopenharmony_ci}; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_cistatic const char cxgb4_priv_flags_strings[][ETH_GSTRING_LEN] = { 16662306a36Sopenharmony_ci [PRIV_FLAG_PORT_TX_VM_BIT] = "port_tx_vm_wr", 16762306a36Sopenharmony_ci}; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistatic int get_sset_count(struct net_device *dev, int sset) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci switch (sset) { 17262306a36Sopenharmony_ci case ETH_SS_STATS: 17362306a36Sopenharmony_ci return ARRAY_SIZE(stats_strings) + 17462306a36Sopenharmony_ci ARRAY_SIZE(adapter_stats_strings) + 17562306a36Sopenharmony_ci ARRAY_SIZE(loopback_stats_strings); 17662306a36Sopenharmony_ci case ETH_SS_PRIV_FLAGS: 17762306a36Sopenharmony_ci return ARRAY_SIZE(cxgb4_priv_flags_strings); 17862306a36Sopenharmony_ci case ETH_SS_TEST: 17962306a36Sopenharmony_ci return ARRAY_SIZE(cxgb4_selftest_strings); 18062306a36Sopenharmony_ci default: 18162306a36Sopenharmony_ci return -EOPNOTSUPP; 18262306a36Sopenharmony_ci } 18362306a36Sopenharmony_ci} 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_cistatic int get_regs_len(struct net_device *dev) 18662306a36Sopenharmony_ci{ 18762306a36Sopenharmony_ci struct adapter *adap = netdev2adap(dev); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci return t4_get_regs_len(adap); 19062306a36Sopenharmony_ci} 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_cistatic int get_eeprom_len(struct net_device *dev) 19362306a36Sopenharmony_ci{ 19462306a36Sopenharmony_ci return EEPROMSIZE; 19562306a36Sopenharmony_ci} 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_cistatic void get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) 19862306a36Sopenharmony_ci{ 19962306a36Sopenharmony_ci struct adapter *adapter = netdev2adap(dev); 20062306a36Sopenharmony_ci u32 exprom_vers; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci strscpy(info->driver, cxgb4_driver_name, sizeof(info->driver)); 20362306a36Sopenharmony_ci strscpy(info->bus_info, pci_name(adapter->pdev), 20462306a36Sopenharmony_ci sizeof(info->bus_info)); 20562306a36Sopenharmony_ci info->regdump_len = get_regs_len(dev); 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci if (adapter->params.fw_vers) 20862306a36Sopenharmony_ci snprintf(info->fw_version, sizeof(info->fw_version), 20962306a36Sopenharmony_ci "%u.%u.%u.%u, TP %u.%u.%u.%u", 21062306a36Sopenharmony_ci FW_HDR_FW_VER_MAJOR_G(adapter->params.fw_vers), 21162306a36Sopenharmony_ci FW_HDR_FW_VER_MINOR_G(adapter->params.fw_vers), 21262306a36Sopenharmony_ci FW_HDR_FW_VER_MICRO_G(adapter->params.fw_vers), 21362306a36Sopenharmony_ci FW_HDR_FW_VER_BUILD_G(adapter->params.fw_vers), 21462306a36Sopenharmony_ci FW_HDR_FW_VER_MAJOR_G(adapter->params.tp_vers), 21562306a36Sopenharmony_ci FW_HDR_FW_VER_MINOR_G(adapter->params.tp_vers), 21662306a36Sopenharmony_ci FW_HDR_FW_VER_MICRO_G(adapter->params.tp_vers), 21762306a36Sopenharmony_ci FW_HDR_FW_VER_BUILD_G(adapter->params.tp_vers)); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci if (!t4_get_exprom_version(adapter, &exprom_vers)) 22062306a36Sopenharmony_ci snprintf(info->erom_version, sizeof(info->erom_version), 22162306a36Sopenharmony_ci "%u.%u.%u.%u", 22262306a36Sopenharmony_ci FW_HDR_FW_VER_MAJOR_G(exprom_vers), 22362306a36Sopenharmony_ci FW_HDR_FW_VER_MINOR_G(exprom_vers), 22462306a36Sopenharmony_ci FW_HDR_FW_VER_MICRO_G(exprom_vers), 22562306a36Sopenharmony_ci FW_HDR_FW_VER_BUILD_G(exprom_vers)); 22662306a36Sopenharmony_ci info->n_priv_flags = ARRAY_SIZE(cxgb4_priv_flags_strings); 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_cistatic void get_strings(struct net_device *dev, u32 stringset, u8 *data) 23062306a36Sopenharmony_ci{ 23162306a36Sopenharmony_ci if (stringset == ETH_SS_STATS) { 23262306a36Sopenharmony_ci memcpy(data, stats_strings, sizeof(stats_strings)); 23362306a36Sopenharmony_ci data += sizeof(stats_strings); 23462306a36Sopenharmony_ci memcpy(data, adapter_stats_strings, 23562306a36Sopenharmony_ci sizeof(adapter_stats_strings)); 23662306a36Sopenharmony_ci data += sizeof(adapter_stats_strings); 23762306a36Sopenharmony_ci memcpy(data, loopback_stats_strings, 23862306a36Sopenharmony_ci sizeof(loopback_stats_strings)); 23962306a36Sopenharmony_ci } else if (stringset == ETH_SS_PRIV_FLAGS) { 24062306a36Sopenharmony_ci memcpy(data, cxgb4_priv_flags_strings, 24162306a36Sopenharmony_ci sizeof(cxgb4_priv_flags_strings)); 24262306a36Sopenharmony_ci } else if (stringset == ETH_SS_TEST) { 24362306a36Sopenharmony_ci memcpy(data, cxgb4_selftest_strings, 24462306a36Sopenharmony_ci sizeof(cxgb4_selftest_strings)); 24562306a36Sopenharmony_ci } 24662306a36Sopenharmony_ci} 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci/* port stats maintained per queue of the port. They should be in the same 24962306a36Sopenharmony_ci * order as in stats_strings above. 25062306a36Sopenharmony_ci */ 25162306a36Sopenharmony_cistruct queue_port_stats { 25262306a36Sopenharmony_ci u64 tso; 25362306a36Sopenharmony_ci u64 uso; 25462306a36Sopenharmony_ci u64 tx_csum; 25562306a36Sopenharmony_ci u64 rx_csum; 25662306a36Sopenharmony_ci u64 vlan_ex; 25762306a36Sopenharmony_ci u64 vlan_ins; 25862306a36Sopenharmony_ci u64 gro_pkts; 25962306a36Sopenharmony_ci u64 gro_merged; 26062306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_CHELSIO_TLS_DEVICE) 26162306a36Sopenharmony_ci u64 tx_tls_encrypted_packets; 26262306a36Sopenharmony_ci u64 tx_tls_encrypted_bytes; 26362306a36Sopenharmony_ci u64 tx_tls_ctx; 26462306a36Sopenharmony_ci u64 tx_tls_ooo; 26562306a36Sopenharmony_ci u64 tx_tls_skip_no_sync_data; 26662306a36Sopenharmony_ci u64 tx_tls_drop_no_sync_data; 26762306a36Sopenharmony_ci u64 tx_tls_drop_bypass_req; 26862306a36Sopenharmony_ci#endif 26962306a36Sopenharmony_ci}; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_cistruct adapter_stats { 27262306a36Sopenharmony_ci u64 db_drop; 27362306a36Sopenharmony_ci u64 db_full; 27462306a36Sopenharmony_ci u64 db_empty; 27562306a36Sopenharmony_ci u64 wc_success; 27662306a36Sopenharmony_ci u64 wc_fail; 27762306a36Sopenharmony_ci}; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_cistatic void collect_sge_port_stats(const struct adapter *adap, 28062306a36Sopenharmony_ci const struct port_info *p, 28162306a36Sopenharmony_ci struct queue_port_stats *s) 28262306a36Sopenharmony_ci{ 28362306a36Sopenharmony_ci const struct sge_eth_txq *tx = &adap->sge.ethtxq[p->first_qset]; 28462306a36Sopenharmony_ci const struct sge_eth_rxq *rx = &adap->sge.ethrxq[p->first_qset]; 28562306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_CHELSIO_TLS_DEVICE) 28662306a36Sopenharmony_ci const struct ch_ktls_port_stats_debug *ktls_stats; 28762306a36Sopenharmony_ci#endif 28862306a36Sopenharmony_ci struct sge_eohw_txq *eohw_tx; 28962306a36Sopenharmony_ci unsigned int i; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci memset(s, 0, sizeof(*s)); 29262306a36Sopenharmony_ci for (i = 0; i < p->nqsets; i++, rx++, tx++) { 29362306a36Sopenharmony_ci s->tso += tx->tso; 29462306a36Sopenharmony_ci s->uso += tx->uso; 29562306a36Sopenharmony_ci s->tx_csum += tx->tx_cso; 29662306a36Sopenharmony_ci s->rx_csum += rx->stats.rx_cso; 29762306a36Sopenharmony_ci s->vlan_ex += rx->stats.vlan_ex; 29862306a36Sopenharmony_ci s->vlan_ins += tx->vlan_ins; 29962306a36Sopenharmony_ci s->gro_pkts += rx->stats.lro_pkts; 30062306a36Sopenharmony_ci s->gro_merged += rx->stats.lro_merged; 30162306a36Sopenharmony_ci } 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci if (adap->sge.eohw_txq) { 30462306a36Sopenharmony_ci eohw_tx = &adap->sge.eohw_txq[p->first_qset]; 30562306a36Sopenharmony_ci for (i = 0; i < p->nqsets; i++, eohw_tx++) { 30662306a36Sopenharmony_ci s->tso += eohw_tx->tso; 30762306a36Sopenharmony_ci s->uso += eohw_tx->uso; 30862306a36Sopenharmony_ci s->tx_csum += eohw_tx->tx_cso; 30962306a36Sopenharmony_ci s->vlan_ins += eohw_tx->vlan_ins; 31062306a36Sopenharmony_ci } 31162306a36Sopenharmony_ci } 31262306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_CHELSIO_TLS_DEVICE) 31362306a36Sopenharmony_ci ktls_stats = &adap->ch_ktls_stats.ktls_port[p->port_id]; 31462306a36Sopenharmony_ci s->tx_tls_encrypted_packets = 31562306a36Sopenharmony_ci atomic64_read(&ktls_stats->ktls_tx_encrypted_packets); 31662306a36Sopenharmony_ci s->tx_tls_encrypted_bytes = 31762306a36Sopenharmony_ci atomic64_read(&ktls_stats->ktls_tx_encrypted_bytes); 31862306a36Sopenharmony_ci s->tx_tls_ctx = atomic64_read(&ktls_stats->ktls_tx_ctx); 31962306a36Sopenharmony_ci s->tx_tls_ooo = atomic64_read(&ktls_stats->ktls_tx_ooo); 32062306a36Sopenharmony_ci s->tx_tls_skip_no_sync_data = 32162306a36Sopenharmony_ci atomic64_read(&ktls_stats->ktls_tx_skip_no_sync_data); 32262306a36Sopenharmony_ci s->tx_tls_drop_no_sync_data = 32362306a36Sopenharmony_ci atomic64_read(&ktls_stats->ktls_tx_drop_no_sync_data); 32462306a36Sopenharmony_ci s->tx_tls_drop_bypass_req = 32562306a36Sopenharmony_ci atomic64_read(&ktls_stats->ktls_tx_drop_bypass_req); 32662306a36Sopenharmony_ci#endif 32762306a36Sopenharmony_ci} 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_cistatic void collect_adapter_stats(struct adapter *adap, struct adapter_stats *s) 33062306a36Sopenharmony_ci{ 33162306a36Sopenharmony_ci u64 val1, val2; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci memset(s, 0, sizeof(*s)); 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci s->db_drop = adap->db_stats.db_drop; 33662306a36Sopenharmony_ci s->db_full = adap->db_stats.db_full; 33762306a36Sopenharmony_ci s->db_empty = adap->db_stats.db_empty; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci if (!is_t4(adap->params.chip)) { 34062306a36Sopenharmony_ci int v; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci v = t4_read_reg(adap, SGE_STAT_CFG_A); 34362306a36Sopenharmony_ci if (STATSOURCE_T5_G(v) == 7) { 34462306a36Sopenharmony_ci val2 = t4_read_reg(adap, SGE_STAT_MATCH_A); 34562306a36Sopenharmony_ci val1 = t4_read_reg(adap, SGE_STAT_TOTAL_A); 34662306a36Sopenharmony_ci s->wc_success = val1 - val2; 34762306a36Sopenharmony_ci s->wc_fail = val2; 34862306a36Sopenharmony_ci } 34962306a36Sopenharmony_ci } 35062306a36Sopenharmony_ci} 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_cistatic void get_stats(struct net_device *dev, struct ethtool_stats *stats, 35362306a36Sopenharmony_ci u64 *data) 35462306a36Sopenharmony_ci{ 35562306a36Sopenharmony_ci struct port_info *pi = netdev_priv(dev); 35662306a36Sopenharmony_ci struct adapter *adapter = pi->adapter; 35762306a36Sopenharmony_ci struct lb_port_stats s; 35862306a36Sopenharmony_ci int i; 35962306a36Sopenharmony_ci u64 *p0; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci t4_get_port_stats_offset(adapter, pi->tx_chan, 36262306a36Sopenharmony_ci (struct port_stats *)data, 36362306a36Sopenharmony_ci &pi->stats_base); 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci data += sizeof(struct port_stats) / sizeof(u64); 36662306a36Sopenharmony_ci collect_sge_port_stats(adapter, pi, (struct queue_port_stats *)data); 36762306a36Sopenharmony_ci data += sizeof(struct queue_port_stats) / sizeof(u64); 36862306a36Sopenharmony_ci collect_adapter_stats(adapter, (struct adapter_stats *)data); 36962306a36Sopenharmony_ci data += sizeof(struct adapter_stats) / sizeof(u64); 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci *data++ = (u64)pi->port_id; 37262306a36Sopenharmony_ci memset(&s, 0, sizeof(s)); 37362306a36Sopenharmony_ci t4_get_lb_stats(adapter, pi->port_id, &s); 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci p0 = &s.octets; 37662306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(loopback_stats_strings) - 1; i++) 37762306a36Sopenharmony_ci *data++ = (unsigned long long)*p0++; 37862306a36Sopenharmony_ci} 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_cistatic void get_regs(struct net_device *dev, struct ethtool_regs *regs, 38162306a36Sopenharmony_ci void *buf) 38262306a36Sopenharmony_ci{ 38362306a36Sopenharmony_ci struct adapter *adap = netdev2adap(dev); 38462306a36Sopenharmony_ci size_t buf_size; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci buf_size = t4_get_regs_len(adap); 38762306a36Sopenharmony_ci regs->version = mk_adap_vers(adap); 38862306a36Sopenharmony_ci t4_get_regs(adap, buf, buf_size); 38962306a36Sopenharmony_ci} 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_cistatic int restart_autoneg(struct net_device *dev) 39262306a36Sopenharmony_ci{ 39362306a36Sopenharmony_ci struct port_info *p = netdev_priv(dev); 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci if (!netif_running(dev)) 39662306a36Sopenharmony_ci return -EAGAIN; 39762306a36Sopenharmony_ci if (p->link_cfg.autoneg != AUTONEG_ENABLE) 39862306a36Sopenharmony_ci return -EINVAL; 39962306a36Sopenharmony_ci t4_restart_aneg(p->adapter, p->adapter->pf, p->tx_chan); 40062306a36Sopenharmony_ci return 0; 40162306a36Sopenharmony_ci} 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_cistatic int identify_port(struct net_device *dev, 40462306a36Sopenharmony_ci enum ethtool_phys_id_state state) 40562306a36Sopenharmony_ci{ 40662306a36Sopenharmony_ci unsigned int val; 40762306a36Sopenharmony_ci struct adapter *adap = netdev2adap(dev); 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci if (state == ETHTOOL_ID_ACTIVE) 41062306a36Sopenharmony_ci val = 0xffff; 41162306a36Sopenharmony_ci else if (state == ETHTOOL_ID_INACTIVE) 41262306a36Sopenharmony_ci val = 0; 41362306a36Sopenharmony_ci else 41462306a36Sopenharmony_ci return -EINVAL; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci return t4_identify_port(adap, adap->pf, netdev2pinfo(dev)->viid, val); 41762306a36Sopenharmony_ci} 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci/** 42062306a36Sopenharmony_ci * from_fw_port_mod_type - translate Firmware Port/Module type to Ethtool 42162306a36Sopenharmony_ci * @port_type: Firmware Port Type 42262306a36Sopenharmony_ci * @mod_type: Firmware Module Type 42362306a36Sopenharmony_ci * 42462306a36Sopenharmony_ci * Translate Firmware Port/Module type to Ethtool Port Type. 42562306a36Sopenharmony_ci */ 42662306a36Sopenharmony_cistatic int from_fw_port_mod_type(enum fw_port_type port_type, 42762306a36Sopenharmony_ci enum fw_port_module_type mod_type) 42862306a36Sopenharmony_ci{ 42962306a36Sopenharmony_ci if (port_type == FW_PORT_TYPE_BT_SGMII || 43062306a36Sopenharmony_ci port_type == FW_PORT_TYPE_BT_XFI || 43162306a36Sopenharmony_ci port_type == FW_PORT_TYPE_BT_XAUI) { 43262306a36Sopenharmony_ci return PORT_TP; 43362306a36Sopenharmony_ci } else if (port_type == FW_PORT_TYPE_FIBER_XFI || 43462306a36Sopenharmony_ci port_type == FW_PORT_TYPE_FIBER_XAUI) { 43562306a36Sopenharmony_ci return PORT_FIBRE; 43662306a36Sopenharmony_ci } else if (port_type == FW_PORT_TYPE_SFP || 43762306a36Sopenharmony_ci port_type == FW_PORT_TYPE_QSFP_10G || 43862306a36Sopenharmony_ci port_type == FW_PORT_TYPE_QSA || 43962306a36Sopenharmony_ci port_type == FW_PORT_TYPE_QSFP || 44062306a36Sopenharmony_ci port_type == FW_PORT_TYPE_CR4_QSFP || 44162306a36Sopenharmony_ci port_type == FW_PORT_TYPE_CR_QSFP || 44262306a36Sopenharmony_ci port_type == FW_PORT_TYPE_CR2_QSFP || 44362306a36Sopenharmony_ci port_type == FW_PORT_TYPE_SFP28) { 44462306a36Sopenharmony_ci if (mod_type == FW_PORT_MOD_TYPE_LR || 44562306a36Sopenharmony_ci mod_type == FW_PORT_MOD_TYPE_SR || 44662306a36Sopenharmony_ci mod_type == FW_PORT_MOD_TYPE_ER || 44762306a36Sopenharmony_ci mod_type == FW_PORT_MOD_TYPE_LRM) 44862306a36Sopenharmony_ci return PORT_FIBRE; 44962306a36Sopenharmony_ci else if (mod_type == FW_PORT_MOD_TYPE_TWINAX_PASSIVE || 45062306a36Sopenharmony_ci mod_type == FW_PORT_MOD_TYPE_TWINAX_ACTIVE) 45162306a36Sopenharmony_ci return PORT_DA; 45262306a36Sopenharmony_ci else 45362306a36Sopenharmony_ci return PORT_OTHER; 45462306a36Sopenharmony_ci } else if (port_type == FW_PORT_TYPE_KR4_100G || 45562306a36Sopenharmony_ci port_type == FW_PORT_TYPE_KR_SFP28 || 45662306a36Sopenharmony_ci port_type == FW_PORT_TYPE_KR_XLAUI) { 45762306a36Sopenharmony_ci return PORT_NONE; 45862306a36Sopenharmony_ci } 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci return PORT_OTHER; 46162306a36Sopenharmony_ci} 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci/** 46462306a36Sopenharmony_ci * speed_to_fw_caps - translate Port Speed to Firmware Port Capabilities 46562306a36Sopenharmony_ci * @speed: speed in Kb/s 46662306a36Sopenharmony_ci * 46762306a36Sopenharmony_ci * Translates a specific Port Speed into a Firmware Port Capabilities 46862306a36Sopenharmony_ci * value. 46962306a36Sopenharmony_ci */ 47062306a36Sopenharmony_cistatic unsigned int speed_to_fw_caps(int speed) 47162306a36Sopenharmony_ci{ 47262306a36Sopenharmony_ci if (speed == 100) 47362306a36Sopenharmony_ci return FW_PORT_CAP32_SPEED_100M; 47462306a36Sopenharmony_ci if (speed == 1000) 47562306a36Sopenharmony_ci return FW_PORT_CAP32_SPEED_1G; 47662306a36Sopenharmony_ci if (speed == 10000) 47762306a36Sopenharmony_ci return FW_PORT_CAP32_SPEED_10G; 47862306a36Sopenharmony_ci if (speed == 25000) 47962306a36Sopenharmony_ci return FW_PORT_CAP32_SPEED_25G; 48062306a36Sopenharmony_ci if (speed == 40000) 48162306a36Sopenharmony_ci return FW_PORT_CAP32_SPEED_40G; 48262306a36Sopenharmony_ci if (speed == 50000) 48362306a36Sopenharmony_ci return FW_PORT_CAP32_SPEED_50G; 48462306a36Sopenharmony_ci if (speed == 100000) 48562306a36Sopenharmony_ci return FW_PORT_CAP32_SPEED_100G; 48662306a36Sopenharmony_ci if (speed == 200000) 48762306a36Sopenharmony_ci return FW_PORT_CAP32_SPEED_200G; 48862306a36Sopenharmony_ci if (speed == 400000) 48962306a36Sopenharmony_ci return FW_PORT_CAP32_SPEED_400G; 49062306a36Sopenharmony_ci return 0; 49162306a36Sopenharmony_ci} 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci/** 49462306a36Sopenharmony_ci * fw_caps_to_lmm - translate Firmware to ethtool Link Mode Mask 49562306a36Sopenharmony_ci * @port_type: Firmware Port Type 49662306a36Sopenharmony_ci * @fw_caps: Firmware Port Capabilities 49762306a36Sopenharmony_ci * @link_mode_mask: ethtool Link Mode Mask 49862306a36Sopenharmony_ci * 49962306a36Sopenharmony_ci * Translate a Firmware Port Capabilities specification to an ethtool 50062306a36Sopenharmony_ci * Link Mode Mask. 50162306a36Sopenharmony_ci */ 50262306a36Sopenharmony_cistatic void fw_caps_to_lmm(enum fw_port_type port_type, 50362306a36Sopenharmony_ci fw_port_cap32_t fw_caps, 50462306a36Sopenharmony_ci unsigned long *link_mode_mask) 50562306a36Sopenharmony_ci{ 50662306a36Sopenharmony_ci #define SET_LMM(__lmm_name) \ 50762306a36Sopenharmony_ci do { \ 50862306a36Sopenharmony_ci __set_bit(ETHTOOL_LINK_MODE_ ## __lmm_name ## _BIT, \ 50962306a36Sopenharmony_ci link_mode_mask); \ 51062306a36Sopenharmony_ci } while (0) 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci #define FW_CAPS_TO_LMM(__fw_name, __lmm_name) \ 51362306a36Sopenharmony_ci do { \ 51462306a36Sopenharmony_ci if (fw_caps & FW_PORT_CAP32_ ## __fw_name) \ 51562306a36Sopenharmony_ci SET_LMM(__lmm_name); \ 51662306a36Sopenharmony_ci } while (0) 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci switch (port_type) { 51962306a36Sopenharmony_ci case FW_PORT_TYPE_BT_SGMII: 52062306a36Sopenharmony_ci case FW_PORT_TYPE_BT_XFI: 52162306a36Sopenharmony_ci case FW_PORT_TYPE_BT_XAUI: 52262306a36Sopenharmony_ci SET_LMM(TP); 52362306a36Sopenharmony_ci FW_CAPS_TO_LMM(SPEED_100M, 100baseT_Full); 52462306a36Sopenharmony_ci FW_CAPS_TO_LMM(SPEED_1G, 1000baseT_Full); 52562306a36Sopenharmony_ci FW_CAPS_TO_LMM(SPEED_10G, 10000baseT_Full); 52662306a36Sopenharmony_ci break; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci case FW_PORT_TYPE_KX4: 52962306a36Sopenharmony_ci case FW_PORT_TYPE_KX: 53062306a36Sopenharmony_ci SET_LMM(Backplane); 53162306a36Sopenharmony_ci FW_CAPS_TO_LMM(SPEED_1G, 1000baseKX_Full); 53262306a36Sopenharmony_ci FW_CAPS_TO_LMM(SPEED_10G, 10000baseKX4_Full); 53362306a36Sopenharmony_ci break; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci case FW_PORT_TYPE_KR: 53662306a36Sopenharmony_ci SET_LMM(Backplane); 53762306a36Sopenharmony_ci FW_CAPS_TO_LMM(SPEED_10G, 10000baseKR_Full); 53862306a36Sopenharmony_ci break; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci case FW_PORT_TYPE_BP_AP: 54162306a36Sopenharmony_ci SET_LMM(Backplane); 54262306a36Sopenharmony_ci FW_CAPS_TO_LMM(SPEED_1G, 1000baseKX_Full); 54362306a36Sopenharmony_ci FW_CAPS_TO_LMM(SPEED_10G, 10000baseR_FEC); 54462306a36Sopenharmony_ci FW_CAPS_TO_LMM(SPEED_10G, 10000baseKR_Full); 54562306a36Sopenharmony_ci break; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci case FW_PORT_TYPE_BP4_AP: 54862306a36Sopenharmony_ci SET_LMM(Backplane); 54962306a36Sopenharmony_ci FW_CAPS_TO_LMM(SPEED_1G, 1000baseKX_Full); 55062306a36Sopenharmony_ci FW_CAPS_TO_LMM(SPEED_10G, 10000baseR_FEC); 55162306a36Sopenharmony_ci FW_CAPS_TO_LMM(SPEED_10G, 10000baseKR_Full); 55262306a36Sopenharmony_ci FW_CAPS_TO_LMM(SPEED_10G, 10000baseKX4_Full); 55362306a36Sopenharmony_ci break; 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci case FW_PORT_TYPE_FIBER_XFI: 55662306a36Sopenharmony_ci case FW_PORT_TYPE_FIBER_XAUI: 55762306a36Sopenharmony_ci case FW_PORT_TYPE_SFP: 55862306a36Sopenharmony_ci case FW_PORT_TYPE_QSFP_10G: 55962306a36Sopenharmony_ci case FW_PORT_TYPE_QSA: 56062306a36Sopenharmony_ci SET_LMM(FIBRE); 56162306a36Sopenharmony_ci FW_CAPS_TO_LMM(SPEED_1G, 1000baseT_Full); 56262306a36Sopenharmony_ci FW_CAPS_TO_LMM(SPEED_10G, 10000baseT_Full); 56362306a36Sopenharmony_ci break; 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci case FW_PORT_TYPE_BP40_BA: 56662306a36Sopenharmony_ci case FW_PORT_TYPE_QSFP: 56762306a36Sopenharmony_ci SET_LMM(FIBRE); 56862306a36Sopenharmony_ci FW_CAPS_TO_LMM(SPEED_1G, 1000baseT_Full); 56962306a36Sopenharmony_ci FW_CAPS_TO_LMM(SPEED_10G, 10000baseT_Full); 57062306a36Sopenharmony_ci FW_CAPS_TO_LMM(SPEED_40G, 40000baseSR4_Full); 57162306a36Sopenharmony_ci break; 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci case FW_PORT_TYPE_CR_QSFP: 57462306a36Sopenharmony_ci case FW_PORT_TYPE_SFP28: 57562306a36Sopenharmony_ci SET_LMM(FIBRE); 57662306a36Sopenharmony_ci FW_CAPS_TO_LMM(SPEED_1G, 1000baseT_Full); 57762306a36Sopenharmony_ci FW_CAPS_TO_LMM(SPEED_10G, 10000baseT_Full); 57862306a36Sopenharmony_ci FW_CAPS_TO_LMM(SPEED_25G, 25000baseCR_Full); 57962306a36Sopenharmony_ci break; 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci case FW_PORT_TYPE_KR_SFP28: 58262306a36Sopenharmony_ci SET_LMM(Backplane); 58362306a36Sopenharmony_ci FW_CAPS_TO_LMM(SPEED_1G, 1000baseT_Full); 58462306a36Sopenharmony_ci FW_CAPS_TO_LMM(SPEED_10G, 10000baseKR_Full); 58562306a36Sopenharmony_ci FW_CAPS_TO_LMM(SPEED_25G, 25000baseKR_Full); 58662306a36Sopenharmony_ci break; 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci case FW_PORT_TYPE_KR_XLAUI: 58962306a36Sopenharmony_ci SET_LMM(Backplane); 59062306a36Sopenharmony_ci FW_CAPS_TO_LMM(SPEED_1G, 1000baseKX_Full); 59162306a36Sopenharmony_ci FW_CAPS_TO_LMM(SPEED_10G, 10000baseKR_Full); 59262306a36Sopenharmony_ci FW_CAPS_TO_LMM(SPEED_40G, 40000baseKR4_Full); 59362306a36Sopenharmony_ci break; 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci case FW_PORT_TYPE_CR2_QSFP: 59662306a36Sopenharmony_ci SET_LMM(FIBRE); 59762306a36Sopenharmony_ci FW_CAPS_TO_LMM(SPEED_50G, 50000baseSR2_Full); 59862306a36Sopenharmony_ci break; 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci case FW_PORT_TYPE_KR4_100G: 60162306a36Sopenharmony_ci case FW_PORT_TYPE_CR4_QSFP: 60262306a36Sopenharmony_ci SET_LMM(FIBRE); 60362306a36Sopenharmony_ci FW_CAPS_TO_LMM(SPEED_1G, 1000baseT_Full); 60462306a36Sopenharmony_ci FW_CAPS_TO_LMM(SPEED_10G, 10000baseKR_Full); 60562306a36Sopenharmony_ci FW_CAPS_TO_LMM(SPEED_40G, 40000baseSR4_Full); 60662306a36Sopenharmony_ci FW_CAPS_TO_LMM(SPEED_25G, 25000baseCR_Full); 60762306a36Sopenharmony_ci FW_CAPS_TO_LMM(SPEED_50G, 50000baseCR2_Full); 60862306a36Sopenharmony_ci FW_CAPS_TO_LMM(SPEED_100G, 100000baseCR4_Full); 60962306a36Sopenharmony_ci break; 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci default: 61262306a36Sopenharmony_ci break; 61362306a36Sopenharmony_ci } 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci if (fw_caps & FW_PORT_CAP32_FEC_V(FW_PORT_CAP32_FEC_M)) { 61662306a36Sopenharmony_ci FW_CAPS_TO_LMM(FEC_RS, FEC_RS); 61762306a36Sopenharmony_ci FW_CAPS_TO_LMM(FEC_BASER_RS, FEC_BASER); 61862306a36Sopenharmony_ci } else { 61962306a36Sopenharmony_ci SET_LMM(FEC_NONE); 62062306a36Sopenharmony_ci } 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci FW_CAPS_TO_LMM(ANEG, Autoneg); 62362306a36Sopenharmony_ci FW_CAPS_TO_LMM(802_3_PAUSE, Pause); 62462306a36Sopenharmony_ci FW_CAPS_TO_LMM(802_3_ASM_DIR, Asym_Pause); 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci #undef FW_CAPS_TO_LMM 62762306a36Sopenharmony_ci #undef SET_LMM 62862306a36Sopenharmony_ci} 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci/** 63162306a36Sopenharmony_ci * lmm_to_fw_caps - translate ethtool Link Mode Mask to Firmware 63262306a36Sopenharmony_ci * capabilities 63362306a36Sopenharmony_ci * @link_mode_mask: ethtool Link Mode Mask 63462306a36Sopenharmony_ci * 63562306a36Sopenharmony_ci * Translate ethtool Link Mode Mask into a Firmware Port capabilities 63662306a36Sopenharmony_ci * value. 63762306a36Sopenharmony_ci */ 63862306a36Sopenharmony_cistatic unsigned int lmm_to_fw_caps(const unsigned long *link_mode_mask) 63962306a36Sopenharmony_ci{ 64062306a36Sopenharmony_ci unsigned int fw_caps = 0; 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci #define LMM_TO_FW_CAPS(__lmm_name, __fw_name) \ 64362306a36Sopenharmony_ci do { \ 64462306a36Sopenharmony_ci if (test_bit(ETHTOOL_LINK_MODE_ ## __lmm_name ## _BIT, \ 64562306a36Sopenharmony_ci link_mode_mask)) \ 64662306a36Sopenharmony_ci fw_caps |= FW_PORT_CAP32_ ## __fw_name; \ 64762306a36Sopenharmony_ci } while (0) 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci LMM_TO_FW_CAPS(100baseT_Full, SPEED_100M); 65062306a36Sopenharmony_ci LMM_TO_FW_CAPS(1000baseT_Full, SPEED_1G); 65162306a36Sopenharmony_ci LMM_TO_FW_CAPS(10000baseT_Full, SPEED_10G); 65262306a36Sopenharmony_ci LMM_TO_FW_CAPS(40000baseSR4_Full, SPEED_40G); 65362306a36Sopenharmony_ci LMM_TO_FW_CAPS(25000baseCR_Full, SPEED_25G); 65462306a36Sopenharmony_ci LMM_TO_FW_CAPS(50000baseCR2_Full, SPEED_50G); 65562306a36Sopenharmony_ci LMM_TO_FW_CAPS(100000baseCR4_Full, SPEED_100G); 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci #undef LMM_TO_FW_CAPS 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci return fw_caps; 66062306a36Sopenharmony_ci} 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_cistatic int get_link_ksettings(struct net_device *dev, 66362306a36Sopenharmony_ci struct ethtool_link_ksettings *link_ksettings) 66462306a36Sopenharmony_ci{ 66562306a36Sopenharmony_ci struct port_info *pi = netdev_priv(dev); 66662306a36Sopenharmony_ci struct ethtool_link_settings *base = &link_ksettings->base; 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci /* For the nonce, the Firmware doesn't send up Port State changes 66962306a36Sopenharmony_ci * when the Virtual Interface attached to the Port is down. So 67062306a36Sopenharmony_ci * if it's down, let's grab any changes. 67162306a36Sopenharmony_ci */ 67262306a36Sopenharmony_ci if (!netif_running(dev)) 67362306a36Sopenharmony_ci (void)t4_update_port_info(pi); 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci ethtool_link_ksettings_zero_link_mode(link_ksettings, supported); 67662306a36Sopenharmony_ci ethtool_link_ksettings_zero_link_mode(link_ksettings, advertising); 67762306a36Sopenharmony_ci ethtool_link_ksettings_zero_link_mode(link_ksettings, lp_advertising); 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci base->port = from_fw_port_mod_type(pi->port_type, pi->mod_type); 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci if (pi->mdio_addr >= 0) { 68262306a36Sopenharmony_ci base->phy_address = pi->mdio_addr; 68362306a36Sopenharmony_ci base->mdio_support = (pi->port_type == FW_PORT_TYPE_BT_SGMII 68462306a36Sopenharmony_ci ? ETH_MDIO_SUPPORTS_C22 68562306a36Sopenharmony_ci : ETH_MDIO_SUPPORTS_C45); 68662306a36Sopenharmony_ci } else { 68762306a36Sopenharmony_ci base->phy_address = 255; 68862306a36Sopenharmony_ci base->mdio_support = 0; 68962306a36Sopenharmony_ci } 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci fw_caps_to_lmm(pi->port_type, pi->link_cfg.pcaps, 69262306a36Sopenharmony_ci link_ksettings->link_modes.supported); 69362306a36Sopenharmony_ci fw_caps_to_lmm(pi->port_type, 69462306a36Sopenharmony_ci t4_link_acaps(pi->adapter, 69562306a36Sopenharmony_ci pi->lport, 69662306a36Sopenharmony_ci &pi->link_cfg), 69762306a36Sopenharmony_ci link_ksettings->link_modes.advertising); 69862306a36Sopenharmony_ci fw_caps_to_lmm(pi->port_type, pi->link_cfg.lpacaps, 69962306a36Sopenharmony_ci link_ksettings->link_modes.lp_advertising); 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci base->speed = (netif_carrier_ok(dev) 70262306a36Sopenharmony_ci ? pi->link_cfg.speed 70362306a36Sopenharmony_ci : SPEED_UNKNOWN); 70462306a36Sopenharmony_ci base->duplex = DUPLEX_FULL; 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci base->autoneg = pi->link_cfg.autoneg; 70762306a36Sopenharmony_ci if (pi->link_cfg.pcaps & FW_PORT_CAP32_ANEG) 70862306a36Sopenharmony_ci ethtool_link_ksettings_add_link_mode(link_ksettings, 70962306a36Sopenharmony_ci supported, Autoneg); 71062306a36Sopenharmony_ci if (pi->link_cfg.autoneg) 71162306a36Sopenharmony_ci ethtool_link_ksettings_add_link_mode(link_ksettings, 71262306a36Sopenharmony_ci advertising, Autoneg); 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci return 0; 71562306a36Sopenharmony_ci} 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_cistatic int set_link_ksettings(struct net_device *dev, 71862306a36Sopenharmony_ci const struct ethtool_link_ksettings *link_ksettings) 71962306a36Sopenharmony_ci{ 72062306a36Sopenharmony_ci struct port_info *pi = netdev_priv(dev); 72162306a36Sopenharmony_ci struct link_config *lc = &pi->link_cfg; 72262306a36Sopenharmony_ci const struct ethtool_link_settings *base = &link_ksettings->base; 72362306a36Sopenharmony_ci struct link_config old_lc; 72462306a36Sopenharmony_ci unsigned int fw_caps; 72562306a36Sopenharmony_ci int ret = 0; 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci /* only full-duplex supported */ 72862306a36Sopenharmony_ci if (base->duplex != DUPLEX_FULL) 72962306a36Sopenharmony_ci return -EINVAL; 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci old_lc = *lc; 73262306a36Sopenharmony_ci if (!(lc->pcaps & FW_PORT_CAP32_ANEG) || 73362306a36Sopenharmony_ci base->autoneg == AUTONEG_DISABLE) { 73462306a36Sopenharmony_ci fw_caps = speed_to_fw_caps(base->speed); 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci /* Speed must be supported by Physical Port Capabilities. */ 73762306a36Sopenharmony_ci if (!(lc->pcaps & fw_caps)) 73862306a36Sopenharmony_ci return -EINVAL; 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci lc->speed_caps = fw_caps; 74162306a36Sopenharmony_ci lc->acaps = fw_caps; 74262306a36Sopenharmony_ci } else { 74362306a36Sopenharmony_ci fw_caps = 74462306a36Sopenharmony_ci lmm_to_fw_caps(link_ksettings->link_modes.advertising); 74562306a36Sopenharmony_ci if (!(lc->pcaps & fw_caps)) 74662306a36Sopenharmony_ci return -EINVAL; 74762306a36Sopenharmony_ci lc->speed_caps = 0; 74862306a36Sopenharmony_ci lc->acaps = fw_caps | FW_PORT_CAP32_ANEG; 74962306a36Sopenharmony_ci } 75062306a36Sopenharmony_ci lc->autoneg = base->autoneg; 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci /* If the firmware rejects the Link Configuration request, back out 75362306a36Sopenharmony_ci * the changes and report the error. 75462306a36Sopenharmony_ci */ 75562306a36Sopenharmony_ci ret = t4_link_l1cfg(pi->adapter, pi->adapter->mbox, pi->tx_chan, lc); 75662306a36Sopenharmony_ci if (ret) 75762306a36Sopenharmony_ci *lc = old_lc; 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci return ret; 76062306a36Sopenharmony_ci} 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci/* Translate the Firmware FEC value into the ethtool value. */ 76362306a36Sopenharmony_cistatic inline unsigned int fwcap_to_eth_fec(unsigned int fw_fec) 76462306a36Sopenharmony_ci{ 76562306a36Sopenharmony_ci unsigned int eth_fec = 0; 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci if (fw_fec & FW_PORT_CAP32_FEC_RS) 76862306a36Sopenharmony_ci eth_fec |= ETHTOOL_FEC_RS; 76962306a36Sopenharmony_ci if (fw_fec & FW_PORT_CAP32_FEC_BASER_RS) 77062306a36Sopenharmony_ci eth_fec |= ETHTOOL_FEC_BASER; 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci /* if nothing is set, then FEC is off */ 77362306a36Sopenharmony_ci if (!eth_fec) 77462306a36Sopenharmony_ci eth_fec = ETHTOOL_FEC_OFF; 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci return eth_fec; 77762306a36Sopenharmony_ci} 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci/* Translate Common Code FEC value into ethtool value. */ 78062306a36Sopenharmony_cistatic inline unsigned int cc_to_eth_fec(unsigned int cc_fec) 78162306a36Sopenharmony_ci{ 78262306a36Sopenharmony_ci unsigned int eth_fec = 0; 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci if (cc_fec & FEC_AUTO) 78562306a36Sopenharmony_ci eth_fec |= ETHTOOL_FEC_AUTO; 78662306a36Sopenharmony_ci if (cc_fec & FEC_RS) 78762306a36Sopenharmony_ci eth_fec |= ETHTOOL_FEC_RS; 78862306a36Sopenharmony_ci if (cc_fec & FEC_BASER_RS) 78962306a36Sopenharmony_ci eth_fec |= ETHTOOL_FEC_BASER; 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci /* if nothing is set, then FEC is off */ 79262306a36Sopenharmony_ci if (!eth_fec) 79362306a36Sopenharmony_ci eth_fec = ETHTOOL_FEC_OFF; 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci return eth_fec; 79662306a36Sopenharmony_ci} 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci/* Translate ethtool FEC value into Common Code value. */ 79962306a36Sopenharmony_cistatic inline unsigned int eth_to_cc_fec(unsigned int eth_fec) 80062306a36Sopenharmony_ci{ 80162306a36Sopenharmony_ci unsigned int cc_fec = 0; 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci if (eth_fec & ETHTOOL_FEC_OFF) 80462306a36Sopenharmony_ci return cc_fec; 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci if (eth_fec & ETHTOOL_FEC_AUTO) 80762306a36Sopenharmony_ci cc_fec |= FEC_AUTO; 80862306a36Sopenharmony_ci if (eth_fec & ETHTOOL_FEC_RS) 80962306a36Sopenharmony_ci cc_fec |= FEC_RS; 81062306a36Sopenharmony_ci if (eth_fec & ETHTOOL_FEC_BASER) 81162306a36Sopenharmony_ci cc_fec |= FEC_BASER_RS; 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci return cc_fec; 81462306a36Sopenharmony_ci} 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_cistatic int get_fecparam(struct net_device *dev, struct ethtool_fecparam *fec) 81762306a36Sopenharmony_ci{ 81862306a36Sopenharmony_ci const struct port_info *pi = netdev_priv(dev); 81962306a36Sopenharmony_ci const struct link_config *lc = &pi->link_cfg; 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci /* Translate the Firmware FEC Support into the ethtool value. We 82262306a36Sopenharmony_ci * always support IEEE 802.3 "automatic" selection of Link FEC type if 82362306a36Sopenharmony_ci * any FEC is supported. 82462306a36Sopenharmony_ci */ 82562306a36Sopenharmony_ci fec->fec = fwcap_to_eth_fec(lc->pcaps); 82662306a36Sopenharmony_ci if (fec->fec != ETHTOOL_FEC_OFF) 82762306a36Sopenharmony_ci fec->fec |= ETHTOOL_FEC_AUTO; 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci /* Translate the current internal FEC parameters into the 83062306a36Sopenharmony_ci * ethtool values. 83162306a36Sopenharmony_ci */ 83262306a36Sopenharmony_ci fec->active_fec = cc_to_eth_fec(lc->fec); 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci return 0; 83562306a36Sopenharmony_ci} 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_cistatic int set_fecparam(struct net_device *dev, struct ethtool_fecparam *fec) 83862306a36Sopenharmony_ci{ 83962306a36Sopenharmony_ci struct port_info *pi = netdev_priv(dev); 84062306a36Sopenharmony_ci struct link_config *lc = &pi->link_cfg; 84162306a36Sopenharmony_ci struct link_config old_lc; 84262306a36Sopenharmony_ci int ret; 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci /* Save old Link Configuration in case the L1 Configure below 84562306a36Sopenharmony_ci * fails. 84662306a36Sopenharmony_ci */ 84762306a36Sopenharmony_ci old_lc = *lc; 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci /* Try to perform the L1 Configure and return the result of that 85062306a36Sopenharmony_ci * effort. If it fails, revert the attempted change. 85162306a36Sopenharmony_ci */ 85262306a36Sopenharmony_ci lc->requested_fec = eth_to_cc_fec(fec->fec); 85362306a36Sopenharmony_ci ret = t4_link_l1cfg(pi->adapter, pi->adapter->mbox, 85462306a36Sopenharmony_ci pi->tx_chan, lc); 85562306a36Sopenharmony_ci if (ret) 85662306a36Sopenharmony_ci *lc = old_lc; 85762306a36Sopenharmony_ci return ret; 85862306a36Sopenharmony_ci} 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_cistatic void get_pauseparam(struct net_device *dev, 86162306a36Sopenharmony_ci struct ethtool_pauseparam *epause) 86262306a36Sopenharmony_ci{ 86362306a36Sopenharmony_ci struct port_info *p = netdev_priv(dev); 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci epause->autoneg = (p->link_cfg.requested_fc & PAUSE_AUTONEG) != 0; 86662306a36Sopenharmony_ci epause->rx_pause = (p->link_cfg.advertised_fc & PAUSE_RX) != 0; 86762306a36Sopenharmony_ci epause->tx_pause = (p->link_cfg.advertised_fc & PAUSE_TX) != 0; 86862306a36Sopenharmony_ci} 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_cistatic int set_pauseparam(struct net_device *dev, 87162306a36Sopenharmony_ci struct ethtool_pauseparam *epause) 87262306a36Sopenharmony_ci{ 87362306a36Sopenharmony_ci struct port_info *p = netdev_priv(dev); 87462306a36Sopenharmony_ci struct link_config *lc = &p->link_cfg; 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci if (epause->autoneg == AUTONEG_DISABLE) 87762306a36Sopenharmony_ci lc->requested_fc = 0; 87862306a36Sopenharmony_ci else if (lc->pcaps & FW_PORT_CAP32_ANEG) 87962306a36Sopenharmony_ci lc->requested_fc = PAUSE_AUTONEG; 88062306a36Sopenharmony_ci else 88162306a36Sopenharmony_ci return -EINVAL; 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci if (epause->rx_pause) 88462306a36Sopenharmony_ci lc->requested_fc |= PAUSE_RX; 88562306a36Sopenharmony_ci if (epause->tx_pause) 88662306a36Sopenharmony_ci lc->requested_fc |= PAUSE_TX; 88762306a36Sopenharmony_ci if (netif_running(dev)) 88862306a36Sopenharmony_ci return t4_link_l1cfg(p->adapter, p->adapter->mbox, p->tx_chan, 88962306a36Sopenharmony_ci lc); 89062306a36Sopenharmony_ci return 0; 89162306a36Sopenharmony_ci} 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_cistatic void get_sge_param(struct net_device *dev, struct ethtool_ringparam *e, 89462306a36Sopenharmony_ci struct kernel_ethtool_ringparam *kernel_e, 89562306a36Sopenharmony_ci struct netlink_ext_ack *extack) 89662306a36Sopenharmony_ci{ 89762306a36Sopenharmony_ci const struct port_info *pi = netdev_priv(dev); 89862306a36Sopenharmony_ci const struct sge *s = &pi->adapter->sge; 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci e->rx_max_pending = MAX_RX_BUFFERS; 90162306a36Sopenharmony_ci e->rx_mini_max_pending = MAX_RSPQ_ENTRIES; 90262306a36Sopenharmony_ci e->rx_jumbo_max_pending = 0; 90362306a36Sopenharmony_ci e->tx_max_pending = MAX_TXQ_ENTRIES; 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_ci e->rx_pending = s->ethrxq[pi->first_qset].fl.size - 8; 90662306a36Sopenharmony_ci e->rx_mini_pending = s->ethrxq[pi->first_qset].rspq.size; 90762306a36Sopenharmony_ci e->rx_jumbo_pending = 0; 90862306a36Sopenharmony_ci e->tx_pending = s->ethtxq[pi->first_qset].q.size; 90962306a36Sopenharmony_ci} 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_cistatic int set_sge_param(struct net_device *dev, struct ethtool_ringparam *e, 91262306a36Sopenharmony_ci struct kernel_ethtool_ringparam *kernel_e, 91362306a36Sopenharmony_ci struct netlink_ext_ack *extack) 91462306a36Sopenharmony_ci{ 91562306a36Sopenharmony_ci int i; 91662306a36Sopenharmony_ci const struct port_info *pi = netdev_priv(dev); 91762306a36Sopenharmony_ci struct adapter *adapter = pi->adapter; 91862306a36Sopenharmony_ci struct sge *s = &adapter->sge; 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci if (e->rx_pending > MAX_RX_BUFFERS || e->rx_jumbo_pending || 92162306a36Sopenharmony_ci e->tx_pending > MAX_TXQ_ENTRIES || 92262306a36Sopenharmony_ci e->rx_mini_pending > MAX_RSPQ_ENTRIES || 92362306a36Sopenharmony_ci e->rx_mini_pending < MIN_RSPQ_ENTRIES || 92462306a36Sopenharmony_ci e->rx_pending < MIN_FL_ENTRIES || e->tx_pending < MIN_TXQ_ENTRIES) 92562306a36Sopenharmony_ci return -EINVAL; 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci if (adapter->flags & CXGB4_FULL_INIT_DONE) 92862306a36Sopenharmony_ci return -EBUSY; 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci for (i = 0; i < pi->nqsets; ++i) { 93162306a36Sopenharmony_ci s->ethtxq[pi->first_qset + i].q.size = e->tx_pending; 93262306a36Sopenharmony_ci s->ethrxq[pi->first_qset + i].fl.size = e->rx_pending + 8; 93362306a36Sopenharmony_ci s->ethrxq[pi->first_qset + i].rspq.size = e->rx_mini_pending; 93462306a36Sopenharmony_ci } 93562306a36Sopenharmony_ci return 0; 93662306a36Sopenharmony_ci} 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci/** 93962306a36Sopenharmony_ci * set_rx_intr_params - set a net devices's RX interrupt holdoff paramete! 94062306a36Sopenharmony_ci * @dev: the network device 94162306a36Sopenharmony_ci * @us: the hold-off time in us, or 0 to disable timer 94262306a36Sopenharmony_ci * @cnt: the hold-off packet count, or 0 to disable counter 94362306a36Sopenharmony_ci * 94462306a36Sopenharmony_ci * Set the RX interrupt hold-off parameters for a network device. 94562306a36Sopenharmony_ci */ 94662306a36Sopenharmony_cistatic int set_rx_intr_params(struct net_device *dev, 94762306a36Sopenharmony_ci unsigned int us, unsigned int cnt) 94862306a36Sopenharmony_ci{ 94962306a36Sopenharmony_ci int i, err; 95062306a36Sopenharmony_ci struct port_info *pi = netdev_priv(dev); 95162306a36Sopenharmony_ci struct adapter *adap = pi->adapter; 95262306a36Sopenharmony_ci struct sge_eth_rxq *q = &adap->sge.ethrxq[pi->first_qset]; 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci for (i = 0; i < pi->nqsets; i++, q++) { 95562306a36Sopenharmony_ci err = cxgb4_set_rspq_intr_params(&q->rspq, us, cnt); 95662306a36Sopenharmony_ci if (err) 95762306a36Sopenharmony_ci return err; 95862306a36Sopenharmony_ci } 95962306a36Sopenharmony_ci return 0; 96062306a36Sopenharmony_ci} 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_cistatic int set_adaptive_rx_setting(struct net_device *dev, int adaptive_rx) 96362306a36Sopenharmony_ci{ 96462306a36Sopenharmony_ci int i; 96562306a36Sopenharmony_ci struct port_info *pi = netdev_priv(dev); 96662306a36Sopenharmony_ci struct adapter *adap = pi->adapter; 96762306a36Sopenharmony_ci struct sge_eth_rxq *q = &adap->sge.ethrxq[pi->first_qset]; 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci for (i = 0; i < pi->nqsets; i++, q++) 97062306a36Sopenharmony_ci q->rspq.adaptive_rx = adaptive_rx; 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_ci return 0; 97362306a36Sopenharmony_ci} 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_cistatic int get_adaptive_rx_setting(struct net_device *dev) 97662306a36Sopenharmony_ci{ 97762306a36Sopenharmony_ci struct port_info *pi = netdev_priv(dev); 97862306a36Sopenharmony_ci struct adapter *adap = pi->adapter; 97962306a36Sopenharmony_ci struct sge_eth_rxq *q = &adap->sge.ethrxq[pi->first_qset]; 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_ci return q->rspq.adaptive_rx; 98262306a36Sopenharmony_ci} 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_ci/* Return the current global Adapter SGE Doorbell Queue Timer Tick for all 98562306a36Sopenharmony_ci * Ethernet TX Queues. 98662306a36Sopenharmony_ci */ 98762306a36Sopenharmony_cistatic int get_dbqtimer_tick(struct net_device *dev) 98862306a36Sopenharmony_ci{ 98962306a36Sopenharmony_ci struct port_info *pi = netdev_priv(dev); 99062306a36Sopenharmony_ci struct adapter *adap = pi->adapter; 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_ci if (!(adap->flags & CXGB4_SGE_DBQ_TIMER)) 99362306a36Sopenharmony_ci return 0; 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_ci return adap->sge.dbqtimer_tick; 99662306a36Sopenharmony_ci} 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci/* Return the SGE Doorbell Queue Timer Value for the Ethernet TX Queues 99962306a36Sopenharmony_ci * associated with a Network Device. 100062306a36Sopenharmony_ci */ 100162306a36Sopenharmony_cistatic int get_dbqtimer(struct net_device *dev) 100262306a36Sopenharmony_ci{ 100362306a36Sopenharmony_ci struct port_info *pi = netdev_priv(dev); 100462306a36Sopenharmony_ci struct adapter *adap = pi->adapter; 100562306a36Sopenharmony_ci struct sge_eth_txq *txq; 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ci txq = &adap->sge.ethtxq[pi->first_qset]; 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci if (!(adap->flags & CXGB4_SGE_DBQ_TIMER)) 101062306a36Sopenharmony_ci return 0; 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_ci /* all of the TX Queues use the same Timer Index */ 101362306a36Sopenharmony_ci return adap->sge.dbqtimer_val[txq->dbqtimerix]; 101462306a36Sopenharmony_ci} 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_ci/* Set the global Adapter SGE Doorbell Queue Timer Tick for all Ethernet TX 101762306a36Sopenharmony_ci * Queues. This is the fundamental "Tick" that sets the scale of values which 101862306a36Sopenharmony_ci * can be used. Individual Ethernet TX Queues index into a relatively small 101962306a36Sopenharmony_ci * array of Tick Multipliers. Changing the base Tick will thus change all of 102062306a36Sopenharmony_ci * the resulting Timer Values associated with those multipliers for all 102162306a36Sopenharmony_ci * Ethernet TX Queues. 102262306a36Sopenharmony_ci */ 102362306a36Sopenharmony_cistatic int set_dbqtimer_tick(struct net_device *dev, int usecs) 102462306a36Sopenharmony_ci{ 102562306a36Sopenharmony_ci struct port_info *pi = netdev_priv(dev); 102662306a36Sopenharmony_ci struct adapter *adap = pi->adapter; 102762306a36Sopenharmony_ci struct sge *s = &adap->sge; 102862306a36Sopenharmony_ci u32 param, val; 102962306a36Sopenharmony_ci int ret; 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_ci if (!(adap->flags & CXGB4_SGE_DBQ_TIMER)) 103262306a36Sopenharmony_ci return 0; 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ci /* return early if it's the same Timer Tick we're already using */ 103562306a36Sopenharmony_ci if (s->dbqtimer_tick == usecs) 103662306a36Sopenharmony_ci return 0; 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ci /* attempt to set the new Timer Tick value */ 103962306a36Sopenharmony_ci param = (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_DEV) | 104062306a36Sopenharmony_ci FW_PARAMS_PARAM_X_V(FW_PARAMS_PARAM_DEV_DBQ_TIMERTICK)); 104162306a36Sopenharmony_ci val = usecs; 104262306a36Sopenharmony_ci ret = t4_set_params(adap, adap->mbox, adap->pf, 0, 1, ¶m, &val); 104362306a36Sopenharmony_ci if (ret) 104462306a36Sopenharmony_ci return ret; 104562306a36Sopenharmony_ci s->dbqtimer_tick = usecs; 104662306a36Sopenharmony_ci 104762306a36Sopenharmony_ci /* if successful, reread resulting dependent Timer values */ 104862306a36Sopenharmony_ci ret = t4_read_sge_dbqtimers(adap, ARRAY_SIZE(s->dbqtimer_val), 104962306a36Sopenharmony_ci s->dbqtimer_val); 105062306a36Sopenharmony_ci return ret; 105162306a36Sopenharmony_ci} 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_ci/* Set the SGE Doorbell Queue Timer Value for the Ethernet TX Queues 105462306a36Sopenharmony_ci * associated with a Network Device. There is a relatively small array of 105562306a36Sopenharmony_ci * possible Timer Values so we need to pick the closest value available. 105662306a36Sopenharmony_ci */ 105762306a36Sopenharmony_cistatic int set_dbqtimer(struct net_device *dev, int usecs) 105862306a36Sopenharmony_ci{ 105962306a36Sopenharmony_ci int qix, timerix, min_timerix, delta, min_delta; 106062306a36Sopenharmony_ci struct port_info *pi = netdev_priv(dev); 106162306a36Sopenharmony_ci struct adapter *adap = pi->adapter; 106262306a36Sopenharmony_ci struct sge *s = &adap->sge; 106362306a36Sopenharmony_ci struct sge_eth_txq *txq; 106462306a36Sopenharmony_ci u32 param, val; 106562306a36Sopenharmony_ci int ret; 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_ci if (!(adap->flags & CXGB4_SGE_DBQ_TIMER)) 106862306a36Sopenharmony_ci return 0; 106962306a36Sopenharmony_ci 107062306a36Sopenharmony_ci /* Find the SGE Doorbell Timer Value that's closest to the requested 107162306a36Sopenharmony_ci * value. 107262306a36Sopenharmony_ci */ 107362306a36Sopenharmony_ci min_delta = INT_MAX; 107462306a36Sopenharmony_ci min_timerix = 0; 107562306a36Sopenharmony_ci for (timerix = 0; timerix < ARRAY_SIZE(s->dbqtimer_val); timerix++) { 107662306a36Sopenharmony_ci delta = s->dbqtimer_val[timerix] - usecs; 107762306a36Sopenharmony_ci if (delta < 0) 107862306a36Sopenharmony_ci delta = -delta; 107962306a36Sopenharmony_ci if (delta < min_delta) { 108062306a36Sopenharmony_ci min_delta = delta; 108162306a36Sopenharmony_ci min_timerix = timerix; 108262306a36Sopenharmony_ci } 108362306a36Sopenharmony_ci } 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci /* Return early if it's the same Timer Index we're already using. 108662306a36Sopenharmony_ci * We use the same Timer Index for all of the TX Queues for an 108762306a36Sopenharmony_ci * interface so it's only necessary to check the first one. 108862306a36Sopenharmony_ci */ 108962306a36Sopenharmony_ci txq = &s->ethtxq[pi->first_qset]; 109062306a36Sopenharmony_ci if (txq->dbqtimerix == min_timerix) 109162306a36Sopenharmony_ci return 0; 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ci for (qix = 0; qix < pi->nqsets; qix++, txq++) { 109462306a36Sopenharmony_ci if (adap->flags & CXGB4_FULL_INIT_DONE) { 109562306a36Sopenharmony_ci param = 109662306a36Sopenharmony_ci (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_DMAQ) | 109762306a36Sopenharmony_ci FW_PARAMS_PARAM_X_V(FW_PARAMS_PARAM_DMAQ_EQ_TIMERIX) | 109862306a36Sopenharmony_ci FW_PARAMS_PARAM_YZ_V(txq->q.cntxt_id)); 109962306a36Sopenharmony_ci val = min_timerix; 110062306a36Sopenharmony_ci ret = t4_set_params(adap, adap->mbox, adap->pf, 0, 110162306a36Sopenharmony_ci 1, ¶m, &val); 110262306a36Sopenharmony_ci if (ret) 110362306a36Sopenharmony_ci return ret; 110462306a36Sopenharmony_ci } 110562306a36Sopenharmony_ci txq->dbqtimerix = min_timerix; 110662306a36Sopenharmony_ci } 110762306a36Sopenharmony_ci return 0; 110862306a36Sopenharmony_ci} 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_ci/* Set the global Adapter SGE Doorbell Queue Timer Tick for all Ethernet TX 111162306a36Sopenharmony_ci * Queues and the Timer Value for the Ethernet TX Queues associated with a 111262306a36Sopenharmony_ci * Network Device. Since changing the global Tick changes all of the 111362306a36Sopenharmony_ci * available Timer Values, we need to do this first before selecting the 111462306a36Sopenharmony_ci * resulting closest Timer Value. Moreover, since the Tick is global, 111562306a36Sopenharmony_ci * changing it affects the Timer Values for all Network Devices on the 111662306a36Sopenharmony_ci * adapter. So, before changing the Tick, we grab all of the current Timer 111762306a36Sopenharmony_ci * Values for other Network Devices on this Adapter and then attempt to select 111862306a36Sopenharmony_ci * new Timer Values which are close to the old values ... 111962306a36Sopenharmony_ci */ 112062306a36Sopenharmony_cistatic int set_dbqtimer_tickval(struct net_device *dev, 112162306a36Sopenharmony_ci int tick_usecs, int timer_usecs) 112262306a36Sopenharmony_ci{ 112362306a36Sopenharmony_ci struct port_info *pi = netdev_priv(dev); 112462306a36Sopenharmony_ci struct adapter *adap = pi->adapter; 112562306a36Sopenharmony_ci int timer[MAX_NPORTS]; 112662306a36Sopenharmony_ci unsigned int port; 112762306a36Sopenharmony_ci int ret; 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_ci /* Grab the other adapter Network Interface current timers and fill in 113062306a36Sopenharmony_ci * the new one for this Network Interface. 113162306a36Sopenharmony_ci */ 113262306a36Sopenharmony_ci for_each_port(adap, port) 113362306a36Sopenharmony_ci if (port == pi->port_id) 113462306a36Sopenharmony_ci timer[port] = timer_usecs; 113562306a36Sopenharmony_ci else 113662306a36Sopenharmony_ci timer[port] = get_dbqtimer(adap->port[port]); 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_ci /* Change the global Tick first ... */ 113962306a36Sopenharmony_ci ret = set_dbqtimer_tick(dev, tick_usecs); 114062306a36Sopenharmony_ci if (ret) 114162306a36Sopenharmony_ci return ret; 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ci /* ... and then set all of the Network Interface Timer Values ... */ 114462306a36Sopenharmony_ci for_each_port(adap, port) { 114562306a36Sopenharmony_ci ret = set_dbqtimer(adap->port[port], timer[port]); 114662306a36Sopenharmony_ci if (ret) 114762306a36Sopenharmony_ci return ret; 114862306a36Sopenharmony_ci } 114962306a36Sopenharmony_ci 115062306a36Sopenharmony_ci return 0; 115162306a36Sopenharmony_ci} 115262306a36Sopenharmony_ci 115362306a36Sopenharmony_cistatic int set_coalesce(struct net_device *dev, 115462306a36Sopenharmony_ci struct ethtool_coalesce *coalesce, 115562306a36Sopenharmony_ci struct kernel_ethtool_coalesce *kernel_coal, 115662306a36Sopenharmony_ci struct netlink_ext_ack *extack) 115762306a36Sopenharmony_ci{ 115862306a36Sopenharmony_ci int ret; 115962306a36Sopenharmony_ci 116062306a36Sopenharmony_ci set_adaptive_rx_setting(dev, coalesce->use_adaptive_rx_coalesce); 116162306a36Sopenharmony_ci 116262306a36Sopenharmony_ci ret = set_rx_intr_params(dev, coalesce->rx_coalesce_usecs, 116362306a36Sopenharmony_ci coalesce->rx_max_coalesced_frames); 116462306a36Sopenharmony_ci if (ret) 116562306a36Sopenharmony_ci return ret; 116662306a36Sopenharmony_ci 116762306a36Sopenharmony_ci return set_dbqtimer_tickval(dev, 116862306a36Sopenharmony_ci coalesce->tx_coalesce_usecs_irq, 116962306a36Sopenharmony_ci coalesce->tx_coalesce_usecs); 117062306a36Sopenharmony_ci} 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_cistatic int get_coalesce(struct net_device *dev, struct ethtool_coalesce *c, 117362306a36Sopenharmony_ci struct kernel_ethtool_coalesce *kernel_coal, 117462306a36Sopenharmony_ci struct netlink_ext_ack *extack) 117562306a36Sopenharmony_ci{ 117662306a36Sopenharmony_ci const struct port_info *pi = netdev_priv(dev); 117762306a36Sopenharmony_ci const struct adapter *adap = pi->adapter; 117862306a36Sopenharmony_ci const struct sge_rspq *rq = &adap->sge.ethrxq[pi->first_qset].rspq; 117962306a36Sopenharmony_ci 118062306a36Sopenharmony_ci c->rx_coalesce_usecs = qtimer_val(adap, rq); 118162306a36Sopenharmony_ci c->rx_max_coalesced_frames = (rq->intr_params & QINTR_CNT_EN_F) ? 118262306a36Sopenharmony_ci adap->sge.counter_val[rq->pktcnt_idx] : 0; 118362306a36Sopenharmony_ci c->use_adaptive_rx_coalesce = get_adaptive_rx_setting(dev); 118462306a36Sopenharmony_ci c->tx_coalesce_usecs_irq = get_dbqtimer_tick(dev); 118562306a36Sopenharmony_ci c->tx_coalesce_usecs = get_dbqtimer(dev); 118662306a36Sopenharmony_ci return 0; 118762306a36Sopenharmony_ci} 118862306a36Sopenharmony_ci 118962306a36Sopenharmony_ci/* The next two routines implement eeprom read/write from physical addresses. 119062306a36Sopenharmony_ci */ 119162306a36Sopenharmony_cistatic int eeprom_rd_phys(struct adapter *adap, unsigned int phys_addr, u32 *v) 119262306a36Sopenharmony_ci{ 119362306a36Sopenharmony_ci int vaddr = t4_eeprom_ptov(phys_addr, adap->pf, EEPROMPFSIZE); 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_ci if (vaddr >= 0) 119662306a36Sopenharmony_ci vaddr = pci_read_vpd(adap->pdev, vaddr, sizeof(u32), v); 119762306a36Sopenharmony_ci return vaddr < 0 ? vaddr : 0; 119862306a36Sopenharmony_ci} 119962306a36Sopenharmony_ci 120062306a36Sopenharmony_cistatic int eeprom_wr_phys(struct adapter *adap, unsigned int phys_addr, u32 v) 120162306a36Sopenharmony_ci{ 120262306a36Sopenharmony_ci int vaddr = t4_eeprom_ptov(phys_addr, adap->pf, EEPROMPFSIZE); 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_ci if (vaddr >= 0) 120562306a36Sopenharmony_ci vaddr = pci_write_vpd(adap->pdev, vaddr, sizeof(u32), &v); 120662306a36Sopenharmony_ci return vaddr < 0 ? vaddr : 0; 120762306a36Sopenharmony_ci} 120862306a36Sopenharmony_ci 120962306a36Sopenharmony_ci#define EEPROM_MAGIC 0x38E2F10C 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_cistatic int get_eeprom(struct net_device *dev, struct ethtool_eeprom *e, 121262306a36Sopenharmony_ci u8 *data) 121362306a36Sopenharmony_ci{ 121462306a36Sopenharmony_ci int i, err = 0; 121562306a36Sopenharmony_ci struct adapter *adapter = netdev2adap(dev); 121662306a36Sopenharmony_ci u8 *buf = kvzalloc(EEPROMSIZE, GFP_KERNEL); 121762306a36Sopenharmony_ci 121862306a36Sopenharmony_ci if (!buf) 121962306a36Sopenharmony_ci return -ENOMEM; 122062306a36Sopenharmony_ci 122162306a36Sopenharmony_ci e->magic = EEPROM_MAGIC; 122262306a36Sopenharmony_ci for (i = e->offset & ~3; !err && i < e->offset + e->len; i += 4) 122362306a36Sopenharmony_ci err = eeprom_rd_phys(adapter, i, (u32 *)&buf[i]); 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_ci if (!err) 122662306a36Sopenharmony_ci memcpy(data, buf + e->offset, e->len); 122762306a36Sopenharmony_ci kvfree(buf); 122862306a36Sopenharmony_ci return err; 122962306a36Sopenharmony_ci} 123062306a36Sopenharmony_ci 123162306a36Sopenharmony_cistatic int set_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom, 123262306a36Sopenharmony_ci u8 *data) 123362306a36Sopenharmony_ci{ 123462306a36Sopenharmony_ci u8 *buf; 123562306a36Sopenharmony_ci int err = 0; 123662306a36Sopenharmony_ci u32 aligned_offset, aligned_len, *p; 123762306a36Sopenharmony_ci struct adapter *adapter = netdev2adap(dev); 123862306a36Sopenharmony_ci 123962306a36Sopenharmony_ci if (eeprom->magic != EEPROM_MAGIC) 124062306a36Sopenharmony_ci return -EINVAL; 124162306a36Sopenharmony_ci 124262306a36Sopenharmony_ci aligned_offset = eeprom->offset & ~3; 124362306a36Sopenharmony_ci aligned_len = (eeprom->len + (eeprom->offset & 3) + 3) & ~3; 124462306a36Sopenharmony_ci 124562306a36Sopenharmony_ci if (adapter->pf > 0) { 124662306a36Sopenharmony_ci u32 start = 1024 + adapter->pf * EEPROMPFSIZE; 124762306a36Sopenharmony_ci 124862306a36Sopenharmony_ci if (aligned_offset < start || 124962306a36Sopenharmony_ci aligned_offset + aligned_len > start + EEPROMPFSIZE) 125062306a36Sopenharmony_ci return -EPERM; 125162306a36Sopenharmony_ci } 125262306a36Sopenharmony_ci 125362306a36Sopenharmony_ci if (aligned_offset != eeprom->offset || aligned_len != eeprom->len) { 125462306a36Sopenharmony_ci /* RMW possibly needed for first or last words. 125562306a36Sopenharmony_ci */ 125662306a36Sopenharmony_ci buf = kvzalloc(aligned_len, GFP_KERNEL); 125762306a36Sopenharmony_ci if (!buf) 125862306a36Sopenharmony_ci return -ENOMEM; 125962306a36Sopenharmony_ci err = eeprom_rd_phys(adapter, aligned_offset, (u32 *)buf); 126062306a36Sopenharmony_ci if (!err && aligned_len > 4) 126162306a36Sopenharmony_ci err = eeprom_rd_phys(adapter, 126262306a36Sopenharmony_ci aligned_offset + aligned_len - 4, 126362306a36Sopenharmony_ci (u32 *)&buf[aligned_len - 4]); 126462306a36Sopenharmony_ci if (err) 126562306a36Sopenharmony_ci goto out; 126662306a36Sopenharmony_ci memcpy(buf + (eeprom->offset & 3), data, eeprom->len); 126762306a36Sopenharmony_ci } else { 126862306a36Sopenharmony_ci buf = data; 126962306a36Sopenharmony_ci } 127062306a36Sopenharmony_ci 127162306a36Sopenharmony_ci err = t4_seeprom_wp(adapter, false); 127262306a36Sopenharmony_ci if (err) 127362306a36Sopenharmony_ci goto out; 127462306a36Sopenharmony_ci 127562306a36Sopenharmony_ci for (p = (u32 *)buf; !err && aligned_len; aligned_len -= 4, p++) { 127662306a36Sopenharmony_ci err = eeprom_wr_phys(adapter, aligned_offset, *p); 127762306a36Sopenharmony_ci aligned_offset += 4; 127862306a36Sopenharmony_ci } 127962306a36Sopenharmony_ci 128062306a36Sopenharmony_ci if (!err) 128162306a36Sopenharmony_ci err = t4_seeprom_wp(adapter, true); 128262306a36Sopenharmony_ciout: 128362306a36Sopenharmony_ci if (buf != data) 128462306a36Sopenharmony_ci kvfree(buf); 128562306a36Sopenharmony_ci return err; 128662306a36Sopenharmony_ci} 128762306a36Sopenharmony_ci 128862306a36Sopenharmony_cistatic int cxgb4_ethtool_flash_bootcfg(struct net_device *netdev, 128962306a36Sopenharmony_ci const u8 *data, u32 size) 129062306a36Sopenharmony_ci{ 129162306a36Sopenharmony_ci struct adapter *adap = netdev2adap(netdev); 129262306a36Sopenharmony_ci int ret; 129362306a36Sopenharmony_ci 129462306a36Sopenharmony_ci ret = t4_load_bootcfg(adap, data, size); 129562306a36Sopenharmony_ci if (ret) 129662306a36Sopenharmony_ci dev_err(adap->pdev_dev, "Failed to load boot cfg image\n"); 129762306a36Sopenharmony_ci 129862306a36Sopenharmony_ci return ret; 129962306a36Sopenharmony_ci} 130062306a36Sopenharmony_ci 130162306a36Sopenharmony_cistatic int cxgb4_ethtool_flash_boot(struct net_device *netdev, 130262306a36Sopenharmony_ci const u8 *bdata, u32 size) 130362306a36Sopenharmony_ci{ 130462306a36Sopenharmony_ci struct adapter *adap = netdev2adap(netdev); 130562306a36Sopenharmony_ci unsigned int offset; 130662306a36Sopenharmony_ci u8 *data; 130762306a36Sopenharmony_ci int ret; 130862306a36Sopenharmony_ci 130962306a36Sopenharmony_ci data = kmemdup(bdata, size, GFP_KERNEL); 131062306a36Sopenharmony_ci if (!data) 131162306a36Sopenharmony_ci return -ENOMEM; 131262306a36Sopenharmony_ci 131362306a36Sopenharmony_ci offset = OFFSET_G(t4_read_reg(adap, PF_REG(0, PCIE_PF_EXPROM_OFST_A))); 131462306a36Sopenharmony_ci 131562306a36Sopenharmony_ci ret = t4_load_boot(adap, data, offset, size); 131662306a36Sopenharmony_ci if (ret) 131762306a36Sopenharmony_ci dev_err(adap->pdev_dev, "Failed to load boot image\n"); 131862306a36Sopenharmony_ci 131962306a36Sopenharmony_ci kfree(data); 132062306a36Sopenharmony_ci return ret; 132162306a36Sopenharmony_ci} 132262306a36Sopenharmony_ci 132362306a36Sopenharmony_ci#define CXGB4_PHY_SIG 0x130000ea 132462306a36Sopenharmony_ci 132562306a36Sopenharmony_cistatic int cxgb4_validate_phy_image(const u8 *data, u32 *size) 132662306a36Sopenharmony_ci{ 132762306a36Sopenharmony_ci struct cxgb4_fw_data *header; 132862306a36Sopenharmony_ci 132962306a36Sopenharmony_ci header = (struct cxgb4_fw_data *)data; 133062306a36Sopenharmony_ci if (be32_to_cpu(header->signature) != CXGB4_PHY_SIG) 133162306a36Sopenharmony_ci return -EINVAL; 133262306a36Sopenharmony_ci 133362306a36Sopenharmony_ci return 0; 133462306a36Sopenharmony_ci} 133562306a36Sopenharmony_ci 133662306a36Sopenharmony_cistatic int cxgb4_ethtool_flash_phy(struct net_device *netdev, 133762306a36Sopenharmony_ci const u8 *data, u32 size) 133862306a36Sopenharmony_ci{ 133962306a36Sopenharmony_ci struct adapter *adap = netdev2adap(netdev); 134062306a36Sopenharmony_ci int ret; 134162306a36Sopenharmony_ci 134262306a36Sopenharmony_ci ret = cxgb4_validate_phy_image(data, NULL); 134362306a36Sopenharmony_ci if (ret) { 134462306a36Sopenharmony_ci dev_err(adap->pdev_dev, "PHY signature mismatch\n"); 134562306a36Sopenharmony_ci return ret; 134662306a36Sopenharmony_ci } 134762306a36Sopenharmony_ci 134862306a36Sopenharmony_ci /* We have to RESET the chip/firmware because we need the 134962306a36Sopenharmony_ci * chip in uninitialized state for loading new PHY image. 135062306a36Sopenharmony_ci * Otherwise, the running firmware will only store the PHY 135162306a36Sopenharmony_ci * image in local RAM which will be lost after next reset. 135262306a36Sopenharmony_ci */ 135362306a36Sopenharmony_ci ret = t4_fw_reset(adap, adap->mbox, PIORSTMODE_F | PIORST_F); 135462306a36Sopenharmony_ci if (ret < 0) { 135562306a36Sopenharmony_ci dev_err(adap->pdev_dev, 135662306a36Sopenharmony_ci "Set FW to RESET for flashing PHY FW failed. ret: %d\n", 135762306a36Sopenharmony_ci ret); 135862306a36Sopenharmony_ci return ret; 135962306a36Sopenharmony_ci } 136062306a36Sopenharmony_ci 136162306a36Sopenharmony_ci ret = t4_load_phy_fw(adap, MEMWIN_NIC, NULL, data, size); 136262306a36Sopenharmony_ci if (ret < 0) { 136362306a36Sopenharmony_ci dev_err(adap->pdev_dev, "Failed to load PHY FW. ret: %d\n", 136462306a36Sopenharmony_ci ret); 136562306a36Sopenharmony_ci return ret; 136662306a36Sopenharmony_ci } 136762306a36Sopenharmony_ci 136862306a36Sopenharmony_ci return 0; 136962306a36Sopenharmony_ci} 137062306a36Sopenharmony_ci 137162306a36Sopenharmony_cistatic int cxgb4_ethtool_flash_fw(struct net_device *netdev, 137262306a36Sopenharmony_ci const u8 *data, u32 size) 137362306a36Sopenharmony_ci{ 137462306a36Sopenharmony_ci struct adapter *adap = netdev2adap(netdev); 137562306a36Sopenharmony_ci unsigned int mbox = PCIE_FW_MASTER_M + 1; 137662306a36Sopenharmony_ci int ret; 137762306a36Sopenharmony_ci 137862306a36Sopenharmony_ci /* If the adapter has been fully initialized then we'll go ahead and 137962306a36Sopenharmony_ci * try to get the firmware's cooperation in upgrading to the new 138062306a36Sopenharmony_ci * firmware image otherwise we'll try to do the entire job from the 138162306a36Sopenharmony_ci * host ... and we always "force" the operation in this path. 138262306a36Sopenharmony_ci */ 138362306a36Sopenharmony_ci if (adap->flags & CXGB4_FULL_INIT_DONE) 138462306a36Sopenharmony_ci mbox = adap->mbox; 138562306a36Sopenharmony_ci 138662306a36Sopenharmony_ci ret = t4_fw_upgrade(adap, mbox, data, size, 1); 138762306a36Sopenharmony_ci if (ret) 138862306a36Sopenharmony_ci dev_err(adap->pdev_dev, 138962306a36Sopenharmony_ci "Failed to flash firmware\n"); 139062306a36Sopenharmony_ci 139162306a36Sopenharmony_ci return ret; 139262306a36Sopenharmony_ci} 139362306a36Sopenharmony_ci 139462306a36Sopenharmony_cistatic int cxgb4_ethtool_flash_region(struct net_device *netdev, 139562306a36Sopenharmony_ci const u8 *data, u32 size, u32 region) 139662306a36Sopenharmony_ci{ 139762306a36Sopenharmony_ci struct adapter *adap = netdev2adap(netdev); 139862306a36Sopenharmony_ci int ret; 139962306a36Sopenharmony_ci 140062306a36Sopenharmony_ci switch (region) { 140162306a36Sopenharmony_ci case CXGB4_ETHTOOL_FLASH_FW: 140262306a36Sopenharmony_ci ret = cxgb4_ethtool_flash_fw(netdev, data, size); 140362306a36Sopenharmony_ci break; 140462306a36Sopenharmony_ci case CXGB4_ETHTOOL_FLASH_PHY: 140562306a36Sopenharmony_ci ret = cxgb4_ethtool_flash_phy(netdev, data, size); 140662306a36Sopenharmony_ci break; 140762306a36Sopenharmony_ci case CXGB4_ETHTOOL_FLASH_BOOT: 140862306a36Sopenharmony_ci ret = cxgb4_ethtool_flash_boot(netdev, data, size); 140962306a36Sopenharmony_ci break; 141062306a36Sopenharmony_ci case CXGB4_ETHTOOL_FLASH_BOOTCFG: 141162306a36Sopenharmony_ci ret = cxgb4_ethtool_flash_bootcfg(netdev, data, size); 141262306a36Sopenharmony_ci break; 141362306a36Sopenharmony_ci default: 141462306a36Sopenharmony_ci ret = -EOPNOTSUPP; 141562306a36Sopenharmony_ci break; 141662306a36Sopenharmony_ci } 141762306a36Sopenharmony_ci 141862306a36Sopenharmony_ci if (!ret) 141962306a36Sopenharmony_ci dev_info(adap->pdev_dev, 142062306a36Sopenharmony_ci "loading %s successful, reload cxgb4 driver\n", 142162306a36Sopenharmony_ci flash_region_strings[region]); 142262306a36Sopenharmony_ci return ret; 142362306a36Sopenharmony_ci} 142462306a36Sopenharmony_ci 142562306a36Sopenharmony_ci#define CXGB4_FW_SIG 0x4368656c 142662306a36Sopenharmony_ci#define CXGB4_FW_SIG_OFFSET 0x160 142762306a36Sopenharmony_ci 142862306a36Sopenharmony_cistatic int cxgb4_validate_fw_image(const u8 *data, u32 *size) 142962306a36Sopenharmony_ci{ 143062306a36Sopenharmony_ci struct cxgb4_fw_data *header; 143162306a36Sopenharmony_ci 143262306a36Sopenharmony_ci header = (struct cxgb4_fw_data *)&data[CXGB4_FW_SIG_OFFSET]; 143362306a36Sopenharmony_ci if (be32_to_cpu(header->signature) != CXGB4_FW_SIG) 143462306a36Sopenharmony_ci return -EINVAL; 143562306a36Sopenharmony_ci 143662306a36Sopenharmony_ci if (size) 143762306a36Sopenharmony_ci *size = be16_to_cpu(((struct fw_hdr *)data)->len512) * 512; 143862306a36Sopenharmony_ci 143962306a36Sopenharmony_ci return 0; 144062306a36Sopenharmony_ci} 144162306a36Sopenharmony_ci 144262306a36Sopenharmony_cistatic int cxgb4_validate_bootcfg_image(const u8 *data, u32 *size) 144362306a36Sopenharmony_ci{ 144462306a36Sopenharmony_ci struct cxgb4_bootcfg_data *header; 144562306a36Sopenharmony_ci 144662306a36Sopenharmony_ci header = (struct cxgb4_bootcfg_data *)data; 144762306a36Sopenharmony_ci if (le16_to_cpu(header->signature) != BOOT_CFG_SIG) 144862306a36Sopenharmony_ci return -EINVAL; 144962306a36Sopenharmony_ci 145062306a36Sopenharmony_ci return 0; 145162306a36Sopenharmony_ci} 145262306a36Sopenharmony_ci 145362306a36Sopenharmony_cistatic int cxgb4_validate_boot_image(const u8 *data, u32 *size) 145462306a36Sopenharmony_ci{ 145562306a36Sopenharmony_ci struct cxgb4_pci_exp_rom_header *exp_header; 145662306a36Sopenharmony_ci struct cxgb4_pcir_data *pcir_header; 145762306a36Sopenharmony_ci struct legacy_pci_rom_hdr *header; 145862306a36Sopenharmony_ci const u8 *cur_header = data; 145962306a36Sopenharmony_ci u16 pcir_offset; 146062306a36Sopenharmony_ci 146162306a36Sopenharmony_ci exp_header = (struct cxgb4_pci_exp_rom_header *)data; 146262306a36Sopenharmony_ci 146362306a36Sopenharmony_ci if (le16_to_cpu(exp_header->signature) != BOOT_SIGNATURE) 146462306a36Sopenharmony_ci return -EINVAL; 146562306a36Sopenharmony_ci 146662306a36Sopenharmony_ci if (size) { 146762306a36Sopenharmony_ci do { 146862306a36Sopenharmony_ci header = (struct legacy_pci_rom_hdr *)cur_header; 146962306a36Sopenharmony_ci pcir_offset = le16_to_cpu(header->pcir_offset); 147062306a36Sopenharmony_ci pcir_header = (struct cxgb4_pcir_data *)(cur_header + 147162306a36Sopenharmony_ci pcir_offset); 147262306a36Sopenharmony_ci 147362306a36Sopenharmony_ci *size += header->size512 * 512; 147462306a36Sopenharmony_ci cur_header += header->size512 * 512; 147562306a36Sopenharmony_ci } while (!(pcir_header->indicator & CXGB4_HDR_INDI)); 147662306a36Sopenharmony_ci } 147762306a36Sopenharmony_ci 147862306a36Sopenharmony_ci return 0; 147962306a36Sopenharmony_ci} 148062306a36Sopenharmony_ci 148162306a36Sopenharmony_cistatic int cxgb4_ethtool_get_flash_region(const u8 *data, u32 *size) 148262306a36Sopenharmony_ci{ 148362306a36Sopenharmony_ci if (!cxgb4_validate_fw_image(data, size)) 148462306a36Sopenharmony_ci return CXGB4_ETHTOOL_FLASH_FW; 148562306a36Sopenharmony_ci if (!cxgb4_validate_boot_image(data, size)) 148662306a36Sopenharmony_ci return CXGB4_ETHTOOL_FLASH_BOOT; 148762306a36Sopenharmony_ci if (!cxgb4_validate_phy_image(data, size)) 148862306a36Sopenharmony_ci return CXGB4_ETHTOOL_FLASH_PHY; 148962306a36Sopenharmony_ci if (!cxgb4_validate_bootcfg_image(data, size)) 149062306a36Sopenharmony_ci return CXGB4_ETHTOOL_FLASH_BOOTCFG; 149162306a36Sopenharmony_ci 149262306a36Sopenharmony_ci return -EOPNOTSUPP; 149362306a36Sopenharmony_ci} 149462306a36Sopenharmony_ci 149562306a36Sopenharmony_cistatic int set_flash(struct net_device *netdev, struct ethtool_flash *ef) 149662306a36Sopenharmony_ci{ 149762306a36Sopenharmony_ci struct adapter *adap = netdev2adap(netdev); 149862306a36Sopenharmony_ci const struct firmware *fw; 149962306a36Sopenharmony_ci unsigned int master; 150062306a36Sopenharmony_ci u8 master_vld = 0; 150162306a36Sopenharmony_ci const u8 *fw_data; 150262306a36Sopenharmony_ci size_t fw_size; 150362306a36Sopenharmony_ci u32 size = 0; 150462306a36Sopenharmony_ci u32 pcie_fw; 150562306a36Sopenharmony_ci int region; 150662306a36Sopenharmony_ci int ret; 150762306a36Sopenharmony_ci 150862306a36Sopenharmony_ci pcie_fw = t4_read_reg(adap, PCIE_FW_A); 150962306a36Sopenharmony_ci master = PCIE_FW_MASTER_G(pcie_fw); 151062306a36Sopenharmony_ci if (pcie_fw & PCIE_FW_MASTER_VLD_F) 151162306a36Sopenharmony_ci master_vld = 1; 151262306a36Sopenharmony_ci /* if csiostor is the master return */ 151362306a36Sopenharmony_ci if (master_vld && (master != adap->pf)) { 151462306a36Sopenharmony_ci dev_warn(adap->pdev_dev, 151562306a36Sopenharmony_ci "cxgb4 driver needs to be loaded as MASTER to support FW flash\n"); 151662306a36Sopenharmony_ci return -EOPNOTSUPP; 151762306a36Sopenharmony_ci } 151862306a36Sopenharmony_ci 151962306a36Sopenharmony_ci ef->data[sizeof(ef->data) - 1] = '\0'; 152062306a36Sopenharmony_ci ret = request_firmware(&fw, ef->data, adap->pdev_dev); 152162306a36Sopenharmony_ci if (ret < 0) 152262306a36Sopenharmony_ci return ret; 152362306a36Sopenharmony_ci 152462306a36Sopenharmony_ci fw_data = fw->data; 152562306a36Sopenharmony_ci fw_size = fw->size; 152662306a36Sopenharmony_ci if (ef->region == ETHTOOL_FLASH_ALL_REGIONS) { 152762306a36Sopenharmony_ci while (fw_size > 0) { 152862306a36Sopenharmony_ci size = 0; 152962306a36Sopenharmony_ci region = cxgb4_ethtool_get_flash_region(fw_data, &size); 153062306a36Sopenharmony_ci if (region < 0 || !size) { 153162306a36Sopenharmony_ci ret = region; 153262306a36Sopenharmony_ci goto out_free_fw; 153362306a36Sopenharmony_ci } 153462306a36Sopenharmony_ci 153562306a36Sopenharmony_ci ret = cxgb4_ethtool_flash_region(netdev, fw_data, size, 153662306a36Sopenharmony_ci region); 153762306a36Sopenharmony_ci if (ret) 153862306a36Sopenharmony_ci goto out_free_fw; 153962306a36Sopenharmony_ci 154062306a36Sopenharmony_ci fw_data += size; 154162306a36Sopenharmony_ci fw_size -= size; 154262306a36Sopenharmony_ci } 154362306a36Sopenharmony_ci } else { 154462306a36Sopenharmony_ci ret = cxgb4_ethtool_flash_region(netdev, fw_data, fw_size, 154562306a36Sopenharmony_ci ef->region); 154662306a36Sopenharmony_ci } 154762306a36Sopenharmony_ci 154862306a36Sopenharmony_ciout_free_fw: 154962306a36Sopenharmony_ci release_firmware(fw); 155062306a36Sopenharmony_ci return ret; 155162306a36Sopenharmony_ci} 155262306a36Sopenharmony_ci 155362306a36Sopenharmony_cistatic int get_ts_info(struct net_device *dev, struct ethtool_ts_info *ts_info) 155462306a36Sopenharmony_ci{ 155562306a36Sopenharmony_ci struct port_info *pi = netdev_priv(dev); 155662306a36Sopenharmony_ci struct adapter *adapter = pi->adapter; 155762306a36Sopenharmony_ci 155862306a36Sopenharmony_ci ts_info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE | 155962306a36Sopenharmony_ci SOF_TIMESTAMPING_RX_SOFTWARE | 156062306a36Sopenharmony_ci SOF_TIMESTAMPING_SOFTWARE; 156162306a36Sopenharmony_ci 156262306a36Sopenharmony_ci ts_info->so_timestamping |= SOF_TIMESTAMPING_RX_HARDWARE | 156362306a36Sopenharmony_ci SOF_TIMESTAMPING_TX_HARDWARE | 156462306a36Sopenharmony_ci SOF_TIMESTAMPING_RAW_HARDWARE; 156562306a36Sopenharmony_ci 156662306a36Sopenharmony_ci ts_info->tx_types = (1 << HWTSTAMP_TX_OFF) | 156762306a36Sopenharmony_ci (1 << HWTSTAMP_TX_ON); 156862306a36Sopenharmony_ci 156962306a36Sopenharmony_ci ts_info->rx_filters = (1 << HWTSTAMP_FILTER_NONE) | 157062306a36Sopenharmony_ci (1 << HWTSTAMP_FILTER_PTP_V2_L4_EVENT) | 157162306a36Sopenharmony_ci (1 << HWTSTAMP_FILTER_PTP_V1_L4_SYNC) | 157262306a36Sopenharmony_ci (1 << HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ) | 157362306a36Sopenharmony_ci (1 << HWTSTAMP_FILTER_PTP_V2_L4_SYNC) | 157462306a36Sopenharmony_ci (1 << HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ); 157562306a36Sopenharmony_ci 157662306a36Sopenharmony_ci if (adapter->ptp_clock) 157762306a36Sopenharmony_ci ts_info->phc_index = ptp_clock_index(adapter->ptp_clock); 157862306a36Sopenharmony_ci else 157962306a36Sopenharmony_ci ts_info->phc_index = -1; 158062306a36Sopenharmony_ci 158162306a36Sopenharmony_ci return 0; 158262306a36Sopenharmony_ci} 158362306a36Sopenharmony_ci 158462306a36Sopenharmony_cistatic u32 get_rss_table_size(struct net_device *dev) 158562306a36Sopenharmony_ci{ 158662306a36Sopenharmony_ci const struct port_info *pi = netdev_priv(dev); 158762306a36Sopenharmony_ci 158862306a36Sopenharmony_ci return pi->rss_size; 158962306a36Sopenharmony_ci} 159062306a36Sopenharmony_ci 159162306a36Sopenharmony_cistatic int get_rss_table(struct net_device *dev, u32 *p, u8 *key, u8 *hfunc) 159262306a36Sopenharmony_ci{ 159362306a36Sopenharmony_ci const struct port_info *pi = netdev_priv(dev); 159462306a36Sopenharmony_ci unsigned int n = pi->rss_size; 159562306a36Sopenharmony_ci 159662306a36Sopenharmony_ci if (hfunc) 159762306a36Sopenharmony_ci *hfunc = ETH_RSS_HASH_TOP; 159862306a36Sopenharmony_ci if (!p) 159962306a36Sopenharmony_ci return 0; 160062306a36Sopenharmony_ci while (n--) 160162306a36Sopenharmony_ci p[n] = pi->rss[n]; 160262306a36Sopenharmony_ci return 0; 160362306a36Sopenharmony_ci} 160462306a36Sopenharmony_ci 160562306a36Sopenharmony_cistatic int set_rss_table(struct net_device *dev, const u32 *p, const u8 *key, 160662306a36Sopenharmony_ci const u8 hfunc) 160762306a36Sopenharmony_ci{ 160862306a36Sopenharmony_ci unsigned int i; 160962306a36Sopenharmony_ci struct port_info *pi = netdev_priv(dev); 161062306a36Sopenharmony_ci 161162306a36Sopenharmony_ci /* We require at least one supported parameter to be changed and no 161262306a36Sopenharmony_ci * change in any of the unsupported parameters 161362306a36Sopenharmony_ci */ 161462306a36Sopenharmony_ci if (key || 161562306a36Sopenharmony_ci (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP)) 161662306a36Sopenharmony_ci return -EOPNOTSUPP; 161762306a36Sopenharmony_ci if (!p) 161862306a36Sopenharmony_ci return 0; 161962306a36Sopenharmony_ci 162062306a36Sopenharmony_ci /* Interface must be brought up atleast once */ 162162306a36Sopenharmony_ci if (pi->adapter->flags & CXGB4_FULL_INIT_DONE) { 162262306a36Sopenharmony_ci for (i = 0; i < pi->rss_size; i++) 162362306a36Sopenharmony_ci pi->rss[i] = p[i]; 162462306a36Sopenharmony_ci 162562306a36Sopenharmony_ci return cxgb4_write_rss(pi, pi->rss); 162662306a36Sopenharmony_ci } 162762306a36Sopenharmony_ci 162862306a36Sopenharmony_ci return -EPERM; 162962306a36Sopenharmony_ci} 163062306a36Sopenharmony_ci 163162306a36Sopenharmony_cistatic struct filter_entry *cxgb4_get_filter_entry(struct adapter *adap, 163262306a36Sopenharmony_ci u32 ftid) 163362306a36Sopenharmony_ci{ 163462306a36Sopenharmony_ci struct tid_info *t = &adap->tids; 163562306a36Sopenharmony_ci 163662306a36Sopenharmony_ci if (ftid >= t->hpftid_base && ftid < t->hpftid_base + t->nhpftids) 163762306a36Sopenharmony_ci return &t->hpftid_tab[ftid - t->hpftid_base]; 163862306a36Sopenharmony_ci 163962306a36Sopenharmony_ci if (ftid >= t->ftid_base && ftid < t->ftid_base + t->nftids) 164062306a36Sopenharmony_ci return &t->ftid_tab[ftid - t->ftid_base]; 164162306a36Sopenharmony_ci 164262306a36Sopenharmony_ci return lookup_tid(t, ftid); 164362306a36Sopenharmony_ci} 164462306a36Sopenharmony_ci 164562306a36Sopenharmony_cistatic void cxgb4_fill_filter_rule(struct ethtool_rx_flow_spec *fs, 164662306a36Sopenharmony_ci struct ch_filter_specification *dfs) 164762306a36Sopenharmony_ci{ 164862306a36Sopenharmony_ci switch (dfs->val.proto) { 164962306a36Sopenharmony_ci case IPPROTO_TCP: 165062306a36Sopenharmony_ci if (dfs->type) 165162306a36Sopenharmony_ci fs->flow_type = TCP_V6_FLOW; 165262306a36Sopenharmony_ci else 165362306a36Sopenharmony_ci fs->flow_type = TCP_V4_FLOW; 165462306a36Sopenharmony_ci break; 165562306a36Sopenharmony_ci case IPPROTO_UDP: 165662306a36Sopenharmony_ci if (dfs->type) 165762306a36Sopenharmony_ci fs->flow_type = UDP_V6_FLOW; 165862306a36Sopenharmony_ci else 165962306a36Sopenharmony_ci fs->flow_type = UDP_V4_FLOW; 166062306a36Sopenharmony_ci break; 166162306a36Sopenharmony_ci } 166262306a36Sopenharmony_ci 166362306a36Sopenharmony_ci if (dfs->type) { 166462306a36Sopenharmony_ci fs->h_u.tcp_ip6_spec.psrc = cpu_to_be16(dfs->val.fport); 166562306a36Sopenharmony_ci fs->m_u.tcp_ip6_spec.psrc = cpu_to_be16(dfs->mask.fport); 166662306a36Sopenharmony_ci fs->h_u.tcp_ip6_spec.pdst = cpu_to_be16(dfs->val.lport); 166762306a36Sopenharmony_ci fs->m_u.tcp_ip6_spec.pdst = cpu_to_be16(dfs->mask.lport); 166862306a36Sopenharmony_ci memcpy(&fs->h_u.tcp_ip6_spec.ip6src, &dfs->val.fip[0], 166962306a36Sopenharmony_ci sizeof(fs->h_u.tcp_ip6_spec.ip6src)); 167062306a36Sopenharmony_ci memcpy(&fs->m_u.tcp_ip6_spec.ip6src, &dfs->mask.fip[0], 167162306a36Sopenharmony_ci sizeof(fs->m_u.tcp_ip6_spec.ip6src)); 167262306a36Sopenharmony_ci memcpy(&fs->h_u.tcp_ip6_spec.ip6dst, &dfs->val.lip[0], 167362306a36Sopenharmony_ci sizeof(fs->h_u.tcp_ip6_spec.ip6dst)); 167462306a36Sopenharmony_ci memcpy(&fs->m_u.tcp_ip6_spec.ip6dst, &dfs->mask.lip[0], 167562306a36Sopenharmony_ci sizeof(fs->m_u.tcp_ip6_spec.ip6dst)); 167662306a36Sopenharmony_ci fs->h_u.tcp_ip6_spec.tclass = dfs->val.tos; 167762306a36Sopenharmony_ci fs->m_u.tcp_ip6_spec.tclass = dfs->mask.tos; 167862306a36Sopenharmony_ci } else { 167962306a36Sopenharmony_ci fs->h_u.tcp_ip4_spec.psrc = cpu_to_be16(dfs->val.fport); 168062306a36Sopenharmony_ci fs->m_u.tcp_ip4_spec.psrc = cpu_to_be16(dfs->mask.fport); 168162306a36Sopenharmony_ci fs->h_u.tcp_ip4_spec.pdst = cpu_to_be16(dfs->val.lport); 168262306a36Sopenharmony_ci fs->m_u.tcp_ip4_spec.pdst = cpu_to_be16(dfs->mask.lport); 168362306a36Sopenharmony_ci memcpy(&fs->h_u.tcp_ip4_spec.ip4src, &dfs->val.fip[0], 168462306a36Sopenharmony_ci sizeof(fs->h_u.tcp_ip4_spec.ip4src)); 168562306a36Sopenharmony_ci memcpy(&fs->m_u.tcp_ip4_spec.ip4src, &dfs->mask.fip[0], 168662306a36Sopenharmony_ci sizeof(fs->m_u.tcp_ip4_spec.ip4src)); 168762306a36Sopenharmony_ci memcpy(&fs->h_u.tcp_ip4_spec.ip4dst, &dfs->val.lip[0], 168862306a36Sopenharmony_ci sizeof(fs->h_u.tcp_ip4_spec.ip4dst)); 168962306a36Sopenharmony_ci memcpy(&fs->m_u.tcp_ip4_spec.ip4dst, &dfs->mask.lip[0], 169062306a36Sopenharmony_ci sizeof(fs->m_u.tcp_ip4_spec.ip4dst)); 169162306a36Sopenharmony_ci fs->h_u.tcp_ip4_spec.tos = dfs->val.tos; 169262306a36Sopenharmony_ci fs->m_u.tcp_ip4_spec.tos = dfs->mask.tos; 169362306a36Sopenharmony_ci } 169462306a36Sopenharmony_ci fs->h_ext.vlan_tci = cpu_to_be16(dfs->val.ivlan); 169562306a36Sopenharmony_ci fs->m_ext.vlan_tci = cpu_to_be16(dfs->mask.ivlan); 169662306a36Sopenharmony_ci fs->flow_type |= FLOW_EXT; 169762306a36Sopenharmony_ci 169862306a36Sopenharmony_ci if (dfs->action == FILTER_DROP) 169962306a36Sopenharmony_ci fs->ring_cookie = RX_CLS_FLOW_DISC; 170062306a36Sopenharmony_ci else 170162306a36Sopenharmony_ci fs->ring_cookie = dfs->iq; 170262306a36Sopenharmony_ci} 170362306a36Sopenharmony_ci 170462306a36Sopenharmony_cistatic int cxgb4_ntuple_get_filter(struct net_device *dev, 170562306a36Sopenharmony_ci struct ethtool_rxnfc *cmd, 170662306a36Sopenharmony_ci unsigned int loc) 170762306a36Sopenharmony_ci{ 170862306a36Sopenharmony_ci const struct port_info *pi = netdev_priv(dev); 170962306a36Sopenharmony_ci struct adapter *adap = netdev2adap(dev); 171062306a36Sopenharmony_ci struct filter_entry *f; 171162306a36Sopenharmony_ci int ftid; 171262306a36Sopenharmony_ci 171362306a36Sopenharmony_ci if (!(adap->flags & CXGB4_FULL_INIT_DONE)) 171462306a36Sopenharmony_ci return -EAGAIN; 171562306a36Sopenharmony_ci 171662306a36Sopenharmony_ci /* Check for maximum filter range */ 171762306a36Sopenharmony_ci if (!adap->ethtool_filters) 171862306a36Sopenharmony_ci return -EOPNOTSUPP; 171962306a36Sopenharmony_ci 172062306a36Sopenharmony_ci if (loc >= adap->ethtool_filters->nentries) 172162306a36Sopenharmony_ci return -ERANGE; 172262306a36Sopenharmony_ci 172362306a36Sopenharmony_ci if (!test_bit(loc, adap->ethtool_filters->port[pi->port_id].bmap)) 172462306a36Sopenharmony_ci return -ENOENT; 172562306a36Sopenharmony_ci 172662306a36Sopenharmony_ci ftid = adap->ethtool_filters->port[pi->port_id].loc_array[loc]; 172762306a36Sopenharmony_ci 172862306a36Sopenharmony_ci /* Fetch filter_entry */ 172962306a36Sopenharmony_ci f = cxgb4_get_filter_entry(adap, ftid); 173062306a36Sopenharmony_ci 173162306a36Sopenharmony_ci cxgb4_fill_filter_rule(&cmd->fs, &f->fs); 173262306a36Sopenharmony_ci 173362306a36Sopenharmony_ci return 0; 173462306a36Sopenharmony_ci} 173562306a36Sopenharmony_ci 173662306a36Sopenharmony_cistatic int get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *info, 173762306a36Sopenharmony_ci u32 *rules) 173862306a36Sopenharmony_ci{ 173962306a36Sopenharmony_ci const struct port_info *pi = netdev_priv(dev); 174062306a36Sopenharmony_ci struct adapter *adap = netdev2adap(dev); 174162306a36Sopenharmony_ci unsigned int count = 0, index = 0; 174262306a36Sopenharmony_ci int ret = 0; 174362306a36Sopenharmony_ci 174462306a36Sopenharmony_ci switch (info->cmd) { 174562306a36Sopenharmony_ci case ETHTOOL_GRXFH: { 174662306a36Sopenharmony_ci unsigned int v = pi->rss_mode; 174762306a36Sopenharmony_ci 174862306a36Sopenharmony_ci info->data = 0; 174962306a36Sopenharmony_ci switch (info->flow_type) { 175062306a36Sopenharmony_ci case TCP_V4_FLOW: 175162306a36Sopenharmony_ci if (v & FW_RSS_VI_CONFIG_CMD_IP4FOURTUPEN_F) 175262306a36Sopenharmony_ci info->data = RXH_IP_SRC | RXH_IP_DST | 175362306a36Sopenharmony_ci RXH_L4_B_0_1 | RXH_L4_B_2_3; 175462306a36Sopenharmony_ci else if (v & FW_RSS_VI_CONFIG_CMD_IP4TWOTUPEN_F) 175562306a36Sopenharmony_ci info->data = RXH_IP_SRC | RXH_IP_DST; 175662306a36Sopenharmony_ci break; 175762306a36Sopenharmony_ci case UDP_V4_FLOW: 175862306a36Sopenharmony_ci if ((v & FW_RSS_VI_CONFIG_CMD_IP4FOURTUPEN_F) && 175962306a36Sopenharmony_ci (v & FW_RSS_VI_CONFIG_CMD_UDPEN_F)) 176062306a36Sopenharmony_ci info->data = RXH_IP_SRC | RXH_IP_DST | 176162306a36Sopenharmony_ci RXH_L4_B_0_1 | RXH_L4_B_2_3; 176262306a36Sopenharmony_ci else if (v & FW_RSS_VI_CONFIG_CMD_IP4TWOTUPEN_F) 176362306a36Sopenharmony_ci info->data = RXH_IP_SRC | RXH_IP_DST; 176462306a36Sopenharmony_ci break; 176562306a36Sopenharmony_ci case SCTP_V4_FLOW: 176662306a36Sopenharmony_ci case AH_ESP_V4_FLOW: 176762306a36Sopenharmony_ci case IPV4_FLOW: 176862306a36Sopenharmony_ci if (v & FW_RSS_VI_CONFIG_CMD_IP4TWOTUPEN_F) 176962306a36Sopenharmony_ci info->data = RXH_IP_SRC | RXH_IP_DST; 177062306a36Sopenharmony_ci break; 177162306a36Sopenharmony_ci case TCP_V6_FLOW: 177262306a36Sopenharmony_ci if (v & FW_RSS_VI_CONFIG_CMD_IP6FOURTUPEN_F) 177362306a36Sopenharmony_ci info->data = RXH_IP_SRC | RXH_IP_DST | 177462306a36Sopenharmony_ci RXH_L4_B_0_1 | RXH_L4_B_2_3; 177562306a36Sopenharmony_ci else if (v & FW_RSS_VI_CONFIG_CMD_IP6TWOTUPEN_F) 177662306a36Sopenharmony_ci info->data = RXH_IP_SRC | RXH_IP_DST; 177762306a36Sopenharmony_ci break; 177862306a36Sopenharmony_ci case UDP_V6_FLOW: 177962306a36Sopenharmony_ci if ((v & FW_RSS_VI_CONFIG_CMD_IP6FOURTUPEN_F) && 178062306a36Sopenharmony_ci (v & FW_RSS_VI_CONFIG_CMD_UDPEN_F)) 178162306a36Sopenharmony_ci info->data = RXH_IP_SRC | RXH_IP_DST | 178262306a36Sopenharmony_ci RXH_L4_B_0_1 | RXH_L4_B_2_3; 178362306a36Sopenharmony_ci else if (v & FW_RSS_VI_CONFIG_CMD_IP6TWOTUPEN_F) 178462306a36Sopenharmony_ci info->data = RXH_IP_SRC | RXH_IP_DST; 178562306a36Sopenharmony_ci break; 178662306a36Sopenharmony_ci case SCTP_V6_FLOW: 178762306a36Sopenharmony_ci case AH_ESP_V6_FLOW: 178862306a36Sopenharmony_ci case IPV6_FLOW: 178962306a36Sopenharmony_ci if (v & FW_RSS_VI_CONFIG_CMD_IP6TWOTUPEN_F) 179062306a36Sopenharmony_ci info->data = RXH_IP_SRC | RXH_IP_DST; 179162306a36Sopenharmony_ci break; 179262306a36Sopenharmony_ci } 179362306a36Sopenharmony_ci return 0; 179462306a36Sopenharmony_ci } 179562306a36Sopenharmony_ci case ETHTOOL_GRXRINGS: 179662306a36Sopenharmony_ci info->data = pi->nqsets; 179762306a36Sopenharmony_ci return 0; 179862306a36Sopenharmony_ci case ETHTOOL_GRXCLSRLCNT: 179962306a36Sopenharmony_ci info->rule_cnt = 180062306a36Sopenharmony_ci adap->ethtool_filters->port[pi->port_id].in_use; 180162306a36Sopenharmony_ci return 0; 180262306a36Sopenharmony_ci case ETHTOOL_GRXCLSRULE: 180362306a36Sopenharmony_ci return cxgb4_ntuple_get_filter(dev, info, info->fs.location); 180462306a36Sopenharmony_ci case ETHTOOL_GRXCLSRLALL: 180562306a36Sopenharmony_ci info->data = adap->ethtool_filters->nentries; 180662306a36Sopenharmony_ci while (count < info->rule_cnt) { 180762306a36Sopenharmony_ci ret = cxgb4_ntuple_get_filter(dev, info, index); 180862306a36Sopenharmony_ci if (!ret) 180962306a36Sopenharmony_ci rules[count++] = index; 181062306a36Sopenharmony_ci index++; 181162306a36Sopenharmony_ci } 181262306a36Sopenharmony_ci return 0; 181362306a36Sopenharmony_ci } 181462306a36Sopenharmony_ci 181562306a36Sopenharmony_ci return -EOPNOTSUPP; 181662306a36Sopenharmony_ci} 181762306a36Sopenharmony_ci 181862306a36Sopenharmony_cistatic int cxgb4_ntuple_del_filter(struct net_device *dev, 181962306a36Sopenharmony_ci struct ethtool_rxnfc *cmd) 182062306a36Sopenharmony_ci{ 182162306a36Sopenharmony_ci struct cxgb4_ethtool_filter_info *filter_info; 182262306a36Sopenharmony_ci struct adapter *adapter = netdev2adap(dev); 182362306a36Sopenharmony_ci struct port_info *pi = netdev_priv(dev); 182462306a36Sopenharmony_ci struct filter_entry *f; 182562306a36Sopenharmony_ci u32 filter_id; 182662306a36Sopenharmony_ci int ret; 182762306a36Sopenharmony_ci 182862306a36Sopenharmony_ci if (!(adapter->flags & CXGB4_FULL_INIT_DONE)) 182962306a36Sopenharmony_ci return -EAGAIN; /* can still change nfilters */ 183062306a36Sopenharmony_ci 183162306a36Sopenharmony_ci if (!adapter->ethtool_filters) 183262306a36Sopenharmony_ci return -EOPNOTSUPP; 183362306a36Sopenharmony_ci 183462306a36Sopenharmony_ci if (cmd->fs.location >= adapter->ethtool_filters->nentries) { 183562306a36Sopenharmony_ci dev_err(adapter->pdev_dev, 183662306a36Sopenharmony_ci "Location must be < %u", 183762306a36Sopenharmony_ci adapter->ethtool_filters->nentries); 183862306a36Sopenharmony_ci return -ERANGE; 183962306a36Sopenharmony_ci } 184062306a36Sopenharmony_ci 184162306a36Sopenharmony_ci filter_info = &adapter->ethtool_filters->port[pi->port_id]; 184262306a36Sopenharmony_ci 184362306a36Sopenharmony_ci if (!test_bit(cmd->fs.location, filter_info->bmap)) 184462306a36Sopenharmony_ci return -ENOENT; 184562306a36Sopenharmony_ci 184662306a36Sopenharmony_ci filter_id = filter_info->loc_array[cmd->fs.location]; 184762306a36Sopenharmony_ci f = cxgb4_get_filter_entry(adapter, filter_id); 184862306a36Sopenharmony_ci 184962306a36Sopenharmony_ci if (f->fs.prio) 185062306a36Sopenharmony_ci filter_id -= adapter->tids.hpftid_base; 185162306a36Sopenharmony_ci else if (!f->fs.hash) 185262306a36Sopenharmony_ci filter_id -= (adapter->tids.ftid_base - adapter->tids.nhpftids); 185362306a36Sopenharmony_ci 185462306a36Sopenharmony_ci ret = cxgb4_flow_rule_destroy(dev, f->fs.tc_prio, &f->fs, filter_id); 185562306a36Sopenharmony_ci if (ret) 185662306a36Sopenharmony_ci goto err; 185762306a36Sopenharmony_ci 185862306a36Sopenharmony_ci clear_bit(cmd->fs.location, filter_info->bmap); 185962306a36Sopenharmony_ci filter_info->in_use--; 186062306a36Sopenharmony_ci 186162306a36Sopenharmony_cierr: 186262306a36Sopenharmony_ci return ret; 186362306a36Sopenharmony_ci} 186462306a36Sopenharmony_ci 186562306a36Sopenharmony_ci/* Add Ethtool n-tuple filters. */ 186662306a36Sopenharmony_cistatic int cxgb4_ntuple_set_filter(struct net_device *netdev, 186762306a36Sopenharmony_ci struct ethtool_rxnfc *cmd) 186862306a36Sopenharmony_ci{ 186962306a36Sopenharmony_ci struct ethtool_rx_flow_spec_input input = {}; 187062306a36Sopenharmony_ci struct cxgb4_ethtool_filter_info *filter_info; 187162306a36Sopenharmony_ci struct adapter *adapter = netdev2adap(netdev); 187262306a36Sopenharmony_ci struct port_info *pi = netdev_priv(netdev); 187362306a36Sopenharmony_ci struct ch_filter_specification fs; 187462306a36Sopenharmony_ci struct ethtool_rx_flow_rule *flow; 187562306a36Sopenharmony_ci u32 tid; 187662306a36Sopenharmony_ci int ret; 187762306a36Sopenharmony_ci 187862306a36Sopenharmony_ci if (!(adapter->flags & CXGB4_FULL_INIT_DONE)) 187962306a36Sopenharmony_ci return -EAGAIN; /* can still change nfilters */ 188062306a36Sopenharmony_ci 188162306a36Sopenharmony_ci if (!adapter->ethtool_filters) 188262306a36Sopenharmony_ci return -EOPNOTSUPP; 188362306a36Sopenharmony_ci 188462306a36Sopenharmony_ci if (cmd->fs.location >= adapter->ethtool_filters->nentries) { 188562306a36Sopenharmony_ci dev_err(adapter->pdev_dev, 188662306a36Sopenharmony_ci "Location must be < %u", 188762306a36Sopenharmony_ci adapter->ethtool_filters->nentries); 188862306a36Sopenharmony_ci return -ERANGE; 188962306a36Sopenharmony_ci } 189062306a36Sopenharmony_ci 189162306a36Sopenharmony_ci if (test_bit(cmd->fs.location, 189262306a36Sopenharmony_ci adapter->ethtool_filters->port[pi->port_id].bmap)) 189362306a36Sopenharmony_ci return -EEXIST; 189462306a36Sopenharmony_ci 189562306a36Sopenharmony_ci memset(&fs, 0, sizeof(fs)); 189662306a36Sopenharmony_ci 189762306a36Sopenharmony_ci input.fs = &cmd->fs; 189862306a36Sopenharmony_ci flow = ethtool_rx_flow_rule_create(&input); 189962306a36Sopenharmony_ci if (IS_ERR(flow)) { 190062306a36Sopenharmony_ci ret = PTR_ERR(flow); 190162306a36Sopenharmony_ci goto exit; 190262306a36Sopenharmony_ci } 190362306a36Sopenharmony_ci 190462306a36Sopenharmony_ci fs.hitcnts = 1; 190562306a36Sopenharmony_ci 190662306a36Sopenharmony_ci ret = cxgb4_flow_rule_replace(netdev, flow->rule, cmd->fs.location, 190762306a36Sopenharmony_ci NULL, &fs, &tid); 190862306a36Sopenharmony_ci if (ret) 190962306a36Sopenharmony_ci goto free; 191062306a36Sopenharmony_ci 191162306a36Sopenharmony_ci filter_info = &adapter->ethtool_filters->port[pi->port_id]; 191262306a36Sopenharmony_ci 191362306a36Sopenharmony_ci if (fs.prio) 191462306a36Sopenharmony_ci tid += adapter->tids.hpftid_base; 191562306a36Sopenharmony_ci else if (!fs.hash) 191662306a36Sopenharmony_ci tid += (adapter->tids.ftid_base - adapter->tids.nhpftids); 191762306a36Sopenharmony_ci 191862306a36Sopenharmony_ci filter_info->loc_array[cmd->fs.location] = tid; 191962306a36Sopenharmony_ci set_bit(cmd->fs.location, filter_info->bmap); 192062306a36Sopenharmony_ci filter_info->in_use++; 192162306a36Sopenharmony_ci 192262306a36Sopenharmony_cifree: 192362306a36Sopenharmony_ci ethtool_rx_flow_rule_destroy(flow); 192462306a36Sopenharmony_ciexit: 192562306a36Sopenharmony_ci return ret; 192662306a36Sopenharmony_ci} 192762306a36Sopenharmony_ci 192862306a36Sopenharmony_cistatic int set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd) 192962306a36Sopenharmony_ci{ 193062306a36Sopenharmony_ci int ret = -EOPNOTSUPP; 193162306a36Sopenharmony_ci 193262306a36Sopenharmony_ci switch (cmd->cmd) { 193362306a36Sopenharmony_ci case ETHTOOL_SRXCLSRLINS: 193462306a36Sopenharmony_ci ret = cxgb4_ntuple_set_filter(dev, cmd); 193562306a36Sopenharmony_ci break; 193662306a36Sopenharmony_ci case ETHTOOL_SRXCLSRLDEL: 193762306a36Sopenharmony_ci ret = cxgb4_ntuple_del_filter(dev, cmd); 193862306a36Sopenharmony_ci break; 193962306a36Sopenharmony_ci default: 194062306a36Sopenharmony_ci break; 194162306a36Sopenharmony_ci } 194262306a36Sopenharmony_ci 194362306a36Sopenharmony_ci return ret; 194462306a36Sopenharmony_ci} 194562306a36Sopenharmony_ci 194662306a36Sopenharmony_cistatic int set_dump(struct net_device *dev, struct ethtool_dump *eth_dump) 194762306a36Sopenharmony_ci{ 194862306a36Sopenharmony_ci struct adapter *adapter = netdev2adap(dev); 194962306a36Sopenharmony_ci u32 len = 0; 195062306a36Sopenharmony_ci 195162306a36Sopenharmony_ci len = sizeof(struct cudbg_hdr) + 195262306a36Sopenharmony_ci sizeof(struct cudbg_entity_hdr) * CUDBG_MAX_ENTITY; 195362306a36Sopenharmony_ci len += cxgb4_get_dump_length(adapter, eth_dump->flag); 195462306a36Sopenharmony_ci 195562306a36Sopenharmony_ci adapter->eth_dump.flag = eth_dump->flag; 195662306a36Sopenharmony_ci adapter->eth_dump.len = len; 195762306a36Sopenharmony_ci return 0; 195862306a36Sopenharmony_ci} 195962306a36Sopenharmony_ci 196062306a36Sopenharmony_cistatic int get_dump_flag(struct net_device *dev, struct ethtool_dump *eth_dump) 196162306a36Sopenharmony_ci{ 196262306a36Sopenharmony_ci struct adapter *adapter = netdev2adap(dev); 196362306a36Sopenharmony_ci 196462306a36Sopenharmony_ci eth_dump->flag = adapter->eth_dump.flag; 196562306a36Sopenharmony_ci eth_dump->len = adapter->eth_dump.len; 196662306a36Sopenharmony_ci eth_dump->version = adapter->eth_dump.version; 196762306a36Sopenharmony_ci return 0; 196862306a36Sopenharmony_ci} 196962306a36Sopenharmony_ci 197062306a36Sopenharmony_cistatic int get_dump_data(struct net_device *dev, struct ethtool_dump *eth_dump, 197162306a36Sopenharmony_ci void *buf) 197262306a36Sopenharmony_ci{ 197362306a36Sopenharmony_ci struct adapter *adapter = netdev2adap(dev); 197462306a36Sopenharmony_ci u32 len = 0; 197562306a36Sopenharmony_ci int ret = 0; 197662306a36Sopenharmony_ci 197762306a36Sopenharmony_ci if (adapter->eth_dump.flag == CXGB4_ETH_DUMP_NONE) 197862306a36Sopenharmony_ci return -ENOENT; 197962306a36Sopenharmony_ci 198062306a36Sopenharmony_ci len = sizeof(struct cudbg_hdr) + 198162306a36Sopenharmony_ci sizeof(struct cudbg_entity_hdr) * CUDBG_MAX_ENTITY; 198262306a36Sopenharmony_ci len += cxgb4_get_dump_length(adapter, adapter->eth_dump.flag); 198362306a36Sopenharmony_ci if (eth_dump->len < len) 198462306a36Sopenharmony_ci return -ENOMEM; 198562306a36Sopenharmony_ci 198662306a36Sopenharmony_ci ret = cxgb4_cudbg_collect(adapter, buf, &len, adapter->eth_dump.flag); 198762306a36Sopenharmony_ci if (ret) 198862306a36Sopenharmony_ci return ret; 198962306a36Sopenharmony_ci 199062306a36Sopenharmony_ci eth_dump->flag = adapter->eth_dump.flag; 199162306a36Sopenharmony_ci eth_dump->len = len; 199262306a36Sopenharmony_ci eth_dump->version = adapter->eth_dump.version; 199362306a36Sopenharmony_ci return 0; 199462306a36Sopenharmony_ci} 199562306a36Sopenharmony_ci 199662306a36Sopenharmony_cistatic bool cxgb4_fw_mod_type_info_available(unsigned int fw_mod_type) 199762306a36Sopenharmony_ci{ 199862306a36Sopenharmony_ci /* Read port module EEPROM as long as it is plugged-in and 199962306a36Sopenharmony_ci * safe to read. 200062306a36Sopenharmony_ci */ 200162306a36Sopenharmony_ci return (fw_mod_type != FW_PORT_MOD_TYPE_NONE && 200262306a36Sopenharmony_ci fw_mod_type != FW_PORT_MOD_TYPE_ERROR); 200362306a36Sopenharmony_ci} 200462306a36Sopenharmony_ci 200562306a36Sopenharmony_cistatic int cxgb4_get_module_info(struct net_device *dev, 200662306a36Sopenharmony_ci struct ethtool_modinfo *modinfo) 200762306a36Sopenharmony_ci{ 200862306a36Sopenharmony_ci struct port_info *pi = netdev_priv(dev); 200962306a36Sopenharmony_ci u8 sff8472_comp, sff_diag_type, sff_rev; 201062306a36Sopenharmony_ci struct adapter *adapter = pi->adapter; 201162306a36Sopenharmony_ci int ret; 201262306a36Sopenharmony_ci 201362306a36Sopenharmony_ci if (!cxgb4_fw_mod_type_info_available(pi->mod_type)) 201462306a36Sopenharmony_ci return -EINVAL; 201562306a36Sopenharmony_ci 201662306a36Sopenharmony_ci switch (pi->port_type) { 201762306a36Sopenharmony_ci case FW_PORT_TYPE_SFP: 201862306a36Sopenharmony_ci case FW_PORT_TYPE_QSA: 201962306a36Sopenharmony_ci case FW_PORT_TYPE_SFP28: 202062306a36Sopenharmony_ci ret = t4_i2c_rd(adapter, adapter->mbox, pi->tx_chan, 202162306a36Sopenharmony_ci I2C_DEV_ADDR_A0, SFF_8472_COMP_ADDR, 202262306a36Sopenharmony_ci SFF_8472_COMP_LEN, &sff8472_comp); 202362306a36Sopenharmony_ci if (ret) 202462306a36Sopenharmony_ci return ret; 202562306a36Sopenharmony_ci ret = t4_i2c_rd(adapter, adapter->mbox, pi->tx_chan, 202662306a36Sopenharmony_ci I2C_DEV_ADDR_A0, SFP_DIAG_TYPE_ADDR, 202762306a36Sopenharmony_ci SFP_DIAG_TYPE_LEN, &sff_diag_type); 202862306a36Sopenharmony_ci if (ret) 202962306a36Sopenharmony_ci return ret; 203062306a36Sopenharmony_ci 203162306a36Sopenharmony_ci if (!sff8472_comp || (sff_diag_type & SFP_DIAG_ADDRMODE)) { 203262306a36Sopenharmony_ci modinfo->type = ETH_MODULE_SFF_8079; 203362306a36Sopenharmony_ci modinfo->eeprom_len = ETH_MODULE_SFF_8079_LEN; 203462306a36Sopenharmony_ci } else { 203562306a36Sopenharmony_ci modinfo->type = ETH_MODULE_SFF_8472; 203662306a36Sopenharmony_ci if (sff_diag_type & SFP_DIAG_IMPLEMENTED) 203762306a36Sopenharmony_ci modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN; 203862306a36Sopenharmony_ci else 203962306a36Sopenharmony_ci modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN / 2; 204062306a36Sopenharmony_ci } 204162306a36Sopenharmony_ci break; 204262306a36Sopenharmony_ci 204362306a36Sopenharmony_ci case FW_PORT_TYPE_QSFP: 204462306a36Sopenharmony_ci case FW_PORT_TYPE_QSFP_10G: 204562306a36Sopenharmony_ci case FW_PORT_TYPE_CR_QSFP: 204662306a36Sopenharmony_ci case FW_PORT_TYPE_CR2_QSFP: 204762306a36Sopenharmony_ci case FW_PORT_TYPE_CR4_QSFP: 204862306a36Sopenharmony_ci ret = t4_i2c_rd(adapter, adapter->mbox, pi->tx_chan, 204962306a36Sopenharmony_ci I2C_DEV_ADDR_A0, SFF_REV_ADDR, 205062306a36Sopenharmony_ci SFF_REV_LEN, &sff_rev); 205162306a36Sopenharmony_ci /* For QSFP type ports, revision value >= 3 205262306a36Sopenharmony_ci * means the SFP is 8636 compliant. 205362306a36Sopenharmony_ci */ 205462306a36Sopenharmony_ci if (ret) 205562306a36Sopenharmony_ci return ret; 205662306a36Sopenharmony_ci if (sff_rev >= 0x3) { 205762306a36Sopenharmony_ci modinfo->type = ETH_MODULE_SFF_8636; 205862306a36Sopenharmony_ci modinfo->eeprom_len = ETH_MODULE_SFF_8636_LEN; 205962306a36Sopenharmony_ci } else { 206062306a36Sopenharmony_ci modinfo->type = ETH_MODULE_SFF_8436; 206162306a36Sopenharmony_ci modinfo->eeprom_len = ETH_MODULE_SFF_8436_LEN; 206262306a36Sopenharmony_ci } 206362306a36Sopenharmony_ci break; 206462306a36Sopenharmony_ci 206562306a36Sopenharmony_ci default: 206662306a36Sopenharmony_ci return -EINVAL; 206762306a36Sopenharmony_ci } 206862306a36Sopenharmony_ci 206962306a36Sopenharmony_ci return 0; 207062306a36Sopenharmony_ci} 207162306a36Sopenharmony_ci 207262306a36Sopenharmony_cistatic int cxgb4_get_module_eeprom(struct net_device *dev, 207362306a36Sopenharmony_ci struct ethtool_eeprom *eprom, u8 *data) 207462306a36Sopenharmony_ci{ 207562306a36Sopenharmony_ci int ret = 0, offset = eprom->offset, len = eprom->len; 207662306a36Sopenharmony_ci struct port_info *pi = netdev_priv(dev); 207762306a36Sopenharmony_ci struct adapter *adapter = pi->adapter; 207862306a36Sopenharmony_ci 207962306a36Sopenharmony_ci memset(data, 0, eprom->len); 208062306a36Sopenharmony_ci if (offset + len <= I2C_PAGE_SIZE) 208162306a36Sopenharmony_ci return t4_i2c_rd(adapter, adapter->mbox, pi->tx_chan, 208262306a36Sopenharmony_ci I2C_DEV_ADDR_A0, offset, len, data); 208362306a36Sopenharmony_ci 208462306a36Sopenharmony_ci /* offset + len spans 0xa0 and 0xa1 pages */ 208562306a36Sopenharmony_ci if (offset <= I2C_PAGE_SIZE) { 208662306a36Sopenharmony_ci /* read 0xa0 page */ 208762306a36Sopenharmony_ci len = I2C_PAGE_SIZE - offset; 208862306a36Sopenharmony_ci ret = t4_i2c_rd(adapter, adapter->mbox, pi->tx_chan, 208962306a36Sopenharmony_ci I2C_DEV_ADDR_A0, offset, len, data); 209062306a36Sopenharmony_ci if (ret) 209162306a36Sopenharmony_ci return ret; 209262306a36Sopenharmony_ci offset = I2C_PAGE_SIZE; 209362306a36Sopenharmony_ci /* Remaining bytes to be read from second page = 209462306a36Sopenharmony_ci * Total length - bytes read from first page 209562306a36Sopenharmony_ci */ 209662306a36Sopenharmony_ci len = eprom->len - len; 209762306a36Sopenharmony_ci } 209862306a36Sopenharmony_ci /* Read additional optical diagnostics from page 0xa2 if supported */ 209962306a36Sopenharmony_ci return t4_i2c_rd(adapter, adapter->mbox, pi->tx_chan, I2C_DEV_ADDR_A2, 210062306a36Sopenharmony_ci offset, len, &data[eprom->len - len]); 210162306a36Sopenharmony_ci} 210262306a36Sopenharmony_ci 210362306a36Sopenharmony_cistatic u32 cxgb4_get_priv_flags(struct net_device *netdev) 210462306a36Sopenharmony_ci{ 210562306a36Sopenharmony_ci struct port_info *pi = netdev_priv(netdev); 210662306a36Sopenharmony_ci struct adapter *adapter = pi->adapter; 210762306a36Sopenharmony_ci 210862306a36Sopenharmony_ci return (adapter->eth_flags | pi->eth_flags); 210962306a36Sopenharmony_ci} 211062306a36Sopenharmony_ci 211162306a36Sopenharmony_ci/** 211262306a36Sopenharmony_ci * set_flags - set/unset specified flags if passed in new_flags 211362306a36Sopenharmony_ci * @cur_flags: pointer to current flags 211462306a36Sopenharmony_ci * @new_flags: new incoming flags 211562306a36Sopenharmony_ci * @flags: set of flags to set/unset 211662306a36Sopenharmony_ci */ 211762306a36Sopenharmony_cistatic inline void set_flags(u32 *cur_flags, u32 new_flags, u32 flags) 211862306a36Sopenharmony_ci{ 211962306a36Sopenharmony_ci *cur_flags = (*cur_flags & ~flags) | (new_flags & flags); 212062306a36Sopenharmony_ci} 212162306a36Sopenharmony_ci 212262306a36Sopenharmony_cistatic int cxgb4_set_priv_flags(struct net_device *netdev, u32 flags) 212362306a36Sopenharmony_ci{ 212462306a36Sopenharmony_ci struct port_info *pi = netdev_priv(netdev); 212562306a36Sopenharmony_ci struct adapter *adapter = pi->adapter; 212662306a36Sopenharmony_ci 212762306a36Sopenharmony_ci set_flags(&adapter->eth_flags, flags, PRIV_FLAGS_ADAP); 212862306a36Sopenharmony_ci set_flags(&pi->eth_flags, flags, PRIV_FLAGS_PORT); 212962306a36Sopenharmony_ci 213062306a36Sopenharmony_ci return 0; 213162306a36Sopenharmony_ci} 213262306a36Sopenharmony_ci 213362306a36Sopenharmony_cistatic void cxgb4_lb_test(struct net_device *netdev, u64 *lb_status) 213462306a36Sopenharmony_ci{ 213562306a36Sopenharmony_ci int dev_state = netif_running(netdev); 213662306a36Sopenharmony_ci 213762306a36Sopenharmony_ci if (dev_state) { 213862306a36Sopenharmony_ci netif_tx_stop_all_queues(netdev); 213962306a36Sopenharmony_ci netif_carrier_off(netdev); 214062306a36Sopenharmony_ci } 214162306a36Sopenharmony_ci 214262306a36Sopenharmony_ci *lb_status = cxgb4_selftest_lb_pkt(netdev); 214362306a36Sopenharmony_ci 214462306a36Sopenharmony_ci if (dev_state) { 214562306a36Sopenharmony_ci netif_tx_start_all_queues(netdev); 214662306a36Sopenharmony_ci netif_carrier_on(netdev); 214762306a36Sopenharmony_ci } 214862306a36Sopenharmony_ci} 214962306a36Sopenharmony_ci 215062306a36Sopenharmony_cistatic void cxgb4_self_test(struct net_device *netdev, 215162306a36Sopenharmony_ci struct ethtool_test *eth_test, u64 *data) 215262306a36Sopenharmony_ci{ 215362306a36Sopenharmony_ci struct port_info *pi = netdev_priv(netdev); 215462306a36Sopenharmony_ci struct adapter *adap = pi->adapter; 215562306a36Sopenharmony_ci 215662306a36Sopenharmony_ci memset(data, 0, sizeof(u64) * CXGB4_ETHTOOL_MAX_TEST); 215762306a36Sopenharmony_ci 215862306a36Sopenharmony_ci if (!(adap->flags & CXGB4_FULL_INIT_DONE) || 215962306a36Sopenharmony_ci !(adap->flags & CXGB4_FW_OK)) { 216062306a36Sopenharmony_ci eth_test->flags |= ETH_TEST_FL_FAILED; 216162306a36Sopenharmony_ci return; 216262306a36Sopenharmony_ci } 216362306a36Sopenharmony_ci 216462306a36Sopenharmony_ci if (eth_test->flags & ETH_TEST_FL_OFFLINE) 216562306a36Sopenharmony_ci cxgb4_lb_test(netdev, &data[CXGB4_ETHTOOL_LB_TEST]); 216662306a36Sopenharmony_ci 216762306a36Sopenharmony_ci if (data[CXGB4_ETHTOOL_LB_TEST]) 216862306a36Sopenharmony_ci eth_test->flags |= ETH_TEST_FL_FAILED; 216962306a36Sopenharmony_ci} 217062306a36Sopenharmony_ci 217162306a36Sopenharmony_cistatic const struct ethtool_ops cxgb_ethtool_ops = { 217262306a36Sopenharmony_ci .supported_coalesce_params = ETHTOOL_COALESCE_USECS | 217362306a36Sopenharmony_ci ETHTOOL_COALESCE_RX_MAX_FRAMES | 217462306a36Sopenharmony_ci ETHTOOL_COALESCE_TX_USECS_IRQ | 217562306a36Sopenharmony_ci ETHTOOL_COALESCE_USE_ADAPTIVE_RX, 217662306a36Sopenharmony_ci .get_link_ksettings = get_link_ksettings, 217762306a36Sopenharmony_ci .set_link_ksettings = set_link_ksettings, 217862306a36Sopenharmony_ci .get_fecparam = get_fecparam, 217962306a36Sopenharmony_ci .set_fecparam = set_fecparam, 218062306a36Sopenharmony_ci .get_drvinfo = get_drvinfo, 218162306a36Sopenharmony_ci .get_msglevel = get_msglevel, 218262306a36Sopenharmony_ci .set_msglevel = set_msglevel, 218362306a36Sopenharmony_ci .get_ringparam = get_sge_param, 218462306a36Sopenharmony_ci .set_ringparam = set_sge_param, 218562306a36Sopenharmony_ci .get_coalesce = get_coalesce, 218662306a36Sopenharmony_ci .set_coalesce = set_coalesce, 218762306a36Sopenharmony_ci .get_eeprom_len = get_eeprom_len, 218862306a36Sopenharmony_ci .get_eeprom = get_eeprom, 218962306a36Sopenharmony_ci .set_eeprom = set_eeprom, 219062306a36Sopenharmony_ci .get_pauseparam = get_pauseparam, 219162306a36Sopenharmony_ci .set_pauseparam = set_pauseparam, 219262306a36Sopenharmony_ci .get_link = ethtool_op_get_link, 219362306a36Sopenharmony_ci .get_strings = get_strings, 219462306a36Sopenharmony_ci .set_phys_id = identify_port, 219562306a36Sopenharmony_ci .nway_reset = restart_autoneg, 219662306a36Sopenharmony_ci .get_sset_count = get_sset_count, 219762306a36Sopenharmony_ci .get_ethtool_stats = get_stats, 219862306a36Sopenharmony_ci .get_regs_len = get_regs_len, 219962306a36Sopenharmony_ci .get_regs = get_regs, 220062306a36Sopenharmony_ci .get_rxnfc = get_rxnfc, 220162306a36Sopenharmony_ci .set_rxnfc = set_rxnfc, 220262306a36Sopenharmony_ci .get_rxfh_indir_size = get_rss_table_size, 220362306a36Sopenharmony_ci .get_rxfh = get_rss_table, 220462306a36Sopenharmony_ci .set_rxfh = set_rss_table, 220562306a36Sopenharmony_ci .self_test = cxgb4_self_test, 220662306a36Sopenharmony_ci .flash_device = set_flash, 220762306a36Sopenharmony_ci .get_ts_info = get_ts_info, 220862306a36Sopenharmony_ci .set_dump = set_dump, 220962306a36Sopenharmony_ci .get_dump_flag = get_dump_flag, 221062306a36Sopenharmony_ci .get_dump_data = get_dump_data, 221162306a36Sopenharmony_ci .get_module_info = cxgb4_get_module_info, 221262306a36Sopenharmony_ci .get_module_eeprom = cxgb4_get_module_eeprom, 221362306a36Sopenharmony_ci .get_priv_flags = cxgb4_get_priv_flags, 221462306a36Sopenharmony_ci .set_priv_flags = cxgb4_set_priv_flags, 221562306a36Sopenharmony_ci}; 221662306a36Sopenharmony_ci 221762306a36Sopenharmony_civoid cxgb4_cleanup_ethtool_filters(struct adapter *adap) 221862306a36Sopenharmony_ci{ 221962306a36Sopenharmony_ci struct cxgb4_ethtool_filter_info *eth_filter_info; 222062306a36Sopenharmony_ci u8 i; 222162306a36Sopenharmony_ci 222262306a36Sopenharmony_ci if (!adap->ethtool_filters) 222362306a36Sopenharmony_ci return; 222462306a36Sopenharmony_ci 222562306a36Sopenharmony_ci eth_filter_info = adap->ethtool_filters->port; 222662306a36Sopenharmony_ci 222762306a36Sopenharmony_ci if (eth_filter_info) { 222862306a36Sopenharmony_ci for (i = 0; i < adap->params.nports; i++) { 222962306a36Sopenharmony_ci kvfree(eth_filter_info[i].loc_array); 223062306a36Sopenharmony_ci bitmap_free(eth_filter_info[i].bmap); 223162306a36Sopenharmony_ci } 223262306a36Sopenharmony_ci kfree(eth_filter_info); 223362306a36Sopenharmony_ci } 223462306a36Sopenharmony_ci 223562306a36Sopenharmony_ci kfree(adap->ethtool_filters); 223662306a36Sopenharmony_ci} 223762306a36Sopenharmony_ci 223862306a36Sopenharmony_ciint cxgb4_init_ethtool_filters(struct adapter *adap) 223962306a36Sopenharmony_ci{ 224062306a36Sopenharmony_ci struct cxgb4_ethtool_filter_info *eth_filter_info; 224162306a36Sopenharmony_ci struct cxgb4_ethtool_filter *eth_filter; 224262306a36Sopenharmony_ci struct tid_info *tids = &adap->tids; 224362306a36Sopenharmony_ci u32 nentries, i; 224462306a36Sopenharmony_ci int ret; 224562306a36Sopenharmony_ci 224662306a36Sopenharmony_ci eth_filter = kzalloc(sizeof(*eth_filter), GFP_KERNEL); 224762306a36Sopenharmony_ci if (!eth_filter) 224862306a36Sopenharmony_ci return -ENOMEM; 224962306a36Sopenharmony_ci 225062306a36Sopenharmony_ci eth_filter_info = kcalloc(adap->params.nports, 225162306a36Sopenharmony_ci sizeof(*eth_filter_info), 225262306a36Sopenharmony_ci GFP_KERNEL); 225362306a36Sopenharmony_ci if (!eth_filter_info) { 225462306a36Sopenharmony_ci ret = -ENOMEM; 225562306a36Sopenharmony_ci goto free_eth_filter; 225662306a36Sopenharmony_ci } 225762306a36Sopenharmony_ci 225862306a36Sopenharmony_ci eth_filter->port = eth_filter_info; 225962306a36Sopenharmony_ci 226062306a36Sopenharmony_ci nentries = tids->nhpftids + tids->nftids; 226162306a36Sopenharmony_ci if (is_hashfilter(adap)) 226262306a36Sopenharmony_ci nentries += tids->nhash + 226362306a36Sopenharmony_ci (adap->tids.stid_base - adap->tids.tid_base); 226462306a36Sopenharmony_ci eth_filter->nentries = nentries; 226562306a36Sopenharmony_ci 226662306a36Sopenharmony_ci for (i = 0; i < adap->params.nports; i++) { 226762306a36Sopenharmony_ci eth_filter->port[i].loc_array = kvzalloc(nentries, GFP_KERNEL); 226862306a36Sopenharmony_ci if (!eth_filter->port[i].loc_array) { 226962306a36Sopenharmony_ci ret = -ENOMEM; 227062306a36Sopenharmony_ci goto free_eth_finfo; 227162306a36Sopenharmony_ci } 227262306a36Sopenharmony_ci 227362306a36Sopenharmony_ci eth_filter->port[i].bmap = bitmap_zalloc(nentries, GFP_KERNEL); 227462306a36Sopenharmony_ci if (!eth_filter->port[i].bmap) { 227562306a36Sopenharmony_ci ret = -ENOMEM; 227662306a36Sopenharmony_ci goto free_eth_finfo; 227762306a36Sopenharmony_ci } 227862306a36Sopenharmony_ci } 227962306a36Sopenharmony_ci 228062306a36Sopenharmony_ci adap->ethtool_filters = eth_filter; 228162306a36Sopenharmony_ci return 0; 228262306a36Sopenharmony_ci 228362306a36Sopenharmony_cifree_eth_finfo: 228462306a36Sopenharmony_ci while (i-- > 0) { 228562306a36Sopenharmony_ci bitmap_free(eth_filter->port[i].bmap); 228662306a36Sopenharmony_ci kvfree(eth_filter->port[i].loc_array); 228762306a36Sopenharmony_ci } 228862306a36Sopenharmony_ci kfree(eth_filter_info); 228962306a36Sopenharmony_ci 229062306a36Sopenharmony_cifree_eth_filter: 229162306a36Sopenharmony_ci kfree(eth_filter); 229262306a36Sopenharmony_ci 229362306a36Sopenharmony_ci return ret; 229462306a36Sopenharmony_ci} 229562306a36Sopenharmony_ci 229662306a36Sopenharmony_civoid cxgb4_set_ethtool_ops(struct net_device *netdev) 229762306a36Sopenharmony_ci{ 229862306a36Sopenharmony_ci netdev->ethtool_ops = &cxgb_ethtool_ops; 229962306a36Sopenharmony_ci} 2300