summaryrefslogtreecommitdiff
path: root/drivers/usb/musb/musb_debugfs.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/musb/musb_debugfs.c')
-rw-r--r--drivers/usb/musb/musb_debugfs.c100
1 files changed, 99 insertions, 1 deletions
diff --git a/drivers/usb/musb/musb_debugfs.c b/drivers/usb/musb/musb_debugfs.c
index 78a283e9ce40..9b22d946c089 100644
--- a/drivers/usb/musb/musb_debugfs.c
+++ b/drivers/usb/musb/musb_debugfs.c
@@ -191,9 +191,16 @@ static ssize_t musb_test_mode_write(struct file *file,
{
struct seq_file *s = file->private_data;
struct musb *musb = s->private;
- u8 test = 0;
+ u8 test;
char buf[18];
+ test = musb_readb(musb->mregs, MUSB_TESTMODE);
+ if (test) {
+ dev_err(musb->controller, "Error: test mode is already set. "
+ "Please do USB Bus Reset to start a new test.\n");
+ return count;
+ }
+
memset(buf, 0x00, sizeof(buf));
if (copy_from_user(buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
@@ -238,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;
@@ -264,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;