mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-08 21:22:09 -04:00
Merge "usb: dwc3: Keep track of interrupt statistics"
This commit is contained in:
commit
e47b3563e4
4 changed files with 238 additions and 0 deletions
|
|
@ -639,6 +639,35 @@ 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 unknown_event;
|
||||
unsigned int total;
|
||||
};
|
||||
|
||||
#define DWC3_EP_FLAG_STALLED BIT(0)
|
||||
#define DWC3_EP_FLAG_WEDGED BIT(1)
|
||||
|
||||
|
|
@ -673,6 +702,9 @@ struct dwc3_event_buffer {
|
|||
* isochronous START TRANSFER command failure workaround
|
||||
* @start_cmd_status: the status of testing START TRANSFER command with
|
||||
* combo_num = 'b00
|
||||
* @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;
|
||||
|
|
@ -723,6 +755,10 @@ struct dwc3_ep {
|
|||
/* For isochronous START TRANSFER workaround only */
|
||||
u8 combo_num;
|
||||
int start_cmd_status;
|
||||
|
||||
struct dwc3_ep_events dbg_ep_events;
|
||||
struct dwc3_ep_events dbg_ep_events_diff;
|
||||
ktime_t dbg_ep_events_kt;
|
||||
};
|
||||
|
||||
enum dwc3_phy {
|
||||
|
|
@ -1231,6 +1267,7 @@ struct dwc3 {
|
|||
|
||||
unsigned int index;
|
||||
void *dwc_ipc_log_ctxt;
|
||||
struct dwc3_gadget_events dbg_gadget_events;
|
||||
};
|
||||
|
||||
#define INCRX_BURST_MODE 0
|
||||
|
|
|
|||
|
|
@ -56,6 +56,9 @@
|
|||
}
|
||||
|
||||
|
||||
#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),
|
||||
|
|
@ -902,9 +905,156 @@ static void dwc3_debugfs_create_endpoint_dirs(struct dwc3 *dwc,
|
|||
}
|
||||
}
|
||||
|
||||
static ssize_t dwc3_gadget_int_events_store(struct file *file,
|
||||
const char __user *ubuf, size_t count, loff_t *ppos)
|
||||
{
|
||||
int i, ret;
|
||||
unsigned long flags;
|
||||
struct seq_file *s = file->private_data;
|
||||
struct dwc3 *dwc = s->private;
|
||||
struct dwc3_ep *dep;
|
||||
ktime_t kt;
|
||||
u8 clear_stats;
|
||||
|
||||
if (ubuf == NULL) {
|
||||
pr_err("[%s] EINVAL\n", __func__);
|
||||
ret = -EINVAL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = kstrtou8_from_user(ubuf, count, 0, &clear_stats);
|
||||
if (ret < 0) {
|
||||
pr_err("can't get enter value.\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (clear_stats != 0) {
|
||||
pr_err("Wrong value. To clear stats, enter value as 0.\n");
|
||||
ret = -EINVAL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
pr_debug("%s(): clearing debug interrupt buffers\n", __func__);
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
kt = ktime_get_real();
|
||||
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_kt = kt;
|
||||
}
|
||||
memset(&dwc->dbg_gadget_events, 0, sizeof(dwc->dbg_gadget_events));
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
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;
|
||||
ktime_t now;
|
||||
s64 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;
|
||||
|
||||
now = ktime_get_real();
|
||||
delta_ms = ktime_ms_delta(now, dep->dbg_ep_events_kt);
|
||||
|
||||
seq_printf(s, "\n\n===== dbg_ep_events for EP(%d) %s =====\n",
|
||||
i, dep->name);
|
||||
seq_printf(s, "xfercomplete:%u @ %llHz\n",
|
||||
dep->dbg_ep_events.xfercomplete,
|
||||
ep_event_rate(xfercomplete, dep->dbg_ep_events,
|
||||
dep->dbg_ep_events_diff, delta_ms));
|
||||
seq_printf(s, "xfernotready:%u @ %llHz\n",
|
||||
dep->dbg_ep_events.xfernotready,
|
||||
ep_event_rate(xfernotready, dep->dbg_ep_events,
|
||||
dep->dbg_ep_events_diff, delta_ms));
|
||||
seq_printf(s, "control_data:%u @ %llHz\n",
|
||||
dep->dbg_ep_events.control_data,
|
||||
ep_event_rate(control_data, dep->dbg_ep_events,
|
||||
dep->dbg_ep_events_diff, delta_ms));
|
||||
seq_printf(s, "control_status:%u @ %llHz\n",
|
||||
dep->dbg_ep_events.control_status,
|
||||
ep_event_rate(control_status, dep->dbg_ep_events,
|
||||
dep->dbg_ep_events_diff, delta_ms));
|
||||
seq_printf(s, "xferinprogress:%u @ %llHz\n",
|
||||
dep->dbg_ep_events.xferinprogress,
|
||||
ep_event_rate(xferinprogress, dep->dbg_ep_events,
|
||||
dep->dbg_ep_events_diff, delta_ms));
|
||||
seq_printf(s, "rxtxfifoevent:%u @ %llHz\n",
|
||||
dep->dbg_ep_events.rxtxfifoevent,
|
||||
ep_event_rate(rxtxfifoevent, dep->dbg_ep_events,
|
||||
dep->dbg_ep_events_diff, delta_ms));
|
||||
seq_printf(s, "streamevent:%u @ %llHz\n",
|
||||
dep->dbg_ep_events.streamevent,
|
||||
ep_event_rate(streamevent, dep->dbg_ep_events,
|
||||
dep->dbg_ep_events_diff, delta_ms));
|
||||
seq_printf(s, "epcmdcomplt:%u @ %llHz\n",
|
||||
dep->dbg_ep_events.epcmdcomplete,
|
||||
ep_event_rate(epcmdcomplete, dep->dbg_ep_events,
|
||||
dep->dbg_ep_events_diff, delta_ms));
|
||||
seq_printf(s, "unknown:%u @ %llHz\n",
|
||||
dep->dbg_ep_events.unknown_event,
|
||||
ep_event_rate(unknown_event, dep->dbg_ep_events,
|
||||
dep->dbg_ep_events_diff, delta_ms));
|
||||
seq_printf(s, "total:%u @ %llHz\n",
|
||||
dep->dbg_ep_events.total,
|
||||
ep_event_rate(total, dep->dbg_ep_events,
|
||||
dep->dbg_ep_events_diff, delta_ms));
|
||||
|
||||
dep->dbg_ep_events_kt = now;
|
||||
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_int_events_open(struct inode *inode, struct file *f)
|
||||
{
|
||||
return single_open(f, dwc3_gadget_int_events_show, inode->i_private);
|
||||
}
|
||||
|
||||
static const struct file_operations dwc3_gadget_int_events_fops = {
|
||||
.open = dwc3_gadget_int_events_open,
|
||||
.write = dwc3_gadget_int_events_store,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
void dwc3_debugfs_init(struct dwc3 *dwc)
|
||||
{
|
||||
struct dentry *root;
|
||||
struct dentry *file;
|
||||
|
||||
dwc->regset = kzalloc(sizeof(*dwc->regset), GFP_KERNEL);
|
||||
if (!dwc->regset)
|
||||
|
|
@ -936,6 +1086,11 @@ void dwc3_debugfs_init(struct dwc3 *dwc)
|
|||
debugfs_create_file("link_state", S_IRUGO | S_IWUSR, root, dwc,
|
||||
&dwc3_link_state_fops);
|
||||
dwc3_debugfs_create_endpoint_dirs(dwc, root);
|
||||
|
||||
file = debugfs_create_file("int_events", 0644, root, dwc,
|
||||
&dwc3_gadget_int_events_fops);
|
||||
if (!file)
|
||||
dev_dbg(dwc->dev, "Can't create debugfs int_events\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1063,8 +1063,16 @@ 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;
|
||||
|
||||
epnum = event->endpoint_number;
|
||||
dep = dwc->eps[epnum];
|
||||
|
||||
switch (event->status) {
|
||||
case DEPEVT_STATUS_CONTROL_DATA:
|
||||
dep->dbg_ep_events.control_data++;
|
||||
|
||||
/*
|
||||
* We already have a DATA transfer in the controller's cache,
|
||||
* if we receive a XferNotReady(DATA) we will ignore it, unless
|
||||
|
|
@ -1086,6 +1094,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;
|
||||
|
||||
|
|
@ -1117,19 +1126,32 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc,
|
|||
void dwc3_ep0_interrupt(struct dwc3 *dwc,
|
||||
const struct dwc3_event_depevt *event)
|
||||
{
|
||||
struct dwc3_ep *dep;
|
||||
u8 epnum = event->endpoint_number;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2608,14 +2608,19 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
|
|||
return;
|
||||
}
|
||||
|
||||
dep->dbg_ep_events.total++;
|
||||
|
||||
switch (event->endpoint_event) {
|
||||
case DWC3_DEPEVT_XFERINPROGRESS:
|
||||
dep->dbg_ep_events.xferinprogress++;
|
||||
dwc3_gadget_endpoint_transfer_in_progress(dep, event);
|
||||
break;
|
||||
case DWC3_DEPEVT_XFERNOTREADY:
|
||||
dep->dbg_ep_events.xfernotready++;
|
||||
dwc3_gadget_endpoint_transfer_not_ready(dep, event);
|
||||
break;
|
||||
case DWC3_DEPEVT_EPCMDCMPLT:
|
||||
dep->dbg_ep_events.epcmdcomplete++;
|
||||
cmd = DEPEVT_PARAMETER_CMD(event->parameters);
|
||||
|
||||
if (cmd == DWC3_DEPCMD_ENDTRANSFER) {
|
||||
|
|
@ -2624,8 +2629,13 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
|
|||
}
|
||||
break;
|
||||
case DWC3_DEPEVT_STREAMEVT:
|
||||
dep->dbg_ep_events.streamevent++;
|
||||
break;
|
||||
case DWC3_DEPEVT_XFERCOMPLETE:
|
||||
dep->dbg_ep_events.xfercomplete++;
|
||||
break;
|
||||
case DWC3_DEPEVT_RXTXFIFOEVT:
|
||||
dep->dbg_ep_events.rxtxfifoevent++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -3094,15 +3104,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,
|
||||
|
|
@ -3113,10 +3127,12 @@ 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_EOPF:
|
||||
/* It changed to be suspend event for version 2.30a and above */
|
||||
if (dwc->revision >= DWC3_REVISION_230A) {
|
||||
dwc->dbg_gadget_events.suspend++;
|
||||
/*
|
||||
* Ignore suspend event until the gadget enters into
|
||||
* USB_STATE_CONFIGURED state.
|
||||
|
|
@ -3127,12 +3143,20 @@ static void dwc3_gadget_interrupt(struct dwc3 *dwc,
|
|||
}
|
||||
break;
|
||||
case DWC3_DEVICE_EVENT_SOF:
|
||||
dwc->dbg_gadget_events.sof++;
|
||||
break;
|
||||
case DWC3_DEVICE_EVENT_ERRATIC_ERROR:
|
||||
dwc->dbg_gadget_events.erratic_error++;
|
||||
break;
|
||||
case DWC3_DEVICE_EVENT_CMD_CMPL:
|
||||
dwc->dbg_gadget_events.cmdcmplt++;
|
||||
break;
|
||||
case DWC3_DEVICE_EVENT_OVERFLOW:
|
||||
dwc->dbg_gadget_events.overflow++;
|
||||
break;
|
||||
default:
|
||||
dev_WARN(dwc->dev, "UNKNOWN IRQ %d\n", event->type);
|
||||
dwc->dbg_gadget_events.unknown_event++;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue