usb: cdns3: use VBUS Valid to determine role for dr_mode OTG

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>
This commit is contained in:
Siddharth Vadapalli 2026-02-16 10:04:58 +05:30 committed by Marek Vasut
parent f9ffeec4bd
commit bfb530e06c
2 changed files with 64 additions and 0 deletions

View 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))

View 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");