summaryrefslogtreecommitdiff
path: root/drivers/usb/cdns3
diff options
context:
space:
mode:
authorPeter Chen <peter.chen@nxp.com>2020-03-16 17:47:01 +0800
committerPeter Chen <peter.chen@nxp.com>2020-03-19 15:06:53 +0800
commit2d985587f1f28e21b44a1120028e468de684ebdb (patch)
treef69166e4da597ed32c82e9091750afc37a4473a8 /drivers/usb/cdns3
parenteca571c273bf4a3dc1e46e4a5cef39f9364c69d0 (diff)
MLK-23595-4 usb: cdns3: gadget: calculate short transfer req->actual correctly
For short transfer, every TRB was finished in TD according to DMA pointer, but the Transfer Length for TRBs which were not handled were not updated, so we can't calculte these TRBs. Reviewed-by: Jun Li <jun.li@nxp.com> Signed-off-by: Peter Chen <peter.chen@nxp.com>
Diffstat (limited to 'drivers/usb/cdns3')
-rw-r--r--drivers/usb/cdns3/gadget.c21
-rw-r--r--drivers/usb/cdns3/gadget.h8
2 files changed, 23 insertions, 6 deletions
diff --git a/drivers/usb/cdns3/gadget.c b/drivers/usb/cdns3/gadget.c
index dce0d1c72ef6..86eb9792e767 100644
--- a/drivers/usb/cdns3/gadget.c
+++ b/drivers/usb/cdns3/gadget.c
@@ -927,12 +927,14 @@ finish:
}
static void cdns3_transfer_completed(struct cdns3_device *priv_dev,
- struct cdns3_endpoint *priv_ep)
+ struct cdns3_endpoint *priv_ep,
+ bool is_short)
{
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);
@@ -958,7 +960,14 @@ static void cdns3_transfer_completed(struct cdns3_device *priv_dev,
trb = priv_ep->trb_pool + priv_ep->dequeue;
trace_cdns3_complete_trb(priv_ep, trb);
- request->actual += TRB_LEN(le32_to_cpu(trb->length));
+ if (!transfer_end)
+ request->actual +=
+ TRB_LEN(le32_to_cpu(trb->length));
+
+ if (is_short && priv_req->num_of_trb > 1 &&
+ le32_to_cpu(trb->control) & TRB_SMM)
+ transfer_end = true;
+
cdns3_ep_inc_deq(priv_ep);
}
@@ -1072,20 +1081,22 @@ static int cdns3_check_ep_interrupt_proceed(struct cdns3_endpoint *priv_ep)
*/
if (priv_ep->type == USB_ENDPOINT_XFER_ISOC &&
!priv_ep->wa1_set)
- cdns3_transfer_completed(priv_dev, priv_ep);
+ cdns3_transfer_completed(priv_dev, priv_ep, false);
else
cdns3_rearm_transfer(priv_ep, priv_ep->wa1_set);
}
if ((ep_sts_reg & EP_STS_IOC) || (ep_sts_reg & EP_STS_ISP)) {
+ bool is_short = !!(ep_sts_reg & EP_STS_ISP);
+
if (priv_ep->flags & EP_QUIRK_EXTRA_BUF_EN) {
- if (ep_sts_reg & EP_STS_ISP)
+ if (is_short)
priv_ep->flags |= EP_QUIRK_END_TRANSFER;
else
priv_ep->flags &= ~EP_QUIRK_END_TRANSFER;
}
- cdns3_transfer_completed(priv_dev, priv_ep);
+ cdns3_transfer_completed(priv_dev, priv_ep, is_short);
}
/*
diff --git a/drivers/usb/cdns3/gadget.h b/drivers/usb/cdns3/gadget.h
index 2443bc3d06b5..35a4e830f955 100644
--- a/drivers/usb/cdns3/gadget.h
+++ b/drivers/usb/cdns3/gadget.h
@@ -964,9 +964,15 @@ struct cdns3_trb {
/* Cycle bit - indicates TRB ownership by driver or hw*/
#define TRB_CYCLE BIT(0)
/*
- * When set to '1', the device will toggle its interpretation of the Cycle bit
+ * When set to '1', the device will toggle its interpretation
+ * of the Cycle bit, this bit is for link TRB
*/
#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)
/* Interrupt on short packet*/
#define TRB_ISP BIT(2)