[RFC 3/4] xHCI: Enable multiple interrupters

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

 



This patch enable all the interupters we allocated and modifies xhci_irq()
and related functions to check all the interrupters when an interrupt is
triggered.

When using MSI-X, the number of interrupters enabled will be equal to
xhci->msix_count; When using MSI or PCI INTx, the number of interrupters
enabled will be 1.

When doing system suspend and resume, register of all the interrupters enabled
will be saved and restored.

Signed-off-by: Andiry Xu <andiry.xu@xxxxxxx>
---
 drivers/usb/host/xhci-mem.c  |   11 ++-
 drivers/usb/host/xhci-ring.c |  210 +++++++++++++++++++++++-------------------
 drivers/usb/host/xhci.c      |  132 +++++++++++++++++---------
 drivers/usb/host/xhci.h      |    8 +-
 4 files changed, 213 insertions(+), 148 deletions(-)

diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
index 4355fa1..3468b8e 100644
--- a/drivers/usb/host/xhci-mem.c
+++ b/drivers/usb/host/xhci-mem.c
@@ -1408,6 +1408,9 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
 		if (xhci->erst[i])
 			kfree(xhci->erst[i]);
 		xhci_dbg(xhci, "Freed ERST %d\n", i);
+		if (xhci->s3.ir_set[i])
+			kfree(xhci->s3.ir_set[i]);
+		xhci_dbg(xhci, "Freed intr reg %d for suspend\n", i);
 		if (xhci->event_ring[i])
 			xhci_ring_free(xhci, xhci->event_ring[i]);
 		xhci->event_ring[i] = NULL;
@@ -1946,6 +1949,13 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
 		xhci->erst[i] = kzalloc(sizeof(struct xhci_erst), flags);
 		if (!xhci->erst[i])
 			goto fail;
+
+		xhci_dbg(xhci, "// Allocating intr reg %d for suspend\n", i);
+		xhci->s3.ir_set[i] = kzalloc(sizeof(struct xhci_intr_reg),
+						flags);
+		if (!xhci->s3.ir_set[i])
+			goto fail;
+
 		xhci_dbg(xhci, "// Allocating event ring %d\n", i);
 		xhci->event_ring[i] = xhci_ring_alloc(xhci, ERST_NUM_SEGS,
 					false, flags);
@@ -2008,7 +2018,6 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
 		xhci_dbg(xhci, "Wrote ERST address to ir_set %d.\n", i);
 		xhci_print_ir_set(xhci, xhci->ir_set, i);
 	}
-	xhci->ir_set = (void *) &xhci->run_regs->ir_set[0];
 
 	/*
 	 * XXX: Might need to set the Interrupter Moderation Register to
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index 864850e..7d4ee35 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -94,11 +94,11 @@ dma_addr_t xhci_trb_virt_to_dma(struct xhci_segment *seg,
  * or was the previous TRB the last TRB on the last segment in the ERST?
  */
 static inline bool last_trb_on_last_seg(struct xhci_hcd *xhci, struct xhci_ring *ring,
-		struct xhci_segment *seg, union xhci_trb *trb)
+		struct xhci_segment *seg, union xhci_trb *trb, int index)
 {
-	if (ring == xhci->event_ring[0])
+	if (ring == xhci->event_ring[index])
 		return (trb == &seg->trbs[TRBS_PER_SEGMENT]) &&
-			(seg->next == xhci->event_ring[0]->first_seg);
+			(seg->next == xhci->event_ring[index]->first_seg);
 	else
 		return trb->link.control & LINK_TOGGLE;
 }
@@ -108,9 +108,9 @@ static inline bool last_trb_on_last_seg(struct xhci_hcd *xhci, struct xhci_ring
  * event seg?
  */
 static inline int last_trb(struct xhci_hcd *xhci, struct xhci_ring *ring,
-		struct xhci_segment *seg, union xhci_trb *trb)
+		struct xhci_segment *seg, union xhci_trb *trb, int index)
 {
-	if (ring == xhci->event_ring[0])
+	if (ring == xhci->event_ring[index])
 		return trb == &seg->trbs[TRBS_PER_SEGMENT];
 	else
 		return (trb->link.control & TRB_TYPE_BITMASK) == TRB_TYPE(TRB_LINK);
@@ -131,7 +131,7 @@ static void next_trb(struct xhci_hcd *xhci,
 		struct xhci_segment **seg,
 		union xhci_trb **trb)
 {
-	if (last_trb(xhci, ring, *seg, *trb)) {
+	if (last_trb(xhci, ring, *seg, *trb, 0)) {
 		*seg = (*seg)->next;
 		*trb = ((*seg)->trbs);
 	} else {
@@ -143,7 +143,8 @@ static void next_trb(struct xhci_hcd *xhci,
  * See Cycle bit rules. SW is the consumer for the event ring only.
  * Don't make a ring full of link TRBs.  That would be dumb and this would loop.
  */
-static void inc_deq(struct xhci_hcd *xhci, struct xhci_ring *ring, bool consumer)
+static void inc_deq(struct xhci_hcd *xhci, struct xhci_ring *ring,
+			bool consumer, int index)
 {
 	union xhci_trb *next = ++(ring->dequeue);
 	unsigned long long addr;
@@ -152,8 +153,8 @@ static void inc_deq(struct xhci_hcd *xhci, struct xhci_ring *ring, bool consumer
 	/* Update the dequeue pointer further if that was a link TRB or we're at
 	 * the end of an event ring segment (which doesn't have link TRBS)
 	 */
-	while (last_trb(xhci, ring, ring->deq_seg, next)) {
-		if (consumer && last_trb_on_last_seg(xhci, ring, ring->deq_seg, next)) {
+	while (last_trb(xhci, ring, ring->deq_seg, next, index)) {
+		if (consumer && last_trb_on_last_seg(xhci, ring, ring->deq_seg, next, index)) {
 			ring->cycle_state = (ring->cycle_state ? 0 : 1);
 			if (!in_interrupt())
 				xhci_dbg(xhci, "Toggle cycle state for ring %p = %i\n",
@@ -165,8 +166,9 @@ static void inc_deq(struct xhci_hcd *xhci, struct xhci_ring *ring, bool consumer
 		next = ring->dequeue;
 	}
 	addr = (unsigned long long) xhci_trb_virt_to_dma(ring->deq_seg, ring->dequeue);
-	if (ring == xhci->event_ring[0])
-		xhci_dbg(xhci, "Event ring 0 deq = 0x%llx (DMA)\n", addr);
+	if (ring == xhci->event_ring[index])
+		xhci_dbg(xhci, "Event ring %d deq = 0x%llx (DMA)\n",
+				index, addr);
 	else if (ring == xhci->cmd_ring)
 		xhci_dbg(xhci, "Command ring deq = 0x%llx (DMA)\n", addr);
 	else
@@ -191,7 +193,7 @@ static void inc_deq(struct xhci_hcd *xhci, struct xhci_ring *ring, bool consumer
  *			prepare_transfer()?
  */
 static void inc_enq(struct xhci_hcd *xhci, struct xhci_ring *ring,
-		bool consumer, bool more_trbs_coming)
+		bool consumer, bool more_trbs_coming, int index)
 {
 	u32 chain;
 	union xhci_trb *next;
@@ -204,9 +206,9 @@ static void inc_enq(struct xhci_hcd *xhci, struct xhci_ring *ring,
 	/* Update the dequeue pointer further if that was a link TRB or we're at
 	 * the end of an event ring segment (which doesn't have link TRBS)
 	 */
-	while (last_trb(xhci, ring, ring->enq_seg, next)) {
+	while (last_trb(xhci, ring, ring->enq_seg, next, index)) {
 		if (!consumer) {
-			if (ring != xhci->event_ring[0]) {
+			if (ring != xhci->event_ring[index]) {
 				/*
 				 * If the caller doesn't plan on enqueueing more
 				 * TDs before ringing the doorbell, then we
@@ -231,7 +233,8 @@ static void inc_enq(struct xhci_hcd *xhci, struct xhci_ring *ring,
 				next->link.control ^= TRB_CYCLE;
 			}
 			/* Toggle the cycle bit after the last ring segment. */
-			if (last_trb_on_last_seg(xhci, ring, ring->enq_seg, next)) {
+			if (last_trb_on_last_seg(xhci, ring, ring->enq_seg,
+				next, index)) {
 				ring->cycle_state = (ring->cycle_state ? 0 : 1);
 				if (!in_interrupt())
 					xhci_dbg(xhci, "Toggle cycle state for ring %p = %i\n",
@@ -244,8 +247,9 @@ static void inc_enq(struct xhci_hcd *xhci, struct xhci_ring *ring,
 		next = ring->enqueue;
 	}
 	addr = (unsigned long long) xhci_trb_virt_to_dma(ring->enq_seg, ring->enqueue);
-	if (ring == xhci->event_ring[0])
-		xhci_dbg(xhci, "Event ring enq = 0x%llx (DMA)\n", addr);
+	if (ring == xhci->event_ring[index])
+		xhci_dbg(xhci, "Event ring %d enq = 0x%llx (DMA)\n",
+				index, addr);
 	else if (ring == xhci->cmd_ring)
 		xhci_dbg(xhci, "Command ring enq = 0x%llx (DMA)\n", addr);
 	else
@@ -269,7 +273,7 @@ static int room_on_ring(struct xhci_hcd *xhci, struct xhci_ring *ring,
 
 	/* If we are currently pointing to a link TRB, advance the
 	 * enqueue pointer before checking for space */
-	while (last_trb(xhci, ring, enq_seg, enq)) {
+	while (last_trb(xhci, ring, enq_seg, enq, 0)) {
 		enq_seg = enq_seg->next;
 		enq = enq_seg->trbs;
 	}
@@ -297,7 +301,7 @@ static int room_on_ring(struct xhci_hcd *xhci, struct xhci_ring *ring,
 		if (enq == ring->dequeue)
 			return 0;
 		enq++;
-		while (last_trb(xhci, ring, enq_seg, enq)) {
+		while (last_trb(xhci, ring, enq_seg, enq, 0)) {
 			enq_seg = enq_seg->next;
 			enq = enq_seg->trbs;
 		}
@@ -1143,7 +1147,7 @@ bandwidth_change:
 		xhci->error_bitmask |= 1 << 6;
 		break;
 	}
-	inc_deq(xhci, xhci->cmd_ring, false);
+	inc_deq(xhci, xhci->cmd_ring, false, 0);
 }
 
 static void handle_vendor_event(struct xhci_hcd *xhci,
@@ -1158,7 +1162,7 @@ static void handle_vendor_event(struct xhci_hcd *xhci,
 }
 
 static void handle_port_status(struct xhci_hcd *xhci,
-		union xhci_trb *event)
+		union xhci_trb *event, int index)
 {
 	struct usb_hcd *hcd = xhci_to_hcd(xhci);
 	u32 port_id;
@@ -1227,7 +1231,7 @@ static void handle_port_status(struct xhci_hcd *xhci,
 
 cleanup:
 	/* Update event ring dequeue pointer before dropping the lock */
-	inc_deq(xhci, xhci->event_ring[0], true);
+	inc_deq(xhci, xhci->event_ring[index], true, index);
 
 	spin_unlock(&xhci->lock);
 	/* Pass this up to the core */
@@ -1414,8 +1418,8 @@ static int finish_td(struct xhci_hcd *xhci, struct xhci_td *td,
 		} else {
 			/* Update ring dequeue pointer */
 			while (ep_ring->dequeue != td->last_trb)
-				inc_deq(xhci, ep_ring, false);
-			inc_deq(xhci, ep_ring, false);
+				inc_deq(xhci, ep_ring, false, 0);
+			inc_deq(xhci, ep_ring, false, 0);
 		}
 
 td_cleanup:
@@ -1460,7 +1464,7 @@ td_cleanup:
  */
 static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td,
 	union xhci_trb *event_trb, struct xhci_transfer_event *event,
-	struct xhci_virt_ep *ep, int *status)
+	struct xhci_virt_ep *ep, int *status, int index)
 {
 	struct xhci_virt_device *xdev;
 	struct xhci_ring *ep_ring;
@@ -1476,7 +1480,7 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td,
 	ep_ctx = xhci_get_ep_ctx(xhci, xdev->out_ctx, ep_index);
 	trb_comp_code = GET_COMP_CODE(event->transfer_len);
 
-	xhci_debug_trb(xhci, xhci->event_ring[0]->dequeue);
+	xhci_debug_trb(xhci, xhci->event_ring[index]->dequeue);
 	switch (trb_comp_code) {
 	case COMP_SUCCESS:
 		if (event_trb == ep_ring->dequeue) {
@@ -1624,8 +1628,8 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td,
 		td->urb->iso_frame_desc[idx].actual_length = 0;
 		/* Update ring dequeue pointer */
 		while (ep_ring->dequeue != td->last_trb)
-			inc_deq(xhci, ep_ring, false);
-		inc_deq(xhci, ep_ring, false);
+			inc_deq(xhci, ep_ring, false, 0);
+		inc_deq(xhci, ep_ring, false, 0);
 		return finish_td(xhci, td, event_trb, event, ep, status, true);
 	}
 
@@ -1776,7 +1780,7 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_td *td,
  * At this point, the host controller is probably hosed and should be reset.
  */
 static int handle_tx_event(struct xhci_hcd *xhci,
-		struct xhci_transfer_event *event)
+		struct xhci_transfer_event *event, int index)
 {
 	struct xhci_virt_device *xdev;
 	struct xhci_virt_ep *ep;
@@ -1955,7 +1959,7 @@ static int handle_tx_event(struct xhci_hcd *xhci,
 		 */
 		if (usb_endpoint_xfer_control(&td->urb->ep->desc))
 			ret = process_ctrl_td(xhci, td, event_trb, event, ep,
-						 &status);
+						 &status, index);
 		else if (usb_endpoint_xfer_isoc(&td->urb->ep->desc))
 			ret = process_isoc_td(xhci, td, event_trb, event, ep,
 						 &status);
@@ -1969,7 +1973,7 @@ cleanup:
 		 * Will roll back to continue process missed tds.
 		 */
 		if (trb_comp_code == COMP_MISSED_INT || !ep->skip) {
-			inc_deq(xhci, xhci->event_ring[0], true);
+			inc_deq(xhci, xhci->event_ring[index], true, index);
 		}
 
 		if (ret) {
@@ -2009,26 +2013,26 @@ cleanup:
  * This function handles all OS-owned events on the event ring.  It may drop
  * xhci->lock between event processing (e.g. to pass up port status changes).
  */
-static void xhci_handle_event(struct xhci_hcd *xhci)
+static void xhci_handle_event(struct xhci_hcd *xhci, int index)
 {
 	union xhci_trb *event;
 	int update_ptrs = 1;
 	int ret;
 
 	xhci_dbg(xhci, "In %s\n", __func__);
-	if (!xhci->event_ring[0] || !xhci->event_ring[0]->dequeue) {
+	if (!xhci->event_ring[index] || !xhci->event_ring[index]->dequeue) {
 		xhci->error_bitmask |= 1 << 1;
 		return;
 	}
 
-	event = xhci->event_ring[0]->dequeue;
+	event = xhci->event_ring[index]->dequeue;
 	/* Does the HC or OS own the TRB? */
 	if ((event->event_cmd.flags & TRB_CYCLE) !=
-			xhci->event_ring[0]->cycle_state) {
+			xhci->event_ring[index]->cycle_state) {
 		xhci->error_bitmask |= 1 << 2;
 		return;
 	}
-	xhci_dbg(xhci, "%s - OS owns TRB\n", __func__);
+	xhci_dbg(xhci, "%s - OS owns TRB for event ring %d\n", __func__, index);
 
 	/* FIXME: Handle more event types. */
 	switch ((event->event_cmd.flags & TRB_TYPE_BITMASK)) {
@@ -2039,13 +2043,13 @@ static void xhci_handle_event(struct xhci_hcd *xhci)
 		break;
 	case TRB_TYPE(TRB_PORT_STATUS):
 		xhci_dbg(xhci, "%s - calling handle_port_status\n", __func__);
-		handle_port_status(xhci, event);
+		handle_port_status(xhci, event, index);
 		xhci_dbg(xhci, "%s - returned from handle_port_status\n", __func__);
 		update_ptrs = 0;
 		break;
 	case TRB_TYPE(TRB_TRANSFER):
 		xhci_dbg(xhci, "%s - calling handle_tx_event\n", __func__);
-		ret = handle_tx_event(xhci, &event->trans_event);
+		ret = handle_tx_event(xhci, &event->trans_event, index);
 		xhci_dbg(xhci, "%s - returned from handle_tx_event\n", __func__);
 		if (ret < 0)
 			xhci->error_bitmask |= 1 << 9;
@@ -2069,10 +2073,10 @@ static void xhci_handle_event(struct xhci_hcd *xhci)
 
 	if (update_ptrs)
 		/* Update SW event ring dequeue pointer */
-		inc_deq(xhci, xhci->event_ring[0], true);
+		inc_deq(xhci, xhci->event_ring[index], true, index);
 
 	/* Are there more items on the event ring? */
-	xhci_handle_event(xhci);
+	xhci_handle_event(xhci, index);
 }
 
 /*
@@ -2088,9 +2092,9 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd)
 	u64 temp_64;
 	union xhci_trb *event_ring_deq;
 	dma_addr_t deq;
+	int i;
 
 	spin_lock(&xhci->lock);
-	trb = xhci->event_ring[0]->dequeue;
 	/* Check if the xHC generated the interrupt, or the irq is shared */
 	status = xhci_readl(xhci, &xhci->op_regs->status);
 	if (status == 0xffffffff)
@@ -2100,15 +2104,6 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd)
 		spin_unlock(&xhci->lock);
 		return IRQ_NONE;
 	}
-	xhci_dbg(xhci, "op reg status = %08x\n", status);
-	xhci_dbg(xhci, "Event ring dequeue ptr:\n");
-	xhci_dbg(xhci, "@%llx %08x %08x %08x %08x\n",
-			(unsigned long long)
-			xhci_trb_virt_to_dma(xhci->event_ring[0]->deq_seg, trb),
-			lower_32_bits(trb->link.segment_ptr),
-			upper_32_bits(trb->link.segment_ptr),
-			(unsigned int) trb->link.intr_target,
-			(unsigned int) trb->link.control);
 
 	if (status & STS_FATAL) {
 		xhci_warn(xhci, "WARNING: Host System Error\n");
@@ -2118,7 +2113,7 @@ hw_died:
 		spin_unlock(&xhci->lock);
 		return -ESHUTDOWN;
 	}
-
+	xhci_dbg(xhci, "op reg status = %08x\n", status);
 	/*
 	 * Clear the op reg interrupt status first,
 	 * so we can receive interrupts from other MSI-X interrupters.
@@ -2126,53 +2121,75 @@ hw_died:
 	 */
 	status |= STS_EINT;
 	xhci_writel(xhci, status, &xhci->op_regs->status);
-	/* FIXME when MSI-X is supported and there are multiple vectors */
-	/* Clear the MSI-X event interrupt status */
 
-	if (hcd->irq != -1) {
-		u32 irq_pending;
-		/* Acknowledge the PCI interrupt */
-		irq_pending = xhci_readl(xhci, &xhci->ir_set->irq_pending);
-		irq_pending |= 0x3;
-		xhci_writel(xhci, irq_pending, &xhci->ir_set->irq_pending);
-	}
+	/* Check all the interrupters */
+	for (i = 0; i < xhci->intr_num; i++) {
+		xhci->ir_set = (void *) &xhci->run_regs->ir_set[i];
+		if (hcd->irq != -1) {
+			u32 irq_pending;
+			/* Acknowledge the PCI interrupt */
+			irq_pending =
+				xhci_readl(xhci, &xhci->ir_set->irq_pending);
+			/* If the interrupt is not from this interrupter,
+			 * continue immediately
+			 */
+			if (!(irq_pending & 0x1))
+				continue;
+			irq_pending |= 0x3;
+			xhci_writel(xhci, irq_pending,
+				&xhci->ir_set->irq_pending);
+		}
 
-	if (xhci->xhc_state & XHCI_STATE_DYING) {
-		xhci_dbg(xhci, "xHCI dying, ignoring interrupt. "
-				"Shouldn't IRQs be disabled?\n");
-		/* Clear the event handler busy flag (RW1C);
-		 * the event ring should be empty.
-		 */
-		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);
+		trb = xhci->event_ring[i]->dequeue;
+		xhci_dbg(xhci, "Event ring %d dequeue ptr:\n", i);
+		xhci_dbg(xhci, "@%llx %08x %08x %08x %08x\n",
+			(unsigned long long)
+			xhci_trb_virt_to_dma(xhci->event_ring[i]->deq_seg, trb),
+			lower_32_bits(trb->link.segment_ptr),
+			upper_32_bits(trb->link.segment_ptr),
+			(unsigned int) trb->link.intr_target,
+			(unsigned int) trb->link.control);
 
-		return IRQ_HANDLED;
-	}
+		if (xhci->xhc_state & XHCI_STATE_DYING) {
+			xhci_dbg(xhci, "xHCI dying, ignoring interrupt. "
+					"Shouldn't IRQs be disabled?\n");
+			/* Clear the event handler busy flag (RW1C);
+			 * the event ring should be empty.
+			 */
+			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);
 
-	event_ring_deq = xhci->event_ring[0]->dequeue;
-	/* FIXME this should be a delayed service routine
-	 * that clears the EHB.
-	 */
-	xhci_handle_event(xhci);
+			return IRQ_HANDLED;
+		}
 
-	temp_64 = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue);
-	/* If necessary, update the HW's version of the event ring deq ptr. */
-	if (event_ring_deq != xhci->event_ring[0]->dequeue) {
-		deq = xhci_trb_virt_to_dma(xhci->event_ring[0]->deq_seg,
-				xhci->event_ring[0]->dequeue);
-		if (deq == 0)
-			xhci_warn(xhci, "WARN something wrong with SW event "
-					"ring dequeue ptr.\n");
-		/* Update HC event ring dequeue pointer */
-		temp_64 &= ERST_PTR_MASK;
-		temp_64 |= ((u64) deq & (u64) ~ERST_PTR_MASK);
-	}
+		event_ring_deq = xhci->event_ring[i]->dequeue;
+		/* FIXME this should be a delayed service routine
+		 * that clears the EHB.
+		 */
+		xhci_handle_event(xhci, i);
 
-	/* Clear the event handler busy flag (RW1C); event ring is empty. */
-	temp_64 |= ERST_EHB;
-	xhci_write_64(xhci, temp_64, &xhci->ir_set->erst_dequeue);
+		temp_64 = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue);
+		/* If necessary, update the HW's version of the event ring
+		 * deq ptr. */
+		if (event_ring_deq != xhci->event_ring[i]->dequeue) {
+			deq = xhci_trb_virt_to_dma(xhci->event_ring[i]->deq_seg,
+					xhci->event_ring[i]->dequeue);
+			if (deq == 0)
+				xhci_warn(xhci, "WARN something wrong with SW "
+						"event ring dequeue ptr.\n");
+			/* Update HC event ring dequeue pointer */
+			temp_64 &= ERST_PTR_MASK;
+			temp_64 |= ((u64) deq & (u64) ~ERST_PTR_MASK);
+		}
+
+		/* Clear the event handler busy flag (RW1C);
+		 * event ring is empty. */
+		temp_64 |= ERST_EHB;
+		xhci_write_64(xhci, temp_64, &xhci->ir_set->erst_dequeue);
+	}
 
 	spin_unlock(&xhci->lock);
 
@@ -2210,7 +2227,7 @@ static void queue_trb(struct xhci_hcd *xhci, struct xhci_ring *ring,
 	trb->field[1] = field2;
 	trb->field[2] = field3;
 	trb->field[3] = field4;
-	inc_enq(xhci, ring, consumer, more_trbs_coming);
+	inc_enq(xhci, ring, consumer, more_trbs_coming, 0);
 }
 
 /*
@@ -2261,7 +2278,7 @@ static int prepare_ring(struct xhci_hcd *xhci, struct xhci_ring *ep_ring,
 		xhci_dbg(xhci, "prepare_ring: pointing to link trb\n");
 		next = ring->enqueue;
 
-		while (last_trb(xhci, ring, ring->enq_seg, next)) {
+		while (last_trb(xhci, ring, ring->enq_seg, next, 0)) {
 
 			/* If we're not dealing with 0.95 hardware,
 			 * clear the chain bit.
@@ -2275,7 +2292,8 @@ static int prepare_ring(struct xhci_hcd *xhci, struct xhci_ring *ep_ring,
 			next->link.control ^= (u32) TRB_CYCLE;
 
 			/* Toggle the cycle bit after the last ring segment. */
-			if (last_trb_on_last_seg(xhci, ring, ring->enq_seg, next)) {
+			if (last_trb_on_last_seg(xhci, ring, ring->enq_seg,
+				next, 0)) {
 				ring->cycle_state = (ring->cycle_state ? 0 : 1);
 				if (!in_interrupt()) {
 					xhci_dbg(xhci, "queue_trb: Toggle cycle "
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 26ff10e..4cfe72c 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -215,8 +215,10 @@ static int xhci_setup_msi(struct xhci_hcd *xhci)
 	if (ret) {
 		xhci_err(xhci, "disable MSI interrupt\n");
 		pci_disable_msi(pdev);
+		return ret;
 	}
 
+	xhci->intr_num = 1;
 	return ret;
 }
 
@@ -257,6 +259,7 @@ static int xhci_setup_msix(struct xhci_hcd *xhci)
 	}
 
 	hcd->msix_enabled = 1;
+	xhci->intr_num = xhci->msix_count;
 	return ret;
 
 disable_msix:
@@ -327,8 +330,6 @@ void xhci_event_ring_work(unsigned long arg)
 	struct xhci_hcd *xhci = (struct xhci_hcd *) arg;
 	int i, j;
 
-	xhci_dbg(xhci, "Poll event ring: %lu\n", jiffies);
-
 	spin_lock_irqsave(&xhci->lock, flags);
 	temp = xhci_readl(xhci, &xhci->op_regs->status);
 	xhci_dbg(xhci, "op reg status = 0x%x\n", temp);
@@ -338,17 +339,26 @@ void xhci_event_ring_work(unsigned long arg)
 		return;
 	}
 
-	temp = xhci_readl(xhci, &xhci->ir_set->irq_pending);
-	xhci_dbg(xhci, "ir_set 0 pending = 0x%x\n", temp);
-	xhci_dbg(xhci, "No-op commands handled = %d\n", xhci->noops_handled);
-	xhci_dbg(xhci, "HC error bitmask = 0x%x\n", xhci->error_bitmask);
-	xhci->error_bitmask = 0;
-	xhci_dbg(xhci, "Event ring:\n");
-	xhci_debug_segment(xhci, xhci->event_ring[0]->deq_seg);
-	xhci_dbg_ring_ptrs(xhci, xhci->event_ring[0]);
-	temp_64 = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue);
-	temp_64 &= ~ERST_PTR_MASK;
-	xhci_dbg(xhci, "ERST deq = 64'h%0lx\n", (long unsigned int) temp_64);
+	for (i = 0; i < xhci->intr_num; i++) {
+		xhci_dbg(xhci, "Poll event ring %d: %lu\n", i, jiffies);
+		xhci->ir_set = (void *) &xhci->run_regs->ir_set[i];
+
+		temp = xhci_readl(xhci, &xhci->ir_set->irq_pending);
+		xhci_dbg(xhci, "ir_set %d pending = 0x%x\n", i, temp);
+		xhci_dbg(xhci, "No-op commands handled = %d\n",
+				xhci->noops_handled);
+		xhci_dbg(xhci, "HC error bitmask = 0x%x\n",
+				xhci->error_bitmask);
+		xhci->error_bitmask = 0;
+		xhci_dbg(xhci, "Event ring %d:\n", i);
+		xhci_debug_segment(xhci, xhci->event_ring[i]->deq_seg);
+		xhci_dbg_ring_ptrs(xhci, xhci->event_ring[i]);
+		temp_64 = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue);
+		temp_64 &= ~ERST_PTR_MASK;
+		xhci_dbg(xhci, "ERST %d deq = 64'h%0lx\n", i,
+				(long unsigned int) temp_64);
+	}
+
 	xhci_dbg(xhci, "Command ring:\n");
 	xhci_debug_segment(xhci, xhci->cmd_ring->deq_seg);
 	xhci_dbg_ring_ptrs(xhci, xhci->cmd_ring);
@@ -393,6 +403,7 @@ int xhci_run(struct usb_hcd *hcd)
 	struct xhci_hcd *xhci = hcd_to_xhci(hcd);
 	struct pci_dev  *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller);
 	void (*doorbell)(struct xhci_hcd *) = NULL;
+	int i;
 
 	hcd->uses_new_polling = 1;
 
@@ -417,6 +428,7 @@ int xhci_run(struct usb_hcd *hcd)
 			return ret;
 		}
 		hcd->irq = pdev->irq;
+		xhci->intr_num = 1;
 	}
 
 #ifdef CONFIG_USB_XHCI_HCD_DEBUGGING
@@ -435,21 +447,6 @@ int xhci_run(struct usb_hcd *hcd)
 	xhci_dbg_ring_ptrs(xhci, xhci->cmd_ring);
 	xhci_dbg_cmd_ptrs(xhci);
 
-	xhci_dbg(xhci, "ERST memory map follows:\n");
-	xhci_dbg_erst(xhci, xhci->erst[0]);
-	xhci_dbg(xhci, "Event ring:\n");
-	xhci_debug_ring(xhci, xhci->event_ring[0]);
-	xhci_dbg_ring_ptrs(xhci, xhci->event_ring[0]);
-	temp_64 = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue);
-	temp_64 &= ~ERST_PTR_MASK;
-	xhci_dbg(xhci, "ERST deq = 64'h%0lx\n", (long unsigned int) temp_64);
-
-	xhci_dbg(xhci, "// Set the interrupt modulation register\n");
-	temp = xhci_readl(xhci, &xhci->ir_set->irq_control);
-	temp &= ~ER_IRQ_INTERVAL_MASK;
-	temp |= (u32) 160;
-	xhci_writel(xhci, temp, &xhci->ir_set->irq_control);
-
 	/* Set the HCD state before we enable the irqs */
 	hcd->state = HC_STATE_RUNNING;
 	temp = xhci_readl(xhci, &xhci->op_regs->command);
@@ -458,12 +455,31 @@ int xhci_run(struct usb_hcd *hcd)
 			temp);
 	xhci_writel(xhci, temp, &xhci->op_regs->command);
 
-	temp = xhci_readl(xhci, &xhci->ir_set->irq_pending);
-	xhci_dbg(xhci, "// Enabling event ring interrupter %p by writing 0x%x to irq_pending\n",
+	for (i = 0; i < xhci->intr_num; i++) {
+		xhci->ir_set = (void *) &xhci->run_regs->ir_set[i];
+		xhci_dbg(xhci, "ERST memory map follows:\n");
+		xhci_dbg_erst(xhci, xhci->erst[i]);
+		xhci_dbg(xhci, "Event ring %d:\n", i);
+		xhci_debug_ring(xhci, xhci->event_ring[i]);
+		xhci_dbg_ring_ptrs(xhci, xhci->event_ring[i]);
+		temp_64 = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue);
+		temp_64 &= ~ERST_PTR_MASK;
+		xhci_dbg(xhci, "ERST deq = 64'h%0lx\n",
+				(long unsigned int) temp_64);
+
+		xhci_dbg(xhci, "// Set the interrupt modulation register\n");
+		temp = xhci_readl(xhci, &xhci->ir_set->irq_control);
+		temp &= ~ER_IRQ_INTERVAL_MASK;
+		temp |= (u32) 160;
+		xhci_writel(xhci, temp, &xhci->ir_set->irq_control);
+
+		temp = xhci_readl(xhci, &xhci->ir_set->irq_pending);
+		xhci_dbg(xhci, "// Enabling event ring interrupter %p by writing 0x%x to irq_pending\n",
 			xhci->ir_set, (unsigned int) ER_IRQ_ENABLE(temp));
-	xhci_writel(xhci, ER_IRQ_ENABLE(temp),
-			&xhci->ir_set->irq_pending);
-	xhci_print_ir_set(xhci, xhci->ir_set, 0);
+		xhci_writel(xhci, ER_IRQ_ENABLE(temp),
+				&xhci->ir_set->irq_pending);
+		xhci_print_ir_set(xhci, xhci->ir_set, i);
+	}
 
 	if (NUM_TEST_NOOPS > 0)
 		doorbell = xhci_setup_one_noop(xhci);
@@ -498,6 +514,7 @@ void xhci_stop(struct usb_hcd *hcd)
 {
 	u32 temp;
 	struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+	int i;
 
 	spin_lock_irq(&xhci->lock);
 	xhci_halt(xhci);
@@ -515,10 +532,14 @@ void xhci_stop(struct usb_hcd *hcd)
 	xhci_dbg(xhci, "// Disabling event ring interrupts\n");
 	temp = xhci_readl(xhci, &xhci->op_regs->status);
 	xhci_writel(xhci, temp & ~STS_EINT, &xhci->op_regs->status);
-	temp = xhci_readl(xhci, &xhci->ir_set->irq_pending);
-	xhci_writel(xhci, ER_IRQ_DISABLE(temp),
-			&xhci->ir_set->irq_pending);
-	xhci_print_ir_set(xhci, xhci->ir_set, 0);
+
+	for (i = 0; i < xhci->intr_num; i++) {
+		xhci->ir_set = (void *) &xhci->run_regs->ir_set[i];
+		temp = xhci_readl(xhci, &xhci->ir_set->irq_pending);
+		xhci_writel(xhci, ER_IRQ_DISABLE(temp),
+				&xhci->ir_set->irq_pending);
+		xhci_print_ir_set(xhci, xhci->ir_set, i);
+	}
 
 	xhci_dbg(xhci, "cleaning up memory\n");
 	xhci_mem_cleanup(xhci);
@@ -550,27 +571,46 @@ void xhci_shutdown(struct usb_hcd *hcd)
 #ifdef CONFIG_PM
 static void xhci_save_registers(struct xhci_hcd *xhci)
 {
+	int i;
+
 	xhci->s3.command = xhci_readl(xhci, &xhci->op_regs->command);
 	xhci->s3.dev_nt = xhci_readl(xhci, &xhci->op_regs->dev_notification);
 	xhci->s3.dcbaa_ptr = xhci_read_64(xhci, &xhci->op_regs->dcbaa_ptr);
 	xhci->s3.config_reg = xhci_readl(xhci, &xhci->op_regs->config_reg);
-	xhci->s3.irq_pending = xhci_readl(xhci, &xhci->ir_set->irq_pending);
-	xhci->s3.irq_control = xhci_readl(xhci, &xhci->ir_set->irq_control);
-	xhci->s3.erst_size = xhci_readl(xhci, &xhci->ir_set->erst_size);
-	xhci->s3.erst_base = xhci_read_64(xhci, &xhci->ir_set->erst_base);
-	xhci->s3.erst_dequeue = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue);
+	for (i = 0; i < xhci->intr_num; i++) {
+		xhci->ir_set = (void *) &xhci->run_regs->ir_set[i];
+		xhci->s3.ir_set[i]->irq_pending =
+			xhci_readl(xhci, &xhci->ir_set->irq_pending);
+		xhci->s3.ir_set[i]->irq_control =
+			xhci_readl(xhci, &xhci->ir_set->irq_control);
+		xhci->s3.ir_set[i]->erst_size =
+			xhci_readl(xhci, &xhci->ir_set->erst_size);
+		xhci->s3.ir_set[i]->erst_base =
+			xhci_read_64(xhci, &xhci->ir_set->erst_base);
+		xhci->s3.ir_set[i]->erst_dequeue =
+			xhci_read_64(xhci, &xhci->ir_set->erst_dequeue);
+	}
 }
 
 static void xhci_restore_registers(struct xhci_hcd *xhci)
 {
+	int i;
+
 	xhci_writel(xhci, xhci->s3.command, &xhci->op_regs->command);
 	xhci_writel(xhci, xhci->s3.dev_nt, &xhci->op_regs->dev_notification);
 	xhci_write_64(xhci, xhci->s3.dcbaa_ptr, &xhci->op_regs->dcbaa_ptr);
 	xhci_writel(xhci, xhci->s3.config_reg, &xhci->op_regs->config_reg);
-	xhci_writel(xhci, xhci->s3.irq_pending, &xhci->ir_set->irq_pending);
-	xhci_writel(xhci, xhci->s3.irq_control, &xhci->ir_set->irq_control);
-	xhci_writel(xhci, xhci->s3.erst_size, &xhci->ir_set->erst_size);
-	xhci_write_64(xhci, xhci->s3.erst_base, &xhci->ir_set->erst_base);
+	for (i = 0; i < xhci->intr_num; i++) {
+		xhci->ir_set = (void *) &xhci->run_regs->ir_set[i];
+		xhci_writel(xhci, xhci->s3.ir_set[i]->irq_pending,
+				&xhci->ir_set->irq_pending);
+		xhci_writel(xhci, xhci->s3.ir_set[i]->irq_control,
+				&xhci->ir_set->irq_control);
+		xhci_writel(xhci, xhci->s3.ir_set[i]->erst_size,
+				&xhci->ir_set->erst_size);
+		xhci_write_64(xhci, xhci->s3.ir_set[i]->erst_base,
+				&xhci->ir_set->erst_base);
+	}
 }
 
 static void xhci_set_cmd_ring_deq(struct xhci_hcd *xhci)
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 4187bf4..a31f833 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -1154,11 +1154,7 @@ struct s3_save {
 	u32	dev_nt;
 	u64	dcbaa_ptr;
 	u32	config_reg;
-	u32	irq_pending;
-	u32	irq_control;
-	u32	erst_size;
-	u64	erst_base;
-	u64	erst_dequeue;
+	struct	xhci_intr_reg *ir_set[32];
 };
 
 /* There is one ehci_hci structure per controller */
@@ -1195,6 +1191,8 @@ struct xhci_hcd {
 	/* msi-x vectors */
 	int		msix_count;
 	struct msix_entry	*msix_entries;
+	/* number of interrupters enabled */
+	int		intr_num;
 	/* data structures */
 	struct xhci_device_context_array *dcbaa;
 	struct xhci_ring	*cmd_ring;
-- 
1.7.0.4



--
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