Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f2f2c261 authored by Kyle Yan's avatar Kyle Yan Committed by Gerrit - the friendly Code Review server
Browse files

Merge "usb: dwc3: Keep track of interrupt statistics" into msm-4.8

parents 9b4bef5e 0c667b42
Loading
Loading
Loading
Loading
+36 −0
Original line number Diff line number Diff line
@@ -516,6 +516,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	(1 << 0)
#define DWC3_EP_FLAG_WEDGED	(1 << 1)

@@ -550,6 +579,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;
@@ -601,6 +633,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 {
@@ -1083,6 +1118,7 @@ struct dwc3 {
	unsigned int		irq_dbg_index;

	wait_queue_head_t	wait_linkstate;
	struct dwc3_gadget_events	dbg_gadget_events;
};

/* -------------------------------------------------------------------------- */
+159 −0
Original line number Diff line number Diff line
@@ -62,6 +62,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),
@@ -843,6 +846,155 @@ static void dwc3_debugfs_create_endpoint_dirs(struct dwc3 *dwc,
	}
}

static ssize_t dwc3_store_int_events(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;
	struct timespec ts;
	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);
	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);
	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, "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;
@@ -892,6 +1044,13 @@ int dwc3_debugfs_init(struct dwc3 *dwc)
			dev_dbg(dwc->dev, "Can't create debugfs link_state\n");

		dwc3_debugfs_create_endpoint_dirs(dwc, root);

		file = debugfs_create_file("int_events", 0644, root, dwc,
				&dwc3_gadget_dbg_events_fops);
		if (!file) {
			ret = -ENOMEM;
			goto err1;
		}
	}

	return 0;
+21 −0
Original line number Diff line number Diff line
@@ -1072,9 +1072,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:
		dwc3_trace(trace_dwc3_ep0, "Control Data");
		dep->dbg_ep_events.control_data++;

		/*
		 * We already have a DATA transfer in the controller's cache,
@@ -1098,6 +1105,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;

@@ -1118,23 +1126,36 @@ 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;

	dwc3_trace(trace_dwc3_ep0, "%s: state '%s'",
			dwc3_ep_event_string(event),
			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;
	}
}
+20 −1
Original line number Diff line number Diff line
@@ -2613,9 +2613,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)) {
			dwc3_trace(trace_dwc3_gadget,
@@ -2627,9 +2630,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 {
@@ -2653,6 +2658,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);
@@ -2675,9 +2681,11 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
		break;
	case DWC3_DEPEVT_RXTXFIFOEVT:
		dwc3_trace(trace_dwc3_gadget, "%s FIFO Overrun", 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;
	}
}
@@ -3266,15 +3274,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, false);
		dwc->dbg_gadget_events.wakeup++;
		break;
	case DWC3_DEVICE_EVENT_HIBER_REQ:
		if (dev_WARN_ONCE(dwc->dev, !dwc->has_hibernation,
@@ -3285,13 +3297,15 @@ 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");

			dwc->dbg_gadget_events.suspend++;
			/*
			 * Ignore suspend event until the gadget enters into
			 * USB_STATE_CONFIGURED state.
@@ -3303,18 +3317,23 @@ static void dwc3_gadget_interrupt(struct dwc3 *dwc,
		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");
		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");
		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);