diff --git a/Documentation/ABI/testing/sysfs-bus-pci-drivers-xhci_hcd b/Documentation/ABI/testing/sysfs-bus-pci-drivers-xhci_hcd
new file mode 100644
index 0000000000000..0088aba4caa86
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-bus-pci-drivers-xhci_hcd
@@ -0,0 +1,25 @@
+What:		/sys/bus/pci/drivers/xhci_hcd/.../dbc
+Date:		June 2017
+Contact:	Lu Baolu <baolu.lu@linux.intel.com>
+Description:
+		xHCI compatible USB host controllers (i.e. super-speed
+		USB3 controllers) are often implemented with the Debug
+		Capability (DbC). It can present a debug device which
+		is fully compliant with the USB framework and provides
+		the equivalent of a very high performance full-duplex
+		serial link for debug purpose.
+
+		The DbC debug device shares a root port with xHCI host.
+		When the DbC is enabled, the root port will be assigned
+		to the Debug Capability. Otherwise, it will be assigned
+		to xHCI.
+
+		Writing "enable" to this attribute will enable the DbC
+		functionality and the shared root port will be assigned
+		to the DbC device. Writing "disable" to this attribute
+		will disable the DbC functionality and the shared root
+		port will roll back to the xHCI.
+
+		Reading this attribute gives the state of the DbC. It
+		can be one of the following states: disabled, enabled,
+		initialized, connected, configured and stalled.
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index b80a94e632af0..6150bed7cfa80 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -27,6 +27,14 @@ config USB_XHCI_HCD
 	  module will be called xhci-hcd.
 
 if USB_XHCI_HCD
+config USB_XHCI_DBGCAP
+	bool "xHCI support for debug capability"
+	depends on TTY
+	---help---
+	  Say 'Y' to enable the support for the xHCI debug capability. Make
+	  sure that your xHCI host supports the extended debug capability and
+	  you want a TTY serial device based on the xHCI debug capability
+	  before enabling this option. If unsure, say 'N'.
 
 config USB_XHCI_PCI
        tristate
diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile
index 32b036e2ffef0..4ede4ce12366f 100644
--- a/drivers/usb/host/Makefile
+++ b/drivers/usb/host/Makefile
@@ -14,6 +14,11 @@ fhci-$(CONFIG_FHCI_DEBUG) += fhci-dbg.o
 xhci-hcd-y := xhci.o xhci-mem.o
 xhci-hcd-y += xhci-ring.o xhci-hub.o xhci-dbg.o
 xhci-hcd-y += xhci-trace.o
+
+ifneq ($(CONFIG_USB_XHCI_DBGCAP), )
+	xhci-hcd-y += xhci-dbgcap.o xhci-dbgtty.o
+endif
+
 ifneq ($(CONFIG_USB_XHCI_MTK), )
 	xhci-hcd-y += xhci-mtk-sch.o
 endif
diff --git a/drivers/usb/host/xhci-dbgcap.c b/drivers/usb/host/xhci-dbgcap.c
new file mode 100644
index 0000000000000..671e5023e683c
--- /dev/null
+++ b/drivers/usb/host/xhci-dbgcap.c
@@ -0,0 +1,996 @@
+/**
+ * xhci-dbgcap.c - xHCI debug capability support
+ *
+ * Copyright (C) 2017 Intel Corporation
+ *
+ * Author: Lu Baolu <baolu.lu@linux.intel.com>
+ */
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <linux/nls.h>
+
+#include "xhci.h"
+#include "xhci-trace.h"
+#include "xhci-dbgcap.h"
+
+static inline void *
+dbc_dma_alloc_coherent(struct xhci_hcd *xhci, size_t size,
+		       dma_addr_t *dma_handle, gfp_t flags)
+{
+	void		*vaddr;
+
+	vaddr = dma_alloc_coherent(xhci_to_hcd(xhci)->self.sysdev,
+				   size, dma_handle, flags);
+	memset(vaddr, 0, size);
+	return vaddr;
+}
+
+static inline void
+dbc_dma_free_coherent(struct xhci_hcd *xhci, size_t size,
+		      void *cpu_addr, dma_addr_t dma_handle)
+{
+	if (cpu_addr)
+		dma_free_coherent(xhci_to_hcd(xhci)->self.sysdev,
+				  size, cpu_addr, dma_handle);
+}
+
+static u32 xhci_dbc_populate_strings(struct dbc_str_descs *strings)
+{
+	struct usb_string_descriptor	*s_desc;
+	u32				string_length;
+
+	/* Serial string: */
+	s_desc = (struct usb_string_descriptor *)strings->serial;
+	utf8s_to_utf16s(DBC_STRING_SERIAL, strlen(DBC_STRING_SERIAL),
+			UTF16_LITTLE_ENDIAN, (wchar_t *)s_desc->wData,
+			DBC_MAX_STRING_LENGTH);
+
+	s_desc->bLength		= (strlen(DBC_STRING_SERIAL) + 1) * 2;
+	s_desc->bDescriptorType	= USB_DT_STRING;
+	string_length		= s_desc->bLength;
+	string_length		<<= 8;
+
+	/* Product string: */
+	s_desc = (struct usb_string_descriptor *)strings->product;
+	utf8s_to_utf16s(DBC_STRING_PRODUCT, strlen(DBC_STRING_PRODUCT),
+			UTF16_LITTLE_ENDIAN, (wchar_t *)s_desc->wData,
+			DBC_MAX_STRING_LENGTH);
+
+	s_desc->bLength		= (strlen(DBC_STRING_PRODUCT) + 1) * 2;
+	s_desc->bDescriptorType	= USB_DT_STRING;
+	string_length		+= s_desc->bLength;
+	string_length		<<= 8;
+
+	/* Manufacture string: */
+	s_desc = (struct usb_string_descriptor *)strings->manufacturer;
+	utf8s_to_utf16s(DBC_STRING_MANUFACTURER,
+			strlen(DBC_STRING_MANUFACTURER),
+			UTF16_LITTLE_ENDIAN, (wchar_t *)s_desc->wData,
+			DBC_MAX_STRING_LENGTH);
+
+	s_desc->bLength		= (strlen(DBC_STRING_MANUFACTURER) + 1) * 2;
+	s_desc->bDescriptorType	= USB_DT_STRING;
+	string_length		+= s_desc->bLength;
+	string_length		<<= 8;
+
+	/* String0: */
+	strings->string0[0]	= 4;
+	strings->string0[1]	= USB_DT_STRING;
+	strings->string0[2]	= 0x09;
+	strings->string0[3]	= 0x04;
+	string_length		+= 4;
+
+	return string_length;
+}
+
+static void xhci_dbc_init_contexts(struct xhci_hcd *xhci, u32 string_length)
+{
+	struct xhci_dbc		*dbc;
+	struct dbc_info_context	*info;
+	struct xhci_ep_ctx	*ep_ctx;
+	u32			dev_info;
+	dma_addr_t		deq, dma;
+	unsigned int		max_burst;
+
+	dbc = xhci->dbc;
+	if (!dbc)
+		return;
+
+	/* Populate info Context: */
+	info			= (struct dbc_info_context *)dbc->ctx->bytes;
+	dma			= dbc->string_dma;
+	info->string0		= cpu_to_le64(dma);
+	info->manufacturer	= cpu_to_le64(dma + DBC_MAX_STRING_LENGTH);
+	info->product		= cpu_to_le64(dma + DBC_MAX_STRING_LENGTH * 2);
+	info->serial		= cpu_to_le64(dma + DBC_MAX_STRING_LENGTH * 3);
+	info->length		= cpu_to_le32(string_length);
+
+	/* Populate bulk out endpoint context: */
+	ep_ctx			= dbc_bulkout_ctx(dbc);
+	max_burst		= DBC_CTRL_MAXBURST(readl(&dbc->regs->control));
+	deq			= dbc_bulkout_enq(dbc);
+	ep_ctx->ep_info		= 0;
+	ep_ctx->ep_info2	= dbc_epctx_info2(BULK_OUT_EP, 1024, max_burst);
+	ep_ctx->deq		= cpu_to_le64(deq | dbc->ring_out->cycle_state);
+
+	/* Populate bulk in endpoint context: */
+	ep_ctx			= dbc_bulkin_ctx(dbc);
+	deq			= dbc_bulkin_enq(dbc);
+	ep_ctx->ep_info		= 0;
+	ep_ctx->ep_info2	= dbc_epctx_info2(BULK_IN_EP, 1024, max_burst);
+	ep_ctx->deq		= cpu_to_le64(deq | dbc->ring_in->cycle_state);
+
+	/* Set DbC context and info registers: */
+	xhci_write_64(xhci, dbc->ctx->dma, &dbc->regs->dccp);
+
+	dev_info = cpu_to_le32((DBC_VENDOR_ID << 16) | DBC_PROTOCOL);
+	writel(dev_info, &dbc->regs->devinfo1);
+
+	dev_info = cpu_to_le32((DBC_DEVICE_REV << 16) | DBC_PRODUCT_ID);
+	writel(dev_info, &dbc->regs->devinfo2);
+}
+
+static void xhci_dbc_giveback(struct dbc_request *req, int status)
+	__releases(&dbc->lock)
+	__acquires(&dbc->lock)
+{
+	struct dbc_ep		*dep = req->dep;
+	struct xhci_dbc		*dbc = dep->dbc;
+	struct xhci_hcd		*xhci = dbc->xhci;
+	struct device		*dev = xhci_to_hcd(dbc->xhci)->self.sysdev;
+
+	list_del_init(&req->list_pending);
+	req->trb_dma = 0;
+	req->trb = NULL;
+
+	if (req->status == -EINPROGRESS)
+		req->status = status;
+
+	trace_xhci_dbc_giveback_request(req);
+
+	dma_unmap_single(dev,
+			 req->dma,
+			 req->length,
+			 dbc_ep_dma_direction(dep));
+
+	/* Give back the transfer request: */
+	spin_unlock(&dbc->lock);
+	req->complete(xhci, req);
+	spin_lock(&dbc->lock);
+}
+
+static void xhci_dbc_flush_single_request(struct dbc_request *req)
+{
+	union xhci_trb	*trb = req->trb;
+
+	trb->generic.field[0]	= 0;
+	trb->generic.field[1]	= 0;
+	trb->generic.field[2]	= 0;
+	trb->generic.field[3]	&= cpu_to_le32(TRB_CYCLE);
+	trb->generic.field[3]	|= cpu_to_le32(TRB_TYPE(TRB_TR_NOOP));
+
+	xhci_dbc_giveback(req, -ESHUTDOWN);
+}
+
+static void xhci_dbc_flush_endpoint_requests(struct dbc_ep *dep)
+{
+	struct dbc_request	*req, *tmp;
+
+	list_for_each_entry_safe(req, tmp, &dep->list_pending, list_pending)
+		xhci_dbc_flush_single_request(req);
+}
+
+static void xhci_dbc_flush_reqests(struct xhci_dbc *dbc)
+{
+	xhci_dbc_flush_endpoint_requests(&dbc->eps[BULK_OUT]);
+	xhci_dbc_flush_endpoint_requests(&dbc->eps[BULK_IN]);
+}
+
+struct dbc_request *
+dbc_alloc_request(struct dbc_ep *dep, gfp_t gfp_flags)
+{
+	struct dbc_request	*req;
+
+	req = kzalloc(sizeof(*req), gfp_flags);
+	if (!req)
+		return NULL;
+
+	req->dep = dep;
+	INIT_LIST_HEAD(&req->list_pending);
+	INIT_LIST_HEAD(&req->list_pool);
+	req->direction = dep->direction;
+
+	trace_xhci_dbc_alloc_request(req);
+
+	return req;
+}
+
+void
+dbc_free_request(struct dbc_ep *dep, struct dbc_request *req)
+{
+	trace_xhci_dbc_free_request(req);
+
+	kfree(req);
+}
+
+static void
+xhci_dbc_queue_trb(struct xhci_ring *ring, u32 field1,
+		   u32 field2, u32 field3, u32 field4)
+{
+	union xhci_trb		*trb, *next;
+
+	trb = ring->enqueue;
+	trb->generic.field[0]	= cpu_to_le32(field1);
+	trb->generic.field[1]	= cpu_to_le32(field2);
+	trb->generic.field[2]	= cpu_to_le32(field3);
+	trb->generic.field[3]	= cpu_to_le32(field4);
+
+	trace_xhci_dbc_gadget_ep_queue(ring, &trb->generic);
+
+	ring->num_trbs_free--;
+	next = ++(ring->enqueue);
+	if (TRB_TYPE_LINK_LE32(next->link.control)) {
+		next->link.control ^= cpu_to_le32(TRB_CYCLE);
+		ring->enqueue = ring->enq_seg->trbs;
+		ring->cycle_state ^= 1;
+	}
+}
+
+static int xhci_dbc_queue_bulk_tx(struct dbc_ep *dep,
+				  struct dbc_request *req)
+{
+	u64			addr;
+	union xhci_trb		*trb;
+	unsigned int		num_trbs;
+	struct xhci_dbc		*dbc = dep->dbc;
+	struct xhci_ring	*ring = dep->ring;
+	u32			length, control, cycle;
+
+	num_trbs = count_trbs(req->dma, req->length);
+	WARN_ON(num_trbs != 1);
+	if (ring->num_trbs_free < num_trbs)
+		return -EBUSY;
+
+	addr	= req->dma;
+	trb	= ring->enqueue;
+	cycle	= ring->cycle_state;
+	length	= TRB_LEN(req->length);
+	control	= TRB_TYPE(TRB_NORMAL) | TRB_IOC;
+
+	if (cycle)
+		control &= cpu_to_le32(~TRB_CYCLE);
+	else
+		control |= cpu_to_le32(TRB_CYCLE);
+
+	req->trb = ring->enqueue;
+	req->trb_dma = xhci_trb_virt_to_dma(ring->enq_seg, ring->enqueue);
+	xhci_dbc_queue_trb(ring,
+			   lower_32_bits(addr),
+			   upper_32_bits(addr),
+			   length, control);
+
+	/*
+	 * Add a barrier between writes of trb fields and flipping
+	 * the cycle bit:
+	 */
+	wmb();
+
+	if (cycle)
+		trb->generic.field[3] |= cpu_to_le32(TRB_CYCLE);
+	else
+		trb->generic.field[3] &= cpu_to_le32(~TRB_CYCLE);
+
+	writel(DBC_DOOR_BELL_TARGET(dep->direction), &dbc->regs->doorbell);
+
+	return 0;
+}
+
+static int
+dbc_ep_do_queue(struct dbc_ep *dep, struct dbc_request *req)
+{
+	int			ret;
+	struct device		*dev;
+	struct xhci_dbc		*dbc = dep->dbc;
+	struct xhci_hcd		*xhci = dbc->xhci;
+
+	dev = xhci_to_hcd(xhci)->self.sysdev;
+
+	if (!req->length || !req->buf)
+		return -EINVAL;
+
+	req->actual		= 0;
+	req->status		= -EINPROGRESS;
+
+	req->dma = dma_map_single(dev,
+				  req->buf,
+				  req->length,
+				  dbc_ep_dma_direction(dep));
+	if (dma_mapping_error(dev, req->dma)) {
+		xhci_err(xhci, "failed to map buffer\n");
+		return -EFAULT;
+	}
+
+	ret = xhci_dbc_queue_bulk_tx(dep, req);
+	if (ret) {
+		xhci_err(xhci, "failed to queue trbs\n");
+		dma_unmap_single(dev,
+				 req->dma,
+				 req->length,
+				 dbc_ep_dma_direction(dep));
+		return -EFAULT;
+	}
+
+	list_add_tail(&req->list_pending, &dep->list_pending);
+
+	return 0;
+}
+
+int dbc_ep_queue(struct dbc_ep *dep, struct dbc_request *req,
+		 gfp_t gfp_flags)
+{
+	struct xhci_dbc		*dbc = dep->dbc;
+	int			ret = -ESHUTDOWN;
+
+	spin_lock(&dbc->lock);
+	if (dbc->state == DS_CONFIGURED)
+		ret = dbc_ep_do_queue(dep, req);
+	spin_unlock(&dbc->lock);
+
+	mod_delayed_work(system_wq, &dbc->event_work, 0);
+
+	trace_xhci_dbc_queue_request(req);
+
+	return ret;
+}
+
+static inline void xhci_dbc_do_eps_init(struct xhci_hcd *xhci, bool direction)
+{
+	struct dbc_ep		*dep;
+	struct xhci_dbc		*dbc = xhci->dbc;
+
+	dep			= &dbc->eps[direction];
+	dep->dbc		= dbc;
+	dep->direction		= direction;
+	dep->ring		= direction ? dbc->ring_in : dbc->ring_out;
+
+	INIT_LIST_HEAD(&dep->list_pending);
+}
+
+static void xhci_dbc_eps_init(struct xhci_hcd *xhci)
+{
+	xhci_dbc_do_eps_init(xhci, BULK_OUT);
+	xhci_dbc_do_eps_init(xhci, BULK_IN);
+}
+
+static void xhci_dbc_eps_exit(struct xhci_hcd *xhci)
+{
+	struct xhci_dbc		*dbc = xhci->dbc;
+
+	memset(dbc->eps, 0, ARRAY_SIZE(dbc->eps));
+}
+
+static int xhci_dbc_mem_init(struct xhci_hcd *xhci, gfp_t flags)
+{
+	int			ret;
+	dma_addr_t		deq;
+	u32			string_length;
+	struct xhci_dbc		*dbc = xhci->dbc;
+
+	/* Allocate various rings for events and transfers: */
+	dbc->ring_evt = xhci_ring_alloc(xhci, 1, 1, TYPE_EVENT, 0, flags);
+	if (!dbc->ring_evt)
+		goto evt_fail;
+
+	dbc->ring_in = xhci_ring_alloc(xhci, 1, 1, TYPE_BULK, 0, flags);
+	if (!dbc->ring_in)
+		goto in_fail;
+
+	dbc->ring_out = xhci_ring_alloc(xhci, 1, 1, TYPE_BULK, 0, flags);
+	if (!dbc->ring_out)
+		goto out_fail;
+
+	/* Allocate and populate ERST: */
+	ret = xhci_alloc_erst(xhci, dbc->ring_evt, &dbc->erst, flags);
+	if (ret)
+		goto erst_fail;
+
+	/* Allocate context data structure: */
+	dbc->ctx = xhci_alloc_container_ctx(xhci, XHCI_CTX_TYPE_DEVICE, flags);
+	if (!dbc->ctx)
+		goto ctx_fail;
+
+	/* Allocate the string table: */
+	dbc->string_size = sizeof(struct dbc_str_descs);
+	dbc->string = dbc_dma_alloc_coherent(xhci,
+					     dbc->string_size,
+					     &dbc->string_dma,
+					     flags);
+	if (!dbc->string)
+		goto string_fail;
+
+	/* Setup ERST register: */
+	writel(dbc->erst.erst_size, &dbc->regs->ersts);
+	xhci_write_64(xhci, dbc->erst.erst_dma_addr, &dbc->regs->erstba);
+	deq = xhci_trb_virt_to_dma(dbc->ring_evt->deq_seg,
+				   dbc->ring_evt->dequeue);
+	xhci_write_64(xhci, deq, &dbc->regs->erdp);
+
+	/* Setup strings and contexts: */
+	string_length = xhci_dbc_populate_strings(dbc->string);
+	xhci_dbc_init_contexts(xhci, string_length);
+
+	mmiowb();
+
+	xhci_dbc_eps_init(xhci);
+	dbc->state = DS_INITIALIZED;
+
+	return 0;
+
+string_fail:
+	xhci_free_container_ctx(xhci, dbc->ctx);
+	dbc->ctx = NULL;
+ctx_fail:
+	xhci_free_erst(xhci, &dbc->erst);
+erst_fail:
+	xhci_ring_free(xhci, dbc->ring_out);
+	dbc->ring_out = NULL;
+out_fail:
+	xhci_ring_free(xhci, dbc->ring_in);
+	dbc->ring_in = NULL;
+in_fail:
+	xhci_ring_free(xhci, dbc->ring_evt);
+	dbc->ring_evt = NULL;
+evt_fail:
+	return -ENOMEM;
+}
+
+static void xhci_dbc_mem_cleanup(struct xhci_hcd *xhci)
+{
+	struct xhci_dbc		*dbc = xhci->dbc;
+
+	if (!dbc)
+		return;
+
+	xhci_dbc_eps_exit(xhci);
+
+	if (dbc->string) {
+		dbc_dma_free_coherent(xhci,
+				      dbc->string_size,
+				      dbc->string, dbc->string_dma);
+		dbc->string = NULL;
+	}
+
+	xhci_free_container_ctx(xhci, dbc->ctx);
+	dbc->ctx = NULL;
+
+	xhci_free_erst(xhci, &dbc->erst);
+	xhci_ring_free(xhci, dbc->ring_out);
+	xhci_ring_free(xhci, dbc->ring_in);
+	xhci_ring_free(xhci, dbc->ring_evt);
+	dbc->ring_in = NULL;
+	dbc->ring_out = NULL;
+	dbc->ring_evt = NULL;
+}
+
+static int xhci_do_dbc_start(struct xhci_hcd *xhci)
+{
+	int			ret;
+	u32			ctrl;
+	struct xhci_dbc		*dbc = xhci->dbc;
+
+	if (dbc->state != DS_DISABLED)
+		return -EINVAL;
+
+	writel(0, &dbc->regs->control);
+	ret = xhci_handshake(&dbc->regs->control,
+			     DBC_CTRL_DBC_ENABLE,
+			     0, 1000);
+	if (ret)
+		return ret;
+
+	ret = xhci_dbc_mem_init(xhci, GFP_ATOMIC);
+	if (ret)
+		return ret;
+
+	ctrl = readl(&dbc->regs->control);
+	writel(ctrl | DBC_CTRL_DBC_ENABLE | DBC_CTRL_PORT_ENABLE,
+	       &dbc->regs->control);
+	ret = xhci_handshake(&dbc->regs->control,
+			     DBC_CTRL_DBC_ENABLE,
+			     DBC_CTRL_DBC_ENABLE, 1000);
+	if (ret)
+		return ret;
+
+	dbc->state = DS_ENABLED;
+
+	return 0;
+}
+
+static void xhci_do_dbc_stop(struct xhci_hcd *xhci)
+{
+	struct xhci_dbc		*dbc = xhci->dbc;
+
+	if (dbc->state == DS_DISABLED)
+		return;
+
+	writel(0, &dbc->regs->control);
+	xhci_dbc_mem_cleanup(xhci);
+	dbc->state = DS_DISABLED;
+}
+
+static int xhci_dbc_start(struct xhci_hcd *xhci)
+{
+	int			ret;
+	struct xhci_dbc		*dbc = xhci->dbc;
+
+	WARN_ON(!dbc);
+
+	pm_runtime_get_sync(xhci_to_hcd(xhci)->self.controller);
+
+	spin_lock(&dbc->lock);
+	ret = xhci_do_dbc_start(xhci);
+	spin_unlock(&dbc->lock);
+
+	if (ret) {
+		pm_runtime_put(xhci_to_hcd(xhci)->self.controller);
+		return ret;
+	}
+
+	return mod_delayed_work(system_wq, &dbc->event_work, 1);
+}
+
+static void xhci_dbc_stop(struct xhci_hcd *xhci)
+{
+	struct xhci_dbc		*dbc = xhci->dbc;
+	struct dbc_port		*port = &dbc->port;
+
+	WARN_ON(!dbc);
+
+	cancel_delayed_work_sync(&dbc->event_work);
+
+	if (port->registered)
+		xhci_dbc_tty_unregister_device(xhci);
+
+	spin_lock(&dbc->lock);
+	xhci_do_dbc_stop(xhci);
+	spin_unlock(&dbc->lock);
+
+	pm_runtime_put_sync(xhci_to_hcd(xhci)->self.controller);
+}
+
+static void
+dbc_handle_port_status(struct xhci_hcd *xhci, union xhci_trb *event)
+{
+	u32			portsc;
+	struct xhci_dbc		*dbc = xhci->dbc;
+
+	portsc = readl(&dbc->regs->portsc);
+	if (portsc & DBC_PORTSC_CONN_CHANGE)
+		xhci_info(xhci, "DbC port connect change\n");
+
+	if (portsc & DBC_PORTSC_RESET_CHANGE)
+		xhci_info(xhci, "DbC port reset change\n");
+
+	if (portsc & DBC_PORTSC_LINK_CHANGE)
+		xhci_info(xhci, "DbC port link status change\n");
+
+	if (portsc & DBC_PORTSC_CONFIG_CHANGE)
+		xhci_info(xhci, "DbC config error change\n");
+
+	/* Port reset change bit will be cleared in other place: */
+	writel(portsc & ~DBC_PORTSC_RESET_CHANGE, &dbc->regs->portsc);
+}
+
+static void dbc_handle_xfer_event(struct xhci_hcd *xhci, union xhci_trb *event)
+{
+	struct dbc_ep		*dep;
+	struct xhci_ring	*ring;
+	int			ep_id;
+	int			status;
+	u32			comp_code;
+	size_t			remain_length;
+	struct dbc_request	*req = NULL, *r;
+
+	comp_code	= GET_COMP_CODE(le32_to_cpu(event->generic.field[2]));
+	remain_length	= EVENT_TRB_LEN(le32_to_cpu(event->generic.field[2]));
+	ep_id		= TRB_TO_EP_ID(le32_to_cpu(event->generic.field[3]));
+	dep		= (ep_id == EPID_OUT) ?
+				get_out_ep(xhci) : get_in_ep(xhci);
+	ring		= dep->ring;
+
+	switch (comp_code) {
+	case COMP_SUCCESS:
+		remain_length = 0;
+	/* FALLTHROUGH */
+	case COMP_SHORT_PACKET:
+		status = 0;
+		break;
+	case COMP_TRB_ERROR:
+	case COMP_BABBLE_DETECTED_ERROR:
+	case COMP_USB_TRANSACTION_ERROR:
+	case COMP_STALL_ERROR:
+		xhci_warn(xhci, "tx error %d detected\n", comp_code);
+		status = -comp_code;
+		break;
+	default:
+		xhci_err(xhci, "unknown tx error %d\n", comp_code);
+		status = -comp_code;
+		break;
+	}
+
+	/* Match the pending request: */
+	list_for_each_entry(r, &dep->list_pending, list_pending) {
+		if (r->trb_dma == event->trans_event.buffer) {
+			req = r;
+			break;
+		}
+	}
+
+	if (!req) {
+		xhci_warn(xhci, "no matched request\n");
+		return;
+	}
+
+	trace_xhci_dbc_handle_transfer(ring, &req->trb->generic);
+
+	ring->num_trbs_free++;
+	req->actual = req->length - remain_length;
+	xhci_dbc_giveback(req, status);
+}
+
+static enum evtreturn xhci_dbc_do_handle_events(struct xhci_dbc *dbc)
+{
+	dma_addr_t		deq;
+	struct dbc_ep		*dep;
+	union xhci_trb		*evt;
+	u32			ctrl, portsc;
+	struct xhci_hcd		*xhci = dbc->xhci;
+	bool			update_erdp = false;
+
+	/* DbC state machine: */
+	switch (dbc->state) {
+	case DS_DISABLED:
+	case DS_INITIALIZED:
+
+		return EVT_ERR;
+	case DS_ENABLED:
+		portsc = readl(&dbc->regs->portsc);
+		if (portsc & DBC_PORTSC_CONN_STATUS) {
+			dbc->state = DS_CONNECTED;
+			xhci_info(xhci, "DbC connected\n");
+		}
+
+		return EVT_DONE;
+	case DS_CONNECTED:
+		ctrl = readl(&dbc->regs->control);
+		if (ctrl & DBC_CTRL_DBC_RUN) {
+			dbc->state = DS_CONFIGURED;
+			xhci_info(xhci, "DbC configured\n");
+			portsc = readl(&dbc->regs->portsc);
+			writel(portsc, &dbc->regs->portsc);
+			return EVT_GSER;
+		}
+
+		return EVT_DONE;
+	case DS_CONFIGURED:
+		/* Handle cable unplug event: */
+		portsc = readl(&dbc->regs->portsc);
+		if (!(portsc & DBC_PORTSC_PORT_ENABLED) &&
+		    !(portsc & DBC_PORTSC_CONN_STATUS)) {
+			xhci_info(xhci, "DbC cable unplugged\n");
+			dbc->state = DS_ENABLED;
+			xhci_dbc_flush_reqests(dbc);
+
+			return EVT_DISC;
+		}
+
+		/* Handle debug port reset event: */
+		if (portsc & DBC_PORTSC_RESET_CHANGE) {
+			xhci_info(xhci, "DbC port reset\n");
+			writel(portsc, &dbc->regs->portsc);
+			dbc->state = DS_ENABLED;
+			xhci_dbc_flush_reqests(dbc);
+
+			return EVT_DISC;
+		}
+
+		/* Handle endpoint stall event: */
+		ctrl = readl(&dbc->regs->control);
+		if ((ctrl & DBC_CTRL_HALT_IN_TR) ||
+		    (ctrl & DBC_CTRL_HALT_OUT_TR)) {
+			xhci_info(xhci, "DbC Endpoint stall\n");
+			dbc->state = DS_STALLED;
+
+			if (ctrl & DBC_CTRL_HALT_IN_TR) {
+				dep = get_in_ep(xhci);
+				xhci_dbc_flush_endpoint_requests(dep);
+			}
+
+			if (ctrl & DBC_CTRL_HALT_OUT_TR) {
+				dep = get_out_ep(xhci);
+				xhci_dbc_flush_endpoint_requests(dep);
+			}
+
+			return EVT_DONE;
+		}
+
+		/* Clear DbC run change bit: */
+		if (ctrl & DBC_CTRL_DBC_RUN_CHANGE) {
+			writel(ctrl, &dbc->regs->control);
+			ctrl = readl(&dbc->regs->control);
+		}
+
+		break;
+	case DS_STALLED:
+		ctrl = readl(&dbc->regs->control);
+		if (!(ctrl & DBC_CTRL_HALT_IN_TR) &&
+		    !(ctrl & DBC_CTRL_HALT_OUT_TR) &&
+		    (ctrl & DBC_CTRL_DBC_RUN)) {
+			dbc->state = DS_CONFIGURED;
+			break;
+		}
+
+		return EVT_DONE;
+	default:
+		xhci_err(xhci, "Unknown DbC state %d\n", dbc->state);
+		break;
+	}
+
+	/* Handle the events in the event ring: */
+	evt = dbc->ring_evt->dequeue;
+	while ((le32_to_cpu(evt->event_cmd.flags) & TRB_CYCLE) ==
+			dbc->ring_evt->cycle_state) {
+		/*
+		 * Add a barrier between reading the cycle flag and any
+		 * reads of the event's flags/data below:
+		 */
+		rmb();
+
+		trace_xhci_dbc_handle_event(dbc->ring_evt, &evt->generic);
+
+		switch (le32_to_cpu(evt->event_cmd.flags) & TRB_TYPE_BITMASK) {
+		case TRB_TYPE(TRB_PORT_STATUS):
+			dbc_handle_port_status(xhci, evt);
+			break;
+		case TRB_TYPE(TRB_TRANSFER):
+			dbc_handle_xfer_event(xhci, evt);
+			break;
+		default:
+			break;
+		}
+
+		inc_deq(xhci, dbc->ring_evt);
+		evt = dbc->ring_evt->dequeue;
+		update_erdp = true;
+	}
+
+	/* Update event ring dequeue pointer: */
+	if (update_erdp) {
+		deq = xhci_trb_virt_to_dma(dbc->ring_evt->deq_seg,
+					   dbc->ring_evt->dequeue);
+		xhci_write_64(xhci, deq, &dbc->regs->erdp);
+	}
+
+	return EVT_DONE;
+}
+
+static void xhci_dbc_handle_events(struct work_struct *work)
+{
+	int			ret;
+	enum evtreturn		evtr;
+	struct xhci_dbc		*dbc;
+	struct xhci_hcd		*xhci;
+
+	dbc = container_of(to_delayed_work(work), struct xhci_dbc, event_work);
+	xhci = dbc->xhci;
+
+	spin_lock(&dbc->lock);
+	evtr = xhci_dbc_do_handle_events(dbc);
+	spin_unlock(&dbc->lock);
+
+	switch (evtr) {
+	case EVT_GSER:
+		ret = xhci_dbc_tty_register_device(xhci);
+		if (ret) {
+			xhci_err(xhci, "failed to alloc tty device\n");
+			break;
+		}
+
+		xhci_info(xhci, "DbC now attached to /dev/ttyDBC0\n");
+		break;
+	case EVT_DISC:
+		xhci_dbc_tty_unregister_device(xhci);
+		break;
+	case EVT_DONE:
+		break;
+	default:
+		xhci_info(xhci, "stop handling dbc events\n");
+		return;
+	}
+
+	mod_delayed_work(system_wq, &dbc->event_work, 1);
+}
+
+static void xhci_do_dbc_exit(struct xhci_hcd *xhci)
+{
+	unsigned long		flags;
+
+	spin_lock_irqsave(&xhci->lock, flags);
+	kfree(xhci->dbc);
+	xhci->dbc = NULL;
+	spin_unlock_irqrestore(&xhci->lock, flags);
+}
+
+static int xhci_do_dbc_init(struct xhci_hcd *xhci)
+{
+	u32			reg;
+	struct xhci_dbc		*dbc;
+	unsigned long		flags;
+	void __iomem		*base;
+	int			dbc_cap_offs;
+
+	base = &xhci->cap_regs->hc_capbase;
+	dbc_cap_offs = xhci_find_next_ext_cap(base, 0, XHCI_EXT_CAPS_DEBUG);
+	if (!dbc_cap_offs)
+		return -ENODEV;
+
+	dbc = kzalloc(sizeof(*dbc), GFP_KERNEL);
+	if (!dbc)
+		return -ENOMEM;
+
+	dbc->regs = base + dbc_cap_offs;
+
+	/* We will avoid using DbC in xhci driver if it's in use. */
+	reg = readl(&dbc->regs->control);
+	if (reg & DBC_CTRL_DBC_ENABLE) {
+		kfree(dbc);
+		return -EBUSY;
+	}
+
+	spin_lock_irqsave(&xhci->lock, flags);
+	if (xhci->dbc) {
+		spin_unlock_irqrestore(&xhci->lock, flags);
+		kfree(dbc);
+		return -EBUSY;
+	}
+	xhci->dbc = dbc;
+	spin_unlock_irqrestore(&xhci->lock, flags);
+
+	dbc->xhci = xhci;
+	INIT_DELAYED_WORK(&dbc->event_work, xhci_dbc_handle_events);
+	spin_lock_init(&dbc->lock);
+
+	return 0;
+}
+
+static ssize_t dbc_show(struct device *dev,
+			struct device_attribute *attr,
+			char *buf)
+{
+	const char		*p;
+	struct xhci_dbc		*dbc;
+	struct xhci_hcd		*xhci;
+
+	xhci = hcd_to_xhci(dev_get_drvdata(dev));
+	dbc = xhci->dbc;
+
+	switch (dbc->state) {
+	case DS_DISABLED:
+		p = "disabled";
+		break;
+	case DS_INITIALIZED:
+		p = "initialized";
+		break;
+	case DS_ENABLED:
+		p = "enabled";
+		break;
+	case DS_CONNECTED:
+		p = "connected";
+		break;
+	case DS_CONFIGURED:
+		p = "configured";
+		break;
+	case DS_STALLED:
+		p = "stalled";
+		break;
+	default:
+		p = "unknown";
+	}
+
+	return sprintf(buf, "%s\n", p);
+}
+
+static ssize_t dbc_store(struct device *dev,
+			 struct device_attribute *attr,
+			 const char *buf, size_t count)
+{
+	struct xhci_dbc		*dbc;
+	struct xhci_hcd		*xhci;
+
+	xhci = hcd_to_xhci(dev_get_drvdata(dev));
+	dbc = xhci->dbc;
+
+	if (!strncmp(buf, "enable", 6))
+		xhci_dbc_start(xhci);
+	else if (!strncmp(buf, "disable", 7))
+		xhci_dbc_stop(xhci);
+	else
+		return -EINVAL;
+
+	return count;
+}
+
+static DEVICE_ATTR(dbc, 0644, dbc_show, dbc_store);
+
+int xhci_dbc_init(struct xhci_hcd *xhci)
+{
+	int			ret;
+	struct device		*dev = xhci_to_hcd(xhci)->self.controller;
+
+	ret = xhci_do_dbc_init(xhci);
+	if (ret)
+		goto init_err3;
+
+	ret = xhci_dbc_tty_register_driver(xhci);
+	if (ret)
+		goto init_err2;
+
+	ret = device_create_file(dev, &dev_attr_dbc);
+	if (ret)
+		goto init_err1;
+
+	return 0;
+
+init_err1:
+	xhci_dbc_tty_unregister_driver();
+init_err2:
+	xhci_do_dbc_exit(xhci);
+init_err3:
+	return ret;
+}
+
+void xhci_dbc_exit(struct xhci_hcd *xhci)
+{
+	struct device		*dev = xhci_to_hcd(xhci)->self.controller;
+
+	if (!xhci->dbc)
+		return;
+
+	device_remove_file(dev, &dev_attr_dbc);
+	xhci_dbc_tty_unregister_driver();
+	xhci_dbc_stop(xhci);
+	xhci_do_dbc_exit(xhci);
+}
+
+#ifdef CONFIG_PM
+int xhci_dbc_suspend(struct xhci_hcd *xhci)
+{
+	struct xhci_dbc		*dbc = xhci->dbc;
+
+	if (!dbc)
+		return 0;
+
+	if (dbc->state == DS_CONFIGURED)
+		dbc->resume_required = 1;
+
+	xhci_dbc_stop(xhci);
+
+	return 0;
+}
+
+int xhci_dbc_resume(struct xhci_hcd *xhci)
+{
+	int			ret = 0;
+	struct xhci_dbc		*dbc = xhci->dbc;
+
+	if (!dbc)
+		return 0;
+
+	if (dbc->resume_required) {
+		dbc->resume_required = 0;
+		xhci_dbc_start(xhci);
+	}
+
+	return ret;
+}
+#endif /* CONFIG_PM */
diff --git a/drivers/usb/host/xhci-dbgcap.h b/drivers/usb/host/xhci-dbgcap.h
new file mode 100644
index 0000000000000..e66ea0748ba37
--- /dev/null
+++ b/drivers/usb/host/xhci-dbgcap.h
@@ -0,0 +1,229 @@
+
+/**
+ * xhci-dbgcap.h - xHCI debug capability support
+ *
+ * Copyright (C) 2017 Intel Corporation
+ *
+ * Author: Lu Baolu <baolu.lu@linux.intel.com>
+ */
+#ifndef __LINUX_XHCI_DBGCAP_H
+#define __LINUX_XHCI_DBGCAP_H
+
+#include <linux/tty.h>
+#include <linux/kfifo.h>
+
+struct dbc_regs {
+	__le32	capability;
+	__le32	doorbell;
+	__le32	ersts;		/* Event Ring Segment Table Size*/
+	__le32	__reserved_0;	/* 0c~0f reserved bits */
+	__le64	erstba;		/* Event Ring Segment Table Base Address */
+	__le64	erdp;		/* Event Ring Dequeue Pointer */
+	__le32	control;
+	__le32	status;
+	__le32	portsc;		/* Port status and control */
+	__le32	__reserved_1;	/* 2b~28 reserved bits */
+	__le64	dccp;		/* Debug Capability Context Pointer */
+	__le32	devinfo1;	/* Device Descriptor Info Register 1 */
+	__le32	devinfo2;	/* Device Descriptor Info Register 2 */
+};
+
+struct dbc_info_context {
+	__le64	string0;
+	__le64	manufacturer;
+	__le64	product;
+	__le64	serial;
+	__le32	length;
+	__le32	__reserved_0[7];
+};
+
+#define DBC_CTRL_DBC_RUN		BIT(0)
+#define DBC_CTRL_PORT_ENABLE		BIT(1)
+#define DBC_CTRL_HALT_OUT_TR		BIT(2)
+#define DBC_CTRL_HALT_IN_TR		BIT(3)
+#define DBC_CTRL_DBC_RUN_CHANGE		BIT(4)
+#define DBC_CTRL_DBC_ENABLE		BIT(31)
+#define DBC_CTRL_MAXBURST(p)		(((p) >> 16) & 0xff)
+#define DBC_DOOR_BELL_TARGET(p)		(((p) & 0xff) << 8)
+
+#define DBC_MAX_PACKET			1024
+#define DBC_MAX_STRING_LENGTH		64
+#define DBC_STRING_MANUFACTURER		"Linux Foundation"
+#define DBC_STRING_PRODUCT		"Linux USB Debug Target"
+#define DBC_STRING_SERIAL		"0001"
+#define	DBC_CONTEXT_SIZE		64
+
+/*
+ * Port status:
+ */
+#define DBC_PORTSC_CONN_STATUS		BIT(0)
+#define DBC_PORTSC_PORT_ENABLED		BIT(1)
+#define DBC_PORTSC_CONN_CHANGE		BIT(17)
+#define DBC_PORTSC_RESET_CHANGE		BIT(21)
+#define DBC_PORTSC_LINK_CHANGE		BIT(22)
+#define DBC_PORTSC_CONFIG_CHANGE	BIT(23)
+
+struct dbc_str_descs {
+	char	string0[DBC_MAX_STRING_LENGTH];
+	char	manufacturer[DBC_MAX_STRING_LENGTH];
+	char	product[DBC_MAX_STRING_LENGTH];
+	char	serial[DBC_MAX_STRING_LENGTH];
+};
+
+#define DBC_PROTOCOL			1	/* GNU Remote Debug Command */
+#define DBC_VENDOR_ID			0x1d6b	/* Linux Foundation 0x1d6b */
+#define DBC_PRODUCT_ID			0x0010	/* device 0010 */
+#define DBC_DEVICE_REV			0x0010	/* 0.10 */
+
+enum dbc_state {
+	DS_DISABLED = 0,
+	DS_INITIALIZED,
+	DS_ENABLED,
+	DS_CONNECTED,
+	DS_CONFIGURED,
+	DS_STALLED,
+};
+
+struct dbc_request {
+	void				*buf;
+	unsigned int			length;
+	dma_addr_t			dma;
+	void				(*complete)(struct xhci_hcd *xhci,
+						    struct dbc_request *req);
+	struct list_head		list_pool;
+	int				status;
+	unsigned int			actual;
+
+	struct dbc_ep			*dep;
+	struct list_head		list_pending;
+	dma_addr_t			trb_dma;
+	union xhci_trb			*trb;
+	unsigned			direction:1;
+};
+
+struct dbc_ep {
+	struct xhci_dbc			*dbc;
+	struct list_head		list_pending;
+	struct xhci_ring		*ring;
+	unsigned			direction:1;
+};
+
+#define DBC_QUEUE_SIZE			16
+#define DBC_WRITE_BUF_SIZE		8192
+
+/*
+ * Private structure for DbC hardware state:
+ */
+struct dbc_port {
+	struct tty_port			port;
+	spinlock_t			port_lock;	/* port access */
+
+	struct list_head		read_pool;
+	struct list_head		read_queue;
+	unsigned int			n_read;
+	struct tasklet_struct		push;
+
+	struct list_head		write_pool;
+	struct kfifo			write_fifo;
+
+	bool				registered;
+	struct dbc_ep			*in;
+	struct dbc_ep			*out;
+};
+
+struct xhci_dbc {
+	spinlock_t			lock;		/* device access */
+	struct xhci_hcd			*xhci;
+	struct dbc_regs __iomem		*regs;
+	struct xhci_ring		*ring_evt;
+	struct xhci_ring		*ring_in;
+	struct xhci_ring		*ring_out;
+	struct xhci_erst		erst;
+	struct xhci_container_ctx	*ctx;
+
+	struct dbc_str_descs		*string;
+	dma_addr_t			string_dma;
+	size_t				string_size;
+
+	enum dbc_state			state;
+	struct delayed_work		event_work;
+	unsigned			resume_required:1;
+	struct dbc_ep			eps[2];
+
+	struct dbc_port			port;
+};
+
+#define dbc_bulkout_ctx(d)		\
+	((struct xhci_ep_ctx *)((d)->ctx->bytes + DBC_CONTEXT_SIZE))
+#define dbc_bulkin_ctx(d)		\
+	((struct xhci_ep_ctx *)((d)->ctx->bytes + DBC_CONTEXT_SIZE * 2))
+#define dbc_bulkout_enq(d)		\
+	xhci_trb_virt_to_dma((d)->ring_out->enq_seg, (d)->ring_out->enqueue)
+#define dbc_bulkin_enq(d)		\
+	xhci_trb_virt_to_dma((d)->ring_in->enq_seg, (d)->ring_in->enqueue)
+#define dbc_epctx_info2(t, p, b)	\
+	cpu_to_le32(EP_TYPE(t) | MAX_PACKET(p) | MAX_BURST(b))
+#define dbc_ep_dma_direction(d)		\
+	((d)->direction ? DMA_FROM_DEVICE : DMA_TO_DEVICE)
+
+#define BULK_OUT			0
+#define BULK_IN				1
+#define EPID_OUT			2
+#define EPID_IN				3
+
+enum evtreturn {
+	EVT_ERR	= -1,
+	EVT_DONE,
+	EVT_GSER,
+	EVT_DISC,
+};
+
+static inline struct dbc_ep *get_in_ep(struct xhci_hcd *xhci)
+{
+	struct xhci_dbc		*dbc = xhci->dbc;
+
+	return &dbc->eps[BULK_IN];
+}
+
+static inline struct dbc_ep *get_out_ep(struct xhci_hcd *xhci)
+{
+	struct xhci_dbc		*dbc = xhci->dbc;
+
+	return &dbc->eps[BULK_OUT];
+}
+
+#ifdef CONFIG_USB_XHCI_DBGCAP
+int xhci_dbc_init(struct xhci_hcd *xhci);
+void xhci_dbc_exit(struct xhci_hcd *xhci);
+int xhci_dbc_tty_register_driver(struct xhci_hcd *xhci);
+void xhci_dbc_tty_unregister_driver(void);
+int xhci_dbc_tty_register_device(struct xhci_hcd *xhci);
+void xhci_dbc_tty_unregister_device(struct xhci_hcd *xhci);
+struct dbc_request *dbc_alloc_request(struct dbc_ep *dep, gfp_t gfp_flags);
+void dbc_free_request(struct dbc_ep *dep, struct dbc_request *req);
+int dbc_ep_queue(struct dbc_ep *dep, struct dbc_request *req, gfp_t gfp_flags);
+#ifdef CONFIG_PM
+int xhci_dbc_suspend(struct xhci_hcd *xhci);
+int xhci_dbc_resume(struct xhci_hcd *xhci);
+#endif /* CONFIG_PM */
+#else
+static inline int xhci_dbc_init(struct xhci_hcd *xhci)
+{
+	return 0;
+}
+
+static inline void xhci_dbc_exit(struct xhci_hcd *xhci)
+{
+}
+
+static inline int xhci_dbc_suspend(struct xhci_hcd *xhci)
+{
+	return 0;
+}
+
+static inline int xhci_dbc_resume(struct xhci_hcd *xhci)
+{
+	return 0;
+}
+#endif /* CONFIG_USB_XHCI_DBGCAP */
+#endif /* __LINUX_XHCI_DBGCAP_H */
diff --git a/drivers/usb/host/xhci-dbgtty.c b/drivers/usb/host/xhci-dbgtty.c
new file mode 100644
index 0000000000000..48811d72a94cc
--- /dev/null
+++ b/drivers/usb/host/xhci-dbgtty.c
@@ -0,0 +1,497 @@
+/**
+ * xhci-dbgtty.c - tty glue for xHCI debug capability
+ *
+ * Copyright (C) 2017 Intel Corporation
+ *
+ * Author: Lu Baolu <baolu.lu@linux.intel.com>
+ */
+
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+
+#include "xhci.h"
+#include "xhci-dbgcap.h"
+
+static unsigned int
+dbc_send_packet(struct dbc_port *port, char *packet, unsigned int size)
+{
+	unsigned int		len;
+
+	len = kfifo_len(&port->write_fifo);
+	if (len < size)
+		size = len;
+	if (size != 0)
+		size = kfifo_out(&port->write_fifo, packet, size);
+	return size;
+}
+
+static int dbc_start_tx(struct dbc_port *port)
+	__releases(&port->port_lock)
+	__acquires(&port->port_lock)
+{
+	int			len;
+	struct dbc_request	*req;
+	int			status = 0;
+	bool			do_tty_wake = false;
+	struct list_head	*pool = &port->write_pool;
+
+	while (!list_empty(pool)) {
+		req = list_entry(pool->next, struct dbc_request, list_pool);
+		len = dbc_send_packet(port, req->buf, DBC_MAX_PACKET);
+		if (len == 0)
+			break;
+		do_tty_wake = true;
+
+		req->length = len;
+		list_del(&req->list_pool);
+
+		spin_unlock(&port->port_lock);
+		status = dbc_ep_queue(port->out, req, GFP_ATOMIC);
+		spin_lock(&port->port_lock);
+
+		if (status) {
+			list_add(&req->list_pool, pool);
+			break;
+		}
+	}
+
+	if (do_tty_wake && port->port.tty)
+		tty_wakeup(port->port.tty);
+
+	return status;
+}
+
+static void dbc_start_rx(struct dbc_port *port)
+	__releases(&port->port_lock)
+	__acquires(&port->port_lock)
+{
+	struct dbc_request	*req;
+	int			status;
+	struct list_head	*pool = &port->read_pool;
+
+	while (!list_empty(pool)) {
+		if (!port->port.tty)
+			break;
+
+		req = list_entry(pool->next, struct dbc_request, list_pool);
+		list_del(&req->list_pool);
+		req->length = DBC_MAX_PACKET;
+
+		spin_unlock(&port->port_lock);
+		status = dbc_ep_queue(port->in, req, GFP_ATOMIC);
+		spin_lock(&port->port_lock);
+
+		if (status) {
+			list_add(&req->list_pool, pool);
+			break;
+		}
+	}
+}
+
+static void
+dbc_read_complete(struct xhci_hcd *xhci, struct dbc_request *req)
+{
+	struct xhci_dbc		*dbc = xhci->dbc;
+	struct dbc_port		*port = &dbc->port;
+
+	spin_lock(&port->port_lock);
+	list_add_tail(&req->list_pool, &port->read_queue);
+	tasklet_schedule(&port->push);
+	spin_unlock(&port->port_lock);
+}
+
+static void dbc_write_complete(struct xhci_hcd *xhci, struct dbc_request *req)
+{
+	struct xhci_dbc		*dbc = xhci->dbc;
+	struct dbc_port		*port = &dbc->port;
+
+	spin_lock(&port->port_lock);
+	list_add(&req->list_pool, &port->write_pool);
+	switch (req->status) {
+	case 0:
+		dbc_start_tx(port);
+		break;
+	case -ESHUTDOWN:
+		break;
+	default:
+		xhci_warn(xhci, "unexpected write complete status %d\n",
+			  req->status);
+		break;
+	}
+	spin_unlock(&port->port_lock);
+}
+
+void xhci_dbc_free_req(struct dbc_ep *dep, struct dbc_request *req)
+{
+	kfree(req->buf);
+	dbc_free_request(dep, req);
+}
+
+static int
+xhci_dbc_alloc_requests(struct dbc_ep *dep, struct list_head *head,
+			void (*fn)(struct xhci_hcd *, struct dbc_request *))
+{
+	int			i;
+	struct dbc_request	*req;
+
+	for (i = 0; i < DBC_QUEUE_SIZE; i++) {
+		req = dbc_alloc_request(dep, GFP_ATOMIC);
+		if (!req)
+			break;
+
+		req->length = DBC_MAX_PACKET;
+		req->buf = kmalloc(req->length, GFP_KERNEL);
+		if (!req->buf) {
+			xhci_dbc_free_req(dep, req);
+			break;
+		}
+
+		req->complete = fn;
+		list_add_tail(&req->list_pool, head);
+	}
+
+	return list_empty(head) ? -ENOMEM : 0;
+}
+
+static void
+xhci_dbc_free_requests(struct dbc_ep *dep, struct list_head *head)
+{
+	struct dbc_request	*req;
+
+	while (!list_empty(head)) {
+		req = list_entry(head->next, struct dbc_request, list_pool);
+		list_del(&req->list_pool);
+		xhci_dbc_free_req(dep, req);
+	}
+}
+
+static int dbc_tty_install(struct tty_driver *driver, struct tty_struct *tty)
+{
+	struct dbc_port		*port = driver->driver_state;
+
+	tty->driver_data = port;
+
+	return tty_port_install(&port->port, driver, tty);
+}
+
+static int dbc_tty_open(struct tty_struct *tty, struct file *file)
+{
+	struct dbc_port		*port = tty->driver_data;
+
+	return tty_port_open(&port->port, tty, file);
+}
+
+static void dbc_tty_close(struct tty_struct *tty, struct file *file)
+{
+	struct dbc_port		*port = tty->driver_data;
+
+	tty_port_close(&port->port, tty, file);
+}
+
+static int dbc_tty_write(struct tty_struct *tty,
+			 const unsigned char *buf,
+			 int count)
+{
+	struct dbc_port		*port = tty->driver_data;
+	unsigned long		flags;
+
+	spin_lock_irqsave(&port->port_lock, flags);
+	if (count)
+		count = kfifo_in(&port->write_fifo, buf, count);
+	dbc_start_tx(port);
+	spin_unlock_irqrestore(&port->port_lock, flags);
+
+	return count;
+}
+
+static int dbc_tty_put_char(struct tty_struct *tty, unsigned char ch)
+{
+	struct dbc_port		*port = tty->driver_data;
+	unsigned long		flags;
+	int			status;
+
+	spin_lock_irqsave(&port->port_lock, flags);
+	status = kfifo_put(&port->write_fifo, ch);
+	spin_unlock_irqrestore(&port->port_lock, flags);
+
+	return status;
+}
+
+static void dbc_tty_flush_chars(struct tty_struct *tty)
+{
+	struct dbc_port		*port = tty->driver_data;
+	unsigned long		flags;
+
+	spin_lock_irqsave(&port->port_lock, flags);
+	dbc_start_tx(port);
+	spin_unlock_irqrestore(&port->port_lock, flags);
+}
+
+static int dbc_tty_write_room(struct tty_struct *tty)
+{
+	struct dbc_port		*port = tty->driver_data;
+	unsigned long		flags;
+	int			room = 0;
+
+	spin_lock_irqsave(&port->port_lock, flags);
+	room = kfifo_avail(&port->write_fifo);
+	spin_unlock_irqrestore(&port->port_lock, flags);
+
+	return room;
+}
+
+static int dbc_tty_chars_in_buffer(struct tty_struct *tty)
+{
+	struct dbc_port		*port = tty->driver_data;
+	unsigned long		flags;
+	int			chars = 0;
+
+	spin_lock_irqsave(&port->port_lock, flags);
+	chars = kfifo_len(&port->write_fifo);
+	spin_unlock_irqrestore(&port->port_lock, flags);
+
+	return chars;
+}
+
+static void dbc_tty_unthrottle(struct tty_struct *tty)
+{
+	struct dbc_port		*port = tty->driver_data;
+	unsigned long		flags;
+
+	spin_lock_irqsave(&port->port_lock, flags);
+	tasklet_schedule(&port->push);
+	spin_unlock_irqrestore(&port->port_lock, flags);
+}
+
+static const struct tty_operations dbc_tty_ops = {
+	.install		= dbc_tty_install,
+	.open			= dbc_tty_open,
+	.close			= dbc_tty_close,
+	.write			= dbc_tty_write,
+	.put_char		= dbc_tty_put_char,
+	.flush_chars		= dbc_tty_flush_chars,
+	.write_room		= dbc_tty_write_room,
+	.chars_in_buffer	= dbc_tty_chars_in_buffer,
+	.unthrottle		= dbc_tty_unthrottle,
+};
+
+static struct tty_driver *dbc_tty_driver;
+
+int xhci_dbc_tty_register_driver(struct xhci_hcd *xhci)
+{
+	int			status;
+	struct xhci_dbc		*dbc = xhci->dbc;
+
+	dbc_tty_driver = tty_alloc_driver(1, TTY_DRIVER_REAL_RAW |
+					  TTY_DRIVER_DYNAMIC_DEV);
+	if (IS_ERR(dbc_tty_driver)) {
+		status = PTR_ERR(dbc_tty_driver);
+		dbc_tty_driver = NULL;
+		return status;
+	}
+
+	dbc_tty_driver->driver_name = "dbc_serial";
+	dbc_tty_driver->name = "ttyDBC";
+
+	dbc_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
+	dbc_tty_driver->subtype = SERIAL_TYPE_NORMAL;
+	dbc_tty_driver->init_termios = tty_std_termios;
+	dbc_tty_driver->init_termios.c_cflag =
+			B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+	dbc_tty_driver->init_termios.c_ispeed = 9600;
+	dbc_tty_driver->init_termios.c_ospeed = 9600;
+	dbc_tty_driver->driver_state = &dbc->port;
+
+	tty_set_operations(dbc_tty_driver, &dbc_tty_ops);
+
+	status = tty_register_driver(dbc_tty_driver);
+	if (status) {
+		xhci_err(xhci,
+			 "can't register dbc tty driver, err %d\n", status);
+		put_tty_driver(dbc_tty_driver);
+		dbc_tty_driver = NULL;
+	}
+
+	return status;
+}
+
+void xhci_dbc_tty_unregister_driver(void)
+{
+	tty_unregister_driver(dbc_tty_driver);
+	put_tty_driver(dbc_tty_driver);
+	dbc_tty_driver = NULL;
+}
+
+static void dbc_rx_push(unsigned long _port)
+{
+	struct dbc_request	*req;
+	struct tty_struct	*tty;
+	bool			do_push = false;
+	bool			disconnect = false;
+	struct dbc_port		*port = (void *)_port;
+	struct list_head	*queue = &port->read_queue;
+
+	spin_lock_irq(&port->port_lock);
+	tty = port->port.tty;
+	while (!list_empty(queue)) {
+		req = list_first_entry(queue, struct dbc_request, list_pool);
+
+		if (tty && tty_throttled(tty))
+			break;
+
+		switch (req->status) {
+		case 0:
+			break;
+		case -ESHUTDOWN:
+			disconnect = true;
+			break;
+		default:
+			pr_warn("ttyDBC0: unexpected RX status %d\n",
+				req->status);
+			break;
+		}
+
+		if (req->actual) {
+			char		*packet = req->buf;
+			unsigned int	n, size = req->actual;
+			int		count;
+
+			n = port->n_read;
+			if (n) {
+				packet += n;
+				size -= n;
+			}
+
+			count = tty_insert_flip_string(&port->port, packet,
+						       size);
+			if (count)
+				do_push = true;
+			if (count != size) {
+				port->n_read += count;
+				break;
+			}
+			port->n_read = 0;
+		}
+
+		list_move(&req->list_pool, &port->read_pool);
+	}
+
+	if (do_push)
+		tty_flip_buffer_push(&port->port);
+
+	if (!list_empty(queue) && tty) {
+		if (!tty_throttled(tty)) {
+			if (do_push)
+				tasklet_schedule(&port->push);
+			else
+				pr_warn("ttyDBC0: RX not scheduled?\n");
+		}
+	}
+
+	if (!disconnect)
+		dbc_start_rx(port);
+
+	spin_unlock_irq(&port->port_lock);
+}
+
+static int dbc_port_activate(struct tty_port *_port, struct tty_struct *tty)
+{
+	struct dbc_port	*port = container_of(_port, struct dbc_port, port);
+
+	spin_lock_irq(&port->port_lock);
+	dbc_start_rx(port);
+	spin_unlock_irq(&port->port_lock);
+
+	return 0;
+}
+
+static const struct tty_port_operations dbc_port_ops = {
+	.activate =	dbc_port_activate,
+};
+
+static void
+xhci_dbc_tty_init_port(struct xhci_hcd *xhci, struct dbc_port *port)
+{
+	tty_port_init(&port->port);
+	spin_lock_init(&port->port_lock);
+	tasklet_init(&port->push, dbc_rx_push, (unsigned long)port);
+	INIT_LIST_HEAD(&port->read_pool);
+	INIT_LIST_HEAD(&port->read_queue);
+	INIT_LIST_HEAD(&port->write_pool);
+
+	port->in =		get_in_ep(xhci);
+	port->out =		get_out_ep(xhci);
+	port->port.ops =	&dbc_port_ops;
+	port->n_read =		0;
+}
+
+static void
+xhci_dbc_tty_exit_port(struct dbc_port *port)
+{
+	tasklet_kill(&port->push);
+	tty_port_destroy(&port->port);
+}
+
+int xhci_dbc_tty_register_device(struct xhci_hcd *xhci)
+{
+	int			ret;
+	struct device		*tty_dev;
+	struct xhci_dbc		*dbc = xhci->dbc;
+	struct dbc_port		*port = &dbc->port;
+
+	xhci_dbc_tty_init_port(xhci, port);
+	tty_dev = tty_port_register_device(&port->port,
+					   dbc_tty_driver, 0, NULL);
+	ret = IS_ERR_OR_NULL(tty_dev);
+	if (ret)
+		goto register_fail;
+
+	ret = kfifo_alloc(&port->write_fifo, DBC_WRITE_BUF_SIZE, GFP_KERNEL);
+	if (ret)
+		goto buf_alloc_fail;
+
+	ret = xhci_dbc_alloc_requests(port->in, &port->read_pool,
+				      dbc_read_complete);
+	if (ret)
+		goto request_fail;
+
+	ret = xhci_dbc_alloc_requests(port->out, &port->write_pool,
+				      dbc_write_complete);
+	if (ret)
+		goto request_fail;
+
+	port->registered = true;
+
+	return 0;
+
+request_fail:
+	xhci_dbc_free_requests(port->in, &port->read_pool);
+	xhci_dbc_free_requests(port->out, &port->write_pool);
+	kfifo_free(&port->write_fifo);
+
+buf_alloc_fail:
+	tty_unregister_device(dbc_tty_driver, 0);
+
+register_fail:
+	xhci_dbc_tty_exit_port(port);
+
+	xhci_err(xhci, "can't register tty port, err %d\n", ret);
+
+	return ret;
+}
+
+void xhci_dbc_tty_unregister_device(struct xhci_hcd *xhci)
+{
+	struct xhci_dbc		*dbc = xhci->dbc;
+	struct dbc_port		*port = &dbc->port;
+
+	tty_unregister_device(dbc_tty_driver, 0);
+	xhci_dbc_tty_exit_port(port);
+	port->registered = false;
+
+	kfifo_free(&port->write_fifo);
+	xhci_dbc_free_requests(get_out_ep(xhci), &port->read_pool);
+	xhci_dbc_free_requests(get_out_ep(xhci), &port->read_queue);
+	xhci_dbc_free_requests(get_in_ep(xhci), &port->write_pool);
+}
diff --git a/drivers/usb/host/xhci-trace.h b/drivers/usb/host/xhci-trace.h
index a3b57c781db16..67ff314a6f41a 100644
--- a/drivers/usb/host/xhci-trace.h
+++ b/drivers/usb/host/xhci-trace.h
@@ -23,6 +23,7 @@
 
 #include <linux/tracepoint.h>
 #include "xhci.h"
+#include "xhci-dbgcap.h"
 
 #define XHCI_MSG_MAX	500
 
@@ -155,6 +156,21 @@ DEFINE_EVENT(xhci_log_trb, xhci_queue_trb,
 	TP_ARGS(ring, trb)
 );
 
+DEFINE_EVENT(xhci_log_trb, xhci_dbc_handle_event,
+	TP_PROTO(struct xhci_ring *ring, struct xhci_generic_trb *trb),
+	TP_ARGS(ring, trb)
+);
+
+DEFINE_EVENT(xhci_log_trb, xhci_dbc_handle_transfer,
+	TP_PROTO(struct xhci_ring *ring, struct xhci_generic_trb *trb),
+	TP_ARGS(ring, trb)
+);
+
+DEFINE_EVENT(xhci_log_trb, xhci_dbc_gadget_ep_queue,
+	TP_PROTO(struct xhci_ring *ring, struct xhci_generic_trb *trb),
+	TP_ARGS(ring, trb)
+);
+
 DECLARE_EVENT_CLASS(xhci_log_virt_dev,
 	TP_PROTO(struct xhci_virt_device *vdev),
 	TP_ARGS(vdev),
@@ -478,6 +494,49 @@ DEFINE_EVENT(xhci_log_portsc, xhci_handle_port_status,
 	     TP_ARGS(portnum, portsc)
 );
 
+DECLARE_EVENT_CLASS(xhci_dbc_log_request,
+	TP_PROTO(struct dbc_request *req),
+	TP_ARGS(req),
+	TP_STRUCT__entry(
+		__field(struct dbc_request *, req)
+		__field(bool, dir)
+		__field(unsigned int, actual)
+		__field(unsigned int, length)
+		__field(int, status)
+	),
+	TP_fast_assign(
+		__entry->req = req;
+		__entry->dir = req->direction;
+		__entry->actual = req->actual;
+		__entry->length = req->length;
+		__entry->status = req->status;
+	),
+	TP_printk("%s: req %p length %u/%u ==> %d",
+		__entry->dir ? "bulk-in" : "bulk-out",
+		__entry->req, __entry->actual,
+		__entry->length, __entry->status
+	)
+);
+
+DEFINE_EVENT(xhci_dbc_log_request, xhci_dbc_alloc_request,
+	TP_PROTO(struct dbc_request *req),
+	TP_ARGS(req)
+);
+
+DEFINE_EVENT(xhci_dbc_log_request, xhci_dbc_free_request,
+	TP_PROTO(struct dbc_request *req),
+	TP_ARGS(req)
+);
+
+DEFINE_EVENT(xhci_dbc_log_request, xhci_dbc_queue_request,
+	TP_PROTO(struct dbc_request *req),
+	TP_ARGS(req)
+);
+
+DEFINE_EVENT(xhci_dbc_log_request, xhci_dbc_giveback_request,
+	TP_PROTO(struct dbc_request *req),
+	TP_ARGS(req)
+);
 #endif /* __XHCI_TRACE_H */
 
 /* this part must be outside header guard */
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index f25b4ce319657..76bb0cb4aba54 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -21,6 +21,7 @@
 #include "xhci-trace.h"
 #include "xhci-mtk.h"
 #include "xhci-debugfs.h"
+#include "xhci-dbgcap.h"
 
 #define DRIVER_AUTHOR "Sarah Sharp"
 #define DRIVER_DESC "'eXtensible' Host Controller (xHC) Driver"
@@ -622,6 +623,8 @@ int xhci_run(struct usb_hcd *hcd)
 	xhci_dbg_trace(xhci, trace_xhci_dbg_init,
 			"Finished xhci_run for USB2 roothub");
 
+	xhci_dbc_init(xhci);
+
 	xhci_debugfs_init(xhci);
 
 	return 0;
@@ -654,6 +657,8 @@ static void xhci_stop(struct usb_hcd *hcd)
 
 	xhci_debugfs_exit(xhci);
 
+	xhci_dbc_exit(xhci);
+
 	spin_lock_irq(&xhci->lock);
 	xhci->xhc_state |= XHCI_STATE_HALTED;
 	xhci->cmd_ring_state = CMD_RING_STATE_STOPPED;
@@ -870,6 +875,8 @@ int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup)
 			xhci->shared_hcd->state != HC_STATE_SUSPENDED)
 		return -EINVAL;
 
+	xhci_dbc_suspend(xhci);
+
 	/* Clear root port wake on bits if wakeup not allowed. */
 	if (!do_wakeup)
 		xhci_disable_port_wake_on_bits(xhci);
@@ -1065,6 +1072,8 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated)
 
 	spin_unlock_irq(&xhci->lock);
 
+	xhci_dbc_resume(xhci);
+
  done:
 	if (retval == 0) {
 		/* Resume root hubs only when have pending events. */
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index f1515500ed88c..e613344f050ac 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -1856,6 +1856,7 @@ struct xhci_hcd {
 	struct dentry		*debugfs_slots;
 	struct list_head	regset_list;
 
+	void			*dbc;
 	/* platform-specific data -- must come last */
 	unsigned long		priv[0] __aligned(sizeof(s64));
 };