From: Wesley Cheng <quic_wcheng@quicinc.com>
To: <balbi@kernel.org>, <gregkh@linuxfoundation.org>
Cc: <linux-usb@vger.kernel.org>, <linux-kernel@vger.kernel.org>,
<quic_jackp@quicinc.com>, <Thinh.Nguyen@synopsys.com>,
Wesley Cheng <quic_wcheng@quicinc.com>
Subject: [RFC PATCH v2 1/3] usb: dwc3: Flush pending SETUP data during stop active xfers
Date: Tue, 15 Feb 2022 16:08:33 -0800 [thread overview]
Message-ID: <20220216000835.25400-2-quic_wcheng@quicinc.com> (raw)
In-Reply-To: <20220216000835.25400-1-quic_wcheng@quicinc.com>
While running the pullup disable sequence, if there are pending SETUP
transfers stored in the controller, then the ENDTRANSFER commands on non
control eps will fail w/ ETIMEDOUT. As a suggestion from SNPS, in order
to drain potentially cached SETUP packets, SW needs to issue a
STARTTRANSFER command. After issuing the STARTTRANSFER, and retrying the
ENDTRANSFER, the command should succeed. Else, if the endpoints are not
properly stopped, the controller halt sequence will fail as well.
One limitation is that the current logic will drop the SETUP data
being received (ie dropping the SETUP packet), however, it should be
acceptable in the pullup disable case, as the device is eventually
going to disconnect from the host.
Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
---
drivers/usb/dwc3/core.h | 7 +++++++
drivers/usb/dwc3/ep0.c | 20 +++++++++++--------
drivers/usb/dwc3/gadget.c | 42 ++++++++++++++++++++++++++++++++++-----
3 files changed, 56 insertions(+), 13 deletions(-)
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index eb9c1efced05..a411682e7f44 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -1551,6 +1551,8 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd,
int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned int cmd,
u32 param);
void dwc3_gadget_clear_tx_fifos(struct dwc3 *dwc);
+void dwc3_ep0_stall_and_restart(struct dwc3 *dwc);
+void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep);
#else
static inline int dwc3_gadget_init(struct dwc3 *dwc)
{ return 0; }
@@ -1572,6 +1574,11 @@ static inline int dwc3_send_gadget_generic_command(struct dwc3 *dwc,
{ return 0; }
static inline void dwc3_gadget_clear_tx_fifos(struct dwc3 *dwc)
{ }
+static inline void dwc3_ep0_stall_and_restart(struct dwc3 *dwc)
+{ }
+static inline void dwc3_ep0_end_control_data(struct dwc3 *dwc,
+ struct dwc3_ep *dep)
+{ }
#endif
#if IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index 658739410992..a2cc94c25dcf 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -197,7 +197,7 @@ int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
int ret;
spin_lock_irqsave(&dwc->lock, flags);
- if (!dep->endpoint.desc || !dwc->pullups_connected) {
+ if (!dep->endpoint.desc || !dwc->pullups_connected || !dwc->connected) {
dev_err(dwc->dev, "%s: can't queue to disabled endpoint\n",
dep->name);
ret = -ESHUTDOWN;
@@ -218,19 +218,21 @@ 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;
/* reinitialize physical ep1 */
dep = dwc->eps[1];
dep->flags = DWC3_EP_ENABLED;
+ dep->trb_enqueue = 0;
/* stall is always issued on EP0 */
dep = dwc->eps[0];
__dwc3_gadget_ep_set_halt(dep, 1, false);
dep->flags = DWC3_EP_ENABLED;
dwc->delayed_status = false;
+ dep->trb_enqueue = 0;
if (!list_empty(&dep->pending_list)) {
struct dwc3_request *req;
@@ -240,7 +242,9 @@ static void dwc3_ep0_stall_and_restart(struct dwc3 *dwc)
}
dwc->ep0state = EP0_SETUP_PHASE;
- dwc3_ep0_out_start(dwc);
+ complete(&dwc->ep0_in_setup);
+ if (dwc->softconnect)
+ dwc3_ep0_out_start(dwc);
}
int __dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value)
@@ -272,8 +276,6 @@ void dwc3_ep0_out_start(struct dwc3 *dwc)
struct dwc3_ep *dep;
int ret;
- complete(&dwc->ep0_in_setup);
-
dep = dwc->eps[0];
dwc3_ep0_prepare_one_trb(dep, dwc->ep0_trb_addr, 8,
DWC3_TRBCTL_CONTROL_SETUP, false);
@@ -922,7 +924,9 @@ static void dwc3_ep0_complete_status(struct dwc3 *dwc,
dwc->setup_packet_pending = true;
dwc->ep0state = EP0_SETUP_PHASE;
- dwc3_ep0_out_start(dwc);
+ complete(&dwc->ep0_in_setup);
+ if (dwc->softconnect)
+ dwc3_ep0_out_start(dwc);
}
static void dwc3_ep0_xfer_complete(struct dwc3 *dwc,
@@ -1073,7 +1077,7 @@ 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;
@@ -1083,7 +1087,7 @@ static void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep)
return;
cmd = DWC3_DEPCMD_ENDTRANSFER;
- cmd |= DWC3_DEPCMD_CMDIOC;
+ cmd |= dwc->connected ? DWC3_DEPCMD_CMDIOC : DWC3_DEPCMD_HIPRI_FORCERM;
cmd |= DWC3_DEPCMD_PARAM(dep->resource_index);
memset(¶ms, 0, sizeof(params));
ret = dwc3_send_gadget_ep_cmd(dep, cmd, ¶ms);
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 183b90923f51..f6801199440c 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -885,12 +885,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);
- if (usb_endpoint_xfer_control(desc))
- goto out;
-
/* Initialize the TRB ring */
dep->trb_dequeue = 0;
dep->trb_enqueue = 0;
+
+ if (usb_endpoint_xfer_control(desc))
+ goto out;
+
memset(dep->trb_pool, 0,
sizeof(struct dwc3_trb) * DWC3_TRB_NUM);
@@ -2476,7 +2477,8 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
* Per databook, when we want to stop the gadget, if a control transfer
* is still in process, complete it and get the core into setup phase.
*/
- if (!is_on && dwc->ep0state != EP0_SETUP_PHASE) {
+ if ((!is_on && (dwc->ep0state != EP0_SETUP_PHASE ||
+ dwc->ep0_next_event != DWC3_EP0_COMPLETE))) {
reinit_completion(&dwc->ep0_in_setup);
ret = wait_for_completion_timeout(&dwc->ep0_in_setup,
@@ -2519,6 +2521,17 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
u32 count;
dwc->connected = false;
+
+ /*
+ * Ensure no pending data/setup stages, and disable ep0 to
+ * block further EP0 transactions before stopping pending
+ * transfers.
+ */
+ dwc3_ep0_end_control_data(dwc, dwc->eps[1]);
+ dwc3_ep0_stall_and_restart(dwc);
+ __dwc3_gadget_ep_disable(dwc->eps[0]);
+ __dwc3_gadget_ep_disable(dwc->eps[1]);
+
/*
* In the Synopsis DesignWare Cores USB3 Databook Rev. 3.30a
* Section 4.1.8 Table 4-7, it states that for a device-initiated
@@ -3600,8 +3613,10 @@ static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
bool interrupt)
{
struct dwc3_gadget_ep_cmd_params params;
+ struct dwc3 *dwc = dep->dwc;
u32 cmd;
int ret;
+ int retries = 1;
if (!(dep->flags & DWC3_EP_TRANSFER_STARTED) ||
(dep->flags & DWC3_EP_END_TRANSFER_PENDING))
@@ -3633,7 +3648,7 @@ static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
*
* This mode is NOT available on the DWC_usb31 IP.
*/
-
+retry:
cmd = DWC3_DEPCMD_ENDTRANSFER;
cmd |= force ? DWC3_DEPCMD_HIPRI_FORCERM : 0;
cmd |= interrupt ? DWC3_DEPCMD_CMDIOC : 0;
@@ -3641,6 +3656,23 @@ static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
memset(¶ms, 0, sizeof(params));
ret = dwc3_send_gadget_ep_cmd(dep, cmd, ¶ms);
WARN_ON_ONCE(ret);
+ if (ret == -ETIMEDOUT) {
+ if (!dwc->connected) {
+ /*
+ * While the controller is in an active setup/control
+ * transfer, the HW is unable to service other eps
+ * potentially leading to an endxfer command timeout.
+ * It was recommended to ensure that there are no
+ * pending/cached setup packets stored in internal
+ * memory. Only way to achieve this is to issue another
+ * start transfer, and retry.
+ */
+ if (retries--) {
+ dwc3_ep0_out_start(dwc);
+ goto retry;
+ }
+ }
+ }
dep->resource_index = 0;
if (!interrupt)
next prev parent reply other threads:[~2022-02-16 0:08 UTC|newest]
Thread overview: 16+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-02-16 0:08 [RFC PATCH v2 0/3] Fix enumeration issues during composition switching Wesley Cheng
2022-02-16 0:08 ` Wesley Cheng [this message]
2022-03-22 18:21 ` [RFC PATCH v2 1/3] usb: dwc3: Flush pending SETUP data during stop active xfers Wesley Cheng
2022-03-23 1:31 ` Thinh Nguyen
[not found] ` <469213d0-526a-e26e-88c2-b34b2aa8dfe7@codeaurora.org>
2022-03-24 0:39 ` Thinh Nguyen
2022-03-24 2:19 ` Thinh Nguyen
2022-03-24 18:53 ` Wesley Cheng
2022-03-25 1:51 ` Thinh Nguyen
2022-03-25 18:35 ` Wesley Cheng
2022-04-01 19:31 ` Wesley Cheng
2022-04-04 23:32 ` Thinh Nguyen
2022-02-16 0:08 ` [RFC PATCH v2 2/3] usb: dwc3: gadget: Wait for ep0 xfers to complete during dequeue Wesley Cheng
2022-02-16 0:08 ` [RFC PATCH v2 3/3] usb: dwc3: Issue core soft reset before enabling run/stop Wesley Cheng
2022-02-17 5:52 ` Jung Daehwan
2022-02-17 9:43 ` Jung Daehwan
2022-02-17 19:01 ` Wesley Cheng
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20220216000835.25400-2-quic_wcheng@quicinc.com \
--to=quic_wcheng@quicinc.com \
--cc=Thinh.Nguyen@synopsys.com \
--cc=balbi@kernel.org \
--cc=gregkh@linuxfoundation.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-usb@vger.kernel.org \
--cc=quic_jackp@quicinc.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox