[U-Boot] [PATCH 0/6] bootstage: Add a number of new features

It is useful to be able to communicate the bootstage information to another program, such as the kernel, either by putting it in memory or the device tree. This makes the information accessible after boot so that it can easily be logged somewhere or perhaps reported back for diagnostics purposes. This series includes a few patches to implement this.
Sometimes a peripheral is touched multiple times during boot, or a single task is called repeatedly. Examples are reading from a device, which might happen in several parts, or setting up an LCD display, which may involve multiple steps to comply with backlight power sequences. This series adds a new bootstage time accumulation feature to add up all the time spent in a particular task.
This series also adds a new command 'bootstage' to provide access to the above features.
Simon Glass (6): bootstage: Export bootstage_add_record() function bootstage: Add time accumulation feature bootstage: Store boot timings in device tree bootstage: Add feature to stash/unstash bootstage info bootstage: Add bootstage command bootstage: Add new bootstage IDs for board, LCD
README | 25 ++++ arch/arm/lib/bootm.c | 3 + common/Makefile | 1 + common/bootstage.c | 306 +++++++++++++++++++++++++++++++++++++++++++++--- common/cmd_bootstage.c | 116 ++++++++++++++++++ include/bootstage.h | 85 +++++++++++++ 6 files changed, 517 insertions(+), 19 deletions(-) create mode 100644 common/cmd_bootstage.c

This function is not static, but not exported either. Add a prototype in the header file and move the required enum to the header also.
Signed-off-by: Simon Glass sjg@chromium.org --- common/bootstage.c | 16 ++++++---------- include/bootstage.h | 17 +++++++++++++++++ 2 files changed, 23 insertions(+), 10 deletions(-)
diff --git a/common/bootstage.c b/common/bootstage.c index 4e01d92..3275499 100644 --- a/common/bootstage.c +++ b/common/bootstage.c @@ -33,13 +33,9 @@
DECLARE_GLOBAL_DATA_PTR;
-enum bootstage_flags { - BOOTSTAGEF_ERROR = 1 << 0, /* Error record */ - BOOTSTAGEF_ALLOC = 1 << 1, /* Allocate an id */ -}; - struct bootstage_record { ulong time_us; + uint32_t start_us; const char *name; int flags; /* see enum bootstage_flags */ enum bootstage_id id; @@ -49,10 +45,9 @@ static struct bootstage_record record[BOOTSTAGE_ID_COUNT] = { {1} }; static int next_id = BOOTSTAGE_ID_USER;
ulong bootstage_add_record(enum bootstage_id id, const char *name, - int flags) + int flags, ulong mark) { struct bootstage_record *rec; - ulong mark = timer_get_boot_us();
if (flags & BOOTSTAGEF_ALLOC) id = next_id++; @@ -77,12 +72,13 @@ ulong bootstage_add_record(enum bootstage_id id, const char *name,
ulong bootstage_mark(enum bootstage_id id) { - return bootstage_add_record(id, NULL, 0); + return bootstage_add_record(id, NULL, 0, timer_get_boot_us()); }
ulong bootstage_error(enum bootstage_id id) { - return bootstage_add_record(id, NULL, BOOTSTAGEF_ERROR); + return bootstage_add_record(id, NULL, BOOTSTAGEF_ERROR, + timer_get_boot_us()); }
ulong bootstage_mark_name(enum bootstage_id id, const char *name) @@ -91,7 +87,7 @@ ulong bootstage_mark_name(enum bootstage_id id, const char *name)
if (id == BOOTSTAGE_ID_ALLOC) flags = BOOTSTAGEF_ALLOC; - return bootstage_add_record(id, name, flags); + return bootstage_add_record(id, name, flags, timer_get_boot_us()); }
static void print_time(unsigned long us_time) diff --git a/include/bootstage.h b/include/bootstage.h index a000538..64b2ec6 100644 --- a/include/bootstage.h +++ b/include/bootstage.h @@ -31,6 +31,12 @@ #define CONFIG_BOOTSTAGE_USER_COUNT 20 #endif
+/* Flags for each bootstage record */ +enum bootstage_flags { + BOOTSTAGEF_ERROR = 1 << 0, /* Error record */ + BOOTSTAGEF_ALLOC = 1 << 1, /* Allocate an id */ +}; + /* * A list of boot stages that we know about. Each of these indicates the * state that we are at, and the action that we are about to perform. For @@ -221,6 +227,17 @@ void show_boot_progress(int val); #ifdef CONFIG_BOOTSTAGE /* This is the full bootstage implementation */
+/** + * Add a new bootstage record + * + * @param id Bootstage ID to use (ignored if flags & BOOTSTAGEF_ALLOC) + * @param name Name of record, or NULL for none + * @param flags Flags (BOOTSTAGEF_...) + * @param mark Time to record in this record, in microseconds + */ +ulong bootstage_add_record(enum bootstage_id id, const char *name, + int flags, ulong mark); + /* * Mark a time stamp for the current boot stage. */

Sometimes we want to add up the amount of time spent in a particular activity when it is happening in a number of discrete chunks.
Add bootstage_start() to mark the start of an acitivity and bootstage_accum() to accumulate the time since the last start. Calling these function in pairs results in the accumulated time being collected.
Signed-off-by: Simon Glass sjg@chromium.org --- common/bootstage.c | 36 +++++++++++++++++++++++++++++++++--- include/bootstage.h | 27 +++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 3 deletions(-)
diff --git a/common/bootstage.c b/common/bootstage.c index 3275499..e7c3fa8 100644 --- a/common/bootstage.c +++ b/common/bootstage.c @@ -90,6 +90,25 @@ ulong bootstage_mark_name(enum bootstage_id id, const char *name) return bootstage_add_record(id, name, flags, timer_get_boot_us()); }
+uint32_t bootstage_start(enum bootstage_id id, const char *name) +{ + struct bootstage_record *rec = &record[id]; + + rec->start_us = timer_get_boot_us(); + rec->name = name; + return rec->start_us; +} + +uint32_t bootstage_accum(enum bootstage_id id) +{ + struct bootstage_record *rec = &record[id]; + uint32_t duration; + + duration = (uint32_t)timer_get_boot_us() - rec->start_us; + rec->time_us += duration; + return duration; +} + static void print_time(unsigned long us_time) { char str[15], *s; @@ -108,8 +127,13 @@ static void print_time(unsigned long us_time) static uint32_t print_time_record(enum bootstage_id id, struct bootstage_record *rec, uint32_t prev) { - print_time(rec->time_us); - print_time(rec->time_us - prev); + if (prev == -1U) { + printf("%11s", ""); + print_time(rec->time_us); + } else { + print_time(rec->time_us); + print_time(rec->time_us - prev); + } if (rec->name) printf(" %s\n", rec->name); else if (id >= BOOTSTAGE_ID_USER) @@ -144,13 +168,19 @@ void bootstage_report(void) qsort(record, ARRAY_SIZE(record), sizeof(*rec), h_compare_record);
for (id = 0; id < BOOTSTAGE_ID_COUNT; id++, rec++) { - if (rec->time_us != 0) + if (rec->time_us != 0 && !rec->start_us) prev = print_time_record(rec->id, rec, prev); } if (next_id > BOOTSTAGE_ID_COUNT) printf("(Overflowed internal boot id table by %d entries\n" "- please increase CONFIG_BOOTSTAGE_USER_COUNT\n", next_id - BOOTSTAGE_ID_COUNT); + + puts("\nAccumulated time:\n"); + for (id = 0, rec = record; id < BOOTSTAGE_ID_COUNT; id++, rec++) { + if (rec->start_us) + prev = print_time_record(id, rec, -1); + } }
ulong __timer_get_boot_us(void) diff --git a/include/bootstage.h b/include/bootstage.h index 64b2ec6..127c94f 100644 --- a/include/bootstage.h +++ b/include/bootstage.h @@ -247,6 +247,33 @@ ulong bootstage_error(enum bootstage_id id);
ulong bootstage_mark_name(enum bootstage_id id, const char *name);
+/** + * Mark the start of a bootstage activity. The end will be marked later with + * bootstage_accum() and at that point we accumulate the time taken. Calling + * this function turns the given id into a accumulator rather than and + * absolute mark in time. Accumulators record the total amount of time spent + * in an activty during boot. + * + * @param id Bootstage id to record this timestamp against + * @param name Textual name to display for this id in the report (maybe NULL) + * @return start timestamp in microseconds + */ +uint32_t bootstage_start(enum bootstage_id id, const char *name); + +/** + * Mark the end of a bootstage activity + * + * After previously marking the start of an activity with bootstage_start(), + * call this function to mark the end. You can call these functions in pairs + * as many times as you like. + * + * @param id Bootstage id to record this timestamp against + * @return time spent in this iteration of the activity (i.e. the time now + * less the start time recorded in the last bootstage_start() call + * with this id. + */ +uint32_t bootstage_accum(enum bootstage_id id); + /* Print a report about boot time */ void bootstage_report(void);

Add an option, CONFIG_BOOTSTAGE_FDT to pass boot timings to the kernel in the device tree, if available. To use this, you must have CONFIG_OF_LIBFDT defined.
Signed-off-by: Simon Glass sjg@chromium.org --- README | 21 +++++++++++ arch/arm/lib/bootm.c | 3 ++ common/bootstage.c | 95 ++++++++++++++++++++++++++++++++++++++++++++++--- include/bootstage.h | 7 ++++ 4 files changed, 120 insertions(+), 6 deletions(-)
diff --git a/README b/README index 5793b0a..eacf8e2 100644 --- a/README +++ b/README @@ -2378,6 +2378,27 @@ The following options need to be configured: 29,916,167 26,005,792 bootm_start 30,361,327 445,160 start_kernel
+ CONFIG_BOOTSTAGE_FDT + Stash the bootstage information in the FDT. A root 'bootstage' + node is created with each bootstage id as a child. Each child + has a 'name' property and either 'mark' containing the + mark time in microsecond, or 'accum' containing the + accumulated time for that bootstage id in microseconds. + For example: + + bootstage { + 154 { + name = "board_init_f"; + mark = <3575678>; + }; + 170 { + name = "lcd"; + accum = <33482>; + }; + }; + + Code in the Linux kernel can find this in /proc/devicetree. + Legacy uImage format:
Arg Where When diff --git a/arch/arm/lib/bootm.c b/arch/arm/lib/bootm.c index 599547d..8d5bc58 100644 --- a/arch/arm/lib/bootm.c +++ b/arch/arm/lib/bootm.c @@ -96,6 +96,9 @@ static void announce_and_cleanup(void) { printf("\nStarting kernel ...\n\n"); bootstage_mark_name(BOOTSTAGE_ID_BOOTM_HANDOFF, "start_kernel"); +#ifdef CONFIG_BOOTSTAGE_FDT + bootstage_fdt_add_report(); +#endif #ifdef CONFIG_BOOTSTAGE_REPORT bootstage_report(); #endif diff --git a/common/bootstage.c b/common/bootstage.c index e7c3fa8..9ea0128 100644 --- a/common/bootstage.c +++ b/common/bootstage.c @@ -124,9 +124,32 @@ static void print_time(unsigned long us_time) } }
+/** + * Get a record name as a printable string + * + * @param buf Buffer to put name if needed + * @param len Length of buffer + * @param rec Boot stage record to get the name from + * @return pointer to name, either from the record or pointing to buf. + */ +static const char *get_record_name(char *buf, int len, + struct bootstage_record *rec) +{ + if (rec->name) + return rec->name; + else if (rec->id >= BOOTSTAGE_ID_USER) + snprintf(buf, len, "user_%d", rec->id - BOOTSTAGE_ID_USER); + else + snprintf(buf, len, "id=%d", rec->id); + + return buf; +} + static uint32_t print_time_record(enum bootstage_id id, struct bootstage_record *rec, uint32_t prev) { + char buf[20]; + if (prev == -1U) { printf("%11s", ""); print_time(rec->time_us); @@ -134,12 +157,8 @@ static uint32_t print_time_record(enum bootstage_id id, print_time(rec->time_us); print_time(rec->time_us - prev); } - if (rec->name) - printf(" %s\n", rec->name); - else if (id >= BOOTSTAGE_ID_USER) - printf(" user_%d\n", id - BOOTSTAGE_ID_USER); - else - printf(" id=%d\n", id); + printf(" %s\n", get_record_name(buf, sizeof(buf), rec)); + return rec->time_us; }
@@ -150,6 +169,70 @@ static int h_compare_record(const void *r1, const void *r2) return rec1->time_us > rec2->time_us ? 1 : -1; }
+#ifdef CONFIG_OF_LIBFDT +/** + * Add all bootstage timings to a device tree. + * + * @param blob Device tree blob + * @return 0 on success, != 0 on failure. + */ +static int add_bootstages_devicetree(struct fdt_header *blob) +{ + int bootstage; + char buf[20]; + int id; + int i; + + if (!blob) + return 0; + + /* + * Create the node for bootstage. + * The address of flat device tree is set up by the command bootm. + */ + bootstage = fdt_add_subnode(blob, 0, "bootstage"); + if (bootstage < 0) + return -1; + + /* + * Insert the timings to the device tree in the reverse order so + * that they can be printed in the Linux kernel in the right order. + */ + for (id = BOOTSTAGE_ID_COUNT - 1, i = 0; id >= 0; id--, i++) { + struct bootstage_record *rec = &record[id]; + int node; + + if (id != BOOTSTAGE_ID_AWAKE && rec->time_us == 0) + continue; + + node = fdt_add_subnode(blob, bootstage, simple_itoa(i)); + if (node < 0) + break; + + /* add properties to the node. */ + if (fdt_setprop_string(blob, node, "name", + get_record_name(buf, sizeof(buf), rec))) + return -1; + + /* Check if this is a 'mark' or 'accum' record */ + if (fdt_setprop_cell(blob, node, + rec->start_us ? "accum" : "mark", + rec->time_us)) + return -1; + } + + return 0; +} + +int bootstage_fdt_add_report(void) +{ + if (add_bootstages_devicetree(working_fdt)) + puts("bootstage: Failed to add to device tree\n"); + + return 0; +} +#endif + void bootstage_report(void) { struct bootstage_record *rec = record; diff --git a/include/bootstage.h b/include/bootstage.h index 127c94f..9113852 100644 --- a/include/bootstage.h +++ b/include/bootstage.h @@ -277,6 +277,13 @@ uint32_t bootstage_accum(enum bootstage_id id); /* Print a report about boot time */ void bootstage_report(void);
+/** + * Add bootstage information to the device tree + * + * @return 0 if ok, -ve on error + */ +int bootstage_fdt_add_report(void); + #else /* * This is a dummy implementation which just calls show_boot_progress(),

It is useful to be able to write the bootstage information to memory for use by a later utility, or the Linux kernel. Provide a function to do this as well as a function to read bootstage information back and incorporate it into the current table.
This also makes it possible for U-Boot to chain to another U-Boot and pass on its bootstage information.
BRANCH=snow,link Signed-off-by: Simon Glass sjg@chromium.org --- common/bootstage.c | 159 +++++++++++++++++++++++++++++++++++++++++++++++++++ include/bootstage.h | 29 +++++++++ 2 files changed, 188 insertions(+), 0 deletions(-)
diff --git a/common/bootstage.c b/common/bootstage.c index 9ea0128..a1e0939 100644 --- a/common/bootstage.c +++ b/common/bootstage.c @@ -44,6 +44,18 @@ struct bootstage_record { static struct bootstage_record record[BOOTSTAGE_ID_COUNT] = { {1} }; static int next_id = BOOTSTAGE_ID_USER;
+enum { + BOOTSTAGE_VERSION = 0, + BOOTSTAGE_MAGIC = 0xb00757a3, +}; + +struct bootstage_hdr { + uint32_t version; /* BOOTSTAGE_VERSION */ + uint32_t count; /* Number of records */ + uint32_t size; /* Total data size (non-zero if valid) */ + uint32_t magic; /* Unused */ +}; + ulong bootstage_add_record(enum bootstage_id id, const char *name, int flags, ulong mark) { @@ -282,3 +294,150 @@ ulong __timer_get_boot_us(void)
ulong timer_get_boot_us(void) __attribute__((weak, alias("__timer_get_boot_us"))); + +/** + * Append data to a memory buffer + * + * Write data to the buffer if there is space. Whether there is space or not, + * the buffer pointer is incremented. + * + * @param ptrp Pointer to buffer, updated by this function + * @param end Pointer to end of buffer + * @param data Data to write to buffer + * @param size Size of data + */ +static void append_data(char **ptrp, char *end, const void *data, int size) +{ + char *ptr = *ptrp; + + *ptrp += size; + if (*ptrp > end) + return; + + memcpy(ptr, data, size); +} + +int bootstage_stash(void *base, int size) +{ + struct bootstage_hdr *hdr = (struct bootstage_hdr *)base; + struct bootstage_record *rec; + char buf[20]; + char *ptr = base, *end = ptr + size; + uint32_t count; + int id; + + if (hdr + 1 > (struct bootstage_hdr *)end) { + debug("%s: Not enough space for bootstage hdr\n", __func__); + return -1; + } + + /* Write an arbitrary version number */ + hdr->version = BOOTSTAGE_VERSION; + + /* Count the number of records, and write that value first */ + for (rec = record, id = count = 0; id < BOOTSTAGE_ID_COUNT; + id++, rec++) { + if (rec->time_us != 0) + count++; + } + hdr->count = count; + hdr->size = 0; + hdr->magic = BOOTSTAGE_MAGIC; + ptr += sizeof(*hdr); + + /* Write the records, silently stopping when we run out of space */ + for (rec = record, id = 0; id < BOOTSTAGE_ID_COUNT; id++, rec++) { + if (rec->time_us != 0) + append_data(&ptr, end, rec, sizeof(*rec)); + } + + /* Write the name strings */ + for (rec = record, id = 0; id < BOOTSTAGE_ID_COUNT; id++, rec++) { + if (rec->time_us != 0) { + const char *name; + + name = get_record_name(buf, sizeof(buf), rec); + append_data(&ptr, end, name, strlen(name) + 1); + } + } + + /* Check for buffer overflow */ + if (ptr > end) { + debug("%s: Not enough space for bootstage stash\n", __func__); + return -1; + } + + /* Update total data size */ + hdr->size = ptr - (char *)base; + printf("Stashed %d records\n", hdr->count); + + return 0; +} + +int bootstage_unstash(void *base, int size) +{ + struct bootstage_hdr *hdr = (struct bootstage_hdr *)base; + struct bootstage_record *rec; + char *ptr = base, *end = ptr + size; + uint rec_size; + int id; + + if (size == -1) + end = (char *)(~(uintptr_t)0); + + if (hdr + 1 > (struct bootstage_hdr *)end) { + debug("%s: Not enough space for bootstage hdr\n", __func__); + return -1; + } + + if (hdr->magic != BOOTSTAGE_MAGIC) { + debug("%s: Invalid bootstage magic\n", __func__); + return -1; + } + + if (ptr + hdr->size > end) { + debug("%s: Bootstage data runs past buffer end\n", __func__); + return -1; + } + + if (hdr->count * sizeof(*rec) > hdr->size) { + debug("%s: Bootstage has %d records needing %d bytes, but " + "only %d bytes is available\n", __func__, hdr->count, + hdr->count * sizeof(*rec), hdr->size); + return -1; + } + + if (hdr->version != BOOTSTAGE_VERSION) { + debug("%s: Bootstage data version %#0x unrecognised\n", + __func__, hdr->version); + return -1; + } + + if (next_id + hdr->count > BOOTSTAGE_ID_COUNT) { + debug("%s: Bootstage has %d records, we have space for %d\n" + "- please increase CONFIG_BOOTSTAGE_USER_COUNT\n", + __func__, hdr->count, BOOTSTAGE_ID_COUNT - next_id); + return -1; + } + + ptr += sizeof(*hdr); + + /* Read the records */ + rec_size = hdr->count * sizeof(*record); + memcpy(record + next_id, ptr, rec_size); + + /* Read the name strings */ + ptr += rec_size; + for (rec = record + next_id, id = 0; id < hdr->count; id++, rec++) { + rec->name = ptr; + + /* Assume no data corruption here */ + ptr += strlen(ptr) + 1; + } + + /* Mark the records as read */ + next_id += hdr->count; + printf("Unstashed %d records\n", hdr->count); + + return 0; +} diff --git a/include/bootstage.h b/include/bootstage.h index 9113852..d6b4e7b 100644 --- a/include/bootstage.h +++ b/include/bootstage.h @@ -284,6 +284,27 @@ void bootstage_report(void); */ int bootstage_fdt_add_report(void);
+/* + * Stash bootstage data into memory + * + * @param base Base address of memory buffer + * @param size Size of memory buffer + * @return 0 if stashed ok, -1 if out of space + */ +int bootstage_stash(void *base, int size); + +/** + * Read bootstage data from memory + * + * Bootstage data is read from memory and placed in the bootstage table + * in the user records. + * + * @param base Base address of memory buffer + * @param size Size of memory buffer (-1 if unknown) + * @return 0 if unstashed ok, -1 if bootstage info not found, or out of space + */ +int bootstage_unstash(void *base, int size); + #else /* * This is a dummy implementation which just calls show_boot_progress(), @@ -307,7 +328,15 @@ static inline ulong bootstage_mark_name(enum bootstage_id id, const char *name) return 0; }
+static inline int bootstage_stash(void *base, int size) +{ + return 0; /* Pretend to succeed */ +}
+static inline int bootstage_unstash(void *base, int size) +{ + return 0; /* Pretend to succeed */ +} #endif /* CONFIG_BOOTSTAGE */
#endif

Now that there are a few features, add a bootstage command to access them.
bootstage report - prints a report bootstage stash/unstash - stashes bootstage records in memory, reads them back
BRANCH=snow,link Signed-off-by: Simon Glass sjg@chromium.org --- README | 4 ++ common/Makefile | 1 + common/cmd_bootstage.c | 116 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 121 insertions(+), 0 deletions(-) create mode 100644 common/cmd_bootstage.c
diff --git a/README b/README index eacf8e2..94eface 100644 --- a/README +++ b/README @@ -2378,6 +2378,10 @@ The following options need to be configured: 29,916,167 26,005,792 bootm_start 30,361,327 445,160 start_kernel
+ CONFIG_CMD_BOOTSTAGE + Add a 'bootstage' command which supports printing a report + and un/stashing of bootstage data. + CONFIG_BOOTSTAGE_FDT Stash the bootstage information in the FDT. A root 'bootstage' node is created with each bootstage id as a child. Each child diff --git a/common/Makefile b/common/Makefile index b56df1d..840eced 100644 --- a/common/Makefile +++ b/common/Makefile @@ -69,6 +69,7 @@ COBJS-$(CONFIG_CMD_BDI) += cmd_bdinfo.o COBJS-$(CONFIG_CMD_BEDBUG) += bedbug.o cmd_bedbug.o COBJS-$(CONFIG_CMD_BMP) += cmd_bmp.o COBJS-$(CONFIG_CMD_BOOTLDR) += cmd_bootldr.o +COBJS-$(CONFIG_CMD_BOOTSTAGE) += cmd_bootstage.o COBJS-$(CONFIG_CMD_CACHE) += cmd_cache.o COBJS-$(CONFIG_CMD_CONSOLE) += cmd_console.o COBJS-$(CONFIG_CMD_CPLBINFO) += cmd_cplbinfo.o diff --git a/common/cmd_bootstage.c b/common/cmd_bootstage.c new file mode 100644 index 0000000..7aa7895 --- /dev/null +++ b/common/cmd_bootstage.c @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2012, Google Inc. All rights reserved. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> + +#ifndef CONFIG_BOOTSTAGE_STASH +#define CONFIG_BOOTSTAGE_STASH -1UL +#define CONFIG_BOOTSTAGE_STASH_SIZE -1 +#endif + +static int do_bootstage_report(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + bootstage_report(); + + return 0; +} + +static int get_base_size(int argc, char * const argv[], ulong *basep, + ulong *sizep) +{ + char *endp; + + *basep = CONFIG_BOOTSTAGE_STASH; + *sizep = CONFIG_BOOTSTAGE_STASH_SIZE; + if (argc < 2) + return 0; + *basep = simple_strtoul(argv[1], &endp, 16); + if (*argv[1] == 0 || *endp != 0) + return -1; + if (argc == 2) + return 0; + *sizep = simple_strtoul(argv[2], &endp, 16); + if (*argv[2] == 0 || *endp != 0) + return -1; + + return 0; +} + +static int do_bootstage_stash(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + ulong base, size; + int ret; + + if (get_base_size(argc, argv, &base, &size)) + return CMD_RET_USAGE; + if (base == -1UL) { + printf("No bootstage stash area defined\n"); + return 1; + } + + if (0 == strcmp(argv[0], "stash")) + ret = bootstage_stash((void *)base, size); + else + ret = bootstage_unstash((void *)base, size); + if (ret) + return 1; + + return 0; +} + +static cmd_tbl_t cmd_bootstage_sub[] = { + U_BOOT_CMD_MKENT(report, 2, 1, do_bootstage_report, "", ""), + U_BOOT_CMD_MKENT(stash, 4, 0, do_bootstage_stash, "", ""), + U_BOOT_CMD_MKENT(unstash, 4, 0, do_bootstage_stash, "", ""), +}; + +/* + * Process a bootstage sub-command + */ +static int do_boostage(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + cmd_tbl_t *c; + + /* Strip off leading 'bootstage' command argument */ + argc--; + argv++; + + c = find_cmd_tbl(argv[0], cmd_bootstage_sub, + ARRAY_SIZE(cmd_bootstage_sub)); + + if (c) + return c->cmd(cmdtp, flag, argc, argv); + else + return CMD_RET_USAGE; +} + + +U_BOOT_CMD(bootstage, 4, 1, do_boostage, + "Boot stage command", + " - check boot progress and timing\n" + "report - Print a report\n" + "stash [<start> [<size>]] - Stash data into memory\n" + "unstash [<start> [<size>]] - Unstash data from memory" +);

Add bootstage IDs for board init and LCD.
Signed-off-by: Simon Glass sjg@chromium.org --- include/bootstage.h | 5 +++++ 1 files changed, 5 insertions(+), 0 deletions(-)
diff --git a/include/bootstage.h b/include/bootstage.h index d6b4e7b..8fa1326 100644 --- a/include/bootstage.h +++ b/include/bootstage.h @@ -187,6 +187,7 @@ enum bootstage_id { * rough boot timing information. */ BOOTSTAGE_ID_AWAKE, + BOOTSTAGE_ID_START_SPL, BOOTSTAGE_ID_START_UBOOT_F, BOOTSTAGE_ID_START_UBOOT_R, BOOTSTAGE_ID_USB_START, @@ -198,11 +199,15 @@ enum bootstage_id { BOOTSTAGE_ID_MAIN_LOOP, BOOTSTAGE_KERNELREAD_START, BOOTSTAGE_KERNELREAD_STOP, + BOOTSTAGE_ID_BOARD_INIT, + BOOTSTAGE_ID_BOARD_INIT_DONE,
BOOTSTAGE_ID_CPU_AWAKE, BOOTSTAGE_ID_MAIN_CPU_AWAKE, BOOTSTAGE_ID_MAIN_CPU_READY,
+ BOOTSTAGE_ID_ACCUM_LCD, + /* a few spare for the user, from here */ BOOTSTAGE_ID_USER, BOOTSTAGE_ID_COUNT = BOOTSTAGE_ID_USER + CONFIG_BOOTSTAGE_USER_COUNT,

Hi Simon,
On Fri, 28 Sep 2012 11:56:34 -0700 Simon Glass sjg@chromium.org wrote:
It is useful to be able to communicate the bootstage information to another program, such as the kernel, either by putting it in memory or the device tree. This makes the information accessible after boot so that it can easily be logged somewhere or perhaps reported back for diagnostics purposes. This series includes a few patches to implement this.
Sometimes a peripheral is touched multiple times during boot, or a single task is called repeatedly. Examples are reading from a device, which might happen in several parts, or setting up an LCD display, which may involve multiple steps to comply with backlight power sequences. This series adds a new bootstage time accumulation feature to add up all the time spent in a particular task.
This series also adds a new command 'bootstage' to provide access to the above features.
Simon Glass (6): bootstage: Export bootstage_add_record() function bootstage: Add time accumulation feature bootstage: Store boot timings in device tree bootstage: Add feature to stash/unstash bootstage info bootstage: Add bootstage command bootstage: Add new bootstage IDs for board, LCD
README | 25 ++++ arch/arm/lib/bootm.c | 3 + common/Makefile | 1 + common/bootstage.c | 306 +++++++++++++++++++++++++++++++++++++++++++++--- common/cmd_bootstage.c | 116 ++++++++++++++++++ include/bootstage.h | 85 +++++++++++++ 6 files changed, 517 insertions(+), 19 deletions(-) create mode 100644 common/cmd_bootstage.c
Applied this series to u-boot-staging/agust@denx.de-next, thanks!
I've dropped "BRANCH=snow,link" in commit log of the patches 4 and 5, hope this is okay?
Thanks, Anatolij

Hi Anatolij,
On Tue, Oct 2, 2012 at 4:02 PM, Anatolij Gustschin agust@denx.de wrote:
Hi Simon,
On Fri, 28 Sep 2012 11:56:34 -0700 Simon Glass sjg@chromium.org wrote:
It is useful to be able to communicate the bootstage information to another program, such as the kernel, either by putting it in memory or the device tree. This makes the information accessible after boot so that it can easily be logged somewhere or perhaps reported back for diagnostics purposes. This series includes a few patches to implement this.
Sometimes a peripheral is touched multiple times during boot, or a single task is called repeatedly. Examples are reading from a device, which might happen in several parts, or setting up an LCD display, which may involve multiple steps to comply with backlight power sequences. This series adds a new bootstage time accumulation feature to add up all the time spent in a particular task.
This series also adds a new command 'bootstage' to provide access to the above features.
Simon Glass (6): bootstage: Export bootstage_add_record() function bootstage: Add time accumulation feature bootstage: Store boot timings in device tree bootstage: Add feature to stash/unstash bootstage info bootstage: Add bootstage command bootstage: Add new bootstage IDs for board, LCD
README | 25 ++++ arch/arm/lib/bootm.c | 3 + common/Makefile | 1 + common/bootstage.c | 306 +++++++++++++++++++++++++++++++++++++++++++++--- common/cmd_bootstage.c | 116 ++++++++++++++++++ include/bootstage.h | 85 +++++++++++++ 6 files changed, 517 insertions(+), 19 deletions(-) create mode 100644 common/cmd_bootstage.c
Applied this series to u-boot-staging/agust@denx.de-next, thanks!
I've dropped "BRANCH=snow,link" in commit log of the patches 4 and 5, hope this is okay?
Thanks - yes, sorry about that, will adjust my process a bit.
Thanks, Anatolij
Regards Simon
participants (2)
-
Anatolij Gustschin
-
Simon Glass