diff options
author | Andiry Xu <andiry.xu@amd.com> | 2010-12-27 04:39:02 -0500 |
---|---|---|
committer | Sarah Sharp <sarah.a.sharp@linux.intel.com> | 2011-01-14 18:28:52 -0500 |
commit | 0029227f1bc30b6c809ae751f9e7af6cef900997 (patch) | |
tree | 4962aacd3349c149c1d1567aaa09cd9ac8efec0f /drivers | |
parent | 7111ebc97ed53a32314011c85a6f235f0dab8ae8 (diff) |
xHCI: synchronize irq in xhci_suspend()
Synchronize the interrupts instead of free them in xhci_suspend(). This will
prevent a double free when the host is suspended and then the card removed.
Set the flag hcd->msix_enabled when using MSI-X, and check the flag in
suspend_common(). MSI-X synchronization will be handled by xhci_suspend(),
and MSI/INTx will be synchronized in suspend_common().
This patch should be queued for the 2.6.37 stable tree.
Reported-by: Matthew Garrett <mjg@redhat.com>
Signed-off-by: Andiry Xu <andiry.xu@amd.com>
Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
Cc: stable@kernel.org
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/usb/core/hcd-pci.c | 7 | ||||
-rw-r--r-- | drivers/usb/host/xhci.c | 46 |
2 files changed, 21 insertions, 32 deletions
diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c index b55d46070a25..f71e8e307e0f 100644 --- a/drivers/usb/core/hcd-pci.c +++ b/drivers/usb/core/hcd-pci.c | |||
@@ -405,7 +405,12 @@ static int suspend_common(struct device *dev, bool do_wakeup) | |||
405 | return retval; | 405 | return retval; |
406 | } | 406 | } |
407 | 407 | ||
408 | synchronize_irq(pci_dev->irq); | 408 | /* If MSI-X is enabled, the driver will have synchronized all vectors |
409 | * in pci_suspend(). If MSI or legacy PCI is enabled, that will be | ||
410 | * synchronized here. | ||
411 | */ | ||
412 | if (!hcd->msix_enabled) | ||
413 | synchronize_irq(pci_dev->irq); | ||
409 | 414 | ||
410 | /* Downstream ports from this root hub should already be quiesced, so | 415 | /* Downstream ports from this root hub should already be quiesced, so |
411 | * there will be no DMA activity. Now we can shut down the upstream | 416 | * there will be no DMA activity. Now we can shut down the upstream |
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 45e4a3108cc3..d48edfa5043a 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c | |||
@@ -226,7 +226,8 @@ static int xhci_setup_msi(struct xhci_hcd *xhci) | |||
226 | static int xhci_setup_msix(struct xhci_hcd *xhci) | 226 | static int xhci_setup_msix(struct xhci_hcd *xhci) |
227 | { | 227 | { |
228 | int i, ret = 0; | 228 | int i, ret = 0; |
229 | struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller); | 229 | struct usb_hcd *hcd = xhci_to_hcd(xhci); |
230 | struct pci_dev *pdev = to_pci_dev(hcd->self.controller); | ||
230 | 231 | ||
231 | /* | 232 | /* |
232 | * calculate number of msi-x vectors supported. | 233 | * calculate number of msi-x vectors supported. |
@@ -265,6 +266,7 @@ static int xhci_setup_msix(struct xhci_hcd *xhci) | |||
265 | goto disable_msix; | 266 | goto disable_msix; |
266 | } | 267 | } |
267 | 268 | ||
269 | hcd->msix_enabled = 1; | ||
268 | return ret; | 270 | return ret; |
269 | 271 | ||
270 | disable_msix: | 272 | disable_msix: |
@@ -280,7 +282,8 @@ free_entries: | |||
280 | /* Free any IRQs and disable MSI-X */ | 282 | /* Free any IRQs and disable MSI-X */ |
281 | static void xhci_cleanup_msix(struct xhci_hcd *xhci) | 283 | static void xhci_cleanup_msix(struct xhci_hcd *xhci) |
282 | { | 284 | { |
283 | struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller); | 285 | struct usb_hcd *hcd = xhci_to_hcd(xhci); |
286 | struct pci_dev *pdev = to_pci_dev(hcd->self.controller); | ||
284 | 287 | ||
285 | xhci_free_irq(xhci); | 288 | xhci_free_irq(xhci); |
286 | 289 | ||
@@ -292,6 +295,7 @@ static void xhci_cleanup_msix(struct xhci_hcd *xhci) | |||
292 | pci_disable_msi(pdev); | 295 | pci_disable_msi(pdev); |
293 | } | 296 | } |
294 | 297 | ||
298 | hcd->msix_enabled = 0; | ||
295 | return; | 299 | return; |
296 | } | 300 | } |
297 | 301 | ||
@@ -647,6 +651,7 @@ int xhci_suspend(struct xhci_hcd *xhci) | |||
647 | int rc = 0; | 651 | int rc = 0; |
648 | struct usb_hcd *hcd = xhci_to_hcd(xhci); | 652 | struct usb_hcd *hcd = xhci_to_hcd(xhci); |
649 | u32 command; | 653 | u32 command; |
654 | int i; | ||
650 | 655 | ||
651 | spin_lock_irq(&xhci->lock); | 656 | spin_lock_irq(&xhci->lock); |
652 | clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); | 657 | clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); |
@@ -677,10 +682,15 @@ int xhci_suspend(struct xhci_hcd *xhci) | |||
677 | spin_unlock_irq(&xhci->lock); | 682 | spin_unlock_irq(&xhci->lock); |
678 | return -ETIMEDOUT; | 683 | return -ETIMEDOUT; |
679 | } | 684 | } |
680 | /* step 5: remove core well power */ | ||
681 | xhci_cleanup_msix(xhci); | ||
682 | spin_unlock_irq(&xhci->lock); | 685 | spin_unlock_irq(&xhci->lock); |
683 | 686 | ||
687 | /* step 5: remove core well power */ | ||
688 | /* synchronize irq when using MSI-X */ | ||
689 | if (xhci->msix_entries) { | ||
690 | for (i = 0; i < xhci->msix_count; i++) | ||
691 | synchronize_irq(xhci->msix_entries[i].vector); | ||
692 | } | ||
693 | |||
684 | return rc; | 694 | return rc; |
685 | } | 695 | } |
686 | 696 | ||
@@ -694,7 +704,6 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated) | |||
694 | { | 704 | { |
695 | u32 command, temp = 0; | 705 | u32 command, temp = 0; |
696 | struct usb_hcd *hcd = xhci_to_hcd(xhci); | 706 | struct usb_hcd *hcd = xhci_to_hcd(xhci); |
697 | struct pci_dev *pdev = to_pci_dev(hcd->self.controller); | ||
698 | int old_state, retval; | 707 | int old_state, retval; |
699 | 708 | ||
700 | old_state = hcd->state; | 709 | old_state = hcd->state; |
@@ -729,9 +738,8 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated) | |||
729 | xhci_dbg(xhci, "Stop HCD\n"); | 738 | xhci_dbg(xhci, "Stop HCD\n"); |
730 | xhci_halt(xhci); | 739 | xhci_halt(xhci); |
731 | xhci_reset(xhci); | 740 | xhci_reset(xhci); |
732 | if (hibernated) | ||
733 | xhci_cleanup_msix(xhci); | ||
734 | spin_unlock_irq(&xhci->lock); | 741 | spin_unlock_irq(&xhci->lock); |
742 | xhci_cleanup_msix(xhci); | ||
735 | 743 | ||
736 | #ifdef CONFIG_USB_XHCI_HCD_DEBUGGING | 744 | #ifdef CONFIG_USB_XHCI_HCD_DEBUGGING |
737 | /* Tell the event ring poll function not to reschedule */ | 745 | /* Tell the event ring poll function not to reschedule */ |
@@ -765,30 +773,6 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated) | |||
765 | return retval; | 773 | return retval; |
766 | } | 774 | } |
767 | 775 | ||
768 | spin_unlock_irq(&xhci->lock); | ||
769 | /* Re-setup MSI-X */ | ||
770 | if (hcd->irq) | ||
771 | free_irq(hcd->irq, hcd); | ||
772 | hcd->irq = -1; | ||
773 | |||
774 | retval = xhci_setup_msix(xhci); | ||
775 | if (retval) | ||
776 | /* fall back to msi*/ | ||
777 | retval = xhci_setup_msi(xhci); | ||
778 | |||
779 | if (retval) { | ||
780 | /* fall back to legacy interrupt*/ | ||
781 | retval = request_irq(pdev->irq, &usb_hcd_irq, IRQF_SHARED, | ||
782 | hcd->irq_descr, hcd); | ||
783 | if (retval) { | ||
784 | xhci_err(xhci, "request interrupt %d failed\n", | ||
785 | pdev->irq); | ||
786 | return retval; | ||
787 | } | ||
788 | hcd->irq = pdev->irq; | ||
789 | } | ||
790 | |||
791 | spin_lock_irq(&xhci->lock); | ||
792 | /* step 4: set Run/Stop bit */ | 776 | /* step 4: set Run/Stop bit */ |
793 | command = xhci_readl(xhci, &xhci->op_regs->command); | 777 | command = xhci_readl(xhci, &xhci->op_regs->command); |
794 | command |= CMD_RUN; | 778 | command |= CMD_RUN; |