[PATCH 3/7] usb: host: xhci: add a threaded irq handler

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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



[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux