summaryrefslogtreecommitdiff
path: root/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-event-reporting.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mtd/nand/gpmi-nfc/gpmi-nfc-event-reporting.c')
-rw-r--r--drivers/mtd/nand/gpmi-nfc/gpmi-nfc-event-reporting.c307
1 files changed, 307 insertions, 0 deletions
diff --git a/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-event-reporting.c b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-event-reporting.c
new file mode 100644
index 000000000000..45574391b0f0
--- /dev/null
+++ b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-event-reporting.c
@@ -0,0 +1,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 */