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]> --- Hello, This patch is based on commit a8d982e1f17 x86: cpu: Fix crash on FTRACE enabled builds of the master branch of U-Boot. Patch has been tested in the context of USB DFU boot on the J784S4 SoC with a Cadence USB 3.0 Controller. Since the Linux device-tree for the J784S4-EVM specifies the 'dr_mode' as OTG, due to reuse of Linux device-tree by U-Boot, the 'dr_mode' of 'otg' causes USB DFU Boot to fail. While one approach to fix it is to update 'dr_mode' to 'peripheral' in the 'k3-j784s4-u-boot.dtsi' file as a HACK, it doesn't scale to other SoCs and all of their 'u-boot.dtsi' files will have to be udpated as well. While I am providing Texas Instruments SoCs as an example, I presume that SoCs from other vendors are also affected by the 'dr_mode' being 'otg'. This patch addresses the issue by using the VBUS Valid bit of the OTG status register to override 'dr_mode' from 'otg' to a deterministic role to enable functionality. The USB DFU Boot Logs for the R5 SPL stage on J784S4 SoC with the current U-Boot master as of commit a8d982e1f17 x86: cpu: Fix crash on FTRACE enabled builds are: https://gist.github.com/Siddharth-Vadapalli-at-TI/b910f326b6c9fad760190bd299ae3ff3 and the logs indicate that USB DFU Boot fails. The failure is specifically due to 'dr_mode' being 'otg' and the cdns3_bind() function failing to bind the device-tree node to the peripheral driver required for DFU Boot. The USB DFU Boot Logs for the R5 SPL stage with just this patch applied while leaving 'dr_mode' at its existing value of 'otg' are: https://gist.github.com/Siddharth-Vadapalli-at-TI/033b9772b6ab105b96806adf890848fe Regards, Siddharth. drivers/usb/cdns3/core.c | 53 ++++++++++++++++++++++++++++++++++++++++ drivers/usb/cdns3/drd.c | 11 +++++++++ 2 files changed, 64 insertions(+) diff --git a/drivers/usb/cdns3/core.c b/drivers/usb/cdns3/core.c index 4434dc15bec..b7be5c48fc9 100644 --- a/drivers/usb/cdns3/core.c +++ b/drivers/usb/cdns3/core.c @@ -392,6 +392,55 @@ 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 OTG role. + */ +static enum usb_dr_mode cdns3_get_otg_mode(ofnode node) +{ + 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) + return USB_DR_MODE_OTG; + + otg_addr = ofnode_get_addr_index(node, otg_reg_index); + if (otg_addr == FDT_ADDR_T_NONE) + return USB_DR_MODE_OTG; + + otg_regs = map_physmem(otg_addr, 0, MAP_NOCACHE); + if (!otg_regs) + return USB_DR_MODE_OTG; + + /* + * 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) + return USB_DR_MODE_PERIPHERAL; + else + return USB_DR_MODE_HOST; +} + int cdns3_bind(struct udevice *parent) { enum usb_dr_mode dr_mode; @@ -413,6 +462,10 @@ 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) + dr_mode = cdns3_get_otg_mode(node); + 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

