mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-11-24 20:30:53 +07:00
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: cb11ea56f3
("usb: dwc3: gadget: Properly handle ClearFeature(halt)")
Signed-off-by: Thinh Nguyen <thinhn@synopsys.com>
Signed-off-by: Felipe Balbi <balbi@kernel.org>
This commit is contained in:
parent
c503672abe
commit
d97c78a190
@ -706,6 +706,7 @@ struct dwc3_ep {
|
||||
#define DWC3_EP_IGNORE_NEXT_NOSTREAM BIT(8)
|
||||
#define DWC3_EP_FORCE_RESTART_STREAM BIT(9)
|
||||
#define DWC3_EP_FIRST_STREAM_PRIMED BIT(10)
|
||||
#define DWC3_EP_PENDING_CLEAR_STALL BIT(11)
|
||||
|
||||
/* This last one is specific to EP0 */
|
||||
#define DWC3_EP0_DIR_IN BIT(31)
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -113,6 +113,7 @@ int dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value);
|
||||
int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
|
||||
gfp_t gfp_flags);
|
||||
int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol);
|
||||
void dwc3_ep0_send_delayed_status(struct dwc3 *dwc);
|
||||
|
||||
/**
|
||||
* dwc3_gadget_ep_get_transfer_index - Gets transfer index from HW
|
||||
|
Loading…
Reference in New Issue
Block a user