usb: dwc3: gadget: allow testmodes changes via debugfs
authorFelipe Balbi <balbi@ti.com>
Mon, 2 Jan 2012 16:38:30 +0000 (18:38 +0200)
committerFelipe Balbi <balbi@ti.com>
Mon, 6 Feb 2012 09:48:22 +0000 (11:48 +0200)
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>
drivers/usb/dwc3/debugfs.c

index 433c97c15fc5447f6cbfb4c5b974ae3fbe674ff2..c7e85024fda8ee01bedc8ad150e4f769a6403c94 100644 (file)
@@ -46,6 +46,8 @@
 #include <linux/delay.h>
 #include <linux/uaccess.h>
 
+#include <linux/usb/ch9.h>
+
 #include "core.h"
 #include "gadget.h"
 #include "io.h"
@@ -464,6 +466,89 @@ static const struct file_operations dwc3_mode_fops = {
        .release                = single_release,
 };
 
+static int dwc3_testmode_show(struct seq_file *s, void *unused)
+{
+       struct dwc3             *dwc = s->private;
+       unsigned long           flags;
+       u32                     reg;
+
+       spin_lock_irqsave(&dwc->lock, flags);
+       reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+       reg &= DWC3_DCTL_TSTCTRL_MASK;
+       reg >>= 1;
+       spin_unlock_irqrestore(&dwc->lock, flags);
+
+       switch (reg) {
+       case 0:
+               seq_printf(s, "no test\n");
+               break;
+       case TEST_J:
+               seq_printf(s, "test_j\n");
+               break;
+       case TEST_K:
+               seq_printf(s, "test_k\n");
+               break;
+       case TEST_SE0_NAK:
+               seq_printf(s, "test_se0_nak\n");
+               break;
+       case TEST_PACKET:
+               seq_printf(s, "test_packet\n");
+               break;
+       case TEST_FORCE_EN:
+               seq_printf(s, "test_force_enable\n");
+               break;
+       default:
+               seq_printf(s, "UNKNOWN %d\n", reg);
+       }
+
+       return 0;
+}
+
+static int dwc3_testmode_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, dwc3_testmode_show, inode->i_private);
+}
+
+static ssize_t dwc3_testmode_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;
+       u32                     testmode = 0;
+       char                    buf[32];
+
+       if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
+               return -EFAULT;
+
+       if (!strncmp(buf, "test_j", 6))
+               testmode = TEST_J;
+       else if (!strncmp(buf, "test_k", 6))
+               testmode = TEST_K;
+       else if (!strncmp(buf, "test_se0_nak", 13))
+               testmode = TEST_SE0_NAK;
+       else if (!strncmp(buf, "test_packet", 12))
+               testmode = TEST_PACKET;
+       else if (!strncmp(buf, "test_force_enable", 18))
+               testmode = TEST_FORCE_EN;
+       else
+               testmode = 0;
+
+       spin_lock_irqsave(&dwc->lock, flags);
+       dwc3_gadget_set_test_mode(dwc, testmode);
+       spin_unlock_irqrestore(&dwc->lock, flags);
+
+       return count;
+}
+
+static const struct file_operations dwc3_testmode_fops = {
+       .open                   = dwc3_testmode_open,
+       .write                  = dwc3_testmode_write,
+       .read                   = seq_read,
+       .llseek                 = seq_lseek,
+       .release                = single_release,
+};
+
 int __devinit dwc3_debugfs_init(struct dwc3 *dwc)
 {
        struct dentry           *root;
@@ -492,6 +577,13 @@ int __devinit dwc3_debugfs_init(struct dwc3 *dwc)
                goto err1;
        }
 
+       file = debugfs_create_file("testmode", S_IRUGO | S_IWUSR, root,
+                       dwc, &dwc3_testmode_fops);
+       if (IS_ERR(file)) {
+               ret = PTR_ERR(file);
+               goto err1;
+       }
+
        return 0;
 
 err1: