From: Stefan Eichenberger <[email protected]>
The i.MX95 SoC has two USB controllers, a DWC3 host-only controller and
a ChipIdea OTG controller. The latter is the one that can be used for
USB gadget functionality. However, U-Boot incorrectly selects the
host-only controller when attempting to set up a USB gadget, leading to
failures in gadget mode.
Iterate over usb devices and skipping any that are host-only when
selecting the controller for gadget mode. This ensures that a device
only or otg controller is used for USB gadget functionality.
Verified on Verdin iMX95 hardware: "ums 0 mmc 0" now correctly flips
the ChipIdea OTG controller into device mode and enumerates on the
host PC.
Fixes: 821ca608d816 ("usb: Use the first available device for ehci_gadget")
Signed-off-by: Stefan Eichenberger <[email protected]>
---
drivers/usb/host/usb-uclass.c | 23 +++++++++++++++++++++--
1 file changed, 21 insertions(+), 2 deletions(-)
diff --git a/drivers/usb/host/usb-uclass.c b/drivers/usb/host/usb-uclass.c
index 1c74d6fd39a..b9e9ef7e86a 100644
--- a/drivers/usb/host/usb-uclass.c
+++ b/drivers/usb/host/usb-uclass.c
@@ -18,6 +18,7 @@
#include <dm/device-internal.h>
#include <dm/lists.h>
#include <dm/uclass-internal.h>
+#include <linux/usb/otg.h>
#include <time.h>
static bool asynch_allowed;
@@ -484,10 +485,23 @@ int usb_setup_ehci_gadget(struct ehci_ctrl **ctlrp)
struct udevice *dev;
int ret;
- /* Find the old device and remove it */
+ /*
+ * Find the first UCLASS_USB device that isn't host-only. SoCs can
+ * have more than one UCLASS_USB controller (e.g. i.MX95, where a
+ * host-only DWC3 port and the ChipIdea OTG port both register under
+ * UCLASS_USB); taking whichever device happens to be first could
+ * pick the host-only controller instead of the OTG-capable one this
+ * is actually meant to flip into device mode.
+ */
ret = uclass_find_first_device(UCLASS_USB, &dev);
if (ret)
return ret;
+ while (dev && usb_get_dr_mode(dev_ofnode(dev)) == USB_DR_MODE_HOST)
+ uclass_find_next_device(&dev);
+ if (!dev)
+ return -ENODEV;
+
+ /* Remove the old device */
ret = device_remove(dev, DM_REMOVE_NORMAL);
if (ret)
return ret;
@@ -507,10 +521,15 @@ int usb_remove_ehci_gadget(struct ehci_ctrl **ctlrp)
struct udevice *dev;
int ret;
- /* Find the old device and remove it */
+ /* Find the same non-host-only device usb_setup_ehci_gadget() used */
ret = uclass_find_first_device(UCLASS_USB, &dev);
if (ret)
return ret;
+ while (dev && usb_get_dr_mode(dev_ofnode(dev)) == USB_DR_MODE_HOST)
+ uclass_find_next_device(&dev);
+ if (!dev)
+ return -ENODEV;
+
ret = device_remove(dev, DM_REMOVE_NORMAL);
if (ret)
return ret;
--
2.55.0