forked from Minki/linux
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
This commit is contained in:
parent
7111ebc97e
commit
0029227f1b
@ -405,7 +405,12 @@ static int suspend_common(struct device *dev, bool do_wakeup)
|
||||
return retval;
|
||||
}
|
||||
|
||||
synchronize_irq(pci_dev->irq);
|
||||
/* If MSI-X is enabled, the driver will have synchronized all vectors
|
||||
* in pci_suspend(). If MSI or legacy PCI is enabled, that will be
|
||||
* synchronized here.
|
||||
*/
|
||||
if (!hcd->msix_enabled)
|
||||
synchronize_irq(pci_dev->irq);
|
||||
|
||||
/* Downstream ports from this root hub should already be quiesced, so
|
||||
* there will be no DMA activity. Now we can shut down the upstream
|
||||
|
@ -226,7 +226,8 @@ static int xhci_setup_msi(struct xhci_hcd *xhci)
|
||||
static int xhci_setup_msix(struct xhci_hcd *xhci)
|
||||
{
|
||||
int i, ret = 0;
|
||||
struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller);
|
||||
struct usb_hcd *hcd = xhci_to_hcd(xhci);
|
||||
struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
|
||||
|
||||
/*
|
||||
* calculate number of msi-x vectors supported.
|
||||
@ -265,6 +266,7 @@ static int xhci_setup_msix(struct xhci_hcd *xhci)
|
||||
goto disable_msix;
|
||||
}
|
||||
|
||||
hcd->msix_enabled = 1;
|
||||
return ret;
|
||||
|
||||
disable_msix:
|
||||
@ -280,7 +282,8 @@ free_entries:
|
||||
/* Free any IRQs and disable MSI-X */
|
||||
static void xhci_cleanup_msix(struct xhci_hcd *xhci)
|
||||
{
|
||||
struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller);
|
||||
struct usb_hcd *hcd = xhci_to_hcd(xhci);
|
||||
struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
|
||||
|
||||
xhci_free_irq(xhci);
|
||||
|
||||
@ -292,6 +295,7 @@ static void xhci_cleanup_msix(struct xhci_hcd *xhci)
|
||||
pci_disable_msi(pdev);
|
||||
}
|
||||
|
||||
hcd->msix_enabled = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -647,6 +651,7 @@ int xhci_suspend(struct xhci_hcd *xhci)
|
||||
int rc = 0;
|
||||
struct usb_hcd *hcd = xhci_to_hcd(xhci);
|
||||
u32 command;
|
||||
int i;
|
||||
|
||||
spin_lock_irq(&xhci->lock);
|
||||
clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
|
||||
@ -677,10 +682,15 @@ int xhci_suspend(struct xhci_hcd *xhci)
|
||||
spin_unlock_irq(&xhci->lock);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
/* step 5: remove core well power */
|
||||
xhci_cleanup_msix(xhci);
|
||||
spin_unlock_irq(&xhci->lock);
|
||||
|
||||
/* step 5: remove core well power */
|
||||
/* synchronize irq when using MSI-X */
|
||||
if (xhci->msix_entries) {
|
||||
for (i = 0; i < xhci->msix_count; i++)
|
||||
synchronize_irq(xhci->msix_entries[i].vector);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -694,7 +704,6 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated)
|
||||
{
|
||||
u32 command, temp = 0;
|
||||
struct usb_hcd *hcd = xhci_to_hcd(xhci);
|
||||
struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
|
||||
int old_state, retval;
|
||||
|
||||
old_state = hcd->state;
|
||||
@ -729,9 +738,8 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated)
|
||||
xhci_dbg(xhci, "Stop HCD\n");
|
||||
xhci_halt(xhci);
|
||||
xhci_reset(xhci);
|
||||
if (hibernated)
|
||||
xhci_cleanup_msix(xhci);
|
||||
spin_unlock_irq(&xhci->lock);
|
||||
xhci_cleanup_msix(xhci);
|
||||
|
||||
#ifdef CONFIG_USB_XHCI_HCD_DEBUGGING
|
||||
/* Tell the event ring poll function not to reschedule */
|
||||
@ -765,30 +773,6 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated)
|
||||
return retval;
|
||||
}
|
||||
|
||||
spin_unlock_irq(&xhci->lock);
|
||||
/* Re-setup MSI-X */
|
||||
if (hcd->irq)
|
||||
free_irq(hcd->irq, hcd);
|
||||
hcd->irq = -1;
|
||||
|
||||
retval = xhci_setup_msix(xhci);
|
||||
if (retval)
|
||||
/* fall back to msi*/
|
||||
retval = xhci_setup_msi(xhci);
|
||||
|
||||
if (retval) {
|
||||
/* fall back to legacy interrupt*/
|
||||
retval = request_irq(pdev->irq, &usb_hcd_irq, IRQF_SHARED,
|
||||
hcd->irq_descr, hcd);
|
||||
if (retval) {
|
||||
xhci_err(xhci, "request interrupt %d failed\n",
|
||||
pdev->irq);
|
||||
return retval;
|
||||
}
|
||||
hcd->irq = pdev->irq;
|
||||
}
|
||||
|
||||
spin_lock_irq(&xhci->lock);
|
||||
/* step 4: set Run/Stop bit */
|
||||
command = xhci_readl(xhci, &xhci->op_regs->command);
|
||||
command |= CMD_RUN;
|
||||
|
@ -112,6 +112,7 @@ struct usb_hcd {
|
||||
/* Flags that get set only during HCD registration or removal. */
|
||||
unsigned rh_registered:1;/* is root hub registered? */
|
||||
unsigned rh_pollable:1; /* may we poll the root hub? */
|
||||
unsigned msix_enabled:1; /* driver has MSI-X enabled? */
|
||||
|
||||
/* The next flag is a stopgap, to be removed when all the HCDs
|
||||
* support the new root-hub polling mechanism. */
|
||||
|
Loading…
Reference in New Issue
Block a user