qlcnic: Support SR-IOV enable and disable
authorRajesh Borundia <rajesh.borundia@qlogic.com>
Fri, 29 Mar 2013 05:46:33 +0000 (05:46 +0000)
committerDavid S. Miller <davem@davemloft.net>
Fri, 29 Mar 2013 19:46:08 +0000 (15:46 -0400)
o Add QLCNIC_SRIOV to Kconfig.
o Provide PCI sysfs hooks to enable and disable SR-IOV.
o Allow enabling only when CONFIG_QLCNIC_SRIOV is defined.
o qlcnic_sriov_pf.c has all the PF related SR-IOV
  functionality.
o qlcnic_sriov_common.c has VF functionality and SR-IOV
  functionality which is common between VF and PF.
o qlcnic_sriov.h is a common header file for SR-IOV defines.

Signed-off-by: Manish Chopra <manish.chopra@qlogic.com>
Signed-off-by: Sucheta Chakraborty <sucheta.chakraborty@qlogic.com>
Signed-off-by: Rajesh Borundia <rajesh.borundia@qlogic.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
12 files changed:
drivers/net/ethernet/qlogic/Kconfig
drivers/net/ethernet/qlogic/qlcnic/Makefile
drivers/net/ethernet/qlogic/qlcnic/qlcnic.h
drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c
drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.h
drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c
drivers/net/ethernet/qlogic/qlcnic/qlcnic_hdr.h
drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.h
drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c
drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov.h [new file with mode: 0644]
drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c [new file with mode: 0644]
drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_pf.c [new file with mode: 0644]

index a8669ad..0e17972 100644 (file)
@@ -35,6 +35,16 @@ config QLCNIC
          This driver supports QLogic QLE8240 and QLE8242 Converged Ethernet
          devices.
 
+config QLCNIC_SRIOV
+       bool "QLOGIC QLCNIC 83XX family SR-IOV Support"
+       depends on QLCNIC && PCI_IOV
+       default y
+       ---help---
+         This configuration parameter enables Single Root Input Output
+         Virtualization support for QLE83XX Converged Ethernet devices.
+         This allows for virtual function acceleration in virtualized
+         environments.
+
 config QLGE
        tristate "QLogic QLGE 10Gb Ethernet Driver Support"
        depends on PCI
index 7722a20..4b1fb3f 100644 (file)
@@ -8,4 +8,6 @@ qlcnic-y := qlcnic_hw.o qlcnic_main.o qlcnic_init.o \
        qlcnic_ethtool.o qlcnic_ctx.o qlcnic_io.o \
        qlcnic_sysfs.o qlcnic_minidump.o qlcnic_83xx_hw.o \
        qlcnic_83xx_init.o qlcnic_83xx_vnic.o \
-       qlcnic_minidump.o
+       qlcnic_minidump.o qlcnic_sriov_common.o
+
+qlcnic-$(CONFIG_QLCNIC_SRIOV) += qlcnic_sriov_pf.o
index 72bbba0..2ecf845 100644 (file)
@@ -449,6 +449,7 @@ struct qlcnic_hardware_context {
        struct qlc_83xx_idc idc;
        struct qlc_83xx_fw_info fw_info;
        struct qlcnic_intrpt_config *intr_tbl;
+       struct qlcnic_sriov *sriov;
        u32 *reg_tbl;
        u32 *ext_reg_tbl;
        u32 mbox_aen[QLC_83XX_MBX_AEN_CNT];
@@ -914,7 +915,9 @@ struct qlcnic_ipaddr {
 #define __QLCNIC_AER                   5
 #define __QLCNIC_DIAG_RES_ALLOC                6
 #define __QLCNIC_LED_ENABLE            7
-#define __QLCNIC_ELB_INPROGRESS        8
+#define __QLCNIC_ELB_INPROGRESS                8
+#define __QLCNIC_SRIOV_ENABLE          10
+#define __QLCNIC_SRIOV_CAPABLE         11
 
 #define QLCNIC_INTERRUPT_TEST          1
 #define QLCNIC_LOOPBACK_TEST           2
@@ -1051,7 +1054,11 @@ struct qlcnic_info_le {
        u8      total_pf;
        u8      total_rss_engines;
        __le16  max_vports;
-       u8      reserved2[64];
+       __le16  linkstate_reg_offset;
+       __le16  bit_offsets;
+       __le16  max_local_ipv6_addrs;
+       __le16  max_remote_ipv6_addrs;
+       u8      reserved2[56];
 } __packed;
 
 struct qlcnic_info {
@@ -1083,6 +1090,10 @@ struct qlcnic_info {
        u8      total_pf;
        u8      total_rss_engines;
        u16     max_vports;
+       u16     linkstate_reg_offset;
+       u16     bit_offsets;
+       u16     max_local_ipv6_addrs;
+       u16     max_remote_ipv6_addrs;
 };
 
 struct qlcnic_pci_info_le {
@@ -1511,6 +1522,7 @@ int qlcnic_reset_npar_config(struct qlcnic_adapter *);
 int qlcnic_set_eswitch_port_config(struct qlcnic_adapter *);
 void qlcnic_add_lb_filter(struct qlcnic_adapter *, struct sk_buff *, int,
                          __le16);
+int qlcnic_83xx_configure_opmode(struct qlcnic_adapter *adapter);
 /*
  * QLOGIC Board information
  */
@@ -1843,5 +1855,9 @@ static inline bool qlcnic_83xx_check(struct qlcnic_adapter *adapter)
        return (device == PCI_DEVICE_ID_QLOGIC_QLE834X) ? true : false;
 }
 
+static inline bool qlcnic_sriov_pf_check(struct qlcnic_adapter *adapter)
+{
+       return (adapter->ahw->op_mode == QLCNIC_SRIOV_PF_FUNC) ? true : false;
+}
 
 #endif                         /* __QLCNIC_H_ */
index 8de8ca5..0e1283d 100644 (file)
@@ -209,6 +209,7 @@ static const struct qlcnic_mailbox_metadata qlcnic_83xx_mbx_tbl[] = {
        {QLCNIC_CMD_SET_LED_CONFIG, 5, 1},
        {QLCNIC_CMD_GET_LED_CONFIG, 1, 5},
        {QLCNIC_CMD_ADD_RCV_RINGS, 130, 26},
+       {QLCNIC_CMD_CONFIG_VPORT, 4, 4},
 };
 
 static const u32 qlcnic_83xx_ext_reg_tbl[] = {
@@ -775,6 +776,9 @@ void qlcnic_83xx_check_vf(struct qlcnic_adapter *adapter,
                         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;
        }
 }
index fbb3d1d..2a05c23 100644 (file)
@@ -243,6 +243,7 @@ struct qlc_83xx_idc {
 #define QLC_83XX_GET_FW_LRO_MSS_CAPABILITY(val)        (val & 0x20000)
 #define QLC_83XX_VIRTUAL_NIC_MODE                      0xFF
 #define QLC_83XX_DEFAULT_MODE                          0x0
+#define QLC_83XX_SRIOV_MODE                            0x1
 #define QLCNIC_BRDTYPE_83XX_10G                        0x0083
 
 #define QLC_83XX_FLASH_SPI_STATUS              0x2808E010
index ba5ac69..51dd81c 100644 (file)
@@ -25,7 +25,6 @@
 #define QLC_83XX_OPCODE_POLL_READ_LIST         0x0100
 
 static int qlcnic_83xx_init_default_driver(struct qlcnic_adapter *adapter);
-static int qlcnic_83xx_configure_opmode(struct qlcnic_adapter *adapter);
 static int qlcnic_83xx_check_heartbeat(struct qlcnic_adapter *p_dev);
 static int qlcnic_83xx_restart_hw(struct qlcnic_adapter *adapter);
 
@@ -1918,6 +1917,9 @@ int qlcnic_83xx_config_default_opmode(struct qlcnic_adapter *adapter)
        qlcnic_get_func_no(adapter);
        op_mode = QLCRDX(ahw, QLC_83XX_DRV_OP_MODE);
 
+       if (test_bit(__QLCNIC_SRIOV_CAPABLE, &adapter->state))
+               op_mode = QLC_83XX_DEFAULT_OPMODE;
+
        if (op_mode == QLC_83XX_DEFAULT_OPMODE) {
                adapter->nic_ops->init_driver = qlcnic_83xx_init_default_driver;
                ahw->idc.state_entry = qlcnic_83xx_idc_ready_state_entry;
@@ -1947,6 +1949,16 @@ int qlcnic_83xx_get_nic_configuration(struct qlcnic_adapter *adapter)
        ahw->max_mac_filters = nic_info.max_mac_filters;
        ahw->max_mtu = nic_info.max_mtu;
 
+       /* VNIC mode is detected by BIT_23 in capabilities. This bit is also
+        * set in case device is SRIOV capable. VNIC and SRIOV are mutually
+        * exclusive. So in case of sriov capable device load driver in
+        * default mode
+        */
+       if (test_bit(__QLCNIC_SRIOV_CAPABLE, &adapter->state)) {
+               ahw->nic_mode = QLC_83XX_DEFAULT_MODE;
+               return ahw->nic_mode;
+       }
+
        if (ahw->capabilities & BIT_23)
                ahw->nic_mode = QLC_83XX_VIRTUAL_NIC_MODE;
        else
@@ -1955,7 +1967,7 @@ int qlcnic_83xx_get_nic_configuration(struct qlcnic_adapter *adapter)
        return ahw->nic_mode;
 }
 
-static int qlcnic_83xx_configure_opmode(struct qlcnic_adapter *adapter)
+int qlcnic_83xx_configure_opmode(struct qlcnic_adapter *adapter)
 {
        int ret;
 
index 44197ca..39dffde 100644 (file)
@@ -714,7 +714,8 @@ enum {
        QLCNIC_MGMT_FUNC        = 0,
        QLCNIC_PRIV_FUNC        = 1,
        QLCNIC_NON_PRIV_FUNC    = 2,
-       QLCNIC_UNKNOWN_FUNC_MODE = 3
+       QLCNIC_SRIOV_PF_FUNC    = 3,
+       QLCNIC_UNKNOWN_FUNC_MODE = 4
 };
 
 enum {
index 5b8749e..91db4ed 100644 (file)
@@ -83,6 +83,7 @@ enum qlcnic_regs {
 #define QLCNIC_CMD_CONFIG_PORT                 0x2e
 #define QLCNIC_CMD_TEMP_SIZE                   0x2f
 #define QLCNIC_CMD_GET_TEMP_HDR                        0x30
+#define        QLCNIC_CMD_CONFIG_VPORT                 0x32
 #define QLCNIC_CMD_GET_MAC_STATS               0x37
 #define QLCNIC_CMD_SET_DRV_VER                 0x38
 #define QLCNIC_CMD_CONFIGURE_RSS               0x41
index d8b9e3b..80a4faa 100644 (file)
@@ -9,6 +9,7 @@
 #include <linux/interrupt.h>
 
 #include "qlcnic.h"
+#include "qlcnic_sriov.h"
 #include "qlcnic_hw.h"
 
 #include <linux/swab.h>
@@ -2022,11 +2023,13 @@ static void qlcnic_remove(struct pci_dev *pdev)
                return;
 
        netdev = adapter->netdev;
+       qlcnic_sriov_pf_disable(adapter);
 
        qlcnic_cancel_idc_work(adapter);
        ahw = adapter->ahw;
 
        unregister_netdev(netdev);
+       qlcnic_sriov_cleanup(adapter);
 
        if (qlcnic_83xx_check(adapter)) {
                qlcnic_83xx_free_mbx_intr(adapter);
@@ -3430,7 +3433,10 @@ static struct pci_driver qlcnic_driver = {
        .resume = qlcnic_resume,
 #endif
        .shutdown = qlcnic_shutdown,
-       .err_handler = &qlcnic_err_handler
+       .err_handler = &qlcnic_err_handler,
+#ifdef CONFIG_QLCNIC_SRIOV
+       .sriov_configure = qlcnic_pci_sriov_configure,
+#endif
 
 };
 
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov.h
new file mode 100644 (file)
index 0000000..bb53307
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * QLogic qlcnic NIC Driver
+ * Copyright (c) 2009-2013 QLogic Corporation
+ *
+ * See LICENSE.qlcnic for copyright and licensing details.
+ */
+
+#ifndef _QLCNIC_83XX_SRIOV_H_
+#define _QLCNIC_83XX_SRIOV_H_
+
+#include "qlcnic.h"
+#include <linux/types.h>
+#include <linux/pci.h>
+
+struct qlcnic_resources {
+       u16 num_tx_mac_filters;
+       u16 num_rx_ucast_mac_filters;
+       u16 num_rx_mcast_mac_filters;
+
+       u16 num_txvlan_keys;
+
+       u16 num_rx_queues;
+       u16 num_tx_queues;
+
+       u16 num_rx_buf_rings;
+       u16 num_rx_status_rings;
+
+       u16 num_destip;
+       u32 num_lro_flows_supported;
+       u16 max_local_ipv6_addrs;
+       u16 max_remote_ipv6_addrs;
+};
+
+struct qlcnic_sriov {
+       u16                             vp_handle;
+       u8                              num_vfs;
+       struct qlcnic_resources         ff_max;
+};
+
+int qlcnic_sriov_init(struct qlcnic_adapter *, int);
+void qlcnic_sriov_cleanup(struct qlcnic_adapter *);
+void __qlcnic_sriov_cleanup(struct qlcnic_adapter *);
+
+static inline bool qlcnic_sriov_enable_check(struct qlcnic_adapter *adapter)
+{
+       return test_bit(__QLCNIC_SRIOV_ENABLE, &adapter->state) ? true : false;
+}
+
+#ifdef CONFIG_QLCNIC_SRIOV
+void qlcnic_sriov_pf_disable(struct qlcnic_adapter *);
+void qlcnic_sriov_pf_cleanup(struct qlcnic_adapter *);
+int qlcnic_pci_sriov_configure(struct pci_dev *, int);
+#else
+static inline void qlcnic_sriov_pf_disable(struct qlcnic_adapter *adapter) {}
+static inline void qlcnic_sriov_pf_cleanup(struct qlcnic_adapter *adapter) {}
+#endif
+
+#endif
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c
new file mode 100644 (file)
index 0000000..fb08ad0
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * QLogic qlcnic NIC Driver
+ * Copyright (c) 2009-2013 QLogic Corporation
+ *
+ * See LICENSE.qlcnic for copyright and licensing details.
+ */
+
+#include "qlcnic_sriov.h"
+#include "qlcnic.h"
+#include <linux/types.h>
+
+int qlcnic_sriov_init(struct qlcnic_adapter *adapter, int num_vfs)
+{
+       struct qlcnic_sriov *sriov;
+
+       if (!qlcnic_sriov_enable_check(adapter))
+               return -EIO;
+
+       sriov  = kzalloc(sizeof(struct qlcnic_sriov), GFP_KERNEL);
+       if (!sriov)
+               return -ENOMEM;
+
+       adapter->ahw->sriov = sriov;
+       sriov->num_vfs = num_vfs;
+       return 0;
+}
+
+void __qlcnic_sriov_cleanup(struct qlcnic_adapter *adapter)
+{
+       if (!qlcnic_sriov_enable_check(adapter))
+               return;
+
+       kfree(adapter->ahw->sriov);
+}
+
+void qlcnic_sriov_cleanup(struct qlcnic_adapter *adapter)
+{
+       if (qlcnic_sriov_pf_check(adapter))
+               qlcnic_sriov_pf_cleanup(adapter);
+}
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_pf.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_pf.c
new file mode 100644 (file)
index 0000000..aa5ba6e
--- /dev/null
@@ -0,0 +1,455 @@
+/*
+ * QLogic qlcnic NIC Driver
+ * Copyright (c) 2009-2013 QLogic Corporation
+ *
+ * See LICENSE.qlcnic for copyright and licensing details.
+ */
+
+#include "qlcnic_sriov.h"
+#include "qlcnic.h"
+#include <linux/types.h>
+
+#define QLCNIC_SRIOV_VF_MAX_MAC 1
+
+static int qlcnic_sriov_pf_get_vport_handle(struct qlcnic_adapter *, u8);
+
+static int qlcnic_sriov_pf_set_vport_info(struct qlcnic_adapter *adapter,
+                                         struct qlcnic_info *npar_info,
+                                         u16 vport_id)
+{
+       struct qlcnic_cmd_args cmd;
+       int err;
+
+       if (qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_SET_NIC_INFO))
+               return -ENOMEM;
+
+       cmd.req.arg[1] = (vport_id << 16) | 0x1;
+       cmd.req.arg[2] = npar_info->bit_offsets;
+       cmd.req.arg[2] |= npar_info->min_tx_bw << 16;
+       cmd.req.arg[3] = npar_info->max_tx_bw | (npar_info->max_tx_ques << 16);
+       cmd.req.arg[4] = npar_info->max_tx_mac_filters;
+       cmd.req.arg[4] |= npar_info->max_rx_mcast_mac_filters << 16;
+       cmd.req.arg[5] = npar_info->max_rx_ucast_mac_filters |
+                        (npar_info->max_rx_ip_addr << 16);
+       cmd.req.arg[6] = npar_info->max_rx_lro_flow |
+                        (npar_info->max_rx_status_rings << 16);
+       cmd.req.arg[7] = npar_info->max_rx_buf_rings |
+                        (npar_info->max_rx_ques << 16);
+       cmd.req.arg[8] = npar_info->max_tx_vlan_keys;
+       cmd.req.arg[8] |= npar_info->max_local_ipv6_addrs << 16;
+       cmd.req.arg[9] = npar_info->max_remote_ipv6_addrs;
+
+       err = qlcnic_issue_cmd(adapter, &cmd);
+       if (err)
+               dev_err(&adapter->pdev->dev,
+                       "Failed to set vport info, err=%d\n", err);
+
+       qlcnic_free_mbx_args(&cmd);
+       return err;
+}
+
+static int qlcnic_sriov_pf_cal_res_limit(struct qlcnic_adapter *adapter,
+                                        struct qlcnic_info *info, u16 func)
+{
+       struct qlcnic_sriov *sriov = adapter->ahw->sriov;
+       struct qlcnic_resources *res = &sriov->ff_max;
+       int ret = -EIO, vpid;
+       u32 temp, num_vf_macs, num_vfs, max;
+
+       vpid = qlcnic_sriov_pf_get_vport_handle(adapter, func);
+       if (vpid < 0)
+               return -EINVAL;
+
+       num_vfs = sriov->num_vfs;
+       max = num_vfs + 1;
+       info->bit_offsets = 0xffff;
+       info->min_tx_bw = 0;
+       info->max_tx_bw = MAX_BW;
+       info->max_tx_ques = res->num_tx_queues / max;
+       info->max_rx_mcast_mac_filters = res->num_rx_mcast_mac_filters;
+       num_vf_macs = QLCNIC_SRIOV_VF_MAX_MAC;
+
+       if (adapter->ahw->pci_func == func) {
+               temp = res->num_rx_mcast_mac_filters - (num_vfs * num_vf_macs);
+               info->max_rx_ucast_mac_filters = temp;
+               temp = res->num_tx_mac_filters - (num_vfs * num_vf_macs);
+               info->max_tx_mac_filters = temp;
+       } else {
+               info->max_rx_ucast_mac_filters = num_vf_macs;
+               info->max_tx_mac_filters = num_vf_macs;
+       }
+
+       info->max_rx_ip_addr = res->num_destip / max;
+       info->max_rx_status_rings = res->num_rx_status_rings / max;
+       info->max_rx_buf_rings = res->num_rx_buf_rings / max;
+       info->max_rx_ques = res->num_rx_queues / max;
+       info->max_rx_lro_flow = res->num_lro_flows_supported / max;
+       info->max_tx_vlan_keys = res->num_txvlan_keys;
+       info->max_local_ipv6_addrs = res->max_local_ipv6_addrs;
+       info->max_remote_ipv6_addrs = res->max_remote_ipv6_addrs;
+
+       ret = qlcnic_sriov_pf_set_vport_info(adapter, info, vpid);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static void qlcnic_sriov_pf_set_ff_max_res(struct qlcnic_adapter *adapter,
+                                          struct qlcnic_info *info)
+{
+       struct qlcnic_resources *ff_max = &adapter->ahw->sriov->ff_max;
+
+       ff_max->num_tx_mac_filters = info->max_tx_mac_filters;
+       ff_max->num_rx_ucast_mac_filters = info->max_rx_ucast_mac_filters;
+       ff_max->num_rx_mcast_mac_filters = info->max_rx_mcast_mac_filters;
+       ff_max->num_txvlan_keys = info->max_tx_vlan_keys;
+       ff_max->num_rx_queues = info->max_rx_ques;
+       ff_max->num_tx_queues = info->max_tx_ques;
+       ff_max->num_lro_flows_supported = info->max_rx_lro_flow;
+       ff_max->num_destip = info->max_rx_ip_addr;
+       ff_max->num_rx_buf_rings = info->max_rx_buf_rings;
+       ff_max->num_rx_status_rings = info->max_rx_status_rings;
+       ff_max->max_remote_ipv6_addrs = info->max_remote_ipv6_addrs;
+       ff_max->max_local_ipv6_addrs = info->max_local_ipv6_addrs;
+}
+
+static int qlcnic_sriov_get_pf_info(struct qlcnic_adapter *adapter,
+                                   struct qlcnic_info *npar_info)
+{
+       int err;
+       struct qlcnic_cmd_args cmd;
+
+       if (qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_NIC_INFO))
+               return -ENOMEM;
+
+       cmd.req.arg[1] = 0x2;
+       err = qlcnic_issue_cmd(adapter, &cmd);
+       if (err) {
+               dev_err(&adapter->pdev->dev,
+                       "Failed to get PF info, err=%d\n", err);
+               goto out;
+       }
+
+       npar_info->total_pf = cmd.rsp.arg[2] & 0xff;
+       npar_info->total_rss_engines = (cmd.rsp.arg[2] >> 8) & 0xff;
+       npar_info->max_vports = MSW(cmd.rsp.arg[2]);
+       npar_info->max_tx_ques =  LSW(cmd.rsp.arg[3]);
+       npar_info->max_tx_mac_filters = MSW(cmd.rsp.arg[3]);
+       npar_info->max_rx_mcast_mac_filters = LSW(cmd.rsp.arg[4]);
+       npar_info->max_rx_ucast_mac_filters = MSW(cmd.rsp.arg[4]);
+       npar_info->max_rx_ip_addr = LSW(cmd.rsp.arg[5]);
+       npar_info->max_rx_lro_flow = MSW(cmd.rsp.arg[5]);
+       npar_info->max_rx_status_rings = LSW(cmd.rsp.arg[6]);
+       npar_info->max_rx_buf_rings = MSW(cmd.rsp.arg[6]);
+       npar_info->max_rx_ques = LSW(cmd.rsp.arg[7]);
+       npar_info->max_tx_vlan_keys = MSW(cmd.rsp.arg[7]);
+       npar_info->max_local_ipv6_addrs = LSW(cmd.rsp.arg[8]);
+       npar_info->max_remote_ipv6_addrs = MSW(cmd.rsp.arg[8]);
+
+       dev_info(&adapter->pdev->dev,
+                "\n\ttotal_pf: %d,\n"
+                "\n\ttotal_rss_engines: %d max_vports: %d max_tx_ques %d,\n"
+                "\tmax_tx_mac_filters: %d max_rx_mcast_mac_filters: %d,\n"
+                "\tmax_rx_ucast_mac_filters: 0x%x, max_rx_ip_addr: %d,\n"
+                "\tmax_rx_lro_flow: %d max_rx_status_rings: %d,\n"
+                "\tmax_rx_buf_rings: %d, max_rx_ques: %d, max_tx_vlan_keys %d\n"
+                "\tmax_local_ipv6_addrs: %d, max_remote_ipv6_addrs: %d\n",
+                npar_info->total_pf, npar_info->total_rss_engines,
+                npar_info->max_vports, npar_info->max_tx_ques,
+                npar_info->max_tx_mac_filters,
+                npar_info->max_rx_mcast_mac_filters,
+                npar_info->max_rx_ucast_mac_filters, npar_info->max_rx_ip_addr,
+                npar_info->max_rx_lro_flow, npar_info->max_rx_status_rings,
+                npar_info->max_rx_buf_rings, npar_info->max_rx_ques,
+                npar_info->max_tx_vlan_keys, npar_info->max_local_ipv6_addrs,
+                npar_info->max_remote_ipv6_addrs);
+
+out:
+       qlcnic_free_mbx_args(&cmd);
+       return err;
+}
+
+static void qlcnic_sriov_pf_reset_vport_handle(struct qlcnic_adapter *adapter,
+                                              u8 func)
+{
+       struct qlcnic_sriov  *sriov = adapter->ahw->sriov;
+
+       if (adapter->ahw->pci_func == func)
+               sriov->vp_handle = 0;
+}
+
+static void qlcnic_sriov_pf_set_vport_handle(struct qlcnic_adapter *adapter,
+                                            u16 vport_handle, u8 func)
+{
+       struct qlcnic_sriov  *sriov = adapter->ahw->sriov;
+
+       if (adapter->ahw->pci_func == func)
+               sriov->vp_handle = vport_handle;
+}
+
+static int qlcnic_sriov_pf_get_vport_handle(struct qlcnic_adapter *adapter,
+                                           u8 func)
+{
+       struct qlcnic_sriov  *sriov = adapter->ahw->sriov;
+
+       if (adapter->ahw->pci_func == func)
+               return sriov->vp_handle;
+
+       return -EINVAL;
+}
+
+static int qlcnic_sriov_pf_config_vport(struct qlcnic_adapter *adapter,
+                                       u8 flag, u16 func)
+{
+       struct qlcnic_cmd_args cmd;
+       int ret;
+       int vpid;
+
+       if (qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CONFIG_VPORT))
+               return -ENOMEM;
+
+       if (flag) {
+               cmd.req.arg[3] = func << 8;
+       } else {
+               vpid = qlcnic_sriov_pf_get_vport_handle(adapter, func);
+               if (vpid < 0) {
+                       ret = -EINVAL;
+                       goto out;
+               }
+               cmd.req.arg[3] = ((vpid & 0xffff) << 8) | 1;
+       }
+
+       ret = qlcnic_issue_cmd(adapter, &cmd);
+       if (ret) {
+               dev_err(&adapter->pdev->dev,
+                       "Failed %s vport, err %d for func 0x%x\n",
+                       (flag ? "enable" : "disable"), ret, func);
+               goto out;
+       }
+
+       if (flag) {
+               vpid = cmd.rsp.arg[2] & 0xffff;
+               qlcnic_sriov_pf_set_vport_handle(adapter, vpid, func);
+       } else {
+               qlcnic_sriov_pf_reset_vport_handle(adapter, func);
+       }
+
+out:
+       qlcnic_free_mbx_args(&cmd);
+       return ret;
+}
+
+static int qlcnic_sriov_pf_cfg_eswitch(struct qlcnic_adapter *adapter,
+                                      u8 func, u8 enable)
+{
+       struct qlcnic_cmd_args cmd;
+       int err = -EIO;
+
+       if (qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_TOGGLE_ESWITCH))
+               return -ENOMEM;
+
+       cmd.req.arg[0] |= (3 << 29);
+       cmd.req.arg[1] = ((func & 0xf) << 2) | BIT_6 | BIT_1;
+       if (enable)
+               cmd.req.arg[1] |= BIT_0;
+
+       err = qlcnic_issue_cmd(adapter, &cmd);
+
+       if (err != QLCNIC_RCODE_SUCCESS) {
+               dev_err(&adapter->pdev->dev,
+                       "Failed to enable sriov eswitch%d\n", err);
+               err = -EIO;
+       }
+
+       qlcnic_free_mbx_args(&cmd);
+       return err;
+}
+
+void qlcnic_sriov_pf_cleanup(struct qlcnic_adapter *adapter)
+{
+       u8 func = adapter->ahw->pci_func;
+
+       if (!qlcnic_sriov_enable_check(adapter))
+               return;
+
+       qlcnic_sriov_pf_config_vport(adapter, 0, func);
+       qlcnic_sriov_pf_cfg_eswitch(adapter, func, 0);
+       __qlcnic_sriov_cleanup(adapter);
+       adapter->ahw->op_mode = QLCNIC_MGMT_FUNC;
+       clear_bit(__QLCNIC_SRIOV_ENABLE, &adapter->state);
+}
+
+void qlcnic_sriov_pf_disable(struct qlcnic_adapter *adapter)
+{
+       if (!qlcnic_sriov_pf_check(adapter))
+               return;
+
+       if (!qlcnic_sriov_enable_check(adapter))
+               return;
+
+       pci_disable_sriov(adapter->pdev);
+       netdev_info(adapter->netdev,
+                   "SR-IOV is disabled successfully on port %d\n",
+                   adapter->portnum);
+}
+
+static int qlcnic_pci_sriov_disable(struct qlcnic_adapter *adapter)
+{
+       struct net_device *netdev = adapter->netdev;
+
+       if (netif_running(netdev))
+               __qlcnic_down(adapter, netdev);
+
+       qlcnic_sriov_pf_disable(adapter);
+
+       qlcnic_sriov_pf_cleanup(adapter);
+
+       /* After disabling SRIOV re-init the driver in default mode
+          configure opmode based on op_mode of function
+        */
+       if (qlcnic_83xx_configure_opmode(adapter))
+               return -EIO;
+
+       if (netif_running(netdev))
+               __qlcnic_up(adapter, netdev);
+
+       return 0;
+}
+
+static int qlcnic_sriov_pf_init(struct qlcnic_adapter *adapter)
+{
+       struct qlcnic_hardware_context *ahw = adapter->ahw;
+       struct qlcnic_info nic_info, pf_info, vp_info;
+       int err;
+       u8 func = ahw->pci_func;
+
+       if (!qlcnic_sriov_enable_check(adapter))
+               return 0;
+
+       err = qlcnic_sriov_pf_cfg_eswitch(adapter, func, 1);
+       if (err)
+               goto clear_sriov_enable;
+
+       err = qlcnic_sriov_pf_config_vport(adapter, 1, func);
+       if (err)
+               goto disable_eswitch;
+
+       err = qlcnic_sriov_get_pf_info(adapter, &pf_info);
+       if (err)
+               goto delete_vport;
+
+       qlcnic_sriov_pf_set_ff_max_res(adapter, &pf_info);
+
+       err = qlcnic_get_nic_info(adapter, &nic_info, func);
+       if (err)
+               goto delete_vport;
+
+       err = qlcnic_sriov_pf_cal_res_limit(adapter, &vp_info, func);
+       if (err)
+               goto delete_vport;
+
+       ahw->physical_port = (u8) nic_info.phys_port;
+       ahw->switch_mode = nic_info.switch_mode;
+       ahw->max_mtu = nic_info.max_mtu;
+       ahw->capabilities = nic_info.capabilities;
+       ahw->nic_mode = QLC_83XX_SRIOV_MODE;
+       return err;
+
+delete_vport:
+       qlcnic_sriov_pf_config_vport(adapter, 0, func);
+
+disable_eswitch:
+       qlcnic_sriov_pf_cfg_eswitch(adapter, func, 0);
+
+clear_sriov_enable:
+       __qlcnic_sriov_cleanup(adapter);
+       adapter->ahw->op_mode = QLCNIC_MGMT_FUNC;
+       clear_bit(__QLCNIC_SRIOV_ENABLE, &adapter->state);
+       return err;
+}
+
+static int qlcnic_sriov_pf_enable(struct qlcnic_adapter *adapter, int num_vfs)
+{
+       int err;
+
+       if (!qlcnic_sriov_enable_check(adapter))
+               return 0;
+
+       err = pci_enable_sriov(adapter->pdev, num_vfs);
+       if (err)
+               qlcnic_sriov_pf_cleanup(adapter);
+
+       return err;
+}
+
+static int __qlcnic_pci_sriov_enable(struct qlcnic_adapter *adapter,
+                                    int num_vfs)
+{
+       int err = 0;
+
+       set_bit(__QLCNIC_SRIOV_ENABLE, &adapter->state);
+       adapter->ahw->op_mode = QLCNIC_SRIOV_PF_FUNC;
+
+       if (qlcnic_sriov_init(adapter, num_vfs)) {
+               clear_bit(__QLCNIC_SRIOV_ENABLE, &adapter->state);
+               adapter->ahw->op_mode = QLCNIC_MGMT_FUNC;
+               return -EIO;
+       }
+
+       if (qlcnic_sriov_pf_init(adapter))
+               return -EIO;
+
+       err = qlcnic_sriov_pf_enable(adapter, num_vfs);
+       return err;
+}
+
+static int qlcnic_pci_sriov_enable(struct qlcnic_adapter *adapter, int num_vfs)
+{
+       struct net_device *netdev = adapter->netdev;
+       int err;
+
+       if (!(adapter->flags & QLCNIC_MSIX_ENABLED)) {
+               netdev_err(netdev,
+                          "SR-IOV cannot be enabled, when legacy interrupts are enabled\n");
+               return -EIO;
+       }
+
+       if (netif_running(netdev))
+               __qlcnic_down(adapter, netdev);
+
+       err = __qlcnic_pci_sriov_enable(adapter, num_vfs);
+       if (err) {
+               netdev_info(netdev, "Failed to enable SR-IOV on port %d\n",
+                           adapter->portnum);
+
+               if (qlcnic_83xx_configure_opmode(adapter))
+                       goto error;
+       } else {
+               netdev_info(adapter->netdev,
+                           "SR-IOV is enabled successfully on port %d\n",
+                           adapter->portnum);
+       }
+       if (netif_running(netdev))
+               __qlcnic_up(adapter, netdev);
+
+error:
+       return err;
+}
+
+int qlcnic_pci_sriov_configure(struct pci_dev *dev, int num_vfs)
+{
+       struct qlcnic_adapter *adapter = pci_get_drvdata(dev);
+       int err;
+
+       if (test_and_set_bit(__QLCNIC_RESETTING, &adapter->state))
+               return -EBUSY;
+
+       if (num_vfs == 0)
+               err = qlcnic_pci_sriov_disable(adapter);
+       else
+               err = qlcnic_pci_sriov_enable(adapter, num_vfs);
+
+       clear_bit(__QLCNIC_RESETTING, &adapter->state);
+       return err;
+}