[PATCH v3] usb: gadget: dwc2_udc_otg: implement pullup()

Mattijs Korpershoek mkorpershoek at baylibre.com
Wed Jan 11 09:19:27 CET 2023

Pullup is used by the usb framework in order to do software-controlled
usb_gadget_connect() and usb_gadget_disconnect().

Implement pullup() for dwc2 using the SOFT_DISCONNECT bit in the dctl
* when pullup is on, clear SOFT_DISCONNECT
* when pullup is off, set SOFT_DISCONNECT

This is especially useful when a gadget disconnection is initiated but
no board_usb_cleanup() is called.

Signed-off-by: Mattijs Korpershoek <mkorpershoek at baylibre.com>
On some boards using the dwc2 controller, like the Khadas VIM3L, whenever
usb_gadget_release() is called, the D+ and D- lines are in an unknown state.

Because of that, the host can't detect usb disconnection.

It was attempted to be be fixed with [1] but ended up doing the gadget disconnection
too early, creating issues on NXP-based boards which use uuu [2].

By implementing pullup() in the controller driver, we ensure that the disconnection will
only be done when the framework calls usb_gadget_disconnect().

[1] https://lore.kernel.org/all/20220728-reset-usb-controller-v2-1-ef7657ce78b1@baylibre.com/
[2] https://lore.kernel.org/all/20230107164807.3597020-1-dario.binacchi@amarulasolutions.com/
Changes in v3:
- Simplified even further usage of clrsetbits_le32 (Marek)
- Link to v2: https://lore.kernel.org/r/20230110-dwc2-pullup-v2-1-d4c0e75a939d@baylibre.com

Changes in v2:
- Simplified code to fix hungarian notation, appropriate type (u32) for register (Harald)
- Use clrsetbits_le32() for register write (Marek)
- Link to v1: https://lore.kernel.org/r/20230110-dwc2-pullup-v1-1-2bf9743e59e5@baylibre.com
 drivers/usb/gadget/dwc2_udc_otg.c | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/drivers/usb/gadget/dwc2_udc_otg.c b/drivers/usb/gadget/dwc2_udc_otg.c
index 77988f78ab30..2bf7ed8d6046 100644
--- a/drivers/usb/gadget/dwc2_udc_otg.c
+++ b/drivers/usb/gadget/dwc2_udc_otg.c
@@ -236,6 +236,14 @@ static int udc_enable(struct dwc2_udc *dev)
 	return 0;
+static int dwc2_gadget_pullup(struct usb_gadget *g, int is_on)
+	clrsetbits_le32(&reg->dctl, SOFT_DISCONNECT,
+			is_on ? 0 : SOFT_DISCONNECT);
+	return 0;
   Register entry point for the peripheral controller driver.
@@ -805,6 +813,7 @@ static void dwc2_fifo_flush(struct usb_ep *_ep)
 static const struct usb_gadget_ops dwc2_udc_ops = {
+	.pullup = dwc2_gadget_pullup,
 	/* current versions must always be self-powered */
 	.udc_start		= dwc2_gadget_start,

base-commit: 81996adedf42e89de97c2ed5dcde62be4bb4e4e0
change-id: 20230110-dwc2-pullup-5b0f5a073d6b

Best regards,
Mattijs Korpershoek <mkorpershoek at baylibre.com>

More information about the U-Boot mailing list