summaryrefslogtreecommitdiff
path: root/drivers/usb
diff options
context:
space:
mode:
authorSiddharth Vadapalli <[email protected]>2026-02-16 10:04:58 +0530
committerMarek Vasut <[email protected]>2026-02-16 15:08:43 +0100
commitbfb530e06ca6c19f66c079601e568c761a001993 (patch)
treeaca3c200bcfe371dbdb297dd68a956e793c87076 /drivers/usb
parentf9ffeec4bdcf1da655a0ffea482062adde78fee8 (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.c53
-rw-r--r--drivers/usb/cdns3/drd.c11
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");