summaryrefslogtreecommitdiff
path: root/drivers/usb/host
diff options
context:
space:
mode:
authorNeil Chen <neilc@nvidia.com>2013-07-17 13:34:21 +0800
committerSuresh Mangipudi <smangipudi@nvidia.com>2013-11-10 21:29:05 -0800
commit06863e28f0a92be1731de963e2ec43d72b2889b9 (patch)
tree99fac01742ae5b78b53299e32b24d3801b56b627 /drivers/usb/host
parent62ec95291e1c977eeb3129a82f2522ad8e673f4e (diff)
USB: debugfs: USB test mode support by usb debugfs
To generate USB testmode pattern by accessing usb debugfs. It need to be enabled with enable "CONFIG_USB_DEBUG" in def_config. And disable autosuspend by sysfs before test as below: echo on > /sys/bus/usb/devices/.../power/control Bug 1323709 Change-Id: I2c9d4cb304435e9d36f11a97a8db2d2ffd582ecf Signed-off-by: Neil Chen <neilc@nvidia.com> Reviewed-on: http://git-master/r/303705 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Suresh Mangipudi <smangipudi@nvidia.com> GVS: Gerrit_Virtual_Submit Reviewed-by: Venkat Moganty <vmoganty@nvidia.com>
Diffstat (limited to 'drivers/usb/host')
-rw-r--r--drivers/usb/host/ehci-dbg.c129
-rw-r--r--drivers/usb/host/ehci-hcd.c2
2 files changed, 131 insertions, 0 deletions
diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c
index 5429d2645bbc..d4c0e95363f8 100644
--- a/drivers/usb/host/ehci-dbg.c
+++ b/drivers/usb/host/ehci-dbg.c
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2001-2002 by David Brownell
+ * Copyright (c) 2013 NVIDIA CORPORATION. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
@@ -18,6 +19,29 @@
/* this file is part of ehci-hcd.c */
+#define PTC_SHIFT 16
+#define PTC_MASK (0xF << PTC_SHIFT)
+#define TEST_MODE_DISABLE 0
+#define J_STATE 1
+#define K_STATE 2
+#define SE0_NAK 3
+#define PACKET 4
+#define FORCE_ENABLE_HS 5
+#define FORCE_ENABLE_FS 6
+#define FORCE_ENABLE_LS 7
+#define TESTMODES_SIZE 8
+
+static char *testmodes[TESTMODES_SIZE] = {
+ "TEST_MODE_DISABLE",
+ "J_STATE",
+ "K_STATE",
+ "SE0",
+ "PACKET",
+ "FORCE_ENABLE_HS",
+ "FORCE_ENABLE_FS",
+ "FORCE_ENABLE_LS"
+};
+
#ifdef DEBUG
/* check the values in the HCSPARAMS register
@@ -336,6 +360,9 @@ static inline void remove_debug_files (struct ehci_hcd *bus) { }
static int debug_async_open(struct inode *, struct file *);
static int debug_periodic_open(struct inode *, struct file *);
static int debug_registers_open(struct inode *, struct file *);
+static int debug_testmodes_open(struct inode *, struct file *);
+static ssize_t debug_testmodes_write(struct file *file,
+ const char __user *buffer, size_t count, loff_t *ppos);
static int debug_async_open(struct inode *, struct file *);
static ssize_t debug_output(struct file*, char __user*, size_t, loff_t*);
@@ -362,6 +389,14 @@ static const struct file_operations debug_registers_fops = {
.release = debug_close,
.llseek = default_llseek,
};
+static const struct file_operations debug_testmodes_fops = {
+ .owner = THIS_MODULE,
+ .open = debug_testmodes_open,
+ .read = debug_output,
+ .write = debug_testmodes_write,
+ .release = debug_close,
+ .llseek = default_llseek,
+};
static struct dentry *ehci_debug_root;
@@ -844,6 +879,33 @@ done:
return buf->alloc_size - size;
}
+static ssize_t fill_testmodes_buffer(struct debug_buffer *buf)
+{
+ struct usb_hcd *hcd;
+ struct ehci_hcd *ehci;
+ unsigned long flags;
+ unsigned temp, size, i, ptc;
+ char *next;
+
+ hcd = bus_to_hcd(buf->bus);
+ ehci = hcd_to_ehci(hcd);
+ next = buf->output_buf;
+ size = buf->alloc_size;
+ spin_lock_irqsave(&ehci->lock, flags);
+ ptc = (ehci_readl(ehci, &ehci->regs->port_status[0])
+ & PTC_MASK) >> PTC_SHIFT;
+ for (i = 0; i < TESTMODES_SIZE; i++) {
+ if (ptc == i)
+ temp = scnprintf(next, size, "-> %s\n", testmodes[i]);
+ else
+ temp = scnprintf(next, size, " %s\n", testmodes[i]);
+ size -= temp;
+ next += temp;
+ }
+ spin_unlock_irqrestore(&ehci->lock, flags);
+ return buf->alloc_size - size;
+}
+
static struct debug_buffer *alloc_buffer(struct usb_bus *bus,
ssize_t (*fill_func)(struct debug_buffer *))
{
@@ -919,6 +981,7 @@ static int debug_close(struct inode *inode, struct file *file)
return 0;
}
+
static int debug_async_open(struct inode *inode, struct file *file)
{
file->private_data = alloc_buffer(inode->i_private, fill_async_buffer);
@@ -946,6 +1009,68 @@ static int debug_registers_open(struct inode *inode, struct file *file)
return file->private_data ? 0 : -ENOMEM;
}
+static int debug_testmodes_open(struct inode *inode, struct file *file)
+{
+ struct debug_buffer *buf;
+ buf = alloc_buffer(inode->i_private, fill_testmodes_buffer);
+
+ if (!buf)
+ return -ENOMEM;
+
+ buf->alloc_size = (sizeof(void *) == 4 ? 6 : 8)*PAGE_SIZE;
+ file->private_data = buf;
+ return 0;
+}
+
+static ssize_t debug_testmodes_write(struct file *file,
+ const char __user *buffer, size_t count, loff_t *ppos)
+{
+ struct usb_hcd *hcd;
+ struct ehci_hcd *ehci;
+ struct usb_device *hub;
+ char buf[50];
+ size_t len, slen;
+ int i;
+ int status = -1;
+ u32 val;
+
+ hcd = bus_to_hcd(((struct debug_buffer *)file->private_data)->bus);
+ ehci = hcd_to_ehci(hcd);
+ hub = hcd->self.root_hub;
+
+ len = min(count, (size_t) sizeof(buf) - 1);
+ if (copy_from_user(buf, buffer, len))
+ return -EFAULT;
+ buf[len] = '\0';
+ if (len > 0 && buf[len - 1] == '\n')
+ buf[len - 1] = '\0';
+
+ for (i = 0; i < TESTMODES_SIZE; i++) {
+ slen = strlen(testmodes[i]);
+ if (!strncmp(buf, testmodes[i], slen)) {
+
+ /*clear the last testmode*/
+ val = ehci_readl(ehci, &ehci->regs->port_status[0])
+ & ~PORT_RWC_BITS;
+ val &= ~(PTC_MASK | PORT_RWC_BITS);
+ ehci_writel(ehci, val, &ehci->regs->port_status[0]);
+
+ /*issue the new testmode*/
+ val = ehci_readl(ehci, &ehci->regs->port_status[0]);
+ val |= (i << PTC_SHIFT) & PTC_MASK;
+ val &= ~PORT_RWC_BITS;
+ ehci_writel(ehci, val, &ehci->regs->port_status[0]);
+
+ status = 0;
+ break;
+ }
+ }
+ if (status >= 0)
+ return count;
+ else
+ return -EINVAL;
+}
+
static inline void create_debug_files (struct ehci_hcd *ehci)
{
struct usb_bus *bus = &ehci_to_hcd(ehci)->self;
@@ -966,6 +1091,10 @@ static inline void create_debug_files (struct ehci_hcd *ehci)
&debug_registers_fops))
goto file_error;
+ if (!debugfs_create_file("testmode", S_IRUGO, ehci->debug_dir, bus,
+ &debug_testmodes_fops))
+ goto file_error;
+
return;
file_error:
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index 162aeecf98ec..9cf090fc4337 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -4,6 +4,7 @@
* Maintainer: Alan Stern <stern@rowland.harvard.edu>
*
* Copyright (c) 2000-2004 by David Brownell
+ * Copyright (c) 2013 NVIDIA CORPORATION. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
@@ -39,6 +40,7 @@
#include <linux/dma-mapping.h>
#include <linux/debugfs.h>
#include <linux/slab.h>
+#include <linux/uaccess.h>
#include <asm/byteorder.h>
#include <asm/io.h>