Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ffc1d299 authored by Bin Liu's avatar Bin Liu Committed by Felipe Balbi
Browse files

usb: musb: add softconnect for host mode



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: default avatarBin Liu <b-liu@ti.com>
Signed-off-by: default avatarFelipe Balbi <balbi@ti.com>
parent d6d22922
Loading
Loading
Loading
Loading
+91 −0
Original line number Diff line number Diff line
@@ -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;