Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Linux/drivers/net/ethernet/intel/ice/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 135 kB image not shown  

Quelle  ice_ethtool.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2018, Intel Corporation. */

/* ethtool support for ice */

#include "ice.h"
#include "ice_ethtool.h"
#include "ice_flow.h"
#include "ice_fltr.h"
#include "ice_lib.h"
#include "ice_dcb_lib.h"
#include <net/dcbnl.h>

struct ice_stats {
 char stat_string[ETH_GSTRING_LEN];
 int sizeof_stat;
 int stat_offset;
};

#define ICE_STAT(_type, _name, _stat) { \
 .stat_string = _name, \
 .sizeof_stat = sizeof_field(_type, _stat), \
 .stat_offset = offsetof(_type, _stat) \
}

#define ICE_VSI_STAT(_name, _stat) \
  ICE_STAT(struct ice_vsi, _name, _stat)
#define ICE_PF_STAT(_name, _stat) \
  ICE_STAT(struct ice_pf, _name, _stat)

static int ice_q_stats_len(struct net_device *netdev)
{
 struct ice_netdev_priv *np = netdev_priv(netdev);

 return ((np->vsi->alloc_txq + np->vsi->alloc_rxq) *
  (sizeof(struct ice_q_stats) / sizeof(u64)));
}

#define ICE_PF_STATS_LEN ARRAY_SIZE(ice_gstrings_pf_stats)
#define ICE_VSI_STATS_LEN ARRAY_SIZE(ice_gstrings_vsi_stats)

#define ICE_PFC_STATS_LEN ( \
  (sizeof_field(struct ice_pf, stats.priority_xoff_rx) + \
   sizeof_field(struct ice_pf, stats.priority_xon_rx) + \
   sizeof_field(struct ice_pf, stats.priority_xoff_tx) + \
   sizeof_field(struct ice_pf, stats.priority_xon_tx)) \
   / sizeof(u64))
#define ICE_ALL_STATS_LEN(n) (ICE_PF_STATS_LEN + ICE_PFC_STATS_LEN + \
     ICE_VSI_STATS_LEN + ice_q_stats_len(n))

static const struct ice_stats ice_gstrings_vsi_stats[] = {
 ICE_VSI_STAT("rx_unicast", eth_stats.rx_unicast),
 ICE_VSI_STAT("tx_unicast", eth_stats.tx_unicast),
 ICE_VSI_STAT("rx_multicast", eth_stats.rx_multicast),
 ICE_VSI_STAT("tx_multicast", eth_stats.tx_multicast),
 ICE_VSI_STAT("rx_broadcast", eth_stats.rx_broadcast),
 ICE_VSI_STAT("tx_broadcast", eth_stats.tx_broadcast),
 ICE_VSI_STAT("rx_bytes", eth_stats.rx_bytes),
 ICE_VSI_STAT("tx_bytes", eth_stats.tx_bytes),
 ICE_VSI_STAT("rx_dropped", eth_stats.rx_discards),
 ICE_VSI_STAT("rx_unknown_protocol", eth_stats.rx_unknown_protocol),
 ICE_VSI_STAT("rx_alloc_fail", rx_buf_failed),
 ICE_VSI_STAT("rx_pg_alloc_fail", rx_page_failed),
 ICE_VSI_STAT("tx_errors", eth_stats.tx_errors),
 ICE_VSI_STAT("tx_linearize", tx_linearize),
 ICE_VSI_STAT("tx_busy", tx_busy),
 ICE_VSI_STAT("tx_restart", tx_restart),
};

enum ice_ethtool_test_id {
 ICE_ETH_TEST_REG = 0,
 ICE_ETH_TEST_EEPROM,
 ICE_ETH_TEST_INTR,
 ICE_ETH_TEST_LOOP,
 ICE_ETH_TEST_LINK,
};

static const char ice_gstrings_test[][ETH_GSTRING_LEN] = {
 "Register test (offline)",
 "EEPROM test (offline)",
 "Interrupt test (offline)",
 "Loopback test (offline)",
 "Link test (on/offline)",
};

#define ICE_TEST_LEN (sizeof(ice_gstrings_test) / ETH_GSTRING_LEN)

/* These PF_STATs might look like duplicates of some NETDEV_STATs,
 * but they aren't. This device is capable of supporting multiple
 * VSIs/netdevs on a single PF. The NETDEV_STATs are for individual
 * netdevs whereas the PF_STATs are for the physical function that's
 * hosting these netdevs.
 *
 * The PF_STATs are appended to the netdev stats only when ethtool -S
 * is queried on the base PF netdev.
 */

static const struct ice_stats ice_gstrings_pf_stats[] = {
 ICE_PF_STAT("rx_bytes.nic", stats.eth.rx_bytes),
 ICE_PF_STAT("tx_bytes.nic", stats.eth.tx_bytes),
 ICE_PF_STAT("rx_unicast.nic", stats.eth.rx_unicast),
 ICE_PF_STAT("tx_unicast.nic", stats.eth.tx_unicast),
 ICE_PF_STAT("rx_multicast.nic", stats.eth.rx_multicast),
 ICE_PF_STAT("tx_multicast.nic", stats.eth.tx_multicast),
 ICE_PF_STAT("rx_broadcast.nic", stats.eth.rx_broadcast),
 ICE_PF_STAT("tx_broadcast.nic", stats.eth.tx_broadcast),
 ICE_PF_STAT("tx_errors.nic", stats.eth.tx_errors),
 ICE_PF_STAT("tx_timeout.nic", tx_timeout_count),
 ICE_PF_STAT("rx_size_64.nic", stats.rx_size_64),
 ICE_PF_STAT("tx_size_64.nic", stats.tx_size_64),
 ICE_PF_STAT("rx_size_127.nic", stats.rx_size_127),
 ICE_PF_STAT("tx_size_127.nic", stats.tx_size_127),
 ICE_PF_STAT("rx_size_255.nic", stats.rx_size_255),
 ICE_PF_STAT("tx_size_255.nic", stats.tx_size_255),
 ICE_PF_STAT("rx_size_511.nic", stats.rx_size_511),
 ICE_PF_STAT("tx_size_511.nic", stats.tx_size_511),
 ICE_PF_STAT("rx_size_1023.nic", stats.rx_size_1023),
 ICE_PF_STAT("tx_size_1023.nic", stats.tx_size_1023),
 ICE_PF_STAT("rx_size_1522.nic", stats.rx_size_1522),
 ICE_PF_STAT("tx_size_1522.nic", stats.tx_size_1522),
 ICE_PF_STAT("rx_size_big.nic", stats.rx_size_big),
 ICE_PF_STAT("tx_size_big.nic", stats.tx_size_big),
 ICE_PF_STAT("link_xon_rx.nic", stats.link_xon_rx),
 ICE_PF_STAT("link_xon_tx.nic", stats.link_xon_tx),
 ICE_PF_STAT("link_xoff_rx.nic", stats.link_xoff_rx),
 ICE_PF_STAT("link_xoff_tx.nic", stats.link_xoff_tx),
 ICE_PF_STAT("tx_dropped_link_down.nic", stats.tx_dropped_link_down),
 ICE_PF_STAT("rx_undersize.nic", stats.rx_undersize),
 ICE_PF_STAT("rx_fragments.nic", stats.rx_fragments),
 ICE_PF_STAT("rx_oversize.nic", stats.rx_oversize),
 ICE_PF_STAT("rx_jabber.nic", stats.rx_jabber),
 ICE_PF_STAT("rx_csum_bad.nic", hw_csum_rx_error),
 ICE_PF_STAT("rx_eipe_error.nic", hw_rx_eipe_error),
 ICE_PF_STAT("rx_dropped.nic", stats.eth.rx_discards),
 ICE_PF_STAT("rx_crc_errors.nic", stats.crc_errors),
 ICE_PF_STAT("illegal_bytes.nic", stats.illegal_bytes),
 ICE_PF_STAT("mac_local_faults.nic", stats.mac_local_faults),
 ICE_PF_STAT("mac_remote_faults.nic", stats.mac_remote_faults),
 ICE_PF_STAT("fdir_sb_match.nic", stats.fd_sb_match),
 ICE_PF_STAT("fdir_sb_status.nic", stats.fd_sb_status),
 ICE_PF_STAT("tx_hwtstamp_skipped", ptp.tx_hwtstamp_skipped),
 ICE_PF_STAT("tx_hwtstamp_timeouts", ptp.tx_hwtstamp_timeouts),
 ICE_PF_STAT("tx_hwtstamp_flushed", ptp.tx_hwtstamp_flushed),
 ICE_PF_STAT("tx_hwtstamp_discarded", ptp.tx_hwtstamp_discarded),
 ICE_PF_STAT("late_cached_phc_updates", ptp.late_cached_phc_updates),
};

static const u32 ice_regs_dump_list[] = {
 PFGEN_STATE,
 PRTGEN_STATUS,
 QRX_CTRL(0),
 QINT_TQCTL(0),
 QINT_RQCTL(0),
 PFINT_OICR_ENA,
 QRX_ITR(0),
#define GLDCB_TLPM_PCI_DM   0x000A0180
 GLDCB_TLPM_PCI_DM,
#define GLDCB_TLPM_TC2PFC   0x000A0194
 GLDCB_TLPM_TC2PFC,
#define TCDCB_TLPM_WAIT_DM(_i)   (0x000A0080 + ((_i) * 4))
 TCDCB_TLPM_WAIT_DM(0),
 TCDCB_TLPM_WAIT_DM(1),
 TCDCB_TLPM_WAIT_DM(2),
 TCDCB_TLPM_WAIT_DM(3),
 TCDCB_TLPM_WAIT_DM(4),
 TCDCB_TLPM_WAIT_DM(5),
 TCDCB_TLPM_WAIT_DM(6),
 TCDCB_TLPM_WAIT_DM(7),
 TCDCB_TLPM_WAIT_DM(8),
 TCDCB_TLPM_WAIT_DM(9),
 TCDCB_TLPM_WAIT_DM(10),
 TCDCB_TLPM_WAIT_DM(11),
 TCDCB_TLPM_WAIT_DM(12),
 TCDCB_TLPM_WAIT_DM(13),
 TCDCB_TLPM_WAIT_DM(14),
 TCDCB_TLPM_WAIT_DM(15),
 TCDCB_TLPM_WAIT_DM(16),
 TCDCB_TLPM_WAIT_DM(17),
 TCDCB_TLPM_WAIT_DM(18),
 TCDCB_TLPM_WAIT_DM(19),
 TCDCB_TLPM_WAIT_DM(20),
 TCDCB_TLPM_WAIT_DM(21),
 TCDCB_TLPM_WAIT_DM(22),
 TCDCB_TLPM_WAIT_DM(23),
 TCDCB_TLPM_WAIT_DM(24),
 TCDCB_TLPM_WAIT_DM(25),
 TCDCB_TLPM_WAIT_DM(26),
 TCDCB_TLPM_WAIT_DM(27),
 TCDCB_TLPM_WAIT_DM(28),
 TCDCB_TLPM_WAIT_DM(29),
 TCDCB_TLPM_WAIT_DM(30),
 TCDCB_TLPM_WAIT_DM(31),
#define GLPCI_WATMK_CLNT_PIPEMON  0x000BFD90
 GLPCI_WATMK_CLNT_PIPEMON,
#define GLPCI_CUR_CLNT_COMMON   0x000BFD84
 GLPCI_CUR_CLNT_COMMON,
#define GLPCI_CUR_CLNT_PIPEMON   0x000BFD88
 GLPCI_CUR_CLNT_PIPEMON,
#define GLPCI_PCIERR    0x0009DEB0
 GLPCI_PCIERR,
#define GLPSM_DEBUG_CTL_STATUS   0x000B0600
 GLPSM_DEBUG_CTL_STATUS,
#define GLPSM0_DEBUG_FIFO_OVERFLOW_DETECT 0x000B0680
 GLPSM0_DEBUG_FIFO_OVERFLOW_DETECT,
#define GLPSM0_DEBUG_FIFO_UNDERFLOW_DETECT 0x000B0684
 GLPSM0_DEBUG_FIFO_UNDERFLOW_DETECT,
#define GLPSM0_DEBUG_DT_OUT_OF_WINDOW  0x000B0688
 GLPSM0_DEBUG_DT_OUT_OF_WINDOW,
#define GLPSM0_DEBUG_INTF_HW_ERROR_DETECT 0x000B069C
 GLPSM0_DEBUG_INTF_HW_ERROR_DETECT,
#define GLPSM0_DEBUG_MISC_HW_ERROR_DETECT 0x000B06A0
 GLPSM0_DEBUG_MISC_HW_ERROR_DETECT,
#define GLPSM1_DEBUG_FIFO_OVERFLOW_DETECT 0x000B0E80
 GLPSM1_DEBUG_FIFO_OVERFLOW_DETECT,
#define GLPSM1_DEBUG_FIFO_UNDERFLOW_DETECT 0x000B0E84
 GLPSM1_DEBUG_FIFO_UNDERFLOW_DETECT,
#define GLPSM1_DEBUG_SRL_FIFO_OVERFLOW_DETECT 0x000B0E88
 GLPSM1_DEBUG_SRL_FIFO_OVERFLOW_DETECT,
#define GLPSM1_DEBUG_SRL_FIFO_UNDERFLOW_DETECT  0x000B0E8C
 GLPSM1_DEBUG_SRL_FIFO_UNDERFLOW_DETECT,
#define GLPSM1_DEBUG_MISC_HW_ERROR_DETECT       0x000B0E90
 GLPSM1_DEBUG_MISC_HW_ERROR_DETECT,
#define GLPSM2_DEBUG_FIFO_OVERFLOW_DETECT       0x000B1680
 GLPSM2_DEBUG_FIFO_OVERFLOW_DETECT,
#define GLPSM2_DEBUG_FIFO_UNDERFLOW_DETECT      0x000B1684
 GLPSM2_DEBUG_FIFO_UNDERFLOW_DETECT,
#define GLPSM2_DEBUG_MISC_HW_ERROR_DETECT       0x000B1688
 GLPSM2_DEBUG_MISC_HW_ERROR_DETECT,
#define GLTDPU_TCLAN_COMP_BOB(_i)               (0x00049ADC + ((_i) * 4))
 GLTDPU_TCLAN_COMP_BOB(1),
 GLTDPU_TCLAN_COMP_BOB(2),
 GLTDPU_TCLAN_COMP_BOB(3),
 GLTDPU_TCLAN_COMP_BOB(4),
 GLTDPU_TCLAN_COMP_BOB(5),
 GLTDPU_TCLAN_COMP_BOB(6),
 GLTDPU_TCLAN_COMP_BOB(7),
 GLTDPU_TCLAN_COMP_BOB(8),
#define GLTDPU_TCB_CMD_BOB(_i)                  (0x0004975C + ((_i) * 4))
 GLTDPU_TCB_CMD_BOB(1),
 GLTDPU_TCB_CMD_BOB(2),
 GLTDPU_TCB_CMD_BOB(3),
 GLTDPU_TCB_CMD_BOB(4),
 GLTDPU_TCB_CMD_BOB(5),
 GLTDPU_TCB_CMD_BOB(6),
 GLTDPU_TCB_CMD_BOB(7),
 GLTDPU_TCB_CMD_BOB(8),
#define GLTDPU_PSM_UPDATE_BOB(_i)               (0x00049B5C + ((_i) * 4))
 GLTDPU_PSM_UPDATE_BOB(1),
 GLTDPU_PSM_UPDATE_BOB(2),
 GLTDPU_PSM_UPDATE_BOB(3),
 GLTDPU_PSM_UPDATE_BOB(4),
 GLTDPU_PSM_UPDATE_BOB(5),
 GLTDPU_PSM_UPDATE_BOB(6),
 GLTDPU_PSM_UPDATE_BOB(7),
 GLTDPU_PSM_UPDATE_BOB(8),
#define GLTCB_CMD_IN_BOB(_i)                    (0x000AE288 + ((_i) * 4))
 GLTCB_CMD_IN_BOB(1),
 GLTCB_CMD_IN_BOB(2),
 GLTCB_CMD_IN_BOB(3),
 GLTCB_CMD_IN_BOB(4),
 GLTCB_CMD_IN_BOB(5),
 GLTCB_CMD_IN_BOB(6),
 GLTCB_CMD_IN_BOB(7),
 GLTCB_CMD_IN_BOB(8),
#define GLLAN_TCLAN_FETCH_CTL_FBK_BOB_CTL(_i)   (0x000FC148 + ((_i) * 4))
 GLLAN_TCLAN_FETCH_CTL_FBK_BOB_CTL(1),
 GLLAN_TCLAN_FETCH_CTL_FBK_BOB_CTL(2),
 GLLAN_TCLAN_FETCH_CTL_FBK_BOB_CTL(3),
 GLLAN_TCLAN_FETCH_CTL_FBK_BOB_CTL(4),
 GLLAN_TCLAN_FETCH_CTL_FBK_BOB_CTL(5),
 GLLAN_TCLAN_FETCH_CTL_FBK_BOB_CTL(6),
 GLLAN_TCLAN_FETCH_CTL_FBK_BOB_CTL(7),
 GLLAN_TCLAN_FETCH_CTL_FBK_BOB_CTL(8),
#define GLLAN_TCLAN_FETCH_CTL_SCHED_BOB_CTL(_i) (0x000FC248 + ((_i) * 4))
 GLLAN_TCLAN_FETCH_CTL_SCHED_BOB_CTL(1),
 GLLAN_TCLAN_FETCH_CTL_SCHED_BOB_CTL(2),
 GLLAN_TCLAN_FETCH_CTL_SCHED_BOB_CTL(3),
 GLLAN_TCLAN_FETCH_CTL_SCHED_BOB_CTL(4),
 GLLAN_TCLAN_FETCH_CTL_SCHED_BOB_CTL(5),
 GLLAN_TCLAN_FETCH_CTL_SCHED_BOB_CTL(6),
 GLLAN_TCLAN_FETCH_CTL_SCHED_BOB_CTL(7),
 GLLAN_TCLAN_FETCH_CTL_SCHED_BOB_CTL(8),
#define GLLAN_TCLAN_CACHE_CTL_BOB_CTL(_i)       (0x000FC1C8 + ((_i) * 4))
 GLLAN_TCLAN_CACHE_CTL_BOB_CTL(1),
 GLLAN_TCLAN_CACHE_CTL_BOB_CTL(2),
 GLLAN_TCLAN_CACHE_CTL_BOB_CTL(3),
 GLLAN_TCLAN_CACHE_CTL_BOB_CTL(4),
 GLLAN_TCLAN_CACHE_CTL_BOB_CTL(5),
 GLLAN_TCLAN_CACHE_CTL_BOB_CTL(6),
 GLLAN_TCLAN_CACHE_CTL_BOB_CTL(7),
 GLLAN_TCLAN_CACHE_CTL_BOB_CTL(8),
#define GLLAN_TCLAN_FETCH_CTL_PROC_BOB_CTL(_i)  (0x000FC188 + ((_i) * 4))
 GLLAN_TCLAN_FETCH_CTL_PROC_BOB_CTL(1),
 GLLAN_TCLAN_FETCH_CTL_PROC_BOB_CTL(2),
 GLLAN_TCLAN_FETCH_CTL_PROC_BOB_CTL(3),
 GLLAN_TCLAN_FETCH_CTL_PROC_BOB_CTL(4),
 GLLAN_TCLAN_FETCH_CTL_PROC_BOB_CTL(5),
 GLLAN_TCLAN_FETCH_CTL_PROC_BOB_CTL(6),
 GLLAN_TCLAN_FETCH_CTL_PROC_BOB_CTL(7),
 GLLAN_TCLAN_FETCH_CTL_PROC_BOB_CTL(8),
#define GLLAN_TCLAN_FETCH_CTL_PCIE_RD_BOB_CTL(_i) (0x000FC288 + ((_i) * 4))
 GLLAN_TCLAN_FETCH_CTL_PCIE_RD_BOB_CTL(1),
 GLLAN_TCLAN_FETCH_CTL_PCIE_RD_BOB_CTL(2),
 GLLAN_TCLAN_FETCH_CTL_PCIE_RD_BOB_CTL(3),
 GLLAN_TCLAN_FETCH_CTL_PCIE_RD_BOB_CTL(4),
 GLLAN_TCLAN_FETCH_CTL_PCIE_RD_BOB_CTL(5),
 GLLAN_TCLAN_FETCH_CTL_PCIE_RD_BOB_CTL(6),
 GLLAN_TCLAN_FETCH_CTL_PCIE_RD_BOB_CTL(7),
 GLLAN_TCLAN_FETCH_CTL_PCIE_RD_BOB_CTL(8),
#define PRTDCB_TCUPM_REG_CM(_i)   (0x000BC360 + ((_i) * 4))
 PRTDCB_TCUPM_REG_CM(0),
 PRTDCB_TCUPM_REG_CM(1),
 PRTDCB_TCUPM_REG_CM(2),
 PRTDCB_TCUPM_REG_CM(3),
#define PRTDCB_TCUPM_REG_DM(_i)   (0x000BC3A0 + ((_i) * 4))
 PRTDCB_TCUPM_REG_DM(0),
 PRTDCB_TCUPM_REG_DM(1),
 PRTDCB_TCUPM_REG_DM(2),
 PRTDCB_TCUPM_REG_DM(3),
#define PRTDCB_TLPM_REG_DM(_i)   (0x000A0000 + ((_i) * 4))
 PRTDCB_TLPM_REG_DM(0),
 PRTDCB_TLPM_REG_DM(1),
 PRTDCB_TLPM_REG_DM(2),
 PRTDCB_TLPM_REG_DM(3),
};

struct ice_priv_flag {
 char name[ETH_GSTRING_LEN];
 u32 bitno;   /* bit position in pf->flags */
};

#define ICE_PRIV_FLAG(_name, _bitno) { \
 .name = _name, \
 .bitno = _bitno, \
}

static const struct ice_priv_flag ice_gstrings_priv_flags[] = {
 ICE_PRIV_FLAG("link-down-on-close", ICE_FLAG_LINK_DOWN_ON_CLOSE_ENA),
 ICE_PRIV_FLAG("fw-lldp-agent", ICE_FLAG_FW_LLDP_AGENT),
 ICE_PRIV_FLAG("vf-true-promisc-support",
        ICE_FLAG_VF_TRUE_PROMISC_ENA),
 ICE_PRIV_FLAG("mdd-auto-reset-vf", ICE_FLAG_MDD_AUTO_RESET_VF),
 ICE_PRIV_FLAG("vf-vlan-pruning", ICE_FLAG_VF_VLAN_PRUNING),
 ICE_PRIV_FLAG("legacy-rx", ICE_FLAG_LEGACY_RX),
};

#define ICE_PRIV_FLAG_ARRAY_SIZE ARRAY_SIZE(ice_gstrings_priv_flags)

static const u32 ice_adv_lnk_speed_100[] __initconst = {
 ETHTOOL_LINK_MODE_100baseT_Full_BIT,
};

static const u32 ice_adv_lnk_speed_1000[] __initconst = {
 ETHTOOL_LINK_MODE_1000baseX_Full_BIT,
 ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
 ETHTOOL_LINK_MODE_1000baseKX_Full_BIT,
};

static const u32 ice_adv_lnk_speed_2500[] __initconst = {
 ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
 ETHTOOL_LINK_MODE_2500baseX_Full_BIT,
};

static const u32 ice_adv_lnk_speed_5000[] __initconst = {
 ETHTOOL_LINK_MODE_5000baseT_Full_BIT,
};

static const u32 ice_adv_lnk_speed_10000[] __initconst = {
 ETHTOOL_LINK_MODE_10000baseT_Full_BIT,
 ETHTOOL_LINK_MODE_10000baseKR_Full_BIT,
 ETHTOOL_LINK_MODE_10000baseSR_Full_BIT,
 ETHTOOL_LINK_MODE_10000baseLR_Full_BIT,
};

static const u32 ice_adv_lnk_speed_25000[] __initconst = {
 ETHTOOL_LINK_MODE_25000baseCR_Full_BIT,
 ETHTOOL_LINK_MODE_25000baseSR_Full_BIT,
 ETHTOOL_LINK_MODE_25000baseKR_Full_BIT,
};

static const u32 ice_adv_lnk_speed_40000[] __initconst = {
 ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT,
 ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT,
 ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT,
 ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT,
};

static const u32 ice_adv_lnk_speed_50000[] __initconst = {
 ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT,
 ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT,
 ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT,
};

static const u32 ice_adv_lnk_speed_100000[] __initconst = {
 ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT,
 ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT,
 ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT,
 ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT,
 ETHTOOL_LINK_MODE_100000baseCR2_Full_BIT,
 ETHTOOL_LINK_MODE_100000baseSR2_Full_BIT,
 ETHTOOL_LINK_MODE_100000baseKR2_Full_BIT,
};

static const u32 ice_adv_lnk_speed_200000[] __initconst = {
 ETHTOOL_LINK_MODE_200000baseKR4_Full_BIT,
 ETHTOOL_LINK_MODE_200000baseSR4_Full_BIT,
 ETHTOOL_LINK_MODE_200000baseLR4_ER4_FR4_Full_BIT,
 ETHTOOL_LINK_MODE_200000baseDR4_Full_BIT,
 ETHTOOL_LINK_MODE_200000baseCR4_Full_BIT,
};

static struct ethtool_forced_speed_map ice_adv_lnk_speed_maps[] __ro_after_init = {
 ETHTOOL_FORCED_SPEED_MAP(ice_adv_lnk_speed, 100),
 ETHTOOL_FORCED_SPEED_MAP(ice_adv_lnk_speed, 1000),
 ETHTOOL_FORCED_SPEED_MAP(ice_adv_lnk_speed, 2500),
 ETHTOOL_FORCED_SPEED_MAP(ice_adv_lnk_speed, 5000),
 ETHTOOL_FORCED_SPEED_MAP(ice_adv_lnk_speed, 10000),
 ETHTOOL_FORCED_SPEED_MAP(ice_adv_lnk_speed, 25000),
 ETHTOOL_FORCED_SPEED_MAP(ice_adv_lnk_speed, 40000),
 ETHTOOL_FORCED_SPEED_MAP(ice_adv_lnk_speed, 50000),
 ETHTOOL_FORCED_SPEED_MAP(ice_adv_lnk_speed, 100000),
 ETHTOOL_FORCED_SPEED_MAP(ice_adv_lnk_speed, 200000),
};

void __init ice_adv_lnk_speed_maps_init(void)
{
 ethtool_forced_speed_maps_init(ice_adv_lnk_speed_maps,
           ARRAY_SIZE(ice_adv_lnk_speed_maps));
}

static void
__ice_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo,
    struct ice_vsi *vsi)
{
 struct ice_pf *pf = vsi->back;
 struct ice_hw *hw = &pf->hw;
 struct ice_orom_info *orom;
 struct ice_nvm_info *nvm;

 nvm = &hw->flash.nvm;
 orom = &hw->flash.orom;

 strscpy(drvinfo->driver, KBUILD_MODNAME, sizeof(drvinfo->driver));

 /* Display NVM version (from which the firmware version can be
 * determined) which contains more pertinent information.
 */

 snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version),
   "%x.%02x 0x%x %d.%d.%d", nvm->major, nvm->minor,
   nvm->eetrack, orom->major, orom->build, orom->patch);

 strscpy(drvinfo->bus_info, pci_name(pf->pdev),
  sizeof(drvinfo->bus_info));
}

static void
ice_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo)
{
 struct ice_netdev_priv *np = netdev_priv(netdev);

 __ice_get_drvinfo(netdev, drvinfo, np->vsi);
 drvinfo->n_priv_flags = ICE_PRIV_FLAG_ARRAY_SIZE;
}

static int ice_get_regs_len(struct net_device __always_unused *netdev)
{
 return (sizeof(ice_regs_dump_list) +
  sizeof(struct ice_regdump_to_ethtool));
}

/**
 * ice_ethtool_get_maxspeed - Get the max speed for given lport
 * @hw: pointer to the HW struct
 * @lport: logical port for which max speed is requested
 * @max_speed: return max speed for input lport
 *
 * Return: 0 on success, negative on failure.
 */

static int ice_ethtool_get_maxspeed(struct ice_hw *hw, u8 lport, u8 *max_speed)
{
 struct ice_aqc_get_port_options_elem options[ICE_AQC_PORT_OPT_MAX] = {};
 bool active_valid = false, pending_valid = true;
 u8 option_count = ICE_AQC_PORT_OPT_MAX;
 u8 active_idx = 0, pending_idx = 0;
 int status;

 status = ice_aq_get_port_options(hw, options, &option_count, lport,
      true, &active_idx, &active_valid,
      &pending_idx, &pending_valid);
 if (status)
  return -EIO;
 if (!active_valid)
  return -EINVAL;

 *max_speed = options[active_idx].max_lane_speed & ICE_AQC_PORT_OPT_MAX_LANE_M;
 return 0;
}

/**
 * ice_is_serdes_muxed - returns whether serdes is muxed in hardware
 * @hw: pointer to the HW struct
 *
 * Return: true when serdes is muxed, false when serdes is not muxed.
 */

static bool ice_is_serdes_muxed(struct ice_hw *hw)
{
 u32 reg_value = rd32(hw, GLGEN_SWITCH_MODE_CONFIG);

 return FIELD_GET(GLGEN_SWITCH_MODE_CONFIG_25X4_QUAD_M, reg_value);
}

static int ice_map_port_topology_for_sfp(struct ice_port_topology *port_topology,
      u8 lport, bool is_muxed)
{
 switch (lport) {
 case 0:
  port_topology->pcs_quad_select = 0;
  port_topology->pcs_port = 0;
  port_topology->primary_serdes_lane = 0;
  break;
 case 1:
  port_topology->pcs_quad_select = 1;
  port_topology->pcs_port = 0;
  if (is_muxed)
   port_topology->primary_serdes_lane = 2;
  else
   port_topology->primary_serdes_lane = 4;
  break;
 case 2:
  port_topology->pcs_quad_select = 0;
  port_topology->pcs_port = 1;
  port_topology->primary_serdes_lane = 1;
  break;
 case 3:
  port_topology->pcs_quad_select = 1;
  port_topology->pcs_port = 1;
  if (is_muxed)
   port_topology->primary_serdes_lane = 3;
  else
   port_topology->primary_serdes_lane = 5;
  break;
 case 4:
  port_topology->pcs_quad_select = 0;
  port_topology->pcs_port = 2;
  port_topology->primary_serdes_lane = 2;
  break;
 case 5:
  port_topology->pcs_quad_select = 1;
  port_topology->pcs_port = 2;
  port_topology->primary_serdes_lane = 6;
  break;
 case 6:
  port_topology->pcs_quad_select = 0;
  port_topology->pcs_port = 3;
  port_topology->primary_serdes_lane = 3;
  break;
 case 7:
  port_topology->pcs_quad_select = 1;
  port_topology->pcs_port = 3;
  port_topology->primary_serdes_lane = 7;
  break;
 default:
  return -EINVAL;
 }

 return 0;
}

static int ice_map_port_topology_for_qsfp(struct ice_port_topology *port_topology,
       u8 lport, bool is_muxed)
{
 switch (lport) {
 case 0:
  port_topology->pcs_quad_select = 0;
  port_topology->pcs_port = 0;
  port_topology->primary_serdes_lane = 0;
  break;
 case 1:
  port_topology->pcs_quad_select = 1;
  port_topology->pcs_port = 0;
  if (is_muxed)
   port_topology->primary_serdes_lane = 2;
  else
   port_topology->primary_serdes_lane = 4;
  break;
 case 2:
  port_topology->pcs_quad_select = 0;
  port_topology->pcs_port = 1;
  port_topology->primary_serdes_lane = 1;
  break;
 case 3:
  port_topology->pcs_quad_select = 1;
  port_topology->pcs_port = 1;
  if (is_muxed)
   port_topology->primary_serdes_lane = 3;
  else
   port_topology->primary_serdes_lane = 5;
  break;
 case 4:
  port_topology->pcs_quad_select = 0;
  port_topology->pcs_port = 2;
  port_topology->primary_serdes_lane = 2;
  break;
 case 5:
  port_topology->pcs_quad_select = 1;
  port_topology->pcs_port = 2;
  port_topology->primary_serdes_lane = 6;
  break;
 case 6:
  port_topology->pcs_quad_select = 0;
  port_topology->pcs_port = 3;
  port_topology->primary_serdes_lane = 3;
  break;
 case 7:
  port_topology->pcs_quad_select = 1;
  port_topology->pcs_port = 3;
  port_topology->primary_serdes_lane = 7;
  break;
 default:
  return -EINVAL;
 }

 return 0;
}

/**
 * ice_get_port_topology - returns physical topology like pcsquad, pcsport,
 *                         serdes number
 * @hw: pointer to the HW struct
 * @lport: logical port for which physical info requested
 * @port_topology: buffer to hold port topology
 *
 * Return: 0 on success, negative on failure.
 */

static int ice_get_port_topology(struct ice_hw *hw, u8 lport,
     struct ice_port_topology *port_topology)
{
 struct ice_aqc_get_link_topo cmd = {};
 u16 node_handle = 0;
 u8 cage_type = 0;
 bool is_muxed;
 int err;
 u8 ctx;

 ctx = ICE_AQC_LINK_TOPO_NODE_TYPE_CAGE << ICE_AQC_LINK_TOPO_NODE_TYPE_S;
 ctx |= ICE_AQC_LINK_TOPO_NODE_CTX_PORT << ICE_AQC_LINK_TOPO_NODE_CTX_S;
 cmd.addr.topo_params.node_type_ctx = ctx;

 err = ice_aq_get_netlist_node(hw, &cmd, &cage_type, &node_handle);
 if (err)
  return -EINVAL;

 is_muxed = ice_is_serdes_muxed(hw);

 if (cage_type == 0x11 || /* SFP+ */
     cage_type == 0x12) { /* SFP28 */
  port_topology->serdes_lane_count = 1;
  err = ice_map_port_topology_for_sfp(port_topology, lport, is_muxed);
  if (err)
   return err;
 } else if (cage_type == 0x13 || /* QSFP */
     cage_type == 0x14) { /* QSFP28 */
  u8 max_speed = 0;

  err = ice_ethtool_get_maxspeed(hw, lport, &max_speed);
  if (err)
   return err;

  if (max_speed == ICE_AQC_PORT_OPT_MAX_LANE_100G)
   port_topology->serdes_lane_count = 4;
  else if (max_speed == ICE_AQC_PORT_OPT_MAX_LANE_50G ||
    max_speed == ICE_AQC_PORT_OPT_MAX_LANE_40G)
   port_topology->serdes_lane_count = 2;
  else
   port_topology->serdes_lane_count = 1;

  err = ice_map_port_topology_for_qsfp(port_topology, lport, is_muxed);
  if (err)
   return err;
 } else {
  return -EINVAL;
 }

 return 0;
}

/**
 * ice_get_tx_rx_equa - read serdes tx rx equaliser param
 * @hw: pointer to the HW struct
 * @serdes_num: represents the serdes number
 * @ptr: structure to read all serdes parameter for given serdes
 *
 * Return: all serdes equalization parameter supported per serdes number
 */

static int ice_get_tx_rx_equa(struct ice_hw *hw, u8 serdes_num,
         struct ice_serdes_equalization_to_ethtool *ptr)
{
 static const int tx = ICE_AQC_OP_CODE_TX_EQU;
 static const int rx = ICE_AQC_OP_CODE_RX_EQU;
 struct {
  int data_in;
  int opcode;
  int *out;
 } aq_params[] = {
  { ICE_AQC_TX_EQU_PRE1, tx, &ptr->tx_equ_pre1 },
  { ICE_AQC_TX_EQU_PRE3, tx, &ptr->tx_equ_pre3 },
  { ICE_AQC_TX_EQU_ATTEN, tx, &ptr->tx_equ_atten },
  { ICE_AQC_TX_EQU_POST1, tx, &ptr->tx_equ_post1 },
  { ICE_AQC_TX_EQU_PRE2, tx, &ptr->tx_equ_pre2 },
  { ICE_AQC_RX_EQU_PRE2, rx, &ptr->rx_equ_pre2 },
  { ICE_AQC_RX_EQU_PRE1, rx, &ptr->rx_equ_pre1 },
  { ICE_AQC_RX_EQU_POST1, rx, &ptr->rx_equ_post1 },
  { ICE_AQC_RX_EQU_BFLF, rx, &ptr->rx_equ_bflf },
  { ICE_AQC_RX_EQU_BFHF, rx, &ptr->rx_equ_bfhf },
  { ICE_AQC_RX_EQU_CTLE_GAINHF, rx, &ptr->rx_equ_ctle_gainhf },
  { ICE_AQC_RX_EQU_CTLE_GAINLF, rx, &ptr->rx_equ_ctle_gainlf },
  { ICE_AQC_RX_EQU_CTLE_GAINDC, rx, &ptr->rx_equ_ctle_gaindc },
  { ICE_AQC_RX_EQU_CTLE_BW, rx, &ptr->rx_equ_ctle_bw },
  { ICE_AQC_RX_EQU_DFE_GAIN, rx, &ptr->rx_equ_dfe_gain },
  { ICE_AQC_RX_EQU_DFE_GAIN2, rx, &ptr->rx_equ_dfe_gain_2 },
  { ICE_AQC_RX_EQU_DFE_2, rx, &ptr->rx_equ_dfe_2 },
  { ICE_AQC_RX_EQU_DFE_3, rx, &ptr->rx_equ_dfe_3 },
  { ICE_AQC_RX_EQU_DFE_4, rx, &ptr->rx_equ_dfe_4 },
  { ICE_AQC_RX_EQU_DFE_5, rx, &ptr->rx_equ_dfe_5 },
  { ICE_AQC_RX_EQU_DFE_6, rx, &ptr->rx_equ_dfe_6 },
  { ICE_AQC_RX_EQU_DFE_7, rx, &ptr->rx_equ_dfe_7 },
  { ICE_AQC_RX_EQU_DFE_8, rx, &ptr->rx_equ_dfe_8 },
  { ICE_AQC_RX_EQU_DFE_9, rx, &ptr->rx_equ_dfe_9 },
  { ICE_AQC_RX_EQU_DFE_10, rx, &ptr->rx_equ_dfe_10 },
  { ICE_AQC_RX_EQU_DFE_11, rx, &ptr->rx_equ_dfe_11 },
  { ICE_AQC_RX_EQU_DFE_12, rx, &ptr->rx_equ_dfe_12 },
 };
 int err;

 for (int i = 0; i < ARRAY_SIZE(aq_params); i++) {
  err = ice_aq_get_phy_equalization(hw, aq_params[i].data_in,
        aq_params[i].opcode,
        serdes_num, aq_params[i].out);
  if (err)
   break;
 }

 return err;
}

/**
 * ice_get_extended_regs - returns FEC correctable, uncorrectable stats per
 *                         pcsquad, pcsport
 * @netdev: pointer to net device structure
 * @p: output buffer to fill requested register dump
 *
 * Return: 0 on success, negative on failure.
 */

static int ice_get_extended_regs(struct net_device *netdev, void *p)
{
 struct ice_netdev_priv *np = netdev_priv(netdev);
 struct ice_regdump_to_ethtool *ice_prv_regs_buf;
 struct ice_port_topology port_topology = {};
 struct ice_port_info *pi;
 struct ice_pf *pf;
 struct ice_hw *hw;
 unsigned int i;
 int err;

 pf = np->vsi->back;
 hw = &pf->hw;
 pi = np->vsi->port_info;

 /* Serdes parameters are not supported if not the PF VSI */
 if (np->vsi->type != ICE_VSI_PF || !pi)
  return -EINVAL;

 err = ice_get_port_topology(hw, pi->lport, &port_topology);
 if (err)
  return -EINVAL;
 if (port_topology.serdes_lane_count > 4)
  return -EINVAL;

 ice_prv_regs_buf = p;

 /* Get serdes equalization parameter for available serdes */
 for (i = 0; i < port_topology.serdes_lane_count; i++) {
  u8 serdes_num = 0;

  serdes_num = port_topology.primary_serdes_lane + i;
  err = ice_get_tx_rx_equa(hw, serdes_num,
      &ice_prv_regs_buf->equalization[i]);
  if (err)
   return -EINVAL;
 }

 return 0;
}

static void
ice_get_regs(struct net_device *netdev, struct ethtool_regs *regs, void *p)
{
 struct ice_netdev_priv *np = netdev_priv(netdev);
 struct ice_pf *pf = np->vsi->back;
 struct ice_hw *hw = &pf->hw;
 u32 *regs_buf = (u32 *)p;
 unsigned int i;

 regs->version = 2;

 for (i = 0; i < ARRAY_SIZE(ice_regs_dump_list); ++i)
  regs_buf[i] = rd32(hw, ice_regs_dump_list[i]);

 ice_get_extended_regs(netdev, (void *)®s_buf[i]);
}

static u32 ice_get_msglevel(struct net_device *netdev)
{
 struct ice_netdev_priv *np = netdev_priv(netdev);
 struct ice_pf *pf = np->vsi->back;

#ifndef CONFIG_DYNAMIC_DEBUG
 if (pf->hw.debug_mask)
  netdev_info(netdev, "hw debug_mask: 0x%llX\n",
       pf->hw.debug_mask);
#endif /* !CONFIG_DYNAMIC_DEBUG */

 return pf->msg_enable;
}

static void ice_set_msglevel(struct net_device *netdev, u32 data)
{
 struct ice_netdev_priv *np = netdev_priv(netdev);
 struct ice_pf *pf = np->vsi->back;

#ifndef CONFIG_DYNAMIC_DEBUG
 if (ICE_DBG_USER & data)
  pf->hw.debug_mask = data;
 else
  pf->msg_enable = data;
#else
 pf->msg_enable = data;
#endif /* !CONFIG_DYNAMIC_DEBUG */
}

static void ice_get_link_ext_stats(struct net_device *netdev,
       struct ethtool_link_ext_stats *stats)
{
 struct ice_netdev_priv *np = netdev_priv(netdev);
 struct ice_pf *pf = np->vsi->back;

 stats->link_down_events = pf->link_down_events;
}

static int ice_get_eeprom_len(struct net_device *netdev)
{
 struct ice_netdev_priv *np = netdev_priv(netdev);
 struct ice_pf *pf = np->vsi->back;

 return (int)pf->hw.flash.flash_size;
}

static int
ice_get_eeprom(struct net_device *netdev, struct ethtool_eeprom *eeprom,
        u8 *bytes)
{
 struct ice_netdev_priv *np = netdev_priv(netdev);
 struct ice_vsi *vsi = np->vsi;
 struct ice_pf *pf = vsi->back;
 struct ice_hw *hw = &pf->hw;
 struct device *dev;
 int ret;
 u8 *buf;

 dev = ice_pf_to_dev(pf);

 eeprom->magic = hw->vendor_id | (hw->device_id << 16);
 netdev_dbg(netdev, "GEEPROM cmd 0x%08x, offset 0x%08x, len 0x%08x\n",
     eeprom->cmd, eeprom->offset, eeprom->len);

 buf = kzalloc(eeprom->len, GFP_KERNEL);
 if (!buf)
  return -ENOMEM;

 ret = ice_acquire_nvm(hw, ICE_RES_READ);
 if (ret) {
  dev_err(dev, "ice_acquire_nvm failed, err %d aq_err %s\n",
   ret, libie_aq_str(hw->adminq.sq_last_status));
  goto out;
 }

 ret = ice_read_flat_nvm(hw, eeprom->offset, &eeprom->len, buf,
    false);
 if (ret) {
  dev_err(dev, "ice_read_flat_nvm failed, err %d aq_err %s\n",
   ret, libie_aq_str(hw->adminq.sq_last_status));
  goto release;
 }

 memcpy(bytes, buf, eeprom->len);
release:
 ice_release_nvm(hw);
out:
 kfree(buf);
 return ret;
}

/**
 * ice_active_vfs - check if there are any active VFs
 * @pf: board private structure
 *
 * Returns true if an active VF is found, otherwise returns false
 */

static bool ice_active_vfs(struct ice_pf *pf)
{
 bool active = false;
 struct ice_vf *vf;
 unsigned int bkt;

 rcu_read_lock();
 ice_for_each_vf_rcu(pf, bkt, vf) {
  if (test_bit(ICE_VF_STATE_ACTIVE, vf->vf_states)) {
   active = true;
   break;
  }
 }
 rcu_read_unlock();

 return active;
}

/**
 * ice_link_test - perform a link test on a given net_device
 * @netdev: network interface device structure
 *
 * This function performs one of the self-tests required by ethtool.
 * Returns 0 on success, non-zero on failure.
 */

static u64 ice_link_test(struct net_device *netdev)
{
 struct ice_netdev_priv *np = netdev_priv(netdev);
 bool link_up = false;
 int status;

 netdev_info(netdev, "link test\n");
 status = ice_get_link_status(np->vsi->port_info, &link_up);
 if (status) {
  netdev_err(netdev, "link query error, status = %d\n",
      status);
  return 1;
 }

 if (!link_up)
  return 2;

 return 0;
}

/**
 * ice_eeprom_test - perform an EEPROM test on a given net_device
 * @netdev: network interface device structure
 *
 * This function performs one of the self-tests required by ethtool.
 * Returns 0 on success, non-zero on failure.
 */

static u64 ice_eeprom_test(struct net_device *netdev)
{
 struct ice_netdev_priv *np = netdev_priv(netdev);
 struct ice_pf *pf = np->vsi->back;

 netdev_info(netdev, "EEPROM test\n");
 return !!(ice_nvm_validate_checksum(&pf->hw));
}

/**
 * ice_reg_pattern_test
 * @hw: pointer to the HW struct
 * @reg: reg to be tested
 * @mask: bits to be touched
 */

static int ice_reg_pattern_test(struct ice_hw *hw, u32 reg, u32 mask)
{
 struct ice_pf *pf = (struct ice_pf *)hw->back;
 struct device *dev = ice_pf_to_dev(pf);
 static const u32 patterns[] = {
  0x5A5A5A5A, 0xA5A5A5A5,
  0x00000000, 0xFFFFFFFF
 };
 u32 val, orig_val;
 unsigned int i;

 orig_val = rd32(hw, reg);
 for (i = 0; i < ARRAY_SIZE(patterns); ++i) {
  u32 pattern = patterns[i] & mask;

  wr32(hw, reg, pattern);
  val = rd32(hw, reg);
  if (val == pattern)
   continue;
  dev_err(dev, "%s: reg pattern test failed - reg 0x%08x pat 0x%08x val 0x%08x\n"
   , __func__, reg, pattern, val);
  return 1;
 }

 wr32(hw, reg, orig_val);
 val = rd32(hw, reg);
 if (val != orig_val) {
  dev_err(dev, "%s: reg restore test failed - reg 0x%08x orig 0x%08x val 0x%08x\n"
   , __func__, reg, orig_val, val);
  return 1;
 }

 return 0;
}

/**
 * ice_reg_test - perform a register test on a given net_device
 * @netdev: network interface device structure
 *
 * This function performs one of the self-tests required by ethtool.
 * Returns 0 on success, non-zero on failure.
 */

static u64 ice_reg_test(struct net_device *netdev)
{
 struct ice_netdev_priv *np = netdev_priv(netdev);
 struct ice_hw *hw = np->vsi->port_info->hw;
 u32 int_elements = hw->func_caps.common_cap.num_msix_vectors ?
  hw->func_caps.common_cap.num_msix_vectors - 1 : 1;
 struct ice_diag_reg_test_info {
  u32 address;
  u32 mask;
  u32 elem_num;
  u32 elem_size;
 } ice_reg_list[] = {
  {GLINT_ITR(0, 0), 0x00000fff, int_elements,
   GLINT_ITR(0, 1) - GLINT_ITR(0, 0)},
  {GLINT_ITR(1, 0), 0x00000fff, int_elements,
   GLINT_ITR(1, 1) - GLINT_ITR(1, 0)},
  {GLINT_ITR(0, 0), 0x00000fff, int_elements,
   GLINT_ITR(2, 1) - GLINT_ITR(2, 0)},
  {GLINT_CTL, 0xffff0001, 1, 0}
 };
 unsigned int i;

 netdev_dbg(netdev, "Register test\n");
 for (i = 0; i < ARRAY_SIZE(ice_reg_list); ++i) {
  u32 j;

  for (j = 0; j < ice_reg_list[i].elem_num; ++j) {
   u32 mask = ice_reg_list[i].mask;
   u32 reg = ice_reg_list[i].address +
    (j * ice_reg_list[i].elem_size);

   /* bail on failure (non-zero return) */
   if (ice_reg_pattern_test(hw, reg, mask))
    return 1;
  }
 }

 return 0;
}

/**
 * ice_lbtest_prepare_rings - configure Tx/Rx test rings
 * @vsi: pointer to the VSI structure
 *
 * Function configures rings of a VSI for loopback test without
 * enabling interrupts or informing the kernel about new queues.
 *
 * Returns 0 on success, negative on failure.
 */

static int ice_lbtest_prepare_rings(struct ice_vsi *vsi)
{
 int status;

 status = ice_vsi_setup_tx_rings(vsi);
 if (status)
  goto err_setup_tx_ring;

 status = ice_vsi_setup_rx_rings(vsi);
 if (status)
  goto err_setup_rx_ring;

 status = ice_vsi_cfg_lan(vsi);
 if (status)
  goto err_setup_rx_ring;

 status = ice_vsi_start_all_rx_rings(vsi);
 if (status)
  goto err_start_rx_ring;

 return 0;

err_start_rx_ring:
 ice_vsi_free_rx_rings(vsi);
err_setup_rx_ring:
 ice_vsi_stop_lan_tx_rings(vsi, ICE_NO_RESET, 0);
err_setup_tx_ring:
 ice_vsi_free_tx_rings(vsi);

 return status;
}

/**
 * ice_lbtest_disable_rings - disable Tx/Rx test rings after loopback test
 * @vsi: pointer to the VSI structure
 *
 * Function stops and frees VSI rings after a loopback test.
 * Returns 0 on success, negative on failure.
 */

static int ice_lbtest_disable_rings(struct ice_vsi *vsi)
{
 int status;

 status = ice_vsi_stop_lan_tx_rings(vsi, ICE_NO_RESET, 0);
 if (status)
  netdev_err(vsi->netdev, "Failed to stop Tx rings, VSI %d error %d\n",
      vsi->vsi_num, status);

 status = ice_vsi_stop_all_rx_rings(vsi);
 if (status)
  netdev_err(vsi->netdev, "Failed to stop Rx rings, VSI %d error %d\n",
      vsi->vsi_num, status);

 ice_vsi_free_tx_rings(vsi);
 ice_vsi_free_rx_rings(vsi);

 return status;
}

/**
 * ice_lbtest_create_frame - create test packet
 * @pf: pointer to the PF structure
 * @ret_data: allocated frame buffer
 * @size: size of the packet data
 *
 * Function allocates a frame with a test pattern on specific offsets.
 * Returns 0 on success, non-zero on failure.
 */

static int ice_lbtest_create_frame(struct ice_pf *pf, u8 **ret_data, u16 size)
{
 u8 *data;

 if (!pf)
  return -EINVAL;

 data = kzalloc(size, GFP_KERNEL);
 if (!data)
  return -ENOMEM;

 /* Since the ethernet test frame should always be at least
 * 64 bytes long, fill some octets in the payload with test data.
 */

 memset(data, 0xFF, size);
 data[32] = 0xDE;
 data[42] = 0xAD;
 data[44] = 0xBE;
 data[46] = 0xEF;

 *ret_data = data;

 return 0;
}

/**
 * ice_lbtest_check_frame - verify received loopback frame
 * @frame: pointer to the raw packet data
 *
 * Function verifies received test frame with a pattern.
 * Returns true if frame matches the pattern, false otherwise.
 */

static bool ice_lbtest_check_frame(u8 *frame)
{
 /* Validate bytes of a frame under offsets chosen earlier */
 if (frame[32] == 0xDE &&
     frame[42] == 0xAD &&
     frame[44] == 0xBE &&
     frame[46] == 0xEF &&
     frame[48] == 0xFF)
  return true;

 return false;
}

/**
 * ice_diag_send - send test frames to the test ring
 * @tx_ring: pointer to the transmit ring
 * @data: pointer to the raw packet data
 * @size: size of the packet to send
 *
 * Function sends loopback packets on a test Tx ring.
 */

static int ice_diag_send(struct ice_tx_ring *tx_ring, u8 *data, u16 size)
{
 struct ice_tx_desc *tx_desc;
 struct ice_tx_buf *tx_buf;
 dma_addr_t dma;
 u64 td_cmd;

 tx_desc = ICE_TX_DESC(tx_ring, tx_ring->next_to_use);
 tx_buf = &tx_ring->tx_buf[tx_ring->next_to_use];

 dma = dma_map_single(tx_ring->dev, data, size, DMA_TO_DEVICE);
 if (dma_mapping_error(tx_ring->dev, dma))
  return -EINVAL;

 tx_desc->buf_addr = cpu_to_le64(dma);

 /* These flags are required for a descriptor to be pushed out */
 td_cmd = (u64)(ICE_TX_DESC_CMD_EOP | ICE_TX_DESC_CMD_RS);
 tx_desc->cmd_type_offset_bsz =
  cpu_to_le64(ICE_TX_DESC_DTYPE_DATA |
       (td_cmd << ICE_TXD_QW1_CMD_S) |
       ((u64)0 << ICE_TXD_QW1_OFFSET_S) |
       ((u64)size << ICE_TXD_QW1_TX_BUF_SZ_S) |
       ((u64)0 << ICE_TXD_QW1_L2TAG1_S));

 tx_buf->next_to_watch = tx_desc;

 /* Force memory write to complete before letting h/w know
 * there are new descriptors to fetch.
 */

 wmb();

 tx_ring->next_to_use++;
 if (tx_ring->next_to_use >= tx_ring->count)
  tx_ring->next_to_use = 0;

 writel_relaxed(tx_ring->next_to_use, tx_ring->tail);

 /* Wait until the packets get transmitted to the receive queue. */
 usleep_range(1000, 2000);
 dma_unmap_single(tx_ring->dev, dma, size, DMA_TO_DEVICE);

 return 0;
}

#define ICE_LB_FRAME_SIZE 64
/**
 * ice_lbtest_receive_frames - receive and verify test frames
 * @rx_ring: pointer to the receive ring
 *
 * Function receives loopback packets and verify their correctness.
 * Returns number of received valid frames.
 */

static int ice_lbtest_receive_frames(struct ice_rx_ring *rx_ring)
{
 struct ice_rx_buf *rx_buf;
 int valid_frames, i;
 u8 *received_buf;

 valid_frames = 0;

 for (i = 0; i < rx_ring->count; i++) {
  union ice_32b_rx_flex_desc *rx_desc;

  rx_desc = ICE_RX_DESC(rx_ring, i);

  if (!(rx_desc->wb.status_error0 &
      (cpu_to_le16(BIT(ICE_RX_FLEX_DESC_STATUS0_DD_S)) |
       cpu_to_le16(BIT(ICE_RX_FLEX_DESC_STATUS0_EOF_S)))))
   continue;

  rx_buf = &rx_ring->rx_buf[i];
  received_buf = page_address(rx_buf->page) + rx_buf->page_offset;

  if (ice_lbtest_check_frame(received_buf))
   valid_frames++;
 }

 return valid_frames;
}

/**
 * ice_loopback_test - perform a loopback test on a given net_device
 * @netdev: network interface device structure
 *
 * This function performs one of the self-tests required by ethtool.
 * Returns 0 on success, non-zero on failure.
 */

static u64 ice_loopback_test(struct net_device *netdev)
{
 struct ice_netdev_priv *np = netdev_priv(netdev);
 struct ice_vsi *orig_vsi = np->vsi, *test_vsi;
 struct ice_pf *pf = orig_vsi->back;
 u8 *tx_frame __free(kfree) = NULL;
 u8 broadcast[ETH_ALEN], ret = 0;
 int num_frames, valid_frames;
 struct ice_tx_ring *tx_ring;
 struct ice_rx_ring *rx_ring;
 int i;

 netdev_info(netdev, "loopback test\n");

 test_vsi = ice_lb_vsi_setup(pf, pf->hw.port_info);
 if (!test_vsi) {
  netdev_err(netdev, "Failed to create a VSI for the loopback test\n");
  return 1;
 }

 test_vsi->netdev = netdev;
 tx_ring = test_vsi->tx_rings[0];
 rx_ring = test_vsi->rx_rings[0];

 if (ice_lbtest_prepare_rings(test_vsi)) {
  ret = 2;
  goto lbtest_vsi_close;
 }

 if (ice_alloc_rx_bufs(rx_ring, rx_ring->count)) {
  ret = 3;
  goto lbtest_rings_dis;
 }

 /* Enable MAC loopback in firmware */
 if (ice_aq_set_mac_loopback(&pf->hw, true, NULL)) {
  ret = 4;
  goto lbtest_mac_dis;
 }

 /* Test VSI needs to receive broadcast packets */
 eth_broadcast_addr(broadcast);
 if (ice_fltr_add_mac(test_vsi, broadcast, ICE_FWD_TO_VSI)) {
  ret = 5;
  goto lbtest_mac_dis;
 }

 if (ice_lbtest_create_frame(pf, &tx_frame, ICE_LB_FRAME_SIZE)) {
  ret = 7;
  goto remove_mac_filters;
 }

 num_frames = min_t(int, tx_ring->count, 32);
 for (i = 0; i < num_frames; i++) {
  if (ice_diag_send(tx_ring, tx_frame, ICE_LB_FRAME_SIZE)) {
   ret = 8;
   goto remove_mac_filters;
  }
 }

 valid_frames = ice_lbtest_receive_frames(rx_ring);
 if (!valid_frames)
  ret = 9;
 else if (valid_frames != num_frames)
  ret = 10;

remove_mac_filters:
 if (ice_fltr_remove_mac(test_vsi, broadcast, ICE_FWD_TO_VSI))
  netdev_err(netdev, "Could not remove MAC filter for the test VSI\n");
lbtest_mac_dis:
 /* Disable MAC loopback after the test is completed. */
 if (ice_aq_set_mac_loopback(&pf->hw, false, NULL))
  netdev_err(netdev, "Could not disable MAC loopback\n");
lbtest_rings_dis:
 if (ice_lbtest_disable_rings(test_vsi))
  netdev_err(netdev, "Could not disable test rings\n");
lbtest_vsi_close:
 test_vsi->netdev = NULL;
 if (ice_vsi_release(test_vsi))
  netdev_err(netdev, "Failed to remove the test VSI\n");

 return ret;
}

/**
 * ice_intr_test - perform an interrupt test on a given net_device
 * @netdev: network interface device structure
 *
 * This function performs one of the self-tests required by ethtool.
 * Returns 0 on success, non-zero on failure.
 */

static u64 ice_intr_test(struct net_device *netdev)
{
 struct ice_netdev_priv *np = netdev_priv(netdev);
 struct ice_pf *pf = np->vsi->back;
 u16 swic_old = pf->sw_int_count;

 netdev_info(netdev, "interrupt test\n");

 wr32(&pf->hw, GLINT_DYN_CTL(pf->oicr_irq.index),
      GLINT_DYN_CTL_SW_ITR_INDX_M |
      GLINT_DYN_CTL_INTENA_MSK_M |
      GLINT_DYN_CTL_SWINT_TRIG_M);

 usleep_range(1000, 2000);
 return (swic_old == pf->sw_int_count);
}

/**
 * ice_self_test - handler function for performing a self-test by ethtool
 * @netdev: network interface device structure
 * @eth_test: ethtool_test structure
 * @data: required by ethtool.self_test
 *
 * This function is called after invoking 'ethtool -t devname' command where
 * devname is the name of the network device on which ethtool should operate.
 * It performs a set of self-tests to check if a device works properly.
 */

static void
ice_self_test(struct net_device *netdev, struct ethtool_test *eth_test,
       u64 *data)
{
 struct ice_netdev_priv *np = netdev_priv(netdev);
 bool if_running = netif_running(netdev);
 struct ice_pf *pf = np->vsi->back;
 struct device *dev;

 dev = ice_pf_to_dev(pf);

 if (eth_test->flags == ETH_TEST_FL_OFFLINE) {
  netdev_info(netdev, "offline testing starting\n");

  set_bit(ICE_TESTING, pf->state);

  if (ice_active_vfs(pf)) {
   dev_warn(dev, "Please take active VFs and Netqueues offline and restart the adapter before running NIC diagnostics\n");
   data[ICE_ETH_TEST_REG] = 1;
   data[ICE_ETH_TEST_EEPROM] = 1;
   data[ICE_ETH_TEST_INTR] = 1;
   data[ICE_ETH_TEST_LOOP] = 1;
   data[ICE_ETH_TEST_LINK] = 1;
   eth_test->flags |= ETH_TEST_FL_FAILED;
   clear_bit(ICE_TESTING, pf->state);
   goto skip_ol_tests;
  }
  /* If the device is online then take it offline */
  if (if_running)
   /* indicate we're in test mode */
   ice_stop(netdev);

  data[ICE_ETH_TEST_LINK] = ice_link_test(netdev);
  data[ICE_ETH_TEST_EEPROM] = ice_eeprom_test(netdev);
  data[ICE_ETH_TEST_INTR] = ice_intr_test(netdev);
  data[ICE_ETH_TEST_LOOP] = ice_loopback_test(netdev);
  data[ICE_ETH_TEST_REG] = ice_reg_test(netdev);

  if (data[ICE_ETH_TEST_LINK] ||
      data[ICE_ETH_TEST_EEPROM] ||
      data[ICE_ETH_TEST_LOOP] ||
      data[ICE_ETH_TEST_INTR] ||
      data[ICE_ETH_TEST_REG])
   eth_test->flags |= ETH_TEST_FL_FAILED;

  clear_bit(ICE_TESTING, pf->state);

  if (if_running) {
   int status = ice_open(netdev);

   if (status) {
    dev_err(dev, "Could not open device %s, err %d\n",
     pf->int_name, status);
   }
  }
 } else {
  /* Online tests */
  netdev_info(netdev, "online testing starting\n");

  data[ICE_ETH_TEST_LINK] = ice_link_test(netdev);
  if (data[ICE_ETH_TEST_LINK])
   eth_test->flags |= ETH_TEST_FL_FAILED;

  /* Offline only tests, not run in online; pass by default */
  data[ICE_ETH_TEST_REG] = 0;
  data[ICE_ETH_TEST_EEPROM] = 0;
  data[ICE_ETH_TEST_INTR] = 0;
  data[ICE_ETH_TEST_LOOP] = 0;
 }

skip_ol_tests:
 netdev_info(netdev, "testing finished\n");
}

static void
__ice_get_strings(struct net_device *netdev, u32 stringset, u8 *data,
    struct ice_vsi *vsi)
{
 unsigned int i;
 u8 *p = data;

 switch (stringset) {
 case ETH_SS_STATS:
  for (i = 0; i < ICE_VSI_STATS_LEN; i++)
   ethtool_puts(&p, ice_gstrings_vsi_stats[i].stat_string);

  if (ice_is_port_repr_netdev(netdev))
   return;

  ice_for_each_alloc_txq(vsi, i) {
   ethtool_sprintf(&p, "tx_queue_%u_packets", i);
   ethtool_sprintf(&p, "tx_queue_%u_bytes", i);
  }

  ice_for_each_alloc_rxq(vsi, i) {
   ethtool_sprintf(&p, "rx_queue_%u_packets", i);
   ethtool_sprintf(&p, "rx_queue_%u_bytes", i);
  }

  if (vsi->type != ICE_VSI_PF)
   return;

  for (i = 0; i < ICE_PF_STATS_LEN; i++)
   ethtool_puts(&p, ice_gstrings_pf_stats[i].stat_string);

  for (i = 0; i < ICE_MAX_USER_PRIORITY; i++) {
   ethtool_sprintf(&p, "tx_priority_%u_xon.nic", i);
   ethtool_sprintf(&p, "tx_priority_%u_xoff.nic", i);
  }
  for (i = 0; i < ICE_MAX_USER_PRIORITY; i++) {
   ethtool_sprintf(&p, "rx_priority_%u_xon.nic", i);
   ethtool_sprintf(&p, "rx_priority_%u_xoff.nic", i);
  }
  break;
 case ETH_SS_TEST:
  memcpy(data, ice_gstrings_test, ICE_TEST_LEN * ETH_GSTRING_LEN);
  break;
 case ETH_SS_PRIV_FLAGS:
  for (i = 0; i < ICE_PRIV_FLAG_ARRAY_SIZE; i++)
   ethtool_puts(&p, ice_gstrings_priv_flags[i].name);
  break;
 default:
  break;
 }
}

static void ice_get_strings(struct net_device *netdev, u32 stringset, u8 *data)
{
 struct ice_netdev_priv *np = netdev_priv(netdev);

 __ice_get_strings(netdev, stringset, data, np->vsi);
}

static int
ice_set_phys_id(struct net_device *netdev, enum ethtool_phys_id_state state)
{
 struct ice_netdev_priv *np = netdev_priv(netdev);
 bool led_active;

 switch (state) {
 case ETHTOOL_ID_ACTIVE:
  led_active = true;
  break;
 case ETHTOOL_ID_INACTIVE:
  led_active = false;
  break;
 default:
  return -EINVAL;
 }

 if (ice_aq_set_port_id_led(np->vsi->port_info, !led_active, NULL))
  return -EIO;

 return 0;
}

/**
 * ice_set_fec_cfg - Set link FEC options
 * @netdev: network interface device structure
 * @req_fec: FEC mode to configure
 */

static int ice_set_fec_cfg(struct net_device *netdev, enum ice_fec_mode req_fec)
{
 struct ice_netdev_priv *np = netdev_priv(netdev);
 struct ice_aqc_set_phy_cfg_data config = { 0 };
 struct ice_vsi *vsi = np->vsi;
 struct ice_port_info *pi;

 pi = vsi->port_info;
 if (!pi)
  return -EOPNOTSUPP;

 /* Changing the FEC parameters is not supported if not the PF VSI */
 if (vsi->type != ICE_VSI_PF) {
  netdev_info(netdev, "Changing FEC parameters only supported for PF VSI\n");
  return -EOPNOTSUPP;
 }

 /* Proceed only if requesting different FEC mode */
 if (pi->phy.curr_user_fec_req == req_fec)
  return 0;

 /* Copy the current user PHY configuration. The current user PHY
 * configuration is initialized during probe from PHY capabilities
 * software mode, and updated on set PHY configuration.
 */

 memcpy(&config, &pi->phy.curr_user_phy_cfg, sizeof(config));

 ice_cfg_phy_fec(pi, &config, req_fec);
 config.caps |= ICE_AQ_PHY_ENA_AUTO_LINK_UPDT;

 if (ice_aq_set_phy_cfg(pi->hw, pi, &config, NULL))
  return -EAGAIN;

 /* Save requested FEC config */
 pi->phy.curr_user_fec_req = req_fec;

 return 0;
}

/**
 * ice_set_fecparam - Set FEC link options
 * @netdev: network interface device structure
 * @fecparam: Ethtool structure to retrieve FEC parameters
 */

static int
ice_set_fecparam(struct net_device *netdev, struct ethtool_fecparam *fecparam)
{
 struct ice_netdev_priv *np = netdev_priv(netdev);
 struct ice_vsi *vsi = np->vsi;
 enum ice_fec_mode fec;

 switch (fecparam->fec) {
 case ETHTOOL_FEC_AUTO:
  fec = ICE_FEC_AUTO;
  break;
 case ETHTOOL_FEC_RS:
  fec = ICE_FEC_RS;
  break;
 case ETHTOOL_FEC_BASER:
  fec = ICE_FEC_BASER;
  break;
 case ETHTOOL_FEC_OFF:
 case ETHTOOL_FEC_NONE:
  fec = ICE_FEC_NONE;
  break;
 default:
  dev_warn(ice_pf_to_dev(vsi->back), "Unsupported FEC mode: %d\n",
    fecparam->fec);
  return -EINVAL;
 }

 return ice_set_fec_cfg(netdev, fec);
}

/**
 * ice_get_fecparam - Get link FEC options
 * @netdev: network interface device structure
 * @fecparam: Ethtool structure to retrieve FEC parameters
 */

static int
ice_get_fecparam(struct net_device *netdev, struct ethtool_fecparam *fecparam)
{
 struct ice_netdev_priv *np = netdev_priv(netdev);
 struct ice_aqc_get_phy_caps_data *caps;
 struct ice_link_status *link_info;
 struct ice_vsi *vsi = np->vsi;
 struct ice_port_info *pi;
 int err;

 pi = vsi->port_info;

 if (!pi)
  return -EOPNOTSUPP;
 link_info = &pi->phy.link_info;

 /* Set FEC mode based on negotiated link info */
 switch (link_info->fec_info) {
 case ICE_AQ_LINK_25G_KR_FEC_EN:
  fecparam->active_fec = ETHTOOL_FEC_BASER;
  break;
 case ICE_AQ_LINK_25G_RS_528_FEC_EN:
 case ICE_AQ_LINK_25G_RS_544_FEC_EN:
  fecparam->active_fec = ETHTOOL_FEC_RS;
  break;
 default:
  fecparam->active_fec = ETHTOOL_FEC_OFF;
  break;
 }

 caps = kzalloc(sizeof(*caps), GFP_KERNEL);
 if (!caps)
  return -ENOMEM;

 err = ice_aq_get_phy_caps(pi, false, ICE_AQC_REPORT_TOPO_CAP_MEDIA,
      caps, NULL);
 if (err)
  goto done;

 /* Set supported/configured FEC modes based on PHY capability */
 if (caps->caps & ICE_AQC_PHY_EN_AUTO_FEC)
  fecparam->fec |= ETHTOOL_FEC_AUTO;
 if (caps->link_fec_options & ICE_AQC_PHY_FEC_10G_KR_40G_KR4_EN ||
     caps->link_fec_options & ICE_AQC_PHY_FEC_10G_KR_40G_KR4_REQ ||
     caps->link_fec_options & ICE_AQC_PHY_FEC_25G_KR_CLAUSE74_EN ||
     caps->link_fec_options & ICE_AQC_PHY_FEC_25G_KR_REQ)
  fecparam->fec |= ETHTOOL_FEC_BASER;
 if (caps->link_fec_options & ICE_AQC_PHY_FEC_25G_RS_528_REQ ||
     caps->link_fec_options & ICE_AQC_PHY_FEC_25G_RS_544_REQ ||
     caps->link_fec_options & ICE_AQC_PHY_FEC_25G_RS_CLAUSE91_EN)
  fecparam->fec |= ETHTOOL_FEC_RS;
 if (caps->link_fec_options == 0)
  fecparam->fec |= ETHTOOL_FEC_OFF;

done:
 kfree(caps);
 return err;
}

/**
 * ice_nway_reset - restart autonegotiation
 * @netdev: network interface device structure
 */

static int ice_nway_reset(struct net_device *netdev)
{
 struct ice_netdev_priv *np = netdev_priv(netdev);
 struct ice_vsi *vsi = np->vsi;
 int err;

 /* If VSI state is up, then restart autoneg with link up */
 if (!test_bit(ICE_DOWN, vsi->back->state))
  err = ice_set_link(vsi, true);
 else
  err = ice_set_link(vsi, false);

 return err;
}

/**
 * ice_get_priv_flags - report device private flags
 * @netdev: 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 ice_gstrings_priv_flags
 * array.
 *
 * Returns a u32 bitmap of flags.
 */

static u32 ice_get_priv_flags(struct net_device *netdev)
{
 struct ice_netdev_priv *np = netdev_priv(netdev);
 struct ice_vsi *vsi = np->vsi;
 struct ice_pf *pf = vsi->back;
 u32 i, ret_flags = 0;

 for (i = 0; i < ICE_PRIV_FLAG_ARRAY_SIZE; i++) {
  const struct ice_priv_flag *priv_flag;

  priv_flag = &ice_gstrings_priv_flags[i];

  if (test_bit(priv_flag->bitno, pf->flags))
   ret_flags |= BIT(i);
 }

 return ret_flags;
}

/**
 * ice_set_priv_flags - set private flags
 * @netdev: network interface device structure
 * @flags: bit flags to be set
 */

static int ice_set_priv_flags(struct net_device *netdev, u32 flags)
{
 struct ice_netdev_priv *np = netdev_priv(netdev);
 DECLARE_BITMAP(change_flags, ICE_PF_FLAGS_NBITS);
 DECLARE_BITMAP(orig_flags, ICE_PF_FLAGS_NBITS);
 struct ice_vsi *vsi = np->vsi;
 struct ice_pf *pf = vsi->back;
 struct device *dev;
 int ret = 0;
 u32 i;

 if (flags > BIT(ICE_PRIV_FLAG_ARRAY_SIZE))
  return -EINVAL;

 dev = ice_pf_to_dev(pf);
 set_bit(ICE_FLAG_ETHTOOL_CTXT, pf->flags);

 bitmap_copy(orig_flags, pf->flags, ICE_PF_FLAGS_NBITS);
 for (i = 0; i < ICE_PRIV_FLAG_ARRAY_SIZE; i++) {
  const struct ice_priv_flag *priv_flag;

  priv_flag = &ice_gstrings_priv_flags[i];

  if (flags & BIT(i))
   set_bit(priv_flag->bitno, pf->flags);
  else
   clear_bit(priv_flag->bitno, pf->flags);
 }

 bitmap_xor(change_flags, pf->flags, orig_flags, ICE_PF_FLAGS_NBITS);

 /* Do not allow change to link-down-on-close when Total Port Shutdown
 * is enabled.
 */

 if (test_bit(ICE_FLAG_LINK_DOWN_ON_CLOSE_ENA, change_flags) &&
     test_bit(ICE_FLAG_TOTAL_PORT_SHUTDOWN_ENA, pf->flags)) {
  dev_err(dev, "Setting link-down-on-close not supported on this port\n");
  set_bit(ICE_FLAG_LINK_DOWN_ON_CLOSE_ENA, pf->flags);
  ret = -EINVAL;
  goto ethtool_exit;
 }

 if (test_bit(ICE_FLAG_FW_LLDP_AGENT, change_flags)) {
  if (!test_bit(ICE_FLAG_FW_LLDP_AGENT, pf->flags)) {
   int status;

   /* Disable FW LLDP engine */
   status = ice_cfg_lldp_mib_change(&pf->hw, false);

   /* If unregistering for LLDP events fails, this is
 * not an error state, as there shouldn't be any
 * events to respond to.
 */

   if (status)
    dev_info(dev, "Failed to unreg for LLDP events\n");

   /* The AQ call to stop the FW LLDP agent will generate
 * an error if the agent is already stopped.
 */

   status = ice_aq_stop_lldp(&pf->hw, truetrue, NULL);
   if (status)
    dev_warn(dev, "Fail to stop LLDP agent\n");
   /* Use case for having the FW LLDP agent stopped
 * will likely not need DCB, so failure to init is
 * not a concern of ethtool
 */

   status = ice_init_pf_dcb(pf, true);
   if (status)
    dev_warn(dev, "Fail to init DCB\n");

   pf->dcbx_cap &= ~DCB_CAP_DCBX_LLD_MANAGED;
   pf->dcbx_cap |= DCB_CAP_DCBX_HOST;
  } else {
   bool dcbx_agent_status;
   int status;

   if (ice_get_pfc_mode(pf) == ICE_QOS_MODE_DSCP) {
    clear_bit(ICE_FLAG_FW_LLDP_AGENT, pf->flags);
    dev_err(dev, "QoS in L3 DSCP mode, FW Agent not allowed to start\n");
    ret = -EOPNOTSUPP;
    goto ethtool_exit;
   }

   /* Remove rule to direct LLDP packets to default VSI.
 * The FW LLDP engine will now be consuming them.
 */

   ice_cfg_sw_rx_lldp(vsi->back, false);

   /* AQ command to start FW LLDP agent will return an
 * error if the agent is already started
 */

   status = ice_aq_start_lldp(&pf->hw, true, NULL);
   if (status)
    dev_warn(dev, "Fail to start LLDP Agent\n");

   /* AQ command to start FW DCBX agent will fail if
 * the agent is already started
 */

   status = ice_aq_start_stop_dcbx(&pf->hw, true,
       &dcbx_agent_status,
       NULL);
   if (status)
    dev_dbg(dev, "Failed to start FW DCBX\n");

   dev_info(dev, "FW DCBX agent is %s\n",
     dcbx_agent_status ? "ACTIVE" : "DISABLED");

   /* Failure to configure MIB change or init DCB is not
 * relevant to ethtool.  Print notification that
 * registration/init failed but do not return error
 * state to ethtool
 */

   status = ice_init_pf_dcb(pf, true);
   if (status)
    dev_dbg(dev, "Fail to init DCB\n");

   /* Register for MIB change events */
   status = ice_cfg_lldp_mib_change(&pf->hw, true);
   if (status)
    dev_dbg(dev, "Fail to enable MIB change events\n");

   pf->dcbx_cap &= ~DCB_CAP_DCBX_HOST;
   pf->dcbx_cap |= DCB_CAP_DCBX_LLD_MANAGED;

   ice_nway_reset(netdev);
  }
 }
 if (test_bit(ICE_FLAG_LEGACY_RX, change_flags)) {
  /* down and up VSI so that changes of Rx cfg are reflected. */
  ice_down_up(vsi);
 }
 /* don't allow modification of this flag when a single VF is in
 * promiscuous mode because it's not supported
 */

 if (test_bit(ICE_FLAG_VF_TRUE_PROMISC_ENA, change_flags) &&
     ice_is_any_vf_in_unicast_promisc(pf)) {
  dev_err(dev, "Changing vf-true-promisc-support flag while VF(s) are in promiscuous mode not supported\n");
  /* toggle bit back to previous state */
  change_bit(ICE_FLAG_VF_TRUE_PROMISC_ENA, pf->flags);
  ret = -EAGAIN;
 }

 if (test_bit(ICE_FLAG_VF_VLAN_PRUNING, change_flags) &&
     ice_has_vfs(pf)) {
  dev_err(dev, "vf-vlan-pruning: VLAN pruning cannot be changed while VFs are active.\n");
  /* toggle bit back to previous state */
  change_bit(ICE_FLAG_VF_VLAN_PRUNING, pf->flags);
  ret = -EOPNOTSUPP;
 }
ethtool_exit:
 clear_bit(ICE_FLAG_ETHTOOL_CTXT, pf->flags);
 return ret;
}

static int ice_get_sset_count(struct net_device *netdev, int sset)
{
 switch (sset) {
 case ETH_SS_STATS:
  /* The number (and order) of strings reported *must* remain
 * constant for a given netdevice. This function must not
 * report a different number based on run time parameters
 * (such as the number of queues in use, or the setting of
 * a private ethtool flag). This is due to the nature of the
 * ethtool stats API.
 *
 * Userspace programs such as ethtool must make 3 separate
 * ioctl requests, one for size, one for the strings, and
 * finally one for the stats. Since these cross into
 * userspace, changes to the number or size could result in
 * undefined memory access or incorrect string<->value
 * correlations for statistics.
 *
 * Even if it appears to be safe, changes to the size or
 * order of strings will suffer from race conditions and are
 * not safe.
 */

  return ICE_ALL_STATS_LEN(netdev);
 case ETH_SS_TEST:
  return ICE_TEST_LEN;
 case ETH_SS_PRIV_FLAGS:
  return ICE_PRIV_FLAG_ARRAY_SIZE;
 default:
  return -EOPNOTSUPP;
 }
}

static void
__ice_get_ethtool_stats(struct net_device *netdev,
   struct ethtool_stats __always_unused *stats, u64 *data,
   struct ice_vsi *vsi)
{
 struct ice_pf *pf = vsi->back;
 struct ice_tx_ring *tx_ring;
 struct ice_rx_ring *rx_ring;
 unsigned int j;
 int i = 0;
 char *p;

 ice_update_pf_stats(pf);
 ice_update_vsi_stats(vsi);

 for (j = 0; j < ICE_VSI_STATS_LEN; j++) {
  p = (char *)vsi + ice_gstrings_vsi_stats[j].stat_offset;
  data[i++] = (ice_gstrings_vsi_stats[j].sizeof_stat ==
        sizeof(u64)) ? *(u64 *)p : *(u32 *)p;
 }

 if (ice_is_port_repr_netdev(netdev))
  return;

 /* populate per queue stats */
 rcu_read_lock();

 ice_for_each_alloc_txq(vsi, j) {
  tx_ring = READ_ONCE(vsi->tx_rings[j]);
  if (tx_ring && tx_ring->ring_stats) {
   data[i++] = tx_ring->ring_stats->stats.pkts;
   data[i++] = tx_ring->ring_stats->stats.bytes;
  } else {
   data[i++] = 0;
   data[i++] = 0;
  }
 }

 ice_for_each_alloc_rxq(vsi, j) {
  rx_ring = READ_ONCE(vsi->rx_rings[j]);
  if (rx_ring && rx_ring->ring_stats) {
   data[i++] = rx_ring->ring_stats->stats.pkts;
   data[i++] = rx_ring->ring_stats->stats.bytes;
  } else {
   data[i++] = 0;
   data[i++] = 0;
  }
 }

 rcu_read_unlock();

 if (vsi->type != ICE_VSI_PF)
  return;

 for (j = 0; j < ICE_PF_STATS_LEN; j++) {
  p = (char *)pf + ice_gstrings_pf_stats[j].stat_offset;
  data[i++] = (ice_gstrings_pf_stats[j].sizeof_stat ==
        sizeof(u64)) ? *(u64 *)p : *(u32 *)p;
 }

 for (j = 0; j < ICE_MAX_USER_PRIORITY; j++) {
  data[i++] = pf->stats.priority_xon_tx[j];
  data[i++] = pf->stats.priority_xoff_tx[j];
 }

 for (j = 0; j < ICE_MAX_USER_PRIORITY; j++) {
  data[i++] = pf->stats.priority_xon_rx[j];
  data[i++] = pf->stats.priority_xoff_rx[j];
 }
}

static void
ice_get_ethtool_stats(struct net_device *netdev,
        struct ethtool_stats __always_unused *stats, u64 *data)
{
 struct ice_netdev_priv *np = netdev_priv(netdev);

 __ice_get_ethtool_stats(netdev, stats, data, np->vsi);
}

#define ICE_PHY_TYPE_LOW_MASK_MIN_1G (ICE_PHY_TYPE_LOW_100BASE_TX | \
      ICE_PHY_TYPE_LOW_100M_SGMII)

#define ICE_PHY_TYPE_LOW_MASK_MIN_25G (ICE_PHY_TYPE_LOW_MASK_MIN_1G | \
      ICE_PHY_TYPE_LOW_1000BASE_T | \
      ICE_PHY_TYPE_LOW_1000BASE_SX | \
      ICE_PHY_TYPE_LOW_1000BASE_LX | \
      ICE_PHY_TYPE_LOW_1000BASE_KX | \
      ICE_PHY_TYPE_LOW_1G_SGMII | \
      ICE_PHY_TYPE_LOW_2500BASE_T | \
      ICE_PHY_TYPE_LOW_2500BASE_X | \
      ICE_PHY_TYPE_LOW_2500BASE_KX | \
      ICE_PHY_TYPE_LOW_5GBASE_T | \
      ICE_PHY_TYPE_LOW_5GBASE_KR | \
      ICE_PHY_TYPE_LOW_10GBASE_T | \
      ICE_PHY_TYPE_LOW_10G_SFI_DA | \
      ICE_PHY_TYPE_LOW_10GBASE_SR | \
      ICE_PHY_TYPE_LOW_10GBASE_LR | \
      ICE_PHY_TYPE_LOW_10GBASE_KR_CR1 | \
      ICE_PHY_TYPE_LOW_10G_SFI_AOC_ACC | \
      ICE_PHY_TYPE_LOW_10G_SFI_C2C)

#define ICE_PHY_TYPE_LOW_MASK_100G (ICE_PHY_TYPE_LOW_100GBASE_CR4 | \
      ICE_PHY_TYPE_LOW_100GBASE_SR4 | \
      ICE_PHY_TYPE_LOW_100GBASE_LR4 | \
      ICE_PHY_TYPE_LOW_100GBASE_KR4 | \
      ICE_PHY_TYPE_LOW_100G_CAUI4_AOC_ACC | \
      ICE_PHY_TYPE_LOW_100G_CAUI4 | \
      ICE_PHY_TYPE_LOW_100G_AUI4_AOC_ACC | \
      ICE_PHY_TYPE_LOW_100G_AUI4 | \
      ICE_PHY_TYPE_LOW_100GBASE_CR_PAM4 | \
      ICE_PHY_TYPE_LOW_100GBASE_KR_PAM4 | \
      ICE_PHY_TYPE_LOW_100GBASE_CP2 | \
      ICE_PHY_TYPE_LOW_100GBASE_SR2 | \
      ICE_PHY_TYPE_LOW_100GBASE_DR)

#define ICE_PHY_TYPE_HIGH_MASK_100G (ICE_PHY_TYPE_HIGH_100GBASE_KR2_PAM4 | \
      ICE_PHY_TYPE_HIGH_100G_CAUI2_AOC_ACC |\
      ICE_PHY_TYPE_HIGH_100G_CAUI2 | \
      ICE_PHY_TYPE_HIGH_100G_AUI2_AOC_ACC | \
      ICE_PHY_TYPE_HIGH_100G_AUI2)

#define ICE_PHY_TYPE_HIGH_MASK_200G (ICE_PHY_TYPE_HIGH_200G_CR4_PAM4 | \
      ICE_PHY_TYPE_HIGH_200G_SR4 | \
      ICE_PHY_TYPE_HIGH_200G_FR4 | \
      ICE_PHY_TYPE_HIGH_200G_LR4 | \
      ICE_PHY_TYPE_HIGH_200G_DR4 | \
      ICE_PHY_TYPE_HIGH_200G_KR4_PAM4 | \
      ICE_PHY_TYPE_HIGH_200G_AUI4_AOC_ACC | \
      ICE_PHY_TYPE_HIGH_200G_AUI4)

/**
 * ice_mask_min_supported_speeds
 * @hw: pointer to the HW structure
 * @phy_types_high: PHY type high
 * @phy_types_low: PHY type low to apply minimum supported speeds mask
 *
 * Apply minimum supported speeds mask to PHY type low. These are the speeds
 * for ethtool supported link mode.
 */

static void
ice_mask_min_supported_speeds(struct ice_hw *hw,
         u64 phy_types_high, u64 *phy_types_low)
{
 /* if QSFP connection with 100G speed, minimum supported speed is 25G */
 if ((*phy_types_low & ICE_PHY_TYPE_LOW_MASK_100G) ||
     (phy_types_high & ICE_PHY_TYPE_HIGH_MASK_100G) ||
     (phy_types_high & ICE_PHY_TYPE_HIGH_MASK_200G))
  *phy_types_low &= ~ICE_PHY_TYPE_LOW_MASK_MIN_25G;
 else if (!ice_is_100m_speed_supported(hw))
  *phy_types_low &= ~ICE_PHY_TYPE_LOW_MASK_MIN_1G;
}

/**
 * ice_linkmode_set_bit - set link mode bit
 * @phy_to_ethtool: PHY type to ethtool link mode struct to set
 * @ks: ethtool link ksettings struct to fill out
 * @req_speeds: speed requested by user
 * @advert_phy_type: advertised PHY type
 * @phy_type: PHY type
 */

static void
ice_linkmode_set_bit(const struct ice_phy_type_to_ethtool *phy_to_ethtool,
       struct ethtool_link_ksettings *ks, u32 req_speeds,
       u64 advert_phy_type, u32 phy_type)
{
 linkmode_set_bit(phy_to_ethtool->link_mode, ks->link_modes.supported);

 if (req_speeds & phy_to_ethtool->aq_link_speed ||
     (!req_speeds && advert_phy_type & BIT(phy_type)))
  linkmode_set_bit(phy_to_ethtool->link_mode,
     ks->link_modes.advertising);
}

/**
 * ice_phy_type_to_ethtool - convert the phy_types to ethtool link modes
 * @netdev: network interface device structure
 * @ks: ethtool link ksettings struct to fill out
 */

static void
ice_phy_type_to_ethtool(struct net_device *netdev,
   struct ethtool_link_ksettings *ks)
{
 struct ice_netdev_priv *np = netdev_priv(netdev);
 struct ice_vsi *vsi = np->vsi;
 struct ice_pf *pf = vsi->back;
 u64 advert_phy_type_lo = 0;
 u64 advert_phy_type_hi = 0;
 u64 phy_types_high = 0;
 u64 phy_types_low = 0;
 u32 req_speeds;
 u32 i;

 req_speeds = vsi->port_info->phy.link_info.req_speeds;

 /* Check if lenient mode is supported and enabled, or in strict mode.
 *
 * In lenient mode the Supported link modes are the PHY types without
 * media. The Advertising link mode is either 1. the user requested
 * speed, 2. the override PHY mask, or 3. the PHY types with media.
 *
 * In strict mode Supported link mode are the PHY type with media,
 * and Advertising link modes are the media PHY type or the speed
 * requested by user.
 */

 if (test_bit(ICE_FLAG_LINK_LENIENT_MODE_ENA, pf->flags)) {
  phy_types_low = le64_to_cpu(pf->nvm_phy_type_lo);
  phy_types_high = le64_to_cpu(pf->nvm_phy_type_hi);

  ice_mask_min_supported_speeds(&pf->hw, phy_types_high,
           &phy_types_low);
  /* determine advertised modes based on link override only
 * if it's supported and if the FW doesn't abstract the
 * driver from having to account for link overrides
 */

  if (ice_fw_supports_link_override(&pf->hw) &&
      !ice_fw_supports_report_dflt_cfg(&pf->hw)) {
   struct ice_link_default_override_tlv *ldo;

   ldo = &pf->link_dflt_override;
   /* If override enabled and PHY mask set, then
 * Advertising link mode is the intersection of the PHY
 * types without media and the override PHY mask.
 */

   if (ldo->options & ICE_LINK_OVERRIDE_EN &&
       (ldo->phy_type_low || ldo->phy_type_high)) {
    advert_phy_type_lo =
     le64_to_cpu(pf->nvm_phy_type_lo) &
     ldo->phy_type_low;
    advert_phy_type_hi =
     le64_to_cpu(pf->nvm_phy_type_hi) &
     ldo->phy_type_high;
   }
  }
 } else {
  /* strict mode */
  phy_types_low = vsi->port_info->phy.phy_type_low;
  phy_types_high = vsi->port_info->phy.phy_type_high;
 }

 /* If Advertising link mode PHY type is not using override PHY type,
 * then use PHY type with media.
 */

 if (!advert_phy_type_lo && !advert_phy_type_hi) {
  advert_phy_type_lo = vsi->port_info->phy.phy_type_low;
  advert_phy_type_hi = vsi->port_info->phy.phy_type_high;
 }

 linkmode_zero(ks->link_modes.supported);
 linkmode_zero(ks->link_modes.advertising);

 for (i = 0; i < ARRAY_SIZE(phy_type_low_lkup); i++) {
  if (phy_types_low & BIT_ULL(i))
   ice_linkmode_set_bit(&phy_type_low_lkup[i], ks,
          req_speeds, advert_phy_type_lo,
          i);
 }

 for (i = 0; i < ARRAY_SIZE(phy_type_high_lkup); i++) {
  if (phy_types_high & BIT_ULL(i))
   ice_linkmode_set_bit(&phy_type_high_lkup[i], ks,
          req_speeds, advert_phy_type_hi,
          i);
 }
}

#define TEST_SET_BITS_TIMEOUT 50
#define TEST_SET_BITS_SLEEP_MAX 2000
#define TEST_SET_BITS_SLEEP_MIN 1000

/**
 * ice_get_settings_link_up - Get Link settings for when link is up
 * @ks: ethtool ksettings to fill in
 * @netdev: network interface device structure
 */

static void
ice_get_settings_link_up(struct ethtool_link_ksettings *ks,
    struct net_device *netdev)
{
 struct ice_netdev_priv *np = netdev_priv(netdev);
 struct ice_port_info *pi = np->vsi->port_info;
 struct ice_link_status *link_info;
 struct ice_vsi *vsi = np->vsi;

 link_info = &vsi->port_info->phy.link_info;

 /* Get supported and advertised settings from PHY ability with media */
 ice_phy_type_to_ethtool(netdev, ks);

 switch (link_info->link_speed) {
 case ICE_AQ_LINK_SPEED_200GB:
  ks->base.speed = SPEED_200000;
  break;
 case ICE_AQ_LINK_SPEED_100GB:
  ks->base.speed = SPEED_100000;
  break;
 case ICE_AQ_LINK_SPEED_50GB:
  ks->base.speed = SPEED_50000;
  break;
 case ICE_AQ_LINK_SPEED_40GB:
  ks->base.speed = SPEED_40000;
  break;
 case ICE_AQ_LINK_SPEED_25GB:
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=97 H=93 G=94

¤ Dauer der Verarbeitung: 0.30 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.