diff --git a/drivers/usb/host/xhci-debugfs.c b/drivers/usb/host/xhci-debugfs.c index 76e446a889bd..cadc01336bf8 100644 --- a/drivers/usb/host/xhci-debugfs.c +++ b/drivers/usb/host/xhci-debugfs.c @@ -8,6 +8,7 @@ */ #include +#include #include "xhci.h" #include "xhci-debugfs.h" @@ -351,8 +352,44 @@ static int xhci_port_open(struct inode *inode, struct file *file) return single_open(file, xhci_portsc_show, inode->i_private); } +static ssize_t xhci_port_write(struct file *file, const char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct seq_file *s = file->private_data; + struct xhci_port *port = s->private; + struct xhci_hcd *xhci = hcd_to_xhci(port->rhub->hcd); + char buf[32]; + u32 portsc; + unsigned long flags; + + if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) + return -EFAULT; + + if (!strncmp(buf, "compliance", 10)) { + /* If CTC is clear, compliance is enabled by default */ + if (!HCC2_CTC(xhci->hcc_params2)) + return count; + spin_lock_irqsave(&xhci->lock, flags); + /* compliance mode can only be enabled on ports in RxDetect */ + portsc = readl(port->addr); + if ((portsc & PORT_PLS_MASK) != XDEV_RXDETECT) { + spin_unlock_irqrestore(&xhci->lock, flags); + return -EPERM; + } + portsc = xhci_port_state_to_neutral(portsc); + portsc &= ~PORT_PLS_MASK; + portsc |= PORT_LINK_STROBE | XDEV_COMP_MODE; + writel(portsc, port->addr); + spin_unlock_irqrestore(&xhci->lock, flags); + } else { + return -EINVAL; + } + return count; +} + static const struct file_operations port_fops = { .open = xhci_port_open, + .write = xhci_port_write, .read = seq_read, .llseek = seq_lseek, .release = single_release, @@ -491,7 +528,7 @@ static void xhci_debugfs_create_ports(struct xhci_hcd *xhci, num_ports + 1); dir = debugfs_create_dir(port_name, parent); port = &xhci->hw_ports[num_ports]; - debugfs_create_file("portsc", 0444, dir, port, &port_fops); + debugfs_create_file("portsc", 0644, dir, port, &port_fops); } }