diff mbox series

[5/8] usb: cdns3: gadget: handle sg list use case at completion correctly

Message ID 20200901084454.28649-6-peter.chen@nxp.com (mailing list archive)
State Superseded
Headers show
Series usb: cdns3: improve the sg use case | expand

Commit Message

Peter Chen Sept. 1, 2020, 8:44 a.m. UTC
- Judge each TRB has been handled at cdns3_trb_handled, since
the DMA pointer may be at the middle of the TD, we can't consider
this TD has finished at that time.
- Calcuate req->actual according to finished TRBs.
- Handle short transfer for sg list use case correctly. When the
short transfer occurs, we check OUT_SMM at TRB to see if it is
the last TRB.

Signed-off-by: Peter Chen <peter.chen@nxp.com>
---
 drivers/usb/cdns3/gadget.c | 85 +++++++++++++++++++++++++-------------
 drivers/usb/cdns3/gadget.h |  9 ++++
 2 files changed, 65 insertions(+), 29 deletions(-)

Comments

Felipe Balbi Sept. 8, 2020, 6:25 a.m. UTC | #1
Peter Chen <peter.chen@nxp.com> writes:

> - Judge each TRB has been handled at cdns3_trb_handled, since
> the DMA pointer may be at the middle of the TD, we can't consider
> this TD has finished at that time.
> - Calcuate req->actual according to finished TRBs.
    ^^^^^^^^
    calculate

> - Handle short transfer for sg list use case correctly. When the
> short transfer occurs, we check OUT_SMM at TRB to see if it is
> the last TRB.

it looks like each of these should be split to its own patch.

> Signed-off-by: Peter Chen <peter.chen@nxp.com>
> ---
>  drivers/usb/cdns3/gadget.c | 85 +++++++++++++++++++++++++-------------
>  drivers/usb/cdns3/gadget.h |  9 ++++
>  2 files changed, 65 insertions(+), 29 deletions(-)
>
> diff --git a/drivers/usb/cdns3/gadget.c b/drivers/usb/cdns3/gadget.c
> index a308a694abc5..6cb44c354f40 100644
> --- a/drivers/usb/cdns3/gadget.c
> +++ b/drivers/usb/cdns3/gadget.c
> @@ -817,6 +817,8 @@ void cdns3_gadget_giveback(struct cdns3_endpoint *priv_ep,
>  		       request->length);
>  
>  	priv_req->flags &= ~(REQUEST_PENDING | REQUEST_UNALIGNED);
> +	/* All TRBs have finished, clear the counter */
> +	priv_req->finished_trb = 0;
>  	trace_cdns3_gadget_giveback(priv_req);
>  
>  	if (priv_dev->dev_ver < DEV_VER_V2) {
> @@ -1241,6 +1243,7 @@ static int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep,
>  	trb = priv_req->trb;
>  
>  	priv_req->flags |= REQUEST_PENDING;
> +	priv_req->num_of_trb = num_trb;
>  
>  	if (sg_iter == 1)
>  		trb->control |= cpu_to_le32(TRB_IOC | TRB_ISP);
> @@ -1362,7 +1365,7 @@ void cdns3_set_hw_configuration(struct cdns3_device *priv_dev)
>  }
>  
>  /**
> - * cdns3_request_handled - check whether request has been handled by DMA
> + * cdns3_trb_handled - check whether trb has been handled by DMA
>   *
>   * @priv_ep: extended endpoint object.
>   * @priv_req: request object for checking
> @@ -1379,32 +1382,28 @@ void cdns3_set_hw_configuration(struct cdns3_device *priv_dev)
>   * ET = priv_req->end_trb - index of last TRB in transfer ring
>   * CI = current_index - index of processed TRB by DMA.
>   *
> - * As first step, function checks if cycle bit for priv_req->start_trb is
> - * correct.
> + * As first step, we check if the TRB between the ST and ET.
> + * Then, we check if cycle bit for index priv_ep->dequeue
> + * is correct.
>   *
>   * some rules:
> - * 1. priv_ep->dequeue never exceed current_index.
> + * 1. priv_ep->dequeue never equals to current_index.
>   * 2  priv_ep->enqueue never exceed priv_ep->dequeue
>   * 3. exception: priv_ep->enqueue == priv_ep->dequeue
>   *    and priv_ep->free_trbs is zero.
>   *    This case indicate that TR is full.
>   *
> - * Then We can split recognition into two parts:
> + * At below two cases, the request have been handled.
>   * Case 1 - priv_ep->dequeue < current_index
>   *      SR ... EQ ... DQ ... CI ... ER
>   *      SR ... DQ ... CI ... EQ ... ER
>   *
> - *      Request has been handled by DMA if ST and ET is between DQ and CI.
> - *
>   * Case 2 - priv_ep->dequeue > current_index
> - * This situation take place when CI go through the LINK TRB at the end of
> + * This situation takes place when CI go through the LINK TRB at the end of

not part of $subject
Peter Chen Sept. 8, 2020, 8:34 a.m. UTC | #2
On 20-09-08 09:25:41, Felipe Balbi wrote:
> Peter Chen <peter.chen@nxp.com> writes:
> 
> > - Judge each TRB has been handled at cdns3_trb_handled, since
> > the DMA pointer may be at the middle of the TD, we can't consider
> > this TD has finished at that time.
> > - Calcuate req->actual according to finished TRBs.
>     ^^^^^^^^
>     calculate
> 
> > - Handle short transfer for sg list use case correctly. When the
> > short transfer occurs, we check OUT_SMM at TRB to see if it is
> > the last TRB.
> 
> it looks like each of these should be split to its own patch.

When I debug sg use case, it indeed took several patches for all
functions work, and some patches improved the old patches since
some short transfers use case did not be considered well.

Using this patch, it could let the completion work for both normal
transfer and short transfer. So I prefer keeping one patch.

> > + * Then, we check if cycle bit for index priv_ep->dequeue
> > + * is correct.
> >   *
> >   * some rules:
> > - * 1. priv_ep->dequeue never exceed current_index.
> > + * 1. priv_ep->dequeue never equals to current_index.
> >   * 2  priv_ep->enqueue never exceed priv_ep->dequeue
> >   * 3. exception: priv_ep->enqueue == priv_ep->dequeue
> >   *    and priv_ep->free_trbs is zero.
> >   *    This case indicate that TR is full.
> >   *
> > - * Then We can split recognition into two parts:
> > + * At below two cases, the request have been handled.
> >   * Case 1 - priv_ep->dequeue < current_index
> >   *      SR ... EQ ... DQ ... CI ... ER
> >   *      SR ... DQ ... CI ... EQ ... ER
> >   *
> > - *      Request has been handled by DMA if ST and ET is between DQ and CI.
> > - *
> >   * Case 2 - priv_ep->dequeue > current_index
> > - * This situation take place when CI go through the LINK TRB at the end of
> > + * This situation takes place when CI go through the LINK TRB at the end of
> 
> not part of $subject
> 

I will make another patch for comment improvement, thanks.
Peter Chen Sept. 10, 2020, 8:37 a.m. UTC | #3
> 
> When I debug sg use case, it indeed took several patches for all functions work,
> and some patches improved the old patches since some short transfers use
> case did not be considered well.
> 
> Using this patch, it could let the completion work for both normal transfer and
> short transfer. So I prefer keeping one patch.
> 
> > > + * Then, we check if cycle bit for index priv_ep->dequeue
> > > + * is correct.
> > >   *
> > >   * some rules:
> > > - * 1. priv_ep->dequeue never exceed current_index.
> > > + * 1. priv_ep->dequeue never equals to current_index.
> > >   * 2  priv_ep->enqueue never exceed priv_ep->dequeue
> > >   * 3. exception: priv_ep->enqueue == priv_ep->dequeue
> > >   *    and priv_ep->free_trbs is zero.
> > >   *    This case indicate that TR is full.
> > >   *
> > > - * Then We can split recognition into two parts:
> > > + * At below two cases, the request have been handled.
> > >   * Case 1 - priv_ep->dequeue < current_index
> > >   *      SR ... EQ ... DQ ... CI ... ER
> > >   *      SR ... DQ ... CI ... EQ ... ER
> > >   *
> > > - *      Request has been handled by DMA if ST and ET is between DQ
> and CI.
> > > - *
> > >   * Case 2 - priv_ep->dequeue > current_index
> > > - * This situation take place when CI go through the LINK TRB at the
> > > end of
> > > + * This situation takes place when CI go through the LINK TRB at
> > > + the end of
> >
> > not part of $subject
> >
> 
> I will make another patch for comment improvement, thanks.
> 
 
I find I change the function from handle request to handle TRB, so the comments for this function
needs to be updated accordingly, it needs to be at the same patch.

Peter
diff mbox series

Patch

diff --git a/drivers/usb/cdns3/gadget.c b/drivers/usb/cdns3/gadget.c
index a308a694abc5..6cb44c354f40 100644
--- a/drivers/usb/cdns3/gadget.c
+++ b/drivers/usb/cdns3/gadget.c
@@ -817,6 +817,8 @@  void cdns3_gadget_giveback(struct cdns3_endpoint *priv_ep,
 		       request->length);
 
 	priv_req->flags &= ~(REQUEST_PENDING | REQUEST_UNALIGNED);
+	/* All TRBs have finished, clear the counter */
+	priv_req->finished_trb = 0;
 	trace_cdns3_gadget_giveback(priv_req);
 
 	if (priv_dev->dev_ver < DEV_VER_V2) {
@@ -1241,6 +1243,7 @@  static int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep,
 	trb = priv_req->trb;
 
 	priv_req->flags |= REQUEST_PENDING;
+	priv_req->num_of_trb = num_trb;
 
 	if (sg_iter == 1)
 		trb->control |= cpu_to_le32(TRB_IOC | TRB_ISP);
@@ -1362,7 +1365,7 @@  void cdns3_set_hw_configuration(struct cdns3_device *priv_dev)
 }
 
 /**
- * cdns3_request_handled - check whether request has been handled by DMA
+ * cdns3_trb_handled - check whether trb has been handled by DMA
  *
  * @priv_ep: extended endpoint object.
  * @priv_req: request object for checking
@@ -1379,32 +1382,28 @@  void cdns3_set_hw_configuration(struct cdns3_device *priv_dev)
  * ET = priv_req->end_trb - index of last TRB in transfer ring
  * CI = current_index - index of processed TRB by DMA.
  *
- * As first step, function checks if cycle bit for priv_req->start_trb is
- * correct.
+ * As first step, we check if the TRB between the ST and ET.
+ * Then, we check if cycle bit for index priv_ep->dequeue
+ * is correct.
  *
  * some rules:
- * 1. priv_ep->dequeue never exceed current_index.
+ * 1. priv_ep->dequeue never equals to current_index.
  * 2  priv_ep->enqueue never exceed priv_ep->dequeue
  * 3. exception: priv_ep->enqueue == priv_ep->dequeue
  *    and priv_ep->free_trbs is zero.
  *    This case indicate that TR is full.
  *
- * Then We can split recognition into two parts:
+ * At below two cases, the request have been handled.
  * Case 1 - priv_ep->dequeue < current_index
  *      SR ... EQ ... DQ ... CI ... ER
  *      SR ... DQ ... CI ... EQ ... ER
  *
- *      Request has been handled by DMA if ST and ET is between DQ and CI.
- *
  * Case 2 - priv_ep->dequeue > current_index
- * This situation take place when CI go through the LINK TRB at the end of
+ * This situation takes place when CI go through the LINK TRB at the end of
  * transfer ring.
  *      SR ... CI ... EQ ... DQ ... ER
- *
- *      Request has been handled by DMA if ET is less then CI or
- *      ET is greater or equal DQ.
  */
-static bool cdns3_request_handled(struct cdns3_endpoint *priv_ep,
+static bool cdns3_trb_handled(struct cdns3_endpoint *priv_ep,
 				  struct cdns3_request *priv_req)
 {
 	struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
@@ -1416,7 +1415,25 @@  static bool cdns3_request_handled(struct cdns3_endpoint *priv_ep,
 	current_index = cdns3_get_dma_pos(priv_dev, priv_ep);
 	doorbell = !!(readl(&priv_dev->regs->ep_cmd) & EP_CMD_DRDY);
 
-	trb = &priv_ep->trb_pool[priv_req->start_trb];
+	/* current trb doesn't belong to this request */
+	if (priv_req->start_trb < priv_req->end_trb) {
+		if (priv_ep->dequeue > priv_req->end_trb)
+			goto finish;
+
+		if (priv_ep->dequeue < priv_req->start_trb)
+			goto finish;
+	}
+
+	if ((priv_req->start_trb > priv_req->end_trb) &&
+		(priv_ep->dequeue > priv_req->end_trb) &&
+		(priv_ep->dequeue < priv_req->start_trb))
+		goto finish;
+
+	if ((priv_req->start_trb == priv_req->end_trb) &&
+		(priv_ep->dequeue != priv_req->end_trb))
+		goto finish;
+
+	trb = &priv_ep->trb_pool[priv_ep->dequeue];
 
 	if ((le32_to_cpu(trb->control) & TRB_CYCLE) != priv_ep->ccs)
 		goto finish;
@@ -1438,12 +1455,8 @@  static bool cdns3_request_handled(struct cdns3_endpoint *priv_ep,
 		    !priv_ep->dequeue)
 			goto finish;
 
-		if (priv_req->end_trb >= priv_ep->dequeue &&
-		    priv_req->end_trb < current_index)
-			handled = 1;
+		handled = 1;
 	} else if (priv_ep->dequeue  > current_index) {
-		if (priv_req->end_trb  < current_index ||
-		    priv_req->end_trb >= priv_ep->dequeue)
 			handled = 1;
 	}
 
@@ -1459,6 +1472,8 @@  static void cdns3_transfer_completed(struct cdns3_device *priv_dev,
 	struct cdns3_request *priv_req;
 	struct usb_request *request;
 	struct cdns3_trb *trb;
+	bool request_handled = false;
+	bool transfer_end = false;
 
 	while (!list_empty(&priv_ep->pending_req_list)) {
 		request = cdns3_next_request(&priv_ep->pending_req_list);
@@ -1478,20 +1493,32 @@  static void cdns3_transfer_completed(struct cdns3_device *priv_dev,
 			 */
 			cdns3_select_ep(priv_dev, priv_ep->endpoint.address);
 
-			if (!cdns3_request_handled(priv_ep, priv_req))
-				goto prepare_next_td;
+			while (cdns3_trb_handled(priv_ep, priv_req)) {
+				priv_req->finished_trb++;
+				if (priv_req->finished_trb >= priv_req->num_of_trb)
+					request_handled = true;
 
-			trb = priv_ep->trb_pool + priv_ep->dequeue;
-			trace_cdns3_complete_trb(priv_ep, trb);
+				trb = priv_ep->trb_pool + priv_ep->dequeue;
+				trace_cdns3_complete_trb(priv_ep, trb);
 
-			if (trb != priv_req->trb)
-				dev_warn(priv_dev->dev,
-					 "request_trb=0x%p, queue_trb=0x%p\n",
-					 priv_req->trb, trb);
+				if (!transfer_end)
+					request->actual +=
+						TRB_LEN(le32_to_cpu(trb->length));
 
-			request->actual = TRB_LEN(le32_to_cpu(trb->length));
-			cdns3_move_deq_to_next_trb(priv_req);
-			cdns3_gadget_giveback(priv_ep, priv_req, 0);
+				if (priv_req->num_of_trb > 1 &&
+					le32_to_cpu(trb->control) & TRB_SMM)
+					transfer_end = true;
+
+				cdns3_ep_inc_deq(priv_ep);
+			}
+
+			if (request_handled) {
+				cdns3_gadget_giveback(priv_ep, priv_req, 0);
+				request_handled = false;
+				transfer_end = false;
+			} else {
+				goto prepare_next_td;
+			}
 
 			if (priv_ep->type != USB_ENDPOINT_XFER_ISOC &&
 			    TRBS_PER_SEGMENT == 2)
diff --git a/drivers/usb/cdns3/gadget.h b/drivers/usb/cdns3/gadget.h
index 52765b098b9e..9f8bd452847e 100644
--- a/drivers/usb/cdns3/gadget.h
+++ b/drivers/usb/cdns3/gadget.h
@@ -1030,6 +1030,11 @@  struct cdns3_trb {
  * When set to '1', the device will toggle its interpretation of the Cycle bit
  */
 #define TRB_TOGGLE		BIT(1)
+/*
+ * The controller will set it if OUTSMM (OUT size mismatch) is detected,
+ * this bit is for normal TRB
+ */
+#define TRB_SMM			BIT(1)
 
 /*
  * Short Packet (SP). OUT EPs at DMULT=1 only. Indicates if the TRB was
@@ -1215,6 +1220,8 @@  struct cdns3_aligned_buf {
  *               this endpoint
  * @flags: flag specifying special usage of request
  * @list: used by internally allocated request to add to wa2_descmiss_req_list.
+ * @finished_trb: number of trb has already finished per request
+ * @num_of_trb: how many trbs in this request
  */
 struct cdns3_request {
 	struct usb_request		request;
@@ -1230,6 +1237,8 @@  struct cdns3_request {
 #define REQUEST_UNALIGNED		BIT(4)
 	u32				flags;
 	struct list_head		list;
+	int				finished_trb;
+	int				num_of_trb;
 };
 
 #define to_cdns3_request(r) (container_of(r, struct cdns3_request, request))