From patchwork Sat Nov 3 01:38:36 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thinh Nguyen X-Patchwork-Id: 10666437 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 45B0B14E2 for ; Sat, 3 Nov 2018 01:38:40 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 33C5E2ADEB for ; Sat, 3 Nov 2018 01:38:40 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 23D9F2AF1A; Sat, 3 Nov 2018 01:38:40 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-8.0 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 7843F2ADEB for ; Sat, 3 Nov 2018 01:38:39 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728405AbeKCKsJ (ORCPT ); Sat, 3 Nov 2018 06:48:09 -0400 Received: from smtprelay.synopsys.com ([198.182.60.111]:54992 "EHLO smtprelay.synopsys.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728233AbeKCKsJ (ORCPT ); Sat, 3 Nov 2018 06:48:09 -0400 Received: from mailhost.synopsys.com (mailhost3.synopsys.com [10.12.238.238]) by smtprelay.synopsys.com (Postfix) with ESMTP id 3F37010C0EA9; Fri, 2 Nov 2018 18:38:38 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=synopsys.com; s=mail; t=1541209118; bh=bvoOGv9apnF5LSAbc/2LVdURSOzY/tiloRbB4DSboUk=; h=Date:From:Subject:To:CC:From; b=ZUEpmxTS1cnBDNJQNHmfHvHYGVZHm71gnNK12lw9zHJZXeKotiizNtZXRdvJrvFiI KPd37HZ/tavFYqM8q7JROyBSTIjynLKmMEU8vInndBPllcG+agpznBGYfjHY+JdxFg mIz4Znp9RiS1Qx3PRMmtVbN4/uojTP2GNz771Z6eAXCiFwAgdCZwuolXf8EDqMPsqO aOHVDk83DWonEi+laWddmz4vtVflv5OL1fK4TvUfKgU46JU7ZL2YCaNL2PA8XLe1NK LmEHp3wu3zXexaykkxXxSIY7GtoDg2E/KmDd1cpYMzU56jFVzdy9vWE8mVLoeiFRs7 LuEQ3TT0Gwz8w== Received: from us01wehtc1.internal.synopsys.com (us01wehtc1-vip.internal.synopsys.com [10.12.239.236]) by mailhost.synopsys.com (Postfix) with ESMTP id 2D5323D5D; Fri, 2 Nov 2018 18:38:38 -0700 (PDT) Received: from US01WEHTC3.internal.synopsys.com (10.15.84.232) by us01wehtc1.internal.synopsys.com (10.12.239.231) with Microsoft SMTP Server (TLS) id 14.3.408.0; Fri, 2 Nov 2018 18:38:38 -0700 Received: from te-lab16 (10.13.184.20) by US01WEHTC3.internal.synopsys.com (10.15.84.231) with Microsoft SMTP Server (TLS) id 14.3.408.0; Fri, 2 Nov 2018 18:38:36 -0700 Received: by te-lab16 (sSMTP sendmail emulation); Fri, 02 Nov 2018 18:38:36 -0700 Date: Fri, 2 Nov 2018 18:38:36 -0700 Message-ID: <0847a3b3698e70ae9a736cca8161706715e0a42e.1541209076.git.thinhn@synopsys.com> From: Thinh Nguyen Subject: [PATCH] usb: dwc3: debugfs: Dump internal states To: Felipe Balbi , CC: John Youn MIME-Version: 1.0 X-Originating-IP: [10.13.184.20] Sender: linux-usb-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-usb@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP To dump internal LSP and endpoint state debug registers, we must write to GDBGLSPMUX register. This patch correctly dump internal BMU, LSP, and endpoint states from the debug registers. If the controller is in device mode, all LSP and endpoint state registers will be dumped. In host mode, the user must specify a LSP register by writing to the debugfs attribute "internal_states" with the LSP number selection. Fixes: 80b776340c78 ("usb: dwc3: Dump LSP and BMU debug info") Signed-off-by: Thinh Nguyen --- drivers/usb/dwc3/core.h | 11 +++ drivers/usb/dwc3/debugfs.c | 169 +++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 176 insertions(+), 4 deletions(-) diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 5bfb62533e0f..662e49ae0510 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -174,6 +174,12 @@ #define DWC3_GSBUSCFG0_INCRBRSTENA (1 << 0) /* undefined length enable */ #define DWC3_GSBUSCFG0_INCRBRST_MASK 0xff +/* Global Debug LSP MUX Select */ +#define DWC3_GDBGLSPMUX_ENDBC BIT(15) /* Host only */ +#define DWC3_GDBGLSPMUX_HOSTSELECT(n) ((n) & 0x3fff) +#define DWC3_GDBGLSPMUX_DEVSELECT(n) (((n) & 0xf) << 4) +#define DWC3_GDBGLSPMUX_EPSELECT(n) ((n) & 0xf) + /* Global Debug Queue/FIFO Space Available Register */ #define DWC3_GDBGFIFOSPACE_NUM(n) ((n) & 0x1f) #define DWC3_GDBGFIFOSPACE_TYPE(n) (((n) << 5) & 0x1e0) @@ -253,6 +259,9 @@ #define DWC3_GSTS_DEVICE_IP BIT(6) #define DWC3_GSTS_CSR_TIMEOUT BIT(5) #define DWC3_GSTS_BUS_ERR_ADDR_VLD BIT(4) +#define DWC3_GSTS_CURMOD(n) ((n) & 0x3) +#define DWC3_GSTS_CURMOD_DEVICE 0 +#define DWC3_GSTS_CURMOD_HOST 1 /* Global USB2 PHY Configuration Register */ #define DWC3_GUSB2PHYCFG_PHYSOFTRST BIT(31) @@ -945,6 +954,7 @@ struct dwc3_scratchpad_array { * @hwparams: copy of hwparams registers * @root: debugfs root folder pointer * @regset: debugfs pointer to regdump file + * @dbg_lsp_select: current debug lsp mux register selection * @test_mode: true when we're entering a USB test mode * @test_mode_nr: test feature selector * @lpm_nyet_threshold: LPM NYET response threshold @@ -1120,6 +1130,7 @@ struct dwc3 { struct dwc3_hwparams hwparams; struct dentry *root; struct debugfs_regset32 *regset; + int dbg_lsp_select; u8 test_mode; u8 test_mode_nr; diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c index df8e73ec3342..cf3c95838a96 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,164 @@ static const struct debugfs_reg32 dwc3_regs[] = { dump_register(OSTS), }; +static u32 dwc3_host_lsp_register(struct dwc3 *dwc, bool is_dbc, int select) +{ + unsigned long flags; + u32 reg; + + reg = DWC3_GDBGLSPMUX_HOSTSELECT(select); + + if (is_dbc) + reg |= DWC3_GDBGLSPMUX_ENDBC; + + spin_lock_irqsave(&dwc->lock, flags); + dwc3_writel(dwc->regs, DWC3_GDBGLSPMUX, reg); + reg = dwc3_readl(dwc->regs, DWC3_GDBGLSP); + spin_unlock_irqrestore(&dwc->lock, flags); + + return reg; +} + +static u32 dwc3_gadget_lsp_register(struct dwc3 *dwc, int select) +{ + unsigned long flags; + u32 reg; + + reg = DWC3_GDBGLSPMUX_DEVSELECT(select); + + spin_lock_irqsave(&dwc->lock, flags); + dwc3_writel(dwc->regs, DWC3_GDBGLSPMUX, reg); + reg = dwc3_readl(dwc->regs, DWC3_GDBGLSP); + spin_unlock_irqrestore(&dwc->lock, flags); + + return reg; +} + +static u64 dwc3_ep_info_register(struct dwc3 *dwc, int select) +{ + unsigned long flags; + u64 ep_info; + u32 lower_32_bits; + u32 upper_32_bits; + u32 reg; + + reg = DWC3_GDBGLSPMUX_EPSELECT(select); + + spin_lock_irqsave(&dwc->lock, flags); + 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); + spin_unlock_irqrestore(&dwc->lock, flags); + + ep_info = ((u64)upper_32_bits << 32) | lower_32_bits; + + return ep_info; +} + +static void dwc3_dump_host_internal_states(struct seq_file *s) +{ + struct dwc3 *dwc = s->private; + int sel; + u32 reg; + + sel = dwc->dbg_lsp_select; + + if (sel == DWC3_LSP_MUX_UNSELECTED) { + seq_printf(s, "No LSP MUX selection\n"); + return; + } + + reg = dwc3_host_lsp_register(dwc, false, sel); + seq_printf(s, "GDBGLSP[%d] = 0x%08x\n", sel, reg); + + reg = dwc3_host_lsp_register(dwc, true, sel); + seq_printf(s, "GDBGLSP_DBC[%d] = 0x%08x\n", sel, reg); +} + +static void dwc3_dump_gadget_internal_states(struct seq_file *s) +{ + struct dwc3 *dwc = s->private; + int num_selects = 16; + int i; + u32 reg; + u64 ep_info; + + for (i = 0; i < num_selects; i++) { + reg = dwc3_gadget_lsp_register(dwc, i); + seq_printf(s, "GDBGLSP[%d] = 0x%08x\n", i, reg); + } + + for (i = 0; i < dwc->num_eps; i++) { + ep_info = dwc3_ep_info_register(dwc, i); + seq_printf(s, "GDBGEPINFO[%d] = 0x%016llx\n", i, ep_info); + } +} + +static int dwc3_internal_states_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); + + reg = dwc3_readl(dwc->regs, DWC3_GDBGBMU); + spin_unlock_irqrestore(&dwc->lock, flags); + + seq_printf(s, "GDBGBMU = 0x%08x\n", reg); + + switch (current_mode) { + case DWC3_GSTS_CURMOD_HOST: + dwc3_dump_host_internal_states(s); + break; + case DWC3_GSTS_CURMOD_DEVICE: + dwc3_dump_gadget_internal_states(s); + break; + default: + seq_printf(s, "Mode is unknown, no LSP register printed\n"); + break; + } + + return 0; +} + +static int dwc3_internal_states_open(struct inode *inode, struct file *file) +{ + return single_open(file, dwc3_internal_states_show, inode->i_private); +} + +static ssize_t dwc3_internal_states_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; + char buf[32] = { 0 }; + int sel; + int ret; + + if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) + return -EFAULT; + + ret = kstrtoint(buf, 0, &sel); + if (ret) + return ret; + + dwc->dbg_lsp_select = sel; + + return count; +} + +static const struct file_operations dwc3_internal_states_fops = { + .open = dwc3_internal_states_open, + .write = dwc3_internal_states_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; @@ -742,6 +898,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 +909,9 @@ void dwc3_debugfs_init(struct dwc3 *dwc) debugfs_create_regset32("regdump", S_IRUGO, root, dwc->regset); + debugfs_create_file("internal_states", S_IRUGO | S_IWUSR, root, dwc, + &dwc3_internal_states_fops); + if (IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)) { debugfs_create_file("mode", S_IRUGO | S_IWUSR, root, dwc, &dwc3_mode_fops);