summaryrefslogtreecommitdiff
path: root/drivers/usb/dwc3/ep0.c
diff options
context:
space:
mode:
authorThinh Nguyen <Thinh.Nguyen@synopsys.com>2020-09-03 04:43:04 +0300
committerFelipe Balbi <balbi@kernel.org>2020-09-24 11:56:01 +0300
commitd97c78a1908e59a1fdbcbece87cd0440b5d7a1f2 (patch)
treeb40473c2b1da41d86769218a671d5487cd92d41f /drivers/usb/dwc3/ep0.c
parentc503672abe1348f10f5a54a662336358c6e1a297 (diff)
downloadlinux-d97c78a1908e59a1fdbcbece87cd0440b5d7a1f2.tar.xz
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/ep0.c')
-rw-r--r--drivers/usb/dwc3/ep0.c16
1 files changed, 16 insertions, 0 deletions
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index 59f2e8c31bd1..92bc1044e7ab 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -524,6 +524,11 @@ static int dwc3_ep0_handle_endpoint(struct dwc3 *dwc,
ret = __dwc3_gadget_ep_set_halt(dep, set, true);
if (ret)
return -EINVAL;
+
+ /* ClearFeature(Halt) may need delayed status */
+ if (!set && (dep->flags & DWC3_EP_END_TRANSFER_PENDING))
+ return USB_GADGET_DELAYED_STATUS;
+
break;
default:
return -EINVAL;
@@ -1042,6 +1047,17 @@ static void dwc3_ep0_do_control_status(struct dwc3 *dwc,
__dwc3_ep0_do_control_status(dwc, dep);
}
+void dwc3_ep0_send_delayed_status(struct dwc3 *dwc)
+{
+ unsigned int direction = !dwc->ep0_expect_in;
+
+ if (dwc->ep0state != EP0_STATUS_PHASE)
+ return;
+
+ dwc->delayed_status = false;
+ __dwc3_ep0_do_control_status(dwc, dwc->eps[direction]);
+}
+
static void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep)
{
struct dwc3_gadget_ep_cmd_params params;