usb: dwc3: gadget: Properly handle ClearFeature(halt)
DWC3 must not issue CLEAR_STALL command to control endpoints. The
controller automatically clears the STALL when it receives the SETUP
token. Also, when the driver receives ClearFeature(halt_ep), DWC3 must
stop any active transfer from the endpoint and give back all the
requests to the function drivers.
Fixes: 72246da40f
("usb: Introduce DesignWare USB3 DRD Driver")
Signed-off-by: Thinh Nguyen <thinhn@synopsys.com>
Signed-off-by: Felipe Balbi <balbi@kernel.org>
This commit is contained in:
Родитель
3428b96f2f
Коммит
cb11ea56f3
|
@ -1508,6 +1508,10 @@ static void dwc3_gadget_ep_skip_trbs(struct dwc3_ep *dep, struct dwc3_request *r
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
/* If req->trb is not set, then the request has not started */
|
||||||
|
if (!req->trb)
|
||||||
|
return;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If request was already started, this means we had to
|
* If request was already started, this means we had to
|
||||||
* stop the transfer. With that we also need to ignore
|
* stop the transfer. With that we also need to ignore
|
||||||
|
@ -1598,6 +1602,8 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol)
|
||||||
{
|
{
|
||||||
struct dwc3_gadget_ep_cmd_params params;
|
struct dwc3_gadget_ep_cmd_params params;
|
||||||
struct dwc3 *dwc = dep->dwc;
|
struct dwc3 *dwc = dep->dwc;
|
||||||
|
struct dwc3_request *req;
|
||||||
|
struct dwc3_request *tmp;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
|
if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
|
||||||
|
@ -1634,13 +1640,37 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol)
|
||||||
else
|
else
|
||||||
dep->flags |= DWC3_EP_STALL;
|
dep->flags |= DWC3_EP_STALL;
|
||||||
} else {
|
} else {
|
||||||
|
/*
|
||||||
|
* Don't issue CLEAR_STALL command to control endpoints. The
|
||||||
|
* controller automatically clears the STALL when it receives
|
||||||
|
* the SETUP token.
|
||||||
|
*/
|
||||||
|
if (dep->number <= 1) {
|
||||||
|
dep->flags &= ~(DWC3_EP_STALL | DWC3_EP_WEDGE);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
ret = dwc3_send_clear_stall_ep_cmd(dep);
|
ret = dwc3_send_clear_stall_ep_cmd(dep);
|
||||||
if (ret)
|
if (ret) {
|
||||||
dev_err(dwc->dev, "failed to clear STALL on %s\n",
|
dev_err(dwc->dev, "failed to clear STALL on %s\n",
|
||||||
dep->name);
|
dep->name);
|
||||||
else
|
return ret;
|
||||||
dep->flags &= ~(DWC3_EP_STALL | DWC3_EP_WEDGE);
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
list_for_each_entry_safe(req, tmp, &dep->pending_list, list)
|
||||||
|
dwc3_gadget_move_cancelled_request(req);
|
||||||
|
|
||||||
|
if (!(dep->flags & DWC3_EP_END_TRANSFER_PENDING)) {
|
||||||
|
dep->flags &= ~DWC3_EP_DELAY_START;
|
||||||
|
dwc3_gadget_ep_cleanup_cancelled_requests(dep);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
|
Загрузка…
Ссылка в новой задаче