aboutsummaryrefslogtreecommitdiff
path: root/drivers/usb/dwc3/debugfs.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/dwc3/debugfs.c')
-rw-r--r--drivers/usb/dwc3/debugfs.c304
1 files changed, 243 insertions, 61 deletions
diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c
index 433c97c15fc..9ac37fe1b6a 100644
--- a/drivers/usb/dwc3/debugfs.c
+++ b/drivers/usb/dwc3/debugfs.c
@@ -6,34 +6,14 @@
* Authors: Felipe Balbi <balbi@ti.com>,
* Sebastian Andrzej Siewior <bigeasy@linutronix.de>
*
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. The names of the above-listed copyright holders may not be used
- * to endorse or promote products derived from this software without
- * specific prior written permission.
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 of
+ * the License as published by the Free Software Foundation.
*
- * ALTERNATIVELY, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2, as published by the Free
- * Software Foundation.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
- * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
- * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
*/
#include <linux/kernel.h>
@@ -46,6 +26,8 @@
#include <linux/delay.h>
#include <linux/uaccess.h>
+#include <linux/usb/ch9.h>
+
#include "core.h"
#include "gadget.h"
#include "io.h"
@@ -54,7 +36,7 @@
#define dump_register(nm) \
{ \
.name = __stringify(nm), \
- .offset = DWC3_ ##nm, \
+ .offset = DWC3_ ##nm - DWC3_GLOBALS_REGS_START, \
}
static const struct debugfs_reg32 dwc3_regs[] = {
@@ -370,31 +352,11 @@ static const struct debugfs_reg32 dwc3_regs[] = {
dump_register(OCFG),
dump_register(OCTL),
+ dump_register(OEVT),
dump_register(OEVTEN),
dump_register(OSTS),
};
-static int dwc3_regdump_show(struct seq_file *s, void *unused)
-{
- struct dwc3 *dwc = s->private;
-
- seq_printf(s, "DesignWare USB3 Core Register Dump\n");
- debugfs_print_regs32(s, dwc3_regs, ARRAY_SIZE(dwc3_regs),
- dwc->regs, "");
- return 0;
-}
-
-static int dwc3_regdump_open(struct inode *inode, struct file *file)
-{
- return single_open(file, dwc3_regdump_show, inode->i_private);
-}
-
-static const struct file_operations dwc3_regdump_fops = {
- .open = dwc3_regdump_open,
- .read = seq_read,
- .release = single_release,
-};
-
static int dwc3_mode_show(struct seq_file *s, void *unused)
{
struct dwc3 *dwc = s->private;
@@ -464,34 +426,254 @@ static const struct file_operations dwc3_mode_fops = {
.release = single_release,
};
-int __devinit dwc3_debugfs_init(struct dwc3 *dwc)
+static int dwc3_testmode_show(struct seq_file *s, void *unused)
+{
+ struct dwc3 *dwc = s->private;
+ unsigned long flags;
+ u32 reg;
+
+ spin_lock_irqsave(&dwc->lock, flags);
+ reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ reg &= DWC3_DCTL_TSTCTRL_MASK;
+ reg >>= 1;
+ spin_unlock_irqrestore(&dwc->lock, flags);
+
+ switch (reg) {
+ case 0:
+ seq_printf(s, "no test\n");
+ break;
+ case TEST_J:
+ seq_printf(s, "test_j\n");
+ break;
+ case TEST_K:
+ seq_printf(s, "test_k\n");
+ break;
+ case TEST_SE0_NAK:
+ seq_printf(s, "test_se0_nak\n");
+ break;
+ case TEST_PACKET:
+ seq_printf(s, "test_packet\n");
+ break;
+ case TEST_FORCE_EN:
+ seq_printf(s, "test_force_enable\n");
+ break;
+ default:
+ seq_printf(s, "UNKNOWN %d\n", reg);
+ }
+
+ return 0;
+}
+
+static int dwc3_testmode_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, dwc3_testmode_show, inode->i_private);
+}
+
+static ssize_t dwc3_testmode_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;
+ u32 testmode = 0;
+ char buf[32];
+
+ if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
+ return -EFAULT;
+
+ if (!strncmp(buf, "test_j", 6))
+ testmode = TEST_J;
+ else if (!strncmp(buf, "test_k", 6))
+ testmode = TEST_K;
+ else if (!strncmp(buf, "test_se0_nak", 12))
+ testmode = TEST_SE0_NAK;
+ else if (!strncmp(buf, "test_packet", 11))
+ testmode = TEST_PACKET;
+ else if (!strncmp(buf, "test_force_enable", 17))
+ testmode = TEST_FORCE_EN;
+ else
+ testmode = 0;
+
+ spin_lock_irqsave(&dwc->lock, flags);
+ dwc3_gadget_set_test_mode(dwc, testmode);
+ spin_unlock_irqrestore(&dwc->lock, flags);
+
+ return count;
+}
+
+static const struct file_operations dwc3_testmode_fops = {
+ .open = dwc3_testmode_open,
+ .write = dwc3_testmode_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int dwc3_link_state_show(struct seq_file *s, void *unused)
+{
+ struct dwc3 *dwc = s->private;
+ unsigned long flags;
+ enum dwc3_link_state state;
+ u32 reg;
+
+ spin_lock_irqsave(&dwc->lock, flags);
+ reg = dwc3_readl(dwc->regs, DWC3_DSTS);
+ state = DWC3_DSTS_USBLNKST(reg);
+ spin_unlock_irqrestore(&dwc->lock, flags);
+
+ switch (state) {
+ case DWC3_LINK_STATE_U0:
+ seq_printf(s, "U0\n");
+ break;
+ case DWC3_LINK_STATE_U1:
+ seq_printf(s, "U1\n");
+ break;
+ case DWC3_LINK_STATE_U2:
+ seq_printf(s, "U2\n");
+ break;
+ case DWC3_LINK_STATE_U3:
+ seq_printf(s, "U3\n");
+ break;
+ case DWC3_LINK_STATE_SS_DIS:
+ seq_printf(s, "SS.Disabled\n");
+ break;
+ case DWC3_LINK_STATE_RX_DET:
+ seq_printf(s, "Rx.Detect\n");
+ break;
+ case DWC3_LINK_STATE_SS_INACT:
+ seq_printf(s, "SS.Inactive\n");
+ break;
+ case DWC3_LINK_STATE_POLL:
+ seq_printf(s, "Poll\n");
+ break;
+ case DWC3_LINK_STATE_RECOV:
+ seq_printf(s, "Recovery\n");
+ break;
+ case DWC3_LINK_STATE_HRESET:
+ seq_printf(s, "HRESET\n");
+ break;
+ case DWC3_LINK_STATE_CMPLY:
+ seq_printf(s, "Compliance\n");
+ break;
+ case DWC3_LINK_STATE_LPBK:
+ seq_printf(s, "Loopback\n");
+ break;
+ case DWC3_LINK_STATE_RESET:
+ seq_printf(s, "Reset\n");
+ break;
+ case DWC3_LINK_STATE_RESUME:
+ seq_printf(s, "Resume\n");
+ break;
+ default:
+ seq_printf(s, "UNKNOWN %d\n", state);
+ }
+
+ return 0;
+}
+
+static int dwc3_link_state_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, dwc3_link_state_show, inode->i_private);
+}
+
+static ssize_t dwc3_link_state_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;
+ enum dwc3_link_state state = 0;
+ char buf[32];
+
+ if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
+ return -EFAULT;
+
+ if (!strncmp(buf, "SS.Disabled", 11))
+ state = DWC3_LINK_STATE_SS_DIS;
+ else if (!strncmp(buf, "Rx.Detect", 9))
+ state = DWC3_LINK_STATE_RX_DET;
+ else if (!strncmp(buf, "SS.Inactive", 11))
+ state = DWC3_LINK_STATE_SS_INACT;
+ else if (!strncmp(buf, "Recovery", 8))
+ state = DWC3_LINK_STATE_RECOV;
+ else if (!strncmp(buf, "Compliance", 10))
+ state = DWC3_LINK_STATE_CMPLY;
+ else if (!strncmp(buf, "Loopback", 8))
+ state = DWC3_LINK_STATE_LPBK;
+ else
+ return -EINVAL;
+
+ spin_lock_irqsave(&dwc->lock, flags);
+ dwc3_gadget_set_link_state(dwc, state);
+ spin_unlock_irqrestore(&dwc->lock, flags);
+
+ return count;
+}
+
+static const struct file_operations dwc3_link_state_fops = {
+ .open = dwc3_link_state_open,
+ .write = dwc3_link_state_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+int dwc3_debugfs_init(struct dwc3 *dwc)
{
struct dentry *root;
struct dentry *file;
int ret;
root = debugfs_create_dir(dev_name(dwc->dev), NULL);
- if (IS_ERR(root)) {
- ret = PTR_ERR(root);
+ if (!root) {
+ ret = -ENOMEM;
goto err0;
}
dwc->root = root;
- file = debugfs_create_file("regdump", S_IRUGO, root, dwc,
- &dwc3_regdump_fops);
- if (IS_ERR(file)) {
- ret = PTR_ERR(file);
+ dwc->regset = kzalloc(sizeof(*dwc->regset), GFP_KERNEL);
+ if (!dwc->regset) {
+ ret = -ENOMEM;
goto err1;
}
- file = debugfs_create_file("mode", S_IRUGO | S_IWUSR, root,
- dwc, &dwc3_mode_fops);
- if (IS_ERR(file)) {
- ret = PTR_ERR(file);
+ dwc->regset->regs = dwc3_regs;
+ dwc->regset->nregs = ARRAY_SIZE(dwc3_regs);
+ dwc->regset->base = dwc->regs;
+
+ file = debugfs_create_regset32("regdump", S_IRUGO, root, dwc->regset);
+ if (!file) {
+ ret = -ENOMEM;
goto err1;
}
+ if (IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)) {
+ file = debugfs_create_file("mode", S_IRUGO | S_IWUSR, root,
+ dwc, &dwc3_mode_fops);
+ if (!file) {
+ ret = -ENOMEM;
+ goto err1;
+ }
+ }
+
+ if (IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE) ||
+ IS_ENABLED(CONFIG_USB_DWC3_GADGET)) {
+ file = debugfs_create_file("testmode", S_IRUGO | S_IWUSR, root,
+ dwc, &dwc3_testmode_fops);
+ if (!file) {
+ ret = -ENOMEM;
+ goto err1;
+ }
+
+ file = debugfs_create_file("link_state", S_IRUGO | S_IWUSR, root,
+ dwc, &dwc3_link_state_fops);
+ if (!file) {
+ ret = -ENOMEM;
+ goto err1;
+ }
+ }
+
return 0;
err1:
@@ -501,7 +683,7 @@ err0:
return ret;
}
-void __devexit dwc3_debugfs_exit(struct dwc3 *dwc)
+void dwc3_debugfs_exit(struct dwc3 *dwc)
{
debugfs_remove_recursive(dwc->root);
dwc->root = NULL;