Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  qlcnic_83xx_hw.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0-only
/*
 * QLogic qlcnic NIC Driver
 * Copyright (c) 2009-2013 QLogic Corporation
 */


#include <linux/if_vlan.h>
#include <linux/ipv6.h>
#include <linux/ethtool.h>
#include <linux/interrupt.h>

#include "qlcnic.h"
#include "qlcnic_sriov.h"

static void __qlcnic_83xx_process_aen(struct qlcnic_adapter *);
static int qlcnic_83xx_clear_lb_mode(struct qlcnic_adapter *, u8);
static void qlcnic_83xx_configure_mac(struct qlcnic_adapter *, u8 *, u8,
          struct qlcnic_cmd_args *);
static int qlcnic_83xx_get_port_config(struct qlcnic_adapter *);
static irqreturn_t qlcnic_83xx_handle_aen(intvoid *);
static pci_ers_result_t qlcnic_83xx_io_error_detected(struct pci_dev *,
            pci_channel_state_t);
static int qlcnic_83xx_set_port_config(struct qlcnic_adapter *);
static pci_ers_result_t qlcnic_83xx_io_slot_reset(struct pci_dev *);
static void qlcnic_83xx_io_resume(struct pci_dev *);
static int qlcnic_83xx_set_lb_mode(struct qlcnic_adapter *, u8);
static void qlcnic_83xx_set_mac_filter_count(struct qlcnic_adapter *);
static int qlcnic_83xx_resume(struct qlcnic_adapter *);
static int qlcnic_83xx_shutdown(struct pci_dev *);
static void qlcnic_83xx_get_beacon_state(struct qlcnic_adapter *);

#define RSS_HASHTYPE_IP_TCP  0x3
#define QLC_83XX_FW_MBX_CMD  0
#define QLC_SKIP_INACTIVE_PCI_REGS 7
#define QLC_MAX_LEGACY_FUNC_SUPP 8

/* 83xx Module type */
#define QLC_83XX_MODULE_FIBRE_10GBASE_LRM 0x1 /* 10GBase-LRM */
#define QLC_83XX_MODULE_FIBRE_10GBASE_LR 0x2 /* 10GBase-LR */
#define QLC_83XX_MODULE_FIBRE_10GBASE_SR 0x3 /* 10GBase-SR */
#define QLC_83XX_MODULE_DA_10GE_PASSIVE_CP 0x4 /* 10GE passive
     * copper(compliant)
     */

#define QLC_83XX_MODULE_DA_10GE_ACTIVE_CP 0x5 /* 10GE active limiting
     * copper(compliant)
     */

#define QLC_83XX_MODULE_DA_10GE_LEGACY_CP 0x6 /* 10GE passive copper
     * (legacy, best effort)
     */

#define QLC_83XX_MODULE_FIBRE_1000BASE_SX 0x7 /* 1000Base-SX */
#define QLC_83XX_MODULE_FIBRE_1000BASE_LX 0x8 /* 1000Base-LX */
#define QLC_83XX_MODULE_FIBRE_1000BASE_CX 0x9 /* 1000Base-CX */
#define QLC_83XX_MODULE_TP_1000BASE_T  0xa /* 1000Base-T*/
#define QLC_83XX_MODULE_DA_1GE_PASSIVE_CP 0xb /* 1GE passive copper
     * (legacy, best effort)
     */

#define QLC_83XX_MODULE_UNKNOWN   0xf /* Unknown module type */

/* Port types */
#define QLC_83XX_10_CAPABLE  BIT_8
#define QLC_83XX_100_CAPABLE  BIT_9
#define QLC_83XX_1G_CAPABLE  BIT_10
#define QLC_83XX_10G_CAPABLE  BIT_11
#define QLC_83XX_AUTONEG_ENABLE  BIT_15

static const struct qlcnic_mailbox_metadata qlcnic_83xx_mbx_tbl[] = {
 {QLCNIC_CMD_CONFIGURE_IP_ADDR, 6, 1},
 {QLCNIC_CMD_CONFIG_INTRPT, 18, 34},
 {QLCNIC_CMD_CREATE_RX_CTX, 136, 27},
 {QLCNIC_CMD_DESTROY_RX_CTX, 2, 1},
 {QLCNIC_CMD_CREATE_TX_CTX, 54, 18},
 {QLCNIC_CMD_DESTROY_TX_CTX, 2, 1},
 {QLCNIC_CMD_CONFIGURE_MAC_LEARNING, 2, 1},
 {QLCNIC_CMD_INTRPT_TEST, 22, 12},
 {QLCNIC_CMD_SET_MTU, 3, 1},
 {QLCNIC_CMD_READ_PHY, 4, 2},
 {QLCNIC_CMD_WRITE_PHY, 5, 1},
 {QLCNIC_CMD_READ_HW_REG, 4, 1},
 {QLCNIC_CMD_GET_FLOW_CTL, 4, 2},
 {QLCNIC_CMD_SET_FLOW_CTL, 4, 1},
 {QLCNIC_CMD_READ_MAX_MTU, 4, 2},
 {QLCNIC_CMD_READ_MAX_LRO, 4, 2},
 {QLCNIC_CMD_MAC_ADDRESS, 4, 3},
 {QLCNIC_CMD_GET_PCI_INFO, 1, 129},
 {QLCNIC_CMD_GET_NIC_INFO, 2, 19},
 {QLCNIC_CMD_SET_NIC_INFO, 32, 1},
 {QLCNIC_CMD_GET_ESWITCH_CAPABILITY, 4, 3},
 {QLCNIC_CMD_TOGGLE_ESWITCH, 4, 1},
 {QLCNIC_CMD_GET_ESWITCH_STATUS, 4, 3},
 {QLCNIC_CMD_SET_PORTMIRRORING, 4, 1},
 {QLCNIC_CMD_CONFIGURE_ESWITCH, 4, 1},
 {QLCNIC_CMD_GET_ESWITCH_PORT_CONFIG, 4, 3},
 {QLCNIC_CMD_GET_ESWITCH_STATS, 5, 1},
 {QLCNIC_CMD_CONFIG_PORT, 4, 1},
 {QLCNIC_CMD_TEMP_SIZE, 1, 4},
 {QLCNIC_CMD_GET_TEMP_HDR, 5, 5},
 {QLCNIC_CMD_GET_LINK_EVENT, 2, 1},
 {QLCNIC_CMD_CONFIG_MAC_VLAN, 4, 3},
 {QLCNIC_CMD_CONFIG_INTR_COAL, 6, 1},
 {QLCNIC_CMD_CONFIGURE_RSS, 14, 1},
 {QLCNIC_CMD_CONFIGURE_LED, 2, 1},
 {QLCNIC_CMD_CONFIGURE_MAC_RX_MODE, 2, 1},
 {QLCNIC_CMD_CONFIGURE_HW_LRO, 2, 1},
 {QLCNIC_CMD_GET_STATISTICS, 2, 80},
 {QLCNIC_CMD_SET_PORT_CONFIG, 2, 1},
 {QLCNIC_CMD_GET_PORT_CONFIG, 2, 2},
 {QLCNIC_CMD_GET_LINK_STATUS, 2, 4},
 {QLCNIC_CMD_IDC_ACK, 5, 1},
 {QLCNIC_CMD_INIT_NIC_FUNC, 3, 1},
 {QLCNIC_CMD_STOP_NIC_FUNC, 2, 1},
 {QLCNIC_CMD_SET_LED_CONFIG, 5, 1},
 {QLCNIC_CMD_GET_LED_CONFIG, 1, 5},
 {QLCNIC_CMD_83XX_SET_DRV_VER, 4, 1},
 {QLCNIC_CMD_ADD_RCV_RINGS, 130, 26},
 {QLCNIC_CMD_CONFIG_VPORT, 4, 4},
 {QLCNIC_CMD_BC_EVENT_SETUP, 2, 1},
 {QLCNIC_CMD_DCB_QUERY_CAP, 1, 2},
 {QLCNIC_CMD_DCB_QUERY_PARAM, 1, 50},
 {QLCNIC_CMD_SET_INGRESS_ENCAP, 2, 1},
 {QLCNIC_CMD_83XX_EXTEND_ISCSI_DUMP_CAP, 4, 1},
};

const u32 qlcnic_83xx_ext_reg_tbl[] = {
 0x38CC,  /* Global Reset */
 0x38F0,  /* Wildcard */
 0x38FC,  /* Informant */
 0x3038,  /* Host MBX ctrl */
 0x303C,  /* FW MBX ctrl */
 0x355C,  /* BOOT LOADER ADDRESS REG */
 0x3560,  /* BOOT LOADER SIZE REG */
 0x3564,  /* FW IMAGE ADDR REG */
 0x1000,  /* MBX intr enable */
 0x1200,  /* Default Intr mask */
 0x1204,  /* Default Interrupt ID */
 0x3780,  /* QLC_83XX_IDC_MAJ_VERSION */
 0x3784,  /* QLC_83XX_IDC_DEV_STATE */
 0x3788,  /* QLC_83XX_IDC_DRV_PRESENCE */
 0x378C,  /* QLC_83XX_IDC_DRV_ACK */
 0x3790,  /* QLC_83XX_IDC_CTRL */
 0x3794,  /* QLC_83XX_IDC_DRV_AUDIT */
 0x3798,  /* QLC_83XX_IDC_MIN_VERSION */
 0x379C,  /* QLC_83XX_RECOVER_DRV_LOCK */
 0x37A0,  /* QLC_83XX_IDC_PF_0 */
 0x37A4,  /* QLC_83XX_IDC_PF_1 */
 0x37A8,  /* QLC_83XX_IDC_PF_2 */
 0x37AC,  /* QLC_83XX_IDC_PF_3 */
 0x37B0,  /* QLC_83XX_IDC_PF_4 */
 0x37B4,  /* QLC_83XX_IDC_PF_5 */
 0x37B8,  /* QLC_83XX_IDC_PF_6 */
 0x37BC,  /* QLC_83XX_IDC_PF_7 */
 0x37C0,  /* QLC_83XX_IDC_PF_8 */
 0x37C4,  /* QLC_83XX_IDC_PF_9 */
 0x37C8,  /* QLC_83XX_IDC_PF_10 */
 0x37CC,  /* QLC_83XX_IDC_PF_11 */
 0x37D0,  /* QLC_83XX_IDC_PF_12 */
 0x37D4,  /* QLC_83XX_IDC_PF_13 */
 0x37D8,  /* QLC_83XX_IDC_PF_14 */
 0x37DC,  /* QLC_83XX_IDC_PF_15 */
 0x37E0,  /* QLC_83XX_IDC_DEV_PARTITION_INFO_1 */
 0x37E4,  /* QLC_83XX_IDC_DEV_PARTITION_INFO_2 */
 0x37F0,  /* QLC_83XX_DRV_OP_MODE */
 0x37F4,  /* QLC_83XX_VNIC_STATE */
 0x3868,  /* QLC_83XX_DRV_LOCK */
 0x386C,  /* QLC_83XX_DRV_UNLOCK */
 0x3504,  /* QLC_83XX_DRV_LOCK_ID */
 0x34A4,  /* QLC_83XX_ASIC_TEMP */
};

const u32 qlcnic_83xx_reg_tbl[] = {
 0x34A8,  /* PEG_HALT_STAT1 */
 0x34AC,  /* PEG_HALT_STAT2 */
 0x34B0,  /* FW_HEARTBEAT */
 0x3500,  /* FLASH LOCK_ID */
 0x3528,  /* FW_CAPABILITIES */
 0x3538,  /* Driver active, DRV_REG0 */
 0x3540,  /* Device state, DRV_REG1 */
 0x3544,  /* Driver state, DRV_REG2 */
 0x3548,  /* Driver scratch, DRV_REG3 */
 0x354C,  /* Device partition info, DRV_REG4 */
 0x3524,  /* Driver IDC ver, DRV_REG5 */
 0x3550,  /* FW_VER_MAJOR */
 0x3554,  /* FW_VER_MINOR */
 0x3558,  /* FW_VER_SUB */
 0x359C,  /* NPAR STATE */
 0x35FC,  /* FW_IMG_VALID */
 0x3650,  /* CMD_PEG_STATE */
 0x373C,  /* RCV_PEG_STATE */
 0x37B4,  /* ASIC TEMP */
 0x356C,  /* FW API */
 0x3570,  /* DRV OP MODE */
 0x3850,  /* FLASH LOCK */
 0x3854,  /* FLASH UNLOCK */
};

static struct qlcnic_hardware_ops qlcnic_83xx_hw_ops = {
 .read_crb   = qlcnic_83xx_read_crb,
 .write_crb   = qlcnic_83xx_write_crb,
 .read_reg   = qlcnic_83xx_rd_reg_indirect,
 .write_reg   = qlcnic_83xx_wrt_reg_indirect,
 .get_mac_address  = qlcnic_83xx_get_mac_address,
 .setup_intr   = qlcnic_83xx_setup_intr,
 .alloc_mbx_args   = qlcnic_83xx_alloc_mbx_args,
 .mbx_cmd   = qlcnic_83xx_issue_cmd,
 .get_func_no   = qlcnic_83xx_get_func_no,
 .api_lock   = qlcnic_83xx_cam_lock,
 .api_unlock   = qlcnic_83xx_cam_unlock,
 .add_sysfs   = qlcnic_83xx_add_sysfs,
 .remove_sysfs   = qlcnic_83xx_remove_sysfs,
 .process_lb_rcv_ring_diag = qlcnic_83xx_process_rcv_ring_diag,
 .create_rx_ctx   = qlcnic_83xx_create_rx_ctx,
 .create_tx_ctx   = qlcnic_83xx_create_tx_ctx,
 .del_rx_ctx   = qlcnic_83xx_del_rx_ctx,
 .del_tx_ctx   = qlcnic_83xx_del_tx_ctx,
 .setup_link_event  = qlcnic_83xx_setup_link_event,
 .get_nic_info   = qlcnic_83xx_get_nic_info,
 .get_pci_info   = qlcnic_83xx_get_pci_info,
 .set_nic_info   = qlcnic_83xx_set_nic_info,
 .change_macvlan   = qlcnic_83xx_sre_macaddr_change,
 .napi_enable   = qlcnic_83xx_napi_enable,
 .napi_disable   = qlcnic_83xx_napi_disable,
 .config_intr_coal  = qlcnic_83xx_config_intr_coal,
 .config_rss   = qlcnic_83xx_config_rss,
 .config_hw_lro   = qlcnic_83xx_config_hw_lro,
 .config_promisc_mode  = qlcnic_83xx_nic_set_promisc,
 .change_l2_filter  = qlcnic_83xx_change_l2_filter,
 .get_board_info   = qlcnic_83xx_get_port_info,
 .set_mac_filter_count  = qlcnic_83xx_set_mac_filter_count,
 .free_mac_list   = qlcnic_82xx_free_mac_list,
 .io_error_detected  = qlcnic_83xx_io_error_detected,
 .io_slot_reset   = qlcnic_83xx_io_slot_reset,
 .io_resume   = qlcnic_83xx_io_resume,
 .get_beacon_state  = qlcnic_83xx_get_beacon_state,
 .enable_sds_intr  = qlcnic_83xx_enable_sds_intr,
 .disable_sds_intr  = qlcnic_83xx_disable_sds_intr,
 .enable_tx_intr   = qlcnic_83xx_enable_tx_intr,
 .disable_tx_intr  = qlcnic_83xx_disable_tx_intr,
 .get_saved_state  = qlcnic_83xx_get_saved_state,
 .set_saved_state  = qlcnic_83xx_set_saved_state,
 .cache_tmpl_hdr_values  = qlcnic_83xx_cache_tmpl_hdr_values,
 .get_cap_size   = qlcnic_83xx_get_cap_size,
 .set_sys_info   = qlcnic_83xx_set_sys_info,
 .store_cap_mask   = qlcnic_83xx_store_cap_mask,
 .encap_rx_offload  = qlcnic_83xx_encap_rx_offload,
 .encap_tx_offload  = qlcnic_83xx_encap_tx_offload,
};

static struct qlcnic_nic_template qlcnic_83xx_ops = {
 .config_bridged_mode = qlcnic_config_bridged_mode,
 .config_led  = qlcnic_config_led,
 .request_reset          = qlcnic_83xx_idc_request_reset,
 .cancel_idc_work        = qlcnic_83xx_idc_exit,
 .napi_add  = qlcnic_83xx_napi_add,
 .napi_del  = qlcnic_83xx_napi_del,
 .config_ipaddr  = qlcnic_83xx_config_ipaddr,
 .clear_legacy_intr = qlcnic_83xx_clear_legacy_intr,
 .shutdown  = qlcnic_83xx_shutdown,
 .resume   = qlcnic_83xx_resume,
};

void qlcnic_83xx_register_map(struct qlcnic_hardware_context *ahw)
{
 ahw->hw_ops  = &qlcnic_83xx_hw_ops;
 ahw->reg_tbl  = (u32 *)qlcnic_83xx_reg_tbl;
 ahw->ext_reg_tbl = (u32 *)qlcnic_83xx_ext_reg_tbl;
}

int qlcnic_83xx_get_fw_version(struct qlcnic_adapter *adapter)
{
 u32 fw_major, fw_minor, fw_build;
 struct pci_dev *pdev = adapter->pdev;

 fw_major = QLC_SHARED_REG_RD32(adapter, QLCNIC_FW_VERSION_MAJOR);
 fw_minor = QLC_SHARED_REG_RD32(adapter, QLCNIC_FW_VERSION_MINOR);
 fw_build = QLC_SHARED_REG_RD32(adapter, QLCNIC_FW_VERSION_SUB);
 adapter->fw_version = QLCNIC_VERSION_CODE(fw_major, fw_minor, fw_build);

 dev_info(&pdev->dev, "Driver v%s, firmware version %d.%d.%d\n",
   QLCNIC_LINUX_VERSIONID, fw_major, fw_minor, fw_build);

 return adapter->fw_version;
}

static int __qlcnic_set_win_base(struct qlcnic_adapter *adapter, u32 addr)
{
 void __iomem *base;
 u32 val;

 base = adapter->ahw->pci_base0 +
        QLC_83XX_CRB_WIN_FUNC(adapter->ahw->pci_func);
 writel(addr, base);
 val = readl(base);
 if (val != addr)
  return -EIO;

 return 0;
}

int qlcnic_83xx_rd_reg_indirect(struct qlcnic_adapter *adapter, ulong addr,
    int *err)
{
 struct qlcnic_hardware_context *ahw = adapter->ahw;

 *err = __qlcnic_set_win_base(adapter, (u32) addr);
 if (!*err) {
  return QLCRDX(ahw, QLCNIC_WILDCARD);
 } else {
  dev_err(&adapter->pdev->dev,
   "%s failed, addr = 0x%lx\n", __func__, addr);
  return -EIO;
 }
}

int qlcnic_83xx_wrt_reg_indirect(struct qlcnic_adapter *adapter, ulong addr,
     u32 data)
{
 int err;
 struct qlcnic_hardware_context *ahw = adapter->ahw;

 err = __qlcnic_set_win_base(adapter, (u32) addr);
 if (!err) {
  QLCWRX(ahw, QLCNIC_WILDCARD, data);
  return 0;
 } else {
  dev_err(&adapter->pdev->dev,
   "%s failed, addr = 0x%x data = 0x%x\n",
   __func__, (int)addr, data);
  return err;
 }
}

static void qlcnic_83xx_enable_legacy(struct qlcnic_adapter *adapter)
{
 struct qlcnic_hardware_context *ahw = adapter->ahw;

 /* MSI-X enablement failed, use legacy interrupt */
 adapter->tgt_status_reg = ahw->pci_base0 + QLC_83XX_INTX_PTR;
 adapter->tgt_mask_reg = ahw->pci_base0 + QLC_83XX_INTX_MASK;
 adapter->isr_int_vec = ahw->pci_base0 + QLC_83XX_INTX_TRGR;
 adapter->msix_entries[0].vector = adapter->pdev->irq;
 dev_info(&adapter->pdev->dev, "using legacy interrupt\n");
}

static int qlcnic_83xx_calculate_msix_vector(struct qlcnic_adapter *adapter)
{
 int num_msix;

 num_msix = adapter->drv_sds_rings;

 /* account for AEN interrupt MSI-X based interrupts */
 num_msix += 1;

 if (!(adapter->flags & QLCNIC_TX_INTR_SHARED))
  num_msix += adapter->drv_tx_rings;

 return num_msix;
}

int qlcnic_83xx_setup_intr(struct qlcnic_adapter *adapter)
{
 struct qlcnic_hardware_context *ahw = adapter->ahw;
 int err, i, num_msix;

 if (adapter->flags & QLCNIC_TSS_RSS) {
  err = qlcnic_setup_tss_rss_intr(adapter);
  if (err < 0)
   return err;
  num_msix = ahw->num_msix;
 } else {
  num_msix = qlcnic_83xx_calculate_msix_vector(adapter);

  err = qlcnic_enable_msix(adapter, num_msix);
  if (err == -ENOMEM)
   return err;

  if (adapter->flags & QLCNIC_MSIX_ENABLED) {
   num_msix = ahw->num_msix;
  } else {
   if (qlcnic_sriov_vf_check(adapter))
    return -EINVAL;
   num_msix = 1;
   adapter->drv_sds_rings = QLCNIC_SINGLE_RING;
   adapter->drv_tx_rings = QLCNIC_SINGLE_RING;
  }
 }

 /* setup interrupt mapping table for fw */
 ahw->intr_tbl =
  vzalloc(array_size(num_msix,
       sizeof(struct qlcnic_intrpt_config)));
 if (!ahw->intr_tbl)
  return -ENOMEM;

 if (!(adapter->flags & QLCNIC_MSIX_ENABLED)) {
  if (adapter->ahw->pci_func >= QLC_MAX_LEGACY_FUNC_SUPP) {
   dev_err(&adapter->pdev->dev, "PCI function number 8 and higher are not supported with legacy interrupt, func 0x%x\n",
    ahw->pci_func);
   return -EOPNOTSUPP;
  }

  qlcnic_83xx_enable_legacy(adapter);
 }

 for (i = 0; i < num_msix; i++) {
  if (adapter->flags & QLCNIC_MSIX_ENABLED)
   ahw->intr_tbl[i].type = QLCNIC_INTRPT_MSIX;
  else
   ahw->intr_tbl[i].type = QLCNIC_INTRPT_INTX;
  ahw->intr_tbl[i].id = i;
  ahw->intr_tbl[i].src = 0;
 }

 return 0;
}

static inline void qlcnic_83xx_clear_legacy_intr_mask(struct qlcnic_adapter *adapter)
{
 writel(0, adapter->tgt_mask_reg);
}

static inline void qlcnic_83xx_set_legacy_intr_mask(struct qlcnic_adapter *adapter)
{
 if (adapter->tgt_mask_reg)
  writel(1, adapter->tgt_mask_reg);
}

static inline void qlcnic_83xx_enable_legacy_msix_mbx_intr(struct qlcnic_adapter
          *adapter)
{
 u32 mask;

 /* Mailbox in MSI-x mode and Legacy Interrupt share the same
 * source register. We could be here before contexts are created
 * and sds_ring->crb_intr_mask has not been initialized, calculate
 * BAR offset for Interrupt Source Register
 */

 mask = QLCRDX(adapter->ahw, QLCNIC_DEF_INT_MASK);
 writel(0, adapter->ahw->pci_base0 + mask);
}

void qlcnic_83xx_disable_mbx_intr(struct qlcnic_adapter *adapter)
{
 u32 mask;

 mask = QLCRDX(adapter->ahw, QLCNIC_DEF_INT_MASK);
 writel(1, adapter->ahw->pci_base0 + mask);
 QLCWRX(adapter->ahw, QLCNIC_MBX_INTR_ENBL, 0);
}

static inline void qlcnic_83xx_get_mbx_data(struct qlcnic_adapter *adapter,
         struct qlcnic_cmd_args *cmd)
{
 int i;

 if (cmd->op_type == QLC_83XX_MBX_POST_BC_OP)
  return;

 for (i = 0; i < cmd->rsp.num; i++)
  cmd->rsp.arg[i] = readl(QLCNIC_MBX_FW(adapter->ahw, i));
}

irqreturn_t qlcnic_83xx_clear_legacy_intr(struct qlcnic_adapter *adapter)
{
 u32 intr_val;
 struct qlcnic_hardware_context *ahw = adapter->ahw;
 int retries = 0;

 intr_val = readl(adapter->tgt_status_reg);

 if (!QLC_83XX_VALID_INTX_BIT31(intr_val))
  return IRQ_NONE;

 if (QLC_83XX_INTX_FUNC(intr_val) != adapter->ahw->pci_func) {
  adapter->stats.spurious_intr++;
  return IRQ_NONE;
 }
 /* The barrier is required to ensure writes to the registers */
 wmb();

 /* clear the interrupt trigger control register */
 writel_relaxed(0, adapter->isr_int_vec);
 intr_val = readl(adapter->isr_int_vec);
 do {
  intr_val = readl(adapter->tgt_status_reg);
  if (QLC_83XX_INTX_FUNC(intr_val) != ahw->pci_func)
   break;
  retries++;
 } while (QLC_83XX_VALID_INTX_BIT30(intr_val) &&
   (retries < QLC_83XX_LEGACY_INTX_MAX_RETRY));

 return IRQ_HANDLED;
}

static inline void qlcnic_83xx_notify_mbx_response(struct qlcnic_mailbox *mbx)
{
 mbx->rsp_status = QLC_83XX_MBX_RESPONSE_ARRIVED;
 complete(&mbx->completion);
}

static void qlcnic_83xx_poll_process_aen(struct qlcnic_adapter *adapter)
{
 u32 resp, event, rsp_status = QLC_83XX_MBX_RESPONSE_ARRIVED;
 struct qlcnic_mailbox *mbx = adapter->ahw->mailbox;
 unsigned long flags;

 spin_lock_irqsave(&mbx->aen_lock, flags);
 resp = QLCRDX(adapter->ahw, QLCNIC_FW_MBX_CTRL);
 if (!(resp & QLCNIC_SET_OWNER))
  goto out;

 event = readl(QLCNIC_MBX_FW(adapter->ahw, 0));
 if (event &  QLCNIC_MBX_ASYNC_EVENT) {
  __qlcnic_83xx_process_aen(adapter);
 } else {
  if (mbx->rsp_status != rsp_status)
   qlcnic_83xx_notify_mbx_response(mbx);
 }
out:
 qlcnic_83xx_enable_legacy_msix_mbx_intr(adapter);
 spin_unlock_irqrestore(&mbx->aen_lock, flags);
}

irqreturn_t qlcnic_83xx_intr(int irq, void *data)
{
 struct qlcnic_adapter *adapter = data;
 struct qlcnic_host_sds_ring *sds_ring;
 struct qlcnic_hardware_context *ahw = adapter->ahw;

 if (qlcnic_83xx_clear_legacy_intr(adapter) == IRQ_NONE)
  return IRQ_NONE;

 qlcnic_83xx_poll_process_aen(adapter);

 if (ahw->diag_test) {
  if (ahw->diag_test == QLCNIC_INTERRUPT_TEST)
   ahw->diag_cnt++;
  qlcnic_83xx_enable_legacy_msix_mbx_intr(adapter);
  return IRQ_HANDLED;
 }

 if (!test_bit(__QLCNIC_DEV_UP, &adapter->state)) {
  qlcnic_83xx_enable_legacy_msix_mbx_intr(adapter);
 } else {
  sds_ring = &adapter->recv_ctx->sds_rings[0];
  napi_schedule(&sds_ring->napi);
 }

 return IRQ_HANDLED;
}

irqreturn_t qlcnic_83xx_tmp_intr(int irq, void *data)
{
 struct qlcnic_host_sds_ring *sds_ring = data;
 struct qlcnic_adapter *adapter = sds_ring->adapter;

 if (adapter->flags & QLCNIC_MSIX_ENABLED)
  goto done;

 if (adapter->nic_ops->clear_legacy_intr(adapter) == IRQ_NONE)
  return IRQ_NONE;

done:
 adapter->ahw->diag_cnt++;
 qlcnic_enable_sds_intr(adapter, sds_ring);

 return IRQ_HANDLED;
}

void qlcnic_83xx_free_mbx_intr(struct qlcnic_adapter *adapter)
{
 u32 num_msix;

 if (!(adapter->flags & QLCNIC_MSIX_ENABLED))
  qlcnic_83xx_set_legacy_intr_mask(adapter);

 qlcnic_83xx_disable_mbx_intr(adapter);

 if (adapter->flags & QLCNIC_MSIX_ENABLED)
  num_msix = adapter->ahw->num_msix - 1;
 else
  num_msix = 0;

 msleep(20);

 if (adapter->msix_entries) {
  synchronize_irq(adapter->msix_entries[num_msix].vector);
  free_irq(adapter->msix_entries[num_msix].vector, adapter);
 }
}

int qlcnic_83xx_setup_mbx_intr(struct qlcnic_adapter *adapter)
{
 irq_handler_t handler;
 u32 val;
 int err = 0;
 unsigned long flags = 0;

 if (!(adapter->flags & QLCNIC_MSI_ENABLED) &&
     !(adapter->flags & QLCNIC_MSIX_ENABLED))
  flags |= IRQF_SHARED;

 if (adapter->flags & QLCNIC_MSIX_ENABLED) {
  handler = qlcnic_83xx_handle_aen;
  val = adapter->msix_entries[adapter->ahw->num_msix - 1].vector;
  err = request_irq(val, handler, flags, "qlcnic-MB", adapter);
  if (err) {
   dev_err(&adapter->pdev->dev,
    "failed to register MBX interrupt\n");
   return err;
  }
 } else {
  handler = qlcnic_83xx_intr;
  val = adapter->msix_entries[0].vector;
  err = request_irq(val, handler, flags, "qlcnic", adapter);
  if (err) {
   dev_err(&adapter->pdev->dev,
    "failed to register INTx interrupt\n");
   return err;
  }
  qlcnic_83xx_clear_legacy_intr_mask(adapter);
 }

 /* Enable mailbox interrupt */
 qlcnic_83xx_enable_mbx_interrupt(adapter);

 return err;
}

void qlcnic_83xx_get_func_no(struct qlcnic_adapter *adapter)
{
 u32 val = QLCRDX(adapter->ahw, QLCNIC_INFORMANT);
 adapter->ahw->pci_func = (val >> 24) & 0xff;
}

int qlcnic_83xx_cam_lock(struct qlcnic_adapter *adapter)
{
 void __iomem *addr;
 u32 val, limit = 0;

 struct qlcnic_hardware_context *ahw = adapter->ahw;

 addr = ahw->pci_base0 + QLC_83XX_SEM_LOCK_FUNC(ahw->pci_func);
 do {
  val = readl(addr);
  if (val) {
   /* write the function number to register */
   QLC_SHARED_REG_WR32(adapter, QLCNIC_FLASH_LOCK_OWNER,
         ahw->pci_func);
   return 0;
  }
  usleep_range(1000, 2000);
 } while (++limit <= QLCNIC_PCIE_SEM_TIMEOUT);

 return -EIO;
}

void qlcnic_83xx_cam_unlock(struct qlcnic_adapter *adapter)
{
 void __iomem *addr;
 struct qlcnic_hardware_context *ahw = adapter->ahw;

 addr = ahw->pci_base0 + QLC_83XX_SEM_UNLOCK_FUNC(ahw->pci_func);
 readl(addr);
}

void qlcnic_83xx_read_crb(struct qlcnic_adapter *adapter, char *buf,
     loff_t offset, size_t size)
{
 int ret = 0;
 u32 data;

 if (qlcnic_api_lock(adapter)) {
  dev_err(&adapter->pdev->dev,
   "%s: failed to acquire lock. addr offset 0x%x\n",
   __func__, (u32)offset);
  return;
 }

 data = QLCRD32(adapter, (u32) offset, &ret);
 qlcnic_api_unlock(adapter);

 if (ret == -EIO) {
  dev_err(&adapter->pdev->dev,
   "%s: failed. addr offset 0x%x\n",
   __func__, (u32)offset);
  return;
 }
 memcpy(buf, &data, size);
}

void qlcnic_83xx_write_crb(struct qlcnic_adapter *adapter, char *buf,
      loff_t offset, size_t size)
{
 u32 data;

 memcpy(&data, buf, size);
 qlcnic_83xx_wrt_reg_indirect(adapter, (u32) offset, data);
}

int qlcnic_83xx_get_port_info(struct qlcnic_adapter *adapter)
{
 struct qlcnic_hardware_context *ahw = adapter->ahw;
 int status;

 status = qlcnic_83xx_get_port_config(adapter);
 if (status) {
  dev_err(&adapter->pdev->dev,
   "Get Port Info failed\n");
 } else {

  if (ahw->port_config & QLC_83XX_10G_CAPABLE) {
   ahw->port_type = QLCNIC_XGBE;
  } else if (ahw->port_config & QLC_83XX_10_CAPABLE ||
      ahw->port_config & QLC_83XX_100_CAPABLE ||
      ahw->port_config & QLC_83XX_1G_CAPABLE) {
   ahw->port_type = QLCNIC_GBE;
  } else {
   ahw->port_type = QLCNIC_XGBE;
  }

  if (QLC_83XX_AUTONEG(ahw->port_config))
   ahw->link_autoneg = AUTONEG_ENABLE;

 }
 return status;
}

static void qlcnic_83xx_set_mac_filter_count(struct qlcnic_adapter *adapter)
{
 struct qlcnic_hardware_context *ahw = adapter->ahw;
 u16 act_pci_fn = ahw->total_nic_func;
 u16 count;

 ahw->max_mc_count = QLC_83XX_MAX_MC_COUNT;
 if (act_pci_fn <= 2)
  count = (QLC_83XX_MAX_UC_COUNT - QLC_83XX_MAX_MC_COUNT) /
    act_pci_fn;
 else
  count = (QLC_83XX_LB_MAX_FILTERS - QLC_83XX_MAX_MC_COUNT) /
    act_pci_fn;
 ahw->max_uc_count = count;
}

void qlcnic_83xx_enable_mbx_interrupt(struct qlcnic_adapter *adapter)
{
 u32 val;

 if (adapter->flags & QLCNIC_MSIX_ENABLED)
  val = BIT_2 | ((adapter->ahw->num_msix - 1) << 8);
 else
  val = BIT_2;

 QLCWRX(adapter->ahw, QLCNIC_MBX_INTR_ENBL, val);
 qlcnic_83xx_enable_legacy_msix_mbx_intr(adapter);
}

void qlcnic_83xx_check_vf(struct qlcnic_adapter *adapter,
     const struct pci_device_id *ent)
{
 u32 op_mode, priv_level;
 struct qlcnic_hardware_context *ahw = adapter->ahw;

 ahw->fw_hal_version = 2;
 qlcnic_get_func_no(adapter);

 if (qlcnic_sriov_vf_check(adapter)) {
  qlcnic_sriov_vf_set_ops(adapter);
  return;
 }

 /* Determine function privilege level */
 op_mode = QLCRDX(adapter->ahw, QLC_83XX_DRV_OP_MODE);
 if (op_mode == QLC_83XX_DEFAULT_OPMODE)
  priv_level = QLCNIC_MGMT_FUNC;
 else
  priv_level = QLC_83XX_GET_FUNC_PRIVILEGE(op_mode,
        ahw->pci_func);

 if (priv_level == QLCNIC_NON_PRIV_FUNC) {
  ahw->op_mode = QLCNIC_NON_PRIV_FUNC;
  dev_info(&adapter->pdev->dev,
    "HAL Version: %d Non Privileged function\n",
    ahw->fw_hal_version);
  adapter->nic_ops = &qlcnic_vf_ops;
 } else {
  if (pci_find_ext_capability(adapter->pdev,
         PCI_EXT_CAP_ID_SRIOV))
   set_bit(__QLCNIC_SRIOV_CAPABLE, &adapter->state);
  adapter->nic_ops = &qlcnic_83xx_ops;
 }
}

static void qlcnic_83xx_handle_link_aen(struct qlcnic_adapter *adapter,
     u32 data[]);
static void qlcnic_83xx_handle_idc_comp_aen(struct qlcnic_adapter *adapter,
         u32 data[]);

void qlcnic_dump_mbx(struct qlcnic_adapter *adapter,
       struct qlcnic_cmd_args *cmd)
{
 int i;

 if (cmd->op_type == QLC_83XX_MBX_POST_BC_OP)
  return;

 dev_info(&adapter->pdev->dev,
   "Host MBX regs(%d)\n", cmd->req.num);
 for (i = 0; i < cmd->req.num; i++) {
  if (i && !(i % 8))
   pr_info("\n");
  pr_info("%08x ", cmd->req.arg[i]);
 }
 pr_info("\n");
 dev_info(&adapter->pdev->dev,
   "FW MBX regs(%d)\n", cmd->rsp.num);
 for (i = 0; i < cmd->rsp.num; i++) {
  if (i && !(i % 8))
   pr_info("\n");
  pr_info("%08x ", cmd->rsp.arg[i]);
 }
 pr_info("\n");
}

static void qlcnic_83xx_poll_for_mbx_completion(struct qlcnic_adapter *adapter,
      struct qlcnic_cmd_args *cmd)
{
 struct qlcnic_hardware_context *ahw = adapter->ahw;
 int opcode = LSW(cmd->req.arg[0]);
 unsigned long max_loops;

 max_loops = cmd->total_cmds * QLC_83XX_MBX_CMD_LOOP;

 for (; max_loops; max_loops--) {
  if (atomic_read(&cmd->rsp_status) ==
      QLC_83XX_MBX_RESPONSE_ARRIVED)
   return;

  udelay(1);
 }

 dev_err(&adapter->pdev->dev,
  "%s: Mailbox command timed out, cmd_op=0x%x, cmd_type=0x%x, pci_func=0x%x, op_mode=0x%x\n",
  __func__, opcode, cmd->type, ahw->pci_func, ahw->op_mode);
 flush_workqueue(ahw->mailbox->work_q);
 return;
}

int qlcnic_83xx_issue_cmd(struct qlcnic_adapter *adapter,
     struct qlcnic_cmd_args *cmd)
{
 struct qlcnic_mailbox *mbx = adapter->ahw->mailbox;
 struct qlcnic_hardware_context *ahw = adapter->ahw;
 int cmd_type, err, opcode;
 unsigned long timeout;

 if (!mbx)
  return -EIO;

 opcode = LSW(cmd->req.arg[0]);
 cmd_type = cmd->type;
 err = mbx->ops->enqueue_cmd(adapter, cmd, &timeout);
 if (err) {
  dev_err(&adapter->pdev->dev,
   "%s: Mailbox not available, cmd_op=0x%x, cmd_context=0x%x, pci_func=0x%x, op_mode=0x%x\n",
   __func__, opcode, cmd->type, ahw->pci_func,
   ahw->op_mode);
  return err;
 }

 switch (cmd_type) {
 case QLC_83XX_MBX_CMD_WAIT:
  if (!wait_for_completion_timeout(&cmd->completion, timeout)) {
   dev_err(&adapter->pdev->dev,
    "%s: Mailbox command timed out, cmd_op=0x%x, cmd_type=0x%x, pci_func=0x%x, op_mode=0x%x\n",
    __func__, opcode, cmd_type, ahw->pci_func,
    ahw->op_mode);
   flush_workqueue(mbx->work_q);
  }
  break;
 case QLC_83XX_MBX_CMD_NO_WAIT:
  return 0;
 case QLC_83XX_MBX_CMD_BUSY_WAIT:
  qlcnic_83xx_poll_for_mbx_completion(adapter, cmd);
  break;
 default:
  dev_err(&adapter->pdev->dev,
   "%s: Invalid mailbox command, cmd_op=0x%x, cmd_type=0x%x, pci_func=0x%x, op_mode=0x%x\n",
   __func__, opcode, cmd_type, ahw->pci_func,
   ahw->op_mode);
  qlcnic_83xx_detach_mailbox_work(adapter);
 }

 return cmd->rsp_opcode;
}

int qlcnic_83xx_alloc_mbx_args(struct qlcnic_cmd_args *mbx,
          struct qlcnic_adapter *adapter, u32 type)
{
 int i, size;
 u32 temp;
 const struct qlcnic_mailbox_metadata *mbx_tbl;

 memset(mbx, 0, sizeof(struct qlcnic_cmd_args));
 mbx_tbl = qlcnic_83xx_mbx_tbl;
 size = ARRAY_SIZE(qlcnic_83xx_mbx_tbl);
 for (i = 0; i < size; i++) {
  if (type == mbx_tbl[i].cmd) {
   mbx->op_type = QLC_83XX_FW_MBX_CMD;
   mbx->req.num = mbx_tbl[i].in_args;
   mbx->rsp.num = mbx_tbl[i].out_args;
   mbx->req.arg = kcalloc(mbx->req.num, sizeof(u32),
            GFP_ATOMIC);
   if (!mbx->req.arg)
    return -ENOMEM;
   mbx->rsp.arg = kcalloc(mbx->rsp.num, sizeof(u32),
            GFP_ATOMIC);
   if (!mbx->rsp.arg) {
    kfree(mbx->req.arg);
    mbx->req.arg = NULL;
    return -ENOMEM;
   }
   temp = adapter->ahw->fw_hal_version << 29;
   mbx->req.arg[0] = (type | (mbx->req.num << 16) | temp);
   mbx->cmd_op = type;
   return 0;
  }
 }

 dev_err(&adapter->pdev->dev, "%s: Invalid mailbox command opcode 0x%x\n",
  __func__, type);
 return -EINVAL;
}

void qlcnic_83xx_idc_aen_work(struct work_struct *work)
{
 struct qlcnic_adapter *adapter;
 struct qlcnic_cmd_args cmd;
 int i, err = 0;

 adapter = container_of(work, struct qlcnic_adapter, idc_aen_work.work);
 err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_IDC_ACK);
 if (err)
  return;

 for (i = 1; i < QLC_83XX_MBX_AEN_CNT; i++)
  cmd.req.arg[i] = adapter->ahw->mbox_aen[i];

 err = qlcnic_issue_cmd(adapter, &cmd);
 if (err)
  dev_info(&adapter->pdev->dev,
    "%s: Mailbox IDC ACK failed.\n", __func__);
 qlcnic_free_mbx_args(&cmd);
}

static void qlcnic_83xx_handle_idc_comp_aen(struct qlcnic_adapter *adapter,
         u32 data[])
{
 dev_dbg(&adapter->pdev->dev, "Completion AEN:0x%x.\n",
  QLCNIC_MBX_RSP(data[0]));
 clear_bit(QLC_83XX_IDC_COMP_AEN, &adapter->ahw->idc.status);
 return;
}

static void __qlcnic_83xx_process_aen(struct qlcnic_adapter *adapter)
{
 struct qlcnic_hardware_context *ahw = adapter->ahw;
 u32 event[QLC_83XX_MBX_AEN_CNT];
 int i;

 for (i = 0; i < QLC_83XX_MBX_AEN_CNT; i++)
  event[i] = readl(QLCNIC_MBX_FW(ahw, i));

 switch (QLCNIC_MBX_RSP(event[0])) {

 case QLCNIC_MBX_LINK_EVENT:
  qlcnic_83xx_handle_link_aen(adapter, event);
  break;
 case QLCNIC_MBX_COMP_EVENT:
  qlcnic_83xx_handle_idc_comp_aen(adapter, event);
  break;
 case QLCNIC_MBX_REQUEST_EVENT:
  for (i = 0; i < QLC_83XX_MBX_AEN_CNT; i++)
   adapter->ahw->mbox_aen[i] = QLCNIC_MBX_RSP(event[i]);
  queue_delayed_work(adapter->qlcnic_wq,
       &adapter->idc_aen_work, 0);
  break;
 case QLCNIC_MBX_TIME_EXTEND_EVENT:
  ahw->extend_lb_time = event[1] >> 8 & 0xf;
  break;
 case QLCNIC_MBX_BC_EVENT:
  qlcnic_sriov_handle_bc_event(adapter, event[1]);
  break;
 case QLCNIC_MBX_SFP_INSERT_EVENT:
  dev_info(&adapter->pdev->dev, "SFP+ Insert AEN:0x%x.\n",
    QLCNIC_MBX_RSP(event[0]));
  break;
 case QLCNIC_MBX_SFP_REMOVE_EVENT:
  dev_info(&adapter->pdev->dev, "SFP Removed AEN:0x%x.\n",
    QLCNIC_MBX_RSP(event[0]));
  break;
 case QLCNIC_MBX_DCBX_CONFIG_CHANGE_EVENT:
  qlcnic_dcb_aen_handler(adapter->dcb, (void *)&event[1]);
  break;
 default:
  dev_dbg(&adapter->pdev->dev, "Unsupported AEN:0x%x.\n",
   QLCNIC_MBX_RSP(event[0]));
  break;
 }

 QLCWRX(ahw, QLCNIC_FW_MBX_CTRL, QLCNIC_CLR_OWNER);
}

static void qlcnic_83xx_process_aen(struct qlcnic_adapter *adapter)
{
 u32 resp, event, rsp_status = QLC_83XX_MBX_RESPONSE_ARRIVED;
 struct qlcnic_hardware_context *ahw = adapter->ahw;
 struct qlcnic_mailbox *mbx = ahw->mailbox;
 unsigned long flags;

 spin_lock_irqsave(&mbx->aen_lock, flags);
 resp = QLCRDX(ahw, QLCNIC_FW_MBX_CTRL);
 if (resp & QLCNIC_SET_OWNER) {
  event = readl(QLCNIC_MBX_FW(ahw, 0));
  if (event &  QLCNIC_MBX_ASYNC_EVENT) {
   __qlcnic_83xx_process_aen(adapter);
  } else {
   if (mbx->rsp_status != rsp_status)
    qlcnic_83xx_notify_mbx_response(mbx);
  }
 }
 spin_unlock_irqrestore(&mbx->aen_lock, flags);
}

static void qlcnic_83xx_mbx_poll_work(struct work_struct *work)
{
 struct qlcnic_adapter *adapter;

 adapter = container_of(work, struct qlcnic_adapter, mbx_poll_work.work);

 if (!test_bit(__QLCNIC_MBX_POLL_ENABLE, &adapter->state))
  return;

 qlcnic_83xx_process_aen(adapter);
 queue_delayed_work(adapter->qlcnic_wq, &adapter->mbx_poll_work,
      (HZ / 10));
}

void qlcnic_83xx_enable_mbx_poll(struct qlcnic_adapter *adapter)
{
 if (test_and_set_bit(__QLCNIC_MBX_POLL_ENABLE, &adapter->state))
  return;

 INIT_DELAYED_WORK(&adapter->mbx_poll_work, qlcnic_83xx_mbx_poll_work);
 queue_delayed_work(adapter->qlcnic_wq, &adapter->mbx_poll_work, 0);
}

void qlcnic_83xx_disable_mbx_poll(struct qlcnic_adapter *adapter)
{
 if (!test_and_clear_bit(__QLCNIC_MBX_POLL_ENABLE, &adapter->state))
  return;
 cancel_delayed_work_sync(&adapter->mbx_poll_work);
}

static int qlcnic_83xx_add_rings(struct qlcnic_adapter *adapter)
{
 int index, i, err, sds_mbx_size;
 u32 *buf, intrpt_id, intr_mask;
 u16 context_id;
 u8 num_sds;
 struct qlcnic_cmd_args cmd;
 struct qlcnic_host_sds_ring *sds;
 struct qlcnic_sds_mbx sds_mbx;
 struct qlcnic_add_rings_mbx_out *mbx_out;
 struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx;
 struct qlcnic_hardware_context *ahw = adapter->ahw;

 sds_mbx_size = sizeof(struct qlcnic_sds_mbx);
 context_id = recv_ctx->context_id;
 num_sds = adapter->drv_sds_rings - QLCNIC_MAX_SDS_RINGS;
 err = ahw->hw_ops->alloc_mbx_args(&cmd, adapter,
     QLCNIC_CMD_ADD_RCV_RINGS);
 if (err) {
  dev_err(&adapter->pdev->dev,
   "Failed to alloc mbx args %d\n", err);
  return err;
 }

 cmd.req.arg[1] = 0 | (num_sds << 8) | (context_id << 16);

 /* set up status rings, mbx 2-81 */
 index = 2;
 for (i = 8; i < adapter->drv_sds_rings; i++) {
  memset(&sds_mbx, 0, sds_mbx_size);
  sds = &recv_ctx->sds_rings[i];
  sds->consumer = 0;
  memset(sds->desc_head, 0, STATUS_DESC_RINGSIZE(sds));
  sds_mbx.phy_addr_low = LSD(sds->phys_addr);
  sds_mbx.phy_addr_high = MSD(sds->phys_addr);
  sds_mbx.sds_ring_size = sds->num_desc;

  if (adapter->flags & QLCNIC_MSIX_ENABLED)
   intrpt_id = ahw->intr_tbl[i].id;
  else
   intrpt_id = QLCRDX(ahw, QLCNIC_DEF_INT_ID);

  if (adapter->ahw->diag_test != QLCNIC_LOOPBACK_TEST)
   sds_mbx.intrpt_id = intrpt_id;
  else
   sds_mbx.intrpt_id = 0xffff;
  sds_mbx.intrpt_val = 0;
  buf = &cmd.req.arg[index];
  memcpy(buf, &sds_mbx, sds_mbx_size);
  index += sds_mbx_size / sizeof(u32);
 }

 /* send the mailbox command */
 err = ahw->hw_ops->mbx_cmd(adapter, &cmd);
 if (err) {
  dev_err(&adapter->pdev->dev,
   "Failed to add rings %d\n", err);
  goto out;
 }

 mbx_out = (struct qlcnic_add_rings_mbx_out *)&cmd.rsp.arg[1];
 index = 0;
 /* status descriptor ring */
 for (i = 8; i < adapter->drv_sds_rings; i++) {
  sds = &recv_ctx->sds_rings[i];
  sds->crb_sts_consumer = ahw->pci_base0 +
     mbx_out->host_csmr[index];
  if (adapter->flags & QLCNIC_MSIX_ENABLED)
   intr_mask = ahw->intr_tbl[i].src;
  else
   intr_mask = QLCRDX(ahw, QLCNIC_DEF_INT_MASK);

  sds->crb_intr_mask = ahw->pci_base0 + intr_mask;
  index++;
 }
out:
 qlcnic_free_mbx_args(&cmd);
 return err;
}

void qlcnic_83xx_del_rx_ctx(struct qlcnic_adapter *adapter)
{
 int err;
 u32 temp = 0;
 struct qlcnic_cmd_args cmd;
 struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx;

 if (qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_DESTROY_RX_CTX))
  return;

 if (qlcnic_sriov_pf_check(adapter) || qlcnic_sriov_vf_check(adapter))
  cmd.req.arg[0] |= (0x3 << 29);

 if (qlcnic_sriov_pf_check(adapter))
  qlcnic_pf_set_interface_id_del_rx_ctx(adapter, &temp);

 cmd.req.arg[1] = recv_ctx->context_id | temp;
 err = qlcnic_issue_cmd(adapter, &cmd);
 if (err)
  dev_err(&adapter->pdev->dev,
   "Failed to destroy rx ctx in firmware\n");

 recv_ctx->state = QLCNIC_HOST_CTX_STATE_FREED;
 qlcnic_free_mbx_args(&cmd);
}

int qlcnic_83xx_create_rx_ctx(struct qlcnic_adapter *adapter)
{
 int i, err, index, sds_mbx_size, rds_mbx_size;
 u8 num_sds, num_rds;
 u32 *buf, intrpt_id, intr_mask, cap = 0;
 struct qlcnic_host_sds_ring *sds;
 struct qlcnic_host_rds_ring *rds;
 struct qlcnic_sds_mbx sds_mbx;
 struct qlcnic_rds_mbx rds_mbx;
 struct qlcnic_cmd_args cmd;
 struct qlcnic_rcv_mbx_out *mbx_out;
 struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx;
 struct qlcnic_hardware_context *ahw = adapter->ahw;
 num_rds = adapter->max_rds_rings;

 if (adapter->drv_sds_rings <= QLCNIC_MAX_SDS_RINGS)
  num_sds = adapter->drv_sds_rings;
 else
  num_sds = QLCNIC_MAX_SDS_RINGS;

 sds_mbx_size = sizeof(struct qlcnic_sds_mbx);
 rds_mbx_size = sizeof(struct qlcnic_rds_mbx);
 cap = QLCNIC_CAP0_LEGACY_CONTEXT;

 if (adapter->flags & QLCNIC_FW_LRO_MSS_CAP)
  cap |= QLC_83XX_FW_CAP_LRO_MSS;

 /* set mailbox hdr and capabilities */
 err = qlcnic_alloc_mbx_args(&cmd, adapter,
        QLCNIC_CMD_CREATE_RX_CTX);
 if (err)
  return err;

 if (qlcnic_sriov_pf_check(adapter) || qlcnic_sriov_vf_check(adapter))
  cmd.req.arg[0] |= (0x3 << 29);

 cmd.req.arg[1] = cap;
 cmd.req.arg[5] = 1 | (num_rds << 5) | (num_sds << 8) |
    (QLC_83XX_HOST_RDS_MODE_UNIQUE << 16);

 if (qlcnic_sriov_pf_check(adapter))
  qlcnic_pf_set_interface_id_create_rx_ctx(adapter,
        &cmd.req.arg[6]);
 /* set up status rings, mbx 8-57/87 */
 index = QLC_83XX_HOST_SDS_MBX_IDX;
 for (i = 0; i < num_sds; i++) {
  memset(&sds_mbx, 0, sds_mbx_size);
  sds = &recv_ctx->sds_rings[i];
  sds->consumer = 0;
  memset(sds->desc_head, 0, STATUS_DESC_RINGSIZE(sds));
  sds_mbx.phy_addr_low = LSD(sds->phys_addr);
  sds_mbx.phy_addr_high = MSD(sds->phys_addr);
  sds_mbx.sds_ring_size = sds->num_desc;
  if (adapter->flags & QLCNIC_MSIX_ENABLED)
   intrpt_id = ahw->intr_tbl[i].id;
  else
   intrpt_id = QLCRDX(ahw, QLCNIC_DEF_INT_ID);
  if (adapter->ahw->diag_test != QLCNIC_LOOPBACK_TEST)
   sds_mbx.intrpt_id = intrpt_id;
  else
   sds_mbx.intrpt_id = 0xffff;
  sds_mbx.intrpt_val = 0;
  buf = &cmd.req.arg[index];
  memcpy(buf, &sds_mbx, sds_mbx_size);
  index += sds_mbx_size / sizeof(u32);
 }
 /* set up receive rings, mbx 88-111/135 */
 index = QLCNIC_HOST_RDS_MBX_IDX;
 rds = &recv_ctx->rds_rings[0];
 rds->producer = 0;
 memset(&rds_mbx, 0, rds_mbx_size);
 rds_mbx.phy_addr_reg_low = LSD(rds->phys_addr);
 rds_mbx.phy_addr_reg_high = MSD(rds->phys_addr);
 rds_mbx.reg_ring_sz = rds->dma_size;
 rds_mbx.reg_ring_len = rds->num_desc;
 /* Jumbo ring */
 rds = &recv_ctx->rds_rings[1];
 rds->producer = 0;
 rds_mbx.phy_addr_jmb_low = LSD(rds->phys_addr);
 rds_mbx.phy_addr_jmb_high = MSD(rds->phys_addr);
 rds_mbx.jmb_ring_sz = rds->dma_size;
 rds_mbx.jmb_ring_len = rds->num_desc;
 buf = &cmd.req.arg[index];
 memcpy(buf, &rds_mbx, rds_mbx_size);

 /* send the mailbox command */
 err = ahw->hw_ops->mbx_cmd(adapter, &cmd);
 if (err) {
  dev_err(&adapter->pdev->dev,
   "Failed to create Rx ctx in firmware%d\n", err);
  goto out;
 }
 mbx_out = (struct qlcnic_rcv_mbx_out *)&cmd.rsp.arg[1];
 recv_ctx->context_id = mbx_out->ctx_id;
 recv_ctx->state = mbx_out->state;
 recv_ctx->virt_port = mbx_out->vport_id;
 dev_info(&adapter->pdev->dev, "Rx Context[%d] Created, state:0x%x\n",
   recv_ctx->context_id, recv_ctx->state);
 /* Receive descriptor ring */
 /* Standard ring */
 rds = &recv_ctx->rds_rings[0];
 rds->crb_rcv_producer = ahw->pci_base0 +
    mbx_out->host_prod[0].reg_buf;
 /* Jumbo ring */
 rds = &recv_ctx->rds_rings[1];
 rds->crb_rcv_producer = ahw->pci_base0 +
    mbx_out->host_prod[0].jmb_buf;
 /* status descriptor ring */
 for (i = 0; i < num_sds; i++) {
  sds = &recv_ctx->sds_rings[i];
  sds->crb_sts_consumer = ahw->pci_base0 +
     mbx_out->host_csmr[i];
  if (adapter->flags & QLCNIC_MSIX_ENABLED)
   intr_mask = ahw->intr_tbl[i].src;
  else
   intr_mask = QLCRDX(ahw, QLCNIC_DEF_INT_MASK);
  sds->crb_intr_mask = ahw->pci_base0 + intr_mask;
 }

 if (adapter->drv_sds_rings > QLCNIC_MAX_SDS_RINGS)
  err = qlcnic_83xx_add_rings(adapter);
out:
 qlcnic_free_mbx_args(&cmd);
 return err;
}

void qlcnic_83xx_del_tx_ctx(struct qlcnic_adapter *adapter,
       struct qlcnic_host_tx_ring *tx_ring)
{
 struct qlcnic_cmd_args cmd;
 u32 temp = 0;

 if (qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_DESTROY_TX_CTX))
  return;

 if (qlcnic_sriov_pf_check(adapter) || qlcnic_sriov_vf_check(adapter))
  cmd.req.arg[0] |= (0x3 << 29);

 if (qlcnic_sriov_pf_check(adapter))
  qlcnic_pf_set_interface_id_del_tx_ctx(adapter, &temp);

 cmd.req.arg[1] = tx_ring->ctx_id | temp;
 if (qlcnic_issue_cmd(adapter, &cmd))
  dev_err(&adapter->pdev->dev,
   "Failed to destroy tx ctx in firmware\n");
 qlcnic_free_mbx_args(&cmd);
}

int qlcnic_83xx_create_tx_ctx(struct qlcnic_adapter *adapter,
         struct qlcnic_host_tx_ring *tx, int ring)
{
 int err;
 u16 msix_id;
 u32 *buf, intr_mask, temp = 0;
 struct qlcnic_cmd_args cmd;
 struct qlcnic_tx_mbx mbx;
 struct qlcnic_tx_mbx_out *mbx_out;
 struct qlcnic_hardware_context *ahw = adapter->ahw;
 u32 msix_vector;

 /* Reset host resources */
 tx->producer = 0;
 tx->sw_consumer = 0;
 *(tx->hw_consumer) = 0;

 memset(&mbx, 0, sizeof(struct qlcnic_tx_mbx));

 /* setup mailbox inbox registerss */
 mbx.phys_addr_low = LSD(tx->phys_addr);
 mbx.phys_addr_high = MSD(tx->phys_addr);
 mbx.cnsmr_index_low = LSD(tx->hw_cons_phys_addr);
 mbx.cnsmr_index_high = MSD(tx->hw_cons_phys_addr);
 mbx.size = tx->num_desc;
 if (adapter->flags & QLCNIC_MSIX_ENABLED) {
  if (!(adapter->flags & QLCNIC_TX_INTR_SHARED))
   msix_vector = adapter->drv_sds_rings + ring;
  else
   msix_vector = adapter->drv_sds_rings - 1;
  msix_id = ahw->intr_tbl[msix_vector].id;
 } else {
  msix_id = QLCRDX(ahw, QLCNIC_DEF_INT_ID);
 }

 if (adapter->ahw->diag_test != QLCNIC_LOOPBACK_TEST)
  mbx.intr_id = msix_id;
 else
  mbx.intr_id = 0xffff;
 mbx.src = 0;

 err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CREATE_TX_CTX);
 if (err)
  return err;

 if (qlcnic_sriov_pf_check(adapter) || qlcnic_sriov_vf_check(adapter))
  cmd.req.arg[0] |= (0x3 << 29);

 if (qlcnic_sriov_pf_check(adapter))
  qlcnic_pf_set_interface_id_create_tx_ctx(adapter, &temp);

 cmd.req.arg[1] = QLCNIC_CAP0_LEGACY_CONTEXT;
 cmd.req.arg[5] = QLCNIC_SINGLE_RING | temp;

 buf = &cmd.req.arg[6];
 memcpy(buf, &mbx, sizeof(struct qlcnic_tx_mbx));
 /* send the mailbox command*/
 err = qlcnic_issue_cmd(adapter, &cmd);
 if (err) {
  netdev_err(adapter->netdev,
      "Failed to create Tx ctx in firmware 0x%x\n", err);
  goto out;
 }
 mbx_out = (struct qlcnic_tx_mbx_out *)&cmd.rsp.arg[2];
 tx->crb_cmd_producer = ahw->pci_base0 + mbx_out->host_prod;
 tx->ctx_id = mbx_out->ctx_id;
 if ((adapter->flags & QLCNIC_MSIX_ENABLED) &&
     !(adapter->flags & QLCNIC_TX_INTR_SHARED)) {
  intr_mask = ahw->intr_tbl[adapter->drv_sds_rings + ring].src;
  tx->crb_intr_mask = ahw->pci_base0 + intr_mask;
 }
 netdev_info(adapter->netdev,
      "Tx Context[0x%x] Created, state:0x%x\n",
      tx->ctx_id, mbx_out->state);
out:
 qlcnic_free_mbx_args(&cmd);
 return err;
}

static int qlcnic_83xx_diag_alloc_res(struct net_device *netdev, int test,
          u8 num_sds_ring)
{
 struct qlcnic_adapter *adapter = netdev_priv(netdev);
 struct qlcnic_host_sds_ring *sds_ring;
 struct qlcnic_host_rds_ring *rds_ring;
 u16 adapter_state = adapter->is_up;
 u8 ring;
 int ret;

 netif_device_detach(netdev);

 if (netif_running(netdev))
  __qlcnic_down(adapter, netdev);

 qlcnic_detach(adapter);

 adapter->drv_sds_rings = QLCNIC_SINGLE_RING;
 adapter->ahw->diag_test = test;
 adapter->ahw->linkup = 0;

 ret = qlcnic_attach(adapter);
 if (ret) {
  netif_device_attach(netdev);
  return ret;
 }

 ret = qlcnic_fw_create_ctx(adapter);
 if (ret) {
  qlcnic_detach(adapter);
  if (adapter_state == QLCNIC_ADAPTER_UP_MAGIC) {
   adapter->drv_sds_rings = num_sds_ring;
   qlcnic_attach(adapter);
  }
  netif_device_attach(netdev);
  return ret;
 }

 for (ring = 0; ring < adapter->max_rds_rings; ring++) {
  rds_ring = &adapter->recv_ctx->rds_rings[ring];
  qlcnic_post_rx_buffers(adapter, rds_ring, ring);
 }

 if (adapter->ahw->diag_test == QLCNIC_INTERRUPT_TEST) {
  for (ring = 0; ring < adapter->drv_sds_rings; ring++) {
   sds_ring = &adapter->recv_ctx->sds_rings[ring];
   qlcnic_enable_sds_intr(adapter, sds_ring);
  }
 }

 if (adapter->ahw->diag_test == QLCNIC_LOOPBACK_TEST) {
  adapter->ahw->loopback_state = 0;
  adapter->ahw->hw_ops->setup_link_event(adapter, 1);
 }

 set_bit(__QLCNIC_DEV_UP, &adapter->state);
 return 0;
}

static void qlcnic_83xx_diag_free_res(struct net_device *netdev,
          u8 drv_sds_rings)
{
 struct qlcnic_adapter *adapter = netdev_priv(netdev);
 struct qlcnic_host_sds_ring *sds_ring;
 int ring;

 clear_bit(__QLCNIC_DEV_UP, &adapter->state);
 if (adapter->ahw->diag_test == QLCNIC_INTERRUPT_TEST) {
  for (ring = 0; ring < adapter->drv_sds_rings; ring++) {
   sds_ring = &adapter->recv_ctx->sds_rings[ring];
   if (adapter->flags & QLCNIC_MSIX_ENABLED)
    qlcnic_disable_sds_intr(adapter, sds_ring);
  }
 }

 qlcnic_fw_destroy_ctx(adapter);
 qlcnic_detach(adapter);

 adapter->ahw->diag_test = 0;
 adapter->drv_sds_rings = drv_sds_rings;

 if (qlcnic_attach(adapter))
  goto out;

 if (netif_running(netdev))
  __qlcnic_up(adapter, netdev);

out:
 netif_device_attach(netdev);
}

static void qlcnic_83xx_get_beacon_state(struct qlcnic_adapter *adapter)
{
 struct qlcnic_hardware_context *ahw = adapter->ahw;
 struct qlcnic_cmd_args cmd;
 u8 beacon_state;
 int err = 0;

 err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_LED_CONFIG);
 if (!err) {
  err = qlcnic_issue_cmd(adapter, &cmd);
  if (!err) {
   beacon_state = cmd.rsp.arg[4];
   if (beacon_state == QLCNIC_BEACON_DISABLE)
    ahw->beacon_state = QLC_83XX_BEACON_OFF;
   else if (beacon_state == QLC_83XX_ENABLE_BEACON)
    ahw->beacon_state = QLC_83XX_BEACON_ON;
  }
 } else {
  netdev_err(adapter->netdev, "Get beacon state failed, err=%d\n",
      err);
 }

 qlcnic_free_mbx_args(&cmd);

 return;
}

int qlcnic_83xx_config_led(struct qlcnic_adapter *adapter, u32 state,
      u32 beacon)
{
 struct qlcnic_cmd_args cmd;
 u32 mbx_in;
 int i, status = 0;

 if (state) {
  /* Get LED configuration */
  status = qlcnic_alloc_mbx_args(&cmd, adapter,
            QLCNIC_CMD_GET_LED_CONFIG);
  if (status)
   return status;

  status = qlcnic_issue_cmd(adapter, &cmd);
  if (status) {
   dev_err(&adapter->pdev->dev,
    "Get led config failed.\n");
   goto mbx_err;
  } else {
   for (i = 0; i < 4; i++)
    adapter->ahw->mbox_reg[i] = cmd.rsp.arg[i+1];
  }
  qlcnic_free_mbx_args(&cmd);
  /* Set LED Configuration */
  mbx_in = (LSW(QLC_83XX_LED_CONFIG) << 16) |
     LSW(QLC_83XX_LED_CONFIG);
  status = qlcnic_alloc_mbx_args(&cmd, adapter,
            QLCNIC_CMD_SET_LED_CONFIG);
  if (status)
   return status;

  cmd.req.arg[1] = mbx_in;
  cmd.req.arg[2] = mbx_in;
  cmd.req.arg[3] = mbx_in;
  if (beacon)
   cmd.req.arg[4] = QLC_83XX_ENABLE_BEACON;
  status = qlcnic_issue_cmd(adapter, &cmd);
  if (status) {
   dev_err(&adapter->pdev->dev,
    "Set led config failed.\n");
  }
mbx_err:
  qlcnic_free_mbx_args(&cmd);
  return status;

 } else {
  /* Restoring default LED configuration */
  status = qlcnic_alloc_mbx_args(&cmd, adapter,
            QLCNIC_CMD_SET_LED_CONFIG);
  if (status)
   return status;

  cmd.req.arg[1] = adapter->ahw->mbox_reg[0];
  cmd.req.arg[2] = adapter->ahw->mbox_reg[1];
  cmd.req.arg[3] = adapter->ahw->mbox_reg[2];
  if (beacon)
   cmd.req.arg[4] = adapter->ahw->mbox_reg[3];
  status = qlcnic_issue_cmd(adapter, &cmd);
  if (status)
   dev_err(&adapter->pdev->dev,
    "Restoring led config failed.\n");
  qlcnic_free_mbx_args(&cmd);
  return status;
 }
}

int  qlcnic_83xx_set_led(struct net_device *netdev,
    enum ethtool_phys_id_state state)
{
 struct qlcnic_adapter *adapter = netdev_priv(netdev);
 int err = -EIO, active = 1;

 if (adapter->ahw->op_mode == QLCNIC_NON_PRIV_FUNC) {
  netdev_warn(netdev,
       "LED test is not supported in non-privileged mode\n");
  return -EOPNOTSUPP;
 }

 switch (state) {
 case ETHTOOL_ID_ACTIVE:
  if (test_and_set_bit(__QLCNIC_LED_ENABLE, &adapter->state))
   return -EBUSY;

  if (test_bit(__QLCNIC_RESETTING, &adapter->state))
   break;

  err = qlcnic_83xx_config_led(adapter, active, 0);
  if (err)
   netdev_err(netdev, "Failed to set LED blink state\n");
  break;
 case ETHTOOL_ID_INACTIVE:
  active = 0;

  if (test_bit(__QLCNIC_RESETTING, &adapter->state))
   break;

  err = qlcnic_83xx_config_led(adapter, active, 0);
  if (err)
   netdev_err(netdev, "Failed to reset LED blink state\n");
  break;

 default:
  return -EINVAL;
 }

 if (!active || err)
  clear_bit(__QLCNIC_LED_ENABLE, &adapter->state);

 return err;
}

void qlcnic_83xx_initialize_nic(struct qlcnic_adapter *adapter, int enable)
{
 struct qlcnic_cmd_args cmd;
 int status;

 if (qlcnic_sriov_vf_check(adapter))
  return;

 if (enable)
  status = qlcnic_alloc_mbx_args(&cmd, adapter,
            QLCNIC_CMD_INIT_NIC_FUNC);
 else
  status = qlcnic_alloc_mbx_args(&cmd, adapter,
            QLCNIC_CMD_STOP_NIC_FUNC);

 if (status)
  return;

 cmd.req.arg[1] = QLC_REGISTER_LB_IDC | QLC_INIT_FW_RESOURCES;

 if (adapter->dcb)
  cmd.req.arg[1] |= QLC_REGISTER_DCB_AEN;

 status = qlcnic_issue_cmd(adapter, &cmd);
 if (status)
  dev_err(&adapter->pdev->dev,
   "Failed to %s in NIC IDC function event.\n",
   (enable ? "register" : "unregister"));

 qlcnic_free_mbx_args(&cmd);
}

static int qlcnic_83xx_set_port_config(struct qlcnic_adapter *adapter)
{
 struct qlcnic_cmd_args cmd;
 int err;

 err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_SET_PORT_CONFIG);
 if (err)
  return err;

 cmd.req.arg[1] = adapter->ahw->port_config;
 err = qlcnic_issue_cmd(adapter, &cmd);
 if (err)
  dev_info(&adapter->pdev->dev, "Set Port Config failed.\n");
 qlcnic_free_mbx_args(&cmd);
 return err;
}

static int qlcnic_83xx_get_port_config(struct qlcnic_adapter *adapter)
{
 struct qlcnic_cmd_args cmd;
 int err;

 err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_PORT_CONFIG);
 if (err)
  return err;

 err = qlcnic_issue_cmd(adapter, &cmd);
 if (err)
  dev_info(&adapter->pdev->dev, "Get Port config failed\n");
 else
  adapter->ahw->port_config = cmd.rsp.arg[1];
 qlcnic_free_mbx_args(&cmd);
 return err;
}

int qlcnic_83xx_setup_link_event(struct qlcnic_adapter *adapter, int enable)
{
 int err;
 u32 temp;
 struct qlcnic_cmd_args cmd;

 err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_LINK_EVENT);
 if (err)
  return err;

 temp = adapter->recv_ctx->context_id << 16;
 cmd.req.arg[1] = (enable ? 1 : 0) | BIT_8 | temp;
 err = qlcnic_issue_cmd(adapter, &cmd);
 if (err)
  dev_info(&adapter->pdev->dev,
    "Setup linkevent mailbox failed\n");
 qlcnic_free_mbx_args(&cmd);
 return err;
}

static void qlcnic_83xx_set_interface_id_promisc(struct qlcnic_adapter *adapter,
       u32 *interface_id)
{
 if (qlcnic_sriov_pf_check(adapter)) {
  qlcnic_alloc_lb_filters_mem(adapter);
  qlcnic_pf_set_interface_id_promisc(adapter, interface_id);
  adapter->rx_mac_learn = true;
 } else {
  if (!qlcnic_sriov_vf_check(adapter))
   *interface_id = adapter->recv_ctx->context_id << 16;
 }
}

int qlcnic_83xx_nic_set_promisc(struct qlcnic_adapter *adapter, u32 mode)
{
 struct qlcnic_cmd_args *cmd = NULL;
 u32 temp = 0;
 int err;

 if (adapter->recv_ctx->state == QLCNIC_HOST_CTX_STATE_FREED)
  return -EIO;

 cmd = kzalloc(sizeof(*cmd), GFP_ATOMIC);
 if (!cmd)
  return -ENOMEM;

 err = qlcnic_alloc_mbx_args(cmd, adapter,
        QLCNIC_CMD_CONFIGURE_MAC_RX_MODE);
 if (err)
  goto out;

 cmd->type = QLC_83XX_MBX_CMD_NO_WAIT;
 qlcnic_83xx_set_interface_id_promisc(adapter, &temp);

 if (qlcnic_84xx_check(adapter) && qlcnic_sriov_pf_check(adapter))
  mode = VPORT_MISS_MODE_ACCEPT_ALL;

 cmd->req.arg[1] = mode | temp;
 err = qlcnic_issue_cmd(adapter, cmd);
 if (!err)
  return err;

 qlcnic_free_mbx_args(cmd);

out:
 kfree(cmd);
 return err;
}

int qlcnic_83xx_loopback_test(struct net_device *netdev, u8 mode)
{
 struct qlcnic_adapter *adapter = netdev_priv(netdev);
 struct qlcnic_hardware_context *ahw = adapter->ahw;
 u8 drv_sds_rings = adapter->drv_sds_rings;
 u8 drv_tx_rings = adapter->drv_tx_rings;
 int ret = 0, loop = 0;

 if (ahw->op_mode == QLCNIC_NON_PRIV_FUNC) {
  netdev_warn(netdev,
       "Loopback test not supported in non privileged mode\n");
  return -ENOTSUPP;
 }

 if (test_bit(__QLCNIC_RESETTING, &adapter->state)) {
  netdev_info(netdev, "Device is resetting\n");
  return -EBUSY;
 }

 if (qlcnic_get_diag_lock(adapter)) {
  netdev_info(netdev, "Device is in diagnostics mode\n");
  return -EBUSY;
 }

 netdev_info(netdev, "%s loopback test in progress\n",
      mode == QLCNIC_ILB_MODE ? "internal" : "external");

 ret = qlcnic_83xx_diag_alloc_res(netdev, QLCNIC_LOOPBACK_TEST,
      drv_sds_rings);
 if (ret)
  goto fail_diag_alloc;

 ret = qlcnic_83xx_set_lb_mode(adapter, mode);
 if (ret)
  goto free_diag_res;

 /* Poll for link up event before running traffic */
 do {
  msleep(QLC_83XX_LB_MSLEEP_COUNT);

  if (test_bit(__QLCNIC_RESETTING, &adapter->state)) {
   netdev_info(netdev,
        "Device is resetting, free LB test resources\n");
   ret = -EBUSY;
   goto free_diag_res;
  }
  if (loop++ > QLC_83XX_LB_WAIT_COUNT) {
   netdev_info(netdev,
        "Firmware didn't sent link up event to loopback request\n");
   ret = -ETIMEDOUT;
   qlcnic_83xx_clear_lb_mode(adapter, mode);
   goto free_diag_res;
  }
 } while ((adapter->ahw->linkup && ahw->has_link_events) != 1);

 ret = qlcnic_do_lb_test(adapter, mode);

 qlcnic_83xx_clear_lb_mode(adapter, mode);

free_diag_res:
 qlcnic_83xx_diag_free_res(netdev, drv_sds_rings);

fail_diag_alloc:
 adapter->drv_sds_rings = drv_sds_rings;
 adapter->drv_tx_rings = drv_tx_rings;
 qlcnic_release_diag_lock(adapter);
 return ret;
}

static void qlcnic_extend_lb_idc_cmpltn_wait(struct qlcnic_adapter *adapter,
          u32 *max_wait_count)
{
 struct qlcnic_hardware_context *ahw = adapter->ahw;
 int temp;

 netdev_info(adapter->netdev, "Received loopback IDC time extend event for 0x%x seconds\n",
      ahw->extend_lb_time);
 temp = ahw->extend_lb_time * 1000;
 *max_wait_count += temp / QLC_83XX_LB_MSLEEP_COUNT;
 ahw->extend_lb_time = 0;
}

static int qlcnic_83xx_set_lb_mode(struct qlcnic_adapter *adapter, u8 mode)
{
 struct qlcnic_hardware_context *ahw = adapter->ahw;
 struct net_device *netdev = adapter->netdev;
 u32 config, max_wait_count;
 int status = 0, loop = 0;

 ahw->extend_lb_time = 0;
 max_wait_count = QLC_83XX_LB_WAIT_COUNT;
 status = qlcnic_83xx_get_port_config(adapter);
 if (status)
  return status;

 config = ahw->port_config;

 /* Check if port is already in loopback mode */
 if ((config & QLC_83XX_CFG_LOOPBACK_HSS) ||
     (config & QLC_83XX_CFG_LOOPBACK_EXT)) {
  netdev_err(netdev,
      "Port already in Loopback mode.\n");
  return -EINPROGRESS;
 }

 set_bit(QLC_83XX_IDC_COMP_AEN, &ahw->idc.status);

 if (mode == QLCNIC_ILB_MODE)
  ahw->port_config |= QLC_83XX_CFG_LOOPBACK_HSS;
 if (mode == QLCNIC_ELB_MODE)
  ahw->port_config |= QLC_83XX_CFG_LOOPBACK_EXT;

 status = qlcnic_83xx_set_port_config(adapter);
 if (status) {
  netdev_err(netdev,
      "Failed to Set Loopback Mode = 0x%x.\n",
      ahw->port_config);
  ahw->port_config = config;
  clear_bit(QLC_83XX_IDC_COMP_AEN, &ahw->idc.status);
  return status;
 }

 /* Wait for Link and IDC Completion AEN */
 do {
  msleep(QLC_83XX_LB_MSLEEP_COUNT);

  if (test_bit(__QLCNIC_RESETTING, &adapter->state)) {
   netdev_info(netdev,
        "Device is resetting, free LB test resources\n");
   clear_bit(QLC_83XX_IDC_COMP_AEN, &ahw->idc.status);
   return -EBUSY;
  }

  if (ahw->extend_lb_time)
   qlcnic_extend_lb_idc_cmpltn_wait(adapter,
        &max_wait_count);

  if (loop++ > max_wait_count) {
   netdev_err(netdev, "%s: Did not receive loopback IDC completion AEN\n",
       __func__);
   clear_bit(QLC_83XX_IDC_COMP_AEN, &ahw->idc.status);
   qlcnic_83xx_clear_lb_mode(adapter, mode);
   return -ETIMEDOUT;
  }
 } while (test_bit(QLC_83XX_IDC_COMP_AEN, &ahw->idc.status));

 qlcnic_sre_macaddr_change(adapter, adapter->mac_addr, 0,
      QLCNIC_MAC_ADD);
 return status;
}

static int qlcnic_83xx_clear_lb_mode(struct qlcnic_adapter *adapter, u8 mode)
{
 struct qlcnic_hardware_context *ahw = adapter->ahw;
 u32 config = ahw->port_config, max_wait_count;
 struct net_device *netdev = adapter->netdev;
 int status = 0, loop = 0;

 ahw->extend_lb_time = 0;
 max_wait_count = QLC_83XX_LB_WAIT_COUNT;
 set_bit(QLC_83XX_IDC_COMP_AEN, &ahw->idc.status);
 if (mode == QLCNIC_ILB_MODE)
  ahw->port_config &= ~QLC_83XX_CFG_LOOPBACK_HSS;
 if (mode == QLCNIC_ELB_MODE)
  ahw->port_config &= ~QLC_83XX_CFG_LOOPBACK_EXT;

 status = qlcnic_83xx_set_port_config(adapter);
 if (status) {
  netdev_err(netdev,
      "Failed to Clear Loopback Mode = 0x%x.\n",
      ahw->port_config);
  ahw->port_config = config;
  clear_bit(QLC_83XX_IDC_COMP_AEN, &ahw->idc.status);
  return status;
 }

 /* Wait for Link and IDC Completion AEN */
 do {
  msleep(QLC_83XX_LB_MSLEEP_COUNT);

  if (test_bit(__QLCNIC_RESETTING, &adapter->state)) {
   netdev_info(netdev,
        "Device is resetting, free LB test resources\n");
   clear_bit(QLC_83XX_IDC_COMP_AEN, &ahw->idc.status);
   return -EBUSY;
  }

  if (ahw->extend_lb_time)
   qlcnic_extend_lb_idc_cmpltn_wait(adapter,
        &max_wait_count);

  if (loop++ > max_wait_count) {
   netdev_err(netdev, "%s: Did not receive loopback IDC completion AEN\n",
       __func__);
   clear_bit(QLC_83XX_IDC_COMP_AEN, &ahw->idc.status);
   return -ETIMEDOUT;
  }
 } while (test_bit(QLC_83XX_IDC_COMP_AEN, &ahw->idc.status));

 qlcnic_sre_macaddr_change(adapter, adapter->mac_addr, 0,
      QLCNIC_MAC_DEL);
 return status;
}

static void qlcnic_83xx_set_interface_id_ipaddr(struct qlcnic_adapter *adapter,
      u32 *interface_id)
{
 if (qlcnic_sriov_pf_check(adapter)) {
  qlcnic_pf_set_interface_id_ipaddr(adapter, interface_id);
 } else {
  if (!qlcnic_sriov_vf_check(adapter))
   *interface_id = adapter->recv_ctx->context_id << 16;
 }
}

void qlcnic_83xx_config_ipaddr(struct qlcnic_adapter *adapter, __be32 ip,
          int mode)
{
 int err;
 u32 temp = 0, temp_ip;
 struct qlcnic_cmd_args cmd;

 err = qlcnic_alloc_mbx_args(&cmd, adapter,
        QLCNIC_CMD_CONFIGURE_IP_ADDR);
 if (err)
  return;

 qlcnic_83xx_set_interface_id_ipaddr(adapter, &temp);

 if (mode == QLCNIC_IP_UP)
  cmd.req.arg[1] = 1 | temp;
 else
  cmd.req.arg[1] = 2 | temp;

 /*
 * Adapter needs IP address in network byte order.
 * But hardware mailbox registers go through writel(), hence IP address
 * gets swapped on big endian architecture.
 * To negate swapping of writel() on big endian architecture
 * use swab32(value).
 */


 temp_ip = swab32(ntohl(ip));
 memcpy(&cmd.req.arg[2], &temp_ip, sizeof(u32));
 err = qlcnic_issue_cmd(adapter, &cmd);
 if (err != QLCNIC_RCODE_SUCCESS)
  dev_err(&adapter->netdev->dev,
   "could not notify %s IP 0x%x request\n",
   (mode == QLCNIC_IP_UP) ? "Add" : "Remove", ip);

 qlcnic_free_mbx_args(&cmd);
}

int qlcnic_83xx_config_hw_lro(struct qlcnic_adapter *adapter, int mode)
{
 int err;
 u32 temp, arg1;
 struct qlcnic_cmd_args cmd;
 int lro_bit_mask;

 lro_bit_mask = (mode ? (BIT_0 | BIT_1 | BIT_2 | BIT_3) : 0);

 if (adapter->recv_ctx->state == QLCNIC_HOST_CTX_STATE_FREED)
  return 0;

 err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CONFIGURE_HW_LRO);
 if (err)
  return err;

 temp = adapter->recv_ctx->context_id << 16;
 arg1 = lro_bit_mask | temp;
 cmd.req.arg[1] = arg1;

 err = qlcnic_issue_cmd(adapter, &cmd);
 if (err)
  dev_info(&adapter->pdev->dev, "LRO config failed\n");
 qlcnic_free_mbx_args(&cmd);

 return err;
}

int qlcnic_83xx_config_rss(struct qlcnic_adapter *adapter, int enable)
{
 struct qlcnic_cmd_args cmd;
 static const u64 key[] = {
  0xbeac01fa6a42b73bULL, 0x8030f20c77cb2da3ULL,
  0xae7b30b4d0ca2bcbULL, 0x43a38fb04167253dULL,
  0x255b0ec26d5a56daULL
 };
 u32 word;
 int err;

 err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CONFIGURE_RSS);
 if (err)
  return err;
 /*
 * RSS request:
 * bits 3-0: Rsvd
 *      5-4: hash_type_ipv4
 * 7-6: hash_type_ipv6
 *   8: enable
 *        9: use indirection table
 *    16-31: indirection table mask
 */

 word =  ((u32)(RSS_HASHTYPE_IP_TCP & 0x3) << 4) |
  ((u32)(RSS_HASHTYPE_IP_TCP & 0x3) << 6) |
  ((u32)(enable & 0x1) << 8) |
  ((0x7ULL) << 16);
 cmd.req.arg[1] = (adapter->recv_ctx->context_id);
 cmd.req.arg[2] = word;
 memcpy(&cmd.req.arg[4], key, sizeof(key));

 err = qlcnic_issue_cmd(adapter, &cmd);

 if (err)
  dev_info(&adapter->pdev->dev, "RSS config failed\n");
 qlcnic_free_mbx_args(&cmd);

 return err;

}

static void qlcnic_83xx_set_interface_id_macaddr(struct qlcnic_adapter *adapter,
       u32 *interface_id)
{
 if (qlcnic_sriov_pf_check(adapter)) {
  qlcnic_pf_set_interface_id_macaddr(adapter, interface_id);
 } else {
  if (!qlcnic_sriov_vf_check(adapter))
   *interface_id = adapter->recv_ctx->context_id << 16;
 }
}

int qlcnic_83xx_sre_macaddr_change(struct qlcnic_adapter *adapter, u8 *addr,
       u16 vlan_id, u8 op)
{
 struct qlcnic_cmd_args *cmd = NULL;
 struct qlcnic_macvlan_mbx mv;
 u32 *buf, temp = 0;
 int err;

 if (adapter->recv_ctx->state == QLCNIC_HOST_CTX_STATE_FREED)
  return -EIO;

 cmd = kzalloc(sizeof(*cmd), GFP_ATOMIC);
 if (!cmd)
  return -ENOMEM;

 err = qlcnic_alloc_mbx_args(cmd, adapter, QLCNIC_CMD_CONFIG_MAC_VLAN);
 if (err)
  goto out;

 cmd->type = QLC_83XX_MBX_CMD_NO_WAIT;

 if (vlan_id)
  op = (op == QLCNIC_MAC_ADD || op == QLCNIC_MAC_VLAN_ADD) ?
       QLCNIC_MAC_VLAN_ADD : QLCNIC_MAC_VLAN_DEL;

 cmd->req.arg[1] = op | (1 << 8);
 qlcnic_83xx_set_interface_id_macaddr(adapter, &temp);
 cmd->req.arg[1] |= temp;
 mv.vlan = vlan_id;
 mv.mac_addr0 = addr[0];
 mv.mac_addr1 = addr[1];
 mv.mac_addr2 = addr[2];
 mv.mac_addr3 = addr[3];
 mv.mac_addr4 = addr[4];
 mv.mac_addr5 = addr[5];
 buf = &cmd->req.arg[2];
 memcpy(buf, &mv, sizeof(struct qlcnic_macvlan_mbx));
 err = qlcnic_issue_cmd(adapter, cmd);
 if (!err)
  return err;

 qlcnic_free_mbx_args(cmd);
out:
 kfree(cmd);
 return err;
}

void qlcnic_83xx_change_l2_filter(struct qlcnic_adapter *adapter, u64 *addr,
      u16 vlan_id,
      struct qlcnic_host_tx_ring *tx_ring)
{
 u8 mac[ETH_ALEN];
 memcpy(&mac, addr, ETH_ALEN);
 qlcnic_83xx_sre_macaddr_change(adapter, mac, vlan_id, QLCNIC_MAC_ADD);
}

static void qlcnic_83xx_configure_mac(struct qlcnic_adapter *adapter, u8 *mac,
          u8 type, struct qlcnic_cmd_args *cmd)
{
 switch (type) {
 case QLCNIC_SET_STATION_MAC:
 case QLCNIC_SET_FAC_DEF_MAC:
  memcpy(&cmd->req.arg[2], mac, sizeof(u32));
  memcpy(&cmd->req.arg[3], &mac[4], sizeof(u16));
  break;
 }
 cmd->req.arg[1] = type;
}

int qlcnic_83xx_get_mac_address(struct qlcnic_adapter *adapter, u8 *mac,
    u8 function)
{
 int err, i;
 struct qlcnic_cmd_args cmd;
 u32 mac_low, mac_high;

 err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_MAC_ADDRESS);
 if (err)
  return err;

 qlcnic_83xx_configure_mac(adapter, mac, QLCNIC_GET_CURRENT_MAC, &cmd);
 err = qlcnic_issue_cmd(adapter, &cmd);

 if (err == QLCNIC_RCODE_SUCCESS) {
  mac_low = cmd.rsp.arg[1];
  mac_high = cmd.rsp.arg[2];

  for (i = 0; i < 2; i++)
   mac[i] = (u8) (mac_high >> ((1 - i) * 8));
  for (i = 2; i < 6; i++)
   mac[i] = (u8) (mac_low >> ((5 - i) * 8));
 } else {
  dev_err(&adapter->pdev->dev, "Failed to get mac address%d\n",
   err);
  err = -EIO;
 }
 qlcnic_free_mbx_args(&cmd);
 return err;
}

static int qlcnic_83xx_set_rx_intr_coal(struct qlcnic_adapter *adapter)
{
 struct qlcnic_nic_intr_coalesce *coal = &adapter->ahw->coal;
 struct qlcnic_cmd_args cmd;
 u16 temp;
 int err;

 err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CONFIG_INTR_COAL);
 if (err)
  return err;

 temp = adapter->recv_ctx->context_id;
 cmd.req.arg[1] = QLCNIC_INTR_COAL_TYPE_RX | temp << 16;
 temp = coal->rx_time_us;
 cmd.req.arg[2] = coal->rx_packets | temp << 16;
 cmd.req.arg[3] = coal->flag;

 err = qlcnic_issue_cmd(adapter, &cmd);
 if (err != QLCNIC_RCODE_SUCCESS)
  netdev_err(adapter->netdev,
      "failed to set interrupt coalescing parameters\n");

 qlcnic_free_mbx_args(&cmd);

 return err;
}

static int qlcnic_83xx_set_tx_intr_coal(struct qlcnic_adapter *adapter)
{
 struct qlcnic_nic_intr_coalesce *coal = &adapter->ahw->coal;
 struct qlcnic_cmd_args cmd;
 u16 temp;
 int err;

 err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CONFIG_INTR_COAL);
 if (err)
  return err;

 temp = adapter->tx_ring->ctx_id;
 cmd.req.arg[1] = QLCNIC_INTR_COAL_TYPE_TX | temp << 16;
 temp = coal->tx_time_us;
 cmd.req.arg[2] = coal->tx_packets | temp << 16;
 cmd.req.arg[3] = coal->flag;

 err = qlcnic_issue_cmd(adapter, &cmd);
 if (err != QLCNIC_RCODE_SUCCESS)
  netdev_err(adapter->netdev,
      "failed to set interrupt coalescing parameters\n");

 qlcnic_free_mbx_args(&cmd);

 return err;
}

int qlcnic_83xx_set_rx_tx_intr_coal(struct qlcnic_adapter *adapter)
{
 int err = 0;

 err = qlcnic_83xx_set_rx_intr_coal(adapter);
 if (err)
  netdev_err(adapter->netdev,
      "failed to set Rx coalescing parameters\n");

 err = qlcnic_83xx_set_tx_intr_coal(adapter);
 if (err)
  netdev_err(adapter->netdev,
      "failed to set Tx coalescing parameters\n");

 return err;
}

int qlcnic_83xx_config_intr_coal(struct qlcnic_adapter *adapter,
     struct ethtool_coalesce *ethcoal)
{
 struct qlcnic_nic_intr_coalesce *coal = &adapter->ahw->coal;
 u32 rx_coalesce_usecs, rx_max_frames;
 u32 tx_coalesce_usecs, tx_max_frames;
 int err;

 if (adapter->recv_ctx->state == QLCNIC_HOST_CTX_STATE_FREED)
  return -EIO;

 tx_coalesce_usecs = ethcoal->tx_coalesce_usecs;
 tx_max_frames = ethcoal->tx_max_coalesced_frames;
 rx_coalesce_usecs = ethcoal->rx_coalesce_usecs;
 rx_max_frames = ethcoal->rx_max_coalesced_frames;
 coal->flag = QLCNIC_INTR_DEFAULT;

 if ((coal->rx_time_us == rx_coalesce_usecs) &&
     (coal->rx_packets == rx_max_frames)) {
  coal->type = QLCNIC_INTR_COAL_TYPE_TX;
  coal->tx_time_us = tx_coalesce_usecs;
  coal->tx_packets = tx_max_frames;
 } else if ((coal->tx_time_us == tx_coalesce_usecs) &&
     (coal->tx_packets == tx_max_frames)) {
  coal->type = QLCNIC_INTR_COAL_TYPE_RX;
  coal->rx_time_us = rx_coalesce_usecs;
  coal->rx_packets = rx_max_frames;
 } else {
  coal->type = QLCNIC_INTR_COAL_TYPE_RX_TX;
  coal->rx_time_us = rx_coalesce_usecs;
  coal->rx_packets = rx_max_frames;
  coal->tx_time_us = tx_coalesce_usecs;
  coal->tx_packets = tx_max_frames;
 }

 switch (coal->type) {
 case QLCNIC_INTR_COAL_TYPE_RX:
  err = qlcnic_83xx_set_rx_intr_coal(adapter);
  break;
 case QLCNIC_INTR_COAL_TYPE_TX:
  err = qlcnic_83xx_set_tx_intr_coal(adapter);
  break;
 case QLCNIC_INTR_COAL_TYPE_RX_TX:
  err = qlcnic_83xx_set_rx_tx_intr_coal(adapter);
  break;
 default:
  err = -EINVAL;
  netdev_err(adapter->netdev,
      "Invalid Interrupt coalescing type\n");
  break;
 }

 return err;
}

static void qlcnic_83xx_handle_link_aen(struct qlcnic_adapter *adapter,
     u32 data[])
{
 struct qlcnic_hardware_context *ahw = adapter->ahw;
 u8 link_status, duplex;
 /* link speed */
 link_status = LSB(data[3]) & 1;
 if (link_status) {
  ahw->link_speed = MSW(data[2]);
  duplex = LSB(MSW(data[3]));
  if (duplex)
   ahw->link_duplex = DUPLEX_FULL;
  else
   ahw->link_duplex = DUPLEX_HALF;
 } else {
  ahw->link_speed = SPEED_UNKNOWN;
  ahw->link_duplex = DUPLEX_UNKNOWN;
 }

 ahw->link_autoneg = MSB(MSW(data[3]));
 ahw->module_type = MSB(LSW(data[3]));
 ahw->has_link_events = 1;
 ahw->lb_mode = data[4] & QLCNIC_LB_MODE_MASK;
 qlcnic_advert_link_change(adapter, link_status);
}

static irqreturn_t qlcnic_83xx_handle_aen(int irq, void *data)
{
 u32 mask, resp, event, rsp_status = QLC_83XX_MBX_RESPONSE_ARRIVED;
 struct qlcnic_adapter *adapter = data;
--> --------------------

--> maximum size reached

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

Messung V0.5
C=96 H=91 G=93

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






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge