[PATCH 4/5] usb: xhci: Add Qualcomm DWC3 xHCI driver

Robert Marko robert.marko at sartura.hr
Mon Aug 17 12:36:05 CEST 2020


Add driver for Qualcomm DWC3 based dual role xHCI USB controller.
Currently tested on IPQ40xx, but should support other Qualcomm SoC families as well.

Signed-off-by: Robert Marko <robert.marko at sartura.hr>
---
 MAINTAINERS                                   |   2 +
 .../usb/qcom-dwc3-ipq.txt                     |  25 +++
 drivers/usb/host/Kconfig                      |   6 +
 drivers/usb/host/Makefile                     |   1 +
 drivers/usb/host/xhci-ipq.c                   | 193 ++++++++++++++++++
 5 files changed, 227 insertions(+)
 create mode 100644 doc/device-tree-bindings/usb/qcom-dwc3-ipq.txt
 create mode 100644 drivers/usb/host/xhci-ipq.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 1a55327406..0e4e281d9b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -239,6 +239,8 @@ S:	Maintained
 F:	arch/arm/mach-ipq40xx/
 F:	include/dt-bindings/reset/qcom,ipq40xx-reset.h
 F:	drivers/reset/reset-ipq40xx.c
+F:	doc/device-tree-bindings/usb/qcom-dwc3-ipq.txt
+F:	drivers/usb/host/xhci-ipq.c
 
 ARM MARVELL KIRKWOOD ARMADA-XP ARMADA-38X ARMADA-37XX ARMADA-7K/8K
 M:	Stefan Roese <sr at denx.de>
diff --git a/doc/device-tree-bindings/usb/qcom-dwc3-ipq.txt b/doc/device-tree-bindings/usb/qcom-dwc3-ipq.txt
new file mode 100644
index 0000000000..591683e520
--- /dev/null
+++ b/doc/device-tree-bindings/usb/qcom-dwc3-ipq.txt
@@ -0,0 +1,25 @@
+Qualcomm SuperSpeed DWC3 USB SoC controller
+
+This controller is integrated in IPQ40xx SoC-s.
+It is a dual role USB3.0/USB2.0 controller.
+
+Required properties :
+ - compatible: must be "qcom,dwc3-ipq"
+ - reg: should contain address and length of the standard XHCI
+   register set for the device.
+
+Optional properties:
+ - rst-ctrl: Magic value used to reset PHY-s properly
+ 			(PHY-s may not function properly without it)
+ - hs-only : If present, specifies that board only has USB2.0(HS)
+ 			port
+
+Example:
+	xhci at 8a00000 {
+		compatible = "qcom,dwc3-ipq";
+		#address-cells = <1>;
+		#size-cells = <1>;
+		reg = <0x8a00000 0xcd00>;
+		rst-ctrl = <0x181E038 0x4>;
+		status = "disabled";
+	};
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index 1c374a7bd8..320c77ead5 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -93,6 +93,12 @@ config USB_XHCI_BRCM
 	  USB controller based on the Broadcom USB3 IP Core.
 	  Supports USB2/3 functionality.
 
+config USB_XHCI_IPQ
+	bool "Support for Qualcomm IPQ on-chip DWC3 xHCI USB controller"
+	depends on DM_USB && USB_XHCI_DWC3 && ARCH_IPQ40XX 
+	help
+	  Enables support for the on-chip xHCI DWC3 controller on Qualcomm IPQ SoCs.
+
 endif # USB_XHCI_HCD
 
 config USB_EHCI_HCD
diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile
index 29d4f87e38..0fa9c8f32a 100644
--- a/drivers/usb/host/Makefile
+++ b/drivers/usb/host/Makefile
@@ -55,6 +55,7 @@ obj-$(CONFIG_USB_XHCI_MVEBU) += xhci-mvebu.o
 obj-$(CONFIG_USB_XHCI_OMAP) += xhci-omap.o
 obj-$(CONFIG_USB_XHCI_PCI) += xhci-pci.o
 obj-$(CONFIG_USB_XHCI_RCAR) += xhci-rcar.o
+obj-$(CONFIG_USB_XHCI_IPQ) += xhci-ipq.o
 obj-$(CONFIG_USB_XHCI_STI) += dwc3-sti-glue.o
 
 # designware
diff --git a/drivers/usb/host/xhci-ipq.c b/drivers/usb/host/xhci-ipq.c
new file mode 100644
index 0000000000..b550cafac2
--- /dev/null
+++ b/drivers/usb/host/xhci-ipq.c
@@ -0,0 +1,193 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2015 Freescale Semiconductor, Inc.
+ * Copyright (c) 2020 Sartura Ltd.
+ *
+ * DWC3 controller driver
+ *
+ * Author: Ramneek Mehresh<ramneek.mehresh at freescale.com>
+ * Author: Robert Marko <robert.marko at sartura.hr>
+ *
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <usb.h>
+#include <linux/compat.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/usb/dwc3.h>
+#include <usb/xhci.h>
+
+/* Declare global data pointer */
+DECLARE_GLOBAL_DATA_PTR;
+
+struct ipq_xhci_platdata {
+	fdt_addr_t hcd_base;
+	unsigned int rst_ctrl;
+	unsigned int hs_only;
+};
+
+struct ipq_xhci {
+	struct ipq_xhci_platdata usb_plat;
+	struct xhci_ctrl ctrl;
+	struct udevice* dev;
+	struct xhci_hccr *hcd;
+	struct dwc3 *dwc3_reg;
+};
+
+void ipq_reset_usb_phy(void *data)
+{
+	unsigned int gcc_rst_ctrl;
+	struct ipq_xhci_platdata *platdata;
+	struct ipq_xhci *ipq = (struct ipq_xhci *)data;
+
+	platdata = dev_get_platdata(ipq->dev);
+	if (platdata == NULL) {
+		printf("Error: %s Failed\n", __func__);
+		return;
+	}
+
+	gcc_rst_ctrl = platdata->rst_ctrl;
+
+	if (gcc_rst_ctrl) {
+		/* assert HS PHY POR reset */
+		setbits_le32(gcc_rst_ctrl, 0x10);
+		mdelay(10);
+
+		/* assert HS PHY SRIF reset */
+		setbits_le32(gcc_rst_ctrl, 0x4);
+		mdelay(10);
+
+		/* deassert HS PHY SRIF reset and program HS PHY registers */
+		clrbits_le32(gcc_rst_ctrl, 0x4);
+		mdelay(10);
+
+		/* de-assert USB3 HS PHY POR reset */
+		clrbits_le32(gcc_rst_ctrl, 0x10);
+		mdelay(10);
+
+		if (!platdata->hs_only) {
+			/* assert SS PHY POR reset */
+			setbits_le32(gcc_rst_ctrl, 0x20);
+			mdelay(10);
+
+			/* deassert SS PHY POR reset */
+			clrbits_le32(gcc_rst_ctrl, 0x20);
+		}
+	}
+}
+
+static int ipq_xhci_core_init(struct ipq_xhci *ipq)
+{
+	int ret = 0;
+
+	ipq_reset_usb_phy((void *)ipq);
+
+	ret = dwc3_core_init(ipq->dwc3_reg);
+	if (ret) {
+		debug("%s:failed to initialize core\n", __func__);
+		return ret;
+	}
+
+	/* We are hard-coding DWC3 core to Host Mode */
+	dwc3_set_mode(ipq->dwc3_reg, DWC3_GCTL_PRTCAP_HOST);
+
+	return ret;
+}
+
+static void ipq_xhci_core_exit(struct ipq_xhci *ipq)
+{
+
+}
+
+static int xhci_usb_remove(struct udevice *dev)
+{
+	int ret;
+	ret = xhci_deregister(dev);
+
+	if (ret != 0) {
+		debug("%s:xhci deregistration failed\n", __func__);
+		return ret;
+	}
+
+	ipq_xhci_core_exit(dev_get_priv(dev));
+
+	return 0;
+}
+
+static int xhci_usb_probe(struct udevice *dev)
+{
+	struct ipq_xhci *context;
+	struct ipq_xhci_platdata *platdata;
+	struct xhci_hcor *hcor;
+	int ret;
+
+	platdata = dev_get_platdata(dev);
+	if (platdata == NULL) {
+		printf("Error: %s Failed\n", __func__);
+		return -ENODEV;
+	}
+
+	context = dev_get_priv(dev);
+	if (context == NULL) {
+		printf("Error: %s Failed\n", __func__);
+		return -ENODEV;
+	}
+
+	context->hcd = (struct xhci_hccr *)platdata->hcd_base;
+	context->dev = dev;
+	context->dwc3_reg = (struct dwc3 *)((char *)(context->hcd) + DWC3_REG_OFFSET);
+	hcor = (struct xhci_hcor *)((uint32_t)context->hcd +
+			HC_LENGTH(xhci_readl(&context->hcd->cr_capbase)));
+
+	ret = ipq_xhci_core_init(context);
+
+	if (ret) {
+		puts("Error initializing the XHCI controller\n");
+		return -EINVAL;
+	}
+
+	return xhci_register(dev, context->hcd, hcor);
+}
+
+static int xhci_ofdata_to_platdata(struct udevice *dev)
+{
+	struct ipq_xhci_platdata *platdata;
+	const void *blob = gd->fdt_blob;
+
+	platdata = dev_get_platdata(dev);
+	if (platdata == NULL) {
+		printf("Error: %s Failed\n", __func__);
+		return -ENODEV;
+	}
+
+	platdata->hcd_base = devfdt_get_addr(dev);
+	if (platdata->hcd_base == FDT_ADDR_T_NONE) {
+		debug("Error getting DWC3 base address\n");
+		return -ENXIO;
+	}
+
+	platdata->rst_ctrl = fdtdec_get_int(blob, dev_of_offset(dev), "rst-ctrl", 0);
+	platdata->hs_only = fdtdec_get_int(blob, dev_of_offset(dev), "hs-only", 0);
+
+	return 0;
+}
+
+static const struct udevice_id xhci_match_ids[] = {
+	{ .compatible = "qcom,dwc3-ipq" },
+	{}
+};
+
+U_BOOT_DRIVER(usb_xhci) = {
+	.name	= "xhci_ipq",
+	.id	= UCLASS_USB,
+	.of_match = xhci_match_ids,
+	.ofdata_to_platdata = xhci_ofdata_to_platdata,
+	.probe = xhci_usb_probe,
+	.remove = xhci_usb_remove,
+	.ops	= &xhci_usb_ops,
+	.platdata_auto_alloc_size = sizeof(struct ipq_xhci_platdata),
+	.priv_auto_alloc_size = sizeof(struct ipq_xhci),
+	.flags	= DM_FLAG_ALLOC_PRIV_DMA,
+};
-- 
2.26.2



More information about the U-Boot mailing list