]> git.ipfire.org Git - thirdparty/u-boot.git/commitdiff
usb: cdns3: use VBUS Valid to determine role for dr_mode OTG
authorSiddharth Vadapalli <s-vadapalli@ti.com>
Mon, 16 Feb 2026 04:34:58 +0000 (10:04 +0530)
committerMarek Vasut <marek.vasut+usb@mailbox.org>
Mon, 16 Feb 2026 14:08:43 +0000 (15:08 +0100)
The cdns3_bind() function is responsible for identifying the appropriate
driver to bind to the USB Controller's device-tree node. If the device-tree
node has the 'dr_mode' property set to 'otg', the existing approach fails
to bind a driver, leading to loss of functionality.

To address this, use the VBUS Valid field of the OTG Status register to
determine the role as follows:
- If VBUS Valid field is set, it indicates that a USB Host is supplying
  power and the Controller should assume the Peripheral role.
- If VBUS Valid field is clear, it indicates the absence of a USB Host and
  the Controller should assume the Host role.

Additionally, when 'dr_mode' happens to be 'otg' and the STRAP settings
are not specified, use VBUS Valid to determine the role in cdns3_drd_init()
and assign it to cdns->dr_mode.

Signed-off-by: Siddharth Vadapalli <s-vadapalli@ti.com>
Reviewed-by: Marek Vasut <marek.vasut@mailbox.org>
drivers/usb/cdns3/core.c
drivers/usb/cdns3/drd.c

index 4434dc15bec577b3a54a062320aae6e132c11925..10bc4cabed44adb4c15246fd418bf427af1884b0 100644 (file)
@@ -392,6 +392,52 @@ static const struct udevice_id cdns3_ids[] = {
        { },
 };
 
+/*
+ * The VBUS Valid Bit in the OTG Status register can be used to determine
+ * the role. When VBUS Valid is set, it indicates that a USB Host is supplying
+ * power, so the Controller should assume the PERIPHERAL role. If it isn't set,
+ * it indicates the absence of a USB Host, so the Controller should assume the
+ * HOST role. If the OTG Status register is inaccessible, return an error.
+ */
+static int cdns3_get_otg_mode(struct udevice *parent, enum usb_dr_mode *mode)
+{
+       /* Create a temporary child device for using devfdt_remap_addr_name() */
+       struct udevice child = {
+               .parent = parent,
+       };
+       struct cdns3 cdns, *cdnsp;
+       void __iomem *otg_regs;
+
+       dev_set_ofnode(&child, ofnode_first_subnode(dev_ofnode(parent)));
+       otg_regs = devfdt_remap_addr_name(&child, "otg");
+       if (!otg_regs) {
+               dev_err(parent, "failed to get otg registers for child node\n");
+               return -ENXIO;
+       }
+
+       /*
+        * As mentioned in drivers/usb/cdns3/drd.c, there are two versions
+        * of the Controller. The following logic detects the version of the
+        * Controller and interprets the register layout accordingly.
+        */
+       cdnsp = &cdns;
+       cdnsp->otg_v0_regs = otg_regs;
+       if (!readl(&cdnsp->otg_v0_regs->cmd)) {
+               cdnsp->otg_regs = otg_regs;
+       } else {
+               cdnsp->otg_v1_regs = otg_regs;
+               cdnsp->otg_regs = (void *)&cdnsp->otg_v1_regs->cmd;
+       }
+
+       /* Use VBUS Valid to determine role */
+       if (readl(&cdnsp->otg_regs->sts) & OTGSTS_VBUS_VALID)
+               *mode = USB_DR_MODE_PERIPHERAL;
+       else
+               *mode = USB_DR_MODE_HOST;
+
+       return 0;
+}
+
 int cdns3_bind(struct udevice *parent)
 {
        enum usb_dr_mode dr_mode;
@@ -413,6 +459,13 @@ int cdns3_bind(struct udevice *parent)
        if (dr_mode == USB_DR_MODE_UNKNOWN)
                dr_mode = usb_get_dr_mode(dev_ofnode(parent));
 
+       /* Use VBUS Valid to determine role */
+       if (dr_mode == USB_DR_MODE_OTG) {
+               ret = cdns3_get_otg_mode(parent, &dr_mode);
+               if (ret < 0)
+                       return ret;
+       }
+
        switch (dr_mode) {
 #if defined(CONFIG_SPL_USB_HOST) || \
        (!defined(CONFIG_XPL_BUILD) && defined(CONFIG_USB_HOST))
index cbb1334234359e0873597f420402d5c6963859a3..0ca40a5cc8d3ab67df3e0e5f1c74366cca982d5b 100644 (file)
@@ -301,6 +301,17 @@ int cdns3_drd_init(struct cdns3 *cdns)
                cdns->dr_mode = USB_DR_MODE_PERIPHERAL;
        }
 
+       /*
+        * In the absence of STRAP configuration, use VBUS Valid to
+        * determine the appropriate role to be assigned to dr_mode.
+        */
+       if (cdns->dr_mode == USB_DR_MODE_OTG) {
+               if (cdns3_get_vbus(cdns))
+                       cdns->dr_mode = USB_DR_MODE_PERIPHERAL;
+               else
+                       cdns->dr_mode = USB_DR_MODE_HOST;
+       }
+
        state = readl(&cdns->otg_regs->sts);
        if (OTGSTS_OTG_NRDY(state) != 0) {
                dev_err(cdns->dev, "Cadence USB3 OTG device not ready\n");