Oliver Neukum acaaa1
From 9d778f0c5f95ca5aa2ff628ea281978697e8d89b Mon Sep 17 00:00:00 2001
Oliver Neukum acaaa1
From: Mayank Rana <quic_mrana@quicinc.com>
Oliver Neukum acaaa1
Date: Wed, 4 May 2022 12:36:41 -0700
Oliver Neukum acaaa1
Subject: [PATCH] usb: dwc3: Fix ep0 handling when getting reset while doing
Oliver Neukum acaaa1
 control transfer
Oliver Neukum acaaa1
Git-commit: 9d778f0c5f95ca5aa2ff628ea281978697e8d89b
Oliver Neukum acaaa1
References: git-fixes
Oliver Neukum acaaa1
Patch-mainline: v5.19-rc1
Oliver Neukum acaaa1
Oliver Neukum acaaa1
According to the databook ep0 should be in setup phase during reset.
Oliver Neukum acaaa1
If host issues reset between control transfers, ep0 will be  in an
Oliver Neukum acaaa1
invalid state. Fix this by issuing stall and restart on ep0 if it
Oliver Neukum acaaa1
is not in setup phase.
Oliver Neukum acaaa1
Oliver Neukum acaaa1
Also SW needs to complete pending control transfer and setup core for
Oliver Neukum acaaa1
next setup stage as per data book. Hence check ep0 state during reset
Oliver Neukum acaaa1
interrupt handling and make sure active transfers on ep0 out/in
Oliver Neukum acaaa1
endpoint are stopped by queuing ENDXFER command for that endpoint and
Oliver Neukum acaaa1
restart ep0 out again to receive next setup packet.
Oliver Neukum acaaa1
Oliver Neukum acaaa1
Signed-off-by: Mayank Rana <quic_mrana@quicinc.com>
Oliver Neukum acaaa1
Link: https://lore.kernel.org/r/1651693001-29891-1-git-send-email-quic_mrana@quicinc.com
Oliver Neukum acaaa1
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Oliver Neukum acaaa1
Signed-off-by: Oliver Neukum <oneukum@suse.com>
Oliver Neukum acaaa1
---
Oliver Neukum acaaa1
 drivers/usb/dwc3/ep0.c    | 11 ++++++++---
Oliver Neukum acaaa1
 drivers/usb/dwc3/gadget.c | 27 +++++++++++++++++++++++++--
Oliver Neukum acaaa1
 drivers/usb/dwc3/gadget.h |  2 ++
Oliver Neukum acaaa1
 3 files changed, 35 insertions(+), 5 deletions(-)
Oliver Neukum acaaa1
Oliver Neukum acaaa1
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
Oliver Neukum acaaa1
index 6615f641b34f..5d642660fd15 100644
Oliver Neukum acaaa1
--- a/drivers/usb/dwc3/ep0.c
Oliver Neukum acaaa1
+++ b/drivers/usb/dwc3/ep0.c
Oliver Neukum acaaa1
@@ -218,7 +218,7 @@ int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
Oliver Neukum acaaa1
 	return ret;
Oliver Neukum acaaa1
 }
Oliver Neukum acaaa1
 
Oliver Neukum acaaa1
-static void dwc3_ep0_stall_and_restart(struct dwc3 *dwc)
Oliver Neukum acaaa1
+void dwc3_ep0_stall_and_restart(struct dwc3 *dwc)
Oliver Neukum acaaa1
 {
Oliver Neukum acaaa1
 	struct dwc3_ep		*dep;
Oliver Neukum acaaa1
 
Oliver Neukum acaaa1
@@ -1088,13 +1088,18 @@ void dwc3_ep0_send_delayed_status(struct dwc3 *dwc)
Oliver Neukum acaaa1
 	__dwc3_ep0_do_control_status(dwc, dwc->eps[direction]);
Oliver Neukum acaaa1
 }
Oliver Neukum acaaa1
 
Oliver Neukum acaaa1
-static void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep)
Oliver Neukum acaaa1
+void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep)
Oliver Neukum acaaa1
 {
Oliver Neukum acaaa1
 	struct dwc3_gadget_ep_cmd_params params;
Oliver Neukum acaaa1
 	u32			cmd;
Oliver Neukum acaaa1
 	int			ret;
Oliver Neukum acaaa1
 
Oliver Neukum acaaa1
-	if (!dep->resource_index)
Oliver Neukum acaaa1
+	/*
Oliver Neukum acaaa1
+	 * For status/DATA OUT stage, TRB will be queued on ep0 out
Oliver Neukum acaaa1
+	 * endpoint for which resource index is zero. Hence allow
Oliver Neukum acaaa1
+	 * queuing ENDXFER command for ep0 out endpoint.
Oliver Neukum acaaa1
+	 */
Oliver Neukum acaaa1
+	if (!dep->resource_index && dep->number)
Oliver Neukum acaaa1
 		return;
Oliver Neukum acaaa1
 
Oliver Neukum acaaa1
 	cmd = DWC3_DEPCMD_ENDTRANSFER;
Oliver Neukum acaaa1
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
Oliver Neukum acaaa1
index 4f54f0ef21df..8c366fa82105 100644
Oliver Neukum acaaa1
--- a/drivers/usb/dwc3/gadget.c
Oliver Neukum acaaa1
+++ b/drivers/usb/dwc3/gadget.c
Oliver Neukum acaaa1
@@ -882,12 +882,13 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action)
Oliver Neukum acaaa1
 		reg |= DWC3_DALEPENA_EP(dep->number);
Oliver Neukum acaaa1
 		dwc3_writel(dwc->regs, DWC3_DALEPENA, reg);
Oliver Neukum acaaa1
 
Oliver Neukum acaaa1
+		dep->trb_dequeue = 0;
Oliver Neukum acaaa1
+		dep->trb_enqueue = 0;
Oliver Neukum acaaa1
+
Oliver Neukum acaaa1
 		if (usb_endpoint_xfer_control(desc))
Oliver Neukum acaaa1
 			goto out;
Oliver Neukum acaaa1
 
Oliver Neukum acaaa1
 		/* Initialize the TRB ring */
Oliver Neukum acaaa1
-		dep->trb_dequeue = 0;
Oliver Neukum acaaa1
-		dep->trb_enqueue = 0;
Oliver Neukum acaaa1
 		memset(dep->trb_pool, 0,
Oliver Neukum acaaa1
 		       sizeof(struct dwc3_trb) * DWC3_TRB_NUM);
Oliver Neukum acaaa1
 
Oliver Neukum acaaa1
@@ -2741,6 +2742,7 @@ static int __dwc3_gadget_start(struct dwc3 *dwc)
Oliver Neukum acaaa1
 
Oliver Neukum acaaa1
 	/* begin to receive SETUP packets */
Oliver Neukum acaaa1
 	dwc->ep0state = EP0_SETUP_PHASE;
Oliver Neukum acaaa1
+	dwc->ep0_bounced = false;
Oliver Neukum acaaa1
 	dwc->link_state = DWC3_LINK_STATE_SS_DIS;
Oliver Neukum acaaa1
 	dwc->delayed_status = false;
Oliver Neukum acaaa1
 	dwc3_ep0_out_start(dwc);
Oliver Neukum acaaa1
@@ -3820,6 +3822,27 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
Oliver Neukum acaaa1
 	}
Oliver Neukum acaaa1
 
Oliver Neukum acaaa1
 	dwc3_reset_gadget(dwc);
Oliver Neukum acaaa1
+
Oliver Neukum acaaa1
+	/*
Oliver Neukum acaaa1
+	 * From SNPS databook section 8.1.2, the EP0 should be in setup
Oliver Neukum acaaa1
+	 * phase. So ensure that EP0 is in setup phase by issuing a stall
Oliver Neukum acaaa1
+	 * and restart if EP0 is not in setup phase.
Oliver Neukum acaaa1
+	 */
Oliver Neukum acaaa1
+	if (dwc->ep0state != EP0_SETUP_PHASE) {
Oliver Neukum acaaa1
+		unsigned int	dir;
Oliver Neukum acaaa1
+
Oliver Neukum acaaa1
+		dir = !!dwc->ep0_expect_in;
Oliver Neukum acaaa1
+		if (dwc->ep0state == EP0_DATA_PHASE)
Oliver Neukum acaaa1
+			dwc3_ep0_end_control_data(dwc, dwc->eps[dir]);
Oliver Neukum acaaa1
+		else
Oliver Neukum acaaa1
+			dwc3_ep0_end_control_data(dwc, dwc->eps[!dir]);
Oliver Neukum acaaa1
+
Oliver Neukum acaaa1
+		dwc->eps[0]->trb_enqueue = 0;
Oliver Neukum acaaa1
+		dwc->eps[1]->trb_enqueue = 0;
Oliver Neukum acaaa1
+
Oliver Neukum acaaa1
+		dwc3_ep0_stall_and_restart(dwc);
Oliver Neukum acaaa1
+	}
Oliver Neukum acaaa1
+
Oliver Neukum acaaa1
 	/*
Oliver Neukum acaaa1
 	 * In the Synopsis DesignWare Cores USB3 Databook Rev. 3.30a
Oliver Neukum acaaa1
 	 * Section 4.1.2 Table 4-2, it states that during a USB reset, the SW
Oliver Neukum acaaa1
diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h
Oliver Neukum acaaa1
index f763380e672e..55a56cf67d73 100644
Oliver Neukum acaaa1
--- a/drivers/usb/dwc3/gadget.h
Oliver Neukum acaaa1
+++ b/drivers/usb/dwc3/gadget.h
Oliver Neukum acaaa1
@@ -110,6 +110,8 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
Oliver Neukum acaaa1
 void dwc3_ep0_interrupt(struct dwc3 *dwc,
Oliver Neukum acaaa1
 		const struct dwc3_event_depevt *event);
Oliver Neukum acaaa1
 void dwc3_ep0_out_start(struct dwc3 *dwc);
Oliver Neukum acaaa1
+void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep);
Oliver Neukum acaaa1
+void dwc3_ep0_stall_and_restart(struct dwc3 *dwc);
Oliver Neukum acaaa1
 int __dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value);
Oliver Neukum acaaa1
 int dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value);
Oliver Neukum acaaa1
 int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
Oliver Neukum acaaa1
-- 
Oliver Neukum acaaa1
2.40.1
Oliver Neukum acaaa1