[PATCH] dwc3: Remove wait for wait_end_transfer event.

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

 



From: Pierre Le Magourou <plemagourou@xxxxxxxxxxxxxxxxxxxx>

We can't wait for END_OF_TRANSFER event in dwc3_gadget_ep_dequeue()
because it can be called in an interruption context.

TRBs are now cleared after the END_OF_TRANSFER completion, in the
interrupt handler.

Signed-off-by: Pierre Le Magourou <plemagourou@xxxxxxxxxxxxxxxxxxxx>
---
 drivers/usb/dwc3/core.h   |  14 ++++++
 drivers/usb/dwc3/gadget.c | 122 +++++++++++++++++++++++++++-------------------
 2 files changed, 85 insertions(+), 51 deletions(-)

diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 7a217a36c36b..5e2070183e3a 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -627,6 +627,7 @@ struct dwc3_event_buffer {
  * @wait_end_transfer: wait_queue_head_t for waiting on End Transfer complete
  * @lock: spinlock for endpoint request queue traversal
  * @regs: pointer to first endpoint register
+ * @pending_trb: pointer to pending TRB structure
  * @trb_pool: array of transaction buffers
  * @trb_pool_dma: dma address of @trb_pool
  * @trb_enqueue: enqueue 'pointer' into TRB array
@@ -654,6 +655,7 @@ struct dwc3_ep {
 	void __iomem		*regs;
 
 	struct dwc3_trb		*trb_pool;
+	struct dwc3_pending_trb	*pending_trb;
 	dma_addr_t		trb_pool_dma;
 	struct dwc3		*dwc;
 
@@ -777,6 +779,18 @@ struct dwc3_trb {
 	u32		ctrl;
 } __packed;
 
+/**
+ * struct dwc3_pending_trb
+ * @dirty_trb: pointer to TRB waiting for END_TRANSFER completion
+ * @extra_trb: true if extra TRB was set to align transfer
+ * @num_pending_sgs: number of pending SGs
+ */
+struct dwc3_pending_trb {
+	struct dwc3_trb *dirty_trb;
+	bool extra_trb;
+	unsigned int num_pending_sgs;
+};
+
 /**
  * struct dwc3_hwparams - copy of HWPARAMS registers
  * @hwparams0: GHWPARAMS0
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 69bf137aab37..dd1f2b74723b 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -1341,6 +1341,7 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
 
 	struct dwc3_ep			*dep = to_dwc3_ep(ep);
 	struct dwc3			*dwc = dep->dwc;
+	struct dwc3_pending_trb *p_trb;
 
 	unsigned long			flags;
 	int				ret = 0;
@@ -1367,63 +1368,29 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
 			 * If request was already started, this means we had to
 			 * stop the transfer. With that we also need to ignore
 			 * all TRBs used by the request, however TRBs can only
-			 * be modified after completion of END_TRANSFER
-			 * command. So what we do here is that we wait for
-			 * END_TRANSFER completion and only after that, we jump
-			 * over TRBs by clearing HWO and incrementing dequeue
-			 * pointer.
+			 * be modified after completion of END_TRANSFER command.
 			 *
-			 * Note that we have 2 possible types of transfers here:
-			 *
-			 * i) Linear buffer request
-			 * ii) SG-list based request
-			 *
-			 * SG-list based requests will have r->num_pending_sgs
-			 * set to a valid number (> 0). Linear requests,
-			 * normally use a single TRB.
-			 *
-			 * For each of these two cases, if r->unaligned flag is
-			 * set, one extra TRB has been used to align transfer
-			 * size to wMaxPacketSize.
-			 *
-			 * All of these cases need to be taken into
-			 * consideration so we don't mess up our TRB ring
-			 * pointers.
+			 * Don't wait for END_TRANSFER completion here because
+			 * dwc3_gadget_ep_dequeue() can be called from within
+			 * the controller's interrupt handler. Save the TRB
+			 * pointer, the number of pending sgs, and the
+			 * extra_trb flag, so that TRBs HWO can be cleared and
+			 * dequeue pointer incremented when the END_TRANSFER
+			 * completion is received.
 			 */
-			wait_event_lock_irq(dep->wait_end_transfer,
-					!(dep->flags & DWC3_EP_END_TRANSFER_PENDING),
-					dwc->lock);
 
 			if (!r->trb)
 				goto out0;
 
-			if (r->num_pending_sgs) {
-				struct dwc3_trb *trb;
-				int i = 0;
-
-				for (i = 0; i < r->num_pending_sgs; i++) {
-					trb = r->trb + i;
-					trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
-					dwc3_ep_inc_deq(dep);
-				}
-
-				if (r->unaligned || r->zero) {
-					trb = r->trb + r->num_pending_sgs + 1;
-					trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
-					dwc3_ep_inc_deq(dep);
-				}
-			} else {
-				struct dwc3_trb *trb = r->trb;
-
-				trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
-				dwc3_ep_inc_deq(dep);
-
-				if (r->unaligned || r->zero) {
-					trb = r->trb + 1;
-					trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
-					dwc3_ep_inc_deq(dep);
-				}
-			}
+			p_trb = kzalloc(sizeof(*p_trb), GFP_KERNEL|GFP_ATOMIC);
+			p_trb->dirty_trb = r->trb;
+			p_trb->num_pending_sgs = r->num_pending_sgs;
+
+			if (r->unaligned || r->zero)
+				p_trb->extra_trb = true;
+
+			dep->pending_trb = p_trb;
+
 			goto out1;
 		}
 		dev_err(dwc->dev, "request %pK was not queued to %s\n",
@@ -2430,6 +2397,56 @@ static void dwc3_gadget_endpoint_transfer_not_ready(struct dwc3_ep *dep,
 	__dwc3_gadget_start_isoc(dep);
 }
 
+/* Clear HWO and increment dequeue pointer.
+ *
+ * 2 types of transfers are possible:
+ *
+ * i) Linear buffer request
+ * ii) SG-list based request
+ *
+ * SG-list based requests have p_trb->num_pending_sgs set to a valid number
+ * (> 0). Linear requests, normally use a single TRB.
+ *
+ * For each of these two cases, if p_trb->extra_trb flag is set, one
+ * extra TRB has been used to align transfer size to wMaxPacketSize.
+ *
+ * All of these cases need to be taken into consideration so TRB ring pointers
+ * are not messed up.
+ */
+static void dwc3_clear_pending_trbs(struct dwc3_ep *dep)
+{
+	struct dwc3_pending_trb *p_trb = dep->pending_trb;
+	struct dwc3_trb *trb;
+
+	if (p_trb->num_pending_sgs) {
+		int i;
+
+		for (i = 0; i < p_trb->num_pending_sgs; i++) {
+			trb = p_trb->dirty_trb + i;
+			trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
+			dwc3_ep_inc_deq(dep);
+		}
+
+		if (p_trb->extra_trb) {
+			trb = p_trb->dirty_trb + p_trb->num_pending_sgs + 1;
+			trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
+			dwc3_ep_inc_deq(dep);
+		}
+	} else {
+		trb = p_trb->dirty_trb;
+		trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
+		dwc3_ep_inc_deq(dep);
+
+		if (p_trb->extra_trb) {
+			trb = p_trb->dirty_trb + 1;
+			trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
+			dwc3_ep_inc_deq(dep);
+		}
+	}
+	kfree(p_trb);
+	dep->pending_trb = NULL;
+}
+
 static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
 		const struct dwc3_event_depevt *event)
 {
@@ -2466,6 +2483,9 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
 		if (cmd == DWC3_DEPCMD_ENDTRANSFER) {
 			dep->flags &= ~DWC3_EP_END_TRANSFER_PENDING;
 			wake_up(&dep->wait_end_transfer);
+
+			if (dep->pending_trb)
+				dwc3_clear_pending_trbs(dep);
 		}
 		break;
 	case DWC3_DEPEVT_STREAMEVT:
-- 
2.16.2

--
To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [Linux for Sparc]     [IETF Annouce]     [Security]     [Bugtraq]     [Linux MIPS]     [ECOS]     [Asterisk Internet PBX]     [Linux API]

  Powered by Linux