diff options
author | James Smart <James.Smart@Emulex.Com> | 2008-12-04 22:38:54 -0500 |
---|---|---|
committer | James Bottomley <James.Bottomley@HansenPartnership.com> | 2008-12-29 12:24:25 -0500 |
commit | 3a55b5327b80d805eb3c9720092fd24f15193696 (patch) | |
tree | 0276c0d71f087d4976929f01eb2432ba08c5f75f /drivers/scsi/lpfc/lpfc_init.c | |
parent | ddcc50f0f3538e4771c8ab9e8ec685a22c90d88c (diff) |
[SCSI] lpfc 8.3.0 : Add support for Power Management Suspend/Resume operations
Implement lpfc_pci_suspend_one() and lpfc_pci_resume_one() and
register them in the pci_driver table.
Signed-off-by: James Smart <James.Smart@emulex.com>
Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
Diffstat (limited to 'drivers/scsi/lpfc/lpfc_init.c')
-rw-r--r-- | drivers/scsi/lpfc/lpfc_init.c | 109 |
1 files changed, 108 insertions, 1 deletions
diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index 1e2a9521853f..b213d1d01fee 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c | |||
@@ -2708,7 +2708,7 @@ out: | |||
2708 | * @pdev: pointer to PCI device | 2708 | * @pdev: pointer to PCI device |
2709 | * | 2709 | * |
2710 | * This routine is to be registered to the kernel's PCI subsystem. When an | 2710 | * This routine is to be registered to the kernel's PCI subsystem. When an |
2711 | * Emulex HBA is removed from PCI bus. It perform all the necessary cleanup | 2711 | * Emulex HBA is removed from PCI bus, it performs all the necessary cleanup |
2712 | * for the HBA device to be removed from the PCI subsystem properly. | 2712 | * for the HBA device to be removed from the PCI subsystem properly. |
2713 | **/ | 2713 | **/ |
2714 | static void __devexit | 2714 | static void __devexit |
@@ -2785,6 +2785,111 @@ lpfc_pci_remove_one(struct pci_dev *pdev) | |||
2785 | } | 2785 | } |
2786 | 2786 | ||
2787 | /** | 2787 | /** |
2788 | * lpfc_pci_suspend_one: lpfc PCI func to suspend device for power management. | ||
2789 | * @pdev: pointer to PCI device | ||
2790 | * @msg: power management message | ||
2791 | * | ||
2792 | * This routine is to be registered to the kernel's PCI subsystem to support | ||
2793 | * system Power Management (PM). When PM invokes this method, it quiesces the | ||
2794 | * device by stopping the driver's worker thread for the device, turning off | ||
2795 | * device's interrupt and DMA, and bring the device offline. Note that as the | ||
2796 | * driver implements the minimum PM requirements to a power-aware driver's PM | ||
2797 | * support for suspend/resume -- all the possible PM messages (SUSPEND, | ||
2798 | * HIBERNATE, FREEZE) to the suspend() method call will be treated as SUSPEND | ||
2799 | * and the driver will fully reinitialize its device during resume() method | ||
2800 | * call, the driver will set device to PCI_D3hot state in PCI config space | ||
2801 | * instead of setting it according to the @msg provided by the PM. | ||
2802 | * | ||
2803 | * Return code | ||
2804 | * 0 - driver suspended the device | ||
2805 | * Error otherwise | ||
2806 | **/ | ||
2807 | static int | ||
2808 | lpfc_pci_suspend_one(struct pci_dev *pdev, pm_message_t msg) | ||
2809 | { | ||
2810 | struct Scsi_Host *shost = pci_get_drvdata(pdev); | ||
2811 | struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba; | ||
2812 | |||
2813 | lpfc_printf_log(phba, KERN_INFO, LOG_INIT, | ||
2814 | "0473 PCI device Power Management suspend.\n"); | ||
2815 | |||
2816 | /* Bring down the device */ | ||
2817 | lpfc_offline_prep(phba); | ||
2818 | lpfc_offline(phba); | ||
2819 | kthread_stop(phba->worker_thread); | ||
2820 | |||
2821 | /* Disable interrupt from device */ | ||
2822 | lpfc_disable_intr(phba); | ||
2823 | |||
2824 | /* Save device state to PCI config space */ | ||
2825 | pci_save_state(pdev); | ||
2826 | pci_set_power_state(pdev, PCI_D3hot); | ||
2827 | |||
2828 | return 0; | ||
2829 | } | ||
2830 | |||
2831 | /** | ||
2832 | * lpfc_pci_resume_one: lpfc PCI func to resume device for power management. | ||
2833 | * @pdev: pointer to PCI device | ||
2834 | * | ||
2835 | * This routine is to be registered to the kernel's PCI subsystem to support | ||
2836 | * system Power Management (PM). When PM invokes this method, it restores | ||
2837 | * the device's PCI config space state and fully reinitializes the device | ||
2838 | * and brings it online. Note that as the driver implements the minimum PM | ||
2839 | * requirements to a power-aware driver's PM for suspend/resume -- all | ||
2840 | * the possible PM messages (SUSPEND, HIBERNATE, FREEZE) to the suspend() | ||
2841 | * method call will be treated as SUSPEND and the driver will fully | ||
2842 | * reinitialize its device during resume() method call, the device will be | ||
2843 | * set to PCI_D0 directly in PCI config space before restoring the state. | ||
2844 | * | ||
2845 | * Return code | ||
2846 | * 0 - driver suspended the device | ||
2847 | * Error otherwise | ||
2848 | **/ | ||
2849 | static int | ||
2850 | lpfc_pci_resume_one(struct pci_dev *pdev) | ||
2851 | { | ||
2852 | struct Scsi_Host *shost = pci_get_drvdata(pdev); | ||
2853 | struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba; | ||
2854 | int error; | ||
2855 | |||
2856 | lpfc_printf_log(phba, KERN_INFO, LOG_INIT, | ||
2857 | "0452 PCI device Power Management resume.\n"); | ||
2858 | |||
2859 | /* Restore device state from PCI config space */ | ||
2860 | pci_set_power_state(pdev, PCI_D0); | ||
2861 | pci_restore_state(pdev); | ||
2862 | if (pdev->is_busmaster) | ||
2863 | pci_set_master(pdev); | ||
2864 | |||
2865 | /* Startup the kernel thread for this host adapter. */ | ||
2866 | phba->worker_thread = kthread_run(lpfc_do_work, phba, | ||
2867 | "lpfc_worker_%d", phba->brd_no); | ||
2868 | if (IS_ERR(phba->worker_thread)) { | ||
2869 | error = PTR_ERR(phba->worker_thread); | ||
2870 | lpfc_printf_log(phba, KERN_ERR, LOG_INIT, | ||
2871 | "0434 PM resume failed to start worker " | ||
2872 | "thread: error=x%x.\n", error); | ||
2873 | return error; | ||
2874 | } | ||
2875 | |||
2876 | /* Enable interrupt from device */ | ||
2877 | error = lpfc_enable_intr(phba); | ||
2878 | if (error) { | ||
2879 | lpfc_printf_log(phba, KERN_ERR, LOG_INIT, | ||
2880 | "0430 PM resume Failed to enable interrupt: " | ||
2881 | "error=x%x.\n", error); | ||
2882 | return error; | ||
2883 | } | ||
2884 | |||
2885 | /* Restart HBA and bring it online */ | ||
2886 | lpfc_sli_brdrestart(phba); | ||
2887 | lpfc_online(phba); | ||
2888 | |||
2889 | return 0; | ||
2890 | } | ||
2891 | |||
2892 | /** | ||
2788 | * lpfc_io_error_detected: Driver method for handling PCI I/O error detected. | 2893 | * lpfc_io_error_detected: Driver method for handling PCI I/O error detected. |
2789 | * @pdev: pointer to PCI device. | 2894 | * @pdev: pointer to PCI device. |
2790 | * @state: the current PCI connection state. | 2895 | * @state: the current PCI connection state. |
@@ -3036,6 +3141,8 @@ static struct pci_driver lpfc_driver = { | |||
3036 | .id_table = lpfc_id_table, | 3141 | .id_table = lpfc_id_table, |
3037 | .probe = lpfc_pci_probe_one, | 3142 | .probe = lpfc_pci_probe_one, |
3038 | .remove = __devexit_p(lpfc_pci_remove_one), | 3143 | .remove = __devexit_p(lpfc_pci_remove_one), |
3144 | .suspend = lpfc_pci_suspend_one, | ||
3145 | .resume = lpfc_pci_resume_one, | ||
3039 | .err_handler = &lpfc_err_handler, | 3146 | .err_handler = &lpfc_err_handler, |
3040 | }; | 3147 | }; |
3041 | 3148 | ||