summaryrefslogtreecommitdiff
path: root/drivers/usb/cdns3
diff options
context:
space:
mode:
authorPeter Chen <peter.chen@nxp.com>2019-12-13 16:52:06 +0800
committerPeter Chen <peter.chen@nxp.com>2019-12-18 15:12:09 +0800
commit4ad131b72980f834dbecfd0fe6681d96223c9c11 (patch)
treed49704d4fa8c7ce22ebc11b447b94bcd5d09ceb7 /drivers/usb/cdns3
parent2f1bdd071f81983e7366455ecbfedc088e8ce5f2 (diff)
LF-468-1 usb: cdns3: gadget: handle trb correctly at sg case
At current transfer complete handler, it doesn't consider sg (scatter buffer list) case, and only handles single trb request. In fact, we need to handle every trbs in request, and giveback the request after all trbs are handled. Signed-off-by: Peter Chen <peter.chen@nxp.com>
Diffstat (limited to 'drivers/usb/cdns3')
-rw-r--r--drivers/usb/cdns3/gadget.c51
1 files changed, 32 insertions, 19 deletions
diff --git a/drivers/usb/cdns3/gadget.c b/drivers/usb/cdns3/gadget.c
index f34bd7de4fc5..63f52ff12ecc 100644
--- a/drivers/usb/cdns3/gadget.c
+++ b/drivers/usb/cdns3/gadget.c
@@ -826,14 +826,28 @@ static bool cdns3_request_handled(struct cdns3_endpoint *priv_ep,
struct cdns3_request *priv_req)
{
struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
- struct cdns3_trb *trb = priv_req->trb;
+ struct cdns3_trb *trb;
int current_index = 0;
int handled = 0;
current_index = (readl(&priv_dev->regs->ep_traddr) -
priv_ep->trb_pool_dma) / TRB_SIZE;
- 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) &&
+ (priv_ep->dequeue > priv_req->end_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 ((trb->control & TRB_CYCLE) != priv_ep->ccs)
goto finish;
@@ -843,12 +857,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;
}
@@ -864,6 +874,7 @@ static void cdns3_transfer_completed(struct cdns3_device *priv_dev,
struct cdns3_request *priv_req;
struct usb_request *request;
struct cdns3_trb *trb;
+ bool trb_handled = false;
while (!list_empty(&priv_ep->pending_req_list)) {
request = cdns3_next_request(&priv_ep->pending_req_list);
@@ -874,21 +885,23 @@ 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))
- return;
-
- trb = priv_ep->trb_pool + priv_ep->dequeue;
- trace_cdns3_complete_trb(priv_ep, trb);
+ while (cdns3_request_handled(priv_ep, priv_req)) {
+ trb_handled = true;
+ 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);
+ request->actual += TRB_LEN(le32_to_cpu(trb->length));
+ cdns3_ep_inc_deq(priv_ep);
+ }
- 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 (trb_handled) {
+ cdns3_gadget_giveback(priv_ep, priv_req, 0);
+ trb_handled = false;
+ } else {
+ return;
+ }
}
+
priv_ep->flags &= ~EP_PENDING_REQUEST;
}