From: Peng Fan <[email protected]>

When dr_mode is "otg", bind both dwc3-generic-host and
dwc3-generic-peripheral children to the same DT node. This allows
runtime mode switching: "usb start" probes the host child while
"ums" or "fastboot" probes the peripheral child.

Add dwc3_otg_deactivate_sibling() which is called at the top of both
host and peripheral probe. It removes the active sibling via
device_remove() so the DWC3 core can be re-initialized in the new
mode (PHY teardown, GCTL.PRTCAPDIR flip, board_usb_cleanup/init).

Signed-off-by: Peng Fan <[email protected]>
---
 drivers/usb/dwc3/dwc3-generic.c | 61 +++++++++++++++++++++++++++++++++++++++--
 1 file changed, 59 insertions(+), 2 deletions(-)

diff --git a/drivers/usb/dwc3/dwc3-generic.c b/drivers/usb/dwc3/dwc3-generic.c
index 2c6f8e6d8ba..7fcb201d5f0 100644
--- a/drivers/usb/dwc3/dwc3-generic.c
+++ b/drivers/usb/dwc3/dwc3-generic.c
@@ -8,6 +8,7 @@
  */
 
 #include <dm.h>
+#include <dm/device-internal.h>
 #include <reset.h>
 #include <usb.h>
 #include <asm/gpio.h>
@@ -194,11 +195,33 @@ static int dwc3_generic_of_to_plat(struct udevice *dev)
        return 0;
 }
 
+/*
+ * When OTG mode is used, both host and peripheral children are bound to
+ * the same DWC3 node.  Only one can be active at a time.  Before probing
+ * one child, remove the other if it is currently active so that the DWC3
+ * core can be re-initialized in the new mode.
+ */
+static void dwc3_otg_deactivate_sibling(struct udevice *dev)
+{
+       struct udevice *parent = dev->parent;
+       struct udevice *child;
+
+       device_foreach_child(child, parent) {
+               if (child != dev && device_active(child)) {
+                       debug("%s: deactivating sibling %s\n",
+                             dev->name, child->name);
+                       device_remove(child, DM_REMOVE_NORMAL);
+               }
+       }
+}
+
 #if CONFIG_IS_ENABLED(DM_USB_GADGET)
 static int dwc3_generic_peripheral_probe(struct udevice *dev)
 {
        struct dwc3_generic_priv *priv = dev_get_priv(dev);
 
+       dwc3_otg_deactivate_sibling(dev);
+
        return dwc3_generic_probe(dev, priv, USB_DR_MODE_PERIPHERAL);
 }
 
@@ -243,6 +266,8 @@ static int dwc3_generic_host_probe(struct udevice *dev)
        struct dwc3_generic_host_priv *priv = dev_get_priv(dev);
        int rc;
 
+       dwc3_otg_deactivate_sibling(dev);
+
        rc = dwc3_generic_probe(dev, &priv->gen_priv, USB_DR_MODE_HOST);
        if (rc)
                return rc;
@@ -550,9 +575,41 @@ static int dwc3_glue_bind_common(struct udevice *parent, 
ofnode node)
        if (dr_mode == USB_DR_MODE_UNKNOWN)
                dr_mode = USB_DR_MODE_OTG;
 
+       /*
+        * For OTG mode, bind both host and peripheral children so that
+        * runtime mode switching is possible via usb start / ums / fastboot.
+        * Only the one that gets probed first will actually initialize;
+        * the other stays bound but inactive until needed.
+        */
+       if (dr_mode == USB_DR_MODE_OTG) {
+               if (CONFIG_IS_ENABLED(USB_HOST)) {
+                       ret = device_bind_driver_to_node(parent, 
"dwc3-generic-host",
+                                                        name, node, &dev);
+                       if (ret) {
+                               debug("%s: failed to bind host: %d\n",
+                                     __func__, ret);
+                               return ret;
+                       }
+                       debug("%s: OTG: bound host child\n", __func__);
+               }
+
+               if (CONFIG_IS_ENABLED(DM_USB_GADGET)) {
+                       ret = device_bind_driver_to_node(parent, 
"dwc3-generic-peripheral",
+                                                        name, node, &dev);
+                       if (ret) {
+                               debug("%s: failed to bind peripheral: %d\n",
+                                     __func__, ret);
+                               return ret;
+                       }
+                       debug("%s: OTG: bound peripheral child\n", __func__);
+               }
+
+               return 0;
+       }
+
        if (CONFIG_IS_ENABLED(DM_USB_GADGET) &&
-           (dr_mode == USB_DR_MODE_PERIPHERAL || dr_mode == USB_DR_MODE_OTG)) {
-               debug("%s: dr_mode: OTG or Peripheral\n", __func__);
+           dr_mode == USB_DR_MODE_PERIPHERAL) {
+               debug("%s: dr_mode: Peripheral\n", __func__);
                driver = "dwc3-generic-peripheral";
        } else if (CONFIG_IS_ENABLED(USB_HOST) && dr_mode == USB_DR_MODE_HOST) {
                debug("%s: dr_mode: HOST\n", __func__);

-- 
2.51.0

Reply via email to