usb: dwc3: gadget: allow Link state changes via debugfs
authorFelipe Balbi <balbi@ti.com>
Mon, 2 Jan 2012 17:25:16 +0000 (19:25 +0200)
committerFelipe Balbi <balbi@ti.com>
Mon, 6 Feb 2012 09:48:25 +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
drivers/usb/dwc3/gadget.c

index c7e8502..a2c1cc6 100644 (file)
@@ -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:
index 8000395..e0e2337 100644 (file)
@@ -117,7 +117,7 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state)
                if (DWC3_DSTS_USBLNKST(reg) == state)
                        return 0;
 
-               usleep_range(500, 1500);
+               udelay(500);
        }
 
        dev_vdbg(dwc->dev, "link state change request timed out\n");