summaryrefslogtreecommitdiffstats
path: root/drivers/usb/dwc3/debugfs.c
diff options
context:
space:
mode:
authorFelipe Balbi <balbi@ti.com>2012-01-02 19:25:16 +0200
committerFelipe Balbi <balbi@ti.com>2012-02-06 11:48:25 +0200
commit138801aaa566ecb5a5739a85909b9ec7285efd70 (patch)
treef7f2712f31e5f9914fa73044eae733a3754b02e6 /drivers/usb/dwc3/debugfs.c
parent8598bde7fa125e85bc97decd6513d37dcf1e7bd9 (diff)
downloadlinux-138801aaa566ecb5a5739a85909b9ec7285efd70.tar.bz2
usb: dwc3: gadget: allow Link state changes via debugfs
This is very useful for low level link testing where we might not have a USB Host stack, only a scope to verify signal integrity. Signed-off-by: Felipe Balbi <balbi@ti.com>
Diffstat (limited to 'drivers/usb/dwc3/debugfs.c')
-rw-r--r--drivers/usb/dwc3/debugfs.c110
1 files changed, 110 insertions, 0 deletions
diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c
index c7e85024fda8..a2c1cc64b48d 100644
--- a/drivers/usb/dwc3/debugfs.c
+++ b/drivers/usb/dwc3/debugfs.c
@@ -549,6 +549,109 @@ static const struct file_operations dwc3_testmode_fops = {
.release = single_release,
};
+static int dwc3_link_state_show(struct seq_file *s, void *unused)
+{
+ struct dwc3 *dwc = s->private;
+ unsigned long flags;
+ enum dwc3_link_state state;
+ u32 reg;
+
+ spin_lock_irqsave(&dwc->lock, flags);
+ reg = dwc3_readl(dwc->regs, DWC3_DSTS);
+ state = DWC3_DSTS_USBLNKST(reg);
+ spin_unlock_irqrestore(&dwc->lock, flags);
+
+ switch (state) {
+ case DWC3_LINK_STATE_U0:
+ seq_printf(s, "U0\n");
+ break;
+ case DWC3_LINK_STATE_U1:
+ seq_printf(s, "U1\n");
+ break;
+ case DWC3_LINK_STATE_U2:
+ seq_printf(s, "U2\n");
+ break;
+ case DWC3_LINK_STATE_U3:
+ seq_printf(s, "U3\n");
+ break;
+ case DWC3_LINK_STATE_SS_DIS:
+ seq_printf(s, "SS.Disabled\n");
+ break;
+ case DWC3_LINK_STATE_RX_DET:
+ seq_printf(s, "Rx.Detect\n");
+ break;
+ case DWC3_LINK_STATE_SS_INACT:
+ seq_printf(s, "SS.Inactive\n");
+ break;
+ case DWC3_LINK_STATE_POLL:
+ seq_printf(s, "Poll\n");
+ break;
+ case DWC3_LINK_STATE_RECOV:
+ seq_printf(s, "Recovery\n");
+ break;
+ case DWC3_LINK_STATE_HRESET:
+ seq_printf(s, "HRESET\n");
+ break;
+ case DWC3_LINK_STATE_CMPLY:
+ seq_printf(s, "Compliance\n");
+ break;
+ case DWC3_LINK_STATE_LPBK:
+ seq_printf(s, "Loopback\n");
+ break;
+ default:
+ seq_printf(s, "UNKNOWN %d\n", reg);
+ }
+
+ return 0;
+}
+
+static int dwc3_link_state_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, dwc3_link_state_show, inode->i_private);
+}
+
+static ssize_t dwc3_link_state_write(struct file *file,
+ const char __user *ubuf, size_t count, loff_t *ppos)
+{
+ struct seq_file *s = file->private_data;
+ struct dwc3 *dwc = s->private;
+ unsigned long flags;
+ enum dwc3_link_state state = 0;
+ char buf[32];
+
+ if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
+ return -EFAULT;
+
+ if (!strncmp(buf, "SS.Disabled", 11))
+ state = DWC3_LINK_STATE_SS_DIS;
+ else if (!strncmp(buf, "Rx.Detect", 9))
+ state = DWC3_LINK_STATE_RX_DET;
+ else if (!strncmp(buf, "SS.Inactive", 11))
+ state = DWC3_LINK_STATE_SS_INACT;
+ else if (!strncmp(buf, "Recovery", 8))
+ state = DWC3_LINK_STATE_RECOV;
+ else if (!strncmp(buf, "Compliance", 10))
+ state = DWC3_LINK_STATE_CMPLY;
+ else if (!strncmp(buf, "Loopback", 8))
+ state = DWC3_LINK_STATE_LPBK;
+ else
+ return -EINVAL;
+
+ spin_lock_irqsave(&dwc->lock, flags);
+ dwc3_gadget_set_link_state(dwc, state);
+ spin_unlock_irqrestore(&dwc->lock, flags);
+
+ return count;
+}
+
+static const struct file_operations dwc3_link_state_fops = {
+ .open = dwc3_link_state_open,
+ .write = dwc3_link_state_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
int __devinit dwc3_debugfs_init(struct dwc3 *dwc)
{
struct dentry *root;
@@ -584,6 +687,13 @@ int __devinit dwc3_debugfs_init(struct dwc3 *dwc)
goto err1;
}
+ file = debugfs_create_file("link_state", S_IRUGO | S_IWUSR, root,
+ dwc, &dwc3_link_state_fops);
+ if (IS_ERR(file)) {
+ ret = PTR_ERR(file);
+ goto err1;
+ }
+
return 0;
err1: