diff options
Diffstat (limited to 'drivers/usb/host/xhci-debugfs.c')
-rw-r--r-- | drivers/usb/host/xhci-debugfs.c | 85 |
1 files changed, 85 insertions, 0 deletions
diff --git a/drivers/usb/host/xhci-debugfs.c b/drivers/usb/host/xhci-debugfs.c index 5851052d4668..cadc01336bf8 100644 --- a/drivers/usb/host/xhci-debugfs.c +++ b/drivers/usb/host/xhci-debugfs.c @@ -8,6 +8,7 @@ */ #include <linux/slab.h> +#include <linux/uaccess.h> #include "xhci.h" #include "xhci-debugfs.h" @@ -333,6 +334,67 @@ static const struct file_operations xhci_context_fops = { .release = single_release, }; + + +static int xhci_portsc_show(struct seq_file *s, void *unused) +{ + struct xhci_port *port = s->private; + u32 portsc; + + portsc = readl(port->addr); + seq_printf(s, "%s\n", xhci_decode_portsc(portsc)); + + return 0; +} + +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, +}; + static void xhci_debugfs_create_files(struct xhci_hcd *xhci, struct xhci_file_map *files, size_t nentries, void *data, @@ -449,6 +511,27 @@ void xhci_debugfs_remove_slot(struct xhci_hcd *xhci, int slot_id) dev->debugfs_private = NULL; } +static void xhci_debugfs_create_ports(struct xhci_hcd *xhci, + struct dentry *parent) +{ + unsigned int num_ports; + char port_name[8]; + struct xhci_port *port; + struct dentry *dir; + + num_ports = HCS_MAX_PORTS(xhci->hcs_params1); + + parent = debugfs_create_dir("ports", parent); + + while (num_ports--) { + scnprintf(port_name, sizeof(port_name), "port%02d", + num_ports + 1); + dir = debugfs_create_dir(port_name, parent); + port = &xhci->hw_ports[num_ports]; + debugfs_create_file("portsc", 0644, dir, port, &port_fops); + } +} + void xhci_debugfs_init(struct xhci_hcd *xhci) { struct device *dev = xhci_to_hcd(xhci)->self.controller; @@ -497,6 +580,8 @@ void xhci_debugfs_init(struct xhci_hcd *xhci) xhci->debugfs_root); xhci->debugfs_slots = debugfs_create_dir("devices", xhci->debugfs_root); + + xhci_debugfs_create_ports(xhci, xhci->debugfs_root); } void xhci_debugfs_exit(struct xhci_hcd *xhci) |