[PATCH 0/6] Allwinner H6 USB3 support

This series adds clock, PHY, and XHCI driver support for the USB3 controller found in the Allwinner H6 SoC. Below is a log showing it functioning on the Orange Pi 3.
Cheers, Samuel
U-Boot 2021.04-rc1-00068-g6b8f4e0d060 (Jan 01 1970 - 00:00:00 +0000) Allwinner Technology
CPU: Allwinner H6 (SUN50I) Model: OrangePi 3 DRAM: 2 GiB MMC: mmc@4020000: 0, mmc@4021000: 2, mmc@4022000: 1 Loading Environment from FAT... Unable to use mmc 1:0... In: serial@5000000 Out: serial@5000000 Err: serial@5000000 Net: No ethernet found. starting USB... Bus usb@5101000: USB EHCI 1.00 Bus usb@5101400: USB OHCI 1.0 Bus usb@5200000: Register 2000140 NbrPorts 2 Starting the controller USB XHCI 1.00 Bus usb@5311000: USB EHCI 1.00 Bus usb@5311400: USB OHCI 1.0 scanning bus usb@5101000 for devices... 1 USB Device(s) found scanning bus usb@5101400 for devices... 1 USB Device(s) found scanning bus usb@5200000 for devices... 4 USB Device(s) found scanning bus usb@5311000 for devices... 1 USB Device(s) found scanning bus usb@5311400 for devices... 1 USB Device(s) found scanning usb for storage devices... 1 Storage Device(s) found Hit any key to stop autoboot: 0 => usb tree USB device tree: 1 Hub (480 Mb/s, 0mA) u-boot EHCI Host Controller
1 Hub (12 Mb/s, 0mA) U-Boot Root Hub
1 Hub (5 Gb/s, 0mA) | U-Boot XHCI Host Controller | +-2 Hub (5 Gb/s, 0mA) | | GenesysLogic USB3.1 Hub | | | +-4 Mass Storage (5 Gb/s, 224mA) | 071044991D8D6A81 | +-3 Hub (480 Mb/s, 100mA) GenesysLogic USB2.1 Hub
1 Hub (480 Mb/s, 0mA) u-boot EHCI Host Controller
1 Hub (12 Mb/s, 0mA) U-Boot Root Hub
Samuel Holland (6): clk: sunxi: Add a dummy clock driver for the RTC clk: sunxi: h6: Add XHCI clocks phy: sun50i-usb3: Add a driver for the H6 USB3 PHY usb: xhci-pci: Move reset logic out of XHCI core usb: xhci-dwc3: Add support for clocks/resets configs: Enable USB3 on Allwinner H6 boards
configs/orangepi_3_defconfig | 5 + configs/pine_h64_defconfig | 5 + drivers/clk/sunxi/Makefile | 2 + drivers/clk/sunxi/clk_h6.c | 2 + drivers/clk/sunxi/clk_sun6i_rtc.c | 35 +++++ drivers/phy/allwinner/Kconfig | 8 ++ drivers/phy/allwinner/Makefile | 1 + drivers/phy/allwinner/phy-sun50i-usb3.c | 171 ++++++++++++++++++++++++ drivers/usb/host/xhci-dwc3.c | 56 ++++++++ drivers/usb/host/xhci-mem.c | 2 - drivers/usb/host/xhci-pci.c | 38 +++++- drivers/usb/host/xhci.c | 35 ----- include/usb/xhci.h | 2 - 13 files changed, 321 insertions(+), 41 deletions(-) create mode 100644 drivers/clk/sunxi/clk_sun6i_rtc.c create mode 100644 drivers/phy/allwinner/phy-sun50i-usb3.c

The 32kHz clock ("LOSC") on sunxi SoCs is provided by the RTC. It is used, among other things, by the XHCI controller in the H6. To be able to call clk_get_bulk() on the XHCI controller, some device needs to provide all referenced clocks.
Since LOSC is a fixed-rate always-on clock, implementation is trivial.
Signed-off-by: Samuel Holland samuel@sholland.org --- drivers/clk/sunxi/Makefile | 2 ++ drivers/clk/sunxi/clk_sun6i_rtc.c | 35 +++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 drivers/clk/sunxi/clk_sun6i_rtc.c
diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile index 0dfc0593fb1..4f9282a8b9b 100644 --- a/drivers/clk/sunxi/Makefile +++ b/drivers/clk/sunxi/Makefile @@ -6,6 +6,8 @@
obj-$(CONFIG_CLK_SUNXI) += clk_sunxi.o
+obj-$(CONFIG_CLK_SUNXI) += clk_sun6i_rtc.o + obj-$(CONFIG_CLK_SUN4I_A10) += clk_a10.o obj-$(CONFIG_CLK_SUN5I_A10S) += clk_a10s.o obj-$(CONFIG_CLK_SUN6I_A31) += clk_a31.o diff --git a/drivers/clk/sunxi/clk_sun6i_rtc.c b/drivers/clk/sunxi/clk_sun6i_rtc.c new file mode 100644 index 00000000000..0c280d221ba --- /dev/null +++ b/drivers/clk/sunxi/clk_sun6i_rtc.c @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright (C) 2018 Amarula Solutions. + * Copyright (C) 2020 Samuel Holland samuel@sholland.org + */ + +#include <clk-uclass.h> +#include <dm.h> + +static int clk_sun6i_rtc_enable(struct clk *clk) +{ + return 0; +} + +static const struct clk_ops clk_sun6i_rtc_ops = { + .enable = clk_sun6i_rtc_enable, +}; + +static const struct udevice_id sun6i_rtc_ids[] = { + { .compatible = "allwinner,sun6i-a31-rtc" }, + { .compatible = "allwinner,sun8i-a23-rtc" }, + { .compatible = "allwinner,sun8i-h3-rtc" }, + { .compatible = "allwinner,sun8i-r40-rtc" }, + { .compatible = "allwinner,sun8i-v3-rtc" }, + { .compatible = "allwinner,sun50i-h5-rtc" }, + { .compatible = "allwinner,sun50i-h6-rtc" }, + { } +}; + +U_BOOT_DRIVER(clk_sun6i_rtc) = { + .name = "clk_sun6i_rtc", + .id = UCLASS_CLK, + .of_match = sun6i_rtc_ids, + .ops = &clk_sun6i_rtc_ops, +};

On Sun, 7 Feb 2021 23:57:19 -0600 Samuel Holland samuel@sholland.org wrote:
The 32kHz clock ("LOSC") on sunxi SoCs is provided by the RTC. It is used, among other things, by the XHCI controller in the H6. To be able to call clk_get_bulk() on the XHCI controller, some device needs to provide all referenced clocks.
Since LOSC is a fixed-rate always-on clock, implementation is trivial.
Signed-off-by: Samuel Holland samuel@sholland.org
Reviewed-by: Andre Przywara andre.przywara@arm.com
Queued for sunxi-next.
Cheers, Andre
drivers/clk/sunxi/Makefile | 2 ++ drivers/clk/sunxi/clk_sun6i_rtc.c | 35 +++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 drivers/clk/sunxi/clk_sun6i_rtc.c
diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile index 0dfc0593fb1..4f9282a8b9b 100644 --- a/drivers/clk/sunxi/Makefile +++ b/drivers/clk/sunxi/Makefile @@ -6,6 +6,8 @@
obj-$(CONFIG_CLK_SUNXI) += clk_sunxi.o
+obj-$(CONFIG_CLK_SUNXI) += clk_sun6i_rtc.o
obj-$(CONFIG_CLK_SUN4I_A10) += clk_a10.o obj-$(CONFIG_CLK_SUN5I_A10S) += clk_a10s.o obj-$(CONFIG_CLK_SUN6I_A31) += clk_a31.o diff --git a/drivers/clk/sunxi/clk_sun6i_rtc.c b/drivers/clk/sunxi/clk_sun6i_rtc.c new file mode 100644 index 00000000000..0c280d221ba --- /dev/null +++ b/drivers/clk/sunxi/clk_sun6i_rtc.c @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/*
- Copyright (C) 2018 Amarula Solutions.
- Copyright (C) 2020 Samuel Holland samuel@sholland.org
- */
+#include <clk-uclass.h> +#include <dm.h>
+static int clk_sun6i_rtc_enable(struct clk *clk) +{
- return 0;
+}
+static const struct clk_ops clk_sun6i_rtc_ops = {
- .enable = clk_sun6i_rtc_enable,
+};
+static const struct udevice_id sun6i_rtc_ids[] = {
- { .compatible = "allwinner,sun6i-a31-rtc" },
- { .compatible = "allwinner,sun8i-a23-rtc" },
- { .compatible = "allwinner,sun8i-h3-rtc" },
- { .compatible = "allwinner,sun8i-r40-rtc" },
- { .compatible = "allwinner,sun8i-v3-rtc" },
- { .compatible = "allwinner,sun50i-h5-rtc" },
- { .compatible = "allwinner,sun50i-h6-rtc" },
- { }
+};
+U_BOOT_DRIVER(clk_sun6i_rtc) = {
- .name = "clk_sun6i_rtc",
- .id = UCLASS_CLK,
- .of_match = sun6i_rtc_ids,
- .ops = &clk_sun6i_rtc_ops,
+};

The XHCI controller has its own clock and reset. Add them.
Signed-off-by: Samuel Holland samuel@sholland.org --- drivers/clk/sunxi/clk_h6.c | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/drivers/clk/sunxi/clk_h6.c b/drivers/clk/sunxi/clk_h6.c index ac8656fe895..df93d96b3b0 100644 --- a/drivers/clk/sunxi/clk_h6.c +++ b/drivers/clk/sunxi/clk_h6.c @@ -43,6 +43,7 @@ static struct ccu_clk_gate h6_gates[] = { [CLK_BUS_OHCI0] = GATE(0xa8c, BIT(0)), [CLK_BUS_OHCI3] = GATE(0xa8c, BIT(3)), [CLK_BUS_EHCI0] = GATE(0xa8c, BIT(4)), + [CLK_BUS_XHCI] = GATE(0xa8c, BIT(5)), [CLK_BUS_EHCI3] = GATE(0xa8c, BIT(7)), [CLK_BUS_OTG] = GATE(0xa8c, BIT(8)), }; @@ -71,6 +72,7 @@ static struct ccu_reset h6_resets[] = { [RST_BUS_OHCI0] = RESET(0xa8c, BIT(16)), [RST_BUS_OHCI3] = RESET(0xa8c, BIT(19)), [RST_BUS_EHCI0] = RESET(0xa8c, BIT(20)), + [RST_BUS_XHCI] = RESET(0xa8c, BIT(21)), [RST_BUS_EHCI3] = RESET(0xa8c, BIT(23)), [RST_BUS_OTG] = RESET(0xa8c, BIT(24)), };

On Sun, 7 Feb 2021 23:57:20 -0600 Samuel Holland samuel@sholland.org wrote:
The XHCI controller has its own clock and reset. Add them.
Signed-off-by: Samuel Holland samuel@sholland.org
Checked against the manual: Reviewed-by: Andre Przywara andre.przywara@arm.com
Cheers, Andre
drivers/clk/sunxi/clk_h6.c | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/drivers/clk/sunxi/clk_h6.c b/drivers/clk/sunxi/clk_h6.c index ac8656fe895..df93d96b3b0 100644 --- a/drivers/clk/sunxi/clk_h6.c +++ b/drivers/clk/sunxi/clk_h6.c @@ -43,6 +43,7 @@ static struct ccu_clk_gate h6_gates[] = { [CLK_BUS_OHCI0] = GATE(0xa8c, BIT(0)), [CLK_BUS_OHCI3] = GATE(0xa8c, BIT(3)), [CLK_BUS_EHCI0] = GATE(0xa8c, BIT(4)),
- [CLK_BUS_XHCI] = GATE(0xa8c, BIT(5)), [CLK_BUS_EHCI3] = GATE(0xa8c, BIT(7)), [CLK_BUS_OTG] = GATE(0xa8c, BIT(8)),
}; @@ -71,6 +72,7 @@ static struct ccu_reset h6_resets[] = { [RST_BUS_OHCI0] = RESET(0xa8c, BIT(16)), [RST_BUS_OHCI3] = RESET(0xa8c, BIT(19)), [RST_BUS_EHCI0] = RESET(0xa8c, BIT(20)),
- [RST_BUS_XHCI] = RESET(0xa8c, BIT(21)), [RST_BUS_EHCI3] = RESET(0xa8c, BIT(23)), [RST_BUS_OTG] = RESET(0xa8c, BIT(24)),
};

This driver is needed for XHCI to work on the Allwinner H6 SoC. The driver is copied from Linux v5.10.
Signed-off-by: Samuel Holland samuel@sholland.org --- drivers/phy/allwinner/Kconfig | 8 ++ drivers/phy/allwinner/Makefile | 1 + drivers/phy/allwinner/phy-sun50i-usb3.c | 171 ++++++++++++++++++++++++ 3 files changed, 180 insertions(+) create mode 100644 drivers/phy/allwinner/phy-sun50i-usb3.c
diff --git a/drivers/phy/allwinner/Kconfig b/drivers/phy/allwinner/Kconfig index dba3bae61c4..6bfb79cbcad 100644 --- a/drivers/phy/allwinner/Kconfig +++ b/drivers/phy/allwinner/Kconfig @@ -11,3 +11,11 @@ config PHY_SUN4I_USB
This driver controls the entire USB PHY block, both the USB OTG parts, as well as the 2 regular USB 2 host PHYs. + +config PHY_SUN50I_USB3 + bool "Allwinner sun50i USB3 PHY driver" + depends on ARCH_SUNXI + select PHY + help + Enable this to support the USB3 transceiver that is part of + Allwinner sun50i SoCs. diff --git a/drivers/phy/allwinner/Makefile b/drivers/phy/allwinner/Makefile index e709fca643c..f2b60ce1a64 100644 --- a/drivers/phy/allwinner/Makefile +++ b/drivers/phy/allwinner/Makefile @@ -4,3 +4,4 @@ #
obj-$(CONFIG_PHY_SUN4I_USB) += phy-sun4i-usb.o +obj-$(CONFIG_PHY_SUN50I_USB3) += phy-sun50i-usb3.o diff --git a/drivers/phy/allwinner/phy-sun50i-usb3.c b/drivers/phy/allwinner/phy-sun50i-usb3.c new file mode 100644 index 00000000000..e5a3d2d92ea --- /dev/null +++ b/drivers/phy/allwinner/phy-sun50i-usb3.c @@ -0,0 +1,171 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Allwinner sun50i(H6) USB 3.0 phy driver + * + * Copyright (C) 2020 Samuel Holland samuel@sholland.org + * + * Based on the Linux driver, which is: + * + * Copyright (C) 2017 Icenowy Zheng icenowy@aosc.io + * + * Based on phy-sun9i-usb.c, which is: + * + * Copyright (C) 2014-2015 Chen-Yu Tsai wens@csie.org + * + * Based on code from Allwinner BSP, which is: + * + * Copyright (c) 2010-2015 Allwinner Technology Co., Ltd. + */ + +#include <asm/io.h> +#include <clk.h> +#include <dm.h> +#include <dm/device_compat.h> +#include <generic-phy.h> +#include <linux/bitops.h> +#include <reset.h> + +/* Interface Status and Control Registers */ +#define SUNXI_ISCR 0x00 +#define SUNXI_PIPE_CLOCK_CONTROL 0x14 +#define SUNXI_PHY_TUNE_LOW 0x18 +#define SUNXI_PHY_TUNE_HIGH 0x1c +#define SUNXI_PHY_EXTERNAL_CONTROL 0x20 + +/* USB2.0 Interface Status and Control Register */ +#define SUNXI_ISCR_FORCE_VBUS (3 << 12) + +/* PIPE Clock Control Register */ +#define SUNXI_PCC_PIPE_CLK_OPEN (1 << 6) + +/* PHY External Control Register */ +#define SUNXI_PEC_EXTERN_VBUS (3 << 1) +#define SUNXI_PEC_SSC_EN (1 << 24) +#define SUNXI_PEC_REF_SSP_EN (1 << 26) + +/* PHY Tune High Register */ +#define SUNXI_TX_DEEMPH_3P5DB(n) ((n) << 19) +#define SUNXI_TX_DEEMPH_3P5DB_MASK GENMASK(24, 19) +#define SUNXI_TX_DEEMPH_6DB(n) ((n) << 13) +#define SUNXI_TX_DEEMPH_6GB_MASK GENMASK(18, 13) +#define SUNXI_TX_SWING_FULL(n) ((n) << 6) +#define SUNXI_TX_SWING_FULL_MASK GENMASK(12, 6) +#define SUNXI_LOS_BIAS(n) ((n) << 3) +#define SUNXI_LOS_BIAS_MASK GENMASK(5, 3) +#define SUNXI_TXVBOOSTLVL(n) ((n) << 0) +#define SUNXI_TXVBOOSTLVL_MASK GENMASK(2, 0) + +struct sun50i_usb3_phy_priv { + void __iomem *regs; + struct reset_ctl reset; + struct clk clk; +}; + +static void sun50i_usb3_phy_open(struct sun50i_usb3_phy_priv *phy) +{ + u32 val; + + val = readl(phy->regs + SUNXI_PHY_EXTERNAL_CONTROL); + val |= SUNXI_PEC_EXTERN_VBUS; + val |= SUNXI_PEC_SSC_EN | SUNXI_PEC_REF_SSP_EN; + writel(val, phy->regs + SUNXI_PHY_EXTERNAL_CONTROL); + + val = readl(phy->regs + SUNXI_PIPE_CLOCK_CONTROL); + val |= SUNXI_PCC_PIPE_CLK_OPEN; + writel(val, phy->regs + SUNXI_PIPE_CLOCK_CONTROL); + + val = readl(phy->regs + SUNXI_ISCR); + val |= SUNXI_ISCR_FORCE_VBUS; + writel(val, phy->regs + SUNXI_ISCR); + + /* + * All the magic numbers written to the PHY_TUNE_{LOW_HIGH} + * registers are directly taken from the BSP USB3 driver from + * Allwiner. + */ + writel(0x0047fc87, phy->regs + SUNXI_PHY_TUNE_LOW); + + val = readl(phy->regs + SUNXI_PHY_TUNE_HIGH); + val &= ~(SUNXI_TXVBOOSTLVL_MASK | SUNXI_LOS_BIAS_MASK | + SUNXI_TX_SWING_FULL_MASK | SUNXI_TX_DEEMPH_6GB_MASK | + SUNXI_TX_DEEMPH_3P5DB_MASK); + val |= SUNXI_TXVBOOSTLVL(0x7); + val |= SUNXI_LOS_BIAS(0x7); + val |= SUNXI_TX_SWING_FULL(0x55); + val |= SUNXI_TX_DEEMPH_6DB(0x20); + val |= SUNXI_TX_DEEMPH_3P5DB(0x15); + writel(val, phy->regs + SUNXI_PHY_TUNE_HIGH); +} + +static int sun50i_usb3_phy_init(struct phy *phy) +{ + struct sun50i_usb3_phy_priv *priv = dev_get_priv(phy->dev); + int ret; + + ret = clk_prepare_enable(&priv->clk); + if (ret) + return ret; + + ret = reset_deassert(&priv->reset); + if (ret) { + clk_disable_unprepare(&priv->clk); + return ret; + } + + sun50i_usb3_phy_open(priv); + + return 0; +} + +static int sun50i_usb3_phy_exit(struct phy *phy) +{ + struct sun50i_usb3_phy_priv *priv = dev_get_priv(phy->dev); + + reset_assert(&priv->reset); + clk_disable_unprepare(&priv->clk); + + return 0; +} + +static const struct phy_ops sun50i_usb3_phy_ops = { + .init = sun50i_usb3_phy_init, + .exit = sun50i_usb3_phy_exit, +}; + +static int sun50i_usb3_phy_probe(struct udevice *dev) +{ + struct sun50i_usb3_phy_priv *priv = dev_get_priv(dev); + int ret; + + ret = clk_get_by_index(dev, 0, &priv->clk); + if (ret) { + dev_err(dev, "failed to get phy clock\n"); + return ret; + } + + ret = reset_get_by_index(dev, 0, &priv->reset); + if (ret) { + dev_err(dev, "failed to get reset control\n"); + return ret; + } + + priv->regs = (void __iomem *)dev_read_addr(dev); + if (IS_ERR(priv->regs)) + return PTR_ERR(priv->regs); + + return 0; +} + +static const struct udevice_id sun50i_usb3_phy_ids[] = { + { .compatible = "allwinner,sun50i-h6-usb3-phy" }, + { }, +}; + +U_BOOT_DRIVER(sun50i_usb3_phy) = { + .name = "sun50i-usb3-phy", + .id = UCLASS_PHY, + .of_match = sun50i_usb3_phy_ids, + .ops = &sun50i_usb3_phy_ops, + .probe = sun50i_usb3_phy_probe, + .priv_auto = sizeof(struct sun50i_usb3_phy_priv), +};

On Sun, 7 Feb 2021 23:57:21 -0600 Samuel Holland samuel@sholland.org wrote:
Hi,
This driver is needed for XHCI to work on the Allwinner H6 SoC. The driver is copied from Linux v5.10.
Signed-off-by: Samuel Holland samuel@sholland.org
Compared against the Linux driver, also against the version I made myself a few months ago (in which I spotted two bugs now ;-)
Reviewed-by: Andre Przywara andre.przywara@arm.com
Cheers, Andre
drivers/phy/allwinner/Kconfig | 8 ++ drivers/phy/allwinner/Makefile | 1 + drivers/phy/allwinner/phy-sun50i-usb3.c | 171 ++++++++++++++++++++++++ 3 files changed, 180 insertions(+) create mode 100644 drivers/phy/allwinner/phy-sun50i-usb3.c
diff --git a/drivers/phy/allwinner/Kconfig b/drivers/phy/allwinner/Kconfig index dba3bae61c4..6bfb79cbcad 100644 --- a/drivers/phy/allwinner/Kconfig +++ b/drivers/phy/allwinner/Kconfig @@ -11,3 +11,11 @@ config PHY_SUN4I_USB
This driver controls the entire USB PHY block, both the USB OTG parts, as well as the 2 regular USB 2 host PHYs.
+config PHY_SUN50I_USB3
- bool "Allwinner sun50i USB3 PHY driver"
- depends on ARCH_SUNXI
- select PHY
- help
Enable this to support the USB3 transceiver that is part of
Allwinner sun50i SoCs.
diff --git a/drivers/phy/allwinner/Makefile b/drivers/phy/allwinner/Makefile index e709fca643c..f2b60ce1a64 100644 --- a/drivers/phy/allwinner/Makefile +++ b/drivers/phy/allwinner/Makefile @@ -4,3 +4,4 @@ #
obj-$(CONFIG_PHY_SUN4I_USB) += phy-sun4i-usb.o +obj-$(CONFIG_PHY_SUN50I_USB3) += phy-sun50i-usb3.o diff --git a/drivers/phy/allwinner/phy-sun50i-usb3.c b/drivers/phy/allwinner/phy-sun50i-usb3.c new file mode 100644 index 00000000000..e5a3d2d92ea --- /dev/null +++ b/drivers/phy/allwinner/phy-sun50i-usb3.c @@ -0,0 +1,171 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- Allwinner sun50i(H6) USB 3.0 phy driver
- Copyright (C) 2020 Samuel Holland samuel@sholland.org
- Based on the Linux driver, which is:
- Copyright (C) 2017 Icenowy Zheng icenowy@aosc.io
- Based on phy-sun9i-usb.c, which is:
- Copyright (C) 2014-2015 Chen-Yu Tsai wens@csie.org
- Based on code from Allwinner BSP, which is:
- Copyright (c) 2010-2015 Allwinner Technology Co., Ltd.
- */
+#include <asm/io.h> +#include <clk.h> +#include <dm.h> +#include <dm/device_compat.h> +#include <generic-phy.h> +#include <linux/bitops.h> +#include <reset.h>
+/* Interface Status and Control Registers */ +#define SUNXI_ISCR 0x00 +#define SUNXI_PIPE_CLOCK_CONTROL 0x14 +#define SUNXI_PHY_TUNE_LOW 0x18 +#define SUNXI_PHY_TUNE_HIGH 0x1c +#define SUNXI_PHY_EXTERNAL_CONTROL 0x20
+/* USB2.0 Interface Status and Control Register */ +#define SUNXI_ISCR_FORCE_VBUS (3 << 12)
+/* PIPE Clock Control Register */ +#define SUNXI_PCC_PIPE_CLK_OPEN (1 << 6)
+/* PHY External Control Register */ +#define SUNXI_PEC_EXTERN_VBUS (3 << 1) +#define SUNXI_PEC_SSC_EN (1 << 24) +#define SUNXI_PEC_REF_SSP_EN (1 << 26)
+/* PHY Tune High Register */ +#define SUNXI_TX_DEEMPH_3P5DB(n) ((n) << 19) +#define SUNXI_TX_DEEMPH_3P5DB_MASK GENMASK(24, 19) +#define SUNXI_TX_DEEMPH_6DB(n) ((n) << 13) +#define SUNXI_TX_DEEMPH_6GB_MASK GENMASK(18, 13) +#define SUNXI_TX_SWING_FULL(n) ((n) << 6) +#define SUNXI_TX_SWING_FULL_MASK GENMASK(12, 6) +#define SUNXI_LOS_BIAS(n) ((n) << 3) +#define SUNXI_LOS_BIAS_MASK GENMASK(5, 3) +#define SUNXI_TXVBOOSTLVL(n) ((n) << 0) +#define SUNXI_TXVBOOSTLVL_MASK GENMASK(2, 0)
+struct sun50i_usb3_phy_priv {
- void __iomem *regs;
- struct reset_ctl reset;
- struct clk clk;
+};
+static void sun50i_usb3_phy_open(struct sun50i_usb3_phy_priv *phy) +{
- u32 val;
- val = readl(phy->regs + SUNXI_PHY_EXTERNAL_CONTROL);
- val |= SUNXI_PEC_EXTERN_VBUS;
- val |= SUNXI_PEC_SSC_EN | SUNXI_PEC_REF_SSP_EN;
- writel(val, phy->regs + SUNXI_PHY_EXTERNAL_CONTROL);
- val = readl(phy->regs + SUNXI_PIPE_CLOCK_CONTROL);
- val |= SUNXI_PCC_PIPE_CLK_OPEN;
- writel(val, phy->regs + SUNXI_PIPE_CLOCK_CONTROL);
- val = readl(phy->regs + SUNXI_ISCR);
- val |= SUNXI_ISCR_FORCE_VBUS;
- writel(val, phy->regs + SUNXI_ISCR);
- /*
* All the magic numbers written to the PHY_TUNE_{LOW_HIGH}
* registers are directly taken from the BSP USB3 driver from
* Allwiner.
*/
- writel(0x0047fc87, phy->regs + SUNXI_PHY_TUNE_LOW);
- val = readl(phy->regs + SUNXI_PHY_TUNE_HIGH);
- val &= ~(SUNXI_TXVBOOSTLVL_MASK | SUNXI_LOS_BIAS_MASK |
SUNXI_TX_SWING_FULL_MASK | SUNXI_TX_DEEMPH_6GB_MASK |
SUNXI_TX_DEEMPH_3P5DB_MASK);
- val |= SUNXI_TXVBOOSTLVL(0x7);
- val |= SUNXI_LOS_BIAS(0x7);
- val |= SUNXI_TX_SWING_FULL(0x55);
- val |= SUNXI_TX_DEEMPH_6DB(0x20);
- val |= SUNXI_TX_DEEMPH_3P5DB(0x15);
- writel(val, phy->regs + SUNXI_PHY_TUNE_HIGH);
+}
+static int sun50i_usb3_phy_init(struct phy *phy) +{
- struct sun50i_usb3_phy_priv *priv = dev_get_priv(phy->dev);
- int ret;
- ret = clk_prepare_enable(&priv->clk);
- if (ret)
return ret;
- ret = reset_deassert(&priv->reset);
- if (ret) {
clk_disable_unprepare(&priv->clk);
return ret;
- }
- sun50i_usb3_phy_open(priv);
- return 0;
+}
+static int sun50i_usb3_phy_exit(struct phy *phy) +{
- struct sun50i_usb3_phy_priv *priv = dev_get_priv(phy->dev);
- reset_assert(&priv->reset);
- clk_disable_unprepare(&priv->clk);
- return 0;
+}
+static const struct phy_ops sun50i_usb3_phy_ops = {
- .init = sun50i_usb3_phy_init,
- .exit = sun50i_usb3_phy_exit,
+};
+static int sun50i_usb3_phy_probe(struct udevice *dev) +{
- struct sun50i_usb3_phy_priv *priv = dev_get_priv(dev);
- int ret;
- ret = clk_get_by_index(dev, 0, &priv->clk);
- if (ret) {
dev_err(dev, "failed to get phy clock\n");
return ret;
- }
- ret = reset_get_by_index(dev, 0, &priv->reset);
- if (ret) {
dev_err(dev, "failed to get reset control\n");
return ret;
- }
- priv->regs = (void __iomem *)dev_read_addr(dev);
- if (IS_ERR(priv->regs))
return PTR_ERR(priv->regs);
- return 0;
+}
+static const struct udevice_id sun50i_usb3_phy_ids[] = {
- { .compatible = "allwinner,sun50i-h6-usb3-phy" },
- { },
+};
+U_BOOT_DRIVER(sun50i_usb3_phy) = {
- .name = "sun50i-usb3-phy",
- .id = UCLASS_PHY,
- .of_match = sun50i_usb3_phy_ids,
- .ops = &sun50i_usb3_phy_ops,
- .probe = sun50i_usb3_phy_probe,
- .priv_auto = sizeof(struct sun50i_usb3_phy_priv),
+};

Resetting an XHCI controller inside xhci_register undoes any register setup performed by the platform driver. And at least on the Allwinner H6, resetting the XHCI controller also resets the PHY, which prevents the controller from working. That means the controller must be taken out of reset before initializing the PHY, which must be done before calling xhci_register.
The logic in the XHCI core was added to support the Raspberry Pi 4 (although this was not mentioned in the commit log!), which uses the xhci-pci platform driver. Move the reset logic to the platform driver, where it belongs, and where it cannot interfere with other platform drivers.
Fixes: 0b80371b350e ("usb: xhci: Add reset controller support") Signed-off-by: Samuel Holland samuel@sholland.org --- drivers/usb/host/xhci-mem.c | 2 -- drivers/usb/host/xhci-pci.c | 38 +++++++++++++++++++++++++++++++++++-- drivers/usb/host/xhci.c | 35 ---------------------------------- include/usb/xhci.h | 2 -- 4 files changed, 36 insertions(+), 41 deletions(-)
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index b002d6f1664..ccec64ff564 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -180,8 +180,6 @@ void xhci_cleanup(struct xhci_ctrl *ctrl) xhci_free_virt_devices(ctrl); free(ctrl->erst.entries); free(ctrl->dcbaa); - if (reset_valid(&ctrl->reset)) - reset_free(&ctrl->reset); memset(ctrl, '\0', sizeof(struct xhci_ctrl)); }
diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index 2b445f21b55..2459b4b62f0 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -10,9 +10,14 @@ #include <init.h> #include <log.h> #include <pci.h> +#include <reset.h> #include <usb.h> #include <usb/xhci.h>
+struct xhci_pci_plat { + struct reset_ctl reset; +}; + static void xhci_pci_init(struct udevice *dev, struct xhci_hccr **ret_hccr, struct xhci_hcor **ret_hcor) { @@ -39,14 +44,43 @@ static void xhci_pci_init(struct udevice *dev, struct xhci_hccr **ret_hccr,
static int xhci_pci_probe(struct udevice *dev) { + struct xhci_pci_plat = dev_get_plat(dev); struct xhci_hccr *hccr; struct xhci_hcor *hcor; + int ret; + + ret = reset_get_by_index(dev, 0, &plat->reset); + if (ret && ret != -ENOENT && ret != -ENOTSUPP) { + dev_err(dev, "failed to get reset\n"); + return ret; + } + + if (reset_valid(&plat->reset)) { + ret = reset_assert(&plat->reset); + if (ret) + return ret; + + ret = reset_deassert(&plat->reset); + if (ret) + return ret; + }
xhci_pci_init(dev, &hccr, &hcor);
return xhci_register(dev, hccr, hcor); }
+static int xhci_pci_remove(struct udevice *dev) +{ + struct xhci_pci_plat = dev_get_plat(dev); + + xhci_deregister(dev); + if (reset_valid(&plat->reset)) + reset_free(&plat->reset); + + return 0; +} + static const struct udevice_id xhci_pci_ids[] = { { .compatible = "xhci-pci" }, { } @@ -56,10 +90,10 @@ U_BOOT_DRIVER(xhci_pci) = { .name = "xhci_pci", .id = UCLASS_USB, .probe = xhci_pci_probe, - .remove = xhci_deregister, + .remove = xhci_pci_remove, .of_match = xhci_pci_ids, .ops = &xhci_usb_ops, - .plat_auto = sizeof(struct usb_plat), + .plat_auto = sizeof(struct xhci_pci_plat), .priv_auto = sizeof(struct xhci_ctrl), .flags = DM_FLAG_ALLOC_PRIV_DMA, }; diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 7080f8fabe7..171f39f90b5 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -188,37 +188,6 @@ static int xhci_start(struct xhci_hcor *hcor) return ret; }
-#if CONFIG_IS_ENABLED(DM_USB) -/** - * Resets XHCI Hardware - * - * @param ctrl pointer to host controller - * @return 0 if OK, or a negative error code. - */ -static int xhci_reset_hw(struct xhci_ctrl *ctrl) -{ - int ret; - - ret = reset_get_by_index(ctrl->dev, 0, &ctrl->reset); - if (ret && ret != -ENOENT && ret != -ENOTSUPP) { - dev_err(ctrl->dev, "failed to get reset\n"); - return ret; - } - - if (reset_valid(&ctrl->reset)) { - ret = reset_assert(&ctrl->reset); - if (ret) - return ret; - - ret = reset_deassert(&ctrl->reset); - if (ret) - return ret; - } - - return 0; -} -#endif - /** * Resets the XHCI Controller * @@ -1534,10 +1503,6 @@ int xhci_register(struct udevice *dev, struct xhci_hccr *hccr,
ctrl->dev = dev;
- ret = xhci_reset_hw(ctrl); - if (ret) - goto err; - /* * XHCI needs to issue a Address device command to setup * proper device context structures, before it can interact diff --git a/include/usb/xhci.h b/include/usb/xhci.h index e1d382369a3..c12706c6a58 100644 --- a/include/usb/xhci.h +++ b/include/usb/xhci.h @@ -16,7 +16,6 @@ #ifndef HOST_XHCI_H_ #define HOST_XHCI_H_
-#include <reset.h> #include <asm/types.h> #include <asm/cache.h> #include <asm/io.h> @@ -1199,7 +1198,6 @@ struct xhci_ctrl { #if CONFIG_IS_ENABLED(DM_USB) struct udevice *dev; #endif - struct reset_ctl reset; struct xhci_hccr *hccr; /* R/O registers, not need for volatile */ struct xhci_hcor *hcor; struct xhci_doorbell_array *dba;

On 2/8/21 6:57 AM, Samuel Holland wrote:
Resetting an XHCI controller inside xhci_register undoes any register setup performed by the platform driver. And at least on the Allwinner H6, resetting the XHCI controller also resets the PHY, which prevents the controller from working. That means the controller must be taken out of reset before initializing the PHY, which must be done before calling xhci_register.
The logic in the XHCI core was added to support the Raspberry Pi 4 (although this was not mentioned in the commit log!), which uses the xhci-pci platform driver. Move the reset logic to the platform driver, where it belongs, and where it cannot interfere with other platform drivers.
Are there any other XHCI drivers using the XHCI core code which might stop resetting correctly due to this patch ?

On Mon, 8 Feb 2021 12:43:52 +0100 Marek Vasut marex@denx.de wrote:
(CC:ing Masahiro, Manni and Kever)
On 2/8/21 6:57 AM, Samuel Holland wrote:
Resetting an XHCI controller inside xhci_register undoes any register setup performed by the platform driver. And at least on the Allwinner H6, resetting the XHCI controller also resets the PHY, which prevents the controller from working. That means the controller must be taken out of reset before initializing the PHY, which must be done before calling xhci_register.
The logic in the XHCI core was added to support the Raspberry Pi 4 (although this was not mentioned in the commit log!), which uses the xhci-pci platform driver. Move the reset logic to the platform driver, where it belongs, and where it cannot interfere with other platform drivers.
Are there any other XHCI drivers using the XHCI core code which might stop resetting correctly due to this patch ?
That's a fair point. I grep'ed all .dts[i] files in both the kernel and U-Boot for snps,dwc3 users, and there various Uniphier .dtsi files that have resets properties, also the Hisilicon 3660 (HiKey960). The HiKey doesn't seem to enable DWC3 in its defconfig, and from what I can see the Uniphier boards have hardcoded reset deasserts in their glue driver? Masahiro, Manni, Kever, can you confirm that your boards still work, after this whole series? I put a branch here for testing: https://gitlab.denx.de/u-boot/custodians/u-boot-sunxi.git test-usb3-h6
As an added bonus, it would be interesting to see if USB 3.0 still works just after this patch. If not, we might need to squash the next patch into this one.
Many thanks! Andre

On 2/8/21 5:43 AM, Marek Vasut wrote:
On 2/8/21 6:57 AM, Samuel Holland wrote:
Resetting an XHCI controller inside xhci_register undoes any register setup performed by the platform driver. And at least on the Allwinner H6, resetting the XHCI controller also resets the PHY, which prevents the controller from working. That means the controller must be taken out of reset before initializing the PHY, which must be done before calling xhci_register.
The logic in the XHCI core was added to support the Raspberry Pi 4 (although this was not mentioned in the commit log!), which uses the xhci-pci platform driver. Move the reset logic to the platform driver, where it belongs, and where it cannot interfere with other platform drivers.
Are there any other XHCI drivers using the XHCI core code which might stop resetting correctly due to this patch ?
Since commit 0b80371b350e was merged, the only new call to xhci_register is in USB_MTU3. Neither the binding for "mediatek,mtu3" nor any device tree node containing that compatible string has a "resets" property. And there have been no calls to reset_(de)assert since then, either. So I do not see any other drivers that would be likely to break.
Samuel

On 2/8/21 8:27 PM, Samuel Holland wrote:
On 2/8/21 5:43 AM, Marek Vasut wrote:
On 2/8/21 6:57 AM, Samuel Holland wrote:
Resetting an XHCI controller inside xhci_register undoes any register setup performed by the platform driver. And at least on the Allwinner H6, resetting the XHCI controller also resets the PHY, which prevents the controller from working. That means the controller must be taken out of reset before initializing the PHY, which must be done before calling xhci_register.
The logic in the XHCI core was added to support the Raspberry Pi 4 (although this was not mentioned in the commit log!), which uses the xhci-pci platform driver. Move the reset logic to the platform driver, where it belongs, and where it cannot interfere with other platform drivers.
Are there any other XHCI drivers using the XHCI core code which might stop resetting correctly due to this patch ?
Since commit 0b80371b350e was merged, the only new call to xhci_register is in USB_MTU3. Neither the binding for "mediatek,mtu3" nor any device tree node containing that compatible string has a "resets" property. And there have been no calls to reset_(de)assert since then, either. So I do
...no calls to reset_(de)assert *removed from any driver* since then...
not see any other drivers that would be likely to break.
Samuel

On 2/9/21 3:28 AM, Samuel Holland wrote:
On 2/8/21 8:27 PM, Samuel Holland wrote:
On 2/8/21 5:43 AM, Marek Vasut wrote:
On 2/8/21 6:57 AM, Samuel Holland wrote:
Resetting an XHCI controller inside xhci_register undoes any register setup performed by the platform driver. And at least on the Allwinner H6, resetting the XHCI controller also resets the PHY, which prevents the controller from working. That means the controller must be taken out of reset before initializing the PHY, which must be done before calling xhci_register.
The logic in the XHCI core was added to support the Raspberry Pi 4 (although this was not mentioned in the commit log!), which uses the xhci-pci platform driver. Move the reset logic to the platform driver, where it belongs, and where it cannot interfere with other platform drivers.
Are there any other XHCI drivers using the XHCI core code which might stop resetting correctly due to this patch ?
Since commit 0b80371b350e was merged, the only new call to xhci_register is in USB_MTU3. Neither the binding for "mediatek,mtu3" nor any device tree node containing that compatible string has a "resets" property. And there have been no calls to reset_(de)assert since then, either. So I do
...no calls to reset_(de)assert *removed from any driver* since then...
not see any other drivers that would be likely to break.
All right, thanks for checking.
I would still prefer the reset logic to stay in core if that's possible, and ideally fix the other drivers to use that reset logic instead of implementing their own thing. If that's not possible, get a RB from Bin, since that's the expert on xhci, and then lets merge this stuff.

Some platforms, like the Allwinner H6, do not have a separate glue layer around the dwc3. Instead, they rely on the clocks/resets/phys referenced from the dwc3 DT node itself. Add support for enabling the clocks/resets referenced from the dwc3 DT node.
Signed-off-by: Samuel Holland samuel@sholland.org --- drivers/usb/host/xhci-dwc3.c | 56 ++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+)
diff --git a/drivers/usb/host/xhci-dwc3.c b/drivers/usb/host/xhci-dwc3.c index 3e0ae80cece..5b12d1358e4 100644 --- a/drivers/usb/host/xhci-dwc3.c +++ b/drivers/usb/host/xhci-dwc3.c @@ -7,10 +7,12 @@ * Author: Ramneek Mehreshramneek.mehresh@freescale.com */
+#include <clk.h> #include <common.h> #include <dm.h> #include <generic-phy.h> #include <log.h> +#include <reset.h> #include <usb.h> #include <dwc3-uboot.h> #include <linux/delay.h> @@ -21,7 +23,9 @@ #include <linux/usb/otg.h>
struct xhci_dwc3_plat { + struct clk_bulk clks; struct phy_bulk phys; + struct reset_ctl_bulk resets; };
void dwc3_set_mode(struct dwc3 *dwc3_reg, u32 mode) @@ -111,6 +115,46 @@ void dwc3_set_fladj(struct dwc3 *dwc3_reg, u32 val) }
#if CONFIG_IS_ENABLED(DM_USB) +static int xhci_dwc3_reset_init(struct udevice *dev, + struct xhci_dwc3_plat *plat) +{ + int ret; + + ret = reset_get_bulk(dev, &plat->resets); + if (ret == -ENOTSUPP || ret == -ENOENT) + return 0; + else if (ret) + return ret; + + ret = reset_deassert_bulk(&plat->resets); + if (ret) { + reset_release_bulk(&plat->resets); + return ret; + } + + return 0; +} + +static int xhci_dwc3_clk_init(struct udevice *dev, + struct xhci_dwc3_plat *plat) +{ + int ret; + + ret = clk_get_bulk(dev, &plat->clks); + if (ret == -ENOSYS || ret == -ENOENT) + return 0; + if (ret) + return ret; + + ret = clk_enable_bulk(&plat->clks); + if (ret) { + clk_release_bulk(&plat->clks); + return ret; + } + + return 0; +} + static int xhci_dwc3_probe(struct udevice *dev) { struct xhci_hcor *hcor; @@ -122,6 +166,14 @@ static int xhci_dwc3_probe(struct udevice *dev) u32 reg; int ret;
+ ret = xhci_dwc3_reset_init(dev, plat); + if (ret) + return ret; + + ret = xhci_dwc3_clk_init(dev, plat); + if (ret) + return ret; + hccr = (struct xhci_hccr *)((uintptr_t)dev_remap_addr(dev)); hcor = (struct xhci_hcor *)((uintptr_t)hccr + HC_LENGTH(xhci_readl(&(hccr)->cr_capbase))); @@ -171,6 +223,10 @@ static int xhci_dwc3_remove(struct udevice *dev)
dwc3_shutdown_phy(dev, &plat->phys);
+ clk_release_bulk(&plat->clks); + + reset_release_bulk(&plat->resets); + return xhci_deregister(dev); }

On Sun, 7 Feb 2021 23:57:23 -0600 Samuel Holland samuel@sholland.org wrote:
(CC:ing Simon and Kever)
Some platforms, like the Allwinner H6, do not have a separate glue layer around the dwc3. Instead, they rely on the clocks/resets/phys referenced from the dwc3 DT node itself. Add support for enabling the clocks/resets referenced from the dwc3 DT node.
Signed-off-by: Samuel Holland samuel@sholland.org
That looks alright on a first glance, but have you checked how this interacts with and relates to dwc3-of-simple.c? This seems to do similar things?
Cheers, Andre
drivers/usb/host/xhci-dwc3.c | 56 ++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+)
diff --git a/drivers/usb/host/xhci-dwc3.c b/drivers/usb/host/xhci-dwc3.c index 3e0ae80cece..5b12d1358e4 100644 --- a/drivers/usb/host/xhci-dwc3.c +++ b/drivers/usb/host/xhci-dwc3.c @@ -7,10 +7,12 @@
- Author: Ramneek Mehreshramneek.mehresh@freescale.com
*/
+#include <clk.h> #include <common.h> #include <dm.h> #include <generic-phy.h> #include <log.h> +#include <reset.h> #include <usb.h> #include <dwc3-uboot.h> #include <linux/delay.h> @@ -21,7 +23,9 @@ #include <linux/usb/otg.h>
struct xhci_dwc3_plat {
- struct clk_bulk clks; struct phy_bulk phys;
- struct reset_ctl_bulk resets;
};
void dwc3_set_mode(struct dwc3 *dwc3_reg, u32 mode) @@ -111,6 +115,46 @@ void dwc3_set_fladj(struct dwc3 *dwc3_reg, u32 val) }
#if CONFIG_IS_ENABLED(DM_USB) +static int xhci_dwc3_reset_init(struct udevice *dev,
struct xhci_dwc3_plat *plat)
+{
- int ret;
- ret = reset_get_bulk(dev, &plat->resets);
- if (ret == -ENOTSUPP || ret == -ENOENT)
return 0;
- else if (ret)
return ret;
- ret = reset_deassert_bulk(&plat->resets);
- if (ret) {
reset_release_bulk(&plat->resets);
return ret;
- }
- return 0;
+}
+static int xhci_dwc3_clk_init(struct udevice *dev,
struct xhci_dwc3_plat *plat)
+{
- int ret;
- ret = clk_get_bulk(dev, &plat->clks);
- if (ret == -ENOSYS || ret == -ENOENT)
return 0;
- if (ret)
return ret;
- ret = clk_enable_bulk(&plat->clks);
- if (ret) {
clk_release_bulk(&plat->clks);
return ret;
- }
- return 0;
+}
static int xhci_dwc3_probe(struct udevice *dev) { struct xhci_hcor *hcor; @@ -122,6 +166,14 @@ static int xhci_dwc3_probe(struct udevice *dev) u32 reg; int ret;
- ret = xhci_dwc3_reset_init(dev, plat);
- if (ret)
return ret;
- ret = xhci_dwc3_clk_init(dev, plat);
- if (ret)
return ret;
- hccr = (struct xhci_hccr *)((uintptr_t)dev_remap_addr(dev)); hcor = (struct xhci_hcor *)((uintptr_t)hccr + HC_LENGTH(xhci_readl(&(hccr)->cr_capbase)));
@@ -171,6 +223,10 @@ static int xhci_dwc3_remove(struct udevice *dev)
dwc3_shutdown_phy(dev, &plat->phys);
- clk_release_bulk(&plat->clks);
- reset_release_bulk(&plat->resets);
- return xhci_deregister(dev);
}

On 2/8/21 7:42 PM, Andre Przywara wrote:
On Sun, 7 Feb 2021 23:57:23 -0600 Samuel Holland samuel@sholland.org wrote:
(CC:ing Simon and Kever)
Some platforms, like the Allwinner H6, do not have a separate glue layer around the dwc3. Instead, they rely on the clocks/resets/phys referenced from the dwc3 DT node itself. Add support for enabling the clocks/resets referenced from the dwc3 DT node.
Signed-off-by: Samuel Holland samuel@sholland.org
That looks alright on a first glance, but have you checked how this interacts with and relates to dwc3-of-simple.c? This seems to do similar things?
Yes, they do the same thing, but with a different DT binding. dwc3-of-simple binds to, and expects the clocks/resets to be in, the DT node _containing_ the snps,dwc3 node. The xhci-dwc3 driver binds to the snps,dwc3 node itself. This is why other platforms were not affected by the change
I don't think there's any way to attach two drivers to the same DT node at the same time, so we cannot take advantage of the dwc3-of-simple code without rearranging the device tree.
Interestingly, Linux has supported the H6 in its version of dwc3-of-simple since commit e362098f0e12 ("usb: dwc3: of-simple: Add compatible for Allwinner H6 platform"). But the device tree ended up not using a glue layer node or this compatible.
Maybe a better forward is to modify the device tree to be more similar to most other platforms? On the other hand, as you found, Hisilicon and Unipher platforms use the same binding as Allwinner. It's unfortunate that there are two distinct but equally "correct" ways to represent the same hardware.
Cheers, Samuel

Pine H64 and Orange Pi 3 both provide a USB3 type A port. Enable it in U-Boot.
Signed-off-by: Samuel Holland samuel@sholland.org --- configs/orangepi_3_defconfig | 5 +++++ configs/pine_h64_defconfig | 5 +++++ 2 files changed, 10 insertions(+)
diff --git a/configs/orangepi_3_defconfig b/configs/orangepi_3_defconfig index 82b9815205e..eb25bd9f504 100644 --- a/configs/orangepi_3_defconfig +++ b/configs/orangepi_3_defconfig @@ -8,5 +8,10 @@ CONFIG_MMC_SUNXI_SLOT_EXTRA=2 CONFIG_BLUETOOTH_DT_DEVICE_FIXUP="brcm,bcm4345c5" CONFIG_DEFAULT_DEVICE_TREE="sun50i-h6-orangepi-3" # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set +CONFIG_PHY_SUN50I_USB3=y +CONFIG_USB_XHCI_HCD=y +CONFIG_USB_XHCI_DWC3=y CONFIG_USB_EHCI_HCD=y CONFIG_USB_OHCI_HCD=y +CONFIG_USB_DWC3=y +# CONFIG_USB_DWC3_GADGET is not set diff --git a/configs/pine_h64_defconfig b/configs/pine_h64_defconfig index 2fa66f38343..0095fb222e6 100644 --- a/configs/pine_h64_defconfig +++ b/configs/pine_h64_defconfig @@ -12,5 +12,10 @@ CONFIG_SPL_SPI_SUNXI=y CONFIG_DEFAULT_DEVICE_TREE="sun50i-h6-pine-h64" # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set CONFIG_SUN8I_EMAC=y +CONFIG_PHY_SUN50I_USB3=y +CONFIG_USB_XHCI_HCD=y +CONFIG_USB_XHCI_DWC3=y CONFIG_USB_EHCI_HCD=y CONFIG_USB_OHCI_HCD=y +CONFIG_USB_DWC3=y +# CONFIG_USB_DWC3_GADGET is not set

On Sun, 7 Feb 2021 23:57:24 -0600 Samuel Holland samuel@sholland.org wrote:
Hi,
(CC:ing Maxime and Chen-Yu)
Pine H64 and Orange Pi 3 both provide a USB3 type A port. Enable it in U-Boot.
Signed-off-by: Samuel Holland samuel@sholland.org
That looks good to me, but the Pine H64 does not enable the USB 3.0 controller in its .dts file. I just added status = "okay"; to the dwc3 and PHY nodes and it worked, though.
Does anyone know a reason this is not enabled in the mainline DT (which we just synced)?
Cheers, Andre
configs/orangepi_3_defconfig | 5 +++++ configs/pine_h64_defconfig | 5 +++++ 2 files changed, 10 insertions(+)
diff --git a/configs/orangepi_3_defconfig b/configs/orangepi_3_defconfig index 82b9815205e..eb25bd9f504 100644 --- a/configs/orangepi_3_defconfig +++ b/configs/orangepi_3_defconfig @@ -8,5 +8,10 @@ CONFIG_MMC_SUNXI_SLOT_EXTRA=2 CONFIG_BLUETOOTH_DT_DEVICE_FIXUP="brcm,bcm4345c5" CONFIG_DEFAULT_DEVICE_TREE="sun50i-h6-orangepi-3" # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set +CONFIG_PHY_SUN50I_USB3=y +CONFIG_USB_XHCI_HCD=y +CONFIG_USB_XHCI_DWC3=y CONFIG_USB_EHCI_HCD=y CONFIG_USB_OHCI_HCD=y +CONFIG_USB_DWC3=y +# CONFIG_USB_DWC3_GADGET is not set diff --git a/configs/pine_h64_defconfig b/configs/pine_h64_defconfig index 2fa66f38343..0095fb222e6 100644 --- a/configs/pine_h64_defconfig +++ b/configs/pine_h64_defconfig @@ -12,5 +12,10 @@ CONFIG_SPL_SPI_SUNXI=y CONFIG_DEFAULT_DEVICE_TREE="sun50i-h6-pine-h64" # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set CONFIG_SUN8I_EMAC=y +CONFIG_PHY_SUN50I_USB3=y +CONFIG_USB_XHCI_HCD=y +CONFIG_USB_XHCI_DWC3=y CONFIG_USB_EHCI_HCD=y CONFIG_USB_OHCI_HCD=y +CONFIG_USB_DWC3=y +# CONFIG_USB_DWC3_GADGET is not set

On Sun, 7 Feb 2021 23:57:18 -0600 Samuel Holland samuel@sholland.org wrote:
Hi Samuel,
This series adds clock, PHY, and XHCI driver support for the USB3 controller found in the Allwinner H6 SoC. Below is a log showing it functioning on the Orange Pi 3.
Many thanks for sharing this! The lack of USB 3.0 support annoyed me for a while (not so much for SuperSpeed, but more for another usable USB port). I had some patches myself, but stopped with some hacks after I realised the resets properties blunder. So many thanks for finding a much better solution for this! There might be still some discussion around this, but at least we have something in a working and non-hackish state.
I commented on the individual patches!
Cheers, Andre
U-Boot 2021.04-rc1-00068-g6b8f4e0d060 (Jan 01 1970 - 00:00:00 +0000) Allwinner Technology
CPU: Allwinner H6 (SUN50I) Model: OrangePi 3 DRAM: 2 GiB MMC: mmc@4020000: 0, mmc@4021000: 2, mmc@4022000: 1 Loading Environment from FAT... Unable to use mmc 1:0... In: serial@5000000 Out: serial@5000000 Err: serial@5000000 Net: No ethernet found. starting USB... Bus usb@5101000: USB EHCI 1.00 Bus usb@5101400: USB OHCI 1.0 Bus usb@5200000: Register 2000140 NbrPorts 2 Starting the controller USB XHCI 1.00 Bus usb@5311000: USB EHCI 1.00 Bus usb@5311400: USB OHCI 1.0 scanning bus usb@5101000 for devices... 1 USB Device(s) found scanning bus usb@5101400 for devices... 1 USB Device(s) found scanning bus usb@5200000 for devices... 4 USB Device(s) found scanning bus usb@5311000 for devices... 1 USB Device(s) found scanning bus usb@5311400 for devices... 1 USB Device(s) found scanning usb for storage devices... 1 Storage Device(s) found Hit any key to stop autoboot: 0 => usb tree USB device tree: 1 Hub (480 Mb/s, 0mA) u-boot EHCI Host Controller
1 Hub (12 Mb/s, 0mA) U-Boot Root Hub
1 Hub (5 Gb/s, 0mA) | U-Boot XHCI Host Controller | +-2 Hub (5 Gb/s, 0mA) | | GenesysLogic USB3.1 Hub | | | +-4 Mass Storage (5 Gb/s, 224mA) | 071044991D8D6A81 | +-3 Hub (480 Mb/s, 100mA) GenesysLogic USB2.1 Hub
1 Hub (480 Mb/s, 0mA) u-boot EHCI Host Controller
1 Hub (12 Mb/s, 0mA) U-Boot Root Hub
Samuel Holland (6): clk: sunxi: Add a dummy clock driver for the RTC clk: sunxi: h6: Add XHCI clocks phy: sun50i-usb3: Add a driver for the H6 USB3 PHY usb: xhci-pci: Move reset logic out of XHCI core usb: xhci-dwc3: Add support for clocks/resets configs: Enable USB3 on Allwinner H6 boards
configs/orangepi_3_defconfig | 5 + configs/pine_h64_defconfig | 5 + drivers/clk/sunxi/Makefile | 2 + drivers/clk/sunxi/clk_h6.c | 2 + drivers/clk/sunxi/clk_sun6i_rtc.c | 35 +++++ drivers/phy/allwinner/Kconfig | 8 ++ drivers/phy/allwinner/Makefile | 1 + drivers/phy/allwinner/phy-sun50i-usb3.c | 171 ++++++++++++++++++++++++ drivers/usb/host/xhci-dwc3.c | 56 ++++++++ drivers/usb/host/xhci-mem.c | 2 - drivers/usb/host/xhci-pci.c | 38 +++++- drivers/usb/host/xhci.c | 35 ----- include/usb/xhci.h | 2 - 13 files changed, 321 insertions(+), 41 deletions(-) create mode 100644 drivers/clk/sunxi/clk_sun6i_rtc.c create mode 100644 drivers/phy/allwinner/phy-sun50i-usb3.c

Hi Samuel,
On 08/02/2021 06:57, Samuel Holland wrote:
This series adds clock, PHY, and XHCI driver support for the USB3 controller found in the Allwinner H6 SoC. Below is a log showing it functioning on the Orange Pi 3.
Cheers, Samuel
U-Boot 2021.04-rc1-00068-g6b8f4e0d060 (Jan 01 1970 - 00:00:00 +0000) Allwinner Technology
CPU: Allwinner H6 (SUN50I) Model: OrangePi 3 DRAM: 2 GiB MMC: mmc@4020000: 0, mmc@4021000: 2, mmc@4022000: 1 Loading Environment from FAT... Unable to use mmc 1:0... In: serial@5000000 Out: serial@5000000 Err: serial@5000000 Net: No ethernet found. starting USB... Bus usb@5101000: USB EHCI 1.00 Bus usb@5101400: USB OHCI 1.0 Bus usb@5200000: Register 2000140 NbrPorts 2 Starting the controller USB XHCI 1.00 Bus usb@5311000: USB EHCI 1.00 Bus usb@5311400: USB OHCI 1.0 scanning bus usb@5101000 for devices... 1 USB Device(s) found scanning bus usb@5101400 for devices... 1 USB Device(s) found scanning bus usb@5200000 for devices... 4 USB Device(s) found scanning bus usb@5311000 for devices... 1 USB Device(s) found scanning bus usb@5311400 for devices... 1 USB Device(s) found scanning usb for storage devices... 1 Storage Device(s) found Hit any key to stop autoboot: 0
on my OrangePi3 I get this upon power up (with an usb3 flash drive as well as a SD card reader):
starting USB... Bus usb@5101000: USB EHCI 1.00 Bus usb@5101400: USB OHCI 1.0 Bus usb@5200000: Register 2000140 NbrPorts 2 Starting the controller USB XHCI 1.00 Bus usb@5311000: USB EHCI 1.00 Bus usb@5311400: USB OHCI 1.0 scanning bus usb@5101000 for devices... 1 USB Device(s) found scanning bus usb@5101400 for devices... 1 USB Device(s) found scanning bus usb@5200000 for devices... cannot reset port 2!? Device NOT ready Request Sense returned 02 3A 00 4 USB Device(s) found scanning bus usb@5311000 for devices... 1 USB Device(s) found scanning bus usb@5311400 for devices... 1 USB Device(s) found scanning usb for storage devices... 1 Storage Device(s) found Hit any key to stop autoboot: 0
A second retry makes the usb@5200000 pass though.
May be related, but sometimes it can then boot off the usb device, but sometimes I get this:
=> setenv boot_targets usb0 => boot
Device 0: Vendor: Generic- Rev: 1.00 Prod: USB3.0 CRW -SD Type: Removable Hard Disk Capacity: 1876.0 MB = 1.8 GB (3842048 x 512) ... is now current device Scanning usb 0:1... WARN halted endpoint, queueing URB anyway. Unexpected XHCI event TRB, skipping... (bbf55f10 00000000 13000000 03008401) BUG at drivers/usb/host/xhci-ring.c:500/abort_td()! BUG! resetting ...
And sometimes it get further but then fails shortly after:
=> setenv boot_targets usb0 => boot
Device 0: Vendor: Generic- Rev: 1.00 Prod: USB3.0 CRW -SD Type: Removable Hard Disk Capacity: 1876.0 MB = 1.8 GB (3842048 x 512) ... is now current device Scanning usb 0:1... Found /extlinux/extlinux.conf Retrieving file: /extlinux/extlinux.conf 206 bytes read in 4 ms (49.8 KiB/s) 1: LibreELEC Retrieving file: /KERNEL 23058440 bytes read in 1135 ms (19.4 MiB/s) append: boot=UUID=1111-5929 disk=UUID=bb4dea68-a794-4306-ac3e-c6d585ec2a6b console=ttyS0,115200 console=tty1 systemd.debug_shell=ttyS0 quiet Retrieving file: /sun50i-h6-orangepi-3.dtb WARN halted endpoint, queueing URB anyway. Unexpected XHCI event TRB, skipping... (bbf55f10 00000000 13000000 03008400) BUG at drivers/usb/host/xhci-ring.c:500/abort_td()! BUG! resetting ...

On 2/9/21 4:46 AM, Andre Heider wrote:
Hi Samuel,
On 08/02/2021 06:57, Samuel Holland wrote:
This series adds clock, PHY, and XHCI driver support for the USB3 controller found in the Allwinner H6 SoC. Below is a log showing it functioning on the Orange Pi 3.
Cheers, Samuel
U-Boot 2021.04-rc1-00068-g6b8f4e0d060 (Jan 01 1970 - 00:00:00 +0000) Allwinner Technology
CPU: Allwinner H6 (SUN50I) Model: OrangePi 3 DRAM: 2 GiB MMC: mmc@4020000: 0, mmc@4021000: 2, mmc@4022000: 1 Loading Environment from FAT... Unable to use mmc 1:0... In: serial@5000000 Out: serial@5000000 Err: serial@5000000 Net: No ethernet found. starting USB... Bus usb@5101000: USB EHCI 1.00 Bus usb@5101400: USB OHCI 1.0 Bus usb@5200000: Register 2000140 NbrPorts 2 Starting the controller USB XHCI 1.00 Bus usb@5311000: USB EHCI 1.00 Bus usb@5311400: USB OHCI 1.0 scanning bus usb@5101000 for devices... 1 USB Device(s) found scanning bus usb@5101400 for devices... 1 USB Device(s) found scanning bus usb@5200000 for devices... 4 USB Device(s) found scanning bus usb@5311000 for devices... 1 USB Device(s) found scanning bus usb@5311400 for devices... 1 USB Device(s) found scanning usb for storage devices... 1 Storage Device(s) found Hit any key to stop autoboot: 0
on my OrangePi3 I get this upon power up (with an usb3 flash drive as well as a SD card reader):
starting USB... Bus usb@5101000: USB EHCI 1.00 Bus usb@5101400: USB OHCI 1.0 Bus usb@5200000: Register 2000140 NbrPorts 2 Starting the controller USB XHCI 1.00 Bus usb@5311000: USB EHCI 1.00 Bus usb@5311400: USB OHCI 1.0 scanning bus usb@5101000 for devices... 1 USB Device(s) found scanning bus usb@5101400 for devices... 1 USB Device(s) found scanning bus usb@5200000 for devices... cannot reset port 2!? Device NOT ready Request Sense returned 02 3A 00 4 USB Device(s) found scanning bus usb@5311000 for devices... 1 USB Device(s) found scanning bus usb@5311400 for devices... 1 USB Device(s) found scanning usb for storage devices... 1 Storage Device(s) found Hit any key to stop autoboot: 0
A second retry makes the usb@5200000 pass though.
May be related, but sometimes it can then boot off the usb device, but sometimes I get this:
=> setenv boot_targets usb0 => boot
Device 0: Vendor: Generic- Rev: 1.00 Prod: USB3.0 CRW -SD Type: Removable Hard Disk Capacity: 1876.0 MB = 1.8 GB (3842048 x 512) ... is now current device Scanning usb 0:1... WARN halted endpoint, queueing URB anyway. Unexpected XHCI event TRB, skipping... (bbf55f10 00000000 13000000 03008401) BUG at drivers/usb/host/xhci-ring.c:500/abort_td()! BUG! resetting ...
And sometimes it get further but then fails shortly after:
=> setenv boot_targets usb0 => boot
Device 0: Vendor: Generic- Rev: 1.00 Prod: USB3.0 CRW -SD Type: Removable Hard Disk Capacity: 1876.0 MB = 1.8 GB (3842048 x 512) ... is now current device Scanning usb 0:1... Found /extlinux/extlinux.conf Retrieving file: /extlinux/extlinux.conf 206 bytes read in 4 ms (49.8 KiB/s) 1: LibreELEC Retrieving file: /KERNEL 23058440 bytes read in 1135 ms (19.4 MiB/s) append: boot=UUID=1111-5929 disk=UUID=bb4dea68-a794-4306-ac3e-c6d585ec2a6b console=ttyS0,115200 console=tty1 systemd.debug_shell=ttyS0 quiet Retrieving file: /sun50i-h6-orangepi-3.dtb WARN halted endpoint, queueing URB anyway. Unexpected XHCI event TRB, skipping... (bbf55f10 00000000 13000000 03008400) BUG at drivers/usb/host/xhci-ring.c:500/abort_td()! BUG! resetting ...
I don't know what the issue could be. I am not familiar with XHCI internals, and it works reliably for me. I have tried both a USB3 flash drive and a bus-powered USB3-to-SATA cable:
starting USB... Bus usb@5101000: USB EHCI 1.00 Bus usb@5101400: USB OHCI 1.0 Bus usb@5200000: Register 2000140 NbrPorts 2 Starting the controller USB XHCI 1.00 Bus usb@5311000: USB EHCI 1.00 Bus usb@5311400: USB OHCI 1.0 scanning bus usb@5101000 for devices... 1 USB Device(s) found scanning bus usb@5101400 for devices... 1 USB Device(s) found scanning bus usb@5200000 for devices... cannot reset port 3!? 4 USB Device(s) found scanning bus usb@5311000 for devices... 1 USB Device(s) found scanning bus usb@5311400 for devices... 1 USB Device(s) found scanning usb for storage devices... 1 Storage Device(s) found Hit any key to stop autoboot: 0 => usb tree USB device tree: 1 Hub (480 Mb/s, 0mA) u-boot EHCI Host Controller
1 Hub (12 Mb/s, 0mA) U-Boot Root Hub
1 Hub (5 Gb/s, 0mA) | U-Boot XHCI Host Controller | +-2 Hub (5 Gb/s, 0mA) | | GenesysLogic USB3.1 Hub | | | +-4 Mass Storage (5 Gb/s, 36mA) | ASUS ASUS USB3.0 to SATAIII Cable 0123456789ABCDEF02CC | +-3 Hub (480 Mb/s, 100mA) GenesysLogic USB2.1 Hub
1 Hub (480 Mb/s, 0mA) u-boot EHCI Host Controller
1 Hub (12 Mb/s, 0mA) U-Boot Root Hub
=> boot switch to partitions #0, OK mmc1(part 0) is current device ** No partition table - mmc 1 ** MMC: no card present
Device 0: Vendor: ASMT Rev: 0 Prod: 2105 Type: Hard Disk Capacity: 122104.3 MB = 119.2 GB (250069680 x 512) ... is now current device Scanning usb 0:1... Found U-Boot script /boot.scr 1020 bytes read in 3 ms (332 KiB/s) ## Executing script at 4fc00000 300 bytes read in 3 ms (97.7 KiB/s) 18870784 bytes read in 81 ms (222.2 MiB/s) 26321 bytes read in 4 ms (6.3 MiB/s) Moving Image from 0x40080000 to 0x40200000, end=41460000 ## Flattened Device Tree blob at 4fa00000 Booting using the fdt blob at 0x4fa00000 EHCI failed to shut down host controller. Host not halted after 16000 microseconds. Loading Device Tree to 0000000049ff6000, end 0000000049fff6d0 ... OK
Starting kernel ...
[ 0.000000] Booting Linux on physical CPU 0x0000000000 [0x410fd034]
Samuel
participants (4)
-
Andre Heider
-
Andre Przywara
-
Marek Vasut
-
Samuel Holland