Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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: Bin Liu <b-liu@ti.com>
Signed-off-by: Felipe Balbi <balbi@ti.com>

authored by

Bin Liu and committed by
Felipe Balbi
ffc1d299 d6d22922

+91
+91
drivers/usb/musb/musb_debugfs.c
··· 245 245 .release = single_release, 246 246 }; 247 247 248 + static int musb_softconnect_show(struct seq_file *s, void *unused) 249 + { 250 + struct musb *musb = s->private; 251 + u8 reg; 252 + int connect; 253 + 254 + switch (musb->xceiv->otg->state) { 255 + case OTG_STATE_A_HOST: 256 + case OTG_STATE_A_WAIT_BCON: 257 + reg = musb_readb(musb->mregs, MUSB_DEVCTL); 258 + connect = reg & MUSB_DEVCTL_SESSION ? 1 : 0; 259 + break; 260 + default: 261 + connect = -1; 262 + } 263 + 264 + seq_printf(s, "%d\n", connect); 265 + 266 + return 0; 267 + } 268 + 269 + static int musb_softconnect_open(struct inode *inode, struct file *file) 270 + { 271 + return single_open(file, musb_softconnect_show, inode->i_private); 272 + } 273 + 274 + static ssize_t musb_softconnect_write(struct file *file, 275 + const char __user *ubuf, size_t count, loff_t *ppos) 276 + { 277 + struct seq_file *s = file->private_data; 278 + struct musb *musb = s->private; 279 + char buf[2]; 280 + u8 reg; 281 + 282 + memset(buf, 0x00, sizeof(buf)); 283 + 284 + if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) 285 + return -EFAULT; 286 + 287 + if (!strncmp(buf, "0", 1)) { 288 + switch (musb->xceiv->otg->state) { 289 + case OTG_STATE_A_HOST: 290 + musb_root_disconnect(musb); 291 + reg = musb_readb(musb->mregs, MUSB_DEVCTL); 292 + reg &= ~MUSB_DEVCTL_SESSION; 293 + musb_writeb(musb->mregs, MUSB_DEVCTL, reg); 294 + break; 295 + default: 296 + break; 297 + } 298 + } else if (!strncmp(buf, "1", 1)) { 299 + switch (musb->xceiv->otg->state) { 300 + case OTG_STATE_A_WAIT_BCON: 301 + /* 302 + * musb_save_context() called in musb_runtime_suspend() 303 + * might cache devctl with SESSION bit cleared during 304 + * soft-disconnect, so specifically set SESSION bit 305 + * here to preserve it for musb_runtime_resume(). 306 + */ 307 + musb->context.devctl |= MUSB_DEVCTL_SESSION; 308 + reg = musb_readb(musb->mregs, MUSB_DEVCTL); 309 + reg |= MUSB_DEVCTL_SESSION; 310 + musb_writeb(musb->mregs, MUSB_DEVCTL, reg); 311 + break; 312 + default: 313 + break; 314 + } 315 + } 316 + 317 + return count; 318 + } 319 + 320 + /* 321 + * In host mode, connect/disconnect the bus without physically 322 + * remove the devices. 323 + */ 324 + static const struct file_operations musb_softconnect_fops = { 325 + .open = musb_softconnect_open, 326 + .write = musb_softconnect_write, 327 + .read = seq_read, 328 + .llseek = seq_lseek, 329 + .release = single_release, 330 + }; 331 + 248 332 int musb_init_debugfs(struct musb *musb) 249 333 { 250 334 struct dentry *root; ··· 350 266 351 267 file = debugfs_create_file("testmode", S_IRUGO | S_IWUSR, 352 268 root, musb, &musb_test_mode_fops); 269 + if (!file) { 270 + ret = -ENOMEM; 271 + goto err1; 272 + } 273 + 274 + file = debugfs_create_file("softconnect", S_IRUGO | S_IWUSR, 275 + root, musb, &musb_softconnect_fops); 353 276 if (!file) { 354 277 ret = -ENOMEM; 355 278 goto err1;