OSDN Git Service

usb: dwc3: Keep track of interrupt statistics
authorMayank Rana <mrana@codeaurora.org>
Fri, 28 Feb 2014 19:04:38 +0000 (11:04 -0800)
committerDavid Keitel <dkeitel@codeaurora.org>
Tue, 22 Mar 2016 18:06:26 +0000 (11:06 -0700)
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>
drivers/usb/dwc3/core.h
drivers/usb/dwc3/debugfs.c
drivers/usb/dwc3/ep0.c
drivers/usb/dwc3/gadget.c

index 16f8906..d15f171 100644 (file)
@@ -447,6 +447,36 @@ struct dwc3_event_buffer {
        struct dwc3             *dwc;
 };
 
+struct dwc3_gadget_events {
+       unsigned int    disconnect;
+       unsigned int    reset;
+       unsigned int    connect;
+       unsigned int    wakeup;
+       unsigned int    link_status_change;
+       unsigned int    eopf;
+       unsigned int    suspend;
+       unsigned int    sof;
+       unsigned int    erratic_error;
+       unsigned int    overflow;
+       unsigned int    vendor_dev_test_lmp;
+       unsigned int    cmdcmplt;
+       unsigned int    unknown_event;
+};
+
+struct dwc3_ep_events {
+       unsigned int    xfercomplete;
+       unsigned int    xfernotready;
+       unsigned int    control_data;
+       unsigned int    control_status;
+       unsigned int    xferinprogress;
+       unsigned int    rxtxfifoevent;
+       unsigned int    streamevent;
+       unsigned int    epcmdcomplete;
+       unsigned int    cmdcmplt;
+       unsigned int    unknown_event;
+       unsigned int    total;
+};
+
 #define DWC3_EP_FLAG_STALLED   (1 << 0)
 #define DWC3_EP_FLAG_WEDGED    (1 << 1)
 
@@ -476,6 +506,9 @@ struct dwc3_event_buffer {
  * @name: a human readable name e.g. ep1out-bulk
  * @direction: true for TX, false for RX
  * @stream_capable: true when streams are enabled
+ * @dbg_ep_events: different events counter for endpoint
+ * @dbg_ep_events_diff: differential events counter for endpoint
+ * @dbg_ep_events_ts: timestamp for previous event counters
  */
 struct dwc3_ep {
        struct usb_ep           endpoint;
@@ -510,6 +543,9 @@ struct dwc3_ep {
 
        unsigned                direction:1;
        unsigned                stream_capable:1;
+       struct dwc3_ep_events   dbg_ep_events;
+       struct dwc3_ep_events   dbg_ep_events_diff;
+       struct timespec         dbg_ep_events_ts;
 };
 
 enum dwc3_phy {
@@ -901,6 +937,8 @@ struct dwc3 {
 
        unsigned                nominal_elastic_buffer:1;
        unsigned                err_evt_seen:1;
+
+       struct dwc3_gadget_events       dbg_gadget_events;
 };
 
 /* -------------------------------------------------------------------------- */
index abd1ce3..5372dac 100644 (file)
@@ -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:
index 925c144..55191ea 100644 (file)
@@ -1074,11 +1074,17 @@ static void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep)
 static void dwc3_ep0_xfernotready(struct dwc3 *dwc,
                const struct dwc3_event_depevt *event)
 {
+       u8                      epnum;
+       struct dwc3_ep          *dep;
+
        dwc->setup_packet_pending = true;
+       epnum = event->endpoint_number;
+       dep = dwc->eps[epnum];
 
        switch (event->status) {
        case DEPEVT_STATUS_CONTROL_DATA:
                dwc3_trace(trace_dwc3_ep0, "Control Data");
+               dep->dbg_ep_events.control_data++;
 
                /*
                 * We already have a DATA transfer in the controller's cache,
@@ -1103,6 +1109,7 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc,
                break;
 
        case DEPEVT_STATUS_CONTROL_STATUS:
+               dep->dbg_ep_events.control_status++;
                if (dwc->ep0_next_event != DWC3_EP0_NRDY_STATUS)
                        return;
 
@@ -1124,25 +1131,36 @@ void dwc3_ep0_interrupt(struct dwc3 *dwc,
                const struct dwc3_event_depevt *event)
 {
        u8                      epnum = event->endpoint_number;
+       struct dwc3_ep          *dep;
 
        dwc3_trace(trace_dwc3_ep0, "%s while ep%d%s in state '%s'",
                        dwc3_ep_event_string(event->endpoint_event),
                        epnum >> 1, (epnum & 1) ? "in" : "out",
                        dwc3_ep0_state_string(dwc->ep0state));
 
+       dep = dwc->eps[epnum];
        switch (event->endpoint_event) {
        case DWC3_DEPEVT_XFERCOMPLETE:
                dwc3_ep0_xfer_complete(dwc, event);
+               dep->dbg_ep_events.xfercomplete++;
                break;
 
        case DWC3_DEPEVT_XFERNOTREADY:
                dwc3_ep0_xfernotready(dwc, event);
+               dep->dbg_ep_events.xfernotready++;
                break;
 
        case DWC3_DEPEVT_XFERINPROGRESS:
+               dep->dbg_ep_events.xferinprogress++;
+               break;
        case DWC3_DEPEVT_RXTXFIFOEVT:
+               dep->dbg_ep_events.rxtxfifoevent++;
+               break;
        case DWC3_DEPEVT_STREAMEVT:
+               dep->dbg_ep_events.streamevent++;
+               break;
        case DWC3_DEPEVT_EPCMDCMPLT:
+               dep->dbg_ep_events.epcmdcomplete++;
                break;
        }
 }
index e6926e5..7d40c92 100644 (file)
@@ -2086,9 +2086,12 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
                return;
        }
 
+       dep->dbg_ep_events.total++;
+
        switch (event->endpoint_event) {
        case DWC3_DEPEVT_XFERCOMPLETE:
                dep->resource_index = 0;
+               dep->dbg_ep_events.xfercomplete++;
 
                if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
                        dev_dbg(dwc->dev, "%s is an Isochronous endpoint\n",
@@ -2099,9 +2102,11 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
                dwc3_endpoint_transfer_complete(dwc, dep, event);
                break;
        case DWC3_DEPEVT_XFERINPROGRESS:
+               dep->dbg_ep_events.xferinprogress++;
                dwc3_endpoint_transfer_complete(dwc, dep, event);
                break;
        case DWC3_DEPEVT_XFERNOTREADY:
+               dep->dbg_ep_events.xfernotready++;
                if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
                        dwc3_gadget_start_isoc(dwc, dep, event);
                } else {
@@ -2124,6 +2129,7 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
 
                break;
        case DWC3_DEPEVT_STREAMEVT:
+               dep->dbg_ep_events.streamevent++;
                if (!usb_endpoint_xfer_bulk(dep->endpoint.desc)) {
                        dev_err(dwc->dev, "Stream event for non-Bulk %s\n",
                                        dep->name);
@@ -2145,9 +2151,11 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
                break;
        case DWC3_DEPEVT_RXTXFIFOEVT:
                dev_dbg(dwc->dev, "%s FIFO Overrun\n", dep->name);
+               dep->dbg_ep_events.rxtxfifoevent++;
                break;
        case DWC3_DEPEVT_EPCMDCMPLT:
                dwc3_trace(trace_dwc3_gadget, "Endpoint Command Complete");
+               dep->dbg_ep_events.epcmdcomplete++;
                break;
        }
 }
@@ -2663,15 +2671,19 @@ static void dwc3_gadget_interrupt(struct dwc3 *dwc,
        switch (event->type) {
        case DWC3_DEVICE_EVENT_DISCONNECT:
                dwc3_gadget_disconnect_interrupt(dwc);
+               dwc->dbg_gadget_events.disconnect++;
                break;
        case DWC3_DEVICE_EVENT_RESET:
                dwc3_gadget_reset_interrupt(dwc);
+               dwc->dbg_gadget_events.reset++;
                break;
        case DWC3_DEVICE_EVENT_CONNECT_DONE:
                dwc3_gadget_conndone_interrupt(dwc);
+               dwc->dbg_gadget_events.connect++;
                break;
        case DWC3_DEVICE_EVENT_WAKEUP:
                dwc3_gadget_wakeup_interrupt(dwc);
+               dwc->dbg_gadget_events.wakeup++;
                break;
        case DWC3_DEVICE_EVENT_HIBER_REQ:
                if (dev_WARN_ONCE(dwc->dev, !dwc->has_hibernation,
@@ -2682,18 +2694,22 @@ static void dwc3_gadget_interrupt(struct dwc3 *dwc,
                break;
        case DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE:
                dwc3_gadget_linksts_change_interrupt(dwc, event->event_info);
+               dwc->dbg_gadget_events.link_status_change++;
                break;
        case DWC3_DEVICE_EVENT_SUSPEND:
                if (dwc->revision < DWC3_REVISION_230A) {
                        dwc3_trace(trace_dwc3_gadget, "End of Periodic Frame");
+                       dwc->dbg_gadget_events.eopf++;
                } else {
                        dwc3_trace(trace_dwc3_gadget, "U3/L1-L2 Suspend Event");
                        dbg_event(0xFF, "GAD SUS", 0);
+                       dwc->dbg_gadget_events.suspend++;
                        dwc3_gadget_suspend_interrupt(dwc, event->event_info);
                }
                break;
        case DWC3_DEVICE_EVENT_SOF:
                dwc3_trace(trace_dwc3_gadget, "Start of Periodic Frame");
+               dwc->dbg_gadget_events.sof++;
                break;
        case DWC3_DEVICE_EVENT_ERRATIC_ERROR:
                dwc3_trace(trace_dwc3_gadget, "Erratic Error");
@@ -2701,16 +2717,20 @@ static void dwc3_gadget_interrupt(struct dwc3 *dwc,
                        dbg_event(0xFF, "ERROR", 0);
                        dwc3_dump_reg_info(dwc);
                }
+               dwc->dbg_gadget_events.erratic_error++;
                break;
        case DWC3_DEVICE_EVENT_CMD_CMPL:
                dwc3_trace(trace_dwc3_gadget, "Command Complete");
+               dwc->dbg_gadget_events.cmdcmplt++;
                break;
        case DWC3_DEVICE_EVENT_OVERFLOW:
                dwc3_trace(trace_dwc3_gadget, "Overflow");
                dbg_event(0xFF, "OVERFL", 0);
+               dwc->dbg_gadget_events.overflow++;
                break;
        default:
                dev_WARN(dwc->dev, "UNKNOWN IRQ %d\n", event->type);
+               dwc->dbg_gadget_events.unknown_event++;
        }
 
        dwc->err_evt_seen = (event->type == DWC3_DEVICE_EVENT_ERRATIC_ERROR);