diff options
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.c | 307 |
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 */ |