diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index ac68aa2d27..cc1dfe463b 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -95,6 +95,15 @@ config USB_XHCI_FSL
 	depends on !SPL_NO_USB
 	help
 	  Enables support for the on-chip xHCI controller on NXP Layerscape SoCs.
+
+config USB_XHCI_IMX8
+	bool "XHCI support for imx8"
+	depends on ARCH_IMX8
+	default y
+	help
+	  Enables support for the on-chip xHCI controller on imx8qm and
+	  imx8qxp SoCs.
+
 endif # USB_XHCI_HCD
 
 config USB_EHCI_HCD
diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile
index 6aa574f6f7..e5a0a4ea5a 100644
--- a/drivers/usb/host/Makefile
+++ b/drivers/usb/host/Makefile
@@ -55,6 +55,7 @@ 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_STI) += dwc3-sti-glue.o
+obj-$(CONFIG_USB_XHCI_IMX8) += xhci-imx8.o
 
 # designware
 obj-$(CONFIG_USB_DWC2) += dwc2.o
diff --git a/drivers/usb/host/xhci-imx8.c b/drivers/usb/host/xhci-imx8.c
new file mode 100644
index 0000000000..3a7086742d
--- /dev/null
+++ b/drivers/usb/host/xhci-imx8.c
@@ -0,0 +1,201 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2019 NXP
+ *
+ * NXP i.MX8 USB HOST xHCI Controller (Cadence IP)
+ *
+ * Author: Peter Chen <peter.chen@nxp.com>
+ */
+
+#include <common.h>
+#include <usb.h>
+#include <malloc.h>
+#include <dm.h>
+#include <dm/device-internal.h>
+#include <clk.h>
+#include <power-domain.h>
+#include <generic-phy.h>
+#include <asm/arch/clock.h>
+#include <linux/compat.h>
+#include "xhci.h"
+
+/* Declare global data pointer */
+DECLARE_GLOBAL_DATA_PTR;
+
+/* Host registers */
+#define HCIVERSION_CAPLENGTH                  0x10000
+#define USBSTS                                0x10084
+
+/* None-core registers */
+#define USB3_CORE_CTRL1    0x00
+#define USB3_CORE_STATUS   0x0c
+#define USB3_SSPHY_STATUS  0x4c
+
+struct xhci_imx8_data {
+	void __iomem *usb3_ctrl_base;
+	void __iomem *usb3_core_base;
+	struct clk_bulk clks;
+	struct phy phy;
+};
+
+static struct xhci_imx8_data imx8_data;
+
+static void imx8_xhci_init(void)
+{
+	u32 tmp_data;
+	int timeout_us = 100000;
+
+	tmp_data = readl(imx8_data.usb3_ctrl_base + USB3_SSPHY_STATUS);
+	writel(tmp_data, imx8_data.usb3_ctrl_base + USB3_SSPHY_STATUS);
+	tmp_data = readl(imx8_data.usb3_ctrl_base + USB3_SSPHY_STATUS);
+	while ((tmp_data & 0xf0000000) != 0xf0000000 && timeout_us-- > 0) {
+		udelay(10);
+		tmp_data = readl(imx8_data.usb3_ctrl_base + USB3_SSPHY_STATUS);
+	}
+
+	if (timeout_us <= 0)
+		printf("clkvld is incorrect = 0x%x\n", tmp_data);
+
+	tmp_data = readl(imx8_data.usb3_ctrl_base + USB3_CORE_CTRL1);
+	tmp_data = (tmp_data & 0xfffffff8) | 0x202;
+	writel(tmp_data, imx8_data.usb3_ctrl_base + USB3_CORE_CTRL1);
+	clrbits_le32(imx8_data.usb3_ctrl_base + USB3_CORE_CTRL1, 1 << 26);
+	generic_phy_init(&imx8_data.phy);
+
+	/* clear all sw_rst */
+	clrbits_le32(imx8_data.usb3_ctrl_base + USB3_CORE_CTRL1, 0xFC << 24);
+
+	debug("wait xhci_power_on_ready\n");
+	tmp_data = readl(imx8_data.usb3_ctrl_base + USB3_CORE_STATUS);
+	timeout_us = 100000;
+	while (!(tmp_data & 0x1000) && timeout_us-- > 0) {
+		tmp_data = readl(imx8_data.usb3_ctrl_base + USB3_CORE_STATUS);
+		udelay(1);
+	}
+
+	if (timeout_us <= 0)
+		printf("wait xhci_power_on_ready timeout\n");
+	debug("xhci_power_on_ready\n");
+
+	tmp_data = readl(imx8_data.usb3_core_base + USBSTS);
+	debug("waiting CNR 0x%x\n", tmp_data);
+	timeout_us = 100000;
+	while (tmp_data & 0x800 && timeout_us-- > 0) {
+		tmp_data = readl(imx8_data.usb3_core_base + USBSTS);
+		udelay(1);
+	}
+
+	if (timeout_us <= 0)
+		printf("wait CNR timeout\n");
+	debug("check CNR has finished\n");
+}
+
+static void imx8_xhci_reset(void)
+{
+	/* Set CORE ctrl to default value, that all rst are hold */
+	writel(0xfc000001, imx8_data.usb3_ctrl_base + USB3_CORE_CTRL1);
+}
+
+static int xhci_imx8_clk_init(struct udevice *dev)
+{
+	int ret;
+
+	ret = clk_get_bulk(dev, &imx8_data.clks);
+	if (ret)
+		return ret;
+
+	ret = clk_enable_bulk(&imx8_data.clks);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int xhci_imx8_get_reg_addr(struct udevice *dev)
+{
+	imx8_data.usb3_ctrl_base =
+			(void __iomem *)devfdt_get_addr_index(dev, 0);
+	imx8_data.usb3_core_base =
+			(void __iomem *)devfdt_get_addr_index(dev, 4);
+
+	return 0;
+}
+
+static int xhci_imx8_probe(struct udevice *dev)
+{
+	struct xhci_hccr *hccr;
+	struct xhci_hcor *hcor;
+	struct udevice usbotg_dev;
+	struct power_domain pd;
+	int usbotg_off;
+	int ret = 0;
+	int len;
+
+	usbotg_off = fdtdec_lookup_phandle(gd->fdt_blob,
+					   dev_of_offset(dev),
+					   "cdns3,usb");
+	if (usbotg_off < 0)
+		return -EINVAL;
+	usbotg_dev.node = offset_to_ofnode(usbotg_off);
+	usbotg_dev.parent = dev->parent;
+	xhci_imx8_get_reg_addr(&usbotg_dev);
+
+#if CONFIG_IS_ENABLED(POWER_DOMAIN)
+	if (!power_domain_get(&usbotg_dev, &pd)) {
+		if (power_domain_on(&pd))
+			return -EINVAL;
+	}
+#endif
+
+	ret = generic_phy_get_by_index(&usbotg_dev, 0, &imx8_data.phy);
+	if (ret && ret != -ENOENT) {
+		printf("Failed to get USB PHY for %s\n", dev->name);
+		return ret;
+	}
+#if CONFIG_IS_ENABLED(CLK)
+	xhci_imx8_clk_init(&usbotg_dev);
+#endif
+
+	imx8_xhci_init();
+
+	hccr = (struct xhci_hccr *)(imx8_data.usb3_core_base +
+				    HCIVERSION_CAPLENGTH);
+	len = HC_LENGTH(xhci_readl(&hccr->cr_capbase));
+	hcor = (struct xhci_hcor *)((uintptr_t)hccr + len);
+	printf("XHCI-imx8 init hccr 0x%p and hcor 0x%p hc_length %d\n",
+	       (uint32_t *)hccr, (uint32_t *)hcor, len);
+
+	return xhci_register(dev, hccr, hcor);
+}
+
+static int xhci_imx8_remove(struct udevice *dev)
+{
+	int ret = xhci_deregister(dev);
+	if (!ret)
+		imx8_xhci_reset();
+
+#if CONFIG_IS_ENABLED(CLK)
+	clk_release_bulk(&imx8_data.clks);
+#endif
+	if (generic_phy_valid(&imx8_data.phy))
+		device_remove(imx8_data.phy.dev, DM_REMOVE_NORMAL);
+
+	return ret;
+}
+
+static const struct udevice_id xhci_usb_ids[] = {
+	{ .compatible = "Cadence,usb3-host", },
+	{ }
+};
+
+U_BOOT_DRIVER(xhci_imx8) = {
+	.name	= "xhci_imx8",
+	.id	= UCLASS_USB,
+	.of_match = xhci_usb_ids,
+	.probe = xhci_imx8_probe,
+	.remove = xhci_imx8_remove,
+	.ops	= &xhci_usb_ops,
+	.platdata_auto_alloc_size = sizeof(struct usb_platdata),
+	.priv_auto_alloc_size = sizeof(struct xhci_ctrl),
+	.flags	= DM_FLAG_ALLOC_PRIV_DMA,
+};
