]> 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 0fef8c3c553ff85a1e952ba7b59c754dfbf137d5..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");
@@ -106,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 *);
@@ -380,8 +379,6 @@ static struct qlcnic_nic_template qlcnic_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
 };
 
@@ -389,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
 };
 
@@ -482,42 +477,45 @@ qlcnic_init_pci_info(struct qlcnic_adapter *adapter)
        int i, ret = 0, err;
        u8 pfn;
 
-       if (!adapter->npars)
-               adapter->npars = kzalloc(sizeof(struct qlcnic_npar_info) *
+       adapter->npars = kzalloc(sizeof(struct qlcnic_npar_info) *
                                QLCNIC_MAX_PCI_FUNC, GFP_KERNEL);
        if (!adapter->npars)
                return -ENOMEM;
 
-       if (!adapter->eswitch)
-               adapter->eswitch = kzalloc(sizeof(struct qlcnic_eswitch) *
+       adapter->eswitch = kzalloc(sizeof(struct qlcnic_eswitch) *
                                QLCNIC_NIU_MAX_XG_PORTS, GFP_KERNEL);
        if (!adapter->eswitch) {
                err = -ENOMEM;
-               goto err_eswitch;
+               goto err_npars;
        }
 
        ret = qlcnic_get_pci_info(adapter, pci_info);
-       if (!ret) {
-               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;
-               }
-
-               for (i = 0; i < QLCNIC_NIU_MAX_XG_PORTS; i++)
-                       adapter->eswitch[i].flags |= QLCNIC_SWITCH_ENABLE;
+       if (ret)
+               goto err_eswitch;
 
-               return ret;
+       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_eswitch:
+err_npars:
        kfree(adapter->npars);
+       adapter->npars = NULL;
 
        return ret;
 }
@@ -769,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)
 {
@@ -833,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;
 
@@ -1136,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;
        }
 
@@ -1306,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) {
@@ -1437,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);
@@ -2483,6 +2527,7 @@ qlcnic_dev_request_reset(struct qlcnic_adapter *adapter)
 {
        u32 state;
 
+       adapter->need_fw_reset = 1;
        if (qlcnic_api_lock(adapter))
                return;
 
@@ -2503,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;
 
@@ -2521,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));
 }
@@ -2631,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)
 {
@@ -2659,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)
@@ -2891,7 +3057,7 @@ static struct bin_attribute bin_attr_mem = {
        .write = qlcnic_sysfs_write_mem,
 };
 
-int
+static int
 validate_pm_config(struct qlcnic_adapter *adapter,
                        struct qlcnic_pm_func_cfg *pm_cfg, int count)
 {
@@ -2990,7 +3156,7 @@ qlcnic_sysfs_read_pm_config(struct file *filp, struct kobject *kobj,
        return size;
 }
 
-int
+static int
 validate_esw_config(struct qlcnic_adapter *adapter,
                        struct qlcnic_esw_func_cfg *esw_cfg, int count)
 {
@@ -3026,9 +3192,8 @@ qlcnic_sysfs_write_esw_config(struct file *file, struct kobject *kobj,
        struct device *dev = container_of(kobj, struct device, kobj);
        struct qlcnic_adapter *adapter = dev_get_drvdata(dev);
        struct qlcnic_esw_func_cfg *esw_cfg;
-       u8 id, discard_tagged, promsc_mode, mac_learn;
-       u8 vlan_tagging, pci_func, vlan_id;
        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);
@@ -3043,17 +3208,13 @@ qlcnic_sysfs_write_esw_config(struct file *file, struct kobject *kobj,
        for (i = 0; i < count; i++) {
                pci_func = esw_cfg[i].pci_func;
                id = adapter->npars[pci_func].phy_port;
-               vlan_tagging = esw_cfg[i].host_vlan_tag;
-               promsc_mode = esw_cfg[i].promisc_mode;
-               mac_learn = esw_cfg[i].mac_learning;
-               vlan_id = esw_cfg[i].vlan_id;
-               discard_tagged = esw_cfg[i].discard_tagged;
-               ret = qlcnic_config_switch_port(adapter, id, vlan_tagging,
-                                               discard_tagged,
-                                               promsc_mode,
-                                               mac_learn,
-                                               pci_func,
-                                               vlan_id);
+               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;
        }
@@ -3099,7 +3260,7 @@ qlcnic_sysfs_read_esw_config(struct file *file, struct kobject *kobj,
        return size;
 }
 
-int
+static int
 validate_npar_config(struct qlcnic_adapter *adapter,
                                struct qlcnic_npar_func_cfg *np_cfg, int count)
 {
@@ -3154,6 +3315,8 @@ qlcnic_sysfs_write_npar_config(struct file *file, struct kobject *kobj,
                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;
@@ -3436,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,
@@ -3446,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);
 
@@ -3459,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);