Skip to contentSkip to conversation

usb: early: add support for early printk through USB3 debug port

PATCH Superseded Lu Baolu 5 participants 29 messages Oct 28, 2015 – Nov 4, 2015

Subsystems (5)

v1 of 8 Superseded Latest is v8 →

lore
Share & tools
lore

Lu Baolu

author
#0 lore raw

cover usb: early: add support for early printk through USB3 debug port

This patch series adds support for early printk through USB3 debug port. USB3 debug port is described in xHCI specification as an optional extended capability.

The first patch adds a file in debugfs, through which users can check whether the debug capability is supported by a specific host controller.

Patch 2 to 10 add the driver for xHCI debug capability. It interfaces with the register set and provides the required ops (read/write/control) to upper layers. Early printk is one consumer of these ops. The hooks for early printk are introduced in patch 9. This design is similar to what we have done in drivers/usb/early/ehci-dbgp.c.

Patch 11 is a minor change to usb_debug module. This change is required to bind usb_debug with the USB3 debug device.

Patch 12 is the design document and user guide.

Lu Baolu (12):
usb: xhci: expose xhci extended capabilities via debugfs
x86: fixmap: add permanent fixmap for xhci debug port
usb: xhci: dbc: probe and setup xhci debug capability
usb: xhci: dbc: add support for Intel xHCI dbc quirk
usb: xhci: dbc: add debug buffer
usb: xhci: dbc: add bulk out and bulk in interfaces
usb: xhci: dbc: handle dbc-configured exit
usb: xhci: dbc: handle endpoint stall
x86: early_printk: add USB3 debug port earlyprintk support
usb: xhci: dbc: add handshake between debug target and host
usb: serial: usb_debug: add support for dbc debug device
usb: doc: add document for xHCI DbC driver

 Documentation/kernel-parameters.txt |    1 +
 Documentation/usb/xhci-dbc.txt      |  325 ++++++++
 MAINTAINERS                         |    8 +
 arch/x86/Kconfig.debug              |   12 +
 arch/x86/include/asm/fixmap.h       |    4 +
 arch/x86/kernel/early_printk.c      |    5 +
 drivers/usb/early/Makefile          |    1 +
 drivers/usb/early/xhci-dbc.c        | 1407 +++++++++++++++++++++++++++++++++++
 drivers/usb/host/xhci-dbg.c         |  212 ++++++
 drivers/usb/host/xhci-ext-caps.h    |    9 +-
 drivers/usb/host/xhci.c             |   27 +-
 drivers/usb/host/xhci.h             |   10 +
 drivers/usb/serial/usb_debug.c      |   29 +-
 include/linux/usb/xhci-dbc.h        |  224 ++++++
 14 files changed, 2269 insertions(+), 5 deletions(-)
 create mode 100644 Documentation/usb/xhci-dbc.txt
 create mode 100644 drivers/usb/early/xhci-dbc.c
 create mode 100644 include/linux/usb/xhci-dbc.h
Signature · 1 line
2.1.4
author
#1 lore raw

PATCH 1/12 usb: xhci: expose xhci extended capabilities via debugfs

The xHCI host exports xHCI-specific extended capabilities utilizing a method similar to PCI extended capabilities. In many cases, users want to know whether a specific extended capability is supported by a host. Unfortunately, currently there's no existing mechanisms in the kernel to do this.

This patch exposes extended capabilities supported by the xHCI host via debugfs.

---
 drivers/usb/host/xhci-dbg.c      | 212 +++++++++++++++++++++++++++++++++++++++
 drivers/usb/host/xhci-ext-caps.h |   9 +-
 drivers/usb/host/xhci.c          |  27 ++++-
 drivers/usb/host/xhci.h          |  10 ++
 4 files changed, 256 insertions(+), 2 deletions(-)
drivers/usb/host/xhci-dbg.c +212 -0
index 74c42f7..d3dcfed 100644
@@ -20,6 +20,11 @@
  * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+#include <linux/debugfs.h>
+#include <linux/usb.h>
+
 #include "xhci.h"
 
 #define XHCI_INIT_VALUE 0x0
@@ -612,3 +617,210 @@ void xhci_dbg_trace(struct xhci_hcd *xhci, void (*trace)(struct va_format *),
 	va_end(args);
 }
 EXPORT_SYMBOL_GPL(xhci_dbg_trace);
+
+#ifdef CONFIG_DEBUG_FS
+struct debug_buffer {
+	ssize_t (*fill_func)(struct debug_buffer *);
+	struct usb_bus *bus;
+	struct mutex mutex;
+	size_t count;
+	char *output_buf;
+	size_t alloc_size;
+};
+
+static const char *get_extcap_desc(u32 cap_id)
+{
+	switch (cap_id) {
+	case XHCI_EXT_CAPS_LEGACY:
+		return "USB Legacy Support";
+	case XHCI_EXT_CAPS_PROTOCOL:
+		return "Supported Protocol";
+	case XHCI_EXT_CAPS_PM:
+		return "Extended Power Management";
+	case XHCI_EXT_CAPS_VIRT:
+		return "I/O Virtualization (xHCI-IOV)";
+	case XHCI_EXT_CAPS_ROUTE:
+		return "Message Interrupt";
+	case XHCI_EXT_CAPS_LOCALMEM:
+		return "Local Memory";
+	case XHCI_EXT_CAPS_DEBUG:
+		return "USB Debug Capability";
+	default:
+		if (XHCI_EXT_CAPS_VENDOR(XHCI_EXT_CAPS_ID(cap_id)))
+			return "Vendor Defined";
+		else
+			return "Unknown";
+	}
+}
+
+static ssize_t fill_extcap_buffer(struct debug_buffer *buf)
+{
+	__le32 __iomem	*addr;
+	struct usb_hcd	*hcd;
+	struct xhci_hcd	*xhci;
+	u32		offset, cap_id;
+	char		*next;
+	int		size, temp;
+	unsigned long	flags;
+	int		time_to_leave;
+
+	hcd = bus_to_hcd(buf->bus);
+	xhci = hcd_to_xhci(hcd);
+	next = buf->output_buf;
+	size = buf->alloc_size;
+
+	spin_lock_irqsave(&xhci->lock, flags);
+
+	addr = &xhci->cap_regs->hcc_params;
+	offset = XHCI_HCC_EXT_CAPS(readl(addr));
+	if (!HCD_HW_ACCESSIBLE(hcd) || !offset) {
+		size = scnprintf(next, size,
+			"bus %s, device %s\n%s\nNo extended capabilities\n",
+			hcd->self.controller->bus->name,
+			dev_name(hcd->self.controller),
+			hcd->product_desc);
+		goto done;
+	}
+
+	temp = scnprintf(next, size, "@addr(virt)\t\tCAP_ID\tDescription\n");
+	size -= temp;
+	next += temp;
+
+	addr = &xhci->cap_regs->hc_capbase + offset;
+	time_to_leave = XHCI_EXT_MAX_CAPID;
+	while (time_to_leave--) {
+		cap_id = readl(addr);
+		temp = scnprintf(next, size, "@%p\t%02x\t%s\n",
+			addr, XHCI_EXT_CAPS_ID(cap_id),
+			get_extcap_desc(XHCI_EXT_CAPS_ID(cap_id)));
+		size -= temp;
+		next += temp;
+
+		offset = XHCI_EXT_CAPS_NEXT(cap_id);
+		if (!offset)
+			break;
+		addr += offset;
+	}
+
+done:
+	spin_unlock_irqrestore(&xhci->lock, flags);
+
+	return buf->alloc_size - size;
+}
+
+static struct debug_buffer *buffer_init(struct usb_bus *bus,
+				ssize_t (*fill_func)(struct debug_buffer *))
+{
+	struct debug_buffer *buf;
+
+	buf = kzalloc(sizeof(struct debug_buffer), GFP_KERNEL);
+	if (!buf)
+		return NULL;
+
+	buf->bus = bus;
+	buf->fill_func = fill_func;
+	mutex_init(&buf->mutex);
+
+	return buf;
+}
+
+static int fill_buffer(struct debug_buffer *buf)
+{
+	int ret;
+
+	if (buf->output_buf)
+		return -EINVAL;
+
+	buf->alloc_size = PAGE_SIZE;
+	buf->output_buf = vmalloc(buf->alloc_size);
+
+	if (!buf->output_buf)
+		return -ENOMEM;
+
+	ret = buf->fill_func(buf);
+	if (ret < 0)
+		return ret;
+
+	buf->count = ret;
+
+	return 0;
+}
+
+static ssize_t debug_output(struct file *file, char __user *user_buf,
+			    size_t len, loff_t *offset)
+{
+	struct debug_buffer *buf = file->private_data;
+	int ret = 0;
+
+	mutex_lock(&buf->mutex);
+	if (!buf->count) {
+		ret = fill_buffer(buf);
+		if (ret) {
+			mutex_unlock(&buf->mutex);
+			return ret;
+		}
+	}
+	mutex_unlock(&buf->mutex);
+
+	return simple_read_from_buffer(user_buf, len, offset,
+				      buf->output_buf, buf->count);
+}
+
+static int debug_close(struct inode *inode, struct file *file)
+{
+	struct debug_buffer *buf = file->private_data;
+
+	if (buf) {
+		vfree(buf->output_buf);
+		kfree(buf);
+	}
+
+	return 0;
+}
+
+static int debug_extcap_open(struct inode *inode, struct file *file)
+{
+	file->private_data = buffer_init(inode->i_private,
+					  fill_extcap_buffer);
+
+	return file->private_data ? 0 : -ENOMEM;
+}
+
+static const struct file_operations debug_extcap_fops = {
+	.owner		= THIS_MODULE,
+	.open		= debug_extcap_open,
+	.read		= debug_output,
+	.release	= debug_close,
+	.llseek		= default_llseek,
+};
+
+struct dentry *xhci_debug_root;
+
+void xhci_create_debug_files(struct xhci_hcd *xhci)
+{
+	struct usb_bus *bus = &xhci_to_hcd(xhci)->self;
+	struct dentry *entry;
+
+	if (!xhci_debug_root)
+		return;
+
+	entry = debugfs_create_dir(bus->bus_name, xhci_debug_root);
+	if (!entry || IS_ERR(entry)) {
+		xhci_info(xhci, "failed to create debug dir");
+		return;
+	}
+	xhci->debug_dir = entry;
+
+	if (!debugfs_create_file("extcap", S_IRUGO,
+				xhci->debug_dir, bus,
+				&debug_extcap_fops))
+		xhci_info(xhci, "failed to create extcap debug file");
+}
+
+void xhci_remove_debug_files(struct xhci_hcd *xhci)
+{
+	debugfs_remove_recursive(xhci->debug_dir);
+	xhci->debug_dir = NULL;
+}
+
+#endif /* CONFIG_DEBUG_FS */
drivers/usb/host/xhci-ext-caps.h +8 -1
index 9fe3225..e233c90 100644
@@ -49,8 +49,15 @@
 #define XHCI_EXT_CAPS_PM	3
 #define XHCI_EXT_CAPS_VIRT	4
 #define XHCI_EXT_CAPS_ROUTE	5
-/* IDs 6-9 reserved */
+#define	XHCI_EXT_CAPS_LOCALMEM	6
+/* IDs 7-9 reserved */
 #define XHCI_EXT_CAPS_DEBUG	10
+/* IDs 192-255 vendor specific */
+#define	XHCI_EXT_CAPS_VEN_START	192
+#define	XHCI_EXT_CAPS_VEN_END	255
+#define	XHCI_EXT_CAPS_VENDOR(p)	(((p) >= XHCI_EXT_CAPS_VEN_START) && \
+				((p) <= XHCI_EXT_CAPS_VEN_END))
+#define	XHCI_EXT_MAX_CAPID	XHCI_EXT_CAPS_VEN_END
 /* USB Legacy Support Capability - section 7.1.1 */
 #define XHCI_HC_BIOS_OWNED	(1 << 16)
 #define XHCI_HC_OS_OWNED	(1 << 24)
drivers/usb/host/xhci.c +26 -1
index 6e7dc6f..ddcb4b7 100644
@@ -28,6 +28,7 @@
 #include <linux/slab.h>
 #include <linux/dmi.h>
 #include <linux/dma-mapping.h>
+#include <linux/debugfs.h>
 
 #include "xhci.h"
 #include "xhci-trace.h"
@@ -651,6 +652,11 @@ int xhci_run(struct usb_hcd *hcd)
 	}
 	xhci_dbg_trace(xhci, trace_xhci_dbg_init,
 			"Finished xhci_run for USB2 roothub");
+
+#ifdef CONFIG_DEBUG_FS
+	xhci_create_debug_files(xhci);
+#endif
+
 	return 0;
 }
 EXPORT_SYMBOL_GPL(xhci_run);
@@ -669,6 +675,10 @@ void xhci_stop(struct usb_hcd *hcd)
 	u32 temp;
 	struct xhci_hcd *xhci = hcd_to_xhci(hcd);
 
+#ifdef CONFIG_DEBUG_FS
+	xhci_remove_debug_files(xhci);
+#endif
+
 	if (xhci->xhc_state & XHCI_STATE_HALTED)
 		return;
 
@@ -5041,6 +5051,15 @@ static int __init xhci_hcd_init(void)
 	BUILD_BUG_ON(sizeof(struct xhci_intr_reg) != 8*32/8);
 	/* xhci_run_regs has eight fields and embeds 128 xhci_intr_regs */
 	BUILD_BUG_ON(sizeof(struct xhci_run_regs) != (8+8*128)*32/8);
+
+#ifdef CONFIG_DEBUG_FS
+	xhci_debug_root = debugfs_create_dir("xhci", usb_debug_root);
+	if (!xhci_debug_root || IS_ERR(xhci_debug_root)) {
+		debugfs_remove(xhci_debug_root);
+		xhci_debug_root = NULL;
+	}
+#endif
+
 	return 0;
 }
 
@@ -5048,7 +5067,13 @@ static int __init xhci_hcd_init(void)
  * If an init function is provided, an exit function must also be provided
  * to allow module unload.
  */
-static void __exit xhci_hcd_fini(void) { }
+static void __exit xhci_hcd_fini(void)
+{
+#ifdef CONFIG_DEBUG_FS
+	debugfs_remove(xhci_debug_root);
+	xhci_debug_root = NULL;
+#endif
+}
 
 module_init(xhci_hcd_init);
 module_exit(xhci_hcd_fini);
drivers/usb/host/xhci.h +10 -0
index be9048e..dc3a5f8 100644
@@ -1657,6 +1657,10 @@ struct xhci_hcd {
 	u32			port_status_u0;
 /* Compliance Mode Timer Triggered every 2 seconds */
 #define COMP_MODE_RCVRY_MSECS 2000
+	/* debug files */
+#ifdef CONFIG_DEBUG_FS
+	struct dentry		*debug_dir;
+#endif /* CONFIG_DEBUG_FS */
 };
 
 /* Platform specific overrides to generic XHCI hc_driver ops */
@@ -1743,6 +1747,12 @@ void xhci_dbg_ep_rings(struct xhci_hcd *xhci,
 void xhci_dbg_trace(struct xhci_hcd *xhci, void (*trace)(struct va_format *),
 			const char *fmt, ...);
 
+#ifdef CONFIG_DEBUG_FS
+extern struct dentry *xhci_debug_root;
+void xhci_create_debug_files(struct xhci_hcd *xhci);
+void xhci_remove_debug_files(struct xhci_hcd *xhci);
+#endif /* CONFIG_DEBUG_FS */
+
 /* xHCI memory management */
 void xhci_mem_cleanup(struct xhci_hcd *xhci);
 int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags);
Signature · 1 line
2.1.4
#2 lore raw

Re: [PATCH 01/12] usb: xhci: expose xhci extended capabilities via debugfs

Quoted 143 lines · Lu Baolu

The xHCI host exports xHCI-specific extended capabilities utilizing a method similar to PCI extended capabilities. In many cases, users want to know whether a specific extended capability is supported by a host. Unfortunately, currently there's no existing mechanisms in the kernel to do this.

This patch exposes extended capabilities supported by the xHCI host via debugfs.

---
 drivers/usb/host/xhci-dbg.c      | 212 +++++++++++++++++++++++++++++++++++++++
 drivers/usb/host/xhci-ext-caps.h |   9 +-
 drivers/usb/host/xhci.c          |  27 ++++-
 drivers/usb/host/xhci.h          |  10 ++
 4 files changed, 256 insertions(+), 2 deletions(-)
drivers/usb/host/xhci-dbg.c +111 -0
index 74c42f7..d3dcfed 100644
@@ -20,6 +20,11 @@
  * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+#include <linux/debugfs.h>
+#include <linux/usb.h>
+
 #include "xhci.h"
 
 #define XHCI_INIT_VALUE 0x0
@@ -612,3 +617,210 @@ void xhci_dbg_trace(struct xhci_hcd *xhci, void (*trace)(struct va_format *),
 	va_end(args);
 }
 EXPORT_SYMBOL_GPL(xhci_dbg_trace);
+
+#ifdef CONFIG_DEBUG_FS
+struct debug_buffer {
+	ssize_t (*fill_func)(struct debug_buffer *);
+	struct usb_bus *bus;
+	struct mutex mutex;
+	size_t count;
+	char *output_buf;
+	size_t alloc_size;
+};
+
+static const char *get_extcap_desc(u32 cap_id)
+{
+	switch (cap_id) {
+	case XHCI_EXT_CAPS_LEGACY:
+		return "USB Legacy Support";
+	case XHCI_EXT_CAPS_PROTOCOL:
+		return "Supported Protocol";
+	case XHCI_EXT_CAPS_PM:
+		return "Extended Power Management";
+	case XHCI_EXT_CAPS_VIRT:
+		return "I/O Virtualization (xHCI-IOV)";
+	case XHCI_EXT_CAPS_ROUTE:
+		return "Message Interrupt";
+	case XHCI_EXT_CAPS_LOCALMEM:
+		return "Local Memory";
+	case XHCI_EXT_CAPS_DEBUG:
+		return "USB Debug Capability";
+	default:
+		if (XHCI_EXT_CAPS_VENDOR(XHCI_EXT_CAPS_ID(cap_id)))
+			return "Vendor Defined";
+		else
+			return "Unknown";
+	}
+}
+
+static ssize_t fill_extcap_buffer(struct debug_buffer *buf)
+{
+	__le32 __iomem	*addr;
+	struct usb_hcd	*hcd;
+	struct xhci_hcd	*xhci;
+	u32		offset, cap_id;
+	char		*next;
+	int		size, temp;
+	unsigned long	flags;
+	int		time_to_leave;
+
+	hcd = bus_to_hcd(buf->bus);
+	xhci = hcd_to_xhci(hcd);
+	next = buf->output_buf;
+	size = buf->alloc_size;
+
+	spin_lock_irqsave(&xhci->lock, flags);
+
+	addr = &xhci->cap_regs->hcc_params;
+	offset = XHCI_HCC_EXT_CAPS(readl(addr));
+	if (!HCD_HW_ACCESSIBLE(hcd) || !offset) {
+		size = scnprintf(next, size,
+			"bus %s, device %s\n%s\nNo extended capabilities\n",
+			hcd->self.controller->bus->name,
+			dev_name(hcd->self.controller),
+			hcd->product_desc);
+		goto done;
+	}
+
+	temp = scnprintf(next, size, "@addr(virt)\t\tCAP_ID\tDescription\n");
+	size -= temp;
+	next += temp;
+
+	addr = &xhci->cap_regs->hc_capbase + offset;
+	time_to_leave = XHCI_EXT_MAX_CAPID;
+	while (time_to_leave--) {
+		cap_id = readl(addr);
+		temp = scnprintf(next, size, "@%p\t%02x\t%s\n",
+			addr, XHCI_EXT_CAPS_ID(cap_id),
+			get_extcap_desc(XHCI_EXT_CAPS_ID(cap_id)));
+		size -= temp;
+		next += temp;
+
+		offset = XHCI_EXT_CAPS_NEXT(cap_id);
+		if (!offset)
+			break;
+		addr += offset;
+	}
+
+done:
+	spin_unlock_irqrestore(&xhci->lock, flags);
+
+	return buf->alloc_size - size;
+}
+
+static struct debug_buffer *buffer_init(struct usb_bus *bus,
+				ssize_t (*fill_func)(struct debug_buffer *))
+{
+	struct debug_buffer *buf;
+
+	buf = kzalloc(sizeof(struct debug_buffer), GFP_KERNEL);
+	if (!buf)
+		return NULL;
+
+	buf->bus = bus;
+	buf->fill_func = fill_func;
+	mutex_init(&buf->mutex);
+
+	return buf;
+}

drivers/usb/host/xhci-dbg.c void xhci_dbg_trace(struct xhci_hcd *xhci, void (*trace)(struct va_format *), · line 735

Quoted 10 lines
drivers/usb/host/xhci-dbg.c +10 -0
+
+static int fill_buffer(struct debug_buffer *buf)
+{
+	int ret;
+
+	if (buf->output_buf)
+		return -EINVAL;
+
+	buf->alloc_size = PAGE_SIZE;
+	buf->output_buf = vmalloc(buf->alloc_size);

That really makes no sense. If you allocate exactly PAGE_SIZE, you should allocate a page.

Regards
Oliver

author
#3 lore raw

Re: [PATCH 01/12] usb: xhci: expose xhci extended capabilities via debugfs

Quoted 11 lines · Oliver Neukum
Quoted 9 lines

+static int fill_buffer(struct debug_buffer *buf)

drivers/usb/host/xhci-dbg.c void xhci_dbg_trace(struct xhci_hcd *xhci, void (*trace)(struct va_format *), · line 735

Quoted 8 lines
drivers/usb/host/xhci-dbg.c +8 -0
+{
+	int ret;
+
+	if (buf->output_buf)
+		return -EINVAL;
+
+	buf->alloc_size = PAGE_SIZE;
+	buf->output_buf = vmalloc(buf->alloc_size);

That really makes no sense. If you allocate exactly PAGE_SIZE, you should allocate a page.

Yes, I will change it in v2.

Quoted 3 lines

Regards
Oliver

Thanks,
Baolu

Quoted 3 lines
#4 lore raw

Re: [PATCH 01/12] usb: xhci: expose xhci extended capabilities via debugfs

In reply to Lu Baolu
Quoted 8 lines · Lu Baolu

The xHCI host exports xHCI-specific extended capabilities utilizing a method similar to PCI extended capabilities. In many cases, users want to know whether a specific extended capability is supported by a host. Unfortunately, currently there's no existing mechanisms in the kernel to do this.

This patch exposes extended capabilities supported by the xHCI host via debugfs.

Why not through sysfs so that all users can see them? Why debugfs, which really is only for "debugging"?

Quoted 26 lines
---
 drivers/usb/host/xhci-dbg.c      | 212 +++++++++++++++++++++++++++++++++++++++
 drivers/usb/host/xhci-ext-caps.h |   9 +-
 drivers/usb/host/xhci.c          |  27 ++++-
 drivers/usb/host/xhci.h          |  10 ++
 4 files changed, 256 insertions(+), 2 deletions(-)
drivers/usb/host/xhci-dbg.c +5 -0
index 74c42f7..d3dcfed 100644
@@ -20,6 +20,11 @@
  * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+#include <linux/debugfs.h>
+#include <linux/usb.h>
+
 #include "xhci.h"
 
 #define XHCI_INIT_VALUE 0x0

drivers/usb/host/xhci-dbg.c void xhci_dbg_trace(struct xhci_hcd *xhci, void (*trace)(struct va_format *), · line 621

Quoted 5 lines
drivers/usb/host/xhci-dbg.c +2 -0
@@ -612,3 +617,210 @@ void xhci_dbg_trace(struct xhci_hcd *xhci, void (*trace)(struct va_format *),
 	va_end(args);
 }
 EXPORT_SYMBOL_GPL(xhci_dbg_trace);
+
+#ifdef CONFIG_DEBUG_FS

You shouldn't ever need to #ifdef your code for CONFIG_DEBUG_FS, the functions work the same if it's enabled or not.

Quoted 16 lines
drivers/usb/host/xhci-dbg.c +16 -0
+struct debug_buffer {
+	ssize_t (*fill_func)(struct debug_buffer *);
+	struct usb_bus *bus;
+	struct mutex mutex;
+	size_t count;
+	char *output_buf;
+	size_t alloc_size;
+};
+
+static const char *get_extcap_desc(u32 cap_id)
+{
+	switch (cap_id) {
+	case XHCI_EXT_CAPS_LEGACY:
+		return "USB Legacy Support";
+	case XHCI_EXT_CAPS_PROTOCOL:
+		return "Supported Protocol";

drivers/usb/host/xhci-dbg.c void xhci_dbg_trace(struct xhci_hcd *xhci, void (*trace)(struct va_format *), · line 647

Quoted 10 lines
drivers/usb/host/xhci-dbg.c +10 -0
+	case XHCI_EXT_CAPS_PM:
+		return "Extended Power Management";
+	case XHCI_EXT_CAPS_VIRT:
+		return "I/O Virtualization (xHCI-IOV)";
+	case XHCI_EXT_CAPS_ROUTE:
+		return "Message Interrupt";
+	case XHCI_EXT_CAPS_LOCALMEM:
+		return "Local Memory";
+	case XHCI_EXT_CAPS_DEBUG:
+		return "USB Debug Capability";

This is a lot more stuff than just debug port, it should be in sysfs as individual files, not one big one that you somehow have to parse in order to determine this information.

Quoted 152 lines
drivers/usb/host/xhci-dbg.c +152 -0
+	default:
+		if (XHCI_EXT_CAPS_VENDOR(XHCI_EXT_CAPS_ID(cap_id)))
+			return "Vendor Defined";
+		else
+			return "Unknown";
+	}
+}
+
+static ssize_t fill_extcap_buffer(struct debug_buffer *buf)
+{
+	__le32 __iomem	*addr;
+	struct usb_hcd	*hcd;
+	struct xhci_hcd	*xhci;
+	u32		offset, cap_id;
+	char		*next;
+	int		size, temp;
+	unsigned long	flags;
+	int		time_to_leave;
+
+	hcd = bus_to_hcd(buf->bus);
+	xhci = hcd_to_xhci(hcd);
+	next = buf->output_buf;
+	size = buf->alloc_size;
+
+	spin_lock_irqsave(&xhci->lock, flags);
+
+	addr = &xhci->cap_regs->hcc_params;
+	offset = XHCI_HCC_EXT_CAPS(readl(addr));
+	if (!HCD_HW_ACCESSIBLE(hcd) || !offset) {
+		size = scnprintf(next, size,
+			"bus %s, device %s\n%s\nNo extended capabilities\n",
+			hcd->self.controller->bus->name,
+			dev_name(hcd->self.controller),
+			hcd->product_desc);
+		goto done;
+	}
+
+	temp = scnprintf(next, size, "@addr(virt)\t\tCAP_ID\tDescription\n");
+	size -= temp;
+	next += temp;
+
+	addr = &xhci->cap_regs->hc_capbase + offset;
+	time_to_leave = XHCI_EXT_MAX_CAPID;
+	while (time_to_leave--) {
+		cap_id = readl(addr);
+		temp = scnprintf(next, size, "@%p\t%02x\t%s\n",
+			addr, XHCI_EXT_CAPS_ID(cap_id),
+			get_extcap_desc(XHCI_EXT_CAPS_ID(cap_id)));
+		size -= temp;
+		next += temp;
+
+		offset = XHCI_EXT_CAPS_NEXT(cap_id);
+		if (!offset)
+			break;
+		addr += offset;
+	}
+
+done:
+	spin_unlock_irqrestore(&xhci->lock, flags);
+
+	return buf->alloc_size - size;
+}
+
+static struct debug_buffer *buffer_init(struct usb_bus *bus,
+				ssize_t (*fill_func)(struct debug_buffer *))
+{
+	struct debug_buffer *buf;
+
+	buf = kzalloc(sizeof(struct debug_buffer), GFP_KERNEL);
+	if (!buf)
+		return NULL;
+
+	buf->bus = bus;
+	buf->fill_func = fill_func;
+	mutex_init(&buf->mutex);
+
+	return buf;
+}
+
+static int fill_buffer(struct debug_buffer *buf)
+{
+	int ret;
+
+	if (buf->output_buf)
+		return -EINVAL;
+
+	buf->alloc_size = PAGE_SIZE;
+	buf->output_buf = vmalloc(buf->alloc_size);
+
+	if (!buf->output_buf)
+		return -ENOMEM;
+
+	ret = buf->fill_func(buf);
+	if (ret < 0)
+		return ret;
+
+	buf->count = ret;
+
+	return 0;
+}
+
+static ssize_t debug_output(struct file *file, char __user *user_buf,
+			    size_t len, loff_t *offset)
+{
+	struct debug_buffer *buf = file->private_data;
+	int ret = 0;
+
+	mutex_lock(&buf->mutex);
+	if (!buf->count) {
+		ret = fill_buffer(buf);
+		if (ret) {
+			mutex_unlock(&buf->mutex);
+			return ret;
+		}
+	}
+	mutex_unlock(&buf->mutex);
+
+	return simple_read_from_buffer(user_buf, len, offset,
+				      buf->output_buf, buf->count);
+}
+
+static int debug_close(struct inode *inode, struct file *file)
+{
+	struct debug_buffer *buf = file->private_data;
+
+	if (buf) {
+		vfree(buf->output_buf);
+		kfree(buf);
+	}
+
+	return 0;
+}
+
+static int debug_extcap_open(struct inode *inode, struct file *file)
+{
+	file->private_data = buffer_init(inode->i_private,
+					  fill_extcap_buffer);
+
+	return file->private_data ? 0 : -ENOMEM;
+}
+
+static const struct file_operations debug_extcap_fops = {
+	.owner		= THIS_MODULE,
+	.open		= debug_extcap_open,
+	.read		= debug_output,
+	.release	= debug_close,
+	.llseek		= default_llseek,
+};
+
+struct dentry *xhci_debug_root;
+
+void xhci_create_debug_files(struct xhci_hcd *xhci)

drivers/usb/host/xhci-dbg.c void xhci_dbg_trace(struct xhci_hcd *xhci, void (*trace)(struct va_format *), · line 809

Quoted 10 lines
drivers/usb/host/xhci-dbg.c +10 -0
+{
+	struct usb_bus *bus = &xhci_to_hcd(xhci)->self;
+	struct dentry *entry;
+
+	if (!xhci_debug_root)
+		return;
+
+	entry = debugfs_create_dir(bus->bus_name, xhci_debug_root);
+	if (!entry || IS_ERR(entry)) {
+		xhci_info(xhci, "failed to create debug dir");

Why are "errors" an info level?

drivers/usb/host/xhci-dbg.c void xhci_dbg_trace(struct xhci_hcd *xhci, void (*trace)(struct va_format *), · line 810

Quoted 1 line
drivers/usb/host/xhci-dbg.c +1 -0
+		return;

No, you just "failed" if debugfs is not enabled.

You don't need to do any real error checking here, just make the call and save off the dentry. If an error happened, it's NULL and you can't really do anything with that, and it's safe to pass back into debugfs for other things, so you are fine. Don't make debugfs interactions more complex than they have to be.

drivers/usb/host/xhci-dbg.c void xhci_dbg_trace(struct xhci_hcd *xhci, void (*trace)(struct va_format *), · line 817

Quoted 7 lines
drivers/usb/host/xhci-dbg.c +7 -0
+	}
+	xhci->debug_dir = entry;
+
+	if (!debugfs_create_file("extcap", S_IRUGO,
+				xhci->debug_dir, bus,
+				&debug_extcap_fops))
+		xhci_info(xhci, "failed to create extcap debug file");

Again, info level?

Quoted 43 lines
drivers/usb/host/xhci-dbg.c +9 -0
+}
+
+void xhci_remove_debug_files(struct xhci_hcd *xhci)
+{
+	debugfs_remove_recursive(xhci->debug_dir);
+	xhci->debug_dir = NULL;
+}
+
+#endif /* CONFIG_DEBUG_FS */
drivers/usb/host/xhci-ext-caps.h +8 -1
index 9fe3225..e233c90 100644
@@ -49,8 +49,15 @@
 #define XHCI_EXT_CAPS_PM	3
 #define XHCI_EXT_CAPS_VIRT	4
 #define XHCI_EXT_CAPS_ROUTE	5
-/* IDs 6-9 reserved */
+#define	XHCI_EXT_CAPS_LOCALMEM	6
+/* IDs 7-9 reserved */
 #define XHCI_EXT_CAPS_DEBUG	10
+/* IDs 192-255 vendor specific */
+#define	XHCI_EXT_CAPS_VEN_START	192
+#define	XHCI_EXT_CAPS_VEN_END	255
+#define	XHCI_EXT_CAPS_VENDOR(p)	(((p) >= XHCI_EXT_CAPS_VEN_START) && \
+				((p) <= XHCI_EXT_CAPS_VEN_END))
+#define	XHCI_EXT_MAX_CAPID	XHCI_EXT_CAPS_VEN_END
 /* USB Legacy Support Capability - section 7.1.1 */
 #define XHCI_HC_BIOS_OWNED	(1 << 16)
 #define XHCI_HC_OS_OWNED	(1 << 24)
drivers/usb/host/xhci.c +1 -0
index 6e7dc6f..ddcb4b7 100644
@@ -28,6 +28,7 @@
 #include <linux/slab.h>
 #include <linux/dmi.h>
 #include <linux/dma-mapping.h>
+#include <linux/debugfs.h>
 
 #include "xhci.h"
 #include "xhci-trace.h"

drivers/usb/host/xhci.c int xhci_run(struct usb_hcd *hcd) · line 658

Quoted 7 lines
drivers/usb/host/xhci.c +4 -0
@@ -651,6 +652,11 @@ int xhci_run(struct usb_hcd *hcd)
 	}
 	xhci_dbg_trace(xhci, trace_xhci_dbg_init,
 			"Finished xhci_run for USB2 roothub");
+
+#ifdef CONFIG_DEBUG_FS
+	xhci_create_debug_files(xhci);
+#endif

Don't ever put ifdefs in .c code if you can prevent it. Hint, you can prevent it here...

drivers/usb/host/xhci.c void xhci_stop(struct usb_hcd *hcd) · line 680

Quoted 11 lines
drivers/usb/host/xhci.c +4 -0
+
 	return 0;
 }
 EXPORT_SYMBOL_GPL(xhci_run);
@@ -669,6 +675,10 @@ void xhci_stop(struct usb_hcd *hcd)
 	u32 temp;
 	struct xhci_hcd *xhci = hcd_to_xhci(hcd);
 
+#ifdef CONFIG_DEBUG_FS
+	xhci_remove_debug_files(xhci);
+#endif

Same here.

Quoted 6 lines
drivers/usb/host/xhci.c +1 -0
+
 	if (xhci->xhc_state & XHCI_STATE_HALTED)
 		return;
 
@@ -5041,6 +5051,15 @@ static int __init xhci_hcd_init(void)
 	BUILD_BUG_ON(sizeof(struct xhci_intr_reg) != 8*32/8);

drivers/usb/host/xhci.c static int __init xhci_hcd_init(void) · line 5061

Quoted 10 lines
drivers/usb/host/xhci.c +8 -0
 	/* xhci_run_regs has eight fields and embeds 128 xhci_intr_regs */
 	BUILD_BUG_ON(sizeof(struct xhci_run_regs) != (8+8*128)*32/8);
+
+#ifdef CONFIG_DEBUG_FS
+	xhci_debug_root = debugfs_create_dir("xhci", usb_debug_root);
+	if (!xhci_debug_root || IS_ERR(xhci_debug_root)) {
+		debugfs_remove(xhci_debug_root);
+		xhci_debug_root = NULL;
+	}
+#endif

And same here.

Quoted 5 lines
drivers/usb/host/xhci.c +1 -0
+
 	return 0;
 }
 

drivers/usb/host/xhci.c static int __init xhci_hcd_init(void) · line 5075

Quoted 10 lines
drivers/usb/host/xhci.c +6 -1
@@ -5048,7 +5067,13 @@ static int __init xhci_hcd_init(void)
  * If an init function is provided, an exit function must also be provided
  * to allow module unload.
  */
-static void __exit xhci_hcd_fini(void) { }
+static void __exit xhci_hcd_fini(void)
+{
+#ifdef CONFIG_DEBUG_FS
+	debugfs_remove(xhci_debug_root);
+	xhci_debug_root = NULL;
+#endif

And here.

Quoted 9 lines
drivers/usb/host/xhci.c +1 -0
+}
 
 module_init(xhci_hcd_init);
 module_exit(xhci_hcd_fini);
drivers/usb/host/xhci.h +0 -0
index be9048e..dc3a5f8 100644

drivers/usb/host/xhci.h struct xhci_hcd { · line 1663

Quoted 7 lines
drivers/usb/host/xhci.h +4 -0
@@ -1657,6 +1657,10 @@ struct xhci_hcd {
 	u32			port_status_u0;
 /* Compliance Mode Timer Triggered every 2 seconds */
 #define COMP_MODE_RCVRY_MSECS 2000
+	/* debug files */
+#ifdef CONFIG_DEBUG_FS
+	struct dentry		*debug_dir;
+#endif /* CONFIG_DEBUG_FS */

No need for ifdef here as well.

drivers/usb/host/xhci.h void xhci_dbg_ep_rings(struct xhci_hcd *xhci, · line 1754

Quoted 12 lines
drivers/usb/host/xhci.h +5 -0
 };
 
 /* Platform specific overrides to generic XHCI hc_driver ops */
@@ -1743,6 +1747,12 @@ void xhci_dbg_ep_rings(struct xhci_hcd *xhci,
 void xhci_dbg_trace(struct xhci_hcd *xhci, void (*trace)(struct va_format *),
 			const char *fmt, ...);
 
+#ifdef CONFIG_DEBUG_FS
+extern struct dentry *xhci_debug_root;
+void xhci_create_debug_files(struct xhci_hcd *xhci);
+void xhci_remove_debug_files(struct xhci_hcd *xhci);
+#endif /* CONFIG_DEBUG_FS */

Or here like this...

But as you are moving this to sysfs, it's kind of moot...

thanks,

greg k-h

author
#5 lore raw

Re: [PATCH 01/12] usb: xhci: expose xhci extended capabilities via debugfs

Quoted 21 lines · Greg Kroah-Hartman
Quoted 21 lines
Quoted 21 lines
drivers/usb/host/xhci-dbg.c +21 -0
+struct debug_buffer {
+	ssize_t (*fill_func)(struct debug_buffer *);
+	struct usb_bus *bus;
+	struct mutex mutex;
+	size_t count;
+	char *output_buf;
+	size_t alloc_size;
+};
+
+static const char *get_extcap_desc(u32 cap_id)
+{
+	switch (cap_id) {
+	case XHCI_EXT_CAPS_LEGACY:
+		return "USB Legacy Support";
+	case XHCI_EXT_CAPS_PROTOCOL:
+		return "Supported Protocol";
+	case XHCI_EXT_CAPS_PM:
+		return "Extended Power Management";
+	case XHCI_EXT_CAPS_VIRT:
+		return "I/O Virtualization (xHCI-IOV)";
+	case XHCI_EXT_CAPS_ROUTE:
Quoted 9 lines
Quoted 5 lines

drivers/usb/host/xhci-dbg.c void xhci_dbg_trace(struct xhci_hcd *xhci, void (*trace)(struct va_format *), · line 647

Quoted 5 lines
drivers/usb/host/xhci-dbg.c +5 -0
+		return "Message Interrupt";
+	case XHCI_EXT_CAPS_LOCALMEM:
+		return "Local Memory";
+	case XHCI_EXT_CAPS_DEBUG:
+		return "USB Debug Capability";

This is a lot more stuff than just debug port, it should be in sysfs as individual files, not one big one that you somehow have to parse in order to determine this information.

I will move it into sysfs in v2.

Thanks,
Baolu

author
#6 lore raw

Re: [PATCH 01/12] usb: xhci: expose xhci extended capabilities via debugfs

In reply to Greg Kroah-Hartman
Quoted 12 lines · Greg Kroah-Hartman
Quoted 12 lines

+static const char *get_extcap_desc(u32 cap_id)

Quoted 11 lines
drivers/usb/host/xhci-dbg.c +11 -0
+{
+	switch (cap_id) {
+	case XHCI_EXT_CAPS_LEGACY:
+		return "USB Legacy Support";
+	case XHCI_EXT_CAPS_PROTOCOL:
+		return "Supported Protocol";
+	case XHCI_EXT_CAPS_PM:
+		return "Extended Power Management";
+	case XHCI_EXT_CAPS_VIRT:
+		return "I/O Virtualization (xHCI-IOV)";
+	case XHCI_EXT_CAPS_ROUTE:
Quoted 9 lines
Quoted 5 lines

drivers/usb/host/xhci-dbg.c void xhci_dbg_trace(struct xhci_hcd *xhci, void (*trace)(struct va_format *), · line 647

Quoted 5 lines
drivers/usb/host/xhci-dbg.c +5 -0
+		return "Message Interrupt";
+	case XHCI_EXT_CAPS_LOCALMEM:
+		return "Local Memory";
+	case XHCI_EXT_CAPS_DEBUG:
+		return "USB Debug Capability";

This is a lot more stuff than just debug port, it should be in sysfs as individual files, not one big one that you somehow have to parse in order to determine this information.

Hi Greg,

It's hard to put each extended capability into a individual sysfs file.

The extended capabilities are optional. One extended capability might be supported in one hardware, but not in another. Also, there are many "vendor defined" capabilities (ID range 192-255). The vendor defined capabilities are not defined in xhci spec and they could be used by the hardware vendor for various purposes.

The purpose of this patch is to let user know what kind of extended capabilities does a host controller supported. For example, on one of my develop machines, it prints,

@addr(virt) CAP_ID Description

@ffffc90001c88000    02    Supported Protocol
@ffffc90001c88020    02    Supported Protocol
@ffffc90001c88070    c0    Vendor Defined
@ffffc90001c8846c    01    USB Legacy Support
@ffffc90001c884f4    c6    Vendor Defined
@ffffc90001c88500    c7    Vendor Defined
@ffffc90001c88600    c2    Vendor Defined
@ffffc90001c88700    0a    USB Debug Capability
@ffffc90001c88740    c3    Vendor Defined
@ffffc90001c88800    c4    Vendor Defined
@ffffc90001c88900    c5    Vendor Defined

With this output I know that "USB Debug Capability" is supported in my machine.

Thanks,
Baolu

#7 lore raw

Re: [PATCH 01/12] usb: xhci: expose xhci extended capabilities via debugfs

Quoted 25 lines · Baolu
Quoted 12 lines · Greg Kroah-Hartman
Quoted 12 lines

+static const char *get_extcap_desc(u32 cap_id)

Quoted 11 lines
drivers/usb/host/xhci-dbg.c +11 -0
+{
+	switch (cap_id) {
+	case XHCI_EXT_CAPS_LEGACY:
+		return "USB Legacy Support";
+	case XHCI_EXT_CAPS_PROTOCOL:
+		return "Supported Protocol";
+	case XHCI_EXT_CAPS_PM:
+		return "Extended Power Management";
+	case XHCI_EXT_CAPS_VIRT:
+		return "I/O Virtualization (xHCI-IOV)";
+	case XHCI_EXT_CAPS_ROUTE:
Quoted 9 lines
Quoted 5 lines

drivers/usb/host/xhci-dbg.c void xhci_dbg_trace(struct xhci_hcd *xhci, void (*trace)(struct va_format *), · line 647

Quoted 5 lines
drivers/usb/host/xhci-dbg.c +5 -0
+		return "Message Interrupt";
+	case XHCI_EXT_CAPS_LOCALMEM:
+		return "Local Memory";
+	case XHCI_EXT_CAPS_DEBUG:
+		return "USB Debug Capability";

This is a lot more stuff than just debug port, it should be in sysfs as individual files, not one big one that you somehow have to parse in order to determine this information.

Quoted 3 lines

Hi Greg,

It's hard to put each extended capability into a individual sysfs file.

Agreed.

Quoted 23 lines

The extended capabilities are optional. One extended capability might be supported in one hardware, but not in another. Also, there are many "vendor defined" capabilities (ID range 192-255). The vendor defined capabilities are not defined in xhci spec and they could be used by the hardware vendor for various purposes.

The purpose of this patch is to let user know what kind of extended capabilities does a host controller supported. For example, on one of my develop machines, it prints,

@addr(virt) CAP_ID Description

@ffffc90001c88000    02    Supported Protocol
@ffffc90001c88020    02    Supported Protocol
@ffffc90001c88070    c0    Vendor Defined
@ffffc90001c8846c    01    USB Legacy Support
@ffffc90001c884f4    c6    Vendor Defined
@ffffc90001c88500    c7    Vendor Defined
@ffffc90001c88600    c2    Vendor Defined
@ffffc90001c88700    0a    USB Debug Capability
@ffffc90001c88740    c3    Vendor Defined
@ffffc90001c88800    c4    Vendor Defined
@ffffc90001c88900    c5    Vendor Defined
Quoted 2 lines

With this output I know that "USB Debug Capability" is supported in my machine.

First off, why are you printing the address out? Userspace never needs to see that. Why not just iterate through the protocols and export the information as different files:
protocol_XX
and if the file is present or not describes if the hardware supports it or not.

The issue with debugfs is that it is not enabled on all systems, and only can be read by the root user, so it is hard for people to find this information out if they want to do normal things with the hardware.

But, if this really is only a debug thing, then it can say a debugfs file, but realize that almost no one will be able to see it (and even then, don't export the kernel addresses of the hardware.)

thanks,

greg k-h

author
#8 lore raw

Re: [PATCH 01/12] usb: xhci: expose xhci extended capabilities via debugfs

Quoted 61 lines · Greg Kroah-Hartman
Quoted 23 lines · Baolu
Quoted 12 lines · Greg Kroah-Hartman
Quoted 12 lines

+static const char *get_extcap_desc(u32 cap_id)

Quoted 11 lines
drivers/usb/host/xhci-dbg.c +11 -0
+{
+	switch (cap_id) {
+	case XHCI_EXT_CAPS_LEGACY:
+		return "USB Legacy Support";
+	case XHCI_EXT_CAPS_PROTOCOL:
+		return "Supported Protocol";
+	case XHCI_EXT_CAPS_PM:
+		return "Extended Power Management";
+	case XHCI_EXT_CAPS_VIRT:
+		return "I/O Virtualization (xHCI-IOV)";
+	case XHCI_EXT_CAPS_ROUTE:
Quoted 9 lines
Quoted 5 lines

drivers/usb/host/xhci-dbg.c void xhci_dbg_trace(struct xhci_hcd *xhci, void (*trace)(struct va_format *), · line 647

Quoted 5 lines
drivers/usb/host/xhci-dbg.c +5 -0
+		return "Message Interrupt";
+	case XHCI_EXT_CAPS_LOCALMEM:
+		return "Local Memory";
+	case XHCI_EXT_CAPS_DEBUG:
+		return "USB Debug Capability";

This is a lot more stuff than just debug port, it should be in sysfs as individual files, not one big one that you somehow have to parse in order to determine this information.

Quoted 3 lines

Hi Greg,

It's hard to put each extended capability into a individual sysfs file.

Agreed.

Quoted 23 lines

The extended capabilities are optional. One extended capability might be supported in one hardware, but not in another. Also, there are many "vendor defined" capabilities (ID range 192-255). The vendor defined capabilities are not defined in xhci spec and they could be used by the hardware vendor for various purposes.

The purpose of this patch is to let user know what kind of extended capabilities does a host controller supported. For example, on one of my develop machines, it prints,

@addr(virt) CAP_ID Description

@ffffc90001c88000    02    Supported Protocol
@ffffc90001c88020    02    Supported Protocol
@ffffc90001c88070    c0    Vendor Defined
@ffffc90001c8846c    01    USB Legacy Support
@ffffc90001c884f4    c6    Vendor Defined
@ffffc90001c88500    c7    Vendor Defined
@ffffc90001c88600    c2    Vendor Defined
@ffffc90001c88700    0a    USB Debug Capability
@ffffc90001c88740    c3    Vendor Defined
@ffffc90001c88800    c4    Vendor Defined
@ffffc90001c88900    c5    Vendor Defined
Quoted 2 lines

With this output I know that "USB Debug Capability" is supported in my machine.

First off, why are you printing the address out? Userspace never needs to see that. Why not just iterate through the protocols and export the information as different files:
protocol_XX
and if the file is present or not describes if the hardware supports it or not.

Quoted 3 lines

The issue with debugfs is that it is not enabled on all systems, and only can be read by the root user, so it is hard for people to find this information out if they want to do normal things with the hardware.

I agree with you that we should do this thru sysfs, not debugfs.

Quoted 4 lines

But, if this really is only a debug thing, then it can say a debugfs file, but realize that almost no one will be able to see it (and even then, don't export the kernel addresses of the hardware.)

I realized that not all extended capabilities are valuable to users.

As far as I can see, debug capability (hw implements USB3 debug port) is one that users should be interested at. Another one that could be valuable is USB 3.1 speed protocol (root ports that support USB 3.1 enhanced super speed).

For this time being, I will only implement sysfs node for debug port. I will make a separated patch for USB 3.1 speed port after I have Mathias' opinion.

Quoted 4 lines

thanks,

greg k-h

Thanks,
Baolu

Quoted 5 lines

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

author
#9 lore raw

PATCH 2/12 x86: fixmap: add permanent fixmap for xhci debug port

In reply to Lu Baolu

xHCI compatible USB3 host controller may provide debug capability which enables low-level system debug over USB. In order to probing this debug capability, Linux kernel needs to map and access the mmio of the host controller during early boot.

This patch adds permenent fixmap pages in fixed_addresses table for xHCI mmio access.

---
 arch/x86/include/asm/fixmap.h | 4 ++++
 1 file changed, 4 insertions(+)
arch/x86/include/asm/fixmap.h +4 -0
index f80d700..fbf452f 100644
@@ -82,6 +82,10 @@ enum fixed_addresses {
 #ifdef CONFIG_PROVIDE_OHCI1394_DMA_INIT
 	FIX_OHCI1394_BASE,
 #endif
+#ifdef CONFIG_EARLY_PRINTK_XDBC
+	FIX_XDBC_BASE,
+	FIX_XDBC_END = FIX_XDBC_BASE + 15,
+#endif
 #ifdef CONFIG_X86_LOCAL_APIC
 	FIX_APIC_BASE,	/* local (CPU) APIC) -- required for SMP or not */
 #endif
Signature · 1 line
2.1.4
author
#10 lore raw

PATCH 3/12 usb: xhci: dbc: probe and setup xhci debug capability

In reply to Lu Baolu

xHCI debug capability (DbC) is an optional functionality provided by an xHCI host controller. Software learns this capability by walking through the extended capability list in mmio of the host.

This patch introduces the code to probe and initialize the debug capability hardware during early boot. With hardware initialization done, the debug target (system under debug which has DbC enabled) will present a debug device through the debug port. The debug device is fully compliant with the USB framework and provides the equivalent of a very high performance (USB3) full-duplex serial link between the debug host and target.

---
 MAINTAINERS                  |   7 +
 arch/x86/Kconfig.debug       |  12 +
 drivers/usb/early/Makefile   |   1 +
 drivers/usb/early/xhci-dbc.c | 787 +++++++++++++++++++++++++++++++++++++++++++
 include/linux/usb/xhci-dbc.h | 187 ++++++++++
 5 files changed, 994 insertions(+)
 create mode 100644 drivers/usb/early/xhci-dbc.c
 create mode 100644 include/linux/usb/xhci-dbc.h
MAINTAINERS +7 -0
index 0425167..585a369 100644
@@ -11040,6 +11040,13 @@ S:	Supported
 F:	drivers/usb/host/xhci*
 F:	drivers/usb/host/pci-quirks*
 
+USB XHCI DEBUG PORT
+M:	Lu Baolu <[email protected]>
+L:	[email protected]
+S:	Supported
+F:	drivers/usb/early/xhci-dbc.c
+F:	include/linux/usb/xhci-dbc.h
+
 USB ZD1201 DRIVER
 L:	[email protected]
 W:	http://linux-lc100020.sourceforge.net
arch/x86/Kconfig.debug +12 -0
index d8c0d32..8d95abd 100644
@@ -65,6 +65,18 @@ config EARLY_PRINTK_EFI
 	  This is useful for kernel debugging when your machine crashes very
 	  early before the console code is initialized.
 
+config EARLY_PRINTK_XDBC
+	bool "Early printk via xHCI debug port"
+	depends on EARLY_PRINTK && PCI
+	---help---
+	  Write kernel log output directly into the xHCI debug port.
+
+	  This is useful for kernel debugging when your machine crashes very
+	  early before the console code is initialized. For normal operation
+	  it is not recommended because it looks ugly and doesn't cooperate
+	  with klogd/syslogd or the X server. You should normally N here,
+	  unless you want to debug such a crash.
+
 config X86_PTDUMP
 	bool "Export kernel pagetable layout to userspace via debugfs"
 	depends on DEBUG_KERNEL
drivers/usb/early/Makefile +1 -0
index 24bbe51..2db5906 100644
@@ -3,3 +3,4 @@
 #
 
 obj-$(CONFIG_EARLY_PRINTK_DBGP) += ehci-dbgp.o
+obj-$(CONFIG_EARLY_PRINTK_XDBC) += xhci-dbc.o
drivers/usb/early/xhci-dbc.c new file +787 -0

787 lines not on this page · raw message

include/linux/usb/xhci-dbc.h new file +187 -0

187 lines not on this page · raw message

Signature · 1 line
2.1.4
author
#11 lore raw

PATCH 4/12 usb: xhci: dbc: add support for Intel xHCI dbc quirk

In reply to Lu Baolu

On Intel platform, if the debug target is connected with debug host, enabling DCE bit in command register leads to a hung bus state. In the hung state, the host system will not see a port connected status bit set. Hence debug target fails to be probed.

The state could be resolved by performing a port reset to the debug port from the host xHCI. This patch introduces this work around.

---
 drivers/usb/early/xhci-dbc.c | 52 ++++++++++++++++++++++++++++++++++++++++++++
 include/linux/usb/xhci-dbc.h |  2 ++
 2 files changed, 54 insertions(+)
drivers/usb/early/xhci-dbc.c +52 -0
index 22a1de9..6b23f09 100644
@@ -255,6 +255,8 @@ static void __iomem *xdbc_map_pci_mmio(u32 bus,
 	xdbcp->bar = bar;
 	xdbcp->xhci_base = base;
 	xdbcp->xhci_length = sz64;
+	xdbcp->vendor = read_pci_config_16(bus, dev, func, PCI_VENDOR_ID);
+	xdbcp->device = read_pci_config_16(bus, dev, func, PCI_DEVICE_ID);
 
 	if (length)
 		*length = sz64;
@@ -651,6 +653,52 @@ static int xdbc_mem_init(void)
 	return 0;
 }
 
+static void xdbc_reset_debug_port_callback(int cap_offset, void *data)
+{
+	u8 major;
+	u32 val, port_offset, port_count;
+	u32 cap_length;
+	void __iomem *ops_reg;
+	void __iomem *portsc;
+	int i;
+
+	val = readl(xdbcp->xhci_base + cap_offset);
+	major = (u8) XHCI_EXT_PORT_MAJOR(val);
+
+	/* only reset super-speed port */
+	if (major != 0x3)
+		return;
+
+	val = readl(xdbcp->xhci_base + cap_offset + 8);
+	port_offset = XHCI_EXT_PORT_OFF(val);
+	port_count = XHCI_EXT_PORT_COUNT(val);
+	xdbc_trace("Extcap Port offset %d count %d\n",
+			port_offset, port_count);
+
+	cap_length = readl(xdbcp->xhci_base) & 0xff;
+	ops_reg = xdbcp->xhci_base + cap_length;
+
+	port_offset--;
+	for (i = port_offset; i < (port_offset + port_count); i++) {
+		portsc = ops_reg + 0x400 + i * 0x10;
+		val = readl(portsc);
+		/* reset the port if CCS bit is cleared */
+		if (!(val & 0x1))
+			writel(val | (1 << 4), portsc);
+	}
+}
+
+static void xdbc_reset_debug_port(void)
+{
+	xdbc_walk_excap(xdbcp->bus,
+			xdbcp->dev,
+			xdbcp->func,
+			XHCI_EXT_CAPS_PROTOCOL,
+			false,
+			xdbc_reset_debug_port_callback,
+			NULL);
+}
+
 /*
  * xdbc_start: start DbC
  *
@@ -669,6 +717,10 @@ static int xdbc_start(void)
 		return -ENODEV;
 	}
 
+	/* reset port to avoid bus hang */
+	if (xdbcp->vendor == PCI_VENDOR_ID_INTEL)
+		xdbc_reset_debug_port();
+
 	/* wait for port connection */
 	if (handshake(&xdbcp->xdbc_reg->portsc, PORTSC_CCS,
 			PORTSC_CCS, 5000000, 100) < 0) {
include/linux/usb/xhci-dbc.h +2 -0
index 153fb87..fc0ef9a 100644
@@ -128,6 +128,8 @@ struct xdbc_state {
 	u32		dev;
 	u32		func;
 	u8		bar;
+	u16		vendor;
+	u16		device;
 	void __iomem	*xhci_base;
 	size_t		xhci_length;
 #define	XDBC_PCI_MAX_BUSES		256
Signature · 1 line
2.1.4
author
#12 lore raw

PATCH 5/12 usb: xhci: dbc: add debug buffer

In reply to Lu Baolu

"printk" is not suitable for dbc debugging especially when console is in usage. This patch adds a debug buffer in dbc driver and puts the debug messages in this local buffer. The debug buffer could be dumped whenever the console is not in use. This part of code will not be visible unless DBC_DEBUG is defined.

---
 drivers/usb/early/xhci-dbc.c | 62 ++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 60 insertions(+), 2 deletions(-)
drivers/usb/early/xhci-dbc.c +60 -2
index 6b23f09..b36a527 100644
@@ -32,8 +32,64 @@ static struct xdbc_state xdbc_stat;
 static struct xdbc_state *xdbcp = &xdbc_stat;
 
 #ifdef DBC_DEBUG
-/* place holder */
-#define	xdbc_trace	printk
+#define	XDBC_DEBUG_BUF_SIZE	(PAGE_SIZE * 32)
+#define	MSG_MAX_LINE		128
+static char xdbc_debug_buf[XDBC_DEBUG_BUF_SIZE];
+static void xdbc_trace(const char *fmt, ...)
+{
+	int i, size;
+	va_list args;
+	static int pos;
+	char temp_buf[MSG_MAX_LINE];
+
+	if (pos >= XDBC_DEBUG_BUF_SIZE - 1)
+		return;
+
+	memset(temp_buf, 0, MSG_MAX_LINE);
+	va_start(args, fmt);
+	vsnprintf(temp_buf, MSG_MAX_LINE - 1, fmt, args);
+	va_end(args);
+
+	i = 0;
+	size = strlen(temp_buf);
+	while (i < size) {
+		xdbc_debug_buf[pos] = temp_buf[i];
+		pos++;
+		i++;
+
+		if (pos >= XDBC_DEBUG_BUF_SIZE - 1)
+			break;
+	}
+}
+
+static void xdbc_dump_debug_buffer(void)
+{
+	int index = 0;
+	int count = 0;
+	char dump_buf[MSG_MAX_LINE];
+
+	xdbc_trace("The end of DbC trace buffer\n");
+	pr_notice("DBC debug buffer:\n");
+	memset(dump_buf, 0, MSG_MAX_LINE);
+
+	while (index < XDBC_DEBUG_BUF_SIZE) {
+		if (!xdbc_debug_buf[index])
+			break;
+
+		if (xdbc_debug_buf[index] == '\n' ||
+				count >= MSG_MAX_LINE - 1) {
+			pr_notice("DBC: @%08x %s\n", index, dump_buf);
+			memset(dump_buf, 0, MSG_MAX_LINE);
+			count = 0;
+		} else {
+			dump_buf[count] = xdbc_debug_buf[index];
+			count++;
+		}
+
+		index++;
+	}
+}
+
 static void xdbc_dbg_dump_regs(char *str)
 {
 	if (!xdbcp->xdbc_reg) {
@@ -165,6 +221,7 @@ static void xdbc_dbg_dump_data(char *str)
 
 #else
 static inline void xdbc_trace(const char *fmt, ...) { }
+static inline void xdbc_dump_debug_buffer(void) { }
 static inline void xdbc_dbg_dump_regs(char *str) { }
 static inline void xdbc_dbg_dump_data(char *str) { }
 #endif	/* DBC_DEBUG */
@@ -832,6 +889,7 @@ int __init early_xdbc_init(char *s)
 		pr_notice("failed to setup xHCI DbC connection\n");
 		xdbcp->xhci_base = NULL;
 		xdbcp->xdbc_reg = NULL;
+		xdbc_dump_debug_buffer();
 		return ret;
 	}
 
Signature · 1 line
2.1.4
author
#13 lore raw

PATCH 6/12 usb: xhci: dbc: add bulk out and bulk in interfaces

In reply to Lu Baolu

This patch adds interfaces for bulk out and bulk in ops. These interfaces could be used to implement early printk bootconsole or hook to various system debuggers.

---
 drivers/usb/early/xhci-dbc.c | 373 +++++++++++++++++++++++++++++++++++++++++++
 include/linux/usb/xhci-dbc.h |  30 ++++
 2 files changed, 403 insertions(+)
drivers/usb/early/xhci-dbc.c +373 -0
index b36a527..f51daa4 100644
@@ -219,11 +219,21 @@ static void xdbc_dbg_dump_data(char *str)
 	xdbc_dbg_dump_string("String Descriptor:");
 }
 
+static void xdbc_dbg_dump_trb(struct xdbc_trb *trb, char *str)
+{
+	xdbc_trace("DBC trb: %s\n", str);
+	xdbc_trace("@%016llx %08x %08x %08x %08x\n", (u64)__pa(trb),
+				le32_to_cpu(trb->field[0]),
+				le32_to_cpu(trb->field[1]),
+				le32_to_cpu(trb->field[2]),
+				le32_to_cpu(trb->field[3]));
+}
 #else
 static inline void xdbc_trace(const char *fmt, ...) { }
 static inline void xdbc_dump_debug_buffer(void) { }
 static inline void xdbc_dbg_dump_regs(char *str) { }
 static inline void xdbc_dbg_dump_data(char *str) { }
+static inline void xdbc_dbg_dump_trb(struct xdbc_trb *trb, char *str) { }
 #endif	/* DBC_DEBUG */
 
 /*
@@ -334,6 +344,7 @@ static void *xdbc_get_page(dma_addr_t *dma_addr,
 	static char in_ring_page[PAGE_SIZE] __aligned(PAGE_SIZE);
 	static char out_ring_page[PAGE_SIZE] __aligned(PAGE_SIZE);
 	static char table_page[PAGE_SIZE] __aligned(PAGE_SIZE);
+	static char bulk_buf_page[PAGE_SIZE] __aligned(PAGE_SIZE);
 
 	switch (type) {
 	case XDBC_PAGE_EVENT:
@@ -348,6 +359,9 @@ static void *xdbc_get_page(dma_addr_t *dma_addr,
 	case XDBC_PAGE_TABLE:
 		virt = (void *)table_page;
 		break;
+	case XDBC_PAGE_BUFFER:
+		virt = (void *)bulk_buf_page;
+		break;
 	default:
 		return NULL;
 	}
@@ -707,6 +721,12 @@ static int xdbc_mem_init(void)
 	dev_info = cpu_to_le32((XDBC_DEVICE_REV << 16) | XDBC_PRODUCT_ID);
 	writel(dev_info, &xdbcp->xdbc_reg->devinfo2);
 
+	/* get and store the transfer buffer */
+	xdbcp->out_buf = xdbc_get_page(&xdbcp->out_dma,
+			XDBC_PAGE_BUFFER);
+	xdbcp->in_buf = xdbcp->out_buf + XDBC_MAX_PACKET;
+	xdbcp->in_dma = xdbcp->out_dma + XDBC_MAX_PACKET;
+
 	return 0;
 }
 
@@ -802,6 +822,9 @@ static int xdbc_start(void)
 
 	xdbc_trace("root hub port number %d\n", DCST_DPN(status));
 
+	xdbcp->in_ep_state = EP_RUNNING;
+	xdbcp->out_ep_state = EP_RUNNING;
+
 	xdbc_trace("DbC is running now, control 0x%08x\n",
 			readl(&xdbcp->xdbc_reg->control));
 
@@ -895,3 +918,353 @@ int __init early_xdbc_init(char *s)
 
 	return 0;
 }
+
+static void xdbc_queue_trb(struct xdbc_ring *ring,
+		u32 field1, u32 field2, u32 field3, u32 field4)
+{
+	struct xdbc_trb *trb, *link_trb;
+
+	trb = ring->enqueue;
+	trb->field[0] = cpu_to_le32(field1);
+	trb->field[1] = cpu_to_le32(field2);
+	trb->field[2] = cpu_to_le32(field3);
+	trb->field[3] = cpu_to_le32(field4);
+
+	xdbc_dbg_dump_trb(trb, "enqueue trb");
+
+	++(ring->enqueue);
+	if (ring->enqueue >= &ring->segment->trbs[TRBS_PER_SEGMENT - 1]) {
+		link_trb = ring->enqueue;
+		if (ring->cycle_state)
+			link_trb->field[3] |= cpu_to_le32(TRB_CYCLE);
+		else
+			link_trb->field[3] &= cpu_to_le32(~TRB_CYCLE);
+
+		ring->enqueue = ring->segment->trbs;
+		ring->cycle_state ^= 1;
+	}
+}
+
+static void xdbc_ring_doorbell(int target)
+{
+	writel(DOOR_BELL_TARGET(target), &xdbcp->xdbc_reg->doorbell);
+}
+
+static void xdbc_handle_port_status(struct xdbc_trb *evt_trb)
+{
+	u32 port_reg;
+
+	port_reg = readl(&xdbcp->xdbc_reg->portsc);
+
+	if (port_reg & PORTSC_CSC) {
+		xdbc_trace("%s: connect status change event\n", __func__);
+		writel(port_reg | PORTSC_CSC, &xdbcp->xdbc_reg->portsc);
+		port_reg = readl(&xdbcp->xdbc_reg->portsc);
+	}
+
+	if (port_reg & PORTSC_PRC) {
+		xdbc_trace("%s: port reset change event\n", __func__);
+		writel(port_reg | PORTSC_PRC, &xdbcp->xdbc_reg->portsc);
+		port_reg = readl(&xdbcp->xdbc_reg->portsc);
+	}
+
+	if (port_reg & PORTSC_PLC) {
+		xdbc_trace("%s: port link status change event\n", __func__);
+		writel(port_reg | PORTSC_PLC, &xdbcp->xdbc_reg->portsc);
+		port_reg = readl(&xdbcp->xdbc_reg->portsc);
+	}
+
+	if (port_reg & PORTSC_CEC) {
+		xdbc_trace("%s: config error change\n", __func__);
+		writel(port_reg | PORTSC_CEC, &xdbcp->xdbc_reg->portsc);
+		port_reg = readl(&xdbcp->xdbc_reg->portsc);
+	}
+}
+
+static void xdbc_handle_tx_event(struct xdbc_trb *evt_trb)
+{
+	u32 comp_code;
+	u32 tx_dma_high, tx_dma_low;
+	u64 in_dma, out_dma;
+	size_t remain_length;
+	int ep_id;
+
+	tx_dma_low = le32_to_cpu(evt_trb->field[0]);
+	tx_dma_high = le32_to_cpu(evt_trb->field[1]);
+	comp_code = GET_COMP_CODE(le32_to_cpu(evt_trb->field[2]));
+	remain_length = EVENT_TRB_LEN(le32_to_cpu(evt_trb->field[2]));
+	ep_id = TRB_TO_EP_ID(le32_to_cpu(evt_trb->field[3]));
+	in_dma = __pa(xdbcp->in_pending);
+	out_dma = __pa(xdbcp->out_pending);
+
+	/*
+	 * Possible Completion Codes for DbC Transfer Event are Success,
+	 * Stall Error, USB Transaction Error, Babble Detected Error,
+	 * TRB Error, Short Packet, Undefined Error, Event Ring Full Error,
+	 * and Vendor Defined Error. TRB error, undefined error and vendor
+	 * defined error will result in HOT/HIT set and be handled the same
+	 * way as Stall error.
+	 */
+	switch (comp_code) {
+	case COMP_SUCCESS:
+		remain_length = 0;
+	case COMP_SHORT_TX:
+		xdbc_trace("%s: endpoint %d remains %d bytes\n", __func__,
+			ep_id, remain_length);
+		break;
+	case COMP_TRB_ERR:
+	case COMP_BABBLE:
+	case COMP_TX_ERR:
+	case COMP_STALL:
+	default:
+		xdbc_trace("%s: endpoint %d halted\n", __func__, ep_id);
+		if (ep_id == XDBC_EPID_OUT)
+			xdbcp->out_ep_state = EP_HALTED;
+		if (ep_id == XDBC_EPID_IN)
+			xdbcp->in_ep_state = EP_HALTED;
+
+		break;
+	}
+
+	if (lower_32_bits(in_dma) == tx_dma_low &&
+			upper_32_bits(in_dma) == tx_dma_high) {
+		xdbcp->in_complete = comp_code;
+		xdbcp->in_complete_length =
+				(remain_length > xdbcp->in_length) ?
+				0 : xdbcp->in_length - remain_length;
+	}
+
+	if (lower_32_bits(out_dma) == tx_dma_low &&
+			upper_32_bits(out_dma) == tx_dma_high) {
+		xdbcp->out_complete = comp_code;
+		xdbcp->out_complete_length =
+				(remain_length > xdbcp->out_length) ?
+				0 : xdbcp->out_length - remain_length;
+	}
+}
+
+static void xdbc_handle_events(void)
+{
+	struct xdbc_trb *evt_trb;
+	bool update_erdp = false;
+
+	evt_trb = xdbcp->evt_ring.dequeue;
+	while ((le32_to_cpu(evt_trb->field[3]) & TRB_CYCLE) ==
+			xdbcp->evt_ring.cycle_state) {
+		/*
+		 * Memory barrier to ensure software sees the trbs
+		 * enqueued by hardware.
+		 */
+		rmb();
+
+		xdbc_dbg_dump_trb(evt_trb, "event trb");
+
+		/* FIXME: Handle more event types. */
+		switch ((le32_to_cpu(evt_trb->field[3]) & TRB_TYPE_BITMASK)) {
+		case TRB_TYPE(TRB_PORT_STATUS):
+			xdbc_handle_port_status(evt_trb);
+			break;
+		case TRB_TYPE(TRB_TRANSFER):
+			xdbc_handle_tx_event(evt_trb);
+			break;
+		default:
+			break;
+		}
+
+		/* advance to the next trb */
+		++(xdbcp->evt_ring.dequeue);
+		if (xdbcp->evt_ring.dequeue ==
+				&xdbcp->evt_seg.trbs[TRBS_PER_SEGMENT]) {
+			xdbcp->evt_ring.dequeue = xdbcp->evt_seg.trbs;
+			xdbcp->evt_ring.cycle_state ^= 1;
+		}
+
+		evt_trb = xdbcp->evt_ring.dequeue;
+		update_erdp = true;
+	}
+
+	/* update event ring dequeue pointer */
+	if (update_erdp)
+		xdbc_write64(__pa(xdbcp->evt_ring.dequeue),
+				&xdbcp->xdbc_reg->erdp);
+}
+
+/*
+ * Check and dispatch events in event ring. It also checks status
+ * of hardware. This function will be called from multiple threads.
+ * An atomic lock is applied to protect the access of event ring.
+ */
+static int xdbc_check_event(void)
+{
+	/* event ring is under checking by other thread? */
+	if (!test_bit(XDBC_ATOMIC_EVENT, &xdbcp->atomic_flags) &&
+			!test_and_set_bit(XDBC_ATOMIC_EVENT,
+			&xdbcp->atomic_flags))
+		return 0;
+
+	xdbc_handle_events();
+
+	test_and_clear_bit(XDBC_ATOMIC_EVENT, &xdbcp->atomic_flags);
+
+	return 0;
+}
+
+#define	BULK_IN_COMPLETED(p)	((xdbcp->in_pending == (p)) && \
+				 xdbcp->in_complete)
+#define	BULK_OUT_COMPLETED(p)	((xdbcp->out_pending == (p)) && \
+				 xdbcp->out_complete)
+
+/*
+ * Wait for a bulk-in or bulk-out transfer completion or timed out.
+ * Return count of the actually transferred bytes or error.
+ */
+static int xdbc_wait_until_bulk_done(struct xdbc_trb *trb, int loops)
+{
+	int timeout = 0;
+	bool read;
+
+	if (trb != xdbcp->in_pending &&
+			trb != xdbcp->out_pending)
+		return -EINVAL;
+
+	read = (trb == xdbcp->in_pending);
+
+	do {
+		if (xdbc_check_event() < 0)
+			break;
+
+		if (read && BULK_IN_COMPLETED(trb)) {
+			if (xdbcp->in_ep_state == EP_HALTED)
+				return -EAGAIN;
+			else
+				return xdbcp->in_complete_length;
+		}
+
+		if (!read && BULK_OUT_COMPLETED(trb)) {
+			if (xdbcp->out_ep_state == EP_HALTED)
+				return -EAGAIN;
+			else
+				return xdbcp->out_complete_length;
+		}
+
+		xdbc_udelay(10);
+	} while ((timeout++ < loops) || !loops);
+
+	return -EIO;
+}
+
+static int xdbc_bulk_transfer(void *data, int size, int loops, bool read)
+{
+	u64 addr;
+	u32 length, control;
+	struct xdbc_trb *trb;
+	struct xdbc_ring *ring;
+	u32 cycle;
+	int ret;
+
+	if (size > XDBC_MAX_PACKET) {
+		xdbc_trace("%s: bad parameter, size %d", __func__, size);
+		return -EINVAL;
+	}
+
+	ring = (read ? &xdbcp->in_ring : &xdbcp->out_ring);
+	trb = ring->enqueue;
+	cycle = ring->cycle_state;
+
+	length = TRB_LEN(size);
+	control = TRB_TYPE(TRB_NORMAL) | TRB_IOC;
+
+	if (cycle)
+		control &= cpu_to_le32(~TRB_CYCLE);
+	else
+		control |= cpu_to_le32(TRB_CYCLE);
+
+	if (read) {
+		memset(xdbcp->in_buf, 0, XDBC_MAX_PACKET);
+		addr = xdbcp->in_dma;
+
+		xdbcp->in_pending = trb;
+		xdbcp->in_length = size;
+		xdbcp->in_complete = 0;
+		xdbcp->in_complete_length = 0;
+	} else {
+		memcpy(xdbcp->out_buf, data, size);
+		addr = xdbcp->out_dma;
+
+		xdbcp->out_pending = trb;
+		xdbcp->out_length = size;
+		xdbcp->out_complete = 0;
+		xdbcp->out_complete_length = 0;
+	}
+
+	xdbc_queue_trb(ring, lower_32_bits(addr),
+			upper_32_bits(addr),
+			length, control);
+
+	/*
+	 * Memory barrier to ensure hardware sees the trbs
+	 * enqueued above.
+	 */
+	wmb();
+	if (cycle)
+		trb->field[3] |= cpu_to_le32(cycle);
+	else
+		trb->field[3] &= cpu_to_le32(~TRB_CYCLE);
+
+	xdbc_ring_doorbell(read ? IN_EP_DOORBELL : OUT_EP_DOORBELL);
+
+	ret = xdbc_wait_until_bulk_done(trb, loops);
+
+	if (read)
+		xdbcp->in_pending = NULL;
+	else
+		xdbcp->out_pending = NULL;
+
+	if (ret > 0) {
+		if (read)
+			memcpy(data, xdbcp->in_buf, size);
+		else
+			memset(xdbcp->out_buf, 0, XDBC_MAX_PACKET);
+	} else {
+		xdbc_trace("%s: bulk %s transfer results in error %d\n",
+				__func__, read ? "in" : "out", ret);
+	}
+
+	return ret;
+}
+
+int xdbc_bulk_read(void *data, int size, int loops)
+{
+	int ret;
+
+	do {
+		if (!test_bit(XDBC_ATOMIC_BULKIN, &xdbcp->atomic_flags) &&
+				!test_and_set_bit(XDBC_ATOMIC_BULKIN,
+				&xdbcp->atomic_flags))
+			break;
+	} while (1);
+
+	ret = xdbc_bulk_transfer(data, size, loops, true);
+
+	test_and_clear_bit(XDBC_ATOMIC_BULKIN, &xdbcp->atomic_flags);
+
+	return ret;
+}
+
+int xdbc_bulk_write(const char *bytes, int size)
+{
+	int ret;
+
+	do {
+		if (!test_bit(XDBC_ATOMIC_BULKOUT, &xdbcp->atomic_flags) &&
+				!test_and_set_bit(XDBC_ATOMIC_BULKOUT,
+				&xdbcp->atomic_flags))
+			break;
+	} while (1);
+
+	ret = xdbc_bulk_transfer((void *)bytes, size, XDBC_LOOPS, false);
+
+	test_and_clear_bit(XDBC_ATOMIC_BULKOUT, &xdbcp->atomic_flags);
+
+	return ret;
+}
include/linux/usb/xhci-dbc.h +30 -0
index fc0ef9a..289ba58 100644
@@ -120,8 +120,17 @@ enum xdbc_page_type {
 	XDBC_PAGE_TXIN,
 	XDBC_PAGE_TXOUT,
 	XDBC_PAGE_TABLE,
+	XDBC_PAGE_BUFFER,
 };
 
+enum xdbc_ep_state {
+	EP_DISABLED,
+	EP_RUNNING,
+	EP_HALTED,
+};
+#define	XDBC_EPID_OUT	2
+#define	XDBC_EPID_IN	1
+
 struct xdbc_state {
 	/* pci device info*/
 	u32		bus;
@@ -170,13 +179,34 @@ struct xdbc_state {
 	/* bulk OUT endpoint */
 	struct xdbc_ring	out_ring;
 	struct xdbc_segment	out_seg;
+	void			*out_buf;
+	dma_addr_t		out_dma;
+	struct xdbc_trb		*out_pending;		/* IN */
+	size_t			out_length;		/* IN */
+	u32			out_complete;		/* OUT */
+	size_t			out_complete_length;	/* OUT */
+	enum xdbc_ep_state	out_ep_state;
 
 	/* bulk IN endpoint */
 	struct xdbc_ring	in_ring;
 	struct xdbc_segment	in_seg;
+	void			*in_buf;
+	dma_addr_t		in_dma;
+	struct xdbc_trb		*in_pending;		/* IN */
+	size_t			in_length;		/* IN */
+	u32			in_complete;		/* OUT */
+	size_t			in_complete_length;	/* OUT */
+	enum xdbc_ep_state	in_ep_state;
+
+	/* atomic flags */
+	unsigned long		atomic_flags;
+#define	XDBC_ATOMIC_BULKOUT	0
+#define	XDBC_ATOMIC_BULKIN	1
+#define	XDBC_ATOMIC_EVENT	2
 };
 
 #define	XDBC_MAX_PACKET		1024
+#define	XDBC_LOOPS		1000
 
 /* door bell target */
 #define	OUT_EP_DOORBELL		0
Signature · 1 line
2.1.4
author
#14 lore raw

PATCH 7/12 usb: xhci: dbc: handle dbc-configured exit

In reply to Lu Baolu

DbC might exit configured state in some cases (refer to 7.6.4.4 in xHCI spec 1.1). Software needs detect and clear this situation by clearing DCCTRL.DCR and wait until the DbC configured before read or write oprations.

---
 drivers/usb/early/xhci-dbc.c | 28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)
drivers/usb/early/xhci-dbc.c +28 -0
index f51daa4..8a5a51f 100644
@@ -1153,6 +1153,29 @@ static int xdbc_wait_until_bulk_done(struct xdbc_trb *trb, int loops)
 	return -EIO;
 }
 
+static int xdbc_wait_until_dbc_configured(void)
+{
+	int timeout = 0;
+	u32 reg;
+
+	/* Port exits configured state */
+	reg = readl(&xdbcp->xdbc_reg->control);
+	if (!(reg & CTRL_DRC))
+		return 0;
+
+	/* clear run change bit (RW1C) */
+	writel(reg | CTRL_DRC, &xdbcp->xdbc_reg->control);
+
+	do {
+		if (readl(&xdbcp->xdbc_reg->control) & CTRL_DCR)
+			return 0;
+
+		xdbc_udelay(10);
+	} while (timeout++ < XDBC_LOOPS);
+
+	return -ETIMEDOUT;
+}
+
 static int xdbc_bulk_transfer(void *data, int size, int loops, bool read)
 {
 	u64 addr;
@@ -1167,6 +1190,11 @@ static int xdbc_bulk_transfer(void *data, int size, int loops, bool read)
 		return -EINVAL;
 	}
 
+	if (xdbc_wait_until_dbc_configured()) {
+		xdbc_trace("%s: hardware not ready\n", __func__);
+		return -EPERM;
+	}
+
 	ring = (read ? &xdbcp->in_ring : &xdbcp->out_ring);
 	trb = ring->enqueue;
 	cycle = ring->cycle_state;
Signature · 1 line
2.1.4
author
#15 lore raw

PATCH 8/12 usb: xhci: dbc: handle endpoint stall

In reply to Lu Baolu

In case of endpoint stall, software is able to detect the situation by reading DCCTRL.HIT or DCCTRL.HOT bits. DbC follows the normal USB framework to handle endpoint stall. When software detects endpoint stall situation, it should wait until endpoint is recovered before read or write oprations.

---
 drivers/usb/early/xhci-dbc.c | 36 ++++++++++++++++++++++++++++++++++++
 1 file changed, 36 insertions(+)
drivers/usb/early/xhci-dbc.c +36 -0
index 8a5a51f..aaf655f 100644
@@ -1176,6 +1176,37 @@ static int xdbc_wait_until_dbc_configured(void)
 	return -ETIMEDOUT;
 }
 
+static int xdbc_wait_until_epstall_cleared(bool read)
+{
+	int timeout = 0;
+
+	if (read) {
+		do {
+			if (!(readl(&xdbcp->xdbc_reg->control) & CTRL_HIT)) {
+				xdbcp->in_ep_state = EP_RUNNING;
+
+				return 0;
+			}
+
+			xdbcp->in_ep_state = EP_HALTED;
+			xdbc_udelay(10);
+		} while (timeout++ < XDBC_LOOPS);
+	} else {
+		do {
+			if (!(readl(&xdbcp->xdbc_reg->control) & CTRL_HOT)) {
+				xdbcp->out_ep_state = EP_RUNNING;
+
+				return 0;
+			}
+
+			xdbcp->out_ep_state = EP_HALTED;
+			xdbc_udelay(10);
+		} while (timeout++ < XDBC_LOOPS);
+	}
+
+	return -ETIMEDOUT;
+}
+
 static int xdbc_bulk_transfer(void *data, int size, int loops, bool read)
 {
 	u64 addr;
@@ -1195,6 +1226,11 @@ static int xdbc_bulk_transfer(void *data, int size, int loops, bool read)
 		return -EPERM;
 	}
 
+	if (xdbc_wait_until_epstall_cleared(read)) {
+		xdbc_trace("%s: endpoint not ready\n", __func__);
+		return -EPERM;
+	}
+
 	ring = (read ? &xdbcp->in_ring : &xdbcp->out_ring);
 	trb = ring->enqueue;
 	cycle = ring->cycle_state;
Signature · 1 line
2.1.4
author
#16 lore raw

PATCH 9/12 x86: early_printk: add USB3 debug port earlyprintk support

In reply to Lu Baolu

Add support for early printk by writing debug messages to the USB3 debug port. Users can use this type of early printk by specifying kernel parameter of "earlyprintk=xdbc". This gives users a chance of providing debug output.

---
 Documentation/kernel-parameters.txt |  1 +
 arch/x86/kernel/early_printk.c      |  5 +++++
 drivers/usb/early/xhci-dbc.c        | 43 +++++++++++++++++++++++++++++++++++++
 include/linux/usb/xhci-dbc.h        |  5 +++++
 4 files changed, 54 insertions(+)
Documentation/kernel-parameters.txt +1 -0
index 22a4b68..b65b07f 100644
@@ -1032,6 +1032,7 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
 			earlyprintk=ttySn[,baudrate]
 			earlyprintk=dbgp[debugController#]
 			earlyprintk=pciserial,bus:device.function[,baudrate]
+			earlyprintk=xdbc[xhciController#]
 
 			earlyprintk is useful when the kernel crashes before
 			the normal console is initialized. It is not enabled by
arch/x86/kernel/early_printk.c +5 -0
index eec40f5..5341a16 100644
@@ -17,6 +17,7 @@
 #include <asm/intel-mid.h>
 #include <asm/pgtable.h>
 #include <linux/usb/ehci_def.h>
+#include <linux/usb/xhci-dbc.h>
 #include <linux/efi.h>
 #include <asm/efi.h>
 #include <asm/pci_x86.h>
@@ -373,6 +374,10 @@ static int __init setup_early_printk(char *buf)
 		if (!strncmp(buf, "dbgp", 4) && !early_dbgp_init(buf + 4))
 			early_console_register(&early_dbgp_console, keep);
 #endif
+#ifdef CONFIG_EARLY_PRINTK_XDBC
+		if (!strncmp(buf, "xdbc", 4) && !early_xdbc_init(buf + 4))
+			early_console_register(&early_xdbc_console, keep);
+#endif
 #ifdef CONFIG_HVC_XEN
 		if (!strncmp(buf, "xen", 3))
 			early_console_register(&xenboot_console, keep);
drivers/usb/early/xhci-dbc.c +43 -0

52 lines not on this page · raw message

include/linux/usb/xhci-dbc.h +5 -0
index 289ba58..a556eb8 100644
@@ -216,4 +216,9 @@ struct xdbc_state {
 #define	xdbc_read64(regs)	xhci_read_64(NULL, (regs))
 #define	xdbc_write64(val, regs)	xhci_write_64(NULL, (val), (regs))
 
+#ifdef CONFIG_EARLY_PRINTK_XDBC
+extern int early_xdbc_init(char *s);
+extern struct console early_xdbc_console;
+#endif /* CONFIG_EARLY_PRINTK_XDBC */
+
 #endif /* __LINUX_XHCI_DBC_H */
Signature · 1 line
2.1.4
author
#17 lore raw

PATCH 10/12 usb: xhci: dbc: add handshake between debug target and host

In reply to Lu Baolu

After DbC setup, debug target needs to wait until tty driver and application (e.g. mincom) on debug taget start. Otherwise, out messages might be ignored.

This patch adds a ping/pong mechanism between debug target and host. Debug target will be waiting there until user presses 'Y' or 'y' in the tty application.

---
 drivers/usb/early/xhci-dbc.c | 27 +++++++++++++++++++++++++++
 1 file changed, 27 insertions(+)
drivers/usb/early/xhci-dbc.c +27 -0
index 68a7139..b75c523 100644
@@ -32,6 +32,9 @@
 static struct xdbc_state xdbc_stat;
 static struct xdbc_state *xdbcp = &xdbc_stat;
 
+static int early_xdbc_read(struct console *con, char *str, unsigned n);
+static void early_xdbc_write(struct console *con, const char *str, u32 n);
+
 #ifdef DBC_DEBUG
 #define	XDBC_DEBUG_BUF_SIZE	(PAGE_SIZE * 32)
 #define	MSG_MAX_LINE		128
@@ -873,8 +876,12 @@ int __init early_xdbc_init(char *s)
 {
 	u32 bus = 0, dev = 0, func = 0;
 	unsigned long dbgp_num = 0;
+	char *ping = "Press Y to continue...\n";
+	char pong[64];
+	size_t size;
 	u32 offset;
 	int ret;
+	int retry = 20;
 
 	if (!early_pci_allowed())
 		return -EPERM;
@@ -917,6 +924,21 @@ int __init early_xdbc_init(char *s)
 		return ret;
 	}
 
+	while (retry > 0) {
+		early_xdbc_write(NULL, ping, strlen(ping));
+		size = early_xdbc_read(NULL, pong, 64);
+		if (size > 0) {
+			xdbc_trace("%s: pong message: %s\n", __func__, pong);
+			if (pong[0] == 'Y' || pong[0] == 'y')
+				break;
+		} else {
+			xdbc_trace("%s: pong message error %d\n",
+				__func__, size);
+		}
+
+		retry--;
+	}
+
 	return 0;
 }
 
@@ -1338,6 +1360,11 @@ int xdbc_bulk_write(const char *bytes, int size)
  * Start a bulk-in or bulk-out transfer, wait until transfer completion
  * or error. Return the count of actually transferred bytes or error.
  */
+static int early_xdbc_read(struct console *con, char *str, unsigned n)
+{
+	return xdbc_bulk_read(str, n, 0);
+}
+
 static void early_xdbc_write(struct console *con, const char *str, u32 n)
 {
 	int chunk, ret;
Signature · 1 line
2.1.4
author
#18 lore raw

PATCH 11/12 usb: serial: usb_debug: add support for dbc debug device

In reply to Lu Baolu

This patch add dbc debug device support in usb_debug driver.

---
 drivers/usb/serial/usb_debug.c | 29 ++++++++++++++++++++++++++---
 1 file changed, 26 insertions(+), 3 deletions(-)
drivers/usb/serial/usb_debug.c +26 -3
index ca2fa5b..d4903b0 100644
@@ -32,7 +32,18 @@ static const struct usb_device_id id_table[] = {
 	{ USB_DEVICE(0x0525, 0x127a) },
 	{ },
 };
-MODULE_DEVICE_TABLE(usb, id_table);
+
+static const struct usb_device_id dbc_id_table[] = {
+	{ USB_DEVICE(0x1d6b, 0x0004) },
+	{ },
+};
+
+static const struct usb_device_id id_table_combined[] = {
+	{ USB_DEVICE(0x0525, 0x127a) },
+	{ USB_DEVICE(0x1d6b, 0x0004) },
+	{ },
+};
+MODULE_DEVICE_TABLE(usb, id_table_combined);
 
 /* This HW really does not support a serial break, so one will be
  * emulated when ever the break state is set to true.
@@ -71,9 +82,21 @@ static struct usb_serial_driver debug_device = {
 	.process_read_urb =	usb_debug_process_read_urb,
 };
 
+static struct usb_serial_driver dbc_device = {
+	.driver = {
+		.owner =	THIS_MODULE,
+		.name =		"xhci_dbc",
+	},
+	.id_table =		dbc_id_table,
+	.num_ports =		1,
+	.bulk_out_size =	1024,
+	.break_ctl =		usb_debug_break_ctl,
+	.process_read_urb =	usb_debug_process_read_urb,
+};
+
 static struct usb_serial_driver * const serial_drivers[] = {
-	&debug_device, NULL
+	&debug_device, &dbc_device, NULL
 };
 
-module_usb_serial_driver(serial_drivers, id_table);
+module_usb_serial_driver(serial_drivers, id_table_combined);
 MODULE_LICENSE("GPL");
Signature · 1 line
2.1.4
maintainer
#19 lore raw

Re: [PATCH 11/12] usb: serial: usb_debug: add support for dbc debug device

Quoted 7 lines · Lu Baolu

This patch add dbc debug device support in usb_debug driver.

---
 drivers/usb/serial/usb_debug.c | 29 ++++++++++++++++++++++++++---
 1 file changed, 26 insertions(+), 3 deletions(-)

drivers/usb/serial/usb_debug.c static struct usb_serial_driver debug_device = { · line 92

Quoted 8 lines
drivers/usb/serial/usb_debug.c +8 -0
+static struct usb_serial_driver dbc_device = {
+	.driver = {
+		.owner =	THIS_MODULE,
+		.name =		"xhci_dbc",
+	},
+	.id_table =		dbc_id_table,
+	.num_ports =		1,
+	.bulk_out_size =	1024,

No need to set this unless you need a larger (or smaller for ehci) buffer than the endpoint size.

Johan

author
#20 lore raw

Re: [PATCH 11/12] usb: serial: usb_debug: add support for dbc debug device

Quoted 8 lines · Johan Hovold
Quoted 6 lines · Lu Baolu

This patch add dbc debug device support in usb_debug driver.

---
  drivers/usb/serial/usb_debug.c | 29 ++++++++++++++++++++++++++---
  1 file changed, 26 insertions(+), 3 deletions(-)
Quoted 10 lines

drivers/usb/serial/usb_debug.c static struct usb_serial_driver debug_device = { · line 92

Quoted 8 lines
drivers/usb/serial/usb_debug.c +8 -0
+static struct usb_serial_driver dbc_device = {
+	.driver = {
+		.owner =	THIS_MODULE,
+		.name =		"xhci_dbc",
+	},
+	.id_table =		dbc_id_table,
+	.num_ports =		1,
+	.bulk_out_size =	1024,

No need to set this unless you need a larger (or smaller for ehci) buffer than the endpoint size.

I will remove it in v2. Thanks for pointing this out.

Quoted 3 lines

Johan

Thanks,
Baolu

#21 lore raw

Re: [PATCH 11/12] usb: serial: usb_debug: add support for dbc debug device

In reply to Lu Baolu
Quoted 18 lines · Lu Baolu

This patch add dbc debug device support in usb_debug driver.

---
 drivers/usb/serial/usb_debug.c | 29 ++++++++++++++++++++++++++---
 1 file changed, 26 insertions(+), 3 deletions(-)
drivers/usb/serial/usb_debug.c +2 -1
index ca2fa5b..d4903b0 100644
@@ -32,7 +32,18 @@ static const struct usb_device_id id_table[] = {
 	{ USB_DEVICE(0x0525, 0x127a) },
 	{ },
 };
-MODULE_DEVICE_TABLE(usb, id_table);
+
+static const struct usb_device_id dbc_id_table[] = {

drivers/usb/serial/usb_debug.c static const struct usb_device_id id_table[] = { · line 46

Quoted 10 lines
drivers/usb/serial/usb_debug.c +10 -0
+	{ USB_DEVICE(0x1d6b, 0x0004) },
+	{ },
+};
+
+static const struct usb_device_id id_table_combined[] = {
+	{ USB_DEVICE(0x0525, 0x127a) },
+	{ USB_DEVICE(0x1d6b, 0x0004) },
+	{ },
+};
+MODULE_DEVICE_TABLE(usb, id_table_combined);

You shouldn't need a "combined" module device table anymore, the module core was changed a while ago to remove that restriction, you should be able to just multiple exports of MODULE_DEVICE_TABLE and everything should "just work" on the export side. Now it might not work on the usb core side, but that's a different issue...

thanks,

greg k-h

author
#22 lore raw

Re: [PATCH 11/12] usb: serial: usb_debug: add support for dbc debug device

Quoted 26 lines · Greg Kroah-Hartman
Quoted 25 lines · Lu Baolu

This patch add dbc debug device support in usb_debug driver.

---
  drivers/usb/serial/usb_debug.c | 29 ++++++++++++++++++++++++++---
  1 file changed, 26 insertions(+), 3 deletions(-)
drivers/usb/serial/usb_debug.c +9 -1
index ca2fa5b..d4903b0 100644
@@ -32,7 +32,18 @@ static const struct usb_device_id id_table[] = {
  	{ USB_DEVICE(0x0525, 0x127a) },
  	{ },
  };
-MODULE_DEVICE_TABLE(usb, id_table);
+
+static const struct usb_device_id dbc_id_table[] = {
+	{ USB_DEVICE(0x1d6b, 0x0004) },
+	{ },
+};
+
+static const struct usb_device_id id_table_combined[] = {
+	{ USB_DEVICE(0x0525, 0x127a) },
+	{ USB_DEVICE(0x1d6b, 0x0004) },
Quoted 8 lines

drivers/usb/serial/usb_debug.c static const struct usb_device_id id_table[] = { · line 46

Quoted 3 lines
drivers/usb/serial/usb_debug.c +3 -0
+	{ },
+};
+MODULE_DEVICE_TABLE(usb, id_table_combined);

You shouldn't need a "combined" module device table anymore, the module core was changed a while ago to remove that restriction, you should be able to just multiple exports of MODULE_DEVICE_TABLE and everything should "just work" on the export side. Now it might not work on the usb core side, but that's a different issue...

Thanks for pointing this out. I will re-factor this code in v2 patch.

Quoted 5 lines

thanks,

greg k-h

author
#23 lore raw

Re: [PATCH 11/12] usb: serial: usb_debug: add support for dbc debug device

In reply to Greg Kroah-Hartman
Quoted 26 lines · Greg Kroah-Hartman
Quoted 25 lines · Lu Baolu

This patch add dbc debug device support in usb_debug driver.

---
  drivers/usb/serial/usb_debug.c | 29 ++++++++++++++++++++++++++---
  1 file changed, 26 insertions(+), 3 deletions(-)
drivers/usb/serial/usb_debug.c +9 -1
index ca2fa5b..d4903b0 100644
@@ -32,7 +32,18 @@ static const struct usb_device_id id_table[] = {
  	{ USB_DEVICE(0x0525, 0x127a) },
  	{ },
  };
-MODULE_DEVICE_TABLE(usb, id_table);
+
+static const struct usb_device_id dbc_id_table[] = {
+	{ USB_DEVICE(0x1d6b, 0x0004) },
+	{ },
+};
+
+static const struct usb_device_id id_table_combined[] = {
+	{ USB_DEVICE(0x0525, 0x127a) },
+	{ USB_DEVICE(0x1d6b, 0x0004) },
Quoted 8 lines

drivers/usb/serial/usb_debug.c static const struct usb_device_id id_table[] = { · line 46

Quoted 3 lines
drivers/usb/serial/usb_debug.c +3 -0
+	{ },
+};
+MODULE_DEVICE_TABLE(usb, id_table_combined);

You shouldn't need a "combined" module device table anymore, the module core was changed a while ago to remove that restriction, you should be able to just multiple exports of MODULE_DEVICE_TABLE and everything should "just work" on the export side. Now it might not work on the usb core side, but that's a different issue...

Before I dive into the serial driver code, can anybody tell me, if I remove the "combined" module device table, what should I specify the second parameter for module_usb_serial_driver()?

The previous declaration is,

module_usb_serial_driver(serial_drivers, id_table_combined);

Thanks,
Baolu

Quoted 5 lines

thanks,

greg k-h

#24 lore raw

Re: [PATCH 11/12] usb: serial: usb_debug: add support for dbc debug device

Quoted 38 lines · Baolu
Quoted 26 lines · Greg Kroah-Hartman
Quoted 25 lines · Lu Baolu

This patch add dbc debug device support in usb_debug driver.

---
 drivers/usb/serial/usb_debug.c | 29 ++++++++++++++++++++++++++---
 1 file changed, 26 insertions(+), 3 deletions(-)
drivers/usb/serial/usb_debug.c +9 -1
index ca2fa5b..d4903b0 100644
@@ -32,7 +32,18 @@ static const struct usb_device_id id_table[] = {
 	{ USB_DEVICE(0x0525, 0x127a) },
 	{ },
 };
-MODULE_DEVICE_TABLE(usb, id_table);
+
+static const struct usb_device_id dbc_id_table[] = {
+	{ USB_DEVICE(0x1d6b, 0x0004) },
+	{ },
+};
+
+static const struct usb_device_id id_table_combined[] = {
+	{ USB_DEVICE(0x0525, 0x127a) },
+	{ USB_DEVICE(0x1d6b, 0x0004) },
Quoted 8 lines

drivers/usb/serial/usb_debug.c static const struct usb_device_id id_table[] = { · line 46

Quoted 3 lines
drivers/usb/serial/usb_debug.c +3 -0
+	{ },
+};
+MODULE_DEVICE_TABLE(usb, id_table_combined);

You shouldn't need a "combined" module device table anymore, the module core was changed a while ago to remove that restriction, you should be able to just multiple exports of MODULE_DEVICE_TABLE and everything should "just work" on the export side. Now it might not work on the usb core side, but that's a different issue...

Quoted 7 lines

Before I dive into the serial driver code, can anybody tell me, if I remove the "combined" module device table, what should I specify the second parameter for module_usb_serial_driver()?

The previous declaration is,

module_usb_serial_driver(serial_drivers, id_table_combined);

Yeah, that's the issue I was alluding to here, maybe this will not work just yet for USB serial drivers, sorry to lead you down the wrong path. Your original patch should be fine for now.

thanks,

greg k-h

author
#25 lore raw

Re: [PATCH 11/12] usb: serial: usb_debug: add support for dbc debug device

Quoted 41 lines · Greg Kroah-Hartman
Quoted 40 lines · Baolu
Quoted 26 lines · Greg Kroah-Hartman
Quoted 25 lines · Lu Baolu

This patch add dbc debug device support in usb_debug driver.

---
  drivers/usb/serial/usb_debug.c | 29 ++++++++++++++++++++++++++---
  1 file changed, 26 insertions(+), 3 deletions(-)
drivers/usb/serial/usb_debug.c +9 -1
index ca2fa5b..d4903b0 100644
@@ -32,7 +32,18 @@ static const struct usb_device_id id_table[] = {
  	{ USB_DEVICE(0x0525, 0x127a) },
  	{ },
  };
-MODULE_DEVICE_TABLE(usb, id_table);
+
+static const struct usb_device_id dbc_id_table[] = {
+	{ USB_DEVICE(0x1d6b, 0x0004) },
+	{ },
+};
+
+static const struct usb_device_id id_table_combined[] = {
+	{ USB_DEVICE(0x0525, 0x127a) },
+	{ USB_DEVICE(0x1d6b, 0x0004) },
Quoted 8 lines

drivers/usb/serial/usb_debug.c static const struct usb_device_id id_table[] = { · line 46

Quoted 3 lines
drivers/usb/serial/usb_debug.c +3 -0
+	{ },
+};
+MODULE_DEVICE_TABLE(usb, id_table_combined);

You shouldn't need a "combined" module device table anymore, the module core was changed a while ago to remove that restriction, you should be able to just multiple exports of MODULE_DEVICE_TABLE and everything should "just work" on the export side. Now it might not work on the usb core side, but that's a different issue...

Before I dive into the serial driver code, can anybody tell me, if I remove the "combined" module device table, what should I specify the second parameter for module_usb_serial_driver()?

Quoted 6 lines
Quoted 3 lines

The previous declaration is,

module_usb_serial_driver(serial_drivers, id_table_combined);

Yeah, that's the issue I was alluding to here, maybe this will not work just yet for USB serial drivers, sorry to lead you down the wrong path. Your original patch should be fine for now.

Okay, I will keep that patch.

Quoted 4 lines

thanks,

greg k-h

Thanks,
Baolu

Quoted 6 lines

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/

author
#26 lore raw

PATCH 12/12 usb: doc: add document for xHCI DbC driver

In reply to Lu Baolu

Add Documentation/usb/xhci-dbc.txt. This document includes development status and usage guide for USB3 debug port.

---
 Documentation/usb/xhci-dbc.txt | 325 +++++++++++++++++++++++++++++++++++++++++
 MAINTAINERS                    |   1 +
 drivers/usb/early/xhci-dbc.c   |   3 +
 3 files changed, 329 insertions(+)
 create mode 100644 Documentation/usb/xhci-dbc.txt
Documentation/usb/xhci-dbc.txt new file +325 -0
new file mode 100644
index 0000000..94b75a9
@@ -0,0 +1,325 @@
+                    xHCI debug capability driver
+
+                 Lu Baolu <[email protected]>
+
+                    Last-updated: September 2015
+
+
+	Contents:
+	---------
+	* What is xHCI DbC?
+	* Debug topologies
+	* Debug stacks
+	* Port Multiplexing
+	* Hardware initialization
+	* External reset
+	* Port reset
+	* Interrupt/DMA/Memory during early boot
+	* Endpoint STALL
+	* Debug device information
+	* How to use DbC early printk?
+	* Limitations
+
+	What is xHCI DbC?
+	-----------------
+
+The xHCI Debugging Capability defined in section 7.6 of xHCI spec 1.1
+provides an optional functionality that enables low-level system debug
+over USB. It provides a means of connecting two systems where one system
+is a Debug Host and the other a Debug Target (System Under Test). The
+Debug Capability provides an interface that is completely independent
+of the xHCI interface. A Debug Target enumerates as a USB debug device
+to the Debug Host, allowing a Debug Host to access a Debug Target through
+the standard USB software stack.
+
+	Debug topologies
+	----------------
+
+Multiple Debug Targets may be attached to a single Debug Host. Debug
+Targets may be connected to any downstream facing port below a Debug
+Host (i.e. anywhere in the fabric, root port or external hub puts).
+A Debug Target may only connect to a Debug Host through a Root Hub port
+of the target. That means connection of a Debug Target to a Debug Host
+through the ports of an external hub is not supported.
+
+Below is a typical connection between Debug Host and Debug target. Two
+Debug targets are connected to a single Debug host.
+
+
+         ________________                ________________
+        |   Debug Host   |              |  Debug Target  |
+        |________________|              |________________|
+        |xHC without DbC |              |  xHC with DbC  |
+        |or DbC disabled |              |     enabled    |
+        |________________|              |________________|
+            |P1|  |p2|                      |P1|  |p2|
+            |__|  |__|                      |__|  |__|
+              |    |                         |
+              |    |_________________________|
+              |_
+                |
+         _______|________                ________________
+        |       HUB      |              |  Debug Target  |
+        |________________|              |________________|
+        | Superspeed hub |              |  xHC with DbC  |
+        |                |              |     enabled    |
+        |________________|              |________________|
+            |P1|  |p2|                      |P1|  |p2|
+            |__|  |__|                      |__|  |__|
+                   |                         |
+                   |_________________________|
+
+	Debug stacks
+	------------
+
+Below is a software stack diagram of both Debug Host and Debug Target.
+
+         ________________                ________________
+        |   Debug Host   |              |  Debug Target  |
+        |________________|              |________________|
+        |   debug App    |              |                |
+        |                |              | system debug   |
+        |   usb_debug    |              |     hooks      |
+        |                |              |                |
+        |    usbcore     |              |                |
+        |                |              |debug capability|
+        |    xhci_hcd    |              |     driver     |
+        |________________|              |________________|
+        |xHC without DbC |              |  xHC with DbC  |
+        |or DbC disabled |              |     enabled    |
+        |________________|              |________________|
+            |P1|  |p2|                      |P1|  |p2|
+            |__|  |__|                      |__|  |__|
+                   |                         |
+                   |_________________________|
+
+
+	Port Multiplexing
+	-----------------
+
+A debug port is always multiplexed with the first xHCI root hub port.
+Whenever debug capability is supported and enabled, and the first root
+hub port is detected to be connected to a downstream super-speed port
+of a Debug Host, the root hub port is assigned to the debug capability
+and operating in an upstream facing mode. Otherwise, all root hub ports
+act as normal downstream facing ports. When the root port is assigned
+to debug capability, it appears through the xHCI as a fully functional
+root hub port that never sees a device attach.
+
+	Hardware initialization
+	-----------------------
+
+xHCI debug capability is initialized during early boot. "early_param"
+micro provides one option. It also provides a global function so that
+early code can call it to initialize the hardware.
+
+int __init early_xdbc_init(char *s)
+
+early_param("string", early_xdbc_init)
+
+Keep in mind that things such as interrupt, system memory, DMA memory,
+PCI configure space access, etc., are all different from a normal device
+driver.
+
+Software probes the debug capability and all its memory and register
+interfaces by walking through the xHCI extended capability List. Debug
+capability is implemented with the capability ID of 10.
+
+After a xHCI debug capability interface was detected, software could
+initialize the hardware so that debug host can enumerate it as a debug
+device. The USB debug device provided by xHC debug capacity experiences
+three modes: disabled mode, enumeration mode and run mode. In each mode,
+software and xHC itself should complete required steps to move it to the
+next mode. Below diagram describes the evolution of each mode.
+
+      __________             ___________             ___________
+     |          |           |           |           |           |
+     |          |           |           |           |           |
+     | Disabled | DbC enable|Enumeration| DbC run   |    Run    |
+     |   mode   |---------->|    mode   |---------->|    mode   |
+     |          |flag 0 to 1|           |flag 0 to 1|           |
+     |          |           |           |           |           |
+     |          |           |           |           |           |
+     |__________|           |___________|           |___________|
+
+To change DbC mode from disabled to enumeration, software should
+1) allocate and initialize all DbC memory data structures. All data
+structures required by a DbC are defined in 7.6 of the spec 1.1;
+2) initialize the registers. All registers required to be initialized
+in disabled mode are defined in 7.6.4.1 of the spec 1.1. With data
+structure and registers initialized, software can Set the debug
+capability enable (DCE) bit to 1 in the Debug Capability Control
+Register (DCCTRL) and DbC enters enumeration mode as soon as this
+bit is set.
+
+DbC hardware is responsible to change DbC mode from enumerate to run.
+As soon as DbC enters enumeration mode, DbC appears as a normal USB
+device which can be enumerated by debug host. Hence, DbC hardware
+should support a default control endpoint, which responds to standard
+USB requests, e.g. SET_ADDRESS, GET_DESCRIPTOR, GET_CONFIGURATION, etc.
+
+After DbC has been configure by debug host, it enters run mode, it's
+the working mode.
+
+	External reset
+	--------------
+
+External reset means DbC reset caused by something outside of DbC hardware
+and software. The external reset sources depends on the DbC System Bus
+Reset(SBR) bit in status register.
+
+If SBR reads 1, the reset source includes,
+
+1) Assertion of chip hardware reset;
+2) System bus reset (e.g. the assertion of PCI RST#);
+3) Transition from the PCI PM D3hot to D0.
+
+If SBR reads 0, the reset source includes,
+
+1) Assertion of chip hardware reset;
+2) Assertion of host controller reset;
+3) Light host controller Reset.
+
+Resetting the DbC shall clear Debug Capability Enable(DCE) bit to 0.
+Software can determing the reset event during runtime by checking the
+DCE bit. The debug device enters disabled mode after reset. DbC driver
+needs to follow above steps to re-initialize the hardware and bring
+the debug device to run state.
+
+	Port reset
+	----------
+
+Debug port resets itself when it detects reset signal from the debug
+host. Software can determine the port reset event by reading the DbC
+port control and staus registers.
+
+The debug device enters enumeration state as soon as the reset signal
+completes. DbC driver should follow above steps to bring the debug
+device into running mode.
+
+	Interrupt/DMA/Memory during early boot
+	--------------------------------------
+
+The driver code needs to take special care during early boot, especially
+when it comes to memory allocation, interrupt, DMA, device MMIO and PCI
+configuration space. This section defines the interfaces used in DbC driver
+for the prior aspects.
+
+All events generated by DbC are put in the event ring, software will
+periodically poll the Event Ring Not Empty bit in the Debug Capability
+Status Register (DCST) to check pending events. To do this, DbC driver
+should 1)poll the event ring after a transfer trb queued and wait until
+transfer completes, or 2) start a thread to do the periodically poll.
+
+A segment of fixed virtual address is reserved for MMIO access purpose.
+Debug capability driver will map the MMIO physical address (exposed in
+PCI BAR) with this fixed virtual address segment.
+
+Debug Capability needs contiguous memory for DMA purpose. The driver
+reserves DMA memory by stating arrays of PAGE_SIZE and ask the compiler
+to align the arrays to PAGE_SIZE.
+
+Base on the following reasons, the driver uses an outb to port 0x80 as
+an I/O delay: timer subsystem might not be ready yet when DbC starts
+to initialize; DbC driver is independent of OS as possible as it can.
+
+	Endpoint STALL
+	--------------
+
+Endpoint STALL happens when data buffer error, parameter Error,
+TRB error, vendor defined error, or undefined error is detected.
+A transfer event will be generated to notify software.
+
+To clear the STALL situation, debug host will send a ClearFeature
+(ENDPOINT_HALT) request. DbC will clear the halt transfer ring
+flag, clear any internal endpoint state, and move the TR dequeue
+pointer to the next TRB in transfer ring.
+
+DbC does not support Soft Retry. Driver must check and determine
+whether to retry the failed transfer.
+
+	Debug device information
+	------------------------
+
+When debug target boots with xHCI debug capability enabled, it appears
+to debug host as a debug device. The debug device is built using one
+interface which declares two bulk endpoints: an IN and an OUT.
+
+class code:	0xdc (diagnostic device, assigned by USB-IF)
+subclass code:	0x02 (debug device, assigned by USB-IF)
+Manufacturer:	"Linux"
+Product:	"Remote GDB"
+Serial:		"0001"
+
+The USB device ID:
+idVendor:	0x1d6b (Linux Foundation)
+idProduct:	0x0004
+
+	How to use DbC early printk?
+	----------------------------
+
+Before using any kernel functionalities based on DbC, user needs to check
+whether Debug Capability is supported by the xHCI host in the system.
+Use "extcap" interface exposed in kernel debugfs.
+
+# cat /sys/kernel/debug/usb/xhci/<pci_bus_name>/extcap
+...
+@ffffc90001c88700       0a      USB Debug Capability
+...
+
+On debug target system, user needs to enable the following kernel
+config option:
+
+    CONFIG_PCI
+    CONFIG_EARLY_PRINTK
+    CONFIG_EARLY_PRINTK_XDBC
+
+Users also need to add below kernel parameter:
+
+    "earlyprintk=xdbc"
+
+If there are multiple xHCI controllers in the system, user can append
+a host contoller index to this kernel parameter. The index is started
+from 0.
+
+On debug host side, user needs to make sure usb_debug module is included.
+
+On some platforms, such as Intel, you need to disable auto-pm of usb
+subsystem on debug host when you are debugging with DbC.
+
+#echo on | tee /sys/bus/usb/devices/*/power/control
+
+Before starting the debug target, user needs to connect the debug port
+on debug target with a root port or port of external hub on the debug
+host. The cable used to connect these two ports should be a USB 3.0
+super-speed A-to-A debugging cable.
+
+During early boot of debug target, DbC hardware gets initialized. Debug
+host should be able to enuerate debug target as a debug device. Debug
+host will bind the debug device with usb_debug driver module and create
+a tty file for serial communication application.
+
+After tty file being created, user needs to open serial communication
+application, such as minicom. After configuring minicom to open the
+tty file created above, user should be able to see below welcome message
+in minicom:
+
+Press Y to continue...
+
+After 'Y' key is pressed, debug target will go ahead with booting and
+all early printk messages should be routed to the minicom on debug
+host.
+
+	Limitations
+	-----------
+
+Early printk through DbC has been verified to work with Intel Sunrise Point
+chip with below known issues:
+
+1. DbC debug device doesn't support suspend/resume. Users need to disable
+auto-pm of the host controller on debug host.
+
+2. Early prink "keep" option doesn't support by current phase.
+
+3. After several restarts of debug target, debug host might fail to read
+the device descriptor of debug device. Users need to restart the debug host.
MAINTAINERS +1 -0
index 585a369..eb48df1 100644
@@ -11046,6 +11046,7 @@ L:	[email protected]
 S:	Supported
 F:	drivers/usb/early/xhci-dbc.c
 F:	include/linux/usb/xhci-dbc.h
+F:	Documentation/usb/xhci-dbc.txt
 
 USB ZD1201 DRIVER
 L:	[email protected]
drivers/usb/early/xhci-dbc.c +3 -0
index b75c523..676fb41 100644
@@ -6,6 +6,9 @@
  * Author: Lu Baolu <[email protected]>
  * Some code shared with EHCI debug port and xHCI driver.
  *
+ * Please read Documentation/usb/xhci-dbc.txt before you start to develop
+ * or use code in this file.
+ *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
  * published by the Free Software Foundation.
Signature · 1 line
2.1.4
#27 lore raw
In reply to Lu Baolu
Quoted 13 lines · Lu Baolu

This patch series adds support for early printk through USB3 debug port. USB3 debug port is described in xHCI specification as an optional extended capability.

The first patch adds a file in debugfs, through which users can check whether the debug capability is supported by a specific host controller.

Patch 2 to 10 add the driver for xHCI debug capability. It interfaces with the register set and provides the required ops (read/write/control) to upper layers. Early printk is one consumer of these ops. The hooks for early printk are introduced in patch 9. This design is similar to what we have done in drivers/usb/early/ehci-dbgp.c.

Quoted 5 lines

Patch 11 is a minor change to usb_debug module. This change is required to bind usb_debug with the USB3 debug device.

Patch 12 is the design document and user guide.

Nice work, I want to try your patches. But I have only one machine with debug capability, I think I can use it as debug target. Can I use another machine with usb 3.0 but no debug capability as the debug host?

BTW, I hacked a cable according to usb 3 spec, cut vbus/d+/d-, cross wired ss pins.

Thanks
Dave

author
#28 lore raw
Quoted 14 lines · Dave Young
Quoted 13 lines · Lu Baolu

This patch series adds support for early printk through USB3 debug port. USB3 debug port is described in xHCI specification as an optional extended capability.

The first patch adds a file in debugfs, through which users can check whether the debug capability is supported by a specific host controller.

Patch 2 to 10 add the driver for xHCI debug capability. It interfaces with the register set and provides the required ops (read/write/control) to upper layers. Early printk is one consumer of these ops. The hooks for early printk are introduced in patch 9. This design is similar to what we have done in drivers/usb/early/ehci-dbgp.c.

Quoted 8 lines
Quoted 5 lines

Patch 11 is a minor change to usb_debug module. This change is required to bind usb_debug with the USB3 debug device.

Patch 12 is the design document and user guide.

Nice work, I want to try your patches. But I have only one machine with debug capability, I think I can use it as debug target. Can I use another machine with usb 3.0 but no debug capability as the debug host?

Yes, you can. The debug host doesn't require debug capability.

Please follow guide in patch 12 when you try it. It has been verified on Intel Skylake machine.

Quoted 3 lines

BTW, I hacked a cable according to usb 3 spec, cut vbus/d+/d-, cross wired ss pins.

Good luck. I just bought one from

http://www.datapro.net/products/usb-3-0-super-speed-a-a-debugging-cable.html.

Quoted 4 lines

Thanks
Dave