Merge branch 'master' of git://source.denx.de/u-boot-usb

- A fix for CDNS3 in correctly determining dr_mode for OTG.
This commit is contained in:
Tom Rini
2026-02-16 11:50:44 -06:00
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");