
We don't support USB super-speed in U-Boot yet, we lack the SS PHY drivers, however from my testing even with a PHY driver there seem to be other issues when talking to super-speed peripherals.
In pursuit of maintaining upstream DT compatibility, and simplifying porting for new devices, let's implement the DT fixups necessary to configure USB in high-speed only mode at runtime. The pattern is identical for all Qualcomm boards that use the Synaptics DWC3 controller:
* Add an additional property on the Qualcomm wrapper node * Remove the super-speed phy phandle and phy-name entries.
Signed-off-by: Caleb Connolly caleb.connolly@linaro.org --- arch/arm/mach-snapdragon/board.c | 88 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+)
diff --git a/arch/arm/mach-snapdragon/board.c b/arch/arm/mach-snapdragon/board.c index ca300dc843a9..44925e369d8e 100644 --- a/arch/arm/mach-snapdragon/board.c +++ b/arch/arm/mach-snapdragon/board.c @@ -146,6 +146,93 @@ int board_usb_init(int index, enum usb_init_type init) return 0; }
+/* U-Boot only supports USB high-speed mode on Qualcomm platforms with DWC3 + * USB controllers. Rather than requiring source level DT changes, we fix up + * DT here. This improves compatibility with upstream DT and simplifies the + * porting process for new devices. + */ +#if CONFIG_IS_ENABLED(OF_LIVE) +static int fixup_qcom_dwc3(struct device_node *glue_np) +{ + struct device_node *dwc3; + int ret, len, hsphy_idx = 1; + const __be32 *phandles; + const char *second_phy_name; + + debug("Fixing up %s\n", glue_np->name); + + /* Tell the glue driver to configure the wrapper for high-speed only operation */ + ret = of_write_prop(glue_np, "qcom,select-utmi-as-pipe-clk", NULL, 0); + if (ret) { + log_err("Failed to add property 'qcom,select-utmi-as-pipe-clk': %d\n", ret); + return ret; + } + + /* Find the DWC3 node itself */ + dwc3 = of_find_compatible_node(glue_np, NULL, "snps,dwc3"); + if (!dwc3) { + log_err("Failed to find dwc3 node\n"); + return -ENOENT; + } + + phandles = of_get_property(dwc3, "phys", &len); + len /= sizeof(*phandles); + if (len == 1) { + log_debug("Only one phy, not a superspeed controller\n"); + return 0; + } + + /* Figure out if the superspeed phy is present and if so then which phy is it? */ + ret = of_property_read_string_index(dwc3, "phy-names", 1, &second_phy_name); + if (ret == -ENODATA) { + log_debug("Only one phy, not a super-speed controller\n"); + return 0; + } else if (ret) { + log_err("Failed to read second phy name: %d\n", ret); + return ret; + } + + if (!strncmp("usb3-phy", second_phy_name, strlen("usb3-phy"))) { + log_debug("Second phy isn't superspeed (is '%s') assuming first phy is SS\n", + second_phy_name); + hsphy_idx = 0; + } + + /* Overwrite the "phys" property to only contain the high-speed phy */ + ret = of_write_prop(dwc3, "phys", sizeof(*phandles), phandles + hsphy_idx); + if (ret) { + log_err("Failed to overwrite 'phys' property: %d\n", ret); + return ret; + } + + /* Overwrite "phy-names" to only contain a single entry */ + ret = of_write_prop(dwc3, "phy-names", strlen("usb2-phy"), "usb2-phy"); + if (ret) { + log_err("Failed to overwrite 'phy-names' property: %d\n", ret); + return ret; + } + + return 0; +} + +static void fixup_usb_nodes(void) +{ + struct device_node *glue_np = NULL; + int ret; + + while ((glue_np = of_find_compatible_node(glue_np, NULL, "qcom,dwc3"))) { + ret = fixup_qcom_dwc3(glue_np); + if (ret) + log_warning("Failed to fixup node %s: %d\n", glue_np->name, ret); + } +} +#else +static void fixup_usb_nodes(void) +{ + log_debug("Unable to dynamically fixup USB nodes, please enable CONFIG_OF_LIVE\n"); +} +#endif + /* * Some boards still need board specific init code, they can implement that by * overriding this function. @@ -159,6 +246,7 @@ void __weak qcom_board_init(void) int board_init(void) { show_psci_version(); + fixup_usb_nodes(); qcom_board_init(); return 0; }