summaryrefslogtreecommitdiff
path: root/drivers/usb/dwc3/gadget.c
diff options
context:
space:
mode:
authorThinh Nguyen <Thinh.Nguyen@synopsys.com>2020-09-02 18:43:04 -0700
committerFelipe Balbi <balbi@kernel.org>2020-09-24 11:56:01 +0300
commitd97c78a1908e59a1fdbcbece87cd0440b5d7a1f2 (patch)
treeb40473c2b1da41d86769218a671d5487cd92d41f /drivers/usb/dwc3/gadget.c
parentc503672abe1348f10f5a54a662336358c6e1a297 (diff)
downloadlwn-d97c78a1908e59a1fdbcbece87cd0440b5d7a1f2.tar.gz
lwn-d97c78a1908e59a1fdbcbece87cd0440b5d7a1f2.zip
usb: dwc3: gadget: END_TRANSFER before CLEAR_STALL command
According the programming guide (for all DWC3 IPs), when the driver handles ClearFeature(halt) request, it should issue CLEAR_STALL command _after_ the END_TRANSFER command completes. The END_TRANSFER command may take some time to complete. So, delay the ClearFeature(halt) request control status stage and wait for END_TRANSFER command completion interrupt. Only after END_TRANSFER command completes that the driver may issue CLEAR_STALL command. Cc: stable@vger.kernel.org Fixes: cb11ea56f37a ("usb: dwc3: gadget: Properly handle ClearFeature(halt)") Signed-off-by: Thinh Nguyen <thinhn@synopsys.com> Signed-off-by: Felipe Balbi <balbi@kernel.org>
Diffstat (limited to 'drivers/usb/dwc3/gadget.c')
-rw-r--r--drivers/usb/dwc3/gadget.c40
1 files changed, 32 insertions, 8 deletions
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index c04f7b29535e..f9843ad93577 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -1827,6 +1827,18 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol)
return 0;
}
+ dwc3_stop_active_transfer(dep, true, true);
+
+ list_for_each_entry_safe(req, tmp, &dep->started_list, list)
+ dwc3_gadget_move_cancelled_request(req);
+
+ if (dep->flags & DWC3_EP_END_TRANSFER_PENDING) {
+ dep->flags |= DWC3_EP_PENDING_CLEAR_STALL;
+ return 0;
+ }
+
+ dwc3_gadget_ep_cleanup_cancelled_requests(dep);
+
ret = dwc3_send_clear_stall_ep_cmd(dep);
if (ret) {
dev_err(dwc->dev, "failed to clear STALL on %s\n",
@@ -1836,14 +1848,6 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol)
dep->flags &= ~(DWC3_EP_STALL | DWC3_EP_WEDGE);
- dwc3_stop_active_transfer(dep, true, true);
-
- list_for_each_entry_safe(req, tmp, &dep->started_list, list)
- dwc3_gadget_move_cancelled_request(req);
-
- if (!(dep->flags & DWC3_EP_END_TRANSFER_PENDING))
- dwc3_gadget_ep_cleanup_cancelled_requests(dep);
-
if ((dep->flags & DWC3_EP_DELAY_START) &&
!usb_endpoint_xfer_isoc(dep->endpoint.desc))
__dwc3_gadget_kick_transfer(dep);
@@ -3003,6 +3007,26 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
dep->flags &= ~DWC3_EP_END_TRANSFER_PENDING;
dep->flags &= ~DWC3_EP_TRANSFER_STARTED;
dwc3_gadget_ep_cleanup_cancelled_requests(dep);
+
+ if (dep->flags & DWC3_EP_PENDING_CLEAR_STALL) {
+ struct dwc3 *dwc = dep->dwc;
+
+ dep->flags &= ~DWC3_EP_PENDING_CLEAR_STALL;
+ if (dwc3_send_clear_stall_ep_cmd(dep)) {
+ struct usb_ep *ep0 = &dwc->eps[0]->endpoint;
+
+ dev_err(dwc->dev, "failed to clear STALL on %s\n",
+ dep->name);
+ if (dwc->delayed_status)
+ __dwc3_gadget_ep0_set_halt(ep0, 1);
+ return;
+ }
+
+ dep->flags &= ~(DWC3_EP_STALL | DWC3_EP_WEDGE);
+ if (dwc->delayed_status)
+ dwc3_ep0_send_delayed_status(dwc);
+ }
+
if ((dep->flags & DWC3_EP_DELAY_START) &&
!usb_endpoint_xfer_isoc(dep->endpoint.desc))
__dwc3_gadget_kick_transfer(dep);