blob: fec96199f82f95fb3d5018318b5b3a480ee513fd [file] [log] [blame]
/**
* debugfs.c - DesignWare USB3 DRD Controller DebugFS file
*
* Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com
*
* 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.
*
* 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.
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/ptrace.h>
#include <linux/types.h>
#include <linux/spinlock.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/delay.h>
#include <linux/uaccess.h>
#include <linux/usb/ch9.h>
#include "core.h"
#include "gadget.h"
#include "io.h"
#include "debug.h"
#define dump_register(nm) \
{ \
.name = __stringify(nm), \
.offset = DWC3_ ##nm - DWC3_GLOBALS_REGS_START, \
}
static const struct debugfs_reg32 dwc3_regs[] = {
dump_register(GSBUSCFG0),
dump_register(GSBUSCFG1),
dump_register(GTXTHRCFG),
dump_register(GRXTHRCFG),
dump_register(GCTL),
dump_register(GEVTEN),
dump_register(GSTS),
dump_register(GSNPSID),
dump_register(GGPIO),
dump_register(GUID),
dump_register(GUCTL),
dump_register(GBUSERRADDR0),
dump_register(GBUSERRADDR1),
dump_register(GPRTBIMAP0),
dump_register(GPRTBIMAP1),
dump_register(GHWPARAMS0),
dump_register(GHWPARAMS1),
dump_register(GHWPARAMS2),
dump_register(GHWPARAMS3),
dump_register(GHWPARAMS4),
dump_register(GHWPARAMS5),
dump_register(GHWPARAMS6),
dump_register(GHWPARAMS7),
dump_register(GDBGFIFOSPACE),
dump_register(GDBGLTSSM),
dump_register(GPRTBIMAP_HS0),
dump_register(GPRTBIMAP_HS1),
dump_register(GPRTBIMAP_FS0),
dump_register(GPRTBIMAP_FS1),
dump_register(GUSB2PHYCFG(0)),
dump_register(GUSB2PHYCFG(1)),
dump_register(GUSB2PHYCFG(2)),
dump_register(GUSB2PHYCFG(3)),
dump_register(GUSB2PHYCFG(4)),
dump_register(GUSB2PHYCFG(5)),
dump_register(GUSB2PHYCFG(6)),
dump_register(GUSB2PHYCFG(7)),
dump_register(GUSB2PHYCFG(8)),
dump_register(GUSB2PHYCFG(9)),
dump_register(GUSB2PHYCFG(10)),
dump_register(GUSB2PHYCFG(11)),
dump_register(GUSB2PHYCFG(12)),
dump_register(GUSB2PHYCFG(13)),
dump_register(GUSB2PHYCFG(14)),
dump_register(GUSB2PHYCFG(15)),
dump_register(GUSB2I2CCTL(0)),
dump_register(GUSB2I2CCTL(1)),
dump_register(GUSB2I2CCTL(2)),
dump_register(GUSB2I2CCTL(3)),
dump_register(GUSB2I2CCTL(4)),
dump_register(GUSB2I2CCTL(5)),
dump_register(GUSB2I2CCTL(6)),
dump_register(GUSB2I2CCTL(7)),
dump_register(GUSB2I2CCTL(8)),
dump_register(GUSB2I2CCTL(9)),
dump_register(GUSB2I2CCTL(10)),
dump_register(GUSB2I2CCTL(11)),
dump_register(GUSB2I2CCTL(12)),
dump_register(GUSB2I2CCTL(13)),
dump_register(GUSB2I2CCTL(14)),
dump_register(GUSB2I2CCTL(15)),
dump_register(GUSB2PHYACC(0)),
dump_register(GUSB2PHYACC(1)),
dump_register(GUSB2PHYACC(2)),
dump_register(GUSB2PHYACC(3)),
dump_register(GUSB2PHYACC(4)),
dump_register(GUSB2PHYACC(5)),
dump_register(GUSB2PHYACC(6)),
dump_register(GUSB2PHYACC(7)),
dump_register(GUSB2PHYACC(8)),
dump_register(GUSB2PHYACC(9)),
dump_register(GUSB2PHYACC(10)),
dump_register(GUSB2PHYACC(11)),
dump_register(GUSB2PHYACC(12)),
dump_register(GUSB2PHYACC(13)),
dump_register(GUSB2PHYACC(14)),
dump_register(GUSB2PHYACC(15)),
dump_register(GUSB3PIPECTL(0)),
dump_register(GUSB3PIPECTL(1)),
dump_register(GUSB3PIPECTL(2)),
dump_register(GUSB3PIPECTL(3)),
dump_register(GUSB3PIPECTL(4)),
dump_register(GUSB3PIPECTL(5)),
dump_register(GUSB3PIPECTL(6)),
dump_register(GUSB3PIPECTL(7)),
dump_register(GUSB3PIPECTL(8)),
dump_register(GUSB3PIPECTL(9)),
dump_register(GUSB3PIPECTL(10)),
dump_register(GUSB3PIPECTL(11)),
dump_register(GUSB3PIPECTL(12)),
dump_register(GUSB3PIPECTL(13)),
dump_register(GUSB3PIPECTL(14)),
dump_register(GUSB3PIPECTL(15)),
dump_register(GTXFIFOSIZ(0)),
dump_register(GTXFIFOSIZ(1)),
dump_register(GTXFIFOSIZ(2)),
dump_register(GTXFIFOSIZ(3)),
dump_register(GTXFIFOSIZ(4)),
dump_register(GTXFIFOSIZ(5)),
dump_register(GTXFIFOSIZ(6)),
dump_register(GTXFIFOSIZ(7)),
dump_register(GTXFIFOSIZ(8)),
dump_register(GTXFIFOSIZ(9)),
dump_register(GTXFIFOSIZ(10)),
dump_register(GTXFIFOSIZ(11)),
dump_register(GTXFIFOSIZ(12)),
dump_register(GTXFIFOSIZ(13)),
dump_register(GTXFIFOSIZ(14)),
dump_register(GTXFIFOSIZ(15)),
dump_register(GTXFIFOSIZ(16)),
dump_register(GTXFIFOSIZ(17)),
dump_register(GTXFIFOSIZ(18)),
dump_register(GTXFIFOSIZ(19)),
dump_register(GTXFIFOSIZ(20)),
dump_register(GTXFIFOSIZ(21)),
dump_register(GTXFIFOSIZ(22)),
dump_register(GTXFIFOSIZ(23)),
dump_register(GTXFIFOSIZ(24)),
dump_register(GTXFIFOSIZ(25)),
dump_register(GTXFIFOSIZ(26)),
dump_register(GTXFIFOSIZ(27)),
dump_register(GTXFIFOSIZ(28)),
dump_register(GTXFIFOSIZ(29)),
dump_register(GTXFIFOSIZ(30)),
dump_register(GTXFIFOSIZ(31)),
dump_register(GRXFIFOSIZ(0)),
dump_register(GRXFIFOSIZ(1)),
dump_register(GRXFIFOSIZ(2)),
dump_register(GRXFIFOSIZ(3)),
dump_register(GRXFIFOSIZ(4)),
dump_register(GRXFIFOSIZ(5)),
dump_register(GRXFIFOSIZ(6)),
dump_register(GRXFIFOSIZ(7)),
dump_register(GRXFIFOSIZ(8)),
dump_register(GRXFIFOSIZ(9)),
dump_register(GRXFIFOSIZ(10)),
dump_register(GRXFIFOSIZ(11)),
dump_register(GRXFIFOSIZ(12)),
dump_register(GRXFIFOSIZ(13)),
dump_register(GRXFIFOSIZ(14)),
dump_register(GRXFIFOSIZ(15)),
dump_register(GRXFIFOSIZ(16)),
dump_register(GRXFIFOSIZ(17)),
dump_register(GRXFIFOSIZ(18)),
dump_register(GRXFIFOSIZ(19)),
dump_register(GRXFIFOSIZ(20)),
dump_register(GRXFIFOSIZ(21)),
dump_register(GRXFIFOSIZ(22)),
dump_register(GRXFIFOSIZ(23)),
dump_register(GRXFIFOSIZ(24)),
dump_register(GRXFIFOSIZ(25)),
dump_register(GRXFIFOSIZ(26)),
dump_register(GRXFIFOSIZ(27)),
dump_register(GRXFIFOSIZ(28)),
dump_register(GRXFIFOSIZ(29)),
dump_register(GRXFIFOSIZ(30)),
dump_register(GRXFIFOSIZ(31)),
dump_register(GEVNTADRLO(0)),
dump_register(GEVNTADRHI(0)),
dump_register(GEVNTSIZ(0)),
dump_register(GEVNTCOUNT(0)),
dump_register(GHWPARAMS8),
dump_register(DCFG),
dump_register(DCTL),
dump_register(DEVTEN),
dump_register(DSTS),
dump_register(DGCMDPAR),
dump_register(DGCMD),
dump_register(DALEPENA),
dump_register(DEPCMDPAR2(0)),
dump_register(DEPCMDPAR2(1)),
dump_register(DEPCMDPAR2(2)),
dump_register(DEPCMDPAR2(3)),
dump_register(DEPCMDPAR2(4)),
dump_register(DEPCMDPAR2(5)),
dump_register(DEPCMDPAR2(6)),
dump_register(DEPCMDPAR2(7)),
dump_register(DEPCMDPAR2(8)),
dump_register(DEPCMDPAR2(9)),
dump_register(DEPCMDPAR2(10)),
dump_register(DEPCMDPAR2(11)),
dump_register(DEPCMDPAR2(12)),
dump_register(DEPCMDPAR2(13)),
dump_register(DEPCMDPAR2(14)),
dump_register(DEPCMDPAR2(15)),
dump_register(DEPCMDPAR2(16)),
dump_register(DEPCMDPAR2(17)),
dump_register(DEPCMDPAR2(18)),
dump_register(DEPCMDPAR2(19)),
dump_register(DEPCMDPAR2(20)),
dump_register(DEPCMDPAR2(21)),
dump_register(DEPCMDPAR2(22)),
dump_register(DEPCMDPAR2(23)),
dump_register(DEPCMDPAR2(24)),
dump_register(DEPCMDPAR2(25)),
dump_register(DEPCMDPAR2(26)),
dump_register(DEPCMDPAR2(27)),
dump_register(DEPCMDPAR2(28)),
dump_register(DEPCMDPAR2(29)),
dump_register(DEPCMDPAR2(30)),
dump_register(DEPCMDPAR2(31)),
dump_register(DEPCMDPAR1(0)),
dump_register(DEPCMDPAR1(1)),
dump_register(DEPCMDPAR1(2)),
dump_register(DEPCMDPAR1(3)),
dump_register(DEPCMDPAR1(4)),
dump_register(DEPCMDPAR1(5)),
dump_register(DEPCMDPAR1(6)),
dump_register(DEPCMDPAR1(7)),
dump_register(DEPCMDPAR1(8)),
dump_register(DEPCMDPAR1(9)),
dump_register(DEPCMDPAR1(10)),
dump_register(DEPCMDPAR1(11)),
dump_register(DEPCMDPAR1(12)),
dump_register(DEPCMDPAR1(13)),
dump_register(DEPCMDPAR1(14)),
dump_register(DEPCMDPAR1(15)),
dump_register(DEPCMDPAR1(16)),
dump_register(DEPCMDPAR1(17)),
dump_register(DEPCMDPAR1(18)),
dump_register(DEPCMDPAR1(19)),
dump_register(DEPCMDPAR1(20)),
dump_register(DEPCMDPAR1(21)),
dump_register(DEPCMDPAR1(22)),
dump_register(DEPCMDPAR1(23)),
dump_register(DEPCMDPAR1(24)),
dump_register(DEPCMDPAR1(25)),
dump_register(DEPCMDPAR1(26)),
dump_register(DEPCMDPAR1(27)),
dump_register(DEPCMDPAR1(28)),
dump_register(DEPCMDPAR1(29)),
dump_register(DEPCMDPAR1(30)),
dump_register(DEPCMDPAR1(31)),
dump_register(DEPCMDPAR0(0)),
dump_register(DEPCMDPAR0(1)),
dump_register(DEPCMDPAR0(2)),
dump_register(DEPCMDPAR0(3)),
dump_register(DEPCMDPAR0(4)),
dump_register(DEPCMDPAR0(5)),
dump_register(DEPCMDPAR0(6)),
dump_register(DEPCMDPAR0(7)),
dump_register(DEPCMDPAR0(8)),
dump_register(DEPCMDPAR0(9)),
dump_register(DEPCMDPAR0(10)),
dump_register(DEPCMDPAR0(11)),
dump_register(DEPCMDPAR0(12)),
dump_register(DEPCMDPAR0(13)),
dump_register(DEPCMDPAR0(14)),
dump_register(DEPCMDPAR0(15)),
dump_register(DEPCMDPAR0(16)),
dump_register(DEPCMDPAR0(17)),
dump_register(DEPCMDPAR0(18)),
dump_register(DEPCMDPAR0(19)),
dump_register(DEPCMDPAR0(20)),
dump_register(DEPCMDPAR0(21)),
dump_register(DEPCMDPAR0(22)),
dump_register(DEPCMDPAR0(23)),
dump_register(DEPCMDPAR0(24)),
dump_register(DEPCMDPAR0(25)),
dump_register(DEPCMDPAR0(26)),
dump_register(DEPCMDPAR0(27)),
dump_register(DEPCMDPAR0(28)),
dump_register(DEPCMDPAR0(29)),
dump_register(DEPCMDPAR0(30)),
dump_register(DEPCMDPAR0(31)),
dump_register(DEPCMD(0)),
dump_register(DEPCMD(1)),
dump_register(DEPCMD(2)),
dump_register(DEPCMD(3)),
dump_register(DEPCMD(4)),
dump_register(DEPCMD(5)),
dump_register(DEPCMD(6)),
dump_register(DEPCMD(7)),
dump_register(DEPCMD(8)),
dump_register(DEPCMD(9)),
dump_register(DEPCMD(10)),
dump_register(DEPCMD(11)),
dump_register(DEPCMD(12)),
dump_register(DEPCMD(13)),
dump_register(DEPCMD(14)),
dump_register(DEPCMD(15)),
dump_register(DEPCMD(16)),
dump_register(DEPCMD(17)),
dump_register(DEPCMD(18)),
dump_register(DEPCMD(19)),
dump_register(DEPCMD(20)),
dump_register(DEPCMD(21)),
dump_register(DEPCMD(22)),
dump_register(DEPCMD(23)),
dump_register(DEPCMD(24)),
dump_register(DEPCMD(25)),
dump_register(DEPCMD(26)),
dump_register(DEPCMD(27)),
dump_register(DEPCMD(28)),
dump_register(DEPCMD(29)),
dump_register(DEPCMD(30)),
dump_register(DEPCMD(31)),
dump_register(OCFG),
dump_register(OCTL),
dump_register(OEVT),
dump_register(OEVTEN),
dump_register(OSTS),
};
static int dwc3_mode_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_GCTL);
spin_unlock_irqrestore(&dwc->lock, flags);
switch (DWC3_GCTL_PRTCAP(reg)) {
case DWC3_GCTL_PRTCAP_HOST:
seq_printf(s, "host\n");
break;
case DWC3_GCTL_PRTCAP_DEVICE:
seq_printf(s, "device\n");
break;
case DWC3_GCTL_PRTCAP_OTG:
seq_printf(s, "OTG\n");
break;
default:
seq_printf(s, "UNKNOWN %08x\n", DWC3_GCTL_PRTCAP(reg));
}
return 0;
}
static int dwc3_mode_open(struct inode *inode, struct file *file)
{
return single_open(file, dwc3_mode_show, inode->i_private);
}
static ssize_t dwc3_mode_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 mode = 0;
char buf[32];
if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
return -EFAULT;
if (!strncmp(buf, "host", 4))
mode |= DWC3_GCTL_PRTCAP_HOST;
if (!strncmp(buf, "device", 6))
mode |= DWC3_GCTL_PRTCAP_DEVICE;
if (!strncmp(buf, "otg", 3))
mode |= DWC3_GCTL_PRTCAP_OTG;
if (mode) {
spin_lock_irqsave(&dwc->lock, flags);
dwc3_set_mode(dwc, mode);
spin_unlock_irqrestore(&dwc->lock, flags);
}
return count;
}
static const struct file_operations dwc3_mode_fops = {
.open = dwc3_mode_open,
.write = dwc3_mode_write,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
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,
};
static int dwc3_hiber_enabled_show(struct seq_file *s, void *unused)
{
struct dwc3 *dwc = s->private;
if (dwc->hiber_enabled)
seq_puts(s, "hibernation enabled\n");
else
seq_puts(s, "hibernation disabled\n");
return 0;
}
static int dwc3_hiber_enabled_open(struct inode *inode, struct file *file)
{
return single_open(file, dwc3_hiber_enabled_show, inode->i_private);
}
static ssize_t dwc3_hiber_enabled_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];
int enabled = 0;
if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
return -EFAULT;
sscanf(buf, "%d", &enabled);
dwc->hiber_enabled = enabled;
return count;
}
static const struct file_operations dwc3_hiber_enabled_fops = {
.open = dwc3_hiber_enabled_open,
.write = dwc3_hiber_enabled_write,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static void dwc3_dump_requests(struct seq_file *s, struct list_head *head,
const char *list_name)
{
struct dwc3_request *dreq;
if (list_empty(head)) {
seq_printf(s, "list %s is empty\n", list_name);
return;
}
seq_printf(s, "list %s:\n", list_name);
list_for_each_entry(dreq, head, list) {
struct usb_request *req = &dreq->request;
seq_printf(s, "usb_request@0x%p: buf@0x%p(dma@0x%llx): len=0x%x: status=%d: actual=0x%x; start_slot=%u: trb@%p: trb_dma@0x%llx\n",
req, req->buf, (unsigned long long)req->dma,
req->length, req->status, req->actual,
dreq->start_slot, dreq->trb,
(unsigned long long)dreq->trb_dma);
}
}
static void dwc3_dump_trbs(struct seq_file *s, struct dwc3_ep *dep)
{
struct dwc3_trb *trb;
int i;
seq_printf(s, "busy_slot = %u, free_slot = %u\n",
dep->busy_slot % DWC3_TRB_NUM,
dep->free_slot % DWC3_TRB_NUM);
seq_puts(s, "\t bpl, bph, size, ctrl\n");
for (i = 0; i < DWC3_TRB_NUM; i++) {
trb = &dep->trb_pool[i];
if (i == dep->busy_slot % DWC3_TRB_NUM) {
seq_puts(s, "busy_slot--|\n");
seq_puts(s, " \\\n");
}
if (i == dep->free_slot % DWC3_TRB_NUM) {
seq_puts(s, "free_slot--|\n");
seq_puts(s, " \\\n");
}
seq_printf(s, "trb[%d](dma&0x%llx): %08x, %08x, %08x, %08x\n",
i, (unsigned long long) dep->trb_pool_dma + i * sizeof(*trb),
trb->bpl, trb->bph, trb->size, trb->ctrl);
}
}
static void dwc3_dump_dev_event(struct seq_file *s, union dwc3_event event)
{
seq_puts(s, "[0]DEV ");
seq_printf(s, "[1:7]%s ",
event.type.type == DWC3_EVENT_TYPE_DEV ? "TYPE_DEV" :
event.type.type == DWC3_EVENT_TYPE_CARKIT ? "TYPE_CARKIT" :
"TYPE_I2C");
switch (event.devt.type) {
case DWC3_DEVICE_EVENT_DISCONNECT:
seq_puts(s, "[8:11] DISCONNECT ");
break;
case DWC3_DEVICE_EVENT_RESET:
seq_puts(s, "[8:11] RESET ");
break;
case DWC3_DEVICE_EVENT_CONNECT_DONE:
seq_puts(s, "[8:11] CONNECTION_DONE ");
break;
case DWC3_DEVICE_EVENT_WAKEUP:
seq_puts(s, "[8:11] WAKEUP ");
break;
case DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE:
seq_puts(s, "[8:11] LINK_CHANGE ");
seq_puts(s, "[16:24] ");
seq_printf(s, "%s ",
event.devt.event_info & BIT(4) ? "SS" : "HS");
switch (event.devt.event_info & DWC3_LINK_STATE_MASK) {
case DWC3_LINK_STATE_U0:
seq_puts(s, "U0");
break;
case DWC3_LINK_STATE_U1:
seq_puts(s, "U1");
break;
case DWC3_LINK_STATE_U2:
seq_puts(s, "U2");
break;
case DWC3_LINK_STATE_U3:
seq_puts(s, "U3");
break;
case DWC3_LINK_STATE_SS_DIS:
seq_puts(s, "SS_DIS");
break;
case DWC3_LINK_STATE_RX_DET:
seq_puts(s, "RX_DET");
break;
case DWC3_LINK_STATE_SS_INACT:
seq_puts(s, "SS_INACT");
break;
case DWC3_LINK_STATE_POLL:
seq_puts(s, "POLL");
break;
case DWC3_LINK_STATE_RECOV:
seq_puts(s, "RECOV");
break;
case DWC3_LINK_STATE_HRESET:
seq_puts(s, "HRESET");
break;
case DWC3_LINK_STATE_CMPLY:
seq_puts(s, "CMPLY");
break;
case DWC3_LINK_STATE_LPBK:
seq_puts(s, "LPBK");
break;
case DWC3_LINK_STATE_RESET:
seq_puts(s, "RESET");
break;
case DWC3_LINK_STATE_RESUME:
seq_puts(s, "RESUME");
break;
}
seq_printf(s, "(%x) ", event.devt.event_info & DWC3_LINK_STATE_MASK);
break;
case DWC3_DEVICE_EVENT_HIBER_REQ:
seq_puts(s, "[8:11] HIBER_REQ ");
break;
case DWC3_DEVICE_EVENT_EOPF:
seq_puts(s, "[8:11] EOPF ");
break;
case DWC3_DEVICE_EVENT_SOF:
seq_puts(s, "[8:11] SOF ");
break;
case DWC3_DEVICE_EVENT_ERRATIC_ERROR:
seq_puts(s, "[8:11] ERRATIC_ERROR ");
break;
case DWC3_DEVICE_EVENT_CMD_CMPL:
seq_puts(s, "[8:11] COMMAND_COMPLETE ");
break;
case DWC3_DEVICE_EVENT_OVERFLOW:
seq_puts(s, "[8:11] OVERFLOW ");
break;
default:
seq_printf(s, "[8:11] UNKNOWN (%x) ", event.devt.type);
}
}
static void dwc3_dump_ep_event(struct seq_file *s, union dwc3_event event)
{
seq_puts(s, "[0]EP ");
seq_printf(s, "[1:5]ep%d ", event.depevt.endpoint_number);
seq_printf(s, "[6:9]%s ",
dwc3_ep_event_string(event.depevt.endpoint_event));
switch (event.depevt.endpoint_event) {
case DWC3_DEPEVT_XFERCOMPLETE:
case DWC3_DEPEVT_XFERINPROGRESS:
if (event.depevt.status & DEPEVT_STATUS_BUSERR)
seq_puts(s, "[12] BUSERR ");
if (event.depevt.status & DEPEVT_STATUS_SHORT)
seq_puts(s, "[13] SHORT ");
if (event.depevt.status & DEPEVT_STATUS_IOC)
seq_puts(s, "[14] IOC ");
if (event.depevt.status & DEPEVT_STATUS_LST)
seq_puts(s, "[15] LST ");
break;
case DWC3_DEPEVT_XFERNOTREADY:
if ((event.depevt.status & 0x3) == 1)
seq_puts(s, "[12:13] Data_Stage ");
else if ((event.depevt.status & 0x3) == 2)
seq_puts(s, "[12:13] Status_Stage ");
if (event.depevt.status & DEPEVT_STATUS_TRANSFER_ACTIVE)
seq_puts(s, "[15] XferActive ");
else
seq_puts(s, "[15] XferNotActive ");
break;
case DWC3_DEPEVT_EPCMDCMPLT:
if (event.depevt.status & BIT(0))
seq_puts(s, "[12:15] Invalid Transfer Resource ");
break;
}
}
static void dwc3_dump_event_buf(struct seq_file *s, struct dwc3_event_buffer *evt)
{
union dwc3_event event;
int i;
seq_printf(s, "evt->buf=0x%p(dma@0x%llx), length=%u, lpos=%u, count=%u, flags=%s\n",
evt->buf, (unsigned long long) evt->dma, evt->length,
evt->lpos, evt->count,
evt->flags & DWC3_EVENT_PENDING ? "pending" : "0");
for (i = 0; i < evt->length; i += 4) {
event.raw = *(u32 *) (evt->buf + i);
if (i == evt->lpos) {
seq_puts(s, "lpos-------|\n");
seq_puts(s, " \\\n");
}
seq_printf(s, "event[%d]: %08x ", i, event.raw);
/* analyze device specific events */
if (event.type.is_devspec)
dwc3_dump_dev_event(s, event);
else
dwc3_dump_ep_event(s, event);
seq_puts(s, "\n");
}
}
static int dwc3_snapshot_show(struct seq_file *s, void *unused)
{
struct dwc3 *dwc = s->private;
unsigned long flags;
int i;
spin_lock_irqsave(&dwc->lock, flags);
for (i = 2; i < DWC3_ENDPOINTS_NUM; i++) {
struct dwc3_ep *dep = dwc->eps[i];
if (!(dep->flags & DWC3_EP_ENABLED))
continue;
seq_printf(s, "[%s]\n", dep->name);
dwc3_dump_requests(s, &dep->request_list, "request_list");
dwc3_dump_requests(s, &dep->req_queued, "req_queued");
if (!list_empty(&dep->req_queued))
dwc3_dump_trbs(s, dep);
seq_puts(s, "\n");
}
dwc3_dump_event_buf(s, dwc->ev_buffs[0]);
spin_unlock_irqrestore(&dwc->lock, flags);
return 0;
}
static int dwc3_snapshot_open(struct inode *inode, struct file *file)
{
return single_open(file, dwc3_snapshot_show, inode->i_private);
}
static ssize_t dwc3_snapshot_write(struct file *file,
const char __user *ubuf, size_t count, loff_t *ppos)
{
return count;
}
static const struct file_operations dwc3_snapshot_fops = {
.open = dwc3_snapshot_open,
.write = dwc3_snapshot_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 (!root) {
ret = -ENOMEM;
goto err0;
}
dwc->root = root;
dwc->regset = kzalloc(sizeof(*dwc->regset), GFP_KERNEL);
if (!dwc->regset) {
ret = -ENOMEM;
goto err1;
}
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;
}
file = debugfs_create_file("hiber_enabled", S_IRUGO | S_IWUSR,
root, dwc, &dwc3_hiber_enabled_fops);
if (!file) {
ret = -ENOMEM;
goto err1;
}
file = debugfs_create_file("snapshot", S_IRUGO | S_IWUSR, root,
dwc, &dwc3_snapshot_fops);
if (!file) {
ret = -ENOMEM;
goto err1;
}
}
return 0;
err1:
debugfs_remove_recursive(root);
err0:
return ret;
}
void dwc3_debugfs_exit(struct dwc3 *dwc)
{
debugfs_remove_recursive(dwc->root);
dwc->root = NULL;
}