The Linux Kernel Mailing List
 help / color / mirror / Atom feed
* [PATCH v2] usb: dwc3: clear forceRM when issuing EndTransfer
@ 2026-08-13 15:14 Elson Serrao
  2026-08-26  0:49 ` Thinh Nguyen
  0 siblings, 1 reply; 2+ messages in thread
From: Elson Serrao @ 2026-08-13 15:14 UTC (permalink / raw)
  To: Thinh.Nguyen, gregkh; +Cc: linux-usb, linux-kernel, stable

The forceRM bit of the DEPCMD register controls the behavior of the
EndTransfer command used to stop an active transfer. Older DWC3
programming guide revisions recommended setting forceRM=1 when
issuing EndTransfer. Newer programming guide revisions recommend
issuing EndTransfer with forceRM cleared.

With forceRM=1 on DWC_usb31 v2.00a and v2.10a controllers, a transfer
aborted through the ep_dequeue path was observed to remain active
after EndTransfer completion. A subsequent StartTransfer issued on the
same endpoint triggered writes associated with the aborted transfer.
This resulted in an SMMU fault because the transfer buffer had already
been unmapped during EndTransfer command-completion cleanup.

Using forceRM=0 eliminates the issue. Although older DWC3 programming
guide revisions recommended setting forceRM=1, no issues are known
from using forceRM=0. Clear forceRM when issuing EndTransfer to provide
consistent EndTransfer behavior and align with newer programming guide
recommendations.

Fixes: 1e43c86d84fb ("usb: dwc3: core: Add DWC31 version 2.00a controller")
Cc: stable@vger.kernel.org
Signed-off-by: Elson Serrao <elson.serrao@oss.qualcomm.com>
---
Changes in v2:
 - Clear forceRM unconditionally and document the programming guide
   recommendation (Thinh).
 - Link to v1: https://lore.kernel.org/all/20260806234035.1078704-1-elson.serrao@oss.qualcomm.com/
---
 drivers/usb/dwc3/ep0.c    |  2 +-
 drivers/usb/dwc3/gadget.c | 21 +++++++++++++--------
 2 files changed, 14 insertions(+), 9 deletions(-)

diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index bfe616194dfa..310b5ffb236a 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -304,7 +304,7 @@ void dwc3_ep0_out_start(struct dwc3 *dwc)
 
 		dwc3_ep->flags &= ~DWC3_EP_DELAY_STOP;
 		if (dwc->connected)
-			dwc3_stop_active_transfer(dwc3_ep, true, true);
+			dwc3_stop_active_transfer(dwc3_ep, false, true);
 		else
 			dwc3_remove_requests(dwc, dwc3_ep, -ESHUTDOWN);
 	}
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index fa0f16ffafef..dad9866b908e 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -1004,7 +1004,7 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action)
 			 * controller to generate an ERDY to initiate the
 			 * stream.
 			 */
-			dwc3_stop_active_transfer(dep, true, true);
+			dwc3_stop_active_transfer(dep, false, true);
 
 			/*
 			 * All stream eps will reinitiate stream on NoStream
@@ -1032,7 +1032,7 @@ void dwc3_remove_requests(struct dwc3 *dwc, struct dwc3_ep *dep, int status)
 {
 	struct dwc3_request		*req;
 
-	dwc3_stop_active_transfer(dep, true, false);
+	dwc3_stop_active_transfer(dep, false, false);
 
 	/* If endxfer is delayed, avoid unmapping requests */
 	if (dep->flags & DWC3_EP_DELAY_STOP)
@@ -1720,7 +1720,7 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep)
 		if (ret == -EAGAIN)
 			return ret;
 
-		dwc3_stop_active_transfer(dep, true, true);
+		dwc3_stop_active_transfer(dep, false, true);
 
 		list_for_each_entry_safe(req, tmp, &dep->started_list, list)
 			dwc3_gadget_move_cancelled_request(req, DWC3_REQUEST_STATUS_DEQUEUED);
@@ -1757,6 +1757,11 @@ static int __dwc3_gadget_get_frame(struct dwc3 *dwc)
  * the controller won't update the TRB progress on command
  * completion. It also won't clear the HWO bit in the TRB.
  * The command will also not complete immediately in that case.
+ *
+ * Older programming guide revisions recommended setting ForceRM to 1
+ * when ending a transfer. Newer programming guide revisions now
+ * recommend keeping ForceRM cleared, and TRBs are properly updated
+ * on command completion.
  */
 static int __dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force, bool interrupt)
 {
@@ -1882,7 +1887,7 @@ static int dwc3_gadget_start_isoc_quirk(struct dwc3_ep *dep)
 		 * to wait for the next XferNotReady to test the command again
 		 */
 		if (cmd_status == 0) {
-			dwc3_stop_active_transfer(dep, true, true);
+			dwc3_stop_active_transfer(dep, false, true);
 			return 0;
 		}
 	}
@@ -2165,7 +2170,7 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
 			struct dwc3_request *t;
 
 			/* wait until it is processed */
-			dwc3_stop_active_transfer(dep, true, true);
+			dwc3_stop_active_transfer(dep, false, true);
 
 			/*
 			 * Remove any started request if the transfer is
@@ -2242,7 +2247,7 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol)
 			return 0;
 		}
 
-		dwc3_stop_active_transfer(dep, true, true);
+		dwc3_stop_active_transfer(dep, false, true);
 
 		list_for_each_entry_safe(req, tmp, &dep->started_list, list)
 			dwc3_gadget_move_cancelled_request(req, DWC3_REQUEST_STATUS_STALLED);
@@ -3368,7 +3373,7 @@ static void dwc3_nostream_work(struct work_struct *work)
 		dwc3_send_gadget_generic_command(dwc, cmd, dep->number);
 	} else {
 		dep->flags |= DWC3_EP_DELAY_START;
-		dwc3_stop_active_transfer(dep, true, true);
+		dwc3_stop_active_transfer(dep, false, true);
 		spin_unlock_irqrestore(&dwc->lock, flags);
 		return;
 	}
@@ -3725,7 +3730,7 @@ static bool dwc3_gadget_endpoint_trbs_complete(struct dwc3_ep *dep,
 	if (usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
 		list_empty(&dep->started_list) &&
 		(list_empty(&dep->pending_list) || status == -EXDEV))
-		dwc3_stop_active_transfer(dep, true, true);
+		dwc3_stop_active_transfer(dep, false, true);
 	else if (dwc3_gadget_ep_should_continue(dep))
 		if (__dwc3_gadget_kick_transfer(dep) == 0)
 			no_started_trb = false;
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 2+ messages in thread

* Re: [PATCH v2] usb: dwc3: clear forceRM when issuing EndTransfer
  2026-08-13 15:14 [PATCH v2] usb: dwc3: clear forceRM when issuing EndTransfer Elson Serrao
@ 2026-08-26  0:49 ` Thinh Nguyen
  0 siblings, 0 replies; 2+ messages in thread
From: Thinh Nguyen @ 2026-08-26  0:49 UTC (permalink / raw)
  To: Elson Serrao
  Cc: Thinh Nguyen, gregkh@linuxfoundation.org,
	linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org,
	stable@vger.kernel.org

On Thu, Aug 13, 2026, Elson Serrao wrote:
> The forceRM bit of the DEPCMD register controls the behavior of the
> EndTransfer command used to stop an active transfer. Older DWC3
> programming guide revisions recommended setting forceRM=1 when
> issuing EndTransfer. Newer programming guide revisions recommend
> issuing EndTransfer with forceRM cleared.
> 
> With forceRM=1 on DWC_usb31 v2.00a and v2.10a controllers, a transfer
> aborted through the ep_dequeue path was observed to remain active
> after EndTransfer completion. A subsequent StartTransfer issued on the
> same endpoint triggered writes associated with the aborted transfer.
> This resulted in an SMMU fault because the transfer buffer had already
> been unmapped during EndTransfer command-completion cleanup.
> 
> Using forceRM=0 eliminates the issue. Although older DWC3 programming
> guide revisions recommended setting forceRM=1, no issues are known
> from using forceRM=0. Clear forceRM when issuing EndTransfer to provide
> consistent EndTransfer behavior and align with newer programming guide
> recommendations.
> 
> Fixes: 1e43c86d84fb ("usb: dwc3: core: Add DWC31 version 2.00a controller")
> Cc: stable@vger.kernel.org
> Signed-off-by: Elson Serrao <elson.serrao@oss.qualcomm.com>
> ---
> Changes in v2:
>  - Clear forceRM unconditionally and document the programming guide
>    recommendation (Thinh).
>  - Link to v1: https://urldefense.com/v3/__https://lore.kernel.org/all/20260806234035.1078704-1-elson.serrao@oss.qualcomm.com/__;!!A4F2R9G_pg!e13OQ2r3nii3qv-VjhV7LQ3IcrOwgi2TfOadJHvi4jwUsKTcdUj54WwHj3XCaUcJ2COrAi6tBLzg-_G2CUPUWc7NpXhPd2g6$ 
> ---
>  drivers/usb/dwc3/ep0.c    |  2 +-
>  drivers/usb/dwc3/gadget.c | 21 +++++++++++++--------
>  2 files changed, 14 insertions(+), 9 deletions(-)
> 
> diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
> index bfe616194dfa..310b5ffb236a 100644
> --- a/drivers/usb/dwc3/ep0.c
> +++ b/drivers/usb/dwc3/ep0.c
> @@ -304,7 +304,7 @@ void dwc3_ep0_out_start(struct dwc3 *dwc)
>  
>  		dwc3_ep->flags &= ~DWC3_EP_DELAY_STOP;
>  		if (dwc->connected)
> -			dwc3_stop_active_transfer(dwc3_ep, true, true);
> +			dwc3_stop_active_transfer(dwc3_ep, false, true);
>  		else
>  			dwc3_remove_requests(dwc, dwc3_ep, -ESHUTDOWN);
>  	}
> diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
> index fa0f16ffafef..dad9866b908e 100644
> --- a/drivers/usb/dwc3/gadget.c
> +++ b/drivers/usb/dwc3/gadget.c
> @@ -1004,7 +1004,7 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action)
>  			 * controller to generate an ERDY to initiate the
>  			 * stream.
>  			 */
> -			dwc3_stop_active_transfer(dep, true, true);
> +			dwc3_stop_active_transfer(dep, false, true);
>  
>  			/*
>  			 * All stream eps will reinitiate stream on NoStream
> @@ -1032,7 +1032,7 @@ void dwc3_remove_requests(struct dwc3 *dwc, struct dwc3_ep *dep, int status)
>  {
>  	struct dwc3_request		*req;
>  
> -	dwc3_stop_active_transfer(dep, true, false);
> +	dwc3_stop_active_transfer(dep, false, false);
>  
>  	/* If endxfer is delayed, avoid unmapping requests */
>  	if (dep->flags & DWC3_EP_DELAY_STOP)
> @@ -1720,7 +1720,7 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep)
>  		if (ret == -EAGAIN)
>  			return ret;
>  
> -		dwc3_stop_active_transfer(dep, true, true);
> +		dwc3_stop_active_transfer(dep, false, true);
>  
>  		list_for_each_entry_safe(req, tmp, &dep->started_list, list)
>  			dwc3_gadget_move_cancelled_request(req, DWC3_REQUEST_STATUS_DEQUEUED);
> @@ -1757,6 +1757,11 @@ static int __dwc3_gadget_get_frame(struct dwc3 *dwc)
>   * the controller won't update the TRB progress on command
>   * completion. It also won't clear the HWO bit in the TRB.
>   * The command will also not complete immediately in that case.
> + *
> + * Older programming guide revisions recommended setting ForceRM to 1
> + * when ending a transfer. Newer programming guide revisions now
> + * recommend keeping ForceRM cleared, and TRBs are properly updated
> + * on command completion.
>   */
>  static int __dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force, bool interrupt)
>  {
> @@ -1882,7 +1887,7 @@ static int dwc3_gadget_start_isoc_quirk(struct dwc3_ep *dep)
>  		 * to wait for the next XferNotReady to test the command again
>  		 */
>  		if (cmd_status == 0) {
> -			dwc3_stop_active_transfer(dep, true, true);
> +			dwc3_stop_active_transfer(dep, false, true);
>  			return 0;
>  		}
>  	}
> @@ -2165,7 +2170,7 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
>  			struct dwc3_request *t;
>  
>  			/* wait until it is processed */
> -			dwc3_stop_active_transfer(dep, true, true);
> +			dwc3_stop_active_transfer(dep, false, true);
>  
>  			/*
>  			 * Remove any started request if the transfer is
> @@ -2242,7 +2247,7 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol)
>  			return 0;
>  		}
>  
> -		dwc3_stop_active_transfer(dep, true, true);
> +		dwc3_stop_active_transfer(dep, false, true);
>  
>  		list_for_each_entry_safe(req, tmp, &dep->started_list, list)
>  			dwc3_gadget_move_cancelled_request(req, DWC3_REQUEST_STATUS_STALLED);
> @@ -3368,7 +3373,7 @@ static void dwc3_nostream_work(struct work_struct *work)
>  		dwc3_send_gadget_generic_command(dwc, cmd, dep->number);
>  	} else {
>  		dep->flags |= DWC3_EP_DELAY_START;
> -		dwc3_stop_active_transfer(dep, true, true);
> +		dwc3_stop_active_transfer(dep, false, true);
>  		spin_unlock_irqrestore(&dwc->lock, flags);
>  		return;
>  	}
> @@ -3725,7 +3730,7 @@ static bool dwc3_gadget_endpoint_trbs_complete(struct dwc3_ep *dep,
>  	if (usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
>  		list_empty(&dep->started_list) &&
>  		(list_empty(&dep->pending_list) || status == -EXDEV))
> -		dwc3_stop_active_transfer(dep, true, true);
> +		dwc3_stop_active_transfer(dep, false, true);
>  	else if (dwc3_gadget_ep_should_continue(dep))
>  		if (__dwc3_gadget_kick_transfer(dep) == 0)
>  			no_started_trb = false;
> -- 
> 2.34.1
> 

Acked-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com>

Thanks,
Thinh

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2026-08-26  0:49 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-13 15:14 [PATCH v2] usb: dwc3: clear forceRM when issuing EndTransfer Elson Serrao
2026-08-26  0:49 ` Thinh Nguyen

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox