]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
usb: dwc3: Fix ep0 handling when getting reset while doing control transfer
authorMayank Rana <quic_mrana@quicinc.com>
Wed, 4 May 2022 19:36:41 +0000 (12:36 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 19 May 2022 16:14:16 +0000 (18:14 +0200)
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>
drivers/usb/dwc3/ep0.c
drivers/usb/dwc3/gadget.c
drivers/usb/dwc3/gadget.h

index 6615f641b34fa6efd3fc75a3db5bba98d2806c4d..5d642660fd155af67b1b412eb8a3964c96c88ab6 100644 (file)
@@ -218,7 +218,7 @@ out:
        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;
 
@@ -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;
index 4f54f0ef21df22fe7015985daa6cd9b047a8d54d..8c366fa82105f5a69182ee99f0d86c4a2a724a5b 100644 (file)
@@ -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);
 
@@ -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);
@@ -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
index f763380e672edbbb605148185288bc4b8591a739..55a56cf67d7364998f9f4a42fd95e5d856cd105c 100644 (file)
@@ -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,