| From 4bf2dd65135a2d7fe202f7c10d65b51bcf645ac6 Mon Sep 17 00:00:00 2001 |
| From: Peter Chen <peter.chen@nxp.com> |
| Date: Wed, 19 Feb 2020 22:14:55 +0800 |
| Subject: usb: cdns3: gadget: toggle cycle bit before reset endpoint |
| |
| From: Peter Chen <peter.chen@nxp.com> |
| |
| commit 4bf2dd65135a2d7fe202f7c10d65b51bcf645ac6 upstream. |
| |
| If there are TRBs pending during reset endpoint operation, the |
| DMA will advance after reset operation, but it isn't expected, |
| since the data is not yet available (For OUT, the data is not |
| yet available). After the data is ready, there won't be any |
| interrupt since the EP_TRADDR already points to next TRB entry |
| and doorbell is not set. |
| |
| To fix it, it toggles cycle bit before reset operation, and restores |
| it after reset, it could avoid unexpected DMA advance due to |
| cycle bit is for software during the endpoint reset operation. |
| |
| Fixes: 7733f6c32e36 ("usb: cdns3: Add Cadence USB3 DRD Driver") |
| Signed-off-by: Peter Chen <peter.chen@nxp.com> |
| Cc: stable <stable@vger.kernel.org> |
| Link: https://lore.kernel.org/r/20200219141455.23257-3-peter.chen@nxp.com |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| drivers/usb/cdns3/gadget.c | 17 ++++++++++++++--- |
| 1 file changed, 14 insertions(+), 3 deletions(-) |
| |
| --- a/drivers/usb/cdns3/gadget.c |
| +++ b/drivers/usb/cdns3/gadget.c |
| @@ -2152,11 +2152,21 @@ int __cdns3_gadget_ep_clear_halt(struct |
| { |
| struct cdns3_device *priv_dev = priv_ep->cdns3_dev; |
| struct usb_request *request; |
| + struct cdns3_request *priv_req; |
| + struct cdns3_trb *trb = NULL; |
| int ret; |
| int val; |
| |
| trace_cdns3_halt(priv_ep, 0, 0); |
| |
| + request = cdns3_next_request(&priv_ep->pending_req_list); |
| + if (request) { |
| + priv_req = to_cdns3_request(request); |
| + trb = priv_req->trb; |
| + if (trb) |
| + trb->control = trb->control ^ TRB_CYCLE; |
| + } |
| + |
| writel(EP_CMD_CSTALL | EP_CMD_EPRST, &priv_dev->regs->ep_cmd); |
| |
| /* wait for EPRST cleared */ |
| @@ -2167,10 +2177,11 @@ int __cdns3_gadget_ep_clear_halt(struct |
| |
| priv_ep->flags &= ~(EP_STALLED | EP_STALL_PENDING); |
| |
| - request = cdns3_next_request(&priv_ep->pending_req_list); |
| - |
| - if (request) |
| + if (request) { |
| + if (trb) |
| + trb->control = trb->control ^ TRB_CYCLE; |
| cdns3_rearm_transfer(priv_ep, 1); |
| + } |
| |
| cdns3_start_all_request(priv_dev, priv_ep); |
| return ret; |