.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),
.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;
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:
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,
break;
case DEPEVT_STATUS_CONTROL_STATUS:
+ dep->dbg_ep_events.control_status++;
if (dwc->ep0_next_event != DWC3_EP0_NRDY_STATUS)
return;
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;
}
}
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",
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 {
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);
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;
}
}
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,
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");
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);