usb: musb: add softconnect for host mode
authorBin Liu <b-liu@ti.com>
Tue, 24 Mar 2015 20:19:56 +0000 (15:19 -0500)
committerFelipe Balbi <balbi@ti.com>
Thu, 7 May 2015 18:49:23 +0000 (13:49 -0500)
Add a debugfs interface - softconnect - for host mode to
connect/disconnect the devices without physically remove the
them.

This adds the capability to re-enumerate the devices which are
permanently mounted on the board with the MUSB controller
together.

Signed-off-by: Bin Liu <b-liu@ti.com>
Signed-off-by: Felipe Balbi <balbi@ti.com>
drivers/usb/musb/musb_debugfs.c

index 04382ec31d3f07adf5a19d21d3cca49c5a1b85ca..9b22d946c089698e2083ab3457e0c5bda1f9bde2 100644 (file)
@@ -245,6 +245,90 @@ static const struct file_operations musb_test_mode_fops = {
        .release                = single_release,
 };
 
+static int musb_softconnect_show(struct seq_file *s, void *unused)
+{
+       struct musb     *musb = s->private;
+       u8              reg;
+       int             connect;
+
+       switch (musb->xceiv->otg->state) {
+       case OTG_STATE_A_HOST:
+       case OTG_STATE_A_WAIT_BCON:
+               reg = musb_readb(musb->mregs, MUSB_DEVCTL);
+               connect = reg & MUSB_DEVCTL_SESSION ? 1 : 0;
+               break;
+       default:
+               connect = -1;
+       }
+
+       seq_printf(s, "%d\n", connect);
+
+       return 0;
+}
+
+static int musb_softconnect_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, musb_softconnect_show, inode->i_private);
+}
+
+static ssize_t musb_softconnect_write(struct file *file,
+               const char __user *ubuf, size_t count, loff_t *ppos)
+{
+       struct seq_file         *s = file->private_data;
+       struct musb             *musb = s->private;
+       char                    buf[2];
+       u8                      reg;
+
+       memset(buf, 0x00, sizeof(buf));
+
+       if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
+               return -EFAULT;
+
+       if (!strncmp(buf, "0", 1)) {
+               switch (musb->xceiv->otg->state) {
+               case OTG_STATE_A_HOST:
+                       musb_root_disconnect(musb);
+                       reg = musb_readb(musb->mregs, MUSB_DEVCTL);
+                       reg &= ~MUSB_DEVCTL_SESSION;
+                       musb_writeb(musb->mregs, MUSB_DEVCTL, reg);
+                       break;
+               default:
+                       break;
+               }
+       } else if (!strncmp(buf, "1", 1)) {
+               switch (musb->xceiv->otg->state) {
+               case OTG_STATE_A_WAIT_BCON:
+                       /*
+                        * musb_save_context() called in musb_runtime_suspend()
+                        * might cache devctl with SESSION bit cleared during
+                        * soft-disconnect, so specifically set SESSION bit
+                        * here to preserve it for musb_runtime_resume().
+                        */
+                       musb->context.devctl |= MUSB_DEVCTL_SESSION;
+                       reg = musb_readb(musb->mregs, MUSB_DEVCTL);
+                       reg |= MUSB_DEVCTL_SESSION;
+                       musb_writeb(musb->mregs, MUSB_DEVCTL, reg);
+                       break;
+               default:
+                       break;
+               }
+       }
+
+       return count;
+}
+
+/*
+ * In host mode, connect/disconnect the bus without physically
+ * remove the devices.
+ */
+static const struct file_operations musb_softconnect_fops = {
+       .open                   = musb_softconnect_open,
+       .write                  = musb_softconnect_write,
+       .read                   = seq_read,
+       .llseek                 = seq_lseek,
+       .release                = single_release,
+};
+
 int musb_init_debugfs(struct musb *musb)
 {
        struct dentry           *root;
@@ -271,6 +355,13 @@ int musb_init_debugfs(struct musb *musb)
                goto err1;
        }
 
+       file = debugfs_create_file("softconnect", S_IRUGO | S_IWUSR,
+                       root, musb, &musb_softconnect_fops);
+       if (!file) {
+               ret = -ENOMEM;
+               goto err1;
+       }
+
        musb->debugfs_root = root;
 
        return 0;