Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Linux/drivers/gpu/drm/ci/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 973 B image not shown  

Quelle  ice_ethtool.c   Sprache: unbekannt

 
// 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

[ zur Elbe Produktseite wechseln0.30Quellennavigators  Analyse erneut starten  ]