[PATCH v2] usb: cdns3: use VBUS Valid to determine role for dr_mode OTG

Siddharth Vadapalli s-vadapalli at ti.com
Fri Feb 13 06:15:55 CET 2026


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 at ti.com>
---

Hello,

This patch is based on commit
f9ffeec4bdc board: toradex: Make A53 get RAM size from DT in K3 boards
of the master branch of U-Boot.

v1 of this patch is at:
https://patchwork.ozlabs.org/project/uboot/patch/20260204121245.2105523-1-s-vadapalli@ti.com/
Changes since v1:
- Addressed feedback from Marek by updating cdns3_get_otg_mode() to:
  i) Return an error code (or zero in case of success) instead of returning dr_mode
  ii) Fill in the mode in a parameter passed to the function
  and updating cdns3_bind() to return an error when cdns3_get_otg_mode()
  fails, instead of continuing.

Patch has been tested on the J784S4 EVM in the context of USB DFU Boot
with dr_mode set to 'otg' in the device-tree. Logs:
https://gist.github.com/Siddharth-Vadapalli-at-TI/7a696ba0a4871317361c54003ab75360

Regards,
Siddharth.

 drivers/usb/cdns3/core.c | 64 ++++++++++++++++++++++++++++++++++++++++
 drivers/usb/cdns3/drd.c  | 11 +++++++
 2 files changed, 75 insertions(+)

diff --git a/drivers/usb/cdns3/core.c b/drivers/usb/cdns3/core.c
index 4434dc15bec..801c4f1091f 100644
--- a/drivers/usb/cdns3/core.c
+++ b/drivers/usb/cdns3/core.c
@@ -392,6 +392,63 @@ 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(ofnode node, enum usb_dr_mode *mode)
+{
+	struct cdns3 cdns, *cdnsp;
+	void __iomem *otg_regs;
+	fdt_addr_t otg_addr;
+	int otg_reg_index;
+	int vbus;
+
+	otg_reg_index = ofnode_stringlist_search(node, "reg-names", "otg");
+	if (otg_reg_index < 0) {
+		printf("%s: dr_mode is otg but otg register name is missing\n", __func__);
+		return -ENOENT;
+	}
+
+	otg_addr = ofnode_get_addr_index(node, otg_reg_index);
+	if (otg_addr == FDT_ADDR_T_NONE) {
+		printf("%s: address of otg registers is unspecified\n", __func__);
+		return -ENOENT;
+	}
+
+	otg_regs = map_physmem(otg_addr, 0, MAP_NOCACHE);
+	if (!otg_regs) {
+		printf("%s: failed to map otg registers\n", __func__);
+		return -EINVAL;
+	}
+
+	/*
+	 * 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 */
+	vbus = !!(readl(&cdnsp->otg_regs->sts) & OTGSTS_VBUS_VALID);
+	if (vbus)
+		*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 +470,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(node, &dr_mode);
+		if (ret < 0)
+			return ret;
+	}
+
 	switch (dr_mode) {
 #if defined(CONFIG_SPL_USB_HOST) || \
 	(!defined(CONFIG_XPL_BUILD) && defined(CONFIG_USB_HOST))
diff --git a/drivers/usb/cdns3/drd.c b/drivers/usb/cdns3/drd.c
index cbb13342343..0ca40a5cc8d 100644
--- a/drivers/usb/cdns3/drd.c
+++ b/drivers/usb/cdns3/drd.c
@@ -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");
-- 
2.51.1



More information about the U-Boot mailing list