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

Quelle  bnx2x_ethtool.c   Sprache: C

 
/* bnx2x_ethtool.c: QLogic Everest network driver.
 *
 * Copyright (c) 2007-2013 Broadcom Corporation
 * Copyright (c) 2014 QLogic Corporation
 * All rights reserved
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation.
 *
 * Maintained by: Ariel Elior <ariel.elior@qlogic.com>
 * Written by: Eliezer Tamir
 * Based on code from Michael Chan's bnx2 driver
 * UDP CSUM errata workaround by Arik Gendelman
 * Slowpath and fastpath rework by Vladislav Zolotarov
 * Statistics and Link management by Yitchak Gertner
 *
 */


#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

#include <linux/ethtool.h>
#include <linux/netdevice.h>
#include <linux/types.h>
#include <linux/sched.h>
#include <linux/crc32.h>
#include "bnx2x.h"
#include "bnx2x_cmn.h"
#include "bnx2x_dump.h"
#include "bnx2x_init.h"

/* Note: in the format strings below %s is replaced by the queue-name which is
 * either its index or 'fcoe' for the fcoe queue. Make sure the format string
 * length does not exceed ETH_GSTRING_LEN - MAX_QUEUE_NAME_LEN + 2
 */

#define MAX_QUEUE_NAME_LEN 4
static const struct {
 long offset;
 int size;
 char string[ETH_GSTRING_LEN];
} bnx2x_q_stats_arr[] = {
/* 1 */ { Q_STATS_OFFSET32(total_bytes_received_hi), 8, "[%d]: rx_bytes" },
 { Q_STATS_OFFSET32(total_unicast_packets_received_hi),
      8, "[%d]: rx_ucast_packets" },
 { Q_STATS_OFFSET32(total_multicast_packets_received_hi),
      8, "[%d]: rx_mcast_packets" },
 { Q_STATS_OFFSET32(total_broadcast_packets_received_hi),
      8, "[%d]: rx_bcast_packets" },
 { Q_STATS_OFFSET32(no_buff_discard_hi), 8, "[%d]: rx_discards" },
 { Q_STATS_OFFSET32(rx_err_discard_pkt),
      4, "[%d]: rx_phy_ip_err_discards"},
 { Q_STATS_OFFSET32(rx_skb_alloc_failed),
      4, "[%d]: rx_skb_alloc_discard" },
 { Q_STATS_OFFSET32(hw_csum_err), 4, "[%d]: rx_csum_offload_errors" },
 { Q_STATS_OFFSET32(driver_xoff), 4, "[%d]: tx_exhaustion_events" },
 { Q_STATS_OFFSET32(total_bytes_transmitted_hi), 8, "[%d]: tx_bytes" },
/* 10 */{ Q_STATS_OFFSET32(total_unicast_packets_transmitted_hi),
      8, "[%d]: tx_ucast_packets" },
 { Q_STATS_OFFSET32(total_multicast_packets_transmitted_hi),
      8, "[%d]: tx_mcast_packets" },
 { Q_STATS_OFFSET32(total_broadcast_packets_transmitted_hi),
      8, "[%d]: tx_bcast_packets" },
 { Q_STATS_OFFSET32(total_tpa_aggregations_hi),
      8, "[%d]: tpa_aggregations" },
 { Q_STATS_OFFSET32(total_tpa_aggregated_frames_hi),
     8, "[%d]: tpa_aggregated_frames"},
 { Q_STATS_OFFSET32(total_tpa_bytes_hi), 8, "[%d]: tpa_bytes"},
 { Q_STATS_OFFSET32(driver_filtered_tx_pkt),
     4, "[%d]: driver_filtered_tx_pkt" }
};

#define BNX2X_NUM_Q_STATS ARRAY_SIZE(bnx2x_q_stats_arr)

static const struct {
 long offset;
 int size;
 bool is_port_stat;
 char string[ETH_GSTRING_LEN];
} bnx2x_stats_arr[] = {
/* 1 */ { STATS_OFFSET32(total_bytes_received_hi),
    8, false"rx_bytes" },
 { STATS_OFFSET32(error_bytes_received_hi),
    8, false"rx_error_bytes" },
 { STATS_OFFSET32(total_unicast_packets_received_hi),
    8, false"rx_ucast_packets" },
 { STATS_OFFSET32(total_multicast_packets_received_hi),
    8, false"rx_mcast_packets" },
 { STATS_OFFSET32(total_broadcast_packets_received_hi),
    8, false"rx_bcast_packets" },
 { STATS_OFFSET32(rx_stat_dot3statsfcserrors_hi),
    8, true"rx_crc_errors" },
 { STATS_OFFSET32(rx_stat_dot3statsalignmenterrors_hi),
    8, true"rx_align_errors" },
 { STATS_OFFSET32(rx_stat_etherstatsundersizepkts_hi),
    8, true"rx_undersize_packets" },
 { STATS_OFFSET32(etherstatsoverrsizepkts_hi),
    8, true"rx_oversize_packets" },
/* 10 */{ STATS_OFFSET32(rx_stat_etherstatsfragments_hi),
    8, true"rx_fragments" },
 { STATS_OFFSET32(rx_stat_etherstatsjabbers_hi),
    8, true"rx_jabbers" },
 { STATS_OFFSET32(no_buff_discard_hi),
    8, false"rx_discards" },
 { STATS_OFFSET32(mac_filter_discard),
    4, true"rx_filtered_packets" },
 { STATS_OFFSET32(mf_tag_discard),
    4, true"rx_mf_tag_discard" },
 { STATS_OFFSET32(pfc_frames_received_hi),
    8, true"pfc_frames_received" },
 { STATS_OFFSET32(pfc_frames_sent_hi),
    8, true"pfc_frames_sent" },
 { STATS_OFFSET32(brb_drop_hi),
    8, true"rx_brb_discard" },
 { STATS_OFFSET32(brb_truncate_hi),
    8, true"rx_brb_truncate" },
 { STATS_OFFSET32(pause_frames_received_hi),
    8, true"rx_pause_frames" },
 { STATS_OFFSET32(rx_stat_maccontrolframesreceived_hi),
    8, true"rx_mac_ctrl_frames" },
 { STATS_OFFSET32(nig_timer_max),
    4, true"rx_constant_pause_events" },
/* 20 */{ STATS_OFFSET32(rx_err_discard_pkt),
    4, false"rx_phy_ip_err_discards"},
 { STATS_OFFSET32(rx_skb_alloc_failed),
    4, false"rx_skb_alloc_discard" },
 { STATS_OFFSET32(hw_csum_err),
    4, false"rx_csum_offload_errors" },
 { STATS_OFFSET32(driver_xoff),
    4, false"tx_exhaustion_events" },
 { STATS_OFFSET32(total_bytes_transmitted_hi),
    8, false"tx_bytes" },
 { STATS_OFFSET32(tx_stat_ifhcoutbadoctets_hi),
    8, true"tx_error_bytes" },
 { STATS_OFFSET32(total_unicast_packets_transmitted_hi),
    8, false"tx_ucast_packets" },
 { STATS_OFFSET32(total_multicast_packets_transmitted_hi),
    8, false"tx_mcast_packets" },
 { STATS_OFFSET32(total_broadcast_packets_transmitted_hi),
    8, false"tx_bcast_packets" },
 { STATS_OFFSET32(tx_stat_dot3statsinternalmactransmiterrors_hi),
    8, true"tx_mac_errors" },
 { STATS_OFFSET32(rx_stat_dot3statscarriersenseerrors_hi),
    8, true"tx_carrier_errors" },
/* 30 */{ STATS_OFFSET32(tx_stat_dot3statssinglecollisionframes_hi),
    8, true"tx_single_collisions" },
 { STATS_OFFSET32(tx_stat_dot3statsmultiplecollisionframes_hi),
    8, true"tx_multi_collisions" },
 { STATS_OFFSET32(tx_stat_dot3statsdeferredtransmissions_hi),
    8, true"tx_deferred" },
 { STATS_OFFSET32(tx_stat_dot3statsexcessivecollisions_hi),
    8, true"tx_excess_collisions" },
 { STATS_OFFSET32(tx_stat_dot3statslatecollisions_hi),
    8, true"tx_late_collisions" },
 { STATS_OFFSET32(tx_stat_etherstatscollisions_hi),
    8, true"tx_total_collisions" },
 { STATS_OFFSET32(tx_stat_etherstatspkts64octets_hi),
    8, true"tx_64_byte_packets" },
 { STATS_OFFSET32(tx_stat_etherstatspkts65octetsto127octets_hi),
    8, true"tx_65_to_127_byte_packets" },
 { STATS_OFFSET32(tx_stat_etherstatspkts128octetsto255octets_hi),
    8, true"tx_128_to_255_byte_packets" },
 { STATS_OFFSET32(tx_stat_etherstatspkts256octetsto511octets_hi),
    8, true"tx_256_to_511_byte_packets" },
/* 40 */{ STATS_OFFSET32(tx_stat_etherstatspkts512octetsto1023octets_hi),
    8, true"tx_512_to_1023_byte_packets" },
 { STATS_OFFSET32(etherstatspkts1024octetsto1522octets_hi),
    8, true"tx_1024_to_1522_byte_packets" },
 { STATS_OFFSET32(etherstatspktsover1522octets_hi),
    8, true"tx_1523_to_9022_byte_packets" },
 { STATS_OFFSET32(pause_frames_sent_hi),
    8, true"tx_pause_frames" },
 { STATS_OFFSET32(total_tpa_aggregations_hi),
    8, false"tpa_aggregations" },
 { STATS_OFFSET32(total_tpa_aggregated_frames_hi),
    8, false"tpa_aggregated_frames"},
 { STATS_OFFSET32(total_tpa_bytes_hi),
    8, false"tpa_bytes"},
 { STATS_OFFSET32(recoverable_error),
    4, false"recoverable_errors" },
 { STATS_OFFSET32(unrecoverable_error),
    4, false"unrecoverable_errors" },
 { STATS_OFFSET32(driver_filtered_tx_pkt),
    4, false"driver_filtered_tx_pkt" },
 { STATS_OFFSET32(eee_tx_lpi),
    4, true"Tx LPI entry count"},
 { STATS_OFFSET32(ptp_skip_tx_ts),
    4, false"ptp_skipped_tx_tstamp" },
};

#define BNX2X_NUM_STATS  ARRAY_SIZE(bnx2x_stats_arr)

static int bnx2x_get_port_type(struct bnx2x *bp)
{
 int port_type;
 u32 phy_idx = bnx2x_get_cur_phy_idx(bp);
 switch (bp->link_params.phy[phy_idx].media_type) {
 case ETH_PHY_SFPP_10G_FIBER:
 case ETH_PHY_SFP_1G_FIBER:
 case ETH_PHY_XFP_FIBER:
 case ETH_PHY_KR:
 case ETH_PHY_CX4:
  port_type = PORT_FIBRE;
  break;
 case ETH_PHY_DA_TWINAX:
  port_type = PORT_DA;
  break;
 case ETH_PHY_BASE_T:
  port_type = PORT_TP;
  break;
 case ETH_PHY_NOT_PRESENT:
  port_type = PORT_NONE;
  break;
 case ETH_PHY_UNSPECIFIED:
 default:
  port_type = PORT_OTHER;
  break;
 }
 return port_type;
}

static int bnx2x_get_vf_link_ksettings(struct net_device *dev,
           struct ethtool_link_ksettings *cmd)
{
 struct bnx2x *bp = netdev_priv(dev);
 u32 supported, advertising;

 ethtool_convert_link_mode_to_legacy_u32(&supported,
      cmd->link_modes.supported);
 ethtool_convert_link_mode_to_legacy_u32(&advertising,
      cmd->link_modes.advertising);

 if (bp->state == BNX2X_STATE_OPEN) {
  if (test_bit(BNX2X_LINK_REPORT_FD,
        &bp->vf_link_vars.link_report_flags))
   cmd->base.duplex = DUPLEX_FULL;
  else
   cmd->base.duplex = DUPLEX_HALF;

  cmd->base.speed = bp->vf_link_vars.line_speed;
 } else {
  cmd->base.duplex = DUPLEX_UNKNOWN;
  cmd->base.speed = SPEED_UNKNOWN;
 }

 cmd->base.port  = PORT_OTHER;
 cmd->base.phy_address = 0;
 cmd->base.autoneg = AUTONEG_DISABLE;

 DP(BNX2X_MSG_ETHTOOL, "ethtool_cmd: cmd %d\n"
    " supported 0x%x advertising 0x%x speed %u\n"
    " duplex %d port %d phy_address %d\n"
    " autoneg %d\n",
    cmd->base.cmd, supported, advertising,
    cmd->base.speed,
    cmd->base.duplex, cmd->base.port, cmd->base.phy_address,
    cmd->base.autoneg);

 return 0;
}

static int bnx2x_get_link_ksettings(struct net_device *dev,
        struct ethtool_link_ksettings *cmd)
{
 struct bnx2x *bp = netdev_priv(dev);
 int cfg_idx = bnx2x_get_link_cfg_idx(bp);
 u32 media_type;
 u32 supported, advertising, lp_advertising;

 ethtool_convert_link_mode_to_legacy_u32(&lp_advertising,
      cmd->link_modes.lp_advertising);

 /* Dual Media boards present all available port types */
 supported = bp->port.supported[cfg_idx] |
  (bp->port.supported[cfg_idx ^ 1] &
   (SUPPORTED_TP | SUPPORTED_FIBRE));
 advertising = bp->port.advertising[cfg_idx];
 media_type = bp->link_params.phy[bnx2x_get_cur_phy_idx(bp)].media_type;
 if (media_type == ETH_PHY_SFP_1G_FIBER) {
  supported &= ~(SUPPORTED_10000baseT_Full);
  advertising &= ~(ADVERTISED_10000baseT_Full);
 }

 if ((bp->state == BNX2X_STATE_OPEN) && bp->link_vars.link_up &&
     !(bp->flags & MF_FUNC_DIS)) {
  cmd->base.duplex = bp->link_vars.duplex;

  if (IS_MF(bp) && !BP_NOMCP(bp))
   cmd->base.speed = bnx2x_get_mf_speed(bp);
  else
   cmd->base.speed = bp->link_vars.line_speed;
 } else {
  cmd->base.duplex = DUPLEX_UNKNOWN;
  cmd->base.speed = SPEED_UNKNOWN;
 }

 cmd->base.port = bnx2x_get_port_type(bp);

 cmd->base.phy_address = bp->mdio.prtad;

 if (bp->link_params.req_line_speed[cfg_idx] == SPEED_AUTO_NEG)
  cmd->base.autoneg = AUTONEG_ENABLE;
 else
  cmd->base.autoneg = AUTONEG_DISABLE;

 /* Publish LP advertised speeds and FC */
 if (bp->link_vars.link_status & LINK_STATUS_AUTO_NEGOTIATE_COMPLETE) {
  u32 status = bp->link_vars.link_status;

  lp_advertising |= ADVERTISED_Autoneg;
  if (status & LINK_STATUS_LINK_PARTNER_SYMMETRIC_PAUSE)
   lp_advertising |= ADVERTISED_Pause;
  if (status & LINK_STATUS_LINK_PARTNER_ASYMMETRIC_PAUSE)
   lp_advertising |= ADVERTISED_Asym_Pause;

  if (status & LINK_STATUS_LINK_PARTNER_10THD_CAPABLE)
   lp_advertising |= ADVERTISED_10baseT_Half;
  if (status & LINK_STATUS_LINK_PARTNER_10TFD_CAPABLE)
   lp_advertising |= ADVERTISED_10baseT_Full;
  if (status & LINK_STATUS_LINK_PARTNER_100TXHD_CAPABLE)
   lp_advertising |= ADVERTISED_100baseT_Half;
  if (status & LINK_STATUS_LINK_PARTNER_100TXFD_CAPABLE)
   lp_advertising |= ADVERTISED_100baseT_Full;
  if (status & LINK_STATUS_LINK_PARTNER_1000THD_CAPABLE)
   lp_advertising |= ADVERTISED_1000baseT_Half;
  if (status & LINK_STATUS_LINK_PARTNER_1000TFD_CAPABLE) {
   if (media_type == ETH_PHY_KR) {
    lp_advertising |=
     ADVERTISED_1000baseKX_Full;
   } else {
    lp_advertising |=
     ADVERTISED_1000baseT_Full;
   }
  }
  if (status & LINK_STATUS_LINK_PARTNER_2500XFD_CAPABLE)
   lp_advertising |= ADVERTISED_2500baseX_Full;
  if (status & LINK_STATUS_LINK_PARTNER_10GXFD_CAPABLE) {
   if (media_type == ETH_PHY_KR) {
    lp_advertising |=
     ADVERTISED_10000baseKR_Full;
   } else {
    lp_advertising |=
     ADVERTISED_10000baseT_Full;
   }
  }
  if (status & LINK_STATUS_LINK_PARTNER_20GXFD_CAPABLE)
   lp_advertising |= ADVERTISED_20000baseKR2_Full;
 }

 ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported,
      supported);
 ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising,
      advertising);
 ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.lp_advertising,
      lp_advertising);

 DP(BNX2X_MSG_ETHTOOL, "ethtool_cmd: cmd %d\n"
    " supported 0x%x advertising 0x%x speed %u\n"
    " duplex %d port %d phy_address %d\n"
    " autoneg %d\n",
    cmd->base.cmd, supported, advertising,
    cmd->base.speed,
    cmd->base.duplex, cmd->base.port, cmd->base.phy_address,
    cmd->base.autoneg);

 return 0;
}

static int bnx2x_set_link_ksettings(struct net_device *dev,
        const struct ethtool_link_ksettings *cmd)
{
 struct bnx2x *bp = netdev_priv(dev);
 u32 advertising, cfg_idx, old_multi_phy_config, new_multi_phy_config;
 u32 speed, phy_idx;
 u32 supported;
 u8 duplex = cmd->base.duplex;

 ethtool_convert_link_mode_to_legacy_u32(&supported,
      cmd->link_modes.supported);
 ethtool_convert_link_mode_to_legacy_u32(&advertising,
      cmd->link_modes.advertising);

 if (IS_MF_SD(bp))
  return 0;

 DP(BNX2X_MSG_ETHTOOL, "ethtool_cmd: cmd %d\n"
    " supported 0x%x advertising 0x%x speed %u\n"
    " duplex %d port %d phy_address %d\n"
    " autoneg %d\n",
    cmd->base.cmd, supported, advertising,
    cmd->base.speed,
    cmd->base.duplex, cmd->base.port, cmd->base.phy_address,
    cmd->base.autoneg);

 speed = cmd->base.speed;

 /* If received a request for an unknown duplex, assume full*/
 if (duplex == DUPLEX_UNKNOWN)
  duplex = DUPLEX_FULL;

 if (IS_MF_SI(bp)) {
  u32 part;
  u32 line_speed = bp->link_vars.line_speed;

  /* use 10G if no link detected */
  if (!line_speed)
   line_speed = 10000;

  if (bp->common.bc_ver < REQ_BC_VER_4_SET_MF_BW) {
   DP(BNX2X_MSG_ETHTOOL,
      "To set speed BC %X or higher is required, please upgrade BC\n",
      REQ_BC_VER_4_SET_MF_BW);
   return -EINVAL;
  }

  part = (speed * 100) / line_speed;

  if (line_speed < speed || !part) {
   DP(BNX2X_MSG_ETHTOOL,
      "Speed setting should be in a range from 1%% to 100%% of actual line speed\n");
   return -EINVAL;
  }

  if (bp->state != BNX2X_STATE_OPEN)
   /* store value for following "load" */
   bp->pending_max = part;
  else
   bnx2x_update_max_mf_config(bp, part);

  return 0;
 }

 cfg_idx = bnx2x_get_link_cfg_idx(bp);
 old_multi_phy_config = bp->link_params.multi_phy_config;
 if (cmd->base.port != bnx2x_get_port_type(bp)) {
  switch (cmd->base.port) {
  case PORT_TP:
   if (!(bp->port.supported[0] & SUPPORTED_TP ||
         bp->port.supported[1] & SUPPORTED_TP)) {
    DP(BNX2X_MSG_ETHTOOL,
       "Unsupported port type\n");
    return -EINVAL;
   }
   bp->link_params.multi_phy_config &=
    ~PORT_HW_CFG_PHY_SELECTION_MASK;
   if (bp->link_params.multi_phy_config &
       PORT_HW_CFG_PHY_SWAPPED_ENABLED)
    bp->link_params.multi_phy_config |=
    PORT_HW_CFG_PHY_SELECTION_SECOND_PHY;
   else
    bp->link_params.multi_phy_config |=
    PORT_HW_CFG_PHY_SELECTION_FIRST_PHY;
   break;
  case PORT_FIBRE:
  case PORT_DA:
  case PORT_NONE:
   if (!(bp->port.supported[0] & SUPPORTED_FIBRE ||
         bp->port.supported[1] & SUPPORTED_FIBRE)) {
    DP(BNX2X_MSG_ETHTOOL,
       "Unsupported port type\n");
    return -EINVAL;
   }
   bp->link_params.multi_phy_config &=
    ~PORT_HW_CFG_PHY_SELECTION_MASK;
   if (bp->link_params.multi_phy_config &
       PORT_HW_CFG_PHY_SWAPPED_ENABLED)
    bp->link_params.multi_phy_config |=
    PORT_HW_CFG_PHY_SELECTION_FIRST_PHY;
   else
    bp->link_params.multi_phy_config |=
    PORT_HW_CFG_PHY_SELECTION_SECOND_PHY;
   break;
  default:
   DP(BNX2X_MSG_ETHTOOL, "Unsupported port type\n");
   return -EINVAL;
  }
 }
 /* Save new config in case command complete successfully */
 new_multi_phy_config = bp->link_params.multi_phy_config;
 /* Get the new cfg_idx */
 cfg_idx = bnx2x_get_link_cfg_idx(bp);
 /* Restore old config in case command failed */
 bp->link_params.multi_phy_config = old_multi_phy_config;
 DP(BNX2X_MSG_ETHTOOL, "cfg_idx = %x\n", cfg_idx);

 if (cmd->base.autoneg == AUTONEG_ENABLE) {
  u32 an_supported_speed = bp->port.supported[cfg_idx];
  if (bp->link_params.phy[EXT_PHY1].type ==
      PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84833)
   an_supported_speed |= (SUPPORTED_100baseT_Half |
            SUPPORTED_100baseT_Full);
  if (!(bp->port.supported[cfg_idx] & SUPPORTED_Autoneg)) {
   DP(BNX2X_MSG_ETHTOOL, "Autoneg not supported\n");
   return -EINVAL;
  }

  /* advertise the requested speed and duplex if supported */
  if (advertising & ~an_supported_speed) {
   DP(BNX2X_MSG_ETHTOOL,
      "Advertisement parameters are not supported\n");
   return -EINVAL;
  }

  bp->link_params.req_line_speed[cfg_idx] = SPEED_AUTO_NEG;
  bp->link_params.req_duplex[cfg_idx] = duplex;
  bp->port.advertising[cfg_idx] = (ADVERTISED_Autoneg |
      advertising);
  if (advertising) {

   bp->link_params.speed_cap_mask[cfg_idx] = 0;
   if (advertising & ADVERTISED_10baseT_Half) {
    bp->link_params.speed_cap_mask[cfg_idx] |=
    PORT_HW_CFG_SPEED_CAPABILITY_D0_10M_HALF;
   }
   if (advertising & ADVERTISED_10baseT_Full)
    bp->link_params.speed_cap_mask[cfg_idx] |=
    PORT_HW_CFG_SPEED_CAPABILITY_D0_10M_FULL;

   if (advertising & ADVERTISED_100baseT_Full)
    bp->link_params.speed_cap_mask[cfg_idx] |=
    PORT_HW_CFG_SPEED_CAPABILITY_D0_100M_FULL;

   if (advertising & ADVERTISED_100baseT_Half) {
    bp->link_params.speed_cap_mask[cfg_idx] |=
         PORT_HW_CFG_SPEED_CAPABILITY_D0_100M_HALF;
   }
   if (advertising & ADVERTISED_1000baseT_Half) {
    bp->link_params.speed_cap_mask[cfg_idx] |=
     PORT_HW_CFG_SPEED_CAPABILITY_D0_1G;
   }
   if (advertising & (ADVERTISED_1000baseT_Full |
      ADVERTISED_1000baseKX_Full))
    bp->link_params.speed_cap_mask[cfg_idx] |=
     PORT_HW_CFG_SPEED_CAPABILITY_D0_1G;

   if (advertising & (ADVERTISED_10000baseT_Full |
      ADVERTISED_10000baseKX4_Full |
      ADVERTISED_10000baseKR_Full))
    bp->link_params.speed_cap_mask[cfg_idx] |=
     PORT_HW_CFG_SPEED_CAPABILITY_D0_10G;

   if (advertising & ADVERTISED_20000baseKR2_Full)
    bp->link_params.speed_cap_mask[cfg_idx] |=
     PORT_HW_CFG_SPEED_CAPABILITY_D0_20G;
  }
 } else { /* forced speed */
  /* advertise the requested speed and duplex if supported */
  switch (speed) {
  case SPEED_10:
   if (duplex == DUPLEX_FULL) {
    if (!(bp->port.supported[cfg_idx] &
          SUPPORTED_10baseT_Full)) {
     DP(BNX2X_MSG_ETHTOOL,
        "10M full not supported\n");
     return -EINVAL;
    }

    advertising = (ADVERTISED_10baseT_Full |
            ADVERTISED_TP);
   } else {
    if (!(bp->port.supported[cfg_idx] &
          SUPPORTED_10baseT_Half)) {
     DP(BNX2X_MSG_ETHTOOL,
        "10M half not supported\n");
     return -EINVAL;
    }

    advertising = (ADVERTISED_10baseT_Half |
            ADVERTISED_TP);
   }
   break;

  case SPEED_100:
   if (duplex == DUPLEX_FULL) {
    if (!(bp->port.supported[cfg_idx] &
      SUPPORTED_100baseT_Full)) {
     DP(BNX2X_MSG_ETHTOOL,
        "100M full not supported\n");
     return -EINVAL;
    }

    advertising = (ADVERTISED_100baseT_Full |
            ADVERTISED_TP);
   } else {
    if (!(bp->port.supported[cfg_idx] &
      SUPPORTED_100baseT_Half)) {
     DP(BNX2X_MSG_ETHTOOL,
        "100M half not supported\n");
     return -EINVAL;
    }

    advertising = (ADVERTISED_100baseT_Half |
            ADVERTISED_TP);
   }
   break;

  case SPEED_1000:
   if (duplex != DUPLEX_FULL) {
    DP(BNX2X_MSG_ETHTOOL,
       "1G half not supported\n");
    return -EINVAL;
   }

   if (bp->port.supported[cfg_idx] &
        SUPPORTED_1000baseT_Full) {
    advertising = (ADVERTISED_1000baseT_Full |
            ADVERTISED_TP);

   } else if (bp->port.supported[cfg_idx] &
       SUPPORTED_1000baseKX_Full) {
    advertising = ADVERTISED_1000baseKX_Full;
   } else {
    DP(BNX2X_MSG_ETHTOOL,
       "1G full not supported\n");
    return -EINVAL;
   }

   break;

  case SPEED_2500:
   if (duplex != DUPLEX_FULL) {
    DP(BNX2X_MSG_ETHTOOL,
       "2.5G half not supported\n");
    return -EINVAL;
   }

   if (!(bp->port.supported[cfg_idx]
         & SUPPORTED_2500baseX_Full)) {
    DP(BNX2X_MSG_ETHTOOL,
       "2.5G full not supported\n");
    return -EINVAL;
   }

   advertising = (ADVERTISED_2500baseX_Full |
           ADVERTISED_TP);
   break;

  case SPEED_10000:
   if (duplex != DUPLEX_FULL) {
    DP(BNX2X_MSG_ETHTOOL,
       "10G half not supported\n");
    return -EINVAL;
   }
   phy_idx = bnx2x_get_cur_phy_idx(bp);
   if ((bp->port.supported[cfg_idx] &
        SUPPORTED_10000baseT_Full) &&
       (bp->link_params.phy[phy_idx].media_type !=
        ETH_PHY_SFP_1G_FIBER)) {
    advertising = (ADVERTISED_10000baseT_Full |
            ADVERTISED_FIBRE);
   } else if (bp->port.supported[cfg_idx] &
          SUPPORTED_10000baseKR_Full) {
    advertising = (ADVERTISED_10000baseKR_Full |
            ADVERTISED_FIBRE);
   } else {
    DP(BNX2X_MSG_ETHTOOL,
       "10G full not supported\n");
    return -EINVAL;
   }

   break;

  default:
   DP(BNX2X_MSG_ETHTOOL, "Unsupported speed %u\n", speed);
   return -EINVAL;
  }

  bp->link_params.req_line_speed[cfg_idx] = speed;
  bp->link_params.req_duplex[cfg_idx] = duplex;
  bp->port.advertising[cfg_idx] = advertising;
 }

 DP(BNX2X_MSG_ETHTOOL, "req_line_speed %d\n"
    " req_duplex %d advertising 0x%x\n",
    bp->link_params.req_line_speed[cfg_idx],
    bp->link_params.req_duplex[cfg_idx],
    bp->port.advertising[cfg_idx]);

 /* Set new config */
 bp->link_params.multi_phy_config = new_multi_phy_config;
 if (netif_running(dev)) {
  bnx2x_stats_handle(bp, STATS_EVENT_STOP);
  bnx2x_force_link_reset(bp);
  bnx2x_link_set(bp);
 }

 return 0;
}

#define DUMP_ALL_PRESETS  0x1FFF
#define DUMP_MAX_PRESETS  13

static int __bnx2x_get_preset_regs_len(struct bnx2x *bp, u32 preset)
{
 if (CHIP_IS_E1(bp))
  return dump_num_registers[0][preset-1];
 else if (CHIP_IS_E1H(bp))
  return dump_num_registers[1][preset-1];
 else if (CHIP_IS_E2(bp))
  return dump_num_registers[2][preset-1];
 else if (CHIP_IS_E3A0(bp))
  return dump_num_registers[3][preset-1];
 else if (CHIP_IS_E3B0(bp))
  return dump_num_registers[4][preset-1];
 else
  return 0;
}

static int __bnx2x_get_regs_len(struct bnx2x *bp)
{
 u32 preset_idx;
 int regdump_len = 0;

 /* Calculate the total preset regs length */
 for (preset_idx = 1; preset_idx <= DUMP_MAX_PRESETS; preset_idx++)
  regdump_len += __bnx2x_get_preset_regs_len(bp, preset_idx);

 return regdump_len;
}

static int bnx2x_get_regs_len(struct net_device *dev)
{
 struct bnx2x *bp = netdev_priv(dev);
 int regdump_len = 0;

 if (IS_VF(bp))
  return 0;

 regdump_len = __bnx2x_get_regs_len(bp);
 regdump_len *= 4;
 regdump_len += sizeof(struct dump_header);

 return regdump_len;
}

#define IS_E1_REG(chips) ((chips & DUMP_CHIP_E1) == DUMP_CHIP_E1)
#define IS_E1H_REG(chips) ((chips & DUMP_CHIP_E1H) == DUMP_CHIP_E1H)
#define IS_E2_REG(chips) ((chips & DUMP_CHIP_E2) == DUMP_CHIP_E2)
#define IS_E3A0_REG(chips) ((chips & DUMP_CHIP_E3A0) == DUMP_CHIP_E3A0)
#define IS_E3B0_REG(chips) ((chips & DUMP_CHIP_E3B0) == DUMP_CHIP_E3B0)

#define IS_REG_IN_PRESET(presets, idx)  \
  ((presets & (1 << (idx-1))) == (1 << (idx-1)))

/******* Paged registers info selectors ********/
static const u32 *__bnx2x_get_page_addr_ar(struct bnx2x *bp)
{
 if (CHIP_IS_E2(bp))
  return page_vals_e2;
 else if (CHIP_IS_E3(bp))
  return page_vals_e3;
 else
  return NULL;
}

static u32 __bnx2x_get_page_reg_num(struct bnx2x *bp)
{
 if (CHIP_IS_E2(bp))
  return PAGE_MODE_VALUES_E2;
 else if (CHIP_IS_E3(bp))
  return PAGE_MODE_VALUES_E3;
 else
  return 0;
}

static const u32 *__bnx2x_get_page_write_ar(struct bnx2x *bp)
{
 if (CHIP_IS_E2(bp))
  return page_write_regs_e2;
 else if (CHIP_IS_E3(bp))
  return page_write_regs_e3;
 else
  return NULL;
}

static u32 __bnx2x_get_page_write_num(struct bnx2x *bp)
{
 if (CHIP_IS_E2(bp))
  return PAGE_WRITE_REGS_E2;
 else if (CHIP_IS_E3(bp))
  return PAGE_WRITE_REGS_E3;
 else
  return 0;
}

static const struct reg_addr *__bnx2x_get_page_read_ar(struct bnx2x *bp)
{
 if (CHIP_IS_E2(bp))
  return page_read_regs_e2;
 else if (CHIP_IS_E3(bp))
  return page_read_regs_e3;
 else
  return NULL;
}

static u32 __bnx2x_get_page_read_num(struct bnx2x *bp)
{
 if (CHIP_IS_E2(bp))
  return PAGE_READ_REGS_E2;
 else if (CHIP_IS_E3(bp))
  return PAGE_READ_REGS_E3;
 else
  return 0;
}

static bool bnx2x_is_reg_in_chip(struct bnx2x *bp,
           const struct reg_addr *reg_info)
{
 if (CHIP_IS_E1(bp))
  return IS_E1_REG(reg_info->chips);
 else if (CHIP_IS_E1H(bp))
  return IS_E1H_REG(reg_info->chips);
 else if (CHIP_IS_E2(bp))
  return IS_E2_REG(reg_info->chips);
 else if (CHIP_IS_E3A0(bp))
  return IS_E3A0_REG(reg_info->chips);
 else if (CHIP_IS_E3B0(bp))
  return IS_E3B0_REG(reg_info->chips);
 else
  return false;
}

static bool bnx2x_is_wreg_in_chip(struct bnx2x *bp,
 const struct wreg_addr *wreg_info)
{
 if (CHIP_IS_E1(bp))
  return IS_E1_REG(wreg_info->chips);
 else if (CHIP_IS_E1H(bp))
  return IS_E1H_REG(wreg_info->chips);
 else if (CHIP_IS_E2(bp))
  return IS_E2_REG(wreg_info->chips);
 else if (CHIP_IS_E3A0(bp))
  return IS_E3A0_REG(wreg_info->chips);
 else if (CHIP_IS_E3B0(bp))
  return IS_E3B0_REG(wreg_info->chips);
 else
  return false;
}

/**
 * bnx2x_read_pages_regs - read "paged" registers
 *
 * @bp: device handle
 * @p: output buffer
 * @preset: the preset value
 *
 * Reads "paged" memories: memories that may only be read by first writing to a
 * specific address ("write address") and then reading from a specific address
 * ("read address"). There may be more than one write address per "page" and
 * more than one read address per write address.
 */

static void bnx2x_read_pages_regs(struct bnx2x *bp, u32 *p, u32 preset)
{
 u32 i, j, k, n;

 /* addresses of the paged registers */
 const u32 *page_addr = __bnx2x_get_page_addr_ar(bp);
 /* number of paged registers */
 int num_pages = __bnx2x_get_page_reg_num(bp);
 /* write addresses */
 const u32 *write_addr = __bnx2x_get_page_write_ar(bp);
 /* number of write addresses */
 int write_num = __bnx2x_get_page_write_num(bp);
 /* read addresses info */
 const struct reg_addr *read_addr = __bnx2x_get_page_read_ar(bp);
 /* number of read addresses */
 int read_num = __bnx2x_get_page_read_num(bp);
 u32 addr, size;

 for (i = 0; i < num_pages; i++) {
  for (j = 0; j < write_num; j++) {
   REG_WR(bp, write_addr[j], page_addr[i]);

   for (k = 0; k < read_num; k++) {
    if (IS_REG_IN_PRESET(read_addr[k].presets,
           preset)) {
     size = read_addr[k].size;
     for (n = 0; n < size; n++) {
      addr = read_addr[k].addr + n*4;
      *p++ = REG_RD(bp, addr);
     }
    }
   }
  }
 }
}

static int __bnx2x_get_preset_regs(struct bnx2x *bp, u32 *p, u32 preset)
{
 u32 i, j, addr;
 const struct wreg_addr *wreg_addr_p = NULL;

 if (CHIP_IS_E1(bp))
  wreg_addr_p = &wreg_addr_e1;
 else if (CHIP_IS_E1H(bp))
  wreg_addr_p = &wreg_addr_e1h;
 else if (CHIP_IS_E2(bp))
  wreg_addr_p = &wreg_addr_e2;
 else if (CHIP_IS_E3A0(bp))
  wreg_addr_p = &wreg_addr_e3;
 else if (CHIP_IS_E3B0(bp))
  wreg_addr_p = &wreg_addr_e3b0;

 /* Read the idle_chk registers */
 for (i = 0; i < IDLE_REGS_COUNT; i++) {
  if (bnx2x_is_reg_in_chip(bp, &idle_reg_addrs[i]) &&
      IS_REG_IN_PRESET(idle_reg_addrs[i].presets, preset)) {
   for (j = 0; j < idle_reg_addrs[i].size; j++)
    *p++ = REG_RD(bp, idle_reg_addrs[i].addr + j*4);
  }
 }

 /* Read the regular registers */
 for (i = 0; i < REGS_COUNT; i++) {
  if (bnx2x_is_reg_in_chip(bp, ®_addrs[i]) &&
      IS_REG_IN_PRESET(reg_addrs[i].presets, preset)) {
   for (j = 0; j < reg_addrs[i].size; j++)
    *p++ = REG_RD(bp, reg_addrs[i].addr + j*4);
  }
 }

 /* Read the CAM registers */
 if (bnx2x_is_wreg_in_chip(bp, wreg_addr_p) &&
     IS_REG_IN_PRESET(wreg_addr_p->presets, preset)) {
  for (i = 0; i < wreg_addr_p->size; i++) {
   *p++ = REG_RD(bp, wreg_addr_p->addr + i*4);

   /* In case of wreg_addr register, read additional
   registers from read_regs array
*/

   for (j = 0; j < wreg_addr_p->read_regs_count; j++) {
    addr = *(wreg_addr_p->read_regs);
    *p++ = REG_RD(bp, addr + j*4);
   }
  }
 }

 /* Paged registers are supported in E2 & E3 only */
 if (CHIP_IS_E2(bp) || CHIP_IS_E3(bp)) {
  /* Read "paged" registers */
  bnx2x_read_pages_regs(bp, p, preset);
 }

 return 0;
}

static void __bnx2x_get_regs(struct bnx2x *bp, u32 *p)
{
 u32 preset_idx;

 /* Read all registers, by reading all preset registers */
 for (preset_idx = 1; preset_idx <= DUMP_MAX_PRESETS; preset_idx++) {
  /* Skip presets with IOR */
  if ((preset_idx == 2) ||
      (preset_idx == 5) ||
      (preset_idx == 8) ||
      (preset_idx == 11))
   continue;
  __bnx2x_get_preset_regs(bp, p, preset_idx);
  p += __bnx2x_get_preset_regs_len(bp, preset_idx);
 }
}

static void bnx2x_get_regs(struct net_device *dev,
      struct ethtool_regs *regs, void *_p)
{
 u32 *p = _p;
 struct bnx2x *bp = netdev_priv(dev);
 struct dump_header dump_hdr = {0};

 regs->version = 2;
 memset(p, 0, regs->len);

 if (!netif_running(bp->dev))
  return;

 /* Disable parity attentions as long as following dump may
 * cause false alarms by reading never written registers. We
 * will re-enable parity attentions right after the dump.
 */


 bnx2x_disable_blocks_parity(bp);

 dump_hdr.header_size = (sizeof(struct dump_header) / 4) - 1;
 dump_hdr.preset = DUMP_ALL_PRESETS;
 dump_hdr.version = BNX2X_DUMP_VERSION;

 /* dump_meta_data presents OR of CHIP and PATH. */
 if (CHIP_IS_E1(bp)) {
  dump_hdr.dump_meta_data = DUMP_CHIP_E1;
 } else if (CHIP_IS_E1H(bp)) {
  dump_hdr.dump_meta_data = DUMP_CHIP_E1H;
 } else if (CHIP_IS_E2(bp)) {
  dump_hdr.dump_meta_data = DUMP_CHIP_E2 |
  (BP_PATH(bp) ? DUMP_PATH_1 : DUMP_PATH_0);
 } else if (CHIP_IS_E3A0(bp)) {
  dump_hdr.dump_meta_data = DUMP_CHIP_E3A0 |
  (BP_PATH(bp) ? DUMP_PATH_1 : DUMP_PATH_0);
 } else if (CHIP_IS_E3B0(bp)) {
  dump_hdr.dump_meta_data = DUMP_CHIP_E3B0 |
  (BP_PATH(bp) ? DUMP_PATH_1 : DUMP_PATH_0);
 }

 memcpy(p, &dump_hdr, sizeof(struct dump_header));
 p += dump_hdr.header_size + 1;

 /* This isn't really an error, but since attention handling is going
 * to print the GRC timeouts using this macro, we use the same.
 */

 BNX2X_ERR("Generating register dump. Might trigger harmless GRC timeouts\n");

 /* Actually read the registers */
 __bnx2x_get_regs(bp, p);

 /* Re-enable parity attentions */
 bnx2x_clear_blocks_parity(bp);
 bnx2x_enable_blocks_parity(bp);
}

static int bnx2x_get_preset_regs_len(struct net_device *dev, u32 preset)
{
 struct bnx2x *bp = netdev_priv(dev);
 int regdump_len = 0;

 regdump_len = __bnx2x_get_preset_regs_len(bp, preset);
 regdump_len *= 4;
 regdump_len += sizeof(struct dump_header);

 return regdump_len;
}

static int bnx2x_set_dump(struct net_device *dev, struct ethtool_dump *val)
{
 struct bnx2x *bp = netdev_priv(dev);

 /* Use the ethtool_dump "flag" field as the dump preset index */
 if (val->flag < 1 || val->flag > DUMP_MAX_PRESETS)
  return -EINVAL;

 bp->dump_preset_idx = val->flag;
 return 0;
}

static int bnx2x_get_dump_flag(struct net_device *dev,
          struct ethtool_dump *dump)
{
 struct bnx2x *bp = netdev_priv(dev);

 dump->version = BNX2X_DUMP_VERSION;
 dump->flag = bp->dump_preset_idx;
 /* Calculate the requested preset idx length */
 dump->len = bnx2x_get_preset_regs_len(dev, bp->dump_preset_idx);
 DP(BNX2X_MSG_ETHTOOL, "Get dump preset %d length=%d\n",
    bp->dump_preset_idx, dump->len);
 return 0;
}

static int bnx2x_get_dump_data(struct net_device *dev,
          struct ethtool_dump *dump,
          void *buffer)
{
 u32 *p = buffer;
 struct bnx2x *bp = netdev_priv(dev);
 struct dump_header dump_hdr = {0};

 /* Disable parity attentions as long as following dump may
 * cause false alarms by reading never written registers. We
 * will re-enable parity attentions right after the dump.
 */


 bnx2x_disable_blocks_parity(bp);

 dump_hdr.header_size = (sizeof(struct dump_header) / 4) - 1;
 dump_hdr.preset = bp->dump_preset_idx;
 dump_hdr.version = BNX2X_DUMP_VERSION;

 DP(BNX2X_MSG_ETHTOOL, "Get dump data of preset %d\n", dump_hdr.preset);

 /* dump_meta_data presents OR of CHIP and PATH. */
 if (CHIP_IS_E1(bp)) {
  dump_hdr.dump_meta_data = DUMP_CHIP_E1;
 } else if (CHIP_IS_E1H(bp)) {
  dump_hdr.dump_meta_data = DUMP_CHIP_E1H;
 } else if (CHIP_IS_E2(bp)) {
  dump_hdr.dump_meta_data = DUMP_CHIP_E2 |
  (BP_PATH(bp) ? DUMP_PATH_1 : DUMP_PATH_0);
 } else if (CHIP_IS_E3A0(bp)) {
  dump_hdr.dump_meta_data = DUMP_CHIP_E3A0 |
  (BP_PATH(bp) ? DUMP_PATH_1 : DUMP_PATH_0);
 } else if (CHIP_IS_E3B0(bp)) {
  dump_hdr.dump_meta_data = DUMP_CHIP_E3B0 |
  (BP_PATH(bp) ? DUMP_PATH_1 : DUMP_PATH_0);
 }

 memcpy(p, &dump_hdr, sizeof(struct dump_header));
 p += dump_hdr.header_size + 1;

 /* Actually read the registers */
 __bnx2x_get_preset_regs(bp, p, dump_hdr.preset);

 /* Re-enable parity attentions */
 bnx2x_clear_blocks_parity(bp);
 bnx2x_enable_blocks_parity(bp);

 return 0;
}

static void bnx2x_get_drvinfo(struct net_device *dev,
         struct ethtool_drvinfo *info)
{
 struct bnx2x *bp = netdev_priv(dev);
 char version[ETHTOOL_FWVERS_LEN];
 int ext_dev_info_offset;
 u32 mbi;

 strscpy(info->driver, DRV_MODULE_NAME, sizeof(info->driver));

 if (SHMEM2_HAS(bp, extended_dev_info_shared_addr)) {
  ext_dev_info_offset = SHMEM2_RD(bp,
      extended_dev_info_shared_addr);
  mbi = REG_RD(bp, ext_dev_info_offset +
        offsetof(struct extended_dev_info_shared_cfg,
          mbi_version));
  if (mbi) {
   memset(version, 0, sizeof(version));
   snprintf(version, ETHTOOL_FWVERS_LEN, "mbi %d.%d.%d ",
     (mbi & 0xff000000) >> 24,
     (mbi & 0x00ff0000) >> 16,
     (mbi & 0x0000ff00) >> 8);
   strscpy(info->fw_version, version,
    sizeof(info->fw_version));
  }
 }

 memset(version, 0, sizeof(version));
 bnx2x_fill_fw_str(bp, version, sizeof(version));
 strlcat(info->fw_version, version, sizeof(info->fw_version));

 strscpy(info->bus_info, pci_name(bp->pdev), sizeof(info->bus_info));
}

static void bnx2x_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
{
 struct bnx2x *bp = netdev_priv(dev);

 if (bp->flags & NO_WOL_FLAG) {
  wol->supported = 0;
  wol->wolopts = 0;
 } else {
  wol->supported = WAKE_MAGIC;
  if (bp->wol)
   wol->wolopts = WAKE_MAGIC;
  else
   wol->wolopts = 0;
 }
 memset(&wol->sopass, 0, sizeof(wol->sopass));
}

static int bnx2x_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
{
 struct bnx2x *bp = netdev_priv(dev);

 if (wol->wolopts & ~WAKE_MAGIC) {
  DP(BNX2X_MSG_ETHTOOL, "WOL not supported\n");
  return -EINVAL;
 }

 if (wol->wolopts & WAKE_MAGIC) {
  if (bp->flags & NO_WOL_FLAG) {
   DP(BNX2X_MSG_ETHTOOL, "WOL not supported\n");
   return -EINVAL;
  }
  bp->wol = 1;
 } else
  bp->wol = 0;

 if (SHMEM2_HAS(bp, curr_cfg))
  SHMEM2_WR(bp, curr_cfg, CURR_CFG_MET_OS);

 return 0;
}

static u32 bnx2x_get_msglevel(struct net_device *dev)
{
 struct bnx2x *bp = netdev_priv(dev);

 return bp->msg_enable;
}

static void bnx2x_set_msglevel(struct net_device *dev, u32 level)
{
 struct bnx2x *bp = netdev_priv(dev);

 if (capable(CAP_NET_ADMIN)) {
  /* dump MCP trace */
  if (IS_PF(bp) && (level & BNX2X_MSG_MCP))
   bnx2x_fw_dump_lvl(bp, KERN_INFO);
  bp->msg_enable = level;
 }
}

static int bnx2x_nway_reset(struct net_device *dev)
{
 struct bnx2x *bp = netdev_priv(dev);

 if (!bp->port.pmf)
  return 0;

 if (netif_running(dev)) {
  bnx2x_stats_handle(bp, STATS_EVENT_STOP);
  bnx2x_force_link_reset(bp);
  bnx2x_link_set(bp);
 }

 return 0;
}

static u32 bnx2x_get_link(struct net_device *dev)
{
 struct bnx2x *bp = netdev_priv(dev);

 if (bp->flags & MF_FUNC_DIS || (bp->state != BNX2X_STATE_OPEN))
  return 0;

 if (IS_VF(bp))
  return !test_bit(BNX2X_LINK_REPORT_LINK_DOWN,
     &bp->vf_link_vars.link_report_flags);

 return bp->link_vars.link_up;
}

static int bnx2x_get_eeprom_len(struct net_device *dev)
{
 struct bnx2x *bp = netdev_priv(dev);

 return bp->common.flash_size;
}

/* Per pf misc lock must be acquired before the per port mcp lock. Otherwise,
 * had we done things the other way around, if two pfs from the same port would
 * attempt to access nvram at the same time, we could run into a scenario such
 * as:
 * pf A takes the port lock.
 * pf B succeeds in taking the same lock since they are from the same port.
 * pf A takes the per pf misc lock. Performs eeprom access.
 * pf A finishes. Unlocks the per pf misc lock.
 * Pf B takes the lock and proceeds to perform its own access.
 * pf A unlocks the per port lock, while pf B is still working (!).
 * mcp takes the per port lock and corrupts pf B's access (and/or has its own
 * access corrupted by pf B)
 */

static int bnx2x_acquire_nvram_lock(struct bnx2x *bp)
{
 int port = BP_PORT(bp);
 int count, i;
 u32 val;

 /* acquire HW lock: protect against other PFs in PF Direct Assignment */
 bnx2x_acquire_hw_lock(bp, HW_LOCK_RESOURCE_NVRAM);

 /* adjust timeout for emulation/FPGA */
 count = BNX2X_NVRAM_TIMEOUT_COUNT;
 if (CHIP_REV_IS_SLOW(bp))
  count *= 100;

 /* request access to nvram interface */
 REG_WR(bp, MCP_REG_MCPR_NVM_SW_ARB,
        (MCPR_NVM_SW_ARB_ARB_REQ_SET1 << port));

 for (i = 0; i < count*10; i++) {
  val = REG_RD(bp, MCP_REG_MCPR_NVM_SW_ARB);
  if (val & (MCPR_NVM_SW_ARB_ARB_ARB1 << port))
   break;

  udelay(5);
 }

 if (!(val & (MCPR_NVM_SW_ARB_ARB_ARB1 << port))) {
  DP(BNX2X_MSG_ETHTOOL | BNX2X_MSG_NVM,
     "cannot get access to nvram interface\n");
  bnx2x_release_hw_lock(bp, HW_LOCK_RESOURCE_NVRAM);
  return -EBUSY;
 }

 return 0;
}

static int bnx2x_release_nvram_lock(struct bnx2x *bp)
{
 int port = BP_PORT(bp);
 int count, i;
 u32 val;

 /* adjust timeout for emulation/FPGA */
 count = BNX2X_NVRAM_TIMEOUT_COUNT;
 if (CHIP_REV_IS_SLOW(bp))
  count *= 100;

 /* relinquish nvram interface */
 REG_WR(bp, MCP_REG_MCPR_NVM_SW_ARB,
        (MCPR_NVM_SW_ARB_ARB_REQ_CLR1 << port));

 for (i = 0; i < count*10; i++) {
  val = REG_RD(bp, MCP_REG_MCPR_NVM_SW_ARB);
  if (!(val & (MCPR_NVM_SW_ARB_ARB_ARB1 << port)))
   break;

  udelay(5);
 }

 if (val & (MCPR_NVM_SW_ARB_ARB_ARB1 << port)) {
  DP(BNX2X_MSG_ETHTOOL | BNX2X_MSG_NVM,
     "cannot free access to nvram interface\n");
  return -EBUSY;
 }

 /* release HW lock: protect against other PFs in PF Direct Assignment */
 bnx2x_release_hw_lock(bp, HW_LOCK_RESOURCE_NVRAM);
 return 0;
}

static void bnx2x_enable_nvram_access(struct bnx2x *bp)
{
 u32 val;

 val = REG_RD(bp, MCP_REG_MCPR_NVM_ACCESS_ENABLE);

 /* enable both bits, even on read */
 REG_WR(bp, MCP_REG_MCPR_NVM_ACCESS_ENABLE,
        (val | MCPR_NVM_ACCESS_ENABLE_EN |
        MCPR_NVM_ACCESS_ENABLE_WR_EN));
}

static void bnx2x_disable_nvram_access(struct bnx2x *bp)
{
 u32 val;

 val = REG_RD(bp, MCP_REG_MCPR_NVM_ACCESS_ENABLE);

 /* disable both bits, even after read */
 REG_WR(bp, MCP_REG_MCPR_NVM_ACCESS_ENABLE,
        (val & ~(MCPR_NVM_ACCESS_ENABLE_EN |
   MCPR_NVM_ACCESS_ENABLE_WR_EN)));
}

static int bnx2x_nvram_read_dword(struct bnx2x *bp, u32 offset, __be32 *ret_val,
      u32 cmd_flags)
{
 int count, i, rc;
 u32 val;

 /* build the command word */
 cmd_flags |= MCPR_NVM_COMMAND_DOIT;

 /* need to clear DONE bit separately */
 REG_WR(bp, MCP_REG_MCPR_NVM_COMMAND, MCPR_NVM_COMMAND_DONE);

 /* address of the NVRAM to read from */
 REG_WR(bp, MCP_REG_MCPR_NVM_ADDR,
        (offset & MCPR_NVM_ADDR_NVM_ADDR_VALUE));

 /* issue a read command */
 REG_WR(bp, MCP_REG_MCPR_NVM_COMMAND, cmd_flags);

 /* adjust timeout for emulation/FPGA */
 count = BNX2X_NVRAM_TIMEOUT_COUNT;
 if (CHIP_REV_IS_SLOW(bp))
  count *= 100;

 /* wait for completion */
 *ret_val = 0;
 rc = -EBUSY;
 for (i = 0; i < count; i++) {
  udelay(5);
  val = REG_RD(bp, MCP_REG_MCPR_NVM_COMMAND);

  if (val & MCPR_NVM_COMMAND_DONE) {
   val = REG_RD(bp, MCP_REG_MCPR_NVM_READ);
   /* we read nvram data in cpu order
 * but ethtool sees it as an array of bytes
 * converting to big-endian will do the work
 */

   *ret_val = cpu_to_be32(val);
   rc = 0;
   break;
  }
 }
 if (rc == -EBUSY)
  DP(BNX2X_MSG_ETHTOOL | BNX2X_MSG_NVM,
     "nvram read timeout expired\n");
 return rc;
}

int bnx2x_nvram_read(struct bnx2x *bp, u32 offset, u8 *ret_buf,
       int buf_size)
{
 int rc;
 u32 cmd_flags;
 __be32 val;

 if ((offset & 0x03) || (buf_size & 0x03) || (buf_size == 0)) {
  DP(BNX2X_MSG_ETHTOOL | BNX2X_MSG_NVM,
     "Invalid parameter: offset 0x%x buf_size 0x%x\n",
     offset, buf_size);
  return -EINVAL;
 }

 if (offset + buf_size > bp->common.flash_size) {
  DP(BNX2X_MSG_ETHTOOL | BNX2X_MSG_NVM,
     "Invalid parameter: offset (0x%x) + buf_size (0x%x) > flash_size (0x%x)\n",
     offset, buf_size, bp->common.flash_size);
  return -EINVAL;
 }

 /* request access to nvram interface */
 rc = bnx2x_acquire_nvram_lock(bp);
 if (rc)
  return rc;

 /* enable access to nvram interface */
 bnx2x_enable_nvram_access(bp);

 /* read the first word(s) */
 cmd_flags = MCPR_NVM_COMMAND_FIRST;
 while ((buf_size > sizeof(u32)) && (rc == 0)) {
  rc = bnx2x_nvram_read_dword(bp, offset, &val, cmd_flags);
  memcpy(ret_buf, &val, 4);

  /* advance to the next dword */
  offset += sizeof(u32);
  ret_buf += sizeof(u32);
  buf_size -= sizeof(u32);
  cmd_flags = 0;
 }

 if (rc == 0) {
  cmd_flags |= MCPR_NVM_COMMAND_LAST;
  rc = bnx2x_nvram_read_dword(bp, offset, &val, cmd_flags);
  memcpy(ret_buf, &val, 4);
 }

 /* disable access to nvram interface */
 bnx2x_disable_nvram_access(bp);
 bnx2x_release_nvram_lock(bp);

 return rc;
}

static int bnx2x_nvram_read32(struct bnx2x *bp, u32 offset, u32 *buf,
         int buf_size)
{
 int rc;

 rc = bnx2x_nvram_read(bp, offset, (u8 *)buf, buf_size);

 if (!rc) {
  __be32 *be = (__be32 *)buf;

  while ((buf_size -= 4) >= 0)
   *buf++ = be32_to_cpu(*be++);
 }

 return rc;
}

static bool bnx2x_is_nvm_accessible(struct bnx2x *bp)
{
 int rc = 1;
 u16 pm = 0;
 struct net_device *dev = pci_get_drvdata(bp->pdev);

 if (bp->pdev->pm_cap)
  rc = pci_read_config_word(bp->pdev,
       bp->pdev->pm_cap + PCI_PM_CTRL, &pm);

 if ((rc && !netif_running(dev)) ||
     (!rc && ((pm & PCI_PM_CTRL_STATE_MASK) != (__force u16)PCI_D0)))
  return false;

 return true;
}

static int bnx2x_get_eeprom(struct net_device *dev,
       struct ethtool_eeprom *eeprom, u8 *eebuf)
{
 struct bnx2x *bp = netdev_priv(dev);

 if (!bnx2x_is_nvm_accessible(bp)) {
  DP(BNX2X_MSG_ETHTOOL  | BNX2X_MSG_NVM,
     "cannot access eeprom when the interface is down\n");
  return -EAGAIN;
 }

 DP(BNX2X_MSG_ETHTOOL | BNX2X_MSG_NVM, "ethtool_eeprom: cmd %d\n"
    " magic 0x%x offset 0x%x (%d) len 0x%x (%d)\n",
    eeprom->cmd, eeprom->magic, eeprom->offset, eeprom->offset,
    eeprom->len, eeprom->len);

 /* parameters already validated in ethtool_get_eeprom */

 return bnx2x_nvram_read(bp, eeprom->offset, eebuf, eeprom->len);
}

static int bnx2x_get_module_eeprom(struct net_device *dev,
       struct ethtool_eeprom *ee,
       u8 *data)
{
 struct bnx2x *bp = netdev_priv(dev);
 int rc = -EINVAL, phy_idx;
 u8 *user_data = data;
 unsigned int start_addr = ee->offset, xfer_size = 0;

 if (!bnx2x_is_nvm_accessible(bp)) {
  DP(BNX2X_MSG_ETHTOOL | BNX2X_MSG_NVM,
     "cannot access eeprom when the interface is down\n");
  return -EAGAIN;
 }

 phy_idx = bnx2x_get_cur_phy_idx(bp);

 /* Read A0 section */
 if (start_addr < ETH_MODULE_SFF_8079_LEN) {
  /* Limit transfer size to the A0 section boundary */
  if (start_addr + ee->len > ETH_MODULE_SFF_8079_LEN)
   xfer_size = ETH_MODULE_SFF_8079_LEN - start_addr;
  else
   xfer_size = ee->len;
  bnx2x_acquire_phy_lock(bp);
  rc = bnx2x_read_sfp_module_eeprom(&bp->link_params.phy[phy_idx],
        &bp->link_params,
        I2C_DEV_ADDR_A0,
        start_addr,
        xfer_size,
        user_data);
  bnx2x_release_phy_lock(bp);
  if (rc) {
   DP(BNX2X_MSG_ETHTOOL, "Failed reading A0 section\n");

   return -EINVAL;
  }
  user_data += xfer_size;
  start_addr += xfer_size;
 }

 /* Read A2 section */
 if ((start_addr >= ETH_MODULE_SFF_8079_LEN) &&
     (start_addr < ETH_MODULE_SFF_8472_LEN)) {
  xfer_size = ee->len - xfer_size;
  /* Limit transfer size to the A2 section boundary */
  if (start_addr + xfer_size > ETH_MODULE_SFF_8472_LEN)
   xfer_size = ETH_MODULE_SFF_8472_LEN - start_addr;
  start_addr -= ETH_MODULE_SFF_8079_LEN;
  bnx2x_acquire_phy_lock(bp);
  rc = bnx2x_read_sfp_module_eeprom(&bp->link_params.phy[phy_idx],
        &bp->link_params,
        I2C_DEV_ADDR_A2,
        start_addr,
        xfer_size,
        user_data);
  bnx2x_release_phy_lock(bp);
  if (rc) {
   DP(BNX2X_MSG_ETHTOOL, "Failed reading A2 section\n");
   return -EINVAL;
  }
 }
 return rc;
}

static int bnx2x_get_module_info(struct net_device *dev,
     struct ethtool_modinfo *modinfo)
{
 struct bnx2x *bp = netdev_priv(dev);
 int phy_idx, rc;
 u8 sff8472_comp, diag_type;

 if (!bnx2x_is_nvm_accessible(bp)) {
  DP(BNX2X_MSG_ETHTOOL | BNX2X_MSG_NVM,
     "cannot access eeprom when the interface is down\n");
  return -EAGAIN;
 }
 phy_idx = bnx2x_get_cur_phy_idx(bp);
 bnx2x_acquire_phy_lock(bp);
 rc = bnx2x_read_sfp_module_eeprom(&bp->link_params.phy[phy_idx],
       &bp->link_params,
       I2C_DEV_ADDR_A0,
       SFP_EEPROM_SFF_8472_COMP_ADDR,
       SFP_EEPROM_SFF_8472_COMP_SIZE,
       &sff8472_comp);
 bnx2x_release_phy_lock(bp);
 if (rc) {
  DP(BNX2X_MSG_ETHTOOL, "Failed reading SFF-8472 comp field\n");
  return -EINVAL;
 }

 bnx2x_acquire_phy_lock(bp);
 rc = bnx2x_read_sfp_module_eeprom(&bp->link_params.phy[phy_idx],
       &bp->link_params,
       I2C_DEV_ADDR_A0,
       SFP_EEPROM_DIAG_TYPE_ADDR,
       SFP_EEPROM_DIAG_TYPE_SIZE,
       &diag_type);
 bnx2x_release_phy_lock(bp);
 if (rc) {
  DP(BNX2X_MSG_ETHTOOL, "Failed reading Diag Type field\n");
  return -EINVAL;
 }

 if (!sff8472_comp ||
     (diag_type & SFP_EEPROM_DIAG_ADDR_CHANGE_REQ) ||
     !(diag_type & SFP_EEPROM_DDM_IMPLEMENTED)) {
  modinfo->type = ETH_MODULE_SFF_8079;
  modinfo->eeprom_len = ETH_MODULE_SFF_8079_LEN;
 } else {
  modinfo->type = ETH_MODULE_SFF_8472;
  modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN;
 }
 return 0;
}

static int bnx2x_nvram_write_dword(struct bnx2x *bp, u32 offset, u32 val,
       u32 cmd_flags)
{
 int count, i, rc;

 /* build the command word */
 cmd_flags |= MCPR_NVM_COMMAND_DOIT | MCPR_NVM_COMMAND_WR;

 /* need to clear DONE bit separately */
 REG_WR(bp, MCP_REG_MCPR_NVM_COMMAND, MCPR_NVM_COMMAND_DONE);

 /* write the data */
 REG_WR(bp, MCP_REG_MCPR_NVM_WRITE, val);

 /* address of the NVRAM to write to */
 REG_WR(bp, MCP_REG_MCPR_NVM_ADDR,
        (offset & MCPR_NVM_ADDR_NVM_ADDR_VALUE));

 /* issue the write command */
 REG_WR(bp, MCP_REG_MCPR_NVM_COMMAND, cmd_flags);

 /* adjust timeout for emulation/FPGA */
 count = BNX2X_NVRAM_TIMEOUT_COUNT;
 if (CHIP_REV_IS_SLOW(bp))
  count *= 100;

 /* wait for completion */
 rc = -EBUSY;
 for (i = 0; i < count; i++) {
  udelay(5);
  val = REG_RD(bp, MCP_REG_MCPR_NVM_COMMAND);
  if (val & MCPR_NVM_COMMAND_DONE) {
   rc = 0;
   break;
  }
 }

 if (rc == -EBUSY)
  DP(BNX2X_MSG_ETHTOOL | BNX2X_MSG_NVM,
     "nvram write timeout expired\n");
 return rc;
}

#define BYTE_OFFSET(offset)  (8 * (offset & 0x03))

static int bnx2x_nvram_write1(struct bnx2x *bp, u32 offset, u8 *data_buf,
         int buf_size)
{
 int rc;
 u32 cmd_flags, align_offset, val;
 __be32 val_be;

 if (offset + buf_size > bp->common.flash_size) {
  DP(BNX2X_MSG_ETHTOOL | BNX2X_MSG_NVM,
     "Invalid parameter: offset (0x%x) + buf_size (0x%x) > flash_size (0x%x)\n",
     offset, buf_size, bp->common.flash_size);
  return -EINVAL;
 }

 /* request access to nvram interface */
 rc = bnx2x_acquire_nvram_lock(bp);
 if (rc)
  return rc;

 /* enable access to nvram interface */
 bnx2x_enable_nvram_access(bp);

 cmd_flags = (MCPR_NVM_COMMAND_FIRST | MCPR_NVM_COMMAND_LAST);
 align_offset = (offset & ~0x03);
 rc = bnx2x_nvram_read_dword(bp, align_offset, &val_be, cmd_flags);

 if (rc == 0) {
  /* nvram data is returned as an array of bytes
 * convert it back to cpu order
 */

  val = be32_to_cpu(val_be);

  val &= ~le32_to_cpu((__force __le32)
        (0xff << BYTE_OFFSET(offset)));
  val |= le32_to_cpu((__force __le32)
       (*data_buf << BYTE_OFFSET(offset)));

  rc = bnx2x_nvram_write_dword(bp, align_offset, val,
          cmd_flags);
 }

 /* disable access to nvram interface */
 bnx2x_disable_nvram_access(bp);
 bnx2x_release_nvram_lock(bp);

 return rc;
}

static int bnx2x_nvram_write(struct bnx2x *bp, u32 offset, u8 *data_buf,
        int buf_size)
{
 int rc;
 u32 cmd_flags;
 u32 val;
 u32 written_so_far;

 if (buf_size == 1) /* ethtool */
  return bnx2x_nvram_write1(bp, offset, data_buf, buf_size);

 if ((offset & 0x03) || (buf_size & 0x03) || (buf_size == 0)) {
  DP(BNX2X_MSG_ETHTOOL | BNX2X_MSG_NVM,
     "Invalid parameter: offset 0x%x buf_size 0x%x\n",
     offset, buf_size);
  return -EINVAL;
 }

 if (offset + buf_size > bp->common.flash_size) {
  DP(BNX2X_MSG_ETHTOOL | BNX2X_MSG_NVM,
     "Invalid parameter: offset (0x%x) + buf_size (0x%x) > flash_size (0x%x)\n",
     offset, buf_size, bp->common.flash_size);
  return -EINVAL;
 }

 /* request access to nvram interface */
 rc = bnx2x_acquire_nvram_lock(bp);
 if (rc)
  return rc;

 /* enable access to nvram interface */
 bnx2x_enable_nvram_access(bp);

 written_so_far = 0;
 cmd_flags = MCPR_NVM_COMMAND_FIRST;
 while ((written_so_far < buf_size) && (rc == 0)) {
  if (written_so_far == (buf_size - sizeof(u32)))
   cmd_flags |= MCPR_NVM_COMMAND_LAST;
  else if (((offset + 4) % BNX2X_NVRAM_PAGE_SIZE) == 0)
   cmd_flags |= MCPR_NVM_COMMAND_LAST;
  else if ((offset % BNX2X_NVRAM_PAGE_SIZE) == 0)
   cmd_flags |= MCPR_NVM_COMMAND_FIRST;

  memcpy(&val, data_buf, 4);

  /* Notice unlike bnx2x_nvram_read_dword() this will not
 * change val using be32_to_cpu(), which causes data to flip
 * if the eeprom is read and then written back. This is due
 * to tools utilizing this functionality that would break
 * if this would be resolved.
 */

  rc = bnx2x_nvram_write_dword(bp, offset, val, cmd_flags);

  /* advance to the next dword */
  offset += sizeof(u32);
  data_buf += sizeof(u32);
  written_so_far += sizeof(u32);

  /* At end of each 4Kb page, release nvram lock to allow MFW
 * chance to take it for its own use.
 */

  if ((cmd_flags & MCPR_NVM_COMMAND_LAST) &&
      (written_so_far < buf_size)) {
   DP(BNX2X_MSG_ETHTOOL | BNX2X_MSG_NVM,
      "Releasing NVM lock after offset 0x%x\n",
      (u32)(offset - sizeof(u32)));
   bnx2x_release_nvram_lock(bp);
   usleep_range(1000, 2000);
   rc = bnx2x_acquire_nvram_lock(bp);
   if (rc)
    return rc;
  }

  cmd_flags = 0;
 }

 /* disable access to nvram interface */
 bnx2x_disable_nvram_access(bp);
 bnx2x_release_nvram_lock(bp);

 return rc;
}

static int bnx2x_set_eeprom(struct net_device *dev,
       struct ethtool_eeprom *eeprom, u8 *eebuf)
{
 struct bnx2x *bp = netdev_priv(dev);
 int port = BP_PORT(bp);
 int rc = 0;
 u32 ext_phy_config;

 if (!bnx2x_is_nvm_accessible(bp)) {
  DP(BNX2X_MSG_ETHTOOL | BNX2X_MSG_NVM,
     "cannot access eeprom when the interface is down\n");
  return -EAGAIN;
 }

 DP(BNX2X_MSG_ETHTOOL | BNX2X_MSG_NVM, "ethtool_eeprom: cmd %d\n"
    " magic 0x%x offset 0x%x (%d) len 0x%x (%d)\n",
    eeprom->cmd, eeprom->magic, eeprom->offset, eeprom->offset,
    eeprom->len, eeprom->len);

 /* parameters already validated in ethtool_set_eeprom */

 /* PHY eeprom can be accessed only by the PMF */
 if ((eeprom->magic >= 0x50485900) && (eeprom->magic <= 0x504859FF) &&
     !bp->port.pmf) {
  DP(BNX2X_MSG_ETHTOOL | BNX2X_MSG_NVM,
     "wrong magic or interface is not pmf\n");
  return -EINVAL;
 }

 ext_phy_config =
  SHMEM_RD(bp,
    dev_info.port_hw_config[port].external_phy_config);

 if (eeprom->magic == 0x50485950) {
  /* 'PHYP' (0x50485950): prepare phy for FW upgrade */
  bnx2x_stats_handle(bp, STATS_EVENT_STOP);

  bnx2x_acquire_phy_lock(bp);
  rc |= bnx2x_link_reset(&bp->link_params,
           &bp->link_vars, 0);
  if (XGXS_EXT_PHY_TYPE(ext_phy_config) ==
     PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101)
   bnx2x_set_gpio(bp, MISC_REGISTERS_GPIO_0,
           MISC_REGISTERS_GPIO_HIGH, port);
  bnx2x_release_phy_lock(bp);
  bnx2x_link_report(bp);

 } else if (eeprom->magic == 0x50485952) {
  /* 'PHYR' (0x50485952): re-init link after FW upgrade */
  if (bp->state == BNX2X_STATE_OPEN) {
   bnx2x_acquire_phy_lock(bp);
   rc |= bnx2x_link_reset(&bp->link_params,
            &bp->link_vars, 1);

   rc |= bnx2x_phy_init(&bp->link_params,
          &bp->link_vars);
   bnx2x_release_phy_lock(bp);
   bnx2x_calc_fc_adv(bp);
  }
 } else if (eeprom->magic == 0x53985943) {
  /* 'PHYC' (0x53985943): PHY FW upgrade completed */
  if (XGXS_EXT_PHY_TYPE(ext_phy_config) ==
           PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101) {

   /* DSP Remove Download Mode */
   bnx2x_set_gpio(bp, MISC_REGISTERS_GPIO_0,
           MISC_REGISTERS_GPIO_LOW, port);

   bnx2x_acquire_phy_lock(bp);

   bnx2x_sfx7101_sp_sw_reset(bp,
      &bp->link_params.phy[EXT_PHY1]);

   /* wait 0.5 sec to allow it to run */
   msleep(500);
   bnx2x_ext_phy_hw_reset(bp, port);
   msleep(500);
   bnx2x_release_phy_lock(bp);
  }
 } else
  rc = bnx2x_nvram_write(bp, eeprom->offset, eebuf, eeprom->len);

 return rc;
}

static int bnx2x_get_coalesce(struct net_device *dev,
         struct ethtool_coalesce *coal,
         struct kernel_ethtool_coalesce *kernel_coal,
         struct netlink_ext_ack *extack)
{
 struct bnx2x *bp = netdev_priv(dev);

 memset(coal, 0, sizeof(struct ethtool_coalesce));

 coal->rx_coalesce_usecs = bp->rx_ticks;
 coal->tx_coalesce_usecs = bp->tx_ticks;

 return 0;
}

static int bnx2x_set_coalesce(struct net_device *dev,
         struct ethtool_coalesce *coal,
         struct kernel_ethtool_coalesce *kernel_coal,
         struct netlink_ext_ack *extack)
{
 struct bnx2x *bp = netdev_priv(dev);

 bp->rx_ticks = (u16)coal->rx_coalesce_usecs;
 if (bp->rx_ticks > BNX2X_MAX_COALESCE_TOUT)
  bp->rx_ticks = BNX2X_MAX_COALESCE_TOUT;

 bp->tx_ticks = (u16)coal->tx_coalesce_usecs;
 if (bp->tx_ticks > BNX2X_MAX_COALESCE_TOUT)
  bp->tx_ticks = BNX2X_MAX_COALESCE_TOUT;

 if (netif_running(dev))
  bnx2x_update_coalesce(bp);

 return 0;
}

static void bnx2x_get_ringparam(struct net_device *dev,
    struct ethtool_ringparam *ering,
    struct kernel_ethtool_ringparam *kernel_ering,
    struct netlink_ext_ack *extack)
{
 struct bnx2x *bp = netdev_priv(dev);

 ering->rx_max_pending = MAX_RX_AVAIL;

 /* If size isn't already set, we give an estimation of the number
 * of buffers we'll have. We're neglecting some possible conditions
 * [we couldn't know for certain at this point if number of queues
 * might shrink] but the number would be correct for the likely
 * scenario.
 */

 if (bp->rx_ring_size)
  ering->rx_pending = bp->rx_ring_size;
 else if (BNX2X_NUM_RX_QUEUES(bp))
  ering->rx_pending = MAX_RX_AVAIL / BNX2X_NUM_RX_QUEUES(bp);
 else
  ering->rx_pending = MAX_RX_AVAIL;

 ering->tx_max_pending = IS_MF_FCOE_AFEX(bp) ? 0 : MAX_TX_AVAIL;
 ering->tx_pending = bp->tx_ring_size;
}

static int bnx2x_set_ringparam(struct net_device *dev,
          struct ethtool_ringparam *ering,
          struct kernel_ethtool_ringparam *kernel_ering,
          struct netlink_ext_ack *extack)
{
 struct bnx2x *bp = netdev_priv(dev);

 DP(BNX2X_MSG_ETHTOOL,
    "set ring params command parameters: rx_pending = %d, tx_pending = %d\n",
    ering->rx_pending, ering->tx_pending);

 if (pci_num_vf(bp->pdev)) {
  DP(BNX2X_MSG_IOV,
     "VFs are enabled, can not change ring parameters\n");
  return -EPERM;
 }

 if (bp->recovery_state != BNX2X_RECOVERY_DONE) {
  DP(BNX2X_MSG_ETHTOOL,
     "Handling parity error recovery. Try again later\n");
  return -EAGAIN;
 }

 if ((ering->rx_pending > MAX_RX_AVAIL) ||
     (ering->rx_pending < (bp->disable_tpa ? MIN_RX_SIZE_NONTPA :
          MIN_RX_SIZE_TPA)) ||
     (ering->tx_pending > (IS_MF_STORAGE_ONLY(bp) ? 0 : MAX_TX_AVAIL)) ||
     (ering->tx_pending <= MAX_SKB_FRAGS + 4)) {
  DP(BNX2X_MSG_ETHTOOL, "Command parameters not supported\n");
  return -EINVAL;
 }

 bp->rx_ring_size = ering->rx_pending;
 bp->tx_ring_size = ering->tx_pending;

 return bnx2x_reload_if_running(dev);
}

static void bnx2x_get_pauseparam(struct net_device *dev,
     struct ethtool_pauseparam *epause)
{
 struct bnx2x *bp = netdev_priv(dev);
 int cfg_idx = bnx2x_get_link_cfg_idx(bp);
 int cfg_reg;

 epause->autoneg = (bp->link_params.req_flow_ctrl[cfg_idx] ==
      BNX2X_FLOW_CTRL_AUTO);

 if (!epause->autoneg)
  cfg_reg = bp->link_params.req_flow_ctrl[cfg_idx];
 else
  cfg_reg = bp->link_params.req_fc_auto_adv;

 epause->rx_pause = ((cfg_reg & BNX2X_FLOW_CTRL_RX) ==
       BNX2X_FLOW_CTRL_RX);
 epause->tx_pause = ((cfg_reg & BNX2X_FLOW_CTRL_TX) ==
       BNX2X_FLOW_CTRL_TX);

 DP(BNX2X_MSG_ETHTOOL, "ethtool_pauseparam: cmd %d\n"
    " autoneg %d rx_pause %d tx_pause %d\n",
    epause->cmd, epause->autoneg, epause->rx_pause, epause->tx_pause);
}

static int bnx2x_set_pauseparam(struct net_device *dev,
    struct ethtool_pauseparam *epause)
{
 struct bnx2x *bp = netdev_priv(dev);
 u32 cfg_idx = bnx2x_get_link_cfg_idx(bp);
 if (IS_MF(bp))
  return 0;

 DP(BNX2X_MSG_ETHTOOL, "ethtool_pauseparam: cmd %d\n"
    " autoneg %d rx_pause %d tx_pause %d\n",
    epause->cmd, epause->autoneg, epause->rx_pause, epause->tx_pause);

 bp->link_params.req_flow_ctrl[cfg_idx] = BNX2X_FLOW_CTRL_AUTO;

 if (epause->rx_pause)
  bp->link_params.req_flow_ctrl[cfg_idx] |= BNX2X_FLOW_CTRL_RX;

 if (epause->tx_pause)
  bp->link_params.req_flow_ctrl[cfg_idx] |= BNX2X_FLOW_CTRL_TX;

 if (bp->link_params.req_flow_ctrl[cfg_idx] == BNX2X_FLOW_CTRL_AUTO)
  bp->link_params.req_flow_ctrl[cfg_idx] = BNX2X_FLOW_CTRL_NONE;

 if (epause->autoneg) {
  if (!(bp->port.supported[cfg_idx] & SUPPORTED_Autoneg)) {
   DP(BNX2X_MSG_ETHTOOL, "autoneg not supported\n");
   return -EINVAL;
  }

  if (bp->link_params.req_line_speed[cfg_idx] == SPEED_AUTO_NEG) {
   bp->link_params.req_flow_ctrl[cfg_idx] =
    BNX2X_FLOW_CTRL_AUTO;
  }
  bp->link_params.req_fc_auto_adv = 0;
  if (epause->rx_pause)
   bp->link_params.req_fc_auto_adv |= BNX2X_FLOW_CTRL_RX;

  if (epause->tx_pause)
   bp->link_params.req_fc_auto_adv |= BNX2X_FLOW_CTRL_TX;

  if (!bp->link_params.req_fc_auto_adv)
   bp->link_params.req_fc_auto_adv |= BNX2X_FLOW_CTRL_NONE;
 }

 DP(BNX2X_MSG_ETHTOOL,
    "req_flow_ctrl 0x%x\n", bp->link_params.req_flow_ctrl[cfg_idx]);

 if (netif_running(dev)) {
  bnx2x_stats_handle(bp, STATS_EVENT_STOP);
  bnx2x_force_link_reset(bp);
  bnx2x_link_set(bp);
 }

 return 0;
}

static const char bnx2x_tests_str_arr[BNX2X_NUM_TESTS_SF][ETH_GSTRING_LEN] = {
 "register_test (offline) ",
 "memory_test (offline) ",
 "int_loopback_test (offline)",
 "ext_loopback_test (offline)",
 "nvram_test (online) ",
 "interrupt_test (online) ",
 "link_test (online) "
};

enum {
 BNX2X_PRI_FLAG_ISCSI,
 BNX2X_PRI_FLAG_FCOE,
 BNX2X_PRI_FLAG_STORAGE,
 BNX2X_PRI_FLAG_LEN,
};

static const char bnx2x_private_arr[BNX2X_PRI_FLAG_LEN][ETH_GSTRING_LEN] = {
 "iSCSI offload support",
 "FCoE offload support",
 "Storage only interface"
};

static void bnx2x_eee_to_linkmode(unsigned long *mode, u32 eee_adv)
{
 if (eee_adv & SHMEM_EEE_100M_ADV)
  linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, mode);
 if (eee_adv & SHMEM_EEE_1G_ADV)
  linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, mode);
 if (eee_adv & SHMEM_EEE_10G_ADV)
  linkmode_set_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT, mode);
}

static u32 bnx2x_linkmode_to_eee(const unsigned long *mode, u32 shift)
{
 u32 eee_adv = 0;

 if (linkmode_test_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, mode))
  eee_adv |= SHMEM_EEE_100M_ADV;
 if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, mode))
  eee_adv |= SHMEM_EEE_1G_ADV;
 if (linkmode_test_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT, mode))
  eee_adv |= SHMEM_EEE_10G_ADV;

 return eee_adv << shift;
}

static int bnx2x_get_eee(struct net_device *dev, struct ethtool_keee *edata)
{
 struct bnx2x *bp = netdev_priv(dev);
 u32 eee_cfg;

 if (!SHMEM2_HAS(bp, eee_status[BP_PORT(bp)])) {
  DP(BNX2X_MSG_ETHTOOL, "BC Version does not support EEE\n");
  return -EOPNOTSUPP;
 }

 eee_cfg = bp->link_vars.eee_status;

 bnx2x_eee_to_linkmode(edata->supported,
         (eee_cfg & SHMEM_EEE_SUPPORTED_MASK) >>
         SHMEM_EEE_SUPPORTED_SHIFT);

 bnx2x_eee_to_linkmode(edata->advertised,
         (eee_cfg & SHMEM_EEE_ADV_STATUS_MASK) >>
         SHMEM_EEE_ADV_STATUS_SHIFT);

 bnx2x_eee_to_linkmode(edata->lp_advertised,
         (eee_cfg & SHMEM_EEE_LP_ADV_STATUS_MASK) >>
         SHMEM_EEE_LP_ADV_STATUS_SHIFT);

 /* SHMEM value is in 16u units --> Convert to 1u units. */
 edata->tx_lpi_timer = (eee_cfg & SHMEM_EEE_TIMER_MASK) << 4;

 edata->eee_enabled    = (eee_cfg & SHMEM_EEE_REQUESTED_BIT) ? 1 : 0;
 edata->eee_active     = (eee_cfg & SHMEM_EEE_ACTIVE_BIT) ? 1 : 0;
 edata->tx_lpi_enabled = (eee_cfg & SHMEM_EEE_LPI_REQUESTED_BIT) ? 1 : 0;

 return 0;
}

static int bnx2x_set_eee(struct net_device *dev, struct ethtool_keee *edata)
{
 struct bnx2x *bp = netdev_priv(dev);
 u32 eee_cfg;
 u32 advertised;

 if (IS_MF(bp))
  return 0;

 if (!SHMEM2_HAS(bp, eee_status[BP_PORT(bp)])) {
  DP(BNX2X_MSG_ETHTOOL, "BC Version does not support EEE\n");
  return -EOPNOTSUPP;
 }

 eee_cfg = bp->link_vars.eee_status;

 if (!(eee_cfg & SHMEM_EEE_SUPPORTED_MASK)) {
  DP(BNX2X_MSG_ETHTOOL, "Board does not support EEE!\n");
  return -EOPNOTSUPP;
 }

 advertised = bnx2x_linkmode_to_eee(edata->advertised,
        SHMEM_EEE_ADV_STATUS_SHIFT);
 if ((advertised != (eee_cfg & SHMEM_EEE_ADV_STATUS_MASK))) {
  DP(BNX2X_MSG_ETHTOOL,
     "Direct manipulation of EEE advertisement is not supported\n");
  return -EINVAL;
 }

 if (edata->tx_lpi_timer > EEE_MODE_TIMER_MASK) {
  DP(BNX2X_MSG_ETHTOOL,
     "Maximal Tx Lpi timer supported is %x(u)\n",
     EEE_MODE_TIMER_MASK);
  return -EINVAL;
 }
 if (edata->tx_lpi_enabled &&
     (edata->tx_lpi_timer < EEE_MODE_NVRAM_AGGRESSIVE_TIME)) {
  DP(BNX2X_MSG_ETHTOOL,
     "Minimal Tx Lpi timer supported is %d(u)\n",
     EEE_MODE_NVRAM_AGGRESSIVE_TIME);
  return -EINVAL;
 }

 /* All is well; Apply changes*/
 if (edata->eee_enabled)
  bp->link_params.eee_mode |= EEE_MODE_ADV_LPI;
 else
  bp->link_params.eee_mode &= ~EEE_MODE_ADV_LPI;

 if (edata->tx_lpi_enabled)
  bp->link_params.eee_mode |= EEE_MODE_ENABLE_LPI;
 else
  bp->link_params.eee_mode &= ~EEE_MODE_ENABLE_LPI;

 bp->link_params.eee_mode &= ~EEE_MODE_TIMER_MASK;
 bp->link_params.eee_mode |= (edata->tx_lpi_timer &
        EEE_MODE_TIMER_MASK) |
        EEE_MODE_OVERRIDE_NVRAM |
        EEE_MODE_OUTPUT_TIME;

 /* Restart link to propagate changes */
 if (netif_running(dev)) {
  bnx2x_stats_handle(bp, STATS_EVENT_STOP);
  bnx2x_force_link_reset(bp);
  bnx2x_link_set(bp);
 }

 return 0;
}

enum {
 BNX2X_CHIP_E1_OFST = 0,
 BNX2X_CHIP_E1H_OFST,
 BNX2X_CHIP_E2_OFST,
 BNX2X_CHIP_E3_OFST,
 BNX2X_CHIP_E3B0_OFST,
 BNX2X_CHIP_MAX_OFST
};

#define BNX2X_CHIP_MASK_E1 (1 << BNX2X_CHIP_E1_OFST)
#define BNX2X_CHIP_MASK_E1H (1 << BNX2X_CHIP_E1H_OFST)
#define BNX2X_CHIP_MASK_E2 (1 << BNX2X_CHIP_E2_OFST)
#define BNX2X_CHIP_MASK_E3 (1 << BNX2X_CHIP_E3_OFST)
#define BNX2X_CHIP_MASK_E3B0 (1 << BNX2X_CHIP_E3B0_OFST)

#define BNX2X_CHIP_MASK_ALL ((1 << BNX2X_CHIP_MAX_OFST) - 1)
#define BNX2X_CHIP_MASK_E1X (BNX2X_CHIP_MASK_E1 | BNX2X_CHIP_MASK_E1H)

static int bnx2x_test_registers(struct bnx2x *bp)
{
 int idx, i, rc = -ENODEV;
 u32 wr_val = 0, hw;
 int port = BP_PORT(bp);
 static const struct {
  u32 hw;
  u32 offset0;
  u32 offset1;
  u32 mask;
 } reg_tbl[] = {
/* 0 */ { BNX2X_CHIP_MASK_ALL,
   BRB1_REG_PAUSE_LOW_THRESHOLD_0, 4, 0x000003ff },
  { BNX2X_CHIP_MASK_ALL,
   DORQ_REG_DB_ADDR0,  4, 0xffffffff },
  { BNX2X_CHIP_MASK_E1X,
   HC_REG_AGG_INT_0,  4, 0x000003ff },
  { BNX2X_CHIP_MASK_ALL,
   PBF_REG_MAC_IF0_ENABLE,  4, 0x00000001 },
  { BNX2X_CHIP_MASK_E1X | BNX2X_CHIP_MASK_E2 | BNX2X_CHIP_MASK_E3,
   PBF_REG_P0_INIT_CRD,  4, 0x000007ff },
  { BNX2X_CHIP_MASK_E3B0,
   PBF_REG_INIT_CRD_Q0,  4, 0x000007ff },
  { BNX2X_CHIP_MASK_ALL,
   PRS_REG_CID_PORT_0,  4, 0x00ffffff },
  { BNX2X_CHIP_MASK_ALL,
   PXP2_REG_PSWRQ_CDU0_L2P, 4, 0x000fffff },
  { BNX2X_CHIP_MASK_ALL,
   PXP2_REG_RQ_CDU0_EFIRST_MEM_ADDR, 8, 0x0003ffff },
  { BNX2X_CHIP_MASK_ALL,
   PXP2_REG_PSWRQ_TM0_L2P,  4, 0x000fffff },
/* 10 */ { BNX2X_CHIP_MASK_ALL,
   PXP2_REG_RQ_USDM0_EFIRST_MEM_ADDR, 8, 0x0003ffff },
  { BNX2X_CHIP_MASK_ALL,
   PXP2_REG_PSWRQ_TSDM0_L2P, 4, 0x000fffff },
  { BNX2X_CHIP_MASK_ALL,
   QM_REG_CONNNUM_0,  4, 0x000fffff },
  { BNX2X_CHIP_MASK_ALL,
   TM_REG_LIN0_MAX_ACTIVE_CID, 4, 0x0003ffff },
  { BNX2X_CHIP_MASK_ALL,
   SRC_REG_KEYRSS0_0,  40, 0xffffffff },
  { BNX2X_CHIP_MASK_ALL,
   SRC_REG_KEYRSS0_7,  40, 0xffffffff },
  { BNX2X_CHIP_MASK_ALL,
   XCM_REG_WU_DA_SET_TMR_CNT_FLG_CMD00, 4, 0x00000001 },
  { BNX2X_CHIP_MASK_ALL,
   XCM_REG_WU_DA_CNT_CMD00, 4, 0x00000003 },
  { BNX2X_CHIP_MASK_ALL,
   XCM_REG_GLB_DEL_ACK_MAX_CNT_0, 4, 0x000000ff },
  { BNX2X_CHIP_MASK_ALL,
   NIG_REG_LLH0_T_BIT,  4, 0x00000001 },
/* 20 */ { BNX2X_CHIP_MASK_E1X | BNX2X_CHIP_MASK_E2,
   NIG_REG_EMAC0_IN_EN,  4, 0x00000001 },
  { BNX2X_CHIP_MASK_E1X | BNX2X_CHIP_MASK_E2,
   NIG_REG_BMAC0_IN_EN,  4, 0x00000001 },
  { BNX2X_CHIP_MASK_ALL,
   NIG_REG_XCM0_OUT_EN,  4, 0x00000001 },
  { BNX2X_CHIP_MASK_ALL,
   NIG_REG_BRB0_OUT_EN,  4, 0x00000001 },
  { BNX2X_CHIP_MASK_ALL,
   NIG_REG_LLH0_XCM_MASK,  4, 0x00000007 },
--> --------------------

--> maximum size reached

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

Messung V0.5
C=96 H=97 G=96

¤ Dauer der Verarbeitung: 0.24 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.