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

Quelle  qlcnic_main.c   Sprache: C

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


#include <linux/vmalloc.h>
#include <linux/interrupt.h>
#include <linux/swab.h>
#include <linux/dma-mapping.h>
#include <linux/if_vlan.h>
#include <net/ip.h>
#include <linux/ipv6.h>
#include <linux/inetdevice.h>
#include <linux/log2.h>
#include <linux/pci.h>
#include <net/vxlan.h>

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

MODULE_DESCRIPTION("QLogic 1/10 GbE Converged/Intelligent Ethernet Driver");
MODULE_LICENSE("GPL");
MODULE_VERSION(QLCNIC_LINUX_VERSIONID);
MODULE_FIRMWARE(QLCNIC_UNIFIED_ROMIMAGE_NAME);

char qlcnic_driver_name[] = "qlcnic";
static const char qlcnic_driver_string[] = "QLogic 1/10 GbE "
 "Converged/Intelligent Ethernet Driver v" QLCNIC_LINUX_VERSIONID;

static int qlcnic_mac_learn;
module_param(qlcnic_mac_learn, int, 0444);
MODULE_PARM_DESC(qlcnic_mac_learn,
   "Mac Filter (0=learning is disabled, 1=Driver learning is enabled, 2=FDB learning is enabled)");

int qlcnic_use_msi = 1;
MODULE_PARM_DESC(use_msi, "MSI interrupt (0=disabled, 1=enabled)");
module_param_named(use_msi, qlcnic_use_msi, int, 0444);

int qlcnic_use_msi_x = 1;
MODULE_PARM_DESC(use_msi_x, "MSI-X interrupt (0=disabled, 1=enabled)");
module_param_named(use_msi_x, qlcnic_use_msi_x, int, 0444);

int qlcnic_auto_fw_reset = 1;
MODULE_PARM_DESC(auto_fw_reset, "Auto firmware reset (0=disabled, 1=enabled)");
module_param_named(auto_fw_reset, qlcnic_auto_fw_reset, int, 0644);

int qlcnic_load_fw_file;
MODULE_PARM_DESC(load_fw_file, "Load firmware from (0=flash, 1=file, 2=POST in fast mode, 3= POST in medium mode, 4=POST in slow mode)");
module_param_named(load_fw_file, qlcnic_load_fw_file, int, 0444);

static int qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent);
static void qlcnic_remove(struct pci_dev *pdev);
static int qlcnic_open(struct net_device *netdev);
static int qlcnic_close(struct net_device *netdev);
static void qlcnic_tx_timeout(struct net_device *netdev, unsigned int txqueue);
static void qlcnic_attach_work(struct work_struct *work);
static void qlcnic_fwinit_work(struct work_struct *work);

static void qlcnic_idc_debug_info(struct qlcnic_adapter *adapter, u8 encoding);
static int qlcnic_can_start_firmware(struct qlcnic_adapter *adapter);

static irqreturn_t qlcnic_tmp_intr(int irq, void *data);
static irqreturn_t qlcnic_intr(int irq, void *data);
static irqreturn_t qlcnic_msi_intr(int irq, void *data);
static irqreturn_t qlcnic_msix_intr(int irq, void *data);
static irqreturn_t qlcnic_msix_tx_intr(int irq, void *data);

static struct net_device_stats *qlcnic_get_stats(struct net_device *netdev);
static int qlcnic_start_firmware(struct qlcnic_adapter *);

static void qlcnic_free_lb_filters_mem(struct qlcnic_adapter *adapter);
static void qlcnic_dev_set_npar_ready(struct qlcnic_adapter *);
static int qlcnicvf_start_firmware(struct qlcnic_adapter *);
static int qlcnic_vlan_rx_add(struct net_device *, __be16, u16);
static int qlcnic_vlan_rx_del(struct net_device *, __be16, u16);

static int qlcnic_82xx_setup_intr(struct qlcnic_adapter *);
static void qlcnic_82xx_dev_request_reset(struct qlcnic_adapter *, u32);
static irqreturn_t qlcnic_82xx_clear_legacy_intr(struct qlcnic_adapter *);
static pci_ers_result_t qlcnic_82xx_io_slot_reset(struct pci_dev *);
static int qlcnic_82xx_start_firmware(struct qlcnic_adapter *);
static void qlcnic_82xx_io_resume(struct pci_dev *);
static void qlcnic_82xx_set_mac_filter_count(struct qlcnic_adapter *);
static pci_ers_result_t qlcnic_82xx_io_error_detected(struct pci_dev *,
            pci_channel_state_t);
static u32 qlcnic_vlan_tx_check(struct qlcnic_adapter *adapter)
{
 struct qlcnic_hardware_context *ahw = adapter->ahw;

 if (adapter->pdev->device == PCI_DEVICE_ID_QLOGIC_QLE824X)
  return ahw->capabilities & QLCNIC_FW_CAPABILITY_FVLANTX;
 else
  return 1;
}

/*  PCI Device ID Table  */
#define ENTRY(device) \
 {PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, (device)), \
 .class = PCI_CLASS_NETWORK_ETHERNET << 8, .class_mask = ~0}

static const struct pci_device_id qlcnic_pci_tbl[] = {
 ENTRY(PCI_DEVICE_ID_QLOGIC_QLE824X),
 ENTRY(PCI_DEVICE_ID_QLOGIC_QLE834X),
 ENTRY(PCI_DEVICE_ID_QLOGIC_VF_QLE834X),
 ENTRY(PCI_DEVICE_ID_QLOGIC_QLE8830),
 ENTRY(PCI_DEVICE_ID_QLOGIC_VF_QLE8C30),
 ENTRY(PCI_DEVICE_ID_QLOGIC_QLE844X),
 ENTRY(PCI_DEVICE_ID_QLOGIC_VF_QLE844X),
 {0,}
};

MODULE_DEVICE_TABLE(pci, qlcnic_pci_tbl);


inline void qlcnic_update_cmd_producer(struct qlcnic_host_tx_ring *tx_ring)
{
 writel(tx_ring->producer, tx_ring->crb_cmd_producer);
}

static const u32 msi_tgt_status[8] = {
 ISR_INT_TARGET_STATUS, ISR_INT_TARGET_STATUS_F1,
 ISR_INT_TARGET_STATUS_F2, ISR_INT_TARGET_STATUS_F3,
 ISR_INT_TARGET_STATUS_F4, ISR_INT_TARGET_STATUS_F5,
 ISR_INT_TARGET_STATUS_F6, ISR_INT_TARGET_STATUS_F7
};

static const u32 qlcnic_reg_tbl[] = {
 0x1B20A8, /* PEG_HALT_STAT1 */
 0x1B20AC, /* PEG_HALT_STAT2 */
 0x1B20B0, /* FW_HEARTBEAT */
 0x1B2100, /* LOCK ID */
 0x1B2128, /* FW_CAPABILITIES */
 0x1B2138, /* drv active */
 0x1B2140, /* dev state */
 0x1B2144, /* drv state */
 0x1B2148, /* drv scratch */
 0x1B214C, /* dev partition info */
 0x1B2174, /* drv idc ver */
 0x1B2150, /* fw version major */
 0x1B2154, /* fw version minor */
 0x1B2158, /* fw version sub */
 0x1B219C, /* npar state */
 0x1B21FC, /* FW_IMG_VALID */
 0x1B2250, /* CMD_PEG_STATE */
 0x1B233C, /* RCV_PEG_STATE */
 0x1B23B4, /* ASIC TEMP */
 0x1B216C, /* FW api */
 0x1B2170, /* drv op mode */
 0x13C010, /* flash lock */
 0x13C014, /* flash unlock */
};

static const struct qlcnic_board_info qlcnic_boards[] = {
 { PCI_VENDOR_ID_QLOGIC,
   PCI_DEVICE_ID_QLOGIC_QLE844X,
   0x0,
   0x0,
   "8400 series 10GbE Converged Network Adapter (TCP/IP Networking)" },
 { PCI_VENDOR_ID_QLOGIC,
   PCI_DEVICE_ID_QLOGIC_QLE834X,
   PCI_VENDOR_ID_QLOGIC,
   0x24e,
   "8300 Series Dual Port 10GbE Converged Network Adapter "
   "(TCP/IP Networking)" },
 { PCI_VENDOR_ID_QLOGIC,
   PCI_DEVICE_ID_QLOGIC_QLE834X,
   PCI_VENDOR_ID_QLOGIC,
   0x243,
   "8300 Series Single Port 10GbE Converged Network Adapter "
   "(TCP/IP Networking)" },
 { PCI_VENDOR_ID_QLOGIC,
   PCI_DEVICE_ID_QLOGIC_QLE834X,
   PCI_VENDOR_ID_QLOGIC,
   0x24a,
   "8300 Series Dual Port 10GbE Converged Network Adapter "
   "(TCP/IP Networking)" },
 { PCI_VENDOR_ID_QLOGIC,
   PCI_DEVICE_ID_QLOGIC_QLE834X,
   PCI_VENDOR_ID_QLOGIC,
   0x246,
   "8300 Series Dual Port 10GbE Converged Network Adapter "
   "(TCP/IP Networking)" },
 { PCI_VENDOR_ID_QLOGIC,
   PCI_DEVICE_ID_QLOGIC_QLE834X,
   PCI_VENDOR_ID_QLOGIC,
   0x252,
   "8300 Series Dual Port 10GbE Converged Network Adapter "
   "(TCP/IP Networking)" },
 { PCI_VENDOR_ID_QLOGIC,
   PCI_DEVICE_ID_QLOGIC_QLE834X,
   PCI_VENDOR_ID_QLOGIC,
   0x26e,
   "8300 Series Dual Port 10GbE Converged Network Adapter "
   "(TCP/IP Networking)" },
 { PCI_VENDOR_ID_QLOGIC,
   PCI_DEVICE_ID_QLOGIC_QLE834X,
   PCI_VENDOR_ID_QLOGIC,
   0x260,
   "8300 Series Dual Port 10GbE Converged Network Adapter "
   "(TCP/IP Networking)" },
 { PCI_VENDOR_ID_QLOGIC,
   PCI_DEVICE_ID_QLOGIC_QLE834X,
   PCI_VENDOR_ID_QLOGIC,
   0x266,
   "8300 Series Single Port 10GbE Converged Network Adapter "
   "(TCP/IP Networking)" },
 { PCI_VENDOR_ID_QLOGIC,
   PCI_DEVICE_ID_QLOGIC_QLE834X,
   PCI_VENDOR_ID_QLOGIC,
   0x269,
   "8300 Series Dual Port 10GbE Converged Network Adapter "
   "(TCP/IP Networking)" },
 { PCI_VENDOR_ID_QLOGIC,
   PCI_DEVICE_ID_QLOGIC_QLE834X,
   PCI_VENDOR_ID_QLOGIC,
   0x271,
   "8300 Series Dual Port 10GbE Converged Network Adapter "
   "(TCP/IP Networking)" },
 { PCI_VENDOR_ID_QLOGIC,
   PCI_DEVICE_ID_QLOGIC_QLE834X,
   0x0, 0x0, "8300 Series 1/10GbE Controller" },
 { PCI_VENDOR_ID_QLOGIC,
   PCI_DEVICE_ID_QLOGIC_QLE8830,
   0x0,
   0x0,
   "8830 Series 1/10GbE Controller" },
 { PCI_VENDOR_ID_QLOGIC,
   PCI_DEVICE_ID_QLOGIC_QLE824X,
   PCI_VENDOR_ID_QLOGIC,
   0x203,
   "8200 Series Single Port 10GbE Converged Network Adapter"
   "(TCP/IP Networking)" },
 { PCI_VENDOR_ID_QLOGIC,
   PCI_DEVICE_ID_QLOGIC_QLE824X,
   PCI_VENDOR_ID_QLOGIC,
   0x207,
   "8200 Series Dual Port 10GbE Converged Network Adapter"
   "(TCP/IP Networking)" },
 { PCI_VENDOR_ID_QLOGIC,
   PCI_DEVICE_ID_QLOGIC_QLE824X,
   PCI_VENDOR_ID_QLOGIC,
   0x20b,
   "3200 Series Dual Port 10Gb Intelligent Ethernet Adapter" },
 { PCI_VENDOR_ID_QLOGIC,
   PCI_DEVICE_ID_QLOGIC_QLE824X,
   PCI_VENDOR_ID_QLOGIC,
   0x20c,
   "3200 Series Quad Port 1Gb Intelligent Ethernet Adapter" },
 { PCI_VENDOR_ID_QLOGIC,
   PCI_DEVICE_ID_QLOGIC_QLE824X,
   PCI_VENDOR_ID_QLOGIC,
   0x20f,
   "3200 Series Single Port 10Gb Intelligent Ethernet Adapter" },
 { PCI_VENDOR_ID_QLOGIC,
   PCI_DEVICE_ID_QLOGIC_QLE824X,
   0x103c, 0x3733,
   "NC523SFP 10Gb 2-port Server Adapter" },
 { PCI_VENDOR_ID_QLOGIC,
   PCI_DEVICE_ID_QLOGIC_QLE824X,
   0x103c, 0x3346,
   "CN1000Q Dual Port Converged Network Adapter" },
 { PCI_VENDOR_ID_QLOGIC,
   PCI_DEVICE_ID_QLOGIC_QLE824X,
   PCI_VENDOR_ID_QLOGIC,
   0x210,
   "QME8242-k 10GbE Dual Port Mezzanine Card" },
 { PCI_VENDOR_ID_QLOGIC,
   PCI_DEVICE_ID_QLOGIC_QLE824X,
   0x0, 0x0, "cLOM8214 1/10GbE Controller" },
};

#define NUM_SUPPORTED_BOARDS ARRAY_SIZE(qlcnic_boards)

static const
struct qlcnic_legacy_intr_set legacy_intr[] = QLCNIC_LEGACY_INTR_CONFIG;

int qlcnic_alloc_sds_rings(struct qlcnic_recv_context *recv_ctx, int count)
{
 int size = sizeof(struct qlcnic_host_sds_ring) * count;

 recv_ctx->sds_rings = kzalloc(size, GFP_KERNEL);

 return recv_ctx->sds_rings == NULL;
}

void qlcnic_free_sds_rings(struct qlcnic_recv_context *recv_ctx)
{
 kfree(recv_ctx->sds_rings);
 recv_ctx->sds_rings = NULL;
}

int qlcnic_read_mac_addr(struct qlcnic_adapter *adapter)
{
 struct net_device *netdev = adapter->netdev;
 struct pci_dev *pdev = adapter->pdev;
 u8 mac_addr[ETH_ALEN];
 int ret;

 ret = qlcnic_get_mac_address(adapter, mac_addr,
         adapter->ahw->pci_func);
 if (ret)
  return ret;

 eth_hw_addr_set(netdev, mac_addr);
 memcpy(adapter->mac_addr, netdev->dev_addr, netdev->addr_len);

 /* set station address */

 if (!is_valid_ether_addr(netdev->dev_addr))
  dev_warn(&pdev->dev, "Bad MAC address %pM.\n",
     netdev->dev_addr);

 return 0;
}

static void qlcnic_delete_adapter_mac(struct qlcnic_adapter *adapter)
{
 struct qlcnic_mac_vlan_list *cur;

 list_for_each_entry(cur, &adapter->mac_list, list) {
  if (ether_addr_equal_unaligned(adapter->mac_addr, cur->mac_addr)) {
   qlcnic_sre_macaddr_change(adapter, cur->mac_addr,
        0, QLCNIC_MAC_DEL);
   list_del(&cur->list);
   kfree(cur);
   return;
  }
 }
}

static int qlcnic_set_mac(struct net_device *netdev, void *p)
{
 struct qlcnic_adapter *adapter = netdev_priv(netdev);
 struct sockaddr *addr = p;

 if (qlcnic_sriov_vf_check(adapter))
  return -EINVAL;

 if ((adapter->flags & QLCNIC_MAC_OVERRIDE_DISABLED))
  return -EOPNOTSUPP;

 if (!is_valid_ether_addr(addr->sa_data))
  return -EINVAL;

 if (ether_addr_equal_unaligned(adapter->mac_addr, addr->sa_data) &&
     ether_addr_equal_unaligned(netdev->dev_addr, addr->sa_data))
  return 0;

 if (test_bit(__QLCNIC_DEV_UP, &adapter->state)) {
  netif_device_detach(netdev);
  qlcnic_napi_disable(adapter);
 }

 qlcnic_delete_adapter_mac(adapter);
 memcpy(adapter->mac_addr, addr->sa_data, netdev->addr_len);
 eth_hw_addr_set(netdev, addr->sa_data);
 qlcnic_set_multi(adapter->netdev);

 if (test_bit(__QLCNIC_DEV_UP, &adapter->state)) {
  netif_device_attach(netdev);
  qlcnic_napi_enable(adapter);
 }
 return 0;
}

static int qlcnic_fdb_del(struct ndmsg *ndm, struct nlattr *tb[],
   struct net_device *netdev,
   const unsigned char *addr, u16 vid, bool *notified,
   struct netlink_ext_ack *extack)
{
 struct qlcnic_adapter *adapter = netdev_priv(netdev);
 int err = -EOPNOTSUPP;

 if (!adapter->fdb_mac_learn)
  return ndo_dflt_fdb_del(ndm, tb, netdev, addr, vid);

 if ((adapter->flags & QLCNIC_ESWITCH_ENABLED) ||
     qlcnic_sriov_check(adapter)) {
  if (is_unicast_ether_addr(addr)) {
   err = dev_uc_del(netdev, addr);
   if (!err)
    err = qlcnic_nic_del_mac(adapter, addr);
  } else if (is_multicast_ether_addr(addr)) {
   err = dev_mc_del(netdev, addr);
  } else {
   err =  -EINVAL;
  }
 }
 return err;
}

static int qlcnic_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
   struct net_device *netdev,
   const unsigned char *addr, u16 vid, u16 flags,
   bool *notified, struct netlink_ext_ack *extack)
{
 struct qlcnic_adapter *adapter = netdev_priv(netdev);
 int err = 0;

 if (!adapter->fdb_mac_learn)
  return ndo_dflt_fdb_add(ndm, tb, netdev, addr, vid, flags);

 if (!(adapter->flags & QLCNIC_ESWITCH_ENABLED) &&
     !qlcnic_sriov_check(adapter)) {
  pr_info("%s: FDB e-switch is not enabled\n", __func__);
  return -EOPNOTSUPP;
 }

 if (ether_addr_equal(addr, adapter->mac_addr))
  return err;

 if (is_unicast_ether_addr(addr)) {
  if (netdev_uc_count(netdev) < adapter->ahw->max_uc_count)
   err = dev_uc_add_excl(netdev, addr);
  else
   err = -ENOMEM;
 } else if (is_multicast_ether_addr(addr)) {
  err = dev_mc_add_excl(netdev, addr);
 } else {
  err = -EINVAL;
 }

 return err;
}

static int qlcnic_fdb_dump(struct sk_buff *skb, struct netlink_callback *ncb,
   struct net_device *netdev,
   struct net_device *filter_dev, int *idx)
{
 struct qlcnic_adapter *adapter = netdev_priv(netdev);
 int err = 0;

 if (!adapter->fdb_mac_learn)
  return ndo_dflt_fdb_dump(skb, ncb, netdev, filter_dev, idx);

 if ((adapter->flags & QLCNIC_ESWITCH_ENABLED) ||
     qlcnic_sriov_check(adapter))
  err = ndo_dflt_fdb_dump(skb, ncb, netdev, filter_dev, idx);

 return err;
}

static void qlcnic_82xx_cancel_idc_work(struct qlcnic_adapter *adapter)
{
 while (test_and_set_bit(__QLCNIC_RESETTING, &adapter->state))
  usleep_range(10000, 11000);

 if (!adapter->fw_work.work.func)
  return;

 cancel_delayed_work_sync(&adapter->fw_work);
}

static int qlcnic_get_phys_port_id(struct net_device *netdev,
       struct netdev_phys_item_id *ppid)
{
 struct qlcnic_adapter *adapter = netdev_priv(netdev);
 struct qlcnic_hardware_context *ahw = adapter->ahw;

 if (!(adapter->flags & QLCNIC_HAS_PHYS_PORT_ID))
  return -EOPNOTSUPP;

 ppid->id_len = sizeof(ahw->phys_port_id);
 memcpy(ppid->id, ahw->phys_port_id, ppid->id_len);

 return 0;
}

static int qlcnic_udp_tunnel_sync(struct net_device *dev, unsigned int table)
{
 struct qlcnic_adapter *adapter = netdev_priv(dev);
 struct udp_tunnel_info ti;
 int err;

 udp_tunnel_nic_get_port(dev, table, 0, &ti);
 if (ti.port) {
  err = qlcnic_set_vxlan_port(adapter, ntohs(ti.port));
  if (err)
   return err;
 }

 return qlcnic_set_vxlan_parsing(adapter, ntohs(ti.port));
}

static const struct udp_tunnel_nic_info qlcnic_udp_tunnels = {
 .sync_table = qlcnic_udp_tunnel_sync,
 .tables  = {
  { .n_entries = 1, .tunnel_types = UDP_TUNNEL_TYPE_VXLAN, },
 },
};

static netdev_features_t qlcnic_features_check(struct sk_buff *skb,
            struct net_device *dev,
            netdev_features_t features)
{
 features = vlan_features_check(skb, features);
 return vxlan_features_check(skb, features);
}

static const struct net_device_ops qlcnic_netdev_ops = {
 .ndo_open    = qlcnic_open,
 .ndo_stop    = qlcnic_close,
 .ndo_start_xmit    = qlcnic_xmit_frame,
 .ndo_get_stats    = qlcnic_get_stats,
 .ndo_validate_addr = eth_validate_addr,
 .ndo_set_rx_mode   = qlcnic_set_multi,
 .ndo_set_mac_address    = qlcnic_set_mac,
 .ndo_change_mtu    = qlcnic_change_mtu,
 .ndo_fix_features  = qlcnic_fix_features,
 .ndo_set_features  = qlcnic_set_features,
 .ndo_tx_timeout    = qlcnic_tx_timeout,
 .ndo_vlan_rx_add_vid = qlcnic_vlan_rx_add,
 .ndo_vlan_rx_kill_vid = qlcnic_vlan_rx_del,
 .ndo_fdb_add  = qlcnic_fdb_add,
 .ndo_fdb_del  = qlcnic_fdb_del,
 .ndo_fdb_dump  = qlcnic_fdb_dump,
 .ndo_get_phys_port_id = qlcnic_get_phys_port_id,
 .ndo_features_check = qlcnic_features_check,
#ifdef CONFIG_QLCNIC_SRIOV
 .ndo_set_vf_mac  = qlcnic_sriov_set_vf_mac,
 .ndo_set_vf_rate = qlcnic_sriov_set_vf_tx_rate,
 .ndo_get_vf_config = qlcnic_sriov_get_vf_config,
 .ndo_set_vf_vlan = qlcnic_sriov_set_vf_vlan,
 .ndo_set_vf_spoofchk = qlcnic_sriov_set_vf_spoofchk,
#endif
};

static const struct net_device_ops qlcnic_netdev_failed_ops = {
 .ndo_open    = qlcnic_open,
};

static struct qlcnic_nic_template qlcnic_ops = {
 .config_bridged_mode = qlcnic_config_bridged_mode,
 .config_led  = qlcnic_82xx_config_led,
 .start_firmware  = qlcnic_82xx_start_firmware,
 .request_reset  = qlcnic_82xx_dev_request_reset,
 .cancel_idc_work = qlcnic_82xx_cancel_idc_work,
 .napi_add  = qlcnic_82xx_napi_add,
 .napi_del  = qlcnic_82xx_napi_del,
 .config_ipaddr  = qlcnic_82xx_config_ipaddr,
 .shutdown  = qlcnic_82xx_shutdown,
 .resume   = qlcnic_82xx_resume,
 .clear_legacy_intr = qlcnic_82xx_clear_legacy_intr,
};

struct qlcnic_nic_template qlcnic_vf_ops = {
 .config_bridged_mode = qlcnicvf_config_bridged_mode,
 .config_led  = qlcnicvf_config_led,
 .start_firmware  = qlcnicvf_start_firmware
};

static struct qlcnic_hardware_ops qlcnic_hw_ops = {
 .read_crb   = qlcnic_82xx_read_crb,
 .write_crb   = qlcnic_82xx_write_crb,
 .read_reg   = qlcnic_82xx_hw_read_wx_2M,
 .write_reg   = qlcnic_82xx_hw_write_wx_2M,
 .get_mac_address  = qlcnic_82xx_get_mac_address,
 .setup_intr   = qlcnic_82xx_setup_intr,
 .alloc_mbx_args   = qlcnic_82xx_alloc_mbx_args,
 .mbx_cmd   = qlcnic_82xx_issue_cmd,
 .get_func_no   = qlcnic_82xx_get_func_no,
 .api_lock   = qlcnic_82xx_api_lock,
 .api_unlock   = qlcnic_82xx_api_unlock,
 .add_sysfs   = qlcnic_82xx_add_sysfs,
 .remove_sysfs   = qlcnic_82xx_remove_sysfs,
 .process_lb_rcv_ring_diag = qlcnic_82xx_process_rcv_ring_diag,
 .create_rx_ctx   = qlcnic_82xx_fw_cmd_create_rx_ctx,
 .create_tx_ctx   = qlcnic_82xx_fw_cmd_create_tx_ctx,
 .del_rx_ctx   = qlcnic_82xx_fw_cmd_del_rx_ctx,
 .del_tx_ctx   = qlcnic_82xx_fw_cmd_del_tx_ctx,
 .setup_link_event  = qlcnic_82xx_linkevent_request,
 .get_nic_info   = qlcnic_82xx_get_nic_info,
 .get_pci_info   = qlcnic_82xx_get_pci_info,
 .set_nic_info   = qlcnic_82xx_set_nic_info,
 .change_macvlan   = qlcnic_82xx_sre_macaddr_change,
 .napi_enable   = qlcnic_82xx_napi_enable,
 .napi_disable   = qlcnic_82xx_napi_disable,
 .config_intr_coal  = qlcnic_82xx_config_intr_coalesce,
 .config_rss   = qlcnic_82xx_config_rss,
 .config_hw_lro   = qlcnic_82xx_config_hw_lro,
 .config_loopback  = qlcnic_82xx_set_lb_mode,
 .clear_loopback   = qlcnic_82xx_clear_lb_mode,
 .config_promisc_mode  = qlcnic_82xx_nic_set_promisc,
 .change_l2_filter  = qlcnic_82xx_change_filter,
 .get_board_info   = qlcnic_82xx_get_board_info,
 .set_mac_filter_count  = qlcnic_82xx_set_mac_filter_count,
 .free_mac_list   = qlcnic_82xx_free_mac_list,
 .read_phys_port_id  = qlcnic_82xx_read_phys_port_id,
 .io_error_detected  = qlcnic_82xx_io_error_detected,
 .io_slot_reset   = qlcnic_82xx_io_slot_reset,
 .io_resume   = qlcnic_82xx_io_resume,
 .get_beacon_state  = qlcnic_82xx_get_beacon_state,
 .enable_sds_intr  = qlcnic_82xx_enable_sds_intr,
 .disable_sds_intr  = qlcnic_82xx_disable_sds_intr,
 .enable_tx_intr   = qlcnic_82xx_enable_tx_intr,
 .disable_tx_intr  = qlcnic_82xx_disable_tx_intr,
 .get_saved_state  = qlcnic_82xx_get_saved_state,
 .set_saved_state  = qlcnic_82xx_set_saved_state,
 .cache_tmpl_hdr_values  = qlcnic_82xx_cache_tmpl_hdr_values,
 .get_cap_size   = qlcnic_82xx_get_cap_size,
 .set_sys_info   = qlcnic_82xx_set_sys_info,
 .store_cap_mask   = qlcnic_82xx_store_cap_mask,
 .encap_rx_offload               = qlcnic_82xx_encap_rx_offload,
 .encap_tx_offload               = qlcnic_82xx_encap_tx_offload,
};

static int qlcnic_check_multi_tx_capability(struct qlcnic_adapter *adapter)
{
 struct qlcnic_hardware_context *ahw = adapter->ahw;

 if (qlcnic_82xx_check(adapter) &&
     (ahw->extra_capability[0] & QLCNIC_FW_CAPABILITY_2_MULTI_TX)) {
  test_and_set_bit(__QLCNIC_MULTI_TX_UNIQUE, &adapter->state);
  return 0;
 } else {
  return 1;
 }
}

static int qlcnic_max_rings(struct qlcnic_adapter *adapter, u8 ring_cnt,
       int queue_type)
{
 int num_rings, max_rings = QLCNIC_MAX_SDS_RINGS;

 if (queue_type == QLCNIC_RX_QUEUE)
  max_rings = adapter->max_sds_rings;
 else if (queue_type == QLCNIC_TX_QUEUE)
  max_rings = adapter->max_tx_rings;

 num_rings = rounddown_pow_of_two(min_t(int, num_online_cpus(),
           max_rings));

 if (ring_cnt > num_rings)
  return num_rings;
 else
  return ring_cnt;
}

void qlcnic_set_tx_ring_count(struct qlcnic_adapter *adapter, u8 tx_cnt)
{
 /* 83xx adapter does not have max_tx_rings intialized in probe */
 if (adapter->max_tx_rings)
  adapter->drv_tx_rings = qlcnic_max_rings(adapter, tx_cnt,
        QLCNIC_TX_QUEUE);
 else
  adapter->drv_tx_rings = tx_cnt;
}

void qlcnic_set_sds_ring_count(struct qlcnic_adapter *adapter, u8 rx_cnt)
{
 /* 83xx adapter does not have max_sds_rings intialized in probe */
 if (adapter->max_sds_rings)
  adapter->drv_sds_rings = qlcnic_max_rings(adapter, rx_cnt,
         QLCNIC_RX_QUEUE);
 else
  adapter->drv_sds_rings = rx_cnt;
}

int qlcnic_setup_tss_rss_intr(struct qlcnic_adapter *adapter)
{
 struct pci_dev *pdev = adapter->pdev;
 int num_msix = 0, err = 0, vector;

 adapter->flags &= ~QLCNIC_TSS_RSS;

 if (adapter->drv_tss_rings > 0)
  num_msix += adapter->drv_tss_rings;
 else
  num_msix += adapter->drv_tx_rings;

 if (adapter->drv_rss_rings > 0)
  num_msix += adapter->drv_rss_rings;
 else
  num_msix += adapter->drv_sds_rings;

 if (qlcnic_83xx_check(adapter))
  num_msix += 1;

 if (!adapter->msix_entries) {
  adapter->msix_entries = kcalloc(num_msix,
      sizeof(struct msix_entry),
      GFP_KERNEL);
  if (!adapter->msix_entries)
   return -ENOMEM;
 }

 for (vector = 0; vector < num_msix; vector++)
  adapter->msix_entries[vector].entry = vector;

restore:
 err = pci_enable_msix_exact(pdev, adapter->msix_entries, num_msix);
 if (err == -ENOSPC) {
  if (!adapter->drv_tss_rings && !adapter->drv_rss_rings)
   return err;

  netdev_info(adapter->netdev,
       "Unable to allocate %d MSI-X vectors, Available vectors %d\n",
       num_msix, err);

  num_msix = adapter->drv_tx_rings + adapter->drv_sds_rings;

  /* Set rings to 0 so we can restore original TSS/RSS count */
  adapter->drv_tss_rings = 0;
  adapter->drv_rss_rings = 0;

  if (qlcnic_83xx_check(adapter))
   num_msix += 1;

  netdev_info(adapter->netdev,
       "Restoring %d Tx, %d SDS rings for total %d vectors.\n",
       adapter->drv_tx_rings, adapter->drv_sds_rings,
       num_msix);

  goto restore;
 } else if (err < 0) {
  return err;
 }

 adapter->ahw->num_msix = num_msix;
 if (adapter->drv_tss_rings > 0)
  adapter->drv_tx_rings = adapter->drv_tss_rings;

 if (adapter->drv_rss_rings > 0)
  adapter->drv_sds_rings = adapter->drv_rss_rings;

 return 0;
}

int qlcnic_enable_msix(struct qlcnic_adapter *adapter, u32 num_msix)
{
 struct pci_dev *pdev = adapter->pdev;
 int err, vector;

 if (!adapter->msix_entries) {
  adapter->msix_entries = kcalloc(num_msix,
      sizeof(struct msix_entry),
      GFP_KERNEL);
  if (!adapter->msix_entries)
   return -ENOMEM;
 }

 adapter->flags &= ~(QLCNIC_MSI_ENABLED | QLCNIC_MSIX_ENABLED);

 if (adapter->ahw->msix_supported) {
enable_msix:
  for (vector = 0; vector < num_msix; vector++)
   adapter->msix_entries[vector].entry = vector;

  err = pci_enable_msix_range(pdev,
         adapter->msix_entries, 1, num_msix);

  if (err == num_msix) {
   adapter->flags |= QLCNIC_MSIX_ENABLED;
   adapter->ahw->num_msix = num_msix;
   dev_info(&pdev->dev, "using msi-x interrupts\n");
   return 0;
  } else if (err > 0) {
   pci_disable_msix(pdev);

   dev_info(&pdev->dev,
     "Unable to allocate %d MSI-X vectors, Available vectors %d\n",
     num_msix, err);

   if (qlcnic_82xx_check(adapter)) {
    num_msix = rounddown_pow_of_two(err);
    if (err < QLCNIC_82XX_MINIMUM_VECTOR)
     return -ENOSPC;
   } else {
    num_msix = rounddown_pow_of_two(err - 1);
    num_msix += 1;
    if (err < QLCNIC_83XX_MINIMUM_VECTOR)
     return -ENOSPC;
   }

   if (qlcnic_82xx_check(adapter) &&
       !qlcnic_check_multi_tx(adapter)) {
    adapter->drv_sds_rings = num_msix;
    adapter->drv_tx_rings = QLCNIC_SINGLE_RING;
   } else {
    /* Distribute vectors equally */
    adapter->drv_tx_rings = num_msix / 2;
    adapter->drv_sds_rings = adapter->drv_tx_rings;
   }

   if (num_msix) {
    dev_info(&pdev->dev,
      "Trying to allocate %d MSI-X interrupt vectors\n",
      num_msix);
    goto enable_msix;
   }
  } else {
   dev_info(&pdev->dev,
     "Unable to allocate %d MSI-X vectors, err=%d\n",
     num_msix, err);
   return err;
  }
 }

 return -EIO;
}

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

 num_msix = adapter->drv_sds_rings;

 if (qlcnic_check_multi_tx(adapter))
  num_msix += adapter->drv_tx_rings;
 else
  num_msix += QLCNIC_SINGLE_RING;

 return num_msix;
}

static int qlcnic_enable_msi_legacy(struct qlcnic_adapter *adapter)
{
 int err = 0;
 u32 offset, mask_reg;
 const struct qlcnic_legacy_intr_set *legacy_intrp;
 struct qlcnic_hardware_context *ahw = adapter->ahw;
 struct pci_dev *pdev = adapter->pdev;

 if (qlcnic_use_msi && !pci_enable_msi(pdev)) {
  adapter->flags |= QLCNIC_MSI_ENABLED;
  offset = msi_tgt_status[adapter->ahw->pci_func];
  adapter->tgt_status_reg = qlcnic_get_ioaddr(adapter->ahw,
           offset);
  dev_info(&pdev->dev, "using msi interrupts\n");
  adapter->msix_entries[0].vector = pdev->irq;
  return err;
 }

 if (qlcnic_use_msi || qlcnic_use_msi_x)
  return -EOPNOTSUPP;

 legacy_intrp = &legacy_intr[adapter->ahw->pci_func];
 adapter->ahw->int_vec_bit = legacy_intrp->int_vec_bit;
 offset = legacy_intrp->tgt_status_reg;
 adapter->tgt_status_reg = qlcnic_get_ioaddr(ahw, offset);
 mask_reg = legacy_intrp->tgt_mask_reg;
 adapter->tgt_mask_reg = qlcnic_get_ioaddr(ahw, mask_reg);
 adapter->isr_int_vec = qlcnic_get_ioaddr(ahw, ISR_INT_VECTOR);
 adapter->crb_int_state_reg = qlcnic_get_ioaddr(ahw, ISR_INT_STATE_REG);
 dev_info(&pdev->dev, "using legacy interrupts\n");
 adapter->msix_entries[0].vector = pdev->irq;
 return err;
}

static int qlcnic_82xx_setup_intr(struct qlcnic_adapter *adapter)
{
 int num_msix, err = 0;

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

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

  if (!(adapter->flags & QLCNIC_MSIX_ENABLED)) {
   qlcnic_disable_multi_tx(adapter);
   adapter->drv_sds_rings = QLCNIC_SINGLE_RING;

   err = qlcnic_enable_msi_legacy(adapter);
   if (err)
    return err;
  }
 }

 return 0;
}

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

 if (qlcnic_check_multi_tx(adapter) &&
     !ahw->diag_test &&
     (adapter->flags & QLCNIC_MSIX_ENABLED)) {
  ahw->intr_tbl =
   vzalloc(array_size(sizeof(struct qlcnic_intrpt_config),
        ahw->num_msix));
  if (!ahw->intr_tbl)
   return -ENOMEM;

  for (i = 0; i < ahw->num_msix; i++) {
   ahw->intr_tbl[i].type = QLCNIC_INTRPT_MSIX;
   ahw->intr_tbl[i].id = i;
   ahw->intr_tbl[i].src = 0;
  }

  err = qlcnic_82xx_config_intrpt(adapter, 1);
  if (err)
   dev_err(&adapter->pdev->dev,
    "Failed to configure Interrupt for %d vector\n",
    ahw->num_msix);
  return err;
 }

 return 0;
}

void qlcnic_teardown_intr(struct qlcnic_adapter *adapter)
{
 if (adapter->flags & QLCNIC_MSIX_ENABLED)
  pci_disable_msix(adapter->pdev);
 if (adapter->flags & QLCNIC_MSI_ENABLED)
  pci_disable_msi(adapter->pdev);

 kfree(adapter->msix_entries);
 adapter->msix_entries = NULL;

 if (adapter->ahw->intr_tbl) {
  vfree(adapter->ahw->intr_tbl);
  adapter->ahw->intr_tbl = NULL;
 }
}

static void qlcnic_cleanup_pci_map(struct qlcnic_hardware_context *ahw)
{
 if (ahw->pci_base0 != NULL)
  iounmap(ahw->pci_base0);
}

static int qlcnic_get_act_pci_func(struct qlcnic_adapter *adapter)
{
 struct qlcnic_hardware_context *ahw = adapter->ahw;
 struct qlcnic_pci_info *pci_info;
 int ret;

 if (!(adapter->flags & QLCNIC_ESWITCH_ENABLED)) {
  switch (ahw->port_type) {
  case QLCNIC_GBE:
   ahw->total_nic_func = QLCNIC_NIU_MAX_GBE_PORTS;
   break;
  case QLCNIC_XGBE:
   ahw->total_nic_func = QLCNIC_NIU_MAX_XG_PORTS;
   break;
  }
  return 0;
 }

 if (ahw->op_mode == QLCNIC_MGMT_FUNC)
  return 0;

 pci_info = kcalloc(ahw->max_vnic_func, sizeof(*pci_info), GFP_KERNEL);
 if (!pci_info)
  return -ENOMEM;

 ret = qlcnic_get_pci_info(adapter, pci_info);
 kfree(pci_info);
 return ret;
}

static bool qlcnic_port_eswitch_cfg_capability(struct qlcnic_adapter *adapter)
{
 bool ret = false;

 if (qlcnic_84xx_check(adapter)) {
  ret = true;
 } else if (qlcnic_83xx_check(adapter)) {
  if (adapter->ahw->extra_capability[0] &
      QLCNIC_FW_CAPABILITY_2_PER_PORT_ESWITCH_CFG)
   ret = true;
  else
   ret = false;
 }

 return ret;
}

int qlcnic_init_pci_info(struct qlcnic_adapter *adapter)
{
 struct qlcnic_hardware_context *ahw = adapter->ahw;
 struct qlcnic_pci_info *pci_info;
 int i, id = 0, ret = 0, j = 0;
 u16 act_pci_func;
 u8 pfn;

 pci_info = kcalloc(ahw->max_vnic_func, sizeof(*pci_info), GFP_KERNEL);
 if (!pci_info)
  return -ENOMEM;

 ret = qlcnic_get_pci_info(adapter, pci_info);
 if (ret)
  goto err_pci_info;

 act_pci_func = ahw->total_nic_func;

 adapter->npars = kcalloc(act_pci_func,
     sizeof(struct qlcnic_npar_info),
     GFP_KERNEL);
 if (!adapter->npars) {
  ret = -ENOMEM;
  goto err_pci_info;
 }

 adapter->eswitch = kcalloc(QLCNIC_NIU_MAX_XG_PORTS,
       sizeof(struct qlcnic_eswitch),
       GFP_KERNEL);
 if (!adapter->eswitch) {
  ret = -ENOMEM;
  goto err_npars;
 }

 for (i = 0; i < ahw->max_vnic_func; i++) {
  pfn = pci_info[i].id;

  if (pfn >= ahw->max_vnic_func) {
   ret = -EINVAL;
   dev_err(&adapter->pdev->dev, "%s: Invalid function 0x%x, max 0x%x\n",
    __func__, pfn, ahw->max_vnic_func);
   goto err_eswitch;
  }

  if (!pci_info[i].active ||
      (pci_info[i].type != QLCNIC_TYPE_NIC))
   continue;

  if (qlcnic_port_eswitch_cfg_capability(adapter)) {
   if (!qlcnic_83xx_set_port_eswitch_status(adapter, pfn,
         &id))
    adapter->npars[j].eswitch_status = true;
   else
    continue;
  } else {
   adapter->npars[j].eswitch_status = true;
  }

  adapter->npars[j].pci_func = pfn;
  adapter->npars[j].active = (u8)pci_info[i].active;
  adapter->npars[j].type = (u8)pci_info[i].type;
  adapter->npars[j].phy_port = (u8)pci_info[i].default_port;
  adapter->npars[j].min_bw = pci_info[i].tx_min_bw;
  adapter->npars[j].max_bw = pci_info[i].tx_max_bw;

  memcpy(&adapter->npars[j].mac, &pci_info[i].mac, ETH_ALEN);
  j++;
 }

 /* Update eSwitch status for adapters without per port eSwitch
 * configuration capability
 */

 if (!qlcnic_port_eswitch_cfg_capability(adapter)) {
  for (i = 0; i < QLCNIC_NIU_MAX_XG_PORTS; i++)
   adapter->eswitch[i].flags |= QLCNIC_SWITCH_ENABLE;
 }

 kfree(pci_info);
 return 0;

err_eswitch:
 kfree(adapter->eswitch);
 adapter->eswitch = NULL;
err_npars:
 kfree(adapter->npars);
 adapter->npars = NULL;
err_pci_info:
 kfree(pci_info);

 return ret;
}

static int
qlcnic_set_function_modes(struct qlcnic_adapter *adapter)
{
 u8 id;
 int ret;
 u32 data = QLCNIC_MGMT_FUNC;
 struct qlcnic_hardware_context *ahw = adapter->ahw;

 ret = qlcnic_api_lock(adapter);
 if (ret)
  goto err_lock;

 id = ahw->pci_func;
 data = QLC_SHARED_REG_RD32(adapter, QLCNIC_DRV_OP_MODE);
 data = (data & ~QLC_DEV_SET_DRV(0xf, id)) |
        QLC_DEV_SET_DRV(QLCNIC_MGMT_FUNC, id);
 QLC_SHARED_REG_WR32(adapter, QLCNIC_DRV_OP_MODE, data);
 qlcnic_api_unlock(adapter);
err_lock:
 return ret;
}

static void qlcnic_check_vf(struct qlcnic_adapter *adapter,
       const struct pci_device_id *ent)
{
 u32 op_mode, priv_level;

 /* Determine FW API version */
 adapter->ahw->fw_hal_version = QLC_SHARED_REG_RD32(adapter,
          QLCNIC_FW_API);

 /* Find PCI function number */
 qlcnic_get_func_no(adapter);

 /* Determine function privilege level */
 op_mode = QLC_SHARED_REG_RD32(adapter, QLCNIC_DRV_OP_MODE);
 if (op_mode == QLC_DEV_DRV_DEFAULT)
  priv_level = QLCNIC_MGMT_FUNC;
 else
  priv_level = QLC_DEV_GET_DRV(op_mode, adapter->ahw->pci_func);

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

#define QLCNIC_82XX_BAR0_LENGTH 0x00200000UL
#define QLCNIC_83XX_BAR0_LENGTH 0x4000
static void qlcnic_get_bar_length(u32 dev_id, ulong *bar)
{
 switch (dev_id) {
 case PCI_DEVICE_ID_QLOGIC_QLE824X:
  *bar = QLCNIC_82XX_BAR0_LENGTH;
  break;
 case PCI_DEVICE_ID_QLOGIC_QLE834X:
 case PCI_DEVICE_ID_QLOGIC_QLE8830:
 case PCI_DEVICE_ID_QLOGIC_QLE844X:
 case PCI_DEVICE_ID_QLOGIC_VF_QLE834X:
 case PCI_DEVICE_ID_QLOGIC_VF_QLE844X:
 case PCI_DEVICE_ID_QLOGIC_VF_QLE8C30:
  *bar = QLCNIC_83XX_BAR0_LENGTH;
  break;
 default:
  *bar = 0;
 }
}

static int qlcnic_setup_pci_map(struct pci_dev *pdev,
    struct qlcnic_hardware_context *ahw)
{
 u32 offset;
 void __iomem *mem_ptr0 = NULL;
 unsigned long mem_len, pci_len0 = 0, bar0_len;

 /* remap phys address */
 mem_len = pci_resource_len(pdev, 0);

 qlcnic_get_bar_length(pdev->device, &bar0_len);
 if (mem_len >= bar0_len) {

  mem_ptr0 = pci_ioremap_bar(pdev, 0);
  if (mem_ptr0 == NULL) {
   dev_err(&pdev->dev, "failed to map PCI bar 0\n");
   return -EIO;
  }
  pci_len0 = mem_len;
 } else {
  return -EIO;
 }

 dev_info(&pdev->dev, "%dKB memory map\n", (int)(mem_len >> 10));

 ahw->pci_base0 = mem_ptr0;
 ahw->pci_len0 = pci_len0;
 offset = QLCNIC_PCIX_PS_REG(PCIX_OCM_WINDOW_REG(ahw->pci_func));
 qlcnic_get_ioaddr(ahw, offset);

 return 0;
}

static bool qlcnic_validate_subsystem_id(struct qlcnic_adapter *adapter,
      int index)
{
 struct pci_dev *pdev = adapter->pdev;
 unsigned short subsystem_vendor;
 bool ret = true;

 subsystem_vendor = pdev->subsystem_vendor;

 if (pdev->device == PCI_DEVICE_ID_QLOGIC_QLE824X ||
     pdev->device == PCI_DEVICE_ID_QLOGIC_QLE834X) {
  if (qlcnic_boards[index].sub_vendor == subsystem_vendor &&
      qlcnic_boards[index].sub_device == pdev->subsystem_device)
   ret = true;
  else
   ret = false;
 }

 return ret;
}

static void qlcnic_get_board_name(struct qlcnic_adapter *adapter, char *name)
{
 struct pci_dev *pdev = adapter->pdev;
 int i, found = 0;

 for (i = 0; i < NUM_SUPPORTED_BOARDS; ++i) {
  if (qlcnic_boards[i].vendor == pdev->vendor &&
      qlcnic_boards[i].device == pdev->device &&
      qlcnic_validate_subsystem_id(adapter, i)) {
   found = 1;
   break;
  }
 }

 if (!found)
  sprintf(name, "%pM Gigabit Ethernet", adapter->mac_addr);
 else
  sprintf(name, "%pM: %s" , adapter->mac_addr,
   qlcnic_boards[i].short_name);
}

static void
qlcnic_check_options(struct qlcnic_adapter *adapter)
{
 int err;
 u32 fw_major, fw_minor, fw_build, prev_fw_version;
 struct pci_dev *pdev = adapter->pdev;
 struct qlcnic_hardware_context *ahw = adapter->ahw;
 struct qlcnic_fw_dump *fw_dump = &ahw->fw_dump;

 prev_fw_version = adapter->fw_version;

 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);

 err = qlcnic_get_board_info(adapter);
 if (err) {
  dev_err(&pdev->dev, "Error getting board config info.\n");
  return;
 }
 if (ahw->op_mode != QLCNIC_NON_PRIV_FUNC) {
  if (fw_dump->tmpl_hdr == NULL ||
    adapter->fw_version > prev_fw_version) {
   vfree(fw_dump->tmpl_hdr);
   if (!qlcnic_fw_cmd_get_minidump_temp(adapter))
    dev_info(&pdev->dev,
     "Supports FW dump capability\n");
  }
 }

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

 if (adapter->ahw->port_type == QLCNIC_XGBE) {
  if (adapter->flags & QLCNIC_ESWITCH_ENABLED) {
   adapter->num_rxd = DEFAULT_RCV_DESCRIPTORS_VF;
   adapter->max_rxd = MAX_RCV_DESCRIPTORS_VF;
  } else {
   adapter->num_rxd = DEFAULT_RCV_DESCRIPTORS_10G;
   adapter->max_rxd = MAX_RCV_DESCRIPTORS_10G;
  }

  adapter->num_jumbo_rxd = MAX_JUMBO_RCV_DESCRIPTORS_10G;
  adapter->max_jumbo_rxd = MAX_JUMBO_RCV_DESCRIPTORS_10G;

 } else if (adapter->ahw->port_type == QLCNIC_GBE) {
  adapter->num_rxd = DEFAULT_RCV_DESCRIPTORS_1G;
  adapter->num_jumbo_rxd = MAX_JUMBO_RCV_DESCRIPTORS_1G;
  adapter->max_jumbo_rxd = MAX_JUMBO_RCV_DESCRIPTORS_1G;
  adapter->max_rxd = MAX_RCV_DESCRIPTORS_1G;
 }

 adapter->ahw->msix_supported = !!qlcnic_use_msi_x;

 adapter->num_txd = MAX_CMD_DESCRIPTORS;

 adapter->max_rds_rings = MAX_RDS_RINGS;
}

static int
qlcnic_initialize_nic(struct qlcnic_adapter *adapter)
{
 struct qlcnic_info nic_info;
 int err = 0;

 memset(&nic_info, 0, sizeof(struct qlcnic_info));
 err = qlcnic_get_nic_info(adapter, &nic_info, adapter->ahw->pci_func);
 if (err)
  return err;

 adapter->ahw->physical_port = (u8)nic_info.phys_port;
 adapter->ahw->switch_mode = nic_info.switch_mode;
 adapter->ahw->max_tx_ques = nic_info.max_tx_ques;
 adapter->ahw->max_rx_ques = nic_info.max_rx_ques;
 adapter->ahw->capabilities = nic_info.capabilities;

 if (adapter->ahw->capabilities & QLCNIC_FW_CAPABILITY_MORE_CAPS) {
  u32 temp;
  temp = QLCRD32(adapter, CRB_FW_CAPABILITIES_2, &err);
  if (err == -EIO)
   return err;
  adapter->ahw->extra_capability[0] = temp;
 } else {
  adapter->ahw->extra_capability[0] = 0;
 }

 adapter->ahw->max_mac_filters = nic_info.max_mac_filters;
 adapter->ahw->max_mtu = nic_info.max_mtu;

 if (adapter->ahw->capabilities & BIT_6) {
  adapter->flags |= QLCNIC_ESWITCH_ENABLED;
  adapter->ahw->nic_mode = QLCNIC_VNIC_MODE;
  adapter->max_tx_rings = QLCNIC_MAX_HW_VNIC_TX_RINGS;
  adapter->max_sds_rings = QLCNIC_MAX_VNIC_SDS_RINGS;

  dev_info(&adapter->pdev->dev, "vNIC mode enabled.\n");
 } else {
  adapter->ahw->nic_mode = QLCNIC_DEFAULT_MODE;
  adapter->max_tx_rings = QLCNIC_MAX_HW_TX_RINGS;
  adapter->max_sds_rings = QLCNIC_MAX_SDS_RINGS;
  adapter->flags &= ~QLCNIC_ESWITCH_ENABLED;
 }

 return err;
}

void qlcnic_set_vlan_config(struct qlcnic_adapter *adapter,
       struct qlcnic_esw_func_cfg *esw_cfg)
{
 if (esw_cfg->discard_tagged)
  adapter->flags &= ~QLCNIC_TAGGING_ENABLED;
 else
  adapter->flags |= QLCNIC_TAGGING_ENABLED;

 if (esw_cfg->vlan_id) {
  adapter->rx_pvid = esw_cfg->vlan_id;
  adapter->tx_pvid = esw_cfg->vlan_id;
 } else {
  adapter->rx_pvid = 0;
  adapter->tx_pvid = 0;
 }
}

static int
qlcnic_vlan_rx_add(struct net_device *netdev, __be16 proto, u16 vid)
{
 struct qlcnic_adapter *adapter = netdev_priv(netdev);
 int err;

 if (qlcnic_sriov_vf_check(adapter)) {
  err = qlcnic_sriov_cfg_vf_guest_vlan(adapter, vid, 1);
  if (err) {
   netdev_err(netdev,
       "Cannot add VLAN filter for VLAN id %d, err=%d",
       vid, err);
   return err;
  }
 }

 set_bit(vid, adapter->vlans);
 return 0;
}

static int
qlcnic_vlan_rx_del(struct net_device *netdev, __be16 proto, u16 vid)
{
 struct qlcnic_adapter *adapter = netdev_priv(netdev);
 int err;

 if (qlcnic_sriov_vf_check(adapter)) {
  err = qlcnic_sriov_cfg_vf_guest_vlan(adapter, vid, 0);
  if (err) {
   netdev_err(netdev,
       "Cannot delete VLAN filter for VLAN id %d, err=%d",
       vid, err);
   return err;
  }
 }

 qlcnic_restore_indev_addr(netdev, NETDEV_DOWN);
 clear_bit(vid, adapter->vlans);
 return 0;
}

void qlcnic_set_eswitch_port_features(struct qlcnic_adapter *adapter,
          struct qlcnic_esw_func_cfg *esw_cfg)
{
 adapter->flags &= ~(QLCNIC_MACSPOOF | QLCNIC_MAC_OVERRIDE_DISABLED |
    QLCNIC_PROMISC_DISABLED);

 if (esw_cfg->mac_anti_spoof)
  adapter->flags |= QLCNIC_MACSPOOF;

 if (!esw_cfg->mac_override)
  adapter->flags |= QLCNIC_MAC_OVERRIDE_DISABLED;

 if (!esw_cfg->promisc_mode)
  adapter->flags |= QLCNIC_PROMISC_DISABLED;
}

int qlcnic_set_eswitch_port_config(struct qlcnic_adapter *adapter)
{
 struct qlcnic_esw_func_cfg esw_cfg;

 if (!(adapter->flags & QLCNIC_ESWITCH_ENABLED))
  return 0;

 esw_cfg.pci_func = adapter->ahw->pci_func;
 if (qlcnic_get_eswitch_port_config(adapter, &esw_cfg))
   return -EIO;
 qlcnic_set_vlan_config(adapter, &esw_cfg);
 qlcnic_set_eswitch_port_features(adapter, &esw_cfg);
 qlcnic_set_netdev_features(adapter, &esw_cfg);

 return 0;
}

void qlcnic_set_netdev_features(struct qlcnic_adapter *adapter,
    struct qlcnic_esw_func_cfg *esw_cfg)
{
 struct net_device *netdev = adapter->netdev;

 if (qlcnic_83xx_check(adapter))
  return;

 adapter->offload_flags = esw_cfg->offload_flags;
 adapter->flags |= QLCNIC_APP_CHANGED_FLAGS;
 netdev_update_features(netdev);
 adapter->flags &= ~QLCNIC_APP_CHANGED_FLAGS;
}

static int
qlcnic_check_eswitch_mode(struct qlcnic_adapter *adapter)
{
 u32 op_mode, priv_level;
 int err = 0;

 err = qlcnic_initialize_nic(adapter);
 if (err)
  return err;

 if (adapter->flags & QLCNIC_ADAPTER_INITIALIZED)
  return 0;

 op_mode = QLC_SHARED_REG_RD32(adapter, QLCNIC_DRV_OP_MODE);
 priv_level = QLC_DEV_GET_DRV(op_mode, adapter->ahw->pci_func);

 if (op_mode == QLC_DEV_DRV_DEFAULT)
  priv_level = QLCNIC_MGMT_FUNC;
 else
  priv_level = QLC_DEV_GET_DRV(op_mode, adapter->ahw->pci_func);

 if (adapter->flags & QLCNIC_ESWITCH_ENABLED) {
  if (priv_level == QLCNIC_MGMT_FUNC) {
   adapter->ahw->op_mode = QLCNIC_MGMT_FUNC;
   err = qlcnic_init_pci_info(adapter);
   if (err)
    return err;
   /* Set privilege level for other functions */
   qlcnic_set_function_modes(adapter);
   dev_info(&adapter->pdev->dev,
    "HAL Version: %d, Management function\n",
     adapter->ahw->fw_hal_version);
  } else if (priv_level == QLCNIC_PRIV_FUNC) {
   adapter->ahw->op_mode = QLCNIC_PRIV_FUNC;
   dev_info(&adapter->pdev->dev,
    "HAL Version: %d, Privileged function\n",
     adapter->ahw->fw_hal_version);
  }
 } else {
  adapter->ahw->nic_mode = QLCNIC_DEFAULT_MODE;
 }

 adapter->flags |= QLCNIC_ADAPTER_INITIALIZED;

 return err;
}

int qlcnic_set_default_offload_settings(struct qlcnic_adapter *adapter)
{
 struct qlcnic_esw_func_cfg esw_cfg;
 struct qlcnic_npar_info *npar;
 u8 i;

 if (adapter->need_fw_reset)
  return 0;

 for (i = 0; i < adapter->ahw->total_nic_func; i++) {
  if (!adapter->npars[i].eswitch_status)
   continue;

  memset(&esw_cfg, 0, sizeof(struct qlcnic_esw_func_cfg));
  esw_cfg.pci_func = adapter->npars[i].pci_func;
  esw_cfg.mac_override = BIT_0;
  esw_cfg.promisc_mode = BIT_0;
  if (qlcnic_82xx_check(adapter)) {
   esw_cfg.offload_flags = BIT_0;
   if (QLCNIC_IS_TSO_CAPABLE(adapter))
    esw_cfg.offload_flags |= (BIT_1 | BIT_2);
  }
  if (qlcnic_config_switch_port(adapter, &esw_cfg))
   return -EIO;
  npar = &adapter->npars[i];
  npar->pvid = esw_cfg.vlan_id;
  npar->mac_override = esw_cfg.mac_override;
  npar->mac_anti_spoof = esw_cfg.mac_anti_spoof;
  npar->discard_tagged = esw_cfg.discard_tagged;
  npar->promisc_mode = esw_cfg.promisc_mode;
  npar->offload_flags = esw_cfg.offload_flags;
 }

 return 0;
}


static int
qlcnic_reset_eswitch_config(struct qlcnic_adapter *adapter,
   struct qlcnic_npar_info *npar, int pci_func)
{
 struct qlcnic_esw_func_cfg esw_cfg;
 esw_cfg.op_mode = QLCNIC_PORT_DEFAULTS;
 esw_cfg.pci_func = pci_func;
 esw_cfg.vlan_id = npar->pvid;
 esw_cfg.mac_override = npar->mac_override;
 esw_cfg.discard_tagged = npar->discard_tagged;
 esw_cfg.mac_anti_spoof = npar->mac_anti_spoof;
 esw_cfg.offload_flags = npar->offload_flags;
 esw_cfg.promisc_mode = npar->promisc_mode;
 if (qlcnic_config_switch_port(adapter, &esw_cfg))
  return -EIO;

 esw_cfg.op_mode = QLCNIC_ADD_VLAN;
 if (qlcnic_config_switch_port(adapter, &esw_cfg))
  return -EIO;

 return 0;
}

int qlcnic_reset_npar_config(struct qlcnic_adapter *adapter)
{
 int i, err;
 struct qlcnic_npar_info *npar;
 struct qlcnic_info nic_info;
 u8 pci_func;

 if (qlcnic_82xx_check(adapter))
  if (!adapter->need_fw_reset)
   return 0;

 /* Set the NPAR config data after FW reset */
 for (i = 0; i < adapter->ahw->total_nic_func; i++) {
  npar = &adapter->npars[i];
  pci_func = npar->pci_func;
  if (!adapter->npars[i].eswitch_status)
   continue;

  memset(&nic_info, 0, sizeof(struct qlcnic_info));
  err = qlcnic_get_nic_info(adapter, &nic_info, pci_func);
  if (err)
   return err;
  nic_info.min_tx_bw = npar->min_bw;
  nic_info.max_tx_bw = npar->max_bw;
  err = qlcnic_set_nic_info(adapter, &nic_info);
  if (err)
   return err;

  if (npar->enable_pm) {
   err = qlcnic_config_port_mirroring(adapter,
          npar->dest_npar, 1,
          pci_func);
   if (err)
    return err;
  }
  err = qlcnic_reset_eswitch_config(adapter, npar, pci_func);
  if (err)
   return err;
 }
 return 0;
}

static int qlcnic_check_npar_opertional(struct qlcnic_adapter *adapter)
{
 u8 npar_opt_timeo = QLCNIC_DEV_NPAR_OPER_TIMEO;
 u32 npar_state;

 if (adapter->ahw->op_mode == QLCNIC_MGMT_FUNC)
  return 0;

 npar_state = QLC_SHARED_REG_RD32(adapter,
      QLCNIC_CRB_DEV_NPAR_STATE);
 while (npar_state != QLCNIC_DEV_NPAR_OPER && --npar_opt_timeo) {
  msleep(1000);
  npar_state = QLC_SHARED_REG_RD32(adapter,
       QLCNIC_CRB_DEV_NPAR_STATE);
 }
 if (!npar_opt_timeo) {
  dev_err(&adapter->pdev->dev,
   "Waiting for NPAR state to operational timeout\n");
  return -EIO;
 }
 return 0;
}

static int
qlcnic_set_mgmt_operations(struct qlcnic_adapter *adapter)
{
 int err;

 if (!(adapter->flags & QLCNIC_ESWITCH_ENABLED) ||
     adapter->ahw->op_mode != QLCNIC_MGMT_FUNC)
  return 0;

 err = qlcnic_set_default_offload_settings(adapter);
 if (err)
  return err;

 err = qlcnic_reset_npar_config(adapter);
 if (err)
  return err;

 qlcnic_dev_set_npar_ready(adapter);

 return err;
}

static int qlcnic_82xx_start_firmware(struct qlcnic_adapter *adapter)
{
 int err;

 err = qlcnic_can_start_firmware(adapter);
 if (err < 0)
  return err;
 else if (!err)
  goto check_fw_status;

 if (qlcnic_load_fw_file)
  qlcnic_request_firmware(adapter);
 else {
  err = qlcnic_check_flash_fw_ver(adapter);
  if (err)
   goto err_out;

  adapter->ahw->fw_type = QLCNIC_FLASH_ROMIMAGE;
 }

 err = qlcnic_need_fw_reset(adapter);
 if (err == 0)
  goto check_fw_status;

 err = qlcnic_pinit_from_rom(adapter);
 if (err)
  goto err_out;

 err = qlcnic_load_firmware(adapter);
 if (err)
  goto err_out;

 qlcnic_release_firmware(adapter);
 QLCWR32(adapter, CRB_DRIVER_VERSION, QLCNIC_DRIVER_VERSION);

check_fw_status:
 err = qlcnic_check_fw_status(adapter);
 if (err)
  goto err_out;

 QLC_SHARED_REG_WR32(adapter, QLCNIC_CRB_DEV_STATE, QLCNIC_DEV_READY);
 qlcnic_idc_debug_info(adapter, 1);
 err = qlcnic_check_eswitch_mode(adapter);
 if (err) {
  dev_err(&adapter->pdev->dev,
   "Memory allocation failed for eswitch\n");
  goto err_out;
 }
 err = qlcnic_set_mgmt_operations(adapter);
 if (err)
  goto err_out;

 qlcnic_check_options(adapter);
 adapter->need_fw_reset = 0;

 qlcnic_release_firmware(adapter);
 return 0;

err_out:
 QLC_SHARED_REG_WR32(adapter, QLCNIC_CRB_DEV_STATE, QLCNIC_DEV_FAILED);
 dev_err(&adapter->pdev->dev, "Device state set to failed\n");

 qlcnic_release_firmware(adapter);
 return err;
}

static int
qlcnic_request_irq(struct qlcnic_adapter *adapter)
{
 irq_handler_t handler;
 struct qlcnic_host_sds_ring *sds_ring;
 struct qlcnic_host_tx_ring *tx_ring;
 int err, ring, num_sds_rings;

 unsigned long flags = 0;
 struct net_device *netdev = adapter->netdev;
 struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx;

 if (adapter->ahw->diag_test == QLCNIC_INTERRUPT_TEST) {
  if (qlcnic_82xx_check(adapter))
   handler = qlcnic_tmp_intr;
  else
   handler = qlcnic_83xx_tmp_intr;
  if (!QLCNIC_IS_MSI_FAMILY(adapter))
   flags |= IRQF_SHARED;

 } else {
  if (adapter->flags & QLCNIC_MSIX_ENABLED)
   handler = qlcnic_msix_intr;
  else if (adapter->flags & QLCNIC_MSI_ENABLED)
   handler = qlcnic_msi_intr;
  else {
   flags |= IRQF_SHARED;
   if (qlcnic_82xx_check(adapter))
    handler = qlcnic_intr;
   else
    handler = qlcnic_83xx_intr;
  }
 }
 adapter->irq = netdev->irq;

 if (adapter->ahw->diag_test != QLCNIC_LOOPBACK_TEST) {
  if (qlcnic_82xx_check(adapter) ||
      (qlcnic_83xx_check(adapter) &&
       (adapter->flags & QLCNIC_MSIX_ENABLED))) {
   num_sds_rings = adapter->drv_sds_rings;
   for (ring = 0; ring < num_sds_rings; ring++) {
    sds_ring = &recv_ctx->sds_rings[ring];
    if (qlcnic_82xx_check(adapter) &&
        !qlcnic_check_multi_tx(adapter) &&
        (ring == (num_sds_rings - 1))) {
     if (!(adapter->flags &
           QLCNIC_MSIX_ENABLED))
      snprintf(sds_ring->name,
        sizeof(sds_ring->name),
        "qlcnic");
     else
      snprintf(sds_ring->name,
        sizeof(sds_ring->name),
        "%s-tx-0-rx-%d",
        netdev->name, ring);
    } else {
     snprintf(sds_ring->name,
       sizeof(sds_ring->name),
       "%s-rx-%d",
       netdev->name, ring);
    }
    err = request_irq(sds_ring->irq, handler, flags,
        sds_ring->name, sds_ring);
    if (err)
     return err;
   }
  }
  if ((qlcnic_82xx_check(adapter) &&
       qlcnic_check_multi_tx(adapter)) ||
      (qlcnic_83xx_check(adapter) &&
       (adapter->flags & QLCNIC_MSIX_ENABLED) &&
       !(adapter->flags & QLCNIC_TX_INTR_SHARED))) {
   handler = qlcnic_msix_tx_intr;
   for (ring = 0; ring < adapter->drv_tx_rings;
        ring++) {
    tx_ring = &adapter->tx_ring[ring];
    snprintf(tx_ring->name, sizeof(tx_ring->name),
      "%s-tx-%d", netdev->name, ring);
    err = request_irq(tx_ring->irq, handler, flags,
        tx_ring->name, tx_ring);
    if (err)
     return err;
   }
  }
 }
 return 0;
}

static void
qlcnic_free_irq(struct qlcnic_adapter *adapter)
{
 int ring;
 struct qlcnic_host_sds_ring *sds_ring;
 struct qlcnic_host_tx_ring *tx_ring;

 struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx;

 if (adapter->ahw->diag_test != QLCNIC_LOOPBACK_TEST) {
  if (qlcnic_82xx_check(adapter) ||
      (qlcnic_83xx_check(adapter) &&
       (adapter->flags & QLCNIC_MSIX_ENABLED))) {
   for (ring = 0; ring < adapter->drv_sds_rings; ring++) {
    sds_ring = &recv_ctx->sds_rings[ring];
    free_irq(sds_ring->irq, sds_ring);
   }
  }
  if ((qlcnic_83xx_check(adapter) &&
       !(adapter->flags & QLCNIC_TX_INTR_SHARED)) ||
      (qlcnic_82xx_check(adapter) &&
       qlcnic_check_multi_tx(adapter))) {
   for (ring = 0; ring < adapter->drv_tx_rings;
        ring++) {
    tx_ring = &adapter->tx_ring[ring];
    if (tx_ring->irq)
     free_irq(tx_ring->irq, tx_ring);
   }
  }
 }
}

static void qlcnic_get_lro_mss_capability(struct qlcnic_adapter *adapter)
{
 u32 capab = 0;

 if (qlcnic_82xx_check(adapter)) {
  if (adapter->ahw->extra_capability[0] &
      QLCNIC_FW_CAPABILITY_2_LRO_MAX_TCP_SEG)
   adapter->flags |= QLCNIC_FW_LRO_MSS_CAP;
 } else {
  capab = adapter->ahw->capabilities;
  if (QLC_83XX_GET_FW_LRO_MSS_CAPABILITY(capab))
   adapter->flags |= QLCNIC_FW_LRO_MSS_CAP;
 }
}

static int qlcnic_config_def_intr_coalesce(struct qlcnic_adapter *adapter)
{
 struct qlcnic_hardware_context *ahw = adapter->ahw;
 int err;

 /* Initialize interrupt coalesce parameters */
 ahw->coal.flag = QLCNIC_INTR_DEFAULT;

 if (qlcnic_83xx_check(adapter)) {
  ahw->coal.type = QLCNIC_INTR_COAL_TYPE_RX_TX;
  ahw->coal.tx_time_us = QLCNIC_DEF_INTR_COALESCE_TX_TIME_US;
  ahw->coal.tx_packets = QLCNIC_DEF_INTR_COALESCE_TX_PACKETS;
  ahw->coal.rx_time_us = QLCNIC_DEF_INTR_COALESCE_RX_TIME_US;
  ahw->coal.rx_packets = QLCNIC_DEF_INTR_COALESCE_RX_PACKETS;

  err = qlcnic_83xx_set_rx_tx_intr_coal(adapter);
 } else {
  ahw->coal.type = QLCNIC_INTR_COAL_TYPE_RX;
  ahw->coal.rx_time_us = QLCNIC_DEF_INTR_COALESCE_RX_TIME_US;
  ahw->coal.rx_packets = QLCNIC_DEF_INTR_COALESCE_RX_PACKETS;

  err = qlcnic_82xx_set_rx_coalesce(adapter);
 }

 return err;
}

int __qlcnic_up(struct qlcnic_adapter *adapter, struct net_device *netdev)
{
 int ring;
 struct qlcnic_host_rds_ring *rds_ring;

 if (adapter->is_up != QLCNIC_ADAPTER_UP_MAGIC)
  return -EIO;

 if (test_bit(__QLCNIC_DEV_UP, &adapter->state))
  return 0;

 if (qlcnic_set_eswitch_port_config(adapter))
  return -EIO;

 qlcnic_get_lro_mss_capability(adapter);

 if (qlcnic_fw_create_ctx(adapter))
  return -EIO;

 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);
 }

 qlcnic_set_multi(netdev);
 qlcnic_fw_cmd_set_mtu(adapter, netdev->mtu);

 adapter->ahw->linkup = 0;

 if (adapter->drv_sds_rings > 1)
  qlcnic_config_rss(adapter, 1);

 qlcnic_config_def_intr_coalesce(adapter);

 if (netdev->features & NETIF_F_LRO)
  qlcnic_config_hw_lro(adapter, QLCNIC_LRO_ENABLED);

 set_bit(__QLCNIC_DEV_UP, &adapter->state);
 qlcnic_napi_enable(adapter);

 qlcnic_linkevent_request(adapter, 1);

 adapter->ahw->reset_context = 0;
 netif_tx_start_all_queues(netdev);
 return 0;
}

int qlcnic_up(struct qlcnic_adapter *adapter, struct net_device *netdev)
{
 int err = 0;

 rtnl_lock();
 if (netif_running(netdev))
  err = __qlcnic_up(adapter, netdev);
 rtnl_unlock();

 return err;
}

void __qlcnic_down(struct qlcnic_adapter *adapter, struct net_device *netdev)
{
 int ring;

 if (adapter->is_up != QLCNIC_ADAPTER_UP_MAGIC)
  return;

 if (!test_and_clear_bit(__QLCNIC_DEV_UP, &adapter->state))
  return;

 smp_mb();
 netif_carrier_off(netdev);
 adapter->ahw->linkup = 0;
 netif_tx_disable(netdev);

 qlcnic_free_mac_list(adapter);

 if (adapter->fhash.fnum)
  qlcnic_delete_lb_filters(adapter);

 qlcnic_nic_set_promisc(adapter, QLCNIC_NIU_NON_PROMISC_MODE);
 if (qlcnic_sriov_vf_check(adapter))
  qlcnic_sriov_cleanup_async_list(&adapter->ahw->sriov->bc);

 qlcnic_napi_disable(adapter);

 qlcnic_fw_destroy_ctx(adapter);
 adapter->flags &= ~QLCNIC_FW_LRO_MSS_CAP;

 qlcnic_reset_rx_buffers_list(adapter);

 for (ring = 0; ring < adapter->drv_tx_rings; ring++)
  qlcnic_release_tx_buffers(adapter, &adapter->tx_ring[ring]);
}

/* Usage: During suspend and firmware recovery module */

void qlcnic_down(struct qlcnic_adapter *adapter, struct net_device *netdev)
{
 rtnl_lock();
 if (netif_running(netdev))
  __qlcnic_down(adapter, netdev);
 rtnl_unlock();

}

int
qlcnic_attach(struct qlcnic_adapter *adapter)
{
 struct net_device *netdev = adapter->netdev;
 struct pci_dev *pdev = adapter->pdev;
 int err;

 if (adapter->is_up == QLCNIC_ADAPTER_UP_MAGIC)
  return 0;

 err = qlcnic_napi_add(adapter, netdev);
 if (err)
  return err;

 err = qlcnic_alloc_sw_resources(adapter);
 if (err) {
  dev_err(&pdev->dev, "Error in setting sw resources\n");
  goto err_out_napi_del;
 }

 err = qlcnic_alloc_hw_resources(adapter);
 if (err) {
  dev_err(&pdev->dev, "Error in setting hw resources\n");
  goto err_out_free_sw;
 }

 err = qlcnic_request_irq(adapter);
 if (err) {
  dev_err(&pdev->dev, "failed to setup interrupt\n");
  goto err_out_free_hw;
 }

 qlcnic_create_sysfs_entries(adapter);

 if (qlcnic_encap_rx_offload(adapter))
  udp_tunnel_nic_reset_ntf(netdev);

 adapter->is_up = QLCNIC_ADAPTER_UP_MAGIC;
 return 0;

err_out_free_hw:
 qlcnic_free_hw_resources(adapter);
err_out_free_sw:
 qlcnic_free_sw_resources(adapter);
err_out_napi_del:
 qlcnic_napi_del(adapter);
 return err;
}

void qlcnic_detach(struct qlcnic_adapter *adapter)
{
 if (adapter->is_up != QLCNIC_ADAPTER_UP_MAGIC)
  return;

 qlcnic_remove_sysfs_entries(adapter);

 qlcnic_free_hw_resources(adapter);
 qlcnic_release_rx_buffers(adapter);
 qlcnic_free_irq(adapter);
 qlcnic_napi_del(adapter);
 qlcnic_free_sw_resources(adapter);

 adapter->is_up = 0;
}

void qlcnic_diag_free_res(struct net_device *netdev, int drv_sds_rings)
{
 struct qlcnic_adapter *adapter = netdev_priv(netdev);
 struct qlcnic_host_sds_ring *sds_ring;
 int drv_tx_rings = adapter->drv_tx_rings;
 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];
   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;
 adapter->drv_tx_rings = drv_tx_rings;

 if (qlcnic_attach(adapter))
  goto out;

 if (netif_running(netdev))
  __qlcnic_up(adapter, netdev);
out:
 netif_device_attach(netdev);
}

static int qlcnic_alloc_adapter_resources(struct qlcnic_adapter *adapter)
{
 struct qlcnic_hardware_context *ahw = adapter->ahw;
 int err = 0;

 adapter->recv_ctx = kzalloc(sizeof(struct qlcnic_recv_context),
    GFP_KERNEL);
 if (!adapter->recv_ctx) {
  err = -ENOMEM;
  goto err_out;
 }

 if (qlcnic_83xx_check(adapter)) {
  ahw->coal.type = QLCNIC_INTR_COAL_TYPE_RX_TX;
  ahw->coal.tx_time_us = QLCNIC_DEF_INTR_COALESCE_TX_TIME_US;
  ahw->coal.tx_packets = QLCNIC_DEF_INTR_COALESCE_TX_PACKETS;
  ahw->coal.rx_time_us = QLCNIC_DEF_INTR_COALESCE_RX_TIME_US;
  ahw->coal.rx_packets = QLCNIC_DEF_INTR_COALESCE_RX_PACKETS;
 } else {
  ahw->coal.type = QLCNIC_INTR_COAL_TYPE_RX;
  ahw->coal.rx_time_us = QLCNIC_DEF_INTR_COALESCE_RX_TIME_US;
  ahw->coal.rx_packets = QLCNIC_DEF_INTR_COALESCE_RX_PACKETS;
 }

 /* clear stats */
 memset(&adapter->stats, 0, sizeof(adapter->stats));
err_out:
 return err;
}

static void qlcnic_free_adapter_resources(struct qlcnic_adapter *adapter)
{
 struct qlcnic_fw_dump *fw_dump = &adapter->ahw->fw_dump;

 kfree(adapter->recv_ctx);
 adapter->recv_ctx = NULL;

 if (fw_dump->tmpl_hdr) {
  vfree(fw_dump->tmpl_hdr);
  fw_dump->tmpl_hdr = NULL;
 }

 if (fw_dump->dma_buffer) {
  dma_free_coherent(&adapter->pdev->dev, QLC_PEX_DMA_READ_SIZE,
      fw_dump->dma_buffer, fw_dump->phys_addr);
  fw_dump->dma_buffer = NULL;
 }

 kfree(adapter->ahw->reset.buff);
 adapter->ahw->fw_dump.tmpl_hdr = NULL;
}

int qlcnic_diag_alloc_res(struct net_device *netdev, int test)
{
 struct qlcnic_adapter *adapter = netdev_priv(netdev);
 struct qlcnic_host_sds_ring *sds_ring;
 struct qlcnic_host_rds_ring *rds_ring;
 int 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);
  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;
  qlcnic_linkevent_request(adapter, 1);
 }

 set_bit(__QLCNIC_DEV_UP, &adapter->state);

 return 0;
}

/* Reset context in hardware only */
static int
qlcnic_reset_hw_context(struct qlcnic_adapter *adapter)
{
 struct net_device *netdev = adapter->netdev;

 if (test_and_set_bit(__QLCNIC_RESETTING, &adapter->state))
  return -EBUSY;

 netif_device_detach(netdev);

 qlcnic_down(adapter, netdev);

 qlcnic_up(adapter, netdev);

 netif_device_attach(netdev);

 clear_bit(__QLCNIC_RESETTING, &adapter->state);
 netdev_info(adapter->netdev, "%s: soft reset complete\n", __func__);
 return 0;
}

int
qlcnic_reset_context(struct qlcnic_adapter *adapter)
{
 int err = 0;
 struct net_device *netdev = adapter->netdev;

 if (test_and_set_bit(__QLCNIC_RESETTING, &adapter->state))
  return -EBUSY;

 if (adapter->is_up == QLCNIC_ADAPTER_UP_MAGIC) {

  netif_device_detach(netdev);

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

  qlcnic_detach(adapter);

  if (netif_running(netdev)) {
   err = qlcnic_attach(adapter);
   if (!err) {
    __qlcnic_up(adapter, netdev);
    qlcnic_restore_indev_addr(netdev, NETDEV_UP);
   }
  }

  netif_device_attach(netdev);
 }

 clear_bit(__QLCNIC_RESETTING, &adapter->state);
 return err;
}

static void qlcnic_82xx_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 = QLCNIC_MAX_MC_COUNT;
 if (act_pci_fn <= 2)
  count = (QLCNIC_MAX_UC_COUNT - QLCNIC_MAX_MC_COUNT) /
    act_pci_fn;
 else
  count = (QLCNIC_LB_MAX_FILTERS - QLCNIC_MAX_MC_COUNT) /
    act_pci_fn;
 ahw->max_uc_count = count;
}

static int qlcnic_set_real_num_queues(struct qlcnic_adapter *adapter,
          u8 tx_queues, u8 rx_queues)
{
 struct net_device *netdev = adapter->netdev;
 int err = 0;

 if (tx_queues) {
  err = netif_set_real_num_tx_queues(netdev, tx_queues);
  if (err) {
   netdev_err(netdev, "failed to set %d Tx queues\n",
       tx_queues);
   return err;
  }
 }

 if (rx_queues) {
  err = netif_set_real_num_rx_queues(netdev, rx_queues);
  if (err)
   netdev_err(netdev, "failed to set %d Rx queues\n",
       rx_queues);
 }

 return err;
}

int
qlcnic_setup_netdev(struct qlcnic_adapter *adapter, struct net_device *netdev)
{
 int err;
 struct pci_dev *pdev = adapter->pdev;

 adapter->rx_csum = 1;
 adapter->ahw->mc_enabled = 0;
 qlcnic_set_mac_filter_count(adapter);

 netdev->netdev_ops    = &qlcnic_netdev_ops;
 netdev->watchdog_timeo     = QLCNIC_WATCHDOG_TIMEOUTVALUE * HZ;

 qlcnic_change_mtu(netdev, netdev->mtu);

 netdev->ethtool_ops = (qlcnic_sriov_vf_check(adapter)) ?
  &qlcnic_sriov_vf_ethtool_ops : &qlcnic_ethtool_ops;

 netdev->features |= (NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_RXCSUM |
        NETIF_F_IPV6_CSUM | NETIF_F_GRO |
        NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HIGHDMA);
 netdev->vlan_features |= (NETIF_F_SG | NETIF_F_IP_CSUM |
      NETIF_F_IPV6_CSUM | NETIF_F_HIGHDMA);

 if (QLCNIC_IS_TSO_CAPABLE(adapter)) {
  netdev->features |= (NETIF_F_TSO | NETIF_F_TSO6);
  netdev->vlan_features |= (NETIF_F_TSO | NETIF_F_TSO6);
 }

 if (qlcnic_vlan_tx_check(adapter))
  netdev->features |= (NETIF_F_HW_VLAN_CTAG_TX);

 if (qlcnic_sriov_vf_check(adapter))
  netdev->features |= NETIF_F_HW_VLAN_CTAG_FILTER;

 if (adapter->ahw->capabilities & QLCNIC_FW_CAPABILITY_HW_LRO)
  netdev->features |= NETIF_F_LRO;

 if (qlcnic_encap_tx_offload(adapter)) {
  netdev->features |= NETIF_F_GSO_UDP_TUNNEL;

  /* encapsulation Tx offload supported by Adapter */
  netdev->hw_enc_features = NETIF_F_IP_CSUM        |
       NETIF_F_GSO_UDP_TUNNEL |
       NETIF_F_TSO            |
       NETIF_F_TSO6;
 }

 if (qlcnic_encap_rx_offload(adapter)) {
  netdev->hw_enc_features |= NETIF_F_RXCSUM;

  netdev->udp_tunnel_nic_info = &qlcnic_udp_tunnels;
 }

 netdev->hw_features = netdev->features;
 netdev->priv_flags |= IFF_UNICAST_FLT;
 netdev->irq = adapter->msix_entries[0].vector;

 /* MTU range: 68 - 9600 */
 netdev->min_mtu = P3P_MIN_MTU;
 netdev->max_mtu = P3P_MAX_MTU;

 err = qlcnic_set_real_num_queues(adapter, adapter->drv_tx_rings,
      adapter->drv_sds_rings);
 if (err)
  return err;

 qlcnic_dcb_init_dcbnl_ops(adapter->dcb);

 err = register_netdev(netdev);
 if (err) {
  dev_err(&pdev->dev, "failed to register net device\n");
  return err;
 }

 return 0;
}

void qlcnic_free_tx_rings(struct qlcnic_adapter *adapter)
{
 int ring;
 struct qlcnic_host_tx_ring *tx_ring;

 for (ring = 0; ring < adapter->drv_tx_rings; ring++) {
  tx_ring = &adapter->tx_ring[ring];
  if (tx_ring) {
   vfree(tx_ring->cmd_buf_arr);
   tx_ring->cmd_buf_arr = NULL;
  }
 }
 kfree(adapter->tx_ring);
}

int qlcnic_alloc_tx_rings(struct qlcnic_adapter *adapter,
     struct net_device *netdev)
{
 int ring, vector, index;
 struct qlcnic_host_tx_ring *tx_ring;
 struct qlcnic_cmd_buffer *cmd_buf_arr;

 tx_ring = kcalloc(adapter->drv_tx_rings,
     sizeof(struct qlcnic_host_tx_ring), GFP_KERNEL);
 if (tx_ring == NULL)
  return -ENOMEM;

 adapter->tx_ring = tx_ring;

 for (ring = 0; ring < adapter->drv_tx_rings; ring++) {
  tx_ring = &adapter->tx_ring[ring];
  tx_ring->num_desc = adapter->num_txd;
  tx_ring->txq = netdev_get_tx_queue(netdev, ring);
  cmd_buf_arr = vzalloc(TX_BUFF_RINGSIZE(tx_ring));
  if (cmd_buf_arr == NULL) {
   qlcnic_free_tx_rings(adapter);
   return -ENOMEM;
  }
  tx_ring->cmd_buf_arr = cmd_buf_arr;
  spin_lock_init(&tx_ring->tx_clean_lock);
 }

 if (qlcnic_83xx_check(adapter) ||
     (qlcnic_82xx_check(adapter) && qlcnic_check_multi_tx(adapter))) {
  for (ring = 0; ring < adapter->drv_tx_rings; ring++) {
   tx_ring = &adapter->tx_ring[ring];
   tx_ring->adapter = adapter;
   if (adapter->flags & QLCNIC_MSIX_ENABLED) {
    index = adapter->drv_sds_rings + ring;
    vector = adapter->msix_entries[index].vector;
    tx_ring->irq = vector;
   }
  }
 }

 return 0;
}

void qlcnic_set_drv_version(struct qlcnic_adapter *adapter)
{
 struct qlcnic_hardware_context *ahw = adapter->ahw;
 u32 fw_cmd = 0;

 if (qlcnic_82xx_check(adapter))
  fw_cmd = QLCNIC_CMD_82XX_SET_DRV_VER;
 else if (qlcnic_83xx_check(adapter))
  fw_cmd = QLCNIC_CMD_83XX_SET_DRV_VER;

 if (ahw->extra_capability[0] & QLCNIC_FW_CAPABILITY_SET_DRV_VER)
  qlcnic_fw_cmd_set_drv_version(adapter, fw_cmd);
}

/* Reset firmware API lock */
static void qlcnic_reset_api_lock(struct qlcnic_adapter *adapter)
{
 qlcnic_api_lock(adapter);
 qlcnic_api_unlock(adapter);
}


static int
qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
 struct net_device *netdev = NULL;
 struct qlcnic_adapter *adapter = NULL;
 struct qlcnic_hardware_context *ahw;
 char board_name[QLCNIC_MAX_BOARD_NAME_LEN + 19]; /* MAC + ": " + name */
 int err;

 err = pci_enable_device(pdev);
 if (err)
  return err;

 if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) {
  err = -ENODEV;
  goto err_out_disable_pdev;
 }

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

--> maximum size reached

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

Messung V0.5
C=98 H=92 G=94

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