Split xhci_irq() into top and bottom half handlers. Note that to make this work, we had to fix MSI interrupts as well to use threaded_irqs. Signed-off-by: Felipe Balbi <felipe.balbi@xxxxxxxxxxxxxxx> --- drivers/usb/host/xhci-ring.c | 70 +++++++++++++++++++++++++++++--------------- drivers/usb/host/xhci.c | 15 +++++----- drivers/usb/host/xhci.h | 2 ++ 3 files changed, 57 insertions(+), 30 deletions(-) diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 7cf66212ceae..695b04d7751e 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -2679,36 +2679,18 @@ static int xhci_handle_event(struct xhci_hcd *xhci) return 1; } -/* - * xHCI spec says we can get an interrupt, and if the HC has an error condition, - * we might get bad data out of the event ring. Section 4.10.2.7 has a list of - * indicators of an event TRB error, but we check the status *first* to be safe. - */ -irqreturn_t xhci_irq(struct usb_hcd *hcd) +irqreturn_t xhci_threaded_irq(struct usb_hcd *hcd) { struct xhci_hcd *xhci = hcd_to_xhci(hcd); u32 status; u64 temp_64; union xhci_trb *event_ring_deq; dma_addr_t deq; + unsigned long flags; - spin_lock(&xhci->lock); + spin_lock_irqsave(&xhci->lock, flags); /* Check if the xHC generated the interrupt, or the irq is shared */ status = readl(&xhci->op_regs->status); - if (status == 0xffffffff) - goto hw_died; - - if (!(status & STS_EINT)) { - spin_unlock(&xhci->lock); - return IRQ_NONE; - } - if (status & STS_FATAL) { - xhci_warn(xhci, "WARNING: Host System Error\n"); - xhci_halt(xhci); -hw_died: - spin_unlock(&xhci->lock); - return IRQ_HANDLED; - } /* * Clear the op reg interrupt status first, @@ -2737,7 +2719,7 @@ hw_died: temp_64 = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue); xhci_write_64(xhci, temp_64 | ERST_EHB, &xhci->ir_set->erst_dequeue); - spin_unlock(&xhci->lock); + spin_unlock_irqrestore(&xhci->lock, flags); return IRQ_HANDLED; } @@ -2765,11 +2747,53 @@ hw_died: temp_64 |= ERST_EHB; xhci_write_64(xhci, temp_64, &xhci->ir_set->erst_dequeue); - spin_unlock(&xhci->lock); + spin_unlock_irqrestore(&xhci->lock, flags); return IRQ_HANDLED; } +/* + * xHCI spec says we can get an interrupt, and if the HC has an error condition, + * we might get bad data out of the event ring. Section 4.10.2.7 has a list of + * indicators of an event TRB error, but we check the status *first* to be safe. + */ +irqreturn_t xhci_irq(struct usb_hcd *hcd) +{ + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + u32 status; + + /* Check if the xHC generated the interrupt, or the irq is shared */ + status = readl(&xhci->op_regs->status); + if (status == 0xffffffff) + return IRQ_HANDLED; + + if (!(status & STS_EINT)) + return IRQ_NONE; + + if (status & STS_FATAL) { + xhci_warn(xhci, "WARNING: Host System Error\n"); + xhci_halt(xhci); + return IRQ_HANDLED; + } + + /* + * Clear the op reg interrupt status first, + * so we can receive interrupts from other MSI-X interrupters. + * Write 1 to clear the interrupt status. + */ + status |= STS_EINT; + writel(status, &xhci->op_regs->status); + /* FIXME when MSI-X is supported and there are multiple vectors */ + /* Clear the MSI-X event interrupt status */ + + return IRQ_WAKE_THREAD; +} + +irqreturn_t xhci_msi_threaded_irq(int irq, void *hcd) +{ + return xhci_threaded_irq(hcd); +} + irqreturn_t xhci_msi_irq(int irq, void *hcd) { return xhci_irq(hcd); diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index d51ee0c3cf9f..13af72be51c6 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -239,8 +239,8 @@ static int xhci_setup_msi(struct xhci_hcd *xhci) return ret; } - ret = request_irq(pdev->irq, xhci_msi_irq, - 0, "xhci_hcd", xhci_to_hcd(xhci)); + ret = request_threaded_irq(pdev->irq, xhci_msi_irq, xhci_msi_threaded_irq, + IRQF_ONESHOT, "xhci_hcd", xhci_to_hcd(xhci)); if (ret) { xhci_dbg_trace(xhci, trace_xhci_dbg_init, "disable MSI interrupt"); @@ -312,9 +312,9 @@ static int xhci_setup_msix(struct xhci_hcd *xhci) } for (i = 0; i < xhci->msix_count; i++) { - ret = request_irq(xhci->msix_entries[i].vector, - xhci_msi_irq, - 0, "xhci_hcd", xhci_to_hcd(xhci)); + ret = request_threaded_irq(xhci->msix_entries[i].vector, + xhci_msi_irq, xhci_msi_threaded_irq, + IRQF_ONESHOT, "xhci_hcd", xhci_to_hcd(xhci)); if (ret) goto disable_msix; } @@ -408,8 +408,8 @@ static int xhci_try_enable_msi(struct usb_hcd *hcd) hcd->driver->description, hcd->self.busnum); /* fall back to legacy interrupt*/ - ret = request_irq(pdev->irq, &usb_hcd_irq, IRQF_SHARED, - hcd->irq_descr, hcd); + ret = request_threaded_irq(pdev->irq, usb_hcd_irq, usb_hcd_threaded_irq, + IRQF_SHARED | IRQF_ONESHOT, hcd->irq_descr, hcd); if (ret) { xhci_err(xhci, "request interrupt %d failed\n", pdev->irq); @@ -4989,6 +4989,7 @@ static const struct hc_driver xhci_hc_driver = { * generic hardware linkage */ .irq = xhci_irq, + .threaded_irq = xhci_threaded_irq, .flags = HCD_MEMORY | HCD_USB3 | HCD_SHARED, /* diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index e293e0974f48..a65ccf4b5a0b 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1851,6 +1851,8 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated); int xhci_get_frame(struct usb_hcd *hcd); irqreturn_t xhci_irq(struct usb_hcd *hcd); irqreturn_t xhci_msi_irq(int irq, void *hcd); +irqreturn_t xhci_threaded_irq(struct usb_hcd *hcd); +irqreturn_t xhci_msi_threaded_irq(int irq, void *hcd); int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev); void xhci_free_dev(struct usb_hcd *hcd, struct usb_device *udev); int xhci_alloc_tt_info(struct xhci_hcd *xhci, -- 2.8.0.rc2 -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html