CONFIG_CMD_IMLS           List all images found in NOR flash
                CONFIG_CMD_IMLS_NAND    * List all images found in NAND flash
                CONFIG_CMD_IMMAP        * IMMR dump support
+               CONFIG_CMD_IOTRACE      * I/O tracing for debugging
                CONFIG_CMD_IMPORTENV    * import an environment
                CONFIG_CMD_INI          * import data from an ini file into the env
                CONFIG_CMD_IRQ          * irqinfo
                Note that if the GPIO device uses I2C, then the I2C interface
                must also be configured. See I2C Support, below.
 
+- I/O tracing:
+               When CONFIG_IO_TRACE is selected, U-Boot intercepts all I/O
+               accesses and can checksum them or write a list of them out
+               to memory. See the 'iotrace' command for details. This is
+               useful for testing device drivers since it can confirm that
+               the driver behaves the same way before and after a code
+               change. Currently this is supported on sandbox and arm. To
+               add support for your architecture, add '#include <iotrace.h>'
+               to the bottom of arch/<arch>/include/asm/io.h and test.
+
+               Example output from the 'iotrace stats' command is below.
+               Note that if the trace buffer is exhausted, the checksum will
+               still continue to operate.
+
+                       iotrace is enabled
+                       Start:  10000000        (buffer start address)
+                       Size:   00010000        (buffer size)
+                       Offset: 00000120        (current buffer offset)
+                       Output: 10000120        (start + offset)
+                       Count:  00000018        (number of trace records)
+                       CRC32:  9526fb66        (CRC32 of all trace records)
+
 - Timestamp Support:
 
                When CONFIG_TIMESTAMP is selected, the timestamp
 
 obj-$(CONFIG_CMD_GETTIME) += cmd_gettime.o
 obj-$(CONFIG_CMD_GPIO) += cmd_gpio.o
 obj-$(CONFIG_CMD_I2C) += cmd_i2c.o
+obj-$(CONFIG_CMD_IOTRACE) += cmd_iotrace.o
 obj-$(CONFIG_CMD_HASH) += cmd_hash.o
 obj-$(CONFIG_CMD_IDE) += cmd_ide.o
 obj-$(CONFIG_CMD_IMMAP) += cmd_immap.o
 obj-$(CONFIG_OF_LIBFDT) += image-fdt.o
 obj-$(CONFIG_FIT) += image-fit.o
 obj-$(CONFIG_FIT_SIGNATURE) += image-sig.o
+obj-$(CONFIG_IO_TRACE) += iotrace.o
 obj-y += memsize.o
 obj-y += stdio.o
 
 
--- /dev/null
+/*
+ * Copyright (c) 2014 Google, Inc
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#include <common.h>
+#include <command.h>
+#include <iotrace.h>
+
+static void do_print_stats(void)
+{
+       ulong start, size, offset, count;
+
+       printf("iotrace is %sabled\n", iotrace_get_enabled() ? "en" : "dis");
+       iotrace_get_buffer(&start, &size, &offset, &count);
+       printf("Start:  %08lx\n", start);
+       printf("Size:   %08lx\n", size);
+       printf("Offset: %08lx\n", offset);
+       printf("Output: %08lx\n", start + offset);
+       printf("Count:  %08lx\n", count);
+       printf("CRC32:  %08lx\n", (ulong)iotrace_get_checksum());
+}
+
+static int do_set_buffer(int argc, char * const argv[])
+{
+       ulong addr = 0, size = 0;
+
+       if (argc == 2) {
+               addr = simple_strtoul(*argv++, NULL, 16);
+               size = simple_strtoul(*argv++, NULL, 16);
+       } else if (argc != 0) {
+               return CMD_RET_USAGE;
+       }
+
+       iotrace_set_buffer(addr, size);
+
+       return 0;
+}
+
+int do_iotrace(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+       const char *cmd = argc < 2 ? NULL : argv[1];
+
+       if (!cmd)
+               return cmd_usage(cmdtp);
+       switch (*cmd) {
+       case 'b':
+               return do_set_buffer(argc - 2, argv + 2);
+       case 'p':
+               iotrace_set_enabled(0);
+               break;
+       case 'r':
+               iotrace_set_enabled(1);
+               break;
+       case 's':
+               do_print_stats();
+               break;
+       default:
+               return CMD_RET_USAGE;
+       }
+
+       return 0;
+}
+
+U_BOOT_CMD(
+       iotrace,        4,      1,      do_iotrace,
+       "iotrace utility commands",
+       "stats                        - display iotrace stats\n"
+       "iotrace buffer <address> <size>      - set iotrace buffer\n"
+       "iotrace pause                        - pause tracing\n"
+       "iotrace resume                       - resume tracing"
+);
 
--- /dev/null
+/*
+ * Copyright (c) 2014 Google, Inc.
+ *
+ * SPDX-License-Identifier:     GPL-2.0+
+ */
+
+#define IOTRACE_IMPL
+
+#include <common.h>
+#include <asm/io.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/* Support up to the machine word length for now */
+typedef ulong iovalue_t;
+
+enum iotrace_flags {
+       IOT_8 = 0,
+       IOT_16,
+       IOT_32,
+
+       IOT_READ = 0 << 3,
+       IOT_WRITE = 1 << 3,
+};
+
+/**
+ * struct iotrace_record - Holds a single I/O trace record
+ *
+ * @flags: I/O access type
+ * @addr: Address of access
+ * @value: Value written or read
+ */
+struct iotrace_record {
+       enum iotrace_flags flags;
+       phys_addr_t addr;
+       iovalue_t value;
+};
+
+/**
+ * struct iotrace - current trace status and checksum
+ *
+ * @start:     Start address of iotrace buffer
+ * @size:      Size of iotrace buffer in bytes
+ * @offset:    Current write offset into iotrace buffer
+ * @crc32:     Current value of CRC chceksum of trace records
+ * @enabled:   true if enabled, false if disabled
+ */
+static struct iotrace {
+       ulong start;
+       ulong size;
+       ulong offset;
+       u32 crc32;
+       bool enabled;
+} iotrace;
+
+static void add_record(int flags, const void *ptr, ulong value)
+{
+       struct iotrace_record srec, *rec = &srec;
+
+       /*
+        * We don't support iotrace before relocation. Since the trace buffer
+        * is set up by a command, it can't be enabled at present. To change
+        * this we would need to set the iotrace buffer at build-time. See
+        * lib/trace.c for how this might be done if you are interested.
+        */
+       if (!(gd->flags & GD_FLG_RELOC) || !iotrace.enabled)
+               return;
+
+       /* Store it if there is room */
+       if (iotrace.offset + sizeof(*rec) < iotrace.size) {
+               rec = (struct iotrace_record *)map_sysmem(
+                                       iotrace.start + iotrace.offset,
+                                       sizeof(value));
+       }
+
+       rec->flags = flags;
+       rec->addr = map_to_sysmem(ptr);
+       rec->value = value;
+
+       /* Update our checksum */
+       iotrace.crc32 = crc32(iotrace.crc32, (unsigned char *)rec,
+                             sizeof(*rec));
+
+       iotrace.offset += sizeof(struct iotrace_record);
+}
+
+u32 iotrace_readl(const void *ptr)
+{
+       u32 v;
+
+       v = readl(ptr);
+       add_record(IOT_32 | IOT_READ, ptr, v);
+
+       return v;
+}
+
+void iotrace_writel(ulong value, const void *ptr)
+{
+       add_record(IOT_32 | IOT_WRITE, ptr, value);
+       writel(value, ptr);
+}
+
+u16 iotrace_readw(const void *ptr)
+{
+       u32 v;
+
+       v = readw(ptr);
+       add_record(IOT_16 | IOT_READ, ptr, v);
+
+       return v;
+}
+
+void iotrace_writew(ulong value, const void *ptr)
+{
+       add_record(IOT_16 | IOT_WRITE, ptr, value);
+       writew(value, ptr);
+}
+
+u8 iotrace_readb(const void *ptr)
+{
+       u32 v;
+
+       v = readb(ptr);
+       add_record(IOT_8 | IOT_READ, ptr, v);
+
+       return v;
+}
+
+void iotrace_writeb(ulong value, const void *ptr)
+{
+       add_record(IOT_8 | IOT_WRITE, ptr, value);
+       writeb(value, ptr);
+}
+
+void iotrace_reset_checksum(void)
+{
+       iotrace.crc32 = 0;
+}
+
+u32 iotrace_get_checksum(void)
+{
+       return iotrace.crc32;
+}
+
+void iotrace_set_enabled(int enable)
+{
+       iotrace.enabled = enable;
+}
+
+int iotrace_get_enabled(void)
+{
+       return iotrace.enabled;
+}
+
+void iotrace_set_buffer(ulong start, ulong size)
+{
+       iotrace.start = start;
+       iotrace.size = size;
+       iotrace.offset = 0;
+       iotrace.crc32 = 0;
+}
+
+void iotrace_get_buffer(ulong *start, ulong *size, ulong *offset, ulong *count)
+{
+       *start = iotrace.start;
+       *size = iotrace.size;
+       *offset = iotrace.offset;
+       *count = iotrace.offset / sizeof(struct iotrace_record);
+}
 
--- /dev/null
+/*
+ * Copyright (c) 2014 Google, Inc.
+ *
+ * SPDX-License-Identifier:     GPL-2.0+
+ */
+
+#ifndef __IOTRACE_H
+#define __IOTRACE_H
+
+#include <linux/types.h>
+
+/*
+ * This file is designed to be included in arch/<arch>/include/asm/io.h.
+ * It redirects all IO access through a tracing/checksumming feature for
+ * testing purposes.
+ */
+
+#if defined(CONFIG_IO_TRACE) && !defined(IOTRACE_IMPL) && \
+       !defined(CONFIG_SPL_BUILD)
+
+#undef readl
+#define readl(addr)    iotrace_readl((const void *)(addr))
+
+#undef writel
+#define writel(val, addr)      iotrace_writel(val, (const void *)(addr))
+
+#undef readw
+#define readw(addr)    iotrace_readw((const void *)(addr))
+
+#undef writew
+#define writew(val, addr)      iotrace_writew(val, (const void *)(addr))
+
+#undef readb
+#define readb(addr)    iotrace_readb((const void *)(addr))
+
+#undef writeb
+#define writeb(val, addr)      iotrace_writeb(val, (const void *)(addr))
+
+#endif
+
+/* Tracing functions which mirror their io.h counterparts */
+u32 iotrace_readl(const void *ptr);
+void iotrace_writel(ulong value, const void *ptr);
+u16 iotrace_readw(const void *ptr);
+void iotrace_writew(ulong value, const void *ptr);
+u8 iotrace_readb(const void *ptr);
+void iotrace_writeb(ulong value, const void *ptr);
+
+/**
+ * iotrace_reset_checksum() - Reset the iotrace checksum
+ */
+void iotrace_reset_checksum(void);
+
+/**
+ * iotrace_get_checksum() - Get the current checksum value
+ *
+ * @return currect checksum value
+ */
+u32 iotrace_get_checksum(void);
+
+/**
+ * iotrace_set_enabled() - Set whether iotracing is enabled or not
+ *
+ * This controls whether the checksum is updated and a trace record added
+ * for each I/O access.
+ *
+ * @enable: true to enable iotracing, false to disable
+ */
+void iotrace_set_enabled(int enable);
+
+/**
+ * iotrace_get_enabled() - Get whether iotracing is enabled or not
+ *
+ * @return true if enabled, false if disabled
+ */
+int iotrace_get_enabled(void);
+
+/**
+ * iotrace_set_buffer() - Set position and size of iotrace buffer
+ *
+ * Defines where the iotrace buffer goes, and resets the output pointer to
+ * the start of the buffer.
+ *
+ * The buffer can be 0 size in which case the checksum is updated but no
+ * trace records are writen. If the buffer is exhausted, the offset will
+ * continue to increase but not new data will be written.
+ *
+ * @start: Start address of buffer
+ * @size: Size of buffer in bytes
+ */
+void iotrace_set_buffer(ulong start, ulong size);
+
+/**
+ * iotrace_get_buffer() - Get buffer information
+ *
+ * @start: Returns start address of buffer
+ * @size: Returns size of buffer in bytes
+ * @offset: Returns the byte offset where the next output trace record will
+ * @count: Returns the number of trace records recorded
+ * be written (or would be if the buffer was large enough)
+ */
+void iotrace_get_buffer(ulong *start, ulong *size, ulong *offset, ulong *count);
+
+#endif /* __IOTRACE_H */