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

usb: dwc3: debugfs: Dump internal LSP and ep registers

To dump internal LSP and endpoint state debug registers, we must write
to GDBGLSPMUX register. This patch correctly dump LSP and endpoint
states from the debug registers.

If the controller is in device mode, all LSP and endpoint state
registers will be dumped via the debugfs attribute "lsp_dump". In host
mode, the user has to write the LSP number to "lsp_dump" to dump a
specific LSP selection.

Fixes: 80b776340c78 ("usb: dwc3: Dump LSP and BMU debug info")
Signed-off-by: Thinh Nguyen <thinhn@synopsys.com>
Signed-off-by: Felipe Balbi <felipe.balbi@linux.intel.com>

authored by

Thinh Nguyen and committed by
Felipe Balbi
62ba09d6 0f874f79

+154 -4
+13
drivers/usb/dwc3/core.h
··· 174 174 #define DWC3_GSBUSCFG0_INCRBRSTENA (1 << 0) /* undefined length enable */ 175 175 #define DWC3_GSBUSCFG0_INCRBRST_MASK 0xff 176 176 177 + /* Global Debug LSP MUX Select */ 178 + #define DWC3_GDBGLSPMUX_ENDBC BIT(15) /* Host only */ 179 + #define DWC3_GDBGLSPMUX_HOSTSELECT(n) ((n) & 0x3fff) 180 + #define DWC3_GDBGLSPMUX_DEVSELECT(n) (((n) & 0xf) << 4) 181 + #define DWC3_GDBGLSPMUX_EPSELECT(n) ((n) & 0xf) 182 + 177 183 /* Global Debug Queue/FIFO Space Available Register */ 178 184 #define DWC3_GDBGFIFOSPACE_NUM(n) ((n) & 0x1f) 179 185 #define DWC3_GDBGFIFOSPACE_TYPE(n) (((n) << 5) & 0x1e0) ··· 259 253 #define DWC3_GSTS_DEVICE_IP BIT(6) 260 254 #define DWC3_GSTS_CSR_TIMEOUT BIT(5) 261 255 #define DWC3_GSTS_BUS_ERR_ADDR_VLD BIT(4) 256 + #define DWC3_GSTS_CURMOD(n) ((n) & 0x3) 257 + #define DWC3_GSTS_CURMOD_DEVICE 0 258 + #define DWC3_GSTS_CURMOD_HOST 1 262 259 263 260 /* Global USB2 PHY Configuration Register */ 264 261 #define DWC3_GUSB2PHYCFG_PHYSOFTRST BIT(31) ··· 330 321 #define DWC3_GHWPARAMS1_EN_PWROPT_HIB 2 331 322 #define DWC3_GHWPARAMS1_PWROPT(n) ((n) << 24) 332 323 #define DWC3_GHWPARAMS1_PWROPT_MASK DWC3_GHWPARAMS1_PWROPT(3) 324 + #define DWC3_GHWPARAMS1_ENDBC BIT(31) 333 325 334 326 /* Global HWPARAMS3 Register */ 335 327 #define DWC3_GHWPARAMS3_SSPHY_IFC(n) ((n) & 3) ··· 955 945 * @hwparams: copy of hwparams registers 956 946 * @root: debugfs root folder pointer 957 947 * @regset: debugfs pointer to regdump file 948 + * @dbg_lsp_select: current debug lsp mux register selection 958 949 * @test_mode: true when we're entering a USB test mode 959 950 * @test_mode_nr: test feature selector 960 951 * @lpm_nyet_threshold: LPM NYET response threshold ··· 1131 1120 struct dwc3_hwparams hwparams; 1132 1121 struct dentry *root; 1133 1122 struct debugfs_regset32 *regset; 1123 + 1124 + u32 dbg_lsp_select; 1134 1125 1135 1126 u8 test_mode; 1136 1127 u8 test_mode_nr;
+141 -4
drivers/usb/dwc3/debugfs.c
··· 25 25 #include "io.h" 26 26 #include "debug.h" 27 27 28 + #define DWC3_LSP_MUX_UNSELECTED 0xfffff 29 + 28 30 #define dump_register(nm) \ 29 31 { \ 30 32 .name = __stringify(nm), \ ··· 84 82 dump_register(GDBGFIFOSPACE), 85 83 dump_register(GDBGLTSSM), 86 84 dump_register(GDBGBMU), 87 - dump_register(GDBGLSPMUX), 88 - dump_register(GDBGLSP), 89 - dump_register(GDBGEPINFO0), 90 - dump_register(GDBGEPINFO1), 91 85 dump_register(GPRTBIMAP_HS0), 92 86 dump_register(GPRTBIMAP_HS1), 93 87 dump_register(GPRTBIMAP_FS0), ··· 275 277 dump_register(OEVT), 276 278 dump_register(OEVTEN), 277 279 dump_register(OSTS), 280 + }; 281 + 282 + static void dwc3_host_lsp(struct seq_file *s) 283 + { 284 + struct dwc3 *dwc = s->private; 285 + bool dbc_enabled; 286 + u32 sel; 287 + u32 reg; 288 + u32 val; 289 + 290 + dbc_enabled = !!(dwc->hwparams.hwparams1 & DWC3_GHWPARAMS1_ENDBC); 291 + 292 + sel = dwc->dbg_lsp_select; 293 + if (sel == DWC3_LSP_MUX_UNSELECTED) { 294 + seq_puts(s, "Write LSP selection to print for host\n"); 295 + return; 296 + } 297 + 298 + reg = DWC3_GDBGLSPMUX_HOSTSELECT(sel); 299 + 300 + dwc3_writel(dwc->regs, DWC3_GDBGLSPMUX, reg); 301 + val = dwc3_readl(dwc->regs, DWC3_GDBGLSP); 302 + seq_printf(s, "GDBGLSP[%d] = 0x%08x\n", sel, val); 303 + 304 + if (dbc_enabled && sel < 256) { 305 + reg |= DWC3_GDBGLSPMUX_ENDBC; 306 + dwc3_writel(dwc->regs, DWC3_GDBGLSPMUX, reg); 307 + val = dwc3_readl(dwc->regs, DWC3_GDBGLSP); 308 + seq_printf(s, "GDBGLSP_DBC[%d] = 0x%08x\n", sel, val); 309 + } 310 + } 311 + 312 + static void dwc3_gadget_lsp(struct seq_file *s) 313 + { 314 + struct dwc3 *dwc = s->private; 315 + int i; 316 + u32 reg; 317 + 318 + for (i = 0; i < 16; i++) { 319 + reg = DWC3_GDBGLSPMUX_DEVSELECT(i); 320 + dwc3_writel(dwc->regs, DWC3_GDBGLSPMUX, reg); 321 + reg = dwc3_readl(dwc->regs, DWC3_GDBGLSP); 322 + seq_printf(s, "GDBGLSP[%d] = 0x%08x\n", i, reg); 323 + } 324 + } 325 + 326 + static int dwc3_lsp_show(struct seq_file *s, void *unused) 327 + { 328 + struct dwc3 *dwc = s->private; 329 + unsigned int current_mode; 330 + unsigned long flags; 331 + u32 reg; 332 + 333 + spin_lock_irqsave(&dwc->lock, flags); 334 + reg = dwc3_readl(dwc->regs, DWC3_GSTS); 335 + current_mode = DWC3_GSTS_CURMOD(reg); 336 + 337 + switch (current_mode) { 338 + case DWC3_GSTS_CURMOD_HOST: 339 + dwc3_host_lsp(s); 340 + break; 341 + case DWC3_GSTS_CURMOD_DEVICE: 342 + dwc3_gadget_lsp(s); 343 + break; 344 + default: 345 + seq_puts(s, "Mode is unknown, no LSP register printed\n"); 346 + break; 347 + } 348 + spin_unlock_irqrestore(&dwc->lock, flags); 349 + 350 + return 0; 351 + } 352 + 353 + static int dwc3_lsp_open(struct inode *inode, struct file *file) 354 + { 355 + return single_open(file, dwc3_lsp_show, inode->i_private); 356 + } 357 + 358 + static ssize_t dwc3_lsp_write(struct file *file, const char __user *ubuf, 359 + size_t count, loff_t *ppos) 360 + { 361 + struct seq_file *s = file->private_data; 362 + struct dwc3 *dwc = s->private; 363 + unsigned long flags; 364 + char buf[32] = { 0 }; 365 + u32 sel; 366 + int ret; 367 + 368 + if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) 369 + return -EFAULT; 370 + 371 + ret = kstrtouint(buf, 0, &sel); 372 + if (ret) 373 + return ret; 374 + 375 + spin_lock_irqsave(&dwc->lock, flags); 376 + dwc->dbg_lsp_select = sel; 377 + spin_unlock_irqrestore(&dwc->lock, flags); 378 + 379 + return count; 380 + } 381 + 382 + static const struct file_operations dwc3_lsp_fops = { 383 + .open = dwc3_lsp_open, 384 + .write = dwc3_lsp_write, 385 + .read = seq_read, 386 + .llseek = seq_lseek, 387 + .release = single_release, 278 388 }; 279 389 280 390 static int dwc3_mode_show(struct seq_file *s, void *unused) ··· 789 683 return 0; 790 684 } 791 685 686 + static int dwc3_ep_info_register_show(struct seq_file *s, void *unused) 687 + { 688 + struct dwc3_ep *dep = s->private; 689 + struct dwc3 *dwc = dep->dwc; 690 + unsigned long flags; 691 + u64 ep_info; 692 + u32 lower_32_bits; 693 + u32 upper_32_bits; 694 + u32 reg; 695 + 696 + spin_lock_irqsave(&dwc->lock, flags); 697 + reg = DWC3_GDBGLSPMUX_EPSELECT(dep->number); 698 + dwc3_writel(dwc->regs, DWC3_GDBGLSPMUX, reg); 699 + 700 + lower_32_bits = dwc3_readl(dwc->regs, DWC3_GDBGEPINFO0); 701 + upper_32_bits = dwc3_readl(dwc->regs, DWC3_GDBGEPINFO1); 702 + 703 + ep_info = ((u64)upper_32_bits << 32) | lower_32_bits; 704 + seq_printf(s, "0x%016llx\n", ep_info); 705 + spin_unlock_irqrestore(&dwc->lock, flags); 706 + 707 + return 0; 708 + } 709 + 792 710 DEFINE_SHOW_ATTRIBUTE(dwc3_tx_fifo_size); 793 711 DEFINE_SHOW_ATTRIBUTE(dwc3_rx_fifo_size); 794 712 DEFINE_SHOW_ATTRIBUTE(dwc3_tx_request_queue); ··· 822 692 DEFINE_SHOW_ATTRIBUTE(dwc3_event_queue); 823 693 DEFINE_SHOW_ATTRIBUTE(dwc3_transfer_type); 824 694 DEFINE_SHOW_ATTRIBUTE(dwc3_trb_ring); 695 + DEFINE_SHOW_ATTRIBUTE(dwc3_ep_info_register); 825 696 826 697 static const struct dwc3_ep_file_map dwc3_ep_file_map[] = { 827 698 { "tx_fifo_size", &dwc3_tx_fifo_size_fops, }, ··· 834 703 { "event_queue", &dwc3_event_queue_fops, }, 835 704 { "transfer_type", &dwc3_transfer_type_fops, }, 836 705 { "trb_ring", &dwc3_trb_ring_fops, }, 706 + { "GDBGEPINFO", &dwc3_ep_info_register_fops, }, 837 707 }; 838 708 839 709 static void dwc3_debugfs_create_endpoint_files(struct dwc3_ep *dep, ··· 882 750 if (!dwc->regset) 883 751 return; 884 752 753 + dwc->dbg_lsp_select = DWC3_LSP_MUX_UNSELECTED; 754 + 885 755 dwc->regset->regs = dwc3_regs; 886 756 dwc->regset->nregs = ARRAY_SIZE(dwc3_regs); 887 757 dwc->regset->base = dwc->regs - DWC3_GLOBALS_REGS_START; ··· 892 758 dwc->root = root; 893 759 894 760 debugfs_create_regset32("regdump", S_IRUGO, root, dwc->regset); 761 + 762 + debugfs_create_file("lsp_dump", S_IRUGO | S_IWUSR, root, dwc, 763 + &dwc3_lsp_fops); 895 764 896 765 if (IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)) { 897 766 debugfs_create_file("mode", S_IRUGO | S_IWUSR, root, dwc,