aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndiry Xu <andiry.xu@amd.com>2010-12-27 04:39:02 -0500
committerSarah Sharp <sarah.a.sharp@linux.intel.com>2011-01-14 18:28:52 -0500
commit0029227f1bc30b6c809ae751f9e7af6cef900997 (patch)
tree4962aacd3349c149c1d1567aaa09cd9ac8efec0f
parent7111ebc97ed53a32314011c85a6f235f0dab8ae8 (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
-rw-r--r--drivers/usb/core/hcd-pci.c7
-rw-r--r--drivers/usb/host/xhci.c46
-rw-r--r--include/linux/usb/hcd.h1
3 files changed, 22 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)
226static int xhci_setup_msix(struct xhci_hcd *xhci) 226static 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
270disable_msix: 272disable_msix:
@@ -280,7 +282,8 @@ free_entries:
280/* Free any IRQs and disable MSI-X */ 282/* Free any IRQs and disable MSI-X */
281static void xhci_cleanup_msix(struct xhci_hcd *xhci) 283static 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;
diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h
index dd6ee49a0844..a854fe89484e 100644
--- a/include/linux/usb/hcd.h
+++ b/include/linux/usb/hcd.h
@@ -112,6 +112,7 @@ struct usb_hcd {
112 /* Flags that get set only during HCD registration or removal. */ 112 /* Flags that get set only during HCD registration or removal. */
113 unsigned rh_registered:1;/* is root hub registered? */ 113 unsigned rh_registered:1;/* is root hub registered? */
114 unsigned rh_pollable:1; /* may we poll the root hub? */ 114 unsigned rh_pollable:1; /* may we poll the root hub? */
115 unsigned msix_enabled:1; /* driver has MSI-X enabled? */
115 116
116 /* The next flag is a stopgap, to be removed when all the HCDs 117 /* The next flag is a stopgap, to be removed when all the HCDs
117 * support the new root-hub polling mechanism. */ 118 * support the new root-hub polling mechanism. */