summaryrefslogtreecommitdiff
path: root/drivers/usb/dwc3/debugfs.c
diff options
context:
space:
mode:
authorMayank Rana <mrana@codeaurora.org>2014-02-28 11:04:38 -0800
committerDavid Keitel <dkeitel@codeaurora.org>2016-03-22 11:06:26 -0700
commit7fcaf722805ce3e528fcd920614e04c574095097 (patch)
tree32a4d4426badbbb983815840b432a330bfe3cddf /drivers/usb/dwc3/debugfs.c
parentce73ca32ca2b78eba2a6084d1dec780c2f0fdec4 (diff)
usb: dwc3: Keep track of interrupt statistics
This change adds debug support to log all received different events with endpoint0, other endpoints and gadget events. It tracks these events per endpoint and displays the same. For each of the endpoint event counters, add the rate (in Hz) at which it occurs in order to better analyze performance and aid in optimization. How to use: - Mount debugfs - To see received all dwc3 events/interrupts cat /sys/kernel/debug/<base_address>.dwc3/int_events - To clear all received dwc3 events/interrupts echo 0 > /sys/kernel/debug/<base_address>.dwc3/int_events Change-Id: Ibf5f3ee57f69c87f94f55a58f50792075be24fbb Signed-off-by: Gilad Broner <gbroner@codeaurora.org> Signed-off-by: Mayank Rana <mrana@codeaurora.org>
Diffstat (limited to 'drivers/usb/dwc3/debugfs.c')
-rw-r--r--drivers/usb/dwc3/debugfs.c159
1 files changed, 159 insertions, 0 deletions
diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c
index abd1ce332942..5372dac984e1 100644
--- a/drivers/usb/dwc3/debugfs.c
+++ b/drivers/usb/dwc3/debugfs.c
@@ -40,6 +40,9 @@
.offset = DWC3_ ##nm - DWC3_GLOBALS_REGS_START, \
}
+#define ep_event_rate(ev, c, p, dt) \
+ ((dt) ? ((c.ev - p.ev) * (MSEC_PER_SEC)) / (dt) : 0)
+
static const struct debugfs_reg32 dwc3_regs[] = {
dump_register(GSBUSCFG0),
dump_register(GSBUSCFG1),
@@ -1020,6 +1023,154 @@ const struct file_operations dwc3_gadget_dbg_data_fops = {
.release = single_release,
};
+static ssize_t dwc3_store_int_events(struct file *file,
+ const char __user *ubuf, size_t count, loff_t *ppos)
+{
+ int clear_stats, i;
+ unsigned long flags;
+ struct seq_file *s = file->private_data;
+ struct dwc3 *dwc = s->private;
+ struct dwc3_ep *dep;
+ struct timespec ts;
+
+ if (ubuf == NULL) {
+ pr_err("[%s] EINVAL\n", __func__);
+ goto done;
+ }
+
+ if (sscanf(ubuf, "%u", &clear_stats) != 1 || clear_stats != 0) {
+ pr_err("Wrong value. To clear stats, enter value as 0.\n");
+ goto done;
+ }
+
+ spin_lock_irqsave(&dwc->lock, flags);
+
+ pr_debug("%s(): clearing debug interrupt buffers\n", __func__);
+ ts = current_kernel_time();
+ for (i = 0; i < DWC3_ENDPOINTS_NUM; i++) {
+ dep = dwc->eps[i];
+ memset(&dep->dbg_ep_events, 0, sizeof(dep->dbg_ep_events));
+ memset(&dep->dbg_ep_events_diff, 0, sizeof(dep->dbg_ep_events));
+ dep->dbg_ep_events_ts = ts;
+ }
+ memset(&dwc->dbg_gadget_events, 0, sizeof(dwc->dbg_gadget_events));
+
+ spin_unlock_irqrestore(&dwc->lock, flags);
+
+done:
+ return count;
+}
+
+static int dwc3_gadget_int_events_show(struct seq_file *s, void *unused)
+{
+ unsigned long flags;
+ struct dwc3 *dwc = s->private;
+ struct dwc3_gadget_events *dbg_gadget_events;
+ struct dwc3_ep *dep;
+ int i;
+ struct timespec ts_delta;
+ struct timespec ts_current;
+ u32 ts_delta_ms;
+
+ spin_lock_irqsave(&dwc->lock, flags);
+ dbg_gadget_events = &dwc->dbg_gadget_events;
+
+ for (i = 0; i < DWC3_ENDPOINTS_NUM; i++) {
+ dep = dwc->eps[i];
+
+ if (dep == NULL || !(dep->flags & DWC3_EP_ENABLED))
+ continue;
+
+ ts_current = current_kernel_time();
+ ts_delta = timespec_sub(ts_current, dep->dbg_ep_events_ts);
+ ts_delta_ms = ts_delta.tv_nsec / NSEC_PER_MSEC +
+ ts_delta.tv_sec * MSEC_PER_SEC;
+
+ seq_printf(s, "\n\n===== dbg_ep_events for EP(%d) %s =====\n",
+ i, dep->name);
+ seq_printf(s, "xfercomplete:%u @ %luHz\n",
+ dep->dbg_ep_events.xfercomplete,
+ ep_event_rate(xfercomplete, dep->dbg_ep_events,
+ dep->dbg_ep_events_diff, ts_delta_ms));
+ seq_printf(s, "xfernotready:%u @ %luHz\n",
+ dep->dbg_ep_events.xfernotready,
+ ep_event_rate(xfernotready, dep->dbg_ep_events,
+ dep->dbg_ep_events_diff, ts_delta_ms));
+ seq_printf(s, "control_data:%u @ %luHz\n",
+ dep->dbg_ep_events.control_data,
+ ep_event_rate(control_data, dep->dbg_ep_events,
+ dep->dbg_ep_events_diff, ts_delta_ms));
+ seq_printf(s, "control_status:%u @ %luHz\n",
+ dep->dbg_ep_events.control_status,
+ ep_event_rate(control_status, dep->dbg_ep_events,
+ dep->dbg_ep_events_diff, ts_delta_ms));
+ seq_printf(s, "xferinprogress:%u @ %luHz\n",
+ dep->dbg_ep_events.xferinprogress,
+ ep_event_rate(xferinprogress, dep->dbg_ep_events,
+ dep->dbg_ep_events_diff, ts_delta_ms));
+ seq_printf(s, "rxtxfifoevent:%u @ %luHz\n",
+ dep->dbg_ep_events.rxtxfifoevent,
+ ep_event_rate(rxtxfifoevent, dep->dbg_ep_events,
+ dep->dbg_ep_events_diff, ts_delta_ms));
+ seq_printf(s, "streamevent:%u @ %luHz\n",
+ dep->dbg_ep_events.streamevent,
+ ep_event_rate(streamevent, dep->dbg_ep_events,
+ dep->dbg_ep_events_diff, ts_delta_ms));
+ seq_printf(s, "epcmdcomplt:%u @ %luHz\n",
+ dep->dbg_ep_events.epcmdcomplete,
+ ep_event_rate(epcmdcomplete, dep->dbg_ep_events,
+ dep->dbg_ep_events_diff, ts_delta_ms));
+ seq_printf(s, "cmdcmplt:%u @ %luHz\n",
+ dep->dbg_ep_events.cmdcmplt,
+ ep_event_rate(cmdcmplt, dep->dbg_ep_events,
+ dep->dbg_ep_events_diff, ts_delta_ms));
+ seq_printf(s, "unknown:%u @ %luHz\n",
+ dep->dbg_ep_events.unknown_event,
+ ep_event_rate(unknown_event, dep->dbg_ep_events,
+ dep->dbg_ep_events_diff, ts_delta_ms));
+ seq_printf(s, "total:%u @ %luHz\n",
+ dep->dbg_ep_events.total,
+ ep_event_rate(total, dep->dbg_ep_events,
+ dep->dbg_ep_events_diff, ts_delta_ms));
+
+ dep->dbg_ep_events_ts = ts_current;
+ dep->dbg_ep_events_diff = dep->dbg_ep_events;
+ }
+
+ seq_puts(s, "\n=== dbg_gadget events ==\n");
+ seq_printf(s, "disconnect:%u\n reset:%u\n",
+ dbg_gadget_events->disconnect, dbg_gadget_events->reset);
+ seq_printf(s, "connect:%u\n wakeup:%u\n",
+ dbg_gadget_events->connect, dbg_gadget_events->wakeup);
+ seq_printf(s, "link_status_change:%u\n eopf:%u\n",
+ dbg_gadget_events->link_status_change, dbg_gadget_events->eopf);
+ seq_printf(s, "sof:%u\n suspend:%u\n",
+ dbg_gadget_events->sof, dbg_gadget_events->suspend);
+ seq_printf(s, "erratic_error:%u\n overflow:%u\n",
+ dbg_gadget_events->erratic_error,
+ dbg_gadget_events->overflow);
+ seq_printf(s, "vendor_dev_test_lmp:%u\n cmdcmplt:%u\n",
+ dbg_gadget_events->vendor_dev_test_lmp,
+ dbg_gadget_events->cmdcmplt);
+ seq_printf(s, "unknown_event:%u\n", dbg_gadget_events->unknown_event);
+
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ return 0;
+}
+
+static int dwc3_gadget_events_open(struct inode *inode, struct file *f)
+{
+ return single_open(f, dwc3_gadget_int_events_show, inode->i_private);
+}
+
+const struct file_operations dwc3_gadget_dbg_events_fops = {
+ .open = dwc3_gadget_events_open,
+ .read = seq_read,
+ .write = dwc3_store_int_events,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
int dwc3_debugfs_init(struct dwc3 *dwc)
{
struct dentry *root;
@@ -1103,6 +1254,14 @@ int dwc3_debugfs_init(struct dwc3 *dwc)
ret = -ENOMEM;
goto err1;
}
+
+ file = debugfs_create_file("int_events", S_IRUGO | S_IWUSR, root,
+ dwc, &dwc3_gadget_dbg_events_fops);
+ if (!file) {
+ ret = -ENOMEM;
+ goto err1;
+ }
+
return 0;
err1: