usb: dwc3: debugfs: add support for changing port mode
authorFelipe Balbi <balbi@ti.com>
Mon, 17 Oct 2011 05:50:39 +0000 (08:50 +0300)
committerFelipe Balbi <balbi@ti.com>
Mon, 12 Dec 2011 09:48:15 +0000 (11:48 +0200)
This makes testing a lot easier when trying to
switch between host and device modes.

Signed-off-by: Felipe Balbi <balbi@ti.com>
drivers/usb/dwc3/core.h
drivers/usb/dwc3/debugfs.c

index 513a7391099f7415b633a112f809889c1ed4bcfc..6f1e8c98d78de2c3fcd7619f0b8b260194252f15 100644 (file)
 #define DWC3_GCTL_CLK_PIPEHALF (2)
 #define DWC3_GCTL_CLK_MASK     (3)
 
+#define DWC3_GCTL_PRTCAP(n)    (((n) & (3 << 12)) >> 12)
 #define DWC3_GCTL_PRTCAPDIR(n) (n << 12)
 #define DWC3_GCTL_PRTCAP_HOST  1
 #define DWC3_GCTL_PRTCAP_DEVICE        2
index da1ad77d8d51bd439e646999d1ef28c547a46960..fc49334a01cf77f74bc90cf0a48f90d488295df4 100644 (file)
@@ -405,6 +405,80 @@ static const struct file_operations dwc3_regdump_fops = {
        .release                = single_release,
 };
 
+static int dwc3_mode_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_GCTL);
+       spin_unlock_irqrestore(&dwc->lock, flags);
+
+       switch (DWC3_GCTL_PRTCAP(reg)) {
+       case DWC3_GCTL_PRTCAP_HOST:
+               seq_printf(s, "host\n");
+               break;
+       case DWC3_GCTL_PRTCAP_DEVICE:
+               seq_printf(s, "device\n");
+               break;
+       case DWC3_GCTL_PRTCAP_OTG:
+               seq_printf(s, "OTG\n");
+               break;
+       default:
+               seq_printf(s, "UNKNOWN %08x\n", DWC3_GCTL_PRTCAP(reg));
+       }
+
+       return 0;
+}
+
+static int dwc3_mode_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, dwc3_mode_show, inode->i_private);
+}
+
+static ssize_t dwc3_mode_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                     reg;
+       char                    buf[32];
+
+       if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
+               return -EFAULT;
+
+       spin_lock_irqsave(&dwc->lock, flags);
+       reg = dwc3_readl(dwc->regs, DWC3_GCTL);
+       spin_unlock_irqrestore(&dwc->lock, flags);
+
+       reg &= ~(DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_OTG));
+
+       if (!strncmp(buf, "host", 4))
+               reg |= DWC3_GCTL_PRTCAP(DWC3_GCTL_PRTCAP_HOST);
+
+       if (!strncmp(buf, "device", 6))
+               reg |= DWC3_GCTL_PRTCAP(DWC3_GCTL_PRTCAP_DEVICE);
+
+       if (!strncmp(buf, "otg", 3))
+               reg |= DWC3_GCTL_PRTCAP(DWC3_GCTL_PRTCAP_OTG);
+
+       spin_lock_irqsave(&dwc->lock, flags);
+       dwc3_writel(dwc->regs, DWC3_GCTL, reg);
+       spin_unlock_irqrestore(&dwc->lock, flags);
+
+       return count;
+}
+
+static const struct file_operations dwc3_mode_fops = {
+       .open                   = dwc3_mode_open,
+       .write                  = dwc3_mode_write,
+       .read                   = seq_read,
+       .llseek                 = seq_lseek,
+       .release                = single_release,
+};
+
 int __devinit dwc3_debugfs_init(struct dwc3 *dwc)
 {
        struct dentry           *root;
@@ -425,6 +499,14 @@ int __devinit dwc3_debugfs_init(struct dwc3 *dwc)
                ret = PTR_ERR(file);
                goto err1;
        }
+
+       file = debugfs_create_file("mode", S_IRUGO | S_IWUSR, root,
+                       dwc, &dwc3_mode_fops);
+       if (IS_ERR(file)) {
+               ret = PTR_ERR(file);
+               goto err1;
+       }
+
        return 0;
 
 err1: