summaryrefslogtreecommitdiff
path: root/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-event-reporting.c
blob: 45574391b0f0fe2b0855de508a1ccd5fdf10ef51 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
/*
 * Freescale GPMI NFC NAND Flash Driver
 *
 * Copyright (C) 2010 Freescale Semiconductor, Inc.
 * Copyright (C) 2008 Embedded Alley Solutions, Inc.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include "gpmi-nfc.h"

#if defined(EVENT_REPORTING)

/*
 * This variable and module parameter controls whether the driver reports event
 * information by printing to the console.
 */

static int report_events;
module_param(report_events, int, 0600);

/**
 * struct event - A single record in the event trace.
 *
 * @time:         The time at which the event occurred.
 * @nesting:      Indicates function call nesting.
 * @description:  A description of the event.
 */

struct event {
	ktime_t       time;
	unsigned int  nesting;
	char          *description;
};

/**
 * The event trace.
 *
 * @overhead:  The delay to take a time stamp and nothing else.
 * @nesting:   The current nesting level.
 * @overflow:  Indicates the trace overflowed.
 * @next:      Index of the next event to write.
 * @events:    The array of events.
 */

#define MAX_EVENT_COUNT  (200)

static struct {
	ktime_t       overhead;
	int           nesting;
	int           overflow;
	unsigned int  next;
	struct event  events[MAX_EVENT_COUNT];
} event_trace;

/**
 * gpmi_nfc_reset_event_trace() - Resets the event trace.
 */
void gpmi_nfc_reset_event_trace(void)
{
	event_trace.nesting  = 0;
	event_trace.overflow = false;
	event_trace.next     = 0;
}

/**
 * gpmi_nfc_add_event() - Adds an event to the event trace.
 *
 * @description:  A description of the event.
 * @delta:        A delta to the nesting level for this event [-1, 0, 1].
 */
void gpmi_nfc_add_event(char *description, int delta)
{
	struct event  *event;

	if (!report_events)
		return;

	if (event_trace.overflow)
		return;

	if (event_trace.next >= MAX_EVENT_COUNT) {
		event_trace.overflow = true;
		return;
	}

	event = event_trace.events + event_trace.next;

	event->time = ktime_get();

	event->description = description;

	if (!delta)
		event->nesting = event_trace.nesting;
	else if (delta < 0) {
		event->nesting = event_trace.nesting - 1;
		event_trace.nesting -= 2;
	} else {
		event->nesting = event_trace.nesting + 1;
		event_trace.nesting += 2;
	}

	if (event_trace.nesting < 0)
		event_trace.nesting = 0;

	event_trace.next++;

}

/**
 * gpmi_nfc_start_event_trace() - Starts an event trace.
 *
 * @description:  A description of the first event.
 */
void gpmi_nfc_start_event_trace(char *description)
{

	ktime_t  t0;
	ktime_t  t1;

	if (!report_events)
		return;

	gpmi_nfc_reset_event_trace();

	t0 = ktime_get();
	t1 = ktime_get();

	event_trace.overhead = ktime_sub(t1, t0);

	gpmi_nfc_add_event(description, 1);

}

/**
 * gpmi_nfc_dump_event_trace() - Dumps the event trace.
 */
void gpmi_nfc_dump_event_trace(void)
{
	unsigned int  i;
	time_t        seconds;
	long          nanoseconds;
	char          line[100];
	int           o;
	struct event  *first_event;
	struct event  *last_event;
	struct event  *matching_event;
	struct event  *event;
	ktime_t       delta;

	/* Check if event reporting is turned off. */

	if (!report_events)
		return;

	/* Print important facts about this event trace. */

	pr_info("\n+----------------\n");

	pr_info("|  Overhead    : [%d:%d]\n", event_trace.overhead.tv.sec,
						event_trace.overhead.tv.nsec);

	if (!event_trace.next) {
		pr_info("|  No Events\n");
		return;
	}

	first_event = event_trace.events;
	last_event  = event_trace.events + (event_trace.next - 1);

	delta = ktime_sub(last_event->time, first_event->time);
	pr_info("|  Elapsed Time: [%d:%d]\n", delta.tv.sec, delta.tv.nsec);

	if (event_trace.overflow)
		pr_info("|  Overflow!\n");

	/* Print the events in this history. */

	for (i = 0, event = event_trace.events;
					i < event_trace.next; i++, event++) {

		/* Get the delta between this event and the previous event. */

		if (!i) {
			seconds     = 0;
			nanoseconds = 0;
		} else {
			delta = ktime_sub(event[0].time, event[-1].time);
			seconds     = delta.tv.sec;
			nanoseconds = delta.tv.nsec;
		}

		/* Print the current event. */

		o = 0;

		o = snprintf(line, sizeof(line) - o, "|  [%ld:% 10ld]%*s %s",
							seconds, nanoseconds,
							event->nesting, "",
							event->description);
		/* Check if this is the last event in a nested series. */

		if (i && (event[0].nesting < event[-1].nesting)) {

			for (matching_event = event - 1;; matching_event--) {

				if (matching_event < event_trace.events) {
					matching_event = 0;
					break;
				}

				if (matching_event->nesting == event->nesting)
					break;

			}

			if (matching_event) {
				delta = ktime_sub(event->time,
							matching_event->time);
				o += snprintf(line + o, sizeof(line) - o,
						" <%d:%d]", delta.tv.sec,
								delta.tv.nsec);
			}

		}

		/* Check if this is the first event in a nested series. */

		if ((i < event_trace.next - 1) &&
				(event[0].nesting < event[1].nesting)) {

			for (matching_event = event + 1;; matching_event++) {

				if (matching_event >=
					(event_trace.events+event_trace.next)) {
					matching_event = 0;
					break;
				}

				if (matching_event->nesting == event->nesting)
					break;

			}

			if (matching_event) {
				delta = ktime_sub(matching_event->time,
								event->time);
				o += snprintf(line + o, sizeof(line) - o,
						" [%d:%d>", delta.tv.sec,
								delta.tv.nsec);
			}

		}

		pr_info("%s\n", line);

	}

	pr_info("+----------------\n");

}

/**
 * gpmi_nfc_stop_event_trace() - Stops an event trace.
 *
 * @description:  A description of the last event.
 */
void gpmi_nfc_stop_event_trace(char *description)
{
	struct event  *event;

	if (!report_events)
		return;

	/*
	 * We want the end of the trace, no matter what happens. If the trace
	 * has already overflowed, or is about to, just jam this event into the
	 * last spot. Otherwise, add this event like any other.
	 */

	if (event_trace.overflow || (event_trace.next >= MAX_EVENT_COUNT)) {
		event = event_trace.events + (MAX_EVENT_COUNT - 1);
		event->time = ktime_get();
		event->description = description;
		event->nesting     = 0;
	} else {
		gpmi_nfc_add_event(description, -1);
	}

	gpmi_nfc_dump_event_trace();
	gpmi_nfc_reset_event_trace();

}

#endif /* EVENT_REPORTING */