diff options
| author | Siddharth Vadapalli <[email protected]> | 2026-02-16 10:04:58 +0530 |
|---|---|---|
| committer | Marek Vasut <[email protected]> | 2026-02-16 15:08:43 +0100 |
| commit | bfb530e06ca6c19f66c079601e568c761a001993 (patch) | |
| tree | aca3c200bcfe371dbdb297dd68a956e793c87076 /drivers/usb | |
| parent | f9ffeec4bdcf1da655a0ffea482062adde78fee8 (diff) | |
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 <[email protected]>
Reviewed-by: Marek Vasut <[email protected]>
Diffstat (limited to 'drivers/usb')
| -rw-r--r-- | drivers/usb/cdns3/core.c | 53 | ||||
| -rw-r--r-- | drivers/usb/cdns3/drd.c | 11 |
2 files changed, 64 insertions, 0 deletions
diff --git a/drivers/usb/cdns3/core.c b/drivers/usb/cdns3/core.c index 4434dc15bec..10bc4cabed4 100644 --- a/drivers/usb/cdns3/core.c +++ b/drivers/usb/cdns3/core.c @@ -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)) 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"); |
