Skip to content

Commit

Permalink
usb: dwc3: Fix ep0 handling when getting reset while doing control tr…
Browse files Browse the repository at this point in the history
…ansfer

According to the databook ep0 should be in setup phase during reset.
If host issues reset between control transfers, ep0 will be  in an
invalid state. Fix this by issuing stall and restart on ep0 if it
is not in setup phase.

Also SW needs to complete pending control transfer and setup core for
next setup stage as per data book. Hence check ep0 state during reset
interrupt handling and make sure active transfers on ep0 out/in
endpoint are stopped by queuing ENDXFER command for that endpoint and
restart ep0 out again to receive next setup packet.

Signed-off-by: Mayank Rana <quic_mrana@quicinc.com>
Link: https://lore.kernel.org/r/1651693001-29891-1-git-send-email-quic_mrana@quicinc.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
  • Loading branch information
Mayank Rana authored and Greg Kroah-Hartman committed May 19, 2022
1 parent 69a1c9a commit 9d778f0
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 5 deletions.
11 changes: 8 additions & 3 deletions drivers/usb/dwc3/ep0.c
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
return ret;
}

static void dwc3_ep0_stall_and_restart(struct dwc3 *dwc)
void dwc3_ep0_stall_and_restart(struct dwc3 *dwc)
{
struct dwc3_ep *dep;

Expand Down Expand Up @@ -1088,13 +1088,18 @@ void dwc3_ep0_send_delayed_status(struct dwc3 *dwc)
__dwc3_ep0_do_control_status(dwc, dwc->eps[direction]);
}

static void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep)
void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep)
{
struct dwc3_gadget_ep_cmd_params params;
u32 cmd;
int ret;

if (!dep->resource_index)
/*
* For status/DATA OUT stage, TRB will be queued on ep0 out
* endpoint for which resource index is zero. Hence allow
* queuing ENDXFER command for ep0 out endpoint.
*/
if (!dep->resource_index && dep->number)
return;

cmd = DWC3_DEPCMD_ENDTRANSFER;
Expand Down
27 changes: 25 additions & 2 deletions drivers/usb/dwc3/gadget.c
Original file line number Diff line number Diff line change
Expand Up @@ -882,12 +882,13 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action)
reg |= DWC3_DALEPENA_EP(dep->number);
dwc3_writel(dwc->regs, DWC3_DALEPENA, reg);

dep->trb_dequeue = 0;
dep->trb_enqueue = 0;

if (usb_endpoint_xfer_control(desc))
goto out;

/* Initialize the TRB ring */
dep->trb_dequeue = 0;
dep->trb_enqueue = 0;
memset(dep->trb_pool, 0,
sizeof(struct dwc3_trb) * DWC3_TRB_NUM);

Expand Down Expand Up @@ -2741,6 +2742,7 @@ static int __dwc3_gadget_start(struct dwc3 *dwc)

/* begin to receive SETUP packets */
dwc->ep0state = EP0_SETUP_PHASE;
dwc->ep0_bounced = false;
dwc->link_state = DWC3_LINK_STATE_SS_DIS;
dwc->delayed_status = false;
dwc3_ep0_out_start(dwc);
Expand Down Expand Up @@ -3820,6 +3822,27 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
}

dwc3_reset_gadget(dwc);

/*
* From SNPS databook section 8.1.2, the EP0 should be in setup
* phase. So ensure that EP0 is in setup phase by issuing a stall
* and restart if EP0 is not in setup phase.
*/
if (dwc->ep0state != EP0_SETUP_PHASE) {
unsigned int dir;

dir = !!dwc->ep0_expect_in;
if (dwc->ep0state == EP0_DATA_PHASE)
dwc3_ep0_end_control_data(dwc, dwc->eps[dir]);
else
dwc3_ep0_end_control_data(dwc, dwc->eps[!dir]);

dwc->eps[0]->trb_enqueue = 0;
dwc->eps[1]->trb_enqueue = 0;

dwc3_ep0_stall_and_restart(dwc);
}

/*
* In the Synopsis DesignWare Cores USB3 Databook Rev. 3.30a
* Section 4.1.2 Table 4-2, it states that during a USB reset, the SW
Expand Down
2 changes: 2 additions & 0 deletions drivers/usb/dwc3/gadget.h
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
void dwc3_ep0_interrupt(struct dwc3 *dwc,
const struct dwc3_event_depevt *event);
void dwc3_ep0_out_start(struct dwc3 *dwc);
void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep);
void dwc3_ep0_stall_and_restart(struct dwc3 *dwc);
int __dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value);
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,
Expand Down

0 comments on commit 9d778f0

Please sign in to comment.