]> bbs.cooldavid.org Git - net-next-2.6.git/blobdiff - drivers/net/qlcnic/qlcnic_main.c
qlcnic: clean up qlcnic_init_pci_info()
[net-next-2.6.git] / drivers / net / qlcnic / qlcnic_main.c
index 38d8fe08b7ffb02b07d501c89f27cb38ad2203e3..6b8df55840c00f6ec098dcd3ed989e4d23b43bc0 100644 (file)
@@ -34,6 +34,7 @@
 #include <linux/ipv6.h>
 #include <linux/inetdevice.h>
 #include <linux/sysfs.h>
+#include <linux/aer.h>
 
 MODULE_DESCRIPTION("QLogic 1/10 GbE Converged/Intelligent Ethernet Driver");
 MODULE_LICENSE("GPL");
@@ -75,7 +76,6 @@ static void __devexit 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);
-static void qlcnic_tx_timeout_task(struct work_struct *work);
 static void qlcnic_attach_work(struct work_struct *work);
 static void qlcnic_fwinit_work(struct work_struct *work);
 static void qlcnic_fw_poll_work(struct work_struct *work);
@@ -107,8 +107,6 @@ static void qlcnic_config_indev_addr(struct net_device *dev, unsigned long);
 static int qlcnic_start_firmware(struct qlcnic_adapter *);
 
 static void qlcnic_dev_set_npar_ready(struct qlcnic_adapter *);
-static void qlcnicvf_clear_ilb_mode(struct qlcnic_adapter *);
-static int qlcnicvf_set_ilb_mode(struct qlcnic_adapter *);
 static int qlcnicvf_config_led(struct qlcnic_adapter *, u32, u32);
 static int qlcnicvf_config_bridged_mode(struct qlcnic_adapter *, u32);
 static int qlcnicvf_start_firmware(struct qlcnic_adapter *);
@@ -378,20 +376,9 @@ static const struct net_device_ops qlcnic_netdev_ops = {
 };
 
 static struct qlcnic_nic_template qlcnic_ops = {
-       .get_mac_addr = qlcnic_get_mac_addr,
-       .config_bridged_mode = qlcnic_config_bridged_mode,
-       .config_led = qlcnic_config_led,
-       .set_ilb_mode = qlcnic_set_ilb_mode,
-       .clear_ilb_mode = qlcnic_clear_ilb_mode,
-       .start_firmware = qlcnic_start_firmware
-};
-
-static struct qlcnic_nic_template qlcnic_pf_ops = {
        .get_mac_addr = qlcnic_get_mac_address,
        .config_bridged_mode = qlcnic_config_bridged_mode,
        .config_led = qlcnic_config_led,
-       .set_ilb_mode = qlcnic_set_ilb_mode,
-       .clear_ilb_mode = qlcnic_clear_ilb_mode,
        .start_firmware = qlcnic_start_firmware
 };
 
@@ -399,8 +386,6 @@ static struct qlcnic_nic_template qlcnic_vf_ops = {
        .get_mac_addr = qlcnic_get_mac_address,
        .config_bridged_mode = qlcnicvf_config_bridged_mode,
        .config_led = qlcnicvf_config_led,
-       .set_ilb_mode = qlcnicvf_set_ilb_mode,
-       .clear_ilb_mode = qlcnicvf_clear_ilb_mode,
        .start_firmware = qlcnicvf_start_firmware
 };
 
@@ -485,6 +470,56 @@ qlcnic_cleanup_pci_map(struct qlcnic_adapter *adapter)
                iounmap(adapter->ahw.pci_base0);
 }
 
+static int
+qlcnic_init_pci_info(struct qlcnic_adapter *adapter)
+{
+       struct qlcnic_pci_info pci_info[QLCNIC_MAX_PCI_FUNC];
+       int i, ret = 0, err;
+       u8 pfn;
+
+       adapter->npars = kzalloc(sizeof(struct qlcnic_npar_info) *
+                               QLCNIC_MAX_PCI_FUNC, GFP_KERNEL);
+       if (!adapter->npars)
+               return -ENOMEM;
+
+       adapter->eswitch = kzalloc(sizeof(struct qlcnic_eswitch) *
+                               QLCNIC_NIU_MAX_XG_PORTS, GFP_KERNEL);
+       if (!adapter->eswitch) {
+               err = -ENOMEM;
+               goto err_npars;
+       }
+
+       ret = qlcnic_get_pci_info(adapter, pci_info);
+       if (ret)
+               goto err_eswitch;
+
+       for (i = 0; i < QLCNIC_MAX_PCI_FUNC; i++) {
+               pfn = pci_info[i].id;
+               if (pfn > QLCNIC_MAX_PCI_FUNC)
+                       return QL_STATUS_INVALID_PARAM;
+               adapter->npars[pfn].active = pci_info[i].active;
+               adapter->npars[pfn].type = pci_info[i].type;
+               adapter->npars[pfn].phy_port = pci_info[i].default_port;
+               adapter->npars[pfn].mac_learning = DEFAULT_MAC_LEARN;
+               adapter->npars[pfn].min_bw = pci_info[i].tx_min_bw;
+               adapter->npars[pfn].max_bw = pci_info[i].tx_max_bw;
+       }
+
+       for (i = 0; i < QLCNIC_NIU_MAX_XG_PORTS; i++)
+               adapter->eswitch[i].flags |= QLCNIC_SWITCH_ENABLE;
+
+       return 0;
+
+err_eswitch:
+       kfree(adapter->eswitch);
+       adapter->eswitch = NULL;
+err_npars:
+       kfree(adapter->npars);
+       adapter->npars = NULL;
+
+       return ret;
+}
+
 static int
 qlcnic_set_function_modes(struct qlcnic_adapter *adapter)
 {
@@ -504,7 +539,7 @@ qlcnic_set_function_modes(struct qlcnic_adapter *adapter)
 
        if (qlcnic_config_npars) {
                for (i = 0; i < QLCNIC_MAX_PCI_FUNC; i++) {
-                       id = adapter->npars[i].id;
+                       id = i;
                        if (adapter->npars[i].type != QLCNIC_TYPE_NIC ||
                                id == adapter->ahw.pci_func)
                                continue;
@@ -529,21 +564,13 @@ qlcnic_get_driver_mode(struct qlcnic_adapter *adapter)
 {
        void __iomem *msix_base_addr;
        void __iomem *priv_op;
+       struct qlcnic_info nic_info;
        u32 func;
        u32 msix_base;
        u32 op_mode, priv_level;
 
        /* Determine FW API version */
        adapter->fw_hal_version = readl(adapter->ahw.pci_base0 + QLCNIC_FW_API);
-       if (adapter->fw_hal_version == ~0) {
-               adapter->nic_ops = &qlcnic_ops;
-               adapter->fw_hal_version = QLCNIC_FW_BASE;
-               adapter->ahw.pci_func = PCI_FUNC(adapter->pdev->devfn);
-               adapter->capabilities = QLCRD32(adapter, CRB_FW_CAPABILITIES_1);
-               dev_info(&adapter->pdev->dev,
-                       "FW does not support nic partion\n");
-               return adapter->fw_hal_version;
-       }
 
        /* Find PCI function number */
        pci_read_config_dword(adapter->pdev, QLCNIC_MSIX_TABLE_OFFSET, &func);
@@ -552,7 +579,14 @@ qlcnic_get_driver_mode(struct qlcnic_adapter *adapter)
        func = (func - msix_base)/QLCNIC_MSIX_TBL_PGSIZE;
        adapter->ahw.pci_func = func;
 
-       qlcnic_get_nic_info(adapter, adapter->ahw.pci_func);
+       if (!qlcnic_get_nic_info(adapter, &nic_info, adapter->ahw.pci_func)) {
+               adapter->capabilities = nic_info.capabilities;
+
+               if (adapter->capabilities & BIT_6)
+                       adapter->flags |= QLCNIC_ESWITCH_ENABLED;
+               else
+                       adapter->flags &= ~QLCNIC_ESWITCH_ENABLED;
+       }
 
        if (!(adapter->flags & QLCNIC_ESWITCH_ENABLED)) {
                adapter->nic_ops = &qlcnic_ops;
@@ -570,8 +604,8 @@ qlcnic_get_driver_mode(struct qlcnic_adapter *adapter)
        switch (priv_level) {
        case QLCNIC_MGMT_FUNC:
                adapter->op_mode = QLCNIC_MGMT_FUNC;
-               adapter->nic_ops = &qlcnic_pf_ops;
-               qlcnic_get_pci_info(adapter);
+               adapter->nic_ops = &qlcnic_ops;
+               qlcnic_init_pci_info(adapter);
                /* Set privilege level for other functions */
                qlcnic_set_function_modes(adapter);
                dev_info(&adapter->pdev->dev,
@@ -583,7 +617,7 @@ qlcnic_get_driver_mode(struct qlcnic_adapter *adapter)
                dev_info(&adapter->pdev->dev,
                        "HAL Version: %d, Privileged function\n",
                        adapter->fw_hal_version);
-               adapter->nic_ops = &qlcnic_pf_ops;
+               adapter->nic_ops = &qlcnic_ops;
                break;
        case QLCNIC_NON_PRIV_FUNC:
                adapter->op_mode = QLCNIC_NON_PRIV_FUNC;
@@ -673,7 +707,7 @@ qlcnic_check_options(struct qlcnic_adapter *adapter)
        int i, offset, val;
        int *ptr32;
        struct pci_dev *pdev = adapter->pdev;
-
+       struct qlcnic_info nic_info;
        adapter->driver_mismatch = 0;
 
        ptr32 = (int *)&serial_num;
@@ -715,7 +749,15 @@ qlcnic_check_options(struct qlcnic_adapter *adapter)
                adapter->num_jumbo_rxd = MAX_JUMBO_RCV_DESCRIPTORS_1G;
        }
 
-       qlcnic_get_nic_info(adapter, adapter->ahw.pci_func);
+       if (!qlcnic_get_nic_info(adapter, &nic_info, adapter->ahw.pci_func)) {
+               adapter->physical_port = nic_info.phys_port;
+               adapter->switch_mode = nic_info.switch_mode;
+               adapter->max_tx_ques = nic_info.max_tx_ques;
+               adapter->max_rx_ques = nic_info.max_rx_ques;
+               adapter->capabilities = nic_info.capabilities;
+               adapter->max_mac_filters = nic_info.max_mac_filters;
+               adapter->max_mtu = nic_info.max_mtu;
+       }
 
        adapter->msix_supported = !!use_msi_x;
        adapter->rss_supported = !!use_msi_x;
@@ -725,6 +767,50 @@ qlcnic_check_options(struct qlcnic_adapter *adapter)
        adapter->max_rds_rings = 2;
 }
 
+static int
+qlcnic_reset_npar_config(struct qlcnic_adapter *adapter)
+{
+       int i, err = 0;
+       struct qlcnic_npar_info *npar;
+       struct qlcnic_info nic_info;
+
+       if (!(adapter->flags & QLCNIC_ESWITCH_ENABLED) ||
+           !adapter->need_fw_reset)
+               return 0;
+
+       if (adapter->op_mode == QLCNIC_MGMT_FUNC) {
+               /* Set the NPAR config data after FW reset */
+               for (i = 0; i < QLCNIC_MAX_PCI_FUNC; i++) {
+                       npar = &adapter->npars[i];
+                       if (npar->type != QLCNIC_TYPE_NIC)
+                               continue;
+                       err = qlcnic_get_nic_info(adapter, &nic_info, i);
+                       if (err)
+                               goto err_out;
+                       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)
+                               goto err_out;
+
+                       if (npar->enable_pm) {
+                               err = qlcnic_config_port_mirroring(adapter,
+                                               npar->dest_npar, 1, i);
+                               if (err)
+                                       goto err_out;
+
+                       }
+                       npar->mac_learning = DEFAULT_MAC_LEARN;
+                       npar->host_vlan_tag = 0;
+                       npar->promisc_mode = 0;
+                       npar->discard_tagged = 0;
+                       npar->vlan_id = 0;
+               }
+       }
+err_out:
+       return err;
+}
+
 static int
 qlcnic_start_firmware(struct qlcnic_adapter *adapter)
 {
@@ -789,10 +875,9 @@ wait_init:
        qlcnic_idc_debug_info(adapter, 1);
 
        qlcnic_check_options(adapter);
-
-       if (adapter->flags & QLCNIC_ESWITCH_ENABLED &&
-               adapter->op_mode != QLCNIC_NON_PRIV_FUNC)
-               qlcnic_dev_set_npar_ready(adapter);
+       if (qlcnic_reset_npar_config(adapter))
+               goto err_out;
+       qlcnic_dev_set_npar_ready(adapter);
 
        adapter->need_fw_reset = 0;
 
@@ -911,6 +996,7 @@ __qlcnic_up(struct qlcnic_adapter *adapter, struct net_device *netdev)
 
        qlcnic_linkevent_request(adapter, 1);
 
+       adapter->reset_context = 0;
        set_bit(__QLCNIC_DEV_UP, &adapter->state);
        return 0;
 }
@@ -1091,6 +1177,7 @@ int qlcnic_diag_alloc_res(struct net_device *netdev, int test)
        ret = qlcnic_fw_create_ctx(adapter);
        if (ret) {
                qlcnic_detach(adapter);
+               netif_device_attach(netdev);
                return ret;
        }
 
@@ -1110,6 +1197,27 @@ int qlcnic_diag_alloc_res(struct net_device *netdev, int test)
        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);
+       return 0;
+}
+
 int
 qlcnic_reset_context(struct qlcnic_adapter *adapter)
 {
@@ -1160,10 +1268,14 @@ qlcnic_setup_netdev(struct qlcnic_adapter *adapter,
        SET_ETHTOOL_OPS(netdev, &qlcnic_ethtool_ops);
 
        netdev->features |= (NETIF_F_SG | NETIF_F_IP_CSUM |
-               NETIF_F_IPV6_CSUM | NETIF_F_GRO | NETIF_F_TSO | NETIF_F_TSO6);
-
+               NETIF_F_IPV6_CSUM | NETIF_F_GRO);
        netdev->vlan_features |= (NETIF_F_SG | NETIF_F_IP_CSUM |
-               NETIF_F_IPV6_CSUM | NETIF_F_TSO | NETIF_F_TSO6);
+               NETIF_F_IPV6_CSUM);
+
+       if (adapter->capabilities & QLCNIC_FW_CAPABILITY_TSO) {
+               netdev->features |= (NETIF_F_TSO | NETIF_F_TSO6);
+               netdev->vlan_features |= (NETIF_F_TSO | NETIF_F_TSO6);
+       }
 
        if (pci_using_dac) {
                netdev->features |= NETIF_F_HIGHDMA;
@@ -1178,8 +1290,6 @@ qlcnic_setup_netdev(struct qlcnic_adapter *adapter,
 
        netdev->irq = adapter->msix_entries[0].vector;
 
-       INIT_WORK(&adapter->tx_timeout_task, qlcnic_tx_timeout_task);
-
        if (qlcnic_read_mac_addr(adapter))
                dev_warn(&pdev->dev, "failed to read mac addr\n");
 
@@ -1238,6 +1348,7 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
                goto err_out_disable_pdev;
 
        pci_set_master(pdev);
+       pci_enable_pcie_error_reporting(pdev);
 
        netdev = alloc_etherdev(sizeof(struct qlcnic_adapter));
        if (!netdev) {
@@ -1350,8 +1461,6 @@ static void __devexit qlcnic_remove(struct pci_dev *pdev)
 
        unregister_netdev(netdev);
 
-       cancel_work_sync(&adapter->tx_timeout_task);
-
        qlcnic_detach(adapter);
 
        if (adapter->npars != NULL)
@@ -1371,6 +1480,7 @@ static void __devexit qlcnic_remove(struct pci_dev *pdev)
 
        qlcnic_release_firmware(adapter);
 
+       pci_disable_pcie_error_reporting(pdev);
        pci_release_regions(pdev);
        pci_disable_device(pdev);
        pci_set_drvdata(pdev, NULL);
@@ -1390,10 +1500,6 @@ static int __qlcnic_shutdown(struct pci_dev *pdev)
        if (netif_running(netdev))
                qlcnic_down(adapter, netdev);
 
-       cancel_work_sync(&adapter->tx_timeout_task);
-
-       qlcnic_detach(adapter);
-
        qlcnic_clr_all_drv_state(adapter);
 
        clear_bit(__QLCNIC_RESETTING, &adapter->state);
@@ -1454,28 +1560,16 @@ qlcnic_resume(struct pci_dev *pdev)
        }
 
        if (netif_running(netdev)) {
-               err = qlcnic_attach(adapter);
-               if (err)
-                       goto err_out;
-
                err = qlcnic_up(adapter, netdev);
                if (err)
-                       goto err_out_detach;
-
+                       goto done;
 
                qlcnic_config_indev_addr(netdev, NETDEV_UP);
        }
-
+done:
        netif_device_attach(netdev);
        qlcnic_schedule_work(adapter, qlcnic_fw_poll_work, FW_POLL_DELAY);
        return 0;
-
-err_out_detach:
-       qlcnic_detach(adapter);
-err_out:
-       qlcnic_clr_all_drv_state(adapter);
-       netif_device_attach(netdev);
-       return err;
 }
 #endif
 
@@ -1868,35 +1962,11 @@ static void qlcnic_tx_timeout(struct net_device *netdev)
                return;
 
        dev_err(&netdev->dev, "transmit timeout, resetting.\n");
-       schedule_work(&adapter->tx_timeout_task);
-}
-
-static void qlcnic_tx_timeout_task(struct work_struct *work)
-{
-       struct qlcnic_adapter *adapter =
-               container_of(work, struct qlcnic_adapter, tx_timeout_task);
-
-       if (!netif_running(adapter->netdev))
-               return;
-
-       if (test_and_set_bit(__QLCNIC_RESETTING, &adapter->state))
-               return;
 
        if (++adapter->tx_timeo_cnt >= QLCNIC_MAX_TX_TIMEOUTS)
-               goto request_reset;
-
-       clear_bit(__QLCNIC_RESETTING, &adapter->state);
-       if (!qlcnic_reset_context(adapter)) {
-               adapter->netdev->trans_start = jiffies;
-               return;
-
-               /* context reset failed, fall through for fw reset */
-       }
-
-request_reset:
-       adapter->need_fw_reset = 1;
-       clear_bit(__QLCNIC_RESETTING, &adapter->state);
-       QLCDB(adapter, DRV, "Resetting adapter\n");
+               adapter->need_fw_reset = 1;
+       else
+               adapter->reset_context = 1;
 }
 
 static struct net_device_stats *qlcnic_get_stats(struct net_device *netdev)
@@ -2426,10 +2496,6 @@ qlcnic_detach_work(struct work_struct *work)
 
        qlcnic_down(adapter, netdev);
 
-       rtnl_lock();
-       qlcnic_detach(adapter);
-       rtnl_unlock();
-
        status = QLCRD32(adapter, QLCNIC_PEG_HALT_STATUS1);
 
        if (status & QLCNIC_RCODE_FATAL_ERROR)
@@ -2461,6 +2527,7 @@ qlcnic_dev_request_reset(struct qlcnic_adapter *adapter)
 {
        u32 state;
 
+       adapter->need_fw_reset = 1;
        if (qlcnic_api_lock(adapter))
                return;
 
@@ -2481,6 +2548,9 @@ qlcnic_dev_set_npar_ready(struct qlcnic_adapter *adapter)
 {
        u32 state;
 
+       if (!(adapter->flags & QLCNIC_ESWITCH_ENABLED) ||
+               adapter->op_mode == QLCNIC_NON_PRIV_FUNC)
+               return;
        if (qlcnic_api_lock(adapter))
                return;
 
@@ -2499,6 +2569,9 @@ static void
 qlcnic_schedule_work(struct qlcnic_adapter *adapter,
                work_func_t func, int delay)
 {
+       if (test_bit(__QLCNIC_AER, &adapter->state))
+               return;
+
        INIT_DELAYED_WORK(&adapter->fw_work, func);
        schedule_delayed_work(&adapter->fw_work, round_jiffies_relative(delay));
 }
@@ -2518,18 +2591,10 @@ qlcnic_attach_work(struct work_struct *work)
        struct qlcnic_adapter *adapter = container_of(work,
                                struct qlcnic_adapter, fw_work.work);
        struct net_device *netdev = adapter->netdev;
-       int err;
 
        if (netif_running(netdev)) {
-               err = qlcnic_attach(adapter);
-               if (err)
-                       goto done;
-
-               err = qlcnic_up(adapter, netdev);
-               if (err) {
-                       qlcnic_detach(adapter);
+               if (qlcnic_up(adapter, netdev))
                        goto done;
-               }
 
                qlcnic_config_indev_addr(netdev, NETDEV_UP);
        }
@@ -2566,6 +2631,13 @@ qlcnic_check_health(struct qlcnic_adapter *adapter)
                adapter->fw_fail_cnt = 0;
                if (adapter->need_fw_reset)
                        goto detach;
+
+               if (adapter->reset_context &&
+                   auto_fw_reset == AUTO_FW_RESET_ENABLED) {
+                       qlcnic_reset_hw_context(adapter);
+                       adapter->netdev->trans_start = jiffies;
+               }
+
                return 0;
        }
 
@@ -2574,7 +2646,8 @@ qlcnic_check_health(struct qlcnic_adapter *adapter)
 
        qlcnic_dev_request_reset(adapter);
 
-       clear_bit(__QLCNIC_FW_ATTACHED, &adapter->state);
+       if ((auto_fw_reset == AUTO_FW_RESET_ENABLED))
+               clear_bit(__QLCNIC_FW_ATTACHED, &adapter->state);
 
        dev_info(&netdev->dev, "firmware hang detected\n");
 
@@ -2609,6 +2682,133 @@ reschedule:
        qlcnic_schedule_work(adapter, qlcnic_fw_poll_work, FW_POLL_DELAY);
 }
 
+static int qlcnic_is_first_func(struct pci_dev *pdev)
+{
+       struct pci_dev *oth_pdev;
+       int val = pdev->devfn;
+
+       while (val-- > 0) {
+               oth_pdev = pci_get_domain_bus_and_slot(pci_domain_nr
+                       (pdev->bus), pdev->bus->number,
+                       PCI_DEVFN(PCI_SLOT(pdev->devfn), val));
+               if (!oth_pdev)
+                       continue;
+
+               if (oth_pdev->current_state != PCI_D3cold) {
+                       pci_dev_put(oth_pdev);
+                       return 0;
+               }
+               pci_dev_put(oth_pdev);
+       }
+       return 1;
+}
+
+static int qlcnic_attach_func(struct pci_dev *pdev)
+{
+       int err, first_func;
+       struct qlcnic_adapter *adapter = pci_get_drvdata(pdev);
+       struct net_device *netdev = adapter->netdev;
+
+       pdev->error_state = pci_channel_io_normal;
+
+       err = pci_enable_device(pdev);
+       if (err)
+               return err;
+
+       pci_set_power_state(pdev, PCI_D0);
+       pci_set_master(pdev);
+       pci_restore_state(pdev);
+
+       first_func = qlcnic_is_first_func(pdev);
+
+       if (qlcnic_api_lock(adapter))
+               return -EINVAL;
+
+       if (first_func) {
+               adapter->need_fw_reset = 1;
+               set_bit(__QLCNIC_START_FW, &adapter->state);
+               QLCWR32(adapter, QLCNIC_CRB_DEV_STATE, QLCNIC_DEV_INITIALIZING);
+               QLCDB(adapter, DRV, "Restarting fw\n");
+       }
+       qlcnic_api_unlock(adapter);
+
+       err = adapter->nic_ops->start_firmware(adapter);
+       if (err)
+               return err;
+
+       qlcnic_clr_drv_state(adapter);
+       qlcnic_setup_intr(adapter);
+
+       if (netif_running(netdev)) {
+               err = qlcnic_attach(adapter);
+               if (err) {
+                       qlcnic_clr_all_drv_state(adapter);
+                       clear_bit(__QLCNIC_AER, &adapter->state);
+                       netif_device_attach(netdev);
+                       return err;
+               }
+
+               err = qlcnic_up(adapter, netdev);
+               if (err)
+                       goto done;
+
+               qlcnic_config_indev_addr(netdev, NETDEV_UP);
+       }
+ done:
+       netif_device_attach(netdev);
+       return err;
+}
+
+static pci_ers_result_t qlcnic_io_error_detected(struct pci_dev *pdev,
+                                               pci_channel_state_t state)
+{
+       struct qlcnic_adapter *adapter = pci_get_drvdata(pdev);
+       struct net_device *netdev = adapter->netdev;
+
+       if (state == pci_channel_io_perm_failure)
+               return PCI_ERS_RESULT_DISCONNECT;
+
+       if (state == pci_channel_io_normal)
+               return PCI_ERS_RESULT_RECOVERED;
+
+       set_bit(__QLCNIC_AER, &adapter->state);
+       netif_device_detach(netdev);
+
+       cancel_delayed_work_sync(&adapter->fw_work);
+
+       if (netif_running(netdev))
+               qlcnic_down(adapter, netdev);
+
+       qlcnic_detach(adapter);
+       qlcnic_teardown_intr(adapter);
+
+       clear_bit(__QLCNIC_RESETTING, &adapter->state);
+
+       pci_save_state(pdev);
+       pci_disable_device(pdev);
+
+       return PCI_ERS_RESULT_NEED_RESET;
+}
+
+static pci_ers_result_t qlcnic_io_slot_reset(struct pci_dev *pdev)
+{
+       return qlcnic_attach_func(pdev) ? PCI_ERS_RESULT_DISCONNECT :
+                               PCI_ERS_RESULT_RECOVERED;
+}
+
+static void qlcnic_io_resume(struct pci_dev *pdev)
+{
+       struct qlcnic_adapter *adapter = pci_get_drvdata(pdev);
+
+       pci_cleanup_aer_uncorrect_error_status(pdev);
+
+       if (QLCRD32(adapter, QLCNIC_CRB_DEV_STATE) == QLCNIC_DEV_READY &&
+           test_and_clear_bit(__QLCNIC_AER, &adapter->state))
+               qlcnic_schedule_work(adapter, qlcnic_fw_poll_work,
+                                               FW_POLL_DELAY);
+}
+
+
 static int
 qlcnicvf_start_firmware(struct qlcnic_adapter *adapter)
 {
@@ -2637,18 +2837,6 @@ qlcnicvf_config_led(struct qlcnic_adapter *adapter, u32 state, u32 rate)
        return -EOPNOTSUPP;
 }
 
-static int
-qlcnicvf_set_ilb_mode(struct qlcnic_adapter *adapter)
-{
-       return -EOPNOTSUPP;
-}
-
-static void
-qlcnicvf_clear_ilb_mode(struct qlcnic_adapter *adapter)
-{
-       return;
-}
-
 static ssize_t
 qlcnic_store_bridged_mode(struct device *dev,
                struct device_attribute *attr, const char *buf, size_t len)
@@ -2869,6 +3057,361 @@ static struct bin_attribute bin_attr_mem = {
        .write = qlcnic_sysfs_write_mem,
 };
 
+static int
+validate_pm_config(struct qlcnic_adapter *adapter,
+                       struct qlcnic_pm_func_cfg *pm_cfg, int count)
+{
+
+       u8 src_pci_func, s_esw_id, d_esw_id;
+       u8 dest_pci_func;
+       int i;
+
+       for (i = 0; i < count; i++) {
+               src_pci_func = pm_cfg[i].pci_func;
+               dest_pci_func = pm_cfg[i].dest_npar;
+               if (src_pci_func >= QLCNIC_MAX_PCI_FUNC
+                               || dest_pci_func >= QLCNIC_MAX_PCI_FUNC)
+                       return QL_STATUS_INVALID_PARAM;
+
+               if (adapter->npars[src_pci_func].type != QLCNIC_TYPE_NIC)
+                       return QL_STATUS_INVALID_PARAM;
+
+               if (adapter->npars[dest_pci_func].type != QLCNIC_TYPE_NIC)
+                       return QL_STATUS_INVALID_PARAM;
+
+               if (!IS_VALID_MODE(pm_cfg[i].action))
+                       return QL_STATUS_INVALID_PARAM;
+
+               s_esw_id = adapter->npars[src_pci_func].phy_port;
+               d_esw_id = adapter->npars[dest_pci_func].phy_port;
+
+               if (s_esw_id != d_esw_id)
+                       return QL_STATUS_INVALID_PARAM;
+
+       }
+       return 0;
+
+}
+
+static ssize_t
+qlcnic_sysfs_write_pm_config(struct file *filp, struct kobject *kobj,
+       struct bin_attribute *attr, char *buf, loff_t offset, size_t size)
+{
+       struct device *dev = container_of(kobj, struct device, kobj);
+       struct qlcnic_adapter *adapter = dev_get_drvdata(dev);
+       struct qlcnic_pm_func_cfg *pm_cfg;
+       u32 id, action, pci_func;
+       int count, rem, i, ret;
+
+       count   = size / sizeof(struct qlcnic_pm_func_cfg);
+       rem     = size % sizeof(struct qlcnic_pm_func_cfg);
+       if (rem)
+               return QL_STATUS_INVALID_PARAM;
+
+       pm_cfg = (struct qlcnic_pm_func_cfg *) buf;
+
+       ret = validate_pm_config(adapter, pm_cfg, count);
+       if (ret)
+               return ret;
+       for (i = 0; i < count; i++) {
+               pci_func = pm_cfg[i].pci_func;
+               action = pm_cfg[i].action;
+               id = adapter->npars[pci_func].phy_port;
+               ret = qlcnic_config_port_mirroring(adapter, id,
+                                               action, pci_func);
+               if (ret)
+                       return ret;
+       }
+
+       for (i = 0; i < count; i++) {
+               pci_func = pm_cfg[i].pci_func;
+               id = adapter->npars[pci_func].phy_port;
+               adapter->npars[pci_func].enable_pm = pm_cfg[i].action;
+               adapter->npars[pci_func].dest_npar = id;
+       }
+       return size;
+}
+
+static ssize_t
+qlcnic_sysfs_read_pm_config(struct file *filp, struct kobject *kobj,
+       struct bin_attribute *attr, char *buf, loff_t offset, size_t size)
+{
+       struct device *dev = container_of(kobj, struct device, kobj);
+       struct qlcnic_adapter *adapter = dev_get_drvdata(dev);
+       struct qlcnic_pm_func_cfg pm_cfg[QLCNIC_MAX_PCI_FUNC];
+       int i;
+
+       if (size != sizeof(pm_cfg))
+               return QL_STATUS_INVALID_PARAM;
+
+       for (i = 0; i < QLCNIC_MAX_PCI_FUNC; i++) {
+               if (adapter->npars[i].type != QLCNIC_TYPE_NIC)
+                       continue;
+               pm_cfg[i].action = adapter->npars[i].enable_pm;
+               pm_cfg[i].dest_npar = 0;
+               pm_cfg[i].pci_func = i;
+       }
+       memcpy(buf, &pm_cfg, size);
+
+       return size;
+}
+
+static int
+validate_esw_config(struct qlcnic_adapter *adapter,
+                       struct qlcnic_esw_func_cfg *esw_cfg, int count)
+{
+       u8 pci_func;
+       int i;
+
+       for (i = 0; i < count; i++) {
+               pci_func = esw_cfg[i].pci_func;
+               if (pci_func >= QLCNIC_MAX_PCI_FUNC)
+                       return QL_STATUS_INVALID_PARAM;
+
+               if (adapter->npars[i].type != QLCNIC_TYPE_NIC)
+                       return QL_STATUS_INVALID_PARAM;
+
+               if (esw_cfg->host_vlan_tag == 1)
+                       if (!IS_VALID_VLAN(esw_cfg[i].vlan_id))
+                               return QL_STATUS_INVALID_PARAM;
+
+               if (!IS_VALID_MODE(esw_cfg[i].promisc_mode)
+                               || !IS_VALID_MODE(esw_cfg[i].host_vlan_tag)
+                               || !IS_VALID_MODE(esw_cfg[i].mac_learning)
+                               || !IS_VALID_MODE(esw_cfg[i].discard_tagged))
+                       return QL_STATUS_INVALID_PARAM;
+       }
+
+       return 0;
+}
+
+static ssize_t
+qlcnic_sysfs_write_esw_config(struct file *file, struct kobject *kobj,
+       struct bin_attribute *attr, char *buf, loff_t offset, size_t size)
+{
+       struct device *dev = container_of(kobj, struct device, kobj);
+       struct qlcnic_adapter *adapter = dev_get_drvdata(dev);
+       struct qlcnic_esw_func_cfg *esw_cfg;
+       int count, rem, i, ret;
+       u8 id, pci_func;
+
+       count   = size / sizeof(struct qlcnic_esw_func_cfg);
+       rem     = size % sizeof(struct qlcnic_esw_func_cfg);
+       if (rem)
+               return QL_STATUS_INVALID_PARAM;
+
+       esw_cfg = (struct qlcnic_esw_func_cfg *) buf;
+       ret = validate_esw_config(adapter, esw_cfg, count);
+       if (ret)
+               return ret;
+
+       for (i = 0; i < count; i++) {
+               pci_func = esw_cfg[i].pci_func;
+               id = adapter->npars[pci_func].phy_port;
+               ret = qlcnic_config_switch_port(adapter, id,
+                                               esw_cfg[i].host_vlan_tag,
+                                               esw_cfg[i].discard_tagged,
+                                               esw_cfg[i].promisc_mode,
+                                               esw_cfg[i].mac_learning,
+                                               esw_cfg[i].pci_func,
+                                               esw_cfg[i].vlan_id);
+               if (ret)
+                       return ret;
+       }
+
+       for (i = 0; i < count; i++) {
+               pci_func = esw_cfg[i].pci_func;
+               adapter->npars[pci_func].promisc_mode = esw_cfg[i].promisc_mode;
+               adapter->npars[pci_func].mac_learning = esw_cfg[i].mac_learning;
+               adapter->npars[pci_func].vlan_id = esw_cfg[i].vlan_id;
+               adapter->npars[pci_func].discard_tagged =
+                                               esw_cfg[i].discard_tagged;
+               adapter->npars[pci_func].host_vlan_tag =
+                                               esw_cfg[i].host_vlan_tag;
+       }
+
+       return size;
+}
+
+static ssize_t
+qlcnic_sysfs_read_esw_config(struct file *file, struct kobject *kobj,
+       struct bin_attribute *attr, char *buf, loff_t offset, size_t size)
+{
+       struct device *dev = container_of(kobj, struct device, kobj);
+       struct qlcnic_adapter *adapter = dev_get_drvdata(dev);
+       struct qlcnic_esw_func_cfg esw_cfg[QLCNIC_MAX_PCI_FUNC];
+       int i;
+
+       if (size != sizeof(esw_cfg))
+               return QL_STATUS_INVALID_PARAM;
+
+       for (i = 0; i < QLCNIC_MAX_PCI_FUNC; i++) {
+               if (adapter->npars[i].type != QLCNIC_TYPE_NIC)
+                       continue;
+
+               esw_cfg[i].host_vlan_tag = adapter->npars[i].host_vlan_tag;
+               esw_cfg[i].promisc_mode = adapter->npars[i].promisc_mode;
+               esw_cfg[i].discard_tagged = adapter->npars[i].discard_tagged;
+               esw_cfg[i].vlan_id = adapter->npars[i].vlan_id;
+               esw_cfg[i].mac_learning = adapter->npars[i].mac_learning;
+       }
+       memcpy(buf, &esw_cfg, size);
+
+       return size;
+}
+
+static int
+validate_npar_config(struct qlcnic_adapter *adapter,
+                               struct qlcnic_npar_func_cfg *np_cfg, int count)
+{
+       u8 pci_func, i;
+
+       for (i = 0; i < count; i++) {
+               pci_func = np_cfg[i].pci_func;
+               if (pci_func >= QLCNIC_MAX_PCI_FUNC)
+                       return QL_STATUS_INVALID_PARAM;
+
+               if (adapter->npars[pci_func].type != QLCNIC_TYPE_NIC)
+                       return QL_STATUS_INVALID_PARAM;
+
+               if (!IS_VALID_BW(np_cfg[i].min_bw)
+                               || !IS_VALID_BW(np_cfg[i].max_bw)
+                               || !IS_VALID_RX_QUEUES(np_cfg[i].max_rx_queues)
+                               || !IS_VALID_TX_QUEUES(np_cfg[i].max_tx_queues))
+                       return QL_STATUS_INVALID_PARAM;
+       }
+       return 0;
+}
+
+static ssize_t
+qlcnic_sysfs_write_npar_config(struct file *file, struct kobject *kobj,
+       struct bin_attribute *attr, char *buf, loff_t offset, size_t size)
+{
+       struct device *dev = container_of(kobj, struct device, kobj);
+       struct qlcnic_adapter *adapter = dev_get_drvdata(dev);
+       struct qlcnic_info nic_info;
+       struct qlcnic_npar_func_cfg *np_cfg;
+       int i, count, rem, ret;
+       u8 pci_func;
+
+       count   = size / sizeof(struct qlcnic_npar_func_cfg);
+       rem     = size % sizeof(struct qlcnic_npar_func_cfg);
+       if (rem)
+               return QL_STATUS_INVALID_PARAM;
+
+       np_cfg = (struct qlcnic_npar_func_cfg *) buf;
+       ret = validate_npar_config(adapter, np_cfg, count);
+       if (ret)
+               return ret;
+
+       for (i = 0; i < count ; i++) {
+               pci_func = np_cfg[i].pci_func;
+               ret = qlcnic_get_nic_info(adapter, &nic_info, pci_func);
+               if (ret)
+                       return ret;
+               nic_info.pci_func = pci_func;
+               nic_info.min_tx_bw = np_cfg[i].min_bw;
+               nic_info.max_tx_bw = np_cfg[i].max_bw;
+               ret = qlcnic_set_nic_info(adapter, &nic_info);
+               if (ret)
+                       return ret;
+               adapter->npars[i].min_bw = nic_info.min_tx_bw;
+               adapter->npars[i].max_bw = nic_info.max_tx_bw;
+       }
+
+       return size;
+
+}
+static ssize_t
+qlcnic_sysfs_read_npar_config(struct file *file, struct kobject *kobj,
+       struct bin_attribute *attr, char *buf, loff_t offset, size_t size)
+{
+       struct device *dev = container_of(kobj, struct device, kobj);
+       struct qlcnic_adapter *adapter = dev_get_drvdata(dev);
+       struct qlcnic_info nic_info;
+       struct qlcnic_npar_func_cfg np_cfg[QLCNIC_MAX_PCI_FUNC];
+       int i, ret;
+
+       if (size != sizeof(np_cfg))
+               return QL_STATUS_INVALID_PARAM;
+
+       for (i = 0; i < QLCNIC_MAX_PCI_FUNC ; i++) {
+               if (adapter->npars[i].type != QLCNIC_TYPE_NIC)
+                       continue;
+               ret = qlcnic_get_nic_info(adapter, &nic_info, i);
+               if (ret)
+                       return ret;
+
+               np_cfg[i].pci_func = i;
+               np_cfg[i].op_mode = nic_info.op_mode;
+               np_cfg[i].port_num = nic_info.phys_port;
+               np_cfg[i].fw_capab = nic_info.capabilities;
+               np_cfg[i].min_bw = nic_info.min_tx_bw ;
+               np_cfg[i].max_bw = nic_info.max_tx_bw;
+               np_cfg[i].max_tx_queues = nic_info.max_tx_ques;
+               np_cfg[i].max_rx_queues = nic_info.max_rx_ques;
+       }
+       memcpy(buf, &np_cfg, size);
+       return size;
+}
+
+static ssize_t
+qlcnic_sysfs_read_pci_config(struct file *file, struct kobject *kobj,
+       struct bin_attribute *attr, char *buf, loff_t offset, size_t size)
+{
+       struct device *dev = container_of(kobj, struct device, kobj);
+       struct qlcnic_adapter *adapter = dev_get_drvdata(dev);
+       struct qlcnic_pci_func_cfg pci_cfg[QLCNIC_MAX_PCI_FUNC];
+       struct qlcnic_pci_info  pci_info[QLCNIC_MAX_PCI_FUNC];
+       int i, ret;
+
+       if (size != sizeof(pci_cfg))
+               return QL_STATUS_INVALID_PARAM;
+
+       ret = qlcnic_get_pci_info(adapter, pci_info);
+       if (ret)
+               return ret;
+
+       for (i = 0; i < QLCNIC_MAX_PCI_FUNC ; i++) {
+               pci_cfg[i].pci_func = pci_info[i].id;
+               pci_cfg[i].func_type = pci_info[i].type;
+               pci_cfg[i].port_num = pci_info[i].default_port;
+               pci_cfg[i].min_bw = pci_info[i].tx_min_bw;
+               pci_cfg[i].max_bw = pci_info[i].tx_max_bw;
+               memcpy(&pci_cfg[i].def_mac_addr, &pci_info[i].mac, ETH_ALEN);
+       }
+       memcpy(buf, &pci_cfg, size);
+       return size;
+
+}
+static struct bin_attribute bin_attr_npar_config = {
+       .attr = {.name = "npar_config", .mode = (S_IRUGO | S_IWUSR)},
+       .size = 0,
+       .read = qlcnic_sysfs_read_npar_config,
+       .write = qlcnic_sysfs_write_npar_config,
+};
+
+static struct bin_attribute bin_attr_pci_config = {
+       .attr = {.name = "pci_config", .mode = (S_IRUGO | S_IWUSR)},
+       .size = 0,
+       .read = qlcnic_sysfs_read_pci_config,
+       .write = NULL,
+};
+
+static struct bin_attribute bin_attr_esw_config = {
+       .attr = {.name = "esw_config", .mode = (S_IRUGO | S_IWUSR)},
+       .size = 0,
+       .read = qlcnic_sysfs_read_esw_config,
+       .write = qlcnic_sysfs_write_esw_config,
+};
+
+static struct bin_attribute bin_attr_pm_config = {
+       .attr = {.name = "pm_config", .mode = (S_IRUGO | S_IWUSR)},
+       .size = 0,
+       .read = qlcnic_sysfs_read_pm_config,
+       .write = qlcnic_sysfs_write_pm_config,
+};
+
 static void
 qlcnic_create_sysfs_entries(struct qlcnic_adapter *adapter)
 {
@@ -2894,23 +3437,45 @@ qlcnic_create_diag_entries(struct qlcnic_adapter *adapter)
 {
        struct device *dev = &adapter->pdev->dev;
 
+       if (adapter->op_mode == QLCNIC_NON_PRIV_FUNC)
+               return;
        if (device_create_file(dev, &dev_attr_diag_mode))
                dev_info(dev, "failed to create diag_mode sysfs entry\n");
        if (device_create_bin_file(dev, &bin_attr_crb))
                dev_info(dev, "failed to create crb sysfs entry\n");
        if (device_create_bin_file(dev, &bin_attr_mem))
                dev_info(dev, "failed to create mem sysfs entry\n");
-}
+       if (!(adapter->flags & QLCNIC_ESWITCH_ENABLED) ||
+                       adapter->op_mode != QLCNIC_MGMT_FUNC)
+               return;
+       if (device_create_bin_file(dev, &bin_attr_pci_config))
+               dev_info(dev, "failed to create pci config sysfs entry");
+       if (device_create_bin_file(dev, &bin_attr_npar_config))
+               dev_info(dev, "failed to create npar config sysfs entry");
+       if (device_create_bin_file(dev, &bin_attr_esw_config))
+               dev_info(dev, "failed to create esw config sysfs entry");
+       if (device_create_bin_file(dev, &bin_attr_pm_config))
+               dev_info(dev, "failed to create pm config sysfs entry");
 
+}
 
 static void
 qlcnic_remove_diag_entries(struct qlcnic_adapter *adapter)
 {
        struct device *dev = &adapter->pdev->dev;
 
+       if (adapter->op_mode == QLCNIC_NON_PRIV_FUNC)
+               return;
        device_remove_file(dev, &dev_attr_diag_mode);
        device_remove_bin_file(dev, &bin_attr_crb);
        device_remove_bin_file(dev, &bin_attr_mem);
+       if (!(adapter->flags & QLCNIC_ESWITCH_ENABLED) ||
+                       adapter->op_mode != QLCNIC_MGMT_FUNC)
+               return;
+       device_remove_bin_file(dev, &bin_attr_pci_config);
+       device_remove_bin_file(dev, &bin_attr_npar_config);
+       device_remove_bin_file(dev, &bin_attr_esw_config);
+       device_remove_bin_file(dev, &bin_attr_pm_config);
 }
 
 #ifdef CONFIG_INET
@@ -3034,6 +3599,11 @@ static void
 qlcnic_config_indev_addr(struct net_device *dev, unsigned long event)
 { }
 #endif
+static struct pci_error_handlers qlcnic_err_handler = {
+       .error_detected = qlcnic_io_error_detected,
+       .slot_reset = qlcnic_io_slot_reset,
+       .resume = qlcnic_io_resume,
+};
 
 static struct pci_driver qlcnic_driver = {
        .name = qlcnic_driver_name,
@@ -3044,11 +3614,14 @@ static struct pci_driver qlcnic_driver = {
        .suspend = qlcnic_suspend,
        .resume = qlcnic_resume,
 #endif
-       .shutdown = qlcnic_shutdown
+       .shutdown = qlcnic_shutdown,
+       .err_handler = &qlcnic_err_handler
+
 };
 
 static int __init qlcnic_init_module(void)
 {
+       int ret;
 
        printk(KERN_INFO "%s\n", qlcnic_driver_string);
 
@@ -3057,8 +3630,15 @@ static int __init qlcnic_init_module(void)
        register_inetaddr_notifier(&qlcnic_inetaddr_cb);
 #endif
 
+       ret = pci_register_driver(&qlcnic_driver);
+       if (ret) {
+#ifdef CONFIG_INET
+               unregister_inetaddr_notifier(&qlcnic_inetaddr_cb);
+               unregister_netdevice_notifier(&qlcnic_netdev_cb);
+#endif
+       }
 
-       return pci_register_driver(&qlcnic_driver);
+       return ret;
 }
 
 module_init(qlcnic_init_module);