diff options
Diffstat (limited to 'drivers/net/qlcnic/qlcnic_main.c')
-rw-r--r-- | drivers/net/qlcnic/qlcnic_main.c | 137 |
1 files changed, 136 insertions, 1 deletions
diff --git a/drivers/net/qlcnic/qlcnic_main.c b/drivers/net/qlcnic/qlcnic_main.c index 0995f90b0bac..d5c94a3364f7 100644 --- a/drivers/net/qlcnic/qlcnic_main.c +++ b/drivers/net/qlcnic/qlcnic_main.c | |||
@@ -34,6 +34,7 @@ | |||
34 | #include <linux/ipv6.h> | 34 | #include <linux/ipv6.h> |
35 | #include <linux/inetdevice.h> | 35 | #include <linux/inetdevice.h> |
36 | #include <linux/sysfs.h> | 36 | #include <linux/sysfs.h> |
37 | #include <linux/aer.h> | ||
37 | 38 | ||
38 | MODULE_DESCRIPTION("QLogic 1/10 GbE Converged/Intelligent Ethernet Driver"); | 39 | MODULE_DESCRIPTION("QLogic 1/10 GbE Converged/Intelligent Ethernet Driver"); |
39 | MODULE_LICENSE("GPL"); | 40 | MODULE_LICENSE("GPL"); |
@@ -1306,6 +1307,7 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) | |||
1306 | goto err_out_disable_pdev; | 1307 | goto err_out_disable_pdev; |
1307 | 1308 | ||
1308 | pci_set_master(pdev); | 1309 | pci_set_master(pdev); |
1310 | pci_enable_pcie_error_reporting(pdev); | ||
1309 | 1311 | ||
1310 | netdev = alloc_etherdev(sizeof(struct qlcnic_adapter)); | 1312 | netdev = alloc_etherdev(sizeof(struct qlcnic_adapter)); |
1311 | if (!netdev) { | 1313 | if (!netdev) { |
@@ -1437,6 +1439,7 @@ static void __devexit qlcnic_remove(struct pci_dev *pdev) | |||
1437 | 1439 | ||
1438 | qlcnic_release_firmware(adapter); | 1440 | qlcnic_release_firmware(adapter); |
1439 | 1441 | ||
1442 | pci_disable_pcie_error_reporting(pdev); | ||
1440 | pci_release_regions(pdev); | 1443 | pci_release_regions(pdev); |
1441 | pci_disable_device(pdev); | 1444 | pci_disable_device(pdev); |
1442 | pci_set_drvdata(pdev, NULL); | 1445 | pci_set_drvdata(pdev, NULL); |
@@ -2521,6 +2524,9 @@ static void | |||
2521 | qlcnic_schedule_work(struct qlcnic_adapter *adapter, | 2524 | qlcnic_schedule_work(struct qlcnic_adapter *adapter, |
2522 | work_func_t func, int delay) | 2525 | work_func_t func, int delay) |
2523 | { | 2526 | { |
2527 | if (test_bit(__QLCNIC_AER, &adapter->state)) | ||
2528 | return; | ||
2529 | |||
2524 | INIT_DELAYED_WORK(&adapter->fw_work, func); | 2530 | INIT_DELAYED_WORK(&adapter->fw_work, func); |
2525 | schedule_delayed_work(&adapter->fw_work, round_jiffies_relative(delay)); | 2531 | schedule_delayed_work(&adapter->fw_work, round_jiffies_relative(delay)); |
2526 | } | 2532 | } |
@@ -2631,6 +2637,128 @@ reschedule: | |||
2631 | qlcnic_schedule_work(adapter, qlcnic_fw_poll_work, FW_POLL_DELAY); | 2637 | qlcnic_schedule_work(adapter, qlcnic_fw_poll_work, FW_POLL_DELAY); |
2632 | } | 2638 | } |
2633 | 2639 | ||
2640 | static int qlcnic_is_first_func(struct pci_dev *pdev) | ||
2641 | { | ||
2642 | struct pci_dev *oth_pdev; | ||
2643 | int val = pdev->devfn; | ||
2644 | |||
2645 | while (val-- > 0) { | ||
2646 | oth_pdev = pci_get_domain_bus_and_slot(pci_domain_nr | ||
2647 | (pdev->bus), pdev->bus->number, | ||
2648 | PCI_DEVFN(PCI_SLOT(pdev->devfn), val)); | ||
2649 | |||
2650 | if (oth_pdev && (oth_pdev->current_state != PCI_D3cold)) | ||
2651 | return 0; | ||
2652 | } | ||
2653 | return 1; | ||
2654 | } | ||
2655 | |||
2656 | static int qlcnic_attach_func(struct pci_dev *pdev) | ||
2657 | { | ||
2658 | int err, first_func; | ||
2659 | struct qlcnic_adapter *adapter = pci_get_drvdata(pdev); | ||
2660 | struct net_device *netdev = adapter->netdev; | ||
2661 | |||
2662 | pdev->error_state = pci_channel_io_normal; | ||
2663 | |||
2664 | err = pci_enable_device(pdev); | ||
2665 | if (err) | ||
2666 | return err; | ||
2667 | |||
2668 | pci_set_power_state(pdev, PCI_D0); | ||
2669 | pci_set_master(pdev); | ||
2670 | pci_restore_state(pdev); | ||
2671 | |||
2672 | first_func = qlcnic_is_first_func(pdev); | ||
2673 | |||
2674 | if (qlcnic_api_lock(adapter)) | ||
2675 | return -EINVAL; | ||
2676 | |||
2677 | if (first_func) { | ||
2678 | adapter->need_fw_reset = 1; | ||
2679 | set_bit(__QLCNIC_START_FW, &adapter->state); | ||
2680 | QLCWR32(adapter, QLCNIC_CRB_DEV_STATE, QLCNIC_DEV_INITIALIZING); | ||
2681 | QLCDB(adapter, DRV, "Restarting fw\n"); | ||
2682 | } | ||
2683 | qlcnic_api_unlock(adapter); | ||
2684 | |||
2685 | err = adapter->nic_ops->start_firmware(adapter); | ||
2686 | if (err) | ||
2687 | return err; | ||
2688 | |||
2689 | qlcnic_clr_drv_state(adapter); | ||
2690 | qlcnic_setup_intr(adapter); | ||
2691 | |||
2692 | if (netif_running(netdev)) { | ||
2693 | err = qlcnic_attach(adapter); | ||
2694 | if (err) { | ||
2695 | qlcnic_clr_all_drv_state(adapter); | ||
2696 | clear_bit(__QLCNIC_AER, &adapter->state); | ||
2697 | netif_device_attach(netdev); | ||
2698 | return err; | ||
2699 | } | ||
2700 | |||
2701 | err = qlcnic_up(adapter, netdev); | ||
2702 | if (err) | ||
2703 | goto done; | ||
2704 | |||
2705 | qlcnic_config_indev_addr(netdev, NETDEV_UP); | ||
2706 | } | ||
2707 | done: | ||
2708 | netif_device_attach(netdev); | ||
2709 | return err; | ||
2710 | } | ||
2711 | |||
2712 | static pci_ers_result_t qlcnic_io_error_detected(struct pci_dev *pdev, | ||
2713 | pci_channel_state_t state) | ||
2714 | { | ||
2715 | struct qlcnic_adapter *adapter = pci_get_drvdata(pdev); | ||
2716 | struct net_device *netdev = adapter->netdev; | ||
2717 | |||
2718 | if (state == pci_channel_io_perm_failure) | ||
2719 | return PCI_ERS_RESULT_DISCONNECT; | ||
2720 | |||
2721 | if (state == pci_channel_io_normal) | ||
2722 | return PCI_ERS_RESULT_RECOVERED; | ||
2723 | |||
2724 | set_bit(__QLCNIC_AER, &adapter->state); | ||
2725 | netif_device_detach(netdev); | ||
2726 | |||
2727 | cancel_delayed_work_sync(&adapter->fw_work); | ||
2728 | |||
2729 | if (netif_running(netdev)) | ||
2730 | qlcnic_down(adapter, netdev); | ||
2731 | |||
2732 | qlcnic_detach(adapter); | ||
2733 | qlcnic_teardown_intr(adapter); | ||
2734 | |||
2735 | clear_bit(__QLCNIC_RESETTING, &adapter->state); | ||
2736 | |||
2737 | pci_save_state(pdev); | ||
2738 | pci_disable_device(pdev); | ||
2739 | |||
2740 | return PCI_ERS_RESULT_NEED_RESET; | ||
2741 | } | ||
2742 | |||
2743 | static pci_ers_result_t qlcnic_io_slot_reset(struct pci_dev *pdev) | ||
2744 | { | ||
2745 | return qlcnic_attach_func(pdev) ? PCI_ERS_RESULT_DISCONNECT : | ||
2746 | PCI_ERS_RESULT_RECOVERED; | ||
2747 | } | ||
2748 | |||
2749 | static void qlcnic_io_resume(struct pci_dev *pdev) | ||
2750 | { | ||
2751 | struct qlcnic_adapter *adapter = pci_get_drvdata(pdev); | ||
2752 | |||
2753 | pci_cleanup_aer_uncorrect_error_status(pdev); | ||
2754 | |||
2755 | if (QLCRD32(adapter, QLCNIC_CRB_DEV_STATE) == QLCNIC_DEV_READY && | ||
2756 | test_and_clear_bit(__QLCNIC_AER, &adapter->state)) | ||
2757 | qlcnic_schedule_work(adapter, qlcnic_fw_poll_work, | ||
2758 | FW_POLL_DELAY); | ||
2759 | } | ||
2760 | |||
2761 | |||
2634 | static int | 2762 | static int |
2635 | qlcnicvf_start_firmware(struct qlcnic_adapter *adapter) | 2763 | qlcnicvf_start_firmware(struct qlcnic_adapter *adapter) |
2636 | { | 2764 | { |
@@ -3436,6 +3564,11 @@ static void | |||
3436 | qlcnic_config_indev_addr(struct net_device *dev, unsigned long event) | 3564 | qlcnic_config_indev_addr(struct net_device *dev, unsigned long event) |
3437 | { } | 3565 | { } |
3438 | #endif | 3566 | #endif |
3567 | static struct pci_error_handlers qlcnic_err_handler = { | ||
3568 | .error_detected = qlcnic_io_error_detected, | ||
3569 | .slot_reset = qlcnic_io_slot_reset, | ||
3570 | .resume = qlcnic_io_resume, | ||
3571 | }; | ||
3439 | 3572 | ||
3440 | static struct pci_driver qlcnic_driver = { | 3573 | static struct pci_driver qlcnic_driver = { |
3441 | .name = qlcnic_driver_name, | 3574 | .name = qlcnic_driver_name, |
@@ -3446,7 +3579,9 @@ static struct pci_driver qlcnic_driver = { | |||
3446 | .suspend = qlcnic_suspend, | 3579 | .suspend = qlcnic_suspend, |
3447 | .resume = qlcnic_resume, | 3580 | .resume = qlcnic_resume, |
3448 | #endif | 3581 | #endif |
3449 | .shutdown = qlcnic_shutdown | 3582 | .shutdown = qlcnic_shutdown, |
3583 | .err_handler = &qlcnic_err_handler | ||
3584 | |||
3450 | }; | 3585 | }; |
3451 | 3586 | ||
3452 | static int __init qlcnic_init_module(void) | 3587 | static int __init qlcnic_init_module(void) |