diff options
Diffstat (limited to 'drivers/usb/dwc3/debugfs.c')
-rw-r--r-- | drivers/usb/dwc3/debugfs.c | 201 |
1 files changed, 187 insertions, 14 deletions
diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c index df8e73ec3342..1c792710348f 100644 --- a/drivers/usb/dwc3/debugfs.c +++ b/drivers/usb/dwc3/debugfs.c @@ -25,6 +25,8 @@ #include "io.h" #include "debug.h" +#define DWC3_LSP_MUX_UNSELECTED 0xfffff + #define dump_register(nm) \ { \ .name = __stringify(nm), \ @@ -82,10 +84,6 @@ static const struct debugfs_reg32 dwc3_regs[] = { dump_register(GDBGFIFOSPACE), dump_register(GDBGLTSSM), dump_register(GDBGBMU), - dump_register(GDBGLSPMUX), - dump_register(GDBGLSP), - dump_register(GDBGEPINFO0), - dump_register(GDBGEPINFO1), dump_register(GPRTBIMAP_HS0), dump_register(GPRTBIMAP_HS1), dump_register(GPRTBIMAP_FS0), @@ -279,6 +277,114 @@ static const struct debugfs_reg32 dwc3_regs[] = { dump_register(OSTS), }; +static void dwc3_host_lsp(struct seq_file *s) +{ + struct dwc3 *dwc = s->private; + bool dbc_enabled; + u32 sel; + u32 reg; + u32 val; + + dbc_enabled = !!(dwc->hwparams.hwparams1 & DWC3_GHWPARAMS1_ENDBC); + + sel = dwc->dbg_lsp_select; + if (sel == DWC3_LSP_MUX_UNSELECTED) { + seq_puts(s, "Write LSP selection to print for host\n"); + return; + } + + reg = DWC3_GDBGLSPMUX_HOSTSELECT(sel); + + dwc3_writel(dwc->regs, DWC3_GDBGLSPMUX, reg); + val = dwc3_readl(dwc->regs, DWC3_GDBGLSP); + seq_printf(s, "GDBGLSP[%d] = 0x%08x\n", sel, val); + + if (dbc_enabled && sel < 256) { + reg |= DWC3_GDBGLSPMUX_ENDBC; + dwc3_writel(dwc->regs, DWC3_GDBGLSPMUX, reg); + val = dwc3_readl(dwc->regs, DWC3_GDBGLSP); + seq_printf(s, "GDBGLSP_DBC[%d] = 0x%08x\n", sel, val); + } +} + +static void dwc3_gadget_lsp(struct seq_file *s) +{ + struct dwc3 *dwc = s->private; + int i; + u32 reg; + + for (i = 0; i < 16; i++) { + reg = DWC3_GDBGLSPMUX_DEVSELECT(i); + dwc3_writel(dwc->regs, DWC3_GDBGLSPMUX, reg); + reg = dwc3_readl(dwc->regs, DWC3_GDBGLSP); + seq_printf(s, "GDBGLSP[%d] = 0x%08x\n", i, reg); + } +} + +static int dwc3_lsp_show(struct seq_file *s, void *unused) +{ + struct dwc3 *dwc = s->private; + unsigned int current_mode; + unsigned long flags; + u32 reg; + + spin_lock_irqsave(&dwc->lock, flags); + reg = dwc3_readl(dwc->regs, DWC3_GSTS); + current_mode = DWC3_GSTS_CURMOD(reg); + + switch (current_mode) { + case DWC3_GSTS_CURMOD_HOST: + dwc3_host_lsp(s); + break; + case DWC3_GSTS_CURMOD_DEVICE: + dwc3_gadget_lsp(s); + break; + default: + seq_puts(s, "Mode is unknown, no LSP register printed\n"); + break; + } + spin_unlock_irqrestore(&dwc->lock, flags); + + return 0; +} + +static int dwc3_lsp_open(struct inode *inode, struct file *file) +{ + return single_open(file, dwc3_lsp_show, inode->i_private); +} + +static ssize_t dwc3_lsp_write(struct file *file, const char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct seq_file *s = file->private_data; + struct dwc3 *dwc = s->private; + unsigned long flags; + char buf[32] = { 0 }; + u32 sel; + int ret; + + if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) + return -EFAULT; + + ret = kstrtouint(buf, 0, &sel); + if (ret) + return ret; + + spin_lock_irqsave(&dwc->lock, flags); + dwc->dbg_lsp_select = sel; + spin_unlock_irqrestore(&dwc->lock, flags); + + return count; +} + +static const struct file_operations dwc3_lsp_fops = { + .open = dwc3_lsp_open, + .write = dwc3_lsp_write, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + static int dwc3_mode_show(struct seq_file *s, void *unused) { struct dwc3 *dwc = s->private; @@ -433,13 +539,24 @@ static int dwc3_link_state_show(struct seq_file *s, void *unused) unsigned long flags; enum dwc3_link_state state; u32 reg; + u8 speed; spin_lock_irqsave(&dwc->lock, flags); + reg = dwc3_readl(dwc->regs, DWC3_GSTS); + if (DWC3_GSTS_CURMOD(reg) != DWC3_GSTS_CURMOD_DEVICE) { + seq_puts(s, "Not available\n"); + spin_unlock_irqrestore(&dwc->lock, flags); + return 0; + } + reg = dwc3_readl(dwc->regs, DWC3_DSTS); state = DWC3_DSTS_USBLNKST(reg); - spin_unlock_irqrestore(&dwc->lock, flags); + speed = reg & DWC3_DSTS_CONNECTSPD; - seq_printf(s, "%s\n", dwc3_gadget_link_string(state)); + seq_printf(s, "%s\n", (speed >= DWC3_DSTS_SUPERSPEED) ? + dwc3_gadget_link_string(state) : + dwc3_gadget_hs_link_string(state)); + spin_unlock_irqrestore(&dwc->lock, flags); return 0; } @@ -457,6 +574,8 @@ static ssize_t dwc3_link_state_write(struct file *file, unsigned long flags; enum dwc3_link_state state = 0; char buf[32]; + u32 reg; + u8 speed; if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) return -EFAULT; @@ -477,6 +596,21 @@ static ssize_t dwc3_link_state_write(struct file *file, return -EINVAL; spin_lock_irqsave(&dwc->lock, flags); + reg = dwc3_readl(dwc->regs, DWC3_GSTS); + if (DWC3_GSTS_CURMOD(reg) != DWC3_GSTS_CURMOD_DEVICE) { + spin_unlock_irqrestore(&dwc->lock, flags); + return -EINVAL; + } + + reg = dwc3_readl(dwc->regs, DWC3_DSTS); + speed = reg & DWC3_DSTS_CONNECTSPD; + + if (speed < DWC3_DSTS_SUPERSPEED && + state != DWC3_LINK_STATE_RECOV) { + spin_unlock_irqrestore(&dwc->lock, flags); + return -EINVAL; + } + dwc3_gadget_set_link_state(dwc, state); spin_unlock_irqrestore(&dwc->lock, flags); @@ -496,7 +630,7 @@ struct dwc3_ep_file_map { const struct file_operations *const fops; }; -static int dwc3_tx_fifo_queue_show(struct seq_file *s, void *unused) +static int dwc3_tx_fifo_size_show(struct seq_file *s, void *unused) { struct dwc3_ep *dep = s->private; struct dwc3 *dwc = dep->dwc; @@ -504,14 +638,18 @@ static int dwc3_tx_fifo_queue_show(struct seq_file *s, void *unused) u32 val; spin_lock_irqsave(&dwc->lock, flags); - val = dwc3_core_fifo_space(dep, DWC3_TXFIFOQ); + val = dwc3_core_fifo_space(dep, DWC3_TXFIFO); + + /* Convert to bytes */ + val *= DWC3_MDWIDTH(dwc->hwparams.hwparams0); + val >>= 3; seq_printf(s, "%u\n", val); spin_unlock_irqrestore(&dwc->lock, flags); return 0; } -static int dwc3_rx_fifo_queue_show(struct seq_file *s, void *unused) +static int dwc3_rx_fifo_size_show(struct seq_file *s, void *unused) { struct dwc3_ep *dep = s->private; struct dwc3 *dwc = dep->dwc; @@ -519,7 +657,11 @@ static int dwc3_rx_fifo_queue_show(struct seq_file *s, void *unused) u32 val; spin_lock_irqsave(&dwc->lock, flags); - val = dwc3_core_fifo_space(dep, DWC3_RXFIFOQ); + val = dwc3_core_fifo_space(dep, DWC3_RXFIFO); + + /* Convert to bytes */ + val *= DWC3_MDWIDTH(dwc->hwparams.hwparams0); + val >>= 3; seq_printf(s, "%u\n", val); spin_unlock_irqrestore(&dwc->lock, flags); @@ -675,8 +817,32 @@ out: return 0; } -DEFINE_SHOW_ATTRIBUTE(dwc3_tx_fifo_queue); -DEFINE_SHOW_ATTRIBUTE(dwc3_rx_fifo_queue); +static int dwc3_ep_info_register_show(struct seq_file *s, void *unused) +{ + struct dwc3_ep *dep = s->private; + struct dwc3 *dwc = dep->dwc; + unsigned long flags; + u64 ep_info; + u32 lower_32_bits; + u32 upper_32_bits; + u32 reg; + + spin_lock_irqsave(&dwc->lock, flags); + reg = DWC3_GDBGLSPMUX_EPSELECT(dep->number); + dwc3_writel(dwc->regs, DWC3_GDBGLSPMUX, reg); + + lower_32_bits = dwc3_readl(dwc->regs, DWC3_GDBGEPINFO0); + upper_32_bits = dwc3_readl(dwc->regs, DWC3_GDBGEPINFO1); + + ep_info = ((u64)upper_32_bits << 32) | lower_32_bits; + seq_printf(s, "0x%016llx\n", ep_info); + spin_unlock_irqrestore(&dwc->lock, flags); + + return 0; +} + +DEFINE_SHOW_ATTRIBUTE(dwc3_tx_fifo_size); +DEFINE_SHOW_ATTRIBUTE(dwc3_rx_fifo_size); DEFINE_SHOW_ATTRIBUTE(dwc3_tx_request_queue); DEFINE_SHOW_ATTRIBUTE(dwc3_rx_request_queue); DEFINE_SHOW_ATTRIBUTE(dwc3_rx_info_queue); @@ -684,10 +850,11 @@ DEFINE_SHOW_ATTRIBUTE(dwc3_descriptor_fetch_queue); DEFINE_SHOW_ATTRIBUTE(dwc3_event_queue); DEFINE_SHOW_ATTRIBUTE(dwc3_transfer_type); DEFINE_SHOW_ATTRIBUTE(dwc3_trb_ring); +DEFINE_SHOW_ATTRIBUTE(dwc3_ep_info_register); static const struct dwc3_ep_file_map dwc3_ep_file_map[] = { - { "tx_fifo_queue", &dwc3_tx_fifo_queue_fops, }, - { "rx_fifo_queue", &dwc3_rx_fifo_queue_fops, }, + { "tx_fifo_size", &dwc3_tx_fifo_size_fops, }, + { "rx_fifo_size", &dwc3_rx_fifo_size_fops, }, { "tx_request_queue", &dwc3_tx_request_queue_fops, }, { "rx_request_queue", &dwc3_rx_request_queue_fops, }, { "rx_info_queue", &dwc3_rx_info_queue_fops, }, @@ -695,6 +862,7 @@ static const struct dwc3_ep_file_map dwc3_ep_file_map[] = { { "event_queue", &dwc3_event_queue_fops, }, { "transfer_type", &dwc3_transfer_type_fops, }, { "trb_ring", &dwc3_trb_ring_fops, }, + { "GDBGEPINFO", &dwc3_ep_info_register_fops, }, }; static void dwc3_debugfs_create_endpoint_files(struct dwc3_ep *dep, @@ -742,6 +910,8 @@ void dwc3_debugfs_init(struct dwc3 *dwc) if (!dwc->regset) return; + dwc->dbg_lsp_select = DWC3_LSP_MUX_UNSELECTED; + dwc->regset->regs = dwc3_regs; dwc->regset->nregs = ARRAY_SIZE(dwc3_regs); dwc->regset->base = dwc->regs - DWC3_GLOBALS_REGS_START; @@ -751,6 +921,9 @@ void dwc3_debugfs_init(struct dwc3 *dwc) debugfs_create_regset32("regdump", S_IRUGO, root, dwc->regset); + debugfs_create_file("lsp_dump", S_IRUGO | S_IWUSR, root, dwc, + &dwc3_lsp_fops); + if (IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)) { debugfs_create_file("mode", S_IRUGO | S_IWUSR, root, dwc, &dwc3_mode_fops); |