// SPDX-License-Identifier: GPL-2.0 /* Copyright(c) 2022 - 2024 Mucse Corporation. */ /* ethtool support for N10M */ #include #include #include #include #include #include #include #include #include #include #include #include "rnpm.h" #include "rnpm_mpe.h" #include "rnpm_mbx.h" #include "rnpm_phy.h" #include "rnpm_sriov.h" #include "rnpm_mbx_fw.h" // #ifdef SIOCETHTOOL #define RNPM_ALL_RAR_ENTRIES 16 // #ifdef ETHTOOL_TEST enum { NETDEV_STATS, RNPM_STATS }; struct rnpm_stats { char stat_string[ETH_GSTRING_LEN]; int sizeof_stat; int stat_offset; }; /* rnpm allocates num_tx_queues and num_rx_queues symmetrically so * we set the num_rx_queues to evaluate to num_tx_queues. This is * used because we do not have a good way to get the max number of * rx queues with CONFIG_RPS disabled. */ #define RNPM_NUM_RX_QUEUES netdev->real_num_rx_queues #define RNPM_NUM_TX_QUEUES netdev->real_num_tx_queues #define RNPM_NETDEV_STAT(_net_stat) \ { \ .stat_string = #_net_stat, \ .sizeof_stat = \ sizeof_field(struct net_device_stats, _net_stat), \ .stat_offset = offsetof(struct net_device_stats, _net_stat) \ } static const struct rnpm_stats rnpm_gstrings_net_stats[] = { RNPM_NETDEV_STAT(rx_packets), RNPM_NETDEV_STAT(tx_packets), RNPM_NETDEV_STAT(rx_bytes), RNPM_NETDEV_STAT(tx_bytes), RNPM_NETDEV_STAT(rx_errors), RNPM_NETDEV_STAT(tx_errors), RNPM_NETDEV_STAT(rx_dropped), RNPM_NETDEV_STAT(tx_dropped), RNPM_NETDEV_STAT(multicast), RNPM_NETDEV_STAT(collisions), RNPM_NETDEV_STAT(rx_over_errors), RNPM_NETDEV_STAT(rx_crc_errors), RNPM_NETDEV_STAT(rx_frame_errors), RNPM_NETDEV_STAT(rx_fifo_errors), RNPM_NETDEV_STAT(rx_missed_errors), RNPM_NETDEV_STAT(tx_aborted_errors), RNPM_NETDEV_STAT(tx_carrier_errors), RNPM_NETDEV_STAT(tx_fifo_errors), RNPM_NETDEV_STAT(tx_heartbeat_errors), }; #define RNPM_GLOBAL_STATS_LEN ARRAY_SIZE(rnpm_gstrings_net_stats) #define RNPM_HW_STAT(_name, _stat) \ { \ .stat_string = _name, \ .sizeof_stat = sizeof_field(struct rnpm_adapter, _stat), \ .stat_offset = offsetof(struct rnpm_adapter, _stat) \ } static struct rnpm_stats rnpm_hwstrings_stats[] = { RNPM_HW_STAT("dma_to_eth", hw_stats.dma_to_eth), RNPM_HW_STAT("dma_to_switch", hw_stats.dma_to_switch), // RNPM_HW_STAT("mac_to_mac", hw_stats.mac_to_mac), // RNPM_HW_STAT("switch_to_switch", hw_stats.switch_to_switch), RNPM_HW_STAT("eth_to_dma", hw_stats.mac_to_dma), RNPM_HW_STAT("switch_to_dma", hw_stats.switch_to_dma), RNPM_HW_STAT("vlan_add_cnt", hw_stats.vlan_add_cnt), RNPM_HW_STAT("vlan_strip_cnt", hw_stats.vlan_strip_cnt), //=== drop== // RNPM_HW_STAT("invalid_droped_packets", hw_stats.invalid_droped_packets), // RNPM_HW_STAT("filter_dropped_packets", hw_stats.filter_dropped_packets), // RNPM_HW_STAT("host_l2_match_drop", hw_stats.host_l2_match_drop), // RNPM_HW_STAT("redir_input_match_drop", hw_stats.redir_input_match_drop), // RNPM_HW_STAT("redir_etype_match_drop", hw_stats.redir_etype_match_drop), // RNPM_HW_STAT("redir_tcp_syn_match_drop", // hw_stats.redir_tcp_syn_match_drop), // RNPM_HW_STAT("redir_tuple5_match_drop", // hw_stats.redir_tuple5_match_drop), RNPM_HW_STAT("redir_tcam_match_drop", // hw_stats.redir_tcam_match_drop), // RNPM_HW_STAT("bmc_dropped_packets", hw_stats.bmc_dropped_packets), // RNPM_HW_STAT("switch_dropped_packets", hw_stats.switch_dropped_packets), RNPM_HW_STAT("rx_csum_offload_errors", hw_csum_rx_error), RNPM_HW_STAT("rx_csum_offload_good", hw_csum_rx_good), RNPM_HW_STAT("rx_broadcast_count", hw_stats.mac_rx_broadcast), RNPM_HW_STAT("rx_multicast_count", hw_stats.mac_rx_multicast), RNPM_HW_STAT("mac_rx_pause_cnt", hw_stats.mac_rx_pause_cnt), RNPM_HW_STAT("mac_tx_pause_cnt", hw_stats.mac_tx_pause_cnt), }; #define RNPM_HWSTRINGS_STATS_LEN ARRAY_SIZE(rnpm_hwstrings_stats) struct rnpm_tx_queue_ring_stat { u64 hw_head; u64 hw_tail; u64 sw_to_clean; u64 sw_to_next_to_use; }; struct rnpm_rx_queue_ring_stat { u64 hw_head; u64 hw_tail; u64 sw_to_use; u64 sw_to_clean; }; #define RNPM_QUEUE_STATS_LEN \ (RNPM_NUM_TX_QUEUES * \ (sizeof(struct rnpm_tx_queue_stats) / sizeof(u64) + \ sizeof(struct rnpm_queue_stats) / sizeof(u64) + \ sizeof(struct rnpm_tx_queue_ring_stat) / sizeof(u64)) + \ RNPM_NUM_RX_QUEUES * \ (sizeof(struct rnpm_rx_queue_stats) / sizeof(u64) + \ sizeof(struct rnpm_queue_stats) / sizeof(u64) + \ sizeof(struct rnpm_rx_queue_ring_stat) / sizeof(u64))) #define RNPM_STATS_LEN \ (RNPM_GLOBAL_STATS_LEN + RNPM_HWSTRINGS_STATS_LEN + \ RNPM_QUEUE_STATS_LEN) #ifdef ETHTOOL_TEST static const char rnpm_gstrings_test[][ETH_GSTRING_LEN] = { "Register test (offline)", "Eeprom test (offline)", "Interrupt test (offline)", "Loopback test (offline)", "Link test (on/offline)" }; #define RNPM_TEST_LEN (sizeof(rnpm_gstrings_test) / ETH_GSTRING_LEN) #else #define RNPM_TEST_LEN 0 #endif static int rnpm_get_regs_len(struct net_device *netdev) { // #define RNPM_REGS_LEN 1129 #define RNPM_REGS_LEN 1 return RNPM_REGS_LEN * sizeof(u32); } static void rnpm_get_regs(struct net_device *netdev, struct ethtool_regs *regs, void *p) { struct rnpm_adapter *adapter = netdev_priv(netdev); struct rnpm_hw *hw = &adapter->hw; u32 *regs_buff = p; int i; memset(p, 0, RNPM_REGS_LEN * sizeof(u32)); for (i = 0; i < RNPM_REGS_LEN; i++) regs_buff[i] = rd32(hw, i * sizeof(u32)); } static const char rnpm_priv_flags_strings[][ETH_GSTRING_LEN] = { #define RNPM_MAC_LOOPBACK BIT(0) #define RNPM_SWITCH_LOOPBACK BIT(1) #define RNPM_VEB_ENABLE BIT(2) #define RNPM_PCIE_CACHE_ALIGN_PATCH BIT(3) #define RNPM_PADDING_DEBUG BIT(4) #define RNPM_PTP_FEATURE BIT(5) #define RNPM_SIMULATE_DOWN BIT(6) #define RNPM_TO_RPU BIT(7) #define RNPM_LEN_ERR BIT(8) #define RNPM_FW_10G_1G_SFP_AUTO_DET_EN BIT(9) #define RNPM_MPE_RELOAD BIT(10) #define RNPM_FORCE_SPEED_ABLITY BIT(11) #define RNPM_LLDP_EN_STAT BIT(12) "mac_loopback", "switch_loopback", "veb_enable", "pcie_patch", "padding_debug", "ptp_performance_debug", "simulate_link_down", "to_rpu", "mask_len_err", "fw_10g_1g_auto_det", "mpe_reload", "force_speed_ablity", "lldp_en" }; #define RNPM_PRIV_FLAGS_STR_LEN ARRAY_SIZE(rnpm_priv_flags_strings) static const char rnpm_phy_statistics_strings[][ETH_GSTRING_LEN] = { "RX crc good (64~1518)", "RX crc good (>1518)", "RX crc good (<64)", "RX crc wrong (64~1518)", "RX crc wrong (>1518)", "RX crc wrong (<64)", "RX SFD missed (nosfd)", "TX crc good (64~1518)", "TX crc good (>1518)", "TX crc good (<64)", "TX crc wrong (64~1518)", "TX crc wrong (>1518)", "TX crc wrong (<64)", "TX SFD missed (nosfd)", }; #define RNPM_PHY_STATISTICS_STR_LEN ARRAY_SIZE(rnpm_phy_statistics_strings) static void rnpm_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo) { struct rnpm_adapter *adapter = netdev_priv(netdev); struct rnpm_hw *hw = &adapter->hw; strscpy(drvinfo->driver, rnpm_driver_name, sizeof(drvinfo->driver)); snprintf(drvinfo->version, sizeof(drvinfo->version), "%s-%x", rnpm_driver_version, hw->ccode); snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version), "%d.%d.%d.%d 0x%08x", ((char *)&(hw->fw_version))[3], ((char *)&(hw->fw_version))[2], ((char *)&(hw->fw_version))[1], ((char *)&(hw->fw_version))[0], hw->fw_uid); strscpy(drvinfo->bus_info, pci_name(adapter->pdev), sizeof(drvinfo->bus_info)); drvinfo->n_stats = RNPM_STATS_LEN; drvinfo->testinfo_len = RNPM_TEST_LEN; drvinfo->regdump_len = rnpm_get_regs_len(netdev); drvinfo->n_priv_flags = RNPM_PRIV_FLAGS_STR_LEN; } static int rnpm_set_autoneg_adv_from_hw(struct rnpm_hw *hw, struct ethtool_link_ksettings *ks) { /* Read autoneg state from phy */ if (hw->phy_type == PHY_TYPE_SGMII) { /* Not support AN, return directly */ if (!(hw->phy.vb_r[0] & BIT(12)) || !hw->link) return 0; if (hw->phy.vb_r[4] & 0x100) ethtool_link_ksettings_add_link_mode(ks, advertising, 100baseT_Full); if (hw->phy.vb_r[4] & 0x80) ethtool_link_ksettings_add_link_mode(ks, advertising, 100baseT_Half); if (hw->phy.vb_r[4] & 0x40) ethtool_link_ksettings_add_link_mode(ks, advertising, 10baseT_Full); if (hw->phy.vb_r[4] & 0x20) ethtool_link_ksettings_add_link_mode(ks, advertising, 10baseT_Half); if (hw->phy.vb_r[9] & 0x200) ethtool_link_ksettings_add_link_mode(ks, advertising, 1000baseT_Full); if (hw->phy.vb_r[9] & 0x100) ethtool_link_ksettings_add_link_mode(ks, advertising, 1000baseT_Half); } return 0; } /** * rnpm_phy_type_to_ethtool - convert the phy_types to ethtool link modes * @adapter: adapter struct with hw->phy_type * @ks: ethtool link ksettings struct to fill out * **/ static void rnpm_phy_type_to_ethtool(struct rnpm_adapter *adapter, struct ethtool_link_ksettings *ks) { struct rnpm_hw *hw = &adapter->hw; u32 supported_link = hw->supported_link; u8 phy_type = hw->phy_type; ethtool_link_ksettings_add_link_mode(ks, supported, Autoneg); ethtool_link_ksettings_add_link_mode(ks, advertising, Autoneg); rnpm_logd(LOG_ETHTOOL, "phy_type_to_ethtool name=%s link=%d speed=%d phy-type=0x%x ", adapter->netdev->name, hw->link, hw->speed, phy_type); rnpm_logd(LOG_ETHTOOL, "sopport-link=0x%x media=0x%x priv_flags=0x%x\n ", supported_link, hw->phy.media_type, adapter->pf_adapter->priv_flags); /* ethtool show all support fiber type when media is unknown */ if (hw->phy.media_type == rnpm_media_type_unknown) { if (hw->speed == SPEED_10000) { ethtool_link_ksettings_add_link_mode(ks, supported, 10000baseT_Full); ethtool_link_ksettings_add_link_mode(ks, advertising, 10000baseT_Full); ethtool_link_ksettings_add_link_mode(ks, supported, 10000baseSR_Full); ethtool_link_ksettings_add_link_mode(ks, advertising, 10000baseSR_Full); ethtool_link_ksettings_add_link_mode(ks, supported, 10000baseLR_Full); ethtool_link_ksettings_add_link_mode(ks, advertising, 10000baseLR_Full); ethtool_link_ksettings_add_link_mode(ks, supported, 10000baseER_Full); ethtool_link_ksettings_add_link_mode(ks, advertising, 10000baseER_Full); if (adapter->pf_adapter->priv_flags & RNPM_PRIV_FLAG_FW_10G_1G_AUTO_DETCH_EN) { ethtool_link_ksettings_add_link_mode( ks, supported, 1000baseX_Full); ethtool_link_ksettings_add_link_mode( ks, advertising, 1000baseX_Full); ethtool_link_ksettings_add_link_mode( ks, supported, 1000baseT_Full); ethtool_link_ksettings_add_link_mode( ks, advertising, 1000baseT_Full); ethtool_link_ksettings_add_link_mode( ks, supported, 1000baseKX_Full); ethtool_link_ksettings_add_link_mode( ks, advertising, 1000baseKX_Full); } } else { ethtool_link_ksettings_add_link_mode(ks, supported, 1000baseX_Full); ethtool_link_ksettings_add_link_mode(ks, advertising, 1000baseX_Full); ethtool_link_ksettings_add_link_mode(ks, supported, 1000baseT_Full); ethtool_link_ksettings_add_link_mode(ks, advertising, 1000baseT_Full); ethtool_link_ksettings_add_link_mode(ks, supported, 1000baseKX_Full); ethtool_link_ksettings_add_link_mode(ks, advertising, 1000baseKX_Full); } /* when media type is unknown, return directly */ return; } if (phy_type == PHY_TYPE_SGMII) { ethtool_link_ksettings_add_link_mode(ks, supported, 1000baseT_Full); ethtool_link_ksettings_add_link_mode(ks, supported, 100baseT_Full); ethtool_link_ksettings_add_link_mode(ks, supported, 10baseT_Full); ethtool_link_ksettings_add_link_mode(ks, supported, 100baseT_Half); ethtool_link_ksettings_add_link_mode(ks, supported, 10baseT_Half); rnpm_set_autoneg_adv_from_hw(hw, ks); } if (rnpm_fw_is_old_ethtool(hw) && (supported_link & RNPM_LINK_SPEED_40GB_FULL)) { supported_link |= RNPM_SFP_MODE_40G_CR4 | RNPM_SFP_MODE_40G_SR4 | PHY_TYPE_40G_BASE_LR4; } if (supported_link & RNPM_SFP_MODE_40G_CR4) { ethtool_link_ksettings_add_link_mode(ks, supported, 40000baseCR4_Full); ethtool_link_ksettings_add_link_mode(ks, advertising, 40000baseCR4_Full); } if (supported_link & RNPM_SFP_MODE_40G_SR4) { ethtool_link_ksettings_add_link_mode(ks, supported, 40000baseSR4_Full); ethtool_link_ksettings_add_link_mode(ks, advertising, 40000baseSR4_Full); } if (supported_link & RNPM_SFP_MODE_40G_LR4) { ethtool_link_ksettings_add_link_mode(ks, supported, 40000baseLR4_Full); ethtool_link_ksettings_add_link_mode(ks, advertising, 40000baseLR4_Full); } if (hw->is_backplane) { if (phy_type == RNPM_LINK_SPEED_40GB_FULL) { ethtool_link_ksettings_add_link_mode(ks, supported, 40000baseKR4_Full); ethtool_link_ksettings_add_link_mode(ks, advertising, 40000baseKR4_Full); } if (phy_type == PHY_TYPE_10G_BASE_KR) { ethtool_link_ksettings_add_link_mode(ks, supported, 10000baseKR_Full); if (supported_link & RNPM_LINK_SPEED_10GB_FULL) ethtool_link_ksettings_add_link_mode( ks, advertising, 10000baseKR_Full); } } if (phy_type == PHY_TYPE_1G_BASE_KX) { if (hw->is_backplane) { ethtool_link_ksettings_add_link_mode(ks, supported, 1000baseKX_Full); if (supported_link & RNPM_LINK_SPEED_1GB_FULL) ethtool_link_ksettings_add_link_mode( ks, advertising, 1000baseKX_Full); } else if (supported_link & RNPM_SFP_MODE_1G_T) { ethtool_link_ksettings_add_link_mode(ks, supported, 1000baseT_Full); if (supported_link & RNPM_LINK_SPEED_1GB_FULL) ethtool_link_ksettings_add_link_mode( ks, advertising, 1000baseT_Full); } else { ethtool_link_ksettings_add_link_mode(ks, supported, 1000baseX_Full); if (supported_link & RNPM_LINK_SPEED_1GB_FULL) ethtool_link_ksettings_add_link_mode( ks, advertising, 1000baseX_Full); } } /* need to add new 10G PHY types */ if (phy_type == PHY_TYPE_10G_BASE_SR) { ethtool_link_ksettings_add_link_mode(ks, supported, 10000baseSR_Full); if (supported_link & RNPM_LINK_SPEED_10GB_FULL) ethtool_link_ksettings_add_link_mode(ks, advertising, 10000baseSR_Full); } if (phy_type == PHY_TYPE_10G_BASE_ER) { ethtool_link_ksettings_add_link_mode(ks, supported, 10000baseER_Full); if (supported_link & RNPM_LINK_SPEED_10GB_FULL) ethtool_link_ksettings_add_link_mode(ks, advertising, 10000baseER_Full); } if (phy_type == PHY_TYPE_10G_BASE_LR) { ethtool_link_ksettings_add_link_mode(ks, supported, 10000baseLR_Full); if (supported_link & RNPM_LINK_SPEED_10GB_FULL) ethtool_link_ksettings_add_link_mode(ks, advertising, 10000baseLR_Full); } if (phy_type == PHY_TYPE_10G_BASE_SR || phy_type == PHY_TYPE_10G_BASE_ER || phy_type == PHY_TYPE_10G_BASE_LR) { if ((hw->speed == SPEED_1000) || (supported_link & RNPM_LINK_SPEED_1GB_FULL)) { ethtool_link_ksettings_add_link_mode(ks, supported, 1000baseX_Full); if (supported_link & RNPM_LINK_SPEED_10GB_FULL) ethtool_link_ksettings_add_link_mode( ks, advertising, 1000baseX_Full); } } } /** * rnpm_get_settings_link_up - Get Link settings for when link is up * @hw: hw structure * @ks: ethtool ksettings to fill in * @netdev: network interface device structure **/ static void rnpm_get_settings_link_up(struct rnpm_hw *hw, struct ethtool_link_ksettings *ks, struct net_device *netdev) { struct rnpm_adapter *adapter = netdev_priv(netdev); struct ethtool_link_ksettings cap_ksettings; u32 supported_link = hw->supported_link; /* Initialize supported and advertised settings based on phy settings */ switch (hw->phy_type) { case PHY_TYPE_40G_BASE_CR4: ethtool_link_ksettings_add_link_mode(ks, supported, Autoneg); ethtool_link_ksettings_add_link_mode(ks, supported, 40000baseCR4_Full); ethtool_link_ksettings_add_link_mode(ks, advertising, Autoneg); ethtool_link_ksettings_add_link_mode(ks, advertising, 40000baseCR4_Full); break; case PHY_TYPE_40G_BASE_SR4: ethtool_link_ksettings_add_link_mode(ks, supported, 40000baseSR4_Full); ethtool_link_ksettings_add_link_mode(ks, advertising, 40000baseSR4_Full); break; case PHY_TYPE_40G_BASE_LR4: ethtool_link_ksettings_add_link_mode(ks, supported, 40000baseLR4_Full); ethtool_link_ksettings_add_link_mode(ks, advertising, 40000baseLR4_Full); break; case PHY_TYPE_10G_BASE_SR: case PHY_TYPE_10G_BASE_LR: case PHY_TYPE_10G_BASE_ER: ethtool_link_ksettings_add_link_mode(ks, supported, Autoneg); ethtool_link_ksettings_add_link_mode(ks, advertising, Autoneg); ethtool_link_ksettings_add_link_mode(ks, supported, 10000baseSR_Full); ethtool_link_ksettings_add_link_mode(ks, advertising, 10000baseSR_Full); ethtool_link_ksettings_add_link_mode(ks, supported, 10000baseLR_Full); ethtool_link_ksettings_add_link_mode(ks, advertising, 10000baseLR_Full); ethtool_link_ksettings_add_link_mode(ks, supported, 10000baseER_Full); ethtool_link_ksettings_add_link_mode(ks, advertising, 10000baseER_Full); ethtool_link_ksettings_add_link_mode(ks, supported, 10000baseT_Full); if (hw->speed == SPEED_10000) ethtool_link_ksettings_add_link_mode(ks, advertising, 10000baseT_Full); if ((hw->speed == SPEED_1000) || (supported_link & RNPM_LINK_SPEED_1GB_FULL)) { ethtool_link_ksettings_add_link_mode(ks, supported, 1000baseX_Full); ethtool_link_ksettings_add_link_mode(ks, advertising, 1000baseX_Full); ethtool_link_ksettings_add_link_mode(ks, supported, 1000baseT_Full); ethtool_link_ksettings_add_link_mode(ks, advertising, 1000baseT_Full); } break; case PHY_TYPE_1G_BASE_KX: ethtool_link_ksettings_add_link_mode(ks, supported, Autoneg); ethtool_link_ksettings_add_link_mode(ks, advertising, Autoneg); if (!!hw->is_backplane) { ethtool_link_ksettings_add_link_mode(ks, supported, 1000baseKX_Full); ethtool_link_ksettings_add_link_mode(ks, advertising, 1000baseKX_Full); } ethtool_link_ksettings_add_link_mode(ks, supported, 1000baseX_Full); ethtool_link_ksettings_add_link_mode(ks, advertising, 1000baseX_Full); ethtool_link_ksettings_add_link_mode(ks, supported, 1000baseT_Full); ethtool_link_ksettings_add_link_mode(ks, advertising, 1000baseT_Full); break; case PHY_TYPE_SGMII: ethtool_link_ksettings_add_link_mode(ks, supported, Autoneg); ethtool_link_ksettings_add_link_mode(ks, advertising, Autoneg); ethtool_link_ksettings_add_link_mode(ks, supported, 1000baseT_Full); ethtool_link_ksettings_add_link_mode(ks, supported, 100baseT_Full); ethtool_link_ksettings_add_link_mode(ks, supported, 10baseT_Full); ethtool_link_ksettings_add_link_mode(ks, supported, 1000baseT_Half); ethtool_link_ksettings_add_link_mode(ks, supported, 100baseT_Half); ethtool_link_ksettings_add_link_mode(ks, supported, 10baseT_Half); ethtool_link_ksettings_add_link_mode(ks, advertising, 1000baseT_Full); ethtool_link_ksettings_add_link_mode(ks, advertising, 100baseT_Full); ethtool_link_ksettings_add_link_mode(ks, advertising, 10baseT_Full); ethtool_link_ksettings_add_link_mode(ks, advertising, 1000baseT_Half); ethtool_link_ksettings_add_link_mode(ks, advertising, 100baseT_Half); ethtool_link_ksettings_add_link_mode(ks, advertising, 10baseT_Half); break; case PHY_TYPE_40G_BASE_KR4: case PHY_TYPE_10G_BASE_KR: ethtool_link_ksettings_add_link_mode(ks, supported, Autoneg); ethtool_link_ksettings_add_link_mode(ks, advertising, Autoneg); ethtool_link_ksettings_add_link_mode(ks, supported, 40000baseKR4_Full); ethtool_link_ksettings_add_link_mode(ks, supported, 10000baseKR_Full); ethtool_link_ksettings_add_link_mode(ks, supported, 1000baseKX_Full); ethtool_link_ksettings_add_link_mode(ks, supported, 10000baseKX4_Full); ethtool_link_ksettings_add_link_mode(ks, advertising, 40000baseKR4_Full); ethtool_link_ksettings_add_link_mode(ks, advertising, 10000baseKR_Full); ethtool_link_ksettings_add_link_mode(ks, advertising, 10000baseKX4_Full); ethtool_link_ksettings_add_link_mode(ks, advertising, 1000baseKX_Full); break; default: /* if we got here and link is up something bad is afoot */ netdev_info(netdev, "WARNING: Link is up but PHY type 0x%x is not ", hw->phy_type); netdev_info(netdev, "recognized, or incorrect cable is in use\n"); } /* Now that we've worked out everything that could be supported by the * current PHY type, get what is supported by the NVM and intersect * them to get what is truly supported */ memset(&cap_ksettings, 0, sizeof(struct ethtool_link_ksettings)); rnpm_phy_type_to_ethtool(adapter, &cap_ksettings); ethtool_intersect_link_masks(ks, &cap_ksettings); /* Set speed and duplex */ ks->base.speed = adapter->speed; ks->base.duplex = hw->duplex; } /** * rnpm_get_settings_link_down - Get the Link settings when link is down * @hw: hw structure * @ks: ethtool ksettings to fill in * @netdev: network interface device structure * * Reports link settings that can be determined when link is down **/ static void rnpm_get_settings_link_down(struct rnpm_hw *hw, struct ethtool_link_ksettings *ks, struct net_device *netdev) { struct rnpm_adapter *adapter = netdev_priv(netdev); /* link is down and the driver needs to fall back on * supported phy types to figure out what info to display */ rnpm_phy_type_to_ethtool(adapter, ks); /* With no link speed and duplex are unknown */ ks->base.speed = SPEED_UNKNOWN; ks->base.duplex = hw->duplex; } /** * rnpm_set_autoneg_state_from_hw - Set the autoneg state from hardware * @hw: hw structure * @ks: ethtool ksettings to fill in * * Set the autoneg state from hardware, like PHY **/ static int rnpm_set_autoneg_state_from_hw(struct rnpm_hw *hw, struct ethtool_link_ksettings *ks) { struct rnpm_adapter *adapter = hw->back; ks->base.autoneg = (adapter->an ? AUTONEG_ENABLE : AUTONEG_DISABLE); /* Read autoneg state from phy */ if (hw->phy_type == PHY_TYPE_SGMII) ks->base.autoneg = hw->phy.an; return 0; } __maybe_unused static int rnpm_get_phy_mdix_from_hw(struct rnpm_hw *hw) { return 0; } __maybe_unused static bool fiber_unsupport(u32 supported_link, u8 phy_type) { if ((phy_type == PHY_TYPE_10G_BASE_KR) || (phy_type == PHY_TYPE_10G_BASE_SR) || (phy_type == PHY_TYPE_10G_BASE_LR) || (phy_type == PHY_TYPE_10G_BASE_ER)) { if (!(supported_link & RNPM_LINK_SPEED_10GB_FULL)) return true; } if ((phy_type == PHY_TYPE_40G_BASE_KR4) || (phy_type == PHY_TYPE_40G_BASE_SR4) || (phy_type == PHY_TYPE_40G_BASE_CR4) || (phy_type == PHY_TYPE_40G_BASE_LR4)) { if (!(supported_link & RNPM_LINK_SPEED_40GB_FULL)) return true; } if (phy_type == PHY_TYPE_1G_BASE_KX) { if (!(supported_link & RNPM_LINK_SPEED_1GB_FULL)) return true; } return false; } static bool rnpm_is_unknown_media(struct rnpm_hw *hw) { return false; } static void rnpm_redefine_phy_type(struct rnpm_adapter *adapter) { struct rnpm_hw *hw = &adapter->hw; if (adapter->pf_adapter->priv_flags & RNPM_PRIV_FLAG_FW_10G_1G_AUTO_DETCH_EN) { // if (hw->phy_type == PHY_TYPE_1G_BASE_KX) { if ((hw->speed == SPEED_1000) || ((hw->phy_type == PHY_TYPE_1G_BASE_KX) || (hw->phy_type == PHY_TYPE_SGMII))) { if (hw->supported_link & RNPM_LINK_SPEED_10GB_FULL) { if (hw->supported_link & RNPM_SFP_MODE_10G_LR) hw->phy_type = PHY_TYPE_10G_BASE_LR; if (hw->supported_link & RNPM_SFP_MODE_10G_SR) hw->phy_type = PHY_TYPE_10G_BASE_SR; if (hw->supported_link & RNPM_SFP_MODE_10G_LRM) hw->phy_type = PHY_TYPE_10G_BASE_LR; if (hw->supported_link & RNPM_SFP_MODE_10G_BASE_T) hw->phy_type = PHY_TYPE_10G_BASE_KR; } } else { // if (hw->speed == SPEED_1000) // hw->phy_type = PHY_TYPE_1G_BASE_KX; } } } static void rnpm_get_media_type(struct rnpm_hw *hw) { switch (hw->phy_type) { case PHY_TYPE_NONE: hw->phy.media_type = rnpm_media_type_unknown; break; case PHY_TYPE_1G_BASE_KX: if (hw->is_backplane) hw->phy.media_type = rnpm_media_type_backplane; else if (hw->is_sgmii) hw->phy.media_type = rnpm_media_type_copper; else { if ((hw->supported_link & RNPM_LINK_SPEED_1GB_FULL) || (hw->supported_link & RNPM_SFP_MODE_1G_LX)) hw->phy.media_type = rnpm_media_type_fiber; else hw->phy.media_type = rnpm_media_type_unknown; } break; case PHY_TYPE_SGMII: hw->phy.media_type = rnpm_media_type_copper; // ks->base.phy_address = adapter->phy_addr; break; case PHY_TYPE_10G_BASE_KR: case PHY_TYPE_25G_BASE_KR: case PHY_TYPE_40G_BASE_KR4: hw->phy.media_type = rnpm_media_type_backplane; break; case PHY_TYPE_10G_BASE_SR: case PHY_TYPE_40G_BASE_SR4: case PHY_TYPE_40G_BASE_CR4: case PHY_TYPE_40G_BASE_LR4: case PHY_TYPE_10G_BASE_LR: case PHY_TYPE_10G_BASE_ER: hw->phy.media_type = rnpm_media_type_fiber; break; default: hw->phy.media_type = rnpm_media_type_unknown; break; } if (hw->supported_link & RNPM_SFP_CONNECTOR_DAC) hw->phy.media_type = rnpm_media_type_da; if ((hw->supported_link & RNPM_SFP_TO_SGMII) || (hw->supported_link & RNPM_SFP_MODE_1G_T)) { hw->phy.media_type = rnpm_media_type_copper; } } /** * rnpm_get_link_ksettings - Get Link Speed and Duplex settings * @netdev: network interface device structure * @ks: ethtool ksettings * * Reports speed/duplex settings based on media_type **/ static int rnpm_get_link_ksettings(struct net_device *netdev, struct ethtool_link_ksettings *ks) { struct rnpm_adapter *adapter = netdev_priv(netdev); struct rnpm_hw *hw = &adapter->hw; bool link_up; if (test_bit(__RNPM_REMOVING, &adapter->pf_adapter->state)) return -1; ethtool_link_ksettings_zero_link_mode(ks, supported); ethtool_link_ksettings_zero_link_mode(ks, advertising); /* update hw from firmware */ if (test_bit(__RNPM_DOWN, &adapter->pf_adapter->state) || test_bit(__RNPM_RESETTING, &adapter->pf_adapter->state)) return -1; /* when turn on auto speed, the phy_type equal 1G is unreliable */ rnpm_redefine_phy_type(adapter); /* update hw->phy.media_type by hw->phy_type */ rnpm_get_media_type(hw); if (hw->phy_type == PHY_TYPE_SGMII) ks->base.phy_address = adapter->phy_addr; /* Check Whether there is media on port */ if (hw->phy.media_type == rnpm_media_type_fiber) { /* If adapter->sfp.mod_abs is 0, there is no media on port. */ if (!adapter->sfp.mod_abs) { hw->phy.media_type = rnpm_media_type_unknown; rnpm_logd(LOG_ETHTOOL, "%s absent, set media type is unknown\n", adapter->netdev->name); } } if (rnpm_is_unknown_media(hw)) hw->phy.media_type = rnpm_media_type_unknown; /* Now set the settings that don't rely on link being up/down */ /* Set autoneg settings */ rnpm_set_autoneg_state_from_hw(hw, ks); link_up = hw->link; if (link_up) rnpm_get_settings_link_up(hw, ks, netdev); else rnpm_get_settings_link_down(hw, ks, netdev); /* Set media type settings */ switch (hw->phy.media_type) { case rnpm_media_type_backplane: ethtool_link_ksettings_add_link_mode(ks, supported, Backplane); ethtool_link_ksettings_add_link_mode(ks, advertising, Backplane); ethtool_link_ksettings_add_link_mode(ks, supported, Autoneg); ethtool_link_ksettings_add_link_mode(ks, advertising, Autoneg); ks->base.port = PORT_NONE; break; case rnpm_media_type_copper: ethtool_link_ksettings_add_link_mode(ks, supported, TP); ethtool_link_ksettings_add_link_mode(ks, advertising, TP); if (hw->phy_type == PHY_TYPE_SGMII) ethtool_link_ksettings_add_link_mode(ks, supported, Autoneg); if (ks->base.autoneg == AUTONEG_ENABLE) ethtool_link_ksettings_add_link_mode(ks, advertising, Autoneg); else ethtool_link_ksettings_del_link_mode(ks, advertising, Autoneg); ks->base.port = PORT_TP; break; case rnpm_media_type_da: case rnpm_media_type_cx4: ethtool_link_ksettings_add_link_mode(ks, supported, FIBRE); ethtool_link_ksettings_add_link_mode(ks, advertising, FIBRE); ks->base.port = PORT_DA; break; case rnpm_media_type_fiber: ethtool_link_ksettings_add_link_mode(ks, supported, FIBRE); ethtool_link_ksettings_add_link_mode(ks, advertising, FIBRE); ks->base.port = PORT_FIBRE; break; case rnpm_media_type_unknown: default: ethtool_link_ksettings_add_link_mode(ks, supported, Autoneg); ethtool_link_ksettings_add_link_mode(ks, advertising, Autoneg); ks->base.port = PORT_OTHER; break; } /* Set flow control settings */ ethtool_link_ksettings_add_link_mode(ks, supported, Pause); ethtool_link_ksettings_add_link_mode(ks, supported, Asym_Pause); switch (hw->fc.requested_mode) { case rnpm_fc_full: ethtool_link_ksettings_add_link_mode(ks, advertising, Pause); break; case rnpm_fc_tx_pause: ethtool_link_ksettings_add_link_mode(ks, advertising, Asym_Pause); break; case rnpm_fc_rx_pause: ethtool_link_ksettings_add_link_mode(ks, advertising, Pause); ethtool_link_ksettings_add_link_mode(ks, advertising, Asym_Pause); break; default: ethtool_link_ksettings_del_link_mode(ks, advertising, Pause); ethtool_link_ksettings_del_link_mode(ks, advertising, Asym_Pause); break; } #ifdef ETH_TP_MDI_X /* MDI-X => 2; MDI =>1; Invalid =>0 */ if (hw->phy_type == PHY_TYPE_SGMII) { if (rnpm_get_phy_mdix_from_hw(hw) < 0) { ks->base.eth_tp_mdix = ETH_TP_MDI_INVALID; } else { ks->base.eth_tp_mdix = hw->phy.is_mdix ? ETH_TP_MDI_X : ETH_TP_MDI; } } #ifdef ETH_TP_MDI_AUTO if (hw->phy.mdix == AUTO_ALL_MODES) ks->base.eth_tp_mdix_ctrl = ETH_TP_MDI_AUTO; else ks->base.eth_tp_mdix_ctrl = hw->phy.mdix; #endif #endif /* ETH_TP_MDI_X */ rnpm_logd(LOG_ETHTOOL, "%s %s set link: speed=%d port=%d duplex=%d autoneg=%d ", __func__, netdev->name, ks->base.speed, ks->base.port, ks->base.duplex, ks->base.autoneg); rnpm_logd(LOG_ETHTOOL, "%s phy_address=%d mdix_ctrl=%d\n", __func__, ks->base.phy_address, ks->base.eth_tp_mdix_ctrl); return 0; } static int rnpm_wol_exclusion(struct rnpm_adapter *adapter, struct ethtool_wolinfo *wol) { struct rnpm_hw *hw = &adapter->hw; int retval = 0; // if (hw->pfvfnum) { // retval = 1; // wol->supported = 0; // } /* WOL not supported for all devices */ if (!rnpm_wol_supported(adapter, hw->device_id, hw->subsystem_device_id)) { retval = 1; wol->supported = 0; } return retval; } static void rnpm_get_wol(struct net_device *netdev, struct ethtool_wolinfo *wol) { struct rnpm_adapter *adapter = netdev_priv(netdev); struct rnpm_hw *hw = &adapter->hw; wol->wolopts = 0; /* we now can't wol */ if (rnpm_wol_exclusion(adapter, wol) || !device_can_wakeup(&adapter->pdev->dev)) return; /* Only support magic */ if (RNPM_WOL_GET_SUPPORTED(adapter)) wol->supported = hw->wol_supported; else wol->supported = 0; if (RNPM_WOL_GET_STATUS(adapter)) wol->wolopts |= hw->wol_supported; // printk("DEBUG: rnpm_get_wol wolopts=0x%x wol=0x%x lane=%d\n", // wol->wolopts, // adapter->wol, // adapter->port); } /** * rnpm_set_wol - set the WakeOnLAN configuration * @netdev: the netdev in question * @wol: the ethtool WoL setting data **/ static int rnpm_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol) { struct rnpm_adapter *adapter = netdev_priv(netdev); struct rnpm_hw *hw = &adapter->hw; // printk("DEBUG: rnp_set_wol wolopts=0x%x wol_supported=0x%x " // "fw_wol_support=0x%x hw->wol=0x%x\n", // wol->wolopts, // hw->wol_supported, // RNPM_WOL_GET_SUPPORTED(adapter), // adapter->wol); if (!!wol->wolopts) { if ((wol->wolopts & (~hw->wol_supported)) || !RNPM_WOL_GET_SUPPORTED(adapter)) return -EOPNOTSUPP; } RNPM_WOL_SET_SUPPORTED(adapter); if (wol->wolopts & WAKE_MAGIC) { RNPM_WOL_SET_SUPPORTED(adapter); RNPM_WOL_SET_STATUS(adapter); } else { RNPM_WOL_CLEAR_STATUS(adapter); } rnpm_mbx_wol_set(hw, RNPM_WOL_GET_STATUS(adapter)); // printk("DEBUG: set wol=0x%x status=%d\n", // adapter->wol, // RNPM_WOL_GET_STATUS(adapter)); device_set_wakeup_enable(&adapter->pdev->dev, !!wol->wolopts); return 0; } /* ethtool register test data */ struct rnpm_reg_test { u16 reg; u8 array_len; u8 test_type; u32 mask; u32 write; }; /* In the hardware, registers are laid out either singly, in arrays * spaced 0x40 bytes apart, or in contiguous tables. We assume * most tests take place on arrays or single registers (handled * as a single-element array) and special-case the tables. * Table tests are always pattern tests. * * We also make provision for some required setup steps by specifying * registers to be written without any read-back testing. */ #define PATTERN_TEST 1 #define SET_READ_TEST 2 #define WRITE_NO_TEST 3 #define TABLE32_TEST 4 #define TABLE64_TEST_LO 5 #define TABLE64_TEST_HI 6 /* default n10 register test */ static struct rnpm_reg_test reg_test_n10[] = { { .reg = 0 } }; /* write and read check */ static bool reg_pattern_test(struct rnpm_adapter *adapter, u64 *data, int reg, u32 mask, u32 write) { u32 pat, val, before; static const u32 test_pattern[] = { 0x5A5A5A5A, 0xA5A5A5A5, 0x00000000, 0xFFFFFFFF }; for (pat = 0; pat < ARRAY_SIZE(test_pattern); pat++) { before = readl(adapter->hw.hw_addr + reg); writel((test_pattern[pat] & write), (adapter->hw.hw_addr + reg)); val = readl(adapter->hw.hw_addr + reg); if (val != (test_pattern[pat] & write & mask)) { e_err(drv, "pattern test reg %04X failed: got 0x%08X expected 0x%08X\n", reg, val, (test_pattern[pat] & write & mask)); *data = reg; writel(before, adapter->hw.hw_addr + reg); return 1; } writel(before, adapter->hw.hw_addr + reg); } return 0; } static bool reg_set_and_check(struct rnpm_adapter *adapter, u64 *data, int reg, u32 mask, u32 write) { u32 val, before; before = readl(adapter->hw.hw_addr + reg); writel((write & mask), (adapter->hw.hw_addr + reg)); val = readl(adapter->hw.hw_addr + reg); if ((write & mask) != (val & mask)) { e_err(drv, "set/check reg %04X test failed: got 0x%08X expected 0x%08X\n", reg, (val & mask), (write & mask)); *data = reg; writel(before, (adapter->hw.hw_addr + reg)); return 1; } writel(before, (adapter->hw.hw_addr + reg)); return 0; } __maybe_unused static bool rnpm_reg_test(struct rnpm_adapter *adapter, u64 *data) { struct rnpm_reg_test *test; struct rnpm_hw *hw = &adapter->hw; // u32 value, before, after; u32 i; if (RNPM_REMOVED(hw->hw_addr)) { e_err(drv, "Adapter removed - register test blocked\n"); *data = 1; return true; } test = reg_test_n10; /* Perform the remainder of the register test, looping through * the test table until we either fail or reach the null entry. */ while (test->reg) { for (i = 0; i < test->array_len; i++) { bool b = false; switch (test->test_type) { case PATTERN_TEST: b = reg_pattern_test(adapter, data, test->reg + (i * 0x40), test->mask, test->write); break; case SET_READ_TEST: b = reg_set_and_check(adapter, data, test->reg + (i * 0x40), test->mask, test->write); break; case WRITE_NO_TEST: wr32(hw, test->reg + (i * 0x40), test->write); break; case TABLE32_TEST: b = reg_pattern_test(adapter, data, test->reg + (i * 4), test->mask, test->write); break; case TABLE64_TEST_LO: b = reg_pattern_test(adapter, data, test->reg + (i * 8), test->mask, test->write); break; case TABLE64_TEST_HI: b = reg_pattern_test(adapter, data, (test->reg + 4) + (i * 8), test->mask, test->write); break; } if (b) return true; } test++; } *data = 0; return false; } static u64 rnpm_link_test(struct rnpm_adapter *adapter, u64 *data) { struct rnpm_hw *hw = &adapter->hw; bool link_up = false; u32 link_speed = 0; *data = 0; hw->mac.ops.check_link(hw, &link_speed, &link_up, true); if (link_up) *data = 0; else *data = 1; return *data; } static void rnpm_diag_test(struct net_device *netdev, struct ethtool_test *eth_test, u64 *data) { struct rnpm_adapter *adapter = netdev_priv(netdev); struct rnpm_hw *hw = &adapter->hw; bool if_running = netif_running(netdev); set_bit(__RNPM_TESTING, &adapter->state); if (eth_test->flags == ETH_TEST_FL_OFFLINE) { if (adapter->flags & RNPM_FLAG_SRIOV_ENABLED) { int i; for (i = 0; i < adapter->num_vfs; i++) { if (adapter->vfinfo[i].clear_to_send) { netdev_warn( netdev, "%s", "offline diagnostic donnot support when VF present\n"); data[0] = 1; data[1] = 1; data[2] = 1; data[3] = 1; eth_test->flags |= ETH_TEST_FL_FAILED; clear_bit(__RNPM_TESTING, &adapter->state); goto skip_ol_tests; } } } /* Offline tests */ e_info(hw, "offline testing starting\n"); /* bringing adapter down disables SFP+ optics */ if (hw->mac.ops.enable_tx_laser) hw->mac.ops.enable_tx_laser(hw); /* Link test performed before hardware reset so autoneg doesn't * interfere with test result */ if (rnpm_link_test(adapter, &data[4])) eth_test->flags |= ETH_TEST_FL_FAILED; rnpm_reset(adapter); e_info(hw, "register testing starting\n"); if (rnpm_reg_test(adapter, &data[0])) eth_test->flags |= ETH_TEST_FL_FAILED; data[1] = 1; data[2] = 1; data[3] = 1; /* If SRIOV or VMDq is enabled then skip MAC * loopback diagnostic */ if (adapter->flags & (RNPM_FLAG_SRIOV_ENABLED | RNPM_FLAG_VMDQ_ENABLED)) { netdev_warn( netdev, "Skip MAC loopback diagnostic in VT mode\n"); data[3] = 0; } /* clear testing bit and return adapter to previous state */ clear_bit(__RNPM_TESTING, &adapter->state); } else { e_info(hw, "online testing starting\n"); /* if adapter is down, SFP+ optics will be disabled */ if (!if_running && hw->mac.ops.enable_tx_laser) hw->mac.ops.enable_tx_laser(hw); /* Online tests */ if (rnpm_link_test(adapter, &data[4])) eth_test->flags |= ETH_TEST_FL_FAILED; /* Offline tests aren't run; pass by default */ data[0] = 0; data[1] = 0; data[2] = 0; data[3] = 0; clear_bit(__RNPM_TESTING, &adapter->state); } /* if adapter was down, ensure SFP+ optics are disabled again */ if (!if_running && hw->mac.ops.disable_tx_laser) hw->mac.ops.disable_tx_laser(hw); skip_ol_tests: msleep_interruptible(4 * 1000); } /** * rnpm_set_link_ksettings - Set Speed and Duplex * @netdev: network interface device structure * @ks: ethtool ksettings * * Set speed/duplex per media_types advertised/forced **/ static int rnpm_set_link_ksettings(struct net_device *netdev, const struct ethtool_link_ksettings *ks) { struct rnpm_adapter *adapter = netdev_priv(netdev); struct rnpm_hw *hw = &adapter->hw; struct ethtool_link_ksettings safe_ks; struct ethtool_link_ksettings copy_ks; bool autoneg_changed = false, duplex_changed = false; int timeout = 50; int err = 0; u8 autoneg; u32 advertising_link_speed; /* copy the ksettings to copy_ks to avoid modifying the origin */ memcpy(©_ks, ks, sizeof(struct ethtool_link_ksettings)); /* save autoneg out of ksettings */ autoneg = copy_ks.base.autoneg; rnpm_logd(LOG_ETHTOOL, "%s %s set link: speed=%d port=%d duplex=%d autoneg=%d ", __func__, netdev->name, copy_ks.base.speed, copy_ks.base.port, copy_ks.base.duplex, copy_ks.base.autoneg); rnpm_logd(LOG_ETHTOOL, "phy_address=%d mdix_ctrl=%d\n", copy_ks.base.phy_address, copy_ks.base.eth_tp_mdix_ctrl); /* get our own copy of the bits to check against */ memset(&safe_ks, 0, sizeof(struct ethtool_link_ksettings)); safe_ks.base.cmd = copy_ks.base.cmd; safe_ks.base.link_mode_masks_nwords = copy_ks.base.link_mode_masks_nwords; if (rnpm_get_link_ksettings(netdev, &safe_ks)) { /* return err */ return 0; } if (!adapter->pf_adapter->force_10g_1g_speed_ablity) { /* Checkout the media_type */ if (hw->phy.media_type != rnpm_media_type_fiber && hw->phy.media_type != rnpm_media_type_copper && hw->phy.media_type != rnpm_media_type_backplane && hw->phy.media_type != rnpm_media_type_cx4 && hw->phy.media_type != rnpm_media_type_da) return -EOPNOTSUPP; } /* Get link modes supported by hardware and check against modes * requested by user. Return an error if unsupported mode was set. */ if (!bitmap_subset(copy_ks.link_modes.advertising, safe_ks.link_modes.supported, __ETHTOOL_LINK_MODE_MASK_NBITS)) return -EINVAL; #ifdef ETH_TP_MDI_AUTO /* MDI setting is only allowed when autoneg enabled because * some hardware doesn't allow MDI setting when speed or * duplex is forced. */ if (copy_ks.base.eth_tp_mdix_ctrl && hw->is_sgmii) { if (hw->phy.media_type != rnpm_media_type_copper) return -EOPNOTSUPP; if (copy_ks.base.eth_tp_mdix_ctrl != ETH_TP_MDI_AUTO && copy_ks.base.autoneg != AUTONEG_ENABLE) { netdev_info( netdev, "forcing MDI/MDI-X state is not supported when link\n"); return -EINVAL; } } #endif /* ETH_TP_MDI_AUTO */ /* set autoneg back to what it currently is */ copy_ks.base.autoneg = safe_ks.base.autoneg; memset(&advertising_link_speed, 0, sizeof(u32)); /* Check autoneg */ if (autoneg == AUTONEG_ENABLE) { /* If autoneg was not already enabled */ if (!(adapter->an)) { /* If autoneg is not supported, return error */ if (!ethtool_link_ksettings_test_link_mode( &safe_ks, supported, Autoneg)) { netdev_info( netdev, "Autoneg not supported on this phy\n"); err = -EINVAL; goto done; } /* Autoneg is allowed to change */ autoneg_changed = true; } if (ethtool_link_ksettings_test_link_mode(ks, advertising, 10baseT_Full)) advertising_link_speed |= RNPM_LINK_SPEED_10_FULL; if (ethtool_link_ksettings_test_link_mode(ks, advertising, 100baseT_Full)) advertising_link_speed |= RNPM_LINK_SPEED_100_FULL; if (ethtool_link_ksettings_test_link_mode(ks, advertising, 1000baseT_Full) || ethtool_link_ksettings_test_link_mode(ks, advertising, 1000baseX_Full) || ethtool_link_ksettings_test_link_mode(ks, advertising, 1000baseKX_Full)) advertising_link_speed |= RNPM_LINK_SPEED_1GB_FULL; if (ethtool_link_ksettings_test_link_mode(ks, advertising, 10baseT_Half)) advertising_link_speed |= RNPM_LINK_SPEED_10_HALF; if (ethtool_link_ksettings_test_link_mode(ks, advertising, 100baseT_Half)) advertising_link_speed |= RNPM_LINK_SPEED_100_HALF; if (ethtool_link_ksettings_test_link_mode(ks, advertising, 1000baseT_Half)) advertising_link_speed |= RNPM_LINK_SPEED_1GB_HALF; if (ethtool_link_ksettings_test_link_mode(ks, advertising, 10000baseT_Full) || ethtool_link_ksettings_test_link_mode(ks, advertising, 10000baseKX4_Full) || ethtool_link_ksettings_test_link_mode(ks, advertising, 10000baseKR_Full) || ethtool_link_ksettings_test_link_mode(ks, advertising, 10000baseCR_Full) || ethtool_link_ksettings_test_link_mode(ks, advertising, 10000baseSR_Full) || ethtool_link_ksettings_test_link_mode(ks, advertising, 10000baseLR_Full)) advertising_link_speed |= RNPM_LINK_SPEED_10GB_FULL; if (ethtool_link_ksettings_test_link_mode(ks, advertising, 40000baseKR4_Full) || ethtool_link_ksettings_test_link_mode(ks, advertising, 40000baseCR4_Full) || ethtool_link_ksettings_test_link_mode(ks, advertising, 40000baseSR4_Full) || ethtool_link_ksettings_test_link_mode(ks, advertising, 40000baseLR4_Full)) advertising_link_speed |= RNPM_LINK_SPEED_40GB_FULL; if (advertising_link_speed) { hw->phy.autoneg_advertised = advertising_link_speed; } else { // err = -EINVAL; // RNPM_LINK_SPEED_UNKNOWN // goto done; } if (hw->is_sgmii && hw->mac.autoneg == false) autoneg_changed = true; hw->mac.autoneg = true; } else { if (!hw->is_sgmii && !adapter->pf_adapter->force_10g_1g_speed_ablity) { err = -EOPNOTSUPP; goto done; } /* If autoneg is currently enabled */ if (adapter->an) { /* If autoneg is supported 10GBASE_T is the only PHY * that can disable it, so otherwise return error */ if (ethtool_link_ksettings_test_link_mode( &safe_ks, supported, Autoneg) && hw->phy.media_type != rnpm_media_type_copper) { netdev_info( netdev, "Autoneg cannot be disabled on this phy\n"); err = -EINVAL; goto done; } /* Autoneg is allowed to change */ autoneg_changed = true; } /* Only allow one speed at a time when autoneg is AUTONEG_DISABLE. */ switch (ks->base.speed) { case SPEED_10: advertising_link_speed = RNPM_LINK_SPEED_10_FULL; break; case SPEED_100: advertising_link_speed = RNPM_LINK_SPEED_100_FULL; break; case SPEED_1000: advertising_link_speed = RNPM_LINK_SPEED_1GB_FULL; break; case SPEED_10000: advertising_link_speed = RNPM_LINK_SPEED_10GB_FULL; break; default: netdev_info(netdev, "unsupported speed\n"); err = -EINVAL; goto done; } hw->mac.autoneg = false; } hw->phy.autoneg_advertised = RNPM_LINK_SPEED_UNKNOWN; /* If speed didn't get set, set it to what it currently is. * This is needed because if advertise is 0 (as it is when autoneg * is disabled) then speed won't get set. */ // old_link_speed = hw->phy.autoneg_advertised; // if (!advertising_link_speed) // advertising_link_speed = old_link_speed; if (hw->is_sgmii) { // duplex_changed = !!(hw->mac.duplex != ks->base.duplex); hw->mac.duplex = ks->base.duplex; duplex_changed = true; } /* If the unsupported speed is set, return -EOPNOTSUPP error. */ // if ((advertising_link_speed | hw->supported_link) != hw->supported_link) // return -EOPNOTSUPP; // if (autoneg_changed || duplex_changed || // (hw->phy.autoneg_advertised != advertising_link_speed)) { /* this sets the link speed and restarts auto-neg */ while (test_and_set_bit(__RNPM_IN_SFP_INIT, &adapter->state)) { timeout--; if (!timeout) return -EBUSY; usleep_range(1000, 2000); } #ifdef ETH_TP_MDI_AUTO /* MDI-X => 2; MDI => 1; Auto => 3 */ if (copy_ks.base.eth_tp_mdix_ctrl) { /* fix up the value for auto (3 => 0) as zero is mapped * internally to auto */ if (copy_ks.base.eth_tp_mdix_ctrl == ETH_TP_MDI_AUTO) hw->phy.mdix = AUTO_ALL_MODES; else hw->phy.mdix = copy_ks.base.eth_tp_mdix_ctrl; } #endif /* ETH_TP_MDI_AUTO */ hw->mac.autotry_restart = true; /* set speed */ err = hw->mac.ops.setup_link(hw, advertising_link_speed, true); if (err) { e_info(probe, "setup link failed with code %d\n", err); // hw->mac.ops.setup_link(hw, old_link_speed, true); } clear_bit(__RNPM_IN_SFP_INIT, &adapter->state); //} done: return err; } /** * rnpm_get_pauseparam - Get Flow Control status * @netdev: netdevice structure * @pause: buffer to return pause parameters * * Return tx/rx-pause status **/ static void rnpm_get_pauseparam(struct net_device *netdev, struct ethtool_pauseparam *pause) { struct rnpm_adapter *adapter = netdev_priv(netdev); struct rnpm_hw *hw = &adapter->hw; rnpm_redefine_phy_type(adapter); rnpm_get_media_type(hw); if (rnpm_device_supports_autoneg_fc(hw) && !hw->fc.disable_fc_autoneg) pause->autoneg = 1; else pause->autoneg = 0; if (hw->fc.current_mode == rnpm_fc_rx_pause) { pause->rx_pause = 1; } else if (hw->fc.current_mode == rnpm_fc_tx_pause) { pause->tx_pause = 1; } else if (hw->fc.current_mode == rnpm_fc_full) { pause->rx_pause = 1; pause->tx_pause = 1; } } /** * rnpm_set_pauseparam - Set Flow Control parameter * @netdev: network interface device structure * @pause: return tx/rx flow control status **/ static int rnpm_set_pauseparam(struct net_device *netdev, struct ethtool_pauseparam *pause) { struct rnpm_adapter *adapter = netdev_priv(netdev); struct rnpm_hw *hw = &adapter->hw; struct rnpm_fc_info fc = hw->fc; /* we not support change in dcb mode */ if (adapter->flags & RNPM_FLAG_DCB_ENABLED) return -EINVAL; rnpm_redefine_phy_type(adapter); rnpm_get_media_type(hw); /* some devices do not support autoneg of flow control */ if ((pause->autoneg == AUTONEG_ENABLE) && !rnpm_device_supports_autoneg_fc(hw)) return -EINVAL; fc.disable_fc_autoneg = (pause->autoneg != AUTONEG_ENABLE); if ((pause->rx_pause && pause->tx_pause) || (pause->autoneg)) fc.requested_mode = rnpm_fc_full; else if (pause->rx_pause) fc.requested_mode = rnpm_fc_rx_pause; else if (pause->tx_pause) fc.requested_mode = rnpm_fc_tx_pause; else fc.requested_mode = rnpm_fc_none; /* if the thing changed then we'll update and use new autoneg */ if (memcmp(&fc, &hw->fc, sizeof(struct rnpm_fc_info))) { hw->fc = fc; /* to tell all vf new pause status */ // rnpm_msg_post_status(adapter, PF_PAUSE_STATUS); if (netif_running(netdev)) rnpm_reinit_locked(adapter); else rnpm_reset(adapter); } return 0; } static int rnpm_get_fecparam(struct net_device *netdev, struct ethtool_fecparam *fecparam) { int err; struct rnpm_adapter *adapter = netdev_priv(netdev); struct rnpm_hw *hw = &adapter->hw; err = rnpm_mbx_get_lane_stat(hw); if (err) return err; if (adapter->fec) fecparam->active_fec = ETHTOOL_FEC_BASER; else fecparam->active_fec = ETHTOOL_FEC_NONE; fecparam->fec = ETHTOOL_FEC_BASER; return 0; } static int rnpm_set_fecparam(struct net_device *netdev, struct ethtool_fecparam *fecparam) { // int err; struct rnpm_adapter *adapter = netdev_priv(netdev); struct rnpm_hw *hw = &adapter->hw; if (fecparam->fec & ETHTOOL_FEC_OFF) return rnpm_set_lane_fun(hw, LANE_FUN_FEC, 0, 0, 0, 0); else if (fecparam->fec & ETHTOOL_FEC_BASER) return rnpm_set_lane_fun(hw, LANE_FUN_FEC, 1, 0, 0, 0); return -EINVAL; } static u32 rnpm_get_msglevel(struct net_device *netdev) { struct rnpm_adapter *adapter = netdev_priv(netdev); return adapter->msg_enable; } static void rnpm_set_msglevel(struct net_device *netdev, u32 data) { struct rnpm_adapter *adapter = netdev_priv(netdev); adapter->msg_enable = data; } static int rnpm_set_phys_id(struct net_device *netdev, enum ethtool_phys_id_state state) { struct rnpm_adapter *adapter = netdev_priv(netdev); struct rnpm_hw *hw = &adapter->hw; switch (state) { case ETHTOOL_ID_ACTIVE: rnpm_mbx_led_set(hw, 1); return 2; /*twic peer seconds*/ case ETHTOOL_ID_ON: rnpm_mbx_led_set(hw, 2); break; case ETHTOOL_ID_OFF: rnpm_mbx_led_set(hw, 3); break; case ETHTOOL_ID_INACTIVE: rnpm_mbx_led_set(hw, 0); break; default: return -ENOENT; } return 0; } static int rnpm_get_ts_info(struct net_device *dev, struct ethtool_ts_info *info) { struct rnpm_adapter *adapter = netdev_priv(dev); #ifndef NO_PTP /*For we just set it as pf0 */ if (!(adapter->flags2 & RNPM_FLAG2_PTP_ENABLED)) return ethtool_op_get_ts_info(dev, info); if (adapter->ptp_clock) info->phc_index = ptp_clock_index(adapter->ptp_clock); else info->phc_index = -1; ptp_dbg("phc_index is %d\n", info->phc_index); info->so_timestamping = SOF_TIMESTAMPING_TX_HARDWARE | SOF_TIMESTAMPING_RX_HARDWARE | SOF_TIMESTAMPING_RX_SOFTWARE | SOF_TIMESTAMPING_TX_SOFTWARE | SOF_TIMESTAMPING_SOFTWARE | SOF_TIMESTAMPING_RAW_HARDWARE; info->tx_types = (1 << HWTSTAMP_TX_OFF) | (1 << HWTSTAMP_TX_ON); info->rx_filters = BIT(HWTSTAMP_FILTER_NONE) | BIT(HWTSTAMP_FILTER_PTP_V1_L4_SYNC) | BIT(HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ) | BIT(HWTSTAMP_FILTER_PTP_V1_L4_EVENT) | BIT(HWTSTAMP_FILTER_PTP_V2_L4_SYNC) | BIT(HWTSTAMP_FILTER_PTP_V2_L4_EVENT) | BIT(HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ) | BIT(HWTSTAMP_FILTER_ALL); #ifdef PTP_802_AS1 /* 802.AS1 */ BIT(HWTSTAMP_FILTER_PTP_V2_L2_EVENT) | BIT(HWTSTAMP_FILTER_PTP_V2_L2_SYNC) | BIT(HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ); #endif #else info->phc_index = -1; #endif return 0; } static unsigned int rnpm_max_channels(struct rnpm_adapter *adapter) { unsigned int max_combined; if (adapter->flags & RNPM_FLAG_SRIOV_ENABLED) { /* SR-IOV currently only allows 2 queue on the PF */ max_combined = PF_RING_CNT_WHEN_IOV_ENABLED; } else { /* support up to 16 queues with RSS */ max_combined = adapter->max_ring_pair_counts; /* should not large than q_vectors ? */ // if dcb is off // max_combined = adapter->num_q_vectors; } return max_combined; } /** * rnpm_get_channels - Get the current channels enabled and max supported etc. * @dev: network interface device structure * @ch: ethtool channels structure * * We don't support separate tx and rx queues as channels. The other count * represents how many queues are being used for control. max_combined counts * how many queue pairs we can support. They may not be mapped 1 to 1 with * q_vectors since we support a lot more queue pairs than q_vectors. **/ static void rnpm_get_channels(struct net_device *dev, struct ethtool_channels *ch) { struct rnpm_adapter *adapter = netdev_priv(dev); /* report maximum channels */ ch->max_combined = rnpm_max_channels(adapter); /* report info for other vector */ ch->max_other = NON_Q_VECTORS; ch->other_count = NON_Q_VECTORS; /* record RSS queues */ ch->combined_count = adapter->ring_feature[RING_F_RSS].indices; /* nothing else to report if RSS is disabled */ if (ch->combined_count == 1) return; /* we do not support ATR queueing if SR-IOV is enabled */ if (adapter->flags & RNPM_FLAG_SRIOV_ENABLED) return; /* same thing goes for being DCB enabled */ if (netdev_get_num_tc(dev) > 1) return; /* report flow director queues as maximum channels */ // ch->combined_count = adapter->ring_feature[RING_F_FDIR].indices; } /** * rnpm_set_channels - Set the new channels count. * @dev: network interface device structure * @ch: ethtool channels structure * * The new channels count may not be the same as requested by the user * since it gets rounded down to a power of 2 value. **/ static int rnpm_set_channels(struct net_device *dev, struct ethtool_channels *ch) { struct rnpm_adapter *adapter = netdev_priv(dev); unsigned int count = ch->combined_count; /* verify they are not requesting separate vectors */ if (!count || ch->rx_count || ch->tx_count) return -EINVAL; /* verify other_count has not changed */ if (ch->other_count != NON_Q_VECTORS) return -EINVAL; /* verify the number of channels does not exceed hardware limits */ if (count > rnpm_max_channels(adapter)) return -EINVAL; /* update feature limits from largest to smallest supported values */ adapter->ring_feature[RING_F_FDIR].limit = count; if (count > adapter->max_ring_pair_counts) count = adapter->max_ring_pair_counts; /* use this to limit ring num */ adapter->ring_feature[RING_F_RSS].limit = count; /* use setup TC to update any traffic class queue mapping */ return rnpm_setup_tc(dev, netdev_get_num_tc(dev)); } /** * rnpm_get_module_info - get (Q)SFP+ module type info * @netdev: network interface device structure * @modinfo: module EEPROM size and layout information structure **/ static int rnpm_get_module_info(struct net_device *dev, struct ethtool_modinfo *modinfo) { struct rnpm_adapter *adapter = netdev_priv(dev); struct rnpm_hw *hw = &adapter->hw; u8 module_id, diag_supported; int rc; rnpm_mbx_get_lane_stat(hw); if (hw->is_sgmii) return -EIO; /* Check if firmware supports reading module EEPROM. */ rc = rnpm_mbx_sfp_module_eeprom_info(hw, 0xA0, SFF_MODULE_ID_OFFSET, 1, &module_id); if (rc || module_id == 0xff) return -EIO; rc = rnpm_mbx_sfp_module_eeprom_info(hw, 0xA0, SFF_DIAG_SUPPORT_OFFSET, 1, &diag_supported); if (!rc) { switch (module_id) { case SFF_MODULE_ID_SFF: case SFF_MODULE_ID_SFP: modinfo->type = ETH_MODULE_SFF_8472; modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN; if (!diag_supported) modinfo->eeprom_len = ETH_MODULE_SFF_8436_LEN; break; case SFF_MODULE_ID_QSFP: case SFF_MODULE_ID_QSFP_PLUS: modinfo->type = ETH_MODULE_SFF_8436; modinfo->eeprom_len = ETH_MODULE_SFF_8436_LEN; break; case SFF_MODULE_ID_QSFP28: modinfo->type = ETH_MODULE_SFF_8636; modinfo->eeprom_len = RNPM_MODULE_QSFP_MAX_LEN; break; default: netdev_err( dev, "SFP module type unrecognized or no SFP connector.\n"); return -EINVAL; } } return 0; } /** * rnpm_get_module_eeprom - fills buffer with (Q)SFP+ module memory contents * @netdev: network interface device structure * @ee: EEPROM dump request structure * @data: buffer to be filled with EEPROM contents **/ static int rnpm_get_module_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom, u8 *data) { struct rnpm_adapter *adapter = netdev_priv(dev); struct rnpm_hw *hw = &adapter->hw; u16 start = eeprom->offset, length = eeprom->len; int rc = 0; memset(data, 0, eeprom->len); /* Read A0 portion of the EEPROM */ if (start < ETH_MODULE_SFF_8436_LEN) { if (start + eeprom->len > ETH_MODULE_SFF_8436_LEN) length = ETH_MODULE_SFF_8436_LEN - start; rc = rnpm_mbx_sfp_module_eeprom_info(hw, 0xA0, start, length, data); if (rc) return rc; start += length; data += length; length = eeprom->len - length; } /* Read A2 portion of the EEPROM */ if (length) { start -= ETH_MODULE_SFF_8436_LEN; rc = rnpm_mbx_sfp_module_eeprom_info(hw, 0xA2, start, length, data); } return rc; } static void rnpm_get_ringparam(struct net_device *netdev, struct ethtool_ringparam *ring, struct kernel_ethtool_ringparam __always_unused *ker, struct netlink_ext_ack __always_unused *extack) { struct rnpm_adapter *adapter = netdev_priv(netdev); /* all ring share the same status*/ ring->rx_max_pending = RNPM_MAX_RXD; ring->tx_max_pending = RNPM_MAX_TXD; ring->rx_mini_max_pending = 0; ring->rx_jumbo_max_pending = 0; ring->rx_pending = adapter->rx_ring_item_count; ring->tx_pending = adapter->tx_ring_item_count; ring->rx_mini_pending = 0; ring->rx_jumbo_pending = 0; } static int rnpm_set_ringparam(struct net_device *netdev, struct ethtool_ringparam *ring, struct kernel_ethtool_ringparam __always_unused *ker, struct netlink_ext_ack __always_unused *extack) { struct rnpm_adapter *adapter = netdev_priv(netdev); struct rnpm_ring *temp_ring; int i, err = 0; u32 new_rx_count, new_tx_count; if ((ring->rx_mini_pending) || (ring->rx_jumbo_pending)) return -EINVAL; if ((ring->tx_pending < RNPM_MIN_TXD) || (ring->tx_pending > RNPM_MAX_TXD) || (ring->rx_pending < RNPM_MIN_RXD) || (ring->rx_pending > RNPM_MAX_RXD)) { netdev_info( netdev, "Descriptors requested (Tx: %d / Rx: %d) out of range [%d-%d]\n", ring->tx_pending, ring->rx_pending, RNPM_MIN_TXD, RNPM_MAX_TXD); return -EINVAL; } new_tx_count = clamp_t(u32, ring->tx_pending, RNPM_MIN_TXD, RNPM_MAX_TXD); new_tx_count = ALIGN(new_tx_count, RNPM_REQ_TX_DESCRIPTOR_MULTIPLE); new_rx_count = clamp_t(u32, ring->rx_pending, RNPM_MIN_RXD, RNPM_MAX_RXD); new_rx_count = ALIGN(new_rx_count, RNPM_REQ_RX_DESCRIPTOR_MULTIPLE); if ((new_tx_count == adapter->tx_ring_item_count) && (new_rx_count == adapter->rx_ring_item_count)) { /* nothing to do */ return 0; } while (test_and_set_bit(__RNPM_RESETTING, &adapter->state)) usleep_range(1000, 2000); if (!netif_running(adapter->netdev)) { for (i = 0; i < adapter->num_tx_queues; i++) adapter->tx_ring[i]->count = new_tx_count; for (i = 0; i < adapter->num_rx_queues; i++) adapter->rx_ring[i]->count = new_rx_count; adapter->tx_ring_item_count = new_tx_count; adapter->rx_ring_item_count = new_rx_count; goto clear_reset; } /* allocate temporary buffer to store rings in */ i = max_t(int, adapter->num_tx_queues, adapter->num_rx_queues); temp_ring = vmalloc(i * sizeof(struct rnpm_ring)); if (!temp_ring) { err = -ENOMEM; goto clear_reset; } memset(temp_ring, 0x00, i * sizeof(struct rnpm_ring)); if (new_rx_count != adapter->rx_ring_item_count) { for (i = 0; i < adapter->num_rx_queues; i++) { struct rnpm_ring *ring = adapter->rx_ring[i]; ring->reset_count = new_rx_count; ring->ring_flags |= RNPM_RING_FLAG_CHANGE_RX_LEN; } } rnpm_down(adapter); /* Setup new Tx resources and free the old Tx resources in that order. * We can then assign the new resources to the rings via a memcpy. * The advantage to this approach is that we are guaranteed to still * have resources even in the case of an allocation failure. */ if (new_tx_count != adapter->tx_ring_item_count) { netdev_info(netdev, "Changing Tx descriptor count from %d to %d\n", adapter->tx_ring_item_count, new_tx_count); for (i = 0; i < adapter->num_tx_queues; i++) { memcpy(&temp_ring[i], adapter->tx_ring[i], sizeof(struct rnpm_ring)); temp_ring[i].count = new_tx_count; err = rnpm_setup_tx_resources(&temp_ring[i], adapter); if (err) { while (i) { i--; rnpm_free_tx_resources(&temp_ring[i]); } goto err_setup; } } for (i = 0; i < adapter->num_tx_queues; i++) { rnpm_free_tx_resources(adapter->tx_ring[i]); memcpy(adapter->tx_ring[i], &temp_ring[i], sizeof(struct rnpm_ring)); } adapter->tx_ring_item_count = new_tx_count; } /* Repeat the process for the Rx rings if needed */ if (new_rx_count != adapter->rx_ring_item_count) { netdev_info(netdev, "Changing Rx descriptor count from %d to %d\n", adapter->rx_ring_item_count, new_rx_count); for (i = 0; i < adapter->num_rx_queues; i++) { memcpy(&temp_ring[i], adapter->rx_ring[i], sizeof(struct rnpm_ring)); /* setup ring count */ if (!(adapter->rx_ring[i]->ring_flags & RNPM_RING_FLAG_DELAY_SETUP_RX_LEN)) { temp_ring[i].count = new_rx_count; } else { /* setup temp count */ temp_ring[i].count = temp_ring[i].temp_count; adapter->rx_ring[i]->reset_count = new_rx_count; } err = rnpm_setup_rx_resources(&temp_ring[i], adapter); if (err) { while (i) { i--; rnpm_free_rx_resources(&temp_ring[i]); } goto err_setup; } } for (i = 0; i < adapter->num_rx_queues; i++) { rnpm_free_rx_resources(adapter->rx_ring[i]); memcpy(adapter->rx_ring[i], &temp_ring[i], sizeof(struct rnpm_ring)); } adapter->rx_ring_item_count = new_rx_count; } err_setup: rnpm_up(adapter); vfree(temp_ring); clear_reset: clear_bit(__RNPM_RESETTING, &adapter->state); return err; } static void rnpm_get_strings(struct net_device *netdev, u32 stringset, u8 *data) { struct rnpm_adapter *adapter = netdev_priv(netdev); char *p = (char *)data; int i; struct rnpm_ring *ring; u32 dma_ch; switch (stringset) { /* maybe we don't support test? */ case ETH_SS_TEST: for (i = 0; i < RNPM_TEST_LEN; i++) { memcpy(data, rnpm_gstrings_test[i], ETH_GSTRING_LEN); data += ETH_GSTRING_LEN; } break; case ETH_SS_STATS: for (i = 0; i < RNPM_GLOBAL_STATS_LEN; i++) { memcpy(p, rnpm_gstrings_net_stats[i].stat_string, ETH_GSTRING_LEN); p += ETH_GSTRING_LEN; } for (i = 0; i < RNPM_HWSTRINGS_STATS_LEN; i++) { memcpy(p, rnpm_hwstrings_stats[i].stat_string, ETH_GSTRING_LEN); p += ETH_GSTRING_LEN; } for (i = 0; i < RNPM_NUM_TX_QUEUES; i++) { #define SHORT_STATS #ifdef SHORT_STATS //==== tx ======== ring = adapter->tx_ring[i]; dma_ch = ring->rnpm_queue_idx; sprintf(p, "---\n queue%u_tx_packets", i); p += ETH_GSTRING_LEN; sprintf(p, "queue%u_tx_bytes", i); p += ETH_GSTRING_LEN; sprintf(p, "queue%u_tx_restart", i); p += ETH_GSTRING_LEN; sprintf(p, "queue%u_tx_busy", i); p += ETH_GSTRING_LEN; sprintf(p, "queue%u_tx_done_old", i); p += ETH_GSTRING_LEN; sprintf(p, "queue%u_tx_clean_desc", i); p += ETH_GSTRING_LEN; sprintf(p, "queue%u_tx_poll_count", i); p += ETH_GSTRING_LEN; sprintf(p, "queue%u_tx_irq_more", i); p += ETH_GSTRING_LEN; sprintf(p, "queue%u_tx_hw_head", i); p += ETH_GSTRING_LEN; sprintf(p, "queue%u_tx_hw_tail", i); p += ETH_GSTRING_LEN; sprintf(p, "queue%u_tx_sw_next_to_clean", i); p += ETH_GSTRING_LEN; sprintf(p, "queue%u_tx_sw_next_to_use", i); p += ETH_GSTRING_LEN; sprintf(p, "queue%u_send_bytes", i); p += ETH_GSTRING_LEN; sprintf(p, "queue%u_send_bytes_to_hw", i); p += ETH_GSTRING_LEN; sprintf(p, "queue%u_todo_update", i); p += ETH_GSTRING_LEN; sprintf(p, "queue%u_send_done_bytes", i); p += ETH_GSTRING_LEN; sprintf(p, "queue%u_added_vlan_packets", i); p += ETH_GSTRING_LEN; sprintf(p, "queue%u_tx_next_to_clean", i); p += ETH_GSTRING_LEN; sprintf(p, "queue%u_tx_irq_miss", i); p += ETH_GSTRING_LEN; sprintf(p, "queue%u_tx_equal_count", i); p += ETH_GSTRING_LEN; //==== rx ======== ring = adapter->rx_ring[i]; dma_ch = ring->rnpm_queue_idx; sprintf(p, "queue%u_rx_packets", i); p += ETH_GSTRING_LEN; sprintf(p, "queue%u_rx_bytes", i); p += ETH_GSTRING_LEN; sprintf(p, "queue%u_rx_driver_dropped_packets", i); p += ETH_GSTRING_LEN; sprintf(p, "queue%u_rx_rsc", i); p += ETH_GSTRING_LEN; sprintf(p, "queue%u_rx_rsc_flush", i); p += ETH_GSTRING_LEN; sprintf(p, "queue%u_rx_non_eop_descs", i); p += ETH_GSTRING_LEN; sprintf(p, "queue%u_rx_alloc_page_failed", i); p += ETH_GSTRING_LEN; sprintf(p, "queue%u_rx_alloc_buff_failed", i); p += ETH_GSTRING_LEN; sprintf(p, "queue%u_rx_csum_offload_errs", i); p += ETH_GSTRING_LEN; sprintf(p, "queue%u_rx_csum_offload_good", i); p += ETH_GSTRING_LEN; sprintf(p, "queue%u_rx_poll_again_count", i); p += ETH_GSTRING_LEN; sprintf(p, "queue%u_rx_rm_vlan_packets", i); p += ETH_GSTRING_LEN; sprintf(p, "queue%u_alloc_rx_page", i); p += ETH_GSTRING_LEN; sprintf(p, "queue%u_rx_hw_head", i); p += ETH_GSTRING_LEN; sprintf(p, "queue%u_rx_hw_tail", i); p += ETH_GSTRING_LEN; sprintf(p, "queue%u_rx_sw_next_to_use", i); p += ETH_GSTRING_LEN; sprintf(p, "queue%u_rx_sw_next_to_clean", i); /* dbg desc */ p += ETH_GSTRING_LEN; sprintf(p, "queue%u_rx_next_to_clean", i); p += ETH_GSTRING_LEN; sprintf(p, "queue%u_rx_irq_miss", i); p += ETH_GSTRING_LEN; sprintf(p, "queue%u_rx_equal_count", i); p += ETH_GSTRING_LEN; sprintf(p, "queue%u_rx_poll_packets", i); p += ETH_GSTRING_LEN; sprintf(p, "queue%u_rx_poll_avg_packets", i); p += ETH_GSTRING_LEN; sprintf(p, "queue%u_rx_poll_itr", i); p += ETH_GSTRING_LEN; #else //==== tx ======== ring = adapter->tx_ring[i]; dma_ch = ring->rnpm_queue_idx; sprintf(p, "queue%u_dma%u_tx_packets", i, dma_ch); p += ETH_GSTRING_LEN; sprintf(p, "queue%u_dma%u_tx_bytes", i, dma_ch); p += ETH_GSTRING_LEN; sprintf(p, "queue%u_dma%u_tx_restart", i, dma_ch); p += ETH_GSTRING_LEN; sprintf(p, "queue%u_dma%u_tx_busy", i, dma_ch); p += ETH_GSTRING_LEN; sprintf(p, "queue%u_dma%u_tx_done_old", i, dma_ch); p += ETH_GSTRING_LEN; sprintf(p, "queue%u_dma%u_tx_clean_desc", i, dma_ch); p += ETH_GSTRING_LEN; sprintf(p, "queue%u_dma%u_tx_poll_count", i, dma_ch); p += ETH_GSTRING_LEN; sprintf(p, "queue%u_dma%u_tx_irq_more", i, dma_ch); p += ETH_GSTRING_LEN; sprintf(p, "queue%u_dma%u_tx_hw_head", i, dma_ch); p += ETH_GSTRING_LEN; sprintf(p, "queue%u_dma%u_tx_hw_tail", i, dma_ch); p += ETH_GSTRING_LEN; sprintf(p, "queue%u_dma%u_tx_sw_next_to_clean", i, dma_ch); p += ETH_GSTRING_LEN; sprintf(p, "queue%u_dma%u_tx_sw_next_to_use", i, dma_ch); p += ETH_GSTRING_LEN; sprintf(p, "queue%u_dma%u_send_bytes", i, dma_ch); p += ETH_GSTRING_LEN; sprintf(p, "queue%u_dma%u_send_bytes_to_hw", i, dma_ch); p += ETH_GSTRING_LEN; sprintf(p, "queue%u_dma%u_todo_update", i, dma_ch); p += ETH_GSTRING_LEN; sprintf(p, "queue%u_dma%u_send_done_bytes", i, dma_ch); p += ETH_GSTRING_LEN; sprintf(p, "queue%u_dma%u_added_vlan_packets", i, dma_ch); p += ETH_GSTRING_LEN; sprintf(p, "queue%u_dma%u_tx_next_to_clean", i, dma_ch); p += ETH_GSTRING_LEN; sprintf(p, "queue%u_dma%u_tx_irq_miss", i, dma_ch); p += ETH_GSTRING_LEN; sprintf(p, "queue%u_dma%u_tx_equal_count", i, dma_ch); p += ETH_GSTRING_LEN; //==== rx ======== ring = adapter->rx_ring[i]; dma_ch = ring->rnpm_queue_idx; sprintf(p, "queue%u_dma%u_rx_packets", i, dma_ch); p += ETH_GSTRING_LEN; sprintf(p, "queue%u_dma%u_rx_bytes", i, dma_ch); p += ETH_GSTRING_LEN; sprintf(p, "queue%u_dma%u_rx_driver_drop_packets", i, dma_ch); p += ETH_GSTRING_LEN; sprintf(p, "queue%u_dma%u_rx_rsc", i, dma_ch); p += ETH_GSTRING_LEN; sprintf(p, "queue%u_dma%u_rx_rsc_flush", i, dma_ch); p += ETH_GSTRING_LEN; sprintf(p, "queue%u_dma%u_rx_non_eop_descs", i, dma_ch); p += ETH_GSTRING_LEN; sprintf(p, "queue%u_dma%u_rx_alloc_page_failed", i, dma_ch); p += ETH_GSTRING_LEN; sprintf(p, "queue%u_dma%u_rx_alloc_buff_failed", i, dma_ch); p += ETH_GSTRING_LEN; sprintf(p, "queue%u_dma%u_rx_csum_offload_errs", i, dma_ch); p += ETH_GSTRING_LEN; sprintf(p, "queue%u_dma%u_rx_csum_offload_good", i, dma_ch); p += ETH_GSTRING_LEN; sprintf(p, "queue%u_dma%u_rx_poll_again_count", i, dma_ch); p += ETH_GSTRING_LEN; sprintf(p, "queue%u_dma%u_rx_rm_vlan_packets", i, dma_ch); p += ETH_GSTRING_LEN; sprintf(p, "queue%u_dma%u_alloc_rx_page", i, dma_ch); p += ETH_GSTRING_LEN; sprintf(p, "queue%u_dma%u_rx_hw_head", i, dma_ch); p += ETH_GSTRING_LEN; sprintf(p, "queue%u_dma%u_rx_hw_tail", i, dma_ch); p += ETH_GSTRING_LEN; sprintf(p, "queue%u_dma%u_rx_sw_next_to_use", i, dma_ch); p += ETH_GSTRING_LEN; sprintf(p, "queue%u_dma%u_rx_sw_next_to_clean", i, dma_ch); /* dbg desc */ p += ETH_GSTRING_LEN; sprintf(p, "queue%u_dma%u_rx_next_to_clean", i, dma_ch); p += ETH_GSTRING_LEN; sprintf(p, "queue%u_dma%u_rx_irq_miss", i, dma_ch); p += ETH_GSTRING_LEN; sprintf(p, "queue%u_dma%u_rx_equal_count", i, dma_ch); p += ETH_GSTRING_LEN; sprintf(p, "queue%u_dma%u_rx_poll_packets", i, dma_ch); p += ETH_GSTRING_LEN; sprintf(p, "queue%u_dma%u_rx_poll_avg_packets", i, dma_ch); p += ETH_GSTRING_LEN; sprintf(p, "queue%u_dma%u_rx_poll_itr", i, dma_ch); p += ETH_GSTRING_LEN; #endif /* SHORT_STATS */ } break; case ETH_SS_PRIV_FLAGS: memcpy(data, rnpm_priv_flags_strings, RNPM_PRIV_FLAGS_STR_LEN * ETH_GSTRING_LEN); break; case ETH_SS_PHY_STATS: memcpy(data, rnpm_phy_statistics_strings, RNPM_PHY_STATISTICS_STR_LEN * ETH_GSTRING_LEN); break; } } __maybe_unused static int rnpm_get_dump_flag(struct net_device *netdev, struct ethtool_dump *dump) { struct rnpm_adapter *adapter = (struct rnpm_adapter *)netdev_priv(netdev); // struct rnpm_hw *hw = &adapter->hw; // struct rnpm_pf_adapter *pf_adapter = adapter->pf_adapter; rnpm_mbx_get_dump(&adapter->hw, 0, NULL, 0); dump->flag = adapter->hw.dump.flag; dump->len = adapter->hw.dump.len; dump->version = adapter->hw.dump.version; return 0; } __maybe_unused static int rnpm_get_dump_data(struct net_device *netdev, struct ethtool_dump *dump, void *buffer) { int err; struct rnpm_adapter *adapter = netdev_priv(netdev); err = rnpm_mbx_get_dump(&adapter->hw, dump->flag, buffer, dump->len); if (err) return err; dump->flag = adapter->hw.dump.flag; dump->len = adapter->hw.dump.len; dump->version = adapter->hw.dump.version; return 0; } __maybe_unused static int rnpm_set_dump(struct net_device *netdev, struct ethtool_dump *dump) { // int err; struct rnpm_adapter *adapter = netdev_priv(netdev); rnpm_mbx_set_dump(&adapter->hw, dump->flag); return 0; } static int rnpm_get_sset_count(struct net_device *netdev, int sset) { #ifdef NO_REAL_QUEUE_NUM struct rnpm_adapter *adapter = (struct rnpm_adapter *)netdev_priv(netdev); #endif switch (sset) { /* now we don't support test */ case ETH_SS_TEST: return RNPM_TEST_LEN; case ETH_SS_STATS: return RNPM_STATS_LEN; case ETH_SS_PRIV_FLAGS: return RNPM_PRIV_FLAGS_STR_LEN; case ETH_SS_PHY_STATS: return RNPM_PHY_STATISTICS_STR_LEN; default: return -EOPNOTSUPP; } } /** * rnpm_get_priv_flags - report device private flags * @dev: network interface device structure * * The get string set count and the string set should be matched for each * flag returned. Add new strings for each flag to the rnpm_gstrings_priv_flags * array. * * Returns a u32 bitmap of flags. **/ static u32 rnpm_get_priv_flags(struct net_device *netdev) { struct rnpm_adapter *adapter = (struct rnpm_adapter *)netdev_priv(netdev); // struct rnpm_hw *hw = &adapter->hw; struct rnpm_pf_adapter *pf_adapter = adapter->pf_adapter; u32 priv_flags = 0; // dbg("adapter priv is %x\n",iface->priv_flags); if (pf_adapter->priv_flags & RNPM_PRIV_FLAG_MAC_LOOPBACK) priv_flags |= RNPM_MAC_LOOPBACK; if (pf_adapter->priv_flags & RNPM_PRIV_FLAG_SWITCH_LOOPBACK) priv_flags |= RNPM_SWITCH_LOOPBACK; if (pf_adapter->priv_flags & RNPM_PRIV_FLAG_VEB_ENABLE) priv_flags |= RNPM_VEB_ENABLE; if (pf_adapter->priv_flags & RNPM_PRIV_FLAG_PCIE_CACHE_ALIGN_PATCH) priv_flags |= RNPM_PCIE_CACHE_ALIGN_PATCH; if (adapter->priv_flags & RNPM_PRIV_FLAG_PADDING_DEBUG) priv_flags |= RNPM_PADDING_DEBUG; if (adapter->priv_flags & RNPM_PRIV_FLAG_PTP_DEBUG) priv_flags |= RNPM_PTP_FEATURE; if (adapter->priv_flags & RNPM_PRIV_FLAG_SIMUATE_DOWN) priv_flags |= RNPM_SIMULATE_DOWN; if (adapter->priv_flags & RNPM_PRIV_FLAG_TO_RPU) priv_flags |= RNPM_TO_RPU; if (pf_adapter->priv_flags & RNPM_PRIV_FLAG_LEN_ERR) priv_flags |= RNPM_LEN_ERR; if (pf_adapter->priv_flags & RNPM_PRIV_FLAG_FW_10G_1G_AUTO_DETCH_EN) priv_flags |= RNPM_FW_10G_1G_SFP_AUTO_DET_EN; if (pf_adapter->priv_flags & RNPM_PRIV_FLAG_FORCE_SPEED_ABLIY) priv_flags |= RNPM_FORCE_SPEED_ABLITY; if (adapter->priv_flags & RNPM_PRIV_FLAG_LLDP_EN_STAT) priv_flags |= RNPM_LLDP_EN_STAT; return priv_flags; } static int rnpm_priv_status_update(struct rnpm_adapter *adapter) { struct rnpm_pf_adapter *pf_adapter = adapter->pf_adapter; int i; u32 priv = 0; u32 data_old, data_new; unsigned long flags; spin_lock_irqsave(&pf_adapter->priv_flags_lock, flags); data_old = rd32(pf_adapter, RNPM_DMA_CONFIG); data_new = data_old; for (i = 0; i < pf_adapter->adapter_cnt; i++) { if (rnpm_port_is_valid(pf_adapter, i)) priv |= pf_adapter->adapter[i]->priv_flags; } if (priv & RNPM_PRIV_FLAG_PCIE_CACHE_ALIGN_PATCH) { pf_adapter->priv_flags |= RNPM_PRIV_FLAG_PCIE_CACHE_ALIGN_PATCH; SET_BIT(padding_enable, data_new); } else { pf_adapter->priv_flags &= (~RNPM_PRIV_FLAG_PCIE_CACHE_ALIGN_PATCH); CLR_BIT(padding_enable, data_new); } if (priv & RNPM_PRIV_FLAG_MAC_LOOPBACK) { pf_adapter->priv_flags |= RNPM_PRIV_FLAG_MAC_LOOPBACK; SET_BIT(mac_loopback, data_new); } else { pf_adapter->priv_flags &= (~RNPM_PRIV_FLAG_MAC_LOOPBACK); CLR_BIT(mac_loopback, data_new); } if (priv & RNPM_PRIV_FLAG_MAC_LOOPBACK) { pf_adapter->priv_flags |= RNPM_PRIV_FLAG_SWITCH_LOOPBACK; SET_BIT(switch_loopback, data_new); } else { pf_adapter->priv_flags &= (~RNPM_PRIV_FLAG_SWITCH_LOOPBACK); CLR_BIT(switch_loopback, data_new); } if (priv & RNPM_PRIV_FLAG_VEB_ENABLE) { pf_adapter->priv_flags |= RNPM_PRIV_FLAG_VEB_ENABLE; SET_BIT(veb_enable, data_new); } else { pf_adapter->priv_flags &= (~RNPM_PRIV_FLAG_VEB_ENABLE); CLR_BIT(veb_enable, data_new); } if (data_old != data_new) wr32(pf_adapter, RNPM_DMA_CONFIG, data_new); spin_unlock_irqrestore(&pf_adapter->priv_flags_lock, flags); return 0; } static int rnpm_priv_fw_10g_1g_auto_detch(struct rnpm_adapter *adapter) { struct rnpm_pf_adapter *pf_adapter = adapter->pf_adapter; unsigned long flags; spin_lock_irqsave(&pf_adapter->priv_flags_lock, flags); if (pf_adapter->priv_flags & RNPM_PRIV_FLAG_FW_10G_1G_AUTO_DETCH_EN) rnpm_hw_set_fw_10g_1g_auto_detch(&adapter->hw, 1); else rnpm_hw_set_fw_10g_1g_auto_detch(&adapter->hw, 0); spin_unlock_irqrestore(&pf_adapter->priv_flags_lock, flags); return 0; } static int rnpm_priv_err_mask_set(struct rnpm_adapter *adapter) { struct rnpm_pf_adapter *pf_adapter = adapter->pf_adapter; u32 data_old, data_new; unsigned long flags; spin_lock_irqsave(&pf_adapter->priv_flags_lock, flags); data_new = data_old = rd32(pf_adapter, RNPM_ETH_ERR_MASK_VECTOR); if (pf_adapter->priv_flags & RNPM_PRIV_FLAG_LEN_ERR) { // pf_adapter->priv_flags |= RNPM_PRIV_FLAG_LEN_ERR; data_new |= (ETH_ERR_PKT_LEN_ERR | ETH_ERR_HDR_LEN_ERR); } else { // pf_adapter->priv_flags &= (~RNPM_PRIV_FLAG_LEN_ERR); data_new &= ~(ETH_ERR_PKT_LEN_ERR | ETH_ERR_HDR_LEN_ERR); } if (data_old != data_new) wr32(pf_adapter, RNPM_ETH_ERR_MASK_VECTOR, data_new); spin_unlock_irqrestore(&pf_adapter->priv_flags_lock, flags); return 0; } /** * rnpm_set_priv_flags - set private flags * @dev: network interface device structure * @flags: bit flags to be set **/ static int rnpm_set_priv_flags(struct net_device *netdev, u32 priv_flags) { struct rnpm_adapter *adapter = (struct rnpm_adapter *)netdev_priv(netdev); struct rnpm_hw *hw = &adapter->hw; u32 orig_flags, new_flags; orig_flags = rd32(hw, RNPM_DMA_CONFIG); new_flags = orig_flags; if (priv_flags & RNPM_MAC_LOOPBACK) { SET_BIT(mac_loopback, new_flags); adapter->priv_flags |= RNPM_PRIV_FLAG_MAC_LOOPBACK; } else if (adapter->priv_flags & RNPM_PRIV_FLAG_MAC_LOOPBACK) { adapter->priv_flags &= (~RNPM_PRIV_FLAG_MAC_LOOPBACK); CLR_BIT(mac_loopback, new_flags); } if (priv_flags & RNPM_LLDP_EN_STAT) { if (rnpm_mbx_lldp_port_enable(hw, true) == 0) { // dump_stack(); adapter->priv_flags |= RNPM_PRIV_FLAG_LLDP_EN_STAT; } else { rnpm_err("%s: set lldp enable failed!\n", adapter->netdev->name); adapter->priv_flags &= (~RNPM_PRIV_FLAG_LLDP_EN_STAT); } } else if (adapter->priv_flags & RNPM_PRIV_FLAG_LLDP_EN_STAT) { adapter->priv_flags &= (~RNPM_PRIV_FLAG_LLDP_EN_STAT); rnpm_mbx_lldp_port_enable(hw, false); } if (priv_flags & RNPM_MPE_RELOAD) rnpm_rpu_mpe_start(adapter->pf_adapter); if (priv_flags & RNPM_SWITCH_LOOPBACK) { SET_BIT(switch_loopback, new_flags); adapter->priv_flags |= RNPM_PRIV_FLAG_SWITCH_LOOPBACK; } else if (adapter->priv_flags & RNPM_PRIV_FLAG_SWITCH_LOOPBACK) { adapter->priv_flags &= (~RNPM_PRIV_FLAG_SWITCH_LOOPBACK); CLR_BIT(switch_loopback, new_flags); } if (priv_flags & RNPM_VEB_ENABLE) { SET_BIT(veb_enable, new_flags); adapter->priv_flags |= RNPM_PRIV_FLAG_VEB_ENABLE; } else if (adapter->priv_flags & RNPM_PRIV_FLAG_VEB_ENABLE) { adapter->priv_flags &= (~RNPM_PRIV_FLAG_VEB_ENABLE); CLR_BIT(veb_enable, new_flags); } if (priv_flags & RNPM_PCIE_CACHE_ALIGN_PATCH) { SET_BIT(padding_enable, new_flags); adapter->priv_flags |= RNPM_PRIV_FLAG_PCIE_CACHE_ALIGN_PATCH; } else if (adapter->priv_flags & RNPM_PRIV_FLAG_PCIE_CACHE_ALIGN_PATCH) { adapter->priv_flags &= (~RNPM_PRIV_FLAG_PCIE_CACHE_ALIGN_PATCH); CLR_BIT(padding_enable, new_flags); } if (priv_flags & RNPM_PADDING_DEBUG) adapter->priv_flags |= RNPM_PRIV_FLAG_PADDING_DEBUG; else if (adapter->priv_flags & RNPM_PRIV_FLAG_PADDING_DEBUG) adapter->priv_flags &= (~RNPM_PRIV_FLAG_PADDING_DEBUG); if (priv_flags & RNPM_PTP_FEATURE) { adapter->priv_flags |= RNPM_PRIV_FLAG_PTP_DEBUG; adapter->flags2 |= ~RNPM_FLAG2_PTP_ENABLED; } else if (adapter->priv_flags & RNPM_PRIV_FLAG_PTP_DEBUG) { adapter->priv_flags &= (~RNPM_PRIV_FLAG_PTP_DEBUG); adapter->flags2 &= (~RNPM_FLAG2_PTP_ENABLED); } if (priv_flags & RNPM_SIMULATE_DOWN) { adapter->priv_flags |= RNPM_PRIV_FLAG_SIMUATE_DOWN; /* set check link again */ adapter->flags |= RNPM_FLAG_NEED_LINK_UPDATE; } else if (adapter->priv_flags & RNPM_PRIV_FLAG_SIMUATE_DOWN) { adapter->priv_flags &= (~RNPM_PRIV_FLAG_SIMUATE_DOWN); /* set check link again */ adapter->flags |= RNPM_FLAG_NEED_LINK_UPDATE; } if (priv_flags & RNPM_TO_RPU) adapter->priv_flags |= RNPM_PRIV_FLAG_TO_RPU; else if (adapter->priv_flags & RNPM_PRIV_FLAG_TO_RPU) adapter->priv_flags &= (~RNPM_PRIV_FLAG_TO_RPU); if (priv_flags & RNPM_FW_10G_1G_SFP_AUTO_DET_EN) { if (rnpm_card_partially_supported_10g_1g_sfp( adapter->pf_adapter)) { adapter->pf_adapter->priv_flags |= RNPM_PRIV_FLAG_FW_10G_1G_AUTO_DETCH_EN; rnpm_priv_fw_10g_1g_auto_detch(adapter); } else { return -EOPNOTSUPP; } } else if (adapter->pf_adapter->priv_flags & RNPM_PRIV_FLAG_FW_10G_1G_AUTO_DETCH_EN) { adapter->pf_adapter->priv_flags &= (~RNPM_PRIV_FLAG_FW_10G_1G_AUTO_DETCH_EN); rnpm_priv_fw_10g_1g_auto_detch(adapter); } if (priv_flags & RNPM_FORCE_SPEED_ABLITY) { if (adapter->hw.max_speed_1g == 1) { adapter->pf_adapter->priv_flags &= ~RNPM_PRIV_FLAG_FORCE_SPEED_ABLIY; adapter->pf_adapter->force_10g_1g_speed_ablity = false; rnpm_err( "%s: max speed is 1G cannot set force_speed_ablity priv-flags !\n", adapter->netdev->name); } else { adapter->pf_adapter->priv_flags |= RNPM_PRIV_FLAG_FORCE_SPEED_ABLIY; adapter->pf_adapter->force_10g_1g_speed_ablity = true; } } else if (adapter->pf_adapter->priv_flags & RNPM_PRIV_FLAG_FORCE_SPEED_ABLIY) { adapter->pf_adapter->priv_flags &= (~RNPM_PRIV_FLAG_FORCE_SPEED_ABLIY); rnpm_mbx_force_speed(hw, 0); set_bit(RNPM_PF_LINK_CHANGE, &adapter->pf_adapter->flags); adapter->pf_adapter->force_10g_1g_speed_ablity = false; } if (priv_flags & RNPM_LEN_ERR) { adapter->pf_adapter->priv_flags |= RNPM_PRIV_FLAG_LEN_ERR; rnpm_priv_err_mask_set(adapter); } else if (adapter->pf_adapter->priv_flags & RNPM_PRIV_FLAG_LEN_ERR) { adapter->pf_adapter->priv_flags &= (~RNPM_PRIV_FLAG_LEN_ERR); rnpm_priv_err_mask_set(adapter); } if (orig_flags != new_flags) { /* we not support this in multiports */ // if (adapter->flags & RNPM_FLAG_MUTIPORT_ENABLED) // return -EINVAL; wr32(hw, RNPM_DMA_CONFIG, new_flags); rnpm_priv_status_update(adapter); } /* if ft_padding changed */ if (CHK_BIT(padding_enable, orig_flags) != CHK_BIT(padding_enable, new_flags)) rnpm_msg_post_status(adapter, PF_FT_PADDING_STATUS); return 0; } /* ethtool register test data */ /** * rnpm_get_coalesce - get a netdev's coalesce settings * @netdev: the netdev to check * @ec: ethtool coalesce data structure * @kec: kernel coalesce parameter * @extack: kernel extack parameter * * Gets the coalesce settings for a particular netdev. Note that if user has * modified per-queue settings, this only guarantees to represent queue 0. See * __rnpm_get_coalesce for more details. **/ static int rnpm_get_coalesce(struct net_device *netdev, struct ethtool_coalesce *coal, struct kernel_ethtool_coalesce __maybe_unused *kernel_coal, struct netlink_ext_ack __maybe_unused *extack) { struct rnpm_adapter *adapter = netdev_priv(netdev); coal->use_adaptive_tx_coalesce = adapter->adaptive_tx_coal; coal->tx_coalesce_usecs = adapter->tx_usecs; coal->tx_coalesce_usecs_irq = 0; coal->tx_max_coalesced_frames = adapter->tx_frames; coal->tx_max_coalesced_frames_irq = adapter->tx_work_limit; coal->use_adaptive_rx_coalesce = adapter->adaptive_rx_coal; coal->rx_coalesce_usecs_irq = 0; coal->rx_coalesce_usecs = adapter->rx_usecs; coal->rx_max_coalesced_frames = adapter->rx_frames; coal->rx_max_coalesced_frames_irq = adapter->napi_budge; /* this is not support */ coal->pkt_rate_low = 0; coal->pkt_rate_high = 0; coal->rx_coalesce_usecs_low = 0; coal->rx_max_coalesced_frames_low = 0; coal->tx_coalesce_usecs_low = 0; coal->tx_max_coalesced_frames_low = 0; coal->rx_coalesce_usecs_high = 0; coal->rx_max_coalesced_frames_high = 0; coal->tx_coalesce_usecs_high = 0; coal->tx_max_coalesced_frames_high = 0; coal->rate_sample_interval = 0; return 0; } /** * rnpm_set_coalesce - set coalesce settings for every queue on the netdev * @netdev: the netdev to change * @ec: ethtool coalesce settings * @kec: kernel coalesce parameter * @extack: kernel extack parameter * * This will set each queue to the same coalesce settings. **/ static int rnpm_set_coalesce(struct net_device *netdev, struct ethtool_coalesce *ec, struct kernel_ethtool_coalesce __maybe_unused *kernel_coal, struct netlink_ext_ack __maybe_unused *extack) { int reset = 0; struct rnpm_adapter *adapter = netdev_priv(netdev); u32 value; /* we don't support close tx and rx coalesce */ if (!(ec->use_adaptive_tx_coalesce) || !(ec->use_adaptive_rx_coalesce)) return -EINVAL; if ((ec->tx_max_coalesced_frames_irq < RNPM_MIN_TX_WORK) || (ec->tx_max_coalesced_frames_irq > RNPM_MAX_TX_WORK)) return -EINVAL; value = ALIGN(ec->tx_max_coalesced_frames_irq, RNPM_WORK_ALIGN); if (adapter->tx_work_limit != value) { reset = 1; adapter->tx_work_limit = value; } if ((ec->tx_max_coalesced_frames < RNPM_MIN_TX_FRAME) || (ec->tx_max_coalesced_frames > RNPM_MAX_TX_FRAME)) return -EINVAL; if (adapter->tx_frames != ec->tx_max_coalesced_frames) { reset = 1; adapter->tx_frames = ec->tx_max_coalesced_frames; } if ((ec->tx_coalesce_usecs < RNPM_MIN_TX_USEC) || (ec->tx_coalesce_usecs > RNPM_MAX_TX_USEC)) return -EINVAL; if (adapter->tx_usecs != ec->tx_coalesce_usecs) { reset = 1; adapter->tx_usecs = ec->tx_coalesce_usecs; } if ((ec->rx_max_coalesced_frames_irq < RNPM_MIN_RX_WORK) || (ec->rx_max_coalesced_frames_irq > RNPM_MAX_RX_WORK)) return -EINVAL; value = ALIGN(ec->rx_max_coalesced_frames_irq, RNPM_WORK_ALIGN); if (adapter->napi_budge != ec->rx_max_coalesced_frames_irq) { reset = 1; adapter->napi_budge = ec->rx_max_coalesced_frames_irq; } if ((ec->rx_max_coalesced_frames < RNPM_MIN_RX_FRAME) || (ec->rx_max_coalesced_frames > RNPM_MAX_RX_FRAME)) return -EINVAL; if (adapter->rx_frames != ec->rx_max_coalesced_frames) { reset = 1; adapter->rx_frames = ec->rx_max_coalesced_frames; } if ((ec->rx_coalesce_usecs < RNPM_MIN_RX_USEC) || (ec->rx_coalesce_usecs > RNPM_MAX_RX_USEC)) return -EINVAL; if (adapter->rx_usecs != ec->rx_coalesce_usecs) { reset = 1; adapter->rx_usecs = ec->rx_coalesce_usecs; } /* other setup is not supported */ if ((ec->pkt_rate_low) || (ec->pkt_rate_high) || (ec->rx_coalesce_usecs_low) || (ec->rx_max_coalesced_frames_low) || (ec->tx_coalesce_usecs_low) || (ec->tx_max_coalesced_frames_low) || (ec->rx_coalesce_usecs_high) || (ec->rx_max_coalesced_frames_high) || (ec->tx_coalesce_usecs_high) || (ec->tx_max_coalesced_frames_high) || (ec->rate_sample_interval) || (ec->tx_coalesce_usecs_irq) || (ec->rx_coalesce_usecs_irq)) return -EINVAL; if (reset) return rnpm_setup_tc(netdev, netdev_get_num_tc(netdev)); return 0; } /** * rnpm_get_rss_hash_opts - Get RSS hash Input Set for each flow type * @pf: pointer to the physical function struct * @cmd: ethtool rxnfc command * * Returns Success if the flow is supported, else Invalid Input. **/ static int rnpm_get_rss_hash_opts(struct rnpm_adapter *adapter, struct ethtool_rxnfc *cmd) { cmd->data = 0; /* Report default options for RSS on rnpm */ switch (cmd->flow_type) { case TCP_V4_FLOW: cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; fallthrough; case UDP_V4_FLOW: case SCTP_V4_FLOW: cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; fallthrough; case AH_ESP_V4_FLOW: case AH_V4_FLOW: case ESP_V4_FLOW: case IPV4_FLOW: cmd->data |= RXH_IP_SRC | RXH_IP_DST; break; case TCP_V6_FLOW: cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; fallthrough; case UDP_V6_FLOW: case SCTP_V6_FLOW: cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; fallthrough; case AH_ESP_V6_FLOW: case AH_V6_FLOW: case ESP_V6_FLOW: case IPV6_FLOW: cmd->data |= RXH_IP_SRC | RXH_IP_DST; break; default: return -EINVAL; } return 0; } static int rnpm_set_rss_hash_opt(struct rnpm_adapter *adapter, struct ethtool_rxnfc *nfc) { if (nfc->data & ~(RXH_IP_SRC | RXH_IP_DST | RXH_L4_B_0_1 | RXH_L4_B_2_3)) return -EINVAL; switch (nfc->flow_type) { case TCP_V4_FLOW: case TCP_V6_FLOW: case UDP_V4_FLOW: case UDP_V6_FLOW: if (!(nfc->data & RXH_IP_SRC) || !(nfc->data & RXH_IP_DST) || !(nfc->data & RXH_L4_B_0_1) || !(nfc->data & RXH_L4_B_2_3)) return -EINVAL; break; case AH_ESP_V4_FLOW: case AH_V4_FLOW: case ESP_V4_FLOW: case SCTP_V4_FLOW: case AH_ESP_V6_FLOW: case AH_V6_FLOW: case ESP_V6_FLOW: case SCTP_V6_FLOW: if (!(nfc->data & RXH_IP_SRC) || !(nfc->data & RXH_IP_DST) || (nfc->data & RXH_L4_B_0_1) || (nfc->data & RXH_L4_B_2_3)) return -EINVAL; break; default: return -EINVAL; } return 0; } /** * rnpm_get_rxnfc - command to get RX flow classification rules * @netdev: network interface device structure * @cmd: ethtool rxnfc command * @rule_locs: pointer to store rule data * * Returns Success if the command is supported. **/ static int rnpm_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd, u32 *rule_locs) { struct rnpm_adapter *adapter = netdev_priv(dev); int ret = -EOPNOTSUPP; switch (cmd->cmd) { case ETHTOOL_GRXRINGS: cmd->data = adapter->num_rx_queues; ret = 0; break; case ETHTOOL_GRXCLSRLCNT: cmd->rule_cnt = adapter->fdir_filter_count; ret = 0; break; case ETHTOOL_GRXCLSRULE: break; case ETHTOOL_GRXCLSRLALL: break; case ETHTOOL_GRXFH: ret = rnpm_get_rss_hash_opts(adapter, cmd); break; default: break; } return ret; } /** * rnpm_set_rxnfc - command to set RX flow classification rules * @dev: network interface device structure * @cmd: ethtool rxnfc command * Returns Success if the command is supported. **/ static int rnpm_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd) { struct rnpm_adapter *adapter = netdev_priv(dev); int ret = -EOPNOTSUPP; switch (cmd->cmd) { case ETHTOOL_SRXCLSRLINS: break; case ETHTOOL_SRXCLSRLDEL: break; case ETHTOOL_SRXFH: ret = rnpm_set_rss_hash_opt(adapter, cmd); break; default: break; } return ret; } /** * rnpm_get_ethtool_stats - copy stat values into supplied buffer * @netdev: the netdev to collect stats for * @stats: ethtool stats command structure * @data: ethtool supplied buffer * * Copy the stats values for this netdev into the buffer. Expects data to be * pre-allocated to the size returned by i40e_get_stats_count.. Note that all * statistics must be copied in a static order, and the count must not change * for a given netdev. See i40e_get_stats_count for more details. * * If a statistic is not currently valid (such as a disabled queue), this * function reports its value as zero. **/ static void rnpm_get_ethtool_stats(struct net_device *netdev, struct ethtool_stats *stats, u64 *data) { struct rnpm_adapter *adapter = netdev_priv(netdev); struct rnpm_hw *hw = &adapter->hw; struct net_device_stats *net_stats = &netdev->stats; struct rnpm_ring *ring; int i, j; char *p = NULL; rnpm_update_stats(adapter); for (i = 0; i < RNPM_GLOBAL_STATS_LEN; i++) { p = (char *)net_stats + rnpm_gstrings_net_stats[i].stat_offset; data[i] = (rnpm_gstrings_net_stats[i].sizeof_stat == sizeof(u64)) ? *(u64 *)p : *(u32 *)p; } for (j = 0; j < RNPM_HWSTRINGS_STATS_LEN; j++, i++) { p = (char *)adapter + rnpm_hwstrings_stats[j].stat_offset; data[i] = (rnpm_hwstrings_stats[j].sizeof_stat == sizeof(u64)) ? *(u64 *)p : *(u32 *)p; } BUG_ON(RNPM_NUM_TX_QUEUES != RNPM_NUM_RX_QUEUES); for (j = 0; j < RNPM_NUM_TX_QUEUES; j++) { /* tx-ring */ ring = adapter->tx_ring[j]; if (!ring) { data[i++] = 0; data[i++] = 0; data[i++] = 0; data[i++] = 0; data[i++] = 0; data[i++] = 0; data[i++] = 0; data[i++] = 0; data[i++] = 0; data[i++] = 0; data[i++] = 0; data[i++] = 0; data[i++] = 0; data[i++] = 0; data[i++] = 0; data[i++] = 0; data[i++] = 0; data[i++] = 0; data[i++] = 0; data[i++] = 0; data[i++] = 0; data[i++] = 0; data[i++] = 0; data[i++] = 0; data[i++] = 0; data[i++] = 0; data[i++] = 0; data[i++] = 0; data[i++] = 0; data[i++] = 0; data[i++] = 0; data[i++] = 0; data[i++] = 0; data[i++] = 0; data[i++] = 0; data[i++] = 0; data[i++] = 0; data[i++] = 0; data[i++] = 0; data[i++] = 0; continue; } data[i++] = ring->stats.packets; data[i++] = ring->stats.bytes; data[i++] = ring->tx_stats.restart_queue; data[i++] = ring->tx_stats.tx_busy; data[i++] = ring->tx_stats.tx_done_old; data[i++] = ring->tx_stats.clean_desc; data[i++] = ring->tx_stats.poll_count; data[i++] = ring->tx_stats.irq_more_count; /* rnpm_tx_queue_ring_stat */ data[i++] = rd32(hw, RNPM_DMA_REG_TX_DESC_BUF_HEAD( ring->rnpm_queue_idx)); data[i++] = rd32(hw, RNPM_DMA_REG_TX_DESC_BUF_TAIL( ring->rnpm_queue_idx)); data[i++] = ring->next_to_clean; data[i++] = ring->next_to_use; data[i++] = ring->tx_stats.send_bytes; data[i++] = ring->tx_stats.send_bytes_to_hw; data[i++] = ring->tx_stats.todo_update; data[i++] = ring->tx_stats.send_done_bytes; data[i++] = ring->tx_stats.vlan_add; if (ring->tx_stats.tx_next_to_clean == -1) data[i++] = ring->count; else data[i++] = ring->tx_stats.tx_next_to_clean; data[i++] = ring->tx_stats.tx_irq_miss; data[i++] = ring->tx_stats.tx_equal_count; /* rx-ring */ ring = adapter->rx_ring[j]; if (!ring) { data[i++] = 0; data[i++] = 0; data[i++] = 0; data[i++] = 0; data[i++] = 0; data[i++] = 0; data[i++] = 0; data[i++] = 0; data[i++] = 0; data[i++] = 0; data[i++] = 0; data[i++] = 0; data[i++] = 0; data[i++] = 0; data[i++] = 0; data[i++] = 0; data[i++] = 0; data[i++] = 0; data[i++] = 0; data[i++] = 0; continue; } data[i++] = ring->stats.packets; data[i++] = ring->stats.bytes; data[i++] = ring->rx_stats.driver_drop_packets; data[i++] = ring->rx_stats.rsc_count; data[i++] = ring->rx_stats.rsc_flush; data[i++] = ring->rx_stats.non_eop_descs; data[i++] = ring->rx_stats.alloc_rx_page_failed; data[i++] = ring->rx_stats.alloc_rx_buff_failed; data[i++] = ring->rx_stats.csum_err; data[i++] = ring->rx_stats.csum_good; data[i++] = ring->rx_stats.poll_again_count; data[i++] = ring->rx_stats.vlan_remove; data[i++] = ring->rx_stats.alloc_rx_page; /* rnpm_rx_queue_ring_stat */ data[i++] = rd32(hw, RNPM_DMA_REG_RX_DESC_BUF_HEAD( ring->rnpm_queue_idx)); data[i++] = rd32(hw, RNPM_DMA_REG_RX_DESC_BUF_TAIL( ring->rnpm_queue_idx)); data[i++] = ring->next_to_use; data[i++] = ring->next_to_clean; if (ring->rx_stats.rx_next_to_clean == -1) data[i++] = ring->count; else data[i++] = ring->rx_stats.rx_next_to_clean; data[i++] = ring->rx_stats.rx_irq_miss; data[i++] = ring->rx_stats.rx_equal_count; data[i++] = ring->rx_stats.rx_poll_packets; data[i++] = ring->rx_stats.rx_poll_avg_packets; data[i++] = ring->rx_stats.rx_poll_itr; } } enum { PART_FW, PART_CFG, PART_MACSN, PART_PCSPHY, PART_PXE, }; #define UCFG_OFF 0x41000 #define UCFG_SZ (4096) #define PXE_OFF 0x4a000 #define PXE_SZ (512 * 1024) static int rnpm_flash_firmware(struct rnpm_adapter *adapter, int region, const u8 *data, int bytes) { struct rnpm_hw *hw = &adapter->hw; switch (region) { case PART_FW: { if (*((u32 *)(data + 28)) != 0xA51BBEAF) return -EINVAL; if (bytes > PXE_OFF) { // fw with pxe int err; int wbytes_seg1 = bytes - PXE_OFF; if (wbytes_seg1 > PXE_SZ) wbytes_seg1 = PXE_SZ; // fw err = rnpm_fw_update(hw, PART_FW, data, UCFG_OFF); if (err) return err; // skip ucfg flush only pxe err = rnpm_fw_update(hw, PART_PXE, data + PXE_OFF, wbytes_seg1); if (err) return err; return 0; } break; } case PART_CFG: { if (*((u32 *)(data)) != 0x00010cf9) return -EINVAL; break; } case PART_MACSN: { break; } case PART_PCSPHY: { if (*((u16 *)(data)) != 0x081d) return -EINVAL; break; } case PART_PXE: { if ((*((u16 *)(data)) != 0xaa55) && (*((u16 *)(data)) != 0x5a4d)) { return -EINVAL; } break; } default: { return -EINVAL; } } return rnpm_fw_update(hw, region, data, bytes); } static int rnpm_flash_firmware_from_file(struct net_device *dev, struct rnpm_adapter *adapter, int region, const char *filename) { const struct firmware *fw; int rc; rc = request_firmware(&fw, filename, &dev->dev); if (rc != 0) { netdev_err(dev, "Error %d requesting firmware file: %s\n", rc, filename); return rc; } rc = rnpm_flash_firmware(adapter, region, fw->data, fw->size); release_firmware(fw); return rc; } static int rnpm_flash_device(struct net_device *dev, struct ethtool_flash *flash) { struct rnpm_adapter *adapter = netdev_priv(dev); if (IS_VF(adapter->hw.pfvfnum)) { netdev_err(dev, "flashdev not supported from a virtual function\n"); return -EINVAL; } return rnpm_flash_firmware_from_file(dev, adapter, flash->region, flash->data); } static uint32_t rnpm_rss_indir_size(struct net_device *netdev) { struct rnpm_adapter *adapter = netdev_priv(netdev); return rnpm_rss_indir_tbl_entries(adapter); } static u32 rnpm_get_rxfh_key_size(struct net_device *netdev) { return RNPM_RSS_KEY_SIZE; } static void rnpm_get_reta(struct rnpm_adapter *adapter, u32 *indir) { int i, reta_size = rnpm_rss_indir_tbl_entries(adapter); u16 rss_m = adapter->ring_feature[RING_F_RSS].mask; if (adapter->flags & RNPM_FLAG_SRIOV_ENABLED) rss_m = adapter->ring_feature[RING_F_RSS].indices - 1; for (i = 0; i < reta_size; i++) { if (adapter->flags & RNPM_FLAG_RXHASH_DISABLE) indir[i] = 0; else indir[i] = adapter->rss_indir_tbl[i] & rss_m; } } static int rnpm_get_rxfh(struct net_device *netdev, u32 *indir, u8 *key, u8 *hfunc) { struct rnpm_adapter *adapter = netdev_priv(netdev); struct rnpm_pf_adapter *pf_adapter = adapter->pf_adapter; if (hfunc) *hfunc = ETH_RSS_HASH_TOP; if (indir) rnpm_get_reta(adapter, indir); if (key) memcpy(key, pf_adapter->rss_key, rnpm_get_rxfh_key_size(netdev)); return 0; } static int rnpm_rss_indir_tbl_max(struct rnpm_adapter *adapter) { if (adapter->hw.rss_type == rnpm_rss_uv3p) return 8; else if (adapter->hw.rss_type == rnpm_rss_uv440) return 128; else if (adapter->hw.rss_type == rnpm_rss_n10) return 128; else return 128; } /** * rnpm_set_rxfh - set the rx flow hash indirection table * @netdev: network interface device structure * @indir: indirection table * @key: hash key * @hfunc: hash function to use * * Returns -EINVAL if the table specifies an invalid queue id, otherwise * returns 0 after programming the table. **/ static int rnpm_set_rxfh(struct net_device *netdev, const u32 *indir, const u8 *key, const u8 hfunc) { struct rnpm_adapter *adapter = netdev_priv(netdev); struct rnpm_pf_adapter *pf_adapter = adapter->pf_adapter; u16 i; u32 reta_entries = rnpm_rss_indir_tbl_entries(adapter); unsigned long flags; if (hfunc) return -EOPNOTSUPP; /* Verify user input. */ if (indir) { int max_queues = min_t(int, adapter->num_rx_queues, rnpm_rss_indir_tbl_max(adapter)); /* in this mode ,do not change rss table */ if (adapter->flags & RNPM_FLAG_RXHASH_DISABLE) return -EINVAL; /*Allow at least 2 queues w/ SR-IOV.*/ if ((adapter->flags & RNPM_FLAG_SRIOV_ENABLED) && (max_queues < 2)) max_queues = 2; /* Verify user input. */ for (i = 0; i < reta_entries; i++) if (indir[i] >= max_queues) return -EINVAL; /* store rss tbl */ for (i = 0; i < reta_entries; i++) adapter->rss_indir_tbl[i] = indir[i]; rnpm_store_reta(adapter); } /* Fill out the rss hash key */ if (key) { /* not support key setup in multiports */ if (adapter->flags & RNPM_FLAG_MUTIPORT_ENABLED) return -EINVAL; spin_lock_irqsave(&pf_adapter->key_setup_lock, flags); memcpy(pf_adapter->rss_key, key, rnpm_get_rxfh_key_size(netdev)); rnpm_store_key(pf_adapter); spin_unlock_irqrestore(&pf_adapter->key_setup_lock, flags); } return 0; } void rnpm_get_phy_statistics(struct net_device *netdev, struct ethtool_stats *stats, u64 *data) { struct rnpm_adapter *adapter = netdev_priv(netdev); struct rnpm_hw *hw = &adapter->hw; struct phy_statistics ps; if (rnpm_mbx_get_phy_statistics(hw, (u8 *)&ps) != 0) return; *data++ = ps.yt.pkg_ib_valid; *data++ = ps.yt.pkg_ib_os_good; *data++ = ps.yt.pkg_ib_us_good; *data++ = ps.yt.pkg_ib_err; *data++ = ps.yt.pkg_ib_os_bad; *data++ = ps.yt.pkg_ib_frag; *data++ = ps.yt.pkg_ib_nosfd; *data++ = ps.yt.pkg_ob_valid; *data++ = ps.yt.pkg_ob_os_good; *data++ = ps.yt.pkg_ob_us_good; *data++ = ps.yt.pkg_ob_err; *data++ = ps.yt.pkg_ob_os_bad; *data++ = ps.yt.pkg_ob_frag; *data++ = ps.yt.pkg_ob_nosfd; } static int rnpm_nway_reset(struct net_device *netdev) { /* restart autonegotiation */ struct rnpm_adapter *adapter = netdev_priv(netdev); if (test_bit(__RNPM_DOWN, &adapter->state)) return 0; netdev_info(netdev, "NIC Link is Down\n"); rnpm_down(adapter); msleep(20); rnpm_up(adapter); return 0; } static const struct ethtool_ops rnpm_ethtool_ops = { .supported_coalesce_params = 0 | ETHTOOL_COALESCE_USECS | ETHTOOL_COALESCE_MAX_FRAMES_IRQ | ETHTOOL_COALESCE_MAX_FRAMES, .get_link_ksettings = rnpm_get_link_ksettings, .set_link_ksettings = rnpm_set_link_ksettings, .get_drvinfo = rnpm_get_drvinfo, .get_regs_len = rnpm_get_regs_len, .get_regs = rnpm_get_regs, .get_wol = rnpm_get_wol, .set_wol = rnpm_set_wol, .nway_reset = rnpm_nway_reset, .get_link = ethtool_op_get_link, .get_ringparam = rnpm_get_ringparam, .set_ringparam = rnpm_set_ringparam, .get_pauseparam = rnpm_get_pauseparam, .set_pauseparam = rnpm_set_pauseparam, .get_msglevel = rnpm_get_msglevel, .set_msglevel = rnpm_set_msglevel, .get_fecparam = rnpm_get_fecparam, .set_fecparam = rnpm_set_fecparam, .self_test = rnpm_diag_test, .get_strings = rnpm_get_strings, .set_phys_id = rnpm_set_phys_id, .get_sset_count = rnpm_get_sset_count, .get_priv_flags = rnpm_get_priv_flags, .set_priv_flags = rnpm_set_priv_flags, .get_ethtool_stats = rnpm_get_ethtool_stats, .get_coalesce = rnpm_get_coalesce, .set_coalesce = rnpm_set_coalesce, .get_rxnfc = rnpm_get_rxnfc, .set_rxnfc = rnpm_set_rxnfc, .get_channels = rnpm_get_channels, .set_channels = rnpm_set_channels, .get_module_info = rnpm_get_module_info, .get_module_eeprom = rnpm_get_module_eeprom, .get_ts_info = rnpm_get_ts_info, .get_rxfh_indir_size = rnpm_rss_indir_size, .get_rxfh_key_size = rnpm_get_rxfh_key_size, .get_rxfh = rnpm_get_rxfh, .set_rxfh = rnpm_set_rxfh, .get_dump_flag = rnpm_get_dump_flag, .get_dump_data = rnpm_get_dump_data, .set_dump = rnpm_set_dump, .flash_device = rnpm_flash_device, .get_ethtool_phy_stats = rnpm_get_phy_statistics, }; void rnpm_set_ethtool_ops(struct net_device *netdev) { netdev->ethtool_ops = &rnpm_ethtool_ops; } // #endif /* SIOCETHTOOL */